第十八節(jié) 協(xié)議棧UART實(shí)驗(yàn)
協(xié)議棧中已經(jīng)用了串口的驅(qū)動(dòng),我們要做的只是對(duì)串口進(jìn)行初始化,然后就可以進(jìn)行串口數(shù)據(jù)的收發(fā)了。
用使用串口,第一步,需要打開(kāi)使能串口功能,通過(guò)配置工程來(lái)實(shí)現(xiàn),這里注意,我們現(xiàn)在不使用USB的CDC類(lèi)來(lái)實(shí)現(xiàn)串口,所以HAL_UART_USB=FALSE。
HAL_UART=TRUE
HAL_UART_USB=FALSE
要使用串口必須先初始化相應(yīng)的串口,那該如何初始化呢?在Hal_uart.h文件中我們可以看到如下函數(shù)。
uint8 HalUARTOpen(uint8 port, halUARTCfg_t *config);
這個(gè)函數(shù)就是用來(lái)初始化串口的,這個(gè)函數(shù)有兩個(gè)參數(shù),第一個(gè)指定串口號(hào),第二個(gè)是串口的配置參數(shù)。我們來(lái)看看這個(gè)結(jié)構(gòu)體的定義:
typedef struct
{
bool configured; // 配置與否
uint8 baudRate; // 波特率
bool flowControl; // 流控制
uint16 flowControlThreshold;
uint8 idleTimeout; // 空閑時(shí)間
halUARTBufControl_t rx; // 接收
halUARTBufControl_t tx; // 發(fā)送
bool intEnable; // 中斷使能
uint32 rxChRvdTime; // 接收數(shù)據(jù)時(shí)間
halUARTCBack_t callBackFunc; // 回調(diào)函數(shù)
}halUARTCfg_t;
這個(gè)結(jié)構(gòu)體成員很多,但是我們?cè)谑褂么诘臅r(shí)候并不需要使用所有的成員。
void Serial_Init(void)
{
halUARTCfg_t SerialCfg = {0};
SerialCfg.baudRate = HAL_UART_BR_115200; // 波特率
SerialCfg.flowControl = HAL_UART_FLOW_OFF; // 流控制
SerialCfg.callBackFunc = SerialCb; // 回調(diào)函數(shù)
SerialCfg.intEnable = TRUE;
SerialCfg.configured = TRUE;
HalLcdWriteString( “Open Uart0”, HAL_LCD_LINE_5 ); // 在第5行顯示啟動(dòng)信息
HalUARTOpen(HAL_UART_PORT_0, &SerialCfg);
HalUARTWrite(HAL_UART_PORT_0, “Hello MT254xBoard ”, osal_strlen(“Hello MT254xBoard ”));
}
在串口回調(diào)函數(shù)中我們只做一件事,將串口接收到的數(shù)據(jù)顯示到LCD中并且原樣的從串口輸出?;卣{(diào)函數(shù)的實(shí)現(xiàn)如下:
static void SerialCb( uint8 port, uint8 events )
{
uint8 RxBuf[64]={0};
if((events & HAL_UART_TX_EMPTY)||( events & HAL_UART_TX_FULL )) // 發(fā)送區(qū)滿(mǎn)或者空
{
return;
}
uint16 usRxBufLen = Hal_UART_RxBufLen(HAL_UART_PORT_0); // 讀取接收據(jù)量
usRxBufLen = MIN(64,usRxBufLen);
uint16 readLen = HalUARTRead(HAL_UART_PORT_0, RxBuf, usRxBufLen);
HalUARTWrite(HAL_UART_PORT_0, RxBuf, usRxBufLen);
}
實(shí)驗(yàn)現(xiàn)象,從實(shí)驗(yàn)現(xiàn)象中可以看到,一開(kāi)始在串口中輸出了一個(gè)標(biāo)志字符串,然后我們通過(guò)串口發(fā)送了0123456789,然后數(shù)據(jù)原樣的從串口輸出了,這和我們預(yù)期的結(jié)果是一樣的。
但是我們發(fā)現(xiàn)LCD上的顯示和我們預(yù)期的不一樣,LCD上只顯示了6789,前面的數(shù)據(jù)并沒(méi)有顯示,這是怎么一回事呢?進(jìn)行單步調(diào)試可以發(fā)現(xiàn),我們發(fā)送一次數(shù)據(jù),回調(diào)函數(shù)被回調(diào)了兩次,第一次回調(diào)只接受到了012345,第二次回調(diào)接收到了6789,而在LCD上的顯示第二次覆蓋了第一次的顯示,所以我們會(huì)看到這種現(xiàn)象,解決的辦法,我們需要定義一個(gè)數(shù)據(jù)幀的時(shí)間間隔,當(dāng)接收數(shù)據(jù)的間隔超過(guò)了此間隔就認(rèn)為接收結(jié)束。
下面我們改寫(xiě)接收處理,我們?cè)诮邮盏綌?shù)據(jù)后開(kāi)啟定時(shí)器,定時(shí)5ms這樣,當(dāng)接收間隔大于5ms后,我們就可以在定時(shí)事件中處理串口接收到的數(shù)據(jù)。
static void SerialCb( uint8 port, uint8 events )
{
if((events & HAL_UART_TX_EMPTY)||( events & HAL_UART_TX_FULL )) // 發(fā)送區(qū)滿(mǎn)或者空
{
return;
}
uint16 usRxBufLen = Hal_UART_RxBufLen(HAL_UART_PORT_0); // 讀取接收據(jù)量
if(usRxBufLen)
{
usRxBufLen = MIN(128,usRxBufLen);
uint16 readLen = HalUARTRead(HAL_UART_PORT_0, &SerialRxBuf[RxIndex], usRxBufLen); // 讀取數(shù)據(jù)到緩沖區(qū)
RxIndex += readLen;
readLen %= 128;
osal_start_timerEx(simpleBLEPeripheral_TaskID, UART_EVENT, 5); // 啟動(dòng)定時(shí)器
}
}
事件處理代碼:
if ( events & UART_EVENT )
{
HalLcdWriteString( (char*)SerialRxBuf, HAL_LCD_LINE_6 ); // 在第5行顯示啟動(dòng)信息
HalUARTWrite(HAL_UART_PORT_0, SerialRxBuf, osal_strlen(SerialRxBuf));
osal_memset(SerialRxBuf, 0, 128);
return (events ^ UART_EVENT);
}
經(jīng)過(guò)這樣的處理后,可以發(fā)現(xiàn)我們剛剛的問(wèn)題已經(jīng)解決了。
到這里串口已經(jīng)可以正常使用了,為了更加方便的使用串口,我在這里添加一個(gè)函數(shù)實(shí)現(xiàn)標(biāo)準(zhǔn)C中printf,這樣更有利于我們輸出。
int SerialPrintf(const char*fmt, 。。。)
{
uint32 ulLen;
va_list ap;
char *pBuf = (char*)osal_mem_alloc(PRINT_BUF_LEN); // 開(kāi)辟緩沖區(qū)
va_start(ap, fmt);
ulLen = vsprintf(pBuf, fmt, ap); // 用虛擬打印函數(shù)實(shí)現(xiàn)
va_end(ap);
HalUARTWrite(HAL_UART_PORT_0, (uint8*)pBuf, ulLen); // 從串口0輸出
osal_mem_free(pBuf); // 釋放內(nèi)存空間
return ulLen;
}
我們可以像使用C標(biāo)準(zhǔn)中的printf來(lái)使用這個(gè)函數(shù),例如我們將LCD的輸出全部導(dǎo)向串口的輸出,在HalLcdWriteString的實(shí)現(xiàn)中添加串口輸出代碼,如下圖:
重新編譯并且燒錄后可以看到LCD的輸出和串口的輸出是一樣的了。
評(píng)論