continue async uart example
This commit is contained in:
parent
4da270346e
commit
beb56efca6
@ -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;
|
||||||
|
Loading…
x
Reference in New Issue
Block a user