- 在
LEDApp_ProcessEvent()
《LEDApp.c 240行》函数里面case ZDO_STATE_CHANGE:
修改代码;后下载不同的代码 协调器CoordinatorEB
、路由器RouterEB
、终端EndDeviceEB
到开发板后,会看的不同的效果(协调器 点亮LED1) (协调器 点亮LED21、LED2 )(协调器 点亮LED2 )。
case ZDO_STATE_CHANGE:
LEDApp_NwkState = (devStates_t)(MSGpkt->hdr.status);
if ( (LEDApp_NwkState == DEV_ZB_COORD)//协调器
|| (LEDApp_NwkState == DEV_ROUTER)//路由器
|| (LEDApp_NwkState == DEV_END_DEVICE) )//终端
{
// Start sending "the" message in a regular interval.
// osal_start_timerEx( LEDApp_TaskID,
// LEDApp_SEND_MSG_EVT,
// LEDApp_SEND_MSG_TIMEOUT );
}
if(LEDApp_NwkState == DEV_ZB_COORD)//协调器
{
P0SEL &=~0x12;//LED引脚为通用口 p0~1 4
P0DIR |= 0x12; //输出模式
P0_4 = 0;//点亮led1
}
if(LEDApp_NwkState == DEV_ROUTER)//路由器
{
P0SEL &=~0x12;//LED引脚为通用口 p0~1 4
P0DIR |= 0x12; //输出模式
P0_4 = 0;//点亮led1
P0_1 = 0;//点亮led2
}
if(LEDApp_NwkState == DEV_END_DEVICE)//终端
{
P0SEL &=~0x12;//LED引脚为通用口 p0~1 4
P0DIR |= 0x12; //输出模式
P0_1 = 0;//点亮led2
}
- 使用函数
osal_set_event(LEDApp_TaskID,LEDApp_SEND_MSG_EVT);
:处理LEDApp_TaskID的LEDApp_SEND_MSG_EVT事件函数《LEDApp.c 343行》。
osal_start_timerEx(LEDApp_TaskID,LEDApp_MY_EVT,5000);
:等待5000us处理LEDApp_TaskID的 LEDApp_SEND_MSG_EVT事件函数《LEDApp.c 343行》。
注:我这做了修改 跳到这里是就是点亮点亮LED2,osal_set_event函数不会延时,osal_start_timerEx函数会有一定延时(更具自己设定时间,我这是5000us延时时间)
if ( events & LEDApp_SEND_MSG_EVT )
{
// // Send "the" message
// LEDApp_SendTheMessage();
//
// // Setup to send message again
// osal_start_timerEx( LEDApp_TaskID,
// LEDApp_SEND_MSG_EVT,
// LEDApp_SEND_MSG_TIMEOUT );
P0SEL &=~0x12;//LED引脚为通用口 p0~1 4
P0DIR |= 0x12; //输出模式
P0_1 = 0;//点亮led2
// return unprocessed events
return (events ^ LEDApp_SEND_MSG_EVT);
}
- 添加自己的事件
官方函数只给了两个事件<LEDApp_SEND_MSG_EVT、LEDApp_RTOS_MSG_EVT>(不同ZStack版本有所不同);在《LEDApp.h 74行》定义。
注:这里的0x0001为 0000 0000 0000 0001 ;在定义事件值时必须是16位中一位为1其他必须为0;<#define LEDApp_MY_EVT 0x0004>我自己定义的事件(0000 0000 0000 0100);这里是定义事件值,那我们就可以在《LEDApp.c 345行左右的if ( events & LEDApp_SEND_MSG_EVT )
》前后模仿写出基础格式
if ( events & LEDApp_MY_EVT )
{
//功能....
return (events ^ LEDApp_MY_EVT);
}
比如:我这一样还是点亮LED2,和第2点,使用方法一样osal_start_timerEx(LEDApp_TaskID,LEDApp_MY_EVT,000);
…
if ( events & LEDApp_MY_EVT )
{
P0SEL &=~0x12;//LED引脚为通用口 p0~1 4
P0DIR |= 0x12; //输出模式
P0_1 = 0;//点亮led2
return (events ^ LEDApp_MY_EVT);
}
- 消息
1)根据上一条可以得出,每个任务最多只能定义16个事件(0000 0000 0000 0000),而在系统运行的时候是远远有空的超过16条事件的,这样就完全不够用了,所以便引出了消息。
消息的处理原理:
在《comdef.h 122行》可以看到系统定义了一个系统事件#define SYS_EVENT_MSG 0x8000 // A message is waiting event
。
比如当应用层任务来处理某个事务的时候,首先是给应用层发送一个消息,调用osal_set_event(LEDApp_TaskID,SYS_EVENT_MSG);
,这样一来,应用层就会进入SYS_EVENT_MSG
事件里进行处理,在这个事件里又将判断到底刚才引发产生SYS_EVENT_MSG
事件是哪一种类型的消息,然后根据消息的类型做出相应的处理。
然而消息的类型可以根据自己的需求随意定义数量,这样消息的类型就很多了,那么应用层处理的事件种类就很多了。
我们可以看到在《ZComDef.h 376行》开始定义了很对消息类型;同时在《LEDApp.cuint16 LEDApp_ProcessEvent( uint8 task_id, uint16 events )
》中就有处理消息的函数<case ZDO_CB_MSG:
、case KEY_CHANGE:
、case AF_DATA_CONFIRM_CMD:
>等等,
2)现在我们自己设计个消息:
我们在协调器里面定义个按钮KEY_CHANGE
改变的消息:
if(LEDApp_NwkState == DEV_ZB_COORD)//协调器
{
P0SEL &=~0x12;//LED引脚为通用口 p0~1 4
P0DIR |= 0x12; //输出模式
P0_4 = 0;//LED1亮
keyChange_t *magPtr;
//定义个按钮改变的消息
magPtr = (keyChange_t *)osal_msg_allocate(sizeof(keyChange_t));
if(magPtr)
{
//给这个消息填写相关的值,类型是按钮状态改变KEY_CHANGE
magPtr->hdr.event = KEY_CHANGE;
magPtr->keys = 3;//消息里面的内容是3
//把发送给应用层LEDApp_TaskID的消息投到消息的队列,并且 osal_set_event( destination_task, SYS_EVENT_MSG );
osal_msg_send(LEDApp_TaskID,(uint8 *)magPtr);
}
// osal_set_event(LEDApp_TaskID,LEDApp_SEND_MSG_EVT);
// //处理LEDApp_SEND_MSG_EVT事件函数(343)行
osal_start_timerEx(LEDApp_TaskID,LEDApp_MY_EVT,2000);
//等待2000us处理LEDApp_TaskID的 LEDApp_SEND_MSG_EVT事件函数(343)行
}
注:其中在osal_msg_send(LEDApp_TaskID,(uint8 *)magPtr);
《OSAL.c 493行》函数里,有一个osal_set_event( destination_task, SYS_EVENT_MSG );
,也就是说运行到这,系统又会回到《LEDApp.c 239行》的uint16 LEDApp_ProcessEvent( uint8 task_id, uint16 events )
,又因为是KEY_CHANGE
事件,程序就会运行到《LEDApp.c 262行左右》case KEY_CHANGE:
里面,LEDApp_HandleKeys( ((keyChange_t *)MSGpkt)->state, ((keyChange_t *)MSGpkt)->keys );
这个函数就将处理对应的keys
,我这修改为:
static void LEDApp_HandleKeys( uint8 shift, uint8 keys ) { osal_start_timerEx(LEDApp_TaskID,LEDApp_MY_EVT,2000); //将运行等2000us到LEDApp_MY_EVT事件去。 }
通过前我自己写的LEDApp_MY_EVT
事件处理函数,最后效果为LED1灭、LED2亮;
2. 生成.hex文件
OSAL原理
1、应用层是一个任务它有一个系统分配给他的唯一的编号叫任务ID
2、任务可以处理实践,处理事件的这些代码都在一个函数里,这个函数叫做任务事件处理函数。
3、应用层任务还有一个2字节的变量 任务事件变量
应用层任务事件变量和应用层定义的事件的关系:如果事件变量与上某个事件的宏值为1时,那么应用层任务将要处理这个事件。
而系统在运行的时候会不断的去读取应用层任务事件变量,当它发现这个变量为0时,就认为应用层任务当前没有事件需要处理;反之,任务应用层任务将要有事件需要被处理,它就会去调用应用层事件处理函数uint16 LEDApp_ProcessEvent( uint8 task_id, uint16 events )
,并且把任务变量的值传给events
,在这个事件处理函数的,这个变量events
会分别和这个应用层所定义的所有事件宏值进行与操作,发现结果为1,那么就去执行这个事件的处理函数代码。
几乎每个层都是一个任务 那么每一层都有一个任务ID 任务事件处理函数 任务事件变量
. 所有的任务事件处理函数—》放在数组里面 :tasksArr[]《OSAL_LEDApp.c 72行》
. 所有的任务事件变量—》放在变量数组里面 :uint16 *tasksEvents;《OSAL_LEDApp.c 72行》
. 任务ID系统分配规则:tasksArr[0] 就意味着0号事件处理函数ID 对应 变量数组[0]0号任务事件
const uint8 tasksCnt = sizeof( tasksArr ) / sizeof( tasksArr[0] );
//tasksCnt 为有多少任务
. osal_init_system();
《ZMain.c 116行》初始化操作系统 -> osalInitTasks();
《OSAL.c 999行》初始化任务函数-> 将分配了任务事件ID
. osal_start_system();
《ZMain.c 140行》启动操作系统 -> 《OASL.c 1020行》这里将是个死循环函数,也就意味着整个系统都将在循环执行此处函数。
–>
osal_run_system();
《OASL.c 1040行》
void osal_run_system( void )
{
uint8 idx = 0;
osalTimeUpdate();
Hal_ProcessPoll();
do {
if (tasksEvents[idx]) // Task is highest priority that is ready.
{
break;//当有任务事件时(非0),就会跳出,此次的idx值就是任务ID号,
}
} while (++idx < tasksCnt);//从tasksEvents[0]--tasksEvents[tasksCnt]
if (idx < tasksCnt)
{
uint16 events;
halIntState_t intState;
HAL_ENTER_CRITICAL_SECTION(intState);
events = tasksEvents[idx];
tasksEvents[idx] = 0; // Clear the Events for this task.
HAL_EXIT_CRITICAL_SECTION(intState);
activeTaskID = idx;
events = (tasksArr[idx])( idx, events );//找到任务事件不为0,将会转到对应的任务事件处理函数《LEDApp.c `uint16 LEDApp_ProcessEvent( uint8 task_id, uint16 events )`里》
activeTaskID = TASK_NO_TASK;
HAL_ENTER_CRITICAL_SECTION(intState);
tasksEvents[idx] |= events;// Add back unprocessed events to the current task.将未处理的事件添加回当前任务
HAL_EXIT_CRITICAL_SECTION(intState);
}
#if defined( POWER_SAVING )
else // Complete pass through all task events with no activity?
{
osal_pwrmgr_powerconserve(); // Put the processor/system into sleep
}
#endif
/* Yield in case cooperative scheduling is being used. */
#if defined (configUSE_PREEMPTION) && (configUSE_PREEMPTION == 0)
{
osal_task_yield();
}
#endif
}