FreeRTOS implementation for MultiTasking

Hi,
I am new to FreeRTOS. I know the basic OS concepts but doesn’t have hands on experience.
I have an idea related to a complex application I want to build using FreeRTOS. Here is the idea I want to discuss to get the pros and cons and implement this.

Idea
I am interfacing the following devices to the 32 bit micro controller. The devices are LCD, Keypad, GPS, GSM, RTC(RealTimeClock), EEPROM , ADC and some sensors (temperature, Humidity and light sensor), serial monitor through RS232.

Here is the communication I have used for bare metal application. I want to use the same for the RTOS application.
LCD - GPIO
External RTC - I2C
Keypad - GPIO based External Interrupt
GPS - UART interrupt
GSM - UART interrupt
EEPROM - SPI interrupt
ADC - SPI interrupt
Serial Debug - UART interrupt
Sensors - Analog (Temperature and Humidity) and Digital Interface (Light Sensor).

Application Working
1.At system power on, GSM initialization has to be done. I am registering on the cellular network.
2.After GSM activity, GPS sync has to be done. This GPS module gets the GPS time, Latitude and longitude. Other than this the system has to get the time every day at particular interval. (For example 5:00 GMT every day). This syncing process takes almost 10 mins to get the GPS fix based on the climatic conditions.

  1. The time got from the GPS is loaded into the External RTC. This RTC has two alarm interrupts. I am using one alarm (15 minute based) interrupt for data transmission for every 15 minutes using GSM (FTP transmission) and another alarm interrupt for taking sensor readings (for every one minute) both TemperatureHumidty & Light sensor using ADC.

  2. The sensor readings are stored on the EEPROM device (based on the configurable interval).

  3. Along with this, the data has to be displayed on LCD based on the user request from the Keypad. The LCD has to display the current activities running on the LCD (for example, sensor readings, RTC time, GSM activity (signal strength), GPS reading (latitude, longitude).

  4. For knowing the internal happenings inside micro controller I am printing log to the serial console through debug port.
    Now, how to manage these activities without compromising other tasks. I want to use preemptive scheduling. Can anyone share ideas how to do this. Moreover, I want to dynamically allocate the memory and free it as I have to store the data in EEPROM for every one minute.
    Let’s discuss how to allocate resources and how many tasks are required (mutex, semaphore, queues, task notifications) for acheiving this application requirement.

As usual, there are many ways to go, and they can all be good.

I would think of all these peripherals as objects, and I would first define their methods. For example, a GPS will have methods like: get_coordinates(), get_time(), and a work() function, that polls for any changes.
Personally I like to create C++ classes and objects for each device or peripheral, but that is not necessary.

If a device is very complex or time-critical, you may decide to let it run in it’s own task. This task can create a Queue to receive commands from other tasks.

I would create one main task, that uses all these devices and sensors. You won’t see any technical details in this code. It is just giving CPU time to each device, and it passes on messages.

Note that “GPS” and “GSM” both become an object (with properties and methods), and each of them will make use of another object of the type “UART”.

Now, how to manage these activities without compromising other tasks

Like I said, give the task a Queue that receives commands. The task will be the owner of the device that it handles.

It looks like you’re going to make a big application, wish you good luck with it.

Hello Mr. Hein TiBosch,
Thanks for your reply. As per your say, I would create one main task, that uses all the devices and sensors.
But let me understand this line.
It is just giving CPU time to each device, and it passes on messages.

How to allocate time for each sub task in this main task? Please specify the method?
How to pass messages from main task to sub task and receive the messages from the sub task?
For example, to understand better in terms of code view. I have a main task along with sub tasks (
GPS_Task,
Sensor_Reading_Task,
GSM_Task,
Get_Time_Task,
LCD_Task,
Serial_Task,
One_Minute_Sensor_reading_Task,
Sensor_Store_Record_Task

void Main_Task(void)
{

//How to allocate time for each sub task in this main task? Please specify the method?
//How to pass messages from main task to sub task and receive the messages from the sub task.
}

void GPS_Task()
{
//How to receive the messages from the main task.
}

void Sensor_Reading_Task()
{
//Reads the sensor data for every 10 min
}

void GSM_Task()
{
//Transmits the data based on the periodic interval (let’s say for every 10 minutes).
}

void Get_Time_Task()
{
//Reads the current RTC registers (minutes, hours, seconds) to read every one minute
}
void LCD_Task()
{
//Displaying the current readings
}
void One_Minute_Sensor_reading_Task()
{
// read sensor reading for every minute.
}

void Sensor_Store_Record_Task()
{
//storing the sensor record in EEPROM
}

void Serial_Task()
{
//prints log to the debug console
}

Regards,
Kumar

Hi Kumar,

Creating 8 tasks to handle 8 devices is probably a bit expensive in terms of RAM usage. Each task needs a task control-block, its own stack, and probably a Queue for communication.
Also the complexity increases with each task added: think of shared resources and their locking, chances of deadlocks.

For instance a keyboard driver: it only needs to poll e.g. every 100 ms to see if a complete line of text has been entered. This can also be done in a state machine:

    int CKeyboard::work()
    {
        poll_keyboard();
        return check_for_EOL();
    }

The main task can call this work() function e.g. every 100 ms. That is how it “gives” CPU time to a state machine (not a task).

void main_task( void *pvParameter )
{
    for( ;; )
    {
        ulTaskNotifyTake( pdMS_TO_TICKS( 100uL ) );
        if( keyboard.work() )
        {
            /* Pass a message on to the command processor. */
            cli.process( keyboard.message() );
        }
    }
}

I would decide per device if it needs a task or if it can become a simple state machine.

An example: I have created an audio device: it can play audio streams from the Internet or from an SD-card. I came up with these tasks:

'IDLE'            // Obligatory
'MACB'            // Ethernet adapter
'IP-task'         // TCP/IP stack

'mainTask'        // The boss of all logic
'logbufTask'      // Sends logging at a low priority
'diskTask'        // Reads audio stream from a disk or from the Internet
'decodeTask'      // Decodes an audio stream
'recorderTask'    // Writes audio to a disk
'HTTP'            // A HTTP server, which serves as a GUI

These are all tasks that are either time-critical, or quite complex. It does have a keyboard, serial connections, and an LCD, but these devices are implemented as state machines. They’re polled by the main task.

Great! That’s a good idea to implement state machine in main task to attend all sub tasks.
I will create a timer task state machine to attend all the sub tasks in a main task.

Will I have to assign time slice for each task in the main task state machine? But the problem is I don’t know how much time the task is going to execute. I think I have to poll each sub task for certain time slice (eg:100ms). Then process each task. Is it correct?
Moreover, how to manage synchronization between each sub task and the interrupts. Can you explain which mechanism I have to use if possible? I am using preemptive scheduler.

In my case as you said, the Keypad Task, Serial Task(for Logging), and LCD are low priority tasks that can be in a state machine.
Where as Sensor Reading, GPS and GSM are high priority tasks that can be set as periodic tasks.

Regards,
Kumar.

Hi Mr. htibosch,
Can you please share the template of playing audio streams over etherent or sdcard as you discussed. So that I can have an idea how to manage state machine with main task. Please do the needful.

Regards,
Kumar

Here is a skeleton of how you could set up a streaming audio driver:
sample_audio_driver.c (4.2 KB)

It would be almost impossible to do this within a single task, because fread() and vDecode() are both blocking functions that take a considerable amount of time to complete.

If you have any questions about FreeRTOS features and functions, please have a look at the API reference and the FreeRTOS Kernel developer docs.

Attached code is not complete and it has not been tested. It is just for educational purposes. I hope it helps you getting a better understanding.

Thank you for the reply and the source. I will go through it.

Regards,
Kumar