Compare commits
No commits in common. "main" and "va108xx-hal-v0.11.0" have entirely different histories.
main
...
va108xx-ha
@ -18,9 +18,9 @@ rustflags = [
|
|||||||
"-C", "link-arg=-Tlink.x",
|
"-C", "link-arg=-Tlink.x",
|
||||||
|
|
||||||
# knurling-rs tooling. If you want to use flip-link, ensure it is installed first.
|
# knurling-rs tooling. If you want to use flip-link, ensure it is installed first.
|
||||||
"-C", "linker=flip-link",
|
# "-C", "linker=flip-link",
|
||||||
# Unfortunately, defmt is clunky to use without probe-rs..
|
# Unfortunately, defmt is clunky to use without probe-rs..
|
||||||
"-C", "link-arg=-Tdefmt.x",
|
# "-C", "link-arg=-Tdefmt.x",
|
||||||
|
|
||||||
# Can be useful for debugging.
|
# Can be useful for debugging.
|
||||||
# "-Clink-args=-Map=app.map"
|
# "-Clink-args=-Map=app.map"
|
||||||
|
@ -13,6 +13,7 @@ members = [
|
|||||||
"flashloader",
|
"flashloader",
|
||||||
]
|
]
|
||||||
exclude = [
|
exclude = [
|
||||||
|
"defmt-testapp",
|
||||||
"flashloader/slot-a-blinky",
|
"flashloader/slot-a-blinky",
|
||||||
"flashloader/slot-b-blinky",
|
"flashloader/slot-b-blinky",
|
||||||
]
|
]
|
||||||
|
30
README.md
30
README.md
@ -34,9 +34,6 @@ It also contains the following helper crates:
|
|||||||
[`RTIC`](https://rtic.rs/2/book/en/) and [`embassy`](https://github.com/embassy-rs/embassy)
|
[`RTIC`](https://rtic.rs/2/book/en/) and [`embassy`](https://github.com/embassy-rs/embassy)
|
||||||
native Rust RTOSes.
|
native Rust RTOSes.
|
||||||
|
|
||||||
The majority of the HAL implementation and the Embassy-rs support are contained in the external
|
|
||||||
[`vorago-shared-periphs`](https://egit.irs.uni-stuttgart.de/rust/vorago-shared-periphs) crate.
|
|
||||||
|
|
||||||
## Using the `.cargo/config.toml` file
|
## Using the `.cargo/config.toml` file
|
||||||
|
|
||||||
Use the following command to have a starting `config.toml` file
|
Use the following command to have a starting `config.toml` file
|
||||||
@ -79,8 +76,8 @@ probe-rs run --chip VA108xx_RAM --protocol jtag target/thumbv6m-none-eabi/debug/
|
|||||||
to flash and run the blinky program on the RAM. There is also a `VA108xx` chip target
|
to flash and run the blinky program on the RAM. There is also a `VA108xx` chip target
|
||||||
available for persistent flashing.
|
available for persistent flashing.
|
||||||
|
|
||||||
Runner configuration is available in the `.cargo/def-config.toml` file to use `probe-rs` for
|
Runner configuration avilable in the `.cargo/def-config.toml` file to use `probe-rs` for
|
||||||
convenience. `probe-rs` is also able to process and display `defmt` strings directly.
|
convenience.
|
||||||
|
|
||||||
### Using VS Code
|
### Using VS Code
|
||||||
|
|
||||||
@ -126,13 +123,13 @@ is also run when running the `jlink-gdb.sh` script)
|
|||||||
|
|
||||||
```sh
|
```sh
|
||||||
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 -jtagconf -1,-1
|
-LocalhostOnly
|
||||||
```
|
```
|
||||||
|
|
||||||
After this, you can flash and debug the application with the following command
|
After this, you can flash and debug the application with the following command
|
||||||
|
|
||||||
```sh
|
```sh
|
||||||
gdb-mutliarch -q -x jlink/jlink.gdb target/thumbv6m-none-eabihf/debug/examples/blinky -tui
|
gdb-mutliarch -q -x jlink/jlink.gdb target/thumbv6m-none-eabihf/debug/examples/blinky
|
||||||
```
|
```
|
||||||
|
|
||||||
Please note that you can automate all steps except starting the GDB server by using a cargo
|
Please note that you can automate all steps except starting the GDB server by using a cargo
|
||||||
@ -151,22 +148,3 @@ example.
|
|||||||
The Segger RTT viewer can be used to display log messages received from the target. The base
|
The Segger RTT viewer can be used to display log messages received from the target. The base
|
||||||
address for the RTT block placement is 0x10000000. It is recommended to use a search range of
|
address for the RTT block placement is 0x10000000. It is recommended to use a search range of
|
||||||
0x1000 around that base address when using the RTT viewer.
|
0x1000 around that base address when using the RTT viewer.
|
||||||
|
|
||||||
The RTT viewer will not be able to process `defmt` printouts. However, you can view the defmt
|
|
||||||
logs by [installing defmt-print](https://crates.io/crates/defmt-print) first and then running
|
|
||||||
|
|
||||||
```sh
|
|
||||||
defmt-print -e <pathToElfFile> tcp
|
|
||||||
```
|
|
||||||
|
|
||||||
The path of the ELF file which is being debugged needs to be specified for this to work.
|
|
||||||
|
|
||||||
## Learning (Embedded) Rust
|
|
||||||
|
|
||||||
If you are unfamiliar with Rust on Embedded Systems or Rust in general, the following resources
|
|
||||||
are recommended:
|
|
||||||
|
|
||||||
- [Rust Book](https://doc.rust-lang.org/book/)
|
|
||||||
- [Embedded Rust Book](https://docs.rust-embedded.org/book/)
|
|
||||||
- [Embedded Rust Discovery](https://docs.rust-embedded.org/discovery/microbit/)
|
|
||||||
- [Awesome Embedded Rust](https://github.com/rust-embedded/awesome-embedded-rust)
|
|
||||||
|
@ -6,13 +6,14 @@ edition = "2021"
|
|||||||
[dependencies]
|
[dependencies]
|
||||||
cortex-m = { version = "0.7.6", features = ["critical-section-single-core"] }
|
cortex-m = { version = "0.7.6", features = ["critical-section-single-core"] }
|
||||||
cortex-m-rt = "0.7"
|
cortex-m-rt = "0.7"
|
||||||
defmt = "1"
|
panic-halt = "1"
|
||||||
defmt-rtt = "0.4"
|
rtt-target = "0.6"
|
||||||
panic-probe = { version = "0.3", features = ["print-defmt"] }
|
panic-rtt-target = "0.2"
|
||||||
embedded-hal = "1"
|
embedded-hal = "1"
|
||||||
|
embedded-hal-nb = "1"
|
||||||
|
embedded-io = "0.6"
|
||||||
|
|
||||||
[dependencies.va108xx-hal]
|
[dependencies.va108xx-hal]
|
||||||
version = "0.11"
|
version = "0.10.0"
|
||||||
features = ["rt"]
|
features = ["rt"]
|
||||||
path = "../va108xx-hal"
|
|
||||||
|
|
||||||
|
@ -7,21 +7,18 @@
|
|||||||
|
|
||||||
use cortex_m_rt::entry;
|
use cortex_m_rt::entry;
|
||||||
use embedded_hal::delay::DelayNs;
|
use embedded_hal::delay::DelayNs;
|
||||||
// Logging provider
|
use panic_rtt_target as _;
|
||||||
use defmt_rtt as _;
|
use rtt_target::{rprintln, rtt_init_print};
|
||||||
// Panic provider
|
|
||||||
use panic_probe as _;
|
|
||||||
use va108xx_hal::{
|
use va108xx_hal::{
|
||||||
gpio::{regs::Gpio, Input, Output, PinState, Pull},
|
gpio::{PinState, PinsA, PinsB},
|
||||||
pac,
|
pac::{self, interrupt},
|
||||||
pins::{PinsA, PinsB, Port},
|
|
||||||
prelude::*,
|
prelude::*,
|
||||||
time::Hertz,
|
time::Hertz,
|
||||||
timer::CountdownTimer,
|
timer::{default_ms_irq_handler, set_up_ms_tick, CountdownTimer, InterruptConfig},
|
||||||
};
|
};
|
||||||
|
|
||||||
#[allow(dead_code)]
|
#[allow(dead_code)]
|
||||||
#[derive(Debug, defmt::Format)]
|
#[derive(Debug)]
|
||||||
enum TestCase {
|
enum TestCase {
|
||||||
// Tie PORTA[0] to PORTA[1] for these tests!
|
// Tie PORTA[0] to PORTA[1] for these tests!
|
||||||
TestBasic,
|
TestBasic,
|
||||||
@ -35,18 +32,18 @@ enum TestCase {
|
|||||||
Pulse,
|
Pulse,
|
||||||
// Tie PA0, PA1 and PA3 to an oscilloscope
|
// Tie PA0, PA1 and PA3 to an oscilloscope
|
||||||
DelayGpio,
|
DelayGpio,
|
||||||
// PA0 can be checked with an oscillsope to verify timing correctness.
|
|
||||||
DelayMs,
|
DelayMs,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[entry]
|
#[entry]
|
||||||
fn main() -> ! {
|
fn main() -> ! {
|
||||||
defmt::println!("-- VA108xx Test Application --");
|
rtt_init_print!();
|
||||||
let dp = pac::Peripherals::take().unwrap();
|
rprintln!("-- VA108xx Test Application --");
|
||||||
|
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(dp.porta);
|
let pinsa = PinsA::new(&mut dp.sysconfig, dp.porta);
|
||||||
let pinsb = PinsB::new(dp.portb);
|
let pinsb = PinsB::new(&mut dp.sysconfig, dp.portb);
|
||||||
let mut led1 = Output::new(pinsa.pa10, PinState::Low);
|
let mut led1 = pinsa.pa10.into_readable_push_pull_output();
|
||||||
let test_case = TestCase::DelayMs;
|
let test_case = TestCase::DelayMs;
|
||||||
|
|
||||||
match test_case {
|
match test_case {
|
||||||
@ -54,20 +51,20 @@ fn main() -> ! {
|
|||||||
| TestCase::TestPulldown
|
| TestCase::TestPulldown
|
||||||
| TestCase::TestPullup
|
| TestCase::TestPullup
|
||||||
| TestCase::TestMask => {
|
| TestCase::TestMask => {
|
||||||
defmt::info!(
|
rprintln!(
|
||||||
"Test case {:?}. Make sure to tie PORTA[0] to PORTA[1]",
|
"Test case {:?}. Make sure to tie PORTA[0] to PORTA[1]",
|
||||||
test_case
|
test_case
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
_ => {
|
_ => {
|
||||||
defmt::info!("Test case {:?}", test_case);
|
rprintln!("Test case {:?}", test_case);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
match test_case {
|
match test_case {
|
||||||
TestCase::TestBasic => {
|
TestCase::TestBasic => {
|
||||||
// Tie PORTA[0] to PORTA[1] for these tests!
|
// Tie PORTA[0] to PORTA[1] for these tests!
|
||||||
let mut out = Output::new(pinsa.pa0, PinState::Low);
|
let mut out = pinsa.pa0.into_readable_push_pull_output();
|
||||||
let input = Input::new_floating(pinsa.pa1);
|
let input = pinsa.pa1.into_floating_input();
|
||||||
out.set_high();
|
out.set_high();
|
||||||
assert!(input.is_high());
|
assert!(input.is_high());
|
||||||
out.set_low();
|
out.set_low();
|
||||||
@ -75,74 +72,73 @@ fn main() -> ! {
|
|||||||
}
|
}
|
||||||
TestCase::TestPullup => {
|
TestCase::TestPullup => {
|
||||||
// Tie PORTA[0] to PORTA[1] for these tests!
|
// Tie PORTA[0] to PORTA[1] for these tests!
|
||||||
let input = Input::new_with_pull(pinsa.pa1, Pull::Up);
|
let input = pinsa.pa1.into_pull_up_input();
|
||||||
assert!(input.is_high());
|
assert!(input.is_high());
|
||||||
let mut out = Output::new(pinsa.pa0, PinState::Low);
|
let mut out = pinsa.pa0.into_readable_push_pull_output();
|
||||||
out.set_low();
|
out.set_low();
|
||||||
assert!(input.is_low());
|
assert!(input.is_low());
|
||||||
out.set_high();
|
out.set_high();
|
||||||
assert!(input.is_high());
|
assert!(input.is_high());
|
||||||
|
out.into_floating_input();
|
||||||
|
assert!(input.is_high());
|
||||||
}
|
}
|
||||||
TestCase::TestPulldown => {
|
TestCase::TestPulldown => {
|
||||||
// Tie PORTA[0] to PORTA[1] for these tests!
|
// Tie PORTA[0] to PORTA[1] for these tests!
|
||||||
let input = Input::new_with_pull(pinsa.pa1, Pull::Down);
|
let input = pinsa.pa1.into_pull_down_input();
|
||||||
assert!(input.is_low());
|
assert!(input.is_low());
|
||||||
let mut out = Output::new(pinsa.pa0, PinState::Low);
|
let mut out = pinsa.pa0.into_push_pull_output();
|
||||||
out.set_low();
|
out.set_low();
|
||||||
assert!(input.is_low());
|
assert!(input.is_low());
|
||||||
out.set_high();
|
out.set_high();
|
||||||
assert!(input.is_high());
|
assert!(input.is_high());
|
||||||
|
out.into_floating_input();
|
||||||
|
assert!(input.is_low());
|
||||||
}
|
}
|
||||||
TestCase::TestMask => {
|
TestCase::TestMask => {
|
||||||
// Tie PORTA[0] to PORTA[1] for these tests!
|
// Tie PORTA[0] to PORTA[1] for these tests!
|
||||||
// Need to test this low-level..
|
let mut input = pinsa.pa1.into_pull_down_input();
|
||||||
/*
|
|
||||||
let mut input = Input::new_with_pull(pinsa.pa1, Pull::Down);
|
|
||||||
input.clear_datamask();
|
input.clear_datamask();
|
||||||
assert!(!input.datamask());
|
assert!(!input.datamask());
|
||||||
let mut out = pinsa.pa0.into_push_pull_output();
|
let mut out = pinsa.pa0.into_push_pull_output();
|
||||||
out.clear_datamask();
|
out.clear_datamask();
|
||||||
assert!(input.is_low_masked().is_err());
|
assert!(input.is_low_masked().is_err());
|
||||||
assert!(out.set_high_masked().is_err());
|
assert!(out.set_high_masked().is_err());
|
||||||
*/
|
|
||||||
}
|
}
|
||||||
TestCase::PortB => {
|
TestCase::PortB => {
|
||||||
// Tie PORTB[22] to PORTB[23] for these tests!
|
// Tie PORTB[22] to PORTB[23] for these tests!
|
||||||
let mut out = Output::new(pinsb.pb22, PinState::Low);
|
let mut out = pinsb.pb22.into_readable_push_pull_output();
|
||||||
let input = Input::new_floating(pinsb.pb23);
|
let input = pinsb.pb23.into_floating_input();
|
||||||
out.set_high();
|
out.set_high();
|
||||||
assert!(input.is_high());
|
assert!(input.is_high());
|
||||||
out.set_low();
|
out.set_low();
|
||||||
assert!(input.is_low());
|
assert!(input.is_low());
|
||||||
}
|
}
|
||||||
TestCase::Perid => {
|
TestCase::Perid => {
|
||||||
let mmio_porta = Gpio::new_mmio(Port::A);
|
assert_eq!(PinsA::get_perid(), 0x004007e1);
|
||||||
assert_eq!(mmio_porta.read_perid(), 0x004007e1);
|
assert_eq!(PinsB::get_perid(), 0x004007e1);
|
||||||
let mmio_porta = Gpio::new_mmio(Port::B);
|
|
||||||
assert_eq!(mmio_porta.read_perid(), 0x004007e1);
|
|
||||||
}
|
}
|
||||||
TestCase::Pulse => {
|
TestCase::Pulse => {
|
||||||
let mut output_pulsed = Output::new(pinsa.pa0, PinState::Low);
|
let mut output_pulsed = pinsa.pa0.into_push_pull_output();
|
||||||
output_pulsed.configure_pulse_mode(true, PinState::Low);
|
output_pulsed.configure_pulse_mode(true, PinState::Low);
|
||||||
defmt::info!("Pulsing high 10 times..");
|
rprintln!("Pulsing high 10 times..");
|
||||||
output_pulsed.set_low();
|
output_pulsed.set_low();
|
||||||
for _ in 0..10 {
|
for _ in 0..10 {
|
||||||
output_pulsed.set_high();
|
output_pulsed.set_high();
|
||||||
cortex_m::asm::delay(25_000_000);
|
cortex_m::asm::delay(25_000_000);
|
||||||
}
|
}
|
||||||
output_pulsed.configure_pulse_mode(true, PinState::High);
|
output_pulsed.configure_pulse_mode(true, PinState::High);
|
||||||
defmt::info!("Pulsing low 10 times..");
|
rprintln!("Pulsing low 10 times..");
|
||||||
for _ in 0..10 {
|
for _ in 0..10 {
|
||||||
output_pulsed.set_low();
|
output_pulsed.set_low();
|
||||||
cortex_m::asm::delay(25_000_000);
|
cortex_m::asm::delay(25_000_000);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
TestCase::DelayGpio => {
|
TestCase::DelayGpio => {
|
||||||
let mut out_0 = Output::new(pinsa.pa0, PinState::Low);
|
let mut out_0 = pinsa.pa0.into_readable_push_pull_output();
|
||||||
out_0.configure_delay(true, false);
|
out_0.configure_delay(true, false);
|
||||||
let mut out_1 = Output::new(pinsa.pa1, PinState::Low);
|
let mut out_1 = pinsa.pa1.into_readable_push_pull_output();
|
||||||
out_1.configure_delay(false, true);
|
out_1.configure_delay(false, true);
|
||||||
let mut out_2 = Output::new(pinsa.pa3, PinState::Low);
|
let mut out_2 = pinsa.pa3.into_readable_push_pull_output();
|
||||||
out_2.configure_delay(true, true);
|
out_2.configure_delay(true, true);
|
||||||
for _ in 0..20 {
|
for _ in 0..20 {
|
||||||
out_0.toggle();
|
out_0.toggle();
|
||||||
@ -152,8 +148,22 @@ fn main() -> ! {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
TestCase::DelayMs => {
|
TestCase::DelayMs => {
|
||||||
let mut delay_timer = CountdownTimer::new(dp.tim1, 50.MHz());
|
let mut ms_timer = set_up_ms_tick(
|
||||||
let mut pa0 = Output::new(pinsa.pa0, PinState::Low);
|
InterruptConfig::new(pac::Interrupt::OC0, true, true),
|
||||||
|
&mut dp.sysconfig,
|
||||||
|
Some(&mut dp.irqsel),
|
||||||
|
50.MHz(),
|
||||||
|
dp.tim0,
|
||||||
|
);
|
||||||
|
for _ in 0..5 {
|
||||||
|
led1.toggle();
|
||||||
|
ms_timer.delay_ms(500);
|
||||||
|
led1.toggle();
|
||||||
|
ms_timer.delay_ms(500);
|
||||||
|
}
|
||||||
|
|
||||||
|
let mut delay_timer = CountdownTimer::new(&mut dp.sysconfig, 50.MHz(), dp.tim1);
|
||||||
|
let mut pa0 = pinsa.pa0.into_readable_push_pull_output();
|
||||||
for _ in 0..5 {
|
for _ in 0..5 {
|
||||||
led1.toggle();
|
led1.toggle();
|
||||||
delay_timer.delay_ms(500);
|
delay_timer.delay_ms(500);
|
||||||
@ -162,23 +172,30 @@ fn main() -> ! {
|
|||||||
}
|
}
|
||||||
let ahb_freq: Hertz = 50.MHz();
|
let ahb_freq: Hertz = 50.MHz();
|
||||||
let mut syst_delay = cortex_m::delay::Delay::new(cp.SYST, ahb_freq.raw());
|
let mut syst_delay = cortex_m::delay::Delay::new(cp.SYST, ahb_freq.raw());
|
||||||
// Release image should be used to verify timings for pin PA0
|
// Test usecond delay using both TIM peripheral and SYST. Use the release image if you
|
||||||
for _ in 0..5 {
|
// want to verify the timings!
|
||||||
pa0.toggle();
|
loop {
|
||||||
syst_delay.delay_us(50);
|
|
||||||
pa0.toggle();
|
|
||||||
syst_delay.delay_us(50);
|
|
||||||
pa0.toggle();
|
pa0.toggle();
|
||||||
delay_timer.delay_us(50);
|
delay_timer.delay_us(50);
|
||||||
pa0.toggle();
|
pa0.toggle();
|
||||||
delay_timer.delay_us(50);
|
delay_timer.delay_us(50);
|
||||||
|
pa0.toggle();
|
||||||
|
syst_delay.delay_us(50);
|
||||||
|
pa0.toggle();
|
||||||
|
syst_delay.delay_us(50);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
defmt::info!("Test success");
|
rprintln!("Test success");
|
||||||
loop {
|
loop {
|
||||||
led1.toggle();
|
led1.toggle();
|
||||||
cortex_m::asm::delay(25_000_000);
|
cortex_m::asm::delay(25_000_000);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[interrupt]
|
||||||
|
#[allow(non_snake_case)]
|
||||||
|
fn OC0() {
|
||||||
|
default_ms_irq_handler()
|
||||||
|
}
|
||||||
|
@ -7,21 +7,18 @@ edition = "2021"
|
|||||||
cortex-m = "0.7"
|
cortex-m = "0.7"
|
||||||
cortex-m-rt = "0.7"
|
cortex-m-rt = "0.7"
|
||||||
embedded-hal = "1"
|
embedded-hal = "1"
|
||||||
defmt-rtt = "0.4"
|
panic-rtt-target = "0.2"
|
||||||
defmt = "1"
|
panic-halt = "1"
|
||||||
panic-probe = { version = "1", features = ["defmt"] }
|
rtt-target = "0.6"
|
||||||
crc = "3"
|
crc = "3"
|
||||||
num_enum = { version = "0.7", default-features = false }
|
num_enum = { version = "0.7", default-features = false }
|
||||||
static_assertions = "1"
|
static_assertions = "1"
|
||||||
|
|
||||||
[dependencies.va108xx-hal]
|
[dependencies.va108xx-hal]
|
||||||
version = "0.11"
|
version = "0.10"
|
||||||
path = "../va108xx-hal"
|
|
||||||
features = ["defmt"]
|
|
||||||
|
|
||||||
[dependencies.vorago-reb1]
|
[dependencies.vorago-reb1]
|
||||||
version = "0.8"
|
version = "0.8"
|
||||||
path = "../vorago-reb1"
|
|
||||||
|
|
||||||
[features]
|
[features]
|
||||||
default = []
|
default = []
|
||||||
|
@ -6,16 +6,17 @@ use cortex_m_rt::entry;
|
|||||||
use crc::{Crc, CRC_16_IBM_3740};
|
use crc::{Crc, CRC_16_IBM_3740};
|
||||||
use embedded_hal::delay::DelayNs;
|
use embedded_hal::delay::DelayNs;
|
||||||
use num_enum::TryFromPrimitive;
|
use num_enum::TryFromPrimitive;
|
||||||
// Import panic provider.
|
#[cfg(not(feature = "rtt-panic"))]
|
||||||
use panic_probe as _;
|
use panic_halt as _;
|
||||||
// Import logger.
|
#[cfg(feature = "rtt-panic")]
|
||||||
use defmt_rtt as _;
|
use panic_rtt_target as _;
|
||||||
use va108xx_hal::{pac, spi::SpiClkConfig, time::Hertz, timer::CountdownTimer};
|
use rtt_target::{rprintln, rtt_init_print};
|
||||||
|
use va108xx_hal::{pac, time::Hertz, timer::CountdownTimer};
|
||||||
use vorago_reb1::m95m01::M95M01;
|
use vorago_reb1::m95m01::M95M01;
|
||||||
|
|
||||||
// Useful for debugging and see what the bootloader is doing. Enabled currently, because
|
// Useful for debugging and see what the bootloader is doing. Enabled currently, because
|
||||||
// the binary stays small enough.
|
// the binary stays small enough.
|
||||||
const DEFMT_PRINTOUT: bool = true;
|
const RTT_PRINTOUT: bool = true;
|
||||||
const DEBUG_PRINTOUTS: bool = true;
|
const DEBUG_PRINTOUTS: bool = true;
|
||||||
// Small delay, allows RTT printout to catch up.
|
// Small delay, allows RTT printout to catch up.
|
||||||
const BOOT_DELAY_MS: u32 = 2000;
|
const BOOT_DELAY_MS: u32 = 2000;
|
||||||
@ -73,7 +74,7 @@ pub const PREFERRED_SLOT_OFFSET: u32 = 0x20000 - 1;
|
|||||||
|
|
||||||
const CRC_ALGO: Crc<u16> = Crc::<u16>::new(&CRC_16_IBM_3740);
|
const CRC_ALGO: Crc<u16> = Crc::<u16>::new(&CRC_16_IBM_3740);
|
||||||
|
|
||||||
#[derive(Debug, Copy, Clone, PartialEq, Eq, TryFromPrimitive, defmt::Format)]
|
#[derive(Debug, Copy, Clone, PartialEq, Eq, TryFromPrimitive)]
|
||||||
#[repr(u8)]
|
#[repr(u8)]
|
||||||
enum AppSel {
|
enum AppSel {
|
||||||
A = 0,
|
A = 0,
|
||||||
@ -99,15 +100,15 @@ impl NvmInterface for NvmWrapper {
|
|||||||
|
|
||||||
#[entry]
|
#[entry]
|
||||||
fn main() -> ! {
|
fn main() -> ! {
|
||||||
if DEFMT_PRINTOUT {
|
if RTT_PRINTOUT {
|
||||||
defmt::println!("-- VA108xx bootloader --");
|
rtt_init_print!();
|
||||||
|
rprintln!("-- VA108xx bootloader --");
|
||||||
}
|
}
|
||||||
let 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 mut timer = CountdownTimer::new(dp.tim0, CLOCK_FREQ);
|
let mut timer = CountdownTimer::new(&mut dp.sysconfig, CLOCK_FREQ, dp.tim0);
|
||||||
|
|
||||||
let clk_config = SpiClkConfig::new(2, 4);
|
let mut nvm = M95M01::new(&mut dp.sysconfig, CLOCK_FREQ, dp.spic);
|
||||||
let mut nvm = M95M01::new(dp.spic, clk_config);
|
|
||||||
|
|
||||||
if FLASH_SELF {
|
if FLASH_SELF {
|
||||||
let mut first_four_bytes: [u8; 4] = [0; 4];
|
let mut first_four_bytes: [u8; 4] = [0; 4];
|
||||||
@ -130,21 +131,21 @@ fn main() -> ! {
|
|||||||
nvm.write(0x4, bootloader_data)
|
nvm.write(0x4, bootloader_data)
|
||||||
.expect("writing to NVM failed");
|
.expect("writing to NVM failed");
|
||||||
if let Err(e) = nvm.verify(0x0, &first_four_bytes) {
|
if let Err(e) = nvm.verify(0x0, &first_four_bytes) {
|
||||||
if DEFMT_PRINTOUT {
|
if RTT_PRINTOUT {
|
||||||
defmt::error!("verification of self-flash to NVM failed: {:?}", e);
|
rprintln!("verification of self-flash to NVM failed: {:?}", e);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if let Err(e) = nvm.verify(0x4, bootloader_data) {
|
if let Err(e) = nvm.verify(0x4, bootloader_data) {
|
||||||
if DEFMT_PRINTOUT {
|
if RTT_PRINTOUT {
|
||||||
defmt::error!("verification of self-flash to NVM failed: {:?}", e);
|
rprintln!("verification of self-flash to NVM failed: {:?}", e);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
nvm.write(BOOTLOADER_CRC_ADDR as usize, &bootloader_crc.to_be_bytes())
|
nvm.write(BOOTLOADER_CRC_ADDR as usize, &bootloader_crc.to_be_bytes())
|
||||||
.expect("writing CRC failed");
|
.expect("writing CRC failed");
|
||||||
if let Err(e) = nvm.verify(BOOTLOADER_CRC_ADDR as usize, &bootloader_crc.to_be_bytes()) {
|
if let Err(e) = nvm.verify(BOOTLOADER_CRC_ADDR as usize, &bootloader_crc.to_be_bytes()) {
|
||||||
if DEFMT_PRINTOUT {
|
if RTT_PRINTOUT {
|
||||||
defmt::error!(
|
rprintln!(
|
||||||
"error: CRC verification for bootloader self-flash failed: {:?}",
|
"error: CRC verification for bootloader self-flash failed: {:?}",
|
||||||
e
|
e
|
||||||
);
|
);
|
||||||
@ -172,8 +173,8 @@ fn main() -> ! {
|
|||||||
} else if check_app_crc(other_app) {
|
} else if check_app_crc(other_app) {
|
||||||
boot_app(&dp.sysconfig, &cp, other_app, &mut timer)
|
boot_app(&dp.sysconfig, &cp, other_app, &mut timer)
|
||||||
} else {
|
} else {
|
||||||
if DEBUG_PRINTOUTS && DEFMT_PRINTOUT {
|
if DEBUG_PRINTOUTS && RTT_PRINTOUT {
|
||||||
defmt::error!("both images corrupt! booting image A");
|
rprintln!("both images corrupt! booting image A");
|
||||||
}
|
}
|
||||||
// TODO: Shift a CCSDS packet out to inform host/OBC about image corruption.
|
// TODO: Shift a CCSDS packet out to inform host/OBC about image corruption.
|
||||||
// Both images seem to be corrupt. Boot default image A.
|
// Both images seem to be corrupt. Boot default image A.
|
||||||
@ -185,7 +186,7 @@ fn check_own_crc(
|
|||||||
sysconfig: &pac::Sysconfig,
|
sysconfig: &pac::Sysconfig,
|
||||||
cp: &cortex_m::Peripherals,
|
cp: &cortex_m::Peripherals,
|
||||||
nvm: &mut NvmWrapper,
|
nvm: &mut NvmWrapper,
|
||||||
timer: &mut CountdownTimer,
|
timer: &mut CountdownTimer<pac::Tim0>,
|
||||||
) {
|
) {
|
||||||
let crc_exp = unsafe { (BOOTLOADER_CRC_ADDR as *const u16).read_unaligned().to_be() };
|
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
|
// I'd prefer to use [core::slice::from_raw_parts], but that is problematic
|
||||||
@ -203,8 +204,8 @@ fn check_own_crc(
|
|||||||
});
|
});
|
||||||
let crc_calc = digest.finalize();
|
let crc_calc = digest.finalize();
|
||||||
if crc_exp == 0x0000 || crc_exp == 0xffff {
|
if crc_exp == 0x0000 || crc_exp == 0xffff {
|
||||||
if DEBUG_PRINTOUTS && DEFMT_PRINTOUT {
|
if DEBUG_PRINTOUTS && RTT_PRINTOUT {
|
||||||
defmt::info!("BL CRC blank - prog new CRC");
|
rprintln!("BL CRC blank - prog new CRC");
|
||||||
}
|
}
|
||||||
// Blank CRC, write it to NVM.
|
// Blank CRC, write it to NVM.
|
||||||
nvm.write(BOOTLOADER_CRC_ADDR as usize, &crc_calc.to_be_bytes())
|
nvm.write(BOOTLOADER_CRC_ADDR as usize, &crc_calc.to_be_bytes())
|
||||||
@ -214,8 +215,8 @@ fn check_own_crc(
|
|||||||
// cortex_m::peripheral::SCB::sys_reset();
|
// cortex_m::peripheral::SCB::sys_reset();
|
||||||
} else if crc_exp != crc_calc {
|
} else if crc_exp != crc_calc {
|
||||||
// Bootloader is corrupted. Try to run App A.
|
// Bootloader is corrupted. Try to run App A.
|
||||||
if DEBUG_PRINTOUTS && DEFMT_PRINTOUT {
|
if DEBUG_PRINTOUTS && RTT_PRINTOUT {
|
||||||
defmt::warn!(
|
rprintln!(
|
||||||
"bootloader CRC corrupt, read {} and expected {}. booting image A immediately",
|
"bootloader CRC corrupt, read {} and expected {}. booting image A immediately",
|
||||||
crc_calc,
|
crc_calc,
|
||||||
crc_exp
|
crc_exp
|
||||||
@ -240,8 +241,8 @@ fn read_four_bytes_at_addr_zero(buf: &mut [u8; 4]) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
fn check_app_crc(app_sel: AppSel) -> bool {
|
fn check_app_crc(app_sel: AppSel) -> bool {
|
||||||
if DEBUG_PRINTOUTS && DEFMT_PRINTOUT {
|
if DEBUG_PRINTOUTS && RTT_PRINTOUT {
|
||||||
defmt::info!("Checking image {:?}", app_sel);
|
rprintln!("Checking image {:?}", app_sel);
|
||||||
}
|
}
|
||||||
if app_sel == AppSel::A {
|
if app_sel == AppSel::A {
|
||||||
check_app_given_addr(APP_A_CRC_ADDR, APP_A_START_ADDR, APP_A_SIZE_ADDR)
|
check_app_given_addr(APP_A_CRC_ADDR, APP_A_START_ADDR, APP_A_SIZE_ADDR)
|
||||||
@ -255,8 +256,8 @@ fn check_app_given_addr(crc_addr: u32, start_addr: u32, image_size_addr: u32) ->
|
|||||||
let image_size = unsafe { (image_size_addr as *const u32).read_unaligned().to_be() };
|
let image_size = unsafe { (image_size_addr as *const u32).read_unaligned().to_be() };
|
||||||
// Sanity check.
|
// Sanity check.
|
||||||
if image_size > APP_A_END_ADDR - APP_A_START_ADDR - 8 {
|
if image_size > APP_A_END_ADDR - APP_A_START_ADDR - 8 {
|
||||||
if DEFMT_PRINTOUT {
|
if RTT_PRINTOUT {
|
||||||
defmt::error!("detected invalid app size {}", image_size);
|
rprintln!("detected invalid app size {}", image_size);
|
||||||
}
|
}
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
@ -275,10 +276,10 @@ fn boot_app(
|
|||||||
syscfg: &pac::Sysconfig,
|
syscfg: &pac::Sysconfig,
|
||||||
cp: &cortex_m::Peripherals,
|
cp: &cortex_m::Peripherals,
|
||||||
app_sel: AppSel,
|
app_sel: AppSel,
|
||||||
timer: &mut CountdownTimer,
|
timer: &mut CountdownTimer<pac::Tim0>,
|
||||||
) -> ! {
|
) -> ! {
|
||||||
if DEBUG_PRINTOUTS && DEFMT_PRINTOUT {
|
if DEBUG_PRINTOUTS && RTT_PRINTOUT {
|
||||||
defmt::info!("booting app {:?}", app_sel);
|
rprintln!("booting app {:?}", app_sel);
|
||||||
}
|
}
|
||||||
timer.delay_ms(BOOT_DELAY_MS);
|
timer.delay_ms(BOOT_DELAY_MS);
|
||||||
|
|
||||||
|
48
defmt-testapp/.cargo/config.toml
Normal file
48
defmt-testapp/.cargo/config.toml
Normal file
@ -0,0 +1,48 @@
|
|||||||
|
[target.'cfg(all(target_arch = "arm", target_os = "none"))']
|
||||||
|
# uncomment ONE of these three option to make `cargo run` start a GDB session
|
||||||
|
# which option to pick depends on your system
|
||||||
|
# runner = "arm-none-eabi-gdb -q -x openocd.gdb"
|
||||||
|
# runner = "gdb-multiarch -q -x openocd.gdb"
|
||||||
|
# runner = "gdb -q -x openocd.gdb"
|
||||||
|
runner = "gdb-multiarch -q -x jlink.gdb"
|
||||||
|
|
||||||
|
# Probe-rs is currently problematic: https://github.com/probe-rs/probe-rs/issues/2567
|
||||||
|
# runner = "probe-rs run --chip VA108xx --chip-description-path ./scripts/VA108xx_Series.yaml"
|
||||||
|
# runner = ["probe-rs", "run", "--chip", "$CHIP", "--log-format", "{L} {s}"]
|
||||||
|
|
||||||
|
rustflags = [
|
||||||
|
# This is needed if your flash or ram addresses are not aligned to 0x10000 in memory.x
|
||||||
|
# See https://github.com/rust-embedded/cortex-m-quickstart/pull/95
|
||||||
|
"-C", "link-arg=--nmagic",
|
||||||
|
|
||||||
|
# LLD (shipped with the Rust toolchain) is used as the default linker
|
||||||
|
"-C", "link-arg=-Tlink.x",
|
||||||
|
|
||||||
|
# knurling-rs tooling. If you want to use flip-link, ensure it is installed first.
|
||||||
|
"-C", "linker=flip-link",
|
||||||
|
# Unfortunately, defmt is clunky to use without probe-rs..
|
||||||
|
"-C", "link-arg=-Tdefmt.x",
|
||||||
|
|
||||||
|
# Can be useful for debugging.
|
||||||
|
"-Clink-args=-Map=app.map"
|
||||||
|
|
||||||
|
]
|
||||||
|
|
||||||
|
[build]
|
||||||
|
# Pick ONE of these compilation targets
|
||||||
|
target = "thumbv6m-none-eabi" # Cortex-M0 and Cortex-M0+
|
||||||
|
# target = "thumbv7m-none-eabi" # Cortex-M3
|
||||||
|
# target = "thumbv7em-none-eabi" # Cortex-M4 and Cortex-M7 (no FPU)
|
||||||
|
# target = "thumbv7em-none-eabihf" # Cortex-M4F and Cortex-M7F (with FPU)
|
||||||
|
# target = "thumbv8m.base-none-eabi" # Cortex-M23
|
||||||
|
# target = "thumbv8m.main-none-eabi" # Cortex-M33 (no FPU)
|
||||||
|
# target = "thumbv8m.main-none-eabihf" # Cortex-M33 (with FPU)
|
||||||
|
|
||||||
|
[alias]
|
||||||
|
re = "run --example"
|
||||||
|
rb = "run --bin"
|
||||||
|
rrb = "run --release --bin"
|
||||||
|
ut = "test --target x86_64-unknown-linux-gnu"
|
||||||
|
|
||||||
|
[env]
|
||||||
|
DEFMT_LOG = "info"
|
1
defmt-testapp/.gitignore
vendored
Normal file
1
defmt-testapp/.gitignore
vendored
Normal file
@ -0,0 +1 @@
|
|||||||
|
/target
|
34
defmt-testapp/Cargo.toml
Normal file
34
defmt-testapp/Cargo.toml
Normal file
@ -0,0 +1,34 @@
|
|||||||
|
[package]
|
||||||
|
name = "defmt-testapp"
|
||||||
|
version = "0.1.0"
|
||||||
|
edition = "2021"
|
||||||
|
|
||||||
|
[dependencies]
|
||||||
|
cortex-m = {version = "0.7", features = ["critical-section-single-core"]}
|
||||||
|
panic-rtt-target = "0.1"
|
||||||
|
cortex-m-rt = "0.7"
|
||||||
|
rtt-target = "0.5"
|
||||||
|
rtic-sync = { version = "1.3", features = ["defmt-03"] }
|
||||||
|
embedded-hal = "1"
|
||||||
|
embedded-hal-nb = "1"
|
||||||
|
embedded-io = "0.6"
|
||||||
|
cortex-m-semihosting = "0.5.0"
|
||||||
|
# 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]
|
||||||
|
version = "0.10"
|
||||||
|
features = ["rt", "defmt"]
|
||||||
|
|
||||||
|
[dependencies.va108xx]
|
||||||
|
version = "0.5"
|
9
defmt-testapp/README.md
Normal file
9
defmt-testapp/README.md
Normal file
@ -0,0 +1,9 @@
|
|||||||
|
defmt Testapp
|
||||||
|
======
|
||||||
|
|
||||||
|
`defmt` is clunky to use without probe-rs and requires special configuration inside the
|
||||||
|
`.cargo/config.toml` file.
|
||||||
|
|
||||||
|
`probe-rs` is currently problematic for usage with the VA108xx , so it is not the default tool
|
||||||
|
recommended and used for the whole workspace. This project contains an isolated, `defmt` compatible
|
||||||
|
configuration for testing with `defmt` (and `probe-rs`).
|
53
defmt-testapp/src/lib.rs
Normal file
53
defmt-testapp/src/lib.rs
Normal file
@ -0,0 +1,53 @@
|
|||||||
|
#![no_main]
|
||||||
|
#![no_std]
|
||||||
|
|
||||||
|
use cortex_m_semihosting::debug;
|
||||||
|
|
||||||
|
use defmt_brtt as _; // global logger
|
||||||
|
|
||||||
|
use va108xx_hal as _; // memory layout
|
||||||
|
|
||||||
|
use panic_probe as _;
|
||||||
|
|
||||||
|
// same panicking *behavior* as `panic-probe` but doesn't print a panic message
|
||||||
|
// this prevents the panic message being printed *twice* when `defmt::panic` is invoked
|
||||||
|
// #[defmt::panic_handler]
|
||||||
|
/*
|
||||||
|
fn panic() -> ! {
|
||||||
|
cortex_m::asm::udf()
|
||||||
|
}
|
||||||
|
*/
|
||||||
|
|
||||||
|
/// Terminates the application and makes a semihosting-capable debug tool exit
|
||||||
|
/// with status code 0.
|
||||||
|
pub fn exit() -> ! {
|
||||||
|
loop {
|
||||||
|
debug::exit(debug::EXIT_SUCCESS);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Hardfault handler.
|
||||||
|
///
|
||||||
|
/// Terminates the application and makes a semihosting-capable debug tool exit
|
||||||
|
/// with an error. This seems better than the default, which is to spin in a
|
||||||
|
/// loop.
|
||||||
|
#[cortex_m_rt::exception]
|
||||||
|
unsafe fn HardFault(_frame: &cortex_m_rt::ExceptionFrame) -> ! {
|
||||||
|
loop {
|
||||||
|
debug::exit(debug::EXIT_FAILURE);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// defmt-test 0.3.0 has the limitation that this `#[tests]` attribute can only be used
|
||||||
|
// once within a crate. the module can be in any file but there can only be at most
|
||||||
|
// one `#[tests]` module in this library crate
|
||||||
|
#[cfg(test)]
|
||||||
|
#[defmt_test::tests]
|
||||||
|
mod unit_tests {
|
||||||
|
use defmt::assert;
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn it_works() {
|
||||||
|
assert!(true)
|
||||||
|
}
|
||||||
|
}
|
29
defmt-testapp/src/main.rs
Normal file
29
defmt-testapp/src/main.rs
Normal file
@ -0,0 +1,29 @@
|
|||||||
|
//! Empty RTIC project template
|
||||||
|
#![no_main]
|
||||||
|
#![no_std]
|
||||||
|
|
||||||
|
use defmt_testapp as _;
|
||||||
|
|
||||||
|
#[rtic::app(device = pac)]
|
||||||
|
mod app {
|
||||||
|
use va108xx_hal::pac;
|
||||||
|
|
||||||
|
#[local]
|
||||||
|
struct Local {}
|
||||||
|
|
||||||
|
#[shared]
|
||||||
|
struct Shared {}
|
||||||
|
|
||||||
|
#[init]
|
||||||
|
fn init(_ctx: init::Context) -> (Shared, Local) {
|
||||||
|
defmt::println!("-- Vorago RTIC template --");
|
||||||
|
(Shared {}, Local {})
|
||||||
|
}
|
||||||
|
|
||||||
|
// `shared` cannot be accessed from this context
|
||||||
|
#[idle]
|
||||||
|
fn idle(_cx: idle::Context) -> ! {
|
||||||
|
#[allow(clippy::empty_loop)]
|
||||||
|
loop {}
|
||||||
|
}
|
||||||
|
}
|
@ -5,21 +5,19 @@ edition = "2021"
|
|||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
cfg-if = "1"
|
cfg-if = "1"
|
||||||
# cortex-m = { version = "0.7", features = ["critical-section-single-core"] }
|
cortex-m = { version = "0.7", features = ["critical-section-single-core"] }
|
||||||
cortex-m-rt = "0.7"
|
cortex-m-rt = "0.7"
|
||||||
# embedded-hal = "1"
|
embedded-hal = "1"
|
||||||
embedded-hal-async = "1"
|
embedded-hal-async = "1"
|
||||||
embedded-io = "0.6"
|
embedded-io = "0.6"
|
||||||
embedded-io-async = "0.6"
|
embedded-io-async = "0.6"
|
||||||
heapless = "0.8"
|
heapless = "0.8"
|
||||||
static_cell = "2"
|
static_cell = "2"
|
||||||
|
|
||||||
defmt = "1"
|
rtt-target = "0.6"
|
||||||
defmt-rtt = "0.4"
|
panic-rtt-target = "0.2"
|
||||||
panic-probe = { version = "0.3", features = ["print-defmt"] }
|
|
||||||
|
|
||||||
critical-section = "1"
|
critical-section = "1"
|
||||||
# portable-atomic = { version = "1", features = ["unsafe-assume-single-core"]}
|
portable-atomic = { version = "1", features = ["unsafe-assume-single-core"]}
|
||||||
|
|
||||||
embassy-sync = "0.6"
|
embassy-sync = "0.6"
|
||||||
embassy-time = "0.4"
|
embassy-time = "0.4"
|
||||||
@ -29,7 +27,7 @@ embassy-executor = { version = "0.7", features = [
|
|||||||
"executor-interrupt"
|
"executor-interrupt"
|
||||||
]}
|
]}
|
||||||
|
|
||||||
va108xx-hal = { version = "0.11", path = "../../va108xx-hal", features = ["defmt"] }
|
va108xx-hal = { version = "0.11", path = "../../va108xx-hal" }
|
||||||
va108xx-embassy = { version = "0.2", path = "../../va108xx-embassy" }
|
va108xx-embassy = { version = "0.2", path = "../../va108xx-embassy" }
|
||||||
|
|
||||||
[features]
|
[features]
|
||||||
@ -37,6 +35,3 @@ default = ["ticks-hz-1_000", "va108xx-embassy/irq-oc30-oc31"]
|
|||||||
custom-irqs = []
|
custom-irqs = []
|
||||||
ticks-hz-1_000 = ["embassy-time/tick-hz-1_000"]
|
ticks-hz-1_000 = ["embassy-time/tick-hz-1_000"]
|
||||||
ticks-hz-32_768 = ["embassy-time/tick-hz-32_768"]
|
ticks-hz-32_768 = ["embassy-time/tick-hz-32_768"]
|
||||||
|
|
||||||
[package.metadata.cargo-machete]
|
|
||||||
ignored = ["cortex-m-rt"]
|
|
||||||
|
@ -4,18 +4,19 @@
|
|||||||
//! and then set the `CHECK_PB22_TO_PB23` to true to also test async operations on Port B.
|
//! and then set the `CHECK_PB22_TO_PB23` to true to also test async operations on Port B.
|
||||||
#![no_std]
|
#![no_std]
|
||||||
#![no_main]
|
#![no_main]
|
||||||
// This imports the logger and the panic handler.
|
|
||||||
use embassy_example as _;
|
|
||||||
|
|
||||||
use embassy_executor::Spawner;
|
use embassy_executor::Spawner;
|
||||||
use embassy_sync::channel::{Receiver, Sender};
|
use embassy_sync::channel::{Receiver, Sender};
|
||||||
use embassy_sync::{blocking_mutex::raw::ThreadModeRawMutex, channel::Channel};
|
use embassy_sync::{blocking_mutex::raw::ThreadModeRawMutex, channel::Channel};
|
||||||
use embassy_time::{Duration, Instant, Timer};
|
use embassy_time::{Duration, Instant, Timer};
|
||||||
use embedded_hal_async::digital::Wait;
|
use embedded_hal_async::digital::Wait;
|
||||||
use va108xx_hal::gpio::asynch::{on_interrupt_for_async_gpio_for_port, InputPinAsync};
|
use panic_rtt_target as _;
|
||||||
use va108xx_hal::gpio::{Input, Output, PinState, Port};
|
use rtt_target::{rprintln, rtt_init_print};
|
||||||
use va108xx_hal::pins::{PinsA, PinsB};
|
use va108xx_hal::gpio::{
|
||||||
|
on_interrupt_for_async_gpio_for_port, InputDynPinAsync, InputPinAsync, PinsB, Port,
|
||||||
|
};
|
||||||
use va108xx_hal::{
|
use va108xx_hal::{
|
||||||
|
gpio::{DynPin, PinsA},
|
||||||
pac::{self, interrupt},
|
pac::{self, interrupt},
|
||||||
prelude::*,
|
prelude::*,
|
||||||
};
|
};
|
||||||
@ -57,50 +58,59 @@ static CHANNEL_PB22_TO_PB23: Channel<ThreadModeRawMutex, GpioCmd, 3> = Channel::
|
|||||||
|
|
||||||
#[embassy_executor::main]
|
#[embassy_executor::main]
|
||||||
async fn main(spawner: Spawner) {
|
async fn main(spawner: Spawner) {
|
||||||
defmt::println!("-- VA108xx Async GPIO Demo --");
|
rtt_init_print!();
|
||||||
|
rprintln!("-- VA108xx Async GPIO Demo --");
|
||||||
|
|
||||||
let dp = pac::Peripherals::take().unwrap();
|
let mut dp = pac::Peripherals::take().unwrap();
|
||||||
|
|
||||||
// Safety: Only called once here.
|
// Safety: Only called once here.
|
||||||
va108xx_embassy::init(dp.tim23, dp.tim22, SYSCLK_FREQ);
|
va108xx_embassy::init(
|
||||||
|
&mut dp.sysconfig,
|
||||||
|
&dp.irqsel,
|
||||||
|
SYSCLK_FREQ,
|
||||||
|
dp.tim23,
|
||||||
|
dp.tim22,
|
||||||
|
);
|
||||||
|
|
||||||
let porta = PinsA::new(dp.porta);
|
let porta = PinsA::new(&mut dp.sysconfig, dp.porta);
|
||||||
let portb = PinsB::new(dp.portb);
|
let portb = PinsB::new(&mut dp.sysconfig, dp.portb);
|
||||||
let mut led0 = Output::new(porta.pa10, PinState::Low);
|
let mut led0 = porta.pa10.into_readable_push_pull_output();
|
||||||
let out_pa0 = Output::new(porta.pa0, PinState::Low);
|
let out_pa0 = porta.pa0.into_readable_push_pull_output();
|
||||||
let in_pa1 = Input::new_floating(porta.pa1);
|
let in_pa1 = porta.pa1.into_floating_input();
|
||||||
let out_pb22 = Output::new(portb.pb22, PinState::Low);
|
let out_pb22 = portb.pb22.into_readable_push_pull_output();
|
||||||
let in_pb23 = Input::new_floating(portb.pb23);
|
let in_pb23 = portb.pb23.into_floating_input();
|
||||||
|
|
||||||
let in_pa1_async = InputPinAsync::new(in_pa1, pac::Interrupt::OC10);
|
let in_pa1_async = InputPinAsync::new(in_pa1, pac::Interrupt::OC10);
|
||||||
let in_pb23_async = InputPinAsync::new(in_pb23, PB22_TO_PB23_IRQ);
|
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
|
spawner
|
||||||
.spawn(output_task(
|
.spawn(output_task(
|
||||||
"PA0 to PA1",
|
"PA0 to PA1",
|
||||||
out_pa0,
|
out_pa0_dyn,
|
||||||
CHANNEL_PA0_PA1.receiver(),
|
CHANNEL_PA0_PA1.receiver(),
|
||||||
))
|
))
|
||||||
.unwrap();
|
.unwrap();
|
||||||
spawner
|
spawner
|
||||||
.spawn(output_task(
|
.spawn(output_task(
|
||||||
"PB22 to PB23",
|
"PB22 to PB23",
|
||||||
out_pb22,
|
out_pb22_dyn,
|
||||||
CHANNEL_PB22_TO_PB23.receiver(),
|
CHANNEL_PB22_TO_PB23.receiver(),
|
||||||
))
|
))
|
||||||
.unwrap();
|
.unwrap();
|
||||||
|
|
||||||
if CHECK_PA0_TO_PA1 {
|
if CHECK_PA0_TO_PA1 {
|
||||||
check_pin_to_pin_async_ops("PA0 to PA1", CHANNEL_PA0_PA1.sender(), in_pa1_async).await;
|
check_pin_to_pin_async_ops("PA0 to PA1", CHANNEL_PA0_PA1.sender(), in_pa1_async).await;
|
||||||
defmt::info!("Example PA0 to PA1 done");
|
rprintln!("Example PA0 to PA1 done");
|
||||||
}
|
}
|
||||||
if CHECK_PB22_TO_PB23 {
|
if CHECK_PB22_TO_PB23 {
|
||||||
check_pin_to_pin_async_ops("PB22 to PB23", CHANNEL_PB22_TO_PB23.sender(), in_pb23_async)
|
check_pin_to_pin_async_ops("PB22 to PB23", CHANNEL_PB22_TO_PB23.sender(), in_pb23_async)
|
||||||
.await;
|
.await;
|
||||||
defmt::info!("Example PB22 to PB23 done");
|
rprintln!("Example PB22 to PB23 done");
|
||||||
}
|
}
|
||||||
|
|
||||||
defmt::info!("Example done, toggling LED0");
|
rprintln!("Example done, toggling LED0");
|
||||||
loop {
|
loop {
|
||||||
led0.toggle();
|
led0.toggle();
|
||||||
Timer::after(Duration::from_millis(500)).await;
|
Timer::after(Duration::from_millis(500)).await;
|
||||||
@ -112,46 +122,46 @@ async fn check_pin_to_pin_async_ops(
|
|||||||
sender: Sender<'static, ThreadModeRawMutex, GpioCmd, 3>,
|
sender: Sender<'static, ThreadModeRawMutex, GpioCmd, 3>,
|
||||||
mut async_input: impl Wait,
|
mut async_input: impl Wait,
|
||||||
) {
|
) {
|
||||||
defmt::info!(
|
rprintln!(
|
||||||
"{}: sending SetHigh command ({} ms)",
|
"{}: sending SetHigh command ({} ms)",
|
||||||
ctx,
|
ctx,
|
||||||
Instant::now().as_millis()
|
Instant::now().as_millis()
|
||||||
);
|
);
|
||||||
sender.send(GpioCmd::new(GpioCmdType::SetHigh, 20)).await;
|
sender.send(GpioCmd::new(GpioCmdType::SetHigh, 20)).await;
|
||||||
async_input.wait_for_high().await.unwrap();
|
async_input.wait_for_high().await.unwrap();
|
||||||
defmt::info!(
|
rprintln!(
|
||||||
"{}: Input pin is high now ({} ms)",
|
"{}: Input pin is high now ({} ms)",
|
||||||
ctx,
|
ctx,
|
||||||
Instant::now().as_millis()
|
Instant::now().as_millis()
|
||||||
);
|
);
|
||||||
|
|
||||||
defmt::info!(
|
rprintln!(
|
||||||
"{}: sending SetLow command ({} ms)",
|
"{}: sending SetLow command ({} ms)",
|
||||||
ctx,
|
ctx,
|
||||||
Instant::now().as_millis()
|
Instant::now().as_millis()
|
||||||
);
|
);
|
||||||
sender.send(GpioCmd::new(GpioCmdType::SetLow, 20)).await;
|
sender.send(GpioCmd::new(GpioCmdType::SetLow, 20)).await;
|
||||||
async_input.wait_for_low().await.unwrap();
|
async_input.wait_for_low().await.unwrap();
|
||||||
defmt::info!(
|
rprintln!(
|
||||||
"{}: Input pin is low now ({} ms)",
|
"{}: Input pin is low now ({} ms)",
|
||||||
ctx,
|
ctx,
|
||||||
Instant::now().as_millis()
|
Instant::now().as_millis()
|
||||||
);
|
);
|
||||||
|
|
||||||
defmt::info!(
|
rprintln!(
|
||||||
"{}: sending RisingEdge command ({} ms)",
|
"{}: sending RisingEdge command ({} ms)",
|
||||||
ctx,
|
ctx,
|
||||||
Instant::now().as_millis()
|
Instant::now().as_millis()
|
||||||
);
|
);
|
||||||
sender.send(GpioCmd::new(GpioCmdType::RisingEdge, 20)).await;
|
sender.send(GpioCmd::new(GpioCmdType::RisingEdge, 20)).await;
|
||||||
async_input.wait_for_rising_edge().await.unwrap();
|
async_input.wait_for_rising_edge().await.unwrap();
|
||||||
defmt::info!(
|
rprintln!(
|
||||||
"{}: input pin had rising edge ({} ms)",
|
"{}: input pin had rising edge ({} ms)",
|
||||||
ctx,
|
ctx,
|
||||||
Instant::now().as_millis()
|
Instant::now().as_millis()
|
||||||
);
|
);
|
||||||
|
|
||||||
defmt::info!(
|
rprintln!(
|
||||||
"{}: sending Falling command ({} ms)",
|
"{}: sending Falling command ({} ms)",
|
||||||
ctx,
|
ctx,
|
||||||
Instant::now().as_millis()
|
Instant::now().as_millis()
|
||||||
@ -160,13 +170,13 @@ async fn check_pin_to_pin_async_ops(
|
|||||||
.send(GpioCmd::new(GpioCmdType::FallingEdge, 20))
|
.send(GpioCmd::new(GpioCmdType::FallingEdge, 20))
|
||||||
.await;
|
.await;
|
||||||
async_input.wait_for_falling_edge().await.unwrap();
|
async_input.wait_for_falling_edge().await.unwrap();
|
||||||
defmt::info!(
|
rprintln!(
|
||||||
"{}: input pin had a falling edge ({} ms)",
|
"{}: input pin had a falling edge ({} ms)",
|
||||||
ctx,
|
ctx,
|
||||||
Instant::now().as_millis()
|
Instant::now().as_millis()
|
||||||
);
|
);
|
||||||
|
|
||||||
defmt::info!(
|
rprintln!(
|
||||||
"{}: sending Falling command ({} ms)",
|
"{}: sending Falling command ({} ms)",
|
||||||
ctx,
|
ctx,
|
||||||
Instant::now().as_millis()
|
Instant::now().as_millis()
|
||||||
@ -175,20 +185,20 @@ async fn check_pin_to_pin_async_ops(
|
|||||||
.send(GpioCmd::new(GpioCmdType::FallingEdge, 20))
|
.send(GpioCmd::new(GpioCmdType::FallingEdge, 20))
|
||||||
.await;
|
.await;
|
||||||
async_input.wait_for_any_edge().await.unwrap();
|
async_input.wait_for_any_edge().await.unwrap();
|
||||||
defmt::info!(
|
rprintln!(
|
||||||
"{}: input pin had a falling (any) edge ({} ms)",
|
"{}: input pin had a falling (any) edge ({} ms)",
|
||||||
ctx,
|
ctx,
|
||||||
Instant::now().as_millis()
|
Instant::now().as_millis()
|
||||||
);
|
);
|
||||||
|
|
||||||
defmt::info!(
|
rprintln!(
|
||||||
"{}: sending Falling command ({} ms)",
|
"{}: sending Falling command ({} ms)",
|
||||||
ctx,
|
ctx,
|
||||||
Instant::now().as_millis()
|
Instant::now().as_millis()
|
||||||
);
|
);
|
||||||
sender.send(GpioCmd::new(GpioCmdType::RisingEdge, 20)).await;
|
sender.send(GpioCmd::new(GpioCmdType::RisingEdge, 20)).await;
|
||||||
async_input.wait_for_any_edge().await.unwrap();
|
async_input.wait_for_any_edge().await.unwrap();
|
||||||
defmt::info!(
|
rprintln!(
|
||||||
"{}: input pin had a rising (any) edge ({} ms)",
|
"{}: input pin had a rising (any) edge ({} ms)",
|
||||||
ctx,
|
ctx,
|
||||||
Instant::now().as_millis()
|
Instant::now().as_millis()
|
||||||
@ -198,7 +208,7 @@ async fn check_pin_to_pin_async_ops(
|
|||||||
#[embassy_executor::task(pool_size = 2)]
|
#[embassy_executor::task(pool_size = 2)]
|
||||||
async fn output_task(
|
async fn output_task(
|
||||||
ctx: &'static str,
|
ctx: &'static str,
|
||||||
mut out: Output,
|
mut out: DynPin,
|
||||||
receiver: Receiver<'static, ThreadModeRawMutex, GpioCmd, 3>,
|
receiver: Receiver<'static, ThreadModeRawMutex, GpioCmd, 3>,
|
||||||
) {
|
) {
|
||||||
loop {
|
loop {
|
||||||
@ -206,26 +216,26 @@ async fn output_task(
|
|||||||
Timer::after(Duration::from_millis(next_cmd.after_delay.into())).await;
|
Timer::after(Duration::from_millis(next_cmd.after_delay.into())).await;
|
||||||
match next_cmd.cmd_type {
|
match next_cmd.cmd_type {
|
||||||
GpioCmdType::SetHigh => {
|
GpioCmdType::SetHigh => {
|
||||||
defmt::info!("{}: Set output high", ctx);
|
rprintln!("{}: Set output high", ctx);
|
||||||
out.set_high();
|
out.set_high().unwrap();
|
||||||
}
|
}
|
||||||
GpioCmdType::SetLow => {
|
GpioCmdType::SetLow => {
|
||||||
defmt::info!("{}: Set output low", ctx);
|
rprintln!("{}: Set output low", ctx);
|
||||||
out.set_low();
|
out.set_low().unwrap();
|
||||||
}
|
}
|
||||||
GpioCmdType::RisingEdge => {
|
GpioCmdType::RisingEdge => {
|
||||||
defmt::info!("{}: Rising edge", ctx);
|
rprintln!("{}: Rising edge", ctx);
|
||||||
if !out.is_set_low() {
|
if !out.is_low().unwrap() {
|
||||||
out.set_low();
|
out.set_low().unwrap();
|
||||||
}
|
}
|
||||||
out.set_high();
|
out.set_high().unwrap();
|
||||||
}
|
}
|
||||||
GpioCmdType::FallingEdge => {
|
GpioCmdType::FallingEdge => {
|
||||||
defmt::info!("{}: Falling edge", ctx);
|
rprintln!("{}: Falling edge", ctx);
|
||||||
if !out.is_set_high() {
|
if !out.is_high().unwrap() {
|
||||||
out.set_high();
|
out.set_high().unwrap();
|
||||||
}
|
}
|
||||||
out.set_low();
|
out.set_low().unwrap();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -13,20 +13,19 @@
|
|||||||
//! RTT logs to see received data.
|
//! RTT logs to see received data.
|
||||||
#![no_std]
|
#![no_std]
|
||||||
#![no_main]
|
#![no_main]
|
||||||
// This imports the logger and the panic handler.
|
|
||||||
use embassy_example as _;
|
|
||||||
|
|
||||||
use core::cell::RefCell;
|
use core::cell::RefCell;
|
||||||
|
|
||||||
use critical_section::Mutex;
|
use critical_section::Mutex;
|
||||||
use embassy_executor::Spawner;
|
use embassy_executor::Spawner;
|
||||||
use embassy_time::Instant;
|
use embassy_time::Instant;
|
||||||
use embedded_io::Write;
|
use embedded_io::Write;
|
||||||
use embedded_io_async::Read;
|
use embedded_io_async::Read;
|
||||||
use heapless::spsc::{Consumer, Producer, Queue};
|
use heapless::spsc::{Consumer, Producer, Queue};
|
||||||
|
use panic_rtt_target as _;
|
||||||
|
use rtt_target::{rprintln, rtt_init_print};
|
||||||
use va108xx_hal::{
|
use va108xx_hal::{
|
||||||
gpio::{Output, PinState},
|
gpio::PinsA,
|
||||||
pac::{self, interrupt},
|
pac::{self, interrupt},
|
||||||
pins::PinsA,
|
|
||||||
prelude::*,
|
prelude::*,
|
||||||
uart::{
|
uart::{
|
||||||
self, on_interrupt_rx_overwriting,
|
self, on_interrupt_rx_overwriting,
|
||||||
@ -50,43 +49,48 @@ static CONSUMER_UART_B: Mutex<RefCell<Option<Consumer<u8, 256>>>> = Mutex::new(R
|
|||||||
// main is itself an async function.
|
// main is itself an async function.
|
||||||
#[embassy_executor::main]
|
#[embassy_executor::main]
|
||||||
async fn main(spawner: Spawner) {
|
async fn main(spawner: Spawner) {
|
||||||
defmt::println!("-- VA108xx Async UART RX Demo --");
|
rtt_init_print!();
|
||||||
|
rprintln!("-- VA108xx Async UART RX Demo --");
|
||||||
|
|
||||||
let dp = pac::Peripherals::take().unwrap();
|
let mut dp = pac::Peripherals::take().unwrap();
|
||||||
|
|
||||||
// Safety: Only called once here.
|
// Safety: Only called once here.
|
||||||
va108xx_embassy::init(dp.tim23, dp.tim22, SYSCLK_FREQ);
|
va108xx_embassy::init(
|
||||||
|
&mut dp.sysconfig,
|
||||||
|
&dp.irqsel,
|
||||||
|
SYSCLK_FREQ,
|
||||||
|
dp.tim23,
|
||||||
|
dp.tim22,
|
||||||
|
);
|
||||||
|
|
||||||
let porta = PinsA::new(dp.porta);
|
let porta = PinsA::new(&mut dp.sysconfig, dp.porta);
|
||||||
let mut led0 = Output::new(porta.pa10, PinState::Low);
|
let mut led0 = porta.pa10.into_readable_push_pull_output();
|
||||||
let mut led1 = Output::new(porta.pa7, PinState::Low);
|
let mut led1 = porta.pa7.into_readable_push_pull_output();
|
||||||
let mut led2 = Output::new(porta.pa6, PinState::Low);
|
let mut led2 = porta.pa6.into_readable_push_pull_output();
|
||||||
|
|
||||||
let tx_uart_a = porta.pa9;
|
let tx_uart_a = porta.pa9.into_funsel_2();
|
||||||
let rx_uart_a = porta.pa8;
|
let rx_uart_a = porta.pa8.into_funsel_2();
|
||||||
|
|
||||||
let uarta = uart::Uart::new_with_interrupt(
|
let uarta = uart::Uart::new_with_interrupt(
|
||||||
dp.uarta,
|
&mut dp.sysconfig,
|
||||||
tx_uart_a,
|
|
||||||
rx_uart_a,
|
|
||||||
50.MHz(),
|
50.MHz(),
|
||||||
115200.Hz().into(),
|
dp.uarta,
|
||||||
|
(tx_uart_a, rx_uart_a),
|
||||||
|
115200.Hz(),
|
||||||
InterruptConfig::new(pac::Interrupt::OC2, true, true),
|
InterruptConfig::new(pac::Interrupt::OC2, true, true),
|
||||||
)
|
);
|
||||||
.unwrap();
|
|
||||||
|
|
||||||
let tx_uart_b = porta.pa3;
|
let tx_uart_b = porta.pa3.into_funsel_2();
|
||||||
let rx_uart_b = porta.pa2;
|
let rx_uart_b = porta.pa2.into_funsel_2();
|
||||||
|
|
||||||
let uartb = uart::Uart::new_with_interrupt(
|
let uartb = uart::Uart::new_with_interrupt(
|
||||||
dp.uartb,
|
&mut dp.sysconfig,
|
||||||
tx_uart_b,
|
|
||||||
rx_uart_b,
|
|
||||||
50.MHz(),
|
50.MHz(),
|
||||||
115200.Hz().into(),
|
dp.uartb,
|
||||||
|
(tx_uart_b, rx_uart_b),
|
||||||
|
115200.Hz(),
|
||||||
InterruptConfig::new(pac::Interrupt::OC3, true, true),
|
InterruptConfig::new(pac::Interrupt::OC3, true, true),
|
||||||
)
|
);
|
||||||
.unwrap();
|
|
||||||
let (mut tx_uart_a, rx_uart_a) = uarta.split();
|
let (mut tx_uart_a, rx_uart_a) = uarta.split();
|
||||||
let (tx_uart_b, rx_uart_b) = uartb.split();
|
let (tx_uart_b, rx_uart_b) = uartb.split();
|
||||||
let (prod_uart_a, cons_uart_a) = QUEUE_UART_A.take().split();
|
let (prod_uart_a, cons_uart_a) = QUEUE_UART_A.take().split();
|
||||||
@ -104,13 +108,13 @@ async fn main(spawner: Spawner) {
|
|||||||
.unwrap();
|
.unwrap();
|
||||||
let mut buf = [0u8; 256];
|
let mut buf = [0u8; 256];
|
||||||
loop {
|
loop {
|
||||||
defmt::info!("Current time UART A: {}", Instant::now().as_secs());
|
rprintln!("Current time UART A: {}", Instant::now().as_secs());
|
||||||
led0.toggle();
|
led0.toggle();
|
||||||
led1.toggle();
|
led1.toggle();
|
||||||
led2.toggle();
|
led2.toggle();
|
||||||
let read_bytes = async_rx_uart_a.read(&mut buf).await.unwrap();
|
let read_bytes = async_rx_uart_a.read(&mut buf).await.unwrap();
|
||||||
let read_str = core::str::from_utf8(&buf[..read_bytes]).unwrap();
|
let read_str = core::str::from_utf8(&buf[..read_bytes]).unwrap();
|
||||||
defmt::info!(
|
rprintln!(
|
||||||
"Read {} bytes asynchronously on UART A: {:?}",
|
"Read {} bytes asynchronously on UART A: {:?}",
|
||||||
read_bytes,
|
read_bytes,
|
||||||
read_str
|
read_str
|
||||||
@ -120,14 +124,14 @@ async fn main(spawner: Spawner) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
#[embassy_executor::task]
|
#[embassy_executor::task]
|
||||||
async fn uart_b_task(mut async_rx: RxAsyncOverwriting<256>, mut tx: Tx) {
|
async fn uart_b_task(mut async_rx: RxAsyncOverwriting<pac::Uartb, 256>, mut tx: Tx<pac::Uartb>) {
|
||||||
let mut buf = [0u8; 256];
|
let mut buf = [0u8; 256];
|
||||||
loop {
|
loop {
|
||||||
defmt::info!("Current time UART B: {}", Instant::now().as_secs());
|
rprintln!("Current time UART B: {}", Instant::now().as_secs());
|
||||||
// Infallible asynchronous operation.
|
// Infallible asynchronous operation.
|
||||||
let read_bytes = async_rx.read(&mut buf).await.unwrap();
|
let read_bytes = async_rx.read(&mut buf).await.unwrap();
|
||||||
let read_str = core::str::from_utf8(&buf[..read_bytes]).unwrap();
|
let read_str = core::str::from_utf8(&buf[..read_bytes]).unwrap();
|
||||||
defmt::info!(
|
rprintln!(
|
||||||
"Read {} bytes asynchronously on UART B: {:?}",
|
"Read {} bytes asynchronously on UART B: {:?}",
|
||||||
read_bytes,
|
read_bytes,
|
||||||
read_str
|
read_str
|
||||||
@ -141,11 +145,11 @@ async fn uart_b_task(mut async_rx: RxAsyncOverwriting<256>, mut tx: Tx) {
|
|||||||
fn OC2() {
|
fn OC2() {
|
||||||
let mut prod =
|
let mut prod =
|
||||||
critical_section::with(|cs| PRODUCER_UART_A.borrow(cs).borrow_mut().take().unwrap());
|
critical_section::with(|cs| PRODUCER_UART_A.borrow(cs).borrow_mut().take().unwrap());
|
||||||
let errors = on_interrupt_rx(Bank::Uart0, &mut prod);
|
let errors = on_interrupt_rx(Bank::A, &mut prod);
|
||||||
critical_section::with(|cs| *PRODUCER_UART_A.borrow(cs).borrow_mut() = Some(prod));
|
critical_section::with(|cs| *PRODUCER_UART_A.borrow(cs).borrow_mut() = Some(prod));
|
||||||
// In a production app, we could use a channel to send the errors to the main task.
|
// In a production app, we could use a channel to send the errors to the main task.
|
||||||
if let Err(errors) = errors {
|
if let Err(errors) = errors {
|
||||||
defmt::info!("UART A errors: {:?}", errors);
|
rprintln!("UART A errors: {:?}", errors);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -154,10 +158,10 @@ fn OC2() {
|
|||||||
fn OC3() {
|
fn OC3() {
|
||||||
let mut prod =
|
let mut prod =
|
||||||
critical_section::with(|cs| PRODUCER_UART_B.borrow(cs).borrow_mut().take().unwrap());
|
critical_section::with(|cs| PRODUCER_UART_B.borrow(cs).borrow_mut().take().unwrap());
|
||||||
let errors = on_interrupt_rx_overwriting(Bank::Uart1, &mut prod, &CONSUMER_UART_B);
|
let errors = on_interrupt_rx_overwriting(Bank::B, &mut prod, &CONSUMER_UART_B);
|
||||||
critical_section::with(|cs| *PRODUCER_UART_B.borrow(cs).borrow_mut() = Some(prod));
|
critical_section::with(|cs| *PRODUCER_UART_B.borrow(cs).borrow_mut() = Some(prod));
|
||||||
// In a production app, we could use a channel to send the errors to the main task.
|
// In a production app, we could use a channel to send the errors to the main task.
|
||||||
if let Err(errors) = errors {
|
if let Err(errors) = errors {
|
||||||
defmt::info!("UART B errors: {:?}", errors);
|
rprintln!("UART B errors: {:?}", errors);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -10,16 +10,14 @@
|
|||||||
//! can verify the correctness of the sent strings.
|
//! can verify the correctness of the sent strings.
|
||||||
#![no_std]
|
#![no_std]
|
||||||
#![no_main]
|
#![no_main]
|
||||||
// This imports the logger and the panic handler.
|
|
||||||
use embassy_example as _;
|
|
||||||
|
|
||||||
use embassy_executor::Spawner;
|
use embassy_executor::Spawner;
|
||||||
use embassy_time::{Duration, Instant, Ticker};
|
use embassy_time::{Duration, Instant, Ticker};
|
||||||
use embedded_io_async::Write;
|
use embedded_io_async::Write;
|
||||||
|
use panic_rtt_target as _;
|
||||||
|
use rtt_target::{rprintln, rtt_init_print};
|
||||||
use va108xx_hal::{
|
use va108xx_hal::{
|
||||||
gpio::{Output, PinState},
|
gpio::PinsA,
|
||||||
pac::{self, interrupt},
|
pac::{self, interrupt},
|
||||||
pins::PinsA,
|
|
||||||
prelude::*,
|
prelude::*,
|
||||||
uart::{self, on_interrupt_tx, Bank, TxAsync},
|
uart::{self, on_interrupt_tx, Bank, TxAsync},
|
||||||
InterruptConfig,
|
InterruptConfig,
|
||||||
@ -37,37 +35,42 @@ const STR_LIST: &[&str] = &[
|
|||||||
// main is itself an async function.
|
// main is itself an async function.
|
||||||
#[embassy_executor::main]
|
#[embassy_executor::main]
|
||||||
async fn main(_spawner: Spawner) {
|
async fn main(_spawner: Spawner) {
|
||||||
defmt::println!("-- VA108xx Async UART TX Demo --");
|
rtt_init_print!();
|
||||||
|
rprintln!("-- VA108xx Async UART TX Demo --");
|
||||||
|
|
||||||
let dp = pac::Peripherals::take().unwrap();
|
let mut dp = pac::Peripherals::take().unwrap();
|
||||||
|
|
||||||
// Safety: Only called once here.
|
// Safety: Only called once here.
|
||||||
va108xx_embassy::init(dp.tim23, dp.tim22, SYSCLK_FREQ);
|
va108xx_embassy::init(
|
||||||
|
&mut dp.sysconfig,
|
||||||
|
&dp.irqsel,
|
||||||
|
SYSCLK_FREQ,
|
||||||
|
dp.tim23,
|
||||||
|
dp.tim22,
|
||||||
|
);
|
||||||
|
|
||||||
let porta = PinsA::new(dp.porta);
|
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 led0 = Output::new(porta.pa10, PinState::Low);
|
let tx = porta.pa9.into_funsel_2();
|
||||||
let mut led1 = Output::new(porta.pa7, PinState::Low);
|
let rx = porta.pa8.into_funsel_2();
|
||||||
let mut led2 = Output::new(porta.pa6, PinState::Low);
|
|
||||||
|
|
||||||
let tx = porta.pa9;
|
|
||||||
let rx = porta.pa8;
|
|
||||||
|
|
||||||
let uarta = uart::Uart::new_with_interrupt(
|
let uarta = uart::Uart::new_with_interrupt(
|
||||||
dp.uarta,
|
&mut dp.sysconfig,
|
||||||
tx,
|
|
||||||
rx,
|
|
||||||
50.MHz(),
|
50.MHz(),
|
||||||
115200.Hz().into(),
|
dp.uarta,
|
||||||
|
(tx, rx),
|
||||||
|
115200.Hz(),
|
||||||
InterruptConfig::new(pac::Interrupt::OC2, true, true),
|
InterruptConfig::new(pac::Interrupt::OC2, true, true),
|
||||||
)
|
);
|
||||||
.unwrap();
|
|
||||||
let (tx, _rx) = uarta.split();
|
let (tx, _rx) = uarta.split();
|
||||||
let mut async_tx = TxAsync::new(tx);
|
let mut async_tx = TxAsync::new(tx);
|
||||||
let mut ticker = Ticker::every(Duration::from_secs(1));
|
let mut ticker = Ticker::every(Duration::from_secs(1));
|
||||||
let mut idx = 0;
|
let mut idx = 0;
|
||||||
loop {
|
loop {
|
||||||
defmt::info!("Current time: {}", Instant::now().as_secs());
|
rprintln!("Current time: {}", Instant::now().as_secs());
|
||||||
led0.toggle();
|
led0.toggle();
|
||||||
led1.toggle();
|
led1.toggle();
|
||||||
led2.toggle();
|
led2.toggle();
|
||||||
@ -86,5 +89,5 @@ async fn main(_spawner: Spawner) {
|
|||||||
#[interrupt]
|
#[interrupt]
|
||||||
#[allow(non_snake_case)]
|
#[allow(non_snake_case)]
|
||||||
fn OC2() {
|
fn OC2() {
|
||||||
on_interrupt_tx(Bank::Uart0);
|
on_interrupt_tx(Bank::A);
|
||||||
}
|
}
|
||||||
|
@ -1,3 +0,0 @@
|
|||||||
#![no_std]
|
|
||||||
use defmt_rtt as _;
|
|
||||||
use panic_probe as _;
|
|
@ -1,8 +1,9 @@
|
|||||||
#![no_std]
|
#![no_std]
|
||||||
#![no_main]
|
#![no_main]
|
||||||
use embassy_example as _;
|
|
||||||
use embassy_executor::Spawner;
|
use embassy_executor::Spawner;
|
||||||
use embassy_time::{Duration, Instant, Ticker};
|
use embassy_time::{Duration, Instant, Ticker};
|
||||||
|
use panic_rtt_target as _;
|
||||||
|
use rtt_target::{rprintln, rtt_init_print};
|
||||||
|
|
||||||
cfg_if::cfg_if! {
|
cfg_if::cfg_if! {
|
||||||
if #[cfg(feature = "custom-irqs")] {
|
if #[cfg(feature = "custom-irqs")] {
|
||||||
@ -12,49 +13,49 @@ cfg_if::cfg_if! {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
use va108xx_hal::{
|
use va108xx_hal::{gpio::PinsA, pac, prelude::*};
|
||||||
gpio::{Output, PinState},
|
|
||||||
pac,
|
|
||||||
pins::PinsA,
|
|
||||||
prelude::*,
|
|
||||||
};
|
|
||||||
|
|
||||||
const SYSCLK_FREQ: Hertz = Hertz::from_raw(50_000_000);
|
const SYSCLK_FREQ: Hertz = Hertz::from_raw(50_000_000);
|
||||||
|
|
||||||
// main is itself an async function.
|
// main is itself an async function.
|
||||||
#[embassy_executor::main]
|
#[embassy_executor::main]
|
||||||
async fn main(_spawner: Spawner) {
|
async fn main(_spawner: Spawner) {
|
||||||
defmt::println!("-- VA108xx Embassy Demo --");
|
rtt_init_print!();
|
||||||
|
rprintln!("-- VA108xx Embassy Demo --");
|
||||||
|
|
||||||
let dp = pac::Peripherals::take().unwrap();
|
let mut dp = pac::Peripherals::take().unwrap();
|
||||||
|
|
||||||
// Safety: Only called once here.
|
// Safety: Only called once here.
|
||||||
cfg_if::cfg_if! {
|
cfg_if::cfg_if! {
|
||||||
if #[cfg(not(feature = "custom-irqs"))] {
|
if #[cfg(not(feature = "custom-irqs"))] {
|
||||||
va108xx_embassy::init(
|
va108xx_embassy::init(
|
||||||
|
&mut dp.sysconfig,
|
||||||
|
&dp.irqsel,
|
||||||
|
SYSCLK_FREQ,
|
||||||
dp.tim23,
|
dp.tim23,
|
||||||
dp.tim22,
|
dp.tim22,
|
||||||
SYSCLK_FREQ,
|
|
||||||
);
|
);
|
||||||
} else {
|
} else {
|
||||||
va108xx_embassy::init_with_custom_irqs(
|
va108xx_embassy::init_with_custom_irqs(
|
||||||
|
&mut dp.sysconfig,
|
||||||
|
&dp.irqsel,
|
||||||
|
SYSCLK_FREQ,
|
||||||
dp.tim23,
|
dp.tim23,
|
||||||
dp.tim22,
|
dp.tim22,
|
||||||
SYSCLK_FREQ,
|
|
||||||
pac::Interrupt::OC23,
|
pac::Interrupt::OC23,
|
||||||
pac::Interrupt::OC24,
|
pac::Interrupt::OC24,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
let porta = PinsA::new(dp.porta);
|
let porta = PinsA::new(&mut dp.sysconfig, dp.porta);
|
||||||
let mut led0 = Output::new(porta.pa10, PinState::Low);
|
let mut led0 = porta.pa10.into_readable_push_pull_output();
|
||||||
let mut led1 = Output::new(porta.pa7, PinState::Low);
|
let mut led1 = porta.pa7.into_readable_push_pull_output();
|
||||||
let mut led2 = Output::new(porta.pa6, PinState::Low);
|
let mut led2 = porta.pa6.into_readable_push_pull_output();
|
||||||
let mut ticker = Ticker::every(Duration::from_secs(1));
|
let mut ticker = Ticker::every(Duration::from_secs(1));
|
||||||
loop {
|
loop {
|
||||||
ticker.next().await;
|
ticker.next().await;
|
||||||
defmt::info!("Current time: {}", Instant::now().as_secs());
|
rprintln!("Current time: {}", Instant::now().as_secs());
|
||||||
led0.toggle();
|
led0.toggle();
|
||||||
led1.toggle();
|
led1.toggle();
|
||||||
led2.toggle();
|
led2.toggle();
|
||||||
|
@ -5,12 +5,21 @@ edition = "2021"
|
|||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
cortex-m = { version = "0.7", features = ["critical-section-single-core"] }
|
cortex-m = { version = "0.7", features = ["critical-section-single-core"] }
|
||||||
|
cortex-m-rt = "0.7"
|
||||||
|
embedded-hal = "1"
|
||||||
embedded-io = "0.6"
|
embedded-io = "0.6"
|
||||||
defmt-rtt = "0.4"
|
rtt-target = "0.6"
|
||||||
defmt = "1"
|
panic-rtt-target = "0.2"
|
||||||
panic-probe = { version = "1", features = ["defmt"] }
|
|
||||||
|
# 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 = { version = "2", features = ["thumbv6-backend"] }
|
||||||
rtic-monotonics = { version = "2", features = ["cortex-m-systick"] }
|
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"] }
|
ringbuf = { version = "0.4.7", default-features = false, features = ["portable-atomic"] }
|
||||||
|
|
||||||
va108xx-hal = { version = "0.11", path = "../../va108xx-hal" }
|
va108xx-hal = { version = "0.11", path = "../../va108xx-hal" }
|
||||||
|
@ -4,29 +4,34 @@
|
|||||||
|
|
||||||
#[rtic::app(device = pac)]
|
#[rtic::app(device = pac)]
|
||||||
mod app {
|
mod app {
|
||||||
|
use panic_rtt_target as _;
|
||||||
use rtic_example::SYSCLK_FREQ;
|
use rtic_example::SYSCLK_FREQ;
|
||||||
// Import panic provider.
|
use rtt_target::{rprintln, rtt_init_default, set_print_channel};
|
||||||
use panic_probe as _;
|
|
||||||
// Import global logger.
|
|
||||||
use defmt_rtt as _;
|
|
||||||
use va108xx_hal::{
|
use va108xx_hal::{
|
||||||
clock::{set_clk_div_register, FilterClkSel},
|
clock::{set_clk_div_register, FilterClkSel},
|
||||||
gpio::{FilterType, InterruptEdge},
|
gpio::{FilterType, InterruptEdge, PinsA},
|
||||||
pac,
|
pac,
|
||||||
pins::PinsA,
|
prelude::*,
|
||||||
timer::InterruptConfig,
|
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);
|
rtic_monotonics::systick_monotonic!(Mono, 1_000);
|
||||||
|
|
||||||
#[derive(Debug, PartialEq, defmt::Format)]
|
#[derive(Debug, PartialEq)]
|
||||||
pub enum PressMode {
|
pub enum PressMode {
|
||||||
Toggle,
|
Toggle,
|
||||||
Keep,
|
Keep,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, PartialEq)]
|
||||||
|
pub enum CfgMode {
|
||||||
|
Prompt,
|
||||||
|
Fixed,
|
||||||
|
}
|
||||||
|
|
||||||
|
const CFG_MODE: CfgMode = CfgMode::Fixed;
|
||||||
// You can change the press mode here
|
// You can change the press mode here
|
||||||
const DEFAULT_MODE: PressMode = PressMode::Toggle;
|
const DEFAULT_MODE: PressMode = PressMode::Toggle;
|
||||||
|
|
||||||
@ -42,35 +47,53 @@ mod app {
|
|||||||
|
|
||||||
#[init]
|
#[init]
|
||||||
fn init(cx: init::Context) -> (Shared, Local) {
|
fn init(cx: init::Context) -> (Shared, Local) {
|
||||||
defmt::println!("-- Vorago Button IRQ Example --");
|
let channels = rtt_init_default!();
|
||||||
|
set_print_channel(channels.up.0);
|
||||||
|
rprintln!("-- Vorago Button IRQ Example --");
|
||||||
Mono::start(cx.core.SYST, SYSCLK_FREQ.raw());
|
Mono::start(cx.core.SYST, SYSCLK_FREQ.raw());
|
||||||
|
|
||||||
let mode = DEFAULT_MODE;
|
let mode = match CFG_MODE {
|
||||||
defmt::info!("Using {:?} mode", mode);
|
// Ask mode from user via RTT
|
||||||
|
CfgMode::Prompt => prompt_mode(channels.down.0),
|
||||||
|
// Use mode hardcoded in `DEFAULT_MODE`
|
||||||
|
CfgMode::Fixed => DEFAULT_MODE,
|
||||||
|
};
|
||||||
|
rprintln!("Using {:?} mode", mode);
|
||||||
|
|
||||||
let mut dp = cx.device;
|
let mut dp = cx.device;
|
||||||
let pinsa = PinsA::new(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);
|
let mut button = Button::new(pinsa.pa11.into_floating_input());
|
||||||
|
|
||||||
if mode == PressMode::Toggle {
|
if mode == PressMode::Toggle {
|
||||||
// This filter debounces the switch for edge based interrupts
|
// This filter debounces the switch for edge based interrupts
|
||||||
button.configure_filter_type(FilterType::FilterFourCycles, FilterClkSel::Clk1);
|
button.configure_filter_type(FilterType::FilterFourClockCycles, FilterClkSel::Clk1);
|
||||||
set_clk_div_register(&mut dp.sysconfig, FilterClkSel::Clk1, 50_000);
|
set_clk_div_register(&mut dp.sysconfig, FilterClkSel::Clk1, 50_000);
|
||||||
}
|
}
|
||||||
button.configure_and_enable_edge_interrupt(
|
button.configure_and_enable_edge_interrupt(
|
||||||
edge_irq,
|
edge_irq,
|
||||||
InterruptConfig::new(pac::interrupt::OC15, true, true),
|
InterruptConfig::new(pac::interrupt::OC15, true, true),
|
||||||
);
|
);
|
||||||
let mut leds = Leds::new(pinsa.pa10, pinsa.pa7, pinsa.pa6);
|
let mut leds = Leds::new(
|
||||||
|
pinsa.pa10.into_push_pull_output(),
|
||||||
|
pinsa.pa7.into_push_pull_output(),
|
||||||
|
pinsa.pa6.into_push_pull_output(),
|
||||||
|
);
|
||||||
for led in leds.iter_mut() {
|
for led in leds.iter_mut() {
|
||||||
led.off();
|
led.off();
|
||||||
}
|
}
|
||||||
|
set_up_ms_tick(
|
||||||
|
InterruptConfig::new(pac::Interrupt::OC0, true, true),
|
||||||
|
&mut dp.sysconfig,
|
||||||
|
Some(&mut dp.irqsel),
|
||||||
|
50.MHz(),
|
||||||
|
dp.tim0,
|
||||||
|
);
|
||||||
(Shared {}, Local { leds, button, mode })
|
(Shared {}, Local { leds, button, mode })
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -95,4 +118,26 @@ mod app {
|
|||||||
leds[0].on();
|
leds[0].on();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[task(binds = OC0)]
|
||||||
|
fn ms_tick(_cx: ms_tick::Context) {
|
||||||
|
default_ms_irq_handler();
|
||||||
|
}
|
||||||
|
|
||||||
|
fn prompt_mode(mut down_channel: rtt_target::DownChannel) -> PressMode {
|
||||||
|
rprintln!("Using prompt mode");
|
||||||
|
rprintln!("Please enter the mode [0: Toggle, 1: Keep]");
|
||||||
|
let mut read_buf: [u8; 16] = [0; 16];
|
||||||
|
let mut read;
|
||||||
|
loop {
|
||||||
|
read = down_channel.read(&mut read_buf);
|
||||||
|
for &byte in &read_buf[..read] {
|
||||||
|
match byte as char {
|
||||||
|
'0' => return PressMode::Toggle,
|
||||||
|
'1' => return PressMode::Keep,
|
||||||
|
_ => continue, // Ignore other characters
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -4,10 +4,8 @@
|
|||||||
|
|
||||||
#[rtic::app(device = pac)]
|
#[rtic::app(device = pac)]
|
||||||
mod app {
|
mod app {
|
||||||
// Import panic provider.
|
use panic_rtt_target as _;
|
||||||
use panic_probe as _;
|
use rtt_target::{rprintln, rtt_init_default};
|
||||||
// Import global logger.
|
|
||||||
use defmt_rtt as _;
|
|
||||||
use va108xx_hal::pac;
|
use va108xx_hal::pac;
|
||||||
|
|
||||||
#[local]
|
#[local]
|
||||||
@ -18,7 +16,8 @@ mod app {
|
|||||||
|
|
||||||
#[init]
|
#[init]
|
||||||
fn init(_ctx: init::Context) -> (Shared, Local) {
|
fn init(_ctx: init::Context) -> (Shared, Local) {
|
||||||
defmt::println!("-- Vorago RTIC template --");
|
rtt_init_default!();
|
||||||
|
rprintln!("-- Vorago RTIC template --");
|
||||||
(Shared {}, Local {})
|
(Shared {}, Local {})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
//! More complex UART application on UART PA8 (TX) and PA9 (RX).
|
//! More complex UART application
|
||||||
//!
|
//!
|
||||||
//! Uses the IRQ capabilities of the VA10820 peripheral and the RTIC framework to poll the UART in
|
//! 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.
|
//! a non-blocking way. All received data will be sent back to the sender.
|
||||||
@ -14,16 +14,14 @@ const RX_RING_BUF_SIZE: usize = 1024;
|
|||||||
mod app {
|
mod app {
|
||||||
use super::*;
|
use super::*;
|
||||||
use embedded_io::Write;
|
use embedded_io::Write;
|
||||||
|
use panic_rtt_target as _;
|
||||||
use ringbuf::traits::{Consumer, Observer, Producer};
|
use ringbuf::traits::{Consumer, Observer, Producer};
|
||||||
use rtic_example::SYSCLK_FREQ;
|
use rtic_example::SYSCLK_FREQ;
|
||||||
// Import panic provider.
|
|
||||||
use panic_probe as _;
|
|
||||||
// Import global logger.
|
|
||||||
use defmt_rtt as _;
|
|
||||||
use rtic_monotonics::Monotonic;
|
use rtic_monotonics::Monotonic;
|
||||||
|
use rtt_target::{rprintln, rtt_init_print};
|
||||||
use va108xx_hal::{
|
use va108xx_hal::{
|
||||||
|
gpio::PinsA,
|
||||||
pac,
|
pac,
|
||||||
pins::PinsA,
|
|
||||||
prelude::*,
|
prelude::*,
|
||||||
uart::{self, RxWithInterrupt, Tx},
|
uart::{self, RxWithInterrupt, Tx},
|
||||||
InterruptConfig,
|
InterruptConfig,
|
||||||
@ -31,8 +29,8 @@ mod app {
|
|||||||
|
|
||||||
#[local]
|
#[local]
|
||||||
struct Local {
|
struct Local {
|
||||||
rx: RxWithInterrupt,
|
rx: RxWithInterrupt<pac::Uarta>,
|
||||||
tx: Tx,
|
tx: Tx<pac::Uarta>,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[shared]
|
#[shared]
|
||||||
@ -44,24 +42,24 @@ mod app {
|
|||||||
|
|
||||||
#[init]
|
#[init]
|
||||||
fn init(cx: init::Context) -> (Shared, Local) {
|
fn init(cx: init::Context) -> (Shared, Local) {
|
||||||
defmt::println!("-- VA108xx UART Echo with IRQ example application--");
|
rtt_init_print!();
|
||||||
|
rprintln!("-- VA108xx UART Echo with IRQ example application--");
|
||||||
|
|
||||||
Mono::start(cx.core.SYST, SYSCLK_FREQ.raw());
|
Mono::start(cx.core.SYST, SYSCLK_FREQ.raw());
|
||||||
|
|
||||||
let dp = cx.device;
|
let mut dp = cx.device;
|
||||||
let gpioa = PinsA::new(dp.porta);
|
let gpioa = PinsA::new(&mut dp.sysconfig, dp.porta);
|
||||||
let tx = gpioa.pa9;
|
let tx = gpioa.pa9.into_funsel_2();
|
||||||
let rx = gpioa.pa8;
|
let rx = gpioa.pa8.into_funsel_2();
|
||||||
|
|
||||||
let irq_uart = uart::Uart::new_with_interrupt(
|
let irq_uart = uart::Uart::new_with_interrupt(
|
||||||
dp.uarta,
|
&mut dp.sysconfig,
|
||||||
tx,
|
|
||||||
rx,
|
|
||||||
SYSCLK_FREQ,
|
SYSCLK_FREQ,
|
||||||
115200.Hz().into(),
|
dp.uarta,
|
||||||
|
(tx, rx),
|
||||||
|
115200.Hz(),
|
||||||
InterruptConfig::new(pac::Interrupt::OC3, true, true),
|
InterruptConfig::new(pac::Interrupt::OC3, true, true),
|
||||||
)
|
);
|
||||||
.unwrap();
|
|
||||||
let (tx, rx) = irq_uart.split();
|
let (tx, rx) = irq_uart.split();
|
||||||
let mut rx = rx.into_rx_with_irq();
|
let mut rx = rx.into_rx_with_irq();
|
||||||
|
|
||||||
@ -106,7 +104,7 @@ mod app {
|
|||||||
}
|
}
|
||||||
if ringbuf_full {
|
if ringbuf_full {
|
||||||
// Could also drop oldest data, but that would require the consumer to be shared.
|
// Could also drop oldest data, but that would require the consumer to be shared.
|
||||||
defmt::println!("buffer full, data was dropped");
|
rprintln!("buffer full, data was dropped");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -5,24 +5,21 @@
|
|||||||
#[rtic::app(device = pac, dispatchers = [OC31, OC30, OC29])]
|
#[rtic::app(device = pac, dispatchers = [OC31, OC30, OC29])]
|
||||||
mod app {
|
mod app {
|
||||||
use cortex_m::asm;
|
use cortex_m::asm;
|
||||||
|
use panic_rtt_target as _;
|
||||||
use rtic_example::SYSCLK_FREQ;
|
use rtic_example::SYSCLK_FREQ;
|
||||||
use rtic_monotonics::systick::prelude::*;
|
use rtic_monotonics::systick::prelude::*;
|
||||||
use rtic_monotonics::Monotonic;
|
use rtic_monotonics::Monotonic;
|
||||||
// Import panic provider.
|
use rtt_target::{rprintln, rtt_init_print};
|
||||||
use panic_probe as _;
|
|
||||||
// Import global logger.
|
|
||||||
use defmt_rtt as _;
|
|
||||||
use va108xx_hal::{
|
use va108xx_hal::{
|
||||||
gpio::{Output, PinState},
|
gpio::{OutputReadablePushPull, Pin, PinsA, PA10, PA6, PA7},
|
||||||
pac,
|
pac,
|
||||||
pins::PinsA,
|
|
||||||
};
|
};
|
||||||
|
|
||||||
#[local]
|
#[local]
|
||||||
struct Local {
|
struct Local {
|
||||||
led0: Output,
|
led0: Pin<PA10, OutputReadablePushPull>,
|
||||||
led1: Output,
|
led1: Pin<PA7, OutputReadablePushPull>,
|
||||||
led2: Output,
|
led2: Pin<PA6, OutputReadablePushPull>,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[shared]
|
#[shared]
|
||||||
@ -31,15 +28,16 @@ mod app {
|
|||||||
rtic_monotonics::systick_monotonic!(Mono, 1_000);
|
rtic_monotonics::systick_monotonic!(Mono, 1_000);
|
||||||
|
|
||||||
#[init]
|
#[init]
|
||||||
fn init(cx: init::Context) -> (Shared, Local) {
|
fn init(mut cx: init::Context) -> (Shared, Local) {
|
||||||
defmt::println!("-- Vorago VA108xx RTIC template --");
|
rtt_init_print!();
|
||||||
|
rprintln!("-- Vorago VA108xx RTIC template --");
|
||||||
|
|
||||||
Mono::start(cx.core.SYST, SYSCLK_FREQ.raw());
|
Mono::start(cx.core.SYST, SYSCLK_FREQ.raw());
|
||||||
|
|
||||||
let porta = PinsA::new(cx.device.porta);
|
let porta = PinsA::new(&mut cx.device.sysconfig, cx.device.porta);
|
||||||
let led0 = Output::new(porta.pa10, PinState::Low);
|
let led0 = porta.pa10.into_readable_push_pull_output();
|
||||||
let led1 = Output::new(porta.pa7, PinState::Low);
|
let led1 = porta.pa7.into_readable_push_pull_output();
|
||||||
let led2 = Output::new(porta.pa6, PinState::Low);
|
let led2 = porta.pa6.into_readable_push_pull_output();
|
||||||
blinky::spawn().ok();
|
blinky::spawn().ok();
|
||||||
(Shared {}, Local { led0, led1, led2 })
|
(Shared {}, Local { led0, led1, led2 })
|
||||||
}
|
}
|
||||||
@ -58,7 +56,7 @@ mod app {
|
|||||||
)]
|
)]
|
||||||
async fn blinky(cx: blinky::Context) {
|
async fn blinky(cx: blinky::Context) {
|
||||||
loop {
|
loop {
|
||||||
defmt::println!("toggling LEDs");
|
rprintln!("toggling LEDs");
|
||||||
cx.local.led0.toggle();
|
cx.local.led0.toggle();
|
||||||
cx.local.led1.toggle();
|
cx.local.led1.toggle();
|
||||||
cx.local.led2.toggle();
|
cx.local.led2.toggle();
|
||||||
|
@ -7,15 +7,19 @@ edition = "2021"
|
|||||||
cortex-m = {version = "0.7", features = ["critical-section-single-core"]}
|
cortex-m = {version = "0.7", features = ["critical-section-single-core"]}
|
||||||
cortex-m-rt = "0.7"
|
cortex-m-rt = "0.7"
|
||||||
panic-halt = "1"
|
panic-halt = "1"
|
||||||
defmt-rtt = "0.4"
|
panic-rtt-target = "0.2"
|
||||||
defmt = "1"
|
critical-section = "1"
|
||||||
panic-probe = { version = "1", features = ["defmt"] }
|
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"
|
||||||
portable-atomic = { version = "1", features = ["unsafe-assume-single-core"] }
|
cortex-m-semihosting = "0.5.0"
|
||||||
|
|
||||||
[dependencies.va108xx-hal]
|
[dependencies.va108xx-hal]
|
||||||
version = "0.11"
|
version = "0.11"
|
||||||
path = "../../va108xx-hal"
|
path = "../../va108xx-hal"
|
||||||
features = ["defmt"]
|
features = ["rt", "defmt"]
|
||||||
|
|
||||||
|
[dependencies.vorago-reb1]
|
||||||
|
version = "0.8"
|
||||||
|
path = "../../vorago-reb1"
|
||||||
|
@ -10,37 +10,52 @@ use cortex_m_rt::entry;
|
|||||||
use embedded_hal::delay::DelayNs;
|
use embedded_hal::delay::DelayNs;
|
||||||
use panic_halt as _;
|
use panic_halt as _;
|
||||||
use va108xx_hal::{
|
use va108xx_hal::{
|
||||||
gpio::{Output, PinState},
|
gpio::PinsA,
|
||||||
pac::{self},
|
pac::{self, interrupt},
|
||||||
pins::PinsA,
|
|
||||||
prelude::*,
|
prelude::*,
|
||||||
timer::CountdownTimer,
|
timer::DelayMs,
|
||||||
|
timer::{default_ms_irq_handler, set_up_ms_tick, CountdownTimer},
|
||||||
|
InterruptConfig,
|
||||||
};
|
};
|
||||||
|
|
||||||
#[entry]
|
#[entry]
|
||||||
fn main() -> ! {
|
fn main() -> ! {
|
||||||
let dp = pac::Peripherals::take().unwrap();
|
let mut dp = pac::Peripherals::take().unwrap();
|
||||||
let mut delay = CountdownTimer::new(dp.tim1, 50.MHz());
|
let mut delay_ms = DelayMs::new(set_up_ms_tick(
|
||||||
let porta = PinsA::new(dp.porta);
|
InterruptConfig::new(interrupt::OC0, true, true),
|
||||||
let mut led1 = Output::new(porta.pa10, PinState::Low);
|
&mut dp.sysconfig,
|
||||||
let mut led2 = Output::new(porta.pa7, PinState::Low);
|
Some(&mut dp.irqsel),
|
||||||
let mut led3 = Output::new(porta.pa6, PinState::Low);
|
50.MHz(),
|
||||||
|
dp.tim0,
|
||||||
|
))
|
||||||
|
.unwrap();
|
||||||
|
let mut delay_tim1 = CountdownTimer::new(&mut dp.sysconfig, 50.MHz(), dp.tim1);
|
||||||
|
let porta = PinsA::new(&mut dp.sysconfig, dp.porta);
|
||||||
|
let mut led1 = porta.pa10.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();
|
||||||
for _ in 0..10 {
|
for _ in 0..10 {
|
||||||
led1.set_low();
|
led1.set_low();
|
||||||
led2.set_low();
|
led2.set_low();
|
||||||
led3.set_low();
|
led3.set_low();
|
||||||
delay.delay_ms(200);
|
delay_ms.delay_ms(200);
|
||||||
led1.set_high();
|
led1.set_high();
|
||||||
led2.set_high();
|
led2.set_high();
|
||||||
led3.set_high();
|
led3.set_high();
|
||||||
delay.delay_ms(200);
|
delay_tim1.delay_ms(200);
|
||||||
}
|
}
|
||||||
loop {
|
loop {
|
||||||
led1.toggle();
|
led1.toggle();
|
||||||
delay.delay_ms(200);
|
delay_ms.delay_ms(200);
|
||||||
led2.toggle();
|
led2.toggle();
|
||||||
delay.delay_ms(200);
|
delay_tim1.delay_ms(200);
|
||||||
led3.toggle();
|
led3.toggle();
|
||||||
delay.delay_ms(200);
|
delay_ms.delay_ms(200);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[interrupt]
|
||||||
|
#[allow(non_snake_case)]
|
||||||
|
fn OC0() {
|
||||||
|
default_ms_irq_handler()
|
||||||
|
}
|
||||||
|
@ -6,68 +6,92 @@
|
|||||||
#![no_std]
|
#![no_std]
|
||||||
#![allow(non_snake_case)]
|
#![allow(non_snake_case)]
|
||||||
|
|
||||||
|
use core::cell::RefCell;
|
||||||
|
use cortex_m::interrupt::Mutex;
|
||||||
use cortex_m_rt::entry;
|
use cortex_m_rt::entry;
|
||||||
use embedded_hal::delay::DelayNs;
|
use embedded_hal::delay::DelayNs;
|
||||||
// Import panic provider.
|
use panic_rtt_target as _;
|
||||||
use panic_probe as _;
|
use rtt_target::{rprintln, rtt_init_print};
|
||||||
// Import logger.
|
|
||||||
use defmt_rtt as _;
|
|
||||||
use va108xx_hal::{
|
use va108xx_hal::{
|
||||||
pac::{self, interrupt},
|
pac::{self, interrupt},
|
||||||
prelude::*,
|
prelude::*,
|
||||||
timer::{CascadeControl, CascadeSelect, CascadeSource, CountdownTimer, InterruptConfig},
|
timer::{
|
||||||
|
default_ms_irq_handler, set_up_ms_delay_provider, CascadeCtrl, CascadeSource,
|
||||||
|
CountdownTimer, Event, InterruptConfig,
|
||||||
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
|
static CSD_TGT_1: Mutex<RefCell<Option<CountdownTimer<pac::Tim4>>>> =
|
||||||
|
Mutex::new(RefCell::new(None));
|
||||||
|
static CSD_TGT_2: Mutex<RefCell<Option<CountdownTimer<pac::Tim5>>>> =
|
||||||
|
Mutex::new(RefCell::new(None));
|
||||||
|
|
||||||
#[entry]
|
#[entry]
|
||||||
fn main() -> ! {
|
fn main() -> ! {
|
||||||
defmt::println!("-- VA108xx Cascade example application--");
|
rtt_init_print!();
|
||||||
|
rprintln!("-- VA108xx Cascade example application--");
|
||||||
|
|
||||||
let dp = pac::Peripherals::take().unwrap();
|
let mut dp = pac::Peripherals::take().unwrap();
|
||||||
let mut delay = CountdownTimer::new(dp.tim0, 50.MHz());
|
let mut delay = set_up_ms_delay_provider(&mut dp.sysconfig, 50.MHz(), dp.tim0);
|
||||||
|
|
||||||
// Will be started periodically to trigger a cascade
|
// Will be started periodically to trigger a cascade
|
||||||
let mut cascade_triggerer = CountdownTimer::new(dp.tim3, 50.MHz());
|
let mut cascade_triggerer =
|
||||||
cascade_triggerer.auto_disable(true);
|
CountdownTimer::new(&mut dp.sysconfig, 50.MHz(), dp.tim3).auto_disable(true);
|
||||||
cascade_triggerer.enable_interrupt(InterruptConfig::new(pac::Interrupt::OC1, true, false));
|
cascade_triggerer.listen(
|
||||||
cascade_triggerer.enable();
|
Event::TimeOut,
|
||||||
|
InterruptConfig::new(pac::Interrupt::OC1, true, false),
|
||||||
|
Some(&mut dp.irqsel),
|
||||||
|
Some(&mut dp.sysconfig),
|
||||||
|
);
|
||||||
|
|
||||||
// First target for cascade
|
// First target for cascade
|
||||||
let mut cascade_target_1 = CountdownTimer::new(dp.tim4, 50.MHz());
|
let mut cascade_target_1 =
|
||||||
cascade_target_1.auto_deactivate(true);
|
CountdownTimer::new(&mut dp.sysconfig, 50.MHz(), dp.tim4).auto_deactivate(true);
|
||||||
cascade_target_1
|
cascade_target_1
|
||||||
.cascade_source(CascadeSelect::Csd0, CascadeSource::Tim(3))
|
.cascade_0_source(CascadeSource::Tim(3))
|
||||||
.unwrap();
|
.expect("Configuring cascade source for TIM4 failed");
|
||||||
let mut csd_cfg = CascadeControl {
|
let mut csd_cfg = CascadeCtrl {
|
||||||
enable_src_0: true,
|
enb_start_src_csd0: true,
|
||||||
trigger_mode_0: true,
|
|
||||||
..Default::default()
|
..Default::default()
|
||||||
};
|
};
|
||||||
|
// Use trigger mode here
|
||||||
|
csd_cfg.trg_csd0 = true;
|
||||||
cascade_target_1.cascade_control(csd_cfg);
|
cascade_target_1.cascade_control(csd_cfg);
|
||||||
// Normally it should already be sufficient to activate IRQ in the CTRL
|
// Normally it should already be sufficient to activate IRQ in the CTRL
|
||||||
// register but a full interrupt is use here to display print output when
|
// register but a full interrupt is use here to display print output when
|
||||||
// the timer expires
|
// the timer expires
|
||||||
cascade_target_1.enable_interrupt(InterruptConfig::new(pac::Interrupt::OC2, true, false));
|
cascade_target_1.listen(
|
||||||
|
Event::TimeOut,
|
||||||
|
InterruptConfig::new(pac::Interrupt::OC2, true, false),
|
||||||
|
Some(&mut dp.irqsel),
|
||||||
|
Some(&mut dp.sysconfig),
|
||||||
|
);
|
||||||
// The counter will only activate when the cascade signal is coming in so
|
// The counter will only activate when the cascade signal is coming in so
|
||||||
// it is okay to call start here to set the reset value
|
// it is okay to call start here to set the reset value
|
||||||
cascade_target_1.start(1.Hz());
|
cascade_target_1.start(1.Hz());
|
||||||
|
|
||||||
// Activated by first cascade target
|
// Activated by first cascade target
|
||||||
let mut cascade_target_2 = CountdownTimer::new(dp.tim5, 50.MHz());
|
let mut cascade_target_2 =
|
||||||
cascade_target_2.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_source(CascadeSelect::Csd1, CascadeSource::Tim(4))
|
.cascade_1_source(CascadeSource::Tim(4))
|
||||||
.unwrap();
|
.expect("Configuring cascade source for TIM5 failed");
|
||||||
|
|
||||||
csd_cfg = CascadeControl::default();
|
csd_cfg = CascadeCtrl::default();
|
||||||
csd_cfg.enable_src_1 = true;
|
csd_cfg.enb_start_src_csd1 = true;
|
||||||
// Use trigger mode here
|
// Use trigger mode here
|
||||||
csd_cfg.trigger_mode_1 = true;
|
csd_cfg.trg_csd1 = true;
|
||||||
cascade_target_2.cascade_control(csd_cfg);
|
cascade_target_2.cascade_control(csd_cfg);
|
||||||
// Normally it should already be sufficient to activate IRQ in the CTRL
|
// Normally it should already be sufficient to activate IRQ in the CTRL
|
||||||
// register but a full interrupt is use here to display print output when
|
// register but a full interrupt is use here to display print output when
|
||||||
// the timer expires
|
// the timer expires
|
||||||
cascade_target_2.enable_interrupt(InterruptConfig::new(pac::Interrupt::OC3, true, false));
|
cascade_target_2.listen(
|
||||||
|
Event::TimeOut,
|
||||||
|
InterruptConfig::new(pac::Interrupt::OC3, true, false),
|
||||||
|
Some(&mut dp.irqsel),
|
||||||
|
Some(&mut dp.sysconfig),
|
||||||
|
);
|
||||||
// The counter will only activate when the cascade signal is coming in so
|
// The counter will only activate when the cascade signal is coming in so
|
||||||
// it is okay to call start here to set the reset value
|
// it is okay to call start here to set the reset value
|
||||||
cascade_target_2.start(1.Hz());
|
cascade_target_2.start(1.Hz());
|
||||||
@ -79,31 +103,40 @@ fn main() -> ! {
|
|||||||
cortex_m::peripheral::NVIC::unmask(pac::Interrupt::OC2);
|
cortex_m::peripheral::NVIC::unmask(pac::Interrupt::OC2);
|
||||||
cortex_m::peripheral::NVIC::unmask(pac::Interrupt::OC3);
|
cortex_m::peripheral::NVIC::unmask(pac::Interrupt::OC3);
|
||||||
}
|
}
|
||||||
|
// Make both cascade targets accessible from the IRQ handler with the Mutex dance
|
||||||
|
cortex_m::interrupt::free(|cs| {
|
||||||
|
CSD_TGT_1.borrow(cs).replace(Some(cascade_target_1));
|
||||||
|
CSD_TGT_2.borrow(cs).replace(Some(cascade_target_2));
|
||||||
|
});
|
||||||
loop {
|
loop {
|
||||||
defmt::info!("-- Triggering cascade in 0.5 seconds --");
|
rprintln!("-- Triggering cascade in 0.5 seconds --");
|
||||||
cascade_triggerer.start(2.Hz());
|
cascade_triggerer.start(2.Hz());
|
||||||
delay.delay_ms(5000);
|
delay.delay_ms(5000);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[interrupt]
|
||||||
|
fn OC0() {
|
||||||
|
default_ms_irq_handler()
|
||||||
|
}
|
||||||
|
|
||||||
#[interrupt]
|
#[interrupt]
|
||||||
fn OC1() {
|
fn OC1() {
|
||||||
static mut IDX: u32 = 0;
|
static mut IDX: u32 = 0;
|
||||||
defmt::info!("{}: Cascade trigger timed out", &IDX);
|
rprintln!("{}: Cascade triggered timed out", &IDX);
|
||||||
*IDX += 1;
|
*IDX += 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
#[interrupt]
|
#[interrupt]
|
||||||
fn OC2() {
|
fn OC2() {
|
||||||
static mut IDX: u32 = 0;
|
static mut IDX: u32 = 0;
|
||||||
defmt::info!("{}: First cascade target timed out", &IDX);
|
rprintln!("{}: First cascade target timed out", &IDX);
|
||||||
*IDX += 1;
|
*IDX += 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
#[interrupt]
|
#[interrupt]
|
||||||
fn OC3() {
|
fn OC3() {
|
||||||
static mut IDX: u32 = 0;
|
static mut IDX: u32 = 0;
|
||||||
defmt::info!("{}: Second cascade target timed out", &IDX);
|
rprintln!("{}: Second cascade target timed out", &IDX);
|
||||||
*IDX += 1;
|
*IDX += 1;
|
||||||
}
|
}
|
||||||
|
@ -1,36 +1,39 @@
|
|||||||
//! Simple PWM example
|
//! Simple PWM example
|
||||||
//!
|
|
||||||
//! Outputs a PWM waveform on pin PA3.
|
|
||||||
#![no_main]
|
#![no_main]
|
||||||
#![no_std]
|
#![no_std]
|
||||||
|
|
||||||
use cortex_m_rt::entry;
|
use cortex_m_rt::entry;
|
||||||
use embedded_hal::{delay::DelayNs, pwm::SetDutyCycle};
|
use embedded_hal::{delay::DelayNs, pwm::SetDutyCycle};
|
||||||
// Import panic provider.
|
use panic_rtt_target as _;
|
||||||
use panic_probe as _;
|
use rtt_target::{rprintln, rtt_init_print};
|
||||||
// Import logger.
|
|
||||||
use defmt_rtt as _;
|
|
||||||
use va108xx_hal::{
|
use va108xx_hal::{
|
||||||
|
gpio::PinsA,
|
||||||
pac,
|
pac,
|
||||||
pins::PinsA,
|
|
||||||
prelude::*,
|
prelude::*,
|
||||||
pwm::{self, get_duty_from_percent, PwmA, PwmB, PwmPin},
|
pwm::{self, get_duty_from_percent, PwmA, PwmB, ReducedPwmPin},
|
||||||
timer::CountdownTimer,
|
timer::set_up_ms_delay_provider,
|
||||||
};
|
};
|
||||||
|
|
||||||
#[entry]
|
#[entry]
|
||||||
fn main() -> ! {
|
fn main() -> ! {
|
||||||
defmt::println!("-- VA108xx PWM example application--");
|
rtt_init_print!();
|
||||||
let dp = pac::Peripherals::take().unwrap();
|
rprintln!("-- VA108xx PWM example application--");
|
||||||
let pinsa = PinsA::new(dp.porta);
|
let mut dp = pac::Peripherals::take().unwrap();
|
||||||
let mut pwm = pwm::PwmPin::new(pinsa.pa3, dp.tim3, 50.MHz(), 10.Hz()).unwrap();
|
let pinsa = PinsA::new(&mut dp.sysconfig, dp.porta);
|
||||||
let mut delay = CountdownTimer::new(dp.tim0, 50.MHz());
|
let mut pwm = pwm::PwmPin::new(
|
||||||
|
&mut dp.sysconfig,
|
||||||
|
50.MHz(),
|
||||||
|
(pinsa.pa3.into_funsel_1(), dp.tim3),
|
||||||
|
10.Hz(),
|
||||||
|
);
|
||||||
|
let mut delay = set_up_ms_delay_provider(&mut dp.sysconfig, 50.MHz(), dp.tim0);
|
||||||
let mut current_duty_cycle = 0.0;
|
let mut current_duty_cycle = 0.0;
|
||||||
pwm.set_duty_cycle(get_duty_from_percent(current_duty_cycle))
|
pwm.set_duty_cycle(get_duty_from_percent(current_duty_cycle))
|
||||||
.unwrap();
|
.unwrap();
|
||||||
pwm.enable();
|
pwm.enable();
|
||||||
|
|
||||||
// Delete type information, increased code readibility for the rest of the code
|
// Delete type information, increased code readibility for the rest of the code
|
||||||
|
let mut reduced_pin = ReducedPwmPin::from(pwm);
|
||||||
loop {
|
loop {
|
||||||
let mut counter = 0;
|
let mut counter = 0;
|
||||||
// Increase duty cycle continuously
|
// Increase duty cycle continuously
|
||||||
@ -39,10 +42,11 @@ fn main() -> ! {
|
|||||||
current_duty_cycle += 0.02;
|
current_duty_cycle += 0.02;
|
||||||
counter += 1;
|
counter += 1;
|
||||||
if counter % 10 == 0 {
|
if counter % 10 == 0 {
|
||||||
defmt::info!("current duty cycle: {}", current_duty_cycle);
|
rprintln!("current duty cycle: {}", current_duty_cycle);
|
||||||
}
|
}
|
||||||
|
|
||||||
pwm.set_duty_cycle(get_duty_from_percent(current_duty_cycle))
|
reduced_pin
|
||||||
|
.set_duty_cycle(get_duty_from_percent(current_duty_cycle))
|
||||||
.unwrap();
|
.unwrap();
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -51,7 +55,7 @@ fn main() -> ! {
|
|||||||
current_duty_cycle = 0.0;
|
current_duty_cycle = 0.0;
|
||||||
let mut upper_limit = 1.0;
|
let mut upper_limit = 1.0;
|
||||||
let mut lower_limit = 0.0;
|
let mut lower_limit = 0.0;
|
||||||
let mut pwmb: PwmPin<PwmB> = PwmPin::from(pwm);
|
let mut pwmb: ReducedPwmPin<PwmB> = ReducedPwmPin::from(reduced_pin);
|
||||||
pwmb.set_pwmb_lower_limit(get_duty_from_percent(lower_limit));
|
pwmb.set_pwmb_lower_limit(get_duty_from_percent(lower_limit));
|
||||||
pwmb.set_pwmb_upper_limit(get_duty_from_percent(upper_limit));
|
pwmb.set_pwmb_upper_limit(get_duty_from_percent(upper_limit));
|
||||||
while lower_limit < 0.5 {
|
while lower_limit < 0.5 {
|
||||||
@ -60,9 +64,9 @@ fn main() -> ! {
|
|||||||
upper_limit -= 0.01;
|
upper_limit -= 0.01;
|
||||||
pwmb.set_pwmb_lower_limit(get_duty_from_percent(lower_limit));
|
pwmb.set_pwmb_lower_limit(get_duty_from_percent(lower_limit));
|
||||||
pwmb.set_pwmb_upper_limit(get_duty_from_percent(upper_limit));
|
pwmb.set_pwmb_upper_limit(get_duty_from_percent(upper_limit));
|
||||||
defmt::info!("Lower limit: {}", pwmb.pwmb_lower_limit());
|
rprintln!("Lower limit: {}", pwmb.pwmb_lower_limit());
|
||||||
defmt::info!("Upper limit: {}", pwmb.pwmb_upper_limit());
|
rprintln!("Upper limit: {}", pwmb.pwmb_upper_limit());
|
||||||
}
|
}
|
||||||
pwm = PwmPin::<PwmA>::from(pwmb);
|
reduced_pin = ReducedPwmPin::<PwmA>::from(pwmb);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
20
examples/simple/examples/rtt-log.rs
Normal file
20
examples/simple/examples/rtt-log.rs
Normal file
@ -0,0 +1,20 @@
|
|||||||
|
//! Code to test RTT logger functionality
|
||||||
|
#![no_main]
|
||||||
|
#![no_std]
|
||||||
|
|
||||||
|
use cortex_m_rt::entry;
|
||||||
|
use panic_rtt_target as _;
|
||||||
|
use rtt_target::{rprintln, rtt_init_print};
|
||||||
|
use va108xx_hal as _;
|
||||||
|
|
||||||
|
#[entry]
|
||||||
|
fn main() -> ! {
|
||||||
|
rtt_init_print!();
|
||||||
|
rprintln!("-- VA108XX RTT example --");
|
||||||
|
let mut counter = 0;
|
||||||
|
loop {
|
||||||
|
rprintln!("{}: Hello, world!", counter);
|
||||||
|
counter += 1;
|
||||||
|
cortex_m::asm::delay(25_000_000);
|
||||||
|
}
|
||||||
|
}
|
@ -1,21 +1,23 @@
|
|||||||
//! SPI example application
|
//! SPI example application
|
||||||
#![no_main]
|
#![no_main]
|
||||||
#![no_std]
|
#![no_std]
|
||||||
|
|
||||||
|
use core::cell::RefCell;
|
||||||
|
|
||||||
use cortex_m_rt::entry;
|
use cortex_m_rt::entry;
|
||||||
use embedded_hal::{
|
use embedded_hal::{
|
||||||
delay::DelayNs,
|
delay::DelayNs,
|
||||||
spi::{Mode, SpiBus, MODE_0},
|
spi::{Mode, SpiBus, MODE_0},
|
||||||
};
|
};
|
||||||
// Import panic provider.
|
use panic_rtt_target as _;
|
||||||
use panic_probe as _;
|
use rtt_target::{rprintln, rtt_init_print};
|
||||||
// Import logger.
|
|
||||||
use defmt_rtt as _;
|
|
||||||
use va108xx_hal::{
|
use va108xx_hal::{
|
||||||
pac,
|
gpio::{PinsA, PinsB},
|
||||||
pins::{PinsA, PinsB},
|
pac::{self, interrupt},
|
||||||
prelude::*,
|
prelude::*,
|
||||||
spi::{self, configure_pin_as_hw_cs_pin, Spi, SpiClkConfig, TransferConfig},
|
spi::{self, Spi, SpiBase, SpiClkConfig, TransferConfigWithHwcs},
|
||||||
timer::CountdownTimer,
|
timer::{default_ms_irq_handler, set_up_ms_tick},
|
||||||
|
InterruptConfig,
|
||||||
};
|
};
|
||||||
|
|
||||||
#[derive(PartialEq, Debug)]
|
#[derive(PartialEq, Debug)]
|
||||||
@ -41,14 +43,23 @@ const FILL_WORD: u8 = 0x0f;
|
|||||||
|
|
||||||
#[entry]
|
#[entry]
|
||||||
fn main() -> ! {
|
fn main() -> ! {
|
||||||
defmt::println!("-- VA108xx SPI example application--");
|
rtt_init_print!();
|
||||||
let dp = pac::Peripherals::take().unwrap();
|
rprintln!("-- VA108xx SPI example application--");
|
||||||
let mut delay = CountdownTimer::new(dp.tim0, 50.MHz());
|
let mut dp = pac::Peripherals::take().unwrap();
|
||||||
|
let mut delay = set_up_ms_tick(
|
||||||
|
InterruptConfig::new(interrupt::OC0, true, true),
|
||||||
|
&mut dp.sysconfig,
|
||||||
|
Some(&mut dp.irqsel),
|
||||||
|
50.MHz(),
|
||||||
|
dp.tim0,
|
||||||
|
);
|
||||||
|
|
||||||
let spi_clk_cfg = SpiClkConfig::from_clk(50.MHz(), SPI_SPEED_KHZ.kHz())
|
let spi_clk_cfg = SpiClkConfig::from_clk(50.MHz(), SPI_SPEED_KHZ.kHz())
|
||||||
.expect("creating SPI clock config failed");
|
.expect("creating SPI clock config failed");
|
||||||
let pinsa = PinsA::new(dp.porta);
|
let spia_ref: RefCell<Option<SpiBase<pac::Spia, u8>>> = RefCell::new(None);
|
||||||
let pinsb = PinsB::new(dp.portb);
|
let spib_ref: RefCell<Option<SpiBase<pac::Spib, u8>>> = RefCell::new(None);
|
||||||
|
let pinsa = PinsA::new(&mut dp.sysconfig, dp.porta);
|
||||||
|
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 {
|
||||||
@ -56,56 +67,92 @@ fn main() -> ! {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Set up the SPI peripheral
|
// Set up the SPI peripheral
|
||||||
let mut spi = match SPI_BUS_SEL {
|
match SPI_BUS_SEL {
|
||||||
SpiBusSelect::SpiAPortA => {
|
SpiBusSelect::SpiAPortA => {
|
||||||
let (sck, mosi, miso) = (pinsa.pa31, pinsa.pa30, pinsa.pa29);
|
let (sck, mosi, miso) = (
|
||||||
let mut spia = Spi::new(dp.spia, (sck, miso, mosi), spi_cfg).unwrap();
|
pinsa.pa31.into_funsel_1(),
|
||||||
|
pinsa.pa30.into_funsel_1(),
|
||||||
|
pinsa.pa29.into_funsel_1(),
|
||||||
|
);
|
||||||
|
let mut spia = Spi::new(
|
||||||
|
&mut dp.sysconfig,
|
||||||
|
50.MHz(),
|
||||||
|
dp.spia,
|
||||||
|
(sck, miso, mosi),
|
||||||
|
spi_cfg,
|
||||||
|
);
|
||||||
spia.set_fill_word(FILL_WORD);
|
spia.set_fill_word(FILL_WORD);
|
||||||
spia
|
spia_ref.borrow_mut().replace(spia.downgrade());
|
||||||
}
|
}
|
||||||
SpiBusSelect::SpiAPortB => {
|
SpiBusSelect::SpiAPortB => {
|
||||||
let (sck, mosi, miso) = (pinsb.pb9, pinsb.pb8, pinsb.pb7);
|
let (sck, mosi, miso) = (
|
||||||
let mut spia = Spi::new(dp.spia, (sck, miso, mosi), spi_cfg).unwrap();
|
pinsb.pb9.into_funsel_2(),
|
||||||
|
pinsb.pb8.into_funsel_2(),
|
||||||
|
pinsb.pb7.into_funsel_2(),
|
||||||
|
);
|
||||||
|
let mut spia = Spi::new(
|
||||||
|
&mut dp.sysconfig,
|
||||||
|
50.MHz(),
|
||||||
|
dp.spia,
|
||||||
|
(sck, miso, mosi),
|
||||||
|
spi_cfg,
|
||||||
|
);
|
||||||
spia.set_fill_word(FILL_WORD);
|
spia.set_fill_word(FILL_WORD);
|
||||||
spia
|
spia_ref.borrow_mut().replace(spia.downgrade());
|
||||||
}
|
}
|
||||||
SpiBusSelect::SpiBPortB => {
|
SpiBusSelect::SpiBPortB => {
|
||||||
let (sck, mosi, miso) = (pinsb.pb5, pinsb.pb4, pinsb.pb3);
|
let (sck, mosi, miso) = (
|
||||||
let mut spib = Spi::new(dp.spib, (sck, miso, mosi), spi_cfg).unwrap();
|
pinsb.pb5.into_funsel_1(),
|
||||||
|
pinsb.pb4.into_funsel_1(),
|
||||||
|
pinsb.pb3.into_funsel_1(),
|
||||||
|
);
|
||||||
|
let mut spib = Spi::new(
|
||||||
|
&mut dp.sysconfig,
|
||||||
|
50.MHz(),
|
||||||
|
dp.spib,
|
||||||
|
(sck, miso, mosi),
|
||||||
|
spi_cfg,
|
||||||
|
);
|
||||||
spib.set_fill_word(FILL_WORD);
|
spib.set_fill_word(FILL_WORD);
|
||||||
spib
|
spib_ref.borrow_mut().replace(spib.downgrade());
|
||||||
|
}
|
||||||
}
|
}
|
||||||
};
|
|
||||||
// Configure transfer specific properties here
|
// Configure transfer specific properties here
|
||||||
match SPI_BUS_SEL {
|
match SPI_BUS_SEL {
|
||||||
SpiBusSelect::SpiAPortA | SpiBusSelect::SpiAPortB => {
|
SpiBusSelect::SpiAPortA | SpiBusSelect::SpiAPortB => {
|
||||||
let transfer_cfg = TransferConfig {
|
if let Some(ref mut spi) = *spia_ref.borrow_mut() {
|
||||||
clk_cfg: Some(spi_clk_cfg),
|
let transfer_cfg = TransferConfigWithHwcs::new_no_hw_cs(
|
||||||
mode: Some(SPI_MODE),
|
Some(spi_clk_cfg),
|
||||||
sod: true,
|
Some(SPI_MODE),
|
||||||
blockmode: BLOCKMODE,
|
BLOCKMODE,
|
||||||
bmstall: true,
|
true,
|
||||||
hw_cs: None,
|
false,
|
||||||
};
|
);
|
||||||
spi.cfg_transfer(&transfer_cfg);
|
spi.cfg_transfer(&transfer_cfg);
|
||||||
}
|
}
|
||||||
|
}
|
||||||
SpiBusSelect::SpiBPortB => {
|
SpiBusSelect::SpiBPortB => {
|
||||||
let hw_cs_pin = configure_pin_as_hw_cs_pin(pinsb.pb2);
|
if let Some(ref mut spi) = *spib_ref.borrow_mut() {
|
||||||
let transfer_cfg = TransferConfig {
|
let hw_cs_pin = pinsb.pb2.into_funsel_1();
|
||||||
clk_cfg: Some(spi_clk_cfg),
|
let transfer_cfg = TransferConfigWithHwcs::new(
|
||||||
mode: Some(SPI_MODE),
|
Some(spi_clk_cfg),
|
||||||
sod: false,
|
Some(SPI_MODE),
|
||||||
blockmode: BLOCKMODE,
|
Some(hw_cs_pin),
|
||||||
bmstall: true,
|
BLOCKMODE,
|
||||||
hw_cs: Some(hw_cs_pin),
|
true,
|
||||||
};
|
false,
|
||||||
|
);
|
||||||
spi.cfg_transfer(&transfer_cfg);
|
spi.cfg_transfer(&transfer_cfg);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// Application logic
|
// Application logic
|
||||||
loop {
|
loop {
|
||||||
let mut reply_buf: [u8; 8] = [0; 8];
|
let mut reply_buf: [u8; 8] = [0; 8];
|
||||||
|
match SPI_BUS_SEL {
|
||||||
|
SpiBusSelect::SpiAPortA | SpiBusSelect::SpiAPortB => {
|
||||||
|
if let Some(ref mut spi) = *spia_ref.borrow_mut() {
|
||||||
// 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.
|
||||||
@ -116,7 +163,7 @@ fn main() -> ! {
|
|||||||
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]);
|
||||||
defmt::info!(
|
rprintln!(
|
||||||
"Received reply: {}, {}, {}",
|
"Received reply: {}, {}, {}",
|
||||||
reply_buf[0],
|
reply_buf[0],
|
||||||
reply_buf[1],
|
reply_buf[1],
|
||||||
@ -126,7 +173,7 @@ fn main() -> ! {
|
|||||||
|
|
||||||
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();
|
||||||
defmt::info!(
|
rprintln!(
|
||||||
"Received reply: {}, {}, {}",
|
"Received reply: {}, {}, {}",
|
||||||
tx_rx_buf[0],
|
tx_rx_buf[0],
|
||||||
tx_rx_buf[1],
|
tx_rx_buf[1],
|
||||||
@ -134,4 +181,44 @@ fn main() -> ! {
|
|||||||
);
|
);
|
||||||
assert_eq!(&tx_rx_buf[0..3], &[0x03, 0x02, 0x01]);
|
assert_eq!(&tx_rx_buf[0..3], &[0x03, 0x02, 0x01]);
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
SpiBusSelect::SpiBPortB => {
|
||||||
|
if let Some(ref mut spi) = *spib_ref.borrow_mut() {
|
||||||
|
// Can't really verify correct reply here.
|
||||||
|
spi.write(&[0x42]).expect("write failed");
|
||||||
|
// 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];
|
||||||
|
spi.transfer(&mut reply_buf[0..3], &tx_buf).unwrap();
|
||||||
|
assert_eq!(tx_buf, reply_buf[0..3]);
|
||||||
|
rprintln!(
|
||||||
|
"Received reply: {}, {}, {}",
|
||||||
|
reply_buf[0],
|
||||||
|
reply_buf[1],
|
||||||
|
reply_buf[2]
|
||||||
|
);
|
||||||
|
delay.delay_ms(500_u32);
|
||||||
|
|
||||||
|
let mut tx_rx_buf: [u8; 3] = [0x03, 0x02, 0x01];
|
||||||
|
spi.transfer_in_place(&mut tx_rx_buf).unwrap();
|
||||||
|
rprintln!(
|
||||||
|
"Received reply: {}, {}, {}",
|
||||||
|
tx_rx_buf[0],
|
||||||
|
tx_rx_buf[1],
|
||||||
|
tx_rx_buf[2]
|
||||||
|
);
|
||||||
|
assert_eq!(&tx_rx_buf[0..3], &[0x03, 0x02, 0x01]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[interrupt]
|
||||||
|
#[allow(non_snake_case)]
|
||||||
|
fn OC0() {
|
||||||
|
default_ms_irq_handler()
|
||||||
}
|
}
|
||||||
|
@ -2,18 +2,19 @@
|
|||||||
#![no_main]
|
#![no_main]
|
||||||
#![no_std]
|
#![no_std]
|
||||||
|
|
||||||
|
use core::cell::Cell;
|
||||||
use cortex_m_rt::entry;
|
use cortex_m_rt::entry;
|
||||||
use embedded_hal::delay::DelayNs;
|
use critical_section::Mutex;
|
||||||
// Import panic provider.
|
use panic_rtt_target as _;
|
||||||
use panic_probe as _;
|
use rtt_target::{rprintln, rtt_init_print};
|
||||||
// Import logger.
|
|
||||||
use defmt_rtt as _;
|
|
||||||
use portable_atomic::AtomicU32;
|
|
||||||
use va108xx_hal::{
|
use va108xx_hal::{
|
||||||
|
clock::{get_sys_clock, set_sys_clock},
|
||||||
pac::{self, interrupt},
|
pac::{self, interrupt},
|
||||||
prelude::*,
|
prelude::*,
|
||||||
time::Hertz,
|
time::Hertz,
|
||||||
timer::{CountdownTimer, InterruptConfig},
|
timer::{
|
||||||
|
default_ms_irq_handler, set_up_ms_tick, CountdownTimer, Event, InterruptConfig, MS_COUNTER,
|
||||||
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
#[allow(dead_code)]
|
#[allow(dead_code)]
|
||||||
@ -22,15 +23,15 @@ enum LibType {
|
|||||||
Hal,
|
Hal,
|
||||||
}
|
}
|
||||||
|
|
||||||
static MS_COUNTER: AtomicU32 = AtomicU32::new(0);
|
static SEC_COUNTER: Mutex<Cell<u32>> = Mutex::new(Cell::new(0));
|
||||||
static SEC_COUNTER: AtomicU32 = AtomicU32::new(0);
|
|
||||||
|
|
||||||
#[entry]
|
#[entry]
|
||||||
fn main() -> ! {
|
fn main() -> ! {
|
||||||
let dp = pac::Peripherals::take().unwrap();
|
rtt_init_print!();
|
||||||
let mut delay = CountdownTimer::new(dp.tim2, 50.MHz());
|
let mut dp = pac::Peripherals::take().unwrap();
|
||||||
let mut last_ms = 0;
|
let mut last_ms = 0;
|
||||||
defmt::info!("-- Vorago system ticks using timers --");
|
rprintln!("-- Vorago system ticks using timers --");
|
||||||
|
set_sys_clock(50.MHz());
|
||||||
let lib_type = LibType::Hal;
|
let lib_type = LibType::Hal;
|
||||||
match lib_type {
|
match lib_type {
|
||||||
LibType::Pac => {
|
LibType::Pac => {
|
||||||
@ -65,24 +66,34 @@ fn main() -> ! {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
LibType::Hal => {
|
LibType::Hal => {
|
||||||
let mut ms_timer = CountdownTimer::new(dp.tim0, 50.MHz());
|
set_up_ms_tick(
|
||||||
ms_timer.enable_interrupt(InterruptConfig::new(interrupt::OC0, true, true));
|
InterruptConfig::new(interrupt::OC0, true, true),
|
||||||
ms_timer.start(1.kHz());
|
&mut dp.sysconfig,
|
||||||
let mut second_timer = CountdownTimer::new(dp.tim1, 50.MHz());
|
Some(&mut dp.irqsel),
|
||||||
second_timer.enable_interrupt(InterruptConfig::new(interrupt::OC1, true, true));
|
50.MHz(),
|
||||||
|
dp.tim0,
|
||||||
|
);
|
||||||
|
let mut second_timer =
|
||||||
|
CountdownTimer::new(&mut dp.sysconfig, get_sys_clock().unwrap(), dp.tim1);
|
||||||
|
second_timer.listen(
|
||||||
|
Event::TimeOut,
|
||||||
|
InterruptConfig::new(interrupt::OC1, true, true),
|
||||||
|
Some(&mut dp.irqsel),
|
||||||
|
Some(&mut dp.sysconfig),
|
||||||
|
);
|
||||||
second_timer.start(1.Hz());
|
second_timer.start(1.Hz());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
loop {
|
loop {
|
||||||
let current_ms = MS_COUNTER.load(portable_atomic::Ordering::Relaxed);
|
let current_ms = critical_section::with(|cs| MS_COUNTER.borrow(cs).get());
|
||||||
if current_ms - last_ms >= 1000 {
|
if current_ms - last_ms >= 1000 {
|
||||||
// To prevent drift.
|
// To prevent drift.
|
||||||
last_ms += 1000;
|
last_ms += 1000;
|
||||||
defmt::info!("MS counter: {}", current_ms);
|
rprintln!("MS counter: {}", current_ms);
|
||||||
let second = SEC_COUNTER.load(portable_atomic::Ordering::Relaxed);
|
let second = critical_section::with(|cs| SEC_COUNTER.borrow(cs).get());
|
||||||
defmt::info!("Second counter: {}", second);
|
rprintln!("Second counter: {}", second);
|
||||||
}
|
}
|
||||||
delay.delay_ms(50);
|
cortex_m::asm::delay(10000);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -96,11 +107,15 @@ fn unmask_irqs() {
|
|||||||
#[interrupt]
|
#[interrupt]
|
||||||
#[allow(non_snake_case)]
|
#[allow(non_snake_case)]
|
||||||
fn OC0() {
|
fn OC0() {
|
||||||
MS_COUNTER.fetch_add(1, portable_atomic::Ordering::Relaxed);
|
default_ms_irq_handler()
|
||||||
}
|
}
|
||||||
|
|
||||||
#[interrupt]
|
#[interrupt]
|
||||||
#[allow(non_snake_case)]
|
#[allow(non_snake_case)]
|
||||||
fn OC1() {
|
fn OC1() {
|
||||||
SEC_COUNTER.fetch_add(1, portable_atomic::Ordering::Relaxed);
|
critical_section::with(|cs| {
|
||||||
|
let mut sec = SEC_COUNTER.borrow(cs).get();
|
||||||
|
sec += 1;
|
||||||
|
SEC_COUNTER.borrow(cs).set(sec);
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
@ -13,23 +13,27 @@
|
|||||||
use cortex_m_rt::entry;
|
use cortex_m_rt::entry;
|
||||||
use embedded_hal_nb::{nb, serial::Read};
|
use embedded_hal_nb::{nb, serial::Read};
|
||||||
use embedded_io::Write as _;
|
use embedded_io::Write as _;
|
||||||
// Import panic provider.
|
use panic_rtt_target as _;
|
||||||
use panic_probe as _;
|
use rtt_target::{rprintln, rtt_init_print};
|
||||||
// Import logger.
|
use va108xx_hal::{gpio::PinsA, pac, prelude::*, uart};
|
||||||
use defmt_rtt as _;
|
|
||||||
use va108xx_hal::{pac, pins::PinsA, prelude::*, uart};
|
|
||||||
|
|
||||||
#[entry]
|
#[entry]
|
||||||
fn main() -> ! {
|
fn main() -> ! {
|
||||||
defmt::println!("-- VA108xx UART example application--");
|
rtt_init_print!();
|
||||||
|
rprintln!("-- VA108xx UART example application--");
|
||||||
|
|
||||||
let dp = pac::Peripherals::take().unwrap();
|
let mut dp = pac::Peripherals::take().unwrap();
|
||||||
|
|
||||||
let gpioa = PinsA::new(dp.porta);
|
let gpioa = PinsA::new(&mut dp.sysconfig, dp.porta);
|
||||||
let tx = gpioa.pa9;
|
let tx = gpioa.pa9.into_funsel_2();
|
||||||
let rx = gpioa.pa8;
|
let rx = gpioa.pa8.into_funsel_2();
|
||||||
let uart =
|
let uart = uart::Uart::new_without_interrupt(
|
||||||
uart::Uart::new_without_interrupt(dp.uarta, tx, rx, 50.MHz(), 115200.Hz().into()).unwrap();
|
&mut dp.sysconfig,
|
||||||
|
50.MHz(),
|
||||||
|
dp.uarta,
|
||||||
|
(tx, rx),
|
||||||
|
115200.Hz(),
|
||||||
|
);
|
||||||
|
|
||||||
let (mut tx, mut rx) = uart.split();
|
let (mut tx, mut rx) = uart.split();
|
||||||
writeln!(tx, "Hello World\r").unwrap();
|
writeln!(tx, "Hello World\r").unwrap();
|
||||||
@ -41,6 +45,9 @@ fn main() -> ! {
|
|||||||
.expect("TX send error");
|
.expect("TX send error");
|
||||||
}
|
}
|
||||||
Err(nb::Error::WouldBlock) => (),
|
Err(nb::Error::WouldBlock) => (),
|
||||||
|
Err(nb::Error::Other(uart_error)) => {
|
||||||
|
rprintln!("UART receive error {:?}", uart_error);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -3,8 +3,7 @@
|
|||||||
#![no_std]
|
#![no_std]
|
||||||
|
|
||||||
use cortex_m_rt::entry;
|
use cortex_m_rt::entry;
|
||||||
use panic_probe as _;
|
use panic_rtt_target as _;
|
||||||
use va108xx_hal as _;
|
|
||||||
|
|
||||||
#[entry]
|
#[entry]
|
||||||
fn main() -> ! {
|
fn main() -> ! {
|
||||||
|
@ -4,31 +4,32 @@ version = "0.1.0"
|
|||||||
edition = "2021"
|
edition = "2021"
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
cortex-m = { version = "0.7", features = ["critical-section-single-core"]}
|
cortex-m = "0.7"
|
||||||
cortex-m-rt = "0.7"
|
cortex-m-rt = "0.7"
|
||||||
|
embedded-hal = "1"
|
||||||
|
embedded-hal-nb = "1"
|
||||||
embedded-io = "0.6"
|
embedded-io = "0.6"
|
||||||
defmt = "1"
|
panic-rtt-target = "0.2"
|
||||||
defmt-rtt = { version = "0.4" }
|
rtt-target = "0.6"
|
||||||
panic-probe = { version = "0.3", features = ["print-defmt"] }
|
|
||||||
num_enum = { version = "0.7", default-features = false }
|
num_enum = { version = "0.7", default-features = false }
|
||||||
|
log = "0.4"
|
||||||
|
crc = "3"
|
||||||
cobs = { version = "0.3", default-features = false }
|
cobs = { version = "0.3", default-features = false }
|
||||||
satrs = { version = "0.2", 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"] }
|
ringbuf = { version = "0.4.7", default-features = false, features = ["portable-atomic"] }
|
||||||
spacepackets = { version = "0.11", default-features = false, features = ["defmt"] }
|
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
|
# Even though we do not use this directly, we need to activate this feature explicitely
|
||||||
# so that RTIC compiles because thumv6 does not have CAS operations natively.
|
# so that RTIC compiles because thumv6 does not have CAS operations natively.
|
||||||
portable-atomic = {version = "1", features = ["unsafe-assume-single-core"]}
|
portable-atomic = {version = "1", features = ["unsafe-assume-single-core"]}
|
||||||
|
|
||||||
rtic = { version = "2", features = ["thumbv6-backend"] }
|
rtic = { version = "2", features = ["thumbv6-backend"] }
|
||||||
rtic-monotonics = { version = "2", features = ["cortex-m-systick"] }
|
rtic-monotonics = { version = "2", features = ["cortex-m-systick"] }
|
||||||
|
rtic-sync = {version = "1", features = ["defmt-03"]}
|
||||||
|
|
||||||
[dependencies.va108xx-hal]
|
[dependencies.va108xx-hal]
|
||||||
version = "0.11"
|
version = "0.10"
|
||||||
path = "../va108xx-hal"
|
|
||||||
features = ["defmt"]
|
|
||||||
|
|
||||||
[dependencies.vorago-reb1]
|
[dependencies.vorago-reb1]
|
||||||
version = "0.8"
|
version = "0.8"
|
||||||
|
|
||||||
[package.metadata.cargo-machete]
|
|
||||||
ignored = ["portable-atomic", "cortex-m-rt"]
|
|
||||||
|
@ -2,15 +2,16 @@
|
|||||||
from typing import List, Tuple
|
from typing import List, Tuple
|
||||||
from spacepackets.ecss.defs import PusService
|
from spacepackets.ecss.defs import PusService
|
||||||
from spacepackets.ecss.tm import PusTm
|
from spacepackets.ecss.tm import PusTm
|
||||||
|
from tmtccmd.com import ComInterface
|
||||||
import toml
|
import toml
|
||||||
import struct
|
import struct
|
||||||
import logging
|
import logging
|
||||||
import argparse
|
import argparse
|
||||||
import time
|
import time
|
||||||
import enum
|
import enum
|
||||||
from com_interface import ComInterface
|
from tmtccmd.com.serial_base import SerialCfg
|
||||||
from com_interface.serial_base import SerialCfg
|
from tmtccmd.com.serial_cobs import SerialCobsComIF
|
||||||
from com_interface.serial_cobs import SerialCobsComIF
|
from tmtccmd.com.ser_utils import prompt_com_port
|
||||||
from crcmod.predefined import PredefinedCrc
|
from crcmod.predefined import PredefinedCrc
|
||||||
from spacepackets.ecss.tc import PusTc
|
from spacepackets.ecss.tc import PusTc
|
||||||
from spacepackets.ecss.pus_verificator import PusVerificator, StatusField
|
from spacepackets.ecss.pus_verificator import PusVerificator, StatusField
|
||||||
@ -100,7 +101,15 @@ class ImageLoader:
|
|||||||
)
|
)
|
||||||
self.verificator.add_tc(action_tc)
|
self.verificator.add_tc(action_tc)
|
||||||
self.com_if.send(bytes(action_tc.pack()))
|
self.com_if.send(bytes(action_tc.pack()))
|
||||||
self.await_for_command_copletion("boot image selection command")
|
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):
|
def handle_ping_cmd(self):
|
||||||
_LOGGER.info("Sending ping command")
|
_LOGGER.info("Sending ping command")
|
||||||
@ -113,26 +122,16 @@ class ImageLoader:
|
|||||||
)
|
)
|
||||||
self.verificator.add_tc(ping_tc)
|
self.verificator.add_tc(ping_tc)
|
||||||
self.com_if.send(bytes(ping_tc.pack()))
|
self.com_if.send(bytes(ping_tc.pack()))
|
||||||
self.await_for_command_copletion("ping command")
|
|
||||||
|
|
||||||
def await_for_command_copletion(self, context: str):
|
data_available = self.com_if.data_available(0.4)
|
||||||
done = False
|
if not data_available:
|
||||||
now = time.time()
|
_LOGGER.warning("no ping reply received")
|
||||||
while time.time() - now < 2.0:
|
|
||||||
if not self.com_if.data_available():
|
|
||||||
time.sleep(0.2)
|
|
||||||
continue
|
|
||||||
for reply in self.com_if.receive():
|
for reply in self.com_if.receive():
|
||||||
result = self.verificator.add_tm(
|
result = self.verificator.add_tm(
|
||||||
Service1Tm.from_tm(PusTm.unpack(reply, 0), UnpackParams(0))
|
Service1Tm.from_tm(PusTm.unpack(reply, 0), UnpackParams(0))
|
||||||
)
|
)
|
||||||
if result is not None and result.completed:
|
if result is not None and result.completed:
|
||||||
_LOGGER.info(f"received {context} reply")
|
_LOGGER.info("received ping completion reply")
|
||||||
done = True
|
|
||||||
if done:
|
|
||||||
break
|
|
||||||
if not done:
|
|
||||||
_LOGGER.warning(f"no {context} reply received")
|
|
||||||
|
|
||||||
def handle_corruption_cmd(self, target: Target):
|
def handle_corruption_cmd(self, target: Target):
|
||||||
if target == Target.BOOTLOADER:
|
if target == Target.BOOTLOADER:
|
||||||
@ -301,12 +300,12 @@ def main() -> int:
|
|||||||
if "serial_port" in parsed_toml:
|
if "serial_port" in parsed_toml:
|
||||||
serial_port = parsed_toml["serial_port"]
|
serial_port = parsed_toml["serial_port"]
|
||||||
if serial_port is None:
|
if serial_port is None:
|
||||||
serial_port = input("Please specify the serial port manually: ")
|
serial_port = prompt_com_port()
|
||||||
serial_cfg = SerialCfg(
|
serial_cfg = SerialCfg(
|
||||||
com_if_id="ser_cobs",
|
com_if_id="ser_cobs",
|
||||||
serial_port=serial_port,
|
serial_port=serial_port,
|
||||||
baud_rate=BAUD_RATE,
|
baud_rate=BAUD_RATE,
|
||||||
polling_frequency=0.1,
|
serial_timeout=0.1,
|
||||||
)
|
)
|
||||||
verificator = PusVerificator()
|
verificator = PusVerificator()
|
||||||
com_if = SerialCobsComIF(serial_cfg)
|
com_if = SerialCobsComIF(serial_cfg)
|
||||||
|
@ -1 +1 @@
|
|||||||
serial_port = "/dev/ttyUSB1"
|
serial_port = "/dev/ttyUSB0"
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
spacepackets == 0.28
|
spacepackets == 0.24
|
||||||
com-interface == 0.1
|
tmtccmd == 8.0.2
|
||||||
toml == 0.10
|
toml == 0.10
|
||||||
pyelftools == 0.31
|
pyelftools == 0.31
|
||||||
crcmod == 1.7
|
crcmod == 1.7
|
||||||
|
@ -9,6 +9,7 @@ edition = "2021"
|
|||||||
cortex-m-rt = "0.7"
|
cortex-m-rt = "0.7"
|
||||||
panic-rtt-target = { version = "0.1.3" }
|
panic-rtt-target = { version = "0.1.3" }
|
||||||
rtt-target = { version = "0.5" }
|
rtt-target = { version = "0.5" }
|
||||||
|
cortex-m = { version = "0.7", features = ["critical-section-single-core"] }
|
||||||
embedded-hal = "1"
|
embedded-hal = "1"
|
||||||
va108xx-hal = { version = "0.10.0" }
|
va108xx-hal = { version = "0.10.0" }
|
||||||
|
|
||||||
|
@ -9,6 +9,7 @@ edition = "2021"
|
|||||||
cortex-m-rt = "0.7"
|
cortex-m-rt = "0.7"
|
||||||
panic-rtt-target = { version = "0.1.3" }
|
panic-rtt-target = { version = "0.1.3" }
|
||||||
rtt-target = { version = "0.5" }
|
rtt-target = { version = "0.5" }
|
||||||
|
cortex-m = { version = "0.7", features = ["critical-section-single-core"] }
|
||||||
embedded-hal = "1"
|
embedded-hal = "1"
|
||||||
va108xx-hal = { version = "0.10.0" }
|
va108xx-hal = { version = "0.10.0" }
|
||||||
|
|
||||||
|
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);
|
||||||
|
}
|
||||||
|
}
|
@ -3,9 +3,8 @@
|
|||||||
#![no_main]
|
#![no_main]
|
||||||
#![no_std]
|
#![no_std]
|
||||||
|
|
||||||
use defmt_rtt as _; // global logger
|
|
||||||
use num_enum::TryFromPrimitive;
|
use num_enum::TryFromPrimitive;
|
||||||
use panic_probe as _;
|
use panic_rtt_target as _;
|
||||||
use ringbuf::{
|
use ringbuf::{
|
||||||
traits::{Consumer, Observer, Producer},
|
traits::{Consumer, Observer, Producer},
|
||||||
StaticRb,
|
StaticRb,
|
||||||
@ -30,7 +29,7 @@ pub enum ActionId {
|
|||||||
SetBootSlot = 130,
|
SetBootSlot = 130,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Copy, Clone, PartialEq, Eq, TryFromPrimitive, defmt::Format)]
|
#[derive(Debug, Copy, Clone, PartialEq, Eq, TryFromPrimitive)]
|
||||||
#[repr(u8)]
|
#[repr(u8)]
|
||||||
enum AppSel {
|
enum AppSel {
|
||||||
A = 0,
|
A = 0,
|
||||||
@ -61,14 +60,16 @@ mod app {
|
|||||||
use super::*;
|
use super::*;
|
||||||
use cortex_m::asm;
|
use cortex_m::asm;
|
||||||
use embedded_io::Write;
|
use embedded_io::Write;
|
||||||
|
use panic_rtt_target as _;
|
||||||
use rtic::Mutex;
|
use rtic::Mutex;
|
||||||
use rtic_monotonics::systick::prelude::*;
|
use rtic_monotonics::systick::prelude::*;
|
||||||
|
use rtt_target::rprintln;
|
||||||
use satrs::pus::verification::{FailParams, VerificationReportCreator};
|
use satrs::pus::verification::{FailParams, VerificationReportCreator};
|
||||||
use spacepackets::ecss::PusServiceId;
|
use spacepackets::ecss::PusServiceId;
|
||||||
use spacepackets::ecss::{
|
use spacepackets::ecss::{
|
||||||
tc::PusTcReader, tm::PusTmCreator, EcssEnumU8, PusPacket, WritablePusPacket,
|
tc::PusTcReader, tm::PusTmCreator, EcssEnumU8, PusPacket, WritablePusPacket,
|
||||||
};
|
};
|
||||||
use va108xx_hal::pins::PinsA;
|
use va108xx_hal::gpio::PinsA;
|
||||||
use va108xx_hal::uart::IrqContextTimeoutOrMaxSize;
|
use va108xx_hal::uart::IrqContextTimeoutOrMaxSize;
|
||||||
use va108xx_hal::{pac, uart, InterruptConfig};
|
use va108xx_hal::{pac, uart, InterruptConfig};
|
||||||
use vorago_reb1::m95m01::M95M01;
|
use vorago_reb1::m95m01::M95M01;
|
||||||
@ -83,8 +84,8 @@ mod app {
|
|||||||
|
|
||||||
#[local]
|
#[local]
|
||||||
struct Local {
|
struct Local {
|
||||||
uart_rx: uart::RxWithInterrupt,
|
uart_rx: uart::RxWithInterrupt<pac::Uarta>,
|
||||||
uart_tx: uart::Tx,
|
uart_tx: uart::Tx<pac::Uarta>,
|
||||||
rx_context: IrqContextTimeoutOrMaxSize,
|
rx_context: IrqContextTimeoutOrMaxSize,
|
||||||
verif_reporter: VerificationReportCreator,
|
verif_reporter: VerificationReportCreator,
|
||||||
nvm: M95M01,
|
nvm: M95M01,
|
||||||
@ -101,26 +102,26 @@ mod app {
|
|||||||
|
|
||||||
#[init]
|
#[init]
|
||||||
fn init(cx: init::Context) -> (Shared, Local) {
|
fn init(cx: init::Context) -> (Shared, Local) {
|
||||||
defmt::println!("-- Vorago flashloader --");
|
rtt_log::init();
|
||||||
|
rprintln!("-- Vorago flashloader --");
|
||||||
|
|
||||||
Mono::start(cx.core.SYST, SYSCLK_FREQ.raw());
|
Mono::start(cx.core.SYST, SYSCLK_FREQ.raw());
|
||||||
|
|
||||||
let mut dp = cx.device;
|
let mut dp = cx.device;
|
||||||
let nvm = M95M01::new(&mut dp.sysconfig, SYSCLK_FREQ, dp.spic);
|
let nvm = M95M01::new(&mut dp.sysconfig, SYSCLK_FREQ, dp.spic);
|
||||||
|
|
||||||
let gpioa = PinsA::new(dp.porta);
|
let gpioa = PinsA::new(&mut dp.sysconfig, dp.porta);
|
||||||
let tx = gpioa.pa9;
|
let tx = gpioa.pa9.into_funsel_2();
|
||||||
let rx = gpioa.pa8;
|
let rx = gpioa.pa8.into_funsel_2();
|
||||||
|
|
||||||
let irq_uart = uart::Uart::new_with_interrupt(
|
let irq_uart = uart::Uart::new_with_interrupt(
|
||||||
dp.uarta,
|
&mut dp.sysconfig,
|
||||||
tx,
|
|
||||||
rx,
|
|
||||||
SYSCLK_FREQ,
|
SYSCLK_FREQ,
|
||||||
UART_BAUDRATE.Hz().into(),
|
dp.uarta,
|
||||||
|
(tx, rx),
|
||||||
|
UART_BAUDRATE.Hz(),
|
||||||
InterruptConfig::new(pac::Interrupt::OC0, true, true),
|
InterruptConfig::new(pac::Interrupt::OC0, true, true),
|
||||||
)
|
);
|
||||||
.unwrap();
|
|
||||||
let (tx, rx) = irq_uart.split();
|
let (tx, rx) = irq_uart.split();
|
||||||
// Unwrap is okay, we explicitely set the interrupt ID.
|
// Unwrap is okay, we explicitely set the interrupt ID.
|
||||||
let mut rx = rx.into_rx_with_irq();
|
let mut rx = rx.into_rx_with_irq();
|
||||||
@ -180,8 +181,8 @@ mod app {
|
|||||||
{
|
{
|
||||||
Ok(result) => {
|
Ok(result) => {
|
||||||
if RX_DEBUGGING {
|
if RX_DEBUGGING {
|
||||||
defmt::debug!("RX Info: {:?}", cx.local.rx_context);
|
log::debug!("RX Info: {:?}", cx.local.rx_context);
|
||||||
defmt::debug!("RX Result: {:?}", result);
|
log::debug!("RX Result: {:?}", result);
|
||||||
}
|
}
|
||||||
if result.complete() {
|
if result.complete() {
|
||||||
// Check frame validity (must have COBS format) and decode the frame.
|
// Check frame validity (must have COBS format) and decode the frame.
|
||||||
@ -192,7 +193,7 @@ mod app {
|
|||||||
let decoded_size =
|
let decoded_size =
|
||||||
cobs::decode_in_place(&mut cx.local.rx_buf[1..result.bytes_read]);
|
cobs::decode_in_place(&mut cx.local.rx_buf[1..result.bytes_read]);
|
||||||
if decoded_size.is_err() {
|
if decoded_size.is_err() {
|
||||||
defmt::warn!("COBS decoding failed");
|
log::warn!("COBS decoding failed");
|
||||||
} else {
|
} else {
|
||||||
let decoded_size = decoded_size.unwrap();
|
let decoded_size = decoded_size.unwrap();
|
||||||
let mut tc_rb_full = false;
|
let mut tc_rb_full = false;
|
||||||
@ -206,13 +207,11 @@ mod app {
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
if tc_rb_full {
|
if tc_rb_full {
|
||||||
defmt::warn!("COBS TC queue full");
|
log::warn!("COBS TC queue full");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
defmt::warn!(
|
log::warn!("COBS frame with invalid format, start and end bytes are not 0");
|
||||||
"COBS frame with invalid format, start and end bytes are not 0"
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Initiate next transfer.
|
// Initiate next transfer.
|
||||||
@ -222,11 +221,11 @@ mod app {
|
|||||||
.expect("read operation failed");
|
.expect("read operation failed");
|
||||||
}
|
}
|
||||||
if result.has_errors() {
|
if result.has_errors() {
|
||||||
defmt::warn!("UART error: {:?}", result.errors.unwrap());
|
log::warn!("UART error: {:?}", result.errors.unwrap());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Err(e) => {
|
Err(e) => {
|
||||||
defmt::warn!("UART error: {:?}", e);
|
log::warn!("UART error: {:?}", e);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -253,7 +252,7 @@ mod app {
|
|||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
let packet_len = packet_len.unwrap();
|
let packet_len = packet_len.unwrap();
|
||||||
defmt::info!("received packet with length {}", packet_len);
|
log::info!(target: "TC Handler", "received packet with length {}", packet_len);
|
||||||
let popped_packet_len = cx
|
let popped_packet_len = cx
|
||||||
.shared
|
.shared
|
||||||
.tc_rb
|
.tc_rb
|
||||||
@ -267,7 +266,7 @@ mod app {
|
|||||||
fn handle_valid_pus_tc(cx: &mut pus_tc_handler::Context) {
|
fn handle_valid_pus_tc(cx: &mut pus_tc_handler::Context) {
|
||||||
let pus_tc = PusTcReader::new(cx.local.tc_buf);
|
let pus_tc = PusTcReader::new(cx.local.tc_buf);
|
||||||
if pus_tc.is_err() {
|
if pus_tc.is_err() {
|
||||||
defmt::warn!("PUS TC error: {}", pus_tc.unwrap_err());
|
log::warn!(target: "TC Handler", "PUS TC error: {}", pus_tc.unwrap_err());
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
let (pus_tc, _) = pus_tc.unwrap();
|
let (pus_tc, _) = pus_tc.unwrap();
|
||||||
@ -313,25 +312,22 @@ mod app {
|
|||||||
write_and_send(&tm);
|
write_and_send(&tm);
|
||||||
};
|
};
|
||||||
if pus_tc.subservice() == ActionId::CorruptImageA as u8 {
|
if pus_tc.subservice() == ActionId::CorruptImageA as u8 {
|
||||||
defmt::info!("corrupting App Image A");
|
rprintln!("corrupting App Image A");
|
||||||
corrupt_image(APP_A_START_ADDR);
|
corrupt_image(APP_A_START_ADDR);
|
||||||
}
|
}
|
||||||
if pus_tc.subservice() == ActionId::CorruptImageB as u8 {
|
if pus_tc.subservice() == ActionId::CorruptImageB as u8 {
|
||||||
defmt::info!("corrupting App Image B");
|
rprintln!("corrupting App Image B");
|
||||||
corrupt_image(APP_B_START_ADDR);
|
corrupt_image(APP_B_START_ADDR);
|
||||||
}
|
}
|
||||||
if pus_tc.subservice() == ActionId::SetBootSlot as u8 {
|
if pus_tc.subservice() == ActionId::SetBootSlot as u8 {
|
||||||
if pus_tc.app_data().is_empty() {
|
if pus_tc.app_data().is_empty() {
|
||||||
defmt::warn!("App data for preferred image command too short");
|
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]);
|
let app_sel_result = AppSel::try_from(pus_tc.app_data()[0]);
|
||||||
if app_sel_result.is_err() {
|
if app_sel_result.is_err() {
|
||||||
defmt::warn!("Invalid app selection value: {}", pus_tc.app_data()[0]);
|
log::warn!("Invalid app selection value: {}", pus_tc.app_data()[0]);
|
||||||
}
|
}
|
||||||
defmt::info!(
|
log::info!(target: "TC Handler", "received boot selection command with app select: {:?}", app_sel_result.unwrap());
|
||||||
"received boot selection command with app select: {:?}",
|
|
||||||
app_sel_result.unwrap()
|
|
||||||
);
|
|
||||||
cx.local
|
cx.local
|
||||||
.nvm
|
.nvm
|
||||||
.write(PREFERRED_SLOT_OFFSET as usize, &[pus_tc.app_data()[0]])
|
.write(PREFERRED_SLOT_OFFSET as usize, &[pus_tc.app_data()[0]])
|
||||||
@ -345,7 +341,7 @@ mod app {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
if pus_tc.service() == PusServiceId::Test as u8 && pus_tc.subservice() == 1 {
|
if pus_tc.service() == PusServiceId::Test as u8 && pus_tc.subservice() == 1 {
|
||||||
defmt::info!("received ping TC");
|
log::info!(target: "TC Handler", "received ping TC");
|
||||||
let tm = cx
|
let tm = cx
|
||||||
.local
|
.local
|
||||||
.verif_reporter
|
.verif_reporter
|
||||||
@ -370,21 +366,23 @@ mod app {
|
|||||||
if pus_tc.subservice() == 2 {
|
if pus_tc.subservice() == 2 {
|
||||||
let app_data = pus_tc.app_data();
|
let app_data = pus_tc.app_data();
|
||||||
if app_data.len() < 10 {
|
if app_data.len() < 10 {
|
||||||
defmt::warn!(
|
log::warn!(
|
||||||
|
target: "TC Handler",
|
||||||
"app data for raw memory write is too short: {}",
|
"app data for raw memory write is too short: {}",
|
||||||
app_data.len()
|
app_data.len()
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
let memory_id = app_data[0];
|
let memory_id = app_data[0];
|
||||||
if memory_id != BOOT_NVM_MEMORY_ID {
|
if memory_id != BOOT_NVM_MEMORY_ID {
|
||||||
defmt::warn!("memory ID {} not supported", memory_id);
|
log::warn!(target: "TC Handler", "memory ID {} not supported", memory_id);
|
||||||
// TODO: Error reporting
|
// TODO: Error reporting
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
let offset = u32::from_be_bytes(app_data[2..6].try_into().unwrap());
|
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());
|
let data_len = u32::from_be_bytes(app_data[6..10].try_into().unwrap());
|
||||||
if 10 + data_len as usize > app_data.len() {
|
if 10 + data_len as usize > app_data.len() {
|
||||||
defmt::warn!(
|
log::warn!(
|
||||||
|
target: "TC Handler",
|
||||||
"invalid data length {} for raw mem write detected",
|
"invalid data length {} for raw mem write detected",
|
||||||
data_len
|
data_len
|
||||||
);
|
);
|
||||||
@ -392,7 +390,12 @@ mod app {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
let data = &app_data[10..10 + data_len as usize];
|
let data = &app_data[10..10 + data_len as usize];
|
||||||
defmt::info!("writing {} bytes at offset {} to NVM", data_len, offset);
|
log::info!(
|
||||||
|
target: "TC Handler",
|
||||||
|
"writing {} bytes at offset {} to NVM",
|
||||||
|
data_len,
|
||||||
|
offset
|
||||||
|
);
|
||||||
cx.local
|
cx.local
|
||||||
.nvm
|
.nvm
|
||||||
.write(offset as usize, data)
|
.write(offset as usize, data)
|
||||||
@ -403,7 +406,7 @@ mod app {
|
|||||||
.verify(offset as usize, data)
|
.verify(offset as usize, data)
|
||||||
.expect("NVM verification failed")
|
.expect("NVM verification failed")
|
||||||
{
|
{
|
||||||
defmt::warn!("verification of data written to NVM failed");
|
log::warn!("verification of data written to NVM failed");
|
||||||
cx.local
|
cx.local
|
||||||
.verif_reporter
|
.verif_reporter
|
||||||
.completion_failure(
|
.completion_failure(
|
||||||
@ -421,7 +424,9 @@ mod app {
|
|||||||
.expect("completion success failed")
|
.expect("completion success failed")
|
||||||
};
|
};
|
||||||
write_and_send(&tm);
|
write_and_send(&tm);
|
||||||
defmt::info!("NVM operation done");
|
log::info!(
|
||||||
|
target: "TC Handler",
|
||||||
|
"NVM operation done");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -452,7 +457,7 @@ mod app {
|
|||||||
cx.local.encoded_buf[send_size + 1] = 0;
|
cx.local.encoded_buf[send_size + 1] = 0;
|
||||||
cx.local
|
cx.local
|
||||||
.uart_tx
|
.uart_tx
|
||||||
.write_all(&cx.local.encoded_buf[0..send_size + 2])
|
.write(&cx.local.encoded_buf[0..send_size + 2])
|
||||||
.unwrap();
|
.unwrap();
|
||||||
occupied_len -= 1;
|
occupied_len -= 1;
|
||||||
Mono::delay(2.millis()).await;
|
Mono::delay(2.millis()).await;
|
||||||
|
@ -1,3 +1,3 @@
|
|||||||
#!/bin/bash
|
#!/bin/bash
|
||||||
JLinkGDBServer -select USB -device VA10820 -endian little -if JTAG -speed auto \
|
JLinkGDBServer -select USB -device Cortex-M0 -endian little -if JTAG -speed auto \
|
||||||
-LocalhostOnly -jtagconf -1,-1
|
-LocalhostOnly
|
||||||
|
@ -1,18 +0,0 @@
|
|||||||
#!/bin/bash
|
|
||||||
|
|
||||||
# Check if binary path was provided
|
|
||||||
if [ "$#" -ne 1 ]; then
|
|
||||||
echo "Usage: $0 <path-to-binary>"
|
|
||||||
exit 1
|
|
||||||
fi
|
|
||||||
|
|
||||||
BINARY="$1"
|
|
||||||
|
|
||||||
# Check if file exists
|
|
||||||
if [ ! -f "$BINARY" ]; then
|
|
||||||
echo "Error: File '$BINARY' not found."
|
|
||||||
exit 1
|
|
||||||
fi
|
|
||||||
|
|
||||||
# Run the command
|
|
||||||
telnet localhost 19021 | defmt-print -e "$BINARY"
|
|
21524
sections/sec-debug.txt
Normal file
21524
sections/sec-debug.txt
Normal file
File diff suppressed because it is too large
Load Diff
6312
sections/sec-release-lto.txt
Normal file
6312
sections/sec-release-lto.txt
Normal file
File diff suppressed because it is too large
Load Diff
@ -8,10 +8,6 @@ and this project adheres to [Semantic Versioning](http://semver.org/).
|
|||||||
|
|
||||||
## [unreleased]
|
## [unreleased]
|
||||||
|
|
||||||
## [v0.2.1] 2025-03-07
|
|
||||||
|
|
||||||
- Bumped allowed va108xx-hal to v0.11
|
|
||||||
|
|
||||||
## [v0.2.0] 2025-02-17
|
## [v0.2.0] 2025-02-17
|
||||||
|
|
||||||
- Bumped va108xx-hal to v0.10.0
|
- Bumped va108xx-hal to v0.10.0
|
||||||
@ -25,7 +21,3 @@ Docs patch
|
|||||||
## [v0.1.0] 2025-02-13
|
## [v0.1.0] 2025-02-13
|
||||||
|
|
||||||
Initial release
|
Initial release
|
||||||
|
|
||||||
[unreleased]: https://egit.irs.uni-stuttgart.de/rust/va108xx-rs/compare/va108xx-embassy-v0.2.1...HEAD
|
|
||||||
[v0.2.1]: https://egit.irs.uni-stuttgart.de/rust/va108xx-rs/compare/va108xx-embassy-v0.2.0...va10xx-embassy-v0.2.1
|
|
||||||
[v0.2.0]: https://egit.irs.uni-stuttgart.de/rust/va108xx-rs/compare/va108xx-embassy-v0.1.2...va10xx-embassy-v0.2.0
|
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
[package]
|
[package]
|
||||||
name = "va108xx-embassy"
|
name = "va108xx-embassy"
|
||||||
version = "0.2.1"
|
version = "0.2.0"
|
||||||
edition = "2021"
|
edition = "2021"
|
||||||
authors = ["Robin Mueller <muellerr@irs.uni-stuttgart.de>"]
|
authors = ["Robin Mueller <muellerr@irs.uni-stuttgart.de>"]
|
||||||
description = "Embassy-rs support for the Vorago VA108xx family of microcontrollers"
|
description = "Embassy-rs support for the Vorago VA108xx family of microcontrollers"
|
||||||
@ -11,8 +11,21 @@ keywords = ["no-std", "hal", "cortex-m", "vorago", "va108xx"]
|
|||||||
categories = ["aerospace", "embedded", "no-std", "hardware-support"]
|
categories = ["aerospace", "embedded", "no-std", "hardware-support"]
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
vorago-shared-periphs = { git = "https://egit.irs.uni-stuttgart.de/rust/vorago-shared-periphs.git", features = ["vor1x"] }
|
critical-section = "1"
|
||||||
va108xx-hal = { path = "../va108xx-hal" }
|
|
||||||
|
embassy-sync = "0.6"
|
||||||
|
embassy-executor = "0.7"
|
||||||
|
embassy-time-driver = "0.2"
|
||||||
|
embassy-time-queue-utils = "0.1"
|
||||||
|
|
||||||
|
once_cell = { version = "1", default-features = false, features = ["critical-section"] }
|
||||||
|
|
||||||
|
va108xx-hal = { version = "0.11", path = "../va108xx-hal" }
|
||||||
|
|
||||||
|
[target.'cfg(all(target_arch = "arm", target_os = "none"))'.dependencies]
|
||||||
|
portable-atomic = { version = "1", features = ["unsafe-assume-single-core"] }
|
||||||
|
[target.'cfg(not(all(target_arch = "arm", target_os = "none")))'.dependencies]
|
||||||
|
portable-atomic = "1"
|
||||||
|
|
||||||
[features]
|
[features]
|
||||||
default = ["irq-oc30-oc31"]
|
default = ["irq-oc30-oc31"]
|
||||||
|
@ -32,12 +32,29 @@
|
|||||||
//! [embassy example projects](https://egit.irs.uni-stuttgart.de/rust/va108xx-rs/src/branch/main/examples/embassy)
|
//! [embassy example projects](https://egit.irs.uni-stuttgart.de/rust/va108xx-rs/src/branch/main/examples/embassy)
|
||||||
#![no_std]
|
#![no_std]
|
||||||
#![cfg_attr(docsrs, feature(doc_auto_cfg))]
|
#![cfg_attr(docsrs, feature(doc_auto_cfg))]
|
||||||
|
use core::cell::{Cell, RefCell};
|
||||||
|
use critical_section::{CriticalSection, Mutex};
|
||||||
|
use portable_atomic::{AtomicU32, Ordering};
|
||||||
|
|
||||||
|
use embassy_time_driver::{time_driver_impl, Driver, TICK_HZ};
|
||||||
|
use embassy_time_queue_utils::Queue;
|
||||||
|
use once_cell::sync::OnceCell;
|
||||||
#[cfg(feature = "irqs-in-lib")]
|
#[cfg(feature = "irqs-in-lib")]
|
||||||
use va108xx_hal::pac::{self, interrupt};
|
use va108xx_hal::pac::interrupt;
|
||||||
use va108xx_hal::time::Hertz;
|
use va108xx_hal::{
|
||||||
use va108xx_hal::timer::TimMarker;
|
clock::enable_peripheral_clock,
|
||||||
use vorago_shared_periphs::embassy::time_driver;
|
enable_nvic_interrupt, pac,
|
||||||
|
prelude::*,
|
||||||
|
timer::{enable_tim_clk, get_tim_raw, TimRegInterface, ValidTim},
|
||||||
|
PeripheralSelect,
|
||||||
|
};
|
||||||
|
|
||||||
|
time_driver_impl!(
|
||||||
|
static TIME_DRIVER: TimerDriver = TimerDriver {
|
||||||
|
periods: AtomicU32::new(0),
|
||||||
|
alarms: Mutex::new(AlarmState::new()),
|
||||||
|
queue: Mutex::new(RefCell::new(Queue::new())),
|
||||||
|
});
|
||||||
|
|
||||||
/// Macro to define the IRQ handlers for the time driver.
|
/// Macro to define the IRQ handlers for the time driver.
|
||||||
///
|
///
|
||||||
@ -82,28 +99,311 @@ embassy_time_driver_irqs!(timekeeper_irq = OC30, alarm_irq = OC29);
|
|||||||
#[cfg(feature = "irq-oc28-oc29")]
|
#[cfg(feature = "irq-oc28-oc29")]
|
||||||
embassy_time_driver_irqs!(timekeeper_irq = OC29, alarm_irq = OC28);
|
embassy_time_driver_irqs!(timekeeper_irq = OC29, alarm_irq = OC28);
|
||||||
|
|
||||||
|
/// Expose the time driver so the user can specify the IRQ handlers themselves.
|
||||||
|
pub fn time_driver() -> &'static TimerDriver {
|
||||||
|
&TIME_DRIVER
|
||||||
|
}
|
||||||
|
|
||||||
/// Initialization method for embassy.
|
/// Initialization method for embassy.
|
||||||
///
|
///
|
||||||
/// This should be used if the interrupt handler is provided by the library, which is the
|
/// This should be used if the interrupt handler is provided by the library, which is the
|
||||||
/// default case.
|
/// default case.
|
||||||
#[cfg(feature = "irqs-in-lib")]
|
#[cfg(feature = "irqs-in-lib")]
|
||||||
pub fn init<TimekeeperTim: TimMarker, AlarmTim: TimMarker>(
|
pub fn init<TimekeeperTim: TimRegInterface + ValidTim, AlarmTim: TimRegInterface + ValidTim>(
|
||||||
|
syscfg: &mut pac::Sysconfig,
|
||||||
|
irqsel: &pac::Irqsel,
|
||||||
|
sysclk: impl Into<Hertz>,
|
||||||
timekeeper_tim: TimekeeperTim,
|
timekeeper_tim: TimekeeperTim,
|
||||||
alarm_tim: AlarmTim,
|
alarm_tim: AlarmTim,
|
||||||
sysclk: Hertz,
|
|
||||||
) {
|
) {
|
||||||
time_driver().__init(sysclk, timekeeper_tim, alarm_tim, TIMEKEEPER_IRQ, ALARM_IRQ)
|
TIME_DRIVER.init(
|
||||||
|
syscfg,
|
||||||
|
irqsel,
|
||||||
|
sysclk,
|
||||||
|
timekeeper_tim,
|
||||||
|
alarm_tim,
|
||||||
|
TIMEKEEPER_IRQ,
|
||||||
|
ALARM_IRQ,
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Initialization method for embassy when using custom IRQ handlers.
|
/// Initialization method for embassy when using custom IRQ handlers.
|
||||||
///
|
///
|
||||||
/// Requires an explicit [pac::Interrupt] argument for the timekeeper and alarm IRQs.
|
/// Requires an explicit [pac::Interrupt] argument for the timekeeper and alarm IRQs.
|
||||||
pub fn init_with_custom_irqs<TimekeeperTim: TimMarker, AlarmTim: TimMarker>(
|
pub fn init_with_custom_irqs<
|
||||||
|
TimekeeperTim: TimRegInterface + ValidTim,
|
||||||
|
AlarmTim: TimRegInterface + ValidTim,
|
||||||
|
>(
|
||||||
|
syscfg: &mut pac::Sysconfig,
|
||||||
|
irqsel: &pac::Irqsel,
|
||||||
|
sysclk: impl Into<Hertz>,
|
||||||
timekeeper_tim: TimekeeperTim,
|
timekeeper_tim: TimekeeperTim,
|
||||||
alarm_tim: AlarmTim,
|
alarm_tim: AlarmTim,
|
||||||
sysclk: Hertz,
|
|
||||||
timekeeper_irq: pac::Interrupt,
|
timekeeper_irq: pac::Interrupt,
|
||||||
alarm_irq: pac::Interrupt,
|
alarm_irq: pac::Interrupt,
|
||||||
) {
|
) {
|
||||||
time_driver().__init(sysclk, timekeeper_tim, alarm_tim, timekeeper_irq, alarm_irq)
|
TIME_DRIVER.init(
|
||||||
|
syscfg,
|
||||||
|
irqsel,
|
||||||
|
sysclk,
|
||||||
|
timekeeper_tim,
|
||||||
|
alarm_tim,
|
||||||
|
timekeeper_irq,
|
||||||
|
alarm_irq,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
struct AlarmState {
|
||||||
|
timestamp: Cell<u64>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl AlarmState {
|
||||||
|
const fn new() -> Self {
|
||||||
|
Self {
|
||||||
|
timestamp: Cell::new(u64::MAX),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
unsafe impl Send for AlarmState {}
|
||||||
|
|
||||||
|
static SCALE: OnceCell<u64> = OnceCell::new();
|
||||||
|
static TIMEKEEPER_TIM: OnceCell<u8> = OnceCell::new();
|
||||||
|
static ALARM_TIM: OnceCell<u8> = OnceCell::new();
|
||||||
|
|
||||||
|
pub struct TimerDriver {
|
||||||
|
periods: AtomicU32,
|
||||||
|
/// Timestamp at which to fire alarm. u64::MAX if no alarm is scheduled.
|
||||||
|
alarms: Mutex<AlarmState>,
|
||||||
|
queue: Mutex<RefCell<Queue>>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl TimerDriver {
|
||||||
|
#[allow(clippy::too_many_arguments)]
|
||||||
|
fn init<TimekeeperTim: TimRegInterface + ValidTim, AlarmTim: TimRegInterface + ValidTim>(
|
||||||
|
&self,
|
||||||
|
syscfg: &mut pac::Sysconfig,
|
||||||
|
irqsel: &pac::Irqsel,
|
||||||
|
sysclk: impl Into<Hertz>,
|
||||||
|
timekeeper_tim: TimekeeperTim,
|
||||||
|
alarm_tim: AlarmTim,
|
||||||
|
timekeeper_irq: pac::Interrupt,
|
||||||
|
alarm_irq: pac::Interrupt,
|
||||||
|
) {
|
||||||
|
if ALARM_TIM.get().is_some() || TIMEKEEPER_TIM.get().is_some() {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
ALARM_TIM.set(AlarmTim::TIM_ID).ok();
|
||||||
|
TIMEKEEPER_TIM.set(TimekeeperTim::TIM_ID).ok();
|
||||||
|
enable_peripheral_clock(syscfg, PeripheralSelect::Irqsel);
|
||||||
|
enable_tim_clk(syscfg, timekeeper_tim.tim_id());
|
||||||
|
let timekeeper_reg_block = timekeeper_tim.reg_block();
|
||||||
|
let alarm_tim_reg_block = alarm_tim.reg_block();
|
||||||
|
let sysclk = sysclk.into();
|
||||||
|
// Initiate scale value here. This is required to convert timer ticks back to a timestamp.
|
||||||
|
SCALE.set((sysclk.raw() / TICK_HZ as u32) as u64).unwrap();
|
||||||
|
timekeeper_reg_block
|
||||||
|
.rst_value()
|
||||||
|
.write(|w| unsafe { w.bits(u32::MAX) });
|
||||||
|
// Decrementing counter.
|
||||||
|
timekeeper_reg_block
|
||||||
|
.cnt_value()
|
||||||
|
.write(|w| unsafe { w.bits(u32::MAX) });
|
||||||
|
// Switch on. Timekeeping should always be done.
|
||||||
|
irqsel
|
||||||
|
.tim0(timekeeper_tim.tim_id() as usize)
|
||||||
|
.write(|w| unsafe { w.bits(timekeeper_irq as u32) });
|
||||||
|
unsafe {
|
||||||
|
enable_nvic_interrupt(timekeeper_irq);
|
||||||
|
}
|
||||||
|
timekeeper_reg_block
|
||||||
|
.ctrl()
|
||||||
|
.modify(|_, w| w.irq_enb().set_bit());
|
||||||
|
timekeeper_reg_block
|
||||||
|
.enable()
|
||||||
|
.write(|w| unsafe { w.bits(1) });
|
||||||
|
|
||||||
|
enable_tim_clk(syscfg, alarm_tim.tim_id());
|
||||||
|
|
||||||
|
// Explicitely disable alarm timer until needed.
|
||||||
|
alarm_tim_reg_block.ctrl().modify(|_, w| {
|
||||||
|
w.irq_enb().clear_bit();
|
||||||
|
w.enable().clear_bit()
|
||||||
|
});
|
||||||
|
// Enable general interrupts. The IRQ enable of the peripheral remains cleared.
|
||||||
|
unsafe {
|
||||||
|
enable_nvic_interrupt(alarm_irq);
|
||||||
|
}
|
||||||
|
irqsel
|
||||||
|
.tim0(alarm_tim.tim_id() as usize)
|
||||||
|
.write(|w| unsafe { w.bits(alarm_irq as u32) });
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Should be called inside the IRQ of the timekeeper timer.
|
||||||
|
///
|
||||||
|
/// # Safety
|
||||||
|
///
|
||||||
|
/// This function has to be called once by the TIM IRQ used for the timekeeping.
|
||||||
|
pub unsafe fn on_interrupt_timekeeping(&self) {
|
||||||
|
self.next_period();
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Should be called inside the IRQ of the alarm timer.
|
||||||
|
///
|
||||||
|
/// # Safety
|
||||||
|
///
|
||||||
|
///This function has to be called once by the TIM IRQ used for the timekeeping.
|
||||||
|
pub unsafe fn on_interrupt_alarm(&self) {
|
||||||
|
critical_section::with(|cs| {
|
||||||
|
if self.alarms.borrow(cs).timestamp.get() <= self.now() {
|
||||||
|
self.trigger_alarm(cs)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
fn timekeeper_tim() -> &'static pac::tim0::RegisterBlock {
|
||||||
|
TIMEKEEPER_TIM
|
||||||
|
.get()
|
||||||
|
.map(|idx| unsafe { get_tim_raw(*idx as usize) })
|
||||||
|
.unwrap()
|
||||||
|
}
|
||||||
|
fn alarm_tim() -> &'static pac::tim0::RegisterBlock {
|
||||||
|
ALARM_TIM
|
||||||
|
.get()
|
||||||
|
.map(|idx| unsafe { get_tim_raw(*idx as usize) })
|
||||||
|
.unwrap()
|
||||||
|
}
|
||||||
|
|
||||||
|
fn next_period(&self) {
|
||||||
|
let period = self.periods.fetch_add(1, Ordering::AcqRel) + 1;
|
||||||
|
let t = (period as u64) << 32;
|
||||||
|
critical_section::with(|cs| {
|
||||||
|
let alarm = &self.alarms.borrow(cs);
|
||||||
|
let at = alarm.timestamp.get();
|
||||||
|
if at < t {
|
||||||
|
self.trigger_alarm(cs);
|
||||||
|
} else {
|
||||||
|
let alarm_tim = Self::alarm_tim();
|
||||||
|
|
||||||
|
let remaining_ticks = (at - t).checked_mul(*SCALE.get().unwrap());
|
||||||
|
if remaining_ticks.is_some_and(|v| v <= u32::MAX as u64) {
|
||||||
|
alarm_tim.enable().write(|w| unsafe { w.bits(0) });
|
||||||
|
alarm_tim
|
||||||
|
.cnt_value()
|
||||||
|
.write(|w| unsafe { w.bits(remaining_ticks.unwrap() as u32) });
|
||||||
|
alarm_tim.ctrl().modify(|_, w| w.irq_enb().set_bit());
|
||||||
|
alarm_tim.enable().write(|w| unsafe { w.bits(1) });
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
fn trigger_alarm(&self, cs: CriticalSection) {
|
||||||
|
Self::alarm_tim().ctrl().modify(|_, w| {
|
||||||
|
w.irq_enb().clear_bit();
|
||||||
|
w.enable().clear_bit()
|
||||||
|
});
|
||||||
|
|
||||||
|
let alarm = &self.alarms.borrow(cs);
|
||||||
|
// Setting the maximum value disables the alarm.
|
||||||
|
alarm.timestamp.set(u64::MAX);
|
||||||
|
|
||||||
|
// Call after clearing alarm, so the callback can set another alarm.
|
||||||
|
let mut next = self
|
||||||
|
.queue
|
||||||
|
.borrow(cs)
|
||||||
|
.borrow_mut()
|
||||||
|
.next_expiration(self.now());
|
||||||
|
while !self.set_alarm(cs, next) {
|
||||||
|
next = self
|
||||||
|
.queue
|
||||||
|
.borrow(cs)
|
||||||
|
.borrow_mut()
|
||||||
|
.next_expiration(self.now());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn set_alarm(&self, cs: CriticalSection, timestamp: u64) -> bool {
|
||||||
|
if SCALE.get().is_none() {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
let alarm_tim = Self::alarm_tim();
|
||||||
|
alarm_tim.ctrl().modify(|_, w| {
|
||||||
|
w.irq_enb().clear_bit();
|
||||||
|
w.enable().clear_bit()
|
||||||
|
});
|
||||||
|
|
||||||
|
let alarm = self.alarms.borrow(cs);
|
||||||
|
alarm.timestamp.set(timestamp);
|
||||||
|
|
||||||
|
let t = self.now();
|
||||||
|
if timestamp <= t {
|
||||||
|
alarm.timestamp.set(u64::MAX);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// If it hasn't triggered yet, setup the relevant reset value, regardless of whether
|
||||||
|
// the interrupts are enabled or not. When they are enabled at a later point, the
|
||||||
|
// right value is already set.
|
||||||
|
|
||||||
|
// If the timestamp is in the next few ticks, add a bit of buffer to be sure the alarm
|
||||||
|
// is not missed.
|
||||||
|
//
|
||||||
|
// This means that an alarm can be delayed for up to 2 ticks (from t+1 to t+3), but this is allowed
|
||||||
|
// by the Alarm trait contract. What's not allowed is triggering alarms *before* their scheduled time,
|
||||||
|
// and we don't do that here.
|
||||||
|
let safe_timestamp = timestamp.max(t + 3);
|
||||||
|
let timer_ticks = (safe_timestamp - t).checked_mul(*SCALE.get().unwrap());
|
||||||
|
alarm_tim.rst_value().write(|w| unsafe { w.bits(u32::MAX) });
|
||||||
|
if timer_ticks.is_some_and(|v| v <= u32::MAX as u64) {
|
||||||
|
alarm_tim
|
||||||
|
.cnt_value()
|
||||||
|
.write(|w| unsafe { w.bits(timer_ticks.unwrap() as u32) });
|
||||||
|
alarm_tim.ctrl().modify(|_, w| w.irq_enb().set_bit());
|
||||||
|
alarm_tim.enable().write(|w| unsafe { w.bits(1) });
|
||||||
|
}
|
||||||
|
// If it's too far in the future, don't enable timer yet.
|
||||||
|
// It will be enabled later by `next_period`.
|
||||||
|
|
||||||
|
true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Driver for TimerDriver {
|
||||||
|
fn now(&self) -> u64 {
|
||||||
|
if SCALE.get().is_none() {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
let mut period1: u32;
|
||||||
|
let mut period2: u32;
|
||||||
|
let mut counter_val: u32;
|
||||||
|
|
||||||
|
loop {
|
||||||
|
// Acquire ensures that we get the latest value of `periods` and
|
||||||
|
// no instructions can be reordered before the load.
|
||||||
|
period1 = self.periods.load(Ordering::Acquire);
|
||||||
|
|
||||||
|
counter_val = u32::MAX - Self::timekeeper_tim().cnt_value().read().bits();
|
||||||
|
|
||||||
|
// Double read to protect against race conditions when the counter is overflowing.
|
||||||
|
period2 = self.periods.load(Ordering::Relaxed);
|
||||||
|
if period1 == period2 {
|
||||||
|
let now = (((period1 as u64) << 32) | counter_val as u64) / *SCALE.get().unwrap();
|
||||||
|
return now;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn schedule_wake(&self, at: u64, waker: &core::task::Waker) {
|
||||||
|
critical_section::with(|cs| {
|
||||||
|
let mut queue = self.queue.borrow(cs).borrow_mut();
|
||||||
|
|
||||||
|
if queue.schedule_wake(at, waker) {
|
||||||
|
let mut next = queue.next_expiration(self.now());
|
||||||
|
while !self.set_alarm(cs, next) {
|
||||||
|
next = queue.next_expiration(self.now());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -8,28 +8,6 @@ and this project adheres to [Semantic Versioning](http://semver.org/).
|
|||||||
|
|
||||||
## [unreleased]
|
## [unreleased]
|
||||||
|
|
||||||
## Changed
|
|
||||||
|
|
||||||
- Move most library components to new [`vorago-shared-periphs`](https://egit.irs.uni-stuttgart.de/rust/vorago-shared-periphs)
|
|
||||||
which is mostly re-exported in this crate.
|
|
||||||
- Overhaul and simplification of several HAL APIs. The system configuration and IRQ router
|
|
||||||
peripheral instance generally does not need to be passed to HAL API anymore.
|
|
||||||
- All HAL drivers are now type erased. The constructors will still expect and consume the PAC
|
|
||||||
singleton component for resource management purposes, but are not cached anymore.
|
|
||||||
- Refactoring of GPIO library to be more inline with embassy GPIO API.
|
|
||||||
|
|
||||||
## Added
|
|
||||||
|
|
||||||
- I2C clock timeout feature support.
|
|
||||||
|
|
||||||
## [v0.11.1] 2025-03-10
|
|
||||||
|
|
||||||
## Fixed
|
|
||||||
|
|
||||||
- Fix `embedded_io` UART implementation to implement the documented contract properly.
|
|
||||||
The implementation will now block until at least one byte is available or can be written, unless
|
|
||||||
the send or receive buffer is empty.
|
|
||||||
|
|
||||||
## [v0.11.0] 2025-03-07
|
## [v0.11.0] 2025-03-07
|
||||||
|
|
||||||
## Changed
|
## Changed
|
||||||
@ -275,7 +253,6 @@ and this project adheres to [Semantic Versioning](http://semver.org/).
|
|||||||
- README with basic instructions how to set up own binary crate
|
- README with basic instructions how to set up own binary crate
|
||||||
|
|
||||||
[unreleased]: https://egit.irs.uni-stuttgart.de/rust/va108xx-rs/compare/va108xx-hal-v0.11.0...HEAD
|
[unreleased]: https://egit.irs.uni-stuttgart.de/rust/va108xx-rs/compare/va108xx-hal-v0.11.0...HEAD
|
||||||
[v0.11.1]: https://egit.irs.uni-stuttgart.de/rust/va108xx-rs/compare/va108xx-hal-v0.11.0...va108xx-hal-v0.11.1
|
|
||||||
[v0.11.0]: https://egit.irs.uni-stuttgart.de/rust/va108xx-rs/compare/va108xx-hal-v0.10.0...va108xx-hal-v0.11.0
|
[v0.11.0]: https://egit.irs.uni-stuttgart.de/rust/va108xx-rs/compare/va108xx-hal-v0.10.0...va108xx-hal-v0.11.0
|
||||||
[v0.10.0]: https://egit.irs.uni-stuttgart.de/rust/va108xx-rs/compare/va108xx-hal-v0.9.0...va108xx-hal-v0.10.0
|
[v0.10.0]: https://egit.irs.uni-stuttgart.de/rust/va108xx-rs/compare/va108xx-hal-v0.9.0...va108xx-hal-v0.10.0
|
||||||
[v0.9.0]: https://egit.irs.uni-stuttgart.de/rust/va108xx-rs/compare/va108xx-hal-v0.8.0...va108xx-hal-v0.9.0
|
[v0.9.0]: https://egit.irs.uni-stuttgart.de/rust/va108xx-rs/compare/va108xx-hal-v0.8.0...va108xx-hal-v0.9.0
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
[package]
|
[package]
|
||||||
name = "va108xx-hal"
|
name = "va108xx-hal"
|
||||||
version = "0.11.1"
|
version = "0.11.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"
|
||||||
@ -12,10 +12,26 @@ categories = ["aerospace", "embedded", "no-std", "hardware-support"]
|
|||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
cortex-m = { version = "0.7", features = ["critical-section-single-core"]}
|
cortex-m = { version = "0.7", features = ["critical-section-single-core"]}
|
||||||
vorago-shared-periphs = { git = "https://egit.irs.uni-stuttgart.de/rust/vorago-shared-periphs.git", features = ["vor1x"] }
|
cortex-m-rt = "0.7"
|
||||||
|
nb = "1"
|
||||||
|
paste = "1"
|
||||||
|
embedded-hal = "1"
|
||||||
|
embedded-hal-async = "1"
|
||||||
|
embedded-hal-nb = "1"
|
||||||
|
embedded-io = "0.6"
|
||||||
|
embedded-io-async = "0.6"
|
||||||
fugit = "0.3"
|
fugit = "0.3"
|
||||||
|
typenum = "1"
|
||||||
|
critical-section = "1"
|
||||||
|
delegate = ">=0.12, <=0.13"
|
||||||
|
heapless = "0.8"
|
||||||
|
static_cell = "2"
|
||||||
thiserror = { version = "2", default-features = false }
|
thiserror = { version = "2", default-features = false }
|
||||||
|
void = { version = "1", default-features = false }
|
||||||
|
once_cell = { version = "1", default-features = false }
|
||||||
va108xx = { version = "0.5", default-features = false, features = ["critical-section", "defmt"] }
|
va108xx = { version = "0.5", default-features = false, features = ["critical-section", "defmt"] }
|
||||||
|
embassy-sync = "0.6"
|
||||||
|
|
||||||
defmt = { version = "0.3", optional = true }
|
defmt = { version = "0.3", optional = true }
|
||||||
|
|
||||||
[target.'cfg(all(target_arch = "arm", target_os = "none"))'.dependencies]
|
[target.'cfg(all(target_arch = "arm", target_os = "none"))'.dependencies]
|
||||||
@ -26,11 +42,8 @@ portable-atomic = "1"
|
|||||||
[features]
|
[features]
|
||||||
default = ["rt"]
|
default = ["rt"]
|
||||||
rt = ["va108xx/rt"]
|
rt = ["va108xx/rt"]
|
||||||
defmt = ["dep:defmt", "vorago-shared-periphs/defmt"]
|
defmt = ["dep:defmt", "fugit/defmt", "embedded-hal/defmt-03"]
|
||||||
|
|
||||||
[package.metadata.docs.rs]
|
[package.metadata.docs.rs]
|
||||||
all-features = true
|
all-features = true
|
||||||
rustdoc-args = ["--generate-link-to-definition"]
|
rustdoc-args = ["--generate-link-to-definition"]
|
||||||
|
|
||||||
[package.metadata.cargo-machete]
|
|
||||||
ignored = ["cortex-m"]
|
|
||||||
|
@ -1,8 +1,41 @@
|
|||||||
//! # API for clock related functionality
|
//! # API for clock related functionality
|
||||||
//!
|
//!
|
||||||
//! This also includes functionality to enable the peripheral clocks
|
//! This also includes functionality to enable the peripheral clocks
|
||||||
pub use vorago_shared_periphs::gpio::FilterClkSel;
|
use crate::time::Hertz;
|
||||||
pub use vorago_shared_periphs::sysconfig::{disable_peripheral_clock, enable_peripheral_clock};
|
use crate::PeripheralSelect;
|
||||||
|
use cortex_m::interrupt::{self, Mutex};
|
||||||
|
use once_cell::unsync::OnceCell;
|
||||||
|
|
||||||
|
static SYS_CLOCK: Mutex<OnceCell<Hertz>> = Mutex::new(OnceCell::new());
|
||||||
|
|
||||||
|
pub type PeripheralClocks = PeripheralSelect;
|
||||||
|
|
||||||
|
#[derive(Debug, PartialEq, Eq)]
|
||||||
|
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
|
||||||
|
pub enum FilterClkSel {
|
||||||
|
SysClk = 0,
|
||||||
|
Clk1 = 1,
|
||||||
|
Clk2 = 2,
|
||||||
|
Clk3 = 3,
|
||||||
|
Clk4 = 4,
|
||||||
|
Clk5 = 5,
|
||||||
|
Clk6 = 6,
|
||||||
|
Clk7 = 7,
|
||||||
|
}
|
||||||
|
|
||||||
|
/// The Vorago in powered by an external clock which might have different frequencies.
|
||||||
|
/// The clock can be set here so it can be used by other software components as well.
|
||||||
|
/// The clock can be set exactly once
|
||||||
|
pub fn set_sys_clock(freq: impl Into<Hertz>) {
|
||||||
|
interrupt::free(|cs| {
|
||||||
|
SYS_CLOCK.borrow(cs).set(freq.into()).ok();
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Returns the configured system clock
|
||||||
|
pub fn get_sys_clock() -> Option<Hertz> {
|
||||||
|
interrupt::free(|cs| SYS_CLOCK.borrow(cs).get().copied())
|
||||||
|
}
|
||||||
|
|
||||||
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 {
|
||||||
@ -30,3 +63,17 @@ pub fn set_clk_div_register(syscfg: &mut va108xx::Sysconfig, clk_sel: FilterClkS
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
pub fn enable_peripheral_clock(syscfg: &mut va108xx::Sysconfig, clock: PeripheralClocks) {
|
||||||
|
syscfg
|
||||||
|
.peripheral_clk_enable()
|
||||||
|
.modify(|r, w| unsafe { w.bits(r.bits() | (1 << clock as u8)) });
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
pub fn disable_peripheral_clock(syscfg: &mut va108xx::Sysconfig, clock: PeripheralClocks) {
|
||||||
|
syscfg
|
||||||
|
.peripheral_clk_enable()
|
||||||
|
.modify(|r, w| unsafe { w.bits(r.bits() & !(1 << clock as u8)) });
|
||||||
|
}
|
||||||
|
366
va108xx-hal/src/gpio/asynch.rs
Normal file
366
va108xx-hal/src/gpio/asynch.rs
Normal file
@ -0,0 +1,366 @@
|
|||||||
|
//! # Async GPIO functionality for the VA108xx family.
|
||||||
|
//!
|
||||||
|
//! This module provides the [InputPinAsync] and [InputDynPinAsync] which both implement
|
||||||
|
//! the [embedded_hal_async::digital::Wait] trait. These types allow for asynchronous waiting
|
||||||
|
//! on GPIO pins. Please note that this module does not specify/declare the interrupt handlers
|
||||||
|
//! which must be provided for async support to work. However, it provides the
|
||||||
|
//! [on_interrupt_for_async_gpio_for_port] generic interrupt handler. This should be called in all
|
||||||
|
//! IRQ functions which handle any GPIO interrupts with the corresponding [Port] argument.
|
||||||
|
//!
|
||||||
|
//! # Example
|
||||||
|
//!
|
||||||
|
//! - [Async GPIO example](https://egit.irs.uni-stuttgart.de/rust/va108xx-rs/src/branch/main/examples/embassy/src/bin/async-gpio.rs)
|
||||||
|
use core::future::Future;
|
||||||
|
|
||||||
|
use embassy_sync::waitqueue::AtomicWaker;
|
||||||
|
use embedded_hal_async::digital::Wait;
|
||||||
|
use portable_atomic::AtomicBool;
|
||||||
|
use va108xx::{self as pac};
|
||||||
|
|
||||||
|
use crate::InterruptConfig;
|
||||||
|
|
||||||
|
use super::{
|
||||||
|
pin, DynPin, DynPinId, InputConfig, InterruptEdge, InvalidPinTypeError, Pin, PinId, Port,
|
||||||
|
NUM_PINS_PORT_A, NUM_PINS_PORT_B,
|
||||||
|
};
|
||||||
|
|
||||||
|
static WAKERS_FOR_PORT_A: [AtomicWaker; NUM_PINS_PORT_A] =
|
||||||
|
[const { AtomicWaker::new() }; NUM_PINS_PORT_A];
|
||||||
|
static WAKERS_FOR_PORT_B: [AtomicWaker; NUM_PINS_PORT_B] =
|
||||||
|
[const { AtomicWaker::new() }; NUM_PINS_PORT_B];
|
||||||
|
static EDGE_DETECTION_PORT_A: [AtomicBool; NUM_PINS_PORT_A] =
|
||||||
|
[const { AtomicBool::new(false) }; NUM_PINS_PORT_A];
|
||||||
|
static EDGE_DETECTION_PORT_B: [AtomicBool; NUM_PINS_PORT_B] =
|
||||||
|
[const { AtomicBool::new(false) }; NUM_PINS_PORT_B];
|
||||||
|
|
||||||
|
/// Generic interrupt handler for GPIO interrupts on a specific port to support async functionalities
|
||||||
|
///
|
||||||
|
/// This function should be called in all interrupt handlers which handle any GPIO interrupts
|
||||||
|
/// matching the [Port] argument.
|
||||||
|
/// The handler will wake the corresponding wakers for the pins that triggered an interrupts
|
||||||
|
/// as well as update the static edge detection structures. This allows the pin future tocomplete
|
||||||
|
/// complete async operations.
|
||||||
|
pub fn on_interrupt_for_async_gpio_for_port(port: Port) {
|
||||||
|
let periphs = unsafe { pac::Peripherals::steal() };
|
||||||
|
|
||||||
|
let (irq_enb, edge_status, wakers, edge_detection) = match port {
|
||||||
|
Port::A => (
|
||||||
|
periphs.porta.irq_enb().read().bits(),
|
||||||
|
periphs.porta.edge_status().read().bits(),
|
||||||
|
WAKERS_FOR_PORT_A.as_ref(),
|
||||||
|
EDGE_DETECTION_PORT_A.as_ref(),
|
||||||
|
),
|
||||||
|
Port::B => (
|
||||||
|
periphs.portb.irq_enb().read().bits(),
|
||||||
|
periphs.portb.edge_status().read().bits(),
|
||||||
|
WAKERS_FOR_PORT_B.as_ref(),
|
||||||
|
EDGE_DETECTION_PORT_B.as_ref(),
|
||||||
|
),
|
||||||
|
};
|
||||||
|
|
||||||
|
on_interrupt_for_port(irq_enb, edge_status, wakers, edge_detection);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
fn on_interrupt_for_port(
|
||||||
|
mut irq_enb: u32,
|
||||||
|
edge_status: u32,
|
||||||
|
wakers: &'static [AtomicWaker],
|
||||||
|
edge_detection: &'static [AtomicBool],
|
||||||
|
) {
|
||||||
|
while irq_enb != 0 {
|
||||||
|
let bit_pos = irq_enb.trailing_zeros() as usize;
|
||||||
|
let bit_mask = 1 << bit_pos;
|
||||||
|
|
||||||
|
wakers[bit_pos].wake();
|
||||||
|
|
||||||
|
if edge_status & bit_mask != 0 {
|
||||||
|
edge_detection[bit_pos].store(true, core::sync::atomic::Ordering::Relaxed);
|
||||||
|
|
||||||
|
// Clear the processed bit
|
||||||
|
irq_enb &= !bit_mask;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Input pin future which implements the [Future] trait.
|
||||||
|
///
|
||||||
|
/// Generally, you want to use the [InputPinAsync] or [InputDynPinAsync] types instead of this
|
||||||
|
/// which also implements the [embedded_hal_async::digital::Wait] trait. However, access to this
|
||||||
|
/// struture is granted to allow writing custom async structures.
|
||||||
|
pub struct InputPinFuture {
|
||||||
|
pin_id: DynPinId,
|
||||||
|
waker_group: &'static [AtomicWaker],
|
||||||
|
edge_detection_group: &'static [AtomicBool],
|
||||||
|
}
|
||||||
|
|
||||||
|
impl InputPinFuture {
|
||||||
|
#[inline]
|
||||||
|
pub fn pin_group_to_waker_and_edge_detection_group(
|
||||||
|
group: Port,
|
||||||
|
) -> (&'static [AtomicWaker], &'static [AtomicBool]) {
|
||||||
|
match group {
|
||||||
|
Port::A => (WAKERS_FOR_PORT_A.as_ref(), EDGE_DETECTION_PORT_A.as_ref()),
|
||||||
|
Port::B => (WAKERS_FOR_PORT_B.as_ref(), EDGE_DETECTION_PORT_B.as_ref()),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn new_with_dyn_pin(
|
||||||
|
pin: &mut DynPin,
|
||||||
|
irq: pac::Interrupt,
|
||||||
|
edge: InterruptEdge,
|
||||||
|
) -> Result<Self, InvalidPinTypeError> {
|
||||||
|
if !pin.is_input_pin() {
|
||||||
|
return Err(InvalidPinTypeError(pin.mode()));
|
||||||
|
}
|
||||||
|
|
||||||
|
let (waker_group, edge_detection_group) =
|
||||||
|
Self::pin_group_to_waker_and_edge_detection_group(pin.id().port());
|
||||||
|
edge_detection_group[pin.id().num() as usize]
|
||||||
|
.store(false, core::sync::atomic::Ordering::Relaxed);
|
||||||
|
pin.configure_edge_interrupt(edge).unwrap();
|
||||||
|
pin.enable_interrupt(InterruptConfig::new(irq, true, true));
|
||||||
|
Ok(Self {
|
||||||
|
pin_id: pin.id(),
|
||||||
|
waker_group,
|
||||||
|
edge_detection_group,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn new_with_pin<I: PinId, C: InputConfig>(
|
||||||
|
pin: &mut Pin<I, pin::Input<C>>,
|
||||||
|
irq: pac::Interrupt,
|
||||||
|
edge: InterruptEdge,
|
||||||
|
) -> Self {
|
||||||
|
let (waker_group, edge_detection_group) =
|
||||||
|
Self::pin_group_to_waker_and_edge_detection_group(pin.id().port());
|
||||||
|
edge_detection_group[pin.id().num() as usize]
|
||||||
|
.store(false, core::sync::atomic::Ordering::Relaxed);
|
||||||
|
pin.configure_edge_interrupt(edge);
|
||||||
|
pin.enable_interrupt(InterruptConfig::new(irq, true, true));
|
||||||
|
Self {
|
||||||
|
pin_id: pin.id(),
|
||||||
|
edge_detection_group,
|
||||||
|
waker_group,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Drop for InputPinFuture {
|
||||||
|
fn drop(&mut self) {
|
||||||
|
// The API ensures that we actually own the pin, so stealing it here is okay.
|
||||||
|
unsafe { DynPin::steal(self.pin_id) }.disable_interrupt(false);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Future for InputPinFuture {
|
||||||
|
type Output = ();
|
||||||
|
fn poll(
|
||||||
|
self: core::pin::Pin<&mut Self>,
|
||||||
|
cx: &mut core::task::Context<'_>,
|
||||||
|
) -> core::task::Poll<Self::Output> {
|
||||||
|
let idx = self.pin_id.num() as usize;
|
||||||
|
self.waker_group[idx].register(cx.waker());
|
||||||
|
if self.edge_detection_group[idx].swap(false, core::sync::atomic::Ordering::Relaxed) {
|
||||||
|
return core::task::Poll::Ready(());
|
||||||
|
}
|
||||||
|
core::task::Poll::Pending
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub struct InputDynPinAsync {
|
||||||
|
pin: DynPin,
|
||||||
|
irq: pac::Interrupt,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl InputDynPinAsync {
|
||||||
|
/// Create a new asynchronous input pin from a [DynPin]. The interrupt ID to be used must be
|
||||||
|
/// passed as well and is used to route and enable the interrupt.
|
||||||
|
///
|
||||||
|
/// Please note that the interrupt handler itself must be provided by the user and the
|
||||||
|
/// generic [on_interrupt_for_async_gpio_for_port] function must be called inside that function
|
||||||
|
/// for the asynchronous functionality to work.
|
||||||
|
pub fn new(pin: DynPin, irq: pac::Interrupt) -> Result<Self, InvalidPinTypeError> {
|
||||||
|
if !pin.is_input_pin() {
|
||||||
|
return Err(InvalidPinTypeError(pin.mode()));
|
||||||
|
}
|
||||||
|
Ok(Self { pin, irq })
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Asynchronously wait until the pin is high.
|
||||||
|
///
|
||||||
|
/// This returns immediately if the pin is already high.
|
||||||
|
pub async fn wait_for_high(&mut self) {
|
||||||
|
// Unwrap okay, checked pin in constructor.
|
||||||
|
let fut =
|
||||||
|
InputPinFuture::new_with_dyn_pin(&mut self.pin, self.irq, InterruptEdge::LowToHigh)
|
||||||
|
.unwrap();
|
||||||
|
if self.pin.is_high().unwrap() {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
fut.await;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Asynchronously wait until the pin is low.
|
||||||
|
///
|
||||||
|
/// This returns immediately if the pin is already high.
|
||||||
|
pub async fn wait_for_low(&mut self) {
|
||||||
|
// Unwrap okay, checked pin in constructor.
|
||||||
|
let fut =
|
||||||
|
InputPinFuture::new_with_dyn_pin(&mut self.pin, self.irq, InterruptEdge::HighToLow)
|
||||||
|
.unwrap();
|
||||||
|
if self.pin.is_low().unwrap() {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
fut.await;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Asynchronously wait until the pin sees a falling edge.
|
||||||
|
pub async fn wait_for_falling_edge(&mut self) {
|
||||||
|
// Unwrap okay, checked pin in constructor.
|
||||||
|
InputPinFuture::new_with_dyn_pin(&mut self.pin, self.irq, InterruptEdge::HighToLow)
|
||||||
|
.unwrap()
|
||||||
|
.await;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Asynchronously wait until the pin sees a rising edge.
|
||||||
|
pub async fn wait_for_rising_edge(&mut self) {
|
||||||
|
// Unwrap okay, checked pin in constructor.
|
||||||
|
InputPinFuture::new_with_dyn_pin(&mut self.pin, self.irq, InterruptEdge::LowToHigh)
|
||||||
|
.unwrap()
|
||||||
|
.await;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Asynchronously wait until the pin sees any edge (either rising or falling).
|
||||||
|
pub async fn wait_for_any_edge(&mut self) {
|
||||||
|
// Unwrap okay, checked pin in constructor.
|
||||||
|
InputPinFuture::new_with_dyn_pin(&mut self.pin, self.irq, InterruptEdge::BothEdges)
|
||||||
|
.unwrap()
|
||||||
|
.await;
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn release(self) -> DynPin {
|
||||||
|
self.pin
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl embedded_hal::digital::ErrorType for InputDynPinAsync {
|
||||||
|
type Error = core::convert::Infallible;
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Wait for InputDynPinAsync {
|
||||||
|
async fn wait_for_high(&mut self) -> Result<(), Self::Error> {
|
||||||
|
self.wait_for_high().await;
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
async fn wait_for_low(&mut self) -> Result<(), Self::Error> {
|
||||||
|
self.wait_for_low().await;
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
async fn wait_for_rising_edge(&mut self) -> Result<(), Self::Error> {
|
||||||
|
self.wait_for_rising_edge().await;
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
async fn wait_for_falling_edge(&mut self) -> Result<(), Self::Error> {
|
||||||
|
self.wait_for_falling_edge().await;
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
async fn wait_for_any_edge(&mut self) -> Result<(), Self::Error> {
|
||||||
|
self.wait_for_any_edge().await;
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub struct InputPinAsync<I: PinId, C: InputConfig> {
|
||||||
|
pin: Pin<I, pin::Input<C>>,
|
||||||
|
irq: pac::Interrupt,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<I: PinId, C: InputConfig> InputPinAsync<I, C> {
|
||||||
|
/// Create a new asynchronous input pin from a typed [Pin]. The interrupt ID to be used must be
|
||||||
|
/// passed as well and is used to route and enable the interrupt.
|
||||||
|
///
|
||||||
|
/// Please note that the interrupt handler itself must be provided by the user and the
|
||||||
|
/// generic [on_interrupt_for_async_gpio_for_port] function must be called inside that function
|
||||||
|
/// for the asynchronous functionality to work.
|
||||||
|
pub fn new(pin: Pin<I, pin::Input<C>>, irq: pac::Interrupt) -> Self {
|
||||||
|
Self { pin, irq }
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Asynchronously wait until the pin is high.
|
||||||
|
///
|
||||||
|
/// This returns immediately if the pin is already high.
|
||||||
|
pub async fn wait_for_high(&mut self) {
|
||||||
|
let fut = InputPinFuture::new_with_pin(&mut self.pin, self.irq, InterruptEdge::LowToHigh);
|
||||||
|
if self.pin.is_high() {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
fut.await;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Asynchronously wait until the pin is low.
|
||||||
|
///
|
||||||
|
/// This returns immediately if the pin is already high.
|
||||||
|
pub async fn wait_for_low(&mut self) {
|
||||||
|
let fut = InputPinFuture::new_with_pin(&mut self.pin, self.irq, InterruptEdge::HighToLow);
|
||||||
|
if self.pin.is_low() {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
fut.await;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Asynchronously wait until the pin sees falling edge.
|
||||||
|
pub async fn wait_for_falling_edge(&mut self) {
|
||||||
|
// Unwrap okay, checked pin in constructor.
|
||||||
|
InputPinFuture::new_with_pin(&mut self.pin, self.irq, InterruptEdge::HighToLow).await;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Asynchronously wait until the pin sees rising edge.
|
||||||
|
pub async fn wait_for_rising_edge(&mut self) {
|
||||||
|
// Unwrap okay, checked pin in constructor.
|
||||||
|
InputPinFuture::new_with_pin(&mut self.pin, self.irq, InterruptEdge::LowToHigh).await;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Asynchronously wait until the pin sees any edge (either rising or falling).
|
||||||
|
pub async fn wait_for_any_edge(&mut self) {
|
||||||
|
InputPinFuture::new_with_pin(&mut self.pin, self.irq, InterruptEdge::BothEdges).await;
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn release(self) -> Pin<I, pin::Input<C>> {
|
||||||
|
self.pin
|
||||||
|
}
|
||||||
|
}
|
||||||
|
impl<I: PinId, C: InputConfig> embedded_hal::digital::ErrorType for InputPinAsync<I, C> {
|
||||||
|
type Error = core::convert::Infallible;
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<I: PinId, C: InputConfig> Wait for InputPinAsync<I, C> {
|
||||||
|
async fn wait_for_high(&mut self) -> Result<(), Self::Error> {
|
||||||
|
self.wait_for_high().await;
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
async fn wait_for_low(&mut self) -> Result<(), Self::Error> {
|
||||||
|
self.wait_for_low().await;
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
async fn wait_for_rising_edge(&mut self) -> Result<(), Self::Error> {
|
||||||
|
self.wait_for_rising_edge().await;
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
async fn wait_for_falling_edge(&mut self) -> Result<(), Self::Error> {
|
||||||
|
self.wait_for_falling_edge().await;
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
async fn wait_for_any_edge(&mut self) -> Result<(), Self::Error> {
|
||||||
|
self.wait_for_any_edge().await;
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
}
|
947
va108xx-hal/src/gpio/dynpin.rs
Normal file
947
va108xx-hal/src/gpio/dynpin.rs
Normal file
@ -0,0 +1,947 @@
|
|||||||
|
//! # Type-erased, value-level module for GPIO pins
|
||||||
|
//!
|
||||||
|
//! Although the type-level API is generally preferred, it is not suitable in
|
||||||
|
//! all cases. Because each pin is represented by a distinct type, it is not
|
||||||
|
//! possible to store multiple pins in a homogeneous data structure. The
|
||||||
|
//! value-level API solves this problem by erasing the type information and
|
||||||
|
//! tracking the pin at run-time.
|
||||||
|
//!
|
||||||
|
//! Value-level pins are represented by the [`DynPin`] type. [`DynPin`] has two
|
||||||
|
//! fields, `id` and `mode` with types [`DynPinId`] and [`DynPinMode`]
|
||||||
|
//! respectively. The implementation of these types closely mirrors the
|
||||||
|
//! type-level API.
|
||||||
|
//!
|
||||||
|
//! Instances of [`DynPin`] cannot be created directly. Rather, they must be
|
||||||
|
//! created from their type-level equivalents using [`From`]/[`Into`].
|
||||||
|
//!
|
||||||
|
//! ```
|
||||||
|
//! // Move a pin out of the Pins struct and convert to a DynPin
|
||||||
|
//! let pa0: DynPin = pins.pa0.into();
|
||||||
|
//! ```
|
||||||
|
//!
|
||||||
|
//! Conversions between pin modes use a value-level version of the type-level
|
||||||
|
//! API.
|
||||||
|
//!
|
||||||
|
//! ```
|
||||||
|
//! // Use one of the literal function names
|
||||||
|
//! pa0.into_floating_input();
|
||||||
|
//! // Use a method and a DynPinMode variant
|
||||||
|
//! pa0.into_mode(DYN_FLOATING_INPUT);
|
||||||
|
//! ```
|
||||||
|
//!
|
||||||
|
//! Because the pin state cannot be tracked at compile-time, many [`DynPin`]
|
||||||
|
//! operations become fallible. Run-time checks are inserted to ensure that
|
||||||
|
//! users don't try to, for example, set the output level of an input pin.
|
||||||
|
//!
|
||||||
|
//! Users may try to convert value-level pins back to their type-level
|
||||||
|
//! equivalents. However, this option is fallible, because the compiler cannot
|
||||||
|
//! guarantee the pin has the correct ID or is in the correct mode at
|
||||||
|
//! compile-time. Use [TryFrom]/[TryInto] for this conversion.
|
||||||
|
//!
|
||||||
|
//! ```
|
||||||
|
//! // Convert to a `DynPin`
|
||||||
|
//! let pa0: DynPin = pins.pa0.into();
|
||||||
|
//! // Change pin mode
|
||||||
|
//! pa0.into_floating_input();
|
||||||
|
//! // Convert back to a `Pin`
|
||||||
|
//! let pa0: Pin<PA0, FloatingInput> = pa0.try_into().unwrap();
|
||||||
|
//! ```
|
||||||
|
//!
|
||||||
|
//! # Embedded HAL traits
|
||||||
|
//!
|
||||||
|
//! This module implements all of the embedded HAL GPIO traits for [`DynPin`].
|
||||||
|
//! However, whereas the type-level API uses
|
||||||
|
//! `Error = core::convert::Infallible`, the value-level API can return a real
|
||||||
|
//! error. If the [`DynPin`] is not in the correct [`DynPinMode`] for the
|
||||||
|
//! operation, the trait functions will return
|
||||||
|
//! [InvalidPinTypeError].
|
||||||
|
|
||||||
|
use super::{
|
||||||
|
pin::{FilterType, Pin, PinId, PinMode},
|
||||||
|
InputDynPinAsync, InterruptEdge, InterruptLevel, IsMaskedError, PinState, Port,
|
||||||
|
};
|
||||||
|
use crate::{clock::FilterClkSel, enable_nvic_interrupt, pac, FunSel};
|
||||||
|
|
||||||
|
//==================================================================================================
|
||||||
|
// DynPinMode configurations
|
||||||
|
//==================================================================================================
|
||||||
|
|
||||||
|
/// Value-level `enum` for disabled configurations
|
||||||
|
#[derive(PartialEq, Eq, Clone, Copy)]
|
||||||
|
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
|
||||||
|
pub enum DynDisabled {
|
||||||
|
Floating,
|
||||||
|
PullDown,
|
||||||
|
PullUp,
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Value-level `enum` for input configurations
|
||||||
|
#[derive(Debug, PartialEq, Eq, Clone, Copy)]
|
||||||
|
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
|
||||||
|
pub enum DynInput {
|
||||||
|
Floating,
|
||||||
|
PullDown,
|
||||||
|
PullUp,
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Value-level `enum` for output configurations
|
||||||
|
#[derive(Debug, PartialEq, Eq, Clone, Copy)]
|
||||||
|
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
|
||||||
|
pub enum DynOutput {
|
||||||
|
PushPull,
|
||||||
|
OpenDrain,
|
||||||
|
ReadablePushPull,
|
||||||
|
ReadableOpenDrain,
|
||||||
|
}
|
||||||
|
|
||||||
|
pub type DynAlternate = FunSel;
|
||||||
|
|
||||||
|
//==============================================================================
|
||||||
|
// Error
|
||||||
|
//==============================================================================
|
||||||
|
|
||||||
|
/// GPIO error type
|
||||||
|
///
|
||||||
|
/// [`DynPin`]s are not tracked and verified at compile-time, so run-time
|
||||||
|
/// operations are fallible. This `enum` represents the corresponding errors.
|
||||||
|
#[derive(Debug, PartialEq, Eq, thiserror::Error)]
|
||||||
|
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
|
||||||
|
#[error("Invalid pin type for operation: {0:?}")]
|
||||||
|
pub struct InvalidPinTypeError(pub DynPinMode);
|
||||||
|
|
||||||
|
impl embedded_hal::digital::Error for InvalidPinTypeError {
|
||||||
|
fn kind(&self) -> embedded_hal::digital::ErrorKind {
|
||||||
|
embedded_hal::digital::ErrorKind::Other
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
//==================================================================================================
|
||||||
|
// DynPinMode
|
||||||
|
//==================================================================================================
|
||||||
|
|
||||||
|
/// Value-level `enum` representing pin modes
|
||||||
|
#[derive(Debug, PartialEq, Eq, Clone, Copy)]
|
||||||
|
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
|
||||||
|
pub enum DynPinMode {
|
||||||
|
Input(DynInput),
|
||||||
|
Output(DynOutput),
|
||||||
|
Alternate(DynAlternate),
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Value-level variant of [`DynPinMode`] for floating input mode
|
||||||
|
pub const DYN_FLOATING_INPUT: DynPinMode = DynPinMode::Input(DynInput::Floating);
|
||||||
|
/// Value-level variant of [`DynPinMode`] for pull-down input mode
|
||||||
|
pub const DYN_PULL_DOWN_INPUT: DynPinMode = DynPinMode::Input(DynInput::PullDown);
|
||||||
|
/// Value-level variant of [`DynPinMode`] for pull-up input mode
|
||||||
|
pub const DYN_PULL_UP_INPUT: DynPinMode = DynPinMode::Input(DynInput::PullUp);
|
||||||
|
|
||||||
|
/// Value-level variant of [`DynPinMode`] for push-pull output mode
|
||||||
|
pub const DYN_PUSH_PULL_OUTPUT: DynPinMode = DynPinMode::Output(DynOutput::PushPull);
|
||||||
|
/// Value-level variant of [`DynPinMode`] for open-drain output mode
|
||||||
|
pub const DYN_OPEN_DRAIN_OUTPUT: DynPinMode = DynPinMode::Output(DynOutput::OpenDrain);
|
||||||
|
/// Value-level variant of [`DynPinMode`] for readable push-pull output mode
|
||||||
|
pub const DYN_RD_PUSH_PULL_OUTPUT: DynPinMode = DynPinMode::Output(DynOutput::ReadablePushPull);
|
||||||
|
/// Value-level variant of [`DynPinMode`] for readable opendrain output mode
|
||||||
|
pub const DYN_RD_OPEN_DRAIN_OUTPUT: DynPinMode = DynPinMode::Output(DynOutput::ReadableOpenDrain);
|
||||||
|
|
||||||
|
/// Value-level variant of [`DynPinMode`] for function select 1
|
||||||
|
pub const DYN_ALT_FUNC_1: DynPinMode = DynPinMode::Alternate(DynAlternate::Sel1);
|
||||||
|
/// Value-level variant of [`DynPinMode`] for function select 2
|
||||||
|
pub const DYN_ALT_FUNC_2: DynPinMode = DynPinMode::Alternate(DynAlternate::Sel2);
|
||||||
|
/// Value-level variant of [`DynPinMode`] for function select 3
|
||||||
|
pub const DYN_ALT_FUNC_3: DynPinMode = DynPinMode::Alternate(DynAlternate::Sel3);
|
||||||
|
|
||||||
|
//==================================================================================================
|
||||||
|
// DynGroup & DynPinId
|
||||||
|
//==================================================================================================
|
||||||
|
|
||||||
|
pub type DynGroup = Port;
|
||||||
|
|
||||||
|
#[derive(Debug, PartialEq, Eq, Clone, Copy)]
|
||||||
|
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
|
||||||
|
pub struct DynPinId {
|
||||||
|
port: Port,
|
||||||
|
num: u8,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl DynPinId {
|
||||||
|
pub const fn new(port: Port, num: u8) -> Self {
|
||||||
|
DynPinId { port, num }
|
||||||
|
}
|
||||||
|
|
||||||
|
pub const fn port(&self) -> Port {
|
||||||
|
self.port
|
||||||
|
}
|
||||||
|
pub const fn num(&self) -> u8 {
|
||||||
|
self.num
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
//==================================================================================================
|
||||||
|
// ModeFields
|
||||||
|
//==================================================================================================
|
||||||
|
|
||||||
|
/// Collect all fields needed to set the [`PinMode`](super::PinMode)
|
||||||
|
#[derive(Default)]
|
||||||
|
struct ModeFields {
|
||||||
|
dir: bool,
|
||||||
|
opendrn: bool,
|
||||||
|
pull_en: bool,
|
||||||
|
/// true for pullup, false for pulldown
|
||||||
|
pull_dir: bool,
|
||||||
|
funsel: u8,
|
||||||
|
enb_input: bool,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl From<DynPinMode> for ModeFields {
|
||||||
|
#[inline]
|
||||||
|
fn from(mode: DynPinMode) -> Self {
|
||||||
|
let mut fields = Self::default();
|
||||||
|
match mode {
|
||||||
|
DynPinMode::Input(config) => {
|
||||||
|
fields.dir = false;
|
||||||
|
fields.funsel = FunSel::Sel0 as u8;
|
||||||
|
match config {
|
||||||
|
DynInput::Floating => (),
|
||||||
|
DynInput::PullUp => {
|
||||||
|
fields.pull_en = true;
|
||||||
|
fields.pull_dir = true;
|
||||||
|
}
|
||||||
|
DynInput::PullDown => {
|
||||||
|
fields.pull_en = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
DynPinMode::Output(config) => {
|
||||||
|
fields.dir = true;
|
||||||
|
fields.funsel = FunSel::Sel0 as u8;
|
||||||
|
match config {
|
||||||
|
DynOutput::PushPull => (),
|
||||||
|
DynOutput::OpenDrain => {
|
||||||
|
fields.opendrn = true;
|
||||||
|
}
|
||||||
|
DynOutput::ReadableOpenDrain => {
|
||||||
|
fields.enb_input = true;
|
||||||
|
fields.opendrn = true;
|
||||||
|
}
|
||||||
|
DynOutput::ReadablePushPull => {
|
||||||
|
fields.enb_input = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
DynPinMode::Alternate(config) => {
|
||||||
|
fields.funsel = config as u8;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
fields
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Type definition to avoid confusion: These register blocks are identical
|
||||||
|
type PortRegisterBlock = pac::porta::RegisterBlock;
|
||||||
|
pub type PortReg = pac::ioconfig::Porta;
|
||||||
|
|
||||||
|
//==================================================================================================
|
||||||
|
// DynPin
|
||||||
|
//==================================================================================================
|
||||||
|
|
||||||
|
/// A value-level pin, parameterized by [`DynPinId`] and [`DynPinMode`]
|
||||||
|
///
|
||||||
|
/// 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.
|
||||||
|
#[derive(Debug)]
|
||||||
|
pub struct DynPin {
|
||||||
|
id: DynPinId,
|
||||||
|
mode: DynPinMode,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl DynPin {
|
||||||
|
/// Create a new [DynPin]
|
||||||
|
///
|
||||||
|
/// # Safety
|
||||||
|
///
|
||||||
|
/// Each [DynPin] must be a singleton. For a given [DynPinId], there
|
||||||
|
/// must be at most one corresponding [`DynPin`] in existence at any given
|
||||||
|
/// time. Violating this requirement is `unsafe`.
|
||||||
|
#[inline]
|
||||||
|
pub(crate) const unsafe fn new(id: DynPinId, mode: DynPinMode) -> Self {
|
||||||
|
DynPin { id, mode }
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Steals a new [DynPin].
|
||||||
|
///
|
||||||
|
/// This function will simply set the internal mode to [DYN_FLOATING_INPUT] pin without
|
||||||
|
/// modifying any registers related to the behaviour of the pin. The user should call
|
||||||
|
/// [Self::into_mode] to ensure the correct mode of the pin.
|
||||||
|
///
|
||||||
|
/// # Safety
|
||||||
|
///
|
||||||
|
/// Circumvents the HAL's safety guarantees. The caller must ensure that the pin is not
|
||||||
|
/// used cocurrently somewhere else. The caller might also want to call [Self::into_mode]
|
||||||
|
/// to ensure the correct desired state of the pin. It is recommended to create the pin using
|
||||||
|
/// [Pin::downgrade] instead.
|
||||||
|
pub const unsafe fn steal(id: DynPinId) -> Self {
|
||||||
|
DynPin {
|
||||||
|
id,
|
||||||
|
mode: DYN_FLOATING_INPUT,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Return a copy of the pin ID
|
||||||
|
#[inline]
|
||||||
|
pub const fn id(&self) -> DynPinId {
|
||||||
|
self.id
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Return a copy of the pin mode
|
||||||
|
#[inline]
|
||||||
|
pub const fn mode(&self) -> DynPinMode {
|
||||||
|
self.mode
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Convert the pin to the requested [`DynPinMode`]
|
||||||
|
#[inline]
|
||||||
|
pub fn into_mode(&mut self, mode: DynPinMode) {
|
||||||
|
self.change_mode(mode);
|
||||||
|
self.mode = mode;
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
pub fn is_input_pin(&self) -> bool {
|
||||||
|
matches!(self.mode, DynPinMode::Input(_))
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
pub fn is_output_pin(&self) -> bool {
|
||||||
|
matches!(self.mode, DynPinMode::Output(_))
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
pub fn into_funsel_1(&mut self) {
|
||||||
|
self.into_mode(DYN_ALT_FUNC_1);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
pub fn into_funsel_2(&mut self) {
|
||||||
|
self.into_mode(DYN_ALT_FUNC_2);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
pub fn into_funsel_3(&mut self) {
|
||||||
|
self.into_mode(DYN_ALT_FUNC_3);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Configure the pin to operate as a floating input
|
||||||
|
#[inline]
|
||||||
|
pub fn into_floating_input(&mut self) {
|
||||||
|
self.into_mode(DYN_FLOATING_INPUT);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Configure the pin to operate as a pulled down input
|
||||||
|
#[inline]
|
||||||
|
pub fn into_pull_down_input(&mut self) {
|
||||||
|
self.into_mode(DYN_PULL_DOWN_INPUT);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Configure the pin to operate as a pulled up input
|
||||||
|
#[inline]
|
||||||
|
pub fn into_pull_up_input(&mut self) {
|
||||||
|
self.into_mode(DYN_PULL_UP_INPUT);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Configure the pin to operate as a push-pull output
|
||||||
|
#[inline]
|
||||||
|
pub fn into_push_pull_output(&mut self) {
|
||||||
|
self.into_mode(DYN_PUSH_PULL_OUTPUT);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Configure the pin to operate as a push-pull output
|
||||||
|
#[inline]
|
||||||
|
pub fn into_open_drain_output(&mut self) {
|
||||||
|
self.into_mode(DYN_OPEN_DRAIN_OUTPUT);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Configure the pin to operate as a push-pull output
|
||||||
|
#[inline]
|
||||||
|
pub fn into_readable_push_pull_output(&mut self) {
|
||||||
|
self.into_mode(DYN_RD_PUSH_PULL_OUTPUT);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Configure the pin to operate as a push-pull output
|
||||||
|
#[inline]
|
||||||
|
pub fn into_readable_open_drain_output(&mut self) {
|
||||||
|
self.into_mode(DYN_RD_OPEN_DRAIN_OUTPUT);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline(always)]
|
||||||
|
pub fn is_low(&self) -> Result<bool, InvalidPinTypeError> {
|
||||||
|
self.read_internal().map(|v| !v)
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline(always)]
|
||||||
|
pub fn is_high(&self) -> Result<bool, InvalidPinTypeError> {
|
||||||
|
self.read_internal()
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline(always)]
|
||||||
|
pub fn set_low(&mut self) -> Result<(), InvalidPinTypeError> {
|
||||||
|
self.write_internal(false)
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline(always)]
|
||||||
|
pub fn set_high(&mut self) -> Result<(), InvalidPinTypeError> {
|
||||||
|
self.write_internal(true)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Toggle the logic level of an output pin
|
||||||
|
#[inline(always)]
|
||||||
|
pub fn toggle(&mut self) -> Result<(), InvalidPinTypeError> {
|
||||||
|
if !self.is_output_pin() {
|
||||||
|
return Err(InvalidPinTypeError(self.mode));
|
||||||
|
}
|
||||||
|
// Safety: TOGOUT is a "mask" register, and we only write the bit for
|
||||||
|
// this pin ID
|
||||||
|
unsafe { self.port_reg().togout().write(|w| w.bits(self.mask_32())) };
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn enable_interrupt(&mut self, irq_cfg: crate::InterruptConfig) {
|
||||||
|
if irq_cfg.route {
|
||||||
|
self.configure_irqsel(irq_cfg.id);
|
||||||
|
}
|
||||||
|
if irq_cfg.enable_in_nvic {
|
||||||
|
unsafe { enable_nvic_interrupt(irq_cfg.id) };
|
||||||
|
}
|
||||||
|
|
||||||
|
// We only manipulate our own bit.
|
||||||
|
self.port_reg()
|
||||||
|
.irq_enb()
|
||||||
|
.modify(|r, w| unsafe { w.bits(r.bits() | self.mask_32()) });
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn disable_interrupt(&mut self, reset_irqsel: bool) {
|
||||||
|
if reset_irqsel {
|
||||||
|
self.reset_irqsel();
|
||||||
|
}
|
||||||
|
// We only manipulate our own bit.
|
||||||
|
self.port_reg()
|
||||||
|
.irq_enb()
|
||||||
|
.modify(|r, w| unsafe { w.bits(r.bits() & !self.mask_32()) });
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Try to recreate a type-level [`Pin`] from a value-level [`DynPin`]
|
||||||
|
///
|
||||||
|
/// There is no way for the compiler to know if the conversion will be
|
||||||
|
/// successful at compile-time. We must verify the conversion at run-time
|
||||||
|
/// or refuse to perform it.
|
||||||
|
#[inline]
|
||||||
|
pub fn upgrade<I: PinId, M: PinMode>(self) -> Result<Pin<I, M>, InvalidPinTypeError> {
|
||||||
|
if self.id == I::DYN && self.mode == M::DYN {
|
||||||
|
// The `DynPin` is consumed, so it is safe to replace it with the
|
||||||
|
// corresponding `Pin`
|
||||||
|
return Ok(unsafe { Pin::new() });
|
||||||
|
}
|
||||||
|
Err(InvalidPinTypeError(self.mode))
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Convert the pin into an async pin. The pin can be converted back by calling
|
||||||
|
/// [InputDynPinAsync::release]
|
||||||
|
pub fn into_async_input(
|
||||||
|
self,
|
||||||
|
irq: crate::pac::Interrupt,
|
||||||
|
) -> Result<InputDynPinAsync, InvalidPinTypeError> {
|
||||||
|
InputDynPinAsync::new(self, irq)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Configure the IRQSEL peripheral for this particular pin with the given interrupt ID.
|
||||||
|
pub fn configure_irqsel(&mut self, id: pac::Interrupt) {
|
||||||
|
let mut syscfg = unsafe { pac::Sysconfig::steal() };
|
||||||
|
let irqsel = unsafe { pac::Irqsel::steal() };
|
||||||
|
crate::clock::enable_peripheral_clock(&mut syscfg, crate::clock::PeripheralClocks::Irqsel);
|
||||||
|
match self.id().port() {
|
||||||
|
// Set the correct interrupt number in the IRQSEL register
|
||||||
|
super::Port::A => {
|
||||||
|
irqsel
|
||||||
|
.porta0(self.id().num() as usize)
|
||||||
|
.write(|w| unsafe { w.bits(id as u32) });
|
||||||
|
}
|
||||||
|
super::Port::B => {
|
||||||
|
irqsel
|
||||||
|
.portb0(self.id().num as usize)
|
||||||
|
.write(|w| unsafe { w.bits(id as u32) });
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Reset the IRQSEL peripheral value for this particular pin.
|
||||||
|
pub fn reset_irqsel(&mut self) {
|
||||||
|
let mut syscfg = unsafe { pac::Sysconfig::steal() };
|
||||||
|
let irqsel = unsafe { pac::Irqsel::steal() };
|
||||||
|
crate::clock::enable_peripheral_clock(&mut syscfg, crate::clock::PeripheralClocks::Irqsel);
|
||||||
|
match self.id().port() {
|
||||||
|
// Set the correct interrupt number in the IRQSEL register
|
||||||
|
super::Port::A => {
|
||||||
|
irqsel
|
||||||
|
.porta0(self.id().num() as usize)
|
||||||
|
.write(|w| unsafe { w.bits(u32::MAX) });
|
||||||
|
}
|
||||||
|
super::Port::B => {
|
||||||
|
irqsel
|
||||||
|
.portb0(self.id().num as usize)
|
||||||
|
.write(|w| unsafe { w.bits(u32::MAX) });
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Get DATAMASK bit for this particular pin
|
||||||
|
#[inline(always)]
|
||||||
|
pub fn datamask(&self) -> bool {
|
||||||
|
(self.port_reg().datamask().read().bits() >> self.id().num) == 1
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Clear DATAMASK bit for this particular pin. This prevents access
|
||||||
|
/// of the corresponding bit for output and input operations
|
||||||
|
#[inline(always)]
|
||||||
|
pub fn clear_datamask(&self) {
|
||||||
|
self.port_reg()
|
||||||
|
.datamask()
|
||||||
|
.modify(|r, w| unsafe { w.bits(r.bits() & !self.mask_32()) });
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Set DATAMASK bit for this particular pin. 1 is the default
|
||||||
|
/// state of the bit and allows access of the corresponding bit
|
||||||
|
#[inline(always)]
|
||||||
|
pub fn set_datamask(&self) {
|
||||||
|
self.port_reg()
|
||||||
|
.datamask()
|
||||||
|
.modify(|r, w| unsafe { w.bits(r.bits() | self.mask_32()) });
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
pub fn is_high_masked(&self) -> Result<bool, crate::gpio::IsMaskedError> {
|
||||||
|
self.read_pin_masked()
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
pub fn is_low_masked(&self) -> Result<bool, crate::gpio::IsMaskedError> {
|
||||||
|
self.read_pin_masked().map(|v| !v)
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
pub fn set_high_masked(&mut self) -> Result<(), crate::gpio::IsMaskedError> {
|
||||||
|
self.write_pin_masked(true)
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
pub fn set_low_masked(&mut self) -> Result<(), crate::gpio::IsMaskedError> {
|
||||||
|
self.write_pin_masked(false)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Possible delays in clock cycles:
|
||||||
|
/// - Delay 1: 1
|
||||||
|
/// - Delay 2: 2
|
||||||
|
/// - Delay 1 + Delay 2: 3
|
||||||
|
#[inline]
|
||||||
|
pub fn configure_delay(
|
||||||
|
&mut self,
|
||||||
|
delay_1: bool,
|
||||||
|
delay_2: bool,
|
||||||
|
) -> Result<(), InvalidPinTypeError> {
|
||||||
|
match self.mode {
|
||||||
|
DynPinMode::Output(_) => {
|
||||||
|
self.configure_delay_internal(delay_1, delay_2);
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
_ => Err(InvalidPinTypeError(self.mode)),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// When configured for pulse mode, a given pin will set the non-default state for exactly
|
||||||
|
/// one clock cycle before returning to the configured default state
|
||||||
|
#[inline]
|
||||||
|
pub fn configure_pulse_mode(
|
||||||
|
&mut self,
|
||||||
|
enable: bool,
|
||||||
|
default_state: PinState,
|
||||||
|
) -> Result<(), InvalidPinTypeError> {
|
||||||
|
match self.mode {
|
||||||
|
DynPinMode::Output(_) => {
|
||||||
|
self.configure_pulse_mode_internal(enable, default_state);
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
_ => Err(InvalidPinTypeError(self.mode)),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// See p.37 and p.38 of the programmers guide for more information.
|
||||||
|
#[inline]
|
||||||
|
pub fn configure_filter_type(
|
||||||
|
&mut self,
|
||||||
|
filter: FilterType,
|
||||||
|
clksel: FilterClkSel,
|
||||||
|
) -> Result<(), InvalidPinTypeError> {
|
||||||
|
match self.mode {
|
||||||
|
DynPinMode::Input(_) => {
|
||||||
|
self.configure_filter_type_internal(filter, clksel);
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
_ => Err(InvalidPinTypeError(self.mode)),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn configure_edge_interrupt(
|
||||||
|
&mut self,
|
||||||
|
edge_type: InterruptEdge,
|
||||||
|
) -> Result<(), InvalidPinTypeError> {
|
||||||
|
match self.mode {
|
||||||
|
DynPinMode::Input(_) | DynPinMode::Output(_) => {
|
||||||
|
self.configure_edge_interrupt_internal(edge_type);
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
_ => Err(InvalidPinTypeError(self.mode)),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn configure_level_interrupt(
|
||||||
|
&mut self,
|
||||||
|
level_type: InterruptLevel,
|
||||||
|
) -> Result<(), InvalidPinTypeError> {
|
||||||
|
match self.mode {
|
||||||
|
DynPinMode::Input(_) | DynPinMode::Output(_) => {
|
||||||
|
self.configure_level_interrupt_internal(level_type);
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
_ => Err(InvalidPinTypeError(self.mode)),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Change the pin mode
|
||||||
|
#[inline]
|
||||||
|
pub(crate) fn change_mode(&mut self, mode: DynPinMode) {
|
||||||
|
let ModeFields {
|
||||||
|
dir,
|
||||||
|
funsel,
|
||||||
|
opendrn,
|
||||||
|
pull_dir,
|
||||||
|
pull_en,
|
||||||
|
enb_input,
|
||||||
|
} = mode.into();
|
||||||
|
let (portreg, iocfg) = (self.port_reg(), self.iocfg_port());
|
||||||
|
iocfg.write(|w| {
|
||||||
|
w.opendrn().bit(opendrn);
|
||||||
|
w.pen().bit(pull_en);
|
||||||
|
w.plevel().bit(pull_dir);
|
||||||
|
w.iewo().bit(enb_input);
|
||||||
|
unsafe { w.funsel().bits(funsel) }
|
||||||
|
});
|
||||||
|
let mask = self.mask_32();
|
||||||
|
unsafe {
|
||||||
|
if dir {
|
||||||
|
portreg.dir().modify(|r, w| w.bits(r.bits() | mask));
|
||||||
|
// Clear output
|
||||||
|
portreg.clrout().write(|w| w.bits(mask));
|
||||||
|
} else {
|
||||||
|
portreg.dir().modify(|r, w| w.bits(r.bits() & !mask));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
const fn port_reg(&self) -> &PortRegisterBlock {
|
||||||
|
match self.id().port() {
|
||||||
|
Port::A => unsafe { &(*pac::Porta::ptr()) },
|
||||||
|
Port::B => unsafe { &(*pac::Portb::ptr()) },
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
const fn iocfg_port(&self) -> &PortReg {
|
||||||
|
let ioconfig = unsafe { va108xx::Ioconfig::ptr().as_ref().unwrap() };
|
||||||
|
match self.id().port() {
|
||||||
|
Port::A => ioconfig.porta(self.id().num() as usize),
|
||||||
|
Port::B => ioconfig.portb0(self.id().num() as usize),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline(always)]
|
||||||
|
fn read_internal(&self) -> Result<bool, InvalidPinTypeError> {
|
||||||
|
match self.mode {
|
||||||
|
DynPinMode::Input(_) | DYN_RD_OPEN_DRAIN_OUTPUT | DYN_RD_PUSH_PULL_OUTPUT => {
|
||||||
|
Ok(self.read_pin())
|
||||||
|
}
|
||||||
|
_ => Err(InvalidPinTypeError(self.mode)),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline(always)]
|
||||||
|
fn write_internal(&mut self, bit: bool) -> Result<(), InvalidPinTypeError> {
|
||||||
|
match self.mode {
|
||||||
|
DynPinMode::Output(_) => {
|
||||||
|
self.write_pin(bit);
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
_ => Err(InvalidPinTypeError(self.mode)),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
/// Read the logic level of an output pin
|
||||||
|
pub(crate) fn read_pin(&self) -> bool {
|
||||||
|
let portreg = self.port_reg();
|
||||||
|
((portreg.datainraw().read().bits() >> self.id().num) & 0x01) == 1
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Read a pin but use the masked version but check whether the datamask for the pin is
|
||||||
|
/// cleared as well
|
||||||
|
#[inline(always)]
|
||||||
|
fn read_pin_masked(&self) -> Result<bool, IsMaskedError> {
|
||||||
|
if !self.datamask() {
|
||||||
|
Err(IsMaskedError)
|
||||||
|
} else {
|
||||||
|
Ok(((self.port_reg().datain().read().bits() >> self.id().num) & 0x01) == 1)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Write the logic level of an output pin
|
||||||
|
#[inline(always)]
|
||||||
|
pub(crate) fn write_pin(&mut self, bit: bool) {
|
||||||
|
// Safety: SETOUT is a "mask" register, and we only write the bit for
|
||||||
|
// this pin ID
|
||||||
|
unsafe {
|
||||||
|
if bit {
|
||||||
|
self.port_reg().setout().write(|w| w.bits(self.mask_32()));
|
||||||
|
} else {
|
||||||
|
self.port_reg().clrout().write(|w| w.bits(self.mask_32()));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Write the logic level of an output pin but check whether the datamask for the pin is
|
||||||
|
/// cleared as well
|
||||||
|
#[inline]
|
||||||
|
fn write_pin_masked(&mut self, bit: bool) -> Result<(), IsMaskedError> {
|
||||||
|
if !self.datamask() {
|
||||||
|
Err(IsMaskedError)
|
||||||
|
} else {
|
||||||
|
// Safety: SETOUT is a "mask" register, and we only write the bit for
|
||||||
|
// this pin ID
|
||||||
|
unsafe {
|
||||||
|
if bit {
|
||||||
|
self.port_reg().setout().write(|w| w.bits(self.mask_32()));
|
||||||
|
} else {
|
||||||
|
self.port_reg().clrout().write(|w| w.bits(self.mask_32()));
|
||||||
|
}
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Toggle the logic level of an output pin
|
||||||
|
#[inline(always)]
|
||||||
|
pub fn toggle_with_togout_reg(&mut self) {
|
||||||
|
// Safety: TOGOUT is a "mask" register, and we only write the bit for
|
||||||
|
// this pin ID
|
||||||
|
unsafe { self.port_reg().togout().write(|w| w.bits(self.mask_32())) };
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Only useful for interrupt pins. Configure whether to use edges or level as interrupt soure
|
||||||
|
/// When using edge mode, it is possible to generate interrupts on both edges as well
|
||||||
|
#[inline]
|
||||||
|
fn configure_edge_interrupt_internal(&mut self, edge_type: InterruptEdge) {
|
||||||
|
unsafe {
|
||||||
|
self.port_reg()
|
||||||
|
.irq_sen()
|
||||||
|
.modify(|r, w| w.bits(r.bits() & !self.mask_32()));
|
||||||
|
match edge_type {
|
||||||
|
InterruptEdge::HighToLow => {
|
||||||
|
self.port_reg()
|
||||||
|
.irq_evt()
|
||||||
|
.modify(|r, w| w.bits(r.bits() & !self.mask_32()));
|
||||||
|
}
|
||||||
|
InterruptEdge::LowToHigh => {
|
||||||
|
self.port_reg()
|
||||||
|
.irq_evt()
|
||||||
|
.modify(|r, w| w.bits(r.bits() | self.mask_32()));
|
||||||
|
}
|
||||||
|
InterruptEdge::BothEdges => {
|
||||||
|
self.port_reg()
|
||||||
|
.irq_edge()
|
||||||
|
.modify(|r, w| w.bits(r.bits() | self.mask_32()));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Configure which edge or level type triggers an interrupt
|
||||||
|
#[inline]
|
||||||
|
fn configure_level_interrupt_internal(&mut self, level: InterruptLevel) {
|
||||||
|
unsafe {
|
||||||
|
self.port_reg()
|
||||||
|
.irq_sen()
|
||||||
|
.modify(|r, w| w.bits(r.bits() | self.mask_32()));
|
||||||
|
if level == InterruptLevel::Low {
|
||||||
|
self.port_reg()
|
||||||
|
.irq_evt()
|
||||||
|
.modify(|r, w| w.bits(r.bits() & !self.mask_32()));
|
||||||
|
} else {
|
||||||
|
self.port_reg()
|
||||||
|
.irq_evt()
|
||||||
|
.modify(|r, w| w.bits(r.bits() | self.mask_32()));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Only useful for input pins
|
||||||
|
#[inline]
|
||||||
|
fn configure_filter_type_internal(&mut self, filter: FilterType, clksel: FilterClkSel) {
|
||||||
|
self.iocfg_port().modify(|_, w| {
|
||||||
|
// Safety: Only write to register for this Pin ID
|
||||||
|
unsafe {
|
||||||
|
w.flttype().bits(filter as u8);
|
||||||
|
w.fltclk().bits(clksel as u8)
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
fn configure_pulse_mode_internal(&mut self, enable: bool, default_state: PinState) {
|
||||||
|
let portreg = self.port_reg();
|
||||||
|
unsafe {
|
||||||
|
if enable {
|
||||||
|
portreg
|
||||||
|
.pulse()
|
||||||
|
.modify(|r, w| w.bits(r.bits() | self.mask_32()));
|
||||||
|
} else {
|
||||||
|
portreg
|
||||||
|
.pulse()
|
||||||
|
.modify(|r, w| w.bits(r.bits() & !self.mask_32()));
|
||||||
|
}
|
||||||
|
if default_state == PinState::Low {
|
||||||
|
portreg
|
||||||
|
.pulsebase()
|
||||||
|
.modify(|r, w| w.bits(r.bits() & !self.mask_32()));
|
||||||
|
} else {
|
||||||
|
portreg
|
||||||
|
.pulsebase()
|
||||||
|
.modify(|r, w| w.bits(r.bits() | self.mask_32()));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Only useful for output pins
|
||||||
|
#[inline]
|
||||||
|
fn configure_delay_internal(&mut self, delay_1: bool, delay_2: bool) {
|
||||||
|
let portreg = self.port_reg();
|
||||||
|
unsafe {
|
||||||
|
if delay_1 {
|
||||||
|
portreg
|
||||||
|
.delay1()
|
||||||
|
.modify(|r, w| w.bits(r.bits() | self.mask_32()));
|
||||||
|
} else {
|
||||||
|
portreg
|
||||||
|
.delay1()
|
||||||
|
.modify(|r, w| w.bits(r.bits() & !self.mask_32()));
|
||||||
|
}
|
||||||
|
if delay_2 {
|
||||||
|
portreg
|
||||||
|
.delay2()
|
||||||
|
.modify(|r, w| w.bits(r.bits() | self.mask_32()));
|
||||||
|
} else {
|
||||||
|
portreg
|
||||||
|
.delay2()
|
||||||
|
.modify(|r, w| w.bits(r.bits() & !self.mask_32()));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Only serves disambiguation purposes for the Embedded HAL impl
|
||||||
|
#[inline(always)]
|
||||||
|
fn is_low_mut(&mut self) -> Result<bool, InvalidPinTypeError> {
|
||||||
|
self.is_low()
|
||||||
|
}
|
||||||
|
|
||||||
|
// Only serves disambiguation purposes for the Embedded HAL impl
|
||||||
|
#[inline(always)]
|
||||||
|
fn is_high_mut(&mut self) -> Result<bool, InvalidPinTypeError> {
|
||||||
|
self.is_high()
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline(always)]
|
||||||
|
const fn mask_32(&self) -> u32 {
|
||||||
|
1 << self.id().num()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
//==================================================================================================
|
||||||
|
// Convert between Pin and DynPin
|
||||||
|
//==================================================================================================
|
||||||
|
|
||||||
|
impl<I: PinId, M: PinMode> From<Pin<I, M>> for DynPin {
|
||||||
|
/// Erase the type-level information in a [`Pin`] and return a value-level
|
||||||
|
/// [`DynPin`]
|
||||||
|
#[inline]
|
||||||
|
fn from(pin: Pin<I, M>) -> Self {
|
||||||
|
pin.downgrade()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<I: PinId, M: PinMode> TryFrom<DynPin> for Pin<I, M> {
|
||||||
|
type Error = InvalidPinTypeError;
|
||||||
|
|
||||||
|
/// 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]
|
||||||
|
fn try_from(pin: DynPin) -> Result<Self, Self::Error> {
|
||||||
|
pin.upgrade()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
//==================================================================================================
|
||||||
|
// Embedded HAL traits
|
||||||
|
//==================================================================================================
|
||||||
|
|
||||||
|
impl embedded_hal::digital::ErrorType for DynPin {
|
||||||
|
type Error = InvalidPinTypeError;
|
||||||
|
}
|
||||||
|
|
||||||
|
impl embedded_hal::digital::OutputPin for DynPin {
|
||||||
|
#[inline]
|
||||||
|
fn set_high(&mut self) -> Result<(), Self::Error> {
|
||||||
|
self.set_high()
|
||||||
|
}
|
||||||
|
#[inline]
|
||||||
|
fn set_low(&mut self) -> Result<(), Self::Error> {
|
||||||
|
self.set_low()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl embedded_hal::digital::InputPin for DynPin {
|
||||||
|
#[inline]
|
||||||
|
fn is_high(&mut self) -> Result<bool, Self::Error> {
|
||||||
|
self.is_high_mut()
|
||||||
|
}
|
||||||
|
#[inline]
|
||||||
|
fn is_low(&mut self) -> Result<bool, Self::Error> {
|
||||||
|
self.is_low_mut()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl embedded_hal::digital::StatefulOutputPin for DynPin {
|
||||||
|
#[inline]
|
||||||
|
fn is_set_high(&mut self) -> Result<bool, Self::Error> {
|
||||||
|
self.is_high_mut()
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
fn is_set_low(&mut self) -> Result<bool, Self::Error> {
|
||||||
|
self.is_low_mut()
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
fn toggle(&mut self) -> Result<(), Self::Error> {
|
||||||
|
self.toggle()
|
||||||
|
}
|
||||||
|
}
|
@ -1,20 +1,74 @@
|
|||||||
//! GPIO support module.
|
//! # API for the GPIO peripheral
|
||||||
//!
|
//!
|
||||||
//! Contains abstractions to use the pins provided by the [crate::pins] module as GPIO or
|
//! The implementation of this GPIO module is heavily based on the
|
||||||
//! IO peripheral pins.
|
//! [ATSAMD HAL implementation](https://docs.rs/atsamd-hal/latest/atsamd_hal/gpio/index.html).
|
||||||
//!
|
//!
|
||||||
//! The core data structures provided for this are the
|
//! This API provides two different submodules, [pin] and [dynpin],
|
||||||
|
//! representing two different ways to handle GPIO pins. The default, [pin],
|
||||||
|
//! is a type-level API that tracks the state of each pin at compile-time. The
|
||||||
|
//! alternative, [dynpin] is a type-erased, value-level API that tracks the
|
||||||
|
//! state of each pin at run-time.
|
||||||
//!
|
//!
|
||||||
//! - [Output] for push-pull output pins.
|
//! The type-level API is strongly preferred. By representing the state of each
|
||||||
//! - [Input] for input pins.
|
//! pin within the type system, the compiler can detect logic errors at
|
||||||
//! - [Flex] for pins with flexible configuration requirements.
|
//! compile-time. Furthermore, the type-level API has absolutely zero run-time
|
||||||
//! - [IoPeriphPin] for IO peripheral pins.
|
//! cost.
|
||||||
//!
|
//!
|
||||||
//! The [crate::pins] module exposes singletons to access the [Pin]s required by this module
|
//! If needed, [dynpin] can be used to erase the type-level differences
|
||||||
//! in a type-safe way.
|
//! between pins. However, by doing so, pins must now be tracked at run-time,
|
||||||
|
//! and each pin has a non-zero memory footprint.
|
||||||
//!
|
//!
|
||||||
//! ## Examples
|
//! ## Examples
|
||||||
//!
|
//!
|
||||||
//! - [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)
|
||||||
//! - [Async GPIO example](https://egit.irs.uni-stuttgart.de/rust/va108xx-rs/src/branch/main/examples/embassy/src/bin/async-gpio.rs)
|
|
||||||
pub use vorago_shared_periphs::gpio::*;
|
//==================================================================================================
|
||||||
|
// Errors, Definitions and Constants
|
||||||
|
//==================================================================================================
|
||||||
|
|
||||||
|
pub const NUM_PINS_PORT_A: usize = 32;
|
||||||
|
pub const NUM_PINS_PORT_B: usize = 24;
|
||||||
|
pub const NUM_GPIO_PINS: usize = NUM_PINS_PORT_A + NUM_PINS_PORT_B;
|
||||||
|
|
||||||
|
#[derive(Debug, PartialEq, Eq, thiserror::Error)]
|
||||||
|
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
|
||||||
|
#[error("The pin is masked")]
|
||||||
|
pub struct IsMaskedError;
|
||||||
|
|
||||||
|
#[derive(Debug, PartialEq, Eq, Clone, Copy)]
|
||||||
|
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
|
||||||
|
pub enum Port {
|
||||||
|
A,
|
||||||
|
B,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, PartialEq, Eq)]
|
||||||
|
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
|
||||||
|
pub enum InterruptEdge {
|
||||||
|
HighToLow,
|
||||||
|
LowToHigh,
|
||||||
|
BothEdges,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, PartialEq, Eq)]
|
||||||
|
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
|
||||||
|
pub enum InterruptLevel {
|
||||||
|
Low = 0,
|
||||||
|
High = 1,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, PartialEq, Eq)]
|
||||||
|
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
|
||||||
|
pub enum PinState {
|
||||||
|
Low = 0,
|
||||||
|
High = 1,
|
||||||
|
}
|
||||||
|
|
||||||
|
pub mod dynpin;
|
||||||
|
pub use dynpin::*;
|
||||||
|
|
||||||
|
pub mod pin;
|
||||||
|
pub use pin::*;
|
||||||
|
|
||||||
|
pub mod asynch;
|
||||||
|
pub use asynch::*;
|
||||||
|
823
va108xx-hal/src/gpio/pin.rs
Normal file
823
va108xx-hal/src/gpio/pin.rs
Normal file
@ -0,0 +1,823 @@
|
|||||||
|
//! # Type-level module for GPIO pins
|
||||||
|
//!
|
||||||
|
//! This documentation is strongly based on the
|
||||||
|
//! [atsamd documentation](https://docs.rs/atsamd-hal/latest/atsamd_hal/gpio/pin/index.html).
|
||||||
|
//!
|
||||||
|
//! This module provides a type-level API for GPIO pins. It uses the type system
|
||||||
|
//! to track the state of pins at compile-time. Representing GPIO pins in this
|
||||||
|
//! manner incurs no run-time overhead. Each [`Pin`] struct is zero-sized, so
|
||||||
|
//! there is no data to copy around. Instead, real code is generated as a side
|
||||||
|
//! effect of type transformations, and the resulting assembly is nearly
|
||||||
|
//! identical to the equivalent, hand-written C.
|
||||||
|
//!
|
||||||
|
//! To track the state of pins at compile-time, this module uses traits to
|
||||||
|
//! represent [type classes] and types as instances of those type classes. For
|
||||||
|
//! example, the trait [`InputConfig`] acts as a [type-level enum] of the
|
||||||
|
//! available input configurations, and the types [`Floating`], [`PullDown`] and
|
||||||
|
//! [`PullUp`] are its type-level variants.
|
||||||
|
//!
|
||||||
|
//! Type-level [`Pin`]s are parameterized by two type-level enums, [`PinId`] and
|
||||||
|
//! [`PinMode`].
|
||||||
|
//!
|
||||||
|
//! ```
|
||||||
|
//! pub struct Pin<I, M>
|
||||||
|
//! where
|
||||||
|
//! I: PinId,
|
||||||
|
//! M: PinMode,
|
||||||
|
//! {
|
||||||
|
//! // ...
|
||||||
|
//! }
|
||||||
|
//! ```
|
||||||
|
//!
|
||||||
|
//! A [PinId] identifies a pin by it's group (A or B) and pin number. Each
|
||||||
|
//! [PinId] instance is named according to its datasheet identifier, e.g.
|
||||||
|
//! [PA2].
|
||||||
|
//!
|
||||||
|
//! A [PinMode] represents the various pin modes. The available [PinMode]
|
||||||
|
//! variants are [`Input`], [`Output`] and [`Alternate`], each with its own
|
||||||
|
//! corresponding configurations.
|
||||||
|
//!
|
||||||
|
//! It is not possible for users to create new instances of a [`Pin`]. Singleton
|
||||||
|
//! instances of each pin are made available to users through the PinsX
|
||||||
|
//! struct.
|
||||||
|
//!
|
||||||
|
//! Example for the pins of PORT A:
|
||||||
|
//!
|
||||||
|
//! To create the [PinsA] struct, users must supply the PAC
|
||||||
|
//! [Port](crate::pac::Porta) peripheral. The [PinsA] struct takes
|
||||||
|
//! ownership of the [Porta] and provides the corresponding pins. Each [`Pin`]
|
||||||
|
//! within the [PinsA] struct can be moved out and used individually.
|
||||||
|
//!
|
||||||
|
//!
|
||||||
|
//! ```
|
||||||
|
//! let mut peripherals = Peripherals::take().unwrap();
|
||||||
|
//! let pinsa = PinsA::new(peripherals.PORT);
|
||||||
|
//! ```
|
||||||
|
//!
|
||||||
|
//! Pins can be converted between modes using several different methods.
|
||||||
|
//!
|
||||||
|
//! ```no_run
|
||||||
|
//! // Use one of the literal function names
|
||||||
|
//! let pa0 = pinsa.pa0.into_floating_input();
|
||||||
|
//! // Use a generic method and one of the `PinMode` variant types
|
||||||
|
//! let pa0 = pinsa.pa0.into_mode::<FloatingInput>();
|
||||||
|
//! // Specify the target type and use `From`/`Into`
|
||||||
|
//! let pa0: Pin<PA0, FloatingInput> = pinsa.pa27.into();
|
||||||
|
//! ```
|
||||||
|
//!
|
||||||
|
//! # Embedded HAL traits
|
||||||
|
//!
|
||||||
|
//! This module implements all of the embedded HAL GPIO traits for each [`Pin`]
|
||||||
|
//! in the corresponding [`PinMode`]s, namely: [embedded_hal::digital::InputPin],
|
||||||
|
//! [embedded_hal::digital::OutputPin] and [embedded_hal::digital::StatefulOutputPin].
|
||||||
|
use super::dynpin::{DynAlternate, DynInput, DynOutput, DynPinId, DynPinMode};
|
||||||
|
use super::{DynPin, InputPinAsync, InterruptEdge, InterruptLevel, PinState, Port};
|
||||||
|
use crate::{
|
||||||
|
pac::{Porta, Portb},
|
||||||
|
typelevel::Sealed,
|
||||||
|
};
|
||||||
|
use core::convert::Infallible;
|
||||||
|
use core::marker::PhantomData;
|
||||||
|
use core::mem::transmute;
|
||||||
|
use paste::paste;
|
||||||
|
|
||||||
|
//==================================================================================================
|
||||||
|
// Input configuration
|
||||||
|
//==================================================================================================
|
||||||
|
|
||||||
|
/// Type-level enum for input configurations
|
||||||
|
///
|
||||||
|
/// The valid options are [Floating], [PullDown] and [PullUp].
|
||||||
|
pub trait InputConfig: Sealed {
|
||||||
|
/// Corresponding [DynInput]
|
||||||
|
const DYN: DynInput;
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug)]
|
||||||
|
pub enum Floating {}
|
||||||
|
#[derive(Debug)]
|
||||||
|
pub enum PullDown {}
|
||||||
|
#[derive(Debug)]
|
||||||
|
pub enum PullUp {}
|
||||||
|
|
||||||
|
impl InputConfig for Floating {
|
||||||
|
const DYN: DynInput = DynInput::Floating;
|
||||||
|
}
|
||||||
|
impl InputConfig for PullDown {
|
||||||
|
const DYN: DynInput = DynInput::PullDown;
|
||||||
|
}
|
||||||
|
impl InputConfig for PullUp {
|
||||||
|
const DYN: DynInput = DynInput::PullUp;
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Sealed for Floating {}
|
||||||
|
impl Sealed for PullDown {}
|
||||||
|
impl Sealed for PullUp {}
|
||||||
|
|
||||||
|
/// Type-level variant of [`PinMode`] for floating input mode
|
||||||
|
pub type InputFloating = Input<Floating>;
|
||||||
|
/// Type-level variant of [`PinMode`] for pull-down input mode
|
||||||
|
pub type InputPullDown = Input<PullDown>;
|
||||||
|
/// Type-level variant of [`PinMode`] for pull-up input mode
|
||||||
|
pub type InputPullUp = Input<PullUp>;
|
||||||
|
|
||||||
|
/// Type-level variant of [`PinMode`] for input modes
|
||||||
|
///
|
||||||
|
/// Type `C` is one of three input configurations: [`Floating`], [`PullDown`] or
|
||||||
|
/// [`PullUp`]
|
||||||
|
#[derive(Debug)]
|
||||||
|
pub struct Input<C: InputConfig> {
|
||||||
|
cfg: PhantomData<C>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<C: InputConfig> Sealed for Input<C> {}
|
||||||
|
|
||||||
|
#[derive(Debug, PartialEq, Eq)]
|
||||||
|
pub enum FilterType {
|
||||||
|
SystemClock = 0,
|
||||||
|
DirectInputWithSynchronization = 1,
|
||||||
|
FilterOneClockCycle = 2,
|
||||||
|
FilterTwoClockCycles = 3,
|
||||||
|
FilterThreeClockCycles = 4,
|
||||||
|
FilterFourClockCycles = 5,
|
||||||
|
}
|
||||||
|
|
||||||
|
pub use crate::clock::FilterClkSel;
|
||||||
|
|
||||||
|
//==================================================================================================
|
||||||
|
// Output configuration
|
||||||
|
//==================================================================================================
|
||||||
|
|
||||||
|
pub trait OutputConfig: Sealed {
|
||||||
|
const DYN: DynOutput;
|
||||||
|
}
|
||||||
|
|
||||||
|
pub trait ReadableOutput: Sealed {}
|
||||||
|
|
||||||
|
/// Type-level variant of [`OutputConfig`] for a push-pull configuration
|
||||||
|
#[derive(Debug)]
|
||||||
|
pub enum PushPull {}
|
||||||
|
/// Type-level variant of [`OutputConfig`] for an open drain configuration
|
||||||
|
#[derive(Debug)]
|
||||||
|
pub enum OpenDrain {}
|
||||||
|
|
||||||
|
/// Type-level variant of [`OutputConfig`] for a readable push-pull configuration
|
||||||
|
#[derive(Debug)]
|
||||||
|
pub enum ReadablePushPull {}
|
||||||
|
/// Type-level variant of [`OutputConfig`] for a readable open-drain configuration
|
||||||
|
#[derive(Debug)]
|
||||||
|
pub enum ReadableOpenDrain {}
|
||||||
|
|
||||||
|
impl Sealed for PushPull {}
|
||||||
|
impl Sealed for OpenDrain {}
|
||||||
|
impl Sealed for ReadableOpenDrain {}
|
||||||
|
impl Sealed for ReadablePushPull {}
|
||||||
|
impl ReadableOutput for ReadableOpenDrain {}
|
||||||
|
impl ReadableOutput for ReadablePushPull {}
|
||||||
|
|
||||||
|
impl OutputConfig for PushPull {
|
||||||
|
const DYN: DynOutput = DynOutput::PushPull;
|
||||||
|
}
|
||||||
|
impl OutputConfig for OpenDrain {
|
||||||
|
const DYN: DynOutput = DynOutput::OpenDrain;
|
||||||
|
}
|
||||||
|
impl OutputConfig for ReadablePushPull {
|
||||||
|
const DYN: DynOutput = DynOutput::ReadablePushPull;
|
||||||
|
}
|
||||||
|
impl OutputConfig for ReadableOpenDrain {
|
||||||
|
const DYN: DynOutput = DynOutput::ReadableOpenDrain;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Type-level variant of [`PinMode`] for output modes
|
||||||
|
///
|
||||||
|
/// Type `C` is one of four output configurations: [`PushPull`], [`OpenDrain`] or
|
||||||
|
/// their respective readable versions
|
||||||
|
#[derive(Debug)]
|
||||||
|
pub struct Output<C: OutputConfig> {
|
||||||
|
cfg: PhantomData<C>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<C: OutputConfig> Sealed for Output<C> {}
|
||||||
|
|
||||||
|
/// Type-level variant of [`PinMode`] for push-pull output mode
|
||||||
|
pub type PushPullOutput = Output<PushPull>;
|
||||||
|
/// Type-level variant of [`PinMode`] for open drain output mode
|
||||||
|
pub type OutputOpenDrain = Output<OpenDrain>;
|
||||||
|
|
||||||
|
pub type OutputReadablePushPull = Output<ReadablePushPull>;
|
||||||
|
pub type OutputReadableOpenDrain = Output<ReadableOpenDrain>;
|
||||||
|
|
||||||
|
//==================================================================================================
|
||||||
|
// Alternate configurations
|
||||||
|
//==================================================================================================
|
||||||
|
|
||||||
|
/// Type-level enum for alternate peripheral function configurations
|
||||||
|
pub trait AlternateConfig: Sealed {
|
||||||
|
const DYN: DynAlternate;
|
||||||
|
}
|
||||||
|
|
||||||
|
pub enum Funsel1 {}
|
||||||
|
pub enum Funsel2 {}
|
||||||
|
pub enum Funsel3 {}
|
||||||
|
|
||||||
|
impl AlternateConfig for Funsel1 {
|
||||||
|
const DYN: DynAlternate = DynAlternate::Sel1;
|
||||||
|
}
|
||||||
|
impl AlternateConfig for Funsel2 {
|
||||||
|
const DYN: DynAlternate = DynAlternate::Sel2;
|
||||||
|
}
|
||||||
|
impl AlternateConfig for Funsel3 {
|
||||||
|
const DYN: DynAlternate = DynAlternate::Sel3;
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Sealed for Funsel1 {}
|
||||||
|
impl Sealed for Funsel2 {}
|
||||||
|
impl Sealed for Funsel3 {}
|
||||||
|
|
||||||
|
/// Type-level variant of [`PinMode`] for alternate peripheral functions
|
||||||
|
///
|
||||||
|
/// Type `C` is an [`AlternateConfig`]
|
||||||
|
pub struct Alternate<C: AlternateConfig> {
|
||||||
|
cfg: PhantomData<C>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<C: AlternateConfig> Sealed for Alternate<C> {}
|
||||||
|
|
||||||
|
pub type AltFunc1 = Alternate<Funsel1>;
|
||||||
|
pub type AltFunc2 = Alternate<Funsel2>;
|
||||||
|
pub type AltFunc3 = Alternate<Funsel3>;
|
||||||
|
|
||||||
|
/// Type alias for the [`PinMode`] at reset
|
||||||
|
pub type Reset = InputFloating;
|
||||||
|
|
||||||
|
//==================================================================================================
|
||||||
|
// Pin modes
|
||||||
|
//==================================================================================================
|
||||||
|
|
||||||
|
/// Type-level enum representing pin modes
|
||||||
|
///
|
||||||
|
/// The valid options are [Input], [Output] and [Alternate].
|
||||||
|
pub trait PinMode: Sealed {
|
||||||
|
/// Corresponding [DynPinMode]
|
||||||
|
const DYN: DynPinMode;
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<C: InputConfig> PinMode for Input<C> {
|
||||||
|
const DYN: DynPinMode = DynPinMode::Input(C::DYN);
|
||||||
|
}
|
||||||
|
impl<C: OutputConfig> PinMode for Output<C> {
|
||||||
|
const DYN: DynPinMode = DynPinMode::Output(C::DYN);
|
||||||
|
}
|
||||||
|
impl<C: AlternateConfig> PinMode for Alternate<C> {
|
||||||
|
const DYN: DynPinMode = DynPinMode::Alternate(C::DYN);
|
||||||
|
}
|
||||||
|
|
||||||
|
//==================================================================================================
|
||||||
|
// Pin IDs
|
||||||
|
//==================================================================================================
|
||||||
|
|
||||||
|
/// Type-level enum for pin IDs
|
||||||
|
pub trait PinId: Sealed {
|
||||||
|
/// Corresponding [DynPinId]
|
||||||
|
const DYN: DynPinId;
|
||||||
|
}
|
||||||
|
|
||||||
|
macro_rules! pin_id {
|
||||||
|
($Group:ident, $Id:ident, $NUM:literal) => {
|
||||||
|
// Need paste macro to use ident in doc attribute
|
||||||
|
paste! {
|
||||||
|
#[doc = "Pin ID representing pin " $Id]
|
||||||
|
#[derive(Debug)]
|
||||||
|
pub enum $Id {}
|
||||||
|
impl Sealed for $Id {}
|
||||||
|
impl PinId for $Id {
|
||||||
|
const DYN: DynPinId = DynPinId::new(Port::$Group, $NUM);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
//==================================================================================================
|
||||||
|
// Pin
|
||||||
|
//==================================================================================================
|
||||||
|
|
||||||
|
/// A type-level GPIO pin, parameterized by [PinId] and [PinMode] types
|
||||||
|
#[derive(Debug)]
|
||||||
|
pub struct Pin<I: PinId, M: PinMode> {
|
||||||
|
inner: DynPin,
|
||||||
|
phantom: PhantomData<(I, M)>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<I: PinId, M: PinMode> Pin<I, M> {
|
||||||
|
/// Create a new [Pin]
|
||||||
|
///
|
||||||
|
/// # Safety
|
||||||
|
///
|
||||||
|
/// Each [Pin] must be a singleton. For a given [PinId], there must be
|
||||||
|
/// at most one corresponding [Pin] in existence at any given time.
|
||||||
|
/// Violating this requirement is `unsafe`.
|
||||||
|
#[inline]
|
||||||
|
pub(crate) const unsafe fn new() -> Pin<I, M> {
|
||||||
|
Pin {
|
||||||
|
inner: DynPin::new(I::DYN, M::DYN),
|
||||||
|
phantom: PhantomData,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
pub const fn id(&self) -> DynPinId {
|
||||||
|
self.inner.id()
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Convert the pin to the requested [`PinMode`]
|
||||||
|
#[inline]
|
||||||
|
pub fn into_mode<N: PinMode>(mut self) -> Pin<I, N> {
|
||||||
|
// Only modify registers if we are actually changing pin mode
|
||||||
|
// This check should compile away
|
||||||
|
if N::DYN != M::DYN {
|
||||||
|
self.inner.change_mode(N::DYN);
|
||||||
|
}
|
||||||
|
// Safe because we drop the existing Pin
|
||||||
|
unsafe { Pin::new() }
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Configure the pin for function select 1. See Programmer Guide p.40 for the function table
|
||||||
|
#[inline]
|
||||||
|
pub fn into_funsel_1(self) -> Pin<I, AltFunc1> {
|
||||||
|
self.into_mode()
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Configure the pin for function select 2. See Programmer Guide p.40 for the function table
|
||||||
|
#[inline]
|
||||||
|
pub fn into_funsel_2(self) -> Pin<I, AltFunc2> {
|
||||||
|
self.into_mode()
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Configure the pin for function select 3. See Programmer Guide p.40 for the function table
|
||||||
|
#[inline]
|
||||||
|
pub fn into_funsel_3(self) -> Pin<I, AltFunc3> {
|
||||||
|
self.into_mode()
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Configure the pin to operate as a floating input
|
||||||
|
#[inline]
|
||||||
|
pub fn into_floating_input(self) -> Pin<I, InputFloating> {
|
||||||
|
self.into_mode()
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Configure the pin to operate as a pulled down input
|
||||||
|
#[inline]
|
||||||
|
pub fn into_pull_down_input(self) -> Pin<I, InputPullDown> {
|
||||||
|
self.into_mode()
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Configure the pin to operate as a pulled up input
|
||||||
|
#[inline]
|
||||||
|
pub fn into_pull_up_input(self) -> Pin<I, InputPullUp> {
|
||||||
|
self.into_mode()
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Configure the pin to operate as a push-pull output
|
||||||
|
#[inline]
|
||||||
|
pub fn into_push_pull_output(self) -> Pin<I, PushPullOutput> {
|
||||||
|
self.into_mode()
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Configure the pin to operate as a readable push-pull output
|
||||||
|
#[inline]
|
||||||
|
pub fn into_readable_push_pull_output(self) -> Pin<I, OutputReadablePushPull> {
|
||||||
|
self.into_mode()
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Configure the pin to operate as a readable open-drain output
|
||||||
|
#[inline]
|
||||||
|
pub fn into_readable_open_drain_output(self) -> Pin<I, OutputReadableOpenDrain> {
|
||||||
|
self.into_mode()
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
pub fn is_low(&self) -> bool {
|
||||||
|
!self.inner.read_pin()
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
pub fn is_high(&self) -> bool {
|
||||||
|
self.inner.read_pin()
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
pub fn datamask(&self) -> bool {
|
||||||
|
self.inner.datamask()
|
||||||
|
}
|
||||||
|
|
||||||
|
#[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 downgrade(self) -> DynPin {
|
||||||
|
self.inner
|
||||||
|
}
|
||||||
|
|
||||||
|
// Those only serve for the embedded HAL implementations which have different mutability.
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
fn is_low_mut(&mut self) -> bool {
|
||||||
|
self.is_low()
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
fn is_high_mut(&mut self) -> bool {
|
||||||
|
self.is_high()
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
pub fn enable_interrupt(&mut self, irq_cfg: crate::InterruptConfig) {
|
||||||
|
self.inner.enable_interrupt(irq_cfg);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
pub fn disable_interrupt(&mut self, reset_irqsel: bool) {
|
||||||
|
self.inner.disable_interrupt(reset_irqsel);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Configure the pin for an edge interrupt but does not enable the interrupt.
|
||||||
|
pub fn configure_edge_interrupt(&mut self, edge_type: InterruptEdge) {
|
||||||
|
self.inner.configure_edge_interrupt(edge_type).unwrap();
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Configure the pin for a level interrupt but does not enable the interrupt.
|
||||||
|
pub fn configure_level_interrupt(&mut self, level_type: InterruptLevel) {
|
||||||
|
self.inner.configure_level_interrupt(level_type).unwrap();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
//==============================================================================
|
||||||
|
// AnyPin
|
||||||
|
//==============================================================================
|
||||||
|
|
||||||
|
/// Type class for [`Pin`] types
|
||||||
|
///
|
||||||
|
/// This trait uses the [`AnyKind`] trait pattern to create a [type class] for
|
||||||
|
/// [`Pin`] types. See the `AnyKind` documentation for more details on the
|
||||||
|
/// pattern.
|
||||||
|
///
|
||||||
|
/// ## `v1` Compatibility
|
||||||
|
///
|
||||||
|
/// Normally, this trait would use `Is<Type = SpecificPin<Self>>` as a super
|
||||||
|
/// trait. But doing so would restrict implementations to only the `v2` `Pin`
|
||||||
|
/// type in this module. To aid in backwards compatibility, we want to implement
|
||||||
|
/// `AnyPin` for the `v1` `Pin` type as well. This is possible for a few
|
||||||
|
/// reasons. First, both structs are zero-sized, so there is no meaningful
|
||||||
|
/// memory layout to begin with. And even if there were, the `v1` `Pin` type is
|
||||||
|
/// a newtype wrapper around a `v2` `Pin`, and single-field structs are
|
||||||
|
/// guaranteed to have the same layout as the field, even for `repr(Rust)`.
|
||||||
|
///
|
||||||
|
/// [`AnyKind`]: crate::typelevel#anykind-trait-pattern
|
||||||
|
/// [type class]: crate::typelevel#type-classes
|
||||||
|
pub trait AnyPin
|
||||||
|
where
|
||||||
|
Self: Sealed,
|
||||||
|
Self: From<SpecificPin<Self>>,
|
||||||
|
Self: Into<SpecificPin<Self>>,
|
||||||
|
Self: AsRef<SpecificPin<Self>>,
|
||||||
|
Self: AsMut<SpecificPin<Self>>,
|
||||||
|
{
|
||||||
|
/// [`PinId`] of the corresponding [`Pin`]
|
||||||
|
type Id: PinId;
|
||||||
|
/// [`PinMode`] of the corresponding [`Pin`]
|
||||||
|
type Mode: PinMode;
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<I, M> Sealed for Pin<I, M>
|
||||||
|
where
|
||||||
|
I: PinId,
|
||||||
|
M: PinMode,
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<I, M> AnyPin for Pin<I, M>
|
||||||
|
where
|
||||||
|
I: PinId,
|
||||||
|
M: PinMode,
|
||||||
|
{
|
||||||
|
type Id = I;
|
||||||
|
type Mode = M;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Type alias to recover the specific [`Pin`] type from an implementation of
|
||||||
|
/// [`AnyPin`]
|
||||||
|
///
|
||||||
|
/// See the [`AnyKind`] documentation for more details on the pattern.
|
||||||
|
///
|
||||||
|
/// [`AnyKind`]: crate::typelevel#anykind-trait-pattern
|
||||||
|
pub type SpecificPin<P> = Pin<<P as AnyPin>::Id, <P as AnyPin>::Mode>;
|
||||||
|
|
||||||
|
impl<P: AnyPin> AsRef<P> for SpecificPin<P> {
|
||||||
|
#[inline]
|
||||||
|
fn as_ref(&self) -> &P {
|
||||||
|
// SAFETY: This is guaranteed to be safe, because P == SpecificPin<P>
|
||||||
|
// Transmuting between `v1` and `v2` `Pin` types is also safe, because
|
||||||
|
// both are zero-sized, and single-field, newtype structs are guaranteed
|
||||||
|
// to have the same layout as the field anyway, even for repr(Rust).
|
||||||
|
unsafe { transmute(self) }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<P: AnyPin> AsMut<P> for SpecificPin<P> {
|
||||||
|
#[inline]
|
||||||
|
fn as_mut(&mut self) -> &mut P {
|
||||||
|
// SAFETY: This is guaranteed to be safe, because P == SpecificPin<P>
|
||||||
|
// Transmuting between `v1` and `v2` `Pin` types is also safe, because
|
||||||
|
// both are zero-sized, and single-field, newtype structs are guaranteed
|
||||||
|
// to have the same layout as the field anyway, even for repr(Rust).
|
||||||
|
unsafe { transmute(self) }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
//==================================================================================================
|
||||||
|
// Additional functionality
|
||||||
|
//==================================================================================================
|
||||||
|
|
||||||
|
impl<I: PinId, C: InputConfig> Pin<I, Input<C>> {
|
||||||
|
/// Convert the pin into an async pin. The pin can be converted back by calling
|
||||||
|
/// [InputPinAsync::release]
|
||||||
|
pub fn into_async_input(self, irq: crate::pac::Interrupt) -> InputPinAsync<I, C> {
|
||||||
|
InputPinAsync::new(self, irq)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<I: PinId, C: OutputConfig> Pin<I, Output<C>> {
|
||||||
|
#[inline]
|
||||||
|
pub fn set_high(&mut self) {
|
||||||
|
self.inner.write_pin(true)
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
pub fn set_low(&mut self) {
|
||||||
|
self.inner.write_pin(false)
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
pub fn toggle(&mut self) {
|
||||||
|
self.inner.toggle().unwrap()
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
pub fn set_high_masked(&mut self) -> Result<(), crate::gpio::IsMaskedError> {
|
||||||
|
self.inner.set_high_masked()
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
pub fn set_low_masked(&mut self) -> Result<(), crate::gpio::IsMaskedError> {
|
||||||
|
self.inner.set_low_masked()
|
||||||
|
}
|
||||||
|
|
||||||
|
/// See p.53 of the programmers guide for more information.
|
||||||
|
/// Possible delays in clock cycles:
|
||||||
|
/// - Delay 1: 1
|
||||||
|
/// - Delay 2: 2
|
||||||
|
/// - Delay 1 + Delay 2: 3
|
||||||
|
#[inline]
|
||||||
|
pub fn configure_delay(&mut self, delay_1: bool, delay_2: bool) {
|
||||||
|
self.inner.configure_delay(delay_1, delay_2).unwrap();
|
||||||
|
}
|
||||||
|
|
||||||
|
/// See p.52 of the programmers guide for more information.
|
||||||
|
///
|
||||||
|
/// When configured for pulse mode, a given pin will set the non-default state for exactly
|
||||||
|
/// one clock cycle before returning to the configured default state
|
||||||
|
pub fn configure_pulse_mode(&mut self, enable: bool, default_state: PinState) {
|
||||||
|
self.inner
|
||||||
|
.configure_pulse_mode(enable, default_state)
|
||||||
|
.unwrap();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<I: PinId, C: InputConfig> Pin<I, Input<C>> {
|
||||||
|
/// See p.37 and p.38 of the programmers guide for more information.
|
||||||
|
#[inline]
|
||||||
|
pub fn configure_filter_type(&mut self, filter: FilterType, clksel: FilterClkSel) {
|
||||||
|
self.inner.configure_filter_type(filter, clksel).unwrap();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
//==================================================================================================
|
||||||
|
// Embedded HAL traits
|
||||||
|
//==================================================================================================
|
||||||
|
|
||||||
|
impl<I, M> embedded_hal::digital::ErrorType for Pin<I, M>
|
||||||
|
where
|
||||||
|
I: PinId,
|
||||||
|
M: PinMode,
|
||||||
|
{
|
||||||
|
type Error = Infallible;
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<I: PinId, C: OutputConfig> embedded_hal::digital::OutputPin for Pin<I, Output<C>> {
|
||||||
|
#[inline]
|
||||||
|
fn set_high(&mut self) -> Result<(), Self::Error> {
|
||||||
|
self.set_high();
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
fn set_low(&mut self) -> Result<(), Self::Error> {
|
||||||
|
self.set_low();
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<I, C> embedded_hal::digital::InputPin for Pin<I, Input<C>>
|
||||||
|
where
|
||||||
|
I: PinId,
|
||||||
|
C: InputConfig,
|
||||||
|
{
|
||||||
|
#[inline]
|
||||||
|
fn is_high(&mut self) -> Result<bool, Self::Error> {
|
||||||
|
Ok(self.is_high_mut())
|
||||||
|
}
|
||||||
|
#[inline]
|
||||||
|
fn is_low(&mut self) -> Result<bool, Self::Error> {
|
||||||
|
Ok(self.is_low_mut())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<I, C> embedded_hal::digital::StatefulOutputPin for Pin<I, Output<C>>
|
||||||
|
where
|
||||||
|
I: PinId,
|
||||||
|
C: OutputConfig + ReadableOutput,
|
||||||
|
{
|
||||||
|
#[inline]
|
||||||
|
fn is_set_high(&mut self) -> Result<bool, Self::Error> {
|
||||||
|
Ok(self.is_high())
|
||||||
|
}
|
||||||
|
#[inline]
|
||||||
|
fn is_set_low(&mut self) -> Result<bool, Self::Error> {
|
||||||
|
Ok(self.is_low())
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
fn toggle(&mut self) -> Result<(), Self::Error> {
|
||||||
|
self.toggle();
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
//==================================================================================================
|
||||||
|
// Pin definitions
|
||||||
|
//==================================================================================================
|
||||||
|
|
||||||
|
macro_rules! pins {
|
||||||
|
(
|
||||||
|
$Port:ident, $PinsName:ident, $($Id:ident,)+,
|
||||||
|
) => {
|
||||||
|
paste!(
|
||||||
|
/// Collection of all the individual [`Pin`]s for a given port (PORTA or PORTB)
|
||||||
|
#[derive(Debug)]
|
||||||
|
pub struct $PinsName {
|
||||||
|
port: $Port,
|
||||||
|
$(
|
||||||
|
#[doc = "Pin " $Id]
|
||||||
|
pub [<$Id:lower>]: Pin<$Id, Reset>,
|
||||||
|
)+
|
||||||
|
}
|
||||||
|
|
||||||
|
impl $PinsName {
|
||||||
|
/// Create a new struct containing all the Pins. Passing the IOCONFIG peripheral
|
||||||
|
/// is optional because it might be required to create pin definitions for both
|
||||||
|
/// ports.
|
||||||
|
#[inline]
|
||||||
|
pub fn new(
|
||||||
|
syscfg: &mut va108xx::Sysconfig,
|
||||||
|
port: $Port
|
||||||
|
) -> $PinsName {
|
||||||
|
syscfg.peripheral_clk_enable().modify(|_, w| {
|
||||||
|
w.[<$Port:lower>]().set_bit();
|
||||||
|
w.gpio().set_bit();
|
||||||
|
w.ioconfig().set_bit()
|
||||||
|
});
|
||||||
|
$PinsName {
|
||||||
|
//iocfg,
|
||||||
|
port,
|
||||||
|
// Safe because we only create one `Pin` per `PinId`
|
||||||
|
$(
|
||||||
|
[<$Id:lower>]: unsafe { Pin::new() },
|
||||||
|
)+
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Get the peripheral ID
|
||||||
|
/// Safety: Read-only register
|
||||||
|
pub fn get_perid() -> u32 {
|
||||||
|
let port = unsafe { &(*$Port::ptr()) };
|
||||||
|
port.perid().read().bits()
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Consumes the Pins struct and returns the port definitions
|
||||||
|
pub fn release(self) -> $Port {
|
||||||
|
self.port
|
||||||
|
}
|
||||||
|
}
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
macro_rules! declare_pins {
|
||||||
|
(
|
||||||
|
$Group:ident, $PinsName:ident, $Port:ident, [$(($Id:ident, $NUM:literal),)+]
|
||||||
|
) => {
|
||||||
|
pins!($Port, $PinsName, $($Id,)+,);
|
||||||
|
$(
|
||||||
|
pin_id!($Group, $Id, $NUM);
|
||||||
|
)+
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
declare_pins!(
|
||||||
|
A,
|
||||||
|
PinsA,
|
||||||
|
Porta,
|
||||||
|
[
|
||||||
|
(PA0, 0),
|
||||||
|
(PA1, 1),
|
||||||
|
(PA2, 2),
|
||||||
|
(PA3, 3),
|
||||||
|
(PA4, 4),
|
||||||
|
(PA5, 5),
|
||||||
|
(PA6, 6),
|
||||||
|
(PA7, 7),
|
||||||
|
(PA8, 8),
|
||||||
|
(PA9, 9),
|
||||||
|
(PA10, 10),
|
||||||
|
(PA11, 11),
|
||||||
|
(PA12, 12),
|
||||||
|
(PA13, 13),
|
||||||
|
(PA14, 14),
|
||||||
|
(PA15, 15),
|
||||||
|
(PA16, 16),
|
||||||
|
(PA17, 17),
|
||||||
|
(PA18, 18),
|
||||||
|
(PA19, 19),
|
||||||
|
(PA20, 20),
|
||||||
|
(PA21, 21),
|
||||||
|
(PA22, 22),
|
||||||
|
(PA23, 23),
|
||||||
|
(PA24, 24),
|
||||||
|
(PA25, 25),
|
||||||
|
(PA26, 26),
|
||||||
|
(PA27, 27),
|
||||||
|
(PA28, 28),
|
||||||
|
(PA29, 29),
|
||||||
|
(PA30, 30),
|
||||||
|
(PA31, 31),
|
||||||
|
]
|
||||||
|
);
|
||||||
|
|
||||||
|
declare_pins!(
|
||||||
|
B,
|
||||||
|
PinsB,
|
||||||
|
Portb,
|
||||||
|
[
|
||||||
|
(PB0, 0),
|
||||||
|
(PB1, 1),
|
||||||
|
(PB2, 2),
|
||||||
|
(PB3, 3),
|
||||||
|
(PB4, 4),
|
||||||
|
(PB5, 5),
|
||||||
|
(PB6, 6),
|
||||||
|
(PB7, 7),
|
||||||
|
(PB8, 8),
|
||||||
|
(PB9, 9),
|
||||||
|
(PB10, 10),
|
||||||
|
(PB11, 11),
|
||||||
|
(PB12, 12),
|
||||||
|
(PB13, 13),
|
||||||
|
(PB14, 14),
|
||||||
|
(PB15, 15),
|
||||||
|
(PB16, 16),
|
||||||
|
(PB17, 17),
|
||||||
|
(PB18, 18),
|
||||||
|
(PB19, 19),
|
||||||
|
(PB20, 20),
|
||||||
|
(PB21, 21),
|
||||||
|
(PB22, 22),
|
||||||
|
(PB23, 23),
|
||||||
|
]
|
||||||
|
);
|
@ -3,4 +3,899 @@
|
|||||||
//! ## 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)
|
||||||
pub use vorago_shared_periphs::i2c::*;
|
use crate::{
|
||||||
|
clock::enable_peripheral_clock, pac, time::Hertz, typelevel::Sealed, PeripheralSelect,
|
||||||
|
};
|
||||||
|
use core::{marker::PhantomData, ops::Deref};
|
||||||
|
use embedded_hal::i2c::{self, Operation, SevenBitAddress, TenBitAddress};
|
||||||
|
|
||||||
|
//==================================================================================================
|
||||||
|
// Defintions
|
||||||
|
//==================================================================================================
|
||||||
|
|
||||||
|
const CLK_100K: Hertz = Hertz::from_raw(100_000);
|
||||||
|
const CLK_400K: Hertz = Hertz::from_raw(400_000);
|
||||||
|
const MIN_CLK_400K: Hertz = Hertz::from_raw(8_000_000);
|
||||||
|
|
||||||
|
#[derive(Debug, PartialEq, Eq, Copy, Clone)]
|
||||||
|
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
|
||||||
|
pub enum FifoEmptyMode {
|
||||||
|
Stall = 0,
|
||||||
|
EndTransaction = 1,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, PartialEq, Eq, thiserror::Error)]
|
||||||
|
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
|
||||||
|
#[error("clock too slow for fast I2C mode")]
|
||||||
|
pub struct ClockTooSlowForFastI2cError;
|
||||||
|
|
||||||
|
#[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))]
|
||||||
|
pub enum Error {
|
||||||
|
#[error("arbitration lost")]
|
||||||
|
ArbitrationLost,
|
||||||
|
#[error("nack address")]
|
||||||
|
NackAddr,
|
||||||
|
/// Data not acknowledged in write operation
|
||||||
|
#[error("data not acknowledged in write operation")]
|
||||||
|
NackData,
|
||||||
|
/// Not enough data received in read operation
|
||||||
|
#[error("insufficient data received")]
|
||||||
|
InsufficientDataReceived,
|
||||||
|
/// Number of bytes in transfer too large (larger than 0x7fe)
|
||||||
|
#[error("data too large (larger than 0x7fe)")]
|
||||||
|
DataTooLarge,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, PartialEq, Eq, thiserror::Error)]
|
||||||
|
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
|
||||||
|
pub enum InitError {
|
||||||
|
/// Wrong address used in constructor
|
||||||
|
#[error("wrong address mode")]
|
||||||
|
WrongAddrMode,
|
||||||
|
/// APB1 clock is too slow for fast I2C mode.
|
||||||
|
#[error("clock too slow for fast I2C mode: {0}")]
|
||||||
|
ClkTooSlow(#[from] ClockTooSlowForFastI2cError),
|
||||||
|
}
|
||||||
|
|
||||||
|
impl embedded_hal::i2c::Error for Error {
|
||||||
|
fn kind(&self) -> embedded_hal::i2c::ErrorKind {
|
||||||
|
match self {
|
||||||
|
Error::ArbitrationLost => embedded_hal::i2c::ErrorKind::ArbitrationLoss,
|
||||||
|
Error::NackAddr => {
|
||||||
|
embedded_hal::i2c::ErrorKind::NoAcknowledge(i2c::NoAcknowledgeSource::Address)
|
||||||
|
}
|
||||||
|
Error::NackData => {
|
||||||
|
embedded_hal::i2c::ErrorKind::NoAcknowledge(i2c::NoAcknowledgeSource::Data)
|
||||||
|
}
|
||||||
|
Error::DataTooLarge | Error::InsufficientDataReceived => {
|
||||||
|
embedded_hal::i2c::ErrorKind::Other
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, PartialEq, Copy, Clone)]
|
||||||
|
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
|
||||||
|
enum I2cCmd {
|
||||||
|
Start = 0b00,
|
||||||
|
Stop = 0b10,
|
||||||
|
StartWithStop = 0b11,
|
||||||
|
Cancel = 0b100,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, PartialEq, Eq, Copy, Clone)]
|
||||||
|
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
|
||||||
|
pub enum I2cSpeed {
|
||||||
|
Regular100khz = 0,
|
||||||
|
Fast400khz = 1,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, PartialEq, Eq)]
|
||||||
|
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
|
||||||
|
pub enum I2cDirection {
|
||||||
|
Send = 0,
|
||||||
|
Read = 1,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, PartialEq, Eq, Copy, Clone)]
|
||||||
|
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
|
||||||
|
pub enum I2cAddress {
|
||||||
|
Regular(u8),
|
||||||
|
TenBit(u16),
|
||||||
|
}
|
||||||
|
|
||||||
|
pub type I2cRegBlock = pac::i2ca::RegisterBlock;
|
||||||
|
|
||||||
|
/// Common trait implemented by all PAC peripheral access structures. The register block
|
||||||
|
/// format is the same for all SPI blocks.
|
||||||
|
pub trait Instance: Deref<Target = I2cRegBlock> {
|
||||||
|
const IDX: u8;
|
||||||
|
const PERIPH_SEL: PeripheralSelect;
|
||||||
|
|
||||||
|
fn ptr() -> *const I2cRegBlock;
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Instance for pac::I2ca {
|
||||||
|
const IDX: u8 = 0;
|
||||||
|
const PERIPH_SEL: PeripheralSelect = PeripheralSelect::I2c0;
|
||||||
|
|
||||||
|
#[inline(always)]
|
||||||
|
fn ptr() -> *const I2cRegBlock {
|
||||||
|
Self::ptr()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Instance for pac::I2cb {
|
||||||
|
const IDX: u8 = 1;
|
||||||
|
const PERIPH_SEL: PeripheralSelect = PeripheralSelect::I2c1;
|
||||||
|
|
||||||
|
#[inline(always)]
|
||||||
|
fn ptr() -> *const I2cRegBlock {
|
||||||
|
Self::ptr()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
//==================================================================================================
|
||||||
|
// Config
|
||||||
|
//==================================================================================================
|
||||||
|
|
||||||
|
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
|
||||||
|
pub struct TrTfThighTlow(u8, u8, u8, u8);
|
||||||
|
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
|
||||||
|
pub struct TsuStoTsuStaThdStaTBuf(u8, u8, u8, u8);
|
||||||
|
|
||||||
|
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
|
||||||
|
pub struct TimingCfg {
|
||||||
|
// 4 bit max width
|
||||||
|
tr: u8,
|
||||||
|
// 4 bit max width
|
||||||
|
tf: u8,
|
||||||
|
// 4 bit max width
|
||||||
|
thigh: u8,
|
||||||
|
// 4 bit max width
|
||||||
|
tlow: u8,
|
||||||
|
// 4 bit max width
|
||||||
|
tsu_sto: u8,
|
||||||
|
// 4 bit max width
|
||||||
|
tsu_sta: u8,
|
||||||
|
// 4 bit max width
|
||||||
|
thd_sta: u8,
|
||||||
|
// 4 bit max width
|
||||||
|
tbuf: u8,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl TimingCfg {
|
||||||
|
pub fn new(
|
||||||
|
first_16_bits: TrTfThighTlow,
|
||||||
|
second_16_bits: TsuStoTsuStaThdStaTBuf,
|
||||||
|
) -> Result<Self, InvalidTimingParamsError> {
|
||||||
|
if first_16_bits.0 > 0xf
|
||||||
|
|| first_16_bits.1 > 0xf
|
||||||
|
|| first_16_bits.2 > 0xf
|
||||||
|
|| first_16_bits.3 > 0xf
|
||||||
|
|| second_16_bits.0 > 0xf
|
||||||
|
|| second_16_bits.1 > 0xf
|
||||||
|
|| second_16_bits.2 > 0xf
|
||||||
|
|| second_16_bits.3 > 0xf
|
||||||
|
{
|
||||||
|
return Err(InvalidTimingParamsError);
|
||||||
|
}
|
||||||
|
Ok(TimingCfg {
|
||||||
|
tr: first_16_bits.0,
|
||||||
|
tf: first_16_bits.1,
|
||||||
|
thigh: first_16_bits.2,
|
||||||
|
tlow: first_16_bits.3,
|
||||||
|
tsu_sto: second_16_bits.0,
|
||||||
|
tsu_sta: second_16_bits.1,
|
||||||
|
thd_sta: second_16_bits.2,
|
||||||
|
tbuf: second_16_bits.3,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn reg(&self) -> u32 {
|
||||||
|
((self.tbuf as u32) << 28)
|
||||||
|
| ((self.thd_sta as u32) << 24)
|
||||||
|
| ((self.tsu_sta as u32) << 20)
|
||||||
|
| ((self.tsu_sto as u32) << 16)
|
||||||
|
| ((self.tlow as u32) << 12)
|
||||||
|
| ((self.thigh as u32) << 8)
|
||||||
|
| ((self.tf as u32) << 4)
|
||||||
|
| (self.tr as u32)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Default for TimingCfg {
|
||||||
|
fn default() -> Self {
|
||||||
|
TimingCfg {
|
||||||
|
tr: 0x02,
|
||||||
|
tf: 0x01,
|
||||||
|
thigh: 0x08,
|
||||||
|
tlow: 0x09,
|
||||||
|
tsu_sto: 0x8,
|
||||||
|
tsu_sta: 0x0a,
|
||||||
|
thd_sta: 0x8,
|
||||||
|
tbuf: 0xa,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
|
||||||
|
pub struct MasterConfig {
|
||||||
|
pub tx_fe_mode: FifoEmptyMode,
|
||||||
|
pub rx_fe_mode: FifoEmptyMode,
|
||||||
|
/// Enable the analog delay glitch filter
|
||||||
|
pub alg_filt: bool,
|
||||||
|
/// Enable the digital glitch filter
|
||||||
|
pub dlg_filt: bool,
|
||||||
|
pub tm_cfg: Option<TimingCfg>,
|
||||||
|
// Loopback mode
|
||||||
|
// lbm: bool,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Default for MasterConfig {
|
||||||
|
fn default() -> Self {
|
||||||
|
MasterConfig {
|
||||||
|
tx_fe_mode: FifoEmptyMode::Stall,
|
||||||
|
rx_fe_mode: FifoEmptyMode::Stall,
|
||||||
|
alg_filt: false,
|
||||||
|
dlg_filt: false,
|
||||||
|
tm_cfg: None,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Sealed for MasterConfig {}
|
||||||
|
|
||||||
|
#[derive(Debug)]
|
||||||
|
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
|
||||||
|
pub struct SlaveConfig {
|
||||||
|
pub tx_fe_mode: FifoEmptyMode,
|
||||||
|
pub rx_fe_mode: FifoEmptyMode,
|
||||||
|
/// Maximum number of words before issuing a negative acknowledge.
|
||||||
|
/// Range should be 0 to 0x7fe. Setting the value to 0x7ff has the same effect as not setting
|
||||||
|
/// the enable bit since RXCOUNT stops counting at 0x7fe.
|
||||||
|
pub max_words: Option<usize>,
|
||||||
|
/// A received address is compared to the ADDRESS register (addr) using the address mask
|
||||||
|
/// (addr_mask). Those bits with a 1 in the address mask must match for there to be an address
|
||||||
|
/// match
|
||||||
|
pub addr: I2cAddress,
|
||||||
|
/// The default address mask will be 0x3ff to only allow full matches
|
||||||
|
pub addr_mask: Option<u16>,
|
||||||
|
/// Optionally specify a second I2C address the slave interface responds to
|
||||||
|
pub addr_b: Option<I2cAddress>,
|
||||||
|
pub addr_b_mask: Option<u16>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl SlaveConfig {
|
||||||
|
/// Build a default slave config given a specified slave address to respond to
|
||||||
|
pub fn new(addr: I2cAddress) -> Self {
|
||||||
|
SlaveConfig {
|
||||||
|
tx_fe_mode: FifoEmptyMode::Stall,
|
||||||
|
rx_fe_mode: FifoEmptyMode::Stall,
|
||||||
|
max_words: None,
|
||||||
|
addr,
|
||||||
|
addr_mask: None,
|
||||||
|
addr_b: None,
|
||||||
|
addr_b_mask: None,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Sealed for SlaveConfig {}
|
||||||
|
|
||||||
|
//==================================================================================================
|
||||||
|
// I2C Base
|
||||||
|
//==================================================================================================
|
||||||
|
|
||||||
|
pub struct I2cBase<I2C> {
|
||||||
|
i2c: I2C,
|
||||||
|
sys_clk: Hertz,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<I2C> I2cBase<I2C> {
|
||||||
|
#[inline]
|
||||||
|
fn unwrap_addr(addr: I2cAddress) -> (u16, u32) {
|
||||||
|
match addr {
|
||||||
|
I2cAddress::Regular(addr) => (addr as u16, 0 << 15),
|
||||||
|
I2cAddress::TenBit(addr) => (addr, 1 << 15),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<I2c: Instance> I2cBase<I2c> {
|
||||||
|
pub fn new(
|
||||||
|
syscfg: &mut pac::Sysconfig,
|
||||||
|
sysclk: impl Into<Hertz>,
|
||||||
|
i2c: I2c,
|
||||||
|
speed_mode: I2cSpeed,
|
||||||
|
ms_cfg: Option<&MasterConfig>,
|
||||||
|
sl_cfg: Option<&SlaveConfig>,
|
||||||
|
) -> Result<Self, ClockTooSlowForFastI2cError> {
|
||||||
|
enable_peripheral_clock(syscfg, I2c::PERIPH_SEL);
|
||||||
|
|
||||||
|
let mut i2c_base = I2cBase {
|
||||||
|
i2c,
|
||||||
|
sys_clk: sysclk.into(),
|
||||||
|
};
|
||||||
|
if let Some(ms_cfg) = ms_cfg {
|
||||||
|
i2c_base.cfg_master(ms_cfg);
|
||||||
|
}
|
||||||
|
|
||||||
|
if let Some(sl_cfg) = sl_cfg {
|
||||||
|
i2c_base.cfg_slave(sl_cfg);
|
||||||
|
}
|
||||||
|
i2c_base.cfg_clk_scale(speed_mode)?;
|
||||||
|
Ok(i2c_base)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn cfg_master(&mut self, ms_cfg: &MasterConfig) {
|
||||||
|
let (txfemd, rxfemd) = match (ms_cfg.tx_fe_mode, ms_cfg.rx_fe_mode) {
|
||||||
|
(FifoEmptyMode::Stall, FifoEmptyMode::Stall) => (false, false),
|
||||||
|
(FifoEmptyMode::Stall, FifoEmptyMode::EndTransaction) => (false, true),
|
||||||
|
(FifoEmptyMode::EndTransaction, FifoEmptyMode::Stall) => (true, false),
|
||||||
|
(FifoEmptyMode::EndTransaction, FifoEmptyMode::EndTransaction) => (true, true),
|
||||||
|
};
|
||||||
|
self.i2c.ctrl().modify(|_, w| {
|
||||||
|
w.txfemd().bit(txfemd);
|
||||||
|
w.rxffmd().bit(rxfemd);
|
||||||
|
w.dlgfilter().bit(ms_cfg.dlg_filt);
|
||||||
|
w.algfilter().bit(ms_cfg.alg_filt)
|
||||||
|
});
|
||||||
|
if let Some(ref tm_cfg) = ms_cfg.tm_cfg {
|
||||||
|
self.i2c
|
||||||
|
.tmconfig()
|
||||||
|
.write(|w| unsafe { w.bits(tm_cfg.reg()) });
|
||||||
|
}
|
||||||
|
self.i2c.fifo_clr().write(|w| {
|
||||||
|
w.rxfifo().set_bit();
|
||||||
|
w.txfifo().set_bit()
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
fn cfg_slave(&mut self, sl_cfg: &SlaveConfig) {
|
||||||
|
let (txfemd, rxfemd) = match (sl_cfg.tx_fe_mode, sl_cfg.rx_fe_mode) {
|
||||||
|
(FifoEmptyMode::Stall, FifoEmptyMode::Stall) => (false, false),
|
||||||
|
(FifoEmptyMode::Stall, FifoEmptyMode::EndTransaction) => (false, true),
|
||||||
|
(FifoEmptyMode::EndTransaction, FifoEmptyMode::Stall) => (true, false),
|
||||||
|
(FifoEmptyMode::EndTransaction, FifoEmptyMode::EndTransaction) => (true, true),
|
||||||
|
};
|
||||||
|
self.i2c.s0_ctrl().modify(|_, w| {
|
||||||
|
w.txfemd().bit(txfemd);
|
||||||
|
w.rxffmd().bit(rxfemd)
|
||||||
|
});
|
||||||
|
self.i2c.s0_fifo_clr().write(|w| {
|
||||||
|
w.rxfifo().set_bit();
|
||||||
|
w.txfifo().set_bit()
|
||||||
|
});
|
||||||
|
let max_words = sl_cfg.max_words;
|
||||||
|
if let Some(max_words) = max_words {
|
||||||
|
self.i2c
|
||||||
|
.s0_maxwords()
|
||||||
|
.write(|w| unsafe { w.bits((1 << 31) | max_words as u32) });
|
||||||
|
}
|
||||||
|
let (addr, addr_mode_mask) = Self::unwrap_addr(sl_cfg.addr);
|
||||||
|
// The first bit is the read/write value. Normally, both read and write are matched
|
||||||
|
// using the RWMASK bit of the address mask register
|
||||||
|
self.i2c
|
||||||
|
.s0_address()
|
||||||
|
.write(|w| unsafe { w.bits((addr << 1) as u32 | addr_mode_mask) });
|
||||||
|
if let Some(addr_mask) = sl_cfg.addr_mask {
|
||||||
|
self.i2c
|
||||||
|
.s0_addressmask()
|
||||||
|
.write(|w| unsafe { w.bits((addr_mask << 1) as u32) });
|
||||||
|
}
|
||||||
|
if let Some(addr_b) = sl_cfg.addr_b {
|
||||||
|
let (addr, addr_mode_mask) = Self::unwrap_addr(addr_b);
|
||||||
|
self.i2c
|
||||||
|
.s0_addressb()
|
||||||
|
.write(|w| unsafe { w.bits((addr << 1) as u32 | addr_mode_mask) });
|
||||||
|
}
|
||||||
|
if let Some(addr_b_mask) = sl_cfg.addr_b_mask {
|
||||||
|
self.i2c
|
||||||
|
.s0_addressmaskb()
|
||||||
|
.write(|w| unsafe { w.bits((addr_b_mask << 1) as u32) });
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
pub fn filters(&mut self, digital_filt: bool, analog_filt: bool) {
|
||||||
|
self.i2c.ctrl().modify(|_, w| {
|
||||||
|
w.dlgfilter().bit(digital_filt);
|
||||||
|
w.algfilter().bit(analog_filt)
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
pub fn fifo_empty_mode(&mut self, rx: FifoEmptyMode, tx: FifoEmptyMode) {
|
||||||
|
self.i2c.ctrl().modify(|_, w| {
|
||||||
|
w.txfemd().bit(tx as u8 != 0);
|
||||||
|
w.rxffmd().bit(rx as u8 != 0)
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
fn calc_clk_div(&self, speed_mode: I2cSpeed) -> Result<u8, ClockTooSlowForFastI2cError> {
|
||||||
|
if speed_mode == I2cSpeed::Regular100khz {
|
||||||
|
Ok(((self.sys_clk.raw() / CLK_100K.raw() / 20) - 1) as u8)
|
||||||
|
} else {
|
||||||
|
if self.sys_clk.raw() < MIN_CLK_400K.raw() {
|
||||||
|
return Err(ClockTooSlowForFastI2cError);
|
||||||
|
}
|
||||||
|
Ok(((self.sys_clk.raw() / CLK_400K.raw() / 25) - 1) as u8)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Configures the clock scale for a given speed mode setting
|
||||||
|
pub fn cfg_clk_scale(
|
||||||
|
&mut self,
|
||||||
|
speed_mode: I2cSpeed,
|
||||||
|
) -> Result<(), ClockTooSlowForFastI2cError> {
|
||||||
|
let clk_div = self.calc_clk_div(speed_mode)?;
|
||||||
|
self.i2c
|
||||||
|
.clkscale()
|
||||||
|
.write(|w| unsafe { w.bits(((speed_mode as u32) << 31) | clk_div as u32) });
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn load_address(&mut self, addr: u16) {
|
||||||
|
// Load address
|
||||||
|
self.i2c
|
||||||
|
.address()
|
||||||
|
.write(|w| unsafe { w.bits((addr << 1) as u32) });
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
fn stop_cmd(&mut self) {
|
||||||
|
self.i2c
|
||||||
|
.cmd()
|
||||||
|
.write(|w| unsafe { w.bits(I2cCmd::Stop as u32) });
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
//==================================================================================================
|
||||||
|
// I2C Master
|
||||||
|
//==================================================================================================
|
||||||
|
|
||||||
|
pub struct I2cMaster<I2c, Addr = SevenBitAddress> {
|
||||||
|
i2c_base: I2cBase<I2c>,
|
||||||
|
addr: PhantomData<Addr>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<I2c: Instance, Addr> I2cMaster<I2c, Addr> {
|
||||||
|
pub fn new(
|
||||||
|
syscfg: &mut pac::Sysconfig,
|
||||||
|
sysclk: impl Into<Hertz>,
|
||||||
|
i2c: I2c,
|
||||||
|
cfg: MasterConfig,
|
||||||
|
speed_mode: I2cSpeed,
|
||||||
|
) -> Result<Self, ClockTooSlowForFastI2cError> {
|
||||||
|
Ok(I2cMaster {
|
||||||
|
i2c_base: I2cBase::new(syscfg, sysclk, i2c, speed_mode, Some(&cfg), None)?,
|
||||||
|
addr: PhantomData,
|
||||||
|
}
|
||||||
|
.enable_master())
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
pub fn cancel_transfer(&self) {
|
||||||
|
self.i2c_base
|
||||||
|
.i2c
|
||||||
|
.cmd()
|
||||||
|
.write(|w| unsafe { w.bits(I2cCmd::Cancel as u32) });
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
pub fn clear_tx_fifo(&self) {
|
||||||
|
self.i2c_base.i2c.fifo_clr().write(|w| w.txfifo().set_bit());
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
pub fn clear_rx_fifo(&self) {
|
||||||
|
self.i2c_base.i2c.fifo_clr().write(|w| w.rxfifo().set_bit());
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
pub fn enable_master(self) -> Self {
|
||||||
|
self.i2c_base.i2c.ctrl().modify(|_, w| w.enable().set_bit());
|
||||||
|
self
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
pub fn disable_master(self) -> Self {
|
||||||
|
self.i2c_base
|
||||||
|
.i2c
|
||||||
|
.ctrl()
|
||||||
|
.modify(|_, w| w.enable().clear_bit());
|
||||||
|
self
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline(always)]
|
||||||
|
fn load_fifo(&self, word: u8) {
|
||||||
|
self.i2c_base
|
||||||
|
.i2c
|
||||||
|
.data()
|
||||||
|
.write(|w| unsafe { w.bits(word as u32) });
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline(always)]
|
||||||
|
fn read_fifo(&self) -> u8 {
|
||||||
|
self.i2c_base.i2c.data().read().bits() as u8
|
||||||
|
}
|
||||||
|
|
||||||
|
fn error_handler_write(&mut self, init_cmd: &I2cCmd) {
|
||||||
|
self.clear_tx_fifo();
|
||||||
|
if *init_cmd == I2cCmd::Start {
|
||||||
|
self.i2c_base.stop_cmd()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn write_base(
|
||||||
|
&mut self,
|
||||||
|
addr: I2cAddress,
|
||||||
|
init_cmd: I2cCmd,
|
||||||
|
bytes: impl IntoIterator<Item = u8>,
|
||||||
|
) -> Result<(), Error> {
|
||||||
|
let mut iter = bytes.into_iter();
|
||||||
|
// Load address
|
||||||
|
let (addr, addr_mode_bit) = I2cBase::<I2c>::unwrap_addr(addr);
|
||||||
|
self.i2c_base.i2c.address().write(|w| unsafe {
|
||||||
|
w.bits(I2cDirection::Send as u32 | (addr << 1) as u32 | addr_mode_bit)
|
||||||
|
});
|
||||||
|
|
||||||
|
self.i2c_base
|
||||||
|
.i2c
|
||||||
|
.cmd()
|
||||||
|
.write(|w| unsafe { w.bits(init_cmd as u32) });
|
||||||
|
let mut load_if_next_available = || {
|
||||||
|
if let Some(next_byte) = iter.next() {
|
||||||
|
self.load_fifo(next_byte);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
loop {
|
||||||
|
let status_reader = self.i2c_base.i2c.status().read();
|
||||||
|
if status_reader.arblost().bit_is_set() {
|
||||||
|
self.error_handler_write(&init_cmd);
|
||||||
|
return Err(Error::ArbitrationLost);
|
||||||
|
} else if status_reader.nackaddr().bit_is_set() {
|
||||||
|
self.error_handler_write(&init_cmd);
|
||||||
|
return Err(Error::NackAddr);
|
||||||
|
} else if status_reader.nackdata().bit_is_set() {
|
||||||
|
self.error_handler_write(&init_cmd);
|
||||||
|
return Err(Error::NackData);
|
||||||
|
} else if status_reader.idle().bit_is_set() {
|
||||||
|
return Ok(());
|
||||||
|
} else {
|
||||||
|
while !status_reader.txnfull().bit_is_set() {
|
||||||
|
load_if_next_available();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn write_from_buffer(
|
||||||
|
&mut self,
|
||||||
|
init_cmd: I2cCmd,
|
||||||
|
addr: I2cAddress,
|
||||||
|
output: &[u8],
|
||||||
|
) -> Result<(), Error> {
|
||||||
|
let len = output.len();
|
||||||
|
// It should theoretically possible to transfer larger data sizes by tracking
|
||||||
|
// the number of sent words and setting it to 0x7fe as soon as only that many
|
||||||
|
// bytes are remaining. However, large transfer like this are not common. This
|
||||||
|
// feature will therefore not be supported for now.
|
||||||
|
if len > 0x7fe {
|
||||||
|
return Err(Error::DataTooLarge);
|
||||||
|
}
|
||||||
|
// Load number of words
|
||||||
|
self.i2c_base
|
||||||
|
.i2c
|
||||||
|
.words()
|
||||||
|
.write(|w| unsafe { w.bits(len as u32) });
|
||||||
|
let mut bytes = output.iter();
|
||||||
|
// FIFO has a depth of 16. We load slightly above the trigger level
|
||||||
|
// but not all of it because the transaction might fail immediately
|
||||||
|
const FILL_DEPTH: usize = 12;
|
||||||
|
|
||||||
|
// load the FIFO
|
||||||
|
for _ in 0..core::cmp::min(FILL_DEPTH, len) {
|
||||||
|
self.load_fifo(*bytes.next().unwrap());
|
||||||
|
}
|
||||||
|
|
||||||
|
self.write_base(addr, init_cmd, output.iter().cloned())
|
||||||
|
}
|
||||||
|
|
||||||
|
fn read_internal(&mut self, addr: I2cAddress, buffer: &mut [u8]) -> Result<(), Error> {
|
||||||
|
let len = buffer.len();
|
||||||
|
// It should theoretically possible to transfer larger data sizes by tracking
|
||||||
|
// the number of sent words and setting it to 0x7fe as soon as only that many
|
||||||
|
// bytes are remaining. However, large transfer like this are not common. This
|
||||||
|
// feature will therefore not be supported for now.
|
||||||
|
if len > 0x7fe {
|
||||||
|
return Err(Error::DataTooLarge);
|
||||||
|
}
|
||||||
|
// Clear the receive FIFO
|
||||||
|
self.clear_rx_fifo();
|
||||||
|
|
||||||
|
// Load number of words
|
||||||
|
self.i2c_base
|
||||||
|
.i2c
|
||||||
|
.words()
|
||||||
|
.write(|w| unsafe { w.bits(len as u32) });
|
||||||
|
let (addr, addr_mode_bit) = match addr {
|
||||||
|
I2cAddress::Regular(addr) => (addr as u16, 0 << 15),
|
||||||
|
I2cAddress::TenBit(addr) => (addr, 1 << 15),
|
||||||
|
};
|
||||||
|
// Load address
|
||||||
|
self.i2c_base.i2c.address().write(|w| unsafe {
|
||||||
|
w.bits(I2cDirection::Read as u32 | (addr << 1) as u32 | addr_mode_bit)
|
||||||
|
});
|
||||||
|
|
||||||
|
let mut buf_iter = buffer.iter_mut();
|
||||||
|
let mut read_bytes = 0;
|
||||||
|
// Start receive transfer
|
||||||
|
self.i2c_base
|
||||||
|
.i2c
|
||||||
|
.cmd()
|
||||||
|
.write(|w| unsafe { w.bits(I2cCmd::StartWithStop as u32) });
|
||||||
|
let mut read_if_next_available = || {
|
||||||
|
if let Some(next_byte) = buf_iter.next() {
|
||||||
|
*next_byte = self.read_fifo();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
loop {
|
||||||
|
let status_reader = self.i2c_base.i2c.status().read();
|
||||||
|
if status_reader.arblost().bit_is_set() {
|
||||||
|
self.clear_rx_fifo();
|
||||||
|
return Err(Error::ArbitrationLost);
|
||||||
|
} else if status_reader.nackaddr().bit_is_set() {
|
||||||
|
self.clear_rx_fifo();
|
||||||
|
return Err(Error::NackAddr);
|
||||||
|
} else if status_reader.idle().bit_is_set() {
|
||||||
|
if read_bytes != len {
|
||||||
|
return Err(Error::InsufficientDataReceived);
|
||||||
|
}
|
||||||
|
return Ok(());
|
||||||
|
} else if status_reader.rxnempty().bit_is_set() {
|
||||||
|
read_if_next_available();
|
||||||
|
read_bytes += 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
//======================================================================================
|
||||||
|
// Embedded HAL I2C implementations
|
||||||
|
//======================================================================================
|
||||||
|
|
||||||
|
impl<I2c> embedded_hal::i2c::ErrorType for I2cMaster<I2c, SevenBitAddress> {
|
||||||
|
type Error = Error;
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<I2c: Instance> embedded_hal::i2c::I2c for I2cMaster<I2c, SevenBitAddress> {
|
||||||
|
fn transaction(
|
||||||
|
&mut self,
|
||||||
|
address: SevenBitAddress,
|
||||||
|
operations: &mut [Operation<'_>],
|
||||||
|
) -> Result<(), Self::Error> {
|
||||||
|
for operation in operations {
|
||||||
|
match operation {
|
||||||
|
Operation::Read(buf) => self.read_internal(I2cAddress::Regular(address), buf)?,
|
||||||
|
Operation::Write(buf) => self.write_from_buffer(
|
||||||
|
I2cCmd::StartWithStop,
|
||||||
|
I2cAddress::Regular(address),
|
||||||
|
buf,
|
||||||
|
)?,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<I2c> embedded_hal::i2c::ErrorType for I2cMaster<I2c, TenBitAddress> {
|
||||||
|
type Error = Error;
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<I2c: Instance> embedded_hal::i2c::I2c<TenBitAddress> for I2cMaster<I2c, TenBitAddress> {
|
||||||
|
fn transaction(
|
||||||
|
&mut self,
|
||||||
|
address: TenBitAddress,
|
||||||
|
operations: &mut [Operation<'_>],
|
||||||
|
) -> Result<(), Self::Error> {
|
||||||
|
for operation in operations {
|
||||||
|
match operation {
|
||||||
|
Operation::Read(buf) => self.read_internal(I2cAddress::TenBit(address), buf)?,
|
||||||
|
Operation::Write(buf) => {
|
||||||
|
self.write_from_buffer(I2cCmd::StartWithStop, I2cAddress::TenBit(address), buf)?
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
//==================================================================================================
|
||||||
|
// I2C Slave
|
||||||
|
//==================================================================================================
|
||||||
|
|
||||||
|
pub struct I2cSlave<I2c, Addr = SevenBitAddress> {
|
||||||
|
i2c_base: I2cBase<I2c>,
|
||||||
|
addr: PhantomData<Addr>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<I2c: Instance, Addr> I2cSlave<I2c, Addr> {
|
||||||
|
fn new_generic(
|
||||||
|
sys_cfg: &mut pac::Sysconfig,
|
||||||
|
sys_clk: impl Into<Hertz>,
|
||||||
|
i2c: I2c,
|
||||||
|
cfg: SlaveConfig,
|
||||||
|
speed_mode: I2cSpeed,
|
||||||
|
) -> Result<Self, ClockTooSlowForFastI2cError> {
|
||||||
|
Ok(I2cSlave {
|
||||||
|
i2c_base: I2cBase::new(sys_cfg, sys_clk, i2c, speed_mode, None, Some(&cfg))?,
|
||||||
|
addr: PhantomData,
|
||||||
|
}
|
||||||
|
.enable_slave())
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
pub fn enable_slave(self) -> Self {
|
||||||
|
self.i2c_base
|
||||||
|
.i2c
|
||||||
|
.s0_ctrl()
|
||||||
|
.modify(|_, w| w.enable().set_bit());
|
||||||
|
self
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
pub fn disable_slave(self) -> Self {
|
||||||
|
self.i2c_base
|
||||||
|
.i2c
|
||||||
|
.s0_ctrl()
|
||||||
|
.modify(|_, w| w.enable().clear_bit());
|
||||||
|
self
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline(always)]
|
||||||
|
fn load_fifo(&self, word: u8) {
|
||||||
|
self.i2c_base
|
||||||
|
.i2c
|
||||||
|
.s0_data()
|
||||||
|
.write(|w| unsafe { w.bits(word as u32) });
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline(always)]
|
||||||
|
fn read_fifo(&self) -> u8 {
|
||||||
|
self.i2c_base.i2c.s0_data().read().bits() as u8
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
fn clear_tx_fifo(&self) {
|
||||||
|
self.i2c_base
|
||||||
|
.i2c
|
||||||
|
.s0_fifo_clr()
|
||||||
|
.write(|w| w.txfifo().set_bit());
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
fn clear_rx_fifo(&self) {
|
||||||
|
self.i2c_base
|
||||||
|
.i2c
|
||||||
|
.s0_fifo_clr()
|
||||||
|
.write(|w| w.rxfifo().set_bit());
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Get the last address that was matched by the slave control and the corresponding
|
||||||
|
/// master direction
|
||||||
|
pub fn last_address(&self) -> (I2cDirection, u32) {
|
||||||
|
let bits = self.i2c_base.i2c.s0_lastaddress().read().bits();
|
||||||
|
match bits & 0x01 {
|
||||||
|
0 => (I2cDirection::Send, bits >> 1),
|
||||||
|
1 => (I2cDirection::Read, bits >> 1),
|
||||||
|
_ => (I2cDirection::Send, bits >> 1),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn write(&mut self, output: &[u8]) -> Result<(), Error> {
|
||||||
|
let len = output.len();
|
||||||
|
// It should theoretically possible to transfer larger data sizes by tracking
|
||||||
|
// the number of sent words and setting it to 0x7fe as soon as only that many
|
||||||
|
// bytes are remaining. However, large transfer like this are not common. This
|
||||||
|
// feature will therefore not be supported for now.
|
||||||
|
if len > 0x7fe {
|
||||||
|
return Err(Error::DataTooLarge);
|
||||||
|
}
|
||||||
|
let mut bytes = output.iter();
|
||||||
|
// FIFO has a depth of 16. We load slightly above the trigger level
|
||||||
|
// but not all of it because the transaction might fail immediately
|
||||||
|
const FILL_DEPTH: usize = 12;
|
||||||
|
|
||||||
|
// load the FIFO
|
||||||
|
for _ in 0..core::cmp::min(FILL_DEPTH, len) {
|
||||||
|
self.load_fifo(*bytes.next().unwrap());
|
||||||
|
}
|
||||||
|
|
||||||
|
let status_reader = self.i2c_base.i2c.s0_status().read();
|
||||||
|
let mut load_if_next_available = || {
|
||||||
|
if let Some(next_byte) = bytes.next() {
|
||||||
|
self.load_fifo(*next_byte);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
loop {
|
||||||
|
if status_reader.nackdata().bit_is_set() {
|
||||||
|
self.clear_tx_fifo();
|
||||||
|
return Err(Error::NackData);
|
||||||
|
} else if status_reader.idle().bit_is_set() {
|
||||||
|
return Ok(());
|
||||||
|
} else {
|
||||||
|
while !status_reader.txnfull().bit_is_set() {
|
||||||
|
load_if_next_available();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn read(&mut self, buffer: &mut [u8]) -> Result<(), Error> {
|
||||||
|
let len = buffer.len();
|
||||||
|
// It should theoretically possible to transfer larger data sizes by tracking
|
||||||
|
// the number of sent words and setting it to 0x7fe as soon as only that many
|
||||||
|
// bytes are remaining. However, large transfer like this are not common. This
|
||||||
|
// feature will therefore not be supported for now.
|
||||||
|
if len > 0x7fe {
|
||||||
|
return Err(Error::DataTooLarge);
|
||||||
|
}
|
||||||
|
// Clear the receive FIFO
|
||||||
|
self.clear_rx_fifo();
|
||||||
|
|
||||||
|
let mut buf_iter = buffer.iter_mut();
|
||||||
|
let mut read_bytes = 0;
|
||||||
|
let mut read_if_next_available = || {
|
||||||
|
if let Some(next_byte) = buf_iter.next() {
|
||||||
|
*next_byte = self.read_fifo();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
loop {
|
||||||
|
let status_reader = self.i2c_base.i2c.s0_status().read();
|
||||||
|
if status_reader.idle().bit_is_set() {
|
||||||
|
if read_bytes != len {
|
||||||
|
return Err(Error::InsufficientDataReceived);
|
||||||
|
}
|
||||||
|
return Ok(());
|
||||||
|
} else if status_reader.rxnempty().bit_is_set() {
|
||||||
|
read_bytes += 1;
|
||||||
|
read_if_next_available();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<I2c: Instance> I2cSlave<I2c, SevenBitAddress> {
|
||||||
|
/// Create a new I2C slave for seven bit addresses
|
||||||
|
pub fn new(
|
||||||
|
sys_cfg: &mut pac::Sysconfig,
|
||||||
|
sys_clk: impl Into<Hertz>,
|
||||||
|
i2c: I2c,
|
||||||
|
cfg: SlaveConfig,
|
||||||
|
speed_mode: I2cSpeed,
|
||||||
|
) -> Result<Self, InitError> {
|
||||||
|
if let I2cAddress::TenBit(_) = cfg.addr {
|
||||||
|
return Err(InitError::WrongAddrMode);
|
||||||
|
}
|
||||||
|
Ok(Self::new_generic(sys_cfg, sys_clk, i2c, cfg, speed_mode)?)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<I2c: Instance> I2cSlave<I2c, TenBitAddress> {
|
||||||
|
pub fn new_ten_bit_addr(
|
||||||
|
sys_cfg: &mut pac::Sysconfig,
|
||||||
|
sys_clk: impl Into<Hertz>,
|
||||||
|
i2c: I2c,
|
||||||
|
cfg: SlaveConfig,
|
||||||
|
speed_mode: I2cSpeed,
|
||||||
|
) -> Result<Self, ClockTooSlowForFastI2cError> {
|
||||||
|
Self::new_generic(sys_cfg, sys_clk, i2c, cfg, speed_mode)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
@ -8,21 +8,69 @@ pub use va108xx as pac;
|
|||||||
pub mod clock;
|
pub mod clock;
|
||||||
pub mod gpio;
|
pub mod gpio;
|
||||||
pub mod i2c;
|
pub mod i2c;
|
||||||
pub mod pins;
|
|
||||||
pub mod prelude;
|
pub mod prelude;
|
||||||
pub mod pwm;
|
pub mod pwm;
|
||||||
pub mod spi;
|
pub mod spi;
|
||||||
pub mod sysconfig;
|
pub mod sysconfig;
|
||||||
pub mod time;
|
pub mod time;
|
||||||
pub mod timer;
|
pub mod timer;
|
||||||
|
pub mod typelevel;
|
||||||
pub mod uart;
|
pub mod uart;
|
||||||
|
|
||||||
pub use vorago_shared_periphs::{
|
#[derive(Debug, Eq, Copy, Clone, PartialEq)]
|
||||||
disable_nvic_interrupt, enable_nvic_interrupt, FunSel, InterruptConfig, PeripheralSelect,
|
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
|
||||||
};
|
pub enum FunSel {
|
||||||
|
Sel0 = 0b00,
|
||||||
|
Sel1 = 0b01,
|
||||||
|
Sel2 = 0b10,
|
||||||
|
Sel3 = 0b11,
|
||||||
|
}
|
||||||
|
|
||||||
/// This is the NONE destination reigster value for the IRQSEL peripheral.
|
#[derive(Debug, Copy, Clone, PartialEq, Eq)]
|
||||||
pub const IRQ_DST_NONE: u32 = 0xffffffff;
|
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
|
||||||
|
pub enum PeripheralSelect {
|
||||||
|
PortA = 0,
|
||||||
|
PortB = 1,
|
||||||
|
Spi0 = 4,
|
||||||
|
Spi1 = 5,
|
||||||
|
Spi2 = 6,
|
||||||
|
Uart0 = 8,
|
||||||
|
Uart1 = 9,
|
||||||
|
I2c0 = 16,
|
||||||
|
I2c1 = 17,
|
||||||
|
Irqsel = 21,
|
||||||
|
Ioconfig = 22,
|
||||||
|
Utility = 23,
|
||||||
|
Gpio = 24,
|
||||||
|
}
|
||||||
|
|
||||||
|
/// 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
|
||||||
|
/// Cortex-M0 NVIC. Both are generally necessary for IRQs to work, but the user might want to
|
||||||
|
/// perform those steps themselves.
|
||||||
|
#[derive(Debug, PartialEq, Eq, Clone, Copy)]
|
||||||
|
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
|
||||||
|
pub struct InterruptConfig {
|
||||||
|
/// Interrupt target vector. Should always be set, might be required for disabling IRQs
|
||||||
|
pub id: pac::Interrupt,
|
||||||
|
/// Specfiy whether IRQ should be routed to an IRQ vector using the IRQSEL peripheral.
|
||||||
|
pub route: bool,
|
||||||
|
/// Specify whether the IRQ is unmasked in the Cortex-M NVIC. If an interrupt is used for
|
||||||
|
/// multiple purposes, the user can enable the interrupts themselves.
|
||||||
|
pub enable_in_nvic: bool,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl InterruptConfig {
|
||||||
|
pub fn new(id: pac::Interrupt, route: bool, enable_in_nvic: bool) -> Self {
|
||||||
|
InterruptConfig {
|
||||||
|
id,
|
||||||
|
route,
|
||||||
|
enable_in_nvic,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub type IrqCfg = InterruptConfig;
|
||||||
|
|
||||||
#[derive(Debug, PartialEq, Eq, thiserror::Error)]
|
#[derive(Debug, PartialEq, Eq, thiserror::Error)]
|
||||||
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
|
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
|
||||||
@ -53,7 +101,20 @@ pub fn port_function_select(
|
|||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
#[allow(dead_code)]
|
/// Enable a specific interrupt using the NVIC peripheral.
|
||||||
pub(crate) mod sealed {
|
///
|
||||||
pub trait Sealed {}
|
/// # 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);
|
||||||
}
|
}
|
||||||
|
@ -1,6 +0,0 @@
|
|||||||
//! Pin resource management singletons.
|
|
||||||
//!
|
|
||||||
//! This module contains the pin singletons. It allows creating those singletons
|
|
||||||
//! to access the [Pin] structures of individual ports in a safe way with checked ownership
|
|
||||||
//! rules.
|
|
||||||
pub use vorago_shared_periphs::pins::*;
|
|
@ -5,4 +5,459 @@
|
|||||||
//! ## Examples
|
//! ## Examples
|
||||||
//!
|
//!
|
||||||
//! - [PWM example](https://egit.irs.uni-stuttgart.de/rust/va108xx-rs/src/branch/main/examples/simple/examples/pwm.rs)
|
//! - [PWM example](https://egit.irs.uni-stuttgart.de/rust/va108xx-rs/src/branch/main/examples/simple/examples/pwm.rs)
|
||||||
pub use vorago_shared_periphs::pwm::*;
|
use core::convert::Infallible;
|
||||||
|
use core::marker::PhantomData;
|
||||||
|
|
||||||
|
use crate::pac;
|
||||||
|
use crate::time::Hertz;
|
||||||
|
use crate::timer::{TimDynRegister, TimPin, TimRegInterface, ValidTim, ValidTimAndPin};
|
||||||
|
use crate::{clock::enable_peripheral_clock, gpio::DynPinId};
|
||||||
|
|
||||||
|
const DUTY_MAX: u16 = u16::MAX;
|
||||||
|
|
||||||
|
#[derive(Debug)]
|
||||||
|
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
|
||||||
|
pub(crate) struct PwmCommon {
|
||||||
|
sys_clk: Hertz,
|
||||||
|
/// For PWMB, this is the upper limit
|
||||||
|
current_duty: u16,
|
||||||
|
/// For PWMA, this value will not be used
|
||||||
|
current_lower_limit: u16,
|
||||||
|
current_period: Hertz,
|
||||||
|
current_rst_val: u32,
|
||||||
|
}
|
||||||
|
|
||||||
|
enum StatusSelPwm {
|
||||||
|
PwmA = 3,
|
||||||
|
PwmB = 4,
|
||||||
|
}
|
||||||
|
|
||||||
|
pub struct PwmA {}
|
||||||
|
pub struct PwmB {}
|
||||||
|
|
||||||
|
//==================================================================================================
|
||||||
|
// Strongly typed PWM pin
|
||||||
|
//==================================================================================================
|
||||||
|
|
||||||
|
pub struct PwmPin<Pin: TimPin, Tim: ValidTim, Mode = PwmA> {
|
||||||
|
pin_and_tim: (Pin, Tim),
|
||||||
|
inner: ReducedPwmPin<Mode>,
|
||||||
|
mode: PhantomData<Mode>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<Pin: TimPin, Tim: ValidTim, Mode> PwmPin<Pin, Tim, Mode>
|
||||||
|
where
|
||||||
|
(Pin, Tim): ValidTimAndPin<Pin, Tim>,
|
||||||
|
{
|
||||||
|
/// Create a new stronlgy typed PWM pin
|
||||||
|
pub fn new(
|
||||||
|
sys_cfg: &mut pac::Sysconfig,
|
||||||
|
sys_clk: impl Into<Hertz> + Copy,
|
||||||
|
pin_and_tim: (Pin, Tim),
|
||||||
|
initial_period: impl Into<Hertz> + Copy,
|
||||||
|
) -> Self {
|
||||||
|
let mut pin = PwmPin {
|
||||||
|
pin_and_tim,
|
||||||
|
inner: ReducedPwmPin::<Mode>::new(
|
||||||
|
Tim::TIM_ID,
|
||||||
|
Pin::DYN,
|
||||||
|
PwmCommon {
|
||||||
|
current_duty: 0,
|
||||||
|
current_lower_limit: 0,
|
||||||
|
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,
|
||||||
|
};
|
||||||
|
enable_peripheral_clock(sys_cfg, crate::clock::PeripheralClocks::Gpio);
|
||||||
|
enable_peripheral_clock(sys_cfg, crate::clock::PeripheralClocks::Ioconfig);
|
||||||
|
sys_cfg
|
||||||
|
.tim_clk_enable()
|
||||||
|
.modify(|r, w| unsafe { w.bits(r.bits() | pin.pin_and_tim.1.mask_32()) });
|
||||||
|
pin.enable_pwm_a();
|
||||||
|
pin.set_period(initial_period);
|
||||||
|
pin
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn downgrade(self) -> ReducedPwmPin<Mode> {
|
||||||
|
self.inner
|
||||||
|
}
|
||||||
|
|
||||||
|
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>
|
||||||
|
where
|
||||||
|
(Pin, Tim): ValidTimAndPin<Pin, Tim>,
|
||||||
|
{
|
||||||
|
fn from(other: PwmPin<Pin, Tim, PwmA>) -> Self {
|
||||||
|
let mut pwmb = Self {
|
||||||
|
mode: PhantomData,
|
||||||
|
pin_and_tim: other.pin_and_tim,
|
||||||
|
inner: other.inner.into(),
|
||||||
|
};
|
||||||
|
pwmb.enable_pwm_b();
|
||||||
|
pwmb
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<PIN: TimPin, TIM: ValidTim> From<PwmPin<PIN, TIM, PwmB>> for PwmPin<PIN, TIM, PwmA>
|
||||||
|
where
|
||||||
|
(PIN, TIM): ValidTimAndPin<PIN, TIM>,
|
||||||
|
{
|
||||||
|
fn from(other: PwmPin<PIN, TIM, PwmB>) -> Self {
|
||||||
|
let mut pwma = Self {
|
||||||
|
mode: PhantomData,
|
||||||
|
pin_and_tim: other.pin_and_tim,
|
||||||
|
inner: other.inner.into(),
|
||||||
|
};
|
||||||
|
pwma.enable_pwm_a();
|
||||||
|
pwma
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<Pin: TimPin, Tim: ValidTim> PwmPin<Pin, Tim, PwmA>
|
||||||
|
where
|
||||||
|
(Pin, Tim): ValidTimAndPin<Pin, Tim>,
|
||||||
|
{
|
||||||
|
pub fn pwma(
|
||||||
|
sys_cfg: &mut pac::Sysconfig,
|
||||||
|
sys_clk: impl Into<Hertz> + Copy,
|
||||||
|
pin_and_tim: (Pin, Tim),
|
||||||
|
initial_period: impl Into<Hertz> + Copy,
|
||||||
|
) -> Self {
|
||||||
|
let mut pin: PwmPin<Pin, Tim, PwmA> =
|
||||||
|
Self::new(sys_cfg, sys_clk, pin_and_tim, initial_period);
|
||||||
|
pin.enable_pwm_a();
|
||||||
|
pin
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<Pin: TimPin, Tim: ValidTim> PwmPin<Pin, Tim, PwmB>
|
||||||
|
where
|
||||||
|
(Pin, Tim): ValidTimAndPin<Pin, Tim>,
|
||||||
|
{
|
||||||
|
pub fn pwmb(
|
||||||
|
sys_cfg: &mut pac::Sysconfig,
|
||||||
|
sys_clk: impl Into<Hertz> + Copy,
|
||||||
|
pin_and_tim: (Pin, Tim),
|
||||||
|
initial_period: impl Into<Hertz> + Copy,
|
||||||
|
) -> Self {
|
||||||
|
let mut pin: PwmPin<Pin, Tim, PwmB> =
|
||||||
|
Self::new(sys_cfg, sys_clk, pin_and_tim, initial_period);
|
||||||
|
pin.enable_pwm_b();
|
||||||
|
pin
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
//==================================================================================================
|
||||||
|
// Reduced PWM pin
|
||||||
|
//==================================================================================================
|
||||||
|
|
||||||
|
/// Reduced version where type information is deleted
|
||||||
|
pub struct ReducedPwmPin<Mode = PwmA> {
|
||||||
|
dyn_reg: TimDynRegister,
|
||||||
|
common: PwmCommon,
|
||||||
|
mode: PhantomData<Mode>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<Mode> ReducedPwmPin<Mode> {
|
||||||
|
pub(crate) fn new(tim_id: u8, pin_id: DynPinId, common: PwmCommon) -> Self {
|
||||||
|
Self {
|
||||||
|
dyn_reg: TimDynRegister { tim_id, pin_id },
|
||||||
|
common,
|
||||||
|
mode: PhantomData,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<Mode> ReducedPwmPin<Mode> {
|
||||||
|
#[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> {
|
||||||
|
fn from(other: ReducedPwmPin<PwmA>) -> Self {
|
||||||
|
let mut pwmb = Self {
|
||||||
|
dyn_reg: other.dyn_reg,
|
||||||
|
common: other.common,
|
||||||
|
mode: PhantomData,
|
||||||
|
};
|
||||||
|
pwmb.enable_pwm_b();
|
||||||
|
pwmb
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl From<ReducedPwmPin<PwmB>> for ReducedPwmPin<PwmA> {
|
||||||
|
fn from(other: ReducedPwmPin<PwmB>) -> Self {
|
||||||
|
let mut pwmb = Self {
|
||||||
|
dyn_reg: other.dyn_reg,
|
||||||
|
common: other.common,
|
||||||
|
mode: PhantomData,
|
||||||
|
};
|
||||||
|
pwmb.enable_pwm_a();
|
||||||
|
pwmb
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
//==================================================================================================
|
||||||
|
// PWMB implementations
|
||||||
|
//==================================================================================================
|
||||||
|
|
||||||
|
impl<Pin: TimPin, Tim: ValidTim> PwmPin<Pin, Tim, PwmB>
|
||||||
|
where
|
||||||
|
(Pin, Tim): ValidTimAndPin<Pin, Tim>,
|
||||||
|
{
|
||||||
|
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> {
|
||||||
|
#[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) });
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
//==================================================================================================
|
||||||
|
// Embedded HAL implementation: PWMA only
|
||||||
|
//==================================================================================================
|
||||||
|
|
||||||
|
impl<Pin: TimPin, Tim: ValidTim> embedded_hal::pwm::ErrorType for PwmPin<Pin, Tim> {
|
||||||
|
type Error = Infallible;
|
||||||
|
}
|
||||||
|
|
||||||
|
impl embedded_hal::pwm::ErrorType for ReducedPwmPin {
|
||||||
|
type Error = Infallible;
|
||||||
|
}
|
||||||
|
|
||||||
|
impl embedded_hal::pwm::SetDutyCycle for ReducedPwmPin {
|
||||||
|
#[inline]
|
||||||
|
fn max_duty_cycle(&self) -> u16 {
|
||||||
|
DUTY_MAX
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
fn set_duty_cycle(&mut self, duty: u16) -> Result<(), Self::Error> {
|
||||||
|
self.common.current_duty = duty;
|
||||||
|
let pwma_val: u64 = (self.common.current_rst_val as u64
|
||||||
|
* (DUTY_MAX 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) });
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<Pin: TimPin, Tim: ValidTim> embedded_hal::pwm::SetDutyCycle for PwmPin<Pin, Tim> {
|
||||||
|
#[inline]
|
||||||
|
fn max_duty_cycle(&self) -> u16 {
|
||||||
|
DUTY_MAX
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
fn set_duty_cycle(&mut self, duty: u16) -> Result<(), Self::Error> {
|
||||||
|
self.inner.set_duty_cycle(duty)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Get the corresponding u16 duty cycle from a percent value ranging between 0.0 and 1.0.
|
||||||
|
///
|
||||||
|
/// Please note that this might load a lot of floating point code because this processor does not
|
||||||
|
/// have a FPU
|
||||||
|
pub fn get_duty_from_percent(percent: f32) -> u16 {
|
||||||
|
if percent > 1.0 {
|
||||||
|
DUTY_MAX
|
||||||
|
} else if percent <= 0.0 {
|
||||||
|
0
|
||||||
|
} else {
|
||||||
|
(percent * DUTY_MAX as f32) as u16
|
||||||
|
}
|
||||||
|
}
|
||||||
|
1260
va108xx-hal/src/spi.rs
Normal file
1260
va108xx-hal/src/spi.rs
Normal file
File diff suppressed because it is too large
Load Diff
@ -1,12 +0,0 @@
|
|||||||
//! API for the SPI peripheral.
|
|
||||||
//!
|
|
||||||
//! The main abstraction provided by this module is the [Spi] an structure.
|
|
||||||
//! It provides the [SpiBus trait](https://docs.rs/embedded-hal/latest/embedded_hal/spi/trait.SpiBus.html),
|
|
||||||
//! but also offer a low level interface via the [SpiLowLevel] trait.
|
|
||||||
//!
|
|
||||||
//! ## Examples
|
|
||||||
//!
|
|
||||||
//! - [Blocking SPI example](https://egit.irs.uni-stuttgart.de/rust/va108xx-rs/src/branch/main/examples/simple/examples/spi.rs)
|
|
||||||
//! - [REB1 ADC example](https://egit.irs.uni-stuttgart.de/rust/va108xx-rs/src/branch/main/vorago-reb1/examples/max11519-adc.rs)
|
|
||||||
//! - [REB1 EEPROM library](https://egit.irs.uni-stuttgart.de/rust/va108xx-rs/src/branch/main/vorago-reb1/src/m95m01.rs)
|
|
||||||
pub use vorago_shared_periphs::spi::*;
|
|
@ -1,3 +1,5 @@
|
|||||||
|
use crate::{pac, PeripheralSelect};
|
||||||
|
|
||||||
#[derive(PartialEq, Eq, Debug)]
|
#[derive(PartialEq, Eq, Debug)]
|
||||||
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
|
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
|
||||||
pub struct InvalidCounterResetVal(pub(crate) ());
|
pub struct InvalidCounterResetVal(pub(crate) ());
|
||||||
@ -6,8 +8,10 @@ pub struct InvalidCounterResetVal(pub(crate) ());
|
|||||||
///
|
///
|
||||||
/// Returns [InvalidCounterResetVal] if the scrub rate is 0
|
/// Returns [InvalidCounterResetVal] if the scrub rate is 0
|
||||||
/// (equivalent to disabling) or larger than 24 bits
|
/// (equivalent to disabling) or larger than 24 bits
|
||||||
pub fn enable_rom_scrubbing(scrub_rate: u32) -> Result<(), InvalidCounterResetVal> {
|
pub fn enable_rom_scrubbing(
|
||||||
let syscfg = unsafe { va108xx::Sysconfig::steal() };
|
syscfg: &mut pac::Sysconfig,
|
||||||
|
scrub_rate: u32,
|
||||||
|
) -> Result<(), InvalidCounterResetVal> {
|
||||||
if scrub_rate == 0 || scrub_rate > u32::pow(2, 24) {
|
if scrub_rate == 0 || scrub_rate > u32::pow(2, 24) {
|
||||||
return Err(InvalidCounterResetVal(()));
|
return Err(InvalidCounterResetVal(()));
|
||||||
}
|
}
|
||||||
@ -15,8 +19,7 @@ pub fn enable_rom_scrubbing(scrub_rate: u32) -> Result<(), InvalidCounterResetVa
|
|||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn disable_rom_scrubbing() {
|
pub fn disable_rom_scrubbing(syscfg: &mut pac::Sysconfig) {
|
||||||
let syscfg = unsafe { va108xx::Sysconfig::steal() };
|
|
||||||
syscfg.rom_scrub().write(|w| unsafe { w.bits(0) });
|
syscfg.rom_scrub().write(|w| unsafe { w.bits(0) });
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -24,8 +27,10 @@ pub fn disable_rom_scrubbing() {
|
|||||||
///
|
///
|
||||||
/// Returns [InvalidCounterResetVal] if the scrub rate is 0
|
/// Returns [InvalidCounterResetVal] if the scrub rate is 0
|
||||||
/// (equivalent to disabling) or larger than 24 bits
|
/// (equivalent to disabling) or larger than 24 bits
|
||||||
pub fn enable_ram_scrubbing(scrub_rate: u32) -> Result<(), InvalidCounterResetVal> {
|
pub fn enable_ram_scrubbing(
|
||||||
let syscfg = unsafe { va108xx::Sysconfig::steal() };
|
syscfg: &mut pac::Sysconfig,
|
||||||
|
scrub_rate: u32,
|
||||||
|
) -> Result<(), InvalidCounterResetVal> {
|
||||||
if scrub_rate == 0 || scrub_rate > u32::pow(2, 24) {
|
if scrub_rate == 0 || scrub_rate > u32::pow(2, 24) {
|
||||||
return Err(InvalidCounterResetVal(()));
|
return Err(InvalidCounterResetVal(()));
|
||||||
}
|
}
|
||||||
@ -33,11 +38,20 @@ pub fn enable_ram_scrubbing(scrub_rate: u32) -> Result<(), InvalidCounterResetVa
|
|||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn disable_ram_scrubbing() {
|
pub fn disable_ram_scrubbing(syscfg: &mut pac::Sysconfig) {
|
||||||
let syscfg = unsafe { va108xx::Sysconfig::steal() };
|
|
||||||
syscfg.ram_scrub().write(|w| unsafe { w.bits(0) });
|
syscfg.ram_scrub().write(|w| unsafe { w.bits(0) });
|
||||||
}
|
}
|
||||||
|
|
||||||
pub use vorago_shared_periphs::sysconfig::{
|
/// Clear the reset bit. This register is active low, so doing this will hold the peripheral
|
||||||
assert_peripheral_reset, disable_peripheral_clock, enable_peripheral_clock,
|
/// in a reset state
|
||||||
};
|
pub fn clear_reset_bit(syscfg: &mut pac::Sysconfig, periph_sel: PeripheralSelect) {
|
||||||
|
syscfg
|
||||||
|
.peripheral_reset()
|
||||||
|
.modify(|r, w| unsafe { w.bits(r.bits() & !(1 << periph_sel as u8)) });
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn set_reset_bit(syscfg: &mut pac::Sysconfig, periph_sel: PeripheralSelect) {
|
||||||
|
syscfg
|
||||||
|
.peripheral_reset()
|
||||||
|
.modify(|r, w| unsafe { w.bits(r.bits() | (1 << periph_sel as u8)) });
|
||||||
|
}
|
||||||
|
@ -1,2 +1,26 @@
|
|||||||
//! Time units
|
//! Time units
|
||||||
pub use vorago_shared_periphs::time::*;
|
|
||||||
|
// Frequency based
|
||||||
|
|
||||||
|
/// Hertz
|
||||||
|
pub type Hertz = fugit::HertzU32;
|
||||||
|
|
||||||
|
/// KiloHertz
|
||||||
|
pub type KiloHertz = fugit::KilohertzU32;
|
||||||
|
|
||||||
|
/// MegaHertz
|
||||||
|
pub type MegaHertz = fugit::MegahertzU32;
|
||||||
|
|
||||||
|
// Period based
|
||||||
|
|
||||||
|
/// Seconds
|
||||||
|
pub type Seconds = fugit::SecsDurationU32;
|
||||||
|
|
||||||
|
/// Milliseconds
|
||||||
|
pub type Milliseconds = fugit::MillisDurationU32;
|
||||||
|
|
||||||
|
/// Microseconds
|
||||||
|
pub type Microseconds = fugit::MicrosDurationU32;
|
||||||
|
|
||||||
|
/// Nanoseconds
|
||||||
|
pub type Nanoseconds = fugit::NanosDurationU32;
|
||||||
|
@ -4,4 +4,786 @@
|
|||||||
//!
|
//!
|
||||||
//! - [MS and second tick implementation](https://egit.irs.uni-stuttgart.de/rust/va108xx-rs/src/branch/main/examples/simple/examples/timer-ticks.rs)
|
//! - [MS and second tick implementation](https://egit.irs.uni-stuttgart.de/rust/va108xx-rs/src/branch/main/examples/simple/examples/timer-ticks.rs)
|
||||||
//! - [Cascade feature example](https://egit.irs.uni-stuttgart.de/rust/va108xx-rs/src/branch/main/examples/simple/examples/cascade.rs)
|
//! - [Cascade feature example](https://egit.irs.uni-stuttgart.de/rust/va108xx-rs/src/branch/main/examples/simple/examples/cascade.rs)
|
||||||
pub use vorago_shared_periphs::timer::*;
|
pub use crate::InterruptConfig;
|
||||||
|
use crate::{
|
||||||
|
clock::{enable_peripheral_clock, PeripheralClocks},
|
||||||
|
enable_nvic_interrupt,
|
||||||
|
gpio::{
|
||||||
|
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,
|
||||||
|
PA9, PB0, PB1, PB10, PB11, PB12, PB13, PB14, PB15, PB16, PB17, PB18, PB19, PB2, PB20, PB21,
|
||||||
|
PB22, PB23, PB3, PB4, PB5, PB6,
|
||||||
|
},
|
||||||
|
pac::{self, tim0},
|
||||||
|
time::Hertz,
|
||||||
|
timer,
|
||||||
|
typelevel::Sealed,
|
||||||
|
};
|
||||||
|
use core::cell::Cell;
|
||||||
|
use critical_section::Mutex;
|
||||||
|
use fugit::RateExtU32;
|
||||||
|
|
||||||
|
const IRQ_DST_NONE: u32 = 0xffffffff;
|
||||||
|
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
|
||||||
|
//==================================================================================================
|
||||||
|
|
||||||
|
/// Interrupt events
|
||||||
|
pub enum Event {
|
||||||
|
/// Timer timed out / count down ended
|
||||||
|
TimeOut,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Default, Debug, PartialEq, Eq, Copy, Clone)]
|
||||||
|
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
|
||||||
|
pub struct CascadeCtrl {
|
||||||
|
/// Enable Cascade 0 signal active as a requirement for counting
|
||||||
|
pub enb_start_src_csd0: bool,
|
||||||
|
/// Invert Cascade 0, making it active low
|
||||||
|
pub inv_csd0: bool,
|
||||||
|
/// Enable Cascade 1 signal active as a requirement for counting
|
||||||
|
pub enb_start_src_csd1: bool,
|
||||||
|
/// Invert Cascade 1, making it active low
|
||||||
|
pub inv_csd1: bool,
|
||||||
|
/// Specify required operation if both Cascade 0 and Cascade 1 are active.
|
||||||
|
/// 0 is a logical AND of both cascade signals, 1 is a logical OR
|
||||||
|
pub dual_csd_op: bool,
|
||||||
|
/// Enable trigger mode for Cascade 0. In trigger mode, couting will start with the selected
|
||||||
|
/// cascade signal active, but once the counter is active, cascade control will be ignored
|
||||||
|
pub trg_csd0: bool,
|
||||||
|
/// Trigger mode, identical to [`trg_csd0`](CascadeCtrl) but for Cascade 1
|
||||||
|
pub trg_csd1: bool,
|
||||||
|
/// Enable Cascade 2 signal active as a requirement to stop counting. This mode is similar
|
||||||
|
/// to the REQ_STOP control bit, but signalled by a Cascade source
|
||||||
|
pub enb_stop_src_csd2: bool,
|
||||||
|
/// Invert Cascade 2, making it active low
|
||||||
|
pub inv_csd2: bool,
|
||||||
|
/// The counter is automatically disabled if the corresponding Cascade 2 level-sensitive input
|
||||||
|
/// souce is active when the count reaches 0. If the counter is not 0, the cascade control is
|
||||||
|
/// ignored
|
||||||
|
pub trg_csd2: bool,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, PartialEq, Eq)]
|
||||||
|
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
|
||||||
|
pub enum CascadeSel {
|
||||||
|
Csd0 = 0,
|
||||||
|
Csd1 = 1,
|
||||||
|
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
|
||||||
|
#[derive(Debug, PartialEq, Eq)]
|
||||||
|
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
|
||||||
|
#[repr(u8)]
|
||||||
|
pub enum CascadeSource {
|
||||||
|
PortA(u8),
|
||||||
|
PortB(u8),
|
||||||
|
Tim(u8),
|
||||||
|
RamSbe = 96,
|
||||||
|
RamMbe = 97,
|
||||||
|
RomSbe = 98,
|
||||||
|
RomMbe = 99,
|
||||||
|
Txev = 100,
|
||||||
|
ClockDivider(u8),
|
||||||
|
}
|
||||||
|
|
||||||
|
impl CascadeSource {
|
||||||
|
fn id(&self) -> Result<u8, InvalidCascadeSourceId> {
|
||||||
|
let port_check = |base: u8, id: u8, len: u8| {
|
||||||
|
if id > len - 1 {
|
||||||
|
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),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
//==================================================================================================
|
||||||
|
// Valid TIM and PIN combinations
|
||||||
|
//==================================================================================================
|
||||||
|
|
||||||
|
pub trait TimPin {
|
||||||
|
const DYN: DynPinId;
|
||||||
|
}
|
||||||
|
|
||||||
|
pub trait ValidTim {
|
||||||
|
// TIM ID ranging from 0 to 23 for 24 TIM peripherals
|
||||||
|
const TIM_ID: u8;
|
||||||
|
}
|
||||||
|
|
||||||
|
macro_rules! tim_marker {
|
||||||
|
($TIMX:path, $ID:expr) => {
|
||||||
|
impl ValidTim for $TIMX {
|
||||||
|
const TIM_ID: u8 = $ID;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
tim_marker!(pac::Tim0, 0);
|
||||||
|
tim_marker!(pac::Tim1, 1);
|
||||||
|
tim_marker!(pac::Tim2, 2);
|
||||||
|
tim_marker!(pac::Tim3, 3);
|
||||||
|
tim_marker!(pac::Tim4, 4);
|
||||||
|
tim_marker!(pac::Tim5, 5);
|
||||||
|
tim_marker!(pac::Tim6, 6);
|
||||||
|
tim_marker!(pac::Tim7, 7);
|
||||||
|
tim_marker!(pac::Tim8, 8);
|
||||||
|
tim_marker!(pac::Tim9, 9);
|
||||||
|
tim_marker!(pac::Tim10, 10);
|
||||||
|
tim_marker!(pac::Tim11, 11);
|
||||||
|
tim_marker!(pac::Tim12, 12);
|
||||||
|
tim_marker!(pac::Tim13, 13);
|
||||||
|
tim_marker!(pac::Tim14, 14);
|
||||||
|
tim_marker!(pac::Tim15, 15);
|
||||||
|
tim_marker!(pac::Tim16, 16);
|
||||||
|
tim_marker!(pac::Tim17, 17);
|
||||||
|
tim_marker!(pac::Tim18, 18);
|
||||||
|
tim_marker!(pac::Tim19, 19);
|
||||||
|
tim_marker!(pac::Tim20, 20);
|
||||||
|
tim_marker!(pac::Tim21, 21);
|
||||||
|
tim_marker!(pac::Tim22, 22);
|
||||||
|
tim_marker!(pac::Tim23, 23);
|
||||||
|
|
||||||
|
pub trait ValidTimAndPin<PIN: TimPin, TIM: ValidTim>: Sealed {}
|
||||||
|
|
||||||
|
macro_rules! pin_and_tim {
|
||||||
|
($PAX:ident, $ALTFUNC:ident, $ID:expr, $TIMX:path) => {
|
||||||
|
impl TimPin for Pin<$PAX, $ALTFUNC>
|
||||||
|
where
|
||||||
|
$PAX: PinId,
|
||||||
|
{
|
||||||
|
const DYN: DynPinId = $PAX::DYN;
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<PIN: TimPin, TIM: ValidTim> ValidTimAndPin<PIN, TIM> for (Pin<$PAX, $ALTFUNC>, $TIMX)
|
||||||
|
where
|
||||||
|
Pin<$PAX, $ALTFUNC>: TimPin,
|
||||||
|
$PAX: PinId,
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Sealed for (Pin<$PAX, $ALTFUNC>, $TIMX) {}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
pin_and_tim!(PA31, AltFunc2, 23, pac::Tim23);
|
||||||
|
pin_and_tim!(PA30, AltFunc2, 22, pac::Tim22);
|
||||||
|
pin_and_tim!(PA29, AltFunc2, 21, pac::Tim21);
|
||||||
|
pin_and_tim!(PA28, AltFunc2, 20, pac::Tim20);
|
||||||
|
pin_and_tim!(PA27, AltFunc2, 19, pac::Tim19);
|
||||||
|
pin_and_tim!(PA26, AltFunc2, 18, pac::Tim18);
|
||||||
|
pin_and_tim!(PA25, AltFunc2, 17, pac::Tim17);
|
||||||
|
pin_and_tim!(PA24, AltFunc2, 16, pac::Tim16);
|
||||||
|
|
||||||
|
pin_and_tim!(PA15, AltFunc1, 15, pac::Tim15);
|
||||||
|
pin_and_tim!(PA14, AltFunc1, 14, pac::Tim14);
|
||||||
|
pin_and_tim!(PA13, AltFunc1, 13, pac::Tim13);
|
||||||
|
pin_and_tim!(PA12, AltFunc1, 12, pac::Tim12);
|
||||||
|
pin_and_tim!(PA11, AltFunc1, 11, pac::Tim11);
|
||||||
|
pin_and_tim!(PA10, AltFunc1, 10, pac::Tim10);
|
||||||
|
pin_and_tim!(PA9, AltFunc1, 9, pac::Tim9);
|
||||||
|
pin_and_tim!(PA8, AltFunc1, 8, pac::Tim8);
|
||||||
|
pin_and_tim!(PA7, AltFunc1, 7, pac::Tim7);
|
||||||
|
pin_and_tim!(PA6, AltFunc1, 6, pac::Tim6);
|
||||||
|
pin_and_tim!(PA5, AltFunc1, 5, pac::Tim5);
|
||||||
|
pin_and_tim!(PA4, AltFunc1, 4, pac::Tim4);
|
||||||
|
pin_and_tim!(PA3, AltFunc1, 3, pac::Tim3);
|
||||||
|
pin_and_tim!(PA2, AltFunc1, 2, pac::Tim2);
|
||||||
|
pin_and_tim!(PA1, AltFunc1, 1, pac::Tim1);
|
||||||
|
pin_and_tim!(PA0, AltFunc1, 0, pac::Tim0);
|
||||||
|
|
||||||
|
pin_and_tim!(PB23, AltFunc3, 23, pac::Tim23);
|
||||||
|
pin_and_tim!(PB22, AltFunc3, 22, pac::Tim22);
|
||||||
|
pin_and_tim!(PB21, AltFunc3, 21, pac::Tim21);
|
||||||
|
pin_and_tim!(PB20, AltFunc3, 20, pac::Tim20);
|
||||||
|
pin_and_tim!(PB19, AltFunc3, 19, pac::Tim19);
|
||||||
|
pin_and_tim!(PB18, AltFunc3, 18, pac::Tim18);
|
||||||
|
pin_and_tim!(PB17, AltFunc3, 17, pac::Tim17);
|
||||||
|
pin_and_tim!(PB16, AltFunc3, 16, pac::Tim16);
|
||||||
|
pin_and_tim!(PB15, AltFunc3, 15, pac::Tim15);
|
||||||
|
pin_and_tim!(PB14, AltFunc3, 14, pac::Tim14);
|
||||||
|
pin_and_tim!(PB13, AltFunc3, 13, pac::Tim13);
|
||||||
|
pin_and_tim!(PB12, AltFunc3, 12, pac::Tim12);
|
||||||
|
pin_and_tim!(PB11, AltFunc3, 11, pac::Tim11);
|
||||||
|
pin_and_tim!(PB10, AltFunc3, 10, pac::Tim10);
|
||||||
|
|
||||||
|
pin_and_tim!(PB6, AltFunc3, 6, pac::Tim6);
|
||||||
|
pin_and_tim!(PB5, AltFunc3, 5, pac::Tim5);
|
||||||
|
pin_and_tim!(PB4, AltFunc3, 4, pac::Tim4);
|
||||||
|
pin_and_tim!(PB3, AltFunc3, 3, pac::Tim3);
|
||||||
|
pin_and_tim!(PB2, AltFunc3, 2, pac::Tim2);
|
||||||
|
pin_and_tim!(PB1, AltFunc3, 1, pac::Tim1);
|
||||||
|
pin_and_tim!(PB0, AltFunc3, 0, pac::Tim0);
|
||||||
|
|
||||||
|
//==================================================================================================
|
||||||
|
// Register Interface for TIM registers and TIM pins
|
||||||
|
//==================================================================================================
|
||||||
|
|
||||||
|
pub type TimRegBlock = tim0::RegisterBlock;
|
||||||
|
|
||||||
|
/// Register interface.
|
||||||
|
///
|
||||||
|
/// This interface provides valid TIM pins a way to access their corresponding TIM
|
||||||
|
/// registers
|
||||||
|
///
|
||||||
|
/// # Safety
|
||||||
|
///
|
||||||
|
/// Users should only implement the [Self::tim_id] function. No default function
|
||||||
|
/// implementations should be overridden. The implementing type must also have
|
||||||
|
/// "control" over the corresponding pin ID, i.e. it must guarantee that a each
|
||||||
|
/// pin ID is a singleton.
|
||||||
|
pub unsafe trait TimRegInterface {
|
||||||
|
fn tim_id(&self) -> u8;
|
||||||
|
|
||||||
|
const PORT_BASE: *const tim0::RegisterBlock = pac::Tim0::ptr() as *const _;
|
||||||
|
|
||||||
|
/// All 24 TIM blocks are identical. This helper functions returns the correct
|
||||||
|
/// memory mapped peripheral depending on the TIM ID.
|
||||||
|
#[inline(always)]
|
||||||
|
fn reg_block(&self) -> &TimRegBlock {
|
||||||
|
unsafe { &*Self::PORT_BASE.offset(self.tim_id() as isize) }
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline(always)]
|
||||||
|
fn mask_32(&self) -> u32 {
|
||||||
|
1 << self.tim_id()
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Clear the reset bit of the TIM, holding it in reset
|
||||||
|
///
|
||||||
|
/// # Safety
|
||||||
|
///
|
||||||
|
/// Only the bit related to the corresponding TIM peripheral is modified
|
||||||
|
#[inline]
|
||||||
|
#[allow(dead_code)]
|
||||||
|
fn clear_tim_reset_bit(&self) {
|
||||||
|
unsafe {
|
||||||
|
va108xx::Peripherals::steal()
|
||||||
|
.sysconfig
|
||||||
|
.tim_reset()
|
||||||
|
.modify(|r, w| w.bits(r.bits() & !self.mask_32()));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
#[allow(dead_code)]
|
||||||
|
fn set_tim_reset_bit(&self) {
|
||||||
|
unsafe {
|
||||||
|
va108xx::Peripherals::steal()
|
||||||
|
.sysconfig
|
||||||
|
.tim_reset()
|
||||||
|
.modify(|r, w| w.bits(r.bits() | self.mask_32()));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
unsafe impl<Tim: ValidTim> TimRegInterface for Tim {
|
||||||
|
fn tim_id(&self) -> u8 {
|
||||||
|
Tim::TIM_ID
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub(crate) struct TimDynRegister {
|
||||||
|
pub(crate) tim_id: u8,
|
||||||
|
#[allow(dead_code)]
|
||||||
|
pub(crate) pin_id: DynPinId,
|
||||||
|
}
|
||||||
|
|
||||||
|
unsafe impl TimRegInterface for TimDynRegister {
|
||||||
|
#[inline(always)]
|
||||||
|
fn tim_id(&self) -> u8 {
|
||||||
|
self.tim_id
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
//==================================================================================================
|
||||||
|
// Timers
|
||||||
|
//==================================================================================================
|
||||||
|
|
||||||
|
/// Hardware timers
|
||||||
|
pub struct CountdownTimer<Tim: ValidTim> {
|
||||||
|
tim: Tim,
|
||||||
|
curr_freq: Hertz,
|
||||||
|
irq_cfg: Option<InterruptConfig>,
|
||||||
|
sys_clk: Hertz,
|
||||||
|
rst_val: u32,
|
||||||
|
last_cnt: u32,
|
||||||
|
listening: bool,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline(always)]
|
||||||
|
pub fn enable_tim_clk(syscfg: &mut pac::Sysconfig, idx: u8) {
|
||||||
|
syscfg
|
||||||
|
.tim_clk_enable()
|
||||||
|
.modify(|r, w| unsafe { w.bits(r.bits() | (1 << idx)) });
|
||||||
|
}
|
||||||
|
|
||||||
|
#[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 {
|
||||||
|
TIM::TIM_ID
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<Tim: ValidTim> CountdownTimer<Tim> {
|
||||||
|
/// 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 {
|
||||||
|
enable_tim_clk(syscfg, Tim::TIM_ID);
|
||||||
|
let cd_timer = CountdownTimer {
|
||||||
|
tim,
|
||||||
|
sys_clk: sys_clk.into(),
|
||||||
|
irq_cfg: None,
|
||||||
|
rst_val: 0,
|
||||||
|
curr_freq: 0.Hz(),
|
||||||
|
listening: false,
|
||||||
|
last_cnt: 0,
|
||||||
|
};
|
||||||
|
cd_timer
|
||||||
|
.tim
|
||||||
|
.reg_block()
|
||||||
|
.ctrl()
|
||||||
|
.modify(|_, w| w.enable().set_bit());
|
||||||
|
cd_timer
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Listen for events. Depending on the IRQ configuration, this also activates the IRQ in the
|
||||||
|
/// IRQSEL peripheral for the provided interrupt and unmasks the interrupt
|
||||||
|
pub fn listen(
|
||||||
|
&mut self,
|
||||||
|
event: Event,
|
||||||
|
irq_cfg: InterruptConfig,
|
||||||
|
irq_sel: Option<&mut pac::Irqsel>,
|
||||||
|
sys_cfg: Option<&mut pac::Sysconfig>,
|
||||||
|
) {
|
||||||
|
match event {
|
||||||
|
Event::TimeOut => {
|
||||||
|
cortex_m::peripheral::NVIC::mask(irq_cfg.id);
|
||||||
|
self.irq_cfg = Some(irq_cfg);
|
||||||
|
if irq_cfg.route {
|
||||||
|
if let Some(sys_cfg) = sys_cfg {
|
||||||
|
enable_peripheral_clock(sys_cfg, PeripheralClocks::Irqsel);
|
||||||
|
}
|
||||||
|
if let Some(irq_sel) = irq_sel {
|
||||||
|
irq_sel
|
||||||
|
.tim0(Tim::TIM_ID as usize)
|
||||||
|
.write(|w| unsafe { w.bits(irq_cfg.id as u32) });
|
||||||
|
}
|
||||||
|
}
|
||||||
|
self.listening = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn unlisten(
|
||||||
|
&mut self,
|
||||||
|
event: Event,
|
||||||
|
syscfg: &mut pac::Sysconfig,
|
||||||
|
irqsel: &mut pac::Irqsel,
|
||||||
|
) {
|
||||||
|
match event {
|
||||||
|
Event::TimeOut => {
|
||||||
|
enable_peripheral_clock(syscfg, PeripheralClocks::Irqsel);
|
||||||
|
irqsel
|
||||||
|
.tim0(Tim::TIM_ID as usize)
|
||||||
|
.write(|w| unsafe { w.bits(IRQ_DST_NONE) });
|
||||||
|
self.disable_interrupt();
|
||||||
|
self.listening = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline(always)]
|
||||||
|
pub fn enable_interrupt(&mut self) {
|
||||||
|
self.tim
|
||||||
|
.reg_block()
|
||||||
|
.ctrl()
|
||||||
|
.modify(|_, w| w.irq_enb().set_bit());
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline(always)]
|
||||||
|
pub fn disable_interrupt(&mut self) {
|
||||||
|
self.tim
|
||||||
|
.reg_block()
|
||||||
|
.ctrl()
|
||||||
|
.modify(|_, w| w.irq_enb().clear_bit());
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn release(self, syscfg: &mut pac::Sysconfig) -> Tim {
|
||||||
|
self.tim
|
||||||
|
.reg_block()
|
||||||
|
.ctrl()
|
||||||
|
.write(|w| w.enable().clear_bit());
|
||||||
|
syscfg
|
||||||
|
.tim_clk_enable()
|
||||||
|
.modify(|r, w| unsafe { w.bits(r.bits() & !(1 << Tim::TIM_ID)) });
|
||||||
|
self.tim
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Load the count down timer with a timeout but do not start it.
|
||||||
|
pub fn load(&mut self, timeout: impl Into<Hertz>) {
|
||||||
|
self.tim
|
||||||
|
.reg_block()
|
||||||
|
.ctrl()
|
||||||
|
.modify(|_, w| w.enable().clear_bit());
|
||||||
|
self.curr_freq = timeout.into();
|
||||||
|
self.rst_val = self.sys_clk.raw() / self.curr_freq.raw();
|
||||||
|
self.set_reload(self.rst_val);
|
||||||
|
self.set_count(self.rst_val);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline(always)]
|
||||||
|
pub fn set_reload(&mut self, val: u32) {
|
||||||
|
self.tim
|
||||||
|
.reg_block()
|
||||||
|
.rst_value()
|
||||||
|
.write(|w| unsafe { w.bits(val) });
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline(always)]
|
||||||
|
pub fn set_count(&mut self, val: u32) {
|
||||||
|
self.tim
|
||||||
|
.reg_block()
|
||||||
|
.cnt_value()
|
||||||
|
.write(|w| unsafe { w.bits(val) });
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline(always)]
|
||||||
|
pub fn count(&self) -> u32 {
|
||||||
|
self.tim.reg_block().cnt_value().read().bits()
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline(always)]
|
||||||
|
pub fn enable(&mut self) {
|
||||||
|
if let Some(irq_cfg) = self.irq_cfg {
|
||||||
|
self.enable_interrupt();
|
||||||
|
if irq_cfg.enable_in_nvic {
|
||||||
|
unsafe { enable_nvic_interrupt(irq_cfg.id) };
|
||||||
|
}
|
||||||
|
}
|
||||||
|
self.tim
|
||||||
|
.reg_block()
|
||||||
|
.enable()
|
||||||
|
.write(|w| unsafe { w.bits(1) });
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline(always)]
|
||||||
|
pub fn disable(&mut self) {
|
||||||
|
self.tim
|
||||||
|
.reg_block()
|
||||||
|
.enable()
|
||||||
|
.write(|w| unsafe { w.bits(0) });
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Disable the counter, setting both enable and active bit to 0
|
||||||
|
pub fn auto_disable(self, enable: bool) -> Self {
|
||||||
|
if enable {
|
||||||
|
self.tim
|
||||||
|
.reg_block()
|
||||||
|
.ctrl()
|
||||||
|
.modify(|_, w| w.auto_disable().set_bit());
|
||||||
|
} else {
|
||||||
|
self.tim
|
||||||
|
.reg_block()
|
||||||
|
.ctrl()
|
||||||
|
.modify(|_, w| w.auto_disable().clear_bit());
|
||||||
|
}
|
||||||
|
self
|
||||||
|
}
|
||||||
|
|
||||||
|
/// This option only applies when the Auto-Disable functionality is 0.
|
||||||
|
///
|
||||||
|
/// The active bit is changed to 0 when count reaches 0, but the counter stays
|
||||||
|
/// enabled. When Auto-Disable is 1, Auto-Deactivate is implied
|
||||||
|
pub fn auto_deactivate(self, enable: bool) -> Self {
|
||||||
|
if enable {
|
||||||
|
self.tim
|
||||||
|
.reg_block()
|
||||||
|
.ctrl()
|
||||||
|
.modify(|_, w| w.auto_deactivate().set_bit());
|
||||||
|
} else {
|
||||||
|
self.tim
|
||||||
|
.reg_block()
|
||||||
|
.ctrl()
|
||||||
|
.modify(|_, w| w.auto_deactivate().clear_bit());
|
||||||
|
}
|
||||||
|
self
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Configure the cascade parameters
|
||||||
|
pub fn cascade_control(&mut self, ctrl: CascadeCtrl) {
|
||||||
|
self.tim.reg_block().csd_ctrl().write(|w| {
|
||||||
|
w.csden0().bit(ctrl.enb_start_src_csd0);
|
||||||
|
w.csdinv0().bit(ctrl.inv_csd0);
|
||||||
|
w.csden1().bit(ctrl.enb_start_src_csd1);
|
||||||
|
w.csdinv1().bit(ctrl.inv_csd1);
|
||||||
|
w.dcasop().bit(ctrl.dual_csd_op);
|
||||||
|
w.csdtrg0().bit(ctrl.trg_csd0);
|
||||||
|
w.csdtrg1().bit(ctrl.trg_csd1);
|
||||||
|
w.csden2().bit(ctrl.enb_stop_src_csd2);
|
||||||
|
w.csdinv2().bit(ctrl.inv_csd2);
|
||||||
|
w.csdtrg2().bit(ctrl.trg_csd2)
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn cascade_0_source(&mut self, src: CascadeSource) -> Result<(), InvalidCascadeSourceId> {
|
||||||
|
let id = src.id()?;
|
||||||
|
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 {
|
||||||
|
self.curr_freq
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn listening(&self) -> bool {
|
||||||
|
self.listening
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// CountDown implementation for TIMx
|
||||||
|
impl<TIM: ValidTim> CountdownTimer<TIM> {
|
||||||
|
#[inline]
|
||||||
|
pub fn start<T>(&mut self, timeout: T)
|
||||||
|
where
|
||||||
|
T: Into<Hertz>,
|
||||||
|
{
|
||||||
|
self.load(timeout);
|
||||||
|
self.enable();
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Return `Ok` if the timer has wrapped. Peripheral will automatically clear the
|
||||||
|
/// flag and restart the time if configured correctly
|
||||||
|
pub fn wait(&mut self) -> nb::Result<(), void::Void> {
|
||||||
|
let cnt = self.tim.reg_block().cnt_value().read().bits();
|
||||||
|
if (cnt > self.last_cnt) || cnt == 0 {
|
||||||
|
self.last_cnt = self.rst_val;
|
||||||
|
Ok(())
|
||||||
|
} else {
|
||||||
|
self.last_cnt = cnt;
|
||||||
|
Err(nb::Error::WouldBlock)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Returns [false] if the timer was not active, and true otherwise.
|
||||||
|
pub fn cancel(&mut self) -> bool {
|
||||||
|
if !self.tim.reg_block().ctrl().read().enable().bit_is_set() {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
self.tim
|
||||||
|
.reg_block()
|
||||||
|
.ctrl()
|
||||||
|
.write(|w| w.enable().clear_bit());
|
||||||
|
true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<TIM: ValidTim> embedded_hal::delay::DelayNs for CountdownTimer<TIM> {
|
||||||
|
fn delay_ns(&mut self, ns: u32) {
|
||||||
|
let ticks = (u64::from(ns)) * (u64::from(self.sys_clk.raw())) / 1_000_000_000;
|
||||||
|
|
||||||
|
let full_cycles = ticks >> 32;
|
||||||
|
let mut last_count;
|
||||||
|
let mut new_count;
|
||||||
|
if full_cycles > 0 {
|
||||||
|
self.set_reload(u32::MAX);
|
||||||
|
self.set_count(u32::MAX);
|
||||||
|
self.enable();
|
||||||
|
|
||||||
|
for _ in 0..full_cycles {
|
||||||
|
// Always ensure that both values are the same at the start.
|
||||||
|
new_count = self.count();
|
||||||
|
last_count = new_count;
|
||||||
|
loop {
|
||||||
|
new_count = self.count();
|
||||||
|
if new_count == 0 {
|
||||||
|
// Wait till timer has wrapped.
|
||||||
|
while self.count() == 0 {
|
||||||
|
cortex_m::asm::nop()
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
// Timer has definitely wrapped.
|
||||||
|
if new_count > last_count {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
last_count = new_count;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
let ticks = (ticks & u32::MAX as u64) as u32;
|
||||||
|
self.disable();
|
||||||
|
if ticks > 1 {
|
||||||
|
self.set_reload(ticks);
|
||||||
|
self.set_count(ticks);
|
||||||
|
self.enable();
|
||||||
|
last_count = ticks;
|
||||||
|
|
||||||
|
loop {
|
||||||
|
new_count = self.count();
|
||||||
|
if new_count == 0 || (new_count > last_count) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
last_count = new_count;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
self.disable();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 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].
|
||||||
|
pub fn set_up_ms_tick<TIM: ValidTim>(
|
||||||
|
irq_cfg: InterruptConfig,
|
||||||
|
sys_cfg: &mut pac::Sysconfig,
|
||||||
|
irq_sel: Option<&mut pac::Irqsel>,
|
||||||
|
sys_clk: impl Into<Hertz>,
|
||||||
|
tim0: TIM,
|
||||||
|
) -> CountdownTimer<TIM> {
|
||||||
|
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.start(1000.Hz());
|
||||||
|
ms_timer
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn set_up_ms_delay_provider<TIM: ValidTim>(
|
||||||
|
sys_cfg: &mut pac::Sysconfig,
|
||||||
|
sys_clk: impl Into<Hertz>,
|
||||||
|
tim: TIM,
|
||||||
|
) -> CountdownTimer<TIM> {
|
||||||
|
let mut provider = CountdownTimer::new(sys_cfg, sys_clk, tim);
|
||||||
|
provider.start(1000.Hz());
|
||||||
|
provider
|
||||||
|
}
|
||||||
|
|
||||||
|
/// This function can be called in a specified interrupt handler to increment
|
||||||
|
/// the MS counter
|
||||||
|
pub fn default_ms_irq_handler() {
|
||||||
|
critical_section::with(|cs| {
|
||||||
|
let mut ms = MS_COUNTER.borrow(cs).get();
|
||||||
|
ms += 1;
|
||||||
|
MS_COUNTER.borrow(cs).set(ms);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Get the current MS tick count
|
||||||
|
pub fn get_ms_ticks() -> u32 {
|
||||||
|
critical_section::with(|cs| MS_COUNTER.borrow(cs).get())
|
||||||
|
}
|
||||||
|
|
||||||
|
//==================================================================================================
|
||||||
|
// Delay implementations
|
||||||
|
//==================================================================================================
|
||||||
|
|
||||||
|
pub struct DelayMs(CountdownTimer<pac::Tim0>);
|
||||||
|
|
||||||
|
impl DelayMs {
|
||||||
|
pub fn new(timer: CountdownTimer<pac::Tim0>) -> Option<Self> {
|
||||||
|
if timer.curr_freq() != Hertz::from_raw(1000) || !timer.listening() {
|
||||||
|
return None;
|
||||||
|
}
|
||||||
|
Some(Self(timer))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// This assumes that the user has already set up a MS tick timer in TIM0 as a system tick
|
||||||
|
/// with [`set_up_ms_delay_provider`]
|
||||||
|
impl embedded_hal::delay::DelayNs for DelayMs {
|
||||||
|
fn delay_ns(&mut self, ns: u32) {
|
||||||
|
let ns_as_ms = ns / 1_000_000;
|
||||||
|
if self.0.curr_freq() != Hertz::from_raw(1000) || !self.0.listening() {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
let start_time = get_ms_ticks();
|
||||||
|
while get_ms_ticks() - start_time < ns_as_ms {
|
||||||
|
cortex_m::asm::nop();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
155
va108xx-hal/src/typelevel.rs
Normal file
155
va108xx-hal/src/typelevel.rs
Normal file
@ -0,0 +1,155 @@
|
|||||||
|
//! Module supporting type-level programming
|
||||||
|
//!
|
||||||
|
//! This module is identical to the
|
||||||
|
//! [atsamd typelevel](https://docs.rs/atsamd-hal/latest/atsamd_hal/typelevel/index.html).
|
||||||
|
|
||||||
|
use core::ops::{Add, Sub};
|
||||||
|
|
||||||
|
use typenum::{Add1, Bit, Sub1, UInt, Unsigned, B1, U0};
|
||||||
|
|
||||||
|
mod private {
|
||||||
|
/// Super trait used to mark traits with an exhaustive set of
|
||||||
|
/// implementations
|
||||||
|
pub trait Sealed {}
|
||||||
|
|
||||||
|
impl Sealed for u8 {}
|
||||||
|
impl Sealed for i8 {}
|
||||||
|
impl Sealed for u16 {}
|
||||||
|
impl Sealed for i16 {}
|
||||||
|
impl Sealed for u32 {}
|
||||||
|
impl Sealed for i32 {}
|
||||||
|
impl Sealed for f32 {}
|
||||||
|
|
||||||
|
/// Mapping from an instance of a countable type to its successor
|
||||||
|
pub trait Increment {
|
||||||
|
/// Successor type of `Self`
|
||||||
|
type Inc;
|
||||||
|
/// Consume an instance of `Self` and return its successor
|
||||||
|
fn inc(self) -> Self::Inc;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Mapping from an instance of a countable type to its predecessor
|
||||||
|
pub trait Decrement {
|
||||||
|
/// Predecessor type of `Self`
|
||||||
|
type Dec;
|
||||||
|
/// Consume an instance of `Self` and return its predecessor
|
||||||
|
fn dec(self) -> Self::Dec;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub(crate) use private::Decrement as PrivateDecrement;
|
||||||
|
pub(crate) use private::Increment as PrivateIncrement;
|
||||||
|
pub(crate) use private::Sealed;
|
||||||
|
|
||||||
|
/// Type-level version of the [`None`] variant
|
||||||
|
#[derive(Default)]
|
||||||
|
pub struct NoneT;
|
||||||
|
|
||||||
|
impl Sealed for NoneT {}
|
||||||
|
|
||||||
|
//==============================================================================
|
||||||
|
// Is
|
||||||
|
//==============================================================================
|
||||||
|
|
||||||
|
/// Marker trait for type identity
|
||||||
|
///
|
||||||
|
/// This trait is used as part of the [`AnyKind`] trait pattern. It represents
|
||||||
|
/// the concept of type identity, because all implementors have
|
||||||
|
/// `<Self as Is>::Type == Self`. When used as a trait bound with a specific
|
||||||
|
/// type, it guarantees that the corresponding type parameter is exactly the
|
||||||
|
/// specific type. Stated differently, it guarantees that `T == Specific` in
|
||||||
|
/// the following example.
|
||||||
|
///
|
||||||
|
/// ```ignore
|
||||||
|
/// where T: Is<Type = Specific>
|
||||||
|
/// ```
|
||||||
|
///
|
||||||
|
/// Moreover, the super traits guarantee that any instance of or reference to a
|
||||||
|
/// type `T` can be converted into the `Specific` type.
|
||||||
|
///
|
||||||
|
/// ```ignore
|
||||||
|
/// fn example<T>(mut any: T)
|
||||||
|
/// where
|
||||||
|
/// T: Is<Type = Specific>,
|
||||||
|
/// {
|
||||||
|
/// let specific_mut: &mut Specific = any.as_mut();
|
||||||
|
/// let specific_ref: &Specific = any.as_ref();
|
||||||
|
/// let specific: Specific = any.into();
|
||||||
|
/// }
|
||||||
|
/// ```
|
||||||
|
///
|
||||||
|
/// [`AnyKind`]: #anykind-trait-pattern
|
||||||
|
pub trait Is
|
||||||
|
where
|
||||||
|
Self: Sealed,
|
||||||
|
Self: From<IsType<Self>>,
|
||||||
|
Self: Into<IsType<Self>>,
|
||||||
|
Self: AsRef<IsType<Self>>,
|
||||||
|
Self: AsMut<IsType<Self>>,
|
||||||
|
{
|
||||||
|
type Type;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Type alias for [`Is::Type`]
|
||||||
|
pub type IsType<T> = <T as Is>::Type;
|
||||||
|
|
||||||
|
impl<T> Is for T
|
||||||
|
where
|
||||||
|
T: Sealed + AsRef<T> + AsMut<T>,
|
||||||
|
{
|
||||||
|
type Type = T;
|
||||||
|
}
|
||||||
|
|
||||||
|
//==============================================================================
|
||||||
|
// Counting
|
||||||
|
//==============================================================================
|
||||||
|
|
||||||
|
/// Implement `Sealed` for [`U0`]
|
||||||
|
impl Sealed for U0 {}
|
||||||
|
|
||||||
|
/// Implement `Sealed` for all type-level, [`Unsigned`] integers *except* [`U0`]
|
||||||
|
impl<U: Unsigned, B: Bit> Sealed for UInt<U, B> {}
|
||||||
|
|
||||||
|
/// Trait mapping each countable type to its successor
|
||||||
|
///
|
||||||
|
/// This trait maps each countable type to its corresponding successor type. The
|
||||||
|
/// actual implementation of this trait is contained within `PrivateIncrement`.
|
||||||
|
/// Access to `PrivateIncrement` is restricted, so that safe HAL APIs can be
|
||||||
|
/// built with it.
|
||||||
|
pub trait Increment: PrivateIncrement {}
|
||||||
|
|
||||||
|
impl<T: PrivateIncrement> Increment for T {}
|
||||||
|
|
||||||
|
/// Trait mapping each countable type to its predecessor
|
||||||
|
///
|
||||||
|
/// This trait maps each countable type to its corresponding predecessor type.
|
||||||
|
/// The actual implementation of this trait is contained within
|
||||||
|
/// `PrivateDecrement`. Access to `PrivateDecrement` is restricted, so that safe
|
||||||
|
/// HAL APIs can be built with it.
|
||||||
|
pub trait Decrement: PrivateDecrement {}
|
||||||
|
|
||||||
|
impl<T: PrivateDecrement> Decrement for T {}
|
||||||
|
|
||||||
|
impl<N> PrivateIncrement for N
|
||||||
|
where
|
||||||
|
N: Unsigned + Add<B1>,
|
||||||
|
Add1<N>: Unsigned,
|
||||||
|
{
|
||||||
|
type Inc = Add1<N>;
|
||||||
|
#[inline]
|
||||||
|
fn inc(self) -> Self::Inc {
|
||||||
|
Self::Inc::default()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<N> PrivateDecrement for N
|
||||||
|
where
|
||||||
|
N: Unsigned + Sub<B1>,
|
||||||
|
Sub1<N>: Unsigned,
|
||||||
|
{
|
||||||
|
type Dec = Sub1<N>;
|
||||||
|
#[inline]
|
||||||
|
fn dec(self) -> Self::Dec {
|
||||||
|
Self::Dec::default()
|
||||||
|
}
|
||||||
|
}
|
File diff suppressed because it is too large
Load Diff
440
va108xx-hal/src/uart/rx_asynch.rs
Normal file
440
va108xx-hal/src/uart/rx_asynch.rs
Normal file
@ -0,0 +1,440 @@
|
|||||||
|
//! # Async UART reception functionality for the VA416xx family.
|
||||||
|
//!
|
||||||
|
//! This module provides the [RxAsync] and [RxAsyncOverwriting] struct which both implement the
|
||||||
|
//! [embedded_io_async::Read] trait.
|
||||||
|
//! This trait allows for asynchronous reception of data streams. Please note that this module does
|
||||||
|
//! not specify/declare the interrupt handlers which must be provided for async support to work.
|
||||||
|
//! However, it provides two interrupt handlers:
|
||||||
|
//!
|
||||||
|
//! - [on_interrupt_rx]
|
||||||
|
//! - [on_interrupt_rx_overwriting]
|
||||||
|
//!
|
||||||
|
//! The first two are used for the [RxAsync] struct, while the latter two are used with the
|
||||||
|
//! [RxAsyncOverwriting] struct. The later two will overwrite old values in the used ring buffer.
|
||||||
|
//!
|
||||||
|
//! Error handling is performed in the user interrupt handler by checking the [AsyncUartErrors]
|
||||||
|
//! structure returned by the interrupt handlers.
|
||||||
|
//!
|
||||||
|
//! # Example
|
||||||
|
//!
|
||||||
|
//! - [Async UART RX example](https://egit.irs.uni-stuttgart.de/rust/va108xx-rs/src/branch/main/examples/embassy/src/bin/async-uart-rx.rs)
|
||||||
|
use core::{cell::RefCell, convert::Infallible, future::Future, sync::atomic::Ordering};
|
||||||
|
|
||||||
|
use critical_section::Mutex;
|
||||||
|
use embassy_sync::waitqueue::AtomicWaker;
|
||||||
|
use embedded_io::ErrorType;
|
||||||
|
use portable_atomic::AtomicBool;
|
||||||
|
use va108xx::uarta as uart_base;
|
||||||
|
|
||||||
|
use super::{Bank, Instance, Rx, UartErrors};
|
||||||
|
|
||||||
|
static UART_RX_WAKERS: [AtomicWaker; 2] = [const { AtomicWaker::new() }; 2];
|
||||||
|
static RX_READ_ACTIVE: [AtomicBool; 2] = [const { AtomicBool::new(false) }; 2];
|
||||||
|
static RX_HAS_DATA: [AtomicBool; 2] = [const { AtomicBool::new(false) }; 2];
|
||||||
|
|
||||||
|
struct RxFuture {
|
||||||
|
uart_idx: usize,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl RxFuture {
|
||||||
|
pub fn new<Uart: Instance>(_rx: &mut Rx<Uart>) -> Self {
|
||||||
|
RX_READ_ACTIVE[Uart::IDX as usize].store(true, Ordering::Relaxed);
|
||||||
|
Self {
|
||||||
|
uart_idx: Uart::IDX as usize,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Future for RxFuture {
|
||||||
|
type Output = Result<(), Infallible>;
|
||||||
|
|
||||||
|
fn poll(
|
||||||
|
self: core::pin::Pin<&mut Self>,
|
||||||
|
cx: &mut core::task::Context<'_>,
|
||||||
|
) -> core::task::Poll<Self::Output> {
|
||||||
|
UART_RX_WAKERS[self.uart_idx].register(cx.waker());
|
||||||
|
if RX_HAS_DATA[self.uart_idx].load(Ordering::Relaxed) {
|
||||||
|
return core::task::Poll::Ready(Ok(()));
|
||||||
|
}
|
||||||
|
core::task::Poll::Pending
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Clone, Copy)]
|
||||||
|
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
|
||||||
|
pub struct AsyncUartErrors {
|
||||||
|
/// Queue has overflowed, data might have been lost.
|
||||||
|
pub queue_overflow: bool,
|
||||||
|
/// UART errors.
|
||||||
|
pub uart_errors: UartErrors,
|
||||||
|
}
|
||||||
|
|
||||||
|
fn on_interrupt_handle_rx_errors(uart: &'static uart_base::RegisterBlock) -> Option<UartErrors> {
|
||||||
|
let rx_status = uart.rxstatus().read();
|
||||||
|
if rx_status.rxovr().bit_is_set()
|
||||||
|
|| rx_status.rxfrm().bit_is_set()
|
||||||
|
|| rx_status.rxpar().bit_is_set()
|
||||||
|
{
|
||||||
|
let mut errors_val = UartErrors::default();
|
||||||
|
|
||||||
|
if rx_status.rxovr().bit_is_set() {
|
||||||
|
errors_val.overflow = true;
|
||||||
|
}
|
||||||
|
if rx_status.rxfrm().bit_is_set() {
|
||||||
|
errors_val.framing = true;
|
||||||
|
}
|
||||||
|
if rx_status.rxpar().bit_is_set() {
|
||||||
|
errors_val.parity = true;
|
||||||
|
}
|
||||||
|
return Some(errors_val);
|
||||||
|
}
|
||||||
|
None
|
||||||
|
}
|
||||||
|
|
||||||
|
fn on_interrupt_rx_common_post_processing(
|
||||||
|
bank: Bank,
|
||||||
|
rx_enabled: bool,
|
||||||
|
read_some_data: bool,
|
||||||
|
irq_end: u32,
|
||||||
|
) -> Option<UartErrors> {
|
||||||
|
let idx = bank as usize;
|
||||||
|
if read_some_data {
|
||||||
|
RX_HAS_DATA[idx].store(true, Ordering::Relaxed);
|
||||||
|
if RX_READ_ACTIVE[idx].load(Ordering::Relaxed) {
|
||||||
|
UART_RX_WAKERS[idx].wake();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
let mut errors = None;
|
||||||
|
let uart_regs = unsafe { bank.reg_block() };
|
||||||
|
// Check for RX errors
|
||||||
|
if rx_enabled {
|
||||||
|
errors = on_interrupt_handle_rx_errors(uart_regs);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Clear the interrupt status bits
|
||||||
|
uart_regs.irq_clr().write(|w| unsafe { w.bits(irq_end) });
|
||||||
|
errors
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Interrupt handler with overwriting behaviour when the ring buffer is full.
|
||||||
|
///
|
||||||
|
/// Should be called in the user interrupt handler to enable
|
||||||
|
/// asynchronous reception. This variant will overwrite old data in the ring buffer in case
|
||||||
|
/// the ring buffer is full.
|
||||||
|
pub fn on_interrupt_rx_overwriting<const N: usize>(
|
||||||
|
bank: Bank,
|
||||||
|
prod: &mut heapless::spsc::Producer<u8, N>,
|
||||||
|
shared_consumer: &Mutex<RefCell<Option<heapless::spsc::Consumer<'static, u8, N>>>>,
|
||||||
|
) -> Result<(), AsyncUartErrors> {
|
||||||
|
on_interrupt_rx_async_heapless_queue_overwriting(bank, prod, shared_consumer)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn on_interrupt_rx_async_heapless_queue_overwriting<const N: usize>(
|
||||||
|
bank: Bank,
|
||||||
|
prod: &mut heapless::spsc::Producer<u8, N>,
|
||||||
|
shared_consumer: &Mutex<RefCell<Option<heapless::spsc::Consumer<'static, u8, N>>>>,
|
||||||
|
) -> Result<(), AsyncUartErrors> {
|
||||||
|
let uart_regs = unsafe { bank.reg_block() };
|
||||||
|
let irq_end = uart_regs.irq_end().read();
|
||||||
|
let enb_status = uart_regs.enable().read();
|
||||||
|
let rx_enabled = enb_status.rxenable().bit_is_set();
|
||||||
|
let mut read_some_data = false;
|
||||||
|
let mut queue_overflow = false;
|
||||||
|
|
||||||
|
// Half-Full interrupt. We have a guaranteed amount of data we can read.
|
||||||
|
if irq_end.irq_rx().bit_is_set() {
|
||||||
|
let available_bytes = uart_regs.rxfifoirqtrg().read().bits() as usize;
|
||||||
|
|
||||||
|
// If this interrupt bit is set, the trigger level is available at the very least.
|
||||||
|
// Read everything as fast as possible
|
||||||
|
for _ in 0..available_bytes {
|
||||||
|
let byte = uart_regs.data().read().bits();
|
||||||
|
if !prod.ready() {
|
||||||
|
queue_overflow = true;
|
||||||
|
critical_section::with(|cs| {
|
||||||
|
let mut cons_ref = shared_consumer.borrow(cs).borrow_mut();
|
||||||
|
cons_ref.as_mut().unwrap().dequeue();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
prod.enqueue(byte as u8).ok();
|
||||||
|
}
|
||||||
|
read_some_data = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Timeout, empty the FIFO completely.
|
||||||
|
if irq_end.irq_rx_to().bit_is_set() {
|
||||||
|
while uart_regs.rxstatus().read().rdavl().bit_is_set() {
|
||||||
|
// While there is data in the FIFO, write it into the reception buffer
|
||||||
|
let byte = uart_regs.data().read().bits();
|
||||||
|
if !prod.ready() {
|
||||||
|
queue_overflow = true;
|
||||||
|
critical_section::with(|cs| {
|
||||||
|
let mut cons_ref = shared_consumer.borrow(cs).borrow_mut();
|
||||||
|
cons_ref.as_mut().unwrap().dequeue();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
prod.enqueue(byte as u8).ok();
|
||||||
|
}
|
||||||
|
read_some_data = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
let uart_errors =
|
||||||
|
on_interrupt_rx_common_post_processing(bank, rx_enabled, read_some_data, irq_end.bits());
|
||||||
|
if uart_errors.is_some() || queue_overflow {
|
||||||
|
return Err(AsyncUartErrors {
|
||||||
|
queue_overflow,
|
||||||
|
uart_errors: uart_errors.unwrap_or_default(),
|
||||||
|
});
|
||||||
|
}
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Interrupt handler for asynchronous RX operations.
|
||||||
|
///
|
||||||
|
/// Should be called in the user interrupt handler to enable asynchronous reception.
|
||||||
|
pub fn on_interrupt_rx<const N: usize>(
|
||||||
|
bank: Bank,
|
||||||
|
prod: &mut heapless::spsc::Producer<'_, u8, N>,
|
||||||
|
) -> Result<(), AsyncUartErrors> {
|
||||||
|
on_interrupt_rx_async_heapless_queue(bank, prod)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn on_interrupt_rx_async_heapless_queue<const N: usize>(
|
||||||
|
bank: Bank,
|
||||||
|
prod: &mut heapless::spsc::Producer<'_, u8, N>,
|
||||||
|
) -> Result<(), AsyncUartErrors> {
|
||||||
|
let uart = unsafe { bank.reg_block() };
|
||||||
|
let irq_end = uart.irq_end().read();
|
||||||
|
let enb_status = uart.enable().read();
|
||||||
|
let rx_enabled = enb_status.rxenable().bit_is_set();
|
||||||
|
let mut read_some_data = false;
|
||||||
|
let mut queue_overflow = false;
|
||||||
|
|
||||||
|
// Half-Full interrupt. We have a guaranteed amount of data we can read.
|
||||||
|
if irq_end.irq_rx().bit_is_set() {
|
||||||
|
let available_bytes = uart.rxfifoirqtrg().read().bits() as usize;
|
||||||
|
|
||||||
|
// If this interrupt bit is set, the trigger level is available at the very least.
|
||||||
|
// Read everything as fast as possible
|
||||||
|
for _ in 0..available_bytes {
|
||||||
|
let byte = uart.data().read().bits();
|
||||||
|
if !prod.ready() {
|
||||||
|
queue_overflow = true;
|
||||||
|
}
|
||||||
|
prod.enqueue(byte as u8).ok();
|
||||||
|
}
|
||||||
|
read_some_data = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Timeout, empty the FIFO completely.
|
||||||
|
if irq_end.irq_rx_to().bit_is_set() {
|
||||||
|
while uart.rxstatus().read().rdavl().bit_is_set() {
|
||||||
|
// While there is data in the FIFO, write it into the reception buffer
|
||||||
|
let byte = uart.data().read().bits();
|
||||||
|
if !prod.ready() {
|
||||||
|
queue_overflow = true;
|
||||||
|
}
|
||||||
|
prod.enqueue(byte as u8).ok();
|
||||||
|
}
|
||||||
|
read_some_data = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
let uart_errors =
|
||||||
|
on_interrupt_rx_common_post_processing(bank, rx_enabled, read_some_data, irq_end.bits());
|
||||||
|
if uart_errors.is_some() || queue_overflow {
|
||||||
|
return Err(AsyncUartErrors {
|
||||||
|
queue_overflow,
|
||||||
|
uart_errors: uart_errors.unwrap_or_default(),
|
||||||
|
});
|
||||||
|
}
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
struct ActiveReadGuard(usize);
|
||||||
|
|
||||||
|
impl Drop for ActiveReadGuard {
|
||||||
|
fn drop(&mut self) {
|
||||||
|
RX_READ_ACTIVE[self.0].store(false, Ordering::Relaxed);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
struct RxAsyncInner<Uart: Instance, const N: usize> {
|
||||||
|
rx: Rx<Uart>,
|
||||||
|
pub queue: heapless::spsc::Consumer<'static, u8, N>,
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Core data structure to allow asynchronous UART reception.
|
||||||
|
///
|
||||||
|
/// If the ring buffer becomes full, data will be lost.
|
||||||
|
pub struct RxAsync<Uart: Instance, const N: usize>(Option<RxAsyncInner<Uart, N>>);
|
||||||
|
|
||||||
|
impl<Uart: Instance, const N: usize> ErrorType for RxAsync<Uart, N> {
|
||||||
|
/// Error reporting is done using the result of the interrupt functions.
|
||||||
|
type Error = Infallible;
|
||||||
|
}
|
||||||
|
|
||||||
|
fn stop_async_rx<Uart: Instance>(rx: &mut Rx<Uart>) {
|
||||||
|
rx.disable_interrupts();
|
||||||
|
rx.disable();
|
||||||
|
rx.clear_fifo();
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<Uart: Instance, const N: usize> RxAsync<Uart, N> {
|
||||||
|
/// Create a new asynchronous receiver.
|
||||||
|
///
|
||||||
|
/// The passed [heapless::spsc::Consumer] will be used to asynchronously receive data which
|
||||||
|
/// is filled by the interrupt handler [on_interrupt_rx].
|
||||||
|
pub fn new(mut rx: Rx<Uart>, queue: heapless::spsc::Consumer<'static, u8, N>) -> Self {
|
||||||
|
rx.disable_interrupts();
|
||||||
|
rx.disable();
|
||||||
|
rx.clear_fifo();
|
||||||
|
// Enable those together.
|
||||||
|
critical_section::with(|_| {
|
||||||
|
rx.enable_interrupts();
|
||||||
|
rx.enable();
|
||||||
|
});
|
||||||
|
Self(Some(RxAsyncInner { rx, queue }))
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn stop(&mut self) {
|
||||||
|
stop_async_rx(&mut self.0.as_mut().unwrap().rx);
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn release(mut self) -> (Rx<Uart>, heapless::spsc::Consumer<'static, u8, N>) {
|
||||||
|
self.stop();
|
||||||
|
let inner = self.0.take().unwrap();
|
||||||
|
(inner.rx, inner.queue)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<Uart: Instance, const N: usize> Drop for RxAsync<Uart, N> {
|
||||||
|
fn drop(&mut self) {
|
||||||
|
self.stop();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<Uart: Instance, const N: usize> embedded_io_async::Read for RxAsync<Uart, N> {
|
||||||
|
async fn read(&mut self, buf: &mut [u8]) -> Result<usize, Self::Error> {
|
||||||
|
// Need to wait for the IRQ to read data and set this flag. If the queue is not
|
||||||
|
// empty, we can read data immediately.
|
||||||
|
if self.0.as_ref().unwrap().queue.len() == 0 {
|
||||||
|
RX_HAS_DATA[Uart::IDX as usize].store(false, Ordering::Relaxed);
|
||||||
|
}
|
||||||
|
let _guard = ActiveReadGuard(Uart::IDX as usize);
|
||||||
|
let mut handle_data_in_queue = |consumer: &mut heapless::spsc::Consumer<'static, u8, N>| {
|
||||||
|
let data_to_read = consumer.len().min(buf.len());
|
||||||
|
for byte in buf.iter_mut().take(data_to_read) {
|
||||||
|
// We own the consumer and we checked that the amount of data is guaranteed to be available.
|
||||||
|
*byte = unsafe { consumer.dequeue_unchecked() };
|
||||||
|
}
|
||||||
|
data_to_read
|
||||||
|
};
|
||||||
|
let mut_ref = self.0.as_mut().unwrap();
|
||||||
|
let fut = RxFuture::new(&mut mut_ref.rx);
|
||||||
|
// Data is available, so read that data immediately.
|
||||||
|
let read_data = handle_data_in_queue(&mut mut_ref.queue);
|
||||||
|
if read_data > 0 {
|
||||||
|
return Ok(read_data);
|
||||||
|
}
|
||||||
|
// Await data.
|
||||||
|
let _ = fut.await;
|
||||||
|
Ok(handle_data_in_queue(&mut mut_ref.queue))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
struct RxAsyncOverwritingInner<Uart: Instance, const N: usize> {
|
||||||
|
rx: Rx<Uart>,
|
||||||
|
pub shared_consumer: &'static Mutex<RefCell<Option<heapless::spsc::Consumer<'static, u8, N>>>>,
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Core data structure to allow asynchronous UART reception.
|
||||||
|
///
|
||||||
|
/// If the ring buffer becomes full, the oldest data will be overwritten when using the
|
||||||
|
/// [on_interrupt_rx_overwriting] interrupt handlers.
|
||||||
|
pub struct RxAsyncOverwriting<Uart: Instance, const N: usize>(
|
||||||
|
Option<RxAsyncOverwritingInner<Uart, N>>,
|
||||||
|
);
|
||||||
|
|
||||||
|
impl<Uart: Instance, const N: usize> ErrorType for RxAsyncOverwriting<Uart, N> {
|
||||||
|
/// Error reporting is done using the result of the interrupt functions.
|
||||||
|
type Error = Infallible;
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<Uart: Instance, const N: usize> RxAsyncOverwriting<Uart, N> {
|
||||||
|
/// Create a new asynchronous receiver.
|
||||||
|
///
|
||||||
|
/// The passed shared [heapless::spsc::Consumer] will be used to asynchronously receive data
|
||||||
|
/// which is filled by the interrupt handler. The shared property allows using it in the
|
||||||
|
/// interrupt handler to overwrite old data.
|
||||||
|
pub fn new(
|
||||||
|
mut rx: Rx<Uart>,
|
||||||
|
shared_consumer: &'static Mutex<RefCell<Option<heapless::spsc::Consumer<'static, u8, N>>>>,
|
||||||
|
) -> Self {
|
||||||
|
rx.disable_interrupts();
|
||||||
|
rx.disable();
|
||||||
|
rx.clear_fifo();
|
||||||
|
// Enable those together.
|
||||||
|
critical_section::with(|_| {
|
||||||
|
rx.enable_interrupts();
|
||||||
|
rx.enable();
|
||||||
|
});
|
||||||
|
Self(Some(RxAsyncOverwritingInner {
|
||||||
|
rx,
|
||||||
|
shared_consumer,
|
||||||
|
}))
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn stop(&mut self) {
|
||||||
|
stop_async_rx(&mut self.0.as_mut().unwrap().rx);
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn release(mut self) -> Rx<Uart> {
|
||||||
|
self.stop();
|
||||||
|
let inner = self.0.take().unwrap();
|
||||||
|
inner.rx
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<Uart: Instance, const N: usize> Drop for RxAsyncOverwriting<Uart, N> {
|
||||||
|
fn drop(&mut self) {
|
||||||
|
self.stop();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<Uart: Instance, const N: usize> embedded_io_async::Read for RxAsyncOverwriting<Uart, N> {
|
||||||
|
async fn read(&mut self, buf: &mut [u8]) -> Result<usize, Self::Error> {
|
||||||
|
// Need to wait for the IRQ to read data and set this flag. If the queue is not
|
||||||
|
// empty, we can read data immediately.
|
||||||
|
|
||||||
|
critical_section::with(|cs| {
|
||||||
|
let queue = self.0.as_ref().unwrap().shared_consumer.borrow(cs);
|
||||||
|
if queue.borrow().as_ref().unwrap().len() == 0 {
|
||||||
|
RX_HAS_DATA[Uart::IDX as usize].store(false, Ordering::Relaxed);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
let _guard = ActiveReadGuard(Uart::IDX as usize);
|
||||||
|
let mut handle_data_in_queue = |inner: &mut RxAsyncOverwritingInner<Uart, N>| {
|
||||||
|
critical_section::with(|cs| {
|
||||||
|
let mut consumer_ref = inner.shared_consumer.borrow(cs).borrow_mut();
|
||||||
|
let consumer = consumer_ref.as_mut().unwrap();
|
||||||
|
let data_to_read = consumer.len().min(buf.len());
|
||||||
|
for byte in buf.iter_mut().take(data_to_read) {
|
||||||
|
// We own the consumer and we checked that the amount of data is guaranteed to be available.
|
||||||
|
*byte = unsafe { consumer.dequeue_unchecked() };
|
||||||
|
}
|
||||||
|
data_to_read
|
||||||
|
})
|
||||||
|
};
|
||||||
|
let fut = RxFuture::new(&mut self.0.as_mut().unwrap().rx);
|
||||||
|
// Data is available, so read that data immediately.
|
||||||
|
let read_data = handle_data_in_queue(self.0.as_mut().unwrap());
|
||||||
|
if read_data > 0 {
|
||||||
|
return Ok(read_data);
|
||||||
|
}
|
||||||
|
// Await data.
|
||||||
|
let _ = fut.await;
|
||||||
|
let read_data = handle_data_in_queue(self.0.as_mut().unwrap());
|
||||||
|
Ok(read_data)
|
||||||
|
}
|
||||||
|
}
|
254
va108xx-hal/src/uart/tx_asynch.rs
Normal file
254
va108xx-hal/src/uart/tx_asynch.rs
Normal file
@ -0,0 +1,254 @@
|
|||||||
|
//! # Async UART transmission functionality for the VA108xx family.
|
||||||
|
//!
|
||||||
|
//! This module provides the [TxAsync] struct which implements the [embedded_io_async::Write] trait.
|
||||||
|
//! This trait allows for asynchronous sending of data streams. Please note that this module does
|
||||||
|
//! not specify/declare the interrupt handlers which must be provided for async support to work.
|
||||||
|
//! However, it the [on_interrupt_tx] interrupt handler.
|
||||||
|
//!
|
||||||
|
//! This handler should be called in ALL user interrupt handlers which handle UART TX interrupts
|
||||||
|
//! for a given UART bank.
|
||||||
|
//!
|
||||||
|
//! # Example
|
||||||
|
//!
|
||||||
|
//! - [Async UART TX example](https://egit.irs.uni-stuttgart.de/rust/va108xx-rs/src/branch/main/examples/embassy/src/bin/async-uart-tx.rs)
|
||||||
|
use core::{cell::RefCell, future::Future};
|
||||||
|
|
||||||
|
use critical_section::Mutex;
|
||||||
|
use embassy_sync::waitqueue::AtomicWaker;
|
||||||
|
use embedded_io_async::Write;
|
||||||
|
use portable_atomic::AtomicBool;
|
||||||
|
|
||||||
|
use super::*;
|
||||||
|
|
||||||
|
static UART_TX_WAKERS: [AtomicWaker; 2] = [const { AtomicWaker::new() }; 2];
|
||||||
|
static TX_CONTEXTS: [Mutex<RefCell<TxContext>>; 2] =
|
||||||
|
[const { Mutex::new(RefCell::new(TxContext::new())) }; 2];
|
||||||
|
// Completion flag. Kept outside of the context structure as an atomic to avoid
|
||||||
|
// critical section.
|
||||||
|
static TX_DONE: [AtomicBool; 2] = [const { AtomicBool::new(false) }; 2];
|
||||||
|
|
||||||
|
/// This is a generic interrupt handler to handle asynchronous UART TX operations for a given
|
||||||
|
/// UART bank.
|
||||||
|
///
|
||||||
|
/// The user has to call this once in the interrupt handler responsible for the TX interrupts on
|
||||||
|
/// the given UART bank.
|
||||||
|
pub fn on_interrupt_tx(bank: Bank) {
|
||||||
|
let uart = unsafe { bank.reg_block() };
|
||||||
|
let idx = bank as usize;
|
||||||
|
let irq_enb = uart.irq_enb().read();
|
||||||
|
// IRQ is not related to TX.
|
||||||
|
if irq_enb.irq_tx().bit_is_clear() || irq_enb.irq_tx_empty().bit_is_clear() {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
let tx_status = uart.txstatus().read();
|
||||||
|
let unexpected_overrun = tx_status.wrlost().bit_is_set();
|
||||||
|
let mut context = critical_section::with(|cs| {
|
||||||
|
let context_ref = TX_CONTEXTS[idx].borrow(cs);
|
||||||
|
*context_ref.borrow()
|
||||||
|
});
|
||||||
|
context.tx_overrun = unexpected_overrun;
|
||||||
|
if context.progress >= context.slice.len && !tx_status.wrbusy().bit_is_set() {
|
||||||
|
uart.irq_enb().modify(|_, w| {
|
||||||
|
w.irq_tx().clear_bit();
|
||||||
|
w.irq_tx_empty().clear_bit();
|
||||||
|
w.irq_tx_status().clear_bit()
|
||||||
|
});
|
||||||
|
uart.enable().modify(|_, w| w.txenable().clear_bit());
|
||||||
|
// Write back updated context structure.
|
||||||
|
critical_section::with(|cs| {
|
||||||
|
let context_ref = TX_CONTEXTS[idx].borrow(cs);
|
||||||
|
*context_ref.borrow_mut() = context;
|
||||||
|
});
|
||||||
|
// Transfer is done.
|
||||||
|
TX_DONE[idx].store(true, core::sync::atomic::Ordering::Relaxed);
|
||||||
|
UART_TX_WAKERS[idx].wake();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
// Safety: We documented that the user provided slice must outlive the future, so we convert
|
||||||
|
// the raw pointer back to the slice here.
|
||||||
|
let slice = unsafe { core::slice::from_raw_parts(context.slice.data, context.slice.len) };
|
||||||
|
while context.progress < context.slice.len {
|
||||||
|
let wrrdy = uart.txstatus().read().wrrdy().bit_is_set();
|
||||||
|
if !wrrdy {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
// Safety: TX structure is owned by the future which does not write into the the data
|
||||||
|
// register, so we can assume we are the only one writing to the data register.
|
||||||
|
uart.data()
|
||||||
|
.write(|w| unsafe { w.bits(slice[context.progress] as u32) });
|
||||||
|
context.progress += 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Write back updated context structure.
|
||||||
|
critical_section::with(|cs| {
|
||||||
|
let context_ref = TX_CONTEXTS[idx].borrow(cs);
|
||||||
|
*context_ref.borrow_mut() = context;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Copy, Clone)]
|
||||||
|
pub struct TxContext {
|
||||||
|
progress: usize,
|
||||||
|
tx_overrun: bool,
|
||||||
|
slice: RawBufSlice,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[allow(clippy::new_without_default)]
|
||||||
|
impl TxContext {
|
||||||
|
pub const fn new() -> Self {
|
||||||
|
Self {
|
||||||
|
progress: 0,
|
||||||
|
tx_overrun: false,
|
||||||
|
slice: RawBufSlice::new_empty(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Copy, Clone)]
|
||||||
|
struct RawBufSlice {
|
||||||
|
data: *const u8,
|
||||||
|
len: usize,
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Safety: This type MUST be used with mutex to ensure concurrent access is valid.
|
||||||
|
unsafe impl Send for RawBufSlice {}
|
||||||
|
|
||||||
|
impl RawBufSlice {
|
||||||
|
/// # Safety
|
||||||
|
///
|
||||||
|
/// This function stores the raw pointer of the passed data slice. The user MUST ensure
|
||||||
|
/// that the slice outlives the data structure.
|
||||||
|
#[allow(dead_code)]
|
||||||
|
const unsafe fn new(data: &[u8]) -> Self {
|
||||||
|
Self {
|
||||||
|
data: data.as_ptr(),
|
||||||
|
len: data.len(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const fn new_empty() -> Self {
|
||||||
|
Self {
|
||||||
|
data: core::ptr::null(),
|
||||||
|
len: 0,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// # Safety
|
||||||
|
///
|
||||||
|
/// This function stores the raw pointer of the passed data slice. The user MUST ensure
|
||||||
|
/// that the slice outlives the data structure.
|
||||||
|
pub unsafe fn set(&mut self, data: &[u8]) {
|
||||||
|
self.data = data.as_ptr();
|
||||||
|
self.len = data.len();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub struct TxFuture {
|
||||||
|
uart_idx: usize,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl TxFuture {
|
||||||
|
/// # Safety
|
||||||
|
///
|
||||||
|
/// This function stores the raw pointer of the passed data slice. The user MUST ensure
|
||||||
|
/// that the slice outlives the data structure.
|
||||||
|
pub unsafe fn new<Uart: Instance>(tx: &mut Tx<Uart>, data: &[u8]) -> Self {
|
||||||
|
TX_DONE[Uart::IDX as usize].store(false, core::sync::atomic::Ordering::Relaxed);
|
||||||
|
tx.disable_interrupts();
|
||||||
|
tx.disable();
|
||||||
|
tx.clear_fifo();
|
||||||
|
|
||||||
|
let uart_tx = unsafe { tx.uart() };
|
||||||
|
let init_fill_count = core::cmp::min(data.len(), 16);
|
||||||
|
// We fill the FIFO.
|
||||||
|
for data in data.iter().take(init_fill_count) {
|
||||||
|
uart_tx.data().write(|w| unsafe { w.bits(*data as u32) });
|
||||||
|
}
|
||||||
|
critical_section::with(|cs| {
|
||||||
|
let context_ref = TX_CONTEXTS[Uart::IDX as usize].borrow(cs);
|
||||||
|
let mut context = context_ref.borrow_mut();
|
||||||
|
context.slice.set(data);
|
||||||
|
context.progress = init_fill_count;
|
||||||
|
|
||||||
|
// Ensure those are enabled inside a critical section at the same time. Can lead to
|
||||||
|
// weird glitches otherwise.
|
||||||
|
tx.enable_interrupts();
|
||||||
|
tx.enable();
|
||||||
|
});
|
||||||
|
Self {
|
||||||
|
uart_idx: Uart::IDX as usize,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Future for TxFuture {
|
||||||
|
type Output = Result<usize, TxOverrunError>;
|
||||||
|
|
||||||
|
fn poll(
|
||||||
|
self: core::pin::Pin<&mut Self>,
|
||||||
|
cx: &mut core::task::Context<'_>,
|
||||||
|
) -> core::task::Poll<Self::Output> {
|
||||||
|
UART_TX_WAKERS[self.uart_idx].register(cx.waker());
|
||||||
|
if TX_DONE[self.uart_idx].swap(false, core::sync::atomic::Ordering::Relaxed) {
|
||||||
|
let progress = critical_section::with(|cs| {
|
||||||
|
TX_CONTEXTS[self.uart_idx].borrow(cs).borrow().progress
|
||||||
|
});
|
||||||
|
return core::task::Poll::Ready(Ok(progress));
|
||||||
|
}
|
||||||
|
core::task::Poll::Pending
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Drop for TxFuture {
|
||||||
|
fn drop(&mut self) {
|
||||||
|
let reg_block = match self.uart_idx {
|
||||||
|
0 => unsafe { pac::Uarta::reg_block() },
|
||||||
|
1 => unsafe { pac::Uartb::reg_block() },
|
||||||
|
_ => unreachable!(),
|
||||||
|
};
|
||||||
|
|
||||||
|
disable_tx_interrupts(reg_block);
|
||||||
|
disable_tx(reg_block);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub struct TxAsync<Uart: Instance> {
|
||||||
|
tx: Tx<Uart>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<Uart: Instance> TxAsync<Uart> {
|
||||||
|
pub fn new(tx: Tx<Uart>) -> Self {
|
||||||
|
Self { tx }
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn release(self) -> Tx<Uart> {
|
||||||
|
self.tx
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, thiserror::Error)]
|
||||||
|
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
|
||||||
|
#[error("TX overrun error")]
|
||||||
|
pub struct TxOverrunError;
|
||||||
|
|
||||||
|
impl embedded_io_async::Error for TxOverrunError {
|
||||||
|
fn kind(&self) -> embedded_io_async::ErrorKind {
|
||||||
|
embedded_io_async::ErrorKind::Other
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<Uart: Instance> embedded_io::ErrorType for TxAsync<Uart> {
|
||||||
|
type Error = TxOverrunError;
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<Uart: Instance> Write for TxAsync<Uart> {
|
||||||
|
/// Write a buffer asynchronously.
|
||||||
|
///
|
||||||
|
/// This implementation is not side effect free, and a started future might have already
|
||||||
|
/// written part of the passed buffer.
|
||||||
|
async fn write(&mut self, buf: &[u8]) -> Result<usize, Self::Error> {
|
||||||
|
let fut = unsafe { TxFuture::new(&mut self.tx, buf) };
|
||||||
|
fut.await
|
||||||
|
}
|
||||||
|
}
|
@ -13,7 +13,7 @@ categories = ["embedded", "no-std", "hardware-support"]
|
|||||||
[dependencies]
|
[dependencies]
|
||||||
cortex-m = "0.7"
|
cortex-m = "0.7"
|
||||||
vcell = "0.1.3"
|
vcell = "0.1.3"
|
||||||
defmt = { version = "1", optional = true }
|
defmt = { version = "0.3", optional = true }
|
||||||
critical-section = { version = "1", optional = true }
|
critical-section = { version = "1", optional = true }
|
||||||
|
|
||||||
[dependencies.cortex-m-rt]
|
[dependencies.cortex-m-rt]
|
||||||
|
@ -8,11 +8,6 @@ and this project adheres to [Semantic Versioning](http://semver.org/).
|
|||||||
|
|
||||||
## [unreleased]
|
## [unreleased]
|
||||||
|
|
||||||
## [v0.8.1] 2025-03-07
|
|
||||||
|
|
||||||
- Bumped allowed `va108xx-hal` dependency to 0.11
|
|
||||||
- Bumped `bitfield` dependency
|
|
||||||
|
|
||||||
## [v0.8.0] 2025-02-17
|
## [v0.8.0] 2025-02-17
|
||||||
|
|
||||||
- Bumped `va108xx-hal` dependency to 0.10
|
- Bumped `va108xx-hal` dependency to 0.10
|
||||||
@ -61,9 +56,3 @@ and this project adheres to [Semantic Versioning](http://semver.org/).
|
|||||||
- Added basic accelerometer example. Board in not populated so it is not complete, but
|
- Added basic accelerometer example. Board in not populated so it is not complete, but
|
||||||
it provides a starting point
|
it provides a starting point
|
||||||
- Added ADC base library and example building on the new max116xx-10bit device driver crate
|
- Added ADC base library and example building on the new max116xx-10bit device driver crate
|
||||||
|
|
||||||
[unreleased]: https://egit.irs.uni-stuttgart.de/rust/va108xx-rs/compare/vorago-reb1-v0.8.1...HEAD
|
|
||||||
[v0.8.1]: https://egit.irs.uni-stuttgart.de/rust/va108xx-rs/compare/vorago-reb1-v0.8.0...vorago-reb1-v0.8.1
|
|
||||||
[v0.8.0]: https://egit.irs.uni-stuttgart.de/rust/va108xx-rs/compare/vorago-reb1-v0.7.0...vorago-reb1-v0.8.0
|
|
||||||
[v0.7.0]: https://egit.irs.uni-stuttgart.de/rust/va108xx-rs/compare/vorago-reb1-v0.6.0...vorago-reb1-v0.7.0
|
|
||||||
[v0.6.0]: https://egit.irs.uni-stuttgart.de/rust/va108xx-rs/compare/vorago-reb1-v0.5.0...vorago-reb1-v0.6.0
|
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
[package]
|
[package]
|
||||||
name = "vorago-reb1"
|
name = "vorago-reb1"
|
||||||
version = "0.8.1"
|
version = "0.8.0"
|
||||||
authors = ["Robin Mueller <muellerr@irs.uni-stuttgart.de>"]
|
authors = ["Robin Mueller <muellerr@irs.uni-stuttgart.de>"]
|
||||||
edition = "2021"
|
edition = "2021"
|
||||||
description = "Board Support Crate for the Vorago REB1 development board"
|
description = "Board Support Crate for the Vorago REB1 development board"
|
||||||
@ -15,8 +15,7 @@ cortex-m = { version = "0.7", features = ["critical-section-single-core"] }
|
|||||||
cortex-m-rt = "0.7"
|
cortex-m-rt = "0.7"
|
||||||
embedded-hal = "1"
|
embedded-hal = "1"
|
||||||
nb = "1"
|
nb = "1"
|
||||||
bitbybit = "1.3"
|
bitfield = ">=0.17, <=0.18"
|
||||||
arbitrary-int = "1.3"
|
|
||||||
max116xx-10bit = "0.3"
|
max116xx-10bit = "0.3"
|
||||||
|
|
||||||
va108xx-hal = { version = ">=0.10, <=0.11", path = "../va108xx-hal", features = ["rt"] }
|
va108xx-hal = { version = ">=0.10, <=0.11", path = "../va108xx-hal", features = ["rt"] }
|
||||||
|
@ -4,17 +4,17 @@ use cortex_m_rt::entry;
|
|||||||
use embedded_hal::delay::DelayNs;
|
use embedded_hal::delay::DelayNs;
|
||||||
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::{pac, prelude::*, timer::CountdownTimer};
|
use va108xx_hal::{pac, prelude::*, timer::set_up_ms_delay_provider};
|
||||||
use vorago_reb1::temp_sensor::Adt75TempSensor;
|
use vorago_reb1::temp_sensor::Adt75TempSensor;
|
||||||
|
|
||||||
#[entry]
|
#[entry]
|
||||||
fn main() -> ! {
|
fn main() -> ! {
|
||||||
rtt_init_print!();
|
rtt_init_print!();
|
||||||
rprintln!("-- Vorago Temperature Sensor and I2C Example --");
|
rprintln!("-- Vorago Temperature Sensor and I2C Example --");
|
||||||
let dp = pac::Peripherals::take().unwrap();
|
let mut dp = pac::Peripherals::take().unwrap();
|
||||||
let mut delay = CountdownTimer::new(dp.tim0, 50.MHz());
|
let mut delay = set_up_ms_delay_provider(&mut dp.sysconfig, 50.MHz(), dp.tim0);
|
||||||
let mut temp_sensor =
|
let mut temp_sensor = Adt75TempSensor::new(&mut dp.sysconfig, 50.MHz(), dp.i2ca)
|
||||||
Adt75TempSensor::new(50.MHz(), dp.i2ca).expect("Creating temperature sensor struct failed");
|
.expect("Creating temperature sensor struct failed");
|
||||||
loop {
|
loop {
|
||||||
let temp = temp_sensor
|
let temp = temp_sensor
|
||||||
.read_temperature()
|
.read_temperature()
|
||||||
|
@ -9,14 +9,13 @@ use embedded_hal::delay::DelayNs;
|
|||||||
use embedded_hal::spi::{SpiBus, MODE_3};
|
use embedded_hal::spi::{SpiBus, MODE_3};
|
||||||
use panic_rtt_target as _;
|
use panic_rtt_target as _;
|
||||||
use rtt_target::{rprintln, rtt_init_print};
|
use rtt_target::{rprintln, rtt_init_print};
|
||||||
use va108xx_hal::gpio::{Output, PinState};
|
use va108xx_hal::spi::SpiClkConfig;
|
||||||
use va108xx_hal::pins::PinsA;
|
|
||||||
use va108xx_hal::spi::{configure_pin_as_hw_cs_pin, SpiClkConfig};
|
|
||||||
use va108xx_hal::timer::CountdownTimer;
|
|
||||||
use va108xx_hal::{
|
use va108xx_hal::{
|
||||||
|
gpio::PinsA,
|
||||||
pac,
|
pac,
|
||||||
prelude::*,
|
prelude::*,
|
||||||
spi::{Spi, SpiConfig},
|
spi::{Spi, SpiConfig},
|
||||||
|
timer::set_up_ms_delay_provider,
|
||||||
};
|
};
|
||||||
|
|
||||||
const READ_MASK: u8 = 1 << 7;
|
const READ_MASK: u8 = 1 << 7;
|
||||||
@ -30,15 +29,19 @@ const PWR_MEASUREMENT_MODE_MASK: u8 = 1 << 3;
|
|||||||
fn main() -> ! {
|
fn main() -> ! {
|
||||||
rtt_init_print!();
|
rtt_init_print!();
|
||||||
rprintln!("-- Vorago Accelerometer Example --");
|
rprintln!("-- Vorago Accelerometer Example --");
|
||||||
let dp = pac::Peripherals::take().unwrap();
|
let mut dp = pac::Peripherals::take().unwrap();
|
||||||
let pinsa = PinsA::new(dp.porta);
|
let mut delay = set_up_ms_delay_provider(&mut dp.sysconfig, 50.MHz(), dp.tim0);
|
||||||
let mut delay = CountdownTimer::new(dp.tim0, 50.MHz());
|
let pinsa = PinsA::new(&mut dp.sysconfig, dp.porta);
|
||||||
let (sck, mosi, miso) = (pinsa.pa20, pinsa.pa19, pinsa.pa18);
|
let (sck, mosi, miso) = (
|
||||||
let cs_pin = pinsa.pa16;
|
pinsa.pa20.into_funsel_2(),
|
||||||
let hw_cs_id = configure_pin_as_hw_cs_pin(cs_pin);
|
pinsa.pa19.into_funsel_2(),
|
||||||
|
pinsa.pa18.into_funsel_2(),
|
||||||
|
);
|
||||||
|
let cs_pin = pinsa.pa16.into_funsel_2();
|
||||||
|
|
||||||
// Need to set the ADC chip select low
|
// Need to set the ADC chip select low
|
||||||
Output::new(pinsa.pa17, PinState::Low);
|
let mut adc_cs = pinsa.pa17.into_push_pull_output();
|
||||||
|
adc_cs.set_high();
|
||||||
|
|
||||||
let spi_cfg = SpiConfig::default()
|
let spi_cfg = SpiConfig::default()
|
||||||
.clk_cfg(
|
.clk_cfg(
|
||||||
@ -46,8 +49,14 @@ fn main() -> ! {
|
|||||||
)
|
)
|
||||||
.mode(MODE_3)
|
.mode(MODE_3)
|
||||||
.slave_output_disable(true);
|
.slave_output_disable(true);
|
||||||
let mut spi = Spi::new(dp.spib, (sck, miso, mosi), spi_cfg).unwrap();
|
let mut spi = Spi::new(
|
||||||
spi.cfg_hw_cs(hw_cs_id);
|
&mut dp.sysconfig,
|
||||||
|
50.MHz(),
|
||||||
|
dp.spib,
|
||||||
|
(sck, miso, mosi),
|
||||||
|
spi_cfg,
|
||||||
|
);
|
||||||
|
spi.cfg_hw_cs_with_pin(&cs_pin);
|
||||||
|
|
||||||
let mut tx_rx_buf: [u8; 3] = [0; 3];
|
let mut tx_rx_buf: [u8; 3] = [0; 3];
|
||||||
tx_rx_buf[0] = READ_MASK | DEVID_REG;
|
tx_rx_buf[0] = READ_MASK | DEVID_REG;
|
||||||
|
@ -10,10 +10,10 @@ 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::{
|
||||||
clock::{set_clk_div_register, FilterClkSel},
|
clock::{set_clk_div_register, FilterClkSel},
|
||||||
gpio::{FilterType, InterruptEdge},
|
gpio::{FilterType, InterruptEdge, PinsA},
|
||||||
pac::{self, interrupt},
|
pac::{self, interrupt},
|
||||||
pins::PinsA,
|
prelude::*,
|
||||||
timer::InterruptConfig,
|
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;
|
||||||
@ -35,18 +35,18 @@ fn main() -> ! {
|
|||||||
rtt_init_print!();
|
rtt_init_print!();
|
||||||
rprintln!("-- Vorago Button IRQ Example --");
|
rprintln!("-- Vorago Button IRQ Example --");
|
||||||
let mut dp = pac::Peripherals::take().unwrap();
|
let mut dp = pac::Peripherals::take().unwrap();
|
||||||
let pinsa = PinsA::new(dp.porta);
|
let pinsa = PinsA::new(&mut dp.sysconfig, dp.porta);
|
||||||
let edge_irq = match PRESS_MODE {
|
let edge_irq = match PRESS_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);
|
let mut button = Button::new(pinsa.pa11.into_floating_input());
|
||||||
|
|
||||||
if PRESS_MODE == PressMode::Toggle {
|
if PRESS_MODE == PressMode::Toggle {
|
||||||
// This filter debounces the switch for edge based interrupts
|
// This filter debounces the switch for edge based interrupts
|
||||||
button.configure_filter_type(FilterType::FilterFourCycles, FilterClkSel::Clk1);
|
button.configure_filter_type(FilterType::FilterFourClockCycles, FilterClkSel::Clk1);
|
||||||
set_clk_div_register(&mut dp.sysconfig, FilterClkSel::Clk1, 50_000);
|
set_clk_div_register(&mut dp.sysconfig, FilterClkSel::Clk1, 50_000);
|
||||||
}
|
}
|
||||||
button.configure_and_enable_edge_interrupt(
|
button.configure_and_enable_edge_interrupt(
|
||||||
@ -54,7 +54,18 @@ fn main() -> ! {
|
|||||||
InterruptConfig::new(pac::interrupt::OC15, true, true),
|
InterruptConfig::new(pac::interrupt::OC15, true, true),
|
||||||
);
|
);
|
||||||
|
|
||||||
let mut leds = Leds::new(pinsa.pa10, pinsa.pa7, pinsa.pa6);
|
set_up_ms_tick(
|
||||||
|
InterruptConfig::new(pac::Interrupt::OC0, true, true),
|
||||||
|
&mut dp.sysconfig,
|
||||||
|
Some(&mut dp.irqsel),
|
||||||
|
50.MHz(),
|
||||||
|
dp.tim0,
|
||||||
|
);
|
||||||
|
let mut leds = Leds::new(
|
||||||
|
pinsa.pa10.into_push_pull_output(),
|
||||||
|
pinsa.pa7.into_push_pull_output(),
|
||||||
|
pinsa.pa6.into_push_pull_output(),
|
||||||
|
);
|
||||||
for led in leds.iter_mut() {
|
for led in leds.iter_mut() {
|
||||||
led.off();
|
led.off();
|
||||||
}
|
}
|
||||||
@ -68,6 +79,11 @@ fn main() -> ! {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[interrupt]
|
||||||
|
fn OC0() {
|
||||||
|
default_ms_irq_handler();
|
||||||
|
}
|
||||||
|
|
||||||
#[interrupt]
|
#[interrupt]
|
||||||
fn OC15() {
|
fn OC15() {
|
||||||
cortex_m::interrupt::free(|cs| {
|
cortex_m::interrupt::free(|cs| {
|
||||||
|
@ -9,13 +9,7 @@
|
|||||||
use cortex_m_rt::entry;
|
use cortex_m_rt::entry;
|
||||||
use embedded_hal::delay::DelayNs;
|
use embedded_hal::delay::DelayNs;
|
||||||
use panic_halt as _;
|
use panic_halt as _;
|
||||||
use va108xx_hal::{
|
use va108xx_hal::{gpio::PinsA, pac, prelude::*, timer::set_up_ms_delay_provider};
|
||||||
gpio::{Output, PinState},
|
|
||||||
pac,
|
|
||||||
pins::PinsA,
|
|
||||||
prelude::*,
|
|
||||||
timer::CountdownTimer,
|
|
||||||
};
|
|
||||||
use vorago_reb1::leds::Leds;
|
use vorago_reb1::leds::Leds;
|
||||||
|
|
||||||
// REB LED pin definitions. All on port A
|
// REB LED pin definitions. All on port A
|
||||||
@ -32,7 +26,7 @@ enum LibType {
|
|||||||
|
|
||||||
#[entry]
|
#[entry]
|
||||||
fn main() -> ! {
|
fn main() -> ! {
|
||||||
let dp = pac::Peripherals::take().unwrap();
|
let mut dp = pac::Peripherals::take().unwrap();
|
||||||
|
|
||||||
let lib_type = LibType::Bsp;
|
let lib_type = LibType::Bsp;
|
||||||
|
|
||||||
@ -66,11 +60,11 @@ fn main() -> ! {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
LibType::Hal => {
|
LibType::Hal => {
|
||||||
let pins = PinsA::new(dp.porta);
|
let pins = PinsA::new(&mut dp.sysconfig, dp.porta);
|
||||||
let mut led1 = Output::new(pins.pa10, PinState::Low);
|
let mut led1 = pins.pa10.into_readable_push_pull_output();
|
||||||
let mut led2 = Output::new(pins.pa7, PinState::Low);
|
let mut led2 = pins.pa7.into_readable_push_pull_output();
|
||||||
let mut led3 = Output::new(pins.pa6, PinState::Low);
|
let mut led3 = pins.pa6.into_readable_push_pull_output();
|
||||||
let mut delay = CountdownTimer::new(dp.tim0, 50.MHz());
|
let mut delay = set_up_ms_delay_provider(&mut dp.sysconfig, 50.MHz(), dp.tim0);
|
||||||
for _ in 0..10 {
|
for _ in 0..10 {
|
||||||
led1.set_low();
|
led1.set_low();
|
||||||
led2.set_low();
|
led2.set_low();
|
||||||
@ -89,9 +83,13 @@ fn main() -> ! {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
LibType::Bsp => {
|
LibType::Bsp => {
|
||||||
let pinsa = PinsA::new(dp.porta);
|
let pinsa = PinsA::new(&mut dp.sysconfig, dp.porta);
|
||||||
let mut leds = Leds::new(pinsa.pa10, pinsa.pa7, pinsa.pa6);
|
let mut leds = Leds::new(
|
||||||
let mut delay = CountdownTimer::new(dp.tim0, 50.MHz());
|
pinsa.pa10.into_push_pull_output(),
|
||||||
|
pinsa.pa7.into_push_pull_output(),
|
||||||
|
pinsa.pa6.into_push_pull_output(),
|
||||||
|
);
|
||||||
|
let mut delay = set_up_ms_delay_provider(&mut dp.sysconfig, 50.MHz(), dp.tim0);
|
||||||
for _ in 0..10 {
|
for _ in 0..10 {
|
||||||
// Blink all LEDs quickly
|
// Blink all LEDs quickly
|
||||||
for led in leds.iter_mut() {
|
for led in leds.iter_mut() {
|
||||||
|
@ -14,19 +14,20 @@ use max116xx_10bit::VoltageRefMode;
|
|||||||
use max116xx_10bit::{AveragingConversions, AveragingResults};
|
use max116xx_10bit::{AveragingConversions, AveragingResults};
|
||||||
use panic_rtt_target as _;
|
use panic_rtt_target as _;
|
||||||
use rtt_target::{rprintln, rtt_init_print};
|
use rtt_target::{rprintln, rtt_init_print};
|
||||||
use va108xx_hal::gpio::{Input, Output, PinState, Port};
|
use va108xx_hal::gpio::Port;
|
||||||
use va108xx_hal::pins::PinsA;
|
use va108xx_hal::spi::{OptionalHwCs, SpiClkConfig};
|
||||||
use va108xx_hal::spi::{configure_pin_as_hw_cs_pin, SpiClkConfig};
|
|
||||||
use va108xx_hal::timer::CountdownTimer;
|
use va108xx_hal::timer::CountdownTimer;
|
||||||
use va108xx_hal::{
|
use va108xx_hal::{
|
||||||
pac,
|
gpio::PinsA,
|
||||||
|
pac::{self, interrupt},
|
||||||
prelude::*,
|
prelude::*,
|
||||||
spi::{HwChipSelectId, Spi, SpiConfig},
|
spi::{Spi, SpiBase, SpiConfig},
|
||||||
|
timer::{default_ms_irq_handler, set_up_ms_tick, DelayMs, InterruptConfig},
|
||||||
};
|
};
|
||||||
use va108xx_hal::{port_function_select, FunSel};
|
use va108xx_hal::{port_function_select, FunSel};
|
||||||
use vorago_reb1::max11619::{
|
use vorago_reb1::max11619::{
|
||||||
max11619_externally_clocked_no_wakeup, max11619_externally_clocked_with_wakeup,
|
max11619_externally_clocked_no_wakeup, max11619_externally_clocked_with_wakeup,
|
||||||
max11619_internally_clocked, AN2_CHANNEL, POTENTIOMETER_CHANNEL,
|
max11619_internally_clocked, EocPin, AN2_CHANNEL, POTENTIOMETER_CHANNEL,
|
||||||
};
|
};
|
||||||
|
|
||||||
#[derive(Debug, PartialEq, Copy, Clone)]
|
#[derive(Debug, PartialEq, Copy, Clone)]
|
||||||
@ -56,34 +57,34 @@ const MUX_MODE: MuxMode = MuxMode::None;
|
|||||||
|
|
||||||
// This is probably more or less a re-implementation of https://docs.rs/embedded-hal-bus/latest/embedded_hal_bus/spi/struct.ExclusiveDevice.html.
|
// This is probably more or less a re-implementation of https://docs.rs/embedded-hal-bus/latest/embedded_hal_bus/spi/struct.ExclusiveDevice.html.
|
||||||
// Users should look at the embedded-hal-bus crate for sharing the bus.
|
// Users should look at the embedded-hal-bus crate for sharing the bus.
|
||||||
pub struct SpiWithHwCs<Delay> {
|
pub struct SpiWithHwCs<Delay, HwCs> {
|
||||||
inner: Spi<u8>,
|
inner: SpiBase<pac::Spib, u8>,
|
||||||
delay_provider: Delay,
|
delay_provider: Delay,
|
||||||
hw_cs_id: HwChipSelectId,
|
hw_cs: HwCs,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<Delay: DelayNs> SpiWithHwCs<Delay> {
|
impl<Delay: DelayNs, HwCs: OptionalHwCs<pac::Spib>> SpiWithHwCs<Delay, HwCs> {
|
||||||
pub fn new(spi: Spi<u8>, hw_cs_id: HwChipSelectId, delay_provider: Delay) -> Self {
|
pub fn new(spi: SpiBase<pac::Spib, u8>, hw_cs: HwCs, delay_provider: Delay) -> Self {
|
||||||
Self {
|
Self {
|
||||||
inner: spi,
|
inner: spi,
|
||||||
hw_cs_id,
|
hw_cs,
|
||||||
delay_provider,
|
delay_provider,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<Delay> embedded_hal::spi::ErrorType for SpiWithHwCs<Delay> {
|
impl<Delay, HwCs> embedded_hal::spi::ErrorType for SpiWithHwCs<Delay, HwCs> {
|
||||||
type Error = Infallible;
|
type Error = Infallible;
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<Delay: DelayNs> SpiDevice for SpiWithHwCs<Delay> {
|
impl<Delay: DelayNs, HwCs: OptionalHwCs<pac::Spib>> SpiDevice for SpiWithHwCs<Delay, HwCs> {
|
||||||
fn transaction(
|
fn transaction(
|
||||||
&mut self,
|
&mut self,
|
||||||
operations: &mut [spi::Operation<'_, u8>],
|
operations: &mut [spi::Operation<'_, u8>],
|
||||||
) -> Result<(), Self::Error> {
|
) -> Result<(), Self::Error> {
|
||||||
// Only the HW CS is configured here. This is not really necessary, but showcases
|
// Only the HW CS is configured here. This is not really necessary, but showcases
|
||||||
// that we could scale this multiple SPI devices.
|
// that we could scale this multiple SPI devices.
|
||||||
self.inner.cfg_hw_cs(self.hw_cs_id);
|
self.inner.cfg_hw_cs_with_pin(&self.hw_cs);
|
||||||
for operation in operations {
|
for operation in operations {
|
||||||
match operation {
|
match operation {
|
||||||
spi::Operation::Read(buf) => self.inner.read(buf).ok().unwrap(),
|
spi::Operation::Read(buf) => self.inner.read(buf).ok().unwrap(),
|
||||||
@ -110,17 +111,28 @@ fn main() -> ! {
|
|||||||
rprintln!("-- Vorago ADC Example --");
|
rprintln!("-- Vorago ADC Example --");
|
||||||
|
|
||||||
let mut dp = pac::Peripherals::take().unwrap();
|
let mut dp = pac::Peripherals::take().unwrap();
|
||||||
let mut delay = CountdownTimer::new(dp.tim0, SYS_CLK);
|
let tim0 = set_up_ms_tick(
|
||||||
|
InterruptConfig::new(pac::Interrupt::OC0, true, true),
|
||||||
|
&mut dp.sysconfig,
|
||||||
|
Some(&mut dp.irqsel),
|
||||||
|
SYS_CLK,
|
||||||
|
dp.tim0,
|
||||||
|
);
|
||||||
|
let delay = DelayMs::new(tim0).unwrap();
|
||||||
unsafe {
|
unsafe {
|
||||||
cortex_m::peripheral::NVIC::unmask(pac::Interrupt::OC0);
|
cortex_m::peripheral::NVIC::unmask(pac::Interrupt::OC0);
|
||||||
}
|
}
|
||||||
|
|
||||||
let pinsa = PinsA::new(dp.porta);
|
let pinsa = PinsA::new(&mut dp.sysconfig, dp.porta);
|
||||||
let spi_cfg = SpiConfig::default()
|
let spi_cfg = SpiConfig::default()
|
||||||
.clk_cfg(SpiClkConfig::from_clk(SYS_CLK, 3.MHz()).unwrap())
|
.clk_cfg(SpiClkConfig::from_clk(SYS_CLK, 3.MHz()).unwrap())
|
||||||
.mode(MODE_0)
|
.mode(MODE_0)
|
||||||
.blockmode(true);
|
.blockmode(true);
|
||||||
let (sck, mosi, miso) = (pinsa.pa20, pinsa.pa19, pinsa.pa18);
|
let (sck, mosi, miso) = (
|
||||||
|
pinsa.pa20.into_funsel_2(),
|
||||||
|
pinsa.pa19.into_funsel_2(),
|
||||||
|
pinsa.pa18.into_funsel_2(),
|
||||||
|
);
|
||||||
|
|
||||||
if MUX_MODE == MuxMode::PortB19to17 {
|
if MUX_MODE == MuxMode::PortB19to17 {
|
||||||
port_function_select(&mut dp.ioconfig, Port::B, 19, FunSel::Sel1).ok();
|
port_function_select(&mut dp.ioconfig, Port::B, 19, FunSel::Sel1).ok();
|
||||||
@ -129,30 +141,39 @@ fn main() -> ! {
|
|||||||
port_function_select(&mut dp.ioconfig, Port::B, 16, FunSel::Sel1).ok();
|
port_function_select(&mut dp.ioconfig, Port::B, 16, FunSel::Sel1).ok();
|
||||||
}
|
}
|
||||||
// Set the accelerometer chip select low in case the board slot is populated
|
// Set the accelerometer chip select low in case the board slot is populated
|
||||||
Output::new(pinsa.pa16, PinState::Low);
|
let mut accel_cs = pinsa.pa16.into_push_pull_output();
|
||||||
|
accel_cs.set_high();
|
||||||
|
|
||||||
let hw_cs_id = configure_pin_as_hw_cs_pin(pinsa.pa17);
|
let spi = Spi::new(
|
||||||
let spi = Spi::new(dp.spib, (sck, miso, mosi), spi_cfg).unwrap();
|
&mut dp.sysconfig,
|
||||||
|
50.MHz(),
|
||||||
let delay_spi = CountdownTimer::new(dp.tim1, SYS_CLK);
|
dp.spib,
|
||||||
let spi_with_hwcs = SpiWithHwCs::new(spi, hw_cs_id, delay_spi);
|
(sck, miso, mosi),
|
||||||
|
spi_cfg,
|
||||||
|
)
|
||||||
|
.downgrade();
|
||||||
|
let delay_provider = CountdownTimer::new(&mut dp.sysconfig, 50.MHz(), dp.tim1);
|
||||||
|
let spi_with_hwcs = SpiWithHwCs::new(spi, pinsa.pa17.into_funsel_2(), delay_provider);
|
||||||
match EXAMPLE_MODE {
|
match EXAMPLE_MODE {
|
||||||
ExampleMode::NotUsingEoc => spi_example_externally_clocked(spi_with_hwcs, &mut delay),
|
ExampleMode::NotUsingEoc => spi_example_externally_clocked(spi_with_hwcs, delay),
|
||||||
ExampleMode::UsingEoc => {
|
ExampleMode::UsingEoc => {
|
||||||
spi_example_internally_clocked(
|
spi_example_internally_clocked(spi_with_hwcs, delay, pinsa.pa14.into_floating_input());
|
||||||
spi_with_hwcs,
|
|
||||||
&mut delay,
|
|
||||||
Input::new_floating(pinsa.pa14),
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
ExampleMode::NotUsingEocWithDelay => {
|
ExampleMode::NotUsingEocWithDelay => {
|
||||||
spi_example_externally_clocked_with_delay(spi_with_hwcs, &mut delay);
|
let delay_us = CountdownTimer::new(&mut dp.sysconfig, 50.MHz(), dp.tim2);
|
||||||
|
spi_example_externally_clocked_with_delay(spi_with_hwcs, delay, delay_us);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[interrupt]
|
||||||
|
#[allow(non_snake_case)]
|
||||||
|
fn OC0() {
|
||||||
|
default_ms_irq_handler();
|
||||||
|
}
|
||||||
|
|
||||||
/// Use the SPI clock as the conversion clock
|
/// Use the SPI clock as the conversion clock
|
||||||
fn spi_example_externally_clocked(spi: impl SpiDevice, delay: &mut impl DelayNs) -> ! {
|
fn spi_example_externally_clocked(spi: impl SpiDevice, mut delay: DelayMs) -> ! {
|
||||||
let mut adc = max11619_externally_clocked_no_wakeup(spi)
|
let mut adc = max11619_externally_clocked_no_wakeup(spi)
|
||||||
.expect("Creating externally clocked MAX11619 device failed");
|
.expect("Creating externally clocked MAX11619 device failed");
|
||||||
if READ_MODE == ReadMode::AverageN {
|
if READ_MODE == ReadMode::AverageN {
|
||||||
@ -207,7 +228,11 @@ fn spi_example_externally_clocked(spi: impl SpiDevice, delay: &mut impl DelayNs)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn spi_example_externally_clocked_with_delay(spi: impl SpiDevice, delay: &mut impl DelayNs) -> ! {
|
fn spi_example_externally_clocked_with_delay(
|
||||||
|
spi: impl SpiDevice,
|
||||||
|
mut delay: DelayMs,
|
||||||
|
mut delay_us: impl DelayNs,
|
||||||
|
) -> ! {
|
||||||
let mut adc =
|
let mut adc =
|
||||||
max11619_externally_clocked_with_wakeup(spi).expect("Creating MAX116xx device failed");
|
max11619_externally_clocked_with_wakeup(spi).expect("Creating MAX116xx device failed");
|
||||||
let mut cmd_buf: [u8; 32] = [0; 32];
|
let mut cmd_buf: [u8; 32] = [0; 32];
|
||||||
@ -219,7 +244,7 @@ fn spi_example_externally_clocked_with_delay(spi: impl SpiDevice, delay: &mut im
|
|||||||
ReadMode::Single => {
|
ReadMode::Single => {
|
||||||
rprintln!("Reading single potentiometer channel");
|
rprintln!("Reading single potentiometer channel");
|
||||||
let pot_val = adc
|
let pot_val = adc
|
||||||
.read_single_channel(&mut cmd_buf, POTENTIOMETER_CHANNEL, delay)
|
.read_single_channel(&mut cmd_buf, POTENTIOMETER_CHANNEL, &mut delay_us)
|
||||||
.expect("Creating externally clocked MAX11619 ADC failed");
|
.expect("Creating externally clocked MAX11619 ADC failed");
|
||||||
rprintln!("Single channel read:");
|
rprintln!("Single channel read:");
|
||||||
rprintln!("\tPotentiometer value: {}", pot_val);
|
rprintln!("\tPotentiometer value: {}", pot_val);
|
||||||
@ -230,7 +255,7 @@ fn spi_example_externally_clocked_with_delay(spi: impl SpiDevice, delay: &mut im
|
|||||||
&mut cmd_buf,
|
&mut cmd_buf,
|
||||||
&mut res_buf.iter_mut(),
|
&mut res_buf.iter_mut(),
|
||||||
POTENTIOMETER_CHANNEL,
|
POTENTIOMETER_CHANNEL,
|
||||||
delay,
|
&mut delay_us,
|
||||||
)
|
)
|
||||||
.expect("Multi-Channel read failed");
|
.expect("Multi-Channel read failed");
|
||||||
print_res_buf(&res_buf);
|
print_res_buf(&res_buf);
|
||||||
@ -241,7 +266,7 @@ fn spi_example_externally_clocked_with_delay(spi: impl SpiDevice, delay: &mut im
|
|||||||
&mut cmd_buf,
|
&mut cmd_buf,
|
||||||
&mut res_buf.iter_mut(),
|
&mut res_buf.iter_mut(),
|
||||||
AN2_CHANNEL,
|
AN2_CHANNEL,
|
||||||
delay,
|
&mut delay_us,
|
||||||
)
|
)
|
||||||
.expect("Multi-Channel read failed");
|
.expect("Multi-Channel read failed");
|
||||||
rprintln!("Multi channel read from 2 to 3:");
|
rprintln!("Multi channel read from 2 to 3:");
|
||||||
@ -258,11 +283,7 @@ fn spi_example_externally_clocked_with_delay(spi: impl SpiDevice, delay: &mut im
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// This function uses the EOC pin to determine whether the conversion finished
|
/// This function uses the EOC pin to determine whether the conversion finished
|
||||||
fn spi_example_internally_clocked(
|
fn spi_example_internally_clocked(spi: impl SpiDevice, mut delay: DelayMs, eoc_pin: EocPin) -> ! {
|
||||||
spi: impl SpiDevice,
|
|
||||||
delay: &mut impl DelayNs,
|
|
||||||
eoc_pin: Input,
|
|
||||||
) -> ! {
|
|
||||||
let mut adc = max11619_internally_clocked(
|
let mut adc = max11619_internally_clocked(
|
||||||
spi,
|
spi,
|
||||||
eoc_pin,
|
eoc_pin,
|
||||||
|
@ -5,7 +5,7 @@ use cortex_m_rt::entry;
|
|||||||
use embedded_hal::delay::DelayNs;
|
use embedded_hal::delay::DelayNs;
|
||||||
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::{pac, spi::SpiClkConfig, time::Hertz, timer::CountdownTimer};
|
use va108xx_hal::{pac, time::Hertz, timer::CountdownTimer};
|
||||||
use vorago_reb1::m95m01::{M95M01, PAGE_SIZE};
|
use vorago_reb1::m95m01::{M95M01, PAGE_SIZE};
|
||||||
|
|
||||||
const CLOCK_FREQ: Hertz = Hertz::from_raw(50_000_000);
|
const CLOCK_FREQ: Hertz = Hertz::from_raw(50_000_000);
|
||||||
@ -15,13 +15,12 @@ fn main() -> ! {
|
|||||||
rtt_init_print!();
|
rtt_init_print!();
|
||||||
rprintln!("-- VA108XX REB1 NVM example --");
|
rprintln!("-- VA108XX REB1 NVM example --");
|
||||||
|
|
||||||
let dp = pac::Peripherals::take().unwrap();
|
let mut dp = pac::Peripherals::take().unwrap();
|
||||||
|
|
||||||
let mut delay = CountdownTimer::new(dp.tim0, CLOCK_FREQ);
|
let mut timer = CountdownTimer::new(&mut dp.sysconfig, CLOCK_FREQ, dp.tim0);
|
||||||
let clk_config = SpiClkConfig::new(2, 4);
|
let mut nvm = M95M01::new(&mut dp.sysconfig, CLOCK_FREQ, dp.spic);
|
||||||
let mut nvm = M95M01::new(dp.spic, clk_config);
|
|
||||||
let status_reg = nvm.read_status_reg().expect("reading status reg failed");
|
let status_reg = nvm.read_status_reg().expect("reading status reg failed");
|
||||||
if status_reg.zero_segment().value() == 0b111 {
|
if status_reg.zero_segment() == 0b111 {
|
||||||
panic!("status register unexpected values");
|
panic!("status register unexpected values");
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -52,6 +51,6 @@ fn main() -> ! {
|
|||||||
|
|
||||||
nvm.write(0, &orig_content).unwrap();
|
nvm.write(0, &orig_content).unwrap();
|
||||||
loop {
|
loop {
|
||||||
delay.delay_ms(500);
|
timer.delay_ms(500);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -5,18 +5,16 @@
|
|||||||
//! - [Button Blinky with IRQs](https://egit.irs.uni-stuttgart.de/rust/va108xx-rs/src/branch/main/vorago-reb1/examples/blinky-button-irq.rs)
|
//! - [Button Blinky with IRQs](https://egit.irs.uni-stuttgart.de/rust/va108xx-rs/src/branch/main/vorago-reb1/examples/blinky-button-irq.rs)
|
||||||
//! - [Button Blinky with IRQs and RTIC](https://egit.irs.uni-stuttgart.de/rust/va108xx-rs/src/branch/main/vorago-reb1/examples/blinky-button-rtic.rs)
|
//! - [Button Blinky with IRQs and RTIC](https://egit.irs.uni-stuttgart.de/rust/va108xx-rs/src/branch/main/vorago-reb1/examples/blinky-button-rtic.rs)
|
||||||
use va108xx_hal::{
|
use va108xx_hal::{
|
||||||
clock::FilterClkSel,
|
gpio::{FilterClkSel, FilterType, InputFloating, InterruptEdge, InterruptLevel, Pin, PA11},
|
||||||
gpio::{FilterType, Input, InterruptEdge, InterruptLevel, Pin},
|
|
||||||
pins::Pa11,
|
|
||||||
InterruptConfig,
|
InterruptConfig,
|
||||||
};
|
};
|
||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
pub struct Button(pub Input);
|
pub struct Button(pub Pin<PA11, InputFloating>);
|
||||||
|
|
||||||
impl Button {
|
impl Button {
|
||||||
pub fn new(pin: Pin<Pa11>) -> Button {
|
pub fn new(pin: Pin<PA11, InputFloating>) -> Button {
|
||||||
Button(Input::new_floating(pin))
|
Button(pin)
|
||||||
}
|
}
|
||||||
|
|
||||||
#[inline]
|
#[inline]
|
||||||
|
@ -6,20 +6,20 @@
|
|||||||
//! - [Button Blinky using IRQs](https://egit.irs.uni-stuttgart.de/rust/va108xx-rs/src/branch/main/vorago-reb1/examples/blinky-button-irq.rs)
|
//! - [Button Blinky using IRQs](https://egit.irs.uni-stuttgart.de/rust/va108xx-rs/src/branch/main/vorago-reb1/examples/blinky-button-irq.rs)
|
||||||
//! - [Button Blinky using IRQs and RTIC](https://egit.irs.uni-stuttgart.de/rust/va108xx-rs/src/branch/main/vorago-reb1/examples/blinky-button-rtic.rs)
|
//! - [Button Blinky using IRQs and RTIC](https://egit.irs.uni-stuttgart.de/rust/va108xx-rs/src/branch/main/vorago-reb1/examples/blinky-button-rtic.rs)
|
||||||
use va108xx_hal::{
|
use va108xx_hal::{
|
||||||
gpio::{Output, PinState},
|
gpio::dynpin::DynPin,
|
||||||
pins::{Pa10, Pa6, Pa7, Pin},
|
gpio::pin::{Pin, PushPullOutput, PA10, PA6, PA7},
|
||||||
};
|
};
|
||||||
|
|
||||||
|
pub type LD2 = Pin<PA10, PushPullOutput>;
|
||||||
|
pub type LD3 = Pin<PA7, PushPullOutput>;
|
||||||
|
pub type LD4 = Pin<PA6, PushPullOutput>;
|
||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
pub struct Leds(pub [Led; 3]);
|
pub struct Leds(pub [Led; 3]);
|
||||||
|
|
||||||
impl Leds {
|
impl Leds {
|
||||||
pub fn new(led_pin1: Pin<Pa10>, led_pin2: Pin<Pa7>, led_pin3: Pin<Pa6>) -> Leds {
|
pub fn new(led_pin1: LD2, led_pin2: LD3, led_pin3: LD4) -> Leds {
|
||||||
Leds([
|
Leds([led_pin1.into(), led_pin2.into(), led_pin3.into()])
|
||||||
Led(Output::new(led_pin1, PinState::Low)),
|
|
||||||
Led(Output::new(led_pin2, PinState::Low)),
|
|
||||||
Led(Output::new(led_pin3, PinState::Low)),
|
|
||||||
])
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -52,24 +52,38 @@ impl core::ops::IndexMut<usize> for Leds {
|
|||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
pub struct Led(Output);
|
pub struct Led(pub DynPin);
|
||||||
|
|
||||||
|
macro_rules! ctor {
|
||||||
|
($($ldx:ident),+) => {
|
||||||
|
$(
|
||||||
|
impl From<$ldx> for Led {
|
||||||
|
fn from(led: $ldx) -> Self {
|
||||||
|
Led(led.into())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
)+
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
ctor!(LD2, LD3, LD4);
|
||||||
|
|
||||||
impl Led {
|
impl Led {
|
||||||
/// Turns the LED off. Setting the pin high actually turns the LED off
|
/// Turns the LED off. Setting the pin high actually turns the LED off
|
||||||
#[inline]
|
#[inline]
|
||||||
pub fn off(&mut self) {
|
pub fn off(&mut self) {
|
||||||
self.0.set_high();
|
self.0.set_high().ok();
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Turns the LED on. Setting the pin low actually turns the LED on
|
/// Turns the LED on. Setting the pin low actually turns the LED on
|
||||||
#[inline]
|
#[inline]
|
||||||
pub fn on(&mut self) {
|
pub fn on(&mut self) {
|
||||||
self.0.set_low();
|
self.0.set_low().ok();
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Toggles the LED
|
/// Toggles the LED
|
||||||
#[inline]
|
#[inline]
|
||||||
pub fn toggle(&mut self) {
|
pub fn toggle(&mut self) {
|
||||||
self.0.toggle();
|
self.0.toggle().ok();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -7,25 +7,20 @@
|
|||||||
//! # Example
|
//! # Example
|
||||||
//!
|
//!
|
||||||
//! - [REB1 EEPROM example](https://egit.irs.uni-stuttgart.de/rust/va108xx-rs/src/branch/main/vorago-reb1/examples/nvm.rs)
|
//! - [REB1 EEPROM example](https://egit.irs.uni-stuttgart.de/rust/va108xx-rs/src/branch/main/vorago-reb1/examples/nvm.rs)
|
||||||
use arbitrary_int::{u2, u3};
|
|
||||||
use core::convert::Infallible;
|
use core::convert::Infallible;
|
||||||
use embedded_hal::spi::SpiBus;
|
use embedded_hal::spi::SpiBus;
|
||||||
|
|
||||||
pub const PAGE_SIZE: usize = 256;
|
pub const PAGE_SIZE: usize = 256;
|
||||||
|
|
||||||
#[bitbybit::bitfield(u8)]
|
bitfield::bitfield! {
|
||||||
#[derive(Debug)]
|
pub struct StatusReg(u8);
|
||||||
pub struct StatusReg {
|
impl Debug;
|
||||||
#[bit(7, r)]
|
u8;
|
||||||
status_register_write_protect: bool,
|
pub status_register_write_protect, _: 7;
|
||||||
#[bits(4..=6, r)]
|
pub zero_segment, _: 6, 4;
|
||||||
zero_segment: u3,
|
pub block_protection_bits, set_block_protection_bits: 3, 2;
|
||||||
#[bits(2..=3, rw)]
|
pub write_enable_latch, _: 1;
|
||||||
block_protection_bits: u2,
|
pub write_in_progress, _: 0;
|
||||||
#[bit(1, r)]
|
|
||||||
write_enable_latch: bool,
|
|
||||||
#[bit(0, r)]
|
|
||||||
write_in_progress: bool,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Registers.
|
// Registers.
|
||||||
@ -47,10 +42,11 @@ pub mod regs {
|
|||||||
use regs::*;
|
use regs::*;
|
||||||
use va108xx_hal::{
|
use va108xx_hal::{
|
||||||
pac,
|
pac,
|
||||||
spi::{Spi, SpiClkConfig, SpiConfig, SpiLowLevel, BMSTART_BMSTOP_MASK},
|
prelude::*,
|
||||||
|
spi::{RomMiso, RomMosi, RomSck, Spi, SpiClkConfig, SpiConfig, BMSTART_BMSTOP_MASK},
|
||||||
};
|
};
|
||||||
|
|
||||||
pub type RomSpi = Spi<u8>;
|
pub type RomSpi = Spi<pac::Spic, (RomSck, RomMiso, RomMosi), u8>;
|
||||||
|
|
||||||
/// Driver for the ST device M95M01 EEPROM memory.
|
/// Driver for the ST device M95M01 EEPROM memory.
|
||||||
///
|
///
|
||||||
@ -63,8 +59,14 @@ pub struct M95M01 {
|
|||||||
pub struct PageBoundaryExceededError;
|
pub struct PageBoundaryExceededError;
|
||||||
|
|
||||||
impl M95M01 {
|
impl M95M01 {
|
||||||
pub fn new(spi: pac::Spic, clk_config: SpiClkConfig) -> Self {
|
pub fn new(syscfg: &mut pac::Sysconfig, sys_clk: impl Into<Hertz>, spi: pac::Spic) -> Self {
|
||||||
let spi = RomSpi::new_for_rom(spi, SpiConfig::default().clk_cfg(clk_config)).unwrap();
|
let spi = RomSpi::new(
|
||||||
|
syscfg,
|
||||||
|
sys_clk,
|
||||||
|
spi,
|
||||||
|
(RomSck, RomMiso, RomMosi),
|
||||||
|
SpiConfig::default().clk_cfg(SpiClkConfig::new(2, 4)),
|
||||||
|
);
|
||||||
let mut spi_dev = Self { spi };
|
let mut spi_dev = Self { spi };
|
||||||
spi_dev.clear_block_protection().unwrap();
|
spi_dev.clear_block_protection().unwrap();
|
||||||
spi_dev
|
spi_dev
|
||||||
@ -72,7 +74,7 @@ impl M95M01 {
|
|||||||
|
|
||||||
pub fn release(mut self) -> pac::Spic {
|
pub fn release(mut self) -> pac::Spic {
|
||||||
self.set_block_protection().unwrap();
|
self.set_block_protection().unwrap();
|
||||||
unsafe { pac::Spic::steal() }
|
self.spi.release().0
|
||||||
}
|
}
|
||||||
|
|
||||||
// Wait until the write-in-progress state is cleared. This exposes a [nb] API, so this function
|
// Wait until the write-in-progress state is cleared. This exposes a [nb] API, so this function
|
||||||
@ -88,7 +90,7 @@ impl M95M01 {
|
|||||||
pub fn read_status_reg(&mut self) -> Result<StatusReg, Infallible> {
|
pub fn read_status_reg(&mut self) -> Result<StatusReg, Infallible> {
|
||||||
let mut write_read: [u8; 2] = [regs::RDSR, 0x00];
|
let mut write_read: [u8; 2] = [regs::RDSR, 0x00];
|
||||||
self.spi.transfer_in_place(&mut write_read)?;
|
self.spi.transfer_in_place(&mut write_read)?;
|
||||||
Ok(StatusReg::new_with_raw_value(write_read[1]))
|
Ok(StatusReg(write_read[1]))
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn write_enable(&mut self) -> Result<(), Infallible> {
|
pub fn write_enable(&mut self) -> Result<(), Infallible> {
|
||||||
@ -102,10 +104,10 @@ impl M95M01 {
|
|||||||
}
|
}
|
||||||
|
|
||||||
pub fn set_block_protection(&mut self) -> Result<(), Infallible> {
|
pub fn set_block_protection(&mut self) -> Result<(), Infallible> {
|
||||||
let mut reg = StatusReg::new_with_raw_value(0);
|
let mut reg = StatusReg(0);
|
||||||
reg.set_block_protection_bits(u2::new(0b11));
|
reg.set_block_protection_bits(0b11);
|
||||||
self.write_enable()?;
|
self.write_enable()?;
|
||||||
self.spi.write(&[WRSR, reg.raw_value()])
|
self.spi.write(&[WRSR, reg.0])
|
||||||
}
|
}
|
||||||
|
|
||||||
fn common_init_write_and_read(&mut self, address: usize, reg: u8) -> Result<(), Infallible> {
|
fn common_init_write_and_read(&mut self, address: usize, reg: u8) -> Result<(), Infallible> {
|
||||||
|
@ -9,7 +9,7 @@ use max116xx_10bit::{
|
|||||||
Error, ExternallyClocked, InternallyClockedInternallyTimedSerialInterface, Max116xx10Bit,
|
Error, ExternallyClocked, InternallyClockedInternallyTimedSerialInterface, Max116xx10Bit,
|
||||||
Max116xx10BitEocExt, VoltageRefMode, WithWakeupDelay, WithoutWakeupDelay,
|
Max116xx10BitEocExt, VoltageRefMode, WithWakeupDelay, WithoutWakeupDelay,
|
||||||
};
|
};
|
||||||
use va108xx_hal::gpio::Input;
|
use va108xx_hal::gpio::{Floating, Input, Pin, PA14};
|
||||||
|
|
||||||
pub type Max11619ExternallyClockedNoWakeup<Spi> =
|
pub type Max11619ExternallyClockedNoWakeup<Spi> =
|
||||||
Max116xx10Bit<Spi, ExternallyClocked, WithoutWakeupDelay>;
|
Max116xx10Bit<Spi, ExternallyClocked, WithoutWakeupDelay>;
|
||||||
@ -17,6 +17,7 @@ pub type Max11619ExternallyClockedWithWakeup<Spi> =
|
|||||||
Max116xx10Bit<Spi, ExternallyClocked, WithWakeupDelay>;
|
Max116xx10Bit<Spi, ExternallyClocked, WithWakeupDelay>;
|
||||||
pub type Max11619InternallyClocked<Spi, Eoc> =
|
pub type Max11619InternallyClocked<Spi, Eoc> =
|
||||||
Max116xx10BitEocExt<Spi, Eoc, InternallyClockedInternallyTimedSerialInterface>;
|
Max116xx10BitEocExt<Spi, Eoc, InternallyClockedInternallyTimedSerialInterface>;
|
||||||
|
pub type EocPin = Pin<PA14, Input<Floating>>;
|
||||||
|
|
||||||
pub const AN0_CHANNEL: u8 = 0;
|
pub const AN0_CHANNEL: u8 = 0;
|
||||||
pub const AN1_CHANNEL: u8 = 1;
|
pub const AN1_CHANNEL: u8 = 1;
|
||||||
@ -43,9 +44,9 @@ pub fn max11619_externally_clocked_with_wakeup<Spi: SpiDevice>(
|
|||||||
|
|
||||||
pub fn max11619_internally_clocked<Spi: SpiDevice>(
|
pub fn max11619_internally_clocked<Spi: SpiDevice>(
|
||||||
spi: Spi,
|
spi: Spi,
|
||||||
eoc: Input,
|
eoc: EocPin,
|
||||||
v_ref: VoltageRefMode,
|
v_ref: VoltageRefMode,
|
||||||
) -> Result<Max11619InternallyClocked<Spi, Input>, Error<Spi::Error, Infallible>> {
|
) -> Result<Max11619InternallyClocked<Spi, EocPin>, Error<Spi::Error, Infallible>> {
|
||||||
let mut adc = Max116xx10Bit::max11619(spi)?
|
let mut adc = Max116xx10Bit::max11619(spi)?
|
||||||
.into_int_clkd_int_timed_through_ser_if_without_wakeup(v_ref, eoc)?;
|
.into_int_clkd_int_timed_through_ser_if_without_wakeup(v_ref, eoc)?;
|
||||||
adc.reset(false)?;
|
adc.reset(false)?;
|
||||||
|
@ -15,7 +15,7 @@ use va108xx_hal::{
|
|||||||
const ADT75_I2C_ADDR: u8 = 0b1001000;
|
const ADT75_I2C_ADDR: u8 = 0b1001000;
|
||||||
|
|
||||||
pub struct Adt75TempSensor {
|
pub struct Adt75TempSensor {
|
||||||
sensor_if: I2cMaster<SevenBitAddress>,
|
sensor_if: I2cMaster<pac::I2ca, SevenBitAddress>,
|
||||||
cmd_buf: [u8; 1],
|
cmd_buf: [u8; 1],
|
||||||
current_reg: RegAddresses,
|
current_reg: RegAddresses,
|
||||||
}
|
}
|
||||||
@ -48,12 +48,17 @@ impl From<Error> for AdtInitError {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl Adt75TempSensor {
|
impl Adt75TempSensor {
|
||||||
pub fn new(sys_clk: Hertz, i2ca: pac::I2ca) -> Result<Self, Error> {
|
pub fn new(
|
||||||
|
sys_cfg: &mut pac::Sysconfig,
|
||||||
|
sys_clk: impl Into<Hertz> + Copy,
|
||||||
|
i2ca: pac::I2ca,
|
||||||
|
) -> Result<Self, Error> {
|
||||||
let mut sensor = Adt75TempSensor {
|
let mut sensor = Adt75TempSensor {
|
||||||
// The master construction can not fail for regular I2C speed.
|
// The master construction can not fail for regular I2C speed.
|
||||||
sensor_if: I2cMaster::new(
|
sensor_if: I2cMaster::new(
|
||||||
i2ca,
|
sys_cfg,
|
||||||
sys_clk,
|
sys_clk,
|
||||||
|
i2ca,
|
||||||
MasterConfig::default(),
|
MasterConfig::default(),
|
||||||
I2cSpeed::Regular100khz,
|
I2cSpeed::Regular100khz,
|
||||||
)
|
)
|
||||||
|
@ -15,10 +15,6 @@
|
|||||||
"device": "Cortex-M0",
|
"device": "Cortex-M0",
|
||||||
"svdFile": "./va108xx/svd/va108xx.svd.patched",
|
"svdFile": "./va108xx/svd/va108xx.svd.patched",
|
||||||
"preLaunchTask": "rust: cargo build led blinky",
|
"preLaunchTask": "rust: cargo build led blinky",
|
||||||
"serverArgs": [
|
|
||||||
"-jtagconf",
|
|
||||||
"-1,-1"
|
|
||||||
],
|
|
||||||
"executable": "${workspaceFolder}/target/thumbv6m-none-eabi/debug/examples/blinky-leds",
|
"executable": "${workspaceFolder}/target/thumbv6m-none-eabi/debug/examples/blinky-leds",
|
||||||
"interface": "jtag",
|
"interface": "jtag",
|
||||||
"runToEntryPoint": "main",
|
"runToEntryPoint": "main",
|
||||||
@ -43,10 +39,6 @@
|
|||||||
"device": "Cortex-M0",
|
"device": "Cortex-M0",
|
||||||
"svdFile": "./va108xx/svd/va108xx.svd.patched",
|
"svdFile": "./va108xx/svd/va108xx.svd.patched",
|
||||||
"preLaunchTask": "rust: cargo build hal tests",
|
"preLaunchTask": "rust: cargo build hal tests",
|
||||||
"serverArgs": [
|
|
||||||
"-jtagconf",
|
|
||||||
"-1,-1"
|
|
||||||
],
|
|
||||||
"executable": "${workspaceFolder}/target/thumbv6m-none-eabi/debug/board-tests",
|
"executable": "${workspaceFolder}/target/thumbv6m-none-eabi/debug/board-tests",
|
||||||
"interface": "jtag",
|
"interface": "jtag",
|
||||||
"runToEntryPoint": "main",
|
"runToEntryPoint": "main",
|
||||||
@ -62,6 +54,30 @@
|
|||||||
]
|
]
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
"type": "cortex-debug",
|
||||||
|
"request": "launch",
|
||||||
|
"name": "Debug RTT",
|
||||||
|
"servertype": "jlink",
|
||||||
|
"cwd": "${workspaceRoot}",
|
||||||
|
"device": "Cortex-M0",
|
||||||
|
"svdFile": "./va108xx/svd/va108xx.svd.patched",
|
||||||
|
"preLaunchTask": "rust: cargo build rtt",
|
||||||
|
"executable": "${workspaceFolder}/target/thumbv6m-none-eabi/debug/examples/rtt-log",
|
||||||
|
"interface": "jtag",
|
||||||
|
"runToEntryPoint": "main",
|
||||||
|
"rttConfig": {
|
||||||
|
"enabled": true,
|
||||||
|
"address": "auto",
|
||||||
|
"decoders": [
|
||||||
|
{
|
||||||
|
"port": 0,
|
||||||
|
"timestamp": true,
|
||||||
|
"type": "console"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
},
|
||||||
{
|
{
|
||||||
"type": "cortex-debug",
|
"type": "cortex-debug",
|
||||||
"request": "launch",
|
"request": "launch",
|
||||||
@ -71,10 +87,6 @@
|
|||||||
"device": "Cortex-M0",
|
"device": "Cortex-M0",
|
||||||
"svdFile": "./va108xx/svd/va108xx.svd.patched",
|
"svdFile": "./va108xx/svd/va108xx.svd.patched",
|
||||||
"preLaunchTask": "rust: cargo build button blinky",
|
"preLaunchTask": "rust: cargo build button blinky",
|
||||||
"serverArgs": [
|
|
||||||
"-jtagconf",
|
|
||||||
"-1,-1"
|
|
||||||
],
|
|
||||||
"executable": "${workspaceFolder}/target/thumbv6m-none-eabi/debug/examples/blinky-button-irq",
|
"executable": "${workspaceFolder}/target/thumbv6m-none-eabi/debug/examples/blinky-button-irq",
|
||||||
"interface": "jtag",
|
"interface": "jtag",
|
||||||
"runToEntryPoint": "main",
|
"runToEntryPoint": "main",
|
||||||
@ -101,10 +113,6 @@
|
|||||||
"preLaunchTask": "rust: cargo build systick",
|
"preLaunchTask": "rust: cargo build systick",
|
||||||
"executable": "${workspaceFolder}/target/thumbv6m-none-eabi/debug/examples/timer-ticks",
|
"executable": "${workspaceFolder}/target/thumbv6m-none-eabi/debug/examples/timer-ticks",
|
||||||
"interface": "jtag",
|
"interface": "jtag",
|
||||||
"serverArgs": [
|
|
||||||
"-jtagconf",
|
|
||||||
"-1,-1"
|
|
||||||
],
|
|
||||||
"runToEntryPoint": "main",
|
"runToEntryPoint": "main",
|
||||||
"rttConfig": {
|
"rttConfig": {
|
||||||
"enabled": true,
|
"enabled": true,
|
||||||
@ -125,11 +133,7 @@
|
|||||||
"servertype": "jlink",
|
"servertype": "jlink",
|
||||||
"cwd": "${workspaceRoot}",
|
"cwd": "${workspaceRoot}",
|
||||||
"device": "Cortex-M0",
|
"device": "Cortex-M0",
|
||||||
"svdFile": "./va108xx/svd/va108xx.svd.patched",
|
"svdFile": "./va108xx/svd/va108xx-base.svd.patched",
|
||||||
"serverArgs": [
|
|
||||||
"-jtagconf",
|
|
||||||
"-1,-1"
|
|
||||||
],
|
|
||||||
"preLaunchTask": "rust: cargo build uart",
|
"preLaunchTask": "rust: cargo build uart",
|
||||||
"executable": "${workspaceFolder}/target/thumbv6m-none-eabi/debug/examples/uart",
|
"executable": "${workspaceFolder}/target/thumbv6m-none-eabi/debug/examples/uart",
|
||||||
"interface": "jtag",
|
"interface": "jtag",
|
||||||
@ -154,10 +158,6 @@
|
|||||||
"cwd": "${workspaceRoot}",
|
"cwd": "${workspaceRoot}",
|
||||||
"device": "Cortex-M0",
|
"device": "Cortex-M0",
|
||||||
"svdFile": "./va108xx/svd/va108xx.svd.patched",
|
"svdFile": "./va108xx/svd/va108xx.svd.patched",
|
||||||
"serverArgs": [
|
|
||||||
"-jtagconf",
|
|
||||||
"-1,-1"
|
|
||||||
],
|
|
||||||
"preLaunchTask": "rust: cargo build spi",
|
"preLaunchTask": "rust: cargo build spi",
|
||||||
"executable": "${workspaceFolder}/target/thumbv6m-none-eabi/debug/examples/spi",
|
"executable": "${workspaceFolder}/target/thumbv6m-none-eabi/debug/examples/spi",
|
||||||
"interface": "jtag",
|
"interface": "jtag",
|
||||||
@ -182,10 +182,6 @@
|
|||||||
"cwd": "${workspaceRoot}",
|
"cwd": "${workspaceRoot}",
|
||||||
"device": "Cortex-M0",
|
"device": "Cortex-M0",
|
||||||
"svdFile": "./va108xx/svd/va108xx-base.svd.patched",
|
"svdFile": "./va108xx/svd/va108xx-base.svd.patched",
|
||||||
"serverArgs": [
|
|
||||||
"-jtagconf",
|
|
||||||
"-1,-1"
|
|
||||||
],
|
|
||||||
"preLaunchTask": "rust: cargo build temp sensor",
|
"preLaunchTask": "rust: cargo build temp sensor",
|
||||||
"executable": "${workspaceFolder}/target/thumbv6m-none-eabi/debug/examples/adt75-temp-sensor",
|
"executable": "${workspaceFolder}/target/thumbv6m-none-eabi/debug/examples/adt75-temp-sensor",
|
||||||
"interface": "jtag",
|
"interface": "jtag",
|
||||||
@ -210,10 +206,6 @@
|
|||||||
"cwd": "${workspaceRoot}",
|
"cwd": "${workspaceRoot}",
|
||||||
"device": "Cortex-M0",
|
"device": "Cortex-M0",
|
||||||
"svdFile": "./va108xx/svd/va108xx-base.svd.patched",
|
"svdFile": "./va108xx/svd/va108xx-base.svd.patched",
|
||||||
"serverArgs": [
|
|
||||||
"-jtagconf",
|
|
||||||
"-1,-1"
|
|
||||||
],
|
|
||||||
"preLaunchTask": "rust: cargo build button blinky rtic",
|
"preLaunchTask": "rust: cargo build button blinky rtic",
|
||||||
"executable": "${workspaceFolder}/target/thumbv6m-none-eabi/debug/examples/blinky-button-rtic",
|
"executable": "${workspaceFolder}/target/thumbv6m-none-eabi/debug/examples/blinky-button-rtic",
|
||||||
"interface": "jtag",
|
"interface": "jtag",
|
||||||
@ -238,10 +230,6 @@
|
|||||||
"cwd": "${workspaceRoot}",
|
"cwd": "${workspaceRoot}",
|
||||||
"device": "Cortex-M0",
|
"device": "Cortex-M0",
|
||||||
"svdFile": "./va108xx/svd/va108xx-base.svd.patched",
|
"svdFile": "./va108xx/svd/va108xx-base.svd.patched",
|
||||||
"serverArgs": [
|
|
||||||
"-jtagconf",
|
|
||||||
"-1,-1"
|
|
||||||
],
|
|
||||||
"preLaunchTask": "uart-echo-rtic-example",
|
"preLaunchTask": "uart-echo-rtic-example",
|
||||||
"executable": "${workspaceFolder}/target/thumbv6m-none-eabi/debug/uart-echo-rtic",
|
"executable": "${workspaceFolder}/target/thumbv6m-none-eabi/debug/uart-echo-rtic",
|
||||||
"interface": "jtag",
|
"interface": "jtag",
|
||||||
@ -266,10 +254,6 @@
|
|||||||
"cwd": "${workspaceRoot}",
|
"cwd": "${workspaceRoot}",
|
||||||
"device": "Cortex-M0",
|
"device": "Cortex-M0",
|
||||||
"svdFile": "./va108xx/svd/va108xx.svd.patched",
|
"svdFile": "./va108xx/svd/va108xx.svd.patched",
|
||||||
"serverArgs": [
|
|
||||||
"-jtagconf",
|
|
||||||
"-1,-1"
|
|
||||||
],
|
|
||||||
"preLaunchTask": "rust: cargo build pwm",
|
"preLaunchTask": "rust: cargo build pwm",
|
||||||
"executable": "${workspaceFolder}/target/thumbv6m-none-eabi/debug/examples/pwm",
|
"executable": "${workspaceFolder}/target/thumbv6m-none-eabi/debug/examples/pwm",
|
||||||
"interface": "jtag",
|
"interface": "jtag",
|
||||||
@ -294,10 +278,6 @@
|
|||||||
"cwd": "${workspaceRoot}",
|
"cwd": "${workspaceRoot}",
|
||||||
"device": "Cortex-M0",
|
"device": "Cortex-M0",
|
||||||
"svdFile": "./va108xx/svd/va108xx.svd.patched",
|
"svdFile": "./va108xx/svd/va108xx.svd.patched",
|
||||||
"serverArgs": [
|
|
||||||
"-jtagconf",
|
|
||||||
"-1,-1"
|
|
||||||
],
|
|
||||||
"preLaunchTask": "rust: cargo build cascade",
|
"preLaunchTask": "rust: cargo build cascade",
|
||||||
"executable": "${workspaceFolder}/target/thumbv6m-none-eabi/debug/examples/cascade",
|
"executable": "${workspaceFolder}/target/thumbv6m-none-eabi/debug/examples/cascade",
|
||||||
"interface": "jtag",
|
"interface": "jtag",
|
||||||
@ -322,10 +302,6 @@
|
|||||||
"cwd": "${workspaceRoot}",
|
"cwd": "${workspaceRoot}",
|
||||||
"device": "Cortex-M0",
|
"device": "Cortex-M0",
|
||||||
"svdFile": "./va108xx/svd/va108xx.svd.patched",
|
"svdFile": "./va108xx/svd/va108xx.svd.patched",
|
||||||
"serverArgs": [
|
|
||||||
"-jtagconf",
|
|
||||||
"-1,-1"
|
|
||||||
],
|
|
||||||
"preLaunchTask": "rust: cargo build accelerometer",
|
"preLaunchTask": "rust: cargo build accelerometer",
|
||||||
"executable": "${workspaceFolder}/target/thumbv6m-none-eabi/debug/examples/adxl343-accelerometer",
|
"executable": "${workspaceFolder}/target/thumbv6m-none-eabi/debug/examples/adxl343-accelerometer",
|
||||||
"interface": "jtag",
|
"interface": "jtag",
|
||||||
@ -342,6 +318,19 @@
|
|||||||
]
|
]
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
"type": "cortex-debug",
|
||||||
|
"request": "launch",
|
||||||
|
"name": "Blinky HAL",
|
||||||
|
"servertype": "jlink",
|
||||||
|
"cwd": "${workspaceRoot}",
|
||||||
|
"device": "Cortex-M0",
|
||||||
|
"svdFile": "./va108xx/svd/va108xx.svd.patched",
|
||||||
|
"preLaunchTask": "blinky-hal",
|
||||||
|
"executable": "${workspaceFolder}/target/thumbv6m-none-eabi/debug/examples/blinky",
|
||||||
|
"interface": "jtag",
|
||||||
|
"runToEntryPoint": "main",
|
||||||
|
},
|
||||||
{
|
{
|
||||||
"type": "cortex-debug",
|
"type": "cortex-debug",
|
||||||
"request": "launch",
|
"request": "launch",
|
||||||
@ -350,10 +339,6 @@
|
|||||||
"cwd": "${workspaceRoot}",
|
"cwd": "${workspaceRoot}",
|
||||||
"device": "Cortex-M0",
|
"device": "Cortex-M0",
|
||||||
"svdFile": "./va108xx/svd/va108xx.svd.patched",
|
"svdFile": "./va108xx/svd/va108xx.svd.patched",
|
||||||
"serverArgs": [
|
|
||||||
"-jtagconf",
|
|
||||||
"-1,-1"
|
|
||||||
],
|
|
||||||
"preLaunchTask": "rust: cargo build adc",
|
"preLaunchTask": "rust: cargo build adc",
|
||||||
"executable": "${workspaceFolder}/target/thumbv6m-none-eabi/debug/examples/max11619-adc",
|
"executable": "${workspaceFolder}/target/thumbv6m-none-eabi/debug/examples/max11619-adc",
|
||||||
"interface": "jtag",
|
"interface": "jtag",
|
||||||
@ -378,10 +363,6 @@
|
|||||||
"cwd": "${workspaceRoot}",
|
"cwd": "${workspaceRoot}",
|
||||||
"device": "Cortex-M0",
|
"device": "Cortex-M0",
|
||||||
"svdFile": "./va108xx/svd/va108xx.svd.patched",
|
"svdFile": "./va108xx/svd/va108xx.svd.patched",
|
||||||
"serverArgs": [
|
|
||||||
"-jtagconf",
|
|
||||||
"-1,-1"
|
|
||||||
],
|
|
||||||
"preLaunchTask": "uart-echo-rtic-example",
|
"preLaunchTask": "uart-echo-rtic-example",
|
||||||
"executable": "${workspaceFolder}/target/thumbv6m-none-eabi/debug/uart-echo-rtic",
|
"executable": "${workspaceFolder}/target/thumbv6m-none-eabi/debug/uart-echo-rtic",
|
||||||
"interface": "jtag",
|
"interface": "jtag",
|
||||||
@ -406,10 +387,6 @@
|
|||||||
"cwd": "${workspaceRoot}",
|
"cwd": "${workspaceRoot}",
|
||||||
"device": "Cortex-M0",
|
"device": "Cortex-M0",
|
||||||
"svdFile": "./va108xx/svd/va108xx.svd.patched",
|
"svdFile": "./va108xx/svd/va108xx.svd.patched",
|
||||||
"serverArgs": [
|
|
||||||
"-jtagconf",
|
|
||||||
"-1,-1"
|
|
||||||
],
|
|
||||||
"preLaunchTask": "reb1-nvm",
|
"preLaunchTask": "reb1-nvm",
|
||||||
"executable": "${workspaceFolder}/target/thumbv6m-none-eabi/debug/examples/nvm",
|
"executable": "${workspaceFolder}/target/thumbv6m-none-eabi/debug/examples/nvm",
|
||||||
"interface": "jtag",
|
"interface": "jtag",
|
||||||
@ -434,10 +411,6 @@
|
|||||||
"cwd": "${workspaceRoot}",
|
"cwd": "${workspaceRoot}",
|
||||||
"device": "Cortex-M0",
|
"device": "Cortex-M0",
|
||||||
"svdFile": "./va108xx/svd/va108xx.svd.patched",
|
"svdFile": "./va108xx/svd/va108xx.svd.patched",
|
||||||
"serverArgs": [
|
|
||||||
"-jtagconf",
|
|
||||||
"-1,-1"
|
|
||||||
],
|
|
||||||
"preLaunchTask": "rtic-example",
|
"preLaunchTask": "rtic-example",
|
||||||
"executable": "${workspaceFolder}/target/thumbv6m-none-eabi/debug/rtic-example",
|
"executable": "${workspaceFolder}/target/thumbv6m-none-eabi/debug/rtic-example",
|
||||||
"interface": "jtag",
|
"interface": "jtag",
|
||||||
@ -462,10 +435,6 @@
|
|||||||
"cwd": "${workspaceRoot}",
|
"cwd": "${workspaceRoot}",
|
||||||
"device": "Cortex-M0",
|
"device": "Cortex-M0",
|
||||||
"svdFile": "./va108xx/svd/va108xx.svd.patched",
|
"svdFile": "./va108xx/svd/va108xx.svd.patched",
|
||||||
"serverArgs": [
|
|
||||||
"-jtagconf",
|
|
||||||
"-1,-1"
|
|
||||||
],
|
|
||||||
"preLaunchTask": "embassy-example",
|
"preLaunchTask": "embassy-example",
|
||||||
"executable": "${workspaceFolder}/target/thumbv6m-none-eabi/debug/embassy-example",
|
"executable": "${workspaceFolder}/target/thumbv6m-none-eabi/debug/embassy-example",
|
||||||
"interface": "jtag",
|
"interface": "jtag",
|
||||||
@ -490,10 +459,6 @@
|
|||||||
"cwd": "${workspaceRoot}",
|
"cwd": "${workspaceRoot}",
|
||||||
"device": "Cortex-M0",
|
"device": "Cortex-M0",
|
||||||
"svdFile": "./va108xx/svd/va108xx.svd.patched",
|
"svdFile": "./va108xx/svd/va108xx.svd.patched",
|
||||||
"serverArgs": [
|
|
||||||
"-jtagconf",
|
|
||||||
"-1,-1"
|
|
||||||
],
|
|
||||||
"preLaunchTask": "bootloader",
|
"preLaunchTask": "bootloader",
|
||||||
"executable": "${workspaceFolder}/target/thumbv6m-none-eabi/release/bootloader",
|
"executable": "${workspaceFolder}/target/thumbv6m-none-eabi/release/bootloader",
|
||||||
"interface": "jtag",
|
"interface": "jtag",
|
||||||
@ -518,10 +483,6 @@
|
|||||||
"cwd": "${workspaceRoot}",
|
"cwd": "${workspaceRoot}",
|
||||||
"device": "Cortex-M0",
|
"device": "Cortex-M0",
|
||||||
"svdFile": "./va108xx/svd/va108xx.svd.patched",
|
"svdFile": "./va108xx/svd/va108xx.svd.patched",
|
||||||
"serverArgs": [
|
|
||||||
"-jtagconf",
|
|
||||||
"-1,-1"
|
|
||||||
],
|
|
||||||
"preLaunchTask": "flashloader",
|
"preLaunchTask": "flashloader",
|
||||||
"executable": "${workspaceFolder}/target/thumbv6m-none-eabi/release/flashloader",
|
"executable": "${workspaceFolder}/target/thumbv6m-none-eabi/release/flashloader",
|
||||||
"interface": "jtag",
|
"interface": "jtag",
|
||||||
@ -546,10 +507,6 @@
|
|||||||
"cwd": "${workspaceRoot}",
|
"cwd": "${workspaceRoot}",
|
||||||
"device": "Cortex-M0",
|
"device": "Cortex-M0",
|
||||||
"svdFile": "./va108xx/svd/va108xx.svd.patched",
|
"svdFile": "./va108xx/svd/va108xx.svd.patched",
|
||||||
"serverArgs": [
|
|
||||||
"-jtagconf",
|
|
||||||
"-1,-1"
|
|
||||||
],
|
|
||||||
"preLaunchTask": "async-gpio",
|
"preLaunchTask": "async-gpio",
|
||||||
"executable": "${workspaceFolder}/target/thumbv6m-none-eabi/debug/async-gpio",
|
"executable": "${workspaceFolder}/target/thumbv6m-none-eabi/debug/async-gpio",
|
||||||
"interface": "jtag",
|
"interface": "jtag",
|
||||||
@ -574,10 +531,6 @@
|
|||||||
"cwd": "${workspaceRoot}",
|
"cwd": "${workspaceRoot}",
|
||||||
"device": "Cortex-M0",
|
"device": "Cortex-M0",
|
||||||
"svdFile": "./va108xx/svd/va108xx.svd.patched",
|
"svdFile": "./va108xx/svd/va108xx.svd.patched",
|
||||||
"serverArgs": [
|
|
||||||
"-jtagconf",
|
|
||||||
"-1,-1"
|
|
||||||
],
|
|
||||||
"preLaunchTask": "async-uart-tx",
|
"preLaunchTask": "async-uart-tx",
|
||||||
"executable": "${workspaceFolder}/target/thumbv6m-none-eabi/debug/async-uart-tx",
|
"executable": "${workspaceFolder}/target/thumbv6m-none-eabi/debug/async-uart-tx",
|
||||||
"interface": "jtag",
|
"interface": "jtag",
|
||||||
@ -602,10 +555,6 @@
|
|||||||
"cwd": "${workspaceRoot}",
|
"cwd": "${workspaceRoot}",
|
||||||
"device": "Cortex-M0",
|
"device": "Cortex-M0",
|
||||||
"svdFile": "./va108xx/svd/va108xx.svd.patched",
|
"svdFile": "./va108xx/svd/va108xx.svd.patched",
|
||||||
"serverArgs": [
|
|
||||||
"-jtagconf",
|
|
||||||
"-1,-1"
|
|
||||||
],
|
|
||||||
"preLaunchTask": "async-uart-rx",
|
"preLaunchTask": "async-uart-rx",
|
||||||
"executable": "${workspaceFolder}/target/thumbv6m-none-eabi/debug/async-uart-rx",
|
"executable": "${workspaceFolder}/target/thumbv6m-none-eabi/debug/async-uart-rx",
|
||||||
"interface": "jtag",
|
"interface": "jtag",
|
||||||
|
@ -31,6 +31,20 @@
|
|||||||
"isDefault": true
|
"isDefault": true
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
"label": "rust: cargo build rtt",
|
||||||
|
"type": "shell",
|
||||||
|
"command": "~/.cargo/bin/cargo", // note: full path to the cargo
|
||||||
|
"args": [
|
||||||
|
"build",
|
||||||
|
"--example",
|
||||||
|
"rtt-log"
|
||||||
|
],
|
||||||
|
"group": {
|
||||||
|
"kind": "build",
|
||||||
|
"isDefault": true
|
||||||
|
}
|
||||||
|
},
|
||||||
{
|
{
|
||||||
"label": "rust: cargo build systick",
|
"label": "rust: cargo build systick",
|
||||||
"type": "shell",
|
"type": "shell",
|
||||||
|
Loading…
x
Reference in New Issue
Block a user