Permalink
Cannot retrieve contributors at this time
Join GitHub today
GitHub is home to over 28 million developers working together to host and review code, manage projects, and build software together.
Sign up
Fetching contributors…
| //! Serial | |
| use core::marker::PhantomData; | |
| use core::ptr; | |
| use core::sync::atomic::{self, Ordering}; | |
| use core::ops::DerefMut; | |
| use stable_deref_trait::StableDeref; | |
| use as_slice::AsMutSlice; | |
| use cast::u16; | |
| use crate::hal::serial; | |
| use nb; | |
| use crate::stm32::{USART1, USART2}; | |
| use void::Void; | |
| use crate::gpio::gpioa::{PA10, PA2, PA3, PA9}; | |
| use crate::gpio::gpiob::{PB6, PB7}; | |
| use crate::gpio::{AF7, Alternate, Input, Floating}; | |
| use crate::rcc::{APB1R1, APB2, Clocks}; | |
| use crate::time::Bps; | |
| use crate::dma::{dma1, CircBuffer}; | |
| /// Interrupt event | |
| pub enum Event { | |
| /// New data has been received | |
| Rxne, | |
| /// New data can be sent | |
| Txe, | |
| /// The line has gone idle | |
| Idle | |
| } | |
| /// Serial error | |
| #[derive(Debug)] | |
| pub enum Error { | |
| /// Framing error | |
| Framing, | |
| /// Noise error | |
| Noise, | |
| /// RX buffer overrun | |
| Overrun, | |
| /// Parity check error | |
| Parity, | |
| #[doc(hidden)] | |
| _Extensible, | |
| } | |
| pub trait Pins<USART> { | |
| const REMAP: u8; | |
| } | |
| impl Pins<USART1> for (PA9<Alternate<AF7, Input<Floating>>>, PA10<Alternate<AF7, Input<Floating>>>) { | |
| const REMAP: u8 = 0; | |
| } | |
| impl Pins<USART1> for (PB6<Alternate<AF7, Input<Floating>>>, PB7<Alternate<AF7, Input<Floating>>>) { | |
| const REMAP: u8 = 1; | |
| } | |
| impl Pins<USART2> for (PA2<Alternate<AF7, Input<Floating>>>, PA3<Alternate<AF7, Input<Floating>>>) { | |
| const REMAP: u8 = 0; | |
| } | |
| // impl Pins<USART2> for (PD5<Alternate<PushPull>>, PD6<Input<Floating>>) { | |
| // const REMAP: u8 = 0; | |
| // } | |
| /// Serial abstraction | |
| pub struct Serial<USART, PINS> { | |
| usart: USART, | |
| pins: PINS, | |
| } | |
| /// Serial receiver | |
| pub struct Rx<USART> { | |
| _usart: PhantomData<USART>, | |
| } | |
| /// Serial transmitter | |
| pub struct Tx<USART> { | |
| _usart: PhantomData<USART>, | |
| } | |
| macro_rules! hal { | |
| ($( | |
| $USARTX:ident: ($usartX:ident, $APB:ident, $usartXen:ident, $usartXrst:ident, $pclkX:ident, tx: ($dmacst:ident, $tx_chan:path), rx: ($dmacsr:ident, $rx_chan:path)), | |
| )+) => { | |
| $( | |
| impl<PINS> Serial<$USARTX, PINS> { | |
| /// Configures a USART peripheral to provide serial communication | |
| pub fn $usartX( | |
| usart: $USARTX, | |
| pins: PINS, | |
| baud_rate: Bps, | |
| clocks: Clocks, | |
| apb: &mut $APB, | |
| ) -> Self | |
| where | |
| PINS: Pins<$USARTX>, | |
| { | |
| // enable or reset $USARTX | |
| apb.enr().modify(|_, w| w.$usartXen().set_bit()); | |
| apb.rstr().modify(|_, w| w.$usartXrst().set_bit()); | |
| apb.rstr().modify(|_, w| w.$usartXrst().clear_bit()); | |
| // TODO implement pin remaping | |
| // disable hardware flow control | |
| // usart.cr3.write(|w| w.rtse().clear_bit().ctse().clear_bit()); | |
| usart.cr3.write(|w| w.dmat().set_bit().dmar().set_bit()); // enable DMA transfers | |
| let brr = clocks.$pclkX().0 / baud_rate.0; | |
| assert!(brr >= 16, "impossible baud rate"); | |
| usart.brr.write(|w| unsafe { w.bits(brr) }); | |
| // UE: enable USART | |
| // RE: enable receiver | |
| // TE: enable transceiver | |
| usart | |
| .cr1 | |
| .write(|w| w.ue().set_bit().re().set_bit().te().set_bit()); | |
| Serial { usart, pins } | |
| } | |
| /// Starts listening for an interrupt event | |
| pub fn listen(&mut self, event: Event) { | |
| match event { | |
| Event::Rxne => { | |
| self.usart.cr1.modify(|_, w| w.rxneie().set_bit()) | |
| }, | |
| Event::Txe => { | |
| self.usart.cr1.modify(|_, w| w.txeie().set_bit()) | |
| }, | |
| Event::Idle => { | |
| self.usart.cr1.modify(|_, w| w.idleie().set_bit()) | |
| }, | |
| } | |
| } | |
| /// Starts listening for an interrupt event | |
| pub fn unlisten(&mut self, event: Event) { | |
| match event { | |
| Event::Rxne => { | |
| self.usart.cr1.modify(|_, w| w.rxneie().clear_bit()) | |
| }, | |
| Event::Txe => { | |
| self.usart.cr1.modify(|_, w| w.txeie().clear_bit()) | |
| }, | |
| Event::Idle => { | |
| self.usart.cr1.modify(|_, w| w.idleie().clear_bit()) | |
| }, | |
| } | |
| } | |
| /// Splits the `Serial` abstraction into a transmitter and a receiver half | |
| pub fn split(self) -> (Tx<$USARTX>, Rx<$USARTX>) { | |
| ( | |
| Tx { | |
| _usart: PhantomData, | |
| }, | |
| Rx { | |
| _usart: PhantomData, | |
| }, | |
| ) | |
| } | |
| /// Releases the USART peripheral and associated pins | |
| pub fn free(self) -> ($USARTX, PINS) { | |
| (self.usart, self.pins) | |
| } | |
| } | |
| impl serial::Read<u8> for Rx<$USARTX> { | |
| type Error = Error; | |
| fn read(&mut self) -> nb::Result<u8, Error> { | |
| // NOTE(unsafe) atomic read with no side effects | |
| let isr = unsafe { (*$USARTX::ptr()).isr.read() }; | |
| Err(if isr.pe().bit_is_set() { | |
| nb::Error::Other(Error::Parity) | |
| } else if isr.fe().bit_is_set() { | |
| nb::Error::Other(Error::Framing) | |
| } else if isr.nf().bit_is_set() { | |
| nb::Error::Other(Error::Noise) | |
| } else if isr.ore().bit_is_set() { | |
| nb::Error::Other(Error::Overrun) | |
| } else if isr.rxne().bit_is_set() { | |
| // NOTE(read_volatile) see `write_volatile` below | |
| return Ok(unsafe { | |
| ptr::read_volatile(&(*$USARTX::ptr()).rdr as *const _ as *const _) | |
| }); | |
| } else { | |
| nb::Error::WouldBlock | |
| }) | |
| } | |
| } | |
| impl serial::Write<u8> for Tx<$USARTX> { | |
| // NOTE(Void) See section "29.7 USART interrupts"; the only possible errors during | |
| // transmission are: clear to send (which is disabled in this case) errors and | |
| // framing errors (which only occur in SmartCard mode); neither of these apply to | |
| // our hardware configuration | |
| type Error = Void; | |
| fn flush(&mut self) -> nb::Result<(), Void> { | |
| // NOTE(unsafe) atomic read with no side effects | |
| let isr = unsafe { (*$USARTX::ptr()).isr.read() }; | |
| if isr.tc().bit_is_set() { | |
| Ok(()) | |
| } else { | |
| Err(nb::Error::WouldBlock) | |
| } | |
| } | |
| fn write(&mut self, byte: u8) -> nb::Result<(), Void> { | |
| // NOTE(unsafe) atomic read with no side effects | |
| let isr = unsafe { (*$USARTX::ptr()).isr.read() }; | |
| if isr.txe().bit_is_set() { | |
| // NOTE(unsafe) atomic write to stateless register | |
| // NOTE(write_volatile) 8-bit write that's not possible through the svd2rust API | |
| unsafe { | |
| ptr::write_volatile(&(*$USARTX::ptr()).tdr as *const _ as *mut _, byte) | |
| } | |
| Ok(()) | |
| } else { | |
| Err(nb::Error::WouldBlock) | |
| } | |
| } | |
| } | |
| impl Rx<$USARTX> { | |
| pub fn circ_read<B, H>( | |
| &self, | |
| mut chan: $rx_chan, | |
| mut buffer: B, | |
| ) -> CircBuffer<B, $rx_chan> | |
| where | |
| B: StableDeref<Target = [H; 2]> + DerefMut, | |
| H: AsMutSlice<Element = u8> | |
| { | |
| { | |
| chan.cmar().write(|w| { | |
| w.ma().bits(buffer[0].as_mut_slice().as_ptr() as usize as u32) | |
| }); | |
| chan.cndtr().write(|w|{ | |
| w.ndt().bits(u16(buffer[0].as_mut_slice().len() * 2).unwrap()) | |
| }); | |
| chan.cpar().write(|w| unsafe { | |
| w.pa().bits(&(*$USARTX::ptr()).rdr as *const _ as usize as u32) | |
| }); | |
| // Tell DMA to request from serial | |
| chan.cselr().write(|w| { | |
| w.$dmacsr().bits(0b0010) | |
| }); | |
| // TODO can we weaken this compiler barrier? | |
| // NOTE(compiler_fence) operations on `buffer` should not be reordered after | |
| // the next statement, which starts the DMA transfer | |
| atomic::compiler_fence(Ordering::SeqCst); | |
| chan.ccr().modify(|_, w| unsafe { | |
| w.mem2mem() | |
| .clear_bit() | |
| // 00: Low, 01: Medium, 10: High, 11: Very high | |
| .pl() | |
| .bits(0b01) | |
| // 00: 8-bits, 01: 16-bits, 10: 32-bits, 11: Reserved | |
| .msize() | |
| .bits(0b00) | |
| // 00: 8-bits, 01: 16-bits, 10: 32-bits, 11: Reserved | |
| .psize() | |
| .bits(0b00) | |
| // incr mem address | |
| .minc() | |
| .set_bit() | |
| .pinc() | |
| .clear_bit() | |
| .circ() | |
| .set_bit() | |
| .dir() | |
| .clear_bit() | |
| .en() | |
| .set_bit() | |
| }); | |
| } | |
| CircBuffer::new(buffer, chan) | |
| } | |
| /// Checks to see if the usart peripheral has detected an idle line and clears the flag | |
| pub fn is_idle(&mut self, clear: bool) -> bool { | |
| let isr = unsafe { &(*$USARTX::ptr()).isr.read() }; | |
| let icr = unsafe { &(*$USARTX::ptr()).icr }; | |
| if isr.idle().bit_is_set() { | |
| if clear { | |
| icr.write(|w| { | |
| w.idlecf() | |
| .set_bit() | |
| }); | |
| } | |
| true | |
| } else { | |
| false | |
| } | |
| } | |
| } | |
| )+ | |
| } | |
| } | |
| hal! { | |
| USART1: (usart1, APB2, usart1en, usart1rst, pclk2, tx: (c4s, dma1::C4), rx: (c5s, dma1::C5)), | |
| USART2: (usart2, APB1R1, usart2en, usart2rst, pclk1, tx: (c7s, dma1::C7), rx: (c6s, dma1::C6)), | |
| } |