Compare commits

..

No commits in common. "main" and "va108xx-embassy-v0.1.2" have entirely different histories.

139 changed files with 1776 additions and 1461 deletions

View File

@ -4,9 +4,10 @@
# runner = "arm-none-eabi-gdb -q -x openocd.gdb" # runner = "arm-none-eabi-gdb -q -x openocd.gdb"
# runner = "gdb-multiarch -q -x openocd.gdb" # runner = "gdb-multiarch -q -x openocd.gdb"
# runner = "gdb -q -x openocd.gdb" # runner = "gdb -q -x openocd.gdb"
# runner = "gdb-multiarch -q -x jlink.gdb" runner = "gdb-multiarch -q -x jlink.gdb"
runner = "probe-rs run --chip VA108xx_RAM --protocol jtag" # 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", "$CHIP", "--log-format", "{L} {s}"] # runner = ["probe-rs", "run", "--chip", "$CHIP", "--log-format", "{L} {s}"]
rustflags = [ rustflags = [

View File

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

View File

@ -60,56 +60,14 @@ 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 You can use CLI or VS Code for flashing, running and debugging. In any case, take
care of installing the pre-requisites first. care of installing the pre-requisites first.
### Using CLI with probe-rs ### Pre-Requisites
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 1. [SEGGER J-Link tools](https://www.segger.com/downloads/jlink/) installed
2. [gdb-multiarch](https://packages.debian.org/sid/gdb-multiarch) or similar 2. [gdb-multiarch](https://packages.debian.org/sid/gdb-multiarch) or similar
cross-architecture debugger installed. All commands here assume `gdb-multiarch`. cross-architecture debugger installed. All commands here assume `gdb-multiarch`.
### Using CLI
You can build the blinky example application with the following command You can build the blinky example application with the following command
```sh ```sh
@ -143,8 +101,25 @@ runner = "gdb-multiarch -q -x jlink/jlink.gdb"
After that, you can simply use `cargo run --example blinky` to flash the blinky After that, you can simply use `cargo run --example blinky` to flash the blinky
example. example.
### Using the RTT Viewer ### Using VS Code
The Segger RTT viewer can be used to display log messages received from the target. The base Assuming a working debug connection to your VA108xx board, you can debug using VS Code with
address for the RTT block placement is 0x10000000. It is recommended to use a search range of the [`Cortex-Debug` plugin](https://marketplace.visualstudio.com/items?itemName=marus25.cortex-debug).
0x1000 around that base address when using the RTT viewer. 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.

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -18,19 +18,21 @@ use core::cell::RefCell;
use critical_section::Mutex; use critical_section::Mutex;
use embassy_executor::Spawner; use embassy_executor::Spawner;
use embassy_time::Instant; use embassy_time::Instant;
use embedded_hal::digital::StatefulOutputPin;
use embedded_io::Write; use embedded_io::Write;
use embedded_io_async::Read; use embedded_io_async::Read;
use heapless::spsc::{Consumer, Producer, Queue}; use heapless::spsc::{Consumer, Producer, Queue};
use panic_rtt_target as _; use panic_rtt_target as _;
use rtt_target::{rprintln, rtt_init_print}; use rtt_target::{rprintln, rtt_init_print};
use va108xx_embassy::embassy;
use va108xx_hal::{ use va108xx_hal::{
gpio::PinsA, gpio::PinsA,
pac::{self, interrupt}, pac::{self, interrupt},
prelude::*, prelude::*,
uart::{ uart::{
self, on_interrupt_rx_overwriting, self, on_interrupt_uart_b_overwriting,
rx_asynch::{on_interrupt_rx, RxAsync}, rx_asynch::{on_interrupt_uart_a, RxAsync},
Bank, RxAsyncOverwriting, Tx, RxAsyncSharedConsumer, Tx,
}, },
InterruptConfig, InterruptConfig,
}; };
@ -55,13 +57,15 @@ async fn main(spawner: Spawner) {
let mut dp = pac::Peripherals::take().unwrap(); let mut dp = pac::Peripherals::take().unwrap();
// Safety: Only called once here. // Safety: Only called once here.
va108xx_embassy::init( unsafe {
&mut dp.sysconfig, embassy::init(
&dp.irqsel, &mut dp.sysconfig,
SYSCLK_FREQ, &dp.irqsel,
dp.tim23, SYSCLK_FREQ,
dp.tim22, dp.tim23,
); dp.tim22,
);
}
let porta = PinsA::new(&mut dp.sysconfig, dp.porta); let porta = PinsA::new(&mut dp.sysconfig, dp.porta);
let mut led0 = porta.pa10.into_readable_push_pull_output(); let mut led0 = porta.pa10.into_readable_push_pull_output();
@ -102,16 +106,16 @@ async fn main(spawner: Spawner) {
*CONSUMER_UART_B.borrow(cs).borrow_mut() = Some(cons_uart_b); *CONSUMER_UART_B.borrow(cs).borrow_mut() = Some(cons_uart_b);
}); });
let mut async_rx_uart_a = RxAsync::new(rx_uart_a, cons_uart_a); let mut async_rx_uart_a = RxAsync::new(rx_uart_a, cons_uart_a);
let async_rx_uart_b = RxAsyncOverwriting::new(rx_uart_b, &CONSUMER_UART_B); let async_rx_uart_b = RxAsyncSharedConsumer::new(rx_uart_b, &CONSUMER_UART_B);
spawner spawner
.spawn(uart_b_task(async_rx_uart_b, tx_uart_b)) .spawn(uart_b_task(async_rx_uart_b, tx_uart_b))
.unwrap(); .unwrap();
let mut buf = [0u8; 256]; let mut buf = [0u8; 256];
loop { loop {
rprintln!("Current time UART A: {}", Instant::now().as_secs()); rprintln!("Current time UART A: {}", Instant::now().as_secs());
led0.toggle(); led0.toggle().ok();
led1.toggle(); led1.toggle().ok();
led2.toggle(); led2.toggle().ok();
let read_bytes = async_rx_uart_a.read(&mut buf).await.unwrap(); let read_bytes = async_rx_uart_a.read(&mut buf).await.unwrap();
let read_str = core::str::from_utf8(&buf[..read_bytes]).unwrap(); let read_str = core::str::from_utf8(&buf[..read_bytes]).unwrap();
rprintln!( rprintln!(
@ -124,7 +128,7 @@ async fn main(spawner: Spawner) {
} }
#[embassy_executor::task] #[embassy_executor::task]
async fn uart_b_task(mut async_rx: RxAsyncOverwriting<pac::Uartb, 256>, mut tx: Tx<pac::Uartb>) { async fn uart_b_task(mut async_rx: RxAsyncSharedConsumer<pac::Uartb, 256>, mut tx: Tx<pac::Uartb>) {
let mut buf = [0u8; 256]; let mut buf = [0u8; 256];
loop { loop {
rprintln!("Current time UART B: {}", Instant::now().as_secs()); rprintln!("Current time UART B: {}", Instant::now().as_secs());
@ -145,7 +149,7 @@ async fn uart_b_task(mut async_rx: RxAsyncOverwriting<pac::Uartb, 256>, mut tx:
fn OC2() { fn OC2() {
let mut prod = let mut prod =
critical_section::with(|cs| PRODUCER_UART_A.borrow(cs).borrow_mut().take().unwrap()); critical_section::with(|cs| PRODUCER_UART_A.borrow(cs).borrow_mut().take().unwrap());
let errors = on_interrupt_rx(Bank::A, &mut prod); let errors = on_interrupt_uart_a(&mut prod);
critical_section::with(|cs| *PRODUCER_UART_A.borrow(cs).borrow_mut() = Some(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. // In a production app, we could use a channel to send the errors to the main task.
if let Err(errors) = errors { if let Err(errors) = errors {
@ -158,7 +162,7 @@ fn OC2() {
fn OC3() { fn OC3() {
let mut prod = let mut prod =
critical_section::with(|cs| PRODUCER_UART_B.borrow(cs).borrow_mut().take().unwrap()); critical_section::with(|cs| PRODUCER_UART_B.borrow(cs).borrow_mut().take().unwrap());
let errors = on_interrupt_rx_overwriting(Bank::B, &mut prod, &CONSUMER_UART_B); let errors = on_interrupt_uart_b_overwriting(&mut prod, &CONSUMER_UART_B);
critical_section::with(|cs| *PRODUCER_UART_B.borrow(cs).borrow_mut() = Some(prod)); 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. // In a production app, we could use a channel to send the errors to the main task.
if let Err(errors) = errors { if let Err(errors) = errors {

View File

@ -12,14 +12,16 @@
#![no_main] #![no_main]
use embassy_executor::Spawner; use embassy_executor::Spawner;
use embassy_time::{Duration, Instant, Ticker}; use embassy_time::{Duration, Instant, Ticker};
use embedded_hal::digital::StatefulOutputPin;
use embedded_io_async::Write; use embedded_io_async::Write;
use panic_rtt_target as _; use panic_rtt_target as _;
use rtt_target::{rprintln, rtt_init_print}; use rtt_target::{rprintln, rtt_init_print};
use va108xx_embassy::embassy;
use va108xx_hal::{ use va108xx_hal::{
gpio::PinsA, gpio::PinsA,
pac::{self, interrupt}, pac::{self, interrupt},
prelude::*, prelude::*,
uart::{self, on_interrupt_tx, Bank, TxAsync}, uart::{self, on_interrupt_uart_a_tx, TxAsync},
InterruptConfig, InterruptConfig,
}; };
@ -41,13 +43,15 @@ async fn main(_spawner: Spawner) {
let mut dp = pac::Peripherals::take().unwrap(); let mut dp = pac::Peripherals::take().unwrap();
// Safety: Only called once here. // Safety: Only called once here.
va108xx_embassy::init( unsafe {
&mut dp.sysconfig, embassy::init(
&dp.irqsel, &mut dp.sysconfig,
SYSCLK_FREQ, &dp.irqsel,
dp.tim23, SYSCLK_FREQ,
dp.tim22, dp.tim23,
); dp.tim22,
);
}
let porta = PinsA::new(&mut dp.sysconfig, dp.porta); let porta = PinsA::new(&mut dp.sysconfig, dp.porta);
let mut led0 = porta.pa10.into_readable_push_pull_output(); let mut led0 = porta.pa10.into_readable_push_pull_output();
@ -71,9 +75,9 @@ async fn main(_spawner: Spawner) {
let mut idx = 0; let mut idx = 0;
loop { loop {
rprintln!("Current time: {}", Instant::now().as_secs()); rprintln!("Current time: {}", Instant::now().as_secs());
led0.toggle(); led0.toggle().ok();
led1.toggle(); led1.toggle().ok();
led2.toggle(); led2.toggle().ok();
let _written = async_tx let _written = async_tx
.write(STR_LIST[idx].as_bytes()) .write(STR_LIST[idx].as_bytes())
.await .await
@ -89,5 +93,5 @@ async fn main(_spawner: Spawner) {
#[interrupt] #[interrupt]
#[allow(non_snake_case)] #[allow(non_snake_case)]
fn OC2() { fn OC2() {
on_interrupt_tx(Bank::A); on_interrupt_uart_a_tx();
} }

View File

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

View File

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

View File

@ -69,16 +69,18 @@ mod app {
// Configure an edge interrupt on the button and route it to interrupt vector 15 // Configure an edge interrupt on the button and route it to interrupt vector 15
let mut button = Button::new(pinsa.pa11.into_floating_input()); 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 { if mode == PressMode::Toggle {
// This filter debounces the switch for edge based interrupts // This filter debounces the switch for edge based interrupts
button.configure_filter_type(FilterType::FilterFourClockCycles, FilterClkSel::Clk1); button.configure_filter_type(FilterType::FilterFourClockCycles, FilterClkSel::Clk1);
set_clk_div_register(&mut dp.sysconfig, FilterClkSel::Clk1, 50_000); 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( let mut leds = Leds::new(
pinsa.pa10.into_push_pull_output(), pinsa.pa10.into_push_pull_output(),
pinsa.pa7.into_push_pull_output(), pinsa.pa7.into_push_pull_output(),

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -8,12 +8,6 @@ and this project adheres to [Semantic Versioning](http://semver.org/).
## [unreleased] ## [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 ## [v0.1.2] and [v0.1.1] 2025-02-13
Docs patch Docs patch

View File

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

View File

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

View File

@ -1,17 +1,17 @@
//! # Embassy-rs support for the Vorago VA108xx MCU family //! # Embassy-rs support for the Vorago VA108xx MCU family
//! //!
//! This repository contains the [embassy-rs](https://github.com/embassy-rs/embassy) support for //! This repository contains the [embassy-rs](https://github.com/embassy-rs/embassy) support for the
//! the VA108xx family. Currently, it contains the time driver to allow using embassy-rs. It uses //! VA108xx family. Currently, it contains the time driver to allow using embassy-rs. It uses the TIM
//! the TIM peripherals provided by the VA108xx family for this purpose. //! peripherals provided by the VA108xx family for this purpose.
//! //!
//! ## Usage //! ## Usage
//! //!
//! This library exposes the [init] or the [init_with_custom_irqs] functions which set up the time //! This library only exposes the [embassy::init] method which sets up the time driver. This
//! driver. This function must be called once at the start of the application. //! function must be called once at the start of the application.
//! //!
//! This implementation requires two TIM peripherals provided by the VA108xx device. //! 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 //! The user can freely specify the two used TIM peripheral by passing the concrete TIM instances
//! into the [init_with_custom_irqs] and [init] method. //! into the [embassy::init_with_custom_irqs] and [embassy::init] method.
//! //!
//! The application also requires two interrupt handlers to handle the timekeeper and alarm //! 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 //! 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 //! 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 //! 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 //! using the [embassy_time_driver_irqs] macro to declare the IRQ handlers in the
//! application code. If this is done, [init_with_custom_irqs] must be used //! application code. If this is done, [embassy::init_with_custom_irqs] must be used
//! method to pass the IRQ numbers to the library. //! method to pass the IRQ numbers to the library.
//! //!
//! ## Examples //! ## Examples
@ -33,7 +33,8 @@
#![no_std] #![no_std]
#![cfg_attr(docsrs, feature(doc_auto_cfg))] #![cfg_attr(docsrs, feature(doc_auto_cfg))]
use core::cell::{Cell, RefCell}; use core::cell::{Cell, RefCell};
use critical_section::{CriticalSection, Mutex}; use critical_section::CriticalSection;
use embassy_sync::blocking_mutex::CriticalSectionMutex as Mutex;
use portable_atomic::{AtomicU32, Ordering}; use portable_atomic::{AtomicU32, Ordering};
use embassy_time_driver::{time_driver_impl, Driver, TICK_HZ}; use embassy_time_driver::{time_driver_impl, Driver, TICK_HZ};
@ -45,7 +46,7 @@ use va108xx_hal::{
clock::enable_peripheral_clock, clock::enable_peripheral_clock,
enable_nvic_interrupt, pac, enable_nvic_interrupt, pac,
prelude::*, prelude::*,
timer::{enable_tim_clk, get_tim_raw, TimRegInterface, ValidTim}, timer::{enable_tim_clk, get_tim_raw, TimRegInterface},
PeripheralSelect, PeripheralSelect,
}; };
@ -76,7 +77,7 @@ macro_rules! embassy_time_driver_irqs {
#[allow(non_snake_case)] #[allow(non_snake_case)]
fn $timekeeper_irq() { fn $timekeeper_irq() {
// Safety: We call it once here. // Safety: We call it once here.
unsafe { $crate::time_driver().on_interrupt_timekeeping() } unsafe { $crate::embassy::time_driver().on_interrupt_timekeeping() }
} }
const ALARM_IRQ: pac::Interrupt = pac::Interrupt::$alarm_irq; const ALARM_IRQ: pac::Interrupt = pac::Interrupt::$alarm_irq;
@ -85,7 +86,7 @@ macro_rules! embassy_time_driver_irqs {
#[allow(non_snake_case)] #[allow(non_snake_case)]
fn $alarm_irq() { fn $alarm_irq() {
// Safety: We call it once here. // Safety: We call it once here.
unsafe { $crate::time_driver().on_interrupt_alarm() } unsafe { $crate::embassy::time_driver().on_interrupt_alarm() }
} }
}; };
} }
@ -99,58 +100,65 @@ embassy_time_driver_irqs!(timekeeper_irq = OC30, alarm_irq = OC29);
#[cfg(feature = "irq-oc28-oc29")] #[cfg(feature = "irq-oc28-oc29")]
embassy_time_driver_irqs!(timekeeper_irq = OC29, alarm_irq = OC28); embassy_time_driver_irqs!(timekeeper_irq = OC29, alarm_irq = OC28);
/// Expose the time driver so the user can specify the IRQ handlers themselves. pub mod embassy {
pub fn time_driver() -> &'static TimerDriver { use super::*;
&TIME_DRIVER use va108xx_hal::{pac, timer::TimRegInterface};
}
/// Initialization method for embassy. /// Expose the time driver so the user can specify the IRQ handlers themselves.
/// pub fn time_driver() -> &'static TimerDriver {
/// This should be used if the interrupt handler is provided by the library, which is the &TIME_DRIVER
/// 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 when using custom IRQ handlers. /// Initialization method for embassy
/// ///
/// Requires an explicit [pac::Interrupt] argument for the timekeeper and alarm IRQs. /// # Safety
pub fn init_with_custom_irqs< ///
TimekeeperTim: TimRegInterface + ValidTim, /// This has to be called once at initialization time to initiate the time driver for
AlarmTim: TimRegInterface + ValidTim, /// embassy.
>( #[cfg(feature = "irqs-in-lib")]
syscfg: &mut pac::Sysconfig, pub unsafe fn init(
irqsel: &pac::Irqsel, syscfg: &mut pac::Sysconfig,
sysclk: impl Into<Hertz>, irqsel: &pac::Irqsel,
timekeeper_tim: TimekeeperTim, sysclk: impl Into<Hertz>,
alarm_tim: AlarmTim, timekeeper_tim: impl TimRegInterface,
timekeeper_irq: pac::Interrupt, alarm_tim: impl TimRegInterface,
alarm_irq: pac::Interrupt, ) {
) { TIME_DRIVER.init(
TIME_DRIVER.init( syscfg,
syscfg, irqsel,
irqsel, sysclk,
sysclk, timekeeper_tim,
timekeeper_tim, alarm_tim,
alarm_tim, TIMEKEEPER_IRQ,
timekeeper_irq, ALARM_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,
)
}
} }
struct AlarmState { struct AlarmState {
@ -180,21 +188,21 @@ pub struct TimerDriver {
impl TimerDriver { impl TimerDriver {
#[allow(clippy::too_many_arguments)] #[allow(clippy::too_many_arguments)]
fn init<TimekeeperTim: TimRegInterface + ValidTim, AlarmTim: TimRegInterface + ValidTim>( fn init(
&self, &self,
syscfg: &mut pac::Sysconfig, syscfg: &mut pac::Sysconfig,
irqsel: &pac::Irqsel, irqsel: &pac::Irqsel,
sysclk: impl Into<Hertz>, sysclk: impl Into<Hertz>,
timekeeper_tim: TimekeeperTim, timekeeper_tim: impl TimRegInterface,
alarm_tim: AlarmTim, alarm_tim: impl TimRegInterface,
timekeeper_irq: pac::Interrupt, timekeeper_irq: pac::Interrupt,
alarm_irq: pac::Interrupt, alarm_irq: pac::Interrupt,
) { ) {
if ALARM_TIM.get().is_some() || TIMEKEEPER_TIM.get().is_some() { if ALARM_TIM.get().is_some() {
return; return;
} }
ALARM_TIM.set(AlarmTim::TIM_ID).ok(); ALARM_TIM.set(alarm_tim.tim_id()).ok();
TIMEKEEPER_TIM.set(TimekeeperTim::TIM_ID).ok(); TIMEKEEPER_TIM.set(timekeeper_tim.tim_id()).ok();
enable_peripheral_clock(syscfg, PeripheralSelect::Irqsel); enable_peripheral_clock(syscfg, PeripheralSelect::Irqsel);
enable_tim_clk(syscfg, timekeeper_tim.tim_id()); enable_tim_clk(syscfg, timekeeper_tim.tim_id());
let timekeeper_reg_block = timekeeper_tim.reg_block(); let timekeeper_reg_block = timekeeper_tim.reg_block();

View File

@ -8,27 +8,7 @@ and this project adheres to [Semantic Versioning](http://semver.org/).
## [unreleased] ## [unreleased]
## [v0.10.0] 2025-02-17 ## [v0.9.0]
## 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 ## Fixed

View File

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

View File

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

View File

@ -57,10 +57,11 @@
//! [InvalidPinTypeError]. //! [InvalidPinTypeError].
use super::{ use super::{
pin::{FilterType, Pin, PinId, PinMode}, pin::{FilterType, InterruptEdge, InterruptLevel, Pin, PinId, PinMode, PinState},
InputDynPinAsync, InterruptEdge, InterruptLevel, IsMaskedError, PinState, Port, reg::RegisterInterface,
InputDynPinAsync,
}; };
use crate::{clock::FilterClkSel, enable_nvic_interrupt, pac, FunSel}; use crate::{clock::FilterClkSel, enable_nvic_interrupt, pac, FunSel, InterruptConfig};
//================================================================================================== //==================================================================================================
// DynPinMode configurations // DynPinMode configurations
@ -155,91 +156,54 @@ pub const DYN_ALT_FUNC_3: DynPinMode = DynPinMode::Alternate(DynAlternate::Sel3)
// DynGroup & DynPinId // DynGroup & DynPinId
//================================================================================================== //==================================================================================================
pub type DynGroup = Port; /// Value-level `enum` for pin groups
#[derive(Debug, PartialEq, Eq, Clone, Copy)]
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
pub enum DynGroup {
A,
B,
}
/// Value-level `struct` representing pin IDs
#[derive(Debug, PartialEq, Eq, Clone, Copy)] #[derive(Debug, PartialEq, Eq, Clone, Copy)]
#[cfg_attr(feature = "defmt", derive(defmt::Format))] #[cfg_attr(feature = "defmt", derive(defmt::Format))]
pub struct DynPinId { pub struct DynPinId {
port: Port, pub group: DynGroup,
num: u8, pub num: u8,
}
impl DynPinId {
pub const fn new(port: Port, num: u8) -> Self {
DynPinId { port, num }
}
pub const fn port(&self) -> Port {
self.port
}
pub const fn num(&self) -> u8 {
self.num
}
} }
//================================================================================================== //==================================================================================================
// ModeFields // DynRegisters
//================================================================================================== //==================================================================================================
/// Collect all fields needed to set the [`PinMode`](super::PinMode) /// Provide a safe register interface for [`DynPin`]s
#[derive(Default)] ///
struct ModeFields { /// This `struct` takes ownership of a [`DynPinId`] and provides an API to
dir: bool, /// access the corresponding regsiters.
opendrn: bool, #[derive(Debug)]
pull_en: bool, pub(crate) struct DynRegisters(DynPinId);
/// true for pullup, false for pulldown
pull_dir: bool,
funsel: u8,
enb_input: bool,
}
impl From<DynPinMode> for ModeFields { // [`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] #[inline]
fn from(mode: DynPinMode) -> Self { fn id(&self) -> DynPinId {
let mut fields = Self::default(); self.0
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 impl DynRegisters {
type PortRegisterBlock = pac::porta::RegisterBlock; /// Create a new instance of [`DynRegisters`]
pub type PortReg = pac::ioconfig::Porta; ///
/// # Safety
///
/// Users must never create two simultaneous instances of this `struct` with
/// the same [`DynPinId`]
#[inline]
unsafe fn new(id: DynPinId) -> Self {
DynRegisters(id)
}
}
//================================================================================================== //==================================================================================================
// DynPin // DynPin
@ -251,59 +215,46 @@ pub type PortReg = pac::ioconfig::Porta;
/// by the same type, and pins are tracked and distinguished at run-time. /// by the same type, and pins are tracked and distinguished at run-time.
#[derive(Debug)] #[derive(Debug)]
pub struct DynPin { pub struct DynPin {
id: DynPinId, pub(crate) regs: DynRegisters,
mode: DynPinMode, mode: DynPinMode,
} }
impl DynPin { impl DynPin {
/// Create a new [DynPin] /// Create a new [`DynPin`]
/// ///
/// # Safety /// # 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 /// must be at most one corresponding [`DynPin`] in existence at any given
/// time. Violating this requirement is `unsafe`. /// time. Violating this requirement is `unsafe`.
#[inline] #[inline]
pub(crate) const unsafe fn new(id: DynPinId, mode: DynPinMode) -> Self { pub(crate) 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 { DynPin {
id, regs: DynRegisters::new(id),
mode: DYN_FLOATING_INPUT, mode,
} }
} }
/// Return a copy of the pin ID /// Return a copy of the pin ID
#[inline] #[inline]
pub const fn id(&self) -> DynPinId { pub fn id(&self) -> DynPinId {
self.id self.regs.0
} }
/// Return a copy of the pin mode /// Return a copy of the pin mode
#[inline] #[inline]
pub const fn mode(&self) -> DynPinMode { pub fn mode(&self) -> DynPinMode {
self.mode self.mode
} }
/// Convert the pin to the requested [`DynPinMode`] /// Convert the pin to the requested [`DynPinMode`]
#[inline] #[inline]
pub fn into_mode(&mut self, mode: DynPinMode) { pub fn into_mode(&mut self, mode: DynPinMode) {
self.change_mode(mode); // Only modify registers if we are actually changing pin mode
self.mode = mode; if mode != self.mode {
self.regs.change_mode(mode);
self.mode = mode;
}
} }
#[inline] #[inline]
@ -311,11 +262,6 @@ impl DynPin {
matches!(self.mode, DynPinMode::Input(_)) matches!(self.mode, DynPinMode::Input(_))
} }
#[inline]
pub fn is_output_pin(&self) -> bool {
matches!(self.mode, DynPinMode::Output(_))
}
#[inline] #[inline]
pub fn into_funsel_1(&mut self) { pub fn into_funsel_1(&mut self) {
self.into_mode(DYN_ALT_FUNC_1); self.into_mode(DYN_ALT_FUNC_1);
@ -373,60 +319,205 @@ impl DynPin {
self.into_mode(DYN_RD_OPEN_DRAIN_OUTPUT); self.into_mode(DYN_RD_OPEN_DRAIN_OUTPUT);
} }
#[inline(always)] #[inline]
pub fn is_low(&self) -> Result<bool, InvalidPinTypeError> { pub fn datamask(&self) -> bool {
self.read_internal().map(|v| !v) self.regs.datamask()
} }
#[inline(always)] #[inline]
pub fn is_high(&self) -> Result<bool, InvalidPinTypeError> { pub fn clear_datamask(&mut self) {
self.read_internal() self.regs.clear_datamask();
} }
#[inline(always)] #[inline]
pub fn set_low(&mut self) -> Result<(), InvalidPinTypeError> { pub fn set_datamask(&mut self) {
self.write_internal(false) self.regs.set_datamask();
} }
#[inline(always)] #[inline]
pub fn set_high(&mut self) -> Result<(), InvalidPinTypeError> { pub fn is_high_masked(&self) -> Result<bool, crate::gpio::IsMaskedError> {
self.write_internal(true) self.regs.read_pin_masked()
} }
/// Toggle the logic level of an output pin #[inline]
#[inline(always)] pub fn is_low_masked(&self) -> Result<bool, crate::gpio::IsMaskedError> {
pub fn toggle(&mut self) -> Result<(), InvalidPinTypeError> { self.regs.read_pin_masked().map(|v| !v)
if !self.is_output_pin() { }
return Err(InvalidPinTypeError(self.mode));
#[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);
} }
// Safety: TOGOUT is a "mask" register, and we only write the bit for self.regs.enable_irq();
// this pin ID if let Some(irqsel) = irqsel {
unsafe { self.port_reg().togout().write(|w| w.bits(self.mask_32())) }; if irq_cfg.route {
Ok(()) match self.regs.id().group {
} // Set the correct interrupt number in the IRQSEL register
DynGroup::A => {
pub fn enable_interrupt(&mut self, irq_cfg: crate::InterruptConfig) { irqsel
if irq_cfg.route { .porta0(self.regs.id().num as usize)
self.configure_irqsel(irq_cfg.id); .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) });
}
}
}
} }
if irq_cfg.enable_in_nvic { if irq_cfg.enable_in_nvic {
unsafe { enable_nvic_interrupt(irq_cfg.id) }; 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()) });
} }
pub fn disable_interrupt(&mut self, reset_irqsel: bool) { /// See p.53 of the programmers guide for more information.
if reset_irqsel { /// Possible delays in clock cycles:
self.reset_irqsel(); /// - 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)),
} }
// We only manipulate our own bit. }
self.port_reg()
.irq_enb() /// See p.52 of the programmers guide for more information.
.modify(|r, w| unsafe { w.bits(r.bits() & !self.mask_32()) }); /// 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)
} }
/// Try to recreate a type-level [`Pin`] from a value-level [`DynPin`] /// Try to recreate a type-level [`Pin`] from a value-level [`DynPin`]
@ -436,7 +527,7 @@ impl DynPin {
/// or refuse to perform it. /// or refuse to perform it.
#[inline] #[inline]
pub fn upgrade<I: PinId, M: PinMode>(self) -> Result<Pin<I, M>, InvalidPinTypeError> { pub fn upgrade<I: PinId, M: PinMode>(self) -> Result<Pin<I, M>, InvalidPinTypeError> {
if self.id == I::DYN && self.mode == M::DYN { if self.regs.0 == I::DYN && self.mode == M::DYN {
// The `DynPin` is consumed, so it is safe to replace it with the // The `DynPin` is consumed, so it is safe to replace it with the
// corresponding `Pin` // corresponding `Pin`
return Ok(unsafe { Pin::new() }); return Ok(unsafe { Pin::new() });
@ -452,424 +543,6 @@ impl DynPin {
) -> Result<InputDynPinAsync, InvalidPinTypeError> { ) -> Result<InputDynPinAsync, InvalidPinTypeError> {
InputDynPinAsync::new(self, irq) 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()
}
} }
//================================================================================================== //==================================================================================================
@ -910,38 +583,33 @@ impl embedded_hal::digital::ErrorType for DynPin {
impl embedded_hal::digital::OutputPin for DynPin { impl embedded_hal::digital::OutputPin for DynPin {
#[inline] #[inline]
fn set_high(&mut self) -> Result<(), Self::Error> { fn set_high(&mut self) -> Result<(), Self::Error> {
self.set_high() self._set_high()
} }
#[inline] #[inline]
fn set_low(&mut self) -> Result<(), Self::Error> { fn set_low(&mut self) -> Result<(), Self::Error> {
self.set_low() self._set_low()
} }
} }
impl embedded_hal::digital::InputPin for DynPin { impl embedded_hal::digital::InputPin for DynPin {
#[inline] #[inline]
fn is_high(&mut self) -> Result<bool, Self::Error> { fn is_high(&mut self) -> Result<bool, Self::Error> {
self.is_high_mut() self._is_high()
} }
#[inline] #[inline]
fn is_low(&mut self) -> Result<bool, Self::Error> { fn is_low(&mut self) -> Result<bool, Self::Error> {
self.is_low_mut() self._is_low()
} }
} }
impl embedded_hal::digital::StatefulOutputPin for DynPin { impl embedded_hal::digital::StatefulOutputPin for DynPin {
#[inline] #[inline]
fn is_set_high(&mut self) -> Result<bool, Self::Error> { fn is_set_high(&mut self) -> Result<bool, Self::Error> {
self.is_high_mut() self._is_high()
} }
#[inline] #[inline]
fn is_set_low(&mut self) -> Result<bool, Self::Error> { fn is_set_low(&mut self) -> Result<bool, Self::Error> {
self.is_low_mut() self._is_low()
}
#[inline]
fn toggle(&mut self) -> Result<(), Self::Error> {
self.toggle()
} }
} }

View File

@ -22,47 +22,14 @@
//! //!
//! - [Blinky example](https://egit.irs.uni-stuttgart.de/rust/va108xx-rs/src/branch/main/examples/simple/examples/blinky.rs) //! - [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)] #[derive(Debug, PartialEq, Eq, thiserror::Error)]
#[cfg_attr(feature = "defmt", derive(defmt::Format))] #[cfg_attr(feature = "defmt", derive(defmt::Format))]
#[error("The pin is masked")] #[error("The pin is masked")]
pub struct IsMaskedError; pub struct IsMaskedError;
#[derive(Debug, PartialEq, Eq, Clone, Copy)] pub const NUM_PINS_PORT_A: usize = 32;
#[cfg_attr(feature = "defmt", derive(defmt::Format))] pub const NUM_PINS_PORT_B: usize = 24;
pub enum Port { pub const NUM_GPIO_PINS: usize = NUM_PINS_PORT_A + NUM_PINS_PORT_B;
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 mod dynpin;
pub use dynpin::*; pub use dynpin::*;
@ -72,3 +39,5 @@ pub use pin::*;
pub mod asynch; pub mod asynch;
pub use asynch::*; pub use asynch::*;
mod reg;

View File

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

375
va108xx-hal/src/gpio/reg.rs Normal file
View File

@ -0,0 +1,375 @@
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,6 +36,8 @@ pub struct InvalidTimingParamsError;
#[derive(Debug, PartialEq, Eq, thiserror::Error)] #[derive(Debug, PartialEq, Eq, thiserror::Error)]
#[cfg_attr(feature = "defmt", derive(defmt::Format))] #[cfg_attr(feature = "defmt", derive(defmt::Format))]
pub enum Error { pub enum Error {
//#[error("Invalid timing parameters")]
//InvalidTimingParams,
#[error("arbitration lost")] #[error("arbitration lost")]
ArbitrationLost, ArbitrationLost,
#[error("nack address")] #[error("nack address")]
@ -80,7 +82,6 @@ impl embedded_hal::i2c::Error for Error {
} }
#[derive(Debug, PartialEq, Copy, Clone)] #[derive(Debug, PartialEq, Copy, Clone)]
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
enum I2cCmd { enum I2cCmd {
Start = 0b00, Start = 0b00,
Stop = 0b10, Stop = 0b10,
@ -251,8 +252,6 @@ impl Default for MasterConfig {
impl Sealed for MasterConfig {} impl Sealed for MasterConfig {}
#[derive(Debug)]
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
pub struct SlaveConfig { pub struct SlaveConfig {
pub tx_fe_mode: FifoEmptyMode, pub tx_fe_mode: FifoEmptyMode,
pub rx_fe_mode: FifoEmptyMode, pub rx_fe_mode: FifoEmptyMode,
@ -456,6 +455,13 @@ 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 // I2C Master
//================================================================================================== //==================================================================================================
@ -667,6 +673,275 @@ 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 // Embedded HAL I2C implementations
//====================================================================================== //======================================================================================

View File

@ -1,7 +1,6 @@
#![no_std] #![no_std]
#![cfg_attr(docsrs, feature(doc_auto_cfg))] #![cfg_attr(docsrs, feature(doc_auto_cfg))]
use gpio::Port;
pub use va108xx; pub use va108xx;
pub use va108xx as pac; pub use va108xx as pac;
@ -20,7 +19,6 @@ pub mod uart;
#[derive(Debug, Eq, Copy, Clone, PartialEq)] #[derive(Debug, Eq, Copy, Clone, PartialEq)]
#[cfg_attr(feature = "defmt", derive(defmt::Format))] #[cfg_attr(feature = "defmt", derive(defmt::Format))]
pub enum FunSel { pub enum FunSel {
Sel0 = 0b00,
Sel1 = 0b01, Sel1 = 0b01,
Sel2 = 0b10, Sel2 = 0b10,
Sel3 = 0b11, Sel3 = 0b11,
@ -28,6 +26,13 @@ pub enum FunSel {
#[derive(Debug, Copy, Clone, PartialEq, Eq)] #[derive(Debug, Copy, Clone, PartialEq, Eq)]
#[cfg_attr(feature = "defmt", derive(defmt::Format))] #[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 { pub enum PeripheralSelect {
PortA = 0, PortA = 0,
PortB = 1, PortB = 1,
@ -49,7 +54,6 @@ pub enum PeripheralSelect {
/// Cortex-M0 NVIC. Both are generally necessary for IRQs to work, but the user might want to /// Cortex-M0 NVIC. Both are generally necessary for IRQs to work, but the user might want to
/// perform those steps themselves. /// perform those steps themselves.
#[derive(Debug, PartialEq, Eq, Clone, Copy)] #[derive(Debug, PartialEq, Eq, Clone, Copy)]
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
pub struct InterruptConfig { pub struct InterruptConfig {
/// Interrupt target vector. Should always be set, might be required for disabling IRQs /// Interrupt target vector. Should always be set, might be required for disabling IRQs
pub id: pac::Interrupt, pub id: pac::Interrupt,
@ -72,33 +76,37 @@ impl InterruptConfig {
pub type IrqCfg = InterruptConfig; pub type IrqCfg = InterruptConfig;
#[derive(Debug, PartialEq, Eq, thiserror::Error)] #[derive(Debug, PartialEq, Eq)]
#[cfg_attr(feature = "defmt", derive(defmt::Format))] #[cfg_attr(feature = "defmt", derive(defmt::Format))]
#[error("invalid pin with number {0}")] pub struct InvalidPin(pub(crate) ());
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( pub fn port_function_select(
ioconfig: &mut pac::Ioconfig, ioconfig: &mut pac::Ioconfig,
port: Port, port: PortSel,
pin: u8, pin: u8,
funsel: FunSel, funsel: FunSel,
) -> Result<(), InvalidPinError> { ) -> Result<(), InvalidPin> {
if (port == Port::A && pin >= 32) || (port == Port::B && pin >= 24) { match port {
return Err(InvalidPinError(pin)); 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(())
}
} }
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. /// Enable a specific interrupt using the NVIC peripheral.

View File

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

View File

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

View File

@ -1,16 +1,18 @@
//! # Async UART reception functionality for the VA416xx family. //! # Async UART reception functionality for the VA108xx family.
//! //!
//! This module provides the [RxAsync] and [RxAsyncOverwriting] struct which both implement the //! This module provides the [RxAsync] and [RxAsyncSharedConsumer] struct which both implement the
//! [embedded_io_async::Read] trait. //! [embedded_io_async::Read] trait.
//! This trait allows for asynchronous reception of data streams. Please note that this module does //! 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. //! not specify/declare the interrupt handlers which must be provided for async support to work.
//! However, it provides two interrupt handlers: //! However, it provides four interrupt handlers:
//! //!
//! - [on_interrupt_rx] //! - [on_interrupt_uart_a]
//! - [on_interrupt_rx_overwriting] //! - [on_interrupt_uart_b]
//! - [on_interrupt_uart_a_overwriting]
//! - [on_interrupt_uart_b_overwriting]
//! //!
//! The first two are used for the [RxAsync] struct, while the latter two are used with the //! The first two are used for the [RxAsync] struct, while the latter two are used with the
//! [RxAsyncOverwriting] struct. The later two will overwrite old values in the used ring buffer. //! [RxAsyncSharedConsumer] struct. The later two will overwrite old values in the used ring buffer.
//! //!
//! Error handling is performed in the user interrupt handler by checking the [AsyncUartErrors] //! Error handling is performed in the user interrupt handler by checking the [AsyncUartErrors]
//! structure returned by the interrupt handlers. //! structure returned by the interrupt handlers.
@ -23,10 +25,11 @@ use core::{cell::RefCell, convert::Infallible, future::Future, sync::atomic::Ord
use critical_section::Mutex; use critical_section::Mutex;
use embassy_sync::waitqueue::AtomicWaker; use embassy_sync::waitqueue::AtomicWaker;
use embedded_io::ErrorType; use embedded_io::ErrorType;
use heapless::spsc::Consumer;
use portable_atomic::AtomicBool; use portable_atomic::AtomicBool;
use va108xx::uarta as uart_base; use va108xx as pac;
use super::{Bank, Instance, Rx, RxError, UartErrors}; use super::{Instance, Rx, RxError, UartErrors};
static UART_RX_WAKERS: [AtomicWaker; 2] = [const { AtomicWaker::new() }; 2]; static UART_RX_WAKERS: [AtomicWaker; 2] = [const { AtomicWaker::new() }; 2];
static RX_READ_ACTIVE: [AtomicBool; 2] = [const { AtomicBool::new(false) }; 2]; static RX_READ_ACTIVE: [AtomicBool; 2] = [const { AtomicBool::new(false) }; 2];
@ -69,7 +72,7 @@ pub struct AsyncUartErrors {
pub uart_errors: UartErrors, pub uart_errors: UartErrors,
} }
fn on_interrupt_handle_rx_errors(uart: &'static uart_base::RegisterBlock) -> Option<UartErrors> { fn on_interrupt_handle_rx_errors<Uart: Instance>(uart: &Uart) -> Option<UartErrors> {
let rx_status = uart.rxstatus().read(); let rx_status = uart.rxstatus().read();
if rx_status.rxovr().bit_is_set() if rx_status.rxovr().bit_is_set()
|| rx_status.rxfrm().bit_is_set() || rx_status.rxfrm().bit_is_set()
@ -91,65 +94,81 @@ fn on_interrupt_handle_rx_errors(uart: &'static uart_base::RegisterBlock) -> Opt
None None
} }
fn on_interrupt_rx_common_post_processing( fn on_interrupt_rx_common_post_processing<Uart: Instance>(
bank: Bank, uart: &Uart,
rx_enabled: bool, rx_enabled: bool,
read_some_data: bool, read_some_data: bool,
irq_end: u32, irq_end: u32,
) -> Option<UartErrors> { ) -> Option<UartErrors> {
let idx = bank as usize;
if read_some_data { if read_some_data {
RX_HAS_DATA[idx].store(true, Ordering::Relaxed); RX_HAS_DATA[Uart::IDX as usize].store(true, Ordering::Relaxed);
if RX_READ_ACTIVE[idx].load(Ordering::Relaxed) { if RX_READ_ACTIVE[Uart::IDX as usize].load(Ordering::Relaxed) {
UART_RX_WAKERS[idx].wake(); UART_RX_WAKERS[Uart::IDX as usize].wake();
} }
} }
let mut errors = None; let mut errors = None;
let uart_regs = unsafe { bank.reg_block() };
// Check for RX errors // Check for RX errors
if rx_enabled { if rx_enabled {
errors = on_interrupt_handle_rx_errors(uart_regs); errors = on_interrupt_handle_rx_errors(uart);
} }
// Clear the interrupt status bits // Clear the interrupt status bits
uart_regs.irq_clr().write(|w| unsafe { w.bits(irq_end) }); uart.irq_clr().write(|w| unsafe { w.bits(irq_end) });
errors errors
} }
/// Interrupt handler with overwriting behaviour when the ring buffer is full. /// Interrupt handler for UART A.
/// ///
/// Should be called in the user interrupt handler to enable /// Should be called in the user interrupt handler to enable
/// asynchronous reception. This variant will overwrite old data in the ring buffer in case /// asynchronous reception. This variant will overwrite old data in the ring buffer in case
/// the ring buffer is full. /// the ring buffer is full.
pub fn on_interrupt_rx_overwriting<const N: usize>( pub fn on_interrupt_uart_a_overwriting<const N: usize>(
bank: Bank,
prod: &mut heapless::spsc::Producer<u8, N>, prod: &mut heapless::spsc::Producer<u8, N>,
shared_consumer: &Mutex<RefCell<Option<heapless::spsc::Consumer<'static, u8, N>>>>, shared_consumer: &Mutex<RefCell<Option<heapless::spsc::Consumer<'static, u8, N>>>>,
) -> Result<(), AsyncUartErrors> { ) -> Result<(), AsyncUartErrors> {
on_interrupt_rx_async_heapless_queue_overwriting(bank, prod, shared_consumer) on_interrupt_rx_async_heapless_queue_overwriting(
unsafe { pac::Uarta::steal() },
prod,
shared_consumer,
)
} }
pub fn on_interrupt_rx_async_heapless_queue_overwriting<const N: usize>( /// Interrupt handler for UART B.
bank: Bank, ///
/// Should be called in the user interrupt handler to enable
/// asynchronous reception. This variant will overwrite old data in the ring buffer in case
/// the ring buffer is full.
pub fn on_interrupt_uart_b_overwriting<const N: usize>(
prod: &mut heapless::spsc::Producer<u8, N>, prod: &mut heapless::spsc::Producer<u8, N>,
shared_consumer: &Mutex<RefCell<Option<heapless::spsc::Consumer<'static, u8, N>>>>, shared_consumer: &Mutex<RefCell<Option<heapless::spsc::Consumer<'static, u8, N>>>>,
) -> Result<(), AsyncUartErrors> { ) -> Result<(), AsyncUartErrors> {
let uart_regs = unsafe { bank.reg_block() }; on_interrupt_rx_async_heapless_queue_overwriting(
let irq_end = uart_regs.irq_end().read(); unsafe { pac::Uartb::steal() },
let enb_status = uart_regs.enable().read(); prod,
shared_consumer,
)
}
pub fn on_interrupt_rx_async_heapless_queue_overwriting<Uart: Instance, const N: usize>(
uart: Uart,
prod: &mut heapless::spsc::Producer<u8, N>,
shared_consumer: &Mutex<RefCell<Option<heapless::spsc::Consumer<'static, u8, N>>>>,
) -> Result<(), AsyncUartErrors> {
let irq_end = uart.irq_end().read();
let enb_status = uart.enable().read();
let rx_enabled = enb_status.rxenable().bit_is_set(); let rx_enabled = enb_status.rxenable().bit_is_set();
let mut read_some_data = false; let mut read_some_data = false;
let mut queue_overflow = false; let mut queue_overflow = false;
// Half-Full interrupt. We have a guaranteed amount of data we can read. // Half-Full interrupt. We have a guaranteed amount of data we can read.
if irq_end.irq_rx().bit_is_set() { if irq_end.irq_rx().bit_is_set() {
let available_bytes = uart_regs.rxfifoirqtrg().read().bits() as usize; let available_bytes = uart.rxfifoirqtrg().read().bits() as usize;
// If this interrupt bit is set, the trigger level is available at the very least. // If this interrupt bit is set, the trigger level is available at the very least.
// Read everything as fast as possible // Read everything as fast as possible
for _ in 0..available_bytes { for _ in 0..available_bytes {
let byte = uart_regs.data().read().bits(); let byte = uart.data().read().bits();
if !prod.ready() { if !prod.ready() {
queue_overflow = true; queue_overflow = true;
critical_section::with(|cs| { critical_section::with(|cs| {
@ -164,9 +183,9 @@ pub fn on_interrupt_rx_async_heapless_queue_overwriting<const N: usize>(
// Timeout, empty the FIFO completely. // Timeout, empty the FIFO completely.
if irq_end.irq_rx_to().bit_is_set() { if irq_end.irq_rx_to().bit_is_set() {
while uart_regs.rxstatus().read().rdavl().bit_is_set() { while uart.rxstatus().read().rdavl().bit_is_set() {
// While there is data in the FIFO, write it into the reception buffer // While there is data in the FIFO, write it into the reception buffer
let byte = uart_regs.data().read().bits(); let byte = uart.data().read().bits();
if !prod.ready() { if !prod.ready() {
queue_overflow = true; queue_overflow = true;
critical_section::with(|cs| { critical_section::with(|cs| {
@ -180,7 +199,7 @@ pub fn on_interrupt_rx_async_heapless_queue_overwriting<const N: usize>(
} }
let uart_errors = let uart_errors =
on_interrupt_rx_common_post_processing(bank, rx_enabled, read_some_data, irq_end.bits()); on_interrupt_rx_common_post_processing(&uart, rx_enabled, read_some_data, irq_end.bits());
if uart_errors.is_some() || queue_overflow { if uart_errors.is_some() || queue_overflow {
return Err(AsyncUartErrors { return Err(AsyncUartErrors {
queue_overflow, queue_overflow,
@ -190,21 +209,29 @@ pub fn on_interrupt_rx_async_heapless_queue_overwriting<const N: usize>(
Ok(()) Ok(())
} }
/// Interrupt handler for asynchronous RX operations. /// Interrupt handler for UART A.
/// ///
/// Should be called in the user interrupt handler to enable asynchronous reception. /// Should be called in the user interrupt handler to enable asynchronous reception.
pub fn on_interrupt_rx<const N: usize>( pub fn on_interrupt_uart_a<const N: usize>(
bank: Bank,
prod: &mut heapless::spsc::Producer<'_, u8, N>, prod: &mut heapless::spsc::Producer<'_, u8, N>,
) -> Result<(), AsyncUartErrors> { ) -> Result<(), AsyncUartErrors> {
on_interrupt_rx_async_heapless_queue(bank, prod) on_interrupt_rx_async_heapless_queue(unsafe { pac::Uarta::steal() }, prod)
} }
pub fn on_interrupt_rx_async_heapless_queue<const N: usize>( /// Interrupt handler for UART B.
bank: Bank, ///
/// Should be called in the user interrupt handler to enable asynchronous reception.
pub fn on_interrupt_uart_b<const N: usize>(
prod: &mut heapless::spsc::Producer<'_, u8, N>, prod: &mut heapless::spsc::Producer<'_, u8, N>,
) -> Result<(), AsyncUartErrors> { ) -> Result<(), AsyncUartErrors> {
let uart = unsafe { bank.reg_block() }; on_interrupt_rx_async_heapless_queue(unsafe { pac::Uartb::steal() }, prod)
}
pub fn on_interrupt_rx_async_heapless_queue<Uart: Instance, const N: usize>(
uart: Uart,
prod: &mut heapless::spsc::Producer<'_, u8, N>,
) -> Result<(), AsyncUartErrors> {
//let uart = unsafe { Uart::steal() };
let irq_end = uart.irq_end().read(); let irq_end = uart.irq_end().read();
let enb_status = uart.enable().read(); let enb_status = uart.enable().read();
let rx_enabled = enb_status.rxenable().bit_is_set(); let rx_enabled = enb_status.rxenable().bit_is_set();
@ -241,7 +268,7 @@ pub fn on_interrupt_rx_async_heapless_queue<const N: usize>(
} }
let uart_errors = let uart_errors =
on_interrupt_rx_common_post_processing(bank, rx_enabled, read_some_data, irq_end.bits()); on_interrupt_rx_common_post_processing(&uart, rx_enabled, read_some_data, irq_end.bits());
if uart_errors.is_some() || queue_overflow { if uart_errors.is_some() || queue_overflow {
return Err(AsyncUartErrors { return Err(AsyncUartErrors {
queue_overflow, queue_overflow,
@ -259,32 +286,24 @@ impl Drop for ActiveReadGuard {
} }
} }
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. /// Core data structure to allow asynchronous UART reception.
/// ///
/// If the ring buffer becomes full, data will be lost. /// If the ring buffer becomes full, data will be lost.
pub struct RxAsync<Uart: Instance, const N: usize>(Option<RxAsyncInner<Uart, N>>); pub struct RxAsync<Uart: Instance, const N: usize> {
rx: Rx<Uart>,
pub queue: heapless::spsc::Consumer<'static, u8, N>,
}
impl<Uart: Instance, const N: usize> ErrorType for RxAsync<Uart, N> { impl<Uart: Instance, const N: usize> ErrorType for RxAsync<Uart, N> {
/// Error reporting is done using the result of the interrupt functions. /// Error reporting is done using the result of the interrupt functions.
type Error = Infallible; 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> { impl<Uart: Instance, const N: usize> RxAsync<Uart, N> {
/// Create a new asynchronous receiver. /// Create a new asynchronous receiver.
/// ///
/// The passed [heapless::spsc::Consumer] will be used to asynchronously receive data which /// The passed [heapless::spsc::Consumer] will be used to asynchronously receive data which
/// is filled by the interrupt handler [on_interrupt_rx]. /// is filled by the interrupt handler.
pub fn new(mut rx: Rx<Uart>, queue: heapless::spsc::Consumer<'static, u8, N>) -> Self { pub fn new(mut rx: Rx<Uart>, queue: heapless::spsc::Consumer<'static, u8, N>) -> Self {
rx.disable_interrupts(); rx.disable_interrupts();
rx.disable(); rx.disable();
@ -294,23 +313,7 @@ impl<Uart: Instance, const N: usize> RxAsync<Uart, N> {
rx.enable_interrupts(); rx.enable_interrupts();
rx.enable(); rx.enable();
}); });
Self(Some(RxAsyncInner { rx, queue })) Self { 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();
} }
} }
@ -318,7 +321,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> { 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 // Need to wait for the IRQ to read data and set this flag. If the queue is not
// empty, we can read data immediately. // empty, we can read data immediately.
if self.0.as_ref().unwrap().queue.len() == 0 { if self.queue.len() == 0 {
RX_HAS_DATA[Uart::IDX as usize].store(false, Ordering::Relaxed); RX_HAS_DATA[Uart::IDX as usize].store(false, Ordering::Relaxed);
} }
let _guard = ActiveReadGuard(Uart::IDX as usize); let _guard = ActiveReadGuard(Uart::IDX as usize);
@ -330,38 +333,33 @@ impl<Uart: Instance, const N: usize> embedded_io_async::Read for RxAsync<Uart, N
} }
data_to_read data_to_read
}; };
let mut_ref = self.0.as_mut().unwrap(); let fut = RxFuture::new(&mut self.rx);
let fut = RxFuture::new(&mut mut_ref.rx);
// Data is available, so read that data immediately. // Data is available, so read that data immediately.
let read_data = handle_data_in_queue(&mut mut_ref.queue); let read_data = handle_data_in_queue(&mut self.queue);
if read_data > 0 { if read_data > 0 {
return Ok(read_data); return Ok(read_data);
} }
// Await data. // Await data.
let _ = fut.await; let _ = fut.await;
Ok(handle_data_in_queue(&mut mut_ref.queue)) Ok(handle_data_in_queue(&mut self.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. /// Core data structure to allow asynchronous UART reception.
/// ///
/// If the ring buffer becomes full, the oldest data will be overwritten when using the /// If the ring buffer becomes full, the oldest data will be overwritten when using the
/// [on_interrupt_rx_overwriting] interrupt handlers. /// [on_interrupt_uart_a_overwriting] and [on_interrupt_uart_b_overwriting] interrupt handlers.
pub struct RxAsyncOverwriting<Uart: Instance, const N: usize>( pub struct RxAsyncSharedConsumer<Uart: Instance, const N: usize> {
Option<RxAsyncOverwritingInner<Uart, N>>, rx: Rx<Uart>,
); queue: &'static Mutex<RefCell<Option<Consumer<'static, u8, N>>>>,
}
impl<Uart: Instance, const N: usize> ErrorType for RxAsyncOverwriting<Uart, N> { impl<Uart: Instance, const N: usize> ErrorType for RxAsyncSharedConsumer<Uart, N> {
/// Error reporting is done using the result of the interrupt functions. /// Error reporting is done using the result of the interrupt functions.
type Error = Infallible; type Error = Infallible;
} }
impl<Uart: Instance, const N: usize> RxAsyncOverwriting<Uart, N> { impl<Uart: Instance, const N: usize> RxAsyncSharedConsumer<Uart, N> {
/// Create a new asynchronous receiver. /// Create a new asynchronous receiver.
/// ///
/// The passed shared [heapless::spsc::Consumer] will be used to asynchronously receive data /// The passed shared [heapless::spsc::Consumer] will be used to asynchronously receive data
@ -369,7 +367,7 @@ impl<Uart: Instance, const N: usize> RxAsyncOverwriting<Uart, N> {
/// interrupt handler to overwrite old data. /// interrupt handler to overwrite old data.
pub fn new( pub fn new(
mut rx: Rx<Uart>, mut rx: Rx<Uart>,
shared_consumer: &'static Mutex<RefCell<Option<heapless::spsc::Consumer<'static, u8, N>>>>, queue: &'static Mutex<RefCell<Option<heapless::spsc::Consumer<'static, u8, N>>>>,
) -> Self { ) -> Self {
rx.disable_interrupts(); rx.disable_interrupts();
rx.disable(); rx.disable();
@ -379,44 +377,25 @@ impl<Uart: Instance, const N: usize> RxAsyncOverwriting<Uart, N> {
rx.enable_interrupts(); rx.enable_interrupts();
rx.enable(); rx.enable();
}); });
Self(Some(RxAsyncOverwritingInner { Self { rx, queue }
rx,
shared_consumer,
}))
}
pub fn stop(&mut self) {
stop_async_rx(&mut self.0.as_mut().unwrap().rx);
}
pub fn release(mut self) -> Rx<Uart> {
self.stop();
let inner = self.0.take().unwrap();
inner.rx
} }
} }
impl<Uart: Instance, const N: usize> Drop for RxAsyncOverwriting<Uart, N> { impl<Uart: Instance, const N: usize> embedded_io_async::Read for RxAsyncSharedConsumer<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> { 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 // Need to wait for the IRQ to read data and set this flag. If the queue is not
// empty, we can read data immediately. // empty, we can read data immediately.
critical_section::with(|cs| { critical_section::with(|cs| {
let queue = self.0.as_ref().unwrap().shared_consumer.borrow(cs); let queue = self.queue.borrow(cs);
if queue.borrow().as_ref().unwrap().len() == 0 { if queue.borrow().as_ref().unwrap().len() == 0 {
RX_HAS_DATA[Uart::IDX as usize].store(false, Ordering::Relaxed); RX_HAS_DATA[Uart::IDX as usize].store(false, Ordering::Relaxed);
} }
}); });
let _guard = ActiveReadGuard(Uart::IDX as usize); let _guard = ActiveReadGuard(Uart::IDX as usize);
let mut handle_data_in_queue = |inner: &mut RxAsyncOverwritingInner<Uart, N>| { let mut handle_data_in_queue = || {
critical_section::with(|cs| { critical_section::with(|cs| {
let mut consumer_ref = inner.shared_consumer.borrow(cs).borrow_mut(); let mut consumer_ref = self.queue.borrow(cs).borrow_mut();
let consumer = consumer_ref.as_mut().unwrap(); let consumer = consumer_ref.as_mut().unwrap();
let data_to_read = consumer.len().min(buf.len()); let data_to_read = consumer.len().min(buf.len());
for byte in buf.iter_mut().take(data_to_read) { for byte in buf.iter_mut().take(data_to_read) {
@ -426,15 +405,15 @@ impl<Uart: Instance, const N: usize> embedded_io_async::Read for RxAsyncOverwrit
data_to_read data_to_read
}) })
}; };
let fut = RxFuture::new(&mut self.0.as_mut().unwrap().rx); let fut = RxFuture::new(&mut self.rx);
// Data is available, so read that data immediately. // Data is available, so read that data immediately.
let read_data = handle_data_in_queue(self.0.as_mut().unwrap()); let read_data = handle_data_in_queue();
if read_data > 0 { if read_data > 0 {
return Ok(read_data); return Ok(read_data);
} }
// Await data. // Await data.
let _ = fut.await; let _ = fut.await;
let read_data = handle_data_in_queue(self.0.as_mut().unwrap()); let read_data = handle_data_in_queue();
Ok(read_data) Ok(read_data)
} }
} }

View File

@ -3,10 +3,13 @@
//! This module provides the [TxAsync] struct which implements the [embedded_io_async::Write] trait. //! 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 //! 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. //! not specify/declare the interrupt handlers which must be provided for async support to work.
//! However, it the [on_interrupt_tx] interrupt handler. //! However, it provides two interrupt handlers:
//! //!
//! This handler should be called in ALL user interrupt handlers which handle UART TX interrupts //! - [on_interrupt_uart_a_tx]
//! for a given UART bank. //! - [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.
//! //!
//! # Example //! # Example
//! //!
@ -27,14 +30,21 @@ static TX_CONTEXTS: [Mutex<RefCell<TxContext>>; 2] =
// critical section. // critical section.
static TX_DONE: [AtomicBool; 2] = [const { AtomicBool::new(false) }; 2]; static TX_DONE: [AtomicBool; 2] = [const { AtomicBool::new(false) }; 2];
/// This is a generic interrupt handler to handle asynchronous UART TX operations for a given /// This is a generic interrupt handler to handle asynchronous UART TX operations. The user
/// UART bank. /// has to call this once in the interrupt handler responsible for UART A TX interrupts for
/// /// asynchronous operations to work.
/// The user has to call this once in the interrupt handler responsible for the TX interrupts on pub fn on_interrupt_uart_a_tx() {
/// the given UART bank. on_interrupt_uart_tx(unsafe { pac::Uarta::steal() });
pub fn on_interrupt_tx(bank: Bank) { }
let uart = unsafe { bank.reg_block() };
let idx = bank as usize; /// 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) {
let irq_enb = uart.irq_enb().read(); let irq_enb = uart.irq_enb().read();
// IRQ is not related to TX. // IRQ is not related to TX.
if irq_enb.irq_tx().bit_is_clear() || irq_enb.irq_tx_empty().bit_is_clear() { if irq_enb.irq_tx().bit_is_clear() || irq_enb.irq_tx_empty().bit_is_clear() {
@ -44,7 +54,7 @@ pub fn on_interrupt_tx(bank: Bank) {
let tx_status = uart.txstatus().read(); let tx_status = uart.txstatus().read();
let unexpected_overrun = tx_status.wrlost().bit_is_set(); let unexpected_overrun = tx_status.wrlost().bit_is_set();
let mut context = critical_section::with(|cs| { let mut context = critical_section::with(|cs| {
let context_ref = TX_CONTEXTS[idx].borrow(cs); let context_ref = TX_CONTEXTS[Uart::IDX as usize].borrow(cs);
*context_ref.borrow() *context_ref.borrow()
}); });
context.tx_overrun = unexpected_overrun; context.tx_overrun = unexpected_overrun;
@ -57,12 +67,12 @@ pub fn on_interrupt_tx(bank: Bank) {
uart.enable().modify(|_, w| w.txenable().clear_bit()); uart.enable().modify(|_, w| w.txenable().clear_bit());
// Write back updated context structure. // Write back updated context structure.
critical_section::with(|cs| { critical_section::with(|cs| {
let context_ref = TX_CONTEXTS[idx].borrow(cs); let context_ref = TX_CONTEXTS[Uart::IDX as usize].borrow(cs);
*context_ref.borrow_mut() = context; *context_ref.borrow_mut() = context;
}); });
// Transfer is done. // Transfer is done.
TX_DONE[idx].store(true, core::sync::atomic::Ordering::Relaxed); TX_DONE[Uart::IDX as usize].store(true, core::sync::atomic::Ordering::Relaxed);
UART_TX_WAKERS[idx].wake(); UART_TX_WAKERS[Uart::IDX as usize].wake();
return; return;
} }
// Safety: We documented that the user provided slice must outlive the future, so we convert // Safety: We documented that the user provided slice must outlive the future, so we convert
@ -82,7 +92,7 @@ pub fn on_interrupt_tx(bank: Bank) {
// Write back updated context structure. // Write back updated context structure.
critical_section::with(|cs| { critical_section::with(|cs| {
let context_ref = TX_CONTEXTS[idx].borrow(cs); let context_ref = TX_CONTEXTS[Uart::IDX as usize].borrow(cs);
*context_ref.borrow_mut() = context; *context_ref.borrow_mut() = context;
}); });
} }

View File

@ -8,10 +8,6 @@ and this project adheres to [Semantic Versioning](http://semver.org/).
## [unreleased] ## [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 ## [v0.4.0] 2025-02-12
- Re-generated PAC with `svd2rust` v0.35.0 - Re-generated PAC with `svd2rust` v0.35.0

View File

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

View File

@ -26,12 +26,6 @@ The `rt` feature is optional and recommended. It brings in support for `cortex-m
For full details on the autgenerated API, please see the For full details on the autgenerated API, please see the
[svd2rust documentation](https://docs.rs/svd2rust/latest/svd2rust/#peripheral-api). [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 ## Regenerating the PAC
If you want to re-generate the PAC, for example if the register file `va416xx.svd` changes 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 svdtools patch svd/va108xx-patch.yml
# See https://github.com/rust-embedded/svd2rust/issues/830 for required re-export. # See https://github.com/rust-embedded/svd2rust/issues/830 for required re-export.
${svd2rust_bin} --reexport-interrupt --impl-defmt defmt --impl-debug-feature debug -i svd/va108xx.svd.patched ${svd2rust_bin} --reexport-interrupt -i svd/va108xx.svd.patched
result=$? result=$?
if [ $result -ne 0 ]; then if [ $result -ne 0 ]; then

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

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