學會Zynq(26)UART輪詢(poll)模式示例

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的輪詢模式。除非是特別簡單的應用,一般不會使用輪詢模式。如果要使用輪詢模式,一定要有個良好的程序架構或實現機制,避免程序無限掛起。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章