FreeRTOS C++ integration

Hello
I’ve done a spot of internet searching, and I’m working on a project that I’m trying to do with the future in mind, in that I am utilising C++ classes to define my architecture – I am working with FreeRTOS 10 as that is integrated in to the BSP I have for my development environment.
I can and have had FreeRTOS working using ‘C’ codebase - the LWiP examples and a good old blinky :slightly_smiling_face:
For my main application, I would like to make use of C++ and have found a couple of ‘add on’ libraries and I am initially looking at the FreeRTOScpp wrapper by Richard Damon.
However, being up against it timewise, I was hoping for some source code with examples utilising the add-ons as trying to understand the library code I am struggling a little - I learn best by seeing concrete code usage/examples.
Can anyone help?
Many thanks
Richard

I’ll admit that the documentation for my library is a bit sparse and could use some examples, the biggest problem with examples is most of the code I have access to is proprietary.

For task, I almost exclusively create them via a class. I will create a C++ class that handles the operation which derives from TaskClassS with a task() member function that has the task code. An instance of this class is created a file scope, so the task is created before main even starts, so main just needs to call vTaskStartScheduler().

Queue, Semaphores and Mutexes tend to be member variable of the classes that use them (maybe a class that defines a task, or maybe a class that just encapsulates an API for a device, like a serial port).

The biggest thing to be careful of is Queue, the type.a Queue stores must be a simple “POD” type, suitable for moving with memcpy, since that IS what FreeRTOS will do

Richard

Thank you for your reply.

Basically, I have a small embedded system, and yes, I would like to have a builder “task” since that needs to create various dynamic instances (screen buffers etc)… Right now, I’m pretty sure freertos is blocking my init code due to not being a task with defined heap

And then yes, a task that handles Comms via a serial port (usb CDC actually) and the usual blinky task to give some low level feedback.

By file level, you mean defined in main.cpp but outside of main() ? I have not seen that before, everyday is a school day!

Regards
Richard

It could be in main.cpp, but often in a different file, outside of any function.

This allows me to add functionality to the program by just adding the file that defines the class without needing to change main.

The only “code” that ever gets run are global constructors, the code of main, code in tasks, or code in ISRs.

All code shares the same “heap”, each task has its own “stack” (which is created from the heap when you create the task unless you use static memory when creating it)

I might look closer at that development model when we look at our next gen development architecture… the current development cycle is more ‘get it working’ while trying to evaluate good practices and evaluating things like FreeRTOS/library integration.
I’m also using the (Xilinx) Zinq7000 processor, so Vivado and Vitis development platforms add o the fun!

Our software intends to be a generic software base, but is customised at initialisation so bring up involves the dynamic creation/memory allocation and thereafter fairly static.

So a TaskS<128k> derived class to init the world, and then create as needed TaskClassS<???> classes to be the main ‘engine’ - a smattering of Queues and Semaphores to make everything “talk” :slight_smile:

Really appreciate the response/feedback

Maybe after my initial work push, i will think about a generic example for a Pi Pico board that can be (overly perhaps) designed to utilise the RTOS libraries :grin:

Regards
Richard

The template parameter to Task classes is the STACK size (in words) the task gets. 128k is an ENORMOUS stack, so unless your task needs a lot of large stack allocated buffers, that seems very large.

That parameter is NOT how much heap the task can use, as all tasks share a common heap, and the heap routines don’t limit usage by task.

Also, Tasks with defined stack sizes use STATICALLY allocated memory, so that is occupied by the Task object (presumed to be a global) so NOT reclaimed when the task ends, so I tend to not create an “Init” task that runs at the beginning and then self-destructs. Rather than start a task specifically to initialize, I create as much of the environment as possible with global objects whose constructors run automatically at startup (these are thus configured at compile time), with any final run time setup done as introductory code in some task that will be staying around, using the Timer startup hook if nothing better is available.

Yes, we have quite a bit of memory available…
in my case, I need two 41k buffers for two attached displays, and then storage for the images that will be transferred to the system… currently, 140 odd images, some of which are also full screen!

Still grateful for you spending the time and replying

Richard

Unless you might have multiple tasks each needing their own set of buffers, I would put those buffers in static memory, not on the stack.

Personally, I tend to associate the video memory with the driver for the video device, and not the task that is sending data to the display (since there can easily become more than one at some point).

The big advantage of using static and heap instead of stack for this sort of thing is you get better notice that you have run out. Stack overflow is the hardest type of thing to find and often ends up in unexplained crashes instead of immediately detection.

Richard
Indeed… it has turned out some of my problem being that the IDE (eclipse based!) crashed in a very bad way last week such that I had to create a new workspace and the projects within it (importing my sources)…
however, i forgot to change the defaults for memory in the linker def file, and indeed, in the freertos configuration (buried in Xilinx’s extension to Eclipse). The ‘numpty’ award for the week. :rofl:
That panic over, I am back to allocation from heap and will be adding a TaskClassS or two to the design as I go
Many thanks

Richard
After much head scratching i have solved a display comms (SPI) issue but now I need to get some nitty gritty ‘task’ related functionality going - and I’m on the back front so apologies for perhaps coming with questions that might have obvious answers!

I create a class files “TaskComms.cpp/h” with

class Task_Comms : public TaskClassS<2048>

If I put initialisation code for my USB stack in the constructor, then my processing code in the ::task() method – does this method need an internal for-ever loop or does it get repeatedly called?

And your recommendation on how I instantiate this class… (I would normally make it a singleton and initially reference it in my main() )

I ideally want to make the read comms have a timeout (so non blocking) – I can have a member of type Timer – and then when reading - i start the timer, and use the .active() ? method to allow a jump out of my loop.

Best regards
Richard

The task function is called once, and if it returns, the task is deleted, so the task function IS the event loop, and generally should have the for-ever loop.

Unless you are having an initialization order issue, I don’t use the singleton pattern, but just make the object as a global. I wouldn’t make a USB stack a singleton, because that says that if you ever go to a part with two USB interfaces, your whole design is broken, as it can only have one interface due to the singleton pattern. Instead I make the class and pass it a pointer/reference to the USB interface it is to use.

Since the USB driver is generally doing most of the I/O work in an ISR, a read request giving a buffer will tend to wait on a semaphore, which could have a timeout assigned. That is still called “Blocking”, as the task does still block for a while. A non-blocking read would be.a call to start the read, and the task periodically checks to see if it is done, and means the task needs to have something else to be doing in the mean time, which might indicate that it really should be two different tasks.