UART, WDT and CLKGEN
This commit is contained in:
parent
0e395d3747
commit
46dcad1c10
@ -1,6 +1,6 @@
|
|||||||
[workspace]
|
[workspace]
|
||||||
resolver = "2"
|
resolver = "2"
|
||||||
members = [
|
members = [ "examples/simple",
|
||||||
"va416xx",
|
"va416xx",
|
||||||
"va416xx-hal",
|
"va416xx-hal",
|
||||||
"vorago-peb1"
|
"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:
|
Use the following command to have a starting configuration for VS Code:
|
||||||
|
|
||||||
```sh
|
```sh
|
||||||
cp vscode .vscode -r
|
cp -rT vscode .vscode
|
||||||
```
|
```
|
||||||
|
|
||||||
You can then adapt the files in `.vscode` to your needs.
|
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
|
1. [SEGGER J-Link tools](https://www.segger.com/downloads/jlink/) installed
|
||||||
2. [gdb-multiarch](https://packages.debian.org/sid/gdb-multiarch) or similar
|
2. [gdb-multiarch](https://packages.debian.org/sid/gdb-multiarch) or similar
|
||||||
cross-architecture debugger installed. All commands here assume `gdb-multiarch`.
|
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
|
You can build the blinky example application with the following command
|
||||||
|
|
||||||
```sh
|
```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
|
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"
|
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.
|
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 cortex_m_rt::entry;
|
||||||
use embedded_hal::digital::StatefulOutputPin;
|
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};
|
use va416xx_hal::{gpio::PinsG, pac};
|
||||||
|
|
||||||
#[entry]
|
#[entry]
|
||||||
fn main() -> ! {
|
fn main() -> ! {
|
||||||
// SAFETY: Peripherals are only stolen once here.
|
rtt_init_print!();
|
||||||
let mut dp = unsafe { pac::Peripherals::steal() };
|
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 portg = PinsG::new(&mut dp.sysconfig, Some(dp.ioconfig), dp.portg);
|
||||||
let mut led = portg.pg5.into_readable_push_pull_output();
|
let mut led = portg.pg5.into_readable_push_pull_output();
|
||||||
//let mut delay = CountDownTimer::new(&mut dp.SYSCONFIG, 50.mhz(), dp.TIM0);
|
//let mut delay = CountDownTimer::new(&mut dp.SYSCONFIG, 50.mhz(), dp.TIM0);
|
@ -11,8 +11,7 @@ const LED_PG5: u32 = 1 << 5;
|
|||||||
|
|
||||||
#[entry]
|
#[entry]
|
||||||
fn main() -> ! {
|
fn main() -> ! {
|
||||||
// SAFETY: Peripherals are only stolen once here.
|
let dp = pac::Peripherals::take().unwrap();
|
||||||
let dp = unsafe { pac::Peripherals::steal() };
|
|
||||||
// Enable all peripheral clocks
|
// Enable all peripheral clocks
|
||||||
dp.sysconfig
|
dp.sysconfig
|
||||||
.peripheral_clk_enable()
|
.peripheral_clk_enable()
|
@ -1,11 +1,10 @@
|
|||||||
//! Code to test RTT logger functionality.
|
// Code to test RTT logger functionality.
|
||||||
#![no_main]
|
#![no_main]
|
||||||
#![no_std]
|
#![no_std]
|
||||||
|
|
||||||
use cortex_m_rt::entry;
|
use cortex_m_rt::entry;
|
||||||
use panic_rtt_target as _;
|
use panic_rtt_target as _;
|
||||||
use rtt_target::{rprintln, rtt_init_print};
|
use rtt_target::{rprintln, rtt_init_print};
|
||||||
use va416xx as _;
|
|
||||||
use va416xx_hal::pac;
|
use va416xx_hal::pac;
|
||||||
|
|
||||||
// Mask for the LED
|
// Mask for the LED
|
||||||
@ -13,8 +12,7 @@ const LED_PG5: u32 = 1 << 5;
|
|||||||
|
|
||||||
#[entry]
|
#[entry]
|
||||||
fn main() -> ! {
|
fn main() -> ! {
|
||||||
// SAFETY: Peripherals are only stolen once here.
|
let dp = pac::Peripherals::take().unwrap();
|
||||||
let dp = unsafe { pac::Peripherals::steal() };
|
|
||||||
// Enable all peripheral clocks
|
// Enable all peripheral clocks
|
||||||
dp.sysconfig
|
dp.sysconfig
|
||||||
.peripheral_clk_enable()
|
.peripheral_clk_enable()
|
||||||
@ -25,7 +23,7 @@ fn main() -> ! {
|
|||||||
.modify(|_, w| unsafe { w.bits(LED_PG5) });
|
.modify(|_, w| unsafe { w.bits(LED_PG5) });
|
||||||
|
|
||||||
rtt_init_print!();
|
rtt_init_print!();
|
||||||
rprintln!("-- RTT Demo --");
|
rprintln!("VA416xx RTT Demo");
|
||||||
let mut counter = 0;
|
let mut counter = 0;
|
||||||
loop {
|
loop {
|
||||||
rprintln!("{}: Hello, world!", counter);
|
rprintln!("{}: Hello, world!", counter);
|
@ -1,13 +1,16 @@
|
|||||||
//! UART example application. Sends a test string over a UART and then enters
|
// UART example application. Sends a test string over a UART and then enters
|
||||||
//! echo mode
|
// echo mode
|
||||||
#![no_main]
|
#![no_main]
|
||||||
#![no_std]
|
#![no_std]
|
||||||
|
|
||||||
use cortex_m_rt::entry;
|
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 panic_rtt_target as _;
|
||||||
use rtt_target::{rprintln, rtt_init_print};
|
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};
|
use va416xx_hal::{gpio::PinsG, pac, uart};
|
||||||
|
|
||||||
#[entry]
|
#[entry]
|
||||||
@ -15,28 +18,34 @@ fn main() -> ! {
|
|||||||
rtt_init_print!();
|
rtt_init_print!();
|
||||||
rprintln!("-- VA416xx UART example application--");
|
rprintln!("-- VA416xx UART example application--");
|
||||||
|
|
||||||
// SAFETY: Peripherals are only stolen once here.
|
let mut dp = pac::Peripherals::take().unwrap();
|
||||||
let mut dp = unsafe { pac::Peripherals::steal() };
|
|
||||||
|
// 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 gpiob = PinsG::new(&mut dp.sysconfig, Some(dp.ioconfig), dp.portg);
|
||||||
let tx = gpiob.pg0.into_funsel_1();
|
let tx = gpiob.pg0.into_funsel_1();
|
||||||
let rx = gpiob.pg1.into_funsel_1();
|
let rx = gpiob.pg1.into_funsel_1();
|
||||||
|
|
||||||
let uartb = uart::Uart::uart0(
|
let uart0 = uart::Uart::uart0(
|
||||||
dp.uart0,
|
dp.uart0,
|
||||||
(tx, rx),
|
(tx, rx),
|
||||||
Hertz::from_raw(115200),
|
Hertz::from_raw(115200),
|
||||||
&mut dp.sysconfig,
|
&mut dp.sysconfig,
|
||||||
Hertz::from_raw(MegaHertz::from_raw(20).to_Hz()),
|
&clocks,
|
||||||
);
|
);
|
||||||
let (mut tx, mut rx) = uartb.split();
|
let (mut tx, mut rx) = uart0.split();
|
||||||
let mut recv_buf: [u8; 32] = [0; 32];
|
writeln!(tx, "Hello World\n\r").unwrap();
|
||||||
writeln!(tx, "Hello World").unwrap();
|
|
||||||
loop {
|
loop {
|
||||||
// Echo what is received on the serial link.
|
// Echo what is received on the serial link.
|
||||||
match rx.read(&mut recv_buf) {
|
match nb::block!(rx.read()) {
|
||||||
Ok(recvd) => {
|
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);
|
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"]
|
categories = ["embedded", "no-std", "hardware-support"]
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
cortex-m = "0.7"
|
cortex-m = { version = "0.7", features = ["critical-section-single-core"] }
|
||||||
cortex-m-rt = "0.7"
|
|
||||||
nb = "1"
|
nb = "1"
|
||||||
paste = "1"
|
paste = "1"
|
||||||
embedded-hal-nb = "1"
|
embedded-hal-nb = "1"
|
||||||
embedded-hal = "1"
|
embedded-hal = "1"
|
||||||
embedded-io = "0.6"
|
embedded-io = "0.6"
|
||||||
typenum = "1.12.0"
|
typenum = "1"
|
||||||
defmt = { version = "0.3", optional = true }
|
defmt = { version = "0.3", optional = true }
|
||||||
fugit = "0.3"
|
fugit = "0.3"
|
||||||
delegate = "0.12"
|
delegate = "0.12"
|
||||||
|
|
||||||
[dependencies.va416xx]
|
[dependencies.va416xx]
|
||||||
path = "../va416xx"
|
path = "../va416xx"
|
||||||
|
default-features = false
|
||||||
version = "0.1.0"
|
version = "0.1.0"
|
||||||
|
features = ["critical-section"]
|
||||||
|
|
||||||
[features]
|
[features]
|
||||||
|
default = ["rt", "revb"]
|
||||||
rt = ["va416xx/rt"]
|
rt = ["va416xx/rt"]
|
||||||
defmt = ["dep:defmt"]
|
revb = []
|
||||||
|
|
||||||
[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"]}
|
|
||||||
|
@ -1,5 +1,11 @@
|
|||||||
//! This also includes functionality to enable the peripheral clocks
|
//! 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)]
|
#[derive(Copy, Clone, PartialEq)]
|
||||||
pub enum PeripheralSelect {
|
pub enum PeripheralSelect {
|
||||||
@ -50,14 +56,470 @@ pub enum FilterClkSel {
|
|||||||
Clk7 = 7,
|
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
|
syscfg
|
||||||
.peripheral_clk_enable()
|
.peripheral_clk_enable()
|
||||||
.modify(|r, w| unsafe { w.bits(r.bits() | (1 << clock as u8)) });
|
.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
|
syscfg
|
||||||
.peripheral_clk_enable()
|
.peripheral_clk_enable()
|
||||||
.modify(|r, w| unsafe { w.bits(r.bits() & !(1 << clock as u8)) });
|
.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)]
|
#[derive(Debug, PartialEq, Eq)]
|
||||||
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
|
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
|
||||||
pub struct IsMaskedError(pub(crate) ());
|
pub struct IsMaskedError;
|
||||||
|
|
||||||
macro_rules! common_reg_if_functions {
|
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
|
// Registers
|
||||||
//==================================================================================================
|
//==================================================================================================
|
||||||
|
@ -209,7 +209,7 @@ pub(super) unsafe trait RegisterInterface {
|
|||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
fn read_pin_masked(&self) -> Result<bool, IsMaskedError> {
|
fn read_pin_masked(&self) -> Result<bool, IsMaskedError> {
|
||||||
if !self.datamask() {
|
if !self.datamask() {
|
||||||
Err(IsMaskedError(()))
|
Err(IsMaskedError)
|
||||||
} else {
|
} else {
|
||||||
Ok(((self.port_reg().datain().read().bits() >> self.id().num) & 0x01) == 1)
|
Ok(((self.port_reg().datain().read().bits() >> self.id().num) & 0x01) == 1)
|
||||||
}
|
}
|
||||||
@ -234,7 +234,7 @@ pub(super) unsafe trait RegisterInterface {
|
|||||||
#[inline]
|
#[inline]
|
||||||
fn write_pin_masked(&mut self, bit: bool) -> Result<(), IsMaskedError> {
|
fn write_pin_masked(&mut self, bit: bool) -> Result<(), IsMaskedError> {
|
||||||
if !self.datamask() {
|
if !self.datamask() {
|
||||||
Err(IsMaskedError(()))
|
Err(IsMaskedError)
|
||||||
} else {
|
} else {
|
||||||
// Safety: SETOUT is a "mask" register, and we only write the bit for
|
// Safety: SETOUT is a "mask" register, and we only write the bit for
|
||||||
// this pin ID
|
// this pin ID
|
||||||
|
@ -1,7 +1,10 @@
|
|||||||
#![no_std]
|
#![no_std]
|
||||||
|
#[cfg(test)]
|
||||||
|
extern crate std;
|
||||||
|
|
||||||
pub use va416xx;
|
pub use va416xx as device;
|
||||||
pub use va416xx as pac;
|
pub use va416xx as pac;
|
||||||
|
|
||||||
pub mod prelude;
|
pub mod prelude;
|
||||||
|
|
||||||
pub mod clock;
|
pub mod clock;
|
||||||
@ -9,6 +12,7 @@ pub mod gpio;
|
|||||||
pub mod time;
|
pub mod time;
|
||||||
pub mod typelevel;
|
pub mod typelevel;
|
||||||
pub mod uart;
|
pub mod uart;
|
||||||
|
pub mod wdt;
|
||||||
|
|
||||||
#[derive(Debug, Eq, Copy, Clone, PartialEq)]
|
#[derive(Debug, Eq, Copy, Clone, PartialEq)]
|
||||||
pub enum FunSel {
|
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
|
/// specific type. Stated differently, it guarantees that `T == Specific` in
|
||||||
/// the following example.
|
/// the following example.
|
||||||
///
|
///
|
||||||
/// ```
|
/// ```ignore
|
||||||
/// where T: Is<Type = Specific>
|
/// where T: Is<Type = Specific>
|
||||||
/// ```
|
/// ```
|
||||||
///
|
///
|
||||||
/// Moreover, the super traits guarantee that any instance of or reference to a
|
/// Moreover, the super traits guarantee that any instance of or reference to a
|
||||||
/// type `T` can be converted into the `Specific` type.
|
/// type `T` can be converted into the `Specific` type.
|
||||||
///
|
///
|
||||||
/// ```
|
/// ```ignore
|
||||||
/// fn example<T>(mut any: T)
|
/// fn example<T>(mut any: T)
|
||||||
/// where
|
/// where
|
||||||
/// T: Is<Type = Specific>,
|
/// T: Is<Type = Specific>,
|
||||||
|
@ -4,7 +4,7 @@ use core::ops::Deref;
|
|||||||
use embedded_hal_nb::serial::Read;
|
use embedded_hal_nb::serial::Read;
|
||||||
use fugit::RateExtU32;
|
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::gpio::{AltFunc1, Pin, PD11, PD12, PE2, PE3, PF11, PF12, PF8, PF9, PG0, PG1};
|
||||||
use crate::time::Hertz;
|
use crate::time::Hertz;
|
||||||
use crate::{
|
use crate::{
|
||||||
@ -337,20 +337,33 @@ pub trait Instance: Deref<Target = uart_base::RegisterBlock> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl<Uart: Instance> UartBase<Uart> {
|
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
|
/// This function assumes that the peripheral clock was alredy enabled
|
||||||
/// in the SYSCONFIG register
|
/// 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 {
|
let baud_multiplier = match config.baud8 {
|
||||||
false => 16,
|
false => 16,
|
||||||
true => 8,
|
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.
|
// 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 integer_part = x as u32;
|
||||||
let frac = (64.0 * (x - integer_part as f32) + 0.5) as u32;
|
self.uart.clkscale().write(|w| unsafe {
|
||||||
self.uart
|
w.frac().bits(frac as u8);
|
||||||
.clkscale()
|
w.int().bits(integer_part)
|
||||||
.write(|w| unsafe { w.bits(integer_part * 64 + frac) });
|
});
|
||||||
|
|
||||||
let (paren, pareven) = match config.parity {
|
let (paren, pareven) = match config.parity {
|
||||||
Parity::None => (false, false),
|
Parity::None => (false, false),
|
||||||
@ -459,10 +472,16 @@ impl<UartInstance, Pins> Uart<UartInstance, Pins>
|
|||||||
where
|
where
|
||||||
UartInstance: Instance,
|
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
|
/// This function assumes that the peripheral clock was already enabled
|
||||||
/// in the SYSCONFIG register
|
/// in the SYSCONFIG register
|
||||||
fn init(mut self, config: Config, sys_clk: Hertz) -> Self {
|
#[allow(dead_code)]
|
||||||
self.inner = self.inner.init(config, sys_clk);
|
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
|
self
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -798,7 +817,7 @@ macro_rules! uart_impl {
|
|||||||
pins: Pins,
|
pins: Pins,
|
||||||
config: impl Into<Config>,
|
config: impl Into<Config>,
|
||||||
syscfg: &mut va416xx::Sysconfig,
|
syscfg: &mut va416xx::Sysconfig,
|
||||||
sys_clk: impl Into<Hertz>
|
clocks: &Clocks
|
||||||
) -> Self
|
) -> Self
|
||||||
{
|
{
|
||||||
crate::clock::enable_peripheral_clock(syscfg, $clk_enb_enum);
|
crate::clock::enable_peripheral_clock(syscfg, $clk_enb_enum);
|
||||||
@ -809,7 +828,29 @@ macro_rules! uart_impl {
|
|||||||
rx: Rx::new(),
|
rx: Rx::new(),
|
||||||
},
|
},
|
||||||
pins,
|
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]
|
[dependencies]
|
||||||
cortex-m = "0.7"
|
cortex-m = "0.7"
|
||||||
vcell = "0.1.3"
|
vcell = "0.1.3"
|
||||||
|
critical-section = { version = "1", optional = true }
|
||||||
|
|
||||||
[dependencies.cortex-m-rt]
|
[dependencies.cortex-m-rt]
|
||||||
optional = true
|
optional = true
|
||||||
@ -22,3 +23,7 @@ version = ">=0.6.15,<0.8"
|
|||||||
|
|
||||||
[features]
|
[features]
|
||||||
rt = ["cortex-m-rt/device"]
|
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)
|
[![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)
|
[![docs.rs](https://img.shields.io/docsrs/va416xx)](https://docs.rs/va416xx)
|
||||||
|
|
||||||
# PAC for the Vorago VA416xx microcontroller family
|
# PAC for the Vorago VA416xx microcontroller family
|
||||||
|
@ -29,7 +29,7 @@ then
|
|||||||
fi
|
fi
|
||||||
|
|
||||||
svdtools patch svd/va416xx-patch.yml
|
svdtools patch svd/va416xx-patch.yml
|
||||||
${svd2rust_bin} -i svd/va416xx.svd.patched
|
${svd2rust_bin} --reexport-interrupt -i svd/va416xx.svd.patched
|
||||||
|
|
||||||
result=$?
|
result=$?
|
||||||
if [ $result -ne 0 ]; then
|
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_camel_case_types)]
|
||||||
#![allow(non_snake_case)]
|
#![allow(non_snake_case)]
|
||||||
#![no_std]
|
#![no_std]
|
||||||
|
// Manually inserted.
|
||||||
|
#![cfg_attr(docs_rs, feature(doc_auto_cfg))]
|
||||||
use core::marker::PhantomData;
|
use core::marker::PhantomData;
|
||||||
use core::ops::Deref;
|
use core::ops::Deref;
|
||||||
#[doc = r"Number available in the NVIC for configuring priority"]
|
#[doc = r"Number available in the NVIC for configuring priority"]
|
||||||
pub const NVIC_PRIO_BITS: u8 = 4;
|
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)]
|
#[allow(unused_imports)]
|
||||||
use generic::*;
|
use generic::*;
|
||||||
#[doc = r"Common register and bit access and modify traits"]
|
#[doc = r"Common register and bit access and modify traits"]
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
// Manually inserted.
|
// Added manually.
|
||||||
#![allow(clippy::identity_op)]
|
#![allow(clippy::identity_op)]
|
||||||
|
|
||||||
#[repr(C)]
|
#[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