ulTaskNotifyTake does not restart its timer

I must have misunderstood something about notifications.

On GPIO edge interrupt, I notify the task.

The task either takes the notification and lights up a green LED, or it times out while waiting and lights up a red LED.

Here is my ISR callback and my task:

void ab_encoder_on_edge_a(unsigned int gpio, uint32_t events)
{
	// some debouncing

	BaseType_t xHigherPriorityTaskWoken = pdFALSE;
	vTaskNotifyGiveFromISR(still_detection_task_handle,
		&xHigherPriorityTaskWoken);

	portYIELD_FROM_ISR(xHigherPriorityTaskWoken);
}

static void still_detection_task()
{
	while (true) {
		led_off(LED_YELLOW);
		led_off(LED_RED);
		led_off(LED_GREEN);

		BaseType_t was_notified =
			ulTaskNotifyTake(pdTRUE, pdMS_TO_TICKS(10));

		led_on(LED_YELLOW);
		if (!was_notified) {
			led_on(LED_RED);
		} else {
			led_on(LED_GREEN);

		}
	}
}

Expected behavior:

  • getting a train of pulses, period 2.2 ms
  • the ISR callback notifies the task at every falling edge
  • the task gets to sleep after handling the notification (green LED)
  • if no new notification within 10 ms, red LED

I’ve watched the LED signals on an oscilloscope, and see this instead.
Actual behavior:

  • the task does wake up at every falling edge
  • it also additionally wakes up every 10 ms, on top of that

It behaves as if there were two parallel running instances of the task, one dealing with the red LED, and the other dealing with the green LED.

An other way to see it is the the time-out in ulTaskNotifyTakedoes not reset to the value passed as parameter.

I only run xTaskCreate on this task once at init.

What is going on?

I now noticed that ulTaskNotifyTake does not return the value I thought it did. Is there no way to check if that function returned because of a notification or because of time-out?

I have now replaced it with xTaskNotifyWait, but get the same behavior:

		BaseType_t was_notified =
			xTaskNotifyWait(0x00,
					 UINT32_MAX,
					 NULL,
					 pdMS_TO_TICKS(10));
		led_on(LED_YELLOW);
		if (was_notified == pdFALSE) {
			led_on(LED_RED);
		} else {

(also changed the notification from the ISR callback:

	BaseType_t xHigherPriorityTaskWoken = pdFALSE;
	xTaskNotifyFromISR(still_detection_task_handle, 1, eIncrement,
			   &xHigherPriorityTaskWoken);

	portYIELD_FROM_ISR(xHigherPriorityTaskWoken);

)

Apologies, but your question isn’t clear to me. The API docs describe the xTaskNotifyWait() return value - did you see this page?

Thank you. I did see this page, and the description of the return value: “pdTRUE if a notification was received, or a notification was already pending when xTaskNotifyWait() was called. pdFALSE if the call to xTaskNotifyWait() timed out before a notification was received.”

Do you see an error in how I call this function in my last code block?

I use this return value to know if the task was notified or time-out. But even when receiving periodical notifications (every 2 ms), I get an extra wake-up every 10 ms (the timeout value). So, one led_on(LED_YELLOW); every 2 ms, and an extra every 10 ms.

New data: if I change the time-out to 100 ms, I get the behavior that I expect.

After this discovery, I did a binary search, and came to the conclusion that time-outs < 20 ms give me the unexpected time-out event. Values >= 20 ms do not time-out unexpectedly.

What could possibly explain this?

And here is my answer:

#define configTICK_RATE_HZ                         100

I don’t know where I got that setting from. Increasing to 1000 Hz solves the problem.

When you have tick rate of 100 Hz, a tick fires every 10 ms. This means that the time granularity is 10 ms - in other words, you are asking your task to block for one tick. That block time can be anything between 0.01 ms to 9.99 ms depending on when the API is called between 2 tick interrupts. Increasing tick rate to 1000 Hz, increases the time granularity to 1 ms which is what fixes your application.

1 Like