cancel
Showing results for 
Search instead for 
Did you mean: 

ADC + DMA stops reading

GComes
Associate II

I have a setup which reads data from 4 step-motors and some sensors over ADC1(CH0, CH1, CH2, CH3), and ADC2(CH0, CH1). 

The system executes continuous ADC conversions in DMA Mode in the following way:

- In the while-loop in the main function, it reads the actual position of the motors and the data of the sensors.

- The TIM3, which is configured to execute in an interval of 20ms, is responsible for controlling the motors.

 

The issue that I am facing is that both ADCs stop reading a few minutes after starting, and no data in the register DR is available, it just freezes, first one ADC then the other one, sometimes ADC1 freezes first, other ADC2. 

I have checked both ADCs Overflow flags & DMA Transfer error flags, but they indicate no error. I thought some race conditions could be happening due to TIM3, so I have commented out its initialization, but even though the same issue persisted. 

 

I am using the STM32F303VET

Microsoft Visual Studio Professional 2019 - Version 16.11.30
VisualGDB Version 5.6R9

The initialization codes are:
ADC1

 

  hadc1.Instance = ADC1;
  hadc1.Init.ClockPrescaler = ADC_CLOCK_SYNC_PCLK_DIV4;
  hadc1.Init.Resolution = ADC_RESOLUTION_12B;
  hadc1.Init.ScanConvMode = ADC_SCAN_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 = 9;
  hadc1.Init.DMAContinuousRequests = ENABLE;
  hadc1.Init.EOCSelection = ADC_EOC_SEQ_CONV;
  hadc1.Init.LowPowerAutoWait = DISABLE;
  hadc1.Init.Overrun = ADC_OVR_DATA_OVERWRITTEN;
  if (HAL_ADC_Init(&hadc1) != HAL_OK)
  {
    Error_Handler();
  }

  /** Configure the ADC multi-mode
  */
  multimode.Mode = ADC_MODE_INDEPENDENT;
  if (HAL_ADCEx_MultiModeConfigChannel(&hadc1, &multimode) != HAL_OK)
  {
    Error_Handler();
  }

  /** Configure Regular Channel
  */
  sConfig.Channel = ADC_CHANNEL_1;
  sConfig.Rank = ADC_REGULAR_RANK_1;
  sConfig.SingleDiff = ADC_SINGLE_ENDED;
sConfig.SamplingTime = ADC_SAMPLETIME_4CYCLES_5;
  sConfig.OffsetNumber = ADC_OFFSET_NONE;
  sConfig.Offset = 0;
  if (HAL_ADC_ConfigChannel(&hadc1, &sConfig) != HAL_OK)
  {
    Error_Handler();
  }

 

 

 The configuration for other Channels and ADC2 is the same

For DMA1:

 

    hdma_adc1.Instance = DMA1_Channel1;
    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;
    if (HAL_DMA_Init(&hdma_adc1) != HAL_OK)
    {
      Error_Handler();
    }

    __HAL_LINKDMA(adcHandle,DMA_Handle,hdma_adc1);

  __HAL_RCC_DMA1_CLK_ENABLE();

  HAL_NVIC_SetPriority(DMA1_Channel1_IRQn, 1, 0);
  HAL_NVIC_EnableIRQ(DMA1_Channel1_IRQn);

 

 

 

 

DMA2

 

    hdma_adc2.Instance = DMA2_Channel1;
    hdma_adc2.Init.Direction = DMA_PERIPH_TO_MEMORY;
    hdma_adc2.Init.PeriphInc = DMA_PINC_DISABLE;
    hdma_adc2.Init.MemInc = DMA_MINC_ENABLE;
    hdma_adc2.Init.PeriphDataAlignment = DMA_PDATAALIGN_HALFWORD;
    hdma_adc2.Init.MemDataAlignment = DMA_MDATAALIGN_HALFWORD;
	  hdma_adc2.Init.Mode = DMA_CIRCULAR;
	  hdma_adc2.Init.Priority = DMA_PRIORITY_HIGH;
    if (HAL_DMA_Init(&hdma_adc2) != HAL_OK)
    {
      Error_Handler();
    }

    __HAL_LINKDMA(adcHandle,DMA_Handle,hdma_adc2);

  __HAL_RCC_DMA2_CLK_ENABLE();

  HAL_NVIC_SetPriority(DMA2_Channel1_IRQn, 1, 0);
  HAL_NVIC_EnableIRQ(DMA2_Channel1_IRQn);

 

 

 

 

TIM3

 

static const uint32_t TIM3_PRIORITY = 2;
static const uint32_t PRS_TIM3 = 19507 - 1;
static const uint8_t ARR_TIM3 = 2 - 1;


void InitializeTimerTim3()
{
	// Enable TIM3 clock
	RCC->APB1ENR |= RCC_APB1ENR_TIM3EN;

	// Configure TIM3
	TIM3->PSC = PRS_TIM3;		// Prescaler
	TIM3->CR1 &= ~TIM_CR1_DIR;	// Counter direction up
	TIM3->ARR = ARR_TIM3;		// Autoreload Register
	TIM3->CR1 |= TIM_CR1_CKD_0;     // Clock division: division by 1
	TIM3->RCR = 0;			// Repetition counter register
	TIM3->CR1 |= TIM_CR1_CEN;	// Enable TIM3
}

void EnableTimerInterruptTim3()
{
	// Configure NVIC for TIM3 interrupt
	HAL_NVIC_SetPriority(TIM3_IRQn, TIM3_PRIORITY, 0); // Set priority
	HAL_NVIC_EnableIRQ(TIM3_IRQn);			// Enable TIM3 interrupt
	TIM3->DIER |= TIM_DIER_UIE;                     // Enable update interrupt
}

 

 

And the function in the main-loop to read from the ADC: adcMcu_executeDma()

 

 

void adcMcu_executeDma(adcMcu_DmaInterface_t *adc, uint64_t time)
{
	if (adc->state == adc_wait && adc->nextUpdateTime <= time)
	{
		startDmaInput(adc, time);
	}
	if (adc->state == adc_finish)
	{
		uint64_t nextTime = UINT64_MAX; 
		for (uint8_t i = 0; i < adc->numberOfChannels; ++i)
		{
			calcInput(adc, i, time);
			if (nextTime > adc->channels[i].nextUpdateTime_ms)
			{
				nextTime = adc->channels[i].nextUpdateTime_ms;
			}
		}
		adc->nextUpdateTime = nextTime;
		adc->state = adc_wait;
	}
}

void startDmaInput(adcMcu_DmaInterface_t *adc, uint64_t time)
{
	HAL_ADC_Start_DMA(adc->hadc, (uint32_t*)adc->rawValueMatrix, adc->numberOfChannels * adc->numberOfValues);
	adc->state = adc_working;
}

 

 

I have run out of ideas about what might be causing it.
Any help is highly appreciated. 

1 ACCEPTED SOLUTION

Accepted Solutions

That doesn't explain why you need to call it more than once per DMA stream. Why call HAL_ADC_Stop_DMA ever if your intention is to convert ADC data continuously?

I suspect there is a program logic bug, but I don't think it's in the code you've shown.

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

View solution in original post

4 REPLIES 4
TDK
Guru

Consider starting DMA only once in circular mode and using the half- and full-complete interrupts to process converted data. Although it seems like you're in circular mode, so why is DMA being started multiple times?

 

> sConfig.SamplingTime = ADC_SAMPLETIME_4CYCLES_5;

Consider increasing this. Could be swamping the system with data.

 

The bug is probably within other code, or within the order in which things get executed. The code logic used to call adcMcu_executeDma and set adc->state == adc_finish would be informative here.

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

Hello @TDK ,  thank you for your prompt response.

 

So, I am starting the DMA multiple times because I am using the DMA1 CH1 for the ADC1 and DMA2 CH1 for the ADC2. As there are many devices I choose to make use of two DMAs

 

These functions were written in my first comment, they are executing in the while-loop in the main() function:
adc_execute( ) → adcMcu_executeDmaInput() → 

→ if (adc->state == adc_wait) → startDmaInput() → HAL_ADC_Start_DMA()  →  adc->state = adc_working

→ if (adc->state == adc_finish) → calcInput()  →  adc->state = adc_wait

 

This is in the interrupt when the ADC conversion is completed:

HAL_ADC_ConvCpltCallback() → adcMcu_dmaInterrupt() → HAL_ADC_Stop_DMA()  → adc->state = adc_finish;

 

void HAL_ADC_ConvCpltCallback(ADC_HandleTypeDef* hadc)
{
	if (hadc == &hadc1)
	{
		adcMcu_dmaInterrupt(&adc1_Interface);
	}
	else if (hadc == &hadc2)
	{
		adcMcu_dmaInterrupt(&adc2_Interface);
	}
}

 

void adcMcu_dmaInterrupt(adcMcu_DmaInterface_t *adc)
{
	HAL_ADC_Stop_DMA(adc->hadc);
	adc->state = adc_finish;
}

 

 

That doesn't explain why you need to call it more than once per DMA stream. Why call HAL_ADC_Stop_DMA ever if your intention is to convert ADC data continuously?

I suspect there is a program logic bug, but I don't think it's in the code you've shown.

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

@TDK The problem was exactly in the continuous calling of the DMA_Start and Stop, somehow it was messing up with all ADCs readings.


So I have commented out the HAL_ADC_Stop_DMA and called the HAL_ADC_Start_DMA() only once before the while-loop.