I've implemented an ADC scan using two DMA controllers as per the application notes. What I'm seeing is inconsistent performance. There is a lot of jitter but the main issue is the results can end up in the wrong places. The results get written to the incorrect memory locations, they slip by one.
The DMA is set up to do each ADC channel three times, ie. 1 2 3 1 2 3 1 2 3 and then I use a median filter on the values when I use them as the inputs for a compensator.
What I am seeing is that sometimes it is 1 2 3 1 2 3 1 2 3 but then it can slip and be 2 3 1 2 3 1 2 3 1.
What have I missed. There is another loop that calls the median filter and scaling and compenstation routines, the ADC loop is meant to run in the background with little CPU interaction.
#include "adc.h"
#define ADC_TEST
#define ADC_PERIOD 10000U
#define ADC16_BASE ADC0
#define ADC16_CHANNEL_GROUP 0U
#define ADC_VIN 2
#define ADC_VBATT 0
#define ADC_IMON 1
#define B_SIZE 9u
#define CHANNELS 3u
uint8_t ADC_mux[CHANNELS] =
{ ADC_VIN, ADC_VBATT, ADC_IMON };
uint16_t ADC0_resultBuffer[B_SIZE] = { 0 };
#define DMAChannel_0 0u
#define DMAChannel_1 1u
edma_handle_t g_EDMA_Handle_0;
edma_handle_t g_EDMA_Handle_1;
static inline uint16_t median_ADC_sample(uint16_t a, uint16_t b, uint16_t c)
{
uint16_t min, max;
min = MIN(a, b);
min = MIN(min, c);
max = MAX(a, b);
max = MAX(max, c);
return (a + b + c - min - max);
}
void get_ADC_raw(ctrl_param_t *ctrl)
{
ctrl->meas.IMON_raw = median_ADC_sample(ADC0_resultBuffer[0], ADC0_resultBuffer[3], ADC0_resultBuffer[6]);
ctrl->meas.VBus_raw = median_ADC_sample(ADC0_resultBuffer[1], ADC0_resultBuffer[4], ADC0_resultBuffer[7]);
ctrl->meas.VBatt_raw = median_ADC_sample(ADC0_resultBuffer[2], ADC0_resultBuffer[5], ADC0_resultBuffer[8]);
}
volatile uint8_t toggle = 0;
void EDMA_Callback_1(edma_handle_t *handle, void *param, bool transferDone, uint32_t tcds)
{
if (transferDone)
{
EDMA_StartTransfer(&g_EDMA_Handle_1);
}
}
#ifdef ADC_TEST
void ADC0_IRQHandler(void)
{
if(toggle == 0)
{
toggle = 1;
}
else
{
toggle = 0;
}
GPIO_PinWrite(BOARD_DIODE_CONTROL_GPIO, BOARD_DIODE_CONTROL_GPIO_PIN, toggle);
}
#endif
int init_ADC_DMA()
{
adc16_config_t adc16ConfigStruct;
adc16_channel_config_t adc16ChannelConfigStruct;
ADC16_GetDefaultConfig(&adc16ConfigStruct);
adc16ConfigStruct.referenceVoltageSource = kADC16_ReferenceVoltageSourceVref;
adc16ConfigStruct.clockSource = kADC16_ClockSourceAlt0;
adc16ConfigStruct.enableAsynchronousClock = false;
adc16ConfigStruct.clockDivider = kADC16_ClockDivider2;
adc16ConfigStruct.resolution = kADC16_ResolutionSE12Bit;
adc16ConfigStruct.longSampleMode = kADC16_LongSampleDisabled;
adc16ConfigStruct.enableHighSpeed = true;
adc16ConfigStruct.enableLowPower = false;
adc16ConfigStruct.enableContinuousConversion = false;
ADC16_Init( ADC0, &adc16ConfigStruct);
ADC16_DoAutoCalibration( ADC0 );
ADC16_EnableHardwareTrigger( ADC0, true);
ADC16_EnableDMA( ADC0, true);
adc16ChannelConfigStruct.channelNumber = ADC_mux[1];
adc16ChannelConfigStruct.enableDifferentialConversion = false;
#ifdef ADC_TEST
EnableIRQ(ADC0_IRQn);
adc16ChannelConfigStruct.enableInterruptOnConversionCompleted = true;
#else
adc16ChannelConfigStruct.enableInterruptOnConversionCompleted = false;
#endif
ADC16_SetChannelConfig( ADC0, 0, &adc16ChannelConfigStruct);
ADC16_SetHardwareAverage(ADC0, kADC16_HardwareAverageDisabled);
SIM->SOPT7 = 0X84;
pit_config_t pitConfig;
PIT_GetDefaultConfig(&pitConfig);
PIT_Init(PIT, &pitConfig);
PIT_SetTimerPeriod(PIT, kPIT_Chnl_0, USEC_TO_COUNT(200U, CLOCK_GetFreq(kCLOCK_BusClk)));
edma_transfer_config_t transferConfig_ch0;
edma_transfer_config_t transferConfig_ch1;
edma_config_t userConfig;
DMAMUX_Init(DMAMUX0);
DMAMUX_SetSource(DMAMUX0, DMAChannel_0, 60);
DMAMUX_EnableChannel(DMAMUX0, DMAChannel_0);
DMAMUX_SetSource(DMAMUX0, DMAChannel_1, 40);
DMAMUX_EnableChannel(DMAMUX0, DMAChannel_1);
EDMA_GetDefaultConfig(&userConfig);
userConfig.enableHaltOnError = false;
EDMA_Init(DMA0, &userConfig);
EDMA_CreateHandle(&g_EDMA_Handle_1, DMA0, DMAChannel_1);
EDMA_SetCallback(&g_EDMA_Handle_1, EDMA_Callback_1, NULL);
EDMA_PrepareTransfer(&transferConfig_ch1,
(uint32_t*) (ADC0->R),
sizeof(uint16_t),
ADC0_resultBuffer,
sizeof(ADC0_resultBuffer[0]),
sizeof(uint16_t),
B_SIZE * 2,
kEDMA_PeripheralToMemory);
EDMA_SubmitTransfer(&g_EDMA_Handle_1, &transferConfig_ch1);
EDMA_SetChannelLink(DMA0, DMAChannel_1, kEDMA_MinorLink, DMAChannel_0);
EDMA_SetChannelLink(DMA0, DMAChannel_1, kEDMA_MajorLink, DMAChannel_0);
EDMA_CreateHandle(&g_EDMA_Handle_0, DMA0, DMAChannel_0);
EDMA_PrepareTransfer(&transferConfig_ch0,
&ADC_mux[0],
sizeof(ADC_mux[0]),
(uint32_t*)(ADC0->SC1),
sizeof(uint8_t),
sizeof(uint8_t),
CHANNELS,
kEDMA_MemoryToPeripheral);
EDMA_SubmitTransfer(&g_EDMA_Handle_0, &transferConfig_ch0);
DMA0->TCD[0].SLAST = -1 * CHANNELS;
DMA0->TCD[1].DLAST_SGA = -2 * B_SIZE;
EDMA_StartTransfer(&g_EDMA_Handle_1);
NVIC_SetPriority(DMA0_IRQn, 0);
PIT_StartTimer(PIT, kPIT_Chnl_0);
return 1;
}