From 179413aec25e00d9b2f8d953d3390da920b99b71 Mon Sep 17 00:00:00 2001 From: Scott Shawcroft Date: Tue, 17 Jan 2017 14:40:43 -0800 Subject: [PATCH] atmel-samd: Support multiple ADC channels at once. Fixes #61 --- atmel-samd/common-hal/nativeio/AnalogIn.c | 56 +++++++++++++++-------- atmel-samd/common-hal/nativeio/types.h | 2 +- 2 files changed, 38 insertions(+), 20 deletions(-) diff --git a/atmel-samd/common-hal/nativeio/AnalogIn.c b/atmel-samd/common-hal/nativeio/AnalogIn.c index 0d5f98310b..37f8c13e6b 100644 --- a/atmel-samd/common-hal/nativeio/AnalogIn.c +++ b/atmel-samd/common-hal/nativeio/AnalogIn.c @@ -26,6 +26,7 @@ #include +#include "py/gc.h" #include "py/nlr.h" #include "py/runtime.h" #include "py/binary.h" @@ -35,6 +36,10 @@ #include "asf/sam0/drivers/adc/adc.h" #include "samd21_pins.h" +// Number of active ADC channels. +volatile uint8_t active_channel_count; +struct adc_module *adc_instance = NULL; + void common_hal_nativeio_analogin_construct(nativeio_analogin_obj_t* self, const mcu_pin_obj_t *pin) { if (!pin->has_adc) { @@ -44,42 +49,55 @@ void common_hal_nativeio_analogin_construct(nativeio_analogin_obj_t* self, self->pin = pin; - struct adc_config config_adc; - adc_get_config_defaults(&config_adc); + if (adc_instance == NULL) { + struct adc_config config_adc; + adc_get_config_defaults(&config_adc); - config_adc.reference = ADC_REFERENCE_INTVCC1; - config_adc.gain_factor = ADC_GAIN_FACTOR_DIV2; - config_adc.positive_input = self->pin->adc_input; - config_adc.resolution = ADC_RESOLUTION_16BIT; - config_adc.clock_prescaler = ADC_CLOCK_PRESCALER_DIV128; + config_adc.reference = ADC_REFERENCE_INTVCC1; + config_adc.gain_factor = ADC_GAIN_FACTOR_DIV2; + config_adc.positive_input = self->pin->adc_input; + config_adc.resolution = ADC_RESOLUTION_16BIT; + config_adc.clock_prescaler = ADC_CLOCK_PRESCALER_DIV128; - adc_init(&self->adc_instance, ADC, &config_adc); + // Allocate the instance on the heap so we only use the memory when we + // need it. + adc_instance = gc_alloc(sizeof(struct adc_module), false); + + adc_init(adc_instance, ADC, &config_adc); + } + + self->adc_instance = adc_instance; + active_channel_count++; } void common_hal_nativeio_analogin_deinit(nativeio_analogin_obj_t *self) { - // TODO(tannewt): Count how many pins are in use and only reset the ADC when - // none are used. - adc_reset(&self->adc_instance); + active_channel_count--; + if (active_channel_count == 0) { + adc_reset(adc_instance); + gc_free(adc_instance); + // Set our reference to NULL so the GC doesn't mistakenly see the + // pointer in memory. + adc_instance = NULL; + } reset_pin(self->pin->pin); } -// TODO(tannewt): Don't turn it all on just for one read. This simplifies -// handling of reading multiple inputs and surviving sleep though so for now its -// ok. uint16_t common_hal_nativeio_analogin_get_value(nativeio_analogin_obj_t *self) { - adc_enable(&self->adc_instance); - adc_start_conversion(&self->adc_instance); + adc_set_positive_input(adc_instance, self->pin->adc_input); + + adc_enable(adc_instance); + adc_start_conversion(adc_instance); uint16_t data; - enum status_code status = adc_read(&self->adc_instance, &data); + enum status_code status = adc_read(adc_instance, &data); while (status == STATUS_BUSY) { - status = adc_read(&self->adc_instance, &data); + status = adc_read(adc_instance, &data); } if (status == STATUS_ERR_OVERFLOW) { // TODO(tannewt): Throw an error. } - adc_disable(&self->adc_instance); + adc_disable(adc_instance); return data; } diff --git a/atmel-samd/common-hal/nativeio/types.h b/atmel-samd/common-hal/nativeio/types.h index 9f83effb13..1f483eced0 100644 --- a/atmel-samd/common-hal/nativeio/types.h +++ b/atmel-samd/common-hal/nativeio/types.h @@ -55,7 +55,7 @@ typedef struct { mp_obj_base_t base; const mcu_pin_obj_t * pin; - struct adc_module adc_instance; + struct adc_module * adc_instance; } nativeio_analogin_obj_t; typedef struct {