continue async uart example

This commit is contained in:
Robin Müller 2025-02-11 17:24:10 +01:00 committed by Robin Mueller
parent 4da270346e
commit beb56efca6

View File

@ -1,8 +1,7 @@
#![no_std] #![no_std]
#![no_main] #![no_main]
use core::{ use core::{
cell::{Cell, RefCell, UnsafeCell}, cell::RefCell,
convert::Infallible,
future::Future, future::Future,
ptr::{self}, ptr::{self},
}; };
@ -13,101 +12,117 @@ use embassy_time::{Duration, Instant, Ticker};
use embedded_hal::digital::StatefulOutputPin; use embedded_hal::digital::StatefulOutputPin;
use embedded_io_async::{ErrorType, Write}; use embedded_io_async::{ErrorType, Write};
use panic_rtt_target as _; use panic_rtt_target as _;
use portable_atomic::{AtomicBool, AtomicUsize};
use rtt_target::{rprintln, rtt_init_print}; use rtt_target::{rprintln, rtt_init_print};
use va108xx_embassy::embassy; use va108xx_embassy::embassy;
use va108xx_hal::{ use va108xx_hal::{
gpio::PinsA, gpio::PinsA,
pac, pac,
prelude::*, prelude::*,
uart::{Instance, Tx}, uart::{self, Instance, Tx},
}; };
const SYSCLK_FREQ: Hertz = Hertz::from_raw(50_000_000); const SYSCLK_FREQ: Hertz = Hertz::from_raw(50_000_000);
static UART_0_WAKER: AtomicWaker = AtomicWaker::new(); static UART_WAKERS: [AtomicWaker; 2] = [const { AtomicWaker::new() }; 2];
static TX_CONTEXTS: [Mutex<RefCell<TxContext>>; 2] =
[const { Mutex::new(RefCell::new(TxContext::new())) }; 2];
#[derive(Debug, Copy, Clone)] #[derive(Debug, Copy, Clone)]
pub struct TxContext { pub struct TxContext {
progress: usize, progress: usize,
done: bool, done: bool,
tx_overrun: bool, tx_overrun: bool,
data: SliceWithRawPtr, slice: RawBufSlice,
} }
#[allow(clippy::new_without_default)]
impl TxContext { impl TxContext {
pub const fn new() -> Self { pub const fn new() -> Self {
Self { Self {
progress: 0, progress: 0,
done: false, done: false,
tx_overrun: false, tx_overrun: false,
data: SliceWithRawPtr::new_empty(), slice: RawBufSlice::new_empty(),
} }
} }
} }
static TX_CONTEXT: Mutex<RefCell<TxContext>> = Mutex::new(RefCell::new(TxContext::new()));
#[derive(Debug, Copy, Clone)] #[derive(Debug, Copy, Clone)]
pub struct SliceWithRawPtr { struct RawBufSlice {
data: *const u8, data: *const u8,
len: usize, len: usize,
} }
/// Safety: We use a mutex to ensure concurrent access is valid. /// Safety: This type MUST be used with mutex to ensure concurrent access is valid.
unsafe impl Send for SliceWithRawPtr {} unsafe impl Send for RawBufSlice {}
impl SliceWithRawPtr { impl RawBufSlice {
pub const unsafe fn new(data: &[u8]) -> Self { /// # Safety
///
/// This function stores the raw pointer of the passed data slice. The user MUST ensure
/// that the slice outlives the data structure.
#[allow(dead_code)]
const unsafe fn new(data: &[u8]) -> Self {
Self { Self {
data: data.as_ptr(), data: data.as_ptr(),
len: data.len(), len: data.len(),
} }
} }
pub const fn new_empty() -> Self { const fn new_empty() -> Self {
Self { Self {
data: ptr::null(), data: ptr::null(),
len: 0, len: 0,
} }
} }
/// # Safety
///
/// This function stores the raw pointer of the passed data slice. The user MUST ensure
/// that the slice outlives the data structure.
pub unsafe fn set(&mut self, data: &[u8]) { pub unsafe fn set(&mut self, data: &[u8]) {
self.data = data.as_ptr(); self.data = data.as_ptr();
self.len = data.len(); self.len = data.len();
} }
} }
//static TX_DATA: Mutex<Cell<SliceWithRawPtr>> = Mutex::new(Cell::new(SliceWithRawPtr::new_empty()));
pub struct TxFuture; pub struct TxFuture {
uart_idx: usize,
}
impl TxFuture { impl TxFuture {
/// # Safety
///
/// This function stores the raw pointer of the passed data slice. The user MUST ensure
/// that the slice outlives the data structure.
pub unsafe fn new<Uart: Instance>(tx: &mut Tx<Uart>, data: &[u8]) -> Self { pub unsafe fn new<Uart: Instance>(tx: &mut Tx<Uart>, data: &[u8]) -> Self {
tx.disable_interrupts(); tx.disable_interrupts();
tx.disable(); tx.disable();
tx.clear_fifo(); tx.clear_fifo();
critical_section::with(|cs| { critical_section::with(|cs| {
let context_ref = TX_CONTEXT.borrow(cs); let context_ref = TX_CONTEXTS[Uart::IDX as usize].borrow(cs);
let mut context = context_ref.borrow_mut(); let mut context = context_ref.borrow_mut();
context.data.set(data); context.slice.set(data);
context.progress = 0; context.progress = 0;
context.done = false; context.done = false;
}); });
let uart_tx = unsafe { tx.uart() }; let uart_tx = unsafe { tx.uart() };
let init_fill_count = core::cmp::min(data.len(), 16); let init_fill_count = core::cmp::min(data.len(), 16);
// We fill the FIFO. // We fill the FIFO.
for idx in 0..init_fill_count { for data in data.iter().take(init_fill_count) {
uart_tx uart_tx.data().write(|w| unsafe { w.bits(*data as u32) });
.data()
.write(|w| unsafe { w.bits(data[idx] as u32) });
} }
critical_section::with(|cs| { critical_section::with(|cs| {
let context_ref = TX_CONTEXT.borrow(cs); let context_ref = TX_CONTEXTS[Uart::IDX as usize].borrow(cs);
let mut context = context_ref.borrow_mut(); let mut context = context_ref.borrow_mut();
context.progress += init_fill_count; context.progress += init_fill_count;
}); });
tx.enable_interrupts(); tx.enable_interrupts();
tx.enable(); tx.enable();
Self Self {
uart_idx: Uart::IDX as usize,
}
} }
} }
@ -118,9 +133,9 @@ impl Future for TxFuture {
self: core::pin::Pin<&mut Self>, self: core::pin::Pin<&mut Self>,
cx: &mut core::task::Context<'_>, cx: &mut core::task::Context<'_>,
) -> core::task::Poll<Self::Output> { ) -> core::task::Poll<Self::Output> {
UART_0_WAKER.register(cx.waker()); UART_WAKERS[self.uart_idx].register(cx.waker());
let (done, progress) = critical_section::with(|cs| { let (done, progress) = critical_section::with(|cs| {
let context = TX_CONTEXT.borrow(cs).borrow(); let context = TX_CONTEXTS[self.uart_idx].borrow(cs).borrow();
(context.done, context.progress) (context.done, context.progress)
}); });
if done { if done {
@ -130,14 +145,15 @@ impl Future for TxFuture {
} }
} }
pub fn on_interrupt_uart_tx() { pub fn on_interrupt_uart_a_tx() {
//let tx_active = TX_ACTIVE.load(portable_atomic::Ordering::Relaxed); on_interrupt_uart_tx(unsafe { pac::Uarta::steal() });
//if !tx_active { }
//return;
//} pub fn on_interrupt_uart_b_tx() {
// TODO: Check whether any TX IRQ is enabled first. on_interrupt_uart_tx(unsafe { pac::Uartb::steal() });
let periphs = unsafe { pac::Peripherals::steal() }; }
let uart = periphs.uarta;
fn on_interrupt_uart_tx<Uart: Instance>(uart: Uart) {
let irq_enb = uart.irq_enb().read(); let irq_enb = uart.irq_enb().read();
// IRQ is not related to TX. // IRQ is not related to TX.
if irq_enb.irq_tx().bit_is_clear() || irq_enb.irq_tx_empty().bit_is_clear() { if irq_enb.irq_tx().bit_is_clear() || irq_enb.irq_tx_empty().bit_is_clear() {
@ -147,28 +163,39 @@ pub fn on_interrupt_uart_tx() {
let tx_status = uart.txstatus().read(); let tx_status = uart.txstatus().read();
let unexpected_overrun = tx_status.wrlost().bit_is_set(); let unexpected_overrun = tx_status.wrlost().bit_is_set();
let mut context = critical_section::with(|cs| { let mut context = critical_section::with(|cs| {
let context_ref = TX_CONTEXT.borrow(cs); let context_ref = TX_CONTEXTS[Uart::IDX as usize].borrow(cs);
*context_ref.borrow() *context_ref.borrow()
}); });
context.tx_overrun = unexpected_overrun; context.tx_overrun = unexpected_overrun;
if context.progress >= context.data.len && !tx_status.wrbusy().bit_is_set() { if context.progress >= context.slice.len && !tx_status.wrbusy().bit_is_set() {
// Transfer is done. // Transfer is done.
context.done = true; context.done = true;
// Write back updated context structure. // Write back updated context structure.
critical_section::with(|cs| { critical_section::with(|cs| {
let context_ref = TX_CONTEXT.borrow(cs); let context_ref = TX_CONTEXTS[Uart::IDX as usize].borrow(cs);
*context_ref.borrow_mut() = context; *context_ref.borrow_mut() = context;
}); });
// TODO: Handle completion, disable TX interrupt and disable TX. // TODO: Handle completion, disable TX interrupt and disable TX.
return; return;
} }
let wrrdy = uart.txstatus().read().wrrdy().bit_is_set(); // Safety: We documented that the user provided slice must outlive the future, so we convert
// TODO: Write data into the FIFO as long as it is not full. Increment progress by written // the raw pointer back to the slice here.
// bytes. let slice = unsafe { core::slice::from_raw_parts(context.slice.data, context.slice.len) };
while context.progress < context.slice.len {
let wrrdy = uart.txstatus().read().wrrdy().bit_is_set();
if !wrrdy {
break;
}
// Safety: TX structure is owned by the future which does not write into the the data
// register, so we can assume we are the only one writing to the data register.
uart.data()
.write(|w| unsafe { w.bits(slice[context.progress] as u32) });
context.progress += 1;
}
// Write back updated context structure. // Write back updated context structure.
critical_section::with(|cs| { critical_section::with(|cs| {
let context_ref = TX_CONTEXT.borrow(cs); let context_ref = TX_CONTEXTS[Uart::IDX as usize].borrow(cs);
*context_ref.borrow_mut() = context; *context_ref.borrow_mut() = context;
}); });
} }
@ -180,6 +207,12 @@ pub struct TxUartAsync<Uart: Instance> {
#[derive(Debug)] #[derive(Debug)]
pub struct TxOverrunError; pub struct TxOverrunError;
impl embedded_io_async::Error for TxOverrunError {
fn kind(&self) -> embedded_io_async::ErrorKind {
embedded_io_async::ErrorKind::Other
}
}
impl<Uart: Instance> ErrorType for TxUartAsync<Uart> { impl<Uart: Instance> ErrorType for TxUartAsync<Uart> {
type Error = TxOverrunError; type Error = TxOverrunError;
} }
@ -187,7 +220,7 @@ impl<Uart: Instance> ErrorType for TxUartAsync<Uart> {
impl<Uart: Instance> Write for TxUartAsync<Uart> { impl<Uart: Instance> Write for TxUartAsync<Uart> {
async fn write(&mut self, buf: &[u8]) -> Result<usize, Self::Error> { async fn write(&mut self, buf: &[u8]) -> Result<usize, Self::Error> {
let fut = unsafe { TxFuture::new(&mut self.tx, buf) }; let fut = unsafe { TxFuture::new(&mut self.tx, buf) };
Ok(fut.await?) fut.await
} }
} }
@ -214,6 +247,15 @@ async fn main(_spawner: Spawner) {
let mut led0 = porta.pa10.into_readable_push_pull_output(); let mut led0 = porta.pa10.into_readable_push_pull_output();
let mut led1 = porta.pa7.into_readable_push_pull_output(); let mut led1 = porta.pa7.into_readable_push_pull_output();
let mut led2 = porta.pa6.into_readable_push_pull_output(); let mut led2 = porta.pa6.into_readable_push_pull_output();
let tx = porta.pa9.into_funsel_2();
let rx = porta.pa8.into_funsel_2();
let uarta = uart::Uart::new(&mut dp.sysconfig, 50.MHz(), dp.uarta, (tx, rx), 115200.Hz());
let (tx, _rx) = uarta.split();
let mut async_tx = TxUartAsync { tx };
let send_data = b"Hello World\r\n";
async_tx.write(send_data).await.ok();
let mut ticker = Ticker::every(Duration::from_secs(1)); let mut ticker = Ticker::every(Duration::from_secs(1));
loop { loop {
ticker.next().await; ticker.next().await;