Skip to content

Commit 297ce70

Browse files
Merge #94
94: Add method to initialise ADC1 and ADC2 together r=richardeoin a=richardeoin See #93 Co-authored-by: Richard Meadows <[email protected]>
2 parents 4624628 + 72a778e commit 297ce70

File tree

5 files changed

+218
-53
lines changed

5 files changed

+218
-53
lines changed

examples/adc.rs

Lines changed: 29 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,8 @@
1-
#![deny(unsafe_code)]
1+
//! Example of reading a voltage with ADC1
2+
//!
3+
//! For an example of using ADC3, see examples/temperature.rs
4+
//! For an example of using ADC1 and ADC2 together, see examples/adc12.rs
5+
26
#![no_main]
37
#![no_std]
48

@@ -28,40 +32,54 @@ fn main() -> ! {
2832
println!(log, "Setup RCC... ");
2933
let rcc = dp.RCC.constrain();
3034

31-
// setting this per_ck to 4 Mhz here (which is gonna choose the CSI that runs at exactly 4 Mhz) as the adc requires per_ck as its
32-
// own kernel clock and wouldn't work at all if per_ck wouldnt be enabled or loose a few bits if it was too fast
33-
// (the maximum for this is 36 Mhz)
35+
// We need to configure a clock for adc_ker_ck_input. The default
36+
// adc_ker_ck_input is pll2_p_ck, but we will use per_ck. Here we
37+
// set per_ck to 4MHz.
38+
//
39+
// The maximum adc_ker_ck_input frequency is 100MHz for revision V and 36MHz
40+
// otherwise
3441
let ccdr = rcc
3542
.sys_ck(100.mhz())
3643
.per_ck(4.mhz())
3744
.freeze(vos, &dp.SYSCFG);
3845

46+
// Switch adc_ker_ck_input multiplexer to per_ck
47+
let d3ccipr = &unsafe { &*pac::RCC::ptr() }.d3ccipr;
48+
d3ccipr.modify(|_, w| unsafe { w.adcsel().bits(0b10) });
49+
3950
println!(log, "");
4051
println!(log, "stm32h7xx-hal example - ADC");
4152
println!(log, "");
4253

4354
let mut delay = Delay::new(cp.SYST, ccdr.clocks);
4455

4556
// Setup ADC
46-
let mut adc3 =
47-
adc::Adc::adc3(dp.ADC3, &mut delay, ccdr.peripheral.ADC3, &ccdr.clocks)
48-
.enable();
49-
adc3.set_resolution(adc::Resolution::SIXTEENBIT);
57+
let mut adc1 = adc::Adc::adc1(
58+
dp.ADC1,
59+
&mut delay,
60+
ccdr.peripheral.ADC12,
61+
&ccdr.clocks,
62+
)
63+
.enable();
64+
adc1.set_resolution(adc::Resolution::SIXTEENBIT);
65+
66+
// We can't use ADC2 here because ccdr.peripheral.ADC12 has been
67+
// consumed. See examples/adc12.rs
5068

5169
// Setup GPIOC
5270
let gpioc = dp.GPIOC.split(ccdr.peripheral.GPIOC);
5371

5472
// Configure pc0 as an analog input
55-
let mut channel = gpioc.pc0.into_analog();
73+
let mut channel = gpioc.pc0.into_analog(); // ANALOG IN 10
5674

5775
loop {
58-
let data: u32 = adc3.read(&mut channel).unwrap();
76+
let data: u32 = adc1.read(&mut channel).unwrap();
5977
// voltage = reading * (vref/resolution)
6078
println!(
6179
log,
6280
"ADC reading: {}, voltage for nucleo: {}",
6381
data,
64-
data as f32 * (3.3 / adc3.max_sample() as f32)
82+
data as f32 * (3.3 / adc1.max_sample() as f32)
6583
);
6684
}
6785
}

examples/adc12.rs

Lines changed: 75 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,75 @@
1+
//! Example of using ADC1 and ADC2 together
2+
//!
3+
//! For an example of using ADC3, see examples/temperature.rs
4+
//! For an example of using ADC1 alone, see examples/adc.rs
5+
6+
#![deny(unsafe_code)]
7+
#![no_main]
8+
#![no_std]
9+
10+
extern crate panic_itm;
11+
12+
use cortex_m;
13+
use cortex_m_rt::entry;
14+
15+
use stm32h7xx_hal::{adc, delay::Delay, pac, prelude::*};
16+
17+
use cortex_m_log::println;
18+
use cortex_m_log::{
19+
destination::Itm, printer::itm::InterruptSync as InterruptSyncItm,
20+
};
21+
22+
#[entry]
23+
fn main() -> ! {
24+
let cp = cortex_m::Peripherals::take().unwrap();
25+
let dp = pac::Peripherals::take().unwrap();
26+
let mut log = InterruptSyncItm::new(Itm::new(cp.ITM));
27+
28+
// Constrain and Freeze power
29+
println!(log, "Setup PWR... ");
30+
let pwr = dp.PWR.constrain();
31+
let vos = pwr.freeze();
32+
33+
// Constrain and Freeze clock
34+
println!(log, "Setup RCC... ");
35+
let rcc = dp.RCC.constrain();
36+
37+
let ccdr = rcc
38+
.sys_ck(100.mhz())
39+
.pll2_p_ck(4.mhz()) // Default adc_ker_ck_input
40+
.freeze(vos, &dp.SYSCFG);
41+
42+
println!(log, "");
43+
println!(log, "stm32h7xx-hal example - ADC1 and ADC2");
44+
println!(log, "");
45+
46+
let mut delay = Delay::new(cp.SYST, ccdr.clocks);
47+
48+
// Setup ADC1 and ADC2
49+
let (adc1, adc2) = adc::adc12(
50+
dp.ADC1,
51+
dp.ADC2,
52+
&mut delay,
53+
ccdr.peripheral.ADC12,
54+
&ccdr.clocks,
55+
);
56+
57+
let mut adc1 = adc1.enable();
58+
adc1.set_resolution(adc::Resolution::SIXTEENBIT);
59+
60+
let mut adc2 = adc2.enable();
61+
adc2.set_resolution(adc::Resolution::SIXTEENBIT);
62+
63+
// Setup GPIOC
64+
// NOTE: PC2 and PC3 are only pinned out on TFBGA packages!!
65+
let gpioc = dp.GPIOC.split(ccdr.peripheral.GPIOC);
66+
let mut channel_pc2 = gpioc.pc2.into_analog(); // AIN 12
67+
let mut channel_pc3 = gpioc.pc3.into_analog(); // AIN 13
68+
69+
loop {
70+
let data_pc2: u32 = adc1.read(&mut channel_pc2).unwrap();
71+
let data_pc3: u32 = adc2.read(&mut channel_pc3).unwrap();
72+
// voltage = reading * (vref/resolution)
73+
println!(log, "ADC readings: {} {}", data_pc2, data_pc3);
74+
}
75+
}

examples/temperature.rs

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,8 @@
1+
//! Example for using ADC3 to read the internal temperature sensor
2+
//!
3+
//! For an example of using ADC1, see examples/adc.rs
4+
//! For an example of using ADC1 and ADC2 together, see examples/adc12.rs
5+
16
#![deny(unsafe_code)]
27
#![no_main]
38
#![no_std]
@@ -36,7 +41,7 @@ fn main() -> ! {
3641

3742
let ccdr = rcc
3843
.sys_ck(100.mhz())
39-
.per_ck(4.mhz())
44+
.pll2_p_ck(4.mhz()) // Default adc_ker_ck_input
4045
.freeze(vos, &dp.SYSCFG);
4146

4247
println!(log, "");

src/adc.rs

Lines changed: 105 additions & 39 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,13 @@
11
//! Analog to Digital Converter (ADC)
2+
//!
3+
//! ADC1 and ADC2 share a reset line. To initialise both of them, use the
4+
//! [`adc12`](adc12) method.
25
36
use crate::hal::adc::{Channel, OneShot};
47
use crate::hal::blocking::delay::DelayUs;
58

69
use core::marker::PhantomData;
710

8-
use crate::stm32;
911
use crate::stm32::{ADC1, ADC2, ADC3, ADC3_COMMON};
1012

1113
use crate::delay::Delay;
@@ -17,7 +19,10 @@ use crate::gpio::gpiof::{
1719
};
1820
use crate::gpio::gpioh::{PH2, PH3, PH4, PH5};
1921
use crate::gpio::Analog;
22+
use crate::rcc::rec::AdcClkSelGetter;
2023
use crate::rcc::{rec, CoreClocks, ResetEnable};
24+
use crate::time::Hertz;
25+
use stm32h7::Variant::Val;
2126

2227
#[cfg(not(feature = "revision_v"))]
2328
const ADC_KER_CK_MAX: u32 = 36_000_000;
@@ -287,13 +292,87 @@ pub trait AdcExt<ADC>: Sized {
287292
#[derive(Copy, Clone, Debug, PartialEq)]
288293
pub struct StoredConfig(AdcSampleTime, Resolution, AdcLshift);
289294

295+
/// Get and check the adc_ker_ck_input
296+
fn check_clock(prec: &impl AdcClkSelGetter, clocks: &CoreClocks) -> Hertz {
297+
// Select Kernel Clock
298+
let adc_clock = match prec.get_kernel_clk_mux() {
299+
Val(rec::AdcClkSel::PLL2_P) => clocks.pll2_p_ck(),
300+
Val(rec::AdcClkSel::PLL3_R) => clocks.pll3_r_ck(),
301+
Val(rec::AdcClkSel::PER) => clocks.per_ck(),
302+
_ => unreachable!(),
303+
}
304+
.expect("adc_ker_ck_input is not running!");
305+
306+
// Check against datasheet requirements
307+
assert!(
308+
adc_clock.0 <= ADC_KER_CK_MAX,
309+
"adc_ker_ck_input is too fast"
310+
);
311+
312+
adc_clock
313+
}
314+
315+
// ADC12 is a unique case where a single reset line is used to control two
316+
// peripherals that have separate peripheral definitons in the SVD.
317+
318+
/// Initialise ADC12 together
319+
///
320+
/// Sets all configurable parameters to one-shot defaults,
321+
/// performs a boot-time calibration.
322+
pub fn adc12(
323+
adc1: ADC1,
324+
adc2: ADC2,
325+
delay: &mut Delay,
326+
prec: rec::Adc12,
327+
clocks: &CoreClocks,
328+
) -> (Adc<ADC1, Disabled>, Adc<ADC2, Disabled>) {
329+
// Consume ADC register block, produce ADC1/2 with default settings
330+
let mut adc1 = Adc::<ADC1, Disabled>::default_from_rb(adc1);
331+
let mut adc2 = Adc::<ADC2, Disabled>::default_from_rb(adc2);
332+
333+
// Check adc_ker_ck_input
334+
check_clock(&prec, clocks);
335+
336+
// Enable AHB clock
337+
let prec = prec.enable();
338+
339+
// Power Down
340+
adc1.power_down();
341+
adc2.power_down();
342+
343+
// Reset peripheral
344+
prec.reset();
345+
346+
// Power Up, Preconfigure and Calibrate
347+
adc1.power_up(delay);
348+
adc2.power_up(delay);
349+
adc1.preconfigure();
350+
adc2.preconfigure();
351+
adc1.calibrate();
352+
adc2.calibrate();
353+
354+
(adc1, adc2)
355+
}
356+
357+
/// Freeing both the periperhal and PREC is possible for ADC3
358+
impl<ED> Adc<ADC3, ED> {
359+
/// Releases the ADC peripheral
360+
pub fn free(self) -> (ADC3, rec::Adc3) {
361+
(
362+
self.rb,
363+
rec::Adc3 {
364+
_marker: PhantomData,
365+
},
366+
)
367+
}
368+
}
369+
290370
#[allow(unused_macros)]
291371
macro_rules! adc_hal {
292372
($(
293373
$ADC:ident: (
294374
$adcX: ident,
295-
$Rec:ident,
296-
$COMMON:ident
375+
$Rec:ident
297376
)
298377
),+ $(,)*) => {
299378
$(
@@ -317,46 +396,39 @@ macro_rules! adc_hal {
317396
pub fn $adcX(adc: $ADC, delay: &mut Delay,
318397
prec: rec::$Rec, clocks: &CoreClocks
319398
) -> Self {
320-
let mut s = Self {
321-
rb: adc,
322-
sample_time: AdcSampleTime::default(),
323-
resolution: Resolution::SIXTEENBIT,
324-
lshift: AdcLshift::default(),
325-
_enabled: PhantomData,
326-
};
399+
// Consume ADC register block, produce Self with default
400+
// settings
401+
let mut adc = Self::default_from_rb(adc);
327402

328-
// Select Kernel Clock
329-
s.enable_clock(
330-
clocks.per_ck().expect("per_ck is not running!").0
331-
);
403+
// Check adc_ker_ck_input
404+
check_clock(&prec, clocks);
332405

333406
// Enable AHB clock
334407
let prec = prec.enable();
335408

336409
// Power Down
337-
s.power_down();
410+
adc.power_down();
338411

339-
// Reset periperal
412+
// Reset peripheral
340413
prec.reset();
341414

342415
// Power Up, Preconfigure and Calibrate
343-
s.power_up(delay);
344-
s.preconfigure();
345-
s.calibrate();
416+
adc.power_up(delay);
417+
adc.preconfigure();
418+
adc.calibrate();
346419

347-
s
420+
adc
348421
}
349-
350-
fn enable_clock(&mut self, per_ck: u32) {
351-
// Set per_ck as adc clock, TODO: we might want to
352-
// change this so we can also use other clocks as
353-
// input for this
354-
assert!(per_ck <= ADC_KER_CK_MAX, "per_ck is not running or too fast");
355-
let d3ccipr = &unsafe { &*stm32::RCC::ptr() }.d3ccipr;
356-
357-
d3ccipr.modify(|_, w| unsafe { w.adcsel().bits(0b10) });
422+
/// Creates ADC with default settings
423+
fn default_from_rb(rb: $ADC) -> Self {
424+
Self {
425+
rb,
426+
sample_time: AdcSampleTime::default(),
427+
resolution: Resolution::SIXTEENBIT,
428+
lshift: AdcLshift::default(),
429+
_enabled: PhantomData,
430+
}
358431
}
359-
360432
/// Disables Deeppowerdown-mode and enables voltage regulator
361433
///
362434
/// Note: After power-up, a [`calibration`](#method.calibrate) shall be run
@@ -571,12 +643,6 @@ macro_rules! adc_hal {
571643
}
572644

573645
impl<ED> Adc<$ADC, ED> {
574-
575-
/// Releases the ADC peripheral
576-
pub fn free(self) -> ($ADC, rec::$Rec) {
577-
(self.rb, rec::$Rec { _marker: PhantomData })
578-
}
579-
580646
/// Save current ADC config
581647
pub fn save_cfg(&mut self) -> StoredConfig {
582648
StoredConfig(self.get_sample_time(), self.get_resolution(), self.get_lshift())
@@ -714,7 +780,7 @@ macro_rules! adc_hal {
714780
}
715781

716782
adc_hal!(
717-
ADC1: (adc1, Adc12, ADC12_COMMON),
718-
ADC2: (adc2, Adc12, ADC12_COMMON),
719-
ADC3: (adc3, Adc3, ADC3_COMMON),
783+
ADC1: (adc1, Adc12), // ADC1
784+
ADC2: (adc2, Adc12), // ADC2
785+
ADC3: (adc3, Adc3), // ADC3
720786
);

src/rcc/rec.rs

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -266,7 +266,7 @@ peripheral_reset_and_enable_control! {
266266
AHB1, "AMBA High-performance Bus (AHB1) peripherals" => [
267267
Eth1Mac, Dma2, Dma1,
268268
#[cfg(any(feature = "dualcore"))] Art,
269-
Adc12
269+
Adc12 [group clk: Adc(Variant) d3ccip "ADC"]
270270
];
271271

272272
AHB2, "AMBA High-performance Bus (AHB2) peripherals" => [
@@ -283,7 +283,8 @@ peripheral_reset_and_enable_control! {
283283
];
284284

285285
AHB4, "AMBA High-performance Bus (AHB4) peripherals" => [
286-
Hsem, Bdma, Crc, Adc3,
286+
Hsem, Bdma, Crc,
287+
Adc3 [group clk: Adc],
287288
Gpioa, Gpiob, Gpioc, Gpiod, Gpioe, Gpiof, Gpiog, Gpioh, Gpioi, Gpioj, Gpiok
288289
];
289290

0 commit comments

Comments
 (0)