S02_CH09_UART串口中斷實驗
本章的UART中斷將在之前PL_PS中斷和定時器中斷上推導出來,因此本章有點難度,如果前兩章還不是很熟悉的話,需要返回到前面兩章把這兩章的內容再次消化一下,再來學習本章的內容。本章的硬件工程可以直接使用定時器中斷的硬件工程,因此此次試驗就直接到SDK軟件部分。
9.1 加載到SDK
Step1:打開定時器中斷的工程。
Step2:導出硬件。
Step3:新建一個空SDK工程,並添加一個main.c的文件。
Step4:在main.c文件中添加以下程序,按Ctrl+S保存后自動開始編譯。
/* * main.c * * Created on: 2016年6月26日 * Author: Administrator */ #include <stdio.h> #include "xadcps.h" #include "xil_types.h" #include "Xscugic.h" #include "Xil_exception.h" #include "xuartps.h" //timer info #define UART_DEVICE_ID XPAR_PS7_UART_1_DEVICE_ID #define INTC_DEVICE_ID XPAR_SCUGIC_SINGLE_DEVICE_ID #define UART_IRPT_INTR XPAR_XUARTPS_1_INTR static XScuGic Intc; //GIC static XUartPs Uart;//uart static void UartIntrHandler(void *CallBackRef) { XUartPs *InstancePtr = (XUartPs *) CallBackRef; u32 IsrStatus; u32 ReceivedCount=0; u32 CsrRegister; /* * Read the interrupt ID register to determine which * interrupt is active */ IsrStatus = XUartPs_ReadReg(InstancePtr->Config.BaseAddress, XUARTPS_IMR_OFFSET);//e0001000+10=regaddr=e0001010 IsrStatus &= XUartPs_ReadReg(InstancePtr->Config.BaseAddress, XUARTPS_ISR_OFFSET);//e0001000+14=regaddr=e0001014 /* Dispatch an appropriate handler. */ if((IsrStatus & ((u32)XUARTPS_IXR_RXOVR | (u32)XUARTPS_IXR_RXEMPTY | (u32)XUARTPS_IXR_RXFULL)) != (u32)0) { CsrRegister = XUartPs_ReadReg(InstancePtr->Config.BaseAddress,//判斷FIFO觸發標准位 XUARTPS_SR_OFFSET);//e0001000+2c=regaddr=e000102c while((CsrRegister & XUARTPS_SR_RXEMPTY)== (u32)0){//讀取FIFO中所有數據 //InstancePtr->ReceiveBuffer.NextBytePtr[ReceivedCount] =//每次循環讀取1byte ; XUartPs_WriteReg(InstancePtr->Config.BaseAddress,//每次循環發送讀取到的數據 XUARTPS_FIFO_OFFSET, XUartPs_ReadReg(InstancePtr->Config. BaseAddress, XUARTPS_FIFO_OFFSET)); ReceivedCount++;//計數 CsrRegister = XUartPs_ReadReg(InstancePtr->Config.BaseAddress, XUARTPS_SR_OFFSET); } } printf("this time ReceivedCount=%d\r\n",ReceivedCount); XUartPs_WriteReg(InstancePtr->Config.BaseAddress, XUARTPS_ISR_OFFSET, IsrStatus); } void SetupInterruptSystem(XScuGic *GicInstancePtr, XUartPs *UartInstancePtr, u16 UartIntrId) { XScuGic_Config *IntcConfig; //GIC config Xil_ExceptionInit(); //initialise the GIC IntcConfig = XScuGic_LookupConfig(INTC_DEVICE_ID); XScuGic_CfgInitialize(GicInstancePtr, IntcConfig, IntcConfig->CpuBaseAddress); Xil_ExceptionRegisterHandler(XIL_EXCEPTION_ID_INT, (Xil_ExceptionHandler)XScuGic_InterruptHandler,//connect to the hardware GicInstancePtr); Xil_ExceptionEnable(); XScuGic_Connect(GicInstancePtr, UartIntrId, (Xil_InterruptHandler)UartIntrHandler,//set up the timer interrupt (void *)UartInstancePtr); XScuGic_Enable(GicInstancePtr, UartIntrId);//enable the interrupt for the Timer at GIC XUartPs_SetInterruptMask(UartInstancePtr, XUARTPS_IXR_RXOVR/* | XUARTPS_IXR_TXEMPTY | XUARTPS_IXR_TNFUL*/ ); // XUartPs_EnableUart(UartInstancePtr);//enable interrupt on the timer Xil_ExceptionEnableMask(XIL_EXCEPTION_IRQ); //Enable interrupts in the Processor. } int main() { XUartPs_Config *UartConfigPtr; //timer config // printf("------------START-------------\n"); UartConfigPtr = XUartPs_LookupConfig(UART_DEVICE_ID); XUartPs_CfgInitialize(&Uart,UartConfigPtr,UartConfigPtr->BaseAddress); //set up the interrupts SetupInterruptSystem(&Intc,&Uart,UART_IRPT_INTR); while(1); return 0; } |
Step4:右擊工程,選擇Debug as ->Debug configuration。
Step5:選中system Debugger,雙擊創建一個系統調試。
Step6:設置系統調試。
打開系統自帶的窗口調試助手,點擊運行按鈕開始運行程序。
系統運行結果如下圖所示:
9.2 程序分析
本章的程序與之前兩章的程序都大同小異,一些函數都在我們之前兩章中看到和介紹過。
首先我們先介紹下面三個宏定義。
第一個是我們的UART的設備ID,第二個是我們中斷的設備ID,第三個是UART的中斷號。
把鼠標停留在UART的中斷號上,按下F3跟蹤它,經過兩次跟蹤后,得到UART的中斷號如下圖所示:
我們可以在ug585中查看一下中斷號82是否是串口中斷。
可以看到,確實是串口中斷,高電平觸發。
再來看看main函數中的內容。首先依然是通過查找配置程序來獲取串口的硬件配置。我們跟蹤這個程序,看看他獲取的配置是什么。
這個程序還是從一個配置表數組中查找的配置文件,繼續往下剝離,看一看這個數組中的內容。
可以看到,這個數組里存放的是UART的設備ID,UART的基地址,時鍾頻率和一個不知道什么作用的對象。后兩個參數是我們沒用到的,因此就略過了。前兩個都是我們在硬件工程中添加了中斷后,系統自動生成的。
接下來還是一個熟悉的函數,對UART進行了初始化。可以看到這個函數的第一個參數指向了定義的UART指針,我們就跟蹤一下這個指針。
我們發現它指向了一個結構體,那么我們繼續跟蹤看看結構體中內容。
這個結構體中的內容比較多,第一個對象是我們UART硬件的一些配置,它指向的是一個結構體。那么就來看看這個結構體吧。
可以看到,這些就是剛才我們查找配置程序獲取到的硬件參數。
回到XUartPs結構體的分析。第二個對象是輸入時鍾頻率,第三個是設備是否初始化並准備好,第四個是波特率,第五個是兩個buffer,一個發送的一個接收的。挑選一個參看一下。
接着第七個是一個Hander,第八個是一個回掉函數,最后一個是platform具體是什么意思不得而知。
回到初始化程序。我們來看看這個函數與之前有什么不同了。
一開始是一長串的初始化,如下圖所示:
接下來的這個函數是一個用於判斷芯片類型的函數。
接下來,程序將Instance(也就是我們的UART硬件)的標志設置為XIL_COMPONENT_IS_READY,表明此時UART已經可以使用了。
接下來,程序將UART的波特率設置為了115200。
接下來的這一句是讀取UART的模式寄存器。
我們可以來看看讀取的什么內容,把鼠標停放在這個函數的上方,看到函數顯示出了這個函數的原函數。
與我們定時器實驗中講到的讀寫寄存器的函數差不多,第一個參數是UART的基地址,這在我們一開始的分析中就提到過,我們反回去看看UART的基地址是多少。
可以知道,此處的基地址為0xE000100,直接計算:E0001000+0x0004= E0001004。打開ug585查看下這個寄存器的介紹。
可以看到,這是一個UART的模式寄存器,通過這個寄存器可以設置串口的數據位寬,有無停止位和奇偶校驗位等信息。
再來看看下一句程序。這句是對剛才讀出的寄存器的一個運算。
首先得到方框中這三個參數的值。這里我們已經查看程序得知這三個值分別為:6,A0,38。然后進行運算:ModeRegister=E0001004 & (~(6|A0|38))=E0001004 & 11 =0。
接下來的這一句也是一個運算,不多講,直接運算。ModeRegister=0|(0|0|20)=20。
這段程序就是一個寫寄存器的功能了。看看這個函數的原函數。
由此得出,這個函數讀寫的地址為剛才模式寄存器的地址,寫入的數據就是運算得出的20h。參照剛才ug585里的模式寄存器說明,顯而易見,經過這段程序之后,把UART設置為了8個數據位,1個停止位和無奇偶校驗位的模式。
接下來的還有3個寫寄存器的程序,分析方法與剛才的一致。這里就只給出它們實現的功能。分別是:設置UART的RX FIFO在8bit處觸發、設置UART的超時為1(4個字符時間)、禁止所有中斷輪詢模式為默認的樣式。
回到main函數的分析當中,接下來的函數實現的是建立起中斷的功能,這個函數在我們上一章也進行過詳細的講解。這里我們關注一下下面這個函數。
當我們運行XScuGic_Connect這個函數的時候,實際運行的就是這個回調函數。這個函數也是真正實現UART發送與接收功能的函數。可以看到這個程序是通過讀寫寄存器的方式來工作的,我們可以用剛才我們講到的方法對其進行分析,在程序中,我們也給出了分析的過程。大家可以認真的去看一看。
9.3 本章小結
本章主要詳細的分析了UART中斷的實現過程,通過本章,我們重點需要掌握的是怎樣分析一個問題的方法。通過這幾張中斷部分的講解,我們應該做到對中斷部分得心應手的程度。