Compare commits
37 Commits
vorago-reb
...
main
Author | SHA1 | Date | |
---|---|---|---|
c693530ab7 | |||
45ee5ad726 | |||
16591346e5 | |||
9ccd147ff6 | |||
521c07460a | |||
5bf7793e6b | |||
75e6d98e44 | |||
80eea170ef | |||
f9d1233d3f | |||
41f7f9d25b | |||
47e754433d | |||
e5e010a276 | |||
caf54e5a70 | |||
31b25b0211 | |||
8b55d0923f | |||
a65f4039ee | |||
97da2a0752 | |||
c43d1f8861 | |||
181c2bdc7b | |||
2bca96b5db | |||
bf41b59a24 | |||
6cbba8414c | |||
fe04a3e7cd | |||
e25fb20b08 | |||
67af1bb9b5 | |||
1a83f932b5 | |||
cdc4807686 | |||
62a4123f82 | |||
17f13fc4dc | |||
b4f1512463 | |||
e9ec01fc60 | |||
d57cd383cd | |||
df4e943f48 | |||
93b67a4795 | |||
a9f2e6dcee | |||
271c853df1 | |||
107189b166 |
@ -4,10 +4,9 @@
|
||||
# runner = "arm-none-eabi-gdb -q -x openocd.gdb"
|
||||
# runner = "gdb-multiarch -q -x openocd.gdb"
|
||||
# runner = "gdb -q -x openocd.gdb"
|
||||
runner = "gdb-multiarch -q -x jlink.gdb"
|
||||
# runner = "gdb-multiarch -q -x jlink.gdb"
|
||||
|
||||
# Probe-rs is currently problematic: https://github.com/probe-rs/probe-rs/issues/2567
|
||||
# runner = "probe-rs run --chip VA108xx --chip-description-path ./scripts/VA108xx_Series.yaml"
|
||||
runner = "probe-rs run --chip VA108xx_RAM --protocol jtag"
|
||||
# runner = ["probe-rs", "run", "--chip", "$CHIP", "--log-format", "{L} {s}"]
|
||||
|
||||
rustflags = [
|
||||
|
6
.github/workflows/ci.yml
vendored
6
.github/workflows/ci.yml
vendored
@ -12,6 +12,8 @@ jobs:
|
||||
targets: "thumbv6m-none-eabi"
|
||||
- run: cargo check --target thumbv6m-none-eabi
|
||||
- run: cargo check --target thumbv6m-none-eabi --examples
|
||||
- run: cargo check -p va108xx --target thumbv6m-none-eabi --all-features
|
||||
- run: cargo check -p va108xx-hal --target thumbv6m-none-eabi --features "defmt"
|
||||
|
||||
test:
|
||||
name: Run Tests
|
||||
@ -39,8 +41,8 @@ jobs:
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- uses: dtolnay/rust-toolchain@nightly
|
||||
- run: RUSTDOCFLAGS="--cfg docsrs --generate-link-to-definition -Z unstable-options" cargo +nightly doc -p va108xx
|
||||
- run: RUSTDOCFLAGS="--cfg docsrs --generate-link-to-definition -Z unstable-options" cargo +nightly doc -p va108xx-hal
|
||||
- run: RUSTDOCFLAGS="--cfg docsrs --generate-link-to-definition -Z unstable-options" cargo +nightly doc -p va108xx --all-features
|
||||
- run: RUSTDOCFLAGS="--cfg docsrs --generate-link-to-definition -Z unstable-options" cargo +nightly doc -p va108xx-hal --all-features
|
||||
- run: RUSTDOCFLAGS="--cfg docsrs --generate-link-to-definition -Z unstable-options" cargo +nightly doc -p vorago-reb1
|
||||
|
||||
clippy:
|
||||
|
75
README.md
75
README.md
@ -14,6 +14,8 @@ This workspace contains the following released crates:
|
||||
crate containing basic low-level register definition.
|
||||
- The [`va108xx-hal`](https://egit.irs.uni-stuttgart.de/rust/va108xx-rs/src/branch/main/va108xx-hal)
|
||||
HAL crate containing higher-level abstractions on top of the PAC register crate.
|
||||
- The [`va108xx-embassy`](https://egit.irs.uni-stuttgart.de/rust/va108xx-rs/src/branch/main/va108xx-embassy)
|
||||
crate containing support for running the embassy-rs RTOS.
|
||||
- The [`vorago-reb1`](https://egit.irs.uni-stuttgart.de/rust/va108xx-rs/src/branch/main/vorago-reb1)
|
||||
BSP crate containing support for the REB1 development board.
|
||||
|
||||
@ -58,14 +60,56 @@ You can then adapt the files in `.vscode` to your needs.
|
||||
You can use CLI or VS Code for flashing, running and debugging. In any case, take
|
||||
care of installing the pre-requisites first.
|
||||
|
||||
### Pre-Requisites
|
||||
### Using CLI with probe-rs
|
||||
|
||||
Install [probe-rs](https://probe.rs/docs/getting-started/installation/) first.
|
||||
|
||||
You can use `probe-rs` to run the software and display RTT log output. However, debugging does not
|
||||
work yet.
|
||||
|
||||
After installation, you can run the following command
|
||||
|
||||
```sh
|
||||
probe-rs run --chip VA108xx_RAM --protocol jtag target/thumbv6m-none-eabi/debug/examples/blinky
|
||||
```
|
||||
|
||||
to flash and run the blinky program on the RAM. There is also a `VA108xx` chip target
|
||||
available for persistent flashing.
|
||||
|
||||
Runner configuration avilable in the `.cargo/def-config.toml` file to use `probe-rs` for
|
||||
convenience.
|
||||
|
||||
### Using VS Code
|
||||
|
||||
Assuming a working debug connection to your VA108xx board, you can debug using VS Code with
|
||||
the [`Cortex-Debug` plugin](https://marketplace.visualstudio.com/items?itemName=marus25.cortex-debug).
|
||||
Please make sure that [`objdump-multiarch` and `nm-multiarch`](https://forums.raspberrypi.com/viewtopic.php?t=333146)
|
||||
are installed as well.
|
||||
|
||||
Some sample configuration files for VS code were provided and can be used by running
|
||||
`cp -rT vscode .vscode` like specified above. After that, you can use `Run and Debug`
|
||||
to automatically rebuild and flash your application.
|
||||
|
||||
If you would like to use a custom GDB application, you can specify the gdb binary in the following
|
||||
configuration variables in your `settings.json`:
|
||||
|
||||
- `"cortex-debug.gdbPath"`
|
||||
- `"cortex-debug.gdbPath.linux"`
|
||||
- `"cortex-debug.gdbPath.windows"`
|
||||
- `"cortex-debug.gdbPath.osx"`
|
||||
|
||||
The provided VS Code configurations also provide an integrated RTT logger, which you can access
|
||||
via the terminal at `RTT Ch:0 console`. In order for the RTT block address detection to
|
||||
work properly, `objdump-multiarch` and `nm-multiarch` need to be installed.
|
||||
|
||||
### Using CLI with GDB and Segger J-Link Tools
|
||||
|
||||
Install the following two tools first:
|
||||
|
||||
1. [SEGGER J-Link tools](https://www.segger.com/downloads/jlink/) installed
|
||||
2. [gdb-multiarch](https://packages.debian.org/sid/gdb-multiarch) or similar
|
||||
cross-architecture debugger installed. All commands here assume `gdb-multiarch`.
|
||||
|
||||
### Using CLI
|
||||
|
||||
You can build the blinky example application with the following command
|
||||
|
||||
```sh
|
||||
@ -99,25 +143,8 @@ runner = "gdb-multiarch -q -x jlink/jlink.gdb"
|
||||
After that, you can simply use `cargo run --example blinky` to flash the blinky
|
||||
example.
|
||||
|
||||
### Using VS Code
|
||||
### Using the RTT Viewer
|
||||
|
||||
Assuming a working debug connection to your VA108xx board, you can debug using VS Code with
|
||||
the [`Cortex-Debug` plugin](https://marketplace.visualstudio.com/items?itemName=marus25.cortex-debug).
|
||||
Please make sure that [`objdump-multiarch` and `nm-multiarch`](https://forums.raspberrypi.com/viewtopic.php?t=333146)
|
||||
are installed as well.
|
||||
|
||||
Some sample configuration files for VS code were provided and can be used by running
|
||||
`cp -rT vscode .vscode` like specified above. After that, you can use `Run and Debug`
|
||||
to automatically rebuild and flash your application.
|
||||
|
||||
If you would like to use a custom GDB application, you can specify the gdb binary in the following
|
||||
configuration variables in your `settings.json`:
|
||||
|
||||
- `"cortex-debug.gdbPath"`
|
||||
- `"cortex-debug.gdbPath.linux"`
|
||||
- `"cortex-debug.gdbPath.windows"`
|
||||
- `"cortex-debug.gdbPath.osx"`
|
||||
|
||||
The provided VS Code configurations also provide an integrated RTT logger, which you can access
|
||||
via the terminal at `RTT Ch:0 console`. In order for the RTT block address detection to
|
||||
work properly, `objdump-multiarch` and `nm-multiarch` need to be installed.
|
||||
The Segger RTT viewer can be used to display log messages received from the target. The base
|
||||
address for the RTT block placement is 0x10000000. It is recommended to use a search range of
|
||||
0x1000 around that base address when using the RTT viewer.
|
||||
|
@ -14,6 +14,6 @@ embedded-hal-nb = "1"
|
||||
embedded-io = "0.6"
|
||||
|
||||
[dependencies.va108xx-hal]
|
||||
path = "../va108xx-hal"
|
||||
version = "0.10.0"
|
||||
features = ["rt"]
|
||||
|
||||
|
@ -6,10 +6,7 @@
|
||||
#![no_std]
|
||||
|
||||
use cortex_m_rt::entry;
|
||||
use embedded_hal::{
|
||||
delay::DelayNs,
|
||||
digital::{InputPin, OutputPin, StatefulOutputPin},
|
||||
};
|
||||
use embedded_hal::delay::DelayNs;
|
||||
use panic_rtt_target as _;
|
||||
use rtt_target::{rprintln, rtt_init_print};
|
||||
use va108xx_hal::{
|
||||
@ -67,35 +64,35 @@ fn main() -> ! {
|
||||
TestCase::TestBasic => {
|
||||
// Tie PORTA[0] to PORTA[1] for these tests!
|
||||
let mut out = pinsa.pa0.into_readable_push_pull_output();
|
||||
let mut input = pinsa.pa1.into_floating_input();
|
||||
out.set_high().unwrap();
|
||||
assert!(input.is_high().unwrap());
|
||||
out.set_low().unwrap();
|
||||
assert!(input.is_low().unwrap());
|
||||
let input = pinsa.pa1.into_floating_input();
|
||||
out.set_high();
|
||||
assert!(input.is_high());
|
||||
out.set_low();
|
||||
assert!(input.is_low());
|
||||
}
|
||||
TestCase::TestPullup => {
|
||||
// Tie PORTA[0] to PORTA[1] for these tests!
|
||||
let mut input = pinsa.pa1.into_pull_up_input();
|
||||
assert!(input.is_high().unwrap());
|
||||
let input = pinsa.pa1.into_pull_up_input();
|
||||
assert!(input.is_high());
|
||||
let mut out = pinsa.pa0.into_readable_push_pull_output();
|
||||
out.set_low().unwrap();
|
||||
assert!(input.is_low().unwrap());
|
||||
out.set_high().unwrap();
|
||||
assert!(input.is_high().unwrap());
|
||||
out.set_low();
|
||||
assert!(input.is_low());
|
||||
out.set_high();
|
||||
assert!(input.is_high());
|
||||
out.into_floating_input();
|
||||
assert!(input.is_high().unwrap());
|
||||
assert!(input.is_high());
|
||||
}
|
||||
TestCase::TestPulldown => {
|
||||
// Tie PORTA[0] to PORTA[1] for these tests!
|
||||
let mut input = pinsa.pa1.into_pull_down_input();
|
||||
assert!(input.is_low().unwrap());
|
||||
let input = pinsa.pa1.into_pull_down_input();
|
||||
assert!(input.is_low());
|
||||
let mut out = pinsa.pa0.into_push_pull_output();
|
||||
out.set_low().unwrap();
|
||||
assert!(input.is_low().unwrap());
|
||||
out.set_high().unwrap();
|
||||
assert!(input.is_high().unwrap());
|
||||
out.set_low();
|
||||
assert!(input.is_low());
|
||||
out.set_high();
|
||||
assert!(input.is_high());
|
||||
out.into_floating_input();
|
||||
assert!(input.is_low().unwrap());
|
||||
assert!(input.is_low());
|
||||
}
|
||||
TestCase::TestMask => {
|
||||
// Tie PORTA[0] to PORTA[1] for these tests!
|
||||
@ -110,11 +107,11 @@ fn main() -> ! {
|
||||
TestCase::PortB => {
|
||||
// Tie PORTB[22] to PORTB[23] for these tests!
|
||||
let mut out = pinsb.pb22.into_readable_push_pull_output();
|
||||
let mut input = pinsb.pb23.into_floating_input();
|
||||
out.set_high().unwrap();
|
||||
assert!(input.is_high().unwrap());
|
||||
out.set_low().unwrap();
|
||||
assert!(input.is_low().unwrap());
|
||||
let input = pinsb.pb23.into_floating_input();
|
||||
out.set_high();
|
||||
assert!(input.is_high());
|
||||
out.set_low();
|
||||
assert!(input.is_low());
|
||||
}
|
||||
TestCase::Perid => {
|
||||
assert_eq!(PinsA::get_perid(), 0x004007e1);
|
||||
@ -122,34 +119,31 @@ fn main() -> ! {
|
||||
}
|
||||
TestCase::Pulse => {
|
||||
let mut output_pulsed = pinsa.pa0.into_push_pull_output();
|
||||
output_pulsed.pulse_mode(true, PinState::Low);
|
||||
output_pulsed.configure_pulse_mode(true, PinState::Low);
|
||||
rprintln!("Pulsing high 10 times..");
|
||||
output_pulsed.set_low().unwrap();
|
||||
output_pulsed.set_low();
|
||||
for _ in 0..10 {
|
||||
output_pulsed.set_high().unwrap();
|
||||
output_pulsed.set_high();
|
||||
cortex_m::asm::delay(25_000_000);
|
||||
}
|
||||
output_pulsed.pulse_mode(true, PinState::High);
|
||||
output_pulsed.configure_pulse_mode(true, PinState::High);
|
||||
rprintln!("Pulsing low 10 times..");
|
||||
for _ in 0..10 {
|
||||
output_pulsed.set_low().unwrap();
|
||||
output_pulsed.set_low();
|
||||
cortex_m::asm::delay(25_000_000);
|
||||
}
|
||||
}
|
||||
TestCase::DelayGpio => {
|
||||
let mut out_0 = pinsa
|
||||
.pa0
|
||||
.into_readable_push_pull_output()
|
||||
.delay(true, false);
|
||||
let mut out_1 = pinsa
|
||||
.pa1
|
||||
.into_readable_push_pull_output()
|
||||
.delay(false, true);
|
||||
let mut out_2 = pinsa.pa3.into_readable_push_pull_output().delay(true, true);
|
||||
let mut out_0 = pinsa.pa0.into_readable_push_pull_output();
|
||||
out_0.configure_delay(true, false);
|
||||
let mut out_1 = pinsa.pa1.into_readable_push_pull_output();
|
||||
out_1.configure_delay(false, true);
|
||||
let mut out_2 = pinsa.pa3.into_readable_push_pull_output();
|
||||
out_2.configure_delay(true, true);
|
||||
for _ in 0..20 {
|
||||
out_0.toggle().unwrap();
|
||||
out_1.toggle().unwrap();
|
||||
out_2.toggle().unwrap();
|
||||
out_0.toggle();
|
||||
out_1.toggle();
|
||||
out_2.toggle();
|
||||
cortex_m::asm::delay(25_000_000);
|
||||
}
|
||||
}
|
||||
@ -162,18 +156,18 @@ fn main() -> ! {
|
||||
dp.tim0,
|
||||
);
|
||||
for _ in 0..5 {
|
||||
led1.toggle().ok();
|
||||
led1.toggle();
|
||||
ms_timer.delay_ms(500);
|
||||
led1.toggle().ok();
|
||||
led1.toggle();
|
||||
ms_timer.delay_ms(500);
|
||||
}
|
||||
|
||||
let mut delay_timer = CountdownTimer::new(&mut dp.sysconfig, 50.MHz(), dp.tim1);
|
||||
let mut pa0 = pinsa.pa0.into_readable_push_pull_output();
|
||||
for _ in 0..5 {
|
||||
led1.toggle().ok();
|
||||
led1.toggle();
|
||||
delay_timer.delay_ms(500);
|
||||
led1.toggle().ok();
|
||||
led1.toggle();
|
||||
delay_timer.delay_ms(500);
|
||||
}
|
||||
let ahb_freq: Hertz = 50.MHz();
|
||||
@ -181,13 +175,13 @@ fn main() -> ! {
|
||||
// Test usecond delay using both TIM peripheral and SYST. Use the release image if you
|
||||
// want to verify the timings!
|
||||
loop {
|
||||
pa0.toggle().ok();
|
||||
pa0.toggle();
|
||||
delay_timer.delay_us(50);
|
||||
pa0.toggle().ok();
|
||||
pa0.toggle();
|
||||
delay_timer.delay_us(50);
|
||||
pa0.toggle_with_toggle_reg();
|
||||
pa0.toggle();
|
||||
syst_delay.delay_us(50);
|
||||
pa0.toggle_with_toggle_reg();
|
||||
pa0.toggle();
|
||||
syst_delay.delay_us(50);
|
||||
}
|
||||
}
|
||||
@ -195,7 +189,7 @@ fn main() -> ! {
|
||||
|
||||
rprintln!("Test success");
|
||||
loop {
|
||||
led1.toggle().ok();
|
||||
led1.toggle();
|
||||
cortex_m::asm::delay(25_000_000);
|
||||
}
|
||||
}
|
||||
|
@ -15,10 +15,10 @@ num_enum = { version = "0.7", default-features = false }
|
||||
static_assertions = "1"
|
||||
|
||||
[dependencies.va108xx-hal]
|
||||
path = "../va108xx-hal"
|
||||
version = "0.10"
|
||||
|
||||
[dependencies.vorago-reb1]
|
||||
path = "../vorago-reb1"
|
||||
version = "0.8"
|
||||
|
||||
[features]
|
||||
default = []
|
||||
|
@ -27,10 +27,8 @@ version = "1"
|
||||
features = ["cortex-m-systick"]
|
||||
|
||||
[dependencies.va108xx-hal]
|
||||
version = "0.6"
|
||||
path = "../va108xx-hal"
|
||||
version = "0.10"
|
||||
features = ["rt", "defmt"]
|
||||
|
||||
[dependencies.va108xx]
|
||||
version = "0.3"
|
||||
path = "../va108xx"
|
||||
version = "0.5"
|
||||
|
@ -27,8 +27,8 @@ embassy-executor = { version = "0.7", features = [
|
||||
"executor-interrupt"
|
||||
]}
|
||||
|
||||
va108xx-hal = "0.9"
|
||||
va108xx-embassy = { path = "../../va108xx-embassy", default-features = false }
|
||||
va108xx-hal = { version = "0.10" }
|
||||
va108xx-embassy = { version = "0.2" }
|
||||
|
||||
[features]
|
||||
default = ["ticks-hz-1_000", "va108xx-embassy/irq-oc30-oc31"]
|
||||
|
@ -9,12 +9,12 @@ use embassy_executor::Spawner;
|
||||
use embassy_sync::channel::{Receiver, Sender};
|
||||
use embassy_sync::{blocking_mutex::raw::ThreadModeRawMutex, channel::Channel};
|
||||
use embassy_time::{Duration, Instant, Timer};
|
||||
use embedded_hal::digital::{InputPin, OutputPin, StatefulOutputPin};
|
||||
use embedded_hal_async::digital::Wait;
|
||||
use panic_rtt_target as _;
|
||||
use rtt_target::{rprintln, rtt_init_print};
|
||||
use va108xx_embassy::embassy;
|
||||
use va108xx_hal::gpio::{on_interrupt_for_asynch_gpio, InputDynPinAsync, InputPinAsync, PinsB};
|
||||
use va108xx_hal::gpio::{
|
||||
on_interrupt_for_async_gpio_for_port, InputDynPinAsync, InputPinAsync, PinsB, Port,
|
||||
};
|
||||
use va108xx_hal::{
|
||||
gpio::{DynPin, PinsA},
|
||||
pac::{self, interrupt},
|
||||
@ -64,15 +64,13 @@ async fn main(spawner: Spawner) {
|
||||
let mut dp = pac::Peripherals::take().unwrap();
|
||||
|
||||
// Safety: Only called once here.
|
||||
unsafe {
|
||||
embassy::init(
|
||||
&mut dp.sysconfig,
|
||||
&dp.irqsel,
|
||||
SYSCLK_FREQ,
|
||||
dp.tim23,
|
||||
dp.tim22,
|
||||
)
|
||||
};
|
||||
va108xx_embassy::init(
|
||||
&mut dp.sysconfig,
|
||||
&dp.irqsel,
|
||||
SYSCLK_FREQ,
|
||||
dp.tim23,
|
||||
dp.tim22,
|
||||
);
|
||||
|
||||
let porta = PinsA::new(&mut dp.sysconfig, dp.porta);
|
||||
let portb = PinsB::new(&mut dp.sysconfig, dp.portb);
|
||||
@ -114,7 +112,7 @@ async fn main(spawner: Spawner) {
|
||||
|
||||
rprintln!("Example done, toggling LED0");
|
||||
loop {
|
||||
led0.toggle().unwrap();
|
||||
led0.toggle();
|
||||
Timer::after(Duration::from_millis(500)).await;
|
||||
}
|
||||
}
|
||||
@ -244,15 +242,16 @@ async fn output_task(
|
||||
}
|
||||
|
||||
// PB22 to PB23 can be handled by both OC10 and OC11 depending on configuration.
|
||||
|
||||
#[interrupt]
|
||||
#[allow(non_snake_case)]
|
||||
fn OC10() {
|
||||
on_interrupt_for_asynch_gpio();
|
||||
on_interrupt_for_async_gpio_for_port(Port::A);
|
||||
on_interrupt_for_async_gpio_for_port(Port::B);
|
||||
}
|
||||
|
||||
// This interrupt only handles PORT B interrupts.
|
||||
#[interrupt]
|
||||
#[allow(non_snake_case)]
|
||||
fn OC11() {
|
||||
on_interrupt_for_asynch_gpio();
|
||||
on_interrupt_for_async_gpio_for_port(Port::B);
|
||||
}
|
||||
|
@ -18,21 +18,19 @@ use core::cell::RefCell;
|
||||
use critical_section::Mutex;
|
||||
use embassy_executor::Spawner;
|
||||
use embassy_time::Instant;
|
||||
use embedded_hal::digital::StatefulOutputPin;
|
||||
use embedded_io::Write;
|
||||
use embedded_io_async::Read;
|
||||
use heapless::spsc::{Consumer, Producer, Queue};
|
||||
use panic_rtt_target as _;
|
||||
use rtt_target::{rprintln, rtt_init_print};
|
||||
use va108xx_embassy::embassy;
|
||||
use va108xx_hal::{
|
||||
gpio::PinsA,
|
||||
pac::{self, interrupt},
|
||||
prelude::*,
|
||||
uart::{
|
||||
self, on_interrupt_uart_b_overwriting,
|
||||
rx_asynch::{on_interrupt_uart_a, RxAsync},
|
||||
RxAsyncSharedConsumer, Tx,
|
||||
self, on_interrupt_rx_overwriting,
|
||||
rx_asynch::{on_interrupt_rx, RxAsync},
|
||||
Bank, RxAsyncOverwriting, Tx,
|
||||
},
|
||||
InterruptConfig,
|
||||
};
|
||||
@ -57,15 +55,13 @@ async fn main(spawner: Spawner) {
|
||||
let mut dp = pac::Peripherals::take().unwrap();
|
||||
|
||||
// Safety: Only called once here.
|
||||
unsafe {
|
||||
embassy::init(
|
||||
&mut dp.sysconfig,
|
||||
&dp.irqsel,
|
||||
SYSCLK_FREQ,
|
||||
dp.tim23,
|
||||
dp.tim22,
|
||||
);
|
||||
}
|
||||
va108xx_embassy::init(
|
||||
&mut dp.sysconfig,
|
||||
&dp.irqsel,
|
||||
SYSCLK_FREQ,
|
||||
dp.tim23,
|
||||
dp.tim22,
|
||||
);
|
||||
|
||||
let porta = PinsA::new(&mut dp.sysconfig, dp.porta);
|
||||
let mut led0 = porta.pa10.into_readable_push_pull_output();
|
||||
@ -106,16 +102,16 @@ async fn main(spawner: Spawner) {
|
||||
*CONSUMER_UART_B.borrow(cs).borrow_mut() = Some(cons_uart_b);
|
||||
});
|
||||
let mut async_rx_uart_a = RxAsync::new(rx_uart_a, cons_uart_a);
|
||||
let async_rx_uart_b = RxAsyncSharedConsumer::new(rx_uart_b, &CONSUMER_UART_B);
|
||||
let async_rx_uart_b = RxAsyncOverwriting::new(rx_uart_b, &CONSUMER_UART_B);
|
||||
spawner
|
||||
.spawn(uart_b_task(async_rx_uart_b, tx_uart_b))
|
||||
.unwrap();
|
||||
let mut buf = [0u8; 256];
|
||||
loop {
|
||||
rprintln!("Current time UART A: {}", Instant::now().as_secs());
|
||||
led0.toggle().ok();
|
||||
led1.toggle().ok();
|
||||
led2.toggle().ok();
|
||||
led0.toggle();
|
||||
led1.toggle();
|
||||
led2.toggle();
|
||||
let read_bytes = async_rx_uart_a.read(&mut buf).await.unwrap();
|
||||
let read_str = core::str::from_utf8(&buf[..read_bytes]).unwrap();
|
||||
rprintln!(
|
||||
@ -128,7 +124,7 @@ async fn main(spawner: Spawner) {
|
||||
}
|
||||
|
||||
#[embassy_executor::task]
|
||||
async fn uart_b_task(mut async_rx: RxAsyncSharedConsumer<pac::Uartb, 256>, mut tx: Tx<pac::Uartb>) {
|
||||
async fn uart_b_task(mut async_rx: RxAsyncOverwriting<pac::Uartb, 256>, mut tx: Tx<pac::Uartb>) {
|
||||
let mut buf = [0u8; 256];
|
||||
loop {
|
||||
rprintln!("Current time UART B: {}", Instant::now().as_secs());
|
||||
@ -149,7 +145,7 @@ async fn uart_b_task(mut async_rx: RxAsyncSharedConsumer<pac::Uartb, 256>, mut t
|
||||
fn OC2() {
|
||||
let mut prod =
|
||||
critical_section::with(|cs| PRODUCER_UART_A.borrow(cs).borrow_mut().take().unwrap());
|
||||
let errors = on_interrupt_uart_a(&mut prod);
|
||||
let errors = on_interrupt_rx(Bank::A, &mut prod);
|
||||
critical_section::with(|cs| *PRODUCER_UART_A.borrow(cs).borrow_mut() = Some(prod));
|
||||
// In a production app, we could use a channel to send the errors to the main task.
|
||||
if let Err(errors) = errors {
|
||||
@ -162,7 +158,7 @@ fn OC2() {
|
||||
fn OC3() {
|
||||
let mut prod =
|
||||
critical_section::with(|cs| PRODUCER_UART_B.borrow(cs).borrow_mut().take().unwrap());
|
||||
let errors = on_interrupt_uart_b_overwriting(&mut prod, &CONSUMER_UART_B);
|
||||
let errors = on_interrupt_rx_overwriting(Bank::B, &mut prod, &CONSUMER_UART_B);
|
||||
critical_section::with(|cs| *PRODUCER_UART_B.borrow(cs).borrow_mut() = Some(prod));
|
||||
// In a production app, we could use a channel to send the errors to the main task.
|
||||
if let Err(errors) = errors {
|
||||
|
@ -12,16 +12,14 @@
|
||||
#![no_main]
|
||||
use embassy_executor::Spawner;
|
||||
use embassy_time::{Duration, Instant, Ticker};
|
||||
use embedded_hal::digital::StatefulOutputPin;
|
||||
use embedded_io_async::Write;
|
||||
use panic_rtt_target as _;
|
||||
use rtt_target::{rprintln, rtt_init_print};
|
||||
use va108xx_embassy::embassy;
|
||||
use va108xx_hal::{
|
||||
gpio::PinsA,
|
||||
pac::{self, interrupt},
|
||||
prelude::*,
|
||||
uart::{self, on_interrupt_uart_a_tx, TxAsync},
|
||||
uart::{self, on_interrupt_tx, Bank, TxAsync},
|
||||
InterruptConfig,
|
||||
};
|
||||
|
||||
@ -43,15 +41,13 @@ async fn main(_spawner: Spawner) {
|
||||
let mut dp = pac::Peripherals::take().unwrap();
|
||||
|
||||
// Safety: Only called once here.
|
||||
unsafe {
|
||||
embassy::init(
|
||||
&mut dp.sysconfig,
|
||||
&dp.irqsel,
|
||||
SYSCLK_FREQ,
|
||||
dp.tim23,
|
||||
dp.tim22,
|
||||
);
|
||||
}
|
||||
va108xx_embassy::init(
|
||||
&mut dp.sysconfig,
|
||||
&dp.irqsel,
|
||||
SYSCLK_FREQ,
|
||||
dp.tim23,
|
||||
dp.tim22,
|
||||
);
|
||||
|
||||
let porta = PinsA::new(&mut dp.sysconfig, dp.porta);
|
||||
let mut led0 = porta.pa10.into_readable_push_pull_output();
|
||||
@ -75,9 +71,9 @@ async fn main(_spawner: Spawner) {
|
||||
let mut idx = 0;
|
||||
loop {
|
||||
rprintln!("Current time: {}", Instant::now().as_secs());
|
||||
led0.toggle().ok();
|
||||
led1.toggle().ok();
|
||||
led2.toggle().ok();
|
||||
led0.toggle();
|
||||
led1.toggle();
|
||||
led2.toggle();
|
||||
let _written = async_tx
|
||||
.write(STR_LIST[idx].as_bytes())
|
||||
.await
|
||||
@ -93,5 +89,5 @@ async fn main(_spawner: Spawner) {
|
||||
#[interrupt]
|
||||
#[allow(non_snake_case)]
|
||||
fn OC2() {
|
||||
on_interrupt_uart_a_tx();
|
||||
on_interrupt_tx(Bank::A);
|
||||
}
|
||||
|
@ -2,10 +2,8 @@
|
||||
#![no_main]
|
||||
use embassy_executor::Spawner;
|
||||
use embassy_time::{Duration, Instant, Ticker};
|
||||
use embedded_hal::digital::StatefulOutputPin;
|
||||
use panic_rtt_target as _;
|
||||
use rtt_target::{rprintln, rtt_init_print};
|
||||
use va108xx_embassy::embassy;
|
||||
|
||||
cfg_if::cfg_if! {
|
||||
if #[cfg(feature = "custom-irqs")] {
|
||||
@ -28,27 +26,25 @@ async fn main(_spawner: Spawner) {
|
||||
let mut dp = pac::Peripherals::take().unwrap();
|
||||
|
||||
// Safety: Only called once here.
|
||||
unsafe {
|
||||
cfg_if::cfg_if! {
|
||||
if #[cfg(not(feature = "custom-irqs"))] {
|
||||
embassy::init(
|
||||
&mut dp.sysconfig,
|
||||
&dp.irqsel,
|
||||
SYSCLK_FREQ,
|
||||
dp.tim23,
|
||||
dp.tim22,
|
||||
);
|
||||
} else {
|
||||
embassy::init_with_custom_irqs(
|
||||
&mut dp.sysconfig,
|
||||
&dp.irqsel,
|
||||
SYSCLK_FREQ,
|
||||
dp.tim23,
|
||||
dp.tim22,
|
||||
pac::Interrupt::OC23,
|
||||
pac::Interrupt::OC24,
|
||||
);
|
||||
}
|
||||
cfg_if::cfg_if! {
|
||||
if #[cfg(not(feature = "custom-irqs"))] {
|
||||
va108xx_embassy::init(
|
||||
&mut dp.sysconfig,
|
||||
&dp.irqsel,
|
||||
SYSCLK_FREQ,
|
||||
dp.tim23,
|
||||
dp.tim22,
|
||||
);
|
||||
} else {
|
||||
va108xx_embassy::init_with_custom_irqs(
|
||||
&mut dp.sysconfig,
|
||||
&dp.irqsel,
|
||||
SYSCLK_FREQ,
|
||||
dp.tim23,
|
||||
dp.tim22,
|
||||
pac::Interrupt::OC23,
|
||||
pac::Interrupt::OC24,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@ -60,8 +56,8 @@ async fn main(_spawner: Spawner) {
|
||||
loop {
|
||||
ticker.next().await;
|
||||
rprintln!("Current time: {}", Instant::now().as_secs());
|
||||
led0.toggle().ok();
|
||||
led1.toggle().ok();
|
||||
led2.toggle().ok();
|
||||
led0.toggle();
|
||||
led1.toggle();
|
||||
led2.toggle();
|
||||
}
|
||||
}
|
||||
|
@ -22,5 +22,5 @@ rtic-sync = { version = "1.3", features = ["defmt-03"] }
|
||||
once_cell = {version = "1", default-features = false, features = ["critical-section"]}
|
||||
ringbuf = { version = "0.4.7", default-features = false, features = ["portable-atomic"] }
|
||||
|
||||
va108xx-hal = "0.9"
|
||||
vorago-reb1 = { path = "../../vorago-reb1" }
|
||||
va108xx-hal = { version = "0.10" }
|
||||
vorago-reb1 = { version = "0.8" }
|
||||
|
@ -69,18 +69,16 @@ mod app {
|
||||
|
||||
// Configure an edge interrupt on the button and route it to interrupt vector 15
|
||||
let mut button = Button::new(pinsa.pa11.into_floating_input());
|
||||
button.configure_edge_interrupt(
|
||||
edge_irq,
|
||||
InterruptConfig::new(pac::interrupt::OC15, true, true),
|
||||
Some(&mut dp.sysconfig),
|
||||
Some(&mut dp.irqsel),
|
||||
);
|
||||
|
||||
if mode == PressMode::Toggle {
|
||||
// This filter debounces the switch for edge based interrupts
|
||||
button.configure_filter_type(FilterType::FilterFourClockCycles, FilterClkSel::Clk1);
|
||||
set_clk_div_register(&mut dp.sysconfig, FilterClkSel::Clk1, 50_000);
|
||||
}
|
||||
button.configure_and_enable_edge_interrupt(
|
||||
edge_irq,
|
||||
InterruptConfig::new(pac::interrupt::OC15, true, true),
|
||||
);
|
||||
let mut leds = Leds::new(
|
||||
pinsa.pa10.into_push_pull_output(),
|
||||
pinsa.pa7.into_push_pull_output(),
|
||||
|
@ -5,7 +5,6 @@
|
||||
#[rtic::app(device = pac, dispatchers = [OC31, OC30, OC29])]
|
||||
mod app {
|
||||
use cortex_m::asm;
|
||||
use embedded_hal::digital::StatefulOutputPin;
|
||||
use panic_rtt_target as _;
|
||||
use rtic_example::SYSCLK_FREQ;
|
||||
use rtic_monotonics::systick::prelude::*;
|
||||
@ -58,9 +57,9 @@ mod app {
|
||||
async fn blinky(cx: blinky::Context) {
|
||||
loop {
|
||||
rprintln!("toggling LEDs");
|
||||
cx.local.led0.toggle().ok();
|
||||
cx.local.led1.toggle().ok();
|
||||
cx.local.led2.toggle().ok();
|
||||
cx.local.led0.toggle();
|
||||
cx.local.led1.toggle();
|
||||
cx.local.led2.toggle();
|
||||
Mono::delay(1000.millis()).await;
|
||||
}
|
||||
}
|
||||
|
@ -16,8 +16,8 @@ embedded-io = "0.6"
|
||||
cortex-m-semihosting = "0.5.0"
|
||||
|
||||
[dependencies.va108xx-hal]
|
||||
version = "0.9"
|
||||
version = "0.10"
|
||||
features = ["rt", "defmt"]
|
||||
|
||||
[dependencies.vorago-reb1]
|
||||
path = "../../vorago-reb1"
|
||||
version = "0.8"
|
||||
|
@ -7,10 +7,7 @@
|
||||
#![no_std]
|
||||
|
||||
use cortex_m_rt::entry;
|
||||
use embedded_hal::{
|
||||
delay::DelayNs,
|
||||
digital::{OutputPin, StatefulOutputPin},
|
||||
};
|
||||
use embedded_hal::delay::DelayNs;
|
||||
use panic_halt as _;
|
||||
use va108xx_hal::{
|
||||
gpio::PinsA,
|
||||
@ -38,21 +35,21 @@ fn main() -> ! {
|
||||
let mut led2 = porta.pa7.into_readable_push_pull_output();
|
||||
let mut led3 = porta.pa6.into_readable_push_pull_output();
|
||||
for _ in 0..10 {
|
||||
led1.set_low().ok();
|
||||
led2.set_low().ok();
|
||||
led3.set_low().ok();
|
||||
led1.set_low();
|
||||
led2.set_low();
|
||||
led3.set_low();
|
||||
delay_ms.delay_ms(200);
|
||||
led1.set_high().ok();
|
||||
led2.set_high().ok();
|
||||
led3.set_high().ok();
|
||||
led1.set_high();
|
||||
led2.set_high();
|
||||
led3.set_high();
|
||||
delay_tim1.delay_ms(200);
|
||||
}
|
||||
loop {
|
||||
led1.toggle().ok();
|
||||
led1.toggle();
|
||||
delay_ms.delay_ms(200);
|
||||
led2.toggle().ok();
|
||||
led2.toggle();
|
||||
delay_tim1.delay_ms(200);
|
||||
led3.toggle().ok();
|
||||
led3.toggle();
|
||||
delay_ms.delay_ms(200);
|
||||
}
|
||||
}
|
||||
|
@ -29,7 +29,7 @@ rtic-monotonics = { version = "2", features = ["cortex-m-systick"] }
|
||||
rtic-sync = {version = "1", features = ["defmt-03"]}
|
||||
|
||||
[dependencies.va108xx-hal]
|
||||
path = "../va108xx-hal"
|
||||
version = "0.10"
|
||||
|
||||
[dependencies.vorago-reb1]
|
||||
path = "../vorago-reb1"
|
||||
version = "0.8"
|
||||
|
@ -11,7 +11,7 @@ panic-rtt-target = { version = "0.1.3" }
|
||||
rtt-target = { version = "0.5" }
|
||||
cortex-m = { version = "0.7", features = ["critical-section-single-core"] }
|
||||
embedded-hal = "1"
|
||||
va108xx-hal = { path = "../../va108xx-hal" }
|
||||
va108xx-hal = { version = "0.10.0" }
|
||||
|
||||
[profile.dev]
|
||||
codegen-units = 1
|
||||
|
@ -11,7 +11,7 @@ panic-rtt-target = { version = "0.1.3" }
|
||||
rtt-target = { version = "0.5" }
|
||||
cortex-m = { version = "0.7", features = ["critical-section-single-core"] }
|
||||
embedded-hal = "1"
|
||||
va108xx-hal = { path = "../../va108xx-hal" }
|
||||
va108xx-hal = { version = "0.10.0" }
|
||||
|
||||
[profile.dev]
|
||||
codegen-units = 1
|
||||
|
23
va108xx-embassy/CHANGELOG.md
Normal file
23
va108xx-embassy/CHANGELOG.md
Normal file
@ -0,0 +1,23 @@
|
||||
Change Log
|
||||
=======
|
||||
|
||||
All notable changes to this project will be documented in this file.
|
||||
|
||||
The format is based on [Keep a Changelog](http://keepachangelog.com/)
|
||||
and this project adheres to [Semantic Versioning](http://semver.org/).
|
||||
|
||||
## [unreleased]
|
||||
|
||||
## [v0.2.0] 2025-02-17
|
||||
|
||||
- Bumped va108xx-hal to v0.10.0
|
||||
- Remove `embassy` module, expose public functions in library root directly
|
||||
|
||||
|
||||
## [v0.1.2] and [v0.1.1] 2025-02-13
|
||||
|
||||
Docs patch
|
||||
|
||||
## [v0.1.0] 2025-02-13
|
||||
|
||||
Initial release
|
@ -1,11 +1,17 @@
|
||||
[package]
|
||||
name = "va108xx-embassy"
|
||||
version = "0.1.0"
|
||||
version = "0.2.0"
|
||||
edition = "2021"
|
||||
authors = ["Robin Mueller <muellerr@irs.uni-stuttgart.de>"]
|
||||
description = "Embassy-rs support for the Vorago VA108xx family of microcontrollers"
|
||||
homepage = "https://egit.irs.uni-stuttgart.de/rust/va108xx-rs"
|
||||
repository = "https://egit.irs.uni-stuttgart.de/rust/va108xx-rs"
|
||||
license = "Apache-2.0"
|
||||
keywords = ["no-std", "hal", "cortex-m", "vorago", "va108xx"]
|
||||
categories = ["aerospace", "embedded", "no-std", "hardware-support"]
|
||||
|
||||
[dependencies]
|
||||
critical-section = "1"
|
||||
portable-atomic = { version = "1", features = ["unsafe-assume-single-core"]}
|
||||
|
||||
embassy-sync = "0.6"
|
||||
embassy-executor = "0.7"
|
||||
@ -14,8 +20,12 @@ embassy-time-queue-utils = "0.1"
|
||||
|
||||
once_cell = { version = "1", default-features = false, features = ["critical-section"] }
|
||||
|
||||
[dependencies.va108xx-hal]
|
||||
path = "../va108xx-hal"
|
||||
va108xx-hal = { version = "0.10" }
|
||||
|
||||
[target.'cfg(all(target_arch = "arm", target_os = "none"))'.dependencies]
|
||||
portable-atomic = { version = "1", features = ["unsafe-assume-single-core"] }
|
||||
[target.'cfg(not(all(target_arch = "arm", target_os = "none")))'.dependencies]
|
||||
portable-atomic = "1"
|
||||
|
||||
[features]
|
||||
default = ["irq-oc30-oc31"]
|
||||
@ -25,3 +35,6 @@ irqs-in-lib = []
|
||||
irq-oc28-oc29 = ["irqs-in-lib"]
|
||||
irq-oc29-oc30 = ["irqs-in-lib"]
|
||||
irq-oc30-oc31 = ["irqs-in-lib"]
|
||||
|
||||
[package.metadata.docs.rs]
|
||||
rustdoc-args = ["--generate-link-to-definition"]
|
||||
|
3
va108xx-embassy/docs.sh
Executable file
3
va108xx-embassy/docs.sh
Executable file
@ -0,0 +1,3 @@
|
||||
#!/bin/bash
|
||||
export RUSTDOCFLAGS="--cfg docsrs --generate-link-to-definition -Z unstable-options"
|
||||
cargo +nightly doc --open
|
@ -1,17 +1,17 @@
|
||||
//! # Embassy-rs support for the Vorago VA108xx MCU family
|
||||
//!
|
||||
//! This repository contains the [embassy-rs](https://github.com/embassy-rs/embassy) support for the
|
||||
//! VA108xx family. Currently, it contains the time driver to allow using embassy-rs. It uses the TIM
|
||||
//! peripherals provided by the VA108xx family for this purpose.
|
||||
//! This repository contains the [embassy-rs](https://github.com/embassy-rs/embassy) support for
|
||||
//! the VA108xx family. Currently, it contains the time driver to allow using embassy-rs. It uses
|
||||
//! the TIM peripherals provided by the VA108xx family for this purpose.
|
||||
//!
|
||||
//! ## Usage
|
||||
//!
|
||||
//! This library only exposes the [embassy::init] method which sets up the time driver. This
|
||||
//! function must be called once at the start of the application.
|
||||
//! This library exposes the [init] or the [init_with_custom_irqs] functions which set up the time
|
||||
//! driver. This function must be called once at the start of the application.
|
||||
//!
|
||||
//! This implementation requires two TIM peripherals provided by the VA108xx device.
|
||||
//! The user can freely specify the two used TIM peripheral by passing the concrete TIM instances
|
||||
//! into the [embassy::init_with_custom_irqs] and [embassy::init] method.
|
||||
//! into the [init_with_custom_irqs] and [init] method.
|
||||
//!
|
||||
//! The application also requires two interrupt handlers to handle the timekeeper and alarm
|
||||
//! interrupts. By default, this library will define the interrupt handler inside the library
|
||||
@ -23,17 +23,17 @@
|
||||
//!
|
||||
//! You can disable the default features and then specify one of the features above to use the
|
||||
//! documented combination of IRQs. It is also possible to specify custom IRQs by importing and
|
||||
//! using the [embassy::embassy_time_driver_irqs] macro to declare the IRQ handlers in the
|
||||
//! application code. If this is done, [embassy::init_with_custom_irqs] must be used
|
||||
//! 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
|
||||
//! method to pass the IRQ numbers to the library.
|
||||
//!
|
||||
//! ## Examples
|
||||
//!
|
||||
//! [embassy example project](https://egit.irs.uni-stuttgart.de/rust/va108xx-rs/src/branch/main/examples/embassy)
|
||||
//! [embassy example projects](https://egit.irs.uni-stuttgart.de/rust/va108xx-rs/src/branch/main/examples/embassy)
|
||||
#![no_std]
|
||||
#![cfg_attr(docsrs, feature(doc_auto_cfg))]
|
||||
use core::cell::{Cell, RefCell};
|
||||
use critical_section::CriticalSection;
|
||||
use embassy_sync::blocking_mutex::CriticalSectionMutex as Mutex;
|
||||
use critical_section::{CriticalSection, Mutex};
|
||||
use portable_atomic::{AtomicU32, Ordering};
|
||||
|
||||
use embassy_time_driver::{time_driver_impl, Driver, TICK_HZ};
|
||||
@ -45,7 +45,7 @@ use va108xx_hal::{
|
||||
clock::enable_peripheral_clock,
|
||||
enable_nvic_interrupt, pac,
|
||||
prelude::*,
|
||||
timer::{enable_tim_clk, get_tim_raw, TimRegInterface},
|
||||
timer::{enable_tim_clk, get_tim_raw, TimRegInterface, ValidTim},
|
||||
PeripheralSelect,
|
||||
};
|
||||
|
||||
@ -62,7 +62,7 @@ time_driver_impl!(
|
||||
/// the feature flags specified. However, the macro is exported to allow users to specify the
|
||||
/// interrupt handlers themselves.
|
||||
///
|
||||
/// Please note that you have to explicitely import the [va108xx_hal::pac::interrupt]
|
||||
/// Please note that you have to explicitely import the [macro@va108xx_hal::pac::interrupt]
|
||||
/// macro in the application code in case this macro is used there.
|
||||
#[macro_export]
|
||||
macro_rules! embassy_time_driver_irqs {
|
||||
@ -76,7 +76,7 @@ macro_rules! embassy_time_driver_irqs {
|
||||
#[allow(non_snake_case)]
|
||||
fn $timekeeper_irq() {
|
||||
// Safety: We call it once here.
|
||||
unsafe { $crate::embassy::time_driver().on_interrupt_timekeeping() }
|
||||
unsafe { $crate::time_driver().on_interrupt_timekeeping() }
|
||||
}
|
||||
|
||||
const ALARM_IRQ: pac::Interrupt = pac::Interrupt::$alarm_irq;
|
||||
@ -85,7 +85,7 @@ macro_rules! embassy_time_driver_irqs {
|
||||
#[allow(non_snake_case)]
|
||||
fn $alarm_irq() {
|
||||
// Safety: We call it once here.
|
||||
unsafe { $crate::embassy::time_driver().on_interrupt_alarm() }
|
||||
unsafe { $crate::time_driver().on_interrupt_alarm() }
|
||||
}
|
||||
};
|
||||
}
|
||||
@ -99,65 +99,58 @@ embassy_time_driver_irqs!(timekeeper_irq = OC30, alarm_irq = OC29);
|
||||
#[cfg(feature = "irq-oc28-oc29")]
|
||||
embassy_time_driver_irqs!(timekeeper_irq = OC29, alarm_irq = OC28);
|
||||
|
||||
pub mod embassy {
|
||||
use super::*;
|
||||
use va108xx_hal::{pac, timer::TimRegInterface};
|
||||
/// Expose the time driver so the user can specify the IRQ handlers themselves.
|
||||
pub fn time_driver() -> &'static TimerDriver {
|
||||
&TIME_DRIVER
|
||||
}
|
||||
|
||||
/// Expose the time driver so the user can specify the IRQ handlers themselves.
|
||||
pub fn time_driver() -> &'static TimerDriver {
|
||||
&TIME_DRIVER
|
||||
}
|
||||
/// Initialization method for embassy.
|
||||
///
|
||||
/// This should be used if the interrupt handler is provided by the library, which is the
|
||||
/// default case.
|
||||
#[cfg(feature = "irqs-in-lib")]
|
||||
pub fn init<TimekeeperTim: TimRegInterface + ValidTim, AlarmTim: TimRegInterface + ValidTim>(
|
||||
syscfg: &mut pac::Sysconfig,
|
||||
irqsel: &pac::Irqsel,
|
||||
sysclk: impl Into<Hertz>,
|
||||
timekeeper_tim: TimekeeperTim,
|
||||
alarm_tim: AlarmTim,
|
||||
) {
|
||||
TIME_DRIVER.init(
|
||||
syscfg,
|
||||
irqsel,
|
||||
sysclk,
|
||||
timekeeper_tim,
|
||||
alarm_tim,
|
||||
TIMEKEEPER_IRQ,
|
||||
ALARM_IRQ,
|
||||
)
|
||||
}
|
||||
|
||||
/// Initialization method for embassy
|
||||
///
|
||||
/// # Safety
|
||||
///
|
||||
/// This has to be called once at initialization time to initiate the time driver for
|
||||
/// embassy.
|
||||
#[cfg(feature = "irqs-in-lib")]
|
||||
pub unsafe fn init(
|
||||
syscfg: &mut pac::Sysconfig,
|
||||
irqsel: &pac::Irqsel,
|
||||
sysclk: impl Into<Hertz>,
|
||||
timekeeper_tim: impl TimRegInterface,
|
||||
alarm_tim: impl TimRegInterface,
|
||||
) {
|
||||
TIME_DRIVER.init(
|
||||
syscfg,
|
||||
irqsel,
|
||||
sysclk,
|
||||
timekeeper_tim,
|
||||
alarm_tim,
|
||||
TIMEKEEPER_IRQ,
|
||||
ALARM_IRQ,
|
||||
)
|
||||
}
|
||||
|
||||
/// Initialization method for embassy
|
||||
///
|
||||
/// # Safety
|
||||
///
|
||||
/// This has to be called once at initialization time to initiate the time driver for
|
||||
/// embassy.
|
||||
pub unsafe fn init_with_custom_irqs(
|
||||
syscfg: &mut pac::Sysconfig,
|
||||
irqsel: &pac::Irqsel,
|
||||
sysclk: impl Into<Hertz>,
|
||||
timekeeper_tim: impl TimRegInterface,
|
||||
alarm_tim: impl TimRegInterface,
|
||||
timekeeper_irq: pac::Interrupt,
|
||||
alarm_irq: pac::Interrupt,
|
||||
) {
|
||||
TIME_DRIVER.init(
|
||||
syscfg,
|
||||
irqsel,
|
||||
sysclk,
|
||||
timekeeper_tim,
|
||||
alarm_tim,
|
||||
timekeeper_irq,
|
||||
alarm_irq,
|
||||
)
|
||||
}
|
||||
/// Initialization method for embassy when using custom IRQ handlers.
|
||||
///
|
||||
/// Requires an explicit [pac::Interrupt] argument for the timekeeper and alarm IRQs.
|
||||
pub fn init_with_custom_irqs<
|
||||
TimekeeperTim: TimRegInterface + ValidTim,
|
||||
AlarmTim: TimRegInterface + ValidTim,
|
||||
>(
|
||||
syscfg: &mut pac::Sysconfig,
|
||||
irqsel: &pac::Irqsel,
|
||||
sysclk: impl Into<Hertz>,
|
||||
timekeeper_tim: TimekeeperTim,
|
||||
alarm_tim: AlarmTim,
|
||||
timekeeper_irq: pac::Interrupt,
|
||||
alarm_irq: pac::Interrupt,
|
||||
) {
|
||||
TIME_DRIVER.init(
|
||||
syscfg,
|
||||
irqsel,
|
||||
sysclk,
|
||||
timekeeper_tim,
|
||||
alarm_tim,
|
||||
timekeeper_irq,
|
||||
alarm_irq,
|
||||
)
|
||||
}
|
||||
|
||||
struct AlarmState {
|
||||
@ -187,21 +180,21 @@ pub struct TimerDriver {
|
||||
|
||||
impl TimerDriver {
|
||||
#[allow(clippy::too_many_arguments)]
|
||||
fn init(
|
||||
fn init<TimekeeperTim: TimRegInterface + ValidTim, AlarmTim: TimRegInterface + ValidTim>(
|
||||
&self,
|
||||
syscfg: &mut pac::Sysconfig,
|
||||
irqsel: &pac::Irqsel,
|
||||
sysclk: impl Into<Hertz>,
|
||||
timekeeper_tim: impl TimRegInterface,
|
||||
alarm_tim: impl TimRegInterface,
|
||||
timekeeper_tim: TimekeeperTim,
|
||||
alarm_tim: AlarmTim,
|
||||
timekeeper_irq: pac::Interrupt,
|
||||
alarm_irq: pac::Interrupt,
|
||||
) {
|
||||
if ALARM_TIM.get().is_some() {
|
||||
if ALARM_TIM.get().is_some() || TIMEKEEPER_TIM.get().is_some() {
|
||||
return;
|
||||
}
|
||||
ALARM_TIM.set(alarm_tim.tim_id()).ok();
|
||||
TIMEKEEPER_TIM.set(timekeeper_tim.tim_id()).ok();
|
||||
ALARM_TIM.set(AlarmTim::TIM_ID).ok();
|
||||
TIMEKEEPER_TIM.set(TimekeeperTim::TIM_ID).ok();
|
||||
enable_peripheral_clock(syscfg, PeripheralSelect::Irqsel);
|
||||
enable_tim_clk(syscfg, timekeeper_tim.tim_id());
|
||||
let timekeeper_reg_block = timekeeper_tim.reg_block();
|
||||
|
@ -8,7 +8,27 @@ and this project adheres to [Semantic Versioning](http://semver.org/).
|
||||
|
||||
## [unreleased]
|
||||
|
||||
## [v0.9.0]
|
||||
## [v0.10.0] 2025-02-17
|
||||
|
||||
## Added
|
||||
|
||||
- A lot of missing `defmt::Format` implementations.
|
||||
|
||||
## Changed
|
||||
|
||||
- Larger refactoring of GPIO library. The edge and level interrupt configurator functions do not
|
||||
enable interrupts anymore. Instead, there are explicit `enbable_interrupt` and
|
||||
`disable_interrupt` methods
|
||||
- Renamed GPIO `DynGroup` to `Port`
|
||||
- Rename generic GPIO interrupt handler into `on_interrupt_for_asynch_gpio`
|
||||
into `on_interrupt_for_async_gpio_for_port` which expects a Port argument
|
||||
|
||||
## Fixed
|
||||
|
||||
- Bug in async GPIO interrupt handler where all enabled interrupts, even the ones which might
|
||||
be unrelated to the pin, were disabled.
|
||||
|
||||
## [v0.9.0] 2025-02-13
|
||||
|
||||
## Fixed
|
||||
|
||||
|
@ -1,6 +1,6 @@
|
||||
[package]
|
||||
name = "va108xx-hal"
|
||||
version = "0.9.0"
|
||||
version = "0.10.0"
|
||||
authors = ["Robin Mueller <muellerr@irs.uni-stuttgart.de>"]
|
||||
edition = "2021"
|
||||
description = "HAL for the Vorago VA108xx family of microcontrollers"
|
||||
@ -28,8 +28,8 @@ heapless = "0.8"
|
||||
static_cell = "2"
|
||||
thiserror = { version = "2", default-features = false }
|
||||
void = { version = "1", default-features = false }
|
||||
once_cell = {version = "1", default-features = false }
|
||||
va108xx = { version = "0.4", default-features = false, features = ["critical-section"] }
|
||||
once_cell = { version = "1", default-features = false }
|
||||
va108xx = { version = "0.5", default-features = false, features = ["critical-section", "defmt"] }
|
||||
embassy-sync = "0.6"
|
||||
|
||||
defmt = { version = "0.3", optional = true }
|
||||
@ -42,7 +42,7 @@ portable-atomic = "1"
|
||||
[features]
|
||||
default = ["rt"]
|
||||
rt = ["va108xx/rt"]
|
||||
defmt = ["dep:defmt", "fugit/defmt"]
|
||||
defmt = ["dep:defmt", "fugit/defmt", "embedded-hal/defmt-03"]
|
||||
|
||||
[package.metadata.docs.rs]
|
||||
all-features = true
|
||||
|
@ -3,9 +3,9 @@
|
||||
//! This module provides the [InputPinAsync] and [InputDynPinAsync] which both implement
|
||||
//! the [embedded_hal_async::digital::Wait] trait. These types allow for asynchronous waiting
|
||||
//! on GPIO pins. Please note that this module does not specify/declare the interrupt handlers
|
||||
//! which must be provided for async support to work. However, it provides one generic
|
||||
//! [handler][on_interrupt_for_asynch_gpio] which should be called in ALL user interrupt handlers
|
||||
//! which handle GPIO interrupts.
|
||||
//! which must be provided for async support to work. However, it provides the
|
||||
//! [on_interrupt_for_async_gpio_for_port] generic interrupt handler. This should be called in all
|
||||
//! IRQ functions which handle any GPIO interrupts with the corresponding [Port] argument.
|
||||
//!
|
||||
//! # Example
|
||||
//!
|
||||
@ -13,68 +13,73 @@
|
||||
use core::future::Future;
|
||||
|
||||
use embassy_sync::waitqueue::AtomicWaker;
|
||||
use embedded_hal::digital::InputPin;
|
||||
use embedded_hal_async::digital::Wait;
|
||||
use portable_atomic::AtomicBool;
|
||||
use va108xx::{self as pac, Irqsel, Sysconfig};
|
||||
use va108xx::{self as pac};
|
||||
|
||||
use crate::InterruptConfig;
|
||||
|
||||
use super::{
|
||||
pin, DynGroup, DynPin, DynPinId, InputConfig, InterruptEdge, InvalidPinTypeError, Pin, PinId,
|
||||
NUM_GPIO_PINS, NUM_PINS_PORT_A,
|
||||
pin, DynPin, DynPinId, InputConfig, InterruptEdge, InvalidPinTypeError, Pin, PinId, Port,
|
||||
NUM_PINS_PORT_A, NUM_PINS_PORT_B,
|
||||
};
|
||||
|
||||
static WAKERS: [AtomicWaker; NUM_GPIO_PINS] = [const { AtomicWaker::new() }; NUM_GPIO_PINS];
|
||||
static EDGE_DETECTION: [AtomicBool; NUM_GPIO_PINS] =
|
||||
[const { AtomicBool::new(false) }; NUM_GPIO_PINS];
|
||||
static WAKERS_FOR_PORT_A: [AtomicWaker; NUM_PINS_PORT_A] =
|
||||
[const { AtomicWaker::new() }; NUM_PINS_PORT_A];
|
||||
static WAKERS_FOR_PORT_B: [AtomicWaker; NUM_PINS_PORT_B] =
|
||||
[const { AtomicWaker::new() }; NUM_PINS_PORT_B];
|
||||
static EDGE_DETECTION_PORT_A: [AtomicBool; NUM_PINS_PORT_A] =
|
||||
[const { AtomicBool::new(false) }; NUM_PINS_PORT_A];
|
||||
static EDGE_DETECTION_PORT_B: [AtomicBool; NUM_PINS_PORT_B] =
|
||||
[const { AtomicBool::new(false) }; NUM_PINS_PORT_B];
|
||||
|
||||
#[inline]
|
||||
fn pin_id_to_offset(dyn_pin_id: DynPinId) -> usize {
|
||||
match dyn_pin_id.group {
|
||||
DynGroup::A => dyn_pin_id.num as usize,
|
||||
DynGroup::B => NUM_PINS_PORT_A + dyn_pin_id.num as usize,
|
||||
}
|
||||
}
|
||||
|
||||
/// Generic interrupt handler for GPIO interrupts to support the async functionalities.
|
||||
/// Generic interrupt handler for GPIO interrupts on a specific port to support async functionalities
|
||||
///
|
||||
/// This handler will wake the correspoding wakers for the pins which triggered an interrupt
|
||||
/// as well as updating the static edge detection structures. This allows the pin future to
|
||||
/// complete async operations. The user should call this function in ALL interrupt handlers
|
||||
/// which handle any GPIO interrupts.
|
||||
#[inline]
|
||||
pub fn on_interrupt_for_asynch_gpio() {
|
||||
/// This function should be called in all interrupt handlers which handle any GPIO interrupts
|
||||
/// matching the [Port] argument.
|
||||
/// The handler will wake the corresponding wakers for the pins that triggered an interrupts
|
||||
/// as well as update the static edge detection structures. This allows the pin future tocomplete
|
||||
/// complete async operations.
|
||||
pub fn on_interrupt_for_async_gpio_for_port(port: Port) {
|
||||
let periphs = unsafe { pac::Peripherals::steal() };
|
||||
|
||||
handle_interrupt_for_gpio_and_port(
|
||||
periphs.porta.irq_enb().read().bits(),
|
||||
periphs.porta.edge_status().read().bits(),
|
||||
0,
|
||||
);
|
||||
handle_interrupt_for_gpio_and_port(
|
||||
periphs.portb.irq_enb().read().bits(),
|
||||
periphs.portb.edge_status().read().bits(),
|
||||
NUM_PINS_PORT_A,
|
||||
);
|
||||
let (irq_enb, edge_status, wakers, edge_detection) = match port {
|
||||
Port::A => (
|
||||
periphs.porta.irq_enb().read().bits(),
|
||||
periphs.porta.edge_status().read().bits(),
|
||||
WAKERS_FOR_PORT_A.as_ref(),
|
||||
EDGE_DETECTION_PORT_A.as_ref(),
|
||||
),
|
||||
Port::B => (
|
||||
periphs.portb.irq_enb().read().bits(),
|
||||
periphs.portb.edge_status().read().bits(),
|
||||
WAKERS_FOR_PORT_B.as_ref(),
|
||||
EDGE_DETECTION_PORT_B.as_ref(),
|
||||
),
|
||||
};
|
||||
|
||||
on_interrupt_for_port(irq_enb, edge_status, wakers, edge_detection);
|
||||
}
|
||||
|
||||
// Uses the enabled interrupt register and the persistent edge status to capture all GPIO events.
|
||||
#[inline]
|
||||
fn handle_interrupt_for_gpio_and_port(mut irq_enb: u32, edge_status: u32, pin_base_offset: usize) {
|
||||
fn on_interrupt_for_port(
|
||||
mut irq_enb: u32,
|
||||
edge_status: u32,
|
||||
wakers: &'static [AtomicWaker],
|
||||
edge_detection: &'static [AtomicBool],
|
||||
) {
|
||||
while irq_enb != 0 {
|
||||
let bit_pos = irq_enb.trailing_zeros() as usize;
|
||||
let bit_mask = 1 << bit_pos;
|
||||
|
||||
WAKERS[pin_base_offset + bit_pos].wake();
|
||||
wakers[bit_pos].wake();
|
||||
|
||||
if edge_status & bit_mask != 0 {
|
||||
EDGE_DETECTION[pin_base_offset + bit_pos]
|
||||
.store(true, core::sync::atomic::Ordering::Relaxed);
|
||||
}
|
||||
edge_detection[bit_pos].store(true, core::sync::atomic::Ordering::Relaxed);
|
||||
|
||||
// Clear the processed bit
|
||||
irq_enb &= !bit_mask;
|
||||
// Clear the processed bit
|
||||
irq_enb &= !bit_mask;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -85,93 +90,66 @@ fn handle_interrupt_for_gpio_and_port(mut irq_enb: u32, edge_status: u32, pin_ba
|
||||
/// struture is granted to allow writing custom async structures.
|
||||
pub struct InputPinFuture {
|
||||
pin_id: DynPinId,
|
||||
waker_group: &'static [AtomicWaker],
|
||||
edge_detection_group: &'static [AtomicBool],
|
||||
}
|
||||
|
||||
impl InputPinFuture {
|
||||
/// # Safety
|
||||
///
|
||||
/// This calls [Self::new_with_dyn_pin] but uses [pac::Peripherals::steal] to get the system configuration
|
||||
/// and IRQ selection peripherals. Users must ensure that the registers and configuration
|
||||
/// related to this input pin are not being used elsewhere concurrently.
|
||||
pub unsafe fn new_unchecked_with_dyn_pin(
|
||||
pin: &mut DynPin,
|
||||
irq: pac::Interrupt,
|
||||
edge: InterruptEdge,
|
||||
) -> Result<Self, InvalidPinTypeError> {
|
||||
let mut periphs = pac::Peripherals::steal();
|
||||
Self::new_with_dyn_pin(pin, irq, edge, &mut periphs.sysconfig, &mut periphs.irqsel)
|
||||
#[inline]
|
||||
pub fn pin_group_to_waker_and_edge_detection_group(
|
||||
group: Port,
|
||||
) -> (&'static [AtomicWaker], &'static [AtomicBool]) {
|
||||
match group {
|
||||
Port::A => (WAKERS_FOR_PORT_A.as_ref(), EDGE_DETECTION_PORT_A.as_ref()),
|
||||
Port::B => (WAKERS_FOR_PORT_B.as_ref(), EDGE_DETECTION_PORT_B.as_ref()),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn new_with_dyn_pin(
|
||||
pin: &mut DynPin,
|
||||
irq: pac::Interrupt,
|
||||
edge: InterruptEdge,
|
||||
sys_cfg: &mut Sysconfig,
|
||||
irq_sel: &mut Irqsel,
|
||||
) -> Result<Self, InvalidPinTypeError> {
|
||||
if !pin.is_input_pin() {
|
||||
return Err(InvalidPinTypeError(pin.mode()));
|
||||
}
|
||||
|
||||
EDGE_DETECTION[pin_id_to_offset(pin.id())]
|
||||
let (waker_group, edge_detection_group) =
|
||||
Self::pin_group_to_waker_and_edge_detection_group(pin.id().port());
|
||||
edge_detection_group[pin.id().num() as usize]
|
||||
.store(false, core::sync::atomic::Ordering::Relaxed);
|
||||
pin.interrupt_edge(
|
||||
edge,
|
||||
InterruptConfig::new(irq, true, true),
|
||||
Some(sys_cfg),
|
||||
Some(irq_sel),
|
||||
)
|
||||
.unwrap();
|
||||
Ok(Self { pin_id: pin.id() })
|
||||
}
|
||||
|
||||
/// # Safety
|
||||
///
|
||||
/// This calls [Self::new_with_pin] but uses [pac::Peripherals::steal] to get the system configuration
|
||||
/// and IRQ selection peripherals. Users must ensure that the registers and configuration
|
||||
/// related to this input pin are not being used elsewhere concurrently.
|
||||
pub unsafe fn new_unchecked_with_pin<I: PinId, C: InputConfig>(
|
||||
pin: &mut Pin<I, pin::Input<C>>,
|
||||
irq: pac::Interrupt,
|
||||
edge: InterruptEdge,
|
||||
) -> Self {
|
||||
let mut periphs = pac::Peripherals::steal();
|
||||
Self::new_with_pin(pin, irq, edge, &mut periphs.sysconfig, &mut periphs.irqsel)
|
||||
pin.configure_edge_interrupt(edge).unwrap();
|
||||
pin.enable_interrupt(InterruptConfig::new(irq, true, true));
|
||||
Ok(Self {
|
||||
pin_id: pin.id(),
|
||||
waker_group,
|
||||
edge_detection_group,
|
||||
})
|
||||
}
|
||||
|
||||
pub fn new_with_pin<I: PinId, C: InputConfig>(
|
||||
pin: &mut Pin<I, pin::Input<C>>,
|
||||
irq: pac::Interrupt,
|
||||
edge: InterruptEdge,
|
||||
sys_cfg: &mut Sysconfig,
|
||||
irq_sel: &mut Irqsel,
|
||||
) -> Self {
|
||||
EDGE_DETECTION[pin_id_to_offset(pin.id())]
|
||||
let (waker_group, edge_detection_group) =
|
||||
Self::pin_group_to_waker_and_edge_detection_group(pin.id().port());
|
||||
edge_detection_group[pin.id().num() as usize]
|
||||
.store(false, core::sync::atomic::Ordering::Relaxed);
|
||||
pin.configure_edge_interrupt(
|
||||
edge,
|
||||
InterruptConfig::new(irq, true, true),
|
||||
Some(sys_cfg),
|
||||
Some(irq_sel),
|
||||
);
|
||||
Self { pin_id: pin.id() }
|
||||
pin.configure_edge_interrupt(edge);
|
||||
pin.enable_interrupt(InterruptConfig::new(irq, true, true));
|
||||
Self {
|
||||
pin_id: pin.id(),
|
||||
edge_detection_group,
|
||||
waker_group,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Drop for InputPinFuture {
|
||||
fn drop(&mut self) {
|
||||
let periphs = unsafe { pac::Peripherals::steal() };
|
||||
if self.pin_id.group == DynGroup::A {
|
||||
periphs
|
||||
.porta
|
||||
.irq_enb()
|
||||
.modify(|r, w| unsafe { w.bits(r.bits() & !(1 << self.pin_id.num)) });
|
||||
} else {
|
||||
periphs
|
||||
.porta
|
||||
.irq_enb()
|
||||
.modify(|r, w| unsafe { w.bits(r.bits() & !(1 << self.pin_id.num)) });
|
||||
}
|
||||
// The API ensures that we actually own the pin, so stealing it here is okay.
|
||||
unsafe { DynPin::steal(self.pin_id) }.disable_interrupt(false);
|
||||
}
|
||||
}
|
||||
|
||||
@ -181,9 +159,9 @@ impl Future for InputPinFuture {
|
||||
self: core::pin::Pin<&mut Self>,
|
||||
cx: &mut core::task::Context<'_>,
|
||||
) -> core::task::Poll<Self::Output> {
|
||||
let idx = pin_id_to_offset(self.pin_id);
|
||||
WAKERS[idx].register(cx.waker());
|
||||
if EDGE_DETECTION[idx].swap(false, core::sync::atomic::Ordering::Relaxed) {
|
||||
let idx = self.pin_id.num() as usize;
|
||||
self.waker_group[idx].register(cx.waker());
|
||||
if self.edge_detection_group[idx].swap(false, core::sync::atomic::Ordering::Relaxed) {
|
||||
return core::task::Poll::Ready(());
|
||||
}
|
||||
core::task::Poll::Pending
|
||||
@ -200,8 +178,8 @@ impl InputDynPinAsync {
|
||||
/// passed as well and is used to route and enable the interrupt.
|
||||
///
|
||||
/// Please note that the interrupt handler itself must be provided by the user and the
|
||||
/// generic [on_interrupt_for_asynch_gpio] function must be called inside that function for
|
||||
/// the asynchronous functionality to work.
|
||||
/// generic [on_interrupt_for_async_gpio_for_port] function must be called inside that function
|
||||
/// for the asynchronous functionality to work.
|
||||
pub fn new(pin: DynPin, irq: pac::Interrupt) -> Result<Self, InvalidPinTypeError> {
|
||||
if !pin.is_input_pin() {
|
||||
return Err(InvalidPinTypeError(pin.mode()));
|
||||
@ -213,15 +191,10 @@ impl InputDynPinAsync {
|
||||
///
|
||||
/// This returns immediately if the pin is already high.
|
||||
pub async fn wait_for_high(&mut self) {
|
||||
let fut = unsafe {
|
||||
// Unwrap okay, checked pin in constructor.
|
||||
InputPinFuture::new_unchecked_with_dyn_pin(
|
||||
&mut self.pin,
|
||||
self.irq,
|
||||
InterruptEdge::LowToHigh,
|
||||
)
|
||||
.unwrap()
|
||||
};
|
||||
// Unwrap okay, checked pin in constructor.
|
||||
let fut =
|
||||
InputPinFuture::new_with_dyn_pin(&mut self.pin, self.irq, InterruptEdge::LowToHigh)
|
||||
.unwrap();
|
||||
if self.pin.is_high().unwrap() {
|
||||
return;
|
||||
}
|
||||
@ -232,15 +205,10 @@ impl InputDynPinAsync {
|
||||
///
|
||||
/// This returns immediately if the pin is already high.
|
||||
pub async fn wait_for_low(&mut self) {
|
||||
let fut = unsafe {
|
||||
// Unwrap okay, checked pin in constructor.
|
||||
InputPinFuture::new_unchecked_with_dyn_pin(
|
||||
&mut self.pin,
|
||||
self.irq,
|
||||
InterruptEdge::HighToLow,
|
||||
)
|
||||
.unwrap()
|
||||
};
|
||||
// Unwrap okay, checked pin in constructor.
|
||||
let fut =
|
||||
InputPinFuture::new_with_dyn_pin(&mut self.pin, self.irq, InterruptEdge::HighToLow)
|
||||
.unwrap();
|
||||
if self.pin.is_low().unwrap() {
|
||||
return;
|
||||
}
|
||||
@ -249,44 +217,26 @@ impl InputDynPinAsync {
|
||||
|
||||
/// Asynchronously wait until the pin sees a falling edge.
|
||||
pub async fn wait_for_falling_edge(&mut self) {
|
||||
unsafe {
|
||||
// Unwrap okay, checked pin in constructor.
|
||||
InputPinFuture::new_unchecked_with_dyn_pin(
|
||||
&mut self.pin,
|
||||
self.irq,
|
||||
InterruptEdge::HighToLow,
|
||||
)
|
||||
// Unwrap okay, checked pin in constructor.
|
||||
InputPinFuture::new_with_dyn_pin(&mut self.pin, self.irq, InterruptEdge::HighToLow)
|
||||
.unwrap()
|
||||
}
|
||||
.await;
|
||||
.await;
|
||||
}
|
||||
|
||||
/// Asynchronously wait until the pin sees a rising edge.
|
||||
pub async fn wait_for_rising_edge(&mut self) {
|
||||
unsafe {
|
||||
// Unwrap okay, checked pin in constructor.
|
||||
InputPinFuture::new_unchecked_with_dyn_pin(
|
||||
&mut self.pin,
|
||||
self.irq,
|
||||
InterruptEdge::LowToHigh,
|
||||
)
|
||||
// Unwrap okay, checked pin in constructor.
|
||||
InputPinFuture::new_with_dyn_pin(&mut self.pin, self.irq, InterruptEdge::LowToHigh)
|
||||
.unwrap()
|
||||
}
|
||||
.await;
|
||||
.await;
|
||||
}
|
||||
|
||||
/// Asynchronously wait until the pin sees any edge (either rising or falling).
|
||||
pub async fn wait_for_any_edge(&mut self) {
|
||||
unsafe {
|
||||
// Unwrap okay, checked pin in constructor.
|
||||
InputPinFuture::new_unchecked_with_dyn_pin(
|
||||
&mut self.pin,
|
||||
self.irq,
|
||||
InterruptEdge::BothEdges,
|
||||
)
|
||||
// Unwrap okay, checked pin in constructor.
|
||||
InputPinFuture::new_with_dyn_pin(&mut self.pin, self.irq, InterruptEdge::BothEdges)
|
||||
.unwrap()
|
||||
}
|
||||
.await;
|
||||
.await;
|
||||
}
|
||||
|
||||
pub fn release(self) -> DynPin {
|
||||
@ -335,8 +285,8 @@ impl<I: PinId, C: InputConfig> InputPinAsync<I, C> {
|
||||
/// passed as well and is used to route and enable the interrupt.
|
||||
///
|
||||
/// Please note that the interrupt handler itself must be provided by the user and the
|
||||
/// generic [on_interrupt_for_asynch_gpio] function must be called inside that function for
|
||||
/// the asynchronous functionality to work.
|
||||
/// generic [on_interrupt_for_async_gpio_for_port] function must be called inside that function
|
||||
/// for the asynchronous functionality to work.
|
||||
pub fn new(pin: Pin<I, pin::Input<C>>, irq: pac::Interrupt) -> Self {
|
||||
Self { pin, irq }
|
||||
}
|
||||
@ -345,14 +295,8 @@ impl<I: PinId, C: InputConfig> InputPinAsync<I, C> {
|
||||
///
|
||||
/// This returns immediately if the pin is already high.
|
||||
pub async fn wait_for_high(&mut self) {
|
||||
let fut = unsafe {
|
||||
InputPinFuture::new_unchecked_with_pin(
|
||||
&mut self.pin,
|
||||
self.irq,
|
||||
InterruptEdge::LowToHigh,
|
||||
)
|
||||
};
|
||||
if self.pin.is_high().unwrap() {
|
||||
let fut = InputPinFuture::new_with_pin(&mut self.pin, self.irq, InterruptEdge::LowToHigh);
|
||||
if self.pin.is_high() {
|
||||
return;
|
||||
}
|
||||
fut.await;
|
||||
@ -362,14 +306,8 @@ impl<I: PinId, C: InputConfig> InputPinAsync<I, C> {
|
||||
///
|
||||
/// This returns immediately if the pin is already high.
|
||||
pub async fn wait_for_low(&mut self) {
|
||||
let fut = unsafe {
|
||||
InputPinFuture::new_unchecked_with_pin(
|
||||
&mut self.pin,
|
||||
self.irq,
|
||||
InterruptEdge::HighToLow,
|
||||
)
|
||||
};
|
||||
if self.pin.is_low().unwrap() {
|
||||
let fut = InputPinFuture::new_with_pin(&mut self.pin, self.irq, InterruptEdge::HighToLow);
|
||||
if self.pin.is_low() {
|
||||
return;
|
||||
}
|
||||
fut.await;
|
||||
@ -377,40 +315,19 @@ impl<I: PinId, C: InputConfig> InputPinAsync<I, C> {
|
||||
|
||||
/// Asynchronously wait until the pin sees falling edge.
|
||||
pub async fn wait_for_falling_edge(&mut self) {
|
||||
unsafe {
|
||||
// Unwrap okay, checked pin in constructor.
|
||||
InputPinFuture::new_unchecked_with_pin(
|
||||
&mut self.pin,
|
||||
self.irq,
|
||||
InterruptEdge::HighToLow,
|
||||
)
|
||||
}
|
||||
.await;
|
||||
// Unwrap okay, checked pin in constructor.
|
||||
InputPinFuture::new_with_pin(&mut self.pin, self.irq, InterruptEdge::HighToLow).await;
|
||||
}
|
||||
|
||||
/// Asynchronously wait until the pin sees rising edge.
|
||||
pub async fn wait_for_rising_edge(&mut self) {
|
||||
unsafe {
|
||||
// Unwrap okay, checked pin in constructor.
|
||||
InputPinFuture::new_unchecked_with_pin(
|
||||
&mut self.pin,
|
||||
self.irq,
|
||||
InterruptEdge::LowToHigh,
|
||||
)
|
||||
}
|
||||
.await;
|
||||
// Unwrap okay, checked pin in constructor.
|
||||
InputPinFuture::new_with_pin(&mut self.pin, self.irq, InterruptEdge::LowToHigh).await;
|
||||
}
|
||||
|
||||
/// Asynchronously wait until the pin sees any edge (either rising or falling).
|
||||
pub async fn wait_for_any_edge(&mut self) {
|
||||
unsafe {
|
||||
InputPinFuture::new_unchecked_with_pin(
|
||||
&mut self.pin,
|
||||
self.irq,
|
||||
InterruptEdge::BothEdges,
|
||||
)
|
||||
}
|
||||
.await;
|
||||
InputPinFuture::new_with_pin(&mut self.pin, self.irq, InterruptEdge::BothEdges).await;
|
||||
}
|
||||
|
||||
pub fn release(self) -> Pin<I, pin::Input<C>> {
|
||||
|
@ -57,11 +57,10 @@
|
||||
//! [InvalidPinTypeError].
|
||||
|
||||
use super::{
|
||||
pin::{FilterType, InterruptEdge, InterruptLevel, Pin, PinId, PinMode, PinState},
|
||||
reg::RegisterInterface,
|
||||
InputDynPinAsync,
|
||||
pin::{FilterType, Pin, PinId, PinMode},
|
||||
InputDynPinAsync, InterruptEdge, InterruptLevel, IsMaskedError, PinState, Port,
|
||||
};
|
||||
use crate::{clock::FilterClkSel, enable_nvic_interrupt, pac, FunSel, InterruptConfig};
|
||||
use crate::{clock::FilterClkSel, enable_nvic_interrupt, pac, FunSel};
|
||||
|
||||
//==================================================================================================
|
||||
// DynPinMode configurations
|
||||
@ -156,55 +155,92 @@ pub const DYN_ALT_FUNC_3: DynPinMode = DynPinMode::Alternate(DynAlternate::Sel3)
|
||||
// DynGroup & DynPinId
|
||||
//==================================================================================================
|
||||
|
||||
/// Value-level `enum` for pin groups
|
||||
#[derive(Debug, PartialEq, Eq, Clone, Copy)]
|
||||
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
|
||||
pub enum DynGroup {
|
||||
A,
|
||||
B,
|
||||
}
|
||||
pub type DynGroup = Port;
|
||||
|
||||
/// Value-level `struct` representing pin IDs
|
||||
#[derive(Debug, PartialEq, Eq, Clone, Copy)]
|
||||
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
|
||||
pub struct DynPinId {
|
||||
pub group: DynGroup,
|
||||
pub num: u8,
|
||||
port: Port,
|
||||
num: u8,
|
||||
}
|
||||
|
||||
//==================================================================================================
|
||||
// DynRegisters
|
||||
//==================================================================================================
|
||||
impl DynPinId {
|
||||
pub const fn new(port: Port, num: u8) -> Self {
|
||||
DynPinId { port, num }
|
||||
}
|
||||
|
||||
/// Provide a safe register interface for [`DynPin`]s
|
||||
///
|
||||
/// This `struct` takes ownership of a [`DynPinId`] and provides an API to
|
||||
/// access the corresponding regsiters.
|
||||
#[derive(Debug)]
|
||||
pub(crate) struct DynRegisters(DynPinId);
|
||||
|
||||
// [`DynRegisters`] takes ownership of the [`DynPinId`], and [`DynPin`]
|
||||
// guarantees that each pin is a singleton, so this implementation is safe.
|
||||
unsafe impl RegisterInterface for DynRegisters {
|
||||
#[inline]
|
||||
fn id(&self) -> DynPinId {
|
||||
self.0
|
||||
pub const fn port(&self) -> Port {
|
||||
self.port
|
||||
}
|
||||
pub const fn num(&self) -> u8 {
|
||||
self.num
|
||||
}
|
||||
}
|
||||
|
||||
impl DynRegisters {
|
||||
/// Create a new instance of [`DynRegisters`]
|
||||
///
|
||||
/// # Safety
|
||||
///
|
||||
/// Users must never create two simultaneous instances of this `struct` with
|
||||
/// the same [`DynPinId`]
|
||||
//==================================================================================================
|
||||
// ModeFields
|
||||
//==================================================================================================
|
||||
|
||||
/// Collect all fields needed to set the [`PinMode`](super::PinMode)
|
||||
#[derive(Default)]
|
||||
struct ModeFields {
|
||||
dir: bool,
|
||||
opendrn: bool,
|
||||
pull_en: bool,
|
||||
/// true for pullup, false for pulldown
|
||||
pull_dir: bool,
|
||||
funsel: u8,
|
||||
enb_input: bool,
|
||||
}
|
||||
|
||||
impl From<DynPinMode> for ModeFields {
|
||||
#[inline]
|
||||
unsafe fn new(id: DynPinId) -> Self {
|
||||
DynRegisters(id)
|
||||
fn from(mode: DynPinMode) -> Self {
|
||||
let mut fields = Self::default();
|
||||
match mode {
|
||||
DynPinMode::Input(config) => {
|
||||
fields.dir = false;
|
||||
fields.funsel = FunSel::Sel0 as u8;
|
||||
match config {
|
||||
DynInput::Floating => (),
|
||||
DynInput::PullUp => {
|
||||
fields.pull_en = true;
|
||||
fields.pull_dir = true;
|
||||
}
|
||||
DynInput::PullDown => {
|
||||
fields.pull_en = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
DynPinMode::Output(config) => {
|
||||
fields.dir = true;
|
||||
fields.funsel = FunSel::Sel0 as u8;
|
||||
match config {
|
||||
DynOutput::PushPull => (),
|
||||
DynOutput::OpenDrain => {
|
||||
fields.opendrn = true;
|
||||
}
|
||||
DynOutput::ReadableOpenDrain => {
|
||||
fields.enb_input = true;
|
||||
fields.opendrn = true;
|
||||
}
|
||||
DynOutput::ReadablePushPull => {
|
||||
fields.enb_input = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
DynPinMode::Alternate(config) => {
|
||||
fields.funsel = config as u8;
|
||||
}
|
||||
}
|
||||
fields
|
||||
}
|
||||
}
|
||||
|
||||
/// Type definition to avoid confusion: These register blocks are identical
|
||||
type PortRegisterBlock = pac::porta::RegisterBlock;
|
||||
pub type PortReg = pac::ioconfig::Porta;
|
||||
|
||||
//==================================================================================================
|
||||
// DynPin
|
||||
//==================================================================================================
|
||||
@ -215,46 +251,59 @@ impl DynRegisters {
|
||||
/// by the same type, and pins are tracked and distinguished at run-time.
|
||||
#[derive(Debug)]
|
||||
pub struct DynPin {
|
||||
pub(crate) regs: DynRegisters,
|
||||
id: DynPinId,
|
||||
mode: DynPinMode,
|
||||
}
|
||||
|
||||
impl DynPin {
|
||||
/// Create a new [`DynPin`]
|
||||
/// Create a new [DynPin]
|
||||
///
|
||||
/// # Safety
|
||||
///
|
||||
/// Each [`DynPin`] must be a singleton. For a given [`DynPinId`], there
|
||||
/// Each [DynPin] must be a singleton. For a given [DynPinId], there
|
||||
/// must be at most one corresponding [`DynPin`] in existence at any given
|
||||
/// time. Violating this requirement is `unsafe`.
|
||||
#[inline]
|
||||
pub(crate) unsafe fn new(id: DynPinId, mode: DynPinMode) -> Self {
|
||||
pub(crate) const unsafe fn new(id: DynPinId, mode: DynPinMode) -> Self {
|
||||
DynPin { id, mode }
|
||||
}
|
||||
|
||||
/// Steals a new [DynPin].
|
||||
///
|
||||
/// This function will simply set the internal mode to [DYN_FLOATING_INPUT] pin without
|
||||
/// modifying any registers related to the behaviour of the pin. The user should call
|
||||
/// [Self::into_mode] to ensure the correct mode of the pin.
|
||||
///
|
||||
/// # Safety
|
||||
///
|
||||
/// Circumvents the HAL's safety guarantees. The caller must ensure that the pin is not
|
||||
/// used cocurrently somewhere else. The caller might also want to call [Self::into_mode]
|
||||
/// to ensure the correct desired state of the pin. It is recommended to create the pin using
|
||||
/// [Pin::downgrade] instead.
|
||||
pub const unsafe fn steal(id: DynPinId) -> Self {
|
||||
DynPin {
|
||||
regs: DynRegisters::new(id),
|
||||
mode,
|
||||
id,
|
||||
mode: DYN_FLOATING_INPUT,
|
||||
}
|
||||
}
|
||||
|
||||
/// Return a copy of the pin ID
|
||||
#[inline]
|
||||
pub fn id(&self) -> DynPinId {
|
||||
self.regs.0
|
||||
pub const fn id(&self) -> DynPinId {
|
||||
self.id
|
||||
}
|
||||
|
||||
/// Return a copy of the pin mode
|
||||
#[inline]
|
||||
pub fn mode(&self) -> DynPinMode {
|
||||
pub const fn mode(&self) -> DynPinMode {
|
||||
self.mode
|
||||
}
|
||||
|
||||
/// Convert the pin to the requested [`DynPinMode`]
|
||||
#[inline]
|
||||
pub fn into_mode(&mut self, mode: DynPinMode) {
|
||||
// Only modify registers if we are actually changing pin mode
|
||||
if mode != self.mode {
|
||||
self.regs.change_mode(mode);
|
||||
self.mode = mode;
|
||||
}
|
||||
self.change_mode(mode);
|
||||
self.mode = mode;
|
||||
}
|
||||
|
||||
#[inline]
|
||||
@ -262,6 +311,11 @@ impl DynPin {
|
||||
matches!(self.mode, DynPinMode::Input(_))
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn is_output_pin(&self) -> bool {
|
||||
matches!(self.mode, DynPinMode::Output(_))
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn into_funsel_1(&mut self) {
|
||||
self.into_mode(DYN_ALT_FUNC_1);
|
||||
@ -319,205 +373,60 @@ impl DynPin {
|
||||
self.into_mode(DYN_RD_OPEN_DRAIN_OUTPUT);
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn datamask(&self) -> bool {
|
||||
self.regs.datamask()
|
||||
#[inline(always)]
|
||||
pub fn is_low(&self) -> Result<bool, InvalidPinTypeError> {
|
||||
self.read_internal().map(|v| !v)
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn clear_datamask(&mut self) {
|
||||
self.regs.clear_datamask();
|
||||
#[inline(always)]
|
||||
pub fn is_high(&self) -> Result<bool, InvalidPinTypeError> {
|
||||
self.read_internal()
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn set_datamask(&mut self) {
|
||||
self.regs.set_datamask();
|
||||
#[inline(always)]
|
||||
pub fn set_low(&mut self) -> Result<(), InvalidPinTypeError> {
|
||||
self.write_internal(false)
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn is_high_masked(&self) -> Result<bool, crate::gpio::IsMaskedError> {
|
||||
self.regs.read_pin_masked()
|
||||
#[inline(always)]
|
||||
pub fn set_high(&mut self) -> Result<(), InvalidPinTypeError> {
|
||||
self.write_internal(true)
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn is_low_masked(&self) -> Result<bool, crate::gpio::IsMaskedError> {
|
||||
self.regs.read_pin_masked().map(|v| !v)
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn set_high_masked(&mut self) -> Result<(), crate::gpio::IsMaskedError> {
|
||||
self.regs.write_pin_masked(true)
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn set_low_masked(&mut self) -> Result<(), crate::gpio::IsMaskedError> {
|
||||
self.regs.write_pin_masked(false)
|
||||
}
|
||||
|
||||
pub(crate) fn irq_enb(
|
||||
&mut self,
|
||||
irq_cfg: crate::InterruptConfig,
|
||||
syscfg: Option<&mut va108xx::Sysconfig>,
|
||||
irqsel: Option<&mut va108xx::Irqsel>,
|
||||
) {
|
||||
if let Some(syscfg) = syscfg {
|
||||
crate::clock::enable_peripheral_clock(syscfg, crate::clock::PeripheralClocks::Irqsel);
|
||||
/// Toggle the logic level of an output pin
|
||||
#[inline(always)]
|
||||
pub fn toggle(&mut self) -> Result<(), InvalidPinTypeError> {
|
||||
if !self.is_output_pin() {
|
||||
return Err(InvalidPinTypeError(self.mode));
|
||||
}
|
||||
self.regs.enable_irq();
|
||||
if let Some(irqsel) = irqsel {
|
||||
if irq_cfg.route {
|
||||
match self.regs.id().group {
|
||||
// Set the correct interrupt number in the IRQSEL register
|
||||
DynGroup::A => {
|
||||
irqsel
|
||||
.porta0(self.regs.id().num as usize)
|
||||
.write(|w| unsafe { w.bits(irq_cfg.id as u32) });
|
||||
}
|
||||
DynGroup::B => {
|
||||
irqsel
|
||||
.portb0(self.regs.id().num as usize)
|
||||
.write(|w| unsafe { w.bits(irq_cfg.id as u32) });
|
||||
}
|
||||
}
|
||||
}
|
||||
// Safety: TOGOUT is a "mask" register, and we only write the bit for
|
||||
// this pin ID
|
||||
unsafe { self.port_reg().togout().write(|w| w.bits(self.mask_32())) };
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn enable_interrupt(&mut self, irq_cfg: crate::InterruptConfig) {
|
||||
if irq_cfg.route {
|
||||
self.configure_irqsel(irq_cfg.id);
|
||||
}
|
||||
if irq_cfg.enable_in_nvic {
|
||||
unsafe { enable_nvic_interrupt(irq_cfg.id) };
|
||||
}
|
||||
|
||||
// We only manipulate our own bit.
|
||||
self.port_reg()
|
||||
.irq_enb()
|
||||
.modify(|r, w| unsafe { w.bits(r.bits() | self.mask_32()) });
|
||||
}
|
||||
|
||||
/// See p.53 of the programmers guide for more information.
|
||||
/// Possible delays in clock cycles:
|
||||
/// - Delay 1: 1
|
||||
/// - Delay 2: 2
|
||||
/// - Delay 1 + Delay 2: 3
|
||||
#[inline]
|
||||
pub fn delay(self, delay_1: bool, delay_2: bool) -> Result<Self, InvalidPinTypeError> {
|
||||
match self.mode {
|
||||
DynPinMode::Output(_) => {
|
||||
self.regs.delay(delay_1, delay_2);
|
||||
Ok(self)
|
||||
}
|
||||
_ => Err(InvalidPinTypeError(self.mode)),
|
||||
pub fn disable_interrupt(&mut self, reset_irqsel: bool) {
|
||||
if reset_irqsel {
|
||||
self.reset_irqsel();
|
||||
}
|
||||
}
|
||||
|
||||
/// See p.52 of the programmers guide for more information.
|
||||
/// When configured for pulse mode, a given pin will set the non-default state for exactly
|
||||
/// one clock cycle before returning to the configured default state
|
||||
#[inline]
|
||||
pub fn pulse_mode(
|
||||
&mut self,
|
||||
enable: bool,
|
||||
default_state: PinState,
|
||||
) -> Result<(), InvalidPinTypeError> {
|
||||
match self.mode {
|
||||
DynPinMode::Output(_) => {
|
||||
self.regs.pulse_mode(enable, default_state);
|
||||
Ok(())
|
||||
}
|
||||
_ => Err(InvalidPinTypeError(self.mode)),
|
||||
}
|
||||
}
|
||||
|
||||
/// See p.37 and p.38 of the programmers guide for more information.
|
||||
#[inline]
|
||||
pub fn filter_type(
|
||||
&mut self,
|
||||
filter: FilterType,
|
||||
clksel: FilterClkSel,
|
||||
) -> Result<(), InvalidPinTypeError> {
|
||||
match self.mode {
|
||||
DynPinMode::Input(_) => {
|
||||
self.regs.filter_type(filter, clksel);
|
||||
Ok(())
|
||||
}
|
||||
_ => Err(InvalidPinTypeError(self.mode)),
|
||||
}
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn interrupt_edge(
|
||||
&mut self,
|
||||
edge_type: InterruptEdge,
|
||||
irq_cfg: InterruptConfig,
|
||||
syscfg: Option<&mut pac::Sysconfig>,
|
||||
irqsel: Option<&mut pac::Irqsel>,
|
||||
) -> Result<(), InvalidPinTypeError> {
|
||||
match self.mode {
|
||||
DynPinMode::Input(_) | DynPinMode::Output(_) => {
|
||||
self.regs.interrupt_edge(edge_type);
|
||||
self.irq_enb(irq_cfg, syscfg, irqsel);
|
||||
Ok(())
|
||||
}
|
||||
_ => Err(InvalidPinTypeError(self.mode)),
|
||||
}
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn interrupt_level(
|
||||
&mut self,
|
||||
level_type: InterruptLevel,
|
||||
irq_cfg: InterruptConfig,
|
||||
syscfg: Option<&mut pac::Sysconfig>,
|
||||
irqsel: Option<&mut pac::Irqsel>,
|
||||
) -> Result<(), InvalidPinTypeError> {
|
||||
match self.mode {
|
||||
DynPinMode::Input(_) | DynPinMode::Output(_) => {
|
||||
self.regs.interrupt_level(level_type);
|
||||
self.irq_enb(irq_cfg, syscfg, irqsel);
|
||||
Ok(())
|
||||
}
|
||||
_ => Err(InvalidPinTypeError(self.mode)),
|
||||
}
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn toggle_with_toggle_reg(&mut self) -> Result<(), InvalidPinTypeError> {
|
||||
match self.mode {
|
||||
DynPinMode::Output(_) => {
|
||||
self.regs.toggle();
|
||||
Ok(())
|
||||
}
|
||||
_ => Err(InvalidPinTypeError(self.mode)),
|
||||
}
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn _read(&self) -> Result<bool, InvalidPinTypeError> {
|
||||
match self.mode {
|
||||
DynPinMode::Input(_) | DYN_RD_OPEN_DRAIN_OUTPUT | DYN_RD_PUSH_PULL_OUTPUT => {
|
||||
Ok(self.regs.read_pin())
|
||||
}
|
||||
_ => Err(InvalidPinTypeError(self.mode)),
|
||||
}
|
||||
}
|
||||
#[inline]
|
||||
fn _write(&mut self, bit: bool) -> Result<(), InvalidPinTypeError> {
|
||||
match self.mode {
|
||||
DynPinMode::Output(_) => {
|
||||
self.regs.write_pin(bit);
|
||||
Ok(())
|
||||
}
|
||||
_ => Err(InvalidPinTypeError(self.mode)),
|
||||
}
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn _is_low(&self) -> Result<bool, InvalidPinTypeError> {
|
||||
self._read().map(|v| !v)
|
||||
}
|
||||
#[inline]
|
||||
fn _is_high(&self) -> Result<bool, InvalidPinTypeError> {
|
||||
self._read()
|
||||
}
|
||||
#[inline]
|
||||
fn _set_low(&mut self) -> Result<(), InvalidPinTypeError> {
|
||||
self._write(false)
|
||||
}
|
||||
#[inline]
|
||||
fn _set_high(&mut self) -> Result<(), InvalidPinTypeError> {
|
||||
self._write(true)
|
||||
// We only manipulate our own bit.
|
||||
self.port_reg()
|
||||
.irq_enb()
|
||||
.modify(|r, w| unsafe { w.bits(r.bits() & !self.mask_32()) });
|
||||
}
|
||||
|
||||
/// Try to recreate a type-level [`Pin`] from a value-level [`DynPin`]
|
||||
@ -527,7 +436,7 @@ impl DynPin {
|
||||
/// or refuse to perform it.
|
||||
#[inline]
|
||||
pub fn upgrade<I: PinId, M: PinMode>(self) -> Result<Pin<I, M>, InvalidPinTypeError> {
|
||||
if self.regs.0 == I::DYN && self.mode == M::DYN {
|
||||
if self.id == I::DYN && self.mode == M::DYN {
|
||||
// The `DynPin` is consumed, so it is safe to replace it with the
|
||||
// corresponding `Pin`
|
||||
return Ok(unsafe { Pin::new() });
|
||||
@ -543,6 +452,424 @@ impl DynPin {
|
||||
) -> Result<InputDynPinAsync, InvalidPinTypeError> {
|
||||
InputDynPinAsync::new(self, irq)
|
||||
}
|
||||
|
||||
/// Configure the IRQSEL peripheral for this particular pin with the given interrupt ID.
|
||||
pub fn configure_irqsel(&mut self, id: pac::Interrupt) {
|
||||
let mut syscfg = unsafe { pac::Sysconfig::steal() };
|
||||
let irqsel = unsafe { pac::Irqsel::steal() };
|
||||
crate::clock::enable_peripheral_clock(&mut syscfg, crate::clock::PeripheralClocks::Irqsel);
|
||||
match self.id().port() {
|
||||
// Set the correct interrupt number in the IRQSEL register
|
||||
super::Port::A => {
|
||||
irqsel
|
||||
.porta0(self.id().num() as usize)
|
||||
.write(|w| unsafe { w.bits(id as u32) });
|
||||
}
|
||||
super::Port::B => {
|
||||
irqsel
|
||||
.portb0(self.id().num as usize)
|
||||
.write(|w| unsafe { w.bits(id as u32) });
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Reset the IRQSEL peripheral value for this particular pin.
|
||||
pub fn reset_irqsel(&mut self) {
|
||||
let mut syscfg = unsafe { pac::Sysconfig::steal() };
|
||||
let irqsel = unsafe { pac::Irqsel::steal() };
|
||||
crate::clock::enable_peripheral_clock(&mut syscfg, crate::clock::PeripheralClocks::Irqsel);
|
||||
match self.id().port() {
|
||||
// Set the correct interrupt number in the IRQSEL register
|
||||
super::Port::A => {
|
||||
irqsel
|
||||
.porta0(self.id().num() as usize)
|
||||
.write(|w| unsafe { w.bits(u32::MAX) });
|
||||
}
|
||||
super::Port::B => {
|
||||
irqsel
|
||||
.portb0(self.id().num as usize)
|
||||
.write(|w| unsafe { w.bits(u32::MAX) });
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Get DATAMASK bit for this particular pin
|
||||
#[inline(always)]
|
||||
pub fn datamask(&self) -> bool {
|
||||
(self.port_reg().datamask().read().bits() >> self.id().num) == 1
|
||||
}
|
||||
|
||||
/// Clear DATAMASK bit for this particular pin. This prevents access
|
||||
/// of the corresponding bit for output and input operations
|
||||
#[inline(always)]
|
||||
pub fn clear_datamask(&self) {
|
||||
self.port_reg()
|
||||
.datamask()
|
||||
.modify(|r, w| unsafe { w.bits(r.bits() & !self.mask_32()) });
|
||||
}
|
||||
|
||||
/// Set DATAMASK bit for this particular pin. 1 is the default
|
||||
/// state of the bit and allows access of the corresponding bit
|
||||
#[inline(always)]
|
||||
pub fn set_datamask(&self) {
|
||||
self.port_reg()
|
||||
.datamask()
|
||||
.modify(|r, w| unsafe { w.bits(r.bits() | self.mask_32()) });
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn is_high_masked(&self) -> Result<bool, crate::gpio::IsMaskedError> {
|
||||
self.read_pin_masked()
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn is_low_masked(&self) -> Result<bool, crate::gpio::IsMaskedError> {
|
||||
self.read_pin_masked().map(|v| !v)
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn set_high_masked(&mut self) -> Result<(), crate::gpio::IsMaskedError> {
|
||||
self.write_pin_masked(true)
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn set_low_masked(&mut self) -> Result<(), crate::gpio::IsMaskedError> {
|
||||
self.write_pin_masked(false)
|
||||
}
|
||||
|
||||
/// Possible delays in clock cycles:
|
||||
/// - Delay 1: 1
|
||||
/// - Delay 2: 2
|
||||
/// - Delay 1 + Delay 2: 3
|
||||
#[inline]
|
||||
pub fn configure_delay(
|
||||
&mut self,
|
||||
delay_1: bool,
|
||||
delay_2: bool,
|
||||
) -> Result<(), InvalidPinTypeError> {
|
||||
match self.mode {
|
||||
DynPinMode::Output(_) => {
|
||||
self.configure_delay_internal(delay_1, delay_2);
|
||||
Ok(())
|
||||
}
|
||||
_ => Err(InvalidPinTypeError(self.mode)),
|
||||
}
|
||||
}
|
||||
|
||||
/// When configured for pulse mode, a given pin will set the non-default state for exactly
|
||||
/// one clock cycle before returning to the configured default state
|
||||
#[inline]
|
||||
pub fn configure_pulse_mode(
|
||||
&mut self,
|
||||
enable: bool,
|
||||
default_state: PinState,
|
||||
) -> Result<(), InvalidPinTypeError> {
|
||||
match self.mode {
|
||||
DynPinMode::Output(_) => {
|
||||
self.configure_pulse_mode_internal(enable, default_state);
|
||||
Ok(())
|
||||
}
|
||||
_ => Err(InvalidPinTypeError(self.mode)),
|
||||
}
|
||||
}
|
||||
|
||||
/// See p.37 and p.38 of the programmers guide for more information.
|
||||
#[inline]
|
||||
pub fn configure_filter_type(
|
||||
&mut self,
|
||||
filter: FilterType,
|
||||
clksel: FilterClkSel,
|
||||
) -> Result<(), InvalidPinTypeError> {
|
||||
match self.mode {
|
||||
DynPinMode::Input(_) => {
|
||||
self.configure_filter_type_internal(filter, clksel);
|
||||
Ok(())
|
||||
}
|
||||
_ => Err(InvalidPinTypeError(self.mode)),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn configure_edge_interrupt(
|
||||
&mut self,
|
||||
edge_type: InterruptEdge,
|
||||
) -> Result<(), InvalidPinTypeError> {
|
||||
match self.mode {
|
||||
DynPinMode::Input(_) | DynPinMode::Output(_) => {
|
||||
self.configure_edge_interrupt_internal(edge_type);
|
||||
Ok(())
|
||||
}
|
||||
_ => Err(InvalidPinTypeError(self.mode)),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn configure_level_interrupt(
|
||||
&mut self,
|
||||
level_type: InterruptLevel,
|
||||
) -> Result<(), InvalidPinTypeError> {
|
||||
match self.mode {
|
||||
DynPinMode::Input(_) | DynPinMode::Output(_) => {
|
||||
self.configure_level_interrupt_internal(level_type);
|
||||
Ok(())
|
||||
}
|
||||
_ => Err(InvalidPinTypeError(self.mode)),
|
||||
}
|
||||
}
|
||||
|
||||
/// Change the pin mode
|
||||
#[inline]
|
||||
pub(crate) fn change_mode(&mut self, mode: DynPinMode) {
|
||||
let ModeFields {
|
||||
dir,
|
||||
funsel,
|
||||
opendrn,
|
||||
pull_dir,
|
||||
pull_en,
|
||||
enb_input,
|
||||
} = mode.into();
|
||||
let (portreg, iocfg) = (self.port_reg(), self.iocfg_port());
|
||||
iocfg.write(|w| {
|
||||
w.opendrn().bit(opendrn);
|
||||
w.pen().bit(pull_en);
|
||||
w.plevel().bit(pull_dir);
|
||||
w.iewo().bit(enb_input);
|
||||
unsafe { w.funsel().bits(funsel) }
|
||||
});
|
||||
let mask = self.mask_32();
|
||||
unsafe {
|
||||
if dir {
|
||||
portreg.dir().modify(|r, w| w.bits(r.bits() | mask));
|
||||
// Clear output
|
||||
portreg.clrout().write(|w| w.bits(mask));
|
||||
} else {
|
||||
portreg.dir().modify(|r, w| w.bits(r.bits() & !mask));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[inline]
|
||||
const fn port_reg(&self) -> &PortRegisterBlock {
|
||||
match self.id().port() {
|
||||
Port::A => unsafe { &(*pac::Porta::ptr()) },
|
||||
Port::B => unsafe { &(*pac::Portb::ptr()) },
|
||||
}
|
||||
}
|
||||
|
||||
#[inline]
|
||||
const fn iocfg_port(&self) -> &PortReg {
|
||||
let ioconfig = unsafe { va108xx::Ioconfig::ptr().as_ref().unwrap() };
|
||||
match self.id().port() {
|
||||
Port::A => ioconfig.porta(self.id().num() as usize),
|
||||
Port::B => ioconfig.portb0(self.id().num() as usize),
|
||||
}
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
fn read_internal(&self) -> Result<bool, InvalidPinTypeError> {
|
||||
match self.mode {
|
||||
DynPinMode::Input(_) | DYN_RD_OPEN_DRAIN_OUTPUT | DYN_RD_PUSH_PULL_OUTPUT => {
|
||||
Ok(self.read_pin())
|
||||
}
|
||||
_ => Err(InvalidPinTypeError(self.mode)),
|
||||
}
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
fn write_internal(&mut self, bit: bool) -> Result<(), InvalidPinTypeError> {
|
||||
match self.mode {
|
||||
DynPinMode::Output(_) => {
|
||||
self.write_pin(bit);
|
||||
Ok(())
|
||||
}
|
||||
_ => Err(InvalidPinTypeError(self.mode)),
|
||||
}
|
||||
}
|
||||
|
||||
#[inline]
|
||||
/// Read the logic level of an output pin
|
||||
pub(crate) fn read_pin(&self) -> bool {
|
||||
let portreg = self.port_reg();
|
||||
((portreg.datainraw().read().bits() >> self.id().num) & 0x01) == 1
|
||||
}
|
||||
|
||||
/// Read a pin but use the masked version but check whether the datamask for the pin is
|
||||
/// cleared as well
|
||||
#[inline(always)]
|
||||
fn read_pin_masked(&self) -> Result<bool, IsMaskedError> {
|
||||
if !self.datamask() {
|
||||
Err(IsMaskedError)
|
||||
} else {
|
||||
Ok(((self.port_reg().datain().read().bits() >> self.id().num) & 0x01) == 1)
|
||||
}
|
||||
}
|
||||
|
||||
/// Write the logic level of an output pin
|
||||
#[inline(always)]
|
||||
pub(crate) fn write_pin(&mut self, bit: bool) {
|
||||
// Safety: SETOUT is a "mask" register, and we only write the bit for
|
||||
// this pin ID
|
||||
unsafe {
|
||||
if bit {
|
||||
self.port_reg().setout().write(|w| w.bits(self.mask_32()));
|
||||
} else {
|
||||
self.port_reg().clrout().write(|w| w.bits(self.mask_32()));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Write the logic level of an output pin but check whether the datamask for the pin is
|
||||
/// cleared as well
|
||||
#[inline]
|
||||
fn write_pin_masked(&mut self, bit: bool) -> Result<(), IsMaskedError> {
|
||||
if !self.datamask() {
|
||||
Err(IsMaskedError)
|
||||
} else {
|
||||
// Safety: SETOUT is a "mask" register, and we only write the bit for
|
||||
// this pin ID
|
||||
unsafe {
|
||||
if bit {
|
||||
self.port_reg().setout().write(|w| w.bits(self.mask_32()));
|
||||
} else {
|
||||
self.port_reg().clrout().write(|w| w.bits(self.mask_32()));
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Toggle the logic level of an output pin
|
||||
#[inline(always)]
|
||||
pub fn toggle_with_togout_reg(&mut self) {
|
||||
// Safety: TOGOUT is a "mask" register, and we only write the bit for
|
||||
// this pin ID
|
||||
unsafe { self.port_reg().togout().write(|w| w.bits(self.mask_32())) };
|
||||
}
|
||||
|
||||
/// Only useful for interrupt pins. Configure whether to use edges or level as interrupt soure
|
||||
/// When using edge mode, it is possible to generate interrupts on both edges as well
|
||||
#[inline]
|
||||
fn configure_edge_interrupt_internal(&mut self, edge_type: InterruptEdge) {
|
||||
unsafe {
|
||||
self.port_reg()
|
||||
.irq_sen()
|
||||
.modify(|r, w| w.bits(r.bits() & !self.mask_32()));
|
||||
match edge_type {
|
||||
InterruptEdge::HighToLow => {
|
||||
self.port_reg()
|
||||
.irq_evt()
|
||||
.modify(|r, w| w.bits(r.bits() & !self.mask_32()));
|
||||
}
|
||||
InterruptEdge::LowToHigh => {
|
||||
self.port_reg()
|
||||
.irq_evt()
|
||||
.modify(|r, w| w.bits(r.bits() | self.mask_32()));
|
||||
}
|
||||
InterruptEdge::BothEdges => {
|
||||
self.port_reg()
|
||||
.irq_edge()
|
||||
.modify(|r, w| w.bits(r.bits() | self.mask_32()));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Configure which edge or level type triggers an interrupt
|
||||
#[inline]
|
||||
fn configure_level_interrupt_internal(&mut self, level: InterruptLevel) {
|
||||
unsafe {
|
||||
self.port_reg()
|
||||
.irq_sen()
|
||||
.modify(|r, w| w.bits(r.bits() | self.mask_32()));
|
||||
if level == InterruptLevel::Low {
|
||||
self.port_reg()
|
||||
.irq_evt()
|
||||
.modify(|r, w| w.bits(r.bits() & !self.mask_32()));
|
||||
} else {
|
||||
self.port_reg()
|
||||
.irq_evt()
|
||||
.modify(|r, w| w.bits(r.bits() | self.mask_32()));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Only useful for input pins
|
||||
#[inline]
|
||||
fn configure_filter_type_internal(&mut self, filter: FilterType, clksel: FilterClkSel) {
|
||||
self.iocfg_port().modify(|_, w| {
|
||||
// Safety: Only write to register for this Pin ID
|
||||
unsafe {
|
||||
w.flttype().bits(filter as u8);
|
||||
w.fltclk().bits(clksel as u8)
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn configure_pulse_mode_internal(&mut self, enable: bool, default_state: PinState) {
|
||||
let portreg = self.port_reg();
|
||||
unsafe {
|
||||
if enable {
|
||||
portreg
|
||||
.pulse()
|
||||
.modify(|r, w| w.bits(r.bits() | self.mask_32()));
|
||||
} else {
|
||||
portreg
|
||||
.pulse()
|
||||
.modify(|r, w| w.bits(r.bits() & !self.mask_32()));
|
||||
}
|
||||
if default_state == PinState::Low {
|
||||
portreg
|
||||
.pulsebase()
|
||||
.modify(|r, w| w.bits(r.bits() & !self.mask_32()));
|
||||
} else {
|
||||
portreg
|
||||
.pulsebase()
|
||||
.modify(|r, w| w.bits(r.bits() | self.mask_32()));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Only useful for output pins
|
||||
#[inline]
|
||||
fn configure_delay_internal(&mut self, delay_1: bool, delay_2: bool) {
|
||||
let portreg = self.port_reg();
|
||||
unsafe {
|
||||
if delay_1 {
|
||||
portreg
|
||||
.delay1()
|
||||
.modify(|r, w| w.bits(r.bits() | self.mask_32()));
|
||||
} else {
|
||||
portreg
|
||||
.delay1()
|
||||
.modify(|r, w| w.bits(r.bits() & !self.mask_32()));
|
||||
}
|
||||
if delay_2 {
|
||||
portreg
|
||||
.delay2()
|
||||
.modify(|r, w| w.bits(r.bits() | self.mask_32()));
|
||||
} else {
|
||||
portreg
|
||||
.delay2()
|
||||
.modify(|r, w| w.bits(r.bits() & !self.mask_32()));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Only serves disambiguation purposes for the Embedded HAL impl
|
||||
#[inline(always)]
|
||||
fn is_low_mut(&mut self) -> Result<bool, InvalidPinTypeError> {
|
||||
self.is_low()
|
||||
}
|
||||
|
||||
// Only serves disambiguation purposes for the Embedded HAL impl
|
||||
#[inline(always)]
|
||||
fn is_high_mut(&mut self) -> Result<bool, InvalidPinTypeError> {
|
||||
self.is_high()
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
const fn mask_32(&self) -> u32 {
|
||||
1 << self.id().num()
|
||||
}
|
||||
}
|
||||
|
||||
//==================================================================================================
|
||||
@ -583,33 +910,38 @@ impl embedded_hal::digital::ErrorType for DynPin {
|
||||
impl embedded_hal::digital::OutputPin for DynPin {
|
||||
#[inline]
|
||||
fn set_high(&mut self) -> Result<(), Self::Error> {
|
||||
self._set_high()
|
||||
self.set_high()
|
||||
}
|
||||
#[inline]
|
||||
fn set_low(&mut self) -> Result<(), Self::Error> {
|
||||
self._set_low()
|
||||
self.set_low()
|
||||
}
|
||||
}
|
||||
|
||||
impl embedded_hal::digital::InputPin for DynPin {
|
||||
#[inline]
|
||||
fn is_high(&mut self) -> Result<bool, Self::Error> {
|
||||
self._is_high()
|
||||
self.is_high_mut()
|
||||
}
|
||||
#[inline]
|
||||
fn is_low(&mut self) -> Result<bool, Self::Error> {
|
||||
self._is_low()
|
||||
self.is_low_mut()
|
||||
}
|
||||
}
|
||||
|
||||
impl embedded_hal::digital::StatefulOutputPin for DynPin {
|
||||
#[inline]
|
||||
fn is_set_high(&mut self) -> Result<bool, Self::Error> {
|
||||
self._is_high()
|
||||
self.is_high_mut()
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn is_set_low(&mut self) -> Result<bool, Self::Error> {
|
||||
self._is_low()
|
||||
self.is_low_mut()
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn toggle(&mut self) -> Result<(), Self::Error> {
|
||||
self.toggle()
|
||||
}
|
||||
}
|
||||
|
@ -22,14 +22,47 @@
|
||||
//!
|
||||
//! - [Blinky example](https://egit.irs.uni-stuttgart.de/rust/va108xx-rs/src/branch/main/examples/simple/examples/blinky.rs)
|
||||
|
||||
//==================================================================================================
|
||||
// Errors, Definitions and Constants
|
||||
//==================================================================================================
|
||||
|
||||
pub const NUM_PINS_PORT_A: usize = 32;
|
||||
pub const NUM_PINS_PORT_B: usize = 24;
|
||||
pub const NUM_GPIO_PINS: usize = NUM_PINS_PORT_A + NUM_PINS_PORT_B;
|
||||
|
||||
#[derive(Debug, PartialEq, Eq, thiserror::Error)]
|
||||
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
|
||||
#[error("The pin is masked")]
|
||||
pub struct IsMaskedError;
|
||||
|
||||
pub const NUM_PINS_PORT_A: usize = 32;
|
||||
pub const NUM_PINS_PORT_B: usize = 24;
|
||||
pub const NUM_GPIO_PINS: usize = NUM_PINS_PORT_A + NUM_PINS_PORT_B;
|
||||
#[derive(Debug, PartialEq, Eq, Clone, Copy)]
|
||||
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
|
||||
pub enum Port {
|
||||
A,
|
||||
B,
|
||||
}
|
||||
|
||||
#[derive(Debug, PartialEq, Eq)]
|
||||
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
|
||||
pub enum InterruptEdge {
|
||||
HighToLow,
|
||||
LowToHigh,
|
||||
BothEdges,
|
||||
}
|
||||
|
||||
#[derive(Debug, PartialEq, Eq)]
|
||||
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
|
||||
pub enum InterruptLevel {
|
||||
Low = 0,
|
||||
High = 1,
|
||||
}
|
||||
|
||||
#[derive(Debug, PartialEq, Eq)]
|
||||
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
|
||||
pub enum PinState {
|
||||
Low = 0,
|
||||
High = 1,
|
||||
}
|
||||
|
||||
pub mod dynpin;
|
||||
pub use dynpin::*;
|
||||
@ -39,5 +72,3 @@ pub use pin::*;
|
||||
|
||||
pub mod asynch;
|
||||
pub use asynch::*;
|
||||
|
||||
mod reg;
|
||||
|
@ -68,45 +68,19 @@
|
||||
//! # Embedded HAL traits
|
||||
//!
|
||||
//! This module implements all of the embedded HAL GPIO traits for each [`Pin`]
|
||||
//! in the corresponding [`PinMode`]s, namely: [`InputPin`], [`OutputPin`],
|
||||
//! and [`StatefulOutputPin`].
|
||||
use super::dynpin::{DynAlternate, DynGroup, DynInput, DynOutput, DynPinId, DynPinMode};
|
||||
use super::reg::RegisterInterface;
|
||||
use super::{DynPin, InputPinAsync};
|
||||
//! in the corresponding [`PinMode`]s, namely: [embedded_hal::digital::InputPin],
|
||||
//! [embedded_hal::digital::OutputPin] and [embedded_hal::digital::StatefulOutputPin].
|
||||
use super::dynpin::{DynAlternate, DynInput, DynOutput, DynPinId, DynPinMode};
|
||||
use super::{DynPin, InputPinAsync, InterruptEdge, InterruptLevel, PinState, Port};
|
||||
use crate::{
|
||||
pac::{Irqsel, Porta, Portb, Sysconfig},
|
||||
pac::{Porta, Portb},
|
||||
typelevel::Sealed,
|
||||
InterruptConfig,
|
||||
};
|
||||
use core::convert::Infallible;
|
||||
use core::marker::PhantomData;
|
||||
use core::mem::transmute;
|
||||
use embedded_hal::digital::{InputPin, OutputPin, StatefulOutputPin};
|
||||
use paste::paste;
|
||||
|
||||
//==================================================================================================
|
||||
// Errors and Definitions
|
||||
//==================================================================================================
|
||||
|
||||
#[derive(Debug, PartialEq, Eq)]
|
||||
pub enum InterruptEdge {
|
||||
HighToLow,
|
||||
LowToHigh,
|
||||
BothEdges,
|
||||
}
|
||||
|
||||
#[derive(Debug, PartialEq, Eq)]
|
||||
pub enum InterruptLevel {
|
||||
Low = 0,
|
||||
High = 1,
|
||||
}
|
||||
|
||||
#[derive(Debug, PartialEq, Eq)]
|
||||
pub enum PinState {
|
||||
Low = 0,
|
||||
High = 1,
|
||||
}
|
||||
|
||||
//==================================================================================================
|
||||
// Input configuration
|
||||
//==================================================================================================
|
||||
@ -317,10 +291,7 @@ macro_rules! pin_id {
|
||||
pub enum $Id {}
|
||||
impl Sealed for $Id {}
|
||||
impl PinId for $Id {
|
||||
const DYN: DynPinId = DynPinId {
|
||||
group: DynGroup::$Group,
|
||||
num: $NUM,
|
||||
};
|
||||
const DYN: DynPinId = DynPinId::new(Port::$Group, $NUM);
|
||||
}
|
||||
}
|
||||
};
|
||||
@ -346,14 +317,15 @@ impl<I: PinId, M: PinMode> Pin<I, M> {
|
||||
/// at most one corresponding [Pin] in existence at any given time.
|
||||
/// Violating this requirement is `unsafe`.
|
||||
#[inline]
|
||||
pub(crate) unsafe fn new() -> Pin<I, M> {
|
||||
pub(crate) const unsafe fn new() -> Pin<I, M> {
|
||||
Pin {
|
||||
inner: DynPin::new(I::DYN, M::DYN),
|
||||
phantom: PhantomData,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn id(&self) -> DynPinId {
|
||||
#[inline]
|
||||
pub const fn id(&self) -> DynPinId {
|
||||
self.inner.id()
|
||||
}
|
||||
|
||||
@ -363,7 +335,7 @@ impl<I: PinId, M: PinMode> Pin<I, M> {
|
||||
// Only modify registers if we are actually changing pin mode
|
||||
// This check should compile away
|
||||
if N::DYN != M::DYN {
|
||||
self.inner.regs.change_mode(N::DYN);
|
||||
self.inner.change_mode(N::DYN);
|
||||
}
|
||||
// Safe because we drop the existing Pin
|
||||
unsafe { Pin::new() }
|
||||
@ -423,6 +395,16 @@ impl<I: PinId, M: PinMode> Pin<I, M> {
|
||||
self.into_mode()
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn is_low(&self) -> bool {
|
||||
!self.inner.read_pin()
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn is_high(&self) -> bool {
|
||||
self.inner.read_pin()
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn datamask(&self) -> bool {
|
||||
self.inner.datamask()
|
||||
@ -448,53 +430,41 @@ impl<I: PinId, M: PinMode> Pin<I, M> {
|
||||
self.inner.is_low_masked()
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn set_high_masked(&mut self) -> Result<(), crate::gpio::IsMaskedError> {
|
||||
self.inner.set_high_masked()
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn set_low_masked(&mut self) -> Result<(), crate::gpio::IsMaskedError> {
|
||||
self.inner.set_low_masked()
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn downgrade(self) -> DynPin {
|
||||
self.inner
|
||||
}
|
||||
|
||||
fn irq_enb(
|
||||
&mut self,
|
||||
irq_cfg: crate::InterruptConfig,
|
||||
syscfg: Option<&mut va108xx::Sysconfig>,
|
||||
irqsel: Option<&mut va108xx::Irqsel>,
|
||||
) {
|
||||
self.inner.irq_enb(irq_cfg, syscfg, irqsel);
|
||||
// Those only serve for the embedded HAL implementations which have different mutability.
|
||||
|
||||
#[inline]
|
||||
fn is_low_mut(&mut self) -> bool {
|
||||
self.is_low()
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub(crate) fn _set_high(&mut self) {
|
||||
self.inner.regs.write_pin(true)
|
||||
fn is_high_mut(&mut self) -> bool {
|
||||
self.is_high()
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub(crate) fn _set_low(&mut self) {
|
||||
self.inner.regs.write_pin(false)
|
||||
pub fn enable_interrupt(&mut self, irq_cfg: crate::InterruptConfig) {
|
||||
self.inner.enable_interrupt(irq_cfg);
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub(crate) fn _toggle_with_toggle_reg(&mut self) {
|
||||
self.inner.regs.toggle();
|
||||
pub fn disable_interrupt(&mut self, reset_irqsel: bool) {
|
||||
self.inner.disable_interrupt(reset_irqsel);
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub(crate) fn _is_low(&self) -> bool {
|
||||
!self.inner.regs.read_pin()
|
||||
/// Configure the pin for an edge interrupt but does not enable the interrupt.
|
||||
pub fn configure_edge_interrupt(&mut self, edge_type: InterruptEdge) {
|
||||
self.inner.configure_edge_interrupt(edge_type).unwrap();
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub(crate) fn _is_high(&self) -> bool {
|
||||
self.inner.regs.read_pin()
|
||||
/// Configure the pin for a level interrupt but does not enable the interrupt.
|
||||
pub fn configure_level_interrupt(&mut self, level_type: InterruptLevel) {
|
||||
self.inner.configure_level_interrupt(level_type).unwrap();
|
||||
}
|
||||
}
|
||||
|
||||
@ -591,74 +561,52 @@ impl<I: PinId, C: InputConfig> Pin<I, Input<C>> {
|
||||
pub fn into_async_input(self, irq: crate::pac::Interrupt) -> InputPinAsync<I, C> {
|
||||
InputPinAsync::new(self, irq)
|
||||
}
|
||||
|
||||
pub fn configure_edge_interrupt(
|
||||
&mut self,
|
||||
edge_type: InterruptEdge,
|
||||
irq_cfg: InterruptConfig,
|
||||
syscfg: Option<&mut Sysconfig>,
|
||||
irqsel: Option<&mut Irqsel>,
|
||||
) {
|
||||
self.inner.regs.interrupt_edge(edge_type);
|
||||
self.irq_enb(irq_cfg, syscfg, irqsel);
|
||||
}
|
||||
|
||||
pub fn configure_level_interrupt(
|
||||
&mut self,
|
||||
level_type: InterruptLevel,
|
||||
irq_cfg: InterruptConfig,
|
||||
syscfg: Option<&mut Sysconfig>,
|
||||
irqsel: Option<&mut Irqsel>,
|
||||
) {
|
||||
self.inner.regs.interrupt_level(level_type);
|
||||
self.irq_enb(irq_cfg, syscfg, irqsel);
|
||||
}
|
||||
}
|
||||
|
||||
impl<I: PinId, C: OutputConfig> Pin<I, Output<C>> {
|
||||
#[inline]
|
||||
pub fn set_high(&mut self) {
|
||||
self.inner.write_pin(true)
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn set_low(&mut self) {
|
||||
self.inner.write_pin(false)
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn toggle(&mut self) {
|
||||
self.inner.toggle().unwrap()
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn set_high_masked(&mut self) -> Result<(), crate::gpio::IsMaskedError> {
|
||||
self.inner.set_high_masked()
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn set_low_masked(&mut self) -> Result<(), crate::gpio::IsMaskedError> {
|
||||
self.inner.set_low_masked()
|
||||
}
|
||||
|
||||
/// See p.53 of the programmers guide for more information.
|
||||
/// Possible delays in clock cycles:
|
||||
/// - Delay 1: 1
|
||||
/// - Delay 2: 2
|
||||
/// - Delay 1 + Delay 2: 3
|
||||
#[inline]
|
||||
pub fn delay(self, delay_1: bool, delay_2: bool) -> Self {
|
||||
self.inner.regs.delay(delay_1, delay_2);
|
||||
self
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn toggle_with_toggle_reg(&mut self) {
|
||||
self._toggle_with_toggle_reg()
|
||||
pub fn configure_delay(&mut self, delay_1: bool, delay_2: bool) {
|
||||
self.inner.configure_delay(delay_1, delay_2).unwrap();
|
||||
}
|
||||
|
||||
/// See p.52 of the programmers guide for more information.
|
||||
///
|
||||
/// When configured for pulse mode, a given pin will set the non-default state for exactly
|
||||
/// one clock cycle before returning to the configured default state
|
||||
pub fn pulse_mode(&mut self, enable: bool, default_state: PinState) {
|
||||
self.inner.regs.pulse_mode(enable, default_state);
|
||||
}
|
||||
|
||||
pub fn interrupt_edge(
|
||||
&mut self,
|
||||
edge_type: InterruptEdge,
|
||||
irq_cfg: InterruptConfig,
|
||||
syscfg: Option<&mut Sysconfig>,
|
||||
irqsel: Option<&mut Irqsel>,
|
||||
) {
|
||||
self.inner.regs.interrupt_edge(edge_type);
|
||||
self.irq_enb(irq_cfg, syscfg, irqsel);
|
||||
}
|
||||
|
||||
pub fn interrupt_level(
|
||||
&mut self,
|
||||
level_type: InterruptLevel,
|
||||
irq_cfg: InterruptConfig,
|
||||
syscfg: Option<&mut Sysconfig>,
|
||||
irqsel: Option<&mut Irqsel>,
|
||||
) {
|
||||
self.inner.regs.interrupt_level(level_type);
|
||||
self.irq_enb(irq_cfg, syscfg, irqsel);
|
||||
pub fn configure_pulse_mode(&mut self, enable: bool, default_state: PinState) {
|
||||
self.inner
|
||||
.configure_pulse_mode(enable, default_state)
|
||||
.unwrap();
|
||||
}
|
||||
}
|
||||
|
||||
@ -666,7 +614,7 @@ impl<I: PinId, C: InputConfig> Pin<I, Input<C>> {
|
||||
/// See p.37 and p.38 of the programmers guide for more information.
|
||||
#[inline]
|
||||
pub fn configure_filter_type(&mut self, filter: FilterType, clksel: FilterClkSel) {
|
||||
self.inner.regs.filter_type(filter, clksel);
|
||||
self.inner.configure_filter_type(filter, clksel).unwrap();
|
||||
}
|
||||
}
|
||||
|
||||
@ -682,63 +630,53 @@ where
|
||||
type Error = Infallible;
|
||||
}
|
||||
|
||||
impl<I: PinId, C: OutputConfig> OutputPin for Pin<I, Output<C>> {
|
||||
impl<I: PinId, C: OutputConfig> embedded_hal::digital::OutputPin for Pin<I, Output<C>> {
|
||||
#[inline]
|
||||
fn set_high(&mut self) -> Result<(), Self::Error> {
|
||||
self._set_high();
|
||||
self.set_high();
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn set_low(&mut self) -> Result<(), Self::Error> {
|
||||
self._set_low();
|
||||
self.set_low();
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
impl<I, C> InputPin for Pin<I, Input<C>>
|
||||
impl<I, C> embedded_hal::digital::InputPin for Pin<I, Input<C>>
|
||||
where
|
||||
I: PinId,
|
||||
C: InputConfig,
|
||||
{
|
||||
#[inline]
|
||||
fn is_high(&mut self) -> Result<bool, Self::Error> {
|
||||
Ok(self._is_high())
|
||||
Ok(self.is_high_mut())
|
||||
}
|
||||
#[inline]
|
||||
fn is_low(&mut self) -> Result<bool, Self::Error> {
|
||||
Ok(self._is_low())
|
||||
Ok(self.is_low_mut())
|
||||
}
|
||||
}
|
||||
|
||||
impl<I, C> StatefulOutputPin for Pin<I, Output<C>>
|
||||
impl<I, C> embedded_hal::digital::StatefulOutputPin for Pin<I, Output<C>>
|
||||
where
|
||||
I: PinId,
|
||||
C: OutputConfig + ReadableOutput,
|
||||
{
|
||||
#[inline]
|
||||
fn is_set_high(&mut self) -> Result<bool, Self::Error> {
|
||||
Ok(self._is_high())
|
||||
Ok(self.is_high())
|
||||
}
|
||||
#[inline]
|
||||
fn is_set_low(&mut self) -> Result<bool, Self::Error> {
|
||||
Ok(self._is_low())
|
||||
}
|
||||
}
|
||||
|
||||
impl<I, C> InputPin for Pin<I, Output<C>>
|
||||
where
|
||||
I: PinId,
|
||||
C: OutputConfig + ReadableOutput,
|
||||
{
|
||||
#[inline]
|
||||
fn is_high(&mut self) -> Result<bool, Self::Error> {
|
||||
Ok(self._is_high())
|
||||
Ok(self.is_low())
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn is_low(&mut self) -> Result<bool, Self::Error> {
|
||||
Ok(self._is_low())
|
||||
fn toggle(&mut self) -> Result<(), Self::Error> {
|
||||
self.toggle();
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1,375 +0,0 @@
|
||||
use super::dynpin::{self, DynGroup, DynPinId, DynPinMode};
|
||||
use super::pin::{FilterType, InterruptEdge, InterruptLevel, PinState};
|
||||
use super::IsMaskedError;
|
||||
use crate::clock::FilterClkSel;
|
||||
use va108xx::{ioconfig, porta};
|
||||
|
||||
/// Type definition to avoid confusion: These register blocks are identical
|
||||
type PortRegisterBlock = porta::RegisterBlock;
|
||||
|
||||
//==================================================================================================
|
||||
// ModeFields
|
||||
//==================================================================================================
|
||||
|
||||
/// Collect all fields needed to set the [`PinMode`](super::PinMode)
|
||||
#[derive(Default)]
|
||||
struct ModeFields {
|
||||
dir: bool,
|
||||
opendrn: bool,
|
||||
pull_en: bool,
|
||||
/// true for pullup, false for pulldown
|
||||
pull_dir: bool,
|
||||
funsel: u8,
|
||||
enb_input: bool,
|
||||
}
|
||||
|
||||
impl From<DynPinMode> for ModeFields {
|
||||
#[inline]
|
||||
fn from(mode: DynPinMode) -> Self {
|
||||
let mut fields = Self::default();
|
||||
use DynPinMode::*;
|
||||
match mode {
|
||||
Input(config) => {
|
||||
use dynpin::DynInput::*;
|
||||
fields.dir = false;
|
||||
match config {
|
||||
Floating => (),
|
||||
PullUp => {
|
||||
fields.pull_en = true;
|
||||
fields.pull_dir = true;
|
||||
}
|
||||
PullDown => {
|
||||
fields.pull_en = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
Output(config) => {
|
||||
use dynpin::DynOutput::*;
|
||||
fields.dir = true;
|
||||
match config {
|
||||
PushPull => (),
|
||||
OpenDrain => {
|
||||
fields.opendrn = true;
|
||||
}
|
||||
ReadableOpenDrain => {
|
||||
fields.enb_input = true;
|
||||
fields.opendrn = true;
|
||||
}
|
||||
ReadablePushPull => {
|
||||
fields.enb_input = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
Alternate(config) => {
|
||||
fields.funsel = config as u8;
|
||||
}
|
||||
}
|
||||
fields
|
||||
}
|
||||
}
|
||||
|
||||
//==================================================================================================
|
||||
// Register Interface
|
||||
//==================================================================================================
|
||||
|
||||
pub type PortReg = ioconfig::Porta;
|
||||
|
||||
/// Provide a safe register interface for pin objects
|
||||
///
|
||||
/// [`PORTA`] and [`PORTB`], like every PAC `struct`, is [`Send`] but not [`Sync`], because it
|
||||
/// points to a `RegisterBlock` of `VolatileCell`s. Unfortunately, such an
|
||||
/// interface is quite restrictive. Instead, it would be ideal if we could split
|
||||
/// the [`PORT`] into independent pins that are both [`Send`] and [`Sync`].
|
||||
///
|
||||
/// [`PORT`] is a single, zero-sized marker `struct` that provides access to
|
||||
/// every [`PORT`] register. Instead, we would like to create zero-sized marker
|
||||
/// `struct`s for every pin, where each pin is only allowed to control its own
|
||||
/// registers. Furthermore, each pin `struct` should be a singleton, so that
|
||||
/// exclusive access to the `struct` also guarantees exclusive access to the
|
||||
/// corresponding registers. Finally, the pin `struct`s should not have any
|
||||
/// interior mutability. Together, these requirements would allow the pin
|
||||
/// `struct`s to be both [`Send`] and [`Sync`].
|
||||
///
|
||||
/// This trait creates a safe API for accomplishing these goals. Implementers
|
||||
/// supply a pin ID through the [`id`] function. The remaining functions provide
|
||||
/// a safe API for accessing the registers associated with that pin ID. Any
|
||||
/// modification of the registers requires `&mut self`, which destroys interior
|
||||
/// mutability.
|
||||
///
|
||||
/// # Safety
|
||||
///
|
||||
/// Users should only implement the [`id`] function. No default function
|
||||
/// implementations should be overridden. The implementing type must also have
|
||||
/// "control" over the corresponding pin ID, i.e. it must guarantee that a each
|
||||
/// pin ID is a singleton.
|
||||
///
|
||||
/// [`id`]: Self::id
|
||||
pub(super) unsafe trait RegisterInterface {
|
||||
/// Provide a [`DynPinId`] identifying the set of registers controlled by
|
||||
/// this type.
|
||||
fn id(&self) -> DynPinId;
|
||||
|
||||
const PORTA: *const PortRegisterBlock = va108xx::Porta::ptr();
|
||||
const PORTB: *const PortRegisterBlock = va108xx::Portb::ptr();
|
||||
|
||||
/// Change the pin mode
|
||||
#[inline]
|
||||
fn change_mode(&mut self, mode: DynPinMode) {
|
||||
let ModeFields {
|
||||
dir,
|
||||
funsel,
|
||||
opendrn,
|
||||
pull_dir,
|
||||
pull_en,
|
||||
enb_input,
|
||||
} = mode.into();
|
||||
let (portreg, iocfg) = (self.port_reg(), self.iocfg_port());
|
||||
iocfg.write(|w| {
|
||||
w.opendrn().bit(opendrn);
|
||||
w.pen().bit(pull_en);
|
||||
w.plevel().bit(pull_dir);
|
||||
w.iewo().bit(enb_input);
|
||||
unsafe { w.funsel().bits(funsel) }
|
||||
});
|
||||
let mask = self.mask_32();
|
||||
unsafe {
|
||||
if dir {
|
||||
portreg.dir().modify(|r, w| w.bits(r.bits() | mask));
|
||||
// Clear output
|
||||
portreg.clrout().write(|w| w.bits(mask));
|
||||
} else {
|
||||
portreg.dir().modify(|r, w| w.bits(r.bits() & !mask));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn port_reg(&self) -> &PortRegisterBlock {
|
||||
match self.id().group {
|
||||
DynGroup::A => unsafe { &(*Self::PORTA) },
|
||||
DynGroup::B => unsafe { &(*Self::PORTB) },
|
||||
}
|
||||
}
|
||||
fn iocfg_port(&self) -> &PortReg {
|
||||
let ioconfig = unsafe { va108xx::Ioconfig::ptr().as_ref().unwrap() };
|
||||
match self.id().group {
|
||||
DynGroup::A => ioconfig.porta(self.id().num as usize),
|
||||
DynGroup::B => ioconfig.portb0(self.id().num as usize),
|
||||
}
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn mask_32(&self) -> u32 {
|
||||
1 << self.id().num
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn enable_irq(&self) {
|
||||
self.port_reg()
|
||||
.irq_enb()
|
||||
.modify(|r, w| unsafe { w.bits(r.bits() | self.mask_32()) });
|
||||
}
|
||||
|
||||
#[inline]
|
||||
/// Read the logic level of an output pin
|
||||
fn read_pin(&self) -> bool {
|
||||
let portreg = self.port_reg();
|
||||
((portreg.datainraw().read().bits() >> self.id().num) & 0x01) == 1
|
||||
}
|
||||
|
||||
// Get DATAMASK bit for this particular pin
|
||||
#[inline(always)]
|
||||
fn datamask(&self) -> bool {
|
||||
let portreg = self.port_reg();
|
||||
(portreg.datamask().read().bits() >> self.id().num) == 1
|
||||
}
|
||||
|
||||
/// Read a pin but use the masked version but check whether the datamask for the pin is
|
||||
/// cleared as well
|
||||
#[inline(always)]
|
||||
fn read_pin_masked(&self) -> Result<bool, IsMaskedError> {
|
||||
if !self.datamask() {
|
||||
Err(IsMaskedError)
|
||||
} else {
|
||||
Ok(((self.port_reg().datain().read().bits() >> self.id().num) & 0x01) == 1)
|
||||
}
|
||||
}
|
||||
|
||||
/// Write the logic level of an output pin
|
||||
#[inline(always)]
|
||||
fn write_pin(&mut self, bit: bool) {
|
||||
// Safety: SETOUT is a "mask" register, and we only write the bit for
|
||||
// this pin ID
|
||||
unsafe {
|
||||
if bit {
|
||||
self.port_reg().setout().write(|w| w.bits(self.mask_32()));
|
||||
} else {
|
||||
self.port_reg().clrout().write(|w| w.bits(self.mask_32()));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Write the logic level of an output pin but check whether the datamask for the pin is
|
||||
/// cleared as well
|
||||
#[inline]
|
||||
fn write_pin_masked(&mut self, bit: bool) -> Result<(), IsMaskedError> {
|
||||
if !self.datamask() {
|
||||
Err(IsMaskedError)
|
||||
} else {
|
||||
// Safety: SETOUT is a "mask" register, and we only write the bit for
|
||||
// this pin ID
|
||||
unsafe {
|
||||
if bit {
|
||||
self.port_reg().setout().write(|w| w.bits(self.mask_32()));
|
||||
} else {
|
||||
self.port_reg().clrout().write(|w| w.bits(self.mask_32()));
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Toggle the logic level of an output pin
|
||||
#[inline(always)]
|
||||
fn toggle(&mut self) {
|
||||
// Safety: TOGOUT is a "mask" register, and we only write the bit for
|
||||
// this pin ID
|
||||
unsafe { self.port_reg().togout().write(|w| w.bits(self.mask_32())) };
|
||||
}
|
||||
|
||||
/// Only useful for interrupt pins. Configure whether to use edges or level as interrupt soure
|
||||
/// When using edge mode, it is possible to generate interrupts on both edges as well
|
||||
#[inline]
|
||||
fn interrupt_edge(&mut self, edge_type: InterruptEdge) {
|
||||
unsafe {
|
||||
self.port_reg()
|
||||
.irq_sen()
|
||||
.modify(|r, w| w.bits(r.bits() & !self.mask_32()));
|
||||
match edge_type {
|
||||
InterruptEdge::HighToLow => {
|
||||
self.port_reg()
|
||||
.irq_evt()
|
||||
.modify(|r, w| w.bits(r.bits() & !self.mask_32()));
|
||||
}
|
||||
InterruptEdge::LowToHigh => {
|
||||
self.port_reg()
|
||||
.irq_evt()
|
||||
.modify(|r, w| w.bits(r.bits() | self.mask_32()));
|
||||
}
|
||||
InterruptEdge::BothEdges => {
|
||||
self.port_reg()
|
||||
.irq_edge()
|
||||
.modify(|r, w| w.bits(r.bits() | self.mask_32()));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Configure which edge or level type triggers an interrupt
|
||||
#[inline]
|
||||
fn interrupt_level(&mut self, level: InterruptLevel) {
|
||||
unsafe {
|
||||
self.port_reg()
|
||||
.irq_sen()
|
||||
.modify(|r, w| w.bits(r.bits() | self.mask_32()));
|
||||
if level == InterruptLevel::Low {
|
||||
self.port_reg()
|
||||
.irq_evt()
|
||||
.modify(|r, w| w.bits(r.bits() & !self.mask_32()));
|
||||
} else {
|
||||
self.port_reg()
|
||||
.irq_evt()
|
||||
.modify(|r, w| w.bits(r.bits() | self.mask_32()));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Only useful for input pins
|
||||
#[inline]
|
||||
fn filter_type(&mut self, filter: FilterType, clksel: FilterClkSel) {
|
||||
self.iocfg_port().modify(|_, w| {
|
||||
// Safety: Only write to register for this Pin ID
|
||||
unsafe {
|
||||
w.flttype().bits(filter as u8);
|
||||
w.fltclk().bits(clksel as u8)
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
/// Set DATAMASK bit for this particular pin. 1 is the default
|
||||
/// state of the bit and allows access of the corresponding bit
|
||||
#[inline(always)]
|
||||
fn set_datamask(&self) {
|
||||
let portreg = self.port_reg();
|
||||
unsafe {
|
||||
portreg
|
||||
.datamask()
|
||||
.modify(|r, w| w.bits(r.bits() | self.mask_32()));
|
||||
}
|
||||
}
|
||||
|
||||
/// Clear DATAMASK bit for this particular pin. This prevents access
|
||||
/// of the corresponding bit for output and input operations
|
||||
#[inline(always)]
|
||||
fn clear_datamask(&self) {
|
||||
let portreg = self.port_reg();
|
||||
unsafe {
|
||||
portreg
|
||||
.datamask()
|
||||
.modify(|r, w| w.bits(r.bits() & !self.mask_32()));
|
||||
}
|
||||
}
|
||||
|
||||
/// Only useful for output pins
|
||||
/// See p.52 of the programmers guide for more information.
|
||||
/// When configured for pulse mode, a given pin will set the non-default state for exactly
|
||||
/// one clock cycle before returning to the configured default state
|
||||
fn pulse_mode(&mut self, enable: bool, default_state: PinState) {
|
||||
let portreg = self.port_reg();
|
||||
unsafe {
|
||||
if enable {
|
||||
portreg
|
||||
.pulse()
|
||||
.modify(|r, w| w.bits(r.bits() | self.mask_32()));
|
||||
} else {
|
||||
portreg
|
||||
.pulse()
|
||||
.modify(|r, w| w.bits(r.bits() & !self.mask_32()));
|
||||
}
|
||||
if default_state == PinState::Low {
|
||||
portreg
|
||||
.pulsebase()
|
||||
.modify(|r, w| w.bits(r.bits() & !self.mask_32()));
|
||||
} else {
|
||||
portreg
|
||||
.pulsebase()
|
||||
.modify(|r, w| w.bits(r.bits() | self.mask_32()));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Only useful for output pins
|
||||
fn delay(&self, delay_1: bool, delay_2: bool) {
|
||||
let portreg = self.port_reg();
|
||||
unsafe {
|
||||
if delay_1 {
|
||||
portreg
|
||||
.delay1()
|
||||
.modify(|r, w| w.bits(r.bits() | self.mask_32()));
|
||||
} else {
|
||||
portreg
|
||||
.delay1()
|
||||
.modify(|r, w| w.bits(r.bits() & !self.mask_32()));
|
||||
}
|
||||
if delay_2 {
|
||||
portreg
|
||||
.delay2()
|
||||
.modify(|r, w| w.bits(r.bits() | self.mask_32()));
|
||||
} else {
|
||||
portreg
|
||||
.delay2()
|
||||
.modify(|r, w| w.bits(r.bits() & !self.mask_32()));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -36,8 +36,6 @@ pub struct InvalidTimingParamsError;
|
||||
#[derive(Debug, PartialEq, Eq, thiserror::Error)]
|
||||
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
|
||||
pub enum Error {
|
||||
//#[error("Invalid timing parameters")]
|
||||
//InvalidTimingParams,
|
||||
#[error("arbitration lost")]
|
||||
ArbitrationLost,
|
||||
#[error("nack address")]
|
||||
@ -82,6 +80,7 @@ impl embedded_hal::i2c::Error for Error {
|
||||
}
|
||||
|
||||
#[derive(Debug, PartialEq, Copy, Clone)]
|
||||
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
|
||||
enum I2cCmd {
|
||||
Start = 0b00,
|
||||
Stop = 0b10,
|
||||
@ -252,6 +251,8 @@ impl Default for MasterConfig {
|
||||
|
||||
impl Sealed for MasterConfig {}
|
||||
|
||||
#[derive(Debug)]
|
||||
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
|
||||
pub struct SlaveConfig {
|
||||
pub tx_fe_mode: FifoEmptyMode,
|
||||
pub rx_fe_mode: FifoEmptyMode,
|
||||
@ -455,13 +456,6 @@ impl<I2c: Instance> I2cBase<I2c> {
|
||||
}
|
||||
}
|
||||
|
||||
// Unique mode to use the loopback functionality
|
||||
// pub struct I2cLoopback<I2C> {
|
||||
// i2c_base: I2cBase<I2C>,
|
||||
// master_cfg: MasterConfig,
|
||||
// slave_cfg: SlaveConfig,
|
||||
// }
|
||||
|
||||
//==================================================================================================
|
||||
// I2C Master
|
||||
//==================================================================================================
|
||||
@ -673,275 +667,6 @@ impl<I2c: Instance, Addr> I2cMaster<I2c, Addr> {
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
macro_rules! i2c_master {
|
||||
($($I2CX:path: ($i2cx:ident, $clk_enb:path),)+) => {
|
||||
$(
|
||||
impl<ADDR> I2cMaster<$I2CX, ADDR> {
|
||||
pub fn $i2cx(
|
||||
i2c: $I2CX,
|
||||
cfg: MasterConfig,
|
||||
sys_clk: impl Into<Hertz> + Copy,
|
||||
speed_mode: I2cSpeed,
|
||||
sys_cfg: Option<&mut pac::Sysconfig>,
|
||||
) -> Self {
|
||||
I2cMaster {
|
||||
i2c_base: I2cBase::$i2cx(
|
||||
i2c,
|
||||
sys_clk,
|
||||
speed_mode,
|
||||
Some(&cfg),
|
||||
None,
|
||||
sys_cfg
|
||||
),
|
||||
_addr: PhantomData,
|
||||
}
|
||||
.enable_master()
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn cancel_transfer(&self) {
|
||||
self.i2c_base
|
||||
.i2c
|
||||
.cmd()
|
||||
.write(|w| unsafe { w.bits(I2cCmd::Cancel as u32) });
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn clear_tx_fifo(&self) {
|
||||
self.i2c_base.i2c.fifo_clr().write(|w| w.txfifo().set_bit());
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn clear_rx_fifo(&self) {
|
||||
self.i2c_base.i2c.fifo_clr().write(|w| w.rxfifo().set_bit());
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn enable_master(self) -> Self {
|
||||
self.i2c_base.i2c.ctrl().modify(|_, w| w.enable().set_bit());
|
||||
self
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn disable_master(self) -> Self {
|
||||
self.i2c_base.i2c.ctrl().modify(|_, w| w.enable().clear_bit());
|
||||
self
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
fn load_fifo(&self, word: u8) {
|
||||
self.i2c_base
|
||||
.i2c
|
||||
.data()
|
||||
.write(|w| unsafe { w.bits(word as u32) });
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
fn read_fifo(&self) -> u8 {
|
||||
self.i2c_base.i2c.data().read().bits() as u8
|
||||
}
|
||||
|
||||
fn error_handler_write(&mut self, init_cmd: &I2cCmd) {
|
||||
self.clear_tx_fifo();
|
||||
if *init_cmd == I2cCmd::Start {
|
||||
self.i2c_base.stop_cmd()
|
||||
}
|
||||
}
|
||||
|
||||
fn write_base(
|
||||
&mut self,
|
||||
addr: I2cAddress,
|
||||
init_cmd: I2cCmd,
|
||||
bytes: impl IntoIterator<Item = u8>,
|
||||
) -> Result<(), Error> {
|
||||
let mut iter = bytes.into_iter();
|
||||
// Load address
|
||||
let (addr, addr_mode_bit) = I2cBase::<$I2CX>::unwrap_addr(addr);
|
||||
self.i2c_base.i2c.address().write(|w| unsafe {
|
||||
w.bits(I2cDirection::Send as u32 | (addr << 1) as u32 | addr_mode_bit)
|
||||
});
|
||||
|
||||
self.i2c_base
|
||||
.i2c
|
||||
.cmd()
|
||||
.write(|w| unsafe { w.bits(init_cmd as u32) });
|
||||
let mut load_if_next_available = || {
|
||||
if let Some(next_byte) = iter.next() {
|
||||
self.load_fifo(next_byte);
|
||||
}
|
||||
};
|
||||
loop {
|
||||
let status_reader = self.i2c_base.i2c.status().read();
|
||||
if status_reader.arblost().bit_is_set() {
|
||||
self.error_handler_write(&init_cmd);
|
||||
return Err(Error::ArbitrationLost);
|
||||
} else if status_reader.nackaddr().bit_is_set() {
|
||||
self.error_handler_write(&init_cmd);
|
||||
return Err(Error::NackAddr);
|
||||
} else if status_reader.nackdata().bit_is_set() {
|
||||
self.error_handler_write(&init_cmd);
|
||||
return Err(Error::NackData);
|
||||
} else if status_reader.idle().bit_is_set() {
|
||||
return Ok(());
|
||||
} else {
|
||||
while !status_reader.txnfull().bit_is_set() {
|
||||
load_if_next_available();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn write_from_buffer(
|
||||
&mut self,
|
||||
init_cmd: I2cCmd,
|
||||
addr: I2cAddress,
|
||||
output: &[u8],
|
||||
) -> Result<(), Error> {
|
||||
let len = output.len();
|
||||
// It should theoretically possible to transfer larger data sizes by tracking
|
||||
// the number of sent words and setting it to 0x7fe as soon as only that many
|
||||
// bytes are remaining. However, large transfer like this are not common. This
|
||||
// feature will therefore not be supported for now.
|
||||
if len > 0x7fe {
|
||||
return Err(Error::DataTooLarge);
|
||||
}
|
||||
// Load number of words
|
||||
self.i2c_base
|
||||
.i2c
|
||||
.words()
|
||||
.write(|w| unsafe { w.bits(len as u32) });
|
||||
let mut bytes = output.iter();
|
||||
// FIFO has a depth of 16. We load slightly above the trigger level
|
||||
// but not all of it because the transaction might fail immediately
|
||||
const FILL_DEPTH: usize = 12;
|
||||
|
||||
// load the FIFO
|
||||
for _ in 0..core::cmp::min(FILL_DEPTH, len) {
|
||||
self.load_fifo(*bytes.next().unwrap());
|
||||
}
|
||||
|
||||
self.write_base(addr, init_cmd, output.iter().cloned())
|
||||
}
|
||||
|
||||
fn read_internal(&mut self, addr: I2cAddress, buffer: &mut [u8]) -> Result<(), Error> {
|
||||
let len = buffer.len();
|
||||
// It should theoretically possible to transfer larger data sizes by tracking
|
||||
// the number of sent words and setting it to 0x7fe as soon as only that many
|
||||
// bytes are remaining. However, large transfer like this are not common. This
|
||||
// feature will therefore not be supported for now.
|
||||
if len > 0x7fe {
|
||||
return Err(Error::DataTooLarge);
|
||||
}
|
||||
// Clear the receive FIFO
|
||||
self.clear_rx_fifo();
|
||||
|
||||
// Load number of words
|
||||
self.i2c_base
|
||||
.i2c
|
||||
.words()
|
||||
.write(|w| unsafe { w.bits(len as u32) });
|
||||
let (addr, addr_mode_bit) = match addr {
|
||||
I2cAddress::Regular(addr) => (addr as u16, 0 << 15),
|
||||
I2cAddress::TenBit(addr) => (addr, 1 << 15),
|
||||
};
|
||||
// Load address
|
||||
self.i2c_base.i2c.address().write(|w| unsafe {
|
||||
w.bits(I2cDirection::Read as u32 | (addr << 1) as u32 | addr_mode_bit)
|
||||
});
|
||||
|
||||
let mut buf_iter = buffer.iter_mut();
|
||||
let mut read_bytes = 0;
|
||||
// Start receive transfer
|
||||
self.i2c_base
|
||||
.i2c
|
||||
.cmd()
|
||||
.write(|w| unsafe { w.bits(I2cCmd::StartWithStop as u32) });
|
||||
let mut read_if_next_available = || {
|
||||
if let Some(next_byte) = buf_iter.next() {
|
||||
*next_byte = self.read_fifo();
|
||||
}
|
||||
};
|
||||
loop {
|
||||
let status_reader = self.i2c_base.i2c.status().read();
|
||||
if status_reader.arblost().bit_is_set() {
|
||||
self.clear_rx_fifo();
|
||||
return Err(Error::ArbitrationLost);
|
||||
} else if status_reader.nackaddr().bit_is_set() {
|
||||
self.clear_rx_fifo();
|
||||
return Err(Error::NackAddr);
|
||||
} else if status_reader.idle().bit_is_set() {
|
||||
if read_bytes != len {
|
||||
return Err(Error::InsufficientDataReceived);
|
||||
}
|
||||
return Ok(());
|
||||
} else if status_reader.rxnempty().bit_is_set() {
|
||||
read_if_next_available();
|
||||
read_bytes += 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//======================================================================================
|
||||
// Embedded HAL I2C implementations
|
||||
//======================================================================================
|
||||
|
||||
impl embedded_hal::i2c::ErrorType for I2cMaster<$I2CX, SevenBitAddress> {
|
||||
type Error = Error;
|
||||
}
|
||||
impl embedded_hal::i2c::I2c for I2cMaster<$I2CX, SevenBitAddress> {
|
||||
fn transaction(
|
||||
&mut self,
|
||||
address: SevenBitAddress,
|
||||
operations: &mut [Operation<'_>],
|
||||
) -> Result<(), Self::Error> {
|
||||
for operation in operations {
|
||||
match operation {
|
||||
Operation::Read(buf) => self.read_internal(I2cAddress::Regular(address), buf)?,
|
||||
Operation::Write(buf) => self.write_from_buffer(
|
||||
I2cCmd::StartWithStop,
|
||||
I2cAddress::Regular(address),
|
||||
buf,
|
||||
)?,
|
||||
}
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
impl embedded_hal::i2c::ErrorType for I2cMaster<$I2CX, TenBitAddress> {
|
||||
type Error = Error;
|
||||
}
|
||||
impl embedded_hal::i2c::I2c<TenBitAddress> for I2cMaster<$I2CX, TenBitAddress> {
|
||||
fn transaction(
|
||||
&mut self,
|
||||
address: TenBitAddress,
|
||||
operations: &mut [Operation<'_>],
|
||||
) -> Result<(), Self::Error> {
|
||||
for operation in operations {
|
||||
match operation {
|
||||
Operation::Read(buf) => self.read_internal(I2cAddress::TenBit(address), buf)?,
|
||||
Operation::Write(buf) => self.write_from_buffer(
|
||||
I2cCmd::StartWithStop,
|
||||
I2cAddress::TenBit(address),
|
||||
buf,
|
||||
)?,
|
||||
}
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
)+
|
||||
}
|
||||
}
|
||||
|
||||
i2c_master!(
|
||||
pac::I2ca: (i2ca, PeripheralClocks::I2c0),
|
||||
pac::I2cb: (i2cb, PeripheralClocks::I2c1),
|
||||
);
|
||||
*/
|
||||
|
||||
//======================================================================================
|
||||
// Embedded HAL I2C implementations
|
||||
//======================================================================================
|
||||
|
@ -1,6 +1,7 @@
|
||||
#![no_std]
|
||||
#![cfg_attr(docsrs, feature(doc_auto_cfg))]
|
||||
|
||||
use gpio::Port;
|
||||
pub use va108xx;
|
||||
pub use va108xx as pac;
|
||||
|
||||
@ -19,6 +20,7 @@ pub mod uart;
|
||||
#[derive(Debug, Eq, Copy, Clone, PartialEq)]
|
||||
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
|
||||
pub enum FunSel {
|
||||
Sel0 = 0b00,
|
||||
Sel1 = 0b01,
|
||||
Sel2 = 0b10,
|
||||
Sel3 = 0b11,
|
||||
@ -26,13 +28,6 @@ pub enum FunSel {
|
||||
|
||||
#[derive(Debug, Copy, Clone, PartialEq, Eq)]
|
||||
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
|
||||
pub enum PortSel {
|
||||
PortA,
|
||||
PortB,
|
||||
}
|
||||
|
||||
#[derive(Copy, Clone, PartialEq, Eq)]
|
||||
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
|
||||
pub enum PeripheralSelect {
|
||||
PortA = 0,
|
||||
PortB = 1,
|
||||
@ -54,6 +49,7 @@ pub enum PeripheralSelect {
|
||||
/// Cortex-M0 NVIC. Both are generally necessary for IRQs to work, but the user might want to
|
||||
/// perform those steps themselves.
|
||||
#[derive(Debug, PartialEq, Eq, Clone, Copy)]
|
||||
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
|
||||
pub struct InterruptConfig {
|
||||
/// Interrupt target vector. Should always be set, might be required for disabling IRQs
|
||||
pub id: pac::Interrupt,
|
||||
@ -76,37 +72,33 @@ impl InterruptConfig {
|
||||
|
||||
pub type IrqCfg = InterruptConfig;
|
||||
|
||||
#[derive(Debug, PartialEq, Eq)]
|
||||
#[derive(Debug, PartialEq, Eq, thiserror::Error)]
|
||||
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
|
||||
pub struct InvalidPin(pub(crate) ());
|
||||
#[error("invalid pin with number {0}")]
|
||||
pub struct InvalidPinError(u8);
|
||||
|
||||
/// Can be used to manually manipulate the function select of port pins
|
||||
/// Can be used to manually manipulate the function select of port pins.
|
||||
///
|
||||
/// The function selection table can be found on p.36 of the programmers guide. Please note
|
||||
/// that most of the structures and APIs in this library will automatically correctly configure
|
||||
/// the pin or statically expect the correct pin type.
|
||||
pub fn port_function_select(
|
||||
ioconfig: &mut pac::Ioconfig,
|
||||
port: PortSel,
|
||||
port: Port,
|
||||
pin: u8,
|
||||
funsel: FunSel,
|
||||
) -> Result<(), InvalidPin> {
|
||||
match port {
|
||||
PortSel::PortA => {
|
||||
if pin > 31 {
|
||||
return Err(InvalidPin(()));
|
||||
}
|
||||
ioconfig
|
||||
.porta(pin as usize)
|
||||
.modify(|_, w| unsafe { w.funsel().bits(funsel as u8) });
|
||||
Ok(())
|
||||
}
|
||||
PortSel::PortB => {
|
||||
if pin > 23 {
|
||||
return Err(InvalidPin(()));
|
||||
}
|
||||
ioconfig
|
||||
.portb0(pin as usize)
|
||||
.modify(|_, w| unsafe { w.funsel().bits(funsel as u8) });
|
||||
Ok(())
|
||||
}
|
||||
) -> Result<(), InvalidPinError> {
|
||||
if (port == Port::A && pin >= 32) || (port == Port::B && pin >= 24) {
|
||||
return Err(InvalidPinError(pin));
|
||||
}
|
||||
|
||||
let reg_block = match port {
|
||||
Port::A => ioconfig.porta(pin as usize),
|
||||
Port::B => ioconfig.portb0(pin as usize),
|
||||
};
|
||||
|
||||
reg_block.modify(|_, w| unsafe { w.funsel().bits(funsel as u8) });
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Enable a specific interrupt using the NVIC peripheral.
|
||||
|
@ -33,6 +33,7 @@ use embedded_hal::spi::{Mode, MODE_0};
|
||||
const FILL_DEPTH: usize = 12;
|
||||
|
||||
pub const BMSTART_BMSTOP_MASK: u32 = 1 << 31;
|
||||
pub const BMSKIPDATA_MASK: u32 = 1 << 30;
|
||||
|
||||
pub const DEFAULT_CLK_DIV: u16 = 2;
|
||||
|
||||
@ -288,6 +289,7 @@ pub trait TransferConfigProvider {
|
||||
/// This struct contains all configuration parameter which are transfer specific
|
||||
/// and might change for transfers to different SPI slaves
|
||||
#[derive(Copy, Clone, Debug)]
|
||||
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
|
||||
pub struct TransferConfigWithHwcs<HwCs> {
|
||||
pub hw_cs: Option<HwCs>,
|
||||
pub cfg: TransferConfig,
|
||||
@ -296,6 +298,7 @@ pub struct TransferConfigWithHwcs<HwCs> {
|
||||
/// Type erased variant of the transfer configuration. This is required to avoid generics in
|
||||
/// the SPI constructor.
|
||||
#[derive(Copy, Clone, Debug)]
|
||||
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
|
||||
pub struct TransferConfig {
|
||||
pub clk_cfg: Option<SpiClkConfig>,
|
||||
pub mode: Option<Mode>,
|
||||
@ -383,6 +386,8 @@ impl<HwCs: HwCsProvider> TransferConfigProvider for TransferConfigWithHwcs<HwCs>
|
||||
}
|
||||
|
||||
/// Configuration options for the whole SPI bus. See Programmer Guide p.92 for more details
|
||||
#[derive(Debug)]
|
||||
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
|
||||
pub struct SpiConfig {
|
||||
clk: SpiClkConfig,
|
||||
// SPI mode configuration
|
||||
@ -532,6 +537,7 @@ pub struct Spi<SpiInstance, Pins, Word = u8> {
|
||||
pins: Pins,
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
pub fn mode_to_cpo_cph_bit(mode: embedded_hal::spi::Mode) -> (bool, bool) {
|
||||
match mode {
|
||||
embedded_hal::spi::MODE_0 => (false, false),
|
||||
@ -575,6 +581,7 @@ impl SpiClkConfig {
|
||||
}
|
||||
|
||||
#[derive(Debug, thiserror::Error)]
|
||||
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
|
||||
pub enum SpiClkConfigError {
|
||||
#[error("division by zero")]
|
||||
DivIsZero,
|
||||
|
@ -22,7 +22,7 @@ use crate::{
|
||||
};
|
||||
use embedded_hal_nb::serial::Read;
|
||||
|
||||
#[derive(Debug)]
|
||||
#[derive(Debug, Clone, Copy)]
|
||||
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
|
||||
pub enum Bank {
|
||||
A = 0,
|
||||
@ -381,6 +381,7 @@ pub struct BufferTooShortError {
|
||||
pub trait Instance: Deref<Target = uart_base::RegisterBlock> {
|
||||
const IDX: u8;
|
||||
const PERIPH_SEL: PeripheralSelect;
|
||||
const PTR: *const uart_base::RegisterBlock;
|
||||
|
||||
/// Retrieve the peripheral structure.
|
||||
///
|
||||
@ -388,7 +389,11 @@ pub trait Instance: Deref<Target = uart_base::RegisterBlock> {
|
||||
///
|
||||
/// This circumvents the safety guarantees of the HAL.
|
||||
unsafe fn steal() -> Self;
|
||||
fn ptr() -> *const uart_base::RegisterBlock;
|
||||
|
||||
#[inline(always)]
|
||||
fn ptr() -> *const uart_base::RegisterBlock {
|
||||
Self::PTR
|
||||
}
|
||||
|
||||
/// Retrieve the type erased peripheral register block.
|
||||
///
|
||||
@ -405,14 +410,11 @@ impl Instance for pac::Uarta {
|
||||
const IDX: u8 = 0;
|
||||
|
||||
const PERIPH_SEL: PeripheralSelect = PeripheralSelect::Uart0;
|
||||
const PTR: *const uart_base::RegisterBlock = Self::PTR;
|
||||
|
||||
#[inline(always)]
|
||||
unsafe fn steal() -> Self {
|
||||
pac::Peripherals::steal().uarta
|
||||
}
|
||||
#[inline(always)]
|
||||
fn ptr() -> *const uart_base::RegisterBlock {
|
||||
Self::ptr() as *const _
|
||||
Self::steal()
|
||||
}
|
||||
}
|
||||
|
||||
@ -420,14 +422,25 @@ impl Instance for pac::Uartb {
|
||||
const IDX: u8 = 1;
|
||||
|
||||
const PERIPH_SEL: PeripheralSelect = PeripheralSelect::Uart1;
|
||||
const PTR: *const uart_base::RegisterBlock = Self::PTR;
|
||||
|
||||
#[inline(always)]
|
||||
unsafe fn steal() -> Self {
|
||||
pac::Peripherals::steal().uartb
|
||||
Self::steal()
|
||||
}
|
||||
#[inline(always)]
|
||||
fn ptr() -> *const uart_base::RegisterBlock {
|
||||
Self::ptr() as *const _
|
||||
}
|
||||
|
||||
impl Bank {
|
||||
/// Retrieve the peripheral register block.
|
||||
///
|
||||
/// # Safety
|
||||
///
|
||||
/// Circumvents the HAL safety guarantees.
|
||||
pub unsafe fn reg_block(&self) -> &'static uart_base::RegisterBlock {
|
||||
match self {
|
||||
Bank::A => unsafe { pac::Uarta::reg_block() },
|
||||
Bank::B => unsafe { pac::Uartb::reg_block() },
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -794,14 +807,12 @@ pub fn disable_rx_interrupts(uart: &uart_base::RegisterBlock) {
|
||||
/// Serial receiver.
|
||||
///
|
||||
/// Can be created by using the [Uart::split] or [UartBase::split] API.
|
||||
pub struct Rx<Uart> {
|
||||
uart: Uart,
|
||||
}
|
||||
pub struct Rx<Uart>(Uart);
|
||||
|
||||
impl<Uart: Instance> Rx<Uart> {
|
||||
#[inline(always)]
|
||||
fn new(uart: Uart) -> Self {
|
||||
Self { uart }
|
||||
const fn new(uart: Uart) -> Self {
|
||||
Self(uart)
|
||||
}
|
||||
|
||||
/// Direct access to the peripheral structure.
|
||||
@ -810,13 +821,13 @@ impl<Uart: Instance> Rx<Uart> {
|
||||
///
|
||||
/// You must ensure that only registers related to the operation of the RX side are used.
|
||||
#[inline(always)]
|
||||
pub unsafe fn uart(&self) -> &Uart {
|
||||
&self.uart
|
||||
pub const unsafe fn uart(&self) -> &Uart {
|
||||
&self.0
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn clear_fifo(&self) {
|
||||
self.uart.fifo_clr().write(|w| w.rxfifo().set_bit());
|
||||
self.0.fifo_clr().write(|w| w.rxfifo().set_bit());
|
||||
}
|
||||
|
||||
#[inline]
|
||||
@ -846,7 +857,7 @@ impl<Uart: Instance> Rx<Uart> {
|
||||
/// value if you use the manual parity mode. See chapter 4.6.2 for more information.
|
||||
#[inline(always)]
|
||||
pub fn read_fifo(&self) -> nb::Result<u32, Infallible> {
|
||||
if self.uart.rxstatus().read().rdavl().bit_is_clear() {
|
||||
if self.0.rxstatus().read().rdavl().bit_is_clear() {
|
||||
return Err(nb::Error::WouldBlock);
|
||||
}
|
||||
Ok(self.read_fifo_unchecked())
|
||||
@ -862,7 +873,7 @@ impl<Uart: Instance> Rx<Uart> {
|
||||
/// value if you use the manual parity mode. See chapter 4.6.2 for more information.
|
||||
#[inline(always)]
|
||||
pub fn read_fifo_unchecked(&self) -> u32 {
|
||||
self.uart.data().read().bits()
|
||||
self.0.data().read().bits()
|
||||
}
|
||||
|
||||
pub fn into_rx_with_irq(self) -> RxWithInterrupt<Uart> {
|
||||
@ -871,7 +882,7 @@ impl<Uart: Instance> Rx<Uart> {
|
||||
|
||||
#[inline(always)]
|
||||
pub fn release(self) -> Uart {
|
||||
self.uart
|
||||
self.0
|
||||
}
|
||||
}
|
||||
|
||||
@ -959,9 +970,7 @@ pub fn disable_tx_interrupts(uart: &uart_base::RegisterBlock) {
|
||||
/// Serial transmitter
|
||||
///
|
||||
/// Can be created by using the [Uart::split] or [UartBase::split] API.
|
||||
pub struct Tx<Uart> {
|
||||
uart: Uart,
|
||||
}
|
||||
pub struct Tx<Uart>(Uart);
|
||||
|
||||
impl<Uart: Instance> Tx<Uart> {
|
||||
/// Retrieve a TX pin without expecting an explicit UART structure
|
||||
@ -971,14 +980,12 @@ impl<Uart: Instance> Tx<Uart> {
|
||||
/// Circumvents the HAL safety guarantees.
|
||||
#[inline(always)]
|
||||
pub unsafe fn steal() -> Self {
|
||||
Self {
|
||||
uart: Uart::steal(),
|
||||
}
|
||||
Self(Uart::steal())
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
fn new(uart: Uart) -> Self {
|
||||
Self { uart }
|
||||
Self(uart)
|
||||
}
|
||||
|
||||
/// Direct access to the peripheral structure.
|
||||
@ -987,25 +994,23 @@ impl<Uart: Instance> Tx<Uart> {
|
||||
///
|
||||
/// You must ensure that only registers related to the operation of the TX side are used.
|
||||
#[inline(always)]
|
||||
pub unsafe fn uart(&self) -> &Uart {
|
||||
&self.uart
|
||||
pub const unsafe fn uart(&self) -> &Uart {
|
||||
&self.0
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn clear_fifo(&self) {
|
||||
self.uart.fifo_clr().write(|w| w.txfifo().set_bit());
|
||||
self.0.fifo_clr().write(|w| w.txfifo().set_bit());
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn enable(&mut self) {
|
||||
// Safety: We own the UART structure
|
||||
enable_tx(unsafe { Uart::reg_block() });
|
||||
self.0.enable().modify(|_, w| w.txenable().set_bit());
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn disable(&mut self) {
|
||||
// Safety: We own the UART structure
|
||||
disable_tx(unsafe { Uart::reg_block() });
|
||||
self.0.enable().modify(|_, w| w.txenable().clear_bit());
|
||||
}
|
||||
|
||||
/// Enables the IRQ_TX, IRQ_TX_STATUS and IRQ_TX_EMPTY interrupts.
|
||||
@ -1037,7 +1042,7 @@ impl<Uart: Instance> Tx<Uart> {
|
||||
/// value if you use the manual parity mode. See chapter 11.4.1 for more information.
|
||||
#[inline(always)]
|
||||
pub fn write_fifo(&self, data: u32) -> nb::Result<(), Infallible> {
|
||||
if self.uart.txstatus().read().wrrdy().bit_is_clear() {
|
||||
if self.0.txstatus().read().wrrdy().bit_is_clear() {
|
||||
return Err(nb::Error::WouldBlock);
|
||||
}
|
||||
self.write_fifo_unchecked(data);
|
||||
@ -1052,7 +1057,7 @@ impl<Uart: Instance> Tx<Uart> {
|
||||
/// API.
|
||||
#[inline(always)]
|
||||
pub fn write_fifo_unchecked(&self, data: u32) {
|
||||
self.uart.data().write(|w| unsafe { w.bits(data) });
|
||||
self.0.data().write(|w| unsafe { w.bits(data) });
|
||||
}
|
||||
|
||||
pub fn into_async(self) -> TxAsync<Uart> {
|
||||
@ -1135,7 +1140,7 @@ impl<Uart: Instance> RxWithInterrupt<Uart> {
|
||||
|
||||
#[inline(always)]
|
||||
pub fn uart(&self) -> &Uart {
|
||||
&self.0.uart
|
||||
&self.0 .0
|
||||
}
|
||||
|
||||
/// This function is used together with the [Self::on_interrupt_max_size_or_timeout_based]
|
||||
|
@ -1,18 +1,16 @@
|
||||
//! # Async UART reception functionality for the VA108xx family.
|
||||
//! # Async UART reception functionality for the VA416xx family.
|
||||
//!
|
||||
//! This module provides the [RxAsync] and [RxAsyncSharedConsumer] struct which both implement the
|
||||
//! This module provides the [RxAsync] and [RxAsyncOverwriting] struct which both implement the
|
||||
//! [embedded_io_async::Read] trait.
|
||||
//! This trait allows for asynchronous reception of data streams. Please note that this module does
|
||||
//! not specify/declare the interrupt handlers which must be provided for async support to work.
|
||||
//! However, it provides four interrupt handlers:
|
||||
//! However, it provides two interrupt handlers:
|
||||
//!
|
||||
//! - [on_interrupt_uart_a]
|
||||
//! - [on_interrupt_uart_b]
|
||||
//! - [on_interrupt_uart_a_overwriting]
|
||||
//! - [on_interrupt_uart_b_overwriting]
|
||||
//! - [on_interrupt_rx]
|
||||
//! - [on_interrupt_rx_overwriting]
|
||||
//!
|
||||
//! The first two are used for the [RxAsync] struct, while the latter two are used with the
|
||||
//! [RxAsyncSharedConsumer] struct. The later two will overwrite old values in the used ring buffer.
|
||||
//! [RxAsyncOverwriting] struct. The later two will overwrite old values in the used ring buffer.
|
||||
//!
|
||||
//! Error handling is performed in the user interrupt handler by checking the [AsyncUartErrors]
|
||||
//! structure returned by the interrupt handlers.
|
||||
@ -25,11 +23,10 @@ use core::{cell::RefCell, convert::Infallible, future::Future, sync::atomic::Ord
|
||||
use critical_section::Mutex;
|
||||
use embassy_sync::waitqueue::AtomicWaker;
|
||||
use embedded_io::ErrorType;
|
||||
use heapless::spsc::Consumer;
|
||||
use portable_atomic::AtomicBool;
|
||||
use va108xx as pac;
|
||||
use va108xx::uarta as uart_base;
|
||||
|
||||
use super::{Instance, Rx, RxError, UartErrors};
|
||||
use super::{Bank, Instance, Rx, RxError, UartErrors};
|
||||
|
||||
static UART_RX_WAKERS: [AtomicWaker; 2] = [const { AtomicWaker::new() }; 2];
|
||||
static RX_READ_ACTIVE: [AtomicBool; 2] = [const { AtomicBool::new(false) }; 2];
|
||||
@ -72,7 +69,7 @@ pub struct AsyncUartErrors {
|
||||
pub uart_errors: UartErrors,
|
||||
}
|
||||
|
||||
fn on_interrupt_handle_rx_errors<Uart: Instance>(uart: &Uart) -> Option<UartErrors> {
|
||||
fn on_interrupt_handle_rx_errors(uart: &'static uart_base::RegisterBlock) -> Option<UartErrors> {
|
||||
let rx_status = uart.rxstatus().read();
|
||||
if rx_status.rxovr().bit_is_set()
|
||||
|| rx_status.rxfrm().bit_is_set()
|
||||
@ -94,81 +91,65 @@ fn on_interrupt_handle_rx_errors<Uart: Instance>(uart: &Uart) -> Option<UartErro
|
||||
None
|
||||
}
|
||||
|
||||
fn on_interrupt_rx_common_post_processing<Uart: Instance>(
|
||||
uart: &Uart,
|
||||
fn on_interrupt_rx_common_post_processing(
|
||||
bank: Bank,
|
||||
rx_enabled: bool,
|
||||
read_some_data: bool,
|
||||
irq_end: u32,
|
||||
) -> Option<UartErrors> {
|
||||
let idx = bank as usize;
|
||||
if read_some_data {
|
||||
RX_HAS_DATA[Uart::IDX as usize].store(true, Ordering::Relaxed);
|
||||
if RX_READ_ACTIVE[Uart::IDX as usize].load(Ordering::Relaxed) {
|
||||
UART_RX_WAKERS[Uart::IDX as usize].wake();
|
||||
RX_HAS_DATA[idx].store(true, Ordering::Relaxed);
|
||||
if RX_READ_ACTIVE[idx].load(Ordering::Relaxed) {
|
||||
UART_RX_WAKERS[idx].wake();
|
||||
}
|
||||
}
|
||||
|
||||
let mut errors = None;
|
||||
let uart_regs = unsafe { bank.reg_block() };
|
||||
// Check for RX errors
|
||||
if rx_enabled {
|
||||
errors = on_interrupt_handle_rx_errors(uart);
|
||||
errors = on_interrupt_handle_rx_errors(uart_regs);
|
||||
}
|
||||
|
||||
// Clear the interrupt status bits
|
||||
uart.irq_clr().write(|w| unsafe { w.bits(irq_end) });
|
||||
uart_regs.irq_clr().write(|w| unsafe { w.bits(irq_end) });
|
||||
errors
|
||||
}
|
||||
|
||||
/// Interrupt handler for UART A.
|
||||
/// Interrupt handler with overwriting behaviour when the ring buffer is full.
|
||||
///
|
||||
/// Should be called in the user interrupt handler to enable
|
||||
/// asynchronous reception. This variant will overwrite old data in the ring buffer in case
|
||||
/// the ring buffer is full.
|
||||
pub fn on_interrupt_uart_a_overwriting<const N: usize>(
|
||||
pub fn on_interrupt_rx_overwriting<const N: usize>(
|
||||
bank: Bank,
|
||||
prod: &mut heapless::spsc::Producer<u8, N>,
|
||||
shared_consumer: &Mutex<RefCell<Option<heapless::spsc::Consumer<'static, u8, N>>>>,
|
||||
) -> Result<(), AsyncUartErrors> {
|
||||
on_interrupt_rx_async_heapless_queue_overwriting(
|
||||
unsafe { pac::Uarta::steal() },
|
||||
prod,
|
||||
shared_consumer,
|
||||
)
|
||||
on_interrupt_rx_async_heapless_queue_overwriting(bank, prod, shared_consumer)
|
||||
}
|
||||
|
||||
/// Interrupt handler for UART B.
|
||||
///
|
||||
/// Should be called in the user interrupt handler to enable
|
||||
/// asynchronous reception. This variant will overwrite old data in the ring buffer in case
|
||||
/// the ring buffer is full.
|
||||
pub fn on_interrupt_uart_b_overwriting<const N: usize>(
|
||||
pub fn on_interrupt_rx_async_heapless_queue_overwriting<const N: usize>(
|
||||
bank: Bank,
|
||||
prod: &mut heapless::spsc::Producer<u8, N>,
|
||||
shared_consumer: &Mutex<RefCell<Option<heapless::spsc::Consumer<'static, u8, N>>>>,
|
||||
) -> Result<(), AsyncUartErrors> {
|
||||
on_interrupt_rx_async_heapless_queue_overwriting(
|
||||
unsafe { pac::Uartb::steal() },
|
||||
prod,
|
||||
shared_consumer,
|
||||
)
|
||||
}
|
||||
|
||||
pub fn on_interrupt_rx_async_heapless_queue_overwriting<Uart: Instance, const N: usize>(
|
||||
uart: Uart,
|
||||
prod: &mut heapless::spsc::Producer<u8, N>,
|
||||
shared_consumer: &Mutex<RefCell<Option<heapless::spsc::Consumer<'static, u8, N>>>>,
|
||||
) -> Result<(), AsyncUartErrors> {
|
||||
let irq_end = uart.irq_end().read();
|
||||
let enb_status = uart.enable().read();
|
||||
let uart_regs = unsafe { bank.reg_block() };
|
||||
let irq_end = uart_regs.irq_end().read();
|
||||
let enb_status = uart_regs.enable().read();
|
||||
let rx_enabled = enb_status.rxenable().bit_is_set();
|
||||
let mut read_some_data = false;
|
||||
let mut queue_overflow = false;
|
||||
|
||||
// Half-Full interrupt. We have a guaranteed amount of data we can read.
|
||||
if irq_end.irq_rx().bit_is_set() {
|
||||
let available_bytes = uart.rxfifoirqtrg().read().bits() as usize;
|
||||
let available_bytes = uart_regs.rxfifoirqtrg().read().bits() as usize;
|
||||
|
||||
// If this interrupt bit is set, the trigger level is available at the very least.
|
||||
// Read everything as fast as possible
|
||||
for _ in 0..available_bytes {
|
||||
let byte = uart.data().read().bits();
|
||||
let byte = uart_regs.data().read().bits();
|
||||
if !prod.ready() {
|
||||
queue_overflow = true;
|
||||
critical_section::with(|cs| {
|
||||
@ -183,9 +164,9 @@ pub fn on_interrupt_rx_async_heapless_queue_overwriting<Uart: Instance, const N:
|
||||
|
||||
// Timeout, empty the FIFO completely.
|
||||
if irq_end.irq_rx_to().bit_is_set() {
|
||||
while uart.rxstatus().read().rdavl().bit_is_set() {
|
||||
while uart_regs.rxstatus().read().rdavl().bit_is_set() {
|
||||
// While there is data in the FIFO, write it into the reception buffer
|
||||
let byte = uart.data().read().bits();
|
||||
let byte = uart_regs.data().read().bits();
|
||||
if !prod.ready() {
|
||||
queue_overflow = true;
|
||||
critical_section::with(|cs| {
|
||||
@ -199,7 +180,7 @@ pub fn on_interrupt_rx_async_heapless_queue_overwriting<Uart: Instance, const N:
|
||||
}
|
||||
|
||||
let uart_errors =
|
||||
on_interrupt_rx_common_post_processing(&uart, rx_enabled, read_some_data, irq_end.bits());
|
||||
on_interrupt_rx_common_post_processing(bank, rx_enabled, read_some_data, irq_end.bits());
|
||||
if uart_errors.is_some() || queue_overflow {
|
||||
return Err(AsyncUartErrors {
|
||||
queue_overflow,
|
||||
@ -209,29 +190,21 @@ pub fn on_interrupt_rx_async_heapless_queue_overwriting<Uart: Instance, const N:
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Interrupt handler for UART A.
|
||||
/// Interrupt handler for asynchronous RX operations.
|
||||
///
|
||||
/// Should be called in the user interrupt handler to enable asynchronous reception.
|
||||
pub fn on_interrupt_uart_a<const N: usize>(
|
||||
pub fn on_interrupt_rx<const N: usize>(
|
||||
bank: Bank,
|
||||
prod: &mut heapless::spsc::Producer<'_, u8, N>,
|
||||
) -> Result<(), AsyncUartErrors> {
|
||||
on_interrupt_rx_async_heapless_queue(unsafe { pac::Uarta::steal() }, prod)
|
||||
on_interrupt_rx_async_heapless_queue(bank, prod)
|
||||
}
|
||||
|
||||
/// Interrupt handler for UART B.
|
||||
///
|
||||
/// Should be called in the user interrupt handler to enable asynchronous reception.
|
||||
pub fn on_interrupt_uart_b<const N: usize>(
|
||||
pub fn on_interrupt_rx_async_heapless_queue<const N: usize>(
|
||||
bank: Bank,
|
||||
prod: &mut heapless::spsc::Producer<'_, u8, N>,
|
||||
) -> Result<(), AsyncUartErrors> {
|
||||
on_interrupt_rx_async_heapless_queue(unsafe { pac::Uartb::steal() }, prod)
|
||||
}
|
||||
|
||||
pub fn on_interrupt_rx_async_heapless_queue<Uart: Instance, const N: usize>(
|
||||
uart: Uart,
|
||||
prod: &mut heapless::spsc::Producer<'_, u8, N>,
|
||||
) -> Result<(), AsyncUartErrors> {
|
||||
//let uart = unsafe { Uart::steal() };
|
||||
let uart = unsafe { bank.reg_block() };
|
||||
let irq_end = uart.irq_end().read();
|
||||
let enb_status = uart.enable().read();
|
||||
let rx_enabled = enb_status.rxenable().bit_is_set();
|
||||
@ -268,7 +241,7 @@ pub fn on_interrupt_rx_async_heapless_queue<Uart: Instance, const N: usize>(
|
||||
}
|
||||
|
||||
let uart_errors =
|
||||
on_interrupt_rx_common_post_processing(&uart, rx_enabled, read_some_data, irq_end.bits());
|
||||
on_interrupt_rx_common_post_processing(bank, rx_enabled, read_some_data, irq_end.bits());
|
||||
if uart_errors.is_some() || queue_overflow {
|
||||
return Err(AsyncUartErrors {
|
||||
queue_overflow,
|
||||
@ -286,24 +259,32 @@ impl Drop for ActiveReadGuard {
|
||||
}
|
||||
}
|
||||
|
||||
/// Core data structure to allow asynchronous UART reception.
|
||||
///
|
||||
/// If the ring buffer becomes full, data will be lost.
|
||||
pub struct RxAsync<Uart: Instance, const N: usize> {
|
||||
struct RxAsyncInner<Uart: Instance, const N: usize> {
|
||||
rx: Rx<Uart>,
|
||||
pub queue: heapless::spsc::Consumer<'static, u8, N>,
|
||||
}
|
||||
|
||||
/// Core data structure to allow asynchronous UART reception.
|
||||
///
|
||||
/// If the ring buffer becomes full, data will be lost.
|
||||
pub struct RxAsync<Uart: Instance, const N: usize>(Option<RxAsyncInner<Uart, N>>);
|
||||
|
||||
impl<Uart: Instance, const N: usize> ErrorType for RxAsync<Uart, N> {
|
||||
/// Error reporting is done using the result of the interrupt functions.
|
||||
type Error = Infallible;
|
||||
}
|
||||
|
||||
fn stop_async_rx<Uart: Instance>(rx: &mut Rx<Uart>) {
|
||||
rx.disable_interrupts();
|
||||
rx.disable();
|
||||
rx.clear_fifo();
|
||||
}
|
||||
|
||||
impl<Uart: Instance, const N: usize> RxAsync<Uart, N> {
|
||||
/// Create a new asynchronous receiver.
|
||||
///
|
||||
/// The passed [heapless::spsc::Consumer] will be used to asynchronously receive data which
|
||||
/// is filled by the interrupt handler.
|
||||
/// is filled by the interrupt handler [on_interrupt_rx].
|
||||
pub fn new(mut rx: Rx<Uart>, queue: heapless::spsc::Consumer<'static, u8, N>) -> Self {
|
||||
rx.disable_interrupts();
|
||||
rx.disable();
|
||||
@ -313,7 +294,23 @@ impl<Uart: Instance, const N: usize> RxAsync<Uart, N> {
|
||||
rx.enable_interrupts();
|
||||
rx.enable();
|
||||
});
|
||||
Self { rx, queue }
|
||||
Self(Some(RxAsyncInner { rx, queue }))
|
||||
}
|
||||
|
||||
pub fn stop(&mut self) {
|
||||
stop_async_rx(&mut self.0.as_mut().unwrap().rx);
|
||||
}
|
||||
|
||||
pub fn release(mut self) -> (Rx<Uart>, heapless::spsc::Consumer<'static, u8, N>) {
|
||||
self.stop();
|
||||
let inner = self.0.take().unwrap();
|
||||
(inner.rx, inner.queue)
|
||||
}
|
||||
}
|
||||
|
||||
impl<Uart: Instance, const N: usize> Drop for RxAsync<Uart, N> {
|
||||
fn drop(&mut self) {
|
||||
self.stop();
|
||||
}
|
||||
}
|
||||
|
||||
@ -321,7 +318,7 @@ impl<Uart: Instance, const N: usize> embedded_io_async::Read for RxAsync<Uart, N
|
||||
async fn read(&mut self, buf: &mut [u8]) -> Result<usize, Self::Error> {
|
||||
// Need to wait for the IRQ to read data and set this flag. If the queue is not
|
||||
// empty, we can read data immediately.
|
||||
if self.queue.len() == 0 {
|
||||
if self.0.as_ref().unwrap().queue.len() == 0 {
|
||||
RX_HAS_DATA[Uart::IDX as usize].store(false, Ordering::Relaxed);
|
||||
}
|
||||
let _guard = ActiveReadGuard(Uart::IDX as usize);
|
||||
@ -333,33 +330,38 @@ impl<Uart: Instance, const N: usize> embedded_io_async::Read for RxAsync<Uart, N
|
||||
}
|
||||
data_to_read
|
||||
};
|
||||
let fut = RxFuture::new(&mut self.rx);
|
||||
let mut_ref = self.0.as_mut().unwrap();
|
||||
let fut = RxFuture::new(&mut mut_ref.rx);
|
||||
// Data is available, so read that data immediately.
|
||||
let read_data = handle_data_in_queue(&mut self.queue);
|
||||
let read_data = handle_data_in_queue(&mut mut_ref.queue);
|
||||
if read_data > 0 {
|
||||
return Ok(read_data);
|
||||
}
|
||||
// Await data.
|
||||
let _ = fut.await;
|
||||
Ok(handle_data_in_queue(&mut self.queue))
|
||||
Ok(handle_data_in_queue(&mut mut_ref.queue))
|
||||
}
|
||||
}
|
||||
|
||||
struct RxAsyncOverwritingInner<Uart: Instance, const N: usize> {
|
||||
rx: Rx<Uart>,
|
||||
pub shared_consumer: &'static Mutex<RefCell<Option<heapless::spsc::Consumer<'static, u8, N>>>>,
|
||||
}
|
||||
|
||||
/// Core data structure to allow asynchronous UART reception.
|
||||
///
|
||||
/// If the ring buffer becomes full, the oldest data will be overwritten when using the
|
||||
/// [on_interrupt_uart_a_overwriting] and [on_interrupt_uart_b_overwriting] interrupt handlers.
|
||||
pub struct RxAsyncSharedConsumer<Uart: Instance, const N: usize> {
|
||||
rx: Rx<Uart>,
|
||||
queue: &'static Mutex<RefCell<Option<Consumer<'static, u8, N>>>>,
|
||||
}
|
||||
/// [on_interrupt_rx_overwriting] interrupt handlers.
|
||||
pub struct RxAsyncOverwriting<Uart: Instance, const N: usize>(
|
||||
Option<RxAsyncOverwritingInner<Uart, N>>,
|
||||
);
|
||||
|
||||
impl<Uart: Instance, const N: usize> ErrorType for RxAsyncSharedConsumer<Uart, N> {
|
||||
impl<Uart: Instance, const N: usize> ErrorType for RxAsyncOverwriting<Uart, N> {
|
||||
/// Error reporting is done using the result of the interrupt functions.
|
||||
type Error = Infallible;
|
||||
}
|
||||
|
||||
impl<Uart: Instance, const N: usize> RxAsyncSharedConsumer<Uart, N> {
|
||||
impl<Uart: Instance, const N: usize> RxAsyncOverwriting<Uart, N> {
|
||||
/// Create a new asynchronous receiver.
|
||||
///
|
||||
/// The passed shared [heapless::spsc::Consumer] will be used to asynchronously receive data
|
||||
@ -367,7 +369,7 @@ impl<Uart: Instance, const N: usize> RxAsyncSharedConsumer<Uart, N> {
|
||||
/// interrupt handler to overwrite old data.
|
||||
pub fn new(
|
||||
mut rx: Rx<Uart>,
|
||||
queue: &'static Mutex<RefCell<Option<heapless::spsc::Consumer<'static, u8, N>>>>,
|
||||
shared_consumer: &'static Mutex<RefCell<Option<heapless::spsc::Consumer<'static, u8, N>>>>,
|
||||
) -> Self {
|
||||
rx.disable_interrupts();
|
||||
rx.disable();
|
||||
@ -377,25 +379,44 @@ impl<Uart: Instance, const N: usize> RxAsyncSharedConsumer<Uart, N> {
|
||||
rx.enable_interrupts();
|
||||
rx.enable();
|
||||
});
|
||||
Self { rx, queue }
|
||||
Self(Some(RxAsyncOverwritingInner {
|
||||
rx,
|
||||
shared_consumer,
|
||||
}))
|
||||
}
|
||||
|
||||
pub fn stop(&mut self) {
|
||||
stop_async_rx(&mut self.0.as_mut().unwrap().rx);
|
||||
}
|
||||
|
||||
pub fn release(mut self) -> Rx<Uart> {
|
||||
self.stop();
|
||||
let inner = self.0.take().unwrap();
|
||||
inner.rx
|
||||
}
|
||||
}
|
||||
|
||||
impl<Uart: Instance, const N: usize> embedded_io_async::Read for RxAsyncSharedConsumer<Uart, N> {
|
||||
impl<Uart: Instance, const N: usize> Drop for RxAsyncOverwriting<Uart, N> {
|
||||
fn drop(&mut self) {
|
||||
self.stop();
|
||||
}
|
||||
}
|
||||
|
||||
impl<Uart: Instance, const N: usize> embedded_io_async::Read for RxAsyncOverwriting<Uart, N> {
|
||||
async fn read(&mut self, buf: &mut [u8]) -> Result<usize, Self::Error> {
|
||||
// Need to wait for the IRQ to read data and set this flag. If the queue is not
|
||||
// empty, we can read data immediately.
|
||||
|
||||
critical_section::with(|cs| {
|
||||
let queue = self.queue.borrow(cs);
|
||||
let queue = self.0.as_ref().unwrap().shared_consumer.borrow(cs);
|
||||
if queue.borrow().as_ref().unwrap().len() == 0 {
|
||||
RX_HAS_DATA[Uart::IDX as usize].store(false, Ordering::Relaxed);
|
||||
}
|
||||
});
|
||||
let _guard = ActiveReadGuard(Uart::IDX as usize);
|
||||
let mut handle_data_in_queue = || {
|
||||
let mut handle_data_in_queue = |inner: &mut RxAsyncOverwritingInner<Uart, N>| {
|
||||
critical_section::with(|cs| {
|
||||
let mut consumer_ref = self.queue.borrow(cs).borrow_mut();
|
||||
let mut consumer_ref = inner.shared_consumer.borrow(cs).borrow_mut();
|
||||
let consumer = consumer_ref.as_mut().unwrap();
|
||||
let data_to_read = consumer.len().min(buf.len());
|
||||
for byte in buf.iter_mut().take(data_to_read) {
|
||||
@ -405,15 +426,15 @@ impl<Uart: Instance, const N: usize> embedded_io_async::Read for RxAsyncSharedCo
|
||||
data_to_read
|
||||
})
|
||||
};
|
||||
let fut = RxFuture::new(&mut self.rx);
|
||||
let fut = RxFuture::new(&mut self.0.as_mut().unwrap().rx);
|
||||
// Data is available, so read that data immediately.
|
||||
let read_data = handle_data_in_queue();
|
||||
let read_data = handle_data_in_queue(self.0.as_mut().unwrap());
|
||||
if read_data > 0 {
|
||||
return Ok(read_data);
|
||||
}
|
||||
// Await data.
|
||||
let _ = fut.await;
|
||||
let read_data = handle_data_in_queue();
|
||||
let read_data = handle_data_in_queue(self.0.as_mut().unwrap());
|
||||
Ok(read_data)
|
||||
}
|
||||
}
|
||||
|
@ -3,13 +3,10 @@
|
||||
//! This module provides the [TxAsync] struct which implements the [embedded_io_async::Write] trait.
|
||||
//! This trait allows for asynchronous sending of data streams. Please note that this module does
|
||||
//! not specify/declare the interrupt handlers which must be provided for async support to work.
|
||||
//! However, it provides two interrupt handlers:
|
||||
//! However, it the [on_interrupt_tx] interrupt handler.
|
||||
//!
|
||||
//! - [on_interrupt_uart_a_tx]
|
||||
//! - [on_interrupt_uart_b_tx]
|
||||
//!
|
||||
//! Those should be called in ALL user interrupt handlers which handle UART TX interrupts,
|
||||
//! depending on which UARTs are used.
|
||||
//! This handler should be called in ALL user interrupt handlers which handle UART TX interrupts
|
||||
//! for a given UART bank.
|
||||
//!
|
||||
//! # Example
|
||||
//!
|
||||
@ -30,21 +27,14 @@ static TX_CONTEXTS: [Mutex<RefCell<TxContext>>; 2] =
|
||||
// critical section.
|
||||
static TX_DONE: [AtomicBool; 2] = [const { AtomicBool::new(false) }; 2];
|
||||
|
||||
/// This is a generic interrupt handler to handle asynchronous UART TX operations. The user
|
||||
/// has to call this once in the interrupt handler responsible for UART A TX interrupts for
|
||||
/// asynchronous operations to work.
|
||||
pub fn on_interrupt_uart_a_tx() {
|
||||
on_interrupt_uart_tx(unsafe { pac::Uarta::steal() });
|
||||
}
|
||||
|
||||
/// This is a generic interrupt handler to handle asynchronous UART TX operations. The user
|
||||
/// has to call this once in the interrupt handler responsible for UART B TX interrupts for
|
||||
/// asynchronous operations to work.
|
||||
pub fn on_interrupt_uart_b_tx() {
|
||||
on_interrupt_uart_tx(unsafe { pac::Uartb::steal() });
|
||||
}
|
||||
|
||||
fn on_interrupt_uart_tx<Uart: Instance>(uart: Uart) {
|
||||
/// This is a generic interrupt handler to handle asynchronous UART TX operations for a given
|
||||
/// UART bank.
|
||||
///
|
||||
/// The user has to call this once in the interrupt handler responsible for the TX interrupts on
|
||||
/// the given UART bank.
|
||||
pub fn on_interrupt_tx(bank: Bank) {
|
||||
let uart = unsafe { bank.reg_block() };
|
||||
let idx = bank as usize;
|
||||
let irq_enb = uart.irq_enb().read();
|
||||
// IRQ is not related to TX.
|
||||
if irq_enb.irq_tx().bit_is_clear() || irq_enb.irq_tx_empty().bit_is_clear() {
|
||||
@ -54,7 +44,7 @@ fn on_interrupt_uart_tx<Uart: Instance>(uart: Uart) {
|
||||
let tx_status = uart.txstatus().read();
|
||||
let unexpected_overrun = tx_status.wrlost().bit_is_set();
|
||||
let mut context = critical_section::with(|cs| {
|
||||
let context_ref = TX_CONTEXTS[Uart::IDX as usize].borrow(cs);
|
||||
let context_ref = TX_CONTEXTS[idx].borrow(cs);
|
||||
*context_ref.borrow()
|
||||
});
|
||||
context.tx_overrun = unexpected_overrun;
|
||||
@ -67,12 +57,12 @@ fn on_interrupt_uart_tx<Uart: Instance>(uart: Uart) {
|
||||
uart.enable().modify(|_, w| w.txenable().clear_bit());
|
||||
// Write back updated context structure.
|
||||
critical_section::with(|cs| {
|
||||
let context_ref = TX_CONTEXTS[Uart::IDX as usize].borrow(cs);
|
||||
let context_ref = TX_CONTEXTS[idx].borrow(cs);
|
||||
*context_ref.borrow_mut() = context;
|
||||
});
|
||||
// Transfer is done.
|
||||
TX_DONE[Uart::IDX as usize].store(true, core::sync::atomic::Ordering::Relaxed);
|
||||
UART_TX_WAKERS[Uart::IDX as usize].wake();
|
||||
TX_DONE[idx].store(true, core::sync::atomic::Ordering::Relaxed);
|
||||
UART_TX_WAKERS[idx].wake();
|
||||
return;
|
||||
}
|
||||
// Safety: We documented that the user provided slice must outlive the future, so we convert
|
||||
@ -92,7 +82,7 @@ fn on_interrupt_uart_tx<Uart: Instance>(uart: Uart) {
|
||||
|
||||
// Write back updated context structure.
|
||||
critical_section::with(|cs| {
|
||||
let context_ref = TX_CONTEXTS[Uart::IDX as usize].borrow(cs);
|
||||
let context_ref = TX_CONTEXTS[idx].borrow(cs);
|
||||
*context_ref.borrow_mut() = context;
|
||||
});
|
||||
}
|
||||
|
@ -8,6 +8,10 @@ and this project adheres to [Semantic Versioning](http://semver.org/).
|
||||
|
||||
## [unreleased]
|
||||
|
||||
## [v0.5.0] 2025-02-17
|
||||
|
||||
- Re-generated PAC with `svd2rust` v0.35.0 and added optional `defmt` and `Debug` implementations
|
||||
|
||||
## [v0.4.0] 2025-02-12
|
||||
|
||||
- Re-generated PAC with `svd2rust` v0.35.0
|
||||
|
@ -1,6 +1,6 @@
|
||||
[package]
|
||||
name = "va108xx"
|
||||
version = "0.4.0"
|
||||
version = "0.5.0"
|
||||
authors = ["Robin Mueller <muellerr@irs.uni-stuttgart.de>"]
|
||||
edition = "2021"
|
||||
description = "PAC for the Vorago VA108xx family of microcontrollers"
|
||||
@ -13,6 +13,7 @@ categories = ["embedded", "no-std", "hardware-support"]
|
||||
[dependencies]
|
||||
cortex-m = "0.7"
|
||||
vcell = "0.1.3"
|
||||
defmt = { version = "0.3", optional = true }
|
||||
critical-section = { version = "1", optional = true }
|
||||
|
||||
[dependencies.cortex-m-rt]
|
||||
@ -21,6 +22,8 @@ version = ">=0.6.15,<0.8"
|
||||
|
||||
[features]
|
||||
rt = ["cortex-m-rt/device"]
|
||||
# Adds Debug implementation
|
||||
debug = []
|
||||
|
||||
[package.metadata.docs.rs]
|
||||
all-features = true
|
||||
|
@ -26,6 +26,12 @@ The `rt` feature is optional and recommended. It brings in support for `cortex-m
|
||||
For full details on the autgenerated API, please see the
|
||||
[svd2rust documentation](https://docs.rs/svd2rust/latest/svd2rust/#peripheral-api).
|
||||
|
||||
## Optional Features
|
||||
|
||||
- [`defmt`](https://defmt.ferrous-systems.com/): Add support for `defmt` by adding the
|
||||
[`defmt::Format`](https://defmt.ferrous-systems.com/format) derive on many types.
|
||||
- `debug`: Add `Debug` derives for various structures
|
||||
|
||||
## Regenerating the PAC
|
||||
|
||||
If you want to re-generate the PAC, for example if the register file `va416xx.svd` changes
|
||||
|
@ -30,7 +30,7 @@ fi
|
||||
|
||||
svdtools patch svd/va108xx-patch.yml
|
||||
# See https://github.com/rust-embedded/svd2rust/issues/830 for required re-export.
|
||||
${svd2rust_bin} --reexport-interrupt -i svd/va108xx.svd.patched
|
||||
${svd2rust_bin} --reexport-interrupt --impl-defmt defmt --impl-debug-feature debug -i svd/va108xx.svd.patched
|
||||
|
||||
result=$?
|
||||
if [ $result -ne 0 ]; then
|
||||
|
@ -2,6 +2,7 @@
|
||||
pub type R = crate::R<AddressSpec>;
|
||||
#[doc = "Register `ADDRESS` writer"]
|
||||
pub type W = crate::W<AddressSpec>;
|
||||
#[cfg(feature = "debug")]
|
||||
impl core::fmt::Debug for R {
|
||||
fn fmt(&self, f: &mut core::fmt::Formatter) -> core::fmt::Result {
|
||||
write!(f, "{}", self.bits())
|
||||
|
@ -2,6 +2,7 @@
|
||||
pub type R = crate::R<ClktolimitSpec>;
|
||||
#[doc = "Register `CLKTOLIMIT` writer"]
|
||||
pub type W = crate::W<ClktolimitSpec>;
|
||||
#[cfg(feature = "debug")]
|
||||
impl core::fmt::Debug for R {
|
||||
fn fmt(&self, f: &mut core::fmt::Formatter) -> core::fmt::Result {
|
||||
write!(f, "{}", self.bits())
|
||||
|
@ -2,6 +2,7 @@
|
||||
pub type R = crate::R<CmdSpec>;
|
||||
#[doc = "Register `CMD` writer"]
|
||||
pub type W = crate::W<CmdSpec>;
|
||||
#[cfg(feature = "debug")]
|
||||
impl core::fmt::Debug for R {
|
||||
fn fmt(&self, f: &mut core::fmt::Formatter) -> core::fmt::Result {
|
||||
write!(f, "{}", self.bits())
|
||||
|
@ -2,6 +2,7 @@
|
||||
pub type R = crate::R<DataSpec>;
|
||||
#[doc = "Register `DATA` writer"]
|
||||
pub type W = crate::W<DataSpec>;
|
||||
#[cfg(feature = "debug")]
|
||||
impl core::fmt::Debug for R {
|
||||
fn fmt(&self, f: &mut core::fmt::Formatter) -> core::fmt::Result {
|
||||
write!(f, "{}", self.bits())
|
||||
|
@ -1,5 +1,6 @@
|
||||
#[doc = "Register `PERID` reader"]
|
||||
pub type R = crate::R<PeridSpec>;
|
||||
#[cfg(feature = "debug")]
|
||||
impl core::fmt::Debug for R {
|
||||
fn fmt(&self, f: &mut core::fmt::Formatter) -> core::fmt::Result {
|
||||
write!(f, "{}", self.bits())
|
||||
|
@ -1,5 +1,6 @@
|
||||
#[doc = "Register `RXCOUNT` reader"]
|
||||
pub type R = crate::R<RxcountSpec>;
|
||||
#[cfg(feature = "debug")]
|
||||
impl core::fmt::Debug for R {
|
||||
fn fmt(&self, f: &mut core::fmt::Formatter) -> core::fmt::Result {
|
||||
write!(f, "{}", self.bits())
|
||||
|
@ -2,6 +2,7 @@
|
||||
pub type R = crate::R<RxfifoirqtrgSpec>;
|
||||
#[doc = "Register `RXFIFOIRQTRG` writer"]
|
||||
pub type W = crate::W<RxfifoirqtrgSpec>;
|
||||
#[cfg(feature = "debug")]
|
||||
impl core::fmt::Debug for R {
|
||||
fn fmt(&self, f: &mut core::fmt::Formatter) -> core::fmt::Result {
|
||||
write!(f, "{}", self.bits())
|
||||
|
@ -2,6 +2,7 @@
|
||||
pub type R = crate::R<S0AddressSpec>;
|
||||
#[doc = "Register `S0_ADDRESS` writer"]
|
||||
pub type W = crate::W<S0AddressSpec>;
|
||||
#[cfg(feature = "debug")]
|
||||
impl core::fmt::Debug for R {
|
||||
fn fmt(&self, f: &mut core::fmt::Formatter) -> core::fmt::Result {
|
||||
write!(f, "{}", self.bits())
|
||||
|
@ -2,6 +2,7 @@
|
||||
pub type R = crate::R<S0AddressbSpec>;
|
||||
#[doc = "Register `S0_ADDRESSB` writer"]
|
||||
pub type W = crate::W<S0AddressbSpec>;
|
||||
#[cfg(feature = "debug")]
|
||||
impl core::fmt::Debug for R {
|
||||
fn fmt(&self, f: &mut core::fmt::Formatter) -> core::fmt::Result {
|
||||
write!(f, "{}", self.bits())
|
||||
|
@ -2,6 +2,7 @@
|
||||
pub type R = crate::R<S0AddressmaskSpec>;
|
||||
#[doc = "Register `S0_ADDRESSMASK` writer"]
|
||||
pub type W = crate::W<S0AddressmaskSpec>;
|
||||
#[cfg(feature = "debug")]
|
||||
impl core::fmt::Debug for R {
|
||||
fn fmt(&self, f: &mut core::fmt::Formatter) -> core::fmt::Result {
|
||||
write!(f, "{}", self.bits())
|
||||
|
@ -2,6 +2,7 @@
|
||||
pub type R = crate::R<S0AddressmaskbSpec>;
|
||||
#[doc = "Register `S0_ADDRESSMASKB` writer"]
|
||||
pub type W = crate::W<S0AddressmaskbSpec>;
|
||||
#[cfg(feature = "debug")]
|
||||
impl core::fmt::Debug for R {
|
||||
fn fmt(&self, f: &mut core::fmt::Formatter) -> core::fmt::Result {
|
||||
write!(f, "{}", self.bits())
|
||||
|
@ -2,6 +2,7 @@
|
||||
pub type R = crate::R<S0DataSpec>;
|
||||
#[doc = "Register `S0_DATA` writer"]
|
||||
pub type W = crate::W<S0DataSpec>;
|
||||
#[cfg(feature = "debug")]
|
||||
impl core::fmt::Debug for R {
|
||||
fn fmt(&self, f: &mut core::fmt::Formatter) -> core::fmt::Result {
|
||||
write!(f, "{}", self.bits())
|
||||
|
@ -1,5 +1,6 @@
|
||||
#[doc = "Register `S0_LASTADDRESS` reader"]
|
||||
pub type R = crate::R<S0LastaddressSpec>;
|
||||
#[cfg(feature = "debug")]
|
||||
impl core::fmt::Debug for R {
|
||||
fn fmt(&self, f: &mut core::fmt::Formatter) -> core::fmt::Result {
|
||||
write!(f, "{}", self.bits())
|
||||
|
@ -2,6 +2,7 @@
|
||||
pub type R = crate::R<S0MaxwordsSpec>;
|
||||
#[doc = "Register `S0_MAXWORDS` writer"]
|
||||
pub type W = crate::W<S0MaxwordsSpec>;
|
||||
#[cfg(feature = "debug")]
|
||||
impl core::fmt::Debug for R {
|
||||
fn fmt(&self, f: &mut core::fmt::Formatter) -> core::fmt::Result {
|
||||
write!(f, "{}", self.bits())
|
||||
|
@ -1,5 +1,6 @@
|
||||
#[doc = "Register `S0_RXCOUNT` reader"]
|
||||
pub type R = crate::R<S0RxcountSpec>;
|
||||
#[cfg(feature = "debug")]
|
||||
impl core::fmt::Debug for R {
|
||||
fn fmt(&self, f: &mut core::fmt::Formatter) -> core::fmt::Result {
|
||||
write!(f, "{}", self.bits())
|
||||
|
@ -2,6 +2,7 @@
|
||||
pub type R = crate::R<S0RxfifoirqtrgSpec>;
|
||||
#[doc = "Register `S0_RXFIFOIRQTRG` writer"]
|
||||
pub type W = crate::W<S0RxfifoirqtrgSpec>;
|
||||
#[cfg(feature = "debug")]
|
||||
impl core::fmt::Debug for R {
|
||||
fn fmt(&self, f: &mut core::fmt::Formatter) -> core::fmt::Result {
|
||||
write!(f, "{}", self.bits())
|
||||
|
@ -1,5 +1,6 @@
|
||||
#[doc = "Register `S0_STATE` reader"]
|
||||
pub type R = crate::R<S0StateSpec>;
|
||||
#[cfg(feature = "debug")]
|
||||
impl core::fmt::Debug for R {
|
||||
fn fmt(&self, f: &mut core::fmt::Formatter) -> core::fmt::Result {
|
||||
write!(f, "{}", self.bits())
|
||||
|
@ -1,5 +1,6 @@
|
||||
#[doc = "Register `S0_TXCOUNT` reader"]
|
||||
pub type R = crate::R<S0TxcountSpec>;
|
||||
#[cfg(feature = "debug")]
|
||||
impl core::fmt::Debug for R {
|
||||
fn fmt(&self, f: &mut core::fmt::Formatter) -> core::fmt::Result {
|
||||
write!(f, "{}", self.bits())
|
||||
|
@ -2,6 +2,7 @@
|
||||
pub type R = crate::R<S0TxfifoirqtrgSpec>;
|
||||
#[doc = "Register `S0_TXFIFOIRQTRG` writer"]
|
||||
pub type W = crate::W<S0TxfifoirqtrgSpec>;
|
||||
#[cfg(feature = "debug")]
|
||||
impl core::fmt::Debug for R {
|
||||
fn fmt(&self, f: &mut core::fmt::Formatter) -> core::fmt::Result {
|
||||
write!(f, "{}", self.bits())
|
||||
|
@ -1,5 +1,6 @@
|
||||
#[doc = "Register `STATE` reader"]
|
||||
pub type R = crate::R<StateSpec>;
|
||||
#[cfg(feature = "debug")]
|
||||
impl core::fmt::Debug for R {
|
||||
fn fmt(&self, f: &mut core::fmt::Formatter) -> core::fmt::Result {
|
||||
write!(f, "{}", self.bits())
|
||||
|
@ -2,6 +2,7 @@
|
||||
pub type R = crate::R<TmconfigSpec>;
|
||||
#[doc = "Register `TMCONFIG` writer"]
|
||||
pub type W = crate::W<TmconfigSpec>;
|
||||
#[cfg(feature = "debug")]
|
||||
impl core::fmt::Debug for R {
|
||||
fn fmt(&self, f: &mut core::fmt::Formatter) -> core::fmt::Result {
|
||||
write!(f, "{}", self.bits())
|
||||
|
@ -1,5 +1,6 @@
|
||||
#[doc = "Register `TXCOUNT` reader"]
|
||||
pub type R = crate::R<TxcountSpec>;
|
||||
#[cfg(feature = "debug")]
|
||||
impl core::fmt::Debug for R {
|
||||
fn fmt(&self, f: &mut core::fmt::Formatter) -> core::fmt::Result {
|
||||
write!(f, "{}", self.bits())
|
||||
|
@ -2,6 +2,7 @@
|
||||
pub type R = crate::R<TxfifoirqtrgSpec>;
|
||||
#[doc = "Register `TXFIFOIRQTRG` writer"]
|
||||
pub type W = crate::W<TxfifoirqtrgSpec>;
|
||||
#[cfg(feature = "debug")]
|
||||
impl core::fmt::Debug for R {
|
||||
fn fmt(&self, f: &mut core::fmt::Formatter) -> core::fmt::Result {
|
||||
write!(f, "{}", self.bits())
|
||||
|
@ -2,6 +2,7 @@
|
||||
pub type R = crate::R<WordsSpec>;
|
||||
#[doc = "Register `WORDS` writer"]
|
||||
pub type W = crate::W<WordsSpec>;
|
||||
#[cfg(feature = "debug")]
|
||||
impl core::fmt::Debug for R {
|
||||
fn fmt(&self, f: &mut core::fmt::Formatter) -> core::fmt::Result {
|
||||
write!(f, "{}", self.bits())
|
||||
|
@ -1,5 +1,6 @@
|
||||
#[doc = "Register `PERID` reader"]
|
||||
pub type R = crate::R<PeridSpec>;
|
||||
#[cfg(feature = "debug")]
|
||||
impl core::fmt::Debug for R {
|
||||
fn fmt(&self, f: &mut core::fmt::Formatter) -> core::fmt::Result {
|
||||
write!(f, "{}", self.bits())
|
||||
|
@ -3,6 +3,7 @@ pub type R = crate::R<PortaSpec>;
|
||||
#[doc = "Register `PORTA[%s]` writer"]
|
||||
pub type W = crate::W<PortaSpec>;
|
||||
#[doc = "Input Filter Selectoin\n\nValue on reset: 0"]
|
||||
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
|
||||
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
|
||||
#[repr(u8)]
|
||||
pub enum Flttype {
|
||||
|
@ -2,6 +2,7 @@
|
||||
pub type R = crate::R<IntRamSbeSpec>;
|
||||
#[doc = "Register `INT_RAM_SBE` writer"]
|
||||
pub type W = crate::W<IntRamSbeSpec>;
|
||||
#[cfg(feature = "debug")]
|
||||
impl core::fmt::Debug for R {
|
||||
fn fmt(&self, f: &mut core::fmt::Formatter) -> core::fmt::Result {
|
||||
write!(f, "{}", self.bits())
|
||||
|
@ -1,5 +1,6 @@
|
||||
#[doc = "Register `PERID` reader"]
|
||||
pub type R = crate::R<PeridSpec>;
|
||||
#[cfg(feature = "debug")]
|
||||
impl core::fmt::Debug for R {
|
||||
fn fmt(&self, f: &mut core::fmt::Formatter) -> core::fmt::Result {
|
||||
write!(f, "{}", self.bits())
|
||||
|
@ -97,6 +97,7 @@ pub static __INTERRUPTS: [Vector; 32] = [
|
||||
Vector { _handler: OC31 },
|
||||
];
|
||||
#[doc = r"Enumeration of all the interrupts."]
|
||||
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
|
||||
#[derive(Copy, Clone, Debug, PartialEq, Eq)]
|
||||
#[repr(u16)]
|
||||
pub enum Interrupt {
|
||||
|
@ -1,5 +1,6 @@
|
||||
#[doc = "Register `DATAIN` reader"]
|
||||
pub type R = crate::R<DatainSpec>;
|
||||
#[cfg(feature = "debug")]
|
||||
impl core::fmt::Debug for R {
|
||||
fn fmt(&self, f: &mut core::fmt::Formatter) -> core::fmt::Result {
|
||||
write!(f, "{}", self.bits())
|
||||
|
@ -1,5 +1,6 @@
|
||||
#[doc = "Register `DATAINBYTE[%s]` reader"]
|
||||
pub type R = crate::R<DatainbyteSpec>;
|
||||
#[cfg(feature = "debug")]
|
||||
impl core::fmt::Debug for R {
|
||||
fn fmt(&self, f: &mut core::fmt::Formatter) -> core::fmt::Result {
|
||||
write!(f, "{}", self.bits())
|
||||
|
@ -2,6 +2,7 @@
|
||||
pub type R = crate::R<DatamaskSpec>;
|
||||
#[doc = "Register `DATAMASK` writer"]
|
||||
pub type W = crate::W<DatamaskSpec>;
|
||||
#[cfg(feature = "debug")]
|
||||
impl core::fmt::Debug for R {
|
||||
fn fmt(&self, f: &mut core::fmt::Formatter) -> core::fmt::Result {
|
||||
write!(f, "{}", self.bits())
|
||||
|
@ -2,6 +2,7 @@
|
||||
pub type R = crate::R<DatamaskbyteSpec>;
|
||||
#[doc = "Register `DATAMASKBYTE[%s]` writer"]
|
||||
pub type W = crate::W<DatamaskbyteSpec>;
|
||||
#[cfg(feature = "debug")]
|
||||
impl core::fmt::Debug for R {
|
||||
fn fmt(&self, f: &mut core::fmt::Formatter) -> core::fmt::Result {
|
||||
write!(f, "{}", self.bits())
|
||||
|
@ -1,5 +1,6 @@
|
||||
#[doc = "Register `DATAOUT` writer"]
|
||||
pub type W = crate::W<DataoutSpec>;
|
||||
#[cfg(feature = "debug")]
|
||||
impl core::fmt::Debug for crate::generic::Reg<DataoutSpec> {
|
||||
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
|
||||
write!(f, "(not readable)")
|
||||
|
@ -1,5 +1,6 @@
|
||||
#[doc = "Register `DATAOUTBYTE[%s]` writer"]
|
||||
pub type W = crate::W<DataoutbyteSpec>;
|
||||
#[cfg(feature = "debug")]
|
||||
impl core::fmt::Debug for crate::generic::Reg<DataoutbyteSpec> {
|
||||
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
|
||||
write!(f, "(not readable)")
|
||||
|
@ -2,6 +2,7 @@
|
||||
pub type R = crate::R<EdgeStatusSpec>;
|
||||
#[doc = "Register `EDGE_STATUS` writer"]
|
||||
pub type W = crate::W<EdgeStatusSpec>;
|
||||
#[cfg(feature = "debug")]
|
||||
impl core::fmt::Debug for R {
|
||||
fn fmt(&self, f: &mut core::fmt::Formatter) -> core::fmt::Result {
|
||||
write!(f, "{}", self.bits())
|
||||
|
@ -2,6 +2,7 @@
|
||||
pub type R = crate::R<IrqEdgeSpec>;
|
||||
#[doc = "Register `IRQ_EDGE` writer"]
|
||||
pub type W = crate::W<IrqEdgeSpec>;
|
||||
#[cfg(feature = "debug")]
|
||||
impl core::fmt::Debug for R {
|
||||
fn fmt(&self, f: &mut core::fmt::Formatter) -> core::fmt::Result {
|
||||
write!(f, "{}", self.bits())
|
||||
|
@ -2,6 +2,7 @@
|
||||
pub type R = crate::R<IrqEnbSpec>;
|
||||
#[doc = "Register `IRQ_ENB` writer"]
|
||||
pub type W = crate::W<IrqEnbSpec>;
|
||||
#[cfg(feature = "debug")]
|
||||
impl core::fmt::Debug for R {
|
||||
fn fmt(&self, f: &mut core::fmt::Formatter) -> core::fmt::Result {
|
||||
write!(f, "{}", self.bits())
|
||||
|
@ -1,5 +1,6 @@
|
||||
#[doc = "Register `IRQ_END` reader"]
|
||||
pub type R = crate::R<IrqEndSpec>;
|
||||
#[cfg(feature = "debug")]
|
||||
impl core::fmt::Debug for R {
|
||||
fn fmt(&self, f: &mut core::fmt::Formatter) -> core::fmt::Result {
|
||||
write!(f, "{}", self.bits())
|
||||
|
@ -2,6 +2,7 @@
|
||||
pub type R = crate::R<IrqEvtSpec>;
|
||||
#[doc = "Register `IRQ_EVT` writer"]
|
||||
pub type W = crate::W<IrqEvtSpec>;
|
||||
#[cfg(feature = "debug")]
|
||||
impl core::fmt::Debug for R {
|
||||
fn fmt(&self, f: &mut core::fmt::Formatter) -> core::fmt::Result {
|
||||
write!(f, "{}", self.bits())
|
||||
|
@ -1,5 +1,6 @@
|
||||
#[doc = "Register `IRQ_RAW` reader"]
|
||||
pub type R = crate::R<IrqRawSpec>;
|
||||
#[cfg(feature = "debug")]
|
||||
impl core::fmt::Debug for R {
|
||||
fn fmt(&self, f: &mut core::fmt::Formatter) -> core::fmt::Result {
|
||||
write!(f, "{}", self.bits())
|
||||
|
@ -2,6 +2,7 @@
|
||||
pub type R = crate::R<IrqSenSpec>;
|
||||
#[doc = "Register `IRQ_SEN` writer"]
|
||||
pub type W = crate::W<IrqSenSpec>;
|
||||
#[cfg(feature = "debug")]
|
||||
impl core::fmt::Debug for R {
|
||||
fn fmt(&self, f: &mut core::fmt::Formatter) -> core::fmt::Result {
|
||||
write!(f, "{}", self.bits())
|
||||
|
@ -1,5 +1,6 @@
|
||||
#[doc = "Register `PERID` reader"]
|
||||
pub type R = crate::R<PeridSpec>;
|
||||
#[cfg(feature = "debug")]
|
||||
impl core::fmt::Debug for R {
|
||||
fn fmt(&self, f: &mut core::fmt::Formatter) -> core::fmt::Result {
|
||||
write!(f, "{}", self.bits())
|
||||
|
@ -2,6 +2,7 @@
|
||||
pub type R = crate::R<ClkprescaleSpec>;
|
||||
#[doc = "Register `CLKPRESCALE` writer"]
|
||||
pub type W = crate::W<ClkprescaleSpec>;
|
||||
#[cfg(feature = "debug")]
|
||||
impl core::fmt::Debug for R {
|
||||
fn fmt(&self, f: &mut core::fmt::Formatter) -> core::fmt::Result {
|
||||
write!(f, "{}", self.bits())
|
||||
|
@ -2,6 +2,7 @@
|
||||
pub type R = crate::R<DataSpec>;
|
||||
#[doc = "Register `DATA` writer"]
|
||||
pub type W = crate::W<DataSpec>;
|
||||
#[cfg(feature = "debug")]
|
||||
impl core::fmt::Debug for R {
|
||||
fn fmt(&self, f: &mut core::fmt::Formatter) -> core::fmt::Result {
|
||||
write!(f, "{}", self.bits())
|
||||
|
@ -1,5 +1,6 @@
|
||||
#[doc = "Register `PERID` reader"]
|
||||
pub type R = crate::R<PeridSpec>;
|
||||
#[cfg(feature = "debug")]
|
||||
impl core::fmt::Debug for R {
|
||||
fn fmt(&self, f: &mut core::fmt::Formatter) -> core::fmt::Result {
|
||||
write!(f, "{}", self.bits())
|
||||
|
@ -2,6 +2,7 @@
|
||||
pub type R = crate::R<RxfifoirqtrgSpec>;
|
||||
#[doc = "Register `RXFIFOIRQTRG` writer"]
|
||||
pub type W = crate::W<RxfifoirqtrgSpec>;
|
||||
#[cfg(feature = "debug")]
|
||||
impl core::fmt::Debug for R {
|
||||
fn fmt(&self, f: &mut core::fmt::Formatter) -> core::fmt::Result {
|
||||
write!(f, "{}", self.bits())
|
||||
|
@ -1,5 +1,6 @@
|
||||
#[doc = "Register `STATE` reader"]
|
||||
pub type R = crate::R<StateSpec>;
|
||||
#[cfg(feature = "debug")]
|
||||
impl core::fmt::Debug for R {
|
||||
fn fmt(&self, f: &mut core::fmt::Formatter) -> core::fmt::Result {
|
||||
write!(f, "{}", self.bits())
|
||||
|
@ -2,6 +2,7 @@
|
||||
pub type R = crate::R<TxfifoirqtrgSpec>;
|
||||
#[doc = "Register `TXFIFOIRQTRG` writer"]
|
||||
pub type W = crate::W<TxfifoirqtrgSpec>;
|
||||
#[cfg(feature = "debug")]
|
||||
impl core::fmt::Debug for R {
|
||||
fn fmt(&self, f: &mut core::fmt::Formatter) -> core::fmt::Result {
|
||||
write!(f, "{}", self.bits())
|
||||
|
@ -1,5 +1,6 @@
|
||||
#[doc = "Register `EF_CONFIG` reader"]
|
||||
pub type R = crate::R<EfConfigSpec>;
|
||||
#[cfg(feature = "debug")]
|
||||
impl core::fmt::Debug for R {
|
||||
fn fmt(&self, f: &mut core::fmt::Formatter) -> core::fmt::Result {
|
||||
write!(f, "{}", self.bits())
|
||||
|
@ -1,5 +1,6 @@
|
||||
#[doc = "Register `EF_ID` reader"]
|
||||
pub type R = crate::R<EfIdSpec>;
|
||||
#[cfg(feature = "debug")]
|
||||
impl core::fmt::Debug for R {
|
||||
fn fmt(&self, f: &mut core::fmt::Formatter) -> core::fmt::Result {
|
||||
write!(f, "{}", self.bits())
|
||||
|
@ -2,6 +2,7 @@
|
||||
pub type R = crate::R<IoconfigClkdivSpec>;
|
||||
#[doc = "Register `IOCONFIG_CLKDIV%s` writer"]
|
||||
pub type W = crate::W<IoconfigClkdivSpec>;
|
||||
#[cfg(feature = "debug")]
|
||||
impl core::fmt::Debug for R {
|
||||
fn fmt(&self, f: &mut core::fmt::Formatter) -> core::fmt::Result {
|
||||
write!(f, "{}", self.bits())
|
||||
|
@ -1,5 +1,6 @@
|
||||
#[doc = "Register `IOCONFIG_CLKDIV0` reader"]
|
||||
pub type R = crate::R<IoconfigClkdiv0Spec>;
|
||||
#[cfg(feature = "debug")]
|
||||
impl core::fmt::Debug for R {
|
||||
fn fmt(&self, f: &mut core::fmt::Formatter) -> core::fmt::Result {
|
||||
write!(f, "{}", self.bits())
|
||||
|
@ -1,5 +1,6 @@
|
||||
#[doc = "Register `PERID` reader"]
|
||||
pub type R = crate::R<PeridSpec>;
|
||||
#[cfg(feature = "debug")]
|
||||
impl core::fmt::Debug for R {
|
||||
fn fmt(&self, f: &mut core::fmt::Formatter) -> core::fmt::Result {
|
||||
write!(f, "{}", self.bits())
|
||||
|
@ -1,5 +1,6 @@
|
||||
#[doc = "Register `PROCID` reader"]
|
||||
pub type R = crate::R<ProcidSpec>;
|
||||
#[cfg(feature = "debug")]
|
||||
impl core::fmt::Debug for R {
|
||||
fn fmt(&self, f: &mut core::fmt::Formatter) -> core::fmt::Result {
|
||||
write!(f, "{}", self.bits())
|
||||
|
@ -2,6 +2,7 @@
|
||||
pub type R = crate::R<RamSbeSpec>;
|
||||
#[doc = "Register `RAM_SBE` writer"]
|
||||
pub type W = crate::W<RamSbeSpec>;
|
||||
#[cfg(feature = "debug")]
|
||||
impl core::fmt::Debug for R {
|
||||
fn fmt(&self, f: &mut core::fmt::Formatter) -> core::fmt::Result {
|
||||
write!(f, "{}", self.bits())
|
||||
|
@ -2,6 +2,7 @@
|
||||
pub type R = crate::R<RefreshConfigSpec>;
|
||||
#[doc = "Register `REFRESH_CONFIG` writer"]
|
||||
pub type W = crate::W<RefreshConfigSpec>;
|
||||
#[cfg(feature = "debug")]
|
||||
impl core::fmt::Debug for R {
|
||||
fn fmt(&self, f: &mut core::fmt::Formatter) -> core::fmt::Result {
|
||||
write!(f, "{}", self.bits())
|
||||
|
@ -1,5 +1,6 @@
|
||||
#[doc = "Register `ROM_RETRIES` reader"]
|
||||
pub type R = crate::R<RomRetriesSpec>;
|
||||
#[cfg(feature = "debug")]
|
||||
impl core::fmt::Debug for R {
|
||||
fn fmt(&self, f: &mut core::fmt::Formatter) -> core::fmt::Result {
|
||||
write!(f, "{}", self.bits())
|
||||
|
@ -2,6 +2,7 @@
|
||||
pub type R = crate::R<TimClkEnableSpec>;
|
||||
#[doc = "Register `TIM_CLK_ENABLE` writer"]
|
||||
pub type W = crate::W<TimClkEnableSpec>;
|
||||
#[cfg(feature = "debug")]
|
||||
impl core::fmt::Debug for R {
|
||||
fn fmt(&self, f: &mut core::fmt::Formatter) -> core::fmt::Result {
|
||||
write!(f, "{}", self.bits())
|
||||
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
x
Reference in New Issue
Block a user