cancel
Showing results for 
Search instead for 
Did you mean: 

Unable to change digital output pin value multiple times without a delay

EMill.1
Associate II

If a digital output pin value is changed multiple times within the same main loop, within the same interrupt, or within the same task and not separated by a delay then the output pin does not work as expected.

This applies to both HAL_GPIO_TogglePin() and HAL_GPIO_WritePin()

Using the NUCLEO-G031K8 PCB with the STM32CubeIDE

Working Example:

Code in main() while loop with no interrupts, and no RTOS

HAL_GPIO_TogglePin() is called 2 times and is separated with a delay

Output pins work as expected

PB0 & PB1 toggle 1 time per ms

Channe1 = PB0, Channel2 = PB1

0693W00000HqpoJQAR.jpgwhile (1)

 {

   HAL_GPIO_TogglePin(GPIOB, GPIO_PIN_0);

   HAL_GPIO_TogglePin(GPIOB, GPIO_PIN_1);

   HAL_Delay(0); //Delay 1 ms

   HAL_GPIO_TogglePin(GPIOB, GPIO_PIN_0);

   HAL_GPIO_TogglePin(GPIOB, GPIO_PIN_1);

   HAL_Delay(0); //Delay 1 ms

   /* USER CODE END WHILE */

   /* USER CODE BEGIN 3 */

 }

Example with digital output not working as expected:

Code in main() while loop with no interrupts, and no RTOS

HAL_GPIO_TogglePin(GPIOB, GPIO_PIN_0) is called 2 times with no delay in between

GPIO_PIN_0 (PB0) does not toggle when it should

PB0 toggles a small random number of times (example: 4 times) then there is a large random length delay (can be hundreds of ms long)

GPIO_PIN_0 (PB1) works as expected because it is separated with delays

Channe1 = PB0, Channel2 = PB1

The main loop is called every 2 ms so the yellow trace (GPIO_PIN_0) and the blue trace (GPIO_PIN_1) should toggle each time through the main loop, but the yellow trace is not toggling when it should.

0693W00000HqpoOQAR.jpgwhile (1)

 {

   HAL_GPIO_TogglePin(GPIOB, GPIO_PIN_0);

   HAL_GPIO_TogglePin(GPIOB, GPIO_PIN_0);

   HAL_GPIO_TogglePin(GPIOB, GPIO_PIN_1);

   HAL_Delay(0); //Delay 1 ms

   HAL_GPIO_TogglePin(GPIOB, GPIO_PIN_1);

   HAL_Delay(0); //Delay 1 ms

   /* USER CODE END WHILE */

   /* USER CODE BEGIN 3 */

 }

6 REPLIES 6
Peter BENSCH
ST Employee

Welcome, @EMill.1​, to the community!

first of all, you benefit from a protection function built into the HAL: HAL_Delay(0) would actually be interpreted as a zero millisecond delay, however the function contains:

__weak void HAL_Delay(uint32_t Delay)
{
 uint32_t tickstart = HAL_GetTick();
 uint32_t wait = Delay;
 
 /* Add a freq to guarantee minimum wait */
 if (wait < HAL_MAX_DELAY)
 {
  wait += (uint32_t)(uwTickFreq);
 }
 while ((HAL_GetTick() - tickstart) < wait) { }
}

In this respect, you also have a millisecond waiting time when specifying 0 - as long as the __weak function was not overwritten and the preset SysTick with 1ms was used.

Now to your question: if you have two statements HAL_GPIO_TogglePin() immediately following each other, of course you only have the delay that HAL_GPIO_TogglePin() needs to switch the pin. With the frequency of the CPU, this is typically in the range of nanoseconds, since only a few instructions are used for toggling:

void HAL_GPIO_TogglePin(GPIO_TypeDef *GPIOx, uint16_t GPIO_Pin)
{
  uint32_t odr;
 
  /* Check the parameters */
  assert_param(IS_GPIO_PIN(GPIO_Pin));
 
  /* get current Output Data Register value */
  odr = GPIOx->ODR;
 
  /* Set selected pins that were at low level, and reset ones that were high */
  GPIOx->BSRR = ((odr & GPIO_Pin) << GPIO_NUMBER) | (~odr & GPIO_Pin);
}

Please review your programme and requirements in this context.

Regards

/Peter

In order to give better visibility on the answered topics, please click on Accept as Solution on the reply which solved your issue or answered your question.
EMill.1
Associate II

Hi Peter

Thank you for the quick reply.

If there are two statements of HAL_GPIO_TogglePin() immediately following each other or 2 statements of HAL_GPIO_WritePin() following each other then the output pin will toggle a few times as expected and then stops responding for a long random period of time (hundreds of milliseconds).

It seems that I need at least a 1 ms delay between the statements that change the output pin state or the output pin stops working correctly. I have tried small and large delays using HAL_Delay() in the main loop, I tried using osDelay() in a task, and I have tried creating my own microsecond delay function, but the output pin does not respond as expected unless the delay is at least 1 millisecond long.

My application requires that I create a periodic narrow pulse (nanoseconds or microseconds in width) on an output pin. During the rising edge of the pulse I take an analog reading.

Do you know a way I can reliably generate 1 narrow pulse (nanoseconds or microseconds in width) every 10 milliseconds without using PWM ?

1. Set the highest GPIO_OSPEEDR setting for given pin

2. Switch oscilloscope to its highest resolution - it won't sample ns at 100ms/div.

JW

Pick some timer. Set period to 10ms. Enable interrupts from timer update event ans start timer.

In timer IRQ routine disable interrupts, perform Pin_set and Pin_reset (generate pulse) and enable interrupts back.

But... you should have a good reason to not use PWM.

If i remember correctly HAL_Delay() is in principle unprecise (-0ms to +1ms ?). Random delay in range to hundred miliseconds smells like Systick overflow value (2^24 / 64MHz = 262ms). Ask to STM32 gurus, can HAL_Delay(0) suffer from some kind of "race" condition between two HAL_GetTick() calls ?

S.Ma
Principal

Use ADC with a conversion triggered by a pin edge, if you want a sw delay, or a filter of the incoming trigger, use a timer capture which will output a trigger compare.

In the original code I feel the issue is that the HAL code function doesn't tell if it is atomic (read modify write safe in multiple async call of the function, such as interrupt, multi thread, multi cores)

Write patterns to GPIO->BSRR via a pointer with the correct volatile attribute, or use assembler for tight/specific control.

Use a free-running maximal TIM for a timebase to resolve the time line sub-microsecond. ie read and delta TIM->CNT

Consider if you can drive a pattern buffer to GPIO-<BSRR via TIM+DMA

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