开发者学堂课程【嵌入式之RFID开发与应用2020版:向OSAL系统添加自定义任务】学习笔记与课程紧密联系,让用户快速学习知识
课程地址:https://developer.aliyun.com/learning/course/665/detail/11131
向OSAL系统添加自定义任务
OSAL系统例子分析
接下来主要讲述OSAL系统的例子以及其如何使用。
要在OSAL基础上创建自己的任务,基本的流程是,首先来看代码的执行过程,如下:
首先进入到主程序当中,如下:
int main ( void )
// Turn off interrupts
osalint _ disable ( INTS _ ALL );
// Initialization for board related stuff such as LED HAL _ BOARD _ INITO );
// Make sure supply voltage is high enough to run zmain _ vdd _ check ();
// Initialize board I/0
// InitBoard ( OB _ COLD );
InitBoard ( OB _ READY );
// Initialze HAL drivers HalDriverInit ();
// Initialize NV System
osal _ nv _ init ( NULL );
// Initialize the MAC
// Initialize the timers osalTimerInitO )
// Initialize the Power Management System
OsalpWrmgr init )
// Initialize the system tasks .
osalInitTasks ();
// Setup efficient search for the first free block of heap osal _ mem _ kick ();
return ( SUCCESS);
}”end osal_ init _system”
在主程序中有很多有关的初始化,暂时先略过不重要的,首先是OSAL相关的,如关中断、按键相关的,还有一些变压键盘硬件,还有一些需要实时存储的数据。
与操作系统有关的首先是 osal_init_system,如下:
uint8 osalinit _ system ( void )
// Initialize the Memory A1location System Osalmem _ init ()
// Initialize the message queue osal _ qHead = NULL ;
// Initialize the timers OsalTimerInit )()
// Initialize the Power Management System
osalpWrmgrinit()
11 Initialize the system tasks osalInitTasks ();
1/ Setup efficient search for the first free block of heap . osalmem _ kick ()
return ( SUCCESS);
}”end osal_ init _system”
这里面有有关内存的初始化,包括定制器,任务,主要是任务,针对所有的任务,在此进行了统一的初始化,如下:
void osalInitTasks
uint8 taskID =0;
tasksEvents =(uint16*) osal _ mem _ alloc ( sizeof (uint16)* tasksCnt ); osal _ memset ( tasksEvents ,0,( sizeof (uint16)* tasksCnt ));
macTaskInit ( taskID ++); nwk _ init ( taskID ++);
Hal _ Init ( taskID ++);
# if defined ( MT _ TASK )
MT _ TaskInit ( taskID ++);# endif
APS _ Init ( taskID ++);
# if defined ( ZIGBEE _ FRAGMENTATION )
APSF _ Init ( taskID ++);
# endif
ZDApp _ Init (
taskID ++);
# if defined ( ZIGBEELFREQAGILITY ) II defined ( zIGBEE _ PANID _ coNFLICT )
ZDNwkMgr _ Init ( taskID ++);
# endif
1/SampleApp_ Init ( taskID ++);
my _ Init ( taskID ++);
这些任务是来自于一个数组 typeof unsigned short (*PTaskEventHandIerFn)(unsiged char task_id,unsiged short event):
这个数组是一个函数指针数,函数类型它包含任务的ID,任务的事件,以及返回未处理完的事件。
如下前面的一些任务是系统自带的:
macEVentLOop ,
nwk _ event _ loop , Hal _ ProcessEvent ,# if defined ( MT _ TASK
MTPrOcessEvent ,# endif
APS _ eventloop ,
# if defined ( ZIGBEE _ FRAGMENTATION
APSFProcessEvent ,# endif
ZDApp _ event _ loop ,
# if defined ( ZIGBEELFREQ AGILITY
ZDNwkMgr _ event _1oop,
# endif
其点进去看不到,只有一个声明,看不到它的实现,它的源代码被封装成库,后面还有很多是这样的。
下面是官方提供的参考案例,就是说明如何去写一个任务,如下:
uint16 SampleApp ProcessEVent (uint8 task _ id ,uint16 events ){
afIncomingMSGPacket _ t * MSGpkt ;
( void ) task _ id ;1/ Intentionally unreferenced parameter if ( events & SYS _ EVENT _ MSG )
{
MSGpkt =( afIncomingMSGPacket _ t *) osal _ msg _ receive ( SampleApp _ TaskID ); while ( MSGpkt )
switch ( MSGpkt -> hdr . event )
{
// Received when a key is pressed case KEY _ CHANGE :
SampleApp _ HandleKeys ((( keyChange _ t *) MSGpkt )-> state ,(( keyChange
break ;
// Received when a messages is received ( OTA ) for this endpoint case AF _ INCOMING _ MSG _ CMD :
SampleApp _ MessageMSGCB ( MSGpkt );
break ;
// Received whenever the device changes state in the network case ZDO_ STATE _ CHANGE ;
因为它本身是一个 forexample,可以在官方源码把它注释起来,可以去添加一个自己的任务,添加任务的本质就是创建一个函数,
如下My_process就是创建的属于自己的函数:
Uint16 My_ProcessEvent(uint8 task_id,uint16 event)
{
afIncomingMSGPacket _ t * MSGpkt = NULL ; if ( events & SYS _ EVENT MSG )
MSGpkt =( afIncomingMSGPacket _ t *) osal _ msg _ receive ( my _ TaskID ); while ( MSGpkt l = NULL ){
Switch ( MSGpkt -> hdr . event )
case ZDO _ STATE _ CHANGE :
identity _ nwk =( devStates _ t )( MSGpkt -> hdr . status );
if ( identitynwk = DEV _ END _ DEVICE ){
debug ("入网成功\ n ");
debug ("获得地址=0x%× In ", NLME _ GetShortAddr ();
# if CTRL _ OR _ GATHER //控制节点/采集节点入网成功后先发一个数据给协调器以确认
send _ coord _ affirm ( SAMPLEAPP _ CTRL _ CLUS
TERID )
;//这是不同
# else
send _ coord _ affirm ( SAMPLEAPP _ SENSOR _
cLuSTERID );//这是不同终端
# endif
break ;
case AF _ INCOMING _ MSGCMD ;
my _ MessageMSGCB ( MSGpkt );
break ;
注意的是函数的返回值和参数要尊重数组的类型、定义。在这个函数中具体要做什么事?这个不是特别重要,只要有一个函数,如果发生了什么事,这个函数就会被执行,
有了这个函数之后接着往下看:
void osalInitTasks ( void )
{
uint8 taskID =0;
tasksEvents =(uint16) osalmem _ alloc ( sizeof (uint16)* tasksCnt ). osal _ memset tasksEvents ,, sizeof (uint15)* tasksCnt ));
macTaskInit ( taskID ++ nwk _ init (
taskID ++);
Hal _ Init (
taskID ++);
# if defined ( MLTASK )
MT _ TaskInit ( taskTD ++);
# endif
APS Init # if defined
APSF _ Init (taskTD ++);
# endif
ZDApp _ Init (# if defined
ZDNwkMgrInitKtaskID ++);
# endif
nwk _ init (
Hal Init (
# if defined ( MTLTASK
MT _ TaskInit ( taskID ++);
# endif
APS _ Init
taskID++;
#1f defined
ZIGBEEL FRAGMENTATION
APSF _ Init (
taskTD ++);
# endif
ZDApp _ Init (taskTD ++);
#1f defined ( ZIGBEELFREQAGILITY defined ( ZIGBEE _ PANID _ CONFLICT
ZDNwkMgr _ Init ( taskID ++);
# endif
//SampleApp_ Init ( taskID ++);
my _ hit ( taskID ++);
首先在这个初始化中,会把所有的任务都去找一遍,会根据任务的个数去申请一块空间,这个空间就作为每一个任务所对应的完整型的事件,比如有10个任务就会申请10个完整型的数据,给到task events,首先把所有的事件全部清零,现在开始对所有的任务进行初始化,初始化非常重要的事情是,把每个任务的ID自己保存起来,
就是初始化函数中,给你分配了一个ID,比如这个是0,这个是1,这个是2…一直到后面,要自己把它存起来,不管 my_Init此ID是多少,要么存在静态中,要么是my _Task升级的,要么是全局变量。此处为了简操作单定义成全局变量,先把它保存到里面,后面是跟当前要做的事情相关的初始化,就是说创建这个任务是为了做什么?完成这些工作之后,将来任务被调动的时候能正常运行。
OSAL 任务创建:
定义自己的任务函数
定义任务初始化函数并保存任务ID
定义完成之后如何声明,不知道其源文件是什么,可以参考申报APP,这是官方提供的,可复制粘贴,简单修改一下
接下来等待任务何时被调度,这个是不一定的,任务何时被调动,是取决于对应的事件是否被置危,可能给任务发送了一个消息:
每个任务最多可以同时设置16个事件,但有
些位已经被系统定义事件占用,所以自定义事件时最好不要与其冲突,如:任务间消息收发事件 SYS EVENTMSG =0x8000
taskEvents 事件要和后面 zigbee 协议栈中的
afIncomingMSGPackett -> hdr . event
这个
8bit的消息事件加以区别
指定任务添加事件:
osal _ set _ event (uint8 task _ id ,uint16 eventflag )
tasksEvents [ task _ id ]= event _ flag ;
指定任务清除事件:
osal _ clear _ event (uint8 task _ id ,
uint16 event _ flag )
tasksEventsLtask _ id ]&=( event flag);
通过 osal set event 给某一个任务ID置危,那可能就被调度了,此处为了操作简单,演示,比如以串口输出去为例:
myTaskID = taskid ;
MTUartnit ()
;//串口初始化
MTUartRegisterTaskID ( task _ id )<>
咨记任务号GPI0初始化*7
P1DIR
P1DIR
P2INP&=~(1<<6);
//打开上拉 LOCAL _LED1= SWITCH _ LED _ CLOSE <>LOCALLED2
= SWITCHLED _ CLOSE
//P1_0定义为输出
//P11定义为输出
P1INP&=~(1<<3)()
P1SEL&=~((1<<)1(1<<1)(1<<3)
P1DIR=(1<<)(1<<1)(1<<3)()
SWITCHLED = SWITCHLED _ CLOSE ()
/*端点初始化*/
my _ epDesc . endPoint = MY _ ENDPOINT ; my _ epDesc . task _ id =8my_ TaskID ; my _ epDesc . simpleDesc
my _ epDesc . latencyReq = noLatencyReqs ;
if ( afRegister (8my_ epDesc )= afStatus _ INVALID _ PARANETER )
( SimpleDescmiptionFormat _ t *)& my _ SimpleDesc ;
debug
("端点注册失败\ n ");
else
首先来看初始化当中, UART串口已经做好,已经可以发送消息,直接搜索任务名,给了任务名消息,收到了一个串口的数据 psmg,
如下:
SampleApp
HalUARTRead ( port ,& buf [ i ],1);j++;
flag =1;
/把数据接收放到 buf 中/记录字符数
//已经从串口接收到信息
if ( flag ==1)
//已经从串口接收到信息
/AIl0Cate memory for the data
产/
//分配内存空间,为机构体内容+数据内容+1个记录长度的数据=( mtOSALSerialDatat ") osal _ msg _ allocate ( sizeof
DMSg
( mtOSALSerialData _ t )+ j +3);
//事件号用原来的 CMD SERIAL MSG pMSg -> hdr . event = CMD _ SERIAL _ MSG ; pMsg -> msg =(uint8*)( pMsg +1);11
pMsg
> msB
= port <>
pMsg -> msg [1]= J ;buf10]=0;
strcpy (( char *)&( pMsg -> msg [2]),( const char *) buf );
for ( i =0; i < j ; i ++)
pMsg -> msg [ i +2]= buf ];
osal msg _ send ( my _ TaskID ,( byte *) pMsg );1/
登记任务,发往上层
/* deallocate the msg “/
osal _ msg _ deallocate ((uint8*) pMSg );/
把数据定位到结构体数据部分
//给上层的数据第一个是串口号
//给上层的数据第二个是长度
//从第二个开始记录数据
//释放内存
发送完成之后任务才会被调用,然后判断这个消息,在其中要去掉一些无用的东西,Zigbee在此处是可有可无的,因为现在不讨论Zigbee的问题,将收到消息的case保留,前面的去掉,如下是表示串口的数据:
while ( MSGpkt != NULL ){
Switch ( MSGpkt -> hdr . event )
case CMD _ SERIAL _ MSG :
uint8 str [ UART _ RECV _ LEN ];
uint8 port =*((( mtoSALSerialData _ t *) MSGpkt )-> msg );uint8 len =*((( mtOSALSerialData _ t *) MSGpkt )-> msg +1) if ( len < UART _ RECV _ LEN )
osal _ memcpy ( str ,(( mtOSALSerialData _ t *) MSGpkt )-
elSe
break ;
if ( len >1){
*( str + len )=0;
debug (" uart % d (% d ):% s In ", port , len , str ); serial _ dispos ( port , len , str );
break ;
这个处理可以暂时不去调用,只需把串口的数据打印出来,
此循环的前面是接受第1次,如果不等于null,则表示接收到了消息,后面这个表示如果还有消息可以在未退出之前再接收一次,若接收到可直接退出,接收到之后继续处理,如下:
Osal _ memcpy ( str ,(( mtOSALSerialData _ t *) MSGpkt )-> ms
e1Se
break ;
if ( len >1){
*( str + len )=0;
debug (" uart % d (% d ):% s In ", port , len , str );
// serial _ dispos ( port , len , str );
break ;
Osalmsg _deal1ocate((uint8*) MSGpkt );
MSGpkt =( afIncomingMSGPacket _ t *) osal _ msg _ receive ( my _ TaskID );
}《
end while MSGpkt !
=NULL》
return ( events
^
SYS
_
EVENT
_
MSG );
else if
《
end
i
f
events8SYS
EVEN
T_MSG
》
else if
events & DEMO _ KEYBOARD _1)
{
debug ("key1 down In ");
my _ SendPointToPointMessage ( SAMPLEAPPSENSOR _ CLUSTERID , SET _ TEMP _ADJ1 mySendPointToPointMessage ( SAMPLEAPPSENSOR CLUSTERID , SET HUMI ADJI my _ SendPointToPointMessage ( SAMPLEAPP _ CTRL _ CLUSTERID , SET _ TEMP _ ADJUS my _ SendPointToPointMessage ( SAMPLEAPP _ CTRL _ CLUSTERID , SET _ HUMI _ ADJUS LOCAL _LED1= SWITCHLED _ OPEN ;
LOCAL _LED2
SWITCHLED _ OPEN ;
SWITCH _ LED =
SWITCH _ LED _ OPEN ;
后面的其他事件可暂时去掉,只处理如下一个事件,当有消息过来时,当有事件触发时,去判断是否发消息,如果发送消息则判断它是否是串口消息。
判断它是否是串口消息,借助构造时运用的CMD_SWRIAL_MSG,如果是串口消息,则直接打印,其他的不做处理。
初始化中关于Zigbee的东西无关紧要,操作完成之后,接下编写程序,
程序编写完成之后,当线串好之后,直接下载,下载完成之后,点击全速运行,此时需要打开串口,串口打开之后,试一下是否输出信息,此时目的不是运用Zigbee,只是通过串口发送数据,看是否能打印出来,打印的内容是“uart%d(%d):%s\n,port,len,str” 关口、长度和内容。
如下是试验:
如果发送一个hello,会在383行,第0个串口,长度为5,内容为hello。因此如果不给它发送任何消息,任务是不会执行的,或者是需通过其他方式设置事件标志,任务才能被执行。
如上就是整个osal要完成自己任务的添加,基本上要遵循的原则:创建自己的任务、初始化,确定ID,确定何时去调用设置事件的接口,也就是osal _set_event让任务具备执行的条件,那么这个任务就会被执行。