Skip to content

Commit 75c3be3

Browse files
authored
Re-initialize ADC before every AnalogIn read. (#255)
`microcontroller.cpu.temperature` uses different ADC settings, and caused AnalogIn to give wrong answers. AnalogIn can no longer assume it's the only user of the ADC.
1 parent 5aa8922 commit 75c3be3

File tree

3 files changed

+45
-17
lines changed

3 files changed

+45
-17
lines changed

atmel-samd/common-hal/analogio/AnalogIn.c

Lines changed: 42 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -40,7 +40,10 @@
4040

4141
// Number of active ADC channels.
4242
volatile uint8_t active_channel_count;
43+
44+
// Shared between all the instances. Allocated only when needed.
4345
struct adc_module *adc_instance = NULL;
46+
struct adc_config *config_adc = NULL;
4447

4548
void common_hal_analogio_analogin_construct(analogio_analogin_obj_t* self,
4649
const mcu_pin_obj_t *pin) {
@@ -53,23 +56,24 @@ void common_hal_analogio_analogin_construct(analogio_analogin_obj_t* self,
5356
self->pin = pin;
5457

5558
if (adc_instance == NULL) {
56-
struct adc_config config_adc;
57-
adc_get_config_defaults(&config_adc);
58-
59-
config_adc.reference = ADC_REFERENCE_INTVCC1;
60-
config_adc.gain_factor = ADC_GAIN_FACTOR_DIV2;
61-
config_adc.positive_input = self->pin->adc_input;
62-
config_adc.resolution = ADC_RESOLUTION_16BIT;
63-
config_adc.clock_prescaler = ADC_CLOCK_PRESCALER_DIV128;
64-
65-
// Allocate the instance on the heap so we only use the memory when we
59+
// Allocate strucs on the heap so we only use the memory when we
6660
// need it.
6761
adc_instance = gc_alloc(sizeof(struct adc_module), false);
62+
config_adc = gc_alloc(sizeof(struct adc_config), false);
63+
64+
adc_get_config_defaults(config_adc);
6865

69-
adc_init(adc_instance, ADC, &config_adc);
66+
config_adc->reference = ADC_REFERENCE_INTVCC1;
67+
config_adc->gain_factor = ADC_GAIN_FACTOR_DIV2;
68+
config_adc->positive_input = self->pin->adc_input;
69+
config_adc->resolution = ADC_RESOLUTION_16BIT;
70+
config_adc->clock_prescaler = ADC_CLOCK_PRESCALER_DIV128;
71+
72+
adc_init(adc_instance, ADC, config_adc);
7073
}
7174

7275
self->adc_instance = adc_instance;
76+
self->config_adc = config_adc;
7377
active_channel_count++;
7478
}
7579

@@ -78,9 +82,11 @@ void common_hal_analogio_analogin_deinit(analogio_analogin_obj_t *self) {
7882
if (active_channel_count == 0) {
7983
adc_reset(adc_instance);
8084
gc_free(adc_instance);
81-
// Set our reference to NULL so the GC doesn't mistakenly see the
82-
// pointer in memory.
85+
gc_free(config_adc);
86+
// Set our references to NULL so the GC doesn't mistakenly see the
87+
// pointers in memory.
8388
adc_instance = NULL;
89+
config_adc = NULL;
8490
}
8591
reset_pin(self->pin->pin);
8692
}
@@ -94,16 +100,35 @@ void analogin_reset() {
94100
}
95101

96102
uint16_t common_hal_analogio_analogin_get_value(analogio_analogin_obj_t *self) {
97-
adc_set_positive_input(adc_instance, self->pin->adc_input);
103+
// Something else might have used the ADC in a different way,
104+
// so we have to completely re-initialize it.
105+
// ADC must have been disabled before adc_init() is called.
106+
adc_init(adc_instance, ADC, config_adc);
107+
config_adc->positive_input = self->pin->adc_input;
98108

99109
adc_enable(adc_instance);
100-
adc_start_conversion(adc_instance);
110+
111+
// Read twice and discard first result, as recommended in section 14 of
112+
// http://www.atmel.com/images/Atmel-42645-ADC-Configurations-with-Examples_ApplicationNote_AT11481.pdf
113+
// "Discard the first conversion result whenever there is a change in ADC configuration
114+
// like voltage reference / ADC channel change"
115+
// Empirical observation shows the first reading is quite different than subsequent ones.
101116

102117
uint16_t data;
103-
enum status_code status = adc_read(adc_instance, &data);
104-
while (status == STATUS_BUSY) {
118+
enum status_code status;
119+
120+
adc_start_conversion(adc_instance);
121+
do {
105122
status = adc_read(adc_instance, &data);
123+
} while (status == STATUS_BUSY);
124+
if (status == STATUS_ERR_OVERFLOW) {
125+
// TODO(tannewt): Throw an error.
106126
}
127+
128+
adc_start_conversion(adc_instance);
129+
do {
130+
status = adc_read(adc_instance, &data);
131+
} while (status == STATUS_BUSY);
107132
if (status == STATUS_ERR_OVERFLOW) {
108133
// TODO(tannewt): Throw an error.
109134
}

atmel-samd/common-hal/analogio/AnalogIn.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -42,6 +42,7 @@ typedef struct {
4242
mp_obj_base_t base;
4343
const mcu_pin_obj_t * pin;
4444
struct adc_module * adc_instance;
45+
struct adc_config * config_adc;
4546
} analogio_analogin_obj_t;
4647

4748
void analogin_reset(void);

atmel-samd/common-hal/microcontroller/Processor.c

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -226,6 +226,8 @@ float common_hal_mcu_processor_get_temperature(void) {
226226
status = adc_read(&adc_instance_struct, &data);
227227
} while (status == STATUS_BUSY);
228228

229+
// Disable so that someone else can use the adc with different settings.
230+
adc_disable(&adc_instance_struct);
229231
return calculate_temperature(data, &nvm_calibration_data);
230232
}
231233

0 commit comments

Comments
 (0)