开发者学堂课程【嵌入式之RFID开发与应用2020版:事件驱动型OSAL系统原理分析】学习笔记与课程紧密联系,让用户快速学习知识
课程地址:https://developer.aliyun.com/learning/course/665/detail/11129
事件驱动型OSAL系统原理分析
1、0SAL, Operating , System A bstraction Layer ,即操作系统抽象层。
2、从 Z -Stack1.4.3及以后开始引入OSAL 。
3、0SAL是一种基于事件驱动的轮询式操作系统,所提供的管理功能有:
(1)任务登记、任务初始化、任务触发
(2)任务间消息传递
(3)任务同步
(4)中断处理
(5)计时器
(6)内存分配
抛开Cc2530芯片单独看OSAL系统。对协议栈如果没有操作系统去支撑,那么它的整个运行,也是可以的。但是很多的逻辑的处理需要自己去做,有了操作系统它的整个的处理就会变得更加的有序也不容易出错。OSAL就是操作系统抽象层的一个缩写。在TI从1.4.3的这个协议栈开始就已经引入了OSAL系统。
为了缩小的体积,变成一个低功耗轻量级的,之前就没有这个系统,或者说很多方案里面就没有这个系统。OSAL操作系统并不是linux windowsWIN七这种大型的操作系统。
这个操作系统更多是像管家一样来管理着各种通讯事件的执行,包括一些定时器内存分配这些工作,它的核心是基于事件驱动。当有事情的时候,创建了任务才会被执行,如果没有事情,这个任务是不执行的,这样节省了时间和空间。他不是一个时间片的,也不会去抢占。他其实就是一个纯事件驱动的,他提供的功能有任务的登记注册初始化,还有一个处罚的方式,任务就是所谓的操作系统,最核心的五大功能之一就是任务调度,所以就把他叫进程或者线程。
当系统里面要用到一些硬件资源,第二个功能就是对软硬件资源进行管理,所以说OSAL也提供了操作Cc2530的管理,包括它的中断定时器IO按键。他其实提供了一套完整的流程,包括串口的操作,还有定时器怎么使用,申请内存怎么去分配都由OSAL来提供服务。
4、osal run system()不断轮询遍历所有任务事件,事件被置位后就会被调度执行该任务:
(1)需要注意的是每次任务被调度时都只处理一个事件,并在处理完后清除该事件。
整个操作系统的核心是靠这个osal run system来进行不断轮询遍历所有事件。
1019:*/
1020: void osal _ start _ system ( void )
1021:{
1022:# if ! defined ( ZBIT )&& Idefined ( UBIT)
1023: for (;;)// Forever Loop
1024:# endif
1025: {
1026: osal _ run _ system ();
1027: }
1028:}
1029:
1030:/**********************************************
1031: *@ fn osal _ run _ system
1032:*
1033:*@ brief
1034:*
1035:*This function wil1 make one pass through the oSAL taskEvents table
1036:* and cal1 the task _ event _ processor () function for the first task that
Start system其实就调了这一个函数,而且他是一个死循环。
它会不断去轮询所有的这些事件有没有发生,总之他的所有的事情都是由这个函数在不断轮询去发生的。这个函数是在我们的mian函数里面调的。这个函数的上面其实就是mian函数,所以这个操作系统首先由mian函数完成有关操作系统一系列的初始化,最后会停在osal run system这个死循环里面。
整个的操作系统是由事件来驱动任务调度什么事件,比如这个主要是用在无线通信里面。假设收到了一个数据,这就发生了一件事情,有一个任务是专门处理接收事情接收消息的,当它接收到消息的时候它的底层就会去设置这个标志位告知某个任务需要去处理。所以当事件发生的时候它驱使这个系统去找到置位的事件,并调用这个任务完成对接受这个事件的处理。当他把这个事情处理完了之后这个任务又停止了,除非这个任务还没处理完。每次只处理一件事情。
如果有两件事情,处理完这件事情之后退出去,下次再轮询的时候,发现另一个事件再去处理,另一个事件可能又有另一个任务去处理,这都是有可能的。
5、任务数组taskArr[]中的任务定义:
(1)unsigned short (*pTaskEventHandLerFn)(unsigned char task_id, unsigned short event);
106:*/
107:void osalInitTasks ( void )
108:{
109:uint8 taskID =θ
110:
111: tasksEvents =(uint16*) osal _ mem _ alloc ( sizeof (uint16)* tasksCnt );
112: osal _ memset ( tasksEvents ,θ,( sizeof (uint16)* tasksCnt ));
113:
114: macTaskInit ( taskID ++);
115: nwk _ init ( taskID ++)
116: Hal _ Init ( taskID ++)
117:# if defined ( MT _ TASK )
118: MT _ TaskInit ( taskID ++);
119:# endif
120:APS _ Init ( taskID ++);
121:# if defined ( zIGBEE _ FRAGMENTATION )
122: APSF _ Init ( taskID ++);
123:# endif
任务其实就是一个函数,创建一个线程必须指定一个函数,Java也是一样的,这个函数专门服务于这个进程,这个线程。
那进程的意义,可能还不太一样,但对于内核来说都叫任务,这个函数专门负责处理这个任务的事情。简单的理解,一个任务其实就是一个函数,系统里面有很多的任务可以创建很多的线程。每一个任务,每一个线程都需要一个函数,从而形成函数列表。这个函数列表是以函数指针数组的形式出现的。这个函数有一个返回值还有一个函数指针名。
第一个就是这个任务的ID,第二个是事件。当任务处理函数被调用的时候会给传一个ID进去,ID是区分不同任务的唯一标识,每增加一个任务ID就会增加一个。从零开始依次增加。每一个任务都有一个事件,都有一组事件标志位,而这一组事件标志位总共可以支持16位,也就是完整形的16位也就是每一个任务最多可以同时标记16个事件。
在这个任务函数指针数组里面存放着所有的任务,事件指实际上指向一系列。完整型的一个变量,这个变量里面有16位每一位代表一个事件,当这个标志位为1就表示这个事件处罚了,如果为0就表示这个时间没有处罚。前面的taskID是任务编号,每增加一个任务,他就加一,递增的,对这个任务进行调度执行之前的第一步是对每一个任务进行初始化,osinitTask其实就是在对我们的任务进行一个初始化。
这个ID的初始值是零,每初始化一个任务他就加一。
每一个任务都对应一个16位的短整型。有一个变量指针叫做task event。他指向了所有的任务的第一个任务的那个起始地址。在提取任务的事件的时候,会给他传一个任务号。这个Index就任务号,然后他这个任务号传进去后把这个短整型提取出来来判断这个是不是非零的。这里面任何一个都不可能是非零的。如果是非零的,就表示要处理这个事件。对这个事件进行处理的时候,这个事件本身是一位标志,比如说1左移N位,N是0~145这么一个取值。判断事件的时候也是用1左移N位,如果是非零就表示要处理这个事件,处理完这个事件之后还要用1左移N位抑或这个变量,目的就是将这个1拉新程里,表示这个事件处理完了,完了之后要把这个结果返回taskArr,其实就是去调用这个函数把任务号传进去,把当前的事件传进去。返回的这个事件就是他本次任务调度没有处理完的,剩下的事件它就会再次还给task event等待下次任务调度去处理。这就是事件驱动型的osal系统。
6、每个任务最多可以同时设置16个事件,但有些位己经被系统定义事件占用,所以自定义事件时最好不要与其冲突,如:
(1)任务间消息收发事件 SYS _ EVENT _ MSG =0x8000
虽然最多可以同时设置16个事件。但是实际上里面有些事件已经被占用了。任务间通信,比如一号任务给二号任务发了一个消息,那这个算不算是有事件发生?是有事件发生的,所以二号任务要去处理这个消息。所以任务间通信其中有一个标志位已经被占用其实就最后那一位。
7、taskEvents 事件要和后面 zigbee 协议栈中的 afIncomingMSGPacket _ t -> hdr . event 这个8bit的消息事件加以区别
8、指定任务添加事件:
(1)osal _ set _ event (uint8 task _ id ,
uint16 event _ flag )
tasksEvents [ task _ id ]= event _ flag ;
9、指定任务清除事件:
(1)osal _ clear _ event (uint8 task _ id ,
uint 16 event _ flag )
tasksEvents [ task _ id ]&=~( event _ flag);
如果启动某一个任务去执行就必须得设置事件,通过这个osal set event去设置事件。创建了那么多任务要让他处理哪一个任务处理哪一个事件, event Flag是1左移N位,也就是将16位里面的某一位置1了。
操作系统在轮询的过程当中,就会发现这个里面有不为零的这种事件,If idx是从零开始,最大值是不能超过他最后任务的总个数taskArr,任务总个数是任务数组的总大小除成员的大小。如果发现tasksEvents是短整型,它里面任何一个值非零,Break表示有置位了。有置位后面就回去处理这个事情,所以只要设置了这个事件轮询的时候就会发现这个时间,一旦发现这个事件就会去调用某个任务,而这个任务在处理完了之后他会怎么样的时间清除这个事件。这个是整个操作系统通过事件。一步步去驱动。
10、任务事件被置位,即任务调度,主要通过以下两种途径实现:
(1)直接通过调用 osal _ set _ event()给任务事件置位。
(2)任务调度结束后返回,通过返回未处理完的事件位重新置位。
11、还有间接通过 osal _ set _ event ()置位的情况,例如:
(1)一个任务给另一个任务发消息
(2)定时触发事件设置 osal _ start _ timerEx()
(3)zigbee 协议栈底层触发调用
不管是直接还是间接他都是通过什么osal set event去完成事件的设置,并且通过轮询去发现这个事件去调用任务处理函数,这里提到事件的设置有很多种情况,比如一个任务给另一个任务发了消息,他会去设置。定时器时间到了,或者是设置到某一个定时也会调用这个去设置。还有协议栈的底层收到的数据,比如接收到数据也是。所以只要有事情发生都通过它来完成事件的设置,并且通过Run system去轮询出哪个事件,然后通过task Arr将没有处理完的事情附给event,然后再把它放回去,这次循环就结束了。结束之后这个函数就退出,下次再进来的时候再去判断这个事件有没有被置位,有置位继续去重复执行下面这个过程,如果 do Well退出的时候发现idX等于最大值,就表示没有任何任务被置位也不需要去调动任何的任务。这就是任务事件驱动的osal系统。