FreeRTOS i2c sensor communication

Thread Starter

zazas321

Joined Nov 29, 2015
926
Hello. I have been learning more and more about FreeRTOS and would like to confirm something. Imagine I have various sensors (I2C and SPI) connected to my microcontroller. I am writing and reading data from them using the SPI and I2C. Is there anything that I need to be careful about when I do that when I have a FreeRTOS running or everything should work as if my MCU didint have the FreeRTOS running?
 

nsaspook

Joined Aug 27, 2009
8,908
You need to be careful with IPC shared data, atomic updates and device resource sharing with preemptive task scheduling vs The One Big While Loop. The One Big While Loop has the same issues with interrupt or DMA tasks operating asynchronously to the main task state machine.

 

Thread Starter

zazas321

Joined Nov 29, 2015
926
Okay I see. But what if I have a simple SPI device, would I write a seperate FreeRTOS task to handle everything? I cant seem to get my head around that. From what I understand, I should not have any issues simply writing and reading register as if my MCU didint have the FreeRTOS running. If my SPI device collects some data (voltage, current). I can just read this data from any other task and I dont see a problem with that. Even if the task gets interrupted by an interrupts or any other task, it will continue where its left after the interrupt or other task completed. Am I missing something here?
 

nsaspook

Joined Aug 27, 2009
8,908
Okay I see. But what if I have a simple SPI device, would I write a seperate FreeRTOS task to handle everything? I cant seem to get my head around that. From what I understand, I should not have any issues simply writing and reading register as if my MCU didint have the FreeRTOS running. If my SPI device collects some data (voltage, current). I can just read this data from any other task and I dont see a problem with that. Even if the task gets interrupted by an interrupts or any other task, it will continue where its left after the interrupt or other task completed. Am I missing something here?
Sure, you could write one task to handle everything by polling registers but why waste you time with a RTOS if that's what you want. What you're missing is the need to buffer data in an interrupt or another task context while the main task runs and changes state with that buffered data. This is usually much more efficient than main waiting for each byte of data from some communications device. This data buffer is your IPC between dependent tasks, with one collecting data and another waiting for data. Some care or proper use of RTOS capabilities is needed to synchronize the two.

https://www.freertos.org/Embedded-RTOS-Queues.html
https://www.freertos.org/RTOS-task-notifications.html
 

Thread Starter

zazas321

Joined Nov 29, 2015
926
I still dont get it. Imagine I have 5 tasks running:
task1
task2
task3
task4
task5

Scenario 1:

Only task1 is dependant on spi device. This task needs to be able to communicate to the SPI device and can simply do so as if there was no FreeRTOS running.


Scenario 2:

task1 and task 2 are communicating with the SPI device. In this scenario, some extra care is needed because both tasks might try to access the same SPI device. I should use mutex or semaphore to ensure the existing communication is finished before the new one can started.

Scenario 3:

Only task1 is using SPI device (same as scenario1), however, some other tasks such as task2 and task3 are dependant on task1 ( if something changes in task1 due to the SPI device reading, it will affect task2 or task3 as well. I assume this can easily be handled by using some global variables. For examples, if something happens in task1 that needs to affect task2, I can simply notify task2 by using some global variable.

Is the above correct?
 

nsaspook

Joined Aug 27, 2009
8,908
That's basically correct but you don't want to use a raw global variable for the notification in Scenario 3. Use a proper RTOS IPC concept for those task changes.

In Scenario 1 simply because there is no formal RTOS running (the while loop state machine) doesn't mean there will be no concurrency issues if CPU efficiency increasing hardware DMA or interrupts are used to communicate with devices. Care must also be used there because we have similar issues with read-write-modify and atomic updates of variables.
 

Thread Starter

zazas321

Joined Nov 29, 2015
926
That's basically correct but you don't want to use a raw global variable for the notification in Scenario 3. Use a proper RTOS IPC concept for those task changes.

In Scenario 1 simply because there is no formal RTOS running (the while loop state machine) doesn't mean there will be no concurrency issues if CPU efficiency increasing hardware DMA or interrupts are used to communicate with devices. Care must also be used there because we have similar issues with read-write-modify and atomic updates of variables.
Thank you very much for clarifying. I have been further reading and learning about FreeRTOS and IPC techniques.Could you help me with my last few questions regarding FreeRTOS or RTOS in general?

1. Imagine I have a project which includes various sensors (Temperature , accelerometer, voltage and current sensor and etc), I also have to control GPIO, such as relays, leds. What is the general way to structure the FreeRTOS project for such tasks? Would I have FreeRTOS task for each sensor(logic component), for example:

1. task_temperature(takes temperature readings)

2. task_gpio ( controls everything related to gpio pins ( toggling LEDs and relays)

3. task_accelerometer (takes accelerometer readings)

4. task_voltage_and_current ( takes voltage and current readings from the sensor)


2. Some pseudo code describing the above tasks:

Code:
SemaphoreHandle_t mutex_i2c; // create global mutex_i2c
mutex_i2c = xSemaphoreCreateMutex();

         void task_temperature(void *argument)
        {
            for(;;)
            {   
                if(xSemaphoreTake(mutex_i2c,0)==pdTRUE){ // enter critical section if mutex is not locked yet
                    vTaskDelay(1000/portTICK_RATE_MS);
                    i2c_temperature = i2c_read_temperature_sensor();
                    xSemaphoreGive(mutex_i2c);// release the mutex so other process can start i2c transaction
                }
            }
        }



        void task_voltage_and_current(void *argument)
        {
            for(;;)
            {   
                if(xSemaphoreTake(mutex_i2c,0)==pdTRUE){ // enter critical section if mutex is not locked yet 
                    vTaskDelay(1000/portTICK_RATE_MS);
                    i2c_voltage = i2c_read_voltage();
                    i2c_current = i2c_read_current();
                    
                    xSemaphoreGive(mutex_i2c); // release the mutex so other process can start i2c transaction
            }
        }


         void task_gpio(void *argument)
        {
            for(;;)
            {   
                vTaskDelay(1000/portTICK_RATE_MS);
                if(i2c_voltage >= 10){
                    // toggle GPIO1
                }
                if(i2c_current >= 100){
                    // turn on safety relay
                }
                if(i2c_temperature >= 20){
                    // turn on fan relay
                }
            }
        }
1.As you can see for the code above, I have used mutex to protect the I2C communciation to ensure it does not get interrupted. Is that a correct way to handle this? What would be the problem if I didint have mutex? I would assume that i2c transaction could be interrupted in the middle of the transaction thus not properly meeting timing requirements. Is that correct?

2. Please could you help me understand how to properly send data from task_voltage_and_current and task_temperature over to task_gpio. As you can see task_gpio must know the i2c reading results to determine whether it needs to toggle some GPIO pins.

Thank you in advance.
 
Top