..and now I'm stuck to do this in Rust.
At first I show what I've done so far.
#![no_std]
#![no_main]
use panic_halt as _;
use cortex_m_rt::entry;
use core::{cell::RefCell};
use core::ops::DerefMut;
use cortex_m::interrupt::{self, Mutex};
use stm32g0::stm32g071::{self, Interrupt, NVIC, TIM3};
static G_TIM: Mutex<RefCell<Option<stm32g071::TIM3>>> =
Mutex::new(RefCell::new(None));
#[entry]
fn main() -> ! {
let p = stm32g071::Peripherals::take().unwrap();
let rcc_r = &p.RCC;
let timer_r = &p.TIM3;
let tim3 = p.TIM3;
unsafe {
NVIC::unmask(Interrupt::TIM3);
};
rcc_r.apbenr1.write(|w| w.tim3en().set_bit());
prepare_timer3(timer_r);
interrupt::free(|cs| {
G_TIM.borrow(cs).replace(Some(tim3))
});
loop {
}
}
fn prepare_timer3(tim3_r_handle: &TIM3) {
tim3_r_handle.cr1.write(|w| w.cen().clear_bit());
tim3_r_handle.psc.write(|w| unsafe { w.psc().bits(16000) });
tim3_r_handle.arr.write(|w| unsafe { w.arr_l().bits(100) });
tim3_r_handle.egr.write(|w| w.ug().set_bit());
tim3_r_handle.dier.write(|w| w.uie().set_bit());
tim3_r_handle.cr1.write(|w| w.cen().set_bit());
}
#[interrupt]
fn TIM3() {
interrupt::free(|cs| {
if let Some(ref mut tim3) = G_TIM.borrow(cs).borrow_mut().deref_mut() {
tim3.sr.write(|w| w.uif().clear_bit());
}
})
}
And I get this compilation error:
error: cannot find attribute `interrupt` in this scope
--> src/main.rs:51:3
|
51 | #[interrupt]
| ^^^^^^^^^
|
= note: consider importing one of these items:
cortex_m_rt::interrupt
crate::stm32g071::interrupt
stm32g0::stm32g071::interrupt
note: `interrupt` is imported here, but it is a module, not an attribute
--> src/main.rs:10:27
|
10 | use cortex_m::interrupt::{self, Mutex};
| ^^^^
error: could not compile `blink-nucleo-g0` due to previous error
Are you sure? Interrupt is an enum but I need interrupt as attribute but if I change line with use cortex_m::interrupt::{self, Mutex}; and add interrupt here use stm32g0::stm32g071::{self, Interrupt, NVIC, TIM3}; then I lost Mutex etc.
If I do that then it's problem with interrupt::free.
error[E0599]: no variant or associated item named `free` found for enum `stm32g0::stm32g071::Interrupt` in the current scope
--> src/main.rs:34:16
|
34 | interrupt::free(|cs| {
| ^^^^ variant or associated item not found in `stm32g0::stm32g071::Interrupt`
error[E0599]: no variant or associated item named `free` found for enum `stm32g0::stm32g071::Interrupt` in the current scope
--> src/main.rs:53:16
|
53 | interrupt::free(|cs| {
| ^^^^ variant or associated item not found in `stm32g0::stm32g071::Interrupt`
For more information about this error, try `rustc --explain E0599`.
error: could not compile `blink-nucleo-g0` due to 2 previous errors
use cortex_m::interrupt::free;
use cortex_m::interrupt::Mutex;
use stm32g0::stm32g071::{self, Interrupt, NVIC, TIM3, interrupt};
and changed calling interrupt::free to just free
I did this and it eventually compiled...
and now I think my ISR is hanging, I mean interrupt is invoking in loop.
How to properly "clear" interrupt?
It's my ISR of tim3.
#[interrupt]
fn TIM3() {
free(|cs| {
if let Some(ref mut tim3) = G_TIM.borrow(cs).borrow_mut().deref_mut() {
if tim3.sr.read().uif().bit_is_set() == true {
tim3.sr.write(|w| w.uif().clear_bit());
}
}
});
}
Try to toggle a gpio inside the if let but outside the nested if in your ISR and scope it to see how regularly the ISR triggers. Toggle a different pin inside the nested if to see if that ever evaluates to true and executes. (Or if you have a HW debugger with Rust support sorted out you can use breakpoints.)
I think that would kinda defeat the object. I'd go for static mut and unsafe, just for testing purposes. I suspect the mutex is part of the reason you never get there. I think the mutex takes too long to yield or something. But take it with a spoon of salt, I have effectively zero experience with Rust in embedded.
The issue here is that you're having a lot of changes. You're using a different chip, with a different HAL, with a different kind of peripheral. And you don't hit your breakpoint. So something is causing that condition to be false.
Maybe you should take a step back. Answer these questions first:
Do you have an oscilloscope. If not, it won't help to toggle GPIOs to see if something is happening. You won't be able to see it anyway. So scratch that idea if you don't have a scope.
Put a breakpoint in the if let but not in the nested if and see if you ever hit the interrupt. Figure out where things go wrong. What does your code do. What is the values of expressions when.
Copy that code from the link above as closely as possible. I.e. use GPIO instead of timer. Let the only difference be the chip model (g071 vs f405). First make sure Mutex works for g071 as well.
#![no_std]
#![no_main]
use panic_halt as _;
use core::cell::RefCell;
use core::ops::DerefMut;
use cortex_m::interrupt::free;
use cortex_m::interrupt::Mutex;
use cortex_m_rt::entry;
use stm32g0::stm32g071::{self, interrupt, Interrupt, NVIC};
static G_TIM: Mutex<RefCell<Option<stm32g071::TIM2>>> = Mutex::new(RefCell::new(None));
static G_GPIOA: Mutex<RefCell<Option<stm32g071::GPIOA>>> = Mutex::new(RefCell::new(None));
#[entry]
fn main() -> ! {
let p = stm32g071::Peripherals::take().unwrap();
let gpioa = &p.GPIOA;
let rcc_r = &p.RCC;
let tim2_ref = &p.TIM2;
// enable Clock for GPIOA
rcc_r.iopenr.modify(|_, w| w.iopaen().set_bit());
// enable Clock for TIM2
rcc_r.apbenr1.write(|w| w.tim2en().set_bit());
// Nucleo G071RB LED so need to set as output
gpioa.moder.modify(|_, w| unsafe { w.moder5().bits(0b01) });
// prescaler 16000, 16 MHz / 16kH = 1000 Hz
tim2_ref.psc.write(|w| unsafe { w.psc().bits(16000) });
// autoreload to 1000 to get 1 Hz interrrupt
tim2_ref.arr.write(|w| unsafe { w.arr_h().bits(0) });
tim2_ref.arr.write(|w| unsafe { w.arr_l().bits(1000) });
// reinitialize timer
tim2_ref.egr.write(|w| w.ug().set_bit());
// update interrupt enable bit
tim2_ref.dier.write(|w| w.uie().set_bit());
// NVIC unmask interrupt
unsafe {
NVIC::unmask(Interrupt::TIM2);
};
let tim2 = p.TIM2;
free(|cs| {
G_TIM.borrow(cs).replace(Some(tim2));
});
let gpioa = p.GPIOA;
free(|cs| {
G_GPIOA.borrow(cs).replace(Some(gpioa));
});
let mut increment = 0;
loop {
increment += 1;
if increment > 1000 {
increment = 0;
}
}
}
#[interrupt]
fn TIM2() {
free(|cs| {
if let Some(ref mut tim2) = G_TIM.borrow(cs).borrow_mut().deref_mut() {
tim2.sr.write(|w| w.uif().clear_bit());
}
});
free(|cs| {
if let Some(ref mut gpioa) = G_GPIOA.borrow(cs).borrow_mut().deref_mut() {
if gpioa.odr.read().odr5().bit_is_set() {
gpioa.odr.modify(|_, w| w.odr5().clear_bit());
} else {
gpioa.odr.modify(|_, w| w.odr5().set_bit());
}
}
});
}
I wrote something like this to cut out unnecessary code. Both if let Some(...) returns false and I don't know why. I think I should give up and back to Embedded C.
Is this for some product you have to get shipped or is it playing around with Rust in a side project?
If the former, go for embedded C all the time. The tools and learning resources are still much more mature than the Rust side.
If the latter, I'd try to stick with it and figure it out. Something is preventing your mutex to yield its contents. I'd start by cutting out all the interrupt stuff and just try to access it in main loop to see if it works in main loop context. Maybe some blocking delay followed by toggling an LED pin protected by the mutex.
If learning is your goal, then feel free to continue. But if you just want to get something working, I would highly recommend RTIC for this.
It's a lightweight framework to share resources across interrupt tasks. It handles all the Mutex bits for you, and it does static analysis to limit the amount of priority inversion when locking. It ends up being as good or better than you could do manually.
If you do look at RTIC, I would recommend using the v0.6 pre-release version. It's due to be stabilized in the next week or two. I also find it easier to understand and use than v0.5.
Edit: you could also get something working with RTIC, and then inspect the macro-expanded code to see how they implement it. That might also help identity your problem.
I followed instructions in GitHub issue and it worked. I wasn't using Mutex and interrupt::free properly. if let.. in ISR was returning false because interrupt was exectued before replace so... it has value of None in interrupt.
This is my code that works, after fix.
#![no_std]
#![no_main]
use panic_halt as _;
use core::cell::RefCell;
use core::ops::DerefMut;
use cortex_m::interrupt::free;
use cortex_m::interrupt::Mutex;
use cortex_m_rt::entry;
use stm32g0::stm32g071::{self, interrupt, Interrupt, NVIC};
static G_TIM: Mutex<RefCell<Option<stm32g071::TIM2>>> = Mutex::new(RefCell::new(None));
static G_GPIOA: Mutex<RefCell<Option<stm32g071::GPIOA>>> = Mutex::new(RefCell::new(None));
#[entry]
fn main() -> ! {
let p = stm32g071::Peripherals::take().unwrap();
let gpioa = &p.GPIOA;
let rcc_r = &p.RCC;
// enable Clock for GPIOA
rcc_r.iopenr.modify(|_, w| w.iopaen().set_bit());
let tim2 = p.TIM2;
// Nucleo G071RB LED so need to set as output
gpioa.moder.modify(|_, w| unsafe { w.moder5().bits(0b01) });
rcc_r.apbenr1.write(|w| w.tim2en().set_bit());
free(|cs| {
tim2.cr1.write(|w| w.cen().clear_bit());
tim2.psc.write(|w| unsafe { w.psc().bits(16000) });
tim2.arr.write(|w| unsafe { w.arr_l().bits(1000) });
tim2.egr.write(|w| w.ug().set_bit());
tim2.dier.write(|w| w.uie().set_bit());
tim2.cr1.write(|w| w.cen().set_bit());
G_TIM.borrow(cs).replace(Some(tim2));
});
// NVIC unmask interrupt
unsafe {
NVIC::unmask(Interrupt::TIM2);
};
let gpioa = p.GPIOA;
free(|cs| {
G_GPIOA.borrow(cs).replace(Some(gpioa));
});
let mut increment = 0;
loop {
increment += 1;
if increment > 1000 {
increment = 0;
}
}
}
#[interrupt]
fn TIM2() {
free(|cs| {
if let Some(ref mut tim2) = G_TIM.borrow(cs).borrow_mut().deref_mut() {
tim2.sr.write(|w| w.uif().clear_bit());
}
});
free(|cs| {
if let Some(ref mut gpioa) = G_GPIOA.borrow(cs).borrow_mut().deref_mut() {
if gpioa.odr.read().odr5().bit_is_set() {
gpioa.odr.modify(|_, w| w.odr5().clear_bit());
} else {
gpioa.odr.modify(|_, w| w.odr5().set_bit());
}
}
});
}