ZigBee协议栈OSAL解析(第二天)

来源:互联网 发布:电脑管理软件 知乎 编辑:程序博客网 时间:2024/04/30 06:23

照着例程协议栈很快就能调通串口,但是我还是要细究些细节!

    解析:

/* Set to TRUE enable UART usage, FALSE disable it */#ifndef HAL_UART  #if (defined ZAPP_P1) || (defined ZAPP_P2) || (defined ZTOOL_P1) || (defined ZTOOL_P2)      #define HAL_UART TRUE  #else      #define HAL_UART FALSE  #endif#endif#if HAL_UART  #ifndef HAL_UART_DMA     #if HAL_DMA         #if (defined ZAPP_P2) || (defined ZTOOL_P2)           #define HAL_UART_DMA  2         #else           #define HAL_UART_DMA  1         #endif     #else         #define HAL_UART_DMA  0     #endif  #endif  #ifndef HAL_UART_ISR      #if HAL_UART_DMA           // Default preference for DMA over ISR.          #define HAL_UART_ISR  0      #elif (defined ZAPP_P2) || (defined ZTOOL_P2)          #define HAL_UART_ISR  2      #else          #define HAL_UART_ISR  1      #endif  #endif  #if (HAL_UART_DMA && (HAL_UART_DMA == HAL_UART_ISR))      #error HAL_UART_DMA & HAL_UART_ISR must be different.  #endif// Used to set P2 priority - USART0 over USART1 if both are defined.  #if ((HAL_UART_DMA == 1) || (HAL_UART_ISR == 1))      #define HAL_UART_PRIPO             0x00  #else      #define HAL_UART_PRIPO             0x40  #endif#else  #define HAL_UART_DMA  0  #define HAL_UART_ISR  0#endif/* USB is not used for CC2530 configuration */#define HAL_UART_USB  0#endif/********************************************************************************************************/
    原来代码的缩进真是惨不忍睹,重新修改缩进之后,总结下
    ZAPP_P1、ZAPP_P2、ZTOOL_P1、ZTOOL_P2定义的话,可以将HAL_UART定义为TURE

    当HAL_UART有定义时,且HAL_DMA定义时,优先定义HAL_UART_DMA的传输,否则不启用DMA

    当HAL_UART_DMA有定义时HAL_UART_ISR失效,否则根据前面说的四个宏定义种类

    实际上P2对应2,而P1对应1,且DMA与ISR是互斥关系。至于优先级是现在暂时先不考虑。

    这样我只要定义ZTOOL_P1编译,那么默认的DMA来传输UART的方式。

    之后就是static void HalUARTPollDMA(void),其实默认的都会都是使用HALUARTDMA那么稍微看逻辑关系。

     那么我们可以说UART凌驾于任务机制之外,每次轮询时都会去执行,这样的话,没必要南辕北辙,去使用任务机制发送

串口数据。

     首先必须看初始化,非常多。去掉代码,只看注释,此时需要看手册确定功能

    

static void HalUARTInitDMA(void){  halDMADesc_t *ch;  P2DIR &= ~P2DIR_PRIPO;//清最高位  P2DIR |= HAL_UART_PRIPO;//P0 功能优先级 uart0 1 timer1#if (HAL_UART_DMA == 1)  PERCFG &= ~HAL_UART_PERCFG_BIT;    // Set UART0 I/O to Alt. 1 location on P0.此时指向该语句#else  PERCFG |= HAL_UART_PERCFG_BIT;     // Set UART1 I/O to Alt. 2 location on P1.#endif  PxSEL  |= HAL_UART_Px_RX_TX;       // Enable Tx and Rx on P1.此时是P0,这里注释不太严谨  ADCCFG &= ~HAL_UART_Px_RX_TX;      // Make sure ADC doesnt use this.  UxCSR = CSR_MODE;                  // Mode is UART Mode.  UxUCR = UCR_FLUSH;                 // Flush it.  //DMA的部分,先略过  ................ }

      总结下就是设置了管脚,并且选择第二功能,配置部分UART的寄存器。此时并没有初始化波特率,所以该初始化完成的

功能非常有限。

     然后看芯片手册:(常用的无需关注,只看DMA部分)

There are two DMA triggers associated with each USART. The DMA triggers are activated by RXcomplete and TX complete events, i.e., the same events as the USART interrupt requests. A DMAchannel can be configured using a USART receive/transmit buffer, UxDBUF, as source or destinationaddress
The main features of the DMA controller are as follows:• Five independent DMA channels• Three configurable levels of DMA channel priority• 32 configurable transfer trigger events• Independent control of source and destination address• Single, block and repeated transfer modes• Supports length field in transfer data, setting variable transfer length• Can operate in either word-size or byte-size mode
The behavior of each of the five DMA channels is configured with the following parameters:Source address: The first address from which the DMA channel should read data.Destination address: The first address to which the DMA channel should write the data read from thesource address. The user must ensure that the destination is writable.Transfer count: The number of transfers to perform before rearming or disarming the DMA channeland alerting the CPU with an interrupt request. The length can be defined in the configuration or it canbe defined as described next for the VLEN setting.VLEN setting: The DMA channel is capable of variable-length transfers, using the first byte or word toset the transfer length. When doing this, various options are available regarding how to count thenumber of bytes to transfer.Priority: The priority of the DMA transfers for the DMA channel with respect to the CPU and otherDMA channels and access ports.Trigger event: All DMA transfers are initiated by so-called DMA trigger events. This trigger eitherstarts a DMA block transfer or a single DMA transfer. In addition to the configured trigger, a DMAchannel can always be triggered by setting its designatedDMAREQ.DMAREQx flag. The DMA triggersources are described in Table 8-1.Source and Destination Increment: The source and destination addresses can be controlled toincrement or decrement or not change.Transfer mode: The transfer mode determines whether the transfer should be a single transfer or ablock transfer, or repeated versions of these.Byte or word transfers: Determines whether each DMA transfer should be 8-bit (byte) or 16-bit(word).Interrupt Mask: An interrupt request is generated on completion of the DMA transfer. The interruptmask bit controls whether the interrupt generation is enabled or disabled.M8: Decide whether to use seven or eight bits per byte byte for transfer length. This is only applicablewhen doing byte transfers.
      有流程图,但是太大了,不好贴上来。之后看后半部代码:

  // Setup Tx by DMA.4通道作为发送  ch = HAL_DMA_GET_DESC1234( HAL_DMA_CH_TX );   // The start address of the destination. 目的地址:0x70c1 为uart0的收发缓冲寄存器地址  HAL_DMA_SET_DEST( ch, DMA_UDBUF );  // Using the length field to determine how many bytes to transfer.  HAL_DMA_SET_VLEN( ch, HAL_DMA_VLEN_USE_LEN );  // One byte is transferred each time.  HAL_DMA_SET_WORD_SIZE( ch, HAL_DMA_WORDSIZE_BYTE );  // The bytes are transferred 1-by-1 on Tx Complete trigger.  HAL_DMA_SET_TRIG_MODE( ch, HAL_DMA_TMODE_SINGLE );  HAL_DMA_SET_TRIG_SRC( ch, DMATRIG_TX );  // The source address is incremented by 1 byte after each transfer.  HAL_DMA_SET_SRC_INC( ch, HAL_DMA_SRCINC_1 );  // The destination address is constant - the Tx Data Buffer.  HAL_DMA_SET_DST_INC( ch, HAL_DMA_DSTINC_0 );  // The DMA Tx done is serviced by ISR in order to maintain full thruput.  HAL_DMA_SET_IRQ( ch, HAL_DMA_IRQMASK_ENABLE );  // Xfer all 8 bits of a byte xfer.  HAL_DMA_SET_M8( ch, HAL_DMA_M8_USE_8_BITS );  // DMA has highest priority for memory access.  HAL_DMA_SET_PRIORITY( ch, HAL_DMA_PRI_HIGH );  // Setup Rx by DMA.  ch = HAL_DMA_GET_DESC1234( HAL_DMA_CH_RX );  // The start address of the source.  HAL_DMA_SET_SOURCE( ch, DMA_UDBUF );  // Using the length field to determine how many bytes to transfer.  HAL_DMA_SET_VLEN( ch, HAL_DMA_VLEN_USE_LEN );  /* The trick is to cfg DMA to xfer 2 bytes for every 1 byte of Rx.   * The byte after the Rx Data Buffer is the Baud Cfg Register,   * which always has a known value. So init Rx buffer to inverse of that   * known value. DMA word xfer will flip the bytes, so every valid Rx byte   * in the Rx buffer will be preceded by a DMA_PAD char equal to the   * Baud Cfg Register value.   */  HAL_DMA_SET_WORD_SIZE( ch, HAL_DMA_WORDSIZE_WORD );  // The bytes are transferred 1-by-1 on Rx Complete trigger.  HAL_DMA_SET_TRIG_MODE( ch, HAL_DMA_TMODE_SINGLE_REPEATED );  HAL_DMA_SET_TRIG_SRC( ch, DMATRIG_RX );  // The source address is constant - the Rx Data Buffer.  HAL_DMA_SET_SRC_INC( ch, HAL_DMA_SRCINC_0 );  // The destination address is incremented by 1 word after each transfer.  HAL_DMA_SET_DST_INC( ch, HAL_DMA_DSTINC_1 );  HAL_DMA_SET_DEST( ch, dmaCfg.rxBuf );  HAL_DMA_SET_LEN( ch, HAL_UART_DMA_RX_MAX );  // The DMA is to be polled and shall not issue an IRQ upon completion.  HAL_DMA_SET_IRQ( ch, HAL_DMA_IRQMASK_DISABLE );  // Xfer all 8 bits of a byte xfer.  HAL_DMA_SET_M8( ch, HAL_DMA_M8_USE_8_BITS );  // DMA has highest priority for memory access.  HAL_DMA_SET_PRIORITY( ch, HAL_DMA_PRI_HIGH );}
      这里注意到,发送接收有所区别,接收的时候是按word接收,且注意有效数据前是U0BAUD的值,但是这一切

都会被TI开发人员处理好了,这里只是大致看下,4通道用于TX,3通道用于RX,且都是single模式,触发都是TX或

RX完成,且都是TX的DES为UART缓冲寄存器,源地址没有设置。RX的目的地址为dmaCfg.rxBuf,源地址是uart缓

冲寄存器。

       

    真正做串口初始化的函数为

uint8 HalUARTOpen(uint8 port, halUARTCfg_t *config){  (void)port;  (void)config;#if (HAL_UART_DMA == 1)  if (port == HAL_UART_PORT_0)  HalUARTOpenDMA(config);#endif#if (HAL_UART_DMA == 2)  if (port == HAL_UART_PORT_1)  HalUARTOpenDMA(config);#endif#if (HAL_UART_ISR == 1)  if (port == HAL_UART_PORT_0)  HalUARTOpenISR(config);#endif#if (HAL_UART_ISR == 2)  if (port == HAL_UART_PORT_1)  HalUARTOpenISR(config);#endif#if (HAL_UART_USB)  HalUARTOpenUSB(config);#endif    return HAL_UART_SUCCESS;}
     然后就不跟踪了,直接看
typedef struct{  bool                configured;  uint8               baudRate;  bool                flowControl;  uint16              flowControlThreshold;  uint8               idleTimeout;  halUARTBufControl_t rx;  halUARTBufControl_t tx;  bool                intEnable;  uint32              rxChRvdTime;  halUARTCBack_t      callBackFunc;}halUARTCfg_t;
   这个时候再看
void MT_UartInit (){  halUARTCfg_t uartConfig;  /* Initialize APP ID */  App_TaskID = 0;  /* UART Configuration */  uartConfig.configured           = TRUE;  uartConfig.baudRate             = MT_UART_DEFAULT_BAUDRATE;  uartConfig.flowControl          = MT_UART_DEFAULT_OVERFLOW;  uartConfig.flowControlThreshold = MT_UART_DEFAULT_THRESHOLD;  uartConfig.rx.maxBufSize        = MT_UART_DEFAULT_MAX_RX_BUFF;  uartConfig.tx.maxBufSize        = MT_UART_DEFAULT_MAX_TX_BUFF;  uartConfig.idleTimeout          = MT_UART_DEFAULT_IDLE_TIMEOUT;  uartConfig.intEnable            = TRUE;#if defined (ZTOOL_P1) || defined (ZTOOL_P2)  uartConfig.callBackFunc         = MT_UartProcessZToolData;#elif defined (ZAPP_P1) || defined (ZAPP_P2)  uartConfig.callBackFunc         = MT_UartProcessZAppData;#else  uartConfig.callBackFunc         = NULL;#endif  /* Start UART */#if defined (MT_UART_DEFAULT_PORT)  HalUARTOpen (MT_UART_DEFAULT_PORT, &uartConfig);#else  /* Silence IAR compiler warning */  (void)uartConfig;#endif  /* Initialize for ZApp */#if defined (ZAPP_P1) || defined (ZAPP_P2)  /* Default max bytes that ZAPP can take */  MT_UartMaxZAppBufLen  = 1;  MT_UartZAppRxStatus   = MT_UART_ZAPP_RX_READY;#endif}
     MT层是一种什么感觉呢?或者他是开发人员interfere层,最终还是封装hal层的函数,我第一天所要做的实验就不用再

做了,没必要。该函数放置于任意初始化位置,然后如果发数据直接调用HalUARTWrite,这里只是拿出来MT的函数直接

使用,具体MT的本来用法,还要看手册。

     至此第一天的收尾工作结束。



     无线传输部分:

     

     1.MT层和协议栈的交互

      

     网蜂的教程有很多不满意的地方,MT的使用感觉有点偷梁换柱,也许偏离了本来的目的,但是作为我入门zigbee的资料

也算是可以了。网蜂已经提到的不做重复工作。

    接下来的函数,使用一种在Verilog里面叫伪顺序操作的编程方式,看到时不免觉得很亲切,我也玩过一段时间的FPGA

void MT_UartProcessZToolData ( uint8 port, uint8 event ){  uint8  ch;  uint8  bytesInRxBuffer;    (void)event;  // Intentionally unreferenced parameter  while (Hal_UART_RxBufLen(port))//这种方式处理数据就是我一直读buffer的字节数,只要不是0就循环  {    HalUARTRead (port, &ch, 1);//一次读一个字节    switch (state)    {      case SOP_STATE:        if (ch == MT_UART_SOF)          state = LEN_STATE;        break;      case LEN_STATE:        LEN_Token = ch;        tempDataLen = 0;        /* Allocate memory for the data */        pMsg = (mtOSALSerialData_t *)osal_msg_allocate( sizeof ( mtOSALSerialData_t ) +                                                        MT_RPC_FRAME_HDR_SZ + LEN_Token );        //此时数据组成为 mtOSALSerialData_t LEN COM1 COM2  DATA        if (pMsg)        {          /* Fill up what we can */          pMsg->hdr.event = CMD_SERIAL_MSG;          pMsg->msg = (uint8*)(pMsg+1);//pMsg->msg指向LEN!          pMsg->msg[MT_RPC_POS_LEN] = LEN_Token;          state = CMD_STATE1;        }        else        {          state = SOP_STATE;          return;        }        break;       .................      case DATA_STATE:        /* Fill in the buffer the first byte of the data */        pMsg->msg[MT_RPC_FRAME_HDR_SZ + tempDataLen++] = ch;        /* Check number of bytes left in the Rx buffer */        bytesInRxBuffer = Hal_UART_RxBufLen(port);        /* If the remain of the data is there, read them all, otherwise, just read enough */        if (bytesInRxBuffer <= LEN_Token - tempDataLen)        {          HalUARTRead (port, &pMsg->msg[MT_RPC_FRAME_HDR_SZ + tempDataLen], bytesInRxBuffer);          tempDataLen += bytesInRxBuffer;        }        else        {          HalUARTRead (port, &pMsg->msg[MT_RPC_FRAME_HDR_SZ + tempDataLen], LEN_Token - tempDataLen);          tempDataLen += (LEN_Token - tempDataLen);        }        //上面的代码比较有意思,想想else执行的情况会是什么情形呢        /* If number of bytes read is equal to data length, time to move on to FCS */        if ( tempDataLen == LEN_Token )            state = FCS_STATE;        break;      case FCS_STATE:        FSC_Token = ch;        /* Make sure it's correct */        if ((MT_UartCalcFCS ((uint8*)&pMsg->msg[0], MT_RPC_FRAME_HDR_SZ + LEN_Token) == FSC_Token))        {          osal_msg_send( App_TaskID, (byte *)pMsg );        }        else        {          /* deallocate the msg */          osal_msg_deallocate ( (uint8 *)pMsg );        }        /* Reset the state, send or discard the buffers at this point */        state = SOP_STATE;        break;      default:       break;    }  }}
       接着适当的解析osal_msg_send,网蜂没有深入。但是为了以后的深入学习,还是要自己动手看看。该函数为简单

的一个任务通信实现,和Linux内核的进程通信比起来,简单多了。
 

uint8 osal_msg_send( uint8 destination_task, uint8 *msg_ptr ){  ...............  OSAL_MSG_ID( msg_ptr ) = destination_task;//见下  // queue message  osal_msg_enqueue( &osal_qHead, msg_ptr );//加入list,消息队列  // Signal the task that a message is waiting  osal_set_event( destination_task, SYS_EVENT_MSG );//给相应任务,设置相应event  return ( SUCCESS );}

    问题1:该方式是有造成内存混乱之嫌,组成的链表方式呢?
((osal_msg_hdr_t *) (msg_ptr) - 1)->dest_id=destination_task
    

   接着如果想,简单通过串口助手去做, 不参考别人的例子,然后自己想想吧,很简单的,我提供一个自己想的版本:

void UartProcessData ( uint8 port, uint8 event ){  uint8  ch;  uint8  bytesInRxBuffer;  static uint8 state2=0;    (void)event;  // Intentionally unreferenced parameter  while (Hal_UART_RxBufLen(port))  {    HalUARTRead (port, &ch, 1);    switch (state2)    {      case 0:        LEN_Token = ch;        tempDataLen = 0;        /* Allocate memory for the data */        pMsg = (mtOSALSerialData_t *)osal_msg_allocate( sizeof ( mtOSALSerialData_t )+ LEN_Token);        if (pMsg)        {          /* Fill up what we can */          pMsg->hdr.event = CMD_SERIAL_MSG;          pMsg->msg = (uint8*)(pMsg+1);          state2 = 1;        }        else        {          state2 = 0;          return;        }        break;      case 1:        /* Fill in the buffer the first byte of the data */        pMsg->msg[tempDataLen++] = ch;        /* Check number of bytes left in the Rx buffer */        bytesInRxBuffer = Hal_UART_RxBufLen(port);        /* If the remain of the data is there, read them all, otherwise, just read enough */        if (bytesInRxBuffer <= LEN_Token - tempDataLen)        {          HalUARTRead (port, &pMsg->msg[tempDataLen], bytesInRxBuffer);          tempDataLen += bytesInRxBuffer;        }        else        {          HalUARTRead (port, &pMsg->msg[tempDataLen], LEN_Token - tempDataLen);          tempDataLen += (LEN_Token - tempDataLen);        }        osal_msg_send( App_TaskID, (byte *)pMsg );            /* Reset the state, send or discard the buffers at this point */        state2 = 0;        break;      default:       break;    }  }}
        取消了CMD,FCS,SOF,datalen,串口数据完全始于原始串口协议。要知道你发送端怎么该,接收端也要相应改

        之后网蜂的做法很取巧,使用广播啊,不要考虑组网啊!

         AF_DataRequest,关于这个函数comment没太大作用,再看看手册,没找到,还是看comment:

/********************************************************************* * @fn      AF_DataRequest * * @brief   Common functionality for invoking APSDE_DataReq() for both *          SendMulti and MSG-Send. * * input parameters * * @param  *dstAddr - Full ZB destination address: Nwk Addr + End Point. * @param  *srcEP - Origination (i.e. respond to or ack to) End Point Descr. * @param   cID - A valid cluster ID as specified by the Profile. * @param   len - Number of bytes of data pointed to by next param. * @param  *buf - A pointer to the data bytes to send. * @param  *transID - A pointer to a byte which can be modified and which will *                    be used as the transaction sequence number of the msg. * @param   options - Valid bit mask of Tx options. * @param   radius - Normally set to AF_DEFAULT_RADIUS. * * output parameters * * @param  *transID - Incremented by one if the return value is success. * * @return  afStatus_t - See previous definition of afStatus_... types.
        太抽象了,还是直接使用吧。复杂问题简单化,我们知道此时它把数据发送出去了。接收端我要提一个问题:

        问题2:AF_INCOMING_MSG_CMD 在哪里被置位呢?

        然后快速做实验!

             
            
      

        

      

    


      




     

       

原创粉丝点击