Skip to content

Commit

Permalink
drivers/analog: fix several issues in ADC driver
Browse files Browse the repository at this point in the history
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 <loic.lefort@microchip.com>
  • Loading branch information
loiclefort committed Oct 5, 2017
1 parent 1277c40 commit 70a39c9
Show file tree
Hide file tree
Showing 6 changed files with 299 additions and 285 deletions.
20 changes: 20 additions & 0 deletions drivers/analog/adc.c
Original file line number Diff line number Diff line change
Expand Up @@ -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.
*
Expand Down
68 changes: 26 additions & 42 deletions drivers/analog/adc.h
Original file line number Diff line number Diff line change
Expand Up @@ -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
*------------------------------------------------------------------------------*/
Expand Down Expand Up @@ -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.
*
Expand Down
133 changes: 66 additions & 67 deletions drivers/analog/adcd.c
Original file line number Diff line number Diff line change
Expand Up @@ -41,12 +41,6 @@
#include "peripherals/pmc.h"
#include "trace.h"

/*----------------------------------------------------------------------------
* Local variables
*----------------------------------------------------------------------------*/

volatile static bool single_transfer_ready;

/*----------------------------------------------------------------------------
* Local functions
*----------------------------------------------------------------------------*/
Expand All @@ -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;
Expand All @@ -72,67 +65,53 @@ 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);
}

/**
* \brief Interrupt handler for the ADC.
*/
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);
}

/**
Expand All @@ -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);

Expand All @@ -152,33 +130,24 @@ 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)
adc_set_sleep_mode(true);
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:
Expand Down Expand Up @@ -219,34 +188,61 @@ 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
}
}
}

/*----------------------------------------------------------------------------
* 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:
Expand All @@ -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);
}

Expand Down Expand Up @@ -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();
}
}
Loading

0 comments on commit 70a39c9

Please sign in to comment.