Compare commits

..

No commits in common. "main" and "vorago-peb1-v0.1.2" have entirely different histories.

63 changed files with 9515 additions and 921 deletions

View File

@ -2,15 +2,18 @@
# runner = "gdb-multiarch -q -x jlink/jlink.gdb" # runner = "gdb-multiarch -q -x jlink/jlink.gdb"
# runner = "arm-none-eabi-gdb -q -x jlink/jlink-reva.gdb" # runner = "arm-none-eabi-gdb -q -x jlink/jlink-reva.gdb"
# runner = "gdb-multiarch -q -x jlink/jlink-reva.gdb" # runner = "gdb-multiarch -q -x jlink/jlink-reva.gdb"
# Probe-rs is currently problematic, possibly because of the
# ROM protection?
runner = "probe-rs run --chip VA416xx_RAM --protocol swd" runner = "probe-rs run --chip VA416xx_RAM --protocol swd"
rustflags = [ rustflags = [
"-C", "-C",
"link-arg=-Tlink.x", "link-arg=-Tlink.x",
"-C", # "-C",
"linker=flip-link", # "linker=flip-link",
"-C", # "-C",
"link-arg=-Tdefmt.x", # "link-arg=-Tdefmt.x",
# This is needed if your flash or ram addresses are not aligned to 0x10000 in memory.x # 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 # See https://github.com/rust-embedded/cortex-m-quickstart/pull/95
"-C", "-C",
@ -34,4 +37,4 @@ ut = "test --target=x86_64-unknown-linux-gnu"
genbin = "objcopy --release -- -O binary app.bin" genbin = "objcopy --release -- -O binary app.bin"
[env] [env]
DEFMT_LOG = "debug" DEFMT_LOG = "info"

View File

@ -74,8 +74,8 @@ probe-rs run --chip VA416xx_RAM --protocol jtag target/thumbv7em-none-eabihf/deb
to flash and run the blinky program on the RAM. There is also a `VA416xx` chip target to flash and run the blinky program on the RAM. There is also a `VA416xx` 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.
### Pre-Requisites ### Pre-Requisites
@ -123,25 +123,16 @@ You can build the blinky example application with the following command
cargo build --example blinky cargo build --example blinky
``` ```
Start the GDB server first. Depending on whether the application is flashed to RAM or the NVM Start the GDB server first. The server needs to be started with a certain configuration and with
flash memory, the server needs to be started with a different configuration a JLink script to disable ROM protection.
For example, on Debian based system the following command can be used to do this (this command For example, on Debian based system the following command can be used to do this (this command
is also run when running the `jlink-gdb.sh` script) is also run when running the `jlink-gdb.sh` script)
**RAM Flash**
```sh ```sh
JLinkGDBServer -select USB -device Cortex-M4 -endian little -if SWD -speed 2000 \ JLinkGDBServer -select USB -device Cortex-M4 -endian little -if SWD -speed 2000 \
-LocalhostOnly -vd -jlinkscriptfile ./jlink/JLinkSettings.JLinkScript -LocalhostOnly -vd -jlinkscriptfile ./jlink/JLinkSettings.JLinkScript
``` ```
**NVM Flash**
```sh
JLinkGDBServer -select USB -device VA416xx -endian little -if SWD -speed 2000 \
-LocalhostOnly -vd
```
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
@ -153,7 +144,7 @@ runner configuration, for example with the following lines in your `.cargo/confi
```toml ```toml
[target.'cfg(all(target_arch = "arm", target_os = "none"))'] [target.'cfg(all(target_arch = "arm", target_os = "none"))']
runner = "gdb-multiarch -q -x jlink/jlink.gdb -tui" runner = "gdb-multiarch -q -x jlink/jlink.gdb"
``` ```
After that, you can simply use `cargo run --example blinky` to flash the blinky After that, you can simply use `cargo run --example blinky` to flash the blinky
@ -164,22 +155,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 0x1fff8000. It is recommended to use a search range of address for the RTT block placement is 0x1fff8000. 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)

View File

@ -6,13 +6,17 @@ edition = "2021"
[dependencies] [dependencies]
cortex-m = "0.7" cortex-m = "0.7"
cortex-m-rt = "0.7" cortex-m-rt = "0.7"
defmt-rtt = "0.4" embedded-hal = "1"
defmt = "1" panic-rtt-target = { version = "0.2" }
panic-probe = { version = "1", features = ["defmt"] } panic-halt = { version = "1" }
rtt-target = { version = "0.6" }
crc = "3" crc = "3"
static_assertions = "1" static_assertions = "1"
[dependencies.va416xx-hal] [dependencies.va416xx-hal]
version = "0.5"
features = ["va41630", "defmt"]
path = "../va416xx-hal" path = "../va416xx-hal"
features = ["va41630"]
[features]
default = []
rtt-panic = []

View File

@ -7,13 +7,17 @@
use cortex_m_rt::entry; use cortex_m_rt::entry;
use crc::{Crc, CRC_32_ISO_HDLC}; use crc::{Crc, CRC_32_ISO_HDLC};
use defmt_rtt as _; #[cfg(not(feature = "rtt-panic"))]
use panic_probe as _; use panic_halt as _;
#[cfg(feature = "rtt-panic")]
use panic_rtt_target as _;
use rtt_target::{rprintln, rtt_init_print};
use va416xx_hal::{ use va416xx_hal::{
clock::{pll_setup_delay, ClkDivSel, ClkselSys, ClockConfigurator}, clock::{pll_setup_delay, ClkDivSel, ClkselSys},
edac, edac,
nvm::Nvm, nvm::Nvm,
pac::{self, interrupt}, pac::{self, interrupt},
prelude::*,
time::Hertz, time::Hertz,
wdt::Wdt, wdt::Wdt,
}; };
@ -31,7 +35,7 @@ const DEBUG_PRINTOUTS: bool = true;
const FLASH_SELF: bool = false; const FLASH_SELF: bool = false;
// 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_PRINTOUTS: bool = true; const RTT_PRINTOUT: bool = true;
// Important bootloader addresses and offsets, vector table information. // Important bootloader addresses and offsets, vector table information.
@ -72,7 +76,7 @@ pub const RESET_VECTOR_OFFSET: u32 = 0x4;
const CRC_ALGO: Crc<u32> = Crc::<u32>::new(&CRC_32_ISO_HDLC); const CRC_ALGO: Crc<u32> = Crc::<u32>::new(&CRC_32_ISO_HDLC);
#[derive(Debug, Copy, Clone, PartialEq, Eq, defmt::Format)] #[derive(Debug, Copy, Clone, PartialEq, Eq)]
enum AppSel { enum AppSel {
A, A,
B, B,
@ -94,8 +98,9 @@ impl WdtInterface for OptWdt {
#[entry] #[entry]
fn main() -> ! { fn main() -> ! {
if DEFMT_PRINTOUTS { if RTT_PRINTOUT {
defmt::println!("-- VA416xx bootloader --"); rtt_init_print!();
rprintln!("-- VA416xx bootloader --");
} }
let mut dp = pac::Peripherals::take().unwrap(); let mut dp = pac::Peripherals::take().unwrap();
let cp = cortex_m::Peripherals::take().unwrap(); let cp = cortex_m::Peripherals::take().unwrap();
@ -103,16 +108,23 @@ fn main() -> ! {
dp.sysconfig.rom_prot().write(|w| unsafe { w.bits(1) }); dp.sysconfig.rom_prot().write(|w| unsafe { w.bits(1) });
setup_edac(&mut dp.sysconfig); setup_edac(&mut dp.sysconfig);
// Use the external clock connected to XTAL_N. // Use the external clock connected to XTAL_N.
let clocks = ClockConfigurator::new(dp.clkgen) let clocks = dp
.clkgen
.constrain()
.xtal_n_clk_with_src_freq(Hertz::from_raw(EXTCLK_FREQ)) .xtal_n_clk_with_src_freq(Hertz::from_raw(EXTCLK_FREQ))
.freeze() .freeze(&mut dp.sysconfig)
.unwrap(); .unwrap();
let mut opt_wdt = OptWdt(None); let mut opt_wdt = OptWdt(None);
if WITH_WDT { if WITH_WDT {
opt_wdt.0 = Some(Wdt::start(dp.watch_dog, &clocks, WDT_FREQ_MS)); opt_wdt.0 = Some(Wdt::start(
&mut dp.sysconfig,
dp.watch_dog,
&clocks,
WDT_FREQ_MS,
));
} }
let nvm = Nvm::new(dp.spi3, &clocks); let nvm = Nvm::new(&mut dp.sysconfig, dp.spi3, &clocks);
if FLASH_SELF { if FLASH_SELF {
let mut first_four_bytes: [u8; 4] = [0; 4]; let mut first_four_bytes: [u8; 4] = [0; 4];
@ -133,20 +145,20 @@ fn main() -> ! {
nvm.write_data(0x0, &first_four_bytes); nvm.write_data(0x0, &first_four_bytes);
nvm.write_data(0x4, bootloader_data); nvm.write_data(0x4, bootloader_data);
if let Err(e) = nvm.verify_data(0x0, &first_four_bytes) { if let Err(e) = nvm.verify_data(0x0, &first_four_bytes) {
if DEFMT_PRINTOUTS { 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_data(0x4, bootloader_data) { if let Err(e) = nvm.verify_data(0x4, bootloader_data) {
if DEFMT_PRINTOUTS { if RTT_PRINTOUT {
defmt::error!("verification of self-flash to NVM failed: {:?}", e); rprintln!("verification of self-flash to NVM failed: {:?}", e);
} }
} }
nvm.write_data(BOOTLOADER_CRC_ADDR, &bootloader_crc.to_be_bytes()); nvm.write_data(BOOTLOADER_CRC_ADDR, &bootloader_crc.to_be_bytes());
if let Err(e) = nvm.verify_data(BOOTLOADER_CRC_ADDR, &bootloader_crc.to_be_bytes()) { if let Err(e) = nvm.verify_data(BOOTLOADER_CRC_ADDR, &bootloader_crc.to_be_bytes()) {
if DEFMT_PRINTOUTS { if RTT_PRINTOUT {
defmt::error!( rprintln!(
"error: CRC verification for bootloader self-flash failed: {:?}", "error: CRC verification for bootloader self-flash failed: {:?}",
e e
); );
@ -162,8 +174,8 @@ fn main() -> ! {
} else if check_app_crc(AppSel::B, &opt_wdt) { } else if check_app_crc(AppSel::B, &opt_wdt) {
boot_app(AppSel::B, &cp) boot_app(AppSel::B, &cp)
} else { } else {
if DEBUG_PRINTOUTS && DEFMT_PRINTOUTS { if DEBUG_PRINTOUTS && RTT_PRINTOUT {
defmt::println!("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.
@ -190,8 +202,8 @@ fn check_own_crc(wdt: &OptWdt, nvm: &Nvm, cp: &cortex_m::Peripherals) {
let crc_calc = digest.finalize(); let crc_calc = digest.finalize();
wdt.feed(); wdt.feed();
if crc_exp == 0x0000 || crc_exp == 0xffff { if crc_exp == 0x0000 || crc_exp == 0xffff {
if DEBUG_PRINTOUTS && DEFMT_PRINTOUTS { 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_data(BOOTLOADER_CRC_ADDR, &crc_calc.to_be_bytes()); nvm.write_data(BOOTLOADER_CRC_ADDR, &crc_calc.to_be_bytes());
@ -200,8 +212,8 @@ fn check_own_crc(wdt: &OptWdt, nvm: &Nvm, cp: &cortex_m::Peripherals) {
// 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_PRINTOUTS { if DEBUG_PRINTOUTS && RTT_PRINTOUT {
defmt::info!( 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
@ -223,8 +235,8 @@ fn read_four_bytes_at_addr_zero(buf: &mut [u8; 4]) {
} }
} }
fn check_app_crc(app_sel: AppSel, wdt: &OptWdt) -> bool { fn check_app_crc(app_sel: AppSel, wdt: &OptWdt) -> bool {
if DEBUG_PRINTOUTS && DEFMT_PRINTOUTS { 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, wdt) check_app_given_addr(APP_A_CRC_ADDR, APP_A_START_ADDR, APP_A_SIZE_ADDR, wdt)
@ -243,8 +255,8 @@ fn check_app_given_addr(
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_PRINTOUTS { if RTT_PRINTOUT {
defmt::info!("detected invalid app size {}", image_size); rprintln!("detected invalid app size {}", image_size);
} }
return false; return false;
} }
@ -260,8 +272,8 @@ fn check_app_given_addr(
} }
fn boot_app(app_sel: AppSel, cp: &cortex_m::Peripherals) -> ! { fn boot_app(app_sel: AppSel, cp: &cortex_m::Peripherals) -> ! {
if DEBUG_PRINTOUTS && DEFMT_PRINTOUTS { if DEBUG_PRINTOUTS && RTT_PRINTOUT {
defmt::info!("booting app {:?}", app_sel); rprintln!("booting app {:?}", app_sel);
} }
let clkgen = unsafe { pac::Clkgen::steal() }; let clkgen = unsafe { pac::Clkgen::steal() };
clkgen clkgen

View File

@ -4,18 +4,20 @@ version = "0.1.0"
edition = "2021" edition = "2021"
[dependencies] [dependencies]
cortex-m-rt = "0.7"
cfg-if = "1" cfg-if = "1"
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"
embedded-hal-async = "1" embedded-hal-async = "1"
embedded-io-async = "0.6" embedded-io-async = "0.6"
rtt-target = { version = "0.6" }
heapless = "0.8" heapless = "0.8"
defmt-rtt = "0.4" panic-rtt-target = { version = "0.2" }
defmt = "1"
panic-probe = { version = "1", features = ["defmt"] }
static_cell = "2" static_cell = "2"
critical-section = "1" critical-section = "1"
once_cell = { version = "1", default-features = false, features = ["critical-section"] }
ringbuf = { version = "0.4", default-features = false } ringbuf = { version = "0.4", default-features = false }
embassy-sync = "0.6" embassy-sync = "0.6"
@ -26,14 +28,11 @@ embassy-executor = { version = "0.7", features = [
"executor-interrupt" "executor-interrupt"
]} ]}
va416xx-hal = { version = "0.5", path = "../../va416xx-hal", features = ["defmt"] } va416xx-hal = { version = "0.5", path = "../../va416xx-hal" }
va416xx-embassy = { version = "0.1", path = "../../va416xx-embassy", default-features = false } va416xx-embassy = { version = "0.1", default-features = false, path = "../../va416xx-embassy" }
[features] [features]
default = ["ticks-hz-1_000", "va416xx-embassy/irq-tim14-tim15"] default = ["ticks-hz-1_000", "va416xx-embassy/irq-tim14-tim15"]
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"]

View File

@ -6,30 +6,31 @@
#![no_std] #![no_std]
#![no_main] #![no_main]
// Import panic provider.
use panic_probe as _;
// Import logger.
use defmt_rtt as _;
use embassy_example::EXTCLK_FREQ; use embassy_example::EXTCLK_FREQ;
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 va416xx_hal::clock::ClockConfigurator; use panic_rtt_target as _;
use va416xx_hal::gpio::asynch::{on_interrupt_for_async_gpio_for_port, InputPinAsync}; use rtt_target::{rprintln, rtt_init_print};
use va416xx_hal::gpio::{Input, Output, PinState, Port}; use va416xx_hal::clock::ClkgenExt;
use va416xx_hal::pac::{self, interrupt}; use va416xx_hal::gpio::{
use va416xx_hal::pins::{PinsA, PinsB, PinsC, PinsD, PinsE, PinsF, PinsG}; on_interrupt_for_async_gpio_for_port, InputDynPinAsync, InputPinAsync, PinsB, PinsC, PinsD,
PinsE, PinsF, PinsG, Port,
};
use va416xx_hal::time::Hertz; use va416xx_hal::time::Hertz;
use va416xx_hal::{
gpio::{DynPin, PinsA},
pac::{self, interrupt},
};
const CHECK_PA0_TO_PA1: bool = true; const CHECK_PA0_TO_PA1: bool = true;
const CHECK_PB0_TO_PB1: bool = false; const CHECK_PB0_TO_PB1: bool = true;
const CHECK_PC14_TO_PC15: bool = false; const CHECK_PC14_TO_PC15: bool = true;
const CHECK_PD2_TO_PD3: bool = false; const CHECK_PD2_TO_PD3: bool = true;
const CHECK_PE0_TO_PE1: bool = false; const CHECK_PE0_TO_PE1: bool = true;
const CHECK_PF0_TO_PF1: bool = false; const CHECK_PF0_TO_PF1: bool = true;
#[derive(Clone, Copy)] #[derive(Clone, Copy)]
pub struct GpioCmd { pub struct GpioCmd {
@ -65,32 +66,44 @@ static CHANNEL_PF0_TO_PF1: Channel<ThreadModeRawMutex, GpioCmd, 3> = Channel::ne
#[embassy_executor::main] #[embassy_executor::main]
async fn main(spawner: Spawner) { async fn main(spawner: Spawner) {
defmt::println!("-- VA416xx Async GPIO Demo --"); rtt_init_print!();
rprintln!("-- VA416xx Async GPIO Demo --");
let dp = pac::Peripherals::take().unwrap(); let mut dp = pac::Peripherals::take().unwrap();
// Initialize the systick interrupt & obtain the token to prove that we did // Initialize the systick interrupt & obtain the token to prove that we did
// Use the external clock connected to XTAL_N. // Use the external clock connected to XTAL_N.
let clocks = ClockConfigurator::new(dp.clkgen) let clocks = dp
.clkgen
.constrain()
.xtal_n_clk_with_src_freq(Hertz::from_raw(EXTCLK_FREQ)) .xtal_n_clk_with_src_freq(Hertz::from_raw(EXTCLK_FREQ))
.freeze() .freeze(&mut dp.sysconfig)
.unwrap(); .unwrap();
// Safety: Only called once here. // Safety: Only called once here.
va416xx_embassy::init(dp.tim15, dp.tim14, &clocks); unsafe {
va416xx_embassy::init(
&mut dp.sysconfig,
&dp.irq_router,
dp.tim15,
dp.tim14,
&clocks,
)
};
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 portc = PinsC::new(dp.portc); let portc = PinsC::new(&mut dp.sysconfig, dp.portc);
let portd = PinsD::new(dp.portd); let portd = PinsD::new(&mut dp.sysconfig, dp.portd);
let porte = PinsE::new(dp.porte); let porte = PinsE::new(&mut dp.sysconfig, dp.porte);
let portf = PinsF::new(dp.portf); let portf = PinsF::new(&mut dp.sysconfig, dp.portf);
let portg = PinsG::new(dp.portg); let portg = PinsG::new(&mut dp.sysconfig, dp.portg);
let mut led = Output::new(portg.pg5, PinState::Low); let mut led = portg.pg5.into_readable_push_pull_output();
if CHECK_PA0_TO_PA1 { if CHECK_PA0_TO_PA1 {
let out_pin = Output::new(porta.pa0, PinState::Low); let out_pin = porta.pa0.into_readable_push_pull_output();
let in_pin = Input::new_floating(porta.pa1); let in_pin = porta.pa1.into_floating_input();
let out_pin = out_pin.downgrade();
let in_pin = InputPinAsync::new(in_pin).unwrap(); let in_pin = InputPinAsync::new(in_pin).unwrap();
spawner spawner
@ -101,13 +114,14 @@ async fn main(spawner: Spawner) {
)) ))
.unwrap(); .unwrap();
check_pin_to_pin_async_ops("PA0 to PA1", CHANNEL_PA0_TO_PA1.sender(), in_pin).await; check_pin_to_pin_async_ops("PA0 to PA1", CHANNEL_PA0_TO_PA1.sender(), in_pin).await;
defmt::info!("Example PA0 to PA1 done"); rprintln!("Example PA0 to PA1 done");
} }
if CHECK_PB0_TO_PB1 { if CHECK_PB0_TO_PB1 {
let out_pin = Output::new(portb.pb0, PinState::Low); let out_pin = portb.pb0.into_readable_push_pull_output();
let in_pin = Input::new_floating(portb.pb1); let in_pin = portb.pb1.into_floating_input();
let in_pin = InputPinAsync::new(in_pin).unwrap(); let out_pin = out_pin.downgrade();
let in_pin = InputDynPinAsync::new(in_pin.downgrade()).unwrap();
spawner spawner
.spawn(output_task( .spawn(output_task(
@ -117,13 +131,14 @@ async fn main(spawner: Spawner) {
)) ))
.unwrap(); .unwrap();
check_pin_to_pin_async_ops("PB0 to PB1", CHANNEL_PB0_TO_PB1.sender(), in_pin).await; check_pin_to_pin_async_ops("PB0 to PB1", CHANNEL_PB0_TO_PB1.sender(), in_pin).await;
defmt::info!("Example PB0 to PB1 done"); rprintln!("Example PB0 to PB1 done");
} }
if CHECK_PC14_TO_PC15 { if CHECK_PC14_TO_PC15 {
let out_pin = Output::new(portc.pc14, PinState::Low); let out_pin = portc.pc14.into_readable_push_pull_output();
let in_pin = Input::new_floating(portc.pc15); let in_pin = portc.pc15.into_floating_input();
let in_pin = InputPinAsync::new(in_pin).unwrap(); let out_pin = out_pin.downgrade();
let in_pin = InputDynPinAsync::new(in_pin.downgrade()).unwrap();
spawner spawner
.spawn(output_task( .spawn(output_task(
"PC14 to PC15", "PC14 to PC15",
@ -132,13 +147,14 @@ async fn main(spawner: Spawner) {
)) ))
.unwrap(); .unwrap();
check_pin_to_pin_async_ops("PC14 to PC15", CHANNEL_PC14_TO_PC15.sender(), in_pin).await; check_pin_to_pin_async_ops("PC14 to PC15", CHANNEL_PC14_TO_PC15.sender(), in_pin).await;
defmt::info!("Example PC14 to PC15 done"); rprintln!("Example PC14 to PC15 done");
} }
if CHECK_PD2_TO_PD3 { if CHECK_PD2_TO_PD3 {
let out_pin = Output::new(portd.pd2, PinState::Low); let out_pin = portd.pd2.into_readable_push_pull_output();
let in_pin = Input::new_floating(portd.pd3); let in_pin = portd.pd3.into_floating_input();
let in_pin = InputPinAsync::new(in_pin).unwrap(); let out_pin = out_pin.downgrade();
let in_pin = InputDynPinAsync::new(in_pin.downgrade()).unwrap();
spawner spawner
.spawn(output_task( .spawn(output_task(
"PD2 to PD3", "PD2 to PD3",
@ -147,13 +163,14 @@ async fn main(spawner: Spawner) {
)) ))
.unwrap(); .unwrap();
check_pin_to_pin_async_ops("PD2 to PD3", CHANNEL_PD2_TO_PD3.sender(), in_pin).await; check_pin_to_pin_async_ops("PD2 to PD3", CHANNEL_PD2_TO_PD3.sender(), in_pin).await;
defmt::info!("Example PD2 to PD3 done"); rprintln!("Example PD2 to PD3 done");
} }
if CHECK_PE0_TO_PE1 { if CHECK_PE0_TO_PE1 {
let out_pin = Output::new(porte.pe0, PinState::Low); let out_pin = porte.pe0.into_readable_push_pull_output();
let in_pin = Input::new_floating(porte.pe1); let in_pin = porte.pe1.into_floating_input();
let in_pin = InputPinAsync::new(in_pin).unwrap(); let out_pin = out_pin.downgrade();
let in_pin = InputDynPinAsync::new(in_pin.downgrade()).unwrap();
spawner spawner
.spawn(output_task( .spawn(output_task(
"PE0 to PE1", "PE0 to PE1",
@ -162,13 +179,14 @@ async fn main(spawner: Spawner) {
)) ))
.unwrap(); .unwrap();
check_pin_to_pin_async_ops("PE0 to PE1", CHANNEL_PE0_TO_PE1.sender(), in_pin).await; check_pin_to_pin_async_ops("PE0 to PE1", CHANNEL_PE0_TO_PE1.sender(), in_pin).await;
defmt::info!("Example PE0 to PE1 done"); rprintln!("Example PE0 to PE1 done");
} }
if CHECK_PF0_TO_PF1 { if CHECK_PF0_TO_PF1 {
let out_pin = Output::new(portf.pf0, PinState::Low); let out_pin = portf.pf0.into_readable_push_pull_output();
let in_pin = Input::new_floating(portf.pf1); let in_pin = portf.pf1.into_floating_input();
let in_pin = InputPinAsync::new(in_pin).unwrap(); let out_pin = out_pin.downgrade();
let in_pin = InputDynPinAsync::new(in_pin.downgrade()).unwrap();
spawner spawner
.spawn(output_task( .spawn(output_task(
"PF0 to PF1", "PF0 to PF1",
@ -177,10 +195,10 @@ async fn main(spawner: Spawner) {
)) ))
.unwrap(); .unwrap();
check_pin_to_pin_async_ops("PF0 to PF1", CHANNEL_PF0_TO_PF1.sender(), in_pin).await; check_pin_to_pin_async_ops("PF0 to PF1", CHANNEL_PF0_TO_PF1.sender(), in_pin).await;
defmt::info!("Example PF0 to PF1 done"); rprintln!("Example PF0 to PF1 done");
} }
defmt::info!("Example done, toggling LED0"); rprintln!("Example done, toggling LED0");
loop { loop {
led.toggle(); led.toggle();
Timer::after(Duration::from_millis(500)).await; Timer::after(Duration::from_millis(500)).await;
@ -192,46 +210,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()
@ -240,13 +258,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()
@ -255,20 +273,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()
@ -279,7 +297,7 @@ async fn check_pin_to_pin_async_ops(
#[embassy_executor::task(pool_size = 8)] #[embassy_executor::task(pool_size = 8)]
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 {
@ -287,29 +305,29 @@ 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();
} }
GpioCmdType::CloseTask => { GpioCmdType::CloseTask => {
defmt::info!("{}: Closing task", ctx); rprintln!("{}: Closing task", ctx);
break; break;
} }
} }

View File

@ -12,12 +12,7 @@
//! RTT logs to see received data. //! RTT logs to see received data.
#![no_std] #![no_std]
#![no_main] #![no_main]
// Import panic provider.
use panic_probe as _;
// Import logger.
use core::cell::RefCell; use core::cell::RefCell;
use defmt_rtt as _;
use defmt_rtt as _;
use critical_section::Mutex; use critical_section::Mutex;
use embassy_example::EXTCLK_FREQ; use embassy_example::EXTCLK_FREQ;
@ -26,11 +21,11 @@ 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::{Producer, Queue}; use heapless::spsc::{Producer, Queue};
use panic_rtt_target as _;
use rtt_target::{rprintln, rtt_init_print};
use va416xx_hal::{ use va416xx_hal::{
clock::ClockConfigurator, gpio::PinsG,
gpio::{Output, PinState},
pac::{self, interrupt}, pac::{self, interrupt},
pins::PinsG,
prelude::*, prelude::*,
time::Hertz, time::Hertz,
uart::{ uart::{
@ -46,24 +41,37 @@ static PRODUCER_UART_A: Mutex<RefCell<Option<Producer<u8, 256>>>> = Mutex::new(R
#[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();
// Initialize the systick interrupt & obtain the token to prove that we did // Initialize the systick interrupt & obtain the token to prove that we did
// Use the external clock connected to XTAL_N. // Use the external clock connected to XTAL_N.
let clocks = ClockConfigurator::new(dp.clkgen) let clocks = dp
.clkgen
.constrain()
.xtal_n_clk_with_src_freq(Hertz::from_raw(EXTCLK_FREQ)) .xtal_n_clk_with_src_freq(Hertz::from_raw(EXTCLK_FREQ))
.freeze() .freeze(&mut dp.sysconfig)
.unwrap(); .unwrap();
// Safety: Only called once here. // Safety: Only called once here.
va416xx_embassy::init(dp.tim15, dp.tim14, &clocks); unsafe {
va416xx_embassy::init(
&mut dp.sysconfig,
&dp.irq_router,
dp.tim15,
dp.tim14,
&clocks,
);
}
let portg = PinsG::new(dp.portg); let portg = PinsG::new(&mut dp.sysconfig, dp.portg);
let mut led = Output::new(portg.pg5, PinState::Low); let mut led = portg.pg5.into_readable_push_pull_output();
let uarta = let tx = portg.pg0.into_funsel_1();
uart::Uart::new(dp.uart0, portg.pg0, portg.pg1, &clocks, 115200.Hz().into()).unwrap(); let rx = portg.pg1.into_funsel_1();
let uarta = uart::Uart::new(&mut dp.sysconfig, dp.uart0, (tx, rx), 115200.Hz(), &clocks);
let (mut tx_uart_a, rx_uart_a) = uarta.split(); let (mut tx_uart_a, rx_uart_a) = uarta.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();
@ -76,11 +84,11 @@ async fn main(_spawner: Spawner) {
let mut async_uart_rx = RxAsync::new(rx_uart_a, cons_uart_a); let mut async_uart_rx = RxAsync::new(rx_uart_a, cons_uart_a);
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());
led.toggle(); led.toggle();
let read_bytes = async_uart_rx.read(&mut buf).await.unwrap(); let read_bytes = async_uart_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 A: {:?}", "Read {} bytes asynchronously on UART A: {:?}",
read_bytes, read_bytes,
read_str read_str
@ -98,6 +106,6 @@ fn UART0_RX() {
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);
} }
} }

View File

@ -11,20 +11,15 @@
//! RTT logs to see received data. //! RTT logs to see received data.
#![no_std] #![no_std]
#![no_main] #![no_main]
// Import panic provider.
use panic_probe as _;
// Import logger.
use defmt_rtt as _;
use embassy_example::EXTCLK_FREQ; use embassy_example::EXTCLK_FREQ;
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 va416xx_hal::{ use va416xx_hal::{
clock::ClockConfigurator, gpio::PinsG,
gpio::{Output, PinState},
pac::{self, interrupt}, pac::{self, interrupt},
pins::PinsG,
prelude::*, prelude::*,
time::Hertz, time::Hertz,
uart::{ uart::{
@ -44,30 +39,43 @@ 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();
// Initialize the systick interrupt & obtain the token to prove that we did // Initialize the systick interrupt & obtain the token to prove that we did
// Use the external clock connected to XTAL_N. // Use the external clock connected to XTAL_N.
let clocks = ClockConfigurator::new(dp.clkgen) let clocks = dp
.clkgen
.constrain()
.xtal_n_clk_with_src_freq(Hertz::from_raw(EXTCLK_FREQ)) .xtal_n_clk_with_src_freq(Hertz::from_raw(EXTCLK_FREQ))
.freeze() .freeze(&mut dp.sysconfig)
.unwrap(); .unwrap();
// Safety: Only called once here. // Safety: Only called once here.
va416xx_embassy::init(dp.tim15, dp.tim14, &clocks); unsafe {
va416xx_embassy::init(
&mut dp.sysconfig,
&dp.irq_router,
dp.tim15,
dp.tim14,
&clocks,
);
}
let pinsg = PinsG::new(dp.portg); let portg = PinsG::new(&mut dp.sysconfig, dp.portg);
let mut led = Output::new(pinsg.pg5, PinState::Low); let mut led = portg.pg5.into_readable_push_pull_output();
let uarta = let tx = portg.pg0.into_funsel_1();
uart::Uart::new(dp.uart0, pinsg.pg0, pinsg.pg1, &clocks, 115200.Hz().into()).unwrap(); let rx = portg.pg1.into_funsel_1();
let uarta = uart::Uart::new(&mut dp.sysconfig, dp.uart0, (tx, rx), 115200.Hz(), &clocks);
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::println!("Current time: {}", Instant::now().as_secs()); rprintln!("Current time: {}", Instant::now().as_secs());
led.toggle(); led.toggle();
let _written = async_tx let _written = async_tx
.write(STR_LIST[idx].as_bytes()) .write(STR_LIST[idx].as_bytes())

View File

@ -9,11 +9,6 @@
//! on the UART without requiring polling. //! on the UART without requiring polling.
#![no_std] #![no_std]
#![no_main] #![no_main]
// Import panic provider.
use panic_probe as _;
// Import logger.
use defmt_rtt as _;
use core::cell::RefCell; use core::cell::RefCell;
use embassy_example::EXTCLK_FREQ; use embassy_example::EXTCLK_FREQ;
@ -22,20 +17,22 @@ use embassy_sync::blocking_mutex::raw::CriticalSectionRawMutex;
use embassy_sync::blocking_mutex::Mutex; use embassy_sync::blocking_mutex::Mutex;
use embassy_time::{Duration, Ticker}; use embassy_time::{Duration, Ticker};
use embedded_io::Write; use embedded_io::Write;
use panic_rtt_target as _;
use ringbuf::{ use ringbuf::{
traits::{Consumer, Observer, Producer}, traits::{Consumer, Observer, Producer},
StaticRb, StaticRb,
}; };
use rtt_target::{rprintln, rtt_init_print};
use va416xx_hal::{ use va416xx_hal::{
clock::ClockConfigurator, gpio::{OutputReadablePushPull, Pin, PinsG, PG5},
gpio::{Output, PinState},
pac::{self, interrupt}, pac::{self, interrupt},
pins::PinsG, prelude::*,
time::Hertz, time::Hertz,
uart, uart,
}; };
pub type SharedUart = Mutex<CriticalSectionRawMutex, RefCell<Option<uart::RxWithInterrupt>>>; pub type SharedUart =
Mutex<CriticalSectionRawMutex, RefCell<Option<uart::RxWithInterrupt<pac::Uart0>>>>;
static RX: SharedUart = Mutex::new(RefCell::new(None)); static RX: SharedUart = Mutex::new(RefCell::new(None));
const BAUDRATE: u32 = 115200; const BAUDRATE: u32 = 115200;
@ -52,29 +49,42 @@ static RINGBUF: SharedRingBuf = Mutex::new(RefCell::new(None));
// a peripheral with embassy. // a peripheral with embassy.
#[embassy_executor::main] #[embassy_executor::main]
async fn main(spawner: Spawner) { async fn main(spawner: Spawner) {
defmt::println!("VA416xx UART-Embassy Example"); rtt_init_print!();
rprintln!("VA416xx UART-Embassy Example");
let dp = pac::Peripherals::take().unwrap(); let mut dp = pac::Peripherals::take().unwrap();
// Initialize the systick interrupt & obtain the token to prove that we did // Initialize the systick interrupt & obtain the token to prove that we did
// Use the external clock connected to XTAL_N. // Use the external clock connected to XTAL_N.
let clocks = ClockConfigurator::new(dp.clkgen) let clocks = dp
.clkgen
.constrain()
.xtal_n_clk_with_src_freq(Hertz::from_raw(EXTCLK_FREQ)) .xtal_n_clk_with_src_freq(Hertz::from_raw(EXTCLK_FREQ))
.freeze() .freeze(&mut dp.sysconfig)
.unwrap(); .unwrap();
// Safety: Only called once here. // Safety: Only called once here.
va416xx_embassy::init(dp.tim15, dp.tim14, &clocks); unsafe {
va416xx_embassy::init(
&mut dp.sysconfig,
&dp.irq_router,
dp.tim15,
dp.tim14,
&clocks,
)
};
let portg = PinsG::new(dp.portg); let portg = PinsG::new(&mut dp.sysconfig, dp.portg);
let tx = portg.pg0.into_funsel_1();
let rx = portg.pg1.into_funsel_1();
let uart0 = uart::Uart::new( let uart0 = uart::Uart::new(
&mut dp.sysconfig,
dp.uart0, dp.uart0,
portg.pg0, (tx, rx),
portg.pg1, Hertz::from_raw(BAUDRATE),
&clocks, &clocks,
Hertz::from_raw(BAUDRATE).into(), );
)
.unwrap();
let (mut tx, rx) = uart0.split(); let (mut tx, rx) = uart0.split();
let mut rx = rx.into_rx_with_irq(); let mut rx = rx.into_rx_with_irq();
rx.start(); rx.start();
@ -85,7 +95,7 @@ async fn main(spawner: Spawner) {
static_rb.borrow_mut().replace(StaticRb::default()); static_rb.borrow_mut().replace(StaticRb::default());
}); });
let led = Output::new(portg.pg5, PinState::Low); let led = portg.pg5.into_readable_push_pull_output();
let mut ticker = Ticker::every(Duration::from_millis(50)); let mut ticker = Ticker::every(Duration::from_millis(50));
let mut processing_buf: [u8; RING_BUF_SIZE] = [0; RING_BUF_SIZE]; let mut processing_buf: [u8; RING_BUF_SIZE] = [0; RING_BUF_SIZE];
let mut read_bytes = 0; let mut read_bytes = 0;
@ -105,7 +115,7 @@ async fn main(spawner: Spawner) {
} }
#[embassy_executor::task] #[embassy_executor::task]
async fn blinky(mut led: Output) { async fn blinky(mut led: Pin<PG5, OutputReadablePushPull>) {
let mut ticker = Ticker::every(Duration::from_millis(500)); let mut ticker = Ticker::every(Duration::from_millis(500));
loop { loop {
led.toggle(); led.toggle();
@ -143,9 +153,9 @@ fn UART0_RX() {
} }
if errors.is_some() { if errors.is_some() {
defmt::info!("UART error: {:?}", errors); rprintln!("UART error: {:?}", errors);
} }
if ringbuf_full { if ringbuf_full {
defmt::info!("ringbuffer is full, deleted oldest data"); rprintln!("ringbuffer is full, deleted oldest data");
} }
} }

View File

@ -1,20 +1,11 @@
#![no_std] #![no_std]
#![no_main] #![no_main]
// Import panic provider.
use panic_probe as _;
// Import logger.
use defmt_rtt as _;
use embassy_example::EXTCLK_FREQ; use embassy_example::EXTCLK_FREQ;
use embassy_executor::Spawner; use embassy_executor::Spawner;
use embassy_time::{Duration, Instant, Ticker}; use embassy_time::{Duration, Instant, Ticker};
use va416xx_hal::{ use panic_rtt_target as _;
clock::ClockConfigurator, use rtt_target::{rprintln, rtt_init_print};
gpio::{Output, PinState}, use va416xx_hal::{gpio::PinsG, pac, prelude::*, time::Hertz};
pac,
pins::PinsG,
time::Hertz,
};
cfg_if::cfg_if! { cfg_if::cfg_if! {
if #[cfg(feature = "custom-irqs")] { if #[cfg(feature = "custom-irqs")] {
@ -26,38 +17,47 @@ cfg_if::cfg_if! {
// 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!("VA416xx Embassy Demo"); rtt_init_print!();
rprintln!("VA416xx Embassy Demo");
let dp = pac::Peripherals::take().unwrap(); let mut dp = pac::Peripherals::take().unwrap();
// Initialize the systick interrupt & obtain the token to prove that we did // Initialize the systick interrupt & obtain the token to prove that we did
// Use the external clock connected to XTAL_N. // Use the external clock connected to XTAL_N.
let clocks = ClockConfigurator::new(dp.clkgen) let clocks = dp
.clkgen
.constrain()
.xtal_n_clk_with_src_freq(Hertz::from_raw(EXTCLK_FREQ)) .xtal_n_clk_with_src_freq(Hertz::from_raw(EXTCLK_FREQ))
.freeze() .freeze(&mut dp.sysconfig)
.unwrap(); .unwrap();
// Safety: Only called once here. // Safety: Only called once here.
unsafe {
cfg_if::cfg_if! { cfg_if::cfg_if! {
if #[cfg(not(feature = "custom-irqs"))] { if #[cfg(not(feature = "custom-irqs"))] {
va416xx_embassy::init( va416xx_embassy::init(
&mut dp.sysconfig,
&dp.irq_router,
dp.tim15, dp.tim15,
dp.tim14, dp.tim14,
&clocks &clocks
); );
} else { } else {
va416xx_embassy::init( va416xx_embassy::init(
&mut dp.sysconfig,
&dp.irq_router,
dp.tim12, dp.tim12,
dp.tim11, dp.tim11,
&clocks &clocks
); );
} }
} }
let pinsg = PinsG::new(dp.portg); }
let mut led = Output::new(pinsg.pg5, PinState::Low); let portg = PinsG::new(&mut dp.sysconfig, dp.portg);
let mut led = portg.pg5.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());
led.toggle(); led.toggle();
} }
} }

View File

@ -5,11 +5,13 @@ edition = "2021"
[dependencies] [dependencies]
cortex-m = { version = "0.7", features = ["critical-section-single-core"] } cortex-m = { version = "0.7", features = ["critical-section-single-core"] }
defmt-rtt = "0.4" cortex-m-rt = "0.7"
defmt = "1" embedded-hal = "1"
panic-probe = { version = "1", features = ["defmt"] } rtt-target = { version = "0.6" }
rtic-sync = { version = "1.3", features = ["defmt-03"] }
panic-rtt-target = { version = "0.2" }
va416xx-hal = { version = "0.5", path = "../../va416xx-hal", features = ["va41630"] } va416xx-hal = { version = "0.5", features = ["va41630"], path = "../../va416xx-hal" }
[dependencies.rtic] [dependencies.rtic]
version = "2" version = "2"

View File

@ -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 logger.
use defmt_rtt as _;
use va416xx_hal::pac; use va416xx_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 {})
} }

View File

@ -9,23 +9,20 @@ const EXTCLK_FREQ: Hertz = Hertz::from_raw(40_000_000);
#[rtic::app(device = pac, dispatchers = [U1, U2, U3])] #[rtic::app(device = pac, dispatchers = [U1, U2, U3])]
mod app { mod app {
use super::*; use super::*;
// Import panic provider.
use panic_probe as _;
// Import logger.
use cortex_m::asm; use cortex_m::asm;
use defmt_rtt as _; use panic_rtt_target as _;
use rtic_monotonics::systick::prelude::*; use rtic_monotonics::systick::prelude::*;
use rtic_monotonics::Monotonic; use rtic_monotonics::Monotonic;
use rtt_target::{rprintln, rtt_init_default};
use va416xx_hal::{ use va416xx_hal::{
clock::ClockConfigurator, gpio::{OutputReadablePushPull, Pin, PinsG, PG5},
gpio::{Output, PinState},
pac, pac,
pins::PinsG, prelude::*,
}; };
#[local] #[local]
struct Local { struct Local {
led: Output, led: Pin<PG5, OutputReadablePushPull>,
} }
#[shared] #[shared]
@ -34,16 +31,20 @@ 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 RTIC example application --"); rtt_init_default!();
rprintln!("-- Vorago RTIC example application --");
// Use the external clock connected to XTAL_N. // Use the external clock connected to XTAL_N.
let clocks = ClockConfigurator::new(cx.device.clkgen) let clocks = cx
.device
.clkgen
.constrain()
.xtal_n_clk_with_src_freq(EXTCLK_FREQ) .xtal_n_clk_with_src_freq(EXTCLK_FREQ)
.freeze() .freeze(&mut cx.device.sysconfig)
.unwrap(); .unwrap();
Mono::start(cx.core.SYST, clocks.sysclk().raw()); Mono::start(cx.core.SYST, clocks.sysclk().raw());
let pinsg = PinsG::new(cx.device.portg); let portg = PinsG::new(&mut cx.device.sysconfig, cx.device.portg);
let led = Output::new(pinsg.pg5, PinState::Low); let led = portg.pg5.into_readable_push_pull_output();
blinky::spawn().ok(); blinky::spawn().ok();
(Shared {}, Local { led }) (Shared {}, Local { led })
} }

View File

@ -7,9 +7,8 @@ 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"
critical-section = "1" critical-section = "1"
defmt-rtt = "0.4" panic-rtt-target = { version = "0.2" }
defmt = "1" rtt-target = { version = "0.6" }
panic-probe = { version = "1", features = ["defmt"] }
embedded-hal = "1" embedded-hal = "1"
embedded-hal-nb = "1" embedded-hal-nb = "1"
nb = "1" nb = "1"
@ -17,12 +16,20 @@ embedded-io = "0.6"
panic-halt = "1" panic-halt = "1"
accelerometer = "0.12" accelerometer = "0.12"
va416xx-hal = { version = "0.5", path = "../../va416xx-hal", features = ["va41630", "defmt"] } va416xx-hal = { version = "0.5", features = ["va41630"], path = "../../va416xx-hal" }
[dependencies.vorago-peb1] [dependencies.vorago-peb1]
path = "../../vorago-peb1" path = "../../vorago-peb1"
optional = true optional = true
[dependencies.rtic]
version = "2"
features = ["thumbv7-backend"]
[dependencies.rtic-monotonics]
version = "2"
features = ["cortex-m-systick"]
[features] [features]
default = ["va41630"] default = ["va41630"]
va41630 = ["va416xx-hal/va41630", "has-adc-dac"] va41630 = ["va416xx-hal/va41630", "has-adc-dac"]

View File

@ -2,18 +2,15 @@
#![no_main] #![no_main]
#![no_std] #![no_std]
// Import panic provider.
use panic_probe as _;
// Import logger.
use defmt_rtt as _;
use cortex_m_rt::entry; use cortex_m_rt::entry;
use embedded_hal::delay::DelayNs; use embedded_hal::delay::DelayNs;
use panic_rtt_target as _;
use rtt_target::{rprintln, rtt_init_print};
use simple_examples::peb1; use simple_examples::peb1;
use va416xx_hal::{ use va416xx_hal::{
adc::{Adc, ChannelSelect, ChannelValue, MultiChannelSelect}, adc::{Adc, ChannelSelect, ChannelValue, MultiChannelSelect},
clock::ClockConfigurator,
pac, pac,
prelude::*,
timer::CountdownTimer, timer::CountdownTimer,
}; };
@ -22,23 +19,26 @@ const ENABLE_BUF_PRINTOUT: bool = false;
#[entry] #[entry]
fn main() -> ! { fn main() -> ! {
defmt::println!("VA416xx ADC example"); rtt_init_print!();
rprintln!("VA416xx ADC example");
let dp = pac::Peripherals::take().unwrap(); let mut dp = pac::Peripherals::take().unwrap();
// Use the external clock connected to XTAL_N. // Use the external clock connected to XTAL_N.
let clocks = ClockConfigurator::new(dp.clkgen) let clocks = dp
.clkgen
.constrain()
.xtal_n_clk_with_src_freq(peb1::EXTCLK_FREQ) .xtal_n_clk_with_src_freq(peb1::EXTCLK_FREQ)
.freeze() .freeze(&mut dp.sysconfig)
.unwrap(); .unwrap();
let adc = Adc::new_with_channel_tag(dp.adc, &clocks); let adc = Adc::new_with_channel_tag(&mut dp.sysconfig, dp.adc, &clocks);
let mut delay = CountdownTimer::new(dp.tim0, &clocks); let mut delay_provider = CountdownTimer::new(&mut dp.sysconfig, dp.tim0, &clocks);
let mut read_buf: [ChannelValue; 8] = [ChannelValue::default(); 8]; let mut read_buf: [ChannelValue; 8] = [ChannelValue::default(); 8];
loop { loop {
let single_value = adc let single_value = adc
.trigger_and_read_single_channel(va416xx_hal::adc::ChannelSelect::TempSensor) .trigger_and_read_single_channel(va416xx_hal::adc::ChannelSelect::TempSensor)
.expect("reading single channel value failed"); .expect("reading single channel value failed");
defmt::info!( rprintln!(
"Read single ADC value on temperature sensor channel: {:?}", "Read single ADC value on temperature sensor channel: {:?}",
single_value single_value
); );
@ -46,8 +46,8 @@ fn main() -> ! {
.sweep_and_read_range(0, 7, &mut read_buf) .sweep_and_read_range(0, 7, &mut read_buf)
.expect("ADC range read failed"); .expect("ADC range read failed");
if ENABLE_BUF_PRINTOUT { if ENABLE_BUF_PRINTOUT {
defmt::info!("ADC Range Read (0-8) read {} values", read_num); rprintln!("ADC Range Read (0-8) read {} values", read_num);
defmt::info!("ADC Range Read (0-8): {:?}", read_buf); rprintln!("ADC Range Read (0-8): {:?}", read_buf);
} }
assert_eq!(read_num, 8); assert_eq!(read_num, 8);
for (idx, ch_val) in read_buf.iter().enumerate() { for (idx, ch_val) in read_buf.iter().enumerate() {
@ -63,11 +63,11 @@ fn main() -> ! {
) )
.expect("ADC multiselect read failed"); .expect("ADC multiselect read failed");
if ENABLE_BUF_PRINTOUT { if ENABLE_BUF_PRINTOUT {
defmt::info!("ADC Multiselect Read(0, 2 and 10): {:?}", &read_buf[0..3]); rprintln!("ADC Multiselect Read(0, 2 and 10): {:?}", &read_buf[0..3]);
} }
assert_eq!(read_buf[0].channel(), ChannelSelect::AnIn0); assert_eq!(read_buf[0].channel(), ChannelSelect::AnIn0);
assert_eq!(read_buf[1].channel(), ChannelSelect::AnIn2); assert_eq!(read_buf[1].channel(), ChannelSelect::AnIn2);
assert_eq!(read_buf[2].channel(), ChannelSelect::TempSensor); assert_eq!(read_buf[2].channel(), ChannelSelect::TempSensor);
delay.delay_ms(500); delay_provider.delay_ms(500);
} }
} }

View File

@ -2,25 +2,19 @@
#![no_main] #![no_main]
#![no_std] #![no_std]
// Import panic provider.
use panic_probe as _;
// Import logger.
use defmt_rtt as _;
use cortex_m_rt::entry; use cortex_m_rt::entry;
use va416xx_hal::{ use panic_rtt_target as _;
gpio::{Output, PinState}, use rtt_target::{rprintln, rtt_init_print};
pac, use va416xx_hal::{gpio::PinsG, pac};
pins::PinsG,
};
#[entry] #[entry]
fn main() -> ! { fn main() -> ! {
defmt::println!("VA416xx HAL blinky example"); rtt_init_print!();
rprintln!("VA416xx HAL blinky example");
let dp = pac::Peripherals::take().unwrap(); let mut dp = pac::Peripherals::take().unwrap();
let portg = PinsG::new(dp.portg); let portg = PinsG::new(&mut dp.sysconfig, dp.portg);
let mut led = Output::new(portg.pg5, PinState::Low); let mut led = portg.pg5.into_readable_push_pull_output();
loop { loop {
cortex_m::asm::delay(2_000_000); cortex_m::asm::delay(2_000_000);
led.toggle(); led.toggle();

View File

@ -2,15 +2,12 @@
#![no_main] #![no_main]
#![no_std] #![no_std]
// Import panic provider.
use panic_probe as _;
// Import logger.
use defmt_rtt as _;
use cortex_m_rt::entry; use cortex_m_rt::entry;
use embedded_hal::delay::DelayNs; use embedded_hal::delay::DelayNs;
use panic_rtt_target as _;
use rtt_target::{rprintln, rtt_init_print};
use simple_examples::peb1; use simple_examples::peb1;
use va416xx_hal::{adc::Adc, clock::ClockConfigurator, dac::Dac, pac, timer::CountdownTimer}; use va416xx_hal::{adc::Adc, dac::Dac, pac, prelude::*, timer::CountdownTimer};
const DAC_INCREMENT: u16 = 256; const DAC_INCREMENT: u16 = 256;
@ -28,17 +25,21 @@ const APP_MODE: AppMode = AppMode::DacAndAdc;
#[entry] #[entry]
fn main() -> ! { fn main() -> ! {
defmt::println!("VA416xx DAC/ADC example"); rtt_init_print!();
rprintln!("VA416xx DAC/ADC example");
let dp = pac::Peripherals::take().unwrap(); let mut dp = pac::Peripherals::take().unwrap();
// Use the external clock connected to XTAL_N. // Use the external clock connected to XTAL_N.
let clocks = ClockConfigurator::new(dp.clkgen) let clocks = dp
.clkgen
.constrain()
.xtal_n_clk_with_src_freq(peb1::EXTCLK_FREQ) .xtal_n_clk_with_src_freq(peb1::EXTCLK_FREQ)
.freeze() .freeze(&mut dp.sysconfig)
.unwrap(); .unwrap();
let mut dac = None; let mut dac = None;
if APP_MODE == AppMode::DacOnly || APP_MODE == AppMode::DacAndAdc { if APP_MODE == AppMode::DacOnly || APP_MODE == AppMode::DacAndAdc {
dac = Some(Dac::new( dac = Some(Dac::new(
&mut dp.sysconfig,
dp.dac0, dp.dac0,
va416xx_hal::dac::DacSettling::Apb2Times100, va416xx_hal::dac::DacSettling::Apb2Times100,
&clocks, &clocks,
@ -46,13 +47,13 @@ fn main() -> ! {
} }
let mut adc = None; let mut adc = None;
if APP_MODE == AppMode::AdcOnly || APP_MODE == AppMode::DacAndAdc { if APP_MODE == AppMode::AdcOnly || APP_MODE == AppMode::DacAndAdc {
adc = Some(Adc::new(dp.adc, &clocks)); adc = Some(Adc::new(&mut dp.sysconfig, dp.adc, &clocks));
} }
let mut delay_provider = CountdownTimer::new(dp.tim0, &clocks); let mut delay_provider = CountdownTimer::new(&mut dp.sysconfig, dp.tim0, &clocks);
let mut current_val = 0; let mut current_val = 0;
loop { loop {
if let Some(dac) = &mut dac { if let Some(dac) = &dac {
defmt::info!("loading DAC with value {}", current_val); rprintln!("loading DAC with value {}", current_val);
dac.load_and_trigger_manually(current_val) dac.load_and_trigger_manually(current_val)
.expect("loading DAC value failed"); .expect("loading DAC value failed");
if current_val + DAC_INCREMENT >= 4096 { if current_val + DAC_INCREMENT >= 4096 {
@ -69,7 +70,7 @@ fn main() -> ! {
let ch_value = adc let ch_value = adc
.trigger_and_read_single_channel(va416xx_hal::adc::ChannelSelect::AnIn0) .trigger_and_read_single_channel(va416xx_hal::adc::ChannelSelect::AnIn0)
.expect("reading ADC channel 0 failed"); .expect("reading ADC channel 0 failed");
defmt::info!("Received channel value {:?}", ch_value); rprintln!("Received channel value {:?}", ch_value);
} }
delay_provider.delay_ms(500); delay_provider.delay_ms(500);

View File

@ -2,22 +2,21 @@
#![no_main] #![no_main]
#![no_std] #![no_std]
// Import panic provider.
use panic_probe as _;
// Import logger.
use defmt_rtt as _;
use va416xx_hal::clock::ClockConfigurator;
use core::cell::Cell; use core::cell::Cell;
use cortex_m_rt::entry; use cortex_m_rt::entry;
use critical_section::Mutex; use critical_section::Mutex;
use embedded_hal::delay::DelayNs; use embedded_hal::delay::DelayNs;
use panic_rtt_target as _;
use rtt_target::{rprintln, rtt_init_print};
use simple_examples::peb1; use simple_examples::peb1;
use va416xx_hal::dma::{Dma, DmaCfg, DmaChannel, DmaCtrlBlock}; use va416xx_hal::dma::{Dma, DmaCfg, DmaChannel, DmaCtrlBlock};
use va416xx_hal::irq_router::enable_and_init_irq_router; use va416xx_hal::irq_router::enable_and_init_irq_router;
use va416xx_hal::pac::{self, interrupt};
use va416xx_hal::timer::CountdownTimer; use va416xx_hal::timer::CountdownTimer;
use va416xx_hal::{
pac::{self, interrupt},
prelude::*,
};
static DMA_DONE_FLAG: Mutex<Cell<bool>> = Mutex::new(Cell::new(false)); static DMA_DONE_FLAG: Mutex<Cell<bool>> = Mutex::new(Cell::new(false));
static DMA_ACTIVE_FLAG: Mutex<Cell<bool>> = Mutex::new(Cell::new(false)); static DMA_ACTIVE_FLAG: Mutex<Cell<bool>> = Mutex::new(Cell::new(false));
@ -36,25 +35,29 @@ static mut DMA_DEST_BUF: [u16; 36] = [0; 36];
#[entry] #[entry]
fn main() -> ! { fn main() -> ! {
defmt::println!("VA416xx DMA example"); rtt_init_print!();
rprintln!("VA416xx DMA example");
let dp = pac::Peripherals::take().unwrap(); let mut dp = pac::Peripherals::take().unwrap();
// Use the external clock connected to XTAL_N. // Use the external clock connected to XTAL_N.
let clocks = ClockConfigurator::new(dp.clkgen) let clocks = dp
.clkgen
.constrain()
.xtal_n_clk_with_src_freq(peb1::EXTCLK_FREQ) .xtal_n_clk_with_src_freq(peb1::EXTCLK_FREQ)
.freeze() .freeze(&mut dp.sysconfig)
.unwrap(); .unwrap();
enable_and_init_irq_router(); enable_and_init_irq_router(&mut dp.sysconfig, &dp.irq_router);
// Safety: The DMA control block has an alignment rule of 128 and we constructed it directly // Safety: The DMA control block has an alignment rule of 128 and we constructed it directly
// statically. // statically.
let dma = Dma::new( let dma = Dma::new(
&mut dp.sysconfig,
dp.dma, dp.dma,
DmaCfg::default(), DmaCfg::default(),
core::ptr::addr_of_mut!(DMA_CTRL_BLOCK), core::ptr::addr_of_mut!(DMA_CTRL_BLOCK),
) )
.expect("error creating DMA"); .expect("error creating DMA");
let (mut dma0, _, _, _) = dma.split(); let (mut dma0, _, _, _) = dma.split();
let mut delay_ms = CountdownTimer::new(dp.tim0, &clocks); let mut delay_ms = CountdownTimer::new(&mut dp.sysconfig, dp.tim0, &clocks);
let mut src_buf_8_bit: [u8; 65] = [0; 65]; let mut src_buf_8_bit: [u8; 65] = [0; 65];
let mut dest_buf_8_bit: [u8; 65] = [0; 65]; let mut dest_buf_8_bit: [u8; 65] = [0; 65];
let mut src_buf_32_bit: [u32; 17] = [0; 17]; let mut src_buf_32_bit: [u32; 17] = [0; 17];
@ -85,7 +88,7 @@ fn transfer_example_8_bit(
src_buf: &mut [u8; 65], src_buf: &mut [u8; 65],
dest_buf: &mut [u8; 65], dest_buf: &mut [u8; 65],
dma0: &mut DmaChannel, dma0: &mut DmaChannel,
delay: &mut CountdownTimer, delay_ms: &mut CountdownTimer<pac::Tim0>,
) { ) {
(0..64).for_each(|i| { (0..64).for_each(|i| {
src_buf[i] = i as u8; src_buf[i] = i as u8;
@ -116,7 +119,7 @@ fn transfer_example_8_bit(
let mut dma_done = false; let mut dma_done = false;
critical_section::with(|cs| { critical_section::with(|cs| {
if DMA_ACTIVE_FLAG.borrow(cs).get() { if DMA_ACTIVE_FLAG.borrow(cs).get() {
defmt::info!("DMA0 is active with 8 bit transfer"); rprintln!("DMA0 is active with 8 bit transfer");
DMA_ACTIVE_FLAG.borrow(cs).set(false); DMA_ACTIVE_FLAG.borrow(cs).set(false);
} }
if DMA_DONE_FLAG.borrow(cs).get() { if DMA_DONE_FLAG.borrow(cs).get() {
@ -124,10 +127,10 @@ fn transfer_example_8_bit(
} }
}); });
if dma_done { if dma_done {
defmt::info!("8-bit transfer done"); rprintln!("8-bit transfer done");
break; break;
} }
delay.delay_ms(1); delay_ms.delay_ms(1);
} }
(0..64).for_each(|i| { (0..64).for_each(|i| {
assert_eq!(dest_buf[i], i as u8); assert_eq!(dest_buf[i], i as u8);
@ -137,7 +140,7 @@ fn transfer_example_8_bit(
dest_buf.fill(0); dest_buf.fill(0);
} }
fn transfer_example_16_bit(dma0: &mut DmaChannel, delay_ms: &mut CountdownTimer) { fn transfer_example_16_bit(dma0: &mut DmaChannel, delay_ms: &mut CountdownTimer<pac::Tim0>) {
let dest_buf_ref = unsafe { &mut *core::ptr::addr_of_mut!(DMA_DEST_BUF[0..33]) }; let dest_buf_ref = unsafe { &mut *core::ptr::addr_of_mut!(DMA_DEST_BUF[0..33]) };
unsafe { unsafe {
// Set values scaled from 0 to 65535 to verify this is really a 16-bit transfer. // Set values scaled from 0 to 65535 to verify this is really a 16-bit transfer.
@ -174,7 +177,7 @@ fn transfer_example_16_bit(dma0: &mut DmaChannel, delay_ms: &mut CountdownTimer)
let mut dma_done = false; let mut dma_done = false;
critical_section::with(|cs| { critical_section::with(|cs| {
if DMA_ACTIVE_FLAG.borrow(cs).get() { if DMA_ACTIVE_FLAG.borrow(cs).get() {
defmt::info!("DMA0 is active with 16-bit transfer"); rprintln!("DMA0 is active with 16-bit transfer");
DMA_ACTIVE_FLAG.borrow(cs).set(false); DMA_ACTIVE_FLAG.borrow(cs).set(false);
} }
if DMA_DONE_FLAG.borrow(cs).get() { if DMA_DONE_FLAG.borrow(cs).get() {
@ -182,7 +185,7 @@ fn transfer_example_16_bit(dma0: &mut DmaChannel, delay_ms: &mut CountdownTimer)
} }
}); });
if dma_done { if dma_done {
defmt::info!("16-bit transfer done"); rprintln!("16-bit transfer done");
break; break;
} }
delay_ms.delay_ms(1); delay_ms.delay_ms(1);
@ -202,7 +205,7 @@ fn transfer_example_32_bit(
src_buf: &mut [u32; 17], src_buf: &mut [u32; 17],
dest_buf: &mut [u32; 17], dest_buf: &mut [u32; 17],
dma0: &mut DmaChannel, dma0: &mut DmaChannel,
delay_ms: &mut CountdownTimer, delay_ms: &mut CountdownTimer<pac::Tim0>,
) { ) {
// Set values scaled from 0 to 65535 to verify this is really a 16-bit transfer. // Set values scaled from 0 to 65535 to verify this is really a 16-bit transfer.
(0..16).for_each(|i| { (0..16).for_each(|i| {
@ -234,7 +237,7 @@ fn transfer_example_32_bit(
let mut dma_done = false; let mut dma_done = false;
critical_section::with(|cs| { critical_section::with(|cs| {
if DMA_ACTIVE_FLAG.borrow(cs).get() { if DMA_ACTIVE_FLAG.borrow(cs).get() {
defmt::info!("DMA0 is active with 32-bit transfer"); rprintln!("DMA0 is active with 32-bit transfer");
DMA_ACTIVE_FLAG.borrow(cs).set(false); DMA_ACTIVE_FLAG.borrow(cs).set(false);
} }
if DMA_DONE_FLAG.borrow(cs).get() { if DMA_DONE_FLAG.borrow(cs).get() {
@ -242,7 +245,7 @@ fn transfer_example_32_bit(
} }
}); });
if dma_done { if dma_done {
defmt::info!("32-bit transfer done"); rprintln!("32-bit transfer done");
break; break;
} }
delay_ms.delay_ms(1); delay_ms.delay_ms(1);

View File

@ -2,21 +2,17 @@
#![no_main] #![no_main]
#![no_std] #![no_std]
// Import panic provider.
use panic_probe as _;
// Import logger.
use defmt_rtt as _;
use accelerometer::{Accelerometer, RawAccelerometer}; use accelerometer::{Accelerometer, RawAccelerometer};
use cortex_m_rt::entry; use cortex_m_rt::entry;
use embedded_hal::delay::DelayNs; use embedded_hal::delay::DelayNs;
use panic_rtt_target as _;
use rtt_target::{rprintln, rtt_init_print};
use simple_examples::peb1; use simple_examples::peb1;
use va416xx_hal::{ use va416xx_hal::{
clock::ClockConfigurator,
i2c, i2c,
pac::{self}, pac::{self},
prelude::*, prelude::*,
timer::CountdownTimer, pwm::CountdownTimer,
}; };
use vorago_peb1::lis2dh12::{self, detect_i2c_addr, FullScale, Odr}; use vorago_peb1::lis2dh12::{self, detect_i2c_addr, FullScale, Odr};
@ -29,21 +25,25 @@ const DISPLAY_MODE: DisplayMode = DisplayMode::Normalized;
#[entry] #[entry]
fn main() -> ! { fn main() -> ! {
rtt_init_print!();
let mut dp = pac::Peripherals::take().unwrap(); let mut dp = pac::Peripherals::take().unwrap();
defmt::println!("-- Vorago PEB1 accelerometer example --"); rprintln!("-- Vorago PEB1 accelerometer example --");
// Use the external clock connected to XTAL_N. // Use the external clock connected to XTAL_N.
let clocks = ClockConfigurator::new(dp.clkgen) let clocks = dp
.clkgen
.constrain()
.xtal_n_clk_with_src_freq(peb1::EXTCLK_FREQ) .xtal_n_clk_with_src_freq(peb1::EXTCLK_FREQ)
.freeze() .freeze(&mut dp.sysconfig)
.unwrap(); .unwrap();
let mut i2c_master = i2c::I2cMaster::new( let mut i2c_master = i2c::I2cMaster::new(
dp.i2c0, dp.i2c0,
&clocks, &mut dp.sysconfig,
i2c::MasterConfig::default(), i2c::MasterConfig::default(),
&clocks,
i2c::I2cSpeed::Regular100khz, i2c::I2cSpeed::Regular100khz,
) )
.expect("creating I2C master failed"); .expect("creating I2C master failed");
let mut delay_provider = CountdownTimer::new(dp.tim1, &clocks); let mut delay_provider = CountdownTimer::new(&mut dp.sysconfig, dp.tim1, &clocks);
// Detect the I2C address of the accelerometer by scanning all possible values. // Detect the I2C address of the accelerometer by scanning all possible values.
let slave_addr = detect_i2c_addr(&mut i2c_master).expect("detecting I2C address failed"); let slave_addr = detect_i2c_addr(&mut i2c_master).expect("detecting I2C address failed");
// Create the accelerometer driver using the PEB1 BSP. // Create the accelerometer driver using the PEB1 BSP.
@ -51,7 +51,7 @@ fn main() -> ! {
.expect("creating accelerometer driver failed"); .expect("creating accelerometer driver failed");
let device_id = accelerometer.get_device_id().unwrap(); let device_id = accelerometer.get_device_id().unwrap();
accelerometer accelerometer
.set_mode(lis2dh12::Mode::Normal) .set_mode(lis2dh12::reg::Mode::Normal)
.expect("setting mode failed"); .expect("setting mode failed");
accelerometer accelerometer
.set_odr(Odr::Hz100) .set_odr(Odr::Hz100)
@ -63,7 +63,7 @@ fn main() -> ! {
accelerometer accelerometer
.enable_temp(true) .enable_temp(true)
.expect("enabling temperature sensor failed"); .expect("enabling temperature sensor failed");
defmt::info!("Device ID: 0x{:02X}", device_id); rprintln!("Device ID: 0x{:02X}", device_id);
// Start reading the accelerometer periodically. // Start reading the accelerometer periodically.
loop { loop {
let temperature = accelerometer let temperature = accelerometer
@ -74,25 +74,13 @@ fn main() -> ! {
let value = accelerometer let value = accelerometer
.accel_norm() .accel_norm()
.expect("reading normalized accelerometer data failed"); .expect("reading normalized accelerometer data failed");
defmt::info!( rprintln!("Accel Norm F32x3: {:.06?} | Temp {} °C", value, temperature);
"Accel Norm F32x3 {{ x: {:05}, y: {:05}, z:{:05}}} | Temp {} °C",
value.x,
value.y,
value.z,
temperature
);
} }
DisplayMode::Raw => { DisplayMode::Raw => {
let value_raw = accelerometer let value_raw = accelerometer
.accel_raw() .accel_raw()
.expect("reading raw accelerometer data failed"); .expect("reading raw accelerometer data failed");
defmt::info!( rprintln!("Accel Raw F32x3: {:?} | Temp {} °C", value_raw, temperature);
"Accel Raw I32x3 {{ x: {:05}, y: {:05}, z:{:05}}} | Temp {} °C",
value_raw.x,
value_raw.y,
value_raw.z,
temperature
);
} }
} }
delay_provider.delay_ms(100); delay_provider.delay_ms(100);

View File

@ -1,45 +1,49 @@
//! Simple PWM example //! Simple PWM example
//!
//! Outputs a PWM waveform on pin PG2.
#![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 simple_examples::peb1; use simple_examples::peb1;
use va416xx_hal::{ use va416xx_hal::{
clock::ClockConfigurator, gpio::PinsA,
pac, pac,
pins::PinsG,
prelude::*, prelude::*,
pwm::{get_duty_from_percent, PwmA, PwmB, PwmPin}, pwm::{self, get_duty_from_percent, PwmA, PwmB, ReducedPwmPin},
timer::CountdownTimer, timer::CountdownTimer,
}; };
#[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 mut dp = pac::Peripherals::take().unwrap();
// Use the external clock connected to XTAL_N. // Use the external clock connected to XTAL_N.
let clocks = ClockConfigurator::new(dp.clkgen) let clocks = dp
.clkgen
.constrain()
.xtal_n_clk_with_src_freq(peb1::EXTCLK_FREQ) .xtal_n_clk_with_src_freq(peb1::EXTCLK_FREQ)
.freeze() .freeze(&mut dp.sysconfig)
.unwrap(); .unwrap();
let pinsg = PinsG::new(dp.portg); let pinsa = PinsA::new(&mut dp.sysconfig, dp.porta);
let mut pwm = PwmPin::new(pinsg.pg2, dp.tim9, &clocks, 10.Hz()).unwrap(); let mut pwm = pwm::PwmPin::new(
let mut delay_timer = CountdownTimer::new(dp.tim0, &clocks); (pinsa.pa3.into_funsel_1(), dp.tim3),
&mut dp.sysconfig,
&clocks,
10.Hz(),
);
let mut delay_timer = CountdownTimer::new(&mut dp.sysconfig, dp.tim0, &clocks);
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
@ -48,10 +52,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();
} }
@ -60,7 +65,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 {
@ -69,9 +74,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);
} }
} }

View File

@ -0,0 +1,36 @@
// 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 va416xx_hal::pac;
// Mask for the LED
const LED_PG5: u32 = 1 << 5;
#[entry]
fn main() -> ! {
let dp = pac::Peripherals::take().unwrap();
// Enable all peripheral clocks
dp.sysconfig
.peripheral_clk_enable()
.modify(|_, w| unsafe { w.bits(0xffffffff) });
dp.portg.dir().modify(|_, w| unsafe { w.bits(LED_PG5) });
dp.portg
.datamask()
.modify(|_, w| unsafe { w.bits(LED_PG5) });
rtt_init_print!();
rprintln!("VA416xx RTT Demo");
let mut counter = 0;
loop {
rprintln!("{}: Hello, world!", counter);
// Still toggle LED. If there are issues with the RTT log, the LED
// blinking ensures that the application is actually running.
dp.portg.togout().write(|w| unsafe { w.bits(LED_PG5) });
counter += 1;
cortex_m::asm::delay(10_000_000);
}
}

View File

@ -3,21 +3,16 @@
//! If you do not use the loopback mode, MOSI and MISO need to be tied together on the board. //! If you do not use the loopback mode, MOSI and MISO need to be tied together on the board.
#![no_main] #![no_main]
#![no_std] #![no_std]
use embedded_hal::delay::DelayNs;
// Import panic provider.
use panic_probe as _;
// Import logger.
use defmt_rtt as _;
use cortex_m_rt::entry; use cortex_m_rt::entry;
use embedded_hal::spi::{Mode, SpiBus, MODE_0}; use embedded_hal::spi::{Mode, SpiBus, MODE_0};
use panic_rtt_target as _;
use rtt_target::{rprintln, rtt_init_print};
use simple_examples::peb1; use simple_examples::peb1;
use va416xx_hal::clock::ClockConfigurator;
use va416xx_hal::spi::{Spi, SpiClkConfig}; use va416xx_hal::spi::{Spi, SpiClkConfig};
use va416xx_hal::timer::CountdownTimer;
use va416xx_hal::{ use va416xx_hal::{
gpio::{PinsB, PinsC},
pac, pac,
pins::{PinsB, PinsC}, prelude::*,
spi::SpiConfig, spi::SpiConfig,
time::Hertz, time::Hertz,
}; };
@ -38,21 +33,31 @@ 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 cp = cortex_m::Peripherals::take().unwrap();
let mut dp = pac::Peripherals::take().unwrap();
// Use the external clock connected to XTAL_N. // Use the external clock connected to XTAL_N.
let clocks = ClockConfigurator::new(dp.clkgen) let clocks = dp
.clkgen
.constrain()
.xtal_n_clk_with_src_freq(peb1::EXTCLK_FREQ) .xtal_n_clk_with_src_freq(peb1::EXTCLK_FREQ)
.freeze() .freeze(&mut dp.sysconfig)
.unwrap(); .unwrap();
let mut delay = CountdownTimer::new(dp.tim1, &clocks); let mut delay_sysclk = cortex_m::delay::Delay::new(cp.SYST, clocks.apb0().raw());
let pins_b = PinsB::new(dp.portb); let pins_b = PinsB::new(&mut dp.sysconfig, dp.portb);
let pins_c = PinsC::new(dp.portc); let pins_c = PinsC::new(&mut dp.sysconfig, dp.portc);
// Configure SPI0 pins.
let (sck, miso, mosi) = (
pins_b.pb15.into_funsel_1(),
pins_c.pc0.into_funsel_1(),
pins_c.pc1.into_funsel_1(),
);
let mut spi_cfg = SpiConfig::default() let mut spi_cfg = SpiConfig::default()
.clk_cfg( .clk_cfg(
SpiClkConfig::from_clks(&clocks, Hertz::from_raw(SPI_SPEED_KHZ)) SpiClkConfig::from_clk(Hertz::from_raw(SPI_SPEED_KHZ), &clocks)
.expect("invalid target clock"), .expect("invalid target clock"),
) )
.mode(SPI_MODE) .mode(SPI_MODE)
@ -61,7 +66,13 @@ fn main() -> ! {
spi_cfg = spi_cfg.loopback(true) spi_cfg = spi_cfg.loopback(true)
} }
// Create SPI peripheral. // Create SPI peripheral.
let mut spi0 = Spi::new(dp.spi0, (pins_b.pb15, pins_c.pc0, pins_c.pc1), spi_cfg).unwrap(); let mut spi0 = Spi::new(
&mut dp.sysconfig,
&clocks,
dp.spi0,
(sck, miso, mosi),
spi_cfg,
);
spi0.set_fill_word(FILL_WORD); spi0.set_fill_word(FILL_WORD);
loop { loop {
let tx_buf: [u8; 4] = [1, 2, 3, 0]; let tx_buf: [u8; 4] = [1, 2, 3, 0];
@ -82,6 +93,6 @@ fn main() -> ! {
spi0.transfer(&mut rx_buf, &tx_buf) spi0.transfer(&mut rx_buf, &tx_buf)
.expect("SPI transfer failed"); .expect("SPI transfer failed");
assert_eq!(rx_buf, [1, 2, 3, 0]); assert_eq!(rx_buf, [1, 2, 3, 0]);
delay.delay_ms(500); delay_sysclk.delay_ms(500);
} }
} }

View File

@ -1,21 +1,19 @@
//! MS and Second counter implemented using the TIM0 and TIM1 peripheral //! MS and Second counter implemented using the TIM0 and TIM1 peripheral
#![no_main] #![no_main]
#![no_std] #![no_std]
// Import panic provider.
use panic_probe as _;
// Import logger.
use defmt_rtt as _;
use core::sync::atomic::{AtomicU32, Ordering}; use core::cell::Cell;
use cortex_m::asm; use cortex_m::asm;
use cortex_m_rt::entry; use cortex_m_rt::entry;
use critical_section::Mutex;
use panic_rtt_target as _;
use rtt_target::{rprintln, rtt_init_print};
use simple_examples::peb1; use simple_examples::peb1;
use va416xx_hal::{ use va416xx_hal::{
clock::ClockConfigurator,
irq_router::enable_and_init_irq_router, irq_router::enable_and_init_irq_router,
pac::{self, interrupt}, pac::{self, interrupt},
prelude::*, prelude::*,
timer::CountdownTimer, timer::{default_ms_irq_handler, set_up_ms_tick, CountdownTimer, MS_COUNTER},
}; };
#[allow(dead_code)] #[allow(dead_code)]
@ -24,34 +22,34 @@ 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 dp = pac::Peripherals::take().unwrap();
let mut last_ms = 0; let mut last_ms = 0;
defmt::println!("-- Vorago system ticks using timers --"); rprintln!("-- Vorago system ticks using timers --");
// Use the external clock connected to XTAL_N. // Use the external clock connected to XTAL_N.
let clocks = ClockConfigurator::new(dp.clkgen) let clocks = dp
.clkgen
.constrain()
.xtal_n_clk_with_src_freq(peb1::EXTCLK_FREQ) .xtal_n_clk_with_src_freq(peb1::EXTCLK_FREQ)
.freeze() .freeze(&mut dp.sysconfig)
.unwrap(); .unwrap();
enable_and_init_irq_router(); enable_and_init_irq_router(&mut dp.sysconfig, &dp.irq_router);
let mut ms_timer = CountdownTimer::new(dp.tim0, &clocks); let _ = set_up_ms_tick(&mut dp.sysconfig, dp.tim0, &clocks);
ms_timer.enable_interrupt(true); let mut second_timer = CountdownTimer::new(&mut dp.sysconfig, dp.tim1, &clocks);
ms_timer.start(1.Hz()); second_timer.listen();
let mut second_timer = CountdownTimer::new(dp.tim1, &clocks);
second_timer.enable_interrupt(true);
second_timer.start(1.Hz()); second_timer.start(1.Hz());
loop { loop {
let current_ms = MS_COUNTER.load(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(Ordering::Relaxed); let second = critical_section::with(|cs| SEC_COUNTER.borrow(cs).get());
defmt::info!("Second counter: {}", second); rprintln!("Second counter: {}", second);
} }
asm::delay(1000); asm::delay(1000);
} }
@ -60,11 +58,15 @@ fn main() -> ! {
#[interrupt] #[interrupt]
#[allow(non_snake_case)] #[allow(non_snake_case)]
fn TIM0() { fn TIM0() {
MS_COUNTER.fetch_add(1, Ordering::Relaxed); default_ms_irq_handler()
} }
#[interrupt] #[interrupt]
#[allow(non_snake_case)] #[allow(non_snake_case)]
fn TIM1() { fn TIM1() {
SEC_COUNTER.fetch_add(1, Ordering::Relaxed); critical_section::with(|cs| {
let mut sec = SEC_COUNTER.borrow(cs).get();
sec += 1;
SEC_COUNTER.borrow(cs).set(sec);
});
} }

View File

@ -2,53 +2,55 @@
// echo mode // echo mode
#![no_main] #![no_main]
#![no_std] #![no_std]
// Import panic provider.
use panic_probe as _;
// Import logger.
use defmt_rtt as _;
use cortex_m_rt::entry; use cortex_m_rt::entry;
use embedded_hal_nb::serial::Read; use embedded_hal_nb::serial::Read;
use embedded_io::Write; use embedded_io::Write;
use panic_rtt_target as _;
use rtt_target::{rprintln, rtt_init_print};
use simple_examples::peb1; use simple_examples::peb1;
use va416xx_hal::clock::ClockConfigurator; use va416xx_hal::clock::ClkgenExt;
use va416xx_hal::pins::PinsG;
use va416xx_hal::time::Hertz; use va416xx_hal::time::Hertz;
use va416xx_hal::{pac, uart}; use va416xx_hal::{gpio::PinsG, pac, uart};
#[entry] #[entry]
fn main() -> ! { fn main() -> ! {
defmt::println!("-- VA416xx UART example application--"); rtt_init_print!();
rprintln!("-- VA416xx UART example application--");
let dp = pac::Peripherals::take().unwrap(); let mut dp = pac::Peripherals::take().unwrap();
// Use the external clock connected to XTAL_N. // Use the external clock connected to XTAL_N.
let clocks = ClockConfigurator::new(dp.clkgen) let clocks = dp
.clkgen
.constrain()
.xtal_n_clk_with_src_freq(peb1::EXTCLK_FREQ) .xtal_n_clk_with_src_freq(peb1::EXTCLK_FREQ)
.freeze() .freeze(&mut dp.sysconfig)
.unwrap(); .unwrap();
let gpiog = PinsG::new(dp.portg); let gpiob = PinsG::new(&mut dp.sysconfig, dp.portg);
let tx = gpiob.pg0.into_funsel_1();
let rx = gpiob.pg1.into_funsel_1();
let uart0 = uart::Uart::new( let uart0 = uart::Uart::new(
&mut dp.sysconfig,
dp.uart0, dp.uart0,
gpiog.pg0, (tx, rx),
gpiog.pg1, Hertz::from_raw(115200),
&clocks, &clocks,
Hertz::from_raw(115200).into(), );
)
.unwrap();
let (mut tx, mut rx) = uart0.split(); let (mut tx, mut rx) = uart0.split();
writeln!(tx, "Hello World\n\r").unwrap(); writeln!(tx, "Hello World\n\r").unwrap();
loop { loop {
// Echo what is received on the serial link. // Echo what is received on the serial link.
match nb::block!(rx.read()) { match nb::block!(rx.read()) {
Ok(recvd) => { Ok(recvd) => {
// Infallible operation. if let Err(e) = embedded_hal_nb::serial::Write::write(&mut tx, recvd) {
embedded_hal_nb::serial::Write::write(&mut tx, recvd).unwrap(); rprintln!("UART TX error: {:?}", e);
}
} }
Err(e) => { Err(e) => {
defmt::info!("UART RX error {:?}", e); rprintln!("UART RX error {:?}", e);
} }
} }
} }

View File

@ -1,20 +1,19 @@
// Code to test the watchdog timer. // Code to test the watchdog timer.
#![no_main] #![no_main]
#![no_std] #![no_std]
// Import panic provider.
use panic_probe as _;
// Import logger.
use defmt_rtt as _;
use va416xx_hal::clock::ClockConfigurator;
use core::sync::atomic::{AtomicU32, Ordering}; use core::cell::Cell;
use cortex_m_rt::entry; use cortex_m_rt::entry;
use critical_section::Mutex;
use panic_rtt_target as _;
use rtt_target::{rprintln, rtt_init_print};
use simple_examples::peb1; use simple_examples::peb1;
use va416xx_hal::irq_router::enable_and_init_irq_router; use va416xx_hal::irq_router::enable_and_init_irq_router;
use va416xx_hal::pac::{self, interrupt}; use va416xx_hal::pac::{self, interrupt};
use va416xx_hal::prelude::*;
use va416xx_hal::wdt::Wdt; use va416xx_hal::wdt::Wdt;
static WDT_INTRPT_COUNT: AtomicU32 = AtomicU32::new(0); static WDT_INTRPT_COUNT: Mutex<Cell<u32>> = Mutex::new(Cell::new(0));
#[derive(Debug, PartialEq, Eq)] #[derive(Debug, PartialEq, Eq)]
#[allow(dead_code)] #[allow(dead_code)]
@ -30,39 +29,36 @@ const WDT_ROLLOVER_MS: u32 = 100;
#[entry] #[entry]
fn main() -> ! { fn main() -> ! {
defmt::println!("-- VA416xx WDT example application--"); rtt_init_print!();
rprintln!("-- VA416xx WDT example application--");
let cp = cortex_m::Peripherals::take().unwrap(); let cp = cortex_m::Peripherals::take().unwrap();
let dp = pac::Peripherals::take().unwrap(); let mut dp = pac::Peripherals::take().unwrap();
// Use the external clock connected to XTAL_N. // Use the external clock connected to XTAL_N.
let clocks = ClockConfigurator::new(dp.clkgen) let clocks = dp
.clkgen
.constrain()
.xtal_n_clk_with_src_freq(peb1::EXTCLK_FREQ) .xtal_n_clk_with_src_freq(peb1::EXTCLK_FREQ)
.freeze() .freeze(&mut dp.sysconfig)
.unwrap(); .unwrap();
enable_and_init_irq_router(); enable_and_init_irq_router(&mut dp.sysconfig, &dp.irq_router);
let mut delay = cortex_m::delay::Delay::new(cp.SYST, clocks.apb0().raw()); let mut delay_sysclk = cortex_m::delay::Delay::new(cp.SYST, clocks.apb0().raw());
let mut last_interrupt_counter = 0; let mut last_interrupt_counter = 0;
let mut wdt_ctrl = Wdt::start(dp.watch_dog, &clocks, WDT_ROLLOVER_MS); let mut wdt_ctrl = Wdt::start(&mut dp.sysconfig, dp.watch_dog, &clocks, WDT_ROLLOVER_MS);
wdt_ctrl.enable_reset(); wdt_ctrl.enable_reset();
let log_divisor = 25;
let mut counter: u32 = 0;
loop { loop {
counter = counter.wrapping_add(1);
if counter % log_divisor == 0 {
defmt::info!("wdt example main loop alive");
}
if TEST_MODE != TestMode::AllowReset { if TEST_MODE != TestMode::AllowReset {
wdt_ctrl.feed(); wdt_ctrl.feed();
} }
let interrupt_counter = WDT_INTRPT_COUNT.load(Ordering::Relaxed); let interrupt_counter = critical_section::with(|cs| WDT_INTRPT_COUNT.borrow(cs).get());
if interrupt_counter > last_interrupt_counter { if interrupt_counter > last_interrupt_counter {
defmt::info!("interrupt counter has increased to {}", interrupt_counter); rprintln!("interrupt counter has increased to {}", interrupt_counter);
last_interrupt_counter = interrupt_counter; last_interrupt_counter = interrupt_counter;
} }
match TEST_MODE { match TEST_MODE {
TestMode::FedByMain => delay.delay_ms(WDT_ROLLOVER_MS / 5), TestMode::FedByMain => delay_sysclk.delay_ms(WDT_ROLLOVER_MS / 5),
TestMode::FedByIrq => delay.delay_ms(WDT_ROLLOVER_MS), TestMode::FedByIrq => delay_sysclk.delay_ms(WDT_ROLLOVER_MS),
_ => (), _ => (),
} }
} }
@ -71,7 +67,11 @@ fn main() -> ! {
#[interrupt] #[interrupt]
#[allow(non_snake_case)] #[allow(non_snake_case)]
fn WATCHDOG() { fn WATCHDOG() {
WDT_INTRPT_COUNT.fetch_add(1, Ordering::Relaxed); critical_section::with(|cs| {
WDT_INTRPT_COUNT
.borrow(cs)
.set(WDT_INTRPT_COUNT.borrow(cs).get() + 1);
});
let wdt = unsafe { pac::WatchDog::steal() }; let wdt = unsafe { pac::WatchDog::steal() };
// Clear interrupt. // Clear interrupt.
if TEST_MODE != TestMode::AllowReset { if TEST_MODE != TestMode::AllowReset {

View File

@ -3,7 +3,7 @@
#![no_std] #![no_std]
use cortex_m_rt::entry; use cortex_m_rt::entry;
use panic_halt as _; use panic_rtt_target as _;
#[entry] #[entry]
fn main() -> ! { fn main() -> ! {

View File

@ -5,18 +5,24 @@ edition = "2021"
[dependencies] [dependencies]
cortex-m = "0.7" cortex-m = "0.7"
cortex-m-rt = "0.7"
embedded-hal = "1"
embedded-hal-nb = "1"
embedded-io = "0.6" embedded-io = "0.6"
defmt-rtt = "0.4" panic-rtt-target = { version = "0.2" }
defmt = "1" rtt-target = { version = "0.6" }
panic-probe = { version = "1", features = ["defmt"] } rtt-log = "0.5"
log = "0.4"
crc = "3"
rtic-sync = "1"
static_cell = "2" static_cell = "2"
satrs = { version = "0.3.0-alpha.0", default-features = false } satrs = { version = "0.3.0-alpha.0", default-features = false }
ringbuf = { version = "0.4", default-features = false } ringbuf = { version = "0.4", default-features = false }
once_cell = { version = "1", default-features = false, features = ["critical-section"] } once_cell = { version = "1", default-features = false, features = ["critical-section"] }
spacepackets = { version = "0.13", default-features = false, features = ["defmt"] } spacepackets = { version = "0.13", default-features = false }
cobs = { version = "0.3", default-features = false } cobs = { version = "0.3", default-features = false }
va416xx-hal = { version = "0.5", features = ["va41630", "defmt"], path = "../va416xx-hal" } va416xx-hal = { version = "0.4", features = ["va41630"] }
rtic = { version = "2", features = ["thumbv7-backend"] } rtic = { version = "2", features = ["thumbv7-backend"] }
rtic-monotonics = { version = "2", features = ["cortex-m-systick"] } rtic-monotonics = { version = "2", features = ["cortex-m-systick"] }

View File

@ -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 com_interface import ComInterface 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.serial_base import SerialCfg from tmtccmd.com.serial_base import SerialCfg
from com_interface.serial_cobs import SerialCobsComIF from tmtccmd.com.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
@ -57,7 +58,6 @@ PING_PAYLOAD_SIZE = 0
class ActionId(enum.IntEnum): class ActionId(enum.IntEnum):
CORRUPT_APP_A = 128 CORRUPT_APP_A = 128
CORRUPT_APP_B = 129 CORRUPT_APP_B = 129
SET_BOOT_SLOT = 130
_LOGGER = logging.getLogger(__name__) _LOGGER = logging.getLogger(__name__)
@ -78,29 +78,11 @@ class Target(enum.Enum):
APP_B = 2 APP_B = 2
class AppSel(enum.IntEnum):
APP_A = 0
APP_B = 1
class ImageLoader: class ImageLoader:
def __init__(self, com_if: ComInterface, verificator: PusVerificator) -> None: def __init__(self, com_if: ComInterface, verificator: PusVerificator) -> None:
self.com_if = com_if self.com_if = com_if
self.verificator = verificator self.verificator = verificator
def handle_boot_sel_cmd(self, target: AppSel):
_LOGGER.info("Sending ping command")
action_tc = PusTc(
apid=0x00,
service=PusService.S8_FUNC_CMD,
subservice=ActionId.SET_BOOT_SLOT,
seq_count=SEQ_PROVIDER.get_and_increment(),
app_data=bytes([target]),
)
self.verificator.add_tc(action_tc)
self.com_if.send(bytes(action_tc.pack()))
self.await_for_command_completion("boot image selection command")
def handle_ping_cmd(self): def handle_ping_cmd(self):
_LOGGER.info("Sending ping command") _LOGGER.info("Sending ping command")
ping_tc = PusTc( ping_tc = PusTc(
@ -112,9 +94,19 @@ 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_completion("ping command")
data_available = self.com_if.data_available(0.4)
if not data_available:
_LOGGER.warning("no ping reply received")
for reply in self.com_if.receive():
result = self.verificator.add_tm(
Service1Tm.from_tm(PusTm.unpack(reply, 0), UnpackParams(0))
)
if result is not None and result.completed:
_LOGGER.info("received ping completion reply")
def handle_corruption_cmd(self, target: Target): def handle_corruption_cmd(self, target: Target):
if target == Target.BOOTLOADER: if target == Target.BOOTLOADER:
_LOGGER.error("can not corrupt bootloader") _LOGGER.error("can not corrupt bootloader")
if target == Target.APP_A: if target == Target.APP_A:
@ -134,25 +126,6 @@ class ImageLoader:
), ),
) )
def await_for_command_completion(self, context: str):
done = False
now = time.time()
while time.time() - now < 2.0:
if self.com_if.data_available() == 0:
time.sleep(0.2)
continue
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(f"received {context} reply")
done = True
if done:
break
if not done:
_LOGGER.warning(f"no {context} reply received")
def handle_flash_cmd(self, target: Target, file_path: Path) -> int: def handle_flash_cmd(self, target: Target, file_path: Path) -> int:
loadable_segments = [] loadable_segments = []
_LOGGER.info("Parsing ELF file for loadable sections") _LOGGER.info("Parsing ELF file for loadable sections")
@ -296,12 +269,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)

View File

@ -1,5 +1,5 @@
spacepackets == 0.28 spacepackets == 0.24
com-interface == 0.1.0 tmtccmd == 8.0.2
toml == 0.10 toml == 0.10
pyelftools == 0.31 pyelftools == 0.31
crcmod == 1.7 crcmod == 1.7

9
flashloader/src/lib.rs Normal file
View File

@ -0,0 +1,9 @@
#![no_std]
#[cfg(test)]
mod tests {
#[test]
fn simple() {
assert_eq!(1 + 1, 2);
}
}

View File

@ -19,6 +19,7 @@
#![no_std] #![no_std]
use once_cell::sync::OnceCell; use once_cell::sync::OnceCell;
use panic_rtt_target as _;
use va416xx_hal::{clock::Clocks, edac, pac, time::Hertz, wdt::Wdt}; use va416xx_hal::{clock::Clocks, edac, pac, time::Hertz, wdt::Wdt};
const EXTCLK_FREQ: u32 = 40_000_000; const EXTCLK_FREQ: u32 = 40_000_000;
@ -32,7 +33,6 @@ const MAX_TM_FRAME_SIZE: usize = cobs::max_encoding_length(MAX_TM_SIZE);
const UART_BAUDRATE: u32 = 115200; const UART_BAUDRATE: u32 = 115200;
const BOOT_NVM_MEMORY_ID: u8 = 1; const BOOT_NVM_MEMORY_ID: u8 = 1;
const RX_DEBUGGING: bool = false; const RX_DEBUGGING: bool = false;
const TX_DEBUGGING: bool = false;
pub enum ActionId { pub enum ActionId {
CorruptImageA = 128, CorruptImageA = 128,
@ -95,25 +95,23 @@ mod app {
use super::*; use super::*;
use cortex_m::asm; use cortex_m::asm;
use embedded_io::Write; use embedded_io::Write;
// Import panic provider. use panic_rtt_target as _;
use panic_probe as _;
// Import logger.
use defmt_rtt 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::VerificationReportCreator; use satrs::pus::verification::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 va416xx_hal::clock::ClockConfigurator;
use va416xx_hal::irq_router::enable_and_init_irq_router; use va416xx_hal::irq_router::enable_and_init_irq_router;
use va416xx_hal::uart::IrqContextTimeoutOrMaxSize; use va416xx_hal::uart::IrqContextTimeoutOrMaxSize;
use va416xx_hal::{ use va416xx_hal::{
clock::ClkgenExt,
edac, edac,
gpio::PinsG,
nvm::Nvm, nvm::Nvm,
pac, pac,
pins::PinsG,
uart::{self, Uart}, uart::{self, Uart},
}; };
@ -129,8 +127,8 @@ mod app {
#[local] #[local]
struct Local { struct Local {
uart_rx: uart::RxWithInterrupt, uart_rx: uart::RxWithInterrupt<pac::Uart0>,
uart_tx: uart::Tx, uart_tx: uart::Tx<pac::Uart0>,
rx_context: IrqContextTimeoutOrMaxSize, rx_context: IrqContextTimeoutOrMaxSize,
rom_spi: Option<pac::Spi3>, rom_spi: Option<pac::Spi3>,
// We handle all TM in one task. // We handle all TM in one task.
@ -152,27 +150,33 @@ mod app {
#[init] #[init]
fn init(mut cx: init::Context) -> (Shared, Local) { fn init(mut cx: init::Context) -> (Shared, Local) {
defmt::println!("-- Vorago flashloader --"); //rtt_init_default!();
rtt_log::init();
rprintln!("-- Vorago flashloader --");
// Initialize the systick interrupt & obtain the token to prove that we did // Initialize the systick interrupt & obtain the token to prove that we did
// Use the external clock connected to XTAL_N. // Use the external clock connected to XTAL_N.
let clocks = ClockConfigurator::new(cx.device.clkgen) let clocks = cx
.device
.clkgen
.constrain()
.xtal_n_clk_with_src_freq(Hertz::from_raw(EXTCLK_FREQ)) .xtal_n_clk_with_src_freq(Hertz::from_raw(EXTCLK_FREQ))
.freeze() .freeze(&mut cx.device.sysconfig)
.unwrap(); .unwrap();
enable_and_init_irq_router(); enable_and_init_irq_router(&mut cx.device.sysconfig, &cx.device.irq_router);
setup_edac(&mut cx.device.sysconfig); setup_edac(&mut cx.device.sysconfig);
let gpiog = PinsG::new(cx.device.portg); let gpiog = PinsG::new(&mut cx.device.sysconfig, cx.device.portg);
let tx = gpiog.pg0.into_funsel_1();
let rx = gpiog.pg1.into_funsel_1();
let uart0 = Uart::new( let uart0 = Uart::new(
&mut cx.device.sysconfig,
cx.device.uart0, cx.device.uart0,
gpiog.pg0, (tx, rx),
gpiog.pg1, Hertz::from_raw(UART_BAUDRATE),
&clocks, &clocks,
Hertz::from_raw(UART_BAUDRATE).into(), );
)
.unwrap();
let (tx, rx) = uart0.split(); let (tx, rx) = uart0.split();
let verif_reporter = VerificationReportCreator::new(0).unwrap(); let verif_reporter = VerificationReportCreator::new(0).unwrap();
@ -252,12 +256,12 @@ mod app {
match cx match cx
.local .local
.uart_rx .uart_rx
.on_interrupt_max_size_or_timeout_based(cx.local.rx_context, cx.local.rx_buf) .irq_handler_max_size_or_timeout_based(cx.local.rx_context, cx.local.rx_buf)
{ {
Ok(result) => { Ok(result) => {
if RX_DEBUGGING { if RX_DEBUGGING {
defmt::info!("RX Info: {:?}", cx.local.rx_context); log::debug!("RX Info: {:?}", cx.local.rx_context);
defmt::info!("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.
@ -268,7 +272,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();
if cx.local.tc_prod.sizes_prod.vacant_len() >= 1 if cx.local.tc_prod.sizes_prod.vacant_len() >= 1
@ -281,13 +285,11 @@ mod app {
.buf_prod .buf_prod
.push_slice(&cx.local.rx_buf[1..1 + decoded_size]); .push_slice(&cx.local.rx_buf[1..1 + decoded_size]);
} else { } else {
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.
@ -297,11 +299,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);
} }
} }
} }
@ -328,7 +330,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);
assert_eq!( assert_eq!(
cx.local cx.local
.tc_cons .tc_cons
@ -344,7 +346,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!("PUS TC error: {}", pus_tc.unwrap_err());
return; return;
} }
let (pus_tc, _) = pus_tc.unwrap(); let (pus_tc, _) = pus_tc.unwrap();
@ -375,7 +377,9 @@ mod app {
let mut corrupt_image = |base_addr: u32| { let mut corrupt_image = |base_addr: u32| {
// Safety: We only use this for NVM handling and we only do NVM // Safety: We only use this for NVM handling and we only do NVM
// handling here. // handling here.
let mut sys_cfg = unsafe { pac::Sysconfig::steal() };
let nvm = Nvm::new( let nvm = Nvm::new(
&mut sys_cfg,
cx.local.rom_spi.take().unwrap(), cx.local.rom_spi.take().unwrap(),
CLOCKS.get().as_ref().unwrap(), CLOCKS.get().as_ref().unwrap(),
); );
@ -383,7 +387,7 @@ mod app {
nvm.read_data(base_addr + 32, &mut buf); nvm.read_data(base_addr + 32, &mut buf);
buf[0] += 1; buf[0] += 1;
nvm.write_data(base_addr + 32, &buf); nvm.write_data(base_addr + 32, &buf);
*cx.local.rom_spi = Some(nvm.release()); *cx.local.rom_spi = Some(nvm.release(&mut sys_cfg));
let tm = cx let tm = cx
.local .local
.verif_reporter .verif_reporter
@ -392,16 +396,16 @@ 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.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
@ -426,21 +430,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
); );
@ -448,22 +454,31 @@ 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
);
// Safety: We only use this for NVM handling and we only do NVM // Safety: We only use this for NVM handling and we only do NVM
// handling here. // handling here.
let mut sys_cfg = unsafe { pac::Sysconfig::steal() };
let nvm = Nvm::new( let nvm = Nvm::new(
&mut sys_cfg,
cx.local.rom_spi.take().unwrap(), cx.local.rom_spi.take().unwrap(),
CLOCKS.get().as_ref().unwrap(), CLOCKS.get().as_ref().unwrap(),
); );
nvm.write_data(offset, data); nvm.write_data(offset, data);
*cx.local.rom_spi = Some(nvm.release()); *cx.local.rom_spi = Some(nvm.release(&mut sys_cfg));
let tm = cx let tm = cx
.local .local
.verif_reporter .verif_reporter
.completion_success(cx.local.src_data_buf, started_token, 0, 0, &[]) .completion_success(cx.local.src_data_buf, started_token, 0, 0, &[])
.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");
} }
} }
} }
@ -492,12 +507,9 @@ mod app {
&mut cx.local.encoded_buf[1..], &mut cx.local.encoded_buf[1..],
); );
cx.local.encoded_buf[send_size + 1] = 0; cx.local.encoded_buf[send_size + 1] = 0;
if TX_DEBUGGING {
defmt::debug!("UART TX: Sending data with size {}", send_size + 2);
}
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();
Mono::delay(2.millis()).await; Mono::delay(2.millis()).await;
} }

View File

@ -1,5 +0,0 @@
#!/bin/bash
# Start the JLinkGDBServer while also specifying the JLinkScript file. The JLinkScript is necessary
# to disable ROM protection to allow flashing
JLinkGDBServer -select USB -device Cortex-M4 -endian little -if SWD -speed 2000 \
-LocalhostOnly -vd -jlinkscriptfile ./jlink/JLinkSettings.JLinkScript

View File

@ -1,5 +1,5 @@
#!/bin/bash #!/bin/bash
# Start the JLinkGDBServer while also specifying the JLinkScript file. The JLinkScript is necessary # Start the JLinkGDBServer while also specifying the JLinkScript file. The JLinkScript is necessary
# to disable ROM protection to allow flashing # to disable ROM protection to allow flashing
JLinkGDBServer -select USB -device VA416xx -endian little -if SWD -speed 2000 \ JLinkGDBServer -select USB -device Cortex-M4 -endian little -if SWD -speed 2000 \
-LocalhostOnly -vd -LocalhostOnly -vd -jlinkscriptfile ./jlink/JLinkSettings.JLinkScript

View File

@ -18,5 +18,6 @@ _stack_start = ORIGIN(RAM) + LENGTH(RAM);
SECTIONS { SECTIONS {
.sram1 (NOLOAD) : ALIGN(8) { .sram1 (NOLOAD) : ALIGN(8) {
*(.sram1 .sram1.*); *(.sram1 .sram1.*);
. = ALIGN(4);
} > SRAM_1 } > SRAM_1
}; };

View File

@ -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
defmt-print -e "$BINARY" tcp

View File

@ -11,8 +11,17 @@ keywords = ["no-std", "hal", "cortex-m", "vorago", "va416xx"]
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 = ["vor4x"] } critical-section = "1"
va416xx-hal = { path = "../va416xx-hal" }
embassy-sync = "0.6"
embassy-executor = "0.7"
embassy-time-driver = "0.2"
embassy-time-queue-utils = "0.1"
portable-atomic = "1"
once_cell = { version = "1", default-features = false, features = ["critical-section"] }
va416xx-hal = { version = "0.5", path = "../va416xx-hal" }
[features] [features]
default = ["irq-tim14-tim15"] default = ["irq-tim14-tim15"]

View File

@ -38,13 +38,34 @@
//! [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},
sync::atomic::{AtomicU32, Ordering},
};
use critical_section::{CriticalSection, Mutex};
use embassy_time_driver::{time_driver_impl, Driver, TICK_HZ};
use embassy_time_queue_utils::Queue;
use once_cell::sync::OnceCell;
use va416xx_hal::{ use va416xx_hal::{
clock::Clocks, clock::Clocks,
enable_nvic_interrupt,
irq_router::enable_and_init_irq_router, irq_router::enable_and_init_irq_router,
pac::{self, interrupt}, pac::{self, interrupt},
timer::{TimMarker, TIM_IRQ_OFFSET}, pwm::ValidTim,
timer::{
assert_tim_reset_for_two_cycles, enable_tim_clk, get_tim_raw, TimRegInterface,
TIM_IRQ_OFFSET,
},
}; };
use vorago_shared_periphs::embassy::time_driver;
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.
/// ///
@ -89,29 +110,289 @@ embassy_time_driver_irqs!(timekeeper_irq = TIM14, alarm_irq = TIM13);
#[cfg(feature = "irq-tim22-tim23")] #[cfg(feature = "irq-tim22-tim23")]
embassy_time_driver_irqs!(timekeeper_irq = TIM23, alarm_irq = TIM22); embassy_time_driver_irqs!(timekeeper_irq = TIM23, alarm_irq = TIM22);
/// 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
/// ///
/// If the interrupt handlers are provided by the library, the ID of the /// If the interrupt handlers are provided by the library, the ID of the
/// used TIM peripherals has to match the ID of the passed timer peripherals. Currently, this /// used TIM peripherals has to match the ID of the passed timer peripherals. Currently, this
/// can only be checked at run-time, and a run-time assertion will panic on the embassy /// can only be checked at run-time, and a run-time assertion will panic on the embassy
/// initialization in case of a missmatch. /// initialization in case of a missmatch.
pub fn init<TimekeeperTim: TimMarker, AlarmTim: TimMarker>( ///
/// # Safety
///
/// This has to be called once at initialization time to initiate the time driver for
/// embassy.
pub unsafe fn init<
TimekeeperTim: TimRegInterface + ValidTim,
AlarmTim: TimRegInterface + ValidTim,
>(
syscfg: &mut pac::Sysconfig,
irq_router: &pac::IrqRouter,
timekeeper: TimekeeperTim, timekeeper: TimekeeperTim,
alarm: AlarmTim, alarm: AlarmTim,
clocks: &Clocks, clocks: &Clocks,
) { ) {
#[cfg(feature = "_irqs-in-lib")] #[cfg(feature = "_irqs-in-lib")]
assert_eq!( assert_eq!(
TimekeeperTim::ID.value(), TimekeeperTim::ID,
TIMEKEEPER_IRQ as u8 - TIM_IRQ_OFFSET as u8, TIMEKEEPER_IRQ as u8 - TIM_IRQ_OFFSET as u8,
"Timekeeper TIM and IRQ missmatch" "Timekeeper TIM and IRQ missmatch"
); );
#[cfg(feature = "_irqs-in-lib")] #[cfg(feature = "_irqs-in-lib")]
assert_eq!( assert_eq!(
AlarmTim::ID.value(), AlarmTim::ID,
ALARM_IRQ as u8 - TIM_IRQ_OFFSET as u8, ALARM_IRQ as u8 - TIM_IRQ_OFFSET as u8,
"Alarm TIM and IRQ missmatch" "Alarm TIM and IRQ missmatch"
); );
enable_and_init_irq_router(); enable_and_init_irq_router(syscfg, irq_router);
time_driver().__init(timekeeper, alarm, clocks) TIME_DRIVER.init(syscfg, timekeeper, alarm, clocks)
}
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 {
fn init<TimekeeperTim: TimRegInterface + ValidTim, AlarmTim: TimRegInterface + ValidTim>(
&self,
syscfg: &mut pac::Sysconfig,
timekeeper_tim: TimekeeperTim,
alarm_tim: AlarmTim,
clocks: &Clocks,
) {
if ALARM_TIM.get().is_some() || TIMEKEEPER_TIM.get().is_some() {
return;
}
ALARM_TIM.set(alarm_tim.tim_id()).ok();
TIMEKEEPER_TIM.set(timekeeper_tim.tim_id()).ok();
enable_tim_clk(syscfg, timekeeper_tim.tim_id());
assert_tim_reset_for_two_cycles(syscfg, alarm_tim.tim_id());
// Initiate scale value here. This is required to convert timer ticks back to a timestamp.
SCALE
.set((TimekeeperTim::clock(clocks).raw() / TICK_HZ as u32) as u64)
.unwrap();
let timekeeper_tim_regs = timekeeper_tim.reg_block();
timekeeper_tim_regs
.rst_value()
.write(|w| unsafe { w.bits(u32::MAX) });
// Decrementing counter.
timekeeper_tim_regs
.cnt_value()
.write(|w| unsafe { w.bits(u32::MAX) });
// Switch on. Timekeeping should always be done.
unsafe {
enable_nvic_interrupt(TimekeeperTim::IRQ);
}
timekeeper_tim_regs
.ctrl()
.modify(|_, w| w.irq_enb().set_bit());
timekeeper_tim_regs.enable().write(|w| unsafe { w.bits(1) });
enable_tim_clk(syscfg, AlarmTim::ID);
assert_tim_reset_for_two_cycles(syscfg, AlarmTim::ID);
let alarm_tim_regs = alarm_tim.reg_block();
// Explicitely disable alarm timer until needed.
alarm_tim_regs.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(AlarmTim::IRQ);
}
}
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()
}
/// 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 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) * *SCALE.get().unwrap();
if remaining_ticks <= u32::MAX as u64 {
alarm_tim.enable().write(|w| unsafe { w.bits(0) });
alarm_tim
.cnt_value()
.write(|w| unsafe { w.bits(remaining_ticks as u32) });
alarm_tim.ctrl().modify(|_, w| w.irq_enb().set_bit());
alarm_tim.enable().write(|w| unsafe { w.bits(1) });
}
}
})
}
fn 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());
}
}
})
}
} }

View File

@ -8,14 +8,6 @@ and this project adheres to [Semantic Versioning](http://semver.org/).
# [unreleased] # [unreleased]
# [v0.5.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.5.0] 2025-03-07 # [v0.5.0] 2025-03-07
- Bugfix for I2C `TimingCfg::reg` - Bugfix for I2C `TimingCfg::reg`
@ -119,7 +111,6 @@ and this project adheres to [Semantic Versioning](http://semver.org/).
- Initial release with basic HAL drivers - Initial release with basic HAL drivers
[unreleased]: https://egit.irs.uni-stuttgart.de/rust/va416xx-rs/compare/va416xx-hal-v0.5.0...HEAD [unreleased]: https://egit.irs.uni-stuttgart.de/rust/va416xx-rs/compare/va416xx-hal-v0.5.0...HEAD
[v0.5.1]: https://egit.irs.uni-stuttgart.de/rust/va416xx-rs/compare/va416xx-hal-v0.5.0...va416xx-hal-v0.5.1
[v0.5.0]: https://egit.irs.uni-stuttgart.de/rust/va416xx-rs/compare/va416xx-hal-v0.4.1...va416xx-hal-v0.5.0 [v0.5.0]: https://egit.irs.uni-stuttgart.de/rust/va416xx-rs/compare/va416xx-hal-v0.4.1...va416xx-hal-v0.5.0
[v0.4.1]: https://egit.irs.uni-stuttgart.de/rust/va416xx-rs/compare/va416xx-hal-v0.4.0...va416xx-hal-v0.4.1 [v0.4.1]: https://egit.irs.uni-stuttgart.de/rust/va416xx-rs/compare/va416xx-hal-v0.4.0...va416xx-hal-v0.4.1
[v0.4.0]: https://egit.irs.uni-stuttgart.de/rust/va416xx-rs/compare/va416xx-hal-v0.3.0...va416xx-hal-v0.4.0 [v0.4.0]: https://egit.irs.uni-stuttgart.de/rust/va416xx-rs/compare/va416xx-hal-v0.3.0...va416xx-hal-v0.4.0

View File

@ -1,6 +1,6 @@
[package] [package]
name = "va416xx-hal" name = "va416xx-hal"
version = "0.5.1" version = "0.5.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 VA416xx family of MCUs" description = "HAL for the Vorago VA416xx family of MCUs"
@ -12,30 +12,39 @@ categories = ["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"] }
va416xx = { version = "0.4", features = ["critical-section"], default-features = false } critical-section = "1"
vorago-shared-periphs = { git = "https://egit.irs.uni-stuttgart.de/rust/vorago-shared-periphs.git", features = ["vor4x"] }
nb = "1" nb = "1"
paste = "1"
embedded-hal-nb = "1"
embedded-hal-async = "1"
embedded-hal = "1" embedded-hal = "1"
embedded-io = "0.6"
embedded-io-async = "0.6"
num_enum = { version = "0.7", default-features = false } num_enum = { version = "0.7", default-features = false }
typenum = "1"
bitflags = "2" bitflags = "2"
bitbybit = "1.3" bitfield = { version = ">=0.17, <=0.18"}
arbitrary-int = "1.3"
fugit = "0.3" fugit = "0.3"
delegate = ">=0.12, <=0.13"
heapless = "0.8"
void = { version = "1", default-features = false }
thiserror = { version = "2", default-features = false } thiserror = { version = "2", default-features = false }
portable-atomic = "1"
embassy-sync = "0.6"
va416xx = { version = "0.4", features = ["critical-section"], default-features = false }
defmt = { version = "0.3", optional = true } defmt = { version = "0.3", optional = true }
[features] [features]
default = ["rt", "revb"] default = ["rt", "revb"]
rt = ["va416xx/rt"] rt = ["va416xx/rt"]
defmt = ["dep:defmt", "fugit/defmt", "vorago-shared-periphs/defmt"] defmt = ["dep:defmt", "fugit/defmt", "embedded-hal/defmt-03"]
va41630 = ["device-selected"] va41630 = ["device-selected"]
va41620 = ["device-selected"] va41620 = ["device-selected"]
va41629 = ["device-selected"] va41629 = ["device-selected"]
va41628 = ["device-selected", "vorago-shared-periphs/va41628"] va41628 = ["device-selected"]
device-selected = [] device-selected = []
revb = [] revb = []

View File

@ -8,9 +8,9 @@ use core::marker::PhantomData;
use crate::clock::Clocks; use crate::clock::Clocks;
use crate::pac; use crate::pac;
use crate::prelude::*;
use crate::time::Hertz; use crate::time::Hertz;
use num_enum::{IntoPrimitive, TryFromPrimitive}; use num_enum::{IntoPrimitive, TryFromPrimitive};
use vorago_shared_periphs::{enable_peripheral_clock, PeripheralSelect};
pub const ADC_MIN_CLK: Hertz = Hertz::from_raw(2_000_000); pub const ADC_MIN_CLK: Hertz = Hertz::from_raw(2_000_000);
pub const ADC_MAX_CLK: Hertz = Hertz::from_raw(12_500_000); pub const ADC_MAX_CLK: Hertz = Hertz::from_raw(12_500_000);
@ -151,8 +151,8 @@ pub struct Adc<TagEnabled = ChannelTagDisabled> {
impl Adc<ChannelTagEnabled> {} impl Adc<ChannelTagEnabled> {}
impl Adc<ChannelTagDisabled> { impl Adc<ChannelTagDisabled> {
pub fn new(adc: pac::Adc, clocks: &Clocks) -> Self { pub fn new(syscfg: &mut pac::Sysconfig, adc: pac::Adc, clocks: &Clocks) -> Self {
Self::generic_new(adc, clocks) Self::generic_new(syscfg, adc, clocks)
} }
pub fn trigger_and_read_single_channel(&self, ch: ChannelSelect) -> Result<u16, AdcEmptyError> { pub fn trigger_and_read_single_channel(&self, ch: ChannelSelect) -> Result<u16, AdcEmptyError> {
@ -209,8 +209,12 @@ impl Adc<ChannelTagDisabled> {
} }
impl Adc<ChannelTagEnabled> { impl Adc<ChannelTagEnabled> {
pub fn new_with_channel_tag(adc: pac::Adc, clocks: &Clocks) -> Self { pub fn new_with_channel_tag(
let mut adc = Self::generic_new(adc, clocks); syscfg: &mut pac::Sysconfig,
adc: pac::Adc,
clocks: &Clocks,
) -> Self {
let mut adc = Self::generic_new(syscfg, adc, clocks);
adc.enable_channel_tag(); adc.enable_channel_tag();
adc adc
} }
@ -282,8 +286,8 @@ impl Adc<ChannelTagEnabled> {
} }
impl<TagEnabled> Adc<TagEnabled> { impl<TagEnabled> Adc<TagEnabled> {
fn generic_new(adc: pac::Adc, _clocks: &Clocks) -> Self { fn generic_new(syscfg: &mut pac::Sysconfig, adc: pac::Adc, _clocks: &Clocks) -> Self {
enable_peripheral_clock(PeripheralSelect::Adc); syscfg.enable_peripheral_clock(crate::clock::PeripheralSelect::Adc);
adc.ctrl().write(|w| unsafe { w.bits(0) }); adc.ctrl().write(|w| unsafe { w.bits(0) });
let adc = Self { let adc = Self {
adc, adc,

View File

@ -1,10 +1,10 @@
//! API for using the [crate::pac::Clkgen] peripheral. //! API for using the [crate::pac::Clkgen] peripheral.
//! //!
//! It also includes functionality to enable the peripheral clocks. //! It also includes functionality to enable the peripheral clocks.
//! Calling [ClockConfigurator::new] returns a builder structure which allows //! Calling [ClkgenExt::constrain] on the [crate::pac::Clkgen] peripheral generates the
//! setting up the clock. //! [ClkgenCfgr] structure which can be used to configure and set up the clock.
//! //!
//! Calling [ClockConfigurator::freeze] returns the frozen clock configuration inside the [Clocks] //! Calling [ClkgenCfgr::freeze] returns the frozen clock configuration inside the [Clocks]
//! structure. This structure can also be used to configure other structures provided by this HAL. //! structure. This structure can also be used to configure other structures provided by this HAL.
//! //!
//! # Examples //! # Examples
@ -15,11 +15,48 @@ use crate::adc::ADC_MAX_CLK;
use crate::pac; use crate::pac;
use crate::time::Hertz; use crate::time::Hertz;
pub use vorago_shared_periphs::clock::{Clocks, HBO_FREQ};
use vorago_shared_periphs::{enable_peripheral_clock, PeripheralSelect};
pub const HBO_FREQ: Hertz = Hertz::from_raw(20_000_000);
pub const XTAL_OSC_TSTART_MS: u32 = 15; pub const XTAL_OSC_TSTART_MS: u32 = 15;
#[derive(Debug, Copy, Clone, PartialEq)]
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
pub enum PeripheralSelect {
Spi0 = 0,
Spi1 = 1,
Spi2 = 2,
Spi3 = 3,
Uart0 = 4,
Uart1 = 5,
Uart2 = 6,
I2c0 = 7,
I2c1 = 8,
I2c2 = 9,
Can0 = 10,
Can1 = 11,
Rng = 12,
Adc = 13,
Dac = 14,
Dma = 15,
Ebi = 16,
Eth = 17,
Spw = 18,
Clkgen = 19,
IrqRouter = 20,
IoConfig = 21,
Utility = 22,
Watchdog = 23,
PortA = 24,
PortB = 25,
PortC = 26,
PortD = 27,
PortE = 28,
PortF = 29,
PortG = 30,
}
pub type PeripheralClock = PeripheralSelect;
#[derive(Debug, PartialEq, Eq)] #[derive(Debug, PartialEq, Eq)]
#[cfg_attr(feature = "defmt", derive(defmt::Format))] #[cfg_attr(feature = "defmt", derive(defmt::Format))]
pub enum FilterClkSel { pub enum FilterClkSel {
@ -33,6 +70,81 @@ pub enum FilterClkSel {
Clk7 = 7, Clk7 = 7,
} }
#[inline(always)]
pub fn enable_peripheral_clock(syscfg: &mut pac::Sysconfig, clock: PeripheralSelect) {
syscfg
.peripheral_clk_enable()
.modify(|r, w| unsafe { w.bits(r.bits() | (1 << clock as u8)) });
}
#[inline(always)]
pub fn disable_peripheral_clock(syscfg: &mut pac::Sysconfig, clock: PeripheralSelect) {
syscfg
.peripheral_clk_enable()
.modify(|r, w| unsafe { w.bits(r.bits() & !(1 << clock as u8)) });
}
#[inline(always)]
pub fn assert_periph_reset(syscfg: &mut pac::Sysconfig, periph: PeripheralSelect) {
syscfg
.peripheral_reset()
.modify(|r, w| unsafe { w.bits(r.bits() & !(1 << periph as u8)) });
}
#[inline(always)]
pub fn deassert_periph_reset(syscfg: &mut pac::Sysconfig, periph: PeripheralSelect) {
syscfg
.peripheral_reset()
.modify(|r, w| unsafe { w.bits(r.bits() | (1 << periph as u8)) });
}
#[inline(always)]
fn assert_periph_reset_for_two_cycles(syscfg: &mut pac::Sysconfig, periph: PeripheralSelect) {
assert_periph_reset(syscfg, periph);
cortex_m::asm::nop();
cortex_m::asm::nop();
deassert_periph_reset(syscfg, periph);
}
pub trait SyscfgExt {
fn enable_peripheral_clock(&mut self, clock: PeripheralClock);
fn disable_peripheral_clock(&mut self, clock: PeripheralClock);
fn assert_periph_reset(&mut self, periph: PeripheralSelect);
fn deassert_periph_reset(&mut self, periph: PeripheralSelect);
fn assert_periph_reset_for_two_cycles(&mut self, periph: PeripheralSelect);
}
impl SyscfgExt for pac::Sysconfig {
#[inline(always)]
fn enable_peripheral_clock(&mut self, clock: PeripheralClock) {
enable_peripheral_clock(self, clock)
}
#[inline(always)]
fn disable_peripheral_clock(&mut self, clock: PeripheralClock) {
disable_peripheral_clock(self, clock)
}
#[inline(always)]
fn assert_periph_reset(&mut self, clock: PeripheralSelect) {
assert_periph_reset(self, clock)
}
#[inline(always)]
fn deassert_periph_reset(&mut self, clock: PeripheralSelect) {
deassert_periph_reset(self, clock)
}
#[inline(always)]
fn assert_periph_reset_for_two_cycles(&mut self, periph: PeripheralSelect) {
assert_periph_reset_for_two_cycles(self, periph)
}
}
/// Refer to chapter 8 (p.57) of the programmers guide for detailed information. /// Refer to chapter 8 (p.57) of the programmers guide for detailed information.
#[derive(Debug, Copy, Clone, PartialEq, Eq)] #[derive(Debug, Copy, Clone, PartialEq, Eq)]
#[cfg_attr(feature = "defmt", derive(defmt::Format))] #[cfg_attr(feature = "defmt", derive(defmt::Format))]
@ -111,12 +223,12 @@ pub fn pll_setup_delay() {
} }
pub trait ClkgenExt { pub trait ClkgenExt {
fn constrain(self) -> ClockConfigurator; fn constrain(self) -> ClkgenCfgr;
} }
impl ClkgenExt for pac::Clkgen { impl ClkgenExt for pac::Clkgen {
fn constrain(self) -> ClockConfigurator { fn constrain(self) -> ClkgenCfgr {
ClockConfigurator { ClkgenCfgr {
source_clk: None, source_clk: None,
ref_clk_sel: RefClkSel::None, ref_clk_sel: RefClkSel::None,
clksel_sys: ClkselSys::Hbo, clksel_sys: ClkselSys::Hbo,
@ -129,6 +241,21 @@ impl ClkgenExt for pac::Clkgen {
} }
} }
pub struct ClkgenCfgr {
ref_clk_sel: RefClkSel,
clksel_sys: ClkselSys,
clk_div_sel: ClkDivSel,
/// The source clock frequency which is either an external clock connected to XTAL_N, or a
/// crystal connected to the XTAL_OSC input.
source_clk: Option<Hertz>,
pll_cfg: Option<PllCfg>,
clk_lost_detection: bool,
/// Feature only works on revision B of the board.
#[cfg(feature = "revb")]
pll_lock_lost_detection: bool,
clkgen: pac::Clkgen,
}
#[derive(Debug, PartialEq, Eq)] #[derive(Debug, PartialEq, Eq)]
#[cfg_attr(feature = "defmt", derive(defmt::Format))] #[cfg_attr(feature = "defmt", derive(defmt::Format))]
pub struct ClkSourceFreqNotSet; pub struct ClkSourceFreqNotSet;
@ -142,21 +269,6 @@ pub enum ClkCfgError {
InconsistentCfg, InconsistentCfg,
} }
pub struct ClockConfigurator {
ref_clk_sel: RefClkSel,
clksel_sys: ClkselSys,
clk_div_sel: ClkDivSel,
/// The source clock frequency which is either an external clock connected to XTAL_N, or a
/// crystal connected to the XTAL_OSC input.
source_clk: Option<Hertz>,
pll_cfg: Option<PllCfg>,
clk_lost_detection: bool,
/// Feature only works on revision B of the board.
#[cfg(feature = "revb")]
pll_lock_lost_detection: bool,
clkgen: pac::Clkgen,
}
/// Delays a given amount of milliseconds. /// Delays a given amount of milliseconds.
/// ///
/// Taken from the HAL implementation. This implementation is probably not precise and it /// Taken from the HAL implementation. This implementation is probably not precise and it
@ -171,30 +283,7 @@ pub fn hbo_clock_delay_ms(ms: u32) {
} }
} }
impl ClockConfigurator { impl ClkgenCfgr {
/// Create a new clock configuration instance.
pub fn new(clkgen: pac::Clkgen) -> Self {
ClockConfigurator {
source_clk: None,
ref_clk_sel: RefClkSel::None,
clksel_sys: ClkselSys::Hbo,
clk_div_sel: ClkDivSel::Div1,
clk_lost_detection: false,
pll_lock_lost_detection: false,
pll_cfg: None,
clkgen,
}
}
/// Steals a new [ClockConfigurator] instance.
///
/// # Safety
///
/// Circumvents HAL ownership rules.
pub unsafe fn steal() -> Self {
Self::new(unsafe { pac::Clkgen::steal() })
}
#[inline] #[inline]
pub fn source_clk(mut self, src_clk: Hertz) -> Self { pub fn source_clk(mut self, src_clk: Hertz) -> Self {
self.source_clk = Some(src_clk); self.source_clk = Some(src_clk);
@ -245,7 +334,7 @@ impl ClockConfigurator {
/// might have had a reason for those, so I am going to keep them. Chances are, this /// might have had a reason for those, so I am going to keep them. Chances are, this
/// process only has to be performed once, and it does not matter if it takes a few /// process only has to be performed once, and it does not matter if it takes a few
/// microseconds or milliseconds longer. /// microseconds or milliseconds longer.
pub fn freeze(self) -> Result<Clocks, ClkCfgError> { pub fn freeze(self, syscfg: &mut pac::Sysconfig) -> Result<Clocks, ClkCfgError> {
// Sanitize configuration. // Sanitize configuration.
if self.source_clk.is_none() { if self.source_clk.is_none() {
return Err(ClkCfgError::ClkSourceFreqNotSet); return Err(ClkCfgError::ClkSourceFreqNotSet);
@ -260,7 +349,7 @@ impl ClockConfigurator {
return Err(ClkCfgError::PllConfigNotSet); return Err(ClkCfgError::PllConfigNotSet);
} }
enable_peripheral_clock(PeripheralSelect::Clkgen); syscfg.enable_peripheral_clock(PeripheralSelect::Clkgen);
let mut final_sysclk = self.source_clk.unwrap(); let mut final_sysclk = self.source_clk.unwrap();
// The HAL forces back the HBO clock here with a delay.. Even though this is // The HAL forces back the HBO clock here with a delay.. Even though this is
// not stricly necessary when coming from a fresh start, it could be still become relevant // not stricly necessary when coming from a fresh start, it could be still become relevant
@ -369,11 +458,13 @@ impl ClockConfigurator {
.ctrl0() .ctrl0()
.modify(|_, w| unsafe { w.clksel_sys().bits(self.clksel_sys as u8) }); .modify(|_, w| unsafe { w.clksel_sys().bits(self.clksel_sys as u8) });
Ok(Clocks::__new( Ok(Clocks {
final_sysclk, sysclk: final_sysclk,
apb1: final_sysclk / 2,
apb2: final_sysclk / 4,
#[cfg(not(feature = "va41628"))] #[cfg(not(feature = "va41628"))]
self.cfg_adc_clk_div(final_sysclk), adc_clk: self.cfg_adc_clk_div(final_sysclk),
)) })
} }
#[cfg(not(feature = "va41628"))] #[cfg(not(feature = "va41628"))]
@ -396,6 +487,54 @@ impl ClockConfigurator {
} }
} }
/// Frozen clock frequencies
///
/// The existence of this value indicates that the clock configuration can no longer be changed.
/// The [self] module documentation gives some more information on how to retrieve an instance
/// of this structure.
#[derive(Copy, Clone, PartialEq, Eq, Debug)]
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
pub struct Clocks {
sysclk: Hertz,
apb1: Hertz,
apb2: Hertz,
#[cfg(not(feature = "va41628"))]
adc_clk: Hertz,
}
impl Clocks {
/// Returns the frequency of the HBO clock
pub const fn hbo(&self) -> Hertz {
HBO_FREQ
}
/// Returns the frequency of the APB0 which is equal to the system clock.
pub const fn apb0(&self) -> Hertz {
self.sysclk()
}
/// Returns system clock divied by 2.
pub const fn apb1(&self) -> Hertz {
self.apb1
}
/// Returns system clock divied by 4.
pub const fn apb2(&self) -> Hertz {
self.apb2
}
/// Returns the system (core) frequency
pub const fn sysclk(&self) -> Hertz {
self.sysclk
}
/// Returns the ADC clock frequency which has a separate divider.
#[cfg(not(feature = "va41628"))]
pub const fn adc_clk(&self) -> Hertz {
self.adc_clk
}
}
pub fn rearm_sysclk_lost() { pub fn rearm_sysclk_lost() {
rearm_sysclk_lost_with_periph(&unsafe { pac::Clkgen::steal() }) rearm_sysclk_lost_with_periph(&unsafe { pac::Clkgen::steal() })
} }

View File

@ -5,24 +5,22 @@
//! - [ADC and DAC example](https://github.com/us-irs/va416xx-rs/blob/main/examples/simple/examples/dac-adc.rs) //! - [ADC and DAC example](https://github.com/us-irs/va416xx-rs/blob/main/examples/simple/examples/dac-adc.rs)
use core::ops::Deref; use core::ops::Deref;
use vorago_shared_periphs::{ use crate::{
disable_peripheral_clock, enable_peripheral_clock, reset_peripheral_for_cycles, clock::{Clocks, PeripheralSelect, SyscfgExt},
PeripheralSelect, pac,
}; };
use crate::{clock::Clocks, pac};
pub type DacRegisterBlock = pac::dac0::RegisterBlock; pub type DacRegisterBlock = pac::dac0::RegisterBlock;
/// Common trait implemented by all PAC peripheral access structures. The register block /// Common trait implemented by all PAC peripheral access structures. The register block
/// format is the same for all DAC blocks. /// format is the same for all DAC blocks.
pub trait DacMarker: Deref<Target = DacRegisterBlock> { pub trait Instance: Deref<Target = DacRegisterBlock> {
const IDX: u8; const IDX: u8;
fn ptr() -> *const DacRegisterBlock; fn ptr() -> *const DacRegisterBlock;
} }
impl DacMarker for pac::Dac0 { impl Instance for pac::Dac0 {
const IDX: u8 = 0; const IDX: u8 = 0;
#[inline(always)] #[inline(always)]
@ -31,7 +29,7 @@ impl DacMarker for pac::Dac0 {
} }
} }
impl DacMarker for pac::Dac1 { impl Instance for pac::Dac1 {
const IDX: u8 = 1; const IDX: u8 = 1;
#[inline(always)] #[inline(always)]
@ -52,37 +50,40 @@ pub enum DacSettling {
Apb2Times150 = 6, Apb2Times150 = 6,
} }
pub struct Dac(*const DacRegisterBlock); pub struct Dac<DacInstance> {
dac: DacInstance,
}
#[derive(Debug, PartialEq, Eq, Copy, Clone)] #[derive(Debug, PartialEq, Eq, Copy, Clone)]
#[cfg_attr(feature = "defmt", derive(defmt::Format))] #[cfg_attr(feature = "defmt", derive(defmt::Format))]
pub struct ValueTooLarge; pub struct ValueTooLarge;
impl Dac { impl<DacInstance: Instance> Dac<DacInstance> {
/// Create a new [Dac] driver instance. /// Create a new [Dac] driver instance.
/// ///
/// The [Clocks] structure is expected here as well to ensure the clock was set up properly. /// The [Clocks] structure is expected here as well to ensure the clock was set up properly.
pub fn new<Dac: DacMarker>(dac: Dac, dac_settling: DacSettling, _clocks: &Clocks) -> Self { pub fn new(
enable_peripheral_clock(PeripheralSelect::Dac); syscfg: &mut pac::Sysconfig,
dac: DacInstance,
dac_settling: DacSettling,
_clocks: &Clocks,
) -> Self {
syscfg.enable_peripheral_clock(PeripheralSelect::Dac);
dac.ctrl1().write(|w| { dac.ctrl1().write(|w| {
w.dac_en().set_bit(); w.dac_en().set_bit();
// SAFETY: Enum values are valid values only. // SAFETY: Enum values are valid values only.
unsafe { w.dac_settling().bits(dac_settling as u8) } unsafe { w.dac_settling().bits(dac_settling as u8) }
}); });
let mut dac = Self(Dac::ptr()); let dac = Self { dac };
dac.clear_fifo(); dac.clear_fifo();
dac.clear_irqs(); dac.clear_irqs();
dac dac
} }
pub const fn regs(&self) -> &DacRegisterBlock {
unsafe { &*self.0 }
}
#[inline(always)] #[inline(always)]
pub fn clear_irqs(&mut self) { pub fn clear_irqs(&self) {
self.regs().irq_clr().write(|w| { self.dac.irq_clr().write(|w| {
w.fifo_oflow().set_bit(); w.fifo_oflow().set_bit();
w.fifo_uflow().set_bit(); w.fifo_uflow().set_bit();
w.dac_done().set_bit(); w.dac_done().set_bit();
@ -91,30 +92,31 @@ impl Dac {
} }
#[inline(always)] #[inline(always)]
pub fn clear_fifo(&mut self) { pub fn clear_fifo(&self) {
self.regs().fifo_clr().write(|w| unsafe { w.bits(1) }); self.dac.fifo_clr().write(|w| unsafe { w.bits(1) });
} }
/// Load next value into the FIFO. /// Load next value into the FIFO.
/// ///
/// Uses the [nb] API to allow blocking and non-blocking usage. /// Uses the [nb] API to allow blocking and non-blocking usage.
#[inline(always)] #[inline(always)]
pub fn load_value(&mut self, val: u16) -> nb::Result<(), ValueTooLarge> { pub fn load_value(&self, val: u16) -> nb::Result<(), ValueTooLarge> {
if val > 2_u16.pow(12) - 1 { if val > 2_u16.pow(12) - 1 {
return Err(nb::Error::Other(ValueTooLarge)); return Err(nb::Error::Other(ValueTooLarge));
} }
let regs = self.regs(); if self.dac.status().read().fifo_entry_cnt().bits() >= 32_u8 {
if regs.status().read().fifo_entry_cnt().bits() >= 32_u8 {
return Err(nb::Error::WouldBlock); return Err(nb::Error::WouldBlock);
} }
regs.fifo_data().write(|w| unsafe { w.bits(val.into()) }); self.dac
.fifo_data()
.write(|w| unsafe { w.bits(val.into()) });
Ok(()) Ok(())
} }
/// This loads and triggers the next value immediately. It also clears the FIFO before /// This loads and triggers the next value immediately. It also clears the FIFO before
/// loading the passed value. /// loading the passed value.
#[inline(always)] #[inline(always)]
pub fn load_and_trigger_manually(&mut self, val: u16) -> Result<(), ValueTooLarge> { pub fn load_and_trigger_manually(&self, val: u16) -> Result<(), ValueTooLarge> {
if val > 2_u16.pow(12) - 1 { if val > 2_u16.pow(12) - 1 {
return Err(ValueTooLarge); return Err(ValueTooLarge);
} }
@ -130,30 +132,31 @@ impl Dac {
/// to be processed by the DAC. /// to be processed by the DAC.
#[inline(always)] #[inline(always)]
pub fn trigger_manually(&self) { pub fn trigger_manually(&self) {
self.regs().ctrl0().write(|w| w.man_trig_en().set_bit()); self.dac.ctrl0().write(|w| w.man_trig_en().set_bit());
} }
#[inline(always)] #[inline(always)]
pub fn enable_external_trigger(&self) { pub fn enable_external_trigger(&self) {
self.regs().ctrl0().write(|w| w.ext_trig_en().set_bit()); self.dac.ctrl0().write(|w| w.ext_trig_en().set_bit());
} }
pub fn is_settled(&self) -> nb::Result<(), ()> { pub fn is_settled(&self) -> nb::Result<(), ()> {
if self.regs().status().read().dac_busy().bit_is_set() { if self.dac.status().read().dac_busy().bit_is_set() {
return Err(nb::Error::WouldBlock); return Err(nb::Error::WouldBlock);
} }
Ok(()) Ok(())
} }
#[inline(always)] #[inline(always)]
pub fn reset(&mut self) { pub fn reset(&mut self, syscfg: &mut pac::Sysconfig) {
enable_peripheral_clock(PeripheralSelect::Dac); syscfg.enable_peripheral_clock(PeripheralSelect::Dac);
reset_peripheral_for_cycles(PeripheralSelect::Dac, 2); syscfg.assert_periph_reset_for_two_cycles(PeripheralSelect::Dac);
} }
/// Stops the DAC, which disables its peripheral clock. /// Relases the DAC, which also disables its peripheral clock.
#[inline(always)] #[inline(always)]
pub fn stop(self) { pub fn release(self, syscfg: &mut pac::Sysconfig) -> DacInstance {
disable_peripheral_clock(PeripheralSelect::Dac); syscfg.disable_peripheral_clock(PeripheralSelect::Dac);
self.dac
} }
} }

View File

@ -3,23 +3,21 @@
//! ## Examples //! ## Examples
//! //!
//! - [Simple DMA example](https://egit.irs.uni-stuttgart.de/rust/va416xx-rs/src/branch/main/examples/simple/examples/dma.rs) //! - [Simple DMA example](https://egit.irs.uni-stuttgart.de/rust/va416xx-rs/src/branch/main/examples/simple/examples/dma.rs)
use arbitrary_int::{u10, u3}; use crate::{
use vorago_shared_periphs::{ clock::{PeripheralClock, PeripheralSelect},
enable_peripheral_clock, reset_peripheral_for_cycles, PeripheralSelect, enable_nvic_interrupt, pac,
prelude::*,
}; };
use crate::{enable_nvic_interrupt, pac};
const MAX_DMA_TRANSFERS_PER_CYCLE: usize = 1024; const MAX_DMA_TRANSFERS_PER_CYCLE: usize = 1024;
const BASE_PTR_ADDR_MASK: u32 = 0b1111111; const BASE_PTR_ADDR_MASK: u32 = 0b1111111;
/// DMA cycle control values. /// DMA cycle control values.
/// ///
/// Refer to chapter 6.3.1 and 6.6.3 of the datasheet for more details. /// Refer to chapter 6.3.1 and 6.6.3 of the datasheet for more details.
#[bitbybit::bitenum(u3, exhaustive = true)]
#[derive(Debug)]
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
#[repr(u8)] #[repr(u8)]
#[derive(Debug, Clone, Copy)]
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
pub enum CycleControl { pub enum CycleControl {
/// Indicates that the data structure is invalid. /// Indicates that the data structure is invalid.
Stop = 0b000, Stop = 0b000,
@ -44,8 +42,7 @@ pub enum CycleControl {
PeriphScatterGatherAlternate = 0b111, PeriphScatterGatherAlternate = 0b111,
} }
#[bitbybit::bitenum(u2, exhaustive = true)] #[derive(Debug, Clone, Copy)]
#[derive(Debug)]
#[cfg_attr(feature = "defmt", derive(defmt::Format))] #[cfg_attr(feature = "defmt", derive(defmt::Format))]
pub enum AddrIncrement { pub enum AddrIncrement {
Byte = 0b00, Byte = 0b00,
@ -54,8 +51,7 @@ pub enum AddrIncrement {
None = 0b11, None = 0b11,
} }
#[bitbybit::bitenum(u2, exhaustive = false)] #[derive(Debug, Clone, Copy)]
#[derive(Debug)]
#[cfg_attr(feature = "defmt", derive(defmt::Format))] #[cfg_attr(feature = "defmt", derive(defmt::Format))]
pub enum DataSize { pub enum DataSize {
Byte = 0b00, Byte = 0b00,
@ -64,8 +60,7 @@ pub enum DataSize {
} }
/// This configuration controls how many DMA transfers can occur before the controller arbitrates. /// This configuration controls how many DMA transfers can occur before the controller arbitrates.
#[bitbybit::bitenum(u4, exhaustive = true)] #[derive(Debug, Clone, Copy)]
#[derive(Debug)]
#[cfg_attr(feature = "defmt", derive(defmt::Format))] #[cfg_attr(feature = "defmt", derive(defmt::Format))]
pub enum RPower { pub enum RPower {
EachTransfer = 0b0000, EachTransfer = 0b0000,
@ -78,12 +73,8 @@ pub enum RPower {
Every128 = 0b0111, Every128 = 0b0111,
Every256 = 0b1000, Every256 = 0b1000,
Every512 = 0b1001, Every512 = 0b1001,
Every1024 = 0b1010, Every1024Min = 0b1010,
Every1024Alt0 = 0b1011, Every1024 = 0b1111,
Every1024Alt1 = 0b1100,
Every1024Alt2 = 0b1101,
Every1024Alt3 = 0b1110,
Every1024Alt4 = 0b1111,
} }
#[derive(Debug, PartialEq, Eq, thiserror::Error)] #[derive(Debug, PartialEq, Eq, thiserror::Error)]
@ -91,7 +82,6 @@ pub enum RPower {
#[cfg_attr(feature = "defmt", derive(defmt::Format))] #[cfg_attr(feature = "defmt", derive(defmt::Format))]
pub struct InvalidCtrlBlockAddrError; pub struct InvalidCtrlBlockAddrError;
/*
bitfield::bitfield! { bitfield::bitfield! {
#[repr(transparent)] #[repr(transparent)]
#[derive(Clone, Copy)] #[derive(Clone, Copy)]
@ -121,33 +111,6 @@ bitfield::bitfield! {
u8; u8;
pub cycle_ctrl, set_cycle_ctr: 2, 0; pub cycle_ctrl, set_cycle_ctr: 2, 0;
} }
*/
#[bitbybit::bitfield(u32, default = 0x0)]
#[derive(Debug)]
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
pub struct ChannelConfig {
#[bits(30..=31, rw)]
dst_inc: AddrIncrement,
#[bits(28..=29, rw)]
dst_size: Option<DataSize>,
#[bits(26..=27, rw)]
src_inc: AddrIncrement,
#[bits(24..=25, rw)]
src_size: Option<DataSize>,
#[bits(21..=23, rw)]
dest_prot_ctrl: u3,
#[bits(18..=20, rw)]
src_prot_ctrl: u3,
#[bits(14..=17, rw)]
r_power: RPower,
#[bits(4..=13, rw)]
n_minus_1: u10,
#[bit(3, rw)]
next_useburst: bool,
#[bits(0..=2, rw)]
cycle_ctrl: CycleControl,
}
#[repr(C)] #[repr(C)]
#[derive(Debug, Copy, Clone)] #[derive(Debug, Copy, Clone)]
@ -164,7 +127,7 @@ impl DmaChannelControl {
Self { Self {
src_end_ptr: 0, src_end_ptr: 0,
dest_end_ptr: 0, dest_end_ptr: 0,
cfg: ChannelConfig::new_with_raw_value(0), cfg: ChannelConfig(0),
padding: 0, padding: 0,
} }
} }
@ -465,18 +428,20 @@ impl DmaChannel {
return Err(DmaTransferInitError::TransferSizeTooLarge(source.len())); return Err(DmaTransferInitError::TransferSizeTooLarge(source.len()));
} }
let len = source.len() - 1; let len = source.len() - 1;
self.ch_ctrl_pri.cfg = ChannelConfig::new_with_raw_value(0); self.ch_ctrl_pri.cfg.set_raw(0);
self.ch_ctrl_pri.src_end_ptr = (source.as_ptr() as u32) self.ch_ctrl_pri.src_end_ptr = (source.as_ptr() as u32)
.checked_add(len as u32) .checked_add(len as u32)
.ok_or(DmaTransferInitError::AddrOverflow)?; .ok_or(DmaTransferInitError::AddrOverflow)?;
self.ch_ctrl_pri.dest_end_ptr = dest as u32; self.ch_ctrl_pri.dest_end_ptr = dest as u32;
self.ch_ctrl_pri.cfg.set_cycle_ctrl(CycleControl::Basic); self.ch_ctrl_pri
self.ch_ctrl_pri.cfg.set_src_size(DataSize::Byte); .cfg
self.ch_ctrl_pri.cfg.set_src_inc(AddrIncrement::Byte); .set_cycle_ctr(CycleControl::Basic as u8);
self.ch_ctrl_pri.cfg.set_dst_size(DataSize::Byte); self.ch_ctrl_pri.cfg.set_src_size(DataSize::Byte as u8);
self.ch_ctrl_pri.cfg.set_dst_inc(AddrIncrement::None); self.ch_ctrl_pri.cfg.set_src_inc(AddrIncrement::Byte as u8);
self.ch_ctrl_pri.cfg.set_n_minus_1(u10::new(len as u16)); self.ch_ctrl_pri.cfg.set_dst_size(DataSize::Byte as u8);
self.ch_ctrl_pri.cfg.set_r_power(RPower::Every8); self.ch_ctrl_pri.cfg.set_dst_inc(AddrIncrement::None as u8);
self.ch_ctrl_pri.cfg.set_n_minus_1(len as u16);
self.ch_ctrl_pri.cfg.set_r_power(RPower::Every8 as u8);
self.select_primary_structure(); self.select_primary_structure();
Ok(()) Ok(())
} }
@ -505,18 +470,16 @@ impl DmaChannel {
data_size: DataSize, data_size: DataSize,
addr_incr: AddrIncrement, addr_incr: AddrIncrement,
) { ) {
self.ch_ctrl_pri.cfg = ChannelConfig::new_with_raw_value(0); self.ch_ctrl_pri.cfg.set_raw(0);
self.ch_ctrl_pri.src_end_ptr = src_end_ptr; self.ch_ctrl_pri.src_end_ptr = src_end_ptr;
self.ch_ctrl_pri.dest_end_ptr = dest_end_ptr; self.ch_ctrl_pri.dest_end_ptr = dest_end_ptr;
self.ch_ctrl_pri.cfg.set_cycle_ctrl(CycleControl::Auto); self.ch_ctrl_pri.cfg.set_cycle_ctr(CycleControl::Auto as u8);
self.ch_ctrl_pri.cfg.set_src_size(data_size); self.ch_ctrl_pri.cfg.set_src_size(data_size as u8);
self.ch_ctrl_pri.cfg.set_src_inc(addr_incr); self.ch_ctrl_pri.cfg.set_src_inc(addr_incr as u8);
self.ch_ctrl_pri.cfg.set_dst_size(data_size); self.ch_ctrl_pri.cfg.set_dst_size(data_size as u8);
self.ch_ctrl_pri.cfg.set_dst_inc(addr_incr); self.ch_ctrl_pri.cfg.set_dst_inc(addr_incr as u8);
self.ch_ctrl_pri self.ch_ctrl_pri.cfg.set_n_minus_1(n_minus_one as u16);
.cfg self.ch_ctrl_pri.cfg.set_r_power(RPower::Every4 as u8);
.set_n_minus_1(u10::new(n_minus_one as u16));
self.ch_ctrl_pri.cfg.set_r_power(RPower::Every4);
self.select_primary_structure(); self.select_primary_structure();
} }
} }
@ -532,6 +495,7 @@ impl Dma {
/// Alternatively, the [DmaCtrlBlock::new_at_addr] function can be used to create the DMA /// Alternatively, the [DmaCtrlBlock::new_at_addr] function can be used to create the DMA
/// control block at a specific address. /// control block at a specific address.
pub fn new( pub fn new(
syscfg: &mut pac::Sysconfig,
dma: pac::Dma, dma: pac::Dma,
cfg: DmaCfg, cfg: DmaCfg,
ctrl_block: *mut DmaCtrlBlock, ctrl_block: *mut DmaCtrlBlock,
@ -541,8 +505,8 @@ impl Dma {
if raw_addr & BASE_PTR_ADDR_MASK > 0 { if raw_addr & BASE_PTR_ADDR_MASK > 0 {
return Err(InvalidCtrlBlockAddrError); return Err(InvalidCtrlBlockAddrError);
} }
enable_peripheral_clock(PeripheralSelect::Dma); syscfg.enable_peripheral_clock(PeripheralClock::Dma);
reset_peripheral_for_cycles(PeripheralSelect::Dma, 2); syscfg.assert_periph_reset_for_two_cycles(PeripheralSelect::Dma);
let dma = Dma { dma, ctrl_block }; let dma = Dma { dma, ctrl_block };
dma.dma dma.dma
.ctrl_base_ptr() .ctrl_base_ptr()

View File

@ -0,0 +1,448 @@
//! # Async GPIO functionality for the VA416xx 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 va416xx::{self as pac};
use crate::enable_nvic_interrupt;
use super::{
pin, DynPin, DynPinId, InputConfig, InterruptEdge, InvalidPinTypeError, Pin, PinId, Port,
NUM_PINS_PORT_A_TO_F,
};
static WAKERS_FOR_PORT_A: [AtomicWaker; NUM_PINS_PORT_A_TO_F] =
[const { AtomicWaker::new() }; NUM_PINS_PORT_A_TO_F];
static WAKERS_FOR_PORT_B: [AtomicWaker; NUM_PINS_PORT_A_TO_F] =
[const { AtomicWaker::new() }; NUM_PINS_PORT_A_TO_F];
static WAKERS_FOR_PORT_C: [AtomicWaker; NUM_PINS_PORT_A_TO_F] =
[const { AtomicWaker::new() }; NUM_PINS_PORT_A_TO_F];
static WAKERS_FOR_PORT_D: [AtomicWaker; NUM_PINS_PORT_A_TO_F] =
[const { AtomicWaker::new() }; NUM_PINS_PORT_A_TO_F];
static WAKERS_FOR_PORT_E: [AtomicWaker; NUM_PINS_PORT_A_TO_F] =
[const { AtomicWaker::new() }; NUM_PINS_PORT_A_TO_F];
static WAKERS_FOR_PORT_F: [AtomicWaker; NUM_PINS_PORT_A_TO_F] =
[const { AtomicWaker::new() }; NUM_PINS_PORT_A_TO_F];
static EDGE_DETECTION_PORT_A: [AtomicBool; NUM_PINS_PORT_A_TO_F] =
[const { AtomicBool::new(false) }; NUM_PINS_PORT_A_TO_F];
static EDGE_DETECTION_PORT_B: [AtomicBool; NUM_PINS_PORT_A_TO_F] =
[const { AtomicBool::new(false) }; NUM_PINS_PORT_A_TO_F];
static EDGE_DETECTION_PORT_C: [AtomicBool; NUM_PINS_PORT_A_TO_F] =
[const { AtomicBool::new(false) }; NUM_PINS_PORT_A_TO_F];
static EDGE_DETECTION_PORT_D: [AtomicBool; NUM_PINS_PORT_A_TO_F] =
[const { AtomicBool::new(false) }; NUM_PINS_PORT_A_TO_F];
static EDGE_DETECTION_PORT_E: [AtomicBool; NUM_PINS_PORT_A_TO_F] =
[const { AtomicBool::new(false) }; NUM_PINS_PORT_A_TO_F];
static EDGE_DETECTION_PORT_F: [AtomicBool; NUM_PINS_PORT_A_TO_F] =
[const { AtomicBool::new(false) }; NUM_PINS_PORT_A_TO_F];
#[derive(Debug, thiserror::Error)]
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
#[error("port G does not support async functionality")]
pub struct PortGDoesNotSupportAsyncError;
#[derive(Debug, thiserror::Error)]
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
pub enum AsyncDynPinError {
#[error("invalid pin type: {0}")]
InvalidPinType(#[from] InvalidPinTypeError),
#[error("port g does not support async functionality: {0}")]
PortGDoesNotSupportAsync(#[from] PortGDoesNotSupportAsyncError),
}
/// 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,
) -> Result<(), PortGDoesNotSupportAsyncError> {
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,
&EDGE_DETECTION_PORT_A,
),
Port::B => (
periphs.portb.irq_enb().read().bits(),
periphs.portb.edge_status().read().bits(),
&WAKERS_FOR_PORT_B,
&EDGE_DETECTION_PORT_B,
),
Port::C => (
periphs.portc.irq_enb().read().bits(),
periphs.portc.edge_status().read().bits(),
&WAKERS_FOR_PORT_C,
&EDGE_DETECTION_PORT_C,
),
Port::D => (
periphs.portd.irq_enb().read().bits(),
periphs.portd.edge_status().read().bits(),
&WAKERS_FOR_PORT_D,
&EDGE_DETECTION_PORT_D,
),
Port::E => (
periphs.porte.irq_enb().read().bits(),
periphs.porte.edge_status().read().bits(),
&WAKERS_FOR_PORT_E,
&EDGE_DETECTION_PORT_E,
),
Port::F => (
periphs.portf.irq_enb().read().bits(),
periphs.portf.edge_status().read().bits(),
&WAKERS_FOR_PORT_F,
&EDGE_DETECTION_PORT_F,
),
Port::G => return Err(PortGDoesNotSupportAsyncError),
};
on_interrupt_for_port(irq_enb, edge_status, wakers, edge_detection);
Ok(())
}
#[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 {
pub fn new_with_dyn_pin(
pin: &mut DynPin,
edge: InterruptEdge,
) -> Result<Self, AsyncDynPinError> {
if !pin.is_input_pin() {
return Err(InvalidPinTypeError(pin.mode()).into());
}
if pin.id().port() == Port::G {
return Err(PortGDoesNotSupportAsyncError.into());
}
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);
// Unwraps okay, checked for PORT G previously
pin.configure_edge_interrupt(edge).unwrap();
unsafe { enable_nvic_interrupt(pin.irq_id().unwrap()) };
pin.enable_interrupt();
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>>,
edge: InterruptEdge,
) -> Result<Self, PortGDoesNotSupportAsyncError> {
if pin.id().port() == Port::G {
return Err(PortGDoesNotSupportAsyncError);
}
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);
// Unwraps okay, checked for PORT G previously
pin.configure_edge_interrupt(edge);
unsafe { enable_nvic_interrupt(I::IRQ.unwrap()) };
pin.enable_interrupt();
Ok(Self {
pin_id: pin.id(),
waker_group,
edge_detection_group,
})
}
#[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()),
Port::C => (WAKERS_FOR_PORT_C.as_ref(), EDGE_DETECTION_PORT_C.as_ref()),
Port::D => (WAKERS_FOR_PORT_D.as_ref(), EDGE_DETECTION_PORT_D.as_ref()),
Port::E => (WAKERS_FOR_PORT_E.as_ref(), EDGE_DETECTION_PORT_E.as_ref()),
Port::F => (WAKERS_FOR_PORT_F.as_ref(), EDGE_DETECTION_PORT_F.as_ref()),
_ => panic!("unexpected pin group G"),
}
}
}
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();
}
}
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,
}
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) -> Result<Self, AsyncDynPinError> {
if !pin.is_input_pin() {
return Err(InvalidPinTypeError(pin.mode()).into());
}
if pin.id().port() == Port::G {
return Err(PortGDoesNotSupportAsyncError.into());
}
Ok(Self { pin })
}
/// 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, 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, 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, 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, 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, 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>>,
}
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>>) -> Result<Self, PortGDoesNotSupportAsyncError> {
if pin.id().port() == Port::G {
return Err(PortGDoesNotSupportAsyncError);
}
Ok(Self { pin })
}
/// 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_pin(&mut self.pin, InterruptEdge::LowToHigh).unwrap();
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, InterruptEdge::HighToLow).unwrap();
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, InterruptEdge::HighToLow)
.unwrap()
.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, 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_pin(&mut self.pin, InterruptEdge::BothEdges)
.unwrap()
.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(())
}
}

File diff suppressed because it is too large Load Diff

View File

@ -1,20 +1,80 @@
//! # 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/va416xx-rs/src/branch/main/examples/simple/examples/blinky.rs) //! - [Blinky example](https://egit.irs.uni-stuttgart.de/rust/va416xx-rs/src/branch/main/examples/simple/examples/blinky.rs)
//! - [Async GPIO example](https://egit.irs.uni-stuttgart.de/rust/va416xx-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_TO_F: usize = 16;
pub const NUM_PINS_PORT_G: usize = 8;
pub const NUM_GPIO_PINS: usize = NUM_PINS_PORT_A_TO_F * 6 + NUM_PINS_PORT_G;
pub const NUM_GPIO_PINS_WITH_IRQ: usize = NUM_GPIO_PINS - NUM_PINS_PORT_G;
#[derive(Debug, PartialEq, Eq, thiserror::Error)]
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
#[error("pin is masked")]
pub struct IsMaskedError;
#[derive(Debug, PartialEq, Eq, Clone, Copy)]
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
pub enum Port {
A,
B,
C,
D,
E,
F,
G,
}
#[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 pin;
pub use pin::*;
pub mod dynpin;
pub use dynpin::*;
pub mod asynch;
pub use asynch::*;

935
va416xx-hal/src/gpio/pin.rs Normal file
View File

@ -0,0 +1,935 @@
//! # 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 to G) 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.
//!
//!
//! ```no_run
//! let mut peripherals = Peripherals::take().unwrap();
//! let pinsa = PinsA::new(peripherals.porta);
//! ```
//!
//! 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 core::{convert::Infallible, marker::PhantomData, mem::transmute};
pub use crate::clock::FilterClkSel;
use crate::typelevel::Sealed;
use va416xx::{self as pac, Porta, Portb, Portc, Portd, Porte, Portf, Portg};
use super::{
DynAlternate, DynInput, DynOutput, DynPin, DynPinId, DynPinMode, InputPinAsync, InterruptEdge,
InterruptLevel, PinState, Port, PortGDoesNotSupportAsyncError,
};
//==================================================================================================
// 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;
}
pub enum Floating {}
pub enum PullDown {}
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]
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,
}
//==================================================================================================
// Output configuration
//==================================================================================================
pub trait OutputConfig: Sealed {
const DYN: DynOutput;
}
pub trait ReadableOutput: Sealed {}
/// Type-level variant of [`OutputConfig`] for a push-pull configuration
pub enum PushPull {}
/// Type-level variant of [`OutputConfig`] for an open drain configuration
pub enum OpenDrain {}
/// Type-level variant of [`OutputConfig`] for a readable push-pull configuration
pub enum ReadablePushPull {}
/// Type-level variant of [`OutputConfig`] for a readable open-drain configuration
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
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;
const IRQ: Option<pac::Interrupt>;
}
macro_rules! pin_id {
($Port:ident, $Id:ident, $NUM:literal, $Irq:expr, $(, $meta: meta)?) => {
// Need paste macro to use ident in doc attribute
paste::paste! {
$(#[$meta])?
#[doc = "Pin ID representing pin " $Id]
pub enum $Id {}
$(#[$meta])?
impl Sealed for $Id {}
$(#[$meta])?
impl PinId for $Id {
const DYN: DynPinId = DynPinId::new(Port::$Port, $NUM);
const IRQ: Option<pac::Interrupt> = $Irq;
}
}
};
}
//==================================================================================================
// Pin
//==================================================================================================
/// A type-level GPIO pin, parameterized by [`PinId`] and [`PinMode`] types
#[derive(Debug)]
pub struct Pin<I: PinId, M: PinMode> {
inner: DynPin,
mode: 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),
mode: PhantomData,
}
}
#[inline]
pub const fn id(&self) -> DynPinId {
self.inner.id()
}
#[inline(always)]
pub const fn irq_id(&self) -> Option<pac::Interrupt> {
I::IRQ
}
/// 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. 286 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. 286 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. 286 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) {
self.inner.enable_interrupt();
}
#[inline]
pub fn disable_interrupt(&mut self) {
self.inner.disable_interrupt();
}
/// 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) -> Result<InputPinAsync<I, C>, PortGDoesNotSupportAsyncError> {
InputPinAsync::new(self)
}
}
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()
}
/// 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();
}
/// 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>> {
#[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(())
}
}
impl<I, C> embedded_hal::digital::InputPin for Pin<I, Output<C>>
where
I: PinId,
C: OutputConfig + ReadableOutput,
{
#[inline]
fn is_high(&mut self) -> Result<bool, Self::Error> {
Ok(self.is_high_mut())
}
#[inline]
fn is_low(&mut self) -> Result<bool, Self::Error> {
Ok(self.is_low_mut())
}
}
//==================================================================================================
// Pin definitions
//==================================================================================================
macro_rules! pins {
(
$Port:ident, $PinsName:ident, $($Id:ident $(, $meta:meta)?)+,
) => {
paste::paste!(
/// Collection of all the individual [`Pin`]s for a given port (PORTA or PORTB)
pub struct $PinsName {
port: $Port,
$(
$(#[$meta])?
#[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 va416xx::Sysconfig,
port: $Port
) -> $PinsName {
syscfg.peripheral_clk_enable().modify(|_, w| {
w.[<$Port:lower>]().set_bit();
w.ioconfig().set_bit()
});
$PinsName {
port,
// Safe because we only create one `Pin` per `PinId`
$(
$(#[$meta])?
[<$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_with_irq {
(
$Group:ident, $PinsName:ident, $Port:ident, [$(($Id:ident, $NUM:literal $(, $meta:meta)?)),+]
) => {
pins!($Port, $PinsName, $($Id $(, $meta)?)+,);
$(
paste::paste! {
pin_id!($Group, $Id, $NUM, Some(pac::Interrupt::[<$Port:upper $NUM>]), $(, $meta)?);
}
)+
}
}
macro_rules! declare_pins {
(
$Group:ident, $PinsName:ident, $Port:ident, [$(($Id:ident, $NUM:literal $(, $meta:meta)?)),+]
) => {
pins!($Port, $PinsName, $($Id $(, $meta)?)+,);
$(
pin_id!($Group, $Id, $NUM, None, $(, $meta)?);
)+
}
}
declare_pins_with_irq!(
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)
]
);
declare_pins_with_irq!(
B,
PinsB,
Portb,
[
(PB0, 0),
(PB1, 1),
(PB2, 2),
(PB3, 3),
(PB4, 4),
(PB5, 5, cfg(not(feature = "va41628"))),
(PB6, 6, cfg(not(feature = "va41628"))),
(PB7, 7, cfg(not(feature = "va41628"))),
(PB8, 8, cfg(not(feature = "va41628"))),
(PB9, 9, cfg(not(feature = "va41628"))),
(PB10, 10, cfg(not(feature = "va41628"))),
(PB11, 11, cfg(not(feature = "va41628"))),
(PB12, 12),
(PB13, 13),
(PB14, 14),
(PB15, 15)
]
);
declare_pins_with_irq!(
C,
PinsC,
Portc,
[
(PC0, 0),
(PC1, 1),
(PC2, 2),
(PC3, 3),
(PC4, 4),
(PC5, 5),
(PC6, 6),
(PC7, 7),
(PC8, 8),
(PC9, 9),
(PC10, 10),
(PC11, 11),
(PC12, 12),
(PC13, 13, cfg(not(feature = "va41628"))),
(PC14, 14),
(PC15, 15, cfg(not(feature = "va41628")))
]
);
declare_pins_with_irq!(
D,
PinsD,
Portd,
[
(PD0, 0, cfg(not(feature = "va41628"))),
(PD1, 1, cfg(not(feature = "va41628"))),
(PD2, 2, cfg(not(feature = "va41628"))),
(PD3, 3, cfg(not(feature = "va41628"))),
(PD4, 4, cfg(not(feature = "va41628"))),
(PD5, 5, cfg(not(feature = "va41628"))),
(PD6, 6, cfg(not(feature = "va41628"))),
(PD7, 7, cfg(not(feature = "va41628"))),
(PD8, 8, cfg(not(feature = "va41628"))),
(PD9, 9, cfg(not(feature = "va41628"))),
(PD10, 10),
(PD11, 11),
(PD12, 12),
(PD13, 13),
(PD14, 14),
(PD15, 15)
]
);
declare_pins_with_irq!(
E,
PinsE,
Porte,
[
(PE0, 0),
(PE1, 1),
(PE2, 2),
(PE3, 3),
(PE4, 4),
(PE5, 5),
(PE6, 6),
(PE7, 7),
(PE8, 8),
(PE9, 9),
(PE10, 10, cfg(not(feature = "va41628"))),
(PE11, 11, cfg(not(feature = "va41628"))),
(PE12, 12),
(PE13, 13),
(PE14, 14),
(PE15, 15)
]
);
declare_pins_with_irq!(
F,
PinsF,
Portf,
[
(PF0, 0),
(PF1, 1),
(PF2, 2, cfg(not(feature = "va41628"))),
(PF3, 3, cfg(not(feature = "va41628"))),
(PF4, 4, cfg(not(feature = "va41628"))),
(PF5, 5, cfg(not(feature = "va41628"))),
(PF6, 6, cfg(not(feature = "va41628"))),
(PF7, 7, cfg(not(feature = "va41628"))),
(PF8, 8, cfg(not(feature = "va41628"))),
(PF9, 9),
(PF10, 10, cfg(not(feature = "va41628"))),
(PF11, 11),
(PF12, 12),
(PF13, 13),
(PF14, 14),
(PF15, 15)
]
);
declare_pins!(
G,
PinsG,
Portg,
[
(PG0, 0),
(PG1, 1),
(PG2, 2),
(PG3, 3),
(PG4, 4),
(PG5, 5),
(PG6, 6),
(PG7, 7)
]
);

View File

@ -3,4 +3,913 @@
//! ## Examples //! ## Examples
//! //!
//! - [PEB1 accelerometer example](https://egit.irs.uni-stuttgart.de/rust/va416xx-rs/src/branch/main/examples/simple/examples/peb1-accelerometer.rs) //! - [PEB1 accelerometer example](https://egit.irs.uni-stuttgart.de/rust/va416xx-rs/src/branch/main/examples/simple/examples/peb1-accelerometer.rs)
pub use vorago_shared_periphs::i2c::*; use crate::{
clock::{Clocks, PeripheralSelect},
pac,
prelude::SyscfgExt,
time::Hertz,
typelevel::Sealed,
};
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(10_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::i2c0::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::I2c0 {
const IDX: u8 = 0;
const PERIPH_SEL: PeripheralSelect = PeripheralSelect::I2c0;
#[inline(always)]
fn ptr() -> *const I2cRegBlock {
Self::ptr()
}
}
impl Instance for pac::I2c1 {
const IDX: u8 = 1;
const PERIPH_SEL: PeripheralSelect = PeripheralSelect::I2c1;
#[inline(always)]
fn ptr() -> *const I2cRegBlock {
Self::ptr()
}
}
impl Instance for pac::I2c2 {
const IDX: u8 = 2;
const PERIPH_SEL: PeripheralSelect = PeripheralSelect::I2c2;
#[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,
clock: 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(
i2c: I2c,
syscfg: &mut pac::Sysconfig,
clocks: &Clocks,
speed_mode: I2cSpeed,
ms_cfg: Option<&MasterConfig>,
sl_cfg: Option<&SlaveConfig>,
) -> Result<Self, ClockTooSlowForFastI2cError> {
syscfg.enable_peripheral_clock(I2c::PERIPH_SEL);
let mut i2c_base = I2cBase {
i2c,
clock: clocks.apb1(),
};
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.clock.raw() / CLK_100K.raw() / 20) - 1) as u8)
} else {
if self.clock.raw() < MIN_CLK_400K.raw() {
return Err(ClockTooSlowForFastI2cError);
}
Ok(((self.clock.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(
i2c: I2c,
sys_cfg: &mut pac::Sysconfig,
cfg: MasterConfig,
clocks: &Clocks,
speed_mode: I2cSpeed,
) -> Result<Self, ClockTooSlowForFastI2cError> {
Ok(I2cMaster {
i2c_base: I2cBase::new(i2c, sys_cfg, clocks, 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(
i2c: I2c,
sys_cfg: &mut pac::Sysconfig,
cfg: SlaveConfig,
clocks: &Clocks,
speed_mode: I2cSpeed,
) -> Result<Self, ClockTooSlowForFastI2cError> {
Ok(I2cSlave {
i2c_base: I2cBase::new(i2c, sys_cfg, clocks, 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(
i2c: I2c,
sys_cfg: &mut pac::Sysconfig,
cfg: SlaveConfig,
clocks: &Clocks,
speed_mode: I2cSpeed,
) -> Result<Self, InitError> {
if let I2cAddress::TenBit(_) = cfg.addr {
return Err(InitError::WrongAddrMode);
}
Ok(Self::new_generic(i2c, sys_cfg, cfg, clocks, speed_mode)?)
}
}
impl<I2c: Instance> I2cSlave<I2c, TenBitAddress> {
pub fn new_ten_bit_addr(
i2c: I2c,
sys_cfg: &mut pac::Sysconfig,
cfg: SlaveConfig,
clocks: &Clocks,
speed_mode: I2cSpeed,
) -> Result<Self, ClockTooSlowForFastI2cError> {
Self::new_generic(i2c, sys_cfg, cfg, clocks, speed_mode)
}
}

View File

@ -1,10 +1,9 @@
//! IRQ Router peripheral support. //! IRQ Router peripheral support.
use vorago_shared_periphs::{ use crate::{
enable_peripheral_clock, reset_peripheral_for_cycles, PeripheralSelect, clock::{PeripheralSelect, SyscfgExt},
pac,
}; };
use crate::pac;
/// This enables and initiates the peripheral. /// This enables and initiates the peripheral.
/// ///
/// Please note that this method also writes 0 to the registers which do not have 0 as the default /// Please note that this method also writes 0 to the registers which do not have 0 as the default
@ -12,10 +11,9 @@ use crate::pac;
/// are inconsistent here, and the registers being non-zero can actually lead to weird bugs /// are inconsistent here, and the registers being non-zero can actually lead to weird bugs
/// when working with interrupts. Registers DMASELx and ADCSEL/DMASELx will reset to 0x7f and 0x1f /// when working with interrupts. Registers DMASELx and ADCSEL/DMASELx will reset to 0x7f and 0x1f
/// respectively instead of 0x00. /// respectively instead of 0x00.
pub fn enable_and_init_irq_router() { pub fn enable_and_init_irq_router(sysconfig: &mut pac::Sysconfig, irq_router: &pac::IrqRouter) {
let irq_router = unsafe { pac::IrqRouter::steal() }; sysconfig.enable_peripheral_clock(PeripheralSelect::IrqRouter);
enable_peripheral_clock(PeripheralSelect::IrqRouter); sysconfig.assert_periph_reset_for_two_cycles(PeripheralSelect::IrqRouter);
reset_peripheral_for_cycles(PeripheralSelect::IrqRouter, 2);
unsafe { unsafe {
irq_router.dmasel0().write_with_zero(|w| w); irq_router.dmasel0().write_with_zero(|w| w);
irq_router.dmasel1().write_with_zero(|w| w); irq_router.dmasel1().write_with_zero(|w| w);

View File

@ -41,11 +41,11 @@ pub mod edac;
pub mod gpio; pub mod gpio;
pub mod i2c; pub mod i2c;
pub mod irq_router; pub mod irq_router;
pub mod pins;
pub mod pwm; pub mod pwm;
pub mod spi; pub mod spi;
pub mod time; pub mod time;
pub mod timer; pub mod timer;
pub mod typelevel;
pub mod uart; pub mod uart;
pub mod wdt; pub mod wdt;
@ -57,11 +57,14 @@ pub mod adc;
#[cfg(not(feature = "va41628"))] #[cfg(not(feature = "va41628"))]
pub mod dac; pub mod dac;
pub use vorago_shared_periphs::{ #[derive(Debug, Eq, Copy, Clone, PartialEq)]
assert_peripheral_reset, deassert_peripheral_reset, disable_nvic_interrupt, #[cfg_attr(feature = "defmt", derive(defmt::Format))]
disable_peripheral_clock, enable_nvic_interrupt, enable_peripheral_clock, pub enum FunSel {
reset_peripheral_for_cycles, FunSel, PeripheralSelect, Sel0 = 0b00,
}; Sel1 = 0b01,
Sel2 = 0b10,
Sel3 = 0b11,
}
#[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))]
@ -97,41 +100,18 @@ pub fn port_function_select(
Ok(()) Ok(())
} }
pub trait SyscfgExt { /// Enable a specific interrupt using the NVIC peripheral.
fn enable_peripheral_clock(&mut self, clock: PeripheralSelect); ///
/// # Safety
fn disable_peripheral_clock(&mut self, clock: PeripheralSelect); ///
/// This function is `unsafe` because it can break mask-based critical sections.
fn assert_periph_reset(&mut self, periph: PeripheralSelect); #[inline]
pub unsafe fn enable_nvic_interrupt(irq: pac::Interrupt) {
fn deassert_periph_reset(&mut self, periph: PeripheralSelect); cortex_m::peripheral::NVIC::unmask(irq);
fn reset_peripheral_reset_for_cycles(&mut self, periph: PeripheralSelect, cycles: usize);
} }
impl SyscfgExt for pac::Sysconfig { /// Disable a specific interrupt using the NVIC peripheral.
#[inline(always)] #[inline]
fn enable_peripheral_clock(&mut self, clock: PeripheralSelect) { pub fn disable_nvic_interrupt(irq: pac::Interrupt) {
enable_peripheral_clock(clock) cortex_m::peripheral::NVIC::mask(irq);
}
#[inline(always)]
fn disable_peripheral_clock(&mut self, clock: PeripheralSelect) {
disable_peripheral_clock(clock)
}
#[inline(always)]
fn assert_periph_reset(&mut self, clock: PeripheralSelect) {
assert_peripheral_reset(clock)
}
#[inline(always)]
fn deassert_periph_reset(&mut self, clock: PeripheralSelect) {
deassert_peripheral_reset(clock)
}
#[inline(always)]
fn reset_peripheral_reset_for_cycles(&mut self, periph: PeripheralSelect, cycles: usize) {
reset_peripheral_for_cycles(periph, cycles)
}
} }

View File

@ -6,14 +6,11 @@
//! //!
//! - [Flashloader application](https://egit.irs.uni-stuttgart.de/rust/va416xx-rs/src/branch/main/flashloader) //! - [Flashloader application](https://egit.irs.uni-stuttgart.de/rust/va416xx-rs/src/branch/main/flashloader)
use embedded_hal::spi::MODE_0; use embedded_hal::spi::MODE_0;
use vorago_shared_periphs::{
disable_peripheral_clock, enable_peripheral_clock, reset_peripheral_for_cycles,
};
use crate::clock::Clocks; use crate::clock::{Clocks, SyscfgExt};
use crate::pac; use crate::pac;
use crate::spi::{ use crate::spi::{
mode_to_cpo_cph_bit, spi_clk_config_from_div, SpiMarker, WordProvider, BMSTART_BMSTOP_MASK, mode_to_cpo_cph_bit, spi_clk_config_from_div, Instance, WordProvider, BMSTART_BMSTOP_MASK,
}; };
const NVM_CLOCK_DIV: u16 = 2; const NVM_CLOCK_DIV: u16 = 2;
@ -68,10 +65,10 @@ pub struct VerifyError {
} }
impl Nvm { impl Nvm {
pub fn new(spi: pac::Spi3, _clocks: &Clocks) -> Self { pub fn new(syscfg: &mut pac::Sysconfig, spi: pac::Spi3, _clocks: &Clocks) -> Self {
enable_peripheral_clock(pac::Spi3::PERIPH_SEL); crate::clock::enable_peripheral_clock(syscfg, pac::Spi3::PERIPH_SEL);
// This is done in the C HAL. // This is done in the C HAL.
reset_peripheral_for_cycles(pac::Spi3::PERIPH_SEL, 2); syscfg.assert_periph_reset_for_two_cycles(pac::Spi3::PERIPH_SEL);
let spi_clk_cfg = spi_clk_config_from_div(NVM_CLOCK_DIV).unwrap(); let spi_clk_cfg = spi_clk_config_from_div(NVM_CLOCK_DIV).unwrap();
let (cpo_bit, cph_bit) = mode_to_cpo_cph_bit(MODE_0); let (cpo_bit, cph_bit) = mode_to_cpo_cph_bit(MODE_0);
@ -237,17 +234,17 @@ impl Nvm {
} }
/// Enable write-protection and disables the peripheral clock. /// Enable write-protection and disables the peripheral clock.
pub fn shutdown(&mut self) { pub fn shutdown(&mut self, sys_cfg: &mut pac::Sysconfig) {
self.wait_for_tx_idle(); self.wait_for_tx_idle();
self.write_with_bmstop(FRAM_WREN); self.write_with_bmstop(FRAM_WREN);
self.wait_for_tx_idle(); self.wait_for_tx_idle();
self.write_single(WPEN_ENABLE_MASK | BP_0_ENABLE_MASK | BP_1_ENABLE_MASK); self.write_single(WPEN_ENABLE_MASK | BP_0_ENABLE_MASK | BP_1_ENABLE_MASK);
disable_peripheral_clock(pac::Spi3::PERIPH_SEL); crate::clock::disable_peripheral_clock(sys_cfg, pac::Spi3::PERIPH_SEL);
} }
/// This function calls [Self::shutdown] and gives back the peripheral structure. /// This function calls [Self::shutdown] and gives back the peripheral structure.
pub fn release(mut self) -> pac::Spi3 { pub fn release(mut self, sys_cfg: &mut pac::Sysconfig) -> pac::Spi3 {
self.shutdown(); self.shutdown(sys_cfg);
self.spi.take().unwrap() self.spi.take().unwrap()
} }
@ -271,7 +268,7 @@ impl Nvm {
impl Drop for Nvm { impl Drop for Nvm {
fn drop(&mut self) { fn drop(&mut self) {
if self.spi.is_some() { if self.spi.is_some() {
self.shutdown(); self.shutdown(unsafe { &mut pac::Sysconfig::steal() });
} }
} }
} }

View File

@ -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::*;

View File

@ -1,4 +1,4 @@
//! Prelude //! Prelude
pub use crate::clock::ClkgenExt; pub use crate::clock::{ClkgenExt, SyscfgExt};
pub use fugit::ExtU32 as _; pub use fugit::ExtU32 as _;
pub use fugit::RateExtU32 as _; pub use fugit::RateExtU32 as _;

View File

@ -1,8 +1,459 @@
//! API for Pulse-Width Modulation (PWM) //! API for Pulse-Width Modulation (PWM)
//! //!
//! The Vorago devices use the TIM peripherals to perform PWM related tasks //! The Vorago VA416xx devices use the TIM peripherals to perform PWM related tasks.
//! //!
//! ## Examples //! ## Examples
//! //!
//! - [PWM example](https://egit.irs.uni-stuttgart.de/rust/va416xx-rs/src/branch/main/examples/simple/examples/pwm.rs) //! - [PWM example](https://egit.irs.uni-stuttgart.de/rust/va416xx-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;
pub use crate::timer::ValidTim;
use crate::timer::{TimAndPinRegister, TimDynRegister, TimPin, TimRegInterface, ValidTimAndPin};
use crate::{clock::Clocks, gpio::DynPinId};
const DUTY_MAX: u16 = u16::MAX;
#[derive(Debug)]
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
pub(crate) struct PwmCommon {
clock: 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> {
reg: TimAndPinRegister<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(
pin_and_tim: (Pin, Tim),
sys_cfg: &mut pac::Sysconfig,
clocks: &Clocks,
initial_period: impl Into<Hertz> + Copy,
) -> Self {
let mut pin = PwmPin {
inner: ReducedPwmPin::<Mode>::new(
Tim::ID,
Pin::DYN,
PwmCommon {
clock: Tim::clock(clocks),
current_duty: 0,
current_lower_limit: 0,
current_period: initial_period.into(),
current_rst_val: 0,
},
),
reg: unsafe { TimAndPinRegister::new(pin_and_tim.0, pin_and_tim.1) },
mode: PhantomData,
};
sys_cfg
.tim_clk_enable()
.modify(|r, w| unsafe { w.bits(r.bits() | pin.reg.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.reg.release()
}
#[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 {
reg: other.reg,
inner: other.inner.into(),
mode: PhantomData,
};
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 pwmb = Self {
reg: other.reg,
inner: other.inner.into(),
mode: PhantomData,
};
pwmb.enable_pwm_a();
pwmb
}
}
impl<Pin: TimPin, Tim: ValidTim> PwmPin<Pin, Tim, PwmA>
where
(Pin, Tim): ValidTimAndPin<Pin, Tim>,
{
pub fn pwma(
tim_and_pin: (Pin, Tim),
sys_cfg: &mut pac::Sysconfig,
clocks: &Clocks,
initial_period: impl Into<Hertz> + Copy,
) -> Self {
let mut pin: PwmPin<Pin, Tim, PwmA> =
Self::new(tim_and_pin, sys_cfg, clocks, 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(
tim_and_pin: (Pin, Tim),
sys_cfg: &mut pac::Sysconfig,
clocks: &Clocks,
initial_period: impl Into<Hertz> + Copy,
) -> Self {
let mut pin: PwmPin<Pin, Tim, PwmB> =
Self::new(tim_and_pin, sys_cfg, clocks, 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,
}
}
#[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.clock.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
}
}

File diff suppressed because it is too large Load Diff

View File

@ -2,8 +2,904 @@
//! //!
//! ## Examples //! ## Examples
//! //!
//! - [MS and second tick implementation](https://egit.irs.uni-stuttgart.de/rust/va416xx-rs/src/branch/main/examples/simple/examples/timer-ticks.rs) //! - [Timer MS and Second Tick Example](https://github.com/us-irs/va416xx-rs/blob/main/examples/simple/examples/timer-ticks.rs)
//! - [Cascade feature example](https://egit.irs.uni-stuttgart.de/rust/va416xx-rs/src/branch/main/examples/simple/examples/cascade.rs) use core::cell::Cell;
pub use vorago_shared_periphs::timer::*;
use cortex_m::asm;
use critical_section::Mutex;
use crate::clock::Clocks;
use crate::gpio::{
AltFunc1, AltFunc2, AltFunc3, DynPinId, Pin, PinId, PA0, PA1, PA10, PA11, PA12, PA13, PA14,
PA15, PA2, PA3, PA4, PA5, PA6, PA7, PB0, PB1, PB12, PB13, PB14, PB15, PB2, PB3, PB4, PC0, PC1,
PD10, PD11, PD12, PD13, PD14, PD15, PE0, PE1, PE12, PE13, PE14, PE15, PE2, PE3, PE4, PE5, PE6,
PE7, PE8, PE9, PF0, PF1, PF11, PF12, PF13, PF14, PF15, PF9, PG0, PG1, PG2, PG3, PG6,
};
#[cfg(not(feature = "va41628"))]
use crate::gpio::{
PB10, PB11, PB5, PB6, PB7, PB8, PB9, PD0, PD1, PD2, PD3, PD4, PD5, PD6, PD7, PD8, PD9, PE10,
PE11, PF10, PF2, PF3, PF4, PF5, PF6, PF7, PF8,
};
use crate::time::Hertz;
use crate::typelevel::Sealed;
use crate::{disable_nvic_interrupt, enable_nvic_interrupt, pac, prelude::*};
pub static MS_COUNTER: Mutex<Cell<u32>> = Mutex::new(Cell::new(0));
pub const TIM_IRQ_OFFSET: usize = 48; pub const TIM_IRQ_OFFSET: usize = 48;
/// 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
//==================================================================================================
#[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 {
Sel0 = 0,
Sel1 = 1,
Sel2 = 2,
}
#[derive(Debug, PartialEq, Eq)]
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
pub struct InvalidCascadeSourceId;
#[derive(Debug, PartialEq, Eq)]
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
pub enum CascadeSource {
PortA(u8),
PortB(u8),
PortC(u8),
PortD(u8),
PortE(u8),
Tim(u8),
TxEv,
AdcIrq,
RomSbe,
RomMbe,
Ram0Sbe,
Ram0Mbe,
Ram1Sbe,
Ram2Mbe,
WdogIrq,
}
impl CascadeSource {
fn id(&self) -> Result<u8, InvalidCascadeSourceId> {
let port_check = |base: u8, id: u8| {
if id > 15 {
return Err(InvalidCascadeSourceId);
}
Ok(base + id)
};
match self {
CascadeSource::PortA(id) => port_check(0, *id),
CascadeSource::PortB(id) => port_check(16, *id),
CascadeSource::PortC(id) => port_check(32, *id),
CascadeSource::PortD(id) => port_check(48, *id),
CascadeSource::PortE(id) => port_check(65, *id),
CascadeSource::Tim(id) => {
if *id > 23 {
return Err(InvalidCascadeSourceId);
}
Ok(80 + id)
}
CascadeSource::TxEv => Ok(104),
CascadeSource::AdcIrq => Ok(105),
CascadeSource::RomSbe => Ok(106),
CascadeSource::RomMbe => Ok(106),
CascadeSource::Ram0Sbe => Ok(108),
CascadeSource::Ram0Mbe => Ok(109),
CascadeSource::Ram1Sbe => Ok(110),
CascadeSource::Ram2Mbe => Ok(111),
CascadeSource::WdogIrq => Ok(112),
}
}
}
//==================================================================================================
// 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 ID: u8;
const IRQ: pac::Interrupt;
fn clock(clocks: &Clocks) -> Hertz {
if Self::ID <= 15 {
clocks.apb1()
} else {
clocks.apb2()
}
}
}
macro_rules! tim_markers {
(
$(
($TimX:path, $id:expr, $Irq:path),
)+
) => {
$(
impl ValidTim for $TimX {
const ID: u8 = $id;
const IRQ: pac::Interrupt = $Irq;
}
)+
};
}
pub const fn const_clock<Tim: ValidTim + ?Sized>(_: &Tim, clocks: &Clocks) -> Hertz {
if Tim::ID <= 15 {
clocks.apb1()
} else {
clocks.apb2()
}
}
tim_markers!(
(pac::Tim0, 0, pac::Interrupt::TIM0),
(pac::Tim1, 1, pac::Interrupt::TIM1),
(pac::Tim2, 2, pac::Interrupt::TIM2),
(pac::Tim3, 3, pac::Interrupt::TIM3),
(pac::Tim4, 4, pac::Interrupt::TIM4),
(pac::Tim5, 5, pac::Interrupt::TIM5),
(pac::Tim6, 6, pac::Interrupt::TIM6),
(pac::Tim7, 7, pac::Interrupt::TIM7),
(pac::Tim8, 8, pac::Interrupt::TIM8),
(pac::Tim9, 9, pac::Interrupt::TIM9),
(pac::Tim10, 10, pac::Interrupt::TIM10),
(pac::Tim11, 11, pac::Interrupt::TIM11),
(pac::Tim12, 12, pac::Interrupt::TIM12),
(pac::Tim13, 13, pac::Interrupt::TIM13),
(pac::Tim14, 14, pac::Interrupt::TIM14),
(pac::Tim15, 15, pac::Interrupt::TIM15),
(pac::Tim16, 16, pac::Interrupt::TIM16),
(pac::Tim17, 17, pac::Interrupt::TIM17),
(pac::Tim18, 18, pac::Interrupt::TIM18),
(pac::Tim19, 19, pac::Interrupt::TIM19),
(pac::Tim20, 20, pac::Interrupt::TIM20),
(pac::Tim21, 21, pac::Interrupt::TIM21),
(pac::Tim22, 22, pac::Interrupt::TIM22),
(pac::Tim23, 23, pac::Interrupt::TIM23),
);
pub trait ValidTimAndPin<Pin: TimPin, Tim: ValidTim>: Sealed {}
macro_rules! valid_pin_and_tims {
(
$(
($PinX:ident, $AltFunc:ident, $TimX:path $(, $meta: meta)?),
)+
) => {
$(
$(#[$meta])?
impl TimPin for Pin<$PinX, $AltFunc>
where
$PinX: PinId,
{
const DYN: DynPinId = $PinX::DYN;
}
$(#[$meta])?
impl<
PinInstance: TimPin,
Tim: ValidTim
> ValidTimAndPin<PinInstance, Tim> for (Pin<$PinX, $AltFunc>, $TimX)
where
Pin<$PinX, $AltFunc>: TimPin,
$PinX: PinId,
{
}
$(#[$meta])?
impl Sealed for (Pin<$PinX, $AltFunc>, $TimX) {}
)+
};
}
valid_pin_and_tims!(
(PA0, AltFunc1, pac::Tim0),
(PA1, AltFunc1, pac::Tim1),
(PA2, AltFunc1, pac::Tim2),
(PA3, AltFunc1, pac::Tim3),
(PA4, AltFunc1, pac::Tim4),
(PA5, AltFunc1, pac::Tim5),
(PA6, AltFunc1, pac::Tim6),
(PA7, AltFunc1, pac::Tim7),
(PA10, AltFunc2, pac::Tim23),
(PA11, AltFunc2, pac::Tim22),
(PA12, AltFunc2, pac::Tim21),
(PA13, AltFunc2, pac::Tim20),
(PA14, AltFunc2, pac::Tim19),
(PA15, AltFunc2, pac::Tim18),
(PB0, AltFunc2, pac::Tim17),
(PB1, AltFunc2, pac::Tim16),
(PB2, AltFunc2, pac::Tim15),
(PB3, AltFunc2, pac::Tim14),
(PB4, AltFunc2, pac::Tim13),
(PB5, AltFunc2, pac::Tim12, cfg(not(feature = "va41628"))),
(PB6, AltFunc2, pac::Tim11, cfg(not(feature = "va41628"))),
(PB7, AltFunc2, pac::Tim10, cfg(not(feature = "va41628"))),
(PB8, AltFunc2, pac::Tim9, cfg(not(feature = "va41628"))),
(PB9, AltFunc2, pac::Tim8, cfg(not(feature = "va41628"))),
(PB10, AltFunc2, pac::Tim7, cfg(not(feature = "va41628"))),
(PB11, AltFunc2, pac::Tim6, cfg(not(feature = "va41628"))),
(PB12, AltFunc2, pac::Tim5),
(PB13, AltFunc2, pac::Tim4),
(PB14, AltFunc2, pac::Tim3),
(PB15, AltFunc2, pac::Tim2),
(PC0, AltFunc2, pac::Tim1),
(PC1, AltFunc2, pac::Tim0),
(PD0, AltFunc2, pac::Tim0, cfg(not(feature = "va41628"))),
(PD1, AltFunc2, pac::Tim1, cfg(not(feature = "va41628"))),
(PD2, AltFunc2, pac::Tim2, cfg(not(feature = "va41628"))),
(PD3, AltFunc2, pac::Tim3, cfg(not(feature = "va41628"))),
(PD4, AltFunc2, pac::Tim4, cfg(not(feature = "va41628"))),
(PD5, AltFunc2, pac::Tim5, cfg(not(feature = "va41628"))),
(PD6, AltFunc2, pac::Tim6, cfg(not(feature = "va41628"))),
(PD7, AltFunc2, pac::Tim7, cfg(not(feature = "va41628"))),
(PD8, AltFunc2, pac::Tim8, cfg(not(feature = "va41628"))),
(PD9, AltFunc2, pac::Tim9, cfg(not(feature = "va41628"))),
(PD10, AltFunc2, pac::Tim10),
(PD11, AltFunc2, pac::Tim11),
(PD12, AltFunc2, pac::Tim12),
(PD13, AltFunc2, pac::Tim13),
(PD14, AltFunc2, pac::Tim14),
(PD15, AltFunc2, pac::Tim15),
(PE0, AltFunc2, pac::Tim16),
(PE1, AltFunc2, pac::Tim17),
(PE2, AltFunc2, pac::Tim18),
(PE3, AltFunc2, pac::Tim19),
(PE4, AltFunc2, pac::Tim20),
(PE5, AltFunc2, pac::Tim21),
(PE6, AltFunc2, pac::Tim22),
(PE7, AltFunc2, pac::Tim23),
(PE8, AltFunc3, pac::Tim16),
(PE9, AltFunc3, pac::Tim17),
(PE10, AltFunc3, pac::Tim18, cfg(not(feature = "va41628"))),
(PE11, AltFunc3, pac::Tim19, cfg(not(feature = "va41628"))),
(PE12, AltFunc3, pac::Tim20),
(PE13, AltFunc3, pac::Tim21),
(PE14, AltFunc3, pac::Tim22),
(PE15, AltFunc3, pac::Tim23),
(PF0, AltFunc3, pac::Tim0),
(PF1, AltFunc3, pac::Tim1),
(PF2, AltFunc3, pac::Tim2, cfg(not(feature = "va41628"))),
(PF3, AltFunc3, pac::Tim3, cfg(not(feature = "va41628"))),
(PF4, AltFunc3, pac::Tim4, cfg(not(feature = "va41628"))),
(PF5, AltFunc3, pac::Tim5, cfg(not(feature = "va41628"))),
(PF6, AltFunc3, pac::Tim6, cfg(not(feature = "va41628"))),
(PF7, AltFunc3, pac::Tim7, cfg(not(feature = "va41628"))),
(PF8, AltFunc3, pac::Tim8, cfg(not(feature = "va41628"))),
(PF9, AltFunc3, pac::Tim9),
(PF10, AltFunc3, pac::Tim10, cfg(not(feature = "va41628"))),
(PF11, AltFunc3, pac::Tim11),
(PF12, AltFunc3, pac::Tim12),
(PF13, AltFunc2, pac::Tim19),
(PF14, AltFunc2, pac::Tim20),
(PF15, AltFunc2, pac::Tim21),
(PG0, AltFunc2, pac::Tim22),
(PG1, AltFunc2, pac::Tim23),
(PG2, AltFunc1, pac::Tim9),
(PG3, AltFunc1, pac::Tim10),
(PG6, AltFunc1, pac::Tim12),
);
//==================================================================================================
// Register Interface for TIM registers and TIM pins
//==================================================================================================
/// Clear the reset bit of the TIM, holding it in reset
///
/// # Safety
///
/// Only the bit related to the corresponding TIM peripheral is modified
#[inline]
pub fn assert_tim_reset(syscfg: &mut pac::Sysconfig, tim_id: u8) {
syscfg
.tim_reset()
.modify(|r, w| unsafe { w.bits(r.bits() & !(1 << tim_id as u32)) });
}
#[inline]
pub fn deassert_tim_reset(syscfg: &mut pac::Sysconfig, tim_id: u8) {
syscfg
.tim_reset()
.modify(|r, w| unsafe { w.bits(r.bits() | (1 << tim_id as u32)) });
}
#[inline]
pub fn assert_tim_reset_for_two_cycles(syscfg: &mut pac::Sysconfig, tim_id: u8) {
assert_tim_reset(syscfg, tim_id);
asm::nop();
asm::nop();
deassert_tim_reset(syscfg, tim_id);
}
pub type TimRegBlock = pac::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 pac::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 assert_tim_reset(&self, syscfg: &mut pac::Sysconfig) {
assert_tim_reset(syscfg, self.tim_id());
}
#[inline]
#[allow(dead_code)]
fn deassert_time_reset(&self, syscfg: &mut pac::Sysconfig) {
deassert_tim_reset(syscfg, self.tim_id());
}
}
unsafe impl<Tim: ValidTim> TimRegInterface for Tim {
fn tim_id(&self) -> u8 {
Tim::ID
}
}
/// Provide a safe register interface for [`ValidTimAndPin`]s
///
/// This `struct` takes ownership of a [`ValidTimAndPin`] and provides an API to
/// access the corresponding registers.
pub(super) struct TimAndPinRegister<Pin: TimPin, Tim: ValidTim> {
pin: Pin,
tim: Tim,
}
pub(super) struct TimRegister<TIM: ValidTim> {
tim: TIM,
}
impl<TIM: ValidTim> TimRegister<TIM> {
#[inline]
pub(super) unsafe fn new(tim: TIM) -> Self {
TimRegister { tim }
}
pub(super) fn release(self) -> TIM {
self.tim
}
}
unsafe impl<Tim: ValidTim> TimRegInterface for TimRegister<Tim> {
#[inline(always)]
fn tim_id(&self) -> u8 {
Tim::ID
}
}
impl<Pin: TimPin, Tim: ValidTim> TimAndPinRegister<Pin, Tim>
where
(Pin, Tim): ValidTimAndPin<Pin, Tim>,
{
#[inline]
pub(super) unsafe fn new(pin: Pin, tim: Tim) -> Self {
TimAndPinRegister { pin, tim }
}
pub(super) fn release(self) -> (Pin, Tim) {
(self.pin, self.tim)
}
}
unsafe impl<Pin: TimPin, Tim: ValidTim> TimRegInterface for TimAndPinRegister<Pin, Tim> {
#[inline(always)]
fn tim_id(&self) -> u8 {
Tim::ID
}
}
pub(crate) struct TimDynRegister {
pub(crate) tim_id: u8,
#[allow(dead_code)]
pub(crate) pin_id: DynPinId,
}
impl<Pin: TimPin, Tim: ValidTim> From<TimAndPinRegister<Pin, Tim>> for TimDynRegister {
fn from(_reg: TimAndPinRegister<Pin, Tim>) -> Self {
Self {
tim_id: Tim::ID,
pin_id: Pin::DYN,
}
}
}
unsafe impl TimRegInterface for TimDynRegister {
#[inline(always)]
fn tim_id(&self) -> u8 {
self.tim_id
}
}
//==================================================================================================
// Timers
//==================================================================================================
/// Hardware timers.
///
/// These timers also implement the [embedded_hal::delay::DelayNs] trait and can be used to delay
/// with a higher resolution compared to the Cortex-M systick delays.
pub struct CountdownTimer<TIM: ValidTim> {
tim: TimRegister<TIM>,
curr_freq: Hertz,
clock: Hertz,
rst_val: u32,
last_cnt: u32,
listening: bool,
}
#[inline]
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)) });
}
unsafe impl<Tim: ValidTim> TimRegInterface for CountdownTimer<Tim> {
#[inline]
fn tim_id(&self) -> u8 {
Tim::ID
}
}
impl<Tim: ValidTim> CountdownTimer<Tim> {
/// Create a new countdown timer, but does not start it.
///
/// You can use [Self::start] to start the countdown timer, and you may optionally call
/// [Self::listen] to enable interrupts for the TIM peripheral as well.
pub fn new(syscfg: &mut pac::Sysconfig, tim: Tim, clocks: &Clocks) -> Self {
enable_tim_clk(syscfg, Tim::ID);
assert_tim_reset(syscfg, Tim::ID);
cortex_m::asm::nop();
cortex_m::asm::nop();
deassert_tim_reset(syscfg, Tim::ID);
CountdownTimer {
tim: unsafe { TimRegister::new(tim) },
clock: Tim::clock(clocks),
rst_val: 0,
curr_freq: 0_u32.Hz(),
listening: false,
last_cnt: 0,
}
}
#[inline]
pub fn start(&mut self, timeout: impl Into<Hertz>) {
self.load(timeout);
self.enable();
}
/// 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
#[inline]
pub fn listen(&mut self) {
self.listening = true;
self.enable_interrupt();
unsafe { enable_nvic_interrupt(Tim::IRQ) }
}
/// 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)
}
}
#[inline]
pub fn stop(&mut self) {
self.tim
.reg_block()
.ctrl()
.write(|w| w.enable().clear_bit());
}
#[inline]
pub fn unlisten(&mut self) {
self.listening = true;
self.disable_interrupt();
disable_nvic_interrupt(Tim::IRQ);
}
#[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());
}
#[inline]
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::ID)) });
self.tim.release()
}
/// 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.clock.raw() / self.curr_freq.raw()) - 1;
self.set_reload(self.rst_val);
// Decrementing counter, to set the reset value.
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) {
self.tim
.reg_block()
.enable()
.write(|w| unsafe { w.bits(1) });
}
#[inline(always)]
pub fn disable(&mut self) {
self.tim
.reg_block()
.ctrl()
.modify(|_, w| w.enable().clear_bit());
}
/// Disable the counter, setting both enable and active bit to 0
#[inline]
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
#[inline]
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
#[inline]
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)
});
}
#[inline]
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(())
}
#[inline]
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(())
}
#[inline]
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(())
}
#[inline]
pub fn curr_freq(&self) -> Hertz {
self.curr_freq
}
#[inline]
pub fn listening(&self) -> bool {
self.listening
}
}
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.clock.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();
}
}
//==================================================================================================
// MS tick implementations
//==================================================================================================
// 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>(
sys_cfg: &mut pac::Sysconfig,
tim: Tim,
clocks: &Clocks,
) -> CountdownTimer<Tim> {
let mut ms_timer = CountdownTimer::new(sys_cfg, tim, clocks);
ms_timer.listen();
ms_timer.start(1000.Hz());
ms_timer
}
/// 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())
}
pub struct DelayMs<Tim: ValidTim = pac::Tim0>(CountdownTimer<Tim>);
impl<Tim: ValidTim> DelayMs<Tim> {
pub fn new(timer: CountdownTimer<Tim>) -> 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 with [set_up_ms_tick]
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();
}
}
}

View 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

View File

@ -0,0 +1,448 @@
//! # 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 va416xx::uart0 as uart_base;
use crate::enable_nvic_interrupt;
use super::{Bank, Instance, Rx, UartErrors};
static UART_RX_WAKERS: [AtomicWaker; 3] = [const { AtomicWaker::new() }; 3];
static RX_READ_ACTIVE: [AtomicBool; 3] = [const { AtomicBool::new(false) }; 3];
static RX_HAS_DATA: [AtomicBool; 3] = [const { AtomicBool::new(false) }; 3];
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();
unsafe {
enable_nvic_interrupt(Uart::IRQ_RX);
}
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(|_| {
unsafe {
enable_nvic_interrupt(Uart::IRQ_RX);
}
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)
}
}

View File

@ -0,0 +1,263 @@
//! # Async UART transmission functionality for the VA416xx 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/va416xx-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; 3] = [const { AtomicWaker::new() }; 3];
static TX_CONTEXTS: [Mutex<RefCell<TxContext>>; 3] =
[const { Mutex::new(RefCell::new(TxContext::new())) }; 3];
// Completion flag. Kept outside of the context structure as an atomic to avoid
// critical section.
static TX_DONE: [AtomicBool; 3] = [const { AtomicBool::new(false) }; 3];
/// 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::Uart0::reg_block() },
1 => unsafe { pac::Uart1::reg_block() },
2 => unsafe { pac::Uart2::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> {
/// Create a new asynchronous TX object.
///
/// This function also enable the NVIC interrupt, but does not enable the peripheral specific
/// interrupts.
pub fn new(tx: Tx<Uart>) -> Self {
// Safety: We own TX now.
unsafe { enable_nvic_interrupt(Uart::IRQ_TX) };
Self { tx }
}
/// This function also disables the NVIC interrupt.
pub fn release(self) -> Tx<Uart> {
disable_nvic_interrupt(Uart::IRQ_TX);
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
}
}

View File

@ -3,12 +3,12 @@
//! ## Examples //! ## Examples
//! //!
//! - [Watchdog simple example](https://egit.irs.uni-stuttgart.de/rust/va416xx-rs/src/branch/main/examples/simple/examples/wdt.rs) //! - [Watchdog simple example](https://egit.irs.uni-stuttgart.de/rust/va416xx-rs/src/branch/main/examples/simple/examples/wdt.rs)
use vorago_shared_periphs::{
enable_peripheral_clock, reset_peripheral_for_cycles, PeripheralSelect,
};
use crate::time::Hertz; use crate::time::Hertz;
use crate::{clock::Clocks, pac}; use crate::{
clock::{Clocks, PeripheralSelect},
pac,
prelude::SyscfgExt,
};
use crate::{disable_nvic_interrupt, enable_nvic_interrupt}; use crate::{disable_nvic_interrupt, enable_nvic_interrupt};
pub const WDT_UNLOCK_VALUE: u32 = 0x1ACC_E551; pub const WDT_UNLOCK_VALUE: u32 = 0x1ACC_E551;
@ -39,13 +39,23 @@ pub fn disable_wdt_interrupts() {
} }
impl Wdt { impl Wdt {
pub fn new(wdt: pac::WatchDog, clocks: &Clocks, wdt_freq_ms: u32) -> Self { pub fn new(
Self::start(wdt, clocks, wdt_freq_ms) syscfg: &mut pac::Sysconfig,
wdt: pac::WatchDog,
clocks: &Clocks,
wdt_freq_ms: u32,
) -> Self {
Self::start(syscfg, wdt, clocks, wdt_freq_ms)
} }
pub fn start(wdt: pac::WatchDog, clocks: &Clocks, wdt_freq_ms: u32) -> Self { pub fn start(
enable_peripheral_clock(PeripheralSelect::Watchdog); syscfg: &mut pac::Sysconfig,
reset_peripheral_for_cycles(PeripheralSelect::Watchdog, 2); wdt: pac::WatchDog,
clocks: &Clocks,
wdt_freq_ms: u32,
) -> Self {
syscfg.enable_peripheral_clock(PeripheralSelect::Watchdog);
syscfg.assert_periph_reset_for_two_cycles(PeripheralSelect::Watchdog);
let wdt_clock = clocks.apb2(); let wdt_clock = clocks.apb2();
let mut wdt_ctrl = Self { let mut wdt_ctrl = Self {

View File

@ -11,10 +11,15 @@ keywords = ["no-std", "peb1", "cortex-m", "vorago", "va416xx"]
categories = ["embedded", "no-std", "hardware-support"] categories = ["embedded", "no-std", "hardware-support"]
[dependencies] [dependencies]
va416xx-hal = { version = ">=0.3, <=0.5", path = "../va416xx-hal", features = ["va41630"] } cortex-m = "0.7"
cortex-m-rt = "0.7"
embedded-hal = "1"
lis2dh12 = { version = "0.7", features = ["out_f32"] } lis2dh12 = { version = "0.7", features = ["out_f32"] }
va416xx-hal = { version = ">=0.3, <=0.5", features = ["va41630"] }
[features] [features]
rt = ["va416xx-hal/rt"]
[package.metadata.docs.rs] [package.metadata.docs.rs]
all-features = true all-features = true

View File

@ -19,7 +19,7 @@ pub mod accelerometer {
}; };
// Accelerometer located on the GPIO board. // Accelerometer located on the GPIO board.
pub type Accelerometer = Lis2dh12<I2cMaster>; pub type Accelerometer = Lis2dh12<I2cMaster<pac::I2c0>>;
#[derive(Debug)] #[derive(Debug)]
pub enum ConstructorError { pub enum ConstructorError {
@ -30,12 +30,14 @@ pub mod accelerometer {
pub fn new_with_addr_detection( pub fn new_with_addr_detection(
i2c: pac::I2c0, i2c: pac::I2c0,
sysconfig: &mut pac::Sysconfig,
clocks: &Clocks, clocks: &Clocks,
) -> Result<Accelerometer, ConstructorError> { ) -> Result<Accelerometer, ConstructorError> {
let mut i2c_master = I2cMaster::new( let mut i2c_master = I2cMaster::new(
i2c, i2c,
clocks, sysconfig,
MasterConfig::default(), MasterConfig::default(),
clocks,
I2cSpeed::Regular100khz, I2cSpeed::Regular100khz,
) )
.map_err(ConstructorError::ClkError)?; .map_err(ConstructorError::ClkError)?;
@ -45,7 +47,7 @@ pub mod accelerometer {
} }
pub fn new_with_i2cm( pub fn new_with_i2cm(
i2c: I2cMaster, i2c: I2cMaster<pac::I2c0>,
addr: lis2dh12::SlaveAddr, addr: lis2dh12::SlaveAddr,
) -> Result<Accelerometer, lis2dh12::Error<i2c::Error>> { ) -> Result<Accelerometer, lis2dh12::Error<i2c::Error>> {
Lis2dh12::new(i2c, addr) Lis2dh12::new(i2c, addr)