132 lines
3.7 KiB
Rust
132 lines
3.7 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. All received data will be sent back to the sender.
|
|
#![no_main]
|
|
#![no_std]
|
|
|
|
use ringbuf::StaticRb;
|
|
|
|
// Larger buffer for TC to be able to hold the possibly large memory write packets.
|
|
const RX_RING_BUF_SIZE: usize = 1024;
|
|
|
|
#[rtic::app(device = pac, dispatchers = [OC4])]
|
|
mod app {
|
|
use super::*;
|
|
use embedded_io::Write;
|
|
use panic_rtt_target as _;
|
|
use ringbuf::traits::{Consumer, Observer, Producer};
|
|
use rtic_example::SYSCLK_FREQ;
|
|
use rtic_monotonics::Monotonic;
|
|
use rtt_target::{rprintln, rtt_init_print};
|
|
use va108xx_hal::{
|
|
gpio::PinsA,
|
|
pac,
|
|
prelude::*,
|
|
uart::{self, RxWithInterrupt, Tx},
|
|
InterruptConfig,
|
|
};
|
|
|
|
#[local]
|
|
struct Local {
|
|
rx: RxWithInterrupt<pac::Uarta>,
|
|
tx: Tx<pac::Uarta>,
|
|
}
|
|
|
|
#[shared]
|
|
struct Shared {
|
|
rb: StaticRb<u8, RX_RING_BUF_SIZE>,
|
|
}
|
|
|
|
rtic_monotonics::systick_monotonic!(Mono, 1_000);
|
|
|
|
#[init]
|
|
fn init(cx: init::Context) -> (Shared, Local) {
|
|
rtt_init_print!();
|
|
rprintln!("-- VA108xx UART Echo with IRQ example application--");
|
|
|
|
Mono::start(cx.core.SYST, SYSCLK_FREQ.raw());
|
|
|
|
let mut dp = cx.device;
|
|
let gpioa = PinsA::new(&mut dp.sysconfig, dp.porta);
|
|
let tx = gpioa.pa9.into_funsel_2();
|
|
let rx = gpioa.pa8.into_funsel_2();
|
|
|
|
let irq_uart = uart::Uart::new_with_interrupt(
|
|
&mut dp.sysconfig,
|
|
SYSCLK_FREQ,
|
|
dp.uarta,
|
|
(tx, rx),
|
|
115200.Hz(),
|
|
InterruptConfig::new(pac::Interrupt::OC3, true, true),
|
|
);
|
|
let (tx, rx) = irq_uart.split();
|
|
let mut rx = rx.into_rx_with_irq();
|
|
|
|
rx.start();
|
|
|
|
echo_handler::spawn().unwrap();
|
|
(
|
|
Shared {
|
|
rb: StaticRb::default(),
|
|
},
|
|
Local { rx, tx },
|
|
)
|
|
}
|
|
|
|
// `shared` cannot be accessed from this context
|
|
#[idle]
|
|
fn idle(_cx: idle::Context) -> ! {
|
|
loop {
|
|
cortex_m::asm::nop();
|
|
}
|
|
}
|
|
|
|
#[task(
|
|
binds = OC3,
|
|
shared = [rb],
|
|
local = [
|
|
rx,
|
|
],
|
|
)]
|
|
fn reception_task(mut cx: reception_task::Context) {
|
|
let mut buf: [u8; 16] = [0; 16];
|
|
let mut ringbuf_full = false;
|
|
let result = cx.local.rx.on_interrupt(&mut buf);
|
|
if result.bytes_read > 0 && result.errors.is_none() {
|
|
cx.shared.rb.lock(|rb| {
|
|
if rb.vacant_len() < result.bytes_read {
|
|
ringbuf_full = true;
|
|
} else {
|
|
rb.push_slice(&buf[0..result.bytes_read]);
|
|
}
|
|
});
|
|
}
|
|
if ringbuf_full {
|
|
// Could also drop oldest data, but that would require the consumer to be shared.
|
|
rprintln!("buffer full, data was dropped");
|
|
}
|
|
}
|
|
|
|
#[task(shared = [rb], local = [
|
|
buf: [u8; RX_RING_BUF_SIZE] = [0; RX_RING_BUF_SIZE],
|
|
|
|
tx
|
|
], priority=1)]
|
|
async fn echo_handler(mut cx: echo_handler::Context) {
|
|
loop {
|
|
cx.shared.rb.lock(|rb| {
|
|
let bytes_to_read = rb.occupied_len();
|
|
if bytes_to_read > 0 {
|
|
let actual_read_bytes = rb.pop_slice(&mut cx.local.buf[0..bytes_to_read]);
|
|
cx.local
|
|
.tx
|
|
.write_all(&cx.local.buf[0..actual_read_bytes])
|
|
.expect("Failed to write to TX");
|
|
}
|
|
});
|
|
Mono::delay(50.millis()).await;
|
|
}
|
|
}
|
|
}
|