CC2530之串列埠接收回撥函式MT_UartProcessZToolData()

小白技術棧發表於2020-10-21

需要測試程式碼的可以留下郵箱,或者關注微信公眾號 小白技術棧 後臺回覆【cc2530串列埠回撥函式】獲取。

CC2530使用MT層實現串列埠讀操作,在MT_UART.c檔案定義回撥函式:

#if defined (ZTOOL_P1) || defined (ZTOOL_P2)
  uartConfig.callBackFunc         = MT_UartProcessZToolData;

在串列埠接收回撥函式MT_UartProcessZToolData()中,Zstack採用狀態機接收的方式對串列埠接收資料進行處理,看似比較複雜,但其實這樣接收資料還能校驗資料的準確性,而且還有osal_msg_send( App_TaskID, (byte *)pMsg )傳送機制,處理資料比較方便。

其接收資料格式為 0xFE + 資料長度(僅是data的長度)+ 命令欄位 + data[] + 校驗和

程式碼註釋如下:

/***************************************************************************************************
 * @fn      MT_UartProcessZToolData
 *
 * @brief   | SOP | Data Length  |   CMD   |   Data   |  FCS  |
 *          |  1  |     1        |    2    |  0-Len   |   1   |
 *
 *          Parses the data and determine either is SPI or just simply serial data
 *          then send the data to correct place (MT or APP)
 *
 * @param   port     - UART port
 *          event    - Event that causes the callback
 *
 *
 * @return  None
 ***************************************************************************************************/
void MT_UartProcessZToolData ( uint8 port, uint8 event )
{
  uint8  ch;
  uint8  bytesInRxBuffer;
  
  (void)event;  // Intentionally unreferenced parameter

  while (Hal_UART_RxBufLen(port))    //判斷是否有資料
  {
    HalUARTRead (port, &ch, 1);    //每次接收一個資料

    switch (state)    
    {
      case SOP_STATE:    //初始狀態state = 0,判斷幀頭是否為SOP_STATE(0xFE)
        if (ch == MT_UART_SOF)
          state = LEN_STATE;    //若幀頭正確,狀態機狀態改為LEN_STATE
        break;

      case LEN_STATE:    //接收第二位元組時,進入LEN_STATE
        LEN_Token = ch;    //將第二位元組(傳輸資料data[]的長度)賦值給LEN_Token

        tempDataLen = 0;    //初始化臨時資料計數器tempDataLen為零

        /* Allocate memory for the data */
        pMsg = (mtOSALSerialData_t *)osal_msg_allocate( sizeof ( mtOSALSerialData_t ) +
                                                        MT_RPC_FRAME_HDR_SZ + LEN_Token );    //分配存放訊息的空間,由3位元組(1位元組資料長度位+2位元組CMD欄位)+ data[]長度

        if (pMsg)    //若有訊息接收
        {
          /* Fill up what we can */
          pMsg->hdr.event = CMD_SERIAL_MSG;    //事件ID,供應用層查詢使用
          pMsg->msg = (uint8*)(pMsg+1);    //加1的目的是為了分配存放資料末尾的1位元組校驗和
          pMsg->msg[MT_RPC_POS_LEN] = LEN_Token;    //將資料長度存放到msg[0]
          state = CMD_STATE1;    //修改狀態機狀態為CMD_STATE1
        }
        else    //無訊息則回到狀態機起始狀態
        {
          state = SOP_STATE;
          return;
        }
        break;

      case CMD_STATE1:    
        pMsg->msg[MT_RPC_POS_CMD0] = ch;    //第三位元組為命令欄位一
        state = CMD_STATE2;
        break;

      case CMD_STATE2:
        pMsg->msg[MT_RPC_POS_CMD1] = ch;    //第四位元組為命令欄位二
        /* If there is no data, skip to FCS state */
        if (LEN_Token)    //如果有資料接收,則狀態機改為DATA_STATE
        {
          state = DATA_STATE;
        }
        else    //如果無資料接收,則狀態機改為FCS_STATE校驗模式
        {
          state = FCS_STATE;
        }
        break;

      case DATA_STATE:

        /* Fill in the buffer the first byte of the data */
        pMsg->msg[MT_RPC_FRAME_HDR_SZ + tempDataLen++] = ch;    //開始存放data[]

        /* 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)    //如果緩衝區剩餘位元組數不大於分配                        的空間,則將剩餘位元組全部存放到msg
        {
          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);
        }

        /* 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;    //將最後一個位元組(校驗和)賦值給FSC_Token
        

        /* 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;
    }
  }
}

然後在SampleApp.c檔案uint16 SampleApp_ProcessEvent( uint8 task_id, uint16 events )函式狀態機中新增如下:

mtOSALSerialData_t *UartMsg;
.
.
.

case CMD_SERIAL_MSG:
                    UartMsg = (mtOSALSerialData_t *)MSGpkt;
                    
                    memcpy(uartRecvBuf, UartMsg->msg, UartMsg->msg[0] + 3);
                    SampleApp_Send_P2P_Message();
                    memset(uartRecvBuf, 0, UartMsg->msg[0] + 3); 

將串列埠接收到的訊息傳送到協調器,通過協調器列印出來。

cc2530串列埠接收資料由stm32傳送測試,其測試程式碼為:

uint8_t MT_UartCalcFCS( uint8_t *msg_ptr, uint8_t len )
{
  uint8_t x;
  uint8_t xorResult;

  xorResult = 0;

  for ( x = 0; x < len; x++, msg_ptr++ )
    xorResult = xorResult ^ *msg_ptr;

  return ( xorResult );
}

/*********************************************************************
 * @fn      led_entry
 *
 * @brief   Led run.
 *
 * @param   parameter - none
 *
 * @return  none
 */
void led_entry(void *parameter)
{
    uint8_t led_state = LED_ON;
		uint8_t test_buf[13] = {0xFE, 0x08, 0x03, 0x03, 0x00, 0x01, 0x02, 0x03, 0x04, 0x00};
		test_buf[11] = 0xff;
		
    while(1)
    {
        LED(led_state);
        led_state = ~led_state;
				test_buf[12] = MT_UartCalcFCS((uint8_t*)&test_buf[1], 11);
        HAL_UART_Transmit(&huart1, test_buf, 13, 0xFF);
				test_buf[4]++;
        rt_thread_mdelay(1000);
    }
}

 

相關文章