Compare commits
17 Commits
f16ddc3114
...
vorago-peb
Author | SHA1 | Date | |
---|---|---|---|
969e5bbc42 | |||
6960c09627 | |||
25f7b79f28
|
|||
2cf7554cab | |||
ff58fb7b55 | |||
b1f63b64ce | |||
22cc40c095
|
|||
1ca319b433 | |||
3813c397f7 | |||
9d8772bf1f | |||
246b084429
|
|||
bea5a852a2 | |||
e1487c8969
|
|||
7e7416efd1 | |||
42e3cfde8a | |||
a50f7a947a | |||
abede6057e |
@ -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 |
|
||||
|
@ -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;
|
||||
|
@ -7,6 +7,7 @@ edition = "2021"
|
||||
cortex-m = { version = "0.7", features = ["critical-section-single-core"] }
|
||||
cortex-m-rt = "0.7"
|
||||
embedded-hal = "1"
|
||||
embedded-io = "0.6"
|
||||
|
||||
rtt-target = { version = "0.5" }
|
||||
panic-rtt-target = { version = "0.1" }
|
||||
@ -16,6 +17,10 @@ embassy-sync = { version = "0.6.0" }
|
||||
embassy-time = { version = "0.3.2" }
|
||||
embassy-time-driver = { version = "0.1" }
|
||||
|
||||
[dependencies.ringbuf]
|
||||
version = "0.4"
|
||||
default-features = false
|
||||
|
||||
[dependencies.once_cell]
|
||||
version = "1"
|
||||
default-features = false
|
||||
|
161
examples/embassy/src/bin/uart-echo-with-irq.rs
Normal file
161
examples/embassy/src/bin/uart-echo-with-irq.rs
Normal file
@ -0,0 +1,161 @@
|
||||
//! 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;
|
||||
|
||||
use embassy_example::EXTCLK_FREQ;
|
||||
use embassy_executor::Spawner;
|
||||
use embassy_sync::blocking_mutex::raw::CriticalSectionRawMutex;
|
||||
use embassy_sync::blocking_mutex::Mutex;
|
||||
use embassy_time::{Duration, Ticker};
|
||||
use embedded_hal::digital::StatefulOutputPin;
|
||||
use embedded_io::Write;
|
||||
use panic_rtt_target as _;
|
||||
use ringbuf::{
|
||||
traits::{Consumer, Observer, Producer},
|
||||
StaticRb,
|
||||
};
|
||||
use rtt_target::{rprintln, rtt_init_print};
|
||||
use va416xx_hal::{
|
||||
gpio::{OutputReadablePushPull, Pin, PinsG, PG5},
|
||||
pac::{self, interrupt},
|
||||
prelude::*,
|
||||
time::Hertz,
|
||||
uart,
|
||||
};
|
||||
|
||||
pub type SharedUart = Mutex<CriticalSectionRawMutex, RefCell<Option<uart::RxWithIrq<pac::Uart0>>>>;
|
||||
static RX: SharedUart = Mutex::new(RefCell::new(None));
|
||||
|
||||
const BAUDRATE: u32 = 115200;
|
||||
|
||||
// Ring buffer size.
|
||||
const RING_BUF_SIZE: usize = 2048;
|
||||
|
||||
pub type SharedRingBuf =
|
||||
Mutex<CriticalSectionRawMutex, RefCell<Option<StaticRb<u8, RING_BUF_SIZE>>>>;
|
||||
// Ring buffers to handling variable sized telemetry
|
||||
static RINGBUF: SharedRingBuf = Mutex::new(RefCell::new(None));
|
||||
|
||||
// See https://embassy.dev/book/#_sharing_using_a_mutex for background information about sharing
|
||||
// a peripheral with embassy.
|
||||
#[embassy_executor::main]
|
||||
async fn main(spawner: Spawner) {
|
||||
rtt_init_print!();
|
||||
rprintln!("VA416xx UART-Embassy Example");
|
||||
|
||||
let mut dp = pac::Peripherals::take().unwrap();
|
||||
|
||||
// Initialize the systick interrupt & obtain the token to prove that we did
|
||||
// Use the external clock connected to XTAL_N.
|
||||
let clocks = dp
|
||||
.clkgen
|
||||
.constrain()
|
||||
.xtal_n_clk_with_src_freq(Hertz::from_raw(EXTCLK_FREQ))
|
||||
.freeze(&mut dp.sysconfig)
|
||||
.unwrap();
|
||||
// Safety: Only called once here.
|
||||
unsafe {
|
||||
embassy_example::init(
|
||||
&mut dp.sysconfig,
|
||||
&dp.irq_router,
|
||||
dp.tim15,
|
||||
dp.tim14,
|
||||
&clocks,
|
||||
)
|
||||
};
|
||||
|
||||
let portg = PinsG::new(&mut dp.sysconfig, dp.portg);
|
||||
|
||||
let tx = portg.pg0.into_funsel_1();
|
||||
let rx = portg.pg1.into_funsel_1();
|
||||
|
||||
let uart0 = uart::Uart::new(
|
||||
dp.uart0,
|
||||
(tx, rx),
|
||||
Hertz::from_raw(BAUDRATE),
|
||||
&mut dp.sysconfig,
|
||||
&clocks,
|
||||
);
|
||||
let (mut tx, rx) = uart0.split();
|
||||
let mut rx = rx.into_rx_with_irq();
|
||||
rx.start();
|
||||
RX.lock(|static_rx| {
|
||||
static_rx.borrow_mut().replace(rx);
|
||||
});
|
||||
RINGBUF.lock(|static_rb| {
|
||||
static_rb.borrow_mut().replace(StaticRb::default());
|
||||
});
|
||||
|
||||
let led = portg.pg5.into_readable_push_pull_output();
|
||||
let mut ticker = Ticker::every(Duration::from_millis(50));
|
||||
let mut processing_buf: [u8; RING_BUF_SIZE] = [0; RING_BUF_SIZE];
|
||||
let mut read_bytes = 0;
|
||||
spawner.spawn(blinky(led)).expect("failed to spawn blinky");
|
||||
loop {
|
||||
RINGBUF.lock(|static_rb| {
|
||||
let mut rb_borrow = static_rb.borrow_mut();
|
||||
let rb_mut = rb_borrow.as_mut().unwrap();
|
||||
read_bytes = rb_mut.occupied_len();
|
||||
rb_mut.pop_slice(&mut processing_buf[0..read_bytes]);
|
||||
});
|
||||
// Simply send back all received data.
|
||||
tx.write_all(&processing_buf[0..read_bytes])
|
||||
.expect("sending back read data failed");
|
||||
ticker.next().await;
|
||||
}
|
||||
}
|
||||
|
||||
#[embassy_executor::task]
|
||||
async fn blinky(mut led: Pin<PG5, OutputReadablePushPull>) {
|
||||
let mut ticker = Ticker::every(Duration::from_millis(500));
|
||||
loop {
|
||||
led.toggle().ok();
|
||||
ticker.next().await;
|
||||
}
|
||||
}
|
||||
|
||||
#[interrupt]
|
||||
#[allow(non_snake_case)]
|
||||
fn UART0_RX() {
|
||||
let mut buf: [u8; 16] = [0; 16];
|
||||
let mut read_len: usize = 0;
|
||||
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();
|
||||
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;
|
||||
if read_len > 0 {
|
||||
// Send the received buffer to the main thread for processing via a ring buffer.
|
||||
RINGBUF.lock(|static_rb| {
|
||||
let mut rb_borrow = static_rb.borrow_mut();
|
||||
let rb_mut_ref = rb_borrow.as_mut().unwrap();
|
||||
if rb_mut_ref.vacant_len() < read_len {
|
||||
ringbuf_full = true;
|
||||
for _ in rb_mut_ref.pop_iter() {}
|
||||
}
|
||||
rb_mut_ref.push_slice(&buf[0..read_len]);
|
||||
});
|
||||
}
|
||||
|
||||
if errors.is_some() {
|
||||
rprintln!("UART error: {:?}", errors);
|
||||
}
|
||||
if ringbuf_full {
|
||||
rprintln!("ringbuffer is full, deleted oldest data");
|
||||
}
|
||||
}
|
@ -1,4 +1,6 @@
|
||||
#![no_std]
|
||||
pub mod time_driver;
|
||||
|
||||
pub const EXTCLK_FREQ: u32 = 40_000_000;
|
||||
|
||||
pub use time_driver::init;
|
||||
|
@ -1,5 +1,6 @@
|
||||
#![no_std]
|
||||
#![no_main]
|
||||
use embassy_example::EXTCLK_FREQ;
|
||||
use embassy_executor::Spawner;
|
||||
use embassy_time::{Duration, Instant, Ticker};
|
||||
use embedded_hal::digital::StatefulOutputPin;
|
||||
@ -7,8 +8,6 @@ use panic_rtt_target as _;
|
||||
use rtt_target::{rprintln, rtt_init_print};
|
||||
use va416xx_hal::{gpio::PinsG, pac, prelude::*, time::Hertz};
|
||||
|
||||
const EXTCLK_FREQ: u32 = 40_000_000;
|
||||
|
||||
// main is itself an async function.
|
||||
#[embassy_executor::main]
|
||||
async fn main(_spawner: Spawner) {
|
||||
|
@ -17,7 +17,7 @@ use va416xx_hal::{
|
||||
enable_interrupt,
|
||||
irq_router::enable_and_init_irq_router,
|
||||
pac::{self, interrupt},
|
||||
pwm::{assert_tim_reset_for_two_cycles, enable_tim_clk, ValidTim},
|
||||
timer::{assert_tim_reset_for_two_cycles, enable_tim_clk, ValidTim},
|
||||
};
|
||||
|
||||
pub type TimekeeperClk = pac::Tim15;
|
||||
|
@ -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::*,
|
||||
|
@ -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]
|
||||
|
@ -169,9 +169,9 @@ mod app {
|
||||
enable_and_init_irq_router(&mut cx.device.sysconfig, &cx.device.irq_router);
|
||||
setup_edac(&mut cx.device.sysconfig);
|
||||
|
||||
let gpiob = PinsG::new(&mut cx.device.sysconfig, cx.device.portg);
|
||||
let tx = gpiob.pg0.into_funsel_1();
|
||||
let rx = gpiob.pg1.into_funsel_1();
|
||||
let gpiog = PinsG::new(&mut cx.device.sysconfig, cx.device.portg);
|
||||
let tx = gpiog.pg0.into_funsel_1();
|
||||
let rx = gpiog.pg1.into_funsel_1();
|
||||
|
||||
let uart0 = Uart::new(
|
||||
cx.device.uart0,
|
||||
@ -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) => {
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
||||
|
@ -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"
|
||||
|
@ -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()) },
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -9,8 +9,10 @@ use core::convert::Infallible;
|
||||
use core::marker::PhantomData;
|
||||
|
||||
use crate::pac;
|
||||
use crate::time::Hertz;
|
||||
pub use crate::timer::ValidTim;
|
||||
use crate::timer::{TimAndPinRegister, TimDynRegister, TimPin, TimRegInterface, ValidTimAndPin};
|
||||
use crate::{clock::Clocks, gpio::DynPinId};
|
||||
pub use crate::{gpio::PinId, time::Hertz, timer::*};
|
||||
|
||||
const DUTY_MAX: u16 = u16::MAX;
|
||||
|
||||
|
@ -7,7 +7,9 @@
|
||||
//! ## Examples
|
||||
//!
|
||||
//! - [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;
|
||||
@ -68,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)]
|
||||
@ -230,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]
|
||||
@ -293,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;
|
||||
@ -388,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 {
|
||||
@ -521,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)>
|
||||
{
|
||||
@ -616,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
|
||||
@ -641,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)
|
||||
}
|
||||
|
||||
@ -650,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
|
||||
@ -674,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
|
||||
@ -762,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;
|
||||
@ -803,10 +1030,10 @@ 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 mut current_idx = 0;
|
||||
|
||||
let irq_end = self.uart().irq_end().read();
|
||||
let enb_status = self.uart().enable().read();
|
||||
@ -814,27 +1041,27 @@ impl<Uart: Instance> RxWithIrq<Uart> {
|
||||
|
||||
// Half-Full interrupt. We have a guaranteed amount of data we can read.
|
||||
if irq_end.irq_rx().bit_is_set() {
|
||||
// Determine the number of bytes to read, ensuring we leave 1 byte in the FIFO.
|
||||
// We use this trick/hack because the timeout feature of the peripheral relies on data
|
||||
// being in the RX FIFO. If data continues arriving, another half-full IRQ will fire.
|
||||
// If not, the last byte(s) is/are emptied by the timeout interrupt.
|
||||
let available_bytes = self.uart().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 {
|
||||
buf[current_idx] = (self.uart().data().read().bits() & 0xff) as u8;
|
||||
current_idx += 1;
|
||||
buf[result.bytes_read] = (self.uart().data().read().bits() & 0xff) as u8;
|
||||
result.bytes_read += 1;
|
||||
}
|
||||
}
|
||||
|
||||
// Timeout, empty the FIFO completely.
|
||||
if irq_end.irq_rx_to().bit_is_set() {
|
||||
let read_result = self.0.read();
|
||||
// While there is data in the FIFO, write it into the reception buffer
|
||||
while let Some(byte) = self.read_handler(&mut result.errors, &read_result) {
|
||||
buf[current_idx] = byte;
|
||||
current_idx += 1;
|
||||
loop {
|
||||
// While there is data in the FIFO, write it into the reception buffer
|
||||
let read_result = self.0.read();
|
||||
if let Some(byte) = self.read_handler(&mut result.errors, &read_result) {
|
||||
buf[result.bytes_read] = byte;
|
||||
result.bytes_read += 1;
|
||||
} else {
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -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
13
vorago-peb1/CHANGELOG.md
Normal 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
|
@ -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"]
|
||||
|
||||
|
@ -1,15 +1,12 @@
|
||||
[](https://crates.io/crates/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
|
||||
|
||||
|
@ -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};
|
||||
|
@ -350,6 +350,36 @@
|
||||
]
|
||||
}
|
||||
},
|
||||
{
|
||||
"type": "cortex-debug",
|
||||
"request": "launch",
|
||||
"name": "UART Echo with IRQ",
|
||||
"servertype": "jlink",
|
||||
"jlinkscript": "${workspaceFolder}/jlink/JLinkSettings.JLinkScript",
|
||||
"cwd": "${workspaceRoot}",
|
||||
"device": "Cortex-M4",
|
||||
"svdFile": "${workspaceFolder}/va416xx/svd/va416xx.svd.patched",
|
||||
"preLaunchTask": "uart-echo-with-irq",
|
||||
"overrideLaunchCommands": [
|
||||
"monitor halt",
|
||||
"monitor reset",
|
||||
"load",
|
||||
],
|
||||
"executable": "${workspaceFolder}/target/thumbv7em-none-eabihf/debug/uart-echo-with-irq",
|
||||
"interface": "swd",
|
||||
"runToEntryPoint": "main",
|
||||
"rttConfig": {
|
||||
"enabled": true,
|
||||
"address": "auto",
|
||||
"decoders": [
|
||||
{
|
||||
"port": 0,
|
||||
"timestamp": true,
|
||||
"type": "console"
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
{
|
||||
"type": "cortex-debug",
|
||||
"request": "launch",
|
||||
|
@ -95,6 +95,19 @@
|
||||
"kind": "build",
|
||||
}
|
||||
},
|
||||
{
|
||||
"label": "uart-echo-with-irq",
|
||||
"type": "shell",
|
||||
"command": "~/.cargo/bin/cargo", // note: full path to the cargo
|
||||
"args": [
|
||||
"build",
|
||||
"--bin",
|
||||
"uart-echo-with-irq"
|
||||
],
|
||||
"group": {
|
||||
"kind": "build",
|
||||
}
|
||||
},
|
||||
{
|
||||
"label": "pwm-example",
|
||||
"type": "shell",
|
||||
@ -200,4 +213,4 @@
|
||||
}
|
||||
},
|
||||
]
|
||||
}
|
||||
}
|
Reference in New Issue
Block a user