27 Commits

Author SHA1 Message Date
ae04f2d537 document probe-rs 2025-02-13 18:41:51 +01:00
62a4123f82 Merge pull request 'minor improvments for embassy lib' (#51) from minor-improvements-embassy into main
Reviewed-on: #51
2025-02-13 18:41:15 +01:00
17f13fc4dc minor improvments for embassy lib 2025-02-13 18:27:05 +01:00
b4f1512463 docs patch 2 2025-02-13 15:47:50 +01:00
e9ec01fc60 Merge pull request 'doc fixes' (#48) from va108xx-embassy-doc-fix into main
Reviewed-on: #48
2025-02-13 15:38:50 +01:00
d57cd383cd doc fixes 2025-02-13 15:38:38 +01:00
df4e943f48 Merge pull request 'use released package versions' (#47) from use-released-package-versions into main
Reviewed-on: #47
2025-02-13 15:26:41 +01:00
93b67a4795 README update 2025-02-13 15:26:24 +01:00
a9f2e6dcee use released package versions 2025-02-13 15:25:01 +01:00
271c853df1 Merge pull request 'prepare va108xx-embassy release' (#46) from prepare-va108xx-embassy-release into main
Reviewed-on: #46
2025-02-13 15:22:36 +01:00
107189b166 prepare va108xx-embassy release 2025-02-13 15:22:18 +01:00
872944bebf Merge pull request 'prepare BSP release' (#45) from vorago-reb1-release into main
Reviewed-on: #45
2025-02-13 15:08:22 +01:00
d077bb6210 prepare BSP release 2025-02-13 15:06:02 +01:00
bd286bdb2a Merge pull request 'small README tweak' (#44) from readme-tweak into main
Reviewed-on: #44
2025-02-13 14:56:48 +01:00
d3cc00a4a5 small README tweak 2025-02-13 14:56:24 +01:00
1018a65447 Merge pull request 'add more defmt features' (#42) from add-more-defmt-features into main
Reviewed-on: #42
2025-02-13 14:52:03 +01:00
0a31b637e6 add more defmt features 2025-02-13 14:51:54 +01:00
6e1ae70054 Merge pull request 'add various debug impls' (#43) from add-various-debug-impls into main
Reviewed-on: #43
2025-02-13 14:51:27 +01:00
8ae2d6189a add various debug impls 2025-02-13 14:50:00 +01:00
549a98dbaf Merge pull request 'all doc fixes' (#41) from doc-fixes into main
Reviewed-on: #41
2025-02-13 12:23:56 +01:00
e24fc608a3 all doc fixes 2025-02-13 11:44:14 +01:00
7b74312013 Merge pull request 'completed async RX support as well' (#39) from async-uart-rx into main
Reviewed-on: #39
2025-02-13 11:33:36 +01:00
417f5b7f67 completed async RX support as well 2025-02-13 11:31:17 +01:00
3e796ef22b Merge pull request 'UART B hotfix' (#40) from uart-b-hotfix into main
Reviewed-on: #40
2025-02-13 11:30:37 +01:00
b145047b95 UART B hotfix 2025-02-13 11:14:39 +01:00
82b4c16f8e Merge pull request 'bump PAC version' (#38) from bump-va108xx-version into main
Reviewed-on: #38
2025-02-12 14:57:53 +01:00
189ac2d256 bump PAC version 2025-02-12 14:47:18 +01:00
37 changed files with 988 additions and 166 deletions

View File

@ -4,10 +4,9 @@
# runner = "arm-none-eabi-gdb -q -x openocd.gdb"
# runner = "gdb-multiarch -q -x openocd.gdb"
# runner = "gdb -q -x openocd.gdb"
runner = "gdb-multiarch -q -x jlink.gdb"
# runner = "gdb-multiarch -q -x jlink.gdb"
# Probe-rs is currently problematic: https://github.com/probe-rs/probe-rs/issues/2567
# runner = "probe-rs run --chip VA108xx --chip-description-path ./scripts/VA108xx_Series.yaml"
runner = "probe-rs run --chip VA108xx_RAM --protocol jtag"
# runner = ["probe-rs", "run", "--chip", "$CHIP", "--log-format", "{L} {s}"]
rustflags = [

View File

@ -14,6 +14,8 @@ This workspace contains the following released crates:
crate containing basic low-level register definition.
- The [`va108xx-hal`](https://egit.irs.uni-stuttgart.de/rust/va108xx-rs/src/branch/main/va108xx-hal)
HAL crate containing higher-level abstractions on top of the PAC register crate.
- The [`va108xx-embassy`](https://egit.irs.uni-stuttgart.de/rust/va108xx-rs/src/branch/main/va108xx-embassy)
crate containing support for running the embassy-rs RTOS.
- The [`vorago-reb1`](https://egit.irs.uni-stuttgart.de/rust/va108xx-rs/src/branch/main/vorago-reb1)
BSP crate containing support for the REB1 development board.
@ -58,14 +60,56 @@ You can then adapt the files in `.vscode` to your needs.
You can use CLI or VS Code for flashing, running and debugging. In any case, take
care of installing the pre-requisites first.
### Pre-Requisites
### Using CLI with probe-rs
Install [probe-rs](https://github.com/probe-rs/probe-rs) first.
You can use `probe-rs` to run the software and display RTT log output. However, debugging does not
work yet.
After installation, you can run the following command
```sh
probe-rs run --chip VA108xx_RAM --protocol jtag --example blinky
```
to flash and run the blinky program on the RAM. There is also a `VA108xx` chip target
available for persistent flashing.
Runner configuration avilable in the `.cargo/def-config.toml` file to use `probe-rs` for
convenience.
### Using VS Code
Assuming a working debug connection to your VA108xx board, you can debug using VS Code with
the [`Cortex-Debug` plugin](https://marketplace.visualstudio.com/items?itemName=marus25.cortex-debug).
Please make sure that [`objdump-multiarch` and `nm-multiarch`](https://forums.raspberrypi.com/viewtopic.php?t=333146)
are installed as well.
Some sample configuration files for VS code were provided and can be used by running
`cp -rT vscode .vscode` like specified above. After that, you can use `Run and Debug`
to automatically rebuild and flash your application.
If you would like to use a custom GDB application, you can specify the gdb binary in the following
configuration variables in your `settings.json`:
- `"cortex-debug.gdbPath"`
- `"cortex-debug.gdbPath.linux"`
- `"cortex-debug.gdbPath.windows"`
- `"cortex-debug.gdbPath.osx"`
The provided VS Code configurations also provide an integrated RTT logger, which you can access
via the terminal at `RTT Ch:0 console`. In order for the RTT block address detection to
work properly, `objdump-multiarch` and `nm-multiarch` need to be installed.
### Using CLI with GDB and Segger J-Link Tools
Install the following two tools first:
1. [SEGGER J-Link tools](https://www.segger.com/downloads/jlink/) installed
2. [gdb-multiarch](https://packages.debian.org/sid/gdb-multiarch) or similar
cross-architecture debugger installed. All commands here assume `gdb-multiarch`.
### Using CLI
You can build the blinky example application with the following command
```sh
@ -99,25 +143,8 @@ runner = "gdb-multiarch -q -x jlink/jlink.gdb"
After that, you can simply use `cargo run --example blinky` to flash the blinky
example.
### Using VS Code
### Using the RTT Viewer
Assuming a working debug connection to your VA108xx board, you can debug using VS Code with
the [`Cortex-Debug` plugin](https://marketplace.visualstudio.com/items?itemName=marus25.cortex-debug).
Please make sure that [`objdump-multiarch` and `nm-multiarch`](https://forums.raspberrypi.com/viewtopic.php?t=333146)
are installed as well.
Some sample configuration files for VS code were provided and can be used by running
`cp -rT vscode .vscode` like specified above. After that, you can use `Run and Debug`
to automatically rebuild and flash your application.
If you would like to use a custom GDB application, you can specify the gdb binary in the following
configuration variables in your `settings.json`:
- `"cortex-debug.gdbPath"`
- `"cortex-debug.gdbPath.linux"`
- `"cortex-debug.gdbPath.windows"`
- `"cortex-debug.gdbPath.osx"`
The provided VS Code configurations also provide an integrated RTT logger, which you can access
via the terminal at `RTT Ch:0 console`. In order for the RTT block address detection to
work properly, `objdump-multiarch` and `nm-multiarch` need to be installed.
The Segger RTT viewer can be used to display log messages received from the target. The base
address for the RTT block placement is 0x10000000. It is recommended to use a search range of
0x1000 around that base address when using the RTT viewer.

View File

@ -15,10 +15,12 @@ num_enum = { version = "0.7", default-features = false }
static_assertions = "1"
[dependencies.va108xx-hal]
path = "../va108xx-hal"
version = "0.9"
# path = "../va108xx-hal"
[dependencies.vorago-reb1]
path = "../vorago-reb1"
version = "0.7"
# path = "../vorago-reb1"
[features]
default = []

View File

@ -9,7 +9,10 @@ cortex-m = { version = "0.7", features = ["critical-section-single-core"] }
cortex-m-rt = "0.7"
embedded-hal = "1"
embedded-hal-async = "1"
embedded-io = "0.6"
embedded-io-async = "0.6"
heapless = "0.8"
static_cell = "2"
rtt-target = "0.6"
panic-rtt-target = "0.2"
@ -24,8 +27,8 @@ embassy-executor = { version = "0.7", features = [
"executor-interrupt"
]}
va108xx-hal = { path = "../../va108xx-hal" }
va108xx-embassy = { path = "../../va108xx-embassy", default-features = false }
va108xx-hal = "0.9"
va108xx-embassy = "0.1"
[features]
default = ["ticks-hz-1_000", "va108xx-embassy/irq-oc30-oc31"]

View File

@ -0,0 +1,171 @@
//! Asynchronous UART reception example application.
//!
//! This application receives data on two UARTs permanently using a ring buffer.
//! The ring buffer are read them asynchronously. UART A is received on ports PA8 and PA9.
//! UART B is received on ports PA2 and PA3.
//!
//! Instructions:
//!
//! 1. Tie a USB to UART converter with RX to PA9 and TX to PA8 for UART A.
//! Tie a USB to UART converter with RX to PA3 and TX to PA2 for UART B.
//! 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_executor::Spawner;
use embassy_time::Instant;
use embedded_hal::digital::StatefulOutputPin;
use embedded_io::Write;
use embedded_io_async::Read;
use heapless::spsc::{Consumer, Producer, Queue};
use panic_rtt_target as _;
use rtt_target::{rprintln, rtt_init_print};
use va108xx_embassy::embassy;
use va108xx_hal::{
gpio::PinsA,
pac::{self, interrupt},
prelude::*,
uart::{
self, on_interrupt_uart_b_overwriting,
rx_asynch::{on_interrupt_uart_a, RxAsync},
RxAsyncSharedConsumer, Tx,
},
InterruptConfig,
};
const SYSCLK_FREQ: Hertz = Hertz::from_raw(50_000_000);
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));
static QUEUE_UART_B: static_cell::ConstStaticCell<Queue<u8, 256>> =
static_cell::ConstStaticCell::new(Queue::new());
static PRODUCER_UART_B: Mutex<RefCell<Option<Producer<u8, 256>>>> = Mutex::new(RefCell::new(None));
static CONSUMER_UART_B: Mutex<RefCell<Option<Consumer<u8, 256>>>> = Mutex::new(RefCell::new(None));
// main is itself an async function.
#[embassy_executor::main]
async fn main(spawner: Spawner) {
rtt_init_print!();
rprintln!("-- VA108xx Async UART RX Demo --");
let mut dp = pac::Peripherals::take().unwrap();
// Safety: Only called once here.
unsafe {
embassy::init(
&mut dp.sysconfig,
&dp.irqsel,
SYSCLK_FREQ,
dp.tim23,
dp.tim22,
);
}
let porta = PinsA::new(&mut dp.sysconfig, dp.porta);
let mut led0 = porta.pa10.into_readable_push_pull_output();
let mut led1 = porta.pa7.into_readable_push_pull_output();
let mut led2 = porta.pa6.into_readable_push_pull_output();
let tx_uart_a = porta.pa9.into_funsel_2();
let rx_uart_a = porta.pa8.into_funsel_2();
let uarta = uart::Uart::new_with_interrupt(
&mut dp.sysconfig,
50.MHz(),
dp.uarta,
(tx_uart_a, rx_uart_a),
115200.Hz(),
InterruptConfig::new(pac::Interrupt::OC2, true, true),
);
let tx_uart_b = porta.pa3.into_funsel_2();
let rx_uart_b = porta.pa2.into_funsel_2();
let uartb = uart::Uart::new_with_interrupt(
&mut dp.sysconfig,
50.MHz(),
dp.uartb,
(tx_uart_b, rx_uart_b),
115200.Hz(),
InterruptConfig::new(pac::Interrupt::OC3, true, true),
);
let (mut tx_uart_a, rx_uart_a) = uarta.split();
let (tx_uart_b, rx_uart_b) = uartb.split();
let (prod_uart_a, cons_uart_a) = QUEUE_UART_A.take().split();
// Pass the producer to the interrupt handler.
let (prod_uart_b, cons_uart_b) = QUEUE_UART_B.take().split();
critical_section::with(|cs| {
*PRODUCER_UART_A.borrow(cs).borrow_mut() = Some(prod_uart_a);
*PRODUCER_UART_B.borrow(cs).borrow_mut() = Some(prod_uart_b);
*CONSUMER_UART_B.borrow(cs).borrow_mut() = Some(cons_uart_b);
});
let mut async_rx_uart_a = RxAsync::new(rx_uart_a, cons_uart_a);
let async_rx_uart_b = RxAsyncSharedConsumer::new(rx_uart_b, &CONSUMER_UART_B);
spawner
.spawn(uart_b_task(async_rx_uart_b, tx_uart_b))
.unwrap();
let mut buf = [0u8; 256];
loop {
rprintln!("Current time UART A: {}", Instant::now().as_secs());
led0.toggle().ok();
led1.toggle().ok();
led2.toggle().ok();
let read_bytes = async_rx_uart_a.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();
}
}
#[embassy_executor::task]
async fn uart_b_task(mut async_rx: RxAsyncSharedConsumer<pac::Uartb, 256>, mut tx: Tx<pac::Uartb>) {
let mut buf = [0u8; 256];
loop {
rprintln!("Current time UART B: {}", Instant::now().as_secs());
// Infallible asynchronous operation.
let read_bytes = async_rx.read(&mut buf).await.unwrap();
let read_str = core::str::from_utf8(&buf[..read_bytes]).unwrap();
rprintln!(
"Read {} bytes asynchronously on UART B: {:?}",
read_bytes,
read_str
);
tx.write_all(read_str.as_bytes()).unwrap();
}
}
#[interrupt]
#[allow(non_snake_case)]
fn OC2() {
let mut prod =
critical_section::with(|cs| PRODUCER_UART_A.borrow(cs).borrow_mut().take().unwrap());
let errors = on_interrupt_uart_a(&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);
}
}
#[interrupt]
#[allow(non_snake_case)]
fn OC3() {
let mut prod =
critical_section::with(|cs| PRODUCER_UART_B.borrow(cs).borrow_mut().take().unwrap());
let errors = on_interrupt_uart_b_overwriting(&mut prod, &CONSUMER_UART_B);
critical_section::with(|cs| *PRODUCER_UART_B.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 B errors: {:?}", errors);
}
}

View File

@ -1,3 +1,13 @@
//! Asynchronous UART transmission example application.
//!
//! This application receives sends 4 strings with different sizes permanently using UART A.
//! Ports PA8 and PA9 are used for this.
//!
//! Instructions:
//!
//! 1. Tie a USB to UART converter with RX to PA9 and TX to PA8 for UART A.
//! 2. Connect to the serial interface by using an application like Putty or picocom. You can
//! can verify the correctness of the sent strings.
#![no_std]
#![no_main]
use embassy_executor::Spawner;
@ -28,7 +38,7 @@ const STR_LIST: &[&str] = &[
#[embassy_executor::main]
async fn main(_spawner: Spawner) {
rtt_init_print!();
rprintln!("-- VA108xx Embassy Demo --");
rprintln!("-- VA108xx Async UART TX Demo --");
let mut dp = pac::Peripherals::take().unwrap();

View File

@ -22,5 +22,5 @@ rtic-sync = { version = "1.3", features = ["defmt-03"] }
once_cell = {version = "1", default-features = false, features = ["critical-section"]}
ringbuf = { version = "0.4.7", default-features = false, features = ["portable-atomic"] }
va108xx-hal = { version = "0.8", path = "../../va108xx-hal" }
vorago-reb1 = { path = "../../vorago-reb1" }
va108xx-hal = "0.9"
vorago-reb1 = "0.7"

View File

@ -16,9 +16,8 @@ embedded-io = "0.6"
cortex-m-semihosting = "0.5.0"
[dependencies.va108xx-hal]
path = "../../va108xx-hal"
version = "0.8"
version = "0.9"
features = ["rt", "defmt"]
[dependencies.vorago-reb1]
path = "../../vorago-reb1"
version = "0.7"

View File

@ -27,15 +27,15 @@ fn main() -> ! {
let gpioa = PinsA::new(&mut dp.sysconfig, dp.porta);
let tx = gpioa.pa9.into_funsel_2();
let rx = gpioa.pa8.into_funsel_2();
let uarta = uart::Uart::new_without_interrupt(
let uart = uart::Uart::new_without_interrupt(
&mut dp.sysconfig,
50.MHz(),
dp.uarta,
(tx, rx),
115200.Hz(),
);
let (mut tx, mut rx) = uarta.split();
let (mut tx, mut rx) = uart.split();
writeln!(tx, "Hello World\r").unwrap();
loop {
// Echo what is received on the serial link.

View File

@ -29,7 +29,9 @@ rtic-monotonics = { version = "2", features = ["cortex-m-systick"] }
rtic-sync = {version = "1", features = ["defmt-03"]}
[dependencies.va108xx-hal]
path = "../va108xx-hal"
version = "0.9"
# path = "../va108xx-hal"
[dependencies.vorago-reb1]
path = "../vorago-reb1"
version = "0.7"
# path = "../vorago-reb1"

View File

@ -0,0 +1,17 @@
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.2] and [v0.1.1] 2025-02-13
Docs patch
## [v0.1.0] 2025-02-13
Initial release

View File

@ -1,11 +1,17 @@
[package]
name = "va108xx-embassy"
version = "0.1.0"
version = "0.1.2"
edition = "2021"
authors = ["Robin Mueller <muellerr@irs.uni-stuttgart.de>"]
description = "Embassy-rs support for the Vorago VA108xx family of microcontrollers"
homepage = "https://egit.irs.uni-stuttgart.de/rust/va108xx-rs"
repository = "https://egit.irs.uni-stuttgart.de/rust/va108xx-rs"
license = "Apache-2.0"
keywords = ["no-std", "hal", "cortex-m", "vorago", "va108xx"]
categories = ["aerospace", "embedded", "no-std", "hardware-support"]
[dependencies]
critical-section = "1"
portable-atomic = { version = "1", features = ["unsafe-assume-single-core"]}
embassy-sync = "0.6"
embassy-executor = "0.7"
@ -14,8 +20,12 @@ embassy-time-queue-utils = "0.1"
once_cell = { version = "1", default-features = false, features = ["critical-section"] }
[dependencies.va108xx-hal]
path = "../va108xx-hal"
va108xx-hal = "0.9"
[target.'cfg(all(target_arch = "arm", target_os = "none"))'.dependencies]
portable-atomic = { version = "1", features = ["unsafe-assume-single-core"] }
[target.'cfg(not(all(target_arch = "arm", target_os = "none")))'.dependencies]
portable-atomic = "1"
[features]
default = ["irq-oc30-oc31"]
@ -25,3 +35,6 @@ irqs-in-lib = []
irq-oc28-oc29 = ["irqs-in-lib"]
irq-oc29-oc30 = ["irqs-in-lib"]
irq-oc30-oc31 = ["irqs-in-lib"]
[package.metadata.docs.rs]
rustdoc-args = ["--generate-link-to-definition"]

3
va108xx-embassy/docs.sh Executable file
View File

@ -0,0 +1,3 @@
#!/bin/bash
export RUSTDOCFLAGS="--cfg docsrs --generate-link-to-definition -Z unstable-options"
cargo +nightly doc --open

View File

@ -23,17 +23,17 @@
//!
//! You can disable the default features and then specify one of the features above to use the
//! documented combination of IRQs. It is also possible to specify custom IRQs by importing and
//! using the [embassy::embassy_time_driver_irqs] macro to declare the IRQ handlers in the
//! using the [embassy_time_driver_irqs] macro to declare the IRQ handlers in the
//! application code. If this is done, [embassy::init_with_custom_irqs] must be used
//! method to pass the IRQ numbers to the library.
//!
//! ## Examples
//!
//! [embassy example project](https://egit.irs.uni-stuttgart.de/rust/va108xx-rs/src/branch/main/examples/embassy)
//! [embassy example projects](https://egit.irs.uni-stuttgart.de/rust/va108xx-rs/src/branch/main/examples/embassy)
#![no_std]
#![cfg_attr(docsrs, feature(doc_auto_cfg))]
use core::cell::{Cell, RefCell};
use critical_section::CriticalSection;
use embassy_sync::blocking_mutex::CriticalSectionMutex as Mutex;
use critical_section::{CriticalSection, Mutex};
use portable_atomic::{AtomicU32, Ordering};
use embassy_time_driver::{time_driver_impl, Driver, TICK_HZ};
@ -45,7 +45,7 @@ use va108xx_hal::{
clock::enable_peripheral_clock,
enable_nvic_interrupt, pac,
prelude::*,
timer::{enable_tim_clk, get_tim_raw, TimRegInterface},
timer::{enable_tim_clk, get_tim_raw, TimRegInterface, ValidTim},
PeripheralSelect,
};
@ -62,7 +62,7 @@ time_driver_impl!(
/// the feature flags specified. However, the macro is exported to allow users to specify the
/// interrupt handlers themselves.
///
/// Please note that you have to explicitely import the [va108xx_hal::pac::interrupt]
/// Please note that you have to explicitely import the [macro@va108xx_hal::pac::interrupt]
/// macro in the application code in case this macro is used there.
#[macro_export]
macro_rules! embassy_time_driver_irqs {
@ -115,12 +115,15 @@ pub mod embassy {
/// This has to be called once at initialization time to initiate the time driver for
/// embassy.
#[cfg(feature = "irqs-in-lib")]
pub unsafe fn init(
pub unsafe fn init<
TimekeeperTim: TimRegInterface + ValidTim,
AlarmTim: TimRegInterface + ValidTim,
>(
syscfg: &mut pac::Sysconfig,
irqsel: &pac::Irqsel,
sysclk: impl Into<Hertz>,
timekeeper_tim: impl TimRegInterface,
alarm_tim: impl TimRegInterface,
timekeeper_tim: TimekeeperTim,
alarm_tim: AlarmTim,
) {
TIME_DRIVER.init(
syscfg,
@ -139,12 +142,15 @@ pub mod embassy {
///
/// This has to be called once at initialization time to initiate the time driver for
/// embassy.
pub unsafe fn init_with_custom_irqs(
pub unsafe fn init_with_custom_irqs<
TimekeeperTim: TimRegInterface + ValidTim,
AlarmTim: TimRegInterface + ValidTim,
>(
syscfg: &mut pac::Sysconfig,
irqsel: &pac::Irqsel,
sysclk: impl Into<Hertz>,
timekeeper_tim: impl TimRegInterface,
alarm_tim: impl TimRegInterface,
timekeeper_tim: TimekeeperTim,
alarm_tim: AlarmTim,
timekeeper_irq: pac::Interrupt,
alarm_irq: pac::Interrupt,
) {
@ -187,21 +193,21 @@ pub struct TimerDriver {
impl TimerDriver {
#[allow(clippy::too_many_arguments)]
fn init(
fn init<TimekeeperTim: TimRegInterface + ValidTim, AlarmTim: TimRegInterface + ValidTim>(
&self,
syscfg: &mut pac::Sysconfig,
irqsel: &pac::Irqsel,
sysclk: impl Into<Hertz>,
timekeeper_tim: impl TimRegInterface,
alarm_tim: impl TimRegInterface,
timekeeper_tim: TimekeeperTim,
alarm_tim: AlarmTim,
timekeeper_irq: pac::Interrupt,
alarm_irq: pac::Interrupt,
) {
if ALARM_TIM.get().is_some() {
if ALARM_TIM.get().is_some() || TIMEKEEPER_TIM.get().is_some() {
return;
}
ALARM_TIM.set(alarm_tim.tim_id()).ok();
TIMEKEEPER_TIM.set(timekeeper_tim.tim_id()).ok();
ALARM_TIM.set(AlarmTim::TIM_ID).ok();
TIMEKEEPER_TIM.set(TimekeeperTim::TIM_ID).ok();
enable_peripheral_clock(syscfg, PeripheralSelect::Irqsel);
enable_tim_clk(syscfg, timekeeper_tim.tim_id());
let timekeeper_reg_block = timekeeper_tim.reg_block();
@ -299,7 +305,7 @@ impl TimerDriver {
.cnt_value()
.write(|w| unsafe { w.bits(remaining_ticks.unwrap() as u32) });
alarm_tim.ctrl().modify(|_, w| w.irq_enb().set_bit());
alarm_tim.enable().write(|w| unsafe { w.bits(1) })
alarm_tim.enable().write(|w| unsafe { w.bits(1) });
}
}
})

View File

@ -10,6 +10,10 @@ and this project adheres to [Semantic Versioning](http://semver.org/).
## [v0.9.0]
## Fixed
- Important bugfix for UART driver which causes UART B drivers not to work.
## Removed
- Deleted some HAL re-exports in the PWM module
@ -38,6 +42,7 @@ and this project adheres to [Semantic Versioning](http://semver.org/).
- `enable_interrupt` and `disable_interrupt` renamed to `enable_nvic_interrupt` and
`disable_nvic_interrupt` to distinguish them from peripheral interrupts more clearly.
- `port_mux` renamed to `port_function_select`
- Renamed `IrqUartErrors` to `UartErrors`.
## Added
@ -45,6 +50,7 @@ and this project adheres to [Semantic Versioning](http://semver.org/).
methods.
- Asynchronous GPIO support.
- Asynchronous UART TX support.
- 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`

View File

@ -1,6 +1,6 @@
[package]
name = "va108xx-hal"
version = "0.8.0"
version = "0.9.0"
authors = ["Robin Mueller <muellerr@irs.uni-stuttgart.de>"]
edition = "2021"
description = "HAL for the Vorago VA108xx family of microcontrollers"
@ -24,10 +24,12 @@ fugit = "0.3"
typenum = "1"
critical-section = "1"
delegate = ">=0.12, <=0.13"
heapless = "0.8"
static_cell = "2"
thiserror = { version = "2", default-features = false }
void = { version = "1", default-features = false }
once_cell = {version = "1", default-features = false }
va108xx = { version = "0.3", default-features = false, features = ["critical-section"] }
va108xx = { version = "0.4", default-features = false, features = ["critical-section"] }
embassy-sync = "0.6"
defmt = { version = "0.3", optional = true }

View File

@ -25,12 +25,6 @@ rustup target add thumbv6m-none-eabi
After that, you can use `cargo build` to build the development version of the crate.
If you have not done this yet, it is recommended to read some of the excellent resources
available to learn Rust:
- [Rust Embedded Book](https://docs.rust-embedded.org/book/)
- [Rust Discovery Book](https://docs.rust-embedded.org/discovery/)
## Setting up your own binary crate
If you have a custom board, you might be interested in setting up a new binary crate for your
@ -65,3 +59,11 @@ is contained within the
7. Flashing the board might work differently for different boards and there is usually
more than one way. You can find example instructions in primary README.
## Embedded Rust
If you have not done this yet, it is recommended to read some of the excellent resources available
to learn Rust:
- [Rust Embedded Book](https://docs.rust-embedded.org/book/)
- [Rust Discovery Book](https://docs.rust-embedded.org/discovery/)

View File

@ -11,6 +11,7 @@ static SYS_CLOCK: Mutex<OnceCell<Hertz>> = Mutex::new(OnceCell::new());
pub type PeripheralClocks = PeripheralSelect;
#[derive(Debug, PartialEq, Eq)]
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
pub enum FilterClkSel {
SysClk = 0,
Clk1 = 1,
@ -39,13 +40,27 @@ pub fn get_sys_clock() -> Option<Hertz> {
pub fn set_clk_div_register(syscfg: &mut va108xx::Sysconfig, clk_sel: FilterClkSel, div: u32) {
match clk_sel {
FilterClkSel::SysClk => (),
FilterClkSel::Clk1 => syscfg.ioconfig_clkdiv1().write(|w| unsafe { w.bits(div) }),
FilterClkSel::Clk2 => syscfg.ioconfig_clkdiv2().write(|w| unsafe { w.bits(div) }),
FilterClkSel::Clk3 => syscfg.ioconfig_clkdiv3().write(|w| unsafe { w.bits(div) }),
FilterClkSel::Clk4 => syscfg.ioconfig_clkdiv4().write(|w| unsafe { w.bits(div) }),
FilterClkSel::Clk5 => syscfg.ioconfig_clkdiv5().write(|w| unsafe { w.bits(div) }),
FilterClkSel::Clk6 => syscfg.ioconfig_clkdiv6().write(|w| unsafe { w.bits(div) }),
FilterClkSel::Clk7 => syscfg.ioconfig_clkdiv7().write(|w| unsafe { w.bits(div) }),
FilterClkSel::Clk1 => {
syscfg.ioconfig_clkdiv1().write(|w| unsafe { w.bits(div) });
}
FilterClkSel::Clk2 => {
syscfg.ioconfig_clkdiv2().write(|w| unsafe { w.bits(div) });
}
FilterClkSel::Clk3 => {
syscfg.ioconfig_clkdiv3().write(|w| unsafe { w.bits(div) });
}
FilterClkSel::Clk4 => {
syscfg.ioconfig_clkdiv4().write(|w| unsafe { w.bits(div) });
}
FilterClkSel::Clk5 => {
syscfg.ioconfig_clkdiv5().write(|w| unsafe { w.bits(div) });
}
FilterClkSel::Clk6 => {
syscfg.ioconfig_clkdiv6().write(|w| unsafe { w.bits(div) });
}
FilterClkSel::Clk7 => {
syscfg.ioconfig_clkdiv7().write(|w| unsafe { w.bits(div) });
}
}
}

View File

@ -4,7 +4,7 @@
//! the [embedded_hal_async::digital::Wait] trait. These types allow for asynchronous waiting
//! on GPIO pins. Please note that this module does not specify/declare the interrupt handlers
//! which must be provided for async support to work. However, it provides one generic
//! [handler][handle_interrupt_for_async_gpio] which should be called in ALL user interrupt handlers
//! [handler][on_interrupt_for_asynch_gpio] which should be called in ALL user interrupt handlers
//! which handle GPIO interrupts.
//!
//! # Example
@ -90,7 +90,7 @@ pub struct InputPinFuture {
impl InputPinFuture {
/// # Safety
///
/// This calls [Self::new] but uses [pac::Peripherals::steal] to get the system configuration
/// This calls [Self::new_with_dyn_pin] but uses [pac::Peripherals::steal] to get the system configuration
/// and IRQ selection peripherals. Users must ensure that the registers and configuration
/// related to this input pin are not being used elsewhere concurrently.
pub unsafe fn new_unchecked_with_dyn_pin(
@ -127,7 +127,7 @@ impl InputPinFuture {
/// # Safety
///
/// This calls [Self::new] but uses [pac::Peripherals::steal] to get the system configuration
/// This calls [Self::new_with_pin] but uses [pac::Peripherals::steal] to get the system configuration
/// and IRQ selection peripherals. Users must ensure that the registers and configuration
/// related to this input pin are not being used elsewhere concurrently.
pub unsafe fn new_unchecked_with_pin<I: PinId, C: InputConfig>(
@ -200,7 +200,7 @@ impl InputDynPinAsync {
/// passed as well and is used to route and enable the interrupt.
///
/// Please note that the interrupt handler itself must be provided by the user and the
/// generic [handle_interrupt_for_async_gpio] function must be called inside that function for
/// generic [on_interrupt_for_asynch_gpio] function must be called inside that function for
/// the asynchronous functionality to work.
pub fn new(pin: DynPin, irq: pac::Interrupt) -> Result<Self, InvalidPinTypeError> {
if !pin.is_input_pin() {
@ -335,7 +335,7 @@ impl<I: PinId, C: InputConfig> InputPinAsync<I, C> {
/// passed as well and is used to route and enable the interrupt.
///
/// Please note that the interrupt handler itself must be provided by the user and the
/// generic [handle_interrupt_for_async_gpio] function must be called inside that function for
/// generic [on_interrupt_for_asynch_gpio] function must be called inside that function for
/// the asynchronous functionality to work.
pub fn new(pin: Pin<I, pin::Input<C>>, irq: pac::Interrupt) -> Self {
Self { pin, irq }

View File

@ -69,6 +69,7 @@ use crate::{clock::FilterClkSel, enable_nvic_interrupt, pac, FunSel, InterruptCo
/// Value-level `enum` for disabled configurations
#[derive(PartialEq, Eq, Clone, Copy)]
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
pub enum DynDisabled {
Floating,
PullDown,
@ -156,14 +157,16 @@ pub const DYN_ALT_FUNC_3: DynPinMode = DynPinMode::Alternate(DynAlternate::Sel3)
//==================================================================================================
/// Value-level `enum` for pin groups
#[derive(PartialEq, Eq, Clone, Copy)]
#[derive(Debug, PartialEq, Eq, Clone, Copy)]
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
pub enum DynGroup {
A,
B,
}
/// Value-level `struct` representing pin IDs
#[derive(PartialEq, Eq, Clone, Copy)]
#[derive(Debug, PartialEq, Eq, Clone, Copy)]
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
pub struct DynPinId {
pub group: DynGroup,
pub num: u8,
@ -177,6 +180,7 @@ pub struct DynPinId {
///
/// This `struct` takes ownership of a [`DynPinId`] and provides an API to
/// access the corresponding regsiters.
#[derive(Debug)]
pub(crate) struct DynRegisters(DynPinId);
// [`DynRegisters`] takes ownership of the [`DynPinId`], and [`DynPin`]
@ -209,6 +213,7 @@ impl DynRegisters {
///
/// This type acts as a type-erased version of [`Pin`]. Every pin is represented
/// by the same type, and pins are tracked and distinguished at run-time.
#[derive(Debug)]
pub struct DynPin {
pub(crate) regs: DynRegisters,
mode: DynPinMode,

View File

@ -119,8 +119,11 @@ pub trait InputConfig: Sealed {
const DYN: DynInput;
}
#[derive(Debug)]
pub enum Floating {}
#[derive(Debug)]
pub enum PullDown {}
#[derive(Debug)]
pub enum PullUp {}
impl InputConfig for Floating {
@ -148,6 +151,7 @@ pub type InputPullUp = Input<PullUp>;
///
/// Type `C` is one of three input configurations: [`Floating`], [`PullDown`] or
/// [`PullUp`]
#[derive(Debug)]
pub struct Input<C: InputConfig> {
cfg: PhantomData<C>,
}
@ -177,13 +181,17 @@ pub trait OutputConfig: Sealed {
pub trait ReadableOutput: Sealed {}
/// Type-level variant of [`OutputConfig`] for a push-pull configuration
#[derive(Debug)]
pub enum PushPull {}
/// Type-level variant of [`OutputConfig`] for an open drain configuration
#[derive(Debug)]
pub enum OpenDrain {}
/// Type-level variant of [`OutputConfig`] for a readable push-pull configuration
#[derive(Debug)]
pub enum ReadablePushPull {}
/// Type-level variant of [`OutputConfig`] for a readable open-drain configuration
#[derive(Debug)]
pub enum ReadableOpenDrain {}
impl Sealed for PushPull {}
@ -210,6 +218,7 @@ impl OutputConfig for ReadableOpenDrain {
///
/// Type `C` is one of four output configurations: [`PushPull`], [`OpenDrain`] or
/// their respective readable versions
#[derive(Debug)]
pub struct Output<C: OutputConfig> {
cfg: PhantomData<C>,
}
@ -304,6 +313,7 @@ macro_rules! pin_id {
// Need paste macro to use ident in doc attribute
paste! {
#[doc = "Pin ID representing pin " $Id]
#[derive(Debug)]
pub enum $Id {}
impl Sealed for $Id {}
impl PinId for $Id {
@ -321,6 +331,7 @@ macro_rules! pin_id {
//==================================================================================================
/// A type-level GPIO pin, parameterized by [PinId] and [PinMode] types
#[derive(Debug)]
pub struct Pin<I: PinId, M: PinMode> {
inner: DynPin,
phantom: PhantomData<(I, M)>,
@ -741,6 +752,7 @@ macro_rules! pins {
) => {
paste!(
/// Collection of all the individual [`Pin`]s for a given port (PORTA or PORTB)
#[derive(Debug)]
pub struct $PinsName {
port: $Port,
$(

View File

@ -304,7 +304,7 @@ pub(super) unsafe trait RegisterInterface {
unsafe {
portreg
.datamask()
.modify(|r, w| w.bits(r.bits() | self.mask_32()))
.modify(|r, w| w.bits(r.bits() | self.mask_32()));
}
}
@ -316,7 +316,7 @@ pub(super) unsafe trait RegisterInterface {
unsafe {
portreg
.datamask()
.modify(|r, w| w.bits(r.bits() & !self.mask_32()))
.modify(|r, w| w.bits(r.bits() & !self.mask_32()));
}
}

View File

@ -18,6 +18,7 @@ const CLK_400K: Hertz = Hertz::from_raw(400_000);
const MIN_CLK_400K: Hertz = Hertz::from_raw(8_000_000);
#[derive(Debug, PartialEq, Eq, Copy, Clone)]
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
pub enum FifoEmptyMode {
Stall = 0,
EndTransaction = 1,
@ -89,18 +90,21 @@ enum I2cCmd {
}
#[derive(Debug, PartialEq, Eq, Copy, Clone)]
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
pub enum I2cSpeed {
Regular100khz = 0,
Fast400khz = 1,
}
#[derive(Debug, PartialEq, Eq)]
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
pub enum I2cDirection {
Send = 0,
Read = 1,
}
#[derive(Debug, PartialEq, Eq, Copy, Clone)]
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
pub enum I2cAddress {
Regular(u8),
TenBit(u16),
@ -141,9 +145,12 @@ impl Instance for pac::I2cb {
// Config
//==================================================================================================
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
pub struct TrTfThighTlow(u8, u8, u8, u8);
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
pub struct TsuStoTsuStaThdStaTBuf(u8, u8, u8, u8);
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
pub struct TimingCfg {
// 4 bit max width
tr: u8,
@ -218,6 +225,7 @@ impl Default for TimingCfg {
}
}
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
pub struct MasterConfig {
pub tx_fe_mode: FifoEmptyMode,
pub rx_fe_mode: FifoEmptyMode,
@ -384,12 +392,12 @@ impl<I2c: Instance> I2cBase<I2c> {
let (addr, addr_mode_mask) = Self::unwrap_addr(addr_b);
self.i2c
.s0_addressb()
.write(|w| unsafe { w.bits((addr << 1) as u32 | addr_mode_mask) })
.write(|w| unsafe { w.bits((addr << 1) as u32 | addr_mode_mask) });
}
if let Some(addr_b_mask) = sl_cfg.addr_b_mask {
self.i2c
.s0_addressmaskb()
.write(|w| unsafe { w.bits((addr_b_mask << 1) as u32) })
.write(|w| unsafe { w.bits((addr_b_mask << 1) as u32) });
}
}

View File

@ -51,8 +51,8 @@ pub enum PeripheralSelect {
/// Generic interrupt config which can be used to specify whether the HAL driver will
/// use the IRQSEL register to route an interrupt, and whether the IRQ will be unmasked in the
/// Cortex-M0 NVIC. Both are generally necessary for IRQs to work, but the user might perform
/// this steps themselves
/// Cortex-M0 NVIC. Both are generally necessary for IRQs to work, but the user might want to
/// perform those steps themselves.
#[derive(Debug, PartialEq, Eq, Clone, Copy)]
pub struct InterruptConfig {
/// Interrupt target vector. Should always be set, might be required for disabling IRQs
@ -77,6 +77,7 @@ impl InterruptConfig {
pub type IrqCfg = InterruptConfig;
#[derive(Debug, PartialEq, Eq)]
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
pub struct InvalidPin(pub(crate) ());
/// Can be used to manually manipulate the function select of port pins

View File

@ -15,7 +15,9 @@ use crate::{clock::enable_peripheral_clock, gpio::DynPinId};
const DUTY_MAX: u16 = u16::MAX;
pub struct PwmCommon {
#[derive(Debug)]
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
pub(crate) struct PwmCommon {
sys_clk: Hertz,
/// For PWMB, this is the upper limit
current_duty: u16,

View File

@ -37,6 +37,7 @@ pub const BMSTART_BMSTOP_MASK: u32 = 1 << 31;
pub const DEFAULT_CLK_DIV: u16 = 2;
#[derive(Debug, PartialEq, Eq, Copy, Clone)]
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
pub enum HwChipSelectId {
Id0 = 0,
Id1 = 1,
@ -50,6 +51,7 @@ pub enum HwChipSelectId {
}
#[derive(Debug)]
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
pub enum SpiPort {
Porta = 0,
Portb = 1,
@ -58,6 +60,7 @@ pub enum SpiPort {
}
#[derive(Debug, PartialEq, Eq, Copy, Clone)]
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
pub enum WordSize {
OneBit = 0x00,
FourBits = 0x03,
@ -789,7 +792,7 @@ where
// initialization. Returns the amount of written bytes.
fn initial_send_fifo_pumping_with_words(&self, words: &[Word]) -> usize {
if self.blockmode {
self.spi.ctrl1().modify(|_, w| w.mtxpause().set_bit())
self.spi.ctrl1().modify(|_, w| w.mtxpause().set_bit());
}
// Fill the first half of the write FIFO
let mut current_write_idx = 0;
@ -803,7 +806,7 @@ where
current_write_idx += 1;
}
if self.blockmode {
self.spi.ctrl1().modify(|_, w| w.mtxpause().clear_bit())
self.spi.ctrl1().modify(|_, w| w.mtxpause().clear_bit());
}
current_write_idx
}
@ -812,7 +815,7 @@ where
// initialization.
fn initial_send_fifo_pumping_with_fill_words(&self, send_len: usize) -> usize {
if self.blockmode {
self.spi.ctrl1().modify(|_, w| w.mtxpause().set_bit())
self.spi.ctrl1().modify(|_, w| w.mtxpause().set_bit());
}
// Fill the first half of the write FIFO
let mut current_write_idx = 0;
@ -826,7 +829,7 @@ where
current_write_idx += 1;
}
if self.blockmode {
self.spi.ctrl1().modify(|_, w| w.mtxpause().clear_bit())
self.spi.ctrl1().modify(|_, w| w.mtxpause().clear_bit());
}
current_write_idx
}

View File

@ -20,7 +20,7 @@ pub fn enable_rom_scrubbing(
}
pub fn disable_rom_scrubbing(syscfg: &mut pac::Sysconfig) {
syscfg.rom_scrub().write(|w| unsafe { w.bits(0) })
syscfg.rom_scrub().write(|w| unsafe { w.bits(0) });
}
/// Enable scrubbing for the RAM
@ -39,7 +39,7 @@ pub fn enable_ram_scrubbing(
}
pub fn disable_ram_scrubbing(syscfg: &mut pac::Sysconfig) {
syscfg.ram_scrub().write(|w| unsafe { w.bits(0) })
syscfg.ram_scrub().write(|w| unsafe { w.bits(0) });
}
/// Clear the reset bit. This register is active low, so doing this will hold the peripheral

View File

@ -79,6 +79,7 @@ pub enum Event {
}
#[derive(Default, Debug, PartialEq, Eq, Copy, Clone)]
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
pub struct CascadeCtrl {
/// Enable Cascade 0 signal active as a requirement for counting
pub enb_start_src_csd0: bool,
@ -108,6 +109,7 @@ pub struct CascadeCtrl {
}
#[derive(Debug, PartialEq, Eq)]
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
pub enum CascadeSel {
Csd0 = 0,
Csd1 = 1,
@ -319,7 +321,7 @@ pub unsafe trait TimRegInterface {
va108xx::Peripherals::steal()
.sysconfig
.tim_reset()
.modify(|r, w| w.bits(r.bits() & !self.mask_32()))
.modify(|r, w| w.bits(r.bits() & !self.mask_32()));
}
}
@ -330,7 +332,7 @@ pub unsafe trait TimRegInterface {
va108xx::Peripherals::steal()
.sysconfig
.tim_reset()
.modify(|r, w| w.bits(r.bits() | self.mask_32()))
.modify(|r, w| w.bits(r.bits() | self.mask_32()));
}
}
}

View File

@ -7,7 +7,6 @@
//! - [Flashloader exposing a CCSDS interface via UART](https://egit.irs.uni-stuttgart.de/rust/va108xx-rs/src/branch/main/flashloader)
use core::{convert::Infallible, ops::Deref};
use fugit::RateExtU32;
use va108xx::Uarta;
pub use crate::InterruptConfig;
use crate::{
@ -23,6 +22,13 @@ use crate::{
};
use embedded_hal_nb::serial::Read;
#[derive(Debug)]
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
pub enum Bank {
A = 0,
B = 1,
}
//==================================================================================================
// Type-Level support
//==================================================================================================
@ -232,6 +238,7 @@ impl From<Hertz> for Config {
//==================================================================================================
#[derive(Debug, Copy, Clone)]
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
pub struct IrqContextTimeoutOrMaxSize {
rx_idx: usize,
mode: IrqReceptionMode,
@ -257,17 +264,19 @@ impl IrqContextTimeoutOrMaxSize {
/// This struct is used to return the default IRQ handler result to the user
#[derive(Debug, Default)]
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
pub struct IrqResult {
pub bytes_read: usize,
pub errors: Option<IrqUartError>,
pub errors: Option<UartErrors>,
}
/// This struct is used to return the default IRQ handler result to the user
#[derive(Debug, Default)]
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
pub struct IrqResultMaxSizeOrTimeout {
complete: bool,
timeout: bool,
pub errors: Option<IrqUartError>,
pub errors: Option<UartErrors>,
pub bytes_read: usize,
}
@ -314,20 +323,22 @@ impl IrqResultMaxSizeOrTimeout {
}
#[derive(Debug, PartialEq, Copy, Clone)]
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
enum IrqReceptionMode {
Idle,
Pending,
}
#[derive(Default, Debug, Copy, Clone)]
pub struct IrqUartError {
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
pub struct UartErrors {
overflow: bool,
framing: bool,
parity: bool,
other: bool,
}
impl IrqUartError {
impl UartErrors {
#[inline(always)]
pub fn overflow(&self) -> bool {
self.overflow
@ -349,7 +360,7 @@ impl IrqUartError {
}
}
impl IrqUartError {
impl UartErrors {
#[inline(always)]
pub fn error(&self) -> bool {
self.overflow || self.framing || self.parity
@ -401,7 +412,7 @@ impl Instance for pac::Uarta {
}
#[inline(always)]
fn ptr() -> *const uart_base::RegisterBlock {
Uarta::ptr() as *const _
Self::ptr() as *const _
}
}
@ -416,7 +427,7 @@ impl Instance for pac::Uartb {
}
#[inline(always)]
fn ptr() -> *const uart_base::RegisterBlock {
Uarta::ptr() as *const _
Self::ptr() as *const _
}
}
@ -752,6 +763,34 @@ where
}
}
#[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.
@ -760,6 +799,7 @@ pub struct Rx<Uart> {
}
impl<Uart: Instance> Rx<Uart> {
#[inline(always)]
fn new(uart: Uart) -> Self {
Self { uart }
}
@ -769,6 +809,7 @@ impl<Uart: Instance> Rx<Uart> {
/// # Safety
///
/// You must ensure that only registers related to the operation of the RX side are used.
#[inline(always)]
pub unsafe fn uart(&self) -> &Uart {
&self.uart
}
@ -778,14 +819,23 @@ impl<Uart: Instance> Rx<Uart> {
self.uart.fifo_clr().write(|w| w.rxfifo().set_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() });
}
#[inline]
pub fn enable(&mut self) {
self.uart.enable().modify(|_, w| w.rxenable().set_bit());
enable_rx(unsafe { Uart::reg_block() });
}
#[inline]
pub fn disable(&mut self) {
self.uart.enable().modify(|_, w| w.rxenable().clear_bit());
disable_rx(unsafe { Uart::reg_block() });
}
/// Low level function to read a word from the UART FIFO.
@ -819,6 +869,7 @@ impl<Uart: Instance> Rx<Uart> {
RxWithInterrupt::new(self)
}
#[inline(always)]
pub fn release(self) -> Uart {
self.uart
}
@ -877,14 +928,17 @@ impl<Uart: Instance> embedded_io::Read for Rx<Uart> {
}
}
#[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();
@ -893,6 +947,7 @@ pub fn enable_tx_interrupts(uart: &uart_base::RegisterBlock) {
});
}
#[inline(always)]
pub fn disable_tx_interrupts(uart: &uart_base::RegisterBlock) {
uart.irq_enb().modify(|_, w| {
w.irq_tx().clear_bit();
@ -914,12 +969,14 @@ impl<Uart: Instance> Tx<Uart> {
/// # Safety
///
/// Circumvents the HAL safety guarantees.
#[inline(always)]
pub unsafe fn steal() -> Self {
Self {
uart: Uart::steal(),
}
}
#[inline(always)]
fn new(uart: Uart) -> Self {
Self { uart }
}
@ -929,6 +986,7 @@ impl<Uart: Instance> Tx<Uart> {
/// # Safety
///
/// You must ensure that only registers related to the operation of the TX side are used.
#[inline(always)]
pub unsafe fn uart(&self) -> &Uart {
&self.uart
}
@ -1055,10 +1113,10 @@ impl<Uart: Instance> embedded_io::Write for Tx<Uart> {
///
/// 1. The first way simply empties the FIFO on an interrupt into a user provided buffer. You
/// can simply use [Self::start] to prepare the peripheral and then call the
/// [Self::irq_handler] in the interrupt service routine.
/// [Self::on_interrupt] in the interrupt service routine.
/// 2. The second way reads packets bounded by a maximum size or a baudtick based timeout. You
/// can use [Self::read_fixed_len_or_timeout_based_using_irq] to prepare the peripheral and
/// then call the [Self::irq_handler_max_size_or_timeout_based] in the interrupt service
/// then call the [Self::on_interrupt_max_size_or_timeout_based] in the interrupt service
/// routine. You have to call [Self::read_fixed_len_or_timeout_based_using_irq] in the ISR to
/// start reading the next packet.
pub struct RxWithInterrupt<Uart>(Rx<Uart>);
@ -1069,7 +1127,7 @@ impl<Uart: Instance> RxWithInterrupt<Uart> {
}
/// This function should be called once at initialization time if the regular
/// [Self::irq_handler] is used to read the UART receiver to enable and start the receiver.
/// [Self::on_interrupt] is used to read the UART receiver to enable and start the receiver.
pub fn start(&mut self) {
self.0.enable();
self.enable_rx_irq_sources(true);
@ -1080,13 +1138,13 @@ impl<Uart: Instance> RxWithInterrupt<Uart> {
&self.0.uart
}
/// This function is used together with the [Self::irq_handler_max_size_or_timeout_based]
/// This function is used together with the [Self::on_interrupt_max_size_or_timeout_based]
/// function to read packets with a maximum size or variable sized packets by using the
/// receive timeout of the hardware.
///
/// This function should be called once at initialization to initiate the context state
/// and to [Self::start] the receiver. After that, it should be called after each
/// completed [Self::irq_handler_max_size_or_timeout_based] call to restart the reception
/// completed [Self::on_interrupt_max_size_or_timeout_based] call to restart the reception
/// of a packet.
pub fn read_fixed_len_or_timeout_based_using_irq(
&mut self,
@ -1266,7 +1324,7 @@ impl<Uart: Instance> RxWithInterrupt<Uart> {
fn read_handler(
&self,
errors: &mut Option<IrqUartError>,
errors: &mut Option<UartErrors>,
read_res: &nb::Result<u8, RxError>,
) -> Option<u8> {
match read_res {
@ -1274,7 +1332,7 @@ impl<Uart: Instance> RxWithInterrupt<Uart> {
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());
let err = errors.get_or_insert(UartErrors::default());
// Now we can safely modify fields inside `err`
match e {
@ -1287,14 +1345,14 @@ impl<Uart: Instance> RxWithInterrupt<Uart> {
}
}
fn check_for_errors(&self, errors: &mut Option<IrqUartError>) {
fn check_for_errors(&self, errors: &mut Option<UartErrors>) {
let rx_status = self.uart().rxstatus().read();
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());
let err = errors.get_or_insert(UartErrors::default());
if rx_status.rxovr().bit_is_set() {
err.overflow = true;
@ -1331,5 +1389,8 @@ impl<Uart: Instance> RxWithInterrupt<Uart> {
}
}
pub mod asynch;
pub use asynch::*;
pub mod tx_asynch;
pub use tx_asynch::*;
pub mod rx_asynch;
pub use rx_asynch::*;

View File

@ -0,0 +1,419 @@
//! # Async UART reception functionality for the VA108xx family.
//!
//! This module provides the [RxAsync] and [RxAsyncSharedConsumer] struct which both implement the
//! [embedded_io_async::Read] trait.
//! This trait allows for asynchronous reception of data streams. Please note that this module does
//! not specify/declare the interrupt handlers which must be provided for async support to work.
//! However, it provides four interrupt handlers:
//!
//! - [on_interrupt_uart_a]
//! - [on_interrupt_uart_b]
//! - [on_interrupt_uart_a_overwriting]
//! - [on_interrupt_uart_b_overwriting]
//!
//! The first two are used for the [RxAsync] struct, while the latter two are used with the
//! [RxAsyncSharedConsumer] struct. The later two will overwrite old values in the used ring buffer.
//!
//! 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 heapless::spsc::Consumer;
use portable_atomic::AtomicBool;
use va108xx as pac;
use super::{Instance, Rx, RxError, UartErrors};
static UART_RX_WAKERS: [AtomicWaker; 2] = [const { AtomicWaker::new() }; 2];
static RX_READ_ACTIVE: [AtomicBool; 2] = [const { AtomicBool::new(false) }; 2];
static RX_HAS_DATA: [AtomicBool; 2] = [const { AtomicBool::new(false) }; 2];
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<(), RxError>;
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: Instance>(uart: &Uart) -> 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<Uart: Instance>(
uart: &Uart,
rx_enabled: bool,
read_some_data: bool,
irq_end: u32,
) -> Option<UartErrors> {
if read_some_data {
RX_HAS_DATA[Uart::IDX as usize].store(true, Ordering::Relaxed);
if RX_READ_ACTIVE[Uart::IDX as usize].load(Ordering::Relaxed) {
UART_RX_WAKERS[Uart::IDX as usize].wake();
}
}
let mut errors = None;
// Check for RX errors
if rx_enabled {
errors = on_interrupt_handle_rx_errors(uart);
}
// Clear the interrupt status bits
uart.irq_clr().write(|w| unsafe { w.bits(irq_end) });
errors
}
/// Interrupt handler for UART A.
///
/// Should be called in the user interrupt handler to enable
/// asynchronous reception. This variant will overwrite old data in the ring buffer in case
/// the ring buffer is full.
pub fn on_interrupt_uart_a_overwriting<const N: usize>(
prod: &mut heapless::spsc::Producer<u8, N>,
shared_consumer: &Mutex<RefCell<Option<heapless::spsc::Consumer<'static, u8, N>>>>,
) -> Result<(), AsyncUartErrors> {
on_interrupt_rx_async_heapless_queue_overwriting(
unsafe { pac::Uarta::steal() },
prod,
shared_consumer,
)
}
/// Interrupt handler for UART B.
///
/// Should be called in the user interrupt handler to enable
/// asynchronous reception. This variant will overwrite old data in the ring buffer in case
/// the ring buffer is full.
pub fn on_interrupt_uart_b_overwriting<const N: usize>(
prod: &mut heapless::spsc::Producer<u8, N>,
shared_consumer: &Mutex<RefCell<Option<heapless::spsc::Consumer<'static, u8, N>>>>,
) -> Result<(), AsyncUartErrors> {
on_interrupt_rx_async_heapless_queue_overwriting(
unsafe { pac::Uartb::steal() },
prod,
shared_consumer,
)
}
pub fn on_interrupt_rx_async_heapless_queue_overwriting<Uart: Instance, const N: usize>(
uart: Uart,
prod: &mut heapless::spsc::Producer<u8, N>,
shared_consumer: &Mutex<RefCell<Option<heapless::spsc::Consumer<'static, u8, N>>>>,
) -> Result<(), AsyncUartErrors> {
let irq_end = uart.irq_end().read();
let enb_status = uart.enable().read();
let 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;
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.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;
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(&uart, 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 UART A.
///
/// Should be called in the user interrupt handler to enable asynchronous reception.
pub fn on_interrupt_uart_a<const N: usize>(
prod: &mut heapless::spsc::Producer<'_, u8, N>,
) -> Result<(), AsyncUartErrors> {
on_interrupt_rx_async_heapless_queue(unsafe { pac::Uarta::steal() }, prod)
}
/// Interrupt handler for UART B.
///
/// Should be called in the user interrupt handler to enable asynchronous reception.
pub fn on_interrupt_uart_b<const N: usize>(
prod: &mut heapless::spsc::Producer<'_, u8, N>,
) -> Result<(), AsyncUartErrors> {
on_interrupt_rx_async_heapless_queue(unsafe { pac::Uartb::steal() }, prod)
}
pub fn on_interrupt_rx_async_heapless_queue<Uart: Instance, const N: usize>(
uart: Uart,
prod: &mut heapless::spsc::Producer<'_, u8, N>,
) -> Result<(), AsyncUartErrors> {
//let uart = unsafe { Uart::steal() };
let 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(&uart, 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);
}
}
/// 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> {
rx: Rx<Uart>,
pub queue: heapless::spsc::Consumer<'static, u8, 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;
}
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.
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(|_| {
rx.enable_interrupts();
rx.enable();
});
Self { rx, queue }
}
}
impl<Uart: Instance, const N: usize> embedded_io_async::Read for RxAsync<Uart, N> {
async fn read(&mut self, buf: &mut [u8]) -> Result<usize, Self::Error> {
// Need to wait for the IRQ to read data and set this flag. If the queue is not
// empty, we can read data immediately.
if self.queue.len() == 0 {
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 fut = RxFuture::new(&mut self.rx);
// Data is available, so read that data immediately.
let read_data = handle_data_in_queue(&mut self.queue);
if read_data > 0 {
return Ok(read_data);
}
// Await data.
let _ = fut.await;
Ok(handle_data_in_queue(&mut self.queue))
}
}
/// Core data structure to allow asynchronous UART reception.
///
/// If the ring buffer becomes full, the oldest data will be overwritten when using the
/// [on_interrupt_uart_a_overwriting] and [on_interrupt_uart_b_overwriting] interrupt handlers.
pub struct RxAsyncSharedConsumer<Uart: Instance, const N: usize> {
rx: Rx<Uart>,
queue: &'static Mutex<RefCell<Option<Consumer<'static, u8, N>>>>,
}
impl<Uart: Instance, const N: usize> ErrorType for RxAsyncSharedConsumer<Uart, N> {
/// Error reporting is done using the result of the interrupt functions.
type Error = Infallible;
}
impl<Uart: Instance, const N: usize> RxAsyncSharedConsumer<Uart, N> {
/// 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>,
queue: &'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 { rx, queue }
}
}
impl<Uart: Instance, const N: usize> embedded_io_async::Read for RxAsyncSharedConsumer<Uart, N> {
async fn read(&mut self, buf: &mut [u8]) -> Result<usize, Self::Error> {
// Need to wait for the IRQ to read data and set this flag. If the queue is not
// empty, we can read data immediately.
critical_section::with(|cs| {
let queue = self.queue.borrow(cs);
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 = || {
critical_section::with(|cs| {
let mut consumer_ref = self.queue.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.rx);
// Data is available, so read that data immediately.
let read_data = handle_data_in_queue();
if read_data > 0 {
return Ok(read_data);
}
// Await data.
let _ = fut.await;
let read_data = handle_data_in_queue();
Ok(read_data)
}
}

View File

@ -1,4 +1,4 @@
//! # Async GPIO functionality for the VA108xx family.
//! # Async UART transmission functionality for the VA108xx 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
@ -13,7 +13,7 @@
//!
//! # Example
//!
//! - [Async UART example](https://egit.irs.uni-stuttgart.de/rust/va108xx-rs/src/branch/async-gpio/examples/embassy/src/bin/async-uart.rs)
//! - [Async UART TX example](https://egit.irs.uni-stuttgart.de/rust/va108xx-rs/src/branch/main/examples/embassy/src/bin/async-uart-tx.rs)
use core::{cell::RefCell, future::Future};
use critical_section::Mutex;
@ -23,7 +23,7 @@ use portable_atomic::AtomicBool;
use super::*;
static UART_WAKERS: [AtomicWaker; 2] = [const { AtomicWaker::new() }; 2];
static UART_TX_WAKERS: [AtomicWaker; 2] = [const { AtomicWaker::new() }; 2];
static TX_CONTEXTS: [Mutex<RefCell<TxContext>>; 2] =
[const { Mutex::new(RefCell::new(TxContext::new())) }; 2];
// Completion flag. Kept outside of the context structure as an atomic to avoid
@ -72,7 +72,7 @@ fn on_interrupt_uart_tx<Uart: Instance>(uart: Uart) {
});
// Transfer is done.
TX_DONE[Uart::IDX as usize].store(true, core::sync::atomic::Ordering::Relaxed);
UART_WAKERS[Uart::IDX as usize].wake();
UART_TX_WAKERS[Uart::IDX as usize].wake();
return;
}
// Safety: We documented that the user provided slice must outlive the future, so we convert
@ -199,7 +199,7 @@ impl Future for TxFuture {
self: core::pin::Pin<&mut Self>,
cx: &mut core::task::Context<'_>,
) -> core::task::Poll<Self::Output> {
UART_WAKERS[self.uart_idx].register(cx.waker());
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

View File

@ -8,6 +8,12 @@ and this project adheres to [Semantic Versioning](http://semver.org/).
## [unreleased]
## [v0.7.0] 2025-02-13
- Bumped `va108xx-hal` dependency to 0.9
- Minor adjustments to `Button` API.
- `Button`, `Led` and `Leds` now simply wrap a type using a tuple struct.
## [v0.6.0] 2024-09-30
- Added M95M01 EEPROM module/API

View File

@ -1,6 +1,6 @@
[package]
name = "vorago-reb1"
version = "0.6.0"
version = "0.7.0"
authors = ["Robin Mueller <muellerr@irs.uni-stuttgart.de>"]
edition = "2021"
description = "Board Support Crate for the Vorago REB1 development board"
@ -19,8 +19,7 @@ bitfield = ">=0.17, <=0.18"
max116xx-10bit = "0.3"
[dependencies.va108xx-hal]
path = "../va108xx-hal"
version = ">=0.8, <0.9"
version = "0.9"
features = ["rt"]
[features]

View File

@ -10,23 +10,22 @@ use va108xx_hal::{
pac, InterruptConfig,
};
pub struct Button {
button: Pin<PA11, InputFloating>,
}
#[derive(Debug)]
pub struct Button(pub Pin<PA11, InputFloating>);
impl Button {
pub fn new(pin: Pin<PA11, InputFloating>) -> Button {
Button { button: pin }
Button(pin)
}
#[inline]
pub fn pressed(&mut self) -> bool {
self.button.is_low().ok().unwrap()
self.0.is_low().ok().unwrap()
}
#[inline]
pub fn released(&mut self) -> bool {
self.button.is_high().ok().unwrap()
self.0.is_high().ok().unwrap()
}
/// Configures an IRQ on edge.
@ -37,7 +36,7 @@ impl Button {
syscfg: Option<&mut pac::Sysconfig>,
irqsel: Option<&mut pac::Irqsel>,
) {
self.button
self.0
.configure_edge_interrupt(edge_type, irq_cfg, syscfg, irqsel);
}
@ -49,7 +48,7 @@ impl Button {
syscfg: Option<&mut pac::Sysconfig>,
irqsel: Option<&mut pac::Irqsel>,
) {
self.button
self.0
.configure_level_interrupt(level, irq_cfg, syscfg, irqsel);
}
@ -58,6 +57,6 @@ impl Button {
/// Please note that you still have to set a clock divisor yourself using the
/// [`va108xx_hal::clock::set_clk_div_register`] function in order for this to work.
pub fn configure_filter_type(&mut self, filter: FilterType, clksel: FilterClkSel) {
self.button.configure_filter_type(filter, clksel);
self.0.configure_filter_type(filter, clksel);
}
}

View File

@ -15,15 +15,12 @@ pub type LD2 = Pin<PA10, PushPullOutput>;
pub type LD3 = Pin<PA7, PushPullOutput>;
pub type LD4 = Pin<PA6, PushPullOutput>;
pub struct Leds {
leds: [Led; 3],
}
#[derive(Debug)]
pub struct Leds(pub [Led; 3]);
impl Leds {
pub fn new(led_pin1: LD2, led_pin2: LD3, led_pin3: LD4) -> Leds {
Leds {
leds: [led_pin1.into(), led_pin2.into(), led_pin3.into()],
}
Leds([led_pin1.into(), led_pin2.into(), led_pin3.into()])
}
}
@ -31,13 +28,13 @@ impl core::ops::Deref for Leds {
type Target = [Led];
fn deref(&self) -> &[Led] {
&self.leds
&self.0
}
}
impl core::ops::DerefMut for Leds {
fn deref_mut(&mut self) -> &mut [Led] {
&mut self.leds
&mut self.0
}
}
@ -45,28 +42,25 @@ impl core::ops::Index<usize> for Leds {
type Output = Led;
fn index(&self, i: usize) -> &Led {
&self.leds[i]
&self.0[i]
}
}
impl core::ops::IndexMut<usize> for Leds {
fn index_mut(&mut self, i: usize) -> &mut Led {
&mut self.leds[i]
&mut self.0[i]
}
}
pub struct Led {
pin: DynPin,
}
#[derive(Debug)]
pub struct Led(pub DynPin);
macro_rules! ctor {
($($ldx:ident),+) => {
$(
impl From<$ldx> for Led {
fn from(led: $ldx) -> Self {
Led {
pin: led.into()
}
Led(led.into())
}
}
)+
@ -79,18 +73,18 @@ impl Led {
/// Turns the LED off. Setting the pin high actually turns the LED off
#[inline]
pub fn off(&mut self) {
self.pin.set_high().ok();
self.0.set_high().ok();
}
/// Turns the LED on. Setting the pin low actually turns the LED on
#[inline]
pub fn on(&mut self) {
self.pin.set_low().ok();
self.0.set_low().ok();
}
/// Toggles the LED
#[inline]
pub fn toggle(&mut self) {
self.pin.toggle_with_toggle_reg().ok();
self.0.toggle_with_toggle_reg().ok();
}
}

View File

@ -526,13 +526,37 @@
{
"type": "cortex-debug",
"request": "launch",
"name": "Async UART",
"name": "Async UART TX",
"servertype": "jlink",
"cwd": "${workspaceRoot}",
"device": "Cortex-M0",
"svdFile": "./va108xx/svd/va108xx.svd.patched",
"preLaunchTask": "async-uart",
"executable": "${workspaceFolder}/target/thumbv6m-none-eabi/debug/async-uart",
"preLaunchTask": "async-uart-tx",
"executable": "${workspaceFolder}/target/thumbv6m-none-eabi/debug/async-uart-tx",
"interface": "jtag",
"runToEntryPoint": "main",
"rttConfig": {
"enabled": true,
"address": "auto",
"decoders": [
{
"port": 0,
"timestamp": true,
"type": "console"
}
]
}
},
{
"type": "cortex-debug",
"request": "launch",
"name": "Async UART RX",
"servertype": "jlink",
"cwd": "${workspaceRoot}",
"device": "Cortex-M0",
"svdFile": "./va108xx/svd/va108xx.svd.patched",
"preLaunchTask": "async-uart-rx",
"executable": "${workspaceFolder}/target/thumbv6m-none-eabi/debug/async-uart-rx",
"interface": "jtag",
"runToEntryPoint": "main",
"rttConfig": {

View File

@ -277,13 +277,23 @@
]
},
{
"label": "async-uart",
"label": "async-uart-tx",
"type": "shell",
"command": "~/.cargo/bin/cargo", // note: full path to the cargo
"args": [
"build",
"--bin",
"async-uart"
"async-uart-tx"
]
},
{
"label": "async-uart-rx",
"type": "shell",
"command": "~/.cargo/bin/cargo", // note: full path to the cargo
"args": [
"build",
"--bin",
"async-uart-rx"
]
},
{