最近在学习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步:
void Timer0_Callback(void *device, uint32_t wpara, uint32_t lpara)
{
if (wpara & TIMER_CHANNEL_TF_TFLG_Msk)
{
SCH_Update();
}
}
这个调度器像个老古董,但又与当前主流的OS异曲同工。