diff --git a/examples/serial_delay.rs b/examples/serial_delay.rs index 63be087..49cf0fc 100644 --- a/examples/serial_delay.rs +++ b/examples/serial_delay.rs @@ -38,7 +38,7 @@ fn main() -> ! { let tx = gpioa.pa9.into_alternate_af7(); let rx = gpiob.pb7.into_alternate_af7(); - let serial = Serial::usart1( + let serial = Serial::new( p.USART1, (tx, rx), clocks, diff --git a/examples/serial_echo.rs b/examples/serial_echo.rs index d69e0b7..8c8e2d9 100644 --- a/examples/serial_echo.rs +++ b/examples/serial_echo.rs @@ -34,7 +34,7 @@ fn main() -> ! { let tx = gpioa.pa9.into_alternate_af7(); let rx = gpiob.pb7.into_alternate_af7(); - let serial = Serial::usart1( + let serial = Serial::new( p.USART1, (tx, rx), clocks, diff --git a/src/serial.rs b/src/serial.rs new file mode 100644 index 0000000..7fcaaf9 --- /dev/null +++ b/src/serial.rs @@ -0,0 +1,345 @@ +use core::fmt::{Result, Write}; +use core::marker::PhantomData; +use core::ops::Deref; +use core::ptr; + +use crate::hal::prelude::*; +use crate::hal::serial; +use crate::device; +use crate::time::U32Ext; +use nb::block; + +#[cfg(any( + feature = "stm32f745", + feature = "stm32f746", +))] +use crate::device::{RCC, USART1, USART2, USART3, USART6}; + +#[cfg(any( + feature = "stm32f745", + feature = "stm32f746", +))] +use crate::gpio::{ + gpioa::{PA2, PA3, PA9, PA10}, + gpiob::{PB6, PB7, PB10, PB11}, + gpioc::{PC6, PC7, PC10, PC11}, + gpiod::{PD5, PD6, PD8, PD9}, + gpiog::{PG9, PG14}, + Alternate, AF7, AF8, +}; + +use crate::rcc::Clocks; +use crate::time::Bps; + +/// 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 {} +pub trait PinTx {} +pub trait PinRx {} + +impl Pins for (TX, RX) +where + TX: PinTx, + RX: PinRx, +{} + +#[cfg(any( + feature = "stm32f745", + feature = "stm32f746", +))] +impl PinTx for PA9> {} +impl PinTx for PB6> {} +impl PinTx for PA2> {} +impl PinTx for PD5> {} +impl PinTx for PB10> {} +impl PinTx for PC10> {} +impl PinTx for PD8> {} +impl PinTx for PC6> {} +impl PinTx for PG14> {} + +#[cfg(any( + feature = "stm32f745", + feature = "stm32f746", +))] +impl PinRx for PA10> {} +impl PinRx for PB7> {} +impl PinRx for PA3> {} +impl PinRx for PD6> {} +impl PinRx for PB11> {} +impl PinRx for PC11> {} +impl PinRx for PD9> {} +impl PinRx for PC7> {} +impl PinRx for PG9> {} + + +/// Serial abstraction +pub struct Serial { + usart: USART, + pins: PINS, +} + +impl Serial { + pub fn new( + usart: USART, + pins: PINS, + clocks: Clocks, + config: Config) -> Self + where + PINS: Pins, + USART: Instance, + { + // NOTE(unsafe) This executes only during initialisation + let rcc = unsafe { &(*RCC::ptr()) }; + + // TODO: The unsafe calls below should be replaced with accessing + // the correct registers directly. + + USART::select_sysclock(rcc); + USART::enable_clock(rcc); + + // Calculate correct baudrate divisor on the fly + let brr = match config.oversampling { + Oversampling::By8 => { + usart.cr1.modify(|_, w| w.over8().set_bit()); + + let usart_div = + 2 * clocks.sysclk().0 / config.baud_rate.0; + + 0xfff0 & usart_div + | 0x0008 & 0 + | 0x0007 & ((usart_div & 0x000f) >> 1) + } + Oversampling::By16 => { + usart.cr1.modify(|_, w| w.over8().clear_bit()); + + clocks.sysclk().0 / config.baud_rate.0 + } + }; + + usart.brr.write(|w| unsafe { w.bits(brr) }); + + // Reset other registers to disable advanced USART features + usart.cr2.reset(); + usart.cr3.reset(); + + // Enable transmission and receiving + usart.cr1.modify(|_, w| + w + .te().enabled() + .re().enabled() + .ue().enabled() + ); + + Serial { usart, pins } + } + + pub fn split(self) -> (Tx, Rx) { + ( + Tx { + _usart: PhantomData, + }, + Rx { + _usart: PhantomData, + }, + ) + } + + pub fn release(self) -> (USART, PINS) { + (self.usart, self.pins) + } +} + +impl serial::Read for Serial + where USART: Instance +{ + type Error = Error; + + fn read(&mut self) -> nb::Result { + let mut rx: Rx = Rx { + _usart: PhantomData, + }; + rx.read() + } +} + +impl serial::Write for Serial + where USART: Instance +{ + type Error = Error; + + fn flush(&mut self) -> nb::Result<(), Self::Error> { + let mut tx: Tx = Tx { + _usart: PhantomData, + }; + tx.flush() + } + + fn write(&mut self, byte: u8) -> nb::Result<(), Self::Error> { + let mut tx: Tx = Tx { + _usart: PhantomData, + }; + tx.write(byte) + } +} + + +/// Serial receiver +pub struct Rx { + _usart: PhantomData, +} + +impl serial::Read for Rx + where USART: Instance +{ + type Error = Error; + + fn read(&mut self) -> nb::Result { + // NOTE(unsafe) atomic read with no side effects + let isr = unsafe { (*USART::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(&(*USART::ptr()).rdr as *const _ as *const _) + }); + } else { + nb::Error::WouldBlock + }) + } +} + + +/// Serial transmitter +pub struct Tx { + _usart: PhantomData, +} + +impl serial::Write for Tx + where USART: Instance +{ + type Error = Error; + + fn flush(&mut self) -> nb::Result<(), Self::Error> { + // NOTE(unsafe) atomic read with no side effects + let isr = unsafe { (*USART::ptr()).isr.read() }; + + if isr.tc().bit_is_set() { + Ok(()) + } else { + Err(nb::Error::WouldBlock) + } + } + + fn write(&mut self, byte: u8) -> nb::Result<(), Self::Error> { + // NOTE(unsafe) atomic read with no side effects + let isr = unsafe { (*USART::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(&(*USART::ptr()).tdr as *const _ as *mut _, byte) + } + Ok(()) + } else { + Err(nb::Error::WouldBlock) + } + } +} + + +/// USART configuration +pub struct Config { + pub baud_rate: Bps, + pub oversampling: Oversampling, +} + +pub enum Oversampling { + By8, + By16, +} + +impl Default for Config { + fn default() -> Self { + Self { + baud_rate: 115_200.bps(), + oversampling: Oversampling::By16, + } + } +} + + +/// Implemented by all USART instances +pub trait Instance: Deref { + fn ptr() -> *const device::usart1::RegisterBlock; + fn select_sysclock(rcc: &device::rcc::RegisterBlock); + fn enable_clock(rcc: &device::rcc::RegisterBlock); +} + +macro_rules! impl_instance { + ($( + $USARTX:ident: ($apbXenr:ident, $usartXsel:ident, $usartXen:ident), + )+) => { + $( + impl Instance for $USARTX { + fn ptr() -> *const device::usart1::RegisterBlock { + $USARTX::ptr() + } + + fn select_sysclock(rcc: &device::rcc::RegisterBlock) { + rcc.dckcfgr2.modify(|_, w| w.$usartXsel().bits(1)); + } + + fn enable_clock(rcc: &device::rcc::RegisterBlock) { + rcc.$apbXenr.modify(|_, w| w.$usartXen().set_bit()); + } + } + )+ + } +} + +#[cfg(any( + feature = "stm32f745", + feature = "stm32f746", +))] +impl_instance! { + USART1: (apb2enr, usart1sel, usart1en), + USART2: (apb1enr, usart2sel, usart2en), + USART3: (apb1enr, usart3sel, usart3en), + USART6: (apb2enr, usart6sel, usart6en), +} + +impl Write for Tx +where + Tx: serial::Write, +{ + fn write_str(&mut self, s: &str) -> Result { + let _ = s + .as_bytes() + .into_iter() + .map(|c| block!(self.write(*c))) + .last(); + Ok(()) + } +} diff --git a/src/serial/macros.rs b/src/serial/macros.rs deleted file mode 100644 index 8f73fa6..0000000 --- a/src/serial/macros.rs +++ /dev/null @@ -1,168 +0,0 @@ -//! Helper macros for deriving the HAL Serial abstraction - -macro_rules! halUsart { - ($( - $USARTX:ident: ($usartX:ident, $apbXenr:ident, $usartXsel:ident, $usartXen:ident), - )+) => { - $( - impl Serial<$USARTX, PINS> { - pub fn $usartX( - usart: $USARTX, - pins: PINS, - clocks: Clocks, - config: Config) -> Self - where - PINS: Pins<$USARTX>, - { - // NOTE(unsafe) This executes only during initialisation - let rcc = unsafe { &(*RCC::ptr()) }; - - // TODO: The unsafe calls below should be replaced with accessing - // the correct registers directly. - - // Use sysclock for baudrate - rcc.dckcfgr2.modify(|_, w| w.$usartXsel().bits(1)); - - // Enable clock for USART - rcc.$apbXenr.modify(|_, w| w.$usartXen().set_bit()); - - // Calculate correct baudrate divisor on the fly - let brr = match config.oversampling { - Oversampling::By8 => { - usart.cr1.modify(|_, w| w.over8().set_bit()); - - let usart_div = - 2 * clocks.sysclk().0 / config.baud_rate.0; - - 0xfff0 & usart_div - | 0x0008 & 0 - | 0x0007 & ((usart_div & 0x000f) >> 1) - } - Oversampling::By16 => { - usart.cr1.modify(|_, w| w.over8().clear_bit()); - - clocks.sysclk().0 / config.baud_rate.0 - } - }; - - usart.brr.write(|w| unsafe { w.bits(brr) }); - - // Reset other registers to disable advanced USART features - usart.cr2.reset(); - usart.cr3.reset(); - - // Enable transmission and receiving - usart.cr1.modify(|_, w| - w - .te().enabled() - .re().enabled() - .ue().enabled() - ); - - Serial { usart, pins } - } - - pub fn split(self) -> (Tx<$USARTX>, Rx<$USARTX>) { - ( - Tx { - _usart: PhantomData, - }, - Rx { - _usart: PhantomData, - }, - ) - } - - pub fn release(self) -> ($USARTX, PINS) { - (self.usart, self.pins) - } - } - - impl serial::Read for Serial<$USARTX, PINS> { - type Error = Error; - - fn read(&mut self) -> nb::Result { - let mut rx: Rx<$USARTX> = Rx { - _usart: PhantomData, - }; - rx.read() - } - } - - impl serial::Read for Rx<$USARTX> { - type Error = Error; - - fn read(&mut self) -> nb::Result { - // 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 for Serial<$USARTX, PINS> { - type Error = Error; - - fn flush(&mut self) -> nb::Result<(), Self::Error> { - let mut tx: Tx<$USARTX> = Tx { - _usart: PhantomData, - }; - tx.flush() - } - - fn write(&mut self, byte: u8) -> nb::Result<(), Self::Error> { - let mut tx: Tx<$USARTX> = Tx { - _usart: PhantomData, - }; - tx.write(byte) - } - } - - impl serial::Write for Tx<$USARTX> { - type Error = Error; - - fn flush(&mut self) -> nb::Result<(), Self::Error> { - // 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<(), Self::Error> { - // 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) - } - } - } - )+ - } -} diff --git a/src/serial/mod.rs b/src/serial/mod.rs deleted file mode 100644 index a9bdb18..0000000 --- a/src/serial/mod.rs +++ /dev/null @@ -1,149 +0,0 @@ -use core::fmt::{Result, Write}; -use core::marker::PhantomData; -use core::ptr; - -use crate::hal::prelude::*; -use crate::hal::serial; -use crate::time::U32Ext; -use nb::block; - -#[cfg(any( - feature = "stm32f745", - feature = "stm32f746", -))] -use crate::device::{RCC, USART1, USART2, USART3, USART6}; - -#[cfg(any( - feature = "stm32f745", - feature = "stm32f746", -))] -use crate::gpio::{ - gpioa::{PA2, PA3, PA9, PA10}, - gpiob::{PB6, PB7, PB10, PB11}, - gpioc::{PC6, PC7, PC10, PC11}, - gpiod::{PD5, PD6, PD8, PD9}, - gpiog::{PG9, PG14}, - Alternate, AF7, AF8, -}; - -use crate::rcc::Clocks; -use crate::time::Bps; - -/// 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 {} -pub trait PinTx {} -pub trait PinRx {} - -impl Pins for (TX, RX) -where - TX: PinTx, - RX: PinRx, -{} - -#[cfg(any( - feature = "stm32f745", - feature = "stm32f746", -))] -impl PinTx for PA9> {} -impl PinTx for PB6> {} -impl PinTx for PA2> {} -impl PinTx for PD5> {} -impl PinTx for PB10> {} -impl PinTx for PC10> {} -impl PinTx for PD8> {} -impl PinTx for PC6> {} -impl PinTx for PG14> {} - -#[cfg(any( - feature = "stm32f745", - feature = "stm32f746", -))] -impl PinRx for PA10> {} -impl PinRx for PB7> {} -impl PinRx for PA3> {} -impl PinRx for PD6> {} -impl PinRx for PB11> {} -impl PinRx for PC11> {} -impl PinRx for PD9> {} -impl PinRx for PC7> {} -impl PinRx for PG9> {} - -/// Serial abstraction -pub struct Serial { - usart: USART, - pins: PINS, -} - -/// Serial receiver -pub struct Rx { - _usart: PhantomData, -} - -/// Serial transmitter -pub struct Tx { - _usart: PhantomData, -} - - -/// USART configuration -pub struct Config { - pub baud_rate: Bps, - pub oversampling: Oversampling, -} - -pub enum Oversampling { - By8, - By16, -} - -impl Default for Config { - fn default() -> Self { - Self { - baud_rate: 115_200.bps(), - oversampling: Oversampling::By16, - } - } -} - - -#[macro_use] -mod macros; - -#[cfg(any( - feature = "stm32f745", - feature = "stm32f746", -))] -halUsart! { - USART1: (usart1, apb2enr, usart1sel, usart1en), - USART2: (usart2, apb1enr, usart2sel, usart2en), - USART3: (usart3, apb1enr, usart3sel, usart3en), - USART6: (usart6, apb2enr, usart6sel, usart6en), -} - -impl Write for Tx -where - Tx: serial::Write, -{ - fn write_str(&mut self, s: &str) -> Result { - let _ = s - .as_bytes() - .into_iter() - .map(|c| block!(self.write(*c))) - .last(); - Ok(()) - } -}