Compare commits

...

29 Commits

Author SHA1 Message Date
c693530ab7 Move to released packages, done 2025-02-18 00:00:27 +01:00
45ee5ad726 Merge pull request 'prep embassy v0.2.0' (#67) from prep-embassy-v0.2.0 into main
Reviewed-on: #67
2025-02-17 23:54:08 +01:00
16591346e5 prep embassy v0.2.0 2025-02-17 23:52:26 +01:00
9ccd147ff6 Merge pull request 'prep embassy v0.1.3' (#66) from prep-embassy-v0.1.3 into main
Reviewed-on: #66
2025-02-17 21:28:24 +01:00
521c07460a prep embassy v0.1.3 2025-02-17 20:50:28 +01:00
5bf7793e6b Merge pull request 'prep BSP v0.8.0' (#65) from prep-bsp-v0.8.0 into main
Reviewed-on: #65
2025-02-17 20:46:39 +01:00
75e6d98e44 prep BSP v0.8.0 2025-02-17 20:34:53 +01:00
80eea170ef Merge pull request 'prep HAL v0.10.0' (#64) from prep-hal-v0.10.0 into main
Reviewed-on: #64
2025-02-17 20:17:53 +01:00
f9d1233d3f prep HAL v0.10.0 2025-02-17 20:15:47 +01:00
41f7f9d25b Merge pull request 'prep PAC v0.5.0' (#63) from prep-pac-v0.5.0 into main
Reviewed-on: #63
2025-02-17 20:05:21 +01:00
47e754433d prep PAC v0.5.0 2025-02-17 20:04:55 +01:00
e5e010a276 Merge pull request 'larger GPIO refactoring and Async UART update' (#62) from uart-gpio-update into main
Reviewed-on: #62
2025-02-17 10:56:21 +01:00
caf54e5a70 larger GPIO refactoring and Async UART update 2025-02-17 10:53:40 +01:00
31b25b0211 Merge pull request 'Adaption Async GPIO' (#59) from adaption-gpio-asynch into main
Reviewed-on: #59
2025-02-15 12:48:48 +01:00
8b55d0923f Bugfix and improvements for async GPIO 2025-02-15 12:46:10 +01:00
a65f4039ee Merge pull request 'minor CI update' (#58) from minor-ci-update into main
Reviewed-on: #58
2025-02-14 16:43:03 +01:00
97da2a0752
minor CI update 2025-02-14 16:40:58 +01:00
c43d1f8861 Merge pull request 'defmt in PAC is optional' (#57) from defmt-in-pac-optional into main
Reviewed-on: #57
2025-02-14 16:29:05 +01:00
181c2bdc7b defmt in PAC is optional 2025-02-14 16:26:27 +01:00
2bca96b5db Merge pull request 'HAL update' (#53) from hal-update into main
Reviewed-on: #53
2025-02-14 16:20:45 +01:00
bf41b59a24 HAL update 2025-02-14 16:18:59 +01:00
6cbba8414c Merge pull request 'bump version' (#55) from pac-update into main
Reviewed-on: #55
2025-02-14 16:14:05 +01:00
fe04a3e7cd
bump version 2025-02-14 16:11:53 +01:00
e25fb20b08 Merge pull request 'PAC update' (#54) from pac-update into main
Reviewed-on: #54
2025-02-14 16:06:49 +01:00
67af1bb9b5 PAC update 2025-02-14 16:02:11 +01:00
1a83f932b5 Merge pull request 'document probe-rs' (#52) from document-probe-rs into main
Reviewed-on: #52
2025-02-13 18:48:47 +01:00
cdc4807686 document probe-rs 2025-02-13 18:48:12 +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
139 changed files with 1457 additions and 1772 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

@ -12,6 +12,8 @@ jobs:
targets: "thumbv6m-none-eabi"
- run: cargo check --target thumbv6m-none-eabi
- run: cargo check --target thumbv6m-none-eabi --examples
- run: cargo check -p va108xx --target thumbv6m-none-eabi --all-features
- run: cargo check -p va108xx-hal --target thumbv6m-none-eabi --features "defmt"
test:
name: Run Tests
@ -39,8 +41,8 @@ jobs:
steps:
- uses: actions/checkout@v4
- uses: dtolnay/rust-toolchain@nightly
- run: RUSTDOCFLAGS="--cfg docsrs --generate-link-to-definition -Z unstable-options" cargo +nightly doc -p va108xx
- run: RUSTDOCFLAGS="--cfg docsrs --generate-link-to-definition -Z unstable-options" cargo +nightly doc -p va108xx-hal
- run: RUSTDOCFLAGS="--cfg docsrs --generate-link-to-definition -Z unstable-options" cargo +nightly doc -p va108xx --all-features
- run: RUSTDOCFLAGS="--cfg docsrs --generate-link-to-definition -Z unstable-options" cargo +nightly doc -p va108xx-hal --all-features
- run: RUSTDOCFLAGS="--cfg docsrs --generate-link-to-definition -Z unstable-options" cargo +nightly doc -p vorago-reb1
clippy:

View File

@ -60,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://probe.rs/docs/getting-started/installation/) 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 target/thumbv6m-none-eabi/debug/examples/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
@ -101,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

@ -14,6 +14,6 @@ embedded-hal-nb = "1"
embedded-io = "0.6"
[dependencies.va108xx-hal]
path = "../va108xx-hal"
version = "0.10.0"
features = ["rt"]

View File

@ -6,10 +6,7 @@
#![no_std]
use cortex_m_rt::entry;
use embedded_hal::{
delay::DelayNs,
digital::{InputPin, OutputPin, StatefulOutputPin},
};
use embedded_hal::delay::DelayNs;
use panic_rtt_target as _;
use rtt_target::{rprintln, rtt_init_print};
use va108xx_hal::{
@ -67,35 +64,35 @@ fn main() -> ! {
TestCase::TestBasic => {
// Tie PORTA[0] to PORTA[1] for these tests!
let mut out = pinsa.pa0.into_readable_push_pull_output();
let mut input = pinsa.pa1.into_floating_input();
out.set_high().unwrap();
assert!(input.is_high().unwrap());
out.set_low().unwrap();
assert!(input.is_low().unwrap());
let input = pinsa.pa1.into_floating_input();
out.set_high();
assert!(input.is_high());
out.set_low();
assert!(input.is_low());
}
TestCase::TestPullup => {
// Tie PORTA[0] to PORTA[1] for these tests!
let mut input = pinsa.pa1.into_pull_up_input();
assert!(input.is_high().unwrap());
let input = pinsa.pa1.into_pull_up_input();
assert!(input.is_high());
let mut out = pinsa.pa0.into_readable_push_pull_output();
out.set_low().unwrap();
assert!(input.is_low().unwrap());
out.set_high().unwrap();
assert!(input.is_high().unwrap());
out.set_low();
assert!(input.is_low());
out.set_high();
assert!(input.is_high());
out.into_floating_input();
assert!(input.is_high().unwrap());
assert!(input.is_high());
}
TestCase::TestPulldown => {
// Tie PORTA[0] to PORTA[1] for these tests!
let mut input = pinsa.pa1.into_pull_down_input();
assert!(input.is_low().unwrap());
let input = pinsa.pa1.into_pull_down_input();
assert!(input.is_low());
let mut out = pinsa.pa0.into_push_pull_output();
out.set_low().unwrap();
assert!(input.is_low().unwrap());
out.set_high().unwrap();
assert!(input.is_high().unwrap());
out.set_low();
assert!(input.is_low());
out.set_high();
assert!(input.is_high());
out.into_floating_input();
assert!(input.is_low().unwrap());
assert!(input.is_low());
}
TestCase::TestMask => {
// Tie PORTA[0] to PORTA[1] for these tests!
@ -110,11 +107,11 @@ fn main() -> ! {
TestCase::PortB => {
// Tie PORTB[22] to PORTB[23] for these tests!
let mut out = pinsb.pb22.into_readable_push_pull_output();
let mut input = pinsb.pb23.into_floating_input();
out.set_high().unwrap();
assert!(input.is_high().unwrap());
out.set_low().unwrap();
assert!(input.is_low().unwrap());
let input = pinsb.pb23.into_floating_input();
out.set_high();
assert!(input.is_high());
out.set_low();
assert!(input.is_low());
}
TestCase::Perid => {
assert_eq!(PinsA::get_perid(), 0x004007e1);
@ -122,34 +119,31 @@ fn main() -> ! {
}
TestCase::Pulse => {
let mut output_pulsed = pinsa.pa0.into_push_pull_output();
output_pulsed.pulse_mode(true, PinState::Low);
output_pulsed.configure_pulse_mode(true, PinState::Low);
rprintln!("Pulsing high 10 times..");
output_pulsed.set_low().unwrap();
output_pulsed.set_low();
for _ in 0..10 {
output_pulsed.set_high().unwrap();
output_pulsed.set_high();
cortex_m::asm::delay(25_000_000);
}
output_pulsed.pulse_mode(true, PinState::High);
output_pulsed.configure_pulse_mode(true, PinState::High);
rprintln!("Pulsing low 10 times..");
for _ in 0..10 {
output_pulsed.set_low().unwrap();
output_pulsed.set_low();
cortex_m::asm::delay(25_000_000);
}
}
TestCase::DelayGpio => {
let mut out_0 = pinsa
.pa0
.into_readable_push_pull_output()
.delay(true, false);
let mut out_1 = pinsa
.pa1
.into_readable_push_pull_output()
.delay(false, true);
let mut out_2 = pinsa.pa3.into_readable_push_pull_output().delay(true, true);
let mut out_0 = pinsa.pa0.into_readable_push_pull_output();
out_0.configure_delay(true, false);
let mut out_1 = pinsa.pa1.into_readable_push_pull_output();
out_1.configure_delay(false, true);
let mut out_2 = pinsa.pa3.into_readable_push_pull_output();
out_2.configure_delay(true, true);
for _ in 0..20 {
out_0.toggle().unwrap();
out_1.toggle().unwrap();
out_2.toggle().unwrap();
out_0.toggle();
out_1.toggle();
out_2.toggle();
cortex_m::asm::delay(25_000_000);
}
}
@ -162,18 +156,18 @@ fn main() -> ! {
dp.tim0,
);
for _ in 0..5 {
led1.toggle().ok();
led1.toggle();
ms_timer.delay_ms(500);
led1.toggle().ok();
led1.toggle();
ms_timer.delay_ms(500);
}
let mut delay_timer = CountdownTimer::new(&mut dp.sysconfig, 50.MHz(), dp.tim1);
let mut pa0 = pinsa.pa0.into_readable_push_pull_output();
for _ in 0..5 {
led1.toggle().ok();
led1.toggle();
delay_timer.delay_ms(500);
led1.toggle().ok();
led1.toggle();
delay_timer.delay_ms(500);
}
let ahb_freq: Hertz = 50.MHz();
@ -181,13 +175,13 @@ fn main() -> ! {
// Test usecond delay using both TIM peripheral and SYST. Use the release image if you
// want to verify the timings!
loop {
pa0.toggle().ok();
pa0.toggle();
delay_timer.delay_us(50);
pa0.toggle().ok();
pa0.toggle();
delay_timer.delay_us(50);
pa0.toggle_with_toggle_reg();
pa0.toggle();
syst_delay.delay_us(50);
pa0.toggle_with_toggle_reg();
pa0.toggle();
syst_delay.delay_us(50);
}
}
@ -195,7 +189,7 @@ fn main() -> ! {
rprintln!("Test success");
loop {
led1.toggle().ok();
led1.toggle();
cortex_m::asm::delay(25_000_000);
}
}

View File

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

View File

@ -27,10 +27,8 @@ version = "1"
features = ["cortex-m-systick"]
[dependencies.va108xx-hal]
version = "0.6"
path = "../va108xx-hal"
version = "0.10"
features = ["rt", "defmt"]
[dependencies.va108xx]
version = "0.3"
path = "../va108xx"
version = "0.5"

View File

@ -27,8 +27,8 @@ embassy-executor = { version = "0.7", features = [
"executor-interrupt"
]}
va108xx-hal = "0.9"
va108xx-embassy = "0.1"
va108xx-hal = { version = "0.10" }
va108xx-embassy = { version = "0.2" }
[features]
default = ["ticks-hz-1_000", "va108xx-embassy/irq-oc30-oc31"]

View File

@ -9,12 +9,12 @@ use embassy_executor::Spawner;
use embassy_sync::channel::{Receiver, Sender};
use embassy_sync::{blocking_mutex::raw::ThreadModeRawMutex, channel::Channel};
use embassy_time::{Duration, Instant, Timer};
use embedded_hal::digital::{InputPin, OutputPin, StatefulOutputPin};
use embedded_hal_async::digital::Wait;
use panic_rtt_target as _;
use rtt_target::{rprintln, rtt_init_print};
use va108xx_embassy::embassy;
use va108xx_hal::gpio::{on_interrupt_for_asynch_gpio, InputDynPinAsync, InputPinAsync, PinsB};
use va108xx_hal::gpio::{
on_interrupt_for_async_gpio_for_port, InputDynPinAsync, InputPinAsync, PinsB, Port,
};
use va108xx_hal::{
gpio::{DynPin, PinsA},
pac::{self, interrupt},
@ -64,15 +64,13 @@ async fn main(spawner: Spawner) {
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,
)
};
va108xx_embassy::init(
&mut dp.sysconfig,
&dp.irqsel,
SYSCLK_FREQ,
dp.tim23,
dp.tim22,
);
let porta = PinsA::new(&mut dp.sysconfig, dp.porta);
let portb = PinsB::new(&mut dp.sysconfig, dp.portb);
@ -114,7 +112,7 @@ async fn main(spawner: Spawner) {
rprintln!("Example done, toggling LED0");
loop {
led0.toggle().unwrap();
led0.toggle();
Timer::after(Duration::from_millis(500)).await;
}
}
@ -244,15 +242,16 @@ async fn output_task(
}
// PB22 to PB23 can be handled by both OC10 and OC11 depending on configuration.
#[interrupt]
#[allow(non_snake_case)]
fn OC10() {
on_interrupt_for_asynch_gpio();
on_interrupt_for_async_gpio_for_port(Port::A);
on_interrupt_for_async_gpio_for_port(Port::B);
}
// This interrupt only handles PORT B interrupts.
#[interrupt]
#[allow(non_snake_case)]
fn OC11() {
on_interrupt_for_asynch_gpio();
on_interrupt_for_async_gpio_for_port(Port::B);
}

View File

@ -18,21 +18,19 @@ 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,
self, on_interrupt_rx_overwriting,
rx_asynch::{on_interrupt_rx, RxAsync},
Bank, RxAsyncOverwriting, Tx,
},
InterruptConfig,
};
@ -57,15 +55,13 @@ async fn main(spawner: Spawner) {
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,
);
}
va108xx_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();
@ -106,16 +102,16 @@ async fn main(spawner: Spawner) {
*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);
let async_rx_uart_b = RxAsyncOverwriting::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();
led0.toggle();
led1.toggle();
led2.toggle();
let read_bytes = async_rx_uart_a.read(&mut buf).await.unwrap();
let read_str = core::str::from_utf8(&buf[..read_bytes]).unwrap();
rprintln!(
@ -128,7 +124,7 @@ async fn main(spawner: Spawner) {
}
#[embassy_executor::task]
async fn uart_b_task(mut async_rx: RxAsyncSharedConsumer<pac::Uartb, 256>, mut tx: Tx<pac::Uartb>) {
async fn uart_b_task(mut async_rx: RxAsyncOverwriting<pac::Uartb, 256>, mut tx: Tx<pac::Uartb>) {
let mut buf = [0u8; 256];
loop {
rprintln!("Current time UART B: {}", Instant::now().as_secs());
@ -149,7 +145,7 @@ async fn uart_b_task(mut async_rx: RxAsyncSharedConsumer<pac::Uartb, 256>, mut t
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);
let errors = on_interrupt_rx(Bank::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 {
@ -162,7 +158,7 @@ fn OC2() {
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);
let errors = on_interrupt_rx_overwriting(Bank::B, &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 {

View File

@ -12,16 +12,14 @@
#![no_main]
use embassy_executor::Spawner;
use embassy_time::{Duration, Instant, Ticker};
use embedded_hal::digital::StatefulOutputPin;
use embedded_io_async::Write;
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_a_tx, TxAsync},
uart::{self, on_interrupt_tx, Bank, TxAsync},
InterruptConfig,
};
@ -43,15 +41,13 @@ async fn main(_spawner: Spawner) {
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,
);
}
va108xx_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();
@ -75,9 +71,9 @@ async fn main(_spawner: Spawner) {
let mut idx = 0;
loop {
rprintln!("Current time: {}", Instant::now().as_secs());
led0.toggle().ok();
led1.toggle().ok();
led2.toggle().ok();
led0.toggle();
led1.toggle();
led2.toggle();
let _written = async_tx
.write(STR_LIST[idx].as_bytes())
.await
@ -93,5 +89,5 @@ async fn main(_spawner: Spawner) {
#[interrupt]
#[allow(non_snake_case)]
fn OC2() {
on_interrupt_uart_a_tx();
on_interrupt_tx(Bank::A);
}

View File

@ -2,10 +2,8 @@
#![no_main]
use embassy_executor::Spawner;
use embassy_time::{Duration, Instant, Ticker};
use embedded_hal::digital::StatefulOutputPin;
use panic_rtt_target as _;
use rtt_target::{rprintln, rtt_init_print};
use va108xx_embassy::embassy;
cfg_if::cfg_if! {
if #[cfg(feature = "custom-irqs")] {
@ -28,27 +26,25 @@ async fn main(_spawner: Spawner) {
let mut dp = pac::Peripherals::take().unwrap();
// Safety: Only called once here.
unsafe {
cfg_if::cfg_if! {
if #[cfg(not(feature = "custom-irqs"))] {
embassy::init(
&mut dp.sysconfig,
&dp.irqsel,
SYSCLK_FREQ,
dp.tim23,
dp.tim22,
);
} else {
embassy::init_with_custom_irqs(
&mut dp.sysconfig,
&dp.irqsel,
SYSCLK_FREQ,
dp.tim23,
dp.tim22,
pac::Interrupt::OC23,
pac::Interrupt::OC24,
);
}
cfg_if::cfg_if! {
if #[cfg(not(feature = "custom-irqs"))] {
va108xx_embassy::init(
&mut dp.sysconfig,
&dp.irqsel,
SYSCLK_FREQ,
dp.tim23,
dp.tim22,
);
} else {
va108xx_embassy::init_with_custom_irqs(
&mut dp.sysconfig,
&dp.irqsel,
SYSCLK_FREQ,
dp.tim23,
dp.tim22,
pac::Interrupt::OC23,
pac::Interrupt::OC24,
);
}
}
@ -60,8 +56,8 @@ async fn main(_spawner: Spawner) {
loop {
ticker.next().await;
rprintln!("Current time: {}", Instant::now().as_secs());
led0.toggle().ok();
led1.toggle().ok();
led2.toggle().ok();
led0.toggle();
led1.toggle();
led2.toggle();
}
}

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 = "0.9"
vorago-reb1 = "0.7"
va108xx-hal = { version = "0.10" }
vorago-reb1 = { version = "0.8" }

View File

@ -69,18 +69,16 @@ mod app {
// Configure an edge interrupt on the button and route it to interrupt vector 15
let mut button = Button::new(pinsa.pa11.into_floating_input());
button.configure_edge_interrupt(
edge_irq,
InterruptConfig::new(pac::interrupt::OC15, true, true),
Some(&mut dp.sysconfig),
Some(&mut dp.irqsel),
);
if mode == PressMode::Toggle {
// This filter debounces the switch for edge based interrupts
button.configure_filter_type(FilterType::FilterFourClockCycles, FilterClkSel::Clk1);
set_clk_div_register(&mut dp.sysconfig, FilterClkSel::Clk1, 50_000);
}
button.configure_and_enable_edge_interrupt(
edge_irq,
InterruptConfig::new(pac::interrupt::OC15, true, true),
);
let mut leds = Leds::new(
pinsa.pa10.into_push_pull_output(),
pinsa.pa7.into_push_pull_output(),

View File

@ -5,7 +5,6 @@
#[rtic::app(device = pac, dispatchers = [OC31, OC30, OC29])]
mod app {
use cortex_m::asm;
use embedded_hal::digital::StatefulOutputPin;
use panic_rtt_target as _;
use rtic_example::SYSCLK_FREQ;
use rtic_monotonics::systick::prelude::*;
@ -58,9 +57,9 @@ mod app {
async fn blinky(cx: blinky::Context) {
loop {
rprintln!("toggling LEDs");
cx.local.led0.toggle().ok();
cx.local.led1.toggle().ok();
cx.local.led2.toggle().ok();
cx.local.led0.toggle();
cx.local.led1.toggle();
cx.local.led2.toggle();
Mono::delay(1000.millis()).await;
}
}

View File

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

View File

@ -7,10 +7,7 @@
#![no_std]
use cortex_m_rt::entry;
use embedded_hal::{
delay::DelayNs,
digital::{OutputPin, StatefulOutputPin},
};
use embedded_hal::delay::DelayNs;
use panic_halt as _;
use va108xx_hal::{
gpio::PinsA,
@ -38,21 +35,21 @@ fn main() -> ! {
let mut led2 = porta.pa7.into_readable_push_pull_output();
let mut led3 = porta.pa6.into_readable_push_pull_output();
for _ in 0..10 {
led1.set_low().ok();
led2.set_low().ok();
led3.set_low().ok();
led1.set_low();
led2.set_low();
led3.set_low();
delay_ms.delay_ms(200);
led1.set_high().ok();
led2.set_high().ok();
led3.set_high().ok();
led1.set_high();
led2.set_high();
led3.set_high();
delay_tim1.delay_ms(200);
}
loop {
led1.toggle().ok();
led1.toggle();
delay_ms.delay_ms(200);
led2.toggle().ok();
led2.toggle();
delay_tim1.delay_ms(200);
led3.toggle().ok();
led3.toggle();
delay_ms.delay_ms(200);
}
}

View File

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

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"
va108xx-hal = { path = "../../va108xx-hal" }
va108xx-hal = { version = "0.10.0" }
[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"
va108xx-hal = { path = "../../va108xx-hal" }
va108xx-hal = { version = "0.10.0" }
[profile.dev]
codegen-units = 1

View File

@ -8,6 +8,12 @@ and this project adheres to [Semantic Versioning](http://semver.org/).
## [unreleased]
## [v0.2.0] 2025-02-17
- Bumped va108xx-hal to v0.10.0
- Remove `embassy` module, expose public functions in library root directly
## [v0.1.2] and [v0.1.1] 2025-02-13
Docs patch

View File

@ -1,6 +1,6 @@
[package]
name = "va108xx-embassy"
version = "0.1.2"
version = "0.2.0"
edition = "2021"
authors = ["Robin Mueller <muellerr@irs.uni-stuttgart.de>"]
description = "Embassy-rs support for the Vorago VA108xx family of microcontrollers"
@ -20,7 +20,7 @@ embassy-time-queue-utils = "0.1"
once_cell = { version = "1", default-features = false, features = ["critical-section"] }
va108xx-hal = "0.9"
va108xx-hal = { version = "0.10" }
[target.'cfg(all(target_arch = "arm", target_os = "none"))'.dependencies]
portable-atomic = { version = "1", features = ["unsafe-assume-single-core"] }

View File

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

View File

@ -1,17 +1,17 @@
//! # Embassy-rs support for the Vorago VA108xx MCU family
//!
//! This repository contains the [embassy-rs](https://github.com/embassy-rs/embassy) support for the
//! VA108xx family. Currently, it contains the time driver to allow using embassy-rs. It uses the TIM
//! peripherals provided by the VA108xx family for this purpose.
//! This repository contains the [embassy-rs](https://github.com/embassy-rs/embassy) support for
//! the VA108xx family. Currently, it contains the time driver to allow using embassy-rs. It uses
//! the TIM peripherals provided by the VA108xx family for this purpose.
//!
//! ## Usage
//!
//! This library only exposes the [embassy::init] method which sets up the time driver. This
//! function must be called once at the start of the application.
//! This library exposes the [init] or the [init_with_custom_irqs] functions which set up the time
//! driver. This function must be called once at the start of the application.
//!
//! This implementation requires two TIM peripherals provided by the VA108xx device.
//! The user can freely specify the two used TIM peripheral by passing the concrete TIM instances
//! into the [embassy::init_with_custom_irqs] and [embassy::init] method.
//! into the [init_with_custom_irqs] and [init] method.
//!
//! The application also requires two interrupt handlers to handle the timekeeper and alarm
//! interrupts. By default, this library will define the interrupt handler inside the library
@ -24,7 +24,7 @@
//! 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_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
//! application code. If this is done, [init_with_custom_irqs] must be used
//! method to pass the IRQ numbers to the library.
//!
//! ## Examples
@ -33,8 +33,7 @@
#![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};
@ -46,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,
};
@ -77,7 +76,7 @@ macro_rules! embassy_time_driver_irqs {
#[allow(non_snake_case)]
fn $timekeeper_irq() {
// Safety: We call it once here.
unsafe { $crate::embassy::time_driver().on_interrupt_timekeeping() }
unsafe { $crate::time_driver().on_interrupt_timekeeping() }
}
const ALARM_IRQ: pac::Interrupt = pac::Interrupt::$alarm_irq;
@ -86,7 +85,7 @@ macro_rules! embassy_time_driver_irqs {
#[allow(non_snake_case)]
fn $alarm_irq() {
// Safety: We call it once here.
unsafe { $crate::embassy::time_driver().on_interrupt_alarm() }
unsafe { $crate::time_driver().on_interrupt_alarm() }
}
};
}
@ -100,65 +99,58 @@ embassy_time_driver_irqs!(timekeeper_irq = OC30, alarm_irq = OC29);
#[cfg(feature = "irq-oc28-oc29")]
embassy_time_driver_irqs!(timekeeper_irq = OC29, alarm_irq = OC28);
pub mod embassy {
use super::*;
use va108xx_hal::{pac, timer::TimRegInterface};
/// Expose the time driver so the user can specify the IRQ handlers themselves.
pub fn time_driver() -> &'static TimerDriver {
&TIME_DRIVER
}
/// Expose the time driver so the user can specify the IRQ handlers themselves.
pub fn time_driver() -> &'static TimerDriver {
&TIME_DRIVER
}
/// Initialization method for embassy.
///
/// This should be used if the interrupt handler is provided by the library, which is the
/// default case.
#[cfg(feature = "irqs-in-lib")]
pub fn init<TimekeeperTim: TimRegInterface + ValidTim, AlarmTim: TimRegInterface + ValidTim>(
syscfg: &mut pac::Sysconfig,
irqsel: &pac::Irqsel,
sysclk: impl Into<Hertz>,
timekeeper_tim: TimekeeperTim,
alarm_tim: AlarmTim,
) {
TIME_DRIVER.init(
syscfg,
irqsel,
sysclk,
timekeeper_tim,
alarm_tim,
TIMEKEEPER_IRQ,
ALARM_IRQ,
)
}
/// Initialization method for embassy
///
/// # Safety
///
/// 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(
syscfg: &mut pac::Sysconfig,
irqsel: &pac::Irqsel,
sysclk: impl Into<Hertz>,
timekeeper_tim: impl TimRegInterface,
alarm_tim: impl TimRegInterface,
) {
TIME_DRIVER.init(
syscfg,
irqsel,
sysclk,
timekeeper_tim,
alarm_tim,
TIMEKEEPER_IRQ,
ALARM_IRQ,
)
}
/// Initialization method for embassy
///
/// # Safety
///
/// This has to be called once at initialization time to initiate the time driver for
/// embassy.
pub unsafe fn init_with_custom_irqs(
syscfg: &mut pac::Sysconfig,
irqsel: &pac::Irqsel,
sysclk: impl Into<Hertz>,
timekeeper_tim: impl TimRegInterface,
alarm_tim: impl TimRegInterface,
timekeeper_irq: pac::Interrupt,
alarm_irq: pac::Interrupt,
) {
TIME_DRIVER.init(
syscfg,
irqsel,
sysclk,
timekeeper_tim,
alarm_tim,
timekeeper_irq,
alarm_irq,
)
}
/// Initialization method for embassy when using custom IRQ handlers.
///
/// Requires an explicit [pac::Interrupt] argument for the timekeeper and alarm IRQs.
pub fn init_with_custom_irqs<
TimekeeperTim: TimRegInterface + ValidTim,
AlarmTim: TimRegInterface + ValidTim,
>(
syscfg: &mut pac::Sysconfig,
irqsel: &pac::Irqsel,
sysclk: impl Into<Hertz>,
timekeeper_tim: TimekeeperTim,
alarm_tim: AlarmTim,
timekeeper_irq: pac::Interrupt,
alarm_irq: pac::Interrupt,
) {
TIME_DRIVER.init(
syscfg,
irqsel,
sysclk,
timekeeper_tim,
alarm_tim,
timekeeper_irq,
alarm_irq,
)
}
struct AlarmState {
@ -188,21 +180,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();

View File

@ -8,7 +8,27 @@ and this project adheres to [Semantic Versioning](http://semver.org/).
## [unreleased]
## [v0.9.0]
## [v0.10.0] 2025-02-17
## Added
- A lot of missing `defmt::Format` implementations.
## Changed
- Larger refactoring of GPIO library. The edge and level interrupt configurator functions do not
enable interrupts anymore. Instead, there are explicit `enbable_interrupt` and
`disable_interrupt` methods
- Renamed GPIO `DynGroup` to `Port`
- Rename generic GPIO interrupt handler into `on_interrupt_for_asynch_gpio`
into `on_interrupt_for_async_gpio_for_port` which expects a Port argument
## Fixed
- Bug in async GPIO interrupt handler where all enabled interrupts, even the ones which might
be unrelated to the pin, were disabled.
## [v0.9.0] 2025-02-13
## Fixed

View File

@ -1,6 +1,6 @@
[package]
name = "va108xx-hal"
version = "0.9.0"
version = "0.10.0"
authors = ["Robin Mueller <muellerr@irs.uni-stuttgart.de>"]
edition = "2021"
description = "HAL for the Vorago VA108xx family of microcontrollers"
@ -28,8 +28,8 @@ 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.4", default-features = false, features = ["critical-section"] }
once_cell = { version = "1", default-features = false }
va108xx = { version = "0.5", default-features = false, features = ["critical-section", "defmt"] }
embassy-sync = "0.6"
defmt = { version = "0.3", optional = true }
@ -42,7 +42,7 @@ portable-atomic = "1"
[features]
default = ["rt"]
rt = ["va108xx/rt"]
defmt = ["dep:defmt", "fugit/defmt"]
defmt = ["dep:defmt", "fugit/defmt", "embedded-hal/defmt-03"]
[package.metadata.docs.rs]
all-features = true

View File

@ -3,9 +3,9 @@
//! This module provides the [InputPinAsync] and [InputDynPinAsync] which both implement
//! 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][on_interrupt_for_asynch_gpio] which should be called in ALL user interrupt handlers
//! which handle GPIO interrupts.
//! which must be provided for async support to work. However, it provides the
//! [on_interrupt_for_async_gpio_for_port] generic interrupt handler. This should be called in all
//! IRQ functions which handle any GPIO interrupts with the corresponding [Port] argument.
//!
//! # Example
//!
@ -13,68 +13,73 @@
use core::future::Future;
use embassy_sync::waitqueue::AtomicWaker;
use embedded_hal::digital::InputPin;
use embedded_hal_async::digital::Wait;
use portable_atomic::AtomicBool;
use va108xx::{self as pac, Irqsel, Sysconfig};
use va108xx::{self as pac};
use crate::InterruptConfig;
use super::{
pin, DynGroup, DynPin, DynPinId, InputConfig, InterruptEdge, InvalidPinTypeError, Pin, PinId,
NUM_GPIO_PINS, NUM_PINS_PORT_A,
pin, DynPin, DynPinId, InputConfig, InterruptEdge, InvalidPinTypeError, Pin, PinId, Port,
NUM_PINS_PORT_A, NUM_PINS_PORT_B,
};
static WAKERS: [AtomicWaker; NUM_GPIO_PINS] = [const { AtomicWaker::new() }; NUM_GPIO_PINS];
static EDGE_DETECTION: [AtomicBool; NUM_GPIO_PINS] =
[const { AtomicBool::new(false) }; NUM_GPIO_PINS];
static WAKERS_FOR_PORT_A: [AtomicWaker; NUM_PINS_PORT_A] =
[const { AtomicWaker::new() }; NUM_PINS_PORT_A];
static WAKERS_FOR_PORT_B: [AtomicWaker; NUM_PINS_PORT_B] =
[const { AtomicWaker::new() }; NUM_PINS_PORT_B];
static EDGE_DETECTION_PORT_A: [AtomicBool; NUM_PINS_PORT_A] =
[const { AtomicBool::new(false) }; NUM_PINS_PORT_A];
static EDGE_DETECTION_PORT_B: [AtomicBool; NUM_PINS_PORT_B] =
[const { AtomicBool::new(false) }; NUM_PINS_PORT_B];
#[inline]
fn pin_id_to_offset(dyn_pin_id: DynPinId) -> usize {
match dyn_pin_id.group {
DynGroup::A => dyn_pin_id.num as usize,
DynGroup::B => NUM_PINS_PORT_A + dyn_pin_id.num as usize,
}
}
/// Generic interrupt handler for GPIO interrupts to support the async functionalities.
/// Generic interrupt handler for GPIO interrupts on a specific port to support async functionalities
///
/// This handler will wake the correspoding wakers for the pins which triggered an interrupt
/// as well as updating the static edge detection structures. This allows the pin future to
/// complete async operations. The user should call this function in ALL interrupt handlers
/// which handle any GPIO interrupts.
#[inline]
pub fn on_interrupt_for_asynch_gpio() {
/// This function should be called in all interrupt handlers which handle any GPIO interrupts
/// matching the [Port] argument.
/// The handler will wake the corresponding wakers for the pins that triggered an interrupts
/// as well as update the static edge detection structures. This allows the pin future tocomplete
/// complete async operations.
pub fn on_interrupt_for_async_gpio_for_port(port: Port) {
let periphs = unsafe { pac::Peripherals::steal() };
handle_interrupt_for_gpio_and_port(
periphs.porta.irq_enb().read().bits(),
periphs.porta.edge_status().read().bits(),
0,
);
handle_interrupt_for_gpio_and_port(
periphs.portb.irq_enb().read().bits(),
periphs.portb.edge_status().read().bits(),
NUM_PINS_PORT_A,
);
let (irq_enb, edge_status, wakers, edge_detection) = match port {
Port::A => (
periphs.porta.irq_enb().read().bits(),
periphs.porta.edge_status().read().bits(),
WAKERS_FOR_PORT_A.as_ref(),
EDGE_DETECTION_PORT_A.as_ref(),
),
Port::B => (
periphs.portb.irq_enb().read().bits(),
periphs.portb.edge_status().read().bits(),
WAKERS_FOR_PORT_B.as_ref(),
EDGE_DETECTION_PORT_B.as_ref(),
),
};
on_interrupt_for_port(irq_enb, edge_status, wakers, edge_detection);
}
// Uses the enabled interrupt register and the persistent edge status to capture all GPIO events.
#[inline]
fn handle_interrupt_for_gpio_and_port(mut irq_enb: u32, edge_status: u32, pin_base_offset: usize) {
fn on_interrupt_for_port(
mut irq_enb: u32,
edge_status: u32,
wakers: &'static [AtomicWaker],
edge_detection: &'static [AtomicBool],
) {
while irq_enb != 0 {
let bit_pos = irq_enb.trailing_zeros() as usize;
let bit_mask = 1 << bit_pos;
WAKERS[pin_base_offset + bit_pos].wake();
wakers[bit_pos].wake();
if edge_status & bit_mask != 0 {
EDGE_DETECTION[pin_base_offset + bit_pos]
.store(true, core::sync::atomic::Ordering::Relaxed);
}
edge_detection[bit_pos].store(true, core::sync::atomic::Ordering::Relaxed);
// Clear the processed bit
irq_enb &= !bit_mask;
// Clear the processed bit
irq_enb &= !bit_mask;
}
}
}
@ -85,93 +90,66 @@ fn handle_interrupt_for_gpio_and_port(mut irq_enb: u32, edge_status: u32, pin_ba
/// struture is granted to allow writing custom async structures.
pub struct InputPinFuture {
pin_id: DynPinId,
waker_group: &'static [AtomicWaker],
edge_detection_group: &'static [AtomicBool],
}
impl InputPinFuture {
/// # Safety
///
/// 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(
pin: &mut DynPin,
irq: pac::Interrupt,
edge: InterruptEdge,
) -> Result<Self, InvalidPinTypeError> {
let mut periphs = pac::Peripherals::steal();
Self::new_with_dyn_pin(pin, irq, edge, &mut periphs.sysconfig, &mut periphs.irqsel)
#[inline]
pub fn pin_group_to_waker_and_edge_detection_group(
group: Port,
) -> (&'static [AtomicWaker], &'static [AtomicBool]) {
match group {
Port::A => (WAKERS_FOR_PORT_A.as_ref(), EDGE_DETECTION_PORT_A.as_ref()),
Port::B => (WAKERS_FOR_PORT_B.as_ref(), EDGE_DETECTION_PORT_B.as_ref()),
}
}
pub fn new_with_dyn_pin(
pin: &mut DynPin,
irq: pac::Interrupt,
edge: InterruptEdge,
sys_cfg: &mut Sysconfig,
irq_sel: &mut Irqsel,
) -> Result<Self, InvalidPinTypeError> {
if !pin.is_input_pin() {
return Err(InvalidPinTypeError(pin.mode()));
}
EDGE_DETECTION[pin_id_to_offset(pin.id())]
let (waker_group, edge_detection_group) =
Self::pin_group_to_waker_and_edge_detection_group(pin.id().port());
edge_detection_group[pin.id().num() as usize]
.store(false, core::sync::atomic::Ordering::Relaxed);
pin.interrupt_edge(
edge,
InterruptConfig::new(irq, true, true),
Some(sys_cfg),
Some(irq_sel),
)
.unwrap();
Ok(Self { pin_id: pin.id() })
}
/// # Safety
///
/// 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>(
pin: &mut Pin<I, pin::Input<C>>,
irq: pac::Interrupt,
edge: InterruptEdge,
) -> Self {
let mut periphs = pac::Peripherals::steal();
Self::new_with_pin(pin, irq, edge, &mut periphs.sysconfig, &mut periphs.irqsel)
pin.configure_edge_interrupt(edge).unwrap();
pin.enable_interrupt(InterruptConfig::new(irq, true, true));
Ok(Self {
pin_id: pin.id(),
waker_group,
edge_detection_group,
})
}
pub fn new_with_pin<I: PinId, C: InputConfig>(
pin: &mut Pin<I, pin::Input<C>>,
irq: pac::Interrupt,
edge: InterruptEdge,
sys_cfg: &mut Sysconfig,
irq_sel: &mut Irqsel,
) -> Self {
EDGE_DETECTION[pin_id_to_offset(pin.id())]
let (waker_group, edge_detection_group) =
Self::pin_group_to_waker_and_edge_detection_group(pin.id().port());
edge_detection_group[pin.id().num() as usize]
.store(false, core::sync::atomic::Ordering::Relaxed);
pin.configure_edge_interrupt(
edge,
InterruptConfig::new(irq, true, true),
Some(sys_cfg),
Some(irq_sel),
);
Self { pin_id: pin.id() }
pin.configure_edge_interrupt(edge);
pin.enable_interrupt(InterruptConfig::new(irq, true, true));
Self {
pin_id: pin.id(),
edge_detection_group,
waker_group,
}
}
}
impl Drop for InputPinFuture {
fn drop(&mut self) {
let periphs = unsafe { pac::Peripherals::steal() };
if self.pin_id.group == DynGroup::A {
periphs
.porta
.irq_enb()
.modify(|r, w| unsafe { w.bits(r.bits() & !(1 << self.pin_id.num)) });
} else {
periphs
.porta
.irq_enb()
.modify(|r, w| unsafe { w.bits(r.bits() & !(1 << self.pin_id.num)) });
}
// The API ensures that we actually own the pin, so stealing it here is okay.
unsafe { DynPin::steal(self.pin_id) }.disable_interrupt(false);
}
}
@ -181,9 +159,9 @@ impl Future for InputPinFuture {
self: core::pin::Pin<&mut Self>,
cx: &mut core::task::Context<'_>,
) -> core::task::Poll<Self::Output> {
let idx = pin_id_to_offset(self.pin_id);
WAKERS[idx].register(cx.waker());
if EDGE_DETECTION[idx].swap(false, core::sync::atomic::Ordering::Relaxed) {
let idx = self.pin_id.num() as usize;
self.waker_group[idx].register(cx.waker());
if self.edge_detection_group[idx].swap(false, core::sync::atomic::Ordering::Relaxed) {
return core::task::Poll::Ready(());
}
core::task::Poll::Pending
@ -200,8 +178,8 @@ 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 [on_interrupt_for_asynch_gpio] function must be called inside that function for
/// the asynchronous functionality to work.
/// generic [on_interrupt_for_async_gpio_for_port] 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() {
return Err(InvalidPinTypeError(pin.mode()));
@ -213,15 +191,10 @@ impl InputDynPinAsync {
///
/// This returns immediately if the pin is already high.
pub async fn wait_for_high(&mut self) {
let fut = unsafe {
// Unwrap okay, checked pin in constructor.
InputPinFuture::new_unchecked_with_dyn_pin(
&mut self.pin,
self.irq,
InterruptEdge::LowToHigh,
)
.unwrap()
};
// Unwrap okay, checked pin in constructor.
let fut =
InputPinFuture::new_with_dyn_pin(&mut self.pin, self.irq, InterruptEdge::LowToHigh)
.unwrap();
if self.pin.is_high().unwrap() {
return;
}
@ -232,15 +205,10 @@ impl InputDynPinAsync {
///
/// This returns immediately if the pin is already high.
pub async fn wait_for_low(&mut self) {
let fut = unsafe {
// Unwrap okay, checked pin in constructor.
InputPinFuture::new_unchecked_with_dyn_pin(
&mut self.pin,
self.irq,
InterruptEdge::HighToLow,
)
.unwrap()
};
// Unwrap okay, checked pin in constructor.
let fut =
InputPinFuture::new_with_dyn_pin(&mut self.pin, self.irq, InterruptEdge::HighToLow)
.unwrap();
if self.pin.is_low().unwrap() {
return;
}
@ -249,44 +217,26 @@ impl InputDynPinAsync {
/// Asynchronously wait until the pin sees a falling edge.
pub async fn wait_for_falling_edge(&mut self) {
unsafe {
// Unwrap okay, checked pin in constructor.
InputPinFuture::new_unchecked_with_dyn_pin(
&mut self.pin,
self.irq,
InterruptEdge::HighToLow,
)
// Unwrap okay, checked pin in constructor.
InputPinFuture::new_with_dyn_pin(&mut self.pin, self.irq, InterruptEdge::HighToLow)
.unwrap()
}
.await;
.await;
}
/// Asynchronously wait until the pin sees a rising edge.
pub async fn wait_for_rising_edge(&mut self) {
unsafe {
// Unwrap okay, checked pin in constructor.
InputPinFuture::new_unchecked_with_dyn_pin(
&mut self.pin,
self.irq,
InterruptEdge::LowToHigh,
)
// Unwrap okay, checked pin in constructor.
InputPinFuture::new_with_dyn_pin(&mut self.pin, self.irq, InterruptEdge::LowToHigh)
.unwrap()
}
.await;
.await;
}
/// Asynchronously wait until the pin sees any edge (either rising or falling).
pub async fn wait_for_any_edge(&mut self) {
unsafe {
// Unwrap okay, checked pin in constructor.
InputPinFuture::new_unchecked_with_dyn_pin(
&mut self.pin,
self.irq,
InterruptEdge::BothEdges,
)
// Unwrap okay, checked pin in constructor.
InputPinFuture::new_with_dyn_pin(&mut self.pin, self.irq, InterruptEdge::BothEdges)
.unwrap()
}
.await;
.await;
}
pub fn release(self) -> DynPin {
@ -335,8 +285,8 @@ 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 [on_interrupt_for_asynch_gpio] function must be called inside that function for
/// the asynchronous functionality to work.
/// generic [on_interrupt_for_async_gpio_for_port] 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 }
}
@ -345,14 +295,8 @@ impl<I: PinId, C: InputConfig> InputPinAsync<I, C> {
///
/// This returns immediately if the pin is already high.
pub async fn wait_for_high(&mut self) {
let fut = unsafe {
InputPinFuture::new_unchecked_with_pin(
&mut self.pin,
self.irq,
InterruptEdge::LowToHigh,
)
};
if self.pin.is_high().unwrap() {
let fut = InputPinFuture::new_with_pin(&mut self.pin, self.irq, InterruptEdge::LowToHigh);
if self.pin.is_high() {
return;
}
fut.await;
@ -362,14 +306,8 @@ impl<I: PinId, C: InputConfig> InputPinAsync<I, C> {
///
/// This returns immediately if the pin is already high.
pub async fn wait_for_low(&mut self) {
let fut = unsafe {
InputPinFuture::new_unchecked_with_pin(
&mut self.pin,
self.irq,
InterruptEdge::HighToLow,
)
};
if self.pin.is_low().unwrap() {
let fut = InputPinFuture::new_with_pin(&mut self.pin, self.irq, InterruptEdge::HighToLow);
if self.pin.is_low() {
return;
}
fut.await;
@ -377,40 +315,19 @@ impl<I: PinId, C: InputConfig> InputPinAsync<I, C> {
/// Asynchronously wait until the pin sees falling edge.
pub async fn wait_for_falling_edge(&mut self) {
unsafe {
// Unwrap okay, checked pin in constructor.
InputPinFuture::new_unchecked_with_pin(
&mut self.pin,
self.irq,
InterruptEdge::HighToLow,
)
}
.await;
// Unwrap okay, checked pin in constructor.
InputPinFuture::new_with_pin(&mut self.pin, self.irq, InterruptEdge::HighToLow).await;
}
/// Asynchronously wait until the pin sees rising edge.
pub async fn wait_for_rising_edge(&mut self) {
unsafe {
// Unwrap okay, checked pin in constructor.
InputPinFuture::new_unchecked_with_pin(
&mut self.pin,
self.irq,
InterruptEdge::LowToHigh,
)
}
.await;
// Unwrap okay, checked pin in constructor.
InputPinFuture::new_with_pin(&mut self.pin, self.irq, InterruptEdge::LowToHigh).await;
}
/// Asynchronously wait until the pin sees any edge (either rising or falling).
pub async fn wait_for_any_edge(&mut self) {
unsafe {
InputPinFuture::new_unchecked_with_pin(
&mut self.pin,
self.irq,
InterruptEdge::BothEdges,
)
}
.await;
InputPinFuture::new_with_pin(&mut self.pin, self.irq, InterruptEdge::BothEdges).await;
}
pub fn release(self) -> Pin<I, pin::Input<C>> {

View File

@ -57,11 +57,10 @@
//! [InvalidPinTypeError].
use super::{
pin::{FilterType, InterruptEdge, InterruptLevel, Pin, PinId, PinMode, PinState},
reg::RegisterInterface,
InputDynPinAsync,
pin::{FilterType, Pin, PinId, PinMode},
InputDynPinAsync, InterruptEdge, InterruptLevel, IsMaskedError, PinState, Port,
};
use crate::{clock::FilterClkSel, enable_nvic_interrupt, pac, FunSel, InterruptConfig};
use crate::{clock::FilterClkSel, enable_nvic_interrupt, pac, FunSel};
//==================================================================================================
// DynPinMode configurations
@ -156,55 +155,92 @@ pub const DYN_ALT_FUNC_3: DynPinMode = DynPinMode::Alternate(DynAlternate::Sel3)
// DynGroup & DynPinId
//==================================================================================================
/// Value-level `enum` for pin groups
#[derive(Debug, PartialEq, Eq, Clone, Copy)]
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
pub enum DynGroup {
A,
B,
}
pub type DynGroup = Port;
/// Value-level `struct` representing pin IDs
#[derive(Debug, PartialEq, Eq, Clone, Copy)]
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
pub struct DynPinId {
pub group: DynGroup,
pub num: u8,
port: Port,
num: u8,
}
//==================================================================================================
// DynRegisters
//==================================================================================================
impl DynPinId {
pub const fn new(port: Port, num: u8) -> Self {
DynPinId { port, num }
}
/// Provide a safe register interface for [`DynPin`]s
///
/// 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`]
// guarantees that each pin is a singleton, so this implementation is safe.
unsafe impl RegisterInterface for DynRegisters {
#[inline]
fn id(&self) -> DynPinId {
self.0
pub const fn port(&self) -> Port {
self.port
}
pub const fn num(&self) -> u8 {
self.num
}
}
impl DynRegisters {
/// Create a new instance of [`DynRegisters`]
///
/// # Safety
///
/// Users must never create two simultaneous instances of this `struct` with
/// the same [`DynPinId`]
//==================================================================================================
// ModeFields
//==================================================================================================
/// Collect all fields needed to set the [`PinMode`](super::PinMode)
#[derive(Default)]
struct ModeFields {
dir: bool,
opendrn: bool,
pull_en: bool,
/// true for pullup, false for pulldown
pull_dir: bool,
funsel: u8,
enb_input: bool,
}
impl From<DynPinMode> for ModeFields {
#[inline]
unsafe fn new(id: DynPinId) -> Self {
DynRegisters(id)
fn from(mode: DynPinMode) -> Self {
let mut fields = Self::default();
match mode {
DynPinMode::Input(config) => {
fields.dir = false;
fields.funsel = FunSel::Sel0 as u8;
match config {
DynInput::Floating => (),
DynInput::PullUp => {
fields.pull_en = true;
fields.pull_dir = true;
}
DynInput::PullDown => {
fields.pull_en = true;
}
}
}
DynPinMode::Output(config) => {
fields.dir = true;
fields.funsel = FunSel::Sel0 as u8;
match config {
DynOutput::PushPull => (),
DynOutput::OpenDrain => {
fields.opendrn = true;
}
DynOutput::ReadableOpenDrain => {
fields.enb_input = true;
fields.opendrn = true;
}
DynOutput::ReadablePushPull => {
fields.enb_input = true;
}
}
}
DynPinMode::Alternate(config) => {
fields.funsel = config as u8;
}
}
fields
}
}
/// Type definition to avoid confusion: These register blocks are identical
type PortRegisterBlock = pac::porta::RegisterBlock;
pub type PortReg = pac::ioconfig::Porta;
//==================================================================================================
// DynPin
//==================================================================================================
@ -215,46 +251,59 @@ impl DynRegisters {
/// by the same type, and pins are tracked and distinguished at run-time.
#[derive(Debug)]
pub struct DynPin {
pub(crate) regs: DynRegisters,
id: DynPinId,
mode: DynPinMode,
}
impl DynPin {
/// Create a new [`DynPin`]
/// Create a new [DynPin]
///
/// # Safety
///
/// Each [`DynPin`] must be a singleton. For a given [`DynPinId`], there
/// Each [DynPin] must be a singleton. For a given [DynPinId], there
/// must be at most one corresponding [`DynPin`] in existence at any given
/// time. Violating this requirement is `unsafe`.
#[inline]
pub(crate) unsafe fn new(id: DynPinId, mode: DynPinMode) -> Self {
pub(crate) const unsafe fn new(id: DynPinId, mode: DynPinMode) -> Self {
DynPin { id, mode }
}
/// Steals a new [DynPin].
///
/// This function will simply set the internal mode to [DYN_FLOATING_INPUT] pin without
/// modifying any registers related to the behaviour of the pin. The user should call
/// [Self::into_mode] to ensure the correct mode of the pin.
///
/// # Safety
///
/// Circumvents the HAL's safety guarantees. The caller must ensure that the pin is not
/// used cocurrently somewhere else. The caller might also want to call [Self::into_mode]
/// to ensure the correct desired state of the pin. It is recommended to create the pin using
/// [Pin::downgrade] instead.
pub const unsafe fn steal(id: DynPinId) -> Self {
DynPin {
regs: DynRegisters::new(id),
mode,
id,
mode: DYN_FLOATING_INPUT,
}
}
/// Return a copy of the pin ID
#[inline]
pub fn id(&self) -> DynPinId {
self.regs.0
pub const fn id(&self) -> DynPinId {
self.id
}
/// Return a copy of the pin mode
#[inline]
pub fn mode(&self) -> DynPinMode {
pub const fn mode(&self) -> DynPinMode {
self.mode
}
/// Convert the pin to the requested [`DynPinMode`]
#[inline]
pub fn into_mode(&mut self, mode: DynPinMode) {
// Only modify registers if we are actually changing pin mode
if mode != self.mode {
self.regs.change_mode(mode);
self.mode = mode;
}
self.change_mode(mode);
self.mode = mode;
}
#[inline]
@ -262,6 +311,11 @@ impl DynPin {
matches!(self.mode, DynPinMode::Input(_))
}
#[inline]
pub fn is_output_pin(&self) -> bool {
matches!(self.mode, DynPinMode::Output(_))
}
#[inline]
pub fn into_funsel_1(&mut self) {
self.into_mode(DYN_ALT_FUNC_1);
@ -319,205 +373,60 @@ impl DynPin {
self.into_mode(DYN_RD_OPEN_DRAIN_OUTPUT);
}
#[inline]
pub fn datamask(&self) -> bool {
self.regs.datamask()
#[inline(always)]
pub fn is_low(&self) -> Result<bool, InvalidPinTypeError> {
self.read_internal().map(|v| !v)
}
#[inline]
pub fn clear_datamask(&mut self) {
self.regs.clear_datamask();
#[inline(always)]
pub fn is_high(&self) -> Result<bool, InvalidPinTypeError> {
self.read_internal()
}
#[inline]
pub fn set_datamask(&mut self) {
self.regs.set_datamask();
#[inline(always)]
pub fn set_low(&mut self) -> Result<(), InvalidPinTypeError> {
self.write_internal(false)
}
#[inline]
pub fn is_high_masked(&self) -> Result<bool, crate::gpio::IsMaskedError> {
self.regs.read_pin_masked()
#[inline(always)]
pub fn set_high(&mut self) -> Result<(), InvalidPinTypeError> {
self.write_internal(true)
}
#[inline]
pub fn is_low_masked(&self) -> Result<bool, crate::gpio::IsMaskedError> {
self.regs.read_pin_masked().map(|v| !v)
}
#[inline]
pub fn set_high_masked(&mut self) -> Result<(), crate::gpio::IsMaskedError> {
self.regs.write_pin_masked(true)
}
#[inline]
pub fn set_low_masked(&mut self) -> Result<(), crate::gpio::IsMaskedError> {
self.regs.write_pin_masked(false)
}
pub(crate) fn irq_enb(
&mut self,
irq_cfg: crate::InterruptConfig,
syscfg: Option<&mut va108xx::Sysconfig>,
irqsel: Option<&mut va108xx::Irqsel>,
) {
if let Some(syscfg) = syscfg {
crate::clock::enable_peripheral_clock(syscfg, crate::clock::PeripheralClocks::Irqsel);
/// Toggle the logic level of an output pin
#[inline(always)]
pub fn toggle(&mut self) -> Result<(), InvalidPinTypeError> {
if !self.is_output_pin() {
return Err(InvalidPinTypeError(self.mode));
}
self.regs.enable_irq();
if let Some(irqsel) = irqsel {
if irq_cfg.route {
match self.regs.id().group {
// Set the correct interrupt number in the IRQSEL register
DynGroup::A => {
irqsel
.porta0(self.regs.id().num as usize)
.write(|w| unsafe { w.bits(irq_cfg.id as u32) });
}
DynGroup::B => {
irqsel
.portb0(self.regs.id().num as usize)
.write(|w| unsafe { w.bits(irq_cfg.id as u32) });
}
}
}
// Safety: TOGOUT is a "mask" register, and we only write the bit for
// this pin ID
unsafe { self.port_reg().togout().write(|w| w.bits(self.mask_32())) };
Ok(())
}
pub fn enable_interrupt(&mut self, irq_cfg: crate::InterruptConfig) {
if irq_cfg.route {
self.configure_irqsel(irq_cfg.id);
}
if irq_cfg.enable_in_nvic {
unsafe { enable_nvic_interrupt(irq_cfg.id) };
}
// We only manipulate our own bit.
self.port_reg()
.irq_enb()
.modify(|r, w| unsafe { w.bits(r.bits() | self.mask_32()) });
}
/// See p.53 of the programmers guide for more information.
/// Possible delays in clock cycles:
/// - Delay 1: 1
/// - Delay 2: 2
/// - Delay 1 + Delay 2: 3
#[inline]
pub fn delay(self, delay_1: bool, delay_2: bool) -> Result<Self, InvalidPinTypeError> {
match self.mode {
DynPinMode::Output(_) => {
self.regs.delay(delay_1, delay_2);
Ok(self)
}
_ => Err(InvalidPinTypeError(self.mode)),
pub fn disable_interrupt(&mut self, reset_irqsel: bool) {
if reset_irqsel {
self.reset_irqsel();
}
}
/// See p.52 of the programmers guide for more information.
/// When configured for pulse mode, a given pin will set the non-default state for exactly
/// one clock cycle before returning to the configured default state
#[inline]
pub fn pulse_mode(
&mut self,
enable: bool,
default_state: PinState,
) -> Result<(), InvalidPinTypeError> {
match self.mode {
DynPinMode::Output(_) => {
self.regs.pulse_mode(enable, default_state);
Ok(())
}
_ => Err(InvalidPinTypeError(self.mode)),
}
}
/// See p.37 and p.38 of the programmers guide for more information.
#[inline]
pub fn filter_type(
&mut self,
filter: FilterType,
clksel: FilterClkSel,
) -> Result<(), InvalidPinTypeError> {
match self.mode {
DynPinMode::Input(_) => {
self.regs.filter_type(filter, clksel);
Ok(())
}
_ => Err(InvalidPinTypeError(self.mode)),
}
}
#[inline]
pub fn interrupt_edge(
&mut self,
edge_type: InterruptEdge,
irq_cfg: InterruptConfig,
syscfg: Option<&mut pac::Sysconfig>,
irqsel: Option<&mut pac::Irqsel>,
) -> Result<(), InvalidPinTypeError> {
match self.mode {
DynPinMode::Input(_) | DynPinMode::Output(_) => {
self.regs.interrupt_edge(edge_type);
self.irq_enb(irq_cfg, syscfg, irqsel);
Ok(())
}
_ => Err(InvalidPinTypeError(self.mode)),
}
}
#[inline]
pub fn interrupt_level(
&mut self,
level_type: InterruptLevel,
irq_cfg: InterruptConfig,
syscfg: Option<&mut pac::Sysconfig>,
irqsel: Option<&mut pac::Irqsel>,
) -> Result<(), InvalidPinTypeError> {
match self.mode {
DynPinMode::Input(_) | DynPinMode::Output(_) => {
self.regs.interrupt_level(level_type);
self.irq_enb(irq_cfg, syscfg, irqsel);
Ok(())
}
_ => Err(InvalidPinTypeError(self.mode)),
}
}
#[inline]
pub fn toggle_with_toggle_reg(&mut self) -> Result<(), InvalidPinTypeError> {
match self.mode {
DynPinMode::Output(_) => {
self.regs.toggle();
Ok(())
}
_ => Err(InvalidPinTypeError(self.mode)),
}
}
#[inline]
fn _read(&self) -> Result<bool, InvalidPinTypeError> {
match self.mode {
DynPinMode::Input(_) | DYN_RD_OPEN_DRAIN_OUTPUT | DYN_RD_PUSH_PULL_OUTPUT => {
Ok(self.regs.read_pin())
}
_ => Err(InvalidPinTypeError(self.mode)),
}
}
#[inline]
fn _write(&mut self, bit: bool) -> Result<(), InvalidPinTypeError> {
match self.mode {
DynPinMode::Output(_) => {
self.regs.write_pin(bit);
Ok(())
}
_ => Err(InvalidPinTypeError(self.mode)),
}
}
#[inline]
fn _is_low(&self) -> Result<bool, InvalidPinTypeError> {
self._read().map(|v| !v)
}
#[inline]
fn _is_high(&self) -> Result<bool, InvalidPinTypeError> {
self._read()
}
#[inline]
fn _set_low(&mut self) -> Result<(), InvalidPinTypeError> {
self._write(false)
}
#[inline]
fn _set_high(&mut self) -> Result<(), InvalidPinTypeError> {
self._write(true)
// We only manipulate our own bit.
self.port_reg()
.irq_enb()
.modify(|r, w| unsafe { w.bits(r.bits() & !self.mask_32()) });
}
/// Try to recreate a type-level [`Pin`] from a value-level [`DynPin`]
@ -527,7 +436,7 @@ impl DynPin {
/// or refuse to perform it.
#[inline]
pub fn upgrade<I: PinId, M: PinMode>(self) -> Result<Pin<I, M>, InvalidPinTypeError> {
if self.regs.0 == I::DYN && self.mode == M::DYN {
if self.id == I::DYN && self.mode == M::DYN {
// The `DynPin` is consumed, so it is safe to replace it with the
// corresponding `Pin`
return Ok(unsafe { Pin::new() });
@ -543,6 +452,424 @@ impl DynPin {
) -> Result<InputDynPinAsync, InvalidPinTypeError> {
InputDynPinAsync::new(self, irq)
}
/// Configure the IRQSEL peripheral for this particular pin with the given interrupt ID.
pub fn configure_irqsel(&mut self, id: pac::Interrupt) {
let mut syscfg = unsafe { pac::Sysconfig::steal() };
let irqsel = unsafe { pac::Irqsel::steal() };
crate::clock::enable_peripheral_clock(&mut syscfg, crate::clock::PeripheralClocks::Irqsel);
match self.id().port() {
// Set the correct interrupt number in the IRQSEL register
super::Port::A => {
irqsel
.porta0(self.id().num() as usize)
.write(|w| unsafe { w.bits(id as u32) });
}
super::Port::B => {
irqsel
.portb0(self.id().num as usize)
.write(|w| unsafe { w.bits(id as u32) });
}
}
}
/// Reset the IRQSEL peripheral value for this particular pin.
pub fn reset_irqsel(&mut self) {
let mut syscfg = unsafe { pac::Sysconfig::steal() };
let irqsel = unsafe { pac::Irqsel::steal() };
crate::clock::enable_peripheral_clock(&mut syscfg, crate::clock::PeripheralClocks::Irqsel);
match self.id().port() {
// Set the correct interrupt number in the IRQSEL register
super::Port::A => {
irqsel
.porta0(self.id().num() as usize)
.write(|w| unsafe { w.bits(u32::MAX) });
}
super::Port::B => {
irqsel
.portb0(self.id().num as usize)
.write(|w| unsafe { w.bits(u32::MAX) });
}
}
}
// Get DATAMASK bit for this particular pin
#[inline(always)]
pub fn datamask(&self) -> bool {
(self.port_reg().datamask().read().bits() >> self.id().num) == 1
}
/// Clear DATAMASK bit for this particular pin. This prevents access
/// of the corresponding bit for output and input operations
#[inline(always)]
pub fn clear_datamask(&self) {
self.port_reg()
.datamask()
.modify(|r, w| unsafe { w.bits(r.bits() & !self.mask_32()) });
}
/// Set DATAMASK bit for this particular pin. 1 is the default
/// state of the bit and allows access of the corresponding bit
#[inline(always)]
pub fn set_datamask(&self) {
self.port_reg()
.datamask()
.modify(|r, w| unsafe { w.bits(r.bits() | self.mask_32()) });
}
#[inline]
pub fn is_high_masked(&self) -> Result<bool, crate::gpio::IsMaskedError> {
self.read_pin_masked()
}
#[inline]
pub fn is_low_masked(&self) -> Result<bool, crate::gpio::IsMaskedError> {
self.read_pin_masked().map(|v| !v)
}
#[inline]
pub fn set_high_masked(&mut self) -> Result<(), crate::gpio::IsMaskedError> {
self.write_pin_masked(true)
}
#[inline]
pub fn set_low_masked(&mut self) -> Result<(), crate::gpio::IsMaskedError> {
self.write_pin_masked(false)
}
/// Possible delays in clock cycles:
/// - Delay 1: 1
/// - Delay 2: 2
/// - Delay 1 + Delay 2: 3
#[inline]
pub fn configure_delay(
&mut self,
delay_1: bool,
delay_2: bool,
) -> Result<(), InvalidPinTypeError> {
match self.mode {
DynPinMode::Output(_) => {
self.configure_delay_internal(delay_1, delay_2);
Ok(())
}
_ => Err(InvalidPinTypeError(self.mode)),
}
}
/// When configured for pulse mode, a given pin will set the non-default state for exactly
/// one clock cycle before returning to the configured default state
#[inline]
pub fn configure_pulse_mode(
&mut self,
enable: bool,
default_state: PinState,
) -> Result<(), InvalidPinTypeError> {
match self.mode {
DynPinMode::Output(_) => {
self.configure_pulse_mode_internal(enable, default_state);
Ok(())
}
_ => Err(InvalidPinTypeError(self.mode)),
}
}
/// See p.37 and p.38 of the programmers guide for more information.
#[inline]
pub fn configure_filter_type(
&mut self,
filter: FilterType,
clksel: FilterClkSel,
) -> Result<(), InvalidPinTypeError> {
match self.mode {
DynPinMode::Input(_) => {
self.configure_filter_type_internal(filter, clksel);
Ok(())
}
_ => Err(InvalidPinTypeError(self.mode)),
}
}
pub fn configure_edge_interrupt(
&mut self,
edge_type: InterruptEdge,
) -> Result<(), InvalidPinTypeError> {
match self.mode {
DynPinMode::Input(_) | DynPinMode::Output(_) => {
self.configure_edge_interrupt_internal(edge_type);
Ok(())
}
_ => Err(InvalidPinTypeError(self.mode)),
}
}
pub fn configure_level_interrupt(
&mut self,
level_type: InterruptLevel,
) -> Result<(), InvalidPinTypeError> {
match self.mode {
DynPinMode::Input(_) | DynPinMode::Output(_) => {
self.configure_level_interrupt_internal(level_type);
Ok(())
}
_ => Err(InvalidPinTypeError(self.mode)),
}
}
/// Change the pin mode
#[inline]
pub(crate) fn change_mode(&mut self, mode: DynPinMode) {
let ModeFields {
dir,
funsel,
opendrn,
pull_dir,
pull_en,
enb_input,
} = mode.into();
let (portreg, iocfg) = (self.port_reg(), self.iocfg_port());
iocfg.write(|w| {
w.opendrn().bit(opendrn);
w.pen().bit(pull_en);
w.plevel().bit(pull_dir);
w.iewo().bit(enb_input);
unsafe { w.funsel().bits(funsel) }
});
let mask = self.mask_32();
unsafe {
if dir {
portreg.dir().modify(|r, w| w.bits(r.bits() | mask));
// Clear output
portreg.clrout().write(|w| w.bits(mask));
} else {
portreg.dir().modify(|r, w| w.bits(r.bits() & !mask));
}
}
}
#[inline]
const fn port_reg(&self) -> &PortRegisterBlock {
match self.id().port() {
Port::A => unsafe { &(*pac::Porta::ptr()) },
Port::B => unsafe { &(*pac::Portb::ptr()) },
}
}
#[inline]
const fn iocfg_port(&self) -> &PortReg {
let ioconfig = unsafe { va108xx::Ioconfig::ptr().as_ref().unwrap() };
match self.id().port() {
Port::A => ioconfig.porta(self.id().num() as usize),
Port::B => ioconfig.portb0(self.id().num() as usize),
}
}
#[inline(always)]
fn read_internal(&self) -> Result<bool, InvalidPinTypeError> {
match self.mode {
DynPinMode::Input(_) | DYN_RD_OPEN_DRAIN_OUTPUT | DYN_RD_PUSH_PULL_OUTPUT => {
Ok(self.read_pin())
}
_ => Err(InvalidPinTypeError(self.mode)),
}
}
#[inline(always)]
fn write_internal(&mut self, bit: bool) -> Result<(), InvalidPinTypeError> {
match self.mode {
DynPinMode::Output(_) => {
self.write_pin(bit);
Ok(())
}
_ => Err(InvalidPinTypeError(self.mode)),
}
}
#[inline]
/// Read the logic level of an output pin
pub(crate) fn read_pin(&self) -> bool {
let portreg = self.port_reg();
((portreg.datainraw().read().bits() >> self.id().num) & 0x01) == 1
}
/// Read a pin but use the masked version but check whether the datamask for the pin is
/// cleared as well
#[inline(always)]
fn read_pin_masked(&self) -> Result<bool, IsMaskedError> {
if !self.datamask() {
Err(IsMaskedError)
} else {
Ok(((self.port_reg().datain().read().bits() >> self.id().num) & 0x01) == 1)
}
}
/// Write the logic level of an output pin
#[inline(always)]
pub(crate) fn write_pin(&mut self, bit: bool) {
// Safety: SETOUT is a "mask" register, and we only write the bit for
// this pin ID
unsafe {
if bit {
self.port_reg().setout().write(|w| w.bits(self.mask_32()));
} else {
self.port_reg().clrout().write(|w| w.bits(self.mask_32()));
}
}
}
/// Write the logic level of an output pin but check whether the datamask for the pin is
/// cleared as well
#[inline]
fn write_pin_masked(&mut self, bit: bool) -> Result<(), IsMaskedError> {
if !self.datamask() {
Err(IsMaskedError)
} else {
// Safety: SETOUT is a "mask" register, and we only write the bit for
// this pin ID
unsafe {
if bit {
self.port_reg().setout().write(|w| w.bits(self.mask_32()));
} else {
self.port_reg().clrout().write(|w| w.bits(self.mask_32()));
}
Ok(())
}
}
}
/// Toggle the logic level of an output pin
#[inline(always)]
pub fn toggle_with_togout_reg(&mut self) {
// Safety: TOGOUT is a "mask" register, and we only write the bit for
// this pin ID
unsafe { self.port_reg().togout().write(|w| w.bits(self.mask_32())) };
}
/// Only useful for interrupt pins. Configure whether to use edges or level as interrupt soure
/// When using edge mode, it is possible to generate interrupts on both edges as well
#[inline]
fn configure_edge_interrupt_internal(&mut self, edge_type: InterruptEdge) {
unsafe {
self.port_reg()
.irq_sen()
.modify(|r, w| w.bits(r.bits() & !self.mask_32()));
match edge_type {
InterruptEdge::HighToLow => {
self.port_reg()
.irq_evt()
.modify(|r, w| w.bits(r.bits() & !self.mask_32()));
}
InterruptEdge::LowToHigh => {
self.port_reg()
.irq_evt()
.modify(|r, w| w.bits(r.bits() | self.mask_32()));
}
InterruptEdge::BothEdges => {
self.port_reg()
.irq_edge()
.modify(|r, w| w.bits(r.bits() | self.mask_32()));
}
}
}
}
/// Configure which edge or level type triggers an interrupt
#[inline]
fn configure_level_interrupt_internal(&mut self, level: InterruptLevel) {
unsafe {
self.port_reg()
.irq_sen()
.modify(|r, w| w.bits(r.bits() | self.mask_32()));
if level == InterruptLevel::Low {
self.port_reg()
.irq_evt()
.modify(|r, w| w.bits(r.bits() & !self.mask_32()));
} else {
self.port_reg()
.irq_evt()
.modify(|r, w| w.bits(r.bits() | self.mask_32()));
}
}
}
/// Only useful for input pins
#[inline]
fn configure_filter_type_internal(&mut self, filter: FilterType, clksel: FilterClkSel) {
self.iocfg_port().modify(|_, w| {
// Safety: Only write to register for this Pin ID
unsafe {
w.flttype().bits(filter as u8);
w.fltclk().bits(clksel as u8)
}
});
}
#[inline]
fn configure_pulse_mode_internal(&mut self, enable: bool, default_state: PinState) {
let portreg = self.port_reg();
unsafe {
if enable {
portreg
.pulse()
.modify(|r, w| w.bits(r.bits() | self.mask_32()));
} else {
portreg
.pulse()
.modify(|r, w| w.bits(r.bits() & !self.mask_32()));
}
if default_state == PinState::Low {
portreg
.pulsebase()
.modify(|r, w| w.bits(r.bits() & !self.mask_32()));
} else {
portreg
.pulsebase()
.modify(|r, w| w.bits(r.bits() | self.mask_32()));
}
}
}
/// Only useful for output pins
#[inline]
fn configure_delay_internal(&mut self, delay_1: bool, delay_2: bool) {
let portreg = self.port_reg();
unsafe {
if delay_1 {
portreg
.delay1()
.modify(|r, w| w.bits(r.bits() | self.mask_32()));
} else {
portreg
.delay1()
.modify(|r, w| w.bits(r.bits() & !self.mask_32()));
}
if delay_2 {
portreg
.delay2()
.modify(|r, w| w.bits(r.bits() | self.mask_32()));
} else {
portreg
.delay2()
.modify(|r, w| w.bits(r.bits() & !self.mask_32()));
}
}
}
// Only serves disambiguation purposes for the Embedded HAL impl
#[inline(always)]
fn is_low_mut(&mut self) -> Result<bool, InvalidPinTypeError> {
self.is_low()
}
// Only serves disambiguation purposes for the Embedded HAL impl
#[inline(always)]
fn is_high_mut(&mut self) -> Result<bool, InvalidPinTypeError> {
self.is_high()
}
#[inline(always)]
const fn mask_32(&self) -> u32 {
1 << self.id().num()
}
}
//==================================================================================================
@ -583,33 +910,38 @@ impl embedded_hal::digital::ErrorType for DynPin {
impl embedded_hal::digital::OutputPin for DynPin {
#[inline]
fn set_high(&mut self) -> Result<(), Self::Error> {
self._set_high()
self.set_high()
}
#[inline]
fn set_low(&mut self) -> Result<(), Self::Error> {
self._set_low()
self.set_low()
}
}
impl embedded_hal::digital::InputPin for DynPin {
#[inline]
fn is_high(&mut self) -> Result<bool, Self::Error> {
self._is_high()
self.is_high_mut()
}
#[inline]
fn is_low(&mut self) -> Result<bool, Self::Error> {
self._is_low()
self.is_low_mut()
}
}
impl embedded_hal::digital::StatefulOutputPin for DynPin {
#[inline]
fn is_set_high(&mut self) -> Result<bool, Self::Error> {
self._is_high()
self.is_high_mut()
}
#[inline]
fn is_set_low(&mut self) -> Result<bool, Self::Error> {
self._is_low()
self.is_low_mut()
}
#[inline]
fn toggle(&mut self) -> Result<(), Self::Error> {
self.toggle()
}
}

View File

@ -22,14 +22,47 @@
//!
//! - [Blinky example](https://egit.irs.uni-stuttgart.de/rust/va108xx-rs/src/branch/main/examples/simple/examples/blinky.rs)
//==================================================================================================
// Errors, Definitions and Constants
//==================================================================================================
pub const NUM_PINS_PORT_A: usize = 32;
pub const NUM_PINS_PORT_B: usize = 24;
pub const NUM_GPIO_PINS: usize = NUM_PINS_PORT_A + NUM_PINS_PORT_B;
#[derive(Debug, PartialEq, Eq, thiserror::Error)]
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
#[error("The pin is masked")]
pub struct IsMaskedError;
pub const NUM_PINS_PORT_A: usize = 32;
pub const NUM_PINS_PORT_B: usize = 24;
pub const NUM_GPIO_PINS: usize = NUM_PINS_PORT_A + NUM_PINS_PORT_B;
#[derive(Debug, PartialEq, Eq, Clone, Copy)]
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
pub enum Port {
A,
B,
}
#[derive(Debug, PartialEq, Eq)]
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
pub enum InterruptEdge {
HighToLow,
LowToHigh,
BothEdges,
}
#[derive(Debug, PartialEq, Eq)]
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
pub enum InterruptLevel {
Low = 0,
High = 1,
}
#[derive(Debug, PartialEq, Eq)]
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
pub enum PinState {
Low = 0,
High = 1,
}
pub mod dynpin;
pub use dynpin::*;
@ -39,5 +72,3 @@ pub use pin::*;
pub mod asynch;
pub use asynch::*;
mod reg;

View File

@ -68,45 +68,19 @@
//! # Embedded HAL traits
//!
//! This module implements all of the embedded HAL GPIO traits for each [`Pin`]
//! in the corresponding [`PinMode`]s, namely: [`InputPin`], [`OutputPin`],
//! and [`StatefulOutputPin`].
use super::dynpin::{DynAlternate, DynGroup, DynInput, DynOutput, DynPinId, DynPinMode};
use super::reg::RegisterInterface;
use super::{DynPin, InputPinAsync};
//! in the corresponding [`PinMode`]s, namely: [embedded_hal::digital::InputPin],
//! [embedded_hal::digital::OutputPin] and [embedded_hal::digital::StatefulOutputPin].
use super::dynpin::{DynAlternate, DynInput, DynOutput, DynPinId, DynPinMode};
use super::{DynPin, InputPinAsync, InterruptEdge, InterruptLevel, PinState, Port};
use crate::{
pac::{Irqsel, Porta, Portb, Sysconfig},
pac::{Porta, Portb},
typelevel::Sealed,
InterruptConfig,
};
use core::convert::Infallible;
use core::marker::PhantomData;
use core::mem::transmute;
use embedded_hal::digital::{InputPin, OutputPin, StatefulOutputPin};
use paste::paste;
//==================================================================================================
// Errors and Definitions
//==================================================================================================
#[derive(Debug, PartialEq, Eq)]
pub enum InterruptEdge {
HighToLow,
LowToHigh,
BothEdges,
}
#[derive(Debug, PartialEq, Eq)]
pub enum InterruptLevel {
Low = 0,
High = 1,
}
#[derive(Debug, PartialEq, Eq)]
pub enum PinState {
Low = 0,
High = 1,
}
//==================================================================================================
// Input configuration
//==================================================================================================
@ -317,10 +291,7 @@ macro_rules! pin_id {
pub enum $Id {}
impl Sealed for $Id {}
impl PinId for $Id {
const DYN: DynPinId = DynPinId {
group: DynGroup::$Group,
num: $NUM,
};
const DYN: DynPinId = DynPinId::new(Port::$Group, $NUM);
}
}
};
@ -346,14 +317,15 @@ impl<I: PinId, M: PinMode> Pin<I, M> {
/// at most one corresponding [Pin] in existence at any given time.
/// Violating this requirement is `unsafe`.
#[inline]
pub(crate) unsafe fn new() -> Pin<I, M> {
pub(crate) const unsafe fn new() -> Pin<I, M> {
Pin {
inner: DynPin::new(I::DYN, M::DYN),
phantom: PhantomData,
}
}
pub fn id(&self) -> DynPinId {
#[inline]
pub const fn id(&self) -> DynPinId {
self.inner.id()
}
@ -363,7 +335,7 @@ impl<I: PinId, M: PinMode> Pin<I, M> {
// Only modify registers if we are actually changing pin mode
// This check should compile away
if N::DYN != M::DYN {
self.inner.regs.change_mode(N::DYN);
self.inner.change_mode(N::DYN);
}
// Safe because we drop the existing Pin
unsafe { Pin::new() }
@ -423,6 +395,16 @@ impl<I: PinId, M: PinMode> Pin<I, M> {
self.into_mode()
}
#[inline]
pub fn is_low(&self) -> bool {
!self.inner.read_pin()
}
#[inline]
pub fn is_high(&self) -> bool {
self.inner.read_pin()
}
#[inline]
pub fn datamask(&self) -> bool {
self.inner.datamask()
@ -448,53 +430,41 @@ impl<I: PinId, M: PinMode> Pin<I, M> {
self.inner.is_low_masked()
}
#[inline]
pub fn set_high_masked(&mut self) -> Result<(), crate::gpio::IsMaskedError> {
self.inner.set_high_masked()
}
#[inline]
pub fn set_low_masked(&mut self) -> Result<(), crate::gpio::IsMaskedError> {
self.inner.set_low_masked()
}
#[inline]
pub fn downgrade(self) -> DynPin {
self.inner
}
fn irq_enb(
&mut self,
irq_cfg: crate::InterruptConfig,
syscfg: Option<&mut va108xx::Sysconfig>,
irqsel: Option<&mut va108xx::Irqsel>,
) {
self.inner.irq_enb(irq_cfg, syscfg, irqsel);
// Those only serve for the embedded HAL implementations which have different mutability.
#[inline]
fn is_low_mut(&mut self) -> bool {
self.is_low()
}
#[inline]
pub(crate) fn _set_high(&mut self) {
self.inner.regs.write_pin(true)
fn is_high_mut(&mut self) -> bool {
self.is_high()
}
#[inline]
pub(crate) fn _set_low(&mut self) {
self.inner.regs.write_pin(false)
pub fn enable_interrupt(&mut self, irq_cfg: crate::InterruptConfig) {
self.inner.enable_interrupt(irq_cfg);
}
#[inline]
pub(crate) fn _toggle_with_toggle_reg(&mut self) {
self.inner.regs.toggle();
pub fn disable_interrupt(&mut self, reset_irqsel: bool) {
self.inner.disable_interrupt(reset_irqsel);
}
#[inline]
pub(crate) fn _is_low(&self) -> bool {
!self.inner.regs.read_pin()
/// Configure the pin for an edge interrupt but does not enable the interrupt.
pub fn configure_edge_interrupt(&mut self, edge_type: InterruptEdge) {
self.inner.configure_edge_interrupt(edge_type).unwrap();
}
#[inline]
pub(crate) fn _is_high(&self) -> bool {
self.inner.regs.read_pin()
/// Configure the pin for a level interrupt but does not enable the interrupt.
pub fn configure_level_interrupt(&mut self, level_type: InterruptLevel) {
self.inner.configure_level_interrupt(level_type).unwrap();
}
}
@ -591,74 +561,52 @@ impl<I: PinId, C: InputConfig> Pin<I, Input<C>> {
pub fn into_async_input(self, irq: crate::pac::Interrupt) -> InputPinAsync<I, C> {
InputPinAsync::new(self, irq)
}
pub fn configure_edge_interrupt(
&mut self,
edge_type: InterruptEdge,
irq_cfg: InterruptConfig,
syscfg: Option<&mut Sysconfig>,
irqsel: Option<&mut Irqsel>,
) {
self.inner.regs.interrupt_edge(edge_type);
self.irq_enb(irq_cfg, syscfg, irqsel);
}
pub fn configure_level_interrupt(
&mut self,
level_type: InterruptLevel,
irq_cfg: InterruptConfig,
syscfg: Option<&mut Sysconfig>,
irqsel: Option<&mut Irqsel>,
) {
self.inner.regs.interrupt_level(level_type);
self.irq_enb(irq_cfg, syscfg, irqsel);
}
}
impl<I: PinId, C: OutputConfig> Pin<I, Output<C>> {
#[inline]
pub fn set_high(&mut self) {
self.inner.write_pin(true)
}
#[inline]
pub fn set_low(&mut self) {
self.inner.write_pin(false)
}
#[inline]
pub fn toggle(&mut self) {
self.inner.toggle().unwrap()
}
#[inline]
pub fn set_high_masked(&mut self) -> Result<(), crate::gpio::IsMaskedError> {
self.inner.set_high_masked()
}
#[inline]
pub fn set_low_masked(&mut self) -> Result<(), crate::gpio::IsMaskedError> {
self.inner.set_low_masked()
}
/// See p.53 of the programmers guide for more information.
/// Possible delays in clock cycles:
/// - Delay 1: 1
/// - Delay 2: 2
/// - Delay 1 + Delay 2: 3
#[inline]
pub fn delay(self, delay_1: bool, delay_2: bool) -> Self {
self.inner.regs.delay(delay_1, delay_2);
self
}
#[inline]
pub fn toggle_with_toggle_reg(&mut self) {
self._toggle_with_toggle_reg()
pub fn configure_delay(&mut self, delay_1: bool, delay_2: bool) {
self.inner.configure_delay(delay_1, delay_2).unwrap();
}
/// See p.52 of the programmers guide for more information.
///
/// When configured for pulse mode, a given pin will set the non-default state for exactly
/// one clock cycle before returning to the configured default state
pub fn pulse_mode(&mut self, enable: bool, default_state: PinState) {
self.inner.regs.pulse_mode(enable, default_state);
}
pub fn interrupt_edge(
&mut self,
edge_type: InterruptEdge,
irq_cfg: InterruptConfig,
syscfg: Option<&mut Sysconfig>,
irqsel: Option<&mut Irqsel>,
) {
self.inner.regs.interrupt_edge(edge_type);
self.irq_enb(irq_cfg, syscfg, irqsel);
}
pub fn interrupt_level(
&mut self,
level_type: InterruptLevel,
irq_cfg: InterruptConfig,
syscfg: Option<&mut Sysconfig>,
irqsel: Option<&mut Irqsel>,
) {
self.inner.regs.interrupt_level(level_type);
self.irq_enb(irq_cfg, syscfg, irqsel);
pub fn configure_pulse_mode(&mut self, enable: bool, default_state: PinState) {
self.inner
.configure_pulse_mode(enable, default_state)
.unwrap();
}
}
@ -666,7 +614,7 @@ impl<I: PinId, C: InputConfig> Pin<I, Input<C>> {
/// See p.37 and p.38 of the programmers guide for more information.
#[inline]
pub fn configure_filter_type(&mut self, filter: FilterType, clksel: FilterClkSel) {
self.inner.regs.filter_type(filter, clksel);
self.inner.configure_filter_type(filter, clksel).unwrap();
}
}
@ -682,63 +630,53 @@ where
type Error = Infallible;
}
impl<I: PinId, C: OutputConfig> OutputPin for Pin<I, Output<C>> {
impl<I: PinId, C: OutputConfig> embedded_hal::digital::OutputPin for Pin<I, Output<C>> {
#[inline]
fn set_high(&mut self) -> Result<(), Self::Error> {
self._set_high();
self.set_high();
Ok(())
}
#[inline]
fn set_low(&mut self) -> Result<(), Self::Error> {
self._set_low();
self.set_low();
Ok(())
}
}
impl<I, C> InputPin for Pin<I, Input<C>>
impl<I, C> embedded_hal::digital::InputPin for Pin<I, Input<C>>
where
I: PinId,
C: InputConfig,
{
#[inline]
fn is_high(&mut self) -> Result<bool, Self::Error> {
Ok(self._is_high())
Ok(self.is_high_mut())
}
#[inline]
fn is_low(&mut self) -> Result<bool, Self::Error> {
Ok(self._is_low())
Ok(self.is_low_mut())
}
}
impl<I, C> StatefulOutputPin for Pin<I, Output<C>>
impl<I, C> embedded_hal::digital::StatefulOutputPin for Pin<I, Output<C>>
where
I: PinId,
C: OutputConfig + ReadableOutput,
{
#[inline]
fn is_set_high(&mut self) -> Result<bool, Self::Error> {
Ok(self._is_high())
Ok(self.is_high())
}
#[inline]
fn is_set_low(&mut self) -> Result<bool, Self::Error> {
Ok(self._is_low())
}
}
impl<I, C> InputPin for Pin<I, Output<C>>
where
I: PinId,
C: OutputConfig + ReadableOutput,
{
#[inline]
fn is_high(&mut self) -> Result<bool, Self::Error> {
Ok(self._is_high())
Ok(self.is_low())
}
#[inline]
fn is_low(&mut self) -> Result<bool, Self::Error> {
Ok(self._is_low())
fn toggle(&mut self) -> Result<(), Self::Error> {
self.toggle();
Ok(())
}
}

View File

@ -1,375 +0,0 @@
use super::dynpin::{self, DynGroup, DynPinId, DynPinMode};
use super::pin::{FilterType, InterruptEdge, InterruptLevel, PinState};
use super::IsMaskedError;
use crate::clock::FilterClkSel;
use va108xx::{ioconfig, porta};
/// Type definition to avoid confusion: These register blocks are identical
type PortRegisterBlock = porta::RegisterBlock;
//==================================================================================================
// ModeFields
//==================================================================================================
/// Collect all fields needed to set the [`PinMode`](super::PinMode)
#[derive(Default)]
struct ModeFields {
dir: bool,
opendrn: bool,
pull_en: bool,
/// true for pullup, false for pulldown
pull_dir: bool,
funsel: u8,
enb_input: bool,
}
impl From<DynPinMode> for ModeFields {
#[inline]
fn from(mode: DynPinMode) -> Self {
let mut fields = Self::default();
use DynPinMode::*;
match mode {
Input(config) => {
use dynpin::DynInput::*;
fields.dir = false;
match config {
Floating => (),
PullUp => {
fields.pull_en = true;
fields.pull_dir = true;
}
PullDown => {
fields.pull_en = true;
}
}
}
Output(config) => {
use dynpin::DynOutput::*;
fields.dir = true;
match config {
PushPull => (),
OpenDrain => {
fields.opendrn = true;
}
ReadableOpenDrain => {
fields.enb_input = true;
fields.opendrn = true;
}
ReadablePushPull => {
fields.enb_input = true;
}
}
}
Alternate(config) => {
fields.funsel = config as u8;
}
}
fields
}
}
//==================================================================================================
// Register Interface
//==================================================================================================
pub type PortReg = ioconfig::Porta;
/// Provide a safe register interface for pin objects
///
/// [`PORTA`] and [`PORTB`], like every PAC `struct`, is [`Send`] but not [`Sync`], because it
/// points to a `RegisterBlock` of `VolatileCell`s. Unfortunately, such an
/// interface is quite restrictive. Instead, it would be ideal if we could split
/// the [`PORT`] into independent pins that are both [`Send`] and [`Sync`].
///
/// [`PORT`] is a single, zero-sized marker `struct` that provides access to
/// every [`PORT`] register. Instead, we would like to create zero-sized marker
/// `struct`s for every pin, where each pin is only allowed to control its own
/// registers. Furthermore, each pin `struct` should be a singleton, so that
/// exclusive access to the `struct` also guarantees exclusive access to the
/// corresponding registers. Finally, the pin `struct`s should not have any
/// interior mutability. Together, these requirements would allow the pin
/// `struct`s to be both [`Send`] and [`Sync`].
///
/// This trait creates a safe API for accomplishing these goals. Implementers
/// supply a pin ID through the [`id`] function. The remaining functions provide
/// a safe API for accessing the registers associated with that pin ID. Any
/// modification of the registers requires `&mut self`, which destroys interior
/// mutability.
///
/// # Safety
///
/// Users should only implement the [`id`] function. No default function
/// implementations should be overridden. The implementing type must also have
/// "control" over the corresponding pin ID, i.e. it must guarantee that a each
/// pin ID is a singleton.
///
/// [`id`]: Self::id
pub(super) unsafe trait RegisterInterface {
/// Provide a [`DynPinId`] identifying the set of registers controlled by
/// this type.
fn id(&self) -> DynPinId;
const PORTA: *const PortRegisterBlock = va108xx::Porta::ptr();
const PORTB: *const PortRegisterBlock = va108xx::Portb::ptr();
/// Change the pin mode
#[inline]
fn change_mode(&mut self, mode: DynPinMode) {
let ModeFields {
dir,
funsel,
opendrn,
pull_dir,
pull_en,
enb_input,
} = mode.into();
let (portreg, iocfg) = (self.port_reg(), self.iocfg_port());
iocfg.write(|w| {
w.opendrn().bit(opendrn);
w.pen().bit(pull_en);
w.plevel().bit(pull_dir);
w.iewo().bit(enb_input);
unsafe { w.funsel().bits(funsel) }
});
let mask = self.mask_32();
unsafe {
if dir {
portreg.dir().modify(|r, w| w.bits(r.bits() | mask));
// Clear output
portreg.clrout().write(|w| w.bits(mask));
} else {
portreg.dir().modify(|r, w| w.bits(r.bits() & !mask));
}
}
}
#[inline]
fn port_reg(&self) -> &PortRegisterBlock {
match self.id().group {
DynGroup::A => unsafe { &(*Self::PORTA) },
DynGroup::B => unsafe { &(*Self::PORTB) },
}
}
fn iocfg_port(&self) -> &PortReg {
let ioconfig = unsafe { va108xx::Ioconfig::ptr().as_ref().unwrap() };
match self.id().group {
DynGroup::A => ioconfig.porta(self.id().num as usize),
DynGroup::B => ioconfig.portb0(self.id().num as usize),
}
}
#[inline]
fn mask_32(&self) -> u32 {
1 << self.id().num
}
#[inline]
fn enable_irq(&self) {
self.port_reg()
.irq_enb()
.modify(|r, w| unsafe { w.bits(r.bits() | self.mask_32()) });
}
#[inline]
/// Read the logic level of an output pin
fn read_pin(&self) -> bool {
let portreg = self.port_reg();
((portreg.datainraw().read().bits() >> self.id().num) & 0x01) == 1
}
// Get DATAMASK bit for this particular pin
#[inline(always)]
fn datamask(&self) -> bool {
let portreg = self.port_reg();
(portreg.datamask().read().bits() >> self.id().num) == 1
}
/// Read a pin but use the masked version but check whether the datamask for the pin is
/// cleared as well
#[inline(always)]
fn read_pin_masked(&self) -> Result<bool, IsMaskedError> {
if !self.datamask() {
Err(IsMaskedError)
} else {
Ok(((self.port_reg().datain().read().bits() >> self.id().num) & 0x01) == 1)
}
}
/// Write the logic level of an output pin
#[inline(always)]
fn write_pin(&mut self, bit: bool) {
// Safety: SETOUT is a "mask" register, and we only write the bit for
// this pin ID
unsafe {
if bit {
self.port_reg().setout().write(|w| w.bits(self.mask_32()));
} else {
self.port_reg().clrout().write(|w| w.bits(self.mask_32()));
}
}
}
/// Write the logic level of an output pin but check whether the datamask for the pin is
/// cleared as well
#[inline]
fn write_pin_masked(&mut self, bit: bool) -> Result<(), IsMaskedError> {
if !self.datamask() {
Err(IsMaskedError)
} else {
// Safety: SETOUT is a "mask" register, and we only write the bit for
// this pin ID
unsafe {
if bit {
self.port_reg().setout().write(|w| w.bits(self.mask_32()));
} else {
self.port_reg().clrout().write(|w| w.bits(self.mask_32()));
}
Ok(())
}
}
}
/// Toggle the logic level of an output pin
#[inline(always)]
fn toggle(&mut self) {
// Safety: TOGOUT is a "mask" register, and we only write the bit for
// this pin ID
unsafe { self.port_reg().togout().write(|w| w.bits(self.mask_32())) };
}
/// Only useful for interrupt pins. Configure whether to use edges or level as interrupt soure
/// When using edge mode, it is possible to generate interrupts on both edges as well
#[inline]
fn interrupt_edge(&mut self, edge_type: InterruptEdge) {
unsafe {
self.port_reg()
.irq_sen()
.modify(|r, w| w.bits(r.bits() & !self.mask_32()));
match edge_type {
InterruptEdge::HighToLow => {
self.port_reg()
.irq_evt()
.modify(|r, w| w.bits(r.bits() & !self.mask_32()));
}
InterruptEdge::LowToHigh => {
self.port_reg()
.irq_evt()
.modify(|r, w| w.bits(r.bits() | self.mask_32()));
}
InterruptEdge::BothEdges => {
self.port_reg()
.irq_edge()
.modify(|r, w| w.bits(r.bits() | self.mask_32()));
}
}
}
}
/// Configure which edge or level type triggers an interrupt
#[inline]
fn interrupt_level(&mut self, level: InterruptLevel) {
unsafe {
self.port_reg()
.irq_sen()
.modify(|r, w| w.bits(r.bits() | self.mask_32()));
if level == InterruptLevel::Low {
self.port_reg()
.irq_evt()
.modify(|r, w| w.bits(r.bits() & !self.mask_32()));
} else {
self.port_reg()
.irq_evt()
.modify(|r, w| w.bits(r.bits() | self.mask_32()));
}
}
}
/// Only useful for input pins
#[inline]
fn filter_type(&mut self, filter: FilterType, clksel: FilterClkSel) {
self.iocfg_port().modify(|_, w| {
// Safety: Only write to register for this Pin ID
unsafe {
w.flttype().bits(filter as u8);
w.fltclk().bits(clksel as u8)
}
});
}
/// Set DATAMASK bit for this particular pin. 1 is the default
/// state of the bit and allows access of the corresponding bit
#[inline(always)]
fn set_datamask(&self) {
let portreg = self.port_reg();
unsafe {
portreg
.datamask()
.modify(|r, w| w.bits(r.bits() | self.mask_32()));
}
}
/// Clear DATAMASK bit for this particular pin. This prevents access
/// of the corresponding bit for output and input operations
#[inline(always)]
fn clear_datamask(&self) {
let portreg = self.port_reg();
unsafe {
portreg
.datamask()
.modify(|r, w| w.bits(r.bits() & !self.mask_32()));
}
}
/// Only useful for output pins
/// See p.52 of the programmers guide for more information.
/// When configured for pulse mode, a given pin will set the non-default state for exactly
/// one clock cycle before returning to the configured default state
fn pulse_mode(&mut self, enable: bool, default_state: PinState) {
let portreg = self.port_reg();
unsafe {
if enable {
portreg
.pulse()
.modify(|r, w| w.bits(r.bits() | self.mask_32()));
} else {
portreg
.pulse()
.modify(|r, w| w.bits(r.bits() & !self.mask_32()));
}
if default_state == PinState::Low {
portreg
.pulsebase()
.modify(|r, w| w.bits(r.bits() & !self.mask_32()));
} else {
portreg
.pulsebase()
.modify(|r, w| w.bits(r.bits() | self.mask_32()));
}
}
}
/// Only useful for output pins
fn delay(&self, delay_1: bool, delay_2: bool) {
let portreg = self.port_reg();
unsafe {
if delay_1 {
portreg
.delay1()
.modify(|r, w| w.bits(r.bits() | self.mask_32()));
} else {
portreg
.delay1()
.modify(|r, w| w.bits(r.bits() & !self.mask_32()));
}
if delay_2 {
portreg
.delay2()
.modify(|r, w| w.bits(r.bits() | self.mask_32()));
} else {
portreg
.delay2()
.modify(|r, w| w.bits(r.bits() & !self.mask_32()));
}
}
}
}

View File

@ -36,8 +36,6 @@ pub struct InvalidTimingParamsError;
#[derive(Debug, PartialEq, Eq, thiserror::Error)]
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
pub enum Error {
//#[error("Invalid timing parameters")]
//InvalidTimingParams,
#[error("arbitration lost")]
ArbitrationLost,
#[error("nack address")]
@ -82,6 +80,7 @@ impl embedded_hal::i2c::Error for Error {
}
#[derive(Debug, PartialEq, Copy, Clone)]
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
enum I2cCmd {
Start = 0b00,
Stop = 0b10,
@ -252,6 +251,8 @@ impl Default for MasterConfig {
impl Sealed for MasterConfig {}
#[derive(Debug)]
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
pub struct SlaveConfig {
pub tx_fe_mode: FifoEmptyMode,
pub rx_fe_mode: FifoEmptyMode,
@ -455,13 +456,6 @@ impl<I2c: Instance> I2cBase<I2c> {
}
}
// Unique mode to use the loopback functionality
// pub struct I2cLoopback<I2C> {
// i2c_base: I2cBase<I2C>,
// master_cfg: MasterConfig,
// slave_cfg: SlaveConfig,
// }
//==================================================================================================
// I2C Master
//==================================================================================================
@ -673,275 +667,6 @@ impl<I2c: Instance, Addr> I2cMaster<I2c, Addr> {
}
}
/*
macro_rules! i2c_master {
($($I2CX:path: ($i2cx:ident, $clk_enb:path),)+) => {
$(
impl<ADDR> I2cMaster<$I2CX, ADDR> {
pub fn $i2cx(
i2c: $I2CX,
cfg: MasterConfig,
sys_clk: impl Into<Hertz> + Copy,
speed_mode: I2cSpeed,
sys_cfg: Option<&mut pac::Sysconfig>,
) -> Self {
I2cMaster {
i2c_base: I2cBase::$i2cx(
i2c,
sys_clk,
speed_mode,
Some(&cfg),
None,
sys_cfg
),
_addr: PhantomData,
}
.enable_master()
}
#[inline]
pub fn cancel_transfer(&self) {
self.i2c_base
.i2c
.cmd()
.write(|w| unsafe { w.bits(I2cCmd::Cancel as u32) });
}
#[inline]
pub fn clear_tx_fifo(&self) {
self.i2c_base.i2c.fifo_clr().write(|w| w.txfifo().set_bit());
}
#[inline]
pub fn clear_rx_fifo(&self) {
self.i2c_base.i2c.fifo_clr().write(|w| w.rxfifo().set_bit());
}
#[inline]
pub fn enable_master(self) -> Self {
self.i2c_base.i2c.ctrl().modify(|_, w| w.enable().set_bit());
self
}
#[inline]
pub fn disable_master(self) -> Self {
self.i2c_base.i2c.ctrl().modify(|_, w| w.enable().clear_bit());
self
}
#[inline(always)]
fn load_fifo(&self, word: u8) {
self.i2c_base
.i2c
.data()
.write(|w| unsafe { w.bits(word as u32) });
}
#[inline(always)]
fn read_fifo(&self) -> u8 {
self.i2c_base.i2c.data().read().bits() as u8
}
fn error_handler_write(&mut self, init_cmd: &I2cCmd) {
self.clear_tx_fifo();
if *init_cmd == I2cCmd::Start {
self.i2c_base.stop_cmd()
}
}
fn write_base(
&mut self,
addr: I2cAddress,
init_cmd: I2cCmd,
bytes: impl IntoIterator<Item = u8>,
) -> Result<(), Error> {
let mut iter = bytes.into_iter();
// Load address
let (addr, addr_mode_bit) = I2cBase::<$I2CX>::unwrap_addr(addr);
self.i2c_base.i2c.address().write(|w| unsafe {
w.bits(I2cDirection::Send as u32 | (addr << 1) as u32 | addr_mode_bit)
});
self.i2c_base
.i2c
.cmd()
.write(|w| unsafe { w.bits(init_cmd as u32) });
let mut load_if_next_available = || {
if let Some(next_byte) = iter.next() {
self.load_fifo(next_byte);
}
};
loop {
let status_reader = self.i2c_base.i2c.status().read();
if status_reader.arblost().bit_is_set() {
self.error_handler_write(&init_cmd);
return Err(Error::ArbitrationLost);
} else if status_reader.nackaddr().bit_is_set() {
self.error_handler_write(&init_cmd);
return Err(Error::NackAddr);
} else if status_reader.nackdata().bit_is_set() {
self.error_handler_write(&init_cmd);
return Err(Error::NackData);
} else if status_reader.idle().bit_is_set() {
return Ok(());
} else {
while !status_reader.txnfull().bit_is_set() {
load_if_next_available();
}
}
}
}
fn write_from_buffer(
&mut self,
init_cmd: I2cCmd,
addr: I2cAddress,
output: &[u8],
) -> Result<(), Error> {
let len = output.len();
// It should theoretically possible to transfer larger data sizes by tracking
// the number of sent words and setting it to 0x7fe as soon as only that many
// bytes are remaining. However, large transfer like this are not common. This
// feature will therefore not be supported for now.
if len > 0x7fe {
return Err(Error::DataTooLarge);
}
// Load number of words
self.i2c_base
.i2c
.words()
.write(|w| unsafe { w.bits(len as u32) });
let mut bytes = output.iter();
// FIFO has a depth of 16. We load slightly above the trigger level
// but not all of it because the transaction might fail immediately
const FILL_DEPTH: usize = 12;
// load the FIFO
for _ in 0..core::cmp::min(FILL_DEPTH, len) {
self.load_fifo(*bytes.next().unwrap());
}
self.write_base(addr, init_cmd, output.iter().cloned())
}
fn read_internal(&mut self, addr: I2cAddress, buffer: &mut [u8]) -> Result<(), Error> {
let len = buffer.len();
// It should theoretically possible to transfer larger data sizes by tracking
// the number of sent words and setting it to 0x7fe as soon as only that many
// bytes are remaining. However, large transfer like this are not common. This
// feature will therefore not be supported for now.
if len > 0x7fe {
return Err(Error::DataTooLarge);
}
// Clear the receive FIFO
self.clear_rx_fifo();
// Load number of words
self.i2c_base
.i2c
.words()
.write(|w| unsafe { w.bits(len as u32) });
let (addr, addr_mode_bit) = match addr {
I2cAddress::Regular(addr) => (addr as u16, 0 << 15),
I2cAddress::TenBit(addr) => (addr, 1 << 15),
};
// Load address
self.i2c_base.i2c.address().write(|w| unsafe {
w.bits(I2cDirection::Read as u32 | (addr << 1) as u32 | addr_mode_bit)
});
let mut buf_iter = buffer.iter_mut();
let mut read_bytes = 0;
// Start receive transfer
self.i2c_base
.i2c
.cmd()
.write(|w| unsafe { w.bits(I2cCmd::StartWithStop as u32) });
let mut read_if_next_available = || {
if let Some(next_byte) = buf_iter.next() {
*next_byte = self.read_fifo();
}
};
loop {
let status_reader = self.i2c_base.i2c.status().read();
if status_reader.arblost().bit_is_set() {
self.clear_rx_fifo();
return Err(Error::ArbitrationLost);
} else if status_reader.nackaddr().bit_is_set() {
self.clear_rx_fifo();
return Err(Error::NackAddr);
} else if status_reader.idle().bit_is_set() {
if read_bytes != len {
return Err(Error::InsufficientDataReceived);
}
return Ok(());
} else if status_reader.rxnempty().bit_is_set() {
read_if_next_available();
read_bytes += 1;
}
}
}
}
//======================================================================================
// Embedded HAL I2C implementations
//======================================================================================
impl embedded_hal::i2c::ErrorType for I2cMaster<$I2CX, SevenBitAddress> {
type Error = Error;
}
impl embedded_hal::i2c::I2c for I2cMaster<$I2CX, SevenBitAddress> {
fn transaction(
&mut self,
address: SevenBitAddress,
operations: &mut [Operation<'_>],
) -> Result<(), Self::Error> {
for operation in operations {
match operation {
Operation::Read(buf) => self.read_internal(I2cAddress::Regular(address), buf)?,
Operation::Write(buf) => self.write_from_buffer(
I2cCmd::StartWithStop,
I2cAddress::Regular(address),
buf,
)?,
}
}
Ok(())
}
}
impl embedded_hal::i2c::ErrorType for I2cMaster<$I2CX, TenBitAddress> {
type Error = Error;
}
impl embedded_hal::i2c::I2c<TenBitAddress> for I2cMaster<$I2CX, TenBitAddress> {
fn transaction(
&mut self,
address: TenBitAddress,
operations: &mut [Operation<'_>],
) -> Result<(), Self::Error> {
for operation in operations {
match operation {
Operation::Read(buf) => self.read_internal(I2cAddress::TenBit(address), buf)?,
Operation::Write(buf) => self.write_from_buffer(
I2cCmd::StartWithStop,
I2cAddress::TenBit(address),
buf,
)?,
}
}
Ok(())
}
}
)+
}
}
i2c_master!(
pac::I2ca: (i2ca, PeripheralClocks::I2c0),
pac::I2cb: (i2cb, PeripheralClocks::I2c1),
);
*/
//======================================================================================
// Embedded HAL I2C implementations
//======================================================================================

View File

@ -1,6 +1,7 @@
#![no_std]
#![cfg_attr(docsrs, feature(doc_auto_cfg))]
use gpio::Port;
pub use va108xx;
pub use va108xx as pac;
@ -19,6 +20,7 @@ pub mod uart;
#[derive(Debug, Eq, Copy, Clone, PartialEq)]
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
pub enum FunSel {
Sel0 = 0b00,
Sel1 = 0b01,
Sel2 = 0b10,
Sel3 = 0b11,
@ -26,13 +28,6 @@ pub enum FunSel {
#[derive(Debug, Copy, Clone, PartialEq, Eq)]
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
pub enum PortSel {
PortA,
PortB,
}
#[derive(Copy, Clone, PartialEq, Eq)]
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
pub enum PeripheralSelect {
PortA = 0,
PortB = 1,
@ -54,6 +49,7 @@ pub enum PeripheralSelect {
/// 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)]
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
pub struct InterruptConfig {
/// Interrupt target vector. Should always be set, might be required for disabling IRQs
pub id: pac::Interrupt,
@ -76,37 +72,33 @@ impl InterruptConfig {
pub type IrqCfg = InterruptConfig;
#[derive(Debug, PartialEq, Eq)]
#[derive(Debug, PartialEq, Eq, thiserror::Error)]
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
pub struct InvalidPin(pub(crate) ());
#[error("invalid pin with number {0}")]
pub struct InvalidPinError(u8);
/// Can be used to manually manipulate the function select of port pins
/// Can be used to manually manipulate the function select of port pins.
///
/// The function selection table can be found on p.36 of the programmers guide. Please note
/// that most of the structures and APIs in this library will automatically correctly configure
/// the pin or statically expect the correct pin type.
pub fn port_function_select(
ioconfig: &mut pac::Ioconfig,
port: PortSel,
port: Port,
pin: u8,
funsel: FunSel,
) -> Result<(), InvalidPin> {
match port {
PortSel::PortA => {
if pin > 31 {
return Err(InvalidPin(()));
}
ioconfig
.porta(pin as usize)
.modify(|_, w| unsafe { w.funsel().bits(funsel as u8) });
Ok(())
}
PortSel::PortB => {
if pin > 23 {
return Err(InvalidPin(()));
}
ioconfig
.portb0(pin as usize)
.modify(|_, w| unsafe { w.funsel().bits(funsel as u8) });
Ok(())
}
) -> Result<(), InvalidPinError> {
if (port == Port::A && pin >= 32) || (port == Port::B && pin >= 24) {
return Err(InvalidPinError(pin));
}
let reg_block = match port {
Port::A => ioconfig.porta(pin as usize),
Port::B => ioconfig.portb0(pin as usize),
};
reg_block.modify(|_, w| unsafe { w.funsel().bits(funsel as u8) });
Ok(())
}
/// Enable a specific interrupt using the NVIC peripheral.

View File

@ -33,6 +33,7 @@ use embedded_hal::spi::{Mode, MODE_0};
const FILL_DEPTH: usize = 12;
pub const BMSTART_BMSTOP_MASK: u32 = 1 << 31;
pub const BMSKIPDATA_MASK: u32 = 1 << 30;
pub const DEFAULT_CLK_DIV: u16 = 2;
@ -288,6 +289,7 @@ pub trait TransferConfigProvider {
/// This struct contains all configuration parameter which are transfer specific
/// and might change for transfers to different SPI slaves
#[derive(Copy, Clone, Debug)]
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
pub struct TransferConfigWithHwcs<HwCs> {
pub hw_cs: Option<HwCs>,
pub cfg: TransferConfig,
@ -296,6 +298,7 @@ pub struct TransferConfigWithHwcs<HwCs> {
/// Type erased variant of the transfer configuration. This is required to avoid generics in
/// the SPI constructor.
#[derive(Copy, Clone, Debug)]
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
pub struct TransferConfig {
pub clk_cfg: Option<SpiClkConfig>,
pub mode: Option<Mode>,
@ -383,6 +386,8 @@ impl<HwCs: HwCsProvider> TransferConfigProvider for TransferConfigWithHwcs<HwCs>
}
/// Configuration options for the whole SPI bus. See Programmer Guide p.92 for more details
#[derive(Debug)]
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
pub struct SpiConfig {
clk: SpiClkConfig,
// SPI mode configuration
@ -532,6 +537,7 @@ pub struct Spi<SpiInstance, Pins, Word = u8> {
pins: Pins,
}
#[inline(always)]
pub fn mode_to_cpo_cph_bit(mode: embedded_hal::spi::Mode) -> (bool, bool) {
match mode {
embedded_hal::spi::MODE_0 => (false, false),
@ -575,6 +581,7 @@ impl SpiClkConfig {
}
#[derive(Debug, thiserror::Error)]
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
pub enum SpiClkConfigError {
#[error("division by zero")]
DivIsZero,

View File

@ -22,7 +22,7 @@ use crate::{
};
use embedded_hal_nb::serial::Read;
#[derive(Debug)]
#[derive(Debug, Clone, Copy)]
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
pub enum Bank {
A = 0,
@ -381,6 +381,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;
/// Retrieve the peripheral structure.
///
@ -388,7 +389,11 @@ 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.
///
@ -405,14 +410,11 @@ impl Instance for pac::Uarta {
const IDX: u8 = 0;
const PERIPH_SEL: PeripheralSelect = PeripheralSelect::Uart0;
const PTR: *const uart_base::RegisterBlock = Self::PTR;
#[inline(always)]
unsafe fn steal() -> Self {
pac::Peripherals::steal().uarta
}
#[inline(always)]
fn ptr() -> *const uart_base::RegisterBlock {
Self::ptr() as *const _
Self::steal()
}
}
@ -420,14 +422,25 @@ impl Instance for pac::Uartb {
const IDX: u8 = 1;
const PERIPH_SEL: PeripheralSelect = PeripheralSelect::Uart1;
const PTR: *const uart_base::RegisterBlock = Self::PTR;
#[inline(always)]
unsafe fn steal() -> Self {
pac::Peripherals::steal().uartb
Self::steal()
}
#[inline(always)]
fn ptr() -> *const uart_base::RegisterBlock {
Self::ptr() as *const _
}
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::A => unsafe { pac::Uarta::reg_block() },
Bank::B => unsafe { pac::Uartb::reg_block() },
}
}
}
@ -794,14 +807,12 @@ pub fn disable_rx_interrupts(uart: &uart_base::RegisterBlock) {
/// Serial receiver.
///
/// Can be created by using the [Uart::split] or [UartBase::split] API.
pub struct Rx<Uart> {
uart: Uart,
}
pub struct Rx<Uart>(Uart);
impl<Uart: Instance> Rx<Uart> {
#[inline(always)]
fn new(uart: Uart) -> Self {
Self { uart }
const fn new(uart: Uart) -> Self {
Self(uart)
}
/// Direct access to the peripheral structure.
@ -810,13 +821,13 @@ impl<Uart: Instance> Rx<Uart> {
///
/// 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
pub const unsafe fn uart(&self) -> &Uart {
&self.0
}
#[inline]
pub fn clear_fifo(&self) {
self.uart.fifo_clr().write(|w| w.rxfifo().set_bit());
self.0.fifo_clr().write(|w| w.rxfifo().set_bit());
}
#[inline]
@ -846,7 +857,7 @@ impl<Uart: Instance> Rx<Uart> {
/// value if you use the manual parity mode. See chapter 4.6.2 for more information.
#[inline(always)]
pub fn read_fifo(&self) -> nb::Result<u32, Infallible> {
if self.uart.rxstatus().read().rdavl().bit_is_clear() {
if self.0.rxstatus().read().rdavl().bit_is_clear() {
return Err(nb::Error::WouldBlock);
}
Ok(self.read_fifo_unchecked())
@ -862,7 +873,7 @@ impl<Uart: Instance> Rx<Uart> {
/// value if you use the manual parity mode. See chapter 4.6.2 for more information.
#[inline(always)]
pub fn read_fifo_unchecked(&self) -> u32 {
self.uart.data().read().bits()
self.0.data().read().bits()
}
pub fn into_rx_with_irq(self) -> RxWithInterrupt<Uart> {
@ -871,7 +882,7 @@ impl<Uart: Instance> Rx<Uart> {
#[inline(always)]
pub fn release(self) -> Uart {
self.uart
self.0
}
}
@ -959,9 +970,7 @@ pub fn disable_tx_interrupts(uart: &uart_base::RegisterBlock) {
/// Serial transmitter
///
/// Can be created by using the [Uart::split] or [UartBase::split] API.
pub struct Tx<Uart> {
uart: Uart,
}
pub struct Tx<Uart>(Uart);
impl<Uart: Instance> Tx<Uart> {
/// Retrieve a TX pin without expecting an explicit UART structure
@ -971,14 +980,12 @@ impl<Uart: Instance> Tx<Uart> {
/// Circumvents the HAL safety guarantees.
#[inline(always)]
pub unsafe fn steal() -> Self {
Self {
uart: Uart::steal(),
}
Self(Uart::steal())
}
#[inline(always)]
fn new(uart: Uart) -> Self {
Self { uart }
Self(uart)
}
/// Direct access to the peripheral structure.
@ -987,25 +994,23 @@ impl<Uart: Instance> Tx<Uart> {
///
/// 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
pub const unsafe fn uart(&self) -> &Uart {
&self.0
}
#[inline]
pub fn clear_fifo(&self) {
self.uart.fifo_clr().write(|w| w.txfifo().set_bit());
self.0.fifo_clr().write(|w| w.txfifo().set_bit());
}
#[inline]
pub fn enable(&mut self) {
// Safety: We own the UART structure
enable_tx(unsafe { Uart::reg_block() });
self.0.enable().modify(|_, w| w.txenable().set_bit());
}
#[inline]
pub fn disable(&mut self) {
// Safety: We own the UART structure
disable_tx(unsafe { Uart::reg_block() });
self.0.enable().modify(|_, w| w.txenable().clear_bit());
}
/// Enables the IRQ_TX, IRQ_TX_STATUS and IRQ_TX_EMPTY interrupts.
@ -1037,7 +1042,7 @@ impl<Uart: Instance> Tx<Uart> {
/// value if you use the manual parity mode. See chapter 11.4.1 for more information.
#[inline(always)]
pub fn write_fifo(&self, data: u32) -> nb::Result<(), Infallible> {
if self.uart.txstatus().read().wrrdy().bit_is_clear() {
if self.0.txstatus().read().wrrdy().bit_is_clear() {
return Err(nb::Error::WouldBlock);
}
self.write_fifo_unchecked(data);
@ -1052,7 +1057,7 @@ impl<Uart: Instance> Tx<Uart> {
/// API.
#[inline(always)]
pub fn write_fifo_unchecked(&self, data: u32) {
self.uart.data().write(|w| unsafe { w.bits(data) });
self.0.data().write(|w| unsafe { w.bits(data) });
}
pub fn into_async(self) -> TxAsync<Uart> {
@ -1135,7 +1140,7 @@ impl<Uart: Instance> RxWithInterrupt<Uart> {
#[inline(always)]
pub fn uart(&self) -> &Uart {
&self.0.uart
&self.0 .0
}
/// This function is used together with the [Self::on_interrupt_max_size_or_timeout_based]

View File

@ -1,18 +1,16 @@
//! # Async UART reception functionality for the VA108xx family.
//! # Async UART reception functionality for the VA416xx family.
//!
//! This module provides the [RxAsync] and [RxAsyncSharedConsumer] struct which both implement the
//! 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 four interrupt handlers:
//! However, it provides two interrupt handlers:
//!
//! - [on_interrupt_uart_a]
//! - [on_interrupt_uart_b]
//! - [on_interrupt_uart_a_overwriting]
//! - [on_interrupt_uart_b_overwriting]
//! - [on_interrupt_rx]
//! - [on_interrupt_rx_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.
//! [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.
@ -25,11 +23,10 @@ use core::{cell::RefCell, convert::Infallible, future::Future, sync::atomic::Ord
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 va108xx::uarta as uart_base;
use super::{Instance, Rx, RxError, UartErrors};
use super::{Bank, 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];
@ -72,7 +69,7 @@ pub struct AsyncUartErrors {
pub uart_errors: UartErrors,
}
fn on_interrupt_handle_rx_errors<Uart: Instance>(uart: &Uart) -> Option<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()
@ -94,81 +91,65 @@ fn on_interrupt_handle_rx_errors<Uart: Instance>(uart: &Uart) -> Option<UartErro
None
}
fn on_interrupt_rx_common_post_processing<Uart: Instance>(
uart: &Uart,
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[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();
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);
errors = on_interrupt_handle_rx_errors(uart_regs);
}
// Clear the interrupt status bits
uart.irq_clr().write(|w| unsafe { w.bits(irq_end) });
uart_regs.irq_clr().write(|w| unsafe { w.bits(irq_end) });
errors
}
/// Interrupt handler for UART A.
/// 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_uart_a_overwriting<const N: usize>(
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(
unsafe { pac::Uarta::steal() },
prod,
shared_consumer,
)
on_interrupt_rx_async_heapless_queue_overwriting(bank, 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>(
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> {
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 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.rxfifoirqtrg().read().bits() as usize;
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.data().read().bits();
let byte = uart_regs.data().read().bits();
if !prod.ready() {
queue_overflow = true;
critical_section::with(|cs| {
@ -183,9 +164,9 @@ pub fn on_interrupt_rx_async_heapless_queue_overwriting<Uart: Instance, const N:
// Timeout, empty the FIFO completely.
if irq_end.irq_rx_to().bit_is_set() {
while uart.rxstatus().read().rdavl().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.data().read().bits();
let byte = uart_regs.data().read().bits();
if !prod.ready() {
queue_overflow = true;
critical_section::with(|cs| {
@ -199,7 +180,7 @@ pub fn on_interrupt_rx_async_heapless_queue_overwriting<Uart: Instance, const N:
}
let uart_errors =
on_interrupt_rx_common_post_processing(&uart, rx_enabled, read_some_data, irq_end.bits());
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,
@ -209,29 +190,21 @@ pub fn on_interrupt_rx_async_heapless_queue_overwriting<Uart: Instance, const N:
Ok(())
}
/// Interrupt handler for UART A.
/// Interrupt handler for asynchronous RX operations.
///
/// Should be called in the user interrupt handler to enable asynchronous reception.
pub fn on_interrupt_uart_a<const N: usize>(
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(unsafe { pac::Uarta::steal() }, prod)
on_interrupt_rx_async_heapless_queue(bank, 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>(
pub fn on_interrupt_rx_async_heapless_queue<const N: usize>(
bank: Bank,
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 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();
@ -268,7 +241,7 @@ pub fn on_interrupt_rx_async_heapless_queue<Uart: Instance, const N: usize>(
}
let uart_errors =
on_interrupt_rx_common_post_processing(&uart, rx_enabled, read_some_data, irq_end.bits());
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,
@ -286,24 +259,32 @@ impl Drop for ActiveReadGuard {
}
}
/// 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> {
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();
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.
/// 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();
@ -313,7 +294,23 @@ impl<Uart: Instance, const N: usize> RxAsync<Uart, N> {
rx.enable_interrupts();
rx.enable();
});
Self { rx, queue }
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();
}
}
@ -321,7 +318,7 @@ 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 {
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);
@ -333,33 +330,38 @@ impl<Uart: Instance, const N: usize> embedded_io_async::Read for RxAsync<Uart, N
}
data_to_read
};
let fut = RxFuture::new(&mut self.rx);
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 self.queue);
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 self.queue))
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_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>>>>,
}
/// [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 RxAsyncSharedConsumer<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> RxAsyncSharedConsumer<Uart, N> {
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
@ -367,7 +369,7 @@ impl<Uart: Instance, const N: usize> RxAsyncSharedConsumer<Uart, N> {
/// interrupt handler to overwrite old data.
pub fn new(
mut rx: Rx<Uart>,
queue: &'static Mutex<RefCell<Option<heapless::spsc::Consumer<'static, u8, N>>>>,
shared_consumer: &'static Mutex<RefCell<Option<heapless::spsc::Consumer<'static, u8, N>>>>,
) -> Self {
rx.disable_interrupts();
rx.disable();
@ -377,25 +379,44 @@ impl<Uart: Instance, const N: usize> RxAsyncSharedConsumer<Uart, N> {
rx.enable_interrupts();
rx.enable();
});
Self { rx, queue }
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> embedded_io_async::Read for RxAsyncSharedConsumer<Uart, N> {
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.queue.borrow(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 = || {
let mut handle_data_in_queue = |inner: &mut RxAsyncOverwritingInner<Uart, N>| {
critical_section::with(|cs| {
let mut consumer_ref = self.queue.borrow(cs).borrow_mut();
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) {
@ -405,15 +426,15 @@ impl<Uart: Instance, const N: usize> embedded_io_async::Read for RxAsyncSharedCo
data_to_read
})
};
let fut = RxFuture::new(&mut self.rx);
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();
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();
let read_data = handle_data_in_queue(self.0.as_mut().unwrap());
Ok(read_data)
}
}

View File

@ -3,13 +3,10 @@
//! 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 provides two interrupt handlers:
//! However, it the [on_interrupt_tx] interrupt handler.
//!
//! - [on_interrupt_uart_a_tx]
//! - [on_interrupt_uart_b_tx]
//!
//! Those should be called in ALL user interrupt handlers which handle UART TX interrupts,
//! depending on which UARTs are used.
//! This handler should be called in ALL user interrupt handlers which handle UART TX interrupts
//! for a given UART bank.
//!
//! # Example
//!
@ -30,21 +27,14 @@ static TX_CONTEXTS: [Mutex<RefCell<TxContext>>; 2] =
// critical section.
static TX_DONE: [AtomicBool; 2] = [const { AtomicBool::new(false) }; 2];
/// This is a generic interrupt handler to handle asynchronous UART TX operations. The user
/// has to call this once in the interrupt handler responsible for UART A TX interrupts for
/// asynchronous operations to work.
pub fn on_interrupt_uart_a_tx() {
on_interrupt_uart_tx(unsafe { pac::Uarta::steal() });
}
/// This is a generic interrupt handler to handle asynchronous UART TX operations. The user
/// has to call this once in the interrupt handler responsible for UART B TX interrupts for
/// asynchronous operations to work.
pub fn on_interrupt_uart_b_tx() {
on_interrupt_uart_tx(unsafe { pac::Uartb::steal() });
}
fn on_interrupt_uart_tx<Uart: Instance>(uart: Uart) {
/// 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() {
@ -54,7 +44,7 @@ fn on_interrupt_uart_tx<Uart: Instance>(uart: Uart) {
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[Uart::IDX as usize].borrow(cs);
let context_ref = TX_CONTEXTS[idx].borrow(cs);
*context_ref.borrow()
});
context.tx_overrun = unexpected_overrun;
@ -67,12 +57,12 @@ fn on_interrupt_uart_tx<Uart: Instance>(uart: Uart) {
uart.enable().modify(|_, w| w.txenable().clear_bit());
// Write back updated context structure.
critical_section::with(|cs| {
let context_ref = TX_CONTEXTS[Uart::IDX as usize].borrow(cs);
let context_ref = TX_CONTEXTS[idx].borrow(cs);
*context_ref.borrow_mut() = context;
});
// Transfer is done.
TX_DONE[Uart::IDX as usize].store(true, core::sync::atomic::Ordering::Relaxed);
UART_TX_WAKERS[Uart::IDX as usize].wake();
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
@ -92,7 +82,7 @@ fn on_interrupt_uart_tx<Uart: Instance>(uart: Uart) {
// Write back updated context structure.
critical_section::with(|cs| {
let context_ref = TX_CONTEXTS[Uart::IDX as usize].borrow(cs);
let context_ref = TX_CONTEXTS[idx].borrow(cs);
*context_ref.borrow_mut() = context;
});
}

View File

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

View File

@ -1,6 +1,6 @@
[package]
name = "va108xx"
version = "0.4.0"
version = "0.5.0"
authors = ["Robin Mueller <muellerr@irs.uni-stuttgart.de>"]
edition = "2021"
description = "PAC for the Vorago VA108xx family of microcontrollers"
@ -13,6 +13,7 @@ categories = ["embedded", "no-std", "hardware-support"]
[dependencies]
cortex-m = "0.7"
vcell = "0.1.3"
defmt = { version = "0.3", optional = true }
critical-section = { version = "1", optional = true }
[dependencies.cortex-m-rt]
@ -21,6 +22,8 @@ version = ">=0.6.15,<0.8"
[features]
rt = ["cortex-m-rt/device"]
# Adds Debug implementation
debug = []
[package.metadata.docs.rs]
all-features = true

View File

@ -26,6 +26,12 @@ The `rt` feature is optional and recommended. It brings in support for `cortex-m
For full details on the autgenerated API, please see the
[svd2rust documentation](https://docs.rs/svd2rust/latest/svd2rust/#peripheral-api).
## Optional Features
- [`defmt`](https://defmt.ferrous-systems.com/): Add support for `defmt` by adding the
[`defmt::Format`](https://defmt.ferrous-systems.com/format) derive on many types.
- `debug`: Add `Debug` derives for various structures
## Regenerating the PAC
If you want to re-generate the PAC, for example if the register file `va416xx.svd` changes

View File

@ -30,7 +30,7 @@ fi
svdtools patch svd/va108xx-patch.yml
# See https://github.com/rust-embedded/svd2rust/issues/830 for required re-export.
${svd2rust_bin} --reexport-interrupt -i svd/va108xx.svd.patched
${svd2rust_bin} --reexport-interrupt --impl-defmt defmt --impl-debug-feature debug -i svd/va108xx.svd.patched
result=$?
if [ $result -ne 0 ]; then

View File

@ -2,6 +2,7 @@
pub type R = crate::R<AddressSpec>;
#[doc = "Register `ADDRESS` writer"]
pub type W = crate::W<AddressSpec>;
#[cfg(feature = "debug")]
impl core::fmt::Debug for R {
fn fmt(&self, f: &mut core::fmt::Formatter) -> core::fmt::Result {
write!(f, "{}", self.bits())

View File

@ -2,6 +2,7 @@
pub type R = crate::R<ClktolimitSpec>;
#[doc = "Register `CLKTOLIMIT` writer"]
pub type W = crate::W<ClktolimitSpec>;
#[cfg(feature = "debug")]
impl core::fmt::Debug for R {
fn fmt(&self, f: &mut core::fmt::Formatter) -> core::fmt::Result {
write!(f, "{}", self.bits())

View File

@ -2,6 +2,7 @@
pub type R = crate::R<CmdSpec>;
#[doc = "Register `CMD` writer"]
pub type W = crate::W<CmdSpec>;
#[cfg(feature = "debug")]
impl core::fmt::Debug for R {
fn fmt(&self, f: &mut core::fmt::Formatter) -> core::fmt::Result {
write!(f, "{}", self.bits())

View File

@ -2,6 +2,7 @@
pub type R = crate::R<DataSpec>;
#[doc = "Register `DATA` writer"]
pub type W = crate::W<DataSpec>;
#[cfg(feature = "debug")]
impl core::fmt::Debug for R {
fn fmt(&self, f: &mut core::fmt::Formatter) -> core::fmt::Result {
write!(f, "{}", self.bits())

View File

@ -1,5 +1,6 @@
#[doc = "Register `PERID` reader"]
pub type R = crate::R<PeridSpec>;
#[cfg(feature = "debug")]
impl core::fmt::Debug for R {
fn fmt(&self, f: &mut core::fmt::Formatter) -> core::fmt::Result {
write!(f, "{}", self.bits())

View File

@ -1,5 +1,6 @@
#[doc = "Register `RXCOUNT` reader"]
pub type R = crate::R<RxcountSpec>;
#[cfg(feature = "debug")]
impl core::fmt::Debug for R {
fn fmt(&self, f: &mut core::fmt::Formatter) -> core::fmt::Result {
write!(f, "{}", self.bits())

View File

@ -2,6 +2,7 @@
pub type R = crate::R<RxfifoirqtrgSpec>;
#[doc = "Register `RXFIFOIRQTRG` writer"]
pub type W = crate::W<RxfifoirqtrgSpec>;
#[cfg(feature = "debug")]
impl core::fmt::Debug for R {
fn fmt(&self, f: &mut core::fmt::Formatter) -> core::fmt::Result {
write!(f, "{}", self.bits())

View File

@ -2,6 +2,7 @@
pub type R = crate::R<S0AddressSpec>;
#[doc = "Register `S0_ADDRESS` writer"]
pub type W = crate::W<S0AddressSpec>;
#[cfg(feature = "debug")]
impl core::fmt::Debug for R {
fn fmt(&self, f: &mut core::fmt::Formatter) -> core::fmt::Result {
write!(f, "{}", self.bits())

View File

@ -2,6 +2,7 @@
pub type R = crate::R<S0AddressbSpec>;
#[doc = "Register `S0_ADDRESSB` writer"]
pub type W = crate::W<S0AddressbSpec>;
#[cfg(feature = "debug")]
impl core::fmt::Debug for R {
fn fmt(&self, f: &mut core::fmt::Formatter) -> core::fmt::Result {
write!(f, "{}", self.bits())

View File

@ -2,6 +2,7 @@
pub type R = crate::R<S0AddressmaskSpec>;
#[doc = "Register `S0_ADDRESSMASK` writer"]
pub type W = crate::W<S0AddressmaskSpec>;
#[cfg(feature = "debug")]
impl core::fmt::Debug for R {
fn fmt(&self, f: &mut core::fmt::Formatter) -> core::fmt::Result {
write!(f, "{}", self.bits())

View File

@ -2,6 +2,7 @@
pub type R = crate::R<S0AddressmaskbSpec>;
#[doc = "Register `S0_ADDRESSMASKB` writer"]
pub type W = crate::W<S0AddressmaskbSpec>;
#[cfg(feature = "debug")]
impl core::fmt::Debug for R {
fn fmt(&self, f: &mut core::fmt::Formatter) -> core::fmt::Result {
write!(f, "{}", self.bits())

View File

@ -2,6 +2,7 @@
pub type R = crate::R<S0DataSpec>;
#[doc = "Register `S0_DATA` writer"]
pub type W = crate::W<S0DataSpec>;
#[cfg(feature = "debug")]
impl core::fmt::Debug for R {
fn fmt(&self, f: &mut core::fmt::Formatter) -> core::fmt::Result {
write!(f, "{}", self.bits())

View File

@ -1,5 +1,6 @@
#[doc = "Register `S0_LASTADDRESS` reader"]
pub type R = crate::R<S0LastaddressSpec>;
#[cfg(feature = "debug")]
impl core::fmt::Debug for R {
fn fmt(&self, f: &mut core::fmt::Formatter) -> core::fmt::Result {
write!(f, "{}", self.bits())

View File

@ -2,6 +2,7 @@
pub type R = crate::R<S0MaxwordsSpec>;
#[doc = "Register `S0_MAXWORDS` writer"]
pub type W = crate::W<S0MaxwordsSpec>;
#[cfg(feature = "debug")]
impl core::fmt::Debug for R {
fn fmt(&self, f: &mut core::fmt::Formatter) -> core::fmt::Result {
write!(f, "{}", self.bits())

View File

@ -1,5 +1,6 @@
#[doc = "Register `S0_RXCOUNT` reader"]
pub type R = crate::R<S0RxcountSpec>;
#[cfg(feature = "debug")]
impl core::fmt::Debug for R {
fn fmt(&self, f: &mut core::fmt::Formatter) -> core::fmt::Result {
write!(f, "{}", self.bits())

View File

@ -2,6 +2,7 @@
pub type R = crate::R<S0RxfifoirqtrgSpec>;
#[doc = "Register `S0_RXFIFOIRQTRG` writer"]
pub type W = crate::W<S0RxfifoirqtrgSpec>;
#[cfg(feature = "debug")]
impl core::fmt::Debug for R {
fn fmt(&self, f: &mut core::fmt::Formatter) -> core::fmt::Result {
write!(f, "{}", self.bits())

View File

@ -1,5 +1,6 @@
#[doc = "Register `S0_STATE` reader"]
pub type R = crate::R<S0StateSpec>;
#[cfg(feature = "debug")]
impl core::fmt::Debug for R {
fn fmt(&self, f: &mut core::fmt::Formatter) -> core::fmt::Result {
write!(f, "{}", self.bits())

View File

@ -1,5 +1,6 @@
#[doc = "Register `S0_TXCOUNT` reader"]
pub type R = crate::R<S0TxcountSpec>;
#[cfg(feature = "debug")]
impl core::fmt::Debug for R {
fn fmt(&self, f: &mut core::fmt::Formatter) -> core::fmt::Result {
write!(f, "{}", self.bits())

View File

@ -2,6 +2,7 @@
pub type R = crate::R<S0TxfifoirqtrgSpec>;
#[doc = "Register `S0_TXFIFOIRQTRG` writer"]
pub type W = crate::W<S0TxfifoirqtrgSpec>;
#[cfg(feature = "debug")]
impl core::fmt::Debug for R {
fn fmt(&self, f: &mut core::fmt::Formatter) -> core::fmt::Result {
write!(f, "{}", self.bits())

View File

@ -1,5 +1,6 @@
#[doc = "Register `STATE` reader"]
pub type R = crate::R<StateSpec>;
#[cfg(feature = "debug")]
impl core::fmt::Debug for R {
fn fmt(&self, f: &mut core::fmt::Formatter) -> core::fmt::Result {
write!(f, "{}", self.bits())

View File

@ -2,6 +2,7 @@
pub type R = crate::R<TmconfigSpec>;
#[doc = "Register `TMCONFIG` writer"]
pub type W = crate::W<TmconfigSpec>;
#[cfg(feature = "debug")]
impl core::fmt::Debug for R {
fn fmt(&self, f: &mut core::fmt::Formatter) -> core::fmt::Result {
write!(f, "{}", self.bits())

View File

@ -1,5 +1,6 @@
#[doc = "Register `TXCOUNT` reader"]
pub type R = crate::R<TxcountSpec>;
#[cfg(feature = "debug")]
impl core::fmt::Debug for R {
fn fmt(&self, f: &mut core::fmt::Formatter) -> core::fmt::Result {
write!(f, "{}", self.bits())

View File

@ -2,6 +2,7 @@
pub type R = crate::R<TxfifoirqtrgSpec>;
#[doc = "Register `TXFIFOIRQTRG` writer"]
pub type W = crate::W<TxfifoirqtrgSpec>;
#[cfg(feature = "debug")]
impl core::fmt::Debug for R {
fn fmt(&self, f: &mut core::fmt::Formatter) -> core::fmt::Result {
write!(f, "{}", self.bits())

View File

@ -2,6 +2,7 @@
pub type R = crate::R<WordsSpec>;
#[doc = "Register `WORDS` writer"]
pub type W = crate::W<WordsSpec>;
#[cfg(feature = "debug")]
impl core::fmt::Debug for R {
fn fmt(&self, f: &mut core::fmt::Formatter) -> core::fmt::Result {
write!(f, "{}", self.bits())

View File

@ -1,5 +1,6 @@
#[doc = "Register `PERID` reader"]
pub type R = crate::R<PeridSpec>;
#[cfg(feature = "debug")]
impl core::fmt::Debug for R {
fn fmt(&self, f: &mut core::fmt::Formatter) -> core::fmt::Result {
write!(f, "{}", self.bits())

View File

@ -3,6 +3,7 @@ pub type R = crate::R<PortaSpec>;
#[doc = "Register `PORTA[%s]` writer"]
pub type W = crate::W<PortaSpec>;
#[doc = "Input Filter Selectoin\n\nValue on reset: 0"]
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
#[repr(u8)]
pub enum Flttype {

View File

@ -2,6 +2,7 @@
pub type R = crate::R<IntRamSbeSpec>;
#[doc = "Register `INT_RAM_SBE` writer"]
pub type W = crate::W<IntRamSbeSpec>;
#[cfg(feature = "debug")]
impl core::fmt::Debug for R {
fn fmt(&self, f: &mut core::fmt::Formatter) -> core::fmt::Result {
write!(f, "{}", self.bits())

View File

@ -1,5 +1,6 @@
#[doc = "Register `PERID` reader"]
pub type R = crate::R<PeridSpec>;
#[cfg(feature = "debug")]
impl core::fmt::Debug for R {
fn fmt(&self, f: &mut core::fmt::Formatter) -> core::fmt::Result {
write!(f, "{}", self.bits())

View File

@ -97,6 +97,7 @@ pub static __INTERRUPTS: [Vector; 32] = [
Vector { _handler: OC31 },
];
#[doc = r"Enumeration of all the interrupts."]
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
#[derive(Copy, Clone, Debug, PartialEq, Eq)]
#[repr(u16)]
pub enum Interrupt {

View File

@ -1,5 +1,6 @@
#[doc = "Register `DATAIN` reader"]
pub type R = crate::R<DatainSpec>;
#[cfg(feature = "debug")]
impl core::fmt::Debug for R {
fn fmt(&self, f: &mut core::fmt::Formatter) -> core::fmt::Result {
write!(f, "{}", self.bits())

View File

@ -1,5 +1,6 @@
#[doc = "Register `DATAINBYTE[%s]` reader"]
pub type R = crate::R<DatainbyteSpec>;
#[cfg(feature = "debug")]
impl core::fmt::Debug for R {
fn fmt(&self, f: &mut core::fmt::Formatter) -> core::fmt::Result {
write!(f, "{}", self.bits())

View File

@ -2,6 +2,7 @@
pub type R = crate::R<DatamaskSpec>;
#[doc = "Register `DATAMASK` writer"]
pub type W = crate::W<DatamaskSpec>;
#[cfg(feature = "debug")]
impl core::fmt::Debug for R {
fn fmt(&self, f: &mut core::fmt::Formatter) -> core::fmt::Result {
write!(f, "{}", self.bits())

View File

@ -2,6 +2,7 @@
pub type R = crate::R<DatamaskbyteSpec>;
#[doc = "Register `DATAMASKBYTE[%s]` writer"]
pub type W = crate::W<DatamaskbyteSpec>;
#[cfg(feature = "debug")]
impl core::fmt::Debug for R {
fn fmt(&self, f: &mut core::fmt::Formatter) -> core::fmt::Result {
write!(f, "{}", self.bits())

View File

@ -1,5 +1,6 @@
#[doc = "Register `DATAOUT` writer"]
pub type W = crate::W<DataoutSpec>;
#[cfg(feature = "debug")]
impl core::fmt::Debug for crate::generic::Reg<DataoutSpec> {
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
write!(f, "(not readable)")

View File

@ -1,5 +1,6 @@
#[doc = "Register `DATAOUTBYTE[%s]` writer"]
pub type W = crate::W<DataoutbyteSpec>;
#[cfg(feature = "debug")]
impl core::fmt::Debug for crate::generic::Reg<DataoutbyteSpec> {
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
write!(f, "(not readable)")

View File

@ -2,6 +2,7 @@
pub type R = crate::R<EdgeStatusSpec>;
#[doc = "Register `EDGE_STATUS` writer"]
pub type W = crate::W<EdgeStatusSpec>;
#[cfg(feature = "debug")]
impl core::fmt::Debug for R {
fn fmt(&self, f: &mut core::fmt::Formatter) -> core::fmt::Result {
write!(f, "{}", self.bits())

View File

@ -2,6 +2,7 @@
pub type R = crate::R<IrqEdgeSpec>;
#[doc = "Register `IRQ_EDGE` writer"]
pub type W = crate::W<IrqEdgeSpec>;
#[cfg(feature = "debug")]
impl core::fmt::Debug for R {
fn fmt(&self, f: &mut core::fmt::Formatter) -> core::fmt::Result {
write!(f, "{}", self.bits())

View File

@ -2,6 +2,7 @@
pub type R = crate::R<IrqEnbSpec>;
#[doc = "Register `IRQ_ENB` writer"]
pub type W = crate::W<IrqEnbSpec>;
#[cfg(feature = "debug")]
impl core::fmt::Debug for R {
fn fmt(&self, f: &mut core::fmt::Formatter) -> core::fmt::Result {
write!(f, "{}", self.bits())

View File

@ -1,5 +1,6 @@
#[doc = "Register `IRQ_END` reader"]
pub type R = crate::R<IrqEndSpec>;
#[cfg(feature = "debug")]
impl core::fmt::Debug for R {
fn fmt(&self, f: &mut core::fmt::Formatter) -> core::fmt::Result {
write!(f, "{}", self.bits())

View File

@ -2,6 +2,7 @@
pub type R = crate::R<IrqEvtSpec>;
#[doc = "Register `IRQ_EVT` writer"]
pub type W = crate::W<IrqEvtSpec>;
#[cfg(feature = "debug")]
impl core::fmt::Debug for R {
fn fmt(&self, f: &mut core::fmt::Formatter) -> core::fmt::Result {
write!(f, "{}", self.bits())

View File

@ -1,5 +1,6 @@
#[doc = "Register `IRQ_RAW` reader"]
pub type R = crate::R<IrqRawSpec>;
#[cfg(feature = "debug")]
impl core::fmt::Debug for R {
fn fmt(&self, f: &mut core::fmt::Formatter) -> core::fmt::Result {
write!(f, "{}", self.bits())

View File

@ -2,6 +2,7 @@
pub type R = crate::R<IrqSenSpec>;
#[doc = "Register `IRQ_SEN` writer"]
pub type W = crate::W<IrqSenSpec>;
#[cfg(feature = "debug")]
impl core::fmt::Debug for R {
fn fmt(&self, f: &mut core::fmt::Formatter) -> core::fmt::Result {
write!(f, "{}", self.bits())

View File

@ -1,5 +1,6 @@
#[doc = "Register `PERID` reader"]
pub type R = crate::R<PeridSpec>;
#[cfg(feature = "debug")]
impl core::fmt::Debug for R {
fn fmt(&self, f: &mut core::fmt::Formatter) -> core::fmt::Result {
write!(f, "{}", self.bits())

View File

@ -2,6 +2,7 @@
pub type R = crate::R<ClkprescaleSpec>;
#[doc = "Register `CLKPRESCALE` writer"]
pub type W = crate::W<ClkprescaleSpec>;
#[cfg(feature = "debug")]
impl core::fmt::Debug for R {
fn fmt(&self, f: &mut core::fmt::Formatter) -> core::fmt::Result {
write!(f, "{}", self.bits())

View File

@ -2,6 +2,7 @@
pub type R = crate::R<DataSpec>;
#[doc = "Register `DATA` writer"]
pub type W = crate::W<DataSpec>;
#[cfg(feature = "debug")]
impl core::fmt::Debug for R {
fn fmt(&self, f: &mut core::fmt::Formatter) -> core::fmt::Result {
write!(f, "{}", self.bits())

View File

@ -1,5 +1,6 @@
#[doc = "Register `PERID` reader"]
pub type R = crate::R<PeridSpec>;
#[cfg(feature = "debug")]
impl core::fmt::Debug for R {
fn fmt(&self, f: &mut core::fmt::Formatter) -> core::fmt::Result {
write!(f, "{}", self.bits())

View File

@ -2,6 +2,7 @@
pub type R = crate::R<RxfifoirqtrgSpec>;
#[doc = "Register `RXFIFOIRQTRG` writer"]
pub type W = crate::W<RxfifoirqtrgSpec>;
#[cfg(feature = "debug")]
impl core::fmt::Debug for R {
fn fmt(&self, f: &mut core::fmt::Formatter) -> core::fmt::Result {
write!(f, "{}", self.bits())

View File

@ -1,5 +1,6 @@
#[doc = "Register `STATE` reader"]
pub type R = crate::R<StateSpec>;
#[cfg(feature = "debug")]
impl core::fmt::Debug for R {
fn fmt(&self, f: &mut core::fmt::Formatter) -> core::fmt::Result {
write!(f, "{}", self.bits())

View File

@ -2,6 +2,7 @@
pub type R = crate::R<TxfifoirqtrgSpec>;
#[doc = "Register `TXFIFOIRQTRG` writer"]
pub type W = crate::W<TxfifoirqtrgSpec>;
#[cfg(feature = "debug")]
impl core::fmt::Debug for R {
fn fmt(&self, f: &mut core::fmt::Formatter) -> core::fmt::Result {
write!(f, "{}", self.bits())

View File

@ -1,5 +1,6 @@
#[doc = "Register `EF_CONFIG` reader"]
pub type R = crate::R<EfConfigSpec>;
#[cfg(feature = "debug")]
impl core::fmt::Debug for R {
fn fmt(&self, f: &mut core::fmt::Formatter) -> core::fmt::Result {
write!(f, "{}", self.bits())

View File

@ -1,5 +1,6 @@
#[doc = "Register `EF_ID` reader"]
pub type R = crate::R<EfIdSpec>;
#[cfg(feature = "debug")]
impl core::fmt::Debug for R {
fn fmt(&self, f: &mut core::fmt::Formatter) -> core::fmt::Result {
write!(f, "{}", self.bits())

View File

@ -2,6 +2,7 @@
pub type R = crate::R<IoconfigClkdivSpec>;
#[doc = "Register `IOCONFIG_CLKDIV%s` writer"]
pub type W = crate::W<IoconfigClkdivSpec>;
#[cfg(feature = "debug")]
impl core::fmt::Debug for R {
fn fmt(&self, f: &mut core::fmt::Formatter) -> core::fmt::Result {
write!(f, "{}", self.bits())

View File

@ -1,5 +1,6 @@
#[doc = "Register `IOCONFIG_CLKDIV0` reader"]
pub type R = crate::R<IoconfigClkdiv0Spec>;
#[cfg(feature = "debug")]
impl core::fmt::Debug for R {
fn fmt(&self, f: &mut core::fmt::Formatter) -> core::fmt::Result {
write!(f, "{}", self.bits())

View File

@ -1,5 +1,6 @@
#[doc = "Register `PERID` reader"]
pub type R = crate::R<PeridSpec>;
#[cfg(feature = "debug")]
impl core::fmt::Debug for R {
fn fmt(&self, f: &mut core::fmt::Formatter) -> core::fmt::Result {
write!(f, "{}", self.bits())

View File

@ -1,5 +1,6 @@
#[doc = "Register `PROCID` reader"]
pub type R = crate::R<ProcidSpec>;
#[cfg(feature = "debug")]
impl core::fmt::Debug for R {
fn fmt(&self, f: &mut core::fmt::Formatter) -> core::fmt::Result {
write!(f, "{}", self.bits())

View File

@ -2,6 +2,7 @@
pub type R = crate::R<RamSbeSpec>;
#[doc = "Register `RAM_SBE` writer"]
pub type W = crate::W<RamSbeSpec>;
#[cfg(feature = "debug")]
impl core::fmt::Debug for R {
fn fmt(&self, f: &mut core::fmt::Formatter) -> core::fmt::Result {
write!(f, "{}", self.bits())

View File

@ -2,6 +2,7 @@
pub type R = crate::R<RefreshConfigSpec>;
#[doc = "Register `REFRESH_CONFIG` writer"]
pub type W = crate::W<RefreshConfigSpec>;
#[cfg(feature = "debug")]
impl core::fmt::Debug for R {
fn fmt(&self, f: &mut core::fmt::Formatter) -> core::fmt::Result {
write!(f, "{}", self.bits())

View File

@ -1,5 +1,6 @@
#[doc = "Register `ROM_RETRIES` reader"]
pub type R = crate::R<RomRetriesSpec>;
#[cfg(feature = "debug")]
impl core::fmt::Debug for R {
fn fmt(&self, f: &mut core::fmt::Formatter) -> core::fmt::Result {
write!(f, "{}", self.bits())

View File

@ -2,6 +2,7 @@
pub type R = crate::R<TimClkEnableSpec>;
#[doc = "Register `TIM_CLK_ENABLE` writer"]
pub type W = crate::W<TimClkEnableSpec>;
#[cfg(feature = "debug")]
impl core::fmt::Debug for R {
fn fmt(&self, f: &mut core::fmt::Formatter) -> core::fmt::Result {
write!(f, "{}", self.bits())

Some files were not shown because too many files have changed in this diff Show More