目录
软件定时器概述
在学习单片机的的时候,会使用定时器来做很多事情,这个定时器时单片机自带的也就是硬件定时器,而UCOSIII内核提供了一个模拟定时器的机制类似于任务,但是占用资源少,只能做一些简单的定时控制,如可以定时器喂狗控灯。在软件定时器中绝对不能添加事件管理函数,阻塞等待函数。
使用步骤:
创建软件定时器:
利用OSTmrCreate()函数,函数原型如下:
参数:
p_tmr, 软件定时器对象
p_name, 软件定时器的名字
dly, 启动定时器后,延迟多长时间执行,默认隐含dly10ms
period, 定时周期,默认隐含period10ms
opt: 模式
OS_OPT_TMR_ONE_SHOT,软件定时器执行一遍
OS_OPT_TMR_PERIODIC,软件定时器周期性执行
p_callback, 软件定时器执行的回调函数 void MyCallback (OS_TMR *p_tmr, void *p_arg);
p_callback_arg, 传递参数给软件定时器的回调函数
p_err, 返回错误码,没有错误的就返回OS_ERR_NONE
启动软件定时器:
函数原型如下:
参数:
p_tmr :软件定时器对象
p_err :返回错误码,没有错误就返回OS_ERR_NONE
返回值:
DEF_TRUE is the timer was started
DEF_FALSE if not or upon an error
停止软件定时器:
函数原型如下:
参数:
p_tmr 软件定时器对象
os_opt 默认参数,OS_OPT_TMR_NONE
p_callback_arg 填写OSTmrCreate创建时传递给软件定时器arg的参数
p_err 返回错误码,没有错误就返回OS_ERR_NONE
删除软件定时器:
函数原型:
参数:
p_tmr,软件定时器对象
p_err,返回错误码,没有错误的就返回OS_ERR_NONE
单次定时器:
使用OSTmrCreate()函数创建定时器参数时候把参数opt设置为OS_OPT_TMR_ONE_SHOT,就是创建的单次定时器。创建一个单次定时器以后,我们一旦调用OSTmrStart()函数定时器就会是从创建定义的dly开始倒计时直到减为0调用回调函数。
上图展示了单次定时器的运行,dly减到0的时候调用回调函数,到这里定时器就停止运行了不再做任何事,我们可以调用OSTmrStop()函数来删除这个运行完成的定时器,也可以重新调用OSTmrStart()函数来重新开启,如下图
周期定时器:
无初始化延时:
使用OSTmrCreate()函数创建定时器时把参数opt设置为OS_OPT_TMR_PERIODIC ,就是创建周期定时器。当计时器倒计数完成之后,定时器就会调用周期函数,并重置计数器开始下一轮定时,这样一直循环下去。如果使用OSTmrCreate函数创建定时器的时候,参数dly为0的话,那么定时器在每个周期开始时计数器的初始值就是period,如下图
有初始化延时:
在创建任务的时候可以创建带有初始化延时,初始化初始化延时就是使用OSTmrCreate()函数中的参数dly就是初始化延时,定时器的第一个周期是dly。当第一个周期完成后就用参数period作为周期值,主要流程如下图:
软件定时器实验:
任务要求:先两个任务,任务A和任务B,任务A用于创建两个定时器:是定时器1和定时器2,任务A还创建了另一个任务B。其中定时器 1 为周期定时器,初始延时为 200ms,以后的定时器周期为 1000ms,定时器 2 为单次定时器,延时为2000ms。
任务 B 作为按键检测任务,当 KEY_UP 键按下的时候,打开定时器 1;当 KEY0 按下的时候打开定时器 2;当 KEY1 按下的时候,同时关闭定时器 1 和 2;任务 B 还用来控制 LED0,使其闪烁,提示系统正在运行。
定时器 1 定时完成以后调用回调函数串口输出定时器1进入一次。定时器 2 是单次定时器,我们通过串口打印来观察单次定时器的运行情况。
实验效果:
代码历程:
#include "led.h" #include "delay.h" #include "sys.h" #include "usart.h" #include "includes.h" #include "os_app_hooks.h" #include "key.h" //UCOSIII中以下优先级用户程序不能使用,ALIENTEK //将这些优先级分配给了UCOSIII的5个系统内部任务 //优先级0:中断服务服务管理任务 OS_IntQTask() //优先级1:时钟节拍任务 OS_TickTask() //优先级2:定时任务 OS_TmrTask() //优先级OS_CFG_PRIO_MAX-2:统计任务 OS_StatTask() //优先级OS_CFG_PRIO_MAX-1:空闲任务 OS_IdleTask() //创建任务A //定义任务优先级 #define TASK_A_PRIO 3 //定义任务控制块 OS_TCB TASK_A_TCB; //定义任务堆栈大小 #define TASK_A_STK_SIZE 128 //定义任务堆栈 CPU_STK TASK_A_STK[TASK_A_STK_SIZE]; //定义任务函数 void TASK_A(void *arg); //创建任务B //定义任务优先级 #define TASK_B_PRIO 4 //定义任务控制块 OS_TCB TASK_B_TCB; //定义任务堆栈大小 #define TASK_B_STK_SIZE 128 //定义任务堆栈 CPU_STK TASK_B_STK[TASK_B_STK_SIZE]; //定义任务函数 void TASK_B(void *arg); / OS_TMR tmr1;//定时器1 OS_TMR tmr2;//定时器1 void tmr1_callback(void *p_tmr, void *p_arg); //定时器1回调函数 void tmr2_callback(void *p_tmr, void *p_arg); //定时器1回调函数 int main(void) { OS_ERR err1;//错误码变量 CPU_SR_ALLOC();//定义临界区需要的变量 //硬件初始化 delay_init(); //延时初始化 NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2); //中断分组配置 uart_init(115200); //串口波特率设置 LED_Init(); KEY_Init(); OSInit(&err1);//初始化UCOSIII OS_CRITICAL_ENTER();//进入临界区代码 //创建定时器1 //创建开始任务1 OSTaskCreate((OS_TCB * )&TASK_A_TCB, //任务控制块 (CPU_CHAR * )"main TASKA", //任务名字 (OS_TASK_PTR )TASK_A, //任务函数 (void * )0, //传递给任务函数的参数 (OS_PRIO )TASK_A_PRIO, //任务优先级 (CPU_STK * )&TASK_A_STK[0], //任务堆栈基地址 (CPU_STK_SIZE)TASK_A_STK_SIZE/10, //任务堆栈深度限位 (CPU_STK_SIZE)TASK_A_STK_SIZE, //任务堆栈大小 (OS_MSG_QTY )0, //任务内部消息队列能够接收的最大消息数目,为0时禁止接收消息 (OS_TICK )0, //当使能时间片轮转时的时间片长度,为0时为默认长度, (void * )0, //用户补充的存储区 (OS_OPT )OS_OPT_TASK_STK_CHK|OS_OPT_TASK_STK_CLR, //任务选项 (OS_ERR * )&err1); //存放该函数错误时的返回值 OS_CRITICAL_EXIT();//退出临界区代码 OSStart(&err1);//开启UCOSIII while(1); } //开始任务函数 void TASK_A(void *arg) { OS_ERR err2_3;//错误码变量 CPU_SR_ALLOC();//定义临界区需要的变量 arg = arg; CPU_Init(); #if OS_CFG_STAT_TASK_EN > 0u OSStatTaskCPUUsageInit(&err2_3); //统计任务 #endif #ifdef CPU_CFG_INT_DIS_MEAS_EN //如果使能了测量中断关闭时间 CPU_IntDisMeasMaxCurReset(); #endif #if OS_CFG_SCHED_ROUND_ROBIN_EN //当使用时间片轮转的时候 //使能时间片轮转调度功能,时间片长度为1个系统时钟节拍,既1*5=5ms OSSchedRoundRobinCfg(DEF_ENABLED,1,&err2_3); #endif //#if OS_CFG_APP_HOOKS_EN //使用钩子函数 // App_OS_SetAllHooks(); //#endif #if OS_CFG_SCHED_ROUND_ROBIN_EN //使用时间片轮转调度功能,时间片长度为一个时钟节拍即1*5=5ms OSSchedRoundRobinCfg(DEF_ENABLED,1,&err2_3); #endif //创建定时器1 OSTmrCreate((OS_TMR *)&tmr1, //定时器1 (CPU_CHAR *)"tmr1", //定时器名字 (OS_TICK )20, //20*10=200ms (OS_TICK )100, //100*10=1000ms (OS_OPT )OS_OPT_TMR_PERIODIC, //周期模式 (OS_TMR_CALLBACK_PTR)tmr1_callback,//定时器1回调函数 (void *)0, //参数为0 (OS_ERR *)&err2_3); //返回的错误 //创建定时器2 OSTmrCreate((OS_TMR *)&tmr2, //定时器1 (CPU_CHAR *)"tmr2", //定时器名字 (OS_TICK )200, //200*10=2000ms (OS_TICK )0, // (OS_OPT )OS_OPT_TMR_ONE_SHOT, //单次延时 (OS_TMR_CALLBACK_PTR)tmr2_callback,//定时器2回调函数 (void *)0, //参数为0 (OS_ERR *)&err2_3); //返回的错误码 OS_CRITICAL_ENTER();//进入临界区代码 //创建开始任务B OSTaskCreate((OS_TCB * )&TASK_B_TCB, //任务控制块 (CPU_CHAR * )"main TASKB", //任务名字 (OS_TASK_PTR )TASK_B, //任务函数 (void * )0, //传递给任务函数的参数 (OS_PRIO )TASK_B_PRIO, //任务优先级 (CPU_STK * )&TASK_B_STK[0], //任务堆栈基地址 (CPU_STK_SIZE)TASK_B_STK_SIZE/10, //任务堆栈深度限位 (CPU_STK_SIZE)TASK_B_STK_SIZE, //任务堆栈大小 (OS_MSG_QTY )0, //任务内部消息队列能够接收的最大消息数目,为0时禁止接收消息 (OS_TICK )0, //当使能时间片轮转时的时间片长度,为0时为默认长度, (void * )0, //用户补充的存储区 (OS_OPT )OS_OPT_TASK_STK_CHK|OS_OPT_TASK_STK_CLR, //任务选项 (OS_ERR * )&err2_3); //存放该函数错误时的返回值 OS_CRITICAL_EXIT();//退出临界区代码 //任务一执行完函数之后删掉自身 OSTaskDel((OS_TCB *)0,&err2_3); } void TASK_B(void *arg) { u8 key,num; OS_ERR err_B; while(1) { key = KEY_Scan(0); switch(key) { case WKUP_PRES: //当key_up按下的话打开定时器1 OSTmrStart(&tmr1,&err_B); //开启定时器1 printf("开启定时器1\r\n"); break ; case KEY0_PRES://当key0按下的话打开定时器2 OSTmrStart(&tmr2,&err_B); //开启定时器2 printf("开启定时器2\r\n"); break ; case KEY1_PRES: OSTmrStop(&tmr1,OS_OPT_TMR_NONE,0,&err_B); //关闭定时器1 OSTmrStop(&tmr2,OS_OPT_TMR_NONE,0,&err_B); //关闭定时器2 printf("关闭定时器1和2\r\n"); break ; } num++; if(num==50)//每500msled闪烁一次 { num=0; LED0 = ~LED0; } OSTimeDlyHMSM(0,0,0,10,OS_OPT_TIME_PERIODIC,&err_B);//延时10ms } } //定时器1回调函数 void tmr1_callback(void *p_tmr, void *p_arg) { static u8 tmr1_num=0; tmr1_num++; //定时器1执行次数加1 printf("定时器1进入一次\r\n"); } //定时器2回调函数 void tmr2_callback(void *p_tmr, void *p_arg) { static u8 tmr2_num = 0; tmr2_num++; //定时器2执行次数加1 LED1 = ~LED1; printf("定时器2运行结束\r\n"); }