91超碰碰碰碰久久久久久综合_超碰av人澡人澡人澡人澡人掠_国产黄大片在线观看画质优化_txt小说免费全本

溫馨提示×

溫馨提示×

您好,登錄后才能下訂單哦!

密碼登錄×
登錄注冊×
其他方式登錄
點擊 登錄注冊 即表示同意《億速云用戶服務條款》

Z-Stack串口通信使用心得

發布時間:2020-08-18 16:00:57 來源:網絡 閱讀:3837 作者:dcsarsen 欄目:開發技術

最近在做一個智能家居的項目,用到了TI的CC2530芯片以及對應的zstack協議棧,其中串口通信部分使用的最多,下面就分享一下Z-Stack對串口封裝的使用心得。


Z-Stack中對串口操作的封裝主要在hal_uart.h,hal_uart.c中, 支持DMA和ISR兩種處理方式, 真正的實現則都封裝在_hal_uart_dma.c 和_hal_uart_isr.c中,  但系統只推薦使用DMA方式, 可以通過修改宏定義來改為ISR的方式,宏定義在hal_board_cfg.h中


Z-Stack對串口操作的封裝使用了緩沖區的方式, 讀寫都是直接操作緩沖區, 不管是DMA方式還是ISR方式都是如此,下面以DMA為例介紹:

typedef struct
{
  uint16 rxBuf[HAL_UART_DMA_RX_MAX];
#if HAL_UART_DMA_RX_MAX < 256
  uint8 rxHead;
  uint8 rxTail;
#else
  uint16 rxHead;
  uint16 rxTail;
#endif
  uint8 rxTick;
  uint8 rxShdw;
  uint8 txBuf[2][HAL_UART_DMA_TX_MAX];
#if HAL_UART_DMA_TX_MAX < 256
  uint8 txIdx[2];
#else
  uint16 txIdx[2];
#endif
  volatile uint8 txSel;
  uint8 txMT;
  uint8 txTick;           // 1-character time in 32kHz ticks according to baud rate,
                          // to be used in calculating time lapse since DMA ISR
                          // to allow delay margin before start firing DMA, so that
                          // DMA does not overwrite UART DBUF of previous packet
  
  volatile uint8 txShdw;  // Sleep Timer LSB shadow.
  volatile uint8 txShdwValid; // TX shadow value is valid
  uint8 txDMAPending;     // UART TX DMA is pending
  halUARTCBack_t uartCB;
} uartDMACfg_t;

uartDMACfg_t結構體定力了相關的數據結構, 其中rxBuf和txBuf分別對應讀寫緩沖區


1、寫操作

static uint16 HalUARTWriteDMA(uint8 *buf, uint16 len)
{
  uint16 cnt;
  halIntState_t his;
  uint8 txIdx, txSel;
  // Enforce all or none.
  if ((len + dmaCfg.txIdx[dmaCfg.txSel]) > HAL_UART_DMA_TX_MAX)
  {
    return 0;
  }
  HAL_ENTER_CRITICAL_SECTION(his);
  txSel = dmaCfg.txSel;
  txIdx = dmaCfg.txIdx[txSel];
  HAL_EXIT_CRITICAL_SECTION(his);
  for (cnt = 0; cnt < len; cnt++)
  {
    dmaCfg.txBuf[txSel][txIdx++] = buf[cnt];
  }
  HAL_ENTER_CRITICAL_SECTION(his);
  if (txSel != dmaCfg.txSel)
  {
    HAL_EXIT_CRITICAL_SECTION(his);
    txSel = dmaCfg.txSel;
    txIdx = dmaCfg.txIdx[txSel];
    for (cnt = 0; cnt < len; cnt++)
    {
      dmaCfg.txBuf[txSel][txIdx++] = buf[cnt];
    }
    HAL_ENTER_CRITICAL_SECTION(his);
  }
  dmaCfg.txIdx[txSel] = txIdx;
  if (dmaCfg.txIdx[(txSel ^ 1)] == 0)
  {
    // TX DMA is expected to be fired
    dmaCfg.txDMAPending = TRUE;
  }
  HAL_EXIT_CRITICAL_SECTION(his);
  return cnt;
}

從這段代碼可以明顯看出, Z-Stack對串口的寫如果緩沖區剩余空間少于用戶寫入長度, 會直接返回0

也就是注釋里的enforce all or none,  由于DMA方式使用的是雙緩沖區,這個函數里也對緩沖區切換的情況做了保護。


2、讀操作

static uint16 HalUARTReadDMA(uint8 *buf, uint16 len)
{
  uint16 cnt;
  for (cnt = 0; cnt < len; cnt++)
  {
    if (!HAL_UART_DMA_NEW_RX_BYTE(dmaCfg.rxHead))
    {
      break;
    }
    *buf++ = HAL_UART_DMA_GET_RX_BYTE(dmaCfg.rxHead);
    HAL_UART_DMA_CLR_RX_BYTE(dmaCfg.rxHead);
    if (++(dmaCfg.rxHead) >= HAL_UART_DMA_RX_MAX)
    {
      dmaCfg.rxHead = 0;
    }
  }
  PxOUT &= ~HAL_UART_Px_RTS;  // Re-enable the flow on any read.
  return cnt;
}

這個函數很簡單,就是直接從rxBuf里讀取數據到用戶緩沖區中, 需要注意的是,如果讀到緩沖區的末尾,會自動調整游標到緩沖區頭, 可能造成讀到的數據并非真實接受到的數據, 所以在調用這個函數的時候,最好讀取數據不要超過HAL_UART_DMA_RX_MAX


3、poll操作

static void HalUARTPollDMA(void)
{
  uint16 cnt = 0;
  uint8 evt = 0;
  if (HAL_UART_DMA_NEW_RX_BYTE(dmaCfg.rxHead))
  {
    uint16 tail = findTail();
    // If the DMA has transferred in more Rx bytes, reset the Rx idle timer.
    if (dmaCfg.rxTail != tail)
    {
      dmaCfg.rxTail = tail;
      // Re-sync the shadow on any 1st byte(s) received.
      if (dmaCfg.rxTick == 0)
      {
        dmaCfg.rxShdw = ST0;
      }
      dmaCfg.rxTick = HAL_UART_DMA_IDLE;
    }
    else if (dmaCfg.rxTick)
    {
      // Use the LSB of the sleep timer (ST0 must be read first anyway).
      uint8 decr = ST0 - dmaCfg.rxShdw;
      if (dmaCfg.rxTick > decr)
      {
        dmaCfg.rxTick -= decr;
        dmaCfg.rxShdw = ST0;
      }
      else
      {
        dmaCfg.rxTick = 0;
      }
    }
    cnt = HalUARTRxAvailDMA();
  }
  else
  {
    dmaCfg.rxTick = 0;
  }
  if (cnt >= HAL_UART_DMA_FULL)
  {
    evt = HAL_UART_RX_FULL;
  }
  else if (cnt >= HAL_UART_DMA_HIGH)
  {
    evt = HAL_UART_RX_ABOUT_FULL;
    PxOUT |= HAL_UART_Px_RTS;
  }
  else if (cnt && !dmaCfg.rxTick)
  {
    evt = HAL_UART_RX_TIMEOUT;
  }
  if (dmaCfg.txMT)
  {
    dmaCfg.txMT = FALSE;
    evt |= HAL_UART_TX_EMPTY;
  }
  if (dmaCfg.txShdwValid)
  {
    uint8 decr = ST0;
    decr -= dmaCfg.txShdw;
    if (decr > dmaCfg.txTick)
    {
      // No protection for txShdwValid is required
      // because while the shadow was valid, DMA ISR cannot be triggered
      // to cause concurrent access to this variable.
      dmaCfg.txShdwValid = FALSE;
    }
  }
  
  if (dmaCfg.txDMAPending && !dmaCfg.txShdwValid)
  {
    // UART TX DMA is expected to be fired and enough time has lapsed since last DMA ISR
    // to know that DBUF can be overwritten
    halDMADesc_t *ch = HAL_DMA_GET_DESC1234(HAL_DMA_CH_TX);
    halIntState_t intState;
    // Clear the DMA pending flag
    dmaCfg.txDMAPending = FALSE;
    
    HAL_DMA_SET_SOURCE(ch, dmaCfg.txBuf[dmaCfg.txSel]);
    HAL_DMA_SET_LEN(ch, dmaCfg.txIdx[dmaCfg.txSel]);
    dmaCfg.txSel ^= 1;
    HAL_ENTER_CRITICAL_SECTION(intState);
    HAL_DMA_ARM_CH(HAL_DMA_CH_TX);
    do
    {
      asm("NOP");
    } while (!HAL_DMA_CH_ARMED(HAL_DMA_CH_TX));
    HAL_DMA_CLEAR_IRQ(HAL_DMA_CH_TX);
    HAL_DMA_MAN_TRIGGER(HAL_DMA_CH_TX);
    HAL_EXIT_CRITICAL_SECTION(intState);
  }
  if (evt && (dmaCfg.uartCB != NULL))
  {
    dmaCfg.uartCB(HAL_UART_DMA-1, evt);
  }
}

HalUARTPollDMA函數是整個串口操作的核心, 該函數會被系統大循環定時的調用,在這個函數里

會判斷讀寫緩沖區的狀態, 進而觸發回調函數halUARTCfg_t.callBackFunc。在觸發會調函數的時候,會傳給回調函數幾個事件,而這些事件涉及到以下4個值:

#define HAL_UART_DMA_FULL         (HAL_UART_DMA_RX_MAX - 16)
#define HAL_UART_DMA_HIGH         (HAL_UART_DMA_RX_MAX / 2 - 16)
#define HAL_UART_DMA_IDLE         (6 * HAL_UART_MSECS_TO_TICKS)
dmaCfg.txMT

當緩沖區數據長度大于等于HAL_UART_DMA_FULL 時, 觸發HAL_UART_RX_FULL事件

當緩沖區數據長度大于等于HAL_UART_DMA_HIGH 時, 觸發HAL_UART_RX_ABOUT_FULL事件

當緩沖區數據長度小于HAL_UART_DMA_FULL且等待時間達到HAL_UART_DMA_IDLE 時, 觸發HAL_UART_TIMEOUT事件

當dmaCfg.txMT為真時,表明寫緩沖區數據已經全部寫入串口,觸發HAL_UART_TX_EMPTY事件


所以用Z-Stack的hal_uart庫對串口進行操作時, 推薦的做法是在回調函數里根據事件來判斷是否需要讀取數據,而寫操作可以放到程序的任何位置,包括回調函數里, 寫入數據的時候要判斷一下返回值, 看數據是否真正寫入到緩沖區中。


HalUARTPollDMA的調用頻率大概是間隔200ms, 參考

http://www.360doc.com/content/11/1022/09/7906690_158136472.shtml




向AI問一下細節

免責聲明:本站發布的內容(圖片、視頻和文字)以原創、轉載和分享為主,文章觀點不代表本網站立場,如果涉及侵權請聯系站長郵箱:is@yisu.com進行舉報,并提供相關證據,一經查實,將立刻刪除涉嫌侵權內容。

AI

临桂县| 红安县| 嘉峪关市| 沂南县| 美姑县| 富裕县| 福海县| 佛冈县| 民县| 彭州市| 卢湾区| 买车| 陆丰市| 虎林市| 甘孜| 西乌珠穆沁旗| 历史| 阿鲁科尔沁旗| 江西省| 白水县| 临汾市| 淮阳县| 靖宇县| 江都市| 武隆县| 鄯善县| 买车| 汶川县| 钟山县| 临洮县| 乳源| 乐山市| 江门市| 阿巴嘎旗| 东辽县| 嵊州市| 时尚| 芦溪县| 应用必备| 江口县| 乌兰浩特市|