I started on the Arduino and moved to bear bones AVR from there. When I began writing my library (see signature) I looked at some HAL and RTOS options but thought they were way too heavy for such a small MCU. They used dynamic allocation and for small MCUs the idea of preemptive scheduling is way overkill IMO. So I wrote my own.
My library is in C++ with templates. Not many of you use C++ (or templates) from what I gather. Nonetheless, I think I managed to create a nice way to do a cooperative task without too much overhead or complexity for the dev.
I have created a set of macros (picked up from a C++ article on the web) that allows you to easily create a task class. Here's how one could look:
(stripped down version of the
TimeoutTask)
template<class BaseT, typename DelaysT, const uint32_t Timeout>
class TimeoutTask : public BaseT
{
public:
/** Call this method repeatedly from the main loop.
* Each time the Timeout expires the BaseT::OnTimeout() method is called.
*/
Task_Begin(Execute)
{
while (true)
{
Task_YieldUntil(DelaysT::Wait(BaseT::getId(), Timeout));
BaseT::OnTimeout();
}
}
Task_End
private:
uint16_t _task; // required by the Task macros
};
The DelaysT is a library class that counts down delay/wait timeouts.
You can see the Execute method has a while loop (!!) and waits until the timeout has passed and then calls the OnTimeout on the BaseT class.
Usage is very simple: Declare the tasks
TimeoutTask<OutputPinTask<13>, TaskSchedule, 1000> task1;
TimeoutTask<OutputPinTask<12>, TaskSchedule, 200> task2;
TimeoutTask<OutputPinTask<11>, TaskSchedule, 500> task3;
TimeoutTask<OutputPinTask<10>, TaskSchedule, 1500> task4;
And call them in the main loop (Arduino)
void loop() {
// update the scheduler - this counts down the wait-times
TaskSchedule::Update();
task1.Execute();
task2.Execute();
task3.Execute();
task4.Execute();
}
The TaskScheduler is that DelayT class (typedef Delays<Time<Milliseconds>, 4> TaskSchedule;).
To my eyes this is one of the most elegant solutions I have seen so far (not just because I made it
)
- Each Task is isolated in their own class.
- Multiple instances of the same Task can be used (like we do here).
- There is no dynamic allocation. RAM usage is minimal. A lot of the (template) params are hard coded.
My 2c's.