图60.1.6 软件定时器管理任务流程
当运行完软件定时器的到时处理函数之后,需要进行该定时器控制块在链表中的移除和再插入操作。插入前需要重新计算定时器下次到时时所处的分组。计算公式如下:
定时器下次到时的OSTmrTime值(OSTmrMatch)=定时器定时值+当前OSTmrTime值
新分组=定时器下次到时的OSTmrTime值(OSTmrMatch)%OS_TMR_CFG_WHEEL_SIZE
接下来我们看看在UCOSII中,与软件定时器相关的几个函数。
1) 创建软件定时器函数
创建软件定时器通过函数OSTmrCreate实现,该函数原型为:OS_TMR *OSTmrCreate (INT32U dly, INT32U period, INT8U opt, OS_TMR_CALLBACK callback,void *callback_arg, INT8U *pname, INT8U *perr)。
dly,用于初始化定时时间,对单次定时(ONE-SHOT模式)的软件定时器来说,这就是该定时器的定时时间,而对于周期定时(PERIODIC模式)的软件定时器来说,这是该定时器第一次定时的时间,从第二次开始定时时间变为period。
period,在周期定时(PERIODIC模式),该值为软件定时器的周期溢出时间。
opt,用于设置软件定时器工作模式。可以设置的值为:OS_TMR_OPT_ONE_SHOT或OS_TMR_OPT_PERIODIC,如果设置为前者,说明是一个单次定时器;设置为后者则表示是周期定时器。
callback,为软件定时器的回调函数,当软件定时器的定时时间到达时,会调用该函数。
callback_arg,回调函数的参数。
pname,为软件定时器的名字。
perr,为错误信息。
软件定时器的回调函数有固定的格式,我们必须按照这个格式编写,软件定时器的回调函数格式为:void (*OS_TMR_CALLBACK)(void *ptmr, void *parg)。其中,函数名我们可以自己随意设置,而ptmr这个参数,软件定时器用来传递当前定时器的控制块指针,所以我们一般设置其类型为OS_TMR*类型,第二个参数(parg)为回调函数的参数,这个就可以根据自己需要设置了,你也可以不用,但是必须有这个参数。
2) 开启软件定时器函数
任务可以通过调用函数OSTmrStart开启某个软件定时器,该函数的原型为:BOOLEAN OSTmrStart (OS_TMR *ptmr, INT8U *perr)。其中ptmr为要开启的软件定时器指针,perr为错误信息。
3) 停止软件定时器函数
任务可以通过调用函数OSTmrStop停止某个软件定时器,该函数的原型为:BOOLEAN OSTmrStop (OS_TMR *ptmr,INT8U opt,void *callback_arg,INT8U *perr)。
其中ptmr为要停止的软件定时器指针。
opt为停止选项,可以设置的值及其对应的意义为:
OS_TMR_OPT_NONE,直接停止,不做任何其他处理
OS_TMR_OPT_CALLBACK,停止,用初始化的参数执行一次回调函数
OS_TMR_OPT_CALLBACK_ARG,停止,用新的参数执行一次回调函数
callback_arg,新的回调函数参数。
perr,错误信息。
软件定时器我们就介绍到这。
60.2 硬件设计
本节实验功能简介:本章我们在UCOSII里面创建7个任务:开始任务、LED任务、触摸屏任务、队列消息显示任务、信号量集任务、按键扫描任务和主任务,开始任务用于创建邮箱、消息队列、信号量集以及其他任务,之后挂起;触摸屏任务用于在屏幕上画图,测试CPU使用率;队列消息显示任务请求消息队列,在得到消息后显示收到的消息数据;信号量集任务用于测试信号量集,采用OS_FLAG_WAIT_SET_ANY的方法,任何按键按下(包括TPAD),该任务都会控制蜂鸣器发出“滴”的一声;按键扫描任务用于按键扫描,优先级最高,将得到的键值通过消息邮箱发送出去;主任务创建3个软件定时器(定时器1,100ms溢出一次,显示CPU和内存使用率;定时2,200ms溢出一次,在固定区域不停的显示不同颜色;定时3,,100ms溢出一次,用于自动发送消息到消息队列),并通过查询消息邮箱获得键值,根据键值执行DS1控制、控制软件定时器3的开关、触摸区域清屏、触摸屏校和软件定时器2的开关控制等。
所要用到的硬件资源如下:
1) 指示灯DS0 、DS1
2) 4个机械按键(KEY0/KEY1/KEY2/WK_UP)
3) TPAD触摸按键
4) 蜂鸣器
5) TFTLCD模块
这些,我们在前面的学习中都已经介绍过了。
60.3 软件设计
本章,我们在第四十三章实验 (实验38 )的基础上修改,首先,是UCOSII代码的添加,具体方法同第五十九章一模一样,本章就不再详细介绍了。本章OS_TICKS_PER_SEC的设置还是为500,即UCOSII的时钟节拍为2ms。另外由于我们创建了7个任务,加上统计任务、空闲任务和软件定时器任务,总共10个任务,如果你还想添加其他任务,请把OS_MAX_TASKS的值适当改大。
另外,我们还需要在os_cfg.h里面修改软件定时器管理部分的宏定义,修改如下:
#define OS_TMR_EN 1u //使能软件定时器功能
#define OS_TMR_CFG_MAX 16u //最大软件定时器个数
#define OS_TMR_CFG_NAME_EN 1u //使能软件定时器命名
#define OS_TMR_CFG_WHEEL_SIZE 8u //软件定时器轮大小
#define OS_TMR_CFG_TICKS_PER_SEC 100u //软件定时器的时钟节拍(10ms)
#define OS_TASK_TMR_PRIO 0u //软件定时器的优先级,设置为最高
这样我们就使能UCOSII的软件定时器功能了,并且设置最大软件定时器个数为16,定时器轮大小为8,软件定时器时钟节拍为10ms(即定时器的最少溢出时间为10ms)。
最后,我们只需要修改test.c函数了,打开test.c,输入如下代码:
/////////////////////////UCOSII任务设置///////////////////////////////////
//START 任务
#define START_TASK_PRIO 10 //设置任务优先级
#define START_STK_SIZE 64 //设置任务堆栈大小
OS_STK START_TASK_STK[START_STK_SIZE]; //任务堆栈
void start_task(void *pdata); //任务函数
//LED任务
#define LED_TASK_PRIO 7 //设置任务优先级
#define LED_STK_SIZE 64 //设置任务堆栈大小
OS_STK LED_TASK_STK[LED_STK_SIZE]; //任务堆栈
void led_task(void *pdata); //任务函数
//触摸屏任务
#define TOUCH_TASK_PRIO 6 //设置任务优先级
#define TOUCH_STK_SIZE 64 //设置任务堆栈大小
OS_STK TOUCH_TASK_STK[TOUCH_STK_SIZE];//任务堆栈
void touch_task(void *pdata); //任务函数
//队列消息显示任务
#define QMSGSHOW_TASK_PRIO 5 //设置任务优先级
#define QMSGSHOW_STK_SIZE 64 //设置任务堆栈大小
OS_STK QMSGSHOW_TASK_STK[QMSGSHOW_STK_SIZE]; //任务堆栈
void qmsgshow_task(void *pdata); //任务函数
//主任务
#define MAIN_TASK_PRIO 4 //设置任务优先级
#define MAIN_STK_SIZE 128 //设置任务堆栈大小
OS_STK MAIN_TASK_STK[MAIN_STK_SIZE]; //任务堆栈
void main_task(void *pdata); //任务函数
//////////////////////////////////////////////////////////////////////////////
//信号量集任务
#define FLAGS_TASK_PRIO 3 //设置任务优先级
#define FLAGS_STK_SIZE 64 //设置任务堆栈大小
OS_STK FLAGS_TASK_STK[FLAGS_STK_SIZE]; //任务堆栈
void flags_task(void *pdata); //任务函数
//按键扫描任务
#define KEY_TASK_PRIO 2 //设置任务优先级
#define KEY_STK_SIZE 64 //设置任务堆栈大小
OS_STK KEY_TASK_STK[KEY_STK_SIZE]; //任务堆栈
void key_task(void *pdata); //任务函数
OS_EVENT * msg_key; //按键邮箱事件块
OS_EVENT * q_msg; //消息队列
OS_TMR * tmr1; //软件定时器1
OS_TMR * tmr2; //软件定时器2
OS_TMR * tmr3; //软件定时器3
OS_FLAG_GRP * flags_key; //按键信号量集
void * MsgGrp[256]; //消息队列存储地址,最大支持256个消息
//软件定时器1的回调函数
//每100ms执行一次,用于显示CPU使用率和内存使用率
void tmr1_callback(OS_TMR *ptmr,void *p_arg)
{
static u16 cpuusage=0; static u8 tcnt=0;
POINT_COLOR=BLUE;
if(tcnt==5)
{
LCD_ShowxNum(182,10,cpuusage/5,3,16,0); //显示CPU使用率
cpuusage=0; tcnt=0;
}
cpuusage+=OSCPUUsage; tcnt++;
LCD_ShowxNum(182,30,mem_perused(SRAMIN),3,16,0); //显示内存使用率
LCD_ShowxNum(182,50,((OS_Q*)(q_msg->OSEventPtr))->OSQEntries,3,16,0X80);
//显示队列当前的大小
}
//软件定时器2的回调函数
void tmr2_callback(OS_TMR *ptmr,void *p_arg)
{
static u8 sta=0;
switch(sta)
{
case 0: LCD_Fill(121,221,lcddev.width-1,lcddev.height-1,RED); break;
case 1: LCD_Fill(121,221,lcddev.width-1,lcddev.height-1,GREEN); break;
case 2: LCD_Fill(121,221,lcddev.width-1,lcddev.height-1,BLUE); break;
case 3: LCD_Fill(121,221,lcddev.width-1,lcddev.height-1,MAGENTA); break;
case 4: LCD_Fill(121,221,lcddev.width-1,lcddev.height-1,GBLUE); break;
case 5CD_Fill(121,221,lcddev.width-1,lcddev.height-1,YELLOW); break;
case 6: LCD_Fill(121,221,lcddev.width-1,lcddev.height-1,BRRED); break;
}
sta++; if(sta>6)sta=0;
}
//软件定时器3的回调函数
void tmr3_callback(OS_TMR *ptmr,void *p_arg)
{
u8* p; u8 err;
static u8 msg_cnt=0; //msg编号
p=mymalloc(SRAMIN,13); //申请13个字节的内存
if(p)
{
sprintf((char*)p,"ALIENTEK %03d",msg_cnt);
msg_cnt++;
err=OSQPost(q_msg,p); //发送队列
if(err!=OS_ERR_NONE) //发送失败
{
myfree(SRAMIN,p); //释放内存
OSTmrStop(tmr3,OS_TMR_OPT_NONE,0,&err); //关闭软件定时器3
}
}
}
//加载主界面
void ucos_load_main_ui(void)
{
LCD_Clear(WHITE); //清屏
POINT_COLOR=RED; //设置字体为红色
LCD_ShowString(10,10,200,16,16,"WarShip STM32");
LCD_ShowString(10,30,200,16,16,"UCOSII TEST3");
LCD_ShowString(10,50,200,16,16,"ATOM@ALIENTEK");
LCD_ShowString(10,75,240,16,16,"TPAD:TMR2 SW KEY_UP:ADJUST");
LCD_ShowString(10,95,240,16,16,"KEY0S0 KEY1 SW KEY2:CLR");
LCD_DrawLine(0,70,lcddev.width,70);
LCD_DrawLine(120,0,120,70);
LCD_DrawLine(0,120,lcddev.width,120);
LCD_DrawLine(0,220,lcddev.width,220);
LCD_DrawLine(120,120,120,lcddev.height);
LCD_ShowString(5,125,240,16,16,"QUEUE MSG");//队列消息
LCD_ShowString(5,150,240,16,16,"Message:");
LCD_ShowString(5+120,125,240,16,16,"FLAGS");//信号量集
LCD_ShowString(5,225,240,16,16,"TOUCH"); //触摸屏
LCD_ShowString(5+120,225,240,16,16,"TMR2"); //队列消息
POINT_COLOR=BLUE;//设置字体为蓝色
LCD_ShowString(150,10,200,16,16,"CPU: %");
LCD_ShowString(150,30,200,16,16,"MEM: %");
LCD_ShowString(150,50,200,16,16," Q :000");
delay_ms(300);
}