72
72
#define INT1V_DIVIDER_1000 1000.0
73
73
#define ADC_12BIT_FULL_SCALE_VALUE_FLOAT 4095.0
74
74
75
+ // channel argument (ignored in calls below)
76
+ #define IGNORED_CHANNEL 0
75
77
76
78
// Decimal to fraction conversion. (adapted from ASF sample).
77
79
STATIC float convert_dec_to_frac (uint8_t val ) {
@@ -196,14 +198,12 @@ float common_hal_mcu_processor_get_temperature(void) {
196
198
197
199
adc_sync_set_resolution (& adc , ADC_CTRLB_RESSEL_12BIT_Val );
198
200
adc_sync_set_reference (& adc , ADC_REFCTRL_REFSEL_INT1V_Val );
199
- // Channel passed in adc_sync_enable_channel is actually ignored (!) .
200
- adc_sync_enable_channel (& adc , ADC_INPUTCTRL_MUXPOS_TEMP_Val );
201
+ // Channel arg is ignored.
202
+ adc_sync_enable_channel (& adc , IGNORED_CHANNEL );
201
203
adc_sync_set_inputs (& adc ,
202
204
ADC_INPUTCTRL_MUXPOS_TEMP_Val , // pos_input
203
205
ADC_INPUTCTRL_MUXNEG_GND_Val , // neg_input
204
- ADC_INPUTCTRL_MUXPOS_TEMP_Val ); // channel channel (this arg is ignored (!))
205
-
206
- adc_sync_set_resolution (& adc , ADC_CTRLB_RESSEL_12BIT_Val );
206
+ IGNORED_CHANNEL ); // channel (ignored)
207
207
208
208
hri_adc_write_CTRLB_PRESCALER_bf (adc .device .hw , ADC_CTRLB_PRESCALER_DIV32_Val );
209
209
hri_adc_write_SAMPCTRL_SAMPLEN_bf (adc .device .hw , ADC_TEMP_SAMPLE_LENGTH );
@@ -222,62 +222,100 @@ float common_hal_mcu_processor_get_temperature(void) {
222
222
// like voltage reference / ADC channel change"
223
223
// Empirical observation shows the first reading is quite different than subsequent ones.
224
224
225
- // The channel listed in adc_sync_read_channel is actually ignored(!).
226
- // Must be set as above with adc_sync_set_inputs.
227
- adc_sync_read_channel (& adc , ADC_INPUTCTRL_MUXPOS_TEMP_Val , ((uint8_t * ) & value ), 2 );
228
- adc_sync_read_channel (& adc , ADC_INPUTCTRL_MUXPOS_TEMP_Val , ((uint8_t * ) & value ), 2 );
225
+ // Channel arg is ignored.
226
+ adc_sync_read_channel (& adc , IGNORED_CHANNEL , ((uint8_t * ) & value ), 2 );
227
+ adc_sync_read_channel (& adc , IGNORED_CHANNEL , ((uint8_t * ) & value ), 2 );
229
228
230
229
adc_sync_deinit (& adc );
231
230
return calculate_temperature (value );
232
231
#endif // SAMD21
233
232
234
233
#ifdef SAMD51
235
234
adc_sync_set_resolution (& adc , ADC_CTRLB_RESSEL_12BIT_Val );
236
- // Reference voltage choice is a guess. It's not specified in the datasheet that I can see .
235
+ // Using INTVCC0 as the reference voltage .
237
236
// INTVCC1 seems to read a little high.
238
- // INTREF doesn't work: ADC hangs BUSY.
237
+ // INTREF doesn't work: ADC hangs BUSY. It's supposed to work, but does not.
238
+ // The SAME54 example from Atmel START implicitly uses INTREF.
239
239
adc_sync_set_reference (& adc , ADC_REFCTRL_REFSEL_INTVCC0_Val );
240
240
241
- // If ONDEMAND=1, we don't need to use the VREF.TSSEL bit to choose PTAT and CTAT.
242
241
hri_supc_set_VREF_ONDEMAND_bit (SUPC );
242
+ // Enable temperature sensor.
243
243
hri_supc_set_VREF_TSEN_bit (SUPC );
244
+ hri_supc_set_VREF_VREFOE_bit (SUPC );
244
245
245
- // Channel passed in adc_sync_enable_channel is actually ignored (!) .
246
- adc_sync_enable_channel (& adc , ADC_INPUTCTRL_MUXPOS_PTAT_Val );
246
+ // Channel arg is ignored.
247
+ adc_sync_enable_channel (& adc , IGNORED_CHANNEL );
247
248
adc_sync_set_inputs (& adc ,
248
249
ADC_INPUTCTRL_MUXPOS_PTAT_Val , // pos_input
249
250
ADC_INPUTCTRL_MUXNEG_GND_Val , // neg_input
250
- ADC_INPUTCTRL_MUXPOS_PTAT_Val ); // channel (this arg is ignored (!) )
251
+ IGNORED_CHANNEL ); // channel (ignored )
251
252
252
253
// Read both temperature sensors.
253
254
volatile uint16_t ptat ;
254
255
volatile uint16_t ctat ;
255
256
256
- // The channel listed in adc_sync_read_channel is actually ignored(!).
257
- // Must be set as above with adc_sync_set_inputs.
258
- // Read twice for stability (necessary?)
259
- adc_sync_read_channel (& adc , ADC_INPUTCTRL_MUXPOS_PTAT_Val , ((uint8_t * ) & ptat ), 2 );
260
- adc_sync_read_channel (& adc , ADC_INPUTCTRL_MUXPOS_PTAT_Val , ((uint8_t * ) & ptat ), 2 );
257
+ // Read twice for stability (necessary?).
258
+ adc_sync_read_channel (& adc , IGNORED_CHANNEL , ((uint8_t * ) & ptat ), 2 );
259
+ adc_sync_read_channel (& adc , IGNORED_CHANNEL , ((uint8_t * ) & ptat ), 2 );
261
260
262
261
adc_sync_set_inputs (& adc ,
263
262
ADC_INPUTCTRL_MUXPOS_CTAT_Val , // pos_input
264
263
ADC_INPUTCTRL_MUXNEG_GND_Val , // neg_input
265
- ADC_INPUTCTRL_MUXPOS_CTAT_Val ); // channel (this arg is ignored (!))
266
-
267
- // Channel passed in adc_sync_enable_channel is actually ignored (!).
268
- adc_sync_enable_channel (& adc , ADC_INPUTCTRL_MUXPOS_CTAT_Val );
269
- // The channel listed in adc_sync_read_channel is actually ignored(!).
270
- // Must be set as above with adc_sync_set_inputs.
271
- // Read twice for stability (necessary?)
272
- adc_sync_read_channel (& adc , ADC_INPUTCTRL_MUXPOS_CTAT_Val , ((uint8_t * ) & ctat ), 2 );
273
- adc_sync_read_channel (& adc , ADC_INPUTCTRL_MUXPOS_CTAT_Val , ((uint8_t * ) & ctat ), 2 );
274
- hri_supc_set_VREF_ONDEMAND_bit (SUPC );
264
+ IGNORED_CHANNEL ); // channel (ignored)
265
+
266
+ adc_sync_read_channel (& adc , IGNORED_CHANNEL , ((uint8_t * ) & ctat ), 2 );
267
+ adc_sync_read_channel (& adc , IGNORED_CHANNEL , ((uint8_t * ) & ctat ), 2 );
268
+
269
+ // Turn off temp sensor.
270
+ hri_supc_clear_VREF_TSEN_bit (SUPC );
275
271
276
272
adc_sync_deinit (& adc );
277
273
return calculate_temperature (ptat , ctat );
278
274
#endif // SAMD51
279
275
}
280
276
277
+ float common_hal_mcu_processor_get_voltage (void ) {
278
+ struct adc_sync_descriptor adc ;
279
+
280
+ static Adc * adc_insts [] = ADC_INSTS ;
281
+ samd_peripherals_adc_setup (& adc , adc_insts [0 ]);
282
+
283
+ #ifdef SAMD21
284
+ adc_sync_set_reference (& adc , ADC_REFCTRL_REFSEL_INT1V_Val );
285
+ #endif
286
+
287
+ #ifdef SAMD51
288
+ hri_supc_set_VREF_SEL_bf (SUPC , SUPC_VREF_SEL_1V0_Val );
289
+ // ONDEMAND must be clear, and VREFOE must be set, or else the ADC conversion will not complete.
290
+ // See https://community.atmel.com/forum/samd51-using-intref-adc-voltage-reference
291
+ hri_supc_clear_VREF_ONDEMAND_bit (SUPC );
292
+ hri_supc_set_VREF_VREFOE_bit (SUPC );
293
+ adc_sync_set_reference (& adc , ADC_REFCTRL_REFSEL_INTREF_Val );
294
+ #endif
295
+
296
+ adc_sync_set_resolution (& adc , ADC_CTRLB_RESSEL_12BIT_Val );
297
+ // Channel arg is ignored.
298
+ adc_sync_set_inputs (& adc ,
299
+ ADC_INPUTCTRL_MUXPOS_SCALEDIOVCC_Val , // IOVCC/4 (nominal 3.3V/4)
300
+ ADC_INPUTCTRL_MUXNEG_GND_Val , // neg_input
301
+ IGNORED_CHANNEL ); // channel (ignored).
302
+ adc_sync_enable_channel (& adc , IGNORED_CHANNEL );
303
+
304
+ volatile uint16_t reading ;
305
+
306
+ // Channel arg is ignored.
307
+ // Read twice and discard first result, as recommended in section 14 of
308
+ // http://www.atmel.com/images/Atmel-42645-ADC-Configurations-with-Examples_ApplicationNote_AT11481.pdf
309
+ // "Discard the first conversion result whenever there is a change in ADC configuration
310
+ // like voltage reference / ADC channel change"
311
+ // Empirical observation shows the first reading is quite different than subsequent ones.
312
+ adc_sync_read_channel (& adc , IGNORED_CHANNEL , ((uint8_t * ) & reading ), 2 );
313
+ adc_sync_read_channel (& adc , IGNORED_CHANNEL , ((uint8_t * ) & reading ), 2 );
314
+
315
+ adc_sync_deinit (& adc );
316
+ // Multiply by 4 to compensate for SCALEDIOVCC division by 4.
317
+ return (reading / 4095.0f ) * 4.0f ;
318
+ }
281
319
282
320
uint32_t common_hal_mcu_processor_get_frequency (void ) {
283
321
// TODO(tannewt): Determine this dynamically.
0 commit comments