网站首页 > 技术文章 正文
在单片机开发过程,对于简单功能的开发模式,一般使用前后台开发模式,比如主函数实现复杂逻辑函数,外加中断处理一些紧急的事件,此法称之为: 前后台轮询软件架构。
对于需要紧急处理的事件,则通过后台中断的方式进行处理或标记,主要的“繁重任务”依靠前台轮询来完成,这种方式对于初学者来说,简单,可以快速入门。当应用系统需要处理的事件较多时,如用上述架构,显然对于系统维护、开发都不太友好。今天介绍一种单片机裸机架构,基于时间触发的调度机制。
/* main.c */
#include <stdio.h>
int main(void)
{
while (1)
{
func1();
func2();
func3();
...
}
return 0;
}
/* isr.c */
void ISR_Handler(void)
{
...
}
基于时间触发任务调度器,由一个定时器或者任意能产生固定时间戳的中断、以及相应数据结构即可构成,下面将详细介绍。
- 调度器用到如下几个函数
/* 任务调度器数据结构 */
typedef struct
{
void (*pTask)(void); /* 任务函数指针 */
uint16_t Delay; /* 任务延迟启动时间 */
uint16_t Period; /* 任务运行周期 */
uint8_t RunMe; /* 任务调度标志 */
uint8_t Co_op; /* 任务调用方式: 1:合作式 0:抢占式 */
} sTaskH;
/* 自定义系统最大使用的任务个数 */
#define tSCH_MAX_TASKS (16)
/* tsch.h 函数指针声明 */
typedef void (*tsch_init_tmr)(void);
typedef void (*tsch_set_handle)(void (*tsch_update)(void));
typedef void (*tsch_open)(void);
typedef void (*tsch_close)(void);
typedef void (*tsch_sleep)(void);
typedef void (*tsch_clr_wdg)(void);
typedef void (*tsch_update)(void);
/* tsch.h 调度器函数声明 */
void tSCH_Init(tsch_init_tmr t_tmr_init, tsch_set_handle t_handle);
void tSCH_Dispatch_Tasks(tsch_clr_wdg t_wdg_clr, tsch_sleep t_sleep);
void tSCH_Start(tsch_open t_open);
void tSCH_Close(tsch_close t_close);
uint8_t tSCH_Add_Task(void (*Fn_p)(), const uint16_t Del, const uint16_t Per, uint8_t Co_op);
uint8_t tSCH_Delete_Task(uint8_t Task_index);
void tSCH_Update(void);
- 调度器函数实现
/* 定义任务管理结构 */
static sTaskH tSCH_Task_Group[tSCH_MAX_TASKS];
/**
* tSCH_Init 初始化调度器
*
* @param
* t_tmr_init:定时器初始化函数指针
* t_handle:设置任务调度函数句柄
* @return 0:SUCC
* !0:FAIL
* @author 嵌入式1号农民工
* @date 2021-10-10
*/
void tSCH_Init(tsch_init_tmr t_tmr_init, tsch_set_handle t_handle)
{
if(t_tmr_init != NULL)
{
t_tmr_init();
}
if(t_handle != NULL)
{
t_handle(tSCH_Update);
}
}
/**
* tSCH_Add_Task 任务添加
*
* @param
* Fn_p:任务函数指针
* Del: 任务延迟运行时间
* Per:任务调度周期
* Co_op: 1:合作式 0:抢占式
* @return 0:SUCC
* !0:FAIL
* @author 嵌入式1号农民工
* @date 2021-10-10
*/
uint8_t tSCH_Add_Task(void (*Fn_p)(), const uint16_t Del,
const uint16_t Per, uint8_t Co_op)
{
uint8_t Index = 0;
while ((tSCH_Task_Group[Index].pTask != 0) && (Index < tSCH_MAX_TASKS))
{
Index++;
}
if (Index == tSCH_MAX_TASKS)
{
return ERROR_SCH_TOO_MANY_TASKS;
}
tSCH_Task_Group[Index].pTask = Fn_p;
tSCH_Task_Group[Index].Delay = Del;
tSCH_Task_Group[Index].Period = Per;
tSCH_Task_Group[Index].Co_op = Co_op;
tSCH_Task_Group[Index].RunMe = 0;
return Index;
}
/**
* tSCH_Delete_Task 任务删除
*
* @param
* Task_index:任务号索引
* @return 0:SUCC
* !0:FAIL
* @author 嵌入式1号农民工
* @date 2021-10-10
*/
uint8_t tSCH_Delete_Task(uint8_t Task_index)
{
uint8_t Return_code;
if (tSCH_Task_Group[Task_index].pTask == 0)
{
Return_code = RETURN_ERROR;
}
else
{
Return_code = RETURN_NORMAL;
}
tSCH_Task_Group[Task_index].pTask = 0;
tSCH_Task_Group[Task_index].Delay = 0;
tSCH_Task_Group[Task_index].Period = 0;
tSCH_Task_Group[Task_index].RunMe = 0;
return Return_code;
}
/**
* tSCH_Dispatch_Tasks 任务调度运行
*
* @param
* t_wdg_clr:喂狗函数指针
* t_sleep:休眠函数指针
* @return 0:SUCC
* !0:FAIL
* @author 嵌入式1号农民工
* @date 2021-10-10
*/
void tSCH_Dispatch_Tasks(tsch_clr_wdg t_wdg_clr, tsch_sleep t_sleep)
{
uint8_t Index;
for (Index = 0; Index < tSCH_MAX_TASKS; Index++)
{
if ((tSCH_Task_Group[Index].Co_op) && (tSCH_Task_Group[Index].RunMe > 0))
{
if (tSCH_Task_Group[Index].pTask)
{
(*tSCH_Task_Group[Index].pTask)();
}
tSCH_Task_Group[Index].RunMe -= 1;
if (tSCH_Task_Group[Index].Period == 0)
{
tSCH_Task_Group[Index].pTask = 0;
}
}
}
//!< clear watchdog
if (t_wdg_clr != NULL)
{
t_wdg_clr();
}
if (t_sleep != NULL)
{
t_sleep();
}
}
/**
* tSCH_Update 任务调度时基管理,此函数被定时器中断调用
*
* @param None
*
* @return 0:SUCC
* !0:FAIL
* @author 嵌入式1号农民工
* @date 2021-10-10
*/
void tSCH_Update(void)
{
uint8_t Index;
for (Index = 0; Index < tSCH_MAX_TASKS; Index++)
{
if (tSCH_Task_Group[Index].pTask)
{
if (tSCH_Task_Group[Index].Delay == 0)
{
if (tSCH_Task_Group[Index].Co_op)
{
tSCH_Task_Group[Index].RunMe += 1;
}
else
{
(*tSCH_Task_Group[Index].pTask)();
tSCH_Task_Group[Index].RunMe -= 1;
if (tSCH_Task_Group[Index].Period == 0)
{
tSCH_Task_Group[Index].pTask = 0;
}
}
if (tSCH_Task_Group[Index].Period)
{
tSCH_Task_Group[Index].Delay = tSCH_Task_Group[Index].Period - 1;
}
}
else
{
tSCH_Task_Group[Index].Delay -= 1;
}
}
}
}
- 应用案例
#include <stdio.h>
#include "tsch.h"
#include "..."
void task1(void)
{
/* 任务实体 */
}
void task2(void)
{
/* 任务实体 */
}
void task3(void)
{
/* 任务实体 */
}
/* 喂狗函数实现 */
void wdt_refresh(void)
{
}
/* 低功耗休眠操作函数 */
void sys_sleep(void)
{
}
int main(void)
{
tSCH_Add_Task(task1,0,100,1);
tSCH_Add_Task(task2,100,500,1);
tSCH_Add_Task(task3,0,10,0);
tSCH_Init(sys_tick_init, set_task_cb);
while (1)
{
tSCH_Dispatch_Tasks(wdt_refresh, sys_sleep);
}
return 0;
}
set_task_cb函数实现为一个设置tSCH_Update函数被时基中断回调的一个接口,可自行实现,如:
typedef void (*SYS_tick_cb_t)(void);
SYS_tick_cb_t SYSTICK_isr_cb = NULL;
void set_task_cb(SYS_tick_cb_t task)
{
SYSTICK_isr_cb = task;
}
/* SysTick中断 */
void SysTick_Handler(void)
{
if (SYSTICK_isr_cb != NULL)
{
SYSTICK_isr_cb();
}
HAL_IncTick();
}
如上应用案例:
任务1将以100个时基的周期调度,
任务2将以500个时基的周期调度,
任务3将以10个时基的周期调度,
其中任务3优先级最高,其属于中断式任务,需要快速执行的任务才可以设置为中断式任务。每个任务即为一个功能模块,在实际开发过程,可以分为不同文件管理,结构清晰,维护简单。
此架构实现ROM、RAM消耗几乎可以忽略不计,已在小容量8位机、16位机、32位机均有移植并大量使用过。
猜你喜欢
- 2025-06-23 Qt qsort用法 完整版(解释了cmp)(qt中setshortcut的作用)
- 2025-06-23 学习笔记单片机的40个经典实验之5:报警产生器
- 2025-06-23 for、while、do while三种循环的流程图画法总结
- 2025-06-23 Proxy(代理)-对象结构型模式(js 代理对象)
- 2025-06-23 精品博文stm8自学笔记 2016/3/15(stm8系列选型表)
- 2025-06-23 【Linux系统编程】fork()函数详解
- 2025-06-23 链表 | 如何判断两个单链表(无环)是否交叉
- 2025-06-23 C语言灵魂:指针是什么及其常见用法
- 2025-06-23 51单片机-定时器(简易时钟的实现)
- 2025-06-23 4.3 do_while循环结构(do-while循环结构)
- 最近发表
-
- Java中玩转JSON:让数据交互变得简单又有趣
- 爬虫逆向学习-下载网易云音乐(爬虫逆向分析)
- 一篇长文带你在Python里玩转Json数据
- 为何推荐 JsonTree.js 做 JSON 可视化?
- 能运行,不代表它是对的:5 个潜伏在正常功能下的 JavaScript 错误
- 让Android开发者轻松解析json数据的三种工具
- 必知必会!Python json模块全解析(python json encode)
- JavaScript的Symbol,解决了多少你不知道的隐形大麻烦?
- JSON 对象的这些操作和使用场景你知道多少?
- JSON 对象的克隆:浅拷贝与深拷贝(jsonobject深拷贝)
- 标签列表
-
- cmd/c (64)
- c++中::是什么意思 (83)
- 标签用于 (65)
- 主键只能有一个吗 (66)
- c#console.writeline不显示 (75)
- pythoncase语句 (81)
- es6includes (73)
- windowsscripthost (67)
- apt-getinstall-y (86)
- node_modules怎么生成 (76)
- chromepost (65)
- c++int转char (75)
- static函数和普通函数 (76)
- el-date-picker开始日期早于结束日期 (70)
- js判断是否是json字符串 (67)
- localstorage.removeitem (74)
- vector线程安全吗 (70)
- & (66)
- java (73)
- js数组插入 (83)
- linux删除一个文件夹 (65)
- mac安装java (72)
- eacces (67)
- 查看mysql是否启动 (70)
- 无效的列索引 (74)