stm32f4xx-hal
stm32f4xx-hal copied to clipboard
UART RX interrupt example?
Yeah. It would be nice to see it here. Possibly adopt from other hals.
Basic https://github.com/stm32-rs/stm32f1xx-hal/blob/master/examples/serial.rs Similar with DMA https://github.com/stm32-rs/stm32f3xx-hal/blob/master/examples/serial_dma.rs RTIC: https://github.com/stm32-rs/stm32f3xx-hal/blob/master/examples/serial_echo_rtic.rs but it needs better support of interrupts
How far off is what I have below?
impl BleLink {
fn serial_write(&mut self, bytes: &[u8]) {
rprintln!("serial_write: {:02x?}", bytes);
for i in 0..bytes.len() {
block!(self.ble_serial.write(bytes[i])).unwrap();
}
block!(self.ble_serial.flush()).unwrap();
}
fn serial_read(&mut self, duration_ms: u32) -> Vec<u8, 1024> {
let mut rx_buffer: Vec::<u8, 1024> = Vec::new();
self.counter.start(duration_ms.millis()).unwrap();
loop {
match self.counter.wait() {
// exit loop if timer has expired
Ok(()) => {
break;
}
// read while timer has not yet expired
Err(nb::Error::WouldBlock) => {
match self.ble_serial.read() {
// push read byte to buffer
Ok(byte) => {
rx_buffer.push(byte);
}
// no byte available to read
Err(nb::Error::WouldBlock) => {
// TODO: sleep a very small amount?
}
// do not panic on overrun
Err(Overrun) => {
rprintln!("overrun");
}
// panic on serial read error?
Err(err) => {
panic!("{:?}", err);
}
}
}
// panic on timer error?
Err(err) => {
panic!("{:?}", err);
}
}
}
rprintln!("rx_buffer: {:02x?} {}", rx_buffer, core::str::from_utf8(&rx_buffer).unwrap());
return rx_buffer;
}
pub fn init(mut ble_serial: SerialInstance, mut counter: stm32f4xx_hal::timer::Counter<stm32f4xx_hal::pac::TIM1, 1000_u32>) -> BleLink {
ble_serial.listen(stm32f4xx_hal::serial::Event::Rxne);
BleLink {
ble_serial,
counter
}
}
}
#![no_main]
#![no_std]
use panic_probe as _;
use command::WriterExt;
use heapless::Vec;
use protocol::{CanFrame, Packet};
use rtt_target::{rprintln, rtt_init_print};
use stm32f4xx_hal::{
can::Can1,
gpio::{Alternate, PushPull, PB8, PB9},
pac,
otg_fs::USB,
prelude::*,
};
mod can;
mod comm;
mod command;
pub enum State {
Idle,
// Responding to CAN read request
RespondingCan,
}
#[rtic::app(device = stm32f4xx_hal::pac, dispatchers = [SPI1])]
mod app {
use comm::{SerialComm, UsbLink, BleLink};
use super::can::*;
use super::*;
type CanInstance = Can1<(PB9<Alternate<PushPull, 9>>, PB8<Alternate<PushPull, 9>>)>;
#[shared]
struct Shared {
serial: SerialComm,
can: CanInterface<CanInstance>,
cmd_reader: command::Reader,
state: State,
}
#[local]
struct Local {}
#[init]
fn init(cx: init::Context) -> (Shared, Local, init::Monotonics) {
rtt_init_print!();
rprintln!("Initializing");
// Setup clocks
let rcc = cx.device.RCC.constrain();
let clocks = rcc
.cfgr
.use_hse(8.MHz())
.sysclk(48.MHz())
.require_pll48clk()
.freeze();
// Setup CAN
let can = {
let gpiob = cx.device.GPIOB.split();
let rx = gpiob.pb8.into_alternate::<9>();
let tx = gpiob.pb9.into_alternate();
CanInterface::new(cx.device.CAN1.can((tx, rx)))
};
let gpiod = cx.device.GPIOD.split();
let gpioa = cx.device.GPIOA.split();
// Setup USB serial
let usb_link = UsbLink::init(USB {
usb_global: cx.device.OTG_FS_GLOBAL,
usb_device: cx.device.OTG_FS_DEVICE,
usb_pwrclk: cx.device.OTG_FS_PWRCLK,
pin_dm: gpioa.pa11.into_alternate(),
pin_dp: gpioa.pa12.into_alternate(),
hclk: clocks.hclk(),
});
// Setup BLE peripheral
let tx_pin = gpiod.pd5.into_alternate();
let rx_pin = gpiod.pd6.into_alternate();
let ble_serial_config = stm32f4xx_hal::serial::config::Config::default()
.baudrate(115200.bps())
.wordlength_8()
.parity_none()
.stopbits(stm32f4xx_hal::serial::config::StopBits::STOP1);
let mut ble_serial: stm32f4xx_hal::serial::Serial<_, _, u8> = stm32f4xx_hal::serial::Serial::new(
cx.device.USART2,
(tx_pin, rx_pin),
ble_serial_config,
&clocks,
).unwrap();
let mut counter = cx.device.TIM1.counter_ms(&clocks);
let mut ble_link = BleLink::init(ble_serial, counter);
rprintln!("Initialized");
(
Shared {
//serial: SerialComm::Usb(usb_link),
serial: SerialComm::Ble(ble_link),
can,
cmd_reader: command::Reader::new(),
state: State::Idle,
},
Local {},
init::Monotonics(),
)
}
#[task(binds = USART2, priority = 2, shared = [serial, cmd_reader])]
fn on_ble(mut ctx: on_ble::Context) {
rprintln!("1");
ctx.shared.serial.lock(|ble| {
rprintln!("2");
if let SerialComm::Ble(ble) = ble {
rprintln!("3");
ctx.shared.cmd_reader.lock(|reader| {
rprintln!("4");
if reader.read_ble(&mut ble.ble_serial) {
// Trigger command processor
let _ = process_cmd::spawn();
}
});
/*if ble.ble_serial.is_rx_not_empty() {
rprintln!("ble.ble_serial.is_rx_not_empty()");
ctx.shared.cmd_reader.lock(|reader| {
if reader.read_ble(&mut ble.ble_serial) {
// Trigger command processor
let _ = process_cmd::spawn();
}
});
}*/
}
});
}
}
@brandonros You can find a working example in here using RTIC and this HAL: https://github.com/VersBinarii/glare/blob/30b1ca62e5f119d32d56fb4df3fc24432fd457a4/src/main.rs It should be easy to migrate it to a non RTIC setup by just moving the USART task code to the USART interrupt handler.
@burrbull Example with usart RX
https://github.com/alexxy/f411-rtic-playground/blob/main/src/bin/usart-shell.rs
I added an example of working UART code with DMA (Rx), this works better for me than only an interrupt example: examples/uart-dma.rs