帶FIFO的UART數據接收

芯片手冊

某個Cortex-M4芯片帶有1個UART,支持Tx,Rx 的FIFO功能,並且能夠經過寄存器配置FIFO的閾值,芯片的datasheet並不完善,沒有說明RX的FIFO具體有幾個級別,每隔級別的閾值是多少。html

可是須要注意的是 TX, RX 的FIFO均可以經過UART 的 DR 寄存器進行訪問。ui

RX FIFO 閾值

功夫不負有心人,終於在SDK的某段代碼中窺見了RX的幾個FIFO閾值:線程

默認狀況下RX FIFO 是收到32字節纔會產生一次RX中斷。調試

若是收到的數據長度沒有32字節,則一直等,等到知足32字節,才產生RX中斷。code

RX FIFO 閾值 影響RX中斷產生的頻率

之因此注意到這個問題,是由於下面的一件事情:htm

調試的時候發現電腦發送6個字符,可是開發板並無打印6個字符,再加6個字符,仍是沒有,繼續加6個字符,直到知足了32個字節,這個時候纔有了RX中斷,把收到的數據打印了出來。blog

RX 收到的數據不完整

上面雖然收到了數據,可是並無把32個字符都保存下來。原來在UART中斷程序中,在RX中斷條件下,每次都把前面的數據清空了。開發

if (ui32Status & ( AM_REG_UART_IES_RXRIS_M | AM_REG_UART_IES_RTRIS_M)) {
    // 其餘操做
    os_memset(g_uart_buf, '\0', sizeof(g_uart_buf));  // 後來才意識到在 uart 中斷服務程序中每次接收都清空接收緩衝區是一個大錯誤。
    // 接收數據
    *(g_uart_buf + index++) = AM_REGn(UART, ui32Module, DR);
    
}

如上面的例子,會致使每一次中斷中清空前面的數據,永遠只能看到後一次FIFO中的數據,最多爲32個字節。這樣絕對是錯誤的,由於每次從FIFO中取走了一個字節,後面當FIFO滿時又會產生新的RX中斷。
因此每次中斷中儘可能取走全部的數據。該怎麼作呢?須要讀UART寄存器狀態,FIFO不空的狀況下把全部的數據取走,例以下面:get

if(ui32Status & (AM_REG_UART_IES_RXRIS_M | AM_REG_UART_IES_RTRIS_M))
    {
        while ( !AM_BFRn(UART, 0, FR, RXFE) )
        {
            if (uart_data_index < UART_BUF_LENGTH) { // 不能超出 g_uart_buf 的長度
                *(g_uart_buf + uart_data_index++) = AM_REGn(UART, ui32Module, DR);
            } else { // 超出部分直接丟棄
                ui8Char = AM_REGn(UART, ui32Module, DR);
            }
        }
    }

還須要注意,若是對端模塊返回的UART數據超過了接收緩衝區的長度,須要丟棄。若是不丟棄則沒法退出中斷。博客

總結

UART 使用主要是初始化,中斷服務程序以及應用程序對收發數據的處理。
初始化部分,須要完成的工做:

  • 引腳配置
  • UART模塊的時鐘使能
  • UART的配置如波特率,數據位,中止位,奇偶校驗,流控制
  • FIFO配置,通常重點關注RX的FIFO
  • UART模塊使能
  • UART中斷使能
  • UART中斷子類別使能,例如 AM_HAL_UART_INT_RX 表示RX中斷,AM_HAL_UART_INT_RX_TMOUT 表示超時中斷

中斷服務程序,通常來講主要負責接收,能夠按照下面的流程:

  • 讀UART的中斷狀態,斷定是哪種UART中斷,例如是RX仍是RX TMOUT 中斷
  • 根據具體的UART中斷類型採起對應的操做
  • 讀取DR寄存器,放到緩衝區,直到知足某個條件退出讀DR的循環
  • 清除中斷,修改某些標誌位,若是跑了OS,能夠發出信號量通知其餘線程
  • 退出中斷服務程序

應用程序,在某個時候處理UART接收緩衝區,必要的時候須要清空緩衝區。
若是不清空的話,UART中斷會一直把數據拷貝到緩衝區,直到緩衝區滿,後面的數據就浪費了。同時應用程序也會處理這些重複的數據可能沒法進入正常的流程。

按道理說在應用層清空緩衝區就能夠了,可是我擔憂應用程序在操做緩衝區的時候忽然被中斷打斷了,清空操做出錯了或者把剛剛收到的數據清空了,我覺得讓中斷程序在某些條件下清空或許是一個不錯的選擇。
以下是個人實現,應用層發出清空緩衝區的請求uart_data_clear(),若是剛好下一次UART中斷即時,就能夠在中斷一開始清空緩衝區。若是下一次UART中斷來的不及時,那麼第二次調用uart_data_clear()就能夠在應用層清空緩衝區。

extern unsigned char g_uart_buf[UART_BUF_LENGTH]; // UART 接收緩衝區
extern uint16_t uart_data_index; // 當前寫入UART接收緩衝區的數據的位置。
extern uint8_t  uart_data_clear_flag; // 1 -- 表示須要清除數據; 0 -- 不須要清除數據

/**
 * 第一次調用,可讓UART中斷清空緩衝區,第二次調用能夠在應用層清空緩衝區
 */
void uart_data_clear(void)
{
    // 說明尚未進入一次UART中斷,這裏在應用層主動清空UART數據
    if (uart_data_clear_flag) {
        os_memset(g_uart_buf, 0, sizeof(g_uart_buf));
        uart_data_index = 0;
        uart_data_clear_flag = 0;
    } else {
        // 在UART中斷清空數據
        uart_data_clear_flag = 1;
    }
}



// 另外一個文件中的UART中斷服務程序

void am_uart_isr(void)
{
    uint32_t ui32Status;
    uint32_t ui32Module = 0;
    uint8_t  ui8Char = 0;

    //
    // Read the masked interrupt status from the UART.
    //
    ui32Status = am_hal_uart_int_status_get(true);

    if(ui32Status & (AM_REG_UART_IES_RXRIS_M | AM_REG_UART_IES_RTRIS_M))
    {

        // 用戶已經處理完了上一次的UART接收數據,能夠清空緩衝區以備下一次接收
        if (uart_data_clear_flag) {
            uart_data_clear_flag = 0;
            os_memset(g_uart_buf, '\0', sizeof(g_uart_buf));
            uart_data_index = 0;
        }

        while ( !AM_BFRn(UART, 0, FR, RXFE) )
        {
            if (uart_data_index < UART_BUF_LENGTH) { // 不能超出 g_uart_buf 的長度
                *(g_uart_buf + uart_data_index++) = AM_REGn(UART, ui32Module, DR);
            } else { // 超出部分直接丟棄
                ui8Char = AM_REGn(UART, ui32Module, DR);
            }
        }
    }

    // Clear the UART interrupts.
    //
    am_hal_uart_int_clear(ui32Status);

    // 發出信號量通知其餘任務
   // do something...
}

聲明

歡迎轉載,請註明出處和做者,同時保留聲明。
做者:LinTeX9527
出處:http://www.javashuo.com/article/p-pxdevlxt-q.html 本博客的文章如無特殊說明,均爲原創,轉載請註明出處。如未經做者贊成必須保留此段聲明,且在文章頁面明顯位置給出原文鏈接,不然保留追究法律責任的權利。

相關文章
相關標籤/搜索