FreeRTOS Time Slice scheduler and Bluetooth C code

cancel
Showing results for 
Show  only  | Search instead for 
Did you mean: 

FreeRTOS Time Slice scheduler and Bluetooth C code

3,252 Views
kohoutm2
Contributor I

Hello,

I'm starting new project with FreeRTOS running at Kinetis MKW36 MCU with Bluetooth. I'm going to reuse NXP Bluetooth stack and example code for Bluetooth. In all NXP examples I've seen the Time Slice scheduler for RTOS is disabled (#define configUSE_TIME_SLICING 0). So RTOS task switching has to be done explicitly. Is there any reason why the Time Slice scheduler is disables? For example that the Bluetooth stack or C code examples are not "compatible" with Time Slice scheduler?

Labels (1)
10 Replies

2,982 Views
ErichStyger
Senior Contributor V

I agree: having configUSE_TIME_SLICING set to zero is very unusual, and I don't know what the reasons in the stack examples would be. And you don't have to do task switching explicitly (the scheduler is still preemptive and not cooperative), but the scheduler will *not* balance time between tasks of equal priority. Means if your highest priority task does not block and if there are other task with the same priority, the scheduler will not switch to another task of equal priority at tick time.

It only means that context switches will happen less, and that a task having the CPU will keep it having. If (what I don't hope) the stack would rely on someting (e.g. to create a kind of critical section?), I would challenge the architcture and doing something as this won't be very reliable anyway.

I hope this helps,

Erich

0 Kudos

2,982 Views
myke_predko
Senior Contributor III

Martin and Erich,

I wanted to enter this conversation on a few points and ask a few questions about how your are doing things.  I'm running several FreeRTOS applications with "#define configUSE_TIME_SLICING 0" and letting my application select the appropriate executing task without any issues.  I believe that I've archtected my applications in such a way that I won't experience the issues that you do.  

First off, let me say that I think the FreeRTOS examples are terrible; they do not provide anything close to a good example of how the demo interface should be used in a FreeRTOS application.  Most often, they seem to be the bare metal code with just "xTaskCreate" for the functions and a "vTaskStartScheduler" with minimal thought to things like task priorities, stack sizes or how a user would use their code in their applications.  I seriously recommend not trying to use the FreeRTOS demo code in applications - start with the bare metal examples and port them into FreeRTOS tasks that perform the work that you require.  If an author of these examples is out there and offended; please contact me for my opinion on how they should be written to provide meaningful examples to users.  

While I'm dissing NXP FreeRTOS code, I'm also going to say that one of the things I've learned is to NOT use the FreeRTOS drivers in the SDKs.  The reason is somewhat subtle and philosophical - the drivers, from what I can see are well written but they're written for the general case; the code is architected in a way that it would work well in a multi-user system in which multiple tasks are going to access the resource but when we're talking about single purpose MCU, they're overly complex and not optimized for virtually all applications that users are going to be developing for.  

We're blessed with inexpensive chips with a plethora of IO options - that means that the goal of an application should be that each device provides a single function and each peripheral on the device has it's own specific IO.  This probably seems wasteful and inefficient, but I would argue that it greatly simplifies hardware system design as well as application software development and debugging and will result in a cheaper overall product.  The only interfaces that I would consider sharing are I2C and CAN (as "intraSystem" tasks below) as communication data packets include the destination address and there's no need to complicate things with semaphores and mutexes.  

My first questions to you are:

  • Why do you have any data processing tasks that are of the same priority? 
  • Why do you have multiple data processing tasks? 
  • How are you breaking up your task functions and how do you expect them to operate? 

In my (Free)RTOS applications, I have four types of tasks:

  1. Execution initiated by a hardware interrupt (Typically Intersystem Communications and User Inputs)
  2. Execution initiated by a timer delay (Typically repeating Sensor functions)
  3. Execution initiated by another task (User Interface Processing)
  4. Always executing (What I call a "Data Processing" Task/The Basic Application)

These tasks are prioritized using the include file:

/*
* Task_priorities.h
*
* Created on: Apr. 29, 2020
* Author: myke
*/

#ifndef TASK_PRIORITIES_H_
#define TASK_PRIORITIES_H_

#if ((defined configMAX_PRIORITIES) && (10 > configMAX_PRIORITIES))
#error TOO FEW configMAX_PRIORITIES

#elif (!(defined configMAX_PRIORITIES))

#error configMAX_PRIORITIES NOT Defined

#endif


enum { interSystemTask_Priority   = (configMAX_PRIORITIES - 2)
     , intraSystemTask_Priority   = (configMAX_PRIORITIES - 3)
     , sensorTask_Priority        = (configMAX_PRIORITIES - 4)
     , actuatorTask_Priority      = (configMAX_PRIORITIES - 5)
     , fileSystemTask_Priority    = (configMAX_PRIORITIES - 6)
     , userInterfaceTask_Priority = (configMAX_PRIORITIES - 7)
     , consoleTask_Priority       = (configMAX_PRIORITIES - 8)
     , dataProcessing_Priority    = (configMAX_PRIORITIES - 9)
     };


#endif /* TASK_PRIORITIES_H_ */

I prioritize tasks using the philosophy that for a task to execute as fast as possible, all the called tasks are a higher priority.  By doing this, I don't have any task starvation issues.  

Just to explain the task functions:

  • interSystem - Communications with other (typically host) systems.  Examples are USB, Ethernet, BT & UART.  If the application is an I2C or CAN multi-master or slave then these interfaces could be considered at this level
  • intraSystem - Communications with devices within the application.  This typcally is I2C, CAN, SPI, UART
  • sensor - ADC, Digital Inputs, Temperature sensors, etc.  If they are not on the chip, then the task communicates with the interface task at the "intraSystem" level (higher priority).  I should point out that the run time task for run time stats (Erich's Code) is run at this priority level and I use it for measuring dataProcessing delays.  
  • actuator - Digital Outputs, PWM, etc.  The assumption here is that some actuators may require sensor data for correct operation
  • fileSystem - At the lowest level simple non-volatile storage of data.  At a higher level, a FAT file system
  • userInterface - LEDs, LCDs, etc.  Buttons, knobs are all "sensors" and their values are requested from the userInteface
  • consoleTask - USB device/UART/Telnet to access system and provide monitoring/debug interface
  • dataProcessing - Doing the work using data from higher priority tasks and sending commands to other tasks

How are you architecting your applications?  I should point out that in the structure above, I only expect that the userInterface and dataProcessing tasks operate autonomously.  Side tasks that operate autonomously very quickly complicate things and makes debugging/validating the application much more difficult.  The userInterface provides information to the user and may save user inputs until required by the dataProcessing task or send them directly to the dataProcessing task - in rereading what I've written, I'm giving the impression that userInterface operates continuously and that's not the case; userInterface waits on a timer or message from another task to execute.  For all intents and purposes dataProcessing replaces the FreeRTOS IDLE task and is continuously operating (although the console task may halt dataProcessing if a user is connected and needs to directly access different system function tasks).  

I feel like I'm missing something the way that you are architecting and specifying your data processing tasks - could you please describe them?  I'm curious to know if my model would work with them.  

Now, FreeRTOS is a bit different from other RTOS's I've worked with as sending a message doesn't automatically call the scheduler - after putting the message on the receiving task's queue, execution returns to the caller which I think is part of your issues.  To work with this, I use a "send/receive" process in which the sender is blocked (which initiates the scheduler) until it receives a reply from the receiver (after which the receiver waits for another message, becoming blocked and causing the scheduler to resume caller execution).  For example to send a request message and wait for its completion I use the method:

void static inline flashMsgTXRX(struct flashQueueMsg* txMsg
                              , struct flashQueueMsg* rxMsg) {
  xQueueSend(flashQUEUE_Handle
           , (void*)txMsg
           , portMAX_DELAY);
  xQueueReceive(flashRXQUEUE_Handle
              , (void*)rxMsg
              , portMAX_DELAY);
}

This actually works really well with the priority scheme as the higher priority tasks are blocked waiting for a message to come in, at which point they service the request and then reply that it is complete.  

I must confess there is one case that I do cheat on getting data from tasks (that my old university RTOS professor would probably roast me for) and that is getting simple data that a task is storing - rather than sending a message requesting the data and then waiting to receive it I access the variable in the task using the inline method:

uint32_t static inline rtpSensorValueRead(uint32_t dimension) {
extern volatile uint32_t rtpSensorValue[2];
  return rtpSensorValue[dimension];
}

In deference to my professor; he was adament that a task could not directly access any data in another task - this code prevents a task from modifying the other task's variable, which is in the spirit of the professor's dictum. 

It should be noted that FreeRTOS provides a copy of the message to the receiver, not the actual data or a pointer to the data so the rule of not accessing another task's data is followed and by always waiting for a response, I don't get into a situation where the sender continues updating its data which may result in a race condition.  

I *think* I have shown how I have architeched my FreeRTOS applications in such a way that I am not going to experience the issues that you are having, but I'd be interested in your comments back.  

myke

0 Kudos

2,982 Views
ErichStyger
Senior Contributor V

Hi myke,

Lots of points raised by you. I feel this would deserve its own thread instead of diverting this one.

It reminds me about one (or more?) articles I have drafted up but never released on that subject.

There is nothing 'wrong' with your approach, but there are certainly alternatives.

As for FreeRTOS and task priorities, I try to keep the number of priorities small (say 4 to 5), with having the 'urgent and short running' ones in the top priority range and the 'less urgent and longer running' in the lower priority band.

As for IPC (interprocess communication) I'm using mixed methods: from direct task notification to semaphore up to message queues with pointers to the data. I architect things in a way so I can be agnostic of the scheduler details except that 'it is running in preemptive mode': with this I don't have issues running tasks at the same priority, but usually I avoid to run any task at IDLE priority except the IDLE task.

As for the NXP examples: to me they are what they are: simple examples. Nothing really suited (or even intended) to end up in 'production' code. I use them simply as an inspiration and as starting point for my own implementation. And I think this is the main purpose of example code anyway.

Erich

0 Kudos

2,982 Views
myke_predko
Senior Contributor III

Hi Erich,

Thanx for the reply.  I would welcome this being its own thread as I think discussions about task organization and prioritization would be useful to everyone here.  

I'm hard on the FreeRTOS demos and think they are unacceptable because they are a resource for new FreeRTOS developers.  I have been writing RTOS code for literally 35 years and when I moved over to FreeRTOS from MQX and looked at the NXP demo code I was really given a very poor impression of FreeRTOS and it wasn't until I went to freertos.org that I could see that FreeRTOS was a serious tool.  If your first experience with FreeRTOS (and RTOS's in general) comes primarily from the NXP demos, you're going to have a lot of problems going forwards when you are creating more complex applications.  

I would love to have a discussion regarding your approach to prioritizing tasks - I'm curious as to how you (and other people) plan out your applications.  I came up with the categorization of the tasks over many years (including several RTOS's including uCONS-III; sorry bobpaddock) because I found that when mapping out custom priorities for an application to be wasteful in terms of time, invited discussion between team members, and with the tasks categorized with set priorities that are predefined. 

I'm curious about people's vision of the application this is exemplified by your comment about prioritizing tasks that are "urgent and short running" - I've found that over the years the two rarely coexist.  This is why I place my communciations/sensor tasks as the highest priority and keep their operation as short as possible but processing the incoming data is done at a much lower priority and can be done calmly without worrying about starving other tasks.  As I alluded to in my long email I tend to model my task code as providing a single primary application in the device and minimize peripheral sharing.  Most of the examples that I see are much more general, designed for multiple tasks sharing the resource (hence my comment about the FreeRTOS drivers), and I question this approach for Kinets/LPC and other SoC applications because it's more complex, requires more resources/code with the associated execution overhead and is generally not required when you look at the application from a high level.  

Maybe I'm completely off base with this view and I'd be interested in understanding what kind of applications people are doing with FreeRTOS - I'm doing robotics primarily but with fairly complex UIs (the next one will have a touch screen OLED UI and I'm planning on adding streaming video from an onboard camera) and intersystem communications (USB HID and CDC, BT and TCP/IP) and I can usually define the application to be one primary data processing task with the console task providing the user with direct control over the hardware if required.  I have one (QNX) application where I have multiple data processing tasks and rather than relying on the RTOS to provide time slicing, I scheduled their execution using a round robin - the primary requirement was for deterministic execution of the data processing tasks.  

Going back to the original point, I would like to see FreeRTOS examples where there tasks at the same priority level where one is being starved - you and kohoutm2@o2active.cz‌ seem to have this issue with #define configUSE_TIME_SLICING 0.  I'm curious to see what's happening here and what are the application requirements that resulted in this situation.  

myke

0 Kudos

2,982 Views
mario_castaneda
NXP TechSupport
NXP TechSupport

Hi Myke, 

I hope you are doing great.

The BLE example codes are not defined with configUSE_TIME_SLICING, this is because the BLE Stack, it requires specific time defined as the specification. For example, the BLE defines a connection interval is defined at the beginning of the connection, so every wake up the device must receive or transmit some data for not lose the connection. The BLE stack has the highest priority. For FreeRTOS OS only, mutexes are implemented with the priority inheritance mechanism.

For better information please look at the Connectivity Framework Reference Manual documentation included in the SDK.

Also, you could look at the next community post, how to create tasks and queues.

Using the QN908x FreeRTOS BLE Abstraction Layer Stack to create Tasks and Queues. 

Regards,

Mario

0 Kudos

2,982 Views
myke_predko
Senior Contributor III

Hey Mario,

I'm doing excellent, I hope you are doing at least as well.  

Thank you for replying - I didn't realize that FreeRTOS tickless operation was not possible in BT enabled chips.  

When I look at the link (Using the QN908x FreeRTOS BLE Abstraction Layer Stack to create Tasks and Queues.) I can follow it but I would comment that it is *very* device specific and somewhat indicative of the experience the writer has with the systems although the use of the "OSA_MsgQCreate" API doesn't match what I have in the SDK RM.  Not having the code indented in the link makes it a lot harder to follow - I know that's a problem when you're cut and pasting code into the quote box. 

The overall approach to how the task and message queue are specified in the link is good but it is not followed in the various FreeRTOS demos that are importable in MCUXpresso (which is exactly what I'm complaining about) - in the examples I've downloaded there is code (I can't find it right now) where task and other RTOS resources are allocated within other running tasks at arbitrary times which makes reading through the application and understanding the execution flow very difficult. 

Again, I'm not looking for code that can be imported directly into an application, but code that can be followed and easily understood and not leaving the new user asking why something like an "xTaskCreate" appears in an unexpected place in a task.

Anyway, in my non-BT chip enabled applications, I am able to run tickless without any task starvation issues which is what I was curious about in the first place.  I'm still (and always) curious about how people architect their code so I can learn from their approach to doing things.  

Again, thank you for the example and keep well,

myke

0 Kudos

2,982 Views
mario_castaneda
NXP TechSupport
NXP TechSupport

Hi Myke,

That is great. 

The reason for this different behavior is because the wireless examples don't provide flexibility adding queues or tasks, we want to avoid any code that could delay the connectivity process in the BLE stack.

Also, our example could implement Tickless following the next guide Connectivity Software: Implement tickless mode in FreeRTOS 

Please do not hesitate to contact us for any further questions that you have.

Regards,

Mario

2,982 Views
myke_predko
Senior Contributor III

Thanx Mario.

2,982 Views
bobpaddock
Senior Contributor III

An alternative to FreeROTS is uCOS-II and uCOS-III where they and  most of their stacks are now Open Source 

With Apache License.

https://github.com/SiliconLabs?q=uC-&type=&language=

https://doc.micrium.com/display/ucos/ 

Licensing | Micrium https://github.com/SiliconLabs?q=uC-&type=&language=


While I'm biased, you find my name in the first edition of the uCOS-II book, I've always thought they were better written than FreeRTOS.

0 Kudos

2,982 Views
kohoutm2
Contributor I

Thanks for the answer. The questions still persist. Is it safe to use Time Slice scheduler together with Bluetooth stack and example code? 

As I look at other NXP FreeRTOS Kinetis MKW36 examples (without Bluetooth) the Time Slice scheduler is always disabled. And some FreeRTOS examples relay on Time Slice scheduler to be disabled because there is missing correct task synchronization. It makes me nervous because I have selected NXP for new project and I do not want to be in troubles in later project phase.

0 Kudos