From 46dcad1c10749073a62fee199c50d95a8431494c Mon Sep 17 00:00:00 2001 From: Robin Mueller Date: Thu, 13 Jun 2024 17:27:27 +0200 Subject: [PATCH] UART, WDT and CLKGEN --- Cargo.toml | 2 +- README.md | 32 +- examples/simple/Cargo.toml | 16 + .../simple/examples/blinky-hal.rs | 9 +- .../simple}/examples/blinky-pac.rs | 3 +- .../simple}/examples/rtt-log.rs | 8 +- .../simple}/examples/uart.rs | 35 +- examples/simple/examples/wdt.rs | 79 +++ examples/simple/src/lib.rs | 10 + examples/simple/src/main.rs | 13 + va416xx-hal/Cargo.toml | 16 +- va416xx-hal/src/clock.rs | 468 +++++++++++++++++- va416xx-hal/src/gpio/mod.rs | 2 +- va416xx-hal/src/gpio/pin.rs | 16 + va416xx-hal/src/gpio/reg.rs | 4 +- va416xx-hal/src/lib.rs | 6 +- va416xx-hal/src/prelude.rs | 5 +- va416xx-hal/src/typelevel.rs | 4 +- va416xx-hal/src/uart.rs | 63 ++- va416xx-hal/src/wdt.rs | 114 +++++ va416xx/Cargo.toml | 5 + va416xx/README.md | 1 - va416xx/gen-helper.sh | 2 +- va416xx/src/lib.rs | 6 + va416xx/src/porta.rs | 2 +- vorago-peb1/examples/blinky.rs | 62 --- 26 files changed, 855 insertions(+), 128 deletions(-) create mode 100644 examples/simple/Cargo.toml rename va416xx-hal/examples/blinky.rs => examples/simple/examples/blinky-hal.rs (72%) rename {va416xx-hal => examples/simple}/examples/blinky-pac.rs (88%) rename {va416xx-hal => examples/simple}/examples/rtt-log.rs (81%) rename {va416xx-hal => examples/simple}/examples/uart.rs (51%) create mode 100644 examples/simple/examples/wdt.rs create mode 100644 examples/simple/src/lib.rs create mode 100644 examples/simple/src/main.rs create mode 100644 va416xx-hal/src/wdt.rs delete mode 100644 vorago-peb1/examples/blinky.rs diff --git a/Cargo.toml b/Cargo.toml index 51d3076..17fa3df 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,6 +1,6 @@ [workspace] resolver = "2" -members = [ +members = [ "examples/simple", "va416xx", "va416xx-hal", "vorago-peb1" diff --git a/README.md b/README.md index 87252cb..6b5461b 100644 --- a/README.md +++ b/README.md @@ -30,25 +30,28 @@ to conveniently flash with `cargo run`. Use the following command to have a starting configuration for VS Code: ```sh -cp vscode .vscode -r +cp -rT vscode .vscode ``` You can then adapt the files in `.vscode` to your needs. -## Flashing, running and debugging with the command line +## Flashing, running and debugging the software -### Prerequisites +You can use CLI or VS Code for flashing, running and debugging. In any case, take +care of installing the pre-requisites first. + +### Pre-Requisites 1. [SEGGER J-Link tools](https://www.segger.com/downloads/jlink/) installed 2. [gdb-multiarch](https://packages.debian.org/sid/gdb-multiarch) or similar cross-architecture debugger installed. All commands here assume `gdb-multiarch`. -### Flashing and debugging the blinky application +### Using CLI You can build the blinky example application with the following command ```sh -cargo build -p va416xx-hal --example blinky +cargo build --example blinky ``` Start the GDB server first. The server needs to be started with a certain configuration and with @@ -75,9 +78,22 @@ runner configuration, for example with the following lines in your `.cargo/confi runner = "gdb-multiarch -q -x jlink/jlink.gdb" ``` -After that, you can simply use `cargo run -p va416xx-hal --example blinky` to flash the blinky +After that, you can simply use `cargo run --example blinky` to flash the blinky example. -## Flashing, running and debugging with VS Code +### Using VS Code -TODO +Assuming a working debug connection to your VA108xx board, you can debug using VS Code with +the [`Cortex-Debug` plugin](https://marketplace.visualstudio.com/items?itemName=marus25.cortex-debug). + +Some sample configuration files for VS code were provided and can be used by running +`cp -rT vscode .vscode` like specified above. After that, you can use `Run and Debug` +to automatically rebuild and flash your application. + +If you would like to use a custom GDB application, you can specify the gdb binary in the following +configuration variables in your `settings.json`: + +- `"cortex-debug.gdbPath"` +- `"cortex-debug.gdbPath.linux"` +- `"cortex-debug.gdbPath.windows"` +- `"cortex-debug.gdbPath.osx"` diff --git a/examples/simple/Cargo.toml b/examples/simple/Cargo.toml new file mode 100644 index 0000000..bbad5ea --- /dev/null +++ b/examples/simple/Cargo.toml @@ -0,0 +1,16 @@ +[package] +name = "simple_examples" +version = "0.1.0" +edition = "2021" + +[dependencies] +cortex-m-rt = "0.7" +va416xx-hal = { path = "../../va416xx-hal" } +panic-rtt-target = { version = "0.1.3" } +rtt-target = { version = "0.5" } +cortex-m = { version = "0.7", features = ["critical-section-single-core"]} +embedded-hal = "1" +embedded-hal-nb = "1" +nb = "1" +embedded-io = "0.6" +panic-halt = "0.2" diff --git a/va416xx-hal/examples/blinky.rs b/examples/simple/examples/blinky-hal.rs similarity index 72% rename from va416xx-hal/examples/blinky.rs rename to examples/simple/examples/blinky-hal.rs index 5ed2c39..13eab01 100644 --- a/va416xx-hal/examples/blinky.rs +++ b/examples/simple/examples/blinky-hal.rs @@ -4,13 +4,16 @@ use cortex_m_rt::entry; use embedded_hal::digital::StatefulOutputPin; -use panic_halt as _; +use panic_rtt_target as _; +use rtt_target::{rprintln, rtt_init_print}; use va416xx_hal::{gpio::PinsG, pac}; #[entry] fn main() -> ! { - // SAFETY: Peripherals are only stolen once here. - let mut dp = unsafe { pac::Peripherals::steal() }; + rtt_init_print!(); + rprintln!("VA416xx HAL blinky example"); + + let mut dp = pac::Peripherals::take().unwrap(); let portg = PinsG::new(&mut dp.sysconfig, Some(dp.ioconfig), dp.portg); let mut led = portg.pg5.into_readable_push_pull_output(); //let mut delay = CountDownTimer::new(&mut dp.SYSCONFIG, 50.mhz(), dp.TIM0); diff --git a/va416xx-hal/examples/blinky-pac.rs b/examples/simple/examples/blinky-pac.rs similarity index 88% rename from va416xx-hal/examples/blinky-pac.rs rename to examples/simple/examples/blinky-pac.rs index 8db2fa1..1f99f28 100644 --- a/va416xx-hal/examples/blinky-pac.rs +++ b/examples/simple/examples/blinky-pac.rs @@ -11,8 +11,7 @@ const LED_PG5: u32 = 1 << 5; #[entry] fn main() -> ! { - // SAFETY: Peripherals are only stolen once here. - let dp = unsafe { pac::Peripherals::steal() }; + let dp = pac::Peripherals::take().unwrap(); // Enable all peripheral clocks dp.sysconfig .peripheral_clk_enable() diff --git a/va416xx-hal/examples/rtt-log.rs b/examples/simple/examples/rtt-log.rs similarity index 81% rename from va416xx-hal/examples/rtt-log.rs rename to examples/simple/examples/rtt-log.rs index a62c369..8eb2475 100644 --- a/va416xx-hal/examples/rtt-log.rs +++ b/examples/simple/examples/rtt-log.rs @@ -1,11 +1,10 @@ -//! Code to test RTT logger functionality. +// Code to test RTT logger functionality. #![no_main] #![no_std] use cortex_m_rt::entry; use panic_rtt_target as _; use rtt_target::{rprintln, rtt_init_print}; -use va416xx as _; use va416xx_hal::pac; // Mask for the LED @@ -13,8 +12,7 @@ const LED_PG5: u32 = 1 << 5; #[entry] fn main() -> ! { - // SAFETY: Peripherals are only stolen once here. - let dp = unsafe { pac::Peripherals::steal() }; + let dp = pac::Peripherals::take().unwrap(); // Enable all peripheral clocks dp.sysconfig .peripheral_clk_enable() @@ -25,7 +23,7 @@ fn main() -> ! { .modify(|_, w| unsafe { w.bits(LED_PG5) }); rtt_init_print!(); - rprintln!("-- RTT Demo --"); + rprintln!("VA416xx RTT Demo"); let mut counter = 0; loop { rprintln!("{}: Hello, world!", counter); diff --git a/va416xx-hal/examples/uart.rs b/examples/simple/examples/uart.rs similarity index 51% rename from va416xx-hal/examples/uart.rs rename to examples/simple/examples/uart.rs index 672e3b4..51242b1 100644 --- a/va416xx-hal/examples/uart.rs +++ b/examples/simple/examples/uart.rs @@ -1,13 +1,16 @@ -//! UART example application. Sends a test string over a UART and then enters -//! echo mode +// UART example application. Sends a test string over a UART and then enters +// echo mode #![no_main] #![no_std] use cortex_m_rt::entry; -use embedded_io::{Read, Write}; +use embedded_hal_nb::serial::Read; +use embedded_io::Write; use panic_rtt_target as _; use rtt_target::{rprintln, rtt_init_print}; -use va416xx_hal::time::{Hertz, MegaHertz}; +use simple_examples::peb1; +use va416xx_hal::clock::ClkgenExt; +use va416xx_hal::time::Hertz; use va416xx_hal::{gpio::PinsG, pac, uart}; #[entry] @@ -15,28 +18,34 @@ fn main() -> ! { rtt_init_print!(); rprintln!("-- VA416xx UART example application--"); - // SAFETY: Peripherals are only stolen once here. - let mut dp = unsafe { pac::Peripherals::steal() }; + let mut dp = pac::Peripherals::take().unwrap(); + + // Use the external clock connected to XTAL_N. + let clocks = dp + .clkgen + .constrain() + .xtal_n_clk_with_src_freq(peb1::EXTCLK_FREQ) + .freeze(&mut dp.sysconfig) + .unwrap(); let gpiob = PinsG::new(&mut dp.sysconfig, Some(dp.ioconfig), dp.portg); let tx = gpiob.pg0.into_funsel_1(); let rx = gpiob.pg1.into_funsel_1(); - let uartb = uart::Uart::uart0( + let uart0 = uart::Uart::uart0( dp.uart0, (tx, rx), Hertz::from_raw(115200), &mut dp.sysconfig, - Hertz::from_raw(MegaHertz::from_raw(20).to_Hz()), + &clocks, ); - let (mut tx, mut rx) = uartb.split(); - let mut recv_buf: [u8; 32] = [0; 32]; - writeln!(tx, "Hello World").unwrap(); + let (mut tx, mut rx) = uart0.split(); + writeln!(tx, "Hello World\n\r").unwrap(); loop { // Echo what is received on the serial link. - match rx.read(&mut recv_buf) { + match nb::block!(rx.read()) { Ok(recvd) => { - if let Err(e) = tx.write(&recv_buf[0..recvd]) { + if let Err(e) = embedded_hal_nb::serial::Write::write(&mut tx, recvd) { rprintln!("UART TX error: {:?}", e); } } diff --git a/examples/simple/examples/wdt.rs b/examples/simple/examples/wdt.rs new file mode 100644 index 0000000..5e989fc --- /dev/null +++ b/examples/simple/examples/wdt.rs @@ -0,0 +1,79 @@ +// Code to test the watchdog timer. +#![no_main] +#![no_std] + +use core::cell::Cell; +use cortex_m::interrupt::Mutex; +use cortex_m_rt::entry; +use panic_rtt_target as _; +use rtt_target::{rprintln, rtt_init_print}; +use simple_examples::peb1; +use va416xx_hal::pac::{self, interrupt}; +use va416xx_hal::prelude::*; +use va416xx_hal::wdt::WdtController; + +static WDT_INTRPT_COUNT: Mutex> = Mutex::new(Cell::new(0)); + +#[derive(Debug, PartialEq, Eq)] +#[allow(dead_code)] +enum TestMode { + // Watchdog is fed by main loop, which runs with high period. + FedByMain, + // Watchdog is fed by watchdog IRQ. + FedByIrq, + AllowReset, +} +const TEST_MODE: TestMode = TestMode::FedByMain; +const WDT_ROLLOVER_MS: u32 = 100; + +#[entry] +fn main() -> ! { + rtt_init_print!(); + rprintln!("-- VA416xx WDT example application--"); + let cp = cortex_m::Peripherals::take().unwrap(); + let mut dp = pac::Peripherals::take().unwrap(); + + // Use the external clock connected to XTAL_N. + let clocks = dp + .clkgen + .constrain() + .xtal_n_clk_with_src_freq(peb1::EXTCLK_FREQ) + .freeze(&mut dp.sysconfig) + .unwrap(); + let mut delay_sysclk = cortex_m::delay::Delay::new(cp.SYST, clocks.apb0().raw()); + + let mut last_interrupt_counter = 0; + let mut wdt_ctrl = + WdtController::start(&mut dp.sysconfig, dp.watch_dog, &clocks, WDT_ROLLOVER_MS); + wdt_ctrl.enable_reset(); + loop { + if TEST_MODE != TestMode::AllowReset { + wdt_ctrl.feed(); + } + let interrupt_counter = cortex_m::interrupt::free(|cs| WDT_INTRPT_COUNT.borrow(cs).get()); + if interrupt_counter > last_interrupt_counter { + rprintln!("interrupt counter has increased to {}", interrupt_counter); + last_interrupt_counter = interrupt_counter; + } + match TEST_MODE { + TestMode::FedByMain => delay_sysclk.delay_ms(WDT_ROLLOVER_MS / 5), + TestMode::FedByIrq => delay_sysclk.delay_ms(WDT_ROLLOVER_MS), + _ => (), + } + } +} + +#[interrupt] +#[allow(non_snake_case)] +fn WATCHDOG() { + cortex_m::interrupt::free(|cs| { + WDT_INTRPT_COUNT + .borrow(cs) + .set(WDT_INTRPT_COUNT.borrow(cs).get() + 1); + }); + let wdt = unsafe { pac::WatchDog::steal() }; + // Clear interrupt. + if TEST_MODE != TestMode::AllowReset { + wdt.wdogintclr().write(|w| unsafe { w.bits(1) }); + } +} diff --git a/examples/simple/src/lib.rs b/examples/simple/src/lib.rs new file mode 100644 index 0000000..cea324a --- /dev/null +++ b/examples/simple/src/lib.rs @@ -0,0 +1,10 @@ +#![no_std] + +pub mod peb1 { + use va416xx_hal::time::Hertz; + + // The clock on the PEB1 board has a 20 MHz clock which is increased to 40 MHz with a configurable + // PLL by default. + pub const EXTCLK_FREQ: Hertz = Hertz::from_raw(40_000_000); + pub const XTAL_FREQ: Hertz = Hertz::from_raw(10_000_000); +} diff --git a/examples/simple/src/main.rs b/examples/simple/src/main.rs new file mode 100644 index 0000000..ddb240e --- /dev/null +++ b/examples/simple/src/main.rs @@ -0,0 +1,13 @@ +//! Dummy app which does not do anything. +#![no_main] +#![no_std] + +use cortex_m_rt::entry; +use panic_rtt_target as _; + +#[entry] +fn main() -> ! { + loop { + cortex_m::asm::nop(); + } +} diff --git a/va416xx-hal/Cargo.toml b/va416xx-hal/Cargo.toml index e192432..8f0ffb2 100644 --- a/va416xx-hal/Cargo.toml +++ b/va416xx-hal/Cargo.toml @@ -11,28 +11,24 @@ keywords = ["no-std", "hal", "cortex-m", "vorago", "va416xx"] categories = ["embedded", "no-std", "hardware-support"] [dependencies] -cortex-m = "0.7" -cortex-m-rt = "0.7" +cortex-m = { version = "0.7", features = ["critical-section-single-core"] } nb = "1" paste = "1" embedded-hal-nb = "1" embedded-hal = "1" embedded-io = "0.6" -typenum = "1.12.0" +typenum = "1" defmt = { version = "0.3", optional = true } fugit = "0.3" delegate = "0.12" [dependencies.va416xx] path = "../va416xx" +default-features = false version = "0.1.0" +features = ["critical-section"] [features] +default = ["rt", "revb"] rt = ["va416xx/rt"] -defmt = ["dep:defmt"] - -[dev-dependencies] -panic-rtt-target = { version = "0.1.3" } -rtt-target = { version = "0.5" } -panic-halt = "0.2" -cortex-m = { version = "0.7.6", features = ["critical-section-single-core"]} +revb = [] diff --git a/va416xx-hal/src/clock.rs b/va416xx-hal/src/clock.rs index a08d1ab..c3864d2 100644 --- a/va416xx-hal/src/clock.rs +++ b/va416xx-hal/src/clock.rs @@ -1,5 +1,11 @@ //! This also includes functionality to enable the peripheral clocks -use va416xx::Sysconfig; +//use va416xx::{, Sysconfig}; +use crate::pac; + +use crate::time::Hertz; + +pub const HBO_FREQ: Hertz = Hertz::from_raw(20_000_000); +pub const XTAL_OSC_TSTART_MS: u32 = 15; #[derive(Copy, Clone, PartialEq)] pub enum PeripheralSelect { @@ -50,14 +56,470 @@ pub enum FilterClkSel { Clk7 = 7, } -pub fn enable_peripheral_clock(syscfg: &mut Sysconfig, clock: PeripheralSelect) { +#[inline(always)] +pub fn enable_peripheral_clock(syscfg: &mut pac::Sysconfig, clock: PeripheralSelect) { syscfg .peripheral_clk_enable() .modify(|r, w| unsafe { w.bits(r.bits() | (1 << clock as u8)) }); } -pub fn disable_peripheral_clock(syscfg: &mut Sysconfig, clock: PeripheralSelect) { +#[inline(always)] +pub fn disable_peripheral_clock(syscfg: &mut pac::Sysconfig, clock: PeripheralSelect) { syscfg .peripheral_clk_enable() .modify(|r, w| unsafe { w.bits(r.bits() & !(1 << clock as u8)) }); } + +#[inline(always)] +pub fn assert_periph_reset(syscfg: &mut pac::Sysconfig, periph: PeripheralSelect) { + syscfg + .peripheral_reset() + .modify(|r, w| unsafe { w.bits(r.bits() & !(1 << periph as u8)) }); +} + +#[inline(always)] +pub fn deassert_periph_reset(syscfg: &mut pac::Sysconfig, periph: PeripheralSelect) { + syscfg + .peripheral_reset() + .modify(|r, w| unsafe { w.bits(r.bits() | (1 << periph as u8)) }); +} + +pub trait SyscfgExt { + fn enable_peripheral_clock(&mut self, clock: PeripheralClocks); + + fn disable_peripheral_clock(&mut self, clock: PeripheralClocks); + + fn assert_periph_reset(&mut self, clock: PeripheralSelect); + + fn deassert_periph_reset(&mut self, clock: PeripheralSelect); +} + +impl SyscfgExt for pac::Sysconfig { + #[inline(always)] + fn enable_peripheral_clock(&mut self, clock: PeripheralClocks) { + enable_peripheral_clock(self, clock) + } + + #[inline(always)] + fn disable_peripheral_clock(&mut self, clock: PeripheralClocks) { + disable_peripheral_clock(self, clock) + } + + #[inline(always)] + fn assert_periph_reset(&mut self, clock: PeripheralSelect) { + assert_periph_reset(self, clock) + } + + #[inline(always)] + fn deassert_periph_reset(&mut self, clock: PeripheralSelect) { + deassert_periph_reset(self, clock) + } +} + +/// Refer to chapter 8 (p.57) of the programmers guide for detailed information. +#[derive(Debug, Copy, Clone, PartialEq, Eq)] +#[cfg_attr(feature = "defmt", derive(defmt::Format))] +pub enum ClkselSys { + // Internal Heart-Beat Osciallator. Not tightly controlled (+/-20 %). Not recommended as the regular clock! + Hbo = 0b00, + // External clock signal on XTAL_N line, 1-100 MHz + XtalN = 0b01, + // Internal Phase-Locked Loop. + Pll = 0b10, + // Crystal oscillator amplified, 4-10 MHz. + XtalOsc = 0b11, +} + +/// This selects the input clock to the the CLKGEN peripheral in addition to the HBO clock. +/// +/// This can either be a clock connected directly on the XTAL_N line or a chrystal on the XTAL_P +/// line which goes through an oscillator amplifier. +/// +/// Refer to chapter 8 (p.57) of the programmers guide for detailed information. +#[derive(Debug, Default, Copy, Clone, PartialEq, Eq)] +#[cfg_attr(feature = "defmt", derive(defmt::Format))] +pub enum RefClkSel { + #[default] + None = 0b00, + XtalOsc = 0b01, + XtalN = 0b10, +} + +#[derive(Debug, Default, Copy, Clone, PartialEq, Eq)] +#[cfg_attr(feature = "defmt", derive(defmt::Format))] +pub enum ClkDivSel { + #[default] + Div1 = 0b00, + Div2 = 0b01, + Div4 = 0b10, + Div8 = 0b11, +} + +#[derive(Debug, Copy, Clone, PartialEq, Eq)] +#[cfg_attr(feature = "defmt", derive(defmt::Format))] +pub enum AdcClkDivSel { + Div8 = 0b00, + Div4 = 0b01, + Div2 = 0b10, + Div1 = 0b11, +} + +#[derive(Debug, Default, Copy, Clone, PartialEq, Eq)] +#[cfg_attr(feature = "defmt", derive(defmt::Format))] +pub struct PllCfg { + /// Reference clock divider. + pub clkr: u8, + /// Clock divider on feedback path + pub clkf: u8, + // Output clock divider. + pub clkod: u8, + /// Bandwidth adjustment + pub bwadj: u8, +} + +pub fn clk_after_div(clk: Hertz, div_sel: ClkDivSel) -> Hertz { + match div_sel { + ClkDivSel::Div1 => clk, + ClkDivSel::Div2 => clk / 2, + ClkDivSel::Div4 => clk / 4, + ClkDivSel::Div8 => clk / 8, + } +} + +/// Wait for 500 reference clock cycles like specified in the datasheet. +pub fn pll_setup_delay() { + for _ in 0..500 { + cortex_m::asm::nop() + } +} + +pub trait ClkgenExt { + fn constrain(self) -> ClkgenCfgr; +} + +impl ClkgenExt for pac::Clkgen { + fn constrain(self) -> ClkgenCfgr { + ClkgenCfgr { + source_clk: None, + ref_clk_sel: RefClkSel::None, + clksel_sys: ClkselSys::Hbo, + clk_div_sel: ClkDivSel::Div1, + clk_lost_detection: false, + pll_lock_lost_detection: false, + pll_cfg: None, + clkgen: self, + } + } +} + +pub struct ClkgenCfgr { + ref_clk_sel: RefClkSel, + clksel_sys: ClkselSys, + clk_div_sel: ClkDivSel, + /// The source clock frequency which is either an external clock connected to XTAL_N, or a + /// crystal connected to the XTAL_OSC input. + source_clk: Option, + pll_cfg: Option, + clk_lost_detection: bool, + /// Feature only works on revision B of the board. + #[cfg(feature = "revb")] + pll_lock_lost_detection: bool, + clkgen: pac::Clkgen, +} + +#[derive(Debug, PartialEq, Eq)] +#[cfg_attr(feature = "defmt", derive(defmt::Format))] +pub struct ClkSourceFreqNotSet; + +#[derive(Debug, PartialEq, Eq)] +#[cfg_attr(feature = "defmt", derive(defmt::Format))] +pub enum ClkCfgError { + ClkSourceFreqNotSet, + PllConfigNotSet, + PllInitError, + InconsistentCfg, +} + +/// Delays a given amount of milliseconds. +/// +/// Taken from the HAL implementation. This implementation is probably not precise and it +/// also blocks! +pub fn hbo_clock_delay_ms(ms: u32) { + let wdt = unsafe { pac::WatchDog::steal() }; + for _ in 0..ms { + for _ in 0..10_000 { + cortex_m::asm::nop(); + } + wdt.wdogintclr().write(|w| unsafe { w.bits(1) }); + } +} + +impl ClkgenCfgr { + #[inline] + pub fn source_clk(mut self, src_clk: Hertz) -> Self { + self.source_clk = Some(src_clk); + self + } + + /// This function can be used to utilize the XTAL_N clock input directly without the + /// oscillator. + /// + /// It sets the internal configuration to [ClkselSys::XtalN] and [RefClkSel::XtalN]. + #[inline] + pub fn xtal_n_clk(mut self) -> Self { + self.clksel_sys = ClkselSys::XtalN; + self.ref_clk_sel = RefClkSel::XtalN; + self + } + + #[inline] + pub fn xtal_n_clk_with_src_freq(mut self, src_clk: Hertz) -> Self { + self = self.xtal_n_clk(); + self.source_clk(src_clk) + } + + #[inline] + pub fn clksel_sys(mut self, clksel_sys: ClkselSys) -> Self { + self.clksel_sys = clksel_sys; + self + } + + #[inline] + pub fn ref_clk_sel(mut self, ref_clk_sel: RefClkSel) -> Self { + self.ref_clk_sel = ref_clk_sel; + self + } + + /// Configures all clocks and return a clock configuration structure containing the final + /// frozen clock. + /// + /// Internal implementation details: This implementation is based on the HAL implementation + /// which performs a lot of delays. I do not know if all of those are necessary, but + /// I am going to be conservative here and assume that the vendor has tested though and + /// might have had a reason for those, so I am going to keep them. Chances are, this + /// process only has to be performed once, and it does not matter if it takes a few + /// microseconds or milliseconds longer. + pub fn freeze(self, syscfg: &mut pac::Sysconfig) -> Result { + // Sanitize configuration. + if self.source_clk.is_none() { + return Err(ClkCfgError::ClkSourceFreqNotSet); + } + if self.clksel_sys == ClkselSys::XtalOsc && self.ref_clk_sel != RefClkSel::XtalOsc { + return Err(ClkCfgError::InconsistentCfg); + } + if self.clksel_sys == ClkselSys::XtalN && self.ref_clk_sel != RefClkSel::XtalN { + return Err(ClkCfgError::InconsistentCfg); + } + if self.clksel_sys == ClkselSys::Pll && self.pll_cfg.is_none() { + return Err(ClkCfgError::PllConfigNotSet); + } + + syscfg.enable_peripheral_clock(PeripheralSelect::Clkgen); + let mut final_sysclk = self.source_clk.unwrap(); + // The HAL forces back the HBO clock here with a delay.. Even though this is + // not stricly necessary when coming from a fresh start, it could be still become relevant + // later if the clock lost detection mechanism require a re-configuration of the clocks. + // Therefore, we do it here as well. + self.clkgen + .ctrl0() + .modify(|_, w| unsafe { w.clksel_sys().bits(ClkselSys::Hbo as u8) }); + pll_setup_delay(); + self.clkgen + .ctrl0() + .modify(|_, w| unsafe { w.clk_div_sel().bits(ClkDivSel::Div1 as u8) }); + + // Set up oscillator and PLL input clock. + self.clkgen + .ctrl0() + .modify(|_, w| unsafe { w.ref_clk_sel().bits(self.ref_clk_sel as u8) }); + self.clkgen.ctrl1().modify(|_, w| { + w.xtal_en().clear_bit(); + w.xtal_n_en().clear_bit(); + w + }); + match self.ref_clk_sel { + RefClkSel::None => pll_setup_delay(), + RefClkSel::XtalOsc => { + self.clkgen.ctrl1().modify(|_, w| w.xtal_en().set_bit()); + hbo_clock_delay_ms(XTAL_OSC_TSTART_MS); + } + RefClkSel::XtalN => { + self.clkgen.ctrl1().modify(|_, w| w.xtal_n_en().set_bit()); + pll_setup_delay() + } + } + + // Set up PLL configuration. + match self.pll_cfg { + Some(cfg) => { + self.clkgen.ctrl0().modify(|_, w| w.pll_pwdn().clear_bit()); + // Done in C HAL. I guess this gives the PLL some time to power down properly. + cortex_m::asm::nop(); + cortex_m::asm::nop(); + self.clkgen.ctrl0().modify(|_, w| { + unsafe { + w.pll_clkf().bits(cfg.clkf); + } + unsafe { + w.pll_clkr().bits(cfg.clkr); + } + unsafe { + w.pll_clkod().bits(cfg.clkod); + } + unsafe { + w.pll_bwadj().bits(cfg.bwadj); + } + w.pll_test().clear_bit(); + w.pll_bypass().clear_bit(); + w.pll_intfb().set_bit() + }); + // Taken from SystemCoreClockUpdate implementation from Vorago. + final_sysclk /= cfg.clkr as u32 + 1; + final_sysclk *= cfg.clkf as u32 + 1; + final_sysclk /= cfg.clkod as u32 + 1; + + // Reset PLL. + self.clkgen.ctrl0().modify(|_, w| w.pll_reset().set_bit()); + // The HAL does this, the datasheet specifies a delay of 5 us. I guess it does not + // really matter because the PLL lock detect is used later.. + pll_setup_delay(); + self.clkgen.ctrl0().modify(|_, w| w.pll_reset().clear_bit()); + pll_setup_delay(); + + // check for lock + let stat = self.clkgen.stat().read(); + if stat.fbslip().bit() || stat.rfslip().bit() { + pll_setup_delay(); + if stat.fbslip().bit() || stat.rfslip().bit() { + // This is what the HAL does. We could continue, but then we would at least + // have to somehow report a partial error.. Chances are, the user does not + // want to continue with a broken PLL clock. + return Err(ClkCfgError::PllInitError); + } + } + } + None => self.clkgen.ctrl0().modify(|_, w| w.pll_pwdn().set_bit()), + } + + if self.clk_lost_detection { + rearm_sysclk_lost_with_periph(&self.clkgen) + } + #[cfg(feature = "revb")] + if self.pll_lock_lost_detection { + rearm_pll_lock_lost_with_periph(&self.clkgen) + } + + self.clkgen + .ctrl0() + .modify(|_, w| unsafe { w.clk_div_sel().bits(self.clk_div_sel as u8) }); + final_sysclk = clk_after_div(final_sysclk, self.clk_div_sel); + + // The HAL does this. I don't know why.. + pll_setup_delay(); + + self.clkgen + .ctrl0() + .modify(|_, w| unsafe { w.clksel_sys().bits(self.clksel_sys as u8) }); + + // I will just do the ADC stuff like Vorago does it. + // ADC clock (must be 2-12.5 MHz) + // NOTE: Not using divide by 1 or /2 ratio in REVA silicon because of triggering issue + // For this reason, keep SYSCLK above 8MHz to have the ADC /4 ratio in range) + if final_sysclk.raw() <= 50_000_000 { + self.clkgen + .ctrl1() + .modify(|_, w| unsafe { w.adc_clk_div_sel().bits(AdcClkDivSel::Div4 as u8) }); + } else { + self.clkgen + .ctrl1() + .modify(|_, w| unsafe { w.adc_clk_div_sel().bits(AdcClkDivSel::Div8 as u8) }); + } + + Ok(Clocks { + sysclk: final_sysclk, + apb1: final_sysclk / 2, + apb2: final_sysclk / 4, + }) + } +} + +/// Frozen clock frequencies +/// +/// The existence of this value indicates that the clock configuration can no longer be changed +#[derive(Copy, Clone, PartialEq, Eq, Debug)] +#[cfg_attr(feature = "defmt", derive(defmt::Format))] +pub struct Clocks { + sysclk: Hertz, + apb1: Hertz, + apb2: Hertz, +} + +impl Clocks { + /// Returns the frequency of the HBO clock + pub fn hbo(&self) -> Hertz { + HBO_FREQ + } + + /// Returns the frequency of the APB0 which is equal to the system clock. + pub fn apb0(&self) -> Hertz { + self.sysclk() + } + + /// Returns system clock divied by 2. + pub fn apb1(&self) -> Hertz { + self.apb1 + } + + /// Returns system clock divied by 4. + pub fn apb2(&self) -> Hertz { + self.apb2 + } + + /// Returns the system (core) frequency + pub fn sysclk(&self) -> Hertz { + self.sysclk + } +} + +pub fn rearm_sysclk_lost() { + rearm_sysclk_lost_with_periph(&unsafe { pac::Clkgen::steal() }) +} + +fn rearm_sysclk_lost_with_periph(clkgen: &pac::Clkgen) { + clkgen + .ctrl0() + .modify(|_, w| w.sys_clk_lost_det_en().set_bit()); + clkgen + .ctrl1() + .write(|w| w.sys_clk_lost_det_rearm().set_bit()); + clkgen + .ctrl1() + .write(|w| w.sys_clk_lost_det_rearm().clear_bit()); +} + +#[cfg(feature = "revb")] +pub fn rearm_pll_lock_lost() { + rearm_pll_lock_lost_with_periph(&unsafe { pac::Clkgen::steal() }) +} + +fn rearm_pll_lock_lost_with_periph(clkgen: &pac::Clkgen) { + clkgen + .ctrl1() + .modify(|_, w| w.pll_lost_lock_det_en().set_bit()); + clkgen.ctrl1().write(|w| w.pll_lck_det_rearm().set_bit()); + clkgen.ctrl1().write(|w| w.pll_lck_det_rearm().clear_bit()); +} + +#[cfg(test)] +mod tests { + + use super::*; + + #[test] + fn test_basic_div() { + assert_eq!( + clk_after_div(Hertz::from_raw(10_000_000), super::ClkDivSel::Div2), + Hertz::from_raw(5_000_000) + ); + } +} diff --git a/va416xx-hal/src/gpio/mod.rs b/va416xx-hal/src/gpio/mod.rs index ce4d202..bfe6aa7 100644 --- a/va416xx-hal/src/gpio/mod.rs +++ b/va416xx-hal/src/gpio/mod.rs @@ -24,7 +24,7 @@ #[derive(Debug, PartialEq, Eq)] #[cfg_attr(feature = "defmt", derive(defmt::Format))] -pub struct IsMaskedError(pub(crate) ()); +pub struct IsMaskedError; macro_rules! common_reg_if_functions { () => { diff --git a/va416xx-hal/src/gpio/pin.rs b/va416xx-hal/src/gpio/pin.rs index ddd73b6..7dc88b8 100644 --- a/va416xx-hal/src/gpio/pin.rs +++ b/va416xx-hal/src/gpio/pin.rs @@ -554,6 +554,22 @@ where } } +impl InputPin for Pin> +where + I: PinId, + C: OutputConfig + ReadableOutput, +{ + #[inline] + fn is_high(&mut self) -> Result { + Ok(self._is_high()) + } + + #[inline] + fn is_low(&mut self) -> Result { + Ok(self._is_low()) + } +} + //================================================================================================== // Registers //================================================================================================== diff --git a/va416xx-hal/src/gpio/reg.rs b/va416xx-hal/src/gpio/reg.rs index 2067fb9..d8a9022 100644 --- a/va416xx-hal/src/gpio/reg.rs +++ b/va416xx-hal/src/gpio/reg.rs @@ -209,7 +209,7 @@ pub(super) unsafe trait RegisterInterface { #[inline(always)] fn read_pin_masked(&self) -> Result { if !self.datamask() { - Err(IsMaskedError(())) + Err(IsMaskedError) } else { Ok(((self.port_reg().datain().read().bits() >> self.id().num) & 0x01) == 1) } @@ -234,7 +234,7 @@ pub(super) unsafe trait RegisterInterface { #[inline] fn write_pin_masked(&mut self, bit: bool) -> Result<(), IsMaskedError> { if !self.datamask() { - Err(IsMaskedError(())) + Err(IsMaskedError) } else { // Safety: SETOUT is a "mask" register, and we only write the bit for // this pin ID diff --git a/va416xx-hal/src/lib.rs b/va416xx-hal/src/lib.rs index 0f54b47..4c438c1 100644 --- a/va416xx-hal/src/lib.rs +++ b/va416xx-hal/src/lib.rs @@ -1,7 +1,10 @@ #![no_std] +#[cfg(test)] +extern crate std; -pub use va416xx; +pub use va416xx as device; pub use va416xx as pac; + pub mod prelude; pub mod clock; @@ -9,6 +12,7 @@ pub mod gpio; pub mod time; pub mod typelevel; pub mod uart; +pub mod wdt; #[derive(Debug, Eq, Copy, Clone, PartialEq)] pub enum FunSel { diff --git a/va416xx-hal/src/prelude.rs b/va416xx-hal/src/prelude.rs index 8b13789..e67a9ed 100644 --- a/va416xx-hal/src/prelude.rs +++ b/va416xx-hal/src/prelude.rs @@ -1 +1,4 @@ - +//! Prelude +pub use crate::clock::{ClkgenExt, SyscfgExt}; +pub use fugit::ExtU32 as _; +pub use fugit::RateExtU32 as _; diff --git a/va416xx-hal/src/typelevel.rs b/va416xx-hal/src/typelevel.rs index 991593b..7803c20 100644 --- a/va416xx-hal/src/typelevel.rs +++ b/va416xx-hal/src/typelevel.rs @@ -60,14 +60,14 @@ impl Sealed for NoneT {} /// 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, diff --git a/va416xx-hal/src/uart.rs b/va416xx-hal/src/uart.rs index 0beb7b0..b798e23 100644 --- a/va416xx-hal/src/uart.rs +++ b/va416xx-hal/src/uart.rs @@ -4,7 +4,7 @@ use core::ops::Deref; use embedded_hal_nb::serial::Read; use fugit::RateExtU32; -use crate::clock::{self}; +use crate::clock::{self, Clocks}; use crate::gpio::{AltFunc1, Pin, PD11, PD12, PE2, PE3, PF11, PF12, PF8, PF9, PG0, PG1}; use crate::time::Hertz; use crate::{ @@ -337,20 +337,33 @@ pub trait Instance: Deref { } impl UartBase { + fn init(self, config: Config, clocks: &Clocks) -> Self { + if Uart::IDX == 2 { + self.init_with_clock_freq(config, clocks.apb1()) + } else { + self.init_with_clock_freq(config, clocks.apb2()) + } + } + /// This function assumes that the peripheral clock was alredy enabled /// in the SYSCONFIG register - fn init(self, config: Config, sys_clk: Hertz) -> Self { + fn init_with_clock_freq(self, config: Config, apb_clk: Hertz) -> Self { let baud_multiplier = match config.baud8 { false => 16, true => 8, }; + // This is the calculation: (64.0 * (x - integer_part as f32) + 0.5) as u32 without floating + // point calculations. + let frac = ((apb_clk.raw() % (config.baudrate.raw() * 16)) * 64 + + (config.baudrate.raw() * 8)) + / (config.baudrate.raw() * 16); // Calculations here are derived from chapter 10.4.4 (p.74) of the datasheet. - let x = sys_clk.raw() as f32 / (config.baudrate.raw() * baud_multiplier) as f32; + let x = apb_clk.raw() as f32 / (config.baudrate.raw() * baud_multiplier) as f32; let integer_part = x as u32; - let frac = (64.0 * (x - integer_part as f32) + 0.5) as u32; - self.uart - .clkscale() - .write(|w| unsafe { w.bits(integer_part * 64 + frac) }); + 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), @@ -459,10 +472,16 @@ impl Uart where UartInstance: Instance, { + fn init(mut self, config: Config, clocks: &Clocks) -> Self { + self.inner = self.inner.init(config, clocks); + self + } + /// This function assumes that the peripheral clock was already enabled /// in the SYSCONFIG register - fn init(mut self, config: Config, sys_clk: Hertz) -> Self { - self.inner = self.inner.init(config, sys_clk); + #[allow(dead_code)] + fn init_with_clock_freq(mut self, config: Config, sys_clk: Hertz) -> Self { + self.inner = self.inner.init_with_clock_freq(config, sys_clk); self } @@ -798,7 +817,7 @@ macro_rules! uart_impl { pins: Pins, config: impl Into, syscfg: &mut va416xx::Sysconfig, - sys_clk: impl Into + clocks: &Clocks ) -> Self { crate::clock::enable_peripheral_clock(syscfg, $clk_enb_enum); @@ -809,7 +828,29 @@ macro_rules! uart_impl { rx: Rx::new(), }, pins, - }.init(config.into(), sys_clk.into()) + }.init(config.into(), clocks) + } + + paste::paste! { + pub fn [ <$uartx _with_clock_freq> ]( + uart: $Uartx, + pins: Pins, + config: impl Into, + syscfg: &mut va416xx::Sysconfig, + clock: impl Into, + ) -> Self + { + crate::clock::enable_peripheral_clock(syscfg, $clk_enb_enum); + Uart { + inner: UartBase { + uart, + tx: Tx::new(), + rx: Rx::new(), + }, + pins, + }.init_with_clock_freq(config.into(), clock.into()) + } + } } diff --git a/va416xx-hal/src/wdt.rs b/va416xx-hal/src/wdt.rs new file mode 100644 index 0000000..320c029 --- /dev/null +++ b/va416xx-hal/src/wdt.rs @@ -0,0 +1,114 @@ +use crate::time::Hertz; +use crate::{ + clock::{Clocks, PeripheralSelect}, + pac, + prelude::SyscfgExt, +}; + +pub const WDT_UNLOCK_VALUE: u32 = 0x1ACC_E551; + +pub struct WdtController { + clock_freq: Hertz, + wdt: pac::WatchDog, +} + +/// Enable the watchdog interrupt +/// +/// # Safety +/// +/// This function is `unsafe` because it can break mask-based critical sections. +#[inline] +pub unsafe fn enable_wdt_interrupts() { + unsafe { + cortex_m::peripheral::NVIC::unmask(pac::Interrupt::WATCHDOG); + } +} + +#[inline] +pub fn disable_wdt_interrupts() { + cortex_m::peripheral::NVIC::mask(pac::Interrupt::WATCHDOG); +} + +impl WdtController { + pub fn new( + &self, + syscfg: &mut pac::Sysconfig, + wdt: pac::WatchDog, + clocks: &Clocks, + wdt_freq_ms: u32, + ) -> Self { + Self::start(syscfg, wdt, clocks, wdt_freq_ms) + } + + pub fn start( + syscfg: &mut pac::Sysconfig, + wdt: pac::WatchDog, + clocks: &Clocks, + wdt_freq_ms: u32, + ) -> Self { + syscfg.enable_peripheral_clock(PeripheralSelect::Watchdog); + // It's done like that in Vorago examples. Not exactly sure why the reset is necessary + // though.. + syscfg.assert_periph_reset(PeripheralSelect::Watchdog); + cortex_m::asm::nop(); + cortex_m::asm::nop(); + syscfg.deassert_periph_reset(PeripheralSelect::Watchdog); + + let wdt_clock = clocks.apb2(); + let mut wdt_ctrl = Self { + clock_freq: wdt_clock, + wdt, + }; + wdt_ctrl.set_freq(wdt_freq_ms); + wdt_ctrl.wdt.wdogcontrol().write(|w| w.inten().set_bit()); + wdt_ctrl.feed(); + // Unmask the watchdog interrupt + unsafe { + enable_wdt_interrupts(); + } + wdt_ctrl + } + + #[inline] + pub fn set_freq(&mut self, freq_ms: u32) { + let counter = (self.clock_freq.raw() / 1000) * freq_ms; + self.wdt.wdogload().write(|w| unsafe { w.bits(counter) }); + } + + #[inline] + pub fn disable_reset(&mut self) { + self.wdt.wdogcontrol().modify(|_, w| w.resen().clear_bit()) + } + + #[inline] + pub fn enable_reset(&mut self) { + self.wdt.wdogcontrol().modify(|_, w| w.resen().set_bit()) + } + + #[inline] + pub fn counter(&self) -> u32 { + self.wdt.wdogvalue().read().bits() + } + + #[inline] + pub fn feed(&self) { + self.wdt.wdogintclr().write(|w| unsafe { w.bits(1) }); + } + + #[inline] + pub fn lock(&self) { + self.wdt.wdoglock().write(|w| unsafe { w.bits(0) }); + } + + #[inline] + pub fn unlock(&self) { + self.wdt + .wdoglock() + .write(|w| unsafe { w.bits(WDT_UNLOCK_VALUE) }); + } + + #[inline] + pub fn is_locked(&self) -> bool { + self.wdt.wdogload().read().bits() == 1 + } +} diff --git a/va416xx/Cargo.toml b/va416xx/Cargo.toml index b510ff2..fff48de 100644 --- a/va416xx/Cargo.toml +++ b/va416xx/Cargo.toml @@ -15,6 +15,7 @@ categories = ["embedded", "no-std", "hardware-support"] [dependencies] cortex-m = "0.7" vcell = "0.1.3" +critical-section = { version = "1", optional = true } [dependencies.cortex-m-rt] optional = true @@ -22,3 +23,7 @@ version = ">=0.6.15,<0.8" [features] rt = ["cortex-m-rt/device"] + +[package.metadata.docs.rs] +all-features = true +rustdoc-args = ["--cfg", "docs_rs", "--generate-link-to-definition"] diff --git a/va416xx/README.md b/va416xx/README.md index c8c31b5..0737264 100644 --- a/va416xx/README.md +++ b/va416xx/README.md @@ -1,5 +1,4 @@ [![Crates.io](https://img.shields.io/crates/v/va416xx)](https://crates.io/crates/va416xx) -[![build](https://github.com/us-irs/va416xx-rs/actions/workflows/ci.yml/badge.svg)](https://github.com/us-irs/va416xx-rs/actions/workflows/ci.yml) [![docs.rs](https://img.shields.io/docsrs/va416xx)](https://docs.rs/va416xx) # PAC for the Vorago VA416xx microcontroller family diff --git a/va416xx/gen-helper.sh b/va416xx/gen-helper.sh index 8407751..60a9a59 100755 --- a/va416xx/gen-helper.sh +++ b/va416xx/gen-helper.sh @@ -29,7 +29,7 @@ then fi svdtools patch svd/va416xx-patch.yml -${svd2rust_bin} -i svd/va416xx.svd.patched +${svd2rust_bin} --reexport-interrupt -i svd/va416xx.svd.patched result=$? if [ $result -ne 0 ]; then diff --git a/va416xx/src/lib.rs b/va416xx/src/lib.rs index 2ee184f..9fbc88f 100644 --- a/va416xx/src/lib.rs +++ b/va416xx/src/lib.rs @@ -3,10 +3,16 @@ svd2rust release can be generated by cloning the svd2rust [repository], checking #![allow(non_camel_case_types)] #![allow(non_snake_case)] #![no_std] +// Manually inserted. +#![cfg_attr(docs_rs, feature(doc_auto_cfg))] use core::marker::PhantomData; use core::ops::Deref; #[doc = r"Number available in the NVIC for configuring priority"] pub const NVIC_PRIO_BITS: u8 = 4; +#[cfg(feature = "rt")] +pub use self::Interrupt as interrupt; +#[cfg(feature = "rt")] +pub use cortex_m_rt::interrupt; #[allow(unused_imports)] use generic::*; #[doc = r"Common register and bit access and modify traits"] diff --git a/va416xx/src/porta.rs b/va416xx/src/porta.rs index 7d18568..2a15691 100644 --- a/va416xx/src/porta.rs +++ b/va416xx/src/porta.rs @@ -1,4 +1,4 @@ -// Manually inserted. +// Added manually. #![allow(clippy::identity_op)] #[repr(C)] diff --git a/vorago-peb1/examples/blinky.rs b/vorago-peb1/examples/blinky.rs deleted file mode 100644 index a261fcb..0000000 --- a/vorago-peb1/examples/blinky.rs +++ /dev/null @@ -1,62 +0,0 @@ -//! Simple blinky example -#![no_main] -#![no_std] - -use cortex_m_rt::entry; -use embedded_hal::digital::v2::{OutputPin, ToggleableOutputPin}; -use panic_halt as _; -use va416xx_hal::{gpio::PinsG, pac}; - -// Mask for the LED -const LED_PG5: u32 = 1 << 5; - -#[allow(dead_code)] -enum LibType { - Pac, - Hal, - Bsp, -} - -#[entry] -fn main() -> ! { - let mut dp = pac::Peripherals::take().unwrap(); - let lib_type = LibType::Hal; - - match lib_type { - LibType::Pac => { - // Enable all peripheral clocks - dp.SYSCONFIG - .peripheral_clk_enable - .modify(|_, w| w.portg().set_bit()); - dp.PORTG.dir().modify(|_, w| unsafe { w.bits(LED_PG5) }); - dp.PORTG - .datamask() - .modify(|_, w| unsafe { w.bits(LED_PG5) }); - for _ in 0..10 { - dp.PORTG.clrout().write(|w| unsafe { w.bits(LED_PG5) }); - cortex_m::asm::delay(5_000_000); - dp.PORTG.setout().write(|w| unsafe { w.bits(LED_PG5) }); - cortex_m::asm::delay(5_000_000); - } - loop { - dp.PORTG.togout().write(|w| unsafe { w.bits(LED_PG5) }); - cortex_m::asm::delay(25_000_000); - } - } - LibType::Hal => { - let portg = PinsG::new(&mut dp.SYSCONFIG, Some(dp.IOCONFIG), dp.PORTG); - // Enable all peripheral clocks - let mut led = portg.pg5.into_push_pull_output(); - for _ in 0..10 { - led.set_low().ok(); - cortex_m::asm::delay(5_000_000); - led.set_high().ok(); - } - loop { - led.toggle().ok(); - cortex_m::asm::delay(25_000_000); - } - } - LibType::Bsp => loop {}, - } -}