From 2d3fcec1cd0f6eefafb572b634ffe606a9228bf7 Mon Sep 17 00:00:00 2001 From: Adin Ackerman Date: Fri, 18 Aug 2023 19:23:01 -0700 Subject: [PATCH] new interrupt driven serial echo --- CHANGELOG.md | 1 + Cargo.toml | 4 ++ examples/serial_echo_irq.rs | 121 ++++++++++++++++++++++++++++++++++++ 3 files changed, 126 insertions(+) create mode 100644 examples/serial_echo_irq.rs diff --git a/CHANGELOG.md b/CHANGELOG.md index d688384..59e38fc 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -16,6 +16,7 @@ and this project adheres to [Semantic Versioning](http://semver.org/). - Provide getters to serial status flags idle/txe/rxne/tc. - Provide ability to reset timer UIF interrupt flag - PWM complementary output capability for TIM1 with new example to demonstrate +- New `serial_echo_irq` example to showcase an interrupt driven serial echo ### Fixed diff --git a/Cargo.toml b/Cargo.toml index d72313a..c3ce2b2 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -97,3 +97,7 @@ required-features = ["stm32f042", "rt"] [[example]] name = "usb_serial" required-features = ["rt", "stm32f042", "stm32-usbd"] + +[[example]] +name = "serial_echo_irq" +required-features = ["stm32f031"] \ No newline at end of file diff --git a/examples/serial_echo_irq.rs b/examples/serial_echo_irq.rs new file mode 100644 index 0000000..f0d717d --- /dev/null +++ b/examples/serial_echo_irq.rs @@ -0,0 +1,121 @@ +//! Interrupt Driven Serial Echo Example +//! For NUCLEO-F031K6 + +#![no_main] +#![no_std] +#![deny(unsafe_code)] +#![allow(non_camel_case_types)] + +use core::cell::RefCell; +use nb::block; +use panic_halt as _; + +use cortex_m::interrupt::Mutex; +use cortex_m_rt::entry; + +use hal::{ + delay::Delay, + gpio::{ + gpioa::{PA15, PA2}, + Alternate, AF1, + }, + pac::{self, interrupt, Interrupt, USART1}, + prelude::*, + serial::Serial, +}; +use stm32f0xx_hal as hal; + +type SERIAL_PORT = Serial>, PA15>>; + +/* +Create our global variables: + +We use a Mutex because Mutexes require a CriticalSection +context in order to be borrowed. Since CriticalSection +contexts cannot overlap (by definition) we can rest assured +that the resource inside the Mutex will not violate +the RefMut's runtime borrowing rules (Given that we do not +try to borrow the RefMut more than once at a time). +*/ +static GSERIAL: Mutex>> = Mutex::new(RefCell::new(None)); + +#[entry] +fn main() -> ! { + let (mut delay, mut led) = cortex_m::interrupt::free(|cs| { + let dp = pac::Peripherals::take().unwrap(); // might as well panic if this doesn't work + let cp = cortex_m::peripheral::Peripherals::take().unwrap(); + let mut flash = dp.FLASH; + let mut rcc = dp.RCC.configure().sysclk(48.mhz()).freeze(&mut flash); + + let gpioa = dp.GPIOA.split(&mut rcc); + let gpiob = dp.GPIOB.split(&mut rcc); + + let delay = Delay::new(cp.SYST, &rcc); + + // setup UART + let (tx, rx) = ( + gpioa.pa2.into_alternate_af1(cs), + gpioa.pa15.into_alternate_af1(cs), + ); + + // initialize global serial + *GSERIAL.borrow(cs).borrow_mut() = + Some(Serial::usart1(dp.USART1, (tx, rx), 9_600.bps(), &mut rcc)); + + if let Some(ser) = GSERIAL.borrow(cs).borrow_mut().as_mut() { + ser.listen(hal::serial::Event::Rxne); // trigger the USART1 interrupt when bytes are available (receive buffer not empty) + } + + let led = gpiob.pb3.into_push_pull_output(cs); + + (delay, led) + }); + + #[allow(unsafe_code)] // just this once ;) + unsafe { + cortex_m::peripheral::NVIC::unmask(Interrupt::USART1); + } + + loop { + led.toggle().ok(); + + delay.delay_ms(1_000u16); + } +} + +#[interrupt] +fn USART1() { + static mut SERIAL: Option = None; + + /* + Once the main function has initialized the serial port, + we move it into this interrupt handler, giving + it exclusive access to the serial port. + */ + let ser = SERIAL.get_or_insert_with(|| { + cortex_m::interrupt::free(|cs| { + if let Some(ser) = GSERIAL.borrow(cs).take() { + ser + } else { + /* + This means the main function failed to initialize + the serial port. + + For this example, we will panic. + */ + panic!(); + } + }) + }); + + if let Ok(data) = block!(ser.read()) { + block!(ser.write(data)).ok(); + } else { + /* + Failed to read a byte: + + There could be some kind of alignment error or the UART + was disconnected or something. + */ + } +}