stm32f4xx-hal icon indicating copy to clipboard operation
stm32f4xx-hal copied to clipboard

UART RX interrupt example?

Open brandonros opened this issue 2 years ago • 5 comments

brandonros avatar Apr 23 '22 20:04 brandonros

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

burrbull avatar Apr 24 '22 07:04 burrbull

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 avatar Apr 24 '22 17:04 brandonros

@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.

VersBinarii avatar Apr 25 '22 15:04 VersBinarii

@burrbull Example with usart RX

https://github.com/alexxy/f411-rtic-playground/blob/main/src/bin/usart-shell.rs

alexxy avatar Apr 25 '22 19:04 alexxy

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

hacknus avatar Nov 28 '23 21:11 hacknus