hrtim: fix example and auto adjust psc.
This commit is contained in:
parent
8141d53d94
commit
aceba1c03f
@ -144,6 +144,18 @@ impl<'d, T: HighResolutionCaptureCompare16bitInstance> AdvancedPwm<'d, T> {
|
|||||||
T::enable();
|
T::enable();
|
||||||
<T as crate::rcc::sealed::RccPeripheral>::reset();
|
<T as crate::rcc::sealed::RccPeripheral>::reset();
|
||||||
|
|
||||||
|
// // Enable and and stabilize the DLL
|
||||||
|
// T::regs().dllcr().modify(|w| {
|
||||||
|
// // w.set_calen(true);
|
||||||
|
// // w.set_calrte(11);
|
||||||
|
// w.set_cal(true);
|
||||||
|
// });
|
||||||
|
//
|
||||||
|
// debug!("wait for dll calibration");
|
||||||
|
// while !T::regs().isr().read().dllrdy() {}
|
||||||
|
//
|
||||||
|
// debug!("dll calibration complete");
|
||||||
|
|
||||||
Self {
|
Self {
|
||||||
_inner: tim,
|
_inner: tim,
|
||||||
master: Master { phantom: PhantomData },
|
master: Master { phantom: PhantomData },
|
||||||
@ -173,12 +185,14 @@ impl<T: HighResolutionCaptureCompare16bitInstance> BurstController<T> {
|
|||||||
/// light loading conditions, and that the low-side switch must be active for a short time to drive
|
/// light loading conditions, and that the low-side switch must be active for a short time to drive
|
||||||
/// a bootstrapped high-side switch.
|
/// a bootstrapped high-side switch.
|
||||||
pub struct BridgeConverter<T: HighResolutionCaptureCompare16bitInstance, C: AdvancedChannel<T>> {
|
pub struct BridgeConverter<T: HighResolutionCaptureCompare16bitInstance, C: AdvancedChannel<T>> {
|
||||||
phantom: PhantomData<T>,
|
timer: PhantomData<T>,
|
||||||
pub ch: C,
|
channel: PhantomData<C>,
|
||||||
|
dead_time: u16,
|
||||||
|
primary_duty: u16,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<T: HighResolutionCaptureCompare16bitInstance, C: AdvancedChannel<T>> BridgeConverter<T, C> {
|
impl<T: HighResolutionCaptureCompare16bitInstance, C: AdvancedChannel<T>> BridgeConverter<T, C> {
|
||||||
pub fn new(channel: C, frequency: Hertz) -> Self {
|
pub fn new(_channel: C, frequency: Hertz) -> Self {
|
||||||
use crate::pac::hrtim::vals::{Activeeffect, Cont, Inactiveeffect};
|
use crate::pac::hrtim::vals::{Activeeffect, Cont, Inactiveeffect};
|
||||||
|
|
||||||
T::set_channel_frequency(C::raw(), frequency);
|
T::set_channel_frequency(C::raw(), frequency);
|
||||||
@ -186,15 +200,21 @@ impl<T: HighResolutionCaptureCompare16bitInstance, C: AdvancedChannel<T>> Bridge
|
|||||||
// Always enable preload
|
// Always enable preload
|
||||||
T::regs().tim(C::raw()).cr().modify(|w| {
|
T::regs().tim(C::raw()).cr().modify(|w| {
|
||||||
w.set_preen(true);
|
w.set_preen(true);
|
||||||
|
w.set_repu(true);
|
||||||
|
|
||||||
w.set_cont(Cont::CONTINUOUS);
|
w.set_cont(Cont::CONTINUOUS);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
// Enable timer outputs
|
||||||
T::regs().oenr().modify(|w| {
|
T::regs().oenr().modify(|w| {
|
||||||
w.set_t1oen(C::raw(), true);
|
w.set_t1oen(C::raw(), true);
|
||||||
w.set_t2oen(C::raw(), true);
|
w.set_t2oen(C::raw(), true);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
// The dead-time generation unit cannot be used because it forces the other output
|
||||||
|
// to be completely complementary to the first output, which restricts certain waveforms
|
||||||
|
// Therefore, software-implemented dead time must be used when setting the duty cycles
|
||||||
|
|
||||||
// Set output 1 to active on a period event
|
// Set output 1 to active on a period event
|
||||||
T::regs()
|
T::regs()
|
||||||
.tim(C::raw())
|
.tim(C::raw())
|
||||||
@ -207,21 +227,23 @@ impl<T: HighResolutionCaptureCompare16bitInstance, C: AdvancedChannel<T>> Bridge
|
|||||||
.rstr(0)
|
.rstr(0)
|
||||||
.modify(|w| w.set_cmp(0, Inactiveeffect::SETINACTIVE));
|
.modify(|w| w.set_cmp(0, Inactiveeffect::SETINACTIVE));
|
||||||
|
|
||||||
// Set output 2 to active on a compare 1 event
|
// Set output 2 to active on a compare 2 event
|
||||||
T::regs()
|
T::regs()
|
||||||
.tim(C::raw())
|
.tim(C::raw())
|
||||||
.setr(1)
|
.setr(1)
|
||||||
.modify(|w| w.set_cmp(0, Activeeffect::SETACTIVE));
|
.modify(|w| w.set_cmp(1, Activeeffect::SETACTIVE));
|
||||||
|
|
||||||
// Set output 2 to inactive on a compare 2 event
|
// Set output 2 to inactive on a compare 3 event
|
||||||
T::regs()
|
T::regs()
|
||||||
.tim(C::raw())
|
.tim(C::raw())
|
||||||
.rstr(1)
|
.rstr(1)
|
||||||
.modify(|w| w.set_cmp(1, Inactiveeffect::SETINACTIVE));
|
.modify(|w| w.set_cmp(2, Inactiveeffect::SETINACTIVE));
|
||||||
|
|
||||||
Self {
|
Self {
|
||||||
phantom: PhantomData,
|
timer: PhantomData,
|
||||||
ch: channel,
|
channel: PhantomData,
|
||||||
|
dead_time: 0,
|
||||||
|
primary_duty: 0,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -236,7 +258,6 @@ impl<T: HighResolutionCaptureCompare16bitInstance, C: AdvancedChannel<T>> Bridge
|
|||||||
pub fn enable_burst_mode(&mut self) {
|
pub fn enable_burst_mode(&mut self) {
|
||||||
use crate::pac::hrtim::vals::{Idlem, Idles};
|
use crate::pac::hrtim::vals::{Idlem, Idles};
|
||||||
|
|
||||||
// TODO: fix metapac
|
|
||||||
T::regs().tim(C::raw()).outr().modify(|w| {
|
T::regs().tim(C::raw()).outr().modify(|w| {
|
||||||
w.set_idlem(0, Idlem::SETIDLE);
|
w.set_idlem(0, Idlem::SETIDLE);
|
||||||
w.set_idlem(1, Idlem::SETIDLE);
|
w.set_idlem(1, Idlem::SETIDLE);
|
||||||
@ -258,9 +279,18 @@ impl<T: HighResolutionCaptureCompare16bitInstance, C: AdvancedChannel<T>> Bridge
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn update_primary_duty_or_dead_time(&mut self) {
|
||||||
|
T::regs().tim(C::raw()).cmp(0).modify(|w| w.set_cmp(self.primary_duty));
|
||||||
|
T::regs()
|
||||||
|
.tim(C::raw())
|
||||||
|
.cmp(1)
|
||||||
|
.modify(|w| w.set_cmp(self.primary_duty + self.dead_time));
|
||||||
|
}
|
||||||
|
|
||||||
/// Set the dead time as a proportion of the maximum compare value
|
/// Set the dead time as a proportion of the maximum compare value
|
||||||
pub fn set_dead_time(&mut self, value: u16) {
|
pub fn set_dead_time(&mut self, dead_time: u16) {
|
||||||
T::set_channel_dead_time(C::raw(), value);
|
self.dead_time = dead_time;
|
||||||
|
self.update_primary_duty_or_dead_time();
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Get the maximum compare value of a duty cycle
|
/// Get the maximum compare value of a duty cycle
|
||||||
@ -272,15 +302,17 @@ impl<T: HighResolutionCaptureCompare16bitInstance, C: AdvancedChannel<T>> Bridge
|
|||||||
///
|
///
|
||||||
/// In the case of a buck converter, this is the high-side switch
|
/// In the case of a buck converter, this is the high-side switch
|
||||||
/// In the case of a boost converter, this is the low-side switch
|
/// In the case of a boost converter, this is the low-side switch
|
||||||
pub fn set_primary_duty(&mut self, primary: u16) {
|
pub fn set_primary_duty(&mut self, primary_duty: u16) {
|
||||||
T::regs().tim(C::raw()).cmp(0).modify(|w| w.set_cmp(primary));
|
self.primary_duty = primary_duty;
|
||||||
|
self.update_primary_duty_or_dead_time();
|
||||||
}
|
}
|
||||||
|
|
||||||
/// The primary duty is the period in any switch is active
|
/// The secondary duty is the period in any switch is active
|
||||||
///
|
///
|
||||||
/// If less than or equal to the primary duty, the secondary switch will never be active
|
/// If less than or equal to the primary duty, the secondary switch will never be active
|
||||||
pub fn set_secondary_duty(&mut self, secondary: u16) {
|
/// If a fully complementary output is desired, the secondary duty can be set to the max compare
|
||||||
T::regs().tim(C::raw()).cmp(1).modify(|w| w.set_cmp(secondary));
|
pub fn set_secondary_duty(&mut self, secondary_duty: u16) {
|
||||||
|
T::regs().tim(C::raw()).cmp(2).modify(|w| w.set_cmp(secondary_duty));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -290,14 +322,14 @@ impl<T: HighResolutionCaptureCompare16bitInstance, C: AdvancedChannel<T>> Bridge
|
|||||||
/// but does not include secondary rectification, which is appropriate for applications
|
/// but does not include secondary rectification, which is appropriate for applications
|
||||||
/// with a low-voltage on the secondary side.
|
/// with a low-voltage on the secondary side.
|
||||||
pub struct ResonantConverter<T: HighResolutionCaptureCompare16bitInstance, C: AdvancedChannel<T>> {
|
pub struct ResonantConverter<T: HighResolutionCaptureCompare16bitInstance, C: AdvancedChannel<T>> {
|
||||||
phantom: PhantomData<T>,
|
timer: PhantomData<T>,
|
||||||
|
channel: PhantomData<C>,
|
||||||
min_period: u16,
|
min_period: u16,
|
||||||
max_period: u16,
|
max_period: u16,
|
||||||
pub ch: C,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<T: HighResolutionCaptureCompare16bitInstance, C: AdvancedChannel<T>> ResonantConverter<T, C> {
|
impl<T: HighResolutionCaptureCompare16bitInstance, C: AdvancedChannel<T>> ResonantConverter<T, C> {
|
||||||
pub fn new(channel: C, min_frequency: Hertz, max_frequency: Hertz) -> Self {
|
pub fn new(_channel: C, min_frequency: Hertz, max_frequency: Hertz) -> Self {
|
||||||
use crate::pac::hrtim::vals::Cont;
|
use crate::pac::hrtim::vals::Cont;
|
||||||
|
|
||||||
T::set_channel_frequency(C::raw(), min_frequency);
|
T::set_channel_frequency(C::raw(), min_frequency);
|
||||||
@ -305,19 +337,30 @@ impl<T: HighResolutionCaptureCompare16bitInstance, C: AdvancedChannel<T>> Resona
|
|||||||
// Always enable preload
|
// Always enable preload
|
||||||
T::regs().tim(C::raw()).cr().modify(|w| {
|
T::regs().tim(C::raw()).cr().modify(|w| {
|
||||||
w.set_preen(true);
|
w.set_preen(true);
|
||||||
|
w.set_repu(true);
|
||||||
|
|
||||||
w.set_cont(Cont::CONTINUOUS);
|
w.set_cont(Cont::CONTINUOUS);
|
||||||
w.set_half(true);
|
w.set_half(true);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
// Enable timer outputs
|
||||||
|
T::regs().oenr().modify(|w| {
|
||||||
|
w.set_t1oen(C::raw(), true);
|
||||||
|
w.set_t2oen(C::raw(), true);
|
||||||
|
});
|
||||||
|
|
||||||
|
// Dead-time generator can be used in this case because the primary fets
|
||||||
|
// of a resonant converter are always complementary
|
||||||
|
T::regs().tim(C::raw()).outr().modify(|w| w.set_dten(true));
|
||||||
|
|
||||||
let max_period = T::regs().tim(C::raw()).per().read().per();
|
let max_period = T::regs().tim(C::raw()).per().read().per();
|
||||||
let min_period = max_period * (min_frequency.0 / max_frequency.0) as u16;
|
let min_period = max_period * (min_frequency.0 / max_frequency.0) as u16;
|
||||||
|
|
||||||
Self {
|
Self {
|
||||||
|
timer: PhantomData,
|
||||||
|
channel: PhantomData,
|
||||||
min_period: min_period,
|
min_period: min_period,
|
||||||
max_period: max_period,
|
max_period: max_period,
|
||||||
phantom: PhantomData,
|
|
||||||
ch: channel,
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -123,7 +123,7 @@ impl From<u8> for HighResolutionControlPrescaler {
|
|||||||
|
|
||||||
#[cfg(hrtim_v1)]
|
#[cfg(hrtim_v1)]
|
||||||
impl HighResolutionControlPrescaler {
|
impl HighResolutionControlPrescaler {
|
||||||
pub fn compute_min(val: u32) -> Self {
|
pub fn compute_min_high_res(val: u32) -> Self {
|
||||||
*[
|
*[
|
||||||
HighResolutionControlPrescaler::Div1,
|
HighResolutionControlPrescaler::Div1,
|
||||||
HighResolutionControlPrescaler::Div2,
|
HighResolutionControlPrescaler::Div2,
|
||||||
@ -139,6 +139,18 @@ impl HighResolutionControlPrescaler {
|
|||||||
.next()
|
.next()
|
||||||
.unwrap()
|
.unwrap()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn compute_min_low_res(val: u32) -> Self {
|
||||||
|
*[
|
||||||
|
HighResolutionControlPrescaler::Div32,
|
||||||
|
HighResolutionControlPrescaler::Div64,
|
||||||
|
HighResolutionControlPrescaler::Div128,
|
||||||
|
]
|
||||||
|
.iter()
|
||||||
|
.skip_while(|psc| <HighResolutionControlPrescaler as Into<u32>>::into(**psc) <= val)
|
||||||
|
.next()
|
||||||
|
.unwrap()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(crate) mod sealed {
|
pub(crate) mod sealed {
|
||||||
@ -367,10 +379,14 @@ foreach_interrupt! {
|
|||||||
let f = frequency.0;
|
let f = frequency.0;
|
||||||
let timer_f = Self::frequency().0;
|
let timer_f = Self::frequency().0;
|
||||||
let psc_min = (timer_f / f) / (u16::MAX as u32 / 32);
|
let psc_min = (timer_f / f) / (u16::MAX as u32 / 32);
|
||||||
let psc = HighResolutionControlPrescaler::compute_min(psc_min);
|
let psc = if Self::regs().isr().read().dllrdy() {
|
||||||
|
HighResolutionControlPrescaler::compute_min_high_res(psc_min)
|
||||||
|
} else {
|
||||||
|
HighResolutionControlPrescaler::compute_min_low_res(psc_min)
|
||||||
|
};
|
||||||
|
|
||||||
let psc_val: u32 = psc.into();
|
let psc_val: u32 = psc.into();
|
||||||
let timer_f = timer_f / psc_val;
|
let timer_f = 32 * (timer_f / psc_val);
|
||||||
let per: u16 = (timer_f / f) as u16;
|
let per: u16 = (timer_f / f) as u16;
|
||||||
|
|
||||||
let regs = Self::regs();
|
let regs = Self::regs();
|
||||||
@ -386,10 +402,14 @@ foreach_interrupt! {
|
|||||||
let f = frequency.0;
|
let f = frequency.0;
|
||||||
let timer_f = Self::frequency().0;
|
let timer_f = Self::frequency().0;
|
||||||
let psc_min = (timer_f / f) / (u16::MAX as u32 / 32);
|
let psc_min = (timer_f / f) / (u16::MAX as u32 / 32);
|
||||||
let psc = HighResolutionControlPrescaler::compute_min(psc_min);
|
let psc = if Self::regs().isr().read().dllrdy() {
|
||||||
|
HighResolutionControlPrescaler::compute_min_high_res(psc_min)
|
||||||
|
} else {
|
||||||
|
HighResolutionControlPrescaler::compute_min_low_res(psc_min)
|
||||||
|
};
|
||||||
|
|
||||||
let psc_val: u32 = psc.into();
|
let psc_val: u32 = psc.into();
|
||||||
let timer_f = timer_f / psc_val;
|
let timer_f = 32 * (timer_f / psc_val);
|
||||||
let per: u16 = (timer_f / f) as u16;
|
let per: u16 = (timer_f / f) as u16;
|
||||||
|
|
||||||
let regs = Self::regs();
|
let regs = Self::regs();
|
||||||
@ -410,7 +430,12 @@ foreach_interrupt! {
|
|||||||
// The dead-time base clock runs 4 times slower than the hrtim base clock
|
// The dead-time base clock runs 4 times slower than the hrtim base clock
|
||||||
// u9::MAX = 511
|
// u9::MAX = 511
|
||||||
let psc_min = (psc_val * dead_time as u32) / (4 * 511);
|
let psc_min = (psc_val * dead_time as u32) / (4 * 511);
|
||||||
let psc = HighResolutionControlPrescaler::compute_min(psc_min);
|
let psc = if Self::regs().isr().read().dllrdy() {
|
||||||
|
HighResolutionControlPrescaler::compute_min_high_res(psc_min)
|
||||||
|
} else {
|
||||||
|
HighResolutionControlPrescaler::compute_min_low_res(psc_min)
|
||||||
|
};
|
||||||
|
|
||||||
let dt_psc_val: u32 = psc.into();
|
let dt_psc_val: u32 = psc.into();
|
||||||
let dt_val = (dt_psc_val * dead_time as u32) / (4 * psc_val);
|
let dt_val = (dt_psc_val * dead_time as u32) / (4 * psc_val);
|
||||||
|
|
||||||
|
27
examples/stm32f334/src/bin/button.rs
Normal file
27
examples/stm32f334/src/bin/button.rs
Normal file
@ -0,0 +1,27 @@
|
|||||||
|
#![no_std]
|
||||||
|
#![no_main]
|
||||||
|
#![feature(type_alias_impl_trait)]
|
||||||
|
|
||||||
|
use defmt::*;
|
||||||
|
use embassy_executor::Spawner;
|
||||||
|
use embassy_stm32::gpio::{Level, Output, Speed};
|
||||||
|
use embassy_time::{Duration, Timer};
|
||||||
|
use {defmt_rtt as _, panic_probe as _};
|
||||||
|
|
||||||
|
#[embassy_executor::main]
|
||||||
|
async fn main(_spawner: Spawner) {
|
||||||
|
info!("Hello World!");
|
||||||
|
|
||||||
|
let p = embassy_stm32::init(Default::default());
|
||||||
|
|
||||||
|
let mut out1 = Output::new(p.PA8, Level::Low, Speed::High);
|
||||||
|
|
||||||
|
out1.set_high();
|
||||||
|
Timer::after(Duration::from_millis(500)).await;
|
||||||
|
out1.set_low();
|
||||||
|
|
||||||
|
Timer::after(Duration::from_millis(500)).await;
|
||||||
|
info!("end program");
|
||||||
|
|
||||||
|
cortex_m::asm::bkpt();
|
||||||
|
}
|
@ -5,12 +5,20 @@
|
|||||||
use defmt::*;
|
use defmt::*;
|
||||||
use embassy_executor::Spawner;
|
use embassy_executor::Spawner;
|
||||||
use embassy_stm32::pwm::advanced_pwm::*;
|
use embassy_stm32::pwm::advanced_pwm::*;
|
||||||
use embassy_stm32::time::khz;
|
use embassy_stm32::time::{khz, mhz};
|
||||||
|
use embassy_stm32::Config;
|
||||||
|
use embassy_time::{Duration, Timer};
|
||||||
use {defmt_rtt as _, panic_probe as _};
|
use {defmt_rtt as _, panic_probe as _};
|
||||||
|
|
||||||
#[embassy_executor::main]
|
#[embassy_executor::main]
|
||||||
async fn main(_spawner: Spawner) {
|
async fn main(_spawner: Spawner) {
|
||||||
let p = embassy_stm32::init(Default::default());
|
let mut config: Config = Default::default();
|
||||||
|
config.rcc.sysclk = Some(mhz(64));
|
||||||
|
config.rcc.hclk = Some(mhz(64));
|
||||||
|
config.rcc.pclk1 = Some(mhz(32));
|
||||||
|
config.rcc.pclk2 = Some(mhz(64));
|
||||||
|
|
||||||
|
let p = embassy_stm32::init(config);
|
||||||
info!("Hello World!");
|
info!("Hello World!");
|
||||||
|
|
||||||
let ch1 = PwmPin::new_cha(p.PA8);
|
let ch1 = PwmPin::new_cha(p.PA8);
|
||||||
@ -29,34 +37,35 @@ async fn main(_spawner: Spawner) {
|
|||||||
None,
|
None,
|
||||||
);
|
);
|
||||||
|
|
||||||
let mut buck_converter = BridgeConverter::new(pwm.ch_a, khz(100));
|
info!("pwm constructed");
|
||||||
|
|
||||||
buck_converter.set_primary_duty(0);
|
let mut buck_converter = BridgeConverter::new(pwm.ch_a, khz(5));
|
||||||
buck_converter.set_secondary_duty(0);
|
|
||||||
buck_converter.set_dead_time(0);
|
|
||||||
|
|
||||||
// note: if the pins are not passed into the advanced pwm struct, they will not be output
|
// embassy_stm32::pac::HRTIM1
|
||||||
let mut boost_converter = BridgeConverter::new(pwm.ch_b, khz(100));
|
// .tim(0)
|
||||||
|
// .setr(0)
|
||||||
boost_converter.set_primary_duty(0);
|
// .modify(|w| w.set_sst(Activeeffect::SETACTIVE));
|
||||||
boost_converter.set_secondary_duty(0);
|
|
||||||
|
|
||||||
// let max = pwm.get_max_duty();
|
|
||||||
// pwm.set_dead_time(max / 1024);
|
|
||||||
//
|
//
|
||||||
// pwm.enable(Channel::Ch1);
|
// Timer::after(Duration::from_millis(500)).await;
|
||||||
//
|
//
|
||||||
// info!("PWM initialized");
|
// embassy_stm32::pac::HRTIM1
|
||||||
// info!("PWM max duty {}", max);
|
// .tim(0)
|
||||||
//
|
// .rstr(0)
|
||||||
// loop {
|
// .modify(|w| w.set_srt(Inactiveeffect::SETINACTIVE));
|
||||||
// pwm.set_duty(Channel::Ch1, 0);
|
|
||||||
// Timer::after(Duration::from_millis(300)).await;
|
let max_duty = buck_converter.get_max_compare_value();
|
||||||
// pwm.set_duty(Channel::Ch1, max / 4);
|
|
||||||
// Timer::after(Duration::from_millis(300)).await;
|
info!("max compare value: {}", max_duty);
|
||||||
// pwm.set_duty(Channel::Ch1, max / 2);
|
|
||||||
// Timer::after(Duration::from_millis(300)).await;
|
buck_converter.set_dead_time(max_duty / 20);
|
||||||
// pwm.set_duty(Channel::Ch1, max - 1);
|
buck_converter.set_primary_duty(max_duty / 2);
|
||||||
// Timer::after(Duration::from_millis(300)).await;
|
buck_converter.set_secondary_duty(3 * max_duty / 4);
|
||||||
// }
|
|
||||||
|
buck_converter.start();
|
||||||
|
|
||||||
|
Timer::after(Duration::from_millis(500)).await;
|
||||||
|
|
||||||
|
info!("end program");
|
||||||
|
|
||||||
|
cortex_m::asm::bkpt();
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user