UART, WDT and CLKGEN
This commit is contained in:
@ -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,34 +0,0 @@
|
||||
//! Simple blinky example
|
||||
#![no_main]
|
||||
#![no_std]
|
||||
|
||||
use cortex_m_rt::entry;
|
||||
use panic_halt as _;
|
||||
use va416xx_hal::pac;
|
||||
|
||||
// Mask for the LED
|
||||
const LED_PG5: u32 = 1 << 5;
|
||||
|
||||
#[entry]
|
||||
fn main() -> ! {
|
||||
// SAFETY: Peripherals are only stolen once here.
|
||||
let dp = unsafe { pac::Peripherals::steal() };
|
||||
// Enable all peripheral clocks
|
||||
dp.sysconfig
|
||||
.peripheral_clk_enable()
|
||||
.modify(|_, w| unsafe { w.bits(0xffffffff) });
|
||||
dp.portg.dir().modify(|_, w| unsafe { w.bits(LED_PG5) });
|
||||
dp.portg
|
||||
.datamask()
|
||||
.modify(|_, w| unsafe { w.bits(LED_PG5) });
|
||||
for _ in 0..10 {
|
||||
dp.portg.clrout().write(|w| unsafe { w.bits(LED_PG5) });
|
||||
cortex_m::asm::delay(2_000_000);
|
||||
dp.portg.setout().write(|w| unsafe { w.bits(LED_PG5) });
|
||||
cortex_m::asm::delay(2_000_000);
|
||||
}
|
||||
loop {
|
||||
dp.portg.togout().write(|w| unsafe { w.bits(LED_PG5) });
|
||||
cortex_m::asm::delay(2_000_000);
|
||||
}
|
||||
}
|
@ -1,21 +0,0 @@
|
||||
//! Simple blinky example using the HAL
|
||||
#![no_main]
|
||||
#![no_std]
|
||||
|
||||
use cortex_m_rt::entry;
|
||||
use embedded_hal::digital::StatefulOutputPin;
|
||||
use panic_halt as _;
|
||||
use va416xx_hal::{gpio::PinsG, pac};
|
||||
|
||||
#[entry]
|
||||
fn main() -> ! {
|
||||
// SAFETY: Peripherals are only stolen once here.
|
||||
let mut dp = unsafe { pac::Peripherals::steal() };
|
||||
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);
|
||||
loop {
|
||||
cortex_m::asm::delay(2_000_000);
|
||||
led.toggle().ok();
|
||||
}
|
||||
}
|
@ -1,38 +0,0 @@
|
||||
//! Code to test RTT logger functionality.
|
||||
#![no_main]
|
||||
#![no_std]
|
||||
|
||||
use cortex_m_rt::entry;
|
||||
use panic_rtt_target as _;
|
||||
use rtt_target::{rprintln, rtt_init_print};
|
||||
use va416xx as _;
|
||||
use va416xx_hal::pac;
|
||||
|
||||
// Mask for the LED
|
||||
const LED_PG5: u32 = 1 << 5;
|
||||
|
||||
#[entry]
|
||||
fn main() -> ! {
|
||||
// SAFETY: Peripherals are only stolen once here.
|
||||
let dp = unsafe { pac::Peripherals::steal() };
|
||||
// Enable all peripheral clocks
|
||||
dp.sysconfig
|
||||
.peripheral_clk_enable()
|
||||
.modify(|_, w| unsafe { w.bits(0xffffffff) });
|
||||
dp.portg.dir().modify(|_, w| unsafe { w.bits(LED_PG5) });
|
||||
dp.portg
|
||||
.datamask()
|
||||
.modify(|_, w| unsafe { w.bits(LED_PG5) });
|
||||
|
||||
rtt_init_print!();
|
||||
rprintln!("-- RTT Demo --");
|
||||
let mut counter = 0;
|
||||
loop {
|
||||
rprintln!("{}: Hello, world!", counter);
|
||||
// Still toggle LED. If there are issues with the RTT log, the LED
|
||||
// blinking ensures that the application is actually running.
|
||||
dp.portg.togout().write(|w| unsafe { w.bits(LED_PG5) });
|
||||
counter += 1;
|
||||
cortex_m::asm::delay(10_000_000);
|
||||
}
|
||||
}
|
@ -1,48 +0,0 @@
|
||||
//! 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 panic_rtt_target as _;
|
||||
use rtt_target::{rprintln, rtt_init_print};
|
||||
use va416xx_hal::time::{Hertz, MegaHertz};
|
||||
use va416xx_hal::{gpio::PinsG, pac, uart};
|
||||
|
||||
#[entry]
|
||||
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 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(
|
||||
dp.uart0,
|
||||
(tx, rx),
|
||||
Hertz::from_raw(115200),
|
||||
&mut dp.sysconfig,
|
||||
Hertz::from_raw(MegaHertz::from_raw(20).to_Hz()),
|
||||
);
|
||||
let (mut tx, mut rx) = uartb.split();
|
||||
let mut recv_buf: [u8; 32] = [0; 32];
|
||||
writeln!(tx, "Hello World").unwrap();
|
||||
loop {
|
||||
// Echo what is received on the serial link.
|
||||
match rx.read(&mut recv_buf) {
|
||||
Ok(recvd) => {
|
||||
if let Err(e) = tx.write(&recv_buf[0..recvd]) {
|
||||
rprintln!("UART TX error: {:?}", e);
|
||||
}
|
||||
}
|
||||
Err(e) => {
|
||||
rprintln!("UART RX error {:?}", e);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -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
|
||||
}
|
||||
}
|
Reference in New Issue
Block a user