Skip to content
Snippets Groups Projects
Select Git revision
  • 4f5659b849620dc909d0ff1a3261204931d5c5ed
  • master default
2 results

serial.rs

Blame
  • Forked from Per Lindgren / stm32f4-hal
    4 commits behind the upstream repository.
    serial.rs 10.01 KiB
    //! Serial
    //!
    //! # USART2
    //!
    //! - TX = PA2
    //! - RX = PA3
    //! - Interrupt = USART2
    use core::sync::atomic::{self, Ordering};
    use core::ptr;
    use core::marker::{PhantomData, Unsize};
    use cast::u16;
    
    use hal::serial;
    use nb;
    use stm32f4x::{USART1, USART2, USART6};
    use dma::{Static, Transfer, UsartTxStream, R};
    
    // usart2
    use gpio::gpioa::{PA2, PA3};
    // use gpio::gpiob::{PB10, PB11, PB6, PB7};
    // use gpio::gpioc::{PC10, PC11, PC4, PC5};
    // use gpio::gpiod::{PD5, PD6, PD8, PD9};
    // use gpio::gpioe::{PE0, PE1, PE15};
    use gpio::AF7;
    use rcc::{APB1, APB2, Clocks};
    use time::Bps;
    
    /// Interrupt event
    pub enum Event {
        /// New data has been received
        Rxne,
        /// New data can be sent
        Txe,
    }
    
    /// Serial error
    #[derive(Debug)]
    pub enum Error {
        /// Framing error
        Framing,
        /// Noise error
        Noise,
        /// RX buffer overrun
        Overrun,
        /// Parity check error
        Parity,
        #[doc(hidden)] _Extensible,
    }
    
    // FIXME these should be "closed" traits
    /// TX pin - DO NOT IMPLEMENT THIS TRAIT
    pub unsafe trait TxPin<USART> {}
    
    /// RX pin - DO NOT IMPLEMENT THIS TRAIT
    pub unsafe trait RxPin<USART> {}
    
    // unsafe impl TxPin<USART1> for PA9<AF7> {}
    // unsafe impl TxPin<USART1> for PB6<AF7> {}
    // unsafe impl TxPin<USART1> for PC4<AF7> {}
    // unsafe impl TxPin<USART1> for PE0<AF7> {}
    
    // unsafe impl RxPin<USART1> for PA10<AF7> {}
    // unsafe impl RxPin<USART1> for PB7<AF7> {}
    // unsafe impl RxPin<USART1> for PC5<AF7> {}
    // unsafe impl RxPin<USART1> for PE1<AF7> {}
    
    unsafe impl TxPin<USART2> for PA2<AF7> {}
    // // unsafe impl TxPin<USART2> for PA14<AF7> {}
    // // unsafe impl TxPin<USART2> for PB3<AF7> {}
    // unsafe impl TxPin<USART2> for PD5<AF7> {}
    
    unsafe impl RxPin<USART2> for PA3<AF7> {}
    // // unsafe impl RxPin<USART2> for PA15<AF7> {}
    // // unsafe impl RxPin<USART2> for PB4<AF7> {}
    // unsafe impl RxPin<USART2> for PD6<AF7> {}
    
    // unsafe impl TxPin<USART3> for PB10<AF7> {}
    // unsafe impl TxPin<USART3> for PC10<AF7> {}
    // unsafe impl TxPin<USART3> for PD8<AF7> {}
    
    // unsafe impl RxPin<USART3> for PB11<AF7> {}
    // unsafe impl RxPin<USART3> for PC11<AF7> {}
    // unsafe impl RxPin<USART3> for PD9<AF7> {}
    // unsafe impl RxPin<USART3> for PE15<AF7> {}
    
    /// 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),
        )+) => {
            $(
                impl<TX, RX> Serial<$USARTX, (TX, RX)> {
                    /// Configures a USART peripheral to provide serial communication
                    pub fn $usartX(
                        usart: $USARTX,
                        pins: (TX, RX),
                        baud_rate: Bps,
                        clocks: Clocks,
                        apb: &mut $APB,
                    ) -> Self
                    where
                        TX: TxPin<$USARTX>,
                        RX: RxPin<$USARTX>,
                    {
                        // enable and 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());
    
                        // disable hardware flow control
                        // TODO enable DMA
                        // usart.cr3.write(|w| w.rtse().clear_bit().ctse().clear_bit());
    
                        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());
    
                        // disable hardware flow control
                        // enable DMA TX and RX transfers
    
                        usart.cr3.write(|w| {
                            w.rtse()
                                .clear_bit()
                                .ctse()
                                .clear_bit()
                                .dmat()
                                .set_bit()
                                .dmar()
                                .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())
                            },
                        }
                    }
    
                    /// 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())
                            },
                        }
                    }
    
                    /// 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, (TX, RX)) {
                        (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()).sr.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()).dr as *const _ as *const _)
                            });
                        } else {
                            nb::Error::WouldBlock
                        })
                    }
                }
    
                impl serial::Write<u8> for Tx<$USARTX> {
                    // NOTE(!) 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 = !;
    
                    fn flush(&mut self) -> nb::Result<(), !> {
                        // NOTE(unsafe) atomic read with no side effects
                        let isr = unsafe { (*$USARTX::ptr()).sr.read() };
    
                        if isr.tc().bit_is_set() {
                            Ok(())
                        } else {
                            Err(nb::Error::WouldBlock)
                        }
                    }
    
                    fn write(&mut self, byte: u8) -> nb::Result<(), !> {
                        // NOTE(unsafe) atomic read with no side effects
                        let isr = unsafe { (*$USARTX::ptr()).sr.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()).dr as *const _ as *mut _, byte)
                            }
                            Ok(())
                        } else {
                            Err(nb::Error::WouldBlock)
                        }
                    }
                }
                impl Tx<$USARTX> {
                    pub fn write_all<A, B, S>(
                        self, // should be mutable?
                        mut tx_stream: S,
                        buffer: B,
                    ) -> Transfer<R, B, S, Self>
                    where
                        A: Unsize<[u8]>,
                        B: Static<A>,
                        S: UsartTxStream<$USARTX>
                    {
                        {
                            let buf :&[u8] = buffer.borrow();
    
                            // ntdr, par, m0
                            tx_stream.start_transfer(
                                u16(buf.len()).unwrap(),
                                unsafe { &(*$USARTX::ptr()).dr as *const _ as usize as u32 },
                                buf.as_ptr() as u32
                            );
    
                            // 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);
                        }
                        Transfer::r(buffer, tx_stream, self)
                    }
                }
    
            )+
        }
    }
    
    hal! {
        USART1: (usart1, APB2, usart1en, usart1rst, pclk2),
        USART2: (usart2, APB1, usart2en, uart2rst, pclk1),
        USART6: (usart6, APB2, usart6en, usart6rst, pclk2),
    }