diff --git a/examples/serial_echo.rs b/examples/serial_echo.rs index 5a51185..a6e485c 100644 --- a/examples/serial_echo.rs +++ b/examples/serial_echo.rs @@ -15,7 +15,7 @@ use cortex_m_rt::entry; use stm32f7xx_hal::{ pac, prelude::*, - serial::{self, Serial}, + serial::{self, Serial, UART}, }; #[entry] @@ -31,7 +31,7 @@ fn main() -> ! { let tx = gpioa.pa9.into_alternate(); let rx = gpiob.pb7.into_alternate(); - let serial = Serial::new( + let serial = Serial::new_async_no_flwctl( p.USART1, (tx, rx), &clocks, diff --git a/src/serial.rs b/src/serial.rs index f097bb9..cc5a1de 100644 --- a/src/serial.rs +++ b/src/serial.rs @@ -41,11 +41,13 @@ pub enum Error { pub trait Pins {} pub trait PinTx {} pub trait PinRx {} +pub trait PinCts {} +pub trait PinRts {} -impl Pins for (TX, RX) +impl Pins for (TX, RX) where - TX: PinTx, - RX: PinRx, + TX: PinTx, + RX: PinRx, { } @@ -113,34 +115,156 @@ impl PinRx for gpio::PC7> {} impl PinRx for gpio::PG9> {} impl PinRx for gpio::PE7> {} impl PinRx for gpio::PF6> {} +// TODO where is UART8? + +impl PinCts for gpio::PA11> {} +impl PinCts for gpio::PA0> {} +impl PinCts for gpio::PB13> {} +impl PinCts for gpio::PD11> {} +impl PinCts for gpio::PA15> {} +impl PinCts for gpio::PC9> {} +impl PinCts for gpio::PG13> {} +impl PinCts for gpio::PG15> {} +impl PinCts for gpio::PE10> {} +impl PinCts for gpio::PF9> {} + +impl PinRts for gpio::PA12> {} +impl PinRts for gpio::PA1> {} +impl PinRts for gpio::PB14> {} +impl PinRts for gpio::PD12> {} +impl PinRts for gpio::PB0> {} +impl PinRts for gpio::PC8> {} +impl PinRts for gpio::PG8> {} +impl PinRts for gpio::PG12> {} +impl PinRts for gpio::PE9> {} +impl PinRts for gpio::PF8> {} + +pub enum IrDAPower { + Normal, + Low, +} + +pub enum Rs485Polarity { + High, + Low, +} + +enum AsyncFlowControl { + Rs232None, + Rs232CtsRts, + Rs232Cts, + Rs232Rts, + Rs485(Rs485Polarity), +} + +enum SerialMode { + // These options are for any UART, including USART + Asynchronous(AsyncFlowControl), + SingleWire, + MultiprocessorCommunication, + IrDA(IrDAPower), + ModbusCommunication, + LIN, + + // The following options are for USART only + Synchronous, + SmartCard, + SmartCardWithCardLock, +} /// Serial abstraction -pub struct Serial { - usart: USART, +pub struct Serial { + usart: U, pins: PINS, } -impl Serial +pub trait UART> { + fn new_async_no_flwctl<>( + uart: U, pins: PINS, clocks: &Clocks, config: Config, + ) -> Serial { + Serial::new(uart, pins, clocks, config, + SerialMode::Asynchronous(AsyncFlowControl::Rs232None)) + } + + fn new_async_rs232_cts_rts, RTS: PinRts>( + uart: U, pins: PINS, clocks: &Clocks, config: Config, cts: CTS, rts: RTS, + ) -> Serial { + // TODO Clarify if we can borrow cts and rts and keep them borrowed. + // TODO Note that at the moment any CTS and RTS pin of this U(S)ART would be accepted + // This may be too flexible. + Serial::new(uart, pins, clocks, config, + SerialMode::Asynchronous(AsyncFlowControl::Rs232CtsRts)) + } + + fn new_async_rs232_cts>( + uart: U, pins: PINS, clocks: &Clocks, config: Config, cts: CTS, + ) -> Serial { + Serial::new(uart, pins, clocks, config, + SerialMode::Asynchronous(AsyncFlowControl::Rs232Cts)) + } + + fn new_async_rs232_rts>( + uart: U, pins: PINS, clocks: &Clocks, config: Config, rts: RTS, + ) -> Serial { + Serial::new(uart, pins, clocks, config, + SerialMode::Asynchronous(AsyncFlowControl::Rs232Rts)) + } + + fn new_async_rs458>( + uart: U, pins: PINS, clocks: &Clocks, config: Config, polarity: Rs485Polarity, + ) -> Serial { + Serial::new(uart, pins, clocks, config, + SerialMode::Asynchronous(AsyncFlowControl::Rs485(polarity))) + } + + fn new_irda>( + uart: U, pins: PINS, clocks: &Clocks, config: Config, irda_power: IrDAPower, + ) -> Serial { + Serial::new(uart, pins, clocks, config, + SerialMode::IrDA(irda_power)) + } + + // TODO Add constructors for other modes of operation +} +impl > UART for Serial {} +impl > UART for Serial {} +impl > UART for Serial {} +// TODO where is UART8? +impl > UART for Serial {} +impl > UART for Serial {} +impl > UART for Serial {} +impl > UART for Serial {} + +// TODO add USART trait which would have USART-specific modes like "Synchronous" and "SmartCard" +// TOOD add add implementation of those traits for all USART* + +impl Serial where - PINS: Pins, - USART: Instance, + PINS: Pins, + U: Instance, { - pub fn new(usart: USART, pins: PINS, clocks: &Clocks, config: Config) -> Self { + fn new( + usart: U, + pins: PINS, + clocks: &Clocks, + config: Config, + mode: SerialMode + ) -> Self { // TODO remove "pub" // 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, config.sysclock); + U::select_sysclock(rcc, config.sysclock); unsafe { - USART::enable_unchecked(); + U::enable_unchecked(); } let clk = if config.sysclock { clocks.sysclk() } else { - USART::clock(clocks) + U::clock(clocks) }; // Calculate correct baudrate divisor on the fly @@ -192,6 +316,71 @@ where // Enable DMA usart.cr3.write(|w| w.dmat().enabled().dmar().enabled()); + match mode { + SerialMode::Asynchronous(flow_control) => { + usart.cr2.write(|w| w.linen().clear_bit().clken().clear_bit()); + usart.cr3.write(|w| w.scen().clear_bit().iren().clear_bit().hdsel().clear_bit()); + match flow_control { + AsyncFlowControl::Rs232None => { + usart.cr3.write(|w| w.ctse().disabled().rtse().disabled()); + }, + AsyncFlowControl::Rs232CtsRts => { + usart.cr3.write(|w| w.ctse().enabled().rtse().enabled()); + }, + AsyncFlowControl::Rs232Cts => { + usart.cr3.write(|w| w.ctse().enabled().rtse().disabled()); + }, + AsyncFlowControl::Rs232Rts => { + usart.cr3.write(|w| w.ctse().disabled().rtse().enabled()); + }, + AsyncFlowControl::Rs485(polarity) => { + usart.cr3.write(|w| w.dem().enabled()); // drive-enabled mode ON + usart.cr3.write(|w| w.dep().bit(match polarity { + Rs485Polarity::High => false, + Rs485Polarity::Low => true, + })); + usart.cr1.write(|w| w.deat().bits(0)); // setting the assertion time + usart.cr1.write(|w| w.dedt().bits(0)); // setting the de-assertion time + // TODO ^ make the times be configurable? + }, + } + }, + SerialMode::SingleWire => { + usart.cr2.write(|w| w.linen().clear_bit().clken().clear_bit()); + usart.cr3.write(|w| w.scen().clear_bit().iren().clear_bit()); + usart.cr3.write(|w| w.hdsel().set_bit()); + }, + SerialMode::MultiprocessorCommunication => { + // TODO implement this + }, + SerialMode::IrDA(power) => { + usart.cr3.write(|w| w.irlp().bit(match power { + IrDAPower::Normal => false, + IrDAPower::Low => true, + })); + usart.gtpr.write(|w| w.gt().bits(1)); // setting the prescaler + + usart.cr2.write(|w| w.linen().clear_bit().clken().clear_bit().stop().bits(0)); + usart.cr3.write(|w| w.scen().clear_bit().hdsel().clear_bit()); + usart.cr3.write(|w| w.iren().set_bit()); + }, + SerialMode::ModbusCommunication => { + // TODO implement this + }, + SerialMode::LIN => { + // TODO implement this + }, + SerialMode::Synchronous => { + // TODO implement this + }, + SerialMode::SmartCard => { + // TODO implement this + }, + SerialMode::SmartCardWithCardLock => { + // TODO implement this + }, + } + Serial { usart, pins } } @@ -215,7 +404,7 @@ where } } - pub fn split(self) -> (Tx, Rx) { + pub fn split(self) -> (Tx, Rx) { ( Tx { _usart: PhantomData, @@ -226,40 +415,40 @@ where ) } - pub fn release(self) -> (USART, PINS) { + pub fn release(self) -> (U, PINS) { (self.usart, self.pins) } } -impl serial::Read for Serial +impl serial::Read for Serial where - USART: Instance, + U: Instance, { type Error = Error; fn read(&mut self) -> nb::Result { - let mut rx: Rx = Rx { + let mut rx: Rx = Rx { _usart: PhantomData, }; rx.read() } } -impl serial::Write for Serial +impl serial::Write for Serial where - USART: Instance, + U: Instance, { type Error = Error; fn flush(&mut self) -> nb::Result<(), Self::Error> { - let mut tx: Tx = Tx { + let mut tx: Tx = Tx { _usart: PhantomData, }; tx.flush() } fn write(&mut self, byte: u8) -> nb::Result<(), Self::Error> { - let mut tx: Tx = Tx { + let mut tx: Tx = Tx { _usart: PhantomData, }; tx.write(byte) @@ -267,13 +456,13 @@ where } /// Serial receiver -pub struct Rx { - _usart: PhantomData, +pub struct Rx { + _usart: PhantomData, } -impl Rx +impl Rx where - USART: Instance, + U: Instance, Self: dma::Target, { /// Reads data using DMA until `buffer` is full @@ -292,7 +481,7 @@ where { // This is safe, as we're only using the USART instance to access the // address of one register. - let address = &unsafe { &*USART::ptr() }.rdr as *const _ as _; + let address = &unsafe { &*U::ptr() }.rdr as *const _ as _; // Safe, because the trait bounds on this method guarantee that `buffer` // can be written to safely. @@ -309,18 +498,18 @@ where } } -impl serial::Read for Rx +impl serial::Read for Rx where - USART: Instance, + U: 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() }; + let isr = unsafe { (*U::ptr()).isr.read() }; // NOTE(unsafe): Only used for atomic writes, to clear error flags. - let icr = unsafe { &(*USART::ptr()).icr }; + let icr = unsafe { &(*U::ptr()).icr }; if isr.pe().bit_is_set() { icr.write(|w| w.pecf().clear()); @@ -344,7 +533,7 @@ where return Ok(unsafe { // Casting to `u8` should be fine, as we've configured the USART // to use 8 data bits. - (*USART::ptr()).rdr.read().rdr().bits() as u8 + (*U::ptr()).rdr.read().rdr().bits() as u8 }); } @@ -353,14 +542,14 @@ where } /// Serial transmitter -pub struct Tx { - _usart: PhantomData, +pub struct Tx { + _usart: PhantomData, } -impl Tx +impl Tx where Self: dma::Target, - USART: Instance, + U: Instance, { /// Writes data using DMA /// @@ -380,7 +569,7 @@ where // STM32F74xxx, section 31.5.15. // // This is safe, as we're doing just one atomic write. - let usart = unsafe { &*USART::ptr() }; + let usart = unsafe { &*U::ptr() }; usart.icr.write(|w| w.tccf().clear()); // Safe, because the trait bounds on this method guarantee that `buffer` @@ -398,15 +587,15 @@ where } } -impl serial::Write for Tx +impl serial::Write for Tx where - USART: Instance, + U: 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() }; + let isr = unsafe { (*U::ptr()).isr.read() }; if isr.tc().bit_is_set() { Ok(()) @@ -417,12 +606,12 @@ where 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() }; + let isr = unsafe { (*U::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) } + unsafe { ptr::write_volatile(&(*U::ptr()).tdr as *const _ as *mut _, byte) } Ok(()) } else { Err(nb::Error::WouldBlock) @@ -438,7 +627,6 @@ pub struct Config { pub sysclock: bool, pub parity: Parity, pub data_bits: DataBits, - } pub enum Oversampling { @@ -531,9 +719,9 @@ impl_instance! { UART7: (uart7sel), } -impl fmt::Write for Tx +impl fmt::Write for Tx where - Tx: serial::Write, + Tx: serial::Write, { fn write_str(&mut self, s: &str) -> fmt::Result { let _ = s.as_bytes().iter().map(|c| block!(self.write(*c))).last();