Tasks not running after starting ADC conversion (STM32 blue pill)

Hi all. I’m new to the forum so I don’t know if this is the right category in which to post my question.
I’m new to FreeRTOS and I want to make a project in which I use the xTaskNotifyFromISR & xTaskNotifyWait API. I want to start an ADC conversion after pressing a button. I created an ADC Task with the highest priority that starts the ADC conversion when it gets the nofitication from an EXTI IRQ handler. In the project I have other tasks with lower priorites (a LED task, one for an OLED display and a default task just so the sake of it).
After the ADC task starts the conversion I get on the debug printf output only the ADC value and the other tasks are not running.
This is the ADC task:

void StartADCTask(void argument)
{
/
USER CODE BEGIN StartADCTask /
uint32_t ulNotifiedValueADCTask;
/
Infinite loop */
for( ;; )
{

  xTaskNotifyWait( 0U, 0U, &ulNotifiedValueADCTask, portMAX_DELAY );  /* Block indefinitely. */
  
  if(ulNotifiedValueADCTask == 1U) {
  	printf("ADC Task\r\n");
  	HAL_ADC_Start_DMA(&hadc1, (uint32_t *)&adc1_dma_data, sizeof(adc1_dma_data));
  }
  
  if(ulNotifiedValueADCTask == 0U) {
  	HAL_ADC_Stop_DMA(&hadc1);
  	printf("ADC stopped\r\n");
  }
osDelay(1000);

}
/* USER CODE END StartADCTask */
}
Blockquote

EXTI handler for the button push:

void EXTI0_IRQHandler(void)
{
/* USER CODE BEGIN EXTI0_IRQn 0 */

BaseType_t xHigherPriorityTaskWoken = pdFALSE;
flag = flag ^ 1U;
xTaskNotifyFromISR(ADCTaskHandle, flag, eSetValueWithOverwrite, &xHigherPriorityTaskWoken );

portYIELD_FROM_ISR( xHigherPriorityTaskWoken );

/* USER CODE END EXTI0_IRQn 0 /
HAL_GPIO_EXTI_IRQHandler(GPIO_PIN_0);
/
USER CODE BEGIN EXTI0_IRQn 1 */

/* USER CODE END EXTI0_IRQn 1 */
}

And I’m using the callback when the conversion is finished just to output the value:

void HAL_ADC_ConvCpltCallback(ADC_HandleTypeDef* hadc) {
printf(“Conversion complete. Conversion value %u.\r\n”, adc1_dma_data);
}

I’m using Keil IDE and STMCubeMx.
What should I do/change so that after an ADC conversion the other tasks run as well?

Are the other tasks running before you start the ADC conversion and the (seem to) stop on ADC conversion complete output ?
Where is the ADC conversion complete callback called ? From the ADC ISR ?
In this case do not use printf family functions. Mostly they simply can’t be used in ISRs besides being usually pretty stack hungry.

BTW:

I don’t get the purpose of this delay. Especially if you make the ADC conversion task highest prio to run it as soon as possible after getting notified by the push button…

Yes, there are other tasks running before I start the ADC conversion. That’s why I set the ADC conversion to a HIGH priority so it stays in Blocked state until the button is pressed and then it can start the ADC conversion.
The callback is called from ADC_DMAConvCplt() function which is not the ADC ISR. I don’t use printf in the ISRs, just in the task functions and the callbacks so it doesn’t affect the stack.
This is how my output looks when I push the button.
Before pushing:

running default task
Display Task
blinky blinky
Display Task
blinky blinky

After pushing:

running default task
Display Task
blinky blinky
Button was pressed.
Conversion complete. Conversion value 2247.
Conversion complete. Conversion value 2065.
Conversion complete. Conversion value 2062.
Conversion complete. Conversion value 2047.
Conversion complete. Conversion value 2192.
The application that I want to do is to display the ADC value on an OLED display and use the ADC value from DMA as the prescale value for a timer. But when the conversion starts, nothing else runs.
Regarding the delay I thought it was ok to let the delay there. The other tasks have a delay of 500 ms/ 1s. I’ll remove that delay from the ADC task.

Looks like after starting ADC conversion it’s running continuously and with a conversion rate high enough to starve all other tasks. Even the ‘ADC task’ output is missing ?
Which task handles the conversion complete callback ? Either adjust/slow down the conversion rate or maybe use a single shot conversion and restart it in the conversion completion handler.

ADC_DMAConvCplt is set as the DMA complete callback in HAL_ADC_Start_DMA which is called from DMA ISR:

 hadc->DMA_Handle->XferCpltCallback = ADC_DMAConvCplt;

So ADC_DMAConvCplt (and as a result HAL_ADC_ConvCpltCallback) is called in ISR context.

Looking at the button push handler, are you trying to stop the ADC on second button push?

Thanks.

Looking at the button push handler, are you trying to stop the ADC on second button push?

Yes, that’s correct. So the way that I want this application to behave is (I’m going to use the printf statements to suggest which task is running):

ADC task runs → gets Blocked because no notification
Timer task
Display task
From here display and timer task should run normally
Press the button → EXTI callback gives notification to ADC task → ADC Task starts the ADC DMA conversion → ADC DMA conversion complete
Timer task → so here I want to use the ADC value as the prescaler value for the timer
Display task → display the ADC value
ADC DMA conversion finished
Timer task
Display task
ADC DMA conversion finished
Timer task
Display task
ADC DMA conversion finished
Timer task
Display task
I press the button again → ADC task runs and called ADC_DMA_Stop()
Timer task
Display task

So on and so forth. So my idea is to use the ADC task to start the DMA conversion and use the value in the timer and display task. I removed the default and blinky tasks, they are not that important.
Do you have any suggestions how to approach this in a better way?

@hs2 I have no task handling the conversion complete callback. I’ll lower the conversion rate to see if at least another task has a change to run.

@sparky Seems my concern about calling printf in an ISR is right. Better avoid that. Usually doing so breaks things. You’ve been warned :wink:

Seems my concern about calling printf in an ISR is right. Better avoid that. Usually doing so breaks things. You’ve been warned

Haha, truly. I treated HAL_ADC_Start_DMA as a normal function call and didn’t consider the DMA ISR. I’ll try to avoid printf calls from ISR.
@hs2 do you have any suggestions about the reply above with the task ordering?

Sorry - I know too little about your application…
But what about arming the timer right in the ADC conversion complete callback (is it a single shot timer ?) and notify the display task, if it’s intended to show the current ADC converted value while the timer/the next ADC conversion is in progress. Or do you want to re-start the next ADC conversion after the timer has expired ? Then just arm the timer and defer the restart of the ADC conversion to the timer task notified by the expired timer…

Can you not do something like below (and repeat the same in a loop)?

ADC task runs → gets Blocked because no notification
Timer task
Display task
From here display and timer task should run normally
Press the button → EXTI callback gives notification to ADC task → ADC Task starts the ADC DMA conversion → ADC DMA conversion complete -> STOP ADC 
Timer task → so here I want to use the ADC value as the prescaler value for the timer
Display task → display the ADC value

i.e. Stop the ADC in the conversion complete callback so that the ISR does not keep firing?

Thanks.

1 Like

I changed my code and now I’m stopping the ADC conversion from the callback. Now I can toggle when I want to start and stop de ADC task.
Thanks for the help!