From 4fb72ecba61707090f5523c2ab4e375b052dd51c Mon Sep 17 00:00:00 2001 From: Robin Mueller Date: Sat, 12 Apr 2025 17:01:53 +0200 Subject: [PATCH] init new shared periph crate --- bootloader/Cargo.toml | 2 + bootloader/src/main.rs | 10 +- examples/embassy/Cargo.toml | 2 +- examples/embassy/src/bin/async-gpio.rs | 47 +- examples/embassy/src/bin/async-uart-rx.rs | 37 +- examples/embassy/src/bin/async-uart-tx.rs | 24 +- examples/embassy/src/main.rs | 17 +- examples/rtic/Cargo.toml | 9 +- examples/rtic/src/bin/blinky-button-rtic.rs | 75 +- examples/rtic/src/bin/rtic-empty.rs | 9 +- examples/rtic/src/bin/uart-echo-rtic.rs | 33 +- examples/rtic/src/main.rs | 30 +- examples/simple/Cargo.toml | 8 +- examples/simple/examples/blinky.rs | 45 +- examples/simple/examples/cascade.rs | 99 +- examples/simple/examples/pwm.rs | 44 +- examples/simple/examples/rtt-log.rs | 20 - examples/simple/examples/spi.rs | 223 +-- examples/simple/examples/timer-ticks.rs | 61 +- examples/simple/examples/uart.rs | 28 +- examples/simple/src/main.rs | 2 +- flashloader/Cargo.toml | 1 + flashloader/image-loader.py | 49 +- flashloader/loader.toml | 2 +- flashloader/requirements.txt | 4 +- flashloader/src/main.rs | 20 +- va108xx-embassy/Cargo.toml | 2 +- va108xx-embassy/src/lib.rs | 155 +- va108xx-hal/Cargo.toml | 3 +- va108xx-hal/src/clock.rs | 33 +- va108xx-hal/src/gpio/asynch.rs | 366 ----- va108xx-hal/src/gpio/dynpin.rs | 947 ------------ va108xx-hal/src/gpio/mod.rs | 81 +- va108xx-hal/src/gpio/pin.rs | 823 ---------- va108xx-hal/src/i2c.rs | 897 +---------- va108xx-hal/src/lib.rs | 79 +- va108xx-hal/src/pins.rs | 6 + va108xx-hal/src/pwm.rs | 457 +----- va108xx-hal/src/spi.rs | 1260 --------------- va108xx-hal/src/spi/mod.rs | 14 + va108xx-hal/src/sysconfig.rs | 36 +- va108xx-hal/src/time.rs | 26 +- va108xx-hal/src/timer.rs | 784 +--------- va108xx-hal/src/typelevel.rs | 155 -- va108xx-hal/src/uart/mod.rs | 1348 +---------------- va108xx-hal/src/uart/rx_asynch.rs | 440 ------ va108xx-hal/src/uart/tx_asynch.rs | 254 ---- vorago-reb1/Cargo.toml | 5 +- vorago-reb1/examples/adt75-temp-sensor.rs | 10 +- vorago-reb1/examples/adxl343-accelerometer.rs | 35 +- vorago-reb1/examples/blinky-button-irq.rs | 30 +- vorago-reb1/examples/blinky-leds.rs | 30 +- vorago-reb1/examples/max11619-adc.rs | 103 +- vorago-reb1/examples/nvm.rs | 8 +- vorago-reb1/src/button.rs | 10 +- vorago-reb1/src/leds.rs | 38 +- vorago-reb1/src/m95m01.rs | 46 +- vorago-reb1/src/max11619.rs | 7 +- vorago-reb1/src/temp_sensor.rs | 9 +- vscode/launch.json | 129 +- vscode/tasks.json | 16 +- 61 files changed, 718 insertions(+), 8825 deletions(-) delete mode 100644 examples/simple/examples/rtt-log.rs delete mode 100644 va108xx-hal/src/gpio/asynch.rs delete mode 100644 va108xx-hal/src/gpio/dynpin.rs delete mode 100644 va108xx-hal/src/gpio/pin.rs create mode 100644 va108xx-hal/src/pins.rs delete mode 100644 va108xx-hal/src/spi.rs create mode 100644 va108xx-hal/src/spi/mod.rs delete mode 100644 va108xx-hal/src/typelevel.rs delete mode 100644 va108xx-hal/src/uart/rx_asynch.rs delete mode 100644 va108xx-hal/src/uart/tx_asynch.rs diff --git a/bootloader/Cargo.toml b/bootloader/Cargo.toml index 1aaf734..13ab933 100644 --- a/bootloader/Cargo.toml +++ b/bootloader/Cargo.toml @@ -16,9 +16,11 @@ static_assertions = "1" [dependencies.va108xx-hal] version = "0.11" +path = "../va108xx-hal" [dependencies.vorago-reb1] version = "0.8" +path = "../vorago-reb1" [features] default = [] diff --git a/bootloader/src/main.rs b/bootloader/src/main.rs index dd8a028..b55ef6f 100644 --- a/bootloader/src/main.rs +++ b/bootloader/src/main.rs @@ -104,11 +104,11 @@ fn main() -> ! { rtt_init_print!(); rprintln!("-- VA108xx bootloader --"); } - let mut dp = pac::Peripherals::take().unwrap(); + let dp = pac::Peripherals::take().unwrap(); let cp = cortex_m::Peripherals::take().unwrap(); - let mut timer = CountdownTimer::new(&mut dp.sysconfig, CLOCK_FREQ, dp.tim0); + let mut timer = CountdownTimer::new(CLOCK_FREQ, dp.tim0); - let mut nvm = M95M01::new(&mut dp.sysconfig, CLOCK_FREQ, dp.spic); + let mut nvm = M95M01::new(CLOCK_FREQ, dp.spic); if FLASH_SELF { let mut first_four_bytes: [u8; 4] = [0; 4]; @@ -186,7 +186,7 @@ fn check_own_crc( sysconfig: &pac::Sysconfig, cp: &cortex_m::Peripherals, nvm: &mut NvmWrapper, - timer: &mut CountdownTimer, + timer: &mut CountdownTimer, ) { let crc_exp = unsafe { (BOOTLOADER_CRC_ADDR as *const u16).read_unaligned().to_be() }; // I'd prefer to use [core::slice::from_raw_parts], but that is problematic @@ -276,7 +276,7 @@ fn boot_app( syscfg: &pac::Sysconfig, cp: &cortex_m::Peripherals, app_sel: AppSel, - timer: &mut CountdownTimer, + timer: &mut CountdownTimer, ) -> ! { if DEBUG_PRINTOUTS && RTT_PRINTOUT { rprintln!("booting app {:?}", app_sel); diff --git a/examples/embassy/Cargo.toml b/examples/embassy/Cargo.toml index 039e18a..6356d17 100644 --- a/examples/embassy/Cargo.toml +++ b/examples/embassy/Cargo.toml @@ -29,7 +29,7 @@ embassy-executor = { version = "0.7", features = [ "executor-interrupt" ]} -va108xx-hal = { version = "0.11", features = ["defmt"] } +va108xx-hal = { version = "0.11", path = "../../va108xx-hal", features = ["defmt"] } va108xx-embassy = { version = "0.2" } [features] diff --git a/examples/embassy/src/bin/async-gpio.rs b/examples/embassy/src/bin/async-gpio.rs index e06f774..fcb81dd 100644 --- a/examples/embassy/src/bin/async-gpio.rs +++ b/examples/embassy/src/bin/async-gpio.rs @@ -12,11 +12,10 @@ 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 va108xx_hal::gpio::{ - on_interrupt_for_async_gpio_for_port, InputDynPinAsync, InputPinAsync, PinsB, Port, -}; +use va108xx_hal::gpio::asynch::{on_interrupt_for_async_gpio_for_port, InputPinAsync}; +use va108xx_hal::gpio::{Input, Output, PinState, Port}; +use va108xx_hal::pins::{PinsA, PinsB}; use va108xx_hal::{ - gpio::{DynPin, PinsA}, pac::{self, interrupt}, prelude::*, }; @@ -71,30 +70,28 @@ async fn main(spawner: Spawner) { dp.tim22, ); - let porta = PinsA::new(&mut dp.sysconfig, dp.porta); - let portb = PinsB::new(&mut dp.sysconfig, dp.portb); - let mut led0 = porta.pa10.into_readable_push_pull_output(); - let out_pa0 = porta.pa0.into_readable_push_pull_output(); - let in_pa1 = porta.pa1.into_floating_input(); - let out_pb22 = portb.pb22.into_readable_push_pull_output(); - let in_pb23 = portb.pb23.into_floating_input(); + let porta = PinsA::new(dp.porta); + let portb = PinsB::new(dp.portb); + let mut led0 = Output::new(porta.pa10, PinState::Low); + let out_pa0 = Output::new(porta.pa0, PinState::Low); + let in_pa1 = Input::new_floating(porta.pa1); + let out_pb22 = Output::new(portb.pb22, PinState::Low); + let in_pb23 = Input::new_floating(portb.pb23); let in_pa1_async = InputPinAsync::new(in_pa1, pac::Interrupt::OC10); - let out_pa0_dyn = out_pa0.downgrade(); - let in_pb23_async = InputDynPinAsync::new(in_pb23.downgrade(), PB22_TO_PB23_IRQ).unwrap(); - let out_pb22_dyn = out_pb22.downgrade(); + let in_pb23_async = InputPinAsync::new(in_pb23, PB22_TO_PB23_IRQ); spawner .spawn(output_task( "PA0 to PA1", - out_pa0_dyn, + out_pa0, CHANNEL_PA0_PA1.receiver(), )) .unwrap(); spawner .spawn(output_task( "PB22 to PB23", - out_pb22_dyn, + out_pb22, CHANNEL_PB22_TO_PB23.receiver(), )) .unwrap(); @@ -207,7 +204,7 @@ async fn check_pin_to_pin_async_ops( #[embassy_executor::task(pool_size = 2)] async fn output_task( ctx: &'static str, - mut out: DynPin, + mut out: Output, receiver: Receiver<'static, ThreadModeRawMutex, GpioCmd, 3>, ) { loop { @@ -216,25 +213,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(); } } } diff --git a/examples/embassy/src/bin/async-uart-rx.rs b/examples/embassy/src/bin/async-uart-rx.rs index 33a974d..8bf7baf 100644 --- a/examples/embassy/src/bin/async-uart-rx.rs +++ b/examples/embassy/src/bin/async-uart-rx.rs @@ -24,8 +24,9 @@ use embedded_io::Write; use embedded_io_async::Read; use heapless::spsc::{Consumer, Producer, Queue}; use va108xx_hal::{ - gpio::PinsA, + gpio::{Output, PinState}, pac::{self, interrupt}, + pins::PinsA, prelude::*, uart::{ self, on_interrupt_rx_overwriting, @@ -62,34 +63,34 @@ async fn main(spawner: Spawner) { dp.tim22, ); - let porta = PinsA::new(&mut dp.sysconfig, dp.porta); - let mut led0 = porta.pa10.into_readable_push_pull_output(); - let mut led1 = porta.pa7.into_readable_push_pull_output(); - let mut led2 = porta.pa6.into_readable_push_pull_output(); + let porta = PinsA::new(dp.porta); + let mut led0 = Output::new(porta.pa10, PinState::Low); + let mut led1 = Output::new(porta.pa7, PinState::Low); + let mut led2 = Output::new(porta.pa6, PinState::Low); - let tx_uart_a = porta.pa9.into_funsel_2(); - let rx_uart_a = porta.pa8.into_funsel_2(); + let tx_uart_a = porta.pa9; + let rx_uart_a = porta.pa8; let uarta = uart::Uart::new_with_interrupt( - &mut dp.sysconfig, 50.MHz(), dp.uarta, (tx_uart_a, rx_uart_a), - 115200.Hz(), + 115200.Hz().into(), InterruptConfig::new(pac::Interrupt::OC2, true, true), - ); + ) + .unwrap(); - let tx_uart_b = porta.pa3.into_funsel_2(); - let rx_uart_b = porta.pa2.into_funsel_2(); + let tx_uart_b = porta.pa3; + let rx_uart_b = porta.pa2; let uartb = uart::Uart::new_with_interrupt( - &mut dp.sysconfig, 50.MHz(), dp.uartb, (tx_uart_b, rx_uart_b), - 115200.Hz(), + 115200.Hz().into(), InterruptConfig::new(pac::Interrupt::OC3, true, true), - ); + ) + .unwrap(); let (mut tx_uart_a, rx_uart_a) = uarta.split(); let (tx_uart_b, rx_uart_b) = uartb.split(); let (prod_uart_a, cons_uart_a) = QUEUE_UART_A.take().split(); @@ -123,7 +124,7 @@ async fn main(spawner: Spawner) { } #[embassy_executor::task] -async fn uart_b_task(mut async_rx: RxAsyncOverwriting, mut tx: Tx) { +async fn uart_b_task(mut async_rx: RxAsyncOverwriting<256>, mut tx: Tx) { let mut buf = [0u8; 256]; loop { defmt::info!("Current time UART B: {}", Instant::now().as_secs()); @@ -144,7 +145,7 @@ async fn uart_b_task(mut async_rx: RxAsyncOverwriting, mut tx: fn OC2() { let mut prod = critical_section::with(|cs| PRODUCER_UART_A.borrow(cs).borrow_mut().take().unwrap()); - let errors = on_interrupt_rx(Bank::A, &mut prod); + let errors = on_interrupt_rx(Bank::Uart0, &mut prod); critical_section::with(|cs| *PRODUCER_UART_A.borrow(cs).borrow_mut() = Some(prod)); // In a production app, we could use a channel to send the errors to the main task. if let Err(errors) = errors { @@ -157,7 +158,7 @@ fn OC2() { fn OC3() { let mut prod = critical_section::with(|cs| PRODUCER_UART_B.borrow(cs).borrow_mut().take().unwrap()); - let errors = on_interrupt_rx_overwriting(Bank::B, &mut prod, &CONSUMER_UART_B); + let errors = on_interrupt_rx_overwriting(Bank::Uart1, &mut prod, &CONSUMER_UART_B); critical_section::with(|cs| *PRODUCER_UART_B.borrow(cs).borrow_mut() = Some(prod)); // In a production app, we could use a channel to send the errors to the main task. if let Err(errors) = errors { diff --git a/examples/embassy/src/bin/async-uart-tx.rs b/examples/embassy/src/bin/async-uart-tx.rs index 29ee434..8c073c7 100644 --- a/examples/embassy/src/bin/async-uart-tx.rs +++ b/examples/embassy/src/bin/async-uart-tx.rs @@ -17,8 +17,9 @@ use embassy_executor::Spawner; use embassy_time::{Duration, Instant, Ticker}; use embedded_io_async::Write; use va108xx_hal::{ - gpio::PinsA, + gpio::{Output, PinState}, pac::{self, interrupt}, + pins::PinsA, prelude::*, uart::{self, on_interrupt_tx, Bank, TxAsync}, InterruptConfig, @@ -49,22 +50,23 @@ async fn main(_spawner: Spawner) { dp.tim22, ); - let porta = PinsA::new(&mut dp.sysconfig, dp.porta); - let mut led0 = porta.pa10.into_readable_push_pull_output(); - let mut led1 = porta.pa7.into_readable_push_pull_output(); - let mut led2 = porta.pa6.into_readable_push_pull_output(); + let porta = PinsA::new(dp.porta); - let tx = porta.pa9.into_funsel_2(); - let rx = porta.pa8.into_funsel_2(); + let mut led0 = Output::new(porta.pa10, PinState::Low); + let mut led1 = Output::new(porta.pa7, PinState::Low); + let mut led2 = Output::new(porta.pa6, PinState::Low); + + let tx = porta.pa9; + let rx = porta.pa8; let uarta = uart::Uart::new_with_interrupt( - &mut dp.sysconfig, 50.MHz(), dp.uarta, (tx, rx), - 115200.Hz(), + 115200.Hz().into(), InterruptConfig::new(pac::Interrupt::OC2, true, true), - ); + ) + .unwrap(); let (tx, _rx) = uarta.split(); let mut async_tx = TxAsync::new(tx); let mut ticker = Ticker::every(Duration::from_secs(1)); @@ -89,5 +91,5 @@ async fn main(_spawner: Spawner) { #[interrupt] #[allow(non_snake_case)] fn OC2() { - on_interrupt_tx(Bank::A); + on_interrupt_tx(Bank::Uart0); } diff --git a/examples/embassy/src/main.rs b/examples/embassy/src/main.rs index 35e0411..7e38a1c 100644 --- a/examples/embassy/src/main.rs +++ b/examples/embassy/src/main.rs @@ -12,7 +12,12 @@ cfg_if::cfg_if! { } } -use va108xx_hal::{gpio::PinsA, pac, prelude::*}; +use va108xx_hal::{ + gpio::{Output, PinState}, + pac, + pins::PinsA, + prelude::*, +}; const SYSCLK_FREQ: Hertz = Hertz::from_raw(50_000_000); @@ -35,8 +40,6 @@ async fn main(_spawner: Spawner) { ); } else { va108xx_embassy::init_with_custom_irqs( - &mut dp.sysconfig, - &dp.irqsel, SYSCLK_FREQ, dp.tim23, dp.tim22, @@ -46,10 +49,10 @@ async fn main(_spawner: Spawner) { } } - let porta = PinsA::new(&mut dp.sysconfig, dp.porta); - let mut led0 = porta.pa10.into_readable_push_pull_output(); - let mut led1 = porta.pa7.into_readable_push_pull_output(); - let mut led2 = porta.pa6.into_readable_push_pull_output(); + let porta = PinsA::new(dp.porta); + let mut led0 = Output::new(porta.pa10, PinState::Low); + let mut led1 = Output::new(porta.pa7, PinState::Low); + let mut led2 = Output::new(porta.pa6, 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 30ec487..5792e94 100644 --- a/examples/rtic/Cargo.toml +++ b/examples/rtic/Cargo.toml @@ -8,8 +8,9 @@ cortex-m = { version = "0.7", features = ["critical-section-single-core"] } cortex-m-rt = "0.7" embedded-hal = "1" embedded-io = "0.6" -rtt-target = "0.6" -panic-rtt-target = "0.2" +defmt-rtt = "0.4" +defmt = "1" +panic-probe = { version = "1", features = ["defmt"] } # Even though we do not use this directly, we need to activate this feature explicitely # so that RTIC compiles because thumv6 does not have CAS operations natively. @@ -22,5 +23,5 @@ rtic-sync = { version = "1.3", features = ["defmt-03"] } once_cell = {version = "1", default-features = false, features = ["critical-section"]} ringbuf = { version = "0.4.7", default-features = false, features = ["portable-atomic"] } -va108xx-hal = { version = "0.11" } -vorago-reb1 = { version = "0.8" } +va108xx-hal = { version = "0.11", path = "../../va108xx-hal" } +vorago-reb1 = { version = "0.8", path = "../../vorago-reb1" } diff --git a/examples/rtic/src/bin/blinky-button-rtic.rs b/examples/rtic/src/bin/blinky-button-rtic.rs index bba2d4c..c927327 100644 --- a/examples/rtic/src/bin/blinky-button-rtic.rs +++ b/examples/rtic/src/bin/blinky-button-rtic.rs @@ -4,34 +4,29 @@ #[rtic::app(device = pac)] mod app { - use panic_rtt_target as _; use rtic_example::SYSCLK_FREQ; - use rtt_target::{rprintln, rtt_init_default, set_print_channel}; + // Import panic provider. + use panic_probe as _; + // Import global logger. + use defmt_rtt as _; use va108xx_hal::{ clock::{set_clk_div_register, FilterClkSel}, - gpio::{FilterType, InterruptEdge, PinsA}, + gpio::{FilterType, InterruptEdge}, pac, - prelude::*, - timer::{default_ms_irq_handler, set_up_ms_tick, InterruptConfig}, + pins::PinsA, + timer::InterruptConfig, }; use vorago_reb1::button::Button; use vorago_reb1::leds::Leds; rtic_monotonics::systick_monotonic!(Mono, 1_000); - #[derive(Debug, PartialEq)] + #[derive(Debug, PartialEq, defmt::Format)] pub enum PressMode { Toggle, Keep, } - #[derive(Debug, PartialEq)] - pub enum CfgMode { - Prompt, - Fixed, - } - - const CFG_MODE: CfgMode = CfgMode::Fixed; // You can change the press mode here const DEFAULT_MODE: PressMode = PressMode::Toggle; @@ -47,53 +42,35 @@ mod app { #[init] fn init(cx: init::Context) -> (Shared, Local) { - let channels = rtt_init_default!(); - set_print_channel(channels.up.0); - rprintln!("-- Vorago Button IRQ Example --"); + defmt::println!("-- Vorago Button IRQ Example --"); Mono::start(cx.core.SYST, SYSCLK_FREQ.raw()); - let mode = match CFG_MODE { - // Ask mode from user via RTT - CfgMode::Prompt => prompt_mode(channels.down.0), - // Use mode hardcoded in `DEFAULT_MODE` - CfgMode::Fixed => DEFAULT_MODE, - }; - rprintln!("Using {:?} mode", mode); + let mode = DEFAULT_MODE; + defmt::info!("Using {:?} mode", mode); let mut dp = cx.device; - let pinsa = PinsA::new(&mut dp.sysconfig, dp.porta); + let pinsa = PinsA::new(dp.porta); let edge_irq = match mode { PressMode::Toggle => InterruptEdge::HighToLow, PressMode::Keep => InterruptEdge::BothEdges, }; // Configure an edge interrupt on the button and route it to interrupt vector 15 - let mut button = Button::new(pinsa.pa11.into_floating_input()); + let mut button = Button::new(pinsa.pa11); if mode == PressMode::Toggle { // This filter debounces the switch for edge based interrupts - button.configure_filter_type(FilterType::FilterFourClockCycles, FilterClkSel::Clk1); + button.configure_filter_type(FilterType::FilterFourCycles, FilterClkSel::Clk1); set_clk_div_register(&mut dp.sysconfig, FilterClkSel::Clk1, 50_000); } button.configure_and_enable_edge_interrupt( edge_irq, InterruptConfig::new(pac::interrupt::OC15, true, true), ); - let mut leds = Leds::new( - pinsa.pa10.into_push_pull_output(), - pinsa.pa7.into_push_pull_output(), - pinsa.pa6.into_push_pull_output(), - ); + let mut leds = Leds::new(pinsa.pa10, pinsa.pa7, pinsa.pa6); for led in leds.iter_mut() { led.off(); } - set_up_ms_tick( - InterruptConfig::new(pac::Interrupt::OC0, true, true), - &mut dp.sysconfig, - Some(&mut dp.irqsel), - 50.MHz(), - dp.tim0, - ); (Shared {}, Local { leds, button, mode }) } @@ -118,26 +95,4 @@ mod app { leds[0].on(); } } - - #[task(binds = OC0)] - fn ms_tick(_cx: ms_tick::Context) { - default_ms_irq_handler(); - } - - fn prompt_mode(mut down_channel: rtt_target::DownChannel) -> PressMode { - rprintln!("Using prompt mode"); - rprintln!("Please enter the mode [0: Toggle, 1: Keep]"); - let mut read_buf: [u8; 16] = [0; 16]; - let mut read; - loop { - read = down_channel.read(&mut read_buf); - for &byte in &read_buf[..read] { - match byte as char { - '0' => return PressMode::Toggle, - '1' => return PressMode::Keep, - _ => continue, // Ignore other characters - } - } - } - } } diff --git a/examples/rtic/src/bin/rtic-empty.rs b/examples/rtic/src/bin/rtic-empty.rs index 0950065..294e382 100644 --- a/examples/rtic/src/bin/rtic-empty.rs +++ b/examples/rtic/src/bin/rtic-empty.rs @@ -4,8 +4,10 @@ #[rtic::app(device = pac)] mod app { - use panic_rtt_target as _; - use rtt_target::{rprintln, rtt_init_default}; + // Import panic provider. + use panic_probe as _; + // Import global logger. + use defmt_rtt as _; use va108xx_hal::pac; #[local] @@ -16,8 +18,7 @@ mod app { #[init] fn init(_ctx: init::Context) -> (Shared, Local) { - rtt_init_default!(); - rprintln!("-- Vorago RTIC template --"); + defmt::println!("-- Vorago RTIC template --"); (Shared {}, Local {}) } diff --git a/examples/rtic/src/bin/uart-echo-rtic.rs b/examples/rtic/src/bin/uart-echo-rtic.rs index 296e66e..616c195 100644 --- a/examples/rtic/src/bin/uart-echo-rtic.rs +++ b/examples/rtic/src/bin/uart-echo-rtic.rs @@ -1,4 +1,4 @@ -//! More complex UART application +//! More complex UART application on UART PA8 (TX) and PA9 (RX). //! //! Uses the IRQ capabilities of the VA10820 peripheral and the RTIC framework to poll the UART in //! a non-blocking way. All received data will be sent back to the sender. @@ -14,14 +14,16 @@ const RX_RING_BUF_SIZE: usize = 1024; mod app { use super::*; use embedded_io::Write; - use panic_rtt_target as _; use ringbuf::traits::{Consumer, Observer, Producer}; use rtic_example::SYSCLK_FREQ; + // Import panic provider. + use panic_probe as _; + // Import global logger. + use defmt_rtt as _; use rtic_monotonics::Monotonic; - use rtt_target::{rprintln, rtt_init_print}; use va108xx_hal::{ - gpio::PinsA, pac, + pins::PinsA, prelude::*, uart::{self, RxWithInterrupt, Tx}, InterruptConfig, @@ -29,8 +31,8 @@ mod app { #[local] struct Local { - rx: RxWithInterrupt, - tx: Tx, + rx: RxWithInterrupt, + tx: Tx, } #[shared] @@ -42,24 +44,23 @@ mod app { #[init] fn init(cx: init::Context) -> (Shared, Local) { - rtt_init_print!(); - rprintln!("-- VA108xx UART Echo with IRQ example application--"); + defmt::println!("-- VA108xx UART Echo with IRQ example application--"); Mono::start(cx.core.SYST, SYSCLK_FREQ.raw()); - let mut dp = cx.device; - let gpioa = PinsA::new(&mut dp.sysconfig, dp.porta); - let tx = gpioa.pa9.into_funsel_2(); - let rx = gpioa.pa8.into_funsel_2(); + let dp = cx.device; + let gpioa = PinsA::new(dp.porta); + let tx = gpioa.pa9; + let rx = gpioa.pa8; let irq_uart = uart::Uart::new_with_interrupt( - &mut dp.sysconfig, SYSCLK_FREQ, dp.uarta, (tx, rx), - 115200.Hz(), + 115200.Hz().into(), InterruptConfig::new(pac::Interrupt::OC3, true, true), - ); + ) + .unwrap(); let (tx, rx) = irq_uart.split(); let mut rx = rx.into_rx_with_irq(); @@ -104,7 +105,7 @@ mod app { } if ringbuf_full { // Could also drop oldest data, but that would require the consumer to be shared. - rprintln!("buffer full, data was dropped"); + defmt::println!("buffer full, data was dropped"); } } diff --git a/examples/rtic/src/main.rs b/examples/rtic/src/main.rs index ad6ccd9..833a751 100644 --- a/examples/rtic/src/main.rs +++ b/examples/rtic/src/main.rs @@ -5,21 +5,24 @@ #[rtic::app(device = pac, dispatchers = [OC31, OC30, OC29])] mod app { use cortex_m::asm; - use panic_rtt_target as _; use rtic_example::SYSCLK_FREQ; use rtic_monotonics::systick::prelude::*; use rtic_monotonics::Monotonic; - use rtt_target::{rprintln, rtt_init_print}; + // Import panic provider. + use panic_probe as _; + // Import global logger. + use defmt_rtt as _; use va108xx_hal::{ - gpio::{OutputReadablePushPull, Pin, PinsA, PA10, PA6, PA7}, + gpio::{Output, PinState}, pac, + pins::PinsA, }; #[local] struct Local { - led0: Pin, - led1: Pin, - led2: Pin, + led0: Output, + led1: Output, + led2: Output, } #[shared] @@ -28,16 +31,15 @@ mod app { rtic_monotonics::systick_monotonic!(Mono, 1_000); #[init] - fn init(mut cx: init::Context) -> (Shared, Local) { - rtt_init_print!(); - rprintln!("-- Vorago VA108xx RTIC template --"); + fn init(cx: init::Context) -> (Shared, Local) { + defmt::println!("-- Vorago VA108xx RTIC template --"); Mono::start(cx.core.SYST, SYSCLK_FREQ.raw()); - let porta = PinsA::new(&mut cx.device.sysconfig, cx.device.porta); - let led0 = porta.pa10.into_readable_push_pull_output(); - let led1 = porta.pa7.into_readable_push_pull_output(); - let led2 = porta.pa6.into_readable_push_pull_output(); + let porta = PinsA::new(cx.device.porta); + let led0 = Output::new(porta.pa10, PinState::Low); + let led1 = Output::new(porta.pa7, PinState::Low); + let led2 = Output::new(porta.pa6, PinState::Low); blinky::spawn().ok(); (Shared {}, Local { led0, led1, led2 }) } @@ -56,7 +58,7 @@ mod app { )] async fn blinky(cx: blinky::Context) { loop { - rprintln!("toggling LEDs"); + defmt::println!("toggling LEDs"); cx.local.led0.toggle(); cx.local.led1.toggle(); cx.local.led2.toggle(); diff --git a/examples/simple/Cargo.toml b/examples/simple/Cargo.toml index 158cbff..9407c5f 100644 --- a/examples/simple/Cargo.toml +++ b/examples/simple/Cargo.toml @@ -7,17 +7,21 @@ edition = "2021" cortex-m = {version = "0.7", features = ["critical-section-single-core"]} cortex-m-rt = "0.7" panic-halt = "1" -panic-rtt-target = "0.2" critical-section = "1" -rtt-target = "0.6" +defmt-rtt = "0.4" +defmt = "1" +panic-probe = { version = "1", features = ["defmt"] } embedded-hal = "1" embedded-hal-nb = "1" embedded-io = "0.6" cortex-m-semihosting = "0.5.0" +portable-atomic = { version = "1", features = ["unsafe-assume-single-core"] } [dependencies.va108xx-hal] version = "0.11" +path = "../../va108xx-hal" features = ["defmt"] [dependencies.vorago-reb1] +path = "../../vorago-reb1" version = "0.8" diff --git a/examples/simple/examples/blinky.rs b/examples/simple/examples/blinky.rs index bcdfe0c..1d30b82 100644 --- a/examples/simple/examples/blinky.rs +++ b/examples/simple/examples/blinky.rs @@ -10,52 +10,37 @@ use cortex_m_rt::entry; use embedded_hal::delay::DelayNs; use panic_halt as _; use va108xx_hal::{ - gpio::PinsA, - pac::{self, interrupt}, + gpio::{Output, PinState}, + pac::{self}, + pins::PinsA, prelude::*, - timer::DelayMs, - timer::{default_ms_irq_handler, set_up_ms_tick, CountdownTimer}, - InterruptConfig, + timer::CountdownTimer, }; #[entry] fn main() -> ! { - let mut dp = pac::Peripherals::take().unwrap(); - let mut delay_ms = DelayMs::new(set_up_ms_tick( - InterruptConfig::new(interrupt::OC0, true, true), - &mut dp.sysconfig, - Some(&mut dp.irqsel), - 50.MHz(), - dp.tim0, - )) - .unwrap(); - let mut delay_tim1 = CountdownTimer::new(&mut dp.sysconfig, 50.MHz(), dp.tim1); - let porta = PinsA::new(&mut dp.sysconfig, dp.porta); - let mut led1 = porta.pa10.into_readable_push_pull_output(); - let mut led2 = porta.pa7.into_readable_push_pull_output(); - let mut led3 = porta.pa6.into_readable_push_pull_output(); + let dp = pac::Peripherals::take().unwrap(); + let mut delay = CountdownTimer::new(50.MHz(), dp.tim1); + let porta = PinsA::new(dp.porta); + let mut led1 = Output::new(porta.pa10, PinState::Low); + let mut led2 = Output::new(porta.pa7, PinState::Low); + let mut led3 = Output::new(porta.pa6, PinState::Low); for _ in 0..10 { led1.set_low(); led2.set_low(); led3.set_low(); - delay_ms.delay_ms(200); + delay.delay_ms(200); led1.set_high(); led2.set_high(); led3.set_high(); - delay_tim1.delay_ms(200); + delay.delay_ms(200); } loop { led1.toggle(); - delay_ms.delay_ms(200); + delay.delay_ms(200); led2.toggle(); - delay_tim1.delay_ms(200); + delay.delay_ms(200); led3.toggle(); - delay_ms.delay_ms(200); + delay.delay_ms(200); } } - -#[interrupt] -#[allow(non_snake_case)] -fn OC0() { - default_ms_irq_handler() -} diff --git a/examples/simple/examples/cascade.rs b/examples/simple/examples/cascade.rs index bac4745..0d5e7c0 100644 --- a/examples/simple/examples/cascade.rs +++ b/examples/simple/examples/cascade.rs @@ -6,92 +6,68 @@ #![no_std] #![allow(non_snake_case)] -use core::cell::RefCell; -use cortex_m::interrupt::Mutex; use cortex_m_rt::entry; use embedded_hal::delay::DelayNs; -use panic_rtt_target as _; -use rtt_target::{rprintln, rtt_init_print}; +// Import panic provider. +use panic_probe as _; +// Import logger. +use defmt_rtt as _; use va108xx_hal::{ pac::{self, interrupt}, prelude::*, - timer::{ - default_ms_irq_handler, set_up_ms_delay_provider, CascadeCtrl, CascadeSource, - CountdownTimer, Event, InterruptConfig, - }, + timer::{CascadeControl, CascadeSelect, CascadeSource, CountdownTimer, InterruptConfig}, }; -static CSD_TGT_1: Mutex>>> = - Mutex::new(RefCell::new(None)); -static CSD_TGT_2: Mutex>>> = - Mutex::new(RefCell::new(None)); - #[entry] fn main() -> ! { - rtt_init_print!(); - rprintln!("-- VA108xx Cascade example application--"); + defmt::println!("-- VA108xx Cascade example application--"); - let mut dp = pac::Peripherals::take().unwrap(); - let mut delay = set_up_ms_delay_provider(&mut dp.sysconfig, 50.MHz(), dp.tim0); + let dp = pac::Peripherals::take().unwrap(); + let mut delay = CountdownTimer::new(50.MHz(), dp.tim0); // Will be started periodically to trigger a cascade - let mut cascade_triggerer = - CountdownTimer::new(&mut dp.sysconfig, 50.MHz(), dp.tim3).auto_disable(true); - cascade_triggerer.listen( - Event::TimeOut, - InterruptConfig::new(pac::Interrupt::OC1, true, false), - Some(&mut dp.irqsel), - Some(&mut dp.sysconfig), - ); + let mut cascade_triggerer = CountdownTimer::new(50.MHz(), dp.tim3); + cascade_triggerer.auto_disable(true); + cascade_triggerer.enable_interrupt(InterruptConfig::new(pac::Interrupt::OC1, true, false)); + cascade_triggerer.enable(); // First target for cascade - let mut cascade_target_1 = - CountdownTimer::new(&mut dp.sysconfig, 50.MHz(), dp.tim4).auto_deactivate(true); + let mut cascade_target_1 = CountdownTimer::new(50.MHz(), dp.tim4); + cascade_target_1.auto_deactivate(true); cascade_target_1 - .cascade_0_source(CascadeSource::Tim(3)) - .expect("Configuring cascade source for TIM4 failed"); - let mut csd_cfg = CascadeCtrl { - enb_start_src_csd0: true, + .cascade_source(CascadeSelect::Csd0, CascadeSource::Tim(3)) + .unwrap(); + let mut csd_cfg = CascadeControl { + enable_src_0: true, + trigger_mode_0: true, ..Default::default() }; - // Use trigger mode here - csd_cfg.trg_csd0 = true; cascade_target_1.cascade_control(csd_cfg); // Normally it should already be sufficient to activate IRQ in the CTRL // register but a full interrupt is use here to display print output when // the timer expires - cascade_target_1.listen( - Event::TimeOut, - InterruptConfig::new(pac::Interrupt::OC2, true, false), - Some(&mut dp.irqsel), - Some(&mut dp.sysconfig), - ); + cascade_target_1.enable_interrupt(InterruptConfig::new(pac::Interrupt::OC2, true, false)); // The counter will only activate when the cascade signal is coming in so // it is okay to call start here to set the reset value cascade_target_1.start(1.Hz()); // Activated by first cascade target - let mut cascade_target_2 = - CountdownTimer::new(&mut dp.sysconfig, 50.MHz(), dp.tim5).auto_deactivate(true); + let mut cascade_target_2 = CountdownTimer::new(50.MHz(), dp.tim5); + cascade_target_2.auto_deactivate(true); // Set TIM4 as cascade source cascade_target_2 - .cascade_1_source(CascadeSource::Tim(4)) - .expect("Configuring cascade source for TIM5 failed"); + .cascade_source(CascadeSelect::Csd1, CascadeSource::Tim(4)) + .unwrap(); - csd_cfg = CascadeCtrl::default(); - csd_cfg.enb_start_src_csd1 = true; + csd_cfg = CascadeControl::default(); + csd_cfg.enable_src_1 = true; // Use trigger mode here - csd_cfg.trg_csd1 = true; + csd_cfg.trigger_mode_1 = true; cascade_target_2.cascade_control(csd_cfg); // Normally it should already be sufficient to activate IRQ in the CTRL // register but a full interrupt is use here to display print output when // the timer expires - cascade_target_2.listen( - Event::TimeOut, - InterruptConfig::new(pac::Interrupt::OC3, true, false), - Some(&mut dp.irqsel), - Some(&mut dp.sysconfig), - ); + cascade_target_2.enable_interrupt(InterruptConfig::new(pac::Interrupt::OC3, true, false)); // The counter will only activate when the cascade signal is coming in so // it is okay to call start here to set the reset value cascade_target_2.start(1.Hz()); @@ -103,40 +79,31 @@ fn main() -> ! { cortex_m::peripheral::NVIC::unmask(pac::Interrupt::OC2); cortex_m::peripheral::NVIC::unmask(pac::Interrupt::OC3); } - // Make both cascade targets accessible from the IRQ handler with the Mutex dance - cortex_m::interrupt::free(|cs| { - CSD_TGT_1.borrow(cs).replace(Some(cascade_target_1)); - CSD_TGT_2.borrow(cs).replace(Some(cascade_target_2)); - }); + loop { - rprintln!("-- Triggering cascade in 0.5 seconds --"); + defmt::info!("-- Triggering cascade in 0.5 seconds --"); cascade_triggerer.start(2.Hz()); delay.delay_ms(5000); } } -#[interrupt] -fn OC0() { - default_ms_irq_handler() -} - #[interrupt] fn OC1() { static mut IDX: u32 = 0; - rprintln!("{}: Cascade triggered timed out", &IDX); + defmt::info!("{}: Cascade trigger timed out", &IDX); *IDX += 1; } #[interrupt] fn OC2() { static mut IDX: u32 = 0; - rprintln!("{}: First cascade target timed out", &IDX); + defmt::info!("{}: First cascade target timed out", &IDX); *IDX += 1; } #[interrupt] fn OC3() { static mut IDX: u32 = 0; - rprintln!("{}: Second cascade target timed out", &IDX); + defmt::info!("{}: Second cascade target timed out", &IDX); *IDX += 1; } diff --git a/examples/simple/examples/pwm.rs b/examples/simple/examples/pwm.rs index 57acac5..237a511 100644 --- a/examples/simple/examples/pwm.rs +++ b/examples/simple/examples/pwm.rs @@ -1,39 +1,36 @@ //! Simple PWM example +//! +//! Outputs a PWM waveform on pin PA3. #![no_main] #![no_std] use cortex_m_rt::entry; use embedded_hal::{delay::DelayNs, pwm::SetDutyCycle}; -use panic_rtt_target as _; -use rtt_target::{rprintln, rtt_init_print}; +// Import panic provider. +use panic_probe as _; +// Import logger. +use defmt_rtt as _; use va108xx_hal::{ - gpio::PinsA, pac, + pins::PinsA, prelude::*, - pwm::{self, get_duty_from_percent, PwmA, PwmB, ReducedPwmPin}, - timer::set_up_ms_delay_provider, + pwm::{self, get_duty_from_percent, PwmA, PwmB, PwmPin}, + timer::CountdownTimer, }; #[entry] fn main() -> ! { - rtt_init_print!(); - rprintln!("-- VA108xx PWM example application--"); - let mut dp = pac::Peripherals::take().unwrap(); - let pinsa = PinsA::new(&mut dp.sysconfig, dp.porta); - let mut pwm = pwm::PwmPin::new( - &mut dp.sysconfig, - 50.MHz(), - (pinsa.pa3.into_funsel_1(), dp.tim3), - 10.Hz(), - ); - let mut delay = set_up_ms_delay_provider(&mut dp.sysconfig, 50.MHz(), dp.tim0); + defmt::println!("-- VA108xx PWM example application--"); + let dp = pac::Peripherals::take().unwrap(); + let pinsa = PinsA::new(dp.porta); + let mut pwm = pwm::PwmPin::new(50.MHz(), (pinsa.pa3, dp.tim3), 10.Hz()).unwrap(); + let mut delay = CountdownTimer::new(50.MHz(), dp.tim0); 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 @@ -42,11 +39,10 @@ fn main() -> ! { current_duty_cycle += 0.02; counter += 1; if counter % 10 == 0 { - rprintln!("current duty cycle: {}", current_duty_cycle); + 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(); } @@ -55,7 +51,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 { @@ -64,9 +60,9 @@ fn main() -> ! { upper_limit -= 0.01; pwmb.set_pwmb_lower_limit(get_duty_from_percent(lower_limit)); pwmb.set_pwmb_upper_limit(get_duty_from_percent(upper_limit)); - rprintln!("Lower limit: {}", pwmb.pwmb_lower_limit()); - rprintln!("Upper limit: {}", pwmb.pwmb_upper_limit()); + 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 cbd4187..0000000 --- a/examples/simple/examples/rtt-log.rs +++ /dev/null @@ -1,20 +0,0 @@ -//! Code to test RTT logger functionality -#![no_main] -#![no_std] - -use cortex_m_rt::entry; -use panic_rtt_target as _; -use rtt_target::{rprintln, rtt_init_print}; -use va108xx_hal as _; - -#[entry] -fn main() -> ! { - rtt_init_print!(); - rprintln!("-- VA108XX RTT example --"); - let mut counter = 0; - loop { - rprintln!("{}: Hello, world!", counter); - counter += 1; - cortex_m::asm::delay(25_000_000); - } -} diff --git a/examples/simple/examples/spi.rs b/examples/simple/examples/spi.rs index 7869b5a..9424855 100644 --- a/examples/simple/examples/spi.rs +++ b/examples/simple/examples/spi.rs @@ -1,23 +1,21 @@ //! SPI example application #![no_main] #![no_std] - -use core::cell::RefCell; - use cortex_m_rt::entry; use embedded_hal::{ delay::DelayNs, spi::{Mode, SpiBus, MODE_0}, }; -use panic_rtt_target as _; -use rtt_target::{rprintln, rtt_init_print}; +// Import panic provider. +use panic_probe as _; +// Import logger. +use defmt_rtt as _; use va108xx_hal::{ - gpio::{PinsA, PinsB}, - pac::{self, interrupt}, + pac, + pins::{PinsA, PinsB}, prelude::*, - spi::{self, Spi, SpiBase, SpiClkConfig, TransferConfigWithHwcs}, - timer::{default_ms_irq_handler, set_up_ms_tick}, - InterruptConfig, + spi::{self, configure_pin_as_hw_cs_pin, Spi, SpiClkConfig, TransferConfig}, + timer::CountdownTimer, }; #[derive(PartialEq, Debug)] @@ -43,23 +41,14 @@ const FILL_WORD: u8 = 0x0f; #[entry] fn main() -> ! { - rtt_init_print!(); - rprintln!("-- VA108xx SPI example application--"); - let mut dp = pac::Peripherals::take().unwrap(); - let mut delay = set_up_ms_tick( - InterruptConfig::new(interrupt::OC0, true, true), - &mut dp.sysconfig, - Some(&mut dp.irqsel), - 50.MHz(), - dp.tim0, - ); + defmt::println!("-- VA108xx SPI example application--"); + let dp = pac::Peripherals::take().unwrap(); + let mut delay = CountdownTimer::new(50.MHz(), dp.tim0); let spi_clk_cfg = SpiClkConfig::from_clk(50.MHz(), SPI_SPEED_KHZ.kHz()) .expect("creating SPI clock config failed"); - let spia_ref: RefCell>> = RefCell::new(None); - let spib_ref: RefCell>> = RefCell::new(None); - let pinsa = PinsA::new(&mut dp.sysconfig, dp.porta); - let pinsb = PinsB::new(&mut dp.sysconfig, dp.portb); + let pinsa = PinsA::new(dp.porta); + let pinsb = PinsB::new(dp.portb); let mut spi_cfg = spi::SpiConfig::default(); if EXAMPLE_SEL == ExampleSelect::Loopback { @@ -67,158 +56,82 @@ fn main() -> ! { } // Set up the SPI peripheral - match SPI_BUS_SEL { + let mut spi = match SPI_BUS_SEL { SpiBusSelect::SpiAPortA => { - let (sck, mosi, miso) = ( - pinsa.pa31.into_funsel_1(), - pinsa.pa30.into_funsel_1(), - pinsa.pa29.into_funsel_1(), - ); - let mut spia = Spi::new( - &mut dp.sysconfig, - 50.MHz(), - dp.spia, - (sck, miso, mosi), - spi_cfg, - ); + let (sck, mosi, miso) = (pinsa.pa31, pinsa.pa30, pinsa.pa29); + let mut spia = Spi::new(50.MHz(), dp.spia, (sck, miso, mosi), spi_cfg).unwrap(); spia.set_fill_word(FILL_WORD); - spia_ref.borrow_mut().replace(spia.downgrade()); + spia } SpiBusSelect::SpiAPortB => { - let (sck, mosi, miso) = ( - pinsb.pb9.into_funsel_2(), - pinsb.pb8.into_funsel_2(), - pinsb.pb7.into_funsel_2(), - ); - let mut spia = Spi::new( - &mut dp.sysconfig, - 50.MHz(), - dp.spia, - (sck, miso, mosi), - spi_cfg, - ); + let (sck, mosi, miso) = (pinsb.pb9, pinsb.pb8, pinsb.pb7); + let mut spia = Spi::new(50.MHz(), dp.spia, (sck, miso, mosi), spi_cfg).unwrap(); spia.set_fill_word(FILL_WORD); - spia_ref.borrow_mut().replace(spia.downgrade()); + spia } SpiBusSelect::SpiBPortB => { - let (sck, mosi, miso) = ( - pinsb.pb5.into_funsel_1(), - pinsb.pb4.into_funsel_1(), - pinsb.pb3.into_funsel_1(), - ); - let mut spib = Spi::new( - &mut dp.sysconfig, - 50.MHz(), - dp.spib, - (sck, miso, mosi), - spi_cfg, - ); + let (sck, mosi, miso) = (pinsb.pb5, pinsb.pb4, pinsb.pb3); + let mut spib = Spi::new(50.MHz(), dp.spib, (sck, miso, mosi), spi_cfg).unwrap(); spib.set_fill_word(FILL_WORD); - spib_ref.borrow_mut().replace(spib.downgrade()); + spib } - } + }; // Configure transfer specific properties here match SPI_BUS_SEL { SpiBusSelect::SpiAPortA | SpiBusSelect::SpiAPortB => { - if let Some(ref mut spi) = *spia_ref.borrow_mut() { - let transfer_cfg = TransferConfigWithHwcs::new_no_hw_cs( - Some(spi_clk_cfg), - Some(SPI_MODE), - BLOCKMODE, - true, - false, - ); - spi.cfg_transfer(&transfer_cfg); - } + let transfer_cfg = TransferConfig { + clk_cfg: Some(spi_clk_cfg), + mode: Some(SPI_MODE), + sod: true, + blockmode: BLOCKMODE, + bmstall: true, + hw_cs: None, + }; + spi.cfg_transfer(&transfer_cfg); } SpiBusSelect::SpiBPortB => { - if let Some(ref mut spi) = *spib_ref.borrow_mut() { - let hw_cs_pin = pinsb.pb2.into_funsel_1(); - let transfer_cfg = TransferConfigWithHwcs::new( - Some(spi_clk_cfg), - Some(SPI_MODE), - Some(hw_cs_pin), - BLOCKMODE, - true, - false, - ); - spi.cfg_transfer(&transfer_cfg); - } + let hw_cs_pin = configure_pin_as_hw_cs_pin(pinsb.pb2); + let transfer_cfg = TransferConfig { + clk_cfg: Some(spi_clk_cfg), + mode: Some(SPI_MODE), + sod: false, + blockmode: BLOCKMODE, + bmstall: true, + hw_cs: Some(hw_cs_pin), + }; + spi.cfg_transfer(&transfer_cfg); } } // Application logic loop { let mut reply_buf: [u8; 8] = [0; 8]; - match SPI_BUS_SEL { - SpiBusSelect::SpiAPortA | SpiBusSelect::SpiAPortB => { - if let Some(ref mut spi) = *spia_ref.borrow_mut() { - // Can't really verify correct reply here. - spi.write(&[0x42]).expect("write failed"); - // Because of the loopback mode, we should get back the fill word here. - spi.read(&mut reply_buf[0..1]).unwrap(); - assert_eq!(reply_buf[0], FILL_WORD); - delay.delay_ms(500_u32); + // Can't really verify correct reply here. + spi.write(&[0x42]).expect("write failed"); + // Because of the loopback mode, we should get back the fill word here. + spi.read(&mut reply_buf[0..1]).unwrap(); + assert_eq!(reply_buf[0], FILL_WORD); + delay.delay_ms(500_u32); - let tx_buf: [u8; 3] = [0x01, 0x02, 0x03]; - spi.transfer(&mut reply_buf[0..3], &tx_buf).unwrap(); - assert_eq!(tx_buf, reply_buf[0..3]); - rprintln!( - "Received reply: {}, {}, {}", - reply_buf[0], - reply_buf[1], - reply_buf[2] - ); - delay.delay_ms(500_u32); + let tx_buf: [u8; 3] = [0x01, 0x02, 0x03]; + spi.transfer(&mut reply_buf[0..3], &tx_buf).unwrap(); + assert_eq!(tx_buf, reply_buf[0..3]); + defmt::info!( + "Received reply: {}, {}, {}", + reply_buf[0], + reply_buf[1], + reply_buf[2] + ); + delay.delay_ms(500_u32); - let mut tx_rx_buf: [u8; 3] = [0x03, 0x02, 0x01]; - spi.transfer_in_place(&mut tx_rx_buf).unwrap(); - rprintln!( - "Received reply: {}, {}, {}", - tx_rx_buf[0], - tx_rx_buf[1], - tx_rx_buf[2] - ); - assert_eq!(&tx_rx_buf[0..3], &[0x03, 0x02, 0x01]); - } - } - SpiBusSelect::SpiBPortB => { - if let Some(ref mut spi) = *spib_ref.borrow_mut() { - // Can't really verify correct reply here. - spi.write(&[0x42]).expect("write failed"); - // Because of the loopback mode, we should get back the fill word here. - spi.read(&mut reply_buf[0..1]).unwrap(); - assert_eq!(reply_buf[0], FILL_WORD); - delay.delay_ms(500_u32); - - let tx_buf: [u8; 3] = [0x01, 0x02, 0x03]; - spi.transfer(&mut reply_buf[0..3], &tx_buf).unwrap(); - assert_eq!(tx_buf, reply_buf[0..3]); - rprintln!( - "Received reply: {}, {}, {}", - reply_buf[0], - reply_buf[1], - reply_buf[2] - ); - delay.delay_ms(500_u32); - - let mut tx_rx_buf: [u8; 3] = [0x03, 0x02, 0x01]; - spi.transfer_in_place(&mut tx_rx_buf).unwrap(); - rprintln!( - "Received reply: {}, {}, {}", - tx_rx_buf[0], - tx_rx_buf[1], - tx_rx_buf[2] - ); - assert_eq!(&tx_rx_buf[0..3], &[0x03, 0x02, 0x01]); - } - } - } + let mut tx_rx_buf: [u8; 3] = [0x03, 0x02, 0x01]; + spi.transfer_in_place(&mut tx_rx_buf).unwrap(); + defmt::info!( + "Received reply: {}, {}, {}", + tx_rx_buf[0], + tx_rx_buf[1], + tx_rx_buf[2] + ); + assert_eq!(&tx_rx_buf[0..3], &[0x03, 0x02, 0x01]); } } - -#[interrupt] -#[allow(non_snake_case)] -fn OC0() { - default_ms_irq_handler() -} diff --git a/examples/simple/examples/timer-ticks.rs b/examples/simple/examples/timer-ticks.rs index 3b4b275..48c177e 100644 --- a/examples/simple/examples/timer-ticks.rs +++ b/examples/simple/examples/timer-ticks.rs @@ -2,19 +2,19 @@ #![no_main] #![no_std] -use core::cell::Cell; use cortex_m_rt::entry; -use critical_section::Mutex; -use panic_rtt_target as _; -use rtt_target::{rprintln, rtt_init_print}; +use embedded_hal::delay::DelayNs; +// Import panic provider. +use panic_probe as _; +// Import logger. +use defmt_rtt as _; +use portable_atomic::AtomicU32; use va108xx_hal::{ clock::{get_sys_clock, set_sys_clock}, pac::{self, interrupt}, prelude::*, time::Hertz, - timer::{ - default_ms_irq_handler, set_up_ms_tick, CountdownTimer, Event, InterruptConfig, MS_COUNTER, - }, + timer::{CountdownTimer, InterruptConfig}, }; #[allow(dead_code)] @@ -23,14 +23,15 @@ 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() -> ! { - rtt_init_print!(); - let mut dp = pac::Peripherals::take().unwrap(); + let dp = pac::Peripherals::take().unwrap(); + let mut delay = CountdownTimer::new(50.MHz(), dp.tim2); let mut last_ms = 0; - rprintln!("-- Vorago system ticks using timers --"); + defmt::info!("-- Vorago system ticks using timers --"); set_sys_clock(50.MHz()); let lib_type = LibType::Hal; match lib_type { @@ -66,34 +67,24 @@ fn main() -> ! { } } LibType::Hal => { - set_up_ms_tick( - InterruptConfig::new(interrupt::OC0, true, true), - &mut dp.sysconfig, - Some(&mut dp.irqsel), - 50.MHz(), - dp.tim0, - ); - let mut second_timer = - CountdownTimer::new(&mut dp.sysconfig, get_sys_clock().unwrap(), dp.tim1); - second_timer.listen( - Event::TimeOut, - InterruptConfig::new(interrupt::OC1, true, true), - Some(&mut dp.irqsel), - Some(&mut dp.sysconfig), - ); + let mut ms_timer = CountdownTimer::new(get_sys_clock().unwrap(), dp.tim0); + ms_timer.enable_interrupt(InterruptConfig::new(interrupt::OC0, true, true)); + ms_timer.start(1.kHz()); + let mut second_timer = CountdownTimer::new(get_sys_clock().unwrap(), dp.tim1); + second_timer.enable_interrupt(InterruptConfig::new(interrupt::OC1, true, 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(portable_atomic::Ordering::Relaxed); if current_ms - last_ms >= 1000 { // To prevent drift. last_ms += 1000; - rprintln!("MS counter: {}", current_ms); - let second = critical_section::with(|cs| SEC_COUNTER.borrow(cs).get()); - rprintln!("Second counter: {}", second); + defmt::info!("MS counter: {}", current_ms); + let second = SEC_COUNTER.load(portable_atomic::Ordering::Relaxed); + defmt::info!("Second counter: {}", second); } - cortex_m::asm::delay(10000); + delay.delay_ms(50); } } @@ -107,15 +98,11 @@ fn unmask_irqs() { #[interrupt] #[allow(non_snake_case)] fn OC0() { - default_ms_irq_handler() + MS_COUNTER.fetch_add(1, portable_atomic::Ordering::Relaxed); } #[interrupt] #[allow(non_snake_case)] fn OC1() { - 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, portable_atomic::Ordering::Relaxed); } diff --git a/examples/simple/examples/uart.rs b/examples/simple/examples/uart.rs index 5869a03..45e88d3 100644 --- a/examples/simple/examples/uart.rs +++ b/examples/simple/examples/uart.rs @@ -13,27 +13,23 @@ use cortex_m_rt::entry; use embedded_hal_nb::{nb, serial::Read}; use embedded_io::Write as _; -use panic_rtt_target as _; -use rtt_target::{rprintln, rtt_init_print}; -use va108xx_hal::{gpio::PinsA, pac, prelude::*, uart}; +// Import panic provider. +use panic_probe as _; +// Import logger. +use defmt_rtt as _; +use va108xx_hal::{pac, pins::PinsA, prelude::*, uart}; #[entry] fn main() -> ! { - rtt_init_print!(); - rprintln!("-- VA108xx UART example application--"); + defmt::println!("-- VA108xx UART example application--"); - let mut dp = pac::Peripherals::take().unwrap(); + let dp = pac::Peripherals::take().unwrap(); - let gpioa = PinsA::new(&mut dp.sysconfig, dp.porta); - let tx = gpioa.pa9.into_funsel_2(); - let rx = gpioa.pa8.into_funsel_2(); - let uart = uart::Uart::new_without_interrupt( - &mut dp.sysconfig, - 50.MHz(), - dp.uarta, - (tx, rx), - 115200.Hz(), - ); + let gpioa = PinsA::new(dp.porta); + let tx = gpioa.pa9; + let rx = gpioa.pa8; + let uart = uart::Uart::new_without_interrupt(50.MHz(), dp.uarta, (tx, rx), 115200.Hz().into()) + .unwrap(); let (mut tx, mut rx) = uart.split(); writeln!(tx, "Hello World\r").unwrap(); diff --git a/examples/simple/src/main.rs b/examples/simple/src/main.rs index 55f4c3c..25c46d4 100644 --- a/examples/simple/src/main.rs +++ b/examples/simple/src/main.rs @@ -3,7 +3,7 @@ #![no_std] use cortex_m_rt::entry; -use panic_rtt_target as _; +use panic_probe as _; use va108xx_hal as _; #[entry] diff --git a/flashloader/Cargo.toml b/flashloader/Cargo.toml index a979b11..2215825 100644 --- a/flashloader/Cargo.toml +++ b/flashloader/Cargo.toml @@ -29,6 +29,7 @@ rtic-sync = {version = "1", features = ["defmt-03"]} [dependencies.va108xx-hal] version = "0.11" +path = "../va108xx-hal" features = ["defmt"] [dependencies.vorago-reb1] diff --git a/flashloader/image-loader.py b/flashloader/image-loader.py index e260502..0c22c4e 100755 --- a/flashloader/image-loader.py +++ b/flashloader/image-loader.py @@ -2,16 +2,15 @@ from typing import List, Tuple from spacepackets.ecss.defs import PusService from spacepackets.ecss.tm import PusTm -from tmtccmd.com import ComInterface import toml import struct import logging import argparse import time import enum -from tmtccmd.com.serial_base import SerialCfg -from tmtccmd.com.serial_cobs import SerialCobsComIF -from tmtccmd.com.ser_utils import prompt_com_port +from com_interface import ComInterface +from com_interface.serial_base import SerialCfg +from com_interface.serial_cobs import SerialCobsComIF from crcmod.predefined import PredefinedCrc from spacepackets.ecss.tc import PusTc from spacepackets.ecss.pus_verificator import PusVerificator, StatusField @@ -101,15 +100,7 @@ class ImageLoader: ) self.verificator.add_tc(action_tc) self.com_if.send(bytes(action_tc.pack())) - data_available = self.com_if.data_available(0.4) - if not data_available: - _LOGGER.warning("no reply received for boot image selection command") - for reply in self.com_if.receive(): - result = self.verificator.add_tm( - Service1Tm.from_tm(PusTm.unpack(reply, 0), UnpackParams(0)) - ) - if result is not None and result.completed: - _LOGGER.info("received boot image selection command confirmation") + self.await_for_command_copletion("boot image selection command") def handle_ping_cmd(self): _LOGGER.info("Sending ping command") @@ -122,16 +113,26 @@ class ImageLoader: ) self.verificator.add_tc(ping_tc) self.com_if.send(bytes(ping_tc.pack())) + self.await_for_command_copletion("ping command") - data_available = self.com_if.data_available(0.4) - if not data_available: - _LOGGER.warning("no ping reply received") - for reply in self.com_if.receive(): - result = self.verificator.add_tm( - Service1Tm.from_tm(PusTm.unpack(reply, 0), UnpackParams(0)) - ) - if result is not None and result.completed: - _LOGGER.info("received ping completion reply") + def await_for_command_copletion(self, context: str): + done = False + now = time.time() + while time.time() - now < 2.0: + if not self.com_if.data_available(): + time.sleep(0.2) + continue + for reply in self.com_if.receive(): + result = self.verificator.add_tm( + Service1Tm.from_tm(PusTm.unpack(reply, 0), UnpackParams(0)) + ) + if result is not None and result.completed: + _LOGGER.info(f"received {context} reply") + done = True + if done: + break + if not done: + _LOGGER.warning(f"no {context} reply received") def handle_corruption_cmd(self, target: Target): if target == Target.BOOTLOADER: @@ -300,12 +301,12 @@ def main() -> int: if "serial_port" in parsed_toml: serial_port = parsed_toml["serial_port"] if serial_port is None: - serial_port = prompt_com_port() + serial_port = input("Please specify the serial port manually: ") serial_cfg = SerialCfg( com_if_id="ser_cobs", serial_port=serial_port, baud_rate=BAUD_RATE, - serial_timeout=0.1, + polling_frequency=0.1, ) verificator = PusVerificator() com_if = SerialCobsComIF(serial_cfg) diff --git a/flashloader/loader.toml b/flashloader/loader.toml index bfcf1ac..008f3de 100644 --- a/flashloader/loader.toml +++ b/flashloader/loader.toml @@ -1 +1 @@ -serial_port = "/dev/ttyUSB0" +serial_port = "/dev/ttyUSB1" diff --git a/flashloader/requirements.txt b/flashloader/requirements.txt index 5a2f67b..dbcb091 100644 --- a/flashloader/requirements.txt +++ b/flashloader/requirements.txt @@ -1,5 +1,5 @@ -spacepackets == 0.24 -tmtccmd == 8.0.2 +spacepackets == 0.28 +com-interface == 0.1 toml == 0.10 pyelftools == 0.31 crcmod == 1.7 diff --git a/flashloader/src/main.rs b/flashloader/src/main.rs index 4928bfa..641a612 100644 --- a/flashloader/src/main.rs +++ b/flashloader/src/main.rs @@ -68,7 +68,7 @@ mod app { use spacepackets::ecss::{ tc::PusTcReader, tm::PusTmCreator, EcssEnumU8, PusPacket, WritablePusPacket, }; - use va108xx_hal::gpio::PinsA; + use va108xx_hal::pins::PinsA; use va108xx_hal::uart::IrqContextTimeoutOrMaxSize; use va108xx_hal::{pac, uart, InterruptConfig}; use vorago_reb1::m95m01::M95M01; @@ -83,8 +83,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, verif_reporter: VerificationReportCreator, nvm: M95M01, @@ -108,18 +108,18 @@ mod app { let mut dp = cx.device; let nvm = M95M01::new(&mut dp.sysconfig, SYSCLK_FREQ, dp.spic); - let gpioa = PinsA::new(&mut dp.sysconfig, dp.porta); - let tx = gpioa.pa9.into_funsel_2(); - let rx = gpioa.pa8.into_funsel_2(); + let gpioa = PinsA::new(dp.porta); + let tx = gpioa.pa9; + let rx = gpioa.pa8; let irq_uart = uart::Uart::new_with_interrupt( - &mut dp.sysconfig, SYSCLK_FREQ, dp.uarta, (tx, rx), - UART_BAUDRATE.Hz(), + UART_BAUDRATE.Hz().into(), InterruptConfig::new(pac::Interrupt::OC0, true, true), - ); + ) + .unwrap(); let (tx, rx) = irq_uart.split(); // Unwrap is okay, we explicitely set the interrupt ID. let mut rx = rx.into_rx_with_irq(); @@ -451,7 +451,7 @@ mod app { cx.local.encoded_buf[send_size + 1] = 0; cx.local .uart_tx - .write(&cx.local.encoded_buf[0..send_size + 2]) + .write_all(&cx.local.encoded_buf[0..send_size + 2]) .unwrap(); occupied_len -= 1; Mono::delay(2.millis()).await; diff --git a/va108xx-embassy/Cargo.toml b/va108xx-embassy/Cargo.toml index e7520ba..e1ebb69 100644 --- a/va108xx-embassy/Cargo.toml +++ b/va108xx-embassy/Cargo.toml @@ -20,7 +20,7 @@ embassy-time-queue-utils = "0.1" once_cell = { version = "1", default-features = false, features = ["critical-section"] } -va108xx-hal = { version = ">=0.10, <=0.11" } +va108xx-hal = { version = ">=0.10, <=0.11", path = "../va108xx-hal" } [target.'cfg(all(target_arch = "arm", target_os = "none"))'.dependencies] portable-atomic = { version = "1", features = ["unsafe-assume-single-core"] } diff --git a/va108xx-embassy/src/lib.rs b/va108xx-embassy/src/lib.rs index dce3ef7..fe900b5 100644 --- a/va108xx-embassy/src/lib.rs +++ b/va108xx-embassy/src/lib.rs @@ -45,7 +45,11 @@ use va108xx_hal::{ clock::enable_peripheral_clock, enable_nvic_interrupt, pac, prelude::*, - timer::{enable_tim_clk, get_tim_raw, TimRegInterface, ValidTim}, + timer::{ + enable_tim_clk, + regs::{EnableControl, MmioTimer}, + TimId, TimMarker, + }, PeripheralSelect, }; @@ -109,48 +113,25 @@ pub fn time_driver() -> &'static TimerDriver { /// This should be used if the interrupt handler is provided by the library, which is the /// default case. #[cfg(feature = "irqs-in-lib")] -pub fn init( - syscfg: &mut pac::Sysconfig, - irqsel: &pac::Irqsel, - sysclk: impl Into, +pub fn init( + sysclk: Hertz, timekeeper_tim: TimekeeperTim, alarm_tim: AlarmTim, ) { - TIME_DRIVER.init( - syscfg, - irqsel, - sysclk, - timekeeper_tim, - alarm_tim, - TIMEKEEPER_IRQ, - ALARM_IRQ, - ) + TIME_DRIVER.init(sysclk, timekeeper_tim, alarm_tim, TIMEKEEPER_IRQ, ALARM_IRQ) } /// Initialization method for embassy when using custom IRQ handlers. /// /// Requires an explicit [pac::Interrupt] argument for the timekeeper and alarm IRQs. -pub fn init_with_custom_irqs< - TimekeeperTim: TimRegInterface + ValidTim, - AlarmTim: TimRegInterface + ValidTim, ->( - syscfg: &mut pac::Sysconfig, - irqsel: &pac::Irqsel, - sysclk: impl Into, +pub fn init_with_custom_irqs( + sysclk: Hertz, timekeeper_tim: TimekeeperTim, alarm_tim: AlarmTim, timekeeper_irq: pac::Interrupt, alarm_irq: pac::Interrupt, ) { - TIME_DRIVER.init( - syscfg, - irqsel, - sysclk, - timekeeper_tim, - alarm_tim, - timekeeper_irq, - alarm_irq, - ) + TIME_DRIVER.init(sysclk, timekeeper_tim, alarm_tim, timekeeper_irq, alarm_irq) } struct AlarmState { @@ -168,8 +149,8 @@ impl AlarmState { unsafe impl Send for AlarmState {} static SCALE: OnceCell = OnceCell::new(); -static TIMEKEEPER_TIM: OnceCell = OnceCell::new(); -static ALARM_TIM: OnceCell = OnceCell::new(); +static TIMEKEEPER_TIM: OnceCell = OnceCell::new(); +static ALARM_TIM: OnceCell = OnceCell::new(); pub struct TimerDriver { periods: AtomicU32, @@ -180,62 +161,56 @@ pub struct TimerDriver { impl TimerDriver { #[allow(clippy::too_many_arguments)] - fn init( + fn init( &self, - syscfg: &mut pac::Sysconfig, - irqsel: &pac::Irqsel, - sysclk: impl Into, - timekeeper_tim: TimekeeperTim, - alarm_tim: AlarmTim, + sysclk: Hertz, + _timekeeper_tim: TimekeeperTim, + _alarm_tim: AlarmTim, timekeeper_irq: pac::Interrupt, alarm_irq: pac::Interrupt, ) { if ALARM_TIM.get().is_some() || TIMEKEEPER_TIM.get().is_some() { return; } - ALARM_TIM.set(AlarmTim::TIM_ID).ok(); - TIMEKEEPER_TIM.set(TimekeeperTim::TIM_ID).ok(); - enable_peripheral_clock(syscfg, PeripheralSelect::Irqsel); - enable_tim_clk(syscfg, timekeeper_tim.tim_id()); - let timekeeper_reg_block = timekeeper_tim.reg_block(); - let alarm_tim_reg_block = alarm_tim.reg_block(); - let sysclk = sysclk.into(); + ALARM_TIM.set(AlarmTim::ID).ok(); + TIMEKEEPER_TIM.set(TimekeeperTim::ID).ok(); + enable_peripheral_clock(PeripheralSelect::Irqsel); + enable_tim_clk(TimekeeperTim::ID); + let mut timekeeper_reg_block = unsafe { TimekeeperTim::ID.steal_regs() }; + let mut alarm_tim_reg_block = unsafe { AlarmTim::ID.steal_regs() }; // Initiate scale value here. This is required to convert timer ticks back to a timestamp. SCALE.set((sysclk.raw() / TICK_HZ as u32) as u64).unwrap(); - timekeeper_reg_block - .rst_value() - .write(|w| unsafe { w.bits(u32::MAX) }); + timekeeper_reg_block.write_reset_value(u32::MAX); // Decrementing counter. - timekeeper_reg_block - .cnt_value() - .write(|w| unsafe { w.bits(u32::MAX) }); + timekeeper_reg_block.write_count_value(u32::MAX); + let irqsel = unsafe { va108xx_hal::pac::Irqsel::steal() }; // Switch on. Timekeeping should always be done. irqsel - .tim0(timekeeper_tim.tim_id() as usize) + .tim0(TimekeeperTim::ID.value() as usize) .write(|w| unsafe { w.bits(timekeeper_irq as u32) }); unsafe { enable_nvic_interrupt(timekeeper_irq); } - timekeeper_reg_block - .ctrl() - .modify(|_, w| w.irq_enb().set_bit()); - timekeeper_reg_block - .enable() - .write(|w| unsafe { w.bits(1) }); + timekeeper_reg_block.modify_control(|mut value| { + value.set_irq_enable(true); + value + }); + timekeeper_reg_block.write_enable_control(EnableControl::new_enable()); - enable_tim_clk(syscfg, alarm_tim.tim_id()); + enable_tim_clk(AlarmTim::ID); // Explicitely disable alarm timer until needed. - alarm_tim_reg_block.ctrl().modify(|_, w| { - w.irq_enb().clear_bit(); - w.enable().clear_bit() + alarm_tim_reg_block.modify_control(|mut value| { + value.set_irq_enable(false); + value.set_enable(false); + value }); // Enable general interrupts. The IRQ enable of the peripheral remains cleared. unsafe { enable_nvic_interrupt(alarm_irq); } irqsel - .tim0(alarm_tim.tim_id() as usize) + .tim0(AlarmTim::ID.value() as usize) .write(|w| unsafe { w.bits(alarm_irq as u32) }); } @@ -261,16 +236,16 @@ impl TimerDriver { }) } - fn timekeeper_tim() -> &'static pac::tim0::RegisterBlock { + fn timekeeper_tim() -> MmioTimer<'static> { TIMEKEEPER_TIM .get() - .map(|idx| unsafe { get_tim_raw(*idx as usize) }) + .map(|id| unsafe { id.steal_regs() }) .unwrap() } - fn alarm_tim() -> &'static pac::tim0::RegisterBlock { + fn alarm_tim() -> MmioTimer<'static> { ALARM_TIM .get() - .map(|idx| unsafe { get_tim_raw(*idx as usize) }) + .map(|id| unsafe { id.steal_regs() }) .unwrap() } @@ -283,25 +258,27 @@ impl TimerDriver { if at < t { self.trigger_alarm(cs); } else { - let alarm_tim = Self::alarm_tim(); + let mut alarm_tim = Self::alarm_tim(); let remaining_ticks = (at - t).checked_mul(*SCALE.get().unwrap()); if remaining_ticks.is_some_and(|v| v <= u32::MAX as u64) { - alarm_tim.enable().write(|w| unsafe { w.bits(0) }); - alarm_tim - .cnt_value() - .write(|w| unsafe { w.bits(remaining_ticks.unwrap() as u32) }); - alarm_tim.ctrl().modify(|_, w| w.irq_enb().set_bit()); - alarm_tim.enable().write(|w| unsafe { w.bits(1) }); + alarm_tim.write_enable_control(EnableControl::new_disable()); + alarm_tim.write_count_value(remaining_ticks.unwrap() as u32); + alarm_tim.modify_control(|mut value| { + value.set_irq_enable(true); + value + }); + alarm_tim.write_enable_control(EnableControl::new_enable()); } } }) } fn trigger_alarm(&self, cs: CriticalSection) { - Self::alarm_tim().ctrl().modify(|_, w| { - w.irq_enb().clear_bit(); - w.enable().clear_bit() + Self::alarm_tim().modify_control(|mut value| { + value.set_irq_enable(false); + value.set_enable(false); + value }); let alarm = &self.alarms.borrow(cs); @@ -327,10 +304,11 @@ impl TimerDriver { 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 mut alarm_tim = Self::alarm_tim(); + alarm_tim.modify_control(|mut value| { + value.set_irq_enable(false); + value.set_enable(false); + value }); let alarm = self.alarms.borrow(cs); @@ -354,13 +332,14 @@ impl TimerDriver { // 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) }); + alarm_tim.write_reset_value(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) }); + alarm_tim.write_count_value(timer_ticks.unwrap() as u32); + alarm_tim.modify_control(|mut value| { + value.set_irq_enable(true); + value.set_enable(true); + value + }); } // If it's too far in the future, don't enable timer yet. // It will be enabled later by `next_period`. @@ -383,7 +362,7 @@ impl Driver for TimerDriver { // 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(); + counter_val = u32::MAX - Self::timekeeper_tim().read_count_value(); // Double read to protect against race conditions when the counter is overflowing. period2 = self.periods.load(Ordering::Relaxed); diff --git a/va108xx-hal/Cargo.toml b/va108xx-hal/Cargo.toml index 82449b9..d5943a0 100644 --- a/va108xx-hal/Cargo.toml +++ b/va108xx-hal/Cargo.toml @@ -15,6 +15,7 @@ cortex-m = { version = "0.7", features = ["critical-section-single-core"]} cortex-m-rt = "0.7" nb = "1" paste = "1" +vorago-shared-periphs = { git = "https://egit.irs.uni-stuttgart.de/rust/vorago-shared-periphs.git", features = ["vor1x"] } embedded-hal = "1" embedded-hal-async = "1" embedded-hal-nb = "1" @@ -42,7 +43,7 @@ portable-atomic = "1" [features] default = ["rt"] rt = ["va108xx/rt"] -defmt = ["dep:defmt", "fugit/defmt", "embedded-hal/defmt-03"] +defmt = ["dep:defmt", "fugit/defmt", "embedded-hal/defmt-03", "vorago-shared-periphs/defmt"] [package.metadata.docs.rs] all-features = true diff --git a/va108xx-hal/src/clock.rs b/va108xx-hal/src/clock.rs index 3383145..6e9970a 100644 --- a/va108xx-hal/src/clock.rs +++ b/va108xx-hal/src/clock.rs @@ -2,27 +2,14 @@ //! //! This also includes functionality to enable the peripheral clocks use crate::time::Hertz; -use crate::PeripheralSelect; use cortex_m::interrupt::{self, Mutex}; use once_cell::unsync::OnceCell; +pub use vorago_shared_periphs::gpio::FilterClkSel; +pub use vorago_shared_periphs::sysconfig::{disable_peripheral_clock, enable_peripheral_clock}; + static SYS_CLOCK: Mutex> = Mutex::new(OnceCell::new()); -pub type PeripheralClocks = PeripheralSelect; - -#[derive(Debug, PartialEq, Eq)] -#[cfg_attr(feature = "defmt", derive(defmt::Format))] -pub enum FilterClkSel { - SysClk = 0, - Clk1 = 1, - Clk2 = 2, - Clk3 = 3, - Clk4 = 4, - Clk5 = 5, - Clk6 = 6, - Clk7 = 7, -} - /// The Vorago in powered by an external clock which might have different frequencies. /// The clock can be set here so it can be used by other software components as well. /// The clock can be set exactly once @@ -63,17 +50,3 @@ pub fn set_clk_div_register(syscfg: &mut va108xx::Sysconfig, clk_sel: FilterClkS } } } - -#[inline] -pub fn enable_peripheral_clock(syscfg: &mut va108xx::Sysconfig, clock: PeripheralClocks) { - syscfg - .peripheral_clk_enable() - .modify(|r, w| unsafe { w.bits(r.bits() | (1 << clock as u8)) }); -} - -#[inline] -pub fn disable_peripheral_clock(syscfg: &mut va108xx::Sysconfig, clock: PeripheralClocks) { - syscfg - .peripheral_clk_enable() - .modify(|r, w| unsafe { w.bits(r.bits() & !(1 << clock as u8)) }); -} diff --git a/va108xx-hal/src/gpio/asynch.rs b/va108xx-hal/src/gpio/asynch.rs deleted file mode 100644 index 36c4ab8..0000000 --- a/va108xx-hal/src/gpio/asynch.rs +++ /dev/null @@ -1,366 +0,0 @@ -//! # Async GPIO functionality for the VA108xx family. -//! -//! This module provides the [InputPinAsync] and [InputDynPinAsync] which both implement -//! the [embedded_hal_async::digital::Wait] trait. These types allow for asynchronous waiting -//! on GPIO pins. Please note that this module does not specify/declare the interrupt handlers -//! which must be provided for async support to work. However, it provides the -//! [on_interrupt_for_async_gpio_for_port] generic interrupt handler. This should be called in all -//! IRQ functions which handle any GPIO interrupts with the corresponding [Port] argument. -//! -//! # Example -//! -//! - [Async GPIO example](https://egit.irs.uni-stuttgart.de/rust/va108xx-rs/src/branch/main/examples/embassy/src/bin/async-gpio.rs) -use core::future::Future; - -use embassy_sync::waitqueue::AtomicWaker; -use embedded_hal_async::digital::Wait; -use portable_atomic::AtomicBool; -use va108xx::{self as pac}; - -use crate::InterruptConfig; - -use super::{ - pin, DynPin, DynPinId, InputConfig, InterruptEdge, InvalidPinTypeError, Pin, PinId, Port, - NUM_PINS_PORT_A, NUM_PINS_PORT_B, -}; - -static WAKERS_FOR_PORT_A: [AtomicWaker; NUM_PINS_PORT_A] = - [const { AtomicWaker::new() }; NUM_PINS_PORT_A]; -static WAKERS_FOR_PORT_B: [AtomicWaker; NUM_PINS_PORT_B] = - [const { AtomicWaker::new() }; NUM_PINS_PORT_B]; -static EDGE_DETECTION_PORT_A: [AtomicBool; NUM_PINS_PORT_A] = - [const { AtomicBool::new(false) }; NUM_PINS_PORT_A]; -static EDGE_DETECTION_PORT_B: [AtomicBool; NUM_PINS_PORT_B] = - [const { AtomicBool::new(false) }; NUM_PINS_PORT_B]; - -/// Generic interrupt handler for GPIO interrupts on a specific port to support async functionalities -/// -/// This function should be called in all interrupt handlers which handle any GPIO interrupts -/// matching the [Port] argument. -/// The handler will wake the corresponding wakers for the pins that triggered an interrupts -/// as well as update the static edge detection structures. This allows the pin future tocomplete -/// complete async operations. -pub fn on_interrupt_for_async_gpio_for_port(port: Port) { - let periphs = unsafe { pac::Peripherals::steal() }; - - let (irq_enb, edge_status, wakers, edge_detection) = match port { - Port::A => ( - periphs.porta.irq_enb().read().bits(), - periphs.porta.edge_status().read().bits(), - WAKERS_FOR_PORT_A.as_ref(), - EDGE_DETECTION_PORT_A.as_ref(), - ), - Port::B => ( - periphs.portb.irq_enb().read().bits(), - periphs.portb.edge_status().read().bits(), - WAKERS_FOR_PORT_B.as_ref(), - EDGE_DETECTION_PORT_B.as_ref(), - ), - }; - - on_interrupt_for_port(irq_enb, edge_status, wakers, edge_detection); -} - -#[inline] -fn on_interrupt_for_port( - mut irq_enb: u32, - edge_status: u32, - wakers: &'static [AtomicWaker], - edge_detection: &'static [AtomicBool], -) { - while irq_enb != 0 { - let bit_pos = irq_enb.trailing_zeros() as usize; - let bit_mask = 1 << bit_pos; - - wakers[bit_pos].wake(); - - if edge_status & bit_mask != 0 { - edge_detection[bit_pos].store(true, core::sync::atomic::Ordering::Relaxed); - - // Clear the processed bit - irq_enb &= !bit_mask; - } - } -} - -/// Input pin future which implements the [Future] trait. -/// -/// Generally, you want to use the [InputPinAsync] or [InputDynPinAsync] types instead of this -/// which also implements the [embedded_hal_async::digital::Wait] trait. However, access to this -/// struture is granted to allow writing custom async structures. -pub struct InputPinFuture { - pin_id: DynPinId, - waker_group: &'static [AtomicWaker], - edge_detection_group: &'static [AtomicBool], -} - -impl InputPinFuture { - #[inline] - pub fn pin_group_to_waker_and_edge_detection_group( - group: Port, - ) -> (&'static [AtomicWaker], &'static [AtomicBool]) { - match group { - Port::A => (WAKERS_FOR_PORT_A.as_ref(), EDGE_DETECTION_PORT_A.as_ref()), - Port::B => (WAKERS_FOR_PORT_B.as_ref(), EDGE_DETECTION_PORT_B.as_ref()), - } - } - - pub fn new_with_dyn_pin( - pin: &mut DynPin, - irq: pac::Interrupt, - edge: InterruptEdge, - ) -> Result { - if !pin.is_input_pin() { - return Err(InvalidPinTypeError(pin.mode())); - } - - let (waker_group, edge_detection_group) = - Self::pin_group_to_waker_and_edge_detection_group(pin.id().port()); - edge_detection_group[pin.id().num() as usize] - .store(false, core::sync::atomic::Ordering::Relaxed); - pin.configure_edge_interrupt(edge).unwrap(); - pin.enable_interrupt(InterruptConfig::new(irq, true, true)); - Ok(Self { - pin_id: pin.id(), - waker_group, - edge_detection_group, - }) - } - - pub fn new_with_pin( - pin: &mut Pin>, - irq: pac::Interrupt, - edge: InterruptEdge, - ) -> Self { - let (waker_group, edge_detection_group) = - Self::pin_group_to_waker_and_edge_detection_group(pin.id().port()); - edge_detection_group[pin.id().num() as usize] - .store(false, core::sync::atomic::Ordering::Relaxed); - pin.configure_edge_interrupt(edge); - pin.enable_interrupt(InterruptConfig::new(irq, true, true)); - Self { - pin_id: pin.id(), - edge_detection_group, - waker_group, - } - } -} - -impl Drop for InputPinFuture { - fn drop(&mut self) { - // The API ensures that we actually own the pin, so stealing it here is okay. - unsafe { DynPin::steal(self.pin_id) }.disable_interrupt(false); - } -} - -impl Future for InputPinFuture { - type Output = (); - fn poll( - self: core::pin::Pin<&mut Self>, - cx: &mut core::task::Context<'_>, - ) -> core::task::Poll { - let idx = self.pin_id.num() as usize; - self.waker_group[idx].register(cx.waker()); - if self.edge_detection_group[idx].swap(false, core::sync::atomic::Ordering::Relaxed) { - return core::task::Poll::Ready(()); - } - core::task::Poll::Pending - } -} - -pub struct InputDynPinAsync { - pin: DynPin, - irq: pac::Interrupt, -} - -impl InputDynPinAsync { - /// Create a new asynchronous input pin from a [DynPin]. The interrupt ID to be used must be - /// passed as well and is used to route and enable the interrupt. - /// - /// Please note that the interrupt handler itself must be provided by the user and the - /// generic [on_interrupt_for_async_gpio_for_port] function must be called inside that function - /// for the asynchronous functionality to work. - pub fn new(pin: DynPin, irq: pac::Interrupt) -> Result { - if !pin.is_input_pin() { - return Err(InvalidPinTypeError(pin.mode())); - } - Ok(Self { pin, irq }) - } - - /// Asynchronously wait until the pin is high. - /// - /// This returns immediately if the pin is already high. - pub async fn wait_for_high(&mut self) { - // Unwrap okay, checked pin in constructor. - let fut = - InputPinFuture::new_with_dyn_pin(&mut self.pin, self.irq, InterruptEdge::LowToHigh) - .unwrap(); - if self.pin.is_high().unwrap() { - return; - } - fut.await; - } - - /// Asynchronously wait until the pin is low. - /// - /// This returns immediately if the pin is already high. - pub async fn wait_for_low(&mut self) { - // Unwrap okay, checked pin in constructor. - let fut = - InputPinFuture::new_with_dyn_pin(&mut self.pin, self.irq, InterruptEdge::HighToLow) - .unwrap(); - if self.pin.is_low().unwrap() { - return; - } - fut.await; - } - - /// Asynchronously wait until the pin sees a falling edge. - pub async fn wait_for_falling_edge(&mut self) { - // Unwrap okay, checked pin in constructor. - InputPinFuture::new_with_dyn_pin(&mut self.pin, self.irq, InterruptEdge::HighToLow) - .unwrap() - .await; - } - - /// Asynchronously wait until the pin sees a rising edge. - pub async fn wait_for_rising_edge(&mut self) { - // Unwrap okay, checked pin in constructor. - InputPinFuture::new_with_dyn_pin(&mut self.pin, self.irq, InterruptEdge::LowToHigh) - .unwrap() - .await; - } - - /// Asynchronously wait until the pin sees any edge (either rising or falling). - pub async fn wait_for_any_edge(&mut self) { - // Unwrap okay, checked pin in constructor. - InputPinFuture::new_with_dyn_pin(&mut self.pin, self.irq, InterruptEdge::BothEdges) - .unwrap() - .await; - } - - pub fn release(self) -> DynPin { - self.pin - } -} - -impl embedded_hal::digital::ErrorType for InputDynPinAsync { - type Error = core::convert::Infallible; -} - -impl Wait for InputDynPinAsync { - async fn wait_for_high(&mut self) -> Result<(), Self::Error> { - self.wait_for_high().await; - Ok(()) - } - - async fn wait_for_low(&mut self) -> Result<(), Self::Error> { - self.wait_for_low().await; - Ok(()) - } - - async fn wait_for_rising_edge(&mut self) -> Result<(), Self::Error> { - self.wait_for_rising_edge().await; - Ok(()) - } - - async fn wait_for_falling_edge(&mut self) -> Result<(), Self::Error> { - self.wait_for_falling_edge().await; - Ok(()) - } - - async fn wait_for_any_edge(&mut self) -> Result<(), Self::Error> { - self.wait_for_any_edge().await; - Ok(()) - } -} - -pub struct InputPinAsync { - pin: Pin>, - irq: pac::Interrupt, -} - -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>, irq: pac::Interrupt) -> Self { - Self { pin, irq } - } - - /// Asynchronously wait until the pin is high. - /// - /// This returns immediately if the pin is already high. - pub async fn wait_for_high(&mut self) { - let fut = InputPinFuture::new_with_pin(&mut self.pin, self.irq, InterruptEdge::LowToHigh); - if self.pin.is_high() { - return; - } - fut.await; - } - - /// Asynchronously wait until the pin is low. - /// - /// This returns immediately if the pin is already high. - pub async fn wait_for_low(&mut self) { - let fut = InputPinFuture::new_with_pin(&mut self.pin, self.irq, InterruptEdge::HighToLow); - if self.pin.is_low() { - return; - } - fut.await; - } - - /// Asynchronously wait until the pin sees falling edge. - pub async fn wait_for_falling_edge(&mut self) { - // Unwrap okay, checked pin in constructor. - InputPinFuture::new_with_pin(&mut self.pin, self.irq, InterruptEdge::HighToLow).await; - } - - /// Asynchronously wait until the pin sees rising edge. - pub async fn wait_for_rising_edge(&mut self) { - // Unwrap okay, checked pin in constructor. - InputPinFuture::new_with_pin(&mut self.pin, self.irq, InterruptEdge::LowToHigh).await; - } - - /// Asynchronously wait until the pin sees any edge (either rising or falling). - pub async fn wait_for_any_edge(&mut self) { - InputPinFuture::new_with_pin(&mut self.pin, self.irq, InterruptEdge::BothEdges).await; - } - - pub fn release(self) -> Pin> { - 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/va108xx-hal/src/gpio/dynpin.rs b/va108xx-hal/src/gpio/dynpin.rs deleted file mode 100644 index 993150c..0000000 --- a/va108xx-hal/src/gpio/dynpin.rs +++ /dev/null @@ -1,947 +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 super::{ - pin::{FilterType, Pin, PinId, PinMode}, - InputDynPinAsync, InterruptEdge, InterruptLevel, IsMaskedError, PinState, Port, -}; -use crate::{clock::FilterClkSel, enable_nvic_interrupt, pac, FunSel}; - -//================================================================================================== -// DynPinMode configurations -//================================================================================================== - -/// Value-level `enum` for disabled configurations -#[derive(PartialEq, Eq, Clone, Copy)] -#[cfg_attr(feature = "defmt", derive(defmt::Format))] -pub enum DynDisabled { - Floating, - PullDown, - PullUp, -} - -/// Value-level `enum` for input configurations -#[derive(Debug, PartialEq, Eq, Clone, Copy)] -#[cfg_attr(feature = "defmt", derive(defmt::Format))] -pub enum DynInput { - Floating, - PullDown, - PullUp, -} - -/// Value-level `enum` for output configurations -#[derive(Debug, PartialEq, Eq, Clone, Copy)] -#[cfg_attr(feature = "defmt", derive(defmt::Format))] -pub enum DynOutput { - PushPull, - OpenDrain, - ReadablePushPull, - ReadableOpenDrain, -} - -pub type DynAlternate = FunSel; - -//============================================================================== -// Error -//============================================================================== - -/// GPIO error type -/// -/// [`DynPin`]s are not tracked and verified at compile-time, so run-time -/// operations are fallible. This `enum` represents the corresponding errors. -#[derive(Debug, PartialEq, Eq, thiserror::Error)] -#[cfg_attr(feature = "defmt", derive(defmt::Format))] -#[error("Invalid pin type for operation: {0:?}")] -pub struct InvalidPinTypeError(pub DynPinMode); - -impl embedded_hal::digital::Error for InvalidPinTypeError { - fn kind(&self) -> embedded_hal::digital::ErrorKind { - embedded_hal::digital::ErrorKind::Other - } -} - -//================================================================================================== -// DynPinMode -//================================================================================================== - -/// Value-level `enum` representing pin modes -#[derive(Debug, PartialEq, Eq, Clone, Copy)] -#[cfg_attr(feature = "defmt", derive(defmt::Format))] -pub enum DynPinMode { - Input(DynInput), - Output(DynOutput), - Alternate(DynAlternate), -} - -/// Value-level variant of [`DynPinMode`] for floating input mode -pub const DYN_FLOATING_INPUT: DynPinMode = DynPinMode::Input(DynInput::Floating); -/// Value-level variant of [`DynPinMode`] for pull-down input mode -pub const DYN_PULL_DOWN_INPUT: DynPinMode = DynPinMode::Input(DynInput::PullDown); -/// Value-level variant of [`DynPinMode`] for pull-up input mode -pub const DYN_PULL_UP_INPUT: DynPinMode = DynPinMode::Input(DynInput::PullUp); - -/// Value-level variant of [`DynPinMode`] for push-pull output mode -pub const DYN_PUSH_PULL_OUTPUT: DynPinMode = DynPinMode::Output(DynOutput::PushPull); -/// Value-level variant of [`DynPinMode`] for open-drain output mode -pub const DYN_OPEN_DRAIN_OUTPUT: DynPinMode = DynPinMode::Output(DynOutput::OpenDrain); -/// Value-level variant of [`DynPinMode`] for readable push-pull output mode -pub const DYN_RD_PUSH_PULL_OUTPUT: DynPinMode = DynPinMode::Output(DynOutput::ReadablePushPull); -/// Value-level variant of [`DynPinMode`] for readable opendrain output mode -pub const DYN_RD_OPEN_DRAIN_OUTPUT: DynPinMode = DynPinMode::Output(DynOutput::ReadableOpenDrain); - -/// Value-level variant of [`DynPinMode`] for function select 1 -pub const DYN_ALT_FUNC_1: DynPinMode = DynPinMode::Alternate(DynAlternate::Sel1); -/// Value-level variant of [`DynPinMode`] for function select 2 -pub const DYN_ALT_FUNC_2: DynPinMode = DynPinMode::Alternate(DynAlternate::Sel2); -/// Value-level variant of [`DynPinMode`] for function select 3 -pub const DYN_ALT_FUNC_3: DynPinMode = DynPinMode::Alternate(DynAlternate::Sel3); - -//================================================================================================== -// DynGroup & DynPinId -//================================================================================================== - -pub type DynGroup = Port; - -#[derive(Debug, PartialEq, Eq, Clone, Copy)] -#[cfg_attr(feature = "defmt", derive(defmt::Format))] -pub struct DynPinId { - port: Port, - num: u8, -} - -impl DynPinId { - pub const fn new(port: Port, num: u8) -> Self { - DynPinId { port, num } - } - - pub const fn port(&self) -> Port { - self.port - } - pub const fn num(&self) -> u8 { - self.num - } -} - -//================================================================================================== -// ModeFields -//================================================================================================== - -/// Collect all fields needed to set the [`PinMode`](super::PinMode) -#[derive(Default)] -struct ModeFields { - dir: bool, - opendrn: bool, - pull_en: bool, - /// true for pullup, false for pulldown - pull_dir: bool, - funsel: u8, - enb_input: bool, -} - -impl From for ModeFields { - #[inline] - fn from(mode: DynPinMode) -> Self { - let mut fields = Self::default(); - match mode { - DynPinMode::Input(config) => { - fields.dir = false; - fields.funsel = FunSel::Sel0 as u8; - match config { - DynInput::Floating => (), - DynInput::PullUp => { - fields.pull_en = true; - fields.pull_dir = true; - } - DynInput::PullDown => { - fields.pull_en = true; - } - } - } - DynPinMode::Output(config) => { - fields.dir = true; - fields.funsel = FunSel::Sel0 as u8; - match config { - DynOutput::PushPull => (), - DynOutput::OpenDrain => { - fields.opendrn = true; - } - DynOutput::ReadableOpenDrain => { - fields.enb_input = true; - fields.opendrn = true; - } - DynOutput::ReadablePushPull => { - fields.enb_input = true; - } - } - } - DynPinMode::Alternate(config) => { - fields.funsel = config as u8; - } - } - fields - } -} - -/// Type definition to avoid confusion: These register blocks are identical -type PortRegisterBlock = pac::porta::RegisterBlock; -pub type PortReg = pac::ioconfig::Porta; - -//================================================================================================== -// DynPin -//================================================================================================== - -/// A value-level pin, parameterized by [`DynPinId`] and [`DynPinMode`] -/// -/// This type acts as a type-erased version of [`Pin`]. Every pin is represented -/// by the same type, and pins are tracked and distinguished at run-time. -#[derive(Debug)] -pub struct DynPin { - id: DynPinId, - mode: DynPinMode, -} - -impl DynPin { - /// Create a new [DynPin] - /// - /// # Safety - /// - /// Each [DynPin] must be a singleton. For a given [DynPinId], there - /// must be at most one corresponding [`DynPin`] in existence at any given - /// time. Violating this requirement is `unsafe`. - #[inline] - pub(crate) const unsafe fn new(id: DynPinId, mode: DynPinMode) -> Self { - DynPin { id, mode } - } - - /// Steals a new [DynPin]. - /// - /// This function will simply set the internal mode to [DYN_FLOATING_INPUT] pin without - /// modifying any registers related to the behaviour of the pin. The user should call - /// [Self::into_mode] to ensure the correct mode of the pin. - /// - /// # Safety - /// - /// Circumvents the HAL's safety guarantees. The caller must ensure that the pin is not - /// used cocurrently somewhere else. The caller might also want to call [Self::into_mode] - /// to ensure the correct desired state of the pin. It is recommended to create the pin using - /// [Pin::downgrade] instead. - pub const unsafe fn steal(id: DynPinId) -> Self { - DynPin { - id, - mode: DYN_FLOATING_INPUT, - } - } - - /// Return a copy of the pin ID - #[inline] - pub const fn id(&self) -> DynPinId { - self.id - } - - /// Return a copy of the pin mode - #[inline] - pub const fn mode(&self) -> DynPinMode { - self.mode - } - - /// Convert the pin to the requested [`DynPinMode`] - #[inline] - pub fn into_mode(&mut self, mode: DynPinMode) { - self.change_mode(mode); - self.mode = mode; - } - - #[inline] - pub fn is_input_pin(&self) -> bool { - matches!(self.mode, DynPinMode::Input(_)) - } - - #[inline] - pub fn is_output_pin(&self) -> bool { - matches!(self.mode, DynPinMode::Output(_)) - } - - #[inline] - pub fn into_funsel_1(&mut self) { - self.into_mode(DYN_ALT_FUNC_1); - } - - #[inline] - pub fn into_funsel_2(&mut self) { - self.into_mode(DYN_ALT_FUNC_2); - } - - #[inline] - pub fn into_funsel_3(&mut self) { - self.into_mode(DYN_ALT_FUNC_3); - } - - /// Configure the pin to operate as a floating input - #[inline] - pub fn into_floating_input(&mut self) { - self.into_mode(DYN_FLOATING_INPUT); - } - - /// Configure the pin to operate as a pulled down input - #[inline] - pub fn into_pull_down_input(&mut self) { - self.into_mode(DYN_PULL_DOWN_INPUT); - } - - /// Configure the pin to operate as a pulled up input - #[inline] - pub fn into_pull_up_input(&mut self) { - self.into_mode(DYN_PULL_UP_INPUT); - } - - /// Configure the pin to operate as a push-pull output - #[inline] - pub fn into_push_pull_output(&mut self) { - self.into_mode(DYN_PUSH_PULL_OUTPUT); - } - - /// Configure the pin to operate as a push-pull output - #[inline] - pub fn into_open_drain_output(&mut self) { - self.into_mode(DYN_OPEN_DRAIN_OUTPUT); - } - - /// Configure the pin to operate as a push-pull output - #[inline] - pub fn into_readable_push_pull_output(&mut self) { - self.into_mode(DYN_RD_PUSH_PULL_OUTPUT); - } - - /// Configure the pin to operate as a push-pull output - #[inline] - pub fn into_readable_open_drain_output(&mut self) { - self.into_mode(DYN_RD_OPEN_DRAIN_OUTPUT); - } - - #[inline(always)] - pub fn is_low(&self) -> Result { - 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(()) - } - - pub fn enable_interrupt(&mut self, irq_cfg: crate::InterruptConfig) { - if irq_cfg.route { - self.configure_irqsel(irq_cfg.id); - } - if irq_cfg.enable_in_nvic { - unsafe { enable_nvic_interrupt(irq_cfg.id) }; - } - - // We only manipulate our own bit. - self.port_reg() - .irq_enb() - .modify(|r, w| unsafe { w.bits(r.bits() | self.mask_32()) }); - } - - pub fn disable_interrupt(&mut self, reset_irqsel: bool) { - if reset_irqsel { - self.reset_irqsel(); - } - // We only manipulate our own bit. - self.port_reg() - .irq_enb() - .modify(|r, w| unsafe { w.bits(r.bits() & !self.mask_32()) }); - } - - /// Try to recreate a type-level [`Pin`] from a value-level [`DynPin`] - /// - /// There is no way for the compiler to know if the conversion will be - /// successful at compile-time. We must verify the conversion at run-time - /// or refuse to perform it. - #[inline] - pub fn upgrade(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, - irq: crate::pac::Interrupt, - ) -> Result { - InputDynPinAsync::new(self, irq) - } - - /// Configure the IRQSEL peripheral for this particular pin with the given interrupt ID. - pub fn configure_irqsel(&mut self, id: pac::Interrupt) { - let mut syscfg = unsafe { pac::Sysconfig::steal() }; - let irqsel = unsafe { pac::Irqsel::steal() }; - crate::clock::enable_peripheral_clock(&mut syscfg, crate::clock::PeripheralClocks::Irqsel); - match self.id().port() { - // Set the correct interrupt number in the IRQSEL register - super::Port::A => { - irqsel - .porta0(self.id().num() as usize) - .write(|w| unsafe { w.bits(id as u32) }); - } - super::Port::B => { - irqsel - .portb0(self.id().num as usize) - .write(|w| unsafe { w.bits(id as u32) }); - } - } - } - - /// Reset the IRQSEL peripheral value for this particular pin. - pub fn reset_irqsel(&mut self) { - let mut syscfg = unsafe { pac::Sysconfig::steal() }; - let irqsel = unsafe { pac::Irqsel::steal() }; - crate::clock::enable_peripheral_clock(&mut syscfg, crate::clock::PeripheralClocks::Irqsel); - match self.id().port() { - // Set the correct interrupt number in the IRQSEL register - super::Port::A => { - irqsel - .porta0(self.id().num() as usize) - .write(|w| unsafe { w.bits(u32::MAX) }); - } - super::Port::B => { - irqsel - .portb0(self.id().num as usize) - .write(|w| unsafe { w.bits(u32::MAX) }); - } - } - } - - // Get DATAMASK bit for this particular pin - #[inline(always)] - pub fn datamask(&self) -> bool { - (self.port_reg().datamask().read().bits() >> self.id().num) == 1 - } - - /// Clear DATAMASK bit for this particular pin. This prevents access - /// of the corresponding bit for output and input operations - #[inline(always)] - pub fn clear_datamask(&self) { - self.port_reg() - .datamask() - .modify(|r, w| unsafe { w.bits(r.bits() & !self.mask_32()) }); - } - - /// Set DATAMASK bit for this particular pin. 1 is the default - /// state of the bit and allows access of the corresponding bit - #[inline(always)] - pub fn set_datamask(&self) { - self.port_reg() - .datamask() - .modify(|r, w| unsafe { w.bits(r.bits() | self.mask_32()) }); - } - - #[inline] - pub fn is_high_masked(&self) -> Result { - 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) - } - - /// Possible delays in clock cycles: - /// - Delay 1: 1 - /// - Delay 2: 2 - /// - Delay 1 + Delay 2: 3 - #[inline] - pub fn configure_delay( - &mut self, - delay_1: bool, - delay_2: bool, - ) -> Result<(), InvalidPinTypeError> { - match self.mode { - DynPinMode::Output(_) => { - self.configure_delay_internal(delay_1, delay_2); - Ok(()) - } - _ => Err(InvalidPinTypeError(self.mode)), - } - } - - /// When configured for pulse mode, a given pin will set the non-default state for exactly - /// one clock cycle before returning to the configured default state - #[inline] - pub fn configure_pulse_mode( - &mut self, - enable: bool, - default_state: PinState, - ) -> Result<(), InvalidPinTypeError> { - match self.mode { - DynPinMode::Output(_) => { - self.configure_pulse_mode_internal(enable, default_state); - Ok(()) - } - _ => Err(InvalidPinTypeError(self.mode)), - } - } - - /// See p.37 and p.38 of the programmers guide for more information. - #[inline] - pub fn configure_filter_type( - &mut self, - filter: FilterType, - clksel: FilterClkSel, - ) -> Result<(), InvalidPinTypeError> { - match self.mode { - DynPinMode::Input(_) => { - self.configure_filter_type_internal(filter, clksel); - Ok(()) - } - _ => Err(InvalidPinTypeError(self.mode)), - } - } - - pub fn configure_edge_interrupt( - &mut self, - edge_type: InterruptEdge, - ) -> Result<(), InvalidPinTypeError> { - match self.mode { - DynPinMode::Input(_) | DynPinMode::Output(_) => { - self.configure_edge_interrupt_internal(edge_type); - Ok(()) - } - _ => Err(InvalidPinTypeError(self.mode)), - } - } - - pub fn configure_level_interrupt( - &mut self, - level_type: InterruptLevel, - ) -> Result<(), InvalidPinTypeError> { - match self.mode { - DynPinMode::Input(_) | DynPinMode::Output(_) => { - self.configure_level_interrupt_internal(level_type); - Ok(()) - } - _ => Err(InvalidPinTypeError(self.mode)), - } - } - - /// Change the pin mode - #[inline] - pub(crate) fn change_mode(&mut self, mode: DynPinMode) { - let ModeFields { - dir, - funsel, - opendrn, - pull_dir, - pull_en, - enb_input, - } = mode.into(); - let (portreg, iocfg) = (self.port_reg(), self.iocfg_port()); - iocfg.write(|w| { - w.opendrn().bit(opendrn); - w.pen().bit(pull_en); - w.plevel().bit(pull_dir); - w.iewo().bit(enb_input); - unsafe { w.funsel().bits(funsel) } - }); - let mask = self.mask_32(); - unsafe { - if dir { - portreg.dir().modify(|r, w| w.bits(r.bits() | mask)); - // Clear output - portreg.clrout().write(|w| w.bits(mask)); - } else { - portreg.dir().modify(|r, w| w.bits(r.bits() & !mask)); - } - } - } - - #[inline] - const fn port_reg(&self) -> &PortRegisterBlock { - match self.id().port() { - Port::A => unsafe { &(*pac::Porta::ptr()) }, - Port::B => unsafe { &(*pac::Portb::ptr()) }, - } - } - - #[inline] - const fn iocfg_port(&self) -> &PortReg { - let ioconfig = unsafe { va108xx::Ioconfig::ptr().as_ref().unwrap() }; - match self.id().port() { - Port::A => ioconfig.porta(self.id().num() as usize), - Port::B => ioconfig.portb0(self.id().num() as usize), - } - } - - #[inline(always)] - fn read_internal(&self) -> Result { - match self.mode { - DynPinMode::Input(_) | DYN_RD_OPEN_DRAIN_OUTPUT | DYN_RD_PUSH_PULL_OUTPUT => { - Ok(self.read_pin()) - } - _ => Err(InvalidPinTypeError(self.mode)), - } - } - - #[inline(always)] - fn write_internal(&mut self, bit: bool) -> Result<(), InvalidPinTypeError> { - match self.mode { - DynPinMode::Output(_) => { - self.write_pin(bit); - Ok(()) - } - _ => Err(InvalidPinTypeError(self.mode)), - } - } - - #[inline] - /// Read the logic level of an output pin - pub(crate) fn read_pin(&self) -> bool { - let portreg = self.port_reg(); - ((portreg.datainraw().read().bits() >> self.id().num) & 0x01) == 1 - } - - /// Read a pin but use the masked version but check whether the datamask for the pin is - /// cleared as well - #[inline(always)] - fn read_pin_masked(&self) -> Result { - if !self.datamask() { - Err(IsMaskedError) - } else { - Ok(((self.port_reg().datain().read().bits() >> self.id().num) & 0x01) == 1) - } - } - - /// Write the logic level of an output pin - #[inline(always)] - pub(crate) fn write_pin(&mut self, bit: bool) { - // Safety: SETOUT is a "mask" register, and we only write the bit for - // this pin ID - unsafe { - if bit { - self.port_reg().setout().write(|w| w.bits(self.mask_32())); - } else { - self.port_reg().clrout().write(|w| w.bits(self.mask_32())); - } - } - } - - /// Write the logic level of an output pin but check whether the datamask for the pin is - /// cleared as well - #[inline] - fn write_pin_masked(&mut self, bit: bool) -> Result<(), IsMaskedError> { - if !self.datamask() { - Err(IsMaskedError) - } else { - // Safety: SETOUT is a "mask" register, and we only write the bit for - // this pin ID - unsafe { - if bit { - self.port_reg().setout().write(|w| w.bits(self.mask_32())); - } else { - self.port_reg().clrout().write(|w| w.bits(self.mask_32())); - } - Ok(()) - } - } - } - - /// Toggle the logic level of an output pin - #[inline(always)] - pub fn toggle_with_togout_reg(&mut self) { - // Safety: TOGOUT is a "mask" register, and we only write the bit for - // this pin ID - unsafe { self.port_reg().togout().write(|w| w.bits(self.mask_32())) }; - } - - /// Only useful for interrupt pins. Configure whether to use edges or level as interrupt soure - /// When using edge mode, it is possible to generate interrupts on both edges as well - #[inline] - fn configure_edge_interrupt_internal(&mut self, edge_type: InterruptEdge) { - unsafe { - self.port_reg() - .irq_sen() - .modify(|r, w| w.bits(r.bits() & !self.mask_32())); - match edge_type { - InterruptEdge::HighToLow => { - self.port_reg() - .irq_evt() - .modify(|r, w| w.bits(r.bits() & !self.mask_32())); - } - InterruptEdge::LowToHigh => { - self.port_reg() - .irq_evt() - .modify(|r, w| w.bits(r.bits() | self.mask_32())); - } - InterruptEdge::BothEdges => { - self.port_reg() - .irq_edge() - .modify(|r, w| w.bits(r.bits() | self.mask_32())); - } - } - } - } - - /// Configure which edge or level type triggers an interrupt - #[inline] - fn configure_level_interrupt_internal(&mut self, level: InterruptLevel) { - unsafe { - self.port_reg() - .irq_sen() - .modify(|r, w| w.bits(r.bits() | self.mask_32())); - if level == InterruptLevel::Low { - self.port_reg() - .irq_evt() - .modify(|r, w| w.bits(r.bits() & !self.mask_32())); - } else { - self.port_reg() - .irq_evt() - .modify(|r, w| w.bits(r.bits() | self.mask_32())); - } - } - } - - /// Only useful for input pins - #[inline] - fn configure_filter_type_internal(&mut self, filter: FilterType, clksel: FilterClkSel) { - self.iocfg_port().modify(|_, w| { - // Safety: Only write to register for this Pin ID - unsafe { - w.flttype().bits(filter as u8); - w.fltclk().bits(clksel as u8) - } - }); - } - - #[inline] - fn configure_pulse_mode_internal(&mut self, enable: bool, default_state: PinState) { - let portreg = self.port_reg(); - unsafe { - if enable { - portreg - .pulse() - .modify(|r, w| w.bits(r.bits() | self.mask_32())); - } else { - portreg - .pulse() - .modify(|r, w| w.bits(r.bits() & !self.mask_32())); - } - if default_state == PinState::Low { - portreg - .pulsebase() - .modify(|r, w| w.bits(r.bits() & !self.mask_32())); - } else { - portreg - .pulsebase() - .modify(|r, w| w.bits(r.bits() | self.mask_32())); - } - } - } - - /// Only useful for output pins - #[inline] - fn configure_delay_internal(&mut self, delay_1: bool, delay_2: bool) { - let portreg = self.port_reg(); - unsafe { - if delay_1 { - portreg - .delay1() - .modify(|r, w| w.bits(r.bits() | self.mask_32())); - } else { - portreg - .delay1() - .modify(|r, w| w.bits(r.bits() & !self.mask_32())); - } - if delay_2 { - portreg - .delay2() - .modify(|r, w| w.bits(r.bits() | self.mask_32())); - } else { - portreg - .delay2() - .modify(|r, w| w.bits(r.bits() & !self.mask_32())); - } - } - } - - // Only serves disambiguation purposes for the Embedded HAL impl - #[inline(always)] - fn is_low_mut(&mut self) -> Result { - self.is_low() - } - - // Only serves disambiguation purposes for the Embedded HAL impl - #[inline(always)] - fn is_high_mut(&mut self) -> Result { - self.is_high() - } - - #[inline(always)] - const fn mask_32(&self) -> u32 { - 1 << self.id().num() - } -} - -//================================================================================================== -// Convert between Pin and DynPin -//================================================================================================== - -impl From> for DynPin { - /// 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 { - 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 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/va108xx-hal/src/gpio/mod.rs b/va108xx-hal/src/gpio/mod.rs index 6c95ae0..46bcd4c 100644 --- a/va108xx-hal/src/gpio/mod.rs +++ b/va108xx-hal/src/gpio/mod.rs @@ -1,74 +1,27 @@ -//! # 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 //! //! - [Blinky example](https://egit.irs.uni-stuttgart.de/rust/va108xx-rs/src/branch/main/examples/simple/examples/blinky.rs) +//! - [Async GPIO example](https://egit.irs.uni-stuttgart.de/rust/va108xx-rs/src/branch/main/examples/embassy/src/bin/async-gpio.rs) +pub use vorago_shared_periphs::gpio::*; -//================================================================================================== -// Errors, Definitions and Constants -//================================================================================================== +pub use vorago_shared_periphs::gpio::asynch; +/// Low-level GPIO access. +pub use vorago_shared_periphs::gpio::ll; -pub const NUM_PINS_PORT_A: usize = 32; -pub const NUM_PINS_PORT_B: usize = 24; -pub const NUM_GPIO_PINS: usize = NUM_PINS_PORT_A + NUM_PINS_PORT_B; - -#[derive(Debug, PartialEq, Eq, thiserror::Error)] -#[cfg_attr(feature = "defmt", derive(defmt::Format))] -#[error("The pin is masked")] -pub struct IsMaskedError; - -#[derive(Debug, PartialEq, Eq, Clone, Copy)] -#[cfg_attr(feature = "defmt", derive(defmt::Format))] -pub enum Port { - A, - B, -} - -#[derive(Debug, PartialEq, Eq)] -#[cfg_attr(feature = "defmt", derive(defmt::Format))] -pub enum InterruptEdge { - HighToLow, - LowToHigh, - BothEdges, -} - -#[derive(Debug, PartialEq, Eq)] -#[cfg_attr(feature = "defmt", derive(defmt::Format))] -pub enum InterruptLevel { - Low = 0, - High = 1, -} - -#[derive(Debug, PartialEq, Eq)] -#[cfg_attr(feature = "defmt", derive(defmt::Format))] -pub enum PinState { - Low = 0, - High = 1, -} - -pub mod dynpin; -pub use dynpin::*; - -pub mod pin; -pub use pin::*; - -pub mod asynch; -pub use asynch::*; +/// GPIO register definitions. +pub use vorago_shared_periphs::gpio::regs; diff --git a/va108xx-hal/src/gpio/pin.rs b/va108xx-hal/src/gpio/pin.rs deleted file mode 100644 index 8575f2e..0000000 --- a/va108xx-hal/src/gpio/pin.rs +++ /dev/null @@ -1,823 +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 or B) and pin number. Each -//! [PinId] instance is named according to its datasheet identifier, e.g. -//! [PA2]. -//! -//! A [PinMode] represents the various pin modes. The available [PinMode] -//! variants are [`Input`], [`Output`] and [`Alternate`], each with its own -//! corresponding configurations. -//! -//! It is not possible for users to create new instances of a [`Pin`]. Singleton -//! instances of each pin are made available to users through the PinsX -//! struct. -//! -//! Example for the pins of PORT A: -//! -//! To create the [PinsA] struct, users must supply the PAC -//! [Port](crate::pac::Porta) peripheral. The [PinsA] struct takes -//! ownership of the [Porta] and provides the corresponding pins. Each [`Pin`] -//! within the [PinsA] struct can be moved out and used individually. -//! -//! -//! ``` -//! let mut peripherals = Peripherals::take().unwrap(); -//! let pinsa = PinsA::new(peripherals.PORT); -//! ``` -//! -//! Pins can be converted between modes using several different methods. -//! -//! ```no_run -//! // Use one of the literal function names -//! let pa0 = pinsa.pa0.into_floating_input(); -//! // Use a generic method and one of the `PinMode` variant types -//! let pa0 = pinsa.pa0.into_mode::(); -//! // 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 super::dynpin::{DynAlternate, DynInput, DynOutput, DynPinId, DynPinMode}; -use super::{DynPin, InputPinAsync, InterruptEdge, InterruptLevel, PinState, Port}; -use crate::{ - pac::{Porta, Portb}, - typelevel::Sealed, -}; -use core::convert::Infallible; -use core::marker::PhantomData; -use core::mem::transmute; -use paste::paste; - -//================================================================================================== -// Input configuration -//================================================================================================== - -/// Type-level enum for input configurations -/// -/// The valid options are [Floating], [PullDown] and [PullUp]. -pub trait InputConfig: Sealed { - /// Corresponding [DynInput] - const DYN: DynInput; -} - -#[derive(Debug)] -pub enum Floating {} -#[derive(Debug)] -pub enum PullDown {} -#[derive(Debug)] -pub enum PullUp {} - -impl InputConfig for Floating { - const DYN: DynInput = DynInput::Floating; -} -impl InputConfig for PullDown { - const DYN: DynInput = DynInput::PullDown; -} -impl InputConfig for PullUp { - const DYN: DynInput = DynInput::PullUp; -} - -impl Sealed for Floating {} -impl Sealed for PullDown {} -impl Sealed for PullUp {} - -/// Type-level variant of [`PinMode`] for floating input mode -pub type InputFloating = Input; -/// 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`] -#[derive(Debug)] -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, -} - -pub use crate::clock::FilterClkSel; - -//================================================================================================== -// Output configuration -//================================================================================================== - -pub trait OutputConfig: Sealed { - const DYN: DynOutput; -} - -pub trait ReadableOutput: Sealed {} - -/// Type-level variant of [`OutputConfig`] for a push-pull configuration -#[derive(Debug)] -pub enum PushPull {} -/// Type-level variant of [`OutputConfig`] for an open drain configuration -#[derive(Debug)] -pub enum OpenDrain {} - -/// Type-level variant of [`OutputConfig`] for a readable push-pull configuration -#[derive(Debug)] -pub enum ReadablePushPull {} -/// Type-level variant of [`OutputConfig`] for a readable open-drain configuration -#[derive(Debug)] -pub enum ReadableOpenDrain {} - -impl Sealed for PushPull {} -impl Sealed for OpenDrain {} -impl Sealed for ReadableOpenDrain {} -impl Sealed for ReadablePushPull {} -impl ReadableOutput for ReadableOpenDrain {} -impl ReadableOutput for ReadablePushPull {} - -impl OutputConfig for PushPull { - const DYN: DynOutput = DynOutput::PushPull; -} -impl OutputConfig for OpenDrain { - const DYN: DynOutput = DynOutput::OpenDrain; -} -impl OutputConfig for ReadablePushPull { - const DYN: DynOutput = DynOutput::ReadablePushPull; -} -impl OutputConfig for ReadableOpenDrain { - const DYN: DynOutput = DynOutput::ReadableOpenDrain; -} - -/// Type-level variant of [`PinMode`] for output modes -/// -/// Type `C` is one of four output configurations: [`PushPull`], [`OpenDrain`] or -/// their respective readable versions -#[derive(Debug)] -pub struct Output { - 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; -} - -macro_rules! pin_id { - ($Group:ident, $Id:ident, $NUM:literal) => { - // Need paste macro to use ident in doc attribute - paste! { - #[doc = "Pin ID representing pin " $Id] - #[derive(Debug)] - pub enum $Id {} - impl Sealed for $Id {} - impl PinId for $Id { - const DYN: DynPinId = DynPinId::new(Port::$Group, $NUM); - } - } - }; -} - -//================================================================================================== -// Pin -//================================================================================================== - -/// A type-level GPIO pin, parameterized by [PinId] and [PinMode] types -#[derive(Debug)] -pub struct Pin { - inner: DynPin, - phantom: 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), - phantom: PhantomData, - } - } - - #[inline] - pub const fn id(&self) -> DynPinId { - self.inner.id() - } - - /// 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.40 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.40 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.40 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, irq_cfg: crate::InterruptConfig) { - self.inner.enable_interrupt(irq_cfg); - } - - #[inline] - pub fn disable_interrupt(&mut self, reset_irqsel: bool) { - self.inner.disable_interrupt(reset_irqsel); - } - - /// Configure the pin for an edge interrupt but does not enable the interrupt. - pub fn configure_edge_interrupt(&mut self, edge_type: InterruptEdge) { - self.inner.configure_edge_interrupt(edge_type).unwrap(); - } - - /// Configure the pin for a level interrupt but does not enable the interrupt. - pub fn configure_level_interrupt(&mut self, level_type: InterruptLevel) { - self.inner.configure_level_interrupt(level_type).unwrap(); - } -} - -//============================================================================== -// AnyPin -//============================================================================== - -/// Type class for [`Pin`] types -/// -/// This trait uses the [`AnyKind`] trait pattern to create a [type class] for -/// [`Pin`] types. See the `AnyKind` documentation for more details on the -/// pattern. -/// -/// ## `v1` Compatibility -/// -/// Normally, this trait would use `Is>` 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, irq: crate::pac::Interrupt) -> InputPinAsync { - InputPinAsync::new(self, irq) - } -} - -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() - } - - /// See p.53 of the programmers guide for more information. - /// Possible delays in clock cycles: - /// - Delay 1: 1 - /// - Delay 2: 2 - /// - Delay 1 + Delay 2: 3 - #[inline] - pub fn configure_delay(&mut self, delay_1: bool, delay_2: bool) { - self.inner.configure_delay(delay_1, delay_2).unwrap(); - } - - /// See p.52 of the programmers guide for more information. - /// - /// When configured for pulse mode, a given pin will set the non-default state for exactly - /// one clock cycle before returning to the configured default state - pub fn configure_pulse_mode(&mut self, enable: bool, default_state: PinState) { - self.inner - .configure_pulse_mode(enable, default_state) - .unwrap(); - } -} - -impl Pin> { - /// See p.37 and p.38 of the programmers guide for more information. - #[inline] - pub fn configure_filter_type(&mut self, filter: FilterType, clksel: FilterClkSel) { - self.inner.configure_filter_type(filter, clksel).unwrap(); - } -} - -//================================================================================================== -// Embedded HAL traits -//================================================================================================== - -impl 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(()) - } -} - -//================================================================================================== -// Pin definitions -//================================================================================================== - -macro_rules! pins { - ( - $Port:ident, $PinsName:ident, $($Id:ident,)+, - ) => { - paste!( - /// Collection of all the individual [`Pin`]s for a given port (PORTA or PORTB) - #[derive(Debug)] - pub struct $PinsName { - port: $Port, - $( - #[doc = "Pin " $Id] - pub [<$Id:lower>]: Pin<$Id, Reset>, - )+ - } - - impl $PinsName { - /// Create a new struct containing all the Pins. Passing the IOCONFIG peripheral - /// is optional because it might be required to create pin definitions for both - /// ports. - #[inline] - pub fn new( - syscfg: &mut va108xx::Sysconfig, - port: $Port - ) -> $PinsName { - syscfg.peripheral_clk_enable().modify(|_, w| { - w.[<$Port:lower>]().set_bit(); - w.gpio().set_bit(); - w.ioconfig().set_bit() - }); - $PinsName { - //iocfg, - port, - // Safe because we only create one `Pin` per `PinId` - $( - [<$Id:lower>]: unsafe { Pin::new() }, - )+ - } - } - - /// Get the peripheral ID - /// Safety: Read-only register - pub fn get_perid() -> u32 { - let port = unsafe { &(*$Port::ptr()) }; - port.perid().read().bits() - } - - /// Consumes the Pins struct and returns the port definitions - pub fn release(self) -> $Port { - self.port - } - } - ); - } -} - -macro_rules! declare_pins { - ( - $Group:ident, $PinsName:ident, $Port:ident, [$(($Id:ident, $NUM:literal),)+] - ) => { - pins!($Port, $PinsName, $($Id,)+,); - $( - pin_id!($Group, $Id, $NUM); - )+ - } -} - -declare_pins!( - A, - PinsA, - Porta, - [ - (PA0, 0), - (PA1, 1), - (PA2, 2), - (PA3, 3), - (PA4, 4), - (PA5, 5), - (PA6, 6), - (PA7, 7), - (PA8, 8), - (PA9, 9), - (PA10, 10), - (PA11, 11), - (PA12, 12), - (PA13, 13), - (PA14, 14), - (PA15, 15), - (PA16, 16), - (PA17, 17), - (PA18, 18), - (PA19, 19), - (PA20, 20), - (PA21, 21), - (PA22, 22), - (PA23, 23), - (PA24, 24), - (PA25, 25), - (PA26, 26), - (PA27, 27), - (PA28, 28), - (PA29, 29), - (PA30, 30), - (PA31, 31), - ] -); - -declare_pins!( - B, - PinsB, - Portb, - [ - (PB0, 0), - (PB1, 1), - (PB2, 2), - (PB3, 3), - (PB4, 4), - (PB5, 5), - (PB6, 6), - (PB7, 7), - (PB8, 8), - (PB9, 9), - (PB10, 10), - (PB11, 11), - (PB12, 12), - (PB13, 13), - (PB14, 14), - (PB15, 15), - (PB16, 16), - (PB17, 17), - (PB18, 18), - (PB19, 19), - (PB20, 20), - (PB21, 21), - (PB22, 22), - (PB23, 23), - ] -); diff --git a/va108xx-hal/src/i2c.rs b/va108xx-hal/src/i2c.rs index 1dd53e8..eef3838 100644 --- a/va108xx-hal/src/i2c.rs +++ b/va108xx-hal/src/i2c.rs @@ -3,899 +3,4 @@ //! ## Examples //! //! - [REB1 I2C temperature sensor example](https://egit.irs.uni-stuttgart.de/rust/va108xx-rs/src/branch/main/vorago-reb1/examples/adt75-temp-sensor.rs) -use crate::{ - clock::enable_peripheral_clock, pac, time::Hertz, typelevel::Sealed, PeripheralSelect, -}; -use core::{marker::PhantomData, ops::Deref}; -use embedded_hal::i2c::{self, Operation, SevenBitAddress, TenBitAddress}; - -//================================================================================================== -// Defintions -//================================================================================================== - -const CLK_100K: Hertz = Hertz::from_raw(100_000); -const CLK_400K: Hertz = Hertz::from_raw(400_000); -const MIN_CLK_400K: Hertz = Hertz::from_raw(8_000_000); - -#[derive(Debug, PartialEq, Eq, Copy, Clone)] -#[cfg_attr(feature = "defmt", derive(defmt::Format))] -pub enum FifoEmptyMode { - Stall = 0, - EndTransaction = 1, -} - -#[derive(Debug, PartialEq, Eq, thiserror::Error)] -#[cfg_attr(feature = "defmt", derive(defmt::Format))] -#[error("clock too slow for fast I2C mode")] -pub struct ClockTooSlowForFastI2cError; - -#[derive(Debug, PartialEq, Eq, thiserror::Error)] -#[error("invalid timing parameters")] -pub struct InvalidTimingParamsError; - -#[derive(Debug, PartialEq, Eq, thiserror::Error)] -#[cfg_attr(feature = "defmt", derive(defmt::Format))] -pub enum Error { - #[error("arbitration lost")] - ArbitrationLost, - #[error("nack address")] - NackAddr, - /// Data not acknowledged in write operation - #[error("data not acknowledged in write operation")] - NackData, - /// Not enough data received in read operation - #[error("insufficient data received")] - InsufficientDataReceived, - /// Number of bytes in transfer too large (larger than 0x7fe) - #[error("data too large (larger than 0x7fe)")] - DataTooLarge, -} - -#[derive(Debug, PartialEq, Eq, thiserror::Error)] -#[cfg_attr(feature = "defmt", derive(defmt::Format))] -pub enum InitError { - /// Wrong address used in constructor - #[error("wrong address mode")] - WrongAddrMode, - /// APB1 clock is too slow for fast I2C mode. - #[error("clock too slow for fast I2C mode: {0}")] - ClkTooSlow(#[from] ClockTooSlowForFastI2cError), -} - -impl embedded_hal::i2c::Error for Error { - fn kind(&self) -> embedded_hal::i2c::ErrorKind { - match self { - Error::ArbitrationLost => embedded_hal::i2c::ErrorKind::ArbitrationLoss, - Error::NackAddr => { - embedded_hal::i2c::ErrorKind::NoAcknowledge(i2c::NoAcknowledgeSource::Address) - } - Error::NackData => { - embedded_hal::i2c::ErrorKind::NoAcknowledge(i2c::NoAcknowledgeSource::Data) - } - Error::DataTooLarge | Error::InsufficientDataReceived => { - embedded_hal::i2c::ErrorKind::Other - } - } - } -} - -#[derive(Debug, PartialEq, Copy, Clone)] -#[cfg_attr(feature = "defmt", derive(defmt::Format))] -enum I2cCmd { - Start = 0b00, - Stop = 0b10, - StartWithStop = 0b11, - Cancel = 0b100, -} - -#[derive(Debug, PartialEq, Eq, Copy, Clone)] -#[cfg_attr(feature = "defmt", derive(defmt::Format))] -pub enum I2cSpeed { - Regular100khz = 0, - Fast400khz = 1, -} - -#[derive(Debug, PartialEq, Eq)] -#[cfg_attr(feature = "defmt", derive(defmt::Format))] -pub enum I2cDirection { - Send = 0, - Read = 1, -} - -#[derive(Debug, PartialEq, Eq, Copy, Clone)] -#[cfg_attr(feature = "defmt", derive(defmt::Format))] -pub enum I2cAddress { - Regular(u8), - TenBit(u16), -} - -pub type I2cRegBlock = pac::i2ca::RegisterBlock; - -/// Common trait implemented by all PAC peripheral access structures. The register block -/// format is the same for all SPI blocks. -pub trait Instance: Deref { - const IDX: u8; - const PERIPH_SEL: PeripheralSelect; - - fn ptr() -> *const I2cRegBlock; -} - -impl Instance for pac::I2ca { - const IDX: u8 = 0; - const PERIPH_SEL: PeripheralSelect = PeripheralSelect::I2c0; - - #[inline(always)] - fn ptr() -> *const I2cRegBlock { - Self::ptr() - } -} - -impl Instance for pac::I2cb { - const IDX: u8 = 1; - const PERIPH_SEL: PeripheralSelect = PeripheralSelect::I2c1; - - #[inline(always)] - fn ptr() -> *const I2cRegBlock { - Self::ptr() - } -} - -//================================================================================================== -// Config -//================================================================================================== - -#[cfg_attr(feature = "defmt", derive(defmt::Format))] -pub struct TrTfThighTlow(u8, u8, u8, u8); -#[cfg_attr(feature = "defmt", derive(defmt::Format))] -pub struct TsuStoTsuStaThdStaTBuf(u8, u8, u8, u8); - -#[cfg_attr(feature = "defmt", derive(defmt::Format))] -pub struct TimingCfg { - // 4 bit max width - tr: u8, - // 4 bit max width - tf: u8, - // 4 bit max width - thigh: u8, - // 4 bit max width - tlow: u8, - // 4 bit max width - tsu_sto: u8, - // 4 bit max width - tsu_sta: u8, - // 4 bit max width - thd_sta: u8, - // 4 bit max width - tbuf: u8, -} - -impl TimingCfg { - pub fn new( - first_16_bits: TrTfThighTlow, - second_16_bits: TsuStoTsuStaThdStaTBuf, - ) -> Result { - 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, - sys_clk: 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( - syscfg: &mut pac::Sysconfig, - sysclk: impl Into, - i2c: I2c, - speed_mode: I2cSpeed, - ms_cfg: Option<&MasterConfig>, - sl_cfg: Option<&SlaveConfig>, - ) -> Result { - enable_peripheral_clock(syscfg, I2c::PERIPH_SEL); - - let mut i2c_base = I2cBase { - i2c, - sys_clk: sysclk.into(), - }; - if let Some(ms_cfg) = ms_cfg { - i2c_base.cfg_master(ms_cfg); - } - - if let Some(sl_cfg) = sl_cfg { - i2c_base.cfg_slave(sl_cfg); - } - i2c_base.cfg_clk_scale(speed_mode)?; - Ok(i2c_base) - } - - fn cfg_master(&mut self, ms_cfg: &MasterConfig) { - let (txfemd, rxfemd) = match (ms_cfg.tx_fe_mode, ms_cfg.rx_fe_mode) { - (FifoEmptyMode::Stall, FifoEmptyMode::Stall) => (false, false), - (FifoEmptyMode::Stall, FifoEmptyMode::EndTransaction) => (false, true), - (FifoEmptyMode::EndTransaction, FifoEmptyMode::Stall) => (true, false), - (FifoEmptyMode::EndTransaction, FifoEmptyMode::EndTransaction) => (true, true), - }; - self.i2c.ctrl().modify(|_, w| { - w.txfemd().bit(txfemd); - w.rxffmd().bit(rxfemd); - w.dlgfilter().bit(ms_cfg.dlg_filt); - w.algfilter().bit(ms_cfg.alg_filt) - }); - if let Some(ref tm_cfg) = ms_cfg.tm_cfg { - self.i2c - .tmconfig() - .write(|w| unsafe { w.bits(tm_cfg.reg()) }); - } - self.i2c.fifo_clr().write(|w| { - w.rxfifo().set_bit(); - w.txfifo().set_bit() - }); - } - - fn cfg_slave(&mut self, sl_cfg: &SlaveConfig) { - let (txfemd, rxfemd) = match (sl_cfg.tx_fe_mode, sl_cfg.rx_fe_mode) { - (FifoEmptyMode::Stall, FifoEmptyMode::Stall) => (false, false), - (FifoEmptyMode::Stall, FifoEmptyMode::EndTransaction) => (false, true), - (FifoEmptyMode::EndTransaction, FifoEmptyMode::Stall) => (true, false), - (FifoEmptyMode::EndTransaction, FifoEmptyMode::EndTransaction) => (true, true), - }; - self.i2c.s0_ctrl().modify(|_, w| { - w.txfemd().bit(txfemd); - w.rxffmd().bit(rxfemd) - }); - self.i2c.s0_fifo_clr().write(|w| { - w.rxfifo().set_bit(); - w.txfifo().set_bit() - }); - let max_words = sl_cfg.max_words; - if let Some(max_words) = max_words { - self.i2c - .s0_maxwords() - .write(|w| unsafe { w.bits((1 << 31) | max_words as u32) }); - } - let (addr, addr_mode_mask) = Self::unwrap_addr(sl_cfg.addr); - // The first bit is the read/write value. Normally, both read and write are matched - // using the RWMASK bit of the address mask register - self.i2c - .s0_address() - .write(|w| unsafe { w.bits((addr << 1) as u32 | addr_mode_mask) }); - if let Some(addr_mask) = sl_cfg.addr_mask { - self.i2c - .s0_addressmask() - .write(|w| unsafe { w.bits((addr_mask << 1) as u32) }); - } - if let Some(addr_b) = sl_cfg.addr_b { - let (addr, addr_mode_mask) = Self::unwrap_addr(addr_b); - self.i2c - .s0_addressb() - .write(|w| unsafe { w.bits((addr << 1) as u32 | addr_mode_mask) }); - } - if let Some(addr_b_mask) = sl_cfg.addr_b_mask { - self.i2c - .s0_addressmaskb() - .write(|w| unsafe { w.bits((addr_b_mask << 1) as u32) }); - } - } - - #[inline] - pub fn filters(&mut self, digital_filt: bool, analog_filt: bool) { - self.i2c.ctrl().modify(|_, w| { - w.dlgfilter().bit(digital_filt); - w.algfilter().bit(analog_filt) - }); - } - - #[inline] - pub fn fifo_empty_mode(&mut self, rx: FifoEmptyMode, tx: FifoEmptyMode) { - self.i2c.ctrl().modify(|_, w| { - w.txfemd().bit(tx as u8 != 0); - w.rxffmd().bit(rx as u8 != 0) - }); - } - - fn calc_clk_div(&self, speed_mode: I2cSpeed) -> Result { - if speed_mode == I2cSpeed::Regular100khz { - Ok(((self.sys_clk.raw() / CLK_100K.raw() / 20) - 1) as u8) - } else { - if self.sys_clk.raw() < MIN_CLK_400K.raw() { - return Err(ClockTooSlowForFastI2cError); - } - Ok(((self.sys_clk.raw() / CLK_400K.raw() / 25) - 1) as u8) - } - } - - /// Configures the clock scale for a given speed mode setting - pub fn cfg_clk_scale( - &mut self, - speed_mode: I2cSpeed, - ) -> Result<(), ClockTooSlowForFastI2cError> { - let clk_div = self.calc_clk_div(speed_mode)?; - self.i2c - .clkscale() - .write(|w| unsafe { w.bits(((speed_mode as u32) << 31) | clk_div as u32) }); - Ok(()) - } - - pub fn load_address(&mut self, addr: u16) { - // Load address - self.i2c - .address() - .write(|w| unsafe { w.bits((addr << 1) as u32) }); - } - - #[inline] - fn stop_cmd(&mut self) { - self.i2c - .cmd() - .write(|w| unsafe { w.bits(I2cCmd::Stop as u32) }); - } -} - -//================================================================================================== -// I2C Master -//================================================================================================== - -pub struct I2cMaster { - i2c_base: I2cBase, - addr: PhantomData, -} - -impl I2cMaster { - pub fn new( - syscfg: &mut pac::Sysconfig, - sysclk: impl Into, - i2c: I2c, - cfg: MasterConfig, - speed_mode: I2cSpeed, - ) -> Result { - Ok(I2cMaster { - i2c_base: I2cBase::new(syscfg, sysclk, i2c, speed_mode, Some(&cfg), None)?, - addr: PhantomData, - } - .enable_master()) - } - - #[inline] - pub fn cancel_transfer(&self) { - self.i2c_base - .i2c - .cmd() - .write(|w| unsafe { w.bits(I2cCmd::Cancel as u32) }); - } - - #[inline] - pub fn clear_tx_fifo(&self) { - self.i2c_base.i2c.fifo_clr().write(|w| w.txfifo().set_bit()); - } - - #[inline] - pub fn clear_rx_fifo(&self) { - self.i2c_base.i2c.fifo_clr().write(|w| w.rxfifo().set_bit()); - } - - #[inline] - pub fn enable_master(self) -> Self { - self.i2c_base.i2c.ctrl().modify(|_, w| w.enable().set_bit()); - self - } - - #[inline] - pub fn disable_master(self) -> Self { - self.i2c_base - .i2c - .ctrl() - .modify(|_, w| w.enable().clear_bit()); - self - } - - #[inline(always)] - fn load_fifo(&self, word: u8) { - self.i2c_base - .i2c - .data() - .write(|w| unsafe { w.bits(word as u32) }); - } - - #[inline(always)] - fn read_fifo(&self) -> u8 { - self.i2c_base.i2c.data().read().bits() as u8 - } - - fn error_handler_write(&mut self, init_cmd: &I2cCmd) { - self.clear_tx_fifo(); - if *init_cmd == I2cCmd::Start { - self.i2c_base.stop_cmd() - } - } - - fn write_base( - &mut self, - addr: I2cAddress, - init_cmd: I2cCmd, - bytes: impl IntoIterator, - ) -> 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( - sys_cfg: &mut pac::Sysconfig, - sys_clk: impl Into, - i2c: I2c, - cfg: SlaveConfig, - speed_mode: I2cSpeed, - ) -> Result { - Ok(I2cSlave { - i2c_base: I2cBase::new(sys_cfg, sys_clk, i2c, speed_mode, None, Some(&cfg))?, - addr: PhantomData, - } - .enable_slave()) - } - - #[inline] - pub fn enable_slave(self) -> Self { - self.i2c_base - .i2c - .s0_ctrl() - .modify(|_, w| w.enable().set_bit()); - self - } - - #[inline] - pub fn disable_slave(self) -> Self { - self.i2c_base - .i2c - .s0_ctrl() - .modify(|_, w| w.enable().clear_bit()); - self - } - - #[inline(always)] - fn load_fifo(&self, word: u8) { - self.i2c_base - .i2c - .s0_data() - .write(|w| unsafe { w.bits(word as u32) }); - } - - #[inline(always)] - fn read_fifo(&self) -> u8 { - self.i2c_base.i2c.s0_data().read().bits() as u8 - } - - #[inline] - fn clear_tx_fifo(&self) { - self.i2c_base - .i2c - .s0_fifo_clr() - .write(|w| w.txfifo().set_bit()); - } - - #[inline] - fn clear_rx_fifo(&self) { - self.i2c_base - .i2c - .s0_fifo_clr() - .write(|w| w.rxfifo().set_bit()); - } - - /// Get the last address that was matched by the slave control and the corresponding - /// master direction - pub fn last_address(&self) -> (I2cDirection, u32) { - let bits = self.i2c_base.i2c.s0_lastaddress().read().bits(); - match bits & 0x01 { - 0 => (I2cDirection::Send, bits >> 1), - 1 => (I2cDirection::Read, bits >> 1), - _ => (I2cDirection::Send, bits >> 1), - } - } - - pub fn write(&mut self, output: &[u8]) -> Result<(), Error> { - let len = output.len(); - // It should theoretically possible to transfer larger data sizes by tracking - // the number of sent words and setting it to 0x7fe as soon as only that many - // bytes are remaining. However, large transfer like this are not common. This - // feature will therefore not be supported for now. - if len > 0x7fe { - return Err(Error::DataTooLarge); - } - let mut bytes = output.iter(); - // FIFO has a depth of 16. We load slightly above the trigger level - // but not all of it because the transaction might fail immediately - const FILL_DEPTH: usize = 12; - - // load the FIFO - for _ in 0..core::cmp::min(FILL_DEPTH, len) { - self.load_fifo(*bytes.next().unwrap()); - } - - let status_reader = self.i2c_base.i2c.s0_status().read(); - let mut load_if_next_available = || { - if let Some(next_byte) = bytes.next() { - self.load_fifo(*next_byte); - } - }; - loop { - if status_reader.nackdata().bit_is_set() { - self.clear_tx_fifo(); - return Err(Error::NackData); - } else if status_reader.idle().bit_is_set() { - return Ok(()); - } else { - while !status_reader.txnfull().bit_is_set() { - load_if_next_available(); - } - } - } - } - - pub fn read(&mut self, buffer: &mut [u8]) -> Result<(), Error> { - let len = buffer.len(); - // It should theoretically possible to transfer larger data sizes by tracking - // the number of sent words and setting it to 0x7fe as soon as only that many - // bytes are remaining. However, large transfer like this are not common. This - // feature will therefore not be supported for now. - if len > 0x7fe { - return Err(Error::DataTooLarge); - } - // Clear the receive FIFO - self.clear_rx_fifo(); - - let mut buf_iter = buffer.iter_mut(); - let mut read_bytes = 0; - let mut read_if_next_available = || { - if let Some(next_byte) = buf_iter.next() { - *next_byte = self.read_fifo(); - } - }; - loop { - let status_reader = self.i2c_base.i2c.s0_status().read(); - if status_reader.idle().bit_is_set() { - if read_bytes != len { - return Err(Error::InsufficientDataReceived); - } - return Ok(()); - } else if status_reader.rxnempty().bit_is_set() { - read_bytes += 1; - read_if_next_available(); - } - } - } -} - -impl I2cSlave { - /// Create a new I2C slave for seven bit addresses - pub fn new( - sys_cfg: &mut pac::Sysconfig, - sys_clk: impl Into, - i2c: I2c, - cfg: SlaveConfig, - speed_mode: I2cSpeed, - ) -> Result { - if let I2cAddress::TenBit(_) = cfg.addr { - return Err(InitError::WrongAddrMode); - } - Ok(Self::new_generic(sys_cfg, sys_clk, i2c, cfg, speed_mode)?) - } -} - -impl I2cSlave { - pub fn new_ten_bit_addr( - sys_cfg: &mut pac::Sysconfig, - sys_clk: impl Into, - i2c: I2c, - cfg: SlaveConfig, - speed_mode: I2cSpeed, - ) -> Result { - Self::new_generic(sys_cfg, sys_clk, i2c, cfg, speed_mode) - } -} +pub use vorago_shared_periphs::i2c::*; diff --git a/va108xx-hal/src/lib.rs b/va108xx-hal/src/lib.rs index 1b28470..ca23b3a 100644 --- a/va108xx-hal/src/lib.rs +++ b/va108xx-hal/src/lib.rs @@ -8,69 +8,23 @@ pub use va108xx as pac; pub mod clock; pub mod gpio; pub mod i2c; +pub mod pins; pub mod prelude; pub mod pwm; pub mod spi; pub mod sysconfig; pub mod time; pub mod timer; -pub mod typelevel; pub mod uart; -#[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::FunSel; -#[derive(Debug, Copy, Clone, PartialEq, Eq)] -#[cfg_attr(feature = "defmt", derive(defmt::Format))] -pub enum PeripheralSelect { - PortA = 0, - PortB = 1, - Spi0 = 4, - Spi1 = 5, - Spi2 = 6, - Uart0 = 8, - Uart1 = 9, - I2c0 = 16, - I2c1 = 17, - Irqsel = 21, - Ioconfig = 22, - Utility = 23, - Gpio = 24, -} +/// This is the NONE destination reigster value for the IRQSEL peripheral. +pub const IRQ_DST_NONE: u32 = 0xffffffff; -/// Generic interrupt config which can be used to specify whether the HAL driver will -/// use the IRQSEL register to route an interrupt, and whether the IRQ will be unmasked in the -/// Cortex-M0 NVIC. Both are generally necessary for IRQs to work, but the user might want to -/// perform those steps themselves. -#[derive(Debug, PartialEq, Eq, Clone, Copy)] -#[cfg_attr(feature = "defmt", derive(defmt::Format))] -pub struct InterruptConfig { - /// Interrupt target vector. Should always be set, might be required for disabling IRQs - pub id: pac::Interrupt, - /// Specfiy whether IRQ should be routed to an IRQ vector using the IRQSEL peripheral. - pub route: bool, - /// Specify whether the IRQ is unmasked in the Cortex-M NVIC. If an interrupt is used for - /// multiple purposes, the user can enable the interrupts themselves. - pub enable_in_nvic: bool, -} - -impl InterruptConfig { - pub fn new(id: pac::Interrupt, route: bool, enable_in_nvic: bool) -> Self { - InterruptConfig { - id, - route, - enable_in_nvic, - } - } -} - -pub type IrqCfg = InterruptConfig; +pub use vorago_shared_periphs::{ + disable_nvic_interrupt, enable_nvic_interrupt, InterruptConfig, PeripheralSelect, +}; #[derive(Debug, PartialEq, Eq, thiserror::Error)] #[cfg_attr(feature = "defmt", derive(defmt::Format))] @@ -101,20 +55,7 @@ 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) { - unsafe { - cortex_m::peripheral::NVIC::unmask(irq); - } -} - -/// Disable a specific interrupt using the NVIC peripheral. -#[inline] -pub fn disable_nvic_interrupt(irq: pac::Interrupt) { - cortex_m::peripheral::NVIC::mask(irq); +#[allow(dead_code)] +pub(crate) mod sealed { + pub trait Sealed {} } diff --git a/va108xx-hal/src/pins.rs b/va108xx-hal/src/pins.rs new file mode 100644 index 0000000..8500714 --- /dev/null +++ b/va108xx-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/va108xx-hal/src/pwm.rs b/va108xx-hal/src/pwm.rs index b3f1de0..22ad292 100644 --- a/va108xx-hal/src/pwm.rs +++ b/va108xx-hal/src/pwm.rs @@ -5,459 +5,4 @@ //! ## Examples //! //! - [PWM example](https://egit.irs.uni-stuttgart.de/rust/va108xx-rs/src/branch/main/examples/simple/examples/pwm.rs) -use core::convert::Infallible; -use core::marker::PhantomData; - -use crate::pac; -use crate::time::Hertz; -use crate::timer::{TimDynRegister, TimPin, TimRegInterface, ValidTim, ValidTimAndPin}; -use crate::{clock::enable_peripheral_clock, gpio::DynPinId}; - -const DUTY_MAX: u16 = u16::MAX; - -#[derive(Debug)] -#[cfg_attr(feature = "defmt", derive(defmt::Format))] -pub(crate) struct PwmCommon { - sys_clk: Hertz, - /// For PWMB, this is the upper limit - current_duty: u16, - /// For PWMA, this value will not be used - current_lower_limit: u16, - current_period: Hertz, - current_rst_val: u32, -} - -enum StatusSelPwm { - PwmA = 3, - PwmB = 4, -} - -pub struct PwmA {} -pub struct PwmB {} - -//================================================================================================== -// Strongly typed PWM pin -//================================================================================================== - -pub struct PwmPin { - pin_and_tim: (Pin, Tim), - inner: ReducedPwmPin, - mode: PhantomData, -} - -impl PwmPin -where - (Pin, Tim): ValidTimAndPin, -{ - /// Create a new stronlgy typed PWM pin - pub fn new( - sys_cfg: &mut pac::Sysconfig, - sys_clk: impl Into + Copy, - pin_and_tim: (Pin, Tim), - initial_period: impl Into + Copy, - ) -> Self { - let mut pin = PwmPin { - pin_and_tim, - inner: ReducedPwmPin::::new( - Tim::TIM_ID, - Pin::DYN, - PwmCommon { - current_duty: 0, - current_lower_limit: 0, - current_period: initial_period.into(), - current_rst_val: 0, - sys_clk: sys_clk.into(), - }, - ), - //unsafe { TimAndPin::new(tim_and_pin.0, tim_and_pin.1) }, - mode: PhantomData, - }; - enable_peripheral_clock(sys_cfg, crate::clock::PeripheralClocks::Gpio); - enable_peripheral_clock(sys_cfg, crate::clock::PeripheralClocks::Ioconfig); - sys_cfg - .tim_clk_enable() - .modify(|r, w| unsafe { w.bits(r.bits() | pin.pin_and_tim.1.mask_32()) }); - pin.enable_pwm_a(); - pin.set_period(initial_period); - pin - } - - pub fn downgrade(self) -> ReducedPwmPin { - self.inner - } - - pub fn release(self) -> (Pin, Tim) { - self.pin_and_tim - } - - #[inline] - fn enable_pwm_a(&mut self) { - self.inner.enable_pwm_a(); - } - - #[inline] - fn enable_pwm_b(&mut self) { - self.inner.enable_pwm_b(); - } - - #[inline] - pub fn get_period(&self) -> Hertz { - self.inner.get_period() - } - - #[inline] - pub fn set_period(&mut self, period: impl Into) { - 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 { - mode: PhantomData, - pin_and_tim: other.pin_and_tim, - inner: other.inner.into(), - }; - pwmb.enable_pwm_b(); - pwmb - } -} - -impl From> for PwmPin -where - (PIN, TIM): ValidTimAndPin, -{ - fn from(other: PwmPin) -> Self { - let mut pwma = Self { - mode: PhantomData, - pin_and_tim: other.pin_and_tim, - inner: other.inner.into(), - }; - pwma.enable_pwm_a(); - pwma - } -} - -impl PwmPin -where - (Pin, Tim): ValidTimAndPin, -{ - pub fn pwma( - sys_cfg: &mut pac::Sysconfig, - sys_clk: impl Into + Copy, - pin_and_tim: (Pin, Tim), - initial_period: impl Into + Copy, - ) -> Self { - let mut pin: PwmPin = - Self::new(sys_cfg, sys_clk, pin_and_tim, initial_period); - pin.enable_pwm_a(); - pin - } -} - -impl PwmPin -where - (Pin, Tim): ValidTimAndPin, -{ - pub fn pwmb( - sys_cfg: &mut pac::Sysconfig, - sys_clk: impl Into + Copy, - pin_and_tim: (Pin, Tim), - initial_period: impl Into + Copy, - ) -> Self { - let mut pin: PwmPin = - Self::new(sys_cfg, sys_clk, pin_and_tim, initial_period); - pin.enable_pwm_b(); - pin - } -} - -//================================================================================================== -// Reduced PWM pin -//================================================================================================== - -/// Reduced version where type information is deleted -pub struct ReducedPwmPin { - 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, - } - } -} - -impl ReducedPwmPin { - #[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.sys_clk.raw() / self.common.current_period.raw(); - self.dyn_reg - .reg_block() - .rst_value() - .write(|w| unsafe { w.bits(self.common.current_rst_val) }); - } - - #[inline] - pub fn disable(&mut self) { - self.dyn_reg - .reg_block() - .ctrl() - .modify(|_, w| w.enable().clear_bit()); - } - - #[inline] - pub fn enable(&mut self) { - self.dyn_reg - .reg_block() - .ctrl() - .modify(|_, w| w.enable().set_bit()); - } - - #[inline] - pub fn period(&self) -> Hertz { - self.common.current_period - } - - #[inline(always)] - pub fn duty(&self) -> u16 { - self.common.current_duty - } -} - -impl 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/va108xx-hal/src/spi.rs b/va108xx-hal/src/spi.rs deleted file mode 100644 index afc8b5c..0000000 --- a/va108xx-hal/src/spi.rs +++ /dev/null @@ -1,1260 +0,0 @@ -//! 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. -//! -//! ## Examples -//! -//! - [Blocking SPI example](https://egit.irs.uni-stuttgart.de/rust/va108xx-rs/src/branch/main/examples/simple/examples/spi.rs) -//! - [REB1 ADC example](https://egit.irs.uni-stuttgart.de/rust/va108xx-rs/src/branch/main/vorago-reb1/examples/max11519-adc.rs) -//! - [REB1 EEPROM library](https://egit.irs.uni-stuttgart.de/rust/va108xx-rs/src/branch/main/vorago-reb1/src/m95m01.rs) -use crate::{ - clock::enable_peripheral_clock, - gpio::pin::{ - AltFunc1, AltFunc2, AltFunc3, Pin, PA10, PA11, PA12, PA13, PA14, PA15, PA16, PA17, PA18, - PA19, PA20, PA21, PA22, PA23, PA24, PA25, PA26, PA27, PA28, PA29, PA30, PA31, PB0, PB1, - PB10, PB11, PB12, PB13, PB14, PB15, PB16, PB17, PB18, PB19, PB2, PB22, PB23, PB3, PB4, PB5, - PB6, PB7, PB8, PB9, - }, - pac, - time::Hertz, - typelevel::Sealed, - PeripheralSelect, -}; -use core::{convert::Infallible, fmt::Debug, marker::PhantomData, ops::Deref}; -use embedded_hal::spi::{Mode, MODE_0}; - -//================================================================================================== -// Defintions -//================================================================================================== - -// FIFO has a depth of 16. -const FILL_DEPTH: usize = 12; - -pub const BMSTART_BMSTOP_MASK: u32 = 1 << 31; -pub const BMSKIPDATA_MASK: u32 = 1 << 30; - -pub const DEFAULT_CLK_DIV: u16 = 2; - -#[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 SpiPort { - Porta = 0, - Portb = 1, - Portc = 2, - Invalid = 3, -} - -#[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::spia::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::Spia { - const IDX: u8 = 0; - const PERIPH_SEL: PeripheralSelect = PeripheralSelect::Spi0; - - #[inline(always)] - fn ptr() -> *const SpiRegBlock { - Self::ptr() - } -} - -impl Instance for pac::Spib { - const IDX: u8 = 1; - const PERIPH_SEL: PeripheralSelect = PeripheralSelect::Spi1; - - #[inline(always)] - fn ptr() -> *const SpiRegBlock { - Self::ptr() - } -} - -impl Instance for pac::Spic { - const IDX: u8 = 2; - const PERIPH_SEL: PeripheralSelect = PeripheralSelect::Spi2; - - #[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_PORT: SpiPort; -} - -pub trait OptionalHwCs: HwCsProvider + Sealed {} - -macro_rules! hw_cs_pins { - ($SPIx:path, $portId: path: - $( - ($PXx:ident, $AFx:ident, $HwCsIdent:path, $typedef:ident), - )+ - ) => { - $( - impl HwCsProvider for Pin<$PXx, $AFx> { - const CS_ID: HwChipSelectId = $HwCsIdent; - const SPI_PORT: SpiPort = $portId; - } - impl OptionalHwCs<$SPIx> for Pin<$PXx, $AFx> {} - pub type $typedef = Pin<$PXx, $AFx>; - )+ - }; -} - -impl HwCsProvider for NoneT { - const CS_ID: HwChipSelectId = HwChipSelectId::Invalid; - const SPI_PORT: SpiPort = SpiPort::Invalid; -} - -impl OptionalHwCs for NoneT {} -impl OptionalHwCs for NoneT {} - -// SPIA - -impl PinSck for Pin {} -impl PinMosi for Pin {} -impl PinMiso for Pin {} - -pub type SpiAPortASck = Pin; -pub type SpiAPortAMosi = Pin; -pub type SpiAPortAMiso = Pin; - -impl PinSck for Pin {} -impl PinMosi for Pin {} -impl PinMiso for Pin {} - -pub type SpiAPortBSck = Pin; -pub type SpiAPortBMosi = Pin; -pub type SpiAPortBMiso = Pin; - -hw_cs_pins!( - pac::Spia, SpiPort::Porta: - (PA28, AltFunc1, HwChipSelectId::Id0, HwCs0SpiAPortA), - (PA27, AltFunc1, HwChipSelectId::Id1, HwCs1SpiAPortA), - (PA26, AltFunc1, HwChipSelectId::Id2, HwCs2SpiAPortA), - (PA25, AltFunc1, HwChipSelectId::Id3, HwCs3SpiAPortA), - (PA24, AltFunc1, HwChipSelectId::Id4, HwCs4SpiAPortA), - (PA23, AltFunc1, HwChipSelectId::Id5, HwCs5SpiAPortA), - (PA22, AltFunc1, HwChipSelectId::Id6, HwCs6SpiAPortA), - (PA21, AltFunc1, HwChipSelectId::Id7, HwCs7SpiAPortA), - (PB6, AltFunc2, HwChipSelectId::Id0, HwCs0SpiAPortB), - (PB5, AltFunc2, HwChipSelectId::Id6, HwCs6SpiAPortB), - (PB4, AltFunc2, HwChipSelectId::Id5, HwCs5SpiAPortB), - (PB3, AltFunc2, HwChipSelectId::Id4, HwCs4SpiAPortB), - (PB2, AltFunc2, HwChipSelectId::Id3, HwCs3SpiAPortB), - (PB1, AltFunc2, HwChipSelectId::Id2, HwCs2SpiAPortB), - (PB0, AltFunc2, HwChipSelectId::Id1, HwCs1SpiAPortB), -); - -// SPIB - -impl PinSck for Pin {} -impl PinMosi for Pin {} -impl PinMiso for Pin {} - -pub type SpiBPortASck = Pin; -pub type SpiBPortAMosi = Pin; -pub type SpiBPortAMiso = Pin; - -impl PinSck for Pin {} -impl PinMosi for Pin {} -impl PinMiso for Pin {} - -impl PinSck for Pin {} -impl PinMosi for Pin {} -impl PinMiso for Pin {} - -hw_cs_pins!( - pac::Spib, SpiPort::Portb: - (PB16, AltFunc1, HwChipSelectId::Id0, HwCs0SpiBPortB0), - (PB15, AltFunc1, HwChipSelectId::Id1, HwCs1SpiBPortB0), - (PB14, AltFunc1, HwChipSelectId::Id2, HwCs2SpiBPortB0), - (PB13, AltFunc1, HwChipSelectId::Id3, HwCs3SpiBPortB), - (PB12, AltFunc1, HwChipSelectId::Id4, HwCs4SpiBPortB), - (PB11, AltFunc1, HwChipSelectId::Id5, HwCs5SpiBPortB), - (PB12, AltFunc2, HwChipSelectId::Id0, HwCs0SpiBPortB2), - (PB11, AltFunc2, HwChipSelectId::Id1, HwCs1SpiBPortB2), - (PB10, AltFunc1, HwChipSelectId::Id6, HwCs6SpiBPortB), - (PB10, AltFunc2, HwChipSelectId::Id2, HwCs2SpiBPortB2), - (PB2, AltFunc1, HwChipSelectId::Id0, HwCs0SpiBPortB1), - (PB1, AltFunc1, HwChipSelectId::Id1, HwCs1SpiBPortB1), - (PB0, AltFunc1, HwChipSelectId::Id2, HwCs2SpiBPortB1), - (PA17, AltFunc2, HwChipSelectId::Id0, HwCs0SpiBPortA), - (PA16, AltFunc2, HwChipSelectId::Id1, HwCs1SpiBPortA), - (PA15, AltFunc2, HwChipSelectId::Id2, HwCs2SpiBPortA), - (PA14, AltFunc2, HwChipSelectId::Id3, HwCs3SpiBPortA), - (PA13, AltFunc2, HwChipSelectId::Id4, HwCs4SpiBPortA), - (PA12, AltFunc2, HwChipSelectId::Id5, HwCs5SpiBPortA0), - (PA11, AltFunc2, HwChipSelectId::Id6, HwCs6SpiBPortA0), - (PA10, AltFunc2, HwChipSelectId::Id7, HwCs7SpiBPortA0), - (PA23, AltFunc2, HwChipSelectId::Id5, HwCs5SpiBPortA1), - (PA22, AltFunc2, HwChipSelectId::Id6, HwCs6SpiBPortA1), - (PA21, AltFunc2, HwChipSelectId::Id7, HwCs7SpiBPortA1), -); - -// SPIC - -// Dummy pin defintion for the ROM SCK. -pub struct RomSck; -// Dummy pin defintion for the ROM MOSI. -pub struct RomMosi; -// Dummy pin defintion for the ROM MISO. -pub struct RomMiso; -// Dummy pin defintion for the ROM chip select. -pub struct RomCs; - -impl Sealed for RomSck {} -impl PinSck for RomSck {} -impl Sealed for RomMosi {} -impl PinMosi for RomMosi {} -impl Sealed for RomMiso {} -impl PinMiso for RomMiso {} -impl Sealed for RomCs {} - -hw_cs_pins!( - pac::Spic, SpiPort::Portc: - (PB9, AltFunc3, HwChipSelectId::Id1, HwCs1SpiCPortB0), - (PB8, AltFunc3, HwChipSelectId::Id2, HwCs2SpiCPortB0), - (PB7, AltFunc3, HwChipSelectId::Id3, HwCs3SpiCPortB), - (PB23, AltFunc3, HwChipSelectId::Id2, HwCs2SpiCPortB1), - (PB22, AltFunc3, HwChipSelectId::Id1, HwCs1SpiCPortB1), - (PA20, AltFunc1, HwChipSelectId::Id1, HwCs1SpiCPortA0), - (PA19, AltFunc1, HwChipSelectId::Id2, HwCs2SpiCPortA0), - (PB18, AltFunc1, HwChipSelectId::Id3, HwCs3SpiCPortA0), - (PA23, AltFunc3, HwChipSelectId::Id1, HwCs1SpiCPortA1), - (PA22, AltFunc3, HwChipSelectId::Id2, HwCs2SpiCPortA1), - (PA21, AltFunc3, HwChipSelectId::Id3, HwCs3SpiCPortA1), - (PA20, AltFunc3, HwChipSelectId::Id4, HwCs4SpiCPortA), -); - -impl HwCsProvider for RomCs { - const CS_ID: HwChipSelectId = HwChipSelectId::Id0; - const SPI_PORT: SpiPort = SpiPort::Portc; -} -impl OptionalHwCs for RomCs {} - -//================================================================================================== -// 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, - sys_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(sys_clk: impl Into, spi_clk: impl Into) -> Option { - clk_div_for_target_clock(sys_clk, spi_clk).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( - sys_clk: impl Into, - spi_clk: impl Into, -) -> Option { - let spi_clk = spi_clk.into(); - let sys_clk = sys_clk.into(); - if spi_clk > sys_clk { - return None; - } - - // Step 1: Calculate raw divider. - let raw_div = sys_clk.raw() / spi_clk.raw(); - let remainder = sys_clk.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) -} - -// Re-export this so it can be used for the constructor -pub use crate::typelevel::NoneT; - -impl SpiBase -where - >::Error: core::fmt::Debug, -{ - #[inline] - pub fn spi(&self) -> &SpiInstance { - &self.spi - } - - #[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 fill_word(&self) -> Word { - self.fill_word - } - - #[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() - } - - /// Configure the hardware chip select given a hardware chip select ID. - #[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 - }); - } - - /// Configure the hardware chip select given a physical hardware CS pin. - #[inline] - pub fn cfg_hw_cs_with_pin>(&mut self, _: &HwCs) { - self.cfg_hw_cs(HwCs::CS_ID); - } - - /// Disables the hardware chip select functionality. This can be used when performing - /// external chip select handling, for example with GPIO pins. - #[inline] - pub fn cfg_hw_cs_disable(&mut self) { - self.spi.ctrl1().modify(|_, w| { - w.sod().set_bit(); - w - }); - } - - /// Utility function to configure all relevant transfer parameters in one go. - /// This is useful if multiple devices with different clock and mode configurations - /// are connected to one bus. - 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) - }); - } - - 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 - /// * `syscfg` - Can be passed optionally to enable the peripheral clock - /// * `sys_clk` - System clock - /// * `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 - pub fn new( - syscfg: &mut pac::Sysconfig, - sys_clk: impl Into, - spi: SpiI, - pins: (Sck, Miso, Mosi), - spi_cfg: SpiConfig, - ) -> Self { - enable_peripheral_clock(syscfg, 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, - sys_clk: sys_clk.into(), - 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 cfg_mode(&mut self, mode: Mode); - - #[inline] - pub fn perid(&self) -> u32; - - #[inline] - pub fn fill_word(&self) -> Word; - - #[inline] - pub fn spi(&self) -> &SpiI; - - /// Configure the hardware chip select given a hardware chip select ID. - #[inline] - pub fn cfg_hw_cs(&mut self, hw_cs: HwChipSelectId); - - /// Configure the hardware chip select given a physical hardware CS pin. - #[inline] - pub fn cfg_hw_cs_with_pin>(&mut self, _hwcs: &HwCs); - - /// Disables the hardware chip select functionality. This can be used when performing - /// external chip select handling, for example with GPIO pins. - #[inline] - pub fn cfg_hw_cs_disable(&mut self); - - /// Utility function to configure all relevant transfer parameters in one go. - /// This is useful if multiple devices with different clock and mode configurations - /// are connected to one bus. - pub fn cfg_transfer>( - &mut self, transfer_cfg: &TransferConfigWithHwcs - ); - - /// 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>; - - /// Low level function to write a word to the SPI FIFO. - /// - /// 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 using the - /// [nb] API. - #[inline(always)] - pub 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. - #[inline(always)] - pub 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. - #[inline(always)] - pub fn read_fifo_unchecked(&self) -> u32; - } - } - - pub fn set_fill_word(&mut self, fill_word: Word) { - self.inner.fill_word = 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, - fill_word: Default::default(), - bmstall: old_spi.inner.bmstall, - sys_clk: old_spi.inner.sys_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, - sys_clk: old_spi.inner.sys_clk, - fill_word: Default::default(), - word: PhantomData, - }, - pins: old_spi.pins, - } - } -} diff --git a/va108xx-hal/src/spi/mod.rs b/va108xx-hal/src/spi/mod.rs new file mode 100644 index 0000000..0b2cb69 --- /dev/null +++ b/va108xx-hal/src/spi/mod.rs @@ -0,0 +1,14 @@ +//! API for the SPI peripheral. +//! +//! The main abstraction provided by this module is the [Spi] an structure. +//! It provides the [embedded_hal::spi] traits, but also offer a low level interface +//! via the [SpiLowLevel] trait. +//! +//! ## Examples +//! +//! - [Blocking SPI example](https://egit.irs.uni-stuttgart.de/rust/va108xx-rs/src/branch/main/examples/simple/examples/spi.rs) +//! - [REB1 ADC example](https://egit.irs.uni-stuttgart.de/rust/va108xx-rs/src/branch/main/vorago-reb1/examples/max11519-adc.rs) +//! - [REB1 EEPROM library](https://egit.irs.uni-stuttgart.de/rust/va108xx-rs/src/branch/main/vorago-reb1/src/m95m01.rs) +pub use vorago_shared_periphs::spi::*; + +pub use vorago_shared_periphs::spi::pins_vor1x as pins; diff --git a/va108xx-hal/src/sysconfig.rs b/va108xx-hal/src/sysconfig.rs index 5d04355..b664761 100644 --- a/va108xx-hal/src/sysconfig.rs +++ b/va108xx-hal/src/sysconfig.rs @@ -1,5 +1,3 @@ -use crate::{pac, PeripheralSelect}; - #[derive(PartialEq, Eq, Debug)] #[cfg_attr(feature = "defmt", derive(defmt::Format))] pub struct InvalidCounterResetVal(pub(crate) ()); @@ -8,10 +6,8 @@ pub struct InvalidCounterResetVal(pub(crate) ()); /// /// Returns [InvalidCounterResetVal] if the scrub rate is 0 /// (equivalent to disabling) or larger than 24 bits -pub fn enable_rom_scrubbing( - syscfg: &mut pac::Sysconfig, - scrub_rate: u32, -) -> Result<(), InvalidCounterResetVal> { +pub fn enable_rom_scrubbing(scrub_rate: u32) -> Result<(), InvalidCounterResetVal> { + let syscfg = unsafe { va108xx::Sysconfig::steal() }; if scrub_rate == 0 || scrub_rate > u32::pow(2, 24) { return Err(InvalidCounterResetVal(())); } @@ -19,7 +15,8 @@ pub fn enable_rom_scrubbing( Ok(()) } -pub fn disable_rom_scrubbing(syscfg: &mut pac::Sysconfig) { +pub fn disable_rom_scrubbing() { + let syscfg = unsafe { va108xx::Sysconfig::steal() }; syscfg.rom_scrub().write(|w| unsafe { w.bits(0) }); } @@ -27,10 +24,8 @@ pub fn disable_rom_scrubbing(syscfg: &mut pac::Sysconfig) { /// /// Returns [InvalidCounterResetVal] if the scrub rate is 0 /// (equivalent to disabling) or larger than 24 bits -pub fn enable_ram_scrubbing( - syscfg: &mut pac::Sysconfig, - scrub_rate: u32, -) -> Result<(), InvalidCounterResetVal> { +pub fn enable_ram_scrubbing(scrub_rate: u32) -> Result<(), InvalidCounterResetVal> { + let syscfg = unsafe { va108xx::Sysconfig::steal() }; if scrub_rate == 0 || scrub_rate > u32::pow(2, 24) { return Err(InvalidCounterResetVal(())); } @@ -38,20 +33,11 @@ pub fn enable_ram_scrubbing( Ok(()) } -pub fn disable_ram_scrubbing(syscfg: &mut pac::Sysconfig) { +pub fn disable_ram_scrubbing() { + let syscfg = unsafe { va108xx::Sysconfig::steal() }; syscfg.ram_scrub().write(|w| unsafe { w.bits(0) }); } -/// Clear the reset bit. This register is active low, so doing this will hold the peripheral -/// in a reset state -pub fn clear_reset_bit(syscfg: &mut pac::Sysconfig, periph_sel: PeripheralSelect) { - syscfg - .peripheral_reset() - .modify(|r, w| unsafe { w.bits(r.bits() & !(1 << periph_sel as u8)) }); -} - -pub fn set_reset_bit(syscfg: &mut pac::Sysconfig, periph_sel: PeripheralSelect) { - syscfg - .peripheral_reset() - .modify(|r, w| unsafe { w.bits(r.bits() | (1 << periph_sel as u8)) }); -} +pub use vorago_shared_periphs::sysconfig::{ + assert_peripheral_reset, disable_peripheral_clock, enable_peripheral_clock, +}; diff --git a/va108xx-hal/src/time.rs b/va108xx-hal/src/time.rs index 9808028..1663b95 100644 --- a/va108xx-hal/src/time.rs +++ b/va108xx-hal/src/time.rs @@ -1,26 +1,2 @@ //! Time units - -// Frequency based - -/// Hertz -pub type Hertz = fugit::HertzU32; - -/// KiloHertz -pub type KiloHertz = fugit::KilohertzU32; - -/// MegaHertz -pub type MegaHertz = fugit::MegahertzU32; - -// Period based - -/// Seconds -pub type Seconds = fugit::SecsDurationU32; - -/// Milliseconds -pub type Milliseconds = fugit::MillisDurationU32; - -/// Microseconds -pub type Microseconds = fugit::MicrosDurationU32; - -/// Nanoseconds -pub type Nanoseconds = fugit::NanosDurationU32; +pub use vorago_shared_periphs::time::*; diff --git a/va108xx-hal/src/timer.rs b/va108xx-hal/src/timer.rs index d356671..a4fc39c 100644 --- a/va108xx-hal/src/timer.rs +++ b/va108xx-hal/src/timer.rs @@ -4,786 +4,6 @@ //! //! - [MS and second tick implementation](https://egit.irs.uni-stuttgart.de/rust/va108xx-rs/src/branch/main/examples/simple/examples/timer-ticks.rs) //! - [Cascade feature example](https://egit.irs.uni-stuttgart.de/rust/va108xx-rs/src/branch/main/examples/simple/examples/cascade.rs) -pub use crate::InterruptConfig; -use crate::{ - clock::{enable_peripheral_clock, PeripheralClocks}, - enable_nvic_interrupt, - gpio::{ - AltFunc1, AltFunc2, AltFunc3, DynPinId, Pin, PinId, PA0, PA1, PA10, PA11, PA12, PA13, PA14, - PA15, PA2, PA24, PA25, PA26, PA27, PA28, PA29, PA3, PA30, PA31, PA4, PA5, PA6, PA7, PA8, - PA9, PB0, PB1, PB10, PB11, PB12, PB13, PB14, PB15, PB16, PB17, PB18, PB19, PB2, PB20, PB21, - PB22, PB23, PB3, PB4, PB5, PB6, - }, - pac::{self, tim0}, - time::Hertz, - timer, - typelevel::Sealed, -}; -use core::cell::Cell; -use critical_section::Mutex; -use fugit::RateExtU32; +pub use vorago_shared_periphs::timer::*; -const IRQ_DST_NONE: u32 = 0xffffffff; -pub static MS_COUNTER: Mutex> = Mutex::new(Cell::new(0)); - -/// Get the peripheral block of a TIM peripheral given the index. -/// -/// This function panics if the given index is greater than 23. -/// -/// # Safety -/// -/// This returns a direct handle to the peripheral block, which allows to circumvent ownership -/// rules for the peripheral block. You have to ensure that the retrieved peripheral block is not -/// used by any other software component. -#[inline(always)] -pub const unsafe fn get_tim_raw(tim_idx: usize) -> &'static pac::tim0::RegisterBlock { - match tim_idx { - 0 => unsafe { &*pac::Tim0::ptr() }, - 1 => unsafe { &*pac::Tim1::ptr() }, - 2 => unsafe { &*pac::Tim2::ptr() }, - 3 => unsafe { &*pac::Tim3::ptr() }, - 4 => unsafe { &*pac::Tim4::ptr() }, - 5 => unsafe { &*pac::Tim5::ptr() }, - 6 => unsafe { &*pac::Tim6::ptr() }, - 7 => unsafe { &*pac::Tim7::ptr() }, - 8 => unsafe { &*pac::Tim8::ptr() }, - 9 => unsafe { &*pac::Tim9::ptr() }, - 10 => unsafe { &*pac::Tim10::ptr() }, - 11 => unsafe { &*pac::Tim11::ptr() }, - 12 => unsafe { &*pac::Tim12::ptr() }, - 13 => unsafe { &*pac::Tim13::ptr() }, - 14 => unsafe { &*pac::Tim14::ptr() }, - 15 => unsafe { &*pac::Tim15::ptr() }, - 16 => unsafe { &*pac::Tim16::ptr() }, - 17 => unsafe { &*pac::Tim17::ptr() }, - 18 => unsafe { &*pac::Tim18::ptr() }, - 19 => unsafe { &*pac::Tim19::ptr() }, - 20 => unsafe { &*pac::Tim20::ptr() }, - 21 => unsafe { &*pac::Tim21::ptr() }, - 22 => unsafe { &*pac::Tim22::ptr() }, - 23 => unsafe { &*pac::Tim23::ptr() }, - _ => { - panic!("invalid alarm timer index") - } - } -} - -//================================================================================================== -// Defintions -//================================================================================================== - -/// Interrupt events -pub enum Event { - /// Timer timed out / count down ended - TimeOut, -} - -#[derive(Default, Debug, PartialEq, Eq, Copy, Clone)] -#[cfg_attr(feature = "defmt", derive(defmt::Format))] -pub struct CascadeCtrl { - /// Enable Cascade 0 signal active as a requirement for counting - pub enb_start_src_csd0: bool, - /// Invert Cascade 0, making it active low - pub inv_csd0: bool, - /// Enable Cascade 1 signal active as a requirement for counting - pub enb_start_src_csd1: bool, - /// Invert Cascade 1, making it active low - pub inv_csd1: bool, - /// Specify required operation if both Cascade 0 and Cascade 1 are active. - /// 0 is a logical AND of both cascade signals, 1 is a logical OR - pub dual_csd_op: bool, - /// Enable trigger mode for Cascade 0. In trigger mode, couting will start with the selected - /// cascade signal active, but once the counter is active, cascade control will be ignored - pub trg_csd0: bool, - /// Trigger mode, identical to [`trg_csd0`](CascadeCtrl) but for Cascade 1 - pub trg_csd1: bool, - /// Enable Cascade 2 signal active as a requirement to stop counting. This mode is similar - /// to the REQ_STOP control bit, but signalled by a Cascade source - pub enb_stop_src_csd2: bool, - /// Invert Cascade 2, making it active low - pub inv_csd2: bool, - /// The counter is automatically disabled if the corresponding Cascade 2 level-sensitive input - /// souce is active when the count reaches 0. If the counter is not 0, the cascade control is - /// ignored - pub trg_csd2: bool, -} - -#[derive(Debug, PartialEq, Eq)] -#[cfg_attr(feature = "defmt", derive(defmt::Format))] -pub enum CascadeSel { - Csd0 = 0, - Csd1 = 1, - Csd2 = 2, -} - -#[derive(Debug, PartialEq, Eq)] -#[cfg_attr(feature = "defmt", derive(defmt::Format))] -pub struct InvalidCascadeSourceId; - -/// The numbers are the base numbers for bundles like PORTA, PORTB or TIM -#[derive(Debug, PartialEq, Eq)] -#[cfg_attr(feature = "defmt", derive(defmt::Format))] -#[repr(u8)] -pub enum CascadeSource { - PortA(u8), - PortB(u8), - Tim(u8), - RamSbe = 96, - RamMbe = 97, - RomSbe = 98, - RomMbe = 99, - Txev = 100, - ClockDivider(u8), -} - -impl CascadeSource { - fn id(&self) -> Result { - let port_check = |base: u8, id: u8, len: u8| { - if id > len - 1 { - return Err(InvalidCascadeSourceId); - } - Ok(base + id) - }; - match self { - CascadeSource::PortA(id) => port_check(0, *id, 32), - CascadeSource::PortB(id) => port_check(32, *id, 32), - CascadeSource::Tim(id) => port_check(64, *id, 24), - CascadeSource::RamSbe => Ok(96), - CascadeSource::RamMbe => Ok(97), - CascadeSource::RomSbe => Ok(98), - CascadeSource::RomMbe => Ok(99), - CascadeSource::Txev => Ok(100), - CascadeSource::ClockDivider(id) => port_check(120, *id, 8), - } - } -} - -//================================================================================================== -// Valid TIM and PIN combinations -//================================================================================================== - -pub trait TimPin { - const DYN: DynPinId; -} - -pub trait ValidTim { - // TIM ID ranging from 0 to 23 for 24 TIM peripherals - const TIM_ID: u8; -} - -macro_rules! tim_marker { - ($TIMX:path, $ID:expr) => { - impl ValidTim for $TIMX { - const TIM_ID: u8 = $ID; - } - }; -} - -tim_marker!(pac::Tim0, 0); -tim_marker!(pac::Tim1, 1); -tim_marker!(pac::Tim2, 2); -tim_marker!(pac::Tim3, 3); -tim_marker!(pac::Tim4, 4); -tim_marker!(pac::Tim5, 5); -tim_marker!(pac::Tim6, 6); -tim_marker!(pac::Tim7, 7); -tim_marker!(pac::Tim8, 8); -tim_marker!(pac::Tim9, 9); -tim_marker!(pac::Tim10, 10); -tim_marker!(pac::Tim11, 11); -tim_marker!(pac::Tim12, 12); -tim_marker!(pac::Tim13, 13); -tim_marker!(pac::Tim14, 14); -tim_marker!(pac::Tim15, 15); -tim_marker!(pac::Tim16, 16); -tim_marker!(pac::Tim17, 17); -tim_marker!(pac::Tim18, 18); -tim_marker!(pac::Tim19, 19); -tim_marker!(pac::Tim20, 20); -tim_marker!(pac::Tim21, 21); -tim_marker!(pac::Tim22, 22); -tim_marker!(pac::Tim23, 23); - -pub trait ValidTimAndPin: Sealed {} - -macro_rules! pin_and_tim { - ($PAX:ident, $ALTFUNC:ident, $ID:expr, $TIMX:path) => { - impl TimPin for Pin<$PAX, $ALTFUNC> - where - $PAX: PinId, - { - const DYN: DynPinId = $PAX::DYN; - } - - impl ValidTimAndPin for (Pin<$PAX, $ALTFUNC>, $TIMX) - where - Pin<$PAX, $ALTFUNC>: TimPin, - $PAX: PinId, - { - } - - impl Sealed for (Pin<$PAX, $ALTFUNC>, $TIMX) {} - }; -} - -pin_and_tim!(PA31, AltFunc2, 23, pac::Tim23); -pin_and_tim!(PA30, AltFunc2, 22, pac::Tim22); -pin_and_tim!(PA29, AltFunc2, 21, pac::Tim21); -pin_and_tim!(PA28, AltFunc2, 20, pac::Tim20); -pin_and_tim!(PA27, AltFunc2, 19, pac::Tim19); -pin_and_tim!(PA26, AltFunc2, 18, pac::Tim18); -pin_and_tim!(PA25, AltFunc2, 17, pac::Tim17); -pin_and_tim!(PA24, AltFunc2, 16, pac::Tim16); - -pin_and_tim!(PA15, AltFunc1, 15, pac::Tim15); -pin_and_tim!(PA14, AltFunc1, 14, pac::Tim14); -pin_and_tim!(PA13, AltFunc1, 13, pac::Tim13); -pin_and_tim!(PA12, AltFunc1, 12, pac::Tim12); -pin_and_tim!(PA11, AltFunc1, 11, pac::Tim11); -pin_and_tim!(PA10, AltFunc1, 10, pac::Tim10); -pin_and_tim!(PA9, AltFunc1, 9, pac::Tim9); -pin_and_tim!(PA8, AltFunc1, 8, pac::Tim8); -pin_and_tim!(PA7, AltFunc1, 7, pac::Tim7); -pin_and_tim!(PA6, AltFunc1, 6, pac::Tim6); -pin_and_tim!(PA5, AltFunc1, 5, pac::Tim5); -pin_and_tim!(PA4, AltFunc1, 4, pac::Tim4); -pin_and_tim!(PA3, AltFunc1, 3, pac::Tim3); -pin_and_tim!(PA2, AltFunc1, 2, pac::Tim2); -pin_and_tim!(PA1, AltFunc1, 1, pac::Tim1); -pin_and_tim!(PA0, AltFunc1, 0, pac::Tim0); - -pin_and_tim!(PB23, AltFunc3, 23, pac::Tim23); -pin_and_tim!(PB22, AltFunc3, 22, pac::Tim22); -pin_and_tim!(PB21, AltFunc3, 21, pac::Tim21); -pin_and_tim!(PB20, AltFunc3, 20, pac::Tim20); -pin_and_tim!(PB19, AltFunc3, 19, pac::Tim19); -pin_and_tim!(PB18, AltFunc3, 18, pac::Tim18); -pin_and_tim!(PB17, AltFunc3, 17, pac::Tim17); -pin_and_tim!(PB16, AltFunc3, 16, pac::Tim16); -pin_and_tim!(PB15, AltFunc3, 15, pac::Tim15); -pin_and_tim!(PB14, AltFunc3, 14, pac::Tim14); -pin_and_tim!(PB13, AltFunc3, 13, pac::Tim13); -pin_and_tim!(PB12, AltFunc3, 12, pac::Tim12); -pin_and_tim!(PB11, AltFunc3, 11, pac::Tim11); -pin_and_tim!(PB10, AltFunc3, 10, pac::Tim10); - -pin_and_tim!(PB6, AltFunc3, 6, pac::Tim6); -pin_and_tim!(PB5, AltFunc3, 5, pac::Tim5); -pin_and_tim!(PB4, AltFunc3, 4, pac::Tim4); -pin_and_tim!(PB3, AltFunc3, 3, pac::Tim3); -pin_and_tim!(PB2, AltFunc3, 2, pac::Tim2); -pin_and_tim!(PB1, AltFunc3, 1, pac::Tim1); -pin_and_tim!(PB0, AltFunc3, 0, pac::Tim0); - -//================================================================================================== -// Register Interface for TIM registers and TIM pins -//================================================================================================== - -pub type TimRegBlock = tim0::RegisterBlock; - -/// Register interface. -/// -/// This interface provides valid TIM pins a way to access their corresponding TIM -/// registers -/// -/// # Safety -/// -/// Users should only implement the [Self::tim_id] function. No default function -/// implementations should be overridden. The implementing type must also have -/// "control" over the corresponding pin ID, i.e. it must guarantee that a each -/// pin ID is a singleton. -pub unsafe trait TimRegInterface { - fn tim_id(&self) -> u8; - - const PORT_BASE: *const tim0::RegisterBlock = pac::Tim0::ptr() as *const _; - - /// All 24 TIM blocks are identical. This helper functions returns the correct - /// memory mapped peripheral depending on the TIM ID. - #[inline(always)] - fn reg_block(&self) -> &TimRegBlock { - unsafe { &*Self::PORT_BASE.offset(self.tim_id() as isize) } - } - - #[inline(always)] - fn mask_32(&self) -> u32 { - 1 << self.tim_id() - } - - /// Clear the reset bit of the TIM, holding it in reset - /// - /// # Safety - /// - /// Only the bit related to the corresponding TIM peripheral is modified - #[inline] - #[allow(dead_code)] - fn clear_tim_reset_bit(&self) { - unsafe { - va108xx::Peripherals::steal() - .sysconfig - .tim_reset() - .modify(|r, w| w.bits(r.bits() & !self.mask_32())); - } - } - - #[inline] - #[allow(dead_code)] - fn set_tim_reset_bit(&self) { - unsafe { - va108xx::Peripherals::steal() - .sysconfig - .tim_reset() - .modify(|r, w| w.bits(r.bits() | self.mask_32())); - } - } -} - -unsafe impl TimRegInterface for Tim { - fn tim_id(&self) -> u8 { - Tim::TIM_ID - } -} - -pub(crate) struct TimDynRegister { - pub(crate) tim_id: u8, - #[allow(dead_code)] - pub(crate) pin_id: DynPinId, -} - -unsafe impl TimRegInterface for TimDynRegister { - #[inline(always)] - fn tim_id(&self) -> u8 { - self.tim_id - } -} - -//================================================================================================== -// Timers -//================================================================================================== - -/// Hardware timers -pub struct CountdownTimer { - tim: Tim, - curr_freq: Hertz, - irq_cfg: Option, - sys_clk: Hertz, - rst_val: u32, - last_cnt: u32, - listening: bool, -} - -#[inline(always)] -pub fn enable_tim_clk(syscfg: &mut pac::Sysconfig, idx: u8) { - syscfg - .tim_clk_enable() - .modify(|r, w| unsafe { w.bits(r.bits() | (1 << idx)) }); -} - -#[inline(always)] -pub fn disable_tim_clk(syscfg: &mut pac::Sysconfig, idx: u8) { - syscfg - .tim_clk_enable() - .modify(|r, w| unsafe { w.bits(r.bits() & !(1 << idx)) }); -} - -unsafe impl TimRegInterface for CountdownTimer { - fn tim_id(&self) -> u8 { - TIM::TIM_ID - } -} - -impl CountdownTimer { - /// Configures a TIM peripheral as a periodic count down timer - pub fn new(syscfg: &mut pac::Sysconfig, sys_clk: impl Into, tim: Tim) -> Self { - enable_tim_clk(syscfg, Tim::TIM_ID); - let cd_timer = CountdownTimer { - tim, - sys_clk: sys_clk.into(), - irq_cfg: None, - rst_val: 0, - curr_freq: 0.Hz(), - listening: false, - last_cnt: 0, - }; - cd_timer - .tim - .reg_block() - .ctrl() - .modify(|_, w| w.enable().set_bit()); - cd_timer - } - - /// Listen for events. Depending on the IRQ configuration, this also activates the IRQ in the - /// IRQSEL peripheral for the provided interrupt and unmasks the interrupt - pub fn listen( - &mut self, - event: Event, - irq_cfg: InterruptConfig, - irq_sel: Option<&mut pac::Irqsel>, - sys_cfg: Option<&mut pac::Sysconfig>, - ) { - match event { - Event::TimeOut => { - cortex_m::peripheral::NVIC::mask(irq_cfg.id); - self.irq_cfg = Some(irq_cfg); - if irq_cfg.route { - if let Some(sys_cfg) = sys_cfg { - enable_peripheral_clock(sys_cfg, PeripheralClocks::Irqsel); - } - if let Some(irq_sel) = irq_sel { - irq_sel - .tim0(Tim::TIM_ID as usize) - .write(|w| unsafe { w.bits(irq_cfg.id as u32) }); - } - } - self.listening = true; - } - } - } - - pub fn unlisten( - &mut self, - event: Event, - syscfg: &mut pac::Sysconfig, - irqsel: &mut pac::Irqsel, - ) { - match event { - Event::TimeOut => { - enable_peripheral_clock(syscfg, PeripheralClocks::Irqsel); - irqsel - .tim0(Tim::TIM_ID as usize) - .write(|w| unsafe { w.bits(IRQ_DST_NONE) }); - self.disable_interrupt(); - self.listening = false; - } - } - } - - #[inline(always)] - pub fn enable_interrupt(&mut self) { - self.tim - .reg_block() - .ctrl() - .modify(|_, w| w.irq_enb().set_bit()); - } - - #[inline(always)] - pub fn disable_interrupt(&mut self) { - self.tim - .reg_block() - .ctrl() - .modify(|_, w| w.irq_enb().clear_bit()); - } - - pub fn release(self, syscfg: &mut pac::Sysconfig) -> Tim { - self.tim - .reg_block() - .ctrl() - .write(|w| w.enable().clear_bit()); - syscfg - .tim_clk_enable() - .modify(|r, w| unsafe { w.bits(r.bits() & !(1 << Tim::TIM_ID)) }); - self.tim - } - - /// Load the count down timer with a timeout but do not start it. - pub fn load(&mut self, timeout: impl Into) { - self.tim - .reg_block() - .ctrl() - .modify(|_, w| w.enable().clear_bit()); - self.curr_freq = timeout.into(); - self.rst_val = self.sys_clk.raw() / self.curr_freq.raw(); - self.set_reload(self.rst_val); - self.set_count(self.rst_val); - } - - #[inline(always)] - pub fn set_reload(&mut self, val: u32) { - self.tim - .reg_block() - .rst_value() - .write(|w| unsafe { w.bits(val) }); - } - - #[inline(always)] - pub fn set_count(&mut self, val: u32) { - self.tim - .reg_block() - .cnt_value() - .write(|w| unsafe { w.bits(val) }); - } - - #[inline(always)] - pub fn count(&self) -> u32 { - self.tim.reg_block().cnt_value().read().bits() - } - - #[inline(always)] - pub fn enable(&mut self) { - if let Some(irq_cfg) = self.irq_cfg { - self.enable_interrupt(); - if irq_cfg.enable_in_nvic { - unsafe { enable_nvic_interrupt(irq_cfg.id) }; - } - } - self.tim - .reg_block() - .enable() - .write(|w| unsafe { w.bits(1) }); - } - - #[inline(always)] - pub fn disable(&mut self) { - self.tim - .reg_block() - .enable() - .write(|w| unsafe { w.bits(0) }); - } - - /// Disable the counter, setting both enable and active bit to 0 - pub fn auto_disable(self, enable: bool) -> Self { - if enable { - self.tim - .reg_block() - .ctrl() - .modify(|_, w| w.auto_disable().set_bit()); - } else { - self.tim - .reg_block() - .ctrl() - .modify(|_, w| w.auto_disable().clear_bit()); - } - self - } - - /// This option only applies when the Auto-Disable functionality is 0. - /// - /// The active bit is changed to 0 when count reaches 0, but the counter stays - /// enabled. When Auto-Disable is 1, Auto-Deactivate is implied - pub fn auto_deactivate(self, enable: bool) -> Self { - if enable { - self.tim - .reg_block() - .ctrl() - .modify(|_, w| w.auto_deactivate().set_bit()); - } else { - self.tim - .reg_block() - .ctrl() - .modify(|_, w| w.auto_deactivate().clear_bit()); - } - self - } - - /// Configure the cascade parameters - pub fn cascade_control(&mut self, ctrl: CascadeCtrl) { - self.tim.reg_block().csd_ctrl().write(|w| { - w.csden0().bit(ctrl.enb_start_src_csd0); - w.csdinv0().bit(ctrl.inv_csd0); - w.csden1().bit(ctrl.enb_start_src_csd1); - w.csdinv1().bit(ctrl.inv_csd1); - w.dcasop().bit(ctrl.dual_csd_op); - w.csdtrg0().bit(ctrl.trg_csd0); - w.csdtrg1().bit(ctrl.trg_csd1); - w.csden2().bit(ctrl.enb_stop_src_csd2); - w.csdinv2().bit(ctrl.inv_csd2); - w.csdtrg2().bit(ctrl.trg_csd2) - }); - } - - pub fn cascade_0_source(&mut self, src: CascadeSource) -> Result<(), InvalidCascadeSourceId> { - let id = src.id()?; - self.tim - .reg_block() - .cascade0() - .write(|w| unsafe { w.cassel().bits(id) }); - Ok(()) - } - - pub fn cascade_1_source(&mut self, src: CascadeSource) -> Result<(), InvalidCascadeSourceId> { - let id = src.id()?; - self.tim - .reg_block() - .cascade1() - .write(|w| unsafe { w.cassel().bits(id) }); - Ok(()) - } - - pub fn cascade_2_source(&mut self, src: CascadeSource) -> Result<(), InvalidCascadeSourceId> { - let id = src.id()?; - self.tim - .reg_block() - .cascade2() - .write(|w| unsafe { w.cassel().bits(id) }); - Ok(()) - } - - pub fn curr_freq(&self) -> Hertz { - self.curr_freq - } - - pub fn listening(&self) -> bool { - self.listening - } -} - -/// CountDown implementation for TIMx -impl CountdownTimer { - #[inline] - pub fn start(&mut self, timeout: T) - where - T: Into, - { - self.load(timeout); - self.enable(); - } - - /// Return `Ok` if the timer has wrapped. Peripheral will automatically clear the - /// flag and restart the time if configured correctly - pub fn wait(&mut self) -> nb::Result<(), void::Void> { - let cnt = self.tim.reg_block().cnt_value().read().bits(); - if (cnt > self.last_cnt) || cnt == 0 { - self.last_cnt = self.rst_val; - Ok(()) - } else { - self.last_cnt = cnt; - Err(nb::Error::WouldBlock) - } - } - - /// Returns [false] if the timer was not active, and true otherwise. - pub fn cancel(&mut self) -> bool { - if !self.tim.reg_block().ctrl().read().enable().bit_is_set() { - return false; - } - self.tim - .reg_block() - .ctrl() - .write(|w| w.enable().clear_bit()); - true - } -} - -impl embedded_hal::delay::DelayNs for CountdownTimer { - fn delay_ns(&mut self, ns: u32) { - let ticks = (u64::from(ns)) * (u64::from(self.sys_clk.raw())) / 1_000_000_000; - - let full_cycles = ticks >> 32; - let mut last_count; - let mut new_count; - if full_cycles > 0 { - self.set_reload(u32::MAX); - self.set_count(u32::MAX); - self.enable(); - - for _ in 0..full_cycles { - // Always ensure that both values are the same at the start. - new_count = self.count(); - last_count = new_count; - loop { - new_count = self.count(); - if new_count == 0 { - // Wait till timer has wrapped. - while self.count() == 0 { - cortex_m::asm::nop() - } - break; - } - // Timer has definitely wrapped. - if new_count > last_count { - break; - } - last_count = new_count; - } - } - } - let ticks = (ticks & u32::MAX as u64) as u32; - self.disable(); - if ticks > 1 { - self.set_reload(ticks); - self.set_count(ticks); - self.enable(); - last_count = ticks; - - loop { - new_count = self.count(); - if new_count == 0 || (new_count > last_count) { - break; - } - last_count = new_count; - } - } - - self.disable(); - } -} - -// Set up a millisecond timer on TIM0. Please note that the user still has to provide an IRQ handler -// which should call [default_ms_irq_handler]. -pub fn set_up_ms_tick( - irq_cfg: InterruptConfig, - sys_cfg: &mut pac::Sysconfig, - irq_sel: Option<&mut pac::Irqsel>, - sys_clk: impl Into, - tim0: TIM, -) -> CountdownTimer { - let mut ms_timer = CountdownTimer::new(sys_cfg, sys_clk, tim0); - ms_timer.listen(timer::Event::TimeOut, irq_cfg, irq_sel, Some(sys_cfg)); - ms_timer.start(1000.Hz()); - ms_timer -} - -pub fn set_up_ms_delay_provider( - sys_cfg: &mut pac::Sysconfig, - sys_clk: impl Into, - tim: TIM, -) -> CountdownTimer { - let mut provider = CountdownTimer::new(sys_cfg, sys_clk, tim); - provider.start(1000.Hz()); - provider -} - -/// This function can be called in a specified interrupt handler to increment -/// the MS counter -pub fn default_ms_irq_handler() { - critical_section::with(|cs| { - let mut ms = MS_COUNTER.borrow(cs).get(); - ms += 1; - MS_COUNTER.borrow(cs).set(ms); - }); -} - -/// Get the current MS tick count -pub fn get_ms_ticks() -> u32 { - critical_section::with(|cs| MS_COUNTER.borrow(cs).get()) -} - -//================================================================================================== -// Delay implementations -//================================================================================================== - -pub struct DelayMs(CountdownTimer); - -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 in TIM0 as a system tick -/// with [`set_up_ms_delay_provider`] -impl embedded_hal::delay::DelayNs for DelayMs { - fn delay_ns(&mut self, ns: u32) { - let ns_as_ms = ns / 1_000_000; - if self.0.curr_freq() != Hertz::from_raw(1000) || !self.0.listening() { - return; - } - let start_time = get_ms_ticks(); - while get_ms_ticks() - start_time < ns_as_ms { - cortex_m::asm::nop(); - } - } -} +pub use vorago_shared_periphs::timer::regs; diff --git a/va108xx-hal/src/typelevel.rs b/va108xx-hal/src/typelevel.rs deleted file mode 100644 index 7803c20..0000000 --- a/va108xx-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/va108xx-hal/src/uart/mod.rs b/va108xx-hal/src/uart/mod.rs index 929b6c2..49303f3 100644 --- a/va108xx-hal/src/uart/mod.rs +++ b/va108xx-hal/src/uart/mod.rs @@ -1,6 +1,6 @@ //! # 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. //! @@ -12,1345 +12,9 @@ //! - [UART simple example](https://egit.irs.uni-stuttgart.de/rust/va108xx-rs/src/branch/main/examples/simple/examples/uart.rs) //! - [UART with IRQ and RTIC](https://egit.irs.uni-stuttgart.de/rust/va108xx-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/va108xx-rs/src/branch/main/flashloader) -use core::{convert::Infallible, ops::Deref}; -use fugit::RateExtU32; +//! - [Async UART RX example](https://egit.irs.uni-stuttgart.de/rust/va108xx-rs/src/branch/main/examples/embassy/src/bin/async-uart-rx.rs) +//! - [Async UART TX example](https://egit.irs.uni-stuttgart.de/rust/va108xx-rs/src/branch/main/examples/embassy/src/bin/async-uart-tx.rs) +pub use vorago_shared_periphs::uart::*; -pub use crate::InterruptConfig; -use crate::{ - clock::enable_peripheral_clock, - enable_nvic_interrupt, - gpio::pin::{ - AltFunc1, AltFunc2, AltFunc3, Pin, PA16, PA17, PA18, PA19, PA2, PA26, PA27, PA3, PA30, - PA31, PA8, PA9, PB18, PB19, PB20, PB21, PB22, PB23, PB6, PB7, PB8, PB9, - }, - pac::{self, uarta as uart_base}, - time::Hertz, - PeripheralSelect, -}; -use embedded_hal_nb::serial::Read; - -#[derive(Debug, Clone, Copy)] -#[cfg_attr(feature = "defmt", derive(defmt::Format))] -pub enum Bank { - A = 0, - B = 1, -} - -//================================================================================================== -// Type-Level support -//================================================================================================== - -pub trait Pins {} - -impl Pins for (Pin, Pin) {} -impl Pins for (Pin, Pin) {} -impl Pins for (Pin, Pin) {} - -impl Pins for (Pin, Pin) {} -impl Pins for (Pin, Pin) {} - -impl Pins for (Pin, Pin) {} -impl Pins for (Pin, Pin) {} -impl Pins for (Pin, Pin) {} - -impl Pins for (Pin, Pin) {} -impl Pins for (Pin, Pin) {} -impl Pins for (Pin, Pin) {} - -//================================================================================================== -// Regular Definitions -//================================================================================================== - -#[derive(Debug, PartialEq, Eq, thiserror::Error)] -#[cfg_attr(feature = "defmt", derive(defmt::Format))] -#[error("no interrupt ID was set")] -pub struct NoInterruptIdWasSet; - -#[derive(Debug, PartialEq, Eq, thiserror::Error)] -#[cfg_attr(feature = "defmt", derive(defmt::Format))] -#[error("transer is pending")] -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 { - let baudrate = 115_200_u32.Hz(); - Config { - baudrate, - parity: Parity::None, - stopbits: StopBits::One, - baud8: false, - wordsize: WordSize::Eight, - enable_tx: true, - enable_rx: true, - } - } -} - -impl From for Config { - fn from(baud: Hertz) -> Self { - Config::default().baudrate(baud) - } -} - -//================================================================================================== -// 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 || self.other - } -} - -#[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; - - /// 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 pac::Uarta { - const IDX: u8 = 0; - - const PERIPH_SEL: PeripheralSelect = PeripheralSelect::Uart0; - const PTR: *const uart_base::RegisterBlock = Self::PTR; - - #[inline(always)] - unsafe fn steal() -> Self { - Self::steal() - } -} - -impl Instance for pac::Uartb { - const IDX: u8 = 1; - - const PERIPH_SEL: PeripheralSelect = PeripheralSelect::Uart1; - const PTR: *const uart_base::RegisterBlock = Self::PTR; - - #[inline(always)] - unsafe fn steal() -> Self { - Self::steal() - } -} - -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::A => unsafe { pac::Uarta::reg_block() }, - Bank::B => unsafe { pac::Uartb::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 { - /// This function assumes that the peripheral clock was alredy enabled - /// in the SYSCONFIG register - fn init(self, config: Config, sys_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 = ((sys_clk.raw() % (config.baudrate.raw() * 16)) * 64 - + (config.baudrate.raw() * 8)) - / (config.baudrate.raw() * 16); - // Calculations here are derived from chapter 4.8.5 (p.79) of the datasheet. - let x = sys_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()); - } - - #[inline] - pub fn clear_rx_status(&mut self) { - self.uart.fifo_clr().write(|w| w.rxsts().set_bit()); - } - - #[inline] - pub fn clear_tx_status(&mut self) { - self.uart.fifo_clr().write(|w| w.txsts().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() - }); - 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 Uart -where - UartInstance: Instance, - PinsInstance: Pins, -{ - /// Calls [Self::new] with the interrupt configuration to some valid value. - pub fn new_with_interrupt( - syscfg: &mut va108xx::Sysconfig, - sys_clk: impl Into, - uart: UartInstance, - pins: PinsInstance, - config: impl Into, - irq_cfg: InterruptConfig, - ) -> Self { - Self::new(syscfg, sys_clk, uart, pins, config, Some(irq_cfg)) - } - - /// Calls [Self::new] with the interrupt configuration to [None]. - pub fn new_without_interrupt( - syscfg: &mut va108xx::Sysconfig, - sys_clk: impl Into, - uart: UartInstance, - pins: PinsInstance, - config: impl Into, - ) -> Self { - Self::new(syscfg, sys_clk, uart, pins, config, None) - } - - /// Create a new UART peripheral with an interrupt configuration. - /// - /// # Arguments - /// - /// - `syscfg`: The system configuration register block - /// - `sys_clk`: The system clock frequency - /// - `uart`: The concrete UART peripheral instance. - /// - `pins`: UART TX and RX pin tuple. - /// - `config`: UART specific configuration parameters like baudrate. - /// - `irq_cfg`: Optional interrupt configuration. This should be a valid value if the plan - /// is to use TX or RX functionality relying on interrupts. If only the blocking API without - /// any interrupt support is used, this can be [None]. - pub fn new( - syscfg: &mut va108xx::Sysconfig, - sys_clk: impl Into, - uart: UartInstance, - pins: PinsInstance, - config: impl Into, - opt_irq_cfg: Option, - ) -> Self { - crate::clock::enable_peripheral_clock(syscfg, UartInstance::PERIPH_SEL); - let uart = Uart { - inner: UartBase { - uart, - tx: Tx::new(unsafe { UartInstance::steal() }), - rx: Rx::new(unsafe { UartInstance::steal() }), - }, - pins, - } - .init(config.into(), sys_clk.into()); - - if let Some(irq_cfg) = opt_irq_cfg { - if irq_cfg.route { - enable_peripheral_clock(syscfg, PeripheralSelect::Irqsel); - unsafe { pac::Irqsel::steal() } - .uart0(UartInstance::IDX as usize) - .write(|w| unsafe { w.bits(irq_cfg.id as u32) }); - } - if irq_cfg.enable_in_nvic { - // Safety: User has specifically configured this. - unsafe { enable_nvic_interrupt(irq_cfg.id) }; - } - } - uart - } - - /// This function assumes that the peripheral clock was alredy enabled - /// in the SYSCONFIG register - fn init(mut self, config: Config, sys_clk: Hertz) -> Self { - self.inner = self.inner.init(config, sys_clk); - self - } - - /// Poll receiver errors. - pub fn poll_rx_errors(&self) -> Option { - self.inner.poll_rx_errors() - } - - #[inline] - pub fn enable_rx(&mut self) { - self.inner.enable_rx(); - } - - #[inline] - pub fn disable_rx(&mut self) { - self.inner.enable_rx(); - } - - #[inline] - pub fn enable_tx(&mut self) { - self.inner.enable_tx(); - } - - #[inline] - pub fn disable_tx(&mut self) { - self.inner.disable_tx(); - } - - #[inline] - pub fn clear_rx_fifo(&mut self) { - self.inner.clear_rx_fifo(); - } - - #[inline] - pub fn clear_tx_fifo(&mut self) { - self.inner.clear_tx_fifo(); - } - - #[inline] - pub fn clear_rx_status(&mut self) { - self.inner.clear_rx_status(); - } - - #[inline] - pub fn clear_tx_status(&mut self) { - self.inner.clear_tx_status(); - } - - pub fn listen(&self, event: Event) { - self.inner.listen(event); - } - - pub fn unlisten(&self, event: Event) { - self.inner.unlisten(event); - } - - pub fn release(self) -> (UartInstance, PinsInstance) { - (self.inner.release(), self.pins) - } - - pub fn downgrade(self) -> UartBase { - UartBase { - uart: self.inner.uart, - tx: self.inner.tx, - rx: self.inner.rx, - } - } - - pub fn split(self) -> (Tx, Rx) { - self.inner.split() - } -} - -#[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 { - #[inline(always)] - const 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. - #[inline(always)] - pub const 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 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() }); - } - - #[inline] - pub fn enable(&mut self) { - enable_rx(unsafe { Uart::reg_block() }); - } - - #[inline] - pub fn disable(&mut self) { - disable_rx(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 4.6.2 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 4.6.2 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::new(self) - } - - #[inline(always)] - 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) }); - } - - 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::on_interrupt] 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::on_interrupt_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 { - pub fn new(rx: Rx) -> Self { - Self(rx) - } - - /// This function should be called once at initialization time if the regular - /// [Self::on_interrupt] 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); - } - - #[inline(always)] - pub fn uart(&self) -> &Uart { - &self.0 .0 - } - - /// This function is used together with the [Self::on_interrupt_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::on_interrupt_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::*; +pub use vorago_shared_periphs::uart::rx_asynch; +pub use vorago_shared_periphs::uart::tx_asynch; diff --git a/va108xx-hal/src/uart/rx_asynch.rs b/va108xx-hal/src/uart/rx_asynch.rs deleted file mode 100644 index 215bfae..0000000 --- a/va108xx-hal/src/uart/rx_asynch.rs +++ /dev/null @@ -1,440 +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 va108xx::uarta as uart_base; - -use super::{Bank, Instance, Rx, UartErrors}; - -static UART_RX_WAKERS: [AtomicWaker; 2] = [const { AtomicWaker::new() }; 2]; -static RX_READ_ACTIVE: [AtomicBool; 2] = [const { AtomicBool::new(false) }; 2]; -static RX_HAS_DATA: [AtomicBool; 2] = [const { AtomicBool::new(false) }; 2]; - -struct RxFuture { - uart_idx: usize, -} - -impl RxFuture { - pub fn new(_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(); - 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(|_| { - 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/va108xx-hal/src/uart/tx_asynch.rs b/va108xx-hal/src/uart/tx_asynch.rs deleted file mode 100644 index 3dc36f7..0000000 --- a/va108xx-hal/src/uart/tx_asynch.rs +++ /dev/null @@ -1,254 +0,0 @@ -//! # Async UART transmission functionality for the VA108xx family. -//! -//! This module provides the [TxAsync] struct which implements the [embedded_io_async::Write] trait. -//! This trait allows for asynchronous sending of data streams. Please note that this module does -//! not specify/declare the interrupt handlers which must be provided for async support to work. -//! However, it the [on_interrupt_tx] interrupt handler. -//! -//! This handler should be called in ALL user interrupt handlers which handle UART TX interrupts -//! for a given UART bank. -//! -//! # Example -//! -//! - [Async UART TX example](https://egit.irs.uni-stuttgart.de/rust/va108xx-rs/src/branch/main/examples/embassy/src/bin/async-uart-tx.rs) -use core::{cell::RefCell, future::Future}; - -use critical_section::Mutex; -use embassy_sync::waitqueue::AtomicWaker; -use embedded_io_async::Write; -use portable_atomic::AtomicBool; - -use super::*; - -static UART_TX_WAKERS: [AtomicWaker; 2] = [const { AtomicWaker::new() }; 2]; -static TX_CONTEXTS: [Mutex>; 2] = - [const { Mutex::new(RefCell::new(TxContext::new())) }; 2]; -// Completion flag. Kept outside of the context structure as an atomic to avoid -// critical section. -static TX_DONE: [AtomicBool; 2] = [const { AtomicBool::new(false) }; 2]; - -/// This is a generic interrupt handler to handle asynchronous UART TX operations for a given -/// UART bank. -/// -/// The user has to call this once in the interrupt handler responsible for the TX interrupts on -/// the given UART bank. -pub fn on_interrupt_tx(bank: Bank) { - let uart = unsafe { bank.reg_block() }; - let idx = bank as usize; - let irq_enb = uart.irq_enb().read(); - // IRQ is not related to TX. - if irq_enb.irq_tx().bit_is_clear() || irq_enb.irq_tx_empty().bit_is_clear() { - return; - } - - let tx_status = uart.txstatus().read(); - let unexpected_overrun = tx_status.wrlost().bit_is_set(); - let mut context = critical_section::with(|cs| { - let context_ref = TX_CONTEXTS[idx].borrow(cs); - *context_ref.borrow() - }); - context.tx_overrun = unexpected_overrun; - if context.progress >= context.slice.len && !tx_status.wrbusy().bit_is_set() { - uart.irq_enb().modify(|_, w| { - w.irq_tx().clear_bit(); - w.irq_tx_empty().clear_bit(); - w.irq_tx_status().clear_bit() - }); - uart.enable().modify(|_, w| w.txenable().clear_bit()); - // Write back updated context structure. - critical_section::with(|cs| { - let context_ref = TX_CONTEXTS[idx].borrow(cs); - *context_ref.borrow_mut() = context; - }); - // Transfer is done. - TX_DONE[idx].store(true, core::sync::atomic::Ordering::Relaxed); - UART_TX_WAKERS[idx].wake(); - return; - } - // Safety: We documented that the user provided slice must outlive the future, so we convert - // the raw pointer back to the slice here. - let slice = unsafe { core::slice::from_raw_parts(context.slice.data, context.slice.len) }; - while context.progress < context.slice.len { - let wrrdy = uart.txstatus().read().wrrdy().bit_is_set(); - if !wrrdy { - break; - } - // Safety: TX structure is owned by the future which does not write into the the data - // register, so we can assume we are the only one writing to the data register. - uart.data() - .write(|w| unsafe { w.bits(slice[context.progress] as u32) }); - context.progress += 1; - } - - // Write back updated context structure. - critical_section::with(|cs| { - let context_ref = TX_CONTEXTS[idx].borrow(cs); - *context_ref.borrow_mut() = context; - }); -} - -#[derive(Debug, Copy, Clone)] -pub struct TxContext { - progress: usize, - tx_overrun: bool, - slice: RawBufSlice, -} - -#[allow(clippy::new_without_default)] -impl TxContext { - pub const fn new() -> Self { - Self { - progress: 0, - tx_overrun: false, - slice: RawBufSlice::new_empty(), - } - } -} - -#[derive(Debug, Copy, Clone)] -struct RawBufSlice { - data: *const u8, - len: usize, -} - -/// Safety: This type MUST be used with mutex to ensure concurrent access is valid. -unsafe impl Send for RawBufSlice {} - -impl RawBufSlice { - /// # Safety - /// - /// This function stores the raw pointer of the passed data slice. The user MUST ensure - /// that the slice outlives the data structure. - #[allow(dead_code)] - const unsafe fn new(data: &[u8]) -> Self { - Self { - data: data.as_ptr(), - len: data.len(), - } - } - - const fn new_empty() -> Self { - Self { - data: core::ptr::null(), - len: 0, - } - } - - /// # Safety - /// - /// This function stores the raw pointer of the passed data slice. The user MUST ensure - /// that the slice outlives the data structure. - pub unsafe fn set(&mut self, data: &[u8]) { - self.data = data.as_ptr(); - self.len = data.len(); - } -} - -pub struct TxFuture { - uart_idx: usize, -} - -impl TxFuture { - /// # Safety - /// - /// This function stores the raw pointer of the passed data slice. The user MUST ensure - /// that the slice outlives the data structure. - pub unsafe fn new(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::Uarta::reg_block() }, - 1 => unsafe { pac::Uartb::reg_block() }, - _ => unreachable!(), - }; - - disable_tx_interrupts(reg_block); - disable_tx(reg_block); - } -} - -pub struct TxAsync { - tx: Tx, -} - -impl TxAsync { - pub fn new(tx: Tx) -> Self { - Self { tx } - } - - pub fn release(self) -> 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/vorago-reb1/Cargo.toml b/vorago-reb1/Cargo.toml index ff242e0..c32c151 100644 --- a/vorago-reb1/Cargo.toml +++ b/vorago-reb1/Cargo.toml @@ -15,10 +15,11 @@ cortex-m = { version = "0.7", features = ["critical-section-single-core"] } cortex-m-rt = "0.7" embedded-hal = "1" nb = "1" -bitfield = ">=0.17, <=0.19" +bitbybit = "1.3" +arbitrary-int = "1.3" max116xx-10bit = "0.3" -va108xx-hal = { version = ">=0.10, <=0.11", features = ["rt"] } +va108xx-hal = { version = ">=0.10, <=0.11", path = "../va108xx-hal", features = ["rt"] } [features] rt = ["va108xx-hal/rt"] diff --git a/vorago-reb1/examples/adt75-temp-sensor.rs b/vorago-reb1/examples/adt75-temp-sensor.rs index 40187bd..bf02c53 100644 --- a/vorago-reb1/examples/adt75-temp-sensor.rs +++ b/vorago-reb1/examples/adt75-temp-sensor.rs @@ -4,17 +4,17 @@ use cortex_m_rt::entry; use embedded_hal::delay::DelayNs; use panic_rtt_target as _; use rtt_target::{rprintln, rtt_init_print}; -use va108xx_hal::{pac, prelude::*, timer::set_up_ms_delay_provider}; +use va108xx_hal::{pac, prelude::*, timer::CountdownTimer}; use vorago_reb1::temp_sensor::Adt75TempSensor; #[entry] fn main() -> ! { rtt_init_print!(); rprintln!("-- Vorago Temperature Sensor and I2C Example --"); - let mut dp = pac::Peripherals::take().unwrap(); - let mut delay = set_up_ms_delay_provider(&mut dp.sysconfig, 50.MHz(), dp.tim0); - let mut temp_sensor = Adt75TempSensor::new(&mut dp.sysconfig, 50.MHz(), dp.i2ca) - .expect("Creating temperature sensor struct failed"); + let dp = pac::Peripherals::take().unwrap(); + let mut delay = CountdownTimer::new(50.MHz(), dp.tim0); + let mut temp_sensor = + Adt75TempSensor::new(50.MHz(), dp.i2ca).expect("Creating temperature sensor struct failed"); loop { let temp = temp_sensor .read_temperature() diff --git a/vorago-reb1/examples/adxl343-accelerometer.rs b/vorago-reb1/examples/adxl343-accelerometer.rs index ce7be6c..f23355e 100644 --- a/vorago-reb1/examples/adxl343-accelerometer.rs +++ b/vorago-reb1/examples/adxl343-accelerometer.rs @@ -9,13 +9,14 @@ use embedded_hal::delay::DelayNs; use embedded_hal::spi::{SpiBus, MODE_3}; use panic_rtt_target as _; use rtt_target::{rprintln, rtt_init_print}; -use va108xx_hal::spi::SpiClkConfig; +use va108xx_hal::gpio::{Output, PinState}; +use va108xx_hal::pins::PinsA; +use va108xx_hal::spi::{configure_pin_as_hw_cs_pin, SpiClkConfig}; +use va108xx_hal::timer::CountdownTimer; use va108xx_hal::{ - gpio::PinsA, pac, prelude::*, spi::{Spi, SpiConfig}, - timer::set_up_ms_delay_provider, }; const READ_MASK: u8 = 1 << 7; @@ -29,19 +30,15 @@ const PWR_MEASUREMENT_MODE_MASK: u8 = 1 << 3; fn main() -> ! { rtt_init_print!(); rprintln!("-- Vorago Accelerometer Example --"); - let mut dp = pac::Peripherals::take().unwrap(); - let mut delay = set_up_ms_delay_provider(&mut dp.sysconfig, 50.MHz(), dp.tim0); - let pinsa = PinsA::new(&mut dp.sysconfig, dp.porta); - let (sck, mosi, miso) = ( - pinsa.pa20.into_funsel_2(), - pinsa.pa19.into_funsel_2(), - pinsa.pa18.into_funsel_2(), - ); - let cs_pin = pinsa.pa16.into_funsel_2(); + let dp = pac::Peripherals::take().unwrap(); + let pinsa = PinsA::new(dp.porta); + let mut delay = CountdownTimer::new(50.MHz(), dp.tim0); + let (sck, mosi, miso) = (pinsa.pa20, pinsa.pa19, pinsa.pa18); + let cs_pin = pinsa.pa16; + let hw_cs_id = configure_pin_as_hw_cs_pin(cs_pin); // Need to set the ADC chip select low - let mut adc_cs = pinsa.pa17.into_push_pull_output(); - adc_cs.set_high(); + Output::new(pinsa.pa17, PinState::Low); let spi_cfg = SpiConfig::default() .clk_cfg( @@ -49,14 +46,8 @@ fn main() -> ! { ) .mode(MODE_3) .slave_output_disable(true); - let mut spi = Spi::new( - &mut dp.sysconfig, - 50.MHz(), - dp.spib, - (sck, miso, mosi), - spi_cfg, - ); - spi.cfg_hw_cs_with_pin(&cs_pin); + let mut spi = Spi::new(50.MHz(), dp.spib, (sck, miso, mosi), spi_cfg).unwrap(); + spi.cfg_hw_cs(hw_cs_id); let mut tx_rx_buf: [u8; 3] = [0; 3]; tx_rx_buf[0] = READ_MASK | DEVID_REG; diff --git a/vorago-reb1/examples/blinky-button-irq.rs b/vorago-reb1/examples/blinky-button-irq.rs index bc113fc..36fac07 100644 --- a/vorago-reb1/examples/blinky-button-irq.rs +++ b/vorago-reb1/examples/blinky-button-irq.rs @@ -10,10 +10,10 @@ use panic_rtt_target as _; use rtt_target::{rprintln, rtt_init_print}; use va108xx_hal::{ clock::{set_clk_div_register, FilterClkSel}, - gpio::{FilterType, InterruptEdge, PinsA}, + gpio::{FilterType, InterruptEdge}, pac::{self, interrupt}, - prelude::*, - timer::{default_ms_irq_handler, set_up_ms_tick, InterruptConfig}, + pins::PinsA, + timer::InterruptConfig, }; use vorago_reb1::button::Button; use vorago_reb1::leds::Leds; @@ -35,18 +35,18 @@ fn main() -> ! { rtt_init_print!(); rprintln!("-- Vorago Button IRQ Example --"); let mut dp = pac::Peripherals::take().unwrap(); - let pinsa = PinsA::new(&mut dp.sysconfig, dp.porta); + let pinsa = PinsA::new(dp.porta); let edge_irq = match PRESS_MODE { PressMode::Toggle => InterruptEdge::HighToLow, PressMode::Keep => InterruptEdge::BothEdges, }; // Configure an edge interrupt on the button and route it to interrupt vector 15 - let mut button = Button::new(pinsa.pa11.into_floating_input()); + let mut button = Button::new(pinsa.pa11); if PRESS_MODE == PressMode::Toggle { // This filter debounces the switch for edge based interrupts - button.configure_filter_type(FilterType::FilterFourClockCycles, FilterClkSel::Clk1); + button.configure_filter_type(FilterType::FilterFourCycles, FilterClkSel::Clk1); set_clk_div_register(&mut dp.sysconfig, FilterClkSel::Clk1, 50_000); } button.configure_and_enable_edge_interrupt( @@ -54,18 +54,7 @@ fn main() -> ! { InterruptConfig::new(pac::interrupt::OC15, true, true), ); - set_up_ms_tick( - InterruptConfig::new(pac::Interrupt::OC0, true, true), - &mut dp.sysconfig, - Some(&mut dp.irqsel), - 50.MHz(), - dp.tim0, - ); - let mut leds = Leds::new( - pinsa.pa10.into_push_pull_output(), - pinsa.pa7.into_push_pull_output(), - pinsa.pa6.into_push_pull_output(), - ); + let mut leds = Leds::new(pinsa.pa10, pinsa.pa7, pinsa.pa6); for led in leds.iter_mut() { led.off(); } @@ -79,11 +68,6 @@ fn main() -> ! { } } -#[interrupt] -fn OC0() { - default_ms_irq_handler(); -} - #[interrupt] fn OC15() { cortex_m::interrupt::free(|cs| { diff --git a/vorago-reb1/examples/blinky-leds.rs b/vorago-reb1/examples/blinky-leds.rs index de9fbd5..b5aea2c 100644 --- a/vorago-reb1/examples/blinky-leds.rs +++ b/vorago-reb1/examples/blinky-leds.rs @@ -9,7 +9,13 @@ use cortex_m_rt::entry; use embedded_hal::delay::DelayNs; use panic_halt as _; -use va108xx_hal::{gpio::PinsA, pac, prelude::*, timer::set_up_ms_delay_provider}; +use va108xx_hal::{ + gpio::{Output, PinState}, + pac, + pins::PinsA, + prelude::*, + timer::CountdownTimer, +}; use vorago_reb1::leds::Leds; // REB LED pin definitions. All on port A @@ -26,7 +32,7 @@ enum LibType { #[entry] fn main() -> ! { - let mut dp = pac::Peripherals::take().unwrap(); + let dp = pac::Peripherals::take().unwrap(); let lib_type = LibType::Bsp; @@ -60,11 +66,11 @@ fn main() -> ! { } } LibType::Hal => { - let pins = PinsA::new(&mut dp.sysconfig, dp.porta); - let mut led1 = pins.pa10.into_readable_push_pull_output(); - let mut led2 = pins.pa7.into_readable_push_pull_output(); - let mut led3 = pins.pa6.into_readable_push_pull_output(); - let mut delay = set_up_ms_delay_provider(&mut dp.sysconfig, 50.MHz(), dp.tim0); + let pins = PinsA::new(dp.porta); + let mut led1 = Output::new(pins.pa10, PinState::Low); + let mut led2 = Output::new(pins.pa7, PinState::Low); + let mut led3 = Output::new(pins.pa6, PinState::Low); + let mut delay = CountdownTimer::new(50.MHz(), dp.tim0); for _ in 0..10 { led1.set_low(); led2.set_low(); @@ -83,13 +89,9 @@ fn main() -> ! { } } LibType::Bsp => { - let pinsa = PinsA::new(&mut dp.sysconfig, dp.porta); - let mut leds = Leds::new( - pinsa.pa10.into_push_pull_output(), - pinsa.pa7.into_push_pull_output(), - pinsa.pa6.into_push_pull_output(), - ); - let mut delay = set_up_ms_delay_provider(&mut dp.sysconfig, 50.MHz(), dp.tim0); + let pinsa = PinsA::new(dp.porta); + let mut leds = Leds::new(pinsa.pa10, pinsa.pa7, pinsa.pa6); + let mut delay = CountdownTimer::new(50.MHz(), dp.tim0); for _ in 0..10 { // Blink all LEDs quickly for led in leds.iter_mut() { diff --git a/vorago-reb1/examples/max11619-adc.rs b/vorago-reb1/examples/max11619-adc.rs index 14c03c5..636479d 100644 --- a/vorago-reb1/examples/max11619-adc.rs +++ b/vorago-reb1/examples/max11619-adc.rs @@ -14,20 +14,19 @@ use max116xx_10bit::VoltageRefMode; use max116xx_10bit::{AveragingConversions, AveragingResults}; use panic_rtt_target as _; use rtt_target::{rprintln, rtt_init_print}; -use va108xx_hal::gpio::Port; -use va108xx_hal::spi::{OptionalHwCs, SpiClkConfig}; +use va108xx_hal::gpio::{Input, Output, PinState, Port}; +use va108xx_hal::pins::PinsA; +use va108xx_hal::spi::{configure_pin_as_hw_cs_pin, SpiClkConfig}; use va108xx_hal::timer::CountdownTimer; use va108xx_hal::{ - gpio::PinsA, - pac::{self, interrupt}, + pac, prelude::*, - spi::{Spi, SpiBase, SpiConfig}, - timer::{default_ms_irq_handler, set_up_ms_tick, DelayMs, InterruptConfig}, + spi::{HwChipSelectId, Spi, SpiConfig}, }; use va108xx_hal::{port_function_select, FunSel}; use vorago_reb1::max11619::{ max11619_externally_clocked_no_wakeup, max11619_externally_clocked_with_wakeup, - max11619_internally_clocked, EocPin, AN2_CHANNEL, POTENTIOMETER_CHANNEL, + max11619_internally_clocked, AN2_CHANNEL, POTENTIOMETER_CHANNEL, }; #[derive(Debug, PartialEq, Copy, Clone)] @@ -57,34 +56,34 @@ const MUX_MODE: MuxMode = MuxMode::None; // This is probably more or less a re-implementation of https://docs.rs/embedded-hal-bus/latest/embedded_hal_bus/spi/struct.ExclusiveDevice.html. // Users should look at the embedded-hal-bus crate for sharing the bus. -pub struct SpiWithHwCs { - inner: SpiBase, +pub struct SpiWithHwCs { + inner: Spi, delay_provider: Delay, - hw_cs: HwCs, + hw_cs_id: HwChipSelectId, } -impl> SpiWithHwCs { - pub fn new(spi: SpiBase, hw_cs: HwCs, delay_provider: Delay) -> Self { +impl SpiWithHwCs { + pub fn new(spi: Spi, hw_cs_id: HwChipSelectId, delay_provider: Delay) -> Self { Self { inner: spi, - hw_cs, + hw_cs_id, delay_provider, } } } -impl embedded_hal::spi::ErrorType for SpiWithHwCs { +impl embedded_hal::spi::ErrorType for SpiWithHwCs { type Error = Infallible; } -impl> SpiDevice for SpiWithHwCs { +impl SpiDevice for SpiWithHwCs { fn transaction( &mut self, operations: &mut [spi::Operation<'_, u8>], ) -> Result<(), Self::Error> { // Only the HW CS is configured here. This is not really necessary, but showcases // that we could scale this multiple SPI devices. - self.inner.cfg_hw_cs_with_pin(&self.hw_cs); + self.inner.cfg_hw_cs(self.hw_cs_id); for operation in operations { match operation { spi::Operation::Read(buf) => self.inner.read(buf).ok().unwrap(), @@ -111,28 +110,17 @@ fn main() -> ! { rprintln!("-- Vorago ADC Example --"); let mut dp = pac::Peripherals::take().unwrap(); - let tim0 = set_up_ms_tick( - InterruptConfig::new(pac::Interrupt::OC0, true, true), - &mut dp.sysconfig, - Some(&mut dp.irqsel), - SYS_CLK, - dp.tim0, - ); - let delay = DelayMs::new(tim0).unwrap(); + let mut delay = CountdownTimer::new(SYS_CLK, dp.tim0); unsafe { cortex_m::peripheral::NVIC::unmask(pac::Interrupt::OC0); } - let pinsa = PinsA::new(&mut dp.sysconfig, dp.porta); + let pinsa = PinsA::new(dp.porta); let spi_cfg = SpiConfig::default() .clk_cfg(SpiClkConfig::from_clk(SYS_CLK, 3.MHz()).unwrap()) .mode(MODE_0) .blockmode(true); - let (sck, mosi, miso) = ( - pinsa.pa20.into_funsel_2(), - pinsa.pa19.into_funsel_2(), - pinsa.pa18.into_funsel_2(), - ); + let (sck, mosi, miso) = (pinsa.pa20, pinsa.pa19, pinsa.pa18); if MUX_MODE == MuxMode::PortB19to17 { port_function_select(&mut dp.ioconfig, Port::B, 19, FunSel::Sel1).ok(); @@ -141,39 +129,30 @@ fn main() -> ! { port_function_select(&mut dp.ioconfig, Port::B, 16, FunSel::Sel1).ok(); } // Set the accelerometer chip select low in case the board slot is populated - let mut accel_cs = pinsa.pa16.into_push_pull_output(); - accel_cs.set_high(); + Output::new(pinsa.pa16, PinState::Low); - let spi = Spi::new( - &mut dp.sysconfig, - 50.MHz(), - dp.spib, - (sck, miso, mosi), - spi_cfg, - ) - .downgrade(); - let delay_provider = CountdownTimer::new(&mut dp.sysconfig, 50.MHz(), dp.tim1); - let spi_with_hwcs = SpiWithHwCs::new(spi, pinsa.pa17.into_funsel_2(), delay_provider); + let hw_cs_id = configure_pin_as_hw_cs_pin(pinsa.pa17); + let spi = Spi::new(50.MHz(), dp.spib, (sck, miso, mosi), spi_cfg).unwrap(); + + let delay_spi = CountdownTimer::new(SYS_CLK, dp.tim1); + let spi_with_hwcs = SpiWithHwCs::new(spi, hw_cs_id, delay_spi); match EXAMPLE_MODE { - ExampleMode::NotUsingEoc => spi_example_externally_clocked(spi_with_hwcs, delay), + ExampleMode::NotUsingEoc => spi_example_externally_clocked(spi_with_hwcs, &mut delay), ExampleMode::UsingEoc => { - spi_example_internally_clocked(spi_with_hwcs, delay, pinsa.pa14.into_floating_input()); + spi_example_internally_clocked( + spi_with_hwcs, + &mut delay, + Input::new_floating(pinsa.pa14), + ); } ExampleMode::NotUsingEocWithDelay => { - let delay_us = CountdownTimer::new(&mut dp.sysconfig, 50.MHz(), dp.tim2); - spi_example_externally_clocked_with_delay(spi_with_hwcs, delay, delay_us); + spi_example_externally_clocked_with_delay(spi_with_hwcs, &mut delay); } } } -#[interrupt] -#[allow(non_snake_case)] -fn OC0() { - default_ms_irq_handler(); -} - /// Use the SPI clock as the conversion clock -fn spi_example_externally_clocked(spi: impl SpiDevice, mut delay: DelayMs) -> ! { +fn spi_example_externally_clocked(spi: impl SpiDevice, delay: &mut impl DelayNs) -> ! { let mut adc = max11619_externally_clocked_no_wakeup(spi) .expect("Creating externally clocked MAX11619 device failed"); if READ_MODE == ReadMode::AverageN { @@ -228,11 +207,7 @@ fn spi_example_externally_clocked(spi: impl SpiDevice, mut delay: DelayMs) -> ! } } -fn spi_example_externally_clocked_with_delay( - spi: impl SpiDevice, - mut delay: DelayMs, - mut delay_us: impl DelayNs, -) -> ! { +fn spi_example_externally_clocked_with_delay(spi: impl SpiDevice, delay: &mut impl DelayNs) -> ! { let mut adc = max11619_externally_clocked_with_wakeup(spi).expect("Creating MAX116xx device failed"); let mut cmd_buf: [u8; 32] = [0; 32]; @@ -244,7 +219,7 @@ fn spi_example_externally_clocked_with_delay( ReadMode::Single => { rprintln!("Reading single potentiometer channel"); let pot_val = adc - .read_single_channel(&mut cmd_buf, POTENTIOMETER_CHANNEL, &mut delay_us) + .read_single_channel(&mut cmd_buf, POTENTIOMETER_CHANNEL, delay) .expect("Creating externally clocked MAX11619 ADC failed"); rprintln!("Single channel read:"); rprintln!("\tPotentiometer value: {}", pot_val); @@ -255,7 +230,7 @@ fn spi_example_externally_clocked_with_delay( &mut cmd_buf, &mut res_buf.iter_mut(), POTENTIOMETER_CHANNEL, - &mut delay_us, + delay, ) .expect("Multi-Channel read failed"); print_res_buf(&res_buf); @@ -266,7 +241,7 @@ fn spi_example_externally_clocked_with_delay( &mut cmd_buf, &mut res_buf.iter_mut(), AN2_CHANNEL, - &mut delay_us, + delay, ) .expect("Multi-Channel read failed"); rprintln!("Multi channel read from 2 to 3:"); @@ -283,7 +258,11 @@ fn spi_example_externally_clocked_with_delay( } /// This function uses the EOC pin to determine whether the conversion finished -fn spi_example_internally_clocked(spi: impl SpiDevice, mut delay: DelayMs, eoc_pin: EocPin) -> ! { +fn spi_example_internally_clocked( + spi: impl SpiDevice, + delay: &mut impl DelayNs, + eoc_pin: Input, +) -> ! { let mut adc = max11619_internally_clocked( spi, eoc_pin, diff --git a/vorago-reb1/examples/nvm.rs b/vorago-reb1/examples/nvm.rs index ee7c3d7..38434b7 100644 --- a/vorago-reb1/examples/nvm.rs +++ b/vorago-reb1/examples/nvm.rs @@ -15,12 +15,12 @@ fn main() -> ! { rtt_init_print!(); rprintln!("-- VA108XX REB1 NVM example --"); - let mut dp = pac::Peripherals::take().unwrap(); + let dp = pac::Peripherals::take().unwrap(); - let mut timer = CountdownTimer::new(&mut dp.sysconfig, CLOCK_FREQ, dp.tim0); - let mut nvm = M95M01::new(&mut dp.sysconfig, CLOCK_FREQ, dp.spic); + let mut timer = CountdownTimer::new(CLOCK_FREQ, dp.tim0); + let mut nvm = M95M01::new(CLOCK_FREQ, dp.spic); let status_reg = nvm.read_status_reg().expect("reading status reg failed"); - if status_reg.zero_segment() == 0b111 { + if status_reg.zero_segment().value() == 0b111 { panic!("status register unexpected values"); } diff --git a/vorago-reb1/src/button.rs b/vorago-reb1/src/button.rs index a16ff7f..1d261f7 100644 --- a/vorago-reb1/src/button.rs +++ b/vorago-reb1/src/button.rs @@ -5,16 +5,18 @@ //! - [Button Blinky with IRQs](https://egit.irs.uni-stuttgart.de/rust/va108xx-rs/src/branch/main/vorago-reb1/examples/blinky-button-irq.rs) //! - [Button Blinky with IRQs and RTIC](https://egit.irs.uni-stuttgart.de/rust/va108xx-rs/src/branch/main/vorago-reb1/examples/blinky-button-rtic.rs) use va108xx_hal::{ - gpio::{FilterClkSel, FilterType, InputFloating, InterruptEdge, InterruptLevel, Pin, PA11}, + clock::FilterClkSel, + gpio::{FilterType, Input, InterruptEdge, InterruptLevel, Pin}, + pins::Pa11, InterruptConfig, }; #[derive(Debug)] -pub struct Button(pub Pin); +pub struct Button(pub Input); impl Button { - pub fn new(pin: Pin) -> Button { - Button(pin) + pub fn new(pin: Pin) -> Button { + Button(Input::new_floating(pin)) } #[inline] diff --git a/vorago-reb1/src/leds.rs b/vorago-reb1/src/leds.rs index dfa9ec1..560e5a2 100644 --- a/vorago-reb1/src/leds.rs +++ b/vorago-reb1/src/leds.rs @@ -6,20 +6,20 @@ //! - [Button Blinky using IRQs](https://egit.irs.uni-stuttgart.de/rust/va108xx-rs/src/branch/main/vorago-reb1/examples/blinky-button-irq.rs) //! - [Button Blinky using IRQs and RTIC](https://egit.irs.uni-stuttgart.de/rust/va108xx-rs/src/branch/main/vorago-reb1/examples/blinky-button-rtic.rs) use va108xx_hal::{ - gpio::dynpin::DynPin, - gpio::pin::{Pin, PushPullOutput, PA10, PA6, PA7}, + gpio::{Output, PinState}, + pins::{Pa10, Pa6, Pa7, Pin}, }; -pub type LD2 = Pin; -pub type LD3 = Pin; -pub type LD4 = Pin; - #[derive(Debug)] pub struct Leds(pub [Led; 3]); impl Leds { - pub fn new(led_pin1: LD2, led_pin2: LD3, led_pin3: LD4) -> Leds { - Leds([led_pin1.into(), led_pin2.into(), led_pin3.into()]) + pub fn new(led_pin1: Pin, led_pin2: Pin, led_pin3: Pin) -> Leds { + Leds([ + Led(Output::new(led_pin1, PinState::Low)), + Led(Output::new(led_pin2, PinState::Low)), + Led(Output::new(led_pin3, PinState::Low)), + ]) } } @@ -52,38 +52,24 @@ impl core::ops::IndexMut for Leds { } #[derive(Debug)] -pub struct Led(pub DynPin); - -macro_rules! ctor { - ($($ldx:ident),+) => { - $( - impl From<$ldx> for Led { - fn from(led: $ldx) -> Self { - Led(led.into()) - } - } - )+ - } -} - -ctor!(LD2, LD3, LD4); +pub struct Led(Output); impl Led { /// Turns the LED off. Setting the pin high actually turns the LED off #[inline] pub fn off(&mut self) { - self.0.set_high().ok(); + self.0.set_high(); } /// Turns the LED on. Setting the pin low actually turns the LED on #[inline] pub fn on(&mut self) { - self.0.set_low().ok(); + self.0.set_low(); } /// Toggles the LED #[inline] pub fn toggle(&mut self) { - self.0.toggle().ok(); + self.0.toggle(); } } diff --git a/vorago-reb1/src/m95m01.rs b/vorago-reb1/src/m95m01.rs index 528b9b3..c83a2ef 100644 --- a/vorago-reb1/src/m95m01.rs +++ b/vorago-reb1/src/m95m01.rs @@ -7,20 +7,25 @@ //! # Example //! //! - [REB1 EEPROM example](https://egit.irs.uni-stuttgart.de/rust/va108xx-rs/src/branch/main/vorago-reb1/examples/nvm.rs) +use arbitrary_int::{u2, u3}; use core::convert::Infallible; use embedded_hal::spi::SpiBus; pub const PAGE_SIZE: usize = 256; -bitfield::bitfield! { - pub struct StatusReg(u8); - impl Debug; - u8; - pub status_register_write_protect, _: 7; - pub zero_segment, _: 6, 4; - pub block_protection_bits, set_block_protection_bits: 3, 2; - pub write_enable_latch, _: 1; - pub write_in_progress, _: 0; +#[bitbybit::bitfield(u8)] +#[derive(Debug)] +pub struct StatusReg { + #[bit(7, r)] + status_register_write_protect: bool, + #[bits(4..=6, r)] + zero_segment: u3, + #[bits(2..=3, rw)] + block_protection_bits: u2, + #[bit(1, r)] + write_enable_latch: bool, + #[bit(0, r)] + write_in_progress: bool, } // Registers. @@ -43,10 +48,10 @@ use regs::*; use va108xx_hal::{ pac, prelude::*, - spi::{RomMiso, RomMosi, RomSck, Spi, SpiClkConfig, SpiConfig, BMSTART_BMSTOP_MASK}, + spi::{Spi, SpiClkConfig, SpiConfig, SpiLowLevel, BMSTART_BMSTOP_MASK}, }; -pub type RomSpi = Spi; +pub type RomSpi = Spi; /// Driver for the ST device M95M01 EEPROM memory. /// @@ -59,14 +64,13 @@ pub struct M95M01 { pub struct PageBoundaryExceededError; impl M95M01 { - pub fn new(syscfg: &mut pac::Sysconfig, sys_clk: impl Into, spi: pac::Spic) -> Self { - let spi = RomSpi::new( - syscfg, + pub fn new(sys_clk: Hertz, spi: pac::Spic) -> Self { + let spi = RomSpi::new_for_rom( sys_clk, spi, - (RomSck, RomMiso, RomMosi), SpiConfig::default().clk_cfg(SpiClkConfig::new(2, 4)), - ); + ) + .unwrap(); let mut spi_dev = Self { spi }; spi_dev.clear_block_protection().unwrap(); spi_dev @@ -74,7 +78,7 @@ impl M95M01 { pub fn release(mut self) -> pac::Spic { self.set_block_protection().unwrap(); - self.spi.release().0 + unsafe { pac::Spic::steal() } } // Wait until the write-in-progress state is cleared. This exposes a [nb] API, so this function @@ -90,7 +94,7 @@ impl M95M01 { pub fn read_status_reg(&mut self) -> Result { let mut write_read: [u8; 2] = [regs::RDSR, 0x00]; self.spi.transfer_in_place(&mut write_read)?; - Ok(StatusReg(write_read[1])) + Ok(StatusReg::new_with_raw_value(write_read[1])) } pub fn write_enable(&mut self) -> Result<(), Infallible> { @@ -104,10 +108,10 @@ impl M95M01 { } pub fn set_block_protection(&mut self) -> Result<(), Infallible> { - let mut reg = StatusReg(0); - reg.set_block_protection_bits(0b11); + let mut reg = StatusReg::new_with_raw_value(0); + reg.set_block_protection_bits(u2::new(0b11)); self.write_enable()?; - self.spi.write(&[WRSR, reg.0]) + self.spi.write(&[WRSR, reg.raw_value()]) } fn common_init_write_and_read(&mut self, address: usize, reg: u8) -> Result<(), Infallible> { diff --git a/vorago-reb1/src/max11619.rs b/vorago-reb1/src/max11619.rs index c4805ed..eb15b82 100644 --- a/vorago-reb1/src/max11619.rs +++ b/vorago-reb1/src/max11619.rs @@ -9,7 +9,7 @@ use max116xx_10bit::{ Error, ExternallyClocked, InternallyClockedInternallyTimedSerialInterface, Max116xx10Bit, Max116xx10BitEocExt, VoltageRefMode, WithWakeupDelay, WithoutWakeupDelay, }; -use va108xx_hal::gpio::{Floating, Input, Pin, PA14}; +use va108xx_hal::gpio::Input; pub type Max11619ExternallyClockedNoWakeup = Max116xx10Bit; @@ -17,7 +17,6 @@ pub type Max11619ExternallyClockedWithWakeup = Max116xx10Bit; pub type Max11619InternallyClocked = Max116xx10BitEocExt; -pub type EocPin = Pin>; pub const AN0_CHANNEL: u8 = 0; pub const AN1_CHANNEL: u8 = 1; @@ -44,9 +43,9 @@ pub fn max11619_externally_clocked_with_wakeup( pub fn max11619_internally_clocked( spi: Spi, - eoc: EocPin, + eoc: Input, v_ref: VoltageRefMode, -) -> Result, Error> { +) -> Result, Error> { let mut adc = Max116xx10Bit::max11619(spi)? .into_int_clkd_int_timed_through_ser_if_without_wakeup(v_ref, eoc)?; adc.reset(false)?; diff --git a/vorago-reb1/src/temp_sensor.rs b/vorago-reb1/src/temp_sensor.rs index 25b20df..3bc2a4a 100644 --- a/vorago-reb1/src/temp_sensor.rs +++ b/vorago-reb1/src/temp_sensor.rs @@ -15,7 +15,7 @@ use va108xx_hal::{ const ADT75_I2C_ADDR: u8 = 0b1001000; pub struct Adt75TempSensor { - sensor_if: I2cMaster, + sensor_if: I2cMaster, cmd_buf: [u8; 1], current_reg: RegAddresses, } @@ -48,15 +48,10 @@ impl From for AdtInitError { } impl Adt75TempSensor { - pub fn new( - sys_cfg: &mut pac::Sysconfig, - sys_clk: impl Into + Copy, - i2ca: pac::I2ca, - ) -> Result { + pub fn new(sys_clk: Hertz, i2ca: pac::I2ca) -> Result { let mut sensor = Adt75TempSensor { // The master construction can not fail for regular I2C speed. sensor_if: I2cMaster::new( - sys_cfg, sys_clk, i2ca, MasterConfig::default(), diff --git a/vscode/launch.json b/vscode/launch.json index 01362f9..a57fb55 100644 --- a/vscode/launch.json +++ b/vscode/launch.json @@ -15,6 +15,10 @@ "device": "Cortex-M0", "svdFile": "./va108xx/svd/va108xx.svd.patched", "preLaunchTask": "rust: cargo build led blinky", + "serverArgs": [ + "-jtagconf", + "-1,-1" + ], "executable": "${workspaceFolder}/target/thumbv6m-none-eabi/debug/examples/blinky-leds", "interface": "jtag", "runToEntryPoint": "main", @@ -39,6 +43,10 @@ "device": "Cortex-M0", "svdFile": "./va108xx/svd/va108xx.svd.patched", "preLaunchTask": "rust: cargo build hal tests", + "serverArgs": [ + "-jtagconf", + "-1,-1" + ], "executable": "${workspaceFolder}/target/thumbv6m-none-eabi/debug/board-tests", "interface": "jtag", "runToEntryPoint": "main", @@ -54,30 +62,6 @@ ] } }, - { - "type": "cortex-debug", - "request": "launch", - "name": "Debug RTT", - "servertype": "jlink", - "cwd": "${workspaceRoot}", - "device": "Cortex-M0", - "svdFile": "./va108xx/svd/va108xx.svd.patched", - "preLaunchTask": "rust: cargo build rtt", - "executable": "${workspaceFolder}/target/thumbv6m-none-eabi/debug/examples/rtt-log", - "interface": "jtag", - "runToEntryPoint": "main", - "rttConfig": { - "enabled": true, - "address": "auto", - "decoders": [ - { - "port": 0, - "timestamp": true, - "type": "console" - } - ] - } - }, { "type": "cortex-debug", "request": "launch", @@ -87,6 +71,10 @@ "device": "Cortex-M0", "svdFile": "./va108xx/svd/va108xx.svd.patched", "preLaunchTask": "rust: cargo build button blinky", + "serverArgs": [ + "-jtagconf", + "-1,-1" + ], "executable": "${workspaceFolder}/target/thumbv6m-none-eabi/debug/examples/blinky-button-irq", "interface": "jtag", "runToEntryPoint": "main", @@ -113,6 +101,10 @@ "preLaunchTask": "rust: cargo build systick", "executable": "${workspaceFolder}/target/thumbv6m-none-eabi/debug/examples/timer-ticks", "interface": "jtag", + "serverArgs": [ + "-jtagconf", + "-1,-1" + ], "runToEntryPoint": "main", "rttConfig": { "enabled": true, @@ -133,7 +125,11 @@ "servertype": "jlink", "cwd": "${workspaceRoot}", "device": "Cortex-M0", - "svdFile": "./va108xx/svd/va108xx-base.svd.patched", + "svdFile": "./va108xx/svd/va108xx.svd.patched", + "serverArgs": [ + "-jtagconf", + "-1,-1" + ], "preLaunchTask": "rust: cargo build uart", "executable": "${workspaceFolder}/target/thumbv6m-none-eabi/debug/examples/uart", "interface": "jtag", @@ -158,6 +154,10 @@ "cwd": "${workspaceRoot}", "device": "Cortex-M0", "svdFile": "./va108xx/svd/va108xx.svd.patched", + "serverArgs": [ + "-jtagconf", + "-1,-1" + ], "preLaunchTask": "rust: cargo build spi", "executable": "${workspaceFolder}/target/thumbv6m-none-eabi/debug/examples/spi", "interface": "jtag", @@ -182,6 +182,10 @@ "cwd": "${workspaceRoot}", "device": "Cortex-M0", "svdFile": "./va108xx/svd/va108xx-base.svd.patched", + "serverArgs": [ + "-jtagconf", + "-1,-1" + ], "preLaunchTask": "rust: cargo build temp sensor", "executable": "${workspaceFolder}/target/thumbv6m-none-eabi/debug/examples/adt75-temp-sensor", "interface": "jtag", @@ -206,6 +210,10 @@ "cwd": "${workspaceRoot}", "device": "Cortex-M0", "svdFile": "./va108xx/svd/va108xx-base.svd.patched", + "serverArgs": [ + "-jtagconf", + "-1,-1" + ], "preLaunchTask": "rust: cargo build button blinky rtic", "executable": "${workspaceFolder}/target/thumbv6m-none-eabi/debug/examples/blinky-button-rtic", "interface": "jtag", @@ -230,6 +238,10 @@ "cwd": "${workspaceRoot}", "device": "Cortex-M0", "svdFile": "./va108xx/svd/va108xx-base.svd.patched", + "serverArgs": [ + "-jtagconf", + "-1,-1" + ], "preLaunchTask": "uart-echo-rtic-example", "executable": "${workspaceFolder}/target/thumbv6m-none-eabi/debug/uart-echo-rtic", "interface": "jtag", @@ -254,6 +266,10 @@ "cwd": "${workspaceRoot}", "device": "Cortex-M0", "svdFile": "./va108xx/svd/va108xx.svd.patched", + "serverArgs": [ + "-jtagconf", + "-1,-1" + ], "preLaunchTask": "rust: cargo build pwm", "executable": "${workspaceFolder}/target/thumbv6m-none-eabi/debug/examples/pwm", "interface": "jtag", @@ -278,6 +294,10 @@ "cwd": "${workspaceRoot}", "device": "Cortex-M0", "svdFile": "./va108xx/svd/va108xx.svd.patched", + "serverArgs": [ + "-jtagconf", + "-1,-1" + ], "preLaunchTask": "rust: cargo build cascade", "executable": "${workspaceFolder}/target/thumbv6m-none-eabi/debug/examples/cascade", "interface": "jtag", @@ -302,6 +322,10 @@ "cwd": "${workspaceRoot}", "device": "Cortex-M0", "svdFile": "./va108xx/svd/va108xx.svd.patched", + "serverArgs": [ + "-jtagconf", + "-1,-1" + ], "preLaunchTask": "rust: cargo build accelerometer", "executable": "${workspaceFolder}/target/thumbv6m-none-eabi/debug/examples/adxl343-accelerometer", "interface": "jtag", @@ -318,19 +342,6 @@ ] } }, - { - "type": "cortex-debug", - "request": "launch", - "name": "Blinky HAL", - "servertype": "jlink", - "cwd": "${workspaceRoot}", - "device": "Cortex-M0", - "svdFile": "./va108xx/svd/va108xx.svd.patched", - "preLaunchTask": "blinky-hal", - "executable": "${workspaceFolder}/target/thumbv6m-none-eabi/debug/examples/blinky", - "interface": "jtag", - "runToEntryPoint": "main", - }, { "type": "cortex-debug", "request": "launch", @@ -339,6 +350,10 @@ "cwd": "${workspaceRoot}", "device": "Cortex-M0", "svdFile": "./va108xx/svd/va108xx.svd.patched", + "serverArgs": [ + "-jtagconf", + "-1,-1" + ], "preLaunchTask": "rust: cargo build adc", "executable": "${workspaceFolder}/target/thumbv6m-none-eabi/debug/examples/max11619-adc", "interface": "jtag", @@ -363,6 +378,10 @@ "cwd": "${workspaceRoot}", "device": "Cortex-M0", "svdFile": "./va108xx/svd/va108xx.svd.patched", + "serverArgs": [ + "-jtagconf", + "-1,-1" + ], "preLaunchTask": "uart-echo-rtic-example", "executable": "${workspaceFolder}/target/thumbv6m-none-eabi/debug/uart-echo-rtic", "interface": "jtag", @@ -387,6 +406,10 @@ "cwd": "${workspaceRoot}", "device": "Cortex-M0", "svdFile": "./va108xx/svd/va108xx.svd.patched", + "serverArgs": [ + "-jtagconf", + "-1,-1" + ], "preLaunchTask": "reb1-nvm", "executable": "${workspaceFolder}/target/thumbv6m-none-eabi/debug/examples/nvm", "interface": "jtag", @@ -411,6 +434,10 @@ "cwd": "${workspaceRoot}", "device": "Cortex-M0", "svdFile": "./va108xx/svd/va108xx.svd.patched", + "serverArgs": [ + "-jtagconf", + "-1,-1" + ], "preLaunchTask": "rtic-example", "executable": "${workspaceFolder}/target/thumbv6m-none-eabi/debug/rtic-example", "interface": "jtag", @@ -435,6 +462,10 @@ "cwd": "${workspaceRoot}", "device": "Cortex-M0", "svdFile": "./va108xx/svd/va108xx.svd.patched", + "serverArgs": [ + "-jtagconf", + "-1,-1" + ], "preLaunchTask": "embassy-example", "executable": "${workspaceFolder}/target/thumbv6m-none-eabi/debug/embassy-example", "interface": "jtag", @@ -459,6 +490,10 @@ "cwd": "${workspaceRoot}", "device": "Cortex-M0", "svdFile": "./va108xx/svd/va108xx.svd.patched", + "serverArgs": [ + "-jtagconf", + "-1,-1" + ], "preLaunchTask": "bootloader", "executable": "${workspaceFolder}/target/thumbv6m-none-eabi/release/bootloader", "interface": "jtag", @@ -483,6 +518,10 @@ "cwd": "${workspaceRoot}", "device": "Cortex-M0", "svdFile": "./va108xx/svd/va108xx.svd.patched", + "serverArgs": [ + "-jtagconf", + "-1,-1" + ], "preLaunchTask": "flashloader", "executable": "${workspaceFolder}/target/thumbv6m-none-eabi/release/flashloader", "interface": "jtag", @@ -507,6 +546,10 @@ "cwd": "${workspaceRoot}", "device": "Cortex-M0", "svdFile": "./va108xx/svd/va108xx.svd.patched", + "serverArgs": [ + "-jtagconf", + "-1,-1" + ], "preLaunchTask": "async-gpio", "executable": "${workspaceFolder}/target/thumbv6m-none-eabi/debug/async-gpio", "interface": "jtag", @@ -531,6 +574,10 @@ "cwd": "${workspaceRoot}", "device": "Cortex-M0", "svdFile": "./va108xx/svd/va108xx.svd.patched", + "serverArgs": [ + "-jtagconf", + "-1,-1" + ], "preLaunchTask": "async-uart-tx", "executable": "${workspaceFolder}/target/thumbv6m-none-eabi/debug/async-uart-tx", "interface": "jtag", @@ -555,6 +602,10 @@ "cwd": "${workspaceRoot}", "device": "Cortex-M0", "svdFile": "./va108xx/svd/va108xx.svd.patched", + "serverArgs": [ + "-jtagconf", + "-1,-1" + ], "preLaunchTask": "async-uart-rx", "executable": "${workspaceFolder}/target/thumbv6m-none-eabi/debug/async-uart-rx", "interface": "jtag", @@ -572,4 +623,4 @@ } }, ] -} +} \ No newline at end of file diff --git a/vscode/tasks.json b/vscode/tasks.json index 595a42c..5b26aec 100644 --- a/vscode/tasks.json +++ b/vscode/tasks.json @@ -31,20 +31,6 @@ "isDefault": true } }, - { - "label": "rust: cargo build rtt", - "type": "shell", - "command": "~/.cargo/bin/cargo", // note: full path to the cargo - "args": [ - "build", - "--example", - "rtt-log" - ], - "group": { - "kind": "build", - "isDefault": true - } - }, { "label": "rust: cargo build systick", "type": "shell", @@ -319,4 +305,4 @@ ] } ] -} +} \ No newline at end of file