34
34
//! ```
35
35
36
36
#[ cfg( feature = "device-selected" ) ]
37
- use embedded_hal :: adc :: { Channel , OneShot } ;
37
+ const VREFCAL : * const u16 = 0x1FFF_F7BA as * const u16 ;
38
38
39
39
#[ cfg( feature = "device-selected" ) ]
40
- use crate :: { stm32, gpio:: * } ;
40
+ const VTEMPCAL30 : * const u16 = 0x1FFF_F7B8 as * const u16 ;
41
+
42
+ #[ cfg( feature = "device-selected" ) ]
43
+ const VTEMPCAL110 : * const u16 = 0x1FFF_F7C2 as * const u16 ;
44
+
45
+ #[ cfg( feature = "device-selected" ) ]
46
+ const VDD_CALIB : u16 = 3300 ;
47
+
48
+ #[ cfg( feature = "device-selected" ) ]
49
+ use core:: ptr;
50
+
51
+ #[ cfg( feature = "device-selected" ) ]
52
+ use embedded_hal:: {
53
+ adc:: { Channel , OneShot } ,
54
+ blocking:: delay:: DelayUs ,
55
+ } ;
56
+
57
+ #[ cfg( feature = "device-selected" ) ]
58
+ use crate :: { delay:: Delay , gpio:: * , stm32} ;
41
59
42
60
#[ cfg( feature = "device-selected" ) ]
43
61
/// Analog to Digital converter interface
@@ -48,7 +66,7 @@ pub struct Adc {
48
66
precision : AdcPrecision ,
49
67
}
50
68
51
- #[ derive( Debug , PartialEq ) ]
69
+ #[ derive( Clone , Copy , Debug , PartialEq ) ]
52
70
/// ADC Sampling time
53
71
///
54
72
/// Options for the sampling time, each is T + 0.5 ADC clock cycles.
@@ -96,7 +114,7 @@ impl AdcSampleTime {
96
114
}
97
115
}
98
116
99
- #[ derive( Debug , PartialEq ) ]
117
+ #[ derive( Clone , Copy , Debug , PartialEq ) ]
100
118
/// ADC Result Alignment
101
119
pub enum AdcAlign {
102
120
/// Left aligned results (most significant bits)
@@ -137,7 +155,7 @@ impl AdcAlign {
137
155
}
138
156
}
139
157
140
- #[ derive( Debug , PartialEq ) ]
158
+ #[ derive( Clone , Copy , Debug , PartialEq ) ]
141
159
/// ADC Sampling Precision
142
160
pub enum AdcPrecision {
143
161
/// 12 bit precision
@@ -241,6 +259,59 @@ impl VTemp {
241
259
pub fn disable ( & mut self , adc : & mut Adc ) {
242
260
adc. rb . ccr . modify ( |_, w| w. tsen ( ) . clear_bit ( ) ) ;
243
261
}
262
+
263
+ /// Checks if the temperature sensor is enabled, does not account for the
264
+ /// t<sub>START</sub> time however.
265
+ pub fn is_enabled ( & self , adc : & Adc ) -> bool {
266
+ adc. rb . ccr . read ( ) . tsen ( ) . bit_is_set ( )
267
+ }
268
+
269
+ fn convert_temp ( vtemp : u16 , vdda : u16 ) -> i16 {
270
+ let vtemp30_cal = i32:: from ( unsafe { ptr:: read ( VTEMPCAL30 ) } ) * 100 ;
271
+ let vtemp110_cal = i32:: from ( unsafe { ptr:: read ( VTEMPCAL110 ) } ) * 100 ;
272
+
273
+ let mut temperature: i32 = ( vtemp as i32 ) * 100 ;
274
+ temperature = ( temperature * ( vdda as i32 ) / ( VDD_CALIB as i32 ) ) - vtemp30_cal;
275
+ temperature *= ( 110 - 30 ) * 100 ;
276
+ temperature /= vtemp110_cal - vtemp30_cal;
277
+ temperature += 3000 ;
278
+ temperature as i16
279
+ }
280
+
281
+ /// Read the value of the internal temperature sensor and return the
282
+ /// result in 100ths of a degree centigrade.
283
+ ///
284
+ /// Given a delay reference it will attempt to restrict to the
285
+ /// minimum delay needed to ensure a 10 us t<sub>START</sub> value.
286
+ /// Otherwise it will approximate the required delay using ADC reads.
287
+ pub fn read ( adc : & mut Adc , delay : Option < & mut Delay > ) -> i16 {
288
+ let mut vtemp = Self :: new ( ) ;
289
+ let vtemp_preenable = vtemp. is_enabled ( & adc) ;
290
+
291
+ if !vtemp_preenable {
292
+ vtemp. enable ( adc) ;
293
+
294
+ if let Some ( dref) = delay {
295
+ dref. delay_us ( 2_u16 ) ;
296
+ } else {
297
+ // Double read of vdda to allow sufficient startup time for the temp sensor
298
+ VRef :: read_vdda ( adc) ;
299
+ }
300
+ }
301
+ let vdda = VRef :: read_vdda ( adc) ;
302
+
303
+ let prev_cfg = adc. default_cfg ( ) ;
304
+
305
+ let vtemp_val = adc. read ( & mut vtemp) . unwrap ( ) ;
306
+
307
+ if !vtemp_preenable {
308
+ vtemp. disable ( adc) ;
309
+ }
310
+
311
+ adc. restore_cfg ( prev_cfg) ;
312
+
313
+ Self :: convert_temp ( vtemp_val, vdda)
314
+ }
244
315
}
245
316
246
317
#[ cfg( feature = "device-selected" ) ]
@@ -259,6 +330,34 @@ impl VRef {
259
330
pub fn disable ( & mut self , adc : & mut Adc ) {
260
331
adc. rb . ccr . modify ( |_, w| w. vrefen ( ) . clear_bit ( ) ) ;
261
332
}
333
+
334
+ /// Returns if the internal voltage reference is enabled.
335
+ pub fn is_enabled ( & self , adc : & Adc ) -> bool {
336
+ adc. rb . ccr . read ( ) . vrefen ( ) . bit_is_set ( )
337
+ }
338
+
339
+ /// Reads the value of VDDA in milli-volts
340
+ pub fn read_vdda ( adc : & mut Adc ) -> u16 {
341
+ let vrefint_cal = u32:: from ( unsafe { ptr:: read ( VREFCAL ) } ) ;
342
+ let mut vref = Self :: new ( ) ;
343
+
344
+ let prev_cfg = adc. default_cfg ( ) ;
345
+
346
+ let vref_val: u32 = if vref. is_enabled ( & adc) {
347
+ adc. read ( & mut vref) . unwrap ( )
348
+ } else {
349
+ vref. enable ( adc) ;
350
+
351
+ let ret = adc. read ( & mut vref) . unwrap ( ) ;
352
+
353
+ vref. disable ( adc) ;
354
+ ret
355
+ } ;
356
+
357
+ adc. restore_cfg ( prev_cfg) ;
358
+
359
+ ( ( VDD_CALIB as u32 ) * vrefint_cal / vref_val) as u16
360
+ }
262
361
}
263
362
264
363
#[ cfg( feature = "stm32f042" ) ]
@@ -288,8 +387,35 @@ impl VBat {
288
387
pub fn disable ( & mut self , adc : & mut Adc ) {
289
388
adc. rb . ccr . modify ( |_, w| w. vbaten ( ) . clear_bit ( ) ) ;
290
389
}
390
+
391
+ /// Returns if the internal VBat sense is enabled
392
+ pub fn is_enabled ( & self , adc : & Adc ) -> bool {
393
+ adc. rb . ccr . read ( ) . vbaten ( ) . bit_is_set ( )
394
+ }
395
+
396
+ /// Reads the value of VBat in milli-volts
397
+ pub fn read ( adc : & mut Adc ) -> u16 {
398
+ let mut vbat = Self :: new ( ) ;
399
+
400
+ let vbat_val: u16 = if vbat. is_enabled ( & adc) {
401
+ adc. read_abs_mv ( & mut vbat)
402
+ } else {
403
+ vbat. enable ( adc) ;
404
+
405
+ let ret = adc. read_abs_mv ( & mut vbat) ;
406
+
407
+ vbat. disable ( adc) ;
408
+ ret
409
+ } ;
410
+
411
+ vbat_val * 2
412
+ }
291
413
}
292
414
415
+ /// A stored ADC config, can be restored by using the `Adc::restore_cfg` method
416
+ #[ derive( Copy , Clone , Debug , PartialEq ) ]
417
+ pub struct StoredConfig ( AdcSampleTime , AdcAlign , AdcPrecision ) ;
418
+
293
419
#[ cfg( feature = "device-selected" ) ]
294
420
impl Adc {
295
421
/// Init a new Adc
@@ -309,6 +435,28 @@ impl Adc {
309
435
s
310
436
}
311
437
438
+ /// Saves a copy of the current ADC config
439
+ pub fn save_cfg ( & mut self ) -> StoredConfig {
440
+ StoredConfig ( self . sample_time , self . align , self . precision )
441
+ }
442
+
443
+ /// Restores a stored config
444
+ pub fn restore_cfg ( & mut self , cfg : StoredConfig ) {
445
+ self . sample_time = cfg. 0 ;
446
+ self . align = cfg. 1 ;
447
+ self . precision = cfg. 2 ;
448
+ }
449
+
450
+ /// Resets the ADC config to default, returning the existing config as
451
+ /// a stored config.
452
+ pub fn default_cfg ( & mut self ) -> StoredConfig {
453
+ let cfg = self . save_cfg ( ) ;
454
+ self . sample_time = AdcSampleTime :: default ( ) ;
455
+ self . align = AdcAlign :: default ( ) ;
456
+ self . precision = AdcPrecision :: default ( ) ;
457
+ cfg
458
+ }
459
+
312
460
/// Set the Adc sampling time
313
461
///
314
462
/// Options can be found in [AdcSampleTime](crate::adc::AdcSampleTime).
@@ -330,6 +478,32 @@ impl Adc {
330
478
self . precision = precision;
331
479
}
332
480
481
+ /// Returns the largest possible sample value for the current settings
482
+ pub fn max_sample ( & self ) -> u16 {
483
+ match self . align {
484
+ AdcAlign :: Left => u16:: max_value ( ) ,
485
+ AdcAlign :: LeftAsRM => match self . precision {
486
+ AdcPrecision :: B_6 => u8:: max_value ( ) as u16 ,
487
+ _ => u16:: max_value ( ) ,
488
+ } ,
489
+ AdcAlign :: Right => match self . precision {
490
+ AdcPrecision :: B_12 => ( 1 << 12 ) - 1 ,
491
+ AdcPrecision :: B_10 => ( 1 << 10 ) - 1 ,
492
+ AdcPrecision :: B_8 => ( 1 << 8 ) - 1 ,
493
+ AdcPrecision :: B_6 => ( 1 << 6 ) - 1 ,
494
+ } ,
495
+ }
496
+ }
497
+
498
+ /// Read the value of a channel and converts the result to milli-volts
499
+ pub fn read_abs_mv < PIN : Channel < Adc , ID = u8 > > ( & mut self , pin : & mut PIN ) -> u16 {
500
+ let vdda = VRef :: read_vdda ( self ) as u32 ;
501
+ let v: u32 = self . read ( pin) . unwrap ( ) ;
502
+ let max_samp = self . max_sample ( ) as u32 ;
503
+
504
+ ( v * vdda / max_samp) as u16
505
+ }
506
+
333
507
fn calibrate ( & mut self ) {
334
508
/* Ensure that ADEN = 0 */
335
509
if self . rb . cr . read ( ) . aden ( ) . bit_is_set ( ) {
0 commit comments