Rework library structure
Changed: - Move most library components to new [`vorago-shared-periphs`](https://egit.irs.uni-stuttgart.de/rust/vorago-shared-periphs) which is mostly re-exported in this crate. - Overhaul and simplification of several HAL APIs. The system configuration and IRQ router peripheral instance generally does not need to be passed to HAL API anymore. - All HAL drivers are now type erased. The constructors will still expect and consume the PAC singleton component for resource management purposes, but are not cached anymore. - Refactoring of GPIO library to be more inline with embassy GPIO API. Added: - I2C clock timeout feature support.
This commit is contained in:
parent
d641f3943f
commit
935ee9dbb1
@ -6,7 +6,6 @@ edition = "2021"
|
|||||||
[dependencies]
|
[dependencies]
|
||||||
cortex-m = "0.7"
|
cortex-m = "0.7"
|
||||||
cortex-m-rt = "0.7"
|
cortex-m-rt = "0.7"
|
||||||
embedded-hal = "1"
|
|
||||||
defmt-rtt = "0.4"
|
defmt-rtt = "0.4"
|
||||||
defmt = "1"
|
defmt = "1"
|
||||||
panic-probe = { version = "1", features = ["defmt"] }
|
panic-probe = { version = "1", features = ["defmt"] }
|
||||||
@ -16,3 +15,4 @@ static_assertions = "1"
|
|||||||
[dependencies.va416xx-hal]
|
[dependencies.va416xx-hal]
|
||||||
version = "0.5"
|
version = "0.5"
|
||||||
features = ["va41630", "defmt"]
|
features = ["va41630", "defmt"]
|
||||||
|
path = "../va416xx-hal"
|
||||||
|
@ -10,11 +10,10 @@ use crc::{Crc, CRC_32_ISO_HDLC};
|
|||||||
use defmt_rtt as _;
|
use defmt_rtt as _;
|
||||||
use panic_probe as _;
|
use panic_probe as _;
|
||||||
use va416xx_hal::{
|
use va416xx_hal::{
|
||||||
clock::{pll_setup_delay, ClkDivSel, ClkselSys},
|
clock::{pll_setup_delay, ClkDivSel, ClkselSys, ClockConfigurator},
|
||||||
edac,
|
edac,
|
||||||
nvm::Nvm,
|
nvm::Nvm,
|
||||||
pac::{self, interrupt},
|
pac::{self, interrupt},
|
||||||
prelude::*,
|
|
||||||
time::Hertz,
|
time::Hertz,
|
||||||
wdt::Wdt,
|
wdt::Wdt,
|
||||||
};
|
};
|
||||||
@ -104,23 +103,16 @@ 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 = dp
|
let clocks = ClockConfigurator::new(dp.clkgen)
|
||||||
.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(&mut dp.sysconfig)
|
.freeze()
|
||||||
.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(
|
opt_wdt.0 = Some(Wdt::start(dp.watch_dog, &clocks, WDT_FREQ_MS));
|
||||||
&mut dp.sysconfig,
|
|
||||||
dp.watch_dog,
|
|
||||||
&clocks,
|
|
||||||
WDT_FREQ_MS,
|
|
||||||
));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
let nvm = Nvm::new(&mut dp.sysconfig, dp.spi3, &clocks);
|
let nvm = Nvm::new(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];
|
||||||
|
@ -4,10 +4,8 @@ version = "0.1.0"
|
|||||||
edition = "2021"
|
edition = "2021"
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
cfg-if = "1"
|
|
||||||
cortex-m = { version = "0.7", features = ["critical-section-single-core"] }
|
|
||||||
cortex-m-rt = "0.7"
|
cortex-m-rt = "0.7"
|
||||||
embedded-hal = "1"
|
cfg-if = "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"
|
||||||
@ -18,7 +16,6 @@ defmt = "1"
|
|||||||
panic-probe = { version = "1", features = ["defmt"] }
|
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"
|
||||||
@ -29,11 +26,14 @@ embassy-executor = { version = "0.7", features = [
|
|||||||
"executor-interrupt"
|
"executor-interrupt"
|
||||||
]}
|
]}
|
||||||
|
|
||||||
va416xx-hal = { version = "0.5", features = ["defmt"] }
|
va416xx-hal = { version = "0.5", path = "../../va416xx-hal", features = ["defmt"] }
|
||||||
va416xx-embassy = { version = "0.1", default-features = false }
|
va416xx-embassy = { version = "0.1", path = "../../va416xx-embassy", default-features = false }
|
||||||
|
|
||||||
[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"]
|
||||||
|
@ -5,6 +5,7 @@
|
|||||||
//! [CHECK_XXX_TO_XXX] constants to true.
|
//! [CHECK_XXX_TO_XXX] constants to true.
|
||||||
#![no_std]
|
#![no_std]
|
||||||
#![no_main]
|
#![no_main]
|
||||||
|
|
||||||
// Import panic provider.
|
// Import panic provider.
|
||||||
use panic_probe as _;
|
use panic_probe as _;
|
||||||
// Import logger.
|
// Import logger.
|
||||||
@ -16,23 +17,19 @@ 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::ClkgenExt;
|
use va416xx_hal::clock::ClockConfigurator;
|
||||||
use va416xx_hal::gpio::{
|
use va416xx_hal::gpio::asynch::{on_interrupt_for_async_gpio_for_port, InputPinAsync};
|
||||||
on_interrupt_for_async_gpio_for_port, InputDynPinAsync, InputPinAsync, PinsB, PinsC, PinsD,
|
use va416xx_hal::gpio::{Input, Output, PinState, Port};
|
||||||
PinsE, PinsF, PinsG, Port,
|
use va416xx_hal::pac::{self, interrupt};
|
||||||
};
|
use va416xx_hal::pins::{PinsA, PinsB, PinsC, PinsD, PinsE, PinsF, PinsG};
|
||||||
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 = true;
|
const CHECK_PB0_TO_PB1: bool = false;
|
||||||
const CHECK_PC14_TO_PC15: bool = true;
|
const CHECK_PC14_TO_PC15: bool = false;
|
||||||
const CHECK_PD2_TO_PD3: bool = true;
|
const CHECK_PD2_TO_PD3: bool = false;
|
||||||
const CHECK_PE0_TO_PE1: bool = true;
|
const CHECK_PE0_TO_PE1: bool = false;
|
||||||
const CHECK_PF0_TO_PF1: bool = true;
|
const CHECK_PF0_TO_PF1: bool = false;
|
||||||
|
|
||||||
#[derive(Clone, Copy)]
|
#[derive(Clone, Copy)]
|
||||||
pub struct GpioCmd {
|
pub struct GpioCmd {
|
||||||
@ -70,41 +67,30 @@ static CHANNEL_PF0_TO_PF1: Channel<ThreadModeRawMutex, GpioCmd, 3> = Channel::ne
|
|||||||
async fn main(spawner: Spawner) {
|
async fn main(spawner: Spawner) {
|
||||||
defmt::println!("-- VA416xx Async GPIO Demo --");
|
defmt::println!("-- VA416xx Async GPIO Demo --");
|
||||||
|
|
||||||
let mut dp = pac::Peripherals::take().unwrap();
|
let 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 = dp
|
let clocks = ClockConfigurator::new(dp.clkgen)
|
||||||
.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(&mut dp.sysconfig)
|
.freeze()
|
||||||
.unwrap();
|
.unwrap();
|
||||||
// Safety: Only called once here.
|
// Safety: Only called once here.
|
||||||
unsafe {
|
va416xx_embassy::init(dp.tim15, dp.tim14, &clocks);
|
||||||
va416xx_embassy::init(
|
|
||||||
&mut dp.sysconfig,
|
|
||||||
&dp.irq_router,
|
|
||||||
dp.tim15,
|
|
||||||
dp.tim14,
|
|
||||||
&clocks,
|
|
||||||
)
|
|
||||||
};
|
|
||||||
|
|
||||||
let porta = PinsA::new(&mut dp.sysconfig, dp.porta);
|
let porta = PinsA::new(dp.porta);
|
||||||
let portb = PinsB::new(&mut dp.sysconfig, dp.portb);
|
let portb = PinsB::new(dp.portb);
|
||||||
let portc = PinsC::new(&mut dp.sysconfig, dp.portc);
|
let portc = PinsC::new(dp.portc);
|
||||||
let portd = PinsD::new(&mut dp.sysconfig, dp.portd);
|
let portd = PinsD::new(dp.portd);
|
||||||
let porte = PinsE::new(&mut dp.sysconfig, dp.porte);
|
let porte = PinsE::new(dp.porte);
|
||||||
let portf = PinsF::new(&mut dp.sysconfig, dp.portf);
|
let portf = PinsF::new(dp.portf);
|
||||||
|
|
||||||
let portg = PinsG::new(&mut dp.sysconfig, dp.portg);
|
let portg = PinsG::new(dp.portg);
|
||||||
let mut led = portg.pg5.into_readable_push_pull_output();
|
let mut led = Output::new(portg.pg5, PinState::Low);
|
||||||
|
|
||||||
if CHECK_PA0_TO_PA1 {
|
if CHECK_PA0_TO_PA1 {
|
||||||
let out_pin = porta.pa0.into_readable_push_pull_output();
|
let out_pin = Output::new(porta.pa0, PinState::Low);
|
||||||
let in_pin = porta.pa1.into_floating_input();
|
let in_pin = Input::new_floating(porta.pa1);
|
||||||
let out_pin = out_pin.downgrade();
|
|
||||||
let in_pin = InputPinAsync::new(in_pin).unwrap();
|
let in_pin = InputPinAsync::new(in_pin).unwrap();
|
||||||
|
|
||||||
spawner
|
spawner
|
||||||
@ -119,10 +105,9 @@ async fn main(spawner: Spawner) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if CHECK_PB0_TO_PB1 {
|
if CHECK_PB0_TO_PB1 {
|
||||||
let out_pin = portb.pb0.into_readable_push_pull_output();
|
let out_pin = Output::new(portb.pb0, PinState::Low);
|
||||||
let in_pin = portb.pb1.into_floating_input();
|
let in_pin = Input::new_floating(portb.pb1);
|
||||||
let out_pin = out_pin.downgrade();
|
let in_pin = InputPinAsync::new(in_pin).unwrap();
|
||||||
let in_pin = InputDynPinAsync::new(in_pin.downgrade()).unwrap();
|
|
||||||
|
|
||||||
spawner
|
spawner
|
||||||
.spawn(output_task(
|
.spawn(output_task(
|
||||||
@ -136,10 +121,9 @@ async fn main(spawner: Spawner) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if CHECK_PC14_TO_PC15 {
|
if CHECK_PC14_TO_PC15 {
|
||||||
let out_pin = portc.pc14.into_readable_push_pull_output();
|
let out_pin = Output::new(portc.pc14, PinState::Low);
|
||||||
let in_pin = portc.pc15.into_floating_input();
|
let in_pin = Input::new_floating(portc.pc15);
|
||||||
let out_pin = out_pin.downgrade();
|
let in_pin = InputPinAsync::new(in_pin).unwrap();
|
||||||
let in_pin = InputDynPinAsync::new(in_pin.downgrade()).unwrap();
|
|
||||||
spawner
|
spawner
|
||||||
.spawn(output_task(
|
.spawn(output_task(
|
||||||
"PC14 to PC15",
|
"PC14 to PC15",
|
||||||
@ -152,10 +136,9 @@ async fn main(spawner: Spawner) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if CHECK_PD2_TO_PD3 {
|
if CHECK_PD2_TO_PD3 {
|
||||||
let out_pin = portd.pd2.into_readable_push_pull_output();
|
let out_pin = Output::new(portd.pd2, PinState::Low);
|
||||||
let in_pin = portd.pd3.into_floating_input();
|
let in_pin = Input::new_floating(portd.pd3);
|
||||||
let out_pin = out_pin.downgrade();
|
let in_pin = InputPinAsync::new(in_pin).unwrap();
|
||||||
let in_pin = InputDynPinAsync::new(in_pin.downgrade()).unwrap();
|
|
||||||
spawner
|
spawner
|
||||||
.spawn(output_task(
|
.spawn(output_task(
|
||||||
"PD2 to PD3",
|
"PD2 to PD3",
|
||||||
@ -168,10 +151,9 @@ async fn main(spawner: Spawner) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if CHECK_PE0_TO_PE1 {
|
if CHECK_PE0_TO_PE1 {
|
||||||
let out_pin = porte.pe0.into_readable_push_pull_output();
|
let out_pin = Output::new(porte.pe0, PinState::Low);
|
||||||
let in_pin = porte.pe1.into_floating_input();
|
let in_pin = Input::new_floating(porte.pe1);
|
||||||
let out_pin = out_pin.downgrade();
|
let in_pin = InputPinAsync::new(in_pin).unwrap();
|
||||||
let in_pin = InputDynPinAsync::new(in_pin.downgrade()).unwrap();
|
|
||||||
spawner
|
spawner
|
||||||
.spawn(output_task(
|
.spawn(output_task(
|
||||||
"PE0 to PE1",
|
"PE0 to PE1",
|
||||||
@ -184,10 +166,9 @@ async fn main(spawner: Spawner) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if CHECK_PF0_TO_PF1 {
|
if CHECK_PF0_TO_PF1 {
|
||||||
let out_pin = portf.pf0.into_readable_push_pull_output();
|
let out_pin = Output::new(portf.pf0, PinState::Low);
|
||||||
let in_pin = portf.pf1.into_floating_input();
|
let in_pin = Input::new_floating(portf.pf1);
|
||||||
let out_pin = out_pin.downgrade();
|
let in_pin = InputPinAsync::new(in_pin).unwrap();
|
||||||
let in_pin = InputDynPinAsync::new(in_pin.downgrade()).unwrap();
|
|
||||||
spawner
|
spawner
|
||||||
.spawn(output_task(
|
.spawn(output_task(
|
||||||
"PF0 to PF1",
|
"PF0 to PF1",
|
||||||
@ -298,7 +279,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: DynPin,
|
mut out: Output,
|
||||||
receiver: Receiver<'static, ThreadModeRawMutex, GpioCmd, 3>,
|
receiver: Receiver<'static, ThreadModeRawMutex, GpioCmd, 3>,
|
||||||
) {
|
) {
|
||||||
loop {
|
loop {
|
||||||
@ -307,25 +288,25 @@ async fn output_task(
|
|||||||
match next_cmd.cmd_type {
|
match next_cmd.cmd_type {
|
||||||
GpioCmdType::SetHigh => {
|
GpioCmdType::SetHigh => {
|
||||||
defmt::info!("{}: Set output high", ctx);
|
defmt::info!("{}: Set output high", ctx);
|
||||||
out.set_high().unwrap();
|
out.set_high();
|
||||||
}
|
}
|
||||||
GpioCmdType::SetLow => {
|
GpioCmdType::SetLow => {
|
||||||
defmt::info!("{}: Set output low", ctx);
|
defmt::info!("{}: Set output low", ctx);
|
||||||
out.set_low().unwrap();
|
out.set_low();
|
||||||
}
|
}
|
||||||
GpioCmdType::RisingEdge => {
|
GpioCmdType::RisingEdge => {
|
||||||
defmt::info!("{}: Rising edge", ctx);
|
defmt::info!("{}: Rising edge", ctx);
|
||||||
if !out.is_low().unwrap() {
|
if !out.is_set_low() {
|
||||||
out.set_low().unwrap();
|
out.set_low();
|
||||||
}
|
}
|
||||||
out.set_high().unwrap();
|
out.set_high();
|
||||||
}
|
}
|
||||||
GpioCmdType::FallingEdge => {
|
GpioCmdType::FallingEdge => {
|
||||||
defmt::info!("{}: Falling edge", ctx);
|
defmt::info!("{}: Falling edge", ctx);
|
||||||
if !out.is_high().unwrap() {
|
if !out.is_set_high() {
|
||||||
out.set_high().unwrap();
|
out.set_high();
|
||||||
}
|
}
|
||||||
out.set_low().unwrap();
|
out.set_low();
|
||||||
}
|
}
|
||||||
GpioCmdType::CloseTask => {
|
GpioCmdType::CloseTask => {
|
||||||
defmt::info!("{}: Closing task", ctx);
|
defmt::info!("{}: Closing task", ctx);
|
||||||
|
@ -27,8 +27,10 @@ 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 va416xx_hal::{
|
use va416xx_hal::{
|
||||||
gpio::PinsG,
|
clock::ClockConfigurator,
|
||||||
|
gpio::{Output, PinState},
|
||||||
pac::{self, interrupt},
|
pac::{self, interrupt},
|
||||||
|
pins::PinsG,
|
||||||
prelude::*,
|
prelude::*,
|
||||||
time::Hertz,
|
time::Hertz,
|
||||||
uart::{
|
uart::{
|
||||||
@ -46,34 +48,22 @@ static PRODUCER_UART_A: Mutex<RefCell<Option<Producer<u8, 256>>>> = Mutex::new(R
|
|||||||
async fn main(_spawner: Spawner) {
|
async fn main(_spawner: Spawner) {
|
||||||
defmt::println!("-- VA108xx Async UART RX Demo --");
|
defmt::println!("-- VA108xx Async UART RX Demo --");
|
||||||
|
|
||||||
let mut dp = pac::Peripherals::take().unwrap();
|
let 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 = dp
|
let clocks = ClockConfigurator::new(dp.clkgen)
|
||||||
.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(&mut dp.sysconfig)
|
.freeze()
|
||||||
.unwrap();
|
.unwrap();
|
||||||
// Safety: Only called once here.
|
// Safety: Only called once here.
|
||||||
unsafe {
|
va416xx_embassy::init(dp.tim15, dp.tim14, &clocks);
|
||||||
va416xx_embassy::init(
|
|
||||||
&mut dp.sysconfig,
|
|
||||||
&dp.irq_router,
|
|
||||||
dp.tim15,
|
|
||||||
dp.tim14,
|
|
||||||
&clocks,
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
let portg = PinsG::new(&mut dp.sysconfig, dp.portg);
|
let portg = PinsG::new(dp.portg);
|
||||||
let mut led = portg.pg5.into_readable_push_pull_output();
|
let mut led = Output::new(portg.pg5, PinState::Low);
|
||||||
|
|
||||||
let tx = portg.pg0.into_funsel_1();
|
let uarta =
|
||||||
let rx = portg.pg1.into_funsel_1();
|
uart::Uart::new(dp.uart0, portg.pg0, portg.pg1, &clocks, 115200.Hz().into()).unwrap();
|
||||||
|
|
||||||
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();
|
||||||
|
@ -21,8 +21,10 @@ 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 va416xx_hal::{
|
use va416xx_hal::{
|
||||||
gpio::PinsG,
|
clock::ClockConfigurator,
|
||||||
|
gpio::{Output, PinState},
|
||||||
pac::{self, interrupt},
|
pac::{self, interrupt},
|
||||||
|
pins::PinsG,
|
||||||
prelude::*,
|
prelude::*,
|
||||||
time::Hertz,
|
time::Hertz,
|
||||||
uart::{
|
uart::{
|
||||||
@ -44,34 +46,22 @@ const STR_LIST: &[&str] = &[
|
|||||||
async fn main(_spawner: Spawner) {
|
async fn main(_spawner: Spawner) {
|
||||||
defmt::println!("-- VA108xx Async UART TX Demo --");
|
defmt::println!("-- VA108xx Async UART TX Demo --");
|
||||||
|
|
||||||
let mut dp = pac::Peripherals::take().unwrap();
|
let 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 = dp
|
let clocks = ClockConfigurator::new(dp.clkgen)
|
||||||
.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(&mut dp.sysconfig)
|
.freeze()
|
||||||
.unwrap();
|
.unwrap();
|
||||||
// Safety: Only called once here.
|
// Safety: Only called once here.
|
||||||
unsafe {
|
va416xx_embassy::init(dp.tim15, dp.tim14, &clocks);
|
||||||
va416xx_embassy::init(
|
|
||||||
&mut dp.sysconfig,
|
|
||||||
&dp.irq_router,
|
|
||||||
dp.tim15,
|
|
||||||
dp.tim14,
|
|
||||||
&clocks,
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
let portg = PinsG::new(&mut dp.sysconfig, dp.portg);
|
let pinsg = PinsG::new(dp.portg);
|
||||||
let mut led = portg.pg5.into_readable_push_pull_output();
|
let mut led = Output::new(pinsg.pg5, PinState::Low);
|
||||||
|
|
||||||
let tx = portg.pg0.into_funsel_1();
|
let uarta =
|
||||||
let rx = portg.pg1.into_funsel_1();
|
uart::Uart::new(dp.uart0, pinsg.pg0, pinsg.pg1, &clocks, 115200.Hz().into()).unwrap();
|
||||||
|
|
||||||
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));
|
||||||
|
@ -27,15 +27,15 @@ use ringbuf::{
|
|||||||
StaticRb,
|
StaticRb,
|
||||||
};
|
};
|
||||||
use va416xx_hal::{
|
use va416xx_hal::{
|
||||||
gpio::{OutputReadablePushPull, Pin, PinsG, PG5},
|
clock::ClockConfigurator,
|
||||||
|
gpio::{Output, PinState},
|
||||||
pac::{self, interrupt},
|
pac::{self, interrupt},
|
||||||
prelude::*,
|
pins::PinsG,
|
||||||
time::Hertz,
|
time::Hertz,
|
||||||
uart,
|
uart,
|
||||||
};
|
};
|
||||||
|
|
||||||
pub type SharedUart =
|
pub type SharedUart = Mutex<CriticalSectionRawMutex, RefCell<Option<uart::RxWithInterrupt>>>;
|
||||||
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;
|
||||||
@ -54,39 +54,27 @@ static RINGBUF: SharedRingBuf = Mutex::new(RefCell::new(None));
|
|||||||
async fn main(spawner: Spawner) {
|
async fn main(spawner: Spawner) {
|
||||||
defmt::println!("VA416xx UART-Embassy Example");
|
defmt::println!("VA416xx UART-Embassy Example");
|
||||||
|
|
||||||
let mut dp = pac::Peripherals::take().unwrap();
|
let 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 = dp
|
let clocks = ClockConfigurator::new(dp.clkgen)
|
||||||
.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(&mut dp.sysconfig)
|
.freeze()
|
||||||
.unwrap();
|
.unwrap();
|
||||||
// Safety: Only called once here.
|
// Safety: Only called once here.
|
||||||
unsafe {
|
va416xx_embassy::init(dp.tim15, dp.tim14, &clocks);
|
||||||
va416xx_embassy::init(
|
|
||||||
&mut dp.sysconfig,
|
|
||||||
&dp.irq_router,
|
|
||||||
dp.tim15,
|
|
||||||
dp.tim14,
|
|
||||||
&clocks,
|
|
||||||
)
|
|
||||||
};
|
|
||||||
|
|
||||||
let portg = PinsG::new(&mut dp.sysconfig, dp.portg);
|
let portg = PinsG::new(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,
|
||||||
(tx, rx),
|
portg.pg0,
|
||||||
Hertz::from_raw(BAUDRATE),
|
portg.pg1,
|
||||||
&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();
|
||||||
@ -97,7 +85,7 @@ async fn main(spawner: Spawner) {
|
|||||||
static_rb.borrow_mut().replace(StaticRb::default());
|
static_rb.borrow_mut().replace(StaticRb::default());
|
||||||
});
|
});
|
||||||
|
|
||||||
let led = portg.pg5.into_readable_push_pull_output();
|
let led = Output::new(portg.pg5, PinState::Low);
|
||||||
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;
|
||||||
@ -117,7 +105,7 @@ async fn main(spawner: Spawner) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
#[embassy_executor::task]
|
#[embassy_executor::task]
|
||||||
async fn blinky(mut led: Pin<PG5, OutputReadablePushPull>) {
|
async fn blinky(mut led: Output) {
|
||||||
let mut ticker = Ticker::every(Duration::from_millis(500));
|
let mut ticker = Ticker::every(Duration::from_millis(500));
|
||||||
loop {
|
loop {
|
||||||
led.toggle();
|
led.toggle();
|
||||||
|
@ -8,7 +8,13 @@ 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::{gpio::PinsG, pac, prelude::*, time::Hertz};
|
use va416xx_hal::{
|
||||||
|
clock::ClockConfigurator,
|
||||||
|
gpio::{Output, PinState},
|
||||||
|
pac,
|
||||||
|
pins::PinsG,
|
||||||
|
time::Hertz,
|
||||||
|
};
|
||||||
|
|
||||||
cfg_if::cfg_if! {
|
cfg_if::cfg_if! {
|
||||||
if #[cfg(feature = "custom-irqs")] {
|
if #[cfg(feature = "custom-irqs")] {
|
||||||
@ -22,40 +28,32 @@ cfg_if::cfg_if! {
|
|||||||
async fn main(_spawner: Spawner) {
|
async fn main(_spawner: Spawner) {
|
||||||
defmt::println!("VA416xx Embassy Demo");
|
defmt::println!("VA416xx Embassy Demo");
|
||||||
|
|
||||||
let mut dp = pac::Peripherals::take().unwrap();
|
let 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 = dp
|
let clocks = ClockConfigurator::new(dp.clkgen)
|
||||||
.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(&mut dp.sysconfig)
|
.freeze()
|
||||||
.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(
|
dp.tim15,
|
||||||
&mut dp.sysconfig,
|
dp.tim14,
|
||||||
&dp.irq_router,
|
&clocks
|
||||||
dp.tim15,
|
);
|
||||||
dp.tim14,
|
} else {
|
||||||
&clocks
|
va416xx_embassy::init(
|
||||||
);
|
dp.tim12,
|
||||||
} else {
|
dp.tim11,
|
||||||
va416xx_embassy::init(
|
&clocks
|
||||||
&mut dp.sysconfig,
|
);
|
||||||
&dp.irq_router,
|
|
||||||
dp.tim12,
|
|
||||||
dp.tim11,
|
|
||||||
&clocks
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
let portg = PinsG::new(&mut dp.sysconfig, dp.portg);
|
let pinsg = PinsG::new(dp.portg);
|
||||||
let mut led = portg.pg5.into_readable_push_pull_output();
|
let mut led = Output::new(pinsg.pg5, PinState::Low);
|
||||||
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;
|
||||||
|
@ -5,14 +5,11 @@ edition = "2021"
|
|||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
cortex-m = { version = "0.7", features = ["critical-section-single-core"] }
|
cortex-m = { version = "0.7", features = ["critical-section-single-core"] }
|
||||||
cortex-m-rt = "0.7"
|
|
||||||
embedded-hal = "1"
|
|
||||||
defmt-rtt = "0.4"
|
defmt-rtt = "0.4"
|
||||||
defmt = "1"
|
defmt = "1"
|
||||||
panic-probe = { version = "1", features = ["defmt"] }
|
panic-probe = { version = "1", features = ["defmt"] }
|
||||||
rtic-sync = { version = "1.3", features = ["defmt-03"] }
|
|
||||||
|
|
||||||
va416xx-hal = { version = "0.5", features = ["va41630"] }
|
va416xx-hal = { version = "0.5", path = "../../va416xx-hal", features = ["va41630"] }
|
||||||
|
|
||||||
[dependencies.rtic]
|
[dependencies.rtic]
|
||||||
version = "2"
|
version = "2"
|
||||||
|
@ -17,14 +17,15 @@ mod app {
|
|||||||
use rtic_monotonics::systick::prelude::*;
|
use rtic_monotonics::systick::prelude::*;
|
||||||
use rtic_monotonics::Monotonic;
|
use rtic_monotonics::Monotonic;
|
||||||
use va416xx_hal::{
|
use va416xx_hal::{
|
||||||
gpio::{OutputReadablePushPull, Pin, PinsG, PG5},
|
clock::ClockConfigurator,
|
||||||
|
gpio::{Output, PinState},
|
||||||
pac,
|
pac,
|
||||||
prelude::*,
|
pins::PinsG,
|
||||||
};
|
};
|
||||||
|
|
||||||
#[local]
|
#[local]
|
||||||
struct Local {
|
struct Local {
|
||||||
led: Pin<PG5, OutputReadablePushPull>,
|
led: Output,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[shared]
|
#[shared]
|
||||||
@ -33,19 +34,16 @@ mod app {
|
|||||||
rtic_monotonics::systick_monotonic!(Mono, 1_000);
|
rtic_monotonics::systick_monotonic!(Mono, 1_000);
|
||||||
|
|
||||||
#[init]
|
#[init]
|
||||||
fn init(mut cx: init::Context) -> (Shared, Local) {
|
fn init(cx: init::Context) -> (Shared, Local) {
|
||||||
defmt::println!("-- Vorago RTIC example application --");
|
defmt::println!("-- Vorago RTIC example application --");
|
||||||
// Use the external clock connected to XTAL_N.
|
// Use the external clock connected to XTAL_N.
|
||||||
let clocks = cx
|
let clocks = ClockConfigurator::new(cx.device.clkgen)
|
||||||
.device
|
|
||||||
.clkgen
|
|
||||||
.constrain()
|
|
||||||
.xtal_n_clk_with_src_freq(EXTCLK_FREQ)
|
.xtal_n_clk_with_src_freq(EXTCLK_FREQ)
|
||||||
.freeze(&mut cx.device.sysconfig)
|
.freeze()
|
||||||
.unwrap();
|
.unwrap();
|
||||||
Mono::start(cx.core.SYST, clocks.sysclk().raw());
|
Mono::start(cx.core.SYST, clocks.sysclk().raw());
|
||||||
let portg = PinsG::new(&mut cx.device.sysconfig, cx.device.portg);
|
let pinsg = PinsG::new(cx.device.portg);
|
||||||
let led = portg.pg5.into_readable_push_pull_output();
|
let led = Output::new(pinsg.pg5, PinState::Low);
|
||||||
blinky::spawn().ok();
|
blinky::spawn().ok();
|
||||||
(Shared {}, Local { led })
|
(Shared {}, Local { led })
|
||||||
}
|
}
|
||||||
|
@ -17,20 +17,12 @@ embedded-io = "0.6"
|
|||||||
panic-halt = "1"
|
panic-halt = "1"
|
||||||
accelerometer = "0.12"
|
accelerometer = "0.12"
|
||||||
|
|
||||||
va416xx-hal = { version = "0.5", features = ["va41630", "defmt"] }
|
va416xx-hal = { version = "0.5", path = "../../va416xx-hal", features = ["va41630", "defmt"] }
|
||||||
|
|
||||||
[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"]
|
||||||
|
@ -12,8 +12,8 @@ use embedded_hal::delay::DelayNs;
|
|||||||
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,
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -24,17 +24,15 @@ const ENABLE_BUF_PRINTOUT: bool = false;
|
|||||||
fn main() -> ! {
|
fn main() -> ! {
|
||||||
defmt::println!("VA416xx ADC example");
|
defmt::println!("VA416xx ADC example");
|
||||||
|
|
||||||
let mut dp = pac::Peripherals::take().unwrap();
|
let dp = pac::Peripherals::take().unwrap();
|
||||||
// Use the external clock connected to XTAL_N.
|
// Use the external clock connected to XTAL_N.
|
||||||
let clocks = dp
|
let clocks = ClockConfigurator::new(dp.clkgen)
|
||||||
.clkgen
|
|
||||||
.constrain()
|
|
||||||
.xtal_n_clk_with_src_freq(peb1::EXTCLK_FREQ)
|
.xtal_n_clk_with_src_freq(peb1::EXTCLK_FREQ)
|
||||||
.freeze(&mut dp.sysconfig)
|
.freeze()
|
||||||
.unwrap();
|
.unwrap();
|
||||||
|
|
||||||
let adc = Adc::new_with_channel_tag(&mut dp.sysconfig, dp.adc, &clocks);
|
let adc = Adc::new_with_channel_tag(dp.adc, &clocks);
|
||||||
let mut delay_provider = CountdownTimer::new(&mut dp.sysconfig, dp.tim0, &clocks);
|
let mut delay = CountdownTimer::new(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
|
||||||
@ -70,6 +68,6 @@ fn main() -> ! {
|
|||||||
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_provider.delay_ms(500);
|
delay.delay_ms(500);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -8,15 +8,19 @@ use panic_probe as _;
|
|||||||
use defmt_rtt as _;
|
use defmt_rtt as _;
|
||||||
|
|
||||||
use cortex_m_rt::entry;
|
use cortex_m_rt::entry;
|
||||||
use va416xx_hal::{gpio::PinsG, pac};
|
use va416xx_hal::{
|
||||||
|
gpio::{Output, PinState},
|
||||||
|
pac,
|
||||||
|
pins::PinsG,
|
||||||
|
};
|
||||||
|
|
||||||
#[entry]
|
#[entry]
|
||||||
fn main() -> ! {
|
fn main() -> ! {
|
||||||
defmt::println!("VA416xx HAL blinky example");
|
defmt::println!("VA416xx HAL blinky example");
|
||||||
|
|
||||||
let mut dp = pac::Peripherals::take().unwrap();
|
let dp = pac::Peripherals::take().unwrap();
|
||||||
let portg = PinsG::new(&mut dp.sysconfig, dp.portg);
|
let portg = PinsG::new(dp.portg);
|
||||||
let mut led = portg.pg5.into_readable_push_pull_output();
|
let mut led = Output::new(portg.pg5, PinState::Low);
|
||||||
loop {
|
loop {
|
||||||
cortex_m::asm::delay(2_000_000);
|
cortex_m::asm::delay(2_000_000);
|
||||||
led.toggle();
|
led.toggle();
|
||||||
|
@ -10,7 +10,7 @@ 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 simple_examples::peb1;
|
use simple_examples::peb1;
|
||||||
use va416xx_hal::{adc::Adc, dac::Dac, pac, prelude::*, timer::CountdownTimer};
|
use va416xx_hal::{adc::Adc, clock::ClockConfigurator, dac::Dac, pac, timer::CountdownTimer};
|
||||||
|
|
||||||
const DAC_INCREMENT: u16 = 256;
|
const DAC_INCREMENT: u16 = 256;
|
||||||
|
|
||||||
@ -30,18 +30,15 @@ const APP_MODE: AppMode = AppMode::DacAndAdc;
|
|||||||
fn main() -> ! {
|
fn main() -> ! {
|
||||||
defmt::println!("VA416xx DAC/ADC example");
|
defmt::println!("VA416xx DAC/ADC example");
|
||||||
|
|
||||||
let mut dp = pac::Peripherals::take().unwrap();
|
let dp = pac::Peripherals::take().unwrap();
|
||||||
// Use the external clock connected to XTAL_N.
|
// Use the external clock connected to XTAL_N.
|
||||||
let clocks = dp
|
let clocks = ClockConfigurator::new(dp.clkgen)
|
||||||
.clkgen
|
|
||||||
.constrain()
|
|
||||||
.xtal_n_clk_with_src_freq(peb1::EXTCLK_FREQ)
|
.xtal_n_clk_with_src_freq(peb1::EXTCLK_FREQ)
|
||||||
.freeze(&mut dp.sysconfig)
|
.freeze()
|
||||||
.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,
|
||||||
@ -49,12 +46,12 @@ 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(&mut dp.sysconfig, dp.adc, &clocks));
|
adc = Some(Adc::new(dp.adc, &clocks));
|
||||||
}
|
}
|
||||||
let mut delay_provider = CountdownTimer::new(&mut dp.sysconfig, dp.tim0, &clocks);
|
let mut delay_provider = CountdownTimer::new(dp.tim0, &clocks);
|
||||||
let mut current_val = 0;
|
let mut current_val = 0;
|
||||||
loop {
|
loop {
|
||||||
if let Some(dac) = &dac {
|
if let Some(dac) = &mut dac {
|
||||||
defmt::info!("loading DAC with value {}", current_val);
|
defmt::info!("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");
|
||||||
|
@ -6,6 +6,7 @@
|
|||||||
use panic_probe as _;
|
use panic_probe as _;
|
||||||
// Import logger.
|
// Import logger.
|
||||||
use defmt_rtt as _;
|
use defmt_rtt as _;
|
||||||
|
use va416xx_hal::clock::ClockConfigurator;
|
||||||
|
|
||||||
use core::cell::Cell;
|
use core::cell::Cell;
|
||||||
|
|
||||||
@ -15,11 +16,8 @@ use embedded_hal::delay::DelayNs;
|
|||||||
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));
|
||||||
@ -40,26 +38,23 @@ static mut DMA_DEST_BUF: [u16; 36] = [0; 36];
|
|||||||
fn main() -> ! {
|
fn main() -> ! {
|
||||||
defmt::println!("VA416xx DMA example");
|
defmt::println!("VA416xx DMA example");
|
||||||
|
|
||||||
let mut dp = pac::Peripherals::take().unwrap();
|
let dp = pac::Peripherals::take().unwrap();
|
||||||
// Use the external clock connected to XTAL_N.
|
// Use the external clock connected to XTAL_N.
|
||||||
let clocks = dp
|
let clocks = ClockConfigurator::new(dp.clkgen)
|
||||||
.clkgen
|
|
||||||
.constrain()
|
|
||||||
.xtal_n_clk_with_src_freq(peb1::EXTCLK_FREQ)
|
.xtal_n_clk_with_src_freq(peb1::EXTCLK_FREQ)
|
||||||
.freeze(&mut dp.sysconfig)
|
.freeze()
|
||||||
.unwrap();
|
.unwrap();
|
||||||
enable_and_init_irq_router(&mut dp.sysconfig, &dp.irq_router);
|
enable_and_init_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(&mut dp.sysconfig, dp.tim0, &clocks);
|
let mut delay_ms = CountdownTimer::new(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];
|
||||||
@ -90,7 +85,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_ms: &mut CountdownTimer<pac::Tim0>,
|
delay: &mut CountdownTimer,
|
||||||
) {
|
) {
|
||||||
(0..64).for_each(|i| {
|
(0..64).for_each(|i| {
|
||||||
src_buf[i] = i as u8;
|
src_buf[i] = i as u8;
|
||||||
@ -132,7 +127,7 @@ fn transfer_example_8_bit(
|
|||||||
defmt::info!("8-bit transfer done");
|
defmt::info!("8-bit transfer done");
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
delay_ms.delay_ms(1);
|
delay.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);
|
||||||
@ -142,7 +137,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<pac::Tim0>) {
|
fn transfer_example_16_bit(dma0: &mut DmaChannel, delay_ms: &mut CountdownTimer) {
|
||||||
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.
|
||||||
@ -207,7 +202,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<pac::Tim0>,
|
delay_ms: &mut CountdownTimer,
|
||||||
) {
|
) {
|
||||||
// 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| {
|
||||||
|
@ -12,10 +12,11 @@ use cortex_m_rt::entry;
|
|||||||
use embedded_hal::delay::DelayNs;
|
use embedded_hal::delay::DelayNs;
|
||||||
use simple_examples::peb1;
|
use simple_examples::peb1;
|
||||||
use va416xx_hal::{
|
use va416xx_hal::{
|
||||||
|
clock::ClockConfigurator,
|
||||||
i2c,
|
i2c,
|
||||||
pac::{self},
|
pac::{self},
|
||||||
prelude::*,
|
prelude::*,
|
||||||
pwm::CountdownTimer,
|
timer::CountdownTimer,
|
||||||
};
|
};
|
||||||
use vorago_peb1::lis2dh12::{self, detect_i2c_addr, FullScale, Odr};
|
use vorago_peb1::lis2dh12::{self, detect_i2c_addr, FullScale, Odr};
|
||||||
|
|
||||||
@ -31,21 +32,18 @@ fn main() -> ! {
|
|||||||
let mut dp = pac::Peripherals::take().unwrap();
|
let mut dp = pac::Peripherals::take().unwrap();
|
||||||
defmt::println!("-- Vorago PEB1 accelerometer example --");
|
defmt::println!("-- Vorago PEB1 accelerometer example --");
|
||||||
// Use the external clock connected to XTAL_N.
|
// Use the external clock connected to XTAL_N.
|
||||||
let clocks = dp
|
let clocks = ClockConfigurator::new(dp.clkgen)
|
||||||
.clkgen
|
|
||||||
.constrain()
|
|
||||||
.xtal_n_clk_with_src_freq(peb1::EXTCLK_FREQ)
|
.xtal_n_clk_with_src_freq(peb1::EXTCLK_FREQ)
|
||||||
.freeze(&mut dp.sysconfig)
|
.freeze()
|
||||||
.unwrap();
|
.unwrap();
|
||||||
let mut i2c_master = i2c::I2cMaster::new(
|
let mut i2c_master = i2c::I2cMaster::new(
|
||||||
dp.i2c0,
|
dp.i2c0,
|
||||||
&mut dp.sysconfig,
|
|
||||||
i2c::MasterConfig::default(),
|
|
||||||
&clocks,
|
&clocks,
|
||||||
|
i2c::MasterConfig::default(),
|
||||||
i2c::I2cSpeed::Regular100khz,
|
i2c::I2cSpeed::Regular100khz,
|
||||||
)
|
)
|
||||||
.expect("creating I2C master failed");
|
.expect("creating I2C master failed");
|
||||||
let mut delay_provider = CountdownTimer::new(&mut dp.sysconfig, dp.tim1, &clocks);
|
let mut delay_provider = CountdownTimer::new(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.
|
||||||
@ -53,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::reg::Mode::Normal)
|
.set_mode(lis2dh12::Mode::Normal)
|
||||||
.expect("setting mode failed");
|
.expect("setting mode failed");
|
||||||
accelerometer
|
accelerometer
|
||||||
.set_odr(Odr::Hz100)
|
.set_odr(Odr::Hz100)
|
||||||
@ -65,7 +63,7 @@ fn main() -> ! {
|
|||||||
accelerometer
|
accelerometer
|
||||||
.enable_temp(true)
|
.enable_temp(true)
|
||||||
.expect("enabling temperature sensor failed");
|
.expect("enabling temperature sensor failed");
|
||||||
rprintln!("Device ID: 0x{:02X}", device_id);
|
defmt::info!("Device ID: 0x{:02X}", device_id);
|
||||||
// Start reading the accelerometer periodically.
|
// Start reading the accelerometer periodically.
|
||||||
loop {
|
loop {
|
||||||
let temperature = accelerometer
|
let temperature = accelerometer
|
||||||
@ -76,13 +74,25 @@ fn main() -> ! {
|
|||||||
let value = accelerometer
|
let value = accelerometer
|
||||||
.accel_norm()
|
.accel_norm()
|
||||||
.expect("reading normalized accelerometer data failed");
|
.expect("reading normalized accelerometer data failed");
|
||||||
rprintln!("Accel Norm F32x3: {:.06?} | Temp {} °C", value, temperature);
|
defmt::info!(
|
||||||
|
"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");
|
||||||
rprintln!("Accel Raw F32x3: {:?} | Temp {} °C", value_raw, temperature);
|
defmt::info!(
|
||||||
|
"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);
|
||||||
|
@ -1,4 +1,6 @@
|
|||||||
//! Simple PWM example
|
//! Simple PWM example
|
||||||
|
//!
|
||||||
|
//! Outputs a PWM waveform on pin PG2.
|
||||||
#![no_main]
|
#![no_main]
|
||||||
#![no_std]
|
#![no_std]
|
||||||
|
|
||||||
@ -10,41 +12,34 @@ use panic_probe as _;
|
|||||||
use defmt_rtt as _;
|
use defmt_rtt as _;
|
||||||
use simple_examples::peb1;
|
use simple_examples::peb1;
|
||||||
use va416xx_hal::{
|
use va416xx_hal::{
|
||||||
gpio::PinsA,
|
clock::ClockConfigurator,
|
||||||
pac,
|
pac,
|
||||||
|
pins::PinsG,
|
||||||
prelude::*,
|
prelude::*,
|
||||||
pwm::{self, get_duty_from_percent, PwmA, PwmB, ReducedPwmPin},
|
pwm::{get_duty_from_percent, PwmA, PwmB, PwmPin},
|
||||||
timer::CountdownTimer,
|
timer::CountdownTimer,
|
||||||
};
|
};
|
||||||
|
|
||||||
#[entry]
|
#[entry]
|
||||||
fn main() -> ! {
|
fn main() -> ! {
|
||||||
defmt::println!("-- VA108xx PWM example application--");
|
defmt::println!("-- VA108xx PWM example application--");
|
||||||
let mut dp = pac::Peripherals::take().unwrap();
|
let dp = pac::Peripherals::take().unwrap();
|
||||||
|
|
||||||
// Use the external clock connected to XTAL_N.
|
// Use the external clock connected to XTAL_N.
|
||||||
let clocks = dp
|
let clocks = ClockConfigurator::new(dp.clkgen)
|
||||||
.clkgen
|
|
||||||
.constrain()
|
|
||||||
.xtal_n_clk_with_src_freq(peb1::EXTCLK_FREQ)
|
.xtal_n_clk_with_src_freq(peb1::EXTCLK_FREQ)
|
||||||
.freeze(&mut dp.sysconfig)
|
.freeze()
|
||||||
.unwrap();
|
.unwrap();
|
||||||
|
|
||||||
let pinsa = PinsA::new(&mut dp.sysconfig, dp.porta);
|
let pinsg = PinsG::new(dp.portg);
|
||||||
let mut pwm = pwm::PwmPin::new(
|
let mut pwm = PwmPin::new(pinsg.pg2, dp.tim9, &clocks, 10.Hz()).unwrap();
|
||||||
(pinsa.pa3.into_funsel_1(), dp.tim3),
|
let mut delay_timer = CountdownTimer::new(dp.tim0, &clocks);
|
||||||
&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
|
||||||
@ -56,8 +51,7 @@ fn main() -> ! {
|
|||||||
defmt::info!("current duty cycle: {}", current_duty_cycle);
|
defmt::info!("current duty cycle: {}", current_duty_cycle);
|
||||||
}
|
}
|
||||||
|
|
||||||
reduced_pin
|
pwm.set_duty_cycle(get_duty_from_percent(current_duty_cycle))
|
||||||
.set_duty_cycle(get_duty_from_percent(current_duty_cycle))
|
|
||||||
.unwrap();
|
.unwrap();
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -66,7 +60,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: ReducedPwmPin<PwmB> = ReducedPwmPin::from(reduced_pin);
|
let mut pwmb: PwmPin<PwmB> = PwmPin::from(pwm);
|
||||||
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 {
|
||||||
@ -78,6 +72,6 @@ fn main() -> ! {
|
|||||||
defmt::info!("Lower limit: {}", pwmb.pwmb_lower_limit());
|
defmt::info!("Lower limit: {}", pwmb.pwmb_lower_limit());
|
||||||
defmt::info!("Upper limit: {}", pwmb.pwmb_upper_limit());
|
defmt::info!("Upper limit: {}", pwmb.pwmb_upper_limit());
|
||||||
}
|
}
|
||||||
reduced_pin = ReducedPwmPin::<PwmA>::from(pwmb);
|
pwm = PwmPin::<PwmA>::from(pwmb);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,38 +0,0 @@
|
|||||||
// Code to test RTT logger functionality.
|
|
||||||
#![no_main]
|
|
||||||
#![no_std]
|
|
||||||
|
|
||||||
// Import panic provider.
|
|
||||||
use panic_probe as _;
|
|
||||||
// Import logger.
|
|
||||||
use defmt_rtt as _;
|
|
||||||
|
|
||||||
use cortex_m_rt::entry;
|
|
||||||
use va416xx_hal::pac;
|
|
||||||
|
|
||||||
// Mask for the LED
|
|
||||||
const LED_PG5: u32 = 1 << 5;
|
|
||||||
|
|
||||||
#[entry]
|
|
||||||
fn main() -> ! {
|
|
||||||
defmt::println!("VA416xx RTT Demo");
|
|
||||||
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) });
|
|
||||||
|
|
||||||
let mut counter = 0;
|
|
||||||
loop {
|
|
||||||
defmt::info!("{}: 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);
|
|
||||||
}
|
|
||||||
}
|
|
@ -3,6 +3,7 @@
|
|||||||
//! 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.
|
// Import panic provider.
|
||||||
use panic_probe as _;
|
use panic_probe as _;
|
||||||
// Import logger.
|
// Import logger.
|
||||||
@ -11,11 +12,12 @@ 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 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,
|
||||||
prelude::*,
|
pins::{PinsB, PinsC},
|
||||||
spi::SpiConfig,
|
spi::SpiConfig,
|
||||||
time::Hertz,
|
time::Hertz,
|
||||||
};
|
};
|
||||||
@ -37,29 +39,20 @@ const FILL_WORD: u8 = 0x0f;
|
|||||||
#[entry]
|
#[entry]
|
||||||
fn main() -> ! {
|
fn main() -> ! {
|
||||||
defmt::println!("-- VA108xx SPI example application--");
|
defmt::println!("-- VA108xx SPI example application--");
|
||||||
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 = dp
|
let clocks = ClockConfigurator::new(dp.clkgen)
|
||||||
.clkgen
|
|
||||||
.constrain()
|
|
||||||
.xtal_n_clk_with_src_freq(peb1::EXTCLK_FREQ)
|
.xtal_n_clk_with_src_freq(peb1::EXTCLK_FREQ)
|
||||||
.freeze(&mut dp.sysconfig)
|
.freeze()
|
||||||
.unwrap();
|
.unwrap();
|
||||||
let mut delay_sysclk = cortex_m::delay::Delay::new(cp.SYST, clocks.apb0().raw());
|
let mut delay = CountdownTimer::new(dp.tim1, &clocks);
|
||||||
|
|
||||||
let pins_b = PinsB::new(&mut dp.sysconfig, dp.portb);
|
let pins_b = PinsB::new(dp.portb);
|
||||||
let pins_c = PinsC::new(&mut dp.sysconfig, dp.portc);
|
let pins_c = PinsC::new(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_clk(Hertz::from_raw(SPI_SPEED_KHZ), &clocks)
|
SpiClkConfig::from_clks(&clocks, Hertz::from_raw(SPI_SPEED_KHZ))
|
||||||
.expect("invalid target clock"),
|
.expect("invalid target clock"),
|
||||||
)
|
)
|
||||||
.mode(SPI_MODE)
|
.mode(SPI_MODE)
|
||||||
@ -68,13 +61,7 @@ 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(
|
let mut spi0 = Spi::new(dp.spi0, (pins_b.pb15, pins_c.pc0, pins_c.pc1), spi_cfg).unwrap();
|
||||||
&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];
|
||||||
@ -95,6 +82,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_sysclk.delay_ms(500);
|
delay.delay_ms(500);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -6,16 +6,16 @@ use panic_probe as _;
|
|||||||
// Import logger.
|
// Import logger.
|
||||||
use defmt_rtt as _;
|
use defmt_rtt as _;
|
||||||
|
|
||||||
use core::cell::Cell;
|
use core::sync::atomic::{AtomicU32, Ordering};
|
||||||
use cortex_m::asm;
|
use cortex_m::asm;
|
||||||
use cortex_m_rt::entry;
|
use cortex_m_rt::entry;
|
||||||
use critical_section::Mutex;
|
|
||||||
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::{default_ms_irq_handler, set_up_ms_tick, CountdownTimer, MS_COUNTER},
|
timer::CountdownTimer,
|
||||||
};
|
};
|
||||||
|
|
||||||
#[allow(dead_code)]
|
#[allow(dead_code)]
|
||||||
@ -24,32 +24,33 @@ enum LibType {
|
|||||||
Hal,
|
Hal,
|
||||||
}
|
}
|
||||||
|
|
||||||
static SEC_COUNTER: Mutex<Cell<u32>> = Mutex::new(Cell::new(0));
|
static MS_COUNTER: AtomicU32 = AtomicU32::new(0);
|
||||||
|
static SEC_COUNTER: AtomicU32 = AtomicU32::new(0);
|
||||||
|
|
||||||
#[entry]
|
#[entry]
|
||||||
fn main() -> ! {
|
fn main() -> ! {
|
||||||
let mut dp = pac::Peripherals::take().unwrap();
|
let dp = pac::Peripherals::take().unwrap();
|
||||||
let mut last_ms = 0;
|
let mut last_ms = 0;
|
||||||
defmt::println!("-- Vorago system ticks using timers --");
|
defmt::println!("-- Vorago system ticks using timers --");
|
||||||
// Use the external clock connected to XTAL_N.
|
// Use the external clock connected to XTAL_N.
|
||||||
let clocks = dp
|
let clocks = ClockConfigurator::new(dp.clkgen)
|
||||||
.clkgen
|
|
||||||
.constrain()
|
|
||||||
.xtal_n_clk_with_src_freq(peb1::EXTCLK_FREQ)
|
.xtal_n_clk_with_src_freq(peb1::EXTCLK_FREQ)
|
||||||
.freeze(&mut dp.sysconfig)
|
.freeze()
|
||||||
.unwrap();
|
.unwrap();
|
||||||
enable_and_init_irq_router(&mut dp.sysconfig, &dp.irq_router);
|
enable_and_init_irq_router();
|
||||||
let _ = set_up_ms_tick(&mut dp.sysconfig, dp.tim0, &clocks);
|
let mut ms_timer = CountdownTimer::new(dp.tim0, &clocks);
|
||||||
let mut second_timer = CountdownTimer::new(&mut dp.sysconfig, dp.tim1, &clocks);
|
ms_timer.enable_interrupt(true);
|
||||||
second_timer.listen();
|
ms_timer.start(1.Hz());
|
||||||
|
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 = critical_section::with(|cs| MS_COUNTER.borrow(cs).get());
|
let current_ms = MS_COUNTER.load(Ordering::Relaxed);
|
||||||
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);
|
defmt::info!("MS counter: {}", current_ms);
|
||||||
let second = critical_section::with(|cs| SEC_COUNTER.borrow(cs).get());
|
let second = SEC_COUNTER.load(Ordering::Relaxed);
|
||||||
defmt::info!("Second counter: {}", second);
|
defmt::info!("Second counter: {}", second);
|
||||||
}
|
}
|
||||||
asm::delay(1000);
|
asm::delay(1000);
|
||||||
@ -59,15 +60,11 @@ fn main() -> ! {
|
|||||||
#[interrupt]
|
#[interrupt]
|
||||||
#[allow(non_snake_case)]
|
#[allow(non_snake_case)]
|
||||||
fn TIM0() {
|
fn TIM0() {
|
||||||
default_ms_irq_handler()
|
MS_COUNTER.fetch_add(1, Ordering::Relaxed);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[interrupt]
|
#[interrupt]
|
||||||
#[allow(non_snake_case)]
|
#[allow(non_snake_case)]
|
||||||
fn TIM1() {
|
fn TIM1() {
|
||||||
critical_section::with(|cs| {
|
SEC_COUNTER.fetch_add(1, Ordering::Relaxed);
|
||||||
let mut sec = SEC_COUNTER.borrow(cs).get();
|
|
||||||
sec += 1;
|
|
||||||
SEC_COUNTER.borrow(cs).set(sec);
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
@ -11,35 +11,33 @@ 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 simple_examples::peb1;
|
use simple_examples::peb1;
|
||||||
use va416xx_hal::clock::ClkgenExt;
|
use va416xx_hal::clock::ClockConfigurator;
|
||||||
|
use va416xx_hal::pins::PinsG;
|
||||||
use va416xx_hal::time::Hertz;
|
use va416xx_hal::time::Hertz;
|
||||||
use va416xx_hal::{gpio::PinsG, pac, uart};
|
use va416xx_hal::{pac, uart};
|
||||||
|
|
||||||
#[entry]
|
#[entry]
|
||||||
fn main() -> ! {
|
fn main() -> ! {
|
||||||
defmt::println!("-- VA416xx UART example application--");
|
defmt::println!("-- VA416xx UART example application--");
|
||||||
|
|
||||||
let mut dp = pac::Peripherals::take().unwrap();
|
let dp = pac::Peripherals::take().unwrap();
|
||||||
|
|
||||||
// Use the external clock connected to XTAL_N.
|
// Use the external clock connected to XTAL_N.
|
||||||
let clocks = dp
|
let clocks = ClockConfigurator::new(dp.clkgen)
|
||||||
.clkgen
|
|
||||||
.constrain()
|
|
||||||
.xtal_n_clk_with_src_freq(peb1::EXTCLK_FREQ)
|
.xtal_n_clk_with_src_freq(peb1::EXTCLK_FREQ)
|
||||||
.freeze(&mut dp.sysconfig)
|
.freeze()
|
||||||
.unwrap();
|
.unwrap();
|
||||||
|
|
||||||
let gpiob = PinsG::new(&mut dp.sysconfig, dp.portg);
|
let gpiog = PinsG::new(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,
|
||||||
(tx, rx),
|
gpiog.pg0,
|
||||||
Hertz::from_raw(115200),
|
gpiog.pg1,
|
||||||
&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 {
|
||||||
|
@ -5,17 +5,16 @@
|
|||||||
use panic_probe as _;
|
use panic_probe as _;
|
||||||
// Import logger.
|
// Import logger.
|
||||||
use defmt_rtt as _;
|
use defmt_rtt as _;
|
||||||
|
use va416xx_hal::clock::ClockConfigurator;
|
||||||
|
|
||||||
use core::cell::Cell;
|
use core::sync::atomic::{AtomicU32, Ordering};
|
||||||
use cortex_m_rt::entry;
|
use cortex_m_rt::entry;
|
||||||
use critical_section::Mutex;
|
|
||||||
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: Mutex<Cell<u32>> = Mutex::new(Cell::new(0));
|
static WDT_INTRPT_COUNT: AtomicU32 = AtomicU32::new(0);
|
||||||
|
|
||||||
#[derive(Debug, PartialEq, Eq)]
|
#[derive(Debug, PartialEq, Eq)]
|
||||||
#[allow(dead_code)]
|
#[allow(dead_code)]
|
||||||
@ -33,33 +32,37 @@ const WDT_ROLLOVER_MS: u32 = 100;
|
|||||||
fn main() -> ! {
|
fn main() -> ! {
|
||||||
defmt::println!("-- VA416xx WDT example application--");
|
defmt::println!("-- VA416xx WDT example application--");
|
||||||
let cp = cortex_m::Peripherals::take().unwrap();
|
let cp = cortex_m::Peripherals::take().unwrap();
|
||||||
let mut dp = pac::Peripherals::take().unwrap();
|
let dp = pac::Peripherals::take().unwrap();
|
||||||
|
|
||||||
// Use the external clock connected to XTAL_N.
|
// Use the external clock connected to XTAL_N.
|
||||||
let clocks = dp
|
let clocks = ClockConfigurator::new(dp.clkgen)
|
||||||
.clkgen
|
|
||||||
.constrain()
|
|
||||||
.xtal_n_clk_with_src_freq(peb1::EXTCLK_FREQ)
|
.xtal_n_clk_with_src_freq(peb1::EXTCLK_FREQ)
|
||||||
.freeze(&mut dp.sysconfig)
|
.freeze()
|
||||||
.unwrap();
|
.unwrap();
|
||||||
enable_and_init_irq_router(&mut dp.sysconfig, &dp.irq_router);
|
enable_and_init_irq_router();
|
||||||
let mut delay_sysclk = cortex_m::delay::Delay::new(cp.SYST, clocks.apb0().raw());
|
let mut delay = 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(&mut dp.sysconfig, dp.watch_dog, &clocks, WDT_ROLLOVER_MS);
|
let mut wdt_ctrl = Wdt::start(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 = critical_section::with(|cs| WDT_INTRPT_COUNT.borrow(cs).get());
|
let interrupt_counter = WDT_INTRPT_COUNT.load(Ordering::Relaxed);
|
||||||
if interrupt_counter > last_interrupt_counter {
|
if interrupt_counter > last_interrupt_counter {
|
||||||
defmt::info!("interrupt counter has increased to {}", interrupt_counter);
|
defmt::info!("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_sysclk.delay_ms(WDT_ROLLOVER_MS / 5),
|
TestMode::FedByMain => delay.delay_ms(WDT_ROLLOVER_MS / 5),
|
||||||
TestMode::FedByIrq => delay_sysclk.delay_ms(WDT_ROLLOVER_MS),
|
TestMode::FedByIrq => delay.delay_ms(WDT_ROLLOVER_MS),
|
||||||
_ => (),
|
_ => (),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -68,11 +71,7 @@ fn main() -> ! {
|
|||||||
#[interrupt]
|
#[interrupt]
|
||||||
#[allow(non_snake_case)]
|
#[allow(non_snake_case)]
|
||||||
fn WATCHDOG() {
|
fn WATCHDOG() {
|
||||||
critical_section::with(|cs| {
|
WDT_INTRPT_COUNT.fetch_add(1, Ordering::Relaxed);
|
||||||
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 {
|
||||||
|
@ -5,16 +5,10 @@ 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"
|
defmt-rtt = "0.4"
|
||||||
defmt = "1"
|
defmt = "1"
|
||||||
panic-probe = { version = "1", features = ["defmt"] }
|
panic-probe = { version = "1", features = ["defmt"] }
|
||||||
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 }
|
||||||
@ -22,7 +16,7 @@ once_cell = { version = "1", default-features = false, features = ["critical-sec
|
|||||||
spacepackets = { version = "0.13", default-features = false, features = ["defmt"] }
|
spacepackets = { version = "0.13", default-features = false, features = ["defmt"] }
|
||||||
cobs = { version = "0.3", default-features = false }
|
cobs = { version = "0.3", default-features = false }
|
||||||
|
|
||||||
va416xx-hal = { version = "0.5", features = ["va41630", "defmt"] }
|
va416xx-hal = { version = "0.5", features = ["va41630", "defmt"], path = "../va416xx-hal" }
|
||||||
|
|
||||||
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"] }
|
||||||
|
@ -99,7 +99,7 @@ class ImageLoader:
|
|||||||
)
|
)
|
||||||
self.verificator.add_tc(action_tc)
|
self.verificator.add_tc(action_tc)
|
||||||
self.com_if.send(bytes(action_tc.pack()))
|
self.com_if.send(bytes(action_tc.pack()))
|
||||||
self.await_for_command_copletion("boot image selection command")
|
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")
|
||||||
@ -112,7 +112,7 @@ class ImageLoader:
|
|||||||
)
|
)
|
||||||
self.verificator.add_tc(ping_tc)
|
self.verificator.add_tc(ping_tc)
|
||||||
self.com_if.send(bytes(ping_tc.pack()))
|
self.com_if.send(bytes(ping_tc.pack()))
|
||||||
self.await_for_command_copletion("ping command")
|
self.await_for_command_completion("ping command")
|
||||||
|
|
||||||
def handle_corruption_cmd(self, target: Target):
|
def handle_corruption_cmd(self, target: Target):
|
||||||
if target == Target.BOOTLOADER:
|
if target == Target.BOOTLOADER:
|
||||||
@ -134,11 +134,11 @@ class ImageLoader:
|
|||||||
),
|
),
|
||||||
)
|
)
|
||||||
|
|
||||||
def await_for_command_copletion(self, context: str):
|
def await_for_command_completion(self, context: str):
|
||||||
done = False
|
done = False
|
||||||
now = time.time()
|
now = time.time()
|
||||||
while time.time() - now < 2.0:
|
while time.time() - now < 2.0:
|
||||||
if not self.com_if.data_available():
|
if self.com_if.data_available() == 0:
|
||||||
time.sleep(0.2)
|
time.sleep(0.2)
|
||||||
continue
|
continue
|
||||||
for reply in self.com_if.receive():
|
for reply in self.com_if.receive():
|
||||||
|
@ -32,6 +32,7 @@ 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,
|
||||||
@ -105,14 +106,14 @@ mod app {
|
|||||||
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},
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -128,8 +129,8 @@ mod app {
|
|||||||
|
|
||||||
#[local]
|
#[local]
|
||||||
struct Local {
|
struct Local {
|
||||||
uart_rx: uart::RxWithInterrupt<pac::Uart0>,
|
uart_rx: uart::RxWithInterrupt,
|
||||||
uart_tx: uart::Tx<pac::Uart0>,
|
uart_tx: uart::Tx,
|
||||||
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.
|
||||||
@ -154,28 +155,24 @@ mod app {
|
|||||||
defmt::println!("-- Vorago flashloader --");
|
defmt::println!("-- 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 = cx
|
let clocks = ClockConfigurator::new(cx.device.clkgen)
|
||||||
.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(&mut cx.device.sysconfig)
|
.freeze()
|
||||||
.unwrap();
|
.unwrap();
|
||||||
|
|
||||||
enable_and_init_irq_router(&mut cx.device.sysconfig, &cx.device.irq_router);
|
enable_and_init_irq_router();
|
||||||
setup_edac(&mut cx.device.sysconfig);
|
setup_edac(&mut cx.device.sysconfig);
|
||||||
|
|
||||||
let gpiog = PinsG::new(&mut cx.device.sysconfig, cx.device.portg);
|
let gpiog = PinsG::new(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,
|
||||||
(tx, rx),
|
gpiog.pg0,
|
||||||
Hertz::from_raw(UART_BAUDRATE),
|
gpiog.pg1,
|
||||||
&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();
|
||||||
@ -259,8 +256,8 @@ mod app {
|
|||||||
{
|
{
|
||||||
Ok(result) => {
|
Ok(result) => {
|
||||||
if RX_DEBUGGING {
|
if RX_DEBUGGING {
|
||||||
log::debug!("RX Info: {:?}", cx.local.rx_context);
|
defmt::info!("RX Info: {:?}", cx.local.rx_context);
|
||||||
log::debug!("RX Result: {:?}", result);
|
defmt::info!("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.
|
||||||
@ -331,7 +328,7 @@ mod app {
|
|||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
let packet_len = packet_len.unwrap();
|
let packet_len = packet_len.unwrap();
|
||||||
log::info!(target: "TC Handler", "received packet with length {}", packet_len);
|
defmt::info!("received packet with length {}", packet_len);
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
cx.local
|
cx.local
|
||||||
.tc_cons
|
.tc_cons
|
||||||
@ -378,9 +375,7 @@ 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(),
|
||||||
);
|
);
|
||||||
@ -388,7 +383,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(&mut sys_cfg));
|
*cx.local.rom_spi = Some(nvm.release());
|
||||||
let tm = cx
|
let tm = cx
|
||||||
.local
|
.local
|
||||||
.verif_reporter
|
.verif_reporter
|
||||||
@ -406,7 +401,7 @@ mod app {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
if pus_tc.service() == PusServiceId::Test as u8 && pus_tc.subservice() == 1 {
|
if pus_tc.service() == PusServiceId::Test as u8 && pus_tc.subservice() == 1 {
|
||||||
log::info!(target: "TC Handler", "received ping TC");
|
defmt::info!("received ping TC");
|
||||||
let tm = cx
|
let tm = cx
|
||||||
.local
|
.local
|
||||||
.verif_reporter
|
.verif_reporter
|
||||||
@ -453,31 +448,22 @@ mod app {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
let data = &app_data[10..10 + data_len as usize];
|
let data = &app_data[10..10 + data_len as usize];
|
||||||
log::info!(
|
defmt::info!("writing {} bytes at offset {} to NVM", data_len, offset);
|
||||||
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(&mut sys_cfg));
|
*cx.local.rom_spi = Some(nvm.release());
|
||||||
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);
|
||||||
log::info!(
|
defmt::info!("NVM operation done");
|
||||||
target: "TC Handler",
|
|
||||||
"NVM operation done");
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -506,9 +492,12 @@ 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(&cx.local.encoded_buf[0..send_size + 2])
|
.write_all(&cx.local.encoded_buf[0..send_size + 2])
|
||||||
.unwrap();
|
.unwrap();
|
||||||
Mono::delay(2.millis()).await;
|
Mono::delay(2.millis()).await;
|
||||||
}
|
}
|
||||||
|
@ -11,17 +11,8 @@ keywords = ["no-std", "hal", "cortex-m", "vorago", "va416xx"]
|
|||||||
categories = ["aerospace", "embedded", "no-std", "hardware-support"]
|
categories = ["aerospace", "embedded", "no-std", "hardware-support"]
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
critical-section = "1"
|
vorago-shared-periphs = { git = "https://egit.irs.uni-stuttgart.de/rust/vorago-shared-periphs.git", features = ["vor4x"] }
|
||||||
|
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.4, <=0.5" }
|
|
||||||
|
|
||||||
[features]
|
[features]
|
||||||
default = ["irq-tim14-tim15"]
|
default = ["irq-tim14-tim15"]
|
||||||
|
@ -21,11 +21,11 @@
|
|||||||
//! itself by using the `irq-tim14-tim15` feature flag. This library exposes three combinations:
|
//! itself by using the `irq-tim14-tim15` feature flag. This library exposes three combinations:
|
||||||
//!
|
//!
|
||||||
//! - `irq-tim14-tim15`: Uses [pac::Interrupt::TIM14] for alarm and [pac::Interrupt::TIM15]
|
//! - `irq-tim14-tim15`: Uses [pac::Interrupt::TIM14] for alarm and [pac::Interrupt::TIM15]
|
||||||
//! for timekeeper
|
//! for timekeeper
|
||||||
//! - `irq-tim13-tim14`: Uses [pac::Interrupt::TIM13] for alarm and [pac::Interrupt::TIM14]
|
//! - `irq-tim13-tim14`: Uses [pac::Interrupt::TIM13] for alarm and [pac::Interrupt::TIM14]
|
||||||
//! for timekeeper
|
//! for timekeeper
|
||||||
//! - `irq-tim22-tim23`: Uses [pac::Interrupt::TIM22] for alarm and [pac::Interrupt::TIM23]
|
//! - `irq-tim22-tim23`: Uses [pac::Interrupt::TIM22] for alarm and [pac::Interrupt::TIM23]
|
||||||
//! for timekeeper
|
//! for timekeeper
|
||||||
//!
|
//!
|
||||||
//! You can disable the default features and then specify one of the features above to use the
|
//! You can disable the default features and then specify one of the features above to use the
|
||||||
//! documented combination of IRQs. It is also possible to specify custom IRQs by importing and
|
//! documented combination of IRQs. It is also possible to specify custom IRQs by importing and
|
||||||
@ -38,34 +38,13 @@
|
|||||||
//! [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},
|
||||||
pwm::ValidTim,
|
timer::{TimMarker, TIM_IRQ_OFFSET},
|
||||||
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.
|
||||||
///
|
///
|
||||||
@ -110,289 +89,29 @@ 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,
|
TimekeeperTim::ID.value(),
|
||||||
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,
|
AlarmTim::ID.value(),
|
||||||
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(syscfg, irq_router);
|
enable_and_init_irq_router();
|
||||||
TIME_DRIVER.init(syscfg, timekeeper, alarm, clocks)
|
time_driver().__init(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());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
@ -12,39 +12,30 @@ 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"] }
|
||||||
critical-section = "1"
|
|
||||||
nb = "1"
|
|
||||||
paste = "1"
|
|
||||||
embedded-hal-nb = "1"
|
|
||||||
embedded-hal-async = "1"
|
|
||||||
embedded-hal = "1"
|
|
||||||
embedded-io = "0.6"
|
|
||||||
embedded-io-async = "0.6"
|
|
||||||
num_enum = { version = "0.7", default-features = false }
|
|
||||||
typenum = "1"
|
|
||||||
bitflags = "2"
|
|
||||||
bitfield = { version = ">=0.17, <=0.18"}
|
|
||||||
fugit = "0.3"
|
|
||||||
delegate = ">=0.12, <=0.13"
|
|
||||||
heapless = "0.8"
|
|
||||||
void = { version = "1", 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 }
|
va416xx = { version = "0.4", features = ["critical-section"], default-features = false }
|
||||||
|
vorago-shared-periphs = { git = "https://egit.irs.uni-stuttgart.de/rust/vorago-shared-periphs.git", features = ["vor4x"] }
|
||||||
|
|
||||||
|
nb = "1"
|
||||||
|
embedded-hal = "1"
|
||||||
|
num_enum = { version = "0.7", default-features = false }
|
||||||
|
bitflags = "2"
|
||||||
|
bitbybit = "1.3"
|
||||||
|
arbitrary-int = "1.3"
|
||||||
|
fugit = "0.3"
|
||||||
|
thiserror = { version = "2", 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", "embedded-hal/defmt-03"]
|
defmt = ["dep:defmt", "fugit/defmt", "vorago-shared-periphs/defmt"]
|
||||||
|
|
||||||
va41630 = ["device-selected"]
|
va41630 = ["device-selected"]
|
||||||
va41620 = ["device-selected"]
|
va41620 = ["device-selected"]
|
||||||
|
|
||||||
va41629 = ["device-selected"]
|
va41629 = ["device-selected"]
|
||||||
va41628 = ["device-selected"]
|
va41628 = ["device-selected", "vorago-shared-periphs/va41628"]
|
||||||
|
|
||||||
device-selected = []
|
device-selected = []
|
||||||
revb = []
|
revb = []
|
||||||
|
@ -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(syscfg: &mut pac::Sysconfig, adc: pac::Adc, clocks: &Clocks) -> Self {
|
pub fn new(adc: pac::Adc, clocks: &Clocks) -> Self {
|
||||||
Self::generic_new(syscfg, adc, clocks)
|
Self::generic_new(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,12 +209,8 @@ impl Adc<ChannelTagDisabled> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl Adc<ChannelTagEnabled> {
|
impl Adc<ChannelTagEnabled> {
|
||||||
pub fn new_with_channel_tag(
|
pub fn new_with_channel_tag(adc: pac::Adc, clocks: &Clocks) -> Self {
|
||||||
syscfg: &mut pac::Sysconfig,
|
let mut adc = Self::generic_new(adc, clocks);
|
||||||
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
|
||||||
}
|
}
|
||||||
@ -286,8 +282,8 @@ impl Adc<ChannelTagEnabled> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl<TagEnabled> Adc<TagEnabled> {
|
impl<TagEnabled> Adc<TagEnabled> {
|
||||||
fn generic_new(syscfg: &mut pac::Sysconfig, adc: pac::Adc, _clocks: &Clocks) -> Self {
|
fn generic_new(adc: pac::Adc, _clocks: &Clocks) -> Self {
|
||||||
syscfg.enable_peripheral_clock(crate::clock::PeripheralSelect::Adc);
|
enable_peripheral_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,
|
||||||
|
@ -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 [ClkgenExt::constrain] on the [crate::pac::Clkgen] peripheral generates the
|
//! Calling [ClockConfigurator::new] returns a builder structure which allows
|
||||||
//! [ClkgenCfgr] structure which can be used to configure and set up the clock.
|
//! setting up the clock.
|
||||||
//!
|
//!
|
||||||
//! Calling [ClkgenCfgr::freeze] returns the frozen clock configuration inside the [Clocks]
|
//! Calling [ClockConfigurator::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,48 +15,11 @@ 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 {
|
||||||
@ -70,81 +33,6 @@ 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))]
|
||||||
@ -223,12 +111,12 @@ pub fn pll_setup_delay() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
pub trait ClkgenExt {
|
pub trait ClkgenExt {
|
||||||
fn constrain(self) -> ClkgenCfgr;
|
fn constrain(self) -> ClockConfigurator;
|
||||||
}
|
}
|
||||||
|
|
||||||
impl ClkgenExt for pac::Clkgen {
|
impl ClkgenExt for pac::Clkgen {
|
||||||
fn constrain(self) -> ClkgenCfgr {
|
fn constrain(self) -> ClockConfigurator {
|
||||||
ClkgenCfgr {
|
ClockConfigurator {
|
||||||
source_clk: None,
|
source_clk: None,
|
||||||
ref_clk_sel: RefClkSel::None,
|
ref_clk_sel: RefClkSel::None,
|
||||||
clksel_sys: ClkselSys::Hbo,
|
clksel_sys: ClkselSys::Hbo,
|
||||||
@ -241,21 +129,6 @@ 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;
|
||||||
@ -269,6 +142,21 @@ 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
|
||||||
@ -283,7 +171,30 @@ pub fn hbo_clock_delay_ms(ms: u32) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl ClkgenCfgr {
|
impl ClockConfigurator {
|
||||||
|
/// 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);
|
||||||
@ -334,7 +245,7 @@ impl ClkgenCfgr {
|
|||||||
/// 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, syscfg: &mut pac::Sysconfig) -> Result<Clocks, ClkCfgError> {
|
pub fn freeze(self) -> 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);
|
||||||
@ -349,7 +260,7 @@ impl ClkgenCfgr {
|
|||||||
return Err(ClkCfgError::PllConfigNotSet);
|
return Err(ClkCfgError::PllConfigNotSet);
|
||||||
}
|
}
|
||||||
|
|
||||||
syscfg.enable_peripheral_clock(PeripheralSelect::Clkgen);
|
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
|
||||||
@ -458,13 +369,11 @@ impl ClkgenCfgr {
|
|||||||
.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 {
|
Ok(Clocks::__new(
|
||||||
sysclk: final_sysclk,
|
final_sysclk,
|
||||||
apb1: final_sysclk / 2,
|
|
||||||
apb2: final_sysclk / 4,
|
|
||||||
#[cfg(not(feature = "va41628"))]
|
#[cfg(not(feature = "va41628"))]
|
||||||
adc_clk: self.cfg_adc_clk_div(final_sysclk),
|
self.cfg_adc_clk_div(final_sysclk),
|
||||||
})
|
))
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(not(feature = "va41628"))]
|
#[cfg(not(feature = "va41628"))]
|
||||||
@ -487,54 +396,6 @@ impl ClkgenCfgr {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// 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() })
|
||||||
}
|
}
|
||||||
|
@ -5,22 +5,24 @@
|
|||||||
//! - [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 crate::{
|
use vorago_shared_periphs::{
|
||||||
clock::{Clocks, PeripheralSelect, SyscfgExt},
|
disable_peripheral_clock, enable_peripheral_clock, reset_peripheral_for_cycles,
|
||||||
pac,
|
PeripheralSelect,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
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 Instance: Deref<Target = DacRegisterBlock> {
|
pub trait DacMarker: Deref<Target = DacRegisterBlock> {
|
||||||
const IDX: u8;
|
const IDX: u8;
|
||||||
|
|
||||||
fn ptr() -> *const DacRegisterBlock;
|
fn ptr() -> *const DacRegisterBlock;
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Instance for pac::Dac0 {
|
impl DacMarker for pac::Dac0 {
|
||||||
const IDX: u8 = 0;
|
const IDX: u8 = 0;
|
||||||
|
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
@ -29,7 +31,7 @@ impl Instance for pac::Dac0 {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Instance for pac::Dac1 {
|
impl DacMarker for pac::Dac1 {
|
||||||
const IDX: u8 = 1;
|
const IDX: u8 = 1;
|
||||||
|
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
@ -50,40 +52,37 @@ pub enum DacSettling {
|
|||||||
Apb2Times150 = 6,
|
Apb2Times150 = 6,
|
||||||
}
|
}
|
||||||
|
|
||||||
pub struct Dac<DacInstance> {
|
pub struct Dac(*const DacRegisterBlock);
|
||||||
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<DacInstance: Instance> Dac<DacInstance> {
|
impl Dac {
|
||||||
/// 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(
|
pub fn new<Dac: DacMarker>(dac: Dac, dac_settling: DacSettling, _clocks: &Clocks) -> Self {
|
||||||
syscfg: &mut pac::Sysconfig,
|
enable_peripheral_clock(PeripheralSelect::Dac);
|
||||||
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 dac = Self { dac };
|
let mut dac = Self(Dac::ptr());
|
||||||
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(&self) {
|
pub fn clear_irqs(&mut self) {
|
||||||
self.dac.irq_clr().write(|w| {
|
self.regs().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();
|
||||||
@ -92,31 +91,30 @@ impl<DacInstance: Instance> Dac<DacInstance> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
pub fn clear_fifo(&self) {
|
pub fn clear_fifo(&mut self) {
|
||||||
self.dac.fifo_clr().write(|w| unsafe { w.bits(1) });
|
self.regs().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(&self, val: u16) -> nb::Result<(), ValueTooLarge> {
|
pub fn load_value(&mut 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));
|
||||||
}
|
}
|
||||||
if self.dac.status().read().fifo_entry_cnt().bits() >= 32_u8 {
|
let regs = self.regs();
|
||||||
|
if regs.status().read().fifo_entry_cnt().bits() >= 32_u8 {
|
||||||
return Err(nb::Error::WouldBlock);
|
return Err(nb::Error::WouldBlock);
|
||||||
}
|
}
|
||||||
self.dac
|
regs.fifo_data().write(|w| unsafe { w.bits(val.into()) });
|
||||||
.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(&self, val: u16) -> Result<(), ValueTooLarge> {
|
pub fn load_and_trigger_manually(&mut 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);
|
||||||
}
|
}
|
||||||
@ -132,31 +130,30 @@ impl<DacInstance: Instance> Dac<DacInstance> {
|
|||||||
/// 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.dac.ctrl0().write(|w| w.man_trig_en().set_bit());
|
self.regs().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.dac.ctrl0().write(|w| w.ext_trig_en().set_bit());
|
self.regs().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.dac.status().read().dac_busy().bit_is_set() {
|
if self.regs().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, syscfg: &mut pac::Sysconfig) {
|
pub fn reset(&mut self) {
|
||||||
syscfg.enable_peripheral_clock(PeripheralSelect::Dac);
|
enable_peripheral_clock(PeripheralSelect::Dac);
|
||||||
syscfg.assert_periph_reset_for_two_cycles(PeripheralSelect::Dac);
|
reset_peripheral_for_cycles(PeripheralSelect::Dac, 2);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Relases the DAC, which also disables its peripheral clock.
|
/// Stops the DAC, which disables its peripheral clock.
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
pub fn release(self, syscfg: &mut pac::Sysconfig) -> DacInstance {
|
pub fn stop(self) {
|
||||||
syscfg.disable_peripheral_clock(PeripheralSelect::Dac);
|
disable_peripheral_clock(PeripheralSelect::Dac);
|
||||||
self.dac
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -3,21 +3,23 @@
|
|||||||
//! ## 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 crate::{
|
use arbitrary_int::{u10, u3};
|
||||||
clock::{PeripheralClock, PeripheralSelect},
|
use vorago_shared_periphs::{
|
||||||
enable_nvic_interrupt, pac,
|
enable_peripheral_clock, reset_peripheral_for_cycles, PeripheralSelect,
|
||||||
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.
|
||||||
#[repr(u8)]
|
#[bitbybit::bitenum(u3, exhaustive = true)]
|
||||||
#[derive(Debug, Clone, Copy)]
|
#[derive(Debug)]
|
||||||
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
|
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
|
||||||
|
#[repr(u8)]
|
||||||
pub enum CycleControl {
|
pub enum CycleControl {
|
||||||
/// Indicates that the data structure is invalid.
|
/// Indicates that the data structure is invalid.
|
||||||
Stop = 0b000,
|
Stop = 0b000,
|
||||||
@ -42,7 +44,8 @@ pub enum CycleControl {
|
|||||||
PeriphScatterGatherAlternate = 0b111,
|
PeriphScatterGatherAlternate = 0b111,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Clone, Copy)]
|
#[bitbybit::bitenum(u2, exhaustive = true)]
|
||||||
|
#[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,
|
||||||
@ -51,7 +54,8 @@ pub enum AddrIncrement {
|
|||||||
None = 0b11,
|
None = 0b11,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Clone, Copy)]
|
#[bitbybit::bitenum(u2, exhaustive = false)]
|
||||||
|
#[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,
|
||||||
@ -60,7 +64,8 @@ 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.
|
||||||
#[derive(Debug, Clone, Copy)]
|
#[bitbybit::bitenum(u4, exhaustive = true)]
|
||||||
|
#[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,
|
||||||
@ -73,8 +78,12 @@ pub enum RPower {
|
|||||||
Every128 = 0b0111,
|
Every128 = 0b0111,
|
||||||
Every256 = 0b1000,
|
Every256 = 0b1000,
|
||||||
Every512 = 0b1001,
|
Every512 = 0b1001,
|
||||||
Every1024Min = 0b1010,
|
Every1024 = 0b1010,
|
||||||
Every1024 = 0b1111,
|
Every1024Alt0 = 0b1011,
|
||||||
|
Every1024Alt1 = 0b1100,
|
||||||
|
Every1024Alt2 = 0b1101,
|
||||||
|
Every1024Alt3 = 0b1110,
|
||||||
|
Every1024Alt4 = 0b1111,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, PartialEq, Eq, thiserror::Error)]
|
#[derive(Debug, PartialEq, Eq, thiserror::Error)]
|
||||||
@ -82,6 +91,7 @@ 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)]
|
||||||
@ -111,6 +121,33 @@ 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)]
|
||||||
@ -127,7 +164,7 @@ impl DmaChannelControl {
|
|||||||
Self {
|
Self {
|
||||||
src_end_ptr: 0,
|
src_end_ptr: 0,
|
||||||
dest_end_ptr: 0,
|
dest_end_ptr: 0,
|
||||||
cfg: ChannelConfig(0),
|
cfg: ChannelConfig::new_with_raw_value(0),
|
||||||
padding: 0,
|
padding: 0,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -428,20 +465,18 @@ 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.set_raw(0);
|
self.ch_ctrl_pri.cfg = ChannelConfig::new_with_raw_value(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
|
self.ch_ctrl_pri.cfg.set_cycle_ctrl(CycleControl::Basic);
|
||||||
.cfg
|
self.ch_ctrl_pri.cfg.set_src_size(DataSize::Byte);
|
||||||
.set_cycle_ctr(CycleControl::Basic as u8);
|
self.ch_ctrl_pri.cfg.set_src_inc(AddrIncrement::Byte);
|
||||||
self.ch_ctrl_pri.cfg.set_src_size(DataSize::Byte as u8);
|
self.ch_ctrl_pri.cfg.set_dst_size(DataSize::Byte);
|
||||||
self.ch_ctrl_pri.cfg.set_src_inc(AddrIncrement::Byte as u8);
|
self.ch_ctrl_pri.cfg.set_dst_inc(AddrIncrement::None);
|
||||||
self.ch_ctrl_pri.cfg.set_dst_size(DataSize::Byte as u8);
|
self.ch_ctrl_pri.cfg.set_n_minus_1(u10::new(len as u16));
|
||||||
self.ch_ctrl_pri.cfg.set_dst_inc(AddrIncrement::None as u8);
|
self.ch_ctrl_pri.cfg.set_r_power(RPower::Every8);
|
||||||
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(())
|
||||||
}
|
}
|
||||||
@ -470,16 +505,18 @@ impl DmaChannel {
|
|||||||
data_size: DataSize,
|
data_size: DataSize,
|
||||||
addr_incr: AddrIncrement,
|
addr_incr: AddrIncrement,
|
||||||
) {
|
) {
|
||||||
self.ch_ctrl_pri.cfg.set_raw(0);
|
self.ch_ctrl_pri.cfg = ChannelConfig::new_with_raw_value(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_ctr(CycleControl::Auto as u8);
|
self.ch_ctrl_pri.cfg.set_cycle_ctrl(CycleControl::Auto);
|
||||||
self.ch_ctrl_pri.cfg.set_src_size(data_size as u8);
|
self.ch_ctrl_pri.cfg.set_src_size(data_size);
|
||||||
self.ch_ctrl_pri.cfg.set_src_inc(addr_incr as u8);
|
self.ch_ctrl_pri.cfg.set_src_inc(addr_incr);
|
||||||
self.ch_ctrl_pri.cfg.set_dst_size(data_size as u8);
|
self.ch_ctrl_pri.cfg.set_dst_size(data_size);
|
||||||
self.ch_ctrl_pri.cfg.set_dst_inc(addr_incr as u8);
|
self.ch_ctrl_pri.cfg.set_dst_inc(addr_incr);
|
||||||
self.ch_ctrl_pri.cfg.set_n_minus_1(n_minus_one as u16);
|
self.ch_ctrl_pri
|
||||||
self.ch_ctrl_pri.cfg.set_r_power(RPower::Every4 as u8);
|
.cfg
|
||||||
|
.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();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -495,7 +532,6 @@ 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,
|
||||||
@ -505,8 +541,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);
|
||||||
}
|
}
|
||||||
syscfg.enable_peripheral_clock(PeripheralClock::Dma);
|
enable_peripheral_clock(PeripheralSelect::Dma);
|
||||||
syscfg.assert_periph_reset_for_two_cycles(PeripheralSelect::Dma);
|
reset_peripheral_for_cycles(PeripheralSelect::Dma, 2);
|
||||||
let dma = Dma { dma, ctrl_block };
|
let dma = Dma { dma, ctrl_block };
|
||||||
dma.dma
|
dma.dma
|
||||||
.ctrl_base_ptr()
|
.ctrl_base_ptr()
|
||||||
|
@ -1,448 +0,0 @@
|
|||||||
//! # 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
@ -1,80 +1,20 @@
|
|||||||
//! # API for the GPIO peripheral
|
//! # GPIO support module.
|
||||||
//!
|
//!
|
||||||
//! The implementation of this GPIO module is heavily based on the
|
//! Contains abstractions to use the pins provided by the [crate::pins] module as GPIO or
|
||||||
//! [ATSAMD HAL implementation](https://docs.rs/atsamd-hal/latest/atsamd_hal/gpio/index.html).
|
//! IO peripheral pins.
|
||||||
//!
|
//!
|
||||||
//! This API provides two different submodules, [pin] and [dynpin],
|
//! The core data structures provided for this are the
|
||||||
//! 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.
|
|
||||||
//!
|
//!
|
||||||
//! The type-level API is strongly preferred. By representing the state of each
|
//! - [Output] for push-pull output pins.
|
||||||
//! pin within the type system, the compiler can detect logic errors at
|
//! - [Input] for input pins.
|
||||||
//! compile-time. Furthermore, the type-level API has absolutely zero run-time
|
//! - [Flex] for pins with flexible configuration requirements.
|
||||||
//! cost.
|
//! - [IoPeriphPin] for IO peripheral pins.
|
||||||
//!
|
//!
|
||||||
//! If needed, [dynpin] can be used to erase the type-level differences
|
//! The [crate::pins] module exposes singletons to access the [Pin]s required by this module
|
||||||
//! between pins. However, by doing so, pins must now be tracked at run-time,
|
//! in a type-safe way.
|
||||||
//! 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::*;
|
|
||||||
|
@ -1,935 +0,0 @@
|
|||||||
//! # 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)
|
|
||||||
]
|
|
||||||
);
|
|
@ -3,913 +3,4 @@
|
|||||||
//! ## 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)
|
||||||
use crate::{
|
pub use vorago_shared_periphs::i2c::*;
|
||||||
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)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
@ -1,9 +1,10 @@
|
|||||||
//! IRQ Router peripheral support.
|
//! IRQ Router peripheral support.
|
||||||
use crate::{
|
use vorago_shared_periphs::{
|
||||||
clock::{PeripheralSelect, SyscfgExt},
|
enable_peripheral_clock, reset_peripheral_for_cycles, PeripheralSelect,
|
||||||
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
|
||||||
@ -11,9 +12,10 @@ use crate::{
|
|||||||
/// 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(sysconfig: &mut pac::Sysconfig, irq_router: &pac::IrqRouter) {
|
pub fn enable_and_init_irq_router() {
|
||||||
sysconfig.enable_peripheral_clock(PeripheralSelect::IrqRouter);
|
let irq_router = unsafe { pac::IrqRouter::steal() };
|
||||||
sysconfig.assert_periph_reset_for_two_cycles(PeripheralSelect::IrqRouter);
|
enable_peripheral_clock(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);
|
||||||
|
@ -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,14 +57,11 @@ pub mod adc;
|
|||||||
#[cfg(not(feature = "va41628"))]
|
#[cfg(not(feature = "va41628"))]
|
||||||
pub mod dac;
|
pub mod dac;
|
||||||
|
|
||||||
#[derive(Debug, Eq, Copy, Clone, PartialEq)]
|
pub use vorago_shared_periphs::{
|
||||||
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
|
assert_peripheral_reset, deassert_peripheral_reset, disable_nvic_interrupt,
|
||||||
pub enum FunSel {
|
disable_peripheral_clock, enable_nvic_interrupt, enable_peripheral_clock,
|
||||||
Sel0 = 0b00,
|
reset_peripheral_for_cycles, FunSel, PeripheralSelect,
|
||||||
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))]
|
||||||
@ -100,18 +97,41 @@ pub fn port_function_select(
|
|||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Enable a specific interrupt using the NVIC peripheral.
|
pub trait SyscfgExt {
|
||||||
///
|
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.
|
|
||||||
#[inline]
|
fn assert_periph_reset(&mut self, periph: PeripheralSelect);
|
||||||
pub unsafe fn enable_nvic_interrupt(irq: pac::Interrupt) {
|
|
||||||
cortex_m::peripheral::NVIC::unmask(irq);
|
fn deassert_periph_reset(&mut self, periph: PeripheralSelect);
|
||||||
|
|
||||||
|
fn reset_peripheral_reset_for_cycles(&mut self, periph: PeripheralSelect, cycles: usize);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Disable a specific interrupt using the NVIC peripheral.
|
impl SyscfgExt for pac::Sysconfig {
|
||||||
#[inline]
|
#[inline(always)]
|
||||||
pub fn disable_nvic_interrupt(irq: pac::Interrupt) {
|
fn enable_peripheral_clock(&mut self, clock: PeripheralSelect) {
|
||||||
cortex_m::peripheral::NVIC::mask(irq);
|
enable_peripheral_clock(clock)
|
||||||
|
}
|
||||||
|
|
||||||
|
#[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)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -6,11 +6,14 @@
|
|||||||
//!
|
//!
|
||||||
//! - [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, SyscfgExt};
|
use crate::clock::Clocks;
|
||||||
use crate::pac;
|
use crate::pac;
|
||||||
use crate::spi::{
|
use crate::spi::{
|
||||||
mode_to_cpo_cph_bit, spi_clk_config_from_div, Instance, WordProvider, BMSTART_BMSTOP_MASK,
|
mode_to_cpo_cph_bit, spi_clk_config_from_div, SpiMarker, WordProvider, BMSTART_BMSTOP_MASK,
|
||||||
};
|
};
|
||||||
|
|
||||||
const NVM_CLOCK_DIV: u16 = 2;
|
const NVM_CLOCK_DIV: u16 = 2;
|
||||||
@ -65,10 +68,10 @@ pub struct VerifyError {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl Nvm {
|
impl Nvm {
|
||||||
pub fn new(syscfg: &mut pac::Sysconfig, spi: pac::Spi3, _clocks: &Clocks) -> Self {
|
pub fn new(spi: pac::Spi3, _clocks: &Clocks) -> Self {
|
||||||
crate::clock::enable_peripheral_clock(syscfg, pac::Spi3::PERIPH_SEL);
|
enable_peripheral_clock(pac::Spi3::PERIPH_SEL);
|
||||||
// This is done in the C HAL.
|
// This is done in the C HAL.
|
||||||
syscfg.assert_periph_reset_for_two_cycles(pac::Spi3::PERIPH_SEL);
|
reset_peripheral_for_cycles(pac::Spi3::PERIPH_SEL, 2);
|
||||||
|
|
||||||
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);
|
||||||
@ -234,17 +237,17 @@ impl Nvm {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// Enable write-protection and disables the peripheral clock.
|
/// Enable write-protection and disables the peripheral clock.
|
||||||
pub fn shutdown(&mut self, sys_cfg: &mut pac::Sysconfig) {
|
pub fn shutdown(&mut self) {
|
||||||
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);
|
||||||
crate::clock::disable_peripheral_clock(sys_cfg, pac::Spi3::PERIPH_SEL);
|
disable_peripheral_clock(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, sys_cfg: &mut pac::Sysconfig) -> pac::Spi3 {
|
pub fn release(mut self) -> pac::Spi3 {
|
||||||
self.shutdown(sys_cfg);
|
self.shutdown();
|
||||||
self.spi.take().unwrap()
|
self.spi.take().unwrap()
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -268,7 +271,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(unsafe { &mut pac::Sysconfig::steal() });
|
self.shutdown();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
6
va416xx-hal/src/pins.rs
Normal file
6
va416xx-hal/src/pins.rs
Normal file
@ -0,0 +1,6 @@
|
|||||||
|
//! 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::*;
|
@ -1,4 +1,4 @@
|
|||||||
//! Prelude
|
//! Prelude
|
||||||
pub use crate::clock::{ClkgenExt, SyscfgExt};
|
pub use crate::clock::ClkgenExt;
|
||||||
pub use fugit::ExtU32 as _;
|
pub use fugit::ExtU32 as _;
|
||||||
pub use fugit::RateExtU32 as _;
|
pub use fugit::RateExtU32 as _;
|
||||||
|
@ -1,459 +1,8 @@
|
|||||||
//! API for Pulse-Width Modulation (PWM)
|
//! API for Pulse-Width Modulation (PWM)
|
||||||
//!
|
//!
|
||||||
//! The Vorago VA416xx devices use the TIM peripherals to perform PWM related tasks.
|
//! The Vorago 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)
|
||||||
use core::convert::Infallible;
|
pub use vorago_shared_periphs::pwm::*;
|
||||||
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
@ -2,904 +2,8 @@
|
|||||||
//!
|
//!
|
||||||
//! ## Examples
|
//! ## Examples
|
||||||
//!
|
//!
|
||||||
//! - [Timer MS and Second Tick Example](https://github.com/us-irs/va416xx-rs/blob/main/examples/simple/examples/timer-ticks.rs)
|
//! - [MS and second tick implementation](https://egit.irs.uni-stuttgart.de/rust/va416xx-rs/src/branch/main/examples/simple/examples/timer-ticks.rs)
|
||||||
use core::cell::Cell;
|
//! - [Cascade feature example](https://egit.irs.uni-stuttgart.de/rust/va416xx-rs/src/branch/main/examples/simple/examples/cascade.rs)
|
||||||
|
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();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
@ -1,155 +0,0 @@
|
|||||||
//! 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
@ -1,448 +0,0 @@
|
|||||||
//! # Async UART reception functionality for the VA416xx family.
|
|
||||||
//!
|
|
||||||
//! This module provides the [RxAsync] and [RxAsyncOverwriting] struct which both implement the
|
|
||||||
//! [embedded_io_async::Read] trait.
|
|
||||||
//! This trait allows for asynchronous reception of data streams. Please note that this module does
|
|
||||||
//! not specify/declare the interrupt handlers which must be provided for async support to work.
|
|
||||||
//! However, it provides two interrupt handlers:
|
|
||||||
//!
|
|
||||||
//! - [on_interrupt_rx]
|
|
||||||
//! - [on_interrupt_rx_overwriting]
|
|
||||||
//!
|
|
||||||
//! The first two are used for the [RxAsync] struct, while the latter two are used with the
|
|
||||||
//! [RxAsyncOverwriting] struct. The later two will overwrite old values in the used ring buffer.
|
|
||||||
//!
|
|
||||||
//! Error handling is performed in the user interrupt handler by checking the [AsyncUartErrors]
|
|
||||||
//! structure returned by the interrupt handlers.
|
|
||||||
//!
|
|
||||||
//! # Example
|
|
||||||
//!
|
|
||||||
//! - [Async UART RX example](https://egit.irs.uni-stuttgart.de/rust/va108xx-rs/src/branch/main/examples/embassy/src/bin/async-uart-rx.rs)
|
|
||||||
use core::{cell::RefCell, convert::Infallible, future::Future, sync::atomic::Ordering};
|
|
||||||
|
|
||||||
use critical_section::Mutex;
|
|
||||||
use embassy_sync::waitqueue::AtomicWaker;
|
|
||||||
use embedded_io::ErrorType;
|
|
||||||
use portable_atomic::AtomicBool;
|
|
||||||
use 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)
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,263 +0,0 @@
|
|||||||
//! # 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
|
|
||||||
}
|
|
||||||
}
|
|
@ -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 crate::time::Hertz;
|
use vorago_shared_periphs::{
|
||||||
use crate::{
|
enable_peripheral_clock, reset_peripheral_for_cycles, PeripheralSelect,
|
||||||
clock::{Clocks, PeripheralSelect},
|
|
||||||
pac,
|
|
||||||
prelude::SyscfgExt,
|
|
||||||
};
|
};
|
||||||
|
|
||||||
|
use crate::time::Hertz;
|
||||||
|
use crate::{clock::Clocks, pac};
|
||||||
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,23 +39,13 @@ pub fn disable_wdt_interrupts() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl Wdt {
|
impl Wdt {
|
||||||
pub fn new(
|
pub fn new(wdt: pac::WatchDog, clocks: &Clocks, wdt_freq_ms: u32) -> Self {
|
||||||
syscfg: &mut pac::Sysconfig,
|
Self::start(wdt, clocks, wdt_freq_ms)
|
||||||
wdt: pac::WatchDog,
|
|
||||||
clocks: &Clocks,
|
|
||||||
wdt_freq_ms: u32,
|
|
||||||
) -> Self {
|
|
||||||
Self::start(syscfg, wdt, clocks, wdt_freq_ms)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn start(
|
pub fn start(wdt: pac::WatchDog, clocks: &Clocks, wdt_freq_ms: u32) -> Self {
|
||||||
syscfg: &mut pac::Sysconfig,
|
enable_peripheral_clock(PeripheralSelect::Watchdog);
|
||||||
wdt: pac::WatchDog,
|
reset_peripheral_for_cycles(PeripheralSelect::Watchdog, 2);
|
||||||
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 {
|
||||||
|
@ -11,15 +11,10 @@ keywords = ["no-std", "peb1", "cortex-m", "vorago", "va416xx"]
|
|||||||
categories = ["embedded", "no-std", "hardware-support"]
|
categories = ["embedded", "no-std", "hardware-support"]
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
cortex-m = "0.7"
|
va416xx-hal = { version = ">=0.3, <=0.5", path = "../va416xx-hal", features = ["va41630"] }
|
||||||
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
|
||||||
|
@ -19,7 +19,7 @@ pub mod accelerometer {
|
|||||||
};
|
};
|
||||||
|
|
||||||
// Accelerometer located on the GPIO board.
|
// Accelerometer located on the GPIO board.
|
||||||
pub type Accelerometer = Lis2dh12<I2cMaster<pac::I2c0>>;
|
pub type Accelerometer = Lis2dh12<I2cMaster>;
|
||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
pub enum ConstructorError {
|
pub enum ConstructorError {
|
||||||
@ -30,14 +30,12 @@ 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,
|
||||||
sysconfig,
|
|
||||||
MasterConfig::default(),
|
|
||||||
clocks,
|
clocks,
|
||||||
|
MasterConfig::default(),
|
||||||
I2cSpeed::Regular100khz,
|
I2cSpeed::Regular100khz,
|
||||||
)
|
)
|
||||||
.map_err(ConstructorError::ClkError)?;
|
.map_err(ConstructorError::ClkError)?;
|
||||||
@ -47,7 +45,7 @@ pub mod accelerometer {
|
|||||||
}
|
}
|
||||||
|
|
||||||
pub fn new_with_i2cm(
|
pub fn new_with_i2cm(
|
||||||
i2c: I2cMaster<pac::I2c0>,
|
i2c: I2cMaster,
|
||||||
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)
|
||||||
|
Loading…
x
Reference in New Issue
Block a user