Skip to content

Commit b944b87

Browse files
committed
Use timer in adc example
1 parent b1a554e commit b944b87

File tree

2 files changed

+76
-7
lines changed

2 files changed

+76
-7
lines changed

examples/adc.rs

Lines changed: 70 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -6,13 +6,35 @@
66
use defmt_rtt as _;
77
use panic_probe as _;
88

9-
use defmt;
9+
use core::cell::RefCell;
1010

1111
use cortex_m::asm;
12+
use cortex_m::interrupt::Mutex;
1213
use cortex_m_rt::entry;
1314

1415
use embedded_hal::adc::OneShot;
15-
use stm32f3xx_hal::{adc, pac, prelude::*};
16+
use stm32f3xx_hal::{
17+
adc,
18+
pac::{self, interrupt},
19+
prelude::*,
20+
timer,
21+
};
22+
23+
/// That's a mouthful, so explain it:
24+
///
25+
/// 1. Wrap the Timer in a Mutex, so it can be safely shared between
26+
/// main loop and interrupt or rather used in functions, which could theoretically
27+
/// be preempted.
28+
/// 2. Wrap the Timer in a RefCell to be able obtain a mutable reference to the Timer itself.
29+
/// E.g. the interrupt can't take ownership of the timer, it is shared between main-loop and
30+
/// interrupt context.
31+
/// 3. Wrap the Timer in an Option, so that it can be "lazily initialized". Statics have to be
32+
/// initialized with const values, which the timer itself is obviously not, but None is.
33+
///
34+
/// This could all be done just with a static mut && unsafe as
35+
/// the usecase it pretty clear, but this is to show the definitely safe
36+
/// alternative.
37+
static TIMER: Mutex<RefCell<Option<timer::Timer<pac::TIM2>>>> = Mutex::new(RefCell::new(None));
1638

1739
#[entry]
1840
fn main() -> ! {
@@ -21,12 +43,26 @@ fn main() -> ! {
2143
let mut rcc = dp.RCC.constrain();
2244
let clocks = rcc.cfgr.freeze(&mut dp.FLASH.constrain().acr);
2345

46+
// This is a workaround, so that the debugger will not disconnect imidiatly on asm::wfi();
47+
// https://github.com/probe-rs/probe-rs/issues/350#issuecomment-740550519
48+
dp.DBGMCU.cr.modify(|_, w| {
49+
w.dbg_sleep().set_bit();
50+
w.dbg_standby().set_bit();
51+
w.dbg_stop().set_bit()
52+
});
53+
54+
// Create a Common ADC instance, which is shared between ADC1 and ADC2 in this case,
55+
// and for example is in control of the clock of both of these peripherals.
2456
let mut adc_common = adc::CommonAdc::new(dp.ADC1_2, &clocks, &mut rcc.ahb);
2557

58+
// We have to pack these peripherals in a tuple, so that `TemperatureSensor` can
59+
// get a mutable reference to both of these as a singular argument.
60+
//
61+
// This is needed, as both ADC1 and ADC2 have to be of to enable the `TemperatureSensor`.
2662
let mut tuple = (dp.ADC1, dp.ADC2);
2763
let mut ts = adc::TemperatureSensor::new(&mut adc_common, &mut tuple);
2864

29-
// set up adc1
65+
// Set up ADC1
3066
let mut adc = adc::Adc::new(
3167
tuple.0, // The ADC we are going to control
3268
adc::config::Config::default(),
@@ -54,6 +90,19 @@ fn main() -> ! {
5490
let mut gpioa = dp.GPIOA.split(&mut rcc.ahb);
5591
let mut analog_pin = gpioa.pa0.into_analog(&mut gpioa.moder, &mut gpioa.pupdr);
5692

93+
let mut timer = timer::Timer::new(dp.TIM2, clocks, &mut rcc.apb1);
94+
95+
unsafe {
96+
cortex_m::peripheral::NVIC::unmask(timer.interrupt());
97+
}
98+
timer.enable_interrupt(timer::Event::Update);
99+
// Start a timer which fires regularly to wake up from `asm::wfi`
100+
timer.start(500.milliseconds());
101+
// Put the timer in the global context.
102+
cortex_m::interrupt::free(|cs| {
103+
TIMER.borrow(cs).replace(Some(timer));
104+
});
105+
57106
// Be aware that the values in the table below depend on the input of VREF.
58107
// To have a stable VREF input, put a condensator and a volt limiting diode in front of it.
59108
//
@@ -77,8 +126,24 @@ fn main() -> ! {
77126
defmt::trace!("PA0 reads {}", adc_data);
78127
let adc_data: u16 = adc.read(&mut ts).unwrap();
79128
defmt::trace!("TemperatureSensor reads {}", adc_data);
80-
asm::delay(2_000_000);
129+
asm::wfi();
81130
}
82131
}
83132

84-
// TODO: Add adc example, which uses Continuous or Discontinous and interrupts.
133+
#[interrupt]
134+
fn TIM2() {
135+
// Just handle the pending interrupt event.
136+
cortex_m::interrupt::free(|cs| {
137+
TIMER
138+
// Unlock resource for use in critical section
139+
.borrow(cs)
140+
// Get a mutable reference from the RefCell
141+
.borrow_mut()
142+
// Make the inner Option<T> -> Option<&mut T>
143+
.as_mut()
144+
// Unwrap the option, we know, that it has Some()!
145+
.unwrap()
146+
// Finally operate on the timer itself.
147+
.clear_event(timer::Event::Update);
148+
})
149+
}

src/timer.rs

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3,8 +3,12 @@
33
//! Abstractions of the internal timer peripherals
44
//! The timer modules implements the [`CountDown`] and [`Periodic`] traits.
55
//!
6-
//! [Read]: embedded_hal::timer::CountDown
7-
//! [Write]: embedded_hal::timer::Periodic
6+
//! ## Examples
7+
//!
8+
//! Check out [examples/adc.rs], where a [`Periodic`] timer is used to wake
9+
//! up the main-loop regularly.
10+
//!
11+
//! [examples/adc.rs]: https://github.com/stm32-rs/stm32f3xx-hal/blob/v0.8.1/examples/adc.rs
812
913
use core::convert::{From, TryFrom};
1014

0 commit comments

Comments
 (0)