[Scheduler Library]My new Scheduler can run tasks while only costs few RAM!

Release AVR_Scheduler v1.0.3 · SUNSETDEVER/AVR_TaskScheduler
YOU CAN DOWNLOAD IT FROM LIBRARY MANAGER NOW!

A Task scheduler based on millis()

Come and use! I need your suggestions! This is a cooperative task scheduling system, provides API like FreeRTOS and extra time functions like ulAVRGetDeltaMls(...). This is the first version of it and it can be use on many MCUs not only AVR series.

What is this scheduler different from others?
This scheduler is different from FreeRTOS, every task should be allocated in a task handle. Scheduler accesses the task handle to control the task. A task handle only costs few bytes in your RAM.
There is a special function called xGetCPUUsage() to measure the CPU performance. But this function only works in bare-metal MCUs

Now I 'm still learning in the Markdown layout(^_^)
I 'm trying to make my ENGLISH more natural because I 'm Chinese(U_U)

This is the header file you can view just now(if you are interest in)

// AVR_TaskScheduler.h
#ifndef AVR_TASKSCHEDULER_H
#define AVR_TASKSCHEDULER_H

#include <stdint.h>
#include <stdbool.h>

#if defined(ARDUINO) && ARDUINO >= 100
#include "Arduino.h"
#else
#include "WProgram.h"
#endif

// Version information
#define AVR_TASKSCHEDULER_VERSION_MAJOR 1
#define AVR_TASKSCHEDULER_VERSION_MINOR 1
#define AVR_TASKSCHEDULER_VERSION_PATCH 2  // Version number update

// Debug option
// #define AVR_TASKSCHEDULER_DEBUG 1

/// @brief Task state definitions
typedef enum {
    AVR_TASK_READY = 0,     ///< Task is ready to run
    AVR_TASK_RUNNING,       ///< Task is currently running
    AVR_TASK_SUSPENDED,     ///< Task is suspended
    AVR_TASK_DELETED        ///< Task has been deleted
} eAVRTaskState;

/// @brief Task callback function type
typedef void (*TaskFunction)(void *pvParameters);

/// @brief Task delay information structure
typedef struct {
    uint32_t ulDelayUntil;    ///< Time when delay ends
    bool bIsDelaying;         ///< Whether task is currently delaying
} xAVRTaskDelayInfo;

/// @brief Task control block structure
typedef struct avrTaskControlBlock {
    TaskFunction pvTaskCode;            ///< Task function pointer
    void *pvParameters;                 ///< Task parameters
    uint32_t ulInterval;                ///< Task execution interval (ms)
    uint32_t ulLastExecutionTime;       ///< Last execution time
    uint32_t ulExecutionCount;          ///< Execution count statistics
    eAVRTaskState eCurrentState;        ///< Current task state
    uint8_t ucPriority;                 ///< Task priority
    const char *pcTaskName;             ///< Task name
    xAVRTaskDelayInfo xDelayInfo;       ///< Task delay information
    struct avrTaskControlBlock *pxNextTask; ///< Next task pointer
} xAVRTaskHandle;

/// @brief Scheduler statistics structure
typedef struct {
    uint32_t ulTotalTasksExecuted;      ///< Total tasks executed
    uint32_t ulSchedulerCycles;         ///< Scheduler cycles count
    uint32_t ulMaxSchedulerTime;        ///< Maximum scheduler time
    uint32_t ulMinSchedulerTime;        ///< Minimum scheduler time
    uint32_t ulTotalDelayTime;          ///< Total delay time statistics
} xAVRSchedulerStats;

/// @brief Scheduler state structure
typedef struct {
    xAVRTaskHandle *pxFirstTask;        ///< First task in list
    xAVRTaskHandle *pxCurrentTask;      ///< Currently running task
    bool bSchedulerRunning;             ///< Scheduler running flag
    uint32_t ulSchedulerStartTime;      ///< Scheduler start time
    xAVRSchedulerStats xStats;          ///< Statistics
} xAVRSchedulerState;

typedef xAVRTaskHandle AVRTaskHandle_t;

// ========== Public API Functions ==========

/// @brief Create a task
/// @param pvTaskCode Task function pointer
/// @param pcTaskName Task name
/// @param pvParameters Task parameters
/// @param uxPriority Task priority
/// @param pxCreatedTask Pointer to store created task handle
/// @param ulInterval Task execution interval in milliseconds
/// @return true if task was created, false otherwise
bool vAVRTaskCreate(TaskFunction pvTaskCode, 
                   const char *pcTaskName,
                   void *pvParameters,
                   uint8_t uxPriority,
                   xAVRTaskHandle *pxCreatedTask,
                   uint32_t ulInterval = 0);

/// @brief Delete a task
/// @param pxTaskToDelete Pointer to the task handle to delete
void vAVRTaskDelete(xAVRTaskHandle *pxTaskToDelete);

/// @brief Suspend a task
/// @param pxTaskToSuspend Pointer to the task handle to suspend
void vAVRTaskSuspend(xAVRTaskHandle *pxTaskToSuspend);

/// @brief Resume a task
/// @param pxTaskToResume Pointer to the task handle to resume
void vAVRTaskResume(xAVRTaskHandle *pxTaskToResume);

/// @brief Get current tick count
/// @return Current tick count in milliseconds
uint32_t ulAVRTaskGetTickCount(void);

/// @brief Delay a task
/// @param ulDelayTime Delay time in milliseconds
void vAVRTaskDelay(uint32_t ulDelayTime);

/// @brief Delay a task until a specific time
/// @param pxLastWakeTime Pointer to last wake time
/// @param xFrequency Frequency in milliseconds
void vAVRTaskDelayUntil(uint32_t *pxLastWakeTime, uint32_t xFrequency);

/// @brief Check if a task is delaying
/// @param pxTask Pointer to the task handle
/// @return true if task is delaying, false otherwise
bool bAVRIsTaskDelaying(xAVRTaskHandle *pxTask);

/// @brief Get remaining delay time for a task
/// @param pxTask Pointer to the task handle
/// @return Remaining delay time in milliseconds
uint32_t ulAVRGetTaskRemainingDelay(xAVRTaskHandle *pxTask);

/// @brief Start the scheduler (blocking)
void vAVRTaskStartScheduler(void);

/// @brief Start the scheduler (non-blocking)
void vAVRTaskStartSchedulerNonBlocking(void);

/// @brief End the scheduler
void vAVRTaskEndScheduler(void);

/// @brief Schedule tasks
void vAVRTaskSchedule(void);

/// @brief Reset all task timers
void vAVRResetAllTaskTimers(void);

/// @brief Reset a specific task timer
/// @param pxTask Pointer to the task handle
void vAVRResetTaskTimer(xAVRTaskHandle *pxTask);

/// @brief Get current task handle
/// @return Pointer to the current task handle
xAVRTaskHandle *pxAVRGetCurrentTaskHandle(void);

/// @brief Get task state
/// @param pxTask Pointer to the task handle
/// @return Task state
eAVRTaskState eAVRGetTaskState(xAVRTaskHandle *pxTask);

/// @brief Get task name
/// @param pxTask Pointer to the task handle
/// @return Pointer to the task name string
const char* pcAVRGetTaskName(xAVRTaskHandle *pxTask);

/// @brief Get task execution count
/// @param pxTask Pointer to the task handle
/// @return Task execution count
uint32_t ulAVRGetTaskExecutionCount(xAVRTaskHandle *pxTask);

/// @brief Set task execution interval
/// @param pxTask Pointer to the task handle
/// @param ulNewInterval New interval in milliseconds
void vAVRSetTaskInterval(xAVRTaskHandle *pxTask, uint32_t ulNewInterval);

/// @brief Get task execution interval
/// @param pxTask Pointer to the task handle
/// @return Task execution interval in milliseconds
uint32_t ulAVRGetTaskInterval(xAVRTaskHandle *pxTask);

/// @brief Set task priority
/// @param pxTask Pointer to the task handle
/// @param ucNewPriority New priority value
void vAVRSetTaskPriority(xAVRTaskHandle *pxTask, uint8_t ucNewPriority);

/// @brief Get task priority
/// @param pxTask Pointer to the task handle
/// @return Task priority value
uint8_t ucAVRGetTaskPriority(xAVRTaskHandle *pxTask);

/// @brief Get number of tasks
/// @return Number of tasks
uint8_t ucAVRGetNumberOfTasks(void);

/// @brief Get scheduler uptime
/// @return Scheduler uptime in milliseconds
uint32_t ulAVRGetSchedulerUptime(void);

/// @brief Get scheduler statistics
/// @return Scheduler statistics structure
xAVRSchedulerStats xAVRGetSchedulerStats(void);

/// @brief Reset scheduler statistics
void vAVRResetSchedulerStats(void);

/// @brief IDLE task function
/// @param pvParameters Task parameters
void IDLETask(void* pvParameters);

/// @brief Get CPU usage
/// @return CPU usage percentage
float xGetCPUUsage(void);

/// @brief Initalize AVRScheduler
void AVRSchedulerInit(void);

/// @brief Check if millisecond time has elapsed
/// @param timeValue Time value to check
/// @param startTime Start time
/// @return true if time has elapsed, false otherwise
bool AVRDeltaMlsCheck(uint32_t timeValue, uint32_t startTime);

/// @brief Get elapsed milliseconds
/// @param startTime Start time
/// @return Elapsed time in milliseconds
uint32_t ulAVRDeltaMlsGet(uint32_t startTime);

/// @brief Check if microsecond time has elapsed
/// @param timeValue Time value to check
/// @param startTime Start time
/// @return true if time has elapsed, false otherwise
bool AVRDeltaMicroCheck(uint32_t timeValue, uint32_t startTime);

/// @brief Get elapsed microseconds
/// @param startTime Start time
/// @return Elapsed time in microseconds
uint32_t ulAVRDeltaMicroGet(uint32_t startTime);

// Global scheduler instance
extern xAVRSchedulerState xAVRScheduler;
#endif // AVR_TASKSCHEDULER_H

Could you provide an English translation of the readme.md?

There is also a layout problem with the layout of the quick start section

There is Arduino_FreeRTOS.h Library with which we can do multitasking on Arduino UNO R3. What is the advantage of your Scheduler?

Zephyr is the goto scheduler now, it is both co-operative and pre-emptive.

I fixed it on the latest. I don't know how to use markdown, so I can't fix the QuickStart part, I 'm sorry.(QWQ)

My Scheduler costs less RAMs to run a task. Only few bytes are using for a taskhandle. You can have more RAMs for your main program.

@sunsetdever

Created a PR with an update for readme.md

you could consider

  • a build workflow to have quality tests in place for PR's.
  • a lint check
  • a separate changelog.md file

I opened your one example. However, you can post here a simple two led (running at at different rates) multi-tasking sketch so that I can compare it with that of Arduino-FreeRTOS.h.

Thank you for co-operation.

Welcome!

Thanks for sharing!

Your topic does not indicate a problem with IDE 2.x and therefore has been moved to a more suitable location on the forum.

Is the Zephyr core available for AVR based boards?

Don't know.

OK, no problem. In the first version, I actually included a benchmarking example like you suggested, but I deleted it because I thought it was useless. However, I now realize that adding it back would be much more useful for showing the differences between the other schedulers. I will also print the CPU usage (which can only be measured on bare-metal MCUs).

// DifferentRateExample.ino
#include <AVR_TaskScheduler.h>

#define LED_PIN1 2 //One extern LED
#define LED_PIN2 LED_BUILTIN //Builtin LED

// 任务句柄
AVRTaskHandle_t xTask1, xTask2, xTask3;//Every task should be have a taskHandle.

void vTaskBlinkLED1(void *pvParameters) {
    static bool ledState = false;
    digitalWrite(LED_PIN1, ledState);
    ledState = !ledState;
    
    #ifdef AVR_TASKSCHEDULER_DEBUG
    static uint32_t count = 0;
    Serial.print("LED Task1 executed: ");
    Serial.println(count++);
    #endif
    vAVRTaskDelay(100);
}

void vTaskBlinkLED2(void *pvParameters) {
    static bool ledState = false;
    digitalWrite(LED_PIN2, ledState);
    ledState = !ledState;
    
    #ifdef AVR_TASKSCHEDULER_DEBUG
    static uint32_t count = 0;
    Serial.print("LED Task2 executed: ");
    Serial.println(count++);
    #endif
    vAVRTaskDelay(500);
}

void vTaskPrintCPUUsage(void *pvParameters) {
    char ret[10];
    sprintf(ret, "CPU: %.1f", xGetCPUUsage());
    Serial.println(ret);
    vAVRTaskDelay(500);
}

void setup() {
    // 先初始化硬件
    Serial.begin(115200);
    pinMode(LED_BUILTIN, OUTPUT);
    
    // 短暂的硬件稳定延迟
    delay(100);
    
    Serial.println("AVR Task Scheduler - Basic Example (Fixed)");
    Serial.println("Initializing tasks...");
    
    if (!vAVRTaskCreate(vTaskBlinkLED1, "LED Blink1", NULL, 1, &xTask1, 0)) {
        Serial.println("Failed to create LED task1!");
    }

    if (!vAVRTaskCreate(vTaskBlinkLED2, "LED Blink2", NULL, 1, &xTask2, 0)) {
        Serial.println("Failed to create LED task2!");
    }

    if (!vAVRTaskCreate(vTaskPrintCPUUsage, "Voltage Output", NULL, 1, &xTask3, 0)) {
        Serial.println("Failed to create CPU Usage task!");
    }
    
    vAVRTaskStartSchedulerNonBlocking();
    
    Serial.println("Scheduler started successfully!");
    Serial.print("Number of tasks: ");
    Serial.println(ucAVRGetNumberOfTasks());
    Serial.print("Scheduler start time: ");
    Serial.println(ulAVRTaskGetTickCount());
}

void loop() {
    vAVRTaskSchedule();
}

Thank you! I couldn't find a suitable topic because I 'm new there. :slight_smile:

So you will provide example tests using all the different schedulers eventually so we can truly evaluate yours?

Could you explain what the benefit to use a scheduler based on millis if we have a millis() itself?

It is hard to compare a scheduler with RTOS. The performances in some conditions are almost the same and some are not. In another word, It is difficult to compare the cooperative mode with the preemptive mode. Every mode has its own benefit.

Why do we have to use the hardware driver library if we have digitalWrite(...)?

Are you referring to Arduino.h Library?

Zephyr OS does not have support for AVR: