diff --git a/Cargo.toml b/Cargo.toml index a5e6df99..28372d75 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -149,3 +149,8 @@ required-features = ["has-can", "rt"] [[example]] name = "gpio_input" required-features = ["stm32f103"] + +[[example]] +name = "serial-interrupt-idle" +required-features = ["stm32f103", "rt", "medium"] +default-target = "thumbv7m-none-eabi" diff --git a/examples/serial-interrupt-idle.rs b/examples/serial-interrupt-idle.rs new file mode 100644 index 00000000..ee6a6779 --- /dev/null +++ b/examples/serial-interrupt-idle.rs @@ -0,0 +1,102 @@ +//! Serial interface loopback test +//! +//! You have to short the TX and RX pins to make this program work + +#![no_main] +#![no_std] + +use panic_halt as _; + +use cortex_m_rt::entry; +use stm32f1xx_hal::{ + pac, + pac::interrupt, + pac::USART1, + prelude::*, + serial::{Config, Rx, Serial, Tx}, +}; + +static mut RX: Option> = None; +static mut TX: Option> = None; +#[entry] +fn main() -> ! { + // Get access to the device specific peripherals from the peripheral access crate + let p = pac::Peripherals::take().unwrap(); + + // Take ownership over the raw flash and rcc devices and convert them into the corresponding + // HAL structs + let mut flash = p.FLASH.constrain(); + let rcc = p.RCC.constrain(); + + // Freeze the configuration of all the clocks in the system and store the frozen frequencies in + // `clocks` + let clocks = rcc.cfgr.freeze(&mut flash.acr); + + // Prepare the alternate function I/O registers + let mut afio = p.AFIO.constrain(); + + // Prepare the GPIOB peripheral + let mut gpiob = p.GPIOB.split(); + + // USART1 + let tx = gpiob.pb6.into_alternate_push_pull(&mut gpiob.crl); + let rx = gpiob.pb7; + + // Set up the usart device. Taks ownership over the USART register and tx/rx pins. The rest of + // the registers are used to enable and configure the device. + let (mut tx, mut rx) = Serial::usart1( + p.USART1, + (tx, rx), + &mut afio.mapr, + Config::default().baudrate(115200.bps()), + clocks, + ) + .split(); + tx.listen(); + rx.listen(); + rx.listen_idle(); + + cortex_m::interrupt::free(|_| unsafe { + TX.replace(tx); + RX.replace(rx); + }); + unsafe { + cortex_m::peripheral::NVIC::unmask(pac::Interrupt::USART1); + } + + loop { + cortex_m::asm::wfi() + } +} +const BUFFER_LEN: usize = 4096; +static mut BUFFER: &mut [u8; BUFFER_LEN] = &mut [0; BUFFER_LEN]; +static mut WIDX: usize = 0; + +unsafe fn write(buf: &[u8]) { + if let Some(tx) = TX.as_mut() { + buf.iter() + .for_each(|w| if let Err(_err) = nb::block!(tx.write(*w)) {}) + } +} +#[interrupt] +unsafe fn USART1() { + cortex_m::interrupt::free(|_| { + if let Some(rx) = RX.as_mut() { + if rx.is_rxne() { + if let Ok(w) = nb::block!(rx.read()) { + BUFFER[WIDX] = w; + WIDX += 1; + if WIDX >= BUFFER_LEN - 1 { + write(&BUFFER[..]); + WIDX = 0; + } + } + rx.listen_idle(); + } else if rx.is_idle() { + rx.unlisten_idle(); + write(&BUFFER[0..WIDX]); + WIDX = 0; + } + } + }) +} diff --git a/src/serial.rs b/src/serial.rs index e7b1527d..6dbbf4ac 100644 --- a/src/serial.rs +++ b/src/serial.rs @@ -342,6 +342,21 @@ where } } + /// Return true if the line idle status is set + pub fn is_idle(&self) -> bool { + self.usart.sr.read().idle().bit_is_set() + } + + /// Return true if the tx register is empty (and can accept data) + pub fn is_txe(&self) -> bool { + self.usart.sr.read().txe().bit_is_set() + } + + /// Return true if the rx register is not empty (and can be read) + pub fn is_rxne(&self) -> bool { + self.usart.sr.read().rxne().bit_is_set() + } + /// Returns ownership of the borrowed register handles pub fn release(self) -> (USART, PINS) { (self.usart, self.pins) @@ -418,6 +433,10 @@ macro_rules! hal { pub fn unlisten(&mut self) { unsafe { (*$USARTX::ptr()).cr1.modify(|_, w| w.txeie().clear_bit()) }; } + + pub fn is_txe(&self) -> bool { + unsafe { (*$USARTX::ptr()).sr.read().txe().bit_is_set() } + } } impl Rx<$USARTX> { @@ -428,6 +447,22 @@ macro_rules! hal { pub fn unlisten(&mut self) { unsafe { (*$USARTX::ptr()).cr1.modify(|_, w| w.rxneie().clear_bit()) }; } + + pub fn listen_idle(&mut self) { + unsafe { (*$USARTX::ptr()).cr1.modify(|_, w| w.idleie().set_bit()) }; + } + + pub fn unlisten_idle(&mut self) { + unsafe { (*$USARTX::ptr()).cr1.modify(|_, w| w.idleie().clear_bit()) }; + } + + pub fn is_idle(&self) -> bool { + unsafe { (*$USARTX::ptr()).sr.read().idle().bit_is_set() } + } + + pub fn is_rxne(&self) -> bool { + unsafe { (*$USARTX::ptr()).sr.read().rxne().bit_is_set() } + } } impl crate::hal::serial::Read for Rx<$USARTX> {