From 70a39c9c673e381a2e32ffeb1de3a72a105d935b Mon Sep 17 00:00:00 2001 From: Loic Lefort Date: Wed, 4 Oct 2017 13:54:10 +0200 Subject: [PATCH] drivers/analog: fix several issues in ADC driver Fix several issues in ADC driver: - remove sequencer support (was not working anyway) - fix both polling/irq and DMA transfers - fix triggers Signed-off-by: Loic Lefort --- drivers/analog/adc.c | 20 +++ drivers/analog/adc.h | 68 ++++----- drivers/analog/adcd.c | 133 ++++++++-------- drivers/analog/adcd.h | 33 ++-- examples/adc/TESTING.md | 2 - examples/adc/main.c | 328 +++++++++++++++++++++------------------- 6 files changed, 299 insertions(+), 285 deletions(-) diff --git a/drivers/analog/adc.c b/drivers/analog/adc.c index efee6667e..358004ea8 100644 --- a/drivers/analog/adc.c +++ b/drivers/analog/adc.c @@ -190,6 +190,26 @@ void adc_disable_it(uint32_t mask) ADC->ADC_IDR = mask; } +uint32_t adc_get_status(void) +{ + return ADC->ADC_ISR; +} + +void adc_start_conversion(void) +{ + ADC->ADC_CR = ADC_CR_START; +} + +void adc_enable_channel(uint32_t channel) +{ + ADC->ADC_CHER = 1u << channel; +} + +void adc_disable_channel(uint32_t channel) +{ + ADC->ADC_CHDR = 1u << channel; +} + /** * \brief Set ADC timing. * diff --git a/drivers/analog/adc.h b/drivers/analog/adc.h index 6c6038a14..31a41dc74 100644 --- a/drivers/analog/adc.h +++ b/drivers/analog/adc.h @@ -75,48 +75,6 @@ extern "C" { #endif -/*------------------------------------------------------------------------------ - * Macros function of register access - *------------------------------------------------------------------------------*/ - -#define adc_get_mode_reg() (ADC->ADC_MR) - -#define adc_start_conversion() (ADC->ADC_CR = ADC_CR_START) - -#define adc_enable_channel(channel) { \ - ADC->ADC_CHER = (1 << (channel)); \ - } - -#define adc_disable_channel(channel) { \ - ADC->ADC_CHDR = (1 << (channel)); \ - } - -#define adc_enable_interrupt(mode) { \ - ADC->ADC_IER = (mode); \ - } - -#define adc_disable_interrupt(mode) { \ - ADC->ADC_IDR = (mode); \ - } - -#define adc_set_channel_gain(mode) { \ - ADC->ADC_CGR = mode; \ - } - -#define adc_enable_data_ready_it() (ADC->ADC_IER = ADC_IER_DRDY) - -#define adc_get_status() (ADC->ADC_ISR) - -#define adc_get_compare_mode() ((ADC->ADC_EMR)& (ADC_EMR_CMPMODE_Msk)) - -#define adc_get_channel_status() (ADC->ADC_CHSR) - -#define adc_interrupt_mask_status() (ADC->ADC_IMR) - -#define adc_get_last_converted_data() (ADC->ADC_LCDR) - -#define adc_get_overrun_status() (ADC->ADC_OVER) - /*------------------------------------------------------------------------------ * Exported functions *------------------------------------------------------------------------------*/ @@ -159,6 +117,32 @@ extern void adc_enable_it(uint32_t mask); */ extern void adc_disable_it(uint32_t mask); +/** + * \brief Get ADC Interrupt Status + * + * \return the content of the ADC interrupt status register + */ +extern uint32_t adc_get_status(void); + +/** + * \brief Trigger ADC conversion (i.e. software trigger) + */ +extern void adc_start_conversion(void); + +/** + * \brief Enable ADC channel + * + * \param channel index of ADC channel to enable + */ +extern void adc_enable_channel(uint32_t channel); + +/** + * \brief Disable ADC channel + * + * \param channel index of ADC channel to disable + */ +extern void adc_disable_channel(uint32_t channel); + /** * \brief Set ADC timing. * diff --git a/drivers/analog/adcd.c b/drivers/analog/adcd.c index 3bf65a223..75caab93f 100644 --- a/drivers/analog/adcd.c +++ b/drivers/analog/adcd.c @@ -41,12 +41,6 @@ #include "peripherals/pmc.h" #include "trace.h" -/*---------------------------------------------------------------------------- - * Local variables - *----------------------------------------------------------------------------*/ - -volatile static bool single_transfer_ready; - /*---------------------------------------------------------------------------- * Local functions *----------------------------------------------------------------------------*/ @@ -61,7 +55,6 @@ static int _adcd_dma_callback(void *arg, void* arg2) cache_invalidate_region((uint32_t*)desc->xfer.buf->data, desc->xfer.buf->size); mutex_unlock(&desc->mutex); - callback_call(&desc->xfer.callback, NULL); return 0; @@ -72,19 +65,14 @@ static void _adcd_transfer_buffer_dma(struct _adcd_desc* desc) struct _dma_transfer_cfg cfg; struct _callback _cb; - adc_start_conversion(); - cfg.saddr = (void*)&ADC->ADC_LCDR; cfg.daddr = desc->xfer.buf->data; cfg.len = desc->xfer.buf->size / 2; - dma_configure_transfer(desc->xfer.dma.channel, &desc->xfer.dma.cfg_dma, &cfg, 0); + dma_configure_transfer(desc->xfer.dma.channel, &desc->xfer.dma.cfg_dma, &cfg, 1); callback_set(&_cb, _adcd_dma_callback, desc); dma_set_callback(desc->xfer.dma.channel, &_cb); dma_start_transfer(desc->xfer.dma.channel); - - adcd_wait_transfer(desc); - dma_free_channel(desc->xfer.dma.channel); } /** @@ -92,47 +80,38 @@ static void _adcd_transfer_buffer_dma(struct _adcd_desc* desc) */ static void _adcd_handler(uint32_t source, void* user_arg) { - uint32_t status; - uint8_t i; - uint32_t value; struct _adcd_desc* desc = (struct _adcd_desc*)user_arg; + uint16_t* data = (uint16_t*)desc->xfer.buf->data; + uint32_t mask = 1u << (31 - CLZ(desc->cfg.channel_mask)); + uint32_t status; + int index = 0; + int i; /* Get Interrupt Status (ISR) */ status = adc_get_status(); - adc_disable_it(0xFFFFFFFF); - /* check at least one EOCn flag set */ - if (status & 0x00000FFF) { + if (status & mask) { + /* Read results */ for (i = 0; i < adc_get_num_channels(); i++) { - value = adc_get_converted_data(i); - /* Check ISR "End of Conversion" corresponding bit */ - if ((status & (1u << i))) { - *(uint16_t*)(desc->xfer.buf->data + i * sizeof (uint16_t)) - = (i << ADC_LCDR_CHNB_Pos) | value; + uint32_t chan_bit = 1u << i; + if (desc->cfg.channel_mask & chan_bit) { + uint32_t value = adc_get_converted_data(i); + data[index++] = (i << ADC_LCDR_CHNB_Pos) | value; } } + + mutex_unlock(&desc->mutex); + callback_call(&desc->xfer.callback, NULL); } - single_transfer_ready = true; } static void _adcd_transfer_buffer_polling(struct _adcd_desc* desc) { - uint32_t i; - uint32_t ier_mask = 0; - uint8_t channels = desc->xfer.buf->size / sizeof(uint16_t); - - single_transfer_ready = false; - /* Enable Data ready interrupt */ - for (i = 0; i < channels; i++) { - ier_mask |= 0x1u << desc->cfg.chan_sequence[i]; - } - adc_enable_it(ier_mask) ; + /* Enable EOC interrupt for higher numbered channel */ + adc_disable_it(0xffffffffu); + adc_get_status(); + adc_enable_it(1u << (31 - CLZ(desc->cfg.channel_mask))); irq_enable(ID_ADC); - adc_start_conversion(); - - while(!single_transfer_ready); - mutex_unlock(&desc->mutex); - callback_call(&desc->xfer.callback, NULL); } /** @@ -141,8 +120,7 @@ static void _adcd_transfer_buffer_polling(struct _adcd_desc* desc) */ static void adcd_configure(struct _adcd_desc* desc) { - uint8_t i = 0; - uint8_t channels = desc->xfer.buf->size / sizeof(uint16_t); + int i = 0; irq_disable(ID_ADC); @@ -152,21 +130,12 @@ static void adcd_configure(struct _adcd_desc* desc) desc->xfer.dma.cfg_dma.data_width = DMA_DATA_WIDTH_HALF_WORD; desc->xfer.dma.cfg_dma.chunk_size = DMA_CHUNK_SIZE_1; - for (i = 0; i < channels; i++) - adc_disable_channel(desc->cfg.channel_used[i]); - - /* Enable/disable sequencer */ - if (desc->cfg.sequence_enabled) { - /* Set user defined channel sequence */ - adc_set_sequence_by_list(desc->cfg.chan_sequence, channels); - /* Enable sequencer */ - adc_set_sequence_mode(true); + for (i = 0; i < adc_get_num_channels(); i++) + adc_disable_channel(i); - } else { - adc_set_sequence(0, 0); - /* Disable sequencer */ - adc_set_sequence_mode(false); - } + /* Disable sequencer */ + adc_set_sequence(0, 0); + adc_set_sequence_mode(false); /* Set power save */ if (desc->cfg.power_save_enabled) @@ -174,11 +143,11 @@ static void adcd_configure(struct _adcd_desc* desc) else adc_set_sleep_mode(false); - /* Configure trigger mode and start convention */ + /* Configure trigger mode */ switch (desc->cfg.trigger_mode) { case TRIGGER_MODE_SOFTWARE: /* No trigger, only software trigger can start conversions */ - adc_set_trigger(ADC_MR_TRGSEL_ADC_TRIG0); + adc_set_trigger_mode(ADC_TRGR_TRGMOD_NO_TRIGGER); break; case TRIGGER_MODE_ADTRG: @@ -219,22 +188,47 @@ static void adcd_configure(struct _adcd_desc* desc) break; #endif /* ADC_MR_TRGSEL_ADC_TRIG5 */ - case TRIGGER_MODE_ADC_TIMER : + case TRIGGER_MODE_ADC_TIMER: /* Trigger on internal ADC timer */ - adc_set_trigger(ADC_MR_TRGSEL_ADC_TRIG0); + adc_set_trigger_mode(ADC_TRGR_TRGMOD_PERIOD_TRIG); adc_set_trigger_period(250); break; default : break; } - adc_set_trigger_mode(desc->cfg.trigger_edge); - /* Enable channels, gain, single mode */ - for (i = 0; i < channels; i++) { - adc_enable_channel(desc->cfg.channel_used[i]); + /* Configure trigger edge */ + if (desc->cfg.trigger_mode != TRIGGER_MODE_SOFTWARE && + desc->cfg.trigger_mode != TRIGGER_MODE_ADC_TIMER) { + switch (desc->cfg.trigger_edge) { + case TRIGGER_EXT_TRIG_RISE: + adc_set_trigger_mode(ADC_TRGR_TRGMOD_EXT_TRIG_RISE); + break; + case TRIGGER_EXT_TRIG_FALL: + adc_set_trigger_mode(ADC_TRGR_TRGMOD_EXT_TRIG_FALL); + break; + case TRIGGER_EXT_TRIG_ANY: + adc_set_trigger_mode(ADC_TRGR_TRGMOD_EXT_TRIG_ANY); + break; + case TRIGGER_PEN: + adc_set_trigger_mode(ADC_TRGR_TRGMOD_PEN_TRIG); + break; + case TRIGGER_CONTINUOUS: + adc_set_trigger_mode(ADC_TRGR_TRGMOD_CONTINUOUS); + break; + default: + break; + } + } + + /* Enable channels, single mode */ + for (i = 0; i < adc_get_num_channels(); i++) { + if (desc->cfg.channel_mask & (1u << i)) { + adc_enable_channel(i); #ifdef CONFIG_HAVE_ADC_DIFF_INPUT - adc_disable_channel_differential_input(desc->cfg.chan_sequence[i]); + adc_disable_channel_differential_input(i); #endif + } } } @@ -242,11 +236,13 @@ static void adcd_configure(struct _adcd_desc* desc) * Public functions *----------------------------------------------------------------------------*/ + void adcd_initialize(struct _adcd_desc* desc) { /* Initialize ADC */ adc_initialize(); adc_set_ts_mode(0); + /* * Formula: ADCClock = MCK / ((PRESCAL+1) * 2) * For example, MCK = 64MHZ, PRESCAL = 4, then: @@ -268,13 +264,16 @@ void adcd_initialize(struct _adcd_desc* desc) */ /* Set ADC timing */ adc_set_timing(ADC_MR_STARTUP_SUT512, 0, 0); + /* Enable channel number tag */ adc_set_tag_enable(true); + /* Configure IRQ handler */ irq_add_handler(ID_ADC, _adcd_handler, desc); /* Allocate one DMA channel for receive data from ADC_LCDR */ - desc->xfer.dma.channel = dma_allocate_channel(ID_ADC, DMA_PERIPH_MEMORY); + if (!desc->xfer.dma.channel) + desc->xfer.dma.channel = dma_allocate_channel(ID_ADC, DMA_PERIPH_MEMORY); assert(desc->xfer.dma.channel); } @@ -303,7 +302,7 @@ bool adcd_is_busy(struct _adcd_desc* desc) void adcd_wait_transfer(struct _adcd_desc* desc) { while (adcd_is_busy(desc)) { - if(desc->cfg.dma_enabled) + if (desc->cfg.dma_enabled) dma_poll(); } } diff --git a/drivers/analog/adcd.h b/drivers/analog/adcd.h index 7538be97b..c9cbc2803 100644 --- a/drivers/analog/adcd.h +++ b/drivers/analog/adcd.h @@ -28,8 +28,8 @@ */ -#ifndef ADCD_HEADER__ -#define ADCD_HEADER__ +#ifndef ADCD_H_ +#define ADCD_H_ /*------------------------------------------------------------------------------ * Header @@ -50,6 +50,8 @@ #define ADCD_ERROR_LOCK (1) #define ADCD_ERROR_TRANSFER (2) +#define ADCD_MAX_CHANNELS (12) + /** ADC trigger modes */ enum _trg_mode { @@ -75,21 +77,22 @@ enum _trg_edge TRIGGER_CONTINUOUS }; +/* structure to define ADC config */ +struct _adcd_cfg { + enum _trg_mode trigger_mode; + enum _trg_edge trigger_edge; + bool dma_enabled; + bool power_save_enabled; + uint32_t freq; + uint32_t channel_mask; +}; + +/* structure to define ADC state */ struct _adcd_desc { - /* structure to define AES parameter */ + struct _adcd_cfg cfg; /* following fields are used internally */ mutex_t mutex; - struct { - enum _trg_mode trigger_mode; - enum _trg_edge trigger_edge; - bool sequence_enabled; - bool dma_enabled; - bool power_save_enabled; - uint32_t freq; - uint8_t channel_used[4]; - uint8_t chan_sequence[4]; - } cfg; /* structure to hold data about current transfer */ struct { @@ -107,8 +110,6 @@ struct _adcd_desc { * Functions *----------------------------------------------------------------------------*/ -extern void adcd_configure_mode(struct _adcd_desc* desc); - extern void adcd_initialize(struct _adcd_desc* desc); extern uint32_t adcd_transfer(struct _adcd_desc* desc, struct _buffer* buffer, struct _callback* cb); @@ -117,4 +118,4 @@ extern bool adcd_is_busy(struct _adcd_desc* desc); extern void adcd_wait_transfer(struct _adcd_desc* desc); -#endif /* ADCD_HEADER__ */ +#endif /* ADCD_H_ */ diff --git a/examples/adc/TESTING.md b/examples/adc/TESTING.md index 26739320a..d4e683a57 100644 --- a/examples/adc/TESTING.md +++ b/examples/adc/TESTING.md @@ -44,7 +44,6 @@ Menu: press a key to change the configuration.`` [X] 0: Set ADC trigger mode: Software. [ ] 1: Set ADC trigger mode: ADTRG. [ ] 2: Set ADC trigger mode: Timer TIOA. -[D] S: Enable/Disable sequencer. [D] D: Enable/Disable to tranfer with DMA. [D] P: Enable/Disable ADC power save mode. =========================================================`` @@ -58,7 +57,6 @@ Step | Description | Expected Result | Result Press '0' | Software trigger is selected, continuous converted value will be printed: ``Count:* CH03:*mV CH04:*mV CH00:*mV CH01:*mV CH02:*mV`` | PASSED | PASSED Press '1' | External trigger on rising edge of the ADTRG pin is selected, the converted value will be updated when triggered| PASSED | PASSED Press '2' | Timer TIOA trigger is selected, continuous converted value will be printed if triggered | PASSED | PASSED -Press 'S' | Enable/Disable sequencer, continuous converted value will be printed if triggered | PASSED | PASSED Press 'D' | Enable/Disable to tranfer with DMA, continuous converted value will be printed if triggered | PASSED | PASSED Press 'P' | Enable/Disable ADC power save mode, continuous converted value will be printed if triggered | PASSED | PASSED diff --git a/examples/adc/main.c b/examples/adc/main.c index 4a0296a11..8054e457b 100644 --- a/examples/adc/main.c +++ b/examples/adc/main.c @@ -49,9 +49,8 @@ * \section Description * * This application shows how to use the ADC using the several modes: - * with/without DMA, several types of trigger (Software, ADTRG, Timer, etc.), - * gain and offset selection, using sequencer. User can select different mode - * by configuration menu in the terminal. + * with/without DMA, several types of trigger (Software, ADTRG, Timer, etc.). + * User can select different mode by configuration menu in the terminal. * * * \section Usage @@ -85,7 +84,6 @@ * [X] 0: Set ADC trigger mode: Software. * [ ] 1: Set ADC trigger mode: ADTRG. * [ ] 2: Set ADC trigger mode: Timer TIOA. - * [E] S: Enable/Disable sequencer * [E] D: Enable/Disable to tranfer with DMA. * [D] P: Enable/Disable ADC power save mode. * Q: Quit configuration and start ADC. @@ -145,22 +143,24 @@ /** MAXIMUM DIGITAL VALUE */ #define DIGITAL_MAX ((1 << adc_get_resolution()) - 1) -/** ADC slected channels */ -static uint8_t adc_channel_used[] = -{ - 0, - 1, - 2, - 3, -}; +/** ADC channels to acquire */ +static uint8_t adc_channels[] = { 0, 1, 2, 3 }; /** Total number of ADC channels in use */ -#define NUM_CHANNELS ARRAY_SIZE(adc_channel_used) +#define NUM_CHANNELS ARRAY_SIZE(adc_channels) /*---------------------------------------------------------------------------- * Local types *----------------------------------------------------------------------------*/ +enum _adc_state { + STATE_WAITING = 0, + STATE_CONFIGURED = 1, + STATE_STARTED = 2, + STATE_CAPTURING = 3, + STATE_CAPTURED = 4, +}; + /** ADC sample data */ struct _adc_sample { @@ -170,21 +170,29 @@ struct _adc_sample CACHE_ALIGNED static uint16_t adc_buffer[NUM_CHANNELS]; -bool adc_converted = false; -unsigned count = 0; +static volatile bool adc_converted = false; +static unsigned count = 0; /*---------------------------------------------------------------------------- * Local variables *----------------------------------------------------------------------------*/ +static struct _timeout adc_timeout; + +static volatile enum _adc_state state; + /** ADC sample data */ static struct _adc_sample _data; -/** ADCD test instance */ +/** ADCD instance */ static struct _adcd_desc adcd; -/* /\** Definition of ADTRG pin *\/ */ +/** ADCD configuration for next capture */ +static struct _adcd_cfg adcd_cfg; + +/** ADTRG pin */ struct _pin pin_adtrg[] = {PIN_ADTRG}; + #if defined(CONFIG_BOARD_SAMA5D4_XPLAINED) struct _pin pin_trig = { PIO_GROUP_E, PIO_PE23, PIO_OUTPUT_1, PIO_PULLUP }; struct _pin pin_tioa0 = { PIO_GROUP_E, PIO_PE15C_TIOA0, PIO_PERIPH_C , PIO_DEFAULT }; @@ -231,86 +239,58 @@ static void _display_menu(void) printf("=========================================================\n\r"); printf("Menu: press a key to change the configuration.\n\r"); printf("---------------------------------------------------------\n\r"); - tmp = (adcd.cfg.trigger_mode == TRIGGER_MODE_SOFTWARE) ? 'X' : ' '; + tmp = (adcd_cfg.trigger_mode == TRIGGER_MODE_SOFTWARE) ? 'X' : ' '; printf("[%c] 0: Set ADC trigger mode: Software.\n\r", tmp); - tmp = (adcd.cfg.trigger_mode == TRIGGER_MODE_ADTRG) ? 'X' : ' '; + tmp = (adcd_cfg.trigger_mode == TRIGGER_MODE_ADTRG) ? 'X' : ' '; printf("[%c] 1: Set ADC trigger mode: ADTRG.\n\r", tmp); #ifdef ADC_TRIG_TIOA0 - tmp = (adcd.cfg.trigger_mode == TRIGGER_MODE_TIOA0) ? 'X' : ' '; + tmp = (adcd_cfg.trigger_mode == TRIGGER_MODE_TIOA0) ? 'X' : ' '; printf("[%c] 2: Set ADC trigger mode: Timer TIOA.\n\r", tmp); #endif /* ADC_TRIG_TIOA0 */ - tmp = (adcd.cfg.trigger_mode == TRIGGER_MODE_ADC_TIMER) ? 'X' : ' '; + tmp = (adcd_cfg.trigger_mode == TRIGGER_MODE_ADC_TIMER) ? 'X' : ' '; printf("[%c] 3: Set ADC trigger mode: ADC Internal Timer.\n\r", tmp); - tmp = (adcd.cfg.sequence_enabled ) ? 'E' : 'D'; - printf("[%c] S: Enable/Disable sequencer.\n\r", tmp); - tmp = (adcd.cfg.dma_enabled) ? 'E' : 'D'; + tmp = (adcd_cfg.dma_enabled) ? 'E' : 'D'; printf("[%c] D: Enable/Disable to tranfer with DMA.\n\r", tmp); - tmp = (adcd.cfg.power_save_enabled) ? 'E' : 'D'; + tmp = (adcd_cfg.power_save_enabled) ? 'E' : 'D'; printf("[%c] P: Enable/Disable ADC power save mode.\n\r", tmp); printf("=========================================================\n\r"); } static void console_handler(uint8_t key) { - uint32_t i; - switch (key) { case '0' : - adcd.cfg.trigger_mode = TRIGGER_MODE_SOFTWARE; + adcd_cfg.trigger_mode = TRIGGER_MODE_SOFTWARE; + adcd_cfg.trigger_edge = TRIGGER_NO; break; case '1' : - adcd.cfg.trigger_mode = TRIGGER_MODE_ADTRG; + adcd_cfg.trigger_mode = TRIGGER_MODE_ADTRG; + adcd_cfg.trigger_edge = TRIGGER_EXT_TRIG_ANY; break; #ifdef ADC_TRIG_TIOA0 case '2' : - adcd.cfg.trigger_mode = TRIGGER_MODE_TIOA0; + adcd_cfg.trigger_mode = TRIGGER_MODE_TIOA0; + adcd_cfg.trigger_edge = TRIGGER_EXT_TRIG_RISE; break; #endif /* ADC_TRIG_TIOA0 */ case '3' : - adcd.cfg.trigger_mode = TRIGGER_MODE_ADC_TIMER; - break; - - case 's' : - case 'S' : - /* Enable/disable sequencer */ - if (adcd.cfg.sequence_enabled) - adcd.cfg.sequence_enabled = 0; - else - adcd.cfg.sequence_enabled = 1; - - if (adcd.cfg.sequence_enabled) { - /* channel 0 and channel 1 is repeated 2 times.*/ - adcd.cfg.chan_sequence[0] = 0; - adcd.cfg.chan_sequence[1] = 0; - adcd.cfg.chan_sequence[2] = 1; - adcd.cfg.chan_sequence[3] = 1; - } else { - /* Set default sequence */ - adcd.cfg.chan_sequence[0] = 0; - adcd.cfg.chan_sequence[1] = 1; - adcd.cfg.chan_sequence[2] = 2; - adcd.cfg.chan_sequence[3] = 3; - } - for (i = 0; i < NUM_CHANNELS; i++) { - _data.channel[i] = i; - _data.value[i] = 0; - } + adcd_cfg.trigger_mode = TRIGGER_MODE_ADC_TIMER; + adcd_cfg.trigger_edge = TRIGGER_PERIOD; break; case 'd' : case 'D' : - if (adcd.cfg.dma_enabled) - adcd.cfg.dma_enabled = 0; + if (adcd_cfg.dma_enabled) + adcd_cfg.dma_enabled = 0; else - adcd.cfg.dma_enabled = 1; + adcd_cfg.dma_enabled = 1; break; case 'p' : case 'P' : - if (adcd.cfg.power_save_enabled) - adcd.cfg.power_save_enabled = 0; + if (adcd_cfg.power_save_enabled) + adcd_cfg.power_save_enabled = 0; else - adcd.cfg.power_save_enabled = 1; + adcd_cfg.power_save_enabled = 1; break; - default : break; } @@ -341,21 +321,14 @@ static void _configure_tc_trigger(void) static int _adc_callback(void* args, void* arg2) { - int i, j, chan, value; + int i; for (i = 0; i < NUM_CHANNELS; ++i) { - chan = ADC_CHANNEL_NUM_IN_LCDR(adc_buffer[i]); - value = ADC_LAST_DATA_IN_LCDR(adc_buffer[i]); - /* Store value to channel according to sequence table*/ - for (j = 0; j < NUM_CHANNELS; j++) { - if ( _data.channel[j] == chan) { - _data.value[j] = value; - break; - } - } + _data.channel[i] = ADC_CHANNEL_NUM_IN_LCDR(adc_buffer[i]); + _data.value[i] = ADC_LAST_DATA_IN_LCDR(adc_buffer[i]); } - adc_converted = true; + state = STATE_CAPTURED; return 0; } @@ -363,34 +336,116 @@ static int _adc_callback(void* args, void* arg2) * \brief (Re)init config ADC. * */ -static void _adc_start_transfer(void) +static void _adc_configure(void) { struct _callback _cb; + int i; - if (adcd.cfg.trigger_mode == TRIGGER_MODE_SOFTWARE) - adcd.cfg.trigger_edge = TRIGGER_NO; - else if (adcd.cfg.trigger_mode == TRIGGER_MODE_ADTRG) - adcd.cfg.trigger_edge = TRIGGER_EXT_TRIG_ANY; -#ifdef ADC_TRIG_TIOA0 - else if (adcd.cfg.trigger_mode == TRIGGER_MODE_TIOA0) - adcd.cfg.trigger_edge = TRIGGER_EXT_TRIG_RISE; -#endif - else if (adcd.cfg.trigger_mode == TRIGGER_MODE_ADC_TIMER) - adcd.cfg.trigger_edge = TRIGGER_PERIOD; + memcpy(&adcd.cfg, &adcd_cfg, sizeof(adcd_cfg)); + + /* Init channel number and reset values */ + for (i = 0; i < NUM_CHANNELS; i++) { + adcd.cfg.channel_mask |= (1u << adc_channels[i]); + _data.channel[i] = 0; + _data.value[i] = 0; + } -#ifdef ADC_TRIG_TIOA0 - tc_stop(TC0, 0); - if (adcd.cfg.trigger_mode == TRIGGER_MODE_TIOA0) - /* Start the Timer */ - tc_start(TC0, 0); -#endif struct _buffer buf = { .data = (uint8_t*)adc_buffer, .size = NUM_CHANNELS * sizeof(uint16_t), }; callback_set(&_cb, _adc_callback, &adcd); + adcd_initialize(&adcd); adcd_transfer(&adcd, &buf, &_cb); - adcd_wait_transfer(&adcd); + + switch (adcd.cfg.trigger_mode) { + case TRIGGER_MODE_ADTRG: + pio_clear(&pin_trig); + break; + +#ifdef ADC_TRIG_TIOA0 + case TRIGGER_MODE_TIOA0: + _configure_tc_trigger(); + break; +#endif + case TRIGGER_MODE_SOFTWARE: + case TRIGGER_MODE_ADC_TIMER: + default: + break; + } + + state = STATE_CONFIGURED; +} + +static void _adc_start(void) +{ + switch (adcd.cfg.trigger_mode) { + case TRIGGER_MODE_SOFTWARE: + case TRIGGER_MODE_ADTRG: + timer_start_timeout(&adc_timeout, 250); + state = STATE_STARTED; + break; + +#ifdef ADC_TRIG_TIOA0 + case TRIGGER_MODE_TIOA0: + tc_start(TC0, 0); + state = STATE_CAPTURING; + break; +#endif + case TRIGGER_MODE_ADC_TIMER: + default: + state = STATE_CAPTURING; + break; + } + +} + +static void _adc_trigger_capture(void) +{ + switch (adcd.cfg.trigger_mode) { + case TRIGGER_MODE_SOFTWARE: + /* ADC software trigger every 500ms */ + if (timer_timeout_reached(&adc_timeout)) { + state = STATE_CAPTURING; + adc_start_conversion(); + led_toggle(LED_RED); + } + break; + + case TRIGGER_MODE_ADTRG: + /* ADTRG trigger every 500ms */ + if (timer_timeout_reached(&adc_timeout)) { + state = STATE_CAPTURING; + pio_set(&pin_trig); + led_toggle(LED_RED); + } + break; + +#ifdef ADC_TRIG_TIOA0 + case TRIGGER_MODE_TIOA0: +#endif + case TRIGGER_MODE_ADC_TIMER: + default: + break; + } +} + +static void _adc_print_results(void) +{ + int i; + +#ifdef ADC_TRIG_TIOA0 + if (adcd.cfg.trigger_mode == TRIGGER_MODE_TIOA0) + tc_stop(TC0, 0); +#endif + + printf("Count: %08d ", count++); + for (i = 0; i < NUM_CHANNELS; ++i) { + printf(" CH%02d: %04d mV ", _data.channel[i], + (int)(_data.value[i] * BOARD_ADC_VREF / DIGITAL_MAX)); + } + printf("\r"); + state = STATE_WAITING; } /*---------------------------------------------------------------------------- @@ -404,8 +459,6 @@ static void _adc_start_transfer(void) */ int main(void) { - uint8_t i; - /* Configure console interrupts */ console_set_rx_handler(console_handler); console_enable_rx_interrupt(); @@ -415,76 +468,35 @@ int main(void) /* Configure trigger pins */ pio_configure(&pin_trig, 1); - pio_clear(&pin_trig); pio_configure(pin_adtrg, ARRAY_SIZE(pin_adtrg)); - /* Set defaut ADC test mode */ - adcd.cfg.trigger_mode = TRIGGER_MODE_SOFTWARE; - adcd.cfg.trigger_edge = TRIGGER_NO; - - adcd.cfg.dma_enabled = 0; - adcd.cfg.freq = ADC_FREQ; - - /* Set default sequence */ - adcd.cfg.chan_sequence[0] = 0; - adcd.cfg.chan_sequence[1] = 1; - adcd.cfg.chan_sequence[2] = 2; - adcd.cfg.chan_sequence[3] = 3; - - /* Initialize ADC clock */ - adcd_initialize(&adcd); - - /* Launch first timeout */ - struct _timeout timeout; - timer_start_timeout(&timeout, 500); -#ifdef ADC_TRIG_TIOA0 - _configure_tc_trigger(); -#endif - - /* Init channel number and reset value */ - for (i = 0; i < NUM_CHANNELS; i++) { - adcd.cfg.channel_used[i] = adc_channel_used[i]; - _data.channel[i] = adcd.cfg.chan_sequence[i]; - _data.value[i] = 0; - } - - _adc_start_transfer(); + /* Set defaut ADC configuration */ + memset(&adcd_cfg, 0, sizeof(adcd_cfg)); + adcd_cfg.trigger_mode = TRIGGER_MODE_SOFTWARE; + adcd_cfg.trigger_edge = TRIGGER_NO; + adcd_cfg.freq = ADC_FREQ; + memcpy(&adcd.cfg, &adcd_cfg, sizeof(adcd_cfg)); + state = STATE_WAITING; _display_menu(); while (1) { - /* ADC software trigger per 100ms */ - if (adcd.cfg.trigger_mode == TRIGGER_MODE_SOFTWARE) { - if (timer_timeout_reached(&timeout)) { - adc_start_conversion(); - timer_start_timeout(&timeout, 500); - led_toggle(LED_RED); - } - } - if (adcd.cfg.trigger_mode == TRIGGER_MODE_ADTRG) { - msleep(500); - if (pio_get(&pin_trig)) { - led_clear(LED_RED); - pio_clear(&pin_trig); - } else { - pio_set(&pin_trig); - led_set(LED_RED); - } - } - /* Check if ADC sample is done */ - if (adc_converted) { - printf("Count: %08d ", count++); - for (i = 0; i < NUM_CHANNELS; ++i) { - printf(" CH%02d: %04d mV ", - (int)(_data.channel[adcd.cfg.chan_sequence[i]]), - (int)(_data.value[adcd.cfg.chan_sequence[i]] - * BOARD_ADC_VREF / DIGITAL_MAX)); - } - printf("\r"); - adc_converted = false; - _adc_start_transfer(); + switch (state) { + case STATE_WAITING: + _adc_configure(); + break; + case STATE_CONFIGURED: + _adc_start(); + case STATE_STARTED: + _adc_trigger_capture(); + break; + case STATE_CAPTURING: + break; + case STATE_CAPTURED: + _adc_print_results(); + break; } } }