diff --git a/bootloader/Cargo.toml b/bootloader/Cargo.toml index 3e984af..c406cfc 100644 --- a/bootloader/Cargo.toml +++ b/bootloader/Cargo.toml @@ -6,7 +6,6 @@ edition = "2021" [dependencies] cortex-m = "0.7" cortex-m-rt = "0.7" -embedded-hal = "1" defmt-rtt = "0.4" defmt = "1" panic-probe = { version = "1", features = ["defmt"] } @@ -16,3 +15,4 @@ static_assertions = "1" [dependencies.va416xx-hal] version = "0.5" features = ["va41630", "defmt"] +path = "../va416xx-hal" diff --git a/bootloader/src/main.rs b/bootloader/src/main.rs index 1551e05..35ebe06 100644 --- a/bootloader/src/main.rs +++ b/bootloader/src/main.rs @@ -10,11 +10,10 @@ use crc::{Crc, CRC_32_ISO_HDLC}; use defmt_rtt as _; use panic_probe as _; use va416xx_hal::{ - clock::{pll_setup_delay, ClkDivSel, ClkselSys}, + clock::{pll_setup_delay, ClkDivSel, ClkselSys, ClockConfigurator}, edac, nvm::Nvm, pac::{self, interrupt}, - prelude::*, time::Hertz, wdt::Wdt, }; @@ -104,23 +103,16 @@ fn main() -> ! { dp.sysconfig.rom_prot().write(|w| unsafe { w.bits(1) }); setup_edac(&mut dp.sysconfig); // Use the external clock connected to XTAL_N. - let clocks = dp - .clkgen - .constrain() + let clocks = ClockConfigurator::new(dp.clkgen) .xtal_n_clk_with_src_freq(Hertz::from_raw(EXTCLK_FREQ)) - .freeze(&mut dp.sysconfig) + .freeze() .unwrap(); let mut opt_wdt = OptWdt(None); if WITH_WDT { - opt_wdt.0 = Some(Wdt::start( - &mut dp.sysconfig, - dp.watch_dog, - &clocks, - WDT_FREQ_MS, - )); + opt_wdt.0 = Some(Wdt::start(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 { let mut first_four_bytes: [u8; 4] = [0; 4]; diff --git a/examples/embassy/Cargo.toml b/examples/embassy/Cargo.toml index 4c41c7f..b37f672 100644 --- a/examples/embassy/Cargo.toml +++ b/examples/embassy/Cargo.toml @@ -4,10 +4,8 @@ version = "0.1.0" edition = "2021" [dependencies] -cfg-if = "1" -cortex-m = { version = "0.7", features = ["critical-section-single-core"] } cortex-m-rt = "0.7" -embedded-hal = "1" +cfg-if = "1" embedded-io = "0.6" embedded-hal-async = "1" embedded-io-async = "0.6" @@ -18,7 +16,6 @@ defmt = "1" panic-probe = { version = "1", features = ["defmt"] } static_cell = "2" critical-section = "1" -once_cell = { version = "1", default-features = false, features = ["critical-section"] } ringbuf = { version = "0.4", default-features = false } embassy-sync = "0.6" @@ -29,11 +26,14 @@ embassy-executor = { version = "0.7", features = [ "executor-interrupt" ]} -va416xx-hal = { version = "0.5", features = ["defmt"] } -va416xx-embassy = { version = "0.1", default-features = false } +va416xx-hal = { version = "0.5", path = "../../va416xx-hal", features = ["defmt"] } +va416xx-embassy = { version = "0.1", path = "../../va416xx-embassy", default-features = false } [features] default = ["ticks-hz-1_000", "va416xx-embassy/irq-tim14-tim15"] custom-irqs = [] ticks-hz-1_000 = ["embassy-time/tick-hz-1_000"] ticks-hz-32_768 = ["embassy-time/tick-hz-32_768"] + +[package.metadata.cargo-machete] +ignored = ["cortex-m-rt"] diff --git a/examples/embassy/src/bin/async-gpio.rs b/examples/embassy/src/bin/async-gpio.rs index 56b3729..e62e536 100644 --- a/examples/embassy/src/bin/async-gpio.rs +++ b/examples/embassy/src/bin/async-gpio.rs @@ -5,6 +5,7 @@ //! [CHECK_XXX_TO_XXX] constants to true. #![no_std] #![no_main] + // Import panic provider. use panic_probe as _; // Import logger. @@ -16,23 +17,19 @@ use embassy_sync::channel::{Receiver, Sender}; use embassy_sync::{blocking_mutex::raw::ThreadModeRawMutex, channel::Channel}; use embassy_time::{Duration, Instant, Timer}; use embedded_hal_async::digital::Wait; -use va416xx_hal::clock::ClkgenExt; -use va416xx_hal::gpio::{ - on_interrupt_for_async_gpio_for_port, InputDynPinAsync, InputPinAsync, PinsB, PinsC, PinsD, - PinsE, PinsF, PinsG, Port, -}; +use va416xx_hal::clock::ClockConfigurator; +use va416xx_hal::gpio::asynch::{on_interrupt_for_async_gpio_for_port, InputPinAsync}; +use va416xx_hal::gpio::{Input, Output, PinState, 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::{ - gpio::{DynPin, PinsA}, - pac::{self, interrupt}, -}; const CHECK_PA0_TO_PA1: bool = true; -const CHECK_PB0_TO_PB1: bool = true; -const CHECK_PC14_TO_PC15: bool = true; -const CHECK_PD2_TO_PD3: bool = true; -const CHECK_PE0_TO_PE1: bool = true; -const CHECK_PF0_TO_PF1: bool = true; +const CHECK_PB0_TO_PB1: bool = false; +const CHECK_PC14_TO_PC15: bool = false; +const CHECK_PD2_TO_PD3: bool = false; +const CHECK_PE0_TO_PE1: bool = false; +const CHECK_PF0_TO_PF1: bool = false; #[derive(Clone, Copy)] pub struct GpioCmd { @@ -70,41 +67,30 @@ static CHANNEL_PF0_TO_PF1: Channel = Channel::ne async fn main(spawner: Spawner) { 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 // Use the external clock connected to XTAL_N. - let clocks = dp - .clkgen - .constrain() + let clocks = ClockConfigurator::new(dp.clkgen) .xtal_n_clk_with_src_freq(Hertz::from_raw(EXTCLK_FREQ)) - .freeze(&mut dp.sysconfig) + .freeze() .unwrap(); // Safety: Only called once here. - unsafe { - va416xx_embassy::init( - &mut dp.sysconfig, - &dp.irq_router, - dp.tim15, - dp.tim14, - &clocks, - ) - }; + va416xx_embassy::init(dp.tim15, dp.tim14, &clocks); - let porta = PinsA::new(&mut dp.sysconfig, dp.porta); - let portb = PinsB::new(&mut dp.sysconfig, dp.portb); - let portc = PinsC::new(&mut dp.sysconfig, dp.portc); - let portd = PinsD::new(&mut dp.sysconfig, dp.portd); - let porte = PinsE::new(&mut dp.sysconfig, dp.porte); - let portf = PinsF::new(&mut dp.sysconfig, dp.portf); + let porta = PinsA::new(dp.porta); + let portb = PinsB::new(dp.portb); + let portc = PinsC::new(dp.portc); + let portd = PinsD::new(dp.portd); + let porte = PinsE::new(dp.porte); + let portf = PinsF::new(dp.portf); - let portg = PinsG::new(&mut dp.sysconfig, dp.portg); - let mut led = portg.pg5.into_readable_push_pull_output(); + let portg = PinsG::new(dp.portg); + let mut led = Output::new(portg.pg5, PinState::Low); if CHECK_PA0_TO_PA1 { - let out_pin = porta.pa0.into_readable_push_pull_output(); - let in_pin = porta.pa1.into_floating_input(); - let out_pin = out_pin.downgrade(); + let out_pin = Output::new(porta.pa0, PinState::Low); + let in_pin = Input::new_floating(porta.pa1); let in_pin = InputPinAsync::new(in_pin).unwrap(); spawner @@ -119,10 +105,9 @@ async fn main(spawner: Spawner) { } if CHECK_PB0_TO_PB1 { - let out_pin = portb.pb0.into_readable_push_pull_output(); - let in_pin = portb.pb1.into_floating_input(); - let out_pin = out_pin.downgrade(); - let in_pin = InputDynPinAsync::new(in_pin.downgrade()).unwrap(); + let out_pin = Output::new(portb.pb0, PinState::Low); + let in_pin = Input::new_floating(portb.pb1); + let in_pin = InputPinAsync::new(in_pin).unwrap(); spawner .spawn(output_task( @@ -136,10 +121,9 @@ async fn main(spawner: Spawner) { } if CHECK_PC14_TO_PC15 { - let out_pin = portc.pc14.into_readable_push_pull_output(); - let in_pin = portc.pc15.into_floating_input(); - let out_pin = out_pin.downgrade(); - let in_pin = InputDynPinAsync::new(in_pin.downgrade()).unwrap(); + let out_pin = Output::new(portc.pc14, PinState::Low); + let in_pin = Input::new_floating(portc.pc15); + let in_pin = InputPinAsync::new(in_pin).unwrap(); spawner .spawn(output_task( "PC14 to PC15", @@ -152,10 +136,9 @@ async fn main(spawner: Spawner) { } if CHECK_PD2_TO_PD3 { - let out_pin = portd.pd2.into_readable_push_pull_output(); - let in_pin = portd.pd3.into_floating_input(); - let out_pin = out_pin.downgrade(); - let in_pin = InputDynPinAsync::new(in_pin.downgrade()).unwrap(); + let out_pin = Output::new(portd.pd2, PinState::Low); + let in_pin = Input::new_floating(portd.pd3); + let in_pin = InputPinAsync::new(in_pin).unwrap(); spawner .spawn(output_task( "PD2 to PD3", @@ -168,10 +151,9 @@ async fn main(spawner: Spawner) { } if CHECK_PE0_TO_PE1 { - let out_pin = porte.pe0.into_readable_push_pull_output(); - let in_pin = porte.pe1.into_floating_input(); - let out_pin = out_pin.downgrade(); - let in_pin = InputDynPinAsync::new(in_pin.downgrade()).unwrap(); + let out_pin = Output::new(porte.pe0, PinState::Low); + let in_pin = Input::new_floating(porte.pe1); + let in_pin = InputPinAsync::new(in_pin).unwrap(); spawner .spawn(output_task( "PE0 to PE1", @@ -184,10 +166,9 @@ async fn main(spawner: Spawner) { } if CHECK_PF0_TO_PF1 { - let out_pin = portf.pf0.into_readable_push_pull_output(); - let in_pin = portf.pf1.into_floating_input(); - let out_pin = out_pin.downgrade(); - let in_pin = InputDynPinAsync::new(in_pin.downgrade()).unwrap(); + let out_pin = Output::new(portf.pf0, PinState::Low); + let in_pin = Input::new_floating(portf.pf1); + let in_pin = InputPinAsync::new(in_pin).unwrap(); spawner .spawn(output_task( "PF0 to PF1", @@ -298,7 +279,7 @@ async fn check_pin_to_pin_async_ops( #[embassy_executor::task(pool_size = 8)] async fn output_task( ctx: &'static str, - mut out: DynPin, + mut out: Output, receiver: Receiver<'static, ThreadModeRawMutex, GpioCmd, 3>, ) { loop { @@ -307,25 +288,25 @@ async fn output_task( match next_cmd.cmd_type { GpioCmdType::SetHigh => { defmt::info!("{}: Set output high", ctx); - out.set_high().unwrap(); + out.set_high(); } GpioCmdType::SetLow => { defmt::info!("{}: Set output low", ctx); - out.set_low().unwrap(); + out.set_low(); } GpioCmdType::RisingEdge => { defmt::info!("{}: Rising edge", ctx); - if !out.is_low().unwrap() { - out.set_low().unwrap(); + if !out.is_set_low() { + out.set_low(); } - out.set_high().unwrap(); + out.set_high(); } GpioCmdType::FallingEdge => { defmt::info!("{}: Falling edge", ctx); - if !out.is_high().unwrap() { - out.set_high().unwrap(); + if !out.is_set_high() { + out.set_high(); } - out.set_low().unwrap(); + out.set_low(); } GpioCmdType::CloseTask => { defmt::info!("{}: Closing task", ctx); diff --git a/examples/embassy/src/bin/async-uart-rx.rs b/examples/embassy/src/bin/async-uart-rx.rs index 4f3447d..53b6fc4 100644 --- a/examples/embassy/src/bin/async-uart-rx.rs +++ b/examples/embassy/src/bin/async-uart-rx.rs @@ -27,8 +27,10 @@ use embedded_io::Write; use embedded_io_async::Read; use heapless::spsc::{Producer, Queue}; use va416xx_hal::{ - gpio::PinsG, + clock::ClockConfigurator, + gpio::{Output, PinState}, pac::{self, interrupt}, + pins::PinsG, prelude::*, time::Hertz, uart::{ @@ -46,34 +48,22 @@ static PRODUCER_UART_A: Mutex>>> = Mutex::new(R async fn main(_spawner: Spawner) { 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 // Use the external clock connected to XTAL_N. - let clocks = dp - .clkgen - .constrain() + let clocks = ClockConfigurator::new(dp.clkgen) .xtal_n_clk_with_src_freq(Hertz::from_raw(EXTCLK_FREQ)) - .freeze(&mut dp.sysconfig) + .freeze() .unwrap(); // Safety: Only called once here. - unsafe { - va416xx_embassy::init( - &mut dp.sysconfig, - &dp.irq_router, - dp.tim15, - dp.tim14, - &clocks, - ); - } + va416xx_embassy::init(dp.tim15, dp.tim14, &clocks); - let portg = PinsG::new(&mut dp.sysconfig, dp.portg); - let mut led = portg.pg5.into_readable_push_pull_output(); + let portg = PinsG::new(dp.portg); + let mut led = Output::new(portg.pg5, PinState::Low); - let tx = portg.pg0.into_funsel_1(); - let rx = portg.pg1.into_funsel_1(); - - let uarta = uart::Uart::new(&mut dp.sysconfig, dp.uart0, (tx, rx), 115200.Hz(), &clocks); + let uarta = + uart::Uart::new(dp.uart0, portg.pg0, portg.pg1, &clocks, 115200.Hz().into()).unwrap(); let (mut tx_uart_a, rx_uart_a) = uarta.split(); let (prod_uart_a, cons_uart_a) = QUEUE_UART_A.take().split(); diff --git a/examples/embassy/src/bin/async-uart-tx.rs b/examples/embassy/src/bin/async-uart-tx.rs index c110182..ee4bae9 100644 --- a/examples/embassy/src/bin/async-uart-tx.rs +++ b/examples/embassy/src/bin/async-uart-tx.rs @@ -21,8 +21,10 @@ use embassy_executor::Spawner; use embassy_time::{Duration, Instant, Ticker}; use embedded_io_async::Write; use va416xx_hal::{ - gpio::PinsG, + clock::ClockConfigurator, + gpio::{Output, PinState}, pac::{self, interrupt}, + pins::PinsG, prelude::*, time::Hertz, uart::{ @@ -44,34 +46,22 @@ const STR_LIST: &[&str] = &[ async fn main(_spawner: Spawner) { 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 // Use the external clock connected to XTAL_N. - let clocks = dp - .clkgen - .constrain() + let clocks = ClockConfigurator::new(dp.clkgen) .xtal_n_clk_with_src_freq(Hertz::from_raw(EXTCLK_FREQ)) - .freeze(&mut dp.sysconfig) + .freeze() .unwrap(); // Safety: Only called once here. - unsafe { - va416xx_embassy::init( - &mut dp.sysconfig, - &dp.irq_router, - dp.tim15, - dp.tim14, - &clocks, - ); - } + va416xx_embassy::init(dp.tim15, dp.tim14, &clocks); - let portg = PinsG::new(&mut dp.sysconfig, dp.portg); - let mut led = portg.pg5.into_readable_push_pull_output(); + let pinsg = PinsG::new(dp.portg); + let mut led = Output::new(pinsg.pg5, PinState::Low); - let tx = portg.pg0.into_funsel_1(); - let rx = portg.pg1.into_funsel_1(); - - let uarta = uart::Uart::new(&mut dp.sysconfig, dp.uart0, (tx, rx), 115200.Hz(), &clocks); + let uarta = + uart::Uart::new(dp.uart0, pinsg.pg0, pinsg.pg1, &clocks, 115200.Hz().into()).unwrap(); let (tx, _rx) = uarta.split(); let mut async_tx = TxAsync::new(tx); let mut ticker = Ticker::every(Duration::from_secs(1)); diff --git a/examples/embassy/src/bin/uart-echo-with-irq.rs b/examples/embassy/src/bin/uart-echo-with-irq.rs index e8c6f22..769a471 100644 --- a/examples/embassy/src/bin/uart-echo-with-irq.rs +++ b/examples/embassy/src/bin/uart-echo-with-irq.rs @@ -27,15 +27,15 @@ use ringbuf::{ StaticRb, }; use va416xx_hal::{ - gpio::{OutputReadablePushPull, Pin, PinsG, PG5}, + clock::ClockConfigurator, + gpio::{Output, PinState}, pac::{self, interrupt}, - prelude::*, + pins::PinsG, time::Hertz, uart, }; -pub type SharedUart = - Mutex>>>; +pub type SharedUart = Mutex>>; static RX: SharedUart = Mutex::new(RefCell::new(None)); const BAUDRATE: u32 = 115200; @@ -54,39 +54,27 @@ static RINGBUF: SharedRingBuf = Mutex::new(RefCell::new(None)); async fn main(spawner: Spawner) { 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 // Use the external clock connected to XTAL_N. - let clocks = dp - .clkgen - .constrain() + let clocks = ClockConfigurator::new(dp.clkgen) .xtal_n_clk_with_src_freq(Hertz::from_raw(EXTCLK_FREQ)) - .freeze(&mut dp.sysconfig) + .freeze() .unwrap(); // Safety: Only called once here. - unsafe { - va416xx_embassy::init( - &mut dp.sysconfig, - &dp.irq_router, - dp.tim15, - dp.tim14, - &clocks, - ) - }; + va416xx_embassy::init(dp.tim15, dp.tim14, &clocks); - let portg = PinsG::new(&mut dp.sysconfig, dp.portg); - - let tx = portg.pg0.into_funsel_1(); - let rx = portg.pg1.into_funsel_1(); + let portg = PinsG::new(dp.portg); let uart0 = uart::Uart::new( - &mut dp.sysconfig, dp.uart0, - (tx, rx), - Hertz::from_raw(BAUDRATE), + portg.pg0, + portg.pg1, &clocks, - ); + Hertz::from_raw(BAUDRATE).into(), + ) + .unwrap(); let (mut tx, rx) = uart0.split(); let mut rx = rx.into_rx_with_irq(); rx.start(); @@ -97,7 +85,7 @@ async fn main(spawner: Spawner) { 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 processing_buf: [u8; RING_BUF_SIZE] = [0; RING_BUF_SIZE]; let mut read_bytes = 0; @@ -117,7 +105,7 @@ async fn main(spawner: Spawner) { } #[embassy_executor::task] -async fn blinky(mut led: Pin) { +async fn blinky(mut led: Output) { let mut ticker = Ticker::every(Duration::from_millis(500)); loop { led.toggle(); diff --git a/examples/embassy/src/main.rs b/examples/embassy/src/main.rs index f89d348..c6d3da0 100644 --- a/examples/embassy/src/main.rs +++ b/examples/embassy/src/main.rs @@ -8,7 +8,13 @@ use defmt_rtt as _; use embassy_example::EXTCLK_FREQ; use embassy_executor::Spawner; 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! { if #[cfg(feature = "custom-irqs")] { @@ -22,40 +28,32 @@ cfg_if::cfg_if! { async fn main(_spawner: Spawner) { 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 // Use the external clock connected to XTAL_N. - let clocks = dp - .clkgen - .constrain() + let clocks = ClockConfigurator::new(dp.clkgen) .xtal_n_clk_with_src_freq(Hertz::from_raw(EXTCLK_FREQ)) - .freeze(&mut dp.sysconfig) + .freeze() .unwrap(); // Safety: Only called once here. - unsafe { - cfg_if::cfg_if! { - if #[cfg(not(feature = "custom-irqs"))] { - va416xx_embassy::init( - &mut dp.sysconfig, - &dp.irq_router, - dp.tim15, - dp.tim14, - &clocks - ); - } else { - va416xx_embassy::init( - &mut dp.sysconfig, - &dp.irq_router, - dp.tim12, - dp.tim11, - &clocks - ); - } + cfg_if::cfg_if! { + if #[cfg(not(feature = "custom-irqs"))] { + va416xx_embassy::init( + dp.tim15, + dp.tim14, + &clocks + ); + } else { + va416xx_embassy::init( + dp.tim12, + dp.tim11, + &clocks + ); } } - let portg = PinsG::new(&mut dp.sysconfig, dp.portg); - let mut led = portg.pg5.into_readable_push_pull_output(); + let pinsg = PinsG::new(dp.portg); + let mut led = Output::new(pinsg.pg5, PinState::Low); let mut ticker = Ticker::every(Duration::from_secs(1)); loop { ticker.next().await; diff --git a/examples/rtic/Cargo.toml b/examples/rtic/Cargo.toml index f04fd4b..d5c40bd 100644 --- a/examples/rtic/Cargo.toml +++ b/examples/rtic/Cargo.toml @@ -5,14 +5,11 @@ edition = "2021" [dependencies] cortex-m = { version = "0.7", features = ["critical-section-single-core"] } -cortex-m-rt = "0.7" -embedded-hal = "1" defmt-rtt = "0.4" defmt = "1" 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] version = "2" diff --git a/examples/rtic/src/main.rs b/examples/rtic/src/main.rs index d2bbc6c..5e2e9d7 100644 --- a/examples/rtic/src/main.rs +++ b/examples/rtic/src/main.rs @@ -17,14 +17,15 @@ mod app { use rtic_monotonics::systick::prelude::*; use rtic_monotonics::Monotonic; use va416xx_hal::{ - gpio::{OutputReadablePushPull, Pin, PinsG, PG5}, + clock::ClockConfigurator, + gpio::{Output, PinState}, pac, - prelude::*, + pins::PinsG, }; #[local] struct Local { - led: Pin, + led: Output, } #[shared] @@ -33,19 +34,16 @@ mod app { rtic_monotonics::systick_monotonic!(Mono, 1_000); #[init] - fn init(mut cx: init::Context) -> (Shared, Local) { + fn init(cx: init::Context) -> (Shared, Local) { defmt::println!("-- Vorago RTIC example application --"); // Use the external clock connected to XTAL_N. - let clocks = cx - .device - .clkgen - .constrain() + let clocks = ClockConfigurator::new(cx.device.clkgen) .xtal_n_clk_with_src_freq(EXTCLK_FREQ) - .freeze(&mut cx.device.sysconfig) + .freeze() .unwrap(); Mono::start(cx.core.SYST, clocks.sysclk().raw()); - let portg = PinsG::new(&mut cx.device.sysconfig, cx.device.portg); - let led = portg.pg5.into_readable_push_pull_output(); + let pinsg = PinsG::new(cx.device.portg); + let led = Output::new(pinsg.pg5, PinState::Low); blinky::spawn().ok(); (Shared {}, Local { led }) } diff --git a/examples/simple/Cargo.toml b/examples/simple/Cargo.toml index c2dbf7a..adbd244 100644 --- a/examples/simple/Cargo.toml +++ b/examples/simple/Cargo.toml @@ -17,20 +17,12 @@ embedded-io = "0.6" panic-halt = "1" 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] path = "../../vorago-peb1" optional = true -[dependencies.rtic] -version = "2" -features = ["thumbv7-backend"] - -[dependencies.rtic-monotonics] -version = "2" -features = ["cortex-m-systick"] - [features] default = ["va41630"] va41630 = ["va416xx-hal/va41630", "has-adc-dac"] diff --git a/examples/simple/examples/adc.rs b/examples/simple/examples/adc.rs index 3321a1f..fe11eff 100644 --- a/examples/simple/examples/adc.rs +++ b/examples/simple/examples/adc.rs @@ -12,8 +12,8 @@ use embedded_hal::delay::DelayNs; use simple_examples::peb1; use va416xx_hal::{ adc::{Adc, ChannelSelect, ChannelValue, MultiChannelSelect}, + clock::ClockConfigurator, pac, - prelude::*, timer::CountdownTimer, }; @@ -24,17 +24,15 @@ const ENABLE_BUF_PRINTOUT: bool = false; fn main() -> ! { 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. - let clocks = dp - .clkgen - .constrain() + let clocks = ClockConfigurator::new(dp.clkgen) .xtal_n_clk_with_src_freq(peb1::EXTCLK_FREQ) - .freeze(&mut dp.sysconfig) + .freeze() .unwrap(); - let adc = Adc::new_with_channel_tag(&mut dp.sysconfig, dp.adc, &clocks); - let mut delay_provider = CountdownTimer::new(&mut dp.sysconfig, dp.tim0, &clocks); + let adc = Adc::new_with_channel_tag(dp.adc, &clocks); + let mut delay = CountdownTimer::new(dp.tim0, &clocks); let mut read_buf: [ChannelValue; 8] = [ChannelValue::default(); 8]; loop { let single_value = adc @@ -70,6 +68,6 @@ fn main() -> ! { assert_eq!(read_buf[0].channel(), ChannelSelect::AnIn0); assert_eq!(read_buf[1].channel(), ChannelSelect::AnIn2); assert_eq!(read_buf[2].channel(), ChannelSelect::TempSensor); - delay_provider.delay_ms(500); + delay.delay_ms(500); } } diff --git a/examples/simple/examples/blinky.rs b/examples/simple/examples/blinky.rs index 57bb4cc..2c29855 100644 --- a/examples/simple/examples/blinky.rs +++ b/examples/simple/examples/blinky.rs @@ -8,15 +8,19 @@ use panic_probe as _; use defmt_rtt as _; use cortex_m_rt::entry; -use va416xx_hal::{gpio::PinsG, pac}; +use va416xx_hal::{ + gpio::{Output, PinState}, + pac, + pins::PinsG, +}; #[entry] fn main() -> ! { defmt::println!("VA416xx HAL blinky example"); - let mut dp = pac::Peripherals::take().unwrap(); - let portg = PinsG::new(&mut dp.sysconfig, dp.portg); - let mut led = portg.pg5.into_readable_push_pull_output(); + let dp = pac::Peripherals::take().unwrap(); + let portg = PinsG::new(dp.portg); + let mut led = Output::new(portg.pg5, PinState::Low); loop { cortex_m::asm::delay(2_000_000); led.toggle(); diff --git a/examples/simple/examples/dac-adc.rs b/examples/simple/examples/dac-adc.rs index 411fd80..3d326fe 100644 --- a/examples/simple/examples/dac-adc.rs +++ b/examples/simple/examples/dac-adc.rs @@ -10,7 +10,7 @@ use defmt_rtt as _; use cortex_m_rt::entry; use embedded_hal::delay::DelayNs; 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; @@ -30,18 +30,15 @@ const APP_MODE: AppMode = AppMode::DacAndAdc; fn main() -> ! { 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. - let clocks = dp - .clkgen - .constrain() + let clocks = ClockConfigurator::new(dp.clkgen) .xtal_n_clk_with_src_freq(peb1::EXTCLK_FREQ) - .freeze(&mut dp.sysconfig) + .freeze() .unwrap(); let mut dac = None; if APP_MODE == AppMode::DacOnly || APP_MODE == AppMode::DacAndAdc { dac = Some(Dac::new( - &mut dp.sysconfig, dp.dac0, va416xx_hal::dac::DacSettling::Apb2Times100, &clocks, @@ -49,12 +46,12 @@ fn main() -> ! { } let mut adc = None; 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; loop { - if let Some(dac) = &dac { + if let Some(dac) = &mut dac { defmt::info!("loading DAC with value {}", current_val); dac.load_and_trigger_manually(current_val) .expect("loading DAC value failed"); diff --git a/examples/simple/examples/dma.rs b/examples/simple/examples/dma.rs index e49df7a..bdd36aa 100644 --- a/examples/simple/examples/dma.rs +++ b/examples/simple/examples/dma.rs @@ -6,6 +6,7 @@ use panic_probe as _; // Import logger. use defmt_rtt as _; +use va416xx_hal::clock::ClockConfigurator; use core::cell::Cell; @@ -15,11 +16,8 @@ use embedded_hal::delay::DelayNs; use simple_examples::peb1; use va416xx_hal::dma::{Dma, DmaCfg, DmaChannel, DmaCtrlBlock}; use va416xx_hal::irq_router::enable_and_init_irq_router; +use va416xx_hal::pac::{self, interrupt}; use va416xx_hal::timer::CountdownTimer; -use va416xx_hal::{ - pac::{self, interrupt}, - prelude::*, -}; static DMA_DONE_FLAG: Mutex> = Mutex::new(Cell::new(false)); static DMA_ACTIVE_FLAG: Mutex> = Mutex::new(Cell::new(false)); @@ -40,26 +38,23 @@ static mut DMA_DEST_BUF: [u16; 36] = [0; 36]; fn main() -> ! { 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. - let clocks = dp - .clkgen - .constrain() + let clocks = ClockConfigurator::new(dp.clkgen) .xtal_n_clk_with_src_freq(peb1::EXTCLK_FREQ) - .freeze(&mut dp.sysconfig) + .freeze() .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 // statically. let dma = Dma::new( - &mut dp.sysconfig, dp.dma, DmaCfg::default(), core::ptr::addr_of_mut!(DMA_CTRL_BLOCK), ) .expect("error creating DMA"); 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 dest_buf_8_bit: [u8; 65] = [0; 65]; let mut src_buf_32_bit: [u32; 17] = [0; 17]; @@ -90,7 +85,7 @@ fn transfer_example_8_bit( src_buf: &mut [u8; 65], dest_buf: &mut [u8; 65], dma0: &mut DmaChannel, - delay_ms: &mut CountdownTimer, + delay: &mut CountdownTimer, ) { (0..64).for_each(|i| { src_buf[i] = i as u8; @@ -132,7 +127,7 @@ fn transfer_example_8_bit( defmt::info!("8-bit transfer done"); break; } - delay_ms.delay_ms(1); + delay.delay_ms(1); } (0..64).for_each(|i| { assert_eq!(dest_buf[i], i as u8); @@ -142,7 +137,7 @@ fn transfer_example_8_bit( dest_buf.fill(0); } -fn transfer_example_16_bit(dma0: &mut DmaChannel, delay_ms: &mut CountdownTimer) { +fn transfer_example_16_bit(dma0: &mut DmaChannel, delay_ms: &mut CountdownTimer) { let dest_buf_ref = unsafe { &mut *core::ptr::addr_of_mut!(DMA_DEST_BUF[0..33]) }; unsafe { // 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], dest_buf: &mut [u32; 17], dma0: &mut DmaChannel, - delay_ms: &mut CountdownTimer, + delay_ms: &mut CountdownTimer, ) { // Set values scaled from 0 to 65535 to verify this is really a 16-bit transfer. (0..16).for_each(|i| { diff --git a/examples/simple/examples/peb1-accelerometer.rs b/examples/simple/examples/peb1-accelerometer.rs index 533c8de..3e19cf2 100644 --- a/examples/simple/examples/peb1-accelerometer.rs +++ b/examples/simple/examples/peb1-accelerometer.rs @@ -12,10 +12,11 @@ use cortex_m_rt::entry; use embedded_hal::delay::DelayNs; use simple_examples::peb1; use va416xx_hal::{ + clock::ClockConfigurator, i2c, pac::{self}, prelude::*, - pwm::CountdownTimer, + timer::CountdownTimer, }; use vorago_peb1::lis2dh12::{self, detect_i2c_addr, FullScale, Odr}; @@ -31,21 +32,18 @@ fn main() -> ! { let mut dp = pac::Peripherals::take().unwrap(); defmt::println!("-- Vorago PEB1 accelerometer example --"); // Use the external clock connected to XTAL_N. - let clocks = dp - .clkgen - .constrain() + let clocks = ClockConfigurator::new(dp.clkgen) .xtal_n_clk_with_src_freq(peb1::EXTCLK_FREQ) - .freeze(&mut dp.sysconfig) + .freeze() .unwrap(); let mut i2c_master = i2c::I2cMaster::new( dp.i2c0, - &mut dp.sysconfig, - i2c::MasterConfig::default(), &clocks, + i2c::MasterConfig::default(), i2c::I2cSpeed::Regular100khz, ) .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. let slave_addr = detect_i2c_addr(&mut i2c_master).expect("detecting I2C address failed"); // Create the accelerometer driver using the PEB1 BSP. @@ -53,7 +51,7 @@ fn main() -> ! { .expect("creating accelerometer driver failed"); let device_id = accelerometer.get_device_id().unwrap(); accelerometer - .set_mode(lis2dh12::reg::Mode::Normal) + .set_mode(lis2dh12::Mode::Normal) .expect("setting mode failed"); accelerometer .set_odr(Odr::Hz100) @@ -65,7 +63,7 @@ fn main() -> ! { accelerometer .enable_temp(true) .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. loop { let temperature = accelerometer @@ -76,13 +74,25 @@ fn main() -> ! { let value = accelerometer .accel_norm() .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 => { let value_raw = accelerometer .accel_raw() .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); diff --git a/examples/simple/examples/pwm.rs b/examples/simple/examples/pwm.rs index e439735..b5a76f1 100644 --- a/examples/simple/examples/pwm.rs +++ b/examples/simple/examples/pwm.rs @@ -1,4 +1,6 @@ //! Simple PWM example +//! +//! Outputs a PWM waveform on pin PG2. #![no_main] #![no_std] @@ -10,41 +12,34 @@ use panic_probe as _; use defmt_rtt as _; use simple_examples::peb1; use va416xx_hal::{ - gpio::PinsA, + clock::ClockConfigurator, pac, + pins::PinsG, prelude::*, - pwm::{self, get_duty_from_percent, PwmA, PwmB, ReducedPwmPin}, + pwm::{get_duty_from_percent, PwmA, PwmB, PwmPin}, timer::CountdownTimer, }; #[entry] fn main() -> ! { 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. - let clocks = dp - .clkgen - .constrain() + let clocks = ClockConfigurator::new(dp.clkgen) .xtal_n_clk_with_src_freq(peb1::EXTCLK_FREQ) - .freeze(&mut dp.sysconfig) + .freeze() .unwrap(); - let pinsa = PinsA::new(&mut dp.sysconfig, dp.porta); - let mut pwm = pwm::PwmPin::new( - (pinsa.pa3.into_funsel_1(), dp.tim3), - &mut dp.sysconfig, - &clocks, - 10.Hz(), - ); - let mut delay_timer = CountdownTimer::new(&mut dp.sysconfig, dp.tim0, &clocks); + let pinsg = PinsG::new(dp.portg); + let mut pwm = PwmPin::new(pinsg.pg2, dp.tim9, &clocks, 10.Hz()).unwrap(); + let mut delay_timer = CountdownTimer::new(dp.tim0, &clocks); let mut current_duty_cycle = 0.0; pwm.set_duty_cycle(get_duty_from_percent(current_duty_cycle)) .unwrap(); pwm.enable(); // Delete type information, increased code readibility for the rest of the code - let mut reduced_pin = ReducedPwmPin::from(pwm); loop { let mut counter = 0; // Increase duty cycle continuously @@ -56,8 +51,7 @@ fn main() -> ! { defmt::info!("current duty cycle: {}", current_duty_cycle); } - reduced_pin - .set_duty_cycle(get_duty_from_percent(current_duty_cycle)) + pwm.set_duty_cycle(get_duty_from_percent(current_duty_cycle)) .unwrap(); } @@ -66,7 +60,7 @@ fn main() -> ! { current_duty_cycle = 0.0; let mut upper_limit = 1.0; let mut lower_limit = 0.0; - let mut pwmb: ReducedPwmPin = ReducedPwmPin::from(reduced_pin); + let mut pwmb: PwmPin = PwmPin::from(pwm); pwmb.set_pwmb_lower_limit(get_duty_from_percent(lower_limit)); pwmb.set_pwmb_upper_limit(get_duty_from_percent(upper_limit)); while lower_limit < 0.5 { @@ -78,6 +72,6 @@ fn main() -> ! { defmt::info!("Lower limit: {}", pwmb.pwmb_lower_limit()); defmt::info!("Upper limit: {}", pwmb.pwmb_upper_limit()); } - reduced_pin = ReducedPwmPin::::from(pwmb); + pwm = PwmPin::::from(pwmb); } } diff --git a/examples/simple/examples/rtt-log.rs b/examples/simple/examples/rtt-log.rs deleted file mode 100644 index bbf8ac7..0000000 --- a/examples/simple/examples/rtt-log.rs +++ /dev/null @@ -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); - } -} diff --git a/examples/simple/examples/spi.rs b/examples/simple/examples/spi.rs index 26038d0..7196999 100644 --- a/examples/simple/examples/spi.rs +++ b/examples/simple/examples/spi.rs @@ -3,6 +3,7 @@ //! If you do not use the loopback mode, MOSI and MISO need to be tied together on the board. #![no_main] #![no_std] +use embedded_hal::delay::DelayNs; // Import panic provider. use panic_probe as _; // Import logger. @@ -11,11 +12,12 @@ use defmt_rtt as _; use cortex_m_rt::entry; use embedded_hal::spi::{Mode, SpiBus, MODE_0}; use simple_examples::peb1; +use va416xx_hal::clock::ClockConfigurator; use va416xx_hal::spi::{Spi, SpiClkConfig}; +use va416xx_hal::timer::CountdownTimer; use va416xx_hal::{ - gpio::{PinsB, PinsC}, pac, - prelude::*, + pins::{PinsB, PinsC}, spi::SpiConfig, time::Hertz, }; @@ -37,29 +39,20 @@ const FILL_WORD: u8 = 0x0f; #[entry] fn main() -> ! { defmt::println!("-- VA108xx SPI example application--"); - 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. - let clocks = dp - .clkgen - .constrain() + let clocks = ClockConfigurator::new(dp.clkgen) .xtal_n_clk_with_src_freq(peb1::EXTCLK_FREQ) - .freeze(&mut dp.sysconfig) + .freeze() .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_c = PinsC::new(&mut dp.sysconfig, dp.portc); - // Configure SPI0 pins. - let (sck, miso, mosi) = ( - pins_b.pb15.into_funsel_1(), - pins_c.pc0.into_funsel_1(), - pins_c.pc1.into_funsel_1(), - ); + let pins_b = PinsB::new(dp.portb); + let pins_c = PinsC::new(dp.portc); let mut spi_cfg = SpiConfig::default() .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"), ) .mode(SPI_MODE) @@ -68,13 +61,7 @@ fn main() -> ! { spi_cfg = spi_cfg.loopback(true) } // Create SPI peripheral. - let mut spi0 = Spi::new( - &mut dp.sysconfig, - &clocks, - dp.spi0, - (sck, miso, mosi), - spi_cfg, - ); + let mut spi0 = Spi::new(dp.spi0, (pins_b.pb15, pins_c.pc0, pins_c.pc1), spi_cfg).unwrap(); spi0.set_fill_word(FILL_WORD); loop { let tx_buf: [u8; 4] = [1, 2, 3, 0]; @@ -95,6 +82,6 @@ fn main() -> ! { spi0.transfer(&mut rx_buf, &tx_buf) .expect("SPI transfer failed"); assert_eq!(rx_buf, [1, 2, 3, 0]); - delay_sysclk.delay_ms(500); + delay.delay_ms(500); } } diff --git a/examples/simple/examples/timer-ticks.rs b/examples/simple/examples/timer-ticks.rs index 353bf34..a9cda46 100644 --- a/examples/simple/examples/timer-ticks.rs +++ b/examples/simple/examples/timer-ticks.rs @@ -6,16 +6,16 @@ use panic_probe as _; // Import logger. use defmt_rtt as _; -use core::cell::Cell; +use core::sync::atomic::{AtomicU32, Ordering}; use cortex_m::asm; use cortex_m_rt::entry; -use critical_section::Mutex; use simple_examples::peb1; use va416xx_hal::{ + clock::ClockConfigurator, irq_router::enable_and_init_irq_router, pac::{self, interrupt}, prelude::*, - timer::{default_ms_irq_handler, set_up_ms_tick, CountdownTimer, MS_COUNTER}, + timer::CountdownTimer, }; #[allow(dead_code)] @@ -24,32 +24,33 @@ enum LibType { Hal, } -static SEC_COUNTER: Mutex> = Mutex::new(Cell::new(0)); +static MS_COUNTER: AtomicU32 = AtomicU32::new(0); +static SEC_COUNTER: AtomicU32 = AtomicU32::new(0); #[entry] fn main() -> ! { - let mut dp = pac::Peripherals::take().unwrap(); + let dp = pac::Peripherals::take().unwrap(); let mut last_ms = 0; defmt::println!("-- Vorago system ticks using timers --"); // Use the external clock connected to XTAL_N. - let clocks = dp - .clkgen - .constrain() + let clocks = ClockConfigurator::new(dp.clkgen) .xtal_n_clk_with_src_freq(peb1::EXTCLK_FREQ) - .freeze(&mut dp.sysconfig) + .freeze() .unwrap(); - enable_and_init_irq_router(&mut dp.sysconfig, &dp.irq_router); - let _ = set_up_ms_tick(&mut dp.sysconfig, dp.tim0, &clocks); - let mut second_timer = CountdownTimer::new(&mut dp.sysconfig, dp.tim1, &clocks); - second_timer.listen(); + enable_and_init_irq_router(); + let mut ms_timer = CountdownTimer::new(dp.tim0, &clocks); + ms_timer.enable_interrupt(true); + ms_timer.start(1.Hz()); + let mut second_timer = CountdownTimer::new(dp.tim1, &clocks); + second_timer.enable_interrupt(true); second_timer.start(1.Hz()); 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 { // To prevent drift. last_ms += 1000; 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); } asm::delay(1000); @@ -59,15 +60,11 @@ fn main() -> ! { #[interrupt] #[allow(non_snake_case)] fn TIM0() { - default_ms_irq_handler() + MS_COUNTER.fetch_add(1, Ordering::Relaxed); } #[interrupt] #[allow(non_snake_case)] fn TIM1() { - critical_section::with(|cs| { - let mut sec = SEC_COUNTER.borrow(cs).get(); - sec += 1; - SEC_COUNTER.borrow(cs).set(sec); - }); + SEC_COUNTER.fetch_add(1, Ordering::Relaxed); } diff --git a/examples/simple/examples/uart.rs b/examples/simple/examples/uart.rs index f7c722f..e2308e9 100644 --- a/examples/simple/examples/uart.rs +++ b/examples/simple/examples/uart.rs @@ -11,35 +11,33 @@ use cortex_m_rt::entry; use embedded_hal_nb::serial::Read; use embedded_io::Write; 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::{gpio::PinsG, pac, uart}; +use va416xx_hal::{pac, uart}; #[entry] fn main() -> ! { 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. - let clocks = dp - .clkgen - .constrain() + let clocks = ClockConfigurator::new(dp.clkgen) .xtal_n_clk_with_src_freq(peb1::EXTCLK_FREQ) - .freeze(&mut dp.sysconfig) + .freeze() .unwrap(); - let gpiob = PinsG::new(&mut dp.sysconfig, dp.portg); - let tx = gpiob.pg0.into_funsel_1(); - let rx = gpiob.pg1.into_funsel_1(); + let gpiog = PinsG::new(dp.portg); let uart0 = uart::Uart::new( - &mut dp.sysconfig, dp.uart0, - (tx, rx), - Hertz::from_raw(115200), + gpiog.pg0, + gpiog.pg1, &clocks, - ); + Hertz::from_raw(115200).into(), + ) + .unwrap(); let (mut tx, mut rx) = uart0.split(); writeln!(tx, "Hello World\n\r").unwrap(); loop { diff --git a/examples/simple/examples/wdt.rs b/examples/simple/examples/wdt.rs index f9d749a..3fa3119 100644 --- a/examples/simple/examples/wdt.rs +++ b/examples/simple/examples/wdt.rs @@ -5,17 +5,16 @@ use panic_probe as _; // Import logger. 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 critical_section::Mutex; use simple_examples::peb1; use va416xx_hal::irq_router::enable_and_init_irq_router; use va416xx_hal::pac::{self, interrupt}; -use va416xx_hal::prelude::*; use va416xx_hal::wdt::Wdt; -static WDT_INTRPT_COUNT: Mutex> = Mutex::new(Cell::new(0)); +static WDT_INTRPT_COUNT: AtomicU32 = AtomicU32::new(0); #[derive(Debug, PartialEq, Eq)] #[allow(dead_code)] @@ -33,33 +32,37 @@ const WDT_ROLLOVER_MS: u32 = 100; fn main() -> ! { defmt::println!("-- VA416xx WDT example application--"); 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. - let clocks = dp - .clkgen - .constrain() + let clocks = ClockConfigurator::new(dp.clkgen) .xtal_n_clk_with_src_freq(peb1::EXTCLK_FREQ) - .freeze(&mut dp.sysconfig) + .freeze() .unwrap(); - enable_and_init_irq_router(&mut dp.sysconfig, &dp.irq_router); - let mut delay_sysclk = cortex_m::delay::Delay::new(cp.SYST, clocks.apb0().raw()); + enable_and_init_irq_router(); + let mut delay = cortex_m::delay::Delay::new(cp.SYST, clocks.apb0().raw()); 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(); + let log_divisor = 25; + let mut counter: u32 = 0; loop { + counter = counter.wrapping_add(1); + if counter % log_divisor == 0 { + defmt::info!("wdt example main loop alive"); + } if TEST_MODE != TestMode::AllowReset { 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 { defmt::info!("interrupt counter has increased to {}", interrupt_counter); last_interrupt_counter = interrupt_counter; } match TEST_MODE { - TestMode::FedByMain => delay_sysclk.delay_ms(WDT_ROLLOVER_MS / 5), - TestMode::FedByIrq => delay_sysclk.delay_ms(WDT_ROLLOVER_MS), + TestMode::FedByMain => delay.delay_ms(WDT_ROLLOVER_MS / 5), + TestMode::FedByIrq => delay.delay_ms(WDT_ROLLOVER_MS), _ => (), } } @@ -68,11 +71,7 @@ fn main() -> ! { #[interrupt] #[allow(non_snake_case)] fn WATCHDOG() { - critical_section::with(|cs| { - WDT_INTRPT_COUNT - .borrow(cs) - .set(WDT_INTRPT_COUNT.borrow(cs).get() + 1); - }); + WDT_INTRPT_COUNT.fetch_add(1, Ordering::Relaxed); let wdt = unsafe { pac::WatchDog::steal() }; // Clear interrupt. if TEST_MODE != TestMode::AllowReset { diff --git a/flashloader/Cargo.toml b/flashloader/Cargo.toml index b497c68..6c3493d 100644 --- a/flashloader/Cargo.toml +++ b/flashloader/Cargo.toml @@ -5,16 +5,10 @@ edition = "2021" [dependencies] cortex-m = "0.7" -cortex-m-rt = "0.7" -embedded-hal = "1" -embedded-hal-nb = "1" embedded-io = "0.6" defmt-rtt = "0.4" defmt = "1" panic-probe = { version = "1", features = ["defmt"] } -log = "0.4" -crc = "3" -rtic-sync = "1" static_cell = "2" satrs = { version = "0.3.0-alpha.0", 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"] } 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-monotonics = { version = "2", features = ["cortex-m-systick"] } diff --git a/flashloader/image-loader.py b/flashloader/image-loader.py index 2310042..336d342 100755 --- a/flashloader/image-loader.py +++ b/flashloader/image-loader.py @@ -99,7 +99,7 @@ class ImageLoader: ) self.verificator.add_tc(action_tc) 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): _LOGGER.info("Sending ping command") @@ -112,7 +112,7 @@ class ImageLoader: ) self.verificator.add_tc(ping_tc) 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): 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 now = time.time() while time.time() - now < 2.0: - if not self.com_if.data_available(): + if self.com_if.data_available() == 0: time.sleep(0.2) continue for reply in self.com_if.receive(): diff --git a/flashloader/src/main.rs b/flashloader/src/main.rs index f3dbf5c..189dac0 100644 --- a/flashloader/src/main.rs +++ b/flashloader/src/main.rs @@ -32,6 +32,7 @@ const MAX_TM_FRAME_SIZE: usize = cobs::max_encoding_length(MAX_TM_SIZE); const UART_BAUDRATE: u32 = 115200; const BOOT_NVM_MEMORY_ID: u8 = 1; const RX_DEBUGGING: bool = false; +const TX_DEBUGGING: bool = false; pub enum ActionId { CorruptImageA = 128, @@ -105,14 +106,14 @@ mod app { use spacepackets::ecss::{ 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::uart::IrqContextTimeoutOrMaxSize; use va416xx_hal::{ - clock::ClkgenExt, edac, - gpio::PinsG, nvm::Nvm, pac, + pins::PinsG, uart::{self, Uart}, }; @@ -128,8 +129,8 @@ mod app { #[local] struct Local { - uart_rx: uart::RxWithInterrupt, - uart_tx: uart::Tx, + uart_rx: uart::RxWithInterrupt, + uart_tx: uart::Tx, rx_context: IrqContextTimeoutOrMaxSize, rom_spi: Option, // We handle all TM in one task. @@ -154,28 +155,24 @@ mod app { defmt::println!("-- Vorago flashloader --"); // Initialize the systick interrupt & obtain the token to prove that we did // Use the external clock connected to XTAL_N. - let clocks = cx - .device - .clkgen - .constrain() + let clocks = ClockConfigurator::new(cx.device.clkgen) .xtal_n_clk_with_src_freq(Hertz::from_raw(EXTCLK_FREQ)) - .freeze(&mut cx.device.sysconfig) + .freeze() .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); - let gpiog = PinsG::new(&mut cx.device.sysconfig, cx.device.portg); - let tx = gpiog.pg0.into_funsel_1(); - let rx = gpiog.pg1.into_funsel_1(); + let gpiog = PinsG::new(cx.device.portg); let uart0 = Uart::new( - &mut cx.device.sysconfig, cx.device.uart0, - (tx, rx), - Hertz::from_raw(UART_BAUDRATE), + gpiog.pg0, + gpiog.pg1, &clocks, - ); + Hertz::from_raw(UART_BAUDRATE).into(), + ) + .unwrap(); let (tx, rx) = uart0.split(); let verif_reporter = VerificationReportCreator::new(0).unwrap(); @@ -259,8 +256,8 @@ mod app { { Ok(result) => { if RX_DEBUGGING { - log::debug!("RX Info: {:?}", cx.local.rx_context); - log::debug!("RX Result: {:?}", result); + defmt::info!("RX Info: {:?}", cx.local.rx_context); + defmt::info!("RX Result: {:?}", result); } if result.complete() { // Check frame validity (must have COBS format) and decode the frame. @@ -331,7 +328,7 @@ mod app { continue; } 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!( cx.local .tc_cons @@ -378,9 +375,7 @@ mod app { let mut corrupt_image = |base_addr: u32| { // Safety: We only use this for NVM handling and we only do NVM // handling here. - let mut sys_cfg = unsafe { pac::Sysconfig::steal() }; let nvm = Nvm::new( - &mut sys_cfg, cx.local.rom_spi.take().unwrap(), CLOCKS.get().as_ref().unwrap(), ); @@ -388,7 +383,7 @@ mod app { nvm.read_data(base_addr + 32, &mut buf); buf[0] += 1; 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 .local .verif_reporter @@ -406,7 +401,7 @@ mod app { } } 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 .local .verif_reporter @@ -453,31 +448,22 @@ mod app { return; } let data = &app_data[10..10 + data_len as usize]; - log::info!( - target: "TC Handler", - "writing {} bytes at offset {} to NVM", - data_len, - offset - ); + defmt::info!("writing {} bytes at offset {} to NVM", data_len, offset); // Safety: We only use this for NVM handling and we only do NVM // handling here. - let mut sys_cfg = unsafe { pac::Sysconfig::steal() }; let nvm = Nvm::new( - &mut sys_cfg, cx.local.rom_spi.take().unwrap(), CLOCKS.get().as_ref().unwrap(), ); 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 .local .verif_reporter .completion_success(cx.local.src_data_buf, started_token, 0, 0, &[]) .expect("completion success failed"); write_and_send(&tm); - log::info!( - target: "TC Handler", - "NVM operation done"); + defmt::info!("NVM operation done"); } } } @@ -506,9 +492,12 @@ mod app { &mut cx.local.encoded_buf[1..], ); cx.local.encoded_buf[send_size + 1] = 0; + if TX_DEBUGGING { + defmt::debug!("UART TX: Sending data with size {}", send_size + 2); + } cx.local .uart_tx - .write(&cx.local.encoded_buf[0..send_size + 2]) + .write_all(&cx.local.encoded_buf[0..send_size + 2]) .unwrap(); Mono::delay(2.millis()).await; } diff --git a/va416xx-embassy/Cargo.toml b/va416xx-embassy/Cargo.toml index bfc3053..30e7412 100644 --- a/va416xx-embassy/Cargo.toml +++ b/va416xx-embassy/Cargo.toml @@ -11,17 +11,8 @@ keywords = ["no-std", "hal", "cortex-m", "vorago", "va416xx"] categories = ["aerospace", "embedded", "no-std", "hardware-support"] [dependencies] -critical-section = "1" - -embassy-sync = "0.6" -embassy-executor = "0.7" -embassy-time-driver = "0.2" -embassy-time-queue-utils = "0.1" -portable-atomic = "1" - -once_cell = { version = "1", default-features = false, features = ["critical-section"] } - -va416xx-hal = { version = ">=0.4, <=0.5" } +vorago-shared-periphs = { git = "https://egit.irs.uni-stuttgart.de/rust/vorago-shared-periphs.git", features = ["vor4x"] } +va416xx-hal = { path = "../va416xx-hal" } [features] default = ["irq-tim14-tim15"] diff --git a/va416xx-embassy/src/lib.rs b/va416xx-embassy/src/lib.rs index b1f6864..5f350f5 100644 --- a/va416xx-embassy/src/lib.rs +++ b/va416xx-embassy/src/lib.rs @@ -21,11 +21,11 @@ //! 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] -//! for timekeeper +//! for timekeeper //! - `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] -//! for timekeeper +//! for timekeeper //! //! 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 @@ -38,34 +38,13 @@ //! [embassy example projects](https://egit.irs.uni-stuttgart.de/rust/va108xx-rs/src/branch/main/examples/embassy) #![no_std] #![cfg_attr(docsrs, feature(doc_auto_cfg))] -use core::{ - cell::{Cell, RefCell}, - 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::{ clock::Clocks, - enable_nvic_interrupt, irq_router::enable_and_init_irq_router, pac::{self, interrupt}, - pwm::ValidTim, - timer::{ - assert_tim_reset_for_two_cycles, enable_tim_clk, get_tim_raw, TimRegInterface, - TIM_IRQ_OFFSET, - }, + timer::{TimMarker, TIM_IRQ_OFFSET}, }; - -time_driver_impl!( - static TIME_DRIVER: TimerDriver = TimerDriver { - periods: AtomicU32::new(0), - alarms: Mutex::new(AlarmState::new()), - queue: Mutex::new(RefCell::new(Queue::new())), -}); +use vorago_shared_periphs::embassy::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")] 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 /// /// 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 /// can only be checked at run-time, and a run-time assertion will panic on the embassy /// initialization in case of a missmatch. -/// -/// # 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, +pub fn init( timekeeper: TimekeeperTim, alarm: AlarmTim, clocks: &Clocks, ) { #[cfg(feature = "_irqs-in-lib")] assert_eq!( - TimekeeperTim::ID, + TimekeeperTim::ID.value(), TIMEKEEPER_IRQ as u8 - TIM_IRQ_OFFSET as u8, "Timekeeper TIM and IRQ missmatch" ); #[cfg(feature = "_irqs-in-lib")] assert_eq!( - AlarmTim::ID, + AlarmTim::ID.value(), ALARM_IRQ as u8 - TIM_IRQ_OFFSET as u8, "Alarm TIM and IRQ missmatch" ); - enable_and_init_irq_router(syscfg, irq_router); - TIME_DRIVER.init(syscfg, timekeeper, alarm, clocks) -} - -struct AlarmState { - timestamp: Cell, -} - -impl AlarmState { - const fn new() -> Self { - Self { - timestamp: Cell::new(u64::MAX), - } - } -} - -unsafe impl Send for AlarmState {} - -static SCALE: OnceCell = OnceCell::new(); -static TIMEKEEPER_TIM: OnceCell = OnceCell::new(); -static ALARM_TIM: OnceCell = OnceCell::new(); - -pub struct TimerDriver { - periods: AtomicU32, - /// Timestamp at which to fire alarm. u64::MAX if no alarm is scheduled. - alarms: Mutex, - queue: Mutex>, -} - -impl TimerDriver { - fn init( - &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()); - } - } - }) - } + enable_and_init_irq_router(); + time_driver().__init(timekeeper, alarm, clocks) } diff --git a/va416xx-hal/Cargo.toml b/va416xx-hal/Cargo.toml index 1a14d1e..9334b9c 100644 --- a/va416xx-hal/Cargo.toml +++ b/va416xx-hal/Cargo.toml @@ -12,39 +12,30 @@ categories = ["embedded", "no-std", "hardware-support"] [dependencies] 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 } +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 } [features] default = ["rt", "revb"] rt = ["va416xx/rt"] -defmt = ["dep:defmt", "fugit/defmt", "embedded-hal/defmt-03"] +defmt = ["dep:defmt", "fugit/defmt", "vorago-shared-periphs/defmt"] va41630 = ["device-selected"] va41620 = ["device-selected"] va41629 = ["device-selected"] -va41628 = ["device-selected"] +va41628 = ["device-selected", "vorago-shared-periphs/va41628"] device-selected = [] revb = [] diff --git a/va416xx-hal/src/adc.rs b/va416xx-hal/src/adc.rs index 2eec515..7d80a29 100644 --- a/va416xx-hal/src/adc.rs +++ b/va416xx-hal/src/adc.rs @@ -8,9 +8,9 @@ use core::marker::PhantomData; use crate::clock::Clocks; use crate::pac; -use crate::prelude::*; use crate::time::Hertz; 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_MAX_CLK: Hertz = Hertz::from_raw(12_500_000); @@ -151,8 +151,8 @@ pub struct Adc { impl Adc {} impl Adc { - pub fn new(syscfg: &mut pac::Sysconfig, adc: pac::Adc, clocks: &Clocks) -> Self { - Self::generic_new(syscfg, adc, clocks) + pub fn new(adc: pac::Adc, clocks: &Clocks) -> Self { + Self::generic_new(adc, clocks) } pub fn trigger_and_read_single_channel(&self, ch: ChannelSelect) -> Result { @@ -209,12 +209,8 @@ impl Adc { } impl Adc { - pub fn new_with_channel_tag( - syscfg: &mut pac::Sysconfig, - adc: pac::Adc, - clocks: &Clocks, - ) -> Self { - let mut adc = Self::generic_new(syscfg, adc, clocks); + pub fn new_with_channel_tag(adc: pac::Adc, clocks: &Clocks) -> Self { + let mut adc = Self::generic_new(adc, clocks); adc.enable_channel_tag(); adc } @@ -286,8 +282,8 @@ impl Adc { } impl Adc { - fn generic_new(syscfg: &mut pac::Sysconfig, adc: pac::Adc, _clocks: &Clocks) -> Self { - syscfg.enable_peripheral_clock(crate::clock::PeripheralSelect::Adc); + fn generic_new(adc: pac::Adc, _clocks: &Clocks) -> Self { + enable_peripheral_clock(PeripheralSelect::Adc); adc.ctrl().write(|w| unsafe { w.bits(0) }); let adc = Self { adc, diff --git a/va416xx-hal/src/clock.rs b/va416xx-hal/src/clock.rs index 6bdd7ee..a5c9b43 100644 --- a/va416xx-hal/src/clock.rs +++ b/va416xx-hal/src/clock.rs @@ -1,10 +1,10 @@ //! API for using the [crate::pac::Clkgen] peripheral. //! //! It also includes functionality to enable the peripheral clocks. -//! Calling [ClkgenExt::constrain] on the [crate::pac::Clkgen] peripheral generates the -//! [ClkgenCfgr] structure which can be used to configure and set up the clock. +//! Calling [ClockConfigurator::new] returns a builder structure which allows +//! 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. //! //! # Examples @@ -15,48 +15,11 @@ use crate::adc::ADC_MAX_CLK; use crate::pac; 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; -#[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)] #[cfg_attr(feature = "defmt", derive(defmt::Format))] pub enum FilterClkSel { @@ -70,81 +33,6 @@ pub enum FilterClkSel { 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. #[derive(Debug, Copy, Clone, PartialEq, Eq)] #[cfg_attr(feature = "defmt", derive(defmt::Format))] @@ -223,12 +111,12 @@ pub fn pll_setup_delay() { } pub trait ClkgenExt { - fn constrain(self) -> ClkgenCfgr; + fn constrain(self) -> ClockConfigurator; } impl ClkgenExt for pac::Clkgen { - fn constrain(self) -> ClkgenCfgr { - ClkgenCfgr { + fn constrain(self) -> ClockConfigurator { + ClockConfigurator { source_clk: None, ref_clk_sel: RefClkSel::None, 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, - pll_cfg: Option, - 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)] #[cfg_attr(feature = "defmt", derive(defmt::Format))] pub struct ClkSourceFreqNotSet; @@ -269,6 +142,21 @@ pub enum ClkCfgError { 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, + pll_cfg: Option, + 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. /// /// 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] pub fn source_clk(mut self, src_clk: Hertz) -> Self { 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 /// process only has to be performed once, and it does not matter if it takes a few /// microseconds or milliseconds longer. - pub fn freeze(self, syscfg: &mut pac::Sysconfig) -> Result { + pub fn freeze(self) -> Result { // Sanitize configuration. if self.source_clk.is_none() { return Err(ClkCfgError::ClkSourceFreqNotSet); @@ -349,7 +260,7 @@ impl ClkgenCfgr { return Err(ClkCfgError::PllConfigNotSet); } - syscfg.enable_peripheral_clock(PeripheralSelect::Clkgen); + enable_peripheral_clock(PeripheralSelect::Clkgen); let mut final_sysclk = self.source_clk.unwrap(); // 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 @@ -458,13 +369,11 @@ impl ClkgenCfgr { .ctrl0() .modify(|_, w| unsafe { w.clksel_sys().bits(self.clksel_sys as u8) }); - Ok(Clocks { - sysclk: final_sysclk, - apb1: final_sysclk / 2, - apb2: final_sysclk / 4, + Ok(Clocks::__new( + final_sysclk, #[cfg(not(feature = "va41628"))] - adc_clk: self.cfg_adc_clk_div(final_sysclk), - }) + self.cfg_adc_clk_div(final_sysclk), + )) } #[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() { rearm_sysclk_lost_with_periph(&unsafe { pac::Clkgen::steal() }) } diff --git a/va416xx-hal/src/dac.rs b/va416xx-hal/src/dac.rs index a009093..1d77c59 100644 --- a/va416xx-hal/src/dac.rs +++ b/va416xx-hal/src/dac.rs @@ -5,22 +5,24 @@ //! - [ADC and DAC example](https://github.com/us-irs/va416xx-rs/blob/main/examples/simple/examples/dac-adc.rs) use core::ops::Deref; -use crate::{ - clock::{Clocks, PeripheralSelect, SyscfgExt}, - pac, +use vorago_shared_periphs::{ + disable_peripheral_clock, enable_peripheral_clock, reset_peripheral_for_cycles, + PeripheralSelect, }; +use crate::{clock::Clocks, pac}; + pub type DacRegisterBlock = pac::dac0::RegisterBlock; /// Common trait implemented by all PAC peripheral access structures. The register block /// format is the same for all DAC blocks. -pub trait Instance: Deref { +pub trait DacMarker: Deref { const IDX: u8; fn ptr() -> *const DacRegisterBlock; } -impl Instance for pac::Dac0 { +impl DacMarker for pac::Dac0 { const IDX: u8 = 0; #[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; #[inline(always)] @@ -50,40 +52,37 @@ pub enum DacSettling { Apb2Times150 = 6, } -pub struct Dac { - dac: DacInstance, -} +pub struct Dac(*const DacRegisterBlock); #[derive(Debug, PartialEq, Eq, Copy, Clone)] #[cfg_attr(feature = "defmt", derive(defmt::Format))] pub struct ValueTooLarge; -impl Dac { +impl Dac { /// Create a new [Dac] driver instance. /// /// The [Clocks] structure is expected here as well to ensure the clock was set up properly. - pub fn new( - syscfg: &mut pac::Sysconfig, - dac: DacInstance, - dac_settling: DacSettling, - _clocks: &Clocks, - ) -> Self { - syscfg.enable_peripheral_clock(PeripheralSelect::Dac); + pub fn new(dac: Dac, dac_settling: DacSettling, _clocks: &Clocks) -> Self { + enable_peripheral_clock(PeripheralSelect::Dac); dac.ctrl1().write(|w| { w.dac_en().set_bit(); // SAFETY: Enum values are valid values only. unsafe { w.dac_settling().bits(dac_settling as u8) } }); - let dac = Self { dac }; + let mut dac = Self(Dac::ptr()); dac.clear_fifo(); dac.clear_irqs(); dac } + pub const fn regs(&self) -> &DacRegisterBlock { + unsafe { &*self.0 } + } + #[inline(always)] - pub fn clear_irqs(&self) { - self.dac.irq_clr().write(|w| { + pub fn clear_irqs(&mut self) { + self.regs().irq_clr().write(|w| { w.fifo_oflow().set_bit(); w.fifo_uflow().set_bit(); w.dac_done().set_bit(); @@ -92,31 +91,30 @@ impl Dac { } #[inline(always)] - pub fn clear_fifo(&self) { - self.dac.fifo_clr().write(|w| unsafe { w.bits(1) }); + pub fn clear_fifo(&mut self) { + self.regs().fifo_clr().write(|w| unsafe { w.bits(1) }); } /// Load next value into the FIFO. /// /// Uses the [nb] API to allow blocking and non-blocking usage. #[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 { 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); } - self.dac - .fifo_data() - .write(|w| unsafe { w.bits(val.into()) }); + regs.fifo_data().write(|w| unsafe { w.bits(val.into()) }); Ok(()) } /// This loads and triggers the next value immediately. It also clears the FIFO before /// loading the passed value. #[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 { return Err(ValueTooLarge); } @@ -132,31 +130,30 @@ impl Dac { /// to be processed by the DAC. #[inline(always)] 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)] 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<(), ()> { - 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); } Ok(()) } #[inline(always)] - pub fn reset(&mut self, syscfg: &mut pac::Sysconfig) { - syscfg.enable_peripheral_clock(PeripheralSelect::Dac); - syscfg.assert_periph_reset_for_two_cycles(PeripheralSelect::Dac); + pub fn reset(&mut self) { + enable_peripheral_clock(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)] - pub fn release(self, syscfg: &mut pac::Sysconfig) -> DacInstance { - syscfg.disable_peripheral_clock(PeripheralSelect::Dac); - self.dac + pub fn stop(self) { + disable_peripheral_clock(PeripheralSelect::Dac); } } diff --git a/va416xx-hal/src/dma.rs b/va416xx-hal/src/dma.rs index 9590a1a..ef1c1fa 100644 --- a/va416xx-hal/src/dma.rs +++ b/va416xx-hal/src/dma.rs @@ -3,21 +3,23 @@ //! ## Examples //! //! - [Simple DMA example](https://egit.irs.uni-stuttgart.de/rust/va416xx-rs/src/branch/main/examples/simple/examples/dma.rs) -use crate::{ - clock::{PeripheralClock, PeripheralSelect}, - enable_nvic_interrupt, pac, - prelude::*, +use arbitrary_int::{u10, u3}; +use vorago_shared_periphs::{ + enable_peripheral_clock, reset_peripheral_for_cycles, PeripheralSelect, }; +use crate::{enable_nvic_interrupt, pac}; + const MAX_DMA_TRANSFERS_PER_CYCLE: usize = 1024; const BASE_PTR_ADDR_MASK: u32 = 0b1111111; /// DMA cycle control values. /// /// Refer to chapter 6.3.1 and 6.6.3 of the datasheet for more details. -#[repr(u8)] -#[derive(Debug, Clone, Copy)] +#[bitbybit::bitenum(u3, exhaustive = true)] +#[derive(Debug)] #[cfg_attr(feature = "defmt", derive(defmt::Format))] +#[repr(u8)] pub enum CycleControl { /// Indicates that the data structure is invalid. Stop = 0b000, @@ -42,7 +44,8 @@ pub enum CycleControl { PeriphScatterGatherAlternate = 0b111, } -#[derive(Debug, Clone, Copy)] +#[bitbybit::bitenum(u2, exhaustive = true)] +#[derive(Debug)] #[cfg_attr(feature = "defmt", derive(defmt::Format))] pub enum AddrIncrement { Byte = 0b00, @@ -51,7 +54,8 @@ pub enum AddrIncrement { None = 0b11, } -#[derive(Debug, Clone, Copy)] +#[bitbybit::bitenum(u2, exhaustive = false)] +#[derive(Debug)] #[cfg_attr(feature = "defmt", derive(defmt::Format))] pub enum DataSize { Byte = 0b00, @@ -60,7 +64,8 @@ pub enum DataSize { } /// 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))] pub enum RPower { EachTransfer = 0b0000, @@ -73,8 +78,12 @@ pub enum RPower { Every128 = 0b0111, Every256 = 0b1000, Every512 = 0b1001, - Every1024Min = 0b1010, - Every1024 = 0b1111, + Every1024 = 0b1010, + Every1024Alt0 = 0b1011, + Every1024Alt1 = 0b1100, + Every1024Alt2 = 0b1101, + Every1024Alt3 = 0b1110, + Every1024Alt4 = 0b1111, } #[derive(Debug, PartialEq, Eq, thiserror::Error)] @@ -82,6 +91,7 @@ pub enum RPower { #[cfg_attr(feature = "defmt", derive(defmt::Format))] pub struct InvalidCtrlBlockAddrError; +/* bitfield::bitfield! { #[repr(transparent)] #[derive(Clone, Copy)] @@ -111,6 +121,33 @@ bitfield::bitfield! { u8; 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, + #[bits(26..=27, rw)] + src_inc: AddrIncrement, + #[bits(24..=25, rw)] + src_size: Option, + #[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)] #[derive(Debug, Copy, Clone)] @@ -127,7 +164,7 @@ impl DmaChannelControl { Self { src_end_ptr: 0, dest_end_ptr: 0, - cfg: ChannelConfig(0), + cfg: ChannelConfig::new_with_raw_value(0), padding: 0, } } @@ -428,20 +465,18 @@ impl DmaChannel { return Err(DmaTransferInitError::TransferSizeTooLarge(source.len())); } 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) .checked_add(len as u32) .ok_or(DmaTransferInitError::AddrOverflow)?; self.ch_ctrl_pri.dest_end_ptr = dest as u32; - self.ch_ctrl_pri - .cfg - .set_cycle_ctr(CycleControl::Basic as u8); - self.ch_ctrl_pri.cfg.set_src_size(DataSize::Byte as u8); - self.ch_ctrl_pri.cfg.set_src_inc(AddrIncrement::Byte as u8); - self.ch_ctrl_pri.cfg.set_dst_size(DataSize::Byte as u8); - self.ch_ctrl_pri.cfg.set_dst_inc(AddrIncrement::None as u8); - self.ch_ctrl_pri.cfg.set_n_minus_1(len as u16); - self.ch_ctrl_pri.cfg.set_r_power(RPower::Every8 as u8); + self.ch_ctrl_pri.cfg.set_cycle_ctrl(CycleControl::Basic); + self.ch_ctrl_pri.cfg.set_src_size(DataSize::Byte); + self.ch_ctrl_pri.cfg.set_src_inc(AddrIncrement::Byte); + self.ch_ctrl_pri.cfg.set_dst_size(DataSize::Byte); + self.ch_ctrl_pri.cfg.set_dst_inc(AddrIncrement::None); + self.ch_ctrl_pri.cfg.set_n_minus_1(u10::new(len as u16)); + self.ch_ctrl_pri.cfg.set_r_power(RPower::Every8); self.select_primary_structure(); Ok(()) } @@ -470,16 +505,18 @@ impl DmaChannel { data_size: DataSize, 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.dest_end_ptr = dest_end_ptr; - self.ch_ctrl_pri.cfg.set_cycle_ctr(CycleControl::Auto as u8); - self.ch_ctrl_pri.cfg.set_src_size(data_size as u8); - self.ch_ctrl_pri.cfg.set_src_inc(addr_incr as u8); - self.ch_ctrl_pri.cfg.set_dst_size(data_size as u8); - self.ch_ctrl_pri.cfg.set_dst_inc(addr_incr as u8); - self.ch_ctrl_pri.cfg.set_n_minus_1(n_minus_one as u16); - self.ch_ctrl_pri.cfg.set_r_power(RPower::Every4 as u8); + self.ch_ctrl_pri.cfg.set_cycle_ctrl(CycleControl::Auto); + self.ch_ctrl_pri.cfg.set_src_size(data_size); + self.ch_ctrl_pri.cfg.set_src_inc(addr_incr); + self.ch_ctrl_pri.cfg.set_dst_size(data_size); + self.ch_ctrl_pri.cfg.set_dst_inc(addr_incr); + self.ch_ctrl_pri + .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(); } } @@ -495,7 +532,6 @@ impl Dma { /// Alternatively, the [DmaCtrlBlock::new_at_addr] function can be used to create the DMA /// control block at a specific address. pub fn new( - syscfg: &mut pac::Sysconfig, dma: pac::Dma, cfg: DmaCfg, ctrl_block: *mut DmaCtrlBlock, @@ -505,8 +541,8 @@ impl Dma { if raw_addr & BASE_PTR_ADDR_MASK > 0 { return Err(InvalidCtrlBlockAddrError); } - syscfg.enable_peripheral_clock(PeripheralClock::Dma); - syscfg.assert_periph_reset_for_two_cycles(PeripheralSelect::Dma); + enable_peripheral_clock(PeripheralSelect::Dma); + reset_peripheral_for_cycles(PeripheralSelect::Dma, 2); let dma = Dma { dma, ctrl_block }; dma.dma .ctrl_base_ptr() diff --git a/va416xx-hal/src/gpio/asynch.rs b/va416xx-hal/src/gpio/asynch.rs deleted file mode 100644 index 7d18c82..0000000 --- a/va416xx-hal/src/gpio/asynch.rs +++ /dev/null @@ -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 { - 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( - pin: &mut Pin>, - edge: InterruptEdge, - ) -> Result { - 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 { - 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 { - 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 { - pin: Pin>, -} - -impl InputPinAsync { - /// 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>) -> Result { - 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> { - self.pin - } -} -impl embedded_hal::digital::ErrorType for InputPinAsync { - type Error = core::convert::Infallible; -} - -impl Wait for InputPinAsync { - 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(()) - } -} diff --git a/va416xx-hal/src/gpio/dynpin.rs b/va416xx-hal/src/gpio/dynpin.rs deleted file mode 100644 index a944ca6..0000000 --- a/va416xx-hal/src/gpio/dynpin.rs +++ /dev/null @@ -1,1041 +0,0 @@ -//! # Type-erased, value-level module for GPIO pins -//! -//! Although the type-level API is generally preferred, it is not suitable in -//! all cases. Because each pin is represented by a distinct type, it is not -//! possible to store multiple pins in a homogeneous data structure. The -//! value-level API solves this problem by erasing the type information and -//! tracking the pin at run-time. -//! -//! Value-level pins are represented by the [`DynPin`] type. [`DynPin`] has two -//! fields, `id` and `mode` with types [`DynPinId`] and [`DynPinMode`] -//! respectively. The implementation of these types closely mirrors the -//! type-level API. -//! -//! Instances of [`DynPin`] cannot be created directly. Rather, they must be -//! created from their type-level equivalents using [`From`]/[`Into`]. -//! -//! ``` -//! // Move a pin out of the Pins struct and convert to a DynPin -//! let pa0: DynPin = pins.pa0.into(); -//! ``` -//! -//! Conversions between pin modes use a value-level version of the type-level -//! API. -//! -//! ``` -//! // Use one of the literal function names -//! pa0.into_floating_input(); -//! // Use a method and a DynPinMode variant -//! pa0.into_mode(DYN_FLOATING_INPUT); -//! ``` -//! -//! Because the pin state cannot be tracked at compile-time, many [`DynPin`] -//! operations become fallible. Run-time checks are inserted to ensure that -//! users don't try to, for example, set the output level of an input pin. -//! -//! Users may try to convert value-level pins back to their type-level -//! equivalents. However, this option is fallible, because the compiler cannot -//! guarantee the pin has the correct ID or is in the correct mode at -//! compile-time. Use [TryFrom]/[TryInto] for this conversion. -//! -//! ``` -//! // Convert to a `DynPin` -//! let pa0: DynPin = pins.pa0.into(); -//! // Change pin mode -//! pa0.into_floating_input(); -//! // Convert back to a `Pin` -//! let pa0: Pin = pa0.try_into().unwrap(); -//! ``` -//! -//! # Embedded HAL traits -//! -//! This module implements all of the embedded HAL GPIO traits for [`DynPin`]. -//! However, whereas the type-level API uses -//! `Error = core::convert::Infallible`, the value-level API can return a real -//! error. If the [`DynPin`] is not in the correct [`DynPinMode`] for the -//! operation, the trait functions will return -//! [InvalidPinTypeError]. - -use crate::FunSel; - -use va416xx as pac; - -use super::{ - AsyncDynPinError, FilterClkSel, FilterType, InputDynPinAsync, InterruptEdge, InterruptLevel, - IsMaskedError, Pin, PinId, PinMode, PinState, Port, -}; - -//================================================================================================== -// DynPinMode configurations -//================================================================================================== - -/// Value-level `enum` for disabled configurations -#[derive(Debug, PartialEq, Eq, Clone, Copy)] -#[cfg_attr(feature = "defmt", derive(defmt::Format))] -pub enum DynDisabled { - Floating, - PullDown, - PullUp, -} - -/// Value-level `enum` for input configurations -#[derive(Debug, PartialEq, Eq, Clone, Copy)] -#[cfg_attr(feature = "defmt", derive(defmt::Format))] -pub enum DynInput { - Floating, - PullDown, - PullUp, -} - -/// Value-level `enum` for output configurations -#[derive(Debug, PartialEq, Eq, Clone, Copy)] -#[cfg_attr(feature = "defmt", derive(defmt::Format))] -pub enum DynOutput { - PushPull, - OpenDrain, - ReadablePushPull, - ReadableOpenDrain, -} - -pub type DynAlternate = crate::FunSel; - -//============================================================================== -// Error -//============================================================================== - -/// GPIO error type -/// -/// [`DynPin`]s are not tracked and verified at compile-time, so run-time -/// operations are fallible. This `enum` represents the corresponding errors. -#[derive(Debug, PartialEq, Eq, thiserror::Error)] -#[cfg_attr(feature = "defmt", derive(defmt::Format))] -#[error("Invalid pin type for operation: {0:?}")] -pub struct InvalidPinTypeError(pub DynPinMode); - -impl embedded_hal::digital::Error for InvalidPinTypeError { - fn kind(&self) -> embedded_hal::digital::ErrorKind { - embedded_hal::digital::ErrorKind::Other - } -} - -//================================================================================================== -// DynPinMode -//================================================================================================== - -/// Value-level `enum` representing pin modes -#[derive(Debug, PartialEq, Eq, Clone, Copy)] -#[cfg_attr(feature = "defmt", derive(defmt::Format))] -pub enum DynPinMode { - Input(DynInput), - Output(DynOutput), - Alternate(DynAlternate), -} - -/// Value-level variant of [`DynPinMode`] for floating input mode -pub const DYN_FLOATING_INPUT: DynPinMode = DynPinMode::Input(DynInput::Floating); -/// Value-level variant of [`DynPinMode`] for pull-down input mode -pub const DYN_PULL_DOWN_INPUT: DynPinMode = DynPinMode::Input(DynInput::PullDown); -/// Value-level variant of [`DynPinMode`] for pull-up input mode -pub const DYN_PULL_UP_INPUT: DynPinMode = DynPinMode::Input(DynInput::PullUp); - -/// Value-level variant of [`DynPinMode`] for push-pull output mode -pub const DYN_PUSH_PULL_OUTPUT: DynPinMode = DynPinMode::Output(DynOutput::PushPull); -/// Value-level variant of [`DynPinMode`] for open-drain output mode -pub const DYN_OPEN_DRAIN_OUTPUT: DynPinMode = DynPinMode::Output(DynOutput::OpenDrain); -/// Value-level variant of [`DynPinMode`] for readable push-pull output mode -pub const DYN_RD_PUSH_PULL_OUTPUT: DynPinMode = DynPinMode::Output(DynOutput::ReadablePushPull); -/// Value-level variant of [`DynPinMode`] for readable opendrain output mode -pub const DYN_RD_OPEN_DRAIN_OUTPUT: DynPinMode = DynPinMode::Output(DynOutput::ReadableOpenDrain); - -/// Value-level variant of [`DynPinMode`] for function select 1 -pub const DYN_ALT_FUNC_1: DynPinMode = DynPinMode::Alternate(DynAlternate::Sel1); -/// Value-level variant of [`DynPinMode`] for function select 2 -pub const DYN_ALT_FUNC_2: DynPinMode = DynPinMode::Alternate(DynAlternate::Sel2); -/// Value-level variant of [`DynPinMode`] for function select 3 -pub const DYN_ALT_FUNC_3: DynPinMode = DynPinMode::Alternate(DynAlternate::Sel3); - -//================================================================================================== -// DynGroup & DynPinId -//================================================================================================== - -pub type DynGroup = Port; - -#[inline] -pub const fn irq_id(port: Port, num: u8) -> Option { - match port { - Port::A => match num { - 0 => Some(va416xx::Interrupt::PORTA0), - 1 => Some(va416xx::Interrupt::PORTA1), - 2 => Some(va416xx::Interrupt::PORTA2), - 3 => Some(va416xx::Interrupt::PORTA3), - 4 => Some(va416xx::Interrupt::PORTA4), - 5 => Some(va416xx::Interrupt::PORTA5), - 6 => Some(va416xx::Interrupt::PORTA6), - 7 => Some(va416xx::Interrupt::PORTA7), - 8 => Some(va416xx::Interrupt::PORTA8), - 9 => Some(va416xx::Interrupt::PORTA9), - 10 => Some(va416xx::Interrupt::PORTA10), - 11 => Some(va416xx::Interrupt::PORTA11), - 12 => Some(va416xx::Interrupt::PORTA12), - 13 => Some(va416xx::Interrupt::PORTA13), - 14 => Some(va416xx::Interrupt::PORTA14), - 15 => Some(va416xx::Interrupt::PORTA15), - _ => None, - }, - Port::B => match num { - 0 => Some(va416xx::Interrupt::PORTB0), - 1 => Some(va416xx::Interrupt::PORTB1), - 2 => Some(va416xx::Interrupt::PORTB2), - 3 => Some(va416xx::Interrupt::PORTB3), - 4 => Some(va416xx::Interrupt::PORTB4), - 5 => Some(va416xx::Interrupt::PORTB5), - 6 => Some(va416xx::Interrupt::PORTB6), - 7 => Some(va416xx::Interrupt::PORTB7), - 8 => Some(va416xx::Interrupt::PORTB8), - 9 => Some(va416xx::Interrupt::PORTB9), - 10 => Some(va416xx::Interrupt::PORTB10), - 11 => Some(va416xx::Interrupt::PORTB11), - 12 => Some(va416xx::Interrupt::PORTB12), - 13 => Some(va416xx::Interrupt::PORTB13), - 14 => Some(va416xx::Interrupt::PORTB14), - 15 => Some(va416xx::Interrupt::PORTB15), - _ => None, - }, - Port::C => match num { - 0 => Some(va416xx::Interrupt::PORTC0), - 1 => Some(va416xx::Interrupt::PORTC1), - 2 => Some(va416xx::Interrupt::PORTC2), - 3 => Some(va416xx::Interrupt::PORTC3), - 4 => Some(va416xx::Interrupt::PORTC4), - 5 => Some(va416xx::Interrupt::PORTC5), - 6 => Some(va416xx::Interrupt::PORTC6), - 7 => Some(va416xx::Interrupt::PORTC7), - 8 => Some(va416xx::Interrupt::PORTC8), - 9 => Some(va416xx::Interrupt::PORTC9), - 10 => Some(va416xx::Interrupt::PORTC10), - 11 => Some(va416xx::Interrupt::PORTC11), - 12 => Some(va416xx::Interrupt::PORTC12), - 13 => Some(va416xx::Interrupt::PORTC13), - 14 => Some(va416xx::Interrupt::PORTC14), - 15 => Some(va416xx::Interrupt::PORTC15), - _ => None, - }, - Port::D => match num { - 0 => Some(va416xx::Interrupt::PORTD0), - 1 => Some(va416xx::Interrupt::PORTD1), - 2 => Some(va416xx::Interrupt::PORTD2), - 3 => Some(va416xx::Interrupt::PORTD3), - 4 => Some(va416xx::Interrupt::PORTD4), - 5 => Some(va416xx::Interrupt::PORTD5), - 6 => Some(va416xx::Interrupt::PORTD6), - 7 => Some(va416xx::Interrupt::PORTD7), - 8 => Some(va416xx::Interrupt::PORTD8), - 9 => Some(va416xx::Interrupt::PORTD9), - 10 => Some(va416xx::Interrupt::PORTD10), - 11 => Some(va416xx::Interrupt::PORTD11), - 12 => Some(va416xx::Interrupt::PORTD12), - 13 => Some(va416xx::Interrupt::PORTD13), - 14 => Some(va416xx::Interrupt::PORTD14), - 15 => Some(va416xx::Interrupt::PORTD15), - _ => None, - }, - Port::E => match num { - 0 => Some(va416xx::Interrupt::PORTE0), - 1 => Some(va416xx::Interrupt::PORTE1), - 2 => Some(va416xx::Interrupt::PORTE2), - 3 => Some(va416xx::Interrupt::PORTE3), - 4 => Some(va416xx::Interrupt::PORTE4), - 5 => Some(va416xx::Interrupt::PORTE5), - 6 => Some(va416xx::Interrupt::PORTE6), - 7 => Some(va416xx::Interrupt::PORTE7), - 8 => Some(va416xx::Interrupt::PORTE8), - 9 => Some(va416xx::Interrupt::PORTE9), - 10 => Some(va416xx::Interrupt::PORTE10), - 11 => Some(va416xx::Interrupt::PORTE11), - 12 => Some(va416xx::Interrupt::PORTE12), - 13 => Some(va416xx::Interrupt::PORTE13), - 14 => Some(va416xx::Interrupt::PORTE14), - 15 => Some(va416xx::Interrupt::PORTE15), - _ => None, - }, - Port::F => match num { - 0 => Some(va416xx::Interrupt::PORTF0), - 1 => Some(va416xx::Interrupt::PORTF1), - 2 => Some(va416xx::Interrupt::PORTF2), - 3 => Some(va416xx::Interrupt::PORTF3), - 4 => Some(va416xx::Interrupt::PORTF4), - 5 => Some(va416xx::Interrupt::PORTF5), - 6 => Some(va416xx::Interrupt::PORTF6), - 7 => Some(va416xx::Interrupt::PORTF7), - 8 => Some(va416xx::Interrupt::PORTF8), - 9 => Some(va416xx::Interrupt::PORTF9), - 10 => Some(va416xx::Interrupt::PORTF10), - 11 => Some(va416xx::Interrupt::PORTF11), - 12 => Some(va416xx::Interrupt::PORTF12), - 13 => Some(va416xx::Interrupt::PORTF13), - 14 => Some(va416xx::Interrupt::PORTF14), - 15 => Some(va416xx::Interrupt::PORTF15), - _ => None, - }, - Port::G => None, - } -} - -#[derive(Debug, PartialEq, Eq, Clone, Copy)] -#[cfg_attr(feature = "defmt", derive(defmt::Format))] -pub struct DynPinId { - port: Port, - num: u8, -} - -impl DynPinId { - pub const fn new(port: Port, num: u8) -> Self { - DynPinId { port, num } - } - - pub const fn port(&self) -> Port { - self.port - } - pub const fn num(&self) -> u8 { - self.num - } -} - -//================================================================================================== -// ModeFields -//================================================================================================== - -/// Collect all fields needed to set the [`PinMode`](super::PinMode) -#[derive(Default)] -struct ModeFields { - dir: bool, - opendrn: bool, - pull_en: bool, - /// true for pullup, false for pulldown - pull_dir: bool, - funsel: u8, - enb_input: bool, -} - -impl From for ModeFields { - #[inline] - fn from(mode: DynPinMode) -> Self { - let mut fields = Self::default(); - match mode { - DynPinMode::Input(config) => { - fields.dir = false; - fields.funsel = FunSel::Sel0 as u8; - match config { - DynInput::Floating => (), - DynInput::PullUp => { - fields.pull_en = true; - fields.pull_dir = true; - } - DynInput::PullDown => { - fields.pull_en = true; - } - } - } - DynPinMode::Output(config) => { - fields.dir = true; - fields.funsel = FunSel::Sel0 as u8; - match config { - DynOutput::PushPull => (), - DynOutput::OpenDrain => { - fields.opendrn = true; - } - DynOutput::ReadableOpenDrain => { - fields.enb_input = true; - fields.opendrn = true; - } - DynOutput::ReadablePushPull => { - fields.enb_input = true; - } - } - } - DynPinMode::Alternate(config) => { - fields.funsel = config as u8; - } - } - fields - } -} - -/// Type definition to avoid confusion: These register blocks are identical -type PortRegisterBlock = pac::porta::RegisterBlock; -pub type PortReg = pac::ioconfig::Porta; - -//================================================================================================== -// DynPin -//================================================================================================== - -/// A value-level pin, parameterized by [`DynPinId`] and [`DynPinMode`] -/// -/// This type acts as a type-erased version of [`Pin`]. Every pin is represented -/// by the same type, and pins are tracked and distinguished at run-time. -#[derive(Debug)] -#[cfg_attr(feature = "defmt", derive(defmt::Format))] -pub struct DynPin { - id: DynPinId, - mode: DynPinMode, -} - -impl DynPin { - /// Create a new [DynPin] - /// - /// # Safety - /// - /// Each [DynPin] must be a singleton. For a given [DynPinId], there - /// must be at most one corresponding [`DynPin`] in existence at any given - /// time. Violating this requirement is `unsafe`. - #[inline] - pub(crate) const unsafe fn new(id: DynPinId, mode: DynPinMode) -> Self { - DynPin { id, mode } - } - - /// Steals a new [DynPin]. - /// - /// This function will simply set the internal mode to [DYN_FLOATING_INPUT] pin without - /// modifying any registers related to the behaviour of the pin. The user should call - /// [Self::into_mode] to ensure the correct mode of the pin. - /// - /// # Safety - /// - /// Circumvents the HAL's safety guarantees. The caller must ensure that the pin is not - /// used cocurrently somewhere else. The caller might also want to call [Self::into_mode] - /// to ensure the correct desired state of the pin. It is recommended to create the pin using - /// [Pin::downgrade] instead. - pub const unsafe fn steal(id: DynPinId) -> Self { - DynPin { - id, - mode: DYN_FLOATING_INPUT, - } - } - - /// Return a copy of the pin ID - #[inline] - pub const fn id(&self) -> DynPinId { - self.id - } - - #[inline] - pub const fn irq_id(&self) -> Option { - irq_id(self.id.port(), self.id.num()) - } - - /// Return a copy of the pin mode - #[inline] - pub const fn mode(&self) -> DynPinMode { - self.mode - } - - /// Convert the pin to the requested [`DynPinMode`] - #[inline] - pub fn into_mode(&mut self, mode: DynPinMode) { - self.change_mode(mode); - self.mode = mode; - } - - #[inline] - pub fn is_input_pin(&self) -> bool { - matches!(self.mode, DynPinMode::Input(_)) - } - - #[inline] - pub fn is_output_pin(&self) -> bool { - matches!(self.mode, DynPinMode::Output(_)) - } - - /// Configure the pin for function select 1. See Programmer Guide p.286 for the function table - #[inline] - pub fn into_funsel_1(&mut self) { - self.into_mode(DYN_ALT_FUNC_1); - } - - /// Configure the pin for function select 2. See Programmer Guide p.286 for the function table - #[inline] - pub fn into_funsel_2(&mut self) { - self.into_mode(DYN_ALT_FUNC_2); - } - - /// Configure the pin for function select 3. See Programmer Guide p.286 for the function table - #[inline] - pub fn into_funsel_3(&mut self) { - self.into_mode(DYN_ALT_FUNC_3); - } - - /// Configure the pin to operate as a floating input - #[inline] - pub fn into_floating_input(&mut self) { - self.into_mode(DYN_FLOATING_INPUT); - } - - /// Configure the pin to operate as a pulled down input - #[inline] - pub fn into_pull_down_input(&mut self) { - self.into_mode(DYN_PULL_DOWN_INPUT); - } - - /// Configure the pin to operate as a pulled up input - #[inline] - pub fn into_pull_up_input(&mut self) { - self.into_mode(DYN_PULL_UP_INPUT); - } - - /// Configure the pin to operate as a push-pull output - #[inline] - pub fn into_push_pull_output(&mut self) { - self.into_mode(DYN_PUSH_PULL_OUTPUT); - } - - /// Configure the pin to operate as a push-pull output - #[inline] - pub fn into_open_drain_output(&mut self) { - self.into_mode(DYN_OPEN_DRAIN_OUTPUT); - } - - /// Configure the pin to operate as a push-pull output - #[inline] - pub fn into_readable_push_pull_output(&mut self) { - self.into_mode(DYN_RD_PUSH_PULL_OUTPUT); - } - - /// Configure the pin to operate as a push-pull output - #[inline] - pub fn into_readable_open_drain_output(&mut self) { - self.into_mode(DYN_RD_OPEN_DRAIN_OUTPUT); - } - - #[inline(always)] - pub fn is_low(&self) -> Result { - self.read_internal().map(|v| !v) - } - - #[inline(always)] - pub fn is_high(&self) -> Result { - self.read_internal() - } - - #[inline(always)] - pub fn set_low(&mut self) -> Result<(), InvalidPinTypeError> { - self.write_internal(false) - } - - #[inline(always)] - pub fn set_high(&mut self) -> Result<(), InvalidPinTypeError> { - self.write_internal(true) - } - - /// Toggle the logic level of an output pin - #[inline(always)] - pub fn toggle(&mut self) -> Result<(), InvalidPinTypeError> { - if !self.is_output_pin() { - return Err(InvalidPinTypeError(self.mode)); - } - // Safety: TOGOUT is a "mask" register, and we only write the bit for - // this pin ID - unsafe { self.port_reg().togout().write(|w| w.bits(self.mask_32())) }; - Ok(()) - } - - #[inline(always)] - pub fn enable_interrupt(&self) { - self.port_reg() - .irq_enb() - .modify(|r, w| unsafe { w.bits(r.bits() | self.mask_32()) }); - } - - #[inline(always)] - pub fn disable_interrupt(&self) { - self.port_reg() - .irq_enb() - .modify(|r, w| unsafe { w.bits(r.bits() & !self.mask_32()) }); - } - - /// Try to recreate a type-level [`Pin`] from a value-level [`DynPin`] - /// - /// There is no way for the compiler to know if the conversion will be - /// successful at compile-time. We must verify the conversion at run-time - /// or refuse to perform it. - #[inline] - pub fn upgrade(self) -> Result, InvalidPinTypeError> { - if self.id == I::DYN && self.mode == M::DYN { - // The `DynPin` is consumed, so it is safe to replace it with the - // corresponding `Pin` - return Ok(unsafe { Pin::new() }); - } - Err(InvalidPinTypeError(self.mode)) - } - - /// Convert the pin into an async pin. The pin can be converted back by calling - /// [InputDynPinAsync::release] - pub fn into_async_input(self) -> Result { - InputDynPinAsync::new(self) - } - - // Get DATAMASK bit for this particular pin - #[inline(always)] - pub fn datamask(&self) -> bool { - (self.port_reg().datamask().read().bits() >> self.id().num) == 1 - } - - /// Clear DATAMASK bit for this particular pin. This prevents access - /// of the corresponding bit for output and input operations - #[inline(always)] - pub fn clear_datamask(&self) { - self.port_reg() - .datamask() - .modify(|r, w| unsafe { w.bits(r.bits() & !self.mask_32()) }); - } - - /// Set DATAMASK bit for this particular pin. 1 is the default - /// state of the bit and allows access of the corresponding bit - #[inline(always)] - pub fn set_datamask(&self) { - self.port_reg() - .datamask() - .modify(|r, w| unsafe { w.bits(r.bits() | self.mask_32()) }); - } - - #[inline] - pub fn is_high_masked(&self) -> Result { - self.read_pin_masked() - } - - #[inline] - pub fn is_low_masked(&self) -> Result { - self.read_pin_masked().map(|v| !v) - } - - #[inline] - pub fn set_high_masked(&mut self) -> Result<(), crate::gpio::IsMaskedError> { - self.write_pin_masked(true) - } - - #[inline] - pub fn set_low_masked(&mut self) -> Result<(), crate::gpio::IsMaskedError> { - self.write_pin_masked(false) - } - - /// See p.293 of the programmers guide for more information. - /// Possible delays in clock cycles: - /// - Delay 1: 1 - /// - Delay 2: 2 - /// - Delay 1 + Delay 2: 3 - #[inline] - pub fn configure_delay( - &mut self, - delay_1: bool, - delay_2: bool, - ) -> Result<(), InvalidPinTypeError> { - match self.mode { - DynPinMode::Output(_) => { - self.configure_delay_internal(delay_1, delay_2); - Ok(()) - } - _ => Err(InvalidPinTypeError(self.mode)), - } - } - - /// See p.293 of the programmers guide for more information. - /// When configured for pulse mode, a given pin will set the non-default state for exactly - /// one clock cycle before returning to the configured default state - pub fn configure_pulse_mode( - &mut self, - enable: bool, - default_state: PinState, - ) -> Result<(), InvalidPinTypeError> { - match self.mode { - DynPinMode::Output(_) => { - self.configure_pulse_mode_internal(enable, default_state); - Ok(()) - } - _ => Err(InvalidPinTypeError(self.mode)), - } - } - - /// See p.284 of the programmers guide for more information. - #[inline] - pub fn configure_filter_type( - &mut self, - filter: FilterType, - clksel: FilterClkSel, - ) -> Result<(), InvalidPinTypeError> { - match self.mode { - DynPinMode::Input(_) => { - self.configure_filter_type_internal(filter, clksel); - Ok(()) - } - _ => Err(InvalidPinTypeError(self.mode)), - } - } - - pub fn configure_edge_interrupt( - &mut self, - edge_type: InterruptEdge, - ) -> Result<(), InvalidPinTypeError> { - match self.mode { - DynPinMode::Input(_) | DynPinMode::Output(_) => { - self.configure_edge_interrupt_internal(edge_type); - Ok(()) - } - _ => Err(InvalidPinTypeError(self.mode)), - } - } - - pub fn configure_level_interrupt( - &mut self, - level_type: InterruptLevel, - ) -> Result<(), InvalidPinTypeError> { - match self.mode { - DynPinMode::Input(_) | DynPinMode::Output(_) => { - self.configure_level_interrupt_internal(level_type); - Ok(()) - } - _ => Err(InvalidPinTypeError(self.mode)), - } - } - - #[inline(always)] - fn read_internal(&self) -> Result { - match self.mode { - DynPinMode::Input(_) | DYN_RD_OPEN_DRAIN_OUTPUT | DYN_RD_PUSH_PULL_OUTPUT => { - Ok(self.read_pin()) - } - _ => Err(InvalidPinTypeError(self.mode)), - } - } - - #[inline(always)] - fn write_internal(&mut self, bit: bool) -> Result<(), InvalidPinTypeError> { - match self.mode { - DynPinMode::Output(_) => { - self.write_pin(bit); - Ok(()) - } - _ => Err(InvalidPinTypeError(self.mode)), - } - } - - /// Change the pin mode - #[inline] - pub(crate) fn change_mode(&mut self, mode: DynPinMode) { - let ModeFields { - dir, - funsel, - opendrn, - pull_dir, - pull_en, - enb_input, - } = mode.into(); - let (portreg, iocfg) = (self.port_reg(), self.iocfg_port()); - iocfg.write(|w| { - w.opendrn().bit(opendrn); - w.pen().bit(pull_en); - w.plevel().bit(pull_dir); - w.iewo().bit(enb_input); - unsafe { w.funsel().bits(funsel) } - }); - let mask = self.mask_32(); - unsafe { - if dir { - portreg.dir().modify(|r, w| w.bits(r.bits() | mask)); - // Clear output - portreg.clrout().write(|w| w.bits(mask)); - } else { - portreg.dir().modify(|r, w| w.bits(r.bits() & !mask)); - } - } - } - - #[inline(always)] - const fn port_reg(&self) -> &PortRegisterBlock { - match self.id().port() { - Port::A => unsafe { &(*pac::Porta::ptr()) }, - Port::B => unsafe { &(*pac::Portb::ptr()) }, - Port::C => unsafe { &(*pac::Portc::ptr()) }, - Port::D => unsafe { &(*pac::Portd::ptr()) }, - Port::E => unsafe { &(*pac::Porte::ptr()) }, - Port::F => unsafe { &(*pac::Portf::ptr()) }, - Port::G => unsafe { &(*pac::Portg::ptr()) }, - } - } - - #[inline(always)] - const fn iocfg_port(&self) -> &PortReg { - let ioconfig = unsafe { pac::Ioconfig::ptr().as_ref().unwrap() }; - match self.id().port() { - Port::A => ioconfig.porta(self.id().num() as usize), - Port::B => ioconfig.portb0(self.id().num() as usize), - Port::C => ioconfig.portc0(self.id().num() as usize), - Port::D => ioconfig.portd0(self.id().num() as usize), - Port::E => ioconfig.porte0(self.id().num() as usize), - Port::F => ioconfig.portf0(self.id().num() as usize), - Port::G => ioconfig.portg0(self.id().num() as usize), - } - } - - #[inline(always)] - const fn mask_32(&self) -> u32 { - 1 << self.id().num() - } - - #[inline] - /// Read the logic level of an output pin - pub(crate) fn read_pin(&self) -> bool { - let portreg = self.port_reg(); - ((portreg.datainraw().read().bits() >> self.id().num) & 0x01) == 1 - } - - /// Read a pin but use the masked version but check whether the datamask for the pin is - /// cleared as well - #[inline(always)] - fn read_pin_masked(&self) -> Result { - if !self.datamask() { - Err(IsMaskedError) - } else { - Ok(((self.port_reg().datain().read().bits() >> self.id().num) & 0x01) == 1) - } - } - - /// Write the logic level of an output pin - #[inline(always)] - pub(crate) fn write_pin(&mut self, bit: bool) { - // Safety: SETOUT is a "mask" register, and we only write the bit for - // this pin ID - unsafe { - if bit { - self.port_reg().setout().write(|w| w.bits(self.mask_32())); - } else { - self.port_reg().clrout().write(|w| w.bits(self.mask_32())); - } - } - } - - /// Write the logic level of an output pin but check whether the datamask for the pin is - /// cleared as well - #[inline] - fn write_pin_masked(&mut self, bit: bool) -> Result<(), IsMaskedError> { - if !self.datamask() { - Err(IsMaskedError) - } else { - // Safety: SETOUT is a "mask" register, and we only write the bit for - // this pin ID - unsafe { - if bit { - self.port_reg().setout().write(|w| w.bits(self.mask_32())); - } else { - self.port_reg().clrout().write(|w| w.bits(self.mask_32())); - } - Ok(()) - } - } - } - - /// Only useful for interrupt pins. Configure whether to use edges or level as interrupt soure - /// When using edge mode, it is possible to generate interrupts on both edges as well - #[inline] - fn configure_edge_interrupt_internal(&mut self, edge_type: InterruptEdge) { - unsafe { - self.port_reg() - .irq_sen() - .modify(|r, w| w.bits(r.bits() & !self.mask_32())); - match edge_type { - InterruptEdge::HighToLow => { - self.port_reg() - .irq_evt() - .modify(|r, w| w.bits(r.bits() & !self.mask_32())); - } - InterruptEdge::LowToHigh => { - self.port_reg() - .irq_evt() - .modify(|r, w| w.bits(r.bits() | self.mask_32())); - } - InterruptEdge::BothEdges => { - self.port_reg() - .irq_edge() - .modify(|r, w| w.bits(r.bits() | self.mask_32())); - } - } - } - } - - /// Configure which edge or level type triggers an interrupt - #[inline] - fn configure_level_interrupt_internal(&mut self, level: InterruptLevel) { - unsafe { - self.port_reg() - .irq_sen() - .modify(|r, w| w.bits(r.bits() | self.mask_32())); - if level == InterruptLevel::Low { - self.port_reg() - .irq_evt() - .modify(|r, w| w.bits(r.bits() & !self.mask_32())); - } else { - self.port_reg() - .irq_evt() - .modify(|r, w| w.bits(r.bits() | self.mask_32())); - } - } - } - - /// Only useful for input pins - #[inline] - fn configure_filter_type_internal(&mut self, filter: FilterType, clksel: FilterClkSel) { - self.iocfg_port().modify(|_, w| { - // Safety: Only write to register for this Pin ID - unsafe { - w.flttype().bits(filter as u8); - w.fltclk().bits(clksel as u8) - } - }); - } - - /// Only useful for output pins - /// See p.293 of the programmers guide for more information. - /// When configured for pulse mode, a given pin will set the non-default state for exactly - /// one clock cycle before returning to the configured default state - #[inline] - fn configure_pulse_mode_internal(&mut self, enable: bool, default_state: PinState) { - let portreg = self.port_reg(); - unsafe { - if enable { - portreg - .pulse() - .modify(|r, w| w.bits(r.bits() | self.mask_32())); - } else { - portreg - .pulse() - .modify(|r, w| w.bits(r.bits() & !self.mask_32())); - } - if default_state == PinState::Low { - portreg - .pulsebase() - .modify(|r, w| w.bits(r.bits() & !self.mask_32())); - } else { - portreg - .pulsebase() - .modify(|r, w| w.bits(r.bits() | self.mask_32())); - } - } - } - - /// Only useful for output pins - #[inline] - fn configure_delay_internal(&mut self, delay_1: bool, delay_2: bool) { - let portreg = self.port_reg(); - unsafe { - if delay_1 { - portreg - .delay1() - .modify(|r, w| w.bits(r.bits() | self.mask_32())); - } else { - portreg - .delay1() - .modify(|r, w| w.bits(r.bits() & !self.mask_32())); - } - if delay_2 { - portreg - .delay2() - .modify(|r, w| w.bits(r.bits() | self.mask_32())); - } else { - portreg - .delay2() - .modify(|r, w| w.bits(r.bits() & !self.mask_32())); - } - } - } - - // Only serves disambiguation purposes for the Embedded HAL impl - #[inline(always)] - fn is_low_mut(&mut self) -> Result { - self.is_low() - } - - // Only serves disambiguation purposes for the Embedded HAL impl - #[inline(always)] - fn is_high_mut(&mut self) -> Result { - self.is_high() - } -} - -//============================================================================== -// Convert between Pin and DynPin -//============================================================================== - -impl From> for DynPin -where - I: PinId, - M: PinMode, -{ - /// Erase the type-level information in a [`Pin`] and return a value-level - /// [`DynPin`] - #[inline] - fn from(pin: Pin) -> Self { - pin.downgrade() - } -} - -impl TryFrom for Pin -where - I: PinId, - M: PinMode, -{ - type Error = InvalidPinTypeError; - - /// Try to recreate a type-level [`Pin`] from a value-level [`DynPin`] - /// - /// There is no way for the compiler to know if the conversion will be - /// successful at compile-time. We must verify the conversion at run-time - /// or refuse to perform it. - #[inline] - fn try_from(pin: DynPin) -> Result { - pin.upgrade() - } -} - -//============================================================================== -// Embedded HAL v1 traits -//============================================================================== - -impl embedded_hal::digital::ErrorType for DynPin { - type Error = InvalidPinTypeError; -} - -impl embedded_hal::digital::OutputPin for DynPin { - #[inline] - fn set_high(&mut self) -> Result<(), Self::Error> { - self.set_high() - } - #[inline] - fn set_low(&mut self) -> Result<(), Self::Error> { - self.set_low() - } -} - -impl embedded_hal::digital::InputPin for DynPin { - #[inline] - fn is_high(&mut self) -> Result { - self.is_high_mut() - } - #[inline] - fn is_low(&mut self) -> Result { - self.is_low_mut() - } -} - -impl embedded_hal::digital::StatefulOutputPin for DynPin { - #[inline] - fn is_set_high(&mut self) -> Result { - self.is_high_mut() - } - #[inline] - fn is_set_low(&mut self) -> Result { - self.is_low_mut() - } - - #[inline] - fn toggle(&mut self) -> Result<(), Self::Error> { - self.toggle() - } -} diff --git a/va416xx-hal/src/gpio/mod.rs b/va416xx-hal/src/gpio/mod.rs index a01f8c7..a36c741 100644 --- a/va416xx-hal/src/gpio/mod.rs +++ b/va416xx-hal/src/gpio/mod.rs @@ -1,80 +1,20 @@ -//! # API for the GPIO peripheral +//! # GPIO support module. //! -//! The implementation of this GPIO module is heavily based on the -//! [ATSAMD HAL implementation](https://docs.rs/atsamd-hal/latest/atsamd_hal/gpio/index.html). +//! Contains abstractions to use the pins provided by the [crate::pins] module as GPIO or +//! IO peripheral pins. //! -//! This API provides two different submodules, [pin] and [dynpin], -//! representing two different ways to handle GPIO pins. The default, [pin], -//! is a type-level API that tracks the state of each pin at compile-time. The -//! alternative, [dynpin] is a type-erased, value-level API that tracks the -//! state of each pin at run-time. +//! The core data structures provided for this are the //! -//! The type-level API is strongly preferred. By representing the state of each -//! pin within the type system, the compiler can detect logic errors at -//! compile-time. Furthermore, the type-level API has absolutely zero run-time -//! cost. +//! - [Output] for push-pull output pins. +//! - [Input] for input pins. +//! - [Flex] for pins with flexible configuration requirements. +//! - [IoPeriphPin] for IO peripheral pins. //! -//! If needed, [dynpin] can be used to erase the type-level differences -//! between pins. However, by doing so, pins must now be tracked at run-time, -//! and each pin has a non-zero memory footprint. +//! The [crate::pins] module exposes singletons to access the [Pin]s required by this module +//! in a type-safe way. //! -//! ## Examples +//! # Examples //! //! - [Blinky example](https://egit.irs.uni-stuttgart.de/rust/va416xx-rs/src/branch/main/examples/simple/examples/blinky.rs) - -//================================================================================================== -// 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::*; +//! - [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::*; diff --git a/va416xx-hal/src/gpio/pin.rs b/va416xx-hal/src/gpio/pin.rs deleted file mode 100644 index 07927df..0000000 --- a/va416xx-hal/src/gpio/pin.rs +++ /dev/null @@ -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 -//! 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::(); -//! // Specify the target type and use `From`/`Into` -//! let pa0: Pin = 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; -/// Type-level variant of [PinMode] for pull-down input mode -pub type InputPullDown = Input; -/// Type-level variant of [PinMode] for pull-up input mode -pub type InputPullUp = Input; - -/// Type-level variant of [PinMode] for input modes -/// -/// Type `C` is one of three input configurations: [Floating], [PullDown] or -/// [PullUp] -pub struct Input { - cfg: PhantomData, -} - -impl Sealed for Input {} - -#[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 { - cfg: PhantomData, -} - -impl Sealed for Output {} - -/// Type-level variant of [`PinMode`] for push-pull output mode -pub type PushPullOutput = Output; -/// Type-level variant of [`PinMode`] for open drain output mode -pub type OutputOpenDrain = Output; - -pub type OutputReadablePushPull = Output; -pub type OutputReadableOpenDrain = Output; - -//================================================================================================== -// 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 { - cfg: PhantomData, -} - -impl Sealed for Alternate {} - -pub type AltFunc1 = Alternate; -pub type AltFunc2 = Alternate; -pub type AltFunc3 = Alternate; - -/// 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 PinMode for Input { - const DYN: DynPinMode = DynPinMode::Input(C::DYN); -} -impl PinMode for Output { - const DYN: DynPinMode = DynPinMode::Output(C::DYN); -} -impl PinMode for Alternate { - 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; -} - -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 = $Irq; - } - } - }; -} - -//================================================================================================== -// Pin -//================================================================================================== - -/// A type-level GPIO pin, parameterized by [`PinId`] and [`PinMode`] types -#[derive(Debug)] -pub struct Pin { - inner: DynPin, - mode: PhantomData<(I, M)>, -} - -impl Pin { - /// 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 { - 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 { - I::IRQ - } - - /// Convert the pin to the requested [`PinMode`] - #[inline] - pub fn into_mode(mut self) -> Pin { - // 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 { - 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 { - 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 { - self.into_mode() - } - - /// Configure the pin to operate as a floating input - #[inline] - pub fn into_floating_input(self) -> Pin { - self.into_mode() - } - - /// Configure the pin to operate as a pulled down input - #[inline] - pub fn into_pull_down_input(self) -> Pin { - self.into_mode() - } - - /// Configure the pin to operate as a pulled up input - #[inline] - pub fn into_pull_up_input(self) -> Pin { - self.into_mode() - } - - /// Configure the pin to operate as a push-pull output - #[inline] - pub fn into_push_pull_output(self) -> Pin { - self.into_mode() - } - - /// Configure the pin to operate as a readable push-pull output - #[inline] - pub fn into_readable_push_pull_output(self) -> Pin { - self.into_mode() - } - - /// Configure the pin to operate as a readable open-drain output - #[inline] - pub fn into_readable_open_drain_output(self) -> Pin { - 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 { - self.inner.is_high_masked() - } - - #[inline] - pub fn is_low_masked(&self) -> Result { - 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>` 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>, - Self: Into>, - Self: AsRef>, - Self: AsMut>, -{ - /// [`PinId`] of the corresponding [`Pin`] - type Id: PinId; - /// [`PinMode`] of the corresponding [`Pin`] - type Mode: PinMode; -} - -impl Sealed for Pin -where - I: PinId, - M: PinMode, -{ -} - -impl AnyPin for Pin -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

= Pin<

::Id,

::Mode>; - -impl AsRef

for SpecificPin

{ - #[inline] - fn as_ref(&self) -> &P { - // SAFETY: This is guaranteed to be safe, because P == SpecificPin

- // 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 AsMut

for SpecificPin

{ - #[inline] - fn as_mut(&mut self) -> &mut P { - // SAFETY: This is guaranteed to be safe, because P == SpecificPin

- // 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 Pin> { - /// Convert the pin into an async pin. The pin can be converted back by calling - /// [InputPinAsync::release] - pub fn into_async_input(self) -> Result, PortGDoesNotSupportAsyncError> { - InputPinAsync::new(self) - } -} - -impl Pin> { - #[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 Pin> { - #[inline] - pub fn configure_filter_type(&mut self, filter: FilterType, clksel: FilterClkSel) { - self.inner.configure_filter_type(filter, clksel).unwrap(); - } -} - -//================================================================================================== -// Embedded HAL traits -//================================================================================================== - -impl embedded_hal::digital::ErrorType for Pin -where - I: PinId, - M: PinMode, -{ - type Error = Infallible; -} - -impl embedded_hal::digital::OutputPin for Pin> { - #[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 embedded_hal::digital::InputPin for Pin> -where - I: PinId, - C: InputConfig, -{ - #[inline] - fn is_high(&mut self) -> Result { - Ok(self.is_high_mut()) - } - #[inline] - fn is_low(&mut self) -> Result { - Ok(self.is_low_mut()) - } -} - -impl embedded_hal::digital::StatefulOutputPin for Pin> -where - I: PinId, - C: OutputConfig + ReadableOutput, -{ - #[inline] - fn is_set_high(&mut self) -> Result { - Ok(self.is_high()) - } - #[inline] - fn is_set_low(&mut self) -> Result { - Ok(self.is_low()) - } - - #[inline] - fn toggle(&mut self) -> Result<(), Self::Error> { - self.toggle(); - Ok(()) - } -} - -impl embedded_hal::digital::InputPin for Pin> -where - I: PinId, - C: OutputConfig + ReadableOutput, -{ - #[inline] - fn is_high(&mut self) -> Result { - Ok(self.is_high_mut()) - } - - #[inline] - fn is_low(&mut self) -> Result { - 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) - ] -); diff --git a/va416xx-hal/src/i2c.rs b/va416xx-hal/src/i2c.rs index 23fdc93..0dafeaa 100644 --- a/va416xx-hal/src/i2c.rs +++ b/va416xx-hal/src/i2c.rs @@ -3,913 +3,4 @@ //! ## Examples //! //! - [PEB1 accelerometer example](https://egit.irs.uni-stuttgart.de/rust/va416xx-rs/src/branch/main/examples/simple/examples/peb1-accelerometer.rs) -use crate::{ - clock::{Clocks, PeripheralSelect}, - pac, - prelude::SyscfgExt, - time::Hertz, - typelevel::Sealed, -}; -use core::{marker::PhantomData, ops::Deref}; -use embedded_hal::i2c::{self, Operation, SevenBitAddress, TenBitAddress}; - -//================================================================================================== -// Defintions -//================================================================================================== - -const CLK_100K: Hertz = Hertz::from_raw(100_000); -const CLK_400K: Hertz = Hertz::from_raw(400_000); -const MIN_CLK_400K: Hertz = Hertz::from_raw(10_000_000); - -#[derive(Debug, PartialEq, Eq, Copy, Clone)] -#[cfg_attr(feature = "defmt", derive(defmt::Format))] -pub enum FifoEmptyMode { - Stall = 0, - EndTransaction = 1, -} - -#[derive(Debug, PartialEq, Eq, thiserror::Error)] -#[cfg_attr(feature = "defmt", derive(defmt::Format))] -#[error("clock too slow for fast I2C mode")] -pub struct ClockTooSlowForFastI2cError; - -#[derive(Debug, PartialEq, Eq, thiserror::Error)] -#[error("invalid timing parameters")] -pub struct InvalidTimingParamsError; - -#[derive(Debug, PartialEq, Eq, thiserror::Error)] -#[cfg_attr(feature = "defmt", derive(defmt::Format))] -pub enum Error { - #[error("arbitration lost")] - ArbitrationLost, - #[error("nack address")] - NackAddr, - /// Data not acknowledged in write operation - #[error("data not acknowledged in write operation")] - NackData, - /// Not enough data received in read operation - #[error("insufficient data received")] - InsufficientDataReceived, - /// Number of bytes in transfer too large (larger than 0x7fe) - #[error("data too large (larger than 0x7fe)")] - DataTooLarge, -} - -#[derive(Debug, PartialEq, Eq, thiserror::Error)] -#[cfg_attr(feature = "defmt", derive(defmt::Format))] -pub enum InitError { - /// Wrong address used in constructor - #[error("wrong address mode")] - WrongAddrMode, - /// APB1 clock is too slow for fast I2C mode. - #[error("clock too slow for fast I2C mode: {0}")] - ClkTooSlow(#[from] ClockTooSlowForFastI2cError), -} - -impl embedded_hal::i2c::Error for Error { - fn kind(&self) -> embedded_hal::i2c::ErrorKind { - match self { - Error::ArbitrationLost => embedded_hal::i2c::ErrorKind::ArbitrationLoss, - Error::NackAddr => { - embedded_hal::i2c::ErrorKind::NoAcknowledge(i2c::NoAcknowledgeSource::Address) - } - Error::NackData => { - embedded_hal::i2c::ErrorKind::NoAcknowledge(i2c::NoAcknowledgeSource::Data) - } - Error::DataTooLarge | Error::InsufficientDataReceived => { - embedded_hal::i2c::ErrorKind::Other - } - } - } -} - -#[derive(Debug, PartialEq, Copy, Clone)] -#[cfg_attr(feature = "defmt", derive(defmt::Format))] -enum I2cCmd { - Start = 0b00, - Stop = 0b10, - StartWithStop = 0b11, - Cancel = 0b100, -} - -#[derive(Debug, PartialEq, Eq, Copy, Clone)] -#[cfg_attr(feature = "defmt", derive(defmt::Format))] -pub enum I2cSpeed { - Regular100khz = 0, - Fast400khz = 1, -} - -#[derive(Debug, PartialEq, Eq)] -#[cfg_attr(feature = "defmt", derive(defmt::Format))] -pub enum I2cDirection { - Send = 0, - Read = 1, -} - -#[derive(Debug, PartialEq, Eq, Copy, Clone)] -#[cfg_attr(feature = "defmt", derive(defmt::Format))] -pub enum I2cAddress { - Regular(u8), - TenBit(u16), -} - -pub type I2cRegBlock = pac::i2c0::RegisterBlock; - -/// Common trait implemented by all PAC peripheral access structures. The register block -/// format is the same for all SPI blocks. -pub trait Instance: Deref { - 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 { - 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, - // 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, - /// 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, - /// Optionally specify a second I2C address the slave interface responds to - pub addr_b: Option, - pub addr_b_mask: Option, -} - -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, - clock: Hertz, -} - -impl I2cBase { - #[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 I2cBase { - pub fn new( - i2c: I2c, - syscfg: &mut pac::Sysconfig, - clocks: &Clocks, - speed_mode: I2cSpeed, - ms_cfg: Option<&MasterConfig>, - sl_cfg: Option<&SlaveConfig>, - ) -> Result { - 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 { - 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_base: I2cBase, - addr: PhantomData, -} - -impl I2cMaster { - pub fn new( - i2c: I2c, - sys_cfg: &mut pac::Sysconfig, - cfg: MasterConfig, - clocks: &Clocks, - speed_mode: I2cSpeed, - ) -> Result { - 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, - ) -> Result<(), Error> { - let mut iter = bytes.into_iter(); - // Load address - let (addr, addr_mode_bit) = I2cBase::::unwrap_addr(addr); - self.i2c_base.i2c.address().write(|w| unsafe { - w.bits(I2cDirection::Send as u32 | (addr << 1) as u32 | addr_mode_bit) - }); - - self.i2c_base - .i2c - .cmd() - .write(|w| unsafe { w.bits(init_cmd as u32) }); - let mut load_if_next_available = || { - if let Some(next_byte) = iter.next() { - self.load_fifo(next_byte); - } - }; - loop { - let status_reader = self.i2c_base.i2c.status().read(); - if status_reader.arblost().bit_is_set() { - self.error_handler_write(&init_cmd); - return Err(Error::ArbitrationLost); - } else if status_reader.nackaddr().bit_is_set() { - self.error_handler_write(&init_cmd); - return Err(Error::NackAddr); - } else if status_reader.nackdata().bit_is_set() { - self.error_handler_write(&init_cmd); - return Err(Error::NackData); - } else if status_reader.idle().bit_is_set() { - return Ok(()); - } else { - while !status_reader.txnfull().bit_is_set() { - load_if_next_available(); - } - } - } - } - - fn write_from_buffer( - &mut self, - init_cmd: I2cCmd, - addr: I2cAddress, - output: &[u8], - ) -> Result<(), Error> { - let len = output.len(); - // It should theoretically possible to transfer larger data sizes by tracking - // the number of sent words and setting it to 0x7fe as soon as only that many - // bytes are remaining. However, large transfer like this are not common. This - // feature will therefore not be supported for now. - if len > 0x7fe { - return Err(Error::DataTooLarge); - } - // Load number of words - self.i2c_base - .i2c - .words() - .write(|w| unsafe { w.bits(len as u32) }); - let mut bytes = output.iter(); - // FIFO has a depth of 16. We load slightly above the trigger level - // but not all of it because the transaction might fail immediately - const FILL_DEPTH: usize = 12; - - // load the FIFO - for _ in 0..core::cmp::min(FILL_DEPTH, len) { - self.load_fifo(*bytes.next().unwrap()); - } - - self.write_base(addr, init_cmd, output.iter().cloned()) - } - - fn read_internal(&mut self, addr: I2cAddress, buffer: &mut [u8]) -> Result<(), Error> { - let len = buffer.len(); - // It should theoretically possible to transfer larger data sizes by tracking - // the number of sent words and setting it to 0x7fe as soon as only that many - // bytes are remaining. However, large transfer like this are not common. This - // feature will therefore not be supported for now. - if len > 0x7fe { - return Err(Error::DataTooLarge); - } - // Clear the receive FIFO - self.clear_rx_fifo(); - - // Load number of words - self.i2c_base - .i2c - .words() - .write(|w| unsafe { w.bits(len as u32) }); - let (addr, addr_mode_bit) = match addr { - I2cAddress::Regular(addr) => (addr as u16, 0 << 15), - I2cAddress::TenBit(addr) => (addr, 1 << 15), - }; - // Load address - self.i2c_base.i2c.address().write(|w| unsafe { - w.bits(I2cDirection::Read as u32 | (addr << 1) as u32 | addr_mode_bit) - }); - - let mut buf_iter = buffer.iter_mut(); - let mut read_bytes = 0; - // Start receive transfer - self.i2c_base - .i2c - .cmd() - .write(|w| unsafe { w.bits(I2cCmd::StartWithStop as u32) }); - let mut read_if_next_available = || { - if let Some(next_byte) = buf_iter.next() { - *next_byte = self.read_fifo(); - } - }; - loop { - let status_reader = self.i2c_base.i2c.status().read(); - if status_reader.arblost().bit_is_set() { - self.clear_rx_fifo(); - return Err(Error::ArbitrationLost); - } else if status_reader.nackaddr().bit_is_set() { - self.clear_rx_fifo(); - return Err(Error::NackAddr); - } else if status_reader.idle().bit_is_set() { - if read_bytes != len { - return Err(Error::InsufficientDataReceived); - } - return Ok(()); - } else if status_reader.rxnempty().bit_is_set() { - read_if_next_available(); - read_bytes += 1; - } - } - } -} - -//====================================================================================== -// Embedded HAL I2C implementations -//====================================================================================== - -impl embedded_hal::i2c::ErrorType for I2cMaster { - type Error = Error; -} - -impl embedded_hal::i2c::I2c for I2cMaster { - fn transaction( - &mut self, - address: SevenBitAddress, - operations: &mut [Operation<'_>], - ) -> Result<(), Self::Error> { - for operation in operations { - match operation { - Operation::Read(buf) => self.read_internal(I2cAddress::Regular(address), buf)?, - Operation::Write(buf) => self.write_from_buffer( - I2cCmd::StartWithStop, - I2cAddress::Regular(address), - buf, - )?, - } - } - Ok(()) - } -} - -impl embedded_hal::i2c::ErrorType for I2cMaster { - type Error = Error; -} - -impl embedded_hal::i2c::I2c for I2cMaster { - 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_base: I2cBase, - addr: PhantomData, -} - -impl I2cSlave { - fn new_generic( - i2c: I2c, - sys_cfg: &mut pac::Sysconfig, - cfg: SlaveConfig, - clocks: &Clocks, - speed_mode: I2cSpeed, - ) -> Result { - 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 I2cSlave { - /// 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 { - if let I2cAddress::TenBit(_) = cfg.addr { - return Err(InitError::WrongAddrMode); - } - Ok(Self::new_generic(i2c, sys_cfg, cfg, clocks, speed_mode)?) - } -} - -impl I2cSlave { - pub fn new_ten_bit_addr( - i2c: I2c, - sys_cfg: &mut pac::Sysconfig, - cfg: SlaveConfig, - clocks: &Clocks, - speed_mode: I2cSpeed, - ) -> Result { - Self::new_generic(i2c, sys_cfg, cfg, clocks, speed_mode) - } -} +pub use vorago_shared_periphs::i2c::*; diff --git a/va416xx-hal/src/irq_router.rs b/va416xx-hal/src/irq_router.rs index 7c44816..93d6f5e 100644 --- a/va416xx-hal/src/irq_router.rs +++ b/va416xx-hal/src/irq_router.rs @@ -1,9 +1,10 @@ //! IRQ Router peripheral support. -use crate::{ - clock::{PeripheralSelect, SyscfgExt}, - pac, +use vorago_shared_periphs::{ + enable_peripheral_clock, reset_peripheral_for_cycles, PeripheralSelect, }; +use crate::pac; + /// 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 @@ -11,9 +12,10 @@ use crate::{ /// 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 /// respectively instead of 0x00. -pub fn enable_and_init_irq_router(sysconfig: &mut pac::Sysconfig, irq_router: &pac::IrqRouter) { - sysconfig.enable_peripheral_clock(PeripheralSelect::IrqRouter); - sysconfig.assert_periph_reset_for_two_cycles(PeripheralSelect::IrqRouter); +pub fn enable_and_init_irq_router() { + let irq_router = unsafe { pac::IrqRouter::steal() }; + enable_peripheral_clock(PeripheralSelect::IrqRouter); + reset_peripheral_for_cycles(PeripheralSelect::IrqRouter, 2); unsafe { irq_router.dmasel0().write_with_zero(|w| w); irq_router.dmasel1().write_with_zero(|w| w); diff --git a/va416xx-hal/src/lib.rs b/va416xx-hal/src/lib.rs index 676a702..84b058d 100644 --- a/va416xx-hal/src/lib.rs +++ b/va416xx-hal/src/lib.rs @@ -41,11 +41,11 @@ pub mod edac; pub mod gpio; pub mod i2c; pub mod irq_router; +pub mod pins; pub mod pwm; pub mod spi; pub mod time; pub mod timer; -pub mod typelevel; pub mod uart; pub mod wdt; @@ -57,14 +57,11 @@ pub mod adc; #[cfg(not(feature = "va41628"))] pub mod dac; -#[derive(Debug, Eq, Copy, Clone, PartialEq)] -#[cfg_attr(feature = "defmt", derive(defmt::Format))] -pub enum FunSel { - Sel0 = 0b00, - Sel1 = 0b01, - Sel2 = 0b10, - Sel3 = 0b11, -} +pub use vorago_shared_periphs::{ + assert_peripheral_reset, deassert_peripheral_reset, disable_nvic_interrupt, + disable_peripheral_clock, enable_nvic_interrupt, enable_peripheral_clock, + reset_peripheral_for_cycles, FunSel, PeripheralSelect, +}; #[derive(Debug, PartialEq, Eq, thiserror::Error)] #[cfg_attr(feature = "defmt", derive(defmt::Format))] @@ -100,18 +97,41 @@ pub fn port_function_select( Ok(()) } -/// Enable a specific interrupt using the NVIC peripheral. -/// -/// # Safety -/// -/// This function is `unsafe` because it can break mask-based critical sections. -#[inline] -pub unsafe fn enable_nvic_interrupt(irq: pac::Interrupt) { - cortex_m::peripheral::NVIC::unmask(irq); +pub trait SyscfgExt { + fn enable_peripheral_clock(&mut self, clock: PeripheralSelect); + + fn disable_peripheral_clock(&mut self, clock: PeripheralSelect); + + fn assert_periph_reset(&mut self, periph: PeripheralSelect); + + 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. -#[inline] -pub fn disable_nvic_interrupt(irq: pac::Interrupt) { - cortex_m::peripheral::NVIC::mask(irq); +impl SyscfgExt for pac::Sysconfig { + #[inline(always)] + fn enable_peripheral_clock(&mut self, clock: PeripheralSelect) { + 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) + } } diff --git a/va416xx-hal/src/nvm.rs b/va416xx-hal/src/nvm.rs index e9de33f..2c72db9 100644 --- a/va416xx-hal/src/nvm.rs +++ b/va416xx-hal/src/nvm.rs @@ -6,11 +6,14 @@ //! //! - [Flashloader application](https://egit.irs.uni-stuttgart.de/rust/va416xx-rs/src/branch/main/flashloader) 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::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; @@ -65,10 +68,10 @@ pub struct VerifyError { } impl Nvm { - pub fn new(syscfg: &mut pac::Sysconfig, spi: pac::Spi3, _clocks: &Clocks) -> Self { - crate::clock::enable_peripheral_clock(syscfg, pac::Spi3::PERIPH_SEL); + pub fn new(spi: pac::Spi3, _clocks: &Clocks) -> Self { + enable_peripheral_clock(pac::Spi3::PERIPH_SEL); // 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 (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. - pub fn shutdown(&mut self, sys_cfg: &mut pac::Sysconfig) { + pub fn shutdown(&mut self) { self.wait_for_tx_idle(); self.write_with_bmstop(FRAM_WREN); self.wait_for_tx_idle(); 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. - pub fn release(mut self, sys_cfg: &mut pac::Sysconfig) -> pac::Spi3 { - self.shutdown(sys_cfg); + pub fn release(mut self) -> pac::Spi3 { + self.shutdown(); self.spi.take().unwrap() } @@ -268,7 +271,7 @@ impl Nvm { impl Drop for Nvm { fn drop(&mut self) { if self.spi.is_some() { - self.shutdown(unsafe { &mut pac::Sysconfig::steal() }); + self.shutdown(); } } } diff --git a/va416xx-hal/src/pins.rs b/va416xx-hal/src/pins.rs new file mode 100644 index 0000000..8500714 --- /dev/null +++ b/va416xx-hal/src/pins.rs @@ -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::*; diff --git a/va416xx-hal/src/prelude.rs b/va416xx-hal/src/prelude.rs index e67a9ed..1b86abe 100644 --- a/va416xx-hal/src/prelude.rs +++ b/va416xx-hal/src/prelude.rs @@ -1,4 +1,4 @@ //! Prelude -pub use crate::clock::{ClkgenExt, SyscfgExt}; +pub use crate::clock::ClkgenExt; pub use fugit::ExtU32 as _; pub use fugit::RateExtU32 as _; diff --git a/va416xx-hal/src/pwm.rs b/va416xx-hal/src/pwm.rs index 9d92cc9..decede2 100644 --- a/va416xx-hal/src/pwm.rs +++ b/va416xx-hal/src/pwm.rs @@ -1,459 +1,8 @@ //! 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 //! //! - [PWM example](https://egit.irs.uni-stuttgart.de/rust/va416xx-rs/src/branch/main/examples/simple/examples/pwm.rs) -use core::convert::Infallible; -use core::marker::PhantomData; - -use crate::pac; -use crate::time::Hertz; -pub use crate::timer::ValidTim; -use crate::timer::{TimAndPinRegister, TimDynRegister, TimPin, TimRegInterface, ValidTimAndPin}; -use crate::{clock::Clocks, gpio::DynPinId}; - -const DUTY_MAX: u16 = u16::MAX; - -#[derive(Debug)] -#[cfg_attr(feature = "defmt", derive(defmt::Format))] -pub(crate) struct PwmCommon { - clock: Hertz, - /// For PWMB, this is the upper limit - current_duty: u16, - /// For PWMA, this value will not be used - current_lower_limit: u16, - current_period: Hertz, - current_rst_val: u32, -} - -enum StatusSelPwm { - PwmA = 3, - PwmB = 4, -} - -pub struct PwmA {} -pub struct PwmB {} - -//================================================================================================== -// Strongly typed PWM pin -//================================================================================================== - -pub struct PwmPin { - reg: TimAndPinRegister, - inner: ReducedPwmPin, - mode: PhantomData, -} - -impl PwmPin -where - (Pin, Tim): ValidTimAndPin, -{ - /// 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 + Copy, - ) -> Self { - let mut pin = PwmPin { - inner: ReducedPwmPin::::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 { - 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) { - 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 From> for PwmPin -where - (Pin, Tim): ValidTimAndPin, -{ - fn from(other: PwmPin) -> Self { - let mut pwmb = Self { - reg: other.reg, - inner: other.inner.into(), - mode: PhantomData, - }; - pwmb.enable_pwm_b(); - pwmb - } -} - -impl From> for PwmPin -where - (PIN, TIM): ValidTimAndPin, -{ - fn from(other: PwmPin) -> Self { - let mut pwmb = Self { - reg: other.reg, - inner: other.inner.into(), - mode: PhantomData, - }; - pwmb.enable_pwm_a(); - pwmb - } -} - -impl PwmPin -where - (Pin, Tim): ValidTimAndPin, -{ - pub fn pwma( - tim_and_pin: (Pin, Tim), - sys_cfg: &mut pac::Sysconfig, - clocks: &Clocks, - initial_period: impl Into + Copy, - ) -> Self { - let mut pin: PwmPin = - Self::new(tim_and_pin, sys_cfg, clocks, initial_period); - pin.enable_pwm_a(); - pin - } -} - -impl PwmPin -where - (Pin, Tim): ValidTimAndPin, -{ - pub fn pwmb( - tim_and_pin: (Pin, Tim), - sys_cfg: &mut pac::Sysconfig, - clocks: &Clocks, - initial_period: impl Into + Copy, - ) -> Self { - let mut pin: PwmPin = - 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 { - dyn_reg: TimDynRegister, - common: PwmCommon, - mode: PhantomData, -} - -impl ReducedPwmPin { - 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) { - 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 From> for ReducedPwmPin -where - (Pin, Tim): ValidTimAndPin, -{ - fn from(value: PwmPin) -> Self { - value.downgrade() - } -} - -impl From> for ReducedPwmPin -where - (Pin, Tim): ValidTimAndPin, -{ - fn from(value: PwmPin) -> Self { - value.downgrade() - } -} - -impl From> for ReducedPwmPin { - fn from(other: ReducedPwmPin) -> Self { - let mut pwmb = Self { - dyn_reg: other.dyn_reg, - common: other.common, - mode: PhantomData, - }; - pwmb.enable_pwm_b(); - pwmb - } -} - -impl From> for ReducedPwmPin { - fn from(other: ReducedPwmPin) -> Self { - let mut pwmb = Self { - dyn_reg: other.dyn_reg, - common: other.common, - mode: PhantomData, - }; - pwmb.enable_pwm_a(); - pwmb - } -} - -//================================================================================================== -// PWMB implementations -//================================================================================================== - -impl PwmPin -where - (Pin, Tim): ValidTimAndPin, -{ - 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 { - #[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 embedded_hal::pwm::ErrorType for PwmPin { - 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 embedded_hal::pwm::SetDutyCycle for PwmPin { - #[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 - } -} +pub use vorago_shared_periphs::pwm::*; diff --git a/va416xx-hal/src/spi.rs b/va416xx-hal/src/spi.rs index 2681b83..09c5580 100644 --- a/va416xx-hal/src/spi.rs +++ b/va416xx-hal/src/spi.rs @@ -1,1251 +1,11 @@ -//! API for the SPI peripheral +//! API for the SPI peripheral. //! -//! The main abstraction provided by this module are the [Spi] and the [SpiBase] structure. -//! These provide the [embedded_hal::spi] traits, but also offer a low level interface -//! via the [SpiLowLevel] trait. +//! The main abstraction provided by this module is the [Spi] an structure. +//! It provides the [SpiBus trait](https://docs.rs/embedded-hal/latest/embedded_hal/spi/trait.SpiBus.html), +//! but also offer a low level interface via the [SpiLowLevel] trait. //! //! ## Examples //! //! - [Blocking SPI example](https://egit.irs.uni-stuttgart.de/rust/va416xx-rs/src/branch/main/examples/simple/examples/spi.rs) -use core::{convert::Infallible, marker::PhantomData, ops::Deref}; - -use embedded_hal::spi::{Mode, MODE_0}; - -use crate::{ - clock::{Clocks, PeripheralSelect, SyscfgExt}, - gpio::{ - AltFunc1, AltFunc2, AltFunc3, Pin, PA0, PA1, PA2, PA3, PA4, PA5, PA6, PA7, PA8, PA9, PB0, - PB1, PB12, PB13, PB14, PB15, PB2, PB3, PB4, PC0, PC1, PC10, PC11, PC7, PC8, PC9, PE12, - PE13, PE14, PE15, PE5, PE6, PE7, PE8, PE9, PF0, PF1, PG2, PG3, PG4, - }, - pac, - time::Hertz, - typelevel::{NoneT, Sealed}, -}; - -#[cfg(not(feature = "va41628"))] -use crate::gpio::{PB10, PB11, PB5, PB6, PB7, PB8, PB9, PE10, PE11, PF2, PF3, PF4, PF5, PF6, PF7}; - -//================================================================================================== -// Defintions -//================================================================================================== - -// FIFO has a depth of 16. -const FILL_DEPTH: usize = 12; - -pub const DEFAULT_CLK_DIV: u16 = 2; - -pub const BMSTART_BMSTOP_MASK: u32 = 1 << 31; -pub const BMSKIPDATA_MASK: u32 = 1 << 30; - -#[derive(Debug, PartialEq, Eq, Copy, Clone)] -#[cfg_attr(feature = "defmt", derive(defmt::Format))] -pub enum HwChipSelectId { - Id0 = 0, - Id1 = 1, - Id2 = 2, - Id3 = 3, - Id4 = 4, - Id5 = 5, - Id6 = 6, - Id7 = 7, - Invalid = 0xff, -} - -#[derive(Debug)] -#[cfg_attr(feature = "defmt", derive(defmt::Format))] -pub enum SpiId { - Spi0, - Spi1, - Spi2, - Spi3, - Invalid, -} - -#[derive(Debug, PartialEq, Eq, Copy, Clone)] -#[cfg_attr(feature = "defmt", derive(defmt::Format))] -pub enum WordSize { - OneBit = 0x00, - FourBits = 0x03, - EightBits = 0x07, - SixteenBits = 0x0f, -} - -pub type SpiRegBlock = pac::spi0::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 { - const IDX: u8; - const PERIPH_SEL: PeripheralSelect; - - fn ptr() -> *const SpiRegBlock; -} - -impl Instance for pac::Spi0 { - const IDX: u8 = 0; - const PERIPH_SEL: PeripheralSelect = PeripheralSelect::Spi0; - - #[inline(always)] - fn ptr() -> *const SpiRegBlock { - Self::ptr() - } -} - -impl Instance for pac::Spi1 { - const IDX: u8 = 1; - const PERIPH_SEL: PeripheralSelect = PeripheralSelect::Spi1; - - #[inline(always)] - fn ptr() -> *const SpiRegBlock { - Self::ptr() - } -} - -impl Instance for pac::Spi2 { - const IDX: u8 = 2; - const PERIPH_SEL: PeripheralSelect = PeripheralSelect::Spi2; - - #[inline(always)] - fn ptr() -> *const SpiRegBlock { - Self::ptr() - } -} - -impl Instance for pac::Spi3 { - const IDX: u8 = 3; - const PERIPH_SEL: PeripheralSelect = PeripheralSelect::Spi3; - - #[inline(always)] - fn ptr() -> *const SpiRegBlock { - Self::ptr() - } -} - -//================================================================================================== -// Pin type definitions -//================================================================================================== - -pub trait PinSck: Sealed {} -pub trait PinMosi: Sealed {} -pub trait PinMiso: Sealed {} - -pub trait HwCsProvider: Sealed { - const CS_ID: HwChipSelectId; - const SPI_ID: SpiId; -} - -pub trait OptionalHwCs: HwCsProvider + Sealed {} - -macro_rules! hw_cs_pins { - ($SPIx:path, $portId: path: - $( - ($PXx:ident, $AFx:ident, $HwCsIdent:path, $typedef:ident $(, $meta: meta)?), - )+ - ) => { - $( - $(#[$meta])? - impl HwCsProvider for Pin<$PXx, $AFx> { - const CS_ID: HwChipSelectId = $HwCsIdent; - const SPI_ID: SpiId = $portId; - } - - $(#[$meta])? - impl OptionalHwCs<$SPIx> for Pin<$PXx, $AFx> {} - - $(#[$meta])? - pub type $typedef = Pin<$PXx, $AFx>; - )+ - }; -} - -impl HwCsProvider for NoneT { - const CS_ID: HwChipSelectId = HwChipSelectId::Invalid; - const SPI_ID: SpiId = SpiId::Invalid; -} - -impl OptionalHwCs for NoneT {} -impl OptionalHwCs for NoneT {} -impl OptionalHwCs for NoneT {} -impl OptionalHwCs for NoneT {} - -pub struct RomSpiSck; -pub struct RomSpiMiso; -pub struct RomSpiMosi; - -impl Sealed for RomSpiSck {} -impl Sealed for RomSpiMosi {} -impl Sealed for RomSpiMiso {} - -// SPI 0 - -impl PinSck for Pin {} -impl PinMosi for Pin {} -impl PinMiso for Pin {} - -// SPI 1 -#[cfg(not(feature = "va41628"))] -impl PinSck for Pin {} -#[cfg(not(feature = "va41628"))] -impl PinMosi for Pin {} -#[cfg(not(feature = "va41628"))] -impl PinMiso for Pin {} - -impl PinSck for Pin {} -impl PinMosi for Pin {} -impl PinMiso for Pin {} - -impl PinSck for Pin {} -impl PinMiso for Pin {} - -impl PinSck for Pin {} -impl PinMosi for Pin {} -impl PinMiso for Pin {} - -#[cfg(not(feature = "va41628"))] -impl PinSck for Pin {} -#[cfg(not(feature = "va41628"))] -impl PinMosi for Pin {} -#[cfg(not(feature = "va41628"))] -impl PinMiso for Pin {} - -// SPI 2 - -impl PinSck for Pin {} -impl PinMosi for Pin {} -impl PinMiso for Pin {} - -#[cfg(not(feature = "va41628"))] -impl PinSck for Pin {} -#[cfg(not(feature = "va41628"))] -impl PinMosi for Pin {} -#[cfg(not(feature = "va41628"))] -impl PinMiso for Pin {} - -// SPI3 is shared with the ROM SPI pins and has its own dedicated pins. -// -impl PinSck for RomSpiSck {} -impl PinMosi for RomSpiMosi {} -impl PinMiso for RomSpiMiso {} - -// SPI 0 HW CS pins - -hw_cs_pins!( - pac::Spi0, SpiId::Spi0: - (PB14, AltFunc1, HwChipSelectId::Id0, HwCs0Spi0), - (PB13, AltFunc1, HwChipSelectId::Id1, HwCs1Spi0), - (PB12, AltFunc1, HwChipSelectId::Id2, HwCs2Spi0), - (PB11, AltFunc1, HwChipSelectId::Id3, HwCs3Spi0, cfg(not(feature="va41628"))), -); - -hw_cs_pins!( - pac::Spi1, SpiId::Spi1: - (PB7, AltFunc3, HwChipSelectId::Id0, HwCs0Spi1Pb, cfg(not(feature="va41628"))), - (PB6, AltFunc3, HwChipSelectId::Id1, HwCs1Spi1Pb, cfg(not(feature="va41628"))), - (PB5, AltFunc3, HwChipSelectId::Id2, HwCs2Spi1Pb, cfg(not(feature="va41628"))), - (PB4, AltFunc3, HwChipSelectId::Id3, HwCs3Spi1Pb), - (PB3, AltFunc3, HwChipSelectId::Id4, HwCs4Spi1Pb), - (PB2, AltFunc3, HwChipSelectId::Id5, HwCs5Spi1Pb), - (PB1, AltFunc3, HwChipSelectId::Id6, HwCs6Spi1Pb), - (PB0, AltFunc3, HwChipSelectId::Id7, HwCs7Spi1Pb), - (PC8, AltFunc2, HwChipSelectId::Id0, HwCs0Spi1Pc), - (PC7, AltFunc2, HwChipSelectId::Id1, HwCs1Spi1Pc), - (PE12, AltFunc2, HwChipSelectId::Id0, HwCs0Spi1Pe), - (PE11, AltFunc2, HwChipSelectId::Id1, HwCs1Spi1Pe, cfg(not(feature="va41628"))), - (PE10, AltFunc2, HwChipSelectId::Id2, HwCs2Spi1Pe, cfg(not(feature="va41628"))), - (PE9, AltFunc2, HwChipSelectId::Id3, HwCs3Spi1Pe), - (PE8, AltFunc2, HwChipSelectId::Id4, HwCs4Spi1Pe), - (PE7, AltFunc3, HwChipSelectId::Id5, HwCs5Spi1Pe), - (PE6, AltFunc3, HwChipSelectId::Id6, HwCs6Spi1Pe), - (PE5, AltFunc3, HwChipSelectId::Id7, HwCs7Spi1Pe), - (PF2, AltFunc1, HwChipSelectId::Id0, HwCs0Spi1Pf, cfg(not(feature="va41628"))), - (PG2, AltFunc2, HwChipSelectId::Id0, HwCs0Spi1Pg), -); - -hw_cs_pins!( - pac::Spi2, SpiId::Spi2: - (PA4, AltFunc2, HwChipSelectId::Id0, HwCs0Spi2Pa), - (PA3, AltFunc2, HwChipSelectId::Id1, HwCs1Spi2Pa), - (PA2, AltFunc2, HwChipSelectId::Id2, HwCs2Spi2Pa), - (PA1, AltFunc2, HwChipSelectId::Id3, HwCs3Spi2Pa), - (PA0, AltFunc2, HwChipSelectId::Id4, HwCs4Spi2Pa), - (PA8, AltFunc2, HwChipSelectId::Id6, HwCs6Spi2Pa), - (PA9, AltFunc2, HwChipSelectId::Id5, HwCs5Spi2Pa), - (PF0, AltFunc2, HwChipSelectId::Id4, HwCs4Spi2Pf), - (PF1, AltFunc2, HwChipSelectId::Id3, HwCs3Spi2Pf), - (PF2, AltFunc2, HwChipSelectId::Id2, HwCs2Spi2Pf, cfg(not(feature="va41628"))), - (PF3, AltFunc2, HwChipSelectId::Id1, HwCs1Spi2Pf, cfg(not(feature="va41628"))), - (PF4, AltFunc2, HwChipSelectId::Id0, HwCs0Spi2Pf, cfg(not(feature="va41628"))), -); - -//================================================================================================== -// Config -//================================================================================================== - -pub trait TransferConfigProvider { - fn sod(&mut self, sod: bool); - fn blockmode(&mut self, blockmode: bool); - fn mode(&mut self, mode: Mode); - fn clk_cfg(&mut self, clk_cfg: SpiClkConfig); - fn hw_cs_id(&self) -> u8; -} - -/// This struct contains all configuration parameter which are transfer specific -/// and might change for transfers to different SPI slaves -#[derive(Copy, Clone, Debug)] -#[cfg_attr(feature = "defmt", derive(defmt::Format))] -pub struct TransferConfigWithHwcs { - pub hw_cs: Option, - pub cfg: TransferConfig, -} - -/// Type erased variant of the transfer configuration. This is required to avoid generics in -/// the SPI constructor. -#[derive(Copy, Clone, Debug)] -#[cfg_attr(feature = "defmt", derive(defmt::Format))] -pub struct TransferConfig { - pub clk_cfg: Option, - pub mode: Option, - pub sod: bool, - /// If this is enabled, all data in the FIFO is transmitted in a single frame unless - /// the BMSTOP bit is set on a dataword. A frame is defined as CSn being active for the - /// duration of multiple data words - pub blockmode: bool, - /// Only used when blockmode is used. The SCK will be stalled until an explicit stop bit - /// is set on a written word. - pub bmstall: bool, - pub hw_cs: HwChipSelectId, -} - -impl TransferConfigWithHwcs { - pub fn new_no_hw_cs( - clk_cfg: Option, - mode: Option, - blockmode: bool, - bmstall: bool, - sod: bool, - ) -> Self { - TransferConfigWithHwcs { - hw_cs: None, - cfg: TransferConfig { - clk_cfg, - mode, - sod, - blockmode, - bmstall, - hw_cs: HwChipSelectId::Invalid, - }, - } - } -} - -impl TransferConfigWithHwcs { - pub fn new( - clk_cfg: Option, - mode: Option, - hw_cs: Option, - blockmode: bool, - bmstall: bool, - sod: bool, - ) -> Self { - TransferConfigWithHwcs { - hw_cs, - cfg: TransferConfig { - clk_cfg, - mode, - sod, - blockmode, - bmstall, - hw_cs: HwCs::CS_ID, - }, - } - } - - pub fn downgrade(self) -> TransferConfig { - self.cfg - } -} - -impl TransferConfigProvider for TransferConfigWithHwcs { - /// Slave Output Disable - fn sod(&mut self, sod: bool) { - self.cfg.sod = sod; - } - - fn blockmode(&mut self, blockmode: bool) { - self.cfg.blockmode = blockmode; - } - - fn mode(&mut self, mode: Mode) { - self.cfg.mode = Some(mode); - } - - fn clk_cfg(&mut self, clk_cfg: SpiClkConfig) { - self.cfg.clk_cfg = Some(clk_cfg); - } - - fn hw_cs_id(&self) -> u8 { - HwCs::CS_ID as u8 - } -} - -/// Configuration options for the whole SPI bus. See Programmer Guide p.92 for more details -#[derive(Debug)] -#[cfg_attr(feature = "defmt", derive(defmt::Format))] -pub struct SpiConfig { - clk: SpiClkConfig, - // SPI mode configuration - pub init_mode: Mode, - /// If this is enabled, all data in the FIFO is transmitted in a single frame unless - /// the BMSTOP bit is set on a dataword. A frame is defined as CSn being active for the - /// duration of multiple data words. Defaults to true. - pub blockmode: bool, - /// This enables the stalling of the SPI SCK if in blockmode and the FIFO is empty. - /// Currently enabled by default. - pub bmstall: bool, - /// By default, configure SPI for master mode (ms == false) - ms: bool, - /// Slave output disable. Useful if separate GPIO pins or decoders are used for CS control - pub slave_output_disable: bool, - /// Loopback mode. If you use this, don't connect MISO to MOSI, they will be tied internally - pub loopback_mode: bool, - /// Enable Master Delayer Capture Mode. See Programmers Guide p.92 for more details - pub master_delayer_capture: bool, -} - -impl Default for SpiConfig { - fn default() -> Self { - Self { - init_mode: MODE_0, - blockmode: true, - bmstall: true, - // Default value is definitely valid. - clk: SpiClkConfig::from_div(DEFAULT_CLK_DIV).unwrap(), - ms: Default::default(), - slave_output_disable: Default::default(), - loopback_mode: Default::default(), - master_delayer_capture: Default::default(), - } - } -} - -impl SpiConfig { - pub fn loopback(mut self, enable: bool) -> Self { - self.loopback_mode = enable; - self - } - - pub fn blockmode(mut self, enable: bool) -> Self { - self.blockmode = enable; - self - } - - pub fn bmstall(mut self, enable: bool) -> Self { - self.bmstall = enable; - self - } - - pub fn mode(mut self, mode: Mode) -> Self { - self.init_mode = mode; - self - } - - pub fn clk_cfg(mut self, clk_cfg: SpiClkConfig) -> Self { - self.clk = clk_cfg; - self - } - - pub fn master_mode(mut self, master: bool) -> Self { - self.ms = !master; - self - } - - pub fn slave_output_disable(mut self, sod: bool) -> Self { - self.slave_output_disable = sod; - self - } -} - -//================================================================================================== -// Word Size -//================================================================================================== - -/// Configuration trait for the Word Size used by the SPI peripheral -pub trait WordProvider: Copy + Default + Into + TryFrom + 'static { - const MASK: u32; - fn word_reg() -> u8; -} - -impl WordProvider for u8 { - const MASK: u32 = 0xff; - fn word_reg() -> u8 { - 0x07 - } -} - -impl WordProvider for u16 { - const MASK: u32 = 0xffff; - fn word_reg() -> u8 { - 0x0f - } -} - -//================================================================================================== -// Spi -//================================================================================================== - -/// Low level access trait for the SPI peripheral. -pub trait SpiLowLevel { - /// Low level function to write a word to the SPI FIFO but also checks whether - /// there is actually data in the FIFO. - /// - /// Uses the [nb] API to allow usage in blocking and non-blocking contexts. - fn write_fifo(&self, data: u32) -> nb::Result<(), Infallible>; - - /// Low level function to write a word to the SPI FIFO without checking whether - /// there FIFO is full. - /// - /// This does not necesarily mean there is a space in the FIFO available. - /// Use [Self::write_fifo] function to write a word into the FIFO reliably. - fn write_fifo_unchecked(&self, data: u32); - - /// Low level function to read a word from the SPI FIFO. Must be preceeded by a - /// [Self::write_fifo] call. - /// - /// Uses the [nb] API to allow usage in blocking and non-blocking contexts. - fn read_fifo(&self) -> nb::Result; - - /// Low level function to read a word from from the SPI FIFO. - /// - /// This does not necesarily mean there is a word in the FIFO available. - /// Use the [Self::read_fifo] function to read a word from the FIFO reliably using the [nb] - /// API. - /// You might also need to mask the value to ignore the BMSTART/BMSTOP bit. - fn read_fifo_unchecked(&self) -> u32; -} - -pub struct SpiBase { - spi: SpiInstance, - cfg: SpiConfig, - apb1_clk: Hertz, - /// Fill word for read-only SPI transactions. - pub fill_word: Word, - blockmode: bool, - bmstall: bool, - word: PhantomData, -} - -pub struct Spi { - inner: SpiBase, - pins: Pins, -} - -#[inline(always)] -pub fn mode_to_cpo_cph_bit(mode: embedded_hal::spi::Mode) -> (bool, bool) { - match mode { - embedded_hal::spi::MODE_0 => (false, false), - embedded_hal::spi::MODE_1 => (false, true), - embedded_hal::spi::MODE_2 => (true, false), - embedded_hal::spi::MODE_3 => (true, true), - } -} - -#[derive(Debug, Copy, Clone, PartialEq, Eq)] -#[cfg_attr(feature = "defmt", derive(defmt::Format))] -pub struct SpiClkConfig { - prescale_val: u16, - scrdv: u8, -} - -impl SpiClkConfig { - pub fn prescale_val(&self) -> u16 { - self.prescale_val - } - pub fn scrdv(&self) -> u8 { - self.scrdv - } -} - -impl SpiClkConfig { - pub fn new(prescale_val: u16, scrdv: u8) -> Self { - Self { - prescale_val, - scrdv, - } - } - - pub fn from_div(div: u16) -> Result { - spi_clk_config_from_div(div) - } - - pub fn from_clk(spi_clk: Hertz, clocks: &Clocks) -> Option { - clk_div_for_target_clock(spi_clk, clocks).map(|div| spi_clk_config_from_div(div).unwrap()) - } -} - -#[derive(Debug, thiserror::Error)] -#[cfg_attr(feature = "defmt", derive(defmt::Format))] -pub enum SpiClkConfigError { - #[error("division by zero")] - DivIsZero, - #[error("divide value is not even")] - DivideValueNotEven, - #[error("scrdv value is too large")] - ScrdvValueTooLarge, -} - -#[inline] -pub fn spi_clk_config_from_div(mut div: u16) -> Result { - if div == 0 { - return Err(SpiClkConfigError::DivIsZero); - } - if div % 2 != 0 { - return Err(SpiClkConfigError::DivideValueNotEven); - } - let mut prescale_val = 0; - - // find largest (even) prescale value that divides into div - for i in (2..=0xfe).rev().step_by(2) { - if div % i == 0 { - prescale_val = i; - break; - } - } - - if prescale_val == 0 { - return Err(SpiClkConfigError::DivideValueNotEven); - } - - div /= prescale_val; - if div > u8::MAX as u16 + 1 { - return Err(SpiClkConfigError::ScrdvValueTooLarge); - } - Ok(SpiClkConfig { - prescale_val, - scrdv: (div - 1) as u8, - }) -} - -#[inline] -pub fn clk_div_for_target_clock(spi_clk: impl Into, clocks: &Clocks) -> Option { - let spi_clk = spi_clk.into(); - if spi_clk > clocks.apb1() { - return None; - } - - // Step 1: Calculate raw divider. - let raw_div = clocks.apb1().raw() / spi_clk.raw(); - let remainder = clocks.apb1().raw() % spi_clk.raw(); - - // Step 2: Round up if necessary. - let mut rounded_div = if remainder * 2 >= spi_clk.raw() { - raw_div + 1 - } else { - raw_div - }; - - if rounded_div % 2 != 0 { - // Take slower clock conservatively. - rounded_div += 1; - } - if rounded_div > u16::MAX as u32 { - return None; - } - Some(rounded_div as u16) -} - -impl SpiBase -where - >::Error: core::fmt::Debug, -{ - #[inline] - pub fn cfg_clock(&mut self, cfg: SpiClkConfig) { - self.spi - .ctrl0() - .modify(|_, w| unsafe { w.scrdv().bits(cfg.scrdv) }); - self.spi - .clkprescale() - .write(|w| unsafe { w.bits(cfg.prescale_val as u32) }); - } - - #[inline] - pub fn cfg_clock_from_div(&mut self, div: u16) -> Result<(), SpiClkConfigError> { - let val = spi_clk_config_from_div(div)?; - self.cfg_clock(val); - Ok(()) - } - - #[inline] - pub fn cfg_mode(&mut self, mode: Mode) { - let (cpo_bit, cph_bit) = mode_to_cpo_cph_bit(mode); - self.spi.ctrl0().modify(|_, w| { - w.spo().bit(cpo_bit); - w.sph().bit(cph_bit) - }); - } - - #[inline] - pub fn spi(&self) -> &SpiInstance { - &self.spi - } - - #[inline] - pub fn clear_tx_fifo(&self) { - self.spi.fifo_clr().write(|w| w.txfifo().set_bit()); - } - - #[inline] - pub fn clear_rx_fifo(&self) { - self.spi.fifo_clr().write(|w| w.rxfifo().set_bit()); - } - - #[inline] - pub fn perid(&self) -> u32 { - self.spi.perid().read().bits() - } - - #[inline] - pub fn cfg_hw_cs(&mut self, hw_cs: HwChipSelectId) { - if hw_cs == HwChipSelectId::Invalid { - return; - } - self.spi.ctrl1().modify(|_, w| { - w.sod().clear_bit(); - unsafe { - w.ss().bits(hw_cs as u8); - } - w - }); - } - - #[inline] - pub fn cfg_hw_cs_with_pin>(&mut self, _: &HwCs) { - self.cfg_hw_cs(HwCs::CS_ID); - } - - #[inline] - pub fn cfg_hw_cs_disable(&mut self) { - self.spi.ctrl1().modify(|_, w| { - w.sod().set_bit(); - w - }); - } - - pub fn cfg_transfer>( - &mut self, - transfer_cfg: &TransferConfigWithHwcs, - ) { - if let Some(trans_clk_div) = transfer_cfg.cfg.clk_cfg { - self.cfg_clock(trans_clk_div); - } - if let Some(mode) = transfer_cfg.cfg.mode { - self.cfg_mode(mode); - } - self.blockmode = transfer_cfg.cfg.blockmode; - self.spi.ctrl1().modify(|_, w| { - if transfer_cfg.cfg.sod { - w.sod().set_bit(); - } else if transfer_cfg.hw_cs.is_some() { - w.sod().clear_bit(); - unsafe { - w.ss().bits(HwCs::CS_ID as u8); - } - } else { - w.sod().clear_bit(); - } - w.blockmode().bit(transfer_cfg.cfg.blockmode); - w.bmstall().bit(transfer_cfg.cfg.bmstall) - }); - } - - /// Low level function to write a word to the SPI FIFO but also checks whether - /// there is actually data in the FIFO. - /// - /// Uses the [nb] API to allow usage in blocking and non-blocking contexts. - #[inline(always)] - pub fn write_fifo(&self, data: u32) -> nb::Result<(), Infallible> { - if self.spi.status().read().tnf().bit_is_clear() { - return Err(nb::Error::WouldBlock); - } - self.write_fifo_unchecked(data); - Ok(()) - } - - /// Low level function to write a word to the SPI FIFO without checking whether - /// there FIFO is full. - /// - /// This does not necesarily mean there is a space in the FIFO available. - /// Use [Self::write_fifo] function to write a word into the FIFO reliably. - #[inline(always)] - pub fn write_fifo_unchecked(&self, data: u32) { - self.spi.data().write(|w| unsafe { w.bits(data) }); - } - - /// Low level function to read a word from the SPI FIFO. Must be preceeded by a - /// [Self::write_fifo] call. - /// - /// Uses the [nb] API to allow usage in blocking and non-blocking contexts. - #[inline(always)] - pub fn read_fifo(&self) -> nb::Result { - if self.spi.status().read().rne().bit_is_clear() { - return Err(nb::Error::WouldBlock); - } - Ok(self.read_fifo_unchecked()) - } - - /// Low level function to read a word from from the SPI FIFO. - /// - /// This does not necesarily mean there is a word in the FIFO available. - /// Use the [Self::read_fifo] function to read a word from the FIFO reliably using the [nb] - /// API. - /// You might also need to mask the value to ignore the BMSTART/BMSTOP bit. - #[inline(always)] - pub fn read_fifo_unchecked(&self) -> u32 { - self.spi.data().read().bits() - } - - fn flush_internal(&self) { - let mut status_reg = self.spi.status().read(); - while status_reg.tfe().bit_is_clear() - || status_reg.rne().bit_is_set() - || status_reg.busy().bit_is_set() - { - if status_reg.rne().bit_is_set() { - self.read_fifo_unchecked(); - } - status_reg = self.spi.status().read(); - } - } - - fn transfer_preparation(&self, words: &[Word]) -> Result<(), Infallible> { - if words.is_empty() { - return Ok(()); - } - self.flush_internal(); - Ok(()) - } - - // The FIFO can hold a guaranteed amount of data, so we can pump it on transfer - // initialization. Returns the amount of written bytes. - fn initial_send_fifo_pumping_with_words(&self, words: &[Word]) -> usize { - if self.blockmode { - self.spi.ctrl1().modify(|_, w| w.mtxpause().set_bit()); - } - // Fill the first half of the write FIFO - let mut current_write_idx = 0; - let smaller_idx = core::cmp::min(FILL_DEPTH, words.len()); - for _ in 0..smaller_idx { - if current_write_idx == smaller_idx.saturating_sub(1) && self.bmstall { - self.write_fifo_unchecked(words[current_write_idx].into() | BMSTART_BMSTOP_MASK); - } else { - self.write_fifo_unchecked(words[current_write_idx].into()); - } - current_write_idx += 1; - } - if self.blockmode { - self.spi.ctrl1().modify(|_, w| w.mtxpause().clear_bit()); - } - current_write_idx - } - - // The FIFO can hold a guaranteed amount of data, so we can pump it on transfer - // initialization. - fn initial_send_fifo_pumping_with_fill_words(&self, send_len: usize) -> usize { - if self.blockmode { - self.spi.ctrl1().modify(|_, w| w.mtxpause().set_bit()); - } - // Fill the first half of the write FIFO - let mut current_write_idx = 0; - let smaller_idx = core::cmp::min(FILL_DEPTH, send_len); - for _ in 0..smaller_idx { - if current_write_idx == smaller_idx.saturating_sub(1) && self.bmstall { - self.write_fifo_unchecked(self.fill_word.into() | BMSTART_BMSTOP_MASK); - } else { - self.write_fifo_unchecked(self.fill_word.into()); - } - current_write_idx += 1; - } - if self.blockmode { - self.spi.ctrl1().modify(|_, w| w.mtxpause().clear_bit()); - } - current_write_idx - } -} - -impl SpiLowLevel for SpiBase -where - >::Error: core::fmt::Debug, -{ - #[inline(always)] - fn write_fifo(&self, data: u32) -> nb::Result<(), Infallible> { - if self.spi.status().read().tnf().bit_is_clear() { - return Err(nb::Error::WouldBlock); - } - self.write_fifo_unchecked(data); - Ok(()) - } - - #[inline(always)] - fn write_fifo_unchecked(&self, data: u32) { - self.spi.data().write(|w| unsafe { w.bits(data) }); - } - - #[inline(always)] - fn read_fifo(&self) -> nb::Result { - if self.spi.status().read().rne().bit_is_clear() { - return Err(nb::Error::WouldBlock); - } - Ok(self.read_fifo_unchecked()) - } - - #[inline(always)] - fn read_fifo_unchecked(&self) -> u32 { - self.spi.data().read().bits() - } -} - -impl embedded_hal::spi::ErrorType for SpiBase { - type Error = Infallible; -} - -impl embedded_hal::spi::SpiBus for SpiBase -where - >::Error: core::fmt::Debug, -{ - fn read(&mut self, words: &mut [Word]) -> Result<(), Self::Error> { - self.transfer_preparation(words)?; - let mut current_read_idx = 0; - let mut current_write_idx = self.initial_send_fifo_pumping_with_fill_words(words.len()); - loop { - if current_read_idx < words.len() { - words[current_read_idx] = (nb::block!(self.read_fifo())? & Word::MASK) - .try_into() - .unwrap(); - current_read_idx += 1; - } - if current_write_idx < words.len() { - if current_write_idx == words.len() - 1 && self.bmstall { - nb::block!(self.write_fifo(self.fill_word.into() | BMSTART_BMSTOP_MASK))?; - } else { - nb::block!(self.write_fifo(self.fill_word.into()))?; - } - current_write_idx += 1; - } - if current_read_idx >= words.len() && current_write_idx >= words.len() { - break; - } - } - Ok(()) - } - - fn write(&mut self, words: &[Word]) -> Result<(), Self::Error> { - self.transfer_preparation(words)?; - let mut current_write_idx = self.initial_send_fifo_pumping_with_words(words); - while current_write_idx < words.len() { - if current_write_idx == words.len() - 1 && self.bmstall { - nb::block!(self.write_fifo(words[current_write_idx].into() | BMSTART_BMSTOP_MASK))?; - } else { - nb::block!(self.write_fifo(words[current_write_idx].into()))?; - } - current_write_idx += 1; - // Ignore received words. - if self.spi.status().read().rne().bit_is_set() { - self.clear_rx_fifo(); - } - } - Ok(()) - } - - fn transfer(&mut self, read: &mut [Word], write: &[Word]) -> Result<(), Self::Error> { - self.transfer_preparation(write)?; - let mut current_read_idx = 0; - let mut current_write_idx = self.initial_send_fifo_pumping_with_words(write); - while current_read_idx < read.len() || current_write_idx < write.len() { - if current_write_idx < write.len() { - if current_write_idx == write.len() - 1 && self.bmstall { - nb::block!( - self.write_fifo(write[current_write_idx].into() | BMSTART_BMSTOP_MASK) - )?; - } else { - nb::block!(self.write_fifo(write[current_write_idx].into()))?; - } - current_write_idx += 1; - } - if current_read_idx < read.len() { - read[current_read_idx] = (nb::block!(self.read_fifo())? & Word::MASK) - .try_into() - .unwrap(); - current_read_idx += 1; - } - } - - Ok(()) - } - - fn transfer_in_place(&mut self, words: &mut [Word]) -> Result<(), Self::Error> { - self.transfer_preparation(words)?; - let mut current_read_idx = 0; - let mut current_write_idx = self.initial_send_fifo_pumping_with_words(words); - - while current_read_idx < words.len() || current_write_idx < words.len() { - if current_write_idx < words.len() { - if current_write_idx == words.len() - 1 && self.bmstall { - nb::block!( - self.write_fifo(words[current_write_idx].into() | BMSTART_BMSTOP_MASK) - )?; - } else { - nb::block!(self.write_fifo(words[current_write_idx].into()))?; - } - current_write_idx += 1; - } - if current_read_idx < words.len() && current_read_idx < current_write_idx { - words[current_read_idx] = (nb::block!(self.read_fifo())? & Word::MASK) - .try_into() - .unwrap(); - current_read_idx += 1; - } - } - Ok(()) - } - - fn flush(&mut self) -> Result<(), Self::Error> { - self.flush_internal(); - Ok(()) - } -} - -impl< - SpiI: Instance, - Sck: PinSck, - Miso: PinMiso, - Mosi: PinMosi, - Word: WordProvider, - > Spi -where - >::Error: core::fmt::Debug, -{ - /// Create a new SPI struct - /// - /// You can delete the pin type information by calling the - /// [`downgrade`](Self::downgrade) function - /// - /// ## Arguments - /// * `spi` - SPI bus to use - /// * `pins` - Pins to be used for SPI transactions. These pins are consumed - /// to ensure the pins can not be used for other purposes anymore - /// * `spi_cfg` - Configuration specific to the SPI bus - /// * `transfer_cfg` - Optional initial transfer configuration which includes - /// configuration which can change across individual SPI transfers like SPI mode - /// or SPI clock. If only one device is connected, this configuration only needs - /// to be done once. - /// * `syscfg` - Can be passed optionally to enable the peripheral clock - pub fn new( - syscfg: &mut pac::Sysconfig, - clocks: &crate::clock::Clocks, - spi: SpiI, - pins: (Sck, Miso, Mosi), - spi_cfg: SpiConfig, - ) -> Self { - crate::clock::enable_peripheral_clock(syscfg, SpiI::PERIPH_SEL); - // This is done in the C HAL. - syscfg.assert_periph_reset_for_two_cycles(SpiI::PERIPH_SEL); - let SpiConfig { - clk, - init_mode, - blockmode, - bmstall, - ms, - slave_output_disable, - loopback_mode, - master_delayer_capture, - } = spi_cfg; - - let (cpo_bit, cph_bit) = mode_to_cpo_cph_bit(init_mode); - spi.ctrl0().write(|w| { - unsafe { - w.size().bits(Word::word_reg()); - w.scrdv().bits(clk.scrdv); - // Clear clock phase and polarity. Will be set to correct value for each - // transfer - w.spo().bit(cpo_bit); - w.sph().bit(cph_bit) - } - }); - - spi.ctrl1().write(|w| { - w.lbm().bit(loopback_mode); - w.sod().bit(slave_output_disable); - w.ms().bit(ms); - w.mdlycap().bit(master_delayer_capture); - w.blockmode().bit(blockmode); - w.bmstall().bit(bmstall); - unsafe { w.ss().bits(0) } - }); - spi.clkprescale() - .write(|w| unsafe { w.bits(clk.prescale_val as u32) }); - - spi.fifo_clr().write(|w| { - w.rxfifo().set_bit(); - w.txfifo().set_bit() - }); - // Enable the peripheral as the last step as recommended in the - // programmers guide - spi.ctrl1().modify(|_, w| w.enable().set_bit()); - Spi { - inner: SpiBase { - spi, - cfg: spi_cfg, - apb1_clk: clocks.apb1(), - fill_word: Default::default(), - bmstall, - blockmode, - word: PhantomData, - }, - pins, - } - } - - delegate::delegate! { - to self.inner { - #[inline] - pub fn cfg_clock(&mut self, cfg: SpiClkConfig); - - #[inline] - pub fn cfg_clock_from_div(&mut self, div: u16) -> Result<(), SpiClkConfigError>; - - #[inline] - pub fn spi(&self) -> &SpiI; - - #[inline] - pub fn cfg_mode(&mut self, mode: Mode); - - #[inline] - pub fn perid(&self) -> u32; - - pub fn cfg_transfer>( - &mut self, transfer_cfg: &TransferConfigWithHwcs - ); - } - } - - #[inline] - pub fn set_fill_word(&mut self, fill_word: Word) { - self.inner.fill_word = fill_word; - } - - #[inline] - pub fn fill_word(&self) -> Word { - self.inner.fill_word - } - - /// Releases the SPI peripheral and associated pins - pub fn release(self) -> (SpiI, (Sck, Miso, Mosi), SpiConfig) { - (self.inner.spi, self.pins, self.inner.cfg) - } - - pub fn downgrade(self) -> SpiBase { - self.inner - } -} - -impl< - SpiI: Instance, - Sck: PinSck, - Miso: PinMiso, - Mosi: PinMosi, - Word: WordProvider, - > SpiLowLevel for Spi -where - >::Error: core::fmt::Debug, -{ - delegate::delegate! { - to self.inner { - fn write_fifo(&self, data: u32) -> nb::Result<(), Infallible>; - fn write_fifo_unchecked(&self, data: u32); - fn read_fifo(&self) -> nb::Result; - fn read_fifo_unchecked(&self) -> u32; - } - } -} - -impl< - SpiI: Instance, - Word: WordProvider, - Sck: PinSck, - Miso: PinMiso, - Mosi: PinMosi, - > embedded_hal::spi::ErrorType for Spi -{ - type Error = Infallible; -} - -impl< - SpiI: Instance, - Word: WordProvider, - Sck: PinSck, - Miso: PinMiso, - Mosi: PinMosi, - > embedded_hal::spi::SpiBus for Spi -where - >::Error: core::fmt::Debug, -{ - delegate::delegate! { - to self.inner { - fn read(&mut self, words: &mut [Word]) -> Result<(), Self::Error>; - fn write(&mut self, words: &[Word]) -> Result<(), Self::Error>; - fn transfer(&mut self, read: &mut [Word], write: &[Word]) -> Result<(), Self::Error>; - fn transfer_in_place(&mut self, words: &mut [Word]) -> Result<(), Self::Error>; - fn flush(&mut self) -> Result<(), Self::Error>; - } - } -} - -/// Changing the word size also requires a type conversion -impl, Miso: PinMiso, Mosi: PinMosi> - From> for Spi -{ - fn from(old_spi: Spi) -> Self { - old_spi - .inner - .spi - .ctrl0() - .modify(|_, w| unsafe { w.size().bits(WordSize::SixteenBits as u8) }); - Spi { - inner: SpiBase { - spi: old_spi.inner.spi, - cfg: old_spi.inner.cfg, - blockmode: old_spi.inner.blockmode, - bmstall: old_spi.inner.bmstall, - fill_word: Default::default(), - apb1_clk: old_spi.inner.apb1_clk, - word: PhantomData, - }, - pins: old_spi.pins, - } - } -} - -/// Changing the word size also requires a type conversion -impl, Miso: PinMiso, Mosi: PinMosi> - From> for Spi -{ - fn from(old_spi: Spi) -> Self { - old_spi - .inner - .spi - .ctrl0() - .modify(|_, w| unsafe { w.size().bits(WordSize::EightBits as u8) }); - Spi { - inner: SpiBase { - spi: old_spi.inner.spi, - cfg: old_spi.inner.cfg, - blockmode: old_spi.inner.blockmode, - bmstall: old_spi.inner.bmstall, - apb1_clk: old_spi.inner.apb1_clk, - fill_word: Default::default(), - word: PhantomData, - }, - pins: old_spi.pins, - } - } -} +//! - [NVM library][crate::nvm] +pub use vorago_shared_periphs::spi::*; diff --git a/va416xx-hal/src/timer.rs b/va416xx-hal/src/timer.rs index 07de4fe..6e2b083 100644 --- a/va416xx-hal/src/timer.rs +++ b/va416xx-hal/src/timer.rs @@ -2,904 +2,8 @@ //! //! ## Examples //! -//! - [Timer MS and Second Tick Example](https://github.com/us-irs/va416xx-rs/blob/main/examples/simple/examples/timer-ticks.rs) -use core::cell::Cell; - -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> = Mutex::new(Cell::new(0)); +//! - [MS and second tick implementation](https://egit.irs.uni-stuttgart.de/rust/va416xx-rs/src/branch/main/examples/simple/examples/timer-ticks.rs) +//! - [Cascade feature example](https://egit.irs.uni-stuttgart.de/rust/va416xx-rs/src/branch/main/examples/simple/examples/cascade.rs) +pub use vorago_shared_periphs::timer::*; 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 { - 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, 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: 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 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 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: Pin, - tim: Tim, -} - -pub(super) struct TimRegister { - tim: TIM, -} - -impl TimRegister { - #[inline] - pub(super) unsafe fn new(tim: TIM) -> Self { - TimRegister { tim } - } - - pub(super) fn release(self) -> TIM { - self.tim - } -} - -unsafe impl TimRegInterface for TimRegister { - #[inline(always)] - fn tim_id(&self) -> u8 { - Tim::ID - } -} - -impl TimAndPinRegister -where - (Pin, Tim): ValidTimAndPin, -{ - #[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 TimRegInterface for TimAndPinRegister { - #[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 From> for TimDynRegister { - fn from(_reg: TimAndPinRegister) -> 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: TimRegister, - 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 TimRegInterface for CountdownTimer { - #[inline] - fn tim_id(&self) -> u8 { - Tim::ID - } -} - -impl CountdownTimer { - /// 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) { - 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) { - 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 embedded_hal::delay::DelayNs for CountdownTimer { - 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( - sys_cfg: &mut pac::Sysconfig, - tim: Tim, - clocks: &Clocks, -) -> CountdownTimer { - 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(CountdownTimer); - -impl DelayMs { - pub fn new(timer: CountdownTimer) -> Option { - 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(); - } - } -} diff --git a/va416xx-hal/src/typelevel.rs b/va416xx-hal/src/typelevel.rs deleted file mode 100644 index 7803c20..0000000 --- a/va416xx-hal/src/typelevel.rs +++ /dev/null @@ -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 -/// `::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 -/// ``` -/// -/// 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(mut any: T) -/// where -/// T: Is, -/// { -/// 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>, - Self: Into>, - Self: AsRef>, - Self: AsMut>, -{ - type Type; -} - -/// Type alias for [`Is::Type`] -pub type IsType = ::Type; - -impl Is for T -where - T: Sealed + AsRef + AsMut, -{ - type Type = T; -} - -//============================================================================== -// Counting -//============================================================================== - -/// Implement `Sealed` for [`U0`] -impl Sealed for U0 {} - -/// Implement `Sealed` for all type-level, [`Unsigned`] integers *except* [`U0`] -impl Sealed for UInt {} - -/// 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 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 Decrement for T {} - -impl PrivateIncrement for N -where - N: Unsigned + Add, - Add1: Unsigned, -{ - type Inc = Add1; - #[inline] - fn inc(self) -> Self::Inc { - Self::Inc::default() - } -} - -impl PrivateDecrement for N -where - N: Unsigned + Sub, - Sub1: Unsigned, -{ - type Dec = Sub1; - #[inline] - fn dec(self) -> Self::Dec { - Self::Dec::default() - } -} diff --git a/va416xx-hal/src/uart/mod.rs b/va416xx-hal/src/uart/mod.rs index e3d1ee6..e460508 100644 --- a/va416xx-hal/src/uart/mod.rs +++ b/va416xx-hal/src/uart/mod.rs @@ -1,1329 +1,17 @@ //! # API for the UART peripheral //! -//! The core of this API are the [Uart], [UartBase], [Rx] and [Tx] structures. +//! The core of this API are the [Uart], [Rx] and [Tx] structures. //! The RX structure also has a dedicated [RxWithInterrupt] variant which allows reading the receiver //! using interrupts. //! +//! The [rx_asynch] and [tx_asynch] modules provide an asynchronous non-blocking API for the UART +//! peripheral. +//! //! ## Examples //! //! - [UART simple example](https://egit.irs.uni-stuttgart.de/rust/va416xx-rs/src/branch/main/examples/simple/examples/uart.rs) -//! - [UART echo with IRQ and Embassy](https://egit.irs.uni-stuttgart.de/rust/va416xx-rs/src/branch/main/examples/embassy/src/bin/uart-echo-with-irq.rs) -//! - [Flashloader app using UART with IRQs](https://egit.irs.uni-stuttgart.de/rust/va416xx-rs/src/branch/main/flashloader) -use core::convert::Infallible; -use core::ops::Deref; - -use embedded_hal_nb::serial::Read; -use fugit::RateExtU32; - -use crate::clock::{Clocks, PeripheralSelect, SyscfgExt}; -use crate::gpio::PF13; -use crate::time::Hertz; -use crate::{disable_nvic_interrupt, enable_nvic_interrupt}; -use crate::{ - gpio::{ - AltFunc1, AltFunc2, AltFunc3, Pin, PA2, PA3, PB14, PB15, PC14, PC4, PC5, PD11, PD12, PE2, - PE3, PF12, PF9, PG0, PG1, - }, - pac::{self, uart0 as uart_base, Uart0, Uart1, Uart2}, -}; - -#[cfg(not(feature = "va41628"))] -use crate::gpio::{PC15, PF8}; - -#[derive(Debug, Clone, Copy)] -#[cfg_attr(feature = "defmt", derive(defmt::Format))] -pub enum Bank { - Uart0 = 0, - Uart1 = 1, - Uart2 = 2, -} - -//================================================================================================== -// Type-Level support -//================================================================================================== - -pub trait RxPin {} -pub trait TxPin {} - -impl TxPin for Pin {} -impl RxPin for Pin {} - -impl TxPin for Pin {} -impl RxPin for Pin {} - -impl TxPin for Pin {} -impl RxPin for Pin {} - -impl TxPin for Pin {} -impl RxPin for Pin {} - -impl TxPin for Pin {} -impl RxPin for Pin {} - -impl TxPin for Pin {} -impl RxPin for Pin {} - -impl TxPin for Pin {} -impl RxPin for Pin {} - -impl TxPin for Pin {} -#[cfg(not(feature = "va41628"))] -impl RxPin for Pin {} - -#[cfg(not(feature = "va41628"))] -impl TxPin for Pin {} -impl RxPin for Pin {} - -//================================================================================================== -// Regular Definitions -//================================================================================================== - -#[derive(Debug, PartialEq, Eq)] -#[cfg_attr(feature = "defmt", derive(defmt::Format))] -pub struct TransferPendingError; - -#[derive(Debug, PartialEq, Eq, Copy, Clone)] -#[cfg_attr(feature = "defmt", derive(defmt::Format))] -pub enum Event { - // Receiver FIFO interrupt enable. Generates interrupt - // when FIFO is at least half full. Half full is defined as FIFO - // count >= RXFIFOIRQTRG - RxFifoHalfFull, - // Framing error, Overrun error, Parity Error and Break error - RxError, - // Event for timeout condition: Data in the FIFO and no receiver - // FIFO activity for 4 character times - RxTimeout, - - // Transmitter FIFO interrupt enable. Generates interrupt - // when FIFO is at least half full. Half full is defined as FIFO - // count >= TXFIFOIRQTRG - TxFifoHalfFull, - // FIFO overflow error - TxError, - // Generate interrupt when transmit FIFO is empty and TXBUSY is 0 - TxEmpty, - // Interrupt when CTSn changes value - TxCts, -} - -#[derive(Debug, Copy, Clone, PartialEq, Eq)] -#[cfg_attr(feature = "defmt", derive(defmt::Format))] -pub enum Parity { - None, - Odd, - Even, -} - -#[derive(Debug, Copy, Clone, PartialEq, Eq)] -#[cfg_attr(feature = "defmt", derive(defmt::Format))] -pub enum StopBits { - One = 0, - Two = 1, -} - -#[derive(Debug, Copy, Clone, PartialEq, Eq)] -#[cfg_attr(feature = "defmt", derive(defmt::Format))] -pub enum WordSize { - Five = 0, - Six = 1, - Seven = 2, - Eight = 3, -} - -#[derive(Debug, Copy, Clone, PartialEq, Eq)] -#[cfg_attr(feature = "defmt", derive(defmt::Format))] -pub struct Config { - pub baudrate: Hertz, - pub parity: Parity, - pub stopbits: StopBits, - // When false, use standard 16x baud clock, other 8x baud clock - pub baud8: bool, - pub wordsize: WordSize, - pub enable_tx: bool, - pub enable_rx: bool, -} - -impl Config { - pub fn baudrate(mut self, baudrate: Hertz) -> Self { - self.baudrate = baudrate; - self - } - - pub fn parity_none(mut self) -> Self { - self.parity = Parity::None; - self - } - - pub fn parity_even(mut self) -> Self { - self.parity = Parity::Even; - self - } - - pub fn parity_odd(mut self) -> Self { - self.parity = Parity::Odd; - self - } - - pub fn stopbits(mut self, stopbits: StopBits) -> Self { - self.stopbits = stopbits; - self - } - - pub fn wordsize(mut self, wordsize: WordSize) -> Self { - self.wordsize = wordsize; - self - } - - pub fn baud8(mut self, baud: bool) -> Self { - self.baud8 = baud; - self - } -} - -impl Default for Config { - fn default() -> Config { - Config { - baudrate: 115200_u32.Hz(), - parity: Parity::None, - stopbits: StopBits::One, - baud8: false, - wordsize: WordSize::Eight, - enable_tx: true, - enable_rx: true, - } - } -} - -impl From for Config { - fn from(value: Hertz) -> Self { - Config::default().baudrate(value) - } -} - -//================================================================================================== -// IRQ Definitions -//================================================================================================== - -#[derive(Debug, Copy, Clone)] -#[cfg_attr(feature = "defmt", derive(defmt::Format))] -pub struct IrqContextTimeoutOrMaxSize { - rx_idx: usize, - mode: IrqReceptionMode, - pub max_len: usize, -} - -impl IrqContextTimeoutOrMaxSize { - pub fn new(max_len: usize) -> Self { - IrqContextTimeoutOrMaxSize { - rx_idx: 0, - max_len, - mode: IrqReceptionMode::Idle, - } - } -} - -impl IrqContextTimeoutOrMaxSize { - pub fn reset(&mut self) { - self.rx_idx = 0; - self.mode = IrqReceptionMode::Idle; - } -} - -/// This struct is used to return the default IRQ handler result to the user -#[derive(Debug, Default)] -#[cfg_attr(feature = "defmt", derive(defmt::Format))] -pub struct IrqResult { - pub bytes_read: usize, - pub errors: Option, -} - -/// This struct is used to return the default IRQ handler result to the user -#[derive(Debug, Default)] -#[cfg_attr(feature = "defmt", derive(defmt::Format))] -pub struct IrqResultMaxSizeOrTimeout { - complete: bool, - timeout: bool, - pub errors: Option, - pub bytes_read: usize, -} - -impl IrqResultMaxSizeOrTimeout { - pub fn new() -> Self { - IrqResultMaxSizeOrTimeout { - complete: false, - timeout: false, - errors: None, - bytes_read: 0, - } - } -} -impl IrqResultMaxSizeOrTimeout { - #[inline] - pub fn has_errors(&self) -> bool { - self.errors.is_some() - } - - #[inline] - pub fn overflow_error(&self) -> bool { - self.errors.is_some_and(|e| e.overflow) - } - - #[inline] - pub fn framing_error(&self) -> bool { - self.errors.is_some_and(|e| e.framing) - } - - #[inline] - pub fn parity_error(&self) -> bool { - self.errors.is_some_and(|e| e.parity) - } - - #[inline] - pub fn timeout(&self) -> bool { - self.timeout - } - - #[inline] - pub fn complete(&self) -> bool { - self.complete - } -} - -#[derive(Debug, PartialEq, Copy, Clone)] -#[cfg_attr(feature = "defmt", derive(defmt::Format))] -enum IrqReceptionMode { - Idle, - Pending, -} - -#[derive(Default, Debug, Copy, Clone)] -#[cfg_attr(feature = "defmt", derive(defmt::Format))] -pub struct UartErrors { - overflow: bool, - framing: bool, - parity: bool, - other: bool, -} - -impl UartErrors { - #[inline(always)] - pub fn overflow(&self) -> bool { - self.overflow - } - - #[inline(always)] - pub fn framing(&self) -> bool { - self.framing - } - - #[inline(always)] - pub fn parity(&self) -> bool { - self.parity - } - - #[inline(always)] - pub fn other(&self) -> bool { - self.other - } -} - -impl UartErrors { - #[inline(always)] - pub fn error(&self) -> bool { - self.overflow || self.framing || self.parity - } -} - -#[derive(Debug, PartialEq, Eq)] -#[cfg_attr(feature = "defmt", derive(defmt::Format))] -pub struct BufferTooShortError { - found: usize, - expected: usize, -} - -//================================================================================================== -// UART peripheral wrapper -//================================================================================================== - -pub trait Instance: Deref { - const IDX: u8; - const PERIPH_SEL: PeripheralSelect; - const PTR: *const uart_base::RegisterBlock; - const IRQ_RX: pac::Interrupt; - const IRQ_TX: pac::Interrupt; - - /// Retrieve the peripheral structure. - /// - /// # Safety - /// - /// This circumvents the safety guarantees of the HAL. - unsafe fn steal() -> Self; - - #[inline(always)] - fn ptr() -> *const uart_base::RegisterBlock { - Self::PTR - } - - /// Retrieve the type erased peripheral register block. - /// - /// # Safety - /// - /// This circumvents the safety guarantees of the HAL. - #[inline(always)] - unsafe fn reg_block() -> &'static uart_base::RegisterBlock { - unsafe { &(*Self::ptr()) } - } -} - -impl Instance for Uart0 { - const IDX: u8 = 0; - const PERIPH_SEL: PeripheralSelect = PeripheralSelect::Uart0; - const IRQ_RX: pac::Interrupt = pac::Interrupt::UART0_RX; - const IRQ_TX: pac::Interrupt = pac::Interrupt::UART0_TX; - const PTR: *const uart_base::RegisterBlock = Self::PTR; - - unsafe fn steal() -> Self { - Self::steal() - } - fn ptr() -> *const uart_base::RegisterBlock { - Self::ptr() as *const _ - } -} - -impl Instance for Uart1 { - const IDX: u8 = 1; - const PERIPH_SEL: PeripheralSelect = PeripheralSelect::Uart1; - const IRQ_RX: pac::Interrupt = pac::Interrupt::UART1_RX; - const IRQ_TX: pac::Interrupt = pac::Interrupt::UART1_TX; - const PTR: *const uart_base::RegisterBlock = Self::PTR; - - unsafe fn steal() -> Self { - Self::steal() - } - fn ptr() -> *const uart_base::RegisterBlock { - Self::ptr() as *const _ - } -} - -impl Instance for Uart2 { - const IDX: u8 = 2; - const PERIPH_SEL: PeripheralSelect = PeripheralSelect::Uart2; - const IRQ_RX: pac::Interrupt = pac::Interrupt::UART2_RX; - const IRQ_TX: pac::Interrupt = pac::Interrupt::UART2_TX; - const PTR: *const uart_base::RegisterBlock = Self::PTR; - - unsafe fn steal() -> Self { - Self::steal() - } - fn ptr() -> *const uart_base::RegisterBlock { - Self::ptr() as *const _ - } -} - -impl Bank { - /// Retrieve the peripheral register block. - /// - /// # Safety - /// - /// Circumvents the HAL safety guarantees. - pub unsafe fn reg_block(&self) -> &'static uart_base::RegisterBlock { - match self { - Bank::Uart0 => unsafe { pac::Uart0::reg_block() }, - Bank::Uart1 => unsafe { pac::Uart1::reg_block() }, - Bank::Uart2 => unsafe { pac::Uart2::reg_block() }, - } - } -} - -//================================================================================================== -// UART implementation -//================================================================================================== - -/// Type erased variant of a UART. Can be created with the [Uart::downgrade] function. -pub struct UartBase { - uart: Uart, - tx: Tx, - rx: Rx, -} - -impl UartBase { - fn init(self, config: Config, clocks: &Clocks) -> Self { - if Uart::IDX == 2 { - self.init_with_clock_freq(config, clocks.apb1()) - } else { - self.init_with_clock_freq(config, clocks.apb2()) - } - } - - /// This function assumes that the peripheral clock was alredy enabled - /// in the SYSCONFIG register - fn init_with_clock_freq(self, config: Config, apb_clk: Hertz) -> Self { - let baud_multiplier = match config.baud8 { - false => 16, - true => 8, - }; - // This is the calculation: (64.0 * (x - integer_part as f32) + 0.5) as u32 without floating - // point calculations. - let frac = ((apb_clk.raw() % (config.baudrate.raw() * 16)) * 64 - + (config.baudrate.raw() * 8)) - / (config.baudrate.raw() * 16); - // Calculations here are derived from chapter 10.4.4 (p.74) of the datasheet. - let x = apb_clk.raw() as f32 / (config.baudrate.raw() * baud_multiplier) as f32; - let integer_part = x as u32; - self.uart.clkscale().write(|w| unsafe { - w.frac().bits(frac as u8); - w.int().bits(integer_part) - }); - - let (paren, pareven) = match config.parity { - Parity::None => (false, false), - Parity::Odd => (true, false), - Parity::Even => (true, true), - }; - let stopbits = match config.stopbits { - StopBits::One => false, - StopBits::Two => true, - }; - let wordsize = config.wordsize as u8; - let baud8 = config.baud8; - self.uart.ctrl().write(|w| { - w.paren().bit(paren); - w.pareven().bit(pareven); - w.stopbits().bit(stopbits); - w.baud8().bit(baud8); - unsafe { w.wordsize().bits(wordsize) } - }); - let (txenb, rxenb) = (config.enable_tx, config.enable_rx); - // Clear the FIFO - self.uart.fifo_clr().write(|w| { - w.rxfifo().set_bit(); - w.txfifo().set_bit() - }); - self.uart.enable().write(|w| { - w.rxenable().bit(rxenb); - w.txenable().bit(txenb) - }); - self - } - - #[inline] - pub fn enable_rx(&mut self) { - self.uart.enable().modify(|_, w| w.rxenable().set_bit()); - } - - #[inline] - pub fn disable_rx(&mut self) { - self.uart.enable().modify(|_, w| w.rxenable().clear_bit()); - } - - #[inline] - pub fn enable_tx(&mut self) { - self.uart.enable().modify(|_, w| w.txenable().set_bit()); - } - - #[inline] - pub fn disable_tx(&mut self) { - self.uart.enable().modify(|_, w| w.txenable().clear_bit()); - } - - #[inline] - pub fn clear_rx_fifo(&mut self) { - self.uart.fifo_clr().write(|w| w.rxfifo().set_bit()); - } - - #[inline] - pub fn clear_tx_fifo(&mut self) { - self.uart.fifo_clr().write(|w| w.txfifo().set_bit()); - } - - pub fn listen(&self, event: Event) { - self.uart.irq_enb().modify(|_, w| match event { - Event::RxError => w.irq_rx_status().set_bit(), - Event::RxFifoHalfFull => w.irq_rx().set_bit(), - Event::RxTimeout => w.irq_rx_to().set_bit(), - Event::TxEmpty => w.irq_tx_empty().set_bit(), - Event::TxError => w.irq_tx_status().set_bit(), - Event::TxFifoHalfFull => w.irq_tx().set_bit(), - Event::TxCts => w.irq_tx_cts().set_bit(), - }); - } - - pub fn unlisten(&self, event: Event) { - self.uart.irq_enb().modify(|_, w| match event { - Event::RxError => w.irq_rx_status().clear_bit(), - Event::RxFifoHalfFull => w.irq_rx().clear_bit(), - Event::RxTimeout => w.irq_rx_to().clear_bit(), - Event::TxEmpty => w.irq_tx_empty().clear_bit(), - Event::TxError => w.irq_tx_status().clear_bit(), - Event::TxFifoHalfFull => w.irq_tx().clear_bit(), - Event::TxCts => w.irq_tx_cts().clear_bit(), - }); - } - - pub fn release(self) -> Uart { - // Clear the FIFO - self.uart.fifo_clr().write(|w| { - w.rxfifo().set_bit(); - w.txfifo().set_bit() - }); - self.uart.enable().write(|w| { - w.rxenable().clear_bit(); - w.txenable().clear_bit() - }); - disable_nvic_interrupt(Uart::IRQ_RX); - disable_nvic_interrupt(Uart::IRQ_TX); - self.uart - } - - /// Poll receiver errors. - pub fn poll_rx_errors(&self) -> Option { - self.rx.poll_errors() - } - - pub fn split(self) -> (Tx, Rx) { - (self.tx, self.rx) - } -} - -impl embedded_io::ErrorType for UartBase { - type Error = Infallible; -} - -impl embedded_hal_nb::serial::ErrorType for UartBase { - type Error = Infallible; -} - -impl embedded_hal_nb::serial::Read for UartBase { - fn read(&mut self) -> nb::Result { - self.rx.read() - } -} - -impl embedded_hal_nb::serial::Write for UartBase { - fn write(&mut self, word: u8) -> nb::Result<(), Self::Error> { - self.tx.write(word).map_err(|e| { - if let nb::Error::Other(_) = e { - unreachable!() - } - nb::Error::WouldBlock - }) - } - - fn flush(&mut self) -> nb::Result<(), Self::Error> { - self.tx.flush().map_err(|e| { - if let nb::Error::Other(_) = e { - unreachable!() - } - nb::Error::WouldBlock - }) - } -} - -/// Serial abstraction. Entry point to create a new UART -pub struct Uart { - inner: UartBase, - pins: Pins, -} - -impl, RxPinInst: RxPin, UartInstance: Instance> - Uart -{ - pub fn new( - syscfg: &mut va416xx::Sysconfig, - uart: UartInstance, - pins: (TxPinInst, RxPinInst), - config: impl Into, - clocks: &Clocks, - ) -> Self { - crate::clock::enable_peripheral_clock(syscfg, UartInstance::PERIPH_SEL); - // This is done in the C HAL. - syscfg.assert_periph_reset_for_two_cycles(UartInstance::PERIPH_SEL); - Uart { - inner: UartBase { - uart, - tx: Tx::new(unsafe { UartInstance::steal() }), - rx: Rx::new(unsafe { UartInstance::steal() }), - }, - pins, - } - .init(config.into(), clocks) - } - - pub fn new_with_clock_freq( - syscfg: &mut va416xx::Sysconfig, - uart: UartInstance, - pins: (TxPinInst, RxPinInst), - config: impl Into, - clock: impl Into, - ) -> Self { - crate::clock::enable_peripheral_clock(syscfg, UartInstance::PERIPH_SEL); - Uart { - inner: UartBase { - uart, - tx: Tx::new(unsafe { UartInstance::steal() }), - rx: Rx::new(unsafe { UartInstance::steal() }), - }, - pins, - } - .init_with_clock_freq(config.into(), clock.into()) - } - - fn init(mut self, config: Config, clocks: &Clocks) -> Self { - self.inner = self.inner.init(config, clocks); - self - } - - /// This function assumes that the peripheral clock was already enabled - /// in the SYSCONFIG register - #[allow(dead_code)] - fn init_with_clock_freq(mut self, config: Config, sys_clk: Hertz) -> Self { - self.inner = self.inner.init_with_clock_freq(config, sys_clk); - self - } - - delegate::delegate! { - to self.inner { - /// Poll receiver errors. - pub fn poll_rx_errors(&self) -> Option; - #[inline] - pub fn enable_rx(&mut self); - #[inline] - pub fn disable_rx(&mut self); - - #[inline] - pub fn enable_tx(&mut self); - #[inline] - pub fn disable_tx(&mut self); - - #[inline] - pub fn clear_rx_fifo(&mut self); - #[inline] - pub fn clear_tx_fifo(&mut self); - - #[inline] - pub fn listen(&self, event: Event); - #[inline] - pub fn unlisten(&self, event: Event); - #[inline] - pub fn split(self) -> (Tx, Rx); - } - } - - pub fn downgrade(self) -> UartBase { - UartBase { - uart: self.inner.uart, - tx: self.inner.tx, - rx: self.inner.rx, - } - } - - pub fn release(self) -> (UartInstance, (TxPinInst, RxPinInst)) { - (self.inner.release(), self.pins) - } -} - -#[inline(always)] -pub fn enable_rx(uart: &uart_base::RegisterBlock) { - uart.enable().modify(|_, w| w.rxenable().set_bit()); -} - -#[inline(always)] -pub fn disable_rx(uart: &uart_base::RegisterBlock) { - uart.enable().modify(|_, w| w.rxenable().clear_bit()); -} - -#[inline(always)] -pub fn enable_rx_interrupts(uart: &uart_base::RegisterBlock) { - uart.irq_enb().modify(|_, w| { - w.irq_rx().set_bit(); - w.irq_rx_to().set_bit(); - w.irq_rx_status().set_bit() - }); -} - -#[inline(always)] -pub fn disable_rx_interrupts(uart: &uart_base::RegisterBlock) { - uart.irq_enb().modify(|_, w| { - w.irq_rx().clear_bit(); - w.irq_rx_to().clear_bit(); - w.irq_rx_status().clear_bit() - }); -} - -/// Serial receiver. -/// -/// Can be created by using the [Uart::split] or [UartBase::split] API. -pub struct Rx(Uart); - -impl Rx { - fn new(uart: Uart) -> Self { - Self(uart) - } - - /// Direct access to the peripheral structure. - /// - /// # Safety - /// - /// You must ensure that only registers related to the operation of the RX side are used. - pub unsafe fn uart(&self) -> &Uart { - &self.0 - } - - pub fn poll_errors(&self) -> Option { - let mut errors = UartErrors::default(); - - let uart = unsafe { &(*Uart::ptr()) }; - let status_reader = uart.rxstatus().read(); - if status_reader.rxovr().bit_is_set() { - errors.overflow = true; - } else if status_reader.rxfrm().bit_is_set() { - errors.framing = true; - } else if status_reader.rxpar().bit_is_set() { - errors.parity = true; - } else { - return None; - }; - Some(errors) - } - - #[inline] - pub fn clear_fifo(&self) { - self.0.fifo_clr().write(|w| w.rxfifo().set_bit()); - } - - #[inline] - pub fn enable(&mut self) { - self.0.enable().modify(|_, w| w.rxenable().set_bit()); - } - - #[inline] - pub fn disable(&mut self) { - self.0.enable().modify(|_, w| w.rxenable().clear_bit()); - } - - #[inline] - pub fn disable_interrupts(&mut self) { - disable_rx_interrupts(unsafe { Uart::reg_block() }); - } - #[inline] - pub fn enable_interrupts(&mut self) { - enable_rx_interrupts(unsafe { Uart::reg_block() }); - } - - /// Low level function to read a word from the UART FIFO. - /// - /// Uses the [nb] API to allow usage in blocking and non-blocking contexts. - /// - /// Please note that you might have to mask the returned value with 0xff to retrieve the actual - /// value if you use the manual parity mode. See chapter 11.4.1 for more information. - #[inline(always)] - pub fn read_fifo(&self) -> nb::Result { - if self.0.rxstatus().read().rdavl().bit_is_clear() { - return Err(nb::Error::WouldBlock); - } - Ok(self.read_fifo_unchecked()) - } - - /// Low level function to read a word from from the UART FIFO. - /// - /// This does not necesarily mean there is a word in the FIFO available. - /// Use the [Self::read_fifo] function to read a word from the FIFO reliably using the [nb] - /// API. - /// - /// Please note that you might have to mask the returned value with 0xff to retrieve the actual - /// value if you use the manual parity mode. See chapter 11.4.1 for more information. - #[inline(always)] - pub fn read_fifo_unchecked(&self) -> u32 { - self.0.data().read().bits() - } - - pub fn into_rx_with_irq(self) -> RxWithInterrupt { - RxWithInterrupt(self) - } - - pub fn release(self) -> Uart { - self.0 - } -} - -impl embedded_io::ErrorType for Rx { - type Error = Infallible; -} - -impl embedded_hal_nb::serial::ErrorType for Rx { - type Error = Infallible; -} - -impl embedded_hal_nb::serial::Read for Rx { - fn read(&mut self) -> nb::Result { - self.read_fifo().map(|val| (val & 0xff) as u8).map_err(|e| { - if let nb::Error::Other(_) = e { - unreachable!() - } - nb::Error::WouldBlock - }) - } -} - -impl embedded_io::Read for Rx { - fn read(&mut self, buf: &mut [u8]) -> Result { - if buf.is_empty() { - return Ok(0); - } - let mut read = 0; - loop { - if self.0.rxstatus().read().rdavl().bit_is_set() { - break; - } - } - for byte in buf.iter_mut() { - match >::read(self) { - Ok(w) => { - *byte = w; - read += 1; - } - Err(nb::Error::WouldBlock) => break, - } - } - - Ok(read) - } -} - -#[inline(always)] -pub fn enable_tx(uart: &uart_base::RegisterBlock) { - uart.enable().modify(|_, w| w.txenable().set_bit()); -} - -#[inline(always)] -pub fn disable_tx(uart: &uart_base::RegisterBlock) { - uart.enable().modify(|_, w| w.txenable().clear_bit()); -} - -#[inline(always)] -pub fn enable_tx_interrupts(uart: &uart_base::RegisterBlock) { - uart.irq_enb().modify(|_, w| { - w.irq_tx().set_bit(); - w.irq_tx_status().set_bit(); - w.irq_tx_empty().set_bit() - }); -} - -#[inline(always)] -pub fn disable_tx_interrupts(uart: &uart_base::RegisterBlock) { - uart.irq_enb().modify(|_, w| { - w.irq_tx().clear_bit(); - w.irq_tx_status().clear_bit(); - w.irq_tx_empty().clear_bit() - }); -} - -/// Serial transmitter -/// -/// Can be created by using the [Uart::split] or [UartBase::split] API. -pub struct Tx(Uart); - -impl Tx { - /// Retrieve a TX pin without expecting an explicit UART structure - /// - /// # Safety - /// - /// Circumvents the HAL safety guarantees. - #[inline(always)] - pub unsafe fn steal() -> Self { - Self(Uart::steal()) - } - - #[inline(always)] - fn new(uart: Uart) -> Self { - Self(uart) - } - - /// Direct access to the peripheral structure. - /// - /// # Safety - /// - /// You must ensure that only registers related to the operation of the TX side are used. - #[inline(always)] - pub const unsafe fn uart(&self) -> &Uart { - &self.0 - } - - #[inline] - pub fn clear_fifo(&self) { - self.0.fifo_clr().write(|w| w.txfifo().set_bit()); - } - - #[inline] - pub fn enable(&mut self) { - self.0.enable().modify(|_, w| w.txenable().set_bit()); - } - - #[inline] - pub fn disable(&mut self) { - self.0.enable().modify(|_, w| w.txenable().clear_bit()); - } - - /// Enables the IRQ_TX, IRQ_TX_STATUS and IRQ_TX_EMPTY interrupts. - /// - /// - The IRQ_TX interrupt is generated when the TX FIFO is at least half empty. - /// - The IRQ_TX_STATUS interrupt is generated when write data is lost due to a FIFO overflow - /// - The IRQ_TX_EMPTY interrupt is generated when the TX FIFO is empty and the TXBUSY signal - /// is 0 - #[inline] - pub fn enable_interrupts(&self) { - // Safety: We own the UART structure - enable_tx_interrupts(unsafe { Uart::reg_block() }); - } - - /// Disables the IRQ_TX, IRQ_TX_STATUS and IRQ_TX_EMPTY interrupts. - /// - /// [Self::enable_interrupts] documents the interrupts. - #[inline] - pub fn disable_interrupts(&self) { - // Safety: We own the UART structure - disable_tx_interrupts(unsafe { Uart::reg_block() }); - } - - /// Low level function to write a word to the UART FIFO. - /// - /// Uses the [nb] API to allow usage in blocking and non-blocking contexts. - /// - /// Please note that you might have to mask the returned value with 0xff to retrieve the actual - /// value if you use the manual parity mode. See chapter 11.4.1 for more information. - #[inline(always)] - pub fn write_fifo(&self, data: u32) -> nb::Result<(), Infallible> { - if self.0.txstatus().read().wrrdy().bit_is_clear() { - return Err(nb::Error::WouldBlock); - } - self.write_fifo_unchecked(data); - Ok(()) - } - - /// Low level function to write a word to the UART FIFO. - /// - /// This does not necesarily mean that the FIFO can process another word because it might be - /// full. - /// Use the [Self::write_fifo] function to write a word to the FIFO reliably using the [nb] - /// API. - #[inline(always)] - pub fn write_fifo_unchecked(&self, data: u32) { - self.0.data().write(|w| unsafe { w.bits(data) }); - } - - #[inline] - pub fn into_async(self) -> TxAsync { - TxAsync::new(self) - } -} - -impl embedded_io::ErrorType for Tx { - type Error = Infallible; -} - -impl embedded_hal_nb::serial::ErrorType for Tx { - type Error = Infallible; -} - -impl embedded_hal_nb::serial::Write for Tx { - fn write(&mut self, word: u8) -> nb::Result<(), Self::Error> { - self.write_fifo(word as u32) - } - - fn flush(&mut self) -> nb::Result<(), Self::Error> { - // SAFETY: Only TX related registers are used. - let reader = unsafe { &(*Uart::ptr()) }.txstatus().read(); - if reader.wrbusy().bit_is_set() { - return Err(nb::Error::WouldBlock); - } - Ok(()) - } -} - -impl embedded_io::Write for Tx { - fn write(&mut self, buf: &[u8]) -> Result { - if buf.is_empty() { - return Ok(0); - } - loop { - if self.0.txstatus().read().wrrdy().bit_is_set() { - break; - } - } - let mut written = 0; - for byte in buf.iter() { - match >::write(self, *byte) { - Ok(_) => written += 1, - Err(nb::Error::WouldBlock) => return Ok(written), - } - } - Ok(written) - } - - fn flush(&mut self) -> Result<(), Self::Error> { - nb::block!(>::flush(self)) - } -} - -/// Serial receiver, using interrupts to offload reading to the hardware. -/// -/// You can use [Rx::into_rx_with_irq] to convert a normal [Rx] structure into this structure. -/// This structure provides two distinct ways to read the UART RX using interrupts. It should -/// be noted that the interrupt service routine (ISR) still has to be provided by the user. However, -/// this structure provides API calls which can be used inside the ISRs to simplify the reading -/// of the UART. -/// -/// 1. The first way simply empties the FIFO on an interrupt into a user provided buffer. You -/// can simply use [Self::start] to prepare the peripheral and then call the -/// [Self::irq_handler] in the interrupt service routine. -/// 2. The second way reads packets bounded by a maximum size or a baudtick based timeout. You -/// can use [Self::read_fixed_len_or_timeout_based_using_irq] to prepare the peripheral and -/// then call the [Self::irq_handler_max_size_or_timeout_based] in the interrupt service -/// routine. You have to call [Self::read_fixed_len_or_timeout_based_using_irq] in the ISR to -/// start reading the next packet. -pub struct RxWithInterrupt(Rx); - -impl RxWithInterrupt { - /// This function should be called once at initialization time if the regular - /// [Self::irq_handler] is used to read the UART receiver to enable and start the receiver. - pub fn start(&mut self) { - self.0.enable(); - self.enable_rx_irq_sources(true); - unsafe { enable_nvic_interrupt(Uart::IRQ_RX) }; - } - - #[inline(always)] - pub fn uart(&self) -> &Uart { - &self.0 .0 - } - - /// This function is used together with the [Self::irq_handler_max_size_or_timeout_based] - /// function to read packets with a maximum size or variable sized packets by using the - /// receive timeout of the hardware. - /// - /// This function should be called once at initialization to initiate the context state - /// and to [Self::start] the receiver. After that, it should be called after each - /// completed [Self::irq_handler_max_size_or_timeout_based] call to restart the reception - /// of a packet. - pub fn read_fixed_len_or_timeout_based_using_irq( - &mut self, - context: &mut IrqContextTimeoutOrMaxSize, - ) -> Result<(), TransferPendingError> { - if context.mode != IrqReceptionMode::Idle { - return Err(TransferPendingError); - } - context.mode = IrqReceptionMode::Pending; - context.rx_idx = 0; - self.start(); - Ok(()) - } - - #[inline] - fn enable_rx_irq_sources(&mut self, timeout: bool) { - self.uart().irq_enb().modify(|_, w| { - if timeout { - w.irq_rx_to().set_bit(); - } - w.irq_rx_status().set_bit(); - w.irq_rx().set_bit() - }); - } - - #[inline] - fn disable_rx_irq_sources(&mut self) { - self.uart().irq_enb().modify(|_, w| { - w.irq_rx_to().clear_bit(); - w.irq_rx_status().clear_bit(); - w.irq_rx().clear_bit() - }); - } - - pub fn cancel_transfer(&mut self) { - self.disable_rx_irq_sources(); - self.0.clear_fifo(); - } - - /// This function should be called in the user provided UART interrupt handler. - /// - /// It simply empties any bytes in the FIFO into the user provided buffer and returns the - /// result of the operation. - /// - /// This function will not disable the RX interrupts, so you don't need to call any other - /// API after calling this function to continue emptying the FIFO. RX errors are handled - /// as partial errors and are returned as part of the [IrqResult]. - pub fn on_interrupt(&mut self, buf: &mut [u8; 16]) -> IrqResult { - let mut result = IrqResult::default(); - - let irq_end = self.uart().irq_end().read(); - let enb_status = self.uart().enable().read(); - let rx_enabled = enb_status.rxenable().bit_is_set(); - - // Half-Full interrupt. We have a guaranteed amount of data we can read. - if irq_end.irq_rx().bit_is_set() { - let available_bytes = self.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 { - buf[result.bytes_read] = (self.uart().data().read().bits() & 0xff) as u8; - result.bytes_read += 1; - } - } - - // Timeout, empty the FIFO completely. - if irq_end.irq_rx_to().bit_is_set() { - // While there is data in the FIFO, write it into the reception buffer - while let Ok(byte) = self.0.read_fifo() { - buf[result.bytes_read] = byte as u8; - result.bytes_read += 1; - } - } - - // RX transfer not complete, check for RX errors - if rx_enabled { - self.check_for_errors(&mut result.errors); - } - - // Clear the interrupt status bits - self.uart() - .irq_clr() - .write(|w| unsafe { w.bits(irq_end.bits()) }); - result - } - - /// This function should be called in the user provided UART interrupt handler. - /// - /// This function is used to read packets which either have a maximum size or variable sized - /// packet which are bounded by sufficient delays between them, triggering a hardware timeout. - /// - /// If either the maximum number of packets have been read or a timeout occured, the transfer - /// will be deemed completed. The state information of the transfer is tracked in the - /// [IrqContextTimeoutOrMaxSize] structure. - /// - /// If passed buffer is equal to or larger than the specified maximum length, an - /// [BufferTooShortError] will be returned. Other RX errors are treated as partial errors - /// and returned inside the [IrqResultMaxSizeOrTimeout] structure. - pub fn on_interrupt_max_size_or_timeout_based( - &mut self, - context: &mut IrqContextTimeoutOrMaxSize, - buf: &mut [u8], - ) -> Result { - if buf.len() < context.max_len { - return Err(BufferTooShortError { - found: buf.len(), - expected: context.max_len, - }); - } - let mut result = IrqResultMaxSizeOrTimeout::default(); - - let irq_end = self.uart().irq_end().read(); - let enb_status = self.uart().enable().read(); - let rx_enabled = enb_status.rxenable().bit_is_set(); - - // Half-Full interrupt. We have a guaranteed amount of data we can read. - if irq_end.irq_rx().bit_is_set() { - // Determine the number of bytes to read, ensuring we leave 1 byte in the FIFO. - // We use this trick/hack because the timeout feature of the peripheral relies on data - // being in the RX FIFO. If data continues arriving, another half-full IRQ will fire. - // If not, the last byte(s) is/are emptied by the timeout interrupt. - let available_bytes = self.uart().rxfifoirqtrg().read().bits() as usize; - - let bytes_to_read = core::cmp::min( - available_bytes.saturating_sub(1), - context.max_len - context.rx_idx, - ); - - // If this interrupt bit is set, the trigger level is available at the very least. - // Read everything as fast as possible - for _ in 0..bytes_to_read { - buf[context.rx_idx] = (self.uart().data().read().bits() & 0xff) as u8; - context.rx_idx += 1; - } - - // On high-baudrates, data might be available immediately, and we possible have to - // read continuosly? Then again, the CPU should always be faster than that. I'd rather - // rely on the hardware firing another IRQ. I have not tried baudrates higher than - // 115200 so far. - } - // Timeout, empty the FIFO completely. - if irq_end.irq_rx_to().bit_is_set() { - // While there is data in the FIFO, write it into the reception buffer - loop { - if context.rx_idx == context.max_len { - break; - } - // While there is data in the FIFO, write it into the reception buffer - match self.0.read() { - Ok(byte) => { - buf[result.bytes_read] = byte; - result.bytes_read += 1; - } - Err(_) => break, - } - } - self.irq_completion_handler_max_size_timeout(&mut result, context); - return Ok(result); - } - - // RX transfer not complete, check for RX errors - if (context.rx_idx < context.max_len) && rx_enabled { - self.check_for_errors(&mut result.errors); - } - - // Clear the interrupt status bits - self.uart() - .irq_clr() - .write(|w| unsafe { w.bits(irq_end.bits()) }); - Ok(result) - } - - fn check_for_errors(&self, errors: &mut Option) { - let rx_status = self.uart().rxstatus().read(); - - if rx_status.rxovr().bit_is_set() - || rx_status.rxfrm().bit_is_set() - || rx_status.rxpar().bit_is_set() - { - let err = errors.get_or_insert(UartErrors::default()); - - if rx_status.rxovr().bit_is_set() { - err.overflow = true; - } - if rx_status.rxfrm().bit_is_set() { - err.framing = true; - } - if rx_status.rxpar().bit_is_set() { - err.parity = true; - } - } - } - - fn irq_completion_handler_max_size_timeout( - &mut self, - res: &mut IrqResultMaxSizeOrTimeout, - context: &mut IrqContextTimeoutOrMaxSize, - ) { - self.disable_rx_irq_sources(); - self.0.disable(); - res.bytes_read = context.rx_idx; - res.complete = true; - context.mode = IrqReceptionMode::Idle; - context.rx_idx = 0; - } - - /// # Safety - /// - /// This API allows creating multiple UART instances when releasing the TX structure as well. - /// The user must ensure that these instances are not used to create multiple overlapping - /// UART drivers. - pub unsafe fn release(self) -> Uart { - self.0.release() - } -} - -pub mod tx_asynch; -pub use tx_asynch::*; - -pub mod rx_asynch; -pub use rx_asynch::*; +//! - [UART with IRQ and RTIC](https://egit.irs.uni-stuttgart.de/rust/va416xx-rs/src/branch/main/examples/rtic/src/bin/uart-echo-rtic.rs) +//! - [Flashloader exposing a CCSDS interface via UART](https://egit.irs.uni-stuttgart.de/rust/va416xx-rs/src/branch/main/flashloader) +//! - [Async UART RX example](https://egit.irs.uni-stuttgart.de/rust/va416xx-rs/src/branch/main/examples/embassy/src/bin/async-uart-rx.rs) +//! - [Async UART TX example](https://egit.irs.uni-stuttgart.de/rust/va416xx-rs/src/branch/main/examples/embassy/src/bin/async-uart-tx.rs) +pub use vorago_shared_periphs::uart::*; diff --git a/va416xx-hal/src/uart/rx_asynch.rs b/va416xx-hal/src/uart/rx_asynch.rs deleted file mode 100644 index 5e409b5..0000000 --- a/va416xx-hal/src/uart/rx_asynch.rs +++ /dev/null @@ -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(_rx: &mut Rx) -> 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 { - 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 { - 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 { - 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( - bank: Bank, - prod: &mut heapless::spsc::Producer, - shared_consumer: &Mutex>>>, -) -> Result<(), AsyncUartErrors> { - on_interrupt_rx_async_heapless_queue_overwriting(bank, prod, shared_consumer) -} - -pub fn on_interrupt_rx_async_heapless_queue_overwriting( - bank: Bank, - prod: &mut heapless::spsc::Producer, - shared_consumer: &Mutex>>>, -) -> 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( - 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( - 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 { - rx: Rx, - 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(Option>); - -impl ErrorType for RxAsync { - /// Error reporting is done using the result of the interrupt functions. - type Error = Infallible; -} - -fn stop_async_rx(rx: &mut Rx) { - rx.disable_interrupts(); - rx.disable(); - unsafe { - enable_nvic_interrupt(Uart::IRQ_RX); - } - rx.clear_fifo(); -} - -impl RxAsync { - /// 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, 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, heapless::spsc::Consumer<'static, u8, N>) { - self.stop(); - let inner = self.0.take().unwrap(); - (inner.rx, inner.queue) - } -} - -impl Drop for RxAsync { - fn drop(&mut self) { - self.stop(); - } -} - -impl embedded_io_async::Read for RxAsync { - async fn read(&mut self, buf: &mut [u8]) -> Result { - // 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 { - rx: Rx, - pub shared_consumer: &'static Mutex>>>, -} - -/// 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( - Option>, -); - -impl ErrorType for RxAsyncOverwriting { - /// Error reporting is done using the result of the interrupt functions. - type Error = Infallible; -} - -impl RxAsyncOverwriting { - /// 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, - shared_consumer: &'static Mutex>>>, - ) -> 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 { - self.stop(); - let inner = self.0.take().unwrap(); - inner.rx - } -} - -impl Drop for RxAsyncOverwriting { - fn drop(&mut self) { - self.stop(); - } -} - -impl embedded_io_async::Read for RxAsyncOverwriting { - async fn read(&mut self, buf: &mut [u8]) -> Result { - // 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| { - 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) - } -} diff --git a/va416xx-hal/src/uart/tx_asynch.rs b/va416xx-hal/src/uart/tx_asynch.rs deleted file mode 100644 index a1adb8d..0000000 --- a/va416xx-hal/src/uart/tx_asynch.rs +++ /dev/null @@ -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>; 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(tx: &mut Tx, 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; - - fn poll( - self: core::pin::Pin<&mut Self>, - cx: &mut core::task::Context<'_>, - ) -> core::task::Poll { - 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 { - tx: Tx, -} - -impl TxAsync { - /// 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) -> 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 { - 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 embedded_io::ErrorType for TxAsync { - type Error = TxOverrunError; -} - -impl Write for TxAsync { - /// 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 { - let fut = unsafe { TxFuture::new(&mut self.tx, buf) }; - fut.await - } -} diff --git a/va416xx-hal/src/wdt.rs b/va416xx-hal/src/wdt.rs index 24b9cf4..ec7f6da 100644 --- a/va416xx-hal/src/wdt.rs +++ b/va416xx-hal/src/wdt.rs @@ -3,12 +3,12 @@ //! ## Examples //! //! - [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 crate::{ - clock::{Clocks, PeripheralSelect}, - pac, - prelude::SyscfgExt, +use vorago_shared_periphs::{ + enable_peripheral_clock, reset_peripheral_for_cycles, PeripheralSelect, }; + +use crate::time::Hertz; +use crate::{clock::Clocks, pac}; use crate::{disable_nvic_interrupt, enable_nvic_interrupt}; pub const WDT_UNLOCK_VALUE: u32 = 0x1ACC_E551; @@ -39,23 +39,13 @@ pub fn disable_wdt_interrupts() { } impl Wdt { - pub fn new( - syscfg: &mut pac::Sysconfig, - wdt: pac::WatchDog, - clocks: &Clocks, - wdt_freq_ms: u32, - ) -> Self { - Self::start(syscfg, wdt, clocks, wdt_freq_ms) + pub fn new(wdt: pac::WatchDog, clocks: &Clocks, wdt_freq_ms: u32) -> Self { + Self::start(wdt, clocks, wdt_freq_ms) } - pub fn start( - syscfg: &mut pac::Sysconfig, - wdt: pac::WatchDog, - clocks: &Clocks, - wdt_freq_ms: u32, - ) -> Self { - syscfg.enable_peripheral_clock(PeripheralSelect::Watchdog); - syscfg.assert_periph_reset_for_two_cycles(PeripheralSelect::Watchdog); + pub fn start(wdt: pac::WatchDog, clocks: &Clocks, wdt_freq_ms: u32) -> Self { + enable_peripheral_clock(PeripheralSelect::Watchdog); + reset_peripheral_for_cycles(PeripheralSelect::Watchdog, 2); let wdt_clock = clocks.apb2(); let mut wdt_ctrl = Self { diff --git a/vorago-peb1/Cargo.toml b/vorago-peb1/Cargo.toml index 3432575..f77acdc 100644 --- a/vorago-peb1/Cargo.toml +++ b/vorago-peb1/Cargo.toml @@ -11,15 +11,10 @@ keywords = ["no-std", "peb1", "cortex-m", "vorago", "va416xx"] categories = ["embedded", "no-std", "hardware-support"] [dependencies] -cortex-m = "0.7" -cortex-m-rt = "0.7" -embedded-hal = "1" +va416xx-hal = { version = ">=0.3, <=0.5", path = "../va416xx-hal", features = ["va41630"] } lis2dh12 = { version = "0.7", features = ["out_f32"] } -va416xx-hal = { version = ">=0.3, <=0.5", features = ["va41630"] } - [features] -rt = ["va416xx-hal/rt"] [package.metadata.docs.rs] all-features = true diff --git a/vorago-peb1/src/lib.rs b/vorago-peb1/src/lib.rs index 5453cec..7e9f403 100644 --- a/vorago-peb1/src/lib.rs +++ b/vorago-peb1/src/lib.rs @@ -19,7 +19,7 @@ pub mod accelerometer { }; // Accelerometer located on the GPIO board. - pub type Accelerometer = Lis2dh12>; + pub type Accelerometer = Lis2dh12; #[derive(Debug)] pub enum ConstructorError { @@ -30,14 +30,12 @@ pub mod accelerometer { pub fn new_with_addr_detection( i2c: pac::I2c0, - sysconfig: &mut pac::Sysconfig, clocks: &Clocks, ) -> Result { let mut i2c_master = I2cMaster::new( i2c, - sysconfig, - MasterConfig::default(), clocks, + MasterConfig::default(), I2cSpeed::Regular100khz, ) .map_err(ConstructorError::ClkError)?; @@ -47,7 +45,7 @@ pub mod accelerometer { } pub fn new_with_i2cm( - i2c: I2cMaster, + i2c: I2cMaster, addr: lis2dh12::SlaveAddr, ) -> Result> { Lis2dh12::new(i2c, addr)