What if I need to run a function forever?

Hello,

I am trying to create a device that controls an array of 8 LED’s. I can communicate to the CYBLE-416045-02 module with Bluetooth connection and call different functions that turn the LED’s on and off per specific patterns. A particular pattern is blinking the LED’s forever with an infinite loop implemented with for (;;).

If I once start this function to run forever, I cannot stop it with another Bluetooth message. I couldn’t find how to implement an interrupt mechanism that would break the forever loop and start another patter of LED’s blinking.

Do I need to consider to use tasks rather than functions with forever loops? I just want the blinking pattern to run until I send another mode command via Bluetooth or turn the device off.

Thanks

Basically you‘re better off using separate task(s) for concurrent functions. Maybe including FreeRTOS timers (implemented as task, as well).
But you could also set a global (volatile) variable or flag in an ISR you can check in the infinite loop and break to invoke another function or simply select an other pattern within this loop.

Hello Geodreieck, in order to get more insight into your project, maybe you can attach some code that you have already written?

What if I need to run a function forever?

In fact, a task is a function that runs for ever. One thing to remember is that a task must always yield or block, in order to give CPU time to other tasks.

A simple task function could look like this:

void vMyTask( void *pvParameter )
{
	for( ;; )
	{
		vTaskDelay( pdMS_TO_TICKS( 10u ) );	/* Sleep for 10 msec. */
		/* Do something here. */
	}
}

If your code has a loop forever with nothing in it to stop to accept new data, that is what the code will do. It sounds like that blinking loop should have in it some test to see if a new pattern is available. This likely means that the display operation is a task, and gathering new patterns may well be a separate task that when it gets the new information sends that to the display task.

One way to do this is use something like a queue to post the new pattern, and the display loop does a read from queue with no wait, and IF you get a new pattern, you switch to it, and if you don’t you keep the same pattern.

Hello, thank you very much for your suggestion. Here is a snippet for the code.

void bleTask(void *arg)
{
    (void)arg;

    bleSemaphore = xSemaphoreCreateCounting(UINT_MAX,0);    
    Cy_BLE_Start(genericEventHandler);
    
    while(Cy_BLE_GetState() != CY_BLE_STATE_ON) // Get the stack going
    {
        Cy_BLE_ProcessEvents();
    }

    Cy_BLE_RegisterAppHostCallback(bleInterruptNotify);

    for(;;)
    {
       xSemaphoreTake(bleSemaphore,portMAX_DELAY);
       Cy_BLE_ProcessEvents();
     }
}

Here is my bleInterruptNotify.

void bleInterruptNotify()
{
     BaseType_t xHigherPriorityTaskWoken;
     xHigherPriorityTaskWoken = pdFALSE;
     xSemaphoreGiveFromISR(bleSemaphore, &xHigherPriorityTaskWoken); 
     portYIELD_FROM_ISR( xHigherPriorityTaskWoken );
}

And this is my GenericEventHandler.

void genericEventHandler(uint32_t event, void *eventParam)
{
	/* Take an action based on the current event */
	printf("genericEventHandler\r\n");
	switch (event)
	{
		/* This event is received when the BLE stack is Started */
        ...
		
		case CY_BLE_EVT_GATTS_WRITE_REQ:
			printf("Command received\r\n");
			running = false;
			handleBluetoothMessage(eventParam);
			break;
		
		default:
			printf("Unhandled Bluetooth event\r\n");
			break;
	}
}

handleBluetoothMessage is the function that decodes received Bluetooth message and starts the blinking pattern. I also have added some debug code and I checked them with UART. The problem is, once the blinking pattern starts, the Bluetooth stack stops receiving any messages. If the pattern ends then it starts accepting messages again. If the pattern is a forever loop, then it never handles any other Bluetooth message.

I guess I am doing a very basic design error somewhere in the code, but I couldn’t figure it out by myself. I hope the snippets explain it better.

Thanks

One thing about the FreeRTOS code: your function bleInterruptNotify() may only be called from an interrupt (ISR), not from a normal task like you do.

This function could be used by a task:

void bleNormalNotify()
{
     xSemaphoreGive(bleSemaphore); 
}

Googling your function, I found this example. It calls Cy_BLE_GATTS_WriteRsp(handle) after handling the request. Did you also do so?

If the pattern ends then it starts accepting messages again

Wait, which code is producing the blinking? The function handleBluetoothMessage()?
I would expect that function to return immediately so you can receive more messages.
The easiest solution is to add a separate task that does the blinking, like e.g.

typedef enum eBlinkType {
    eBlinkStop,
    eBlinkSlow,
} BlinkType_t;

volatile BlinkType_t eBlinkType = eBlinkStop;
volatile BaseType_t xLed = pdFALSE;

void vBlinkTask( void *pvParameter )
{
    for( ;; )
    {
    TickType_t uxTicks;
        switch( eBlinkType )
        {
            case eBlinkStop:
                uxTicks = pdMS_TO_TICKS( 1000u );
                xLed = pdFALSE;
                break;
            case eBlinkSlow:
                uxTicks = pdMS_TO_TICKS( 2000u );
                xLed = !xLed;
                break;
        }
        set_gpio( GPIO_LED, xLed );
        ulTaskNotifyTake( pdFALSE, uxTicks );
    }
}

The code show is not yet tested. Would it work for you?

@htibosch Thank you very much for your help. After your message, I found that I had to study the task concept a little bit more and I have spent a good three hours on studying the “Mastering the FreeRTOS™ Real Time Kernel” document.

Now I have two seperate task. One is for the Bluetooth, one is for the blinking. They communicate over a queue mechanism. It works like a charm.

Thank you again for your guidance. I feel much more comfortable with tasks now.

Here is the snippet to implement the queue.

xStatus = xQueueSendToBack( xTriggerQueue, &data_to_send, 0 );
xStatus = xQueueReceive( xTriggerQueue, &lReceivedValue, xTicksToWait);

@richard-damon Your suggestion was exactly what I needed. I appreciate your input.

Regards