學(xué)習(xí)內(nèi)容
本文主要介紹關(guān)于ZYNQ芯片的串口中斷功能,并編寫相關(guān)讀寫測試代碼,完成串口中斷的讀寫測試。
開發(fā)環(huán)境
vivado 18.3&SDK,PYNQ-Z2開發(fā)板。
UART控制器
簡介
UART控制器是一個全雙工異步接收和發(fā)送,支持可編程波特率和I/O信號格式。 該控制器可實現(xiàn)奇偶校驗自動生成和多主檢測模式。UART操作由配置和模式寄存器控制。
FIFO、調(diào)制解調(diào)器信號和其他控制器功能的狀態(tài)是使用狀態(tài)、中斷狀態(tài)和調(diào)制解調(diào)器狀態(tài)寄存器讀取的。UART控制器有獨立的RX和TX數(shù)據(jù)路徑。每個路徑包括一個64字節(jié)的FIFO。 控制器TX和RX FIFO中的數(shù)據(jù)進行串行化和反串行化,并包括一個模式開關(guān),以支持RXD和TXD信號的各種環(huán)回配置。(RXD和TXD使用模式:正常模式、各種環(huán)回診斷測試模式)
FIFO中斷狀態(tài)位支持輪詢或中斷驅(qū)動的處理程序。 軟件使用RX和TX數(shù)據(jù)端口寄存器讀取和寫入數(shù)據(jù)字節(jié)。當(dāng)在類似于調(diào)制解調(diào)器的應(yīng)用程序中使用UART時,調(diào)制解調(diào)器控制模塊檢測并生成調(diào)制解調(diào)器握手信號,并且還根據(jù)握手協(xié)議控制接收和發(fā)送器路徑。(調(diào)制解調(diào)器控制信號:CTS, RTS, DSR, DTR, RI和DCD只有在EMIO接口可用)
系統(tǒng)框圖
UART控制器的系統(tǒng)框圖如下圖所示:
在圖中,SLCR寄存器(系統(tǒng)級控制寄存器(SLCR)由用于控制PS行為的各種寄存器組成。這些寄存器可以通過中央互連使用加載和存儲指令訪問。)包括控制位用于UART時鐘,復(fù)位和MIO-EMIO信號映射。軟件可以使用APB 32位的從接口訪問UART控制器寄存器。每個控制器的IRQ(中斷號為59,82)連接到PS中斷控制器,并連接到PL。UART控制器由參考時鐘( UART REF_CLK)驅(qū)動,同時控制器也需要連接APB 總線時鐘( CPU_1x clock), UART REF_CLK 和 CPU_1x clock 都是來自于 PS 時鐘子系統(tǒng)。
內(nèi)部框圖
UART控制器內(nèi)部框圖如下圖所示。
由上圖可知,UART控制器使用PS AXI interconnect 進行數(shù)據(jù)交互,通過APB Slave接口來接收PS端口的配置信息和一些數(shù)據(jù)信息。假設(shè)要發(fā)送數(shù)據(jù),則系統(tǒng)先將發(fā)生的字符串緩存到TxFIFO下,然后經(jīng)Transmitter模塊實現(xiàn)并轉(zhuǎn)串,如果工作在正常模式下,則數(shù)據(jù)之間接到MIO/EMIO引腳上,正常向接收設(shè)備發(fā)送;假設(shè)要接收其他設(shè)備傳來的串口信息,則首先通過串口接收引腳接收串行數(shù)據(jù),然后經(jīng)過receiver模塊實現(xiàn)串轉(zhuǎn)并,轉(zhuǎn)換過后存入到RxFIFO下,經(jīng)過APB從接口傳輸?shù)絇S端,即可對接收數(shù)據(jù)進行處理。
通過控制狀態(tài)寄存器可以對UART控制器進行控制,而這些引腳只能連接到EMIO。UART控制器內(nèi)部包括一個中斷模塊,所以可以和其他模塊一樣正常接收到來自系統(tǒng)的中斷信號。對于UART的參考時鐘在控制器內(nèi)部首先進行了一個八分頻,接著再產(chǎn)生波特率時鐘。
Transmit FIFO
Transmit FIFO (Tx FIFO)存儲由APB從接口寫入的數(shù)據(jù),發(fā)送模塊收到FIFO中的數(shù)據(jù)后刪除FIFO的數(shù)據(jù)然后進行串并轉(zhuǎn)換,并裝入其移位寄存器。TxFIFO的最大數(shù)據(jù)寬度為8位。數(shù)據(jù)通過寫入TxFIFO寄存器加載到TxFIFO。
當(dāng)數(shù)據(jù)加載到TxFIFO時,TxFIFO空標(biāo)志將被清除并保持在這個Low狀態(tài),直到TxFIFO中的最后一個字符被刪除并加載到發(fā)送器移位寄存器中。TxFIFO滿中斷狀態(tài)(TFULL)表明TxFIFO已經(jīng)完全寫滿了,并且阻止數(shù)據(jù)被寫入到TxFIFO中。如果對TxFIFO執(zhí)行另一個APB寫入操作,則觸發(fā)溢出,寫入數(shù)據(jù)不會加載到TxFIFO中。
發(fā)送 FIFO接近滿標(biāo)志(TNFULL)表明在FIFO中沒有足夠的空間來進行一次程序大小的寫入,這是由模式寄存器的WSIZE位控制的。TxFIFO接近滿標(biāo)志(TNFULL)表示TxFIFO中只有字節(jié)空閑。
可以在TxFIFO填充級別上設(shè)置一個閾值觸發(fā)器(TTRIG)。發(fā)射器觸發(fā)寄存器可以用來設(shè)置這個值,這樣當(dāng)TxFIFO填充深度達到設(shè)定的閾值時觸發(fā)可以設(shè)置。
Receiver FIFO
Receiver FIFO和Transmit FIFO類似。RxFIFO存儲由接收器串行移位寄存器接收的數(shù)據(jù)。RxFIFO的最大數(shù)據(jù)寬度是8位。當(dāng)數(shù)據(jù)加載到RxFIFO時,RxFIFO空標(biāo)志被清除,并且這種狀態(tài)保持為低,直到RxFIFO中的所有數(shù)據(jù)通過APB接口傳輸完畢??諛?biāo)志重新置位為高,如果接著讀FIFO的話,則會從空的RxFIFO讀取返回0。
RxFIFO滿狀態(tài)(Chnl_int_sts_reg0 [RFUL]和Channel_sts_reg0 [RFUL]位)表明RxFIFO滿了,阻止數(shù)據(jù)被加載到RxFIFO。同時也可以在RxFIFO上設(shè)置一個閾值觸發(fā)器(RTRIG)。接收器觸發(fā)級別寄存器(Rcvr_FIFO_trigger_level0)可以用來設(shè)置這個值,取值范圍是1 ~ 63。
I / O模式切換
這里的模式切換即為內(nèi)部框圖中的 Mode Switch 模塊,如下圖所示。
該模式由mode_reg0 [CHMODE]寄存器設(shè)置控制,總共分為四種模式,分別 為:正常模式( Normal Mode)、自動回音模式( Automatic Echo Mode)、本地環(huán)回模式( Local Loopback Mode)和遠程環(huán)回模式( Remote Loopback Mode)。
正常模式( Normal Mode) :用于標(biāo)準(zhǔn)UART操作,就是發(fā)送接收功能。
自動回音模式( Automatic Echo Mode) :Automatic Echo Mode模式在RxD上接收數(shù)據(jù),模式開關(guān)將數(shù)據(jù)連接到接收端和UARTx_TxD。而PS的TXD端口的數(shù)據(jù)無法正常發(fā)出。
本地環(huán)回模式( Local Loopback Mode) :本地環(huán)回模式不連接到RxD或TxD引腳,而是直接把PS發(fā)送的數(shù)據(jù)傳輸?shù)絇S的接收端。
遠程環(huán)回模式( Remote Loopback Mode) :遠程環(huán)回模式將RxD信號連接到TxD信號。在這種模式下,控制器不能在TxD上發(fā)送任何內(nèi)容,也不能在RxD上接收任何內(nèi)容。
UART啟動順序
UART 的啟動順序如下:
- 復(fù)位UART控制器,在PS進行系統(tǒng)復(fù)位時進行控制器的復(fù)位。
- 配置 IO 引腳信號。
- 配置 UART 參考時鐘(可以保護默認)。
- 配置控制器功能( UART 控制器初始化)。
- 配置中斷,通過中斷來管理 RxFIFO 和 TxFIFO。
- 配置串口模式控制(可選)。
- 管理發(fā)送和接收的數(shù)據(jù),采用輪詢或中斷驅(qū)動處理兩種方式。
配置控制器功能步驟
在UART控制器中,控制器可以配置字符幀、波特率、FIFO觸發(fā)級別、Rx超時機制,并使能控制器。所有步驟都必須在復(fù)位之后。 配置控制器的功能步驟如下:
- 配置 UART 數(shù)據(jù)幀格式。 數(shù)據(jù)位長度、停止位、校驗方式、 IO 模式等。
- 設(shè)置波特率。
- 設(shè)置 RxFIFO 觸發(fā)器等級,可以選擇啟用或禁用該功能。
- 使能 UART 控制器。
- 配置接收器的超時機制,可以選擇啟用或禁用該功能。
發(fā)送數(shù)據(jù)步驟
編寫軟件程序時,可以通過使用輪訓(xùn)和中斷的方式控制RxFIFO和TxFIFO中的數(shù)據(jù)流。
使用輪詢方式發(fā)送數(shù)據(jù)順序
- 檢查TxFIFO是否為空。直到uart.Channel sts rego[TEMPTY] =1,執(zhí)行后續(xù)步驟。
- 用數(shù)據(jù)填充TxFIFO。向uart.TX_RX_FIFO0寄存器寫入64字節(jié)的數(shù)據(jù)。
- 向TxFIFO寫入數(shù)據(jù)。有兩種方法:方法一: 可以等待 TxFIFO 為空之后再寫入 64 個字節(jié),即執(zhí)行第 2 步;方法一: 可以檢測 TxFIFO 是否寫滿,即不停的讀取 TFUL 標(biāo)志和寫單個字節(jié)的數(shù)據(jù)。
使用中斷方法發(fā)送數(shù)據(jù)的順序
- 禁用 TxFIFO 空狀態(tài)中斷。
- 寫入數(shù)據(jù)到TxFIFO中。
- 檢測TxFIFO是否還有足夠的空間容納數(shù)據(jù)。
- 重復(fù)2和3操作。
- 使能中斷。
- 等待TxFIFO為空,然后從步驟 1 重新開始。
接收數(shù)據(jù)步驟
使用輪詢方式發(fā)送數(shù)據(jù)順序
- 等待RxFIFO被填滿到觸發(fā)器級別。
- 從RxFIFO讀取數(shù)據(jù)。
- 重復(fù)步驟2,直到FIFO為空。
- 設(shè)置Rx超時中斷狀態(tài)位時清除。
使用中斷方法發(fā)送數(shù)據(jù)順序
- 使能中斷。
- 等待RxFIFO被填滿到觸發(fā)級別或Rx超時。
- 從RxFIFO讀取數(shù)據(jù)。
- 重復(fù)步驟2和3,直到FIFO為空。
- 如果設(shè)置了中斷狀態(tài)位,則清除中斷狀態(tài)位。
系統(tǒng)框圖
這里僅僅使用了UART部分,所以可以利用前文的helloworld工程,不需要進行特殊的配置。通過串口的中斷功能把發(fā)送的數(shù)據(jù)再接收到PS端進行一個回環(huán)顯示。
硬件平臺搭建
新建工程,創(chuàng)建 block design。添加ZYNQ7 ip,根據(jù)本次工程需要對IP進行配置。勾選本次工程使用的uart資源
將ZYNQ無用資源進行取消勾選:
硬件系統(tǒng)構(gòu)建完成如下:
然后我們進行g(shù)enerate output product 然后生成HDL封裝。這里只用到了UART,是MIO引腳,所以不需要進行管腳分配。點擊導(dǎo)出硬件資源(可以不包含bit流文件,因為只用到了PS資源),接著launch SDK。
SDK軟件部分
打開SDK后,新建application project。 在system.mss中可以打開相關(guān)參考文檔輔助設(shè)計。
這里使用串口中斷可以參考賽靈思提供的例程代碼進行修改設(shè)計:
這里導(dǎo)入uart_intr_example例程模板在main.c中輸入以下代碼:
#include "xparameters.h"
#include "stdio.h"
#include "xuartps.h"
#include "xuartps_hw.h"
#include "xscugic.h"
#define UART_0_DEVICE_ID XPAR_PS7_UART_0_DEVICE_ID
#define INTR_DEVICE_ID XPAR_SCUGIC_SINGLE_DEVICE_ID
#define UART_INT_IRQ_ID XPAR_XUARTPS_0_INTR
XUartPs Uart_Inst;
XScuGic ScuGic_Inst;
int uart_init();
void intr_init(XScuGic *intr, XUartPs *uart);
void UartIntr_Handler(void *call_back_ref);
int main(){
//uart初試化函數(shù)
uart_init();
xil_printf("intr\n");
//中斷初始化
intr_init(&ScuGic_Inst,&Uart_Inst);
while(1);
return0;
}
//uart初始化
int uart_init(){
XUartPs_Config *UartPs_Cfg;
int Status;
//查找配置信息
UartPs_Cfg= XUartPs_LookupConfig(UART_0_DEVICE_ID);
//對uart控制器進行初始化
XUartPs_CfgInitialize(&Uart_Inst, UartPs_Cfg, UartPs_Cfg->BaseAddress);
//檢測硬件搭建是否正確
Status = XUartPs_SelfTest(&Uart_Inst);
if (Status != XST_SUCCESS) {
return XST_FAILURE;
}
//設(shè)置波特率
XUartPs_SetBaudRate(&Uart_Inst,115200);
//設(shè)置RXFIFO觸發(fā)閾值
XUartPs_SetFifoThreshold(&Uart_Inst,1);
//設(shè)置操作模式
XUartPs_SetOperMode(&Uart_Inst, XUARTPS_OPER_MODE_NORMAL);
return XST_SUCCESS;
}
//中斷初始化
void intr_init(XScuGic *intr, XUartPs *uart){
XScuGic_Config *IntcConfig;
//中斷控制器初始化
IntcConfig = XScuGic_LookupConfig(INTR_DEVICE_ID);
XScuGic_CfgInitialize(intr,IntcConfig,IntcConfig->CpuBaseAddress);
Xil_ExceptionInit();
Xil_ExceptionRegisterHandler(XIL_EXCEPTION_ID_INT,
(Xil_ExceptionHandler) XScuGic_InterruptHandler,
(void *)intr);
Xil_ExceptionEnable();
//為中斷設(shè)置中斷處理函數(shù)
XScuGic_Connect(intr, UART_INT_IRQ_ID,
(Xil_ExceptionHandler) UartIntr_Handler,
(void *) uart);
//設(shè)置觸發(fā)類型
XUartPs_SetInterruptMask(uart, XUARTPS_IXR_RXOVR);
//使能中斷
XScuGic_Enable(intr, UART_INT_IRQ_ID);
}
//中斷處理函數(shù)
void UartIntr_Handler(void *call_back_ref){
XUartPs *uartinst =(XUartPs *)call_back_ref;
u32 read_data = 0;
u32 intr_status;
//讀取中斷ID寄存器
intr_status = XUartPs_ReadReg(uartinst->Config.BaseAddress,
XUARTPS_IMR_OFFSET);//讀取掩碼
intr_status &= XUartPs_ReadReg(uartinst->Config.BaseAddress,
XUARTPS_ISR_OFFSET);//讀取狀態(tài)
if(intr_status & (u32)XUARTPS_IXR_RXOVR){
read_data = XUartPs_RecvByte(XPAR_PS7_UART_0_BASEADDR);//接收發(fā)送的字節(jié)
XUartPs_WriteReg(uartinst->Config.BaseAddress,XUARTPS_ISR_OFFSET,
XUARTPS_IXR_RXOVR);//清除中斷狀態(tài)
}
//設(shè)置發(fā)送
XUartPs_SendByte(XPAR_PS7_UART_0_BASEADDR,read_data);
}
部分代碼講解
在整體的代碼設(shè)計中,代碼思路如下:
- 初始化UART控制器
- 初始化UART中斷
- 編寫中斷服務(wù)函數(shù)
對于初始化UART部分,在#include "xuartps.h"
頭文件中可以找到很多配置的函數(shù),調(diào)用即可對uart的波特率,中斷觸發(fā)閾值等參數(shù)進行設(shè)置。 對于中斷的配置可以類比GPIO的中斷配置函數(shù),先在初始化SGIC,然后進行異常初始化,完成注冊異常并使能,接著需要連接SGIC和UART,最后設(shè)置中斷類型并使能完成中斷整體操作配置。 在中斷服務(wù)函數(shù)中,實現(xiàn)的功能為回環(huán)讀寫,所以要在中斷函數(shù)中進行檢測中斷類型,當(dāng)進入相應(yīng)的中斷時進行數(shù)據(jù)的讀取,讀取到上位機的發(fā)送數(shù)據(jù),然后清除中斷標(biāo)志,最后把讀到的數(shù)據(jù)進行發(fā)送。
Reference
- 正點原子視頻教程
- UG585