Merge pull request #4175 from felipebalbi/imxrt-rtos-timer
iMXRT OS timer
This commit is contained in:
commit
4dbaa01870
@ -12,7 +12,7 @@ documentation = "https://docs.embassy.dev/embassy-imxrt"
|
|||||||
[package.metadata.embassy_docs]
|
[package.metadata.embassy_docs]
|
||||||
src_base = "https://github.com/embassy-rs/embassy/blob/embassy-imxrt-v$VERSION/embassy-imxrt/src/"
|
src_base = "https://github.com/embassy-rs/embassy/blob/embassy-imxrt-v$VERSION/embassy-imxrt/src/"
|
||||||
src_base_git = "https://github.com/embassy-rs/embassy/blob/$COMMIT/embassy-imxrt/src/"
|
src_base_git = "https://github.com/embassy-rs/embassy/blob/$COMMIT/embassy-imxrt/src/"
|
||||||
features = ["defmt", "unstable-pac", "time", "time-driver"]
|
features = ["defmt", "unstable-pac", "time", "time-driver-os-timer"]
|
||||||
flavors = [
|
flavors = [
|
||||||
{ regex_feature = "mimxrt6.*", target = "thumbv8m.main-none-eabihf" }
|
{ regex_feature = "mimxrt6.*", target = "thumbv8m.main-none-eabihf" }
|
||||||
]
|
]
|
||||||
@ -37,9 +37,12 @@ defmt = ["dep:defmt", "embassy-hal-internal/defmt", "embassy-sync/defmt", "mimxr
|
|||||||
time = ["dep:embassy-time", "embassy-embedded-hal/time"]
|
time = ["dep:embassy-time", "embassy-embedded-hal/time"]
|
||||||
|
|
||||||
## Enable custom embassy time-driver implementation, using 32KHz RTC
|
## Enable custom embassy time-driver implementation, using 32KHz RTC
|
||||||
time-driver-rtc = ["_time-driver"]
|
time-driver-rtc = ["_time-driver", "embassy-time-driver?/tick-hz-1_000"]
|
||||||
|
|
||||||
_time-driver = ["dep:embassy-time-driver", "embassy-time-driver?/tick-hz-1_000", "dep:embassy-time-queue-utils", "embassy-embedded-hal/time"]
|
## Enable custom embassy time-driver implementation, using 1MHz OS Timer
|
||||||
|
time-driver-os-timer = ["_time-driver", "embassy-time-driver?/tick-hz-1_000_000"]
|
||||||
|
|
||||||
|
_time-driver = ["dep:embassy-time-driver", "dep:embassy-time-queue-utils", "embassy-embedded-hal/time"]
|
||||||
|
|
||||||
## Reexport the PAC for the currently enabled chip at `embassy_imxrt::pac` (unstable)
|
## Reexport the PAC for the currently enabled chip at `embassy_imxrt::pac` (unstable)
|
||||||
unstable-pac = []
|
unstable-pac = []
|
||||||
|
|||||||
@ -1,8 +1,6 @@
|
|||||||
//! Clock configuration for the `RT6xx`
|
//! Clock configuration for the `RT6xx`
|
||||||
use core::sync::atomic::{AtomicU32, AtomicU8, Ordering};
|
use core::sync::atomic::{AtomicU32, AtomicU8, Ordering};
|
||||||
|
|
||||||
#[cfg(feature = "defmt")]
|
|
||||||
use defmt;
|
|
||||||
use paste::paste;
|
use paste::paste;
|
||||||
|
|
||||||
use crate::pac;
|
use crate::pac;
|
||||||
@ -503,7 +501,6 @@ impl ConfigurableClock for LposcConfig {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
error!("failed to convert desired clock rate, {:#}, to LPOSC Freq", freq);
|
|
||||||
Err(ClockError::InvalidFrequency)
|
Err(ClockError::InvalidFrequency)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -549,7 +546,6 @@ impl ConfigurableClock for FfroConfig {
|
|||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
fn get_clock_rate(&self) -> Result<u32, ClockError> {
|
fn get_clock_rate(&self) -> Result<u32, ClockError> {
|
||||||
trace!("getting ffro clock rate");
|
|
||||||
Ok(self.freq.load(Ordering::Relaxed))
|
Ok(self.freq.load(Ordering::Relaxed))
|
||||||
}
|
}
|
||||||
fn set_clock_rate(&mut self, _div: u8, _mult: u8, freq: u32) -> Result<(), ClockError> {
|
fn set_clock_rate(&mut self, _div: u8, _mult: u8, freq: u32) -> Result<(), ClockError> {
|
||||||
@ -616,7 +612,6 @@ impl ConfigurableClock for SfroConfig {
|
|||||||
fn set_clock_rate(&mut self, _div: u8, _mult: u8, freq: u32) -> Result<(), ClockError> {
|
fn set_clock_rate(&mut self, _div: u8, _mult: u8, freq: u32) -> Result<(), ClockError> {
|
||||||
if self.state == State::Enabled {
|
if self.state == State::Enabled {
|
||||||
if freq == SFRO_FREQ {
|
if freq == SFRO_FREQ {
|
||||||
trace!("Sfro frequency is already set at 16MHz");
|
|
||||||
Ok(())
|
Ok(())
|
||||||
} else {
|
} else {
|
||||||
Err(ClockError::InvalidFrequency)
|
Err(ClockError::InvalidFrequency)
|
||||||
@ -677,7 +672,6 @@ impl MultiSourceClock for MainPllClkConfig {
|
|||||||
}
|
}
|
||||||
MainPllClkSrc::SFRO => {
|
MainPllClkSrc::SFRO => {
|
||||||
if !clock_src_config.is_enabled() {
|
if !clock_src_config.is_enabled() {
|
||||||
error!("Can't set SFRO as source for MainPll as it's not enabled");
|
|
||||||
return Err(ClockError::ClockNotEnabled);
|
return Err(ClockError::ClockNotEnabled);
|
||||||
}
|
}
|
||||||
// check if desired frequency is a valid multiple of 16m SFRO clock
|
// check if desired frequency is a valid multiple of 16m SFRO clock
|
||||||
@ -703,7 +697,6 @@ impl ConfigurableClock for MainPllClkConfig {
|
|||||||
}
|
}
|
||||||
fn disable(&self) -> Result<(), ClockError> {
|
fn disable(&self) -> Result<(), ClockError> {
|
||||||
if self.is_enabled() {
|
if self.is_enabled() {
|
||||||
error!("Attempting to reset the Main Pll Clock, should be resetting its source");
|
|
||||||
Err(ClockError::ClockNotSupported)
|
Err(ClockError::ClockNotSupported)
|
||||||
} else {
|
} else {
|
||||||
Err(ClockError::ClockNotEnabled)
|
Err(ClockError::ClockNotEnabled)
|
||||||
@ -719,7 +712,6 @@ impl ConfigurableClock for MainPllClkConfig {
|
|||||||
}
|
}
|
||||||
fn set_clock_rate(&mut self, div: u8, mult: u8, freq: u32) -> Result<(), ClockError> {
|
fn set_clock_rate(&mut self, div: u8, mult: u8, freq: u32) -> Result<(), ClockError> {
|
||||||
if self.is_enabled() {
|
if self.is_enabled() {
|
||||||
trace!("attempting to set main pll clock rate");
|
|
||||||
// SAFETY: unsafe needed to take pointers to Sysctl0 and Clkctl0
|
// SAFETY: unsafe needed to take pointers to Sysctl0 and Clkctl0
|
||||||
let clkctl0 = unsafe { crate::pac::Clkctl0::steal() };
|
let clkctl0 = unsafe { crate::pac::Clkctl0::steal() };
|
||||||
let sysctl0 = unsafe { crate::pac::Sysctl0::steal() };
|
let sysctl0 = unsafe { crate::pac::Sysctl0::steal() };
|
||||||
@ -741,15 +733,12 @@ impl ConfigurableClock for MainPllClkConfig {
|
|||||||
base_rate = r;
|
base_rate = r;
|
||||||
}
|
}
|
||||||
MainPllClkSrc::FFRO => {
|
MainPllClkSrc::FFRO => {
|
||||||
trace!("found FFRO as source, wait a bit");
|
|
||||||
delay_loop_clocks(1000, desired_freq);
|
delay_loop_clocks(1000, desired_freq);
|
||||||
match clkctl0.ffroctl0().read().trim_range().is_ffro_48mhz() {
|
match clkctl0.ffroctl0().read().trim_range().is_ffro_48mhz() {
|
||||||
true => base_rate = Into::into(FfroFreq::Ffro48m),
|
true => base_rate = Into::into(FfroFreq::Ffro48m),
|
||||||
false => base_rate = Into::into(FfroFreq::Ffro60m),
|
false => base_rate = Into::into(FfroFreq::Ffro60m),
|
||||||
}
|
}
|
||||||
trace!("found ffro rate to be: {:#}", base_rate);
|
|
||||||
if div == 2 {
|
if div == 2 {
|
||||||
trace!("dividing FFRO rate by 2");
|
|
||||||
clkctl0.syspll0clksel().write(|w| w.sel().ffro_div_2());
|
clkctl0.syspll0clksel().write(|w| w.sel().ffro_div_2());
|
||||||
delay_loop_clocks(150, desired_freq);
|
delay_loop_clocks(150, desired_freq);
|
||||||
base_rate /= 2;
|
base_rate /= 2;
|
||||||
@ -763,10 +752,8 @@ impl ConfigurableClock for MainPllClkConfig {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
base_rate *= u32::from(mult);
|
base_rate *= u32::from(mult);
|
||||||
trace!("calculated base rate at: {:#}", base_rate);
|
|
||||||
if base_rate != freq {
|
if base_rate != freq {
|
||||||
// make sure to power syspll back up before returning the error
|
// make sure to power syspll back up before returning the error
|
||||||
error!("invalid frequency found, powering syspll back up before returning error. Check div and mult");
|
|
||||||
// Clear System PLL reset
|
// Clear System PLL reset
|
||||||
clkctl0.syspll0ctl0().write(|w| w.reset().normal());
|
clkctl0.syspll0ctl0().write(|w| w.reset().normal());
|
||||||
// Power up SYSPLL
|
// Power up SYSPLL
|
||||||
@ -775,13 +762,11 @@ impl ConfigurableClock for MainPllClkConfig {
|
|||||||
.write(|w| w.syspllana_pd().clr_pdruncfg0().syspllldo_pd().clr_pdruncfg0());
|
.write(|w| w.syspllana_pd().clr_pdruncfg0().syspllldo_pd().clr_pdruncfg0());
|
||||||
return Err(ClockError::InvalidFrequency);
|
return Err(ClockError::InvalidFrequency);
|
||||||
}
|
}
|
||||||
trace!("setting default num and denom");
|
|
||||||
// SAFETY: unsafe needed to write the bits for the num and demon fields
|
// SAFETY: unsafe needed to write the bits for the num and demon fields
|
||||||
clkctl0.syspll0num().write(|w| unsafe { w.num().bits(0b0) });
|
clkctl0.syspll0num().write(|w| unsafe { w.num().bits(0b0) });
|
||||||
clkctl0.syspll0denom().write(|w| unsafe { w.denom().bits(0b1) });
|
clkctl0.syspll0denom().write(|w| unsafe { w.denom().bits(0b1) });
|
||||||
delay_loop_clocks(30, desired_freq);
|
delay_loop_clocks(30, desired_freq);
|
||||||
self.mult.store(mult, Ordering::Relaxed);
|
self.mult.store(mult, Ordering::Relaxed);
|
||||||
trace!("setting self.mult as: {:#}", mult);
|
|
||||||
match mult {
|
match mult {
|
||||||
16 => {
|
16 => {
|
||||||
clkctl0.syspll0ctl0().modify(|_r, w| w.mult().div_16());
|
clkctl0.syspll0ctl0().modify(|_r, w| w.mult().div_16());
|
||||||
@ -803,7 +788,6 @@ impl ConfigurableClock for MainPllClkConfig {
|
|||||||
}
|
}
|
||||||
_ => return Err(ClockError::InvalidMult),
|
_ => return Err(ClockError::InvalidMult),
|
||||||
}
|
}
|
||||||
trace!("clear syspll reset");
|
|
||||||
// Clear System PLL reset
|
// Clear System PLL reset
|
||||||
clkctl0.syspll0ctl0().modify(|_r, w| w.reset().normal());
|
clkctl0.syspll0ctl0().modify(|_r, w| w.reset().normal());
|
||||||
// Power up SYSPLL
|
// Power up SYSPLL
|
||||||
@ -819,7 +803,6 @@ impl ConfigurableClock for MainPllClkConfig {
|
|||||||
clkctl0.syspll0ctl0().modify(|_, w| w.holdringoff_ena().dsiable());
|
clkctl0.syspll0ctl0().modify(|_, w| w.holdringoff_ena().dsiable());
|
||||||
delay_loop_clocks(15, desired_freq);
|
delay_loop_clocks(15, desired_freq);
|
||||||
|
|
||||||
trace!("setting new PFD0 bits");
|
|
||||||
// gate the output and clear bits.
|
// gate the output and clear bits.
|
||||||
// SAFETY: unsafe needed to write the bits for pfd0
|
// SAFETY: unsafe needed to write the bits for pfd0
|
||||||
clkctl0
|
clkctl0
|
||||||
@ -833,7 +816,6 @@ impl ConfigurableClock for MainPllClkConfig {
|
|||||||
.modify(|_r, w| unsafe { w.pfd0_clkgate().not_gated().pfd0().bits(0x12) });
|
.modify(|_r, w| unsafe { w.pfd0_clkgate().not_gated().pfd0().bits(0x12) });
|
||||||
// wait for ready bit to be set
|
// wait for ready bit to be set
|
||||||
delay_loop_clocks(50, desired_freq);
|
delay_loop_clocks(50, desired_freq);
|
||||||
trace!("waiting for mainpll clock to be ready");
|
|
||||||
while clkctl0.syspll0pfd().read().pfd0_clkrdy().bit_is_clear() {}
|
while clkctl0.syspll0pfd().read().pfd0_clkrdy().bit_is_clear() {}
|
||||||
// clear by writing a 1
|
// clear by writing a 1
|
||||||
clkctl0.syspll0pfd().modify(|_, w| w.pfd0_clkrdy().set_bit());
|
clkctl0.syspll0pfd().modify(|_, w| w.pfd0_clkrdy().set_bit());
|
||||||
@ -854,11 +836,9 @@ impl ConfigurableClock for MainPllClkConfig {
|
|||||||
impl MainPllClkConfig {
|
impl MainPllClkConfig {
|
||||||
/// Calculate the mult value of a desired frequency, return error if invalid
|
/// Calculate the mult value of a desired frequency, return error if invalid
|
||||||
pub(self) fn calc_mult(rate: u32, base_freq: u32) -> Result<u8, ClockError> {
|
pub(self) fn calc_mult(rate: u32, base_freq: u32) -> Result<u8, ClockError> {
|
||||||
trace!("calculating mult for {:#} / {:#}", rate, base_freq);
|
|
||||||
const VALIDMULTS: [u8; 6] = [16, 17, 20, 22, 27, 33];
|
const VALIDMULTS: [u8; 6] = [16, 17, 20, 22, 27, 33];
|
||||||
if rate > base_freq && rate % base_freq == 0 {
|
if rate > base_freq && rate % base_freq == 0 {
|
||||||
let mult = (rate / base_freq) as u8;
|
let mult = (rate / base_freq) as u8;
|
||||||
trace!("verifying that calculated mult {:#} is a valid one", mult);
|
|
||||||
if VALIDMULTS.into_iter().any(|i| i == mult) {
|
if VALIDMULTS.into_iter().any(|i| i == mult) {
|
||||||
Ok(mult)
|
Ok(mult)
|
||||||
} else {
|
} else {
|
||||||
@ -1112,7 +1092,6 @@ impl ConfigurableClock for MainClkConfig {
|
|||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
fn disable(&self) -> Result<(), ClockError> {
|
fn disable(&self) -> Result<(), ClockError> {
|
||||||
error!("Attempting to reset the main clock, should NOT happen during runtime");
|
|
||||||
Err(ClockError::ClockNotSupported)
|
Err(ClockError::ClockNotSupported)
|
||||||
}
|
}
|
||||||
fn get_clock_rate(&self) -> Result<u32, ClockError> {
|
fn get_clock_rate(&self) -> Result<u32, ClockError> {
|
||||||
@ -1120,7 +1099,6 @@ impl ConfigurableClock for MainClkConfig {
|
|||||||
Ok(rate)
|
Ok(rate)
|
||||||
}
|
}
|
||||||
fn set_clock_rate(&mut self, _div: u8, _mult: u8, _freq: u32) -> Result<(), ClockError> {
|
fn set_clock_rate(&mut self, _div: u8, _mult: u8, _freq: u32) -> Result<(), ClockError> {
|
||||||
error!("The multi-source set_clock_rate_and_source method should be used instead of set_clock_rate");
|
|
||||||
Err(ClockError::ClockNotSupported)
|
Err(ClockError::ClockNotSupported)
|
||||||
}
|
}
|
||||||
fn is_enabled(&self) -> bool {
|
fn is_enabled(&self) -> bool {
|
||||||
@ -1145,7 +1123,6 @@ impl ConfigurableClock for ClkInConfig {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
fn set_clock_rate(&mut self, _div: u8, _mult: u8, freq: u32) -> Result<(), ClockError> {
|
fn set_clock_rate(&mut self, _div: u8, _mult: u8, freq: u32) -> Result<(), ClockError> {
|
||||||
trace!("Setting value of clk in config, this won't change the clock itself");
|
|
||||||
self.freq.as_ref().unwrap().store(freq, Ordering::Relaxed);
|
self.freq.as_ref().unwrap().store(freq, Ordering::Relaxed);
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
@ -1188,7 +1165,6 @@ impl ConfigurableClock for RtcClkConfig {
|
|||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
fn disable(&self) -> Result<(), ClockError> {
|
fn disable(&self) -> Result<(), ClockError> {
|
||||||
error!("Resetting the RTC clock, this should NOT happen during runtime");
|
|
||||||
Err(ClockError::ClockNotSupported)
|
Err(ClockError::ClockNotSupported)
|
||||||
}
|
}
|
||||||
fn set_clock_rate(&mut self, _div: u8, _mult: u8, freq: u32) -> Result<(), ClockError> {
|
fn set_clock_rate(&mut self, _div: u8, _mult: u8, freq: u32) -> Result<(), ClockError> {
|
||||||
@ -1199,7 +1175,6 @@ impl ConfigurableClock for RtcClkConfig {
|
|||||||
match r {
|
match r {
|
||||||
RtcFreq::Default1Hz => {
|
RtcFreq::Default1Hz => {
|
||||||
if rtc.ctrl().read().rtc_en().is_enable() {
|
if rtc.ctrl().read().rtc_en().is_enable() {
|
||||||
trace!("Attempting to enable an already enabled clock, RTC 1Hz");
|
|
||||||
} else {
|
} else {
|
||||||
rtc.ctrl().modify(|_r, w| w.rtc_en().enable());
|
rtc.ctrl().modify(|_r, w| w.rtc_en().enable());
|
||||||
}
|
}
|
||||||
@ -1207,7 +1182,6 @@ impl ConfigurableClock for RtcClkConfig {
|
|||||||
}
|
}
|
||||||
RtcFreq::HighResolution1khz => {
|
RtcFreq::HighResolution1khz => {
|
||||||
if rtc.ctrl().read().rtc1khz_en().is_enable() {
|
if rtc.ctrl().read().rtc1khz_en().is_enable() {
|
||||||
trace!("Attempting to enable an already enabled clock, RTC 1Hz");
|
|
||||||
} else {
|
} else {
|
||||||
rtc.ctrl().modify(|_r, w| w.rtc1khz_en().enable());
|
rtc.ctrl().modify(|_r, w| w.rtc1khz_en().enable());
|
||||||
}
|
}
|
||||||
@ -1215,7 +1189,6 @@ impl ConfigurableClock for RtcClkConfig {
|
|||||||
}
|
}
|
||||||
RtcFreq::SubSecond32kHz => {
|
RtcFreq::SubSecond32kHz => {
|
||||||
if rtc.ctrl().read().rtc_subsec_ena().is_enable() {
|
if rtc.ctrl().read().rtc_subsec_ena().is_enable() {
|
||||||
trace!("Attempting to enable an already enabled clock, RTC 1Hz");
|
|
||||||
} else {
|
} else {
|
||||||
rtc.ctrl().modify(|_r, w| w.rtc_subsec_ena().enable());
|
rtc.ctrl().modify(|_r, w| w.rtc_subsec_ena().enable());
|
||||||
}
|
}
|
||||||
@ -1245,18 +1218,12 @@ impl ConfigurableClock for RtcClkConfig {
|
|||||||
|
|
||||||
impl SysClkConfig {
|
impl SysClkConfig {
|
||||||
/// Updates the system core clock frequency, SW concept used for systick
|
/// Updates the system core clock frequency, SW concept used for systick
|
||||||
fn update_sys_core_clock(&self) {
|
fn update_sys_core_clock(&self) {}
|
||||||
trace!(
|
|
||||||
"System core clock has been updated to {:?}, this involves no HW reg writes",
|
|
||||||
self.sysclkfreq.load(Ordering::Relaxed)
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
impl ConfigurableClock for SysOscConfig {
|
impl ConfigurableClock for SysOscConfig {
|
||||||
fn enable_and_reset(&self) -> Result<(), ClockError> {
|
fn enable_and_reset(&self) -> Result<(), ClockError> {
|
||||||
if self.state == State::Enabled {
|
if self.state == State::Enabled {
|
||||||
trace!("SysOsc was already enabled");
|
|
||||||
return Ok(());
|
return Ok(());
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1498,32 +1465,26 @@ impl ClockOutConfig {
|
|||||||
/// Using the config, enables all desired clocks to desired clock rates
|
/// Using the config, enables all desired clocks to desired clock rates
|
||||||
fn init_clock_hw(config: ClockConfig) -> Result<(), ClockError> {
|
fn init_clock_hw(config: ClockConfig) -> Result<(), ClockError> {
|
||||||
if let Err(e) = config.rtc.enable_and_reset() {
|
if let Err(e) = config.rtc.enable_and_reset() {
|
||||||
error!("couldn't Power on OSC for RTC, result: {:?}", e);
|
|
||||||
return Err(e);
|
return Err(e);
|
||||||
}
|
}
|
||||||
|
|
||||||
if let Err(e) = config.lposc.enable_and_reset() {
|
if let Err(e) = config.lposc.enable_and_reset() {
|
||||||
error!("couldn't Power on LPOSC, result: {:?}", e);
|
|
||||||
return Err(e);
|
return Err(e);
|
||||||
}
|
}
|
||||||
|
|
||||||
if let Err(e) = config.ffro.enable_and_reset() {
|
if let Err(e) = config.ffro.enable_and_reset() {
|
||||||
error!("couldn't Power on FFRO, result: {:?}", e);
|
|
||||||
return Err(e);
|
return Err(e);
|
||||||
}
|
}
|
||||||
|
|
||||||
if let Err(e) = config.sfro.enable_and_reset() {
|
if let Err(e) = config.sfro.enable_and_reset() {
|
||||||
error!("couldn't Power on SFRO, result: {:?}", e);
|
|
||||||
return Err(e);
|
return Err(e);
|
||||||
}
|
}
|
||||||
|
|
||||||
if let Err(e) = config.sys_osc.enable_and_reset() {
|
if let Err(e) = config.sys_osc.enable_and_reset() {
|
||||||
error!("Couldn't enable sys oscillator {:?}", e);
|
|
||||||
return Err(e);
|
return Err(e);
|
||||||
}
|
}
|
||||||
|
|
||||||
if let Err(e) = config.main_pll_clk.enable_and_reset() {
|
if let Err(e) = config.main_pll_clk.enable_and_reset() {
|
||||||
error!("Couldn't enable main pll clock {:?}", e);
|
|
||||||
return Err(e);
|
return Err(e);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1542,7 +1503,6 @@ fn init_clock_hw(config: ClockConfig) -> Result<(), ClockError> {
|
|||||||
init_syscpuahb_clk();
|
init_syscpuahb_clk();
|
||||||
|
|
||||||
if let Err(e) = config.main_clk.enable_and_reset() {
|
if let Err(e) = config.main_clk.enable_and_reset() {
|
||||||
error!("Couldn't enable main clock {:?}", e);
|
|
||||||
return Err(e);
|
return Err(e);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1561,7 +1521,8 @@ pub(crate) unsafe fn init(config: ClockConfig) -> Result<(), ClockError> {
|
|||||||
|
|
||||||
///Trait to expose perph clocks
|
///Trait to expose perph clocks
|
||||||
trait SealedSysconPeripheral {
|
trait SealedSysconPeripheral {
|
||||||
fn enable_and_reset_perph_clock();
|
fn enable_perph_clock();
|
||||||
|
fn reset_perph();
|
||||||
fn disable_perph_clock();
|
fn disable_perph_clock();
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1574,7 +1535,18 @@ pub trait SysconPeripheral: SealedSysconPeripheral + 'static {}
|
|||||||
///
|
///
|
||||||
/// Peripheral must not be in use.
|
/// Peripheral must not be in use.
|
||||||
pub fn enable_and_reset<T: SysconPeripheral>() {
|
pub fn enable_and_reset<T: SysconPeripheral>() {
|
||||||
T::enable_and_reset_perph_clock();
|
T::enable_perph_clock();
|
||||||
|
T::reset_perph();
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Enables peripheral `T`.
|
||||||
|
pub fn enable<T: SysconPeripheral>() {
|
||||||
|
T::enable_perph_clock();
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Reset peripheral `T`.
|
||||||
|
pub fn reset<T: SysconPeripheral>() {
|
||||||
|
T::reset_perph();
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Disables peripheral `T`.
|
/// Disables peripheral `T`.
|
||||||
@ -1588,15 +1560,21 @@ pub fn disable<T: SysconPeripheral>() {
|
|||||||
macro_rules! impl_perph_clk {
|
macro_rules! impl_perph_clk {
|
||||||
($peripheral:ident, $clkctl:ident, $clkreg:ident, $rstctl:ident, $rstreg:ident, $bit:expr) => {
|
($peripheral:ident, $clkctl:ident, $clkreg:ident, $rstctl:ident, $rstreg:ident, $bit:expr) => {
|
||||||
impl SealedSysconPeripheral for crate::peripherals::$peripheral {
|
impl SealedSysconPeripheral for crate::peripherals::$peripheral {
|
||||||
fn enable_and_reset_perph_clock() {
|
fn enable_perph_clock() {
|
||||||
// SAFETY: unsafe needed to take pointers to Rstctl1 and Clkctl1
|
// SAFETY: unsafe needed to take pointers to Rstctl1 and Clkctl1
|
||||||
let cc1 = unsafe { pac::$clkctl::steal() };
|
let cc1 = unsafe { pac::$clkctl::steal() };
|
||||||
let rc1 = unsafe { pac::$rstctl::steal() };
|
|
||||||
|
|
||||||
paste! {
|
paste! {
|
||||||
// SAFETY: unsafe due to the use of bits()
|
// SAFETY: unsafe due to the use of bits()
|
||||||
cc1.[<$clkreg _set>]().write(|w| unsafe { w.bits(1 << $bit) });
|
cc1.[<$clkreg _set>]().write(|w| unsafe { w.bits(1 << $bit) });
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn reset_perph() {
|
||||||
|
// SAFETY: unsafe needed to take pointers to Rstctl1 and Clkctl1
|
||||||
|
let rc1 = unsafe { pac::$rstctl::steal() };
|
||||||
|
|
||||||
|
paste! {
|
||||||
// SAFETY: unsafe due to the use of bits()
|
// SAFETY: unsafe due to the use of bits()
|
||||||
rc1.[<$rstreg _clr>]().write(|w| unsafe { w.bits(1 << $bit) });
|
rc1.[<$rstreg _clr>]().write(|w| unsafe { w.bits(1 << $bit) });
|
||||||
}
|
}
|
||||||
@ -1605,12 +1583,8 @@ macro_rules! impl_perph_clk {
|
|||||||
fn disable_perph_clock() {
|
fn disable_perph_clock() {
|
||||||
// SAFETY: unsafe needed to take pointers to Rstctl1 and Clkctl1
|
// SAFETY: unsafe needed to take pointers to Rstctl1 and Clkctl1
|
||||||
let cc1 = unsafe { pac::$clkctl::steal() };
|
let cc1 = unsafe { pac::$clkctl::steal() };
|
||||||
let rc1 = unsafe { pac::$rstctl::steal() };
|
|
||||||
|
|
||||||
paste! {
|
paste! {
|
||||||
// SAFETY: unsafe due to the use of bits()
|
|
||||||
rc1.[<$rstreg _set>]().write(|w| unsafe { w.bits(1 << $bit) });
|
|
||||||
|
|
||||||
// SAFETY: unsafe due to the use of bits()
|
// SAFETY: unsafe due to the use of bits()
|
||||||
cc1.[<$clkreg _clr>]().write(|w| unsafe { w.bits(1 << $bit) });
|
cc1.[<$clkreg _clr>]().write(|w| unsafe { w.bits(1 << $bit) });
|
||||||
}
|
}
|
||||||
|
|||||||
@ -22,7 +22,7 @@ pub mod gpio;
|
|||||||
pub mod iopctl;
|
pub mod iopctl;
|
||||||
|
|
||||||
#[cfg(feature = "_time-driver")]
|
#[cfg(feature = "_time-driver")]
|
||||||
pub mod rtc;
|
pub mod time_driver;
|
||||||
|
|
||||||
// This mod MUST go last, so that it sees all the `impl_foo!' macros
|
// This mod MUST go last, so that it sees all the `impl_foo!' macros
|
||||||
#[cfg_attr(feature = "mimxrt633s", path = "chips/mimxrt633s.rs")]
|
#[cfg_attr(feature = "mimxrt633s", path = "chips/mimxrt633s.rs")]
|
||||||
@ -132,12 +132,10 @@ pub fn init(config: config::Config) -> Peripherals {
|
|||||||
error!("unable to initialize Clocks for reason: {:?}", e);
|
error!("unable to initialize Clocks for reason: {:?}", e);
|
||||||
// Panic here?
|
// Panic here?
|
||||||
}
|
}
|
||||||
gpio::init();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// init RTC time driver
|
|
||||||
#[cfg(feature = "_time-driver")]
|
#[cfg(feature = "_time-driver")]
|
||||||
rtc::init(config.time_interrupt_priority);
|
time_driver::init(config.time_interrupt_priority);
|
||||||
|
gpio::init();
|
||||||
|
|
||||||
peripherals
|
peripherals
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,5 +1,6 @@
|
|||||||
//! RTC Time Driver.
|
//! Time Driver.
|
||||||
use core::cell::{Cell, RefCell};
|
use core::cell::{Cell, RefCell};
|
||||||
|
#[cfg(feature = "time-driver-rtc")]
|
||||||
use core::sync::atomic::{compiler_fence, AtomicU32, Ordering};
|
use core::sync::atomic::{compiler_fence, AtomicU32, Ordering};
|
||||||
|
|
||||||
use critical_section::CriticalSection;
|
use critical_section::CriticalSection;
|
||||||
@ -8,27 +9,11 @@ use embassy_sync::blocking_mutex::Mutex;
|
|||||||
use embassy_time_driver::Driver;
|
use embassy_time_driver::Driver;
|
||||||
use embassy_time_queue_utils::Queue;
|
use embassy_time_queue_utils::Queue;
|
||||||
|
|
||||||
|
#[cfg(feature = "time-driver-os-timer")]
|
||||||
|
use crate::clocks::enable;
|
||||||
use crate::interrupt::InterruptExt;
|
use crate::interrupt::InterruptExt;
|
||||||
use crate::{interrupt, pac};
|
use crate::{interrupt, pac};
|
||||||
|
|
||||||
fn rtc() -> &'static pac::rtc::RegisterBlock {
|
|
||||||
unsafe { &*pac::Rtc::ptr() }
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Calculate the timestamp from the period count and the tick count.
|
|
||||||
///
|
|
||||||
/// To get `now()`, `period` is read first, then `counter` is read. If the counter value matches
|
|
||||||
/// the expected range for the `period` parity, we're done. If it doesn't, this means that
|
|
||||||
/// a new period start has raced us between reading `period` and `counter`, so we assume the `counter` value
|
|
||||||
/// corresponds to the next period.
|
|
||||||
///
|
|
||||||
/// the 1kHz RTC counter is 16 bits and RTC doesn't have separate compare channels,
|
|
||||||
/// so using a 32 bit GPREG0-2 as counter, compare, and int_en
|
|
||||||
/// `period` is a 32bit integer, gpreg 'counter' is 31 bits plus the parity bit for overflow detection
|
|
||||||
fn calc_now(period: u32, counter: u32) -> u64 {
|
|
||||||
((period as u64) << 31) + ((counter ^ ((period & 1) << 31)) as u64)
|
|
||||||
}
|
|
||||||
|
|
||||||
struct AlarmState {
|
struct AlarmState {
|
||||||
timestamp: Cell<u64>,
|
timestamp: Cell<u64>,
|
||||||
}
|
}
|
||||||
@ -43,6 +28,34 @@ impl AlarmState {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[cfg(feature = "time-driver-rtc")]
|
||||||
|
fn rtc() -> &'static pac::rtc::RegisterBlock {
|
||||||
|
unsafe { &*pac::Rtc::ptr() }
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Calculate the timestamp from the period count and the tick count.
|
||||||
|
///
|
||||||
|
/// To get `now()`, `period` is read first, then `counter` is read. If the counter value matches
|
||||||
|
/// the expected range for the `period` parity, we're done. If it doesn't, this means that
|
||||||
|
/// a new period start has raced us between reading `period` and `counter`, so we assume the `counter` value
|
||||||
|
/// corresponds to the next period.
|
||||||
|
///
|
||||||
|
/// the 1kHz RTC counter is 16 bits and RTC doesn't have separate compare channels,
|
||||||
|
/// so using a 32 bit GPREG0-2 as counter, compare, and int_en
|
||||||
|
/// `period` is a 32bit integer, gpreg 'counter' is 31 bits plus the parity bit for overflow detection
|
||||||
|
#[cfg(feature = "time-driver-rtc")]
|
||||||
|
fn calc_now(period: u32, counter: u32) -> u64 {
|
||||||
|
((period as u64) << 31) + ((counter ^ ((period & 1) << 31)) as u64)
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(feature = "time-driver-rtc")]
|
||||||
|
embassy_time_driver::time_driver_impl!(static DRIVER: Rtc = Rtc {
|
||||||
|
period: AtomicU32::new(0),
|
||||||
|
alarms: Mutex::const_new(CriticalSectionRawMutex::new(), AlarmState::new()),
|
||||||
|
queue: Mutex::new(RefCell::new(Queue::new())),
|
||||||
|
});
|
||||||
|
|
||||||
|
#[cfg(feature = "time-driver-rtc")]
|
||||||
struct Rtc {
|
struct Rtc {
|
||||||
/// Number of 2^31 periods elapsed since boot.
|
/// Number of 2^31 periods elapsed since boot.
|
||||||
period: AtomicU32,
|
period: AtomicU32,
|
||||||
@ -51,12 +64,7 @@ struct Rtc {
|
|||||||
queue: Mutex<CriticalSectionRawMutex, RefCell<Queue>>,
|
queue: Mutex<CriticalSectionRawMutex, RefCell<Queue>>,
|
||||||
}
|
}
|
||||||
|
|
||||||
embassy_time_driver::time_driver_impl!(static DRIVER: Rtc = Rtc {
|
#[cfg(feature = "time-driver-rtc")]
|
||||||
period: AtomicU32::new(0),
|
|
||||||
alarms: Mutex::const_new(CriticalSectionRawMutex::new(), AlarmState::new()),
|
|
||||||
queue: Mutex::new(RefCell::new(Queue::new())),
|
|
||||||
});
|
|
||||||
|
|
||||||
impl Rtc {
|
impl Rtc {
|
||||||
/// Access the GPREG0 register to use it as a 31-bit counter.
|
/// Access the GPREG0 register to use it as a 31-bit counter.
|
||||||
#[inline]
|
#[inline]
|
||||||
@ -219,6 +227,7 @@ impl Rtc {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[cfg(feature = "time-driver-rtc")]
|
||||||
impl Driver for Rtc {
|
impl Driver for Rtc {
|
||||||
fn now(&self) -> u64 {
|
fn now(&self) -> u64 {
|
||||||
// `period` MUST be read before `counter`, see comment at the top for details.
|
// `period` MUST be read before `counter`, see comment at the top for details.
|
||||||
@ -242,13 +251,158 @@ impl Driver for Rtc {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(feature = "rt")]
|
#[cfg(all(feature = "rt", feature = "time-driver-rtc"))]
|
||||||
#[allow(non_snake_case)]
|
#[allow(non_snake_case)]
|
||||||
#[interrupt]
|
#[interrupt]
|
||||||
fn RTC() {
|
fn RTC() {
|
||||||
DRIVER.on_interrupt()
|
DRIVER.on_interrupt()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[cfg(feature = "time-driver-os-timer")]
|
||||||
|
fn os() -> &'static pac::ostimer0::RegisterBlock {
|
||||||
|
unsafe { &*pac::Ostimer0::ptr() }
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Convert gray to decimal
|
||||||
|
///
|
||||||
|
/// Os Event provides a 64-bit timestamp gray-encoded. All we have to
|
||||||
|
/// do here is read both 32-bit halves of the register and convert
|
||||||
|
/// from gray to regular binary.
|
||||||
|
#[cfg(feature = "time-driver-os-timer")]
|
||||||
|
fn gray_to_dec(gray: u64) -> u64 {
|
||||||
|
let mut dec = gray;
|
||||||
|
|
||||||
|
dec ^= dec >> 1;
|
||||||
|
dec ^= dec >> 2;
|
||||||
|
dec ^= dec >> 4;
|
||||||
|
dec ^= dec >> 8;
|
||||||
|
dec ^= dec >> 16;
|
||||||
|
dec ^= dec >> 32;
|
||||||
|
|
||||||
|
dec
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Convert decimal to gray
|
||||||
|
///
|
||||||
|
/// Before writing match value to the target register, we must convert
|
||||||
|
/// it back into gray code.
|
||||||
|
#[cfg(feature = "time-driver-os-timer")]
|
||||||
|
fn dec_to_gray(dec: u64) -> u64 {
|
||||||
|
let gray = dec;
|
||||||
|
gray ^ (gray >> 1)
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(feature = "time-driver-os-timer")]
|
||||||
|
embassy_time_driver::time_driver_impl!(static DRIVER: OsTimer = OsTimer {
|
||||||
|
alarms: Mutex::const_new(CriticalSectionRawMutex::new(), AlarmState::new()),
|
||||||
|
queue: Mutex::new(RefCell::new(Queue::new())),
|
||||||
|
});
|
||||||
|
|
||||||
|
#[cfg(feature = "time-driver-os-timer")]
|
||||||
|
struct OsTimer {
|
||||||
|
/// Timestamp at which to fire alarm. u64::MAX if no alarm is scheduled.
|
||||||
|
alarms: Mutex<CriticalSectionRawMutex, AlarmState>,
|
||||||
|
queue: Mutex<CriticalSectionRawMutex, RefCell<Queue>>,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(feature = "time-driver-os-timer")]
|
||||||
|
impl OsTimer {
|
||||||
|
fn init(&'static self, irq_prio: crate::interrupt::Priority) {
|
||||||
|
// init alarms
|
||||||
|
critical_section::with(|cs| {
|
||||||
|
let alarm = DRIVER.alarms.borrow(cs);
|
||||||
|
alarm.timestamp.set(u64::MAX);
|
||||||
|
});
|
||||||
|
|
||||||
|
// Enable clocks. Documentation advises AGAINST resetting this
|
||||||
|
// peripheral.
|
||||||
|
enable::<crate::peripherals::OS_EVENT>();
|
||||||
|
|
||||||
|
interrupt::OS_EVENT.disable();
|
||||||
|
|
||||||
|
// Make sure interrupt is masked
|
||||||
|
os().osevent_ctrl().modify(|_, w| w.ostimer_intena().clear_bit());
|
||||||
|
|
||||||
|
// Default to the end of time
|
||||||
|
os().match_l().write(|w| unsafe { w.bits(0xffff_ffff) });
|
||||||
|
os().match_h().write(|w| unsafe { w.bits(0xffff_ffff) });
|
||||||
|
|
||||||
|
interrupt::OS_EVENT.unpend();
|
||||||
|
interrupt::OS_EVENT.set_priority(irq_prio);
|
||||||
|
unsafe { interrupt::OS_EVENT.enable() };
|
||||||
|
}
|
||||||
|
|
||||||
|
fn set_alarm(&self, cs: CriticalSection, timestamp: u64) -> bool {
|
||||||
|
let alarm = self.alarms.borrow(cs);
|
||||||
|
alarm.timestamp.set(timestamp);
|
||||||
|
|
||||||
|
let t = self.now();
|
||||||
|
if timestamp <= t {
|
||||||
|
os().osevent_ctrl().modify(|_, w| w.ostimer_intena().clear_bit());
|
||||||
|
alarm.timestamp.set(u64::MAX);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
let gray_timestamp = dec_to_gray(timestamp);
|
||||||
|
|
||||||
|
os().match_l()
|
||||||
|
.write(|w| unsafe { w.bits(gray_timestamp as u32 & 0xffff_ffff) });
|
||||||
|
os().match_h()
|
||||||
|
.write(|w| unsafe { w.bits((gray_timestamp >> 32) as u32) });
|
||||||
|
os().osevent_ctrl().modify(|_, w| w.ostimer_intena().set_bit());
|
||||||
|
|
||||||
|
true
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(feature = "rt")]
|
||||||
|
fn trigger_alarm(&self, cs: CriticalSection) {
|
||||||
|
let mut next = self.queue.borrow(cs).borrow_mut().next_expiration(self.now());
|
||||||
|
while !self.set_alarm(cs, next) {
|
||||||
|
next = self.queue.borrow(cs).borrow_mut().next_expiration(self.now());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(feature = "rt")]
|
||||||
|
fn on_interrupt(&self) {
|
||||||
|
critical_section::with(|cs| {
|
||||||
|
if os().osevent_ctrl().read().ostimer_intrflag().bit_is_set() {
|
||||||
|
os().osevent_ctrl().modify(|_, w| w.ostimer_intena().clear_bit());
|
||||||
|
self.trigger_alarm(cs);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(feature = "time-driver-os-timer")]
|
||||||
|
impl Driver for OsTimer {
|
||||||
|
fn now(&self) -> u64 {
|
||||||
|
let mut t = os().evtimerh().read().bits() as u64;
|
||||||
|
t <<= 32;
|
||||||
|
t |= os().evtimerl().read().bits() as u64;
|
||||||
|
gray_to_dec(t)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn schedule_wake(&self, at: u64, waker: &core::task::Waker) {
|
||||||
|
critical_section::with(|cs| {
|
||||||
|
let mut queue = self.queue.borrow(cs).borrow_mut();
|
||||||
|
|
||||||
|
if queue.schedule_wake(at, waker) {
|
||||||
|
let mut next = queue.next_expiration(self.now());
|
||||||
|
while !self.set_alarm(cs, next) {
|
||||||
|
next = queue.next_expiration(self.now());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(all(feature = "rt", feature = "time-driver-os-timer"))]
|
||||||
|
#[allow(non_snake_case)]
|
||||||
|
#[interrupt]
|
||||||
|
fn OS_EVENT() {
|
||||||
|
DRIVER.on_interrupt()
|
||||||
|
}
|
||||||
|
|
||||||
pub(crate) fn init(irq_prio: crate::interrupt::Priority) {
|
pub(crate) fn init(irq_prio: crate::interrupt::Priority) {
|
||||||
DRIVER.init(irq_prio)
|
DRIVER.init(irq_prio)
|
||||||
}
|
}
|
||||||
@ -12,8 +12,8 @@ defmt-rtt = "1.0"
|
|||||||
|
|
||||||
embassy-executor = { version = "0.7.0", path = "../../embassy-executor", features = ["arch-cortex-m", "executor-thread", "executor-interrupt", "defmt"] }
|
embassy-executor = { version = "0.7.0", path = "../../embassy-executor", features = ["arch-cortex-m", "executor-thread", "executor-interrupt", "defmt"] }
|
||||||
embassy-futures = { version = "0.1.1", path = "../../embassy-futures" }
|
embassy-futures = { version = "0.1.1", path = "../../embassy-futures" }
|
||||||
embassy-imxrt = { version = "0.1.0", path = "../../embassy-imxrt", features = ["defmt", "mimxrt685s", "unstable-pac", "time", "time-driver-rtc"] }
|
embassy-imxrt = { version = "0.1.0", path = "../../embassy-imxrt", features = ["defmt", "mimxrt685s", "unstable-pac", "time", "time-driver-os-timer"] }
|
||||||
embassy-time = { version = "0.4", path = "../../embassy-time" }
|
embassy-time = { version = "0.4", path = "../../embassy-time", features = ["defmt", "defmt-timestamp-uptime"] }
|
||||||
embassy-sync = { version = "0.6.2", path = "../../embassy-sync", features = ["defmt"] }
|
embassy-sync = { version = "0.6.2", path = "../../embassy-sync", features = ["defmt"] }
|
||||||
embedded-hal-1 = { package = "embedded-hal", version = "1.0" }
|
embedded-hal-1 = { package = "embedded-hal", version = "1.0" }
|
||||||
embedded-hal-async = "1.0.0"
|
embedded-hal-async = "1.0.0"
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user