cancel
Showing results for 
Search instead for 
Did you mean: 

MultiMode ADC Stops Shortly After Start When Integrated With FreeRTOS & TouchGFX

MMoha.3
Associate III

Hello,

So in an earlier question, I asked about how I can suspend/restart multimode ADCs of the STM32F429 configured in the triple interleaved mode.

To find an answer, I set up a blank project in which I periodically start/stopped the ADCs after different combinations of delays.

I was able to successfully suspend/restart the ADCs in all combinations with no problem.

My proof for that is that I'm able to see the noise introduced by ADCs sampling when they're running, on the input signal via an oscilloscope.

Now I want to do the same thing but this time with the FreeRTOS and TouchGFX added.

So I implemented the same code in my application as the following:

///////// in screen1view:
void Screen1View::handleTickEvent()
{
	TickCounter++;
	if (TickCounter > 500) {   //startADCs
		All_Init();                  //initialize ADCs & DMA
		HAL_ADC_Start(&hadc3);
		HAL_ADC_Start(&hadc2);
		HAL_ADCEx_MultiModeStart_DMA(&hadc1, (uint32_t *)ADC_Buffer, 3000);
		TickCounter = 0;
		HAL_GPIO_WritePin(GPIOE, GPIO_PIN_4, GPIO_PIN_SET); //PE4 -> 1
	} else if (TickCounter == 250) {    //stop ADCs
		HAL_ADCEx_MultiModeStop_DMA(&hadc1);
		HAL_ADC_Stop(&hadc2);
		HAL_ADC_Stop(&hadc3);
		HAL_GPIO_WritePin(GPIOE, GPIO_PIN_4, GPIO_PIN_RESET); //PE4 -> 0
	}
}
 
 
////// in main:
void All_Init(void) {
	MX_DMA_Init();
	MX_ADC3_Init();
	MX_ADC2_Init();
	MX_ADC1_Init();
}

Note that the code I'm using for suspending/restarting the ADCs is exactly the same as before without FreeRTOS.

Now by running this code, the ADCs start when TickCounter gets above 500, but they stop running after ~7 milliseconds.

Here's the waveform captured on an oscilloscope.

0693W00000DqVEZQA3.pngHere the yellow waveform is the analog signal to the ADCs,

The cyan waveform is the GPIO PE4 which I used in the code above in order to find out when the ADC starts.

As you can see in the marked area in the picture, the ADC sampling starts as soon as the PE4 gets high, but instead of running until the PE4 gets low which is when the ADC suspension should occur, they just turn off after ~7ms.

The DMA interrupt priority is set to 0,0.

If anyone has any suggestions I'd be happy to hear them.

Thank you in advance

9 REPLIES 9
TDK
Guru

Perhaps you are exceeding the available bandwidth. If so, there should be an error flag set in DMA and ADC to indicate overflow. Back off the conversion by a couple orders of magnitude and test.

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

@TDK​ Thank you for the answer,

It does actually fix by reducing the ADC speed. I increased the delay between two samples (TwoSamplingDelay) from 5 cycles into 10. So the sampling rate is now 3.6Msps instead of 7.2Msps.

As you can see now it doesn't stop the ADC until the TickCounter hits 250.

0693W00000DqVliQAF.png 

I don't know if it's the right way to check for ADC&DMA errors, but just I used the following code and added errorstate1~errorstate4 in the watchlist and debugged the code with a breakpoint on the __NOP() line. Also note that the ADC interrupts are disabled.

uint32_t errorstate1 = 0;
uint32_t errorstate2 = 0;
uint32_t errorstate3 = 0;
uint32_t errorstate4 = 0;
void Screen1View::handleTickEvent()
{
	TickCounter++;
	if (TickCounter > 500) {
		All_Init();
		HAL_ADC_Start(&hadc3);
		HAL_ADC_Start(&hadc2);
		HAL_ADCEx_MultiModeStart_DMA(&hadc1, (uint32_t *)ADC_Buffer, 3000);
		TickCounter = 0;
		HAL_GPIO_WritePin(GPIOE, GPIO_PIN_4, GPIO_PIN_SET);
	} else if (TickCounter == 2) {
		errorstate1 = HAL_DMA_GetError(&hdma_adc1);
		errorstate2 = HAL_ADC_GetError(&hadc1);
		errorstate3 = HAL_ADC_GetError(&hadc2);
		errorstate4 = HAL_ADC_GetError(&hadc3);
		__NOP();            // Breakpoint Here
	} else if (TickCounter == 250) {
		HAL_ADCEx_MultiModeStop_DMA(&hadc1);
		HAL_ADC_Stop(&hadc2);
		HAL_ADC_Stop(&hadc3);
		HAL_GPIO_WritePin(GPIOE, GPIO_PIN_4, GPIO_PIN_RESET);
	}
}

but all of the errorstate variables are set to 0x0 hit the breakpoint.

is there something that I'm missing out?

Also note that while the ADCs are sampling @7.2 Msps, the DMA is working in mode 2 which means it transfers two samples at a time and therefore the DMA is actually working @3.6 MHz.

Is there a way to keep it at 7.2Msps while also using FreeRTOS & TouchGFX?

Using the DMA FIFO will help mitigate the issue, but you're taxing the available resources. At some point, it can't keep up. There are faster chips available.
If you feel a post has answered your question, please click "Accept as Solution".

Interestingly enough, the FIFO does not help by any means. No matter what combination of burst size and threshold I choose, each time the suspension occurs at precisely the same time (7.04ms).

Increasing the DMA transfer size (NDTR) also does not have any effect.

By the way, Is my approach for reading the DMA/ADCs error status actually practical?

MMoha.3
Associate III

Just a quick update,

I disabled the TouchGFX task, and made a new task with the same code that I was using for testing the ADCs, and realized that when the TouchGFX is disabled, the ADC doesn't stop.

So the problem begins when the TouchGFX task executes. I think as you also suggested the combination of the TouchGFX and ADCs at the highest frequency results in bandwidth insufficiency.

At first, I was thinking because I'm using the DMA then there shouldn't be any bandwidth limitation since the CPU is not involved. But now I think the LTDC and ADCs maybe share some resources (like APB buses or etc). Which causes the ADC to stop. Please tell me if I'm wrong.

So now I think I should find a way to suspend the TouchGFX task (for example by using vTaskSuspend) until I gather the data I needed from ADCs.

TDK
Guru

HAL_ADC_GetError just returns ErrorCode from within the handle. That's only populated by HAL if you're using its framework for handling errors.

I would examine the registers directly for an error. Although, clearly bandwidth is the issue here so I'm not sure how useful that would be.

DMA doesn't use the CPU, but it does use the system bus, along with pretty much everything else that needs to access memory. The bus is a shared resource. When it's occupied, the CPU will block until it gets what it needs. Since the ADC/DMA can't block, it overruns and will set an error flag. See the "System architecture" figure in the reference manual for a conceptual view of this.

0693W00000DlNbLQAV.png

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

Thank you for your answer,

There's one thing that I don't understand here.

Here I have created two tasks:

  • The first one is the TouchGFX task, with Normal priority
  • And the second one is a task that runs every 500ms and starts/stops ADCs (ADCmanager), with High priority.

Now if I suspend the TouchGFX thread after like 3 seconds via osThreadSuspend, it should not cause any problem for the ADCs after that anymore, because the CPU doesn't use any resources, right?

I tested the following code for the second task:

void ADCManager(void *argument)
{
  /* USER CODE BEGIN ADCManager */
  /* Infinite loop */
  for(;;)
  {
	counter++;
	if (counter == 3) {
		stats = osThreadSuspend(GUI_TaskHandle);
	}
	All_Init();
	HAL_ADC_Start(&hadc3);
	HAL_ADC_Start(&hadc2);
	HAL_ADCEx_MultiModeStart_DMA(&hadc1, (uint32_t *)ADC_Buffer, 3000);
	HAL_GPIO_WritePin(GPIOE, GPIO_PIN_4, GPIO_PIN_SET);
	osDelay(500);
	HAL_ADCEx_MultiModeStop_DMA(&hadc1);
	HAL_ADC_Stop(&hadc2);
	HAL_ADC_Stop(&hadc3);
	HAL_GPIO_WritePin(GPIOE, GPIO_PIN_4, GPIO_PIN_RESET);
	osDelay(500);
  }
  /* USER CODE END ADCManager */
}

So it now suspends the TouchGFX after 3 seconds, and I can confirm that, because I blink an LED in the TouchGFX tick event, and after 3 seconds it does not blink anymore, and also the touchscreen becomes unresponsive.

But even after the 3 seconds are passed, the ADCs still stop working after 7ms!

Now, if I change the if (counter == 3) statement into if (counter == 1), which means that the TouchGFX task is suspended before it is executed even a single time, the problem solves and ADCs keep running.

Sorry if I keep posting beginner-level posts 😅

But I think I just found a solution for what I'm trying to do.

I realized that the TouchGFX is not only relying on the FreeRTOS to execute, but also LTDC interrupts (in the osWrappers).

So instead of suspending the FreeRTOS task, I had to disable the LTDC interrupts in order to keep the CPU hands off the buses.

Here's the code I tested as my second task (ADCmanager task):

void ADCManager(void *argument)
{
  /* USER CODE BEGIN ADCManager */
  /* Infinite loop */
  for(;;)
  {
	__HAL_LTDC_DISABLE_IT(&hltdc,LTDC_IT_LI);         //disable LTDC interrupts
	All_Init();
	HAL_ADC_Start(&hadc3);
	HAL_ADC_Start(&hadc2);
	HAL_ADCEx_MultiModeStart_DMA(&hadc1, (uint32_t *)ADC_Buffer, 3000);
	HAL_GPIO_WritePin(GPIOE, GPIO_PIN_4, GPIO_PIN_SET);
	osDelay(500);
	__HAL_LTDC_ENABLE_IT(&hltdc,LTDC_IT_LI);         //enable LTDC interrupts
	HAL_ADCEx_MultiModeStop_DMA(&hadc1);
	HAL_ADC_Stop(&hadc2);
	HAL_ADC_Stop(&hadc3);
	HAL_GPIO_WritePin(GPIOE, GPIO_PIN_4, GPIO_PIN_RESET);
	osDelay(500);
  }
  /* USER CODE END ADCManager */
}

And here's the result:

0693W00000DlVX7QAN.pngSo as you can see, now the ADCs are running for 500ms, and are suspended for another 500ms.

While the ADCs are running, the screen & touchscreen are basically frozen, and while the ADCs are stopped, the LCD&touch are completely responsive.

hhuan.21
Associate II

Hello, I want to know multimode ADCs of the STM32F765 configured in the triple interleaved mode.I think it is similar to STM32F429 when using the hal library, but my configuration has not been successful. Can you give me some advice?