17 Commits

Author SHA1 Message Date
969e5bbc42 Merge pull request 'use released HAL dependency' (#39) from prep-bsp-release into main
All checks were successful
Rust/va416xx-rs/pipeline/head This commit looks good
Reviewed-on: #39
2024-10-01 10:56:23 +02:00
6960c09627 Merge branch 'main' into prep-bsp-release 2024-10-01 10:56:15 +02:00
25f7b79f28 use released HAL dependency 2024-10-01 10:55:55 +02:00
2cf7554cab Merge pull request 'README and docs update' (#38) from prep-bsp-release into main
All checks were successful
Rust/va416xx-rs/pipeline/head This commit looks good
Reviewed-on: #38
2024-10-01 10:54:52 +02:00
ff58fb7b55 prepare release for BSP 2024-10-01 10:54:11 +02:00
b1f63b64ce Merge pull request 'prepare release for BSP' (#37) from prep-bsp-release into main
All checks were successful
Rust/va416xx-rs/pipeline/head This commit looks good
Reviewed-on: #37
2024-10-01 10:46:28 +02:00
22cc40c095 prepare release for BSP
Some checks are pending
Rust/va416xx-rs/pipeline/head Build started...
2024-10-01 10:43:06 +02:00
1ca319b433 Merge pull request 'bump version' (#36) from prep-hal-release into main
All checks were successful
Rust/va416xx-rs/pipeline/head This commit looks good
Reviewed-on: #36
2024-09-30 15:34:11 +02:00
3813c397f7 bump HAL version
Some checks are pending
Rust/va416xx-rs/pipeline/pr-main Build started...
2024-09-30 15:23:24 +02:00
9d8772bf1f Merge pull request 'prepare HAL release' (#35) from prep-hal-release into main
All checks were successful
Rust/va416xx-rs/pipeline/head This commit looks good
Reviewed-on: #35
2024-09-30 14:45:54 +02:00
246b084429 prepare HAL release
All checks were successful
Rust/va416xx-rs/pipeline/head This commit looks good
2024-09-30 14:13:11 +02:00
bea5a852a2 Merge pull request 'smaller improvements and fixes' (#34) from smaller-improvements-fixes into main
All checks were successful
Rust/va416xx-rs/pipeline/head This commit looks good
Reviewed-on: #34
2024-09-30 13:50:52 +02:00
e1487c8969 smaller improvements and fixes
All checks were successful
Rust/va416xx-rs/pipeline/head This commit looks good
2024-09-30 12:17:05 +02:00
7e7416efd1 Merge pull request 'Improve UART Impl' (#33) from improve-uart-impl into main
All checks were successful
Rust/va416xx-rs/pipeline/head This commit looks good
Reviewed-on: #33
2024-09-24 17:47:47 +02:00
42e3cfde8a improve UART impl
All checks were successful
Rust/va416xx-rs/pipeline/pr-main This commit looks good
2024-09-24 17:22:00 +02:00
a50f7a947a Merge pull request 'UART with IRQ + Embassy example' (#32) from add-uart-embassy-echo-example into main
All checks were successful
Rust/va416xx-rs/pipeline/head This commit looks good
Reviewed-on: #32
2024-09-24 11:07:00 +02:00
abede6057e UART with IRQ + Embassy example
All checks were successful
Rust/va416xx-rs/pipeline/pr-main This commit looks good
2024-09-24 10:59:41 +02:00
16 changed files with 417 additions and 312 deletions

View File

@ -11,10 +11,10 @@ The bootloader uses the following memory map:
| ------ | ---- | ---- |
| 0x0 | Bootloader start | code up to 0x3FFC bytes |
| 0x3FFC | Bootloader CRC | word |
| 0x4000 | App image A start | code up to 0x1DFFC (~120K) bytes |
| 0x4000 | App image A start | code up to 0x1DFF8 (~120K) bytes |
| 0x21FF8 | App image A CRC check length | word |
| 0x21FFC | App image A CRC check value | word |
| 0x22000 | App image B start | code up to 0x1DFFC (~120K) bytes |
| 0x22000 | App image B start | code up to 0x1DFF8 (~120K) bytes |
| 0x3FFF8 | App image B CRC check length | word |
| 0x3FFFC | App image B CRC check value | word |
| 0x40000 | End of NVM | end |

View File

@ -53,7 +53,7 @@ const APP_A_START_ADDR: u32 = BOOTLOADER_END_ADDR;
const APP_A_SIZE_ADDR: u32 = APP_B_END_ADDR - 8;
// 0x21FFC
const APP_A_CRC_ADDR: u32 = APP_B_END_ADDR - 4;
pub const APP_A_END_ADDR: u32 = APP_B_END_ADDR - BOOTLOADER_END_ADDR / 2;
pub const APP_A_END_ADDR: u32 = BOOTLOADER_END_ADDR + APP_IMG_SZ;
// 0x22000
const APP_B_START_ADDR: u32 = APP_A_END_ADDR;

View File

@ -1,3 +1,12 @@
//! This is an example of using the UART HAL abstraction with the IRQ support and embassy.
//!
//! It uses the UART0 for communication with another MCU or a host computer (recommended).
//! You can connect a USB-to-Serial converter to the UART0 pins and then use a serial terminal
//! application like picocom to send data to the microcontroller, which should be echoed
//! back to the sender.
//!
//! This application uses the interrupt support of the VA416xx to read the data arriving
//! on the UART without requiring polling.
#![no_std]
#![no_main]
use core::cell::RefCell;
@ -77,7 +86,7 @@ async fn main(spawner: Spawner) {
&clocks,
);
let (mut tx, rx) = uart0.split();
let mut rx = rx.to_rx_with_irq();
let mut rx = rx.into_rx_with_irq();
rx.start();
RX.lock(|static_rx| {
static_rx.borrow_mut().replace(rx);
@ -119,17 +128,14 @@ async fn blinky(mut led: Pin<PG5, OutputReadablePushPull>) {
fn UART0_RX() {
let mut buf: [u8; 16] = [0; 16];
let mut read_len: usize = 0;
let mut irq_error = None;
let mut errors = None;
RX.lock(|static_rx| {
let mut rx_borrow = static_rx.borrow_mut();
let rx_mut_ref = rx_borrow.as_mut().unwrap();
match rx_mut_ref.irq_handler(&mut buf) {
Ok(result) => {
read_len = result.bytes_read;
}
Err(e) => {
irq_error = Some(e);
}
let result = rx_mut_ref.irq_handler(&mut buf);
read_len = result.bytes_read;
if result.errors.is_some() {
errors = result.errors;
}
});
let mut ringbuf_full = false;
@ -146,8 +152,8 @@ fn UART0_RX() {
});
}
if irq_error.is_some() {
rprintln!("error in IRQ handler: {:?}", irq_error);
if errors.is_some() {
rprintln!("UART error: {:?}", errors);
}
if ringbuf_full {
rprintln!("ringbuffer is full, deleted oldest data");

View File

@ -12,7 +12,7 @@ use rtt_target::{rprintln, rtt_init_print};
use simple_examples::peb1;
use va416xx_hal::dma::{Dma, DmaCfg, DmaChannel, DmaCtrlBlock};
use va416xx_hal::irq_router::enable_and_init_irq_router;
use va416xx_hal::pwm::CountdownTimer;
use va416xx_hal::timer::CountdownTimer;
use va416xx_hal::{
pac::{self, interrupt},
prelude::*,

View File

@ -11,7 +11,8 @@ use va416xx_hal::{
gpio::PinsA,
pac,
prelude::*,
pwm::{self, get_duty_from_percent, CountdownTimer, PwmA, PwmB, ReducedPwmPin},
pwm::{self, get_duty_from_percent, PwmA, PwmB, ReducedPwmPin},
timer::CountdownTimer,
};
#[entry]

View File

@ -193,7 +193,7 @@ mod app {
Mono::start(cx.core.SYST, clocks.sysclk().raw());
CLOCKS.set(clocks).unwrap();
let mut rx = rx.to_rx_with_irq();
let mut rx = rx.into_rx_with_irq();
let mut rx_context = IrqContextTimeoutOrMaxSize::new(MAX_TC_FRAME_SIZE);
rx.read_fixed_len_or_timeout_based_using_irq(&mut rx_context)
.expect("initiating UART RX failed");
@ -293,8 +293,8 @@ mod app {
.read_fixed_len_or_timeout_based_using_irq(cx.local.rx_context)
.expect("read operation failed");
}
if result.error() {
log::warn!("UART error: {:?}", result.error());
if result.has_errors() {
log::warn!("UART error: {:?}", result.errors.unwrap());
}
}
Err(e) => {

View File

@ -1,7 +1,7 @@
/* Special linker script for application slot A with an offset at address 0x4000 */
MEMORY
{
FLASH : ORIGIN = 0x00004000, LENGTH = 256K
FLASH : ORIGIN = 0x00004000, LENGTH = 0x1DFF8
/* RAM is a mandatory region. This RAM refers to the SRAM_0 */
RAM : ORIGIN = 0x1FFF8000, LENGTH = 32K
SRAM_1 : ORIGIN = 0x20000000, LENGTH = 32K

View File

@ -1,7 +1,7 @@
/* Special linker script for application slot B with an offset at address 0x22000 */
MEMORY
{
FLASH : ORIGIN = 0x00022000, LENGTH = 256K
FLASH : ORIGIN = 0x00022000, LENGTH = 0x1DFF8
/* RAM is a mandatory region. This RAM refers to the SRAM_0 */
RAM : ORIGIN = 0x1FFF8000, LENGTH = 32K
SRAM_1 : ORIGIN = 0x20000000, LENGTH = 32K

View File

@ -8,6 +8,8 @@ and this project adheres to [Semantic Versioning](http://semver.org/).
# [unreleased]
# [v0.3.0] 2024-30-09
## Changed
- Improve and fix SPI abstractions. Add new low level interface. The primary SPI constructor now
@ -16,6 +18,8 @@ and this project adheres to [Semantic Versioning](http://semver.org/).
- Added an additional way to read the UART RX with IRQs. The module documentation provides
more information.
- Made the UART with IRQ API more flexible for future additions.
- Improved UART API result and error handling, added low level API to read from and write
to the FIFO directly
## Fixed

View File

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

View File

@ -113,14 +113,6 @@ pub(super) unsafe trait RegisterInterface {
/// this type.
fn id(&self) -> DynPinId;
const PORTA: *const PortRegisterBlock = Porta::ptr();
const PORTB: *const PortRegisterBlock = Portb::ptr();
const PORTC: *const PortRegisterBlock = Portc::ptr();
const PORTD: *const PortRegisterBlock = Portd::ptr();
const PORTE: *const PortRegisterBlock = Porte::ptr();
const PORTF: *const PortRegisterBlock = Portf::ptr();
const PORTG: *const PortRegisterBlock = Portg::ptr();
/// Change the pin mode
#[inline]
fn change_mode(&mut self, mode: DynPinMode) {
@ -155,13 +147,13 @@ pub(super) unsafe trait RegisterInterface {
#[inline]
fn port_reg(&self) -> &PortRegisterBlock {
match self.id().group {
DynGroup::A => unsafe { &(*Self::PORTA) },
DynGroup::B => unsafe { &(*Self::PORTB) },
DynGroup::C => unsafe { &(*Self::PORTC) },
DynGroup::D => unsafe { &(*Self::PORTD) },
DynGroup::E => unsafe { &(*Self::PORTE) },
DynGroup::F => unsafe { &(*Self::PORTF) },
DynGroup::G => unsafe { &(*Self::PORTG) },
DynGroup::A => unsafe { &(*Porta::ptr()) },
DynGroup::B => unsafe { &(*Portb::ptr()) },
DynGroup::C => unsafe { &(*Portc::ptr()) },
DynGroup::D => unsafe { &(*Portd::ptr()) },
DynGroup::E => unsafe { &(*Porte::ptr()) },
DynGroup::F => unsafe { &(*Portf::ptr()) },
DynGroup::G => unsafe { &(*Portg::ptr()) },
}
}

View File

@ -9,6 +9,7 @@
//! - [UART simple example](https://egit.irs.uni-stuttgart.de/rust/va416xx-rs/src/branch/main/examples/simple/examples/uart.rs)
//! - [UART echo with IRQ and Embassy](https://egit.irs.uni-stuttgart.de/rust/va416xx-rs/src/branch/main/examples/embassy/src/bin/uart-echo-with-irq.rs)
//! - [Flashloader app using UART with IRQs](https://egit.irs.uni-stuttgart.de/rust/va416xx-rs/src/branch/main/flashloader)
use core::convert::Infallible;
use core::ops::Deref;
use embedded_hal_nb::serial::Read;
@ -69,15 +70,58 @@ impl RxPin<Uart2> for Pin<PF9, AltFunc1> {}
// Regular Definitions
//==================================================================================================
#[derive(Debug)]
#[derive(Debug, PartialEq, Eq)]
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
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 {
Overrun,
FramingError,
ParityError,
Rx(RxError),
BreakCondition,
TransferPending,
BufferTooShort,
}
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)]
@ -231,50 +275,47 @@ impl IrqContextTimeoutOrMaxSize {
#[derive(Debug, Default)]
pub struct IrqResult {
pub bytes_read: usize,
pub errors: IrqUartError,
pub errors: Option<IrqUartError>,
}
/// This struct is used to return the default IRQ handler result to the user
#[derive(Debug, Default)]
pub struct IrqResultMaxSizeTimeout {
pub struct IrqResultMaxSizeOrTimeout {
complete: bool,
timeout: bool,
pub errors: IrqUartError,
pub errors: Option<IrqUartError>,
pub bytes_read: usize,
}
impl IrqResultMaxSizeTimeout {
impl IrqResultMaxSizeOrTimeout {
pub fn new() -> Self {
IrqResultMaxSizeTimeout {
IrqResultMaxSizeOrTimeout {
complete: false,
timeout: false,
errors: IrqUartError::default(),
errors: None,
bytes_read: 0,
}
}
}
impl IrqResultMaxSizeTimeout {
impl IrqResultMaxSizeOrTimeout {
#[inline]
pub fn error(&self) -> bool {
if self.errors.overflow || self.errors.parity || self.errors.framing {
return true;
}
false
pub fn has_errors(&self) -> bool {
self.errors.is_some()
}
#[inline]
pub fn overflow_error(&self) -> bool {
self.errors.overflow
self.errors.map_or(false, |e| e.overflow)
}
#[inline]
pub fn framing_error(&self) -> bool {
self.errors.framing
self.errors.map_or(false, |e| e.framing)
}
#[inline]
pub fn parity_error(&self) -> bool {
self.errors.parity
self.errors.map_or(false, |e| e.parity)
}
#[inline]
@ -294,44 +335,54 @@ enum IrqReceptionMode {
Pending,
}
//==================================================================================================
// UART implementation
//==================================================================================================
/// Type erased variant of a UART. Can be created with the [Uart::downgrade] function.
pub struct UartBase<Uart> {
uart: Uart,
tx: Tx<Uart>,
rx: Rx<Uart>,
}
/// Serial abstraction. Entry point to create a new UART
pub struct Uart<UartInstance, Pins> {
inner: UartBase<UartInstance>,
pins: Pins,
#[derive(Default, Debug, Copy, Clone)]
pub struct IrqUartError {
overflow: bool,
framing: bool,
parity: bool,
other: bool,
}
/// Serial receiver.
///
/// Can be created by using the [Uart::split] or [UartBase::split] API.
pub struct Rx<Uart>(Uart);
impl IrqUartError {
#[inline(always)]
pub fn overflow(&self) -> bool {
self.overflow
}
/// Serial transmitter
///
/// Can be created by using the [Uart::split] or [UartBase::split] API.
pub struct Tx<Uart>(Uart);
#[inline(always)]
pub fn framing(&self) -> bool {
self.framing
}
impl<Uart: Instance> Rx<Uart> {
fn new(uart: Uart) -> Self {
Self(uart)
#[inline(always)]
pub fn parity(&self) -> bool {
self.parity
}
#[inline(always)]
pub fn other(&self) -> bool {
self.other
}
}
impl<Uart> Tx<Uart> {
fn new(uart: Uart) -> Self {
Self(uart)
impl IrqUartError {
#[inline(always)]
pub fn error(&self) -> bool {
self.overflow || self.framing || self.parity
}
}
#[derive(Debug, PartialEq, Eq)]
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
pub struct BufferTooShortError {
found: usize,
expected: usize,
}
//==================================================================================================
// UART peripheral wrapper
//==================================================================================================
pub trait Instance: Deref<Target = uart_base::RegisterBlock> {
const IDX: u8;
const PERIPH_SEL: PeripheralSelect;
@ -389,6 +440,17 @@ impl Instance for Uart2 {
}
}
//==================================================================================================
// UART implementation
//==================================================================================================
/// Type erased variant of a UART. Can be created with the [Uart::downgrade] function.
pub struct UartBase<Uart> {
uart: Uart,
tx: Tx<Uart>,
rx: Rx<Uart>,
}
impl<Uart: Instance> UartBase<Uart> {
fn init(self, config: Config, clocks: &Clocks) -> Self {
if Uart::IDX == 2 {
@ -522,6 +584,46 @@ impl<Uart: Instance> UartBase<Uart> {
}
}
impl<UartInstance> embedded_io::ErrorType for UartBase<UartInstance> {
type Error = Error;
}
impl<UartInstance> embedded_hal_nb::serial::ErrorType for UartBase<UartInstance> {
type Error = Error;
}
impl<Uart: Instance> embedded_hal_nb::serial::Read<u8> for UartBase<Uart> {
fn read(&mut self) -> nb::Result<u8, Self::Error> {
self.rx.read().map_err(|e| e.map(Error::Rx))
}
}
impl<Uart: Instance> embedded_hal_nb::serial::Write<u8> for UartBase<Uart> {
fn write(&mut self, word: u8) -> nb::Result<(), Self::Error> {
self.tx.write(word).map_err(|e| {
if let nb::Error::Other(_) = e {
unreachable!()
}
nb::Error::WouldBlock
})
}
fn flush(&mut self) -> nb::Result<(), Self::Error> {
self.tx.flush().map_err(|e| {
if let nb::Error::Other(_) = e {
unreachable!()
}
nb::Error::WouldBlock
})
}
}
/// Serial abstraction. Entry point to create a new UART
pub struct Uart<UartInstance, Pins> {
inner: UartBase<UartInstance>,
pins: Pins,
}
impl<TxPinInst: TxPin<UartInstance>, RxPinInst: RxPin<UartInstance>, UartInstance: Instance>
Uart<UartInstance, (TxPinInst, RxPinInst)>
{
@ -617,7 +719,16 @@ impl<TxPinInst: TxPin<UartInstance>, RxPinInst: RxPin<UartInstance>, UartInstanc
}
}
/// Serial receiver.
///
/// Can be created by using the [Uart::split] or [UartBase::split] API.
pub struct Rx<Uart>(Uart);
impl<Uart: Instance> Rx<Uart> {
fn new(uart: Uart) -> Self {
Self(uart)
}
/// Direct access to the peripheral structure.
///
/// # Safety
@ -642,7 +753,34 @@ impl<Uart: Instance> Rx<Uart> {
self.0.enable().modify(|_, w| w.rxenable().clear_bit());
}
pub fn to_rx_with_irq(self) -> RxWithIrq<Uart> {
/// Low level function to read a word from the UART FIFO.
///
/// Uses the [nb] API to allow usage in blocking and non-blocking contexts.
///
/// Please note that you might have to mask the returned value with 0xff to retrieve the actual
/// value if you use the manual parity mode. See chapter 11.4.1 for more information.
#[inline(always)]
pub fn read_fifo(&self) -> nb::Result<u32, Infallible> {
if self.0.rxstatus().read().rdavl().bit_is_clear() {
return Err(nb::Error::WouldBlock);
}
Ok(self.read_fifo_unchecked())
}
/// Low level function to read a word from from the UART FIFO.
///
/// This does not necesarily mean there is a word in the FIFO available.
/// Use the [Self::read_fifo] function to read a word from the FIFO reliably using the [nb]
/// API.
///
/// Please note that you might have to mask the returned value with 0xff to retrieve the actual
/// value if you use the manual parity mode. See chapter 11.4.1 for more information.
#[inline(always)]
pub fn read_fifo_unchecked(&self) -> u32 {
self.0.data().read().bits()
}
pub fn into_rx_with_irq(self) -> RxWithIrq<Uart> {
RxWithIrq(self)
}
@ -651,7 +789,69 @@ impl<Uart: Instance> Rx<Uart> {
}
}
impl<Uart> embedded_io::ErrorType for Rx<Uart> {
type Error = RxError;
}
impl<Uart> embedded_hal_nb::serial::ErrorType for Rx<Uart> {
type Error = RxError;
}
impl<Uart: Instance> embedded_hal_nb::serial::Read<u8> for Rx<Uart> {
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| {
if let nb::Error::Other(_) = e {
unreachable!()
}
nb::Error::WouldBlock
})
}
}
impl<Uart: Instance> embedded_io::Read for Rx<Uart> {
fn read(&mut self, buf: &mut [u8]) -> Result<usize, Self::Error> {
if buf.is_empty() {
return Ok(0);
}
for byte in buf.iter_mut() {
let w = nb::block!(<Self as embedded_hal_nb::serial::Read<u8>>::read(self))?;
*byte = w;
}
Ok(buf.len())
}
}
/// Serial transmitter
///
/// Can be created by using the [Uart::split] or [UartBase::split] API.
pub struct Tx<Uart>(Uart);
impl<Uart: Instance> Tx<Uart> {
fn new(uart: Uart) -> Self {
Self(uart)
}
/// Direct access to the peripheral structure.
///
/// # Safety
@ -675,54 +875,80 @@ impl<Uart: Instance> Tx<Uart> {
pub fn disable(&mut self) {
self.0.enable().modify(|_, w| w.txenable().clear_bit());
}
}
#[derive(Default, Debug)]
pub struct IrqUartError {
overflow: bool,
framing: bool,
parity: bool,
other: bool,
}
impl IrqUartError {
/// Low level function to write a word to the UART FIFO.
///
/// Uses the [nb] API to allow usage in blocking and non-blocking contexts.
///
/// Please note that you might have to mask the returned value with 0xff to retrieve the actual
/// value if you use the manual parity mode. See chapter 11.4.1 for more information.
#[inline(always)]
pub fn overflow(&self) -> bool {
self.overflow
pub fn write_fifo(&self, data: u32) -> nb::Result<(), Infallible> {
if self.0.txstatus().read().wrrdy().bit_is_clear() {
return Err(nb::Error::WouldBlock);
}
self.write_fifo_unchecked(data);
Ok(())
}
/// Low level function to write a word to the UART FIFO.
///
/// This does not necesarily mean that the FIFO can process another word because it might be
/// full.
/// Use the [Self::write_fifo] function to write a word to the FIFO reliably using the [nb]
/// API.
#[inline(always)]
pub fn framing(&self) -> bool {
self.framing
}
#[inline(always)]
pub fn parity(&self) -> bool {
self.parity
}
#[inline(always)]
pub fn other(&self) -> bool {
self.other
pub fn write_fifo_unchecked(&self, data: u32) {
self.0.data().write(|w| unsafe { w.bits(data) });
}
}
impl IrqUartError {
#[inline(always)]
pub fn error(&self) -> bool {
self.overflow || self.framing || self.parity
impl<Uart> embedded_io::ErrorType for Tx<Uart> {
type Error = Infallible;
}
impl<Uart> embedded_hal_nb::serial::ErrorType for Tx<Uart> {
type Error = Infallible;
}
impl<Uart: Instance> embedded_hal_nb::serial::Write<u8> for Tx<Uart> {
fn write(&mut self, word: u8) -> nb::Result<(), Self::Error> {
self.write_fifo(word as u32)
}
fn flush(&mut self) -> nb::Result<(), Self::Error> {
// SAFETY: Only TX related registers are used.
let reader = unsafe { &(*Uart::ptr()) }.txstatus().read();
if reader.wrbusy().bit_is_set() {
return Err(nb::Error::WouldBlock);
}
Ok(())
}
}
#[derive(Debug)]
pub enum IrqError {
BufferTooShort { found: usize, expected: usize },
Uart(IrqUartError),
impl<Uart: Instance> embedded_io::Write for Tx<Uart> {
fn write(&mut self, buf: &[u8]) -> Result<usize, Self::Error> {
if buf.is_empty() {
return Ok(0);
}
for byte in buf.iter() {
nb::block!(<Self as embedded_hal_nb::serial::Write<u8>>::write(
self, *byte
))?;
}
Ok(buf.len())
}
fn flush(&mut self) -> Result<(), Self::Error> {
nb::block!(<Self as embedded_hal_nb::serial::Write<u8>>::flush(self))
}
}
/// Serial receiver, using interrupts to offload reading to the hardware.
///
/// You can use [Rx::to_rx_with_irq] to convert a normal [Rx] structure into this structure.
/// You can use [Rx::into_rx_with_irq] to convert a normal [Rx] structure into this structure.
/// This structure provides two distinct ways to read the UART RX using interrupts. It should
/// be noted that the interrupt service routine (ISR) still has to be provided by the user. However,
/// this structure provides API calls which can be used inside the ISRs to simplify the reading
@ -763,9 +989,9 @@ impl<Uart: Instance> RxWithIrq<Uart> {
pub fn read_fixed_len_or_timeout_based_using_irq(
&mut self,
context: &mut IrqContextTimeoutOrMaxSize,
) -> Result<(), Error> {
) -> Result<(), TransferPendingError> {
if context.mode != IrqReceptionMode::Idle {
return Err(Error::TransferPending);
return Err(TransferPendingError);
}
context.mode = IrqReceptionMode::Pending;
context.rx_idx = 0;
@ -804,8 +1030,9 @@ impl<Uart: Instance> RxWithIrq<Uart> {
/// result of the operation.
///
/// 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.
pub fn irq_handler(&mut self, buf: &mut [u8; 16]) -> Result<IrqResult, IrqUartError> {
/// 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].
pub fn irq_handler(&mut self, buf: &mut [u8; 16]) -> IrqResult {
let mut result = IrqResult::default();
let irq_end = self.uart().irq_end().read();
@ -847,7 +1074,7 @@ impl<Uart: Instance> RxWithIrq<Uart> {
self.uart()
.irq_clr()
.write(|w| unsafe { w.bits(irq_end.bits()) });
Ok(result)
result
}
/// This function should be called in the user provided UART interrupt handler.
@ -860,19 +1087,20 @@ impl<Uart: Instance> RxWithIrq<Uart> {
/// [IrqContextTimeoutOrMaxSize] structure.
///
/// If passed buffer is equal to or larger than the specified maximum length, an
/// [`Error::BufferTooShort`] will be returned
/// [BufferTooShortError] will be returned. Other RX errors are treated as partial errors
/// and returned inside the [IrqResultMaxSizeOrTimeout] structure.
pub fn irq_handler_max_size_or_timeout_based(
&mut self,
context: &mut IrqContextTimeoutOrMaxSize,
buf: &mut [u8],
) -> Result<IrqResultMaxSizeTimeout, IrqError> {
) -> Result<IrqResultMaxSizeOrTimeout, BufferTooShortError> {
if buf.len() < context.max_len {
return Err(IrqError::BufferTooShort {
return Err(BufferTooShortError {
found: buf.len(),
expected: context.max_len,
});
}
let mut result = IrqResultMaxSizeTimeout::default();
let mut result = IrqResultMaxSizeOrTimeout::default();
let irq_end = self.uart().irq_end().read();
let enb_status = self.uart().enable().read();
@ -936,49 +1164,51 @@ impl<Uart: Instance> RxWithIrq<Uart> {
fn read_handler(
&self,
errors: &mut IrqUartError,
read_res: &nb::Result<u8, Error>,
errors: &mut Option<IrqUartError>,
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(IrqUartError::default());
// Now we can safely modify fields inside `err`
match e {
Error::Overrun => {
errors.overflow = true;
}
Error::FramingError => {
errors.framing = true;
}
Error::ParityError => {
errors.parity = true;
}
_ => {
errors.other = true;
}
RxError::Overrun => err.overflow = true,
RxError::Framing => err.framing = true,
RxError::Parity => err.parity = true,
}
None
}
}
}
fn check_for_errors(&self, errors: &mut IrqUartError) {
// Read status register again, might have changed since reading received data
fn check_for_errors(&self, errors: &mut Option<IrqUartError>) {
let rx_status = self.uart().rxstatus().read();
if rx_status.rxovr().bit_is_set() {
errors.overflow = true;
}
if rx_status.rxfrm().bit_is_set() {
errors.framing = true;
}
if rx_status.rxpar().bit_is_set() {
errors.parity = true;
if rx_status.rxovr().bit_is_set()
|| rx_status.rxfrm().bit_is_set()
|| rx_status.rxpar().bit_is_set()
{
let err = errors.get_or_insert(IrqUartError::default());
if rx_status.rxovr().bit_is_set() {
err.overflow = true;
}
if rx_status.rxfrm().bit_is_set() {
err.framing = true;
}
if rx_status.rxpar().bit_is_set() {
err.parity = true;
}
}
}
fn irq_completion_handler_max_size_timeout(
&mut self,
res: &mut IrqResultMaxSizeTimeout,
res: &mut IrqResultMaxSizeOrTimeout,
context: &mut IrqContextTimeoutOrMaxSize,
) {
self.disable_rx_irq_sources();
@ -989,150 +1219,12 @@ impl<Uart: Instance> RxWithIrq<Uart> {
context.rx_idx = 0;
}
pub fn release(self) -> Uart {
/// # Safety
///
/// This API allows creating multiple UART instances when releasing the TX structure as well.
/// The user must ensure that these instances are not used to create multiple overlapping
/// UART drivers.
pub unsafe fn release(self) -> Uart {
self.0.release()
}
}
impl embedded_io::Error for Error {
fn kind(&self) -> embedded_io::ErrorKind {
embedded_io::ErrorKind::Other
}
}
impl embedded_hal_nb::serial::Error for Error {
fn kind(&self) -> embedded_hal_nb::serial::ErrorKind {
embedded_hal_nb::serial::ErrorKind::Other
}
}
impl<Uart> embedded_io::ErrorType for Rx<Uart> {
type Error = Error;
}
impl<Uart> embedded_hal_nb::serial::ErrorType for Rx<Uart> {
type Error = Error;
}
impl<Uart: Instance> embedded_hal_nb::serial::Read<u8> for Rx<Uart> {
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(Error::Overrun)
} else if status_reader.rxfrm().bit_is_set() {
Some(Error::FramingError)
} else if status_reader.rxpar().bit_is_set() {
Some(Error::ParityError)
} 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
uart.data().read().bits();
Err(err.into())
} else if status_reader.rdavl().bit_is_set() {
let data = uart.data().read().bits();
Ok((data & 0xff) as u8)
} else {
Err(nb::Error::WouldBlock)
}
}
}
impl<Uart: Instance> embedded_io::Read for Rx<Uart> {
fn read(&mut self, buf: &mut [u8]) -> Result<usize, Self::Error> {
if buf.is_empty() {
return Ok(0);
}
for byte in buf.iter_mut() {
let w = nb::block!(<Self as embedded_hal_nb::serial::Read<u8>>::read(self))?;
*byte = w;
}
Ok(buf.len())
}
}
impl<Uart> embedded_io::ErrorType for Tx<Uart> {
type Error = Error;
}
impl<Uart> embedded_hal_nb::serial::ErrorType for Tx<Uart> {
type Error = Error;
}
impl<Uart: Instance> embedded_hal_nb::serial::Write<u8> for Tx<Uart> {
fn write(&mut self, word: u8) -> nb::Result<(), Self::Error> {
let reader = unsafe { &(*Uart::ptr()) }.txstatus().read();
if reader.wrrdy().bit_is_clear() {
return Err(nb::Error::WouldBlock);
} else {
// DPARITY bit not supported yet
unsafe {
// NOTE(unsafe) atomic write to data register
// NOTE(write_volatile) 8-bit write that's not
// possible through the svd2rust API
(*Uart::ptr()).data().write(|w| w.bits(word as u32));
}
}
Ok(())
}
fn flush(&mut self) -> nb::Result<(), Self::Error> {
// SAFETY: Only TX related registers are used.
let reader = unsafe { &(*Uart::ptr()) }.txstatus().read();
if reader.wrbusy().bit_is_set() {
return Err(nb::Error::WouldBlock);
}
Ok(())
}
}
impl<Uart: Instance> embedded_io::Write for Tx<Uart> {
fn write(&mut self, buf: &[u8]) -> Result<usize, Self::Error> {
if buf.is_empty() {
return Ok(0);
}
for byte in buf.iter() {
nb::block!(<Self as embedded_hal_nb::serial::Write<u8>>::write(
self, *byte
))?;
}
Ok(buf.len())
}
fn flush(&mut self) -> Result<(), Self::Error> {
nb::block!(<Self as embedded_hal_nb::serial::Write<u8>>::flush(self))
}
}
impl<UartInstance> embedded_io::ErrorType for UartBase<UartInstance> {
type Error = Error;
}
impl<UartInstance> embedded_hal_nb::serial::ErrorType for UartBase<UartInstance> {
type Error = Error;
}
impl<Uart: Instance> embedded_hal_nb::serial::Read<u8> for UartBase<Uart> {
fn read(&mut self) -> nb::Result<u8, Self::Error> {
self.rx.read()
}
}
impl<Uart: Instance> embedded_hal_nb::serial::Write<u8> for UartBase<Uart> {
fn write(&mut self, word: u8) -> nb::Result<(), Self::Error> {
self.tx.write(word)
}
fn flush(&mut self) -> nb::Result<(), Self::Error> {
self.tx.flush()
}
}

13
vorago-peb1/CHANGELOG.md Normal file
View File

@ -0,0 +1,13 @@
Change Log
=======
All notable changes to this project will be documented in this file.
The format is based on [Keep a Changelog](http://keepachangelog.com/)
and this project adheres to [Semantic Versioning](http://semver.org/).
# [unreleased]
# [v0.1.0] 2024-10-01
- Initial release

View File

@ -16,14 +16,10 @@ cortex-m-rt = "0.7"
embedded-hal = "1"
[dependencies.va416xx-hal]
path = "../va416xx-hal"
features = ["va41630"]
version = "0.2.0"
version = ">=0.3, <0.4"
[dependencies.lis2dh12]
git = "https://github.com/us-irs/lis2dh12.git"
# path = "../../lis2dh12"
branch = "all-features"
version = "0.7"
features = ["out_f32"]

View File

@ -1,15 +1,12 @@
[![Crates.io](https://img.shields.io/crates/v/vorago-peb1)](https://crates.io/crates/vorago-peb1)
[![docs.rs](https://img.shields.io/docsrs/vorago-peb1)](https://docs.rs/vorago-peb1)
# Rust BSP for the Vorago PEB1 development board
## Using the `.cargo/config.toml` file
This is the Rust **B**oard **S**upport **P**ackage crate for the Vorago PEB1 development board.
Its aim is to provide drivers for the board features of the PEB1 board.
Use the following command to have a starting `config.toml` file
```sh
cp .cargo/def-config.toml .cargo/config.toml
```
You then can adapt the `config.toml` to your needs. For example, you can configure runners
to conveniently flash with `cargo run`.
The BSP builds on top of the [HAL crate for VA416xx devices](https://egit.irs.uni-stuttgart.de/rust/va416xx-rs/src/branch/main/va416xx-hal).
## Notes on board revisions

View File

@ -5,6 +5,10 @@
pub use lis2dh12;
/// Support for the LIS2DH12 accelerometer on the GPIO board.
///
/// # Example
///
/// - [PEB1 Accelerometer](https://egit.irs.uni-stuttgart.de/rust/va416xx-rs/src/branch/main/examples/simple/examples/peb1-accelerometer.rs)
pub mod accelerometer {
use lis2dh12::{self, detect_i2c_addr, AddrDetectionError, Lis2dh12};