目录
消息处理事务:osal_msg_send&osal_msg_receive
Zigbee协议栈的使用
事件产生函数osal_set_event
uint8 osal_set_event( uint8 task_id, uint16 event_flag );
para1是任务ID,para2是任务类型
在Zstack中的程序代码按照功能来划分,分成不同的层,比如硬件操作相关叫做硬件层,网络相关的代码叫做网络层,自己写应用程序的部分叫做应用层。
几乎每一个层都是一个任务,系统为每一给任务分配一个字节的唯一数值编号,每一个任务都能处理一些它们能够处理的事务。我们把这个数值编号叫做任务ID,而它们能够处理的事务叫做事件。
(1)首先我们进入应用层APP中的任务事件处理函数中的SYS_EVENT_MSG任务中
(2)将应用层初始化函数中的应用层任务ID StarryApp_TaskID拿到
(3)然后再随便选择一个其它的任务,这里我选择StarryApp_SEND_MSG_EVT,并将原先的任务函数内容注释
(4)将这个两个作为osal_set_event的参数,并在协调器中调用
(5)在StarryApp_SEND_MSG_EVT任务中设置一个点亮LED2的操作
(6)下载调试
执行到断点1
再Go,执行到断点2
完成后灯成功点亮
这里补充一点,外设的初始化过程一定要全,比如初始化LED,如果缺少配置为普通IO模式这一步骤,可能会导致LED无法使用。这是因为在启动osal之前main函数中还初始化调用了TI的一些硬件实验,其中很多引脚的功能已经被初始化为片上外设了,所以我们使用时要自己修改,不能漏步骤。如果添加自己的一些外设初始化要放在osal_start_system函数前面,WatchDogEnable之后,如数码管等等
事件定时器触发函数osal_start_timerEx
uint8 osal_start_timerEx( uint8 taskID, uint16 event_id, uint16 timeout_value )
第一个参数是任务编号,第二个参数是要处理的事务,第三个参数是时间(毫秒),指多长时间之后来处理这个事务
这个函数的作用是设置一个软件定时器定时一定时间,时间到了之后自动调用osal_set_event函数来处理相应事件(注意定时只能用一次)
具体使用过程不做介绍
如何自定义事件
跳转到头文件可以,找到事件定义处
定义一个自己的事件,事件宏定义的特点是二进制16位里面只能有1位为1。注意只能按位来定义,只能一个位为1,其余位为0,最多定义16个事件。也就是一个任务最多只能够处理16个不同的事件。
下面使用自定义事件+osal_start_timerEx来实现2s后点亮LED3
消息处理事务:osal_msg_send&osal_msg_receive
在Zstack里,任务事件定义的特点决定了,每一个任务最多只能处理16种不同的事件,而系统在运行的时候有许多事物需要处理,如果每一个事物处理都定义成一个事件,那么16种事件是肯定不够用的,所以引入了消息。
消息处理事务的原理:
定义了一个事件#define SYS_EVENT_MSG 0x8000 // A message is waiting event
当需要应用层任务来处理某个事务的时候,首先通过osal_msg_send函数给应用层任务发送一个消息,调用osal_set_event(StarryApp_TaskID,SYS_EVENT_MSG);
那么一来,应用层就会进入SYS_EVENT_MSG中处理,在这个事件处理里判断到底刚刚引发我们产生SYS_EVENT_MSG事件到底是哪一种类型的消息,然后根据消息的类型做相应的处理。
而消息的类型可以自己定义,这样一来消息的类型可以很多,那么应用层任务处理的事务种类就很多了。
我们以一个消息收发的过程为例。
keyChange_t *msgPtr;
msgPtr = (KeyChange_t *)osal_msg_allocate( sizeof(keyChange_t) );//定义一个按钮状态改变的消息
if( msgPtr )
{
msgPtr->hdr.event = KEY_CHANGE;//给这个消息填写类型
msgPtr->keys=3;//赋值
osal_msg_send( StarryApp_TaskID, (uint8 *)msgPtr );//把发送给应用层StarryApp_TaskID的消息发送到消息队列
}
首先定义一个按钮状态改变的消息,然后给这个消息填写相关的值,类型是按钮状态改变KEY_CHANGE,然后把发送给应用层StarryApp_TaskID的消息发送到消息队列,并且在osal_msg_send( StarryApp_TaskID, (uint8 *)msgPtr );函数中调用了osal_set_event(StarryApp_TaskID,SYS_EVENT_MSG);
所以一旦消息发送出,就会在系统事件中通过(afIncomingMSGPacket_t *)osal_msg_receive( StarryApp_TaskID );函数取出引发系统事件的消息
然后判断得出是按键改变的消息,然后在按键改变的情况中调用StarryApp_HandleKeys函数
这是协议栈自带的处理函数,我们可以根据需要更改
协议栈按键实验
首先添加我们自己的板级支持包
#include "iocc2530.h"
#define uint unsigned int
#define uchar unsigned char
//定义控制LED灯的端口
#define LED1 P1_0 //定义LED1
#define KEY1 P0_0 //中断口
#define LED2 P1_1
#define KEY2 P0_1
//函数声明
void Delayms(uint); //延时函数
void InitLed(void); //初始化P1 P2口
void KeyInit(); // 按键初始化
uchar KeyValue=0;
#include "Starry_key.h"
//延时函数
void Delayms(uint xms) // i=xms 即延时i毫秒
{
uint i,j;
for(i=xms;i>0;i--)
for(j=587;j>0;j--);
}
//LED初始化程序
void InitLed(void)
{
P1SEL &= ~0x01;
P1DIR |= 0x01; // P1_0定义为输出
P1INP |= 0X01; //打开下拉
LED1 = 0; // LED1灯熄灭
P1SEL &= ~0x02;
P1DIR |= 0x02;
P1INP |= 0X02;
LED2 = 0;
}
//KEY初始化程序--外部中断方式
void InitKey()
{
/*配置输入IO口*/
P0SEL &= 0xFE;//1111 1110 P0_0 普通IO模式
P0DIR &= 0xFE;//输入模式
P0INP &= 0xFE;//上下拉模式
P0SEL &= 0xFD;
P0DIR &= 0xFD;//输入模式
P0INP &= 0xFD;//上下拉模式
P2INP &= 0xDF;//上拉
/*配置外部中断*/
P0IE=1;//开启组开关
P0IEN |= 0x01;//开启P0_0中断使能
P0IEN |= (0x01<<1);//开启P0_1中断使能
EA=1;//开启总中断
PICTL |= 0x01;//把P0组配置为下降沿触发
}
//中断处理函数
#pragma vector = P0INT_VECTOR //格式:#pragma vector = 中断向量,紧接着是中断处理程序
__interrupt void P0_ISR(void)
{
if(KEY2==0)//P0_1引发了外部中断
{
Delayms(10); // 去除抖动
LED2=~LED2; // 改变LED2状态
}
if(KEY1==0)//P0_0引发了外部中断
{
Delayms(10); // 去除抖动
LED1=~LED1; // 改变LED1状态
}
P0IFG = 0; //清中断标志
P0IF = 0; //清中断标志
}
然后把协议栈自带的hal_key.c中的外部中断有关部分代码全部注释掉,否则会出现重定义
在zmain.c中调用测试
下面正式进行协议栈按键实验的编写
(1)首先自定义按键事件#define StarryApp_My_KEY_EVT 0x0008
(2)然后在StarryApp_ProcessEvent中编写处理操作
(3)对外部中断内容进行修改,换成osal_start_timerEx(StarryApp_TaskID,StarryApp_MY_KEY_EVT,100);还要注意变量和函数的外部调用
修改后的按键C文件内容
#include "Starry_key.h"
#include "StarryApp.h"
extern byte StarryApp_TaskID;
extern uint8 osal_start_timerEx( uint8 taskID, uint16 event_id, uint16 timeout_value );
//延时函数
void Delayms(uint xms) // i=xms 即延时i毫秒
{
uint i,j;
for(i=xms;i>0;i--)
for(j=587;j>0;j--);
}
//LED初始化程序
void InitLed(void)
{
P1SEL &= ~0x01;
P1DIR |= 0x01; // P1_0定义为输出
P1INP |= 0X01; //打开下拉
LED1 = 0; // LED1灯熄灭
P1SEL &= ~0x02;
P1DIR |= 0x02;
P1INP |= 0X02;
LED2 = 0;
}
//KEY初始化程序--外部中断方式
void InitKey()
{
/*配置输入IO口*/
P0SEL &= 0xFE;//1111 1110 P0_0 普通IO模式
P0DIR &= 0xFE;//输入模式
P0INP &= 0xFE;//上下拉模式
P0SEL &= 0xFD;
P0DIR &= 0xFD;//输入模式
P0INP &= 0xFD;//上下拉模式
P2INP &= 0xDF;//上拉
/*配置外部中断*/
P0IE=1;//开启组开关
P0IEN |= 0x01;//开启P0_0中断使能
P0IEN |= (0x01<<1);//开启P0_1中断使能
EA=1;//开启总中断
PICTL |= 0x01;//把P0组配置为下降沿触发
}
//中断处理函数
#pragma vector = P0INT_VECTOR //格式:#pragma vector = 中断向量,紧接着是中断处理程序
__interrupt void P0_ISR(void)
{
osal_start_timerEx(StarryApp_TaskID,StarryApp_MY_KEY_EVT,100);
P0IFG = 0; //清中断标志
P0IF = 0; //清中断标志
}
对于事件处理,我们还可以进一步添加按钮状态改变消息,从而使用消息发送osal_msg_send函数来通过消息队列的方法进一步优化。
按键消息创建:
按键消息处理:
使用协议栈的osal_start_timerEx(StarryApp_TaskID,StarryApp_MY_KEY_EVT,100);函数来代替中断里的延时的意义是避免了延时函数在中断里浪费过多时间,从而最终影响通信。使用后,即使按键抖动带来误触,也只是将里的触发时间重新置为100ms,并不会导致osal_start_timerEx中的osal_set_event函数被重复执行。不仅避免了按键抖动的问题还大大提高了代码的执行效率。