第十八節 協議棧UART實驗

第十八節 協議棧UART實驗


協議棧中已經用了串口的驅動,我們要做的只是對串口進行初始化,然後就可以進行串口數據的收發了。
用使用串口,第一步,需要打開使能串口功能,通過配置工程來實現,這裏注意,我們現在不使用USB的CDC類來實現串口,所以HAL_UART_USB=FALSE。
HAL_UART=TRUE
HAL_UART_USB=FALSE
要使用串口必須先初始化相應的串口,那該如何初始化呢?在Hal_uart.h文件中我們可以看到如下函數。
uint8 HalUARTOpen(uint8 port, halUARTCfg_t *config);
 這個函數就是用來初始化串口的,這個函數有兩個參數,第一個指定串口號,第二個是串口的配置參數。我們來看看這個結構體的定義:
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 Serial_Init(void)
{
    halUARTCfg_t SerialCfg = {0};

    SerialCfg.baudRate = HAL_UART_BR_115200;    // 波特率
    SerialCfg.flowControl = HAL_UART_FLOW_OFF;  // 流控制

    SerialCfg.callBackFunc = SerialCb;          // 回調函數
    SerialCfg.intEnable    = TRUE;
    SerialCfg.configured   = TRUE;
    HalLcdWriteString( "Open Uart0", HAL_LCD_LINE_5 );    // 在第5行顯示啓動信息
    HalUARTOpen(HAL_UART_PORT_0, &SerialCfg);
    HalUARTWrite(HAL_UART_PORT_0, "Hello MT254xBoard\r\n", osal_strlen("Hello MT254xBoard\r\n"));
}

在串口回調函數中我們只做一件事,將串口接收到的數據顯示到LCD中並且原樣的從串口輸出。回調函數的實現如下:
static void SerialCb( uint8 port, uint8 events )
{
    uint8 RxBuf[64]={0};
    if((events & HAL_UART_TX_EMPTY)||( events & HAL_UART_TX_FULL ))  // 發送區滿或者空
    {
        return;
    }

    uint16 usRxBufLen = Hal_UART_RxBufLen(HAL_UART_PORT_0);  // 讀取接收據量
    usRxBufLen = MIN(64,usRxBufLen);
    uint16 readLen = HalUARTRead(HAL_UART_PORT_0, RxBuf, usRxBufLen);
    HalUARTWrite(HAL_UART_PORT_0, RxBuf, usRxBufLen);
}

 實驗現象,從實驗現象中可以看到,一開始在串口中輸出了一個標誌字符串,然後我們通過串口發送了0123456789,然後數據原樣的從串口輸出了,這和我們預期的結果是一樣的。


    但是我們發現LCD上的顯示和我們預期的不一樣,LCD上只顯示了6789,前面的數據並沒有顯示,這是怎麼一回事呢?進行單步調試可以發現,我們發送一次數據,回調函數被回調了兩次,第一次回調只接受到了012345,第二次回調接收到了6789,而在LCD上的顯示第二次覆蓋了第一次的顯示,所以我們會看到這種現象,解決的辦法,我們需要定義一個數據幀的時間間隔,當接收數據的間隔超過了此間隔就認爲接收結束。


下面我們改寫接收處理,我們在接收到數據後開啓定時器,定時5ms這樣,當接收間隔大於5ms後,我們就可以在定時事件中處理串口接收到的數據。
static void SerialCb( uint8 port, uint8 events )
{
    if((events & HAL_UART_TX_EMPTY)||( events & HAL_UART_TX_FULL ))  // 發送區滿或者空
    {
        return;
    }
    uint16 usRxBufLen = Hal_UART_RxBufLen(HAL_UART_PORT_0);  // 讀取接收據量
    if(usRxBufLen)
    {
        usRxBufLen = MIN(128,usRxBufLen);
        uint16 readLen = HalUARTRead(HAL_UART_PORT_0, &SerialRxBuf[RxIndex], usRxBufLen);   // 讀取數據到緩衝區
        RxIndex += readLen;
        readLen %= 128;
        osal_start_timerEx(simpleBLEPeripheral_TaskID, UART_EVENT, 5);  // 啓動定時器
    }
}

事件處理代碼:
if ( events & UART_EVENT )
  {
    HalLcdWriteString( (char*)SerialRxBuf, HAL_LCD_LINE_6 );    // 在第5行顯示啓動信息
    HalUARTWrite(HAL_UART_PORT_0, SerialRxBuf, osal_strlen(SerialRxBuf));
    osal_memset(SerialRxBuf, 0, 128);
    return (events ^ UART_EVENT);
  }

事件處理代碼:經過這樣的處理後,可以發現我們剛剛的問題已經解決了。



    到這裏串口已經可以正常使用了,爲了更加方便的使用串口,我在這裏添加一個函數實現標準C中printf,這樣更有利於我們輸出。
int SerialPrintf(const char*fmt, ...)
{
    uint32  ulLen;
    va_list ap;

    char *pBuf = (char*)osal_mem_alloc(PRINT_BUF_LEN);  // 開闢緩衝區
    va_start(ap, fmt);
    ulLen = vsprintf(pBuf, fmt, ap);        // 用虛擬打印函數實現
    va_end(ap);

    HalUARTWrite(HAL_UART_PORT_0, (uint8*)pBuf, ulLen); // 從串口0輸出
    osal_mem_free(pBuf);    // 釋放內存空間
    return ulLen;
}

我們可以像使用C標準中的printf來使用這個函數,例如我們將LCD的輸出全部導向串口的輸出,在HalLcdWriteString的實現中添加串口輸出代碼,如下圖:


重新編譯並且燒錄後可以看到LCD的輸出和串口的輸出是一樣的了。


本文章轉載自
http://www.deyisupport.com/question_answer/wireless_connectivity/bluetooth/f/103/t/69222.aspx
請勿用於商業
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章