-
Notifications
You must be signed in to change notification settings - Fork 14
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
* Allow generic time units in timer setup functions -This implements the ability for any embedded-time::duration::* or embedded-time::rate::* to be used where previously only Hertz or Milliseconds were accepted. * WDT skeleton implementing all embedded_hal traits * WDT code written, need example to test function * More functions to use generic time; misc. cleanup * Change delay LED to use timer CountDown features * Added more info about time units/casting in docs * Tried writing to WFAR/WSAR before setting; fails - I'm pretty sure the reason things aren't working is that the WFAR/WSAR access_key1 and access_key2 registers need to be sent their passwords in order to configure the WDT. This commit doesn't work any better than the last one, but it seems like it's worth recording the failure state, so it can be referenced in the future. * Watchdog can now be enabled, WTS bit functioning - The access keys when sent in the correct order do allow for writing registers. Both must be sent, sequentially, immediately before writing. They are currently implemented for all writes relating to the WDT. I still need to try removing them and seeing which registers actually require the codes, and which can be written without. - The watchdog is being triggered, and the WTS bit is being set, but the timing values are off. Will need to investigate. * Watchdog is functioning, at ~30x tick speed - Added example test code with lots of debug printing. - Maybe the 1kHz clock is not being divided from the 32kHz clock. if that's the case, we will want to disable it as a valid clock source for the WDT, since it works for the timers but not the WDT. * WDT working as expected without 1kHz clock source - Disabling the 1kHz clock has the other clocks working as expected. - Still need to find and remove unnecessary access key sending. - Still need to test interrupts. * Add getters, cleanup functions, comments, layout * WDT interrupts working; example now shows use * Fixed `const` error in watchdog.rs get_key fn * fix bjoernQ's suggestions from PR thread
- Loading branch information
1 parent
b673701
commit d96d21e
Showing
7 changed files
with
541 additions
and
24 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,160 @@ | ||
/* | ||
This example code is designed to always fail to `feed()` the watchdog so you can see that it is | ||
working as intended. When the watchdog fails, it sets the WTS bit of the WSR register on the | ||
bl602 to be 1. You can read this bit on initialization of the chip to see if the watchdog has | ||
been triggered. | ||
The code below will read this bit as 0 the first time it is powered on, and then it will fail | ||
to `feed()` the watchdog, resulting in a watchdog reset. The next time (and every subsequent | ||
time) it resets via the watchdog the WTS bit value that is read should be 1. To clear the bit, | ||
you either have to manually clear it via the watchdog.clear_wts() function, or reset the board | ||
using either the reset pin or turning off the power to the board and bringing it back online. | ||
After the board has been reset by the watchdog timer once, the code will change the Watchdog's | ||
mode to call the Watchdog interrupt function instead. This interrupt function will no | ||
longer reset the board, but will instead toggle the red led channel of the RGB led every time | ||
the watchdog is triggered. | ||
*/ | ||
|
||
#![no_std] | ||
#![no_main] | ||
|
||
use bl602_hal as hal; | ||
use core::cell::RefCell; | ||
use core::fmt::Write; | ||
use core::ops::DerefMut; | ||
use embedded_hal::delay::blocking::DelayMs; | ||
use embedded_hal::digital::blocking::{OutputPin, ToggleableOutputPin}; | ||
use embedded_hal::watchdog::blocking::{Enable, Watchdog}; | ||
use embedded_time::{duration::*, rate::*}; | ||
use hal::{ | ||
clock::{Strict, SysclkFreq, UART_PLL_FREQ}, | ||
gpio::{Output, PullDown}, | ||
interrupts::*, | ||
pac, | ||
prelude::*, | ||
serial::*, | ||
timer::*, | ||
watchdog::*, | ||
}; | ||
use panic_halt as _; | ||
use riscv::interrupt::Mutex; | ||
|
||
// Setup custom types to make the code below easier to read: | ||
type RedLedPin = hal::gpio::Pin17<Output<PullDown>>; | ||
type WatchdogTimer = hal::watchdog::ConfiguredWatchdog0; | ||
|
||
// Initialize global static containers for peripheral access within the interrupt function: | ||
static G_INTERRUPT_LED_PIN_R: Mutex<RefCell<Option<RedLedPin>>> = Mutex::new(RefCell::new(None)); | ||
static G_LED_TIMER: Mutex<RefCell<Option<WatchdogTimer>>> = Mutex::new(RefCell::new(None)); | ||
|
||
#[riscv_rt::entry] | ||
fn main() -> ! { | ||
//take control of the device peripherals: | ||
let dp = pac::Peripherals::take().unwrap(); | ||
let mut gpio_pins = dp.GLB.split(); | ||
|
||
// Set up all the clocks we need | ||
let clocks = Strict::new() | ||
.use_pll(40_000_000u32.Hz()) | ||
.sys_clk(SysclkFreq::Pll160Mhz) | ||
.uart_clk(UART_PLL_FREQ.Hz()) | ||
.freeze(&mut gpio_pins.clk_cfg); | ||
|
||
// Set up uart output for debug printing. Since this microcontroller has a pin matrix, | ||
// we need to set up both the pins and the muxs | ||
let pin16 = gpio_pins.pin16.into_uart_sig0(); | ||
let pin7 = gpio_pins.pin7.into_uart_sig7(); | ||
let mux0 = gpio_pins.uart_mux0.into_uart0_tx(); | ||
let mux7 = gpio_pins.uart_mux7.into_uart0_rx(); | ||
|
||
// Configure our UART to 2MBaud, and use the pins we configured above | ||
let mut serial = Serial::uart0( | ||
dp.UART, | ||
Config::default().baudrate(2_000_000.Bd()), | ||
((pin16, mux0), (pin7, mux7)), | ||
clocks, | ||
); | ||
|
||
// Initialize the led pin to their default state: | ||
let mut r_led_pin = gpio_pins.pin17.into_pull_down_output(); | ||
let _ = r_led_pin.set_low(); | ||
|
||
// Create a blocking delay function based on the current cpu frequency | ||
let mut d = bl602_hal::delay::McycleDelay::new(clocks.sysclk().0); | ||
|
||
// Set up the watchdog timer to the slowest tick rate possible: | ||
let timers = dp.TIMER.split(); | ||
let watchdog = timers | ||
.watchdog | ||
.set_clock_source(WdtClockSource::Rc32Khz, 125.Hz()); | ||
|
||
// Before setting up and enabling the watchdog, | ||
// retrieve and print to serial the state of the WTS bit: | ||
let wts_bit_value = match watchdog.has_watchdog_reset_occurred() { | ||
true => 1_u8, | ||
false => 0_u8, | ||
}; | ||
|
||
writeln!(serial, "The watchdog WTS bit reads as: {}\r", wts_bit_value).ok(); | ||
|
||
// On a clean boot, the watchdog will trigger a board reset if not fed in time. | ||
// If the watchdog has previously reset the board, switch to interrupt mode. | ||
match wts_bit_value { | ||
0_u8 => watchdog.set_mode(WatchdogMode::Reset), | ||
1_u8 => { | ||
watchdog.set_mode(WatchdogMode::Interrupt); | ||
enable_interrupt(Interrupt::Watchdog); | ||
} | ||
_ => unreachable!(), | ||
} | ||
|
||
// The watchdog timer doesn't begin counting ticks until it is started. We don't need to handle | ||
// the error state, since the watchdog start function will never actually return an Err(). | ||
let watchdog = watchdog.start(10_u32.seconds()).unwrap(); | ||
|
||
// Move the references to their UnsafeCells once initialized, and before interrupts are enabled: | ||
riscv::interrupt::free(|cs| G_INTERRUPT_LED_PIN_R.borrow(cs).replace(Some(r_led_pin))); | ||
riscv::interrupt::free(|cs| G_LED_TIMER.borrow(cs).replace(Some(watchdog))); | ||
|
||
loop { | ||
// Since we delay for more than 10 seconds, the watchdog is only fed the first time through | ||
// the loop. If you want to prevent the watchdog from triggering, decrease the number of | ||
// milliseconds in the delay to less than 10 seconds. | ||
|
||
// In order to use the watchdog once it has been moved into the RefCell, you must call free(): | ||
riscv::interrupt::free(|cs| { | ||
if let Some(watchdog) = G_LED_TIMER.borrow(cs).borrow_mut().deref_mut() { | ||
watchdog.feed().ok(); | ||
} | ||
}); | ||
d.delay_ms(20_000).ok(); | ||
} | ||
} | ||
|
||
// This is the interrupt handler for the watchdog. It currently toggles the red led channel of the | ||
// RGB led on the board every time the watchdog is triggered after it has been reset at least once. | ||
#[allow(non_snake_case)] | ||
#[no_mangle] | ||
fn Watchdog(_: &mut TrapFrame) { | ||
disable_interrupt(Interrupt::Watchdog); | ||
clear_interrupt(Interrupt::Watchdog); | ||
|
||
//Clear the WDT interrupt flag and feed the watchdog to reset its counter: | ||
riscv::interrupt::free(|cs| { | ||
if let Some(watchdog) = G_LED_TIMER.borrow(cs).borrow_mut().deref_mut() { | ||
watchdog.clear_interrupt(); | ||
watchdog.feed().ok(); | ||
} | ||
}); | ||
|
||
// Toggle the red led whenever the interrupt is triggered: | ||
riscv::interrupt::free(|cs| { | ||
if let Some(led_pin) = G_INTERRUPT_LED_PIN_R.borrow(cs).borrow_mut().deref_mut() { | ||
led_pin.toggle().ok(); | ||
} | ||
}); | ||
|
||
enable_interrupt(Interrupt::Watchdog); | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,3 +1,4 @@ | ||
PROVIDE(Gpio = DefaultHandler); | ||
PROVIDE(TimerCh0 = DefaultHandler); | ||
PROVIDE(TimerCh1 = DefaultHandler); | ||
PROVIDE(Watchdog = DefaultHandler); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.