Skip to content

Commit 72e4079

Browse files
committed
Allow parallel conversion on multiple ADCs
This patch splits the current `convert()` method of ADC in two public methods: `start_conversion()` and `read_sample()`. With this, it is now possible to start conversion on multiple ADCs at the same time and then collect results. Signed-off-by: Petr Horacek <hrck@protonmail.com>
1 parent 04df62f commit 72e4079

File tree

6 files changed

+137
-6
lines changed

6 files changed

+137
-6
lines changed

CHANGELOG.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@
99
* MSRV increased to 1.51.0
1010
* **Breaking**: Simplified API for reading device signature
1111
values. `VAL::get().read()` becomes `VAL::read()`
12+
* adc: Allow parallel execution of multiple ADCs through `start_conversion()`
1213

1314
## [v0.10.0] 2021-07-xx
1415

examples/adc.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22
//!
33
//! For an example of using ADC3, see examples/temperature.rs
44
//! For an example of using ADC1 and ADC2 together, see examples/adc12.rs
5+
//! For an example of using ADC1 and ADC2 in parallel, see examples/adc12_parallel.rs
56
67
#![no_main]
78
#![no_std]

examples/adc12.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22
//!
33
//! For an example of using ADC3, see examples/temperature.rs
44
//! For an example of using ADC1 alone, see examples/adc.rs
5+
//! For an example of using ADC1 and ADC2 in parallel, see examples/adc12_parallel.rs
56
67
#![no_main]
78
#![no_std]

examples/adc12_parallel.rs

Lines changed: 98 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,98 @@
1+
//! Example of reading a voltage with ADC1 and ADC2 on two different channels in
2+
//! parallel
3+
//!
4+
//! For an example of using ADC1, see examples/adc.rs
5+
//! For an example of using ADC3, see examples/temperature.rs
6+
//! For an example of using ADC1 and ADC2 together, see examples/adc12.rs
7+
8+
#![no_main]
9+
#![no_std]
10+
11+
use log::info;
12+
13+
use nb::block;
14+
15+
use cortex_m_rt::entry;
16+
17+
use stm32h7xx_hal::{adc, delay::Delay, pac, prelude::*, rcc::rec::AdcClkSel};
18+
19+
#[macro_use]
20+
mod utilities;
21+
22+
#[entry]
23+
fn main() -> ! {
24+
utilities::logger::init();
25+
let cp = cortex_m::Peripherals::take().unwrap();
26+
let dp = pac::Peripherals::take().unwrap();
27+
28+
// Constrain and Freeze power
29+
info!("Setup PWR...");
30+
let pwr = dp.PWR.constrain();
31+
let pwrcfg = example_power!(pwr).freeze();
32+
33+
// Constrain and Freeze clock
34+
info!("Setup RCC...");
35+
let rcc = dp.RCC.constrain();
36+
37+
// We need to configure a clock for adc_ker_ck_input. The default
38+
// adc_ker_ck_input is pll2_p_ck, but we will use per_ck. Here we
39+
// set per_ck to 4MHz.
40+
//
41+
// The maximum adc_ker_ck_input frequency is 100MHz for revision V and 36MHz
42+
// otherwise
43+
let mut ccdr = rcc
44+
.sys_ck(100.mhz())
45+
.per_ck(4.mhz())
46+
.freeze(pwrcfg, &dp.SYSCFG);
47+
48+
// Switch adc_ker_ck_input multiplexer to per_ck
49+
ccdr.peripheral.kernel_adc_clk_mux(AdcClkSel::PER);
50+
51+
info!("");
52+
info!("stm32h7xx-hal example - ADC");
53+
info!("");
54+
55+
let mut delay = Delay::new(cp.SYST, ccdr.clocks);
56+
57+
// Setup ADC1 and ADC2
58+
let (adc1, adc2) = adc::adc12(
59+
dp.ADC1,
60+
dp.ADC2,
61+
&mut delay,
62+
ccdr.peripheral.ADC12,
63+
&ccdr.clocks,
64+
);
65+
let mut adc1 = adc1.enable();
66+
adc1.set_resolution(adc::Resolution::SIXTEENBIT);
67+
adc1.set_sample_time(adc::AdcSampleTime::T_387);
68+
let mut adc2 = adc2.enable();
69+
adc2.set_resolution(adc::Resolution::SIXTEENBIT);
70+
adc2.set_sample_time(adc::AdcSampleTime::T_387);
71+
72+
// Setup GPIOA
73+
let gpioa = dp.GPIOA.split(ccdr.peripheral.GPIOA);
74+
75+
// Configure pins 23 and 22 as an analog inputs
76+
let mut channel1 = gpioa.pa4.into_analog(); // DAISY PIN 23
77+
let mut channel2 = gpioa.pa5.into_analog(); // DAISY PIN 22
78+
79+
loop {
80+
adc1.start_conversion(&mut channel1);
81+
adc2.start_conversion(&mut channel2);
82+
83+
let data1 = block!(adc1.read_sample()).unwrap();
84+
let data2 = block!(adc2.read_sample()).unwrap();
85+
86+
// voltage = reading * (vref/resolution)
87+
info!(
88+
"ADC1 reading: {}, voltage for Daisy pin X: {}",
89+
data1,
90+
data1 as f32 * (3.3 / adc1.max_sample() as f32)
91+
);
92+
info!(
93+
"ADC2 reading: {}, voltage for Daisy pin X: {}",
94+
data2,
95+
data2 as f32 * (3.3 / adc2.max_sample() as f32)
96+
);
97+
}
98+
}

examples/temperature.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22
//!
33
//! For an example of using ADC1, see examples/adc.rs
44
//! For an example of using ADC1 and ADC2 together, see examples/adc12.rs
5+
//! For an example of using ADC1 and ADC2 in parallel, see examples/adc12_parallel.rs
56
67
#![no_main]
78
#![no_std]

src/adc.rs

Lines changed: 35 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -6,8 +6,11 @@
66
use crate::hal::adc::{Channel, OneShot};
77
use crate::hal::blocking::delay::DelayUs;
88

9+
use core::convert::Infallible;
910
use core::marker::PhantomData;
1011

12+
use nb::block;
13+
1114
#[cfg(feature = "rm0455")]
1215
use crate::stm32::ADC12_COMMON;
1316
use crate::stm32::{ADC1, ADC2};
@@ -69,6 +72,7 @@ pub struct Adc<ADC, ED> {
6972
sample_time: AdcSampleTime,
7073
resolution: Resolution,
7174
lshift: AdcLshift,
75+
current_channel: Option<u8>,
7276
_enabled: PhantomData<ED>,
7377
}
7478

@@ -467,6 +471,7 @@ macro_rules! adc_hal {
467471
sample_time: AdcSampleTime::default(),
468472
resolution: Resolution::SIXTEENBIT,
469473
lshift: AdcLshift::default(),
474+
current_channel: None,
470475
_enabled: PhantomData,
471476
}
472477
}
@@ -568,6 +573,7 @@ macro_rules! adc_hal {
568573
sample_time: self.sample_time,
569574
resolution: self.resolution,
570575
lshift: self.lshift,
576+
current_channel: None,
571577
_enabled: PhantomData,
572578
}
573579
}
@@ -610,9 +616,17 @@ macro_rules! adc_hal {
610616
}
611617
}
612618

619+
/// Start conversion
620+
///
621+
/// This method will start reading sequence on the given pin.
622+
/// The value can be then read through the `read_sample` method.
613623
// Refer to RM0433 Rev 6 - Chapter 24.4.16
614-
fn convert(&mut self, chan: u8) -> u32 {
624+
pub fn start_conversion<PIN>(&mut self, _pin: &mut PIN)
625+
where PIN: Channel<$ADC, ID = u8>,
626+
{
627+
let chan = PIN::channel();
615628
assert!(chan <= 19);
629+
616630
self.check_conversion_conditions();
617631

618632
// Set resolution
@@ -628,19 +642,32 @@ macro_rules! adc_hal {
628642
w.sq1().bits(chan)
629643
.l().bits(0)
630644
});
645+
self.current_channel = Some(chan);
631646

632647
// Perform conversion
633648
self.rb.cr.modify(|_, w| w.adstart().set_bit());
649+
}
634650

635-
// Wait until conversion finished
636-
while self.rb.isr.read().eoc().bit_is_clear() {}
651+
/// Read sample
652+
///
653+
/// `nb::Error::WouldBlock` in case the conversion is still
654+
/// progressing.
655+
// Refer to RM0433 Rev 6 - Chapter 24.4.16
656+
pub fn read_sample(&mut self) -> nb::Result<u32, Infallible> {
657+
let chan = self.current_channel.expect("No channel was selected, use start_conversion first");
658+
659+
// Check if the conversion is finished
660+
if self.rb.isr.read().eoc().bit_is_clear() {
661+
return Err(nb::Error::WouldBlock);
662+
}
637663

638664
// Disable preselection of this channel, refer to RM0433 Rev 6 - Chapter 24.4.12
639665
self.rb.pcsel.modify(|r, w| unsafe { w.pcsel().bits(r.pcsel().bits() & !(1 << chan)) });
666+
self.current_channel = None;
640667

641668
// Retrieve result
642669
let result = self.rb.dr.read().bits();
643-
result
670+
nb::Result::Ok(result)
644671
}
645672

646673
fn check_conversion_conditions(&self) {
@@ -678,6 +705,7 @@ macro_rules! adc_hal {
678705
sample_time: self.sample_time,
679706
resolution: self.resolution,
680707
lshift: self.lshift,
708+
current_channel: None,
681709
_enabled: PhantomData,
682710
}
683711
}
@@ -811,8 +839,9 @@ macro_rules! adc_hal {
811839
{
812840
type Error = ();
813841

814-
fn read(&mut self, _pin: &mut PIN) -> nb::Result<WORD, Self::Error> {
815-
let res = self.convert(PIN::channel());
842+
fn read(&mut self, pin: &mut PIN) -> nb::Result<WORD, Self::Error> {
843+
self.start_conversion(pin);
844+
let res = block!(self.read_sample()).unwrap();
816845
Ok(res.into())
817846
}
818847
}

0 commit comments

Comments
 (0)