帶有FIFO硬件緩存的串口


目錄

51單片機的串口並沒有配置FIFO硬件,故收發數據時只能一字節一字節地收發。最近接觸了FIFO(先進先出)的硬件緩存,這樣串口的配置就更多了一個維度。

以CH58x系列單片機為例,在CH583的手冊中(筆者手頭的是CH582m的板子,與CH583用法一致,只有部分硬件不同),有關於串口和FIFO配置的說明。具體應用可以查看筆者關於串口透傳的一篇隨筆。

9.3.1 波特率計算
1)計算串口內部基准時鍾 Fuart,設置 R8_UARTx_DIV 寄存器,最大值 127,通常寫入 1。
2)計算波特率,設置 R16_UARTx_DL 寄存器。 波特率公式 =Fsys * 2 / R8_UARTx_DIV / 16 / R16_UARTx_DL。    //通過GetSysClock()這個函數獲得主頻

9.3.2 串口發送
串口發送的“THR 寄存器空”中斷 UART_II_THR_EMPTY 是指當前發送 FIFO 空。
當讀取 IIR 寄存器后,該中斷被清除,或者當向 THR 寫入下一個數據后,該中斷也能被清除。     //IIR:中斷識別寄存器 THR:發送保存寄存器
如果僅僅是向 THR寫入 一個字節,那么由於該字節很快被轉移到發送移位寄存器 TSR 中開始發送,所以很快會再次產生發送 THR 寄存器空中斷的請求,此時可以寫入下一個准備發送的數據。
當 TSR 寄存器中的數據被全部移出 后,串口發送才真正完成,此時 LSR 寄存器的 RB_LSR_TX_ALL_EMP 位變為 1 有效。      //TSR:發送移位寄存器
在中斷觸發方式下,當收到串口發送保持寄存器 THR 空的中斷后,如果已使能 FIFO,那么可以向 THR 寄存器及 FIFO 一次寫入最多 8 字節,然后控制器會按順序自動發送;
如果禁止 FIFO,那么一 次只能寫入一個字節;        //使能FIFO后,向THR寫入的數據先存放於FIFO中,自動以此發送,一次可以寫入多個字節
如果沒有數據需要發送,那么可以直接退出(之前讀取 IIR 時已經自動清除中斷)。
在查詢方式下,可以根據 LSR 寄存器的 RB_LSR_TX_FIFO_EMP 位判斷發送 FIFO 是否為空,當此位 為 1 則可以向 THR 寄存器及 FIFO 寫入數據,如果使能 FIFO,那么一次可以寫入最多 8 個字節。
也可讀取 R8_UARTx_TFC 寄存器判斷當前 FIFO 中待發送的剩余數據個數,如果不等於8,則可繼 續向 FIFO 中寫入待發送數據,這種方式可以節約填充時間。

9.3.3 串口接收      //假設設置FIFO設置為7個字節的緩存
串口接收數據可用中斷 UART_II_RECV_RDY 是指接收 FIFO 中的已有數據字節數已經到或超過由FCR 寄存器的 RB_FCR_FIFO_TRIG 設置選擇的 FIFO 觸發點。        //FIFO中的數據達到了設定的7個字節,會觸發_RECV_RDY ,若取出這7個字節,FIFO中會空
當從 RBR 讀取數據使 FIFO 字數低於 FIFO 觸發點時,該中斷被清除。      //RBR:接收緩存寄存器
串口接收數據超時中斷 UART_II_RECV_TOUT 是指接收 FIFO 中至少有一個字節的數據,
並且從上一次串口接收到數據和從上一次被系統取走數據開始,已經等待了相當於接收 4 個數據的時間。      //這里的“4”個數據的時間與FIFO觸發設置的“4”字節無關,注意:FIFO中要留有一個數據進行比較,該標志才能有效
當再次接收到一個新的數據后,該中斷被清除,或者當單片讀取一次 RBR 寄存器后,該中斷也能被清除。      //接收到新數據或者讀取RBR后,超時標志位清除
當接收FIFO全空時, LSR寄存器的RB_LSR_DATA_RDY位為0,當接收FIFO中有數據時, RB_LSR_DATA_RDY 位為 1 有效。      //LSR:線路狀態寄存器 _RDY標志位為1時有效,表示FIFO中有數據
在中斷觸發方式下,當收到串口接收數據超時的中斷后,可以讀取 R8_UARTx_RFC 寄存器查詢當前 FIFO 中剩余數據計數,直接讀取全部數據,     //超時中斷后,判斷為沒有數據了,一次全部讀取FIFO中的數據或者不斷查詢LSR的標志位不斷讀取
或者不斷查詢 LSR 寄存器的 RB_LSR_DATA_RDY,如果 此位有效則讀數據,直到此位無效。
當收到串口接收數據可用的中斷后,可以先從 RBR 寄存器一次性讀取 RB_FCR_FIFO_TRIG 設定字節個數的數據,     //收到可用中斷后,可以從_TRIG寄存器讀取設定字節的個數,亦可以結合_RDY和_RFC讀取所有數
或者也可以根據 RB_LSR_DATA_RDY 位和 R8_UARTx_RFC 寄存器讀取當前 FIFO 中所有數據。
在查詢方式下,可以根據 LSR 寄存器的 RB_LSR_DATA_RDY 位判斷接收 FIFO 是否為空,或讀取 R8_UARTx_RFC 寄存器獲取當前 FIFO 中數據計數,來獲取串口接收的所有數據。

//以下為串口1初始化函數

void u1_init()
{
  uint32_t x;

  /* 配置串口1:先配置IO口模式,再配置串口 */
  GPIOA_SetBits(GPIO_Pin_9); //先將PA9置位為高
  GPIOA_ModeCfg(GPIO_Pin_8, GPIO_ModeIN_PU); // RXD-配置上拉輸入
  GPIOA_ModeCfg(GPIO_Pin_9, GPIO_ModeOut_PP_5mA); // TXD-配置推挽輸出,注意要先讓IO口輸出高電平

  x = 10 * GetSysClock() / 8 / 115200; //串口1定為115200波特率
  x = (x + 5) / 10; //整型運算中四舍五入
  R16_UART1_DL = (uint16_t)x; //給波特率寄存器賦值

  R8_UART1_FCR = (2 << 6) | RB_FCR_TX_FIFO_CLR | RB_FCR_RX_FIFO_CLR | RB_FCR_FIFO_EN;
      //FIFO控制寄存器配置,10左移6位:觸發點4字節;收發FIFO數據都清空;FIFO使能
  R8_UART1_LCR = RB_LCR_WORD_SZ; //線路控制寄存器配置,選擇串口數據長度
  R8_UART1_IER = RB_IER_TXD_EN | RB_IER_RECV_RDY | RB_IER_LINE_STAT; //中斷使能控制器配置,TX引腳輸出使能;接收中斷使能;接收線路狀態中斷使能
  R8_UART1_MCR |= RB_MCR_INT_OE; //調制解調器控制寄存器配置,允許串口的中斷請求輸出
  R8_UART1_DIV = 1; //用於波特率配置,參考手冊中的公式

  PFIC_EnableIRQ(UART1_IRQn); //中斷注冊

}

//下為中斷服務函數

__INTERRUPT
__HIGH_CODE
void UART1_IRQHandler(void)
{
  volatile uint8_t i;

  switch(UART1_GetITFlag())
  {
    //略掉一個case分支

    case UART_II_RECV_RDY: // 數據達到設置觸發點
      for(i = 0; i != trigB-1; i++)//若要能夠走到_TOUT的case分支,這里需要留一個字節數據,故這里為trigB-1(FIFO觸發字節-1)
      {
        RxBuff[i] = UART1_RecvByte();
        UART1_SendByte(RxBuff[i]);
      }
      break;

    case UART_II_RECV_TOUT: // 接收超時,暫時一幀數據接收完成
      i = UART1_RecvString(RxBuff);
      UART1_SendString(RxBuff, i);
      break;

    //略掉幾個case分支

    default:
      break;
  }
}

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM