Using F401 ADC continuous conversion and DMA for multiple channels.
I have pins A0-7, B1, PC0-5 running conversions. For consecutive pins in a group (A0-7) (C0-5), the final pin in use always has a different offset error to the rest. Note this actually means the final /used/ pin in the group, e.g. If all 8 pins in group A are used, pinA7 has a different offset error to the rest. If only the first three 3 are used pinA2 has a different offset error to the rest. If the first 4 pins are used pinA2 no longer has the additional offset error but this is passed on to pinA3.
These results are conversions from all pins being used. The ADC inputs are grounded with pull down resistors in all cases. The different sets of values (0-4 vs18- 25) represent different input impedances from input circuits. The ADC is 12 bits so the offset errors aren't particularly problematic, the issue for me is what could be causing the last value to have a different offset error?
I've measured the pins with my oscilloscope, the B and C group are at 0 volts and the A group are approximately 20mv. I think the issue is in firmware but it's mysterious to me.
My ADC initialization code;
/* Function:
* Initializes the adc modules one by one.
*
*
* Arguments:
* Struct that gives access to arrays describing initialization parameters.
*
* Returns:
* None.
* */
void ADC_Init(local_config* peripheral_config){
set_adc_order(peripheral_config);
uint16_t* ADC_Values = peripheral_config->ADC_Values;
uint8_t* adc_rank = peripheral_config->adc_rank;
uint8_t* input_array = peripheral_config->adc_input_array;
uint8_t num_cov = peripheral_config->adc_num_conv;
/* DMA controller clock enable */
__HAL_RCC_DMA2_CLK_ENABLE();
/* DMA interrupt init */
/* DMA2_Stream0_IRQn interrupt configuration */
HAL_NVIC_SetPriority(DMA2_Stream0_IRQn, 0, 0);
HAL_NVIC_EnableIRQ(DMA2_Stream0_IRQn);
/*
* DMA Init End
****************************************************************/
/*
* ADC Init Start
****************************************************************/
/** Configure the global features of the ADC (Clock, Resolution, Data Alignment and number of conversion)
*/
hadc1.Instance = ADC1;
hadc1.Init.ClockPrescaler = ADC_CLOCK_SYNC_PCLK_DIV2;
hadc1.Init.Resolution = ADC_RESOLUTION_12B;
hadc1.Init.ScanConvMode = ENABLE;
hadc1.Init.ContinuousConvMode = ENABLE;
hadc1.Init.DiscontinuousConvMode = DISABLE;
hadc1.Init.ExternalTrigConvEdge = ADC_EXTERNALTRIGCONVEDGE_NONE;
hadc1.Init.ExternalTrigConv = ADC_SOFTWARE_START;
hadc1.Init.DataAlign = ADC_DATAALIGN_RIGHT;
hadc1.Init.NbrOfConversion = num_cov;
hadc1.Init.DMAContinuousRequests = ENABLE;
hadc1.Init.EOCSelection = ADC_EOC_SEQ_CONV;
if (HAL_ADC_Init(&hadc1) != HAL_OK)
{
Error_Handler();
}
/*
* ADC Init End
****************************************************************/
/*
* MSP Init Start
****************************************************************/
/* Peripheral clock enable */
__HAL_RCC_ADC1_CLK_ENABLE();
__HAL_RCC_GPIOA_CLK_ENABLE();
/*
* It appears GPIO pins have to be initialized together per PORTA
*
* Need to figure out what to do about other ports
*/
GPIO_InitTypeDef GPIO_InitStruct = {0};
GPIO_InitStruct.Pin = 0;
for(int index = 0; index < 8; index++){
if(input_array[index]){
GPIO_InitStruct.Pin = GPIO_InitStruct.Pin|ADC_DI_PINS[index];
}
}
if(GPIO_InitStruct.Pin != 0) {
GPIO_InitStruct.Mode = GPIO_MODE_ANALOG;
GPIO_InitStruct.Pull = GPIO_NOPULL;
HAL_GPIO_Init(GPIOA, &GPIO_InitStruct);
}
/* Feedback pins GPIO init ***************************************************/
GPIO_InitStruct.Pin = FB1_Pin;
GPIO_InitStruct.Mode = GPIO_MODE_ANALOG;
GPIO_InitStruct.Pull = GPIO_NOPULL;
HAL_GPIO_Init(FB1_GPIO_Port, &GPIO_InitStruct);
GPIO_InitStruct.Pin = FB2_Pin|FB3_Pin|FB4_Pin|FB5_Pin
|FB6_Pin|FB7_Pin;
GPIO_InitStruct.Mode = GPIO_MODE_ANALOG;
GPIO_InitStruct.Pull = GPIO_NOPULL;
HAL_GPIO_Init(FB2_GPIO_Port, &GPIO_InitStruct);
/* Feedback pins GPIO init ***************************************************/
/* ADC1 DMA Init */
hdma_adc1.Instance = DMA2_Stream0;
hdma_adc1.Init.Channel = DMA_CHANNEL_0;
hdma_adc1.Init.Direction = DMA_PERIPH_TO_MEMORY;
hdma_adc1.Init.PeriphInc = DMA_PINC_DISABLE;
hdma_adc1.Init.MemInc = DMA_MINC_ENABLE;
hdma_adc1.Init.PeriphDataAlignment = DMA_PDATAALIGN_HALFWORD;
hdma_adc1.Init.MemDataAlignment = DMA_MDATAALIGN_HALFWORD; //DMA_MDATAALIGN_WORD
hdma_adc1.Init.Mode = DMA_CIRCULAR;
hdma_adc1.Init.Priority = DMA_PRIORITY_LOW;
hdma_adc1.Init.FIFOMode = DMA_FIFOMODE_DISABLE;
if (HAL_DMA_Init(&hdma_adc1) != HAL_OK)
{
Error_Handler();
}
__HAL_LINKDMA(&hadc1, DMA_Handle, hdma_adc1);
/* ADC1 Interrupt Enable */
HAL_NVIC_SetPriority(ADC_IRQn, 0, 0);
HAL_NVIC_EnableIRQ(ADC_IRQn);
/*
* MSP Init End
****************************************************************/
/*
* ADC Channel Rank Start
****************************************************************/
/*
* Configuration of ADC Channels
*
* Discontinuous ranks are not allowed.
* First loop assigns the rank to customizable input pins.
*
* Second loop assigns rank for feedback pins.
*/
ADC_ChannelConfTypeDef sConfig = {0};
for(int index = 0; index < 8; index++){
if(input_array[index] && ((adc_rank[index] >= 0) && (adc_rank[index] <= 7))) {
sConfig.Channel = ADC_CHANNELS[index];
sConfig.Rank = adc_rank[index];
sConfig.SamplingTime = ADC_SAMPLETIME_28CYCLES;
if (HAL_ADC_ConfigChannel(&hadc1, &sConfig) != HAL_OK) Error_Handler();
}
}
for(int index = 8; index < 15; index++) {
if((adc_rank[index] >= 0) && (adc_rank[index] <= 14)) {
sConfig.Channel = ADC_CHANNELS[index];
sConfig.Rank = adc_rank[index];
sConfig.SamplingTime = ADC_SAMPLETIME_28CYCLES;
if (HAL_ADC_ConfigChannel(&hadc1, &sConfig) != HAL_OK) Error_Handler();
}
}
/*
* ADC Channel Rank End
****************************************************************/
/*
* Starting ADC,
*/
if(HAL_ADC_Start_DMA(&hadc1, (uint32_t*) ADC_Values, num_cov) != HAL_OK) Error_Handler();
}