内核对象和各种内核机制的函数接口都在os.h里声明,实现在各自的.c文件,比如os_tmr.c和os_time.c。
C语言全局变量一般会默认初始化;局部变量如若不初始化,会分配垃圾数据的;建议使用时都手动初始化。
其实使用内核对象时,就类似与使用任务,只不过在创建对象之前,要先声明一个内核对象。
好了,上面闲聊了几句,今天来说说ucosiii的几个内核对象。
首先说“软件定时器”,其实单纯的讲就是定时作用,这里我们要注意的就是,使用它方法和使用任务类似;那么我们就先来分析分析任务的执行过程:
- main会初始化ucos;调用一个起始任务创建函数(它的参数就是就是任务的工作模式,任务信息,任务函数指针)来创建任务;启动多任务管理。
- 任务函数指针指向起始任务函数会初始化操作系统;调用若干普通任务创建函数(与上一致)来创建任务;删除起始任务本身。
- 任务函数就会完成具体的操作。
那继续说软件定时器,在上面的3里任务函数会声明一个定时器对象,然后调用一个定时器创建函数(它的参数为定时器信息,工作模式,回调函数指针等)来创建任务,回调函数完成一些自定义的操作(每次定时完成会调用此回调函数),最后调用OSTmrStart()启动软件定时器;接下来就可以使用它了。
我们可以知道,用OSTimeDly是将任务置为等待态,CPU的使用权暂时被剥夺,开启定时器之后,该任务还是可以使用CPU。
通过debug我们也可以发现,运行完OSTimeDly之后(也可以说定时了指定时间之后),会完成一次回调函数里面的操作。也就验证了这个结论“定时完成之后会调用回调函数”。
下面我们就具体看一下源码,声明:这是秉火例程里的代码,我只是做了稍加修改。
#include <includes.h> CPU_TS ts_start; //时间戳变量 CPU_TS ts_end; static OS_TCB AppTaskStartTCB; //任务控制块 static OS_TCB AppTaskTmrTCB; static CPU_STK AppTaskStartStk[APP_TASK_START_STK_SIZE]; //任务堆栈 static CPU_STK AppTaskTmrStk [ APP_TASK_TMR_STK_SIZE ]; static void AppTaskStart (void *p_arg); //任务函数声明 static void AppTaskTmr ( void * p_arg ); int main (void) { OS_ERR err; OSInit(&err); //初始化 uC/OS-III /* 创建起始任务 */ OSTaskCreate((OS_TCB *)&AppTaskStartTCB, //任务控制块地址 (CPU_CHAR *)"App Task Start", //任务名称 (OS_TASK_PTR ) AppTaskStart, //任务函数 (void *) 0, //传递给任务函数(形参p_arg)的实参 (OS_PRIO ) APP_TASK_START_PRIO, //任务的优先级 (CPU_STK *)&AppTaskStartStk[0], //任务堆栈的基地址 (CPU_STK_SIZE) APP_TASK_START_STK_SIZE / 10, //任务堆栈空间剩下1/10时限制其增长 (CPU_STK_SIZE) APP_TASK_START_STK_SIZE, //任务堆栈空间(单位:sizeof(CPU_STK)) (OS_MSG_QTY ) 5u, //任务可接收的最大消息数 (OS_TICK ) 0u, //任务的时间片节拍数(0表默认值OSCfg_TickRate_Hz/10) (void *) 0, //任务扩展(0表不扩展) (OS_OPT )(OS_OPT_TASK_STK_CHK | OS_OPT_TASK_STK_CLR), //任务选项 (OS_ERR *)&err); //返回错误类型 OSStart(&err); //启动多任务管理(交由uC/OS-III控制) } static void AppTaskStart (void *p_arg) { CPU_INT32U cpu_clk_freq; CPU_INT32U cnts; OS_ERR err; (void)p_arg; BSP_Init(); //板级初始化 CPU_Init(); //初始化 CPU 组件(时间戳、关中断时间测量和主机名) cpu_clk_freq = BSP_CPU_ClkFreq(); //获取 CPU 内核时钟频率(SysTick 工作时钟) cnts = cpu_clk_freq / (CPU_INT32U)OSCfg_TickRate_Hz; //根据用户设定的时钟节拍频率计算 SysTick 定时器的计数值 OS_CPU_SysTickInit(cnts); //调用 SysTick 初始化函数,设置定时器计数值和启动定时器 Mem_Init(); //初始化内存管理组件(堆内存池和内存池表) #if OS_CFG_STAT_TASK_EN > 0u //如果使能(默认使能)了统计任务 OSStatTaskCPUUsageInit(&err); //计算没有应用任务(只有空闲任务)运行时 CPU 的(最大) #endif //容量(决定 OS_Stat_IdleCtrMax 的值,为后面计算 CPU //使用率使用)。 CPU_IntDisMeasMaxCurReset(); //复位(清零)当前最大关中断时间 /* 创建 AppTaskTmr 任务 */ OSTaskCreate((OS_TCB *)&AppTaskTmrTCB, //任务控制块地址 (CPU_CHAR *)"App Task Tmr", //任务名称 (OS_TASK_PTR ) AppTaskTmr, //任务函数 (void *) 0, //传递给任务函数(形参p_arg)的实参 (OS_PRIO ) APP_TASK_TMR_PRIO, //任务的优先级 (CPU_STK *)&AppTaskTmrStk[0], //任务堆栈的基地址 (CPU_STK_SIZE) APP_TASK_TMR_STK_SIZE / 10, //任务堆栈空间剩下1/10时限制其增长 (CPU_STK_SIZE) APP_TASK_TMR_STK_SIZE, //任务堆栈空间(单位:sizeof(CPU_STK)) (OS_MSG_QTY ) 5u, //任务可接收的最大消息数 (OS_TICK ) 0u, //任务的时间片节拍数(0表默认值OSCfg_TickRate_Hz/10) (void *) 0, //任务扩展(0表不扩展) (OS_OPT )(OS_OPT_TASK_STK_CHK | OS_OPT_TASK_STK_CLR), //任务选项 (OS_ERR *)&err); //返回错误类型 OSTaskDel ( & AppTaskStartTCB, & err ); //删除起始任务本身,该任务不再运行 } /* ********************************************************************************************************* * TMR TASK ********************************************************************************************************* */ void TmrCallback (OS_TMR *p_tmr, void *p_arg) //软件定时器MyTmr的回调函数 { CPU_INT32U cpu_clk_freq; CPU_SR_ALLOC(); //使用到临界段(在关/开中断时)时必需该宏,该宏声明和定义一个局部变 //量,用于保存关中断前的 CPU 状态寄存器 SR(临界段关中断只需保存SR) //,开中断时将该值还原。 printf ( "%s", ( char * ) p_arg ); cpu_clk_freq = BSP_CPU_ClkFreq(); //获取CPU时钟,时间戳是以该时钟计数 macLED1_TOGGLE (); ts_end = OS_TS_GET() - ts_start; //获取定时后的时间戳(以CPU时钟进行计数的一个计数值) //,并计算定时时间。 OS_CRITICAL_ENTER(); //进入临界段,不希望下面串口打印遭到中断 printf ( "\r\n定时1s,通过时间戳测得定时 %07d us,即 %04d ms。\r\n", ts_end / ( cpu_clk_freq / 1000000 ), //将定时时间折算成 us ts_end / ( cpu_clk_freq / 1000 ) ); //将定时时间折算成 ms OS_CRITICAL_EXIT(); ts_start = OS_TS_GET(); //获取定时前时间戳 } static void AppTaskTmr ( void * p_arg ) { OS_ERR err; OS_TMR my_tmr; //声明软件定时器对象 (void)p_arg; /* 创建软件定时器 */ OSTmrCreate ((OS_TMR *)&my_tmr, //软件定时器对象 (CPU_CHAR *)"MySoftTimer", //命名软件定时器 (OS_TICK )10, //定时器初始值,依10Hz时基计算,即为1s (OS_TICK )10, //定时器周期重载值,依10Hz时基计算,即为1s (OS_OPT )OS_OPT_TMR_PERIODIC, //周期性定时 (OS_TMR_CALLBACK_PTR )TmrCallback, //回调函数 (void *)"Timer Over!", //传递实参给回调函数 (OS_ERR *)err); //返回错误类型 /* 启动软件定时器 */ OSTmrStart ((OS_TMR *)&my_tmr, //软件定时器对象 (OS_ERR *)err); //返回错误类型 ts_start = OS_TS_GET(); //获取定时前时间戳 while (DEF_TRUE) { //任务体,通常写成一个死循环DEF_TRUE printf("将任务置为等待态1s"); OSTimeDly ( 1000, OS_OPT_TIME_DLY, & err ); //给任务提供定时 } }
那再啰嗦一下回调函数,上面这个回调函数的类型声明如下:
typedef void (*OS_TMR_CALLBACK_PTR)(void *p_tmr, void *p_arg);
显而易见的是,我们自定义的回调函数的返回类型OS_TMR_CALLBACK_PTR其实就是一个void指针(而这个指针是一个函数指针),为了避免晦涩的基础类型,所以typedef一下,便于理解。
那再说一下void
1.当函数返回为空或参数列表为空使用。
2.void*指向任何类型,只不过使用时要转换成具体类型,才能操作。(上面的回调函数的实现有应用到)。
仔细将这些话与代码对应起来体会,很有意思的。。。