Zigbee使用MT层实现串口写和读操作,简要了解osal_msg_send消息机制

标签: Zigbee  串口  osal_msg_send  MT

MT层的串口API文件是MT_UART.c和MT_UART.h。如下图:

首先在应用层初始化函数下添加以下代码:

MT_UartInit();//串口初始化
MT_UartRegisterTaskID(task_id);//注册该事件,注意,该函数必须在MT_UartInit()调用之后才调用
HalUARTWrite(0 , "hello FANG\n", sizeof("hello FANG\n"));//打印串口。

但是现在还没配置好,我们还需要修改一些初始化选项。右键MT_UartInit(),选择go to definition of ‘MT_UartInit’,如下图:

这样就跟踪到了这个函数下来,该函数前面主要部分如下图:

有点串口基础的同学大概了解该结构体的配置参数,首先配置方式为TRUE,第二个参数即bauRate是波特率,

MT_UART_DEFAULT_BAUDRATE,这个参数意思是默认波特率,go to definiton跟踪它,可以看它对应的波特率参数,跟踪结果如下

 上图本人设置的是115200的速度,我们再跟踪HAL_UART_BR_115200,可以看到以下结果:

因此可以根据以上选择来更改 MT_UART_DEFAULT_BAUDRATE的指定参数。现在我们再回到前面的MT_UartInit函数下来,到第三个属性是flowControl,是流控的意思,问是否使用流控,该协议栈默认是打开流控的,我们要修改为false,跟踪对应的参数

MT_UART_DEFAULT_OVERFLOW,更改结果如下:

剩下的参数不做修改,不过我们也可以分析一下它们大概的意思,有读最大缓冲区大小,有写最大缓冲区大小,这些我们只需默认。接下来还需要修改一个地方,如果我们的串口需要读操作,那么我们就需要看MT_UARTInit函数下这个部分代码块:

 这里我们就可以定义ZTOOL_P1,我们可以通过非程序形式定义这个宏,如图:

这里我们还需要把MT_TASK这个宏给关闭掉,不然测试串口数据可能会乱码,只要在MT_TASK前面加个x就行,或者直接删除掉MT_TASK.

上述代码块语义是如果定义ZTOOL_P1ZTOOL_P2,则这个串口结构体有个属性callBackFunc就等于MT_UartProcessZToolDatacallBackFunc的意思是回调函数的意思,也就是说MT_UartProcessZToolData是一个函数,在这个MT_UART.c中可以找到该函数,当然也可以直接跟踪MT_UartProcessZToolData,结果如下:

 这个函数处理的就是一个串口不断读取的函数,在注册了这个MT_UART后,这个函数会被不断的被调用,相当于事件一样,当串口有数据时,就处理这个数据并发送到上层即我们的应用层,但是,经过本人测试,该函数处理的结果无法成功地把数据发送出去,故我们需要重写这块代码,我们把这块代码修改成以下结果:

void MT_UartProcessZToolData ( uint8 port, uint8 event )
{
  uint8 flag=0,i,j=0; //flag 是判断有没有收到数据,j 记录数据长度
  uint8 buf[128]; //串口buffer 最大缓冲默认是128,我们这里用128.
  (void)event; // Intentionally unreferenced parameter
  while (Hal_UART_RxBufLen(port)) //检测串口数据是否接收完成
  {
    HalUARTRead (port,&buf[j], 1); //把数据接收放到buf 中
    j++; //记录字符数
    flag=1; //已经从串口接收到信息
  }
  if(flag==1) //已经从串口接收到信息
  { 
    /* Allocate memory for the data */
    //分配内存空间,为机构体内容+数据内容+1 个记录长度的数据
    pMsg = (mtOSALSerialData_t *)osal_msg_allocate( sizeof( mtOSALSerialData_t )+j+1);
    //事件号用原来的CMD_SERIAL_MSG
    pMsg->hdr.event = CMD_SERIAL_MSG;
    pMsg->msg = (uint8*)(pMsg+1); // 把数据定位到结构体数据部分
    pMsg->msg [0]= j; //给上层的数据第一个是长度
    for(i=0;i<j;i++) //从第二个开始记录数据
      pMsg->msg[i+1]= buf[i];
    
    osal_msg_send( App_TaskID, (byte *)pMsg ); //登记任务,发往上层
    /* deallocate the msg */
    osal_msg_deallocate ( (uint8 *)pMsg ); //释放内存
  }
}

这样就能成功把消息数据通过osal_msg_send(App_TaskID,(byte*)pMsg)发送给应用层,也就是我们刚刚注册该MT_UART的这层,换句话说就是刚刚调用了MT_UartRegisterTaskID(task_id)的这层。理论上pMsg这个变量可以是任何类型的变量,包括任何的结构体变量,但是,这样的变量上在协议栈中有一个规范,就是必须数据头要有一个属性是osal_event_hdr_t的类型,记住这个osal_event_hdr_t类型,它是消息传递的关键。以上代码首先我们先看pMsg是一个mtOSALSerialData_t类型。其原型如下:

typedef struct
{
  osal_event_hdr_t  hdr;
  uint8             *msg;
} mtOSALSerialData_t;

这个类型是MT_UART.h已经定义好的,并非自定义。当然也可以自己定义,当理解了大致的消息机制的框架我们自己定义类似的结构体。分析这个结构体,mtOSALSerialData_t里面有个osal_event_hdr_t hdr,这个上文提过是消息传递的关键,我们跟踪它,可以看到函数原型如下:

typedef struct
{
  uint8  event;
  uint8  status;
} osal_event_hdr_t;

根据前面代码块的描述pMsg->hdr.event = CMD_SERIAL_MSG,就是该事件的ID,status我们并没有给它赋值,因为此时并没用到,这里我如何分配内存的给pMsg的呢,该实现方式是:

pMsg = (mtOSALSerialData_t *)osal_msg_allocate( sizeof( mtOSALSerialData_t )+j+1);

假如我们跑动这个程序,然后再串口调试助手发送“123456”,说明这里就有6个字符了,通过第一个while循环后,得到 j 的值为6,那为什么+j之后还要+1呢,原因是我们再需要一个空间来描述字符长度,而这个空间只定义了1个字符的空间即最大长度是255,if语句处理数据的图解如下:

此时我们把这样的数据通过osal_msg_send(App_TaskID,(byte*)pMsg)发送给应用层。

接下来应用层如何接收呢,其实该事件发送应用层会触发应用层的SYS_EVENT_MSG系统事件。在我们的应用层里接收到该任何事件都会先把数据包先强制转换成afIncomingMSGPacket_t的类型,然后判断该判断这个数据头的event事件是什么,因此前面提到,其实发送任何数据都没问题,但是前提是数据头必须要有个osal_event_hdr_t类型的变量,我们可以跟踪这个afIncomingMSGPacket_t:

typedef struct
{
  osal_event_hdr_t hdr;     /* OSAL Message header */
  uint16 groupId;           /* Message's group ID - 0 if not set */
  uint16 clusterId;         /* Message's cluster ID */
  afAddrType_t srcAddr;     /* Source Address, if endpoint is STUBAPS_INTER_PAN_EP,
                               it's an InterPAN message */
  uint16 macDestAddr;       /* MAC header destination short address */
  uint8 endPoint;           /* destination endpoint */
  uint8 wasBroadcast;       /* TRUE if network destination was a broadcast address */
  uint8 LinkQuality;        /* The link quality of the received data frame */
  uint8 correlation;        /* The raw correlation value of the received data frame */
  int8  rssi;               /* The received RF power in units dBm */
  uint8 SecurityUse;        /* deprecated */
  uint32 timestamp;         /* receipt timestamp from MAC */
  uint8 nwkSeqNum;          /* network header frame sequence number */
  afMSGCommandFormat_t cmd; /* Application Data */
} afIncomingMSGPacket_t;

虽然后面的数据会跟我们发送的mtOSALSerialData_t类型不一样,只要我们不去随便触碰不应该存在的数据就行了。因为我们知道后面数据是不存在groupid,clusterld,srcAddr等等,我们只知道这个数据包的存储空间结构是我们发送的msg和6和“123456”.因此,我们在测试这个数据时要再次转换回来。首先在uint16 Fang_ProcessEvent( uint8 task_id, uint16 events )函数下找到:

if ( events & SYS_EVENT_MSG )
  {
    MSGpkt = (afIncomingMSGPacket_t *)osal_msg_receive( Fang_TaskID );
    while ( MSGpkt )
    {
      switch ( MSGpkt->hdr.event )
      {

          case AF_INCOMING_MSG_CMD:
               Fang_MessageMSGCB( MSGpkt );
          break;

         // Received whenever the device changes state in the network
         case ZDO_STATE_CHANGE:

           。。。。。

 

然后再switch里面添加case CMD_SERIAL_MSG:,测试代码块我们可以以下实现:

case CMD_SERIAL_MSG:
        UartMsg = (mtOSALSerialData_t *)MSGpkt;
        HalUARTWrite(0,&UartMsg->msg[1],UartMsg->msg[0]);
        break;

UartMsg在函数的前面定义如下:

mtOSALSerialData_t * UartMsg;

 编译时,可能会说什么类型未定义,一般是头文件没加进来,自行检查一般头文件需要加上MT_UART.h和MT.h,编译成功后,烧录程序打开串口调试助手,试着发送数据:

实验成功。 如果数据是乱码,可能是宏的定义列下有个MT_TASK的影响,把这个宏删除掉即可。也可以在这个宏前面加点其他元素比如:

当然有些人直接封装成函数形式实现如下:

case CMD_SERIAL_MSG:
          SerialCMD((mtOSALSerialData_t *)MSGpkt);
          break;

SerialCMD自己定义该函数 void SerialCMD(mtOSALSerialData_t * UartMsg);

 

版权声明:本文为fangjiaze444原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。
本文链接:https://blog.csdn.net/fangjiaze444/article/details/81950939

智能推荐

剑指offer 合并两个排序的链表

题目 链接:https://leetcode-cn.com/problems/he-bing-liang-ge-pai-xu-de-lian-biao-lcof/ 思路 我想的是,与合并两个有序数组一样的思维,新建一个链表,然后判断谁的值大,进而在新的链表上面进行插入。 看书思路 合并链表是一个递归问题:合并一个节点后可以转化为一个子问题。终止条件是其中一个链表为空 代码 链表反转也可以用递归解决...

Java编程思想 第三章:操作符

Java中的操作符和c/c++中的操作符基本一致,因为我之前学习过C语言和C++,所以本章的内容大部分都已熟知,下面只做简单的介绍。 Java操作符及优先级 Java中的操作符包括算术操作符,关系操作符,逻辑操作符,位运算符、自操作运算符、移位运算符、赋值运算符和其他运算符。 算术操作符:包括加减乘除和取余(%),优先级乘除取余高于加减,都是双元运算符,其中加法(+)可以用来连接两个字符串,比如:...

JetBrains 系列开发工具,如何配置 `SCSS` `File Watcher` ,相关输出配置参数详解:webStorm phpStorm IDEA

JetBrains 系列开发工具,如何配置 SCSS File Watcher ,相关输出配置参数详解:webStorm phpStorm IDEA 前言 你目前已经了解了如何使用 SCSS 进行开发,了解了该文章的内容:『 SCSS 日常用法 』 在 JetBrains 系列开发工具中通过 FileWatcher 进行编译的 SCSS 文件都是通过 sass 这个程序进行的。『 如何添加 Fil...

C语言小函数—二进制与十六进制

测试如下 “` int main() { long int num = 15; } “`...

仿微博或微信的文章多图显示(自定义MultiImageView)

按照一般的规矩,先上张图来供大伙看看 如果大致是大伙们需要实现的功能,不烦一观 自定义MultiImageView 工具类 具体使用 app.gradle中添加依赖 implementation 'com.github.bumptech.glide:glide:4.8.0' AndroidManifest.xml中配置联网权限 <uses-permission android:name=&q...

猜你喜欢

经典进程同步和互斥问题

经典进程同步与互斥问题 前言 一、生产者-消费者问题 1.问题描述 2.问题分析 3.代码 二、读者-写者问题 1.问题描述&&分析 2.代码 三、哲学家进餐问题 1.问题描述&&分析 2.代码 四、理发师问题 1.问题描述&&分析 2.代码 前言 在多道程序设计环境中,进程同步是一个非常重要的问题,下面讨论几个经典的进程同步问题。 一、生产者-消费...

java设计模式——ThreadLocal线程单例

1、定义一个ThreadLocal线程单例,代码如下: 2、定义一个多线程类,代码如下: 3、定义一个测试类,代码如下: 4、输出结果,如下图:...

【tensorflow】线性模型实战

线性模型:y = 1.477 * x + 0.089   1. 采样数据 采样噪声eps在均值0,方差0.01的高斯分布中,而后在均匀分布U(0,1)中,区间[-10,10]进行n=100次随机采样:   2. 计算误差 循环计算每个点的预测值与真是值之间差的平方并累加,从而获得训练集上的均芳误差损失值。   3. 计算梯度   4. 梯度更新 对权重w和偏...

常见损失函数和评价指标总结(附公式&代码)

网上看到一篇很实用的帖子关于常见损失函数和评价指标,收藏下来 本文转载于https://zhuanlan.zhihu.com/p/91511706 ------------------------------------------------------------------------------------------------------------------------------...

为什么 4G/5G 的直播延时依然很高

通信技术的发展促进了视频点播和直播业务的兴起,4G 和 5G 网络技术的进步也使得流媒体技术变得越来越重要,但是网络技术并不能解决流媒体直播的高延迟问题。 本文不会介绍网络对直播业务的影响,而是会分析直播中常见的现象 — 主播和观众之间能够感觉到的明显网络延迟。除了业务上要求的延迟直播之外,有哪些因素会导致视频直播的延迟这么高呢? live-streaming  图 1 - ...