Author:家有仙妻谢掌柜
Date:2021/2/3
学一个东西一定要学会总结,学单片机也不例外,总结一下用单片机做项目时代码的架构,写在这里记录自己的成长历程,也分享出去供大家参考!
首先声明在本程序中和移植相关的针对不同MCU等差异所需要的做出的修改如下:
1.中断服务函数
Timer1Callback();
2.主函数中
timer_1_enable(32768/10,Timer1Callback);//我这里用的是100ms中断一次来做实验
参数32768表示1S,我这里32768/10表示100ms,这个速度已经很慢了,但是为了兼容打印,因为事件中没有实际的任务,太快打印跟不上。在实际操作中,中断的时间可以缩短一些,但是不能太短,频繁的中断效率也会低的。
3.这里测试了10个任务同时进行,效果满足预期,只要每一个任务执行的时间小于该任务设定的时间即可。
//时间片架构测试
void Task1(void);
void Task2(void);
void Task3(void);
void Task4(void);
void Task5(void);
void Task6(void);
void Task7(void);
void Task8(void);
void Task9(void);
void Task0(void);
/**************************************************************************************
* Name :TaskStruct
* Description :任务结构体
* Details :它包含了一个任务所需要的所有信息;
* 1:运行状态标志位:0-不运行;1-运行。
* 2:计数器TimerCountdown--
* 3:计数器数值填充器,预设的初值:TimerInitialValue(任务运行间隔时间)
* 4:任务对应的函数指针
**************************************************************************************/
typedef struct _TaskStruct
{
uint8_t RunningStatus;
uint8_t TimerCountdown;
uint8_t TimerInitialValue;
void (*TaskHook)(void);
}TaskStruct;
/**************************************************************************************
* Name :TaskManage
* Description :任务数组
* Details :它包含了所有任务所需要的参数;
**************************************************************************************/
//定时器中断实验目前是100ms中断一次
#define InitStatus 0
#define TimerTask1 10
#define TimerTask2 20
#define TimerTask3 30
#define TimerTask4 40
#define TimerTask5 50
#define TimerTask6 60
#define TimerTask7 70
#define TimerTask8 80
#define TimerTask9 90
#define TimerTask0 100
static TaskStruct TaskManage[] =
{
{InitStatus,TimerTask1,TimerTask1,Task1},
{InitStatus,TimerTask2,TimerTask2,Task2},
{InitStatus,TimerTask3,TimerTask3,Task3},
{InitStatus,TimerTask4,TimerTask4,Task4},
{InitStatus,TimerTask5,TimerTask5,Task5},
{InitStatus,TimerTask6,TimerTask6,Task6},
{InitStatus,TimerTask7,TimerTask7,Task7},
{InitStatus,TimerTask8,TimerTask8,Task8},
{InitStatus,TimerTask9,TimerTask9,Task9},
{InitStatus,TimerTask0,TimerTask0,Task0},
/* 这里添加需要的任务。。。。 */
};
/**************************************************************************************
* Name :TaskList
* Description :任务列表
* Details :包含所有任务;
* 1:ListTask1
* 2:ListTask2
* ...
* N:ListTasksMax:总的可供分配的定时任务数目
**************************************************************************************/
typedef enum _TaskList
{
ListTask1,
ListTask2,
ListTask3,
ListTask4,
ListTask5,
ListTask6,
ListTask7,
ListTask8,
ListTask9,
ListTask0,
/* 这里添加需要的任务。。。。 */
ListTasksMax
}TaskList;
/**************************************************************************************
* FunctionName : TaskMarks()
* Description : 标志位处理函数
* EntryParameter : None
* ReturnValue : None
这个函数是用来更新每个任务的计数器以及它们的运行标志位的,
它是写在单片机的定时器中断服务函数中的
**************************************************************************************/
void TaskMarks(void)
{
uint8_t i;
for (i=0;i<ListTasksMax;i++)//逐个任务时间处理
{
if(TaskManage[i].TimerCountdown)//计数器不为0
{
TaskManage[i].TimerCountdown--; //计数器减去一个节拍
if(TaskManage[i].TimerCountdown == 0)//计数器数值减完了
{
TaskManage[i].TimerCountdown = TaskManage[i].TimerInitialValue;//恢复计时器值,重新下一次
TaskManage[i].RunningStatus = 1;//任务可以运行
}
}
}
}
/**************************************************************************************
* FunctionName : TaskMarks()
* Description : 任务处理函数
* EntryParameter : None
* ReturnValue : None
这个函数用来判断任务是否到了执行时间,它会逐个检查任务列表中的任务
如果到了就执行,同时清空执行标志位,没到就不执行
**************************************************************************************/
void TaskProcess(void)
{
uint8_t i;
for (i=0;i<ListTasksMax;i++) // 逐个任务时间处理
{
if (TaskManage[i].RunningStatus) // 时间不为0
{
TaskManage[i].TaskHook(); // 运行任务
TaskManage[i].RunningStatus = 0; // 标志清0
}
}
}
/**************************************************************************************
* FunctionName : Task1()/Task2()/Task3()/Task4()...
* Description : 任务函数
* EntryParameter : None
* ReturnValue : None
**************************************************************************************/
void Task1(void)
{
dbg_printf("Task1\r\n");
}
void Task2(void)
{
dbg_printf("Task2\r\n");
}
void Task3(void)
{
dbg_printf("Task3\r\n");
}
void Task4(void)
{
dbg_printf("Task4\r\n");
}
void Task5(void)
{
dbg_printf("Task5\r\n");
}
void Task6(void)
{
dbg_printf("Task6\r\n");
}
void Task7(void)
{
dbg_printf("Task7\r\n");
}
void Task8(void)
{
dbg_printf("Task8\r\n");
}
void Task9(void)
{
dbg_printf("Task9\r\n");
}
void Task0(void)
{
dbg_printf("Task0\r\n");
}
/**************************************************************************************
* FunctionName : Timer1Callback()
* Description : 定时中断服务函数,回调函数
* EntryParameter : None
* ReturnValue : None
**************************************************************************************/
static void Timer1Callback(void)
{
TaskMarks();
}
/**************************************************************************************
* FunctionName : main()
* Description : 主函数
* EntryParameter : None
* ReturnValue : None
**************************************************************************************/
int main(void)
{
All_Initialize();
timer_1_enable(32768/10,Timer1Callback);//我这里用的是100ms中断一次来做实验
while(1)
{
TaskProcess();// 任务处理
}
}
总结:
整个程序的执行流程就是:中断函数不断刷新着每一个任务的状态,
任务处理函数根据这些状态来判断哪些函数要被执行,如果要执行,就立马执行。
添加任务就是在任务数组中添加一个结构体信息,编写一个任务函数,在任务列表中添加一个函数。
基本思想:
举个例子,有一个函数A,我们给它设置
一个计数器B(程序多久执行一次)
和一个状态标志位C(用来判断这个程序是否需要执行)
还有一个计数填充值D,
其中的B会被单片机自带的定时器中断服务函数不断地刷新(递减刷新),
当B到0的时候C会被置位,同时B会被重新填满D值,
当主程序运行到A的时候不会直接去运行它,而是先检查C,
如果C没有被置位,说明还没有到A的执行时间,不执行A,
如果C被置位了,就执行A,执行完后将C复位,让C再等上一段时间才再次被执行。
这个是记录自己学习的过程!
感谢您的支持!