30 Commits

Author SHA1 Message Date
1a5670b362 small README update 2025-03-11 16:42:16 +01:00
2b8a9dbce4 Merge pull request 'UART embedded-io fixes' (#66) from uart-embedded-io-fixes into main
Reviewed-on: #66
2025-03-10 17:39:54 +01:00
6528dd855f UART embedded-io fixes 2025-03-10 17:37:12 +01:00
4455cb0343 Merge pull request 'bump dependencies' (#65) from bump-dependencies into main
Reviewed-on: #65
2025-03-07 17:25:55 +01:00
2706dbf461 bump dependencies 2025-03-07 17:25:34 +01:00
c3e16b4278 prepare embassy release 2025-03-07 17:22:19 +01:00
2088d7dc8a prepare peb1 release 2025-03-07 17:19:42 +01:00
a44ba7b8a5 Merge pull request 'UART error handling update' (#64) from uart-error-handling-update into main
Reviewed-on: #64
2025-03-07 17:14:02 +01:00
a3c6366e98 UART error handling update 2025-03-07 17:10:42 +01:00
016c421cb8 small docs fix 2025-02-18 19:10:05 +01:00
0e99e04dd1 use released packages 2025-02-18 19:07:32 +01:00
aea3d835f0 Merge pull request 'prepare embassy release' (#63) from prep-embassy-release into main
Reviewed-on: #63
2025-02-18 18:33:02 +01:00
5f39b916fa prepare embassy release 2025-02-18 18:26:38 +01:00
6d8a114f49 Merge pull request 'prepare HAL patch and embassy release' (#62) from prep-hal-patch-embassy-release into main
Reviewed-on: #62
2025-02-18 18:24:09 +01:00
969f0f4ca5 prepare HAL patch and embassy release 2025-02-18 18:20:11 +01:00
b6971ab7eb Merge pull request 'bump all dependencies and prepare BSP and embassy release' (#61) from prep-bsp-embassy-releases into main
Reviewed-on: #61
2025-02-18 16:56:44 +01:00
41b215e326 bump all dependencies and prepare BSP and embassy release 2025-02-18 16:55:08 +01:00
43da650d78 Merge pull request 'prep HAL release v0.4.0' (#60) from prep-hal-v0.4.0 into main
Reviewed-on: #60
2025-02-18 16:27:58 +01:00
c67f50c96c prep HAL release v0.4.0 2025-02-18 16:26:44 +01:00
770d6cb905 date fix CHANGELOG 2025-02-18 15:16:14 +01:00
9878f3b493 Merge pull request 'update VS Code files' (#59) from update-vscode-files into main
Reviewed-on: #59
2025-02-17 11:38:03 +01:00
1b07d0f258 update VS Code files 2025-02-17 11:36:40 +01:00
d785f8ab88 Merge pull request 'fix for UART example' (#58) from example-fix into main
Reviewed-on: #58
2025-02-17 11:35:40 +01:00
527243ab96 fix for UART example 2025-02-17 11:35:11 +01:00
617ba3cca0 Merge pull request 'PAC changelog' (#57) from pac-changelog into main
Reviewed-on: #57
2025-02-17 11:33:22 +01:00
167cb97f7d PAC changelog 2025-02-17 11:33:07 +01:00
857b233881 Merge pull request 'changelog HAL' (#56) from changelog-hal into main
Reviewed-on: #56
2025-02-17 11:32:47 +01:00
9dcb423976 changelog HAL 2025-02-17 11:30:52 +01:00
186ae6d059 Merge pull request 'UART update' (#54) from uart-update into main
Reviewed-on: #54
2025-02-17 11:28:46 +01:00
54c949421e added async support for UART 2025-02-17 11:28:33 +01:00
30 changed files with 1419 additions and 249 deletions

View File

@@ -14,7 +14,7 @@ This workspace contains the following crates:
PAC crate containing basic low-level register definition
- The [`va416xx-hal`](https://egit.irs.uni-stuttgart.de/rust/va416xx-rs/src/branch/main/va416xx-hal)
HAL crate containing higher-level abstractions on top of the PAC register crate.
- The [`va416xx-embassy`](https://egit.irs.uni-stuttgart.de/rust/va416xx-rs/src/branch/main/va108xx-embassy)
- The [`va416xx-embassy`](https://egit.irs.uni-stuttgart.de/rust/va416xx-rs/src/branch/main/va416xx-embassy)
crate containing support for running the embassy-rs RTOS.
- The [`vorago-peb1`](https://egit.irs.uni-stuttgart.de/rust/va416xx-rs/src/branch/main/vorago-peb1)
BSP crate containing support for the PEB1 development board.
@@ -155,3 +155,13 @@ example.
The Segger RTT viewer can be used to display log messages received from the target. The base
address for the RTT block placement is 0x1fff8000. It is recommended to use a search range of
0x1000 around that base address when using the RTT viewer.
## Learning (Embedded) Rust
If you are unfamiliar with Rust on Embedded Systems or Rust in general, the following resources
are recommended:
- [Rust Book](https://doc.rust-lang.org/book/)
- [Embedded Rust Book](https://docs.rust-embedded.org/book/)
- [Embedded Rust Discovery](https://docs.rust-embedded.org/discovery/microbit/)
- [Awesome Embedded Rust](https://github.com/rust-embedded/awesome-embedded-rust)

View File

@@ -7,14 +7,14 @@ edition = "2021"
cortex-m = "0.7"
cortex-m-rt = "0.7"
embedded-hal = "1"
panic-rtt-target = { version = "0.1.3" }
panic-halt = { version = "0.2" }
rtt-target = { version = "0.5" }
panic-rtt-target = { version = "0.2" }
panic-halt = { version = "1" }
rtt-target = { version = "0.6" }
crc = "3"
static_assertions = "1"
[dependencies.va416xx-hal]
path = "../va416xx-hal"
version = "0.5"
features = ["va41630"]
[features]

View File

@@ -12,8 +12,10 @@ embedded-io = "0.6"
embedded-hal-async = "1"
embedded-io-async = "0.6"
rtt-target = { version = "0.5" }
panic-rtt-target = { version = "0.1" }
rtt-target = { version = "0.6" }
heapless = "0.8"
panic-rtt-target = { version = "0.2" }
static_cell = "2"
critical-section = "1"
once_cell = { version = "1", default-features = false, features = ["critical-section"] }
ringbuf = { version = "0.4", default-features = false }
@@ -26,11 +28,8 @@ embassy-executor = { version = "0.7", features = [
"executor-interrupt"
]}
va416xx-embassy = { path = "../../va416xx-embassy", default-features = false }
[dependencies.va416xx-hal]
path = "../../va416xx-hal"
features = ["va41630"]
va416xx-hal = { version = "0.5" }
va416xx-embassy = { version = "0.1", default-features = false }
[features]
default = ["ticks-hz-1_000", "va416xx-embassy/irq-tim14-tim15"]

View File

@@ -0,0 +1,111 @@
//! Asynchronous UART reception example application.
//!
//! This application receives data on two UARTs permanently using a ring buffer.
//! The ring buffer are read them asynchronously.
//! It uses PORTG0 as TX pin and PORTG1 as RX pin, which is the UART0 on the PEB1 board.
//!
//! Instructions:
//!
//! 1. Tie a USB to UART converter with RX to PORTG0 and TX to PORTG1.
//! 2. Connect to the serial interface by using an application like Putty or picocom. You can
//! type something in the terminal and check if the data is echoed back. You can also check the
//! RTT logs to see received data.
#![no_std]
#![no_main]
use core::cell::RefCell;
use critical_section::Mutex;
use embassy_example::EXTCLK_FREQ;
use embassy_executor::Spawner;
use embassy_time::Instant;
use embedded_io::Write;
use embedded_io_async::Read;
use heapless::spsc::{Producer, Queue};
use panic_rtt_target as _;
use rtt_target::{rprintln, rtt_init_print};
use va416xx_hal::{
gpio::PinsG,
pac::{self, interrupt},
prelude::*,
time::Hertz,
uart::{
self,
rx_asynch::{on_interrupt_rx, RxAsync},
Bank,
},
};
static QUEUE_UART_A: static_cell::ConstStaticCell<Queue<u8, 256>> =
static_cell::ConstStaticCell::new(Queue::new());
static PRODUCER_UART_A: Mutex<RefCell<Option<Producer<u8, 256>>>> = Mutex::new(RefCell::new(None));
#[embassy_executor::main]
async fn main(_spawner: Spawner) {
rtt_init_print!();
rprintln!("-- VA108xx Async UART RX Demo --");
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 {
va416xx_embassy::init(
&mut dp.sysconfig,
&dp.irq_router,
dp.tim15,
dp.tim14,
&clocks,
);
}
let portg = PinsG::new(&mut dp.sysconfig, dp.portg);
let mut led = portg.pg5.into_readable_push_pull_output();
let tx = portg.pg0.into_funsel_1();
let rx = portg.pg1.into_funsel_1();
let uarta = uart::Uart::new(&mut dp.sysconfig, dp.uart0, (tx, rx), 115200.Hz(), &clocks);
let (mut tx_uart_a, rx_uart_a) = uarta.split();
let (prod_uart_a, cons_uart_a) = QUEUE_UART_A.take().split();
// Pass the producer to the interrupt handler.
critical_section::with(|cs| {
*PRODUCER_UART_A.borrow(cs).borrow_mut() = Some(prod_uart_a);
});
// TODO: Add example for RxAsyncOverwriting using another UART.
let mut async_uart_rx = RxAsync::new(rx_uart_a, cons_uart_a);
let mut buf = [0u8; 256];
loop {
rprintln!("Current time UART A: {}", Instant::now().as_secs());
led.toggle();
let read_bytes = async_uart_rx.read(&mut buf).await.unwrap();
let read_str = core::str::from_utf8(&buf[..read_bytes]).unwrap();
rprintln!(
"Read {} bytes asynchronously on UART A: {:?}",
read_bytes,
read_str
);
tx_uart_a.write_all(read_str.as_bytes()).unwrap();
}
}
#[interrupt]
#[allow(non_snake_case)]
fn UART0_RX() {
let mut prod =
critical_section::with(|cs| PRODUCER_UART_A.borrow(cs).borrow_mut().take().unwrap());
let errors = on_interrupt_rx(Bank::Uart0, &mut prod);
critical_section::with(|cs| *PRODUCER_UART_A.borrow(cs).borrow_mut() = Some(prod));
// In a production app, we could use a channel to send the errors to the main task.
if let Err(errors) = errors {
rprintln!("UART A errors: {:?}", errors);
}
}

View File

@@ -0,0 +1,96 @@
//! Asynchronous UART transmission example application.
//!
//! This application receives sends 4 strings with different sizes permanently.
//! It uses PORTG0 as TX pin and PORTG1 as RX pin, which is the UART0 on the PEB1 board.
//!
//! Instructions:
//!
//! 1. Tie a USB to UART converter with RX to PORTG0 and TX to PORTG1.
//! 2. Connect to the serial interface by using an application like Putty or picocom. You can
//! type something in the terminal and check if the data is echoed back. You can also check the
//! RTT logs to see received data.
#![no_std]
#![no_main]
use embassy_example::EXTCLK_FREQ;
use embassy_executor::Spawner;
use embassy_time::{Duration, Instant, Ticker};
use embedded_io_async::Write;
use panic_rtt_target as _;
use rtt_target::{rprintln, rtt_init_print};
use va416xx_hal::{
gpio::PinsG,
pac::{self, interrupt},
prelude::*,
time::Hertz,
uart::{
self,
tx_asynch::{on_interrupt_tx, TxAsync},
Bank,
},
};
const STR_LIST: &[&str] = &[
"Hello World\r\n",
"Smoll\r\n",
"A string which is larger than the FIFO size\r\n",
"A really large string which is significantly larger than the FIFO size\r\n",
];
// main is itself an async function.
#[embassy_executor::main]
async fn main(_spawner: Spawner) {
rtt_init_print!();
rprintln!("-- VA108xx Async UART TX Demo --");
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 {
va416xx_embassy::init(
&mut dp.sysconfig,
&dp.irq_router,
dp.tim15,
dp.tim14,
&clocks,
);
}
let portg = PinsG::new(&mut dp.sysconfig, dp.portg);
let mut led = portg.pg5.into_readable_push_pull_output();
let tx = portg.pg0.into_funsel_1();
let rx = portg.pg1.into_funsel_1();
let uarta = uart::Uart::new(&mut dp.sysconfig, dp.uart0, (tx, rx), 115200.Hz(), &clocks);
let (tx, _rx) = uarta.split();
let mut async_tx = TxAsync::new(tx);
let mut ticker = Ticker::every(Duration::from_secs(1));
let mut idx = 0;
loop {
rprintln!("Current time: {}", Instant::now().as_secs());
led.toggle();
let _written = async_tx
.write(STR_LIST[idx].as_bytes())
.await
.expect("writing failed");
idx += 1;
if idx == STR_LIST.len() {
idx = 0;
}
ticker.next().await;
}
}
#[interrupt]
#[allow(non_snake_case)]
fn UART0_TX() {
on_interrupt_tx(Bank::Uart0);
}

View File

@@ -79,10 +79,10 @@ async fn main(spawner: Spawner) {
let rx = portg.pg1.into_funsel_1();
let uart0 = uart::Uart::new(
&mut dp.sysconfig,
dp.uart0,
(tx, rx),
Hertz::from_raw(BAUDRATE),
&mut dp.sysconfig,
&clocks,
);
let (mut tx, rx) = uart0.split();
@@ -132,7 +132,7 @@ fn UART0_RX() {
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);
let result = rx_mut_ref.on_interrupt(&mut buf);
read_len = result.bytes_read;
if result.errors.is_some() {
errors = result.errors;

View File

@@ -7,13 +7,11 @@ edition = "2021"
cortex-m = { version = "0.7", features = ["critical-section-single-core"] }
cortex-m-rt = "0.7"
embedded-hal = "1"
rtt-target = { version = "0.5" }
rtt-target = { version = "0.6" }
rtic-sync = { version = "1.3", features = ["defmt-03"] }
panic-rtt-target = { version = "0.1.3" }
panic-rtt-target = { version = "0.2" }
[dependencies.va416xx-hal]
path = "../../va416xx-hal"
features = ["va41630"]
va416xx-hal = { version = "0.5", features = ["va41630"] }
[dependencies.rtic]
version = "2"

View File

@@ -7,17 +7,16 @@ edition = "2021"
cortex-m = { version = "0.7", features = ["critical-section-single-core"] }
cortex-m-rt = "0.7"
critical-section = "1"
panic-rtt-target = { version = "0.1.3" }
rtt-target = { version = "0.5" }
panic-rtt-target = { version = "0.2" }
rtt-target = { version = "0.6" }
embedded-hal = "1"
embedded-hal-nb = "1"
nb = "1"
embedded-io = "0.6"
panic-halt = "0.2"
panic-halt = "1"
accelerometer = "0.12"
[dependencies.va416xx-hal]
path = "../../va416xx-hal"
va416xx-hal = { version = "0.5", features = ["va41630"] }
[dependencies.vorago-peb1]
path = "../../vorago-peb1"

View File

@@ -33,10 +33,10 @@ fn main() -> ! {
let rx = gpiob.pg1.into_funsel_1();
let uart0 = uart::Uart::new(
&mut dp.sysconfig,
dp.uart0,
(tx, rx),
Hertz::from_raw(115200),
&mut dp.sysconfig,
&clocks,
);
let (mut tx, mut rx) = uart0.split();

View File

@@ -9,44 +9,20 @@ cortex-m-rt = "0.7"
embedded-hal = "1"
embedded-hal-nb = "1"
embedded-io = "0.6"
panic-rtt-target = { version = "0.1.3" }
rtt-target = { version = "0.5" }
rtt-log = "0.3"
panic-rtt-target = { version = "0.2" }
rtt-target = { version = "0.6" }
rtt-log = "0.5"
log = "0.4"
crc = "3"
rtic-sync = "1"
static_cell = "2"
satrs = { version = "0.3.0-alpha.0", default-features = false }
ringbuf = { version = "0.4", default-features = false }
once_cell = { version = "1", default-features = false, features = ["critical-section"] }
spacepackets = { version = "0.13", default-features = false }
cobs = { version = "0.3", default-features = false }
[dependencies.satrs]
version = "0.2"
default-features = false
va416xx-hal = { version = "0.5", features = ["va41630"] }
[dependencies.ringbuf]
version = "0.4"
default-features = false
[dependencies.once_cell]
version = "1"
default-features = false
features = ["critical-section"]
[dependencies.spacepackets]
version = "0.11"
default-features = false
[dependencies.cobs]
git = "https://github.com/robamu/cobs.rs.git"
branch = "all_features"
default-features = false
[dependencies.va416xx-hal]
path = "../va416xx-hal"
features = ["va41630"]
[dependencies.rtic]
version = "2"
features = ["thumbv7-backend"]
[dependencies.rtic-monotonics]
version = "2"
features = ["cortex-m-systick"]
rtic = { version = "2", features = ["thumbv7-backend"] }
rtic-monotonics = { version = "2", features = ["cortex-m-systick"] }

View File

@@ -11,7 +11,7 @@ panic-rtt-target = { version = "0.1.3" }
rtt-target = { version = "0.5" }
cortex-m = { version = "0.7", features = ["critical-section-single-core"] }
embedded-hal = "1"
va416xx-hal = { path = "../../va416xx-hal", features = ["va41630"] }
va416xx-hal = { path = "0.4", features = ["va41630"] }
[profile.dev]
codegen-units = 1

View File

@@ -11,7 +11,7 @@ panic-rtt-target = { version = "0.1.3" }
rtt-target = { version = "0.5" }
cortex-m = { version = "0.7", features = ["critical-section-single-core"] }
embedded-hal = "1"
va416xx-hal = { path = "../../va416xx-hal", features = ["va41630"] }
va416xx-hal = { path = "0.4", features = ["va41630"] }
[profile.dev]
codegen-units = 1

View File

@@ -171,10 +171,10 @@ mod app {
let rx = gpiog.pg1.into_funsel_1();
let uart0 = Uart::new(
&mut cx.device.sysconfig,
cx.device.uart0,
(tx, rx),
Hertz::from_raw(UART_BAUDRATE),
&mut cx.device.sysconfig,
&clocks,
);
let (tx, rx) = uart0.split();
@@ -256,7 +256,7 @@ mod app {
match cx
.local
.uart_rx
.irq_handler_max_size_or_timeout_based(cx.local.rx_context, cx.local.rx_buf)
.on_interrupt_max_size_or_timeout_based(cx.local.rx_context, cx.local.rx_buf)
{
Ok(result) => {
if RX_DEBUGGING {

View File

@@ -8,6 +8,14 @@ and this project adheres to [Semantic Versioning](http://semver.org/).
## [unreleased]
## [v0.1.0] 2025-02-13
## [v0.1.1] 2025-03-07
- Bumped allowed HAL dependency to v0.5
## [v0.1.0] 2025-02-18
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]
name = "va416xx-embassy"
version = "0.1.0"
version = "0.1.1"
edition = "2021"
authors = ["Robin Mueller <muellerr@irs.uni-stuttgart.de>"]
description = "Embassy-rs support for the Vorago VA416xx family of microcontrollers"
@@ -21,17 +21,20 @@ portable-atomic = "1"
once_cell = { version = "1", default-features = false, features = ["critical-section"] }
va416xx-hal = { version = "0.3", path = "../va416xx-hal" }
va416xx-hal = { version = ">=0.4, <=0.5" }
[features]
default = ["irq-tim14-tim15"]
irqs-in-lib = []
# This determines the reserved interrupt functions for the embassy time drivers. Only one
# is allowed to be selected!
irq-tim14-tim15 = ["irqs-in-lib"]
irq-tim13-tim14 = ["irqs-in-lib"]
irq-tim14-tim15 = ["_irqs-in-lib"]
irq-tim13-tim14 = ["_irqs-in-lib"]
# These TIMs are clocked slower!
irq-tim22-tim23 = ["irqs-in-lib"]
irq-tim22-tim23 = ["_irqs-in-lib"]
# Private feature.
_irqs-in-lib = []
[package.metadata.docs.rs]
rustdoc-args = ["--generate-link-to-definition"]

View File

@@ -136,13 +136,13 @@ pub unsafe fn init<
alarm: AlarmTim,
clocks: &Clocks,
) {
#[cfg(feature = "irqs-in-lib")]
#[cfg(feature = "_irqs-in-lib")]
assert_eq!(
TimekeeperTim::ID,
TIMEKEEPER_IRQ as u8 - TIM_IRQ_OFFSET as u8,
"Timekeeper TIM and IRQ missmatch"
);
#[cfg(feature = "irqs-in-lib")]
#[cfg(feature = "_irqs-in-lib")]
assert_eq!(
AlarmTim::ID,
ALARM_IRQ as u8 - TIM_IRQ_OFFSET as u8,

View File

@@ -8,7 +8,28 @@ and this project adheres to [Semantic Versioning](http://semver.org/).
# [unreleased]
# [v0.4.0]
# [v0.5.1] 2025-03-10
## Fixed
- Fix `embedded_io` UART implementation to implement the documented contract properly.
The implementation will now block until at least one byte is available or can be written, unless
the send or receive buffer is empty.
# [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
- Chip selection is not enforced anymore, but advised through documentation. This makes using
the HAL in libraries a lot easier.
# [v0.4.0] 2025-02-18
## Changed
@@ -44,6 +65,7 @@ and this project adheres to [Semantic Versioning](http://semver.org/).
- Asynchronous UART RX support.
- Add new `get_tim_raw` unsafe method to retrieve TIM peripheral blocks.
- `Uart::with_with_interrupt` and `Uart::new_without_interrupt`
- A lot of missing `defmt::Format` implementations.
# [v0.3.0] 2024-30-09
@@ -95,3 +117,12 @@ and this project adheres to [Semantic Versioning](http://semver.org/).
# [v0.1.0] 2024-07-01
- 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.1]: https://egit.irs.uni-stuttgart.de/rust/va416xx-rs/compare/va416xx-hal-v0.5.0...va416xx-hal-v0.5.1
[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]
name = "va416xx-hal"
version = "0.3.0"
version = "0.5.1"
authors = ["Robin Mueller <muellerr@irs.uni-stuttgart.de>"]
edition = "2021"
description = "HAL for the Vorago VA416xx family of MCUs"
@@ -19,25 +19,22 @@ embedded-hal-nb = "1"
embedded-hal-async = "1"
embedded-hal = "1"
embedded-io = "0.6"
embedded-io-async = "0.6"
num_enum = { version = "0.7", default-features = false }
typenum = "1"
bitflags = "2"
bitfield = "0.17"
bitfield = { version = ">=0.17, <=0.18"}
fugit = "0.3"
delegate = "0.12"
delegate = ">=0.12, <=0.13"
heapless = "0.8"
void = { version = "1", default-features = false }
thiserror = { version = "2", default-features = false }
portable-atomic = "1"
embassy-sync = "0.6"
va416xx = { version = "0.4", features = ["critical-section"], default-features = false }
defmt = { version = "0.3", optional = true }
[dependencies.va416xx]
default-features = false
path = "../va416xx"
version = "0.4"
features = ["critical-section"]
[features]
default = ["rt", "revb"]
rt = ["va416xx/rt"]

View File

@@ -11,14 +11,17 @@ raw PAC. This crate also implements traits specified by the
[embedded-hal](https://github.com/rust-embedded/embedded-hal) project, making it compatible with
various drivers in the embedded rust ecosystem.
You have to enable one of the following device features to use this crate depending on
which chip you are using:
It is generally advised to enable ONE of the following device features to use this crate
depending on which chip you are using:
- `va41630`
- `va41629`
- `va41628`
- `va41620`
If no chip is specified, only access to APIs which are common for all families or
which are not disabled for specific families is granted.
## Building
Building an application requires the `thumbv7em-none-eabihf` cross-compiler toolchain.

View File

@@ -1,3 +1,3 @@
#!/bin/sh
export RUSTDOCFLAGS="--cfg docsrs --generate-link-to-definition -Z unstable-options"
cargo +nightly doc --all-features --open
cargo +nightly doc --features "defmt va41630" --open

View File

@@ -212,13 +212,13 @@ impl TimingCfg {
}
pub fn reg(&self) -> u32 {
(self.tbuf as u32) << 28
| (self.thd_sta as u32) << 24
| (self.tsu_sta as u32) << 20
| (self.tsu_sto as u32) << 16
| (self.tlow as u32) << 12
| (self.thigh as u32) << 8
| (self.tf as u32) << 4
((self.tbuf as u32) << 28)
| ((self.thd_sta as u32) << 24)
| ((self.tsu_sta as u32) << 20)
| ((self.tsu_sto as u32) << 16)
| ((self.tlow as u32) << 12)
| ((self.thigh as u32) << 8)
| ((self.tf as u32) << 4)
| (self.tr as u32)
}
}
@@ -390,7 +390,7 @@ impl<I2c: Instance> I2cBase<I2c> {
if let Some(max_words) = max_words {
self.i2c
.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);
// 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)?;
self.i2c
.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(())
}

View File

@@ -1,20 +1,23 @@
//! This is the **H**ardware **A**bstraction **L**ayer (HAL) for the VA416xx MCU family.
//!
//! It is an additional hardware abstraction on top of the [peripheral access API](https://egit.irs.uni-stuttgart.de/rust/va416xx-rs/src/branch/main/va416xx).
//!
//! It is the result of reading the datasheet for the device and encoding a type-safe layer over the
//! raw PAC. This crate also implements traits specified by the
//! [embedded-hal](https://github.com/rust-embedded/embedded-hal) project, making it compatible with
//! various drivers in the embedded rust ecosystem.
//! You have to enable one of the following device features to use this crate depending on
//! which chip you are using:
//!
//! It is generally advised to enable ONE of the following device features to use this crate
//! depending on which chip you are using:
//!
//! - `va41630`
//! - `va41629`
//! - `va41628`
//! - `va41620`
//!
//! If no option is specified, only access to APIs which are common for all families or
//! which are not disabled for specific families is granted.
//!
//! When using this HAL and writing applications for the VA416xx family in general, it is strongly
//! recommended that you set up the clock properly, because the default internal HBO clock
//! is not very accurate. You can use the [crate::clock] module for this. If you are working
@@ -26,15 +29,6 @@
#[cfg(test)]
extern crate std;
#[cfg(not(feature = "device-selected"))]
compile_error!(
"This crate requires one of the following device features enabled:
va41630
va41629
va41628
"
);
use gpio::Port;
pub use va416xx as device;
pub use va416xx as pac;

View File

@@ -30,6 +30,14 @@ use crate::{
#[cfg(not(feature = "va41628"))]
use crate::gpio::{PC15, PF8};
#[derive(Debug, Clone, Copy)]
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
pub enum Bank {
Uart0 = 0,
Uart1 = 1,
Uart2 = 2,
}
//==================================================================================================
// Type-Level support
//==================================================================================================
@@ -74,56 +82,6 @@ impl RxPin<Uart2> for Pin<PF9, AltFunc1> {}
#[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 {
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)]
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
pub enum Event {
@@ -391,6 +349,7 @@ pub struct BufferTooShortError {
pub trait Instance: Deref<Target = uart_base::RegisterBlock> {
const IDX: u8;
const PERIPH_SEL: PeripheralSelect;
const PTR: *const uart_base::RegisterBlock;
const IRQ_RX: pac::Interrupt;
const IRQ_TX: pac::Interrupt;
@@ -400,7 +359,21 @@ pub trait Instance: Deref<Target = uart_base::RegisterBlock> {
///
/// This circumvents the safety guarantees of the HAL.
unsafe fn steal() -> Self;
fn ptr() -> *const uart_base::RegisterBlock;
#[inline(always)]
fn ptr() -> *const uart_base::RegisterBlock {
Self::PTR
}
/// Retrieve the type erased peripheral register block.
///
/// # Safety
///
/// This circumvents the safety guarantees of the HAL.
#[inline(always)]
unsafe fn reg_block() -> &'static uart_base::RegisterBlock {
unsafe { &(*Self::ptr()) }
}
}
impl Instance for Uart0 {
@@ -408,6 +381,7 @@ impl Instance for Uart0 {
const PERIPH_SEL: PeripheralSelect = PeripheralSelect::Uart0;
const IRQ_RX: pac::Interrupt = pac::Interrupt::UART0_RX;
const IRQ_TX: pac::Interrupt = pac::Interrupt::UART0_TX;
const PTR: *const uart_base::RegisterBlock = Self::PTR;
unsafe fn steal() -> Self {
Self::steal()
@@ -422,6 +396,7 @@ impl Instance for Uart1 {
const PERIPH_SEL: PeripheralSelect = PeripheralSelect::Uart1;
const IRQ_RX: pac::Interrupt = pac::Interrupt::UART1_RX;
const IRQ_TX: pac::Interrupt = pac::Interrupt::UART1_TX;
const PTR: *const uart_base::RegisterBlock = Self::PTR;
unsafe fn steal() -> Self {
Self::steal()
@@ -436,6 +411,7 @@ impl Instance for Uart2 {
const PERIPH_SEL: PeripheralSelect = PeripheralSelect::Uart2;
const IRQ_RX: pac::Interrupt = pac::Interrupt::UART2_RX;
const IRQ_TX: pac::Interrupt = pac::Interrupt::UART2_TX;
const PTR: *const uart_base::RegisterBlock = Self::PTR;
unsafe fn steal() -> Self {
Self::steal()
@@ -445,6 +421,21 @@ impl Instance for Uart2 {
}
}
impl Bank {
/// Retrieve the peripheral register block.
///
/// # Safety
///
/// Circumvents the HAL safety guarantees.
pub unsafe fn reg_block(&self) -> &'static uart_base::RegisterBlock {
match self {
Bank::Uart0 => unsafe { pac::Uart0::reg_block() },
Bank::Uart1 => unsafe { pac::Uart1::reg_block() },
Bank::Uart2 => unsafe { pac::Uart2::reg_block() },
}
}
}
//==================================================================================================
// UART implementation
//==================================================================================================
@@ -581,25 +572,31 @@ impl<Uart: Instance> UartBase<Uart> {
w.txenable().clear_bit()
});
disable_nvic_interrupt(Uart::IRQ_RX);
disable_nvic_interrupt(Uart::IRQ_TX);
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>) {
(self.tx, self.rx)
}
}
impl<UartInstance> embedded_io::ErrorType for UartBase<UartInstance> {
type Error = Error;
type Error = Infallible;
}
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> {
fn read(&mut self) -> nb::Result<u8, Self::Error> {
self.rx.read().map_err(|e| e.map(Error::Rx))
self.rx.read()
}
}
@@ -633,10 +630,10 @@ impl<TxPinInst: TxPin<UartInstance>, RxPinInst: RxPin<UartInstance>, UartInstanc
Uart<UartInstance, (TxPinInst, RxPinInst)>
{
pub fn new(
syscfg: &mut va416xx::Sysconfig,
uart: UartInstance,
pins: (TxPinInst, RxPinInst),
config: impl Into<Config>,
syscfg: &mut va416xx::Sysconfig,
clocks: &Clocks,
) -> Self {
crate::clock::enable_peripheral_clock(syscfg, UartInstance::PERIPH_SEL);
@@ -654,10 +651,10 @@ impl<TxPinInst: TxPin<UartInstance>, RxPinInst: RxPin<UartInstance>, UartInstanc
}
pub fn new_with_clock_freq(
syscfg: &mut va416xx::Sysconfig,
uart: UartInstance,
pins: (TxPinInst, RxPinInst),
config: impl Into<Config>,
syscfg: &mut va416xx::Sysconfig,
clock: impl Into<Hertz>,
) -> Self {
crate::clock::enable_peripheral_clock(syscfg, UartInstance::PERIPH_SEL);
@@ -687,6 +684,8 @@ impl<TxPinInst: TxPin<UartInstance>, RxPinInst: RxPin<UartInstance>, UartInstanc
delegate::delegate! {
to self.inner {
/// Poll receiver errors.
pub fn poll_rx_errors(&self) -> Option<UartErrors>;
#[inline]
pub fn enable_rx(&mut self);
#[inline]
@@ -724,6 +723,34 @@ impl<TxPinInst: TxPin<UartInstance>, RxPinInst: RxPin<UartInstance>, UartInstanc
}
}
#[inline(always)]
pub fn enable_rx(uart: &uart_base::RegisterBlock) {
uart.enable().modify(|_, w| w.rxenable().set_bit());
}
#[inline(always)]
pub fn disable_rx(uart: &uart_base::RegisterBlock) {
uart.enable().modify(|_, w| w.rxenable().clear_bit());
}
#[inline(always)]
pub fn enable_rx_interrupts(uart: &uart_base::RegisterBlock) {
uart.irq_enb().modify(|_, w| {
w.irq_rx().set_bit();
w.irq_rx_to().set_bit();
w.irq_rx_status().set_bit()
});
}
#[inline(always)]
pub fn disable_rx_interrupts(uart: &uart_base::RegisterBlock) {
uart.irq_enb().modify(|_, w| {
w.irq_rx().clear_bit();
w.irq_rx_to().clear_bit();
w.irq_rx_status().clear_bit()
});
}
/// Serial receiver.
///
/// Can be created by using the [Uart::split] or [UartBase::split] API.
@@ -743,6 +770,23 @@ impl<Uart: Instance> Rx<Uart> {
&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]
pub fn clear_fifo(&self) {
self.0.fifo_clr().write(|w| w.rxfifo().set_bit());
@@ -758,6 +802,15 @@ impl<Uart: Instance> Rx<Uart> {
self.0.enable().modify(|_, w| w.rxenable().clear_bit());
}
#[inline]
pub fn disable_interrupts(&mut self) {
disable_rx_interrupts(unsafe { Uart::reg_block() });
}
#[inline]
pub fn enable_interrupts(&mut self) {
enable_rx_interrupts(unsafe { Uart::reg_block() });
}
/// Low level function to read a word from the UART FIFO.
///
/// Uses the [nb] API to allow usage in blocking and non-blocking contexts.
@@ -795,34 +848,15 @@ impl<Uart: Instance> 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> {
type Error = RxError;
type Error = Infallible;
}
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!()
@@ -837,22 +871,71 @@ impl<Uart: Instance> embedded_io::Read for Rx<Uart> {
if buf.is_empty() {
return Ok(0);
}
let mut read = 0;
loop {
if self.0.rxstatus().read().rdavl().bit_is_set() {
break;
}
}
for byte in buf.iter_mut() {
let w = nb::block!(<Self as embedded_hal_nb::serial::Read<u8>>::read(self))?;
*byte = w;
match <Self as embedded_hal_nb::serial::Read<u8>>::read(self) {
Ok(w) => {
*byte = w;
read += 1;
}
Err(nb::Error::WouldBlock) => break,
}
}
Ok(buf.len())
Ok(read)
}
}
#[inline(always)]
pub fn enable_tx(uart: &uart_base::RegisterBlock) {
uart.enable().modify(|_, w| w.txenable().set_bit());
}
#[inline(always)]
pub fn disable_tx(uart: &uart_base::RegisterBlock) {
uart.enable().modify(|_, w| w.txenable().clear_bit());
}
#[inline(always)]
pub fn enable_tx_interrupts(uart: &uart_base::RegisterBlock) {
uart.irq_enb().modify(|_, w| {
w.irq_tx().set_bit();
w.irq_tx_status().set_bit();
w.irq_tx_empty().set_bit()
});
}
#[inline(always)]
pub fn disable_tx_interrupts(uart: &uart_base::RegisterBlock) {
uart.irq_enb().modify(|_, w| {
w.irq_tx().clear_bit();
w.irq_tx_status().clear_bit();
w.irq_tx_empty().clear_bit()
});
}
/// Serial transmitter
///
/// Can be created by using the [Uart::split] or [UartBase::split] API.
pub struct Tx<Uart>(Uart);
impl<Uart: Instance> Tx<Uart> {
/// Retrieve a TX pin without expecting an explicit UART structure
///
/// # Safety
///
/// Circumvents the HAL safety guarantees.
#[inline(always)]
pub unsafe fn steal() -> Self {
Self(Uart::steal())
}
#[inline(always)]
fn new(uart: Uart) -> Self {
Self(uart)
}
@@ -862,7 +945,8 @@ impl<Uart: Instance> Tx<Uart> {
/// # Safety
///
/// You must ensure that only registers related to the operation of the TX side are used.
pub unsafe fn uart(&self) -> &Uart {
#[inline(always)]
pub const unsafe fn uart(&self) -> &Uart {
&self.0
}
@@ -881,6 +965,27 @@ impl<Uart: Instance> Tx<Uart> {
self.0.enable().modify(|_, w| w.txenable().clear_bit());
}
/// Enables the IRQ_TX, IRQ_TX_STATUS and IRQ_TX_EMPTY interrupts.
///
/// - The IRQ_TX interrupt is generated when the TX FIFO is at least half empty.
/// - The IRQ_TX_STATUS interrupt is generated when write data is lost due to a FIFO overflow
/// - The IRQ_TX_EMPTY interrupt is generated when the TX FIFO is empty and the TXBUSY signal
/// is 0
#[inline]
pub fn enable_interrupts(&self) {
// Safety: We own the UART structure
enable_tx_interrupts(unsafe { Uart::reg_block() });
}
/// Disables the IRQ_TX, IRQ_TX_STATUS and IRQ_TX_EMPTY interrupts.
///
/// [Self::enable_interrupts] documents the interrupts.
#[inline]
pub fn disable_interrupts(&self) {
// Safety: We own the UART structure
disable_tx_interrupts(unsafe { Uart::reg_block() });
}
/// Low level function to write a word to the UART FIFO.
///
/// Uses the [nb] API to allow usage in blocking and non-blocking contexts.
@@ -906,6 +1011,11 @@ impl<Uart: Instance> Tx<Uart> {
pub fn write_fifo_unchecked(&self, data: u32) {
self.0.data().write(|w| unsafe { w.bits(data) });
}
#[inline]
pub fn into_async(self) -> TxAsync<Uart> {
TxAsync::new(self)
}
}
impl<Uart> embedded_io::ErrorType for Tx<Uart> {
@@ -936,14 +1046,19 @@ impl<Uart: Instance> embedded_io::Write for Tx<Uart> {
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
))?;
loop {
if self.0.txstatus().read().wrrdy().bit_is_set() {
break;
}
}
Ok(buf.len())
let mut written = 0;
for byte in buf.iter() {
match <Self as embedded_hal_nb::serial::Write<u8>>::write(self, *byte) {
Ok(_) => written += 1,
Err(nb::Error::WouldBlock) => return Ok(written),
}
}
Ok(written)
}
fn flush(&mut self) -> Result<(), Self::Error> {
@@ -1037,7 +1152,7 @@ impl<Uart: Instance> RxWithInterrupt<Uart> {
/// 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
/// 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 irq_end = self.uart().irq_end().read();
@@ -1058,15 +1173,10 @@ impl<Uart: Instance> RxWithInterrupt<Uart> {
// Timeout, empty the FIFO completely.
if irq_end.irq_rx_to().bit_is_set() {
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;
}
// While there is data in the FIFO, write it into the reception buffer
while let Ok(byte) = self.0.read_fifo() {
buf[result.bytes_read] = byte as u8;
result.bytes_read += 1;
}
}
@@ -1094,7 +1204,7 @@ impl<Uart: Instance> RxWithInterrupt<Uart> {
/// 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
/// 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,
context: &mut IrqContextTimeoutOrMaxSize,
buf: &mut [u8],
@@ -1143,12 +1253,13 @@ impl<Uart: Instance> RxWithInterrupt<Uart> {
if context.rx_idx == context.max_len {
break;
}
let read_result = self.0.read();
if let Some(byte) = self.read_handler(&mut result.errors, &read_result) {
buf[context.rx_idx] = byte;
context.rx_idx += 1;
} else {
break;
// While there is data in the FIFO, write it into the reception buffer
match self.0.read() {
Ok(byte) => {
buf[result.bytes_read] = byte;
result.bytes_read += 1;
}
Err(_) => break,
}
}
self.irq_completion_handler_max_size_timeout(&mut result, context);
@@ -1167,29 +1278,6 @@ impl<Uart: Instance> RxWithInterrupt<Uart> {
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>) {
let rx_status = self.uart().rxstatus().read();
@@ -1233,3 +1321,9 @@ impl<Uart: Instance> RxWithInterrupt<Uart> {
self.0.release()
}
}
pub mod tx_asynch;
pub use tx_asynch::*;
pub mod rx_asynch;
pub use rx_asynch::*;

View File

@@ -0,0 +1,448 @@
//! # Async UART reception functionality for the VA416xx family.
//!
//! This module provides the [RxAsync] and [RxAsyncOverwriting] struct which both implement the
//! [embedded_io_async::Read] trait.
//! This trait allows for asynchronous reception of data streams. Please note that this module does
//! not specify/declare the interrupt handlers which must be provided for async support to work.
//! However, it provides two interrupt handlers:
//!
//! - [on_interrupt_rx]
//! - [on_interrupt_rx_overwriting]
//!
//! The first two are used for the [RxAsync] struct, while the latter two are used with the
//! [RxAsyncOverwriting] struct. The later two will overwrite old values in the used ring buffer.
//!
//! Error handling is performed in the user interrupt handler by checking the [AsyncUartErrors]
//! structure returned by the interrupt handlers.
//!
//! # Example
//!
//! - [Async UART RX example](https://egit.irs.uni-stuttgart.de/rust/va108xx-rs/src/branch/main/examples/embassy/src/bin/async-uart-rx.rs)
use core::{cell::RefCell, convert::Infallible, future::Future, sync::atomic::Ordering};
use critical_section::Mutex;
use embassy_sync::waitqueue::AtomicWaker;
use embedded_io::ErrorType;
use portable_atomic::AtomicBool;
use va416xx::uart0 as uart_base;
use crate::enable_nvic_interrupt;
use super::{Bank, Instance, Rx, UartErrors};
static UART_RX_WAKERS: [AtomicWaker; 3] = [const { AtomicWaker::new() }; 3];
static RX_READ_ACTIVE: [AtomicBool; 3] = [const { AtomicBool::new(false) }; 3];
static RX_HAS_DATA: [AtomicBool; 3] = [const { AtomicBool::new(false) }; 3];
struct RxFuture {
uart_idx: usize,
}
impl RxFuture {
pub fn new<Uart: Instance>(_rx: &mut Rx<Uart>) -> Self {
RX_READ_ACTIVE[Uart::IDX as usize].store(true, Ordering::Relaxed);
Self {
uart_idx: Uart::IDX as usize,
}
}
}
impl Future for RxFuture {
type Output = Result<(), Infallible>;
fn poll(
self: core::pin::Pin<&mut Self>,
cx: &mut core::task::Context<'_>,
) -> core::task::Poll<Self::Output> {
UART_RX_WAKERS[self.uart_idx].register(cx.waker());
if RX_HAS_DATA[self.uart_idx].load(Ordering::Relaxed) {
return core::task::Poll::Ready(Ok(()));
}
core::task::Poll::Pending
}
}
#[derive(Debug, Clone, Copy)]
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
pub struct AsyncUartErrors {
/// Queue has overflowed, data might have been lost.
pub queue_overflow: bool,
/// UART errors.
pub uart_errors: UartErrors,
}
fn on_interrupt_handle_rx_errors(uart: &'static uart_base::RegisterBlock) -> Option<UartErrors> {
let rx_status = uart.rxstatus().read();
if rx_status.rxovr().bit_is_set()
|| rx_status.rxfrm().bit_is_set()
|| rx_status.rxpar().bit_is_set()
{
let mut errors_val = UartErrors::default();
if rx_status.rxovr().bit_is_set() {
errors_val.overflow = true;
}
if rx_status.rxfrm().bit_is_set() {
errors_val.framing = true;
}
if rx_status.rxpar().bit_is_set() {
errors_val.parity = true;
}
return Some(errors_val);
}
None
}
fn on_interrupt_rx_common_post_processing(
bank: Bank,
rx_enabled: bool,
read_some_data: bool,
irq_end: u32,
) -> Option<UartErrors> {
let idx = bank as usize;
if read_some_data {
RX_HAS_DATA[idx].store(true, Ordering::Relaxed);
if RX_READ_ACTIVE[idx].load(Ordering::Relaxed) {
UART_RX_WAKERS[idx].wake();
}
}
let mut errors = None;
let uart_regs = unsafe { bank.reg_block() };
// Check for RX errors
if rx_enabled {
errors = on_interrupt_handle_rx_errors(uart_regs);
}
// Clear the interrupt status bits
uart_regs.irq_clr().write(|w| unsafe { w.bits(irq_end) });
errors
}
/// Interrupt handler with overwriting behaviour when the ring buffer is full.
///
/// Should be called in the user interrupt handler to enable
/// asynchronous reception. This variant will overwrite old data in the ring buffer in case
/// the ring buffer is full.
pub fn on_interrupt_rx_overwriting<const N: usize>(
bank: Bank,
prod: &mut heapless::spsc::Producer<u8, N>,
shared_consumer: &Mutex<RefCell<Option<heapless::spsc::Consumer<'static, u8, N>>>>,
) -> Result<(), AsyncUartErrors> {
on_interrupt_rx_async_heapless_queue_overwriting(bank, prod, shared_consumer)
}
pub fn on_interrupt_rx_async_heapless_queue_overwriting<const N: usize>(
bank: Bank,
prod: &mut heapless::spsc::Producer<u8, N>,
shared_consumer: &Mutex<RefCell<Option<heapless::spsc::Consumer<'static, u8, N>>>>,
) -> Result<(), AsyncUartErrors> {
let uart_regs = unsafe { bank.reg_block() };
let irq_end = uart_regs.irq_end().read();
let enb_status = uart_regs.enable().read();
let rx_enabled = enb_status.rxenable().bit_is_set();
let mut read_some_data = false;
let mut queue_overflow = false;
// Half-Full interrupt. We have a guaranteed amount of data we can read.
if irq_end.irq_rx().bit_is_set() {
let available_bytes = uart_regs.rxfifoirqtrg().read().bits() as usize;
// If this interrupt bit is set, the trigger level is available at the very least.
// Read everything as fast as possible
for _ in 0..available_bytes {
let byte = uart_regs.data().read().bits();
if !prod.ready() {
queue_overflow = true;
critical_section::with(|cs| {
let mut cons_ref = shared_consumer.borrow(cs).borrow_mut();
cons_ref.as_mut().unwrap().dequeue();
});
}
prod.enqueue(byte as u8).ok();
}
read_some_data = true;
}
// Timeout, empty the FIFO completely.
if irq_end.irq_rx_to().bit_is_set() {
while uart_regs.rxstatus().read().rdavl().bit_is_set() {
// While there is data in the FIFO, write it into the reception buffer
let byte = uart_regs.data().read().bits();
if !prod.ready() {
queue_overflow = true;
critical_section::with(|cs| {
let mut cons_ref = shared_consumer.borrow(cs).borrow_mut();
cons_ref.as_mut().unwrap().dequeue();
});
}
prod.enqueue(byte as u8).ok();
}
read_some_data = true;
}
let uart_errors =
on_interrupt_rx_common_post_processing(bank, rx_enabled, read_some_data, irq_end.bits());
if uart_errors.is_some() || queue_overflow {
return Err(AsyncUartErrors {
queue_overflow,
uart_errors: uart_errors.unwrap_or_default(),
});
}
Ok(())
}
/// Interrupt handler for asynchronous RX operations.
///
/// Should be called in the user interrupt handler to enable asynchronous reception.
pub fn on_interrupt_rx<const N: usize>(
bank: Bank,
prod: &mut heapless::spsc::Producer<'_, u8, N>,
) -> Result<(), AsyncUartErrors> {
on_interrupt_rx_async_heapless_queue(bank, prod)
}
pub fn on_interrupt_rx_async_heapless_queue<const N: usize>(
bank: Bank,
prod: &mut heapless::spsc::Producer<'_, u8, N>,
) -> Result<(), AsyncUartErrors> {
let uart = unsafe { bank.reg_block() };
let irq_end = uart.irq_end().read();
let enb_status = uart.enable().read();
let rx_enabled = enb_status.rxenable().bit_is_set();
let mut read_some_data = false;
let mut queue_overflow = false;
// Half-Full interrupt. We have a guaranteed amount of data we can read.
if irq_end.irq_rx().bit_is_set() {
let available_bytes = uart.rxfifoirqtrg().read().bits() as usize;
// If this interrupt bit is set, the trigger level is available at the very least.
// Read everything as fast as possible
for _ in 0..available_bytes {
let byte = uart.data().read().bits();
if !prod.ready() {
queue_overflow = true;
}
prod.enqueue(byte as u8).ok();
}
read_some_data = true;
}
// Timeout, empty the FIFO completely.
if irq_end.irq_rx_to().bit_is_set() {
while uart.rxstatus().read().rdavl().bit_is_set() {
// While there is data in the FIFO, write it into the reception buffer
let byte = uart.data().read().bits();
if !prod.ready() {
queue_overflow = true;
}
prod.enqueue(byte as u8).ok();
}
read_some_data = true;
}
let uart_errors =
on_interrupt_rx_common_post_processing(bank, rx_enabled, read_some_data, irq_end.bits());
if uart_errors.is_some() || queue_overflow {
return Err(AsyncUartErrors {
queue_overflow,
uart_errors: uart_errors.unwrap_or_default(),
});
}
Ok(())
}
struct ActiveReadGuard(usize);
impl Drop for ActiveReadGuard {
fn drop(&mut self) {
RX_READ_ACTIVE[self.0].store(false, Ordering::Relaxed);
}
}
struct RxAsyncInner<Uart: Instance, const N: usize> {
rx: Rx<Uart>,
pub queue: heapless::spsc::Consumer<'static, u8, N>,
}
/// Core data structure to allow asynchronous UART reception.
///
/// If the ring buffer becomes full, data will be lost.
pub struct RxAsync<Uart: Instance, const N: usize>(Option<RxAsyncInner<Uart, N>>);
impl<Uart: Instance, const N: usize> ErrorType for RxAsync<Uart, N> {
/// Error reporting is done using the result of the interrupt functions.
type Error = Infallible;
}
fn stop_async_rx<Uart: Instance>(rx: &mut Rx<Uart>) {
rx.disable_interrupts();
rx.disable();
unsafe {
enable_nvic_interrupt(Uart::IRQ_RX);
}
rx.clear_fifo();
}
impl<Uart: Instance, const N: usize> RxAsync<Uart, N> {
/// Create a new asynchronous receiver.
///
/// The passed [heapless::spsc::Consumer] will be used to asynchronously receive data which
/// is filled by the interrupt handler [on_interrupt_rx].
pub fn new(mut rx: Rx<Uart>, queue: heapless::spsc::Consumer<'static, u8, N>) -> Self {
rx.disable_interrupts();
rx.disable();
rx.clear_fifo();
// Enable those together.
critical_section::with(|_| {
unsafe {
enable_nvic_interrupt(Uart::IRQ_RX);
}
rx.enable_interrupts();
rx.enable();
});
Self(Some(RxAsyncInner { rx, queue }))
}
pub fn stop(&mut self) {
stop_async_rx(&mut self.0.as_mut().unwrap().rx);
}
pub fn release(mut self) -> (Rx<Uart>, heapless::spsc::Consumer<'static, u8, N>) {
self.stop();
let inner = self.0.take().unwrap();
(inner.rx, inner.queue)
}
}
impl<Uart: Instance, const N: usize> Drop for RxAsync<Uart, N> {
fn drop(&mut self) {
self.stop();
}
}
impl<Uart: Instance, const N: usize> embedded_io_async::Read for RxAsync<Uart, N> {
async fn read(&mut self, buf: &mut [u8]) -> Result<usize, Self::Error> {
// Need to wait for the IRQ to read data and set this flag. If the queue is not
// empty, we can read data immediately.
if self.0.as_ref().unwrap().queue.len() == 0 {
RX_HAS_DATA[Uart::IDX as usize].store(false, Ordering::Relaxed);
}
let _guard = ActiveReadGuard(Uart::IDX as usize);
let mut handle_data_in_queue = |consumer: &mut heapless::spsc::Consumer<'static, u8, N>| {
let data_to_read = consumer.len().min(buf.len());
for byte in buf.iter_mut().take(data_to_read) {
// We own the consumer and we checked that the amount of data is guaranteed to be available.
*byte = unsafe { consumer.dequeue_unchecked() };
}
data_to_read
};
let mut_ref = self.0.as_mut().unwrap();
let fut = RxFuture::new(&mut mut_ref.rx);
// Data is available, so read that data immediately.
let read_data = handle_data_in_queue(&mut mut_ref.queue);
if read_data > 0 {
return Ok(read_data);
}
// Await data.
let _ = fut.await;
Ok(handle_data_in_queue(&mut mut_ref.queue))
}
}
struct RxAsyncOverwritingInner<Uart: Instance, const N: usize> {
rx: Rx<Uart>,
pub shared_consumer: &'static Mutex<RefCell<Option<heapless::spsc::Consumer<'static, u8, N>>>>,
}
/// Core data structure to allow asynchronous UART reception.
///
/// If the ring buffer becomes full, the oldest data will be overwritten when using the
/// [on_interrupt_rx_overwriting] interrupt handlers.
pub struct RxAsyncOverwriting<Uart: Instance, const N: usize>(
Option<RxAsyncOverwritingInner<Uart, N>>,
);
impl<Uart: Instance, const N: usize> ErrorType for RxAsyncOverwriting<Uart, N> {
/// Error reporting is done using the result of the interrupt functions.
type Error = Infallible;
}
impl<Uart: Instance, const N: usize> RxAsyncOverwriting<Uart, N> {
/// Create a new asynchronous receiver.
///
/// The passed shared [heapless::spsc::Consumer] will be used to asynchronously receive data
/// which is filled by the interrupt handler. The shared property allows using it in the
/// interrupt handler to overwrite old data.
pub fn new(
mut rx: Rx<Uart>,
shared_consumer: &'static Mutex<RefCell<Option<heapless::spsc::Consumer<'static, u8, N>>>>,
) -> Self {
rx.disable_interrupts();
rx.disable();
rx.clear_fifo();
// Enable those together.
critical_section::with(|_| {
rx.enable_interrupts();
rx.enable();
});
Self(Some(RxAsyncOverwritingInner {
rx,
shared_consumer,
}))
}
pub fn stop(&mut self) {
stop_async_rx(&mut self.0.as_mut().unwrap().rx);
}
pub fn release(mut self) -> Rx<Uart> {
self.stop();
let inner = self.0.take().unwrap();
inner.rx
}
}
impl<Uart: Instance, const N: usize> Drop for RxAsyncOverwriting<Uart, N> {
fn drop(&mut self) {
self.stop();
}
}
impl<Uart: Instance, const N: usize> embedded_io_async::Read for RxAsyncOverwriting<Uart, N> {
async fn read(&mut self, buf: &mut [u8]) -> Result<usize, Self::Error> {
// Need to wait for the IRQ to read data and set this flag. If the queue is not
// empty, we can read data immediately.
critical_section::with(|cs| {
let queue = self.0.as_ref().unwrap().shared_consumer.borrow(cs);
if queue.borrow().as_ref().unwrap().len() == 0 {
RX_HAS_DATA[Uart::IDX as usize].store(false, Ordering::Relaxed);
}
});
let _guard = ActiveReadGuard(Uart::IDX as usize);
let mut handle_data_in_queue = |inner: &mut RxAsyncOverwritingInner<Uart, N>| {
critical_section::with(|cs| {
let mut consumer_ref = inner.shared_consumer.borrow(cs).borrow_mut();
let consumer = consumer_ref.as_mut().unwrap();
let data_to_read = consumer.len().min(buf.len());
for byte in buf.iter_mut().take(data_to_read) {
// We own the consumer and we checked that the amount of data is guaranteed to be available.
*byte = unsafe { consumer.dequeue_unchecked() };
}
data_to_read
})
};
let fut = RxFuture::new(&mut self.0.as_mut().unwrap().rx);
// Data is available, so read that data immediately.
let read_data = handle_data_in_queue(self.0.as_mut().unwrap());
if read_data > 0 {
return Ok(read_data);
}
// Await data.
let _ = fut.await;
let read_data = handle_data_in_queue(self.0.as_mut().unwrap());
Ok(read_data)
}
}

View File

@@ -0,0 +1,263 @@
//! # Async UART transmission functionality for the VA416xx family.
//!
//! This module provides the [TxAsync] struct which implements the [embedded_io_async::Write] trait.
//! This trait allows for asynchronous sending of data streams. Please note that this module does
//! not specify/declare the interrupt handlers which must be provided for async support to work.
//! However, it the [on_interrupt_tx] interrupt handler.
//!
//! This handler should be called in ALL user interrupt handlers which handle UART TX interrupts
//! for a given UART bank.
//!
//! # Example
//!
//! - [Async UART TX example](https://egit.irs.uni-stuttgart.de/rust/va416xx-rs/src/branch/main/examples/embassy/src/bin/async-uart-tx.rs)
use core::{cell::RefCell, future::Future};
use critical_section::Mutex;
use embassy_sync::waitqueue::AtomicWaker;
use embedded_io_async::Write;
use portable_atomic::AtomicBool;
use super::*;
static UART_TX_WAKERS: [AtomicWaker; 3] = [const { AtomicWaker::new() }; 3];
static TX_CONTEXTS: [Mutex<RefCell<TxContext>>; 3] =
[const { Mutex::new(RefCell::new(TxContext::new())) }; 3];
// Completion flag. Kept outside of the context structure as an atomic to avoid
// critical section.
static TX_DONE: [AtomicBool; 3] = [const { AtomicBool::new(false) }; 3];
/// This is a generic interrupt handler to handle asynchronous UART TX operations for a given
/// UART bank.
///
/// The user has to call this once in the interrupt handler responsible for the TX interrupts on
/// the given UART bank.
pub fn on_interrupt_tx(bank: Bank) {
let uart = unsafe { bank.reg_block() };
let idx = bank as usize;
let irq_enb = uart.irq_enb().read();
// IRQ is not related to TX.
if irq_enb.irq_tx().bit_is_clear() || irq_enb.irq_tx_empty().bit_is_clear() {
return;
}
let tx_status = uart.txstatus().read();
let unexpected_overrun = tx_status.wrlost().bit_is_set();
let mut context = critical_section::with(|cs| {
let context_ref = TX_CONTEXTS[idx].borrow(cs);
*context_ref.borrow()
});
context.tx_overrun = unexpected_overrun;
if context.progress >= context.slice.len && !tx_status.wrbusy().bit_is_set() {
uart.irq_enb().modify(|_, w| {
w.irq_tx().clear_bit();
w.irq_tx_empty().clear_bit();
w.irq_tx_status().clear_bit()
});
uart.enable().modify(|_, w| w.txenable().clear_bit());
// Write back updated context structure.
critical_section::with(|cs| {
let context_ref = TX_CONTEXTS[idx].borrow(cs);
*context_ref.borrow_mut() = context;
});
// Transfer is done.
TX_DONE[idx].store(true, core::sync::atomic::Ordering::Relaxed);
UART_TX_WAKERS[idx].wake();
return;
}
// Safety: We documented that the user provided slice must outlive the future, so we convert
// the raw pointer back to the slice here.
let slice = unsafe { core::slice::from_raw_parts(context.slice.data, context.slice.len) };
while context.progress < context.slice.len {
let wrrdy = uart.txstatus().read().wrrdy().bit_is_set();
if !wrrdy {
break;
}
// Safety: TX structure is owned by the future which does not write into the the data
// register, so we can assume we are the only one writing to the data register.
uart.data()
.write(|w| unsafe { w.bits(slice[context.progress] as u32) });
context.progress += 1;
}
// Write back updated context structure.
critical_section::with(|cs| {
let context_ref = TX_CONTEXTS[idx].borrow(cs);
*context_ref.borrow_mut() = context;
});
}
#[derive(Debug, Copy, Clone)]
pub struct TxContext {
progress: usize,
tx_overrun: bool,
slice: RawBufSlice,
}
#[allow(clippy::new_without_default)]
impl TxContext {
pub const fn new() -> Self {
Self {
progress: 0,
tx_overrun: false,
slice: RawBufSlice::new_empty(),
}
}
}
#[derive(Debug, Copy, Clone)]
struct RawBufSlice {
data: *const u8,
len: usize,
}
/// Safety: This type MUST be used with mutex to ensure concurrent access is valid.
unsafe impl Send for RawBufSlice {}
impl RawBufSlice {
/// # Safety
///
/// This function stores the raw pointer of the passed data slice. The user MUST ensure
/// that the slice outlives the data structure.
#[allow(dead_code)]
const unsafe fn new(data: &[u8]) -> Self {
Self {
data: data.as_ptr(),
len: data.len(),
}
}
const fn new_empty() -> Self {
Self {
data: core::ptr::null(),
len: 0,
}
}
/// # Safety
///
/// This function stores the raw pointer of the passed data slice. The user MUST ensure
/// that the slice outlives the data structure.
pub unsafe fn set(&mut self, data: &[u8]) {
self.data = data.as_ptr();
self.len = data.len();
}
}
pub struct TxFuture {
uart_idx: usize,
}
impl TxFuture {
/// # Safety
///
/// This function stores the raw pointer of the passed data slice. The user MUST ensure
/// that the slice outlives the data structure.
pub unsafe fn new<Uart: Instance>(tx: &mut Tx<Uart>, data: &[u8]) -> Self {
TX_DONE[Uart::IDX as usize].store(false, core::sync::atomic::Ordering::Relaxed);
tx.disable_interrupts();
tx.disable();
tx.clear_fifo();
let uart_tx = unsafe { tx.uart() };
let init_fill_count = core::cmp::min(data.len(), 16);
// We fill the FIFO.
for data in data.iter().take(init_fill_count) {
uart_tx.data().write(|w| unsafe { w.bits(*data as u32) });
}
critical_section::with(|cs| {
let context_ref = TX_CONTEXTS[Uart::IDX as usize].borrow(cs);
let mut context = context_ref.borrow_mut();
context.slice.set(data);
context.progress = init_fill_count;
// Ensure those are enabled inside a critical section at the same time. Can lead to
// weird glitches otherwise.
tx.enable_interrupts();
tx.enable();
});
Self {
uart_idx: Uart::IDX as usize,
}
}
}
impl Future for TxFuture {
type Output = Result<usize, TxOverrunError>;
fn poll(
self: core::pin::Pin<&mut Self>,
cx: &mut core::task::Context<'_>,
) -> core::task::Poll<Self::Output> {
UART_TX_WAKERS[self.uart_idx].register(cx.waker());
if TX_DONE[self.uart_idx].swap(false, core::sync::atomic::Ordering::Relaxed) {
let progress = critical_section::with(|cs| {
TX_CONTEXTS[self.uart_idx].borrow(cs).borrow().progress
});
return core::task::Poll::Ready(Ok(progress));
}
core::task::Poll::Pending
}
}
impl Drop for TxFuture {
fn drop(&mut self) {
let reg_block = match self.uart_idx {
0 => unsafe { pac::Uart0::reg_block() },
1 => unsafe { pac::Uart1::reg_block() },
2 => unsafe { pac::Uart2::reg_block() },
_ => unreachable!(),
};
disable_tx_interrupts(reg_block);
disable_tx(reg_block);
}
}
pub struct TxAsync<Uart: Instance> {
tx: Tx<Uart>,
}
impl<Uart: Instance> TxAsync<Uart> {
/// Create a new asynchronous TX object.
///
/// This function also enable the NVIC interrupt, but does not enable the peripheral specific
/// interrupts.
pub fn new(tx: Tx<Uart>) -> Self {
// Safety: We own TX now.
unsafe { enable_nvic_interrupt(Uart::IRQ_TX) };
Self { tx }
}
/// This function also disables the NVIC interrupt.
pub fn release(self) -> Tx<Uart> {
disable_nvic_interrupt(Uart::IRQ_TX);
self.tx
}
}
#[derive(Debug, thiserror::Error)]
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
#[error("TX overrun error")]
pub struct TxOverrunError;
impl embedded_io_async::Error for TxOverrunError {
fn kind(&self) -> embedded_io_async::ErrorKind {
embedded_io_async::ErrorKind::Other
}
}
impl<Uart: Instance> embedded_io::ErrorType for TxAsync<Uart> {
type Error = TxOverrunError;
}
impl<Uart: Instance> Write for TxAsync<Uart> {
/// Write a buffer asynchronously.
///
/// This implementation is not side effect free, and a started future might have already
/// written part of the passed buffer.
async fn write(&mut self, buf: &[u8]) -> Result<usize, Self::Error> {
let fut = unsafe { TxFuture::new(&mut self.tx, buf) };
fut.await
}
}

View File

@@ -8,6 +8,10 @@ and this project adheres to [Semantic Versioning](http://semver.org/).
## [unreleased]
## [v0.4.0] 2025-02-18
- Re-generated PAC with `svd2rust` v0.35.0 and added optional `defmt` and `Debug` implementations
## [v0.3.0] 2025-02-13
- Re-generated PAC with `svd2rust` v0.35.0

View File

@@ -8,6 +8,19 @@ and this project adheres to [Semantic Versioning](http://semver.org/).
# [unreleased]
# [v0.1.2] 2025-03-07
- Bump allowed HAL version to v0.5
# [v0.1.1] 2025-02-18
- Bump allowed HAL version to v0.4
# [v0.1.0] 2024-10-01
- 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

@@ -1,6 +1,6 @@
[package]
name = "vorago-peb1"
version = "0.1.0"
version = "0.1.2"
authors = ["Robin Mueller <muellerr@irs.uni-stuttgart.de>"]
edition = "2021"
description = "Board Support Crate for the Vorago PEB1 development board"
@@ -14,15 +14,9 @@ categories = ["embedded", "no-std", "hardware-support"]
cortex-m = "0.7"
cortex-m-rt = "0.7"
embedded-hal = "1"
lis2dh12 = { version = "0.7", features = ["out_f32"] }
[dependencies.va416xx-hal]
path = "../va416xx-hal"
features = ["va41630"]
version = ">=0.3, <0.4"
[dependencies.lis2dh12]
version = "0.7"
features = ["out_f32"]
va416xx-hal = { version = ">=0.3, <=0.5", features = ["va41630"] }
[features]
rt = ["va416xx-hal/rt"]

View File

@@ -353,7 +353,7 @@
{
"type": "cortex-debug",
"request": "launch",
"name": "UART Echo with IRQ",
"name": "UART Line Terminated Example",
"servertype": "jlink",
"jlinkscript": "${workspaceFolder}/jlink/JLinkSettings.JLinkScript",
"cwd": "${workspaceRoot}",
@@ -500,5 +500,95 @@
]
}
},
{
"type": "cortex-debug",
"request": "launch",
"name": "Async GPIO Example",
"servertype": "jlink",
"jlinkscript": "${workspaceFolder}/jlink/JLinkSettings.JLinkScript",
"cwd": "${workspaceRoot}",
"device": "Cortex-M4",
"svdFile": "${workspaceFolder}/va416xx/svd/va416xx.svd.patched",
"preLaunchTask": "async-gpio",
"overrideLaunchCommands": [
"monitor halt",
"monitor reset",
"load",
],
"executable": "${workspaceFolder}/target/thumbv7em-none-eabihf/debug/async-gpio",
"interface": "swd",
"runToEntryPoint": "main",
"rttConfig": {
"enabled": true,
"address": "auto",
"decoders": [
{
"port": 0,
"timestamp": true,
"type": "console"
}
]
}
},
{
"type": "cortex-debug",
"request": "launch",
"name": "Async UART TX Example",
"servertype": "jlink",
"jlinkscript": "${workspaceFolder}/jlink/JLinkSettings.JLinkScript",
"cwd": "${workspaceRoot}",
"device": "Cortex-M4",
"svdFile": "${workspaceFolder}/va416xx/svd/va416xx.svd.patched",
"preLaunchTask": "async-uart-tx",
"overrideLaunchCommands": [
"monitor halt",
"monitor reset",
"load",
],
"executable": "${workspaceFolder}/target/thumbv7em-none-eabihf/debug/async-uart-tx",
"interface": "swd",
"runToEntryPoint": "main",
"rttConfig": {
"enabled": true,
"address": "auto",
"decoders": [
{
"port": 0,
"timestamp": true,
"type": "console"
}
]
}
},
{
"type": "cortex-debug",
"request": "launch",
"name": "Async UART RX Example",
"servertype": "jlink",
"jlinkscript": "${workspaceFolder}/jlink/JLinkSettings.JLinkScript",
"cwd": "${workspaceRoot}",
"device": "Cortex-M4",
"svdFile": "${workspaceFolder}/va416xx/svd/va416xx.svd.patched",
"preLaunchTask": "async-uart-rx",
"overrideLaunchCommands": [
"monitor halt",
"monitor reset",
"load",
],
"executable": "${workspaceFolder}/target/thumbv7em-none-eabihf/debug/async-uart-rx",
"interface": "swd",
"runToEntryPoint": "main",
"rttConfig": {
"enabled": true,
"address": "auto",
"decoders": [
{
"port": 0,
"timestamp": true,
"type": "console"
}
]
}
},
]
}
}

View File

@@ -199,6 +199,19 @@
"kind": "build",
}
},
{
"label": "async-gpio",
"type": "shell",
"command": "~/.cargo/bin/cargo", // note: full path to the cargo
"args": [
"build",
"--bin",
"async-gpio"
],
"group": {
"kind": "build",
}
},
{
"label": "rtic-example",
"type": "shell",
@@ -212,5 +225,31 @@
"kind": "build",
}
},
{
"label": "async-uart-tx",
"type": "shell",
"command": "~/.cargo/bin/cargo", // note: full path to the cargo
"args": [
"build",
"--bin",
"async-uart-tx"
],
"group": {
"kind": "build",
}
},
{
"label": "async-uart-rx",
"type": "shell",
"command": "~/.cargo/bin/cargo", // note: full path to the cargo
"args": [
"build",
"--bin",
"async-uart-rx"
],
"group": {
"kind": "build",
}
},
]
}