Compare commits
2 Commits
vorago-reb
...
6402b9f014
Author | SHA1 | Date | |
---|---|---|---|
6402b9f014
|
|||
2fe92c76c6
|
@ -4,9 +4,10 @@
|
|||||||
# runner = "arm-none-eabi-gdb -q -x openocd.gdb"
|
# runner = "arm-none-eabi-gdb -q -x openocd.gdb"
|
||||||
# runner = "gdb-multiarch -q -x openocd.gdb"
|
# runner = "gdb-multiarch -q -x openocd.gdb"
|
||||||
# runner = "gdb -q -x openocd.gdb"
|
# runner = "gdb -q -x openocd.gdb"
|
||||||
# runner = "gdb-multiarch -q -x jlink.gdb"
|
runner = "gdb-multiarch -q -x jlink.gdb"
|
||||||
|
|
||||||
runner = "probe-rs run --chip VA108xx_RAM --protocol jtag"
|
# Probe-rs is currently problematic: https://github.com/probe-rs/probe-rs/issues/2567
|
||||||
|
# runner = "probe-rs run --chip VA108xx --chip-description-path ./scripts/VA108xx_Series.yaml"
|
||||||
# runner = ["probe-rs", "run", "--chip", "$CHIP", "--log-format", "{L} {s}"]
|
# runner = ["probe-rs", "run", "--chip", "$CHIP", "--log-format", "{L} {s}"]
|
||||||
|
|
||||||
rustflags = [
|
rustflags = [
|
||||||
|
12
.github/workflows/ci.yml
vendored
12
.github/workflows/ci.yml
vendored
@ -10,10 +10,8 @@ jobs:
|
|||||||
- uses: dtolnay/rust-toolchain@stable
|
- uses: dtolnay/rust-toolchain@stable
|
||||||
with:
|
with:
|
||||||
targets: "thumbv6m-none-eabi"
|
targets: "thumbv6m-none-eabi"
|
||||||
- run: cargo check --target thumbv6m-none-eabi
|
- run: cargo check --target thumbv6m-none-eabi --release
|
||||||
- run: cargo check --target thumbv6m-none-eabi --examples
|
- run: cargo check --target thumbv6m-none-eabi --examples --release
|
||||||
- run: cargo check -p va108xx --target thumbv6m-none-eabi --all-features
|
|
||||||
- run: cargo check -p va108xx-hal --target thumbv6m-none-eabi --features "defmt"
|
|
||||||
|
|
||||||
test:
|
test:
|
||||||
name: Run Tests
|
name: Run Tests
|
||||||
@ -23,7 +21,7 @@ jobs:
|
|||||||
- uses: dtolnay/rust-toolchain@stable
|
- uses: dtolnay/rust-toolchain@stable
|
||||||
- name: Install nextest
|
- name: Install nextest
|
||||||
uses: taiki-e/install-action@nextest
|
uses: taiki-e/install-action@nextest
|
||||||
- run: cargo nextest run --all-features -p va108xx-hal --no-tests=pass
|
- run: cargo nextest run --all-features -p va108xx-hal
|
||||||
# I think we can skip those on an embedded crate..
|
# I think we can skip those on an embedded crate..
|
||||||
# - run: cargo test --doc -p va108xx-hal
|
# - run: cargo test --doc -p va108xx-hal
|
||||||
|
|
||||||
@ -41,8 +39,8 @@ jobs:
|
|||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v4
|
- uses: actions/checkout@v4
|
||||||
- uses: dtolnay/rust-toolchain@nightly
|
- uses: dtolnay/rust-toolchain@nightly
|
||||||
- run: RUSTDOCFLAGS="--cfg docsrs --generate-link-to-definition -Z unstable-options" cargo +nightly doc -p va108xx --all-features
|
- run: RUSTDOCFLAGS="--cfg docsrs --generate-link-to-definition -Z unstable-options" cargo +nightly doc -p va108xx
|
||||||
- run: RUSTDOCFLAGS="--cfg docsrs --generate-link-to-definition -Z unstable-options" cargo +nightly doc -p va108xx-hal --all-features
|
- run: RUSTDOCFLAGS="--cfg docsrs --generate-link-to-definition -Z unstable-options" cargo +nightly doc -p va108xx-hal
|
||||||
- run: RUSTDOCFLAGS="--cfg docsrs --generate-link-to-definition -Z unstable-options" cargo +nightly doc -p vorago-reb1
|
- run: RUSTDOCFLAGS="--cfg docsrs --generate-link-to-definition -Z unstable-options" cargo +nightly doc -p vorago-reb1
|
||||||
|
|
||||||
clippy:
|
clippy:
|
||||||
|
@ -1,10 +1,9 @@
|
|||||||
[workspace]
|
[workspace]
|
||||||
resolver = "2"
|
resolver = "2"
|
||||||
members = [
|
members = [
|
||||||
"vorago-reb1",
|
"vorago-reb1",
|
||||||
"va108xx",
|
"va108xx",
|
||||||
"va108xx-hal",
|
"va108xx-hal",
|
||||||
"va108xx-embassy",
|
|
||||||
"examples/simple",
|
"examples/simple",
|
||||||
"examples/rtic",
|
"examples/rtic",
|
||||||
"examples/embassy",
|
"examples/embassy",
|
||||||
@ -23,7 +22,7 @@ codegen-units = 1
|
|||||||
debug = 2
|
debug = 2
|
||||||
debug-assertions = true # <-
|
debug-assertions = true # <-
|
||||||
incremental = false
|
incremental = false
|
||||||
# 1 instead of 0, the flashloader is too larger otherwise..
|
# 1 instead of 0, the flashloader is too larger otherwise..
|
||||||
# opt-level = 1 # <-
|
# opt-level = 1 # <-
|
||||||
overflow-checks = true # <-
|
overflow-checks = true # <-
|
||||||
|
|
||||||
|
75
README.md
75
README.md
@ -14,8 +14,6 @@ This workspace contains the following released crates:
|
|||||||
crate containing basic low-level register definition.
|
crate containing basic low-level register definition.
|
||||||
- The [`va108xx-hal`](https://egit.irs.uni-stuttgart.de/rust/va108xx-rs/src/branch/main/va108xx-hal)
|
- 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.
|
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)
|
- 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.
|
BSP crate containing support for the REB1 development board.
|
||||||
|
|
||||||
@ -60,56 +58,14 @@ You can then adapt the files in `.vscode` to your needs.
|
|||||||
You can use CLI or VS Code for flashing, running and debugging. In any case, take
|
You can use CLI or VS Code for flashing, running and debugging. In any case, take
|
||||||
care of installing the pre-requisites first.
|
care of installing the pre-requisites first.
|
||||||
|
|
||||||
### Using CLI with probe-rs
|
### Pre-Requisites
|
||||||
|
|
||||||
Install [probe-rs](https://probe.rs/docs/getting-started/installation/) first.
|
|
||||||
|
|
||||||
You can use `probe-rs` to run the software and display RTT log output. However, debugging does not
|
|
||||||
work yet.
|
|
||||||
|
|
||||||
After installation, you can run the following command
|
|
||||||
|
|
||||||
```sh
|
|
||||||
probe-rs run --chip VA108xx_RAM --protocol jtag target/thumbv6m-none-eabi/debug/examples/blinky
|
|
||||||
```
|
|
||||||
|
|
||||||
to flash and run the blinky program on the RAM. There is also a `VA108xx` chip target
|
|
||||||
available for persistent flashing.
|
|
||||||
|
|
||||||
Runner configuration avilable in the `.cargo/def-config.toml` file to use `probe-rs` for
|
|
||||||
convenience.
|
|
||||||
|
|
||||||
### Using VS Code
|
|
||||||
|
|
||||||
Assuming a working debug connection to your VA108xx board, you can debug using VS Code with
|
|
||||||
the [`Cortex-Debug` plugin](https://marketplace.visualstudio.com/items?itemName=marus25.cortex-debug).
|
|
||||||
Please make sure that [`objdump-multiarch` and `nm-multiarch`](https://forums.raspberrypi.com/viewtopic.php?t=333146)
|
|
||||||
are installed as well.
|
|
||||||
|
|
||||||
Some sample configuration files for VS code were provided and can be used by running
|
|
||||||
`cp -rT vscode .vscode` like specified above. After that, you can use `Run and Debug`
|
|
||||||
to automatically rebuild and flash your application.
|
|
||||||
|
|
||||||
If you would like to use a custom GDB application, you can specify the gdb binary in the following
|
|
||||||
configuration variables in your `settings.json`:
|
|
||||||
|
|
||||||
- `"cortex-debug.gdbPath"`
|
|
||||||
- `"cortex-debug.gdbPath.linux"`
|
|
||||||
- `"cortex-debug.gdbPath.windows"`
|
|
||||||
- `"cortex-debug.gdbPath.osx"`
|
|
||||||
|
|
||||||
The provided VS Code configurations also provide an integrated RTT logger, which you can access
|
|
||||||
via the terminal at `RTT Ch:0 console`. In order for the RTT block address detection to
|
|
||||||
work properly, `objdump-multiarch` and `nm-multiarch` need to be installed.
|
|
||||||
|
|
||||||
### Using CLI with GDB and Segger J-Link Tools
|
|
||||||
|
|
||||||
Install the following two tools first:
|
|
||||||
|
|
||||||
1. [SEGGER J-Link tools](https://www.segger.com/downloads/jlink/) installed
|
1. [SEGGER J-Link tools](https://www.segger.com/downloads/jlink/) installed
|
||||||
2. [gdb-multiarch](https://packages.debian.org/sid/gdb-multiarch) or similar
|
2. [gdb-multiarch](https://packages.debian.org/sid/gdb-multiarch) or similar
|
||||||
cross-architecture debugger installed. All commands here assume `gdb-multiarch`.
|
cross-architecture debugger installed. All commands here assume `gdb-multiarch`.
|
||||||
|
|
||||||
|
### Using CLI
|
||||||
|
|
||||||
You can build the blinky example application with the following command
|
You can build the blinky example application with the following command
|
||||||
|
|
||||||
```sh
|
```sh
|
||||||
@ -143,8 +99,25 @@ runner = "gdb-multiarch -q -x jlink/jlink.gdb"
|
|||||||
After that, you can simply use `cargo run --example blinky` to flash the blinky
|
After that, you can simply use `cargo run --example blinky` to flash the blinky
|
||||||
example.
|
example.
|
||||||
|
|
||||||
### Using the RTT Viewer
|
### Using VS Code
|
||||||
|
|
||||||
The Segger RTT viewer can be used to display log messages received from the target. The base
|
Assuming a working debug connection to your VA108xx board, you can debug using VS Code with
|
||||||
address for the RTT block placement is 0x10000000. It is recommended to use a search range of
|
the [`Cortex-Debug` plugin](https://marketplace.visualstudio.com/items?itemName=marus25.cortex-debug).
|
||||||
0x1000 around that base address when using the RTT viewer.
|
Please make sure that [`objdump-multiarch` and `nm-multiarch`](https://forums.raspberrypi.com/viewtopic.php?t=333146)
|
||||||
|
are installed as well.
|
||||||
|
|
||||||
|
Some sample configuration files for VS code were provided and can be used by running
|
||||||
|
`cp -rT vscode .vscode` like specified above. After that, you can use `Run and Debug`
|
||||||
|
to automatically rebuild and flash your application.
|
||||||
|
|
||||||
|
If you would like to use a custom GDB application, you can specify the gdb binary in the following
|
||||||
|
configuration variables in your `settings.json`:
|
||||||
|
|
||||||
|
- `"cortex-debug.gdbPath"`
|
||||||
|
- `"cortex-debug.gdbPath.linux"`
|
||||||
|
- `"cortex-debug.gdbPath.windows"`
|
||||||
|
- `"cortex-debug.gdbPath.osx"`
|
||||||
|
|
||||||
|
The provided VS Code configurations also provide an integrated RTT logger, which you can access
|
||||||
|
via the terminal at `RTT Ch:0 console`. In order for the RTT block address detection to
|
||||||
|
work properly, `objdump-multiarch` and `nm-multiarch` need to be installed.
|
||||||
|
@ -6,14 +6,14 @@ edition = "2021"
|
|||||||
[dependencies]
|
[dependencies]
|
||||||
cortex-m = { version = "0.7.6", features = ["critical-section-single-core"] }
|
cortex-m = { version = "0.7.6", features = ["critical-section-single-core"] }
|
||||||
cortex-m-rt = "0.7"
|
cortex-m-rt = "0.7"
|
||||||
panic-halt = "1"
|
panic-halt = "0.2"
|
||||||
rtt-target = "0.6"
|
rtt-target = "0.5"
|
||||||
panic-rtt-target = "0.2"
|
panic-rtt-target = "0.1.3"
|
||||||
embedded-hal = "1"
|
embedded-hal = "1"
|
||||||
embedded-hal-nb = "1"
|
embedded-hal-nb = "1"
|
||||||
embedded-io = "0.6"
|
embedded-io = "0.6"
|
||||||
|
|
||||||
[dependencies.va108xx-hal]
|
[dependencies.va108xx-hal]
|
||||||
version = "0.10.0"
|
path = "../va108xx-hal"
|
||||||
features = ["rt"]
|
features = ["rt"]
|
||||||
|
|
||||||
|
@ -6,7 +6,10 @@
|
|||||||
#![no_std]
|
#![no_std]
|
||||||
|
|
||||||
use cortex_m_rt::entry;
|
use cortex_m_rt::entry;
|
||||||
use embedded_hal::delay::DelayNs;
|
use embedded_hal::{
|
||||||
|
delay::DelayNs,
|
||||||
|
digital::{InputPin, OutputPin, StatefulOutputPin},
|
||||||
|
};
|
||||||
use panic_rtt_target as _;
|
use panic_rtt_target as _;
|
||||||
use rtt_target::{rprintln, rtt_init_print};
|
use rtt_target::{rprintln, rtt_init_print};
|
||||||
use va108xx_hal::{
|
use va108xx_hal::{
|
||||||
@ -14,7 +17,7 @@ use va108xx_hal::{
|
|||||||
pac::{self, interrupt},
|
pac::{self, interrupt},
|
||||||
prelude::*,
|
prelude::*,
|
||||||
time::Hertz,
|
time::Hertz,
|
||||||
timer::{default_ms_irq_handler, set_up_ms_tick, CountdownTimer, InterruptConfig},
|
timer::{default_ms_irq_handler, set_up_ms_tick, CountdownTimer, IrqCfg},
|
||||||
};
|
};
|
||||||
|
|
||||||
#[allow(dead_code)]
|
#[allow(dead_code)]
|
||||||
@ -41,8 +44,8 @@ fn main() -> ! {
|
|||||||
rprintln!("-- VA108xx Test Application --");
|
rprintln!("-- VA108xx Test Application --");
|
||||||
let mut dp = pac::Peripherals::take().unwrap();
|
let mut dp = pac::Peripherals::take().unwrap();
|
||||||
let cp = cortex_m::Peripherals::take().unwrap();
|
let cp = cortex_m::Peripherals::take().unwrap();
|
||||||
let pinsa = PinsA::new(&mut dp.sysconfig, dp.porta);
|
let pinsa = PinsA::new(&mut dp.sysconfig, None, dp.porta);
|
||||||
let pinsb = PinsB::new(&mut dp.sysconfig, dp.portb);
|
let pinsb = PinsB::new(&mut dp.sysconfig, Some(dp.ioconfig), dp.portb);
|
||||||
let mut led1 = pinsa.pa10.into_readable_push_pull_output();
|
let mut led1 = pinsa.pa10.into_readable_push_pull_output();
|
||||||
let test_case = TestCase::DelayMs;
|
let test_case = TestCase::DelayMs;
|
||||||
|
|
||||||
@ -64,110 +67,113 @@ fn main() -> ! {
|
|||||||
TestCase::TestBasic => {
|
TestCase::TestBasic => {
|
||||||
// Tie PORTA[0] to PORTA[1] for these tests!
|
// Tie PORTA[0] to PORTA[1] for these tests!
|
||||||
let mut out = pinsa.pa0.into_readable_push_pull_output();
|
let mut out = pinsa.pa0.into_readable_push_pull_output();
|
||||||
let input = pinsa.pa1.into_floating_input();
|
let mut input = pinsa.pa1.into_floating_input();
|
||||||
out.set_high();
|
out.set_high().unwrap();
|
||||||
assert!(input.is_high());
|
assert!(input.is_high().unwrap());
|
||||||
out.set_low();
|
out.set_low().unwrap();
|
||||||
assert!(input.is_low());
|
assert!(input.is_low().unwrap());
|
||||||
}
|
}
|
||||||
TestCase::TestPullup => {
|
TestCase::TestPullup => {
|
||||||
// Tie PORTA[0] to PORTA[1] for these tests!
|
// Tie PORTA[0] to PORTA[1] for these tests!
|
||||||
let input = pinsa.pa1.into_pull_up_input();
|
let mut input = pinsa.pa1.into_pull_up_input();
|
||||||
assert!(input.is_high());
|
assert!(input.is_high().unwrap());
|
||||||
let mut out = pinsa.pa0.into_readable_push_pull_output();
|
let mut out = pinsa.pa0.into_readable_push_pull_output();
|
||||||
out.set_low();
|
out.set_low().unwrap();
|
||||||
assert!(input.is_low());
|
assert!(input.is_low().unwrap());
|
||||||
out.set_high();
|
out.set_high().unwrap();
|
||||||
assert!(input.is_high());
|
assert!(input.is_high().unwrap());
|
||||||
out.into_floating_input();
|
out.into_floating_input();
|
||||||
assert!(input.is_high());
|
assert!(input.is_high().unwrap());
|
||||||
}
|
}
|
||||||
TestCase::TestPulldown => {
|
TestCase::TestPulldown => {
|
||||||
// Tie PORTA[0] to PORTA[1] for these tests!
|
// Tie PORTA[0] to PORTA[1] for these tests!
|
||||||
let input = pinsa.pa1.into_pull_down_input();
|
let mut input = pinsa.pa1.into_pull_down_input();
|
||||||
assert!(input.is_low());
|
assert!(input.is_low().unwrap());
|
||||||
let mut out = pinsa.pa0.into_push_pull_output();
|
let mut out = pinsa.pa0.into_push_pull_output();
|
||||||
out.set_low();
|
out.set_low().unwrap();
|
||||||
assert!(input.is_low());
|
assert!(input.is_low().unwrap());
|
||||||
out.set_high();
|
out.set_high().unwrap();
|
||||||
assert!(input.is_high());
|
assert!(input.is_high().unwrap());
|
||||||
out.into_floating_input();
|
out.into_floating_input();
|
||||||
assert!(input.is_low());
|
assert!(input.is_low().unwrap());
|
||||||
}
|
}
|
||||||
TestCase::TestMask => {
|
TestCase::TestMask => {
|
||||||
// Tie PORTA[0] to PORTA[1] for these tests!
|
// Tie PORTA[0] to PORTA[1] for these tests!
|
||||||
let mut input = pinsa.pa1.into_pull_down_input();
|
let input = pinsa.pa1.into_pull_down_input().clear_datamask();
|
||||||
input.clear_datamask();
|
|
||||||
assert!(!input.datamask());
|
assert!(!input.datamask());
|
||||||
let mut out = pinsa.pa0.into_push_pull_output();
|
let mut out = pinsa.pa0.into_push_pull_output().clear_datamask();
|
||||||
out.clear_datamask();
|
|
||||||
assert!(input.is_low_masked().is_err());
|
assert!(input.is_low_masked().is_err());
|
||||||
assert!(out.set_high_masked().is_err());
|
assert!(out.set_high_masked().is_err());
|
||||||
}
|
}
|
||||||
TestCase::PortB => {
|
TestCase::PortB => {
|
||||||
// Tie PORTB[22] to PORTB[23] for these tests!
|
// Tie PORTB[22] to PORTB[23] for these tests!
|
||||||
let mut out = pinsb.pb22.into_readable_push_pull_output();
|
let mut out = pinsb.pb22.into_readable_push_pull_output();
|
||||||
let input = pinsb.pb23.into_floating_input();
|
let mut input = pinsb.pb23.into_floating_input();
|
||||||
out.set_high();
|
out.set_high().unwrap();
|
||||||
assert!(input.is_high());
|
assert!(input.is_high().unwrap());
|
||||||
out.set_low();
|
out.set_low().unwrap();
|
||||||
assert!(input.is_low());
|
assert!(input.is_low().unwrap());
|
||||||
}
|
}
|
||||||
TestCase::Perid => {
|
TestCase::Perid => {
|
||||||
assert_eq!(PinsA::get_perid(), 0x004007e1);
|
assert_eq!(PinsA::get_perid(), 0x004007e1);
|
||||||
assert_eq!(PinsB::get_perid(), 0x004007e1);
|
assert_eq!(PinsB::get_perid(), 0x004007e1);
|
||||||
}
|
}
|
||||||
TestCase::Pulse => {
|
TestCase::Pulse => {
|
||||||
let mut output_pulsed = pinsa.pa0.into_push_pull_output();
|
let mut output_pulsed = pinsa
|
||||||
output_pulsed.configure_pulse_mode(true, PinState::Low);
|
.pa0
|
||||||
|
.into_push_pull_output()
|
||||||
|
.pulse_mode(true, PinState::Low);
|
||||||
rprintln!("Pulsing high 10 times..");
|
rprintln!("Pulsing high 10 times..");
|
||||||
output_pulsed.set_low();
|
output_pulsed.set_low().unwrap();
|
||||||
for _ in 0..10 {
|
for _ in 0..10 {
|
||||||
output_pulsed.set_high();
|
output_pulsed.set_high().unwrap();
|
||||||
cortex_m::asm::delay(25_000_000);
|
cortex_m::asm::delay(25_000_000);
|
||||||
}
|
}
|
||||||
output_pulsed.configure_pulse_mode(true, PinState::High);
|
let mut output_pulsed = output_pulsed.pulse_mode(true, PinState::High);
|
||||||
rprintln!("Pulsing low 10 times..");
|
rprintln!("Pulsing low 10 times..");
|
||||||
for _ in 0..10 {
|
for _ in 0..10 {
|
||||||
output_pulsed.set_low();
|
output_pulsed.set_low().unwrap();
|
||||||
cortex_m::asm::delay(25_000_000);
|
cortex_m::asm::delay(25_000_000);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
TestCase::DelayGpio => {
|
TestCase::DelayGpio => {
|
||||||
let mut out_0 = pinsa.pa0.into_readable_push_pull_output();
|
let mut out_0 = pinsa
|
||||||
out_0.configure_delay(true, false);
|
.pa0
|
||||||
let mut out_1 = pinsa.pa1.into_readable_push_pull_output();
|
.into_readable_push_pull_output()
|
||||||
out_1.configure_delay(false, true);
|
.delay(true, false);
|
||||||
let mut out_2 = pinsa.pa3.into_readable_push_pull_output();
|
let mut out_1 = pinsa
|
||||||
out_2.configure_delay(true, true);
|
.pa1
|
||||||
|
.into_readable_push_pull_output()
|
||||||
|
.delay(false, true);
|
||||||
|
let mut out_2 = pinsa.pa3.into_readable_push_pull_output().delay(true, true);
|
||||||
for _ in 0..20 {
|
for _ in 0..20 {
|
||||||
out_0.toggle();
|
out_0.toggle().unwrap();
|
||||||
out_1.toggle();
|
out_1.toggle().unwrap();
|
||||||
out_2.toggle();
|
out_2.toggle().unwrap();
|
||||||
cortex_m::asm::delay(25_000_000);
|
cortex_m::asm::delay(25_000_000);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
TestCase::DelayMs => {
|
TestCase::DelayMs => {
|
||||||
let mut ms_timer = set_up_ms_tick(
|
let mut ms_timer = set_up_ms_tick(
|
||||||
InterruptConfig::new(pac::Interrupt::OC0, true, true),
|
IrqCfg::new(pac::Interrupt::OC0, true, true),
|
||||||
&mut dp.sysconfig,
|
&mut dp.sysconfig,
|
||||||
Some(&mut dp.irqsel),
|
Some(&mut dp.irqsel),
|
||||||
50.MHz(),
|
50.MHz(),
|
||||||
dp.tim0,
|
dp.tim0,
|
||||||
);
|
);
|
||||||
for _ in 0..5 {
|
for _ in 0..5 {
|
||||||
led1.toggle();
|
led1.toggle().ok();
|
||||||
ms_timer.delay_ms(500);
|
ms_timer.delay_ms(500);
|
||||||
led1.toggle();
|
led1.toggle().ok();
|
||||||
ms_timer.delay_ms(500);
|
ms_timer.delay_ms(500);
|
||||||
}
|
}
|
||||||
|
|
||||||
let mut delay_timer = CountdownTimer::new(&mut dp.sysconfig, 50.MHz(), dp.tim1);
|
let mut delay_timer = CountdownTimer::new(&mut dp.sysconfig, 50.MHz(), dp.tim1);
|
||||||
let mut pa0 = pinsa.pa0.into_readable_push_pull_output();
|
let mut pa0 = pinsa.pa0.into_readable_push_pull_output();
|
||||||
for _ in 0..5 {
|
for _ in 0..5 {
|
||||||
led1.toggle();
|
led1.toggle().ok();
|
||||||
delay_timer.delay_ms(500);
|
delay_timer.delay_ms(500);
|
||||||
led1.toggle();
|
led1.toggle().ok();
|
||||||
delay_timer.delay_ms(500);
|
delay_timer.delay_ms(500);
|
||||||
}
|
}
|
||||||
let ahb_freq: Hertz = 50.MHz();
|
let ahb_freq: Hertz = 50.MHz();
|
||||||
@ -175,13 +181,13 @@ fn main() -> ! {
|
|||||||
// Test usecond delay using both TIM peripheral and SYST. Use the release image if you
|
// Test usecond delay using both TIM peripheral and SYST. Use the release image if you
|
||||||
// want to verify the timings!
|
// want to verify the timings!
|
||||||
loop {
|
loop {
|
||||||
pa0.toggle();
|
pa0.toggle().ok();
|
||||||
delay_timer.delay_us(50);
|
delay_timer.delay_us(50);
|
||||||
pa0.toggle();
|
pa0.toggle().ok();
|
||||||
delay_timer.delay_us(50);
|
delay_timer.delay_us(50);
|
||||||
pa0.toggle();
|
pa0.toggle_with_toggle_reg();
|
||||||
syst_delay.delay_us(50);
|
syst_delay.delay_us(50);
|
||||||
pa0.toggle();
|
pa0.toggle_with_toggle_reg();
|
||||||
syst_delay.delay_us(50);
|
syst_delay.delay_us(50);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -189,7 +195,7 @@ fn main() -> ! {
|
|||||||
|
|
||||||
rprintln!("Test success");
|
rprintln!("Test success");
|
||||||
loop {
|
loop {
|
||||||
led1.toggle();
|
led1.toggle().ok();
|
||||||
cortex_m::asm::delay(25_000_000);
|
cortex_m::asm::delay(25_000_000);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -7,18 +7,18 @@ edition = "2021"
|
|||||||
cortex-m = "0.7"
|
cortex-m = "0.7"
|
||||||
cortex-m-rt = "0.7"
|
cortex-m-rt = "0.7"
|
||||||
embedded-hal = "1"
|
embedded-hal = "1"
|
||||||
panic-rtt-target = "0.2"
|
panic-rtt-target = { version = "0.1.3" }
|
||||||
panic-halt = "1"
|
panic-halt = { version = "0.2" }
|
||||||
rtt-target = "0.6"
|
rtt-target = { version = "0.5" }
|
||||||
crc = "3"
|
crc = "3"
|
||||||
num_enum = { version = "0.7", default-features = false }
|
num_enum = { version = "0.7", default-features = false }
|
||||||
static_assertions = "1"
|
static_assertions = "1"
|
||||||
|
|
||||||
[dependencies.va108xx-hal]
|
[dependencies.va108xx-hal]
|
||||||
version = "0.10"
|
path = "../va108xx-hal"
|
||||||
|
|
||||||
[dependencies.vorago-reb1]
|
[dependencies.vorago-reb1]
|
||||||
version = "0.8"
|
path = "../vorago-reb1"
|
||||||
|
|
||||||
[features]
|
[features]
|
||||||
default = []
|
default = []
|
||||||
|
@ -27,8 +27,10 @@ version = "1"
|
|||||||
features = ["cortex-m-systick"]
|
features = ["cortex-m-systick"]
|
||||||
|
|
||||||
[dependencies.va108xx-hal]
|
[dependencies.va108xx-hal]
|
||||||
version = "0.10"
|
version = "0.6"
|
||||||
|
path = "../va108xx-hal"
|
||||||
features = ["rt", "defmt"]
|
features = ["rt", "defmt"]
|
||||||
|
|
||||||
[dependencies.va108xx]
|
[dependencies.va108xx]
|
||||||
version = "0.5"
|
version = "0.3"
|
||||||
|
path = "../va108xx"
|
||||||
|
@ -20,14 +20,6 @@ cargo run --bin rtic-example
|
|||||||
|
|
||||||
## Embassy example
|
## Embassy example
|
||||||
|
|
||||||
Blinky with time driver IRQs in library
|
|
||||||
|
|
||||||
```rs
|
```rs
|
||||||
cargo run --bin embassy-example
|
cargo run --bin embassy-example
|
||||||
```
|
```
|
||||||
|
|
||||||
Blinky with custom time driver IRQs
|
|
||||||
|
|
||||||
```rs
|
|
||||||
cargo run --bin embassy-example --no-default-features --features custom-irqs
|
|
||||||
```
|
|
||||||
|
@ -4,34 +4,37 @@ version = "0.1.0"
|
|||||||
edition = "2021"
|
edition = "2021"
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
cfg-if = "1"
|
|
||||||
cortex-m = { version = "0.7", features = ["critical-section-single-core"] }
|
cortex-m = { version = "0.7", features = ["critical-section-single-core"] }
|
||||||
cortex-m-rt = "0.7"
|
cortex-m-rt = "0.7"
|
||||||
embedded-hal = "1"
|
embedded-hal = "1"
|
||||||
embedded-hal-async = "1"
|
|
||||||
embedded-io = "0.6"
|
|
||||||
embedded-io-async = "0.6"
|
|
||||||
heapless = "0.8"
|
|
||||||
static_cell = "2"
|
|
||||||
|
|
||||||
rtt-target = "0.6"
|
rtt-target = { version = "0.5" }
|
||||||
panic-rtt-target = "0.2"
|
panic-rtt-target = { version = "0.1" }
|
||||||
critical-section = "1"
|
critical-section = "1"
|
||||||
portable-atomic = { version = "1", features = ["unsafe-assume-single-core"]}
|
portable-atomic = { version = "1", features = ["unsafe-assume-single-core"]}
|
||||||
|
|
||||||
embassy-sync = "0.6"
|
embassy-sync = { version = "0.6.0" }
|
||||||
embassy-time = "0.4"
|
embassy-time = { version = "0.3.2" }
|
||||||
embassy-executor = { version = "0.7", features = [
|
embassy-time-driver = { version = "0.1" }
|
||||||
"arch-cortex-m",
|
|
||||||
"executor-thread",
|
|
||||||
"executor-interrupt"
|
|
||||||
]}
|
|
||||||
|
|
||||||
va108xx-hal = { version = "0.11", path = "../../va108xx-hal" }
|
[dependencies.once_cell]
|
||||||
va108xx-embassy = { version = "0.2", path = "../../va108xx-embassy" }
|
version = "1"
|
||||||
|
default-features = false
|
||||||
|
features = ["critical-section"]
|
||||||
|
|
||||||
|
[dependencies.embassy-executor]
|
||||||
|
version = "0.6.0"
|
||||||
|
features = [
|
||||||
|
"arch-cortex-m",
|
||||||
|
"executor-thread",
|
||||||
|
"executor-interrupt",
|
||||||
|
"integrated-timers",
|
||||||
|
]
|
||||||
|
|
||||||
|
[dependencies.va108xx-hal]
|
||||||
|
path = "../../va108xx-hal"
|
||||||
|
|
||||||
[features]
|
[features]
|
||||||
default = ["ticks-hz-1_000", "va108xx-embassy/irq-oc30-oc31"]
|
default = ["ticks-hz-1_000"]
|
||||||
custom-irqs = []
|
|
||||||
ticks-hz-1_000 = ["embassy-time/tick-hz-1_000"]
|
ticks-hz-1_000 = ["embassy-time/tick-hz-1_000"]
|
||||||
ticks-hz-32_768 = ["embassy-time/tick-hz-32_768"]
|
ticks-hz-32_768 = ["embassy-time/tick-hz-32_768"]
|
||||||
|
@ -1,257 +0,0 @@
|
|||||||
//! This example demonstrates the usage of async GPIO operations on VA108xx.
|
|
||||||
//!
|
|
||||||
//! You need to tie the PA0 to the PA1 pin for this example to work. You can optionally tie the PB22 to PB23 pins well
|
|
||||||
//! and then set the `CHECK_PB22_TO_PB23` to true to also test async operations on Port B.
|
|
||||||
#![no_std]
|
|
||||||
#![no_main]
|
|
||||||
|
|
||||||
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_async::digital::Wait;
|
|
||||||
use panic_rtt_target as _;
|
|
||||||
use rtt_target::{rprintln, rtt_init_print};
|
|
||||||
use va108xx_hal::gpio::{
|
|
||||||
on_interrupt_for_async_gpio_for_port, InputDynPinAsync, InputPinAsync, PinsB, Port,
|
|
||||||
};
|
|
||||||
use va108xx_hal::{
|
|
||||||
gpio::{DynPin, PinsA},
|
|
||||||
pac::{self, interrupt},
|
|
||||||
prelude::*,
|
|
||||||
};
|
|
||||||
|
|
||||||
const SYSCLK_FREQ: Hertz = Hertz::from_raw(50_000_000);
|
|
||||||
|
|
||||||
const CHECK_PA0_TO_PA1: bool = true;
|
|
||||||
const CHECK_PB22_TO_PB23: bool = false;
|
|
||||||
|
|
||||||
// Can also be set to OC10 and works as well.
|
|
||||||
const PB22_TO_PB23_IRQ: pac::Interrupt = pac::Interrupt::OC11;
|
|
||||||
|
|
||||||
#[derive(Clone, Copy)]
|
|
||||||
pub struct GpioCmd {
|
|
||||||
cmd_type: GpioCmdType,
|
|
||||||
after_delay: u32,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl GpioCmd {
|
|
||||||
pub fn new(cmd_type: GpioCmdType, after_delay: u32) -> Self {
|
|
||||||
Self {
|
|
||||||
cmd_type,
|
|
||||||
after_delay,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Clone, Copy)]
|
|
||||||
pub enum GpioCmdType {
|
|
||||||
SetHigh,
|
|
||||||
SetLow,
|
|
||||||
RisingEdge,
|
|
||||||
FallingEdge,
|
|
||||||
}
|
|
||||||
|
|
||||||
// Declare a bounded channel of 3 u32s.
|
|
||||||
static CHANNEL_PA0_PA1: Channel<ThreadModeRawMutex, GpioCmd, 3> = Channel::new();
|
|
||||||
static CHANNEL_PB22_TO_PB23: Channel<ThreadModeRawMutex, GpioCmd, 3> = Channel::new();
|
|
||||||
|
|
||||||
#[embassy_executor::main]
|
|
||||||
async fn main(spawner: Spawner) {
|
|
||||||
rtt_init_print!();
|
|
||||||
rprintln!("-- VA108xx Async GPIO Demo --");
|
|
||||||
|
|
||||||
let mut dp = pac::Peripherals::take().unwrap();
|
|
||||||
|
|
||||||
// Safety: Only called once here.
|
|
||||||
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);
|
|
||||||
let mut led0 = porta.pa10.into_readable_push_pull_output();
|
|
||||||
let out_pa0 = porta.pa0.into_readable_push_pull_output();
|
|
||||||
let in_pa1 = porta.pa1.into_floating_input();
|
|
||||||
let out_pb22 = portb.pb22.into_readable_push_pull_output();
|
|
||||||
let in_pb23 = portb.pb23.into_floating_input();
|
|
||||||
|
|
||||||
let in_pa1_async = InputPinAsync::new(in_pa1, pac::Interrupt::OC10);
|
|
||||||
let out_pa0_dyn = out_pa0.downgrade();
|
|
||||||
let in_pb23_async = InputDynPinAsync::new(in_pb23.downgrade(), PB22_TO_PB23_IRQ).unwrap();
|
|
||||||
let out_pb22_dyn = out_pb22.downgrade();
|
|
||||||
|
|
||||||
spawner
|
|
||||||
.spawn(output_task(
|
|
||||||
"PA0 to PA1",
|
|
||||||
out_pa0_dyn,
|
|
||||||
CHANNEL_PA0_PA1.receiver(),
|
|
||||||
))
|
|
||||||
.unwrap();
|
|
||||||
spawner
|
|
||||||
.spawn(output_task(
|
|
||||||
"PB22 to PB23",
|
|
||||||
out_pb22_dyn,
|
|
||||||
CHANNEL_PB22_TO_PB23.receiver(),
|
|
||||||
))
|
|
||||||
.unwrap();
|
|
||||||
|
|
||||||
if CHECK_PA0_TO_PA1 {
|
|
||||||
check_pin_to_pin_async_ops("PA0 to PA1", CHANNEL_PA0_PA1.sender(), in_pa1_async).await;
|
|
||||||
rprintln!("Example PA0 to PA1 done");
|
|
||||||
}
|
|
||||||
if CHECK_PB22_TO_PB23 {
|
|
||||||
check_pin_to_pin_async_ops("PB22 to PB23", CHANNEL_PB22_TO_PB23.sender(), in_pb23_async)
|
|
||||||
.await;
|
|
||||||
rprintln!("Example PB22 to PB23 done");
|
|
||||||
}
|
|
||||||
|
|
||||||
rprintln!("Example done, toggling LED0");
|
|
||||||
loop {
|
|
||||||
led0.toggle();
|
|
||||||
Timer::after(Duration::from_millis(500)).await;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
async fn check_pin_to_pin_async_ops(
|
|
||||||
ctx: &'static str,
|
|
||||||
sender: Sender<'static, ThreadModeRawMutex, GpioCmd, 3>,
|
|
||||||
mut async_input: impl Wait,
|
|
||||||
) {
|
|
||||||
rprintln!(
|
|
||||||
"{}: sending SetHigh command ({} ms)",
|
|
||||||
ctx,
|
|
||||||
Instant::now().as_millis()
|
|
||||||
);
|
|
||||||
sender.send(GpioCmd::new(GpioCmdType::SetHigh, 20)).await;
|
|
||||||
async_input.wait_for_high().await.unwrap();
|
|
||||||
rprintln!(
|
|
||||||
"{}: Input pin is high now ({} ms)",
|
|
||||||
ctx,
|
|
||||||
Instant::now().as_millis()
|
|
||||||
);
|
|
||||||
|
|
||||||
rprintln!(
|
|
||||||
"{}: sending SetLow command ({} ms)",
|
|
||||||
ctx,
|
|
||||||
Instant::now().as_millis()
|
|
||||||
);
|
|
||||||
sender.send(GpioCmd::new(GpioCmdType::SetLow, 20)).await;
|
|
||||||
async_input.wait_for_low().await.unwrap();
|
|
||||||
rprintln!(
|
|
||||||
"{}: Input pin is low now ({} ms)",
|
|
||||||
ctx,
|
|
||||||
Instant::now().as_millis()
|
|
||||||
);
|
|
||||||
|
|
||||||
rprintln!(
|
|
||||||
"{}: sending RisingEdge command ({} ms)",
|
|
||||||
ctx,
|
|
||||||
Instant::now().as_millis()
|
|
||||||
);
|
|
||||||
sender.send(GpioCmd::new(GpioCmdType::RisingEdge, 20)).await;
|
|
||||||
async_input.wait_for_rising_edge().await.unwrap();
|
|
||||||
rprintln!(
|
|
||||||
"{}: input pin had rising edge ({} ms)",
|
|
||||||
ctx,
|
|
||||||
Instant::now().as_millis()
|
|
||||||
);
|
|
||||||
|
|
||||||
rprintln!(
|
|
||||||
"{}: sending Falling command ({} ms)",
|
|
||||||
ctx,
|
|
||||||
Instant::now().as_millis()
|
|
||||||
);
|
|
||||||
sender
|
|
||||||
.send(GpioCmd::new(GpioCmdType::FallingEdge, 20))
|
|
||||||
.await;
|
|
||||||
async_input.wait_for_falling_edge().await.unwrap();
|
|
||||||
rprintln!(
|
|
||||||
"{}: input pin had a falling edge ({} ms)",
|
|
||||||
ctx,
|
|
||||||
Instant::now().as_millis()
|
|
||||||
);
|
|
||||||
|
|
||||||
rprintln!(
|
|
||||||
"{}: sending Falling command ({} ms)",
|
|
||||||
ctx,
|
|
||||||
Instant::now().as_millis()
|
|
||||||
);
|
|
||||||
sender
|
|
||||||
.send(GpioCmd::new(GpioCmdType::FallingEdge, 20))
|
|
||||||
.await;
|
|
||||||
async_input.wait_for_any_edge().await.unwrap();
|
|
||||||
rprintln!(
|
|
||||||
"{}: input pin had a falling (any) edge ({} ms)",
|
|
||||||
ctx,
|
|
||||||
Instant::now().as_millis()
|
|
||||||
);
|
|
||||||
|
|
||||||
rprintln!(
|
|
||||||
"{}: sending Falling command ({} ms)",
|
|
||||||
ctx,
|
|
||||||
Instant::now().as_millis()
|
|
||||||
);
|
|
||||||
sender.send(GpioCmd::new(GpioCmdType::RisingEdge, 20)).await;
|
|
||||||
async_input.wait_for_any_edge().await.unwrap();
|
|
||||||
rprintln!(
|
|
||||||
"{}: input pin had a rising (any) edge ({} ms)",
|
|
||||||
ctx,
|
|
||||||
Instant::now().as_millis()
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
#[embassy_executor::task(pool_size = 2)]
|
|
||||||
async fn output_task(
|
|
||||||
ctx: &'static str,
|
|
||||||
mut out: DynPin,
|
|
||||||
receiver: Receiver<'static, ThreadModeRawMutex, GpioCmd, 3>,
|
|
||||||
) {
|
|
||||||
loop {
|
|
||||||
let next_cmd = receiver.receive().await;
|
|
||||||
Timer::after(Duration::from_millis(next_cmd.after_delay.into())).await;
|
|
||||||
match next_cmd.cmd_type {
|
|
||||||
GpioCmdType::SetHigh => {
|
|
||||||
rprintln!("{}: Set output high", ctx);
|
|
||||||
out.set_high().unwrap();
|
|
||||||
}
|
|
||||||
GpioCmdType::SetLow => {
|
|
||||||
rprintln!("{}: Set output low", ctx);
|
|
||||||
out.set_low().unwrap();
|
|
||||||
}
|
|
||||||
GpioCmdType::RisingEdge => {
|
|
||||||
rprintln!("{}: Rising edge", ctx);
|
|
||||||
if !out.is_low().unwrap() {
|
|
||||||
out.set_low().unwrap();
|
|
||||||
}
|
|
||||||
out.set_high().unwrap();
|
|
||||||
}
|
|
||||||
GpioCmdType::FallingEdge => {
|
|
||||||
rprintln!("{}: Falling edge", ctx);
|
|
||||||
if !out.is_high().unwrap() {
|
|
||||||
out.set_high().unwrap();
|
|
||||||
}
|
|
||||||
out.set_low().unwrap();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// PB22 to PB23 can be handled by both OC10 and OC11 depending on configuration.
|
|
||||||
#[interrupt]
|
|
||||||
#[allow(non_snake_case)]
|
|
||||||
fn OC10() {
|
|
||||||
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_async_gpio_for_port(Port::B);
|
|
||||||
}
|
|
@ -1,167 +0,0 @@
|
|||||||
//! Asynchronous UART reception example application.
|
|
||||||
//!
|
|
||||||
//! This application receives data on two UARTs permanently using a ring buffer.
|
|
||||||
//! The ring buffer are read them asynchronously. UART A is received on ports PA8 and PA9.
|
|
||||||
//! UART B is received on ports PA2 and PA3.
|
|
||||||
//!
|
|
||||||
//! Instructions:
|
|
||||||
//!
|
|
||||||
//! 1. Tie a USB to UART converter with RX to PA9 and TX to PA8 for UART A.
|
|
||||||
//! Tie a USB to UART converter with RX to PA3 and TX to PA2 for UART B.
|
|
||||||
//! 2. Connect to the serial interface by using an application like Putty or picocom. You can
|
|
||||||
//! type something in the terminal and check if the data is echoed back. You can also check the
|
|
||||||
//! RTT logs to see received data.
|
|
||||||
#![no_std]
|
|
||||||
#![no_main]
|
|
||||||
use core::cell::RefCell;
|
|
||||||
|
|
||||||
use critical_section::Mutex;
|
|
||||||
use embassy_executor::Spawner;
|
|
||||||
use embassy_time::Instant;
|
|
||||||
use embedded_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_hal::{
|
|
||||||
gpio::PinsA,
|
|
||||||
pac::{self, interrupt},
|
|
||||||
prelude::*,
|
|
||||||
uart::{
|
|
||||||
self, on_interrupt_rx_overwriting,
|
|
||||||
rx_asynch::{on_interrupt_rx, RxAsync},
|
|
||||||
Bank, RxAsyncOverwriting, Tx,
|
|
||||||
},
|
|
||||||
InterruptConfig,
|
|
||||||
};
|
|
||||||
|
|
||||||
const SYSCLK_FREQ: Hertz = Hertz::from_raw(50_000_000);
|
|
||||||
|
|
||||||
static QUEUE_UART_A: static_cell::ConstStaticCell<Queue<u8, 256>> =
|
|
||||||
static_cell::ConstStaticCell::new(Queue::new());
|
|
||||||
static PRODUCER_UART_A: Mutex<RefCell<Option<Producer<u8, 256>>>> = Mutex::new(RefCell::new(None));
|
|
||||||
|
|
||||||
static QUEUE_UART_B: static_cell::ConstStaticCell<Queue<u8, 256>> =
|
|
||||||
static_cell::ConstStaticCell::new(Queue::new());
|
|
||||||
static PRODUCER_UART_B: Mutex<RefCell<Option<Producer<u8, 256>>>> = Mutex::new(RefCell::new(None));
|
|
||||||
static CONSUMER_UART_B: Mutex<RefCell<Option<Consumer<u8, 256>>>> = Mutex::new(RefCell::new(None));
|
|
||||||
|
|
||||||
// main is itself an async function.
|
|
||||||
#[embassy_executor::main]
|
|
||||||
async fn main(spawner: Spawner) {
|
|
||||||
rtt_init_print!();
|
|
||||||
rprintln!("-- VA108xx Async UART RX Demo --");
|
|
||||||
|
|
||||||
let mut dp = pac::Peripherals::take().unwrap();
|
|
||||||
|
|
||||||
// Safety: Only called once here.
|
|
||||||
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();
|
|
||||||
let mut led1 = porta.pa7.into_readable_push_pull_output();
|
|
||||||
let mut led2 = porta.pa6.into_readable_push_pull_output();
|
|
||||||
|
|
||||||
let tx_uart_a = porta.pa9.into_funsel_2();
|
|
||||||
let rx_uart_a = porta.pa8.into_funsel_2();
|
|
||||||
|
|
||||||
let uarta = uart::Uart::new_with_interrupt(
|
|
||||||
&mut dp.sysconfig,
|
|
||||||
50.MHz(),
|
|
||||||
dp.uarta,
|
|
||||||
(tx_uart_a, rx_uart_a),
|
|
||||||
115200.Hz(),
|
|
||||||
InterruptConfig::new(pac::Interrupt::OC2, true, true),
|
|
||||||
);
|
|
||||||
|
|
||||||
let tx_uart_b = porta.pa3.into_funsel_2();
|
|
||||||
let rx_uart_b = porta.pa2.into_funsel_2();
|
|
||||||
|
|
||||||
let uartb = uart::Uart::new_with_interrupt(
|
|
||||||
&mut dp.sysconfig,
|
|
||||||
50.MHz(),
|
|
||||||
dp.uartb,
|
|
||||||
(tx_uart_b, rx_uart_b),
|
|
||||||
115200.Hz(),
|
|
||||||
InterruptConfig::new(pac::Interrupt::OC3, true, true),
|
|
||||||
);
|
|
||||||
let (mut tx_uart_a, rx_uart_a) = uarta.split();
|
|
||||||
let (tx_uart_b, rx_uart_b) = uartb.split();
|
|
||||||
let (prod_uart_a, cons_uart_a) = QUEUE_UART_A.take().split();
|
|
||||||
// Pass the producer to the interrupt handler.
|
|
||||||
let (prod_uart_b, cons_uart_b) = QUEUE_UART_B.take().split();
|
|
||||||
critical_section::with(|cs| {
|
|
||||||
*PRODUCER_UART_A.borrow(cs).borrow_mut() = Some(prod_uart_a);
|
|
||||||
*PRODUCER_UART_B.borrow(cs).borrow_mut() = Some(prod_uart_b);
|
|
||||||
*CONSUMER_UART_B.borrow(cs).borrow_mut() = Some(cons_uart_b);
|
|
||||||
});
|
|
||||||
let mut async_rx_uart_a = RxAsync::new(rx_uart_a, cons_uart_a);
|
|
||||||
let async_rx_uart_b = 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();
|
|
||||||
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!(
|
|
||||||
"Read {} bytes asynchronously on UART A: {:?}",
|
|
||||||
read_bytes,
|
|
||||||
read_str
|
|
||||||
);
|
|
||||||
tx_uart_a.write_all(read_str.as_bytes()).unwrap();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[embassy_executor::task]
|
|
||||||
async fn uart_b_task(mut async_rx: RxAsyncOverwriting<pac::Uartb, 256>, mut tx: Tx<pac::Uartb>) {
|
|
||||||
let mut buf = [0u8; 256];
|
|
||||||
loop {
|
|
||||||
rprintln!("Current time UART B: {}", Instant::now().as_secs());
|
|
||||||
// Infallible asynchronous operation.
|
|
||||||
let read_bytes = async_rx.read(&mut buf).await.unwrap();
|
|
||||||
let read_str = core::str::from_utf8(&buf[..read_bytes]).unwrap();
|
|
||||||
rprintln!(
|
|
||||||
"Read {} bytes asynchronously on UART B: {:?}",
|
|
||||||
read_bytes,
|
|
||||||
read_str
|
|
||||||
);
|
|
||||||
tx.write_all(read_str.as_bytes()).unwrap();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[interrupt]
|
|
||||||
#[allow(non_snake_case)]
|
|
||||||
fn OC2() {
|
|
||||||
let mut prod =
|
|
||||||
critical_section::with(|cs| PRODUCER_UART_A.borrow(cs).borrow_mut().take().unwrap());
|
|
||||||
let errors = on_interrupt_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 {
|
|
||||||
rprintln!("UART A errors: {:?}", errors);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[interrupt]
|
|
||||||
#[allow(non_snake_case)]
|
|
||||||
fn OC3() {
|
|
||||||
let mut prod =
|
|
||||||
critical_section::with(|cs| PRODUCER_UART_B.borrow(cs).borrow_mut().take().unwrap());
|
|
||||||
let errors = on_interrupt_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 {
|
|
||||||
rprintln!("UART B errors: {:?}", errors);
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,93 +0,0 @@
|
|||||||
//! Asynchronous UART transmission example application.
|
|
||||||
//!
|
|
||||||
//! This application receives sends 4 strings with different sizes permanently using UART A.
|
|
||||||
//! Ports PA8 and PA9 are used for this.
|
|
||||||
//!
|
|
||||||
//! Instructions:
|
|
||||||
//!
|
|
||||||
//! 1. Tie a USB to UART converter with RX to PA9 and TX to PA8 for UART A.
|
|
||||||
//! 2. Connect to the serial interface by using an application like Putty or picocom. You can
|
|
||||||
//! can verify the correctness of the sent strings.
|
|
||||||
#![no_std]
|
|
||||||
#![no_main]
|
|
||||||
use embassy_executor::Spawner;
|
|
||||||
use embassy_time::{Duration, Instant, Ticker};
|
|
||||||
use embedded_io_async::Write;
|
|
||||||
use panic_rtt_target as _;
|
|
||||||
use rtt_target::{rprintln, rtt_init_print};
|
|
||||||
use va108xx_hal::{
|
|
||||||
gpio::PinsA,
|
|
||||||
pac::{self, interrupt},
|
|
||||||
prelude::*,
|
|
||||||
uart::{self, on_interrupt_tx, Bank, TxAsync},
|
|
||||||
InterruptConfig,
|
|
||||||
};
|
|
||||||
|
|
||||||
const SYSCLK_FREQ: Hertz = Hertz::from_raw(50_000_000);
|
|
||||||
|
|
||||||
const STR_LIST: &[&str] = &[
|
|
||||||
"Hello World\r\n",
|
|
||||||
"Smoll\r\n",
|
|
||||||
"A string which is larger than the FIFO size\r\n",
|
|
||||||
"A really large string which is significantly larger than the FIFO size\r\n",
|
|
||||||
];
|
|
||||||
|
|
||||||
// main is itself an async function.
|
|
||||||
#[embassy_executor::main]
|
|
||||||
async fn main(_spawner: Spawner) {
|
|
||||||
rtt_init_print!();
|
|
||||||
rprintln!("-- VA108xx Async UART TX Demo --");
|
|
||||||
|
|
||||||
let mut dp = pac::Peripherals::take().unwrap();
|
|
||||||
|
|
||||||
// Safety: Only called once here.
|
|
||||||
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();
|
|
||||||
let mut led1 = porta.pa7.into_readable_push_pull_output();
|
|
||||||
let mut led2 = porta.pa6.into_readable_push_pull_output();
|
|
||||||
|
|
||||||
let tx = porta.pa9.into_funsel_2();
|
|
||||||
let rx = porta.pa8.into_funsel_2();
|
|
||||||
|
|
||||||
let uarta = uart::Uart::new_with_interrupt(
|
|
||||||
&mut dp.sysconfig,
|
|
||||||
50.MHz(),
|
|
||||||
dp.uarta,
|
|
||||||
(tx, rx),
|
|
||||||
115200.Hz(),
|
|
||||||
InterruptConfig::new(pac::Interrupt::OC2, true, true),
|
|
||||||
);
|
|
||||||
let (tx, _rx) = uarta.split();
|
|
||||||
let mut async_tx = TxAsync::new(tx);
|
|
||||||
let mut ticker = Ticker::every(Duration::from_secs(1));
|
|
||||||
let mut idx = 0;
|
|
||||||
loop {
|
|
||||||
rprintln!("Current time: {}", Instant::now().as_secs());
|
|
||||||
led0.toggle();
|
|
||||||
led1.toggle();
|
|
||||||
led2.toggle();
|
|
||||||
let _written = async_tx
|
|
||||||
.write(STR_LIST[idx].as_bytes())
|
|
||||||
.await
|
|
||||||
.expect("writing failed");
|
|
||||||
idx += 1;
|
|
||||||
if idx == STR_LIST.len() {
|
|
||||||
idx = 0;
|
|
||||||
}
|
|
||||||
ticker.next().await;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[interrupt]
|
|
||||||
#[allow(non_snake_case)]
|
|
||||||
fn OC2() {
|
|
||||||
on_interrupt_tx(Bank::A);
|
|
||||||
}
|
|
4
examples/embassy/src/lib.rs
Normal file
4
examples/embassy/src/lib.rs
Normal file
@ -0,0 +1,4 @@
|
|||||||
|
#![no_std]
|
||||||
|
pub mod time_driver;
|
||||||
|
|
||||||
|
pub use time_driver::init;
|
@ -2,17 +2,9 @@
|
|||||||
#![no_main]
|
#![no_main]
|
||||||
use embassy_executor::Spawner;
|
use embassy_executor::Spawner;
|
||||||
use embassy_time::{Duration, Instant, Ticker};
|
use embassy_time::{Duration, Instant, Ticker};
|
||||||
|
use embedded_hal::digital::StatefulOutputPin;
|
||||||
use panic_rtt_target as _;
|
use panic_rtt_target as _;
|
||||||
use rtt_target::{rprintln, rtt_init_print};
|
use rtt_target::{rprintln, rtt_init_print};
|
||||||
|
|
||||||
cfg_if::cfg_if! {
|
|
||||||
if #[cfg(feature = "custom-irqs")] {
|
|
||||||
use va108xx_embassy::embassy_time_driver_irqs;
|
|
||||||
use va108xx_hal::pac::interrupt;
|
|
||||||
embassy_time_driver_irqs!(timekeeper_irq = OC23, alarm_irq = OC24);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
use va108xx_hal::{gpio::PinsA, pac, prelude::*};
|
use va108xx_hal::{gpio::PinsA, pac, prelude::*};
|
||||||
|
|
||||||
const SYSCLK_FREQ: Hertz = Hertz::from_raw(50_000_000);
|
const SYSCLK_FREQ: Hertz = Hertz::from_raw(50_000_000);
|
||||||
@ -26,29 +18,17 @@ async fn main(_spawner: Spawner) {
|
|||||||
let mut dp = pac::Peripherals::take().unwrap();
|
let mut dp = pac::Peripherals::take().unwrap();
|
||||||
|
|
||||||
// Safety: Only called once here.
|
// Safety: Only called once here.
|
||||||
cfg_if::cfg_if! {
|
unsafe {
|
||||||
if #[cfg(not(feature = "custom-irqs"))] {
|
embassy_example::init(
|
||||||
va108xx_embassy::init(
|
&mut dp.sysconfig,
|
||||||
&mut dp.sysconfig,
|
&dp.irqsel,
|
||||||
&dp.irqsel,
|
SYSCLK_FREQ,
|
||||||
SYSCLK_FREQ,
|
dp.tim23,
|
||||||
dp.tim23,
|
dp.tim22,
|
||||||
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,
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
let porta = PinsA::new(&mut dp.sysconfig, dp.porta);
|
let porta = PinsA::new(&mut dp.sysconfig, Some(dp.ioconfig), dp.porta);
|
||||||
let mut led0 = porta.pa10.into_readable_push_pull_output();
|
let mut led0 = porta.pa10.into_readable_push_pull_output();
|
||||||
let mut led1 = porta.pa7.into_readable_push_pull_output();
|
let mut led1 = porta.pa7.into_readable_push_pull_output();
|
||||||
let mut led2 = porta.pa6.into_readable_push_pull_output();
|
let mut led2 = porta.pa6.into_readable_push_pull_output();
|
||||||
@ -56,8 +36,8 @@ async fn main(_spawner: Spawner) {
|
|||||||
loop {
|
loop {
|
||||||
ticker.next().await;
|
ticker.next().await;
|
||||||
rprintln!("Current time: {}", Instant::now().as_secs());
|
rprintln!("Current time: {}", Instant::now().as_secs());
|
||||||
led0.toggle();
|
led0.toggle().ok();
|
||||||
led1.toggle();
|
led1.toggle().ok();
|
||||||
led2.toggle();
|
led2.toggle().ok();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
333
examples/embassy/src/time_driver.rs
Normal file
333
examples/embassy/src/time_driver.rs
Normal file
@ -0,0 +1,333 @@
|
|||||||
|
//! This is a sample time driver implementation for the VA108xx family of devices, supporting
|
||||||
|
//! one alarm and requiring/reserving 2 TIM peripherals. You could adapt this implementation to
|
||||||
|
//! support more alarms.
|
||||||
|
//!
|
||||||
|
//! This driver implementation reserves interrupts OC31 and OC30 for the timekeeping.
|
||||||
|
use core::{cell::Cell, mem, ptr};
|
||||||
|
use critical_section::CriticalSection;
|
||||||
|
use embassy_sync::blocking_mutex::raw::CriticalSectionRawMutex;
|
||||||
|
use embassy_sync::blocking_mutex::Mutex;
|
||||||
|
use portable_atomic::{AtomicU32, AtomicU8, Ordering};
|
||||||
|
|
||||||
|
use embassy_time_driver::{time_driver_impl, AlarmHandle, Driver, TICK_HZ};
|
||||||
|
use once_cell::sync::OnceCell;
|
||||||
|
use va108xx_hal::{
|
||||||
|
clock::enable_peripheral_clock,
|
||||||
|
enable_interrupt,
|
||||||
|
pac::{self, interrupt},
|
||||||
|
prelude::*,
|
||||||
|
timer::{enable_tim_clk, ValidTim},
|
||||||
|
PeripheralSelect,
|
||||||
|
};
|
||||||
|
|
||||||
|
pub type TimekeeperClk = pac::Tim23;
|
||||||
|
pub type AlarmClk0 = pac::Tim22;
|
||||||
|
pub type AlarmClk1 = pac::Tim21;
|
||||||
|
pub type AlarmClk2 = pac::Tim20;
|
||||||
|
|
||||||
|
const TIMEKEEPER_IRQ: pac::Interrupt = pac::Interrupt::OC31;
|
||||||
|
const ALARM_IRQ: pac::Interrupt = pac::Interrupt::OC30;
|
||||||
|
|
||||||
|
/// 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(
|
||||||
|
syscfg: &mut pac::Sysconfig,
|
||||||
|
irqsel: &pac::Irqsel,
|
||||||
|
sysclk: impl Into<Hertz>,
|
||||||
|
timekeeper: TimekeeperClk,
|
||||||
|
alarm_tim: AlarmClk0,
|
||||||
|
) {
|
||||||
|
DRIVER.init(syscfg, irqsel, sysclk, timekeeper, alarm_tim)
|
||||||
|
}
|
||||||
|
|
||||||
|
time_driver_impl!(
|
||||||
|
static DRIVER: TimerDriverEmbassy = TimerDriverEmbassy {
|
||||||
|
periods: AtomicU32::new(0),
|
||||||
|
alarm_count: AtomicU8::new(0),
|
||||||
|
alarms: Mutex::const_new(CriticalSectionRawMutex::new(), [AlarmState::new(); ALARM_COUNT])
|
||||||
|
});
|
||||||
|
|
||||||
|
/// Timekeeper interrupt.
|
||||||
|
#[interrupt]
|
||||||
|
#[allow(non_snake_case)]
|
||||||
|
fn OC31() {
|
||||||
|
DRIVER.on_interrupt_timekeeping()
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Alarm timer interrupt.
|
||||||
|
#[interrupt]
|
||||||
|
#[allow(non_snake_case)]
|
||||||
|
fn OC30() {
|
||||||
|
DRIVER.on_interrupt_alarm(0)
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline(always)]
|
||||||
|
const fn alarm_tim(idx: usize) -> &'static pac::tim0::RegisterBlock {
|
||||||
|
// Safety: This is a static memory-mapped peripheral.
|
||||||
|
match idx {
|
||||||
|
0 => unsafe { &*AlarmClk0::ptr() },
|
||||||
|
1 => unsafe { &*AlarmClk1::ptr() },
|
||||||
|
2 => unsafe { &*AlarmClk2::ptr() },
|
||||||
|
_ => {
|
||||||
|
panic!("invalid alarm timer index")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline(always)]
|
||||||
|
const fn timekeeping_tim() -> &'static pac::tim0::RegisterBlock {
|
||||||
|
// Safety: This is a memory-mapped peripheral.
|
||||||
|
unsafe { &*TimekeeperClk::ptr() }
|
||||||
|
}
|
||||||
|
|
||||||
|
struct AlarmState {
|
||||||
|
timestamp: Cell<u64>,
|
||||||
|
|
||||||
|
// This is really a Option<(fn(*mut ()), *mut ())>
|
||||||
|
// but fn pointers aren't allowed in const yet
|
||||||
|
callback: Cell<*const ()>,
|
||||||
|
ctx: Cell<*mut ()>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl AlarmState {
|
||||||
|
const fn new() -> Self {
|
||||||
|
Self {
|
||||||
|
timestamp: Cell::new(u64::MAX),
|
||||||
|
callback: Cell::new(ptr::null()),
|
||||||
|
ctx: Cell::new(ptr::null_mut()),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
unsafe impl Send for AlarmState {}
|
||||||
|
|
||||||
|
const ALARM_COUNT: usize = 1;
|
||||||
|
|
||||||
|
static SCALE: OnceCell<u64> = OnceCell::new();
|
||||||
|
|
||||||
|
pub struct TimerDriverEmbassy {
|
||||||
|
periods: AtomicU32,
|
||||||
|
alarm_count: AtomicU8,
|
||||||
|
/// Timestamp at which to fire alarm. u64::MAX if no alarm is scheduled.
|
||||||
|
alarms: Mutex<CriticalSectionRawMutex, [AlarmState; ALARM_COUNT]>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl TimerDriverEmbassy {
|
||||||
|
fn init(
|
||||||
|
&self,
|
||||||
|
syscfg: &mut pac::Sysconfig,
|
||||||
|
irqsel: &pac::Irqsel,
|
||||||
|
sysclk: impl Into<Hertz>,
|
||||||
|
timekeeper: TimekeeperClk,
|
||||||
|
alarm_tim: AlarmClk0,
|
||||||
|
) {
|
||||||
|
enable_peripheral_clock(syscfg, PeripheralSelect::Irqsel);
|
||||||
|
enable_tim_clk(syscfg, TimekeeperClk::TIM_ID);
|
||||||
|
let sysclk = sysclk.into();
|
||||||
|
// Initiate scale value here. This is required to convert timer ticks back to a timestamp.
|
||||||
|
SCALE.set((sysclk.raw() / TICK_HZ as u32) as u64).unwrap();
|
||||||
|
timekeeper
|
||||||
|
.rst_value()
|
||||||
|
.write(|w| unsafe { w.bits(u32::MAX) });
|
||||||
|
// Decrementing counter.
|
||||||
|
timekeeper
|
||||||
|
.cnt_value()
|
||||||
|
.write(|w| unsafe { w.bits(u32::MAX) });
|
||||||
|
// Switch on. Timekeeping should always be done.
|
||||||
|
irqsel
|
||||||
|
.tim0(TimekeeperClk::TIM_ID as usize)
|
||||||
|
.write(|w| unsafe { w.bits(TIMEKEEPER_IRQ as u32) });
|
||||||
|
unsafe {
|
||||||
|
enable_interrupt(TIMEKEEPER_IRQ);
|
||||||
|
}
|
||||||
|
timekeeper.ctrl().modify(|_, w| w.irq_enb().set_bit());
|
||||||
|
timekeeper.enable().write(|w| unsafe { w.bits(1) });
|
||||||
|
|
||||||
|
enable_tim_clk(syscfg, AlarmClk0::TIM_ID);
|
||||||
|
|
||||||
|
// Explicitely disable alarm timer until needed.
|
||||||
|
alarm_tim.ctrl().modify(|_, w| {
|
||||||
|
w.irq_enb().clear_bit();
|
||||||
|
w.enable().clear_bit()
|
||||||
|
});
|
||||||
|
// Enable general interrupts. The IRQ enable of the peripheral remains cleared.
|
||||||
|
unsafe {
|
||||||
|
enable_interrupt(ALARM_IRQ);
|
||||||
|
}
|
||||||
|
irqsel
|
||||||
|
.tim0(AlarmClk0::TIM_ID as usize)
|
||||||
|
.write(|w| unsafe { w.bits(ALARM_IRQ as u32) });
|
||||||
|
}
|
||||||
|
|
||||||
|
// Should be called inside the IRQ of the timekeeper timer.
|
||||||
|
fn on_interrupt_timekeeping(&self) {
|
||||||
|
self.next_period();
|
||||||
|
}
|
||||||
|
|
||||||
|
// Should be called inside the IRQ of the alarm timer.
|
||||||
|
fn on_interrupt_alarm(&self, idx: usize) {
|
||||||
|
critical_section::with(|cs| {
|
||||||
|
if self.alarms.borrow(cs)[idx].timestamp.get() <= self.now() {
|
||||||
|
self.trigger_alarm(idx, cs)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
fn next_period(&self) {
|
||||||
|
let period = self.periods.fetch_add(1, Ordering::AcqRel) + 1;
|
||||||
|
let t = (period as u64) << 32;
|
||||||
|
critical_section::with(|cs| {
|
||||||
|
for i in 0..ALARM_COUNT {
|
||||||
|
let alarm = &self.alarms.borrow(cs)[i];
|
||||||
|
let at = alarm.timestamp.get();
|
||||||
|
let alarm_tim = alarm_tim(0);
|
||||||
|
if at < t {
|
||||||
|
self.trigger_alarm(i, cs);
|
||||||
|
} else {
|
||||||
|
let remaining_ticks = (at - t) * *SCALE.get().unwrap();
|
||||||
|
if remaining_ticks <= u32::MAX as u64 {
|
||||||
|
alarm_tim.enable().write(|w| unsafe { w.bits(0) });
|
||||||
|
alarm_tim
|
||||||
|
.cnt_value()
|
||||||
|
.write(|w| unsafe { w.bits(remaining_ticks as u32) });
|
||||||
|
alarm_tim.ctrl().modify(|_, w| w.irq_enb().set_bit());
|
||||||
|
alarm_tim.enable().write(|w| unsafe { w.bits(1) })
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
fn get_alarm<'a>(&'a self, cs: CriticalSection<'a>, alarm: AlarmHandle) -> &'a AlarmState {
|
||||||
|
// safety: we're allowed to assume the AlarmState is created by us, and
|
||||||
|
// we never create one that's out of bounds.
|
||||||
|
unsafe { self.alarms.borrow(cs).get_unchecked(alarm.id() as usize) }
|
||||||
|
}
|
||||||
|
|
||||||
|
fn trigger_alarm(&self, n: usize, cs: CriticalSection) {
|
||||||
|
alarm_tim(n).ctrl().modify(|_, w| {
|
||||||
|
w.irq_enb().clear_bit();
|
||||||
|
w.enable().clear_bit()
|
||||||
|
});
|
||||||
|
|
||||||
|
let alarm = &self.alarms.borrow(cs)[n];
|
||||||
|
// Setting the maximum value disables the alarm.
|
||||||
|
alarm.timestamp.set(u64::MAX);
|
||||||
|
|
||||||
|
// Call after clearing alarm, so the callback can set another alarm.
|
||||||
|
|
||||||
|
// safety:
|
||||||
|
// - we can ignore the possiblity of `f` being unset (null) because of the safety contract of `allocate_alarm`.
|
||||||
|
// - other than that we only store valid function pointers into alarm.callback
|
||||||
|
let f: fn(*mut ()) = unsafe { mem::transmute(alarm.callback.get()) };
|
||||||
|
f(alarm.ctx.get());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Driver for TimerDriverEmbassy {
|
||||||
|
fn now(&self) -> u64 {
|
||||||
|
if SCALE.get().is_none() {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
let mut period1: u32;
|
||||||
|
let mut period2: u32;
|
||||||
|
let mut counter_val: u32;
|
||||||
|
|
||||||
|
loop {
|
||||||
|
// Acquire ensures that we get the latest value of `periods` and
|
||||||
|
// no instructions can be reordered before the load.
|
||||||
|
period1 = self.periods.load(Ordering::Acquire);
|
||||||
|
|
||||||
|
counter_val = u32::MAX - timekeeping_tim().cnt_value().read().bits();
|
||||||
|
|
||||||
|
// Double read to protect against race conditions when the counter is overflowing.
|
||||||
|
period2 = self.periods.load(Ordering::Relaxed);
|
||||||
|
if period1 == period2 {
|
||||||
|
let now = (((period1 as u64) << 32) | counter_val as u64) / *SCALE.get().unwrap();
|
||||||
|
return now;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
unsafe fn allocate_alarm(&self) -> Option<AlarmHandle> {
|
||||||
|
let id = self
|
||||||
|
.alarm_count
|
||||||
|
.fetch_update(Ordering::AcqRel, Ordering::Acquire, |x| {
|
||||||
|
if x < ALARM_COUNT as u8 {
|
||||||
|
Some(x + 1)
|
||||||
|
} else {
|
||||||
|
None
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
match id {
|
||||||
|
Ok(id) => Some(AlarmHandle::new(id)),
|
||||||
|
Err(_) => None,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn set_alarm_callback(
|
||||||
|
&self,
|
||||||
|
alarm: embassy_time_driver::AlarmHandle,
|
||||||
|
callback: fn(*mut ()),
|
||||||
|
ctx: *mut (),
|
||||||
|
) {
|
||||||
|
critical_section::with(|cs| {
|
||||||
|
let alarm = self.get_alarm(cs, alarm);
|
||||||
|
|
||||||
|
alarm.callback.set(callback as *const ());
|
||||||
|
alarm.ctx.set(ctx);
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
fn set_alarm(&self, alarm: embassy_time_driver::AlarmHandle, timestamp: u64) -> bool {
|
||||||
|
if SCALE.get().is_none() {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
critical_section::with(|cs| {
|
||||||
|
let n = alarm.id();
|
||||||
|
let alarm_tim = alarm_tim(n.into());
|
||||||
|
alarm_tim.ctrl().modify(|_, w| {
|
||||||
|
w.irq_enb().clear_bit();
|
||||||
|
w.enable().clear_bit()
|
||||||
|
});
|
||||||
|
|
||||||
|
let alarm = self.get_alarm(cs, alarm);
|
||||||
|
alarm.timestamp.set(timestamp);
|
||||||
|
|
||||||
|
let t = self.now();
|
||||||
|
if timestamp <= t {
|
||||||
|
alarm.timestamp.set(u64::MAX);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// If it hasn't triggered yet, setup the relevant reset value, regardless of whether
|
||||||
|
// the interrupts are enabled or not. When they are enabled at a later point, the
|
||||||
|
// right value is already set.
|
||||||
|
|
||||||
|
// If the timestamp is in the next few ticks, add a bit of buffer to be sure the alarm
|
||||||
|
// is not missed.
|
||||||
|
//
|
||||||
|
// This means that an alarm can be delayed for up to 2 ticks (from t+1 to t+3), but this is allowed
|
||||||
|
// by the Alarm trait contract. What's not allowed is triggering alarms *before* their scheduled time,
|
||||||
|
// and we don't do that here.
|
||||||
|
let safe_timestamp = timestamp.max(t + 3);
|
||||||
|
let timer_ticks = (safe_timestamp - t) * *SCALE.get().unwrap();
|
||||||
|
alarm_tim.rst_value().write(|w| unsafe { w.bits(u32::MAX) });
|
||||||
|
if timer_ticks <= u32::MAX as u64 {
|
||||||
|
alarm_tim
|
||||||
|
.cnt_value()
|
||||||
|
.write(|w| unsafe { w.bits(timer_ticks as u32) });
|
||||||
|
alarm_tim.ctrl().modify(|_, w| w.irq_enb().set_bit());
|
||||||
|
alarm_tim.enable().write(|w| unsafe { w.bits(1) });
|
||||||
|
}
|
||||||
|
// If it's too far in the future, don't enable timer yet.
|
||||||
|
// It will be enabled later by `next_period`.
|
||||||
|
|
||||||
|
true
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
@ -8,19 +8,37 @@ cortex-m = { version = "0.7", features = ["critical-section-single-core"] }
|
|||||||
cortex-m-rt = "0.7"
|
cortex-m-rt = "0.7"
|
||||||
embedded-hal = "1"
|
embedded-hal = "1"
|
||||||
embedded-io = "0.6"
|
embedded-io = "0.6"
|
||||||
rtt-target = "0.6"
|
rtt-target = { version = "0.5" }
|
||||||
panic-rtt-target = "0.2"
|
panic-rtt-target = { version = "0.1" }
|
||||||
|
|
||||||
# Even though we do not use this directly, we need to activate this feature explicitely
|
# Even though we do not use this directly, we need to activate this feature explicitely
|
||||||
# so that RTIC compiles because thumv6 does not have CAS operations natively.
|
# so that RTIC compiles because thumv6 does not have CAS operations natively.
|
||||||
portable-atomic = { version = "1", features = ["unsafe-assume-single-core"]}
|
portable-atomic = { version = "1", features = ["unsafe-assume-single-core"]}
|
||||||
|
|
||||||
rtic = { version = "2", features = ["thumbv6-backend"] }
|
[dependencies.rtic]
|
||||||
rtic-monotonics = { version = "2", features = ["cortex-m-systick"] }
|
version = "2"
|
||||||
rtic-sync = { version = "1.3", features = ["defmt-03"] }
|
features = ["thumbv6-backend"]
|
||||||
|
|
||||||
once_cell = {version = "1", default-features = false, features = ["critical-section"]}
|
[dependencies.rtic-monotonics]
|
||||||
ringbuf = { version = "0.4.7", default-features = false, features = ["portable-atomic"] }
|
version = "2"
|
||||||
|
features = ["cortex-m-systick"]
|
||||||
|
|
||||||
va108xx-hal = { version = "0.11", path = "../../va108xx-hal" }
|
[dependencies.rtic-sync]
|
||||||
vorago-reb1 = { version = "0.8", path = "../../vorago-reb1" }
|
version = "1.3"
|
||||||
|
features = ["defmt-03"]
|
||||||
|
|
||||||
|
[dependencies.once_cell]
|
||||||
|
version = "1"
|
||||||
|
default-features = false
|
||||||
|
features = ["critical-section"]
|
||||||
|
|
||||||
|
[dependencies.ringbuf]
|
||||||
|
version = "0.4.7"
|
||||||
|
default-features = false
|
||||||
|
features = ["portable-atomic"]
|
||||||
|
|
||||||
|
[dependencies.va108xx-hal]
|
||||||
|
version = "0.8"
|
||||||
|
|
||||||
|
[dependencies.vorago-reb1]
|
||||||
|
path = "../../vorago-reb1"
|
||||||
|
@ -12,7 +12,7 @@ mod app {
|
|||||||
gpio::{FilterType, InterruptEdge, PinsA},
|
gpio::{FilterType, InterruptEdge, PinsA},
|
||||||
pac,
|
pac,
|
||||||
prelude::*,
|
prelude::*,
|
||||||
timer::{default_ms_irq_handler, set_up_ms_tick, InterruptConfig},
|
timer::{default_ms_irq_handler, set_up_ms_tick, IrqCfg},
|
||||||
};
|
};
|
||||||
use vorago_reb1::button::Button;
|
use vorago_reb1::button::Button;
|
||||||
use vorago_reb1::leds::Leds;
|
use vorago_reb1::leds::Leds;
|
||||||
@ -61,24 +61,25 @@ mod app {
|
|||||||
rprintln!("Using {:?} mode", mode);
|
rprintln!("Using {:?} mode", mode);
|
||||||
|
|
||||||
let mut dp = cx.device;
|
let mut dp = cx.device;
|
||||||
let pinsa = PinsA::new(&mut dp.sysconfig, dp.porta);
|
let pinsa = PinsA::new(&mut dp.sysconfig, Some(dp.ioconfig), dp.porta);
|
||||||
let edge_irq = match mode {
|
let edge_irq = match mode {
|
||||||
PressMode::Toggle => InterruptEdge::HighToLow,
|
PressMode::Toggle => InterruptEdge::HighToLow,
|
||||||
PressMode::Keep => InterruptEdge::BothEdges,
|
PressMode::Keep => InterruptEdge::BothEdges,
|
||||||
};
|
};
|
||||||
|
|
||||||
// Configure an edge interrupt on the button and route it to interrupt vector 15
|
// Configure an edge interrupt on the button and route it to interrupt vector 15
|
||||||
let mut button = Button::new(pinsa.pa11.into_floating_input());
|
let mut button = Button::new(pinsa.pa11.into_floating_input()).edge_irq(
|
||||||
|
edge_irq,
|
||||||
|
IrqCfg::new(pac::interrupt::OC15, true, true),
|
||||||
|
Some(&mut dp.sysconfig),
|
||||||
|
Some(&mut dp.irqsel),
|
||||||
|
);
|
||||||
|
|
||||||
if mode == PressMode::Toggle {
|
if mode == PressMode::Toggle {
|
||||||
// This filter debounces the switch for edge based interrupts
|
// This filter debounces the switch for edge based interrupts
|
||||||
button.configure_filter_type(FilterType::FilterFourClockCycles, FilterClkSel::Clk1);
|
button = button.filter_type(FilterType::FilterFourClockCycles, FilterClkSel::Clk1);
|
||||||
set_clk_div_register(&mut dp.sysconfig, FilterClkSel::Clk1, 50_000);
|
set_clk_div_register(&mut dp.sysconfig, FilterClkSel::Clk1, 50_000);
|
||||||
}
|
}
|
||||||
button.configure_and_enable_edge_interrupt(
|
|
||||||
edge_irq,
|
|
||||||
InterruptConfig::new(pac::interrupt::OC15, true, true),
|
|
||||||
);
|
|
||||||
let mut leds = Leds::new(
|
let mut leds = Leds::new(
|
||||||
pinsa.pa10.into_push_pull_output(),
|
pinsa.pa10.into_push_pull_output(),
|
||||||
pinsa.pa7.into_push_pull_output(),
|
pinsa.pa7.into_push_pull_output(),
|
||||||
@ -88,7 +89,7 @@ mod app {
|
|||||||
led.off();
|
led.off();
|
||||||
}
|
}
|
||||||
set_up_ms_tick(
|
set_up_ms_tick(
|
||||||
InterruptConfig::new(pac::Interrupt::OC0, true, true),
|
IrqCfg::new(pac::Interrupt::OC0, true, true),
|
||||||
&mut dp.sysconfig,
|
&mut dp.sysconfig,
|
||||||
Some(&mut dp.irqsel),
|
Some(&mut dp.irqsel),
|
||||||
50.MHz(),
|
50.MHz(),
|
||||||
|
@ -23,13 +23,12 @@ mod app {
|
|||||||
gpio::PinsA,
|
gpio::PinsA,
|
||||||
pac,
|
pac,
|
||||||
prelude::*,
|
prelude::*,
|
||||||
uart::{self, RxWithInterrupt, Tx},
|
uart::{self, RxWithIrq, Tx},
|
||||||
InterruptConfig,
|
|
||||||
};
|
};
|
||||||
|
|
||||||
#[local]
|
#[local]
|
||||||
struct Local {
|
struct Local {
|
||||||
rx: RxWithInterrupt<pac::Uarta>,
|
rx: RxWithIrq<pac::Uarta>,
|
||||||
tx: Tx<pac::Uarta>,
|
tx: Tx<pac::Uarta>,
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -48,20 +47,19 @@ mod app {
|
|||||||
Mono::start(cx.core.SYST, SYSCLK_FREQ.raw());
|
Mono::start(cx.core.SYST, SYSCLK_FREQ.raw());
|
||||||
|
|
||||||
let mut dp = cx.device;
|
let mut dp = cx.device;
|
||||||
let gpioa = PinsA::new(&mut dp.sysconfig, dp.porta);
|
let gpioa = PinsA::new(&mut dp.sysconfig, Some(dp.ioconfig), dp.porta);
|
||||||
let tx = gpioa.pa9.into_funsel_2();
|
let tx = gpioa.pa9.into_funsel_2();
|
||||||
let rx = gpioa.pa8.into_funsel_2();
|
let rx = gpioa.pa8.into_funsel_2();
|
||||||
|
|
||||||
let irq_uart = uart::Uart::new_with_interrupt(
|
let irq_uart = uart::Uart::new(
|
||||||
&mut dp.sysconfig,
|
&mut dp.sysconfig,
|
||||||
SYSCLK_FREQ,
|
SYSCLK_FREQ,
|
||||||
dp.uarta,
|
dp.uarta,
|
||||||
(tx, rx),
|
(tx, rx),
|
||||||
115200.Hz(),
|
115200.Hz(),
|
||||||
InterruptConfig::new(pac::Interrupt::OC3, true, true),
|
|
||||||
);
|
);
|
||||||
let (tx, rx) = irq_uart.split();
|
let (tx, rx) = irq_uart.split();
|
||||||
let mut rx = rx.into_rx_with_irq();
|
let mut rx = rx.into_rx_with_irq(&mut dp.sysconfig, &mut dp.irqsel, pac::interrupt::OC3);
|
||||||
|
|
||||||
rx.start();
|
rx.start();
|
||||||
|
|
||||||
@ -70,7 +68,10 @@ mod app {
|
|||||||
Shared {
|
Shared {
|
||||||
rb: StaticRb::default(),
|
rb: StaticRb::default(),
|
||||||
},
|
},
|
||||||
Local { rx, tx },
|
Local {
|
||||||
|
rx,
|
||||||
|
tx,
|
||||||
|
},
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -92,7 +93,7 @@ mod app {
|
|||||||
fn reception_task(mut cx: reception_task::Context) {
|
fn reception_task(mut cx: reception_task::Context) {
|
||||||
let mut buf: [u8; 16] = [0; 16];
|
let mut buf: [u8; 16] = [0; 16];
|
||||||
let mut ringbuf_full = false;
|
let mut ringbuf_full = false;
|
||||||
let result = cx.local.rx.on_interrupt(&mut buf);
|
let result = cx.local.rx.irq_handler(&mut buf);
|
||||||
if result.bytes_read > 0 && result.errors.is_none() {
|
if result.bytes_read > 0 && result.errors.is_none() {
|
||||||
cx.shared.rb.lock(|rb| {
|
cx.shared.rb.lock(|rb| {
|
||||||
if rb.vacant_len() < result.bytes_read {
|
if rb.vacant_len() < result.bytes_read {
|
||||||
|
@ -5,6 +5,7 @@
|
|||||||
#[rtic::app(device = pac, dispatchers = [OC31, OC30, OC29])]
|
#[rtic::app(device = pac, dispatchers = [OC31, OC30, OC29])]
|
||||||
mod app {
|
mod app {
|
||||||
use cortex_m::asm;
|
use cortex_m::asm;
|
||||||
|
use embedded_hal::digital::StatefulOutputPin;
|
||||||
use panic_rtt_target as _;
|
use panic_rtt_target as _;
|
||||||
use rtic_example::SYSCLK_FREQ;
|
use rtic_example::SYSCLK_FREQ;
|
||||||
use rtic_monotonics::systick::prelude::*;
|
use rtic_monotonics::systick::prelude::*;
|
||||||
@ -34,7 +35,11 @@ mod app {
|
|||||||
|
|
||||||
Mono::start(cx.core.SYST, SYSCLK_FREQ.raw());
|
Mono::start(cx.core.SYST, SYSCLK_FREQ.raw());
|
||||||
|
|
||||||
let porta = PinsA::new(&mut cx.device.sysconfig, cx.device.porta);
|
let porta = PinsA::new(
|
||||||
|
&mut cx.device.sysconfig,
|
||||||
|
Some(cx.device.ioconfig),
|
||||||
|
cx.device.porta,
|
||||||
|
);
|
||||||
let led0 = porta.pa10.into_readable_push_pull_output();
|
let led0 = porta.pa10.into_readable_push_pull_output();
|
||||||
let led1 = porta.pa7.into_readable_push_pull_output();
|
let led1 = porta.pa7.into_readable_push_pull_output();
|
||||||
let led2 = porta.pa6.into_readable_push_pull_output();
|
let led2 = porta.pa6.into_readable_push_pull_output();
|
||||||
@ -57,9 +62,9 @@ mod app {
|
|||||||
async fn blinky(cx: blinky::Context) {
|
async fn blinky(cx: blinky::Context) {
|
||||||
loop {
|
loop {
|
||||||
rprintln!("toggling LEDs");
|
rprintln!("toggling LEDs");
|
||||||
cx.local.led0.toggle();
|
cx.local.led0.toggle().ok();
|
||||||
cx.local.led1.toggle();
|
cx.local.led1.toggle().ok();
|
||||||
cx.local.led2.toggle();
|
cx.local.led2.toggle().ok();
|
||||||
Mono::delay(1000.millis()).await;
|
Mono::delay(1000.millis()).await;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -6,20 +6,18 @@ edition = "2021"
|
|||||||
[dependencies]
|
[dependencies]
|
||||||
cortex-m = {version = "0.7", features = ["critical-section-single-core"]}
|
cortex-m = {version = "0.7", features = ["critical-section-single-core"]}
|
||||||
cortex-m-rt = "0.7"
|
cortex-m-rt = "0.7"
|
||||||
panic-halt = "1"
|
panic-halt = "0.2"
|
||||||
panic-rtt-target = "0.2"
|
panic-rtt-target = "0.1"
|
||||||
critical-section = "1"
|
critical-section = "1"
|
||||||
rtt-target = "0.6"
|
rtt-target = "0.5"
|
||||||
embedded-hal = "1"
|
embedded-hal = "1"
|
||||||
embedded-hal-nb = "1"
|
embedded-hal-nb = "1"
|
||||||
embedded-io = "0.6"
|
embedded-io = "0.6"
|
||||||
cortex-m-semihosting = "0.5.0"
|
cortex-m-semihosting = "0.5.0"
|
||||||
|
|
||||||
[dependencies.va108xx-hal]
|
[dependencies.va108xx-hal]
|
||||||
version = "0.11"
|
version = "0.8"
|
||||||
path = "../../va108xx-hal"
|
|
||||||
features = ["rt", "defmt"]
|
features = ["rt", "defmt"]
|
||||||
|
|
||||||
[dependencies.vorago-reb1]
|
[dependencies.vorago-reb1]
|
||||||
version = "0.8"
|
|
||||||
path = "../../vorago-reb1"
|
path = "../../vorago-reb1"
|
||||||
|
@ -7,7 +7,10 @@
|
|||||||
#![no_std]
|
#![no_std]
|
||||||
|
|
||||||
use cortex_m_rt::entry;
|
use cortex_m_rt::entry;
|
||||||
use embedded_hal::delay::DelayNs;
|
use embedded_hal::{
|
||||||
|
delay::DelayNs,
|
||||||
|
digital::{OutputPin, StatefulOutputPin},
|
||||||
|
};
|
||||||
use panic_halt as _;
|
use panic_halt as _;
|
||||||
use va108xx_hal::{
|
use va108xx_hal::{
|
||||||
gpio::PinsA,
|
gpio::PinsA,
|
||||||
@ -15,14 +18,14 @@ use va108xx_hal::{
|
|||||||
prelude::*,
|
prelude::*,
|
||||||
timer::DelayMs,
|
timer::DelayMs,
|
||||||
timer::{default_ms_irq_handler, set_up_ms_tick, CountdownTimer},
|
timer::{default_ms_irq_handler, set_up_ms_tick, CountdownTimer},
|
||||||
InterruptConfig,
|
IrqCfg,
|
||||||
};
|
};
|
||||||
|
|
||||||
#[entry]
|
#[entry]
|
||||||
fn main() -> ! {
|
fn main() -> ! {
|
||||||
let mut dp = pac::Peripherals::take().unwrap();
|
let mut dp = pac::Peripherals::take().unwrap();
|
||||||
let mut delay_ms = DelayMs::new(set_up_ms_tick(
|
let mut delay_ms = DelayMs::new(set_up_ms_tick(
|
||||||
InterruptConfig::new(interrupt::OC0, true, true),
|
IrqCfg::new(interrupt::OC0, true, true),
|
||||||
&mut dp.sysconfig,
|
&mut dp.sysconfig,
|
||||||
Some(&mut dp.irqsel),
|
Some(&mut dp.irqsel),
|
||||||
50.MHz(),
|
50.MHz(),
|
||||||
@ -30,26 +33,26 @@ fn main() -> ! {
|
|||||||
))
|
))
|
||||||
.unwrap();
|
.unwrap();
|
||||||
let mut delay_tim1 = CountdownTimer::new(&mut dp.sysconfig, 50.MHz(), dp.tim1);
|
let mut delay_tim1 = CountdownTimer::new(&mut dp.sysconfig, 50.MHz(), dp.tim1);
|
||||||
let porta = PinsA::new(&mut dp.sysconfig, dp.porta);
|
let porta = PinsA::new(&mut dp.sysconfig, Some(dp.ioconfig), dp.porta);
|
||||||
let mut led1 = porta.pa10.into_readable_push_pull_output();
|
let mut led1 = porta.pa10.into_readable_push_pull_output();
|
||||||
let mut led2 = porta.pa7.into_readable_push_pull_output();
|
let mut led2 = porta.pa7.into_readable_push_pull_output();
|
||||||
let mut led3 = porta.pa6.into_readable_push_pull_output();
|
let mut led3 = porta.pa6.into_readable_push_pull_output();
|
||||||
for _ in 0..10 {
|
for _ in 0..10 {
|
||||||
led1.set_low();
|
led1.set_low().ok();
|
||||||
led2.set_low();
|
led2.set_low().ok();
|
||||||
led3.set_low();
|
led3.set_low().ok();
|
||||||
delay_ms.delay_ms(200);
|
delay_ms.delay_ms(200);
|
||||||
led1.set_high();
|
led1.set_high().ok();
|
||||||
led2.set_high();
|
led2.set_high().ok();
|
||||||
led3.set_high();
|
led3.set_high().ok();
|
||||||
delay_tim1.delay_ms(200);
|
delay_tim1.delay_ms(200);
|
||||||
}
|
}
|
||||||
loop {
|
loop {
|
||||||
led1.toggle();
|
led1.toggle().ok();
|
||||||
delay_ms.delay_ms(200);
|
delay_ms.delay_ms(200);
|
||||||
led2.toggle();
|
led2.toggle().ok();
|
||||||
delay_tim1.delay_ms(200);
|
delay_tim1.delay_ms(200);
|
||||||
led3.toggle();
|
led3.toggle().ok();
|
||||||
delay_ms.delay_ms(200);
|
delay_ms.delay_ms(200);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -17,7 +17,7 @@ use va108xx_hal::{
|
|||||||
prelude::*,
|
prelude::*,
|
||||||
timer::{
|
timer::{
|
||||||
default_ms_irq_handler, set_up_ms_delay_provider, CascadeCtrl, CascadeSource,
|
default_ms_irq_handler, set_up_ms_delay_provider, CascadeCtrl, CascadeSource,
|
||||||
CountdownTimer, Event, InterruptConfig,
|
CountdownTimer, Event, IrqCfg,
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -39,7 +39,7 @@ fn main() -> ! {
|
|||||||
CountdownTimer::new(&mut dp.sysconfig, 50.MHz(), dp.tim3).auto_disable(true);
|
CountdownTimer::new(&mut dp.sysconfig, 50.MHz(), dp.tim3).auto_disable(true);
|
||||||
cascade_triggerer.listen(
|
cascade_triggerer.listen(
|
||||||
Event::TimeOut,
|
Event::TimeOut,
|
||||||
InterruptConfig::new(pac::Interrupt::OC1, true, false),
|
IrqCfg::new(pac::Interrupt::OC1, true, false),
|
||||||
Some(&mut dp.irqsel),
|
Some(&mut dp.irqsel),
|
||||||
Some(&mut dp.sysconfig),
|
Some(&mut dp.sysconfig),
|
||||||
);
|
);
|
||||||
@ -62,7 +62,7 @@ fn main() -> ! {
|
|||||||
// the timer expires
|
// the timer expires
|
||||||
cascade_target_1.listen(
|
cascade_target_1.listen(
|
||||||
Event::TimeOut,
|
Event::TimeOut,
|
||||||
InterruptConfig::new(pac::Interrupt::OC2, true, false),
|
IrqCfg::new(pac::Interrupt::OC2, true, false),
|
||||||
Some(&mut dp.irqsel),
|
Some(&mut dp.irqsel),
|
||||||
Some(&mut dp.sysconfig),
|
Some(&mut dp.sysconfig),
|
||||||
);
|
);
|
||||||
@ -88,7 +88,7 @@ fn main() -> ! {
|
|||||||
// the timer expires
|
// the timer expires
|
||||||
cascade_target_2.listen(
|
cascade_target_2.listen(
|
||||||
Event::TimeOut,
|
Event::TimeOut,
|
||||||
InterruptConfig::new(pac::Interrupt::OC3, true, false),
|
IrqCfg::new(pac::Interrupt::OC3, true, false),
|
||||||
Some(&mut dp.irqsel),
|
Some(&mut dp.irqsel),
|
||||||
Some(&mut dp.sysconfig),
|
Some(&mut dp.sysconfig),
|
||||||
);
|
);
|
||||||
|
@ -19,7 +19,7 @@ fn main() -> ! {
|
|||||||
rtt_init_print!();
|
rtt_init_print!();
|
||||||
rprintln!("-- VA108xx PWM example application--");
|
rprintln!("-- VA108xx PWM example application--");
|
||||||
let mut dp = pac::Peripherals::take().unwrap();
|
let mut dp = pac::Peripherals::take().unwrap();
|
||||||
let pinsa = PinsA::new(&mut dp.sysconfig, dp.porta);
|
let pinsa = PinsA::new(&mut dp.sysconfig, None, dp.porta);
|
||||||
let mut pwm = pwm::PwmPin::new(
|
let mut pwm = pwm::PwmPin::new(
|
||||||
&mut dp.sysconfig,
|
&mut dp.sysconfig,
|
||||||
50.MHz(),
|
50.MHz(),
|
||||||
|
@ -17,7 +17,7 @@ use va108xx_hal::{
|
|||||||
prelude::*,
|
prelude::*,
|
||||||
spi::{self, Spi, SpiBase, SpiClkConfig, TransferConfigWithHwcs},
|
spi::{self, Spi, SpiBase, SpiClkConfig, TransferConfigWithHwcs},
|
||||||
timer::{default_ms_irq_handler, set_up_ms_tick},
|
timer::{default_ms_irq_handler, set_up_ms_tick},
|
||||||
InterruptConfig,
|
IrqCfg,
|
||||||
};
|
};
|
||||||
|
|
||||||
#[derive(PartialEq, Debug)]
|
#[derive(PartialEq, Debug)]
|
||||||
@ -47,7 +47,7 @@ fn main() -> ! {
|
|||||||
rprintln!("-- VA108xx SPI example application--");
|
rprintln!("-- VA108xx SPI example application--");
|
||||||
let mut dp = pac::Peripherals::take().unwrap();
|
let mut dp = pac::Peripherals::take().unwrap();
|
||||||
let mut delay = set_up_ms_tick(
|
let mut delay = set_up_ms_tick(
|
||||||
InterruptConfig::new(interrupt::OC0, true, true),
|
IrqCfg::new(interrupt::OC0, true, true),
|
||||||
&mut dp.sysconfig,
|
&mut dp.sysconfig,
|
||||||
Some(&mut dp.irqsel),
|
Some(&mut dp.irqsel),
|
||||||
50.MHz(),
|
50.MHz(),
|
||||||
@ -58,8 +58,8 @@ fn main() -> ! {
|
|||||||
.expect("creating SPI clock config failed");
|
.expect("creating SPI clock config failed");
|
||||||
let spia_ref: RefCell<Option<SpiBase<pac::Spia, u8>>> = RefCell::new(None);
|
let spia_ref: RefCell<Option<SpiBase<pac::Spia, u8>>> = RefCell::new(None);
|
||||||
let spib_ref: RefCell<Option<SpiBase<pac::Spib, u8>>> = RefCell::new(None);
|
let spib_ref: RefCell<Option<SpiBase<pac::Spib, u8>>> = RefCell::new(None);
|
||||||
let pinsa = PinsA::new(&mut dp.sysconfig, dp.porta);
|
let pinsa = PinsA::new(&mut dp.sysconfig, None, dp.porta);
|
||||||
let pinsb = PinsB::new(&mut dp.sysconfig, dp.portb);
|
let pinsb = PinsB::new(&mut dp.sysconfig, Some(dp.ioconfig), dp.portb);
|
||||||
|
|
||||||
let mut spi_cfg = spi::SpiConfig::default();
|
let mut spi_cfg = spi::SpiConfig::default();
|
||||||
if EXAMPLE_SEL == ExampleSelect::Loopback {
|
if EXAMPLE_SEL == ExampleSelect::Loopback {
|
||||||
|
@ -12,9 +12,7 @@ use va108xx_hal::{
|
|||||||
pac::{self, interrupt},
|
pac::{self, interrupt},
|
||||||
prelude::*,
|
prelude::*,
|
||||||
time::Hertz,
|
time::Hertz,
|
||||||
timer::{
|
timer::{default_ms_irq_handler, set_up_ms_tick, CountdownTimer, Event, IrqCfg, MS_COUNTER},
|
||||||
default_ms_irq_handler, set_up_ms_tick, CountdownTimer, Event, InterruptConfig, MS_COUNTER,
|
|
||||||
},
|
|
||||||
};
|
};
|
||||||
|
|
||||||
#[allow(dead_code)]
|
#[allow(dead_code)]
|
||||||
@ -67,7 +65,7 @@ fn main() -> ! {
|
|||||||
}
|
}
|
||||||
LibType::Hal => {
|
LibType::Hal => {
|
||||||
set_up_ms_tick(
|
set_up_ms_tick(
|
||||||
InterruptConfig::new(interrupt::OC0, true, true),
|
IrqCfg::new(interrupt::OC0, true, true),
|
||||||
&mut dp.sysconfig,
|
&mut dp.sysconfig,
|
||||||
Some(&mut dp.irqsel),
|
Some(&mut dp.irqsel),
|
||||||
50.MHz(),
|
50.MHz(),
|
||||||
@ -77,7 +75,7 @@ fn main() -> ! {
|
|||||||
CountdownTimer::new(&mut dp.sysconfig, get_sys_clock().unwrap(), dp.tim1);
|
CountdownTimer::new(&mut dp.sysconfig, get_sys_clock().unwrap(), dp.tim1);
|
||||||
second_timer.listen(
|
second_timer.listen(
|
||||||
Event::TimeOut,
|
Event::TimeOut,
|
||||||
InterruptConfig::new(interrupt::OC1, true, true),
|
IrqCfg::new(interrupt::OC1, true, true),
|
||||||
Some(&mut dp.irqsel),
|
Some(&mut dp.irqsel),
|
||||||
Some(&mut dp.sysconfig),
|
Some(&mut dp.sysconfig),
|
||||||
);
|
);
|
||||||
|
@ -24,18 +24,12 @@ fn main() -> ! {
|
|||||||
|
|
||||||
let mut dp = pac::Peripherals::take().unwrap();
|
let mut dp = pac::Peripherals::take().unwrap();
|
||||||
|
|
||||||
let gpioa = PinsA::new(&mut dp.sysconfig, dp.porta);
|
let gpioa = PinsA::new(&mut dp.sysconfig, Some(dp.ioconfig), dp.porta);
|
||||||
let tx = gpioa.pa9.into_funsel_2();
|
let tx = gpioa.pa9.into_funsel_2();
|
||||||
let rx = gpioa.pa8.into_funsel_2();
|
let rx = gpioa.pa8.into_funsel_2();
|
||||||
let uart = uart::Uart::new_without_interrupt(
|
|
||||||
&mut dp.sysconfig,
|
|
||||||
50.MHz(),
|
|
||||||
dp.uarta,
|
|
||||||
(tx, rx),
|
|
||||||
115200.Hz(),
|
|
||||||
);
|
|
||||||
|
|
||||||
let (mut tx, mut rx) = uart.split();
|
let uarta = uart::Uart::new(&mut dp.sysconfig, 50.MHz(), dp.uarta, (tx, rx), 115200.Hz());
|
||||||
|
let (mut tx, mut rx) = uarta.split();
|
||||||
writeln!(tx, "Hello World\r").unwrap();
|
writeln!(tx, "Hello World\r").unwrap();
|
||||||
loop {
|
loop {
|
||||||
// Echo what is received on the serial link.
|
// Echo what is received on the serial link.
|
||||||
@ -45,6 +39,9 @@ fn main() -> ! {
|
|||||||
.expect("TX send error");
|
.expect("TX send error");
|
||||||
}
|
}
|
||||||
Err(nb::Error::WouldBlock) => (),
|
Err(nb::Error::WouldBlock) => (),
|
||||||
|
Err(nb::Error::Other(uart_error)) => {
|
||||||
|
rprintln!("UART receive error {:?}", uart_error);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -9,27 +9,58 @@ cortex-m-rt = "0.7"
|
|||||||
embedded-hal = "1"
|
embedded-hal = "1"
|
||||||
embedded-hal-nb = "1"
|
embedded-hal-nb = "1"
|
||||||
embedded-io = "0.6"
|
embedded-io = "0.6"
|
||||||
panic-rtt-target = "0.2"
|
panic-rtt-target = { version = "0.1.3" }
|
||||||
rtt-target = "0.6"
|
rtt-target = { version = "0.5" }
|
||||||
num_enum = { version = "0.7", default-features = false }
|
num_enum = { version = "0.7", default-features = false }
|
||||||
log = "0.4"
|
log = "0.4"
|
||||||
crc = "3"
|
crc = "3"
|
||||||
cobs = { version = "0.3", default-features = false }
|
|
||||||
satrs = { version = "0.2", default-features = false }
|
[dependencies.satrs]
|
||||||
rtt-log = "0.5"
|
version = "0.2"
|
||||||
ringbuf = { version = "0.4.7", default-features = false, features = ["portable-atomic"] }
|
default-features = false
|
||||||
once_cell = { version = "1", default-features = false, features = ["critical-section"] }
|
|
||||||
spacepackets = { version = "0.11", default-features = false }
|
[dependencies.rtt-log]
|
||||||
|
version = "0.4"
|
||||||
|
|
||||||
|
[dependencies.ringbuf]
|
||||||
|
version = "0.4.7"
|
||||||
|
default-features = false
|
||||||
|
features = ["portable-atomic"]
|
||||||
|
|
||||||
|
[dependencies.once_cell]
|
||||||
|
version = "1"
|
||||||
|
default-features = false
|
||||||
|
features = ["critical-section"]
|
||||||
|
|
||||||
|
[dependencies.spacepackets]
|
||||||
|
version = "0.11"
|
||||||
|
default-features = false
|
||||||
|
|
||||||
|
[dependencies.cobs]
|
||||||
|
git = "https://github.com/robamu/cobs.rs.git"
|
||||||
|
branch = "all_features"
|
||||||
|
default-features = false
|
||||||
|
|
||||||
# Even though we do not use this directly, we need to activate this feature explicitely
|
# Even though we do not use this directly, we need to activate this feature explicitely
|
||||||
# so that RTIC compiles because thumv6 does not have CAS operations natively.
|
# so that RTIC compiles because thumv6 does not have CAS operations natively.
|
||||||
portable-atomic = {version = "1", features = ["unsafe-assume-single-core"]}
|
[dependencies.portable-atomic]
|
||||||
|
version = "1"
|
||||||
|
features = ["unsafe-assume-single-core"]
|
||||||
|
|
||||||
rtic = { version = "2", features = ["thumbv6-backend"] }
|
[dependencies.rtic]
|
||||||
rtic-monotonics = { version = "2", features = ["cortex-m-systick"] }
|
version = "2"
|
||||||
rtic-sync = {version = "1", features = ["defmt-03"]}
|
features = ["thumbv6-backend"]
|
||||||
|
|
||||||
|
[dependencies.rtic-monotonics]
|
||||||
|
version = "2"
|
||||||
|
features = ["cortex-m-systick"]
|
||||||
|
|
||||||
|
[dependencies.rtic-sync]
|
||||||
|
version = "1"
|
||||||
|
features = ["defmt-03"]
|
||||||
|
|
||||||
[dependencies.va108xx-hal]
|
[dependencies.va108xx-hal]
|
||||||
version = "0.10"
|
path = "../va108xx-hal"
|
||||||
|
|
||||||
[dependencies.vorago-reb1]
|
[dependencies.vorago-reb1]
|
||||||
version = "0.8"
|
path = "../vorago-reb1"
|
||||||
|
@ -11,7 +11,7 @@ panic-rtt-target = { version = "0.1.3" }
|
|||||||
rtt-target = { version = "0.5" }
|
rtt-target = { version = "0.5" }
|
||||||
cortex-m = { version = "0.7", features = ["critical-section-single-core"] }
|
cortex-m = { version = "0.7", features = ["critical-section-single-core"] }
|
||||||
embedded-hal = "1"
|
embedded-hal = "1"
|
||||||
va108xx-hal = { version = "0.10.0" }
|
va108xx-hal = { path = "../../va108xx-hal" }
|
||||||
|
|
||||||
[profile.dev]
|
[profile.dev]
|
||||||
codegen-units = 1
|
codegen-units = 1
|
||||||
|
@ -11,7 +11,7 @@ panic-rtt-target = { version = "0.1.3" }
|
|||||||
rtt-target = { version = "0.5" }
|
rtt-target = { version = "0.5" }
|
||||||
cortex-m = { version = "0.7", features = ["critical-section-single-core"] }
|
cortex-m = { version = "0.7", features = ["critical-section-single-core"] }
|
||||||
embedded-hal = "1"
|
embedded-hal = "1"
|
||||||
va108xx-hal = { version = "0.10.0" }
|
va108xx-hal = { path = "../../va108xx-hal" }
|
||||||
|
|
||||||
[profile.dev]
|
[profile.dev]
|
||||||
codegen-units = 1
|
codegen-units = 1
|
||||||
|
@ -71,7 +71,7 @@ mod app {
|
|||||||
};
|
};
|
||||||
use va108xx_hal::gpio::PinsA;
|
use va108xx_hal::gpio::PinsA;
|
||||||
use va108xx_hal::uart::IrqContextTimeoutOrMaxSize;
|
use va108xx_hal::uart::IrqContextTimeoutOrMaxSize;
|
||||||
use va108xx_hal::{pac, uart, InterruptConfig};
|
use va108xx_hal::{pac, uart};
|
||||||
use vorago_reb1::m95m01::M95M01;
|
use vorago_reb1::m95m01::M95M01;
|
||||||
|
|
||||||
#[derive(Default, Debug, Copy, Clone, PartialEq, Eq)]
|
#[derive(Default, Debug, Copy, Clone, PartialEq, Eq)]
|
||||||
@ -84,7 +84,7 @@ mod app {
|
|||||||
|
|
||||||
#[local]
|
#[local]
|
||||||
struct Local {
|
struct Local {
|
||||||
uart_rx: uart::RxWithInterrupt<pac::Uarta>,
|
uart_rx: uart::RxWithIrq<pac::Uarta>,
|
||||||
uart_tx: uart::Tx<pac::Uarta>,
|
uart_tx: uart::Tx<pac::Uarta>,
|
||||||
rx_context: IrqContextTimeoutOrMaxSize,
|
rx_context: IrqContextTimeoutOrMaxSize,
|
||||||
verif_reporter: VerificationReportCreator,
|
verif_reporter: VerificationReportCreator,
|
||||||
@ -110,21 +110,19 @@ mod app {
|
|||||||
let mut dp = cx.device;
|
let mut dp = cx.device;
|
||||||
let nvm = M95M01::new(&mut dp.sysconfig, SYSCLK_FREQ, dp.spic);
|
let nvm = M95M01::new(&mut dp.sysconfig, SYSCLK_FREQ, dp.spic);
|
||||||
|
|
||||||
let gpioa = PinsA::new(&mut dp.sysconfig, dp.porta);
|
let gpioa = PinsA::new(&mut dp.sysconfig, Some(dp.ioconfig), dp.porta);
|
||||||
let tx = gpioa.pa9.into_funsel_2();
|
let tx = gpioa.pa9.into_funsel_2();
|
||||||
let rx = gpioa.pa8.into_funsel_2();
|
let rx = gpioa.pa8.into_funsel_2();
|
||||||
|
|
||||||
let irq_uart = uart::Uart::new_with_interrupt(
|
let irq_uart = uart::Uart::new(
|
||||||
&mut dp.sysconfig,
|
&mut dp.sysconfig,
|
||||||
SYSCLK_FREQ,
|
SYSCLK_FREQ,
|
||||||
dp.uarta,
|
dp.uarta,
|
||||||
(tx, rx),
|
(tx, rx),
|
||||||
UART_BAUDRATE.Hz(),
|
UART_BAUDRATE.Hz(),
|
||||||
InterruptConfig::new(pac::Interrupt::OC0, true, true),
|
|
||||||
);
|
);
|
||||||
let (tx, rx) = irq_uart.split();
|
let (tx, rx) = irq_uart.split();
|
||||||
// Unwrap is okay, we explicitely set the interrupt ID.
|
let mut rx = rx.into_rx_with_irq(&mut dp.sysconfig, &mut dp.irqsel, pac::interrupt::OC0);
|
||||||
let mut rx = rx.into_rx_with_irq();
|
|
||||||
|
|
||||||
let verif_reporter = VerificationReportCreator::new(0).unwrap();
|
let verif_reporter = VerificationReportCreator::new(0).unwrap();
|
||||||
|
|
||||||
@ -177,7 +175,7 @@ mod app {
|
|||||||
match cx
|
match cx
|
||||||
.local
|
.local
|
||||||
.uart_rx
|
.uart_rx
|
||||||
.on_interrupt_max_size_or_timeout_based(cx.local.rx_context, cx.local.rx_buf)
|
.irq_handler_max_size_or_timeout_based(cx.local.rx_context, cx.local.rx_buf)
|
||||||
{
|
{
|
||||||
Ok(result) => {
|
Ok(result) => {
|
||||||
if RX_DEBUGGING {
|
if RX_DEBUGGING {
|
||||||
@ -253,10 +251,10 @@ mod app {
|
|||||||
}
|
}
|
||||||
let packet_len = packet_len.unwrap();
|
let packet_len = packet_len.unwrap();
|
||||||
log::info!(target: "TC Handler", "received packet with length {}", packet_len);
|
log::info!(target: "TC Handler", "received packet with length {}", packet_len);
|
||||||
let popped_packet_len = cx
|
let popped_packet_len = cx.shared.tc_rb.lock(|rb| {
|
||||||
.shared
|
rb.buf
|
||||||
.tc_rb
|
.pop_slice(&mut cx.local.tc_buf[0..packet_len])
|
||||||
.lock(|rb| rb.buf.pop_slice(&mut cx.local.tc_buf[0..packet_len]));
|
});
|
||||||
assert_eq!(popped_packet_len, packet_len);
|
assert_eq!(popped_packet_len, packet_len);
|
||||||
// Read a telecommand, now handle it.
|
// Read a telecommand, now handle it.
|
||||||
handle_valid_pus_tc(&mut cx);
|
handle_valid_pus_tc(&mut cx);
|
||||||
@ -274,7 +272,8 @@ mod app {
|
|||||||
let written_size = tm.write_to_bytes(cx.local.verif_buf).unwrap();
|
let written_size = tm.write_to_bytes(cx.local.verif_buf).unwrap();
|
||||||
cx.shared.tm_rb.lock(|prod| {
|
cx.shared.tm_rb.lock(|prod| {
|
||||||
prod.sizes.try_push(tm.len_written()).unwrap();
|
prod.sizes.try_push(tm.len_written()).unwrap();
|
||||||
prod.buf.push_slice(&cx.local.verif_buf[0..written_size]);
|
prod.buf
|
||||||
|
.push_slice(&cx.local.verif_buf[0..written_size]);
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
let token = cx.local.verif_reporter.add_tc(&pus_tc);
|
let token = cx.local.verif_reporter.add_tc(&pus_tc);
|
||||||
|
@ -1,31 +0,0 @@
|
|||||||
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.1] 2025-03-07
|
|
||||||
|
|
||||||
- Bumped allowed va108xx-hal to v0.11
|
|
||||||
|
|
||||||
## [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
|
|
||||||
|
|
||||||
[unreleased]: https://egit.irs.uni-stuttgart.de/rust/va108xx-rs/compare/va108xx-embassy-v0.2.1...HEAD
|
|
||||||
[v0.2.1]: https://egit.irs.uni-stuttgart.de/rust/va108xx-rs/compare/va108xx-embassy-v0.2.0...va10xx-embassy-v0.2.1
|
|
||||||
[v0.2.0]: https://egit.irs.uni-stuttgart.de/rust/va108xx-rs/compare/va108xx-embassy-v0.1.2...va10xx-embassy-v0.2.0
|
|
@ -1,40 +0,0 @@
|
|||||||
[package]
|
|
||||||
name = "va108xx-embassy"
|
|
||||||
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"
|
|
||||||
|
|
||||||
embassy-sync = "0.6"
|
|
||||||
embassy-executor = "0.7"
|
|
||||||
embassy-time-driver = "0.2"
|
|
||||||
embassy-time-queue-utils = "0.1"
|
|
||||||
|
|
||||||
once_cell = { version = "1", default-features = false, features = ["critical-section"] }
|
|
||||||
|
|
||||||
va108xx-hal = { version = ">=0.10, <=0.11" }
|
|
||||||
|
|
||||||
[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"]
|
|
||||||
irqs-in-lib = []
|
|
||||||
# This determines the reserved interrupt functions for the embassy time drivers. Only one
|
|
||||||
# is allowed to be selected!
|
|
||||||
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"]
|
|
@ -1,10 +0,0 @@
|
|||||||
[](https://crates.io/crates/va108xx-embassy)
|
|
||||||
[](https://docs.rs/va108xx-embassy)
|
|
||||||
|
|
||||||
# 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.
|
|
||||||
|
|
||||||
The documentation contains more information on how to use this crate.
|
|
@ -1,3 +0,0 @@
|
|||||||
#!/bin/bash
|
|
||||||
export RUSTDOCFLAGS="--cfg docsrs --generate-link-to-definition -Z unstable-options"
|
|
||||||
cargo +nightly doc --open
|
|
@ -1,409 +0,0 @@
|
|||||||
//! # 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.
|
|
||||||
//!
|
|
||||||
//! ## Usage
|
|
||||||
//!
|
|
||||||
//! 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 [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
|
|
||||||
//! itself by using the `irq-oc30-oc31` feature flag. This library exposes three combinations:
|
|
||||||
//!
|
|
||||||
//! - `irq-oc30-oc31`: Uses [pac::Interrupt::OC30] and [pac::Interrupt::OC31]
|
|
||||||
//! - `irq-oc29-oc30`: Uses [pac::Interrupt::OC29] and [pac::Interrupt::OC30]
|
|
||||||
//! - `irq-oc28-oc29`: Uses [pac::Interrupt::OC28] and [pac::Interrupt::OC20]
|
|
||||||
//!
|
|
||||||
//! You can disable the default features and then specify one of the features above to use the
|
|
||||||
//! documented combination of IRQs. It is also possible to specify custom IRQs by importing and
|
|
||||||
//! using the [embassy_time_driver_irqs] macro to declare the IRQ handlers in the
|
|
||||||
//! application code. If this is done, [init_with_custom_irqs] must be used
|
|
||||||
//! method to pass the IRQ numbers to the library.
|
|
||||||
//!
|
|
||||||
//! ## Examples
|
|
||||||
//!
|
|
||||||
//! [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, Mutex};
|
|
||||||
use portable_atomic::{AtomicU32, Ordering};
|
|
||||||
|
|
||||||
use embassy_time_driver::{time_driver_impl, Driver, TICK_HZ};
|
|
||||||
use embassy_time_queue_utils::Queue;
|
|
||||||
use once_cell::sync::OnceCell;
|
|
||||||
#[cfg(feature = "irqs-in-lib")]
|
|
||||||
use va108xx_hal::pac::interrupt;
|
|
||||||
use va108xx_hal::{
|
|
||||||
clock::enable_peripheral_clock,
|
|
||||||
enable_nvic_interrupt, pac,
|
|
||||||
prelude::*,
|
|
||||||
timer::{enable_tim_clk, get_tim_raw, TimRegInterface, ValidTim},
|
|
||||||
PeripheralSelect,
|
|
||||||
};
|
|
||||||
|
|
||||||
time_driver_impl!(
|
|
||||||
static TIME_DRIVER: TimerDriver = TimerDriver {
|
|
||||||
periods: AtomicU32::new(0),
|
|
||||||
alarms: Mutex::new(AlarmState::new()),
|
|
||||||
queue: Mutex::new(RefCell::new(Queue::new())),
|
|
||||||
});
|
|
||||||
|
|
||||||
/// Macro to define the IRQ handlers for the time driver.
|
|
||||||
///
|
|
||||||
/// By default, the code generated by this macro will be defined inside the library depending on
|
|
||||||
/// 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 [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 {
|
|
||||||
(
|
|
||||||
timekeeper_irq = $timekeeper_irq:ident,
|
|
||||||
alarm_irq = $alarm_irq:ident
|
|
||||||
) => {
|
|
||||||
const TIMEKEEPER_IRQ: pac::Interrupt = pac::Interrupt::$timekeeper_irq;
|
|
||||||
|
|
||||||
#[interrupt]
|
|
||||||
#[allow(non_snake_case)]
|
|
||||||
fn $timekeeper_irq() {
|
|
||||||
// Safety: We call it once here.
|
|
||||||
unsafe { $crate::time_driver().on_interrupt_timekeeping() }
|
|
||||||
}
|
|
||||||
|
|
||||||
const ALARM_IRQ: pac::Interrupt = pac::Interrupt::$alarm_irq;
|
|
||||||
|
|
||||||
#[interrupt]
|
|
||||||
#[allow(non_snake_case)]
|
|
||||||
fn $alarm_irq() {
|
|
||||||
// Safety: We call it once here.
|
|
||||||
unsafe { $crate::time_driver().on_interrupt_alarm() }
|
|
||||||
}
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
// Provide three combinations of IRQs for the time driver by default.
|
|
||||||
|
|
||||||
#[cfg(feature = "irq-oc30-oc31")]
|
|
||||||
embassy_time_driver_irqs!(timekeeper_irq = OC31, alarm_irq = OC30);
|
|
||||||
#[cfg(feature = "irq-oc29-oc30")]
|
|
||||||
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);
|
|
||||||
|
|
||||||
/// 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 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 {
|
|
||||||
timestamp: Cell<u64>,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl AlarmState {
|
|
||||||
const fn new() -> Self {
|
|
||||||
Self {
|
|
||||||
timestamp: Cell::new(u64::MAX),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
unsafe impl Send for AlarmState {}
|
|
||||||
|
|
||||||
static SCALE: OnceCell<u64> = OnceCell::new();
|
|
||||||
static TIMEKEEPER_TIM: OnceCell<u8> = OnceCell::new();
|
|
||||||
static ALARM_TIM: OnceCell<u8> = OnceCell::new();
|
|
||||||
|
|
||||||
pub struct TimerDriver {
|
|
||||||
periods: AtomicU32,
|
|
||||||
/// Timestamp at which to fire alarm. u64::MAX if no alarm is scheduled.
|
|
||||||
alarms: Mutex<AlarmState>,
|
|
||||||
queue: Mutex<RefCell<Queue>>,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl TimerDriver {
|
|
||||||
#[allow(clippy::too_many_arguments)]
|
|
||||||
fn init<TimekeeperTim: TimRegInterface + ValidTim, AlarmTim: TimRegInterface + ValidTim>(
|
|
||||||
&self,
|
|
||||||
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,
|
|
||||||
) {
|
|
||||||
if ALARM_TIM.get().is_some() || TIMEKEEPER_TIM.get().is_some() {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
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();
|
|
||||||
let alarm_tim_reg_block = alarm_tim.reg_block();
|
|
||||||
let sysclk = sysclk.into();
|
|
||||||
// Initiate scale value here. This is required to convert timer ticks back to a timestamp.
|
|
||||||
SCALE.set((sysclk.raw() / TICK_HZ as u32) as u64).unwrap();
|
|
||||||
timekeeper_reg_block
|
|
||||||
.rst_value()
|
|
||||||
.write(|w| unsafe { w.bits(u32::MAX) });
|
|
||||||
// Decrementing counter.
|
|
||||||
timekeeper_reg_block
|
|
||||||
.cnt_value()
|
|
||||||
.write(|w| unsafe { w.bits(u32::MAX) });
|
|
||||||
// Switch on. Timekeeping should always be done.
|
|
||||||
irqsel
|
|
||||||
.tim0(timekeeper_tim.tim_id() as usize)
|
|
||||||
.write(|w| unsafe { w.bits(timekeeper_irq as u32) });
|
|
||||||
unsafe {
|
|
||||||
enable_nvic_interrupt(timekeeper_irq);
|
|
||||||
}
|
|
||||||
timekeeper_reg_block
|
|
||||||
.ctrl()
|
|
||||||
.modify(|_, w| w.irq_enb().set_bit());
|
|
||||||
timekeeper_reg_block
|
|
||||||
.enable()
|
|
||||||
.write(|w| unsafe { w.bits(1) });
|
|
||||||
|
|
||||||
enable_tim_clk(syscfg, alarm_tim.tim_id());
|
|
||||||
|
|
||||||
// Explicitely disable alarm timer until needed.
|
|
||||||
alarm_tim_reg_block.ctrl().modify(|_, w| {
|
|
||||||
w.irq_enb().clear_bit();
|
|
||||||
w.enable().clear_bit()
|
|
||||||
});
|
|
||||||
// Enable general interrupts. The IRQ enable of the peripheral remains cleared.
|
|
||||||
unsafe {
|
|
||||||
enable_nvic_interrupt(alarm_irq);
|
|
||||||
}
|
|
||||||
irqsel
|
|
||||||
.tim0(alarm_tim.tim_id() as usize)
|
|
||||||
.write(|w| unsafe { w.bits(alarm_irq as u32) });
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Should be called inside the IRQ of the timekeeper timer.
|
|
||||||
///
|
|
||||||
/// # Safety
|
|
||||||
///
|
|
||||||
/// This function has to be called once by the TIM IRQ used for the timekeeping.
|
|
||||||
pub unsafe fn on_interrupt_timekeeping(&self) {
|
|
||||||
self.next_period();
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Should be called inside the IRQ of the alarm timer.
|
|
||||||
///
|
|
||||||
/// # Safety
|
|
||||||
///
|
|
||||||
///This function has to be called once by the TIM IRQ used for the timekeeping.
|
|
||||||
pub unsafe fn on_interrupt_alarm(&self) {
|
|
||||||
critical_section::with(|cs| {
|
|
||||||
if self.alarms.borrow(cs).timestamp.get() <= self.now() {
|
|
||||||
self.trigger_alarm(cs)
|
|
||||||
}
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
fn timekeeper_tim() -> &'static pac::tim0::RegisterBlock {
|
|
||||||
TIMEKEEPER_TIM
|
|
||||||
.get()
|
|
||||||
.map(|idx| unsafe { get_tim_raw(*idx as usize) })
|
|
||||||
.unwrap()
|
|
||||||
}
|
|
||||||
fn alarm_tim() -> &'static pac::tim0::RegisterBlock {
|
|
||||||
ALARM_TIM
|
|
||||||
.get()
|
|
||||||
.map(|idx| unsafe { get_tim_raw(*idx as usize) })
|
|
||||||
.unwrap()
|
|
||||||
}
|
|
||||||
|
|
||||||
fn next_period(&self) {
|
|
||||||
let period = self.periods.fetch_add(1, Ordering::AcqRel) + 1;
|
|
||||||
let t = (period as u64) << 32;
|
|
||||||
critical_section::with(|cs| {
|
|
||||||
let alarm = &self.alarms.borrow(cs);
|
|
||||||
let at = alarm.timestamp.get();
|
|
||||||
if at < t {
|
|
||||||
self.trigger_alarm(cs);
|
|
||||||
} else {
|
|
||||||
let alarm_tim = Self::alarm_tim();
|
|
||||||
|
|
||||||
let remaining_ticks = (at - t).checked_mul(*SCALE.get().unwrap());
|
|
||||||
if remaining_ticks.is_some_and(|v| v <= u32::MAX as u64) {
|
|
||||||
alarm_tim.enable().write(|w| unsafe { w.bits(0) });
|
|
||||||
alarm_tim
|
|
||||||
.cnt_value()
|
|
||||||
.write(|w| unsafe { w.bits(remaining_ticks.unwrap() as u32) });
|
|
||||||
alarm_tim.ctrl().modify(|_, w| w.irq_enb().set_bit());
|
|
||||||
alarm_tim.enable().write(|w| unsafe { w.bits(1) });
|
|
||||||
}
|
|
||||||
}
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
fn trigger_alarm(&self, cs: CriticalSection) {
|
|
||||||
Self::alarm_tim().ctrl().modify(|_, w| {
|
|
||||||
w.irq_enb().clear_bit();
|
|
||||||
w.enable().clear_bit()
|
|
||||||
});
|
|
||||||
|
|
||||||
let alarm = &self.alarms.borrow(cs);
|
|
||||||
// Setting the maximum value disables the alarm.
|
|
||||||
alarm.timestamp.set(u64::MAX);
|
|
||||||
|
|
||||||
// Call after clearing alarm, so the callback can set another alarm.
|
|
||||||
let mut next = self
|
|
||||||
.queue
|
|
||||||
.borrow(cs)
|
|
||||||
.borrow_mut()
|
|
||||||
.next_expiration(self.now());
|
|
||||||
while !self.set_alarm(cs, next) {
|
|
||||||
next = self
|
|
||||||
.queue
|
|
||||||
.borrow(cs)
|
|
||||||
.borrow_mut()
|
|
||||||
.next_expiration(self.now());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn set_alarm(&self, cs: CriticalSection, timestamp: u64) -> bool {
|
|
||||||
if SCALE.get().is_none() {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
let alarm_tim = Self::alarm_tim();
|
|
||||||
alarm_tim.ctrl().modify(|_, w| {
|
|
||||||
w.irq_enb().clear_bit();
|
|
||||||
w.enable().clear_bit()
|
|
||||||
});
|
|
||||||
|
|
||||||
let alarm = self.alarms.borrow(cs);
|
|
||||||
alarm.timestamp.set(timestamp);
|
|
||||||
|
|
||||||
let t = self.now();
|
|
||||||
if timestamp <= t {
|
|
||||||
alarm.timestamp.set(u64::MAX);
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
// If it hasn't triggered yet, setup the relevant reset value, regardless of whether
|
|
||||||
// the interrupts are enabled or not. When they are enabled at a later point, the
|
|
||||||
// right value is already set.
|
|
||||||
|
|
||||||
// If the timestamp is in the next few ticks, add a bit of buffer to be sure the alarm
|
|
||||||
// is not missed.
|
|
||||||
//
|
|
||||||
// This means that an alarm can be delayed for up to 2 ticks (from t+1 to t+3), but this is allowed
|
|
||||||
// by the Alarm trait contract. What's not allowed is triggering alarms *before* their scheduled time,
|
|
||||||
// and we don't do that here.
|
|
||||||
let safe_timestamp = timestamp.max(t + 3);
|
|
||||||
let timer_ticks = (safe_timestamp - t).checked_mul(*SCALE.get().unwrap());
|
|
||||||
alarm_tim.rst_value().write(|w| unsafe { w.bits(u32::MAX) });
|
|
||||||
if timer_ticks.is_some_and(|v| v <= u32::MAX as u64) {
|
|
||||||
alarm_tim
|
|
||||||
.cnt_value()
|
|
||||||
.write(|w| unsafe { w.bits(timer_ticks.unwrap() as u32) });
|
|
||||||
alarm_tim.ctrl().modify(|_, w| w.irq_enb().set_bit());
|
|
||||||
alarm_tim.enable().write(|w| unsafe { w.bits(1) });
|
|
||||||
}
|
|
||||||
// If it's too far in the future, don't enable timer yet.
|
|
||||||
// It will be enabled later by `next_period`.
|
|
||||||
|
|
||||||
true
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Driver for TimerDriver {
|
|
||||||
fn now(&self) -> u64 {
|
|
||||||
if SCALE.get().is_none() {
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
let mut period1: u32;
|
|
||||||
let mut period2: u32;
|
|
||||||
let mut counter_val: u32;
|
|
||||||
|
|
||||||
loop {
|
|
||||||
// Acquire ensures that we get the latest value of `periods` and
|
|
||||||
// no instructions can be reordered before the load.
|
|
||||||
period1 = self.periods.load(Ordering::Acquire);
|
|
||||||
|
|
||||||
counter_val = u32::MAX - Self::timekeeper_tim().cnt_value().read().bits();
|
|
||||||
|
|
||||||
// Double read to protect against race conditions when the counter is overflowing.
|
|
||||||
period2 = self.periods.load(Ordering::Relaxed);
|
|
||||||
if period1 == period2 {
|
|
||||||
let now = (((period1 as u64) << 32) | counter_val as u64) / *SCALE.get().unwrap();
|
|
||||||
return now;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn schedule_wake(&self, at: u64, waker: &core::task::Waker) {
|
|
||||||
critical_section::with(|cs| {
|
|
||||||
let mut queue = self.queue.borrow(cs).borrow_mut();
|
|
||||||
|
|
||||||
if queue.schedule_wake(at, waker) {
|
|
||||||
let mut next = queue.next_expiration(self.now());
|
|
||||||
while !self.set_alarm(cs, next) {
|
|
||||||
next = queue.next_expiration(self.now());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
|
@ -8,82 +8,10 @@ and this project adheres to [Semantic Versioning](http://semver.org/).
|
|||||||
|
|
||||||
## [unreleased]
|
## [unreleased]
|
||||||
|
|
||||||
## [v0.11.0] 2025-03-07
|
## [v0.9.0] 2024-10-07
|
||||||
|
|
||||||
## Changed
|
|
||||||
|
|
||||||
- Bugfix for I2C `TimingCfg::reg`
|
|
||||||
- Simplified UART error handling. All APIs are now infallible because writing to a FIFO or
|
|
||||||
reading from a FIFO never fails. Users can either poll errors using `Rx::poll_errors` or
|
|
||||||
`Uart::poll_rx_errors` / `UartBase::poll_rx_errors`, or detect errors using the provided
|
|
||||||
interrupt handlers.
|
|
||||||
|
|
||||||
## [v0.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
|
|
||||||
|
|
||||||
- Important bugfix for UART driver which causes UART B drivers not to work.
|
|
||||||
|
|
||||||
## Removed
|
|
||||||
|
|
||||||
- Deleted some HAL re-exports in the PWM module
|
- Deleted some HAL re-exports in the PWM module
|
||||||
|
|
||||||
## Changed
|
|
||||||
|
|
||||||
- GPIO API: Interrupt, pulse and filter and `set_datamask` and `clear_datamask` APIs are now
|
|
||||||
methods which mutable modify the pin instead of consuming and returning it.
|
|
||||||
- Simplified PWM module implementation.
|
|
||||||
- All error types now implement `core::error::Error` by using the `thiserror::Error` derive.
|
|
||||||
- `InvalidPinTypeError` now wraps the pin mode.
|
|
||||||
- I2C `TimingCfg` constructor now returns explicit error instead of generic Error.
|
|
||||||
Removed the timing configuration error type from the generic I2C error enumeration.
|
|
||||||
- `PinsA` and `PinsB` constructor do not expect an optional `pac::Ioconfig` argument anymore.
|
|
||||||
- `IrqCfg` renamed to `InterruptConfig`, kept alias for old name.
|
|
||||||
- All library provided interrupt handlers now start with common prefix `on_interrupt_*`
|
|
||||||
- `RxWithIrq` renamed to `RxWithInterrupt`
|
|
||||||
- `Rx::into_rx_with_irq` does not expect any arguments any more.
|
|
||||||
- `filter_type` renamed to `configure_filter_type`.
|
|
||||||
- `level_irq` renamed to `configure_level_interrupt`.
|
|
||||||
- `edge_irq` renamed to `configure_edge_interrupt`.
|
|
||||||
- `PinsA` and `PinsB` constructor do not expect an optional IOCONFIG argument anymore.
|
|
||||||
- UART interrupt management is now handled by the main constructor instead of later stages to
|
|
||||||
statically ensure one interrupt vector for the UART peripheral. `Uart::new` expects an
|
|
||||||
optional `InterruptConfig` argument.
|
|
||||||
- `enable_interrupt` and `disable_interrupt` renamed to `enable_nvic_interrupt` and
|
|
||||||
`disable_nvic_interrupt` to distinguish them from peripheral interrupts more clearly.
|
|
||||||
- `port_mux` renamed to `port_function_select`
|
|
||||||
- Renamed `IrqUartErrors` to `UartErrors`.
|
|
||||||
|
|
||||||
## Added
|
|
||||||
|
|
||||||
- Add `downgrade` method for `Pin` and `upgrade` method for `DynPin` as explicit conversion
|
|
||||||
methods.
|
|
||||||
- Asynchronous GPIO support.
|
|
||||||
- Asynchronous UART TX support.
|
|
||||||
- Asynchronous UART RX support.
|
|
||||||
- Add new `get_tim_raw` unsafe method to retrieve TIM peripheral blocks.
|
|
||||||
- `Uart::with_with_interrupt` and `Uart::new_without_interrupt`
|
|
||||||
|
|
||||||
## [v0.8.0] 2024-09-30
|
## [v0.8.0] 2024-09-30
|
||||||
|
|
||||||
## Changed
|
## Changed
|
||||||
@ -114,14 +42,14 @@ and this project adheres to [Semantic Versioning](http://semver.org/).
|
|||||||
- Updated `embedded-hal` to v1
|
- Updated `embedded-hal` to v1
|
||||||
- Added optional `defmt` v0.3 feature and support.
|
- Added optional `defmt` v0.3 feature and support.
|
||||||
|
|
||||||
## v0.5.2 2024-06-16
|
## [v0.5.2] 2024-06-16
|
||||||
|
|
||||||
## Fixed
|
## Fixed
|
||||||
|
|
||||||
- Replaced usage to `ptr::write_volatile` in UART module which is denied on more recent Rust
|
- Replaced usage to `ptr::write_volatile` in UART module which is denied on more recent Rust
|
||||||
compilers.
|
compilers.
|
||||||
|
|
||||||
## v0.5.1
|
## [v0.5.1]
|
||||||
|
|
||||||
### Changes
|
### Changes
|
||||||
|
|
||||||
@ -130,7 +58,7 @@ and this project adheres to [Semantic Versioning](http://semver.org/).
|
|||||||
- `once_cell` to 1.12.0
|
- `once_cell` to 1.12.0
|
||||||
- Other dependencies: Only revision has changed
|
- Other dependencies: Only revision has changed
|
||||||
|
|
||||||
## v0.5.0
|
## [v0.5.0]
|
||||||
|
|
||||||
### Added
|
### Added
|
||||||
|
|
||||||
@ -143,14 +71,14 @@ and this project adheres to [Semantic Versioning](http://semver.org/).
|
|||||||
|
|
||||||
- Bugfix in UART code where RX and TX could not be enabled or disabled independently
|
- Bugfix in UART code where RX and TX could not be enabled or disabled independently
|
||||||
|
|
||||||
## v0.4.3
|
## [v0.4.3]
|
||||||
|
|
||||||
- Various smaller fixes for READMEs, update of links in documentation
|
- Various smaller fixes for READMEs, update of links in documentation
|
||||||
- Simplified CI for github, do not use `cross`
|
- Simplified CI for github, do not use `cross`
|
||||||
- New `blinky-pac` example
|
- New `blinky-pac` example
|
||||||
- Use HAL delay in `blinky` example
|
- Use HAL delay in `blinky` example
|
||||||
|
|
||||||
## v0.4.2
|
## [v0.4.2]
|
||||||
|
|
||||||
### Added
|
### Added
|
||||||
|
|
||||||
@ -160,24 +88,24 @@ and this project adheres to [Semantic Versioning](http://semver.org/).
|
|||||||
|
|
||||||
- Clear TX and RX FIFO in SPI transfer function
|
- Clear TX and RX FIFO in SPI transfer function
|
||||||
|
|
||||||
## v0.4.1
|
## [v0.4.1]
|
||||||
|
|
||||||
### Fixed
|
### Fixed
|
||||||
|
|
||||||
- Initial blockmode setting was not set in SPI constructor
|
- Initial blockmode setting was not set in SPI constructor
|
||||||
|
|
||||||
## v0.4.0
|
## [v0.4.0]
|
||||||
|
|
||||||
### Changed
|
### Changed
|
||||||
|
|
||||||
- Replaced `Hertz` by `impl Into<Hertz>` completely and removed
|
- Replaced `Hertz` by `impl Into<Hertz>` completely and removed
|
||||||
`+ Copy` where not necessary
|
`+ Copy` where not necessary
|
||||||
|
|
||||||
## v0.3.1
|
## [v0.3.1]
|
||||||
|
|
||||||
- Updated all links to point to new repository
|
- Updated all links to point to new repository
|
||||||
|
|
||||||
## v0.3.0
|
## [v0.3.0]
|
||||||
|
|
||||||
### Added
|
### Added
|
||||||
|
|
||||||
@ -189,7 +117,7 @@ and this project adheres to [Semantic Versioning](http://semver.org/).
|
|||||||
- Primary repository now hosted on IRS external git: https://egit.irs.uni-stuttgart.de/rust/va108xx-hal
|
- Primary repository now hosted on IRS external git: https://egit.irs.uni-stuttgart.de/rust/va108xx-hal
|
||||||
- Relicensed as Apache-2.0
|
- Relicensed as Apache-2.0
|
||||||
|
|
||||||
## v0.2.3
|
## [0.2.3]
|
||||||
|
|
||||||
### Added
|
### Added
|
||||||
|
|
||||||
@ -201,7 +129,7 @@ and this project adheres to [Semantic Versioning](http://semver.org/).
|
|||||||
|
|
||||||
- Improved Timer API. It is now possible to simply use `new` on `CountDownTimer`
|
- Improved Timer API. It is now possible to simply use `new` on `CountDownTimer`
|
||||||
|
|
||||||
## v0.2.2
|
## [0.2.2]
|
||||||
|
|
||||||
### Added
|
### Added
|
||||||
|
|
||||||
@ -213,7 +141,7 @@ and this project adheres to [Semantic Versioning](http://semver.org/).
|
|||||||
|
|
||||||
- API which expects values in Hertz now uses `impl Into<Hertz>` as input parameter
|
- API which expects values in Hertz now uses `impl Into<Hertz>` as input parameter
|
||||||
|
|
||||||
## v0.2.1
|
## [0.2.1]
|
||||||
|
|
||||||
### Added
|
### Added
|
||||||
|
|
||||||
@ -227,7 +155,7 @@ and this project adheres to [Semantic Versioning](http://semver.org/).
|
|||||||
- Moved the `FilterClkSel` struct to the `clock` module, re-exporting in `gpio`
|
- Moved the `FilterClkSel` struct to the `clock` module, re-exporting in `gpio`
|
||||||
- Clearing output state at initialization of Output pins
|
- Clearing output state at initialization of Output pins
|
||||||
|
|
||||||
## v0.2.0
|
## [0.2.0]
|
||||||
|
|
||||||
### Changed
|
### Changed
|
||||||
|
|
||||||
@ -242,7 +170,7 @@ and this project adheres to [Semantic Versioning](http://semver.org/).
|
|||||||
- Some bugfixes for GPIO implementation
|
- Some bugfixes for GPIO implementation
|
||||||
- Rust edition updated to 2021
|
- Rust edition updated to 2021
|
||||||
|
|
||||||
## v0.1.0
|
## [0.1.0]
|
||||||
|
|
||||||
### Added
|
### Added
|
||||||
|
|
||||||
@ -251,11 +179,3 @@ and this project adheres to [Semantic Versioning](http://semver.org/).
|
|||||||
- RTT example application
|
- RTT example application
|
||||||
- Added basic test binary in form of an example
|
- Added basic test binary in form of an example
|
||||||
- README with basic instructions how to set up own binary crate
|
- README with basic instructions how to set up own binary crate
|
||||||
|
|
||||||
[unreleased]: https://egit.irs.uni-stuttgart.de/rust/va108xx-rs/compare/va108xx-hal-v0.11.0...HEAD
|
|
||||||
[v0.11.0]: https://egit.irs.uni-stuttgart.de/rust/va108xx-rs/compare/va108xx-hal-v0.10.0...va108xx-hal-v0.11.0
|
|
||||||
[v0.10.0]: https://egit.irs.uni-stuttgart.de/rust/va108xx-rs/compare/va108xx-hal-v0.9.0...va108xx-hal-v0.10.0
|
|
||||||
[v0.9.0]: https://egit.irs.uni-stuttgart.de/rust/va108xx-rs/compare/va108xx-hal-v0.8.0...va108xx-hal-v0.9.0
|
|
||||||
[v0.8.0]: https://egit.irs.uni-stuttgart.de/rust/va108xx-rs/compare/va108xx-hal-v0.7.0...va108xx-hal-v0.8.0
|
|
||||||
[v0.7.0]: https://egit.irs.uni-stuttgart.de/rust/va108xx-rs/compare/va108xx-hal-v0.6.0...va108xx-hal-v0.7.0
|
|
||||||
[v0.6.0]: https://egit.irs.uni-stuttgart.de/rust/va108xx-rs/src/tag/va108xx-hal-v0.6.0
|
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
[package]
|
[package]
|
||||||
name = "va108xx-hal"
|
name = "va108xx-hal"
|
||||||
version = "0.11.0"
|
version = "0.8.0"
|
||||||
authors = ["Robin Mueller <muellerr@irs.uni-stuttgart.de>"]
|
authors = ["Robin Mueller <muellerr@irs.uni-stuttgart.de>"]
|
||||||
edition = "2021"
|
edition = "2021"
|
||||||
description = "HAL for the Vorago VA108xx family of microcontrollers"
|
description = "HAL for the Vorago VA108xx family of microcontrollers"
|
||||||
@ -15,34 +15,37 @@ cortex-m = { version = "0.7", features = ["critical-section-single-core"]}
|
|||||||
cortex-m-rt = "0.7"
|
cortex-m-rt = "0.7"
|
||||||
nb = "1"
|
nb = "1"
|
||||||
paste = "1"
|
paste = "1"
|
||||||
embedded-hal = "1"
|
|
||||||
embedded-hal-async = "1"
|
|
||||||
embedded-hal-nb = "1"
|
embedded-hal-nb = "1"
|
||||||
embedded-io = "0.6"
|
embedded-io = "0.6"
|
||||||
embedded-io-async = "0.6"
|
|
||||||
fugit = "0.3"
|
fugit = "0.3"
|
||||||
typenum = "1"
|
typenum = "1"
|
||||||
critical-section = "1"
|
critical-section = "1"
|
||||||
delegate = ">=0.12, <=0.13"
|
delegate = "0.12"
|
||||||
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.5", default-features = false, features = ["critical-section", "defmt"] }
|
|
||||||
embassy-sync = "0.6"
|
|
||||||
|
|
||||||
defmt = { version = "0.3", optional = true }
|
[dependencies.va108xx]
|
||||||
|
version = "0.3"
|
||||||
|
default-features = false
|
||||||
|
features = ["critical-section"]
|
||||||
|
|
||||||
[target.'cfg(all(target_arch = "arm", target_os = "none"))'.dependencies]
|
[dependencies.embedded-hal]
|
||||||
portable-atomic = { version = "1", features = ["unsafe-assume-single-core"] }
|
version = "1"
|
||||||
[target.'cfg(not(all(target_arch = "arm", target_os = "none")))'.dependencies]
|
|
||||||
portable-atomic = "1"
|
[dependencies.void]
|
||||||
|
version = "1"
|
||||||
|
default-features = false
|
||||||
|
|
||||||
|
[dependencies.once_cell]
|
||||||
|
version = "1.14"
|
||||||
|
default-features = false
|
||||||
|
|
||||||
|
[dependencies.defmt]
|
||||||
|
version = "0.3"
|
||||||
|
optional = true
|
||||||
|
|
||||||
[features]
|
[features]
|
||||||
default = ["rt"]
|
default = ["rt"]
|
||||||
rt = ["va108xx/rt"]
|
rt = ["va108xx/rt"]
|
||||||
defmt = ["dep:defmt", "fugit/defmt", "embedded-hal/defmt-03"]
|
defmt = ["dep:defmt", "fugit/defmt"]
|
||||||
|
|
||||||
[package.metadata.docs.rs]
|
[package.metadata.docs.rs]
|
||||||
all-features = true
|
all-features = true
|
||||||
|
@ -25,6 +25,12 @@ rustup target add thumbv6m-none-eabi
|
|||||||
|
|
||||||
After that, you can use `cargo build` to build the development version of the crate.
|
After that, you can use `cargo build` to build the development version of the crate.
|
||||||
|
|
||||||
|
If you have not done this yet, it is recommended to read some of the excellent resources
|
||||||
|
available to learn Rust:
|
||||||
|
|
||||||
|
- [Rust Embedded Book](https://docs.rust-embedded.org/book/)
|
||||||
|
- [Rust Discovery Book](https://docs.rust-embedded.org/discovery/)
|
||||||
|
|
||||||
## Setting up your own binary crate
|
## Setting up your own binary crate
|
||||||
|
|
||||||
If you have a custom board, you might be interested in setting up a new binary crate for your
|
If you have a custom board, you might be interested in setting up a new binary crate for your
|
||||||
@ -59,11 +65,3 @@ is contained within the
|
|||||||
|
|
||||||
7. Flashing the board might work differently for different boards and there is usually
|
7. Flashing the board might work differently for different boards and there is usually
|
||||||
more than one way. You can find example instructions in primary README.
|
more than one way. You can find example instructions in primary README.
|
||||||
|
|
||||||
## Embedded Rust
|
|
||||||
|
|
||||||
If you have not done this yet, it is recommended to read some of the excellent resources available
|
|
||||||
to learn Rust:
|
|
||||||
|
|
||||||
- [Rust Embedded Book](https://docs.rust-embedded.org/book/)
|
|
||||||
- [Rust Discovery Book](https://docs.rust-embedded.org/discovery/)
|
|
||||||
|
@ -1,3 +0,0 @@
|
|||||||
#!/bin/sh
|
|
||||||
export RUSTDOCFLAGS="--cfg docsrs --generate-link-to-definition -Z unstable-options"
|
|
||||||
cargo +nightly doc --all-features --open
|
|
@ -11,7 +11,6 @@ static SYS_CLOCK: Mutex<OnceCell<Hertz>> = Mutex::new(OnceCell::new());
|
|||||||
pub type PeripheralClocks = PeripheralSelect;
|
pub type PeripheralClocks = PeripheralSelect;
|
||||||
|
|
||||||
#[derive(Debug, PartialEq, Eq)]
|
#[derive(Debug, PartialEq, Eq)]
|
||||||
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
|
|
||||||
pub enum FilterClkSel {
|
pub enum FilterClkSel {
|
||||||
SysClk = 0,
|
SysClk = 0,
|
||||||
Clk1 = 1,
|
Clk1 = 1,
|
||||||
@ -40,27 +39,13 @@ pub fn get_sys_clock() -> Option<Hertz> {
|
|||||||
pub fn set_clk_div_register(syscfg: &mut va108xx::Sysconfig, clk_sel: FilterClkSel, div: u32) {
|
pub fn set_clk_div_register(syscfg: &mut va108xx::Sysconfig, clk_sel: FilterClkSel, div: u32) {
|
||||||
match clk_sel {
|
match clk_sel {
|
||||||
FilterClkSel::SysClk => (),
|
FilterClkSel::SysClk => (),
|
||||||
FilterClkSel::Clk1 => {
|
FilterClkSel::Clk1 => syscfg.ioconfig_clkdiv1().write(|w| unsafe { w.bits(div) }),
|
||||||
syscfg.ioconfig_clkdiv1().write(|w| unsafe { w.bits(div) });
|
FilterClkSel::Clk2 => syscfg.ioconfig_clkdiv2().write(|w| unsafe { w.bits(div) }),
|
||||||
}
|
FilterClkSel::Clk3 => syscfg.ioconfig_clkdiv3().write(|w| unsafe { w.bits(div) }),
|
||||||
FilterClkSel::Clk2 => {
|
FilterClkSel::Clk4 => syscfg.ioconfig_clkdiv4().write(|w| unsafe { w.bits(div) }),
|
||||||
syscfg.ioconfig_clkdiv2().write(|w| unsafe { w.bits(div) });
|
FilterClkSel::Clk5 => syscfg.ioconfig_clkdiv5().write(|w| unsafe { w.bits(div) }),
|
||||||
}
|
FilterClkSel::Clk6 => syscfg.ioconfig_clkdiv6().write(|w| unsafe { w.bits(div) }),
|
||||||
FilterClkSel::Clk3 => {
|
FilterClkSel::Clk7 => syscfg.ioconfig_clkdiv7().write(|w| unsafe { w.bits(div) }),
|
||||||
syscfg.ioconfig_clkdiv3().write(|w| unsafe { w.bits(div) });
|
|
||||||
}
|
|
||||||
FilterClkSel::Clk4 => {
|
|
||||||
syscfg.ioconfig_clkdiv4().write(|w| unsafe { w.bits(div) });
|
|
||||||
}
|
|
||||||
FilterClkSel::Clk5 => {
|
|
||||||
syscfg.ioconfig_clkdiv5().write(|w| unsafe { w.bits(div) });
|
|
||||||
}
|
|
||||||
FilterClkSel::Clk6 => {
|
|
||||||
syscfg.ioconfig_clkdiv6().write(|w| unsafe { w.bits(div) });
|
|
||||||
}
|
|
||||||
FilterClkSel::Clk7 => {
|
|
||||||
syscfg.ioconfig_clkdiv7().write(|w| unsafe { w.bits(div) });
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1,366 +0,0 @@
|
|||||||
//! # Async GPIO functionality for the VA108xx family.
|
|
||||||
//!
|
|
||||||
//! 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 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
|
|
||||||
//!
|
|
||||||
//! - [Async GPIO example](https://egit.irs.uni-stuttgart.de/rust/va108xx-rs/src/branch/main/examples/embassy/src/bin/async-gpio.rs)
|
|
||||||
use core::future::Future;
|
|
||||||
|
|
||||||
use embassy_sync::waitqueue::AtomicWaker;
|
|
||||||
use embedded_hal_async::digital::Wait;
|
|
||||||
use portable_atomic::AtomicBool;
|
|
||||||
use va108xx::{self as pac};
|
|
||||||
|
|
||||||
use crate::InterruptConfig;
|
|
||||||
|
|
||||||
use super::{
|
|
||||||
pin, DynPin, DynPinId, InputConfig, InterruptEdge, InvalidPinTypeError, Pin, PinId, Port,
|
|
||||||
NUM_PINS_PORT_A, NUM_PINS_PORT_B,
|
|
||||||
};
|
|
||||||
|
|
||||||
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];
|
|
||||||
|
|
||||||
/// Generic interrupt handler for GPIO interrupts on a specific port to support async functionalities
|
|
||||||
///
|
|
||||||
/// This function should be called in all interrupt handlers which handle any GPIO interrupts
|
|
||||||
/// matching the [Port] argument.
|
|
||||||
/// The handler will wake the corresponding wakers for the pins that triggered an interrupts
|
|
||||||
/// as well as update the static edge detection structures. This allows the pin future tocomplete
|
|
||||||
/// complete async operations.
|
|
||||||
pub fn on_interrupt_for_async_gpio_for_port(port: Port) {
|
|
||||||
let periphs = unsafe { pac::Peripherals::steal() };
|
|
||||||
|
|
||||||
let (irq_enb, edge_status, wakers, edge_detection) = match port {
|
|
||||||
Port::A => (
|
|
||||||
periphs.porta.irq_enb().read().bits(),
|
|
||||||
periphs.porta.edge_status().read().bits(),
|
|
||||||
WAKERS_FOR_PORT_A.as_ref(),
|
|
||||||
EDGE_DETECTION_PORT_A.as_ref(),
|
|
||||||
),
|
|
||||||
Port::B => (
|
|
||||||
periphs.portb.irq_enb().read().bits(),
|
|
||||||
periphs.portb.edge_status().read().bits(),
|
|
||||||
WAKERS_FOR_PORT_B.as_ref(),
|
|
||||||
EDGE_DETECTION_PORT_B.as_ref(),
|
|
||||||
),
|
|
||||||
};
|
|
||||||
|
|
||||||
on_interrupt_for_port(irq_enb, edge_status, wakers, edge_detection);
|
|
||||||
}
|
|
||||||
|
|
||||||
#[inline]
|
|
||||||
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[bit_pos].wake();
|
|
||||||
|
|
||||||
if edge_status & bit_mask != 0 {
|
|
||||||
edge_detection[bit_pos].store(true, core::sync::atomic::Ordering::Relaxed);
|
|
||||||
|
|
||||||
// Clear the processed bit
|
|
||||||
irq_enb &= !bit_mask;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Input pin future which implements the [Future] trait.
|
|
||||||
///
|
|
||||||
/// Generally, you want to use the [InputPinAsync] or [InputDynPinAsync] types instead of this
|
|
||||||
/// which also implements the [embedded_hal_async::digital::Wait] trait. However, access to this
|
|
||||||
/// 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 {
|
|
||||||
#[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,
|
|
||||||
) -> Result<Self, InvalidPinTypeError> {
|
|
||||||
if !pin.is_input_pin() {
|
|
||||||
return Err(InvalidPinTypeError(pin.mode()));
|
|
||||||
}
|
|
||||||
|
|
||||||
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).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,
|
|
||||||
) -> Self {
|
|
||||||
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);
|
|
||||||
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) {
|
|
||||||
// The API ensures that we actually own the pin, so stealing it here is okay.
|
|
||||||
unsafe { DynPin::steal(self.pin_id) }.disable_interrupt(false);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Future for InputPinFuture {
|
|
||||||
type Output = ();
|
|
||||||
fn poll(
|
|
||||||
self: core::pin::Pin<&mut Self>,
|
|
||||||
cx: &mut core::task::Context<'_>,
|
|
||||||
) -> core::task::Poll<Self::Output> {
|
|
||||||
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
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub struct InputDynPinAsync {
|
|
||||||
pin: DynPin,
|
|
||||||
irq: pac::Interrupt,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl InputDynPinAsync {
|
|
||||||
/// Create a new asynchronous input pin from a [DynPin]. The interrupt ID to be used must be
|
|
||||||
/// 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_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()));
|
|
||||||
}
|
|
||||||
Ok(Self { pin, irq })
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Asynchronously wait until the pin is high.
|
|
||||||
///
|
|
||||||
/// This returns immediately if the pin is already high.
|
|
||||||
pub async fn wait_for_high(&mut self) {
|
|
||||||
// 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;
|
|
||||||
}
|
|
||||||
fut.await;
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Asynchronously wait until the pin is low.
|
|
||||||
///
|
|
||||||
/// This returns immediately if the pin is already high.
|
|
||||||
pub async fn wait_for_low(&mut self) {
|
|
||||||
// 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;
|
|
||||||
}
|
|
||||||
fut.await;
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Asynchronously wait until the pin sees a falling edge.
|
|
||||||
pub async fn wait_for_falling_edge(&mut self) {
|
|
||||||
// Unwrap okay, checked pin in constructor.
|
|
||||||
InputPinFuture::new_with_dyn_pin(&mut self.pin, self.irq, InterruptEdge::HighToLow)
|
|
||||||
.unwrap()
|
|
||||||
.await;
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Asynchronously wait until the pin sees a rising edge.
|
|
||||||
pub async fn wait_for_rising_edge(&mut self) {
|
|
||||||
// Unwrap okay, checked pin in constructor.
|
|
||||||
InputPinFuture::new_with_dyn_pin(&mut self.pin, self.irq, InterruptEdge::LowToHigh)
|
|
||||||
.unwrap()
|
|
||||||
.await;
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Asynchronously wait until the pin sees any edge (either rising or falling).
|
|
||||||
pub async fn wait_for_any_edge(&mut self) {
|
|
||||||
// Unwrap okay, checked pin in constructor.
|
|
||||||
InputPinFuture::new_with_dyn_pin(&mut self.pin, self.irq, InterruptEdge::BothEdges)
|
|
||||||
.unwrap()
|
|
||||||
.await;
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn release(self) -> DynPin {
|
|
||||||
self.pin
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl embedded_hal::digital::ErrorType for InputDynPinAsync {
|
|
||||||
type Error = core::convert::Infallible;
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Wait for InputDynPinAsync {
|
|
||||||
async fn wait_for_high(&mut self) -> Result<(), Self::Error> {
|
|
||||||
self.wait_for_high().await;
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
|
|
||||||
async fn wait_for_low(&mut self) -> Result<(), Self::Error> {
|
|
||||||
self.wait_for_low().await;
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
|
|
||||||
async fn wait_for_rising_edge(&mut self) -> Result<(), Self::Error> {
|
|
||||||
self.wait_for_rising_edge().await;
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
|
|
||||||
async fn wait_for_falling_edge(&mut self) -> Result<(), Self::Error> {
|
|
||||||
self.wait_for_falling_edge().await;
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
|
|
||||||
async fn wait_for_any_edge(&mut self) -> Result<(), Self::Error> {
|
|
||||||
self.wait_for_any_edge().await;
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub struct InputPinAsync<I: PinId, C: InputConfig> {
|
|
||||||
pin: Pin<I, pin::Input<C>>,
|
|
||||||
irq: pac::Interrupt,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<I: PinId, C: InputConfig> InputPinAsync<I, C> {
|
|
||||||
/// Create a new asynchronous input pin from a typed [Pin]. The interrupt ID to be used must be
|
|
||||||
/// 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_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 }
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Asynchronously wait until the pin is high.
|
|
||||||
///
|
|
||||||
/// This returns immediately if the pin is already high.
|
|
||||||
pub async fn wait_for_high(&mut self) {
|
|
||||||
let fut = InputPinFuture::new_with_pin(&mut self.pin, self.irq, InterruptEdge::LowToHigh);
|
|
||||||
if self.pin.is_high() {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
fut.await;
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Asynchronously wait until the pin is low.
|
|
||||||
///
|
|
||||||
/// This returns immediately if the pin is already high.
|
|
||||||
pub async fn wait_for_low(&mut self) {
|
|
||||||
let fut = InputPinFuture::new_with_pin(&mut self.pin, self.irq, InterruptEdge::HighToLow);
|
|
||||||
if self.pin.is_low() {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
fut.await;
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Asynchronously wait until the pin sees falling edge.
|
|
||||||
pub async fn wait_for_falling_edge(&mut self) {
|
|
||||||
// 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) {
|
|
||||||
// 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) {
|
|
||||||
InputPinFuture::new_with_pin(&mut self.pin, self.irq, InterruptEdge::BothEdges).await;
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn release(self) -> Pin<I, pin::Input<C>> {
|
|
||||||
self.pin
|
|
||||||
}
|
|
||||||
}
|
|
||||||
impl<I: PinId, C: InputConfig> embedded_hal::digital::ErrorType for InputPinAsync<I, C> {
|
|
||||||
type Error = core::convert::Infallible;
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<I: PinId, C: InputConfig> Wait for InputPinAsync<I, C> {
|
|
||||||
async fn wait_for_high(&mut self) -> Result<(), Self::Error> {
|
|
||||||
self.wait_for_high().await;
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
|
|
||||||
async fn wait_for_low(&mut self) -> Result<(), Self::Error> {
|
|
||||||
self.wait_for_low().await;
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
|
|
||||||
async fn wait_for_rising_edge(&mut self) -> Result<(), Self::Error> {
|
|
||||||
self.wait_for_rising_edge().await;
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
|
|
||||||
async fn wait_for_falling_edge(&mut self) -> Result<(), Self::Error> {
|
|
||||||
self.wait_for_falling_edge().await;
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
|
|
||||||
async fn wait_for_any_edge(&mut self) -> Result<(), Self::Error> {
|
|
||||||
self.wait_for_any_edge().await;
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
}
|
|
@ -57,10 +57,10 @@
|
|||||||
//! [InvalidPinTypeError].
|
//! [InvalidPinTypeError].
|
||||||
|
|
||||||
use super::{
|
use super::{
|
||||||
pin::{FilterType, Pin, PinId, PinMode},
|
pin::{FilterType, InterruptEdge, InterruptLevel, Pin, PinId, PinMode, PinState},
|
||||||
InputDynPinAsync, InterruptEdge, InterruptLevel, IsMaskedError, PinState, Port,
|
reg::RegisterInterface,
|
||||||
};
|
};
|
||||||
use crate::{clock::FilterClkSel, enable_nvic_interrupt, pac, FunSel};
|
use crate::{clock::FilterClkSel, pac, FunSel, IrqCfg};
|
||||||
|
|
||||||
//==================================================================================================
|
//==================================================================================================
|
||||||
// DynPinMode configurations
|
// DynPinMode configurations
|
||||||
@ -68,7 +68,6 @@ use crate::{clock::FilterClkSel, enable_nvic_interrupt, pac, FunSel};
|
|||||||
|
|
||||||
/// Value-level `enum` for disabled configurations
|
/// Value-level `enum` for disabled configurations
|
||||||
#[derive(PartialEq, Eq, Clone, Copy)]
|
#[derive(PartialEq, Eq, Clone, Copy)]
|
||||||
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
|
|
||||||
pub enum DynDisabled {
|
pub enum DynDisabled {
|
||||||
Floating,
|
Floating,
|
||||||
PullDown,
|
PullDown,
|
||||||
@ -76,8 +75,7 @@ pub enum DynDisabled {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// Value-level `enum` for input configurations
|
/// Value-level `enum` for input configurations
|
||||||
#[derive(Debug, PartialEq, Eq, Clone, Copy)]
|
#[derive(PartialEq, Eq, Clone, Copy)]
|
||||||
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
|
|
||||||
pub enum DynInput {
|
pub enum DynInput {
|
||||||
Floating,
|
Floating,
|
||||||
PullDown,
|
PullDown,
|
||||||
@ -85,8 +83,7 @@ pub enum DynInput {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// Value-level `enum` for output configurations
|
/// Value-level `enum` for output configurations
|
||||||
#[derive(Debug, PartialEq, Eq, Clone, Copy)]
|
#[derive(PartialEq, Eq, Clone, Copy)]
|
||||||
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
|
|
||||||
pub enum DynOutput {
|
pub enum DynOutput {
|
||||||
PushPull,
|
PushPull,
|
||||||
OpenDrain,
|
OpenDrain,
|
||||||
@ -104,10 +101,9 @@ pub type DynAlternate = FunSel;
|
|||||||
///
|
///
|
||||||
/// [`DynPin`]s are not tracked and verified at compile-time, so run-time
|
/// [`DynPin`]s are not tracked and verified at compile-time, so run-time
|
||||||
/// operations are fallible. This `enum` represents the corresponding errors.
|
/// operations are fallible. This `enum` represents the corresponding errors.
|
||||||
#[derive(Debug, PartialEq, Eq, thiserror::Error)]
|
#[derive(Debug, PartialEq, Eq)]
|
||||||
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
|
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
|
||||||
#[error("Invalid pin type for operation: {0:?}")]
|
pub struct InvalidPinTypeError;
|
||||||
pub struct InvalidPinTypeError(pub DynPinMode);
|
|
||||||
|
|
||||||
impl embedded_hal::digital::Error for InvalidPinTypeError {
|
impl embedded_hal::digital::Error for InvalidPinTypeError {
|
||||||
fn kind(&self) -> embedded_hal::digital::ErrorKind {
|
fn kind(&self) -> embedded_hal::digital::ErrorKind {
|
||||||
@ -120,8 +116,7 @@ impl embedded_hal::digital::Error for InvalidPinTypeError {
|
|||||||
//==================================================================================================
|
//==================================================================================================
|
||||||
|
|
||||||
/// Value-level `enum` representing pin modes
|
/// Value-level `enum` representing pin modes
|
||||||
#[derive(Debug, PartialEq, Eq, Clone, Copy)]
|
#[derive(PartialEq, Eq, Clone, Copy)]
|
||||||
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
|
|
||||||
pub enum DynPinMode {
|
pub enum DynPinMode {
|
||||||
Input(DynInput),
|
Input(DynInput),
|
||||||
Output(DynOutput),
|
Output(DynOutput),
|
||||||
@ -155,91 +150,53 @@ pub const DYN_ALT_FUNC_3: DynPinMode = DynPinMode::Alternate(DynAlternate::Sel3)
|
|||||||
// DynGroup & DynPinId
|
// DynGroup & DynPinId
|
||||||
//==================================================================================================
|
//==================================================================================================
|
||||||
|
|
||||||
pub type DynGroup = Port;
|
/// Value-level `enum` for pin groups
|
||||||
|
#[derive(PartialEq, Eq, Clone, Copy)]
|
||||||
|
pub enum DynGroup {
|
||||||
|
A,
|
||||||
|
B,
|
||||||
|
}
|
||||||
|
|
||||||
#[derive(Debug, PartialEq, Eq, Clone, Copy)]
|
/// Value-level `struct` representing pin IDs
|
||||||
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
|
#[derive(PartialEq, Eq, Clone, Copy)]
|
||||||
pub struct DynPinId {
|
pub struct DynPinId {
|
||||||
port: Port,
|
pub group: DynGroup,
|
||||||
num: u8,
|
pub num: u8,
|
||||||
}
|
|
||||||
|
|
||||||
impl DynPinId {
|
|
||||||
pub const fn new(port: Port, num: u8) -> Self {
|
|
||||||
DynPinId { port, num }
|
|
||||||
}
|
|
||||||
|
|
||||||
pub const fn port(&self) -> Port {
|
|
||||||
self.port
|
|
||||||
}
|
|
||||||
pub const fn num(&self) -> u8 {
|
|
||||||
self.num
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
//==================================================================================================
|
//==================================================================================================
|
||||||
// ModeFields
|
// DynRegisters
|
||||||
//==================================================================================================
|
//==================================================================================================
|
||||||
|
|
||||||
/// Collect all fields needed to set the [`PinMode`](super::PinMode)
|
/// Provide a safe register interface for [`DynPin`]s
|
||||||
#[derive(Default)]
|
///
|
||||||
struct ModeFields {
|
/// This `struct` takes ownership of a [`DynPinId`] and provides an API to
|
||||||
dir: bool,
|
/// access the corresponding regsiters.
|
||||||
opendrn: bool,
|
struct DynRegisters {
|
||||||
pull_en: bool,
|
id: DynPinId,
|
||||||
/// true for pullup, false for pulldown
|
|
||||||
pull_dir: bool,
|
|
||||||
funsel: u8,
|
|
||||||
enb_input: bool,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
impl From<DynPinMode> for ModeFields {
|
// [`DynRegisters`] takes ownership of the [`DynPinId`], and [`DynPin`]
|
||||||
|
// guarantees that each pin is a singleton, so this implementation is safe.
|
||||||
|
unsafe impl RegisterInterface for DynRegisters {
|
||||||
#[inline]
|
#[inline]
|
||||||
fn from(mode: DynPinMode) -> Self {
|
fn id(&self) -> DynPinId {
|
||||||
let mut fields = Self::default();
|
self.id
|
||||||
match mode {
|
|
||||||
DynPinMode::Input(config) => {
|
|
||||||
fields.dir = false;
|
|
||||||
fields.funsel = FunSel::Sel0 as u8;
|
|
||||||
match config {
|
|
||||||
DynInput::Floating => (),
|
|
||||||
DynInput::PullUp => {
|
|
||||||
fields.pull_en = true;
|
|
||||||
fields.pull_dir = true;
|
|
||||||
}
|
|
||||||
DynInput::PullDown => {
|
|
||||||
fields.pull_en = true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
DynPinMode::Output(config) => {
|
|
||||||
fields.dir = true;
|
|
||||||
fields.funsel = FunSel::Sel0 as u8;
|
|
||||||
match config {
|
|
||||||
DynOutput::PushPull => (),
|
|
||||||
DynOutput::OpenDrain => {
|
|
||||||
fields.opendrn = true;
|
|
||||||
}
|
|
||||||
DynOutput::ReadableOpenDrain => {
|
|
||||||
fields.enb_input = true;
|
|
||||||
fields.opendrn = true;
|
|
||||||
}
|
|
||||||
DynOutput::ReadablePushPull => {
|
|
||||||
fields.enb_input = true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
DynPinMode::Alternate(config) => {
|
|
||||||
fields.funsel = config as u8;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
fields
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Type definition to avoid confusion: These register blocks are identical
|
impl DynRegisters {
|
||||||
type PortRegisterBlock = pac::porta::RegisterBlock;
|
/// Create a new instance of [`DynRegisters`]
|
||||||
pub type PortReg = pac::ioconfig::Porta;
|
///
|
||||||
|
/// # Safety
|
||||||
|
///
|
||||||
|
/// Users must never create two simultaneous instances of this `struct` with
|
||||||
|
/// the same [`DynPinId`]
|
||||||
|
#[inline]
|
||||||
|
unsafe fn new(id: DynPinId) -> Self {
|
||||||
|
DynRegisters { id }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
//==================================================================================================
|
//==================================================================================================
|
||||||
// DynPin
|
// DynPin
|
||||||
@ -249,71 +206,47 @@ pub type PortReg = pac::ioconfig::Porta;
|
|||||||
///
|
///
|
||||||
/// This type acts as a type-erased version of [`Pin`]. Every pin is represented
|
/// This type acts as a type-erased version of [`Pin`]. Every pin is represented
|
||||||
/// by the same type, and pins are tracked and distinguished at run-time.
|
/// by the same type, and pins are tracked and distinguished at run-time.
|
||||||
#[derive(Debug)]
|
|
||||||
pub struct DynPin {
|
pub struct DynPin {
|
||||||
id: DynPinId,
|
regs: DynRegisters,
|
||||||
mode: DynPinMode,
|
mode: DynPinMode,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl DynPin {
|
impl DynPin {
|
||||||
/// Create a new [DynPin]
|
/// Create a new [`DynPin`]
|
||||||
///
|
///
|
||||||
/// # Safety
|
/// # Safety
|
||||||
///
|
///
|
||||||
/// Each [DynPin] must be a singleton. For a given [DynPinId], there
|
/// Each [`DynPin`] must be a singleton. For a given [`DynPinId`], there
|
||||||
/// must be at most one corresponding [`DynPin`] in existence at any given
|
/// must be at most one corresponding [`DynPin`] in existence at any given
|
||||||
/// time. Violating this requirement is `unsafe`.
|
/// time. Violating this requirement is `unsafe`.
|
||||||
#[inline]
|
#[inline]
|
||||||
pub(crate) const unsafe fn new(id: DynPinId, mode: DynPinMode) -> Self {
|
unsafe fn new(id: DynPinId, mode: DynPinMode) -> Self {
|
||||||
DynPin { id, mode }
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Steals a new [DynPin].
|
|
||||||
///
|
|
||||||
/// This function will simply set the internal mode to [DYN_FLOATING_INPUT] pin without
|
|
||||||
/// modifying any registers related to the behaviour of the pin. The user should call
|
|
||||||
/// [Self::into_mode] to ensure the correct mode of the pin.
|
|
||||||
///
|
|
||||||
/// # Safety
|
|
||||||
///
|
|
||||||
/// Circumvents the HAL's safety guarantees. The caller must ensure that the pin is not
|
|
||||||
/// used cocurrently somewhere else. The caller might also want to call [Self::into_mode]
|
|
||||||
/// to ensure the correct desired state of the pin. It is recommended to create the pin using
|
|
||||||
/// [Pin::downgrade] instead.
|
|
||||||
pub const unsafe fn steal(id: DynPinId) -> Self {
|
|
||||||
DynPin {
|
DynPin {
|
||||||
id,
|
regs: DynRegisters::new(id),
|
||||||
mode: DYN_FLOATING_INPUT,
|
mode,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Return a copy of the pin ID
|
/// Return a copy of the pin ID
|
||||||
#[inline]
|
#[inline]
|
||||||
pub const fn id(&self) -> DynPinId {
|
pub fn id(&self) -> DynPinId {
|
||||||
self.id
|
self.regs.id
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Return a copy of the pin mode
|
/// Return a copy of the pin mode
|
||||||
#[inline]
|
#[inline]
|
||||||
pub const fn mode(&self) -> DynPinMode {
|
pub fn mode(&self) -> DynPinMode {
|
||||||
self.mode
|
self.mode
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Convert the pin to the requested [`DynPinMode`]
|
/// Convert the pin to the requested [`DynPinMode`]
|
||||||
#[inline]
|
#[inline]
|
||||||
pub fn into_mode(&mut self, mode: DynPinMode) {
|
pub fn into_mode(&mut self, mode: DynPinMode) {
|
||||||
self.change_mode(mode);
|
// Only modify registers if we are actually changing pin mode
|
||||||
self.mode = mode;
|
if mode != self.mode {
|
||||||
}
|
self.regs.change_mode(mode);
|
||||||
|
self.mode = mode;
|
||||||
#[inline]
|
}
|
||||||
pub fn is_input_pin(&self) -> bool {
|
|
||||||
matches!(self.mode, DynPinMode::Input(_))
|
|
||||||
}
|
|
||||||
|
|
||||||
#[inline]
|
|
||||||
pub fn is_output_pin(&self) -> bool {
|
|
||||||
matches!(self.mode, DynPinMode::Output(_))
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#[inline]
|
#[inline]
|
||||||
@ -373,502 +306,137 @@ impl DynPin {
|
|||||||
self.into_mode(DYN_RD_OPEN_DRAIN_OUTPUT);
|
self.into_mode(DYN_RD_OPEN_DRAIN_OUTPUT);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[inline(always)]
|
common_reg_if_functions!();
|
||||||
pub fn is_low(&self) -> Result<bool, InvalidPinTypeError> {
|
|
||||||
self.read_internal().map(|v| !v)
|
|
||||||
}
|
|
||||||
|
|
||||||
#[inline(always)]
|
|
||||||
pub fn is_high(&self) -> Result<bool, InvalidPinTypeError> {
|
|
||||||
self.read_internal()
|
|
||||||
}
|
|
||||||
|
|
||||||
#[inline(always)]
|
|
||||||
pub fn set_low(&mut self) -> Result<(), InvalidPinTypeError> {
|
|
||||||
self.write_internal(false)
|
|
||||||
}
|
|
||||||
|
|
||||||
#[inline(always)]
|
|
||||||
pub fn set_high(&mut self) -> Result<(), InvalidPinTypeError> {
|
|
||||||
self.write_internal(true)
|
|
||||||
}
|
|
||||||
|
|
||||||
/// 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));
|
|
||||||
}
|
|
||||||
// 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()) });
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn disable_interrupt(&mut self, reset_irqsel: bool) {
|
|
||||||
if reset_irqsel {
|
|
||||||
self.reset_irqsel();
|
|
||||||
}
|
|
||||||
// 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`]
|
|
||||||
///
|
|
||||||
/// There is no way for the compiler to know if the conversion will be
|
|
||||||
/// successful at compile-time. We must verify the conversion at run-time
|
|
||||||
/// or refuse to perform it.
|
|
||||||
#[inline]
|
|
||||||
pub fn upgrade<I: PinId, M: PinMode>(self) -> Result<Pin<I, M>, InvalidPinTypeError> {
|
|
||||||
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() });
|
|
||||||
}
|
|
||||||
Err(InvalidPinTypeError(self.mode))
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Convert the pin into an async pin. The pin can be converted back by calling
|
|
||||||
/// [InputDynPinAsync::release]
|
|
||||||
pub fn into_async_input(
|
|
||||||
self,
|
|
||||||
irq: crate::pac::Interrupt,
|
|
||||||
) -> 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)
|
|
||||||
}
|
|
||||||
|
|
||||||
|
/// See p.53 of the programmers guide for more information.
|
||||||
/// Possible delays in clock cycles:
|
/// Possible delays in clock cycles:
|
||||||
/// - Delay 1: 1
|
/// - Delay 1: 1
|
||||||
/// - Delay 2: 2
|
/// - Delay 2: 2
|
||||||
/// - Delay 1 + Delay 2: 3
|
/// - Delay 1 + Delay 2: 3
|
||||||
#[inline]
|
#[inline]
|
||||||
pub fn configure_delay(
|
pub fn delay(self, delay_1: bool, delay_2: bool) -> Result<Self, InvalidPinTypeError> {
|
||||||
&mut self,
|
|
||||||
delay_1: bool,
|
|
||||||
delay_2: bool,
|
|
||||||
) -> Result<(), InvalidPinTypeError> {
|
|
||||||
match self.mode {
|
match self.mode {
|
||||||
DynPinMode::Output(_) => {
|
DynPinMode::Output(_) => {
|
||||||
self.configure_delay_internal(delay_1, delay_2);
|
self.regs.delay(delay_1, delay_2);
|
||||||
Ok(())
|
Ok(self)
|
||||||
}
|
}
|
||||||
_ => Err(InvalidPinTypeError(self.mode)),
|
_ => Err(InvalidPinTypeError),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// See p.52 of the programmers guide for more information.
|
||||||
/// When configured for pulse mode, a given pin will set the non-default state for exactly
|
/// When configured for pulse mode, a given pin will set the non-default state for exactly
|
||||||
/// one clock cycle before returning to the configured default state
|
/// one clock cycle before returning to the configured default state
|
||||||
#[inline]
|
pub fn pulse_mode(
|
||||||
pub fn configure_pulse_mode(
|
self,
|
||||||
&mut self,
|
|
||||||
enable: bool,
|
enable: bool,
|
||||||
default_state: PinState,
|
default_state: PinState,
|
||||||
) -> Result<(), InvalidPinTypeError> {
|
) -> Result<Self, InvalidPinTypeError> {
|
||||||
match self.mode {
|
match self.mode {
|
||||||
DynPinMode::Output(_) => {
|
DynPinMode::Output(_) => {
|
||||||
self.configure_pulse_mode_internal(enable, default_state);
|
self.regs.pulse_mode(enable, default_state);
|
||||||
Ok(())
|
Ok(self)
|
||||||
}
|
}
|
||||||
_ => Err(InvalidPinTypeError(self.mode)),
|
_ => Err(InvalidPinTypeError),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// See p.37 and p.38 of the programmers guide for more information.
|
/// See p.37 and p.38 of the programmers guide for more information.
|
||||||
#[inline]
|
#[inline]
|
||||||
pub fn configure_filter_type(
|
pub fn filter_type(
|
||||||
&mut self,
|
self,
|
||||||
filter: FilterType,
|
filter: FilterType,
|
||||||
clksel: FilterClkSel,
|
clksel: FilterClkSel,
|
||||||
) -> Result<(), InvalidPinTypeError> {
|
) -> Result<Self, InvalidPinTypeError> {
|
||||||
match self.mode {
|
match self.mode {
|
||||||
DynPinMode::Input(_) => {
|
DynPinMode::Input(_) => {
|
||||||
self.configure_filter_type_internal(filter, clksel);
|
self.regs.filter_type(filter, clksel);
|
||||||
Ok(())
|
Ok(self)
|
||||||
}
|
}
|
||||||
_ => Err(InvalidPinTypeError(self.mode)),
|
_ => Err(InvalidPinTypeError),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn configure_edge_interrupt(
|
pub fn interrupt_edge(
|
||||||
&mut self,
|
mut self,
|
||||||
edge_type: InterruptEdge,
|
edge_type: InterruptEdge,
|
||||||
) -> Result<(), InvalidPinTypeError> {
|
irq_cfg: IrqCfg,
|
||||||
|
syscfg: Option<&mut pac::Sysconfig>,
|
||||||
|
irqsel: Option<&mut pac::Irqsel>,
|
||||||
|
) -> Result<Self, InvalidPinTypeError> {
|
||||||
match self.mode {
|
match self.mode {
|
||||||
DynPinMode::Input(_) | DynPinMode::Output(_) => {
|
DynPinMode::Input(_) | DynPinMode::Output(_) => {
|
||||||
self.configure_edge_interrupt_internal(edge_type);
|
self.regs.interrupt_edge(edge_type);
|
||||||
Ok(())
|
self.irq_enb(irq_cfg, syscfg, irqsel);
|
||||||
|
Ok(self)
|
||||||
}
|
}
|
||||||
_ => Err(InvalidPinTypeError(self.mode)),
|
_ => Err(InvalidPinTypeError),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn configure_level_interrupt(
|
pub fn interrupt_level(
|
||||||
&mut self,
|
mut self,
|
||||||
level_type: InterruptLevel,
|
level_type: InterruptLevel,
|
||||||
) -> Result<(), InvalidPinTypeError> {
|
irq_cfg: IrqCfg,
|
||||||
|
syscfg: Option<&mut pac::Sysconfig>,
|
||||||
|
irqsel: Option<&mut pac::Irqsel>,
|
||||||
|
) -> Result<Self, InvalidPinTypeError> {
|
||||||
match self.mode {
|
match self.mode {
|
||||||
DynPinMode::Input(_) | DynPinMode::Output(_) => {
|
DynPinMode::Input(_) | DynPinMode::Output(_) => {
|
||||||
self.configure_level_interrupt_internal(level_type);
|
self.regs.interrupt_level(level_type);
|
||||||
Ok(())
|
self.irq_enb(irq_cfg, syscfg, irqsel);
|
||||||
}
|
Ok(self)
|
||||||
_ => 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));
|
|
||||||
}
|
}
|
||||||
|
_ => Err(InvalidPinTypeError),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[inline]
|
#[inline]
|
||||||
const fn port_reg(&self) -> &PortRegisterBlock {
|
pub fn toggle_with_toggle_reg(&mut self) -> Result<(), InvalidPinTypeError> {
|
||||||
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 {
|
match self.mode {
|
||||||
DynPinMode::Output(_) => {
|
DynPinMode::Output(_) => {
|
||||||
self.write_pin(bit);
|
self.regs.toggle();
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
_ => Err(InvalidPinTypeError(self.mode)),
|
_ => Err(InvalidPinTypeError),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[inline]
|
#[inline]
|
||||||
/// Read the logic level of an output pin
|
fn _read(&self) -> Result<bool, InvalidPinTypeError> {
|
||||||
pub(crate) fn read_pin(&self) -> bool {
|
match self.mode {
|
||||||
let portreg = self.port_reg();
|
DynPinMode::Input(_) | DYN_RD_OPEN_DRAIN_OUTPUT | DYN_RD_PUSH_PULL_OUTPUT => {
|
||||||
((portreg.datainraw().read().bits() >> self.id().num) & 0x01) == 1
|
Ok(self.regs.read_pin())
|
||||||
}
|
|
||||||
|
|
||||||
/// 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()));
|
|
||||||
}
|
}
|
||||||
|
_ => Err(InvalidPinTypeError),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Write the logic level of an output pin but check whether the datamask for the pin is
|
|
||||||
/// cleared as well
|
|
||||||
#[inline]
|
#[inline]
|
||||||
fn write_pin_masked(&mut self, bit: bool) -> Result<(), IsMaskedError> {
|
fn _write(&mut self, bit: bool) -> Result<(), InvalidPinTypeError> {
|
||||||
if !self.datamask() {
|
match self.mode {
|
||||||
Err(IsMaskedError)
|
DynPinMode::Output(_) => {
|
||||||
} else {
|
self.regs.write_pin(bit);
|
||||||
// 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(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
_ => Err(InvalidPinTypeError),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// 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]
|
#[inline]
|
||||||
fn configure_edge_interrupt_internal(&mut self, edge_type: InterruptEdge) {
|
fn _is_low(&self) -> Result<bool, InvalidPinTypeError> {
|
||||||
unsafe {
|
self._read().map(|v| !v)
|
||||||
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]
|
#[inline]
|
||||||
fn configure_level_interrupt_internal(&mut self, level: InterruptLevel) {
|
fn _is_high(&self) -> Result<bool, InvalidPinTypeError> {
|
||||||
unsafe {
|
self._read()
|
||||||
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]
|
#[inline]
|
||||||
fn configure_filter_type_internal(&mut self, filter: FilterType, clksel: FilterClkSel) {
|
fn _set_low(&mut self) -> Result<(), InvalidPinTypeError> {
|
||||||
self.iocfg_port().modify(|_, w| {
|
self._write(false)
|
||||||
// Safety: Only write to register for this Pin ID
|
|
||||||
unsafe {
|
|
||||||
w.flttype().bits(filter as u8);
|
|
||||||
w.fltclk().bits(clksel as u8)
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#[inline]
|
#[inline]
|
||||||
fn configure_pulse_mode_internal(&mut self, enable: bool, default_state: PinState) {
|
fn _set_high(&mut self) -> Result<(), InvalidPinTypeError> {
|
||||||
let portreg = self.port_reg();
|
self._write(true)
|
||||||
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()
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -880,8 +448,10 @@ impl<I: PinId, M: PinMode> From<Pin<I, M>> for DynPin {
|
|||||||
/// Erase the type-level information in a [`Pin`] and return a value-level
|
/// Erase the type-level information in a [`Pin`] and return a value-level
|
||||||
/// [`DynPin`]
|
/// [`DynPin`]
|
||||||
#[inline]
|
#[inline]
|
||||||
fn from(pin: Pin<I, M>) -> Self {
|
fn from(_pin: Pin<I, M>) -> Self {
|
||||||
pin.downgrade()
|
// The `Pin` is consumed, so it is safe to replace it with the
|
||||||
|
// corresponding `DynPin`
|
||||||
|
unsafe { DynPin::new(I::DYN, M::DYN) }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -895,7 +465,13 @@ impl<I: PinId, M: PinMode> TryFrom<DynPin> for Pin<I, M> {
|
|||||||
/// or refuse to perform it.
|
/// or refuse to perform it.
|
||||||
#[inline]
|
#[inline]
|
||||||
fn try_from(pin: DynPin) -> Result<Self, Self::Error> {
|
fn try_from(pin: DynPin) -> Result<Self, Self::Error> {
|
||||||
pin.upgrade()
|
if pin.regs.id == I::DYN && pin.mode == M::DYN {
|
||||||
|
// The `DynPin` is consumed, so it is safe to replace it with the
|
||||||
|
// corresponding `Pin`
|
||||||
|
Ok(unsafe { Self::new() })
|
||||||
|
} else {
|
||||||
|
Err(InvalidPinTypeError)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -910,38 +486,31 @@ impl embedded_hal::digital::ErrorType for DynPin {
|
|||||||
impl embedded_hal::digital::OutputPin for DynPin {
|
impl embedded_hal::digital::OutputPin for DynPin {
|
||||||
#[inline]
|
#[inline]
|
||||||
fn set_high(&mut self) -> Result<(), Self::Error> {
|
fn set_high(&mut self) -> Result<(), Self::Error> {
|
||||||
self.set_high()
|
self._set_high()
|
||||||
}
|
}
|
||||||
#[inline]
|
#[inline]
|
||||||
fn set_low(&mut self) -> Result<(), Self::Error> {
|
fn set_low(&mut self) -> Result<(), Self::Error> {
|
||||||
self.set_low()
|
self._set_low()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl embedded_hal::digital::InputPin for DynPin {
|
impl embedded_hal::digital::InputPin for DynPin {
|
||||||
#[inline]
|
#[inline]
|
||||||
fn is_high(&mut self) -> Result<bool, Self::Error> {
|
fn is_high(&mut self) -> Result<bool, Self::Error> {
|
||||||
self.is_high_mut()
|
self._is_high()
|
||||||
}
|
}
|
||||||
#[inline]
|
#[inline]
|
||||||
fn is_low(&mut self) -> Result<bool, Self::Error> {
|
fn is_low(&mut self) -> Result<bool, Self::Error> {
|
||||||
self.is_low_mut()
|
self._is_low()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl embedded_hal::digital::StatefulOutputPin for DynPin {
|
impl embedded_hal::digital::StatefulOutputPin for DynPin {
|
||||||
#[inline]
|
|
||||||
fn is_set_high(&mut self) -> Result<bool, Self::Error> {
|
fn is_set_high(&mut self) -> Result<bool, Self::Error> {
|
||||||
self.is_high_mut()
|
self._is_high()
|
||||||
}
|
}
|
||||||
|
|
||||||
#[inline]
|
|
||||||
fn is_set_low(&mut self) -> Result<bool, Self::Error> {
|
fn is_set_low(&mut self) -> Result<bool, Self::Error> {
|
||||||
self.is_low_mut()
|
self._is_low()
|
||||||
}
|
|
||||||
|
|
||||||
#[inline]
|
|
||||||
fn toggle(&mut self) -> Result<(), Self::Error> {
|
|
||||||
self.toggle()
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -22,46 +22,83 @@
|
|||||||
//!
|
//!
|
||||||
//! - [Blinky example](https://egit.irs.uni-stuttgart.de/rust/va108xx-rs/src/branch/main/examples/simple/examples/blinky.rs)
|
//! - [Blinky example](https://egit.irs.uni-stuttgart.de/rust/va108xx-rs/src/branch/main/examples/simple/examples/blinky.rs)
|
||||||
|
|
||||||
//==================================================================================================
|
#[derive(Debug, PartialEq, Eq)]
|
||||||
// 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))]
|
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
|
||||||
#[error("The pin is masked")]
|
|
||||||
pub struct IsMaskedError;
|
pub struct IsMaskedError;
|
||||||
|
|
||||||
#[derive(Debug, PartialEq, Eq, Clone, Copy)]
|
macro_rules! common_reg_if_functions {
|
||||||
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
|
() => {
|
||||||
pub enum Port {
|
paste::paste!(
|
||||||
A,
|
#[inline]
|
||||||
B,
|
pub fn datamask(&self) -> bool {
|
||||||
}
|
self.regs.datamask()
|
||||||
|
}
|
||||||
|
|
||||||
#[derive(Debug, PartialEq, Eq)]
|
#[inline]
|
||||||
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
|
pub fn clear_datamask(self) -> Self {
|
||||||
pub enum InterruptEdge {
|
self.regs.clear_datamask();
|
||||||
HighToLow,
|
self
|
||||||
LowToHigh,
|
}
|
||||||
BothEdges,
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Debug, PartialEq, Eq)]
|
#[inline]
|
||||||
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
|
pub fn set_datamask(self) -> Self {
|
||||||
pub enum InterruptLevel {
|
self.regs.set_datamask();
|
||||||
Low = 0,
|
self
|
||||||
High = 1,
|
}
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Debug, PartialEq, Eq)]
|
#[inline]
|
||||||
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
|
pub fn is_high_masked(&self) -> Result<bool, crate::gpio::IsMaskedError> {
|
||||||
pub enum PinState {
|
self.regs.read_pin_masked()
|
||||||
Low = 0,
|
}
|
||||||
High = 1,
|
|
||||||
|
#[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)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn irq_enb(
|
||||||
|
&mut self,
|
||||||
|
irq_cfg: crate::IrqCfg,
|
||||||
|
syscfg: Option<&mut va108xx::Sysconfig>,
|
||||||
|
irqsel: Option<&mut va108xx::Irqsel>,
|
||||||
|
) {
|
||||||
|
if syscfg.is_some() {
|
||||||
|
crate::clock::enable_peripheral_clock(
|
||||||
|
syscfg.unwrap(),
|
||||||
|
crate::clock::PeripheralClocks::Irqsel,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
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.irq as u32) });
|
||||||
|
}
|
||||||
|
DynGroup::B => {
|
||||||
|
irqsel
|
||||||
|
.portb0(self.regs.id().num as usize)
|
||||||
|
.write(|w| unsafe { w.bits(irq_cfg.irq as u32) });
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
);
|
||||||
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
pub mod dynpin;
|
pub mod dynpin;
|
||||||
@ -70,5 +107,4 @@ pub use dynpin::*;
|
|||||||
pub mod pin;
|
pub mod pin;
|
||||||
pub use pin::*;
|
pub use pin::*;
|
||||||
|
|
||||||
pub mod asynch;
|
mod reg;
|
||||||
pub use asynch::*;
|
|
||||||
|
@ -68,19 +68,44 @@
|
|||||||
//! # Embedded HAL traits
|
//! # Embedded HAL traits
|
||||||
//!
|
//!
|
||||||
//! This module implements all of the embedded HAL GPIO traits for each [`Pin`]
|
//! This module implements all of the embedded HAL GPIO traits for each [`Pin`]
|
||||||
//! in the corresponding [`PinMode`]s, namely: [embedded_hal::digital::InputPin],
|
//! in the corresponding [`PinMode`]s, namely: [`InputPin`], [`OutputPin`],
|
||||||
//! [embedded_hal::digital::OutputPin] and [embedded_hal::digital::StatefulOutputPin].
|
//! and [`StatefulOutputPin`].
|
||||||
use super::dynpin::{DynAlternate, DynInput, DynOutput, DynPinId, DynPinMode};
|
use super::dynpin::{DynAlternate, DynGroup, DynInput, DynOutput, DynPinId, DynPinMode};
|
||||||
use super::{DynPin, InputPinAsync, InterruptEdge, InterruptLevel, PinState, Port};
|
use super::reg::RegisterInterface;
|
||||||
use crate::{
|
use crate::{
|
||||||
pac::{Porta, Portb},
|
pac::{Irqsel, Porta, Portb, Sysconfig},
|
||||||
typelevel::Sealed,
|
typelevel::Sealed,
|
||||||
|
IrqCfg,
|
||||||
};
|
};
|
||||||
use core::convert::Infallible;
|
use core::convert::Infallible;
|
||||||
use core::marker::PhantomData;
|
use core::marker::PhantomData;
|
||||||
use core::mem::transmute;
|
use core::mem::transmute;
|
||||||
|
use embedded_hal::digital::{InputPin, OutputPin, StatefulOutputPin};
|
||||||
use paste::paste;
|
use paste::paste;
|
||||||
|
|
||||||
|
//==================================================================================================
|
||||||
|
// Errors and Definitions
|
||||||
|
//==================================================================================================
|
||||||
|
|
||||||
|
#[derive(Debug, PartialEq, Eq)]
|
||||||
|
pub enum InterruptEdge {
|
||||||
|
HighToLow,
|
||||||
|
LowToHigh,
|
||||||
|
BothEdges,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, PartialEq, Eq)]
|
||||||
|
pub enum InterruptLevel {
|
||||||
|
Low = 0,
|
||||||
|
High = 1,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, PartialEq, Eq)]
|
||||||
|
pub enum PinState {
|
||||||
|
Low = 0,
|
||||||
|
High = 1,
|
||||||
|
}
|
||||||
|
|
||||||
//==================================================================================================
|
//==================================================================================================
|
||||||
// Input configuration
|
// Input configuration
|
||||||
//==================================================================================================
|
//==================================================================================================
|
||||||
@ -93,11 +118,8 @@ pub trait InputConfig: Sealed {
|
|||||||
const DYN: DynInput;
|
const DYN: DynInput;
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug)]
|
|
||||||
pub enum Floating {}
|
pub enum Floating {}
|
||||||
#[derive(Debug)]
|
|
||||||
pub enum PullDown {}
|
pub enum PullDown {}
|
||||||
#[derive(Debug)]
|
|
||||||
pub enum PullUp {}
|
pub enum PullUp {}
|
||||||
|
|
||||||
impl InputConfig for Floating {
|
impl InputConfig for Floating {
|
||||||
@ -125,7 +147,6 @@ pub type InputPullUp = Input<PullUp>;
|
|||||||
///
|
///
|
||||||
/// Type `C` is one of three input configurations: [`Floating`], [`PullDown`] or
|
/// Type `C` is one of three input configurations: [`Floating`], [`PullDown`] or
|
||||||
/// [`PullUp`]
|
/// [`PullUp`]
|
||||||
#[derive(Debug)]
|
|
||||||
pub struct Input<C: InputConfig> {
|
pub struct Input<C: InputConfig> {
|
||||||
cfg: PhantomData<C>,
|
cfg: PhantomData<C>,
|
||||||
}
|
}
|
||||||
@ -155,17 +176,13 @@ pub trait OutputConfig: Sealed {
|
|||||||
pub trait ReadableOutput: Sealed {}
|
pub trait ReadableOutput: Sealed {}
|
||||||
|
|
||||||
/// Type-level variant of [`OutputConfig`] for a push-pull configuration
|
/// Type-level variant of [`OutputConfig`] for a push-pull configuration
|
||||||
#[derive(Debug)]
|
|
||||||
pub enum PushPull {}
|
pub enum PushPull {}
|
||||||
/// Type-level variant of [`OutputConfig`] for an open drain configuration
|
/// Type-level variant of [`OutputConfig`] for an open drain configuration
|
||||||
#[derive(Debug)]
|
|
||||||
pub enum OpenDrain {}
|
pub enum OpenDrain {}
|
||||||
|
|
||||||
/// Type-level variant of [`OutputConfig`] for a readable push-pull configuration
|
/// Type-level variant of [`OutputConfig`] for a readable push-pull configuration
|
||||||
#[derive(Debug)]
|
|
||||||
pub enum ReadablePushPull {}
|
pub enum ReadablePushPull {}
|
||||||
/// Type-level variant of [`OutputConfig`] for a readable open-drain configuration
|
/// Type-level variant of [`OutputConfig`] for a readable open-drain configuration
|
||||||
#[derive(Debug)]
|
|
||||||
pub enum ReadableOpenDrain {}
|
pub enum ReadableOpenDrain {}
|
||||||
|
|
||||||
impl Sealed for PushPull {}
|
impl Sealed for PushPull {}
|
||||||
@ -192,7 +209,6 @@ impl OutputConfig for ReadableOpenDrain {
|
|||||||
///
|
///
|
||||||
/// Type `C` is one of four output configurations: [`PushPull`], [`OpenDrain`] or
|
/// Type `C` is one of four output configurations: [`PushPull`], [`OpenDrain`] or
|
||||||
/// their respective readable versions
|
/// their respective readable versions
|
||||||
#[derive(Debug)]
|
|
||||||
pub struct Output<C: OutputConfig> {
|
pub struct Output<C: OutputConfig> {
|
||||||
cfg: PhantomData<C>,
|
cfg: PhantomData<C>,
|
||||||
}
|
}
|
||||||
@ -287,11 +303,13 @@ macro_rules! pin_id {
|
|||||||
// Need paste macro to use ident in doc attribute
|
// Need paste macro to use ident in doc attribute
|
||||||
paste! {
|
paste! {
|
||||||
#[doc = "Pin ID representing pin " $Id]
|
#[doc = "Pin ID representing pin " $Id]
|
||||||
#[derive(Debug)]
|
|
||||||
pub enum $Id {}
|
pub enum $Id {}
|
||||||
impl Sealed for $Id {}
|
impl Sealed for $Id {}
|
||||||
impl PinId for $Id {
|
impl PinId for $Id {
|
||||||
const DYN: DynPinId = DynPinId::new(Port::$Group, $NUM);
|
const DYN: DynPinId = DynPinId {
|
||||||
|
group: DynGroup::$Group,
|
||||||
|
num: $NUM,
|
||||||
|
};
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
@ -302,10 +320,9 @@ macro_rules! pin_id {
|
|||||||
//==================================================================================================
|
//==================================================================================================
|
||||||
|
|
||||||
/// A type-level GPIO pin, parameterized by [PinId] and [PinMode] types
|
/// A type-level GPIO pin, parameterized by [PinId] and [PinMode] types
|
||||||
#[derive(Debug)]
|
|
||||||
pub struct Pin<I: PinId, M: PinMode> {
|
pub struct Pin<I: PinId, M: PinMode> {
|
||||||
inner: DynPin,
|
pub(in crate::gpio) regs: Registers<I>,
|
||||||
phantom: PhantomData<(I, M)>,
|
mode: PhantomData<M>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<I: PinId, M: PinMode> Pin<I, M> {
|
impl<I: PinId, M: PinMode> Pin<I, M> {
|
||||||
@ -317,25 +334,20 @@ impl<I: PinId, M: PinMode> Pin<I, M> {
|
|||||||
/// at most one corresponding [Pin] in existence at any given time.
|
/// at most one corresponding [Pin] in existence at any given time.
|
||||||
/// Violating this requirement is `unsafe`.
|
/// Violating this requirement is `unsafe`.
|
||||||
#[inline]
|
#[inline]
|
||||||
pub(crate) const unsafe fn new() -> Pin<I, M> {
|
pub(crate) unsafe fn new() -> Pin<I, M> {
|
||||||
Pin {
|
Pin {
|
||||||
inner: DynPin::new(I::DYN, M::DYN),
|
regs: Registers::new(),
|
||||||
phantom: PhantomData,
|
mode: PhantomData,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[inline]
|
|
||||||
pub const fn id(&self) -> DynPinId {
|
|
||||||
self.inner.id()
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Convert the pin to the requested [`PinMode`]
|
/// Convert the pin to the requested [`PinMode`]
|
||||||
#[inline]
|
#[inline]
|
||||||
pub fn into_mode<N: PinMode>(mut self) -> Pin<I, N> {
|
pub fn into_mode<N: PinMode>(mut self) -> Pin<I, N> {
|
||||||
// Only modify registers if we are actually changing pin mode
|
// Only modify registers if we are actually changing pin mode
|
||||||
// This check should compile away
|
// This check should compile away
|
||||||
if N::DYN != M::DYN {
|
if N::DYN != M::DYN {
|
||||||
self.inner.change_mode(N::DYN);
|
self.regs.change_mode::<N>();
|
||||||
}
|
}
|
||||||
// Safe because we drop the existing Pin
|
// Safe because we drop the existing Pin
|
||||||
unsafe { Pin::new() }
|
unsafe { Pin::new() }
|
||||||
@ -395,76 +407,31 @@ impl<I: PinId, M: PinMode> Pin<I, M> {
|
|||||||
self.into_mode()
|
self.into_mode()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
common_reg_if_functions!();
|
||||||
|
|
||||||
#[inline]
|
#[inline]
|
||||||
pub fn is_low(&self) -> bool {
|
pub(crate) fn _set_high(&mut self) {
|
||||||
!self.inner.read_pin()
|
self.regs.write_pin(true)
|
||||||
}
|
}
|
||||||
|
|
||||||
#[inline]
|
#[inline]
|
||||||
pub fn is_high(&self) -> bool {
|
pub(crate) fn _set_low(&mut self) {
|
||||||
self.inner.read_pin()
|
self.regs.write_pin(false)
|
||||||
}
|
}
|
||||||
|
|
||||||
#[inline]
|
#[inline]
|
||||||
pub fn datamask(&self) -> bool {
|
pub(crate) fn _toggle_with_toggle_reg(&mut self) {
|
||||||
self.inner.datamask()
|
self.regs.toggle();
|
||||||
}
|
}
|
||||||
|
|
||||||
#[inline]
|
#[inline]
|
||||||
pub fn clear_datamask(&mut self) {
|
pub(crate) fn _is_low(&self) -> bool {
|
||||||
self.inner.clear_datamask()
|
!self.regs.read_pin()
|
||||||
}
|
}
|
||||||
|
|
||||||
#[inline]
|
#[inline]
|
||||||
pub fn set_datamask(&mut self) {
|
pub(crate) fn _is_high(&self) -> bool {
|
||||||
self.inner.set_datamask()
|
self.regs.read_pin()
|
||||||
}
|
|
||||||
|
|
||||||
#[inline]
|
|
||||||
pub fn is_high_masked(&self) -> Result<bool, crate::gpio::IsMaskedError> {
|
|
||||||
self.inner.is_high_masked()
|
|
||||||
}
|
|
||||||
|
|
||||||
#[inline]
|
|
||||||
pub fn is_low_masked(&self) -> Result<bool, crate::gpio::IsMaskedError> {
|
|
||||||
self.inner.is_low_masked()
|
|
||||||
}
|
|
||||||
|
|
||||||
#[inline]
|
|
||||||
pub fn downgrade(self) -> DynPin {
|
|
||||||
self.inner
|
|
||||||
}
|
|
||||||
|
|
||||||
// Those only serve for the embedded HAL implementations which have different mutability.
|
|
||||||
|
|
||||||
#[inline]
|
|
||||||
fn is_low_mut(&mut self) -> bool {
|
|
||||||
self.is_low()
|
|
||||||
}
|
|
||||||
|
|
||||||
#[inline]
|
|
||||||
fn is_high_mut(&mut self) -> bool {
|
|
||||||
self.is_high()
|
|
||||||
}
|
|
||||||
|
|
||||||
#[inline]
|
|
||||||
pub fn enable_interrupt(&mut self, irq_cfg: crate::InterruptConfig) {
|
|
||||||
self.inner.enable_interrupt(irq_cfg);
|
|
||||||
}
|
|
||||||
|
|
||||||
#[inline]
|
|
||||||
pub fn disable_interrupt(&mut self, reset_irqsel: bool) {
|
|
||||||
self.inner.disable_interrupt(reset_irqsel);
|
|
||||||
}
|
|
||||||
|
|
||||||
/// 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();
|
|
||||||
}
|
|
||||||
|
|
||||||
/// 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();
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -556,65 +523,87 @@ impl<P: AnyPin> AsMut<P> for SpecificPin<P> {
|
|||||||
//==================================================================================================
|
//==================================================================================================
|
||||||
|
|
||||||
impl<I: PinId, C: InputConfig> Pin<I, Input<C>> {
|
impl<I: PinId, C: InputConfig> Pin<I, Input<C>> {
|
||||||
/// Convert the pin into an async pin. The pin can be converted back by calling
|
pub fn interrupt_edge(
|
||||||
/// [InputPinAsync::release]
|
mut self,
|
||||||
pub fn into_async_input(self, irq: crate::pac::Interrupt) -> InputPinAsync<I, C> {
|
edge_type: InterruptEdge,
|
||||||
InputPinAsync::new(self, irq)
|
irq_cfg: IrqCfg,
|
||||||
|
syscfg: Option<&mut Sysconfig>,
|
||||||
|
irqsel: Option<&mut Irqsel>,
|
||||||
|
) -> Self {
|
||||||
|
self.regs.interrupt_edge(edge_type);
|
||||||
|
self.irq_enb(irq_cfg, syscfg, irqsel);
|
||||||
|
self
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn interrupt_level(
|
||||||
|
mut self,
|
||||||
|
level_type: InterruptLevel,
|
||||||
|
irq_cfg: IrqCfg,
|
||||||
|
syscfg: Option<&mut Sysconfig>,
|
||||||
|
irqsel: Option<&mut Irqsel>,
|
||||||
|
) -> Self {
|
||||||
|
self.regs.interrupt_level(level_type);
|
||||||
|
self.irq_enb(irq_cfg, syscfg, irqsel);
|
||||||
|
self
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<I: PinId, C: OutputConfig> Pin<I, Output<C>> {
|
impl<I: PinId, C: OutputConfig> Pin<I, Output<C>> {
|
||||||
#[inline]
|
|
||||||
pub fn set_high(&mut self) {
|
|
||||||
self.inner.write_pin(true)
|
|
||||||
}
|
|
||||||
|
|
||||||
#[inline]
|
|
||||||
pub fn set_low(&mut self) {
|
|
||||||
self.inner.write_pin(false)
|
|
||||||
}
|
|
||||||
|
|
||||||
#[inline]
|
|
||||||
pub fn toggle(&mut self) {
|
|
||||||
self.inner.toggle().unwrap()
|
|
||||||
}
|
|
||||||
|
|
||||||
#[inline]
|
|
||||||
pub fn set_high_masked(&mut self) -> Result<(), crate::gpio::IsMaskedError> {
|
|
||||||
self.inner.set_high_masked()
|
|
||||||
}
|
|
||||||
|
|
||||||
#[inline]
|
|
||||||
pub fn set_low_masked(&mut self) -> Result<(), crate::gpio::IsMaskedError> {
|
|
||||||
self.inner.set_low_masked()
|
|
||||||
}
|
|
||||||
|
|
||||||
/// See p.53 of the programmers guide for more information.
|
/// See p.53 of the programmers guide for more information.
|
||||||
/// Possible delays in clock cycles:
|
/// Possible delays in clock cycles:
|
||||||
/// - Delay 1: 1
|
/// - Delay 1: 1
|
||||||
/// - Delay 2: 2
|
/// - Delay 2: 2
|
||||||
/// - Delay 1 + Delay 2: 3
|
/// - Delay 1 + Delay 2: 3
|
||||||
#[inline]
|
#[inline]
|
||||||
pub fn configure_delay(&mut self, delay_1: bool, delay_2: bool) {
|
pub fn delay(self, delay_1: bool, delay_2: bool) -> Self {
|
||||||
self.inner.configure_delay(delay_1, delay_2).unwrap();
|
self.regs.delay(delay_1, delay_2);
|
||||||
|
self
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
pub fn toggle_with_toggle_reg(&mut self) {
|
||||||
|
self._toggle_with_toggle_reg()
|
||||||
}
|
}
|
||||||
|
|
||||||
/// See p.52 of the programmers guide for more information.
|
/// See p.52 of the programmers guide for more information.
|
||||||
///
|
|
||||||
/// When configured for pulse mode, a given pin will set the non-default state for exactly
|
/// When configured for pulse mode, a given pin will set the non-default state for exactly
|
||||||
/// one clock cycle before returning to the configured default state
|
/// one clock cycle before returning to the configured default state
|
||||||
pub fn configure_pulse_mode(&mut self, enable: bool, default_state: PinState) {
|
pub fn pulse_mode(self, enable: bool, default_state: PinState) -> Self {
|
||||||
self.inner
|
self.regs.pulse_mode(enable, default_state);
|
||||||
.configure_pulse_mode(enable, default_state)
|
self
|
||||||
.unwrap();
|
}
|
||||||
|
|
||||||
|
pub fn interrupt_edge(
|
||||||
|
mut self,
|
||||||
|
edge_type: InterruptEdge,
|
||||||
|
irq_cfg: IrqCfg,
|
||||||
|
syscfg: Option<&mut Sysconfig>,
|
||||||
|
irqsel: Option<&mut Irqsel>,
|
||||||
|
) -> Self {
|
||||||
|
self.regs.interrupt_edge(edge_type);
|
||||||
|
self.irq_enb(irq_cfg, syscfg, irqsel);
|
||||||
|
self
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn interrupt_level(
|
||||||
|
mut self,
|
||||||
|
level_type: InterruptLevel,
|
||||||
|
irq_cfg: IrqCfg,
|
||||||
|
syscfg: Option<&mut Sysconfig>,
|
||||||
|
irqsel: Option<&mut Irqsel>,
|
||||||
|
) -> Self {
|
||||||
|
self.regs.interrupt_level(level_type);
|
||||||
|
self.irq_enb(irq_cfg, syscfg, irqsel);
|
||||||
|
self
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<I: PinId, C: InputConfig> Pin<I, Input<C>> {
|
impl<I: PinId, C: InputConfig> Pin<I, Input<C>> {
|
||||||
/// See p.37 and p.38 of the programmers guide for more information.
|
/// See p.37 and p.38 of the programmers guide for more information.
|
||||||
#[inline]
|
#[inline]
|
||||||
pub fn configure_filter_type(&mut self, filter: FilterType, clksel: FilterClkSel) {
|
pub fn filter_type(self, filter: FilterType, clksel: FilterClkSel) -> Self {
|
||||||
self.inner.configure_filter_type(filter, clksel).unwrap();
|
self.regs.filter_type(filter, clksel);
|
||||||
|
self
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -630,53 +619,104 @@ where
|
|||||||
type Error = Infallible;
|
type Error = Infallible;
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<I: PinId, C: OutputConfig> embedded_hal::digital::OutputPin for Pin<I, Output<C>> {
|
impl<I: PinId, C: OutputConfig> OutputPin for Pin<I, Output<C>> {
|
||||||
#[inline]
|
#[inline]
|
||||||
fn set_high(&mut self) -> Result<(), Self::Error> {
|
fn set_high(&mut self) -> Result<(), Self::Error> {
|
||||||
self.set_high();
|
self._set_high();
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
#[inline]
|
#[inline]
|
||||||
fn set_low(&mut self) -> Result<(), Self::Error> {
|
fn set_low(&mut self) -> Result<(), Self::Error> {
|
||||||
self.set_low();
|
self._set_low();
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<I, C> embedded_hal::digital::InputPin for Pin<I, Input<C>>
|
impl<I, C> InputPin for Pin<I, Input<C>>
|
||||||
where
|
where
|
||||||
I: PinId,
|
I: PinId,
|
||||||
C: InputConfig,
|
C: InputConfig,
|
||||||
{
|
{
|
||||||
#[inline]
|
#[inline]
|
||||||
fn is_high(&mut self) -> Result<bool, Self::Error> {
|
fn is_high(&mut self) -> Result<bool, Self::Error> {
|
||||||
Ok(self.is_high_mut())
|
Ok(self._is_high())
|
||||||
}
|
}
|
||||||
#[inline]
|
#[inline]
|
||||||
fn is_low(&mut self) -> Result<bool, Self::Error> {
|
fn is_low(&mut self) -> Result<bool, Self::Error> {
|
||||||
Ok(self.is_low_mut())
|
Ok(self._is_low())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<I, C> embedded_hal::digital::StatefulOutputPin for Pin<I, Output<C>>
|
impl<I, C> StatefulOutputPin for Pin<I, Output<C>>
|
||||||
where
|
where
|
||||||
I: PinId,
|
I: PinId,
|
||||||
C: OutputConfig + ReadableOutput,
|
C: OutputConfig + ReadableOutput,
|
||||||
{
|
{
|
||||||
#[inline]
|
#[inline]
|
||||||
fn is_set_high(&mut self) -> Result<bool, Self::Error> {
|
fn is_set_high(&mut self) -> Result<bool, Self::Error> {
|
||||||
Ok(self.is_high())
|
Ok(self._is_high())
|
||||||
}
|
}
|
||||||
#[inline]
|
#[inline]
|
||||||
fn is_set_low(&mut self) -> Result<bool, Self::Error> {
|
fn is_set_low(&mut self) -> Result<bool, Self::Error> {
|
||||||
Ok(self.is_low())
|
Ok(self._is_low())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<I, C> InputPin for Pin<I, Output<C>>
|
||||||
|
where
|
||||||
|
I: PinId,
|
||||||
|
C: OutputConfig + ReadableOutput,
|
||||||
|
{
|
||||||
|
#[inline]
|
||||||
|
fn is_high(&mut self) -> Result<bool, Self::Error> {
|
||||||
|
Ok(self._is_high())
|
||||||
}
|
}
|
||||||
|
|
||||||
#[inline]
|
#[inline]
|
||||||
fn toggle(&mut self) -> Result<(), Self::Error> {
|
fn is_low(&mut self) -> Result<bool, Self::Error> {
|
||||||
self.toggle();
|
Ok(self._is_low())
|
||||||
Ok(())
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
//==================================================================================================
|
||||||
|
// Registers
|
||||||
|
//==================================================================================================
|
||||||
|
|
||||||
|
/// Provide a safe register interface for [`Pin`]s
|
||||||
|
///
|
||||||
|
/// This `struct` takes ownership of a [`PinId`] and provides an API to
|
||||||
|
/// access the corresponding registers.
|
||||||
|
pub(in crate::gpio) struct Registers<I: PinId> {
|
||||||
|
id: PhantomData<I>,
|
||||||
|
}
|
||||||
|
|
||||||
|
// [`Registers`] takes ownership of the [`PinId`], and [`Pin`] guarantees that
|
||||||
|
// each pin is a singleton, so this implementation is safe.
|
||||||
|
unsafe impl<I: PinId> RegisterInterface for Registers<I> {
|
||||||
|
#[inline]
|
||||||
|
fn id(&self) -> DynPinId {
|
||||||
|
I::DYN
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<I: PinId> Registers<I> {
|
||||||
|
/// Create a new instance of [`Registers`]
|
||||||
|
///
|
||||||
|
/// # Safety
|
||||||
|
///
|
||||||
|
/// Users must never create two simultaneous instances of this `struct` with
|
||||||
|
/// the same [`PinId`]
|
||||||
|
#[inline]
|
||||||
|
unsafe fn new() -> Self {
|
||||||
|
Registers { id: PhantomData }
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Provide a type-level equivalent for the
|
||||||
|
/// [`RegisterInterface::change_mode`] method.
|
||||||
|
#[inline]
|
||||||
|
pub(in crate::gpio) fn change_mode<M: PinMode>(&mut self) {
|
||||||
|
RegisterInterface::change_mode(self, M::DYN);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -690,8 +730,8 @@ macro_rules! pins {
|
|||||||
) => {
|
) => {
|
||||||
paste!(
|
paste!(
|
||||||
/// Collection of all the individual [`Pin`]s for a given port (PORTA or PORTB)
|
/// Collection of all the individual [`Pin`]s for a given port (PORTA or PORTB)
|
||||||
#[derive(Debug)]
|
|
||||||
pub struct $PinsName {
|
pub struct $PinsName {
|
||||||
|
iocfg: Option<va108xx::Ioconfig>,
|
||||||
port: $Port,
|
port: $Port,
|
||||||
$(
|
$(
|
||||||
#[doc = "Pin " $Id]
|
#[doc = "Pin " $Id]
|
||||||
@ -706,6 +746,7 @@ macro_rules! pins {
|
|||||||
#[inline]
|
#[inline]
|
||||||
pub fn new(
|
pub fn new(
|
||||||
syscfg: &mut va108xx::Sysconfig,
|
syscfg: &mut va108xx::Sysconfig,
|
||||||
|
iocfg: Option<va108xx::Ioconfig>,
|
||||||
port: $Port
|
port: $Port
|
||||||
) -> $PinsName {
|
) -> $PinsName {
|
||||||
syscfg.peripheral_clk_enable().modify(|_, w| {
|
syscfg.peripheral_clk_enable().modify(|_, w| {
|
||||||
@ -714,7 +755,7 @@ macro_rules! pins {
|
|||||||
w.ioconfig().set_bit()
|
w.ioconfig().set_bit()
|
||||||
});
|
});
|
||||||
$PinsName {
|
$PinsName {
|
||||||
//iocfg,
|
iocfg,
|
||||||
port,
|
port,
|
||||||
// Safe because we only create one `Pin` per `PinId`
|
// Safe because we only create one `Pin` per `PinId`
|
||||||
$(
|
$(
|
||||||
@ -731,8 +772,8 @@ macro_rules! pins {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// Consumes the Pins struct and returns the port definitions
|
/// Consumes the Pins struct and returns the port definitions
|
||||||
pub fn release(self) -> $Port {
|
pub fn release(self) -> (Option<va108xx::Ioconfig>, $Port) {
|
||||||
self.port
|
(self.iocfg, self.port)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
|
382
va108xx-hal/src/gpio/reg.rs
Normal file
382
va108xx-hal/src/gpio/reg.rs
Normal file
@ -0,0 +1,382 @@
|
|||||||
|
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;
|
||||||
|
/*
|
||||||
|
pub type IocfgPort = ioconfig::Porta;
|
||||||
|
#[repr(C)]
|
||||||
|
pub(super) struct IocfgPortGroup {
|
||||||
|
port: [IocfgPort; 32],
|
||||||
|
}
|
||||||
|
*/
|
||||||
|
|
||||||
|
/// 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(&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(&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()));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -18,48 +18,42 @@ const CLK_400K: Hertz = Hertz::from_raw(400_000);
|
|||||||
const MIN_CLK_400K: Hertz = Hertz::from_raw(8_000_000);
|
const MIN_CLK_400K: Hertz = Hertz::from_raw(8_000_000);
|
||||||
|
|
||||||
#[derive(Debug, PartialEq, Eq, Copy, Clone)]
|
#[derive(Debug, PartialEq, Eq, Copy, Clone)]
|
||||||
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
|
|
||||||
pub enum FifoEmptyMode {
|
pub enum FifoEmptyMode {
|
||||||
Stall = 0,
|
Stall = 0,
|
||||||
EndTransaction = 1,
|
EndTransaction = 1,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, PartialEq, Eq, thiserror::Error)]
|
#[derive(Debug, PartialEq, Eq)]
|
||||||
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
|
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
|
||||||
#[error("clock too slow for fast I2C mode")]
|
pub struct ClockTooSlowForFastI2c;
|
||||||
pub struct ClockTooSlowForFastI2cError;
|
|
||||||
|
|
||||||
#[derive(Debug, PartialEq, Eq, thiserror::Error)]
|
#[derive(Debug, PartialEq, Eq)]
|
||||||
#[error("invalid timing parameters")]
|
|
||||||
pub struct InvalidTimingParamsError;
|
|
||||||
|
|
||||||
#[derive(Debug, PartialEq, Eq, thiserror::Error)]
|
|
||||||
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
|
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
|
||||||
pub enum Error {
|
pub enum Error {
|
||||||
#[error("arbitration lost")]
|
InvalidTimingParams,
|
||||||
ArbitrationLost,
|
ArbitrationLost,
|
||||||
#[error("nack address")]
|
|
||||||
NackAddr,
|
NackAddr,
|
||||||
/// Data not acknowledged in write operation
|
/// Data not acknowledged in write operation
|
||||||
#[error("data not acknowledged in write operation")]
|
|
||||||
NackData,
|
NackData,
|
||||||
/// Not enough data received in read operation
|
/// Not enough data received in read operation
|
||||||
#[error("insufficient data received")]
|
|
||||||
InsufficientDataReceived,
|
InsufficientDataReceived,
|
||||||
/// Number of bytes in transfer too large (larger than 0x7fe)
|
/// Number of bytes in transfer too large (larger than 0x7fe)
|
||||||
#[error("data too large (larger than 0x7fe)")]
|
|
||||||
DataTooLarge,
|
DataTooLarge,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, PartialEq, Eq, thiserror::Error)]
|
#[derive(Debug, PartialEq, Eq)]
|
||||||
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
|
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
|
||||||
pub enum InitError {
|
pub enum InitError {
|
||||||
/// Wrong address used in constructor
|
/// Wrong address used in constructor
|
||||||
#[error("wrong address mode")]
|
|
||||||
WrongAddrMode,
|
WrongAddrMode,
|
||||||
/// APB1 clock is too slow for fast I2C mode.
|
/// APB1 clock is too slow for fast I2C mode.
|
||||||
#[error("clock too slow for fast I2C mode: {0}")]
|
ClkTooSlow(ClockTooSlowForFastI2c),
|
||||||
ClkTooSlow(#[from] ClockTooSlowForFastI2cError),
|
}
|
||||||
|
|
||||||
|
impl From<ClockTooSlowForFastI2c> for InitError {
|
||||||
|
fn from(value: ClockTooSlowForFastI2c) -> Self {
|
||||||
|
Self::ClkTooSlow(value)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl embedded_hal::i2c::Error for Error {
|
impl embedded_hal::i2c::Error for Error {
|
||||||
@ -72,7 +66,7 @@ impl embedded_hal::i2c::Error for Error {
|
|||||||
Error::NackData => {
|
Error::NackData => {
|
||||||
embedded_hal::i2c::ErrorKind::NoAcknowledge(i2c::NoAcknowledgeSource::Data)
|
embedded_hal::i2c::ErrorKind::NoAcknowledge(i2c::NoAcknowledgeSource::Data)
|
||||||
}
|
}
|
||||||
Error::DataTooLarge | Error::InsufficientDataReceived => {
|
Error::DataTooLarge | Error::InsufficientDataReceived | Error::InvalidTimingParams => {
|
||||||
embedded_hal::i2c::ErrorKind::Other
|
embedded_hal::i2c::ErrorKind::Other
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -80,7 +74,6 @@ impl embedded_hal::i2c::Error for Error {
|
|||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, PartialEq, Copy, Clone)]
|
#[derive(Debug, PartialEq, Copy, Clone)]
|
||||||
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
|
|
||||||
enum I2cCmd {
|
enum I2cCmd {
|
||||||
Start = 0b00,
|
Start = 0b00,
|
||||||
Stop = 0b10,
|
Stop = 0b10,
|
||||||
@ -89,21 +82,18 @@ enum I2cCmd {
|
|||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, PartialEq, Eq, Copy, Clone)]
|
#[derive(Debug, PartialEq, Eq, Copy, Clone)]
|
||||||
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
|
|
||||||
pub enum I2cSpeed {
|
pub enum I2cSpeed {
|
||||||
Regular100khz = 0,
|
Regular100khz = 0,
|
||||||
Fast400khz = 1,
|
Fast400khz = 1,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, PartialEq, Eq)]
|
#[derive(Debug, PartialEq, Eq)]
|
||||||
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
|
|
||||||
pub enum I2cDirection {
|
pub enum I2cDirection {
|
||||||
Send = 0,
|
Send = 0,
|
||||||
Read = 1,
|
Read = 1,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, PartialEq, Eq, Copy, Clone)]
|
#[derive(Debug, PartialEq, Eq, Copy, Clone)]
|
||||||
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
|
|
||||||
pub enum I2cAddress {
|
pub enum I2cAddress {
|
||||||
Regular(u8),
|
Regular(u8),
|
||||||
TenBit(u16),
|
TenBit(u16),
|
||||||
@ -144,12 +134,9 @@ impl Instance for pac::I2cb {
|
|||||||
// Config
|
// Config
|
||||||
//==================================================================================================
|
//==================================================================================================
|
||||||
|
|
||||||
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
|
|
||||||
pub struct TrTfThighTlow(u8, u8, u8, u8);
|
pub struct TrTfThighTlow(u8, u8, u8, u8);
|
||||||
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
|
|
||||||
pub struct TsuStoTsuStaThdStaTBuf(u8, u8, u8, u8);
|
pub struct TsuStoTsuStaThdStaTBuf(u8, u8, u8, u8);
|
||||||
|
|
||||||
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
|
|
||||||
pub struct TimingCfg {
|
pub struct TimingCfg {
|
||||||
// 4 bit max width
|
// 4 bit max width
|
||||||
tr: u8,
|
tr: u8,
|
||||||
@ -173,7 +160,7 @@ impl TimingCfg {
|
|||||||
pub fn new(
|
pub fn new(
|
||||||
first_16_bits: TrTfThighTlow,
|
first_16_bits: TrTfThighTlow,
|
||||||
second_16_bits: TsuStoTsuStaThdStaTBuf,
|
second_16_bits: TsuStoTsuStaThdStaTBuf,
|
||||||
) -> Result<Self, InvalidTimingParamsError> {
|
) -> Result<Self, Error> {
|
||||||
if first_16_bits.0 > 0xf
|
if first_16_bits.0 > 0xf
|
||||||
|| first_16_bits.1 > 0xf
|
|| first_16_bits.1 > 0xf
|
||||||
|| first_16_bits.2 > 0xf
|
|| first_16_bits.2 > 0xf
|
||||||
@ -183,7 +170,7 @@ impl TimingCfg {
|
|||||||
|| second_16_bits.2 > 0xf
|
|| second_16_bits.2 > 0xf
|
||||||
|| second_16_bits.3 > 0xf
|
|| second_16_bits.3 > 0xf
|
||||||
{
|
{
|
||||||
return Err(InvalidTimingParamsError);
|
return Err(Error::InvalidTimingParams);
|
||||||
}
|
}
|
||||||
Ok(TimingCfg {
|
Ok(TimingCfg {
|
||||||
tr: first_16_bits.0,
|
tr: first_16_bits.0,
|
||||||
@ -198,13 +185,13 @@ impl TimingCfg {
|
|||||||
}
|
}
|
||||||
|
|
||||||
pub fn reg(&self) -> u32 {
|
pub fn reg(&self) -> u32 {
|
||||||
((self.tbuf as u32) << 28)
|
(self.tbuf as u32) << 28
|
||||||
| ((self.thd_sta as u32) << 24)
|
| (self.thd_sta as u32) << 24
|
||||||
| ((self.tsu_sta as u32) << 20)
|
| (self.tsu_sta as u32) << 20
|
||||||
| ((self.tsu_sto as u32) << 16)
|
| (self.tsu_sto as u32) << 16
|
||||||
| ((self.tlow as u32) << 12)
|
| (self.tlow as u32) << 12
|
||||||
| ((self.thigh as u32) << 8)
|
| (self.thigh as u32) << 8
|
||||||
| ((self.tf as u32) << 4)
|
| (self.tf as u32) << 4
|
||||||
| (self.tr as u32)
|
| (self.tr as u32)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -224,7 +211,6 @@ impl Default for TimingCfg {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
|
|
||||||
pub struct MasterConfig {
|
pub struct MasterConfig {
|
||||||
pub tx_fe_mode: FifoEmptyMode,
|
pub tx_fe_mode: FifoEmptyMode,
|
||||||
pub rx_fe_mode: FifoEmptyMode,
|
pub rx_fe_mode: FifoEmptyMode,
|
||||||
@ -251,8 +237,6 @@ impl Default for MasterConfig {
|
|||||||
|
|
||||||
impl Sealed for MasterConfig {}
|
impl Sealed for MasterConfig {}
|
||||||
|
|
||||||
#[derive(Debug)]
|
|
||||||
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
|
|
||||||
pub struct SlaveConfig {
|
pub struct SlaveConfig {
|
||||||
pub tx_fe_mode: FifoEmptyMode,
|
pub tx_fe_mode: FifoEmptyMode,
|
||||||
pub rx_fe_mode: FifoEmptyMode,
|
pub rx_fe_mode: FifoEmptyMode,
|
||||||
@ -315,7 +299,7 @@ impl<I2c: Instance> I2cBase<I2c> {
|
|||||||
speed_mode: I2cSpeed,
|
speed_mode: I2cSpeed,
|
||||||
ms_cfg: Option<&MasterConfig>,
|
ms_cfg: Option<&MasterConfig>,
|
||||||
sl_cfg: Option<&SlaveConfig>,
|
sl_cfg: Option<&SlaveConfig>,
|
||||||
) -> Result<Self, ClockTooSlowForFastI2cError> {
|
) -> Result<Self, ClockTooSlowForFastI2c> {
|
||||||
enable_peripheral_clock(syscfg, I2c::PERIPH_SEL);
|
enable_peripheral_clock(syscfg, I2c::PERIPH_SEL);
|
||||||
|
|
||||||
let mut i2c_base = I2cBase {
|
let mut i2c_base = I2cBase {
|
||||||
@ -376,7 +360,7 @@ impl<I2c: Instance> I2cBase<I2c> {
|
|||||||
if let Some(max_words) = max_words {
|
if let Some(max_words) = max_words {
|
||||||
self.i2c
|
self.i2c
|
||||||
.s0_maxwords()
|
.s0_maxwords()
|
||||||
.write(|w| unsafe { w.bits((1 << 31) | max_words as u32) });
|
.write(|w| unsafe { w.bits(1 << 31 | max_words as u32) });
|
||||||
}
|
}
|
||||||
let (addr, addr_mode_mask) = Self::unwrap_addr(sl_cfg.addr);
|
let (addr, addr_mode_mask) = Self::unwrap_addr(sl_cfg.addr);
|
||||||
// The first bit is the read/write value. Normally, both read and write are matched
|
// The first bit is the read/write value. Normally, both read and write are matched
|
||||||
@ -393,12 +377,12 @@ impl<I2c: Instance> I2cBase<I2c> {
|
|||||||
let (addr, addr_mode_mask) = Self::unwrap_addr(addr_b);
|
let (addr, addr_mode_mask) = Self::unwrap_addr(addr_b);
|
||||||
self.i2c
|
self.i2c
|
||||||
.s0_addressb()
|
.s0_addressb()
|
||||||
.write(|w| unsafe { w.bits((addr << 1) as u32 | addr_mode_mask) });
|
.write(|w| unsafe { w.bits((addr << 1) as u32 | addr_mode_mask) })
|
||||||
}
|
}
|
||||||
if let Some(addr_b_mask) = sl_cfg.addr_b_mask {
|
if let Some(addr_b_mask) = sl_cfg.addr_b_mask {
|
||||||
self.i2c
|
self.i2c
|
||||||
.s0_addressmaskb()
|
.s0_addressmaskb()
|
||||||
.write(|w| unsafe { w.bits((addr_b_mask << 1) as u32) });
|
.write(|w| unsafe { w.bits((addr_b_mask << 1) as u32) })
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -418,26 +402,23 @@ impl<I2c: Instance> I2cBase<I2c> {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
fn calc_clk_div(&self, speed_mode: I2cSpeed) -> Result<u8, ClockTooSlowForFastI2cError> {
|
fn calc_clk_div(&self, speed_mode: I2cSpeed) -> Result<u8, ClockTooSlowForFastI2c> {
|
||||||
if speed_mode == I2cSpeed::Regular100khz {
|
if speed_mode == I2cSpeed::Regular100khz {
|
||||||
Ok(((self.sys_clk.raw() / CLK_100K.raw() / 20) - 1) as u8)
|
Ok(((self.sys_clk.raw() / CLK_100K.raw() / 20) - 1) as u8)
|
||||||
} else {
|
} else {
|
||||||
if self.sys_clk.raw() < MIN_CLK_400K.raw() {
|
if self.sys_clk.raw() < MIN_CLK_400K.raw() {
|
||||||
return Err(ClockTooSlowForFastI2cError);
|
return Err(ClockTooSlowForFastI2c);
|
||||||
}
|
}
|
||||||
Ok(((self.sys_clk.raw() / CLK_400K.raw() / 25) - 1) as u8)
|
Ok(((self.sys_clk.raw() / CLK_400K.raw() / 25) - 1) as u8)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Configures the clock scale for a given speed mode setting
|
/// Configures the clock scale for a given speed mode setting
|
||||||
pub fn cfg_clk_scale(
|
pub fn cfg_clk_scale(&mut self, speed_mode: I2cSpeed) -> Result<(), ClockTooSlowForFastI2c> {
|
||||||
&mut self,
|
|
||||||
speed_mode: I2cSpeed,
|
|
||||||
) -> Result<(), ClockTooSlowForFastI2cError> {
|
|
||||||
let clk_div = self.calc_clk_div(speed_mode)?;
|
let clk_div = self.calc_clk_div(speed_mode)?;
|
||||||
self.i2c
|
self.i2c
|
||||||
.clkscale()
|
.clkscale()
|
||||||
.write(|w| unsafe { w.bits(((speed_mode as u32) << 31) | clk_div as u32) });
|
.write(|w| unsafe { w.bits((speed_mode as u32) << 31 | clk_div as u32) });
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -456,6 +437,13 @@ impl<I2c: Instance> I2cBase<I2c> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Unique mode to use the loopback functionality
|
||||||
|
// pub struct I2cLoopback<I2C> {
|
||||||
|
// i2c_base: I2cBase<I2C>,
|
||||||
|
// master_cfg: MasterConfig,
|
||||||
|
// slave_cfg: SlaveConfig,
|
||||||
|
// }
|
||||||
|
|
||||||
//==================================================================================================
|
//==================================================================================================
|
||||||
// I2C Master
|
// I2C Master
|
||||||
//==================================================================================================
|
//==================================================================================================
|
||||||
@ -472,7 +460,7 @@ impl<I2c: Instance, Addr> I2cMaster<I2c, Addr> {
|
|||||||
i2c: I2c,
|
i2c: I2c,
|
||||||
cfg: MasterConfig,
|
cfg: MasterConfig,
|
||||||
speed_mode: I2cSpeed,
|
speed_mode: I2cSpeed,
|
||||||
) -> Result<Self, ClockTooSlowForFastI2cError> {
|
) -> Result<Self, ClockTooSlowForFastI2c> {
|
||||||
Ok(I2cMaster {
|
Ok(I2cMaster {
|
||||||
i2c_base: I2cBase::new(syscfg, sysclk, i2c, speed_mode, Some(&cfg), None)?,
|
i2c_base: I2cBase::new(syscfg, sysclk, i2c, speed_mode, Some(&cfg), None)?,
|
||||||
addr: PhantomData,
|
addr: PhantomData,
|
||||||
@ -667,6 +655,275 @@ impl<I2c: Instance, Addr> I2cMaster<I2c, Addr> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
macro_rules! i2c_master {
|
||||||
|
($($I2CX:path: ($i2cx:ident, $clk_enb:path),)+) => {
|
||||||
|
$(
|
||||||
|
impl<ADDR> I2cMaster<$I2CX, ADDR> {
|
||||||
|
pub fn $i2cx(
|
||||||
|
i2c: $I2CX,
|
||||||
|
cfg: MasterConfig,
|
||||||
|
sys_clk: impl Into<Hertz> + Copy,
|
||||||
|
speed_mode: I2cSpeed,
|
||||||
|
sys_cfg: Option<&mut pac::Sysconfig>,
|
||||||
|
) -> Self {
|
||||||
|
I2cMaster {
|
||||||
|
i2c_base: I2cBase::$i2cx(
|
||||||
|
i2c,
|
||||||
|
sys_clk,
|
||||||
|
speed_mode,
|
||||||
|
Some(&cfg),
|
||||||
|
None,
|
||||||
|
sys_cfg
|
||||||
|
),
|
||||||
|
_addr: PhantomData,
|
||||||
|
}
|
||||||
|
.enable_master()
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
pub fn cancel_transfer(&self) {
|
||||||
|
self.i2c_base
|
||||||
|
.i2c
|
||||||
|
.cmd()
|
||||||
|
.write(|w| unsafe { w.bits(I2cCmd::Cancel as u32) });
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
pub fn clear_tx_fifo(&self) {
|
||||||
|
self.i2c_base.i2c.fifo_clr().write(|w| w.txfifo().set_bit());
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
pub fn clear_rx_fifo(&self) {
|
||||||
|
self.i2c_base.i2c.fifo_clr().write(|w| w.rxfifo().set_bit());
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
pub fn enable_master(self) -> Self {
|
||||||
|
self.i2c_base.i2c.ctrl().modify(|_, w| w.enable().set_bit());
|
||||||
|
self
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
pub fn disable_master(self) -> Self {
|
||||||
|
self.i2c_base.i2c.ctrl().modify(|_, w| w.enable().clear_bit());
|
||||||
|
self
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline(always)]
|
||||||
|
fn load_fifo(&self, word: u8) {
|
||||||
|
self.i2c_base
|
||||||
|
.i2c
|
||||||
|
.data()
|
||||||
|
.write(|w| unsafe { w.bits(word as u32) });
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline(always)]
|
||||||
|
fn read_fifo(&self) -> u8 {
|
||||||
|
self.i2c_base.i2c.data().read().bits() as u8
|
||||||
|
}
|
||||||
|
|
||||||
|
fn error_handler_write(&mut self, init_cmd: &I2cCmd) {
|
||||||
|
self.clear_tx_fifo();
|
||||||
|
if *init_cmd == I2cCmd::Start {
|
||||||
|
self.i2c_base.stop_cmd()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn write_base(
|
||||||
|
&mut self,
|
||||||
|
addr: I2cAddress,
|
||||||
|
init_cmd: I2cCmd,
|
||||||
|
bytes: impl IntoIterator<Item = u8>,
|
||||||
|
) -> Result<(), Error> {
|
||||||
|
let mut iter = bytes.into_iter();
|
||||||
|
// Load address
|
||||||
|
let (addr, addr_mode_bit) = I2cBase::<$I2CX>::unwrap_addr(addr);
|
||||||
|
self.i2c_base.i2c.address().write(|w| unsafe {
|
||||||
|
w.bits(I2cDirection::Send as u32 | (addr << 1) as u32 | addr_mode_bit)
|
||||||
|
});
|
||||||
|
|
||||||
|
self.i2c_base
|
||||||
|
.i2c
|
||||||
|
.cmd()
|
||||||
|
.write(|w| unsafe { w.bits(init_cmd as u32) });
|
||||||
|
let mut load_if_next_available = || {
|
||||||
|
if let Some(next_byte) = iter.next() {
|
||||||
|
self.load_fifo(next_byte);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
loop {
|
||||||
|
let status_reader = self.i2c_base.i2c.status().read();
|
||||||
|
if status_reader.arblost().bit_is_set() {
|
||||||
|
self.error_handler_write(&init_cmd);
|
||||||
|
return Err(Error::ArbitrationLost);
|
||||||
|
} else if status_reader.nackaddr().bit_is_set() {
|
||||||
|
self.error_handler_write(&init_cmd);
|
||||||
|
return Err(Error::NackAddr);
|
||||||
|
} else if status_reader.nackdata().bit_is_set() {
|
||||||
|
self.error_handler_write(&init_cmd);
|
||||||
|
return Err(Error::NackData);
|
||||||
|
} else if status_reader.idle().bit_is_set() {
|
||||||
|
return Ok(());
|
||||||
|
} else {
|
||||||
|
while !status_reader.txnfull().bit_is_set() {
|
||||||
|
load_if_next_available();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn write_from_buffer(
|
||||||
|
&mut self,
|
||||||
|
init_cmd: I2cCmd,
|
||||||
|
addr: I2cAddress,
|
||||||
|
output: &[u8],
|
||||||
|
) -> Result<(), Error> {
|
||||||
|
let len = output.len();
|
||||||
|
// It should theoretically possible to transfer larger data sizes by tracking
|
||||||
|
// the number of sent words and setting it to 0x7fe as soon as only that many
|
||||||
|
// bytes are remaining. However, large transfer like this are not common. This
|
||||||
|
// feature will therefore not be supported for now.
|
||||||
|
if len > 0x7fe {
|
||||||
|
return Err(Error::DataTooLarge);
|
||||||
|
}
|
||||||
|
// Load number of words
|
||||||
|
self.i2c_base
|
||||||
|
.i2c
|
||||||
|
.words()
|
||||||
|
.write(|w| unsafe { w.bits(len as u32) });
|
||||||
|
let mut bytes = output.iter();
|
||||||
|
// FIFO has a depth of 16. We load slightly above the trigger level
|
||||||
|
// but not all of it because the transaction might fail immediately
|
||||||
|
const FILL_DEPTH: usize = 12;
|
||||||
|
|
||||||
|
// load the FIFO
|
||||||
|
for _ in 0..core::cmp::min(FILL_DEPTH, len) {
|
||||||
|
self.load_fifo(*bytes.next().unwrap());
|
||||||
|
}
|
||||||
|
|
||||||
|
self.write_base(addr, init_cmd, output.iter().cloned())
|
||||||
|
}
|
||||||
|
|
||||||
|
fn read_internal(&mut self, addr: I2cAddress, buffer: &mut [u8]) -> Result<(), Error> {
|
||||||
|
let len = buffer.len();
|
||||||
|
// It should theoretically possible to transfer larger data sizes by tracking
|
||||||
|
// the number of sent words and setting it to 0x7fe as soon as only that many
|
||||||
|
// bytes are remaining. However, large transfer like this are not common. This
|
||||||
|
// feature will therefore not be supported for now.
|
||||||
|
if len > 0x7fe {
|
||||||
|
return Err(Error::DataTooLarge);
|
||||||
|
}
|
||||||
|
// Clear the receive FIFO
|
||||||
|
self.clear_rx_fifo();
|
||||||
|
|
||||||
|
// Load number of words
|
||||||
|
self.i2c_base
|
||||||
|
.i2c
|
||||||
|
.words()
|
||||||
|
.write(|w| unsafe { w.bits(len as u32) });
|
||||||
|
let (addr, addr_mode_bit) = match addr {
|
||||||
|
I2cAddress::Regular(addr) => (addr as u16, 0 << 15),
|
||||||
|
I2cAddress::TenBit(addr) => (addr, 1 << 15),
|
||||||
|
};
|
||||||
|
// Load address
|
||||||
|
self.i2c_base.i2c.address().write(|w| unsafe {
|
||||||
|
w.bits(I2cDirection::Read as u32 | (addr << 1) as u32 | addr_mode_bit)
|
||||||
|
});
|
||||||
|
|
||||||
|
let mut buf_iter = buffer.iter_mut();
|
||||||
|
let mut read_bytes = 0;
|
||||||
|
// Start receive transfer
|
||||||
|
self.i2c_base
|
||||||
|
.i2c
|
||||||
|
.cmd()
|
||||||
|
.write(|w| unsafe { w.bits(I2cCmd::StartWithStop as u32) });
|
||||||
|
let mut read_if_next_available = || {
|
||||||
|
if let Some(next_byte) = buf_iter.next() {
|
||||||
|
*next_byte = self.read_fifo();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
loop {
|
||||||
|
let status_reader = self.i2c_base.i2c.status().read();
|
||||||
|
if status_reader.arblost().bit_is_set() {
|
||||||
|
self.clear_rx_fifo();
|
||||||
|
return Err(Error::ArbitrationLost);
|
||||||
|
} else if status_reader.nackaddr().bit_is_set() {
|
||||||
|
self.clear_rx_fifo();
|
||||||
|
return Err(Error::NackAddr);
|
||||||
|
} else if status_reader.idle().bit_is_set() {
|
||||||
|
if read_bytes != len {
|
||||||
|
return Err(Error::InsufficientDataReceived);
|
||||||
|
}
|
||||||
|
return Ok(());
|
||||||
|
} else if status_reader.rxnempty().bit_is_set() {
|
||||||
|
read_if_next_available();
|
||||||
|
read_bytes += 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
//======================================================================================
|
||||||
|
// Embedded HAL I2C implementations
|
||||||
|
//======================================================================================
|
||||||
|
|
||||||
|
impl embedded_hal::i2c::ErrorType for I2cMaster<$I2CX, SevenBitAddress> {
|
||||||
|
type Error = Error;
|
||||||
|
}
|
||||||
|
impl embedded_hal::i2c::I2c for I2cMaster<$I2CX, SevenBitAddress> {
|
||||||
|
fn transaction(
|
||||||
|
&mut self,
|
||||||
|
address: SevenBitAddress,
|
||||||
|
operations: &mut [Operation<'_>],
|
||||||
|
) -> Result<(), Self::Error> {
|
||||||
|
for operation in operations {
|
||||||
|
match operation {
|
||||||
|
Operation::Read(buf) => self.read_internal(I2cAddress::Regular(address), buf)?,
|
||||||
|
Operation::Write(buf) => self.write_from_buffer(
|
||||||
|
I2cCmd::StartWithStop,
|
||||||
|
I2cAddress::Regular(address),
|
||||||
|
buf,
|
||||||
|
)?,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl embedded_hal::i2c::ErrorType for I2cMaster<$I2CX, TenBitAddress> {
|
||||||
|
type Error = Error;
|
||||||
|
}
|
||||||
|
impl embedded_hal::i2c::I2c<TenBitAddress> for I2cMaster<$I2CX, TenBitAddress> {
|
||||||
|
fn transaction(
|
||||||
|
&mut self,
|
||||||
|
address: TenBitAddress,
|
||||||
|
operations: &mut [Operation<'_>],
|
||||||
|
) -> Result<(), Self::Error> {
|
||||||
|
for operation in operations {
|
||||||
|
match operation {
|
||||||
|
Operation::Read(buf) => self.read_internal(I2cAddress::TenBit(address), buf)?,
|
||||||
|
Operation::Write(buf) => self.write_from_buffer(
|
||||||
|
I2cCmd::StartWithStop,
|
||||||
|
I2cAddress::TenBit(address),
|
||||||
|
buf,
|
||||||
|
)?,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
)+
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
i2c_master!(
|
||||||
|
pac::I2ca: (i2ca, PeripheralClocks::I2c0),
|
||||||
|
pac::I2cb: (i2cb, PeripheralClocks::I2c1),
|
||||||
|
);
|
||||||
|
*/
|
||||||
|
|
||||||
//======================================================================================
|
//======================================================================================
|
||||||
// Embedded HAL I2C implementations
|
// Embedded HAL I2C implementations
|
||||||
//======================================================================================
|
//======================================================================================
|
||||||
@ -733,7 +990,7 @@ impl<I2c: Instance, Addr> I2cSlave<I2c, Addr> {
|
|||||||
i2c: I2c,
|
i2c: I2c,
|
||||||
cfg: SlaveConfig,
|
cfg: SlaveConfig,
|
||||||
speed_mode: I2cSpeed,
|
speed_mode: I2cSpeed,
|
||||||
) -> Result<Self, ClockTooSlowForFastI2cError> {
|
) -> Result<Self, ClockTooSlowForFastI2c> {
|
||||||
Ok(I2cSlave {
|
Ok(I2cSlave {
|
||||||
i2c_base: I2cBase::new(sys_cfg, sys_clk, i2c, speed_mode, None, Some(&cfg))?,
|
i2c_base: I2cBase::new(sys_cfg, sys_clk, i2c, speed_mode, None, Some(&cfg))?,
|
||||||
addr: PhantomData,
|
addr: PhantomData,
|
||||||
@ -895,7 +1152,7 @@ impl<I2c: Instance> I2cSlave<I2c, TenBitAddress> {
|
|||||||
i2c: I2c,
|
i2c: I2c,
|
||||||
cfg: SlaveConfig,
|
cfg: SlaveConfig,
|
||||||
speed_mode: I2cSpeed,
|
speed_mode: I2cSpeed,
|
||||||
) -> Result<Self, ClockTooSlowForFastI2cError> {
|
) -> Result<Self, ClockTooSlowForFastI2c> {
|
||||||
Self::new_generic(sys_cfg, sys_clk, i2c, cfg, speed_mode)
|
Self::new_generic(sys_cfg, sys_clk, i2c, cfg, speed_mode)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,7 +1,6 @@
|
|||||||
#![no_std]
|
#![no_std]
|
||||||
#![cfg_attr(docsrs, feature(doc_auto_cfg))]
|
#![cfg_attr(docsrs, feature(doc_auto_cfg))]
|
||||||
|
|
||||||
use gpio::Port;
|
|
||||||
pub use va108xx;
|
pub use va108xx;
|
||||||
pub use va108xx as pac;
|
pub use va108xx as pac;
|
||||||
|
|
||||||
@ -18,16 +17,19 @@ pub mod typelevel;
|
|||||||
pub mod uart;
|
pub mod uart;
|
||||||
|
|
||||||
#[derive(Debug, Eq, Copy, Clone, PartialEq)]
|
#[derive(Debug, Eq, Copy, Clone, PartialEq)]
|
||||||
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
|
|
||||||
pub enum FunSel {
|
pub enum FunSel {
|
||||||
Sel0 = 0b00,
|
|
||||||
Sel1 = 0b01,
|
Sel1 = 0b01,
|
||||||
Sel2 = 0b10,
|
Sel2 = 0b10,
|
||||||
Sel3 = 0b11,
|
Sel3 = 0b11,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Copy, Clone, PartialEq, Eq)]
|
#[derive(Debug, Copy, Clone, PartialEq, Eq)]
|
||||||
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
|
pub enum PortSel {
|
||||||
|
PortA,
|
||||||
|
PortB,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Copy, Clone, PartialEq, Eq)]
|
||||||
pub enum PeripheralSelect {
|
pub enum PeripheralSelect {
|
||||||
PortA = 0,
|
PortA = 0,
|
||||||
PortB = 1,
|
PortB = 1,
|
||||||
@ -44,61 +46,56 @@ pub enum PeripheralSelect {
|
|||||||
Gpio = 24,
|
Gpio = 24,
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Generic interrupt config which can be used to specify whether the HAL driver will
|
/// Generic IRQ config which can be used to specify whether the HAL driver will
|
||||||
/// use the IRQSEL register to route an interrupt, and whether the IRQ will be unmasked in the
|
/// use the IRQSEL register to route an interrupt, and whether the IRQ will be unmasked in the
|
||||||
/// Cortex-M0 NVIC. Both are generally necessary for IRQs to work, but the user might want to
|
/// Cortex-M0 NVIC. Both are generally necessary for IRQs to work, but the user might perform
|
||||||
/// perform those steps themselves.
|
/// this steps themselves
|
||||||
#[derive(Debug, PartialEq, Eq, Clone, Copy)]
|
#[derive(Debug, PartialEq, Eq, Clone, Copy)]
|
||||||
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
|
pub struct IrqCfg {
|
||||||
pub struct InterruptConfig {
|
|
||||||
/// Interrupt target vector. Should always be set, might be required for disabling IRQs
|
/// Interrupt target vector. Should always be set, might be required for disabling IRQs
|
||||||
pub id: pac::Interrupt,
|
pub irq: pac::Interrupt,
|
||||||
/// Specfiy whether IRQ should be routed to an IRQ vector using the IRQSEL peripheral.
|
/// Specfiy whether IRQ should be routed to an IRQ vector using the IRQSEL peripheral
|
||||||
pub route: bool,
|
pub route: bool,
|
||||||
/// Specify whether the IRQ is unmasked in the Cortex-M NVIC. If an interrupt is used for
|
/// Specify whether the IRQ is unmasked in the Cortex-M NVIC
|
||||||
/// multiple purposes, the user can enable the interrupts themselves.
|
pub enable: bool,
|
||||||
pub enable_in_nvic: bool,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
impl InterruptConfig {
|
impl IrqCfg {
|
||||||
pub fn new(id: pac::Interrupt, route: bool, enable_in_nvic: bool) -> Self {
|
pub fn new(irq: pac::Interrupt, route: bool, enable: bool) -> Self {
|
||||||
InterruptConfig {
|
IrqCfg { irq, route, enable }
|
||||||
id,
|
|
||||||
route,
|
|
||||||
enable_in_nvic,
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub type IrqCfg = InterruptConfig;
|
#[derive(Debug, PartialEq, Eq)]
|
||||||
|
pub struct InvalidPin(pub(crate) ());
|
||||||
|
|
||||||
#[derive(Debug, PartialEq, Eq, thiserror::Error)]
|
/// Can be used to manually manipulate the function select of port pins
|
||||||
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
|
pub fn port_mux(
|
||||||
#[error("invalid pin with number {0}")]
|
|
||||||
pub struct InvalidPinError(u8);
|
|
||||||
|
|
||||||
/// 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,
|
ioconfig: &mut pac::Ioconfig,
|
||||||
port: Port,
|
port: PortSel,
|
||||||
pin: u8,
|
pin: u8,
|
||||||
funsel: FunSel,
|
funsel: FunSel,
|
||||||
) -> Result<(), InvalidPinError> {
|
) -> Result<(), InvalidPin> {
|
||||||
if (port == Port::A && pin >= 32) || (port == Port::B && pin >= 24) {
|
match port {
|
||||||
return Err(InvalidPinError(pin));
|
PortSel::PortA => {
|
||||||
|
if pin > 31 {
|
||||||
|
return Err(InvalidPin(()));
|
||||||
|
}
|
||||||
|
ioconfig
|
||||||
|
.porta(pin as usize)
|
||||||
|
.modify(|_, w| unsafe { w.funsel().bits(funsel as u8) });
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
PortSel::PortB => {
|
||||||
|
if pin > 23 {
|
||||||
|
return Err(InvalidPin(()));
|
||||||
|
}
|
||||||
|
ioconfig
|
||||||
|
.portb0(pin as usize)
|
||||||
|
.modify(|_, w| unsafe { w.funsel().bits(funsel as u8) });
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
let reg_block = match port {
|
|
||||||
Port::A => ioconfig.porta(pin as usize),
|
|
||||||
Port::B => ioconfig.portb0(pin as usize),
|
|
||||||
};
|
|
||||||
|
|
||||||
reg_block.modify(|_, w| unsafe { w.funsel().bits(funsel as u8) });
|
|
||||||
Ok(())
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Enable a specific interrupt using the NVIC peripheral.
|
/// Enable a specific interrupt using the NVIC peripheral.
|
||||||
@ -107,7 +104,7 @@ pub fn port_function_select(
|
|||||||
///
|
///
|
||||||
/// This function is `unsafe` because it can break mask-based critical sections.
|
/// This function is `unsafe` because it can break mask-based critical sections.
|
||||||
#[inline]
|
#[inline]
|
||||||
pub unsafe fn enable_nvic_interrupt(irq: pac::Interrupt) {
|
pub unsafe fn enable_interrupt(irq: pac::Interrupt) {
|
||||||
unsafe {
|
unsafe {
|
||||||
cortex_m::peripheral::NVIC::unmask(irq);
|
cortex_m::peripheral::NVIC::unmask(irq);
|
||||||
}
|
}
|
||||||
@ -115,6 +112,6 @@ pub unsafe fn enable_nvic_interrupt(irq: pac::Interrupt) {
|
|||||||
|
|
||||||
/// Disable a specific interrupt using the NVIC peripheral.
|
/// Disable a specific interrupt using the NVIC peripheral.
|
||||||
#[inline]
|
#[inline]
|
||||||
pub fn disable_nvic_interrupt(irq: pac::Interrupt) {
|
pub fn disable_interrupt(irq: pac::Interrupt) {
|
||||||
cortex_m::peripheral::NVIC::mask(irq);
|
cortex_m::peripheral::NVIC::mask(irq);
|
||||||
}
|
}
|
||||||
|
@ -10,14 +10,14 @@ use core::marker::PhantomData;
|
|||||||
|
|
||||||
use crate::pac;
|
use crate::pac;
|
||||||
use crate::time::Hertz;
|
use crate::time::Hertz;
|
||||||
use crate::timer::{TimDynRegister, TimPin, TimRegInterface, ValidTim, ValidTimAndPin};
|
use crate::timer::{
|
||||||
|
TimAndPinRegister, TimDynRegister, TimPin, TimRegInterface, ValidTim, ValidTimAndPin,
|
||||||
|
};
|
||||||
use crate::{clock::enable_peripheral_clock, gpio::DynPinId};
|
use crate::{clock::enable_peripheral_clock, gpio::DynPinId};
|
||||||
|
|
||||||
const DUTY_MAX: u16 = u16::MAX;
|
const DUTY_MAX: u16 = u16::MAX;
|
||||||
|
|
||||||
#[derive(Debug)]
|
pub struct PwmBase {
|
||||||
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
|
|
||||||
pub(crate) struct PwmCommon {
|
|
||||||
sys_clk: Hertz,
|
sys_clk: Hertz,
|
||||||
/// For PWMB, this is the upper limit
|
/// For PWMB, this is the upper limit
|
||||||
current_duty: u16,
|
current_duty: u16,
|
||||||
@ -35,13 +35,123 @@ enum StatusSelPwm {
|
|||||||
pub struct PwmA {}
|
pub struct PwmA {}
|
||||||
pub struct PwmB {}
|
pub struct PwmB {}
|
||||||
|
|
||||||
|
//==================================================================================================
|
||||||
|
// Common
|
||||||
|
//==================================================================================================
|
||||||
|
|
||||||
|
macro_rules! pwm_common_func {
|
||||||
|
() => {
|
||||||
|
#[inline]
|
||||||
|
fn enable_pwm_a(&mut self) {
|
||||||
|
self.reg
|
||||||
|
.reg()
|
||||||
|
.ctrl()
|
||||||
|
.modify(|_, w| unsafe { w.status_sel().bits(StatusSelPwm::PwmA as u8) });
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
fn enable_pwm_b(&mut self) {
|
||||||
|
self.reg
|
||||||
|
.reg()
|
||||||
|
.ctrl()
|
||||||
|
.modify(|_, w| unsafe { w.status_sel().bits(StatusSelPwm::PwmB as u8) });
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
pub fn get_period(&self) -> Hertz {
|
||||||
|
self.pwm_base.current_period
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
pub fn set_period(&mut self, period: impl Into<Hertz>) {
|
||||||
|
self.pwm_base.current_period = period.into();
|
||||||
|
// Avoid division by 0
|
||||||
|
if self.pwm_base.current_period.raw() == 0 {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
self.pwm_base.current_rst_val =
|
||||||
|
self.pwm_base.sys_clk.raw() / self.pwm_base.current_period.raw();
|
||||||
|
self.reg
|
||||||
|
.reg()
|
||||||
|
.rst_value()
|
||||||
|
.write(|w| unsafe { w.bits(self.pwm_base.current_rst_val) });
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
pub fn disable(&mut self) {
|
||||||
|
self.reg.reg().ctrl().modify(|_, w| w.enable().clear_bit());
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
pub fn enable(&mut self) {
|
||||||
|
self.reg.reg().ctrl().modify(|_, w| w.enable().set_bit());
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
pub fn period(&self) -> Hertz {
|
||||||
|
self.pwm_base.current_period
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline(always)]
|
||||||
|
pub fn duty(&self) -> u16 {
|
||||||
|
self.pwm_base.current_duty
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
macro_rules! pwmb_func {
|
||||||
|
() => {
|
||||||
|
pub fn pwmb_lower_limit(&self) -> u16 {
|
||||||
|
self.pwm_base.current_lower_limit
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn pwmb_upper_limit(&self) -> u16 {
|
||||||
|
self.pwm_base.current_duty
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Set the lower limit for PWMB
|
||||||
|
///
|
||||||
|
/// The PWM signal will be 1 as long as the current RST counter is larger than
|
||||||
|
/// the lower limit. For example, with a lower limit of 0.5 and and an upper limit
|
||||||
|
/// of 0.7, Only a fixed period between 0.5 * period and 0.7 * period will be in a high
|
||||||
|
/// state
|
||||||
|
pub fn set_pwmb_lower_limit(&mut self, duty: u16) {
|
||||||
|
self.pwm_base.current_lower_limit = duty;
|
||||||
|
let pwmb_val: u64 = (self.pwm_base.current_rst_val as u64
|
||||||
|
* self.pwm_base.current_lower_limit as u64)
|
||||||
|
/ DUTY_MAX as u64;
|
||||||
|
self.reg
|
||||||
|
.reg()
|
||||||
|
.pwmb_value()
|
||||||
|
.write(|w| unsafe { w.bits(pwmb_val as u32) });
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Set the higher limit for PWMB
|
||||||
|
///
|
||||||
|
/// The PWM signal will be 1 as long as the current RST counter is smaller than
|
||||||
|
/// the higher limit. For example, with a lower limit of 0.5 and and an upper limit
|
||||||
|
/// of 0.7, Only a fixed period between 0.5 * period and 0.7 * period will be in a high
|
||||||
|
/// state
|
||||||
|
pub fn set_pwmb_upper_limit(&mut self, duty: u16) {
|
||||||
|
self.pwm_base.current_duty = duty;
|
||||||
|
let pwma_val: u64 = (self.pwm_base.current_rst_val as u64
|
||||||
|
* self.pwm_base.current_duty as u64)
|
||||||
|
/ DUTY_MAX as u64;
|
||||||
|
self.reg
|
||||||
|
.reg()
|
||||||
|
.pwma_value()
|
||||||
|
.write(|w| unsafe { w.bits(pwma_val as u32) });
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
//==================================================================================================
|
//==================================================================================================
|
||||||
// Strongly typed PWM pin
|
// Strongly typed PWM pin
|
||||||
//==================================================================================================
|
//==================================================================================================
|
||||||
|
|
||||||
pub struct PwmPin<Pin: TimPin, Tim: ValidTim, Mode = PwmA> {
|
pub struct PwmPin<Pin: TimPin, Tim: ValidTim, Mode = PwmA> {
|
||||||
pin_and_tim: (Pin, Tim),
|
reg: TimAndPinRegister<Pin, Tim>,
|
||||||
inner: ReducedPwmPin<Mode>,
|
pwm_base: PwmBase,
|
||||||
mode: PhantomData<Mode>,
|
mode: PhantomData<Mode>,
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -53,82 +163,34 @@ where
|
|||||||
pub fn new(
|
pub fn new(
|
||||||
sys_cfg: &mut pac::Sysconfig,
|
sys_cfg: &mut pac::Sysconfig,
|
||||||
sys_clk: impl Into<Hertz> + Copy,
|
sys_clk: impl Into<Hertz> + Copy,
|
||||||
pin_and_tim: (Pin, Tim),
|
tim_and_pin: (Pin, Tim),
|
||||||
initial_period: impl Into<Hertz> + Copy,
|
initial_period: impl Into<Hertz> + Copy,
|
||||||
) -> Self {
|
) -> Self {
|
||||||
let mut pin = PwmPin {
|
let mut pin = PwmPin {
|
||||||
pin_and_tim,
|
pwm_base: PwmBase {
|
||||||
inner: ReducedPwmPin::<Mode>::new(
|
current_duty: 0,
|
||||||
Tim::TIM_ID,
|
current_lower_limit: 0,
|
||||||
Pin::DYN,
|
current_period: initial_period.into(),
|
||||||
PwmCommon {
|
current_rst_val: 0,
|
||||||
current_duty: 0,
|
sys_clk: sys_clk.into(),
|
||||||
current_lower_limit: 0,
|
},
|
||||||
current_period: initial_period.into(),
|
reg: unsafe { TimAndPinRegister::new(tim_and_pin.0, tim_and_pin.1) },
|
||||||
current_rst_val: 0,
|
|
||||||
sys_clk: sys_clk.into(),
|
|
||||||
},
|
|
||||||
),
|
|
||||||
//unsafe { TimAndPin::new(tim_and_pin.0, tim_and_pin.1) },
|
|
||||||
mode: PhantomData,
|
mode: PhantomData,
|
||||||
};
|
};
|
||||||
enable_peripheral_clock(sys_cfg, crate::clock::PeripheralClocks::Gpio);
|
enable_peripheral_clock(sys_cfg, crate::clock::PeripheralClocks::Gpio);
|
||||||
enable_peripheral_clock(sys_cfg, crate::clock::PeripheralClocks::Ioconfig);
|
enable_peripheral_clock(sys_cfg, crate::clock::PeripheralClocks::Ioconfig);
|
||||||
sys_cfg
|
sys_cfg
|
||||||
.tim_clk_enable()
|
.tim_clk_enable()
|
||||||
.modify(|r, w| unsafe { w.bits(r.bits() | pin.pin_and_tim.1.mask_32()) });
|
.modify(|r, w| unsafe { w.bits(r.bits() | pin.reg.mask_32()) });
|
||||||
pin.enable_pwm_a();
|
pin.enable_pwm_a();
|
||||||
pin.set_period(initial_period);
|
pin.set_period(initial_period);
|
||||||
pin
|
pin
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn downgrade(self) -> ReducedPwmPin<Mode> {
|
|
||||||
self.inner
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn release(self) -> (Pin, Tim) {
|
pub fn release(self) -> (Pin, Tim) {
|
||||||
self.pin_and_tim
|
self.reg.release()
|
||||||
}
|
}
|
||||||
|
|
||||||
#[inline]
|
pwm_common_func!();
|
||||||
fn enable_pwm_a(&mut self) {
|
|
||||||
self.inner.enable_pwm_a();
|
|
||||||
}
|
|
||||||
|
|
||||||
#[inline]
|
|
||||||
fn enable_pwm_b(&mut self) {
|
|
||||||
self.inner.enable_pwm_b();
|
|
||||||
}
|
|
||||||
|
|
||||||
#[inline]
|
|
||||||
pub fn get_period(&self) -> Hertz {
|
|
||||||
self.inner.get_period()
|
|
||||||
}
|
|
||||||
|
|
||||||
#[inline]
|
|
||||||
pub fn set_period(&mut self, period: impl Into<Hertz>) {
|
|
||||||
self.inner.set_period(period);
|
|
||||||
}
|
|
||||||
|
|
||||||
#[inline]
|
|
||||||
pub fn disable(&mut self) {
|
|
||||||
self.inner.disable();
|
|
||||||
}
|
|
||||||
|
|
||||||
#[inline]
|
|
||||||
pub fn enable(&mut self) {
|
|
||||||
self.inner.enable();
|
|
||||||
}
|
|
||||||
|
|
||||||
#[inline]
|
|
||||||
pub fn period(&self) -> Hertz {
|
|
||||||
self.inner.period()
|
|
||||||
}
|
|
||||||
|
|
||||||
#[inline(always)]
|
|
||||||
pub fn duty(&self) -> u16 {
|
|
||||||
self.inner.duty()
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<Pin: TimPin, Tim: ValidTim> From<PwmPin<Pin, Tim, PwmA>> for PwmPin<Pin, Tim, PwmB>
|
impl<Pin: TimPin, Tim: ValidTim> From<PwmPin<Pin, Tim, PwmA>> for PwmPin<Pin, Tim, PwmB>
|
||||||
@ -137,9 +199,9 @@ where
|
|||||||
{
|
{
|
||||||
fn from(other: PwmPin<Pin, Tim, PwmA>) -> Self {
|
fn from(other: PwmPin<Pin, Tim, PwmA>) -> Self {
|
||||||
let mut pwmb = Self {
|
let mut pwmb = Self {
|
||||||
|
reg: other.reg,
|
||||||
|
pwm_base: other.pwm_base,
|
||||||
mode: PhantomData,
|
mode: PhantomData,
|
||||||
pin_and_tim: other.pin_and_tim,
|
|
||||||
inner: other.inner.into(),
|
|
||||||
};
|
};
|
||||||
pwmb.enable_pwm_b();
|
pwmb.enable_pwm_b();
|
||||||
pwmb
|
pwmb
|
||||||
@ -151,13 +213,13 @@ where
|
|||||||
(PIN, TIM): ValidTimAndPin<PIN, TIM>,
|
(PIN, TIM): ValidTimAndPin<PIN, TIM>,
|
||||||
{
|
{
|
||||||
fn from(other: PwmPin<PIN, TIM, PwmB>) -> Self {
|
fn from(other: PwmPin<PIN, TIM, PwmB>) -> Self {
|
||||||
let mut pwma = Self {
|
let mut pwmb = Self {
|
||||||
|
reg: other.reg,
|
||||||
|
pwm_base: other.pwm_base,
|
||||||
mode: PhantomData,
|
mode: PhantomData,
|
||||||
pin_and_tim: other.pin_and_tim,
|
|
||||||
inner: other.inner.into(),
|
|
||||||
};
|
};
|
||||||
pwma.enable_pwm_a();
|
pwmb.enable_pwm_a();
|
||||||
pwma
|
pwmb
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -201,107 +263,33 @@ where
|
|||||||
|
|
||||||
/// Reduced version where type information is deleted
|
/// Reduced version where type information is deleted
|
||||||
pub struct ReducedPwmPin<Mode = PwmA> {
|
pub struct ReducedPwmPin<Mode = PwmA> {
|
||||||
dyn_reg: TimDynRegister,
|
reg: TimDynRegister,
|
||||||
common: PwmCommon,
|
pwm_base: PwmBase,
|
||||||
|
pin_id: DynPinId,
|
||||||
mode: PhantomData<Mode>,
|
mode: PhantomData<Mode>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<Mode> ReducedPwmPin<Mode> {
|
impl<PIN: TimPin, TIM: ValidTim> From<PwmPin<PIN, TIM>> for ReducedPwmPin<PwmA> {
|
||||||
pub(crate) fn new(tim_id: u8, pin_id: DynPinId, common: PwmCommon) -> Self {
|
fn from(pwm_pin: PwmPin<PIN, TIM>) -> Self {
|
||||||
Self {
|
ReducedPwmPin {
|
||||||
dyn_reg: TimDynRegister { tim_id, pin_id },
|
reg: TimDynRegister::from(pwm_pin.reg),
|
||||||
common,
|
pwm_base: pwm_pin.pwm_base,
|
||||||
|
pin_id: PIN::DYN,
|
||||||
mode: PhantomData,
|
mode: PhantomData,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<Mode> ReducedPwmPin<Mode> {
|
impl<MODE> ReducedPwmPin<MODE> {
|
||||||
#[inline]
|
pwm_common_func!();
|
||||||
fn enable_pwm_a(&mut self) {
|
|
||||||
self.dyn_reg
|
|
||||||
.reg_block()
|
|
||||||
.ctrl()
|
|
||||||
.modify(|_, w| unsafe { w.status_sel().bits(StatusSelPwm::PwmA as u8) });
|
|
||||||
}
|
|
||||||
|
|
||||||
#[inline]
|
|
||||||
fn enable_pwm_b(&mut self) {
|
|
||||||
self.dyn_reg
|
|
||||||
.reg_block()
|
|
||||||
.ctrl()
|
|
||||||
.modify(|_, w| unsafe { w.status_sel().bits(StatusSelPwm::PwmB as u8) });
|
|
||||||
}
|
|
||||||
|
|
||||||
#[inline]
|
|
||||||
pub fn get_period(&self) -> Hertz {
|
|
||||||
self.common.current_period
|
|
||||||
}
|
|
||||||
|
|
||||||
#[inline]
|
|
||||||
pub fn set_period(&mut self, period: impl Into<Hertz>) {
|
|
||||||
self.common.current_period = period.into();
|
|
||||||
// Avoid division by 0
|
|
||||||
if self.common.current_period.raw() == 0 {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
self.common.current_rst_val = self.common.sys_clk.raw() / self.common.current_period.raw();
|
|
||||||
self.dyn_reg
|
|
||||||
.reg_block()
|
|
||||||
.rst_value()
|
|
||||||
.write(|w| unsafe { w.bits(self.common.current_rst_val) });
|
|
||||||
}
|
|
||||||
|
|
||||||
#[inline]
|
|
||||||
pub fn disable(&mut self) {
|
|
||||||
self.dyn_reg
|
|
||||||
.reg_block()
|
|
||||||
.ctrl()
|
|
||||||
.modify(|_, w| w.enable().clear_bit());
|
|
||||||
}
|
|
||||||
|
|
||||||
#[inline]
|
|
||||||
pub fn enable(&mut self) {
|
|
||||||
self.dyn_reg
|
|
||||||
.reg_block()
|
|
||||||
.ctrl()
|
|
||||||
.modify(|_, w| w.enable().set_bit());
|
|
||||||
}
|
|
||||||
|
|
||||||
#[inline]
|
|
||||||
pub fn period(&self) -> Hertz {
|
|
||||||
self.common.current_period
|
|
||||||
}
|
|
||||||
|
|
||||||
#[inline(always)]
|
|
||||||
pub fn duty(&self) -> u16 {
|
|
||||||
self.common.current_duty
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<Pin: TimPin, Tim: ValidTim> From<PwmPin<Pin, Tim, PwmA>> for ReducedPwmPin<PwmA>
|
|
||||||
where
|
|
||||||
(Pin, Tim): ValidTimAndPin<Pin, Tim>,
|
|
||||||
{
|
|
||||||
fn from(value: PwmPin<Pin, Tim, PwmA>) -> Self {
|
|
||||||
value.downgrade()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<Pin: TimPin, Tim: ValidTim> From<PwmPin<Pin, Tim, PwmB>> for ReducedPwmPin<PwmB>
|
|
||||||
where
|
|
||||||
(Pin, Tim): ValidTimAndPin<Pin, Tim>,
|
|
||||||
{
|
|
||||||
fn from(value: PwmPin<Pin, Tim, PwmB>) -> Self {
|
|
||||||
value.downgrade()
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
impl From<ReducedPwmPin<PwmA>> for ReducedPwmPin<PwmB> {
|
impl From<ReducedPwmPin<PwmA>> for ReducedPwmPin<PwmB> {
|
||||||
fn from(other: ReducedPwmPin<PwmA>) -> Self {
|
fn from(other: ReducedPwmPin<PwmA>) -> Self {
|
||||||
let mut pwmb = Self {
|
let mut pwmb = Self {
|
||||||
dyn_reg: other.dyn_reg,
|
reg: other.reg,
|
||||||
common: other.common,
|
pwm_base: other.pwm_base,
|
||||||
|
pin_id: other.pin_id,
|
||||||
mode: PhantomData,
|
mode: PhantomData,
|
||||||
};
|
};
|
||||||
pwmb.enable_pwm_b();
|
pwmb.enable_pwm_b();
|
||||||
@ -312,8 +300,9 @@ impl From<ReducedPwmPin<PwmA>> for ReducedPwmPin<PwmB> {
|
|||||||
impl From<ReducedPwmPin<PwmB>> for ReducedPwmPin<PwmA> {
|
impl From<ReducedPwmPin<PwmB>> for ReducedPwmPin<PwmA> {
|
||||||
fn from(other: ReducedPwmPin<PwmB>) -> Self {
|
fn from(other: ReducedPwmPin<PwmB>) -> Self {
|
||||||
let mut pwmb = Self {
|
let mut pwmb = Self {
|
||||||
dyn_reg: other.dyn_reg,
|
reg: other.reg,
|
||||||
common: other.common,
|
pwm_base: other.pwm_base,
|
||||||
|
pin_id: other.pin_id,
|
||||||
mode: PhantomData,
|
mode: PhantomData,
|
||||||
};
|
};
|
||||||
pwmb.enable_pwm_a();
|
pwmb.enable_pwm_a();
|
||||||
@ -325,83 +314,15 @@ impl From<ReducedPwmPin<PwmB>> for ReducedPwmPin<PwmA> {
|
|||||||
// PWMB implementations
|
// PWMB implementations
|
||||||
//==================================================================================================
|
//==================================================================================================
|
||||||
|
|
||||||
impl<Pin: TimPin, Tim: ValidTim> PwmPin<Pin, Tim, PwmB>
|
impl<PIN: TimPin, TIM: ValidTim> PwmPin<PIN, TIM, PwmB>
|
||||||
where
|
where
|
||||||
(Pin, Tim): ValidTimAndPin<Pin, Tim>,
|
(PIN, TIM): ValidTimAndPin<PIN, TIM>,
|
||||||
{
|
{
|
||||||
pub fn pwmb_lower_limit(&self) -> u16 {
|
pwmb_func!();
|
||||||
self.inner.pwmb_lower_limit()
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn pwmb_upper_limit(&self) -> u16 {
|
|
||||||
self.inner.pwmb_upper_limit()
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Set the lower limit for PWMB
|
|
||||||
///
|
|
||||||
/// The PWM signal will be 1 as long as the current RST counter is larger than
|
|
||||||
/// the lower limit. For example, with a lower limit of 0.5 and and an upper limit
|
|
||||||
/// of 0.7, Only a fixed period between 0.5 * period and 0.7 * period will be in a high
|
|
||||||
/// state
|
|
||||||
pub fn set_pwmb_lower_limit(&mut self, duty: u16) {
|
|
||||||
self.inner.set_pwmb_lower_limit(duty);
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Set the higher limit for PWMB
|
|
||||||
///
|
|
||||||
/// The PWM signal will be 1 as long as the current RST counter is smaller than
|
|
||||||
/// the higher limit. For example, with a lower limit of 0.5 and and an upper limit
|
|
||||||
/// of 0.7, Only a fixed period between 0.5 * period and 0.7 * period will be in a high
|
|
||||||
/// state
|
|
||||||
pub fn set_pwmb_upper_limit(&mut self, duty: u16) {
|
|
||||||
self.inner.set_pwmb_upper_limit(duty);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
impl ReducedPwmPin<PwmB> {
|
impl ReducedPwmPin<PwmB> {
|
||||||
#[inline(always)]
|
pwmb_func!();
|
||||||
pub fn pwmb_lower_limit(&self) -> u16 {
|
|
||||||
self.common.current_lower_limit
|
|
||||||
}
|
|
||||||
|
|
||||||
#[inline(always)]
|
|
||||||
pub fn pwmb_upper_limit(&self) -> u16 {
|
|
||||||
self.common.current_duty
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Set the lower limit for PWMB
|
|
||||||
///
|
|
||||||
/// The PWM signal will be 1 as long as the current RST counter is larger than
|
|
||||||
/// the lower limit. For example, with a lower limit of 0.5 and and an upper limit
|
|
||||||
/// of 0.7, Only a fixed period between 0.5 * period and 0.7 * period will be in a high
|
|
||||||
/// state
|
|
||||||
#[inline(always)]
|
|
||||||
pub fn set_pwmb_lower_limit(&mut self, duty: u16) {
|
|
||||||
self.common.current_lower_limit = duty;
|
|
||||||
let pwmb_val: u64 = (self.common.current_rst_val as u64
|
|
||||||
* self.common.current_lower_limit as u64)
|
|
||||||
/ DUTY_MAX as u64;
|
|
||||||
self.dyn_reg
|
|
||||||
.reg_block()
|
|
||||||
.pwmb_value()
|
|
||||||
.write(|w| unsafe { w.bits(pwmb_val as u32) });
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Set the higher limit for PWMB
|
|
||||||
///
|
|
||||||
/// The PWM signal will be 1 as long as the current RST counter is smaller than
|
|
||||||
/// the higher limit. For example, with a lower limit of 0.5 and and an upper limit
|
|
||||||
/// of 0.7, Only a fixed period between 0.5 * period and 0.7 * period will be in a high
|
|
||||||
/// state
|
|
||||||
pub fn set_pwmb_upper_limit(&mut self, duty: u16) {
|
|
||||||
self.common.current_duty = duty;
|
|
||||||
let pwma_val: u64 = (self.common.current_rst_val as u64 * self.common.current_duty as u64)
|
|
||||||
/ DUTY_MAX as u64;
|
|
||||||
self.dyn_reg
|
|
||||||
.reg_block()
|
|
||||||
.pwma_value()
|
|
||||||
.write(|w| unsafe { w.bits(pwma_val as u32) });
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
//==================================================================================================
|
//==================================================================================================
|
||||||
@ -424,12 +345,12 @@ impl embedded_hal::pwm::SetDutyCycle for ReducedPwmPin {
|
|||||||
|
|
||||||
#[inline]
|
#[inline]
|
||||||
fn set_duty_cycle(&mut self, duty: u16) -> Result<(), Self::Error> {
|
fn set_duty_cycle(&mut self, duty: u16) -> Result<(), Self::Error> {
|
||||||
self.common.current_duty = duty;
|
self.pwm_base.current_duty = duty;
|
||||||
let pwma_val: u64 = (self.common.current_rst_val as u64
|
let pwma_val: u64 = (self.pwm_base.current_rst_val as u64
|
||||||
* (DUTY_MAX as u64 - self.common.current_duty as u64))
|
* (DUTY_MAX as u64 - self.pwm_base.current_duty as u64))
|
||||||
/ DUTY_MAX as u64;
|
/ DUTY_MAX as u64;
|
||||||
self.dyn_reg
|
self.reg
|
||||||
.reg_block()
|
.reg()
|
||||||
.pwma_value()
|
.pwma_value()
|
||||||
.write(|w| unsafe { w.bits(pwma_val as u32) });
|
.write(|w| unsafe { w.bits(pwma_val as u32) });
|
||||||
Ok(())
|
Ok(())
|
||||||
@ -444,7 +365,15 @@ impl<Pin: TimPin, Tim: ValidTim> embedded_hal::pwm::SetDutyCycle for PwmPin<Pin,
|
|||||||
|
|
||||||
#[inline]
|
#[inline]
|
||||||
fn set_duty_cycle(&mut self, duty: u16) -> Result<(), Self::Error> {
|
fn set_duty_cycle(&mut self, duty: u16) -> Result<(), Self::Error> {
|
||||||
self.inner.set_duty_cycle(duty)
|
self.pwm_base.current_duty = duty;
|
||||||
|
let pwma_val: u64 = (self.pwm_base.current_rst_val as u64
|
||||||
|
* (DUTY_MAX as u64 - self.pwm_base.current_duty as u64))
|
||||||
|
/ DUTY_MAX as u64;
|
||||||
|
self.reg
|
||||||
|
.reg()
|
||||||
|
.pwma_value()
|
||||||
|
.write(|w| unsafe { w.bits(pwma_val as u32) });
|
||||||
|
Ok(())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -33,12 +33,10 @@ use embedded_hal::spi::{Mode, MODE_0};
|
|||||||
const FILL_DEPTH: usize = 12;
|
const FILL_DEPTH: usize = 12;
|
||||||
|
|
||||||
pub const BMSTART_BMSTOP_MASK: u32 = 1 << 31;
|
pub const BMSTART_BMSTOP_MASK: u32 = 1 << 31;
|
||||||
pub const BMSKIPDATA_MASK: u32 = 1 << 30;
|
|
||||||
|
|
||||||
pub const DEFAULT_CLK_DIV: u16 = 2;
|
pub const DEFAULT_CLK_DIV: u16 = 2;
|
||||||
|
|
||||||
#[derive(Debug, PartialEq, Eq, Copy, Clone)]
|
#[derive(Debug, PartialEq, Eq, Copy, Clone)]
|
||||||
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
|
|
||||||
pub enum HwChipSelectId {
|
pub enum HwChipSelectId {
|
||||||
Id0 = 0,
|
Id0 = 0,
|
||||||
Id1 = 1,
|
Id1 = 1,
|
||||||
@ -52,7 +50,6 @@ pub enum HwChipSelectId {
|
|||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
|
|
||||||
pub enum SpiPort {
|
pub enum SpiPort {
|
||||||
Porta = 0,
|
Porta = 0,
|
||||||
Portb = 1,
|
Portb = 1,
|
||||||
@ -61,7 +58,6 @@ pub enum SpiPort {
|
|||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, PartialEq, Eq, Copy, Clone)]
|
#[derive(Debug, PartialEq, Eq, Copy, Clone)]
|
||||||
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
|
|
||||||
pub enum WordSize {
|
pub enum WordSize {
|
||||||
OneBit = 0x00,
|
OneBit = 0x00,
|
||||||
FourBits = 0x03,
|
FourBits = 0x03,
|
||||||
@ -289,7 +285,6 @@ pub trait TransferConfigProvider {
|
|||||||
/// This struct contains all configuration parameter which are transfer specific
|
/// This struct contains all configuration parameter which are transfer specific
|
||||||
/// and might change for transfers to different SPI slaves
|
/// and might change for transfers to different SPI slaves
|
||||||
#[derive(Copy, Clone, Debug)]
|
#[derive(Copy, Clone, Debug)]
|
||||||
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
|
|
||||||
pub struct TransferConfigWithHwcs<HwCs> {
|
pub struct TransferConfigWithHwcs<HwCs> {
|
||||||
pub hw_cs: Option<HwCs>,
|
pub hw_cs: Option<HwCs>,
|
||||||
pub cfg: TransferConfig,
|
pub cfg: TransferConfig,
|
||||||
@ -298,7 +293,6 @@ pub struct TransferConfigWithHwcs<HwCs> {
|
|||||||
/// Type erased variant of the transfer configuration. This is required to avoid generics in
|
/// Type erased variant of the transfer configuration. This is required to avoid generics in
|
||||||
/// the SPI constructor.
|
/// the SPI constructor.
|
||||||
#[derive(Copy, Clone, Debug)]
|
#[derive(Copy, Clone, Debug)]
|
||||||
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
|
|
||||||
pub struct TransferConfig {
|
pub struct TransferConfig {
|
||||||
pub clk_cfg: Option<SpiClkConfig>,
|
pub clk_cfg: Option<SpiClkConfig>,
|
||||||
pub mode: Option<Mode>,
|
pub mode: Option<Mode>,
|
||||||
@ -386,8 +380,6 @@ impl<HwCs: HwCsProvider> TransferConfigProvider for TransferConfigWithHwcs<HwCs>
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// Configuration options for the whole SPI bus. See Programmer Guide p.92 for more details
|
/// Configuration options for the whole SPI bus. See Programmer Guide p.92 for more details
|
||||||
#[derive(Debug)]
|
|
||||||
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
|
|
||||||
pub struct SpiConfig {
|
pub struct SpiConfig {
|
||||||
clk: SpiClkConfig,
|
clk: SpiClkConfig,
|
||||||
// SPI mode configuration
|
// SPI mode configuration
|
||||||
@ -537,7 +529,6 @@ pub struct Spi<SpiInstance, Pins, Word = u8> {
|
|||||||
pins: Pins,
|
pins: Pins,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[inline(always)]
|
|
||||||
pub fn mode_to_cpo_cph_bit(mode: embedded_hal::spi::Mode) -> (bool, bool) {
|
pub fn mode_to_cpo_cph_bit(mode: embedded_hal::spi::Mode) -> (bool, bool) {
|
||||||
match mode {
|
match mode {
|
||||||
embedded_hal::spi::MODE_0 => (false, false),
|
embedded_hal::spi::MODE_0 => (false, false),
|
||||||
@ -580,14 +571,10 @@ impl SpiClkConfig {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, thiserror::Error)]
|
#[derive(Debug)]
|
||||||
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
|
|
||||||
pub enum SpiClkConfigError {
|
pub enum SpiClkConfigError {
|
||||||
#[error("division by zero")]
|
|
||||||
DivIsZero,
|
DivIsZero,
|
||||||
#[error("divide value is not even")]
|
|
||||||
DivideValueNotEven,
|
DivideValueNotEven,
|
||||||
#[error("scrdv value is too large")]
|
|
||||||
ScrdvValueTooLarge,
|
ScrdvValueTooLarge,
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -799,7 +786,7 @@ where
|
|||||||
// initialization. Returns the amount of written bytes.
|
// initialization. Returns the amount of written bytes.
|
||||||
fn initial_send_fifo_pumping_with_words(&self, words: &[Word]) -> usize {
|
fn initial_send_fifo_pumping_with_words(&self, words: &[Word]) -> usize {
|
||||||
if self.blockmode {
|
if self.blockmode {
|
||||||
self.spi.ctrl1().modify(|_, w| w.mtxpause().set_bit());
|
self.spi.ctrl1().modify(|_, w| w.mtxpause().set_bit())
|
||||||
}
|
}
|
||||||
// Fill the first half of the write FIFO
|
// Fill the first half of the write FIFO
|
||||||
let mut current_write_idx = 0;
|
let mut current_write_idx = 0;
|
||||||
@ -813,7 +800,7 @@ where
|
|||||||
current_write_idx += 1;
|
current_write_idx += 1;
|
||||||
}
|
}
|
||||||
if self.blockmode {
|
if self.blockmode {
|
||||||
self.spi.ctrl1().modify(|_, w| w.mtxpause().clear_bit());
|
self.spi.ctrl1().modify(|_, w| w.mtxpause().clear_bit())
|
||||||
}
|
}
|
||||||
current_write_idx
|
current_write_idx
|
||||||
}
|
}
|
||||||
@ -822,7 +809,7 @@ where
|
|||||||
// initialization.
|
// initialization.
|
||||||
fn initial_send_fifo_pumping_with_fill_words(&self, send_len: usize) -> usize {
|
fn initial_send_fifo_pumping_with_fill_words(&self, send_len: usize) -> usize {
|
||||||
if self.blockmode {
|
if self.blockmode {
|
||||||
self.spi.ctrl1().modify(|_, w| w.mtxpause().set_bit());
|
self.spi.ctrl1().modify(|_, w| w.mtxpause().set_bit())
|
||||||
}
|
}
|
||||||
// Fill the first half of the write FIFO
|
// Fill the first half of the write FIFO
|
||||||
let mut current_write_idx = 0;
|
let mut current_write_idx = 0;
|
||||||
@ -836,7 +823,7 @@ where
|
|||||||
current_write_idx += 1;
|
current_write_idx += 1;
|
||||||
}
|
}
|
||||||
if self.blockmode {
|
if self.blockmode {
|
||||||
self.spi.ctrl1().modify(|_, w| w.mtxpause().clear_bit());
|
self.spi.ctrl1().modify(|_, w| w.mtxpause().clear_bit())
|
||||||
}
|
}
|
||||||
current_write_idx
|
current_write_idx
|
||||||
}
|
}
|
||||||
|
@ -20,7 +20,7 @@ pub fn enable_rom_scrubbing(
|
|||||||
}
|
}
|
||||||
|
|
||||||
pub fn disable_rom_scrubbing(syscfg: &mut pac::Sysconfig) {
|
pub fn disable_rom_scrubbing(syscfg: &mut pac::Sysconfig) {
|
||||||
syscfg.rom_scrub().write(|w| unsafe { w.bits(0) });
|
syscfg.rom_scrub().write(|w| unsafe { w.bits(0) })
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Enable scrubbing for the RAM
|
/// Enable scrubbing for the RAM
|
||||||
@ -39,7 +39,7 @@ pub fn enable_ram_scrubbing(
|
|||||||
}
|
}
|
||||||
|
|
||||||
pub fn disable_ram_scrubbing(syscfg: &mut pac::Sysconfig) {
|
pub fn disable_ram_scrubbing(syscfg: &mut pac::Sysconfig) {
|
||||||
syscfg.ram_scrub().write(|w| unsafe { w.bits(0) });
|
syscfg.ram_scrub().write(|w| unsafe { w.bits(0) })
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Clear the reset bit. This register is active low, so doing this will hold the peripheral
|
/// Clear the reset bit. This register is active low, so doing this will hold the peripheral
|
||||||
|
@ -4,10 +4,10 @@
|
|||||||
//!
|
//!
|
||||||
//! - [MS and second tick implementation](https://egit.irs.uni-stuttgart.de/rust/va108xx-rs/src/branch/main/examples/simple/examples/timer-ticks.rs)
|
//! - [MS and second tick implementation](https://egit.irs.uni-stuttgart.de/rust/va108xx-rs/src/branch/main/examples/simple/examples/timer-ticks.rs)
|
||||||
//! - [Cascade feature example](https://egit.irs.uni-stuttgart.de/rust/va108xx-rs/src/branch/main/examples/simple/examples/cascade.rs)
|
//! - [Cascade feature example](https://egit.irs.uni-stuttgart.de/rust/va108xx-rs/src/branch/main/examples/simple/examples/cascade.rs)
|
||||||
pub use crate::InterruptConfig;
|
pub use crate::IrqCfg;
|
||||||
use crate::{
|
use crate::{
|
||||||
clock::{enable_peripheral_clock, PeripheralClocks},
|
clock::{enable_peripheral_clock, PeripheralClocks},
|
||||||
enable_nvic_interrupt,
|
enable_interrupt,
|
||||||
gpio::{
|
gpio::{
|
||||||
AltFunc1, AltFunc2, AltFunc3, DynPinId, Pin, PinId, PA0, PA1, PA10, PA11, PA12, PA13, PA14,
|
AltFunc1, AltFunc2, AltFunc3, DynPinId, Pin, PinId, PA0, PA1, PA10, PA11, PA12, PA13, PA14,
|
||||||
PA15, PA2, PA24, PA25, PA26, PA27, PA28, PA29, PA3, PA30, PA31, PA4, PA5, PA6, PA7, PA8,
|
PA15, PA2, PA24, PA25, PA26, PA27, PA28, PA29, PA3, PA30, PA31, PA4, PA5, PA6, PA7, PA8,
|
||||||
@ -26,48 +26,6 @@ use fugit::RateExtU32;
|
|||||||
const IRQ_DST_NONE: u32 = 0xffffffff;
|
const IRQ_DST_NONE: u32 = 0xffffffff;
|
||||||
pub static MS_COUNTER: Mutex<Cell<u32>> = Mutex::new(Cell::new(0));
|
pub static MS_COUNTER: Mutex<Cell<u32>> = Mutex::new(Cell::new(0));
|
||||||
|
|
||||||
/// Get the peripheral block of a TIM peripheral given the index.
|
|
||||||
///
|
|
||||||
/// This function panics if the given index is greater than 23.
|
|
||||||
///
|
|
||||||
/// # Safety
|
|
||||||
///
|
|
||||||
/// This returns a direct handle to the peripheral block, which allows to circumvent ownership
|
|
||||||
/// rules for the peripheral block. You have to ensure that the retrieved peripheral block is not
|
|
||||||
/// used by any other software component.
|
|
||||||
#[inline(always)]
|
|
||||||
pub const unsafe fn get_tim_raw(tim_idx: usize) -> &'static pac::tim0::RegisterBlock {
|
|
||||||
match tim_idx {
|
|
||||||
0 => unsafe { &*pac::Tim0::ptr() },
|
|
||||||
1 => unsafe { &*pac::Tim1::ptr() },
|
|
||||||
2 => unsafe { &*pac::Tim2::ptr() },
|
|
||||||
3 => unsafe { &*pac::Tim3::ptr() },
|
|
||||||
4 => unsafe { &*pac::Tim4::ptr() },
|
|
||||||
5 => unsafe { &*pac::Tim5::ptr() },
|
|
||||||
6 => unsafe { &*pac::Tim6::ptr() },
|
|
||||||
7 => unsafe { &*pac::Tim7::ptr() },
|
|
||||||
8 => unsafe { &*pac::Tim8::ptr() },
|
|
||||||
9 => unsafe { &*pac::Tim9::ptr() },
|
|
||||||
10 => unsafe { &*pac::Tim10::ptr() },
|
|
||||||
11 => unsafe { &*pac::Tim11::ptr() },
|
|
||||||
12 => unsafe { &*pac::Tim12::ptr() },
|
|
||||||
13 => unsafe { &*pac::Tim13::ptr() },
|
|
||||||
14 => unsafe { &*pac::Tim14::ptr() },
|
|
||||||
15 => unsafe { &*pac::Tim15::ptr() },
|
|
||||||
16 => unsafe { &*pac::Tim16::ptr() },
|
|
||||||
17 => unsafe { &*pac::Tim17::ptr() },
|
|
||||||
18 => unsafe { &*pac::Tim18::ptr() },
|
|
||||||
19 => unsafe { &*pac::Tim19::ptr() },
|
|
||||||
20 => unsafe { &*pac::Tim20::ptr() },
|
|
||||||
21 => unsafe { &*pac::Tim21::ptr() },
|
|
||||||
22 => unsafe { &*pac::Tim22::ptr() },
|
|
||||||
23 => unsafe { &*pac::Tim23::ptr() },
|
|
||||||
_ => {
|
|
||||||
panic!("invalid alarm timer index")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
//==================================================================================================
|
//==================================================================================================
|
||||||
// Defintions
|
// Defintions
|
||||||
//==================================================================================================
|
//==================================================================================================
|
||||||
@ -79,7 +37,6 @@ pub enum Event {
|
|||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Default, Debug, PartialEq, Eq, Copy, Clone)]
|
#[derive(Default, Debug, PartialEq, Eq, Copy, Clone)]
|
||||||
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
|
|
||||||
pub struct CascadeCtrl {
|
pub struct CascadeCtrl {
|
||||||
/// Enable Cascade 0 signal active as a requirement for counting
|
/// Enable Cascade 0 signal active as a requirement for counting
|
||||||
pub enb_start_src_csd0: bool,
|
pub enb_start_src_csd0: bool,
|
||||||
@ -109,7 +66,6 @@ pub struct CascadeCtrl {
|
|||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, PartialEq, Eq)]
|
#[derive(Debug, PartialEq, Eq)]
|
||||||
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
|
|
||||||
pub enum CascadeSel {
|
pub enum CascadeSel {
|
||||||
Csd0 = 0,
|
Csd0 = 0,
|
||||||
Csd1 = 1,
|
Csd1 = 1,
|
||||||
@ -288,11 +244,11 @@ pub type TimRegBlock = tim0::RegisterBlock;
|
|||||||
///
|
///
|
||||||
/// # Safety
|
/// # Safety
|
||||||
///
|
///
|
||||||
/// Users should only implement the [Self::tim_id] function. No default function
|
/// Users should only implement the [`tim_id`] function. No default function
|
||||||
/// implementations should be overridden. The implementing type must also have
|
/// implementations should be overridden. The implementing type must also have
|
||||||
/// "control" over the corresponding pin ID, i.e. it must guarantee that a each
|
/// "control" over the corresponding pin ID, i.e. it must guarantee that a each
|
||||||
/// pin ID is a singleton.
|
/// pin ID is a singleton.
|
||||||
pub unsafe trait TimRegInterface {
|
pub(super) unsafe trait TimRegInterface {
|
||||||
fn tim_id(&self) -> u8;
|
fn tim_id(&self) -> u8;
|
||||||
|
|
||||||
const PORT_BASE: *const tim0::RegisterBlock = pac::Tim0::ptr() as *const _;
|
const PORT_BASE: *const tim0::RegisterBlock = pac::Tim0::ptr() as *const _;
|
||||||
@ -300,7 +256,7 @@ pub unsafe trait TimRegInterface {
|
|||||||
/// All 24 TIM blocks are identical. This helper functions returns the correct
|
/// All 24 TIM blocks are identical. This helper functions returns the correct
|
||||||
/// memory mapped peripheral depending on the TIM ID.
|
/// memory mapped peripheral depending on the TIM ID.
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
fn reg_block(&self) -> &TimRegBlock {
|
fn reg(&self) -> &TimRegBlock {
|
||||||
unsafe { &*Self::PORT_BASE.offset(self.tim_id() as isize) }
|
unsafe { &*Self::PORT_BASE.offset(self.tim_id() as isize) }
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -321,7 +277,7 @@ pub unsafe trait TimRegInterface {
|
|||||||
va108xx::Peripherals::steal()
|
va108xx::Peripherals::steal()
|
||||||
.sysconfig
|
.sysconfig
|
||||||
.tim_reset()
|
.tim_reset()
|
||||||
.modify(|r, w| w.bits(r.bits() & !self.mask_32()));
|
.modify(|r, w| w.bits(r.bits() & !self.mask_32()))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -332,21 +288,75 @@ pub unsafe trait TimRegInterface {
|
|||||||
va108xx::Peripherals::steal()
|
va108xx::Peripherals::steal()
|
||||||
.sysconfig
|
.sysconfig
|
||||||
.tim_reset()
|
.tim_reset()
|
||||||
.modify(|r, w| w.bits(r.bits() | self.mask_32()));
|
.modify(|r, w| w.bits(r.bits() | self.mask_32()))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
unsafe impl<Tim: ValidTim> TimRegInterface for Tim {
|
/// Provide a safe register interface for [`ValidTimAndPin`]s
|
||||||
fn tim_id(&self) -> u8 {
|
///
|
||||||
Tim::TIM_ID
|
/// This `struct` takes ownership of a [`ValidTimAndPin`] and provides an API to
|
||||||
|
/// access the corresponding registers.
|
||||||
|
pub(super) struct TimAndPinRegister<Pin: TimPin, Tim: ValidTim> {
|
||||||
|
pin: Pin,
|
||||||
|
tim: Tim,
|
||||||
|
}
|
||||||
|
|
||||||
|
pub(super) struct TimRegister<TIM: ValidTim> {
|
||||||
|
tim: TIM,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<TIM: ValidTim> TimRegister<TIM> {
|
||||||
|
#[inline]
|
||||||
|
pub(super) unsafe fn new(tim: TIM) -> Self {
|
||||||
|
TimRegister { tim }
|
||||||
|
}
|
||||||
|
|
||||||
|
pub(super) fn release(self) -> TIM {
|
||||||
|
self.tim
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(crate) struct TimDynRegister {
|
unsafe impl<TIM: ValidTim> TimRegInterface for TimRegister<TIM> {
|
||||||
pub(crate) tim_id: u8,
|
fn tim_id(&self) -> u8 {
|
||||||
|
TIM::TIM_ID
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<PIN: TimPin, TIM: ValidTim> TimAndPinRegister<PIN, TIM>
|
||||||
|
where
|
||||||
|
(PIN, TIM): ValidTimAndPin<PIN, TIM>,
|
||||||
|
{
|
||||||
|
#[inline]
|
||||||
|
pub(super) unsafe fn new(pin: PIN, tim: TIM) -> Self {
|
||||||
|
TimAndPinRegister { pin, tim }
|
||||||
|
}
|
||||||
|
|
||||||
|
pub(super) fn release(self) -> (PIN, TIM) {
|
||||||
|
(self.pin, self.tim)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
unsafe impl<PIN: TimPin, TIM: ValidTim> TimRegInterface for TimAndPinRegister<PIN, TIM> {
|
||||||
|
#[inline(always)]
|
||||||
|
fn tim_id(&self) -> u8 {
|
||||||
|
TIM::TIM_ID
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub(super) struct TimDynRegister {
|
||||||
|
tim_id: u8,
|
||||||
#[allow(dead_code)]
|
#[allow(dead_code)]
|
||||||
pub(crate) pin_id: DynPinId,
|
pin_id: DynPinId,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<PIN: TimPin, TIM: ValidTim> From<TimAndPinRegister<PIN, TIM>> for TimDynRegister {
|
||||||
|
fn from(_reg: TimAndPinRegister<PIN, TIM>) -> Self {
|
||||||
|
Self {
|
||||||
|
tim_id: TIM::TIM_ID,
|
||||||
|
pin_id: PIN::DYN,
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
unsafe impl TimRegInterface for TimDynRegister {
|
unsafe impl TimRegInterface for TimDynRegister {
|
||||||
@ -361,10 +371,10 @@ unsafe impl TimRegInterface for TimDynRegister {
|
|||||||
//==================================================================================================
|
//==================================================================================================
|
||||||
|
|
||||||
/// Hardware timers
|
/// Hardware timers
|
||||||
pub struct CountdownTimer<Tim: ValidTim> {
|
pub struct CountdownTimer<TIM: ValidTim> {
|
||||||
tim: Tim,
|
tim: TimRegister<TIM>,
|
||||||
curr_freq: Hertz,
|
curr_freq: Hertz,
|
||||||
irq_cfg: Option<InterruptConfig>,
|
irq_cfg: Option<IrqCfg>,
|
||||||
sys_clk: Hertz,
|
sys_clk: Hertz,
|
||||||
rst_val: u32,
|
rst_val: u32,
|
||||||
last_cnt: u32,
|
last_cnt: u32,
|
||||||
@ -391,12 +401,12 @@ unsafe impl<TIM: ValidTim> TimRegInterface for CountdownTimer<TIM> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<Tim: ValidTim> CountdownTimer<Tim> {
|
impl<TIM: ValidTim> CountdownTimer<TIM> {
|
||||||
/// Configures a TIM peripheral as a periodic count down timer
|
/// Configures a TIM peripheral as a periodic count down timer
|
||||||
pub fn new(syscfg: &mut pac::Sysconfig, sys_clk: impl Into<Hertz>, tim: Tim) -> Self {
|
pub fn new(syscfg: &mut pac::Sysconfig, sys_clk: impl Into<Hertz>, tim: TIM) -> Self {
|
||||||
enable_tim_clk(syscfg, Tim::TIM_ID);
|
enable_tim_clk(syscfg, TIM::TIM_ID);
|
||||||
let cd_timer = CountdownTimer {
|
let cd_timer = CountdownTimer {
|
||||||
tim,
|
tim: unsafe { TimRegister::new(tim) },
|
||||||
sys_clk: sys_clk.into(),
|
sys_clk: sys_clk.into(),
|
||||||
irq_cfg: None,
|
irq_cfg: None,
|
||||||
rst_val: 0,
|
rst_val: 0,
|
||||||
@ -406,7 +416,7 @@ impl<Tim: ValidTim> CountdownTimer<Tim> {
|
|||||||
};
|
};
|
||||||
cd_timer
|
cd_timer
|
||||||
.tim
|
.tim
|
||||||
.reg_block()
|
.reg()
|
||||||
.ctrl()
|
.ctrl()
|
||||||
.modify(|_, w| w.enable().set_bit());
|
.modify(|_, w| w.enable().set_bit());
|
||||||
cd_timer
|
cd_timer
|
||||||
@ -417,13 +427,13 @@ impl<Tim: ValidTim> CountdownTimer<Tim> {
|
|||||||
pub fn listen(
|
pub fn listen(
|
||||||
&mut self,
|
&mut self,
|
||||||
event: Event,
|
event: Event,
|
||||||
irq_cfg: InterruptConfig,
|
irq_cfg: IrqCfg,
|
||||||
irq_sel: Option<&mut pac::Irqsel>,
|
irq_sel: Option<&mut pac::Irqsel>,
|
||||||
sys_cfg: Option<&mut pac::Sysconfig>,
|
sys_cfg: Option<&mut pac::Sysconfig>,
|
||||||
) {
|
) {
|
||||||
match event {
|
match event {
|
||||||
Event::TimeOut => {
|
Event::TimeOut => {
|
||||||
cortex_m::peripheral::NVIC::mask(irq_cfg.id);
|
cortex_m::peripheral::NVIC::mask(irq_cfg.irq);
|
||||||
self.irq_cfg = Some(irq_cfg);
|
self.irq_cfg = Some(irq_cfg);
|
||||||
if irq_cfg.route {
|
if irq_cfg.route {
|
||||||
if let Some(sys_cfg) = sys_cfg {
|
if let Some(sys_cfg) = sys_cfg {
|
||||||
@ -431,8 +441,8 @@ impl<Tim: ValidTim> CountdownTimer<Tim> {
|
|||||||
}
|
}
|
||||||
if let Some(irq_sel) = irq_sel {
|
if let Some(irq_sel) = irq_sel {
|
||||||
irq_sel
|
irq_sel
|
||||||
.tim0(Tim::TIM_ID as usize)
|
.tim0(TIM::TIM_ID as usize)
|
||||||
.write(|w| unsafe { w.bits(irq_cfg.id as u32) });
|
.write(|w| unsafe { w.bits(irq_cfg.irq as u32) });
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
self.listening = true;
|
self.listening = true;
|
||||||
@ -450,7 +460,7 @@ impl<Tim: ValidTim> CountdownTimer<Tim> {
|
|||||||
Event::TimeOut => {
|
Event::TimeOut => {
|
||||||
enable_peripheral_clock(syscfg, PeripheralClocks::Irqsel);
|
enable_peripheral_clock(syscfg, PeripheralClocks::Irqsel);
|
||||||
irqsel
|
irqsel
|
||||||
.tim0(Tim::TIM_ID as usize)
|
.tim0(TIM::TIM_ID as usize)
|
||||||
.write(|w| unsafe { w.bits(IRQ_DST_NONE) });
|
.write(|w| unsafe { w.bits(IRQ_DST_NONE) });
|
||||||
self.disable_interrupt();
|
self.disable_interrupt();
|
||||||
self.listening = false;
|
self.listening = false;
|
||||||
@ -460,37 +470,25 @@ impl<Tim: ValidTim> CountdownTimer<Tim> {
|
|||||||
|
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
pub fn enable_interrupt(&mut self) {
|
pub fn enable_interrupt(&mut self) {
|
||||||
self.tim
|
self.tim.reg().ctrl().modify(|_, w| w.irq_enb().set_bit());
|
||||||
.reg_block()
|
|
||||||
.ctrl()
|
|
||||||
.modify(|_, w| w.irq_enb().set_bit());
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
pub fn disable_interrupt(&mut self) {
|
pub fn disable_interrupt(&mut self) {
|
||||||
self.tim
|
self.tim.reg().ctrl().modify(|_, w| w.irq_enb().clear_bit());
|
||||||
.reg_block()
|
|
||||||
.ctrl()
|
|
||||||
.modify(|_, w| w.irq_enb().clear_bit());
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn release(self, syscfg: &mut pac::Sysconfig) -> Tim {
|
pub fn release(self, syscfg: &mut pac::Sysconfig) -> TIM {
|
||||||
self.tim
|
self.tim.reg().ctrl().write(|w| w.enable().clear_bit());
|
||||||
.reg_block()
|
|
||||||
.ctrl()
|
|
||||||
.write(|w| w.enable().clear_bit());
|
|
||||||
syscfg
|
syscfg
|
||||||
.tim_clk_enable()
|
.tim_clk_enable()
|
||||||
.modify(|r, w| unsafe { w.bits(r.bits() & !(1 << Tim::TIM_ID)) });
|
.modify(|r, w| unsafe { w.bits(r.bits() & !(1 << TIM::TIM_ID)) });
|
||||||
self.tim
|
self.tim.release()
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Load the count down timer with a timeout but do not start it.
|
/// Load the count down timer with a timeout but do not start it.
|
||||||
pub fn load(&mut self, timeout: impl Into<Hertz>) {
|
pub fn load(&mut self, timeout: impl Into<Hertz>) {
|
||||||
self.tim
|
self.tim.reg().ctrl().modify(|_, w| w.enable().clear_bit());
|
||||||
.reg_block()
|
|
||||||
.ctrl()
|
|
||||||
.modify(|_, w| w.enable().clear_bit());
|
|
||||||
self.curr_freq = timeout.into();
|
self.curr_freq = timeout.into();
|
||||||
self.rst_val = self.sys_clk.raw() / self.curr_freq.raw();
|
self.rst_val = self.sys_clk.raw() / self.curr_freq.raw();
|
||||||
self.set_reload(self.rst_val);
|
self.set_reload(self.rst_val);
|
||||||
@ -499,57 +497,45 @@ impl<Tim: ValidTim> CountdownTimer<Tim> {
|
|||||||
|
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
pub fn set_reload(&mut self, val: u32) {
|
pub fn set_reload(&mut self, val: u32) {
|
||||||
self.tim
|
self.tim.reg().rst_value().write(|w| unsafe { w.bits(val) });
|
||||||
.reg_block()
|
|
||||||
.rst_value()
|
|
||||||
.write(|w| unsafe { w.bits(val) });
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
pub fn set_count(&mut self, val: u32) {
|
pub fn set_count(&mut self, val: u32) {
|
||||||
self.tim
|
self.tim.reg().cnt_value().write(|w| unsafe { w.bits(val) });
|
||||||
.reg_block()
|
|
||||||
.cnt_value()
|
|
||||||
.write(|w| unsafe { w.bits(val) });
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
pub fn count(&self) -> u32 {
|
pub fn count(&self) -> u32 {
|
||||||
self.tim.reg_block().cnt_value().read().bits()
|
self.tim.reg().cnt_value().read().bits()
|
||||||
}
|
}
|
||||||
|
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
pub fn enable(&mut self) {
|
pub fn enable(&mut self) {
|
||||||
if let Some(irq_cfg) = self.irq_cfg {
|
if let Some(irq_cfg) = self.irq_cfg {
|
||||||
self.enable_interrupt();
|
self.enable_interrupt();
|
||||||
if irq_cfg.enable_in_nvic {
|
if irq_cfg.enable {
|
||||||
unsafe { enable_nvic_interrupt(irq_cfg.id) };
|
unsafe { enable_interrupt(irq_cfg.irq) };
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
self.tim
|
self.tim.reg().enable().write(|w| unsafe { w.bits(1) });
|
||||||
.reg_block()
|
|
||||||
.enable()
|
|
||||||
.write(|w| unsafe { w.bits(1) });
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
pub fn disable(&mut self) {
|
pub fn disable(&mut self) {
|
||||||
self.tim
|
self.tim.reg().enable().write(|w| unsafe { w.bits(0) });
|
||||||
.reg_block()
|
|
||||||
.enable()
|
|
||||||
.write(|w| unsafe { w.bits(0) });
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Disable the counter, setting both enable and active bit to 0
|
/// Disable the counter, setting both enable and active bit to 0
|
||||||
pub fn auto_disable(self, enable: bool) -> Self {
|
pub fn auto_disable(self, enable: bool) -> Self {
|
||||||
if enable {
|
if enable {
|
||||||
self.tim
|
self.tim
|
||||||
.reg_block()
|
.reg()
|
||||||
.ctrl()
|
.ctrl()
|
||||||
.modify(|_, w| w.auto_disable().set_bit());
|
.modify(|_, w| w.auto_disable().set_bit());
|
||||||
} else {
|
} else {
|
||||||
self.tim
|
self.tim
|
||||||
.reg_block()
|
.reg()
|
||||||
.ctrl()
|
.ctrl()
|
||||||
.modify(|_, w| w.auto_disable().clear_bit());
|
.modify(|_, w| w.auto_disable().clear_bit());
|
||||||
}
|
}
|
||||||
@ -563,12 +549,12 @@ impl<Tim: ValidTim> CountdownTimer<Tim> {
|
|||||||
pub fn auto_deactivate(self, enable: bool) -> Self {
|
pub fn auto_deactivate(self, enable: bool) -> Self {
|
||||||
if enable {
|
if enable {
|
||||||
self.tim
|
self.tim
|
||||||
.reg_block()
|
.reg()
|
||||||
.ctrl()
|
.ctrl()
|
||||||
.modify(|_, w| w.auto_deactivate().set_bit());
|
.modify(|_, w| w.auto_deactivate().set_bit());
|
||||||
} else {
|
} else {
|
||||||
self.tim
|
self.tim
|
||||||
.reg_block()
|
.reg()
|
||||||
.ctrl()
|
.ctrl()
|
||||||
.modify(|_, w| w.auto_deactivate().clear_bit());
|
.modify(|_, w| w.auto_deactivate().clear_bit());
|
||||||
}
|
}
|
||||||
@ -577,7 +563,7 @@ impl<Tim: ValidTim> CountdownTimer<Tim> {
|
|||||||
|
|
||||||
/// Configure the cascade parameters
|
/// Configure the cascade parameters
|
||||||
pub fn cascade_control(&mut self, ctrl: CascadeCtrl) {
|
pub fn cascade_control(&mut self, ctrl: CascadeCtrl) {
|
||||||
self.tim.reg_block().csd_ctrl().write(|w| {
|
self.tim.reg().csd_ctrl().write(|w| {
|
||||||
w.csden0().bit(ctrl.enb_start_src_csd0);
|
w.csden0().bit(ctrl.enb_start_src_csd0);
|
||||||
w.csdinv0().bit(ctrl.inv_csd0);
|
w.csdinv0().bit(ctrl.inv_csd0);
|
||||||
w.csden1().bit(ctrl.enb_start_src_csd1);
|
w.csden1().bit(ctrl.enb_start_src_csd1);
|
||||||
@ -594,7 +580,7 @@ impl<Tim: ValidTim> CountdownTimer<Tim> {
|
|||||||
pub fn cascade_0_source(&mut self, src: CascadeSource) -> Result<(), InvalidCascadeSourceId> {
|
pub fn cascade_0_source(&mut self, src: CascadeSource) -> Result<(), InvalidCascadeSourceId> {
|
||||||
let id = src.id()?;
|
let id = src.id()?;
|
||||||
self.tim
|
self.tim
|
||||||
.reg_block()
|
.reg()
|
||||||
.cascade0()
|
.cascade0()
|
||||||
.write(|w| unsafe { w.cassel().bits(id) });
|
.write(|w| unsafe { w.cassel().bits(id) });
|
||||||
Ok(())
|
Ok(())
|
||||||
@ -603,7 +589,7 @@ impl<Tim: ValidTim> CountdownTimer<Tim> {
|
|||||||
pub fn cascade_1_source(&mut self, src: CascadeSource) -> Result<(), InvalidCascadeSourceId> {
|
pub fn cascade_1_source(&mut self, src: CascadeSource) -> Result<(), InvalidCascadeSourceId> {
|
||||||
let id = src.id()?;
|
let id = src.id()?;
|
||||||
self.tim
|
self.tim
|
||||||
.reg_block()
|
.reg()
|
||||||
.cascade1()
|
.cascade1()
|
||||||
.write(|w| unsafe { w.cassel().bits(id) });
|
.write(|w| unsafe { w.cassel().bits(id) });
|
||||||
Ok(())
|
Ok(())
|
||||||
@ -612,7 +598,7 @@ impl<Tim: ValidTim> CountdownTimer<Tim> {
|
|||||||
pub fn cascade_2_source(&mut self, src: CascadeSource) -> Result<(), InvalidCascadeSourceId> {
|
pub fn cascade_2_source(&mut self, src: CascadeSource) -> Result<(), InvalidCascadeSourceId> {
|
||||||
let id = src.id()?;
|
let id = src.id()?;
|
||||||
self.tim
|
self.tim
|
||||||
.reg_block()
|
.reg()
|
||||||
.cascade2()
|
.cascade2()
|
||||||
.write(|w| unsafe { w.cassel().bits(id) });
|
.write(|w| unsafe { w.cassel().bits(id) });
|
||||||
Ok(())
|
Ok(())
|
||||||
@ -641,7 +627,7 @@ impl<TIM: ValidTim> CountdownTimer<TIM> {
|
|||||||
/// Return `Ok` if the timer has wrapped. Peripheral will automatically clear the
|
/// Return `Ok` if the timer has wrapped. Peripheral will automatically clear the
|
||||||
/// flag and restart the time if configured correctly
|
/// flag and restart the time if configured correctly
|
||||||
pub fn wait(&mut self) -> nb::Result<(), void::Void> {
|
pub fn wait(&mut self) -> nb::Result<(), void::Void> {
|
||||||
let cnt = self.tim.reg_block().cnt_value().read().bits();
|
let cnt = self.tim.reg().cnt_value().read().bits();
|
||||||
if (cnt > self.last_cnt) || cnt == 0 {
|
if (cnt > self.last_cnt) || cnt == 0 {
|
||||||
self.last_cnt = self.rst_val;
|
self.last_cnt = self.rst_val;
|
||||||
Ok(())
|
Ok(())
|
||||||
@ -653,13 +639,10 @@ impl<TIM: ValidTim> CountdownTimer<TIM> {
|
|||||||
|
|
||||||
/// Returns [false] if the timer was not active, and true otherwise.
|
/// Returns [false] if the timer was not active, and true otherwise.
|
||||||
pub fn cancel(&mut self) -> bool {
|
pub fn cancel(&mut self) -> bool {
|
||||||
if !self.tim.reg_block().ctrl().read().enable().bit_is_set() {
|
if !self.tim.reg().ctrl().read().enable().bit_is_set() {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
self.tim
|
self.tim.reg().ctrl().write(|w| w.enable().clear_bit());
|
||||||
.reg_block()
|
|
||||||
.ctrl()
|
|
||||||
.write(|w| w.enable().clear_bit());
|
|
||||||
true
|
true
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -721,7 +704,7 @@ impl<TIM: ValidTim> embedded_hal::delay::DelayNs for CountdownTimer<TIM> {
|
|||||||
// Set up a millisecond timer on TIM0. Please note that the user still has to provide an IRQ handler
|
// Set up a millisecond timer on TIM0. Please note that the user still has to provide an IRQ handler
|
||||||
// which should call [default_ms_irq_handler].
|
// which should call [default_ms_irq_handler].
|
||||||
pub fn set_up_ms_tick<TIM: ValidTim>(
|
pub fn set_up_ms_tick<TIM: ValidTim>(
|
||||||
irq_cfg: InterruptConfig,
|
irq_cfg: IrqCfg,
|
||||||
sys_cfg: &mut pac::Sysconfig,
|
sys_cfg: &mut pac::Sysconfig,
|
||||||
irq_sel: Option<&mut pac::Irqsel>,
|
irq_sel: Option<&mut pac::Irqsel>,
|
||||||
sys_clk: impl Into<Hertz>,
|
sys_clk: impl Into<Hertz>,
|
||||||
|
File diff suppressed because it is too large
Load Diff
@ -1,440 +0,0 @@
|
|||||||
//! # Async UART reception functionality for the VA416xx family.
|
|
||||||
//!
|
|
||||||
//! This module provides the [RxAsync] and [RxAsyncOverwriting] struct which both implement the
|
|
||||||
//! [embedded_io_async::Read] trait.
|
|
||||||
//! This trait allows for asynchronous reception of data streams. Please note that this module does
|
|
||||||
//! not specify/declare the interrupt handlers which must be provided for async support to work.
|
|
||||||
//! However, it provides two interrupt handlers:
|
|
||||||
//!
|
|
||||||
//! - [on_interrupt_rx]
|
|
||||||
//! - [on_interrupt_rx_overwriting]
|
|
||||||
//!
|
|
||||||
//! The first two are used for the [RxAsync] struct, while the latter two are used with the
|
|
||||||
//! [RxAsyncOverwriting] struct. The later two will overwrite old values in the used ring buffer.
|
|
||||||
//!
|
|
||||||
//! Error handling is performed in the user interrupt handler by checking the [AsyncUartErrors]
|
|
||||||
//! structure returned by the interrupt handlers.
|
|
||||||
//!
|
|
||||||
//! # Example
|
|
||||||
//!
|
|
||||||
//! - [Async UART RX example](https://egit.irs.uni-stuttgart.de/rust/va108xx-rs/src/branch/main/examples/embassy/src/bin/async-uart-rx.rs)
|
|
||||||
use core::{cell::RefCell, convert::Infallible, future::Future, sync::atomic::Ordering};
|
|
||||||
|
|
||||||
use critical_section::Mutex;
|
|
||||||
use embassy_sync::waitqueue::AtomicWaker;
|
|
||||||
use embedded_io::ErrorType;
|
|
||||||
use portable_atomic::AtomicBool;
|
|
||||||
use va108xx::uarta as uart_base;
|
|
||||||
|
|
||||||
use super::{Bank, Instance, Rx, UartErrors};
|
|
||||||
|
|
||||||
static UART_RX_WAKERS: [AtomicWaker; 2] = [const { AtomicWaker::new() }; 2];
|
|
||||||
static RX_READ_ACTIVE: [AtomicBool; 2] = [const { AtomicBool::new(false) }; 2];
|
|
||||||
static RX_HAS_DATA: [AtomicBool; 2] = [const { AtomicBool::new(false) }; 2];
|
|
||||||
|
|
||||||
struct RxFuture {
|
|
||||||
uart_idx: usize,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl RxFuture {
|
|
||||||
pub fn new<Uart: Instance>(_rx: &mut Rx<Uart>) -> Self {
|
|
||||||
RX_READ_ACTIVE[Uart::IDX as usize].store(true, Ordering::Relaxed);
|
|
||||||
Self {
|
|
||||||
uart_idx: Uart::IDX as usize,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Future for RxFuture {
|
|
||||||
type Output = Result<(), Infallible>;
|
|
||||||
|
|
||||||
fn poll(
|
|
||||||
self: core::pin::Pin<&mut Self>,
|
|
||||||
cx: &mut core::task::Context<'_>,
|
|
||||||
) -> core::task::Poll<Self::Output> {
|
|
||||||
UART_RX_WAKERS[self.uart_idx].register(cx.waker());
|
|
||||||
if RX_HAS_DATA[self.uart_idx].load(Ordering::Relaxed) {
|
|
||||||
return core::task::Poll::Ready(Ok(()));
|
|
||||||
}
|
|
||||||
core::task::Poll::Pending
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Debug, Clone, Copy)]
|
|
||||||
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
|
|
||||||
pub struct AsyncUartErrors {
|
|
||||||
/// Queue has overflowed, data might have been lost.
|
|
||||||
pub queue_overflow: bool,
|
|
||||||
/// UART errors.
|
|
||||||
pub uart_errors: UartErrors,
|
|
||||||
}
|
|
||||||
|
|
||||||
fn on_interrupt_handle_rx_errors(uart: &'static uart_base::RegisterBlock) -> Option<UartErrors> {
|
|
||||||
let rx_status = uart.rxstatus().read();
|
|
||||||
if rx_status.rxovr().bit_is_set()
|
|
||||||
|| rx_status.rxfrm().bit_is_set()
|
|
||||||
|| rx_status.rxpar().bit_is_set()
|
|
||||||
{
|
|
||||||
let mut errors_val = UartErrors::default();
|
|
||||||
|
|
||||||
if rx_status.rxovr().bit_is_set() {
|
|
||||||
errors_val.overflow = true;
|
|
||||||
}
|
|
||||||
if rx_status.rxfrm().bit_is_set() {
|
|
||||||
errors_val.framing = true;
|
|
||||||
}
|
|
||||||
if rx_status.rxpar().bit_is_set() {
|
|
||||||
errors_val.parity = true;
|
|
||||||
}
|
|
||||||
return Some(errors_val);
|
|
||||||
}
|
|
||||||
None
|
|
||||||
}
|
|
||||||
|
|
||||||
fn on_interrupt_rx_common_post_processing(
|
|
||||||
bank: Bank,
|
|
||||||
rx_enabled: bool,
|
|
||||||
read_some_data: bool,
|
|
||||||
irq_end: u32,
|
|
||||||
) -> Option<UartErrors> {
|
|
||||||
let idx = bank as usize;
|
|
||||||
if read_some_data {
|
|
||||||
RX_HAS_DATA[idx].store(true, Ordering::Relaxed);
|
|
||||||
if RX_READ_ACTIVE[idx].load(Ordering::Relaxed) {
|
|
||||||
UART_RX_WAKERS[idx].wake();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
let mut errors = None;
|
|
||||||
let uart_regs = unsafe { bank.reg_block() };
|
|
||||||
// Check for RX errors
|
|
||||||
if rx_enabled {
|
|
||||||
errors = on_interrupt_handle_rx_errors(uart_regs);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Clear the interrupt status bits
|
|
||||||
uart_regs.irq_clr().write(|w| unsafe { w.bits(irq_end) });
|
|
||||||
errors
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Interrupt handler with overwriting behaviour when the ring buffer is full.
|
|
||||||
///
|
|
||||||
/// Should be called in the user interrupt handler to enable
|
|
||||||
/// asynchronous reception. This variant will overwrite old data in the ring buffer in case
|
|
||||||
/// the ring buffer is full.
|
|
||||||
pub fn on_interrupt_rx_overwriting<const N: usize>(
|
|
||||||
bank: Bank,
|
|
||||||
prod: &mut heapless::spsc::Producer<u8, N>,
|
|
||||||
shared_consumer: &Mutex<RefCell<Option<heapless::spsc::Consumer<'static, u8, N>>>>,
|
|
||||||
) -> Result<(), AsyncUartErrors> {
|
|
||||||
on_interrupt_rx_async_heapless_queue_overwriting(bank, prod, shared_consumer)
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn on_interrupt_rx_async_heapless_queue_overwriting<const N: usize>(
|
|
||||||
bank: Bank,
|
|
||||||
prod: &mut heapless::spsc::Producer<u8, N>,
|
|
||||||
shared_consumer: &Mutex<RefCell<Option<heapless::spsc::Consumer<'static, u8, N>>>>,
|
|
||||||
) -> Result<(), AsyncUartErrors> {
|
|
||||||
let uart_regs = unsafe { bank.reg_block() };
|
|
||||||
let irq_end = uart_regs.irq_end().read();
|
|
||||||
let enb_status = uart_regs.enable().read();
|
|
||||||
let rx_enabled = enb_status.rxenable().bit_is_set();
|
|
||||||
let mut read_some_data = false;
|
|
||||||
let mut queue_overflow = false;
|
|
||||||
|
|
||||||
// Half-Full interrupt. We have a guaranteed amount of data we can read.
|
|
||||||
if irq_end.irq_rx().bit_is_set() {
|
|
||||||
let available_bytes = uart_regs.rxfifoirqtrg().read().bits() as usize;
|
|
||||||
|
|
||||||
// If this interrupt bit is set, the trigger level is available at the very least.
|
|
||||||
// Read everything as fast as possible
|
|
||||||
for _ in 0..available_bytes {
|
|
||||||
let byte = uart_regs.data().read().bits();
|
|
||||||
if !prod.ready() {
|
|
||||||
queue_overflow = true;
|
|
||||||
critical_section::with(|cs| {
|
|
||||||
let mut cons_ref = shared_consumer.borrow(cs).borrow_mut();
|
|
||||||
cons_ref.as_mut().unwrap().dequeue();
|
|
||||||
});
|
|
||||||
}
|
|
||||||
prod.enqueue(byte as u8).ok();
|
|
||||||
}
|
|
||||||
read_some_data = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Timeout, empty the FIFO completely.
|
|
||||||
if irq_end.irq_rx_to().bit_is_set() {
|
|
||||||
while uart_regs.rxstatus().read().rdavl().bit_is_set() {
|
|
||||||
// While there is data in the FIFO, write it into the reception buffer
|
|
||||||
let byte = uart_regs.data().read().bits();
|
|
||||||
if !prod.ready() {
|
|
||||||
queue_overflow = true;
|
|
||||||
critical_section::with(|cs| {
|
|
||||||
let mut cons_ref = shared_consumer.borrow(cs).borrow_mut();
|
|
||||||
cons_ref.as_mut().unwrap().dequeue();
|
|
||||||
});
|
|
||||||
}
|
|
||||||
prod.enqueue(byte as u8).ok();
|
|
||||||
}
|
|
||||||
read_some_data = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
let uart_errors =
|
|
||||||
on_interrupt_rx_common_post_processing(bank, rx_enabled, read_some_data, irq_end.bits());
|
|
||||||
if uart_errors.is_some() || queue_overflow {
|
|
||||||
return Err(AsyncUartErrors {
|
|
||||||
queue_overflow,
|
|
||||||
uart_errors: uart_errors.unwrap_or_default(),
|
|
||||||
});
|
|
||||||
}
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Interrupt handler for asynchronous RX operations.
|
|
||||||
///
|
|
||||||
/// Should be called in the user interrupt handler to enable asynchronous reception.
|
|
||||||
pub fn on_interrupt_rx<const N: usize>(
|
|
||||||
bank: Bank,
|
|
||||||
prod: &mut heapless::spsc::Producer<'_, u8, N>,
|
|
||||||
) -> Result<(), AsyncUartErrors> {
|
|
||||||
on_interrupt_rx_async_heapless_queue(bank, prod)
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn on_interrupt_rx_async_heapless_queue<const N: usize>(
|
|
||||||
bank: Bank,
|
|
||||||
prod: &mut heapless::spsc::Producer<'_, u8, N>,
|
|
||||||
) -> Result<(), AsyncUartErrors> {
|
|
||||||
let uart = unsafe { bank.reg_block() };
|
|
||||||
let irq_end = uart.irq_end().read();
|
|
||||||
let enb_status = uart.enable().read();
|
|
||||||
let rx_enabled = enb_status.rxenable().bit_is_set();
|
|
||||||
let mut read_some_data = false;
|
|
||||||
let mut queue_overflow = false;
|
|
||||||
|
|
||||||
// Half-Full interrupt. We have a guaranteed amount of data we can read.
|
|
||||||
if irq_end.irq_rx().bit_is_set() {
|
|
||||||
let available_bytes = uart.rxfifoirqtrg().read().bits() as usize;
|
|
||||||
|
|
||||||
// If this interrupt bit is set, the trigger level is available at the very least.
|
|
||||||
// Read everything as fast as possible
|
|
||||||
for _ in 0..available_bytes {
|
|
||||||
let byte = uart.data().read().bits();
|
|
||||||
if !prod.ready() {
|
|
||||||
queue_overflow = true;
|
|
||||||
}
|
|
||||||
prod.enqueue(byte as u8).ok();
|
|
||||||
}
|
|
||||||
read_some_data = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Timeout, empty the FIFO completely.
|
|
||||||
if irq_end.irq_rx_to().bit_is_set() {
|
|
||||||
while uart.rxstatus().read().rdavl().bit_is_set() {
|
|
||||||
// While there is data in the FIFO, write it into the reception buffer
|
|
||||||
let byte = uart.data().read().bits();
|
|
||||||
if !prod.ready() {
|
|
||||||
queue_overflow = true;
|
|
||||||
}
|
|
||||||
prod.enqueue(byte as u8).ok();
|
|
||||||
}
|
|
||||||
read_some_data = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
let uart_errors =
|
|
||||||
on_interrupt_rx_common_post_processing(bank, rx_enabled, read_some_data, irq_end.bits());
|
|
||||||
if uart_errors.is_some() || queue_overflow {
|
|
||||||
return Err(AsyncUartErrors {
|
|
||||||
queue_overflow,
|
|
||||||
uart_errors: uart_errors.unwrap_or_default(),
|
|
||||||
});
|
|
||||||
}
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
|
|
||||||
struct ActiveReadGuard(usize);
|
|
||||||
|
|
||||||
impl Drop for ActiveReadGuard {
|
|
||||||
fn drop(&mut self) {
|
|
||||||
RX_READ_ACTIVE[self.0].store(false, Ordering::Relaxed);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
struct RxAsyncInner<Uart: Instance, const N: usize> {
|
|
||||||
rx: Rx<Uart>,
|
|
||||||
pub queue: heapless::spsc::Consumer<'static, u8, N>,
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Core data structure to allow asynchronous UART reception.
|
|
||||||
///
|
|
||||||
/// If the ring buffer becomes full, data will be lost.
|
|
||||||
pub struct RxAsync<Uart: Instance, const N: usize>(Option<RxAsyncInner<Uart, N>>);
|
|
||||||
|
|
||||||
impl<Uart: Instance, const N: usize> ErrorType for RxAsync<Uart, N> {
|
|
||||||
/// Error reporting is done using the result of the interrupt functions.
|
|
||||||
type Error = Infallible;
|
|
||||||
}
|
|
||||||
|
|
||||||
fn stop_async_rx<Uart: Instance>(rx: &mut Rx<Uart>) {
|
|
||||||
rx.disable_interrupts();
|
|
||||||
rx.disable();
|
|
||||||
rx.clear_fifo();
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<Uart: Instance, const N: usize> RxAsync<Uart, N> {
|
|
||||||
/// Create a new asynchronous receiver.
|
|
||||||
///
|
|
||||||
/// The passed [heapless::spsc::Consumer] will be used to asynchronously receive data which
|
|
||||||
/// is filled by the interrupt handler [on_interrupt_rx].
|
|
||||||
pub fn new(mut rx: Rx<Uart>, queue: heapless::spsc::Consumer<'static, u8, N>) -> Self {
|
|
||||||
rx.disable_interrupts();
|
|
||||||
rx.disable();
|
|
||||||
rx.clear_fifo();
|
|
||||||
// Enable those together.
|
|
||||||
critical_section::with(|_| {
|
|
||||||
rx.enable_interrupts();
|
|
||||||
rx.enable();
|
|
||||||
});
|
|
||||||
Self(Some(RxAsyncInner { rx, queue }))
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn stop(&mut self) {
|
|
||||||
stop_async_rx(&mut self.0.as_mut().unwrap().rx);
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn release(mut self) -> (Rx<Uart>, heapless::spsc::Consumer<'static, u8, N>) {
|
|
||||||
self.stop();
|
|
||||||
let inner = self.0.take().unwrap();
|
|
||||||
(inner.rx, inner.queue)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<Uart: Instance, const N: usize> Drop for RxAsync<Uart, N> {
|
|
||||||
fn drop(&mut self) {
|
|
||||||
self.stop();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<Uart: Instance, const N: usize> embedded_io_async::Read for RxAsync<Uart, N> {
|
|
||||||
async fn read(&mut self, buf: &mut [u8]) -> Result<usize, Self::Error> {
|
|
||||||
// Need to wait for the IRQ to read data and set this flag. If the queue is not
|
|
||||||
// empty, we can read data immediately.
|
|
||||||
if self.0.as_ref().unwrap().queue.len() == 0 {
|
|
||||||
RX_HAS_DATA[Uart::IDX as usize].store(false, Ordering::Relaxed);
|
|
||||||
}
|
|
||||||
let _guard = ActiveReadGuard(Uart::IDX as usize);
|
|
||||||
let mut handle_data_in_queue = |consumer: &mut heapless::spsc::Consumer<'static, u8, N>| {
|
|
||||||
let data_to_read = consumer.len().min(buf.len());
|
|
||||||
for byte in buf.iter_mut().take(data_to_read) {
|
|
||||||
// We own the consumer and we checked that the amount of data is guaranteed to be available.
|
|
||||||
*byte = unsafe { consumer.dequeue_unchecked() };
|
|
||||||
}
|
|
||||||
data_to_read
|
|
||||||
};
|
|
||||||
let mut_ref = self.0.as_mut().unwrap();
|
|
||||||
let fut = RxFuture::new(&mut mut_ref.rx);
|
|
||||||
// Data is available, so read that data immediately.
|
|
||||||
let read_data = handle_data_in_queue(&mut mut_ref.queue);
|
|
||||||
if read_data > 0 {
|
|
||||||
return Ok(read_data);
|
|
||||||
}
|
|
||||||
// Await data.
|
|
||||||
let _ = fut.await;
|
|
||||||
Ok(handle_data_in_queue(&mut mut_ref.queue))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
struct RxAsyncOverwritingInner<Uart: Instance, const N: usize> {
|
|
||||||
rx: Rx<Uart>,
|
|
||||||
pub shared_consumer: &'static Mutex<RefCell<Option<heapless::spsc::Consumer<'static, u8, N>>>>,
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Core data structure to allow asynchronous UART reception.
|
|
||||||
///
|
|
||||||
/// If the ring buffer becomes full, the oldest data will be overwritten when using the
|
|
||||||
/// [on_interrupt_rx_overwriting] interrupt handlers.
|
|
||||||
pub struct RxAsyncOverwriting<Uart: Instance, const N: usize>(
|
|
||||||
Option<RxAsyncOverwritingInner<Uart, N>>,
|
|
||||||
);
|
|
||||||
|
|
||||||
impl<Uart: Instance, const N: usize> ErrorType for RxAsyncOverwriting<Uart, N> {
|
|
||||||
/// Error reporting is done using the result of the interrupt functions.
|
|
||||||
type Error = Infallible;
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<Uart: Instance, const N: usize> RxAsyncOverwriting<Uart, N> {
|
|
||||||
/// Create a new asynchronous receiver.
|
|
||||||
///
|
|
||||||
/// The passed shared [heapless::spsc::Consumer] will be used to asynchronously receive data
|
|
||||||
/// which is filled by the interrupt handler. The shared property allows using it in the
|
|
||||||
/// interrupt handler to overwrite old data.
|
|
||||||
pub fn new(
|
|
||||||
mut rx: Rx<Uart>,
|
|
||||||
shared_consumer: &'static Mutex<RefCell<Option<heapless::spsc::Consumer<'static, u8, N>>>>,
|
|
||||||
) -> Self {
|
|
||||||
rx.disable_interrupts();
|
|
||||||
rx.disable();
|
|
||||||
rx.clear_fifo();
|
|
||||||
// Enable those together.
|
|
||||||
critical_section::with(|_| {
|
|
||||||
rx.enable_interrupts();
|
|
||||||
rx.enable();
|
|
||||||
});
|
|
||||||
Self(Some(RxAsyncOverwritingInner {
|
|
||||||
rx,
|
|
||||||
shared_consumer,
|
|
||||||
}))
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn stop(&mut self) {
|
|
||||||
stop_async_rx(&mut self.0.as_mut().unwrap().rx);
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn release(mut self) -> Rx<Uart> {
|
|
||||||
self.stop();
|
|
||||||
let inner = self.0.take().unwrap();
|
|
||||||
inner.rx
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<Uart: Instance, const N: usize> Drop for RxAsyncOverwriting<Uart, N> {
|
|
||||||
fn drop(&mut self) {
|
|
||||||
self.stop();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<Uart: Instance, const N: usize> embedded_io_async::Read for RxAsyncOverwriting<Uart, N> {
|
|
||||||
async fn read(&mut self, buf: &mut [u8]) -> Result<usize, Self::Error> {
|
|
||||||
// Need to wait for the IRQ to read data and set this flag. If the queue is not
|
|
||||||
// empty, we can read data immediately.
|
|
||||||
|
|
||||||
critical_section::with(|cs| {
|
|
||||||
let queue = self.0.as_ref().unwrap().shared_consumer.borrow(cs);
|
|
||||||
if queue.borrow().as_ref().unwrap().len() == 0 {
|
|
||||||
RX_HAS_DATA[Uart::IDX as usize].store(false, Ordering::Relaxed);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
let _guard = ActiveReadGuard(Uart::IDX as usize);
|
|
||||||
let mut handle_data_in_queue = |inner: &mut RxAsyncOverwritingInner<Uart, N>| {
|
|
||||||
critical_section::with(|cs| {
|
|
||||||
let mut consumer_ref = inner.shared_consumer.borrow(cs).borrow_mut();
|
|
||||||
let consumer = consumer_ref.as_mut().unwrap();
|
|
||||||
let data_to_read = consumer.len().min(buf.len());
|
|
||||||
for byte in buf.iter_mut().take(data_to_read) {
|
|
||||||
// We own the consumer and we checked that the amount of data is guaranteed to be available.
|
|
||||||
*byte = unsafe { consumer.dequeue_unchecked() };
|
|
||||||
}
|
|
||||||
data_to_read
|
|
||||||
})
|
|
||||||
};
|
|
||||||
let fut = RxFuture::new(&mut self.0.as_mut().unwrap().rx);
|
|
||||||
// Data is available, so read that data immediately.
|
|
||||||
let read_data = handle_data_in_queue(self.0.as_mut().unwrap());
|
|
||||||
if read_data > 0 {
|
|
||||||
return Ok(read_data);
|
|
||||||
}
|
|
||||||
// Await data.
|
|
||||||
let _ = fut.await;
|
|
||||||
let read_data = handle_data_in_queue(self.0.as_mut().unwrap());
|
|
||||||
Ok(read_data)
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,254 +0,0 @@
|
|||||||
//! # Async UART transmission functionality for the VA108xx family.
|
|
||||||
//!
|
|
||||||
//! This module provides the [TxAsync] struct which implements the [embedded_io_async::Write] trait.
|
|
||||||
//! This trait allows for asynchronous sending of data streams. Please note that this module does
|
|
||||||
//! not specify/declare the interrupt handlers which must be provided for async support to work.
|
|
||||||
//! However, it the [on_interrupt_tx] interrupt handler.
|
|
||||||
//!
|
|
||||||
//! This handler should be called in ALL user interrupt handlers which handle UART TX interrupts
|
|
||||||
//! for a given UART bank.
|
|
||||||
//!
|
|
||||||
//! # Example
|
|
||||||
//!
|
|
||||||
//! - [Async UART TX example](https://egit.irs.uni-stuttgart.de/rust/va108xx-rs/src/branch/main/examples/embassy/src/bin/async-uart-tx.rs)
|
|
||||||
use core::{cell::RefCell, future::Future};
|
|
||||||
|
|
||||||
use critical_section::Mutex;
|
|
||||||
use embassy_sync::waitqueue::AtomicWaker;
|
|
||||||
use embedded_io_async::Write;
|
|
||||||
use portable_atomic::AtomicBool;
|
|
||||||
|
|
||||||
use super::*;
|
|
||||||
|
|
||||||
static UART_TX_WAKERS: [AtomicWaker; 2] = [const { AtomicWaker::new() }; 2];
|
|
||||||
static TX_CONTEXTS: [Mutex<RefCell<TxContext>>; 2] =
|
|
||||||
[const { Mutex::new(RefCell::new(TxContext::new())) }; 2];
|
|
||||||
// Completion flag. Kept outside of the context structure as an atomic to avoid
|
|
||||||
// critical section.
|
|
||||||
static TX_DONE: [AtomicBool; 2] = [const { AtomicBool::new(false) }; 2];
|
|
||||||
|
|
||||||
/// This is a generic interrupt handler to handle asynchronous UART TX operations for a given
|
|
||||||
/// UART bank.
|
|
||||||
///
|
|
||||||
/// The user has to call this once in the interrupt handler responsible for the TX interrupts on
|
|
||||||
/// the given UART bank.
|
|
||||||
pub fn on_interrupt_tx(bank: Bank) {
|
|
||||||
let uart = unsafe { bank.reg_block() };
|
|
||||||
let idx = bank as usize;
|
|
||||||
let irq_enb = uart.irq_enb().read();
|
|
||||||
// IRQ is not related to TX.
|
|
||||||
if irq_enb.irq_tx().bit_is_clear() || irq_enb.irq_tx_empty().bit_is_clear() {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
let tx_status = uart.txstatus().read();
|
|
||||||
let unexpected_overrun = tx_status.wrlost().bit_is_set();
|
|
||||||
let mut context = critical_section::with(|cs| {
|
|
||||||
let context_ref = TX_CONTEXTS[idx].borrow(cs);
|
|
||||||
*context_ref.borrow()
|
|
||||||
});
|
|
||||||
context.tx_overrun = unexpected_overrun;
|
|
||||||
if context.progress >= context.slice.len && !tx_status.wrbusy().bit_is_set() {
|
|
||||||
uart.irq_enb().modify(|_, w| {
|
|
||||||
w.irq_tx().clear_bit();
|
|
||||||
w.irq_tx_empty().clear_bit();
|
|
||||||
w.irq_tx_status().clear_bit()
|
|
||||||
});
|
|
||||||
uart.enable().modify(|_, w| w.txenable().clear_bit());
|
|
||||||
// Write back updated context structure.
|
|
||||||
critical_section::with(|cs| {
|
|
||||||
let context_ref = TX_CONTEXTS[idx].borrow(cs);
|
|
||||||
*context_ref.borrow_mut() = context;
|
|
||||||
});
|
|
||||||
// Transfer is done.
|
|
||||||
TX_DONE[idx].store(true, core::sync::atomic::Ordering::Relaxed);
|
|
||||||
UART_TX_WAKERS[idx].wake();
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
// Safety: We documented that the user provided slice must outlive the future, so we convert
|
|
||||||
// the raw pointer back to the slice here.
|
|
||||||
let slice = unsafe { core::slice::from_raw_parts(context.slice.data, context.slice.len) };
|
|
||||||
while context.progress < context.slice.len {
|
|
||||||
let wrrdy = uart.txstatus().read().wrrdy().bit_is_set();
|
|
||||||
if !wrrdy {
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
// Safety: TX structure is owned by the future which does not write into the the data
|
|
||||||
// register, so we can assume we are the only one writing to the data register.
|
|
||||||
uart.data()
|
|
||||||
.write(|w| unsafe { w.bits(slice[context.progress] as u32) });
|
|
||||||
context.progress += 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Write back updated context structure.
|
|
||||||
critical_section::with(|cs| {
|
|
||||||
let context_ref = TX_CONTEXTS[idx].borrow(cs);
|
|
||||||
*context_ref.borrow_mut() = context;
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Debug, Copy, Clone)]
|
|
||||||
pub struct TxContext {
|
|
||||||
progress: usize,
|
|
||||||
tx_overrun: bool,
|
|
||||||
slice: RawBufSlice,
|
|
||||||
}
|
|
||||||
|
|
||||||
#[allow(clippy::new_without_default)]
|
|
||||||
impl TxContext {
|
|
||||||
pub const fn new() -> Self {
|
|
||||||
Self {
|
|
||||||
progress: 0,
|
|
||||||
tx_overrun: false,
|
|
||||||
slice: RawBufSlice::new_empty(),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Debug, Copy, Clone)]
|
|
||||||
struct RawBufSlice {
|
|
||||||
data: *const u8,
|
|
||||||
len: usize,
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Safety: This type MUST be used with mutex to ensure concurrent access is valid.
|
|
||||||
unsafe impl Send for RawBufSlice {}
|
|
||||||
|
|
||||||
impl RawBufSlice {
|
|
||||||
/// # Safety
|
|
||||||
///
|
|
||||||
/// This function stores the raw pointer of the passed data slice. The user MUST ensure
|
|
||||||
/// that the slice outlives the data structure.
|
|
||||||
#[allow(dead_code)]
|
|
||||||
const unsafe fn new(data: &[u8]) -> Self {
|
|
||||||
Self {
|
|
||||||
data: data.as_ptr(),
|
|
||||||
len: data.len(),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
const fn new_empty() -> Self {
|
|
||||||
Self {
|
|
||||||
data: core::ptr::null(),
|
|
||||||
len: 0,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// # Safety
|
|
||||||
///
|
|
||||||
/// This function stores the raw pointer of the passed data slice. The user MUST ensure
|
|
||||||
/// that the slice outlives the data structure.
|
|
||||||
pub unsafe fn set(&mut self, data: &[u8]) {
|
|
||||||
self.data = data.as_ptr();
|
|
||||||
self.len = data.len();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub struct TxFuture {
|
|
||||||
uart_idx: usize,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl TxFuture {
|
|
||||||
/// # Safety
|
|
||||||
///
|
|
||||||
/// This function stores the raw pointer of the passed data slice. The user MUST ensure
|
|
||||||
/// that the slice outlives the data structure.
|
|
||||||
pub unsafe fn new<Uart: Instance>(tx: &mut Tx<Uart>, data: &[u8]) -> Self {
|
|
||||||
TX_DONE[Uart::IDX as usize].store(false, core::sync::atomic::Ordering::Relaxed);
|
|
||||||
tx.disable_interrupts();
|
|
||||||
tx.disable();
|
|
||||||
tx.clear_fifo();
|
|
||||||
|
|
||||||
let uart_tx = unsafe { tx.uart() };
|
|
||||||
let init_fill_count = core::cmp::min(data.len(), 16);
|
|
||||||
// We fill the FIFO.
|
|
||||||
for data in data.iter().take(init_fill_count) {
|
|
||||||
uart_tx.data().write(|w| unsafe { w.bits(*data as u32) });
|
|
||||||
}
|
|
||||||
critical_section::with(|cs| {
|
|
||||||
let context_ref = TX_CONTEXTS[Uart::IDX as usize].borrow(cs);
|
|
||||||
let mut context = context_ref.borrow_mut();
|
|
||||||
context.slice.set(data);
|
|
||||||
context.progress = init_fill_count;
|
|
||||||
|
|
||||||
// Ensure those are enabled inside a critical section at the same time. Can lead to
|
|
||||||
// weird glitches otherwise.
|
|
||||||
tx.enable_interrupts();
|
|
||||||
tx.enable();
|
|
||||||
});
|
|
||||||
Self {
|
|
||||||
uart_idx: Uart::IDX as usize,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Future for TxFuture {
|
|
||||||
type Output = Result<usize, TxOverrunError>;
|
|
||||||
|
|
||||||
fn poll(
|
|
||||||
self: core::pin::Pin<&mut Self>,
|
|
||||||
cx: &mut core::task::Context<'_>,
|
|
||||||
) -> core::task::Poll<Self::Output> {
|
|
||||||
UART_TX_WAKERS[self.uart_idx].register(cx.waker());
|
|
||||||
if TX_DONE[self.uart_idx].swap(false, core::sync::atomic::Ordering::Relaxed) {
|
|
||||||
let progress = critical_section::with(|cs| {
|
|
||||||
TX_CONTEXTS[self.uart_idx].borrow(cs).borrow().progress
|
|
||||||
});
|
|
||||||
return core::task::Poll::Ready(Ok(progress));
|
|
||||||
}
|
|
||||||
core::task::Poll::Pending
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Drop for TxFuture {
|
|
||||||
fn drop(&mut self) {
|
|
||||||
let reg_block = match self.uart_idx {
|
|
||||||
0 => unsafe { pac::Uarta::reg_block() },
|
|
||||||
1 => unsafe { pac::Uartb::reg_block() },
|
|
||||||
_ => unreachable!(),
|
|
||||||
};
|
|
||||||
|
|
||||||
disable_tx_interrupts(reg_block);
|
|
||||||
disable_tx(reg_block);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub struct TxAsync<Uart: Instance> {
|
|
||||||
tx: Tx<Uart>,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<Uart: Instance> TxAsync<Uart> {
|
|
||||||
pub fn new(tx: Tx<Uart>) -> Self {
|
|
||||||
Self { tx }
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn release(self) -> Tx<Uart> {
|
|
||||||
self.tx
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Debug, thiserror::Error)]
|
|
||||||
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
|
|
||||||
#[error("TX overrun error")]
|
|
||||||
pub struct TxOverrunError;
|
|
||||||
|
|
||||||
impl embedded_io_async::Error for TxOverrunError {
|
|
||||||
fn kind(&self) -> embedded_io_async::ErrorKind {
|
|
||||||
embedded_io_async::ErrorKind::Other
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<Uart: Instance> embedded_io::ErrorType for TxAsync<Uart> {
|
|
||||||
type Error = TxOverrunError;
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<Uart: Instance> Write for TxAsync<Uart> {
|
|
||||||
/// Write a buffer asynchronously.
|
|
||||||
///
|
|
||||||
/// This implementation is not side effect free, and a started future might have already
|
|
||||||
/// written part of the passed buffer.
|
|
||||||
async fn write(&mut self, buf: &[u8]) -> Result<usize, Self::Error> {
|
|
||||||
let fut = unsafe { TxFuture::new(&mut self.tx, buf) };
|
|
||||||
fut.await
|
|
||||||
}
|
|
||||||
}
|
|
@ -8,11 +8,7 @@ and this project adheres to [Semantic Versioning](http://semver.org/).
|
|||||||
|
|
||||||
## [unreleased]
|
## [unreleased]
|
||||||
|
|
||||||
## [v0.5.0] 2025-02-17
|
## [v0.4.0]
|
||||||
|
|
||||||
- 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
|
- Re-generated PAC with `svd2rust` v0.35.0
|
||||||
|
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
[package]
|
[package]
|
||||||
name = "va108xx"
|
name = "va108xx"
|
||||||
version = "0.5.0"
|
version = "0.4.0"
|
||||||
authors = ["Robin Mueller <muellerr@irs.uni-stuttgart.de>"]
|
authors = ["Robin Mueller <muellerr@irs.uni-stuttgart.de>"]
|
||||||
edition = "2021"
|
edition = "2021"
|
||||||
description = "PAC for the Vorago VA108xx family of microcontrollers"
|
description = "PAC for the Vorago VA108xx family of microcontrollers"
|
||||||
@ -13,7 +13,6 @@ categories = ["embedded", "no-std", "hardware-support"]
|
|||||||
[dependencies]
|
[dependencies]
|
||||||
cortex-m = "0.7"
|
cortex-m = "0.7"
|
||||||
vcell = "0.1.3"
|
vcell = "0.1.3"
|
||||||
defmt = { version = "0.3", optional = true }
|
|
||||||
critical-section = { version = "1", optional = true }
|
critical-section = { version = "1", optional = true }
|
||||||
|
|
||||||
[dependencies.cortex-m-rt]
|
[dependencies.cortex-m-rt]
|
||||||
@ -22,8 +21,6 @@ version = ">=0.6.15,<0.8"
|
|||||||
|
|
||||||
[features]
|
[features]
|
||||||
rt = ["cortex-m-rt/device"]
|
rt = ["cortex-m-rt/device"]
|
||||||
# Adds Debug implementation
|
|
||||||
debug = []
|
|
||||||
|
|
||||||
[package.metadata.docs.rs]
|
[package.metadata.docs.rs]
|
||||||
all-features = true
|
all-features = true
|
||||||
|
@ -26,12 +26,6 @@ The `rt` feature is optional and recommended. It brings in support for `cortex-m
|
|||||||
For full details on the autgenerated API, please see the
|
For full details on the autgenerated API, please see the
|
||||||
[svd2rust documentation](https://docs.rs/svd2rust/latest/svd2rust/#peripheral-api).
|
[svd2rust documentation](https://docs.rs/svd2rust/latest/svd2rust/#peripheral-api).
|
||||||
|
|
||||||
## Optional Features
|
|
||||||
|
|
||||||
- [`defmt`](https://defmt.ferrous-systems.com/): Add support for `defmt` by adding the
|
|
||||||
[`defmt::Format`](https://defmt.ferrous-systems.com/format) derive on many types.
|
|
||||||
- `debug`: Add `Debug` derives for various structures
|
|
||||||
|
|
||||||
## Regenerating the PAC
|
## Regenerating the PAC
|
||||||
|
|
||||||
If you want to re-generate the PAC, for example if the register file `va416xx.svd` changes
|
If you want to re-generate the PAC, for example if the register file `va416xx.svd` changes
|
||||||
|
@ -1,3 +0,0 @@
|
|||||||
#!/bin/sh
|
|
||||||
export RUSTDOCFLAGS="--cfg docsrs --generate-link-to-definition -Z unstable-options"
|
|
||||||
cargo +nightly doc --all-features --open
|
|
@ -30,7 +30,7 @@ fi
|
|||||||
|
|
||||||
svdtools patch svd/va108xx-patch.yml
|
svdtools patch svd/va108xx-patch.yml
|
||||||
# See https://github.com/rust-embedded/svd2rust/issues/830 for required re-export.
|
# See https://github.com/rust-embedded/svd2rust/issues/830 for required re-export.
|
||||||
${svd2rust_bin} --reexport-interrupt --impl-defmt defmt --impl-debug-feature debug -i svd/va108xx.svd.patched
|
${svd2rust_bin} --reexport-interrupt -i svd/va108xx.svd.patched
|
||||||
|
|
||||||
result=$?
|
result=$?
|
||||||
if [ $result -ne 0 ]; then
|
if [ $result -ne 0 ]; then
|
||||||
|
@ -2,7 +2,6 @@
|
|||||||
pub type R = crate::R<AddressSpec>;
|
pub type R = crate::R<AddressSpec>;
|
||||||
#[doc = "Register `ADDRESS` writer"]
|
#[doc = "Register `ADDRESS` writer"]
|
||||||
pub type W = crate::W<AddressSpec>;
|
pub type W = crate::W<AddressSpec>;
|
||||||
#[cfg(feature = "debug")]
|
|
||||||
impl core::fmt::Debug for R {
|
impl core::fmt::Debug for R {
|
||||||
fn fmt(&self, f: &mut core::fmt::Formatter) -> core::fmt::Result {
|
fn fmt(&self, f: &mut core::fmt::Formatter) -> core::fmt::Result {
|
||||||
write!(f, "{}", self.bits())
|
write!(f, "{}", self.bits())
|
||||||
|
@ -2,7 +2,6 @@
|
|||||||
pub type R = crate::R<ClktolimitSpec>;
|
pub type R = crate::R<ClktolimitSpec>;
|
||||||
#[doc = "Register `CLKTOLIMIT` writer"]
|
#[doc = "Register `CLKTOLIMIT` writer"]
|
||||||
pub type W = crate::W<ClktolimitSpec>;
|
pub type W = crate::W<ClktolimitSpec>;
|
||||||
#[cfg(feature = "debug")]
|
|
||||||
impl core::fmt::Debug for R {
|
impl core::fmt::Debug for R {
|
||||||
fn fmt(&self, f: &mut core::fmt::Formatter) -> core::fmt::Result {
|
fn fmt(&self, f: &mut core::fmt::Formatter) -> core::fmt::Result {
|
||||||
write!(f, "{}", self.bits())
|
write!(f, "{}", self.bits())
|
||||||
|
@ -2,7 +2,6 @@
|
|||||||
pub type R = crate::R<CmdSpec>;
|
pub type R = crate::R<CmdSpec>;
|
||||||
#[doc = "Register `CMD` writer"]
|
#[doc = "Register `CMD` writer"]
|
||||||
pub type W = crate::W<CmdSpec>;
|
pub type W = crate::W<CmdSpec>;
|
||||||
#[cfg(feature = "debug")]
|
|
||||||
impl core::fmt::Debug for R {
|
impl core::fmt::Debug for R {
|
||||||
fn fmt(&self, f: &mut core::fmt::Formatter) -> core::fmt::Result {
|
fn fmt(&self, f: &mut core::fmt::Formatter) -> core::fmt::Result {
|
||||||
write!(f, "{}", self.bits())
|
write!(f, "{}", self.bits())
|
||||||
|
@ -2,7 +2,6 @@
|
|||||||
pub type R = crate::R<DataSpec>;
|
pub type R = crate::R<DataSpec>;
|
||||||
#[doc = "Register `DATA` writer"]
|
#[doc = "Register `DATA` writer"]
|
||||||
pub type W = crate::W<DataSpec>;
|
pub type W = crate::W<DataSpec>;
|
||||||
#[cfg(feature = "debug")]
|
|
||||||
impl core::fmt::Debug for R {
|
impl core::fmt::Debug for R {
|
||||||
fn fmt(&self, f: &mut core::fmt::Formatter) -> core::fmt::Result {
|
fn fmt(&self, f: &mut core::fmt::Formatter) -> core::fmt::Result {
|
||||||
write!(f, "{}", self.bits())
|
write!(f, "{}", self.bits())
|
||||||
|
@ -1,6 +1,5 @@
|
|||||||
#[doc = "Register `PERID` reader"]
|
#[doc = "Register `PERID` reader"]
|
||||||
pub type R = crate::R<PeridSpec>;
|
pub type R = crate::R<PeridSpec>;
|
||||||
#[cfg(feature = "debug")]
|
|
||||||
impl core::fmt::Debug for R {
|
impl core::fmt::Debug for R {
|
||||||
fn fmt(&self, f: &mut core::fmt::Formatter) -> core::fmt::Result {
|
fn fmt(&self, f: &mut core::fmt::Formatter) -> core::fmt::Result {
|
||||||
write!(f, "{}", self.bits())
|
write!(f, "{}", self.bits())
|
||||||
|
@ -1,6 +1,5 @@
|
|||||||
#[doc = "Register `RXCOUNT` reader"]
|
#[doc = "Register `RXCOUNT` reader"]
|
||||||
pub type R = crate::R<RxcountSpec>;
|
pub type R = crate::R<RxcountSpec>;
|
||||||
#[cfg(feature = "debug")]
|
|
||||||
impl core::fmt::Debug for R {
|
impl core::fmt::Debug for R {
|
||||||
fn fmt(&self, f: &mut core::fmt::Formatter) -> core::fmt::Result {
|
fn fmt(&self, f: &mut core::fmt::Formatter) -> core::fmt::Result {
|
||||||
write!(f, "{}", self.bits())
|
write!(f, "{}", self.bits())
|
||||||
|
@ -2,7 +2,6 @@
|
|||||||
pub type R = crate::R<RxfifoirqtrgSpec>;
|
pub type R = crate::R<RxfifoirqtrgSpec>;
|
||||||
#[doc = "Register `RXFIFOIRQTRG` writer"]
|
#[doc = "Register `RXFIFOIRQTRG` writer"]
|
||||||
pub type W = crate::W<RxfifoirqtrgSpec>;
|
pub type W = crate::W<RxfifoirqtrgSpec>;
|
||||||
#[cfg(feature = "debug")]
|
|
||||||
impl core::fmt::Debug for R {
|
impl core::fmt::Debug for R {
|
||||||
fn fmt(&self, f: &mut core::fmt::Formatter) -> core::fmt::Result {
|
fn fmt(&self, f: &mut core::fmt::Formatter) -> core::fmt::Result {
|
||||||
write!(f, "{}", self.bits())
|
write!(f, "{}", self.bits())
|
||||||
|
@ -2,7 +2,6 @@
|
|||||||
pub type R = crate::R<S0AddressSpec>;
|
pub type R = crate::R<S0AddressSpec>;
|
||||||
#[doc = "Register `S0_ADDRESS` writer"]
|
#[doc = "Register `S0_ADDRESS` writer"]
|
||||||
pub type W = crate::W<S0AddressSpec>;
|
pub type W = crate::W<S0AddressSpec>;
|
||||||
#[cfg(feature = "debug")]
|
|
||||||
impl core::fmt::Debug for R {
|
impl core::fmt::Debug for R {
|
||||||
fn fmt(&self, f: &mut core::fmt::Formatter) -> core::fmt::Result {
|
fn fmt(&self, f: &mut core::fmt::Formatter) -> core::fmt::Result {
|
||||||
write!(f, "{}", self.bits())
|
write!(f, "{}", self.bits())
|
||||||
|
@ -2,7 +2,6 @@
|
|||||||
pub type R = crate::R<S0AddressbSpec>;
|
pub type R = crate::R<S0AddressbSpec>;
|
||||||
#[doc = "Register `S0_ADDRESSB` writer"]
|
#[doc = "Register `S0_ADDRESSB` writer"]
|
||||||
pub type W = crate::W<S0AddressbSpec>;
|
pub type W = crate::W<S0AddressbSpec>;
|
||||||
#[cfg(feature = "debug")]
|
|
||||||
impl core::fmt::Debug for R {
|
impl core::fmt::Debug for R {
|
||||||
fn fmt(&self, f: &mut core::fmt::Formatter) -> core::fmt::Result {
|
fn fmt(&self, f: &mut core::fmt::Formatter) -> core::fmt::Result {
|
||||||
write!(f, "{}", self.bits())
|
write!(f, "{}", self.bits())
|
||||||
|
@ -2,7 +2,6 @@
|
|||||||
pub type R = crate::R<S0AddressmaskSpec>;
|
pub type R = crate::R<S0AddressmaskSpec>;
|
||||||
#[doc = "Register `S0_ADDRESSMASK` writer"]
|
#[doc = "Register `S0_ADDRESSMASK` writer"]
|
||||||
pub type W = crate::W<S0AddressmaskSpec>;
|
pub type W = crate::W<S0AddressmaskSpec>;
|
||||||
#[cfg(feature = "debug")]
|
|
||||||
impl core::fmt::Debug for R {
|
impl core::fmt::Debug for R {
|
||||||
fn fmt(&self, f: &mut core::fmt::Formatter) -> core::fmt::Result {
|
fn fmt(&self, f: &mut core::fmt::Formatter) -> core::fmt::Result {
|
||||||
write!(f, "{}", self.bits())
|
write!(f, "{}", self.bits())
|
||||||
|
@ -2,7 +2,6 @@
|
|||||||
pub type R = crate::R<S0AddressmaskbSpec>;
|
pub type R = crate::R<S0AddressmaskbSpec>;
|
||||||
#[doc = "Register `S0_ADDRESSMASKB` writer"]
|
#[doc = "Register `S0_ADDRESSMASKB` writer"]
|
||||||
pub type W = crate::W<S0AddressmaskbSpec>;
|
pub type W = crate::W<S0AddressmaskbSpec>;
|
||||||
#[cfg(feature = "debug")]
|
|
||||||
impl core::fmt::Debug for R {
|
impl core::fmt::Debug for R {
|
||||||
fn fmt(&self, f: &mut core::fmt::Formatter) -> core::fmt::Result {
|
fn fmt(&self, f: &mut core::fmt::Formatter) -> core::fmt::Result {
|
||||||
write!(f, "{}", self.bits())
|
write!(f, "{}", self.bits())
|
||||||
|
@ -2,7 +2,6 @@
|
|||||||
pub type R = crate::R<S0DataSpec>;
|
pub type R = crate::R<S0DataSpec>;
|
||||||
#[doc = "Register `S0_DATA` writer"]
|
#[doc = "Register `S0_DATA` writer"]
|
||||||
pub type W = crate::W<S0DataSpec>;
|
pub type W = crate::W<S0DataSpec>;
|
||||||
#[cfg(feature = "debug")]
|
|
||||||
impl core::fmt::Debug for R {
|
impl core::fmt::Debug for R {
|
||||||
fn fmt(&self, f: &mut core::fmt::Formatter) -> core::fmt::Result {
|
fn fmt(&self, f: &mut core::fmt::Formatter) -> core::fmt::Result {
|
||||||
write!(f, "{}", self.bits())
|
write!(f, "{}", self.bits())
|
||||||
|
@ -1,6 +1,5 @@
|
|||||||
#[doc = "Register `S0_LASTADDRESS` reader"]
|
#[doc = "Register `S0_LASTADDRESS` reader"]
|
||||||
pub type R = crate::R<S0LastaddressSpec>;
|
pub type R = crate::R<S0LastaddressSpec>;
|
||||||
#[cfg(feature = "debug")]
|
|
||||||
impl core::fmt::Debug for R {
|
impl core::fmt::Debug for R {
|
||||||
fn fmt(&self, f: &mut core::fmt::Formatter) -> core::fmt::Result {
|
fn fmt(&self, f: &mut core::fmt::Formatter) -> core::fmt::Result {
|
||||||
write!(f, "{}", self.bits())
|
write!(f, "{}", self.bits())
|
||||||
|
@ -2,7 +2,6 @@
|
|||||||
pub type R = crate::R<S0MaxwordsSpec>;
|
pub type R = crate::R<S0MaxwordsSpec>;
|
||||||
#[doc = "Register `S0_MAXWORDS` writer"]
|
#[doc = "Register `S0_MAXWORDS` writer"]
|
||||||
pub type W = crate::W<S0MaxwordsSpec>;
|
pub type W = crate::W<S0MaxwordsSpec>;
|
||||||
#[cfg(feature = "debug")]
|
|
||||||
impl core::fmt::Debug for R {
|
impl core::fmt::Debug for R {
|
||||||
fn fmt(&self, f: &mut core::fmt::Formatter) -> core::fmt::Result {
|
fn fmt(&self, f: &mut core::fmt::Formatter) -> core::fmt::Result {
|
||||||
write!(f, "{}", self.bits())
|
write!(f, "{}", self.bits())
|
||||||
|
@ -1,6 +1,5 @@
|
|||||||
#[doc = "Register `S0_RXCOUNT` reader"]
|
#[doc = "Register `S0_RXCOUNT` reader"]
|
||||||
pub type R = crate::R<S0RxcountSpec>;
|
pub type R = crate::R<S0RxcountSpec>;
|
||||||
#[cfg(feature = "debug")]
|
|
||||||
impl core::fmt::Debug for R {
|
impl core::fmt::Debug for R {
|
||||||
fn fmt(&self, f: &mut core::fmt::Formatter) -> core::fmt::Result {
|
fn fmt(&self, f: &mut core::fmt::Formatter) -> core::fmt::Result {
|
||||||
write!(f, "{}", self.bits())
|
write!(f, "{}", self.bits())
|
||||||
|
@ -2,7 +2,6 @@
|
|||||||
pub type R = crate::R<S0RxfifoirqtrgSpec>;
|
pub type R = crate::R<S0RxfifoirqtrgSpec>;
|
||||||
#[doc = "Register `S0_RXFIFOIRQTRG` writer"]
|
#[doc = "Register `S0_RXFIFOIRQTRG` writer"]
|
||||||
pub type W = crate::W<S0RxfifoirqtrgSpec>;
|
pub type W = crate::W<S0RxfifoirqtrgSpec>;
|
||||||
#[cfg(feature = "debug")]
|
|
||||||
impl core::fmt::Debug for R {
|
impl core::fmt::Debug for R {
|
||||||
fn fmt(&self, f: &mut core::fmt::Formatter) -> core::fmt::Result {
|
fn fmt(&self, f: &mut core::fmt::Formatter) -> core::fmt::Result {
|
||||||
write!(f, "{}", self.bits())
|
write!(f, "{}", self.bits())
|
||||||
|
@ -1,6 +1,5 @@
|
|||||||
#[doc = "Register `S0_STATE` reader"]
|
#[doc = "Register `S0_STATE` reader"]
|
||||||
pub type R = crate::R<S0StateSpec>;
|
pub type R = crate::R<S0StateSpec>;
|
||||||
#[cfg(feature = "debug")]
|
|
||||||
impl core::fmt::Debug for R {
|
impl core::fmt::Debug for R {
|
||||||
fn fmt(&self, f: &mut core::fmt::Formatter) -> core::fmt::Result {
|
fn fmt(&self, f: &mut core::fmt::Formatter) -> core::fmt::Result {
|
||||||
write!(f, "{}", self.bits())
|
write!(f, "{}", self.bits())
|
||||||
|
@ -1,6 +1,5 @@
|
|||||||
#[doc = "Register `S0_TXCOUNT` reader"]
|
#[doc = "Register `S0_TXCOUNT` reader"]
|
||||||
pub type R = crate::R<S0TxcountSpec>;
|
pub type R = crate::R<S0TxcountSpec>;
|
||||||
#[cfg(feature = "debug")]
|
|
||||||
impl core::fmt::Debug for R {
|
impl core::fmt::Debug for R {
|
||||||
fn fmt(&self, f: &mut core::fmt::Formatter) -> core::fmt::Result {
|
fn fmt(&self, f: &mut core::fmt::Formatter) -> core::fmt::Result {
|
||||||
write!(f, "{}", self.bits())
|
write!(f, "{}", self.bits())
|
||||||
|
@ -2,7 +2,6 @@
|
|||||||
pub type R = crate::R<S0TxfifoirqtrgSpec>;
|
pub type R = crate::R<S0TxfifoirqtrgSpec>;
|
||||||
#[doc = "Register `S0_TXFIFOIRQTRG` writer"]
|
#[doc = "Register `S0_TXFIFOIRQTRG` writer"]
|
||||||
pub type W = crate::W<S0TxfifoirqtrgSpec>;
|
pub type W = crate::W<S0TxfifoirqtrgSpec>;
|
||||||
#[cfg(feature = "debug")]
|
|
||||||
impl core::fmt::Debug for R {
|
impl core::fmt::Debug for R {
|
||||||
fn fmt(&self, f: &mut core::fmt::Formatter) -> core::fmt::Result {
|
fn fmt(&self, f: &mut core::fmt::Formatter) -> core::fmt::Result {
|
||||||
write!(f, "{}", self.bits())
|
write!(f, "{}", self.bits())
|
||||||
|
@ -1,6 +1,5 @@
|
|||||||
#[doc = "Register `STATE` reader"]
|
#[doc = "Register `STATE` reader"]
|
||||||
pub type R = crate::R<StateSpec>;
|
pub type R = crate::R<StateSpec>;
|
||||||
#[cfg(feature = "debug")]
|
|
||||||
impl core::fmt::Debug for R {
|
impl core::fmt::Debug for R {
|
||||||
fn fmt(&self, f: &mut core::fmt::Formatter) -> core::fmt::Result {
|
fn fmt(&self, f: &mut core::fmt::Formatter) -> core::fmt::Result {
|
||||||
write!(f, "{}", self.bits())
|
write!(f, "{}", self.bits())
|
||||||
|
@ -2,7 +2,6 @@
|
|||||||
pub type R = crate::R<TmconfigSpec>;
|
pub type R = crate::R<TmconfigSpec>;
|
||||||
#[doc = "Register `TMCONFIG` writer"]
|
#[doc = "Register `TMCONFIG` writer"]
|
||||||
pub type W = crate::W<TmconfigSpec>;
|
pub type W = crate::W<TmconfigSpec>;
|
||||||
#[cfg(feature = "debug")]
|
|
||||||
impl core::fmt::Debug for R {
|
impl core::fmt::Debug for R {
|
||||||
fn fmt(&self, f: &mut core::fmt::Formatter) -> core::fmt::Result {
|
fn fmt(&self, f: &mut core::fmt::Formatter) -> core::fmt::Result {
|
||||||
write!(f, "{}", self.bits())
|
write!(f, "{}", self.bits())
|
||||||
|
@ -1,6 +1,5 @@
|
|||||||
#[doc = "Register `TXCOUNT` reader"]
|
#[doc = "Register `TXCOUNT` reader"]
|
||||||
pub type R = crate::R<TxcountSpec>;
|
pub type R = crate::R<TxcountSpec>;
|
||||||
#[cfg(feature = "debug")]
|
|
||||||
impl core::fmt::Debug for R {
|
impl core::fmt::Debug for R {
|
||||||
fn fmt(&self, f: &mut core::fmt::Formatter) -> core::fmt::Result {
|
fn fmt(&self, f: &mut core::fmt::Formatter) -> core::fmt::Result {
|
||||||
write!(f, "{}", self.bits())
|
write!(f, "{}", self.bits())
|
||||||
|
@ -2,7 +2,6 @@
|
|||||||
pub type R = crate::R<TxfifoirqtrgSpec>;
|
pub type R = crate::R<TxfifoirqtrgSpec>;
|
||||||
#[doc = "Register `TXFIFOIRQTRG` writer"]
|
#[doc = "Register `TXFIFOIRQTRG` writer"]
|
||||||
pub type W = crate::W<TxfifoirqtrgSpec>;
|
pub type W = crate::W<TxfifoirqtrgSpec>;
|
||||||
#[cfg(feature = "debug")]
|
|
||||||
impl core::fmt::Debug for R {
|
impl core::fmt::Debug for R {
|
||||||
fn fmt(&self, f: &mut core::fmt::Formatter) -> core::fmt::Result {
|
fn fmt(&self, f: &mut core::fmt::Formatter) -> core::fmt::Result {
|
||||||
write!(f, "{}", self.bits())
|
write!(f, "{}", self.bits())
|
||||||
|
@ -2,7 +2,6 @@
|
|||||||
pub type R = crate::R<WordsSpec>;
|
pub type R = crate::R<WordsSpec>;
|
||||||
#[doc = "Register `WORDS` writer"]
|
#[doc = "Register `WORDS` writer"]
|
||||||
pub type W = crate::W<WordsSpec>;
|
pub type W = crate::W<WordsSpec>;
|
||||||
#[cfg(feature = "debug")]
|
|
||||||
impl core::fmt::Debug for R {
|
impl core::fmt::Debug for R {
|
||||||
fn fmt(&self, f: &mut core::fmt::Formatter) -> core::fmt::Result {
|
fn fmt(&self, f: &mut core::fmt::Formatter) -> core::fmt::Result {
|
||||||
write!(f, "{}", self.bits())
|
write!(f, "{}", self.bits())
|
||||||
|
@ -2,8 +2,8 @@
|
|||||||
#[doc = "Register block"]
|
#[doc = "Register block"]
|
||||||
pub struct RegisterBlock {
|
pub struct RegisterBlock {
|
||||||
porta: [Porta; 32],
|
porta: [Porta; 32],
|
||||||
portb0: [Portb; 32],
|
portbs: Portbs,
|
||||||
_reserved2: [u8; 0x0efc],
|
_reserved2: [u8; 0x0f78],
|
||||||
perid: Perid,
|
perid: Perid,
|
||||||
}
|
}
|
||||||
impl RegisterBlock {
|
impl RegisterBlock {
|
||||||
@ -18,16 +18,10 @@ impl RegisterBlock {
|
|||||||
pub fn porta_iter(&self) -> impl Iterator<Item = &Porta> {
|
pub fn porta_iter(&self) -> impl Iterator<Item = &Porta> {
|
||||||
self.porta.iter()
|
self.porta.iter()
|
||||||
}
|
}
|
||||||
#[doc = "0x80..0x100 - PORTB Pin Configuration Register"]
|
#[doc = "0x80 - PORTB Pin Configuration Register"]
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
pub const fn portb0(&self, n: usize) -> &Portb {
|
pub const fn portbs(&self) -> &Portbs {
|
||||||
&self.portb0[n]
|
&self.portbs
|
||||||
}
|
|
||||||
#[doc = "Iterator for array of:"]
|
|
||||||
#[doc = "0x80..0x100 - PORTB Pin Configuration Register"]
|
|
||||||
#[inline(always)]
|
|
||||||
pub fn portb0_iter(&self) -> impl Iterator<Item = &Portb> {
|
|
||||||
self.portb0.iter()
|
|
||||||
}
|
}
|
||||||
#[doc = "0xffc - Peripheral ID Register"]
|
#[doc = "0xffc - Peripheral ID Register"]
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
@ -41,8 +35,8 @@ module"]
|
|||||||
pub type Porta = crate::Reg<porta::PortaSpec>;
|
pub type Porta = crate::Reg<porta::PortaSpec>;
|
||||||
#[doc = "PORTA Pin Configuration Register"]
|
#[doc = "PORTA Pin Configuration Register"]
|
||||||
pub mod porta;
|
pub mod porta;
|
||||||
pub use porta as portb;
|
pub use porta as portbs;
|
||||||
pub use Porta as Portb;
|
pub use Porta as Portbs;
|
||||||
#[doc = "PERID (r) register accessor: Peripheral ID Register\n\nYou can [`read`](crate::Reg::read) this register and get [`perid::R`]. See [API](https://docs.rs/svd2rust/#read--modify--write-api).\n\nFor information about available fields see [`mod@perid`]
|
#[doc = "PERID (r) register accessor: Peripheral ID Register\n\nYou can [`read`](crate::Reg::read) this register and get [`perid::R`]. See [API](https://docs.rs/svd2rust/#read--modify--write-api).\n\nFor information about available fields see [`mod@perid`]
|
||||||
module"]
|
module"]
|
||||||
#[doc(alias = "PERID")]
|
#[doc(alias = "PERID")]
|
||||||
|
@ -1,6 +1,5 @@
|
|||||||
#[doc = "Register `PERID` reader"]
|
#[doc = "Register `PERID` reader"]
|
||||||
pub type R = crate::R<PeridSpec>;
|
pub type R = crate::R<PeridSpec>;
|
||||||
#[cfg(feature = "debug")]
|
|
||||||
impl core::fmt::Debug for R {
|
impl core::fmt::Debug for R {
|
||||||
fn fmt(&self, f: &mut core::fmt::Formatter) -> core::fmt::Result {
|
fn fmt(&self, f: &mut core::fmt::Formatter) -> core::fmt::Result {
|
||||||
write!(f, "{}", self.bits())
|
write!(f, "{}", self.bits())
|
||||||
|
@ -3,7 +3,6 @@ pub type R = crate::R<PortaSpec>;
|
|||||||
#[doc = "Register `PORTA[%s]` writer"]
|
#[doc = "Register `PORTA[%s]` writer"]
|
||||||
pub type W = crate::W<PortaSpec>;
|
pub type W = crate::W<PortaSpec>;
|
||||||
#[doc = "Input Filter Selectoin\n\nValue on reset: 0"]
|
#[doc = "Input Filter Selectoin\n\nValue on reset: 0"]
|
||||||
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
|
|
||||||
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
|
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
|
||||||
#[repr(u8)]
|
#[repr(u8)]
|
||||||
pub enum Flttype {
|
pub enum Flttype {
|
||||||
|
@ -2,7 +2,6 @@
|
|||||||
pub type R = crate::R<IntRamSbeSpec>;
|
pub type R = crate::R<IntRamSbeSpec>;
|
||||||
#[doc = "Register `INT_RAM_SBE` writer"]
|
#[doc = "Register `INT_RAM_SBE` writer"]
|
||||||
pub type W = crate::W<IntRamSbeSpec>;
|
pub type W = crate::W<IntRamSbeSpec>;
|
||||||
#[cfg(feature = "debug")]
|
|
||||||
impl core::fmt::Debug for R {
|
impl core::fmt::Debug for R {
|
||||||
fn fmt(&self, f: &mut core::fmt::Formatter) -> core::fmt::Result {
|
fn fmt(&self, f: &mut core::fmt::Formatter) -> core::fmt::Result {
|
||||||
write!(f, "{}", self.bits())
|
write!(f, "{}", self.bits())
|
||||||
|
@ -1,6 +1,5 @@
|
|||||||
#[doc = "Register `PERID` reader"]
|
#[doc = "Register `PERID` reader"]
|
||||||
pub type R = crate::R<PeridSpec>;
|
pub type R = crate::R<PeridSpec>;
|
||||||
#[cfg(feature = "debug")]
|
|
||||||
impl core::fmt::Debug for R {
|
impl core::fmt::Debug for R {
|
||||||
fn fmt(&self, f: &mut core::fmt::Formatter) -> core::fmt::Result {
|
fn fmt(&self, f: &mut core::fmt::Formatter) -> core::fmt::Result {
|
||||||
write!(f, "{}", self.bits())
|
write!(f, "{}", self.bits())
|
||||||
|
@ -1,10 +1,8 @@
|
|||||||
#![doc = "Peripheral access API for VA108XX microcontrollers (generated using svd2rust v0.35.0 (e10f920 2025-02-12))\n\nYou can find an overview of the generated API [here].\n\nAPI features to be included in the [next]
|
#![doc = "Peripheral access API for VA108XX microcontrollers (generated using svd2rust v0.35.0 (dac8766 2025-02-08))\n\nYou can find an overview of the generated API [here].\n\nAPI features to be included in the [next]
|
||||||
svd2rust release can be generated by cloning the svd2rust [repository], checking out the above commit, and running `cargo doc --open`.\n\n[here]: https://docs.rs/svd2rust/0.35.0/svd2rust/#peripheral-api\n[next]: https://github.com/rust-embedded/svd2rust/blob/master/CHANGELOG.md#unreleased\n[repository]: https://github.com/rust-embedded/svd2rust"]
|
svd2rust release can be generated by cloning the svd2rust [repository], checking out the above commit, and running `cargo doc --open`.\n\n[here]: https://docs.rs/svd2rust/0.35.0/svd2rust/#peripheral-api\n[next]: https://github.com/rust-embedded/svd2rust/blob/master/CHANGELOG.md#unreleased\n[repository]: https://github.com/rust-embedded/svd2rust"]
|
||||||
#![allow(non_camel_case_types)]
|
#![allow(non_camel_case_types)]
|
||||||
#![allow(non_snake_case)]
|
#![allow(non_snake_case)]
|
||||||
#![no_std]
|
#![no_std]
|
||||||
// Manually inserted.
|
|
||||||
#![cfg_attr(docsrs, feature(doc_auto_cfg))]
|
|
||||||
use core::marker::PhantomData;
|
use core::marker::PhantomData;
|
||||||
use core::ops::Deref;
|
use core::ops::Deref;
|
||||||
#[doc = r"Number available in the NVIC for configuring priority"]
|
#[doc = r"Number available in the NVIC for configuring priority"]
|
||||||
@ -97,7 +95,6 @@ pub static __INTERRUPTS: [Vector; 32] = [
|
|||||||
Vector { _handler: OC31 },
|
Vector { _handler: OC31 },
|
||||||
];
|
];
|
||||||
#[doc = r"Enumeration of all the interrupts."]
|
#[doc = r"Enumeration of all the interrupts."]
|
||||||
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
|
|
||||||
#[derive(Copy, Clone, Debug, PartialEq, Eq)]
|
#[derive(Copy, Clone, Debug, PartialEq, Eq)]
|
||||||
#[repr(u16)]
|
#[repr(u16)]
|
||||||
pub enum Interrupt {
|
pub enum Interrupt {
|
||||||
|
@ -45,17 +45,8 @@ impl RegisterBlock {
|
|||||||
}
|
}
|
||||||
#[doc = "0x04 - Data In Raw Register by Byte"]
|
#[doc = "0x04 - Data In Raw Register by Byte"]
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
pub const fn datainrawbyte0(&self, n: usize) -> &Datainrawbyte {
|
pub const fn datainrawbytes(&self) -> &Datainrawbytes {
|
||||||
#[allow(clippy::no_effect)]
|
unsafe { &*core::ptr::from_ref(self).cast::<u8>().add(4).cast() }
|
||||||
[(); 4][n];
|
|
||||||
unsafe { &*core::ptr::from_ref(self).cast::<u8>().add(4).add(n).cast() }
|
|
||||||
}
|
|
||||||
#[doc = "Iterator for array of:"]
|
|
||||||
#[doc = "0x04 - Data In Raw Register by Byte"]
|
|
||||||
#[inline(always)]
|
|
||||||
pub fn datainrawbyte0_iter(&self) -> impl Iterator<Item = &Datainrawbyte> {
|
|
||||||
(0..4)
|
|
||||||
.map(move |n| unsafe { &*core::ptr::from_ref(self).cast::<u8>().add(4).add(n).cast() })
|
|
||||||
}
|
}
|
||||||
#[doc = "0x04 - Data In Raw Register"]
|
#[doc = "0x04 - Data In Raw Register"]
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
@ -83,17 +74,8 @@ impl RegisterBlock {
|
|||||||
}
|
}
|
||||||
#[doc = "0x0c - Data Out Register by Byte"]
|
#[doc = "0x0c - Data Out Register by Byte"]
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
pub const fn dataoutrawbyte0(&self, n: usize) -> &Dataoutrawbyte {
|
pub const fn dataoutrawbytes(&self) -> &Dataoutrawbytes {
|
||||||
#[allow(clippy::no_effect)]
|
unsafe { &*core::ptr::from_ref(self).cast::<u8>().add(12).cast() }
|
||||||
[(); 4][n];
|
|
||||||
unsafe { &*core::ptr::from_ref(self).cast::<u8>().add(12).add(n).cast() }
|
|
||||||
}
|
|
||||||
#[doc = "Iterator for array of:"]
|
|
||||||
#[doc = "0x0c - Data Out Register by Byte"]
|
|
||||||
#[inline(always)]
|
|
||||||
pub fn dataoutrawbyte0_iter(&self) -> impl Iterator<Item = &Dataoutrawbyte> {
|
|
||||||
(0..4)
|
|
||||||
.map(move |n| unsafe { &*core::ptr::from_ref(self).cast::<u8>().add(12).add(n).cast() })
|
|
||||||
}
|
}
|
||||||
#[doc = "0x0c - Data Out Register"]
|
#[doc = "0x0c - Data Out Register"]
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
@ -102,17 +84,8 @@ impl RegisterBlock {
|
|||||||
}
|
}
|
||||||
#[doc = "0x10 - Set Out Register by Byte"]
|
#[doc = "0x10 - Set Out Register by Byte"]
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
pub const fn setoutbyte0(&self, n: usize) -> &Setoutbyte {
|
pub const fn setoutbytes(&self) -> &Setoutbytes {
|
||||||
#[allow(clippy::no_effect)]
|
unsafe { &*core::ptr::from_ref(self).cast::<u8>().add(16).cast() }
|
||||||
[(); 4][n];
|
|
||||||
unsafe { &*core::ptr::from_ref(self).cast::<u8>().add(16).add(n).cast() }
|
|
||||||
}
|
|
||||||
#[doc = "Iterator for array of:"]
|
|
||||||
#[doc = "0x10 - Set Out Register by Byte"]
|
|
||||||
#[inline(always)]
|
|
||||||
pub fn setoutbyte0_iter(&self) -> impl Iterator<Item = &Setoutbyte> {
|
|
||||||
(0..4)
|
|
||||||
.map(move |n| unsafe { &*core::ptr::from_ref(self).cast::<u8>().add(16).add(n).cast() })
|
|
||||||
}
|
}
|
||||||
#[doc = "0x10 - Set Out Register"]
|
#[doc = "0x10 - Set Out Register"]
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
@ -121,17 +94,8 @@ impl RegisterBlock {
|
|||||||
}
|
}
|
||||||
#[doc = "0x14 - Clear Out Register by Byte"]
|
#[doc = "0x14 - Clear Out Register by Byte"]
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
pub const fn clroutbyte0(&self, n: usize) -> &Clroutbyte {
|
pub const fn clroutbytes(&self) -> &Clroutbytes {
|
||||||
#[allow(clippy::no_effect)]
|
unsafe { &*core::ptr::from_ref(self).cast::<u8>().add(20).cast() }
|
||||||
[(); 4][n];
|
|
||||||
unsafe { &*core::ptr::from_ref(self).cast::<u8>().add(20).add(n).cast() }
|
|
||||||
}
|
|
||||||
#[doc = "Iterator for array of:"]
|
|
||||||
#[doc = "0x14 - Clear Out Register by Byte"]
|
|
||||||
#[inline(always)]
|
|
||||||
pub fn clroutbyte0_iter(&self) -> impl Iterator<Item = &Clroutbyte> {
|
|
||||||
(0..4)
|
|
||||||
.map(move |n| unsafe { &*core::ptr::from_ref(self).cast::<u8>().add(20).add(n).cast() })
|
|
||||||
}
|
}
|
||||||
#[doc = "0x14 - Clear Out Register"]
|
#[doc = "0x14 - Clear Out Register"]
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
@ -140,17 +104,8 @@ impl RegisterBlock {
|
|||||||
}
|
}
|
||||||
#[doc = "0x18 - Toggle Out Register by Byte"]
|
#[doc = "0x18 - Toggle Out Register by Byte"]
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
pub const fn togoutbyte0(&self, n: usize) -> &Togoutbyte {
|
pub const fn togoutbytes(&self) -> &Togoutbytes {
|
||||||
#[allow(clippy::no_effect)]
|
unsafe { &*core::ptr::from_ref(self).cast::<u8>().add(24).cast() }
|
||||||
[(); 4][n];
|
|
||||||
unsafe { &*core::ptr::from_ref(self).cast::<u8>().add(24).add(n).cast() }
|
|
||||||
}
|
|
||||||
#[doc = "Iterator for array of:"]
|
|
||||||
#[doc = "0x18 - Toggle Out Register by Byte"]
|
|
||||||
#[inline(always)]
|
|
||||||
pub fn togoutbyte0_iter(&self) -> impl Iterator<Item = &Togoutbyte> {
|
|
||||||
(0..4)
|
|
||||||
.map(move |n| unsafe { &*core::ptr::from_ref(self).cast::<u8>().add(24).add(n).cast() })
|
|
||||||
}
|
}
|
||||||
#[doc = "0x18 - Toggle Out Register"]
|
#[doc = "0x18 - Toggle Out Register"]
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
@ -178,17 +133,8 @@ impl RegisterBlock {
|
|||||||
}
|
}
|
||||||
#[doc = "0x20 - Direction Register by Byte"]
|
#[doc = "0x20 - Direction Register by Byte"]
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
pub const fn dirbyte0(&self, n: usize) -> &Dirbyte {
|
pub const fn dirbytes(&self) -> &Dirbytes {
|
||||||
#[allow(clippy::no_effect)]
|
unsafe { &*core::ptr::from_ref(self).cast::<u8>().add(32).cast() }
|
||||||
[(); 4][n];
|
|
||||||
unsafe { &*core::ptr::from_ref(self).cast::<u8>().add(32).add(n).cast() }
|
|
||||||
}
|
|
||||||
#[doc = "Iterator for array of:"]
|
|
||||||
#[doc = "0x20 - Direction Register by Byte"]
|
|
||||||
#[inline(always)]
|
|
||||||
pub fn dirbyte0_iter(&self) -> impl Iterator<Item = &Dirbyte> {
|
|
||||||
(0..4)
|
|
||||||
.map(move |n| unsafe { &*core::ptr::from_ref(self).cast::<u8>().add(32).add(n).cast() })
|
|
||||||
}
|
}
|
||||||
#[doc = "0x20 - Direction Register (1:Output, 0:Input)"]
|
#[doc = "0x20 - Direction Register (1:Output, 0:Input)"]
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
@ -197,17 +143,8 @@ impl RegisterBlock {
|
|||||||
}
|
}
|
||||||
#[doc = "0x24 - Pulse Mode Register by Byte"]
|
#[doc = "0x24 - Pulse Mode Register by Byte"]
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
pub const fn pulsebyte0(&self, n: usize) -> &Pulsebyte {
|
pub const fn pulsebytes(&self) -> &Pulsebytes {
|
||||||
#[allow(clippy::no_effect)]
|
unsafe { &*core::ptr::from_ref(self).cast::<u8>().add(36).cast() }
|
||||||
[(); 4][n];
|
|
||||||
unsafe { &*core::ptr::from_ref(self).cast::<u8>().add(36).add(n).cast() }
|
|
||||||
}
|
|
||||||
#[doc = "Iterator for array of:"]
|
|
||||||
#[doc = "0x24 - Pulse Mode Register by Byte"]
|
|
||||||
#[inline(always)]
|
|
||||||
pub fn pulsebyte0_iter(&self) -> impl Iterator<Item = &Pulsebyte> {
|
|
||||||
(0..4)
|
|
||||||
.map(move |n| unsafe { &*core::ptr::from_ref(self).cast::<u8>().add(36).add(n).cast() })
|
|
||||||
}
|
}
|
||||||
#[doc = "0x24 - Pulse Mode Register"]
|
#[doc = "0x24 - Pulse Mode Register"]
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
@ -216,17 +153,8 @@ impl RegisterBlock {
|
|||||||
}
|
}
|
||||||
#[doc = "0x28 - Pulse Base Mode Register by Byte"]
|
#[doc = "0x28 - Pulse Base Mode Register by Byte"]
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
pub const fn pulsebasebyte0(&self, n: usize) -> &Pulsebasebyte {
|
pub const fn pulsebasebytes(&self) -> &Pulsebasebytes {
|
||||||
#[allow(clippy::no_effect)]
|
unsafe { &*core::ptr::from_ref(self).cast::<u8>().add(40).cast() }
|
||||||
[(); 4][n];
|
|
||||||
unsafe { &*core::ptr::from_ref(self).cast::<u8>().add(40).add(n).cast() }
|
|
||||||
}
|
|
||||||
#[doc = "Iterator for array of:"]
|
|
||||||
#[doc = "0x28 - Pulse Base Mode Register by Byte"]
|
|
||||||
#[inline(always)]
|
|
||||||
pub fn pulsebasebyte0_iter(&self) -> impl Iterator<Item = &Pulsebasebyte> {
|
|
||||||
(0..4)
|
|
||||||
.map(move |n| unsafe { &*core::ptr::from_ref(self).cast::<u8>().add(40).add(n).cast() })
|
|
||||||
}
|
}
|
||||||
#[doc = "0x28 - Pulse Base Value Register"]
|
#[doc = "0x28 - Pulse Base Value Register"]
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
@ -235,17 +163,8 @@ impl RegisterBlock {
|
|||||||
}
|
}
|
||||||
#[doc = "0x2c - Delay1 Register by Byte"]
|
#[doc = "0x2c - Delay1 Register by Byte"]
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
pub const fn delay1byte0(&self, n: usize) -> &Delay1byte {
|
pub const fn delay1bytes(&self) -> &Delay1bytes {
|
||||||
#[allow(clippy::no_effect)]
|
unsafe { &*core::ptr::from_ref(self).cast::<u8>().add(44).cast() }
|
||||||
[(); 4][n];
|
|
||||||
unsafe { &*core::ptr::from_ref(self).cast::<u8>().add(44).add(n).cast() }
|
|
||||||
}
|
|
||||||
#[doc = "Iterator for array of:"]
|
|
||||||
#[doc = "0x2c - Delay1 Register by Byte"]
|
|
||||||
#[inline(always)]
|
|
||||||
pub fn delay1byte0_iter(&self) -> impl Iterator<Item = &Delay1byte> {
|
|
||||||
(0..4)
|
|
||||||
.map(move |n| unsafe { &*core::ptr::from_ref(self).cast::<u8>().add(44).add(n).cast() })
|
|
||||||
}
|
}
|
||||||
#[doc = "0x2c - Delay1 Register"]
|
#[doc = "0x2c - Delay1 Register"]
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
@ -254,17 +173,8 @@ impl RegisterBlock {
|
|||||||
}
|
}
|
||||||
#[doc = "0x30 - Delay2 Register by Byte"]
|
#[doc = "0x30 - Delay2 Register by Byte"]
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
pub const fn delay2byte0(&self, n: usize) -> &Delay2byte {
|
pub const fn delay2bytes(&self) -> &Delay2bytes {
|
||||||
#[allow(clippy::no_effect)]
|
unsafe { &*core::ptr::from_ref(self).cast::<u8>().add(48).cast() }
|
||||||
[(); 4][n];
|
|
||||||
unsafe { &*core::ptr::from_ref(self).cast::<u8>().add(48).add(n).cast() }
|
|
||||||
}
|
|
||||||
#[doc = "Iterator for array of:"]
|
|
||||||
#[doc = "0x30 - Delay2 Register by Byte"]
|
|
||||||
#[inline(always)]
|
|
||||||
pub fn delay2byte0_iter(&self) -> impl Iterator<Item = &Delay2byte> {
|
|
||||||
(0..4)
|
|
||||||
.map(move |n| unsafe { &*core::ptr::from_ref(self).cast::<u8>().add(48).add(n).cast() })
|
|
||||||
}
|
}
|
||||||
#[doc = "0x30 - Delay2 Register"]
|
#[doc = "0x30 - Delay2 Register"]
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
@ -325,9 +235,9 @@ pub type Datainbyte = crate::Reg<datainbyte::DatainbyteSpec>;
|
|||||||
#[doc = "Data In Register by Byte"]
|
#[doc = "Data In Register by Byte"]
|
||||||
pub mod datainbyte;
|
pub mod datainbyte;
|
||||||
pub use datain as datainraw;
|
pub use datain as datainraw;
|
||||||
pub use datainbyte as datainrawbyte;
|
pub use datainbyte as datainrawbytes;
|
||||||
pub use Datain as Datainraw;
|
pub use Datain as Datainraw;
|
||||||
pub use Datainbyte as Datainrawbyte;
|
pub use Datainbyte as Datainrawbytes;
|
||||||
#[doc = "DATAOUT (w) register accessor: Data Out Register\n\nYou can [`reset`](crate::Reg::reset), [`write`](crate::Reg::write), [`write_with_zero`](crate::Reg::write_with_zero) this register using [`dataout::W`]. See [API](https://docs.rs/svd2rust/#read--modify--write-api).\n\nFor information about available fields see [`mod@dataout`]
|
#[doc = "DATAOUT (w) register accessor: Data Out Register\n\nYou can [`reset`](crate::Reg::reset), [`write`](crate::Reg::write), [`write_with_zero`](crate::Reg::write_with_zero) this register using [`dataout::W`]. See [API](https://docs.rs/svd2rust/#read--modify--write-api).\n\nFor information about available fields see [`mod@dataout`]
|
||||||
module"]
|
module"]
|
||||||
#[doc(alias = "DATAOUT")]
|
#[doc(alias = "DATAOUT")]
|
||||||
@ -344,18 +254,18 @@ pub use dataout as dataoutraw;
|
|||||||
pub use dataout as setout;
|
pub use dataout as setout;
|
||||||
pub use dataout as clrout;
|
pub use dataout as clrout;
|
||||||
pub use dataout as togout;
|
pub use dataout as togout;
|
||||||
pub use dataoutbyte as dataoutrawbyte;
|
pub use dataoutbyte as dataoutrawbytes;
|
||||||
pub use dataoutbyte as setoutbyte;
|
pub use dataoutbyte as setoutbytes;
|
||||||
pub use dataoutbyte as clroutbyte;
|
pub use dataoutbyte as clroutbytes;
|
||||||
pub use dataoutbyte as togoutbyte;
|
pub use dataoutbyte as togoutbytes;
|
||||||
pub use Dataout as Dataoutraw;
|
pub use Dataout as Dataoutraw;
|
||||||
pub use Dataout as Setout;
|
pub use Dataout as Setout;
|
||||||
pub use Dataout as Clrout;
|
pub use Dataout as Clrout;
|
||||||
pub use Dataout as Togout;
|
pub use Dataout as Togout;
|
||||||
pub use Dataoutbyte as Dataoutrawbyte;
|
pub use Dataoutbyte as Dataoutrawbytes;
|
||||||
pub use Dataoutbyte as Setoutbyte;
|
pub use Dataoutbyte as Setoutbytes;
|
||||||
pub use Dataoutbyte as Clroutbyte;
|
pub use Dataoutbyte as Clroutbytes;
|
||||||
pub use Dataoutbyte as Togoutbyte;
|
pub use Dataoutbyte as Togoutbytes;
|
||||||
#[doc = "DATAMASK (rw) register accessor: Data mask Register\n\nYou can [`read`](crate::Reg::read) this register and get [`datamask::R`]. You can [`reset`](crate::Reg::reset), [`write`](crate::Reg::write), [`write_with_zero`](crate::Reg::write_with_zero) this register using [`datamask::W`]. You can also [`modify`](crate::Reg::modify) this register. See [API](https://docs.rs/svd2rust/#read--modify--write-api).\n\nFor information about available fields see [`mod@datamask`]
|
#[doc = "DATAMASK (rw) register accessor: Data mask Register\n\nYou can [`read`](crate::Reg::read) this register and get [`datamask::R`]. You can [`reset`](crate::Reg::reset), [`write`](crate::Reg::write), [`write_with_zero`](crate::Reg::write_with_zero) this register using [`datamask::W`]. You can also [`modify`](crate::Reg::modify) this register. See [API](https://docs.rs/svd2rust/#read--modify--write-api).\n\nFor information about available fields see [`mod@datamask`]
|
||||||
module"]
|
module"]
|
||||||
#[doc(alias = "DATAMASK")]
|
#[doc(alias = "DATAMASK")]
|
||||||
@ -373,21 +283,21 @@ pub use datamask as pulse;
|
|||||||
pub use datamask as pulsebase;
|
pub use datamask as pulsebase;
|
||||||
pub use datamask as delay1;
|
pub use datamask as delay1;
|
||||||
pub use datamask as delay2;
|
pub use datamask as delay2;
|
||||||
pub use datamaskbyte as dirbyte;
|
pub use datamaskbyte as dirbytes;
|
||||||
pub use datamaskbyte as pulsebyte;
|
pub use datamaskbyte as pulsebytes;
|
||||||
pub use datamaskbyte as pulsebasebyte;
|
pub use datamaskbyte as pulsebasebytes;
|
||||||
pub use datamaskbyte as delay1byte;
|
pub use datamaskbyte as delay1bytes;
|
||||||
pub use datamaskbyte as delay2byte;
|
pub use datamaskbyte as delay2bytes;
|
||||||
pub use Datamask as Dir;
|
pub use Datamask as Dir;
|
||||||
pub use Datamask as Pulse;
|
pub use Datamask as Pulse;
|
||||||
pub use Datamask as Pulsebase;
|
pub use Datamask as Pulsebase;
|
||||||
pub use Datamask as Delay1;
|
pub use Datamask as Delay1;
|
||||||
pub use Datamask as Delay2;
|
pub use Datamask as Delay2;
|
||||||
pub use Datamaskbyte as Dirbyte;
|
pub use Datamaskbyte as Dirbytes;
|
||||||
pub use Datamaskbyte as Pulsebyte;
|
pub use Datamaskbyte as Pulsebytes;
|
||||||
pub use Datamaskbyte as Pulsebasebyte;
|
pub use Datamaskbyte as Pulsebasebytes;
|
||||||
pub use Datamaskbyte as Delay1byte;
|
pub use Datamaskbyte as Delay1bytes;
|
||||||
pub use Datamaskbyte as Delay2byte;
|
pub use Datamaskbyte as Delay2bytes;
|
||||||
#[doc = "IRQ_SEN (rw) register accessor: Interrupt Sense Register (1:Level Sensitive, 0:Edge Sensitive)\n\nYou can [`read`](crate::Reg::read) this register and get [`irq_sen::R`]. You can [`reset`](crate::Reg::reset), [`write`](crate::Reg::write), [`write_with_zero`](crate::Reg::write_with_zero) this register using [`irq_sen::W`]. You can also [`modify`](crate::Reg::modify) this register. See [API](https://docs.rs/svd2rust/#read--modify--write-api).\n\nFor information about available fields see [`mod@irq_sen`]
|
#[doc = "IRQ_SEN (rw) register accessor: Interrupt Sense Register (1:Level Sensitive, 0:Edge Sensitive)\n\nYou can [`read`](crate::Reg::read) this register and get [`irq_sen::R`]. You can [`reset`](crate::Reg::reset), [`write`](crate::Reg::write), [`write_with_zero`](crate::Reg::write_with_zero) this register using [`irq_sen::W`]. You can also [`modify`](crate::Reg::modify) this register. See [API](https://docs.rs/svd2rust/#read--modify--write-api).\n\nFor information about available fields see [`mod@irq_sen`]
|
||||||
module"]
|
module"]
|
||||||
#[doc(alias = "IRQ_SEN")]
|
#[doc(alias = "IRQ_SEN")]
|
||||||
|
@ -1,6 +1,5 @@
|
|||||||
#[doc = "Register `DATAIN` reader"]
|
#[doc = "Register `DATAIN` reader"]
|
||||||
pub type R = crate::R<DatainSpec>;
|
pub type R = crate::R<DatainSpec>;
|
||||||
#[cfg(feature = "debug")]
|
|
||||||
impl core::fmt::Debug for R {
|
impl core::fmt::Debug for R {
|
||||||
fn fmt(&self, f: &mut core::fmt::Formatter) -> core::fmt::Result {
|
fn fmt(&self, f: &mut core::fmt::Formatter) -> core::fmt::Result {
|
||||||
write!(f, "{}", self.bits())
|
write!(f, "{}", self.bits())
|
||||||
|
@ -1,6 +1,5 @@
|
|||||||
#[doc = "Register `DATAINBYTE[%s]` reader"]
|
#[doc = "Register `DATAINBYTE[%s]` reader"]
|
||||||
pub type R = crate::R<DatainbyteSpec>;
|
pub type R = crate::R<DatainbyteSpec>;
|
||||||
#[cfg(feature = "debug")]
|
|
||||||
impl core::fmt::Debug for R {
|
impl core::fmt::Debug for R {
|
||||||
fn fmt(&self, f: &mut core::fmt::Formatter) -> core::fmt::Result {
|
fn fmt(&self, f: &mut core::fmt::Formatter) -> core::fmt::Result {
|
||||||
write!(f, "{}", self.bits())
|
write!(f, "{}", self.bits())
|
||||||
|
@ -2,7 +2,6 @@
|
|||||||
pub type R = crate::R<DatamaskSpec>;
|
pub type R = crate::R<DatamaskSpec>;
|
||||||
#[doc = "Register `DATAMASK` writer"]
|
#[doc = "Register `DATAMASK` writer"]
|
||||||
pub type W = crate::W<DatamaskSpec>;
|
pub type W = crate::W<DatamaskSpec>;
|
||||||
#[cfg(feature = "debug")]
|
|
||||||
impl core::fmt::Debug for R {
|
impl core::fmt::Debug for R {
|
||||||
fn fmt(&self, f: &mut core::fmt::Formatter) -> core::fmt::Result {
|
fn fmt(&self, f: &mut core::fmt::Formatter) -> core::fmt::Result {
|
||||||
write!(f, "{}", self.bits())
|
write!(f, "{}", self.bits())
|
||||||
|
@ -2,7 +2,6 @@
|
|||||||
pub type R = crate::R<DatamaskbyteSpec>;
|
pub type R = crate::R<DatamaskbyteSpec>;
|
||||||
#[doc = "Register `DATAMASKBYTE[%s]` writer"]
|
#[doc = "Register `DATAMASKBYTE[%s]` writer"]
|
||||||
pub type W = crate::W<DatamaskbyteSpec>;
|
pub type W = crate::W<DatamaskbyteSpec>;
|
||||||
#[cfg(feature = "debug")]
|
|
||||||
impl core::fmt::Debug for R {
|
impl core::fmt::Debug for R {
|
||||||
fn fmt(&self, f: &mut core::fmt::Formatter) -> core::fmt::Result {
|
fn fmt(&self, f: &mut core::fmt::Formatter) -> core::fmt::Result {
|
||||||
write!(f, "{}", self.bits())
|
write!(f, "{}", self.bits())
|
||||||
|
@ -1,6 +1,5 @@
|
|||||||
#[doc = "Register `DATAOUT` writer"]
|
#[doc = "Register `DATAOUT` writer"]
|
||||||
pub type W = crate::W<DataoutSpec>;
|
pub type W = crate::W<DataoutSpec>;
|
||||||
#[cfg(feature = "debug")]
|
|
||||||
impl core::fmt::Debug for crate::generic::Reg<DataoutSpec> {
|
impl core::fmt::Debug for crate::generic::Reg<DataoutSpec> {
|
||||||
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
|
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
|
||||||
write!(f, "(not readable)")
|
write!(f, "(not readable)")
|
||||||
|
@ -1,6 +1,5 @@
|
|||||||
#[doc = "Register `DATAOUTBYTE[%s]` writer"]
|
#[doc = "Register `DATAOUTBYTE[%s]` writer"]
|
||||||
pub type W = crate::W<DataoutbyteSpec>;
|
pub type W = crate::W<DataoutbyteSpec>;
|
||||||
#[cfg(feature = "debug")]
|
|
||||||
impl core::fmt::Debug for crate::generic::Reg<DataoutbyteSpec> {
|
impl core::fmt::Debug for crate::generic::Reg<DataoutbyteSpec> {
|
||||||
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
|
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
|
||||||
write!(f, "(not readable)")
|
write!(f, "(not readable)")
|
||||||
|
@ -2,7 +2,6 @@
|
|||||||
pub type R = crate::R<EdgeStatusSpec>;
|
pub type R = crate::R<EdgeStatusSpec>;
|
||||||
#[doc = "Register `EDGE_STATUS` writer"]
|
#[doc = "Register `EDGE_STATUS` writer"]
|
||||||
pub type W = crate::W<EdgeStatusSpec>;
|
pub type W = crate::W<EdgeStatusSpec>;
|
||||||
#[cfg(feature = "debug")]
|
|
||||||
impl core::fmt::Debug for R {
|
impl core::fmt::Debug for R {
|
||||||
fn fmt(&self, f: &mut core::fmt::Formatter) -> core::fmt::Result {
|
fn fmt(&self, f: &mut core::fmt::Formatter) -> core::fmt::Result {
|
||||||
write!(f, "{}", self.bits())
|
write!(f, "{}", self.bits())
|
||||||
|
@ -2,7 +2,6 @@
|
|||||||
pub type R = crate::R<IrqEdgeSpec>;
|
pub type R = crate::R<IrqEdgeSpec>;
|
||||||
#[doc = "Register `IRQ_EDGE` writer"]
|
#[doc = "Register `IRQ_EDGE` writer"]
|
||||||
pub type W = crate::W<IrqEdgeSpec>;
|
pub type W = crate::W<IrqEdgeSpec>;
|
||||||
#[cfg(feature = "debug")]
|
|
||||||
impl core::fmt::Debug for R {
|
impl core::fmt::Debug for R {
|
||||||
fn fmt(&self, f: &mut core::fmt::Formatter) -> core::fmt::Result {
|
fn fmt(&self, f: &mut core::fmt::Formatter) -> core::fmt::Result {
|
||||||
write!(f, "{}", self.bits())
|
write!(f, "{}", self.bits())
|
||||||
|
@ -2,7 +2,6 @@
|
|||||||
pub type R = crate::R<IrqEnbSpec>;
|
pub type R = crate::R<IrqEnbSpec>;
|
||||||
#[doc = "Register `IRQ_ENB` writer"]
|
#[doc = "Register `IRQ_ENB` writer"]
|
||||||
pub type W = crate::W<IrqEnbSpec>;
|
pub type W = crate::W<IrqEnbSpec>;
|
||||||
#[cfg(feature = "debug")]
|
|
||||||
impl core::fmt::Debug for R {
|
impl core::fmt::Debug for R {
|
||||||
fn fmt(&self, f: &mut core::fmt::Formatter) -> core::fmt::Result {
|
fn fmt(&self, f: &mut core::fmt::Formatter) -> core::fmt::Result {
|
||||||
write!(f, "{}", self.bits())
|
write!(f, "{}", self.bits())
|
||||||
|
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user