cancel
Showing results for 
Search instead for 
Did you mean: 

Problem with triple adc Regular simultaneous mode with DMA

VRakc.1
Associate II

Hi,

I am trying to set up simple example project with DMA and triple adc working together. What I am getting is that DMA interrupt is only activated once - I verified all the data is trasfered as supposed to. But, next time I start ADC in the for loop, data is being converted (I can see that in debugger) but the DMA interrupt is not appearing. Part of the code in main: (whole code example available on github: https://github.com/VasilyRakche/triple_adc_dma )

/* USER CODE BEGIN PV */
uint16_t adc_data[3];
volatile uint8_t adc_data_ready_flag;
/* USER CODE END PV */
 
/* Private function prototypes -----------------------------------------------*/
void SystemClock_Config(void);
/* USER CODE BEGIN PFP */
 
/* USER CODE END PFP */
 
/* Private user code ---------------------------------------------------------*/
/* USER CODE BEGIN 0 */
 
/* USER CODE END 0 */
 
/**
  * @brief  The application entry point.
  * @retval int
  */
int main(void)
{
  /* USER CODE BEGIN 1 */
 
  /* USER CODE END 1 */
 
  /* MCU Configuration--------------------------------------------------------*/
 
  /* Reset of all peripherals, Initializes the Flash interface and the Systick. */
  HAL_Init();
 
  /* USER CODE BEGIN Init */
 
  /* USER CODE END Init */
 
  /* Configure the system clock */
  SystemClock_Config();
 
  /* USER CODE BEGIN SysInit */
 
  /* USER CODE END SysInit */
 
  /* Initialize all configured peripherals */
  MX_GPIO_Init();
  MX_DMA_Init();
  MX_ADC1_Init();
  MX_ADC2_Init();
  MX_ADC3_Init();
  MX_TIM1_Init();
  /* USER CODE BEGIN 2 */
  //Enable ADC and DMA
  if(HAL_ADC_Start_IT(&hadc1)!= HAL_OK)
  {
	  Error_Handler();
  }
  if(HAL_ADC_Start(&hadc2)!= HAL_OK)
  {
	  Error_Handler();
  }
  if(HAL_ADC_Start(&hadc3)!= HAL_OK)
  {
	  Error_Handler();
  }
  HAL_Delay(200);
  if(HAL_DMA_Start_IT(&hdma_adc1, (uint32_t)&ADC_COMMON_REGISTER(hadc1)->CDR, (uint32_t)adc_data, 3)!= HAL_OK)	//TODO: interrupt is not necessery here
  {
	  Error_Handler();
  }
 
  for(int i=0; i<5; i++)
  {
		adc_data_ready_flag = 0;        // clear flag
		HAL_ADC_Start(&hadc1);
 
		while (!adc_data_ready_flag); // wait for new adc data
		// First time HAL_ADC_Start(&hadc1); is called
		// data successfully received and flag updated
		// next times hangs in while loop
 
  }

DMA configuration:

/* ADC1 DMA Init */
    /* ADC1 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;
    hdma_adc1.Init.Mode = DMA_CIRCULAR;
    hdma_adc1.Init.Priority = DMA_PRIORITY_HIGH;
    hdma_adc1.Init.FIFOMode = DMA_FIFOMODE_DISABLE;

ADC initialisation:

void MX_ADC1_Init(void)
{
 
  /* USER CODE BEGIN ADC1_Init 0 */
 
  /* USER CODE END ADC1_Init 0 */
 
  ADC_MultiModeTypeDef multimode = {0};
  ADC_ChannelConfTypeDef sConfig = {0};
 
  /* USER CODE BEGIN ADC1_Init 1 */
 
  /* USER CODE END ADC1_Init 1 */
  /** 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_DIV4;
  hadc1.Init.Resolution = ADC_RESOLUTION_12B;
  hadc1.Init.ScanConvMode = DISABLE;
  hadc1.Init.ContinuousConvMode = DISABLE;
  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 = 1;
  hadc1.Init.DMAContinuousRequests = DISABLE;
  hadc1.Init.EOCSelection = ADC_EOC_SINGLE_CONV;
  if (HAL_ADC_Init(&hadc1) != HAL_OK)
  {
    Error_Handler();
  }
  /** Configure the ADC multi-mode
  */
  multimode.Mode = ADC_TRIPLEMODE_REGSIMULT;
  multimode.DMAAccessMode = ADC_DMAACCESSMODE_1;
  multimode.TwoSamplingDelay = ADC_TWOSAMPLINGDELAY_5CYCLES;
  if (HAL_ADCEx_MultiModeConfigChannel(&hadc1, &multimode) != HAL_OK)
  {
    Error_Handler();
  }
  /** Configure for the selected ADC regular channel its corresponding rank in the sequencer and its sample time.
  */
  sConfig.Channel = ADC_CHANNEL_10;
  sConfig.Rank = 1;
  sConfig.SamplingTime = ADC_SAMPLETIME_3CYCLES;
  if (HAL_ADC_ConfigChannel(&hadc1, &sConfig) != HAL_OK)
  {
    Error_Handler();
  }
  /* USER CODE BEGIN ADC1_Init 2 */
 
  /* USER CODE END ADC1_Init 2 */
 
}
/* ADC2 init function */
void MX_ADC2_Init(void)
{
 
  /* USER CODE BEGIN ADC2_Init 0 */
 
  /* USER CODE END ADC2_Init 0 */
 
  ADC_ChannelConfTypeDef sConfig = {0};
 
  /* USER CODE BEGIN ADC2_Init 1 */
 
  /* USER CODE END ADC2_Init 1 */
  /** Configure the global features of the ADC (Clock, Resolution, Data Alignment and number of conversion)
  */
  hadc2.Instance = ADC2;
  hadc2.Init.ClockPrescaler = ADC_CLOCK_SYNC_PCLK_DIV4;
  hadc2.Init.Resolution = ADC_RESOLUTION_12B;
  hadc2.Init.ScanConvMode = DISABLE;
  hadc2.Init.ContinuousConvMode = DISABLE;
  hadc2.Init.DiscontinuousConvMode = DISABLE;
  hadc2.Init.DataAlign = ADC_DATAALIGN_RIGHT;
  hadc2.Init.NbrOfConversion = 1;
  hadc2.Init.DMAContinuousRequests = DISABLE;
  hadc2.Init.EOCSelection = ADC_EOC_SINGLE_CONV;
  if (HAL_ADC_Init(&hadc2) != HAL_OK)
  {
    Error_Handler();
  }
  /** Configure for the selected ADC regular channel its corresponding rank in the sequencer and its sample time.
  */
  sConfig.Channel = ADC_CHANNEL_11;
  sConfig.Rank = 1;
  sConfig.SamplingTime = ADC_SAMPLETIME_3CYCLES;
  if (HAL_ADC_ConfigChannel(&hadc2, &sConfig) != HAL_OK)
  {
    Error_Handler();
  }
  /* USER CODE BEGIN ADC2_Init 2 */
 
  /* USER CODE END ADC2_Init 2 */
 
}
/* ADC3 init function */
void MX_ADC3_Init(void)
{
 
  /* USER CODE BEGIN ADC3_Init 0 */
 
  /* USER CODE END ADC3_Init 0 */
 
  ADC_ChannelConfTypeDef sConfig = {0};
 
  /* USER CODE BEGIN ADC3_Init 1 */
 
  /* USER CODE END ADC3_Init 1 */
  /** Configure the global features of the ADC (Clock, Resolution, Data Alignment and number of conversion)
  */
  hadc3.Instance = ADC3;
  hadc3.Init.ClockPrescaler = ADC_CLOCK_SYNC_PCLK_DIV4;
  hadc3.Init.Resolution = ADC_RESOLUTION_12B;
  hadc3.Init.ScanConvMode = DISABLE;
  hadc3.Init.ContinuousConvMode = DISABLE;
  hadc3.Init.DiscontinuousConvMode = DISABLE;
  hadc3.Init.DataAlign = ADC_DATAALIGN_RIGHT;
  hadc3.Init.NbrOfConversion = 1;
  hadc3.Init.DMAContinuousRequests = DISABLE;
  hadc3.Init.EOCSelection = ADC_EOC_SINGLE_CONV;
  if (HAL_ADC_Init(&hadc3) != HAL_OK)
  {
    Error_Handler();
  }
  /** Configure for the selected ADC regular channel its corresponding rank in the sequencer and its sample time.
  */
  sConfig.Channel = ADC_CHANNEL_0;
  sConfig.Rank = 1;
  sConfig.SamplingTime = ADC_SAMPLETIME_3CYCLES;
  if (HAL_ADC_ConfigChannel(&hadc3, &sConfig) != HAL_OK)
  {
    Error_Handler();
  }
  /* USER CODE BEGIN ADC3_Init 2 */
 
  /* USER CODE END ADC3_Init 2 */
 
}

Whole code example availble on github: https://github.com/VasilyRakche/triple_adc_dma

What can be wrong?

4 REPLIES 4
TDK
Guru

HAL_DMA_Start_IT shouldn't be called directly. It is called in the relevant start function, in this case HAL_ADC_Start_DMA.

If you feel a post has answered your question, please click "Accept as Solution".
VRakc.1
Associate II

So, for the people who will have same problem in the future, hope this solution will save them some time:

Dont forget to Enable the DMA Continuous requests(only in ADC1 is required):

  hadc1.Init.DMAContinuousRequests = ENABLE;

You can initialize everything like this in the main file:

	if(HAL_ADC_Start(&hadc2)!= HAL_OK)
	{
	  Error_Handler();
	}
	if(HAL_ADC_Start(&hadc3)!= HAL_OK)
	{
	  Error_Handler();
	}
	/* By calling this function - MultiMode will be triggered and DMA Transfer interrupt should be expected
	 * More details: It starts ADC1, triggers start of the ADC2 and ADC3 which triggers DMA
	 * */
	if(HAL_ADCEx_MultiModeStart_DMA(&hadc1, (uint32_t*)adc_data, 3)!= HAL_OK)
	{
	  Error_Handler();
	}
 
    for(int i=0; i<5; i++)
    {
  		adc_data_ready_flag = 0;        // clear flag
//  	    hadc1.Instance->CR2 |= (uint32_t)ADC_CR2_SWSTART;
  		HAL_ADC_Start(&hadc1);
 
  		while (!adc_data_ready_flag); // wait for new adc data
 
    }

DSun.2
Associate

Thanks for the nice and helpful post! I have two questions here:

  1. In the original code, shouldn't adc_data be uint32_t as HAL_ADCEx_MultiModeStart_DMA stores 32 bit integers only?
  2. I suspect that adc_data contains adc values from three ADC channels (adc1, adc2, & adc3). How do you split them into individual adc values: adc1_data, adc2_data & adc3_data, where adc1_data being uint16_t for using 12-bit ADC resolution?

I will greatly appreciate your help!

The forum says my email address doesn't have an account. Test to see if replying to posts works.
If you feel a post has answered your question, please click "Accept as Solution".