UART, CLKGEN & WDT #3
@ -1,6 +1,6 @@
|
||||
[workspace]
|
||||
resolver = "2"
|
||||
members = [
|
||||
members = [ "examples/simple",
|
||||
"va416xx",
|
||||
"va416xx-hal",
|
||||
"vorago-peb1"
|
||||
|
32
README.md
32
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"`
|
||||
|
16
examples/simple/Cargo.toml
Normal file
16
examples/simple/Cargo.toml
Normal file
@ -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"
|
@ -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);
|
@ -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()
|
@ -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);
|
@ -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);
|
||||
}
|
||||
}
|
79
examples/simple/examples/wdt.rs
Normal file
79
examples/simple/examples/wdt.rs
Normal file
@ -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<Cell<u32>> = 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) });
|
||||
}
|
||||
}
|
10
examples/simple/src/lib.rs
Normal file
10
examples/simple/src/lib.rs
Normal file
@ -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);
|
||||
}
|
13
examples/simple/src/main.rs
Normal file
13
examples/simple/src/main.rs
Normal file
@ -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();
|
||||
}
|
||||
}
|
@ -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 = []
|
||||
|
@ -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<Hertz>,
|
||||
pll_cfg: Option<PllCfg>,
|
||||
clk_lost_detection: bool,
|
||||
/// Feature only works on revision B of the board.
|
||||
#[cfg(feature = "revb")]
|
||||
pll_lock_lost_detection: bool,
|
||||
clkgen: pac::Clkgen,
|
||||
}
|
||||
|
||||
#[derive(Debug, PartialEq, Eq)]
|
||||
#[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<Clocks, ClkCfgError> {
|
||||
// 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)
|
||||
);
|
||||
}
|
||||
}
|
||||
|
@ -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 {
|
||||
() => {
|
||||
|
@ -554,6 +554,22 @@ where
|
||||
}
|
||||
}
|
||||
|
||||
impl<I, C> InputPin for Pin<I, Output<C>>
|
||||
where
|
||||
I: PinId,
|
||||
C: OutputConfig + ReadableOutput,
|
||||
{
|
||||
#[inline]
|
||||
fn is_high(&mut self) -> Result<bool, Self::Error> {
|
||||
Ok(self._is_high())
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn is_low(&mut self) -> Result<bool, Self::Error> {
|
||||
Ok(self._is_low())
|
||||
}
|
||||
}
|
||||
|
||||
//==================================================================================================
|
||||
// Registers
|
||||
//==================================================================================================
|
||||
|
@ -209,7 +209,7 @@ pub(super) unsafe trait RegisterInterface {
|
||||
#[inline(always)]
|
||||
fn read_pin_masked(&self) -> Result<bool, IsMaskedError> {
|
||||
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
|
||||
|
@ -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 {
|
||||
|
@ -1 +1,4 @@
|
||||
|
||||
//! Prelude
|
||||
pub use crate::clock::{ClkgenExt, SyscfgExt};
|
||||
pub use fugit::ExtU32 as _;
|
||||
pub use fugit::RateExtU32 as _;
|
||||
|
@ -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<Type = Specific>
|
||||
/// ```
|
||||
///
|
||||
/// Moreover, the super traits guarantee that any instance of or reference to a
|
||||
/// type `T` can be converted into the `Specific` type.
|
||||
///
|
||||
/// ```
|
||||
/// ```ignore
|
||||
/// fn example<T>(mut any: T)
|
||||
/// where
|
||||
/// T: Is<Type = Specific>,
|
||||
|
@ -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<Target = uart_base::RegisterBlock> {
|
||||
}
|
||||
|
||||
impl<Uart: Instance> UartBase<Uart> {
|
||||
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<UartInstance, Pins> Uart<UartInstance, Pins>
|
||||
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<Config>,
|
||||
syscfg: &mut va416xx::Sysconfig,
|
||||
sys_clk: impl Into<Hertz>
|
||||
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<Config>,
|
||||
syscfg: &mut va416xx::Sysconfig,
|
||||
clock: impl Into<Hertz>,
|
||||
) -> 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())
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
|
114
va416xx-hal/src/wdt.rs
Normal file
114
va416xx-hal/src/wdt.rs
Normal file
@ -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
|
||||
}
|
||||
}
|
@ -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"]
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -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"]
|
||||
|
@ -1,4 +1,4 @@
|
||||
// Manually inserted.
|
||||
// Added manually.
|
||||
#![allow(clippy::identity_op)]
|
||||
|
||||
#[repr(C)]
|
||||
|
@ -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 {},
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue
Block a user