cancel
Showing results for 
Search instead for 
Did you mean: 

STM32L4 ADC + DMA Multiple Channels (> 6 Channels) not working

Hailing SKY
Associate II

Dear All,

I am using the ADC + DMA function in STM32L476 to record data from multiple sensing inputs (12 channels in total). I have succeeded to record 4 channels using ADC1_IN1, IN6, IN7 and IN8, but when I increased the channel number to 6, the results are wrong.

The CH1 data were overwritten by the data from CH6, and there was nothing for the location where data from CH6 should be.

Here are my codes (partial). Please let me know your ideas.

The attached code is for NUCLEO - L476RG board.

#define BUF_SIZE (4000) // data length for each channel
#define CHANNEL_COUNT (4) // change to 6 in the 6-channel case
 
ADC_HandleTypeDef hadc1;
DMA_HandleTypeDef hdma_adc1;
 
/* Private variables ---------------------------------------------------------*/
uint8_t datastore1[BUF_SIZE]={0};
uint8_t datastore2[BUF_SIZE]={0};
uint8_t datastore3[BUF_SIZE]={0};
uint8_t datastore4[BUF_SIZE]={0};
// uint8_t datastore5[BUF_SIZE]={0};
// uint8_t datastore6[BUF_SIZE]={0};
uint8_t adcValue[CHANNEL_COUNT*BUF_SIZE]={0};
uint8_t indi_led = 0;
uint8_t indi_int = 0;
 
/* USER CODE END PV */
/* Private function prototypes -----------------------------------------------*/
 
void SystemClock_Config(void);
static void MX_GPIO_Init(void);
static void MX_DMA_Init(void);
static void MX_USART1_UART_Init(void);
static void MX_ADC1_Init(void);
 
int main(void)
{
 
 /* Reset of all peripherals, Initializes the Flash interface and the Systick. */
 HAL_Init();
 SystemClock_Config();
 
 /* Initialize all configured peripherals */
 MX_GPIO_Init();
 MX_DMA_Init();
 // MX_USART1_UART_Init();
 MX_ADC1_Init();
 
 /* Infinite loop */
 /* USER CODE BEGIN WHILE */
 while (1)
 { // if interruptted, indi_int = 1
 if (indi_int == 1)
 {
 // HAL_NVIC_EnableIRQ(EXTI9_5_IRQn); // PE7 interrupt
 // HAL_Delay(3000);
 indi_int = 0;
 HAL_GPIO_WritePin(GPIOE, GPIO_PIN_8, GPIO_PIN_SET);
 HAL_ADC_Start_DMA(&hadc1,(uint32_t*)adcValue,BUF_SIZE*CHANNEL_COUNT); // start conversion
 HAL_GPIO_WritePin(GPIOE, GPIO_PIN_9, GPIO_PIN_SET);
 }
 
 if (indi_led == 1)
 {
 HAL_UART_Transmit(&huart1,(uint8_t*)datastore1,BUF_SIZE,3000);
 HAL_UART_Transmit(&huart1,(uint8_t*)datastore2,BUF_SIZE,3000);
  HAL_UART_Transmit(&huart1,(uint8_t*)datastore3,BUF_SIZE,3000);
  indi_led = 0;
  HAL_UART_Transmit(&huart1,(uint8_t*)datastore4,BUF_SIZE,3000);
  // HAL_UART_Transmit(&huart1,(uint8_t*)datastore5,BUF_SIZE,3000);
  // HAL_UART_Transmit(&huart1,(uint8_t*)datastore6,BUF_SIZE,3000);
 HAL_GPIO_WritePin(GPIOE, GPIO_PIN_9, GPIO_PIN_RESET);	
 HAL_GPIO_WritePin(GPIOE, GPIO_PIN_8, GPIO_PIN_RESET);
 indi_int = 0;
 HAL_NVIC_EnableIRQ(EXTI9_5_IRQn);
 }
 }
}
 
// transfer data from adcValue to the varibles for each channel
void HAL_ADC_ConvHalfCpltCallback(ADC_HandleTypeDef* hadc)
{
	uint16_t tem_index=0;
	uint16_t data_num=0;
	for(tem_index=0;tem_index<BUF_SIZE/2;tem_index++)
	{
 //HAL_GPIO_WritePin(GPIOE, GPIO_PIN_8, GPIO_PIN_SET);
 data_num=tem_index*4; // 6 for 6-channel
 datastore1[tem_index]=adcValue[data_num];
 datastore2[tem_index]=adcValue[data_num+1];
 datastore3[tem_index]=adcValue[data_num+2];
 datastore4[tem_index]=adcValue[data_num+3];
 //datastore5[tem_index]=adcValue[data_num+4];
 //datastore6[tem_index]=adcValue[data_num+5];
	}
}
 
void HAL_ADC_ConvCpltCallback(ADC_HandleTypeDef* hadc)
{
	uint16_t tem_index=BUF_SIZE/2;
	uint16_t data_num=0;
	for(tem_index=BUF_SIZE/2;tem_index<BUF_SIZE;tem_index++)
	{
 data_num=tem_index*4; // 6 for 6-channel
 datastore1[tem_index]=adcValue[data_num];
 datastore2[tem_index]=adcValue[data_num+1];
 datastore3[tem_index]=adcValue[data_num+2];
 datastore4[tem_index]=adcValue[data_num+3];
 //datastore5[tem_index]=adcValue[data_num+4];
 //datastore6[tem_index]=adcValue[data_num+5];
 //HAL_GPIO_WritePin(GPIOA, GPIO_PIN_5, GPIO_PIN_SET); 
 HAL_ADC_Stop_DMA(&hadc1);
 indi_led = 1;
	}
}
 
/* ADC1 init function */
static void MX_ADC1_Init(void)
{
 ADC_MultiModeTypeDef multimode;
 ADC_ChannelConfTypeDef sConfig;
 
  /**Common config 
  */
 hadc1.Instance = ADC1;
 hadc1.Init.ClockPrescaler = ADC_CLOCK_ASYNC_DIV1;
 hadc1.Init.Resolution = ADC_RESOLUTION_8B;
 hadc1.Init.DataAlign = ADC_DATAALIGN_RIGHT;
 hadc1.Init.ScanConvMode = ADC_SCAN_ENABLE;
 hadc1.Init.EOCSelection = ADC_EOC_SINGLE_CONV;
 hadc1.Init.LowPowerAutoWait = DISABLE;
 hadc1.Init.ContinuousConvMode = ENABLE;
 hadc1.Init.NbrOfConversion = 4; // change to 6 for the 6-channel case
 hadc1.Init.DiscontinuousConvMode = DISABLE;
 hadc1.Init.NbrOfDiscConversion = 1;
 hadc1.Init.ExternalTrigConv = ADC_SOFTWARE_START;
 hadc1.Init.ExternalTrigConvEdge = ADC_EXTERNALTRIGCONVEDGE_NONE;
 hadc1.Init.DMAContinuousRequests = ENABLE;
 hadc1.Init.Overrun = ADC_OVR_DATA_PRESERVED;
 hadc1.Init.OversamplingMode = DISABLE;
 
 if (HAL_ADC_Init(&hadc1) != HAL_OK)
 {
  _Error_Handler(__FILE__, __LINE__);
 }
 
  /**Configure the ADC multi-mode 
  */
 multimode.Mode = ADC_MODE_INDEPENDENT;
 if (HAL_ADCEx_MultiModeConfigChannel(&hadc1, &multimode) != HAL_OK)
 {
  _Error_Handler(__FILE__, __LINE__);
 }
 
  /**Configure Regular Channel 
  */
 sConfig.Channel = ADC_CHANNEL_1;
 sConfig.Rank = 1;
 sConfig.SamplingTime = ADC_SAMPLETIME_24CYCLES_5;
 sConfig.SingleDiff = ADC_SINGLE_ENDED;
 sConfig.OffsetNumber = ADC_OFFSET_NONE;
 sConfig.Offset = 0;
 if (HAL_ADC_ConfigChannel(&hadc1, &sConfig) != HAL_OK)
 {
  _Error_Handler(__FILE__, __LINE__);
 }
 
  /**Configure Regular Channel 
  */
 sConfig.Channel = ADC_CHANNEL_6;
 sConfig.Rank = 2;
 if (HAL_ADC_ConfigChannel(&hadc1, &sConfig) != HAL_OK)
 {
  _Error_Handler(__FILE__, __LINE__);
 }
 
  /**Configure Regular Channel 
  */
 sConfig.Channel = ADC_CHANNEL_7;
 sConfig.Rank = 3;
 if (HAL_ADC_ConfigChannel(&hadc1, &sConfig) != HAL_OK)
 {
  _Error_Handler(__FILE__, __LINE__);
 }
 
   /**Configure Regular Channel 
  */
 sConfig.Channel = ADC_CHANNEL_8;
 sConfig.Rank = 4;
 if (HAL_ADC_ConfigChannel(&hadc1, &sConfig) != HAL_OK)
 {
  _Error_Handler(__FILE__, __LINE__);
 }
 
  /**Configure Regular Channel 
  */
 //sConfig.Channel = ADC_CHANNEL_9;
 //sConfig.Rank = 5;
// if (HAL_ADC_ConfigChannel(&hadc1, &sConfig) != HAL_OK)
 //{
 // _Error_Handler(__FILE__, __LINE__);
 //}
 
  /**Configure Regular Channel 
  */
// sConfig.Channel = ADC_CHANNEL_10;
// sConfig.Rank = 6;
// if (HAL_ADC_ConfigChannel(&hadc1, &sConfig) != HAL_OK)
// {
 // _Error_Handler(__FILE__, __LINE__);
 //}
}

32 REPLIES 32
Hailing SKY
Associate II

This figure shows the 6-channel results. The results from ch1 and ch6 are wrong. I compared with an oscilloscope. Do not know the reason.

Ch 1 was correct in the 4-channel test.

I checked my code and results again. I found ch2 - ch4 are great,

but the data from Ch 1 were overwritten by the data from Ch 6.

0690X000006BtijQAC.jpg

Byte wide DMA doesn't look right.

Tips, buy me a coffee, or three.. PayPal Venmo Up vote any posts that you find helpful, it shows what's working..
Hailing SKY
Associate II

@Community member​  Thanks for replying. I am not quite sure the data transfer mechanism between ADC to memory by DMA.

Would you please tell me more about how to configurate the MCU to achieve this 12-channel goal?

Hailing SKY
Associate II

@Community member​ This is the features I found about DMA.

All DMA channels independently configurable:

– Transfer size of source and destination are independent (byte, half-word, word), emulating packing and unpacking. Source and destination addresses must be aligned on the data size.

– Programmable number of data to be transferred: 0 to 2^16 - 1

Hailing SKY
Associate II

I checked my code and results again. I found ch2 - ch4 are great,

but the data from Ch 1 were overwritten by the data from Ch 6.

In the variable for Ch 6, there is nothing. I think something is wrong here:

HAL_ADC_Start_DMA(&hadc1,(uint32_t*)adcValue,BUF_SIZE*CHANNEL_COUNT); // start conversion

I am still trying to find out why.

Hailing SKY
Associate II

Seems like no one has such experiences before.​

A lack of constituency is one issue I suppose. I can't afford the time to get deeply involved in CubeMX/HAL issues over hundreds of board/processor combinations.

It would surprise me if the the chip implementation were broken. So either a software issue or misconfiguring the hardware plumbing.

For commercial projects consider pushing on the FAE supporting your account, or consultancy options.

Tips, buy me a coffee, or three.. PayPal Venmo Up vote any posts that you find helpful, it shows what's working..
T J
Lead

I checked my code, you init is slightly different...

although, I was surprised to see the HalfCallback, I think you should remove that one...

static void MX_ADC_Init(void)
{
 
  ADC_ChannelConfTypeDef sConfig;
 
    /**Configure the global features of the ADC (Clock, Resolution, Data Alignment and number of conversion) 
    */
  hadc.Instance = ADC1;
  hadc.Init.ClockPrescaler = ADC_CLOCK_ASYNC_DIV1;
  hadc.Init.Resolution = ADC_RESOLUTION_12B;
  hadc.Init.DataAlign = ADC_DATAALIGN_RIGHT;
  hadc.Init.ScanConvMode = ADC_SCAN_DIRECTION_FORWARD;
  hadc.Init.EOCSelection = ADC_EOC_SEQ_CONV;
  hadc.Init.LowPowerAutoWait = DISABLE;
  hadc.Init.LowPowerAutoPowerOff = DISABLE;
  hadc.Init.ContinuousConvMode = DISABLE;
  hadc.Init.DiscontinuousConvMode = DISABLE;
  hadc.Init.ExternalTrigConv = ADC_EXTERNALTRIGCONV_T3_TRGO;
  hadc.Init.ExternalTrigConvEdge = ADC_EXTERNALTRIGCONVEDGE_RISING;
  hadc.Init.DMAContinuousRequests = ENABLE;
  hadc.Init.Overrun = ADC_OVR_DATA_PRESERVED;
  if (HAL_ADC_Init(&hadc) != HAL_OK)
  {
    _Error_Handler(__FILE__, __LINE__);
  }
 
    /**Configure for the selected ADC regular channel to be converted. 
    */
  sConfig.Channel = ADC_CHANNEL_1;
  sConfig.Rank = ADC_RANK_CHANNEL_NUMBER;
  sConfig.SamplingTime = ADC_SAMPLETIME_239CYCLES_5;
  if (HAL_ADC_ConfigChannel(&hadc, &sConfig) != HAL_OK)
  {
    _Error_Handler(__FILE__, __LINE__);
  }
 
    /**Configure for the selected ADC regular channel to be converted. 
    */
  sConfig.Channel = ADC_CHANNEL_9;
  if (HAL_ADC_ConfigChannel(&hadc, &sConfig) != HAL_OK)
  {
    _Error_Handler(__FILE__, __LINE__);
  }
 
    /**Configure for the selected ADC regular channel to be converted. 
    */
  sConfig.Channel = ADC_CHANNEL_TEMPSENSOR;
  if (HAL_ADC_ConfigChannel(&hadc, &sConfig) != HAL_OK)
  {
    _Error_Handler(__FILE__, __LINE__);
  }
 
    /**Configure for the selected ADC regular channel to be converted. 
    */
  sConfig.Channel = ADC_CHANNEL_VREFINT;
  if (HAL_ADC_ConfigChannel(&hadc, &sConfig) != HAL_OK)
  {
    _Error_Handler(__FILE__, __LINE__);
  }
 
}

T J
Lead

I created a Table for all the ADC/DMA results to come in..

I made it 16 rows high, so I have time to get around .

char ReCalADCrequested,, ADC_DMA_Wait, showLocalADCs;
char  ADC1IRQFlag, ADChasBeenRecalibrated;
char  HAL_ADC_CompleteCycle;
uint8_t ADC_RowCounter, Ave_ADC_ChannelCounter, ADC_ChannelsCounter;
 
#define ADC_RowCount 16
uint16_t ADC_DMABuffer[ADC_ChannelCount];   // 18x 12bit words for different channels
uint16_t ADC_Channel_AVE[ADC_ChannelCount];
uint16_t HAL_ADC_Channel_Buffer[ADC_RowCount][ADC_ChannelCount];   	// 16 to sum to calc averages for each channel
 
I calculate the ADC_ChannelCount  needed with ENUM
typedef enum {
    AD1,
    AD9,
    VTemp_Ad16,
    VRef_Ad17,
    //VBat_Ad18,              // not used on F042 
    ADC_ChannelCount,     //   <- here
} ADCresultsAveColumn_t;