larger GPIO refactoring and Async UART update
This commit is contained in:
@ -22,7 +22,7 @@ use crate::{
|
||||
};
|
||||
use embedded_hal_nb::serial::Read;
|
||||
|
||||
#[derive(Debug)]
|
||||
#[derive(Debug, Clone, Copy)]
|
||||
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
|
||||
pub enum Bank {
|
||||
A = 0,
|
||||
@ -381,6 +381,7 @@ pub struct BufferTooShortError {
|
||||
pub trait Instance: Deref<Target = uart_base::RegisterBlock> {
|
||||
const IDX: u8;
|
||||
const PERIPH_SEL: PeripheralSelect;
|
||||
const PTR: *const uart_base::RegisterBlock;
|
||||
|
||||
/// Retrieve the peripheral structure.
|
||||
///
|
||||
@ -388,7 +389,11 @@ pub trait Instance: Deref<Target = uart_base::RegisterBlock> {
|
||||
///
|
||||
/// This circumvents the safety guarantees of the HAL.
|
||||
unsafe fn steal() -> Self;
|
||||
fn ptr() -> *const uart_base::RegisterBlock;
|
||||
|
||||
#[inline(always)]
|
||||
fn ptr() -> *const uart_base::RegisterBlock {
|
||||
Self::PTR
|
||||
}
|
||||
|
||||
/// Retrieve the type erased peripheral register block.
|
||||
///
|
||||
@ -405,14 +410,11 @@ impl Instance for pac::Uarta {
|
||||
const IDX: u8 = 0;
|
||||
|
||||
const PERIPH_SEL: PeripheralSelect = PeripheralSelect::Uart0;
|
||||
const PTR: *const uart_base::RegisterBlock = Self::PTR;
|
||||
|
||||
#[inline(always)]
|
||||
unsafe fn steal() -> Self {
|
||||
pac::Peripherals::steal().uarta
|
||||
}
|
||||
#[inline(always)]
|
||||
fn ptr() -> *const uart_base::RegisterBlock {
|
||||
Self::ptr() as *const _
|
||||
Self::steal()
|
||||
}
|
||||
}
|
||||
|
||||
@ -420,14 +422,25 @@ impl Instance for pac::Uartb {
|
||||
const IDX: u8 = 1;
|
||||
|
||||
const PERIPH_SEL: PeripheralSelect = PeripheralSelect::Uart1;
|
||||
const PTR: *const uart_base::RegisterBlock = Self::PTR;
|
||||
|
||||
#[inline(always)]
|
||||
unsafe fn steal() -> Self {
|
||||
pac::Peripherals::steal().uartb
|
||||
Self::steal()
|
||||
}
|
||||
#[inline(always)]
|
||||
fn ptr() -> *const uart_base::RegisterBlock {
|
||||
Self::ptr() as *const _
|
||||
}
|
||||
|
||||
impl Bank {
|
||||
/// Retrieve the peripheral register block.
|
||||
///
|
||||
/// # Safety
|
||||
///
|
||||
/// Circumvents the HAL safety guarantees.
|
||||
pub unsafe fn reg_block(&self) -> &'static uart_base::RegisterBlock {
|
||||
match self {
|
||||
Bank::A => unsafe { pac::Uarta::reg_block() },
|
||||
Bank::B => unsafe { pac::Uartb::reg_block() },
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -794,14 +807,12 @@ pub fn disable_rx_interrupts(uart: &uart_base::RegisterBlock) {
|
||||
/// Serial receiver.
|
||||
///
|
||||
/// Can be created by using the [Uart::split] or [UartBase::split] API.
|
||||
pub struct Rx<Uart> {
|
||||
uart: Uart,
|
||||
}
|
||||
pub struct Rx<Uart>(Uart);
|
||||
|
||||
impl<Uart: Instance> Rx<Uart> {
|
||||
#[inline(always)]
|
||||
fn new(uart: Uart) -> Self {
|
||||
Self { uart }
|
||||
const fn new(uart: Uart) -> Self {
|
||||
Self(uart)
|
||||
}
|
||||
|
||||
/// Direct access to the peripheral structure.
|
||||
@ -810,13 +821,13 @@ impl<Uart: Instance> Rx<Uart> {
|
||||
///
|
||||
/// You must ensure that only registers related to the operation of the RX side are used.
|
||||
#[inline(always)]
|
||||
pub unsafe fn uart(&self) -> &Uart {
|
||||
&self.uart
|
||||
pub const unsafe fn uart(&self) -> &Uart {
|
||||
&self.0
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn clear_fifo(&self) {
|
||||
self.uart.fifo_clr().write(|w| w.rxfifo().set_bit());
|
||||
self.0.fifo_clr().write(|w| w.rxfifo().set_bit());
|
||||
}
|
||||
|
||||
#[inline]
|
||||
@ -846,7 +857,7 @@ impl<Uart: Instance> Rx<Uart> {
|
||||
/// value if you use the manual parity mode. See chapter 4.6.2 for more information.
|
||||
#[inline(always)]
|
||||
pub fn read_fifo(&self) -> nb::Result<u32, Infallible> {
|
||||
if self.uart.rxstatus().read().rdavl().bit_is_clear() {
|
||||
if self.0.rxstatus().read().rdavl().bit_is_clear() {
|
||||
return Err(nb::Error::WouldBlock);
|
||||
}
|
||||
Ok(self.read_fifo_unchecked())
|
||||
@ -862,7 +873,7 @@ impl<Uart: Instance> Rx<Uart> {
|
||||
/// value if you use the manual parity mode. See chapter 4.6.2 for more information.
|
||||
#[inline(always)]
|
||||
pub fn read_fifo_unchecked(&self) -> u32 {
|
||||
self.uart.data().read().bits()
|
||||
self.0.data().read().bits()
|
||||
}
|
||||
|
||||
pub fn into_rx_with_irq(self) -> RxWithInterrupt<Uart> {
|
||||
@ -871,7 +882,7 @@ impl<Uart: Instance> Rx<Uart> {
|
||||
|
||||
#[inline(always)]
|
||||
pub fn release(self) -> Uart {
|
||||
self.uart
|
||||
self.0
|
||||
}
|
||||
}
|
||||
|
||||
@ -959,9 +970,7 @@ pub fn disable_tx_interrupts(uart: &uart_base::RegisterBlock) {
|
||||
/// Serial transmitter
|
||||
///
|
||||
/// Can be created by using the [Uart::split] or [UartBase::split] API.
|
||||
pub struct Tx<Uart> {
|
||||
uart: Uart,
|
||||
}
|
||||
pub struct Tx<Uart>(Uart);
|
||||
|
||||
impl<Uart: Instance> Tx<Uart> {
|
||||
/// Retrieve a TX pin without expecting an explicit UART structure
|
||||
@ -971,14 +980,12 @@ impl<Uart: Instance> Tx<Uart> {
|
||||
/// Circumvents the HAL safety guarantees.
|
||||
#[inline(always)]
|
||||
pub unsafe fn steal() -> Self {
|
||||
Self {
|
||||
uart: Uart::steal(),
|
||||
}
|
||||
Self(Uart::steal())
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
fn new(uart: Uart) -> Self {
|
||||
Self { uart }
|
||||
Self(uart)
|
||||
}
|
||||
|
||||
/// Direct access to the peripheral structure.
|
||||
@ -987,25 +994,23 @@ impl<Uart: Instance> Tx<Uart> {
|
||||
///
|
||||
/// You must ensure that only registers related to the operation of the TX side are used.
|
||||
#[inline(always)]
|
||||
pub unsafe fn uart(&self) -> &Uart {
|
||||
&self.uart
|
||||
pub const unsafe fn uart(&self) -> &Uart {
|
||||
&self.0
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn clear_fifo(&self) {
|
||||
self.uart.fifo_clr().write(|w| w.txfifo().set_bit());
|
||||
self.0.fifo_clr().write(|w| w.txfifo().set_bit());
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn enable(&mut self) {
|
||||
// Safety: We own the UART structure
|
||||
enable_tx(unsafe { Uart::reg_block() });
|
||||
self.0.enable().modify(|_, w| w.txenable().set_bit());
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn disable(&mut self) {
|
||||
// Safety: We own the UART structure
|
||||
disable_tx(unsafe { Uart::reg_block() });
|
||||
self.0.enable().modify(|_, w| w.txenable().clear_bit());
|
||||
}
|
||||
|
||||
/// Enables the IRQ_TX, IRQ_TX_STATUS and IRQ_TX_EMPTY interrupts.
|
||||
@ -1037,7 +1042,7 @@ impl<Uart: Instance> Tx<Uart> {
|
||||
/// value if you use the manual parity mode. See chapter 11.4.1 for more information.
|
||||
#[inline(always)]
|
||||
pub fn write_fifo(&self, data: u32) -> nb::Result<(), Infallible> {
|
||||
if self.uart.txstatus().read().wrrdy().bit_is_clear() {
|
||||
if self.0.txstatus().read().wrrdy().bit_is_clear() {
|
||||
return Err(nb::Error::WouldBlock);
|
||||
}
|
||||
self.write_fifo_unchecked(data);
|
||||
@ -1052,7 +1057,7 @@ impl<Uart: Instance> Tx<Uart> {
|
||||
/// API.
|
||||
#[inline(always)]
|
||||
pub fn write_fifo_unchecked(&self, data: u32) {
|
||||
self.uart.data().write(|w| unsafe { w.bits(data) });
|
||||
self.0.data().write(|w| unsafe { w.bits(data) });
|
||||
}
|
||||
|
||||
pub fn into_async(self) -> TxAsync<Uart> {
|
||||
@ -1135,7 +1140,7 @@ impl<Uart: Instance> RxWithInterrupt<Uart> {
|
||||
|
||||
#[inline(always)]
|
||||
pub fn uart(&self) -> &Uart {
|
||||
&self.0.uart
|
||||
&self.0 .0
|
||||
}
|
||||
|
||||
/// This function is used together with the [Self::on_interrupt_max_size_or_timeout_based]
|
||||
|
@ -1,18 +1,16 @@
|
||||
//! # Async UART reception functionality for the VA108xx family.
|
||||
//! # Async UART reception functionality for the VA416xx family.
|
||||
//!
|
||||
//! This module provides the [RxAsync] and [RxAsyncSharedConsumer] struct which both implement the
|
||||
//! This module provides the [RxAsync] and [RxAsyncOverwriting] struct which both implement the
|
||||
//! [embedded_io_async::Read] trait.
|
||||
//! This trait allows for asynchronous reception of data streams. Please note that this module does
|
||||
//! not specify/declare the interrupt handlers which must be provided for async support to work.
|
||||
//! However, it provides four interrupt handlers:
|
||||
//! However, it provides two interrupt handlers:
|
||||
//!
|
||||
//! - [on_interrupt_uart_a]
|
||||
//! - [on_interrupt_uart_b]
|
||||
//! - [on_interrupt_uart_a_overwriting]
|
||||
//! - [on_interrupt_uart_b_overwriting]
|
||||
//! - [on_interrupt_rx]
|
||||
//! - [on_interrupt_rx_overwriting]
|
||||
//!
|
||||
//! The first two are used for the [RxAsync] struct, while the latter two are used with the
|
||||
//! [RxAsyncSharedConsumer] struct. The later two will overwrite old values in the used ring buffer.
|
||||
//! [RxAsyncOverwriting] struct. The later two will overwrite old values in the used ring buffer.
|
||||
//!
|
||||
//! Error handling is performed in the user interrupt handler by checking the [AsyncUartErrors]
|
||||
//! structure returned by the interrupt handlers.
|
||||
@ -25,11 +23,10 @@ use core::{cell::RefCell, convert::Infallible, future::Future, sync::atomic::Ord
|
||||
use critical_section::Mutex;
|
||||
use embassy_sync::waitqueue::AtomicWaker;
|
||||
use embedded_io::ErrorType;
|
||||
use heapless::spsc::Consumer;
|
||||
use portable_atomic::AtomicBool;
|
||||
use va108xx as pac;
|
||||
use va108xx::uarta as uart_base;
|
||||
|
||||
use super::{Instance, Rx, RxError, UartErrors};
|
||||
use super::{Bank, Instance, Rx, RxError, UartErrors};
|
||||
|
||||
static UART_RX_WAKERS: [AtomicWaker; 2] = [const { AtomicWaker::new() }; 2];
|
||||
static RX_READ_ACTIVE: [AtomicBool; 2] = [const { AtomicBool::new(false) }; 2];
|
||||
@ -72,7 +69,7 @@ pub struct AsyncUartErrors {
|
||||
pub uart_errors: UartErrors,
|
||||
}
|
||||
|
||||
fn on_interrupt_handle_rx_errors<Uart: Instance>(uart: &Uart) -> Option<UartErrors> {
|
||||
fn on_interrupt_handle_rx_errors(uart: &'static uart_base::RegisterBlock) -> Option<UartErrors> {
|
||||
let rx_status = uart.rxstatus().read();
|
||||
if rx_status.rxovr().bit_is_set()
|
||||
|| rx_status.rxfrm().bit_is_set()
|
||||
@ -94,81 +91,65 @@ fn on_interrupt_handle_rx_errors<Uart: Instance>(uart: &Uart) -> Option<UartErro
|
||||
None
|
||||
}
|
||||
|
||||
fn on_interrupt_rx_common_post_processing<Uart: Instance>(
|
||||
uart: &Uart,
|
||||
fn on_interrupt_rx_common_post_processing(
|
||||
bank: Bank,
|
||||
rx_enabled: bool,
|
||||
read_some_data: bool,
|
||||
irq_end: u32,
|
||||
) -> Option<UartErrors> {
|
||||
let idx = bank as usize;
|
||||
if read_some_data {
|
||||
RX_HAS_DATA[Uart::IDX as usize].store(true, Ordering::Relaxed);
|
||||
if RX_READ_ACTIVE[Uart::IDX as usize].load(Ordering::Relaxed) {
|
||||
UART_RX_WAKERS[Uart::IDX as usize].wake();
|
||||
RX_HAS_DATA[idx].store(true, Ordering::Relaxed);
|
||||
if RX_READ_ACTIVE[idx].load(Ordering::Relaxed) {
|
||||
UART_RX_WAKERS[idx].wake();
|
||||
}
|
||||
}
|
||||
|
||||
let mut errors = None;
|
||||
let uart_regs = unsafe { bank.reg_block() };
|
||||
// Check for RX errors
|
||||
if rx_enabled {
|
||||
errors = on_interrupt_handle_rx_errors(uart);
|
||||
errors = on_interrupt_handle_rx_errors(uart_regs);
|
||||
}
|
||||
|
||||
// Clear the interrupt status bits
|
||||
uart.irq_clr().write(|w| unsafe { w.bits(irq_end) });
|
||||
uart_regs.irq_clr().write(|w| unsafe { w.bits(irq_end) });
|
||||
errors
|
||||
}
|
||||
|
||||
/// Interrupt handler for UART A.
|
||||
/// Interrupt handler with overwriting behaviour when the ring buffer is full.
|
||||
///
|
||||
/// Should be called in the user interrupt handler to enable
|
||||
/// asynchronous reception. This variant will overwrite old data in the ring buffer in case
|
||||
/// the ring buffer is full.
|
||||
pub fn on_interrupt_uart_a_overwriting<const N: usize>(
|
||||
pub fn on_interrupt_rx_overwriting<const N: usize>(
|
||||
bank: Bank,
|
||||
prod: &mut heapless::spsc::Producer<u8, N>,
|
||||
shared_consumer: &Mutex<RefCell<Option<heapless::spsc::Consumer<'static, u8, N>>>>,
|
||||
) -> Result<(), AsyncUartErrors> {
|
||||
on_interrupt_rx_async_heapless_queue_overwriting(
|
||||
unsafe { pac::Uarta::steal() },
|
||||
prod,
|
||||
shared_consumer,
|
||||
)
|
||||
on_interrupt_rx_async_heapless_queue_overwriting(bank, prod, shared_consumer)
|
||||
}
|
||||
|
||||
/// Interrupt handler for UART B.
|
||||
///
|
||||
/// Should be called in the user interrupt handler to enable
|
||||
/// asynchronous reception. This variant will overwrite old data in the ring buffer in case
|
||||
/// the ring buffer is full.
|
||||
pub fn on_interrupt_uart_b_overwriting<const N: usize>(
|
||||
pub fn on_interrupt_rx_async_heapless_queue_overwriting<const N: usize>(
|
||||
bank: Bank,
|
||||
prod: &mut heapless::spsc::Producer<u8, N>,
|
||||
shared_consumer: &Mutex<RefCell<Option<heapless::spsc::Consumer<'static, u8, N>>>>,
|
||||
) -> Result<(), AsyncUartErrors> {
|
||||
on_interrupt_rx_async_heapless_queue_overwriting(
|
||||
unsafe { pac::Uartb::steal() },
|
||||
prod,
|
||||
shared_consumer,
|
||||
)
|
||||
}
|
||||
|
||||
pub fn on_interrupt_rx_async_heapless_queue_overwriting<Uart: Instance, const N: usize>(
|
||||
uart: Uart,
|
||||
prod: &mut heapless::spsc::Producer<u8, N>,
|
||||
shared_consumer: &Mutex<RefCell<Option<heapless::spsc::Consumer<'static, u8, N>>>>,
|
||||
) -> Result<(), AsyncUartErrors> {
|
||||
let irq_end = uart.irq_end().read();
|
||||
let enb_status = uart.enable().read();
|
||||
let uart_regs = unsafe { bank.reg_block() };
|
||||
let irq_end = uart_regs.irq_end().read();
|
||||
let enb_status = uart_regs.enable().read();
|
||||
let rx_enabled = enb_status.rxenable().bit_is_set();
|
||||
let mut read_some_data = false;
|
||||
let mut queue_overflow = false;
|
||||
|
||||
// Half-Full interrupt. We have a guaranteed amount of data we can read.
|
||||
if irq_end.irq_rx().bit_is_set() {
|
||||
let available_bytes = uart.rxfifoirqtrg().read().bits() as usize;
|
||||
let available_bytes = uart_regs.rxfifoirqtrg().read().bits() as usize;
|
||||
|
||||
// If this interrupt bit is set, the trigger level is available at the very least.
|
||||
// Read everything as fast as possible
|
||||
for _ in 0..available_bytes {
|
||||
let byte = uart.data().read().bits();
|
||||
let byte = uart_regs.data().read().bits();
|
||||
if !prod.ready() {
|
||||
queue_overflow = true;
|
||||
critical_section::with(|cs| {
|
||||
@ -183,9 +164,9 @@ pub fn on_interrupt_rx_async_heapless_queue_overwriting<Uart: Instance, const N:
|
||||
|
||||
// Timeout, empty the FIFO completely.
|
||||
if irq_end.irq_rx_to().bit_is_set() {
|
||||
while uart.rxstatus().read().rdavl().bit_is_set() {
|
||||
while uart_regs.rxstatus().read().rdavl().bit_is_set() {
|
||||
// While there is data in the FIFO, write it into the reception buffer
|
||||
let byte = uart.data().read().bits();
|
||||
let byte = uart_regs.data().read().bits();
|
||||
if !prod.ready() {
|
||||
queue_overflow = true;
|
||||
critical_section::with(|cs| {
|
||||
@ -199,7 +180,7 @@ pub fn on_interrupt_rx_async_heapless_queue_overwriting<Uart: Instance, const N:
|
||||
}
|
||||
|
||||
let uart_errors =
|
||||
on_interrupt_rx_common_post_processing(&uart, rx_enabled, read_some_data, irq_end.bits());
|
||||
on_interrupt_rx_common_post_processing(bank, rx_enabled, read_some_data, irq_end.bits());
|
||||
if uart_errors.is_some() || queue_overflow {
|
||||
return Err(AsyncUartErrors {
|
||||
queue_overflow,
|
||||
@ -209,29 +190,21 @@ pub fn on_interrupt_rx_async_heapless_queue_overwriting<Uart: Instance, const N:
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Interrupt handler for UART A.
|
||||
/// Interrupt handler for asynchronous RX operations.
|
||||
///
|
||||
/// Should be called in the user interrupt handler to enable asynchronous reception.
|
||||
pub fn on_interrupt_uart_a<const N: usize>(
|
||||
pub fn on_interrupt_rx<const N: usize>(
|
||||
bank: Bank,
|
||||
prod: &mut heapless::spsc::Producer<'_, u8, N>,
|
||||
) -> Result<(), AsyncUartErrors> {
|
||||
on_interrupt_rx_async_heapless_queue(unsafe { pac::Uarta::steal() }, prod)
|
||||
on_interrupt_rx_async_heapless_queue(bank, prod)
|
||||
}
|
||||
|
||||
/// Interrupt handler for UART B.
|
||||
///
|
||||
/// Should be called in the user interrupt handler to enable asynchronous reception.
|
||||
pub fn on_interrupt_uart_b<const N: usize>(
|
||||
pub fn on_interrupt_rx_async_heapless_queue<const N: usize>(
|
||||
bank: Bank,
|
||||
prod: &mut heapless::spsc::Producer<'_, u8, N>,
|
||||
) -> Result<(), AsyncUartErrors> {
|
||||
on_interrupt_rx_async_heapless_queue(unsafe { pac::Uartb::steal() }, prod)
|
||||
}
|
||||
|
||||
pub fn on_interrupt_rx_async_heapless_queue<Uart: Instance, const N: usize>(
|
||||
uart: Uart,
|
||||
prod: &mut heapless::spsc::Producer<'_, u8, N>,
|
||||
) -> Result<(), AsyncUartErrors> {
|
||||
//let uart = unsafe { Uart::steal() };
|
||||
let uart = unsafe { bank.reg_block() };
|
||||
let irq_end = uart.irq_end().read();
|
||||
let enb_status = uart.enable().read();
|
||||
let rx_enabled = enb_status.rxenable().bit_is_set();
|
||||
@ -268,7 +241,7 @@ pub fn on_interrupt_rx_async_heapless_queue<Uart: Instance, const N: usize>(
|
||||
}
|
||||
|
||||
let uart_errors =
|
||||
on_interrupt_rx_common_post_processing(&uart, rx_enabled, read_some_data, irq_end.bits());
|
||||
on_interrupt_rx_common_post_processing(bank, rx_enabled, read_some_data, irq_end.bits());
|
||||
if uart_errors.is_some() || queue_overflow {
|
||||
return Err(AsyncUartErrors {
|
||||
queue_overflow,
|
||||
@ -286,24 +259,32 @@ impl Drop for ActiveReadGuard {
|
||||
}
|
||||
}
|
||||
|
||||
/// Core data structure to allow asynchronous UART reception.
|
||||
///
|
||||
/// If the ring buffer becomes full, data will be lost.
|
||||
pub struct RxAsync<Uart: Instance, const N: usize> {
|
||||
struct RxAsyncInner<Uart: Instance, const N: usize> {
|
||||
rx: Rx<Uart>,
|
||||
pub queue: heapless::spsc::Consumer<'static, u8, N>,
|
||||
}
|
||||
|
||||
/// Core data structure to allow asynchronous UART reception.
|
||||
///
|
||||
/// If the ring buffer becomes full, data will be lost.
|
||||
pub struct RxAsync<Uart: Instance, const N: usize>(Option<RxAsyncInner<Uart, N>>);
|
||||
|
||||
impl<Uart: Instance, const N: usize> ErrorType for RxAsync<Uart, N> {
|
||||
/// Error reporting is done using the result of the interrupt functions.
|
||||
type Error = Infallible;
|
||||
}
|
||||
|
||||
fn stop_async_rx<Uart: Instance>(rx: &mut Rx<Uart>) {
|
||||
rx.disable_interrupts();
|
||||
rx.disable();
|
||||
rx.clear_fifo();
|
||||
}
|
||||
|
||||
impl<Uart: Instance, const N: usize> RxAsync<Uart, N> {
|
||||
/// Create a new asynchronous receiver.
|
||||
///
|
||||
/// The passed [heapless::spsc::Consumer] will be used to asynchronously receive data which
|
||||
/// is filled by the interrupt handler.
|
||||
/// is filled by the interrupt handler [on_interrupt_rx].
|
||||
pub fn new(mut rx: Rx<Uart>, queue: heapless::spsc::Consumer<'static, u8, N>) -> Self {
|
||||
rx.disable_interrupts();
|
||||
rx.disable();
|
||||
@ -313,7 +294,23 @@ impl<Uart: Instance, const N: usize> RxAsync<Uart, N> {
|
||||
rx.enable_interrupts();
|
||||
rx.enable();
|
||||
});
|
||||
Self { rx, queue }
|
||||
Self(Some(RxAsyncInner { rx, queue }))
|
||||
}
|
||||
|
||||
pub fn stop(&mut self) {
|
||||
stop_async_rx(&mut self.0.as_mut().unwrap().rx);
|
||||
}
|
||||
|
||||
pub fn release(mut self) -> (Rx<Uart>, heapless::spsc::Consumer<'static, u8, N>) {
|
||||
self.stop();
|
||||
let inner = self.0.take().unwrap();
|
||||
(inner.rx, inner.queue)
|
||||
}
|
||||
}
|
||||
|
||||
impl<Uart: Instance, const N: usize> Drop for RxAsync<Uart, N> {
|
||||
fn drop(&mut self) {
|
||||
self.stop();
|
||||
}
|
||||
}
|
||||
|
||||
@ -321,7 +318,7 @@ impl<Uart: Instance, const N: usize> embedded_io_async::Read for RxAsync<Uart, N
|
||||
async fn read(&mut self, buf: &mut [u8]) -> Result<usize, Self::Error> {
|
||||
// Need to wait for the IRQ to read data and set this flag. If the queue is not
|
||||
// empty, we can read data immediately.
|
||||
if self.queue.len() == 0 {
|
||||
if self.0.as_ref().unwrap().queue.len() == 0 {
|
||||
RX_HAS_DATA[Uart::IDX as usize].store(false, Ordering::Relaxed);
|
||||
}
|
||||
let _guard = ActiveReadGuard(Uart::IDX as usize);
|
||||
@ -333,33 +330,38 @@ impl<Uart: Instance, const N: usize> embedded_io_async::Read for RxAsync<Uart, N
|
||||
}
|
||||
data_to_read
|
||||
};
|
||||
let fut = RxFuture::new(&mut self.rx);
|
||||
let mut_ref = self.0.as_mut().unwrap();
|
||||
let fut = RxFuture::new(&mut mut_ref.rx);
|
||||
// Data is available, so read that data immediately.
|
||||
let read_data = handle_data_in_queue(&mut self.queue);
|
||||
let read_data = handle_data_in_queue(&mut mut_ref.queue);
|
||||
if read_data > 0 {
|
||||
return Ok(read_data);
|
||||
}
|
||||
// Await data.
|
||||
let _ = fut.await;
|
||||
Ok(handle_data_in_queue(&mut self.queue))
|
||||
Ok(handle_data_in_queue(&mut mut_ref.queue))
|
||||
}
|
||||
}
|
||||
|
||||
struct RxAsyncOverwritingInner<Uart: Instance, const N: usize> {
|
||||
rx: Rx<Uart>,
|
||||
pub shared_consumer: &'static Mutex<RefCell<Option<heapless::spsc::Consumer<'static, u8, N>>>>,
|
||||
}
|
||||
|
||||
/// Core data structure to allow asynchronous UART reception.
|
||||
///
|
||||
/// If the ring buffer becomes full, the oldest data will be overwritten when using the
|
||||
/// [on_interrupt_uart_a_overwriting] and [on_interrupt_uart_b_overwriting] interrupt handlers.
|
||||
pub struct RxAsyncSharedConsumer<Uart: Instance, const N: usize> {
|
||||
rx: Rx<Uart>,
|
||||
queue: &'static Mutex<RefCell<Option<Consumer<'static, u8, N>>>>,
|
||||
}
|
||||
/// [on_interrupt_rx_overwriting] interrupt handlers.
|
||||
pub struct RxAsyncOverwriting<Uart: Instance, const N: usize>(
|
||||
Option<RxAsyncOverwritingInner<Uart, N>>,
|
||||
);
|
||||
|
||||
impl<Uart: Instance, const N: usize> ErrorType for RxAsyncSharedConsumer<Uart, N> {
|
||||
impl<Uart: Instance, const N: usize> ErrorType for RxAsyncOverwriting<Uart, N> {
|
||||
/// Error reporting is done using the result of the interrupt functions.
|
||||
type Error = Infallible;
|
||||
}
|
||||
|
||||
impl<Uart: Instance, const N: usize> RxAsyncSharedConsumer<Uart, N> {
|
||||
impl<Uart: Instance, const N: usize> RxAsyncOverwriting<Uart, N> {
|
||||
/// Create a new asynchronous receiver.
|
||||
///
|
||||
/// The passed shared [heapless::spsc::Consumer] will be used to asynchronously receive data
|
||||
@ -367,7 +369,7 @@ impl<Uart: Instance, const N: usize> RxAsyncSharedConsumer<Uart, N> {
|
||||
/// interrupt handler to overwrite old data.
|
||||
pub fn new(
|
||||
mut rx: Rx<Uart>,
|
||||
queue: &'static Mutex<RefCell<Option<heapless::spsc::Consumer<'static, u8, N>>>>,
|
||||
shared_consumer: &'static Mutex<RefCell<Option<heapless::spsc::Consumer<'static, u8, N>>>>,
|
||||
) -> Self {
|
||||
rx.disable_interrupts();
|
||||
rx.disable();
|
||||
@ -377,25 +379,44 @@ impl<Uart: Instance, const N: usize> RxAsyncSharedConsumer<Uart, N> {
|
||||
rx.enable_interrupts();
|
||||
rx.enable();
|
||||
});
|
||||
Self { rx, queue }
|
||||
Self(Some(RxAsyncOverwritingInner {
|
||||
rx,
|
||||
shared_consumer,
|
||||
}))
|
||||
}
|
||||
|
||||
pub fn stop(&mut self) {
|
||||
stop_async_rx(&mut self.0.as_mut().unwrap().rx);
|
||||
}
|
||||
|
||||
pub fn release(mut self) -> Rx<Uart> {
|
||||
self.stop();
|
||||
let inner = self.0.take().unwrap();
|
||||
inner.rx
|
||||
}
|
||||
}
|
||||
|
||||
impl<Uart: Instance, const N: usize> embedded_io_async::Read for RxAsyncSharedConsumer<Uart, N> {
|
||||
impl<Uart: Instance, const N: usize> Drop for RxAsyncOverwriting<Uart, N> {
|
||||
fn drop(&mut self) {
|
||||
self.stop();
|
||||
}
|
||||
}
|
||||
|
||||
impl<Uart: Instance, const N: usize> embedded_io_async::Read for RxAsyncOverwriting<Uart, N> {
|
||||
async fn read(&mut self, buf: &mut [u8]) -> Result<usize, Self::Error> {
|
||||
// Need to wait for the IRQ to read data and set this flag. If the queue is not
|
||||
// empty, we can read data immediately.
|
||||
|
||||
critical_section::with(|cs| {
|
||||
let queue = self.queue.borrow(cs);
|
||||
let queue = self.0.as_ref().unwrap().shared_consumer.borrow(cs);
|
||||
if queue.borrow().as_ref().unwrap().len() == 0 {
|
||||
RX_HAS_DATA[Uart::IDX as usize].store(false, Ordering::Relaxed);
|
||||
}
|
||||
});
|
||||
let _guard = ActiveReadGuard(Uart::IDX as usize);
|
||||
let mut handle_data_in_queue = || {
|
||||
let mut handle_data_in_queue = |inner: &mut RxAsyncOverwritingInner<Uart, N>| {
|
||||
critical_section::with(|cs| {
|
||||
let mut consumer_ref = self.queue.borrow(cs).borrow_mut();
|
||||
let mut consumer_ref = inner.shared_consumer.borrow(cs).borrow_mut();
|
||||
let consumer = consumer_ref.as_mut().unwrap();
|
||||
let data_to_read = consumer.len().min(buf.len());
|
||||
for byte in buf.iter_mut().take(data_to_read) {
|
||||
@ -405,15 +426,15 @@ impl<Uart: Instance, const N: usize> embedded_io_async::Read for RxAsyncSharedCo
|
||||
data_to_read
|
||||
})
|
||||
};
|
||||
let fut = RxFuture::new(&mut self.rx);
|
||||
let fut = RxFuture::new(&mut self.0.as_mut().unwrap().rx);
|
||||
// Data is available, so read that data immediately.
|
||||
let read_data = handle_data_in_queue();
|
||||
let read_data = handle_data_in_queue(self.0.as_mut().unwrap());
|
||||
if read_data > 0 {
|
||||
return Ok(read_data);
|
||||
}
|
||||
// Await data.
|
||||
let _ = fut.await;
|
||||
let read_data = handle_data_in_queue();
|
||||
let read_data = handle_data_in_queue(self.0.as_mut().unwrap());
|
||||
Ok(read_data)
|
||||
}
|
||||
}
|
||||
|
@ -3,13 +3,10 @@
|
||||
//! This module provides the [TxAsync] struct which implements the [embedded_io_async::Write] trait.
|
||||
//! This trait allows for asynchronous sending of data streams. Please note that this module does
|
||||
//! not specify/declare the interrupt handlers which must be provided for async support to work.
|
||||
//! However, it provides two interrupt handlers:
|
||||
//! However, it the [on_interrupt_tx] interrupt handler.
|
||||
//!
|
||||
//! - [on_interrupt_uart_a_tx]
|
||||
//! - [on_interrupt_uart_b_tx]
|
||||
//!
|
||||
//! Those should be called in ALL user interrupt handlers which handle UART TX interrupts,
|
||||
//! depending on which UARTs are used.
|
||||
//! This handler should be called in ALL user interrupt handlers which handle UART TX interrupts
|
||||
//! for a given UART bank.
|
||||
//!
|
||||
//! # Example
|
||||
//!
|
||||
@ -30,21 +27,14 @@ static TX_CONTEXTS: [Mutex<RefCell<TxContext>>; 2] =
|
||||
// critical section.
|
||||
static TX_DONE: [AtomicBool; 2] = [const { AtomicBool::new(false) }; 2];
|
||||
|
||||
/// This is a generic interrupt handler to handle asynchronous UART TX operations. The user
|
||||
/// has to call this once in the interrupt handler responsible for UART A TX interrupts for
|
||||
/// asynchronous operations to work.
|
||||
pub fn on_interrupt_uart_a_tx() {
|
||||
on_interrupt_uart_tx(unsafe { pac::Uarta::steal() });
|
||||
}
|
||||
|
||||
/// This is a generic interrupt handler to handle asynchronous UART TX operations. The user
|
||||
/// has to call this once in the interrupt handler responsible for UART B TX interrupts for
|
||||
/// asynchronous operations to work.
|
||||
pub fn on_interrupt_uart_b_tx() {
|
||||
on_interrupt_uart_tx(unsafe { pac::Uartb::steal() });
|
||||
}
|
||||
|
||||
fn on_interrupt_uart_tx<Uart: Instance>(uart: Uart) {
|
||||
/// This is a generic interrupt handler to handle asynchronous UART TX operations for a given
|
||||
/// UART bank.
|
||||
///
|
||||
/// The user has to call this once in the interrupt handler responsible for the TX interrupts on
|
||||
/// the given UART bank.
|
||||
pub fn on_interrupt_tx(bank: Bank) {
|
||||
let uart = unsafe { bank.reg_block() };
|
||||
let idx = bank as usize;
|
||||
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() {
|
||||
@ -54,7 +44,7 @@ fn on_interrupt_uart_tx<Uart: Instance>(uart: Uart) {
|
||||
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_CONTEXTS[Uart::IDX as usize].borrow(cs);
|
||||
let context_ref = TX_CONTEXTS[idx].borrow(cs);
|
||||
*context_ref.borrow()
|
||||
});
|
||||
context.tx_overrun = unexpected_overrun;
|
||||
@ -67,12 +57,12 @@ fn on_interrupt_uart_tx<Uart: Instance>(uart: Uart) {
|
||||
uart.enable().modify(|_, w| w.txenable().clear_bit());
|
||||
// Write back updated context structure.
|
||||
critical_section::with(|cs| {
|
||||
let context_ref = TX_CONTEXTS[Uart::IDX as usize].borrow(cs);
|
||||
let context_ref = TX_CONTEXTS[idx].borrow(cs);
|
||||
*context_ref.borrow_mut() = context;
|
||||
});
|
||||
// Transfer is done.
|
||||
TX_DONE[Uart::IDX as usize].store(true, core::sync::atomic::Ordering::Relaxed);
|
||||
UART_TX_WAKERS[Uart::IDX as usize].wake();
|
||||
TX_DONE[idx].store(true, core::sync::atomic::Ordering::Relaxed);
|
||||
UART_TX_WAKERS[idx].wake();
|
||||
return;
|
||||
}
|
||||
// Safety: We documented that the user provided slice must outlive the future, so we convert
|
||||
@ -92,7 +82,7 @@ fn on_interrupt_uart_tx<Uart: Instance>(uart: Uart) {
|
||||
|
||||
// Write back updated context structure.
|
||||
critical_section::with(|cs| {
|
||||
let context_ref = TX_CONTEXTS[Uart::IDX as usize].borrow(cs);
|
||||
let context_ref = TX_CONTEXTS[idx].borrow(cs);
|
||||
*context_ref.borrow_mut() = context;
|
||||
});
|
||||
}
|
||||
|
Reference in New Issue
Block a user