学习达到效果:
1)了解UCOS工作运行的原理;
2)了解UCOS的文件结构,能够自行阅读相关文件;
3)掌握UCOS的API函数。多写,多练。
学习的主要文档:
1)UCOSII源码;
2)μCOS-II_2.92 API函数详解 v2.0
3)嵌入式实时操作系统μCOS-II原理及应用-任哲(高清版本)
第1章 操作系统的概念
1.1 操作系统的基本概念
操作系统的定义:操作系统(Operating System,OS)介于硬件和软件之间,为开发者提供API(Application Programming Interface)函数接口,屏蔽硬件细节,提高开发效率。
操作系统的分类:实时操作系统(FreeRTOS,UCOS,RTX,RT-Thread。)和分时操作系统(高版本的Linux,windows)。
RTOS:Real Time Operating System 。一般用于低端场合,芯片的主频较低。实时性:对于重要的事件能够及时响应,可能同时发生很多事情,虽然MCU不可能同时全部处理,但是他一定能够处理最重要的一件事。在工业产品中有很多应用。
UCOS:UCOS的学习资料丰富。代码体系完善。
前后台系统:
裸机代码:前后台系统,一般将while(1)循环里面的程序称之为后台代码,将中断服务函数称之为前台代码。程序功能是通过在主函数当中调用一个个的子函数,并通过控制语句控制程序流向。
我们一般在中断服务函数修改标志位,在主函数判断标志位处理任务,但是这中间会有时间延时,称之为任务延时,当我们的主函数功能越来越多,判断条件越来越多,会导致操作时间越来越长,功能维护越来越困难。
操作系统:通过任务调度器对内核资源的管理(管理CPU的使用权),能够实现对多个任务的及时、并发性(每次还是只有一个任务运行,任务切换很快)的处理。
UCOS的核心是任务,程序功能是通过一个个任务去实现的。每个任务都有一个while(1)死循环,没有退出条件,没有返回值, 我们是通过在任务中调用系统提供的API实现任务和任务之间的转换。
1.2 UCOS操作系统
1.2.1 UCOS的源码体系结构
与处理器无关 | os_core.c | 系统核心文件 |
os_flag.c | 任务通信:事件标志组底层文件 | |
os_mbox.c | 任务通信:消息邮箱 | |
os_mem.c | 内存管理:实现内存动态分配,以及释放功能类似标准C malloc free | |
os_mutex.c | 任务通信:互斥锁支持 | |
os_q.c | 任务通信:消息队列支持 | |
os_task.c | 任务管理 | |
os_time.c | 时间管理相关函数 | |
os_tmr.c | 软件定时器相关函数 | |
与处理器有关 | os_cpu_c.c,os_cpu_a.s | 移植时候需要修改的文件 |
os_cpu.h | cpu相关的头文件 | |
| ucos_ii.h | μC/OS中所有可用API函数声明文件 |
1.2.2 UCOS系统的特点
微型化: 代码量很少,最小可以是8K左右。
专用性强:UCOS系统能够应用在很多低端芯片上,比如STM32。
可裁剪: 我们在移植时,只需要在OS_CFG_H文件中将相应的宏开关关闭,就能裁剪掉我们不需要的代码程序,达到系统裁剪的目的。
实时性高:因为UCOS操作系统是抢占任务式操作系统,系统正在执行的任务一定是就绪的任务当中,任务优先级最高的。
第2章 UCOS任务
2.0.1 任务的概念
什么叫任务?在系统中,可以先简单的认为每个UCOS任务就是一个while(1)循环。
在UCOS系统中,程序的流向不是由主函数决定的。而是由系统分配CPU使用权的。
裸机代码,没有跑操作系统,程序基本都是在while(1)循环里实现,同一时刻,只会执行一个while(1)循环,while(1)循环的切换一般用break实现。 | 跑系统,每个任务就是一个while(1)循环,可以多个while(1)循环并行执行(两个循环的切换非常快,延时很小。) |
int main(void) { //各模块初始化 while(1) { //通过各个控制语句控制程序流向 } } | int main(void) { //启动UCOS系统 }
void task1(void) { //这部分代码也会执行,但是只会在任务第一次执行时执行 while(1) { //只要任务还在运行,就会继续执行 } }
void task2(void) { //这部分代码也会执行,但是只会在任务第一次执行时执行 while(1) { //只要任务还在运行,就会继续执行 } }
|
2.0.2 任务状态
2.0.3 任务间关系
2.1 任务优先级
2.1.1 任务优先级
2.8.6版本之后的UCOS有256个不同的优先级设置,优先级用数字表示0~255,数字越小优先级越高。系统已经默认将最低优先级255分配给空闲任务;如果使用了统计任务,系统还会将优先级254分配给统计任务。一般将软件定时器的优先级设定为最高0。(我们移植的ucos系统,只允许设置64层优先级,可以在配置文件修改。)
一般不建议将任务设置为最高的几个优先级,建议分配中间那部分的优先级。留出较高和较低方便拓展。
注意:每个任务对应一个优先级,一个优先级一定是只能对应一个任务。
2.1.2 空闲任务
由于任务总是在5种状态之间切换,所以系统极有可能在某一时刻没有任务可执行,但是停止CPU工作会导致一些莫名错误,所以系统创建了一个空闲任务,当系统中的其他任务都不执行时,系统就会运行空闲任务。
2.1.3 统计任务
统计任务统计CPU在1秒钟内被其他任务占用的时间,并将结果保存在全局变量 OSCPUsage当中
2.2 任务调度
UCOS任务切换是通过任务调度器实现的,任务调度器的任务调度,只有在正在运行的任务放弃CPU使用权,或者发生任务抢占时才工作,中断退出。
2.3 任务控制块
UCOS的任务控制块是用来记录任务的堆栈指针,当前状态,优先级等等与任务相关的信息,每个任务被创建时,UCOS都会分配给她一个空白的任务控制块用以存储该任务的相关信息。当一个任务被删除之后,他的任务控制块又可以重新使用。
任务控制块:TCB,task control block。
该控制块的定义在ucosii.h文件的 530行。
第3章 新建UCOS工程
3.1 需要的资料
1) 一个已经完整的裸机代码
2) 相应的UCOS源文件,官网下载micrum
3) os_cpu.h,os_cpu_a.asm,os_cpu_c.c,os_cfg.h,includes.h
3.2 第一步
将相关文件添加到工程,根据编译提示,去除相应文件,并且到os_cfg.h文件修改宏开关量。
3.3 第二步
使用滴答定时器为系统生成时钟节拍,调用相关函数完成节拍统计。
3.4 第三步
初始化系统,建立任务,启动UCOS
第4章 UCOS系统初始化
4.1 系统节拍介绍
UCOS系统任务切换是以时间片为单位的,时间片即是UCOS系统的心跳、系统节拍。可以用滴答定时器实现,或者其他定时器,在相关的定时中断中调用系统关于节拍统计的相关函数。
时钟节拍数:指的是系统一秒钟进入的定时器中断的次数。
假设系统节拍是200,那么请问滴答定时器多久产生一次滴答中断。
答:每个节拍时间是5ms。
在OS_CFG_H文件中,定义了时钟节拍数,建议初始化节拍时调用该量。
假设函数SysTick_Init_ms(u16 ms);要实现一秒钟200个节拍。
SysTick_Init_ms(1000/OS_TICKS_PER_SEC);
一个节拍的时间越小:节拍数越多,CPU越忙,系统的实时性更高。
一个节拍的时间越大:节拍数越少,CPU越闲,系统的实时性较差。
一般建议一个节拍5~10ms。节拍数:200~100;
滴答中断的优先级:应该配置为最低优先级。
4.2 系统节拍初始化代码
void SysTick_Init_xms(u32 ms) { SysTick->CTRL &=~(1<<0); //关闭滴答定时器 SysTick->CTRL &=~(1<<2); //选择时钟源为 21 MHz SysTick->CTRL &=~(1<<1); //关闭滴答中断 SysTick->LOAD = 21000*ms; //向自动重载寄存器写入计数值 SysTick->VAL =0; //初始化当前值寄存器 SysTick->CTRL |=1<<1; //打开滴答中断 NVIC_SetPriorityGrouping(5); //分组设置 NVIC_SetPriority(SysTick_IRQn,NVIC_EncodePriority(5,3,3) ); //优先级设置为最低 NVIC_EnableIRQ(SysTick_IRQn); //中断通道使能 SysTick->CTRL |=1<<0; //打开滴答定时器 } |
void SysTick_Handler(void) { OSIntEnter();//通知内核中断函数正在执行。 if( SysTick->CTRL & (1<<16)) { OSTimeTick();//节拍统计 } OSIntExit(); //通知内核一个中断服务已执行完毕。 } |
4.3 系统初始化
OSInit(); // 初始化 uC/OS-II // 最少创建一个任务 OSStart(); //启动多任务内核。 |
第5章 时间管理函数
5.1 时钟节拍初始化
在os_cfg.h 中有定义:#define OS_TICKS_PER_SEC 1000,意思是把 1 秒分成 1000 份,则每份是 1ms , 则一个时钟节拍就是 1MS ,一秒有1000个时钟节拍。
在使用滴答定时器初始化时,则
SysTick_Init(1000/OS_TICKS_PER_SEC);//一秒的节拍数是200 |
修改时钟节拍数,只需要修改OS_TICKS_PER_SEC的值。
5.2 任务延时函数OSTimeDly()
作用: 任务延时函数(时钟节拍数)。 实际是上放弃 CPU使用权,休眠去了。 函数原型: void OSTimeDly(INT32U ticks); 参数: ticks: 为要延时的时钟节拍数。 OSTimeDly(100); //延时100个节拍,500ms 注意:所有的任务当中都应该有放弃CPU使用权的操作或者命令,否则优先级低于此任务的其他任务就永远没有办法执行。 |
5.3 任务延时函数OSTimeDlyHMSM()
作用: 任务延时函数(时钟节拍数)。 函数原型: INT8U OSTimeDlyHMSM (INT8U hours, INT8U minutes, INT8U seconds, INT16U ms); 参数: hours: 为延时小时数,范围从 0-255。 minutes: 为延时分钟数,范围从 0-59。 seconds: 为延时秒数,范围从 0-59。 ms: 为延时毫秒数,范围从 0-999。需要说明的是,延时操作函数都是以时钟节拍为为单位的。实 际的延时时间是时钟节拍的整数倍。 例如:系统每次时钟节拍间隔是 10ms,如果设定延时为 5ms,将不产生任何延时操作,而设定延时 15ms,实际的延时是两个时钟节拍,也就是 20ms。 |
第6章 任务管理
6.1 任务创建函数
作用:创建一个新任务。 函数原型: INT8U OSTaskCreate(void (*task)(void *pd), void *pdata, OS_STK *ptos, INT8U prio); OSTaskCreate()建立一个新任务。任务的建立可以在多任务环境启动之前,也可以在正在运行的任务中建立。中断处理程序中不能建立任务。 一个任务必须为无限循环结构,且不能有返回点。 参数: task: 是指向任务代码的指针。 pdata:指向一个数据结构,该结构用来在建立任务时向任务传递参数。 ptos: 为指向任务堆栈栈顶的指针。 prio: 为任务的优先级。每个任务必须有一个唯一的优先级作为标识。数字越小,优先级越高。 栈顶栈底: 数组buff[64],最高地址&buff[63] 数据地址由高地址向低地址增长, buff[63](栈顶)-> buff[0](栈底) |
6.2 任务删除函数
6.2.1 直接删除
作用: 删除任务,不让再有机会调度,除非重新创建。 函数原型: INT8U OSTaskDel(INT8U prio); 参数: prio: 为指定要删除任务的优先级,也可以用参数 OS_PRIO_SELF 代替,此时,下一个优先级最高的就绪 任务将开始运行。 |
6.2.2 请求删除任务函数
作用: 请求一个任务删除其它任务或自身。 函数原型: INT8U OSTaskDelReq(INT8U prio); 参数: prio: 为要求删除任务的优先级。如果参数为 OS_PRIO_SELF,则表示调用函数的任务正在查询是否有来自 其他任务的删除请求。 返回值: OSTaskDelReq()返回下列错误代码之一: OS_ERR_NONE: 删除请求已任务记录。 OS_ERR_TASK_NOT_EXIST:指定的任务不存在。发送删除请求的任务可以等待此返回值看是否成功。 OS_ERR_TASK_IDLE: 尝试删除空闲任务,不允许。 OS_ERR_PRIO_INVALID: 指定任务的优先级比 OS_LOWEST_PRIO 高于或不指定 OS_PRIO_SELF。 OS_ERR_TASK_DEL: 任务被分配到一互斥。 OS_ERR_TASK_DEL_REQ: 当前任务收到来自其他任务的删除请求。 |
第7章 任务间通信
7.1 任务间通信
在裸机代码当中,功能是通过各个函数实现的。
各个函数之间如何同步、通信:参数传递,地址传递,返回值,全局变量。
在操作系统当中,功能是通过各个任务实现的,任务没有退出条件。
任务间通信:任务没有调用关系:参数传递,地址传递(不方便),只在任务创建时可用;全局变量。
i=10;i++; 假如中断当中对变量i执行操作i--。最后i=?
1)假如已经获得了i的值,然后中断发生,中断退出时不再去提取i的值。
2)中断当中,i--,获取i的当前值。i=10;i--;i=9;
3)之前将i的值10,已经被保存到栈区,直接使用,最后i=11;
为了实现任务间的实时性,可靠的通信。我们引入事件的概念。
事件:实现任务间的通信、同步。
事件的类型:信号量、消息邮箱、消息队列、互斥型信号量、事件标志组。
7.1.1 什么是事件
任务间通信需要用到事件。
事件类型:信号量,消息邮箱,消息队列,互斥型信号量,事件标志组。
事件控制块:当我们创建一个事件的时候,UCOS就会创建一个相应的事件控制块,事件控制块用来存储事件的相关信息,本质是一个结构体。
typedef struct os_event { INT8U OSEventType; void *OSEventPtr; INT16U OSEventCnt; OS_PRIO OSEventGrp; OS_PRIO OSEventTbl[OS_EVENT_TBL_SIZE]; #if OS_EVENT_NAME_EN > 0u INT8U *OSEventName; #endif } OS_EVENT; |
7.2 信号量创建和使用
信号量:信号量用于对共享资源的访问。信号量标识了共享资源的有效可被访问数量,要获得共享资源的访问权,首先必须得到信号量这把钥匙。信号量本质是一个u16的整型变量,我们对信号量的操作,其实都是在对这个变量进行++或者--的操作,或者赋值操作。
我们使用信号量,只需要将它当作一个变量使用。(定义、++、--,赋值)
作用:所有的你在裸机代码当中全局变量(u16)能完成的事情,都能完成。
7.2.1 创建信号量
作用: 建立并初始化一个信号量。 函数原型: OS_EVENT *OSSemCreate(INT16U value); 参数: value 参数是建立的信号量的初始值,可以取 0 到 65535 之间的任何值。 返回值: OSSemCreate()函数返回指向分配给所建立的消息邮箱的事件控制块的指针。如果没有可用的事件控制块, OSSemCreate()函数返回空指针。 |
7.2.2 发送信号量
作用: 发出一个信号量,把信号值加 1。 函数原型: INT8U OSSemPost(OS_EVENT *pevent); OSSemPost()函数置起(把信号值加 1)指定的信号量。如果指定的信号量是零或大于零, OSSemPost()函数递 增该信号量并返回。如果有任何任务在等待信号量,最高优先级的任务将得到信号量并进入就绪状态,任务调度 函数将进行任务调度。 参数: pevent: 指向信号量的指针。 |
7.2.3 无等待查看信息
作用: 无等待查看消息邮箱是否收到消息。 函数原型: INT16U OSSemAccept(OS_EVENT *pevent); OSSemAccept()函数查看设备是否就绪或事件是否发生。不同于 OSSemPend()函数,如果设备没有就绪, OSSemAccept()函数并不挂起任务。中断调用该函数来查询信号量。 参数: pevent: 是指向需要查询的设备的信号量。当建立信号量时,该指针返回到用户程序。(参考 OSSemCreate ()函数)。 返回值: 当调用 OSSemAccept()函数时,设备信号量的值大于零,说明设备就绪,这个值被返回调用者,设备信号量的值减一。如果调用 OSSemAccept()函数时,设备信号量的值等于零,说明设备没有就绪,返回零。 |
7.2.4 挂起任务等待信号量
作用: 申请一个信号量(挂起任务等待信号量)。 函数原型: void OSSemPend (OS_EVENT *pevent, INT32U timeout, INT8U *perr); OSSemPend()函数用于任务试图取得设备的使用权,任务需要和其他任务或中断同步,任务需要等待特定事 件的发生的场合。 如果任务调用 OSSemPend()函数时,信号量的值大于零, OSSemPend()函数递减该值。 如果调用时信号量等于零, OSSemPend()函数函数将任务加入该信号量的等待队列。 参数: pevent: 指向消息邮箱的指针。 timeout: 任务等待超时周期, 为 0 时表示无限等待; 其它, 递减到 0 时任务恢复执行。 perr: 做为输出参数使用, 指向错误代码变量的指针。 |
7.2.5 设置信号量的值OSSemSet()
作用: 改变当前信号量的计数值。 当信号量被用于表示某事件发生了多少次的情况下会使用。 参数: |
7.3
消息邮箱
7.4 消息邮箱
7.4.1 建立消息邮箱
作用: 建立并初始化一个消息邮箱。 函数原型: OS_EVENT *OSMboxCreate (void *pmsg); 参数: msg: 参数用来初始化建立的消息邮箱。如果该指针不为空, 则建立的消息邮箱将含有消息。 返回值: 指向分配给所建立的消息邮箱的事件控制块的指针。如果没有可用的事件控制块,返回空指针。 |
7.4.2 发送消息
作用: 向邮箱发送一则消息。 函数原型: INT8U OSMboxPost (OS_EVENT *pevent,void *pmsg); OSMboxPost()函数通过消息邮箱向任务发送消息。消息是一个指针长度的变量,在不同的程序中消息的使用 也可能不同。如果消息邮箱中已经存在消息,返回错误码说明消息邮箱已满,OSMboxPost()函数立即返回调用者, 消息也没有能够发到消息邮箱。 如果有任何任务在等待消息邮箱的消息,最高优先级的任务将得到这个消息。 如 果等待消息的任务优先级比发送消息的任务优先级高,那么高优先级的任务将得到消息而恢复执行,也就是说, 发生了一次任务切换。 参数: pevent: 指向消息邮箱的指针。 pmsg: 是即将发送给任务的消息。消息是一个指针长度的变量,在不同的程序中消息的使用也可能不同。不允许传递一个空指针。 |
7.4.3 获取消息
作用: 无等待查看消息邮箱是否收到消息。 函数原型: void *OSMboxAccept(OS_EVENT *pevent); OSMboxAccept()函数查看指定的消息邮箱是否有需要的消息。不同于 OSMboxPend()函数,如果没有需要的消息,OSMboxAccept()函数并不挂起任务。如果消息已经到达,该消息被传递到用户任务并且从消息邮箱中清除。通常中断调用该函数,因为中断不允许挂起等待消息。 参数: pevent: 是指向需要查看的消息邮箱的指针。当建立消息邮箱时,该指针返回到用户程序。(参考 OSMboxCreate()函数)。 返回值: 如果消息已经到达,返回指向该消息的指针; |
7.5 作业
用消息邮箱实现将触摸屏得到的LCD坐标的值通过串口打印出来,要求用两个任务完成。
注意:要使用触摸屏,需要进行相应的初始化。
7.6 消息队列
消息队列其实就是多个消息邮箱的组合,消息邮箱一般用在发送方发送较慢,接收方处理消息较快的时候;
消息队列多用在发送方发送消息较快,接收方处理消息较慢的时候。
7.6.1 创建消息队列
作用: 建立并初始化一个消息队列。 函数原型: OS_EVENT *OSQCreate (void **start,INT16U size) OSQCreate()函数建立一个消息队列。任务或中断可以通过消息队列向其他一个或多个任务发送消息。消息 的含义是和具体的应用密切相关的。 参数: start: 是消息内存区的基地址,消息内存区是一个指针数组。 size: 是消息内存区的大小。 |
7.6.2 按规则发送消息FIFO
作用: 向邮箱发送一则消息(FIFO)。 函数原型: INT8U OSQPost (OS_EVENT *pevent,void *pmsg); OSQPost()函数通过消息队列向任务发送消息。消息是一个指针长度的变量,在不同的程序中消息的使用也 可能不同。 如果队列中已经存满消息,返回错误码;OSQPost()函数立即返回调用者,消息也没有能够发到队列。 如果有任何任务在等待队列中的消息,最高优先级的任务将得到这个消息。 如果等待消息的任务优先级比发送消息的任务优先级高,那么高优先级的任务将得到消息而恢复执行,也就是说,发生了一次任务切换。 消息队列是先入先出(FIFO)机制的,先进入队列的消息先被传递给任务。 参数: pevent: 指向消息队列的指针。 pmsg: 是即将发送给任务的消息。消息是一个指针长度的变量,在不同的程序中消息的使用也可能不同。 不允许传递一个空指针。 |
7.6.3 发送LIFO消息
作用: 向邮箱发送一则消息(LIFO)。 函数原型: INT8U OSQPostFront (OS_EVENT *pevent,void *pmsg); OSQPostFront()函数通过消息队列向任务发送消息。 OSQPostFront()函数和 OSQPost()函数非常相似, 不同之处在于 OSQPostFront()函数将发送的消息插到消息队列的最前端。也就是说,OSQPostFront()函数使得消息队列按照后入先出(LIFO)的方式工作,而不是先入先出(FIFO)。 如果队列中已经存满消息,返回错误码。 OSQPost()函数立即返回调用者,消息也没能发到队列。如果有任何任务在等待队列中的消息,最高优先级的任务将得到这个消息。如果等待消息的任务优先级比发送消息的任务优先级高,那么高优先级的任务将得到消息而恢复执行,也就是说,发生了一次任务切换。 |
7.6.4 查看消息队列
作用: 无等待查看消息队列是否收到消息。 函数原型: void *OSQAccept (OS_EVENT *pevent,INT8U *perr); OSQAccept()函数检查消息队列中是否已经有需要的消息。不同于 OSQPend()函数,如果没有需要的消息, OSQAccept()函数并不挂起任务。如果消息已经到达,该消息被传递到用户任务。通常中断调用该函数,因为中 断不允许挂起等待消息。 参数: pevent: 是指向需要查看的消息队列的指针。当建立消息邮箱时,该指针返回到用户程序。(参考 OSQCreate () 函数)。 perr: 指向一个用于保存错误代码的变量。 |
7.7 互斥型信号量
7.7.1 创建互斥信号量
作用: 建立并初始化一个互斥型信号量。 函数原型: OS_EVENT *OSMutexCreate (INT8U prio,INT8U *perr); 参数: prio: 较高的空闲优先级,用于任务提权。 perr: 指向一个用于保存错误代码的变量。 |
7.7.2 申请互斥信号量
作用: 无等待请求互斥型信号量。 函数原型: BOOLEAN OSMutexAccept (OS_EVENT *pevent,INT8U *perr); 参数: pevent: 指向要请求的互斥信号量的指针。 perr: 指向一个用于保存错误代码的变量。 |
7.7.3 释放互斥信号量
作用: 释放一个互斥型信号量。 函数原型: INT8U OSMutexPost (OS_EVENT *pevent); 参数: pevent: 指向一个互斥型信号量的指针。 |
7.8 软件定时器
7.8.1 创建软件定时器
作用: 创建软件定时器。 函数原型: OS_TMR *OSTmrCreate(INT32U dly, INT32U period, INT8U opt, OS_TMR_CALLBACK callback, void *callback_arg, INT8U *pname, INT8U *perr); 参数: dly: 指定使用的计时器初始延迟时间。 l 在单次触发模式时,这是单次定时的时间。 l 在周期模式下,这是最初定时器进入周期模式之前的延时。 l 延时单位取决于你多久调用一次 OSTmrSignal()。如果 OS_TMR_CFG_TICKS_PER_SEC 设置为 10,则 DLY 指定延迟时间为 1/10 秒(也就是 100ms)。 OS_TMR_CFG_TICKS_PER_SEC 设置为多 少,就是 1/OS_TMR_CFG_TICKS_PER_SEC 秒。 需要注意的是在创建时的定时器不能启动。 period: 指定计时器周期性定时的时间量。 如果设置为 0,那么将使用单次模式。 opt: OS_TMR_OPT_PERIODIC:指定周期性定时模式。 OS_TMR_OPT_ONE_SHOT:指定单次定时模式。 请注意,必须选择其中一个选项。 callback: n 指定一个回调函数的地址(可选),这个函数将被调用,处理定时结束的相关事项,回调函数必须 声明如下: void MyCallback (void *ptmr, void *callback_arg); n 如果不需要回调函数,可以传入一个空指针。 callback_arg: 是传递给回调函数参数,如果回调函数不需要参数,可以是一个空指针。 pname : 指向一个 ASCII 字符串,用于设置定时器名称。通过调用 ostmrnameget()获取这个名称。 perr: 指向存放错误代码的指针, |
7.8.2 启动软件定时器
作用: 启动计时器。 函数原型: BOOLEAN OSTmrStart(OS_TMR *ptmr,INT8U *perr); 参数: ptmr: 指向想要启动的定时器。该指针的值为在创建定时器时返回的值(见 OSTmrCreate())。 perr: 指向存放错误代码的变量。 |
7.9 临界宏
OS_CPU_SR cpu_sr; OS_ENTER_CRITICAL(); //核心代码 OS_EXIT_CRITICAL(); |
7.10 作业