From beb56efca62590b1679575a89afc14a5ffd5779e Mon Sep 17 00:00:00 2001 From: Robin Mueller Date: Tue, 11 Feb 2025 17:24:10 +0100 Subject: [PATCH] continue async uart example --- examples/embassy/src/bin/async-uart.rs | 128 ++++++++++++++++--------- 1 file changed, 85 insertions(+), 43 deletions(-) diff --git a/examples/embassy/src/bin/async-uart.rs b/examples/embassy/src/bin/async-uart.rs index 79a5519..3bcdc9a 100644 --- a/examples/embassy/src/bin/async-uart.rs +++ b/examples/embassy/src/bin/async-uart.rs @@ -1,8 +1,7 @@ #![no_std] #![no_main] use core::{ - cell::{Cell, RefCell, UnsafeCell}, - convert::Infallible, + cell::RefCell, future::Future, ptr::{self}, }; @@ -13,101 +12,117 @@ use embassy_time::{Duration, Instant, Ticker}; use embedded_hal::digital::StatefulOutputPin; use embedded_io_async::{ErrorType, Write}; use panic_rtt_target as _; -use portable_atomic::{AtomicBool, AtomicUsize}; use rtt_target::{rprintln, rtt_init_print}; use va108xx_embassy::embassy; use va108xx_hal::{ gpio::PinsA, pac, prelude::*, - uart::{Instance, Tx}, + uart::{self, Instance, Tx}, }; 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>; 2] = + [const { Mutex::new(RefCell::new(TxContext::new())) }; 2]; #[derive(Debug, Copy, Clone)] pub struct TxContext { progress: usize, done: bool, tx_overrun: bool, - data: SliceWithRawPtr, + slice: RawBufSlice, } +#[allow(clippy::new_without_default)] impl TxContext { pub const fn new() -> Self { Self { progress: 0, done: false, tx_overrun: false, - data: SliceWithRawPtr::new_empty(), + slice: RawBufSlice::new_empty(), } } } -static TX_CONTEXT: Mutex> = Mutex::new(RefCell::new(TxContext::new())); #[derive(Debug, Copy, Clone)] -pub struct SliceWithRawPtr { +struct RawBufSlice { data: *const u8, len: usize, } -/// Safety: We use a mutex to ensure concurrent access is valid. -unsafe impl Send for SliceWithRawPtr {} +/// Safety: This type MUST be used with mutex to ensure concurrent access is valid. +unsafe impl Send for RawBufSlice {} -impl SliceWithRawPtr { - pub const unsafe fn new(data: &[u8]) -> Self { +impl RawBufSlice { + /// # 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 { data: data.as_ptr(), len: data.len(), } } - pub const fn new_empty() -> Self { + const fn new_empty() -> Self { Self { data: ptr::null(), 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]) { self.data = data.as_ptr(); self.len = data.len(); } } -//static TX_DATA: Mutex> = Mutex::new(Cell::new(SliceWithRawPtr::new_empty())); -pub struct TxFuture; +pub struct TxFuture { + uart_idx: usize, +} 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(tx: &mut Tx, data: &[u8]) -> Self { tx.disable_interrupts(); tx.disable(); tx.clear_fifo(); + 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(); - context.data.set(data); + context.slice.set(data); context.progress = 0; context.done = false; }); let uart_tx = unsafe { tx.uart() }; let init_fill_count = core::cmp::min(data.len(), 16); // We fill the FIFO. - for idx in 0..init_fill_count { - uart_tx - .data() - .write(|w| unsafe { w.bits(data[idx] as u32) }); + for data in data.iter().take(init_fill_count) { + uart_tx.data().write(|w| unsafe { w.bits(*data as u32) }); } 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(); context.progress += init_fill_count; }); tx.enable_interrupts(); tx.enable(); - Self + Self { + uart_idx: Uart::IDX as usize, + } } } @@ -118,9 +133,9 @@ impl Future for TxFuture { self: core::pin::Pin<&mut Self>, cx: &mut core::task::Context<'_>, ) -> core::task::Poll { - UART_0_WAKER.register(cx.waker()); + UART_WAKERS[self.uart_idx].register(cx.waker()); 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) }); if done { @@ -130,14 +145,15 @@ impl Future for TxFuture { } } -pub fn on_interrupt_uart_tx() { - //let tx_active = TX_ACTIVE.load(portable_atomic::Ordering::Relaxed); - //if !tx_active { - //return; - //} - // TODO: Check whether any TX IRQ is enabled first. - let periphs = unsafe { pac::Peripherals::steal() }; - let uart = periphs.uarta; +pub fn on_interrupt_uart_a_tx() { + on_interrupt_uart_tx(unsafe { pac::Uarta::steal() }); +} + +pub fn on_interrupt_uart_b_tx() { + on_interrupt_uart_tx(unsafe { pac::Uartb::steal() }); +} + +fn on_interrupt_uart_tx(uart: Uart) { let irq_enb = uart.irq_enb().read(); // IRQ is not related to TX. 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 unexpected_overrun = tx_status.wrlost().bit_is_set(); 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.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. context.done = true; // Write back updated context structure. 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; }); // TODO: Handle completion, disable TX interrupt and disable TX. return; } - let wrrdy = uart.txstatus().read().wrrdy().bit_is_set(); - // TODO: Write data into the FIFO as long as it is not full. Increment progress by written - // bytes. - + // Safety: We documented that the user provided slice must outlive the future, so we convert + // the raw pointer back to the slice here. + 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. 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; }); } @@ -180,6 +207,12 @@ pub struct TxUartAsync { #[derive(Debug)] pub struct TxOverrunError; +impl embedded_io_async::Error for TxOverrunError { + fn kind(&self) -> embedded_io_async::ErrorKind { + embedded_io_async::ErrorKind::Other + } +} + impl ErrorType for TxUartAsync { type Error = TxOverrunError; } @@ -187,7 +220,7 @@ impl ErrorType for TxUartAsync { impl Write for TxUartAsync { async fn write(&mut self, buf: &[u8]) -> Result { 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 led1 = porta.pa7.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)); loop { ticker.next().await;