Zynq中的UART支持輪詢和中斷驅動兩種模式。本文給出兩個使用輪詢模式的例子,在24篇程序框架的基礎上進行改動(貼出主要改動代碼,改動很小的地方,如函數接口變化導致函數聲明也要改,相信你可以根據我的代碼和設計目的自己完成),最後再討論一下輪詢模式的特點。
第一個例子
改造user_uart.c文件中的Uart_Send函數,將模式設置爲本地迴環。UART發送數據(小於64個字節,即FIFO長度)後阻塞等待,直到所有數據發送完成。然後再次阻塞,等待所有數據都讀取完。比較發送和接收的數據,一致則表示通信成功。
//--------------------------------------------------------------
// UART數據發送函數
//--------------------------------------------------------------
int Uart_Send(XUartPs* Uart_Ps, u8 *SendBuffer, u8 *RecvBuffer, int length)
{
u16 SentCount, RecvCount;
XUartPs_SetOperMode(Uart_Ps, XUARTPS_OPER_MODE_LOCAL_LOOP); //本地迴環模式
// 發送緩衝區中的數據
SentCount = XUartPs_Send(Uart_Ps, SendBuffer, length);
if (SentCount != length) {
return XST_FAILURE;
}
// 如果UART正在發送數據則等待
while(XUartPs_IsSending(Uart_Ps));
// 接收數據,阻塞直到接收了全部數據
RecvCount = 0;
while(RecvCount < length) {
RecvCount += XUartPs_Recv(Uart_Ps, &RecvBuffer[RecvCount],
(length - RecvCount));
}
XUartPs_SetOperMode(Uart_Ps, XUARTPS_OPER_MODE_NORMAL); //正常模式
// 比較接收緩衝區與發送緩衝區中的內容
int result = memcmp(SendBuffer, RecvBuffer, length);
if (result != 0) {
xil_printf("UART Polled Mode Failed!\r\n");
return XST_FAILURE;
}
xil_printf("UART Polled Mode succeeded!\r\n");
return XST_SUCCESS;
}
main.c文件的代碼如下,每隔一段時間進行一次串口的輪詢模式測試:
#include "user_uart.h"
#define BUFFER_SIZE 32
XUartPs Uart_Ps; /* The instance of the UART Driver */
static u8 SendBuffer[BUFFER_SIZE];
static u8 RecvBuffer[BUFFER_SIZE];
int main(void)
{
int Status;
/* 串口初始化 */
Status = Uart_Init(&Uart_Ps, UART_DEVICE_ID);
if (Status == XST_FAILURE) {
xil_printf("Uartps Failed\r\n");
return XST_FAILURE;
}
while (1)
{
sleep(3);
/* buffer初始化 */
for (u16 index = 0; index < BUFFER_SIZE; index++) {
SendBuffer[index] = '0' + index;
RecvBuffer[index] = 0;
}
/* UART輪詢模式 */
Uart_Send(&Uart_Ps, SendBuffer, RecvBuffer, BUFFER_SIZE);
}
return Status;
}
SDK Terminal中添加串口,波特率設置爲程序制定的9600,運行程序,將看到輪詢測試成功通過。
相關API函數
1.模式配置
本例中我們在輪詢時使用本地迴環模式,自發自收,驗證UART的功能。回顧22篇對UART控制器的介紹。
完成一次輪詢模式下的自發自收後,我們希望比較收發數據是否一致,並通過串口打印相關信息。此時如果還是停留在本地迴環模式,數據是無法傳輸到Zynq外部的。因此每次輪詢測試後,都將模式重新設置爲正常模式。
2.數據發送
程序中使用XUartPs_Send函數發送數據。這個函數是非阻塞的,輪詢模式和中斷驅動模式下都可以使用。它會盡可能地想TxFIFO填充數據,並返回發送的字節數;如果無法填充,會返回0表示發送了0字節,便於用戶處理。
中斷模式下,該函數會發送指定的緩衝區(Buffer)中的內容,中斷處理程序負責將所有數據全部發送完。此時會調用綁定的回調函數,標識發送完成。關於中斷的用法在後面文章中專門介紹。
u32 XUartPs_Send(XUartPs *InstancePtr, u8 *BufferPtr, u32 NumBytes)
第二個參數是指向要發送的數據緩衝區的指針;第三個參數是發送的字節數;返回值標識實際發送的字節數。本例程序中就是利用返回值確保所有數據都依次發送(雖然本例的數量不大,但要學習這個用法)。
這個函數還有個特殊用法,如果將第三個參數設爲0,則會停止正在進行的發送操作,並將已經在TxFIFO中的所有數據都發送出去。可以用這個用法實現某些特殊功能。
3.發送狀態判斷
發送數據後使用XUartPs_IsSending函數檢測UART是否正在發送數據。因爲XUartPs_Send函數只是將數據傳入TxFIFO中,UART控制器還要將其串行化轉移到發送的移位寄存器中。
u32 XUartPs_IsSending(XUartPs *InstancePtr)
這樣做是爲了確保接收數據時可以“確確實實的”收到所有數據。
4.數據接收
程序中使用XUartPs_Recv函數接收數據。這個函數本身是非阻塞的。輪詢模式中,該函數只會接收已經在RxFIFO中的數據,因此用while來反覆調用該函數,阻塞接收指定數目的數據。中斷模式下該函數的使用在第25篇中介紹。
u32 XUartPs_Recv(XUartPs *InstancePtr, u8 *BufferPtr, u32 NumBytes)
第二參數指針指向接收到數據要存儲的緩衝區;第三個參賽NumBytes是“要”接收的字節數;返回的是實際接收到的字節數。重新節選出接收數據的代碼:
RecvCount = 0;
while(RecvCount < length) {
RecvCount += XUartPs_Recv(Uart_Ps, &RecvBuffer[RecvCount],
(length - RecvCount));
}
正是利用了第三個參賽和返回值,實現了接收特定數目數據的功能,我們要學習這個寫法。我們本例設計的是輪詢模式,因此使用while進行阻塞。
和發送一樣,這個函數將第三個參數設爲0,也會停止正在進行的接收操作。
第二個例子
我們思考一下:輪詢模式和中斷驅動模式的最主要區別是什麼?從上個例子我們還看不太出,因爲我們自發自收,數據到的都很及時。考慮下面這個例子。
改造Uart_Send函數,模式設置爲正常模式不變,數據源這次由外部輸入。爲了方便將緩衝區大小改爲8。接收時阻塞等待,直到所有數據都讀取完,再將收到的數據echo返回。函數代碼如下(其它部分只是小改動,相信你可以自己完成):
int Uart_Send(XUartPs* Uart_Ps, u8 *RecvBuffer, int length)
{
u16 SentCount, RecvCount;
// 如果UART正在發送數據則等待
while(XUartPs_IsSending(Uart_Ps));
// 接收數據,阻塞直到接收了全部數據
RecvCount = 0;
while(RecvCount < length) {
RecvCount += XUartPs_Recv(Uart_Ps, &RecvBuffer[RecvCount],
(length - RecvCount));
}
// 發送接收到的數據
SentCount = XUartPs_Send(Uart_Ps, RecvBuffer, length);
if (SentCount != length) {
return XST_FAILURE;
}
xil_printf("\r\nUART Polled Mode succeeded!\r\n");
return XST_SUCCESS;
}
這種情況就比較尷尬了,我們會發現如果UART沒有收到指定數目的數據,則會一致停留在while中掛起等待,然而這也是實際情況。這也是輪詢模式的缺點。無操作系統的嵌入式開發本來就是單線程的,如果我們一直在這裏掛起,便無法執行其它程序。
SDK自帶的串口終端發送數據時會自動加個換行符,爲了觀察方便,用串口調試助手進行測試,結果如下。
只有我們發送夠了8個數據後,UART的掛起狀態纔會結束。如果我們一次性發送的數據超過了8個(如紅框所示),則多餘的數據會留在RxFIFO中,直到下一次輪詢才被讀取。
總結
本文介紹了UART的輪詢模式。除非是特別簡單的應用,一般不會使用輪詢模式。如果要使用輪詢模式,一定要有個良好的程序架構或實現機制,避免程序無限掛起。