前言
我們在使用 PS 的時候,通常會添加 UART 控制器,用於打印信息和調試代碼。除此之外, PS 在和外部設備通信時,也會經常使用串口進行通信。先從UART控制器開始講起吧,從簡單的測試再到工程實例。
UART 控制器介紹
UART 控制器是一個全雙工異步收發控制器, ZYNQ 內部包含兩個 UART 控制器, UART0 和 UART1。每一個 UART 控制器支持可編程的波特率發生器、 64 字節的接收 FIFO 和發送 FIFO、產生中斷、 RXD 和TXD 信號的環回模式設置以及可配置的數據位長度、停止位和校驗方式等。UART 控制器系統框圖如下圖所示:
由上圖可知, UART 控制器和 IO 端口由參考時鍾( UART REF_CLK)驅動,同時控制器也需要連接APB 總線時鍾( CPU_1x clock), UART REF_CLK 和 CPU_1x clock 都是來自於 PS 時鍾子系統。 UART 控制器的配置以及狀態的獲取由控制( Control)和狀態寄存器( Status Registers)完成。另外, UART 控制器不僅可以連接至 MIO,也可以映射到 EMIO,從而使用 PL 的端口來實現串口通信的功能。當 UART 控制器連接到 MIO 時,只有 Tx(發送)和 Rx(接收)兩個引腳;而當連接 EMIO 時,除 Tx 和 Rx 引腳外,可選的還有 CTSN、 DSDN、 DSRN 等引腳,這些引腳用於串口的流控制,即調制解調器的數據通訊中。UART 控制器采用獨立的接收和發送數據路徑,每個路徑包含一個 64 字節的 FIFO,控制器對發送和接收 FIFO 中的數據進行串並轉換操作。 FIFO 的中斷標志支持輪詢處理或中斷驅動處理兩種方式。 另外,控制器中還有一個模式開關,支持 RXD 和 TXD 信號的各種環回配置。 UART 控制器內部框圖如下圖所示:
UART 控制器的寄存器通過 APB 從機接口和 PS AXI 總線互聯,控制器的寄存器用於對 UART 控制器進行配置和獲取狀態。波特率發生器( Baud Rate Generator)為 UART 控制器的接收端和發送端提供位周期時鍾;中斷控制器( GIC)為串口的收發提供了中斷服務的功能。APB 總線接口通過向 TxFIFO 寄存器寫值,將數據加載到 TxFIFO 存儲器中。當數據加載至 TxFIFO 后, TxFIFO 的空標志變成無效的狀態,直到最后一個數據從 TxFIFO 中移出,加載至傳輸移位寄存器, TxFIFO恢復空的標志位。同時 TxFIFO 使用 TFULL(滿中斷狀態)用於表示當前 TxFIFO 已經寫滿,並且會阻止數據繼續寫入。如果此時繼續執行寫操作,那么會觸發溢出,數據不會加載到 TxFIFO 中。RxFIFO 存儲器接收來自接收移位寄存器的數據,當接收完數據后, RxFIFO 空標志信號同樣變成無效的狀態,直到所有的數據通過 APB 總線發送出去。 RxFIFO 的滿標志狀態用於表示 RxFIFO 已經寫滿,並且會阻止更多的數據寫入。
發送FIFO與數據流
我們通過向TxFIFO寄存器寫值來把數據加載到TxFIFO中。TxFIFO有空標志,FIFO中有數據時此標志無效。當TxFIFO進入完全中斷狀態(TFULL)時,則表明FIFO已滿。此時再執行寫操作,會觸發溢出,數據不會加載到TxFIFO中。TxFIFO幾乎滿標志(TNFULL)表示FIFO中只有一個字節的空閑空間。我們可以設置門限觸發器(TTRIG),當FIFO中的字節達到此值時會觸發TTRIG(圖示見下小節)。
傳輸模塊從TxFIFO中移出並行數據,將其加載到發射機移位寄存器中,實現串行化。在波特率時鍾的下降沿傳輸數據,先傳LSB,如下圖:
接收FIFO與數據捕獲
RxFIFO存儲接收器串行移位寄存器收到的數據。RxFIFO也有與TxFIFO功能類似的空標志、完全中斷狀態RFUL、門限觸發器RTRIG。幾種標志和中斷的示意如下圖:
接收時,UART不斷地對RxD信號進行過采樣,當采樣檢測到一個由高到低的轉換時,便將其視作起始位的開始。在波特率時鍾周期的一半時,再進行三次采樣,如果仍然時低電平,則認為這是一個有效的起始位。示意如下,下圖是16倍的過采樣:
確定了一個有效的起始位后,重新同步接收器的波特率時鍾,以便在每一位的中心位置對傳入的RxD信號進行采樣(防止滑碼),示意如下圖。如果您編寫過Verilog的UART收發程序,對這個處理方法應該不會感到陌生。
當確定了一個串行數據位的值后,將其轉移到接收移位寄存器中。當組裝好一個完整的字符后,移位寄存器中的內容被推送到RxFIFO中。
接收器錯誤檢測
UART控制器提供了四種錯誤檢測機制。
- 奇偶檢驗錯誤。每接收一個字符時,接收器根據UART計算接收數據位的奇偶校驗值,將它與接收到的奇偶檢驗位進行比較。如果值不同,則奇偶校驗錯誤標志位置1,並產生中斷。
- 幀錯誤。當接收器在幀末沒有接收到有效的停止位時,幀錯誤標志位置1,產生中斷。
- 溢出錯誤。當接收到一個字符時,UART控制器檢測RxFIFO是否有空間。如果有則將該字符寫入RxFIFO;如果RxFIFO已滿則等待;如果又檢測到了下一個數據的起始位,且RxFIFO仍然是滿的,那么等待的數據將丟失,同時溢出標志位置1,產生中斷。
- 超時機制。接收器有一個10位的遞減計數器,每當在RxD上收到一個新的起始位或者程序向專門的標志位寫入1重置時,計數器都會重新加載並倒數。當計數器減到0時認定發生超時,相應標志位置1(此時程序中應重置計數器),產生中斷。
上面介紹的4種中斷都不是必須的,我們可以屏蔽這些中斷,也可以禁用超時機制。
I/O模式選擇
下圖中的模式切換( Mode Switch)控制 RxD 和 TxD 的信號連接方式,總共分為四種模式,分別為:正常模式( Normal Mode)、自動回環模式( Automatic Echo Mode)、本地環回模式( Local LoopbackMode)和遠程環回模式( Remote Loopback Mode)。
模式切換的功能示意圖如所示:
從上圖中可以清晰的看出 UART 不同模式下所實現的功能。正常模式是標准的 UART 操作模式;自動回環模式下, RxD 連接至 TxD,控制器可以接收數據,但是不能發送數據;本地環回模式沒有連接 RxD 和TxD 的引腳,用於本地程序的環回測試;遠程環回模式下, RxD 連接至 TxD,但是並沒有和控制器連接,因此控制器在此模式下無法發送數據和接收數據。當然在實際應用中,最常用的就是 UART 的正常模式。在講解完 UART 控制器之后,接下來我們向大家介紹程序中 UART 控制器的設計方法。如果我們只是用串口來打印信息的話,那么可以直接使用 print()或者 xil_printf()函數就可以了,無需在程序中對串口做配置。但是如果我們需要使用 UART 來完成某些特定功能的話,如串口接收中斷,那么就要了解 UART 控制器初始化、 UART 中斷初始化以及 UART 常用的 API 函數等相關內容了。
UART 的啟動順序
UART 的啟動順序如下:
1、 復位控制器(在 PS 系統復位時);
2、 配置 IO 引腳信號。 RxD 和 TxD 可以連接至 MIO 或者 EMIO,只有 EMIO 可以使用串口的流控制。
3、 配置 UART 參考時鍾;
4、 配置控制器功能( UART 控制器初始化);
5、 配置中斷,通過中斷來管理 RxFIFO 和 TxFIFO;
6、 配置串口流控制(可選);
7、 管理發送和接收的數據,可以采用輪詢或中斷驅動處理兩種方式。
配置控制器功能
控制器功能主要配置字符幀、波特率、 FIFO 觸發器等級、 Rx 超時機制,並啟用控制器。重置控制器后必須要配置所有這些參數。步驟如下:
1、 配置 UART 數據幀格式。如: 數據位長度、停止位、校驗方式、 IO 模式等;
2、 設置波特率;
3、 設置 RxFIFO 觸發器等級,可以選擇啟用或禁用該功能;
4、 使能 UART 控制器;
5、 配置接收器的超時機制,可以選擇啟用或禁用該功能。
發送數據
我們可以使用輪詢或者中斷兩種方式控制 TxFIFO 和 RxFIFO 的數據流。這兩個 FIFO 大小均為 64 個字節,因此當 TxFIFO 的空標志有效時,我們可以直接向其寫入 64 個字節,無需檢查 TxFIFO 的狀態。實際上當發送器處於活躍狀態時,可寫入的字節數要超過 64 個字節,因為控制器同時也在移出數據,將其串行化轉移到 TxD 信號上。
采用輪詢方法發送數據的順序如下:
1、 檢查 TxFIFO 是否為空;
2、 向 TxFIFO 寫入數據,可以寫入 64 個字節;
3、 向 TxFIFO 中寫入更多數據。我們可以等待 TxFIFO 為空之后再寫入 64 個字節,即執行第 2 步;也可以檢測 TxFIFO 是否寫滿,即不停的讀取 TFUL 標志和寫單個字節的數據。采用中斷方法發送數據的順序如下:
1、 禁用 TxFIFO 空中斷;
2、 向 TxFIFO 寫數據,可以寫入 64 個字節的數據;
3、 檢測 TxFIFO 是否為滿狀態,不停的讀取 TFUL 標志和寫單個字節的數據;
4、 重復步驟 2 和 3,直到 TxFIFO 已滿;
5、 使能TxFIFO空中斷
6、 等待,直到 TxFIFO 為空,然后從步驟 1 重新開始;
接收數據
采用輪詢方法接收數據的順序如下:
1、 等待,直到 RxFIFO 中的數據數量達到觸發等級;
2、 從 RxFIFO 中讀取數據;
3、 重復步驟 2 直到 FIFO 空;
4、 發生 Rx 超時中斷時將其重置。
采用中斷方法接收數據的順序如下:
1、 使能中斷;
2、 等待,直到 RxFIFO 中的數據數量達到觸發等級或者發生超時;
3、 從 RxFIFO 中讀取數據;
4、 重復步驟 2 和 3,直到 RxFIFO 為空;
5、 清除中斷標志
測試代碼1
下面還是代碼說話吧,還是由於這次沒機會用到,借鑒參考了Xilinx社區博主的代碼,SDK中user_uart.h文件代碼如下:
#ifndef SRC_USER_UART_H_
#define SRC_USER_UART_H_
#include "xparameters.h"
#include "xuartps.h"
#include "xil_printf.h"
#include "sleep.h"
#define UART_DEVICE_ID XPAR_XUARTPS_0_DEVICE_ID
int Uart_Send(XUartPs* Uart_Ps, u8 *sendbuf, int length);
int Uart_Init(XUartPs* Uart_Ps, u16 DeviceId);
#endif /* SRC_USER_UART_H_ */
user_uart.c文件的代碼如下:
#include "user_uart.h"
// UART格式
XUartPsFormat uart_format =
{
9600,
//XUARTPS_DFT_BAUDRATE, //默認波特率 115200
XUARTPS_FORMAT_8_BITS,
XUARTPS_FORMAT_NO_PARITY,
XUARTPS_FORMAT_1_STOP_BIT,
};
//--------------------------------------------------------------
// UART初始化函數
//--------------------------------------------------------------
int Uart_Init(XUartPs* Uart_Ps, u16 DeviceId)
{
int Status;
XUartPs_Config *Config;
/* 初始化UART設備 */
Config = XUartPs_LookupConfig(DeviceId);
if (NULL == Config) {
return XST_FAILURE;
}
Status = XUartPs_CfgInitialize(Uart_Ps, Config, Config->BaseAddress);
if (Status != XST_SUCCESS) {
return XST_FAILURE;
}
/* UART設備自檢 */
Status = XUartPs_SelfTest(Uart_Ps);
if (Status != XST_SUCCESS) {
return XST_FAILURE;
}
/* 設置UART模式與參數 */
XUartPs_SetOperMode(Uart_Ps, XUARTPS_OPER_MODE_NORMAL); //正常模式
XUartPs_SetDataFormat(Uart_Ps, &uart_format); //設置UART格式
return XST_SUCCESS;
}
//--------------------------------------------------------------
// UART數據發送函數
//--------------------------------------------------------------
int Uart_Send(XUartPs* Uart_Ps, u8 *sendbuf, int length)
{
int SentCount = 0;
while (SentCount < length) {
SentCount += XUartPs_Send(Uart_Ps, &sendbuf[SentCount], 1);
}
return SentCount;
}
main函數如下:
#include "user_uart.h"
XUartPs Uart_Ps; /* The instance of the UART Driver */
int main(void)
{
int Status;
u8 sendbuf[] = "Hello World!\r\n";
/* 串口初始化 */
Status = Uart_Init(&Uart_Ps, UART_DEVICE_ID);
if (Status == XST_FAILURE) {
xil_printf("Uartps Failed\r\n");
return XST_FAILURE;
}
while (1)
{
sleep(1);
Uart_Send(&Uart_Ps, sendbuf, 14);
}
return Status;
}
相關API函數
1.UART初始化
對UART設備初始化操作和前面GPIO設備、中斷設備、定時器設備、XADC設備的初始化過程一樣,不再贅述。接着使用XUartPs_SelfTest函數對UART設備自檢。
s32 XUartPs_SelfTest(XUartPs *InstancePtr)
這個函數執行一次本地回環,驗證可以正常發送和接受數據。返回XST_UART_TEST_FAIL表示測試失敗;XST_SUCCESS表示測試成功。我們可以用這個函數檢查硬件工作是否正常。
2.模式配置
初始化函數中還用XUartPs_SetOperMode函數設置了UART的工作模式。工作模式在xuartps.h文件中宏定義。
void XUartPs_SetOperMode(XUartPs *InstancePtr, u8 OperationMode)
四種工作模式的作用請參考前文。下表給出每種模式的宏定義:
宏定義 | 實際值 | 工作模式 |
---|---|---|
XUARTPS_OPER_MODE_NORMAL | 0x00U | 正常模式 |
XUARTPS_OPER_MODE_AUTO_ECHOL | 0x01U | 自動echo模式 |
XUARTPS_OPER_MODE_LOCAL_LOOP | 0x02U | 本地回環模式 |
XUARTPS_OPER_MODE_REMOTE_LOOP | 0x03U | 遠程回環模式 |
3.格式配置
接下來使用XUartPs_SetDataFormat函數設置UART的數據格式,包括波特率、數據位數、停止位數和奇偶校驗。調用此函數時應確保UART沒有收發數據。
s32 XUartPs_SetDataFormat(XUartPs *InstancePtr, XUartPsFormat * FormatPtr)
如果設置成功則返回XST_SUCCESS;如果該波特率在當前參考時鍾頻率下無法實現,則返回XST_UART_BAUD_ERROR表示無法設置波特率;函數的任意一個輸入參數無效時返回XST_INVALI_PARAM。我們絕大多數情況下都會使用“8位數據、1位停止、無奇偶校驗”,因此如果想進一步提高程序效率,可以僅使用XUartPs_SetBaudRate函數來設置波特率。
s32 XUartPs_SetBaudRate(XUartPs *InstancePtr, u32 BaudRate)
使用該函數也會檢查輸入的波特率值是否有效。檢查的依據是xuartps.c中下面這個宏定義,即最大允許的波特率錯誤率:
#define XUARTPS_MAX_BAUD_ERROR_RATE 3U /* max % error allowed */
其實質上是波特率生成器產生的實際波特率與設置的波特率之間的差值。如果不滿足這個條件,便會返回XST_UART_BAUD_ERROR,保持原有波特率不變。
4.數據格式
上面的函數中使用了XuartPsFormat類型的結構體來設置UART格式。該結構體原型如下:
typedef struct {
u32 BaudRate; /**< In bps, ie 1200 */
u32 DataBits; /**< Number of data bits */
u32 Parity; /**< Parity */
u8 StopBits; /**< Number of stop bits */
} XUartPsFormat;
下表總結了與數據格式相關的宏定義,使用時要將其填到結構體變量的對應位置。一般波特率可以寫成數字形式,其余三個成員都要用宏定義的形式。
5.數據發送
程序中使用XUartPs_Send函數發送數據。這個函數是非阻塞的,輪詢模式和中斷驅動模式下都可以使用。它會盡可能地想TxFIFO填充數據,並返回發送的字節數;如果無法填充,會返回0表示發送了0字節,便於用戶處理。
中斷模式下,該函數會發送指定的緩沖區(Buffer)中的內容,中斷處理程序負責將所有數據全部發送完。此時會調用綁定的回調函數,標識發送完成。關於中斷的用法在后面文章中專門介紹。
u32 XUartPs_Send(XUartPs *InstancePtr, u8 *BufferPtr, u32 NumBytes)
第二個參數是指向要發送的數據緩沖區的指針;第三個參數是發送的字節數;返回值標識實際發送的字節數。本例程序中就是利用返回值確保所有數據都依次發送(雖然本例的數量不大,但要學習這個用法)。
這個函數還有個特殊用法,如果將第三個參數設為0,則會停止正在進行的發送操作,並將已經在TxFIFO中的所有數據都發送出去。可以用這個用法實現某些特殊功能。
一般來說,相對中斷用的還是多的
測試代碼2
所以更改串口驅動.h為:
#ifndef SRC_USER_UART_H_
#define SRC_USER_UART_H_
#include "xparameters.h"
#include "xuartps.h"
#include "xil_printf.h"
#include "sleep.h"
#include "xscugic.h"
#define UART_DEVICE_ID XPAR_PS7_UART_1_DEVICE_ID //設備ID
#define UART_INT_IRQ_ID XPAR_XUARTPS_1_INTR //中斷號
#define BUFFER_SIZE 8
int Uart_Send(XUartPs* Uart_Ps, u8 *RecvBuffer, int length);
int Uart_Init(XUartPs* Uart_Ps, u16 DeviceId);
void Uart_Intr_System(XScuGic *Intc, XUartPs *Uart_Ps, u16 UartIntrId);
void Handler(void *CallBackRef);
#endif /* SRC_USER_UART_H_ */
現在串口驅動.c為:
#include "user_uart.h"
static u8 RecvBuffer[BUFFER_SIZE];
u8 *RecvBufferPtr;
volatile u32 TotalRecvCnt;
XUartPsFormat uart_format =
{
//9600,
XUARTPS_DFT_BAUDRATE, //默認波特率 115200
XUARTPS_FORMAT_8_BITS,
XUARTPS_FORMAT_NO_PARITY,
XUARTPS_FORMAT_1_STOP_BIT,
};
//--------------------------------------------------------------
// UART初始化函數
//--------------------------------------------------------------
int Uart_Init(XUartPs* Uart_Ps, u16 DeviceId)
{
int Status;
XUartPs_Config *Config;
/* 數據初始化 */
RecvBufferPtr = RecvBuffer;
TotalRecvCnt = 0;
/* 初始化UART設備 */
Config = XUartPs_LookupConfig(DeviceId);
if (NULL == Config) {
return XST_FAILURE;
}
Status = XUartPs_CfgInitialize(Uart_Ps, Config, Config->BaseAddress);
if (Status != XST_SUCCESS) {
return XST_FAILURE;
}
/* UART設備自檢 */
Status = XUartPs_SelfTest(Uart_Ps);
if (Status != XST_SUCCESS) {
return XST_FAILURE;
}
/* 設置UART模式與參數 */
XUartPs_SetOperMode(Uart_Ps, XUARTPS_OPER_MODE_NORMAL); //正常模式
XUartPs_SetDataFormat(Uart_Ps, &uart_format); //設置UART格式
XUartPs_SetFifoThreshold(Uart_Ps, 8); //設置RxFIFO的中斷觸發等級
return XST_SUCCESS;
}
//--------------------------------------------------------------
// UART中斷系統初始化函數
//--------------------------------------------------------------
void Uart_Intr_System(XScuGic *Intc, XUartPs *Uart_Ps, u16 UartIntrId)
{
XScuGic_Connect(Intc, UartIntrId,
(Xil_ExceptionHandler) Handler,
(void *) Uart_Ps);
XScuGic_Enable(Intc, UartIntrId);
// 設置UART的中斷觸發方式
XUartPs_SetInterruptMask(Uart_Ps, XUARTPS_IXR_RXOVR);
}
//--------------------------------------------------------------
// UART中斷處理函數
//--------------------------------------------------------------
void Handler(void *CallBackRef)
{
XUartPs *UartInstancePtr = (XUartPs *) CallBackRef ;
u32 ReceivedCount = 0 ;
u32 IsrStatus ;
//讀取中斷ID寄存器,判斷觸發的是哪種中斷
IsrStatus = XUartPs_ReadReg(UartInstancePtr->Config.BaseAddress,
XUARTPS_IMR_OFFSET);
IsrStatus &= XUartPs_ReadReg(UartInstancePtr->Config.BaseAddress,
XUARTPS_ISR_OFFSET);
if (IsrStatus & (u32)XUARTPS_IXR_RXOVR) /* 檢查RxFIFO是否觸發 */
{
ReceivedCount = XUartPs_Recv(UartInstancePtr, RecvBufferPtr, (BUFFER_SIZE-TotalRecvCnt)) ;
TotalRecvCnt += ReceivedCount ;
RecvBufferPtr += ReceivedCount ;
/* 清除中斷標志 */
XUartPs_WriteReg(UartInstancePtr->Config.BaseAddress, XUARTPS_ISR_OFFSET, XUARTPS_IXR_RXOVR) ;
}
xil_printf("Enter INTR\r\n");
/* 數據處理 */
if (TotalRecvCnt >= BUFFER_SIZE) {
xil_printf("%s", RecvBuffer);
xil_printf("\r\nI have received %d bytes.\r\n", TotalRecvCnt);
RecvBufferPtr = RecvBuffer;
TotalRecvCnt = 0;
}
}
現在的main函數為:
#include "sys_intr.h"
#include "user_uart.h"
XScuGic Intc; //GIC
XUartPs Uart_Ps; //UART
void System_Init(void)
{
Init_Intr_System(&Intc);
Setup_Intr_Exception(&Intc);
Uart_Intr_System(&Intc, &Uart_Ps, UART_INT_IRQ_ID);
}
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;
}
System_Init(); //中斷初始化
while (1){
sleep(1);
xil_printf("Hello World!\r\n");
}
return Status;
}
不過很遺憾,在這段時間基本沒這么認真地配置過PS端的串口,基本都用來調試打印了。PL端通過UartLite或者UartNs550相對用的較多一點(因為要用的串口實在是太多了,不論是作為串口服務器還是作為各種交互)算了還是直接代碼說話吧
工程代碼
先是頭文件,FIFO部分參考萬能的FIFO篇
#ifndef SRC_UART_DRIVER_H_
#define SRC_UART_DRIVER_H_
#include "xparameters.h"
#include "xuartns550.h"
#include "xil_exception.h"
#ifdef XPAR_INTC_0_DEVICE_ID
#include "xintc.h"
#include <stdio.h>
#else
#include "xscugic.h"
#include "xil_printf.h"
#include "xbasic_types.h"
#include "lib_fifo.h"
#include "Uart_driver.h"
#endif
XUartNs550 UartNs550;
#define FIFO_SIZE 512
FIFO Uart0FIFO; /* **參考萬能的FIFO篇**,非常方便的幫助處理收到的大量數據;但是相應的也會犧牲部分效率在數據不是特別大串口不是特別多的時候建議使用,過多可能完全一樣的復制10個串口就有一個莫名奇妙出bug */
u8 RecvBuffer[12][FIFO_SIZE]; /* Buffer for Receiving Data */
#ifndef TESTAPP_GEN
#ifdef XPAR_INTC_0_DEVICE_ID
#define INTC_DEVICE_ID XPAR_INTC_0_DEVICE_ID
#else
#define INTC_DEVICE_ID XPAR_SCUGIC_SINGLE_DEVICE_ID
#endif /* XPAR_INTC_0_DEVICE_ID */
#endif /* TESTAPP_GEN */
/***************** Type Definitions *********************/
#ifdef XPAR_INTC_0_DEVICE_ID
#define INTC XIntc
#define INTC_HANDLER XIntc_InterruptHandler
#else
#define INTC XScuGic
#define INTC_HANDLER XScuGic_InterruptHandler
#endif /* XPAR_INTC_0_DEVICE_ID */
#ifndef TESTAPP_GEN
INTC IntcInstance0; /* Instance of the Interrupt Controller */
XUartNs550 Uart0Ns550Instance; /* Instance of the UART Device */
//先例化一個吧
#endif
/*
* The following buffers are used in this example to send and receive data
* with the UART.
*/
u8 SendBuffer[FIFO_SIZE]; /* Buffer for Transmitting Data */
/*
* The following counters are used to determine when the entire buffer has
* been sent and received.
*/
#ifndef TESTAPP_GEN
#endif
/************************************/
/***** Function Prototypes ************/
int UartNs550Init(INTC *IntcInstancePtr,
XUartNs550 *UartInstancePtr,
u16 UartDeviceId,
u16 UartIntrId);
void Uart0Ns550IntrHandler(void *CallBackRef, u32 Event, unsigned int EventData);
static int UartNs550SetupIntrSystem(INTC *IntcInstancePtr,
XUartNs550 *UartInstancePtr,
u16 UartIntrId);
static void UartNs550DisableIntrSystem(INTC *IntcInstancePtr, u16 UartIntrId);
/* 還需要多少復制粘貼吧 */
#endif /* SRC_UART_DRIVER_H_ */
再然后是驅動函數了
#include "Uart_driver.h"
u8 SendBuffer[FIFO_SIZE]; /* Buffer for Transmitting Data */
/*
* The following counters are used to determine when the entire buffer has
* been sent and received.
*/
//static volatile int TotalReceivedCount;
int TotalReceivedCount;
static volatile int TotalSentCount;
static volatile int TotalErrorCount;
#ifndef TESTAPP_GEN
#endif
/*****************************************************************************/
/**
*
* This function does a minimal test on the UartNs550 device and driver as a
* design example. The purpose of this function is to illustrate how to use the
* XUartNs550 component.
*
* This function transmits data and expects to receive the same data through the
* UART using the local loopback of the hardware.
*
* This function uses interrupt driver mode of the UART.
*
* @param IntcInstancePtr is a pointer to the instance of the
* Interrupt Controller.
* @param UartInstancePtr is a pointer to the instance of the UART .
* @param UartDeviceId is the device Id and is typically
* XPAR_<UARTNS550_instance>_DEVICE_ID value from xparameters.h.
* @param UartIntrId is the interrupt Id and is typically
* XPAR_<INTC_instance>_<UARTNS550_instance>_IP2INTC_IRPT_INTR
* value from xparameters.h.
*
* @return XST_SUCCESS if successful, otherwise XST_FAILURE.
*
* @note
*
* This function contains an infinite loop such that if interrupts are not
* working it may never return.
*
*******************************************************************************/
int UartNs550Init(INTC *IntcInstancePtr,
XUartNs550 *UartInstancePtr,
u16 UartDeviceId,
u16 UartIntrId)
{
int Status;
u32 Index;
u16 Options;
u32 BadByteCount = 0;
XUartNs550_Handler CB;
/*
* Initialize the UART driver so that it's ready to use.
*/
Status = XUartNs550_Initialize(UartInstancePtr, UartDeviceId);
if (Status != XST_SUCCESS) {
return XST_FAILURE;
}
/*
* Perform a self-test to ensure that the hardware was built correctly.
*/
/*Status = XUartNs550_SelfTest(UartInstancePtr);
if (Status != XST_SUCCESS) {
return XST_FAILURE;
}*/
/*
* Connect the UART to the interrupt subsystem such that interrupts can
* occur. This function is application specific.
*/
Status = UartNs550SetupIntrSystem(IntcInstancePtr,
UartInstancePtr,
UartIntrId);
if (Status != XST_SUCCESS) {
return XST_FAILURE;
}
/*
* Setup the handlers for the UART that will be called from the
* interrupt context when data has been sent and received, specify a
* pointer to the UART driver instance as the callback reference so
* the handlers are able to access the instance data.
*/
switch(UartDeviceId)
{
case XPAR_UARTNS550_0_DEVICE_ID:
CB = Uart0Ns550IntrHandler;
break;
default:
break;
}
XUartNs550_SetHandler(UartInstancePtr, CB,
UartInstancePtr);
/*
* Enable the interrupt of the UART so interrupts will occur, setup
* a local loopback so data that is sent will be received, and keep the
* FIFOs enabled.
*/
Options = XUN_OPTION_DATA_INTR | //得把例程的回環屏蔽XUN_OPTION_LOOPBACK |
XUN_OPTION_FIFOS_ENABLE;
XUartNs550_SetOptions(UartInstancePtr, Options);
/*
* Initialize the send buffer bytes with a pattern to send and the
* the receive buffer bytes to zero to allow the receive data to be
* verified.
*/
// for (Index = 0; Index < TEST_BUFFER_SIZE; Index++) {
// SendBuffer[Index] = Index + 'A';
// RecvBuffer[Index] = 0;
// }
/*
* Start receiving data before sending it since there is a loopback,
* ignoring the number of bytes received as the return value since we
* know it will be zero and we are using interrupt mode.
*/
// XUartNs550_Recv(UartInstancePtr, RecvBuffer, TEST_BUFFER_SIZE);
//多好的現成的接收函數
/*
* Send the buffer using the UART and ignore the number of bytes sent
* as the return value since we are using it in interrupt mode.
*/
// XUartNs550_Send(UartInstancePtr, SendBuffer, TEST_BUFFER_SIZE);
//喲呵還有發送呢齊活了
/*
* Wait for the entire buffer to be received, letting the interrupt
* processing work in the background, this function may get locked
* up in this loop if the interrupts are not working correctly.
*/
// while ((TotalReceivedCount != TEST_BUFFER_SIZE) ||
// (TotalSentCount != TEST_BUFFER_SIZE)) {
// }
//
// /*
// * Verify the entire receive buffer was successfully received.
// */
// for (Index = 0; Index < TEST_BUFFER_SIZE; Index++) {
// if (RecvBuffer[Index] != SendBuffer[Index]) {
// BadByteCount++;
// }
// }
/*
* Disable the UartNs550 interrupt.
*/
//UartNs550DisableIntrSystem(IntcInstancePtr, UartIntrId);
/*
* If any bytes were not correct, return an error.
*/
// if (BadByteCount != 0) {
// return XST_FAILURE;
// }
//
// /* Clear the counters */
// TotalErrorCount = 0;
// TotalReceivedCount = 0;
// TotalSentCount = 0;
//
return XST_SUCCESS;
}
/*****************************************************************************/
/**
*
* This function is the handler which performs processing to handle data events
* from the UartNs550. It is called from an interrupt context such that the
* amount of processing performed should be minimized.
*
* This handler provides an example of how to handle data for the UART and
* is application specific.
*
* @param CallBackRef contains a callback reference from the driver,
* in thiscase it is the instance pointer for the UART driver.
* @param Event contains the specific kind of event that has occurred.
* @param EventData contains the number of bytes sent or received for sent
* and receive events.
*
* @return None.
*
* @note None.
*
*******************************************************************************/
void Uart0Ns550IntrHandler(void *CallBackRef, u32 Event, unsigned int EventData)
{
u8 Errors;
XUartNs550 *UartNs550Ptr = (XUartNs550 *)CallBackRef;
u8 recData[256];
/*
* All of the data has been sent.
*/
if (Event == XUN_EVENT_SENT_DATA) {
TotalSentCount = EventData;
}
/*
* All of the data has been received.
*/
if (Event == XUN_EVENT_RECV_DATA)
{
TotalReceivedCount = EventData;
//xil_printf("d-\r\n");
///XUartNs550_Recv(&Uart0Ns550Instance, recData, 10);
//FifoWrite(&Uart0FIFO,recData,10);
}
/*
* Data was received, but not the expected number of bytes, a
* timeout just indicates the data stopped for 4 character times.
*/
if (Event == XUN_EVENT_RECV_TIMEOUT) {
TotalReceivedCount = EventData;
//xil_printf("c-\r\n");
}
/*
* Data was received with an error, keep the data but determine
* what kind of errors occurred.
*/
if (Event == XUN_EVENT_RECV_ERROR) {
TotalReceivedCount = EventData;
TotalErrorCount++;
Errors = XUartNs550_GetLastErrors(UartNs550Ptr);
//xil_printf("c1-\r\n");
}
//xil_printf("TotalReceivedCount=%d\r\n",TotalReceivedCount);
//xil_printf("TotalErrorCount=%d\r\n",TotalErrorCount);
}
/*****************************************************************************/
/**
*
* This function is the handler which performs processing to handle data events
* from the UartNs550. It is called from an interrupt context such that the
* amount of processing performed should be minimized.
*
* This handler provides an example of how to handle data for the UART and
* is application specific.
*
* @param CallBackRef contains a callback reference from the driver,
* in thiscase it is the instance pointer for the UART driver.
* @param Event contains the specific kind of event that has occurred.
* @param EventData contains the number of bytes sent or received for sent
* and receive events.
*
* @return None.
*
* @note None.
*
*******************************************************************************/
/******************************************************************************/
/**
*
* This function setups the interrupt system such that interrupts can occur
* for the UART. This function is application specific since the actual
* system may or may not have an interrupt controller. The UART could be
* directly connected to a processor without an interrupt controller. The
* user should modify this function to fit the application.
*
* @param IntcInstancePtr is a pointer to the instance of the Interrupt
* Controller.
* @param UartInstancePtr is a pointer to the instance of the UART.
* @param UartIntrId is the interrupt Id and is typically
* XPAR_<INTC_instance>_<UARTNS550_instance>_VEC_ID value from
* xparameters.h.
*
* @return XST_SUCCESS if successful, otherwise XST_FAILURE.
*
* @note None.
*
*******************************************************************************/
static int UartNs550SetupIntrSystem(INTC *IntcInstancePtr,
XUartNs550 *UartInstancePtr,
u16 UartIntrId)
{
int Status;
#else
#ifndef TESTAPP_GEN
XScuGic_Config *IntcConfig;
/*
* Initialize the interrupt controller driver so that it is ready to
* use.
*/
IntcConfig = XScuGic_LookupConfig(INTC_DEVICE_ID);
if (NULL == IntcConfig) {
return XST_FAILURE;
}
Status = XScuGic_CfgInitialize(IntcInstancePtr, IntcConfig,
IntcConfig->CpuBaseAddress);
if (Status != XST_SUCCESS) {
return XST_FAILURE;
}
#endif /* TESTAPP_GEN */
XScuGic_SetPriorityTriggerType(IntcInstancePtr, UartIntrId,
0xA0, 0x3);
/*
* Connect the interrupt handler that will be called when an
* interrupt occurs for the device.
*/
Status = XScuGic_Connect(IntcInstancePtr, UartIntrId,
(Xil_ExceptionHandler)XUartNs550_InterruptHandler,
UartInstancePtr);
if (Status != XST_SUCCESS) {
return Status;
}
/*
* Enable the interrupt for the Timer device.
*/
XScuGic_Enable(IntcInstancePtr, UartIntrId);
#endif /* XPAR_INTC_0_DEVICE_ID */
#ifndef TESTAPP_GEN
/*
* Initialize the exception table.
*/
Xil_ExceptionInit();
/*
* Register the interrupt controller handler with the exception table.
*/
Xil_ExceptionRegisterHandler(XIL_EXCEPTION_ID_INT,
(Xil_ExceptionHandler)INTC_HANDLER,
IntcInstancePtr);
/*
* Enable exceptions.
*/
Xil_ExceptionEnable();
#endif /* TESTAPP_GEN */
return XST_SUCCESS;
}
static void UartNs550DisableIntrSystem(INTC *IntcInstancePtr, u16 UartIntrId)
{
/*
* Disconnect and disable the interrupt for the UartNs550 device.
*/
#ifdef XPAR_INTC_0_DEVICE_ID
XIntc_Disconnect(IntcInstancePtr, UartIntrId);
#else
XScuGic_Disable(IntcInstancePtr, UartIntrId);
XScuGic_Disconnect(IntcInstancePtr, UartIntrId);
#endif
}
下面寫主函數,嗯便於工程移植直接寫個demo直接調用吧
void Uart_demo()
{
int i,Status;
u8 sendbuf[] = "HelloWorld";
u8 recvbuf[FIFO_SIZE] = {};
//多余的自己復制粘貼吧
FifoInit(&Uart0FIFO,&RecvBuffer[0],FIFO_SIZE);
//多余的自己復制粘貼吧 /*
* Run the UartNs550 Interrupt example.
*/
Status = UartNs550Init(&IntcInstance0,
&Uart0Ns550Instance,
XPAR_UARTNS550_0_DEVICE_ID,
XPAR_FABRIC_AXI_UART16550_0_IP2INTC_IRPT_INTR);
XUartNs550_SetBaudRate(&Uart0Ns550Instance,115200); //多余的自己復制粘貼吧,這可是個好東西,可以單獨設置波特率,不然就是默認的了,默認的也可以直接填
if (Status != XST_SUCCESS)
{
xil_printf("Uartns550 0 interrupt Example Failed\r\n");
return XST_FAILURE;
}
//多余的自己復制粘貼吧
//XUartNs550_Send(&UartNs550Instance, sendbuf, 14);
//XUartNs550_Recv(&UartNs550Instance, recvbuf, TEST_BUFFER_SIZE);
//XUartNs550_Recv(&UartNs550Instance, recvbuf, 8);
//XUartNs550_Recv(&Uart0Ns550Instance, recvbuf, 1);
XUartNs550_Recv(&Uart0Ns550Instance, recvbuf, 256);
//多余的自己復制粘貼吧
while(1)
{
sleep(1);
//FifoRead(&Uart0FIFO,recvbuf,40);
//因為不知名bug原因暫時屏蔽掉,多余的自己復制粘貼吧 //XUartNs550_ReadReg(XPAR_UARTNS550_0_BASEADDR,0x1000);
XUartNs550_Send(&Uart0Ns550Instance, sendbuf, 10);
//多余的自己復制粘貼吧
if(TotalReceivedCount > 0)
{
XUartNs550_Recv(&Uart0Ns550Instance, recvbuf, 256);
//多余的自己復制粘貼吧 //FifoWrite(&Uart0FIFO,recvbuf,256);
TotalReceivedCount = 0;
}
for(int i=0; i < 20; i++)
{xil_printf("%c ",recvbuf[i]);}
xil_printf("\r\n");
//多余的自己復制粘貼吧
xil_printf("\n\r");
for(int i=0; i < 20; i++)
// {xil_printf("%c ",recvbuf11[i]);}
// xil_printf("\n\r");
memset(recvbuf,0,256);
//多余的自己復制粘貼吧
}
}
先寫這么多了,目前只了解到這些,之后串口服務器篇在補充一些Linux移植后的代碼吧
學習zynq時間較短,認知有限,代碼也比較簡陋。如有錯誤歡迎批評指正。影響閱讀的圖片水印也已去掉歡迎大家收藏
參考:芯片手冊 原子開發手冊 CSDN Xilinx社區等網絡資源