Skip to content

Commit 1dabe77

Browse files
Merge #220
220: Updating ADC to support external reference specifications r=therealprof a=ryan-summers This PR fixes #205 by updating the adc module to not allow access to VREF, VBAT, and the internal temperature sensor from ADC2 and ADC3, since these are only available to ADC1. To accomodate the loss of external reference calibration, the adc configuration has been updated to allow the user to specify the VDDA voltage. Co-authored-by: Ryan Summers <ryan.summers@vertigo-designs.com>
2 parents bb214b6 + cbab009 commit 1dabe77

File tree

2 files changed

+94
-84
lines changed

2 files changed

+94
-84
lines changed

CHANGELOG.md

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,8 @@ and this project adheres to [Semantic Versioning](http://semver.org/).
2121
- Change DMA traits to `embedded-dma`.
2222
- Use bitbanding during clock enabling and peripheral reset to avoid data races.
2323
- Add missing `Write` implementation for `Serial` and implemented better error handling.
24+
- [breaking-change] ADC2 and ADC3 no longer allow access to VREF, VBAT, or the internal
25+
temperature measurement (ADC2 and ADC3 do not have an internal connection for these channels)
2426

2527
### Added
2628

@@ -29,6 +31,7 @@ and this project adheres to [Semantic Versioning](http://semver.org/).
2931
- Added `check_interrupt` method for GPIO pins
3032
- Basic support for DAC
3133
- Add initial DMA support
34+
- Allow specification of ADC reference voltage in ADC configuraton structure
3235

3336
### Fixed
3437
- Stability fixes related to SD card write

src/adc.rs

Lines changed: 91 additions & 84 deletions
Original file line numberDiff line numberDiff line change
@@ -381,6 +381,7 @@ pub mod config {
381381
pub(crate) dma: Dma,
382382
pub(crate) end_of_conversion_interrupt: Eoc,
383383
pub(crate) default_sample_time: SampleTime,
384+
pub(crate) vdda: Option<u32>,
384385
}
385386

386387
impl AdcConfig {
@@ -433,6 +434,15 @@ pub mod config {
433434
self.default_sample_time = default_sample_time;
434435
self
435436
}
437+
438+
/// Specify the reference voltage for the ADC.
439+
///
440+
/// # Args
441+
/// * `vdda_mv` - The ADC reference voltage in millivolts.
442+
pub fn reference_voltage(mut self, vdda_mv: u32) -> Self {
443+
self.vdda = Some(vdda_mv);
444+
self
445+
}
436446
}
437447

438448
impl Default for AdcConfig {
@@ -447,6 +457,7 @@ pub mod config {
447457
dma: Dma::Disabled,
448458
end_of_conversion_interrupt: Eoc::Disabled,
449459
default_sample_time: SampleTime::Cycles_480,
460+
vdda: None,
450461
}
451462
}
452463
}
@@ -606,9 +617,82 @@ impl<ADC> fmt::Debug for Adc<ADC> {
606617
}
607618

608619
macro_rules! adc {
620+
// Note that only ADC1 supports measurement of VREF, VBAT, and the internal temperature sensor.
621+
(additionals: ADC1 => ($common_type:ident)) => {
622+
/// Calculates the system VDDA by sampling the internal VREF channel and comparing
623+
/// the result with the value stored at the factory.
624+
pub fn calibrate(&mut self) {
625+
self.enable();
626+
627+
let vref_en = self.temperature_and_vref_enabled();
628+
if !vref_en {
629+
self.enable_temperature_and_vref();
630+
}
631+
632+
let vref_cal = VrefCal::get().read();
633+
let vref_samp = self.read(&mut Vref).unwrap(); //This can't actually fail, it's just in a result to satisfy hal trait
634+
635+
self.calibrated_vdda = (VDDA_CALIB * u32::from(vref_cal)) / u32::from(vref_samp);
636+
if !vref_en {
637+
self.disable_temperature_and_vref();
638+
}
639+
}
640+
641+
/// Enables the vbat internal channel
642+
pub fn enable_vbat(&self) {
643+
unsafe {
644+
let common = &(*pac::$common_type::ptr());
645+
common.ccr.modify(|_, w| w.vbate().set_bit());
646+
}
647+
}
648+
649+
/// Enables the vbat internal channel
650+
pub fn disable_vbat(&self) {
651+
unsafe {
652+
let common = &(*pac::$common_type::ptr());
653+
common.ccr.modify(|_, w| w.vbate().clear_bit());
654+
}
655+
}
656+
657+
/// Enables the temp and vref internal channels.
658+
/// They can't work while vbat is also enabled so this method also disables vbat.
659+
pub fn enable_temperature_and_vref(&mut self) {
660+
//VBAT prevents TS and VREF from being sampled
661+
self.disable_vbat();
662+
unsafe {
663+
let common = &(*pac::$common_type::ptr());
664+
common.ccr.modify(|_, w| w.tsvrefe().set_bit());
665+
}
666+
}
667+
668+
/// Disables the temp and vref internal channels
669+
pub fn disable_temperature_and_vref(&mut self) {
670+
unsafe {
671+
let common = &(*pac::$common_type::ptr());
672+
common.ccr.modify(|_, w| w.tsvrefe().clear_bit());
673+
}
674+
}
675+
676+
/// Returns if the temp and vref internal channels are enabled
677+
pub fn temperature_and_vref_enabled(&mut self) -> bool {
678+
unsafe {
679+
let common = &(*pac::$common_type::ptr());
680+
common.ccr.read().tsvrefe().bit_is_set()
681+
}
682+
}
683+
};
684+
685+
// Provide a stub implementation for ADCs that do not have a means of sampling VREF.
686+
(additionals: $adc_type:ident => ($common_type:ident)) => {
687+
fn calibrate(&mut self) {}
688+
};
689+
609690
($($adc_type:ident => ($constructor_fn_name:ident, $common_type:ident, $en_bit: expr)),+ $(,)*) => {
610691
$(
611692
impl Adc<pac::$adc_type> {
693+
694+
adc!(additionals: $adc_type => ($common_type));
695+
612696
/// Enables the ADC clock, resets the peripheral (optionally), runs calibration and applies the supplied config
613697
/// # Arguments
614698
/// * `reset` - should a reset be performed. This is provided because on some devices multiple ADCs share the same common reset
@@ -642,6 +726,11 @@ macro_rules! adc {
642726
s.enable();
643727
s.calibrate();
644728

729+
// If the user specified a VDDA, use that over the internally determined value.
730+
if let Some(vdda) = s.config.vdda {
731+
s.calibrated_vdda = vdda;
732+
}
733+
645734
s
646735
}
647736

@@ -656,67 +745,9 @@ macro_rules! adc {
656745
self.set_dma(config.dma);
657746
self.set_end_of_conversion_interrupt(config.end_of_conversion_interrupt);
658747
self.set_default_sample_time(config.default_sample_time);
659-
}
660-
661-
/// Calculates the system VDDA by sampling the internal VREF channel and comparing
662-
/// the result with the value stored at the factory.
663-
pub fn calibrate(&mut self) {
664-
self.enable();
665-
666-
let vref_en = self.temperature_and_vref_enabled();
667-
if !vref_en {
668-
self.enable_temperature_and_vref();
669-
}
670-
671-
let vref_cal = VrefCal::get().read();
672-
let vref_samp = self.read(&mut Vref).unwrap(); //This can't actually fail, it's just in a result to satisfy hal trait
673-
674-
self.calibrated_vdda = (VDDA_CALIB * u32::from(vref_cal)) / u32::from(vref_samp);
675-
if !vref_en {
676-
self.disable_temperature_and_vref();
677-
}
678-
}
679-
680-
/// Enables the vbat internal channel
681-
pub fn enable_vbat(&self) {
682-
unsafe {
683-
let common = &(*pac::$common_type::ptr());
684-
common.ccr.modify(|_, w| w.vbate().set_bit());
685-
}
686-
}
687-
688-
/// Enables the vbat internal channel
689-
pub fn disable_vbat(&self) {
690-
unsafe {
691-
let common = &(*pac::$common_type::ptr());
692-
common.ccr.modify(|_, w| w.vbate().clear_bit());
693-
}
694-
}
695-
696-
/// Enables the temp and vref internal channels.
697-
/// They can't work while vbat is also enabled so this method also disables vbat.
698-
pub fn enable_temperature_and_vref(&mut self) {
699-
//VBAT prevents TS and VREF from being sampled
700-
self.disable_vbat();
701-
unsafe {
702-
let common = &(*pac::$common_type::ptr());
703-
common.ccr.modify(|_, w| w.tsvrefe().set_bit());
704-
}
705-
}
706748

707-
/// Disables the temp and vref internal channels
708-
pub fn disable_temperature_and_vref(&mut self) {
709-
unsafe {
710-
let common = &(*pac::$common_type::ptr());
711-
common.ccr.modify(|_, w| w.tsvrefe().clear_bit());
712-
}
713-
}
714-
715-
/// Returns if the temp and vref internal channels are enabled
716-
pub fn temperature_and_vref_enabled(&mut self) -> bool {
717-
unsafe {
718-
let common = &(*pac::$common_type::ptr());
719-
common.ccr.read().tsvrefe().bit_is_set()
749+
if let Some(vdda) = config.vdda {
750+
self.calibrated_vdda = vdda;
720751
}
721752
}
722753

@@ -1365,14 +1396,8 @@ adc_pins!(
13651396
gpioc::PC5<Analog> => (ADC1, 15),
13661397
gpioc::PC5<Analog> => (ADC2, 15),
13671398
Temperature => (ADC1, 18),
1368-
Temperature => (ADC2, 18),
1369-
Temperature => (ADC3, 18),
13701399
Vbat => (ADC1, 18),
1371-
Vbat => (ADC2, 18),
1372-
Vbat => (ADC3, 18),
13731400
Vref => (ADC1, 17),
1374-
Vref => (ADC2, 17),
1375-
Vref => (ADC3, 17),
13761401
);
13771402

13781403
// Not available on V variant
@@ -1436,14 +1461,8 @@ adc_pins!(
14361461
gpioc::PC5<Analog> => (ADC1, 15),
14371462
gpioc::PC5<Analog> => (ADC2, 15),
14381463
Temperature => (ADC1, 18),
1439-
Temperature => (ADC2, 18),
1440-
Temperature => (ADC3, 18),
14411464
Vbat => (ADC1, 18),
1442-
Vbat => (ADC2, 18),
1443-
Vbat => (ADC3, 18),
14441465
Vref => (ADC1, 17),
1445-
Vref => (ADC2, 17),
1446-
Vref => (ADC3, 17),
14471466
);
14481467

14491468
// Not available on V variant
@@ -1502,14 +1521,8 @@ adc_pins!(
15021521
gpioc::PC4<Analog> => (ADC1, 14),
15031522
gpioc::PC4<Analog> => (ADC2, 14),
15041523
Temperature => (ADC1, 18),
1505-
Temperature => (ADC2, 18),
1506-
Temperature => (ADC3, 18),
15071524
Vbat => (ADC1, 18),
1508-
Vbat => (ADC2, 18),
1509-
Vbat => (ADC3, 18),
15101525
Vref => (ADC1, 17),
1511-
Vref => (ADC2, 17),
1512-
Vref => (ADC3, 17),
15131526
);
15141527

15151528
// Not available on M variant
@@ -1569,14 +1582,8 @@ adc_pins!(
15691582
gpioc::PC1<Analog> => (ADC2, 11),
15701583
gpioc::PC1<Analog> => (ADC3, 11),
15711584
Temperature => (ADC1, 18),
1572-
Temperature => (ADC2, 18),
1573-
Temperature => (ADC3, 18),
15741585
Vbat => (ADC1, 18),
1575-
Vbat => (ADC2, 18),
1576-
Vbat => (ADC3, 18),
15771586
Vref => (ADC1, 17),
1578-
Vref => (ADC2, 17),
1579-
Vref => (ADC3, 17),
15801587
);
15811588

15821589
// Not available on A variant

0 commit comments

Comments
 (0)