【码农日常】时间触发嵌入式系统设计模式

文章目录

    • 概要
    • 整体架构
    • 技术细节
    • 小结

概要

最近在学习FOC电机控制算法时,遇到了一种比较精简的OS系统,时间触发嵌入式系统,适用于资源紧张,低成本的电控方案。
查阅资料发现,该系统设计思路起源于8051单片机,但在如今ARM横行的时代依然大有用途。

整体架构

时间触发嵌入式系统说穿了就是通过时间划片,采用调度器任务的一种处理方式,与我们熟知的freetos或μcos并没有根本的区别。

整体架构的核心包括:
定时器:基于硬件底层实现
调度器:
调度器数据结构
初始化函数
中断服务程序ISR,用来以一定的间隔刷新调度器
想调度器增加任务的函数
使任务在应当运行的时候呗执行的调度函数
从调度器删除任务的函数(并不是所有系统都需要)
任务:
自定义部分

技术细节

以下是这个调度器的全部代码。

/*!
* @file task_scheduler.c
*
* @brief This file provides task scheduling based on millisecond time base functions.
*
*/

/* ===========================================  Includes  =========================================== */
#include "task_scheduler.h"

/* ============================================  Define  ============================================ */

/* ===========================================  Typedef  ============================================ */

/* ==========================================  Variables  =========================================== */
TASK_SCHEDULER g_SchTask[SCH_TASKS_SEQUENCE_MAX];   /*!< Task scheduling structure variable */
uint8_t g_ErrorCode = 0;                            /*!< Task scheduling fault code */
static uint32_t g_ErrorTickCnt;                     /*!< Task scheduling fault detection count */
static uint8_t g_ErrorCodeBuf;                      /*!< Task scheduling fault code previous value */

/* ====================================  Functions declaration  ===================================== */

/* ======================================  Functions define  ======================================== */
/*!
* @brief Delete the task in scheduling sequence.
*
* @param[in] task_index: task sequence index
* @return none
*/
void SCH_Delete_Task(const uint8_t task_index)
{
    if (g_SchTask[task_index].pTask == 0)
    {
        /*
        * No task at this location...
        * Set the global error variable
        */
        g_ErrorCode = ERROR_SCH_CANNOT_DELETE_TASK;
        /* Also return an error code */
    }
    else
    {
    }

    g_SchTask[task_index].pTask   = 0x0000;
    g_SchTask[task_index].delay   = 0;
    g_SchTask[task_index].period  = 0;
    g_SchTask[task_index].runOrder   = 0;
}

/*!
* @brief Report the task scheduler error.
*
* @param[in] none
* @return none
*/
void SCH_Report_Status(void)
{
    #ifdef SCH_REPORT_ERRORS
    /*
    * ONLY APPLIES IF WE ARE REPORTING ERRORS
    * Check for a new error code
    */
    if (g_ErrorCode != g_ErrorCodeBuf)
    {
        g_ErrorCodeBuf = g_ErrorCode;

        if (g_ErrorCode != 0)
        {
            g_ErrorTickCnt = 60000;
        }
        else
        {
            g_ErrorTickCnt = 0;
        }
    }
    else
    {
        if (g_ErrorTickCnt != 0)
        {
            if (--g_ErrorTickCnt == 0)
            {
                /* Reset error code */
                g_ErrorCode = 0;
            }
        }
    }
    #endif
}

/*!
* @brief Dispatch the tasks.
*
* @param[in] none
* @return none
*/
void SCH_Dispatch_Tasks(void)
{
    uint8_t index;
    /* Dispatches (runs) the next task (if one is ready) */
    if (g_SchTask[0].runOrder > 0)
    {
        for (index = 0; index < SCH_TASKS_SEQUENCE_MAX; index++)
        {
            if (g_SchTask[index].runOrder > 0)
            {
                /* Run the task */
                (*g_SchTask[index].pTask)();
                /* Reset / reduce runOrder flag */
                g_SchTask[index].runOrder -= 1;
                /* Periodic tasks will be scheduled to run again */

                /* if this is a 'one shot' task, remove it from the array */
                if (g_SchTask[index].period == 0)
                {
                    SCH_Delete_Task(index);
                }
            }
        }
    }

    /* Report system status */
    SCH_Report_Status();
}

/*!
* @brief Manage the task sequency.
*
* @param[in] void (* pFunction)(), pointer to task function
* @param[in] const uint32_t delayTime, delay time of task base on ms time
* @param[in] const uint32_t taskPeriod, time period of task
* @return none
*/
void SCH_Add_Task(void (* pFunction)(), const uint32_t delayTime, const uint32_t taskPeriod)
{
    uint8_t index = 0;

    /* First find a gap in the array (if there is one) */
    while ((index < SCH_TASKS_SEQUENCE_MAX) && (g_SchTask[index].pTask != 0))
    {
        index++;
    }

    /* Have we reached the end of the list? */
    if (index == SCH_TASKS_SEQUENCE_MAX)
    {
        /*
        * Task list is full
        * Set the global error variable
        */
        g_ErrorCode = ERROR_SCH_TOO_MANY_TASKS;
        return;
    }

    /* If we're here, there is a space in the task array */
    g_SchTask[index].pTask  = pFunction;
    g_SchTask[index].delay  = delayTime;
    g_SchTask[index].period = taskPeriod;
    g_SchTask[index].runOrder  = 0;
}

/*!
* @brief Task scheduler update base on TIMER ISR.
*
* @param[in] none
* @return none
*/
void SCH_Update(void)
{
    uint8_t index;
    /* NOTE: calculations are in *TICKS* (not milliseconds) */
    for (index = 0; index < SCH_TASKS_SEQUENCE_MAX; index++)
    {
        /* Check if there is a task at this location */
        if (g_SchTask[index].pTask)
        {
            if (g_SchTask[index].delay == 0)
            {
                /* The task is due to run */
                g_SchTask[index].runOrder += 1;
                if (g_SchTask[index].period)
                {
                    /* Schedule this regular task to run again */
                    g_SchTask[index].delay = g_SchTask[index].period - 1;
                }
            }
            else
            {
                /* Not yet ready to run: just decrement the delay */
                g_SchTask[index].delay -= 1;
            }
        }
    }
}

/*!
* @brief 1 ms time base tasks, all the 1 ms tasks executing here.
*
* @param[in] none
* @return none
*/
void Task0_1ms(void)
{
    Motor_1ms_Task();
}

/*!
* @brief 2 ms time base tasks, all the 2 ms tasks executing here.
*
* @param[in] none
* @return none
*/
void Task1_2ms(void)
{
    Motor_2ms_Task();
}

/*!
* @brief 4 ms time base tasks, all the 4 ms tasks executing here.
*
* @param[in] none
* @return none
*/
void Task2_4ms(void)
{
    Motor_4ms_Task();
}

/*!
* @brief 6 ms time base tasks, all the 6 ms tasks executing here.
*
* @param[in] none
* @return none
*/
void Task3_6ms(void)
{
    Motor_6ms_Task();
}

/*!
* @brief 10 ms time base tasks, all the 10 ms tasks executing here.
*
* @param[in] none
* @return none
*/
void Task4_10ms(void)
{
    Motor_10ms_Task();
}

/*!
* @brief 100 ms time base tasks, all the 100 ms tasks executing here.
*
* @param[in] none
* @return none
*/
void Task5_100ms(void)
{

}

/*!
* @brief 1000 ms time base tasks, all the 1000 ms tasks executing here.
*
* @param[in] none
* @return none
*/
void Task6_1000ms(void)
{

}

/*!
* @brief 2000 ms time base tasks, all the 2000 ms tasks executing here.
*
* @param[in] none
* @return none
*/
void Task7_2000ms(void)
{

}

/*!
* @brief Task scheduler main processer, called by Main function.
*
* @param[in] none
* @return none
*/
void Task_Scheduler(void)
{
    /* Notice: the delay of each task sequency cannot be the same! */
    /*
    * We can add a new time base task as the follow type:
    * SCH_Add_Task(New_task_function, task_sequence_number, task_time_base);
    * where New_task_function is the name of the task name;
    * task_sequence_number is the position of the task in all task sequences;
    * task_time_base is the time base of the task, indicating its execution cycle.
    */
    /* 1ms time base tasks */
    SCH_Add_Task(Task0_1ms, 0, 1);
    /* 2ms time base tasks */
    SCH_Add_Task(Task1_2ms, 1, 2);
    /* 4ms time base tasks */
    SCH_Add_Task(Task2_4ms, 2, 4);
    /* 6ms time base tasks, reserve */
    SCH_Add_Task(Task3_6ms, 3, 6);
    /* 10ms time base tasks */
    SCH_Add_Task(Task4_10ms, 4, 10);
    /* 100ms time base tasks, reserve */
    SCH_Add_Task(Task5_100ms, 5, 100);
    /* 1000ms time base tasks, reserve */
    SCH_Add_Task(Task6_1000ms, 6, 1000);

    while (1)
    {
        SCH_Dispatch_Tasks();
        MC_Keys_Read();
        Panel_Refresh();
    }
}

/* =============================================  EOF  ============================================== */

使用这个调度器也非常简单,只需要3步:

  1. 给调度器设置好定时器;
void Timer0_Callback(void *device, uint32_t wpara, uint32_t lpara)
{
    if (wpara & TIMER_CHANNEL_TF_TFLG_Msk)
    {
        SCH_Update();
    }
}
  1. 写任务函数;
  2. 规划好任务的调用时间,配置周期和延时。
  3. 调用Task_Scheduler()开始调度。

小结

这个调度器像个老古董,但又与当前主流的OS异曲同工。

你可能感兴趣的:(OS)