Compare commits
68 Commits
vorago-reb
...
vorago-reb
Author | SHA1 | Date | |
---|---|---|---|
872944bebf | |||
d077bb6210 | |||
bd286bdb2a | |||
d3cc00a4a5
|
|||
1018a65447 | |||
0a31b637e6 | |||
6e1ae70054 | |||
8ae2d6189a
|
|||
549a98dbaf | |||
e24fc608a3
|
|||
7b74312013 | |||
417f5b7f67 | |||
3e796ef22b | |||
b145047b95 | |||
82b4c16f8e | |||
189ac2d256
|
|||
c5543d8606 | |||
691911d087
|
|||
3953897c48 | |||
6e0d417a5c | |||
4edba63b02 | |||
bcd79f0f20 | |||
77608da74e | |||
066d91aee5 | |||
e869355960 | |||
99631dbd03 | |||
698ed3a700 | |||
f3d840ace7
|
|||
f781505ec5 | |||
c6e840a991 | |||
454635a473 | |||
67ddba9c42 | |||
6efc902e02 | |||
b6e9a7f68e
|
|||
6842e06bc6 | |||
5b614e1280 | |||
16e5a5f197 | |||
da1f2902b2 | |||
b2d17e10ed | |||
1412e1b7d1
|
|||
88ee85a4cd | |||
4b318ecc76
|
|||
badeea8071 | |||
c95558ff55 | |||
cd222fd1e1 | |||
f438e7e40f
|
|||
9e547668c2
|
|||
cf55fe1504 | |||
74eebdcc03 | |||
35527f092a | |||
c6314f48d7 | |||
7189cb246b | |||
39b8633065 | |||
df0760da98 | |||
8ed26db6a7
|
|||
307174b938 | |||
46df7f1007 | |||
48dd00661f | |||
e98ef8501e
|
|||
b753a465bf | |||
d6f69d4a54 | |||
e2a55e7309 | |||
e971e8dc0d | |||
501d1c973e | |||
acb8b67ae7 | |||
405cc089c3
|
|||
f48ee8231a
|
|||
4fb19fe234
|
10
.github/workflows/ci.yml
vendored
10
.github/workflows/ci.yml
vendored
@ -10,8 +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 --release
|
- run: cargo check --target thumbv6m-none-eabi
|
||||||
- run: cargo check --target thumbv6m-none-eabi --examples --release
|
- run: cargo check --target thumbv6m-none-eabi --examples
|
||||||
|
|
||||||
test:
|
test:
|
||||||
name: Run Tests
|
name: Run Tests
|
||||||
@ -21,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
|
- run: cargo nextest run --all-features -p va108xx-hal --no-tests=pass
|
||||||
# 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
|
||||||
|
|
||||||
@ -39,7 +39,9 @@ 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 --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
|
||||||
|
- run: RUSTDOCFLAGS="--cfg docsrs --generate-link-to-definition -Z unstable-options" cargo +nightly doc -p vorago-reb1
|
||||||
|
|
||||||
clippy:
|
clippy:
|
||||||
name: Clippy
|
name: Clippy
|
||||||
|
2
.gitignore
vendored
2
.gitignore
vendored
@ -16,3 +16,5 @@ Cargo.lock
|
|||||||
# JetBrains IDEs
|
# JetBrains IDEs
|
||||||
/.idea
|
/.idea
|
||||||
*.iml
|
*.iml
|
||||||
|
|
||||||
|
/Embed.toml
|
||||||
|
26
Cargo.toml
26
Cargo.toml
@ -1,15 +1,21 @@
|
|||||||
[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/embassy",
|
||||||
"board-tests",
|
"board-tests",
|
||||||
|
"bootloader",
|
||||||
|
"flashloader",
|
||||||
]
|
]
|
||||||
|
|
||||||
exclude = [
|
exclude = [
|
||||||
"defmt-testapp",
|
"defmt-testapp",
|
||||||
|
"flashloader/slot-a-blinky",
|
||||||
|
"flashloader/slot-b-blinky",
|
||||||
]
|
]
|
||||||
|
|
||||||
[profile.dev]
|
[profile.dev]
|
||||||
@ -17,7 +23,8 @@ codegen-units = 1
|
|||||||
debug = 2
|
debug = 2
|
||||||
debug-assertions = true # <-
|
debug-assertions = true # <-
|
||||||
incremental = false
|
incremental = false
|
||||||
opt-level = 'z' # <-
|
# 1 instead of 0, the flashloader is too larger otherwise..
|
||||||
|
# opt-level = 1 # <-
|
||||||
overflow-checks = true # <-
|
overflow-checks = true # <-
|
||||||
|
|
||||||
# cargo build/run --release
|
# cargo build/run --release
|
||||||
@ -29,3 +36,12 @@ incremental = false
|
|||||||
lto = 'fat'
|
lto = 'fat'
|
||||||
opt-level = 3 # <-
|
opt-level = 3 # <-
|
||||||
overflow-checks = false # <-
|
overflow-checks = false # <-
|
||||||
|
|
||||||
|
[profile.small]
|
||||||
|
inherits = "release"
|
||||||
|
codegen-units = 1
|
||||||
|
debug-assertions = false # <-
|
||||||
|
lto = true
|
||||||
|
opt-level = 'z' # <-
|
||||||
|
overflow-checks = false # <-
|
||||||
|
strip = true # Automatically strip symbols from the binary.
|
||||||
|
12
Embed.toml.sample
Normal file
12
Embed.toml.sample
Normal file
@ -0,0 +1,12 @@
|
|||||||
|
[default.probe]
|
||||||
|
protocol = "Jtag"
|
||||||
|
|
||||||
|
[default.general]
|
||||||
|
chip = "VA108xx_RAM"
|
||||||
|
|
||||||
|
[default.rtt]
|
||||||
|
enabled = true
|
||||||
|
|
||||||
|
[default.gdb]
|
||||||
|
# Whether or not a GDB server should be opened after flashing.
|
||||||
|
enabled = false
|
20
README.md
20
README.md
@ -19,9 +19,18 @@ This workspace contains the following released crates:
|
|||||||
|
|
||||||
It also contains the following helper crates:
|
It also contains the following helper crates:
|
||||||
|
|
||||||
- The `board-tests` contains an application which can be used to test the libraries on the
|
- The [`bootloader`](https://egit.irs.uni-stuttgart.de/rust/va108xx-rs/src/branch/main/bootloader)
|
||||||
board.
|
crate contains a sample bootloader strongly based on the one provided by Vorago.
|
||||||
- The `examples` crates contains various example applications for the HAL and the PAC.
|
- The [`flashloader`](https://egit.irs.uni-stuttgart.de/rust/va108xx-rs/src/branch/main/flashloader)
|
||||||
|
crate contains a sample flashloader which is able to update the redundant images in the NVM which
|
||||||
|
is compatible to the provided bootloader as well.
|
||||||
|
- The [`board-tests`](https://egit.irs.uni-stuttgart.de/rust/va108xx-rs/src/branch/main/board-tests)
|
||||||
|
contains an application which can be used to test the libraries on the board.
|
||||||
|
- The [`examples`](https://egit.irs.uni-stuttgart.de/rust/va108xx-rs/src/branch/main/examples)
|
||||||
|
folder contains various example applications crates using the HAL and the PAC.
|
||||||
|
This folder also contains dedicated example applications using the
|
||||||
|
[`RTIC`](https://rtic.rs/2/book/en/) and [`embassy`](https://github.com/embassy-rs/embassy)
|
||||||
|
native Rust RTOSes.
|
||||||
|
|
||||||
## Using the `.cargo/config.toml` file
|
## Using the `.cargo/config.toml` file
|
||||||
|
|
||||||
@ -94,6 +103,8 @@ example.
|
|||||||
|
|
||||||
Assuming a working debug connection to your VA108xx board, you can debug using VS Code with
|
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).
|
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
|
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`
|
`cp -rT vscode .vscode` like specified above. After that, you can use `Run and Debug`
|
||||||
@ -108,4 +119,5 @@ configuration variables in your `settings.json`:
|
|||||||
- `"cortex-debug.gdbPath.osx"`
|
- `"cortex-debug.gdbPath.osx"`
|
||||||
|
|
||||||
The provided VS Code configurations also provide an integrated RTT logger, which you can access
|
The provided VS Code configurations also provide an integrated RTT logger, which you can access
|
||||||
via the terminal at `RTT Ch:0 console`.
|
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.
|
||||||
|
4
automation/Jenkinsfile
vendored
4
automation/Jenkinsfile
vendored
@ -25,7 +25,9 @@ pipeline {
|
|||||||
stage('Docs') {
|
stage('Docs') {
|
||||||
steps {
|
steps {
|
||||||
sh """
|
sh """
|
||||||
RUSTDOCFLAGS="--cfg docsrs --generate-link-to-definition -Z unstable-options" cargo +nightly doc --all-features
|
RUSTDOCFLAGS="--cfg docsrs --generate-link-to-definition -Z unstable-options" cargo +nightly doc -p va108xx
|
||||||
|
RUSTDOCFLAGS="--cfg docsrs --generate-link-to-definition -Z unstable-options" cargo +nightly doc -p va108xx-hal
|
||||||
|
RUSTDOCFLAGS="--cfg docsrs --generate-link-to-definition -Z unstable-options" cargo +nightly doc -p vorago-reb1
|
||||||
"""
|
"""
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -4,18 +4,16 @@ version = "0.1.0"
|
|||||||
edition = "2021"
|
edition = "2021"
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
cortex-m-rtic = "1"
|
|
||||||
panic-halt = "0.2"
|
|
||||||
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"
|
||||||
rtt-target = "0.5"
|
panic-halt = "1"
|
||||||
panic-rtt-target = "0.1.3"
|
rtt-target = "0.6"
|
||||||
|
panic-rtt-target = "0.2"
|
||||||
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.7"
|
|
||||||
path = "../va108xx-hal"
|
path = "../va108xx-hal"
|
||||||
features = ["rt"]
|
features = ["rt"]
|
||||||
|
|
||||||
|
@ -17,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, IrqCfg},
|
timer::{default_ms_irq_handler, set_up_ms_tick, CountdownTimer, InterruptConfig},
|
||||||
};
|
};
|
||||||
|
|
||||||
#[allow(dead_code)]
|
#[allow(dead_code)]
|
||||||
@ -44,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, None, dp.porta);
|
let pinsa = PinsA::new(&mut dp.sysconfig, dp.porta);
|
||||||
let pinsb = PinsB::new(&mut dp.sysconfig, Some(dp.ioconfig), dp.portb);
|
let pinsb = PinsB::new(&mut dp.sysconfig, 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;
|
||||||
|
|
||||||
@ -99,9 +99,11 @@ fn main() -> ! {
|
|||||||
}
|
}
|
||||||
TestCase::TestMask => {
|
TestCase::TestMask => {
|
||||||
// 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().clear_datamask();
|
let mut input = pinsa.pa1.into_pull_down_input();
|
||||||
|
input.clear_datamask();
|
||||||
assert!(!input.datamask());
|
assert!(!input.datamask());
|
||||||
let mut out = pinsa.pa0.into_push_pull_output().clear_datamask();
|
let mut out = pinsa.pa0.into_push_pull_output();
|
||||||
|
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());
|
||||||
}
|
}
|
||||||
@ -119,17 +121,15 @@ fn main() -> ! {
|
|||||||
assert_eq!(PinsB::get_perid(), 0x004007e1);
|
assert_eq!(PinsB::get_perid(), 0x004007e1);
|
||||||
}
|
}
|
||||||
TestCase::Pulse => {
|
TestCase::Pulse => {
|
||||||
let mut output_pulsed = pinsa
|
let mut output_pulsed = pinsa.pa0.into_push_pull_output();
|
||||||
.pa0
|
output_pulsed.pulse_mode(true, PinState::Low);
|
||||||
.into_push_pull_output()
|
|
||||||
.pulse_mode(true, PinState::Low);
|
|
||||||
rprintln!("Pulsing high 10 times..");
|
rprintln!("Pulsing high 10 times..");
|
||||||
output_pulsed.set_low().unwrap();
|
output_pulsed.set_low().unwrap();
|
||||||
for _ in 0..10 {
|
for _ in 0..10 {
|
||||||
output_pulsed.set_high().unwrap();
|
output_pulsed.set_high().unwrap();
|
||||||
cortex_m::asm::delay(25_000_000);
|
cortex_m::asm::delay(25_000_000);
|
||||||
}
|
}
|
||||||
let mut output_pulsed = output_pulsed.pulse_mode(true, PinState::High);
|
output_pulsed.pulse_mode(true, PinState::High);
|
||||||
rprintln!("Pulsing low 10 times..");
|
rprintln!("Pulsing low 10 times..");
|
||||||
for _ in 0..10 {
|
for _ in 0..10 {
|
||||||
output_pulsed.set_low().unwrap();
|
output_pulsed.set_low().unwrap();
|
||||||
@ -155,7 +155,7 @@ fn main() -> ! {
|
|||||||
}
|
}
|
||||||
TestCase::DelayMs => {
|
TestCase::DelayMs => {
|
||||||
let mut ms_timer = set_up_ms_tick(
|
let mut ms_timer = set_up_ms_tick(
|
||||||
IrqCfg::new(pac::Interrupt::OC0, true, true),
|
InterruptConfig::new(pac::Interrupt::OC0, true, true),
|
||||||
&mut dp.sysconfig,
|
&mut dp.sysconfig,
|
||||||
Some(&mut dp.irqsel),
|
Some(&mut dp.irqsel),
|
||||||
50.MHz(),
|
50.MHz(),
|
||||||
@ -168,7 +168,7 @@ fn main() -> ! {
|
|||||||
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().ok();
|
led1.toggle().ok();
|
||||||
|
25
bootloader/Cargo.toml
Normal file
25
bootloader/Cargo.toml
Normal file
@ -0,0 +1,25 @@
|
|||||||
|
[package]
|
||||||
|
name = "bootloader"
|
||||||
|
version = "0.1.0"
|
||||||
|
edition = "2021"
|
||||||
|
|
||||||
|
[dependencies]
|
||||||
|
cortex-m = "0.7"
|
||||||
|
cortex-m-rt = "0.7"
|
||||||
|
embedded-hal = "1"
|
||||||
|
panic-rtt-target = "0.2"
|
||||||
|
panic-halt = "1"
|
||||||
|
rtt-target = "0.6"
|
||||||
|
crc = "3"
|
||||||
|
num_enum = { version = "0.7", default-features = false }
|
||||||
|
static_assertions = "1"
|
||||||
|
|
||||||
|
[dependencies.va108xx-hal]
|
||||||
|
path = "../va108xx-hal"
|
||||||
|
|
||||||
|
[dependencies.vorago-reb1]
|
||||||
|
path = "../vorago-reb1"
|
||||||
|
|
||||||
|
[features]
|
||||||
|
default = []
|
||||||
|
rtt-panic = []
|
51
bootloader/README.md
Normal file
51
bootloader/README.md
Normal file
@ -0,0 +1,51 @@
|
|||||||
|
VA108xx Bootloader Application
|
||||||
|
=======
|
||||||
|
|
||||||
|
This is the Rust version of the bootloader supplied by Vorago.
|
||||||
|
|
||||||
|
## Memory Map
|
||||||
|
|
||||||
|
The bootloader uses the following memory map:
|
||||||
|
|
||||||
|
| Address | Notes | Size |
|
||||||
|
| ------ | ---- | ---- |
|
||||||
|
| 0x0 | Bootloader start | code up to 0x2FFE bytes |
|
||||||
|
| 0x2FFE | Bootloader CRC | half-word |
|
||||||
|
| 0x3000 | App image A start | code up to 0xE7F4 (~59K) bytes |
|
||||||
|
| 0x117F8 | App image A CRC check length | word |
|
||||||
|
| 0x117FC | App image A CRC check value | word |
|
||||||
|
| 0x117FC | App image B start | code up to 0xE7F4 (~59K) bytes |
|
||||||
|
| 0x1FFF0 | App image B CRC check length | word |
|
||||||
|
| 0x1FFF4 | App image B CRC check value | word |
|
||||||
|
| 0x1FFF8 | Reserved section, contains boot select parameter | 8 bytes |
|
||||||
|
| 0x20000 | End of NVM | end |
|
||||||
|
|
||||||
|
## Additional Information
|
||||||
|
|
||||||
|
This bootloader was specifically written for the REB1 board, so it assumes a M95M01 ST EEPROM
|
||||||
|
is used to load the application code. The bootloader will also delay for a configurable amount
|
||||||
|
of time before booting. This allows to catch the RTT printout, but should probably be disabled
|
||||||
|
for production firmware.
|
||||||
|
|
||||||
|
This bootloader does not provide tools to flash the NVM memory by itself. Instead, you can use
|
||||||
|
the [flashloader](https://egit.irs.uni-stuttgart.de/rust/va108xx-rs/src/branch/main/flashloader)
|
||||||
|
application to perform this task using a CCSDS interface via a UART.
|
||||||
|
|
||||||
|
The bootloader performs the following steps:
|
||||||
|
|
||||||
|
1. The application will calculate the checksum of itself if the bootloader CRC is blank (all zeroes
|
||||||
|
or all ones). If the CRC is not blank and the checksum check fails, it will immediately boot
|
||||||
|
application image A. Otherwise, it proceeds to the next step.
|
||||||
|
2. Read the boot slot from a reserved section at the end of the EEPROM. If no valid value is read,
|
||||||
|
select boot slot A.
|
||||||
|
3. Check the checksum of the boot slot. If that checksum is valid, it will boot that slot. If not,
|
||||||
|
it will proceed to the next step.
|
||||||
|
4. Check the checksum of the other slot . If that checksum is valid, it will boot that slot. If
|
||||||
|
not, it will boot App A as the fallback image.
|
||||||
|
|
||||||
|
In your actual production application, a command to update the preferred boot slot could be exposed
|
||||||
|
to allow performing software updates in a safe way.
|
||||||
|
|
||||||
|
Please note that you *MUST* compile the application at slot A and slot B with an appropriate
|
||||||
|
`memory.x` file where the base address of the `FLASH` was adapted according to the base address
|
||||||
|
shown in the memory map above. The memory files to do this were provided in the `scripts` folder.
|
10
bootloader/src/lib.rs
Normal file
10
bootloader/src/lib.rs
Normal file
@ -0,0 +1,10 @@
|
|||||||
|
#![no_std]
|
||||||
|
|
||||||
|
use core::convert::Infallible;
|
||||||
|
|
||||||
|
/// Simple trait which makes swapping the NVM easier. NVMs only need to implement this interface.
|
||||||
|
pub trait NvmInterface {
|
||||||
|
fn write(&mut self, address: usize, data: &[u8]) -> Result<(), Infallible>;
|
||||||
|
fn read(&mut self, address: usize, buf: &mut [u8]) -> Result<(), Infallible>;
|
||||||
|
fn verify(&mut self, address: usize, data: &[u8]) -> Result<bool, Infallible>;
|
||||||
|
}
|
340
bootloader/src/main.rs
Normal file
340
bootloader/src/main.rs
Normal file
@ -0,0 +1,340 @@
|
|||||||
|
//! Vorago bootloader which can boot from two images.
|
||||||
|
#![no_main]
|
||||||
|
#![no_std]
|
||||||
|
use bootloader::NvmInterface;
|
||||||
|
use cortex_m_rt::entry;
|
||||||
|
use crc::{Crc, CRC_16_IBM_3740};
|
||||||
|
use embedded_hal::delay::DelayNs;
|
||||||
|
use num_enum::TryFromPrimitive;
|
||||||
|
#[cfg(not(feature = "rtt-panic"))]
|
||||||
|
use panic_halt as _;
|
||||||
|
#[cfg(feature = "rtt-panic")]
|
||||||
|
use panic_rtt_target as _;
|
||||||
|
use rtt_target::{rprintln, rtt_init_print};
|
||||||
|
use va108xx_hal::{pac, time::Hertz, timer::CountdownTimer};
|
||||||
|
use vorago_reb1::m95m01::M95M01;
|
||||||
|
|
||||||
|
// Useful for debugging and see what the bootloader is doing. Enabled currently, because
|
||||||
|
// the binary stays small enough.
|
||||||
|
const RTT_PRINTOUT: bool = true;
|
||||||
|
const DEBUG_PRINTOUTS: bool = true;
|
||||||
|
// Small delay, allows RTT printout to catch up.
|
||||||
|
const BOOT_DELAY_MS: u32 = 2000;
|
||||||
|
|
||||||
|
// Dangerous option! An image with this option set to true will flash itself from RAM directly
|
||||||
|
// into the NVM. This can be used as a recovery option from a direct RAM flash to fix the NVM
|
||||||
|
// boot process. Please note that this will flash an image which will also always perform the
|
||||||
|
// self-flash itself. It is recommended that you use a tool like probe-rs, Keil IDE, or a flash
|
||||||
|
// loader to boot a bootloader without this feature.
|
||||||
|
const FLASH_SELF: bool = false;
|
||||||
|
|
||||||
|
// Register definitions for Cortex-M0 SCB register.
|
||||||
|
pub const SCB_AIRCR_VECTKEY_POS: u32 = 16;
|
||||||
|
pub const SCB_AIRCR_VECTKEY_MSK: u32 = 0xFFFF << SCB_AIRCR_VECTKEY_POS;
|
||||||
|
|
||||||
|
pub const SCB_AIRCR_SYSRESETREQ_POS: u32 = 2;
|
||||||
|
pub const SCB_AIRCR_SYSRESETREQ_MSK: u32 = 1 << SCB_AIRCR_SYSRESETREQ_POS;
|
||||||
|
|
||||||
|
const CLOCK_FREQ: Hertz = Hertz::from_raw(50_000_000);
|
||||||
|
|
||||||
|
// Important bootloader addresses and offsets, vector table information.
|
||||||
|
|
||||||
|
const NVM_SIZE: u32 = 0x20000;
|
||||||
|
const BOOTLOADER_START_ADDR: u32 = 0x0;
|
||||||
|
const BOOTLOADER_CRC_ADDR: u32 = BOOTLOADER_END_ADDR - 2;
|
||||||
|
// This is also the maximum size of the bootloader.
|
||||||
|
const BOOTLOADER_END_ADDR: u32 = 0x3000;
|
||||||
|
const APP_A_START_ADDR: u32 = BOOTLOADER_END_ADDR;
|
||||||
|
// 0x117F8
|
||||||
|
const APP_A_SIZE_ADDR: u32 = APP_A_END_ADDR - 8;
|
||||||
|
// Four bytes reserved, even when only 2 byte CRC is used. Leaves flexibility to switch to CRC32.
|
||||||
|
// 0x117FC
|
||||||
|
const APP_A_CRC_ADDR: u32 = APP_A_END_ADDR - 4;
|
||||||
|
// 0x11800
|
||||||
|
pub const APP_A_END_ADDR: u32 = APP_A_START_ADDR + APP_IMG_SZ;
|
||||||
|
// The actual size of the image which is relevant for CRC calculation.
|
||||||
|
const APP_B_START_ADDR: u32 = APP_A_END_ADDR;
|
||||||
|
// The actual size of the image which is relevant for CRC calculation.
|
||||||
|
// 0x1FFF8
|
||||||
|
const APP_B_SIZE_ADDR: u32 = APP_B_END_ADDR - 8;
|
||||||
|
// Four bytes reserved, even when only 2 byte CRC is used. Leaves flexibility to switch to CRC32.
|
||||||
|
// 0x1FFFC
|
||||||
|
const APP_B_CRC_ADDR: u32 = APP_B_END_ADDR - 4;
|
||||||
|
// 0x20000. 8 bytes at end of EEPROM reserved for preferred image parameter. This reserved
|
||||||
|
// size should be a multiple of 8 due to alignment requirements.
|
||||||
|
pub const APP_B_END_ADDR: u32 = NVM_SIZE - 8;
|
||||||
|
pub const APP_IMG_SZ: u32 = (APP_B_END_ADDR - APP_A_START_ADDR) / 2;
|
||||||
|
|
||||||
|
static_assertions::const_assert!((APP_B_END_ADDR - BOOTLOADER_END_ADDR) % 2 == 0);
|
||||||
|
|
||||||
|
pub const VECTOR_TABLE_OFFSET: u32 = 0x0;
|
||||||
|
pub const VECTOR_TABLE_LEN: u32 = 0xC0;
|
||||||
|
pub const RESET_VECTOR_OFFSET: u32 = 0x4;
|
||||||
|
pub const PREFERRED_SLOT_OFFSET: u32 = 0x20000 - 1;
|
||||||
|
|
||||||
|
const CRC_ALGO: Crc<u16> = Crc::<u16>::new(&CRC_16_IBM_3740);
|
||||||
|
|
||||||
|
#[derive(Debug, Copy, Clone, PartialEq, Eq, TryFromPrimitive)]
|
||||||
|
#[repr(u8)]
|
||||||
|
enum AppSel {
|
||||||
|
A = 0,
|
||||||
|
B = 1,
|
||||||
|
}
|
||||||
|
|
||||||
|
pub struct NvmWrapper(pub M95M01);
|
||||||
|
|
||||||
|
// Newtype pattern. We could now more easily swap the used NVM type.
|
||||||
|
impl NvmInterface for NvmWrapper {
|
||||||
|
fn write(&mut self, address: usize, data: &[u8]) -> Result<(), core::convert::Infallible> {
|
||||||
|
self.0.write(address, data)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn read(&mut self, address: usize, buf: &mut [u8]) -> Result<(), core::convert::Infallible> {
|
||||||
|
self.0.read(address, buf)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn verify(&mut self, address: usize, data: &[u8]) -> Result<bool, core::convert::Infallible> {
|
||||||
|
self.0.verify(address, data)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[entry]
|
||||||
|
fn main() -> ! {
|
||||||
|
if RTT_PRINTOUT {
|
||||||
|
rtt_init_print!();
|
||||||
|
rprintln!("-- VA108xx bootloader --");
|
||||||
|
}
|
||||||
|
let mut dp = pac::Peripherals::take().unwrap();
|
||||||
|
let cp = cortex_m::Peripherals::take().unwrap();
|
||||||
|
let mut timer = CountdownTimer::new(&mut dp.sysconfig, CLOCK_FREQ, dp.tim0);
|
||||||
|
|
||||||
|
let mut nvm = M95M01::new(&mut dp.sysconfig, CLOCK_FREQ, dp.spic);
|
||||||
|
|
||||||
|
if FLASH_SELF {
|
||||||
|
let mut first_four_bytes: [u8; 4] = [0; 4];
|
||||||
|
read_four_bytes_at_addr_zero(&mut first_four_bytes);
|
||||||
|
let bootloader_data = {
|
||||||
|
unsafe {
|
||||||
|
&*core::ptr::slice_from_raw_parts(
|
||||||
|
(BOOTLOADER_START_ADDR + 4) as *const u8,
|
||||||
|
(BOOTLOADER_END_ADDR - BOOTLOADER_START_ADDR - 6) as usize,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
};
|
||||||
|
let mut digest = CRC_ALGO.digest();
|
||||||
|
digest.update(&first_four_bytes);
|
||||||
|
digest.update(bootloader_data);
|
||||||
|
let bootloader_crc = digest.finalize();
|
||||||
|
|
||||||
|
nvm.write(0x0, &first_four_bytes)
|
||||||
|
.expect("writing to NVM failed");
|
||||||
|
nvm.write(0x4, bootloader_data)
|
||||||
|
.expect("writing to NVM failed");
|
||||||
|
if let Err(e) = nvm.verify(0x0, &first_four_bytes) {
|
||||||
|
if RTT_PRINTOUT {
|
||||||
|
rprintln!("verification of self-flash to NVM failed: {:?}", e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if let Err(e) = nvm.verify(0x4, bootloader_data) {
|
||||||
|
if RTT_PRINTOUT {
|
||||||
|
rprintln!("verification of self-flash to NVM failed: {:?}", e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
nvm.write(BOOTLOADER_CRC_ADDR as usize, &bootloader_crc.to_be_bytes())
|
||||||
|
.expect("writing CRC failed");
|
||||||
|
if let Err(e) = nvm.verify(BOOTLOADER_CRC_ADDR as usize, &bootloader_crc.to_be_bytes()) {
|
||||||
|
if RTT_PRINTOUT {
|
||||||
|
rprintln!(
|
||||||
|
"error: CRC verification for bootloader self-flash failed: {:?}",
|
||||||
|
e
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
let mut nvm = NvmWrapper(nvm);
|
||||||
|
|
||||||
|
// Check bootloader's CRC (and write it if blank)
|
||||||
|
check_own_crc(&dp.sysconfig, &cp, &mut nvm, &mut timer);
|
||||||
|
|
||||||
|
let mut preferred_app_raw = [0; 1];
|
||||||
|
nvm.read(PREFERRED_SLOT_OFFSET as usize, &mut preferred_app_raw)
|
||||||
|
.expect("reading preferred slot failed");
|
||||||
|
let preferred_app = AppSel::try_from(preferred_app_raw[0]).unwrap_or(AppSel::A);
|
||||||
|
let other_app = if preferred_app == AppSel::A {
|
||||||
|
AppSel::B
|
||||||
|
} else {
|
||||||
|
AppSel::A
|
||||||
|
};
|
||||||
|
|
||||||
|
if check_app_crc(preferred_app) {
|
||||||
|
boot_app(&dp.sysconfig, &cp, preferred_app, &mut timer)
|
||||||
|
} else if check_app_crc(other_app) {
|
||||||
|
boot_app(&dp.sysconfig, &cp, other_app, &mut timer)
|
||||||
|
} else {
|
||||||
|
if DEBUG_PRINTOUTS && RTT_PRINTOUT {
|
||||||
|
rprintln!("both images corrupt! booting image A");
|
||||||
|
}
|
||||||
|
// TODO: Shift a CCSDS packet out to inform host/OBC about image corruption.
|
||||||
|
// Both images seem to be corrupt. Boot default image A.
|
||||||
|
boot_app(&dp.sysconfig, &cp, AppSel::A, &mut timer)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn check_own_crc(
|
||||||
|
sysconfig: &pac::Sysconfig,
|
||||||
|
cp: &cortex_m::Peripherals,
|
||||||
|
nvm: &mut NvmWrapper,
|
||||||
|
timer: &mut CountdownTimer<pac::Tim0>,
|
||||||
|
) {
|
||||||
|
let crc_exp = unsafe { (BOOTLOADER_CRC_ADDR as *const u16).read_unaligned().to_be() };
|
||||||
|
// I'd prefer to use [core::slice::from_raw_parts], but that is problematic
|
||||||
|
// because the address of the bootloader is 0x0, so the NULL check fails and the functions
|
||||||
|
// panics.
|
||||||
|
let mut first_four_bytes: [u8; 4] = [0; 4];
|
||||||
|
read_four_bytes_at_addr_zero(&mut first_four_bytes);
|
||||||
|
let mut digest = CRC_ALGO.digest();
|
||||||
|
digest.update(&first_four_bytes);
|
||||||
|
digest.update(unsafe {
|
||||||
|
&*core::ptr::slice_from_raw_parts(
|
||||||
|
(BOOTLOADER_START_ADDR + 4) as *const u8,
|
||||||
|
(BOOTLOADER_END_ADDR - BOOTLOADER_START_ADDR - 6) as usize,
|
||||||
|
)
|
||||||
|
});
|
||||||
|
let crc_calc = digest.finalize();
|
||||||
|
if crc_exp == 0x0000 || crc_exp == 0xffff {
|
||||||
|
if DEBUG_PRINTOUTS && RTT_PRINTOUT {
|
||||||
|
rprintln!("BL CRC blank - prog new CRC");
|
||||||
|
}
|
||||||
|
// Blank CRC, write it to NVM.
|
||||||
|
nvm.write(BOOTLOADER_CRC_ADDR as usize, &crc_calc.to_be_bytes())
|
||||||
|
.expect("writing CRC failed");
|
||||||
|
// The Vorago bootloader resets here. I am not sure why this is done but I think it is
|
||||||
|
// necessary because somehow the boot will not work if we just continue as usual.
|
||||||
|
// cortex_m::peripheral::SCB::sys_reset();
|
||||||
|
} else if crc_exp != crc_calc {
|
||||||
|
// Bootloader is corrupted. Try to run App A.
|
||||||
|
if DEBUG_PRINTOUTS && RTT_PRINTOUT {
|
||||||
|
rprintln!(
|
||||||
|
"bootloader CRC corrupt, read {} and expected {}. booting image A immediately",
|
||||||
|
crc_calc,
|
||||||
|
crc_exp
|
||||||
|
);
|
||||||
|
}
|
||||||
|
// TODO: Shift out minimal CCSDS frame to notify about bootloader corruption.
|
||||||
|
boot_app(sysconfig, cp, AppSel::A, timer);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Reading from address 0x0 is problematic in Rust.
|
||||||
|
// See https://users.rust-lang.org/t/reading-from-physical-address-0x0/117408/5.
|
||||||
|
// This solution falls back to assembler to deal with this.
|
||||||
|
fn read_four_bytes_at_addr_zero(buf: &mut [u8; 4]) {
|
||||||
|
unsafe {
|
||||||
|
core::arch::asm!(
|
||||||
|
"ldr r0, [{0}]", // Load 4 bytes from src into r0 register
|
||||||
|
"str r0, [{1}]", // Store r0 register into first_four_bytes
|
||||||
|
in(reg) BOOTLOADER_START_ADDR as *const u8, // Input: src pointer (0x0)
|
||||||
|
in(reg) buf as *mut [u8; 4], // Input: destination pointer
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
fn check_app_crc(app_sel: AppSel) -> bool {
|
||||||
|
if DEBUG_PRINTOUTS && RTT_PRINTOUT {
|
||||||
|
rprintln!("Checking image {:?}", app_sel);
|
||||||
|
}
|
||||||
|
if app_sel == AppSel::A {
|
||||||
|
check_app_given_addr(APP_A_CRC_ADDR, APP_A_START_ADDR, APP_A_SIZE_ADDR)
|
||||||
|
} else {
|
||||||
|
check_app_given_addr(APP_B_CRC_ADDR, APP_B_START_ADDR, APP_B_SIZE_ADDR)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn check_app_given_addr(crc_addr: u32, start_addr: u32, image_size_addr: u32) -> bool {
|
||||||
|
let crc_exp = unsafe { (crc_addr as *const u16).read_unaligned().to_be() };
|
||||||
|
let image_size = unsafe { (image_size_addr as *const u32).read_unaligned().to_be() };
|
||||||
|
// Sanity check.
|
||||||
|
if image_size > APP_A_END_ADDR - APP_A_START_ADDR - 8 {
|
||||||
|
if RTT_PRINTOUT {
|
||||||
|
rprintln!("detected invalid app size {}", image_size);
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
let crc_calc = CRC_ALGO.checksum(unsafe {
|
||||||
|
core::slice::from_raw_parts(start_addr as *const u8, image_size as usize)
|
||||||
|
});
|
||||||
|
if crc_calc == crc_exp {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
false
|
||||||
|
}
|
||||||
|
|
||||||
|
// The boot works by copying the interrupt vector table (IVT) of the respective app to the
|
||||||
|
// base address in code RAM (0x0) and then performing a soft reset.
|
||||||
|
fn boot_app(
|
||||||
|
syscfg: &pac::Sysconfig,
|
||||||
|
cp: &cortex_m::Peripherals,
|
||||||
|
app_sel: AppSel,
|
||||||
|
timer: &mut CountdownTimer<pac::Tim0>,
|
||||||
|
) -> ! {
|
||||||
|
if DEBUG_PRINTOUTS && RTT_PRINTOUT {
|
||||||
|
rprintln!("booting app {:?}", app_sel);
|
||||||
|
}
|
||||||
|
timer.delay_ms(BOOT_DELAY_MS);
|
||||||
|
|
||||||
|
// Clear all interrupts set.
|
||||||
|
unsafe {
|
||||||
|
cp.NVIC.icer[0].write(0xFFFFFFFF);
|
||||||
|
cp.NVIC.icpr[0].write(0xFFFFFFFF);
|
||||||
|
}
|
||||||
|
// Disable ROM protection.
|
||||||
|
syscfg.rom_prot().write(|w| w.wren().set_bit());
|
||||||
|
let base_addr = if app_sel == AppSel::A {
|
||||||
|
APP_A_START_ADDR
|
||||||
|
} else {
|
||||||
|
APP_B_START_ADDR
|
||||||
|
};
|
||||||
|
unsafe {
|
||||||
|
// First 4 bytes done with inline assembly, writing to the physical address 0x0 can not
|
||||||
|
// be done without it. See https://users.rust-lang.org/t/reading-from-physical-address-0x0/117408/2.
|
||||||
|
let first_four_bytes = core::ptr::read(base_addr as *const u32);
|
||||||
|
core::arch::asm!(
|
||||||
|
"str {0}, [{1}]",
|
||||||
|
in(reg) first_four_bytes, // Input: App vector table.
|
||||||
|
in(reg) BOOTLOADER_START_ADDR as *mut u32, // Input: destination pointer
|
||||||
|
);
|
||||||
|
core::slice::from_raw_parts_mut(
|
||||||
|
(BOOTLOADER_START_ADDR + 4) as *mut u8,
|
||||||
|
(VECTOR_TABLE_LEN - 4) as usize,
|
||||||
|
)
|
||||||
|
.copy_from_slice(core::slice::from_raw_parts(
|
||||||
|
(base_addr + 4) as *const u8,
|
||||||
|
(VECTOR_TABLE_LEN - 4) as usize,
|
||||||
|
));
|
||||||
|
}
|
||||||
|
// Disable re-loading from FRAM/code ROM on soft reset
|
||||||
|
syscfg
|
||||||
|
.rst_cntl_rom()
|
||||||
|
.modify(|_, w| w.sysrstreq().clear_bit());
|
||||||
|
|
||||||
|
soft_reset(cp);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Soft reset based on https://github.com/ARM-software/CMSIS_6/blob/5782d6f8057906d360f4b95ec08a2354afe5c9b9/CMSIS/Core/Include/core_cm0.h#L874.
|
||||||
|
fn soft_reset(cp: &cortex_m::Peripherals) -> ! {
|
||||||
|
// Ensure all outstanding memory accesses included buffered write are completed before reset.
|
||||||
|
cortex_m::asm::dsb();
|
||||||
|
unsafe {
|
||||||
|
cp.SCB
|
||||||
|
.aircr
|
||||||
|
.write((0x5FA << SCB_AIRCR_VECTKEY_POS) | SCB_AIRCR_SYSRESETREQ_MSK);
|
||||||
|
}
|
||||||
|
// Ensure completion of memory access.
|
||||||
|
cortex_m::asm::dsb();
|
||||||
|
|
||||||
|
// Loop until the reset occurs.
|
||||||
|
loop {
|
||||||
|
cortex_m::asm::nop();
|
||||||
|
}
|
||||||
|
}
|
33
examples/README.md
Normal file
33
examples/README.md
Normal file
@ -0,0 +1,33 @@
|
|||||||
|
VA108xx Example Applications
|
||||||
|
========
|
||||||
|
|
||||||
|
This folder contains various examples
|
||||||
|
Consult the main README first for setup of the repository.
|
||||||
|
|
||||||
|
## Simple examples
|
||||||
|
|
||||||
|
```rs
|
||||||
|
cargo run --example blinky
|
||||||
|
```
|
||||||
|
|
||||||
|
You can have a look at the `simple/examples` folder to see all available simple examples
|
||||||
|
|
||||||
|
## RTIC example
|
||||||
|
|
||||||
|
```rs
|
||||||
|
cargo run --bin rtic-example
|
||||||
|
```
|
||||||
|
|
||||||
|
## Embassy example
|
||||||
|
|
||||||
|
Blinky with time driver IRQs in library
|
||||||
|
|
||||||
|
```rs
|
||||||
|
cargo run --bin embassy-example
|
||||||
|
```
|
||||||
|
|
||||||
|
Blinky with custom time driver IRQs
|
||||||
|
|
||||||
|
```rs
|
||||||
|
cargo run --bin embassy-example --no-default-features --features custom-irqs
|
||||||
|
```
|
37
examples/embassy/Cargo.toml
Normal file
37
examples/embassy/Cargo.toml
Normal file
@ -0,0 +1,37 @@
|
|||||||
|
[package]
|
||||||
|
name = "embassy-example"
|
||||||
|
version = "0.1.0"
|
||||||
|
edition = "2021"
|
||||||
|
|
||||||
|
[dependencies]
|
||||||
|
cfg-if = "1"
|
||||||
|
cortex-m = { version = "0.7", features = ["critical-section-single-core"] }
|
||||||
|
cortex-m-rt = "0.7"
|
||||||
|
embedded-hal = "1"
|
||||||
|
embedded-hal-async = "1"
|
||||||
|
embedded-io = "0.6"
|
||||||
|
embedded-io-async = "0.6"
|
||||||
|
heapless = "0.8"
|
||||||
|
static_cell = "2"
|
||||||
|
|
||||||
|
rtt-target = "0.6"
|
||||||
|
panic-rtt-target = "0.2"
|
||||||
|
critical-section = "1"
|
||||||
|
portable-atomic = { version = "1", features = ["unsafe-assume-single-core"]}
|
||||||
|
|
||||||
|
embassy-sync = "0.6"
|
||||||
|
embassy-time = "0.4"
|
||||||
|
embassy-executor = { version = "0.7", features = [
|
||||||
|
"arch-cortex-m",
|
||||||
|
"executor-thread",
|
||||||
|
"executor-interrupt"
|
||||||
|
]}
|
||||||
|
|
||||||
|
va108xx-hal = "0.9"
|
||||||
|
va108xx-embassy = { path = "../../va108xx-embassy", default-features = false }
|
||||||
|
|
||||||
|
[features]
|
||||||
|
default = ["ticks-hz-1_000", "va108xx-embassy/irq-oc30-oc31"]
|
||||||
|
custom-irqs = []
|
||||||
|
ticks-hz-1_000 = ["embassy-time/tick-hz-1_000"]
|
||||||
|
ticks-hz-32_768 = ["embassy-time/tick-hz-32_768"]
|
258
examples/embassy/src/bin/async-gpio.rs
Normal file
258
examples/embassy/src/bin/async-gpio.rs
Normal file
@ -0,0 +1,258 @@
|
|||||||
|
//! 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::digital::{InputPin, OutputPin, StatefulOutputPin};
|
||||||
|
use embedded_hal_async::digital::Wait;
|
||||||
|
use panic_rtt_target as _;
|
||||||
|
use rtt_target::{rprintln, rtt_init_print};
|
||||||
|
use va108xx_embassy::embassy;
|
||||||
|
use va108xx_hal::gpio::{on_interrupt_for_asynch_gpio, InputDynPinAsync, InputPinAsync, PinsB};
|
||||||
|
use va108xx_hal::{
|
||||||
|
gpio::{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.
|
||||||
|
unsafe {
|
||||||
|
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().unwrap();
|
||||||
|
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_asynch_gpio();
|
||||||
|
}
|
||||||
|
|
||||||
|
#[interrupt]
|
||||||
|
#[allow(non_snake_case)]
|
||||||
|
fn OC11() {
|
||||||
|
on_interrupt_for_asynch_gpio();
|
||||||
|
}
|
171
examples/embassy/src/bin/async-uart-rx.rs
Normal file
171
examples/embassy/src/bin/async-uart-rx.rs
Normal file
@ -0,0 +1,171 @@
|
|||||||
|
//! Asynchronous UART reception example application.
|
||||||
|
//!
|
||||||
|
//! This application receives data on two UARTs permanently using a ring buffer.
|
||||||
|
//! The ring buffer are read them asynchronously. UART A is received on ports PA8 and PA9.
|
||||||
|
//! UART B is received on ports PA2 and PA3.
|
||||||
|
//!
|
||||||
|
//! Instructions:
|
||||||
|
//!
|
||||||
|
//! 1. Tie a USB to UART converter with RX to PA9 and TX to PA8 for UART A.
|
||||||
|
//! Tie a USB to UART converter with RX to PA3 and TX to PA2 for UART B.
|
||||||
|
//! 2. Connect to the serial interface by using an application like Putty or picocom. You can
|
||||||
|
//! type something in the terminal and check if the data is echoed back. You can also check the
|
||||||
|
//! RTT logs to see received data.
|
||||||
|
#![no_std]
|
||||||
|
#![no_main]
|
||||||
|
use core::cell::RefCell;
|
||||||
|
|
||||||
|
use critical_section::Mutex;
|
||||||
|
use embassy_executor::Spawner;
|
||||||
|
use embassy_time::Instant;
|
||||||
|
use embedded_hal::digital::StatefulOutputPin;
|
||||||
|
use embedded_io::Write;
|
||||||
|
use embedded_io_async::Read;
|
||||||
|
use heapless::spsc::{Consumer, Producer, Queue};
|
||||||
|
use panic_rtt_target as _;
|
||||||
|
use rtt_target::{rprintln, rtt_init_print};
|
||||||
|
use va108xx_embassy::embassy;
|
||||||
|
use va108xx_hal::{
|
||||||
|
gpio::PinsA,
|
||||||
|
pac::{self, interrupt},
|
||||||
|
prelude::*,
|
||||||
|
uart::{
|
||||||
|
self, on_interrupt_uart_b_overwriting,
|
||||||
|
rx_asynch::{on_interrupt_uart_a, RxAsync},
|
||||||
|
RxAsyncSharedConsumer, Tx,
|
||||||
|
},
|
||||||
|
InterruptConfig,
|
||||||
|
};
|
||||||
|
|
||||||
|
const SYSCLK_FREQ: Hertz = Hertz::from_raw(50_000_000);
|
||||||
|
|
||||||
|
static QUEUE_UART_A: static_cell::ConstStaticCell<Queue<u8, 256>> =
|
||||||
|
static_cell::ConstStaticCell::new(Queue::new());
|
||||||
|
static PRODUCER_UART_A: Mutex<RefCell<Option<Producer<u8, 256>>>> = Mutex::new(RefCell::new(None));
|
||||||
|
|
||||||
|
static QUEUE_UART_B: static_cell::ConstStaticCell<Queue<u8, 256>> =
|
||||||
|
static_cell::ConstStaticCell::new(Queue::new());
|
||||||
|
static PRODUCER_UART_B: Mutex<RefCell<Option<Producer<u8, 256>>>> = Mutex::new(RefCell::new(None));
|
||||||
|
static CONSUMER_UART_B: Mutex<RefCell<Option<Consumer<u8, 256>>>> = Mutex::new(RefCell::new(None));
|
||||||
|
|
||||||
|
// main is itself an async function.
|
||||||
|
#[embassy_executor::main]
|
||||||
|
async fn main(spawner: Spawner) {
|
||||||
|
rtt_init_print!();
|
||||||
|
rprintln!("-- VA108xx Async UART RX Demo --");
|
||||||
|
|
||||||
|
let mut dp = pac::Peripherals::take().unwrap();
|
||||||
|
|
||||||
|
// Safety: Only called once here.
|
||||||
|
unsafe {
|
||||||
|
embassy::init(
|
||||||
|
&mut dp.sysconfig,
|
||||||
|
&dp.irqsel,
|
||||||
|
SYSCLK_FREQ,
|
||||||
|
dp.tim23,
|
||||||
|
dp.tim22,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
let porta = PinsA::new(&mut dp.sysconfig, dp.porta);
|
||||||
|
let mut led0 = porta.pa10.into_readable_push_pull_output();
|
||||||
|
let mut led1 = porta.pa7.into_readable_push_pull_output();
|
||||||
|
let mut led2 = porta.pa6.into_readable_push_pull_output();
|
||||||
|
|
||||||
|
let tx_uart_a = porta.pa9.into_funsel_2();
|
||||||
|
let rx_uart_a = porta.pa8.into_funsel_2();
|
||||||
|
|
||||||
|
let uarta = uart::Uart::new_with_interrupt(
|
||||||
|
&mut dp.sysconfig,
|
||||||
|
50.MHz(),
|
||||||
|
dp.uarta,
|
||||||
|
(tx_uart_a, rx_uart_a),
|
||||||
|
115200.Hz(),
|
||||||
|
InterruptConfig::new(pac::Interrupt::OC2, true, true),
|
||||||
|
);
|
||||||
|
|
||||||
|
let tx_uart_b = porta.pa3.into_funsel_2();
|
||||||
|
let rx_uart_b = porta.pa2.into_funsel_2();
|
||||||
|
|
||||||
|
let uartb = uart::Uart::new_with_interrupt(
|
||||||
|
&mut dp.sysconfig,
|
||||||
|
50.MHz(),
|
||||||
|
dp.uartb,
|
||||||
|
(tx_uart_b, rx_uart_b),
|
||||||
|
115200.Hz(),
|
||||||
|
InterruptConfig::new(pac::Interrupt::OC3, true, true),
|
||||||
|
);
|
||||||
|
let (mut tx_uart_a, rx_uart_a) = uarta.split();
|
||||||
|
let (tx_uart_b, rx_uart_b) = uartb.split();
|
||||||
|
let (prod_uart_a, cons_uart_a) = QUEUE_UART_A.take().split();
|
||||||
|
// Pass the producer to the interrupt handler.
|
||||||
|
let (prod_uart_b, cons_uart_b) = QUEUE_UART_B.take().split();
|
||||||
|
critical_section::with(|cs| {
|
||||||
|
*PRODUCER_UART_A.borrow(cs).borrow_mut() = Some(prod_uart_a);
|
||||||
|
*PRODUCER_UART_B.borrow(cs).borrow_mut() = Some(prod_uart_b);
|
||||||
|
*CONSUMER_UART_B.borrow(cs).borrow_mut() = Some(cons_uart_b);
|
||||||
|
});
|
||||||
|
let mut async_rx_uart_a = RxAsync::new(rx_uart_a, cons_uart_a);
|
||||||
|
let async_rx_uart_b = RxAsyncSharedConsumer::new(rx_uart_b, &CONSUMER_UART_B);
|
||||||
|
spawner
|
||||||
|
.spawn(uart_b_task(async_rx_uart_b, tx_uart_b))
|
||||||
|
.unwrap();
|
||||||
|
let mut buf = [0u8; 256];
|
||||||
|
loop {
|
||||||
|
rprintln!("Current time UART A: {}", Instant::now().as_secs());
|
||||||
|
led0.toggle().ok();
|
||||||
|
led1.toggle().ok();
|
||||||
|
led2.toggle().ok();
|
||||||
|
let read_bytes = async_rx_uart_a.read(&mut buf).await.unwrap();
|
||||||
|
let read_str = core::str::from_utf8(&buf[..read_bytes]).unwrap();
|
||||||
|
rprintln!(
|
||||||
|
"Read {} bytes asynchronously on UART A: {:?}",
|
||||||
|
read_bytes,
|
||||||
|
read_str
|
||||||
|
);
|
||||||
|
tx_uart_a.write_all(read_str.as_bytes()).unwrap();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[embassy_executor::task]
|
||||||
|
async fn uart_b_task(mut async_rx: RxAsyncSharedConsumer<pac::Uartb, 256>, mut tx: Tx<pac::Uartb>) {
|
||||||
|
let mut buf = [0u8; 256];
|
||||||
|
loop {
|
||||||
|
rprintln!("Current time UART B: {}", Instant::now().as_secs());
|
||||||
|
// Infallible asynchronous operation.
|
||||||
|
let read_bytes = async_rx.read(&mut buf).await.unwrap();
|
||||||
|
let read_str = core::str::from_utf8(&buf[..read_bytes]).unwrap();
|
||||||
|
rprintln!(
|
||||||
|
"Read {} bytes asynchronously on UART B: {:?}",
|
||||||
|
read_bytes,
|
||||||
|
read_str
|
||||||
|
);
|
||||||
|
tx.write_all(read_str.as_bytes()).unwrap();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[interrupt]
|
||||||
|
#[allow(non_snake_case)]
|
||||||
|
fn OC2() {
|
||||||
|
let mut prod =
|
||||||
|
critical_section::with(|cs| PRODUCER_UART_A.borrow(cs).borrow_mut().take().unwrap());
|
||||||
|
let errors = on_interrupt_uart_a(&mut prod);
|
||||||
|
critical_section::with(|cs| *PRODUCER_UART_A.borrow(cs).borrow_mut() = Some(prod));
|
||||||
|
// In a production app, we could use a channel to send the errors to the main task.
|
||||||
|
if let Err(errors) = errors {
|
||||||
|
rprintln!("UART A errors: {:?}", errors);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[interrupt]
|
||||||
|
#[allow(non_snake_case)]
|
||||||
|
fn OC3() {
|
||||||
|
let mut prod =
|
||||||
|
critical_section::with(|cs| PRODUCER_UART_B.borrow(cs).borrow_mut().take().unwrap());
|
||||||
|
let errors = on_interrupt_uart_b_overwriting(&mut prod, &CONSUMER_UART_B);
|
||||||
|
critical_section::with(|cs| *PRODUCER_UART_B.borrow(cs).borrow_mut() = Some(prod));
|
||||||
|
// In a production app, we could use a channel to send the errors to the main task.
|
||||||
|
if let Err(errors) = errors {
|
||||||
|
rprintln!("UART B errors: {:?}", errors);
|
||||||
|
}
|
||||||
|
}
|
97
examples/embassy/src/bin/async-uart-tx.rs
Normal file
97
examples/embassy/src/bin/async-uart-tx.rs
Normal file
@ -0,0 +1,97 @@
|
|||||||
|
//! 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_hal::digital::StatefulOutputPin;
|
||||||
|
use embedded_io_async::Write;
|
||||||
|
use panic_rtt_target as _;
|
||||||
|
use rtt_target::{rprintln, rtt_init_print};
|
||||||
|
use va108xx_embassy::embassy;
|
||||||
|
use va108xx_hal::{
|
||||||
|
gpio::PinsA,
|
||||||
|
pac::{self, interrupt},
|
||||||
|
prelude::*,
|
||||||
|
uart::{self, on_interrupt_uart_a_tx, TxAsync},
|
||||||
|
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.
|
||||||
|
unsafe {
|
||||||
|
embassy::init(
|
||||||
|
&mut dp.sysconfig,
|
||||||
|
&dp.irqsel,
|
||||||
|
SYSCLK_FREQ,
|
||||||
|
dp.tim23,
|
||||||
|
dp.tim22,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
let porta = PinsA::new(&mut dp.sysconfig, dp.porta);
|
||||||
|
let mut led0 = porta.pa10.into_readable_push_pull_output();
|
||||||
|
let mut led1 = porta.pa7.into_readable_push_pull_output();
|
||||||
|
let mut led2 = porta.pa6.into_readable_push_pull_output();
|
||||||
|
|
||||||
|
let tx = 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().ok();
|
||||||
|
led1.toggle().ok();
|
||||||
|
led2.toggle().ok();
|
||||||
|
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_uart_a_tx();
|
||||||
|
}
|
67
examples/embassy/src/main.rs
Normal file
67
examples/embassy/src/main.rs
Normal file
@ -0,0 +1,67 @@
|
|||||||
|
#![no_std]
|
||||||
|
#![no_main]
|
||||||
|
use embassy_executor::Spawner;
|
||||||
|
use embassy_time::{Duration, Instant, Ticker};
|
||||||
|
use embedded_hal::digital::StatefulOutputPin;
|
||||||
|
use panic_rtt_target as _;
|
||||||
|
use rtt_target::{rprintln, rtt_init_print};
|
||||||
|
use va108xx_embassy::embassy;
|
||||||
|
|
||||||
|
cfg_if::cfg_if! {
|
||||||
|
if #[cfg(feature = "custom-irqs")] {
|
||||||
|
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::*};
|
||||||
|
|
||||||
|
const SYSCLK_FREQ: Hertz = Hertz::from_raw(50_000_000);
|
||||||
|
|
||||||
|
// main is itself an async function.
|
||||||
|
#[embassy_executor::main]
|
||||||
|
async fn main(_spawner: Spawner) {
|
||||||
|
rtt_init_print!();
|
||||||
|
rprintln!("-- VA108xx Embassy Demo --");
|
||||||
|
|
||||||
|
let mut dp = pac::Peripherals::take().unwrap();
|
||||||
|
|
||||||
|
// Safety: Only called once here.
|
||||||
|
unsafe {
|
||||||
|
cfg_if::cfg_if! {
|
||||||
|
if #[cfg(not(feature = "custom-irqs"))] {
|
||||||
|
embassy::init(
|
||||||
|
&mut dp.sysconfig,
|
||||||
|
&dp.irqsel,
|
||||||
|
SYSCLK_FREQ,
|
||||||
|
dp.tim23,
|
||||||
|
dp.tim22,
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
embassy::init_with_custom_irqs(
|
||||||
|
&mut dp.sysconfig,
|
||||||
|
&dp.irqsel,
|
||||||
|
SYSCLK_FREQ,
|
||||||
|
dp.tim23,
|
||||||
|
dp.tim22,
|
||||||
|
pac::Interrupt::OC23,
|
||||||
|
pac::Interrupt::OC24,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
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 mut ticker = Ticker::every(Duration::from_secs(1));
|
||||||
|
loop {
|
||||||
|
ticker.next().await;
|
||||||
|
rprintln!("Current time: {}", Instant::now().as_secs());
|
||||||
|
led0.toggle().ok();
|
||||||
|
led1.toggle().ok();
|
||||||
|
led2.toggle().ok();
|
||||||
|
}
|
||||||
|
}
|
26
examples/rtic/Cargo.toml
Normal file
26
examples/rtic/Cargo.toml
Normal file
@ -0,0 +1,26 @@
|
|||||||
|
[package]
|
||||||
|
name = "rtic-example"
|
||||||
|
version = "0.1.0"
|
||||||
|
edition = "2021"
|
||||||
|
|
||||||
|
[dependencies]
|
||||||
|
cortex-m = { version = "0.7", features = ["critical-section-single-core"] }
|
||||||
|
cortex-m-rt = "0.7"
|
||||||
|
embedded-hal = "1"
|
||||||
|
embedded-io = "0.6"
|
||||||
|
rtt-target = "0.6"
|
||||||
|
panic-rtt-target = "0.2"
|
||||||
|
|
||||||
|
# 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.
|
||||||
|
portable-atomic = { version = "1", features = ["unsafe-assume-single-core"]}
|
||||||
|
|
||||||
|
rtic = { version = "2", features = ["thumbv6-backend"] }
|
||||||
|
rtic-monotonics = { version = "2", features = ["cortex-m-systick"] }
|
||||||
|
rtic-sync = { version = "1.3", features = ["defmt-03"] }
|
||||||
|
|
||||||
|
once_cell = {version = "1", default-features = false, features = ["critical-section"]}
|
||||||
|
ringbuf = { version = "0.4.7", default-features = false, features = ["portable-atomic"] }
|
||||||
|
|
||||||
|
va108xx-hal = "0.9"
|
||||||
|
vorago-reb1 = { path = "../../vorago-reb1" }
|
@ -5,18 +5,20 @@
|
|||||||
#[rtic::app(device = pac)]
|
#[rtic::app(device = pac)]
|
||||||
mod app {
|
mod app {
|
||||||
use panic_rtt_target as _;
|
use panic_rtt_target as _;
|
||||||
use rtic_monotonics::systick::Systick;
|
use rtic_example::SYSCLK_FREQ;
|
||||||
use rtt_target::{rprintln, rtt_init_default, set_print_channel};
|
use rtt_target::{rprintln, rtt_init_default, set_print_channel};
|
||||||
use va108xx_hal::{
|
use va108xx_hal::{
|
||||||
clock::{set_clk_div_register, FilterClkSel},
|
clock::{set_clk_div_register, FilterClkSel},
|
||||||
gpio::{FilterType, InterruptEdge, PinsA},
|
gpio::{FilterType, InterruptEdge, PinsA},
|
||||||
pac,
|
pac,
|
||||||
prelude::*,
|
prelude::*,
|
||||||
timer::{default_ms_irq_handler, set_up_ms_tick, IrqCfg},
|
timer::{default_ms_irq_handler, set_up_ms_tick, InterruptConfig},
|
||||||
};
|
};
|
||||||
use vorago_reb1::button::Button;
|
use vorago_reb1::button::Button;
|
||||||
use vorago_reb1::leds::Leds;
|
use vorago_reb1::leds::Leds;
|
||||||
|
|
||||||
|
rtic_monotonics::systick_monotonic!(Mono, 1_000);
|
||||||
|
|
||||||
#[derive(Debug, PartialEq)]
|
#[derive(Debug, PartialEq)]
|
||||||
pub enum PressMode {
|
pub enum PressMode {
|
||||||
Toggle,
|
Toggle,
|
||||||
@ -44,17 +46,11 @@ mod app {
|
|||||||
struct Shared {}
|
struct Shared {}
|
||||||
|
|
||||||
#[init]
|
#[init]
|
||||||
fn init(ctx: init::Context) -> (Shared, Local) {
|
fn init(cx: init::Context) -> (Shared, Local) {
|
||||||
let channels = rtt_init_default!();
|
let channels = rtt_init_default!();
|
||||||
set_print_channel(channels.up.0);
|
set_print_channel(channels.up.0);
|
||||||
rprintln!("-- Vorago Button IRQ Example --");
|
rprintln!("-- Vorago Button IRQ Example --");
|
||||||
// Initialize the systick interrupt & obtain the token to prove that we did
|
Mono::start(cx.core.SYST, SYSCLK_FREQ.raw());
|
||||||
let systick_mono_token = rtic_monotonics::create_systick_token!();
|
|
||||||
Systick::start(
|
|
||||||
ctx.core.SYST,
|
|
||||||
Hertz::from(50.MHz()).raw(),
|
|
||||||
systick_mono_token,
|
|
||||||
);
|
|
||||||
|
|
||||||
let mode = match CFG_MODE {
|
let mode = match CFG_MODE {
|
||||||
// Ask mode from user via RTT
|
// Ask mode from user via RTT
|
||||||
@ -64,24 +60,25 @@ mod app {
|
|||||||
};
|
};
|
||||||
rprintln!("Using {:?} mode", mode);
|
rprintln!("Using {:?} mode", mode);
|
||||||
|
|
||||||
let mut dp = ctx.device;
|
let mut dp = cx.device;
|
||||||
let pinsa = PinsA::new(&mut dp.sysconfig, Some(dp.ioconfig), dp.porta);
|
let pinsa = PinsA::new(&mut dp.sysconfig, 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()).edge_irq(
|
let mut button = Button::new(pinsa.pa11.into_floating_input());
|
||||||
|
button.configure_edge_interrupt(
|
||||||
edge_irq,
|
edge_irq,
|
||||||
IrqCfg::new(pac::interrupt::OC15, true, true),
|
InterruptConfig::new(pac::interrupt::OC15, true, true),
|
||||||
Some(&mut dp.sysconfig),
|
Some(&mut dp.sysconfig),
|
||||||
Some(&mut dp.irqsel),
|
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 = button.filter_type(FilterType::FilterFourClockCycles, FilterClkSel::Clk1);
|
button.configure_filter_type(FilterType::FilterFourClockCycles, FilterClkSel::Clk1);
|
||||||
set_clk_div_register(&mut dp.sysconfig, FilterClkSel::Clk1, 50_000);
|
set_clk_div_register(&mut dp.sysconfig, FilterClkSel::Clk1, 50_000);
|
||||||
}
|
}
|
||||||
let mut leds = Leds::new(
|
let mut leds = Leds::new(
|
||||||
@ -93,7 +90,7 @@ mod app {
|
|||||||
led.off();
|
led.off();
|
||||||
}
|
}
|
||||||
set_up_ms_tick(
|
set_up_ms_tick(
|
||||||
IrqCfg::new(pac::Interrupt::OC0, true, true),
|
InterruptConfig::new(pac::Interrupt::OC0, true, true),
|
||||||
&mut dp.sysconfig,
|
&mut dp.sysconfig,
|
||||||
Some(&mut dp.irqsel),
|
Some(&mut dp.irqsel),
|
||||||
50.MHz(),
|
50.MHz(),
|
||||||
@ -117,12 +114,10 @@ mod app {
|
|||||||
let mode = cx.local.mode;
|
let mode = cx.local.mode;
|
||||||
if *mode == PressMode::Toggle {
|
if *mode == PressMode::Toggle {
|
||||||
leds[0].toggle();
|
leds[0].toggle();
|
||||||
|
} else if button.released() {
|
||||||
|
leds[0].off();
|
||||||
} else {
|
} else {
|
||||||
if button.released() {
|
leds[0].on();
|
||||||
leds[0].off();
|
|
||||||
} else {
|
|
||||||
leds[0].on();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -138,14 +133,11 @@ mod app {
|
|||||||
let mut read;
|
let mut read;
|
||||||
loop {
|
loop {
|
||||||
read = down_channel.read(&mut read_buf);
|
read = down_channel.read(&mut read_buf);
|
||||||
for i in 0..read {
|
for &byte in &read_buf[..read] {
|
||||||
let val = read_buf[i] as char;
|
match byte as char {
|
||||||
if val == '0' || val == '1' {
|
'0' => return PressMode::Toggle,
|
||||||
return if val == '0' {
|
'1' => return PressMode::Keep,
|
||||||
PressMode::Toggle
|
_ => continue, // Ignore other characters
|
||||||
} else {
|
|
||||||
PressMode::Keep
|
|
||||||
};
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
131
examples/rtic/src/bin/uart-echo-rtic.rs
Normal file
131
examples/rtic/src/bin/uart-echo-rtic.rs
Normal file
@ -0,0 +1,131 @@
|
|||||||
|
//! More complex UART application
|
||||||
|
//!
|
||||||
|
//! Uses the IRQ capabilities of the VA10820 peripheral and the RTIC framework to poll the UART in
|
||||||
|
//! a non-blocking way. All received data will be sent back to the sender.
|
||||||
|
#![no_main]
|
||||||
|
#![no_std]
|
||||||
|
|
||||||
|
use ringbuf::StaticRb;
|
||||||
|
|
||||||
|
// Larger buffer for TC to be able to hold the possibly large memory write packets.
|
||||||
|
const RX_RING_BUF_SIZE: usize = 1024;
|
||||||
|
|
||||||
|
#[rtic::app(device = pac, dispatchers = [OC4])]
|
||||||
|
mod app {
|
||||||
|
use super::*;
|
||||||
|
use embedded_io::Write;
|
||||||
|
use panic_rtt_target as _;
|
||||||
|
use ringbuf::traits::{Consumer, Observer, Producer};
|
||||||
|
use rtic_example::SYSCLK_FREQ;
|
||||||
|
use rtic_monotonics::Monotonic;
|
||||||
|
use rtt_target::{rprintln, rtt_init_print};
|
||||||
|
use va108xx_hal::{
|
||||||
|
gpio::PinsA,
|
||||||
|
pac,
|
||||||
|
prelude::*,
|
||||||
|
uart::{self, RxWithInterrupt, Tx},
|
||||||
|
InterruptConfig,
|
||||||
|
};
|
||||||
|
|
||||||
|
#[local]
|
||||||
|
struct Local {
|
||||||
|
rx: RxWithInterrupt<pac::Uarta>,
|
||||||
|
tx: Tx<pac::Uarta>,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[shared]
|
||||||
|
struct Shared {
|
||||||
|
rb: StaticRb<u8, RX_RING_BUF_SIZE>,
|
||||||
|
}
|
||||||
|
|
||||||
|
rtic_monotonics::systick_monotonic!(Mono, 1_000);
|
||||||
|
|
||||||
|
#[init]
|
||||||
|
fn init(cx: init::Context) -> (Shared, Local) {
|
||||||
|
rtt_init_print!();
|
||||||
|
rprintln!("-- VA108xx UART Echo with IRQ example application--");
|
||||||
|
|
||||||
|
Mono::start(cx.core.SYST, SYSCLK_FREQ.raw());
|
||||||
|
|
||||||
|
let mut dp = cx.device;
|
||||||
|
let gpioa = PinsA::new(&mut dp.sysconfig, dp.porta);
|
||||||
|
let tx = gpioa.pa9.into_funsel_2();
|
||||||
|
let rx = gpioa.pa8.into_funsel_2();
|
||||||
|
|
||||||
|
let irq_uart = uart::Uart::new_with_interrupt(
|
||||||
|
&mut dp.sysconfig,
|
||||||
|
SYSCLK_FREQ,
|
||||||
|
dp.uarta,
|
||||||
|
(tx, rx),
|
||||||
|
115200.Hz(),
|
||||||
|
InterruptConfig::new(pac::Interrupt::OC3, true, true),
|
||||||
|
);
|
||||||
|
let (tx, rx) = irq_uart.split();
|
||||||
|
let mut rx = rx.into_rx_with_irq();
|
||||||
|
|
||||||
|
rx.start();
|
||||||
|
|
||||||
|
echo_handler::spawn().unwrap();
|
||||||
|
(
|
||||||
|
Shared {
|
||||||
|
rb: StaticRb::default(),
|
||||||
|
},
|
||||||
|
Local { rx, tx },
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
// `shared` cannot be accessed from this context
|
||||||
|
#[idle]
|
||||||
|
fn idle(_cx: idle::Context) -> ! {
|
||||||
|
loop {
|
||||||
|
cortex_m::asm::nop();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[task(
|
||||||
|
binds = OC3,
|
||||||
|
shared = [rb],
|
||||||
|
local = [
|
||||||
|
rx,
|
||||||
|
],
|
||||||
|
)]
|
||||||
|
fn reception_task(mut cx: reception_task::Context) {
|
||||||
|
let mut buf: [u8; 16] = [0; 16];
|
||||||
|
let mut ringbuf_full = false;
|
||||||
|
let result = cx.local.rx.on_interrupt(&mut buf);
|
||||||
|
if result.bytes_read > 0 && result.errors.is_none() {
|
||||||
|
cx.shared.rb.lock(|rb| {
|
||||||
|
if rb.vacant_len() < result.bytes_read {
|
||||||
|
ringbuf_full = true;
|
||||||
|
} else {
|
||||||
|
rb.push_slice(&buf[0..result.bytes_read]);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
if ringbuf_full {
|
||||||
|
// Could also drop oldest data, but that would require the consumer to be shared.
|
||||||
|
rprintln!("buffer full, data was dropped");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[task(shared = [rb], local = [
|
||||||
|
buf: [u8; RX_RING_BUF_SIZE] = [0; RX_RING_BUF_SIZE],
|
||||||
|
|
||||||
|
tx
|
||||||
|
], priority=1)]
|
||||||
|
async fn echo_handler(mut cx: echo_handler::Context) {
|
||||||
|
loop {
|
||||||
|
cx.shared.rb.lock(|rb| {
|
||||||
|
let bytes_to_read = rb.occupied_len();
|
||||||
|
if bytes_to_read > 0 {
|
||||||
|
let actual_read_bytes = rb.pop_slice(&mut cx.local.buf[0..bytes_to_read]);
|
||||||
|
cx.local
|
||||||
|
.tx
|
||||||
|
.write_all(&cx.local.buf[0..actual_read_bytes])
|
||||||
|
.expect("Failed to write to TX");
|
||||||
|
}
|
||||||
|
});
|
||||||
|
Mono::delay(50.millis()).await;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
4
examples/rtic/src/lib.rs
Normal file
4
examples/rtic/src/lib.rs
Normal file
@ -0,0 +1,4 @@
|
|||||||
|
#![no_std]
|
||||||
|
use va108xx_hal::time::Hertz;
|
||||||
|
|
||||||
|
pub const SYSCLK_FREQ: Hertz = Hertz::from_raw(50_000_000);
|
67
examples/rtic/src/main.rs
Normal file
67
examples/rtic/src/main.rs
Normal file
@ -0,0 +1,67 @@
|
|||||||
|
//! RTIC minimal blinky
|
||||||
|
#![no_main]
|
||||||
|
#![no_std]
|
||||||
|
|
||||||
|
#[rtic::app(device = pac, dispatchers = [OC31, OC30, OC29])]
|
||||||
|
mod app {
|
||||||
|
use cortex_m::asm;
|
||||||
|
use embedded_hal::digital::StatefulOutputPin;
|
||||||
|
use panic_rtt_target as _;
|
||||||
|
use rtic_example::SYSCLK_FREQ;
|
||||||
|
use rtic_monotonics::systick::prelude::*;
|
||||||
|
use rtic_monotonics::Monotonic;
|
||||||
|
use rtt_target::{rprintln, rtt_init_print};
|
||||||
|
use va108xx_hal::{
|
||||||
|
gpio::{OutputReadablePushPull, Pin, PinsA, PA10, PA6, PA7},
|
||||||
|
pac,
|
||||||
|
};
|
||||||
|
|
||||||
|
#[local]
|
||||||
|
struct Local {
|
||||||
|
led0: Pin<PA10, OutputReadablePushPull>,
|
||||||
|
led1: Pin<PA7, OutputReadablePushPull>,
|
||||||
|
led2: Pin<PA6, OutputReadablePushPull>,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[shared]
|
||||||
|
struct Shared {}
|
||||||
|
|
||||||
|
rtic_monotonics::systick_monotonic!(Mono, 1_000);
|
||||||
|
|
||||||
|
#[init]
|
||||||
|
fn init(mut cx: init::Context) -> (Shared, Local) {
|
||||||
|
rtt_init_print!();
|
||||||
|
rprintln!("-- Vorago VA108xx RTIC template --");
|
||||||
|
|
||||||
|
Mono::start(cx.core.SYST, SYSCLK_FREQ.raw());
|
||||||
|
|
||||||
|
let porta = PinsA::new(&mut cx.device.sysconfig, cx.device.porta);
|
||||||
|
let led0 = porta.pa10.into_readable_push_pull_output();
|
||||||
|
let led1 = porta.pa7.into_readable_push_pull_output();
|
||||||
|
let led2 = porta.pa6.into_readable_push_pull_output();
|
||||||
|
blinky::spawn().ok();
|
||||||
|
(Shared {}, Local { led0, led1, led2 })
|
||||||
|
}
|
||||||
|
|
||||||
|
// `shared` cannot be accessed from this context
|
||||||
|
#[idle]
|
||||||
|
fn idle(_cx: idle::Context) -> ! {
|
||||||
|
loop {
|
||||||
|
asm::nop();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[task(
|
||||||
|
priority = 3,
|
||||||
|
local=[led0, led1, led2],
|
||||||
|
)]
|
||||||
|
async fn blinky(cx: blinky::Context) {
|
||||||
|
loop {
|
||||||
|
rprintln!("toggling LEDs");
|
||||||
|
cx.local.led0.toggle().ok();
|
||||||
|
cx.local.led1.toggle().ok();
|
||||||
|
cx.local.led2.toggle().ok();
|
||||||
|
Mono::delay(1000.millis()).await;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -4,30 +4,20 @@ version = "0.1.0"
|
|||||||
edition = "2021"
|
edition = "2021"
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
panic-halt = "0.2"
|
|
||||||
cortex-m = {version = "0.7", features = ["critical-section-single-core"]}
|
cortex-m = {version = "0.7", features = ["critical-section-single-core"]}
|
||||||
panic-rtt-target = "0.1"
|
|
||||||
cortex-m-rt = "0.7"
|
cortex-m-rt = "0.7"
|
||||||
rtt-target = "0.5"
|
panic-halt = "1"
|
||||||
rtic-sync = { version = "1.3", features = ["defmt-03"] }
|
panic-rtt-target = "0.2"
|
||||||
|
critical-section = "1"
|
||||||
|
rtt-target = "0.6"
|
||||||
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"
|
||||||
# I'd really like to use those, but it is tricky without probe-rs..
|
|
||||||
# defmt = "0.3"
|
|
||||||
# defmt-brtt = { version = "0.1", default-features = false, features = ["rtt"] }
|
|
||||||
# panic-probe = { version = "0.3", features = ["print-defmt"] }
|
|
||||||
|
|
||||||
[dependencies.rtic]
|
|
||||||
version = "2"
|
|
||||||
features = ["thumbv6-backend"]
|
|
||||||
|
|
||||||
[dependencies.rtic-monotonics]
|
|
||||||
version = "1"
|
|
||||||
features = ["cortex-m-systick"]
|
|
||||||
|
|
||||||
[dependencies.va108xx-hal]
|
[dependencies.va108xx-hal]
|
||||||
version = "0.7"
|
version = "0.9"
|
||||||
path = "../../va108xx-hal"
|
|
||||||
features = ["rt", "defmt"]
|
features = ["rt", "defmt"]
|
||||||
|
|
||||||
|
[dependencies.vorago-reb1]
|
||||||
|
path = "../../vorago-reb1"
|
||||||
|
@ -16,24 +16,24 @@ use va108xx_hal::{
|
|||||||
gpio::PinsA,
|
gpio::PinsA,
|
||||||
pac::{self, interrupt},
|
pac::{self, interrupt},
|
||||||
prelude::*,
|
prelude::*,
|
||||||
pwm::{default_ms_irq_handler, set_up_ms_tick, CountDownTimer},
|
|
||||||
timer::DelayMs,
|
timer::DelayMs,
|
||||||
IrqCfg,
|
timer::{default_ms_irq_handler, set_up_ms_tick, CountdownTimer},
|
||||||
|
InterruptConfig,
|
||||||
};
|
};
|
||||||
|
|
||||||
#[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(
|
||||||
IrqCfg::new(interrupt::OC0, true, true),
|
InterruptConfig::new(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,
|
||||||
))
|
))
|
||||||
.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, Some(dp.ioconfig), dp.porta);
|
let porta = PinsA::new(&mut dp.sysconfig, 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();
|
||||||
|
@ -17,13 +17,13 @@ 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, IrqCfg,
|
CountdownTimer, Event, InterruptConfig,
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
static CSD_TGT_1: Mutex<RefCell<Option<CountDownTimer<pac::Tim4>>>> =
|
static CSD_TGT_1: Mutex<RefCell<Option<CountdownTimer<pac::Tim4>>>> =
|
||||||
Mutex::new(RefCell::new(None));
|
Mutex::new(RefCell::new(None));
|
||||||
static CSD_TGT_2: Mutex<RefCell<Option<CountDownTimer<pac::Tim5>>>> =
|
static CSD_TGT_2: Mutex<RefCell<Option<CountdownTimer<pac::Tim5>>>> =
|
||||||
Mutex::new(RefCell::new(None));
|
Mutex::new(RefCell::new(None));
|
||||||
|
|
||||||
#[entry]
|
#[entry]
|
||||||
@ -36,19 +36,19 @@ fn main() -> ! {
|
|||||||
|
|
||||||
// Will be started periodically to trigger a cascade
|
// Will be started periodically to trigger a cascade
|
||||||
let mut cascade_triggerer =
|
let mut cascade_triggerer =
|
||||||
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,
|
||||||
IrqCfg::new(pac::Interrupt::OC1, true, false),
|
InterruptConfig::new(pac::Interrupt::OC1, true, false),
|
||||||
Some(&mut dp.irqsel),
|
Some(&mut dp.irqsel),
|
||||||
Some(&mut dp.sysconfig),
|
Some(&mut dp.sysconfig),
|
||||||
);
|
);
|
||||||
|
|
||||||
// First target for cascade
|
// First target for cascade
|
||||||
let mut cascade_target_1 =
|
let mut cascade_target_1 =
|
||||||
CountDownTimer::new(&mut dp.sysconfig, 50.MHz(), dp.tim4).auto_deactivate(true);
|
CountdownTimer::new(&mut dp.sysconfig, 50.MHz(), dp.tim4).auto_deactivate(true);
|
||||||
cascade_target_1
|
cascade_target_1
|
||||||
.cascade_0_source(CascadeSource::TimBase, Some(3))
|
.cascade_0_source(CascadeSource::Tim(3))
|
||||||
.expect("Configuring cascade source for TIM4 failed");
|
.expect("Configuring cascade source for TIM4 failed");
|
||||||
let mut csd_cfg = CascadeCtrl {
|
let mut csd_cfg = CascadeCtrl {
|
||||||
enb_start_src_csd0: true,
|
enb_start_src_csd0: true,
|
||||||
@ -62,7 +62,7 @@ fn main() -> ! {
|
|||||||
// the timer expires
|
// the timer expires
|
||||||
cascade_target_1.listen(
|
cascade_target_1.listen(
|
||||||
Event::TimeOut,
|
Event::TimeOut,
|
||||||
IrqCfg::new(pac::Interrupt::OC2, true, false),
|
InterruptConfig::new(pac::Interrupt::OC2, true, false),
|
||||||
Some(&mut dp.irqsel),
|
Some(&mut dp.irqsel),
|
||||||
Some(&mut dp.sysconfig),
|
Some(&mut dp.sysconfig),
|
||||||
);
|
);
|
||||||
@ -72,10 +72,10 @@ fn main() -> ! {
|
|||||||
|
|
||||||
// Activated by first cascade target
|
// Activated by first cascade target
|
||||||
let mut cascade_target_2 =
|
let mut cascade_target_2 =
|
||||||
CountDownTimer::new(&mut dp.sysconfig, 50.MHz(), dp.tim5).auto_deactivate(true);
|
CountdownTimer::new(&mut dp.sysconfig, 50.MHz(), dp.tim5).auto_deactivate(true);
|
||||||
// Set TIM4 as cascade source
|
// Set TIM4 as cascade source
|
||||||
cascade_target_2
|
cascade_target_2
|
||||||
.cascade_1_source(CascadeSource::TimBase, Some(4))
|
.cascade_1_source(CascadeSource::Tim(4))
|
||||||
.expect("Configuring cascade source for TIM5 failed");
|
.expect("Configuring cascade source for TIM5 failed");
|
||||||
|
|
||||||
csd_cfg = CascadeCtrl::default();
|
csd_cfg = CascadeCtrl::default();
|
||||||
@ -88,7 +88,7 @@ fn main() -> ! {
|
|||||||
// the timer expires
|
// the timer expires
|
||||||
cascade_target_2.listen(
|
cascade_target_2.listen(
|
||||||
Event::TimeOut,
|
Event::TimeOut,
|
||||||
IrqCfg::new(pac::Interrupt::OC3, true, false),
|
InterruptConfig::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, None, dp.porta);
|
let pinsa = PinsA::new(&mut dp.sysconfig, dp.porta);
|
||||||
let mut pwm = pwm::PwmPin::new(
|
let mut pwm = pwm::PwmPin::new(
|
||||||
&mut dp.sysconfig,
|
&mut dp.sysconfig,
|
||||||
50.MHz(),
|
50.MHz(),
|
||||||
|
@ -15,17 +15,16 @@ use va108xx_hal::{
|
|||||||
gpio::{PinsA, PinsB},
|
gpio::{PinsA, PinsB},
|
||||||
pac::{self, interrupt},
|
pac::{self, interrupt},
|
||||||
prelude::*,
|
prelude::*,
|
||||||
pwm::{default_ms_irq_handler, set_up_ms_tick},
|
spi::{self, Spi, SpiBase, SpiClkConfig, TransferConfigWithHwcs},
|
||||||
spi::{self, Spi, SpiBase, TransferConfig},
|
timer::{default_ms_irq_handler, set_up_ms_tick},
|
||||||
IrqCfg,
|
InterruptConfig,
|
||||||
};
|
};
|
||||||
|
|
||||||
#[derive(PartialEq, Debug)]
|
#[derive(PartialEq, Debug)]
|
||||||
pub enum ExampleSelect {
|
pub enum ExampleSelect {
|
||||||
// Enter loopback mode. It is not necessary to tie MOSI/MISO together for this
|
// Enter loopback mode. It is not necessary to tie MOSI/MISO together for this
|
||||||
Loopback,
|
Loopback,
|
||||||
// Send a test buffer and print everything received
|
MosiMisoTiedTogetherManually,
|
||||||
TestBuffer,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(PartialEq, Debug)]
|
#[derive(PartialEq, Debug)]
|
||||||
@ -48,17 +47,19 @@ 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(
|
||||||
IrqCfg::new(interrupt::OC0, true, true),
|
InterruptConfig::new(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,
|
||||||
);
|
);
|
||||||
|
|
||||||
|
let spi_clk_cfg = SpiClkConfig::from_clk(50.MHz(), SPI_SPEED_KHZ.kHz())
|
||||||
|
.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, None, dp.porta);
|
let pinsa = PinsA::new(&mut dp.sysconfig, dp.porta);
|
||||||
let pinsb = PinsB::new(&mut dp.sysconfig, Some(dp.ioconfig), dp.portb);
|
let pinsb = PinsB::new(&mut dp.sysconfig, 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 {
|
||||||
@ -79,7 +80,6 @@ fn main() -> ! {
|
|||||||
dp.spia,
|
dp.spia,
|
||||||
(sck, miso, mosi),
|
(sck, miso, mosi),
|
||||||
spi_cfg,
|
spi_cfg,
|
||||||
None,
|
|
||||||
);
|
);
|
||||||
spia.set_fill_word(FILL_WORD);
|
spia.set_fill_word(FILL_WORD);
|
||||||
spia_ref.borrow_mut().replace(spia.downgrade());
|
spia_ref.borrow_mut().replace(spia.downgrade());
|
||||||
@ -96,7 +96,6 @@ fn main() -> ! {
|
|||||||
dp.spia,
|
dp.spia,
|
||||||
(sck, miso, mosi),
|
(sck, miso, mosi),
|
||||||
spi_cfg,
|
spi_cfg,
|
||||||
None,
|
|
||||||
);
|
);
|
||||||
spia.set_fill_word(FILL_WORD);
|
spia.set_fill_word(FILL_WORD);
|
||||||
spia_ref.borrow_mut().replace(spia.downgrade());
|
spia_ref.borrow_mut().replace(spia.downgrade());
|
||||||
@ -113,7 +112,6 @@ fn main() -> ! {
|
|||||||
dp.spib,
|
dp.spib,
|
||||||
(sck, miso, mosi),
|
(sck, miso, mosi),
|
||||||
spi_cfg,
|
spi_cfg,
|
||||||
None,
|
|
||||||
);
|
);
|
||||||
spib.set_fill_word(FILL_WORD);
|
spib.set_fill_word(FILL_WORD);
|
||||||
spib_ref.borrow_mut().replace(spib.downgrade());
|
spib_ref.borrow_mut().replace(spib.downgrade());
|
||||||
@ -123,19 +121,25 @@ fn main() -> ! {
|
|||||||
match SPI_BUS_SEL {
|
match SPI_BUS_SEL {
|
||||||
SpiBusSelect::SpiAPortA | SpiBusSelect::SpiAPortB => {
|
SpiBusSelect::SpiAPortA | SpiBusSelect::SpiAPortB => {
|
||||||
if let Some(ref mut spi) = *spia_ref.borrow_mut() {
|
if let Some(ref mut spi) = *spia_ref.borrow_mut() {
|
||||||
let transfer_cfg =
|
let transfer_cfg = TransferConfigWithHwcs::new_no_hw_cs(
|
||||||
TransferConfig::new_no_hw_cs(SPI_SPEED_KHZ.kHz(), SPI_MODE, BLOCKMODE, false);
|
Some(spi_clk_cfg),
|
||||||
|
Some(SPI_MODE),
|
||||||
|
BLOCKMODE,
|
||||||
|
true,
|
||||||
|
false,
|
||||||
|
);
|
||||||
spi.cfg_transfer(&transfer_cfg);
|
spi.cfg_transfer(&transfer_cfg);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
SpiBusSelect::SpiBPortB => {
|
SpiBusSelect::SpiBPortB => {
|
||||||
if let Some(ref mut spi) = *spib_ref.borrow_mut() {
|
if let Some(ref mut spi) = *spib_ref.borrow_mut() {
|
||||||
let hw_cs_pin = pinsb.pb2.into_funsel_1();
|
let hw_cs_pin = pinsb.pb2.into_funsel_1();
|
||||||
let transfer_cfg = TransferConfig::new(
|
let transfer_cfg = TransferConfigWithHwcs::new(
|
||||||
SPI_SPEED_KHZ.kHz(),
|
Some(spi_clk_cfg),
|
||||||
SPI_MODE,
|
Some(SPI_MODE),
|
||||||
Some(hw_cs_pin),
|
Some(hw_cs_pin),
|
||||||
BLOCKMODE,
|
BLOCKMODE,
|
||||||
|
true,
|
||||||
false,
|
false,
|
||||||
);
|
);
|
||||||
spi.cfg_transfer(&transfer_cfg);
|
spi.cfg_transfer(&transfer_cfg);
|
||||||
@ -149,92 +153,64 @@ fn main() -> ! {
|
|||||||
match SPI_BUS_SEL {
|
match SPI_BUS_SEL {
|
||||||
SpiBusSelect::SpiAPortA | SpiBusSelect::SpiAPortB => {
|
SpiBusSelect::SpiAPortA | SpiBusSelect::SpiAPortB => {
|
||||||
if let Some(ref mut spi) = *spia_ref.borrow_mut() {
|
if let Some(ref mut spi) = *spia_ref.borrow_mut() {
|
||||||
if EXAMPLE_SEL == ExampleSelect::Loopback {
|
// Can't really verify correct reply here.
|
||||||
// Can't really verify correct reply here.
|
spi.write(&[0x42]).expect("write failed");
|
||||||
spi.write(&[0x42]).expect("write failed");
|
// Because of the loopback mode, we should get back the fill word here.
|
||||||
// Because of the loopback mode, we should get back the fill word here.
|
spi.read(&mut reply_buf[0..1]).unwrap();
|
||||||
spi.read(&mut reply_buf[0..1]).unwrap();
|
assert_eq!(reply_buf[0], FILL_WORD);
|
||||||
assert_eq!(reply_buf[0], FILL_WORD);
|
delay.delay_ms(500_u32);
|
||||||
delay.delay_ms(500_u32);
|
|
||||||
|
|
||||||
let tx_buf: [u8; 3] = [0x01, 0x02, 0x03];
|
let tx_buf: [u8; 3] = [0x01, 0x02, 0x03];
|
||||||
spi.transfer(&mut reply_buf[0..3], &tx_buf).unwrap();
|
spi.transfer(&mut reply_buf[0..3], &tx_buf).unwrap();
|
||||||
assert_eq!(tx_buf, reply_buf[0..3]);
|
assert_eq!(tx_buf, reply_buf[0..3]);
|
||||||
rprintln!(
|
rprintln!(
|
||||||
"Received reply: {}, {}, {}",
|
"Received reply: {}, {}, {}",
|
||||||
reply_buf[0],
|
reply_buf[0],
|
||||||
reply_buf[1],
|
reply_buf[1],
|
||||||
reply_buf[2]
|
reply_buf[2]
|
||||||
);
|
);
|
||||||
delay.delay_ms(500_u32);
|
delay.delay_ms(500_u32);
|
||||||
|
|
||||||
let mut tx_rx_buf: [u8; 3] = [0x03, 0x02, 0x01];
|
let mut tx_rx_buf: [u8; 3] = [0x03, 0x02, 0x01];
|
||||||
spi.transfer_in_place(&mut tx_rx_buf).unwrap();
|
spi.transfer_in_place(&mut tx_rx_buf).unwrap();
|
||||||
rprintln!(
|
rprintln!(
|
||||||
"Received reply: {}, {}, {}",
|
"Received reply: {}, {}, {}",
|
||||||
tx_rx_buf[0],
|
tx_rx_buf[0],
|
||||||
tx_rx_buf[1],
|
tx_rx_buf[1],
|
||||||
tx_rx_buf[2]
|
tx_rx_buf[2]
|
||||||
);
|
);
|
||||||
assert_eq!(&tx_rx_buf[0..3], &[0x03, 0x02, 0x01]);
|
assert_eq!(&tx_rx_buf[0..3], &[0x03, 0x02, 0x01]);
|
||||||
} else {
|
|
||||||
let send_buf: [u8; 3] = [0x01, 0x02, 0x03];
|
|
||||||
spi.transfer(&mut reply_buf[0..3], &send_buf).unwrap();
|
|
||||||
rprintln!(
|
|
||||||
"Received reply: {}, {}, {}",
|
|
||||||
reply_buf[0],
|
|
||||||
reply_buf[1],
|
|
||||||
reply_buf[2]
|
|
||||||
);
|
|
||||||
delay.delay_ms(1000_u32);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
SpiBusSelect::SpiBPortB => {
|
SpiBusSelect::SpiBPortB => {
|
||||||
if let Some(ref mut spi) = *spib_ref.borrow_mut() {
|
if let Some(ref mut spi) = *spib_ref.borrow_mut() {
|
||||||
if EXAMPLE_SEL == ExampleSelect::Loopback {
|
// Can't really verify correct reply here.
|
||||||
// Can't really verify correct reply here.
|
spi.write(&[0x42]).expect("write failed");
|
||||||
spi.write(&[0x42]).expect("write failed");
|
// Because of the loopback mode, we should get back the fill word here.
|
||||||
// Need small delay.. otherwise we will read back the sent byte (which we don't want here).
|
spi.read(&mut reply_buf[0..1]).unwrap();
|
||||||
// The write function will return as soon as all bytes were shifted out, ignoring the
|
assert_eq!(reply_buf[0], FILL_WORD);
|
||||||
// reply bytes.
|
delay.delay_ms(500_u32);
|
||||||
delay.delay_us(50);
|
|
||||||
// Because of the loopback mode, we should get back the fill word here.
|
|
||||||
spi.read(&mut reply_buf[0..1]).unwrap();
|
|
||||||
assert_eq!(reply_buf[0], FILL_WORD);
|
|
||||||
delay.delay_ms(500_u32);
|
|
||||||
|
|
||||||
let tx_buf: [u8; 3] = [0x01, 0x02, 0x03];
|
let tx_buf: [u8; 3] = [0x01, 0x02, 0x03];
|
||||||
spi.transfer(&mut reply_buf[0..3], &tx_buf).unwrap();
|
spi.transfer(&mut reply_buf[0..3], &tx_buf).unwrap();
|
||||||
assert_eq!(tx_buf, reply_buf[0..3]);
|
assert_eq!(tx_buf, reply_buf[0..3]);
|
||||||
rprintln!(
|
rprintln!(
|
||||||
"Received reply: {}, {}, {}",
|
"Received reply: {}, {}, {}",
|
||||||
reply_buf[0],
|
reply_buf[0],
|
||||||
reply_buf[1],
|
reply_buf[1],
|
||||||
reply_buf[2]
|
reply_buf[2]
|
||||||
);
|
);
|
||||||
delay.delay_ms(500_u32);
|
delay.delay_ms(500_u32);
|
||||||
|
|
||||||
let mut tx_rx_buf: [u8; 3] = [0x03, 0x02, 0x01];
|
let mut tx_rx_buf: [u8; 3] = [0x03, 0x02, 0x01];
|
||||||
spi.transfer_in_place(&mut tx_rx_buf).unwrap();
|
spi.transfer_in_place(&mut tx_rx_buf).unwrap();
|
||||||
rprintln!(
|
rprintln!(
|
||||||
"Received reply: {}, {}, {}",
|
"Received reply: {}, {}, {}",
|
||||||
tx_rx_buf[0],
|
tx_rx_buf[0],
|
||||||
tx_rx_buf[1],
|
tx_rx_buf[1],
|
||||||
tx_rx_buf[2]
|
tx_rx_buf[2]
|
||||||
);
|
);
|
||||||
assert_eq!(&tx_rx_buf[0..3], &[0x03, 0x02, 0x01]);
|
assert_eq!(&tx_rx_buf[0..3], &[0x03, 0x02, 0x01]);
|
||||||
} else {
|
|
||||||
let send_buf: [u8; 3] = [0x01, 0x02, 0x03];
|
|
||||||
spi.transfer(&mut reply_buf[0..3], &send_buf).unwrap();
|
|
||||||
rprintln!(
|
|
||||||
"Received reply: {}, {}, {}",
|
|
||||||
reply_buf[0],
|
|
||||||
reply_buf[1],
|
|
||||||
reply_buf[2]
|
|
||||||
);
|
|
||||||
delay.delay_ms(1000_u32);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -3,8 +3,8 @@
|
|||||||
#![no_std]
|
#![no_std]
|
||||||
|
|
||||||
use core::cell::Cell;
|
use core::cell::Cell;
|
||||||
use cortex_m::interrupt::Mutex;
|
|
||||||
use cortex_m_rt::entry;
|
use cortex_m_rt::entry;
|
||||||
|
use critical_section::Mutex;
|
||||||
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::{
|
||||||
@ -12,7 +12,9 @@ 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, Event, IrqCfg, MS_COUNTER},
|
timer::{
|
||||||
|
default_ms_irq_handler, set_up_ms_tick, CountdownTimer, Event, InterruptConfig, MS_COUNTER,
|
||||||
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
#[allow(dead_code)]
|
#[allow(dead_code)]
|
||||||
@ -65,17 +67,17 @@ fn main() -> ! {
|
|||||||
}
|
}
|
||||||
LibType::Hal => {
|
LibType::Hal => {
|
||||||
set_up_ms_tick(
|
set_up_ms_tick(
|
||||||
IrqCfg::new(interrupt::OC0, true, true),
|
InterruptConfig::new(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,
|
||||||
);
|
);
|
||||||
let mut second_timer =
|
let mut second_timer =
|
||||||
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,
|
||||||
IrqCfg::new(interrupt::OC1, true, true),
|
InterruptConfig::new(interrupt::OC1, true, true),
|
||||||
Some(&mut dp.irqsel),
|
Some(&mut dp.irqsel),
|
||||||
Some(&mut dp.sysconfig),
|
Some(&mut dp.sysconfig),
|
||||||
);
|
);
|
||||||
@ -83,11 +85,12 @@ fn main() -> ! {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
loop {
|
loop {
|
||||||
let current_ms = cortex_m::interrupt::free(|cs| MS_COUNTER.borrow(cs).get());
|
let current_ms = critical_section::with(|cs| MS_COUNTER.borrow(cs).get());
|
||||||
if current_ms - last_ms >= 1000 {
|
if current_ms - last_ms >= 1000 {
|
||||||
last_ms = current_ms;
|
// To prevent drift.
|
||||||
|
last_ms += 1000;
|
||||||
rprintln!("MS counter: {}", current_ms);
|
rprintln!("MS counter: {}", current_ms);
|
||||||
let second = cortex_m::interrupt::free(|cs| SEC_COUNTER.borrow(cs).get());
|
let second = critical_section::with(|cs| SEC_COUNTER.borrow(cs).get());
|
||||||
rprintln!("Second counter: {}", second);
|
rprintln!("Second counter: {}", second);
|
||||||
}
|
}
|
||||||
cortex_m::asm::delay(10000);
|
cortex_m::asm::delay(10000);
|
||||||
@ -110,7 +113,7 @@ fn OC0() {
|
|||||||
#[interrupt]
|
#[interrupt]
|
||||||
#[allow(non_snake_case)]
|
#[allow(non_snake_case)]
|
||||||
fn OC1() {
|
fn OC1() {
|
||||||
cortex_m::interrupt::free(|cs| {
|
critical_section::with(|cs| {
|
||||||
let mut sec = SEC_COUNTER.borrow(cs).get();
|
let mut sec = SEC_COUNTER.borrow(cs).get();
|
||||||
sec += 1;
|
sec += 1;
|
||||||
SEC_COUNTER.borrow(cs).set(sec);
|
SEC_COUNTER.borrow(cs).set(sec);
|
||||||
|
@ -1,172 +0,0 @@
|
|||||||
//! More complex UART application
|
|
||||||
//!
|
|
||||||
//! Uses the IRQ capabilities of the VA10820 peripheral and the RTIC framework to poll the UART in
|
|
||||||
//! a non-blocking way. You can send variably sized strings to the VA10820 which will be echoed
|
|
||||||
//! back to the sender.
|
|
||||||
//!
|
|
||||||
//! This script was tested with an Arduino Due. You can find the test script in the
|
|
||||||
//! [`/test/DueSerialTest`](https://egit.irs.uni-stuttgart.de/rust/va108xx-hal/src/branch/main/test/DueSerialTest)
|
|
||||||
//! folder.
|
|
||||||
#![no_main]
|
|
||||||
#![no_std]
|
|
||||||
|
|
||||||
#[rtic::app(device = pac, dispatchers = [OC4])]
|
|
||||||
mod app {
|
|
||||||
use embedded_io::Write;
|
|
||||||
use panic_rtt_target as _;
|
|
||||||
use rtic_monotonics::systick::Systick;
|
|
||||||
use rtic_sync::make_channel;
|
|
||||||
use rtt_target::{rprintln, rtt_init_print};
|
|
||||||
use va108xx_hal::{
|
|
||||||
gpio::PinsB,
|
|
||||||
pac,
|
|
||||||
prelude::*,
|
|
||||||
time::Hertz,
|
|
||||||
uart::{self, IrqCfg, IrqResult, UartWithIrqBase},
|
|
||||||
};
|
|
||||||
|
|
||||||
#[local]
|
|
||||||
struct Local {
|
|
||||||
rx_info_tx: rtic_sync::channel::Sender<'static, RxInfo, 3>,
|
|
||||||
rx_info_rx: rtic_sync::channel::Receiver<'static, RxInfo, 3>,
|
|
||||||
}
|
|
||||||
|
|
||||||
#[shared]
|
|
||||||
struct Shared {
|
|
||||||
irq_uart: UartWithIrqBase<pac::Uartb>,
|
|
||||||
rx_buf: [u8; 64],
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Debug, Copy, Clone)]
|
|
||||||
struct RxInfo {
|
|
||||||
pub bytes_read: usize,
|
|
||||||
pub end_idx: usize,
|
|
||||||
pub timeout: bool,
|
|
||||||
}
|
|
||||||
|
|
||||||
#[init]
|
|
||||||
fn init(cx: init::Context) -> (Shared, Local) {
|
|
||||||
rtt_init_print!();
|
|
||||||
//set_print_channel(channels.up.0);
|
|
||||||
rprintln!("-- VA108xx UART IRQ example application--");
|
|
||||||
|
|
||||||
// Initialize the systick interrupt & obtain the token to prove that we did
|
|
||||||
let systick_mono_token = rtic_monotonics::create_systick_token!();
|
|
||||||
Systick::start(
|
|
||||||
cx.core.SYST,
|
|
||||||
Hertz::from(50.MHz()).raw(),
|
|
||||||
systick_mono_token,
|
|
||||||
);
|
|
||||||
|
|
||||||
let mut dp = cx.device;
|
|
||||||
let gpiob = PinsB::new(&mut dp.sysconfig, Some(dp.ioconfig), dp.portb);
|
|
||||||
let tx = gpiob.pb21.into_funsel_1();
|
|
||||||
let rx = gpiob.pb20.into_funsel_1();
|
|
||||||
|
|
||||||
let irq_cfg = IrqCfg::new(pac::interrupt::OC3, true, true);
|
|
||||||
let (mut irq_uart, _) =
|
|
||||||
uart::Uart::new(&mut dp.sysconfig, 50.MHz(), dp.uartb, (tx, rx), 115200.Hz())
|
|
||||||
.into_uart_with_irq(irq_cfg, Some(&mut dp.sysconfig), Some(&mut dp.irqsel))
|
|
||||||
.downgrade();
|
|
||||||
irq_uart
|
|
||||||
.read_fixed_len_using_irq(64, true)
|
|
||||||
.expect("Read initialization failed");
|
|
||||||
|
|
||||||
let (rx_info_tx, rx_info_rx) = make_channel!(RxInfo, 3);
|
|
||||||
let rx_buf: [u8; 64] = [0; 64];
|
|
||||||
//reply_handler::spawn().expect("spawning reply handler failed");
|
|
||||||
(
|
|
||||||
Shared { irq_uart, rx_buf },
|
|
||||||
Local {
|
|
||||||
rx_info_tx,
|
|
||||||
rx_info_rx,
|
|
||||||
},
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
// `shared` cannot be accessed from this context
|
|
||||||
#[idle]
|
|
||||||
fn idle(_cx: idle::Context) -> ! {
|
|
||||||
loop {
|
|
||||||
cortex_m::asm::nop();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[task(
|
|
||||||
binds = OC3,
|
|
||||||
shared = [irq_uart, rx_buf],
|
|
||||||
local = [cnt: u32 = 0, result: IrqResult = IrqResult::new(), rx_info_tx],
|
|
||||||
)]
|
|
||||||
fn reception_task(cx: reception_task::Context) {
|
|
||||||
let result = cx.local.result;
|
|
||||||
let cnt: &mut u32 = cx.local.cnt;
|
|
||||||
let irq_uart = cx.shared.irq_uart;
|
|
||||||
let rx_buf = cx.shared.rx_buf;
|
|
||||||
let (completed, end_idx) = (irq_uart, rx_buf).lock(|irq_uart, rx_buf| {
|
|
||||||
match irq_uart.irq_handler(result, rx_buf) {
|
|
||||||
Ok(_) => {
|
|
||||||
if result.complete() {
|
|
||||||
// Initiate next transfer immediately
|
|
||||||
irq_uart
|
|
||||||
.read_fixed_len_using_irq(64, true)
|
|
||||||
.expect("Read operation init failed");
|
|
||||||
|
|
||||||
let mut end_idx = 0;
|
|
||||||
for idx in 0..rx_buf.len() {
|
|
||||||
if (rx_buf[idx] as char) == '\n' {
|
|
||||||
end_idx = idx;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
(true, end_idx)
|
|
||||||
} else {
|
|
||||||
(false, 0)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
Err(e) => {
|
|
||||||
rprintln!("reception error {:?}", e);
|
|
||||||
(false, 0)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
});
|
|
||||||
if completed {
|
|
||||||
rprintln!("counter: {}", cnt);
|
|
||||||
cx.local
|
|
||||||
.rx_info_tx
|
|
||||||
.try_send(RxInfo {
|
|
||||||
bytes_read: result.bytes_read,
|
|
||||||
end_idx,
|
|
||||||
timeout: result.timeout(),
|
|
||||||
})
|
|
||||||
.expect("RX queue full");
|
|
||||||
}
|
|
||||||
*cnt += 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
#[task(shared = [irq_uart, rx_buf], local = [rx_info_rx], priority=1)]
|
|
||||||
async fn reply_handler(cx: reply_handler::Context) {
|
|
||||||
let mut irq_uart = cx.shared.irq_uart;
|
|
||||||
let mut rx_buf = cx.shared.rx_buf;
|
|
||||||
loop {
|
|
||||||
match cx.local.rx_info_rx.recv().await {
|
|
||||||
Ok(rx_info) => {
|
|
||||||
rprintln!("reception success, {} bytes read", rx_info.bytes_read);
|
|
||||||
if rx_info.timeout {
|
|
||||||
rprintln!("timeout occurred");
|
|
||||||
}
|
|
||||||
rx_buf.lock(|rx_buf| {
|
|
||||||
let string = core::str::from_utf8(&rx_buf[0..rx_info.end_idx])
|
|
||||||
.expect("Invalid string format");
|
|
||||||
rprintln!("read string: {}", string);
|
|
||||||
irq_uart.lock(|uart| {
|
|
||||||
writeln!(uart.uart, "{}", string).expect("Sending reply failed");
|
|
||||||
});
|
|
||||||
});
|
|
||||||
}
|
|
||||||
Err(e) => {
|
|
||||||
rprintln!("error receiving RX info: {:?}", e);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
@ -24,12 +24,18 @@ fn main() -> ! {
|
|||||||
|
|
||||||
let mut dp = pac::Peripherals::take().unwrap();
|
let mut dp = pac::Peripherals::take().unwrap();
|
||||||
|
|
||||||
let gpioa = PinsA::new(&mut dp.sysconfig, Some(dp.ioconfig), dp.porta);
|
let gpioa = PinsA::new(&mut dp.sysconfig, 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 uarta = uart::Uart::new(&mut dp.sysconfig, 50.MHz(), dp.uarta, (tx, rx), 115200.Hz());
|
let (mut tx, mut rx) = uart.split();
|
||||||
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.
|
||||||
|
1
flashloader/.gitignore
vendored
Normal file
1
flashloader/.gitignore
vendored
Normal file
@ -0,0 +1 @@
|
|||||||
|
/venv
|
35
flashloader/Cargo.toml
Normal file
35
flashloader/Cargo.toml
Normal file
@ -0,0 +1,35 @@
|
|||||||
|
[package]
|
||||||
|
name = "flashloader"
|
||||||
|
version = "0.1.0"
|
||||||
|
edition = "2021"
|
||||||
|
|
||||||
|
[dependencies]
|
||||||
|
cortex-m = "0.7"
|
||||||
|
cortex-m-rt = "0.7"
|
||||||
|
embedded-hal = "1"
|
||||||
|
embedded-hal-nb = "1"
|
||||||
|
embedded-io = "0.6"
|
||||||
|
panic-rtt-target = "0.2"
|
||||||
|
rtt-target = "0.6"
|
||||||
|
num_enum = { version = "0.7", default-features = false }
|
||||||
|
log = "0.4"
|
||||||
|
crc = "3"
|
||||||
|
cobs = { version = "0.3", default-features = false }
|
||||||
|
satrs = { version = "0.2", default-features = false }
|
||||||
|
rtt-log = "0.5"
|
||||||
|
ringbuf = { version = "0.4.7", default-features = false, features = ["portable-atomic"] }
|
||||||
|
once_cell = { version = "1", default-features = false, features = ["critical-section"] }
|
||||||
|
spacepackets = { version = "0.11", default-features = false }
|
||||||
|
# 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.
|
||||||
|
portable-atomic = {version = "1", features = ["unsafe-assume-single-core"]}
|
||||||
|
|
||||||
|
rtic = { version = "2", features = ["thumbv6-backend"] }
|
||||||
|
rtic-monotonics = { version = "2", features = ["cortex-m-systick"] }
|
||||||
|
rtic-sync = {version = "1", features = ["defmt-03"]}
|
||||||
|
|
||||||
|
[dependencies.va108xx-hal]
|
||||||
|
path = "../va108xx-hal"
|
||||||
|
|
||||||
|
[dependencies.vorago-reb1]
|
||||||
|
path = "../vorago-reb1"
|
75
flashloader/README.md
Normal file
75
flashloader/README.md
Normal file
@ -0,0 +1,75 @@
|
|||||||
|
VA108xx Flashloader Application
|
||||||
|
========
|
||||||
|
|
||||||
|
This flashloader shows a minimal example for a self-updatable Rust software which exposes
|
||||||
|
a simple PUS (CCSDS) interface to update the software. It also provides a Python application
|
||||||
|
called the `image-loader.py` which can be used to upload compiled images to the flashloader
|
||||||
|
application to write them to the NVM.
|
||||||
|
|
||||||
|
Please note that the both the application and the image loader are tailored towards usage
|
||||||
|
with the [bootloader provided by this repository](https://egit.irs.uni-stuttgart.de/rust/va108xx-rs/src/branch/main/bootloader).
|
||||||
|
|
||||||
|
The software can quickly be adapted to interface with a real primary on-board software instead of
|
||||||
|
the Python script provided here to upload images because it uses a low-level CCSDS based packet
|
||||||
|
interface.
|
||||||
|
|
||||||
|
## Using the Python image loader
|
||||||
|
|
||||||
|
The Python image loader communicates with the Rust flashload application using a dedicated serial
|
||||||
|
port with a baudrate of 115200.
|
||||||
|
|
||||||
|
It is recommended to run the script in a dedicated virtual environment. For example, on UNIX
|
||||||
|
systems you can use `python3 -m venv venv` and then `source venv/bin/activate` to create
|
||||||
|
and activate a virtual environment.
|
||||||
|
|
||||||
|
After that, you can use
|
||||||
|
|
||||||
|
```sh
|
||||||
|
pip install -r requirements.txt
|
||||||
|
```
|
||||||
|
|
||||||
|
to install all required dependencies.
|
||||||
|
|
||||||
|
After that, it is recommended to use `./image-load.py -h` to get an overview of some options.
|
||||||
|
The flash loader uses the UART0 with the Pins PA8 (RX) and PA9 (TX) interface of the VA108xx to perform CCSDS based
|
||||||
|
communication. The Python image loader application will search for a file named `loader.toml` and
|
||||||
|
use the `serial_port` key to determine the serial port to use for serial communication.
|
||||||
|
|
||||||
|
### Examples
|
||||||
|
|
||||||
|
You can use
|
||||||
|
|
||||||
|
```sh
|
||||||
|
./image-loader.py -p
|
||||||
|
```
|
||||||
|
|
||||||
|
to send a ping an verify the connection.
|
||||||
|
|
||||||
|
You can use
|
||||||
|
|
||||||
|
```sh
|
||||||
|
cd flashloader/slot-a-blinky
|
||||||
|
cargo build --release
|
||||||
|
cd ../..
|
||||||
|
./image-loader.py -t a ./slot-a-blinky/target/thumbv6m-none-eabi/release/slot-a-blinky
|
||||||
|
```
|
||||||
|
|
||||||
|
to build the slot A sample application and upload it to a running flash loader application
|
||||||
|
to write it to slot A.
|
||||||
|
|
||||||
|
You can use
|
||||||
|
|
||||||
|
```sh
|
||||||
|
./image-loader.py -s a
|
||||||
|
```
|
||||||
|
|
||||||
|
to select the Slot A as a boot slot. The boot slot is stored in a reserved section in EEPROM
|
||||||
|
and will be read and used by the bootloader to determine which slot to boot.
|
||||||
|
|
||||||
|
You can use
|
||||||
|
|
||||||
|
```sh
|
||||||
|
./image-loader.py -c -t a
|
||||||
|
```
|
||||||
|
|
||||||
|
to corrupt the image A and test that it switches to image B after a failed CRC check instead.
|
475
flashloader/image-loader.py
Executable file
475
flashloader/image-loader.py
Executable file
@ -0,0 +1,475 @@
|
|||||||
|
#!/usr/bin/env python3
|
||||||
|
from typing import List, Tuple
|
||||||
|
from spacepackets.ecss.defs import PusService
|
||||||
|
from spacepackets.ecss.tm import PusTm
|
||||||
|
from tmtccmd.com import ComInterface
|
||||||
|
import toml
|
||||||
|
import struct
|
||||||
|
import logging
|
||||||
|
import argparse
|
||||||
|
import time
|
||||||
|
import enum
|
||||||
|
from tmtccmd.com.serial_base import SerialCfg
|
||||||
|
from tmtccmd.com.serial_cobs import SerialCobsComIF
|
||||||
|
from tmtccmd.com.ser_utils import prompt_com_port
|
||||||
|
from crcmod.predefined import PredefinedCrc
|
||||||
|
from spacepackets.ecss.tc import PusTc
|
||||||
|
from spacepackets.ecss.pus_verificator import PusVerificator, StatusField
|
||||||
|
from spacepackets.ecss.pus_1_verification import Service1Tm, UnpackParams
|
||||||
|
from spacepackets.seqcount import SeqCountProvider
|
||||||
|
from pathlib import Path
|
||||||
|
import dataclasses
|
||||||
|
from elftools.elf.elffile import ELFFile
|
||||||
|
|
||||||
|
|
||||||
|
BAUD_RATE = 115200
|
||||||
|
|
||||||
|
BOOTLOADER_START_ADDR = 0x0
|
||||||
|
BOOTLOADER_END_ADDR = 0x3000
|
||||||
|
BOOTLOADER_CRC_ADDR = BOOTLOADER_END_ADDR - 2
|
||||||
|
BOOTLOADER_MAX_SIZE = BOOTLOADER_END_ADDR - BOOTLOADER_START_ADDR - 2
|
||||||
|
|
||||||
|
APP_A_START_ADDR = 0x3000
|
||||||
|
APP_B_END_ADDR = 0x20000 - 8
|
||||||
|
IMG_SLOT_SIZE = (APP_B_END_ADDR - APP_A_START_ADDR) // 2
|
||||||
|
|
||||||
|
APP_A_END_ADDR = APP_A_START_ADDR + IMG_SLOT_SIZE
|
||||||
|
# The actual size of the image which is relevant for CRC calculation.
|
||||||
|
APP_A_SIZE_ADDR = APP_A_END_ADDR - 8
|
||||||
|
APP_A_CRC_ADDR = APP_A_END_ADDR - 4
|
||||||
|
APP_A_MAX_SIZE = APP_A_END_ADDR - APP_A_START_ADDR - 8
|
||||||
|
|
||||||
|
APP_B_START_ADDR = APP_A_END_ADDR
|
||||||
|
# The actual size of the image which is relevant for CRC calculation.
|
||||||
|
APP_B_SIZE_ADDR = APP_B_END_ADDR - 8
|
||||||
|
APP_B_CRC_ADDR = APP_B_END_ADDR - 4
|
||||||
|
APP_B_MAX_SIZE = APP_A_END_ADDR - APP_A_START_ADDR - 8
|
||||||
|
|
||||||
|
|
||||||
|
CHUNK_SIZE = 400
|
||||||
|
|
||||||
|
MEMORY_SERVICE = 6
|
||||||
|
ACTION_SERVICE = 8
|
||||||
|
|
||||||
|
RAW_MEMORY_WRITE_SUBSERVICE = 2
|
||||||
|
BOOT_NVM_MEMORY_ID = 1
|
||||||
|
PING_PAYLOAD_SIZE = 0
|
||||||
|
|
||||||
|
|
||||||
|
class ActionId(enum.IntEnum):
|
||||||
|
CORRUPT_APP_A = 128
|
||||||
|
CORRUPT_APP_B = 129
|
||||||
|
SET_BOOT_SLOT = 130
|
||||||
|
|
||||||
|
|
||||||
|
_LOGGER = logging.getLogger(__name__)
|
||||||
|
SEQ_PROVIDER = SeqCountProvider(bit_width=14)
|
||||||
|
|
||||||
|
|
||||||
|
@dataclasses.dataclass
|
||||||
|
class LoadableSegment:
|
||||||
|
name: str
|
||||||
|
offset: int
|
||||||
|
size: int
|
||||||
|
data: bytes
|
||||||
|
|
||||||
|
|
||||||
|
class Target(enum.Enum):
|
||||||
|
BOOTLOADER = 0
|
||||||
|
APP_A = 1
|
||||||
|
APP_B = 2
|
||||||
|
|
||||||
|
|
||||||
|
class AppSel(enum.IntEnum):
|
||||||
|
APP_A = 0
|
||||||
|
APP_B = 1
|
||||||
|
|
||||||
|
|
||||||
|
class ImageLoader:
|
||||||
|
def __init__(self, com_if: ComInterface, verificator: PusVerificator) -> None:
|
||||||
|
self.com_if = com_if
|
||||||
|
self.verificator = verificator
|
||||||
|
|
||||||
|
def handle_boot_sel_cmd(self, target: AppSel):
|
||||||
|
_LOGGER.info("Sending ping command")
|
||||||
|
action_tc = PusTc(
|
||||||
|
apid=0x00,
|
||||||
|
service=PusService.S8_FUNC_CMD,
|
||||||
|
subservice=ActionId.SET_BOOT_SLOT,
|
||||||
|
seq_count=SEQ_PROVIDER.get_and_increment(),
|
||||||
|
app_data=bytes([target]),
|
||||||
|
)
|
||||||
|
self.verificator.add_tc(action_tc)
|
||||||
|
self.com_if.send(bytes(action_tc.pack()))
|
||||||
|
data_available = self.com_if.data_available(0.4)
|
||||||
|
if not data_available:
|
||||||
|
_LOGGER.warning("no reply received for boot image selection command")
|
||||||
|
for reply in self.com_if.receive():
|
||||||
|
result = self.verificator.add_tm(
|
||||||
|
Service1Tm.from_tm(PusTm.unpack(reply, 0), UnpackParams(0))
|
||||||
|
)
|
||||||
|
if result is not None and result.completed:
|
||||||
|
_LOGGER.info("received boot image selection command confirmation")
|
||||||
|
|
||||||
|
def handle_ping_cmd(self):
|
||||||
|
_LOGGER.info("Sending ping command")
|
||||||
|
ping_tc = PusTc(
|
||||||
|
apid=0x00,
|
||||||
|
service=PusService.S17_TEST,
|
||||||
|
subservice=1,
|
||||||
|
seq_count=SEQ_PROVIDER.get_and_increment(),
|
||||||
|
app_data=bytes(PING_PAYLOAD_SIZE),
|
||||||
|
)
|
||||||
|
self.verificator.add_tc(ping_tc)
|
||||||
|
self.com_if.send(bytes(ping_tc.pack()))
|
||||||
|
|
||||||
|
data_available = self.com_if.data_available(0.4)
|
||||||
|
if not data_available:
|
||||||
|
_LOGGER.warning("no ping reply received")
|
||||||
|
for reply in self.com_if.receive():
|
||||||
|
result = self.verificator.add_tm(
|
||||||
|
Service1Tm.from_tm(PusTm.unpack(reply, 0), UnpackParams(0))
|
||||||
|
)
|
||||||
|
if result is not None and result.completed:
|
||||||
|
_LOGGER.info("received ping completion reply")
|
||||||
|
|
||||||
|
def handle_corruption_cmd(self, target: Target):
|
||||||
|
if target == Target.BOOTLOADER:
|
||||||
|
_LOGGER.error("can not corrupt bootloader")
|
||||||
|
if target == Target.APP_A:
|
||||||
|
self.send_tc(
|
||||||
|
PusTc(
|
||||||
|
apid=0,
|
||||||
|
service=ACTION_SERVICE,
|
||||||
|
subservice=ActionId.CORRUPT_APP_A,
|
||||||
|
),
|
||||||
|
)
|
||||||
|
if target == Target.APP_B:
|
||||||
|
self.send_tc(
|
||||||
|
PusTc(
|
||||||
|
apid=0,
|
||||||
|
service=ACTION_SERVICE,
|
||||||
|
subservice=ActionId.CORRUPT_APP_B,
|
||||||
|
),
|
||||||
|
)
|
||||||
|
|
||||||
|
def handle_flash_cmd(self, target: Target, file_path: Path) -> int:
|
||||||
|
loadable_segments = []
|
||||||
|
_LOGGER.info("Parsing ELF file for loadable sections")
|
||||||
|
total_size = 0
|
||||||
|
loadable_segments, total_size = create_loadable_segments(target, file_path)
|
||||||
|
check_segments(target, total_size)
|
||||||
|
print_segments_info(target, loadable_segments, total_size, file_path)
|
||||||
|
result = self._perform_flashing_algorithm(loadable_segments)
|
||||||
|
if result != 0:
|
||||||
|
return result
|
||||||
|
self._crc_and_app_size_postprocessing(target, total_size, loadable_segments)
|
||||||
|
return 0
|
||||||
|
|
||||||
|
def _perform_flashing_algorithm(
|
||||||
|
self,
|
||||||
|
loadable_segments: List[LoadableSegment],
|
||||||
|
) -> int:
|
||||||
|
# Perform the flashing algorithm.
|
||||||
|
for segment in loadable_segments:
|
||||||
|
segment_end = segment.offset + segment.size
|
||||||
|
current_addr = segment.offset
|
||||||
|
pos_in_segment = 0
|
||||||
|
while pos_in_segment < segment.size:
|
||||||
|
next_chunk_size = min(segment_end - current_addr, CHUNK_SIZE)
|
||||||
|
data = segment.data[pos_in_segment : pos_in_segment + next_chunk_size]
|
||||||
|
next_packet = pack_memory_write_command(current_addr, data)
|
||||||
|
_LOGGER.info(
|
||||||
|
f"Sending memory write command for address {current_addr:#08x} and data with "
|
||||||
|
f"length {len(data)}"
|
||||||
|
)
|
||||||
|
self.verificator.add_tc(next_packet)
|
||||||
|
self.com_if.send(bytes(next_packet.pack()))
|
||||||
|
current_addr += next_chunk_size
|
||||||
|
pos_in_segment += next_chunk_size
|
||||||
|
start_time = time.time()
|
||||||
|
while True:
|
||||||
|
if time.time() - start_time > 1.0:
|
||||||
|
_LOGGER.error("Timeout while waiting for reply")
|
||||||
|
return -1
|
||||||
|
data_available = self.com_if.data_available(0.1)
|
||||||
|
done = False
|
||||||
|
if not data_available:
|
||||||
|
continue
|
||||||
|
replies = self.com_if.receive()
|
||||||
|
for reply in replies:
|
||||||
|
tm = PusTm.unpack(reply, 0)
|
||||||
|
if tm.service != 1:
|
||||||
|
continue
|
||||||
|
service_1_tm = Service1Tm.from_tm(tm, UnpackParams(0))
|
||||||
|
check_result = self.verificator.add_tm(service_1_tm)
|
||||||
|
# We could send after we have received the step reply, but that can
|
||||||
|
# somehow lead to overrun errors. I think it's okay to do it like
|
||||||
|
# this as long as the flash loader only uses polling..
|
||||||
|
if (
|
||||||
|
check_result is not None
|
||||||
|
and check_result.status.completed == StatusField.SUCCESS
|
||||||
|
):
|
||||||
|
done = True
|
||||||
|
|
||||||
|
# This is an optimized variant, but I think the small delay is not an issue.
|
||||||
|
"""
|
||||||
|
if (
|
||||||
|
check_result is not None
|
||||||
|
and check_result.status.step == StatusField.SUCCESS
|
||||||
|
and len(check_result.status.step_list) == 1
|
||||||
|
):
|
||||||
|
done = True
|
||||||
|
"""
|
||||||
|
self.verificator.remove_completed_entries()
|
||||||
|
if done:
|
||||||
|
break
|
||||||
|
return 0
|
||||||
|
|
||||||
|
def _crc_and_app_size_postprocessing(
|
||||||
|
self,
|
||||||
|
target: Target,
|
||||||
|
total_size: int,
|
||||||
|
loadable_segments: List[LoadableSegment],
|
||||||
|
):
|
||||||
|
if target == Target.BOOTLOADER:
|
||||||
|
_LOGGER.info("Blanking the bootloader checksum")
|
||||||
|
# Blank the checksum. For the bootloader, the bootloader will calculate the
|
||||||
|
# checksum itself on the initial run.
|
||||||
|
checksum_write_packet = pack_memory_write_command(
|
||||||
|
BOOTLOADER_CRC_ADDR, bytes([0x00, 0x00])
|
||||||
|
)
|
||||||
|
self.send_tc(checksum_write_packet)
|
||||||
|
else:
|
||||||
|
crc_addr = None
|
||||||
|
size_addr = None
|
||||||
|
if target == Target.APP_A:
|
||||||
|
crc_addr = APP_A_CRC_ADDR
|
||||||
|
size_addr = APP_A_SIZE_ADDR
|
||||||
|
elif target == Target.APP_B:
|
||||||
|
crc_addr = APP_B_CRC_ADDR
|
||||||
|
size_addr = APP_B_SIZE_ADDR
|
||||||
|
assert crc_addr is not None
|
||||||
|
assert size_addr is not None
|
||||||
|
_LOGGER.info(f"Writing app size {total_size} at address {size_addr:#08x}")
|
||||||
|
size_write_packet = pack_memory_write_command(
|
||||||
|
size_addr, struct.pack("!I", total_size)
|
||||||
|
)
|
||||||
|
self.com_if.send(bytes(size_write_packet.pack()))
|
||||||
|
time.sleep(0.2)
|
||||||
|
crc_calc = PredefinedCrc("crc-ccitt-false")
|
||||||
|
for segment in loadable_segments:
|
||||||
|
crc_calc.update(segment.data)
|
||||||
|
checksum = crc_calc.digest()
|
||||||
|
_LOGGER.info(
|
||||||
|
f"Writing checksum 0x[{checksum.hex(sep=',')}] at address {crc_addr:#08x}"
|
||||||
|
)
|
||||||
|
self.send_tc(pack_memory_write_command(crc_addr, checksum))
|
||||||
|
|
||||||
|
def send_tc(self, tc: PusTc):
|
||||||
|
self.com_if.send(bytes(tc.pack()))
|
||||||
|
|
||||||
|
|
||||||
|
def main() -> int:
|
||||||
|
print("Python VA108XX Image Loader Application")
|
||||||
|
logging.basicConfig(
|
||||||
|
format="[%(asctime)s] [%(levelname)s] %(message)s", level=logging.DEBUG
|
||||||
|
)
|
||||||
|
parser = argparse.ArgumentParser(
|
||||||
|
prog="image-loader", description="Python VA416XX Image Loader Application"
|
||||||
|
)
|
||||||
|
parser.add_argument("-p", "--ping", action="store_true", help="Send ping command")
|
||||||
|
parser.add_argument(
|
||||||
|
"-s", "--sel", choices=["a", "b"], help="Set boot slot (Slot A or B)"
|
||||||
|
)
|
||||||
|
parser.add_argument("-c", "--corrupt", action="store_true", help="Corrupt a target")
|
||||||
|
parser.add_argument(
|
||||||
|
"-t",
|
||||||
|
"--target",
|
||||||
|
choices=["bl", "a", "b"],
|
||||||
|
help="Target (Bootloader or slot A or B)",
|
||||||
|
)
|
||||||
|
parser.add_argument(
|
||||||
|
"path", nargs="?", default=None, help="Path to the App to flash"
|
||||||
|
)
|
||||||
|
args = parser.parse_args()
|
||||||
|
serial_port = None
|
||||||
|
if Path("loader.toml").exists():
|
||||||
|
with open("loader.toml", "r") as toml_file:
|
||||||
|
parsed_toml = toml.loads(toml_file.read())
|
||||||
|
if "serial_port" in parsed_toml:
|
||||||
|
serial_port = parsed_toml["serial_port"]
|
||||||
|
if serial_port is None:
|
||||||
|
serial_port = prompt_com_port()
|
||||||
|
serial_cfg = SerialCfg(
|
||||||
|
com_if_id="ser_cobs",
|
||||||
|
serial_port=serial_port,
|
||||||
|
baud_rate=BAUD_RATE,
|
||||||
|
serial_timeout=0.1,
|
||||||
|
)
|
||||||
|
verificator = PusVerificator()
|
||||||
|
com_if = SerialCobsComIF(serial_cfg)
|
||||||
|
com_if.open()
|
||||||
|
target = None
|
||||||
|
if args.target == "bl":
|
||||||
|
target = Target.BOOTLOADER
|
||||||
|
elif args.target == "a":
|
||||||
|
target = Target.APP_A
|
||||||
|
elif args.target == "b":
|
||||||
|
target = Target.APP_B
|
||||||
|
|
||||||
|
boot_sel = None
|
||||||
|
if args.sel:
|
||||||
|
if args.sel == "a":
|
||||||
|
boot_sel = AppSel.APP_A
|
||||||
|
elif args.sel == "b":
|
||||||
|
boot_sel = AppSel.APP_B
|
||||||
|
|
||||||
|
image_loader = ImageLoader(com_if, verificator)
|
||||||
|
file_path = None
|
||||||
|
result = -1
|
||||||
|
if args.ping:
|
||||||
|
image_loader.handle_ping_cmd()
|
||||||
|
com_if.close()
|
||||||
|
return 0
|
||||||
|
if args.sel and boot_sel is not None:
|
||||||
|
image_loader.handle_boot_sel_cmd(boot_sel)
|
||||||
|
if target:
|
||||||
|
if not args.corrupt:
|
||||||
|
if not args.path:
|
||||||
|
_LOGGER.error("App Path needs to be specified for the flash process")
|
||||||
|
file_path = Path(args.path)
|
||||||
|
if not file_path.exists():
|
||||||
|
_LOGGER.error("File does not exist")
|
||||||
|
if args.corrupt:
|
||||||
|
if not target:
|
||||||
|
_LOGGER.error("target for corruption command required")
|
||||||
|
com_if.close()
|
||||||
|
return -1
|
||||||
|
image_loader.handle_corruption_cmd(target)
|
||||||
|
else:
|
||||||
|
if file_path is not None:
|
||||||
|
assert target is not None
|
||||||
|
result = image_loader.handle_flash_cmd(target, file_path)
|
||||||
|
|
||||||
|
com_if.close()
|
||||||
|
return result
|
||||||
|
|
||||||
|
|
||||||
|
def create_loadable_segments(
|
||||||
|
target: Target, file_path: Path
|
||||||
|
) -> Tuple[List[LoadableSegment], int]:
|
||||||
|
loadable_segments = []
|
||||||
|
total_size = 0
|
||||||
|
with open(file_path, "rb") as app_file:
|
||||||
|
elf_file = ELFFile(app_file)
|
||||||
|
|
||||||
|
for idx, segment in enumerate(elf_file.iter_segments("PT_LOAD")):
|
||||||
|
if segment.header.p_filesz == 0:
|
||||||
|
continue
|
||||||
|
# Basic validity checks of the base addresses.
|
||||||
|
if idx == 0:
|
||||||
|
if (
|
||||||
|
target == Target.BOOTLOADER
|
||||||
|
and segment.header.p_paddr != BOOTLOADER_START_ADDR
|
||||||
|
):
|
||||||
|
raise ValueError(
|
||||||
|
f"detected possibly invalid start address {segment.header.p_paddr:#08x} for "
|
||||||
|
f"bootloader, expected {BOOTLOADER_START_ADDR}"
|
||||||
|
)
|
||||||
|
if (
|
||||||
|
target == Target.APP_A
|
||||||
|
and segment.header.p_paddr != APP_A_START_ADDR
|
||||||
|
):
|
||||||
|
raise ValueError(
|
||||||
|
f"detected possibly invalid start address {segment.header.p_paddr:#08x} for "
|
||||||
|
f"App A, expected {APP_A_START_ADDR}"
|
||||||
|
)
|
||||||
|
if (
|
||||||
|
target == Target.APP_B
|
||||||
|
and segment.header.p_paddr != APP_B_START_ADDR
|
||||||
|
):
|
||||||
|
raise ValueError(
|
||||||
|
f"detected possibly invalid start address {segment.header.p_paddr:#08x} for "
|
||||||
|
f"App B, expected {APP_B_START_ADDR}"
|
||||||
|
)
|
||||||
|
name = None
|
||||||
|
for section in elf_file.iter_sections():
|
||||||
|
if (
|
||||||
|
section.header.sh_offset == segment.header.p_offset
|
||||||
|
and section.header.sh_size > 0
|
||||||
|
):
|
||||||
|
name = section.name
|
||||||
|
if name is None:
|
||||||
|
_LOGGER.warning("no fitting section found for segment")
|
||||||
|
continue
|
||||||
|
# print(f"Segment Addr: {segment.header.p_paddr}")
|
||||||
|
# print(f"Segment Offset: {segment.header.p_offset}")
|
||||||
|
# print(f"Segment Filesize: {segment.header.p_filesz}")
|
||||||
|
loadable_segments.append(
|
||||||
|
LoadableSegment(
|
||||||
|
name=name,
|
||||||
|
offset=segment.header.p_paddr,
|
||||||
|
size=segment.header.p_filesz,
|
||||||
|
data=segment.data(),
|
||||||
|
)
|
||||||
|
)
|
||||||
|
total_size += segment.header.p_filesz
|
||||||
|
return loadable_segments, total_size
|
||||||
|
|
||||||
|
|
||||||
|
def check_segments(
|
||||||
|
target: Target,
|
||||||
|
total_size: int,
|
||||||
|
):
|
||||||
|
# Set context string and perform basic sanity checks.
|
||||||
|
if target == Target.BOOTLOADER and total_size > BOOTLOADER_MAX_SIZE:
|
||||||
|
raise ValueError(
|
||||||
|
f"provided bootloader app larger than allowed {total_size} bytes"
|
||||||
|
)
|
||||||
|
elif target == Target.APP_A and total_size > APP_A_MAX_SIZE:
|
||||||
|
raise ValueError(f"provided App A larger than allowed {total_size} bytes")
|
||||||
|
elif target == Target.APP_B and total_size > APP_B_MAX_SIZE:
|
||||||
|
raise ValueError(f"provided App B larger than allowed {total_size} bytes")
|
||||||
|
|
||||||
|
|
||||||
|
def print_segments_info(
|
||||||
|
target: Target,
|
||||||
|
loadable_segments: List[LoadableSegment],
|
||||||
|
total_size: int,
|
||||||
|
file_path: Path,
|
||||||
|
):
|
||||||
|
# Set context string and perform basic sanity checks.
|
||||||
|
if target == Target.BOOTLOADER:
|
||||||
|
context_str = "Bootloader"
|
||||||
|
elif target == Target.APP_A:
|
||||||
|
context_str = "App Slot A"
|
||||||
|
elif target == Target.APP_B:
|
||||||
|
context_str = "App Slot B"
|
||||||
|
_LOGGER.info(f"Flashing {context_str} with image {file_path} (size {total_size})")
|
||||||
|
for idx, segment in enumerate(loadable_segments):
|
||||||
|
_LOGGER.info(
|
||||||
|
f"Loadable section {idx} {segment.name} with offset {segment.offset:#08x} and "
|
||||||
|
f"size {segment.size}"
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
def pack_memory_write_command(addr: int, data: bytes) -> PusTc:
|
||||||
|
app_data = bytearray()
|
||||||
|
app_data.append(BOOT_NVM_MEMORY_ID)
|
||||||
|
# N parameter is always 1 here.
|
||||||
|
app_data.append(1)
|
||||||
|
app_data.extend(struct.pack("!I", addr))
|
||||||
|
app_data.extend(struct.pack("!I", len(data)))
|
||||||
|
app_data.extend(data)
|
||||||
|
return PusTc(
|
||||||
|
apid=0,
|
||||||
|
service=MEMORY_SERVICE,
|
||||||
|
subservice=RAW_MEMORY_WRITE_SUBSERVICE,
|
||||||
|
seq_count=SEQ_PROVIDER.get_and_increment(),
|
||||||
|
app_data=bytes(app_data),
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
main()
|
1
flashloader/loader.toml
Normal file
1
flashloader/loader.toml
Normal file
@ -0,0 +1 @@
|
|||||||
|
serial_port = "/dev/ttyUSB0"
|
5
flashloader/requirements.txt
Normal file
5
flashloader/requirements.txt
Normal file
@ -0,0 +1,5 @@
|
|||||||
|
spacepackets == 0.24
|
||||||
|
tmtccmd == 8.0.2
|
||||||
|
toml == 0.10
|
||||||
|
pyelftools == 0.31
|
||||||
|
crcmod == 1.7
|
2
flashloader/slot-a-blinky/.gitignore
vendored
Normal file
2
flashloader/slot-a-blinky/.gitignore
vendored
Normal file
@ -0,0 +1,2 @@
|
|||||||
|
/target
|
||||||
|
/app.map
|
42
flashloader/slot-a-blinky/Cargo.toml
Normal file
42
flashloader/slot-a-blinky/Cargo.toml
Normal file
@ -0,0 +1,42 @@
|
|||||||
|
[package]
|
||||||
|
name = "slot-a-blinky"
|
||||||
|
version = "0.1.0"
|
||||||
|
edition = "2021"
|
||||||
|
|
||||||
|
[workspace]
|
||||||
|
|
||||||
|
[dependencies]
|
||||||
|
cortex-m-rt = "0.7"
|
||||||
|
panic-rtt-target = { version = "0.1.3" }
|
||||||
|
rtt-target = { version = "0.5" }
|
||||||
|
cortex-m = { version = "0.7", features = ["critical-section-single-core"] }
|
||||||
|
embedded-hal = "1"
|
||||||
|
va108xx-hal = { path = "../../va108xx-hal" }
|
||||||
|
|
||||||
|
[profile.dev]
|
||||||
|
codegen-units = 1
|
||||||
|
debug = 2
|
||||||
|
debug-assertions = true # <-
|
||||||
|
incremental = false
|
||||||
|
# This is problematic for stepping..
|
||||||
|
# opt-level = 'z' # <-
|
||||||
|
overflow-checks = true # <-
|
||||||
|
|
||||||
|
# cargo build/run --release
|
||||||
|
[profile.release]
|
||||||
|
codegen-units = 1
|
||||||
|
debug = 2
|
||||||
|
debug-assertions = false # <-
|
||||||
|
incremental = false
|
||||||
|
lto = 'fat'
|
||||||
|
opt-level = 3 # <-
|
||||||
|
overflow-checks = false # <-
|
||||||
|
|
||||||
|
[profile.small]
|
||||||
|
inherits = "release"
|
||||||
|
codegen-units = 1
|
||||||
|
debug-assertions = false # <-
|
||||||
|
lto = true
|
||||||
|
opt-level = 'z' # <-
|
||||||
|
overflow-checks = false # <-
|
||||||
|
# strip = true # Automatically strip symbols from the binary.
|
11
flashloader/slot-a-blinky/memory.x
Normal file
11
flashloader/slot-a-blinky/memory.x
Normal file
@ -0,0 +1,11 @@
|
|||||||
|
/* Special linker script for application slot A with an offset at address 0x3000 */
|
||||||
|
MEMORY
|
||||||
|
{
|
||||||
|
FLASH : ORIGIN = 0x00003000, LENGTH = 0xE7FC
|
||||||
|
RAM : ORIGIN = 0x10000000, LENGTH = 0x08000 /* 32K */
|
||||||
|
}
|
||||||
|
|
||||||
|
/* This is where the call stack will be allocated. */
|
||||||
|
/* The stack is of the full descending type. */
|
||||||
|
/* NOTE Do NOT modify `_stack_start` unless you know what you are doing */
|
||||||
|
_stack_start = ORIGIN(RAM) + LENGTH(RAM);
|
25
flashloader/slot-a-blinky/src/main.rs
Normal file
25
flashloader/slot-a-blinky/src/main.rs
Normal file
@ -0,0 +1,25 @@
|
|||||||
|
//! Simple blinky example using the HAL
|
||||||
|
#![no_main]
|
||||||
|
#![no_std]
|
||||||
|
|
||||||
|
use cortex_m_rt::entry;
|
||||||
|
use embedded_hal::{delay::DelayNs, digital::StatefulOutputPin};
|
||||||
|
use panic_rtt_target as _;
|
||||||
|
use rtt_target::{rprintln, rtt_init_print};
|
||||||
|
use va108xx_hal::{gpio::PinsA, pac, prelude::*, timer::CountdownTimer};
|
||||||
|
|
||||||
|
#[entry]
|
||||||
|
fn main() -> ! {
|
||||||
|
rtt_init_print!();
|
||||||
|
rprintln!("VA108xx HAL blinky example for App Slot A");
|
||||||
|
|
||||||
|
let mut dp = pac::Peripherals::take().unwrap();
|
||||||
|
let mut timer = CountdownTimer::new(&mut dp.sysconfig, 50.MHz(), dp.tim0);
|
||||||
|
let porta = PinsA::new(&mut dp.sysconfig, Some(dp.ioconfig), dp.porta);
|
||||||
|
let mut led1 = porta.pa10.into_readable_push_pull_output();
|
||||||
|
|
||||||
|
loop {
|
||||||
|
led1.toggle().ok();
|
||||||
|
timer.delay_ms(500);
|
||||||
|
}
|
||||||
|
}
|
2
flashloader/slot-b-blinky/.gitignore
vendored
Normal file
2
flashloader/slot-b-blinky/.gitignore
vendored
Normal file
@ -0,0 +1,2 @@
|
|||||||
|
/target
|
||||||
|
/app.map
|
42
flashloader/slot-b-blinky/Cargo.toml
Normal file
42
flashloader/slot-b-blinky/Cargo.toml
Normal file
@ -0,0 +1,42 @@
|
|||||||
|
[package]
|
||||||
|
name = "slot-b-blinky"
|
||||||
|
version = "0.1.0"
|
||||||
|
edition = "2021"
|
||||||
|
|
||||||
|
[workspace]
|
||||||
|
|
||||||
|
[dependencies]
|
||||||
|
cortex-m-rt = "0.7"
|
||||||
|
panic-rtt-target = { version = "0.1.3" }
|
||||||
|
rtt-target = { version = "0.5" }
|
||||||
|
cortex-m = { version = "0.7", features = ["critical-section-single-core"] }
|
||||||
|
embedded-hal = "1"
|
||||||
|
va108xx-hal = { path = "../../va108xx-hal" }
|
||||||
|
|
||||||
|
[profile.dev]
|
||||||
|
codegen-units = 1
|
||||||
|
debug = 2
|
||||||
|
debug-assertions = true # <-
|
||||||
|
incremental = false
|
||||||
|
# This is problematic for stepping..
|
||||||
|
# opt-level = 'z' # <-
|
||||||
|
overflow-checks = true # <-
|
||||||
|
|
||||||
|
# cargo build/run --release
|
||||||
|
[profile.release]
|
||||||
|
codegen-units = 1
|
||||||
|
debug = 2
|
||||||
|
debug-assertions = false # <-
|
||||||
|
incremental = false
|
||||||
|
lto = 'fat'
|
||||||
|
opt-level = 3 # <-
|
||||||
|
overflow-checks = false # <-
|
||||||
|
|
||||||
|
[profile.small]
|
||||||
|
inherits = "release"
|
||||||
|
codegen-units = 1
|
||||||
|
debug-assertions = false # <-
|
||||||
|
lto = true
|
||||||
|
opt-level = 'z' # <-
|
||||||
|
overflow-checks = false # <-
|
||||||
|
# strip = true # Automatically strip symbols from the binary.
|
11
flashloader/slot-b-blinky/memory.x
Normal file
11
flashloader/slot-b-blinky/memory.x
Normal file
@ -0,0 +1,11 @@
|
|||||||
|
/* Special linker script for application slot B */
|
||||||
|
MEMORY
|
||||||
|
{
|
||||||
|
FLASH : ORIGIN = 0x000117FC, LENGTH = 0xE7FC
|
||||||
|
RAM : ORIGIN = 0x10000000, LENGTH = 0x08000 /* 32K */
|
||||||
|
}
|
||||||
|
|
||||||
|
/* This is where the call stack will be allocated. */
|
||||||
|
/* The stack is of the full descending type. */
|
||||||
|
/* NOTE Do NOT modify `_stack_start` unless you know what you are doing */
|
||||||
|
_stack_start = ORIGIN(RAM) + LENGTH(RAM);
|
25
flashloader/slot-b-blinky/src/main.rs
Normal file
25
flashloader/slot-b-blinky/src/main.rs
Normal file
@ -0,0 +1,25 @@
|
|||||||
|
//! Simple blinky example using the HAL
|
||||||
|
#![no_main]
|
||||||
|
#![no_std]
|
||||||
|
|
||||||
|
use cortex_m_rt::entry;
|
||||||
|
use embedded_hal::{delay::DelayNs, digital::StatefulOutputPin};
|
||||||
|
use panic_rtt_target as _;
|
||||||
|
use rtt_target::{rprintln, rtt_init_print};
|
||||||
|
use va108xx_hal::{gpio::PinsA, pac, prelude::*, timer::CountdownTimer};
|
||||||
|
|
||||||
|
#[entry]
|
||||||
|
fn main() -> ! {
|
||||||
|
rtt_init_print!();
|
||||||
|
rprintln!("VA108xx HAL blinky example for App Slot B");
|
||||||
|
|
||||||
|
let mut dp = pac::Peripherals::take().unwrap();
|
||||||
|
let mut timer = CountdownTimer::new(&mut dp.sysconfig, 50.MHz(), dp.tim0);
|
||||||
|
let porta = PinsA::new(&mut dp.sysconfig, Some(dp.ioconfig), dp.porta);
|
||||||
|
let mut led2 = porta.pa7.into_readable_push_pull_output();
|
||||||
|
|
||||||
|
loop {
|
||||||
|
led2.toggle().ok();
|
||||||
|
timer.delay_ms(1000);
|
||||||
|
}
|
||||||
|
}
|
9
flashloader/src/lib.rs
Normal file
9
flashloader/src/lib.rs
Normal file
@ -0,0 +1,9 @@
|
|||||||
|
#![no_std]
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
mod tests {
|
||||||
|
#[test]
|
||||||
|
fn simple() {
|
||||||
|
assert_eq!(1 + 1, 2);
|
||||||
|
}
|
||||||
|
}
|
468
flashloader/src/main.rs
Normal file
468
flashloader/src/main.rs
Normal file
@ -0,0 +1,468 @@
|
|||||||
|
//! Vorago flashloader which can be used to flash image A and image B via a simple
|
||||||
|
//! low-level CCSDS memory interface via a UART interface.
|
||||||
|
#![no_main]
|
||||||
|
#![no_std]
|
||||||
|
|
||||||
|
use num_enum::TryFromPrimitive;
|
||||||
|
use panic_rtt_target as _;
|
||||||
|
use ringbuf::{
|
||||||
|
traits::{Consumer, Observer, Producer},
|
||||||
|
StaticRb,
|
||||||
|
};
|
||||||
|
use va108xx_hal::prelude::*;
|
||||||
|
|
||||||
|
const SYSCLK_FREQ: Hertz = Hertz::from_raw(50_000_000);
|
||||||
|
|
||||||
|
const MAX_TC_SIZE: usize = 524;
|
||||||
|
const MAX_TC_FRAME_SIZE: usize = cobs::max_encoding_length(MAX_TC_SIZE);
|
||||||
|
|
||||||
|
const MAX_TM_SIZE: usize = 128;
|
||||||
|
const MAX_TM_FRAME_SIZE: usize = cobs::max_encoding_length(MAX_TM_SIZE);
|
||||||
|
|
||||||
|
const UART_BAUDRATE: u32 = 115200;
|
||||||
|
const BOOT_NVM_MEMORY_ID: u8 = 1;
|
||||||
|
const RX_DEBUGGING: bool = false;
|
||||||
|
|
||||||
|
pub enum ActionId {
|
||||||
|
CorruptImageA = 128,
|
||||||
|
CorruptImageB = 129,
|
||||||
|
SetBootSlot = 130,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Copy, Clone, PartialEq, Eq, TryFromPrimitive)]
|
||||||
|
#[repr(u8)]
|
||||||
|
enum AppSel {
|
||||||
|
A = 0,
|
||||||
|
B = 1,
|
||||||
|
}
|
||||||
|
|
||||||
|
// Larger buffer for TC to be able to hold the possibly large memory write packets.
|
||||||
|
const BUF_RB_SIZE_TC: usize = 1024;
|
||||||
|
const SIZES_RB_SIZE_TC: usize = 16;
|
||||||
|
|
||||||
|
const BUF_RB_SIZE_TM: usize = 256;
|
||||||
|
const SIZES_RB_SIZE_TM: usize = 16;
|
||||||
|
|
||||||
|
pub struct RingBufWrapper<const BUF_SIZE: usize, const SIZES_LEN: usize> {
|
||||||
|
pub buf: StaticRb<u8, BUF_SIZE>,
|
||||||
|
pub sizes: StaticRb<usize, SIZES_LEN>,
|
||||||
|
}
|
||||||
|
|
||||||
|
pub const APP_A_START_ADDR: u32 = 0x3000;
|
||||||
|
pub const APP_A_END_ADDR: u32 = 0x117FC;
|
||||||
|
pub const APP_B_START_ADDR: u32 = APP_A_END_ADDR;
|
||||||
|
pub const APP_B_END_ADDR: u32 = 0x20000;
|
||||||
|
|
||||||
|
pub const PREFERRED_SLOT_OFFSET: u32 = 0x20000 - 1;
|
||||||
|
|
||||||
|
#[rtic::app(device = pac, dispatchers = [OC20, OC21, OC22])]
|
||||||
|
mod app {
|
||||||
|
use super::*;
|
||||||
|
use cortex_m::asm;
|
||||||
|
use embedded_io::Write;
|
||||||
|
use panic_rtt_target as _;
|
||||||
|
use rtic::Mutex;
|
||||||
|
use rtic_monotonics::systick::prelude::*;
|
||||||
|
use rtt_target::rprintln;
|
||||||
|
use satrs::pus::verification::{FailParams, VerificationReportCreator};
|
||||||
|
use spacepackets::ecss::PusServiceId;
|
||||||
|
use spacepackets::ecss::{
|
||||||
|
tc::PusTcReader, tm::PusTmCreator, EcssEnumU8, PusPacket, WritablePusPacket,
|
||||||
|
};
|
||||||
|
use va108xx_hal::gpio::PinsA;
|
||||||
|
use va108xx_hal::uart::IrqContextTimeoutOrMaxSize;
|
||||||
|
use va108xx_hal::{pac, uart, InterruptConfig};
|
||||||
|
use vorago_reb1::m95m01::M95M01;
|
||||||
|
|
||||||
|
#[derive(Default, Debug, Copy, Clone, PartialEq, Eq)]
|
||||||
|
pub enum CobsReaderStates {
|
||||||
|
#[default]
|
||||||
|
WaitingForStart,
|
||||||
|
WatingForEnd,
|
||||||
|
FrameOverflow,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[local]
|
||||||
|
struct Local {
|
||||||
|
uart_rx: uart::RxWithInterrupt<pac::Uarta>,
|
||||||
|
uart_tx: uart::Tx<pac::Uarta>,
|
||||||
|
rx_context: IrqContextTimeoutOrMaxSize,
|
||||||
|
verif_reporter: VerificationReportCreator,
|
||||||
|
nvm: M95M01,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[shared]
|
||||||
|
struct Shared {
|
||||||
|
// Having this shared allows multiple tasks to generate telemetry.
|
||||||
|
tm_rb: RingBufWrapper<BUF_RB_SIZE_TM, SIZES_RB_SIZE_TM>,
|
||||||
|
tc_rb: RingBufWrapper<BUF_RB_SIZE_TC, SIZES_RB_SIZE_TC>,
|
||||||
|
}
|
||||||
|
|
||||||
|
rtic_monotonics::systick_monotonic!(Mono, 1000);
|
||||||
|
|
||||||
|
#[init]
|
||||||
|
fn init(cx: init::Context) -> (Shared, Local) {
|
||||||
|
rtt_log::init();
|
||||||
|
rprintln!("-- Vorago flashloader --");
|
||||||
|
|
||||||
|
Mono::start(cx.core.SYST, SYSCLK_FREQ.raw());
|
||||||
|
|
||||||
|
let mut dp = cx.device;
|
||||||
|
let nvm = M95M01::new(&mut dp.sysconfig, SYSCLK_FREQ, dp.spic);
|
||||||
|
|
||||||
|
let gpioa = PinsA::new(&mut dp.sysconfig, dp.porta);
|
||||||
|
let tx = gpioa.pa9.into_funsel_2();
|
||||||
|
let rx = gpioa.pa8.into_funsel_2();
|
||||||
|
|
||||||
|
let irq_uart = uart::Uart::new_with_interrupt(
|
||||||
|
&mut dp.sysconfig,
|
||||||
|
SYSCLK_FREQ,
|
||||||
|
dp.uarta,
|
||||||
|
(tx, rx),
|
||||||
|
UART_BAUDRATE.Hz(),
|
||||||
|
InterruptConfig::new(pac::Interrupt::OC0, true, true),
|
||||||
|
);
|
||||||
|
let (tx, rx) = irq_uart.split();
|
||||||
|
// Unwrap is okay, we explicitely set the interrupt ID.
|
||||||
|
let mut rx = rx.into_rx_with_irq();
|
||||||
|
|
||||||
|
let verif_reporter = VerificationReportCreator::new(0).unwrap();
|
||||||
|
|
||||||
|
let mut rx_context = IrqContextTimeoutOrMaxSize::new(MAX_TC_FRAME_SIZE);
|
||||||
|
rx.read_fixed_len_or_timeout_based_using_irq(&mut rx_context)
|
||||||
|
.expect("initiating UART RX failed");
|
||||||
|
pus_tc_handler::spawn().unwrap();
|
||||||
|
pus_tm_tx_handler::spawn().unwrap();
|
||||||
|
(
|
||||||
|
Shared {
|
||||||
|
tc_rb: RingBufWrapper {
|
||||||
|
buf: StaticRb::default(),
|
||||||
|
sizes: StaticRb::default(),
|
||||||
|
},
|
||||||
|
tm_rb: RingBufWrapper {
|
||||||
|
buf: StaticRb::default(),
|
||||||
|
sizes: StaticRb::default(),
|
||||||
|
},
|
||||||
|
},
|
||||||
|
Local {
|
||||||
|
uart_rx: rx,
|
||||||
|
uart_tx: tx,
|
||||||
|
rx_context,
|
||||||
|
verif_reporter,
|
||||||
|
nvm,
|
||||||
|
},
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
// `shared` cannot be accessed from this context
|
||||||
|
#[idle]
|
||||||
|
fn idle(_cx: idle::Context) -> ! {
|
||||||
|
loop {
|
||||||
|
asm::nop();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// This is the interrupt handler to read all bytes received on the UART0.
|
||||||
|
#[task(
|
||||||
|
binds = OC0,
|
||||||
|
local = [
|
||||||
|
cnt: u32 = 0,
|
||||||
|
rx_buf: [u8; MAX_TC_FRAME_SIZE] = [0; MAX_TC_FRAME_SIZE],
|
||||||
|
rx_context,
|
||||||
|
uart_rx,
|
||||||
|
],
|
||||||
|
shared = [tc_rb]
|
||||||
|
)]
|
||||||
|
fn uart_rx_irq(mut cx: uart_rx_irq::Context) {
|
||||||
|
match cx
|
||||||
|
.local
|
||||||
|
.uart_rx
|
||||||
|
.on_interrupt_max_size_or_timeout_based(cx.local.rx_context, cx.local.rx_buf)
|
||||||
|
{
|
||||||
|
Ok(result) => {
|
||||||
|
if RX_DEBUGGING {
|
||||||
|
log::debug!("RX Info: {:?}", cx.local.rx_context);
|
||||||
|
log::debug!("RX Result: {:?}", result);
|
||||||
|
}
|
||||||
|
if result.complete() {
|
||||||
|
// Check frame validity (must have COBS format) and decode the frame.
|
||||||
|
// Currently, we expect a full frame or a frame received through a timeout
|
||||||
|
// to be one COBS frame. We could parse for multiple COBS packets in one
|
||||||
|
// frame, but the additional complexity is not necessary here..
|
||||||
|
if cx.local.rx_buf[0] == 0 && cx.local.rx_buf[result.bytes_read - 1] == 0 {
|
||||||
|
let decoded_size =
|
||||||
|
cobs::decode_in_place(&mut cx.local.rx_buf[1..result.bytes_read]);
|
||||||
|
if decoded_size.is_err() {
|
||||||
|
log::warn!("COBS decoding failed");
|
||||||
|
} else {
|
||||||
|
let decoded_size = decoded_size.unwrap();
|
||||||
|
let mut tc_rb_full = false;
|
||||||
|
cx.shared.tc_rb.lock(|rb| {
|
||||||
|
if rb.sizes.vacant_len() >= 1 && rb.buf.vacant_len() >= decoded_size
|
||||||
|
{
|
||||||
|
rb.sizes.try_push(decoded_size).unwrap();
|
||||||
|
rb.buf.push_slice(&cx.local.rx_buf[1..1 + decoded_size]);
|
||||||
|
} else {
|
||||||
|
tc_rb_full = true;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
if tc_rb_full {
|
||||||
|
log::warn!("COBS TC queue full");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
log::warn!("COBS frame with invalid format, start and end bytes are not 0");
|
||||||
|
}
|
||||||
|
|
||||||
|
// Initiate next transfer.
|
||||||
|
cx.local
|
||||||
|
.uart_rx
|
||||||
|
.read_fixed_len_or_timeout_based_using_irq(cx.local.rx_context)
|
||||||
|
.expect("read operation failed");
|
||||||
|
}
|
||||||
|
if result.has_errors() {
|
||||||
|
log::warn!("UART error: {:?}", result.errors.unwrap());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Err(e) => {
|
||||||
|
log::warn!("UART error: {:?}", e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[task(
|
||||||
|
priority = 2,
|
||||||
|
local=[
|
||||||
|
tc_buf: [u8; MAX_TC_SIZE] = [0; MAX_TC_SIZE],
|
||||||
|
readback_buf: [u8; MAX_TC_SIZE] = [0; MAX_TC_SIZE],
|
||||||
|
src_data_buf: [u8; 16] = [0; 16],
|
||||||
|
verif_buf: [u8; 32] = [0; 32],
|
||||||
|
nvm,
|
||||||
|
verif_reporter
|
||||||
|
],
|
||||||
|
shared=[tm_rb, tc_rb]
|
||||||
|
)]
|
||||||
|
async fn pus_tc_handler(mut cx: pus_tc_handler::Context) {
|
||||||
|
loop {
|
||||||
|
// Try to read a TC from the ring buffer.
|
||||||
|
let packet_len = cx.shared.tc_rb.lock(|rb| rb.sizes.try_pop());
|
||||||
|
if packet_len.is_none() {
|
||||||
|
// Small delay, TCs might arrive very quickly.
|
||||||
|
Mono::delay(20.millis()).await;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
let packet_len = packet_len.unwrap();
|
||||||
|
log::info!(target: "TC Handler", "received packet with length {}", packet_len);
|
||||||
|
let popped_packet_len = cx
|
||||||
|
.shared
|
||||||
|
.tc_rb
|
||||||
|
.lock(|rb| rb.buf.pop_slice(&mut cx.local.tc_buf[0..packet_len]));
|
||||||
|
assert_eq!(popped_packet_len, packet_len);
|
||||||
|
// Read a telecommand, now handle it.
|
||||||
|
handle_valid_pus_tc(&mut cx);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn handle_valid_pus_tc(cx: &mut pus_tc_handler::Context) {
|
||||||
|
let pus_tc = PusTcReader::new(cx.local.tc_buf);
|
||||||
|
if pus_tc.is_err() {
|
||||||
|
log::warn!(target: "TC Handler", "PUS TC error: {}", pus_tc.unwrap_err());
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
let (pus_tc, _) = pus_tc.unwrap();
|
||||||
|
let mut write_and_send = |tm: &PusTmCreator| {
|
||||||
|
let written_size = tm.write_to_bytes(cx.local.verif_buf).unwrap();
|
||||||
|
cx.shared.tm_rb.lock(|prod| {
|
||||||
|
prod.sizes.try_push(tm.len_written()).unwrap();
|
||||||
|
prod.buf.push_slice(&cx.local.verif_buf[0..written_size]);
|
||||||
|
});
|
||||||
|
};
|
||||||
|
let token = cx.local.verif_reporter.add_tc(&pus_tc);
|
||||||
|
let (tm, accepted_token) = cx
|
||||||
|
.local
|
||||||
|
.verif_reporter
|
||||||
|
.acceptance_success(cx.local.src_data_buf, token, 0, 0, &[])
|
||||||
|
.expect("acceptance success failed");
|
||||||
|
write_and_send(&tm);
|
||||||
|
|
||||||
|
let (tm, started_token) = cx
|
||||||
|
.local
|
||||||
|
.verif_reporter
|
||||||
|
.start_success(cx.local.src_data_buf, accepted_token, 0, 0, &[])
|
||||||
|
.expect("acceptance success failed");
|
||||||
|
write_and_send(&tm);
|
||||||
|
|
||||||
|
if pus_tc.service() == PusServiceId::Action as u8 {
|
||||||
|
let mut corrupt_image = |base_addr: u32| {
|
||||||
|
let mut buf = [0u8; 4];
|
||||||
|
cx.local
|
||||||
|
.nvm
|
||||||
|
.read(base_addr as usize + 32, &mut buf)
|
||||||
|
.expect("reading from NVM failed");
|
||||||
|
buf[0] += 1;
|
||||||
|
cx.local
|
||||||
|
.nvm
|
||||||
|
.write(base_addr as usize + 32, &buf)
|
||||||
|
.expect("writing to NVM failed");
|
||||||
|
let tm = cx
|
||||||
|
.local
|
||||||
|
.verif_reporter
|
||||||
|
.completion_success(cx.local.src_data_buf, started_token, 0, 0, &[])
|
||||||
|
.expect("completion success failed");
|
||||||
|
write_and_send(&tm);
|
||||||
|
};
|
||||||
|
if pus_tc.subservice() == ActionId::CorruptImageA as u8 {
|
||||||
|
rprintln!("corrupting App Image A");
|
||||||
|
corrupt_image(APP_A_START_ADDR);
|
||||||
|
}
|
||||||
|
if pus_tc.subservice() == ActionId::CorruptImageB as u8 {
|
||||||
|
rprintln!("corrupting App Image B");
|
||||||
|
corrupt_image(APP_B_START_ADDR);
|
||||||
|
}
|
||||||
|
if pus_tc.subservice() == ActionId::SetBootSlot as u8 {
|
||||||
|
if pus_tc.app_data().is_empty() {
|
||||||
|
log::warn!(target: "TC Handler", "App data for preferred image command too short");
|
||||||
|
}
|
||||||
|
let app_sel_result = AppSel::try_from(pus_tc.app_data()[0]);
|
||||||
|
if app_sel_result.is_err() {
|
||||||
|
log::warn!("Invalid app selection value: {}", pus_tc.app_data()[0]);
|
||||||
|
}
|
||||||
|
log::info!(target: "TC Handler", "received boot selection command with app select: {:?}", app_sel_result.unwrap());
|
||||||
|
cx.local
|
||||||
|
.nvm
|
||||||
|
.write(PREFERRED_SLOT_OFFSET as usize, &[pus_tc.app_data()[0]])
|
||||||
|
.expect("writing to NVM failed");
|
||||||
|
let tm = cx
|
||||||
|
.local
|
||||||
|
.verif_reporter
|
||||||
|
.completion_success(cx.local.src_data_buf, started_token, 0, 0, &[])
|
||||||
|
.expect("completion success failed");
|
||||||
|
write_and_send(&tm);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if pus_tc.service() == PusServiceId::Test as u8 && pus_tc.subservice() == 1 {
|
||||||
|
log::info!(target: "TC Handler", "received ping TC");
|
||||||
|
let tm = cx
|
||||||
|
.local
|
||||||
|
.verif_reporter
|
||||||
|
.completion_success(cx.local.src_data_buf, started_token, 0, 0, &[])
|
||||||
|
.expect("completion success failed");
|
||||||
|
write_and_send(&tm);
|
||||||
|
} else if pus_tc.service() == PusServiceId::MemoryManagement as u8 {
|
||||||
|
let tm = cx
|
||||||
|
.local
|
||||||
|
.verif_reporter
|
||||||
|
.step_success(
|
||||||
|
cx.local.src_data_buf,
|
||||||
|
&started_token,
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
&[],
|
||||||
|
EcssEnumU8::new(0),
|
||||||
|
)
|
||||||
|
.expect("step success failed");
|
||||||
|
write_and_send(&tm);
|
||||||
|
// Raw memory write TC
|
||||||
|
if pus_tc.subservice() == 2 {
|
||||||
|
let app_data = pus_tc.app_data();
|
||||||
|
if app_data.len() < 10 {
|
||||||
|
log::warn!(
|
||||||
|
target: "TC Handler",
|
||||||
|
"app data for raw memory write is too short: {}",
|
||||||
|
app_data.len()
|
||||||
|
);
|
||||||
|
}
|
||||||
|
let memory_id = app_data[0];
|
||||||
|
if memory_id != BOOT_NVM_MEMORY_ID {
|
||||||
|
log::warn!(target: "TC Handler", "memory ID {} not supported", memory_id);
|
||||||
|
// TODO: Error reporting
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
let offset = u32::from_be_bytes(app_data[2..6].try_into().unwrap());
|
||||||
|
let data_len = u32::from_be_bytes(app_data[6..10].try_into().unwrap());
|
||||||
|
if 10 + data_len as usize > app_data.len() {
|
||||||
|
log::warn!(
|
||||||
|
target: "TC Handler",
|
||||||
|
"invalid data length {} for raw mem write detected",
|
||||||
|
data_len
|
||||||
|
);
|
||||||
|
// TODO: Error reporting
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
let data = &app_data[10..10 + data_len as usize];
|
||||||
|
log::info!(
|
||||||
|
target: "TC Handler",
|
||||||
|
"writing {} bytes at offset {} to NVM",
|
||||||
|
data_len,
|
||||||
|
offset
|
||||||
|
);
|
||||||
|
cx.local
|
||||||
|
.nvm
|
||||||
|
.write(offset as usize, data)
|
||||||
|
.expect("writing to NVM failed");
|
||||||
|
let tm = if !cx
|
||||||
|
.local
|
||||||
|
.nvm
|
||||||
|
.verify(offset as usize, data)
|
||||||
|
.expect("NVM verification failed")
|
||||||
|
{
|
||||||
|
log::warn!("verification of data written to NVM failed");
|
||||||
|
cx.local
|
||||||
|
.verif_reporter
|
||||||
|
.completion_failure(
|
||||||
|
cx.local.src_data_buf,
|
||||||
|
started_token,
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
FailParams::new(&[], &EcssEnumU8::new(0), &[]),
|
||||||
|
)
|
||||||
|
.expect("completion success failed")
|
||||||
|
} else {
|
||||||
|
cx.local
|
||||||
|
.verif_reporter
|
||||||
|
.completion_success(cx.local.src_data_buf, started_token, 0, 0, &[])
|
||||||
|
.expect("completion success failed")
|
||||||
|
};
|
||||||
|
write_and_send(&tm);
|
||||||
|
log::info!(
|
||||||
|
target: "TC Handler",
|
||||||
|
"NVM operation done");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[task(
|
||||||
|
priority = 1,
|
||||||
|
local=[
|
||||||
|
read_buf: [u8;MAX_TM_SIZE] = [0; MAX_TM_SIZE],
|
||||||
|
encoded_buf: [u8;MAX_TM_FRAME_SIZE] = [0; MAX_TM_FRAME_SIZE],
|
||||||
|
uart_tx,
|
||||||
|
],
|
||||||
|
shared=[tm_rb]
|
||||||
|
)]
|
||||||
|
async fn pus_tm_tx_handler(mut cx: pus_tm_tx_handler::Context) {
|
||||||
|
loop {
|
||||||
|
let mut occupied_len = cx.shared.tm_rb.lock(|rb| rb.sizes.occupied_len());
|
||||||
|
while occupied_len > 0 {
|
||||||
|
let next_size = cx.shared.tm_rb.lock(|rb| {
|
||||||
|
let next_size = rb.sizes.try_pop().unwrap();
|
||||||
|
rb.buf.pop_slice(&mut cx.local.read_buf[0..next_size]);
|
||||||
|
next_size
|
||||||
|
});
|
||||||
|
cx.local.encoded_buf[0] = 0;
|
||||||
|
let send_size = cobs::encode(
|
||||||
|
&cx.local.read_buf[0..next_size],
|
||||||
|
&mut cx.local.encoded_buf[1..],
|
||||||
|
);
|
||||||
|
cx.local.encoded_buf[send_size + 1] = 0;
|
||||||
|
cx.local
|
||||||
|
.uart_tx
|
||||||
|
.write(&cx.local.encoded_buf[0..send_size + 2])
|
||||||
|
.unwrap();
|
||||||
|
occupied_len -= 1;
|
||||||
|
Mono::delay(2.millis()).await;
|
||||||
|
}
|
||||||
|
Mono::delay(50.millis()).await;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -1,3 +1,3 @@
|
|||||||
#!/bin/bash
|
#!/bin/bash
|
||||||
JLinkGDBServer -select USB -device Cortex-M0 -endian little -if JTAG-speed auto \
|
JLinkGDBServer -select USB -device Cortex-M0 -endian little -if JTAG -speed auto \
|
||||||
-LocalhostOnly
|
-LocalhostOnly
|
||||||
|
@ -9,27 +9,56 @@ variants:
|
|||||||
core_access_options: !Arm
|
core_access_options: !Arm
|
||||||
ap: 0
|
ap: 0
|
||||||
psel: 0x0
|
psel: 0x0
|
||||||
|
jtag_tap: 1
|
||||||
memory_map:
|
memory_map:
|
||||||
- !Ram
|
- !Ram
|
||||||
name: IRAM1
|
name: DRAM
|
||||||
range:
|
range:
|
||||||
start: 0x10000000
|
start: 0x10000000
|
||||||
end: 0x10008000
|
end: 0x10008000
|
||||||
cores:
|
cores:
|
||||||
- main
|
- main
|
||||||
- !Nvm
|
- !Nvm
|
||||||
name: IROM1
|
name: NVM
|
||||||
range:
|
range:
|
||||||
start: 0x0
|
start: 0x0
|
||||||
end: 0x20000
|
end: 0x20000
|
||||||
is_boot_memory: true
|
|
||||||
cores:
|
cores:
|
||||||
- main
|
- main
|
||||||
|
access:
|
||||||
|
write: false
|
||||||
|
boot: true
|
||||||
flash_algorithms:
|
flash_algorithms:
|
||||||
- va108xx_fm25v20a_fram_128kb_prog
|
- va108xx_fm25v20a_fram_128kb_prog
|
||||||
- va108xx_m95m01_128kb_prog
|
- va108xx_m95m01_128kb_prog
|
||||||
- va108xx_mr25h10_1mb_prog
|
- va108xx_mr25h10_1mb_prog
|
||||||
- va108xx_ttflash_prog
|
- va108xx_ttflash_prog
|
||||||
|
- name: VA108xx_RAM
|
||||||
|
cores:
|
||||||
|
- name: main
|
||||||
|
type: armv6m
|
||||||
|
core_access_options: !Arm
|
||||||
|
ap: 0
|
||||||
|
psel: 0x0
|
||||||
|
jtag_tap: 1
|
||||||
|
memory_map:
|
||||||
|
- !Ram
|
||||||
|
name: DRAM
|
||||||
|
range:
|
||||||
|
start: 0x10000000
|
||||||
|
end: 0x10008000
|
||||||
|
cores:
|
||||||
|
- main
|
||||||
|
- !Ram
|
||||||
|
name: IRAM
|
||||||
|
range:
|
||||||
|
start: 0x0
|
||||||
|
end: 0x20000
|
||||||
|
cores:
|
||||||
|
- main
|
||||||
|
access:
|
||||||
|
write: false
|
||||||
|
boot: true
|
||||||
flash_algorithms:
|
flash_algorithms:
|
||||||
- name: va108xx_fm25v20a_fram_128kb_prog
|
- name: va108xx_fm25v20a_fram_128kb_prog
|
||||||
description: VA108_FM25V20A_FRAM_128KB
|
description: VA108_FM25V20A_FRAM_128KB
|
||||||
|
10
scripts/memory_app_a.x
Normal file
10
scripts/memory_app_a.x
Normal file
@ -0,0 +1,10 @@
|
|||||||
|
MEMORY
|
||||||
|
{
|
||||||
|
FLASH : ORIGIN = 0x00003000, LENGTH = 0xE7F8 /* (128k - 12k) / 2 - 8 */
|
||||||
|
RAM : ORIGIN = 0x10000000, LENGTH = 0x08000 /* 32K */
|
||||||
|
}
|
||||||
|
|
||||||
|
/* This is where the call stack will be allocated. */
|
||||||
|
/* The stack is of the full descending type. */
|
||||||
|
/* NOTE Do NOT modify `_stack_start` unless you know what you are doing */
|
||||||
|
_stack_start = ORIGIN(RAM) + LENGTH(RAM);
|
10
scripts/memory_app_b.x
Normal file
10
scripts/memory_app_b.x
Normal file
@ -0,0 +1,10 @@
|
|||||||
|
MEMORY
|
||||||
|
{
|
||||||
|
FLASH : ORIGIN = 0x00011800, LENGTH = 0xE7F8 /* (128k - 12k) / 2 - 8 */
|
||||||
|
RAM : ORIGIN = 0x10000000, LENGTH = 0x08000 /* 32K */
|
||||||
|
}
|
||||||
|
|
||||||
|
/* This is where the call stack will be allocated. */
|
||||||
|
/* The stack is of the full descending type. */
|
||||||
|
/* NOTE Do NOT modify `_stack_start` unless you know what you are doing */
|
||||||
|
_stack_start = ORIGIN(RAM) + LENGTH(RAM);
|
27
va108xx-embassy/Cargo.toml
Normal file
27
va108xx-embassy/Cargo.toml
Normal file
@ -0,0 +1,27 @@
|
|||||||
|
[package]
|
||||||
|
name = "va108xx-embassy"
|
||||||
|
version = "0.1.0"
|
||||||
|
edition = "2021"
|
||||||
|
|
||||||
|
[dependencies]
|
||||||
|
critical-section = "1"
|
||||||
|
portable-atomic = { version = "1", features = ["unsafe-assume-single-core"]}
|
||||||
|
|
||||||
|
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"] }
|
||||||
|
|
||||||
|
[dependencies.va108xx-hal]
|
||||||
|
path = "../va108xx-hal"
|
||||||
|
|
||||||
|
[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"]
|
10
va108xx-embassy/README.md
Normal file
10
va108xx-embassy/README.md
Normal file
@ -0,0 +1,10 @@
|
|||||||
|
[](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.
|
416
va108xx-embassy/src/lib.rs
Normal file
416
va108xx-embassy/src/lib.rs
Normal file
@ -0,0 +1,416 @@
|
|||||||
|
//! # 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 only exposes the [embassy::init] method which sets up the time driver. This
|
||||||
|
//! function must be called once at the start of the application.
|
||||||
|
//!
|
||||||
|
//! This implementation requires two TIM peripherals provided by the VA108xx device.
|
||||||
|
//! The user can freely specify the two used TIM peripheral by passing the concrete TIM instances
|
||||||
|
//! into the [embassy::init_with_custom_irqs] and [embassy::init] method.
|
||||||
|
//!
|
||||||
|
//! 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::embassy_time_driver_irqs] macro to declare the IRQ handlers in the
|
||||||
|
//! application code. If this is done, [embassy::init_with_custom_irqs] must be used
|
||||||
|
//! method to pass the IRQ numbers to the library.
|
||||||
|
//!
|
||||||
|
//! ## Examples
|
||||||
|
//!
|
||||||
|
//! [embassy example project](https://egit.irs.uni-stuttgart.de/rust/va108xx-rs/src/branch/main/examples/embassy)
|
||||||
|
#![no_std]
|
||||||
|
use core::cell::{Cell, RefCell};
|
||||||
|
use critical_section::CriticalSection;
|
||||||
|
use embassy_sync::blocking_mutex::CriticalSectionMutex as 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},
|
||||||
|
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 [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::embassy::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::embassy::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);
|
||||||
|
|
||||||
|
pub mod embassy {
|
||||||
|
use super::*;
|
||||||
|
use va108xx_hal::{pac, timer::TimRegInterface};
|
||||||
|
|
||||||
|
/// Expose the time driver so the user can specify the IRQ handlers themselves.
|
||||||
|
pub fn time_driver() -> &'static TimerDriver {
|
||||||
|
&TIME_DRIVER
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Initialization method for embassy
|
||||||
|
///
|
||||||
|
/// # Safety
|
||||||
|
///
|
||||||
|
/// This has to be called once at initialization time to initiate the time driver for
|
||||||
|
/// embassy.
|
||||||
|
#[cfg(feature = "irqs-in-lib")]
|
||||||
|
pub unsafe fn init(
|
||||||
|
syscfg: &mut pac::Sysconfig,
|
||||||
|
irqsel: &pac::Irqsel,
|
||||||
|
sysclk: impl Into<Hertz>,
|
||||||
|
timekeeper_tim: impl TimRegInterface,
|
||||||
|
alarm_tim: impl TimRegInterface,
|
||||||
|
) {
|
||||||
|
TIME_DRIVER.init(
|
||||||
|
syscfg,
|
||||||
|
irqsel,
|
||||||
|
sysclk,
|
||||||
|
timekeeper_tim,
|
||||||
|
alarm_tim,
|
||||||
|
TIMEKEEPER_IRQ,
|
||||||
|
ALARM_IRQ,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Initialization method for embassy
|
||||||
|
///
|
||||||
|
/// # Safety
|
||||||
|
///
|
||||||
|
/// This has to be called once at initialization time to initiate the time driver for
|
||||||
|
/// embassy.
|
||||||
|
pub unsafe fn init_with_custom_irqs(
|
||||||
|
syscfg: &mut pac::Sysconfig,
|
||||||
|
irqsel: &pac::Irqsel,
|
||||||
|
sysclk: impl Into<Hertz>,
|
||||||
|
timekeeper_tim: impl TimRegInterface,
|
||||||
|
alarm_tim: impl TimRegInterface,
|
||||||
|
timekeeper_irq: pac::Interrupt,
|
||||||
|
alarm_irq: pac::Interrupt,
|
||||||
|
) {
|
||||||
|
TIME_DRIVER.init(
|
||||||
|
syscfg,
|
||||||
|
irqsel,
|
||||||
|
sysclk,
|
||||||
|
timekeeper_tim,
|
||||||
|
alarm_tim,
|
||||||
|
timekeeper_irq,
|
||||||
|
alarm_irq,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
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(
|
||||||
|
&self,
|
||||||
|
syscfg: &mut pac::Sysconfig,
|
||||||
|
irqsel: &pac::Irqsel,
|
||||||
|
sysclk: impl Into<Hertz>,
|
||||||
|
timekeeper_tim: impl TimRegInterface,
|
||||||
|
alarm_tim: impl TimRegInterface,
|
||||||
|
timekeeper_irq: pac::Interrupt,
|
||||||
|
alarm_irq: pac::Interrupt,
|
||||||
|
) {
|
||||||
|
if ALARM_TIM.get().is_some() {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
ALARM_TIM.set(alarm_tim.tim_id()).ok();
|
||||||
|
TIMEKEEPER_TIM.set(timekeeper_tim.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());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
@ -6,6 +6,70 @@ All notable changes to this project will be documented in this file.
|
|||||||
The format is based on [Keep a Changelog](http://keepachangelog.com/)
|
The format is based on [Keep a Changelog](http://keepachangelog.com/)
|
||||||
and this project adheres to [Semantic Versioning](http://semver.org/).
|
and this project adheres to [Semantic Versioning](http://semver.org/).
|
||||||
|
|
||||||
|
## [unreleased]
|
||||||
|
|
||||||
|
## [v0.9.0]
|
||||||
|
|
||||||
|
## Fixed
|
||||||
|
|
||||||
|
- Important bugfix for UART driver which causes UART B drivers not to work.
|
||||||
|
|
||||||
|
## Removed
|
||||||
|
|
||||||
|
- Deleted some HAL re-exports in the PWM module
|
||||||
|
|
||||||
|
## 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
|
||||||
|
|
||||||
|
## Changed
|
||||||
|
|
||||||
|
- Improves `CascardSource` handling and general API when chosing cascade sources.
|
||||||
|
- Replaced `utility::unmask_irq` by `enable_interrupt` and `disable_interrupt` API.
|
||||||
|
- Improve and fix SPI abstractions. Add new low level interface. The primary SPI constructor now
|
||||||
|
only expects a configuration structure and the transfer configuration needs to be applied in a
|
||||||
|
separate step.
|
||||||
|
- Removed complete `timer` module re-export in `pwm` module
|
||||||
|
- `CountDownTimer` renamed to `CountdownTimer`
|
||||||
|
|
||||||
|
## Fixes
|
||||||
|
|
||||||
|
- Fixes for SPI peripheral: Flush implementation was incorrect and should now flush properly.
|
||||||
|
|
||||||
## [v0.7.0] 2024-07-04
|
## [v0.7.0] 2024-07-04
|
||||||
|
|
||||||
- Replace `uarta` and `uartb` `Uart` constructors by `new` constructor
|
- Replace `uarta` and `uartb` `Uart` constructors by `new` constructor
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
[package]
|
[package]
|
||||||
name = "va108xx-hal"
|
name = "va108xx-hal"
|
||||||
version = "0.7.0"
|
version = "0.9.0"
|
||||||
authors = ["Robin Mueller <muellerr@irs.uni-stuttgart.de>"]
|
authors = ["Robin Mueller <muellerr@irs.uni-stuttgart.de>"]
|
||||||
edition = "2021"
|
edition = "2021"
|
||||||
description = "HAL for the Vorago VA108xx family of microcontrollers"
|
description = "HAL for the Vorago VA108xx family of microcontrollers"
|
||||||
@ -15,32 +15,34 @@ 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"
|
||||||
|
delegate = ">=0.12, <=0.13"
|
||||||
|
heapless = "0.8"
|
||||||
|
static_cell = "2"
|
||||||
|
thiserror = { version = "2", default-features = false }
|
||||||
|
void = { version = "1", default-features = false }
|
||||||
|
once_cell = {version = "1", default-features = false }
|
||||||
|
va108xx = { version = "0.4", default-features = false, features = ["critical-section"] }
|
||||||
|
embassy-sync = "0.6"
|
||||||
|
|
||||||
defmt = { version = "0.3", optional = true }
|
defmt = { version = "0.3", optional = true }
|
||||||
delegate = "0.12"
|
|
||||||
|
|
||||||
[dependencies.va108xx]
|
[target.'cfg(all(target_arch = "arm", target_os = "none"))'.dependencies]
|
||||||
version = "0.3"
|
portable-atomic = { version = "1", features = ["unsafe-assume-single-core"] }
|
||||||
default-features = false
|
[target.'cfg(not(all(target_arch = "arm", target_os = "none")))'.dependencies]
|
||||||
features = ["critical-section"]
|
portable-atomic = "1"
|
||||||
|
|
||||||
[dependencies.embedded-hal]
|
|
||||||
version = "1"
|
|
||||||
|
|
||||||
[dependencies.void]
|
|
||||||
version = "1"
|
|
||||||
default-features = false
|
|
||||||
|
|
||||||
[dependencies.once_cell]
|
|
||||||
version = "1.14"
|
|
||||||
default-features = false
|
|
||||||
|
|
||||||
[features]
|
[features]
|
||||||
default = ["rt"]
|
default = ["rt"]
|
||||||
rt = ["va108xx/rt"]
|
rt = ["va108xx/rt"]
|
||||||
|
defmt = ["dep:defmt", "fugit/defmt"]
|
||||||
|
|
||||||
[package.metadata.docs.rs]
|
[package.metadata.docs.rs]
|
||||||
all-features = true
|
all-features = true
|
||||||
|
@ -25,12 +25,6 @@ rustup target add thumbv6m-none-eabi
|
|||||||
|
|
||||||
After that, you can use `cargo build` to build the development version of the crate.
|
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
|
||||||
@ -65,3 +59,11 @@ 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/)
|
||||||
|
3
va108xx-hal/docs.sh
Executable file
3
va108xx-hal/docs.sh
Executable file
@ -0,0 +1,3 @@
|
|||||||
|
#!/bin/sh
|
||||||
|
export RUSTDOCFLAGS="--cfg docsrs --generate-link-to-definition -Z unstable-options"
|
||||||
|
cargo +nightly doc --all-features --open
|
@ -11,6 +11,7 @@ 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,
|
||||||
@ -39,13 +40,27 @@ pub fn get_sys_clock() -> Option<Hertz> {
|
|||||||
pub fn set_clk_div_register(syscfg: &mut va108xx::Sysconfig, clk_sel: FilterClkSel, div: u32) {
|
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 => syscfg.ioconfig_clkdiv1().write(|w| unsafe { w.bits(div) }),
|
FilterClkSel::Clk1 => {
|
||||||
FilterClkSel::Clk2 => syscfg.ioconfig_clkdiv2().write(|w| unsafe { w.bits(div) }),
|
syscfg.ioconfig_clkdiv1().write(|w| unsafe { w.bits(div) });
|
||||||
FilterClkSel::Clk3 => syscfg.ioconfig_clkdiv3().write(|w| unsafe { w.bits(div) }),
|
}
|
||||||
FilterClkSel::Clk4 => syscfg.ioconfig_clkdiv4().write(|w| unsafe { w.bits(div) }),
|
FilterClkSel::Clk2 => {
|
||||||
FilterClkSel::Clk5 => syscfg.ioconfig_clkdiv5().write(|w| unsafe { w.bits(div) }),
|
syscfg.ioconfig_clkdiv2().write(|w| unsafe { w.bits(div) });
|
||||||
FilterClkSel::Clk6 => syscfg.ioconfig_clkdiv6().write(|w| unsafe { w.bits(div) }),
|
}
|
||||||
FilterClkSel::Clk7 => syscfg.ioconfig_clkdiv7().write(|w| unsafe { w.bits(div) }),
|
FilterClkSel::Clk3 => {
|
||||||
|
syscfg.ioconfig_clkdiv3().write(|w| unsafe { w.bits(div) });
|
||||||
|
}
|
||||||
|
FilterClkSel::Clk4 => {
|
||||||
|
syscfg.ioconfig_clkdiv4().write(|w| unsafe { w.bits(div) });
|
||||||
|
}
|
||||||
|
FilterClkSel::Clk5 => {
|
||||||
|
syscfg.ioconfig_clkdiv5().write(|w| unsafe { w.bits(div) });
|
||||||
|
}
|
||||||
|
FilterClkSel::Clk6 => {
|
||||||
|
syscfg.ioconfig_clkdiv6().write(|w| unsafe { w.bits(div) });
|
||||||
|
}
|
||||||
|
FilterClkSel::Clk7 => {
|
||||||
|
syscfg.ioconfig_clkdiv7().write(|w| unsafe { w.bits(div) });
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
449
va108xx-hal/src/gpio/asynch.rs
Normal file
449
va108xx-hal/src/gpio/asynch.rs
Normal file
@ -0,0 +1,449 @@
|
|||||||
|
//! # 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 one generic
|
||||||
|
//! [handler][on_interrupt_for_asynch_gpio] which should be called in ALL user interrupt handlers
|
||||||
|
//! which handle GPIO interrupts.
|
||||||
|
//!
|
||||||
|
//! # 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::digital::InputPin;
|
||||||
|
use embedded_hal_async::digital::Wait;
|
||||||
|
use portable_atomic::AtomicBool;
|
||||||
|
use va108xx::{self as pac, Irqsel, Sysconfig};
|
||||||
|
|
||||||
|
use crate::InterruptConfig;
|
||||||
|
|
||||||
|
use super::{
|
||||||
|
pin, DynGroup, DynPin, DynPinId, InputConfig, InterruptEdge, InvalidPinTypeError, Pin, PinId,
|
||||||
|
NUM_GPIO_PINS, NUM_PINS_PORT_A,
|
||||||
|
};
|
||||||
|
|
||||||
|
static WAKERS: [AtomicWaker; NUM_GPIO_PINS] = [const { AtomicWaker::new() }; NUM_GPIO_PINS];
|
||||||
|
static EDGE_DETECTION: [AtomicBool; NUM_GPIO_PINS] =
|
||||||
|
[const { AtomicBool::new(false) }; NUM_GPIO_PINS];
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
fn pin_id_to_offset(dyn_pin_id: DynPinId) -> usize {
|
||||||
|
match dyn_pin_id.group {
|
||||||
|
DynGroup::A => dyn_pin_id.num as usize,
|
||||||
|
DynGroup::B => NUM_PINS_PORT_A + dyn_pin_id.num as usize,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Generic interrupt handler for GPIO interrupts to support the async functionalities.
|
||||||
|
///
|
||||||
|
/// This handler will wake the correspoding wakers for the pins which triggered an interrupt
|
||||||
|
/// as well as updating the static edge detection structures. This allows the pin future to
|
||||||
|
/// complete async operations. The user should call this function in ALL interrupt handlers
|
||||||
|
/// which handle any GPIO interrupts.
|
||||||
|
#[inline]
|
||||||
|
pub fn on_interrupt_for_asynch_gpio() {
|
||||||
|
let periphs = unsafe { pac::Peripherals::steal() };
|
||||||
|
|
||||||
|
handle_interrupt_for_gpio_and_port(
|
||||||
|
periphs.porta.irq_enb().read().bits(),
|
||||||
|
periphs.porta.edge_status().read().bits(),
|
||||||
|
0,
|
||||||
|
);
|
||||||
|
handle_interrupt_for_gpio_and_port(
|
||||||
|
periphs.portb.irq_enb().read().bits(),
|
||||||
|
periphs.portb.edge_status().read().bits(),
|
||||||
|
NUM_PINS_PORT_A,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Uses the enabled interrupt register and the persistent edge status to capture all GPIO events.
|
||||||
|
#[inline]
|
||||||
|
fn handle_interrupt_for_gpio_and_port(mut irq_enb: u32, edge_status: u32, pin_base_offset: usize) {
|
||||||
|
while irq_enb != 0 {
|
||||||
|
let bit_pos = irq_enb.trailing_zeros() as usize;
|
||||||
|
let bit_mask = 1 << bit_pos;
|
||||||
|
|
||||||
|
WAKERS[pin_base_offset + bit_pos].wake();
|
||||||
|
|
||||||
|
if edge_status & bit_mask != 0 {
|
||||||
|
EDGE_DETECTION[pin_base_offset + 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,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl InputPinFuture {
|
||||||
|
/// # Safety
|
||||||
|
///
|
||||||
|
/// This calls [Self::new_with_dyn_pin] but uses [pac::Peripherals::steal] to get the system configuration
|
||||||
|
/// and IRQ selection peripherals. Users must ensure that the registers and configuration
|
||||||
|
/// related to this input pin are not being used elsewhere concurrently.
|
||||||
|
pub unsafe fn new_unchecked_with_dyn_pin(
|
||||||
|
pin: &mut DynPin,
|
||||||
|
irq: pac::Interrupt,
|
||||||
|
edge: InterruptEdge,
|
||||||
|
) -> Result<Self, InvalidPinTypeError> {
|
||||||
|
let mut periphs = pac::Peripherals::steal();
|
||||||
|
Self::new_with_dyn_pin(pin, irq, edge, &mut periphs.sysconfig, &mut periphs.irqsel)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn new_with_dyn_pin(
|
||||||
|
pin: &mut DynPin,
|
||||||
|
irq: pac::Interrupt,
|
||||||
|
edge: InterruptEdge,
|
||||||
|
sys_cfg: &mut Sysconfig,
|
||||||
|
irq_sel: &mut Irqsel,
|
||||||
|
) -> Result<Self, InvalidPinTypeError> {
|
||||||
|
if !pin.is_input_pin() {
|
||||||
|
return Err(InvalidPinTypeError(pin.mode()));
|
||||||
|
}
|
||||||
|
|
||||||
|
EDGE_DETECTION[pin_id_to_offset(pin.id())]
|
||||||
|
.store(false, core::sync::atomic::Ordering::Relaxed);
|
||||||
|
pin.interrupt_edge(
|
||||||
|
edge,
|
||||||
|
InterruptConfig::new(irq, true, true),
|
||||||
|
Some(sys_cfg),
|
||||||
|
Some(irq_sel),
|
||||||
|
)
|
||||||
|
.unwrap();
|
||||||
|
Ok(Self { pin_id: pin.id() })
|
||||||
|
}
|
||||||
|
|
||||||
|
/// # Safety
|
||||||
|
///
|
||||||
|
/// This calls [Self::new_with_pin] but uses [pac::Peripherals::steal] to get the system configuration
|
||||||
|
/// and IRQ selection peripherals. Users must ensure that the registers and configuration
|
||||||
|
/// related to this input pin are not being used elsewhere concurrently.
|
||||||
|
pub unsafe fn new_unchecked_with_pin<I: PinId, C: InputConfig>(
|
||||||
|
pin: &mut Pin<I, pin::Input<C>>,
|
||||||
|
irq: pac::Interrupt,
|
||||||
|
edge: InterruptEdge,
|
||||||
|
) -> Self {
|
||||||
|
let mut periphs = pac::Peripherals::steal();
|
||||||
|
Self::new_with_pin(pin, irq, edge, &mut periphs.sysconfig, &mut periphs.irqsel)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn new_with_pin<I: PinId, C: InputConfig>(
|
||||||
|
pin: &mut Pin<I, pin::Input<C>>,
|
||||||
|
irq: pac::Interrupt,
|
||||||
|
edge: InterruptEdge,
|
||||||
|
sys_cfg: &mut Sysconfig,
|
||||||
|
irq_sel: &mut Irqsel,
|
||||||
|
) -> Self {
|
||||||
|
EDGE_DETECTION[pin_id_to_offset(pin.id())]
|
||||||
|
.store(false, core::sync::atomic::Ordering::Relaxed);
|
||||||
|
pin.configure_edge_interrupt(
|
||||||
|
edge,
|
||||||
|
InterruptConfig::new(irq, true, true),
|
||||||
|
Some(sys_cfg),
|
||||||
|
Some(irq_sel),
|
||||||
|
);
|
||||||
|
Self { pin_id: pin.id() }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Drop for InputPinFuture {
|
||||||
|
fn drop(&mut self) {
|
||||||
|
let periphs = unsafe { pac::Peripherals::steal() };
|
||||||
|
if self.pin_id.group == DynGroup::A {
|
||||||
|
periphs
|
||||||
|
.porta
|
||||||
|
.irq_enb()
|
||||||
|
.modify(|r, w| unsafe { w.bits(r.bits() & !(1 << self.pin_id.num)) });
|
||||||
|
} else {
|
||||||
|
periphs
|
||||||
|
.porta
|
||||||
|
.irq_enb()
|
||||||
|
.modify(|r, w| unsafe { w.bits(r.bits() & !(1 << self.pin_id.num)) });
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
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 = pin_id_to_offset(self.pin_id);
|
||||||
|
WAKERS[idx].register(cx.waker());
|
||||||
|
if EDGE_DETECTION[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_asynch_gpio] function must be called inside that function for
|
||||||
|
/// the asynchronous functionality to work.
|
||||||
|
pub fn new(pin: DynPin, irq: pac::Interrupt) -> Result<Self, InvalidPinTypeError> {
|
||||||
|
if !pin.is_input_pin() {
|
||||||
|
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) {
|
||||||
|
let fut = unsafe {
|
||||||
|
// Unwrap okay, checked pin in constructor.
|
||||||
|
InputPinFuture::new_unchecked_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) {
|
||||||
|
let fut = unsafe {
|
||||||
|
// Unwrap okay, checked pin in constructor.
|
||||||
|
InputPinFuture::new_unchecked_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) {
|
||||||
|
unsafe {
|
||||||
|
// Unwrap okay, checked pin in constructor.
|
||||||
|
InputPinFuture::new_unchecked_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) {
|
||||||
|
unsafe {
|
||||||
|
// Unwrap okay, checked pin in constructor.
|
||||||
|
InputPinFuture::new_unchecked_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) {
|
||||||
|
unsafe {
|
||||||
|
// Unwrap okay, checked pin in constructor.
|
||||||
|
InputPinFuture::new_unchecked_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_asynch_gpio] function must be called inside that function for
|
||||||
|
/// the asynchronous functionality to work.
|
||||||
|
pub fn new(pin: Pin<I, pin::Input<C>>, irq: pac::Interrupt) -> Self {
|
||||||
|
Self { pin, irq }
|
||||||
|
}
|
||||||
|
|
||||||
|
/// 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 = unsafe {
|
||||||
|
InputPinFuture::new_unchecked_with_pin(
|
||||||
|
&mut self.pin,
|
||||||
|
self.irq,
|
||||||
|
InterruptEdge::LowToHigh,
|
||||||
|
)
|
||||||
|
};
|
||||||
|
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) {
|
||||||
|
let fut = unsafe {
|
||||||
|
InputPinFuture::new_unchecked_with_pin(
|
||||||
|
&mut self.pin,
|
||||||
|
self.irq,
|
||||||
|
InterruptEdge::HighToLow,
|
||||||
|
)
|
||||||
|
};
|
||||||
|
if self.pin.is_low().unwrap() {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
fut.await;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Asynchronously wait until the pin sees falling edge.
|
||||||
|
pub async fn wait_for_falling_edge(&mut self) {
|
||||||
|
unsafe {
|
||||||
|
// Unwrap okay, checked pin in constructor.
|
||||||
|
InputPinFuture::new_unchecked_with_pin(
|
||||||
|
&mut self.pin,
|
||||||
|
self.irq,
|
||||||
|
InterruptEdge::HighToLow,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
.await;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Asynchronously wait until the pin sees rising edge.
|
||||||
|
pub async fn wait_for_rising_edge(&mut self) {
|
||||||
|
unsafe {
|
||||||
|
// Unwrap okay, checked pin in constructor.
|
||||||
|
InputPinFuture::new_unchecked_with_pin(
|
||||||
|
&mut self.pin,
|
||||||
|
self.irq,
|
||||||
|
InterruptEdge::LowToHigh,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
.await;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Asynchronously wait until the pin sees any edge (either rising or falling).
|
||||||
|
pub async fn wait_for_any_edge(&mut self) {
|
||||||
|
unsafe {
|
||||||
|
InputPinFuture::new_unchecked_with_pin(
|
||||||
|
&mut self.pin,
|
||||||
|
self.irq,
|
||||||
|
InterruptEdge::BothEdges,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
.await;
|
||||||
|
}
|
||||||
|
|
||||||
|
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(())
|
||||||
|
}
|
||||||
|
}
|
@ -59,8 +59,9 @@
|
|||||||
use super::{
|
use super::{
|
||||||
pin::{FilterType, InterruptEdge, InterruptLevel, Pin, PinId, PinMode, PinState},
|
pin::{FilterType, InterruptEdge, InterruptLevel, Pin, PinId, PinMode, PinState},
|
||||||
reg::RegisterInterface,
|
reg::RegisterInterface,
|
||||||
|
InputDynPinAsync,
|
||||||
};
|
};
|
||||||
use crate::{clock::FilterClkSel, pac, FunSel, IrqCfg};
|
use crate::{clock::FilterClkSel, enable_nvic_interrupt, pac, FunSel, InterruptConfig};
|
||||||
|
|
||||||
//==================================================================================================
|
//==================================================================================================
|
||||||
// DynPinMode configurations
|
// DynPinMode configurations
|
||||||
@ -68,6 +69,7 @@ use crate::{clock::FilterClkSel, pac, FunSel, IrqCfg};
|
|||||||
|
|
||||||
/// 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,
|
||||||
@ -75,7 +77,8 @@ pub enum DynDisabled {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// Value-level `enum` for input configurations
|
/// Value-level `enum` for input configurations
|
||||||
#[derive(PartialEq, Eq, Clone, Copy)]
|
#[derive(Debug, PartialEq, Eq, Clone, Copy)]
|
||||||
|
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
|
||||||
pub enum DynInput {
|
pub enum DynInput {
|
||||||
Floating,
|
Floating,
|
||||||
PullDown,
|
PullDown,
|
||||||
@ -83,7 +86,8 @@ pub enum DynInput {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// Value-level `enum` for output configurations
|
/// Value-level `enum` for output configurations
|
||||||
#[derive(PartialEq, Eq, Clone, Copy)]
|
#[derive(Debug, PartialEq, Eq, Clone, Copy)]
|
||||||
|
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
|
||||||
pub enum DynOutput {
|
pub enum DynOutput {
|
||||||
PushPull,
|
PushPull,
|
||||||
OpenDrain,
|
OpenDrain,
|
||||||
@ -101,9 +105,10 @@ 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)]
|
#[derive(Debug, PartialEq, Eq, thiserror::Error)]
|
||||||
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
|
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
|
||||||
pub struct InvalidPinTypeError;
|
#[error("Invalid pin type for operation: {0:?}")]
|
||||||
|
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 {
|
||||||
@ -116,7 +121,8 @@ impl embedded_hal::digital::Error for InvalidPinTypeError {
|
|||||||
//==================================================================================================
|
//==================================================================================================
|
||||||
|
|
||||||
/// Value-level `enum` representing pin modes
|
/// Value-level `enum` representing pin modes
|
||||||
#[derive(PartialEq, Eq, Clone, Copy)]
|
#[derive(Debug, 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),
|
||||||
@ -151,14 +157,16 @@ pub const DYN_ALT_FUNC_3: DynPinMode = DynPinMode::Alternate(DynAlternate::Sel3)
|
|||||||
//==================================================================================================
|
//==================================================================================================
|
||||||
|
|
||||||
/// Value-level `enum` for pin groups
|
/// Value-level `enum` for pin groups
|
||||||
#[derive(PartialEq, Eq, Clone, Copy)]
|
#[derive(Debug, PartialEq, Eq, Clone, Copy)]
|
||||||
|
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
|
||||||
pub enum DynGroup {
|
pub enum DynGroup {
|
||||||
A,
|
A,
|
||||||
B,
|
B,
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Value-level `struct` representing pin IDs
|
/// Value-level `struct` representing pin IDs
|
||||||
#[derive(PartialEq, Eq, Clone, Copy)]
|
#[derive(Debug, PartialEq, Eq, Clone, Copy)]
|
||||||
|
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
|
||||||
pub struct DynPinId {
|
pub struct DynPinId {
|
||||||
pub group: DynGroup,
|
pub group: DynGroup,
|
||||||
pub num: u8,
|
pub num: u8,
|
||||||
@ -172,16 +180,15 @@ pub struct DynPinId {
|
|||||||
///
|
///
|
||||||
/// This `struct` takes ownership of a [`DynPinId`] and provides an API to
|
/// This `struct` takes ownership of a [`DynPinId`] and provides an API to
|
||||||
/// access the corresponding regsiters.
|
/// access the corresponding regsiters.
|
||||||
struct DynRegisters {
|
#[derive(Debug)]
|
||||||
id: DynPinId,
|
pub(crate) struct DynRegisters(DynPinId);
|
||||||
}
|
|
||||||
|
|
||||||
// [`DynRegisters`] takes ownership of the [`DynPinId`], and [`DynPin`]
|
// [`DynRegisters`] takes ownership of the [`DynPinId`], and [`DynPin`]
|
||||||
// guarantees that each pin is a singleton, so this implementation is safe.
|
// guarantees that each pin is a singleton, so this implementation is safe.
|
||||||
unsafe impl RegisterInterface for DynRegisters {
|
unsafe impl RegisterInterface for DynRegisters {
|
||||||
#[inline]
|
#[inline]
|
||||||
fn id(&self) -> DynPinId {
|
fn id(&self) -> DynPinId {
|
||||||
self.id
|
self.0
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -194,7 +201,7 @@ impl DynRegisters {
|
|||||||
/// the same [`DynPinId`]
|
/// the same [`DynPinId`]
|
||||||
#[inline]
|
#[inline]
|
||||||
unsafe fn new(id: DynPinId) -> Self {
|
unsafe fn new(id: DynPinId) -> Self {
|
||||||
DynRegisters { id }
|
DynRegisters(id)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -206,8 +213,9 @@ impl DynRegisters {
|
|||||||
///
|
///
|
||||||
/// 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 {
|
||||||
regs: DynRegisters,
|
pub(crate) regs: DynRegisters,
|
||||||
mode: DynPinMode,
|
mode: DynPinMode,
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -220,7 +228,7 @@ impl DynPin {
|
|||||||
/// 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]
|
||||||
unsafe fn new(id: DynPinId, mode: DynPinMode) -> Self {
|
pub(crate) unsafe fn new(id: DynPinId, mode: DynPinMode) -> Self {
|
||||||
DynPin {
|
DynPin {
|
||||||
regs: DynRegisters::new(id),
|
regs: DynRegisters::new(id),
|
||||||
mode,
|
mode,
|
||||||
@ -230,7 +238,7 @@ impl DynPin {
|
|||||||
/// Return a copy of the pin ID
|
/// Return a copy of the pin ID
|
||||||
#[inline]
|
#[inline]
|
||||||
pub fn id(&self) -> DynPinId {
|
pub fn id(&self) -> DynPinId {
|
||||||
self.regs.id
|
self.regs.0
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Return a copy of the pin mode
|
/// Return a copy of the pin mode
|
||||||
@ -249,6 +257,11 @@ impl DynPin {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
pub fn is_input_pin(&self) -> bool {
|
||||||
|
matches!(self.mode, DynPinMode::Input(_))
|
||||||
|
}
|
||||||
|
|
||||||
#[inline]
|
#[inline]
|
||||||
pub fn into_funsel_1(&mut self) {
|
pub fn into_funsel_1(&mut self) {
|
||||||
self.into_mode(DYN_ALT_FUNC_1);
|
self.into_mode(DYN_ALT_FUNC_1);
|
||||||
@ -306,7 +319,72 @@ impl DynPin {
|
|||||||
self.into_mode(DYN_RD_OPEN_DRAIN_OUTPUT);
|
self.into_mode(DYN_RD_OPEN_DRAIN_OUTPUT);
|
||||||
}
|
}
|
||||||
|
|
||||||
common_reg_if_functions!();
|
#[inline]
|
||||||
|
pub fn datamask(&self) -> bool {
|
||||||
|
self.regs.datamask()
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
pub fn clear_datamask(&mut self) {
|
||||||
|
self.regs.clear_datamask();
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
pub fn set_datamask(&mut self) {
|
||||||
|
self.regs.set_datamask();
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
pub fn is_high_masked(&self) -> Result<bool, crate::gpio::IsMaskedError> {
|
||||||
|
self.regs.read_pin_masked()
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
pub fn is_low_masked(&self) -> Result<bool, crate::gpio::IsMaskedError> {
|
||||||
|
self.regs.read_pin_masked().map(|v| !v)
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
pub fn set_high_masked(&mut self) -> Result<(), crate::gpio::IsMaskedError> {
|
||||||
|
self.regs.write_pin_masked(true)
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
pub fn set_low_masked(&mut self) -> Result<(), crate::gpio::IsMaskedError> {
|
||||||
|
self.regs.write_pin_masked(false)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub(crate) fn irq_enb(
|
||||||
|
&mut self,
|
||||||
|
irq_cfg: crate::InterruptConfig,
|
||||||
|
syscfg: Option<&mut va108xx::Sysconfig>,
|
||||||
|
irqsel: Option<&mut va108xx::Irqsel>,
|
||||||
|
) {
|
||||||
|
if let Some(syscfg) = syscfg {
|
||||||
|
crate::clock::enable_peripheral_clock(syscfg, crate::clock::PeripheralClocks::Irqsel);
|
||||||
|
}
|
||||||
|
self.regs.enable_irq();
|
||||||
|
if let Some(irqsel) = irqsel {
|
||||||
|
if irq_cfg.route {
|
||||||
|
match self.regs.id().group {
|
||||||
|
// Set the correct interrupt number in the IRQSEL register
|
||||||
|
DynGroup::A => {
|
||||||
|
irqsel
|
||||||
|
.porta0(self.regs.id().num as usize)
|
||||||
|
.write(|w| unsafe { w.bits(irq_cfg.id as u32) });
|
||||||
|
}
|
||||||
|
DynGroup::B => {
|
||||||
|
irqsel
|
||||||
|
.portb0(self.regs.id().num as usize)
|
||||||
|
.write(|w| unsafe { w.bits(irq_cfg.id as u32) });
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if irq_cfg.enable_in_nvic {
|
||||||
|
unsafe { enable_nvic_interrupt(irq_cfg.id) };
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// 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:
|
||||||
@ -320,74 +398,77 @@ impl DynPin {
|
|||||||
self.regs.delay(delay_1, delay_2);
|
self.regs.delay(delay_1, delay_2);
|
||||||
Ok(self)
|
Ok(self)
|
||||||
}
|
}
|
||||||
_ => Err(InvalidPinTypeError),
|
_ => Err(InvalidPinTypeError(self.mode)),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// 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
|
||||||
|
#[inline]
|
||||||
pub fn pulse_mode(
|
pub fn pulse_mode(
|
||||||
self,
|
&mut self,
|
||||||
enable: bool,
|
enable: bool,
|
||||||
default_state: PinState,
|
default_state: PinState,
|
||||||
) -> Result<Self, InvalidPinTypeError> {
|
) -> Result<(), InvalidPinTypeError> {
|
||||||
match self.mode {
|
match self.mode {
|
||||||
DynPinMode::Output(_) => {
|
DynPinMode::Output(_) => {
|
||||||
self.regs.pulse_mode(enable, default_state);
|
self.regs.pulse_mode(enable, default_state);
|
||||||
Ok(self)
|
Ok(())
|
||||||
}
|
}
|
||||||
_ => Err(InvalidPinTypeError),
|
_ => Err(InvalidPinTypeError(self.mode)),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// 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 filter_type(
|
pub fn filter_type(
|
||||||
self,
|
&mut self,
|
||||||
filter: FilterType,
|
filter: FilterType,
|
||||||
clksel: FilterClkSel,
|
clksel: FilterClkSel,
|
||||||
) -> Result<Self, InvalidPinTypeError> {
|
) -> Result<(), InvalidPinTypeError> {
|
||||||
match self.mode {
|
match self.mode {
|
||||||
DynPinMode::Input(_) => {
|
DynPinMode::Input(_) => {
|
||||||
self.regs.filter_type(filter, clksel);
|
self.regs.filter_type(filter, clksel);
|
||||||
Ok(self)
|
Ok(())
|
||||||
}
|
}
|
||||||
_ => Err(InvalidPinTypeError),
|
_ => Err(InvalidPinTypeError(self.mode)),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[inline]
|
||||||
pub fn interrupt_edge(
|
pub fn interrupt_edge(
|
||||||
mut self,
|
&mut self,
|
||||||
edge_type: InterruptEdge,
|
edge_type: InterruptEdge,
|
||||||
irq_cfg: IrqCfg,
|
irq_cfg: InterruptConfig,
|
||||||
syscfg: Option<&mut pac::Sysconfig>,
|
syscfg: Option<&mut pac::Sysconfig>,
|
||||||
irqsel: Option<&mut pac::Irqsel>,
|
irqsel: Option<&mut pac::Irqsel>,
|
||||||
) -> Result<Self, InvalidPinTypeError> {
|
) -> Result<(), InvalidPinTypeError> {
|
||||||
match self.mode {
|
match self.mode {
|
||||||
DynPinMode::Input(_) | DynPinMode::Output(_) => {
|
DynPinMode::Input(_) | DynPinMode::Output(_) => {
|
||||||
self.regs.interrupt_edge(edge_type);
|
self.regs.interrupt_edge(edge_type);
|
||||||
self.irq_enb(irq_cfg, syscfg, irqsel);
|
self.irq_enb(irq_cfg, syscfg, irqsel);
|
||||||
Ok(self)
|
Ok(())
|
||||||
}
|
}
|
||||||
_ => Err(InvalidPinTypeError),
|
_ => Err(InvalidPinTypeError(self.mode)),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[inline]
|
||||||
pub fn interrupt_level(
|
pub fn interrupt_level(
|
||||||
mut self,
|
&mut self,
|
||||||
level_type: InterruptLevel,
|
level_type: InterruptLevel,
|
||||||
irq_cfg: IrqCfg,
|
irq_cfg: InterruptConfig,
|
||||||
syscfg: Option<&mut pac::Sysconfig>,
|
syscfg: Option<&mut pac::Sysconfig>,
|
||||||
irqsel: Option<&mut pac::Irqsel>,
|
irqsel: Option<&mut pac::Irqsel>,
|
||||||
) -> Result<Self, InvalidPinTypeError> {
|
) -> Result<(), InvalidPinTypeError> {
|
||||||
match self.mode {
|
match self.mode {
|
||||||
DynPinMode::Input(_) | DynPinMode::Output(_) => {
|
DynPinMode::Input(_) | DynPinMode::Output(_) => {
|
||||||
self.regs.interrupt_level(level_type);
|
self.regs.interrupt_level(level_type);
|
||||||
self.irq_enb(irq_cfg, syscfg, irqsel);
|
self.irq_enb(irq_cfg, syscfg, irqsel);
|
||||||
Ok(self)
|
Ok(())
|
||||||
}
|
}
|
||||||
_ => Err(InvalidPinTypeError),
|
_ => Err(InvalidPinTypeError(self.mode)),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -398,7 +479,7 @@ impl DynPin {
|
|||||||
self.regs.toggle();
|
self.regs.toggle();
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
_ => Err(InvalidPinTypeError),
|
_ => Err(InvalidPinTypeError(self.mode)),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -408,7 +489,7 @@ impl DynPin {
|
|||||||
DynPinMode::Input(_) | DYN_RD_OPEN_DRAIN_OUTPUT | DYN_RD_PUSH_PULL_OUTPUT => {
|
DynPinMode::Input(_) | DYN_RD_OPEN_DRAIN_OUTPUT | DYN_RD_PUSH_PULL_OUTPUT => {
|
||||||
Ok(self.regs.read_pin())
|
Ok(self.regs.read_pin())
|
||||||
}
|
}
|
||||||
_ => Err(InvalidPinTypeError),
|
_ => Err(InvalidPinTypeError(self.mode)),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
#[inline]
|
#[inline]
|
||||||
@ -418,7 +499,7 @@ impl DynPin {
|
|||||||
self.regs.write_pin(bit);
|
self.regs.write_pin(bit);
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
_ => Err(InvalidPinTypeError),
|
_ => Err(InvalidPinTypeError(self.mode)),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -438,6 +519,30 @@ impl DynPin {
|
|||||||
fn _set_high(&mut self) -> Result<(), InvalidPinTypeError> {
|
fn _set_high(&mut self) -> Result<(), InvalidPinTypeError> {
|
||||||
self._write(true)
|
self._write(true)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// 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.regs.0 == 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)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
//==================================================================================================
|
//==================================================================================================
|
||||||
@ -448,10 +553,8 @@ 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 {
|
||||||
// The `Pin` is consumed, so it is safe to replace it with the
|
pin.downgrade()
|
||||||
// corresponding `DynPin`
|
|
||||||
unsafe { DynPin::new(I::DYN, M::DYN) }
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -465,13 +568,7 @@ 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> {
|
||||||
if pin.regs.id == I::DYN && pin.mode == M::DYN {
|
pin.upgrade()
|
||||||
// The `DynPin` is consumed, so it is safe to replace it with the
|
|
||||||
// corresponding `Pin`
|
|
||||||
Ok(unsafe { Self::new() })
|
|
||||||
} else {
|
|
||||||
Err(InvalidPinTypeError)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -506,10 +603,12 @@ impl embedded_hal::digital::InputPin for DynPin {
|
|||||||
}
|
}
|
||||||
|
|
||||||
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()
|
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()
|
self._is_low()
|
||||||
}
|
}
|
||||||
|
@ -22,84 +22,14 @@
|
|||||||
//!
|
//!
|
||||||
//! - [Blinky example](https://egit.irs.uni-stuttgart.de/rust/va108xx-rs/src/branch/main/examples/simple/examples/blinky.rs)
|
//! - [Blinky example](https://egit.irs.uni-stuttgart.de/rust/va108xx-rs/src/branch/main/examples/simple/examples/blinky.rs)
|
||||||
|
|
||||||
#[derive(Debug, PartialEq, Eq)]
|
#[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;
|
||||||
|
|
||||||
macro_rules! common_reg_if_functions {
|
pub const NUM_PINS_PORT_A: usize = 32;
|
||||||
() => {
|
pub const NUM_PINS_PORT_B: usize = 24;
|
||||||
paste::paste!(
|
pub const NUM_GPIO_PINS: usize = NUM_PINS_PORT_A + NUM_PINS_PORT_B;
|
||||||
#[inline]
|
|
||||||
pub fn datamask(&self) -> bool {
|
|
||||||
self.regs.datamask()
|
|
||||||
}
|
|
||||||
|
|
||||||
#[inline]
|
|
||||||
pub fn clear_datamask(self) -> Self {
|
|
||||||
self.regs.clear_datamask();
|
|
||||||
self
|
|
||||||
}
|
|
||||||
|
|
||||||
#[inline]
|
|
||||||
pub fn set_datamask(self) -> Self {
|
|
||||||
self.regs.set_datamask();
|
|
||||||
self
|
|
||||||
}
|
|
||||||
|
|
||||||
#[inline]
|
|
||||||
pub fn is_high_masked(&self) -> Result<bool, crate::gpio::IsMaskedError> {
|
|
||||||
self.regs.read_pin_masked()
|
|
||||||
}
|
|
||||||
|
|
||||||
#[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;
|
||||||
pub use dynpin::*;
|
pub use dynpin::*;
|
||||||
@ -107,4 +37,7 @@ pub use dynpin::*;
|
|||||||
pub mod pin;
|
pub mod pin;
|
||||||
pub use pin::*;
|
pub use pin::*;
|
||||||
|
|
||||||
|
pub mod asynch;
|
||||||
|
pub use asynch::*;
|
||||||
|
|
||||||
mod reg;
|
mod reg;
|
||||||
|
@ -72,10 +72,11 @@
|
|||||||
//! and [`StatefulOutputPin`].
|
//! and [`StatefulOutputPin`].
|
||||||
use super::dynpin::{DynAlternate, DynGroup, DynInput, DynOutput, DynPinId, DynPinMode};
|
use super::dynpin::{DynAlternate, DynGroup, DynInput, DynOutput, DynPinId, DynPinMode};
|
||||||
use super::reg::RegisterInterface;
|
use super::reg::RegisterInterface;
|
||||||
|
use super::{DynPin, InputPinAsync};
|
||||||
use crate::{
|
use crate::{
|
||||||
pac::{Irqsel, Porta, Portb, Sysconfig},
|
pac::{Irqsel, Porta, Portb, Sysconfig},
|
||||||
typelevel::Sealed,
|
typelevel::Sealed,
|
||||||
IrqCfg,
|
InterruptConfig,
|
||||||
};
|
};
|
||||||
use core::convert::Infallible;
|
use core::convert::Infallible;
|
||||||
use core::marker::PhantomData;
|
use core::marker::PhantomData;
|
||||||
@ -118,8 +119,11 @@ 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 {
|
||||||
@ -147,6 +151,7 @@ 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>,
|
||||||
}
|
}
|
||||||
@ -176,13 +181,17 @@ 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 {}
|
||||||
@ -209,6 +218,7 @@ 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>,
|
||||||
}
|
}
|
||||||
@ -303,6 +313,7 @@ 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 {
|
||||||
@ -320,10 +331,10 @@ 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> {
|
||||||
pub(in crate::gpio) regs: Registers<I>,
|
inner: DynPin,
|
||||||
mode: PhantomData<M>,
|
phantom: PhantomData<(I, M)>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<I: PinId, M: PinMode> Pin<I, M> {
|
impl<I: PinId, M: PinMode> Pin<I, M> {
|
||||||
@ -337,18 +348,22 @@ impl<I: PinId, M: PinMode> Pin<I, M> {
|
|||||||
#[inline]
|
#[inline]
|
||||||
pub(crate) unsafe fn new() -> Pin<I, M> {
|
pub(crate) unsafe fn new() -> Pin<I, M> {
|
||||||
Pin {
|
Pin {
|
||||||
regs: Registers::new(),
|
inner: DynPin::new(I::DYN, M::DYN),
|
||||||
mode: PhantomData,
|
phantom: PhantomData,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub 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.regs.change_mode::<N>();
|
self.inner.regs.change_mode(N::DYN);
|
||||||
}
|
}
|
||||||
// Safe because we drop the existing Pin
|
// Safe because we drop the existing Pin
|
||||||
unsafe { Pin::new() }
|
unsafe { Pin::new() }
|
||||||
@ -408,31 +423,78 @@ impl<I: PinId, M: PinMode> Pin<I, M> {
|
|||||||
self.into_mode()
|
self.into_mode()
|
||||||
}
|
}
|
||||||
|
|
||||||
common_reg_if_functions!();
|
#[inline]
|
||||||
|
pub fn datamask(&self) -> bool {
|
||||||
|
self.inner.datamask()
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
pub fn clear_datamask(&mut self) {
|
||||||
|
self.inner.clear_datamask()
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
pub fn set_datamask(&mut self) {
|
||||||
|
self.inner.set_datamask()
|
||||||
|
}
|
||||||
|
|
||||||
|
#[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 set_high_masked(&mut self) -> Result<(), crate::gpio::IsMaskedError> {
|
||||||
|
self.inner.set_high_masked()
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
pub fn set_low_masked(&mut self) -> Result<(), crate::gpio::IsMaskedError> {
|
||||||
|
self.inner.set_low_masked()
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
pub fn downgrade(self) -> DynPin {
|
||||||
|
self.inner
|
||||||
|
}
|
||||||
|
|
||||||
|
fn irq_enb(
|
||||||
|
&mut self,
|
||||||
|
irq_cfg: crate::InterruptConfig,
|
||||||
|
syscfg: Option<&mut va108xx::Sysconfig>,
|
||||||
|
irqsel: Option<&mut va108xx::Irqsel>,
|
||||||
|
) {
|
||||||
|
self.inner.irq_enb(irq_cfg, syscfg, irqsel);
|
||||||
|
}
|
||||||
|
|
||||||
#[inline]
|
#[inline]
|
||||||
pub(crate) fn _set_high(&mut self) {
|
pub(crate) fn _set_high(&mut self) {
|
||||||
self.regs.write_pin(true)
|
self.inner.regs.write_pin(true)
|
||||||
}
|
}
|
||||||
|
|
||||||
#[inline]
|
#[inline]
|
||||||
pub(crate) fn _set_low(&mut self) {
|
pub(crate) fn _set_low(&mut self) {
|
||||||
self.regs.write_pin(false)
|
self.inner.regs.write_pin(false)
|
||||||
}
|
}
|
||||||
|
|
||||||
#[inline]
|
#[inline]
|
||||||
pub(crate) fn _toggle_with_toggle_reg(&mut self) {
|
pub(crate) fn _toggle_with_toggle_reg(&mut self) {
|
||||||
self.regs.toggle();
|
self.inner.regs.toggle();
|
||||||
}
|
}
|
||||||
|
|
||||||
#[inline]
|
#[inline]
|
||||||
pub(crate) fn _is_low(&self) -> bool {
|
pub(crate) fn _is_low(&self) -> bool {
|
||||||
!self.regs.read_pin()
|
!self.inner.regs.read_pin()
|
||||||
}
|
}
|
||||||
|
|
||||||
#[inline]
|
#[inline]
|
||||||
pub(crate) fn _is_high(&self) -> bool {
|
pub(crate) fn _is_high(&self) -> bool {
|
||||||
self.regs.read_pin()
|
self.inner.regs.read_pin()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -524,28 +586,32 @@ 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>> {
|
||||||
pub fn interrupt_edge(
|
/// Convert the pin into an async pin. The pin can be converted back by calling
|
||||||
mut self,
|
/// [InputPinAsync::release]
|
||||||
edge_type: InterruptEdge,
|
pub fn into_async_input(self, irq: crate::pac::Interrupt) -> InputPinAsync<I, C> {
|
||||||
irq_cfg: IrqCfg,
|
InputPinAsync::new(self, irq)
|
||||||
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(
|
pub fn configure_edge_interrupt(
|
||||||
mut self,
|
&mut self,
|
||||||
level_type: InterruptLevel,
|
edge_type: InterruptEdge,
|
||||||
irq_cfg: IrqCfg,
|
irq_cfg: InterruptConfig,
|
||||||
syscfg: Option<&mut Sysconfig>,
|
syscfg: Option<&mut Sysconfig>,
|
||||||
irqsel: Option<&mut Irqsel>,
|
irqsel: Option<&mut Irqsel>,
|
||||||
) -> Self {
|
) {
|
||||||
self.regs.interrupt_level(level_type);
|
self.inner.regs.interrupt_edge(edge_type);
|
||||||
|
self.irq_enb(irq_cfg, syscfg, irqsel);
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn configure_level_interrupt(
|
||||||
|
&mut self,
|
||||||
|
level_type: InterruptLevel,
|
||||||
|
irq_cfg: InterruptConfig,
|
||||||
|
syscfg: Option<&mut Sysconfig>,
|
||||||
|
irqsel: Option<&mut Irqsel>,
|
||||||
|
) {
|
||||||
|
self.inner.regs.interrupt_level(level_type);
|
||||||
self.irq_enb(irq_cfg, syscfg, irqsel);
|
self.irq_enb(irq_cfg, syscfg, irqsel);
|
||||||
self
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -557,7 +623,7 @@ impl<I: PinId, C: OutputConfig> Pin<I, Output<C>> {
|
|||||||
/// - Delay 1 + Delay 2: 3
|
/// - Delay 1 + Delay 2: 3
|
||||||
#[inline]
|
#[inline]
|
||||||
pub fn delay(self, delay_1: bool, delay_2: bool) -> Self {
|
pub fn delay(self, delay_1: bool, delay_2: bool) -> Self {
|
||||||
self.regs.delay(delay_1, delay_2);
|
self.inner.regs.delay(delay_1, delay_2);
|
||||||
self
|
self
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -569,42 +635,38 @@ impl<I: PinId, C: OutputConfig> Pin<I, Output<C>> {
|
|||||||
/// 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 pulse_mode(self, enable: bool, default_state: PinState) -> Self {
|
pub fn pulse_mode(&mut self, enable: bool, default_state: PinState) {
|
||||||
self.regs.pulse_mode(enable, default_state);
|
self.inner.regs.pulse_mode(enable, default_state);
|
||||||
self
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn interrupt_edge(
|
pub fn interrupt_edge(
|
||||||
mut self,
|
&mut self,
|
||||||
edge_type: InterruptEdge,
|
edge_type: InterruptEdge,
|
||||||
irq_cfg: IrqCfg,
|
irq_cfg: InterruptConfig,
|
||||||
syscfg: Option<&mut Sysconfig>,
|
syscfg: Option<&mut Sysconfig>,
|
||||||
irqsel: Option<&mut Irqsel>,
|
irqsel: Option<&mut Irqsel>,
|
||||||
) -> Self {
|
) {
|
||||||
self.regs.interrupt_edge(edge_type);
|
self.inner.regs.interrupt_edge(edge_type);
|
||||||
self.irq_enb(irq_cfg, syscfg, irqsel);
|
self.irq_enb(irq_cfg, syscfg, irqsel);
|
||||||
self
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn interrupt_level(
|
pub fn interrupt_level(
|
||||||
mut self,
|
&mut self,
|
||||||
level_type: InterruptLevel,
|
level_type: InterruptLevel,
|
||||||
irq_cfg: IrqCfg,
|
irq_cfg: InterruptConfig,
|
||||||
syscfg: Option<&mut Sysconfig>,
|
syscfg: Option<&mut Sysconfig>,
|
||||||
irqsel: Option<&mut Irqsel>,
|
irqsel: Option<&mut Irqsel>,
|
||||||
) -> Self {
|
) {
|
||||||
self.regs.interrupt_level(level_type);
|
self.inner.regs.interrupt_level(level_type);
|
||||||
self.irq_enb(irq_cfg, syscfg, irqsel);
|
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 filter_type(self, filter: FilterType, clksel: FilterClkSel) -> Self {
|
pub fn configure_filter_type(&mut self, filter: FilterType, clksel: FilterClkSel) {
|
||||||
self.regs.filter_type(filter, clksel);
|
self.inner.regs.filter_type(filter, clksel);
|
||||||
self
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -680,47 +742,6 @@ where
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
//==================================================================================================
|
|
||||||
// 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);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
//==================================================================================================
|
//==================================================================================================
|
||||||
// Pin definitions
|
// Pin definitions
|
||||||
//==================================================================================================
|
//==================================================================================================
|
||||||
@ -731,8 +752,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]
|
||||||
@ -747,7 +768,6 @@ 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| {
|
||||||
@ -756,7 +776,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`
|
||||||
$(
|
$(
|
||||||
@ -773,8 +793,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) -> (Option<va108xx::Ioconfig>, $Port) {
|
pub fn release(self) -> $Port {
|
||||||
(self.iocfg, self.port)
|
self.port
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
|
@ -73,13 +73,6 @@ impl From<DynPinMode> for ModeFields {
|
|||||||
//==================================================================================================
|
//==================================================================================================
|
||||||
|
|
||||||
pub type PortReg = ioconfig::Porta;
|
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
|
/// Provide a safe register interface for pin objects
|
||||||
///
|
///
|
||||||
@ -293,7 +286,7 @@ pub(super) unsafe trait RegisterInterface {
|
|||||||
|
|
||||||
/// Only useful for input pins
|
/// Only useful for input pins
|
||||||
#[inline]
|
#[inline]
|
||||||
fn filter_type(&self, filter: FilterType, clksel: FilterClkSel) {
|
fn filter_type(&mut self, filter: FilterType, clksel: FilterClkSel) {
|
||||||
self.iocfg_port().modify(|_, w| {
|
self.iocfg_port().modify(|_, w| {
|
||||||
// Safety: Only write to register for this Pin ID
|
// Safety: Only write to register for this Pin ID
|
||||||
unsafe {
|
unsafe {
|
||||||
@ -311,7 +304,7 @@ pub(super) unsafe trait RegisterInterface {
|
|||||||
unsafe {
|
unsafe {
|
||||||
portreg
|
portreg
|
||||||
.datamask()
|
.datamask()
|
||||||
.modify(|r, w| w.bits(r.bits() | self.mask_32()))
|
.modify(|r, w| w.bits(r.bits() | self.mask_32()));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -323,7 +316,7 @@ pub(super) unsafe trait RegisterInterface {
|
|||||||
unsafe {
|
unsafe {
|
||||||
portreg
|
portreg
|
||||||
.datamask()
|
.datamask()
|
||||||
.modify(|r, w| w.bits(r.bits() & !self.mask_32()))
|
.modify(|r, w| w.bits(r.bits() & !self.mask_32()));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -331,7 +324,7 @@ pub(super) unsafe trait RegisterInterface {
|
|||||||
/// 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
|
||||||
fn pulse_mode(&self, enable: bool, default_state: PinState) {
|
fn pulse_mode(&mut self, enable: bool, default_state: PinState) {
|
||||||
let portreg = self.port_reg();
|
let portreg = self.port_reg();
|
||||||
unsafe {
|
unsafe {
|
||||||
if enable {
|
if enable {
|
||||||
|
@ -2,7 +2,7 @@
|
|||||||
//!
|
//!
|
||||||
//! ## Examples
|
//! ## Examples
|
||||||
//!
|
//!
|
||||||
//! - [REB1 I2C temperature sensor example](https://egit.irs.uni-stuttgart.de/rust/va108xx-rs/src/branch/main/vorago-reb1/examples/adt75-temp-sensor.rs
|
//! - [REB1 I2C temperature sensor example](https://egit.irs.uni-stuttgart.de/rust/va108xx-rs/src/branch/main/vorago-reb1/examples/adt75-temp-sensor.rs)
|
||||||
use crate::{
|
use crate::{
|
||||||
clock::enable_peripheral_clock, pac, time::Hertz, typelevel::Sealed, PeripheralSelect,
|
clock::enable_peripheral_clock, pac, time::Hertz, typelevel::Sealed, PeripheralSelect,
|
||||||
};
|
};
|
||||||
@ -18,42 +18,50 @@ 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)]
|
#[derive(Debug, PartialEq, Eq, thiserror::Error)]
|
||||||
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
|
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
|
||||||
pub struct ClockTooSlowForFastI2c;
|
#[error("clock too slow for fast I2C mode")]
|
||||||
|
pub struct ClockTooSlowForFastI2cError;
|
||||||
|
|
||||||
#[derive(Debug, PartialEq, Eq)]
|
#[derive(Debug, PartialEq, Eq, thiserror::Error)]
|
||||||
|
#[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 {
|
||||||
InvalidTimingParams,
|
//#[error("Invalid timing parameters")]
|
||||||
|
//InvalidTimingParams,
|
||||||
|
#[error("arbitration lost")]
|
||||||
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)]
|
#[derive(Debug, PartialEq, Eq, thiserror::Error)]
|
||||||
#[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.
|
||||||
ClkTooSlow(ClockTooSlowForFastI2c),
|
#[error("clock too slow for fast I2C mode: {0}")]
|
||||||
}
|
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 {
|
||||||
@ -66,7 +74,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::InvalidTimingParams => {
|
Error::DataTooLarge | Error::InsufficientDataReceived => {
|
||||||
embedded_hal::i2c::ErrorKind::Other
|
embedded_hal::i2c::ErrorKind::Other
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -82,18 +90,21 @@ 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),
|
||||||
@ -134,9 +145,12 @@ 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,
|
||||||
@ -160,7 +174,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, Error> {
|
) -> Result<Self, InvalidTimingParamsError> {
|
||||||
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
|
||||||
@ -170,7 +184,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(Error::InvalidTimingParams);
|
return Err(InvalidTimingParamsError);
|
||||||
}
|
}
|
||||||
Ok(TimingCfg {
|
Ok(TimingCfg {
|
||||||
tr: first_16_bits.0,
|
tr: first_16_bits.0,
|
||||||
@ -211,6 +225,7 @@ 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,
|
||||||
@ -299,7 +314,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, ClockTooSlowForFastI2c> {
|
) -> Result<Self, ClockTooSlowForFastI2cError> {
|
||||||
enable_peripheral_clock(syscfg, I2c::PERIPH_SEL);
|
enable_peripheral_clock(syscfg, I2c::PERIPH_SEL);
|
||||||
|
|
||||||
let mut i2c_base = I2cBase {
|
let mut i2c_base = I2cBase {
|
||||||
@ -377,12 +392,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) });
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -402,19 +417,22 @@ impl<I2c: Instance> I2cBase<I2c> {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
fn calc_clk_div(&self, speed_mode: I2cSpeed) -> Result<u8, ClockTooSlowForFastI2c> {
|
fn calc_clk_div(&self, speed_mode: I2cSpeed) -> Result<u8, ClockTooSlowForFastI2cError> {
|
||||||
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(ClockTooSlowForFastI2c);
|
return Err(ClockTooSlowForFastI2cError);
|
||||||
}
|
}
|
||||||
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(&mut self, speed_mode: I2cSpeed) -> Result<(), ClockTooSlowForFastI2c> {
|
pub fn cfg_clk_scale(
|
||||||
|
&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()
|
||||||
@ -460,7 +478,7 @@ impl<I2c: Instance, Addr> I2cMaster<I2c, Addr> {
|
|||||||
i2c: I2c,
|
i2c: I2c,
|
||||||
cfg: MasterConfig,
|
cfg: MasterConfig,
|
||||||
speed_mode: I2cSpeed,
|
speed_mode: I2cSpeed,
|
||||||
) -> Result<Self, ClockTooSlowForFastI2c> {
|
) -> Result<Self, ClockTooSlowForFastI2cError> {
|
||||||
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,
|
||||||
@ -990,7 +1008,7 @@ impl<I2c: Instance, Addr> I2cSlave<I2c, Addr> {
|
|||||||
i2c: I2c,
|
i2c: I2c,
|
||||||
cfg: SlaveConfig,
|
cfg: SlaveConfig,
|
||||||
speed_mode: I2cSpeed,
|
speed_mode: I2cSpeed,
|
||||||
) -> Result<Self, ClockTooSlowForFastI2c> {
|
) -> Result<Self, ClockTooSlowForFastI2cError> {
|
||||||
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,
|
||||||
@ -1152,7 +1170,7 @@ impl<I2c: Instance> I2cSlave<I2c, TenBitAddress> {
|
|||||||
i2c: I2c,
|
i2c: I2c,
|
||||||
cfg: SlaveConfig,
|
cfg: SlaveConfig,
|
||||||
speed_mode: I2cSpeed,
|
speed_mode: I2cSpeed,
|
||||||
) -> Result<Self, ClockTooSlowForFastI2c> {
|
) -> Result<Self, ClockTooSlowForFastI2cError> {
|
||||||
Self::new_generic(sys_cfg, sys_clk, i2c, cfg, speed_mode)
|
Self::new_generic(sys_cfg, sys_clk, i2c, cfg, speed_mode)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -15,9 +15,9 @@ pub mod time;
|
|||||||
pub mod timer;
|
pub mod timer;
|
||||||
pub mod typelevel;
|
pub mod typelevel;
|
||||||
pub mod uart;
|
pub mod uart;
|
||||||
pub mod utility;
|
|
||||||
|
|
||||||
#[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 {
|
||||||
Sel1 = 0b01,
|
Sel1 = 0b01,
|
||||||
Sel2 = 0b10,
|
Sel2 = 0b10,
|
||||||
@ -25,12 +25,14 @@ pub enum FunSel {
|
|||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Copy, Clone, PartialEq, Eq)]
|
#[derive(Debug, Copy, Clone, PartialEq, Eq)]
|
||||||
|
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
|
||||||
pub enum PortSel {
|
pub enum PortSel {
|
||||||
PortA,
|
PortA,
|
||||||
PortB,
|
PortB,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Copy, Clone, PartialEq, Eq)]
|
#[derive(Copy, Clone, PartialEq, Eq)]
|
||||||
|
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
|
||||||
pub enum PeripheralSelect {
|
pub enum PeripheralSelect {
|
||||||
PortA = 0,
|
PortA = 0,
|
||||||
PortB = 1,
|
PortB = 1,
|
||||||
@ -47,31 +49,39 @@ pub enum PeripheralSelect {
|
|||||||
Gpio = 24,
|
Gpio = 24,
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Generic IRQ config which can be used to specify whether the HAL driver will
|
/// Generic interrupt config which can be used to specify whether the HAL driver will
|
||||||
/// use the IRQSEL register to route an interrupt, and whether the IRQ will be unmasked in the
|
/// use the IRQSEL register to route an interrupt, and whether the IRQ will be unmasked in the
|
||||||
/// Cortex-M0 NVIC. Both are generally necessary for IRQs to work, but the user might perform
|
/// Cortex-M0 NVIC. Both are generally necessary for IRQs to work, but the user might want to
|
||||||
/// this steps themselves
|
/// perform those steps themselves.
|
||||||
#[derive(Debug, PartialEq, Eq, Clone, Copy)]
|
#[derive(Debug, PartialEq, Eq, Clone, Copy)]
|
||||||
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 irq: pac::Interrupt,
|
pub id: 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
|
/// Specify whether the IRQ is unmasked in the Cortex-M NVIC. If an interrupt is used for
|
||||||
pub enable: bool,
|
/// multiple purposes, the user can enable the interrupts themselves.
|
||||||
|
pub enable_in_nvic: bool,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl IrqCfg {
|
impl InterruptConfig {
|
||||||
pub fn new(irq: pac::Interrupt, route: bool, enable: bool) -> Self {
|
pub fn new(id: pac::Interrupt, route: bool, enable_in_nvic: bool) -> Self {
|
||||||
IrqCfg { irq, route, enable }
|
InterruptConfig {
|
||||||
|
id,
|
||||||
|
route,
|
||||||
|
enable_in_nvic,
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub type IrqCfg = InterruptConfig;
|
||||||
|
|
||||||
#[derive(Debug, PartialEq, Eq)]
|
#[derive(Debug, PartialEq, Eq)]
|
||||||
|
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
|
||||||
pub struct InvalidPin(pub(crate) ());
|
pub struct InvalidPin(pub(crate) ());
|
||||||
|
|
||||||
/// Can be used to manually manipulate the function select of port pins
|
/// Can be used to manually manipulate the function select of port pins
|
||||||
pub fn port_mux(
|
pub fn port_function_select(
|
||||||
ioconfig: &mut pac::Ioconfig,
|
ioconfig: &mut pac::Ioconfig,
|
||||||
port: PortSel,
|
port: PortSel,
|
||||||
pin: u8,
|
pin: u8,
|
||||||
@ -98,3 +108,21 @@ pub fn port_mux(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Enable a specific interrupt using the NVIC peripheral.
|
||||||
|
///
|
||||||
|
/// # Safety
|
||||||
|
///
|
||||||
|
/// This function is `unsafe` because it can break mask-based critical sections.
|
||||||
|
#[inline]
|
||||||
|
pub unsafe fn enable_nvic_interrupt(irq: pac::Interrupt) {
|
||||||
|
unsafe {
|
||||||
|
cortex_m::peripheral::NVIC::unmask(irq);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Disable a specific interrupt using the NVIC peripheral.
|
||||||
|
#[inline]
|
||||||
|
pub fn disable_nvic_interrupt(irq: pac::Interrupt) {
|
||||||
|
cortex_m::peripheral::NVIC::mask(irq);
|
||||||
|
}
|
||||||
|
@ -9,12 +9,15 @@ use core::convert::Infallible;
|
|||||||
use core::marker::PhantomData;
|
use core::marker::PhantomData;
|
||||||
|
|
||||||
use crate::pac;
|
use crate::pac;
|
||||||
|
use crate::time::Hertz;
|
||||||
|
use crate::timer::{TimDynRegister, TimPin, TimRegInterface, ValidTim, ValidTimAndPin};
|
||||||
use crate::{clock::enable_peripheral_clock, gpio::DynPinId};
|
use crate::{clock::enable_peripheral_clock, gpio::DynPinId};
|
||||||
pub use crate::{gpio::PinId, time::Hertz, timer::*};
|
|
||||||
|
|
||||||
const DUTY_MAX: u16 = u16::MAX;
|
const DUTY_MAX: u16 = u16::MAX;
|
||||||
|
|
||||||
pub struct PwmBase {
|
#[derive(Debug)]
|
||||||
|
#[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,
|
||||||
@ -32,123 +35,13 @@ 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> {
|
||||||
reg: TimAndPinRegister<Pin, Tim>,
|
pin_and_tim: (Pin, Tim),
|
||||||
pwm_base: PwmBase,
|
inner: ReducedPwmPin<Mode>,
|
||||||
mode: PhantomData<Mode>,
|
mode: PhantomData<Mode>,
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -160,34 +53,82 @@ 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,
|
||||||
tim_and_pin: (Pin, Tim),
|
pin_and_tim: (Pin, Tim),
|
||||||
initial_period: impl Into<Hertz> + Copy,
|
initial_period: impl Into<Hertz> + Copy,
|
||||||
) -> Self {
|
) -> Self {
|
||||||
let mut pin = PwmPin {
|
let mut pin = PwmPin {
|
||||||
pwm_base: PwmBase {
|
pin_and_tim,
|
||||||
current_duty: 0,
|
inner: ReducedPwmPin::<Mode>::new(
|
||||||
current_lower_limit: 0,
|
Tim::TIM_ID,
|
||||||
current_period: initial_period.into(),
|
Pin::DYN,
|
||||||
current_rst_val: 0,
|
PwmCommon {
|
||||||
sys_clk: sys_clk.into(),
|
current_duty: 0,
|
||||||
},
|
current_lower_limit: 0,
|
||||||
reg: unsafe { TimAndPinRegister::new(tim_and_pin.0, tim_and_pin.1) },
|
current_period: initial_period.into(),
|
||||||
|
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.reg.mask_32()) });
|
.modify(|r, w| unsafe { w.bits(r.bits() | pin.pin_and_tim.1.mask_32()) });
|
||||||
pin.enable_pwm_a();
|
pin.enable_pwm_a();
|
||||||
pin.set_period(initial_period);
|
pin.set_period(initial_period);
|
||||||
pin
|
pin
|
||||||
}
|
}
|
||||||
pub fn release(self) -> (Pin, Tim) {
|
|
||||||
self.reg.release()
|
pub fn downgrade(self) -> ReducedPwmPin<Mode> {
|
||||||
|
self.inner
|
||||||
}
|
}
|
||||||
|
|
||||||
pwm_common_func!();
|
pub fn release(self) -> (Pin, Tim) {
|
||||||
|
self.pin_and_tim
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
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>
|
||||||
@ -196,9 +137,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
|
||||||
@ -210,13 +151,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 pwmb = Self {
|
let mut pwma = 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_a();
|
pwma.enable_pwm_a();
|
||||||
pwmb
|
pwma
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -260,33 +201,107 @@ 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> {
|
||||||
reg: TimDynRegister,
|
dyn_reg: TimDynRegister,
|
||||||
pwm_base: PwmBase,
|
common: PwmCommon,
|
||||||
pin_id: DynPinId,
|
|
||||||
mode: PhantomData<Mode>,
|
mode: PhantomData<Mode>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<PIN: TimPin, TIM: ValidTim> From<PwmPin<PIN, TIM>> for ReducedPwmPin<PwmA> {
|
impl<Mode> ReducedPwmPin<Mode> {
|
||||||
fn from(pwm_pin: PwmPin<PIN, TIM>) -> Self {
|
pub(crate) fn new(tim_id: u8, pin_id: DynPinId, common: PwmCommon) -> Self {
|
||||||
ReducedPwmPin {
|
Self {
|
||||||
reg: TimDynRegister::from(pwm_pin.reg),
|
dyn_reg: TimDynRegister { tim_id, pin_id },
|
||||||
pwm_base: pwm_pin.pwm_base,
|
common,
|
||||||
pin_id: PIN::DYN,
|
|
||||||
mode: PhantomData,
|
mode: PhantomData,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<MODE> ReducedPwmPin<MODE> {
|
impl<Mode> ReducedPwmPin<Mode> {
|
||||||
pwm_common_func!();
|
#[inline]
|
||||||
|
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 {
|
||||||
reg: other.reg,
|
dyn_reg: other.dyn_reg,
|
||||||
pwm_base: other.pwm_base,
|
common: other.common,
|
||||||
pin_id: other.pin_id,
|
|
||||||
mode: PhantomData,
|
mode: PhantomData,
|
||||||
};
|
};
|
||||||
pwmb.enable_pwm_b();
|
pwmb.enable_pwm_b();
|
||||||
@ -297,9 +312,8 @@ 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 {
|
||||||
reg: other.reg,
|
dyn_reg: other.dyn_reg,
|
||||||
pwm_base: other.pwm_base,
|
common: other.common,
|
||||||
pin_id: other.pin_id,
|
|
||||||
mode: PhantomData,
|
mode: PhantomData,
|
||||||
};
|
};
|
||||||
pwmb.enable_pwm_a();
|
pwmb.enable_pwm_a();
|
||||||
@ -311,15 +325,83 @@ 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>,
|
||||||
{
|
{
|
||||||
pwmb_func!();
|
pub fn pwmb_lower_limit(&self) -> u16 {
|
||||||
|
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> {
|
||||||
pwmb_func!();
|
#[inline(always)]
|
||||||
|
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) });
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
//==================================================================================================
|
//==================================================================================================
|
||||||
@ -342,12 +424,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.pwm_base.current_duty = duty;
|
self.common.current_duty = duty;
|
||||||
let pwma_val: u64 = (self.pwm_base.current_rst_val as u64
|
let pwma_val: u64 = (self.common.current_rst_val as u64
|
||||||
* (DUTY_MAX as u64 - self.pwm_base.current_duty as u64))
|
* (DUTY_MAX as u64 - self.common.current_duty as u64))
|
||||||
/ DUTY_MAX as u64;
|
/ DUTY_MAX as u64;
|
||||||
self.reg
|
self.dyn_reg
|
||||||
.reg()
|
.reg_block()
|
||||||
.pwma_value()
|
.pwma_value()
|
||||||
.write(|w| unsafe { w.bits(pwma_val as u32) });
|
.write(|w| unsafe { w.bits(pwma_val as u32) });
|
||||||
Ok(())
|
Ok(())
|
||||||
@ -362,15 +444,7 @@ 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.pwm_base.current_duty = duty;
|
self.inner.set_duty_cycle(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(())
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
File diff suppressed because it is too large
Load Diff
@ -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
|
||||||
|
@ -3,10 +3,11 @@
|
|||||||
//! ## Examples
|
//! ## Examples
|
||||||
//!
|
//!
|
||||||
//! - [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-hal/src/branch/main/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::IrqCfg;
|
pub use crate::InterruptConfig;
|
||||||
use crate::{
|
use crate::{
|
||||||
clock::{enable_peripheral_clock, PeripheralClocks},
|
clock::{enable_peripheral_clock, PeripheralClocks},
|
||||||
|
enable_nvic_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,
|
||||||
@ -17,15 +18,56 @@ use crate::{
|
|||||||
time::Hertz,
|
time::Hertz,
|
||||||
timer,
|
timer,
|
||||||
typelevel::Sealed,
|
typelevel::Sealed,
|
||||||
utility::unmask_irq,
|
|
||||||
};
|
};
|
||||||
use core::cell::Cell;
|
use core::cell::Cell;
|
||||||
use cortex_m::interrupt::Mutex;
|
use critical_section::Mutex;
|
||||||
use fugit::RateExtU32;
|
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
|
||||||
//==================================================================================================
|
//==================================================================================================
|
||||||
@ -37,6 +79,7 @@ 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,
|
||||||
@ -66,31 +109,53 @@ 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,
|
||||||
Csd2 = 2,
|
Csd2 = 2,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, PartialEq, Eq)]
|
||||||
|
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
|
||||||
|
pub struct InvalidCascadeSourceId;
|
||||||
|
|
||||||
/// The numbers are the base numbers for bundles like PORTA, PORTB or TIM
|
/// The numbers are the base numbers for bundles like PORTA, PORTB or TIM
|
||||||
#[derive(Debug, PartialEq, Eq)]
|
#[derive(Debug, PartialEq, Eq)]
|
||||||
|
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
|
||||||
|
#[repr(u8)]
|
||||||
pub enum CascadeSource {
|
pub enum CascadeSource {
|
||||||
PortABase = 0,
|
PortA(u8),
|
||||||
PortBBase = 32,
|
PortB(u8),
|
||||||
TimBase = 64,
|
Tim(u8),
|
||||||
RamSbe = 96,
|
RamSbe = 96,
|
||||||
RamMbe = 97,
|
RamMbe = 97,
|
||||||
RomSbe = 98,
|
RomSbe = 98,
|
||||||
RomMbe = 99,
|
RomMbe = 99,
|
||||||
Txev = 100,
|
Txev = 100,
|
||||||
ClockDividerBase = 120,
|
ClockDivider(u8),
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, PartialEq, Eq)]
|
impl CascadeSource {
|
||||||
pub enum TimerErrors {
|
fn id(&self) -> Result<u8, InvalidCascadeSourceId> {
|
||||||
Canceled,
|
let port_check = |base: u8, id: u8, len: u8| {
|
||||||
/// Invalid input for Cascade source
|
if id > len - 1 {
|
||||||
InvalidCsdSourceInput,
|
return Err(InvalidCascadeSourceId);
|
||||||
|
}
|
||||||
|
Ok(base + id)
|
||||||
|
};
|
||||||
|
match self {
|
||||||
|
CascadeSource::PortA(id) => port_check(0, *id, 32),
|
||||||
|
CascadeSource::PortB(id) => port_check(32, *id, 32),
|
||||||
|
CascadeSource::Tim(id) => port_check(64, *id, 24),
|
||||||
|
CascadeSource::RamSbe => Ok(96),
|
||||||
|
CascadeSource::RamMbe => Ok(97),
|
||||||
|
CascadeSource::RomSbe => Ok(98),
|
||||||
|
CascadeSource::RomMbe => Ok(99),
|
||||||
|
CascadeSource::Txev => Ok(100),
|
||||||
|
CascadeSource::ClockDivider(id) => port_check(120, *id, 8),
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
//==================================================================================================
|
//==================================================================================================
|
||||||
@ -223,11 +288,11 @@ pub type TimRegBlock = tim0::RegisterBlock;
|
|||||||
///
|
///
|
||||||
/// # Safety
|
/// # Safety
|
||||||
///
|
///
|
||||||
/// Users should only implement the [`tim_id`] function. No default function
|
/// Users should only implement the [Self::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(super) unsafe trait TimRegInterface {
|
pub 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 _;
|
||||||
@ -235,7 +300,7 @@ pub(super) 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(&self) -> &TimRegBlock {
|
fn reg_block(&self) -> &TimRegBlock {
|
||||||
unsafe { &*Self::PORT_BASE.offset(self.tim_id() as isize) }
|
unsafe { &*Self::PORT_BASE.offset(self.tim_id() as isize) }
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -256,7 +321,7 @@ pub(super) 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()));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -267,75 +332,21 @@ pub(super) 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()));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Provide a safe register interface for [`ValidTimAndPin`]s
|
unsafe impl<Tim: ValidTim> TimRegInterface for Tim {
|
||||||
///
|
|
||||||
/// 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
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
unsafe impl<TIM: ValidTim> TimRegInterface for TimRegister<TIM> {
|
|
||||||
fn tim_id(&self) -> u8 {
|
fn tim_id(&self) -> u8 {
|
||||||
TIM::TIM_ID
|
Tim::TIM_ID
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<PIN: TimPin, TIM: ValidTim> TimAndPinRegister<PIN, TIM>
|
pub(crate) struct TimDynRegister {
|
||||||
where
|
pub(crate) tim_id: u8,
|
||||||
(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)]
|
||||||
pin_id: DynPinId,
|
pub(crate) 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 {
|
||||||
@ -350,105 +361,42 @@ unsafe impl TimRegInterface for TimDynRegister {
|
|||||||
//==================================================================================================
|
//==================================================================================================
|
||||||
|
|
||||||
/// Hardware timers
|
/// Hardware timers
|
||||||
pub struct CountDownTimer<TIM: ValidTim> {
|
pub struct CountdownTimer<Tim: ValidTim> {
|
||||||
tim: TimRegister<TIM>,
|
tim: Tim,
|
||||||
curr_freq: Hertz,
|
curr_freq: Hertz,
|
||||||
irq_cfg: Option<IrqCfg>,
|
irq_cfg: Option<InterruptConfig>,
|
||||||
sys_clk: Hertz,
|
sys_clk: Hertz,
|
||||||
rst_val: u32,
|
rst_val: u32,
|
||||||
last_cnt: u32,
|
last_cnt: u32,
|
||||||
listening: bool,
|
listening: bool,
|
||||||
}
|
}
|
||||||
|
|
||||||
fn enable_tim_clk(syscfg: &mut pac::Sysconfig, idx: u8) {
|
#[inline(always)]
|
||||||
|
pub fn enable_tim_clk(syscfg: &mut pac::Sysconfig, idx: u8) {
|
||||||
syscfg
|
syscfg
|
||||||
.tim_clk_enable()
|
.tim_clk_enable()
|
||||||
.modify(|r, w| unsafe { w.bits(r.bits() | (1 << idx)) });
|
.modify(|r, w| unsafe { w.bits(r.bits() | (1 << idx)) });
|
||||||
}
|
}
|
||||||
|
|
||||||
unsafe impl<TIM: ValidTim> TimRegInterface for CountDownTimer<TIM> {
|
#[inline(always)]
|
||||||
|
pub fn disable_tim_clk(syscfg: &mut pac::Sysconfig, idx: u8) {
|
||||||
|
syscfg
|
||||||
|
.tim_clk_enable()
|
||||||
|
.modify(|r, w| unsafe { w.bits(r.bits() & !(1 << idx)) });
|
||||||
|
}
|
||||||
|
|
||||||
|
unsafe impl<TIM: ValidTim> TimRegInterface for CountdownTimer<TIM> {
|
||||||
fn tim_id(&self) -> u8 {
|
fn tim_id(&self) -> u8 {
|
||||||
TIM::TIM_ID
|
TIM::TIM_ID
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
macro_rules! csd_sel {
|
impl<Tim: ValidTim> CountdownTimer<Tim> {
|
||||||
($func_name:ident, $csd_reg:ident) => {
|
|
||||||
/// Configure the Cascade sources
|
|
||||||
pub fn $func_name(
|
|
||||||
&mut self,
|
|
||||||
src: CascadeSource,
|
|
||||||
id: Option<u8>,
|
|
||||||
) -> Result<(), TimerErrors> {
|
|
||||||
let mut id_num = 0;
|
|
||||||
if let CascadeSource::PortABase
|
|
||||||
| CascadeSource::PortBBase
|
|
||||||
| CascadeSource::ClockDividerBase
|
|
||||||
| CascadeSource::TimBase = src
|
|
||||||
{
|
|
||||||
if id.is_none() {
|
|
||||||
return Err(TimerErrors::InvalidCsdSourceInput);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if id.is_some() {
|
|
||||||
id_num = id.unwrap();
|
|
||||||
}
|
|
||||||
match src {
|
|
||||||
CascadeSource::PortABase => {
|
|
||||||
if id_num > 55 {
|
|
||||||
return Err(TimerErrors::InvalidCsdSourceInput);
|
|
||||||
}
|
|
||||||
self.tim.reg().$csd_reg().write(|w| unsafe {
|
|
||||||
w.cassel().bits(CascadeSource::PortABase as u8 + id_num)
|
|
||||||
});
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
CascadeSource::PortBBase => {
|
|
||||||
if id_num > 23 {
|
|
||||||
return Err(TimerErrors::InvalidCsdSourceInput);
|
|
||||||
}
|
|
||||||
self.tim.reg().$csd_reg().write(|w| unsafe {
|
|
||||||
w.cassel().bits(CascadeSource::PortBBase as u8 + id_num)
|
|
||||||
});
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
CascadeSource::TimBase => {
|
|
||||||
if id_num > 23 {
|
|
||||||
return Err(TimerErrors::InvalidCsdSourceInput);
|
|
||||||
}
|
|
||||||
self.tim.reg().$csd_reg().write(|w| unsafe {
|
|
||||||
w.cassel().bits(CascadeSource::TimBase as u8 + id_num)
|
|
||||||
});
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
CascadeSource::ClockDividerBase => {
|
|
||||||
if id_num > 7 {
|
|
||||||
return Err(TimerErrors::InvalidCsdSourceInput);
|
|
||||||
}
|
|
||||||
self.tim.reg().cascade0().write(|w| unsafe {
|
|
||||||
w.cassel()
|
|
||||||
.bits(CascadeSource::ClockDividerBase as u8 + id_num)
|
|
||||||
});
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
_ => {
|
|
||||||
self.tim
|
|
||||||
.reg()
|
|
||||||
.$csd_reg()
|
|
||||||
.write(|w| unsafe { w.cassel().bits(src as u8) });
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
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: unsafe { TimRegister::new(tim) },
|
tim,
|
||||||
sys_clk: sys_clk.into(),
|
sys_clk: sys_clk.into(),
|
||||||
irq_cfg: None,
|
irq_cfg: None,
|
||||||
rst_val: 0,
|
rst_val: 0,
|
||||||
@ -458,7 +406,7 @@ impl<TIM: ValidTim> CountDownTimer<TIM> {
|
|||||||
};
|
};
|
||||||
cd_timer
|
cd_timer
|
||||||
.tim
|
.tim
|
||||||
.reg()
|
.reg_block()
|
||||||
.ctrl()
|
.ctrl()
|
||||||
.modify(|_, w| w.enable().set_bit());
|
.modify(|_, w| w.enable().set_bit());
|
||||||
cd_timer
|
cd_timer
|
||||||
@ -469,13 +417,13 @@ impl<TIM: ValidTim> CountDownTimer<TIM> {
|
|||||||
pub fn listen(
|
pub fn listen(
|
||||||
&mut self,
|
&mut self,
|
||||||
event: Event,
|
event: Event,
|
||||||
irq_cfg: IrqCfg,
|
irq_cfg: InterruptConfig,
|
||||||
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.irq);
|
cortex_m::peripheral::NVIC::mask(irq_cfg.id);
|
||||||
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 {
|
||||||
@ -483,8 +431,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.irq as u32) });
|
.write(|w| unsafe { w.bits(irq_cfg.id as u32) });
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
self.listening = true;
|
self.listening = true;
|
||||||
@ -502,7 +450,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;
|
||||||
@ -512,25 +460,37 @@ impl<TIM: ValidTim> CountDownTimer<TIM> {
|
|||||||
|
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
pub fn enable_interrupt(&mut self) {
|
pub fn enable_interrupt(&mut self) {
|
||||||
self.tim.reg().ctrl().modify(|_, w| w.irq_enb().set_bit());
|
self.tim
|
||||||
|
.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.reg().ctrl().modify(|_, w| w.irq_enb().clear_bit());
|
self.tim
|
||||||
|
.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.reg().ctrl().write(|w| w.enable().clear_bit());
|
self.tim
|
||||||
|
.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.release()
|
self.tim
|
||||||
}
|
}
|
||||||
|
|
||||||
/// 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.reg().ctrl().modify(|_, w| w.enable().clear_bit());
|
self.tim
|
||||||
|
.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);
|
||||||
@ -539,45 +499,57 @@ 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.reg().rst_value().write(|w| unsafe { w.bits(val) });
|
self.tim
|
||||||
|
.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.reg().cnt_value().write(|w| unsafe { w.bits(val) });
|
self.tim
|
||||||
|
.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().cnt_value().read().bits()
|
self.tim.reg_block().cnt_value().read().bits()
|
||||||
}
|
}
|
||||||
|
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
pub fn enable(&mut self) {
|
pub fn enable(&mut self) {
|
||||||
self.tim.reg().ctrl().modify(|_, w| w.enable().set_bit());
|
|
||||||
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 {
|
if irq_cfg.enable_in_nvic {
|
||||||
unmask_irq(irq_cfg.irq);
|
unsafe { enable_nvic_interrupt(irq_cfg.id) };
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
self.tim
|
||||||
|
.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.reg().ctrl().modify(|_, w| w.enable().clear_bit());
|
self.tim
|
||||||
|
.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()
|
.reg_block()
|
||||||
.ctrl()
|
.ctrl()
|
||||||
.modify(|_, w| w.auto_disable().set_bit());
|
.modify(|_, w| w.auto_disable().set_bit());
|
||||||
} else {
|
} else {
|
||||||
self.tim
|
self.tim
|
||||||
.reg()
|
.reg_block()
|
||||||
.ctrl()
|
.ctrl()
|
||||||
.modify(|_, w| w.auto_disable().clear_bit());
|
.modify(|_, w| w.auto_disable().clear_bit());
|
||||||
}
|
}
|
||||||
@ -591,12 +563,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()
|
.reg_block()
|
||||||
.ctrl()
|
.ctrl()
|
||||||
.modify(|_, w| w.auto_deactivate().set_bit());
|
.modify(|_, w| w.auto_deactivate().set_bit());
|
||||||
} else {
|
} else {
|
||||||
self.tim
|
self.tim
|
||||||
.reg()
|
.reg_block()
|
||||||
.ctrl()
|
.ctrl()
|
||||||
.modify(|_, w| w.auto_deactivate().clear_bit());
|
.modify(|_, w| w.auto_deactivate().clear_bit());
|
||||||
}
|
}
|
||||||
@ -605,7 +577,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().csd_ctrl().write(|w| {
|
self.tim.reg_block().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);
|
||||||
@ -619,9 +591,32 @@ impl<TIM: ValidTim> CountDownTimer<TIM> {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
csd_sel!(cascade_0_source, cascade0);
|
pub fn cascade_0_source(&mut self, src: CascadeSource) -> Result<(), InvalidCascadeSourceId> {
|
||||||
csd_sel!(cascade_1_source, cascade1);
|
let id = src.id()?;
|
||||||
csd_sel!(cascade_2_source, cascade2);
|
self.tim
|
||||||
|
.reg_block()
|
||||||
|
.cascade0()
|
||||||
|
.write(|w| unsafe { w.cassel().bits(id) });
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn cascade_1_source(&mut self, src: CascadeSource) -> Result<(), InvalidCascadeSourceId> {
|
||||||
|
let id = src.id()?;
|
||||||
|
self.tim
|
||||||
|
.reg_block()
|
||||||
|
.cascade1()
|
||||||
|
.write(|w| unsafe { w.cassel().bits(id) });
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn cascade_2_source(&mut self, src: CascadeSource) -> Result<(), InvalidCascadeSourceId> {
|
||||||
|
let id = src.id()?;
|
||||||
|
self.tim
|
||||||
|
.reg_block()
|
||||||
|
.cascade2()
|
||||||
|
.write(|w| unsafe { w.cassel().bits(id) });
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
pub fn curr_freq(&self) -> Hertz {
|
pub fn curr_freq(&self) -> Hertz {
|
||||||
self.curr_freq
|
self.curr_freq
|
||||||
@ -633,7 +628,7 @@ impl<TIM: ValidTim> CountDownTimer<TIM> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// CountDown implementation for TIMx
|
/// CountDown implementation for TIMx
|
||||||
impl<TIM: ValidTim> CountDownTimer<TIM> {
|
impl<TIM: ValidTim> CountdownTimer<TIM> {
|
||||||
#[inline]
|
#[inline]
|
||||||
pub fn start<T>(&mut self, timeout: T)
|
pub fn start<T>(&mut self, timeout: T)
|
||||||
where
|
where
|
||||||
@ -646,7 +641,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().cnt_value().read().bits();
|
let cnt = self.tim.reg_block().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(())
|
||||||
@ -656,16 +651,20 @@ impl<TIM: ValidTim> CountDownTimer<TIM> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn cancel(&mut self) -> Result<(), TimerErrors> {
|
/// Returns [false] if the timer was not active, and true otherwise.
|
||||||
if !self.tim.reg().ctrl().read().enable().bit_is_set() {
|
pub fn cancel(&mut self) -> bool {
|
||||||
return Err(TimerErrors::Canceled);
|
if !self.tim.reg_block().ctrl().read().enable().bit_is_set() {
|
||||||
|
return false;
|
||||||
}
|
}
|
||||||
self.tim.reg().ctrl().write(|w| w.enable().clear_bit());
|
self.tim
|
||||||
Ok(())
|
.reg_block()
|
||||||
|
.ctrl()
|
||||||
|
.write(|w| w.enable().clear_bit());
|
||||||
|
true
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<TIM: ValidTim> embedded_hal::delay::DelayNs for CountDownTimer<TIM> {
|
impl<TIM: ValidTim> embedded_hal::delay::DelayNs for CountdownTimer<TIM> {
|
||||||
fn delay_ns(&mut self, ns: u32) {
|
fn delay_ns(&mut self, ns: u32) {
|
||||||
let ticks = (u64::from(ns)) * (u64::from(self.sys_clk.raw())) / 1_000_000_000;
|
let ticks = (u64::from(ns)) * (u64::from(self.sys_clk.raw())) / 1_000_000_000;
|
||||||
|
|
||||||
@ -722,13 +721,13 @@ 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: IrqCfg,
|
irq_cfg: InterruptConfig,
|
||||||
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>,
|
||||||
tim0: TIM,
|
tim0: TIM,
|
||||||
) -> CountDownTimer<TIM> {
|
) -> CountdownTimer<TIM> {
|
||||||
let mut ms_timer = CountDownTimer::new(sys_cfg, sys_clk, tim0);
|
let mut ms_timer = CountdownTimer::new(sys_cfg, sys_clk, tim0);
|
||||||
ms_timer.listen(timer::Event::TimeOut, irq_cfg, irq_sel, Some(sys_cfg));
|
ms_timer.listen(timer::Event::TimeOut, irq_cfg, irq_sel, Some(sys_cfg));
|
||||||
ms_timer.start(1000.Hz());
|
ms_timer.start(1000.Hz());
|
||||||
ms_timer
|
ms_timer
|
||||||
@ -738,8 +737,8 @@ pub fn set_up_ms_delay_provider<TIM: ValidTim>(
|
|||||||
sys_cfg: &mut pac::Sysconfig,
|
sys_cfg: &mut pac::Sysconfig,
|
||||||
sys_clk: impl Into<Hertz>,
|
sys_clk: impl Into<Hertz>,
|
||||||
tim: TIM,
|
tim: TIM,
|
||||||
) -> CountDownTimer<TIM> {
|
) -> CountdownTimer<TIM> {
|
||||||
let mut provider = CountDownTimer::new(sys_cfg, sys_clk, tim);
|
let mut provider = CountdownTimer::new(sys_cfg, sys_clk, tim);
|
||||||
provider.start(1000.Hz());
|
provider.start(1000.Hz());
|
||||||
provider
|
provider
|
||||||
}
|
}
|
||||||
@ -747,7 +746,7 @@ pub fn set_up_ms_delay_provider<TIM: ValidTim>(
|
|||||||
/// This function can be called in a specified interrupt handler to increment
|
/// This function can be called in a specified interrupt handler to increment
|
||||||
/// the MS counter
|
/// the MS counter
|
||||||
pub fn default_ms_irq_handler() {
|
pub fn default_ms_irq_handler() {
|
||||||
cortex_m::interrupt::free(|cs| {
|
critical_section::with(|cs| {
|
||||||
let mut ms = MS_COUNTER.borrow(cs).get();
|
let mut ms = MS_COUNTER.borrow(cs).get();
|
||||||
ms += 1;
|
ms += 1;
|
||||||
MS_COUNTER.borrow(cs).set(ms);
|
MS_COUNTER.borrow(cs).set(ms);
|
||||||
@ -756,17 +755,17 @@ pub fn default_ms_irq_handler() {
|
|||||||
|
|
||||||
/// Get the current MS tick count
|
/// Get the current MS tick count
|
||||||
pub fn get_ms_ticks() -> u32 {
|
pub fn get_ms_ticks() -> u32 {
|
||||||
cortex_m::interrupt::free(|cs| MS_COUNTER.borrow(cs).get())
|
critical_section::with(|cs| MS_COUNTER.borrow(cs).get())
|
||||||
}
|
}
|
||||||
|
|
||||||
//==================================================================================================
|
//==================================================================================================
|
||||||
// Delay implementations
|
// Delay implementations
|
||||||
//==================================================================================================
|
//==================================================================================================
|
||||||
|
|
||||||
pub struct DelayMs(CountDownTimer<pac::Tim0>);
|
pub struct DelayMs(CountdownTimer<pac::Tim0>);
|
||||||
|
|
||||||
impl DelayMs {
|
impl DelayMs {
|
||||||
pub fn new(timer: CountDownTimer<pac::Tim0>) -> Option<Self> {
|
pub fn new(timer: CountdownTimer<pac::Tim0>) -> Option<Self> {
|
||||||
if timer.curr_freq() != Hertz::from_raw(1000) || !timer.listening() {
|
if timer.curr_freq() != Hertz::from_raw(1000) || !timer.listening() {
|
||||||
return None;
|
return None;
|
||||||
}
|
}
|
||||||
|
File diff suppressed because it is too large
Load Diff
1396
va108xx-hal/src/uart/mod.rs
Normal file
1396
va108xx-hal/src/uart/mod.rs
Normal file
File diff suppressed because it is too large
Load Diff
419
va108xx-hal/src/uart/rx_asynch.rs
Normal file
419
va108xx-hal/src/uart/rx_asynch.rs
Normal file
@ -0,0 +1,419 @@
|
|||||||
|
//! # Async UART reception functionality for the VA108xx family.
|
||||||
|
//!
|
||||||
|
//! This module provides the [RxAsync] and [RxAsyncSharedConsumer] struct which both implement the
|
||||||
|
//! [embedded_io_async::Read] trait.
|
||||||
|
//! This trait allows for asynchronous reception of data streams. Please note that this module does
|
||||||
|
//! not specify/declare the interrupt handlers which must be provided for async support to work.
|
||||||
|
//! However, it provides four interrupt handlers:
|
||||||
|
//!
|
||||||
|
//! - [on_interrupt_uart_a]
|
||||||
|
//! - [on_interrupt_uart_b]
|
||||||
|
//! - [on_interrupt_uart_a_overwriting]
|
||||||
|
//! - [on_interrupt_uart_b_overwriting]
|
||||||
|
//!
|
||||||
|
//! The first two are used for the [RxAsync] struct, while the latter two are used with the
|
||||||
|
//! [RxAsyncSharedConsumer] struct. The later two will overwrite old values in the used ring buffer.
|
||||||
|
//!
|
||||||
|
//! Error handling is performed in the user interrupt handler by checking the [AsyncUartErrors]
|
||||||
|
//! structure returned by the interrupt handlers.
|
||||||
|
//!
|
||||||
|
//! # Example
|
||||||
|
//!
|
||||||
|
//! - [Async UART RX example](https://egit.irs.uni-stuttgart.de/rust/va108xx-rs/src/branch/main/examples/embassy/src/bin/async-uart-rx.rs)
|
||||||
|
use core::{cell::RefCell, convert::Infallible, future::Future, sync::atomic::Ordering};
|
||||||
|
|
||||||
|
use critical_section::Mutex;
|
||||||
|
use embassy_sync::waitqueue::AtomicWaker;
|
||||||
|
use embedded_io::ErrorType;
|
||||||
|
use heapless::spsc::Consumer;
|
||||||
|
use portable_atomic::AtomicBool;
|
||||||
|
use va108xx as pac;
|
||||||
|
|
||||||
|
use super::{Instance, Rx, RxError, UartErrors};
|
||||||
|
|
||||||
|
static UART_RX_WAKERS: [AtomicWaker; 2] = [const { AtomicWaker::new() }; 2];
|
||||||
|
static RX_READ_ACTIVE: [AtomicBool; 2] = [const { AtomicBool::new(false) }; 2];
|
||||||
|
static RX_HAS_DATA: [AtomicBool; 2] = [const { AtomicBool::new(false) }; 2];
|
||||||
|
|
||||||
|
struct RxFuture {
|
||||||
|
uart_idx: usize,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl RxFuture {
|
||||||
|
pub fn new<Uart: Instance>(_rx: &mut Rx<Uart>) -> Self {
|
||||||
|
RX_READ_ACTIVE[Uart::IDX as usize].store(true, Ordering::Relaxed);
|
||||||
|
Self {
|
||||||
|
uart_idx: Uart::IDX as usize,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Future for RxFuture {
|
||||||
|
type Output = Result<(), RxError>;
|
||||||
|
|
||||||
|
fn poll(
|
||||||
|
self: core::pin::Pin<&mut Self>,
|
||||||
|
cx: &mut core::task::Context<'_>,
|
||||||
|
) -> core::task::Poll<Self::Output> {
|
||||||
|
UART_RX_WAKERS[self.uart_idx].register(cx.waker());
|
||||||
|
if RX_HAS_DATA[self.uart_idx].load(Ordering::Relaxed) {
|
||||||
|
return core::task::Poll::Ready(Ok(()));
|
||||||
|
}
|
||||||
|
core::task::Poll::Pending
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Clone, Copy)]
|
||||||
|
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
|
||||||
|
pub struct AsyncUartErrors {
|
||||||
|
/// Queue has overflowed, data might have been lost.
|
||||||
|
pub queue_overflow: bool,
|
||||||
|
/// UART errors.
|
||||||
|
pub uart_errors: UartErrors,
|
||||||
|
}
|
||||||
|
|
||||||
|
fn on_interrupt_handle_rx_errors<Uart: Instance>(uart: &Uart) -> Option<UartErrors> {
|
||||||
|
let rx_status = uart.rxstatus().read();
|
||||||
|
if rx_status.rxovr().bit_is_set()
|
||||||
|
|| rx_status.rxfrm().bit_is_set()
|
||||||
|
|| rx_status.rxpar().bit_is_set()
|
||||||
|
{
|
||||||
|
let mut errors_val = UartErrors::default();
|
||||||
|
|
||||||
|
if rx_status.rxovr().bit_is_set() {
|
||||||
|
errors_val.overflow = true;
|
||||||
|
}
|
||||||
|
if rx_status.rxfrm().bit_is_set() {
|
||||||
|
errors_val.framing = true;
|
||||||
|
}
|
||||||
|
if rx_status.rxpar().bit_is_set() {
|
||||||
|
errors_val.parity = true;
|
||||||
|
}
|
||||||
|
return Some(errors_val);
|
||||||
|
}
|
||||||
|
None
|
||||||
|
}
|
||||||
|
|
||||||
|
fn on_interrupt_rx_common_post_processing<Uart: Instance>(
|
||||||
|
uart: &Uart,
|
||||||
|
rx_enabled: bool,
|
||||||
|
read_some_data: bool,
|
||||||
|
irq_end: u32,
|
||||||
|
) -> Option<UartErrors> {
|
||||||
|
if read_some_data {
|
||||||
|
RX_HAS_DATA[Uart::IDX as usize].store(true, Ordering::Relaxed);
|
||||||
|
if RX_READ_ACTIVE[Uart::IDX as usize].load(Ordering::Relaxed) {
|
||||||
|
UART_RX_WAKERS[Uart::IDX as usize].wake();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
let mut errors = None;
|
||||||
|
// Check for RX errors
|
||||||
|
if rx_enabled {
|
||||||
|
errors = on_interrupt_handle_rx_errors(uart);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Clear the interrupt status bits
|
||||||
|
uart.irq_clr().write(|w| unsafe { w.bits(irq_end) });
|
||||||
|
errors
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Interrupt handler for UART A.
|
||||||
|
///
|
||||||
|
/// Should be called in the user interrupt handler to enable
|
||||||
|
/// asynchronous reception. This variant will overwrite old data in the ring buffer in case
|
||||||
|
/// the ring buffer is full.
|
||||||
|
pub fn on_interrupt_uart_a_overwriting<const N: usize>(
|
||||||
|
prod: &mut heapless::spsc::Producer<u8, N>,
|
||||||
|
shared_consumer: &Mutex<RefCell<Option<heapless::spsc::Consumer<'static, u8, N>>>>,
|
||||||
|
) -> Result<(), AsyncUartErrors> {
|
||||||
|
on_interrupt_rx_async_heapless_queue_overwriting(
|
||||||
|
unsafe { pac::Uarta::steal() },
|
||||||
|
prod,
|
||||||
|
shared_consumer,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Interrupt handler for UART B.
|
||||||
|
///
|
||||||
|
/// Should be called in the user interrupt handler to enable
|
||||||
|
/// asynchronous reception. This variant will overwrite old data in the ring buffer in case
|
||||||
|
/// the ring buffer is full.
|
||||||
|
pub fn on_interrupt_uart_b_overwriting<const N: usize>(
|
||||||
|
prod: &mut heapless::spsc::Producer<u8, N>,
|
||||||
|
shared_consumer: &Mutex<RefCell<Option<heapless::spsc::Consumer<'static, u8, N>>>>,
|
||||||
|
) -> Result<(), AsyncUartErrors> {
|
||||||
|
on_interrupt_rx_async_heapless_queue_overwriting(
|
||||||
|
unsafe { pac::Uartb::steal() },
|
||||||
|
prod,
|
||||||
|
shared_consumer,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn on_interrupt_rx_async_heapless_queue_overwriting<Uart: Instance, const N: usize>(
|
||||||
|
uart: Uart,
|
||||||
|
prod: &mut heapless::spsc::Producer<u8, N>,
|
||||||
|
shared_consumer: &Mutex<RefCell<Option<heapless::spsc::Consumer<'static, u8, N>>>>,
|
||||||
|
) -> Result<(), AsyncUartErrors> {
|
||||||
|
let irq_end = uart.irq_end().read();
|
||||||
|
let enb_status = uart.enable().read();
|
||||||
|
let rx_enabled = enb_status.rxenable().bit_is_set();
|
||||||
|
let mut read_some_data = false;
|
||||||
|
let mut queue_overflow = false;
|
||||||
|
|
||||||
|
// Half-Full interrupt. We have a guaranteed amount of data we can read.
|
||||||
|
if irq_end.irq_rx().bit_is_set() {
|
||||||
|
let available_bytes = uart.rxfifoirqtrg().read().bits() as usize;
|
||||||
|
|
||||||
|
// If this interrupt bit is set, the trigger level is available at the very least.
|
||||||
|
// Read everything as fast as possible
|
||||||
|
for _ in 0..available_bytes {
|
||||||
|
let byte = uart.data().read().bits();
|
||||||
|
if !prod.ready() {
|
||||||
|
queue_overflow = true;
|
||||||
|
critical_section::with(|cs| {
|
||||||
|
let mut cons_ref = shared_consumer.borrow(cs).borrow_mut();
|
||||||
|
cons_ref.as_mut().unwrap().dequeue();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
prod.enqueue(byte as u8).ok();
|
||||||
|
}
|
||||||
|
read_some_data = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Timeout, empty the FIFO completely.
|
||||||
|
if irq_end.irq_rx_to().bit_is_set() {
|
||||||
|
while uart.rxstatus().read().rdavl().bit_is_set() {
|
||||||
|
// While there is data in the FIFO, write it into the reception buffer
|
||||||
|
let byte = uart.data().read().bits();
|
||||||
|
if !prod.ready() {
|
||||||
|
queue_overflow = true;
|
||||||
|
critical_section::with(|cs| {
|
||||||
|
let mut cons_ref = shared_consumer.borrow(cs).borrow_mut();
|
||||||
|
cons_ref.as_mut().unwrap().dequeue();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
prod.enqueue(byte as u8).ok();
|
||||||
|
}
|
||||||
|
read_some_data = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
let uart_errors =
|
||||||
|
on_interrupt_rx_common_post_processing(&uart, rx_enabled, read_some_data, irq_end.bits());
|
||||||
|
if uart_errors.is_some() || queue_overflow {
|
||||||
|
return Err(AsyncUartErrors {
|
||||||
|
queue_overflow,
|
||||||
|
uart_errors: uart_errors.unwrap_or_default(),
|
||||||
|
});
|
||||||
|
}
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Interrupt handler for UART A.
|
||||||
|
///
|
||||||
|
/// Should be called in the user interrupt handler to enable asynchronous reception.
|
||||||
|
pub fn on_interrupt_uart_a<const N: usize>(
|
||||||
|
prod: &mut heapless::spsc::Producer<'_, u8, N>,
|
||||||
|
) -> Result<(), AsyncUartErrors> {
|
||||||
|
on_interrupt_rx_async_heapless_queue(unsafe { pac::Uarta::steal() }, prod)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Interrupt handler for UART B.
|
||||||
|
///
|
||||||
|
/// Should be called in the user interrupt handler to enable asynchronous reception.
|
||||||
|
pub fn on_interrupt_uart_b<const N: usize>(
|
||||||
|
prod: &mut heapless::spsc::Producer<'_, u8, N>,
|
||||||
|
) -> Result<(), AsyncUartErrors> {
|
||||||
|
on_interrupt_rx_async_heapless_queue(unsafe { pac::Uartb::steal() }, prod)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn on_interrupt_rx_async_heapless_queue<Uart: Instance, const N: usize>(
|
||||||
|
uart: Uart,
|
||||||
|
prod: &mut heapless::spsc::Producer<'_, u8, N>,
|
||||||
|
) -> Result<(), AsyncUartErrors> {
|
||||||
|
//let uart = unsafe { Uart::steal() };
|
||||||
|
let irq_end = uart.irq_end().read();
|
||||||
|
let enb_status = uart.enable().read();
|
||||||
|
let rx_enabled = enb_status.rxenable().bit_is_set();
|
||||||
|
let mut read_some_data = false;
|
||||||
|
let mut queue_overflow = false;
|
||||||
|
|
||||||
|
// Half-Full interrupt. We have a guaranteed amount of data we can read.
|
||||||
|
if irq_end.irq_rx().bit_is_set() {
|
||||||
|
let available_bytes = uart.rxfifoirqtrg().read().bits() as usize;
|
||||||
|
|
||||||
|
// If this interrupt bit is set, the trigger level is available at the very least.
|
||||||
|
// Read everything as fast as possible
|
||||||
|
for _ in 0..available_bytes {
|
||||||
|
let byte = uart.data().read().bits();
|
||||||
|
if !prod.ready() {
|
||||||
|
queue_overflow = true;
|
||||||
|
}
|
||||||
|
prod.enqueue(byte as u8).ok();
|
||||||
|
}
|
||||||
|
read_some_data = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Timeout, empty the FIFO completely.
|
||||||
|
if irq_end.irq_rx_to().bit_is_set() {
|
||||||
|
while uart.rxstatus().read().rdavl().bit_is_set() {
|
||||||
|
// While there is data in the FIFO, write it into the reception buffer
|
||||||
|
let byte = uart.data().read().bits();
|
||||||
|
if !prod.ready() {
|
||||||
|
queue_overflow = true;
|
||||||
|
}
|
||||||
|
prod.enqueue(byte as u8).ok();
|
||||||
|
}
|
||||||
|
read_some_data = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
let uart_errors =
|
||||||
|
on_interrupt_rx_common_post_processing(&uart, rx_enabled, read_some_data, irq_end.bits());
|
||||||
|
if uart_errors.is_some() || queue_overflow {
|
||||||
|
return Err(AsyncUartErrors {
|
||||||
|
queue_overflow,
|
||||||
|
uart_errors: uart_errors.unwrap_or_default(),
|
||||||
|
});
|
||||||
|
}
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
struct ActiveReadGuard(usize);
|
||||||
|
|
||||||
|
impl Drop for ActiveReadGuard {
|
||||||
|
fn drop(&mut self) {
|
||||||
|
RX_READ_ACTIVE[self.0].store(false, Ordering::Relaxed);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Core data structure to allow asynchronous UART reception.
|
||||||
|
///
|
||||||
|
/// If the ring buffer becomes full, data will be lost.
|
||||||
|
pub struct RxAsync<Uart: Instance, const N: usize> {
|
||||||
|
rx: Rx<Uart>,
|
||||||
|
pub queue: heapless::spsc::Consumer<'static, u8, N>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<Uart: Instance, const N: usize> ErrorType for RxAsync<Uart, N> {
|
||||||
|
/// Error reporting is done using the result of the interrupt functions.
|
||||||
|
type Error = Infallible;
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<Uart: Instance, const N: usize> RxAsync<Uart, N> {
|
||||||
|
/// Create a new asynchronous receiver.
|
||||||
|
///
|
||||||
|
/// The passed [heapless::spsc::Consumer] will be used to asynchronously receive data which
|
||||||
|
/// is filled by the interrupt handler.
|
||||||
|
pub fn new(mut rx: Rx<Uart>, queue: heapless::spsc::Consumer<'static, u8, N>) -> Self {
|
||||||
|
rx.disable_interrupts();
|
||||||
|
rx.disable();
|
||||||
|
rx.clear_fifo();
|
||||||
|
// Enable those together.
|
||||||
|
critical_section::with(|_| {
|
||||||
|
rx.enable_interrupts();
|
||||||
|
rx.enable();
|
||||||
|
});
|
||||||
|
Self { rx, queue }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<Uart: Instance, const N: usize> embedded_io_async::Read for RxAsync<Uart, N> {
|
||||||
|
async fn read(&mut self, buf: &mut [u8]) -> Result<usize, Self::Error> {
|
||||||
|
// Need to wait for the IRQ to read data and set this flag. If the queue is not
|
||||||
|
// empty, we can read data immediately.
|
||||||
|
if self.queue.len() == 0 {
|
||||||
|
RX_HAS_DATA[Uart::IDX as usize].store(false, Ordering::Relaxed);
|
||||||
|
}
|
||||||
|
let _guard = ActiveReadGuard(Uart::IDX as usize);
|
||||||
|
let mut handle_data_in_queue = |consumer: &mut heapless::spsc::Consumer<'static, u8, N>| {
|
||||||
|
let data_to_read = consumer.len().min(buf.len());
|
||||||
|
for byte in buf.iter_mut().take(data_to_read) {
|
||||||
|
// We own the consumer and we checked that the amount of data is guaranteed to be available.
|
||||||
|
*byte = unsafe { consumer.dequeue_unchecked() };
|
||||||
|
}
|
||||||
|
data_to_read
|
||||||
|
};
|
||||||
|
let fut = RxFuture::new(&mut self.rx);
|
||||||
|
// Data is available, so read that data immediately.
|
||||||
|
let read_data = handle_data_in_queue(&mut self.queue);
|
||||||
|
if read_data > 0 {
|
||||||
|
return Ok(read_data);
|
||||||
|
}
|
||||||
|
// Await data.
|
||||||
|
let _ = fut.await;
|
||||||
|
Ok(handle_data_in_queue(&mut self.queue))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Core data structure to allow asynchronous UART reception.
|
||||||
|
///
|
||||||
|
/// If the ring buffer becomes full, the oldest data will be overwritten when using the
|
||||||
|
/// [on_interrupt_uart_a_overwriting] and [on_interrupt_uart_b_overwriting] interrupt handlers.
|
||||||
|
pub struct RxAsyncSharedConsumer<Uart: Instance, const N: usize> {
|
||||||
|
rx: Rx<Uart>,
|
||||||
|
queue: &'static Mutex<RefCell<Option<Consumer<'static, u8, N>>>>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<Uart: Instance, const N: usize> ErrorType for RxAsyncSharedConsumer<Uart, N> {
|
||||||
|
/// Error reporting is done using the result of the interrupt functions.
|
||||||
|
type Error = Infallible;
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<Uart: Instance, const N: usize> RxAsyncSharedConsumer<Uart, N> {
|
||||||
|
/// Create a new asynchronous receiver.
|
||||||
|
///
|
||||||
|
/// The passed shared [heapless::spsc::Consumer] will be used to asynchronously receive data
|
||||||
|
/// which is filled by the interrupt handler. The shared property allows using it in the
|
||||||
|
/// interrupt handler to overwrite old data.
|
||||||
|
pub fn new(
|
||||||
|
mut rx: Rx<Uart>,
|
||||||
|
queue: &'static Mutex<RefCell<Option<heapless::spsc::Consumer<'static, u8, N>>>>,
|
||||||
|
) -> Self {
|
||||||
|
rx.disable_interrupts();
|
||||||
|
rx.disable();
|
||||||
|
rx.clear_fifo();
|
||||||
|
// Enable those together.
|
||||||
|
critical_section::with(|_| {
|
||||||
|
rx.enable_interrupts();
|
||||||
|
rx.enable();
|
||||||
|
});
|
||||||
|
Self { rx, queue }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<Uart: Instance, const N: usize> embedded_io_async::Read for RxAsyncSharedConsumer<Uart, N> {
|
||||||
|
async fn read(&mut self, buf: &mut [u8]) -> Result<usize, Self::Error> {
|
||||||
|
// Need to wait for the IRQ to read data and set this flag. If the queue is not
|
||||||
|
// empty, we can read data immediately.
|
||||||
|
|
||||||
|
critical_section::with(|cs| {
|
||||||
|
let queue = self.queue.borrow(cs);
|
||||||
|
if queue.borrow().as_ref().unwrap().len() == 0 {
|
||||||
|
RX_HAS_DATA[Uart::IDX as usize].store(false, Ordering::Relaxed);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
let _guard = ActiveReadGuard(Uart::IDX as usize);
|
||||||
|
let mut handle_data_in_queue = || {
|
||||||
|
critical_section::with(|cs| {
|
||||||
|
let mut consumer_ref = self.queue.borrow(cs).borrow_mut();
|
||||||
|
let consumer = consumer_ref.as_mut().unwrap();
|
||||||
|
let data_to_read = consumer.len().min(buf.len());
|
||||||
|
for byte in buf.iter_mut().take(data_to_read) {
|
||||||
|
// We own the consumer and we checked that the amount of data is guaranteed to be available.
|
||||||
|
*byte = unsafe { consumer.dequeue_unchecked() };
|
||||||
|
}
|
||||||
|
data_to_read
|
||||||
|
})
|
||||||
|
};
|
||||||
|
let fut = RxFuture::new(&mut self.rx);
|
||||||
|
// Data is available, so read that data immediately.
|
||||||
|
let read_data = handle_data_in_queue();
|
||||||
|
if read_data > 0 {
|
||||||
|
return Ok(read_data);
|
||||||
|
}
|
||||||
|
// Await data.
|
||||||
|
let _ = fut.await;
|
||||||
|
let read_data = handle_data_in_queue();
|
||||||
|
Ok(read_data)
|
||||||
|
}
|
||||||
|
}
|
264
va108xx-hal/src/uart/tx_asynch.rs
Normal file
264
va108xx-hal/src/uart/tx_asynch.rs
Normal file
@ -0,0 +1,264 @@
|
|||||||
|
//! # 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 provides two interrupt handlers:
|
||||||
|
//!
|
||||||
|
//! - [on_interrupt_uart_a_tx]
|
||||||
|
//! - [on_interrupt_uart_b_tx]
|
||||||
|
//!
|
||||||
|
//! Those should be called in ALL user interrupt handlers which handle UART TX interrupts,
|
||||||
|
//! depending on which UARTs are used.
|
||||||
|
//!
|
||||||
|
//! # 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. The user
|
||||||
|
/// has to call this once in the interrupt handler responsible for UART A TX interrupts for
|
||||||
|
/// asynchronous operations to work.
|
||||||
|
pub fn on_interrupt_uart_a_tx() {
|
||||||
|
on_interrupt_uart_tx(unsafe { pac::Uarta::steal() });
|
||||||
|
}
|
||||||
|
|
||||||
|
/// This is a generic interrupt handler to handle asynchronous UART TX operations. The user
|
||||||
|
/// has to call this once in the interrupt handler responsible for UART B TX interrupts for
|
||||||
|
/// asynchronous operations to work.
|
||||||
|
pub fn on_interrupt_uart_b_tx() {
|
||||||
|
on_interrupt_uart_tx(unsafe { pac::Uartb::steal() });
|
||||||
|
}
|
||||||
|
|
||||||
|
fn on_interrupt_uart_tx<Uart: Instance>(uart: Uart) {
|
||||||
|
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[Uart::IDX as usize].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[Uart::IDX as usize].borrow(cs);
|
||||||
|
*context_ref.borrow_mut() = context;
|
||||||
|
});
|
||||||
|
// Transfer is done.
|
||||||
|
TX_DONE[Uart::IDX as usize].store(true, core::sync::atomic::Ordering::Relaxed);
|
||||||
|
UART_TX_WAKERS[Uart::IDX as usize].wake();
|
||||||
|
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[Uart::IDX as usize].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
|
||||||
|
}
|
||||||
|
}
|
@ -1,16 +0,0 @@
|
|||||||
//! # API for utility functions like the Error Detection and Correction (EDAC) block
|
|
||||||
//!
|
|
||||||
//! Some more information about the recommended scrub rates can be found on the
|
|
||||||
//! [Vorago White Paper website](https://www.voragotech.com/resources) in the
|
|
||||||
//! application note AN1212
|
|
||||||
use crate::pac;
|
|
||||||
|
|
||||||
/// Unmask and enable an IRQ with the given interrupt number
|
|
||||||
///
|
|
||||||
/// ## Safety
|
|
||||||
///
|
|
||||||
/// The unmask function can break mask-based critical sections
|
|
||||||
#[inline]
|
|
||||||
pub(crate) fn unmask_irq(irq: pac::Interrupt) {
|
|
||||||
unsafe { cortex_m::peripheral::NVIC::unmask(irq) };
|
|
||||||
}
|
|
@ -8,6 +8,10 @@ and this project adheres to [Semantic Versioning](http://semver.org/).
|
|||||||
|
|
||||||
## [unreleased]
|
## [unreleased]
|
||||||
|
|
||||||
|
## [v0.4.0] 2025-02-12
|
||||||
|
|
||||||
|
- Re-generated PAC with `svd2rust` v0.35.0
|
||||||
|
|
||||||
## [v0.3.0] 2024-06-16
|
## [v0.3.0] 2024-06-16
|
||||||
|
|
||||||
- Re-generated PAC with `svd2rust` v0.33.3
|
- Re-generated PAC with `svd2rust` v0.33.3
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
[package]
|
[package]
|
||||||
name = "va108xx"
|
name = "va108xx"
|
||||||
version = "0.3.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"
|
||||||
|
@ -24,7 +24,7 @@ features = ["rt"]
|
|||||||
The `rt` feature is optional and recommended. It brings in support for `cortex-m-rt`.
|
The `rt` feature is optional and recommended. It brings in support for `cortex-m-rt`.
|
||||||
|
|
||||||
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/0.19.0/svd2rust/#peripheral-api).
|
[svd2rust documentation](https://docs.rs/svd2rust/latest/svd2rust/#peripheral-api).
|
||||||
|
|
||||||
## Regenerating the PAC
|
## Regenerating the PAC
|
||||||
|
|
||||||
|
3
va108xx/docs.sh
Executable file
3
va108xx/docs.sh
Executable file
@ -0,0 +1,3 @@
|
|||||||
|
#!/bin/sh
|
||||||
|
export RUSTDOCFLAGS="--cfg docsrs --generate-link-to-definition -Z unstable-options"
|
||||||
|
cargo +nightly doc --all-features --open
|
@ -1,4 +1,4 @@
|
|||||||
#!/bin/sh
|
#!/bin/bash
|
||||||
|
|
||||||
# Use installed tool by default
|
# Use installed tool by default
|
||||||
svd2rust_bin="svd2rust"
|
svd2rust_bin="svd2rust"
|
||||||
|
@ -82,169 +82,6 @@ pub trait Resettable: RegisterSpec {
|
|||||||
Self::RESET_VALUE
|
Self::RESET_VALUE
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
#[doc = " This structure provides volatile access to registers."]
|
|
||||||
#[repr(transparent)]
|
|
||||||
pub struct Reg<REG: RegisterSpec> {
|
|
||||||
register: vcell::VolatileCell<REG::Ux>,
|
|
||||||
_marker: marker::PhantomData<REG>,
|
|
||||||
}
|
|
||||||
unsafe impl<REG: RegisterSpec> Send for Reg<REG> where REG::Ux: Send {}
|
|
||||||
impl<REG: RegisterSpec> Reg<REG> {
|
|
||||||
#[doc = " Returns the underlying memory address of register."]
|
|
||||||
#[doc = ""]
|
|
||||||
#[doc = " ```ignore"]
|
|
||||||
#[doc = " let reg_ptr = periph.reg.as_ptr();"]
|
|
||||||
#[doc = " ```"]
|
|
||||||
#[inline(always)]
|
|
||||||
pub fn as_ptr(&self) -> *mut REG::Ux {
|
|
||||||
self.register.as_ptr()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
impl<REG: Readable> Reg<REG> {
|
|
||||||
#[doc = " Reads the contents of a `Readable` register."]
|
|
||||||
#[doc = ""]
|
|
||||||
#[doc = " You can read the raw contents of a register by using `bits`:"]
|
|
||||||
#[doc = " ```ignore"]
|
|
||||||
#[doc = " let bits = periph.reg.read().bits();"]
|
|
||||||
#[doc = " ```"]
|
|
||||||
#[doc = " or get the content of a particular field of a register:"]
|
|
||||||
#[doc = " ```ignore"]
|
|
||||||
#[doc = " let reader = periph.reg.read();"]
|
|
||||||
#[doc = " let bits = reader.field1().bits();"]
|
|
||||||
#[doc = " let flag = reader.field2().bit_is_set();"]
|
|
||||||
#[doc = " ```"]
|
|
||||||
#[inline(always)]
|
|
||||||
pub fn read(&self) -> R<REG> {
|
|
||||||
R {
|
|
||||||
bits: self.register.get(),
|
|
||||||
_reg: marker::PhantomData,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
impl<REG: Resettable + Writable> Reg<REG> {
|
|
||||||
#[doc = " Writes the reset value to `Writable` register."]
|
|
||||||
#[doc = ""]
|
|
||||||
#[doc = " Resets the register to its initial state."]
|
|
||||||
#[inline(always)]
|
|
||||||
pub fn reset(&self) {
|
|
||||||
self.register.set(REG::RESET_VALUE)
|
|
||||||
}
|
|
||||||
#[doc = " Writes bits to a `Writable` register."]
|
|
||||||
#[doc = ""]
|
|
||||||
#[doc = " You can write raw bits into a register:"]
|
|
||||||
#[doc = " ```ignore"]
|
|
||||||
#[doc = " periph.reg.write(|w| unsafe { w.bits(rawbits) });"]
|
|
||||||
#[doc = " ```"]
|
|
||||||
#[doc = " or write only the fields you need:"]
|
|
||||||
#[doc = " ```ignore"]
|
|
||||||
#[doc = " periph.reg.write(|w| w"]
|
|
||||||
#[doc = " .field1().bits(newfield1bits)"]
|
|
||||||
#[doc = " .field2().set_bit()"]
|
|
||||||
#[doc = " .field3().variant(VARIANT)"]
|
|
||||||
#[doc = " );"]
|
|
||||||
#[doc = " ```"]
|
|
||||||
#[doc = " or an alternative way of saying the same:"]
|
|
||||||
#[doc = " ```ignore"]
|
|
||||||
#[doc = " periph.reg.write(|w| {"]
|
|
||||||
#[doc = " w.field1().bits(newfield1bits);"]
|
|
||||||
#[doc = " w.field2().set_bit();"]
|
|
||||||
#[doc = " w.field3().variant(VARIANT)"]
|
|
||||||
#[doc = " });"]
|
|
||||||
#[doc = " ```"]
|
|
||||||
#[doc = " In the latter case, other fields will be set to their reset value."]
|
|
||||||
#[inline(always)]
|
|
||||||
pub fn write<F>(&self, f: F)
|
|
||||||
where
|
|
||||||
F: FnOnce(&mut W<REG>) -> &mut W<REG>,
|
|
||||||
{
|
|
||||||
self.register.set(
|
|
||||||
f(&mut W {
|
|
||||||
bits: REG::RESET_VALUE & !REG::ONE_TO_MODIFY_FIELDS_BITMAP
|
|
||||||
| REG::ZERO_TO_MODIFY_FIELDS_BITMAP,
|
|
||||||
_reg: marker::PhantomData,
|
|
||||||
})
|
|
||||||
.bits,
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
impl<REG: Writable> Reg<REG> {
|
|
||||||
#[doc = " Writes 0 to a `Writable` register."]
|
|
||||||
#[doc = ""]
|
|
||||||
#[doc = " Similar to `write`, but unused bits will contain 0."]
|
|
||||||
#[doc = ""]
|
|
||||||
#[doc = " # Safety"]
|
|
||||||
#[doc = ""]
|
|
||||||
#[doc = " Unsafe to use with registers which don't allow to write 0."]
|
|
||||||
#[inline(always)]
|
|
||||||
pub unsafe fn write_with_zero<F>(&self, f: F)
|
|
||||||
where
|
|
||||||
F: FnOnce(&mut W<REG>) -> &mut W<REG>,
|
|
||||||
{
|
|
||||||
self.register.set(
|
|
||||||
f(&mut W {
|
|
||||||
bits: REG::Ux::default(),
|
|
||||||
_reg: marker::PhantomData,
|
|
||||||
})
|
|
||||||
.bits,
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
impl<REG: Readable + Writable> Reg<REG> {
|
|
||||||
#[doc = " Modifies the contents of the register by reading and then writing it."]
|
|
||||||
#[doc = ""]
|
|
||||||
#[doc = " E.g. to do a read-modify-write sequence to change parts of a register:"]
|
|
||||||
#[doc = " ```ignore"]
|
|
||||||
#[doc = " periph.reg.modify(|r, w| unsafe { w.bits("]
|
|
||||||
#[doc = " r.bits() | 3"]
|
|
||||||
#[doc = " ) });"]
|
|
||||||
#[doc = " ```"]
|
|
||||||
#[doc = " or"]
|
|
||||||
#[doc = " ```ignore"]
|
|
||||||
#[doc = " periph.reg.modify(|_, w| w"]
|
|
||||||
#[doc = " .field1().bits(newfield1bits)"]
|
|
||||||
#[doc = " .field2().set_bit()"]
|
|
||||||
#[doc = " .field3().variant(VARIANT)"]
|
|
||||||
#[doc = " );"]
|
|
||||||
#[doc = " ```"]
|
|
||||||
#[doc = " or an alternative way of saying the same:"]
|
|
||||||
#[doc = " ```ignore"]
|
|
||||||
#[doc = " periph.reg.modify(|_, w| {"]
|
|
||||||
#[doc = " w.field1().bits(newfield1bits);"]
|
|
||||||
#[doc = " w.field2().set_bit();"]
|
|
||||||
#[doc = " w.field3().variant(VARIANT)"]
|
|
||||||
#[doc = " });"]
|
|
||||||
#[doc = " ```"]
|
|
||||||
#[doc = " Other fields will have the value they had before the call to `modify`."]
|
|
||||||
#[inline(always)]
|
|
||||||
pub fn modify<F>(&self, f: F)
|
|
||||||
where
|
|
||||||
for<'w> F: FnOnce(&R<REG>, &'w mut W<REG>) -> &'w mut W<REG>,
|
|
||||||
{
|
|
||||||
let bits = self.register.get();
|
|
||||||
self.register.set(
|
|
||||||
f(
|
|
||||||
&R {
|
|
||||||
bits,
|
|
||||||
_reg: marker::PhantomData,
|
|
||||||
},
|
|
||||||
&mut W {
|
|
||||||
bits: bits & !REG::ONE_TO_MODIFY_FIELDS_BITMAP
|
|
||||||
| REG::ZERO_TO_MODIFY_FIELDS_BITMAP,
|
|
||||||
_reg: marker::PhantomData,
|
|
||||||
},
|
|
||||||
)
|
|
||||||
.bits,
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
impl<REG: Readable> core::fmt::Debug for crate::generic::Reg<REG>
|
|
||||||
where
|
|
||||||
R<REG>: core::fmt::Debug,
|
|
||||||
{
|
|
||||||
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
|
|
||||||
core::fmt::Debug::fmt(&self.read(), f)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
#[doc(hidden)]
|
#[doc(hidden)]
|
||||||
pub mod raw;
|
pub mod raw;
|
||||||
#[doc = " Register reader."]
|
#[doc = " Register reader."]
|
||||||
@ -369,7 +206,7 @@ pub struct RangeTo<const MAX: u64>;
|
|||||||
#[doc = " Write field Proxy"]
|
#[doc = " Write field Proxy"]
|
||||||
pub type FieldWriter<'a, REG, const WI: u8, FI = u8, Safety = Unsafe> =
|
pub type FieldWriter<'a, REG, const WI: u8, FI = u8, Safety = Unsafe> =
|
||||||
raw::FieldWriter<'a, REG, WI, FI, Safety>;
|
raw::FieldWriter<'a, REG, WI, FI, Safety>;
|
||||||
impl<'a, REG, const WI: u8, FI, Safety> FieldWriter<'a, REG, WI, FI, Safety>
|
impl<REG, const WI: u8, FI, Safety> FieldWriter<'_, REG, WI, FI, Safety>
|
||||||
where
|
where
|
||||||
REG: Writable + RegisterSpec,
|
REG: Writable + RegisterSpec,
|
||||||
FI: FieldSpec,
|
FI: FieldSpec,
|
||||||
@ -616,3 +453,278 @@ where
|
|||||||
self.w
|
self.w
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
#[doc = " This structure provides volatile access to registers."]
|
||||||
|
#[repr(transparent)]
|
||||||
|
pub struct Reg<REG: RegisterSpec> {
|
||||||
|
register: vcell::VolatileCell<REG::Ux>,
|
||||||
|
_marker: marker::PhantomData<REG>,
|
||||||
|
}
|
||||||
|
unsafe impl<REG: RegisterSpec> Send for Reg<REG> where REG::Ux: Send {}
|
||||||
|
impl<REG: RegisterSpec> Reg<REG> {
|
||||||
|
#[doc = " Returns the underlying memory address of register."]
|
||||||
|
#[doc = ""]
|
||||||
|
#[doc = " ```ignore"]
|
||||||
|
#[doc = " let reg_ptr = periph.reg.as_ptr();"]
|
||||||
|
#[doc = " ```"]
|
||||||
|
#[inline(always)]
|
||||||
|
pub fn as_ptr(&self) -> *mut REG::Ux {
|
||||||
|
self.register.as_ptr()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
impl<REG: Readable> Reg<REG> {
|
||||||
|
#[doc = " Reads the contents of a `Readable` register."]
|
||||||
|
#[doc = ""]
|
||||||
|
#[doc = " You can read the raw contents of a register by using `bits`:"]
|
||||||
|
#[doc = " ```ignore"]
|
||||||
|
#[doc = " let bits = periph.reg.read().bits();"]
|
||||||
|
#[doc = " ```"]
|
||||||
|
#[doc = " or get the content of a particular field of a register:"]
|
||||||
|
#[doc = " ```ignore"]
|
||||||
|
#[doc = " let reader = periph.reg.read();"]
|
||||||
|
#[doc = " let bits = reader.field1().bits();"]
|
||||||
|
#[doc = " let flag = reader.field2().bit_is_set();"]
|
||||||
|
#[doc = " ```"]
|
||||||
|
#[inline(always)]
|
||||||
|
pub fn read(&self) -> R<REG> {
|
||||||
|
R {
|
||||||
|
bits: self.register.get(),
|
||||||
|
_reg: marker::PhantomData,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
impl<REG: Resettable + Writable> Reg<REG> {
|
||||||
|
#[doc = " Writes the reset value to `Writable` register."]
|
||||||
|
#[doc = ""]
|
||||||
|
#[doc = " Resets the register to its initial state."]
|
||||||
|
#[inline(always)]
|
||||||
|
pub fn reset(&self) {
|
||||||
|
self.register.set(REG::RESET_VALUE)
|
||||||
|
}
|
||||||
|
#[doc = " Writes bits to a `Writable` register."]
|
||||||
|
#[doc = ""]
|
||||||
|
#[doc = " You can write raw bits into a register:"]
|
||||||
|
#[doc = " ```ignore"]
|
||||||
|
#[doc = " periph.reg.write(|w| unsafe { w.bits(rawbits) });"]
|
||||||
|
#[doc = " ```"]
|
||||||
|
#[doc = " or write only the fields you need:"]
|
||||||
|
#[doc = " ```ignore"]
|
||||||
|
#[doc = " periph.reg.write(|w| w"]
|
||||||
|
#[doc = " .field1().bits(newfield1bits)"]
|
||||||
|
#[doc = " .field2().set_bit()"]
|
||||||
|
#[doc = " .field3().variant(VARIANT)"]
|
||||||
|
#[doc = " );"]
|
||||||
|
#[doc = " ```"]
|
||||||
|
#[doc = " or an alternative way of saying the same:"]
|
||||||
|
#[doc = " ```ignore"]
|
||||||
|
#[doc = " periph.reg.write(|w| {"]
|
||||||
|
#[doc = " w.field1().bits(newfield1bits);"]
|
||||||
|
#[doc = " w.field2().set_bit();"]
|
||||||
|
#[doc = " w.field3().variant(VARIANT)"]
|
||||||
|
#[doc = " });"]
|
||||||
|
#[doc = " ```"]
|
||||||
|
#[doc = " In the latter case, other fields will be set to their reset value."]
|
||||||
|
#[inline(always)]
|
||||||
|
pub fn write<F>(&self, f: F) -> REG::Ux
|
||||||
|
where
|
||||||
|
F: FnOnce(&mut W<REG>) -> &mut W<REG>,
|
||||||
|
{
|
||||||
|
let value = f(&mut W {
|
||||||
|
bits: REG::RESET_VALUE & !REG::ONE_TO_MODIFY_FIELDS_BITMAP
|
||||||
|
| REG::ZERO_TO_MODIFY_FIELDS_BITMAP,
|
||||||
|
_reg: marker::PhantomData,
|
||||||
|
})
|
||||||
|
.bits;
|
||||||
|
self.register.set(value);
|
||||||
|
value
|
||||||
|
}
|
||||||
|
#[doc = " Writes bits to a `Writable` register and produce a value."]
|
||||||
|
#[doc = ""]
|
||||||
|
#[doc = " You can write raw bits into a register:"]
|
||||||
|
#[doc = " ```ignore"]
|
||||||
|
#[doc = " periph.reg.write_and(|w| unsafe { w.bits(rawbits); });"]
|
||||||
|
#[doc = " ```"]
|
||||||
|
#[doc = " or write only the fields you need:"]
|
||||||
|
#[doc = " ```ignore"]
|
||||||
|
#[doc = " periph.reg.write_and(|w| {"]
|
||||||
|
#[doc = " w.field1().bits(newfield1bits)"]
|
||||||
|
#[doc = " .field2().set_bit()"]
|
||||||
|
#[doc = " .field3().variant(VARIANT);"]
|
||||||
|
#[doc = " });"]
|
||||||
|
#[doc = " ```"]
|
||||||
|
#[doc = " or an alternative way of saying the same:"]
|
||||||
|
#[doc = " ```ignore"]
|
||||||
|
#[doc = " periph.reg.write_and(|w| {"]
|
||||||
|
#[doc = " w.field1().bits(newfield1bits);"]
|
||||||
|
#[doc = " w.field2().set_bit();"]
|
||||||
|
#[doc = " w.field3().variant(VARIANT);"]
|
||||||
|
#[doc = " });"]
|
||||||
|
#[doc = " ```"]
|
||||||
|
#[doc = " In the latter case, other fields will be set to their reset value."]
|
||||||
|
#[doc = ""]
|
||||||
|
#[doc = " Values can be returned from the closure:"]
|
||||||
|
#[doc = " ```ignore"]
|
||||||
|
#[doc = " let state = periph.reg.write_and(|w| State::set(w.field1()));"]
|
||||||
|
#[doc = " ```"]
|
||||||
|
#[inline(always)]
|
||||||
|
pub fn from_write<F, T>(&self, f: F) -> T
|
||||||
|
where
|
||||||
|
F: FnOnce(&mut W<REG>) -> T,
|
||||||
|
{
|
||||||
|
let mut writer = W {
|
||||||
|
bits: REG::RESET_VALUE & !REG::ONE_TO_MODIFY_FIELDS_BITMAP
|
||||||
|
| REG::ZERO_TO_MODIFY_FIELDS_BITMAP,
|
||||||
|
_reg: marker::PhantomData,
|
||||||
|
};
|
||||||
|
let result = f(&mut writer);
|
||||||
|
self.register.set(writer.bits);
|
||||||
|
result
|
||||||
|
}
|
||||||
|
}
|
||||||
|
impl<REG: Writable> Reg<REG> {
|
||||||
|
#[doc = " Writes 0 to a `Writable` register."]
|
||||||
|
#[doc = ""]
|
||||||
|
#[doc = " Similar to `write`, but unused bits will contain 0."]
|
||||||
|
#[doc = ""]
|
||||||
|
#[doc = " # Safety"]
|
||||||
|
#[doc = ""]
|
||||||
|
#[doc = " Unsafe to use with registers which don't allow to write 0."]
|
||||||
|
#[inline(always)]
|
||||||
|
pub unsafe fn write_with_zero<F>(&self, f: F) -> REG::Ux
|
||||||
|
where
|
||||||
|
F: FnOnce(&mut W<REG>) -> &mut W<REG>,
|
||||||
|
{
|
||||||
|
let value = f(&mut W {
|
||||||
|
bits: REG::Ux::default(),
|
||||||
|
_reg: marker::PhantomData,
|
||||||
|
})
|
||||||
|
.bits;
|
||||||
|
self.register.set(value);
|
||||||
|
value
|
||||||
|
}
|
||||||
|
#[doc = " Writes 0 to a `Writable` register and produces a value."]
|
||||||
|
#[doc = ""]
|
||||||
|
#[doc = " Similar to `write`, but unused bits will contain 0."]
|
||||||
|
#[doc = ""]
|
||||||
|
#[doc = " # Safety"]
|
||||||
|
#[doc = ""]
|
||||||
|
#[doc = " Unsafe to use with registers which don't allow to write 0."]
|
||||||
|
#[inline(always)]
|
||||||
|
pub unsafe fn from_write_with_zero<F, T>(&self, f: F) -> T
|
||||||
|
where
|
||||||
|
F: FnOnce(&mut W<REG>) -> T,
|
||||||
|
{
|
||||||
|
let mut writer = W {
|
||||||
|
bits: REG::Ux::default(),
|
||||||
|
_reg: marker::PhantomData,
|
||||||
|
};
|
||||||
|
let result = f(&mut writer);
|
||||||
|
self.register.set(writer.bits);
|
||||||
|
result
|
||||||
|
}
|
||||||
|
}
|
||||||
|
impl<REG: Readable + Writable> Reg<REG> {
|
||||||
|
#[doc = " Modifies the contents of the register by reading and then writing it."]
|
||||||
|
#[doc = ""]
|
||||||
|
#[doc = " E.g. to do a read-modify-write sequence to change parts of a register:"]
|
||||||
|
#[doc = " ```ignore"]
|
||||||
|
#[doc = " periph.reg.modify(|r, w| unsafe { w.bits("]
|
||||||
|
#[doc = " r.bits() | 3"]
|
||||||
|
#[doc = " ) });"]
|
||||||
|
#[doc = " ```"]
|
||||||
|
#[doc = " or"]
|
||||||
|
#[doc = " ```ignore"]
|
||||||
|
#[doc = " periph.reg.modify(|_, w| w"]
|
||||||
|
#[doc = " .field1().bits(newfield1bits)"]
|
||||||
|
#[doc = " .field2().set_bit()"]
|
||||||
|
#[doc = " .field3().variant(VARIANT)"]
|
||||||
|
#[doc = " );"]
|
||||||
|
#[doc = " ```"]
|
||||||
|
#[doc = " or an alternative way of saying the same:"]
|
||||||
|
#[doc = " ```ignore"]
|
||||||
|
#[doc = " periph.reg.modify(|_, w| {"]
|
||||||
|
#[doc = " w.field1().bits(newfield1bits);"]
|
||||||
|
#[doc = " w.field2().set_bit();"]
|
||||||
|
#[doc = " w.field3().variant(VARIANT)"]
|
||||||
|
#[doc = " });"]
|
||||||
|
#[doc = " ```"]
|
||||||
|
#[doc = " Other fields will have the value they had before the call to `modify`."]
|
||||||
|
#[inline(always)]
|
||||||
|
pub fn modify<F>(&self, f: F) -> REG::Ux
|
||||||
|
where
|
||||||
|
for<'w> F: FnOnce(&R<REG>, &'w mut W<REG>) -> &'w mut W<REG>,
|
||||||
|
{
|
||||||
|
let bits = self.register.get();
|
||||||
|
let value = f(
|
||||||
|
&R {
|
||||||
|
bits,
|
||||||
|
_reg: marker::PhantomData,
|
||||||
|
},
|
||||||
|
&mut W {
|
||||||
|
bits: bits & !REG::ONE_TO_MODIFY_FIELDS_BITMAP | REG::ZERO_TO_MODIFY_FIELDS_BITMAP,
|
||||||
|
_reg: marker::PhantomData,
|
||||||
|
},
|
||||||
|
)
|
||||||
|
.bits;
|
||||||
|
self.register.set(value);
|
||||||
|
value
|
||||||
|
}
|
||||||
|
#[doc = " Modifies the contents of the register by reading and then writing it"]
|
||||||
|
#[doc = " and produces a value."]
|
||||||
|
#[doc = ""]
|
||||||
|
#[doc = " E.g. to do a read-modify-write sequence to change parts of a register:"]
|
||||||
|
#[doc = " ```ignore"]
|
||||||
|
#[doc = " let bits = periph.reg.modify(|r, w| {"]
|
||||||
|
#[doc = " let new_bits = r.bits() | 3;"]
|
||||||
|
#[doc = " unsafe {"]
|
||||||
|
#[doc = " w.bits(new_bits);"]
|
||||||
|
#[doc = " }"]
|
||||||
|
#[doc = ""]
|
||||||
|
#[doc = " new_bits"]
|
||||||
|
#[doc = " });"]
|
||||||
|
#[doc = " ```"]
|
||||||
|
#[doc = " or"]
|
||||||
|
#[doc = " ```ignore"]
|
||||||
|
#[doc = " periph.reg.modify(|_, w| {"]
|
||||||
|
#[doc = " w.field1().bits(newfield1bits)"]
|
||||||
|
#[doc = " .field2().set_bit()"]
|
||||||
|
#[doc = " .field3().variant(VARIANT);"]
|
||||||
|
#[doc = " });"]
|
||||||
|
#[doc = " ```"]
|
||||||
|
#[doc = " or an alternative way of saying the same:"]
|
||||||
|
#[doc = " ```ignore"]
|
||||||
|
#[doc = " periph.reg.modify(|_, w| {"]
|
||||||
|
#[doc = " w.field1().bits(newfield1bits);"]
|
||||||
|
#[doc = " w.field2().set_bit();"]
|
||||||
|
#[doc = " w.field3().variant(VARIANT);"]
|
||||||
|
#[doc = " });"]
|
||||||
|
#[doc = " ```"]
|
||||||
|
#[doc = " Other fields will have the value they had before the call to `modify`."]
|
||||||
|
#[inline(always)]
|
||||||
|
pub fn from_modify<F, T>(&self, f: F) -> T
|
||||||
|
where
|
||||||
|
for<'w> F: FnOnce(&R<REG>, &'w mut W<REG>) -> T,
|
||||||
|
{
|
||||||
|
let bits = self.register.get();
|
||||||
|
let mut writer = W {
|
||||||
|
bits: bits & !REG::ONE_TO_MODIFY_FIELDS_BITMAP | REG::ZERO_TO_MODIFY_FIELDS_BITMAP,
|
||||||
|
_reg: marker::PhantomData,
|
||||||
|
};
|
||||||
|
let result = f(
|
||||||
|
&R {
|
||||||
|
bits,
|
||||||
|
_reg: marker::PhantomData,
|
||||||
|
},
|
||||||
|
&mut writer,
|
||||||
|
);
|
||||||
|
self.register.set(writer.bits);
|
||||||
|
result
|
||||||
|
}
|
||||||
|
}
|
||||||
|
impl<REG: Readable> core::fmt::Debug for crate::generic::Reg<REG>
|
||||||
|
where
|
||||||
|
R<REG>: core::fmt::Debug,
|
||||||
|
{
|
||||||
|
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
|
||||||
|
core::fmt::Debug::fmt(&self.read(), f)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
@ -41,6 +41,7 @@ impl<FI> BitReader<FI> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
#[must_use = "after creating `FieldWriter` you need to call field value setting method"]
|
||||||
pub struct FieldWriter<'a, REG, const WI: u8, FI = u8, Safety = Unsafe>
|
pub struct FieldWriter<'a, REG, const WI: u8, FI = u8, Safety = Unsafe>
|
||||||
where
|
where
|
||||||
REG: Writable + RegisterSpec,
|
REG: Writable + RegisterSpec,
|
||||||
@ -66,6 +67,7 @@ where
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
#[must_use = "after creating `BitWriter` you need to call bit setting method"]
|
||||||
pub struct BitWriter<'a, REG, FI = bool, M = BitM>
|
pub struct BitWriter<'a, REG, FI = bool, M = BitM>
|
||||||
where
|
where
|
||||||
REG: Writable + RegisterSpec,
|
REG: Writable + RegisterSpec,
|
||||||
|
@ -240,67 +240,67 @@ impl RegisterBlock {
|
|||||||
&self.perid
|
&self.perid
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
#[doc = "CTRL (rw) register accessor: Control Register\n\nYou can [`read`](crate::generic::Reg::read) this register and get [`ctrl::R`]. You can [`reset`](crate::generic::Reg::reset), [`write`](crate::generic::Reg::write), [`write_with_zero`](crate::generic::Reg::write_with_zero) this register using [`ctrl::W`]. You can also [`modify`](crate::generic::Reg::modify) this register. See [API](https://docs.rs/svd2rust/#read--modify--write-api).\n\nFor information about available fields see [`mod@ctrl`]
|
#[doc = "CTRL (rw) register accessor: Control Register\n\nYou can [`read`](crate::Reg::read) this register and get [`ctrl::R`]. You can [`reset`](crate::Reg::reset), [`write`](crate::Reg::write), [`write_with_zero`](crate::Reg::write_with_zero) this register using [`ctrl::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@ctrl`]
|
||||||
module"]
|
module"]
|
||||||
#[doc(alias = "CTRL")]
|
#[doc(alias = "CTRL")]
|
||||||
pub type Ctrl = crate::Reg<ctrl::CtrlSpec>;
|
pub type Ctrl = crate::Reg<ctrl::CtrlSpec>;
|
||||||
#[doc = "Control Register"]
|
#[doc = "Control Register"]
|
||||||
pub mod ctrl;
|
pub mod ctrl;
|
||||||
#[doc = "CLKSCALE (rw) register accessor: Clock Scale divide value\n\nYou can [`read`](crate::generic::Reg::read) this register and get [`clkscale::R`]. You can [`reset`](crate::generic::Reg::reset), [`write`](crate::generic::Reg::write), [`write_with_zero`](crate::generic::Reg::write_with_zero) this register using [`clkscale::W`]. You can also [`modify`](crate::generic::Reg::modify) this register. See [API](https://docs.rs/svd2rust/#read--modify--write-api).\n\nFor information about available fields see [`mod@clkscale`]
|
#[doc = "CLKSCALE (rw) register accessor: Clock Scale divide value\n\nYou can [`read`](crate::Reg::read) this register and get [`clkscale::R`]. You can [`reset`](crate::Reg::reset), [`write`](crate::Reg::write), [`write_with_zero`](crate::Reg::write_with_zero) this register using [`clkscale::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@clkscale`]
|
||||||
module"]
|
module"]
|
||||||
#[doc(alias = "CLKSCALE")]
|
#[doc(alias = "CLKSCALE")]
|
||||||
pub type Clkscale = crate::Reg<clkscale::ClkscaleSpec>;
|
pub type Clkscale = crate::Reg<clkscale::ClkscaleSpec>;
|
||||||
#[doc = "Clock Scale divide value"]
|
#[doc = "Clock Scale divide value"]
|
||||||
pub mod clkscale;
|
pub mod clkscale;
|
||||||
#[doc = "WORDS (rw) register accessor: Word Count value\n\nYou can [`read`](crate::generic::Reg::read) this register and get [`words::R`]. You can [`reset`](crate::generic::Reg::reset), [`write`](crate::generic::Reg::write), [`write_with_zero`](crate::generic::Reg::write_with_zero) this register using [`words::W`]. You can also [`modify`](crate::generic::Reg::modify) this register. See [API](https://docs.rs/svd2rust/#read--modify--write-api).\n\nFor information about available fields see [`mod@words`]
|
#[doc = "WORDS (rw) register accessor: Word Count value\n\nYou can [`read`](crate::Reg::read) this register and get [`words::R`]. You can [`reset`](crate::Reg::reset), [`write`](crate::Reg::write), [`write_with_zero`](crate::Reg::write_with_zero) this register using [`words::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@words`]
|
||||||
module"]
|
module"]
|
||||||
#[doc(alias = "WORDS")]
|
#[doc(alias = "WORDS")]
|
||||||
pub type Words = crate::Reg<words::WordsSpec>;
|
pub type Words = crate::Reg<words::WordsSpec>;
|
||||||
#[doc = "Word Count value"]
|
#[doc = "Word Count value"]
|
||||||
pub mod words;
|
pub mod words;
|
||||||
#[doc = "ADDRESS (rw) register accessor: I2C Address value\n\nYou can [`read`](crate::generic::Reg::read) this register and get [`address::R`]. You can [`reset`](crate::generic::Reg::reset), [`write`](crate::generic::Reg::write), [`write_with_zero`](crate::generic::Reg::write_with_zero) this register using [`address::W`]. You can also [`modify`](crate::generic::Reg::modify) this register. See [API](https://docs.rs/svd2rust/#read--modify--write-api).\n\nFor information about available fields see [`mod@address`]
|
#[doc = "ADDRESS (rw) register accessor: I2C Address value\n\nYou can [`read`](crate::Reg::read) this register and get [`address::R`]. You can [`reset`](crate::Reg::reset), [`write`](crate::Reg::write), [`write_with_zero`](crate::Reg::write_with_zero) this register using [`address::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@address`]
|
||||||
module"]
|
module"]
|
||||||
#[doc(alias = "ADDRESS")]
|
#[doc(alias = "ADDRESS")]
|
||||||
pub type Address = crate::Reg<address::AddressSpec>;
|
pub type Address = crate::Reg<address::AddressSpec>;
|
||||||
#[doc = "I2C Address value"]
|
#[doc = "I2C Address value"]
|
||||||
pub mod address;
|
pub mod address;
|
||||||
#[doc = "DATA (rw) register accessor: Data Input/Output\n\nYou can [`read`](crate::generic::Reg::read) this register and get [`data::R`]. You can [`reset`](crate::generic::Reg::reset), [`write`](crate::generic::Reg::write), [`write_with_zero`](crate::generic::Reg::write_with_zero) this register using [`data::W`]. You can also [`modify`](crate::generic::Reg::modify) this register. See [API](https://docs.rs/svd2rust/#read--modify--write-api).\n\nFor information about available fields see [`mod@data`]
|
#[doc = "DATA (rw) register accessor: Data Input/Output\n\nYou can [`read`](crate::Reg::read) this register and get [`data::R`]. You can [`reset`](crate::Reg::reset), [`write`](crate::Reg::write), [`write_with_zero`](crate::Reg::write_with_zero) this register using [`data::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@data`]
|
||||||
module"]
|
module"]
|
||||||
#[doc(alias = "DATA")]
|
#[doc(alias = "DATA")]
|
||||||
pub type Data = crate::Reg<data::DataSpec>;
|
pub type Data = crate::Reg<data::DataSpec>;
|
||||||
#[doc = "Data Input/Output"]
|
#[doc = "Data Input/Output"]
|
||||||
pub mod data;
|
pub mod data;
|
||||||
#[doc = "CMD (rw) register accessor: Command Register\n\nYou can [`read`](crate::generic::Reg::read) this register and get [`cmd::R`]. You can [`reset`](crate::generic::Reg::reset), [`write`](crate::generic::Reg::write), [`write_with_zero`](crate::generic::Reg::write_with_zero) this register using [`cmd::W`]. You can also [`modify`](crate::generic::Reg::modify) this register. See [API](https://docs.rs/svd2rust/#read--modify--write-api).\n\nFor information about available fields see [`mod@cmd`]
|
#[doc = "CMD (rw) register accessor: Command Register\n\nYou can [`read`](crate::Reg::read) this register and get [`cmd::R`]. You can [`reset`](crate::Reg::reset), [`write`](crate::Reg::write), [`write_with_zero`](crate::Reg::write_with_zero) this register using [`cmd::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@cmd`]
|
||||||
module"]
|
module"]
|
||||||
#[doc(alias = "CMD")]
|
#[doc(alias = "CMD")]
|
||||||
pub type Cmd = crate::Reg<cmd::CmdSpec>;
|
pub type Cmd = crate::Reg<cmd::CmdSpec>;
|
||||||
#[doc = "Command Register"]
|
#[doc = "Command Register"]
|
||||||
pub mod cmd;
|
pub mod cmd;
|
||||||
#[doc = "STATUS (r) register accessor: I2C Controller Status Register\n\nYou can [`read`](crate::generic::Reg::read) this register and get [`status::R`]. See [API](https://docs.rs/svd2rust/#read--modify--write-api).\n\nFor information about available fields see [`mod@status`]
|
#[doc = "STATUS (r) register accessor: I2C Controller Status Register\n\nYou can [`read`](crate::Reg::read) this register and get [`status::R`]. See [API](https://docs.rs/svd2rust/#read--modify--write-api).\n\nFor information about available fields see [`mod@status`]
|
||||||
module"]
|
module"]
|
||||||
#[doc(alias = "STATUS")]
|
#[doc(alias = "STATUS")]
|
||||||
pub type Status = crate::Reg<status::StatusSpec>;
|
pub type Status = crate::Reg<status::StatusSpec>;
|
||||||
#[doc = "I2C Controller Status Register"]
|
#[doc = "I2C Controller Status Register"]
|
||||||
pub mod status;
|
pub mod status;
|
||||||
#[doc = "STATE (r) register accessor: Internal STATE of I2C Master Controller\n\nYou can [`read`](crate::generic::Reg::read) this register and get [`state::R`]. See [API](https://docs.rs/svd2rust/#read--modify--write-api).\n\nFor information about available fields see [`mod@state`]
|
#[doc = "STATE (r) register accessor: Internal STATE of I2C Master Controller\n\nYou can [`read`](crate::Reg::read) this register and get [`state::R`]. See [API](https://docs.rs/svd2rust/#read--modify--write-api).\n\nFor information about available fields see [`mod@state`]
|
||||||
module"]
|
module"]
|
||||||
#[doc(alias = "STATE")]
|
#[doc(alias = "STATE")]
|
||||||
pub type State = crate::Reg<state::StateSpec>;
|
pub type State = crate::Reg<state::StateSpec>;
|
||||||
#[doc = "Internal STATE of I2C Master Controller"]
|
#[doc = "Internal STATE of I2C Master Controller"]
|
||||||
pub mod state;
|
pub mod state;
|
||||||
#[doc = "TXCOUNT (r) register accessor: TX Count Register\n\nYou can [`read`](crate::generic::Reg::read) this register and get [`txcount::R`]. See [API](https://docs.rs/svd2rust/#read--modify--write-api).\n\nFor information about available fields see [`mod@txcount`]
|
#[doc = "TXCOUNT (r) register accessor: TX Count Register\n\nYou can [`read`](crate::Reg::read) this register and get [`txcount::R`]. See [API](https://docs.rs/svd2rust/#read--modify--write-api).\n\nFor information about available fields see [`mod@txcount`]
|
||||||
module"]
|
module"]
|
||||||
#[doc(alias = "TXCOUNT")]
|
#[doc(alias = "TXCOUNT")]
|
||||||
pub type Txcount = crate::Reg<txcount::TxcountSpec>;
|
pub type Txcount = crate::Reg<txcount::TxcountSpec>;
|
||||||
#[doc = "TX Count Register"]
|
#[doc = "TX Count Register"]
|
||||||
pub mod txcount;
|
pub mod txcount;
|
||||||
#[doc = "RXCOUNT (r) register accessor: RX Count Register\n\nYou can [`read`](crate::generic::Reg::read) this register and get [`rxcount::R`]. See [API](https://docs.rs/svd2rust/#read--modify--write-api).\n\nFor information about available fields see [`mod@rxcount`]
|
#[doc = "RXCOUNT (r) register accessor: RX Count Register\n\nYou can [`read`](crate::Reg::read) this register and get [`rxcount::R`]. See [API](https://docs.rs/svd2rust/#read--modify--write-api).\n\nFor information about available fields see [`mod@rxcount`]
|
||||||
module"]
|
module"]
|
||||||
#[doc(alias = "RXCOUNT")]
|
#[doc(alias = "RXCOUNT")]
|
||||||
pub type Rxcount = crate::Reg<rxcount::RxcountSpec>;
|
pub type Rxcount = crate::Reg<rxcount::RxcountSpec>;
|
||||||
#[doc = "RX Count Register"]
|
#[doc = "RX Count Register"]
|
||||||
pub mod rxcount;
|
pub mod rxcount;
|
||||||
#[doc = "IRQ_ENB (rw) register accessor: Interrupt Enable Register\n\nYou can [`read`](crate::generic::Reg::read) this register and get [`irq_enb::R`]. You can [`reset`](crate::generic::Reg::reset), [`write`](crate::generic::Reg::write), [`write_with_zero`](crate::generic::Reg::write_with_zero) this register using [`irq_enb::W`]. You can also [`modify`](crate::generic::Reg::modify) this register. See [API](https://docs.rs/svd2rust/#read--modify--write-api).\n\nFor information about available fields see [`mod@irq_enb`]
|
#[doc = "IRQ_ENB (rw) register accessor: Interrupt Enable Register\n\nYou can [`read`](crate::Reg::read) this register and get [`irq_enb::R`]. You can [`reset`](crate::Reg::reset), [`write`](crate::Reg::write), [`write_with_zero`](crate::Reg::write_with_zero) this register using [`irq_enb::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_enb`]
|
||||||
module"]
|
module"]
|
||||||
#[doc(alias = "IRQ_ENB")]
|
#[doc(alias = "IRQ_ENB")]
|
||||||
pub type IrqEnb = crate::Reg<irq_enb::IrqEnbSpec>;
|
pub type IrqEnb = crate::Reg<irq_enb::IrqEnbSpec>;
|
||||||
@ -312,97 +312,97 @@ pub use irq_enb as irq_clr;
|
|||||||
pub use IrqEnb as IrqRaw;
|
pub use IrqEnb as IrqRaw;
|
||||||
pub use IrqEnb as IrqEnd;
|
pub use IrqEnb as IrqEnd;
|
||||||
pub use IrqEnb as IrqClr;
|
pub use IrqEnb as IrqClr;
|
||||||
#[doc = "RXFIFOIRQTRG (rw) register accessor: Rx FIFO IRQ Trigger Level\n\nYou can [`read`](crate::generic::Reg::read) this register and get [`rxfifoirqtrg::R`]. You can [`reset`](crate::generic::Reg::reset), [`write`](crate::generic::Reg::write), [`write_with_zero`](crate::generic::Reg::write_with_zero) this register using [`rxfifoirqtrg::W`]. You can also [`modify`](crate::generic::Reg::modify) this register. See [API](https://docs.rs/svd2rust/#read--modify--write-api).\n\nFor information about available fields see [`mod@rxfifoirqtrg`]
|
#[doc = "RXFIFOIRQTRG (rw) register accessor: Rx FIFO IRQ Trigger Level\n\nYou can [`read`](crate::Reg::read) this register and get [`rxfifoirqtrg::R`]. You can [`reset`](crate::Reg::reset), [`write`](crate::Reg::write), [`write_with_zero`](crate::Reg::write_with_zero) this register using [`rxfifoirqtrg::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@rxfifoirqtrg`]
|
||||||
module"]
|
module"]
|
||||||
#[doc(alias = "RXFIFOIRQTRG")]
|
#[doc(alias = "RXFIFOIRQTRG")]
|
||||||
pub type Rxfifoirqtrg = crate::Reg<rxfifoirqtrg::RxfifoirqtrgSpec>;
|
pub type Rxfifoirqtrg = crate::Reg<rxfifoirqtrg::RxfifoirqtrgSpec>;
|
||||||
#[doc = "Rx FIFO IRQ Trigger Level"]
|
#[doc = "Rx FIFO IRQ Trigger Level"]
|
||||||
pub mod rxfifoirqtrg;
|
pub mod rxfifoirqtrg;
|
||||||
#[doc = "TXFIFOIRQTRG (rw) register accessor: Tx FIFO IRQ Trigger Level\n\nYou can [`read`](crate::generic::Reg::read) this register and get [`txfifoirqtrg::R`]. You can [`reset`](crate::generic::Reg::reset), [`write`](crate::generic::Reg::write), [`write_with_zero`](crate::generic::Reg::write_with_zero) this register using [`txfifoirqtrg::W`]. You can also [`modify`](crate::generic::Reg::modify) this register. See [API](https://docs.rs/svd2rust/#read--modify--write-api).\n\nFor information about available fields see [`mod@txfifoirqtrg`]
|
#[doc = "TXFIFOIRQTRG (rw) register accessor: Tx FIFO IRQ Trigger Level\n\nYou can [`read`](crate::Reg::read) this register and get [`txfifoirqtrg::R`]. You can [`reset`](crate::Reg::reset), [`write`](crate::Reg::write), [`write_with_zero`](crate::Reg::write_with_zero) this register using [`txfifoirqtrg::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@txfifoirqtrg`]
|
||||||
module"]
|
module"]
|
||||||
#[doc(alias = "TXFIFOIRQTRG")]
|
#[doc(alias = "TXFIFOIRQTRG")]
|
||||||
pub type Txfifoirqtrg = crate::Reg<txfifoirqtrg::TxfifoirqtrgSpec>;
|
pub type Txfifoirqtrg = crate::Reg<txfifoirqtrg::TxfifoirqtrgSpec>;
|
||||||
#[doc = "Tx FIFO IRQ Trigger Level"]
|
#[doc = "Tx FIFO IRQ Trigger Level"]
|
||||||
pub mod txfifoirqtrg;
|
pub mod txfifoirqtrg;
|
||||||
#[doc = "FIFO_CLR (w) register accessor: Clear FIFO Register\n\nYou can [`reset`](crate::generic::Reg::reset), [`write`](crate::generic::Reg::write), [`write_with_zero`](crate::generic::Reg::write_with_zero) this register using [`fifo_clr::W`]. See [API](https://docs.rs/svd2rust/#read--modify--write-api).\n\nFor information about available fields see [`mod@fifo_clr`]
|
#[doc = "FIFO_CLR (w) register accessor: Clear FIFO Register\n\nYou can [`reset`](crate::Reg::reset), [`write`](crate::Reg::write), [`write_with_zero`](crate::Reg::write_with_zero) this register using [`fifo_clr::W`]. See [API](https://docs.rs/svd2rust/#read--modify--write-api).\n\nFor information about available fields see [`mod@fifo_clr`]
|
||||||
module"]
|
module"]
|
||||||
#[doc(alias = "FIFO_CLR")]
|
#[doc(alias = "FIFO_CLR")]
|
||||||
pub type FifoClr = crate::Reg<fifo_clr::FifoClrSpec>;
|
pub type FifoClr = crate::Reg<fifo_clr::FifoClrSpec>;
|
||||||
#[doc = "Clear FIFO Register"]
|
#[doc = "Clear FIFO Register"]
|
||||||
pub mod fifo_clr;
|
pub mod fifo_clr;
|
||||||
#[doc = "TMCONFIG (rw) register accessor: Timing Config Register\n\nYou can [`read`](crate::generic::Reg::read) this register and get [`tmconfig::R`]. You can [`reset`](crate::generic::Reg::reset), [`write`](crate::generic::Reg::write), [`write_with_zero`](crate::generic::Reg::write_with_zero) this register using [`tmconfig::W`]. You can also [`modify`](crate::generic::Reg::modify) this register. See [API](https://docs.rs/svd2rust/#read--modify--write-api).\n\nFor information about available fields see [`mod@tmconfig`]
|
#[doc = "TMCONFIG (rw) register accessor: Timing Config Register\n\nYou can [`read`](crate::Reg::read) this register and get [`tmconfig::R`]. You can [`reset`](crate::Reg::reset), [`write`](crate::Reg::write), [`write_with_zero`](crate::Reg::write_with_zero) this register using [`tmconfig::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@tmconfig`]
|
||||||
module"]
|
module"]
|
||||||
#[doc(alias = "TMCONFIG")]
|
#[doc(alias = "TMCONFIG")]
|
||||||
pub type Tmconfig = crate::Reg<tmconfig::TmconfigSpec>;
|
pub type Tmconfig = crate::Reg<tmconfig::TmconfigSpec>;
|
||||||
#[doc = "Timing Config Register"]
|
#[doc = "Timing Config Register"]
|
||||||
pub mod tmconfig;
|
pub mod tmconfig;
|
||||||
#[doc = "CLKTOLIMIT (rw) register accessor: Clock Low Timeout Limit Register\n\nYou can [`read`](crate::generic::Reg::read) this register and get [`clktolimit::R`]. You can [`reset`](crate::generic::Reg::reset), [`write`](crate::generic::Reg::write), [`write_with_zero`](crate::generic::Reg::write_with_zero) this register using [`clktolimit::W`]. You can also [`modify`](crate::generic::Reg::modify) this register. See [API](https://docs.rs/svd2rust/#read--modify--write-api).\n\nFor information about available fields see [`mod@clktolimit`]
|
#[doc = "CLKTOLIMIT (rw) register accessor: Clock Low Timeout Limit Register\n\nYou can [`read`](crate::Reg::read) this register and get [`clktolimit::R`]. You can [`reset`](crate::Reg::reset), [`write`](crate::Reg::write), [`write_with_zero`](crate::Reg::write_with_zero) this register using [`clktolimit::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@clktolimit`]
|
||||||
module"]
|
module"]
|
||||||
#[doc(alias = "CLKTOLIMIT")]
|
#[doc(alias = "CLKTOLIMIT")]
|
||||||
pub type Clktolimit = crate::Reg<clktolimit::ClktolimitSpec>;
|
pub type Clktolimit = crate::Reg<clktolimit::ClktolimitSpec>;
|
||||||
#[doc = "Clock Low Timeout Limit Register"]
|
#[doc = "Clock Low Timeout Limit Register"]
|
||||||
pub mod clktolimit;
|
pub mod clktolimit;
|
||||||
#[doc = "S0_CTRL (rw) register accessor: Slave Control Register\n\nYou can [`read`](crate::generic::Reg::read) this register and get [`s0_ctrl::R`]. You can [`reset`](crate::generic::Reg::reset), [`write`](crate::generic::Reg::write), [`write_with_zero`](crate::generic::Reg::write_with_zero) this register using [`s0_ctrl::W`]. You can also [`modify`](crate::generic::Reg::modify) this register. See [API](https://docs.rs/svd2rust/#read--modify--write-api).\n\nFor information about available fields see [`mod@s0_ctrl`]
|
#[doc = "S0_CTRL (rw) register accessor: Slave Control Register\n\nYou can [`read`](crate::Reg::read) this register and get [`s0_ctrl::R`]. You can [`reset`](crate::Reg::reset), [`write`](crate::Reg::write), [`write_with_zero`](crate::Reg::write_with_zero) this register using [`s0_ctrl::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@s0_ctrl`]
|
||||||
module"]
|
module"]
|
||||||
#[doc(alias = "S0_CTRL")]
|
#[doc(alias = "S0_CTRL")]
|
||||||
pub type S0Ctrl = crate::Reg<s0_ctrl::S0CtrlSpec>;
|
pub type S0Ctrl = crate::Reg<s0_ctrl::S0CtrlSpec>;
|
||||||
#[doc = "Slave Control Register"]
|
#[doc = "Slave Control Register"]
|
||||||
pub mod s0_ctrl;
|
pub mod s0_ctrl;
|
||||||
#[doc = "S0_MAXWORDS (rw) register accessor: Slave MaxWords Register\n\nYou can [`read`](crate::generic::Reg::read) this register and get [`s0_maxwords::R`]. You can [`reset`](crate::generic::Reg::reset), [`write`](crate::generic::Reg::write), [`write_with_zero`](crate::generic::Reg::write_with_zero) this register using [`s0_maxwords::W`]. You can also [`modify`](crate::generic::Reg::modify) this register. See [API](https://docs.rs/svd2rust/#read--modify--write-api).\n\nFor information about available fields see [`mod@s0_maxwords`]
|
#[doc = "S0_MAXWORDS (rw) register accessor: Slave MaxWords Register\n\nYou can [`read`](crate::Reg::read) this register and get [`s0_maxwords::R`]. You can [`reset`](crate::Reg::reset), [`write`](crate::Reg::write), [`write_with_zero`](crate::Reg::write_with_zero) this register using [`s0_maxwords::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@s0_maxwords`]
|
||||||
module"]
|
module"]
|
||||||
#[doc(alias = "S0_MAXWORDS")]
|
#[doc(alias = "S0_MAXWORDS")]
|
||||||
pub type S0Maxwords = crate::Reg<s0_maxwords::S0MaxwordsSpec>;
|
pub type S0Maxwords = crate::Reg<s0_maxwords::S0MaxwordsSpec>;
|
||||||
#[doc = "Slave MaxWords Register"]
|
#[doc = "Slave MaxWords Register"]
|
||||||
pub mod s0_maxwords;
|
pub mod s0_maxwords;
|
||||||
#[doc = "S0_ADDRESS (rw) register accessor: Slave I2C Address Value\n\nYou can [`read`](crate::generic::Reg::read) this register and get [`s0_address::R`]. You can [`reset`](crate::generic::Reg::reset), [`write`](crate::generic::Reg::write), [`write_with_zero`](crate::generic::Reg::write_with_zero) this register using [`s0_address::W`]. You can also [`modify`](crate::generic::Reg::modify) this register. See [API](https://docs.rs/svd2rust/#read--modify--write-api).\n\nFor information about available fields see [`mod@s0_address`]
|
#[doc = "S0_ADDRESS (rw) register accessor: Slave I2C Address Value\n\nYou can [`read`](crate::Reg::read) this register and get [`s0_address::R`]. You can [`reset`](crate::Reg::reset), [`write`](crate::Reg::write), [`write_with_zero`](crate::Reg::write_with_zero) this register using [`s0_address::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@s0_address`]
|
||||||
module"]
|
module"]
|
||||||
#[doc(alias = "S0_ADDRESS")]
|
#[doc(alias = "S0_ADDRESS")]
|
||||||
pub type S0Address = crate::Reg<s0_address::S0AddressSpec>;
|
pub type S0Address = crate::Reg<s0_address::S0AddressSpec>;
|
||||||
#[doc = "Slave I2C Address Value"]
|
#[doc = "Slave I2C Address Value"]
|
||||||
pub mod s0_address;
|
pub mod s0_address;
|
||||||
#[doc = "S0_ADDRESSMASK (rw) register accessor: Slave I2C Address Mask value\n\nYou can [`read`](crate::generic::Reg::read) this register and get [`s0_addressmask::R`]. You can [`reset`](crate::generic::Reg::reset), [`write`](crate::generic::Reg::write), [`write_with_zero`](crate::generic::Reg::write_with_zero) this register using [`s0_addressmask::W`]. You can also [`modify`](crate::generic::Reg::modify) this register. See [API](https://docs.rs/svd2rust/#read--modify--write-api).\n\nFor information about available fields see [`mod@s0_addressmask`]
|
#[doc = "S0_ADDRESSMASK (rw) register accessor: Slave I2C Address Mask value\n\nYou can [`read`](crate::Reg::read) this register and get [`s0_addressmask::R`]. You can [`reset`](crate::Reg::reset), [`write`](crate::Reg::write), [`write_with_zero`](crate::Reg::write_with_zero) this register using [`s0_addressmask::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@s0_addressmask`]
|
||||||
module"]
|
module"]
|
||||||
#[doc(alias = "S0_ADDRESSMASK")]
|
#[doc(alias = "S0_ADDRESSMASK")]
|
||||||
pub type S0Addressmask = crate::Reg<s0_addressmask::S0AddressmaskSpec>;
|
pub type S0Addressmask = crate::Reg<s0_addressmask::S0AddressmaskSpec>;
|
||||||
#[doc = "Slave I2C Address Mask value"]
|
#[doc = "Slave I2C Address Mask value"]
|
||||||
pub mod s0_addressmask;
|
pub mod s0_addressmask;
|
||||||
#[doc = "S0_DATA (rw) register accessor: Slave Data Input/Output\n\nYou can [`read`](crate::generic::Reg::read) this register and get [`s0_data::R`]. You can [`reset`](crate::generic::Reg::reset), [`write`](crate::generic::Reg::write), [`write_with_zero`](crate::generic::Reg::write_with_zero) this register using [`s0_data::W`]. You can also [`modify`](crate::generic::Reg::modify) this register. See [API](https://docs.rs/svd2rust/#read--modify--write-api).\n\nFor information about available fields see [`mod@s0_data`]
|
#[doc = "S0_DATA (rw) register accessor: Slave Data Input/Output\n\nYou can [`read`](crate::Reg::read) this register and get [`s0_data::R`]. You can [`reset`](crate::Reg::reset), [`write`](crate::Reg::write), [`write_with_zero`](crate::Reg::write_with_zero) this register using [`s0_data::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@s0_data`]
|
||||||
module"]
|
module"]
|
||||||
#[doc(alias = "S0_DATA")]
|
#[doc(alias = "S0_DATA")]
|
||||||
pub type S0Data = crate::Reg<s0_data::S0DataSpec>;
|
pub type S0Data = crate::Reg<s0_data::S0DataSpec>;
|
||||||
#[doc = "Slave Data Input/Output"]
|
#[doc = "Slave Data Input/Output"]
|
||||||
pub mod s0_data;
|
pub mod s0_data;
|
||||||
#[doc = "S0_LASTADDRESS (r) register accessor: Slave I2C Last Address value\n\nYou can [`read`](crate::generic::Reg::read) this register and get [`s0_lastaddress::R`]. See [API](https://docs.rs/svd2rust/#read--modify--write-api).\n\nFor information about available fields see [`mod@s0_lastaddress`]
|
#[doc = "S0_LASTADDRESS (r) register accessor: Slave I2C Last Address value\n\nYou can [`read`](crate::Reg::read) this register and get [`s0_lastaddress::R`]. See [API](https://docs.rs/svd2rust/#read--modify--write-api).\n\nFor information about available fields see [`mod@s0_lastaddress`]
|
||||||
module"]
|
module"]
|
||||||
#[doc(alias = "S0_LASTADDRESS")]
|
#[doc(alias = "S0_LASTADDRESS")]
|
||||||
pub type S0Lastaddress = crate::Reg<s0_lastaddress::S0LastaddressSpec>;
|
pub type S0Lastaddress = crate::Reg<s0_lastaddress::S0LastaddressSpec>;
|
||||||
#[doc = "Slave I2C Last Address value"]
|
#[doc = "Slave I2C Last Address value"]
|
||||||
pub mod s0_lastaddress;
|
pub mod s0_lastaddress;
|
||||||
#[doc = "S0_STATUS (r) register accessor: Slave I2C Controller Status Register\n\nYou can [`read`](crate::generic::Reg::read) this register and get [`s0_status::R`]. See [API](https://docs.rs/svd2rust/#read--modify--write-api).\n\nFor information about available fields see [`mod@s0_status`]
|
#[doc = "S0_STATUS (r) register accessor: Slave I2C Controller Status Register\n\nYou can [`read`](crate::Reg::read) this register and get [`s0_status::R`]. See [API](https://docs.rs/svd2rust/#read--modify--write-api).\n\nFor information about available fields see [`mod@s0_status`]
|
||||||
module"]
|
module"]
|
||||||
#[doc(alias = "S0_STATUS")]
|
#[doc(alias = "S0_STATUS")]
|
||||||
pub type S0Status = crate::Reg<s0_status::S0StatusSpec>;
|
pub type S0Status = crate::Reg<s0_status::S0StatusSpec>;
|
||||||
#[doc = "Slave I2C Controller Status Register"]
|
#[doc = "Slave I2C Controller Status Register"]
|
||||||
pub mod s0_status;
|
pub mod s0_status;
|
||||||
#[doc = "S0_STATE (r) register accessor: Internal STATE of I2C Slave Controller\n\nYou can [`read`](crate::generic::Reg::read) this register and get [`s0_state::R`]. See [API](https://docs.rs/svd2rust/#read--modify--write-api).\n\nFor information about available fields see [`mod@s0_state`]
|
#[doc = "S0_STATE (r) register accessor: Internal STATE of I2C Slave Controller\n\nYou can [`read`](crate::Reg::read) this register and get [`s0_state::R`]. See [API](https://docs.rs/svd2rust/#read--modify--write-api).\n\nFor information about available fields see [`mod@s0_state`]
|
||||||
module"]
|
module"]
|
||||||
#[doc(alias = "S0_STATE")]
|
#[doc(alias = "S0_STATE")]
|
||||||
pub type S0State = crate::Reg<s0_state::S0StateSpec>;
|
pub type S0State = crate::Reg<s0_state::S0StateSpec>;
|
||||||
#[doc = "Internal STATE of I2C Slave Controller"]
|
#[doc = "Internal STATE of I2C Slave Controller"]
|
||||||
pub mod s0_state;
|
pub mod s0_state;
|
||||||
#[doc = "S0_TXCOUNT (r) register accessor: Slave TX Count Register\n\nYou can [`read`](crate::generic::Reg::read) this register and get [`s0_txcount::R`]. See [API](https://docs.rs/svd2rust/#read--modify--write-api).\n\nFor information about available fields see [`mod@s0_txcount`]
|
#[doc = "S0_TXCOUNT (r) register accessor: Slave TX Count Register\n\nYou can [`read`](crate::Reg::read) this register and get [`s0_txcount::R`]. See [API](https://docs.rs/svd2rust/#read--modify--write-api).\n\nFor information about available fields see [`mod@s0_txcount`]
|
||||||
module"]
|
module"]
|
||||||
#[doc(alias = "S0_TXCOUNT")]
|
#[doc(alias = "S0_TXCOUNT")]
|
||||||
pub type S0Txcount = crate::Reg<s0_txcount::S0TxcountSpec>;
|
pub type S0Txcount = crate::Reg<s0_txcount::S0TxcountSpec>;
|
||||||
#[doc = "Slave TX Count Register"]
|
#[doc = "Slave TX Count Register"]
|
||||||
pub mod s0_txcount;
|
pub mod s0_txcount;
|
||||||
#[doc = "S0_RXCOUNT (r) register accessor: Slave RX Count Register\n\nYou can [`read`](crate::generic::Reg::read) this register and get [`s0_rxcount::R`]. See [API](https://docs.rs/svd2rust/#read--modify--write-api).\n\nFor information about available fields see [`mod@s0_rxcount`]
|
#[doc = "S0_RXCOUNT (r) register accessor: Slave RX Count Register\n\nYou can [`read`](crate::Reg::read) this register and get [`s0_rxcount::R`]. See [API](https://docs.rs/svd2rust/#read--modify--write-api).\n\nFor information about available fields see [`mod@s0_rxcount`]
|
||||||
module"]
|
module"]
|
||||||
#[doc(alias = "S0_RXCOUNT")]
|
#[doc(alias = "S0_RXCOUNT")]
|
||||||
pub type S0Rxcount = crate::Reg<s0_rxcount::S0RxcountSpec>;
|
pub type S0Rxcount = crate::Reg<s0_rxcount::S0RxcountSpec>;
|
||||||
#[doc = "Slave RX Count Register"]
|
#[doc = "Slave RX Count Register"]
|
||||||
pub mod s0_rxcount;
|
pub mod s0_rxcount;
|
||||||
#[doc = "S0_IRQ_ENB (rw) register accessor: Slave Interrupt Enable Register\n\nYou can [`read`](crate::generic::Reg::read) this register and get [`s0_irq_enb::R`]. You can [`reset`](crate::generic::Reg::reset), [`write`](crate::generic::Reg::write), [`write_with_zero`](crate::generic::Reg::write_with_zero) this register using [`s0_irq_enb::W`]. You can also [`modify`](crate::generic::Reg::modify) this register. See [API](https://docs.rs/svd2rust/#read--modify--write-api).\n\nFor information about available fields see [`mod@s0_irq_enb`]
|
#[doc = "S0_IRQ_ENB (rw) register accessor: Slave Interrupt Enable Register\n\nYou can [`read`](crate::Reg::read) this register and get [`s0_irq_enb::R`]. You can [`reset`](crate::Reg::reset), [`write`](crate::Reg::write), [`write_with_zero`](crate::Reg::write_with_zero) this register using [`s0_irq_enb::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@s0_irq_enb`]
|
||||||
module"]
|
module"]
|
||||||
#[doc(alias = "S0_IRQ_ENB")]
|
#[doc(alias = "S0_IRQ_ENB")]
|
||||||
pub type S0IrqEnb = crate::Reg<s0_irq_enb::S0IrqEnbSpec>;
|
pub type S0IrqEnb = crate::Reg<s0_irq_enb::S0IrqEnbSpec>;
|
||||||
@ -414,37 +414,37 @@ pub use s0_irq_enb as s0_irq_clr;
|
|||||||
pub use S0IrqEnb as S0IrqRaw;
|
pub use S0IrqEnb as S0IrqRaw;
|
||||||
pub use S0IrqEnb as S0IrqEnd;
|
pub use S0IrqEnb as S0IrqEnd;
|
||||||
pub use S0IrqEnb as S0IrqClr;
|
pub use S0IrqEnb as S0IrqClr;
|
||||||
#[doc = "S0_RXFIFOIRQTRG (rw) register accessor: Slave Rx FIFO IRQ Trigger Level\n\nYou can [`read`](crate::generic::Reg::read) this register and get [`s0_rxfifoirqtrg::R`]. You can [`reset`](crate::generic::Reg::reset), [`write`](crate::generic::Reg::write), [`write_with_zero`](crate::generic::Reg::write_with_zero) this register using [`s0_rxfifoirqtrg::W`]. You can also [`modify`](crate::generic::Reg::modify) this register. See [API](https://docs.rs/svd2rust/#read--modify--write-api).\n\nFor information about available fields see [`mod@s0_rxfifoirqtrg`]
|
#[doc = "S0_RXFIFOIRQTRG (rw) register accessor: Slave Rx FIFO IRQ Trigger Level\n\nYou can [`read`](crate::Reg::read) this register and get [`s0_rxfifoirqtrg::R`]. You can [`reset`](crate::Reg::reset), [`write`](crate::Reg::write), [`write_with_zero`](crate::Reg::write_with_zero) this register using [`s0_rxfifoirqtrg::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@s0_rxfifoirqtrg`]
|
||||||
module"]
|
module"]
|
||||||
#[doc(alias = "S0_RXFIFOIRQTRG")]
|
#[doc(alias = "S0_RXFIFOIRQTRG")]
|
||||||
pub type S0Rxfifoirqtrg = crate::Reg<s0_rxfifoirqtrg::S0RxfifoirqtrgSpec>;
|
pub type S0Rxfifoirqtrg = crate::Reg<s0_rxfifoirqtrg::S0RxfifoirqtrgSpec>;
|
||||||
#[doc = "Slave Rx FIFO IRQ Trigger Level"]
|
#[doc = "Slave Rx FIFO IRQ Trigger Level"]
|
||||||
pub mod s0_rxfifoirqtrg;
|
pub mod s0_rxfifoirqtrg;
|
||||||
#[doc = "S0_TXFIFOIRQTRG (rw) register accessor: Slave Tx FIFO IRQ Trigger Level\n\nYou can [`read`](crate::generic::Reg::read) this register and get [`s0_txfifoirqtrg::R`]. You can [`reset`](crate::generic::Reg::reset), [`write`](crate::generic::Reg::write), [`write_with_zero`](crate::generic::Reg::write_with_zero) this register using [`s0_txfifoirqtrg::W`]. You can also [`modify`](crate::generic::Reg::modify) this register. See [API](https://docs.rs/svd2rust/#read--modify--write-api).\n\nFor information about available fields see [`mod@s0_txfifoirqtrg`]
|
#[doc = "S0_TXFIFOIRQTRG (rw) register accessor: Slave Tx FIFO IRQ Trigger Level\n\nYou can [`read`](crate::Reg::read) this register and get [`s0_txfifoirqtrg::R`]. You can [`reset`](crate::Reg::reset), [`write`](crate::Reg::write), [`write_with_zero`](crate::Reg::write_with_zero) this register using [`s0_txfifoirqtrg::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@s0_txfifoirqtrg`]
|
||||||
module"]
|
module"]
|
||||||
#[doc(alias = "S0_TXFIFOIRQTRG")]
|
#[doc(alias = "S0_TXFIFOIRQTRG")]
|
||||||
pub type S0Txfifoirqtrg = crate::Reg<s0_txfifoirqtrg::S0TxfifoirqtrgSpec>;
|
pub type S0Txfifoirqtrg = crate::Reg<s0_txfifoirqtrg::S0TxfifoirqtrgSpec>;
|
||||||
#[doc = "Slave Tx FIFO IRQ Trigger Level"]
|
#[doc = "Slave Tx FIFO IRQ Trigger Level"]
|
||||||
pub mod s0_txfifoirqtrg;
|
pub mod s0_txfifoirqtrg;
|
||||||
#[doc = "S0_FIFO_CLR (w) register accessor: Slave Clear FIFO Register\n\nYou can [`reset`](crate::generic::Reg::reset), [`write`](crate::generic::Reg::write), [`write_with_zero`](crate::generic::Reg::write_with_zero) this register using [`s0_fifo_clr::W`]. See [API](https://docs.rs/svd2rust/#read--modify--write-api).\n\nFor information about available fields see [`mod@s0_fifo_clr`]
|
#[doc = "S0_FIFO_CLR (w) register accessor: Slave Clear FIFO Register\n\nYou can [`reset`](crate::Reg::reset), [`write`](crate::Reg::write), [`write_with_zero`](crate::Reg::write_with_zero) this register using [`s0_fifo_clr::W`]. See [API](https://docs.rs/svd2rust/#read--modify--write-api).\n\nFor information about available fields see [`mod@s0_fifo_clr`]
|
||||||
module"]
|
module"]
|
||||||
#[doc(alias = "S0_FIFO_CLR")]
|
#[doc(alias = "S0_FIFO_CLR")]
|
||||||
pub type S0FifoClr = crate::Reg<s0_fifo_clr::S0FifoClrSpec>;
|
pub type S0FifoClr = crate::Reg<s0_fifo_clr::S0FifoClrSpec>;
|
||||||
#[doc = "Slave Clear FIFO Register"]
|
#[doc = "Slave Clear FIFO Register"]
|
||||||
pub mod s0_fifo_clr;
|
pub mod s0_fifo_clr;
|
||||||
#[doc = "S0_ADDRESSB (rw) register accessor: Slave I2C Address B Value\n\nYou can [`read`](crate::generic::Reg::read) this register and get [`s0_addressb::R`]. You can [`reset`](crate::generic::Reg::reset), [`write`](crate::generic::Reg::write), [`write_with_zero`](crate::generic::Reg::write_with_zero) this register using [`s0_addressb::W`]. You can also [`modify`](crate::generic::Reg::modify) this register. See [API](https://docs.rs/svd2rust/#read--modify--write-api).\n\nFor information about available fields see [`mod@s0_addressb`]
|
#[doc = "S0_ADDRESSB (rw) register accessor: Slave I2C Address B Value\n\nYou can [`read`](crate::Reg::read) this register and get [`s0_addressb::R`]. You can [`reset`](crate::Reg::reset), [`write`](crate::Reg::write), [`write_with_zero`](crate::Reg::write_with_zero) this register using [`s0_addressb::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@s0_addressb`]
|
||||||
module"]
|
module"]
|
||||||
#[doc(alias = "S0_ADDRESSB")]
|
#[doc(alias = "S0_ADDRESSB")]
|
||||||
pub type S0Addressb = crate::Reg<s0_addressb::S0AddressbSpec>;
|
pub type S0Addressb = crate::Reg<s0_addressb::S0AddressbSpec>;
|
||||||
#[doc = "Slave I2C Address B Value"]
|
#[doc = "Slave I2C Address B Value"]
|
||||||
pub mod s0_addressb;
|
pub mod s0_addressb;
|
||||||
#[doc = "S0_ADDRESSMASKB (rw) register accessor: Slave I2C Address B Mask value\n\nYou can [`read`](crate::generic::Reg::read) this register and get [`s0_addressmaskb::R`]. You can [`reset`](crate::generic::Reg::reset), [`write`](crate::generic::Reg::write), [`write_with_zero`](crate::generic::Reg::write_with_zero) this register using [`s0_addressmaskb::W`]. You can also [`modify`](crate::generic::Reg::modify) this register. See [API](https://docs.rs/svd2rust/#read--modify--write-api).\n\nFor information about available fields see [`mod@s0_addressmaskb`]
|
#[doc = "S0_ADDRESSMASKB (rw) register accessor: Slave I2C Address B Mask value\n\nYou can [`read`](crate::Reg::read) this register and get [`s0_addressmaskb::R`]. You can [`reset`](crate::Reg::reset), [`write`](crate::Reg::write), [`write_with_zero`](crate::Reg::write_with_zero) this register using [`s0_addressmaskb::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@s0_addressmaskb`]
|
||||||
module"]
|
module"]
|
||||||
#[doc(alias = "S0_ADDRESSMASKB")]
|
#[doc(alias = "S0_ADDRESSMASKB")]
|
||||||
pub type S0Addressmaskb = crate::Reg<s0_addressmaskb::S0AddressmaskbSpec>;
|
pub type S0Addressmaskb = crate::Reg<s0_addressmaskb::S0AddressmaskbSpec>;
|
||||||
#[doc = "Slave I2C Address B Mask value"]
|
#[doc = "Slave I2C Address B Mask value"]
|
||||||
pub mod s0_addressmaskb;
|
pub mod s0_addressmaskb;
|
||||||
#[doc = "PERID (r) register accessor: Peripheral ID Register\n\nYou can [`read`](crate::generic::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")]
|
||||||
pub type Perid = crate::Reg<perid::PeridSpec>;
|
pub type Perid = crate::Reg<perid::PeridSpec>;
|
||||||
|
@ -8,7 +8,7 @@ impl core::fmt::Debug for R {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
impl W {}
|
impl W {}
|
||||||
#[doc = "I2C Address value\n\nYou can [`read`](crate::generic::Reg::read) this register and get [`address::R`](R). You can [`reset`](crate::generic::Reg::reset), [`write`](crate::generic::Reg::write), [`write_with_zero`](crate::generic::Reg::write_with_zero) this register using [`address::W`](W). You can also [`modify`](crate::generic::Reg::modify) this register. See [API](https://docs.rs/svd2rust/#read--modify--write-api)."]
|
#[doc = "I2C Address value\n\nYou can [`read`](crate::Reg::read) this register and get [`address::R`](R). You can [`reset`](crate::Reg::reset), [`write`](crate::Reg::write), [`write_with_zero`](crate::Reg::write_with_zero) this register using [`address::W`](W). You can also [`modify`](crate::Reg::modify) this register. See [API](https://docs.rs/svd2rust/#read--modify--write-api)."]
|
||||||
pub struct AddressSpec;
|
pub struct AddressSpec;
|
||||||
impl crate::RegisterSpec for AddressSpec {
|
impl crate::RegisterSpec for AddressSpec {
|
||||||
type Ux = u32;
|
type Ux = u32;
|
||||||
|
@ -25,18 +25,16 @@ impl R {
|
|||||||
impl W {
|
impl W {
|
||||||
#[doc = "Bits 0:30 - Enable FastMode"]
|
#[doc = "Bits 0:30 - Enable FastMode"]
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
#[must_use]
|
|
||||||
pub fn value(&mut self) -> ValueW<ClkscaleSpec> {
|
pub fn value(&mut self) -> ValueW<ClkscaleSpec> {
|
||||||
ValueW::new(self, 0)
|
ValueW::new(self, 0)
|
||||||
}
|
}
|
||||||
#[doc = "Bit 31 - Enable FastMode"]
|
#[doc = "Bit 31 - Enable FastMode"]
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
#[must_use]
|
|
||||||
pub fn fastmode(&mut self) -> FastmodeW<ClkscaleSpec> {
|
pub fn fastmode(&mut self) -> FastmodeW<ClkscaleSpec> {
|
||||||
FastmodeW::new(self, 31)
|
FastmodeW::new(self, 31)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
#[doc = "Clock Scale divide value\n\nYou can [`read`](crate::generic::Reg::read) this register and get [`clkscale::R`](R). You can [`reset`](crate::generic::Reg::reset), [`write`](crate::generic::Reg::write), [`write_with_zero`](crate::generic::Reg::write_with_zero) this register using [`clkscale::W`](W). You can also [`modify`](crate::generic::Reg::modify) this register. See [API](https://docs.rs/svd2rust/#read--modify--write-api)."]
|
#[doc = "Clock Scale divide value\n\nYou can [`read`](crate::Reg::read) this register and get [`clkscale::R`](R). You can [`reset`](crate::Reg::reset), [`write`](crate::Reg::write), [`write_with_zero`](crate::Reg::write_with_zero) this register using [`clkscale::W`](W). You can also [`modify`](crate::Reg::modify) this register. See [API](https://docs.rs/svd2rust/#read--modify--write-api)."]
|
||||||
pub struct ClkscaleSpec;
|
pub struct ClkscaleSpec;
|
||||||
impl crate::RegisterSpec for ClkscaleSpec {
|
impl crate::RegisterSpec for ClkscaleSpec {
|
||||||
type Ux = u32;
|
type Ux = u32;
|
||||||
|
@ -8,7 +8,7 @@ impl core::fmt::Debug for R {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
impl W {}
|
impl W {}
|
||||||
#[doc = "Clock Low Timeout Limit Register\n\nYou can [`read`](crate::generic::Reg::read) this register and get [`clktolimit::R`](R). You can [`reset`](crate::generic::Reg::reset), [`write`](crate::generic::Reg::write), [`write_with_zero`](crate::generic::Reg::write_with_zero) this register using [`clktolimit::W`](W). You can also [`modify`](crate::generic::Reg::modify) this register. See [API](https://docs.rs/svd2rust/#read--modify--write-api)."]
|
#[doc = "Clock Low Timeout Limit Register\n\nYou can [`read`](crate::Reg::read) this register and get [`clktolimit::R`](R). You can [`reset`](crate::Reg::reset), [`write`](crate::Reg::write), [`write_with_zero`](crate::Reg::write_with_zero) this register using [`clktolimit::W`](W). You can also [`modify`](crate::Reg::modify) this register. See [API](https://docs.rs/svd2rust/#read--modify--write-api)."]
|
||||||
pub struct ClktolimitSpec;
|
pub struct ClktolimitSpec;
|
||||||
impl crate::RegisterSpec for ClktolimitSpec {
|
impl crate::RegisterSpec for ClktolimitSpec {
|
||||||
type Ux = u32;
|
type Ux = u32;
|
||||||
|
@ -8,7 +8,7 @@ impl core::fmt::Debug for R {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
impl W {}
|
impl W {}
|
||||||
#[doc = "Command Register\n\nYou can [`read`](crate::generic::Reg::read) this register and get [`cmd::R`](R). You can [`reset`](crate::generic::Reg::reset), [`write`](crate::generic::Reg::write), [`write_with_zero`](crate::generic::Reg::write_with_zero) this register using [`cmd::W`](W). You can also [`modify`](crate::generic::Reg::modify) this register. See [API](https://docs.rs/svd2rust/#read--modify--write-api)."]
|
#[doc = "Command Register\n\nYou can [`read`](crate::Reg::read) this register and get [`cmd::R`](R). You can [`reset`](crate::Reg::reset), [`write`](crate::Reg::write), [`write_with_zero`](crate::Reg::write_with_zero) this register using [`cmd::W`](W). You can also [`modify`](crate::Reg::modify) this register. See [API](https://docs.rs/svd2rust/#read--modify--write-api)."]
|
||||||
pub struct CmdSpec;
|
pub struct CmdSpec;
|
||||||
impl crate::RegisterSpec for CmdSpec {
|
impl crate::RegisterSpec for CmdSpec {
|
||||||
type Ux = u32;
|
type Ux = u32;
|
||||||
|
@ -88,60 +88,51 @@ impl R {
|
|||||||
impl W {
|
impl W {
|
||||||
#[doc = "Bit 0 - I2C CLK Enabled"]
|
#[doc = "Bit 0 - I2C CLK Enabled"]
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
#[must_use]
|
|
||||||
pub fn clkenabled(&mut self) -> ClkenabledW<CtrlSpec> {
|
pub fn clkenabled(&mut self) -> ClkenabledW<CtrlSpec> {
|
||||||
ClkenabledW::new(self, 0)
|
ClkenabledW::new(self, 0)
|
||||||
}
|
}
|
||||||
#[doc = "Bit 1 - I2C Activated"]
|
#[doc = "Bit 1 - I2C Activated"]
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
#[must_use]
|
|
||||||
pub fn enabled(&mut self) -> EnabledW<CtrlSpec> {
|
pub fn enabled(&mut self) -> EnabledW<CtrlSpec> {
|
||||||
EnabledW::new(self, 1)
|
EnabledW::new(self, 1)
|
||||||
}
|
}
|
||||||
#[doc = "Bit 2 - I2C Active"]
|
#[doc = "Bit 2 - I2C Active"]
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
#[must_use]
|
|
||||||
pub fn enable(&mut self) -> EnableW<CtrlSpec> {
|
pub fn enable(&mut self) -> EnableW<CtrlSpec> {
|
||||||
EnableW::new(self, 2)
|
EnableW::new(self, 2)
|
||||||
}
|
}
|
||||||
#[doc = "Bit 3 - TX FIFIO Empty Mode"]
|
#[doc = "Bit 3 - TX FIFIO Empty Mode"]
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
#[must_use]
|
|
||||||
pub fn txfemd(&mut self) -> TxfemdW<CtrlSpec> {
|
pub fn txfemd(&mut self) -> TxfemdW<CtrlSpec> {
|
||||||
TxfemdW::new(self, 3)
|
TxfemdW::new(self, 3)
|
||||||
}
|
}
|
||||||
#[doc = "Bit 4 - RX FIFO Full Mode"]
|
#[doc = "Bit 4 - RX FIFO Full Mode"]
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
#[must_use]
|
|
||||||
pub fn rxffmd(&mut self) -> RxffmdW<CtrlSpec> {
|
pub fn rxffmd(&mut self) -> RxffmdW<CtrlSpec> {
|
||||||
RxffmdW::new(self, 4)
|
RxffmdW::new(self, 4)
|
||||||
}
|
}
|
||||||
#[doc = "Bit 5 - Enable Input Analog Glitch Filter"]
|
#[doc = "Bit 5 - Enable Input Analog Glitch Filter"]
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
#[must_use]
|
|
||||||
pub fn algfilter(&mut self) -> AlgfilterW<CtrlSpec> {
|
pub fn algfilter(&mut self) -> AlgfilterW<CtrlSpec> {
|
||||||
AlgfilterW::new(self, 5)
|
AlgfilterW::new(self, 5)
|
||||||
}
|
}
|
||||||
#[doc = "Bit 6 - Enable Input Digital Glitch Filter"]
|
#[doc = "Bit 6 - Enable Input Digital Glitch Filter"]
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
#[must_use]
|
|
||||||
pub fn dlgfilter(&mut self) -> DlgfilterW<CtrlSpec> {
|
pub fn dlgfilter(&mut self) -> DlgfilterW<CtrlSpec> {
|
||||||
DlgfilterW::new(self, 6)
|
DlgfilterW::new(self, 6)
|
||||||
}
|
}
|
||||||
#[doc = "Bit 8 - Enable LoopBack Mode"]
|
#[doc = "Bit 8 - Enable LoopBack Mode"]
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
#[must_use]
|
|
||||||
pub fn loopback(&mut self) -> LoopbackW<CtrlSpec> {
|
pub fn loopback(&mut self) -> LoopbackW<CtrlSpec> {
|
||||||
LoopbackW::new(self, 8)
|
LoopbackW::new(self, 8)
|
||||||
}
|
}
|
||||||
#[doc = "Bit 9 - Enable Timing Config Register"]
|
#[doc = "Bit 9 - Enable Timing Config Register"]
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
#[must_use]
|
|
||||||
pub fn tmconfigenb(&mut self) -> TmconfigenbW<CtrlSpec> {
|
pub fn tmconfigenb(&mut self) -> TmconfigenbW<CtrlSpec> {
|
||||||
TmconfigenbW::new(self, 9)
|
TmconfigenbW::new(self, 9)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
#[doc = "Control Register\n\nYou can [`read`](crate::generic::Reg::read) this register and get [`ctrl::R`](R). You can [`reset`](crate::generic::Reg::reset), [`write`](crate::generic::Reg::write), [`write_with_zero`](crate::generic::Reg::write_with_zero) this register using [`ctrl::W`](W). You can also [`modify`](crate::generic::Reg::modify) this register. See [API](https://docs.rs/svd2rust/#read--modify--write-api)."]
|
#[doc = "Control Register\n\nYou can [`read`](crate::Reg::read) this register and get [`ctrl::R`](R). You can [`reset`](crate::Reg::reset), [`write`](crate::Reg::write), [`write_with_zero`](crate::Reg::write_with_zero) this register using [`ctrl::W`](W). You can also [`modify`](crate::Reg::modify) this register. See [API](https://docs.rs/svd2rust/#read--modify--write-api)."]
|
||||||
pub struct CtrlSpec;
|
pub struct CtrlSpec;
|
||||||
impl crate::RegisterSpec for CtrlSpec {
|
impl crate::RegisterSpec for CtrlSpec {
|
||||||
type Ux = u32;
|
type Ux = u32;
|
||||||
|
@ -8,7 +8,7 @@ impl core::fmt::Debug for R {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
impl W {}
|
impl W {}
|
||||||
#[doc = "Data Input/Output\n\nYou can [`read`](crate::generic::Reg::read) this register and get [`data::R`](R). You can [`reset`](crate::generic::Reg::reset), [`write`](crate::generic::Reg::write), [`write_with_zero`](crate::generic::Reg::write_with_zero) this register using [`data::W`](W). You can also [`modify`](crate::generic::Reg::modify) this register. See [API](https://docs.rs/svd2rust/#read--modify--write-api)."]
|
#[doc = "Data Input/Output\n\nYou can [`read`](crate::Reg::read) this register and get [`data::R`](R). You can [`reset`](crate::Reg::reset), [`write`](crate::Reg::write), [`write_with_zero`](crate::Reg::write_with_zero) this register using [`data::W`](W). You can also [`modify`](crate::Reg::modify) this register. See [API](https://docs.rs/svd2rust/#read--modify--write-api)."]
|
||||||
pub struct DataSpec;
|
pub struct DataSpec;
|
||||||
impl crate::RegisterSpec for DataSpec {
|
impl crate::RegisterSpec for DataSpec {
|
||||||
type Ux = u32;
|
type Ux = u32;
|
||||||
|
@ -7,18 +7,16 @@ pub type TxfifoW<'a, REG> = crate::BitWriter<'a, REG>;
|
|||||||
impl W {
|
impl W {
|
||||||
#[doc = "Bit 0 - Clear Rx FIFO"]
|
#[doc = "Bit 0 - Clear Rx FIFO"]
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
#[must_use]
|
|
||||||
pub fn rxfifo(&mut self) -> RxfifoW<FifoClrSpec> {
|
pub fn rxfifo(&mut self) -> RxfifoW<FifoClrSpec> {
|
||||||
RxfifoW::new(self, 0)
|
RxfifoW::new(self, 0)
|
||||||
}
|
}
|
||||||
#[doc = "Bit 1 - Clear Tx FIFO"]
|
#[doc = "Bit 1 - Clear Tx FIFO"]
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
#[must_use]
|
|
||||||
pub fn txfifo(&mut self) -> TxfifoW<FifoClrSpec> {
|
pub fn txfifo(&mut self) -> TxfifoW<FifoClrSpec> {
|
||||||
TxfifoW::new(self, 1)
|
TxfifoW::new(self, 1)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
#[doc = "Clear FIFO Register\n\nYou can [`reset`](crate::generic::Reg::reset), [`write`](crate::generic::Reg::write), [`write_with_zero`](crate::generic::Reg::write_with_zero) this register using [`fifo_clr::W`](W). See [API](https://docs.rs/svd2rust/#read--modify--write-api)."]
|
#[doc = "Clear FIFO Register\n\nYou can [`reset`](crate::Reg::reset), [`write`](crate::Reg::write), [`write_with_zero`](crate::Reg::write_with_zero) this register using [`fifo_clr::W`](W). See [API](https://docs.rs/svd2rust/#read--modify--write-api)."]
|
||||||
pub struct FifoClrSpec;
|
pub struct FifoClrSpec;
|
||||||
impl crate::RegisterSpec for FifoClrSpec {
|
impl crate::RegisterSpec for FifoClrSpec {
|
||||||
type Ux = u32;
|
type Ux = u32;
|
||||||
|
@ -133,90 +133,76 @@ impl R {
|
|||||||
impl W {
|
impl W {
|
||||||
#[doc = "Bit 0 - I2C Bus is Idle"]
|
#[doc = "Bit 0 - I2C Bus is Idle"]
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
#[must_use]
|
|
||||||
pub fn i2cidle(&mut self) -> I2cidleW<IrqEnbSpec> {
|
pub fn i2cidle(&mut self) -> I2cidleW<IrqEnbSpec> {
|
||||||
I2cidleW::new(self, 0)
|
I2cidleW::new(self, 0)
|
||||||
}
|
}
|
||||||
#[doc = "Bit 1 - Controller is Idle"]
|
#[doc = "Bit 1 - Controller is Idle"]
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
#[must_use]
|
|
||||||
pub fn idle(&mut self) -> IdleW<IrqEnbSpec> {
|
pub fn idle(&mut self) -> IdleW<IrqEnbSpec> {
|
||||||
IdleW::new(self, 1)
|
IdleW::new(self, 1)
|
||||||
}
|
}
|
||||||
#[doc = "Bit 2 - Controller is Waiting"]
|
#[doc = "Bit 2 - Controller is Waiting"]
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
#[must_use]
|
|
||||||
pub fn waiting(&mut self) -> WaitingW<IrqEnbSpec> {
|
pub fn waiting(&mut self) -> WaitingW<IrqEnbSpec> {
|
||||||
WaitingW::new(self, 2)
|
WaitingW::new(self, 2)
|
||||||
}
|
}
|
||||||
#[doc = "Bit 3 - Controller is Stalled"]
|
#[doc = "Bit 3 - Controller is Stalled"]
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
#[must_use]
|
|
||||||
pub fn stalled(&mut self) -> StalledW<IrqEnbSpec> {
|
pub fn stalled(&mut self) -> StalledW<IrqEnbSpec> {
|
||||||
StalledW::new(self, 3)
|
StalledW::new(self, 3)
|
||||||
}
|
}
|
||||||
#[doc = "Bit 4 - I2C Arbitration was lost"]
|
#[doc = "Bit 4 - I2C Arbitration was lost"]
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
#[must_use]
|
|
||||||
pub fn arblost(&mut self) -> ArblostW<IrqEnbSpec> {
|
pub fn arblost(&mut self) -> ArblostW<IrqEnbSpec> {
|
||||||
ArblostW::new(self, 4)
|
ArblostW::new(self, 4)
|
||||||
}
|
}
|
||||||
#[doc = "Bit 5 - I2C Address was not Acknowledged"]
|
#[doc = "Bit 5 - I2C Address was not Acknowledged"]
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
#[must_use]
|
|
||||||
pub fn nackaddr(&mut self) -> NackaddrW<IrqEnbSpec> {
|
pub fn nackaddr(&mut self) -> NackaddrW<IrqEnbSpec> {
|
||||||
NackaddrW::new(self, 5)
|
NackaddrW::new(self, 5)
|
||||||
}
|
}
|
||||||
#[doc = "Bit 6 - I2C Data was not Acknowledged"]
|
#[doc = "Bit 6 - I2C Data was not Acknowledged"]
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
#[must_use]
|
|
||||||
pub fn nackdata(&mut self) -> NackdataW<IrqEnbSpec> {
|
pub fn nackdata(&mut self) -> NackdataW<IrqEnbSpec> {
|
||||||
NackdataW::new(self, 6)
|
NackdataW::new(self, 6)
|
||||||
}
|
}
|
||||||
#[doc = "Bit 7 - I2C Clock Low Timeout"]
|
#[doc = "Bit 7 - I2C Clock Low Timeout"]
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
#[must_use]
|
|
||||||
pub fn clkloto(&mut self) -> ClklotoW<IrqEnbSpec> {
|
pub fn clkloto(&mut self) -> ClklotoW<IrqEnbSpec> {
|
||||||
ClklotoW::new(self, 7)
|
ClklotoW::new(self, 7)
|
||||||
}
|
}
|
||||||
#[doc = "Bit 10 - TX FIFO Overflowed"]
|
#[doc = "Bit 10 - TX FIFO Overflowed"]
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
#[must_use]
|
|
||||||
pub fn txoverflow(&mut self) -> TxoverflowW<IrqEnbSpec> {
|
pub fn txoverflow(&mut self) -> TxoverflowW<IrqEnbSpec> {
|
||||||
TxoverflowW::new(self, 10)
|
TxoverflowW::new(self, 10)
|
||||||
}
|
}
|
||||||
#[doc = "Bit 11 - TX FIFO Overflowed"]
|
#[doc = "Bit 11 - TX FIFO Overflowed"]
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
#[must_use]
|
|
||||||
pub fn rxoverflow(&mut self) -> RxoverflowW<IrqEnbSpec> {
|
pub fn rxoverflow(&mut self) -> RxoverflowW<IrqEnbSpec> {
|
||||||
RxoverflowW::new(self, 11)
|
RxoverflowW::new(self, 11)
|
||||||
}
|
}
|
||||||
#[doc = "Bit 12 - TX FIFO Ready"]
|
#[doc = "Bit 12 - TX FIFO Ready"]
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
#[must_use]
|
|
||||||
pub fn txready(&mut self) -> TxreadyW<IrqEnbSpec> {
|
pub fn txready(&mut self) -> TxreadyW<IrqEnbSpec> {
|
||||||
TxreadyW::new(self, 12)
|
TxreadyW::new(self, 12)
|
||||||
}
|
}
|
||||||
#[doc = "Bit 13 - RX FIFO Ready"]
|
#[doc = "Bit 13 - RX FIFO Ready"]
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
#[must_use]
|
|
||||||
pub fn rxready(&mut self) -> RxreadyW<IrqEnbSpec> {
|
pub fn rxready(&mut self) -> RxreadyW<IrqEnbSpec> {
|
||||||
RxreadyW::new(self, 13)
|
RxreadyW::new(self, 13)
|
||||||
}
|
}
|
||||||
#[doc = "Bit 14 - TX FIFO Empty"]
|
#[doc = "Bit 14 - TX FIFO Empty"]
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
#[must_use]
|
|
||||||
pub fn txempty(&mut self) -> TxemptyW<IrqEnbSpec> {
|
pub fn txempty(&mut self) -> TxemptyW<IrqEnbSpec> {
|
||||||
TxemptyW::new(self, 14)
|
TxemptyW::new(self, 14)
|
||||||
}
|
}
|
||||||
#[doc = "Bit 15 - RX FIFO Full"]
|
#[doc = "Bit 15 - RX FIFO Full"]
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
#[must_use]
|
|
||||||
pub fn rxfull(&mut self) -> RxfullW<IrqEnbSpec> {
|
pub fn rxfull(&mut self) -> RxfullW<IrqEnbSpec> {
|
||||||
RxfullW::new(self, 15)
|
RxfullW::new(self, 15)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
#[doc = "Interrupt Enable Register\n\nYou can [`read`](crate::generic::Reg::read) this register and get [`irq_enb::R`](R). You can [`reset`](crate::generic::Reg::reset), [`write`](crate::generic::Reg::write), [`write_with_zero`](crate::generic::Reg::write_with_zero) this register using [`irq_enb::W`](W). You can also [`modify`](crate::generic::Reg::modify) this register. See [API](https://docs.rs/svd2rust/#read--modify--write-api)."]
|
#[doc = "Interrupt Enable Register\n\nYou can [`read`](crate::Reg::read) this register and get [`irq_enb::R`](R). You can [`reset`](crate::Reg::reset), [`write`](crate::Reg::write), [`write_with_zero`](crate::Reg::write_with_zero) this register using [`irq_enb::W`](W). You can also [`modify`](crate::Reg::modify) this register. See [API](https://docs.rs/svd2rust/#read--modify--write-api)."]
|
||||||
pub struct IrqEnbSpec;
|
pub struct IrqEnbSpec;
|
||||||
impl crate::RegisterSpec for IrqEnbSpec {
|
impl crate::RegisterSpec for IrqEnbSpec {
|
||||||
type Ux = u32;
|
type Ux = u32;
|
||||||
|
@ -5,7 +5,7 @@ impl core::fmt::Debug for R {
|
|||||||
write!(f, "{}", self.bits())
|
write!(f, "{}", self.bits())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
#[doc = "Peripheral ID Register\n\nYou can [`read`](crate::generic::Reg::read) this register and get [`perid::R`](R). See [API](https://docs.rs/svd2rust/#read--modify--write-api)."]
|
#[doc = "Peripheral ID Register\n\nYou can [`read`](crate::Reg::read) this register and get [`perid::R`](R). See [API](https://docs.rs/svd2rust/#read--modify--write-api)."]
|
||||||
pub struct PeridSpec;
|
pub struct PeridSpec;
|
||||||
impl crate::RegisterSpec for PeridSpec {
|
impl crate::RegisterSpec for PeridSpec {
|
||||||
type Ux = u32;
|
type Ux = u32;
|
||||||
|
@ -5,7 +5,7 @@ impl core::fmt::Debug for R {
|
|||||||
write!(f, "{}", self.bits())
|
write!(f, "{}", self.bits())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
#[doc = "RX Count Register\n\nYou can [`read`](crate::generic::Reg::read) this register and get [`rxcount::R`](R). See [API](https://docs.rs/svd2rust/#read--modify--write-api)."]
|
#[doc = "RX Count Register\n\nYou can [`read`](crate::Reg::read) this register and get [`rxcount::R`](R). See [API](https://docs.rs/svd2rust/#read--modify--write-api)."]
|
||||||
pub struct RxcountSpec;
|
pub struct RxcountSpec;
|
||||||
impl crate::RegisterSpec for RxcountSpec {
|
impl crate::RegisterSpec for RxcountSpec {
|
||||||
type Ux = u32;
|
type Ux = u32;
|
||||||
|
@ -8,7 +8,7 @@ impl core::fmt::Debug for R {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
impl W {}
|
impl W {}
|
||||||
#[doc = "Rx FIFO IRQ Trigger Level\n\nYou can [`read`](crate::generic::Reg::read) this register and get [`rxfifoirqtrg::R`](R). You can [`reset`](crate::generic::Reg::reset), [`write`](crate::generic::Reg::write), [`write_with_zero`](crate::generic::Reg::write_with_zero) this register using [`rxfifoirqtrg::W`](W). You can also [`modify`](crate::generic::Reg::modify) this register. See [API](https://docs.rs/svd2rust/#read--modify--write-api)."]
|
#[doc = "Rx FIFO IRQ Trigger Level\n\nYou can [`read`](crate::Reg::read) this register and get [`rxfifoirqtrg::R`](R). You can [`reset`](crate::Reg::reset), [`write`](crate::Reg::write), [`write_with_zero`](crate::Reg::write_with_zero) this register using [`rxfifoirqtrg::W`](W). You can also [`modify`](crate::Reg::modify) this register. See [API](https://docs.rs/svd2rust/#read--modify--write-api)."]
|
||||||
pub struct RxfifoirqtrgSpec;
|
pub struct RxfifoirqtrgSpec;
|
||||||
impl crate::RegisterSpec for RxfifoirqtrgSpec {
|
impl crate::RegisterSpec for RxfifoirqtrgSpec {
|
||||||
type Ux = u32;
|
type Ux = u32;
|
||||||
|
@ -8,7 +8,7 @@ impl core::fmt::Debug for R {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
impl W {}
|
impl W {}
|
||||||
#[doc = "Slave I2C Address Value\n\nYou can [`read`](crate::generic::Reg::read) this register and get [`s0_address::R`](R). You can [`reset`](crate::generic::Reg::reset), [`write`](crate::generic::Reg::write), [`write_with_zero`](crate::generic::Reg::write_with_zero) this register using [`s0_address::W`](W). You can also [`modify`](crate::generic::Reg::modify) this register. See [API](https://docs.rs/svd2rust/#read--modify--write-api)."]
|
#[doc = "Slave I2C Address Value\n\nYou can [`read`](crate::Reg::read) this register and get [`s0_address::R`](R). You can [`reset`](crate::Reg::reset), [`write`](crate::Reg::write), [`write_with_zero`](crate::Reg::write_with_zero) this register using [`s0_address::W`](W). You can also [`modify`](crate::Reg::modify) this register. See [API](https://docs.rs/svd2rust/#read--modify--write-api)."]
|
||||||
pub struct S0AddressSpec;
|
pub struct S0AddressSpec;
|
||||||
impl crate::RegisterSpec for S0AddressSpec {
|
impl crate::RegisterSpec for S0AddressSpec {
|
||||||
type Ux = u32;
|
type Ux = u32;
|
||||||
|
@ -8,7 +8,7 @@ impl core::fmt::Debug for R {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
impl W {}
|
impl W {}
|
||||||
#[doc = "Slave I2C Address B Value\n\nYou can [`read`](crate::generic::Reg::read) this register and get [`s0_addressb::R`](R). You can [`reset`](crate::generic::Reg::reset), [`write`](crate::generic::Reg::write), [`write_with_zero`](crate::generic::Reg::write_with_zero) this register using [`s0_addressb::W`](W). You can also [`modify`](crate::generic::Reg::modify) this register. See [API](https://docs.rs/svd2rust/#read--modify--write-api)."]
|
#[doc = "Slave I2C Address B Value\n\nYou can [`read`](crate::Reg::read) this register and get [`s0_addressb::R`](R). You can [`reset`](crate::Reg::reset), [`write`](crate::Reg::write), [`write_with_zero`](crate::Reg::write_with_zero) this register using [`s0_addressb::W`](W). You can also [`modify`](crate::Reg::modify) this register. See [API](https://docs.rs/svd2rust/#read--modify--write-api)."]
|
||||||
pub struct S0AddressbSpec;
|
pub struct S0AddressbSpec;
|
||||||
impl crate::RegisterSpec for S0AddressbSpec {
|
impl crate::RegisterSpec for S0AddressbSpec {
|
||||||
type Ux = u32;
|
type Ux = u32;
|
||||||
|
@ -8,7 +8,7 @@ impl core::fmt::Debug for R {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
impl W {}
|
impl W {}
|
||||||
#[doc = "Slave I2C Address Mask value\n\nYou can [`read`](crate::generic::Reg::read) this register and get [`s0_addressmask::R`](R). You can [`reset`](crate::generic::Reg::reset), [`write`](crate::generic::Reg::write), [`write_with_zero`](crate::generic::Reg::write_with_zero) this register using [`s0_addressmask::W`](W). You can also [`modify`](crate::generic::Reg::modify) this register. See [API](https://docs.rs/svd2rust/#read--modify--write-api)."]
|
#[doc = "Slave I2C Address Mask value\n\nYou can [`read`](crate::Reg::read) this register and get [`s0_addressmask::R`](R). You can [`reset`](crate::Reg::reset), [`write`](crate::Reg::write), [`write_with_zero`](crate::Reg::write_with_zero) this register using [`s0_addressmask::W`](W). You can also [`modify`](crate::Reg::modify) this register. See [API](https://docs.rs/svd2rust/#read--modify--write-api)."]
|
||||||
pub struct S0AddressmaskSpec;
|
pub struct S0AddressmaskSpec;
|
||||||
impl crate::RegisterSpec for S0AddressmaskSpec {
|
impl crate::RegisterSpec for S0AddressmaskSpec {
|
||||||
type Ux = u32;
|
type Ux = u32;
|
||||||
|
@ -8,7 +8,7 @@ impl core::fmt::Debug for R {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
impl W {}
|
impl W {}
|
||||||
#[doc = "Slave I2C Address B Mask value\n\nYou can [`read`](crate::generic::Reg::read) this register and get [`s0_addressmaskb::R`](R). You can [`reset`](crate::generic::Reg::reset), [`write`](crate::generic::Reg::write), [`write_with_zero`](crate::generic::Reg::write_with_zero) this register using [`s0_addressmaskb::W`](W). You can also [`modify`](crate::generic::Reg::modify) this register. See [API](https://docs.rs/svd2rust/#read--modify--write-api)."]
|
#[doc = "Slave I2C Address B Mask value\n\nYou can [`read`](crate::Reg::read) this register and get [`s0_addressmaskb::R`](R). You can [`reset`](crate::Reg::reset), [`write`](crate::Reg::write), [`write_with_zero`](crate::Reg::write_with_zero) this register using [`s0_addressmaskb::W`](W). You can also [`modify`](crate::Reg::modify) this register. See [API](https://docs.rs/svd2rust/#read--modify--write-api)."]
|
||||||
pub struct S0AddressmaskbSpec;
|
pub struct S0AddressmaskbSpec;
|
||||||
impl crate::RegisterSpec for S0AddressmaskbSpec {
|
impl crate::RegisterSpec for S0AddressmaskbSpec {
|
||||||
type Ux = u32;
|
type Ux = u32;
|
||||||
|
@ -52,36 +52,31 @@ impl R {
|
|||||||
impl W {
|
impl W {
|
||||||
#[doc = "Bit 0 - I2C Enabled"]
|
#[doc = "Bit 0 - I2C Enabled"]
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
#[must_use]
|
|
||||||
pub fn clkenabled(&mut self) -> ClkenabledW<S0CtrlSpec> {
|
pub fn clkenabled(&mut self) -> ClkenabledW<S0CtrlSpec> {
|
||||||
ClkenabledW::new(self, 0)
|
ClkenabledW::new(self, 0)
|
||||||
}
|
}
|
||||||
#[doc = "Bit 1 - I2C Activated"]
|
#[doc = "Bit 1 - I2C Activated"]
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
#[must_use]
|
|
||||||
pub fn enabled(&mut self) -> EnabledW<S0CtrlSpec> {
|
pub fn enabled(&mut self) -> EnabledW<S0CtrlSpec> {
|
||||||
EnabledW::new(self, 1)
|
EnabledW::new(self, 1)
|
||||||
}
|
}
|
||||||
#[doc = "Bit 2 - I2C Active"]
|
#[doc = "Bit 2 - I2C Active"]
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
#[must_use]
|
|
||||||
pub fn enable(&mut self) -> EnableW<S0CtrlSpec> {
|
pub fn enable(&mut self) -> EnableW<S0CtrlSpec> {
|
||||||
EnableW::new(self, 2)
|
EnableW::new(self, 2)
|
||||||
}
|
}
|
||||||
#[doc = "Bit 3 - TX FIFIO Empty Mode"]
|
#[doc = "Bit 3 - TX FIFIO Empty Mode"]
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
#[must_use]
|
|
||||||
pub fn txfemd(&mut self) -> TxfemdW<S0CtrlSpec> {
|
pub fn txfemd(&mut self) -> TxfemdW<S0CtrlSpec> {
|
||||||
TxfemdW::new(self, 3)
|
TxfemdW::new(self, 3)
|
||||||
}
|
}
|
||||||
#[doc = "Bit 4 - RX FIFO Full Mode"]
|
#[doc = "Bit 4 - RX FIFO Full Mode"]
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
#[must_use]
|
|
||||||
pub fn rxffmd(&mut self) -> RxffmdW<S0CtrlSpec> {
|
pub fn rxffmd(&mut self) -> RxffmdW<S0CtrlSpec> {
|
||||||
RxffmdW::new(self, 4)
|
RxffmdW::new(self, 4)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
#[doc = "Slave Control Register\n\nYou can [`read`](crate::generic::Reg::read) this register and get [`s0_ctrl::R`](R). You can [`reset`](crate::generic::Reg::reset), [`write`](crate::generic::Reg::write), [`write_with_zero`](crate::generic::Reg::write_with_zero) this register using [`s0_ctrl::W`](W). You can also [`modify`](crate::generic::Reg::modify) this register. See [API](https://docs.rs/svd2rust/#read--modify--write-api)."]
|
#[doc = "Slave Control Register\n\nYou can [`read`](crate::Reg::read) this register and get [`s0_ctrl::R`](R). You can [`reset`](crate::Reg::reset), [`write`](crate::Reg::write), [`write_with_zero`](crate::Reg::write_with_zero) this register using [`s0_ctrl::W`](W). You can also [`modify`](crate::Reg::modify) this register. See [API](https://docs.rs/svd2rust/#read--modify--write-api)."]
|
||||||
pub struct S0CtrlSpec;
|
pub struct S0CtrlSpec;
|
||||||
impl crate::RegisterSpec for S0CtrlSpec {
|
impl crate::RegisterSpec for S0CtrlSpec {
|
||||||
type Ux = u32;
|
type Ux = u32;
|
||||||
|
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user