Merge pull request 'UART error handling update' (#64) from uart-error-handling-update into main

Reviewed-on: #64
This commit is contained in:
Robin Müller 2025-03-07 17:14:02 +01:00
commit a44ba7b8a5
13 changed files with 104 additions and 141 deletions

View File

@ -28,8 +28,8 @@ embassy-executor = { version = "0.7", features = [
"executor-interrupt" "executor-interrupt"
]} ]}
va416xx-hal = { version = "0.4.1" } va416xx-hal = { version = "0.5", path = "../../va416xx-hal" }
va416xx-embassy = { version = "0.1", default-features = false } va416xx-embassy = { version = "0.1", default-features = false, path = "../../va416xx-embassy" }
[features] [features]
default = ["ticks-hz-1_000", "va416xx-embassy/irq-tim14-tim15"] default = ["ticks-hz-1_000", "va416xx-embassy/irq-tim14-tim15"]

View File

@ -132,7 +132,7 @@ fn UART0_RX() {
RX.lock(|static_rx| { RX.lock(|static_rx| {
let mut rx_borrow = static_rx.borrow_mut(); let mut rx_borrow = static_rx.borrow_mut();
let rx_mut_ref = rx_borrow.as_mut().unwrap(); let rx_mut_ref = rx_borrow.as_mut().unwrap();
let result = rx_mut_ref.irq_handler(&mut buf); let result = rx_mut_ref.on_interrupt(&mut buf);
read_len = result.bytes_read; read_len = result.bytes_read;
if result.errors.is_some() { if result.errors.is_some() {
errors = result.errors; errors = result.errors;

View File

@ -11,7 +11,7 @@ rtt-target = { version = "0.6" }
rtic-sync = { version = "1.3", features = ["defmt-03"] } rtic-sync = { version = "1.3", features = ["defmt-03"] }
panic-rtt-target = { version = "0.2" } panic-rtt-target = { version = "0.2" }
va416xx-hal = { version = "0.4", features = ["va41630"] } va416xx-hal = { version = "0.5", features = ["va41630"], path = "../../va416xx-hal" }
[dependencies.rtic] [dependencies.rtic]
version = "2" version = "2"

View File

@ -16,7 +16,7 @@ embedded-io = "0.6"
panic-halt = "1" panic-halt = "1"
accelerometer = "0.12" accelerometer = "0.12"
va416xx-hal = { version = "0.4", features = ["va41630"] } va416xx-hal = { version = "0.5", features = ["va41630"], path = "../../va416xx-hal" }
[dependencies.vorago-peb1] [dependencies.vorago-peb1]
path = "../../vorago-peb1" path = "../../vorago-peb1"

View File

@ -8,6 +8,14 @@ and this project adheres to [Semantic Versioning](http://semver.org/).
## [unreleased] ## [unreleased]
## [v0.1.1] 2025-03-07
- Bumped allowed HAL dependency to v0.5
## [v0.1.0] 2025-02-18 ## [v0.1.0] 2025-02-18
Initial release Initial release
[unreleased]: https://egit.irs.uni-stuttgart.de/rust/va416xx-rs/compare/va416xx-embassy-v0.1.1...HEAD
[v0.1.1]: https://egit.irs.uni-stuttgart.de/rust/va416xx-rs/compare/va416xx-embassy-v0.1.0...va416xx-embassy-v0.1.1
[v0.1.0]: https://egit.irs.uni-stuttgart.de/rust/va416xx-rs/src/tag/va416xx-embassy-v0.1.0

View File

@ -1,6 +1,6 @@
[package] [package]
name = "va416xx-embassy" name = "va416xx-embassy"
version = "0.1.0" version = "0.1.1"
edition = "2021" edition = "2021"
authors = ["Robin Mueller <muellerr@irs.uni-stuttgart.de>"] authors = ["Robin Mueller <muellerr@irs.uni-stuttgart.de>"]
description = "Embassy-rs support for the Vorago VA416xx family of microcontrollers" description = "Embassy-rs support for the Vorago VA416xx family of microcontrollers"
@ -21,7 +21,7 @@ portable-atomic = "1"
once_cell = { version = "1", default-features = false, features = ["critical-section"] } once_cell = { version = "1", default-features = false, features = ["critical-section"] }
va416xx-hal = { version = "0.4.1" } va416xx-hal = { version = "0.5", path = "../va416xx-hal" }
[features] [features]
default = ["irq-tim14-tim15"] default = ["irq-tim14-tim15"]

View File

@ -8,6 +8,14 @@ and this project adheres to [Semantic Versioning](http://semver.org/).
# [unreleased] # [unreleased]
# [v0.5.0] 2025-03-07
- Bugfix for I2C `TimingCfg::reg`
- Simplified UART error handling. All APIs are now infallible because writing to a FIFO or
reading from a FIFO never fails. Users can either poll errors using `Rx::poll_errors` or
`Uart::poll_rx_errors` / `UartBase::poll_rx_errors`, or detect errors using the provided
interrupt handlers.
# [v0.4.1] 2025-02-18 # [v0.4.1] 2025-02-18
- Chip selection is not enforced anymore, but advised through documentation. This makes using - Chip selection is not enforced anymore, but advised through documentation. This makes using
@ -101,3 +109,11 @@ and this project adheres to [Semantic Versioning](http://semver.org/).
# [v0.1.0] 2024-07-01 # [v0.1.0] 2024-07-01
- Initial release with basic HAL drivers - Initial release with basic HAL drivers
[unreleased]: https://egit.irs.uni-stuttgart.de/rust/va416xx-rs/compare/va416xx-hal-v0.5.0...HEAD
[v0.5.0]: https://egit.irs.uni-stuttgart.de/rust/va416xx-rs/compare/va416xx-hal-v0.4.1...va416xx-hal-v0.5.0
[v0.4.1]: https://egit.irs.uni-stuttgart.de/rust/va416xx-rs/compare/va416xx-hal-v0.4.0...va416xx-hal-v0.4.1
[v0.4.0]: https://egit.irs.uni-stuttgart.de/rust/va416xx-rs/compare/va416xx-hal-v0.3.0...va416xx-hal-v0.4.0
[v0.3.0]: https://egit.irs.uni-stuttgart.de/rust/va416xx-rs/compare/va416xx-hal-v0.2.0...va108xx-hal-v0.3.0
[v0.2.0]: https://egit.irs.uni-stuttgart.de/rust/va416xx-rs/compare/va416xx-hal-v0.1.0...va108xx-hal-v0.2.0
[v0.1.0]: https://egit.irs.uni-stuttgart.de/rust/va416xx-rs/src/tag/va416xx-hal-v0.1.0

View File

@ -1,6 +1,6 @@
[package] [package]
name = "va416xx-hal" name = "va416xx-hal"
version = "0.4.1" version = "0.5.0"
authors = ["Robin Mueller <muellerr@irs.uni-stuttgart.de>"] authors = ["Robin Mueller <muellerr@irs.uni-stuttgart.de>"]
edition = "2021" edition = "2021"
description = "HAL for the Vorago VA416xx family of MCUs" description = "HAL for the Vorago VA416xx family of MCUs"

View File

@ -212,13 +212,13 @@ impl TimingCfg {
} }
pub fn reg(&self) -> u32 { pub fn reg(&self) -> u32 {
(self.tbuf as u32) << 28 ((self.tbuf as u32) << 28)
| (self.thd_sta as u32) << 24 | ((self.thd_sta as u32) << 24)
| (self.tsu_sta as u32) << 20 | ((self.tsu_sta as u32) << 20)
| (self.tsu_sto as u32) << 16 | ((self.tsu_sto as u32) << 16)
| (self.tlow as u32) << 12 | ((self.tlow as u32) << 12)
| (self.thigh as u32) << 8 | ((self.thigh as u32) << 8)
| (self.tf as u32) << 4 | ((self.tf as u32) << 4)
| (self.tr as u32) | (self.tr as u32)
} }
} }
@ -390,7 +390,7 @@ impl<I2c: Instance> I2cBase<I2c> {
if let Some(max_words) = max_words { if let Some(max_words) = max_words {
self.i2c self.i2c
.s0_maxwords() .s0_maxwords()
.write(|w| unsafe { w.bits(1 << 31 | max_words as u32) }); .write(|w| unsafe { w.bits((1 << 31) | max_words as u32) });
} }
let (addr, addr_mode_mask) = Self::unwrap_addr(sl_cfg.addr); let (addr, addr_mode_mask) = Self::unwrap_addr(sl_cfg.addr);
// The first bit is the read/write value. Normally, both read and write are matched // The first bit is the read/write value. Normally, both read and write are matched
@ -451,7 +451,7 @@ impl<I2c: Instance> I2cBase<I2c> {
let clk_div = self.calc_clk_div(speed_mode)?; let clk_div = self.calc_clk_div(speed_mode)?;
self.i2c self.i2c
.clkscale() .clkscale()
.write(|w| unsafe { w.bits((speed_mode as u32) << 31 | clk_div as u32) }); .write(|w| unsafe { w.bits(((speed_mode as u32) << 31) | clk_div as u32) });
Ok(()) Ok(())
} }

View File

@ -82,56 +82,6 @@ impl RxPin<Uart2> for Pin<PF9, AltFunc1> {}
#[cfg_attr(feature = "defmt", derive(defmt::Format))] #[cfg_attr(feature = "defmt", derive(defmt::Format))]
pub struct TransferPendingError; pub struct TransferPendingError;
#[derive(Debug, PartialEq, Eq)]
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
pub enum RxError {
Overrun,
Framing,
Parity,
}
#[derive(Debug, PartialEq, Eq)]
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
pub enum Error {
Rx(RxError),
BreakCondition,
}
impl From<RxError> for Error {
fn from(value: RxError) -> Self {
Self::Rx(value)
}
}
impl embedded_io::Error for Error {
fn kind(&self) -> embedded_io::ErrorKind {
embedded_io::ErrorKind::Other
}
}
impl embedded_io::Error for RxError {
fn kind(&self) -> embedded_io::ErrorKind {
embedded_io::ErrorKind::Other
}
}
impl embedded_hal_nb::serial::Error for RxError {
fn kind(&self) -> embedded_hal_nb::serial::ErrorKind {
match self {
RxError::Overrun => embedded_hal_nb::serial::ErrorKind::Overrun,
RxError::Framing => embedded_hal_nb::serial::ErrorKind::FrameFormat,
RxError::Parity => embedded_hal_nb::serial::ErrorKind::Parity,
}
}
}
impl embedded_hal_nb::serial::Error for Error {
fn kind(&self) -> embedded_hal_nb::serial::ErrorKind {
match self {
Error::Rx(rx_error) => embedded_hal_nb::serial::Error::kind(rx_error),
Error::BreakCondition => embedded_hal_nb::serial::ErrorKind::Other,
}
}
}
#[derive(Debug, PartialEq, Eq, Copy, Clone)] #[derive(Debug, PartialEq, Eq, Copy, Clone)]
#[cfg_attr(feature = "defmt", derive(defmt::Format))] #[cfg_attr(feature = "defmt", derive(defmt::Format))]
pub enum Event { pub enum Event {
@ -626,22 +576,27 @@ impl<Uart: Instance> UartBase<Uart> {
self.uart self.uart
} }
/// Poll receiver errors.
pub fn poll_rx_errors(&self) -> Option<UartErrors> {
self.rx.poll_errors()
}
pub fn split(self) -> (Tx<Uart>, Rx<Uart>) { pub fn split(self) -> (Tx<Uart>, Rx<Uart>) {
(self.tx, self.rx) (self.tx, self.rx)
} }
} }
impl<UartInstance> embedded_io::ErrorType for UartBase<UartInstance> { impl<UartInstance> embedded_io::ErrorType for UartBase<UartInstance> {
type Error = Error; type Error = Infallible;
} }
impl<UartInstance> embedded_hal_nb::serial::ErrorType for UartBase<UartInstance> { impl<UartInstance> embedded_hal_nb::serial::ErrorType for UartBase<UartInstance> {
type Error = Error; type Error = Infallible;
} }
impl<Uart: Instance> embedded_hal_nb::serial::Read<u8> for UartBase<Uart> { impl<Uart: Instance> embedded_hal_nb::serial::Read<u8> for UartBase<Uart> {
fn read(&mut self) -> nb::Result<u8, Self::Error> { fn read(&mut self) -> nb::Result<u8, Self::Error> {
self.rx.read().map_err(|e| e.map(Error::Rx)) self.rx.read()
} }
} }
@ -729,6 +684,8 @@ impl<TxPinInst: TxPin<UartInstance>, RxPinInst: RxPin<UartInstance>, UartInstanc
delegate::delegate! { delegate::delegate! {
to self.inner { to self.inner {
/// Poll receiver errors.
pub fn poll_rx_errors(&self) -> Option<UartErrors>;
#[inline] #[inline]
pub fn enable_rx(&mut self); pub fn enable_rx(&mut self);
#[inline] #[inline]
@ -813,6 +770,23 @@ impl<Uart: Instance> Rx<Uart> {
&self.0 &self.0
} }
pub fn poll_errors(&self) -> Option<UartErrors> {
let mut errors = UartErrors::default();
let uart = unsafe { &(*Uart::ptr()) };
let status_reader = uart.rxstatus().read();
if status_reader.rxovr().bit_is_set() {
errors.overflow = true;
} else if status_reader.rxfrm().bit_is_set() {
errors.framing = true;
} else if status_reader.rxpar().bit_is_set() {
errors.parity = true;
} else {
return None;
};
Some(errors)
}
#[inline] #[inline]
pub fn clear_fifo(&self) { pub fn clear_fifo(&self) {
self.0.fifo_clr().write(|w| w.rxfifo().set_bit()); self.0.fifo_clr().write(|w| w.rxfifo().set_bit());
@ -874,34 +848,15 @@ impl<Uart: Instance> Rx<Uart> {
} }
impl<Uart> embedded_io::ErrorType for Rx<Uart> { impl<Uart> embedded_io::ErrorType for Rx<Uart> {
type Error = RxError; type Error = Infallible;
} }
impl<Uart> embedded_hal_nb::serial::ErrorType for Rx<Uart> { impl<Uart> embedded_hal_nb::serial::ErrorType for Rx<Uart> {
type Error = RxError; type Error = Infallible;
} }
impl<Uart: Instance> embedded_hal_nb::serial::Read<u8> for Rx<Uart> { impl<Uart: Instance> embedded_hal_nb::serial::Read<u8> for Rx<Uart> {
fn read(&mut self) -> nb::Result<u8, Self::Error> { fn read(&mut self) -> nb::Result<u8, Self::Error> {
let uart = unsafe { &(*Uart::ptr()) };
let status_reader = uart.rxstatus().read();
let err = if status_reader.rxovr().bit_is_set() {
Some(RxError::Overrun)
} else if status_reader.rxfrm().bit_is_set() {
Some(RxError::Framing)
} else if status_reader.rxpar().bit_is_set() {
Some(RxError::Parity)
} else {
None
};
if let Some(err) = err {
// The status code is always related to the next bit for the framing
// and parity status bits. We have to read the DATA register
// so that the next status reflects the next DATA word
// For overrun error, we read as well to clear the peripheral
self.read_fifo_unchecked();
return Err(err.into());
}
self.read_fifo().map(|val| (val & 0xff) as u8).map_err(|e| { self.read_fifo().map(|val| (val & 0xff) as u8).map_err(|e| {
if let nb::Error::Other(_) = e { if let nb::Error::Other(_) = e {
unreachable!() unreachable!()
@ -913,16 +868,18 @@ impl<Uart: Instance> embedded_hal_nb::serial::Read<u8> for Rx<Uart> {
impl<Uart: Instance> embedded_io::Read for Rx<Uart> { impl<Uart: Instance> embedded_io::Read for Rx<Uart> {
fn read(&mut self, buf: &mut [u8]) -> Result<usize, Self::Error> { fn read(&mut self, buf: &mut [u8]) -> Result<usize, Self::Error> {
if buf.is_empty() { let mut read = 0;
return Ok(0);
}
for byte in buf.iter_mut() { for byte in buf.iter_mut() {
let w = nb::block!(<Self as embedded_hal_nb::serial::Read<u8>>::read(self))?; match <Self as embedded_hal_nb::serial::Read<u8>>::read(self) {
*byte = w; Ok(w) => {
*byte = w;
read += 1;
}
Err(nb::Error::WouldBlock) => break,
}
} }
Ok(buf.len()) Ok(read)
} }
} }
@ -1182,7 +1139,7 @@ impl<Uart: Instance> RxWithInterrupt<Uart> {
/// This function will not disable the RX interrupts, so you don't need to call any other /// This function will not disable the RX interrupts, so you don't need to call any other
/// API after calling this function to continue emptying the FIFO. RX errors are handled /// API after calling this function to continue emptying the FIFO. RX errors are handled
/// as partial errors and are returned as part of the [IrqResult]. /// as partial errors and are returned as part of the [IrqResult].
pub fn irq_handler(&mut self, buf: &mut [u8; 16]) -> IrqResult { pub fn on_interrupt(&mut self, buf: &mut [u8; 16]) -> IrqResult {
let mut result = IrqResult::default(); let mut result = IrqResult::default();
let irq_end = self.uart().irq_end().read(); let irq_end = self.uart().irq_end().read();
@ -1203,15 +1160,10 @@ impl<Uart: Instance> RxWithInterrupt<Uart> {
// Timeout, empty the FIFO completely. // Timeout, empty the FIFO completely.
if irq_end.irq_rx_to().bit_is_set() { if irq_end.irq_rx_to().bit_is_set() {
loop { // While there is data in the FIFO, write it into the reception buffer
// While there is data in the FIFO, write it into the reception buffer while let Ok(byte) = self.0.read_fifo() {
let read_result = self.0.read(); buf[result.bytes_read] = byte as u8;
if let Some(byte) = self.read_handler(&mut result.errors, &read_result) { result.bytes_read += 1;
buf[result.bytes_read] = byte;
result.bytes_read += 1;
} else {
break;
}
} }
} }
@ -1239,7 +1191,7 @@ impl<Uart: Instance> RxWithInterrupt<Uart> {
/// If passed buffer is equal to or larger than the specified maximum length, an /// If passed buffer is equal to or larger than the specified maximum length, an
/// [BufferTooShortError] will be returned. Other RX errors are treated as partial errors /// [BufferTooShortError] will be returned. Other RX errors are treated as partial errors
/// and returned inside the [IrqResultMaxSizeOrTimeout] structure. /// and returned inside the [IrqResultMaxSizeOrTimeout] structure.
pub fn irq_handler_max_size_or_timeout_based( pub fn on_interrupt_max_size_or_timeout_based(
&mut self, &mut self,
context: &mut IrqContextTimeoutOrMaxSize, context: &mut IrqContextTimeoutOrMaxSize,
buf: &mut [u8], buf: &mut [u8],
@ -1288,12 +1240,13 @@ impl<Uart: Instance> RxWithInterrupt<Uart> {
if context.rx_idx == context.max_len { if context.rx_idx == context.max_len {
break; break;
} }
let read_result = self.0.read(); // While there is data in the FIFO, write it into the reception buffer
if let Some(byte) = self.read_handler(&mut result.errors, &read_result) { match self.0.read() {
buf[context.rx_idx] = byte; Ok(byte) => {
context.rx_idx += 1; buf[result.bytes_read] = byte;
} else { result.bytes_read += 1;
break; }
Err(_) => break,
} }
} }
self.irq_completion_handler_max_size_timeout(&mut result, context); self.irq_completion_handler_max_size_timeout(&mut result, context);
@ -1312,29 +1265,6 @@ impl<Uart: Instance> RxWithInterrupt<Uart> {
Ok(result) Ok(result)
} }
fn read_handler(
&self,
errors: &mut Option<UartErrors>,
read_res: &nb::Result<u8, RxError>,
) -> Option<u8> {
match read_res {
Ok(byte) => Some(*byte),
Err(nb::Error::WouldBlock) => None,
Err(nb::Error::Other(e)) => {
// Ensure `errors` is Some(IrqUartError), initializing if it's None
let err = errors.get_or_insert(UartErrors::default());
// Now we can safely modify fields inside `err`
match e {
RxError::Overrun => err.overflow = true,
RxError::Framing => err.framing = true,
RxError::Parity => err.parity = true,
}
None
}
}
}
fn check_for_errors(&self, errors: &mut Option<UartErrors>) { fn check_for_errors(&self, errors: &mut Option<UartErrors>) {
let rx_status = self.uart().rxstatus().read(); let rx_status = self.uart().rxstatus().read();

View File

@ -28,7 +28,7 @@ use va416xx::uart0 as uart_base;
use crate::enable_nvic_interrupt; use crate::enable_nvic_interrupt;
use super::{Bank, Instance, Rx, RxError, UartErrors}; use super::{Bank, Instance, Rx, UartErrors};
static UART_RX_WAKERS: [AtomicWaker; 3] = [const { AtomicWaker::new() }; 3]; static UART_RX_WAKERS: [AtomicWaker; 3] = [const { AtomicWaker::new() }; 3];
static RX_READ_ACTIVE: [AtomicBool; 3] = [const { AtomicBool::new(false) }; 3]; static RX_READ_ACTIVE: [AtomicBool; 3] = [const { AtomicBool::new(false) }; 3];
@ -48,7 +48,7 @@ impl RxFuture {
} }
impl Future for RxFuture { impl Future for RxFuture {
type Output = Result<(), RxError>; type Output = Result<(), Infallible>;
fn poll( fn poll(
self: core::pin::Pin<&mut Self>, self: core::pin::Pin<&mut Self>,

View File

@ -8,6 +8,10 @@ and this project adheres to [Semantic Versioning](http://semver.org/).
# [unreleased] # [unreleased]
# [v0.1.2] 2025-03-07
- Bump allowed HAL version to v0.5
# [v0.1.1] 2025-02-18 # [v0.1.1] 2025-02-18
- Bump allowed HAL version to v0.4 - Bump allowed HAL version to v0.4
@ -15,3 +19,8 @@ and this project adheres to [Semantic Versioning](http://semver.org/).
# [v0.1.0] 2024-10-01 # [v0.1.0] 2024-10-01
- Initial release - Initial release
[unreleased]: https://egit.irs.uni-stuttgart.de/rust/va416xx-rs/compare/vorago-peb1-v0.1.2...HEAD
[v0.1.2]: https://egit.irs.uni-stuttgart.de/rust/va416xx-rs/compare/vorago-peb1-v0.1.1...vorago-peb1-v0.1.2
[v0.1.1]: https://egit.irs.uni-stuttgart.de/rust/va416xx-rs/compare/vorago-peb1-v0.1.0...vorago-peb1-v0.1.1
[v0.1.0]: https://egit.irs.uni-stuttgart.de/rust/va416xx-rs/src/tag/vorago-peb1-v0.1.0

View File

@ -16,7 +16,7 @@ cortex-m-rt = "0.7"
embedded-hal = "1" embedded-hal = "1"
lis2dh12 = { version = "0.7", features = ["out_f32"] } lis2dh12 = { version = "0.7", features = ["out_f32"] }
va416xx-hal = { version = ">=0.3, <=0.4", features = ["va41630"] } va416xx-hal = { version = ">=0.3, <=0.5", features = ["va41630"] }
[features] [features]
rt = ["va416xx-hal/rt"] rt = ["va416xx-hal/rt"]