va108xx-hal/examples/uart-irq-rtic.rs

127 lines
4.3 KiB
Rust

//! More complex UART application
//!
//! Uses the IRQ capabilities of the VA10820 peripheral and the RTIC framework to poll the UART in
//! a non-blocking way. You can send variably sized strings to the VA10820 which will be echoed
//! back to the sender.
//!
//! This script was tested with an Arduino Due. You can find the test script in the
//! [`/test/DueSerialTest`](https://egit.irs.uni-stuttgart.de/rust/va108xx-hal/src/branch/main/test/DueSerialTest)
//! folder.
#![no_main]
#![no_std]
#[rtic::app(device = pac, dispatchers = [OC4])]
mod app {
use core::fmt::Write;
use panic_rtt_target as _;
use rtt_target::{rprintln, rtt_init_default, set_print_channel};
use va108xx_hal::{
gpio::PinsB,
pac,
prelude::*,
uart::{self, IrqCfg, IrqResult, UartWithIrqBase},
};
#[local]
struct Local {}
#[shared]
struct Shared {
irq_uart: UartWithIrqBase<pac::UARTB>,
rx_buf: [u8; 64],
}
#[init]
fn init(ctx: init::Context) -> (Shared, Local, init::Monotonics) {
let channels = rtt_init_default!();
set_print_channel(channels.up.0);
rprintln!("-- VA108xx UART IRQ example application--");
let mut dp = ctx.device;
let gpiob = PinsB::new(&mut dp.SYSCONFIG, Some(dp.IOCONFIG), dp.PORTB);
let tx = gpiob.pb21.into_funsel_1();
let rx = gpiob.pb20.into_funsel_1();
let irq_cfg = IrqCfg::new(pac::interrupt::OC3, true, true);
let (mut irq_uart, _) = uart::Uart::uartb(
dp.UARTB,
(tx, rx),
115200.bps(),
&mut dp.SYSCONFIG,
50.mhz(),
)
.into_uart_with_irq(irq_cfg, Some(&mut dp.SYSCONFIG), Some(&mut dp.IRQSEL))
.downgrade();
irq_uart
.read_fixed_len_using_irq(64, true)
.expect("Read initialization failed");
let rx_buf: [u8; 64] = [0; 64];
(Shared { irq_uart, rx_buf }, Local {}, init::Monotonics())
}
// `shared` cannot be accessed from this context
#[idle]
fn idle(_cx: idle::Context) -> ! {
loop {}
}
#[task(
binds = OC3,
shared = [irq_uart, rx_buf],
local = [cnt: u32 = 0, result: IrqResult = IrqResult::new()],
priority = 4
)]
fn reception_task(cx: reception_task::Context) {
let result = cx.local.result;
let cnt: &mut u32 = cx.local.cnt;
let irq_uart = cx.shared.irq_uart;
let rx_buf = cx.shared.rx_buf;
let (completed, end_idx) = (irq_uart, rx_buf).lock(|irq_uart, rx_buf| {
match irq_uart.irq_handler(result, rx_buf) {
Ok(_) => {
if result.complete() {
// Initiate next transfer immediately
irq_uart
.read_fixed_len_using_irq(64, true)
.expect("Read operation init failed");
let mut end_idx = 0;
for idx in 0..rx_buf.len() {
if (rx_buf[idx] as char) == '\n' {
end_idx = idx;
break;
}
}
(true, end_idx)
} else {
(false, 0)
}
}
Err(e) => {
rprintln!("Reception Error {:?}", e);
(false, 0)
}
}
});
if completed {
rprintln!("Counter: {}", cnt);
reply_handler::spawn(result.bytes_read, end_idx, result.timeout()).unwrap();
}
*cnt += 1;
}
#[task(shared = [irq_uart, rx_buf], priority = 3)]
fn reply_handler(cx: reply_handler::Context, bytes_read: usize, end_idx: usize, timeout: bool) {
let irq_uart = cx.shared.irq_uart;
let rx_buf = cx.shared.rx_buf;
(irq_uart, rx_buf).lock(|irq_uart, rx_buf| {
rprintln!("Reception success, {} bytes read", bytes_read);
if timeout {
rprintln!("Timeout occured");
}
let string = core::str::from_utf8(&rx_buf[0..end_idx]).expect("Invalid string format");
rprintln!("Read string: {}", string);
writeln!(irq_uart.uart, "{}", string).expect("Sending reply failed");
});
}
}