continue async uart example
This commit is contained in:
parent
4da270346e
commit
beb56efca6
@ -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<RefCell<TxContext>>; 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<RefCell<TxContext>> = 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<Cell<SliceWithRawPtr>> = 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<Uart: Instance>(tx: &mut Tx<Uart>, 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<Self::Output> {
|
||||
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: Instance>(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<Uart: Instance> {
|
||||
#[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<Uart: Instance> ErrorType for TxUartAsync<Uart> {
|
||||
type Error = TxOverrunError;
|
||||
}
|
||||
@ -187,7 +220,7 @@ impl<Uart: Instance> ErrorType for TxUartAsync<Uart> {
|
||||
impl<Uart: Instance> Write for TxUartAsync<Uart> {
|
||||
async fn write(&mut self, buf: &[u8]) -> Result<usize, Self::Error> {
|
||||
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;
|
||||
|
Loading…
x
Reference in New Issue
Block a user