項目簡述
前面的文章我們已經講過CPU之間的軟中斷,PS與PL之間的共享中斷,但是共享中斷還有一種數據類型也就是PS外設引起的中斷。在學習本篇文章的同時一定要學習前面的兩篇文章,可以對ZYNQ的中斷有一個總體的認知。本篇文章將以UART爲例來講解PS的外設中斷的使用。關於UART在ZYNQ的PS端相信大家都使用過print來輸出相應的數據。但是UART如何接收數據是不是大家還沒有真正的使用過,UART接收數據一般情況下我們使用的是中斷來進行相應數據的接收,所以這篇文章我們講解UART的中斷情況下的發射與接收。
工程描述:使用PC機通過串口對PS發送數據,PS端做出相應的相應,並把接受到的數據發送回PS端,從而完成串口的迴環校驗。
本次實驗所用到的軟硬件環境如下:
1、VIVADO 2019.1
2、米聯客MZ7015FA開發板
共享中斷概述
這裏爲了清晰表述,再次引入中斷分類:
從上面可以看出共享中斷分爲PL的中斷和PS的IO中斷,然後下面的共享中斷的詳細情況:
從上面可以看出UART1中斷的ID是82,這在程序中進行初始化相應的中斷時唯一的標識。
這一篇文章我們主要講解UART中斷的例子,引起UART中斷的事件類型有很多,這就需要我們對其進行相應的中斷類型的操作。而且這裏需要注意的一點,這種類型的終端操作調試的時候一般不要使用Debug進行調試,因爲這種情況會使得數據傳輸丟失,當然使用Debug調試還是可以大體驗證自己實驗的正確性。中斷的代碼與前面的軟中斷和PL端的共享中斷大體一直,但是需要我們針對UART中斷多添加一些額外的內容:
1、設置UART中斷類型:IntrMask = XUARTPS_IXR_TOUT | XUARTPS_IXR_PARITY | XUARTPS_IXR_FRAMING |
XUARTPS_IXR_OVER | XUARTPS_IXR_TXEMPTY | XUARTPS_IXR_RXFULL |
XUARTPS_IXR_RXOVR;
XUartPs_SetInterruptMask(&UartPs, IntrMask);
2、設置串口波特率:XUartPs_SetBaudRate(&UartPs,115200);
3、設置中斷服務函數:XUartPs_SetHandler(&UartPs,(XUartPs_Handler)UartHandler,&UartPs);
4、這裏需要注意中斷髮生後一定要接受緩存的數據用來確保下次觸發數據的正確性,XUartPs_Recv(&UartPs, RecBuf, 32);
PL端設計
這裏PL端只需要例化一個ZYNQ的IP,其他部分不需要進行任何處理,所以PL端我們沒有進行相應的設計。其中,Block Design設計如下:
PS端設計
總體代碼與前面的兩篇博客幾乎一樣,只是針對UART的協議使用了幾個函數用來進行相應的設置。代碼如下:
#include <stdio.h>
#include "xscugic.h"
#include "xparameters.h"
#include "sleep.h"
#include "xuartps.h"
#define GIC_ID XPAR_PS7_SCUGIC_0_DEVICE_ID
#define UART_DEVICE_ID XPAR_XUARTPS_0_DEVICE_ID
#define UART1INIR XPAR_XUARTPS_1_INTR
static XScuGic ScuGic;
static XScuGic_Config * ScuGicCfgPtr;
XUartPs UartPs;
XUartPs_Config *Config;
u8 RecBuf[32];
u8 SendBuf[32];
//initial gic & software intr
int initSwIntr();
//callback func
void UartHandler(void *CallBackRef, u32 Event, unsigned int EventData);
int inituart();
int main()
{
int status;
int i;
status = initSwIntr();
status = inituart();
if(status != XST_SUCCESS){
return status;
}
for(i = 0;i < 32;i++)
SendBuf[i] = i;
while(1){
usleep(100000);
}
return 0;
}
int initSwIntr(){
int status;
Xil_ExceptionInit();
ScuGicCfgPtr = XScuGic_LookupConfig(GIC_ID);
status = XScuGic_CfgInitialize(&ScuGic,ScuGicCfgPtr,ScuGicCfgPtr->CpuBaseAddress);
if(status != XST_SUCCESS){
return status;
}
Xil_ExceptionRegisterHandler(XIL_EXCEPTION_ID_INT,(Xil_ExceptionHandler)XScuGic_InterruptHandler,&ScuGic);
status = XScuGic_Connect(&ScuGic,UART1INIR,(Xil_ExceptionHandler)XUartPs_InterruptHandler,&UartPs);
if(status != XST_SUCCESS){
return status;
}
XScuGic_Enable(&ScuGic,UART1INIR);
Xil_ExceptionEnable();
return XST_SUCCESS;
}
int inituart(){
int status;
u32 IntrMask;
IntrMask = XUARTPS_IXR_TOUT | XUARTPS_IXR_PARITY | XUARTPS_IXR_FRAMING |
XUARTPS_IXR_OVER | XUARTPS_IXR_TXEMPTY | XUARTPS_IXR_RXFULL |
XUARTPS_IXR_RXOVR;
Config = XUartPs_LookupConfig(UART_DEVICE_ID);
status = XUartPs_CfgInitialize(&UartPs, Config, Config->BaseAddress);
status = XUartPs_SelfTest(&UartPs);
XUartPs_SetBaudRate(&UartPs,115200);
XUartPs_SetHandler(&UartPs,(XUartPs_Handler)UartHandler,&UartPs);
XUartPs_SetInterruptMask(&UartPs, IntrMask);
XUartPs_SetOperMode(&UartPs, XUARTPS_OPER_MODE_NORMAL);
XUartPs_SetRecvTimeout(&UartPs, 20);
XUartPs_Recv(&UartPs, RecBuf, 32);
return status;
}
void UartHandler(void *CallBackRef, u32 Event, unsigned int EventData){
u32 TotalReceivedCount;
int i;
if (Event == XUARTPS_EVENT_RECV_DATA) {
TotalReceivedCount = EventData;
if(TotalReceivedCount == 32) {
for(i=0;i<TotalReceivedCount;i++)
SendBuf[i] = RecBuf[i];
XUartPs_Send(&UartPs, SendBuf, TotalReceivedCount);
XUartPs_Recv(&UartPs, RecBuf, 32);
TotalReceivedCount=0;
}
}
/*
* Data was received, but not the expected number of bytes, a
* timeout just indicates the data stopped for 8 character times
*/
if (Event == XUARTPS_EVENT_RECV_TOUT) {
TotalReceivedCount = EventData;
for(i=0;i<TotalReceivedCount;i++)
SendBuf[i] = RecBuf[i];
XUartPs_Send(&UartPs, SendBuf, TotalReceivedCount);
XUartPs_Recv(&UartPs, RecBuf, 32);
TotalReceivedCount=0;
}
XUartPs_Recv(&UartPs, RecBuf, 32);
}
大家可以將上面的代碼放到自己的開發板中進行相應的調試,驗證實驗的正確性,現在簡要對上面的函數進行說明。
3、XUartPs_SetHandler(UartInstPtr, (XUartPs_Handler)UartPs_Intr_Handler, UartInstPtr)
函數負責設置中斷產生後的回調函數爲 UartPs_Intr_Handler。
4、 XUartPs_SetInterruptMask(UartInstPtr, IntrMask)
函數負責配置中斷類型
6、 XUartPs_SetRecvTimeout(UartInstPtr, 8)
函數設置了空閒超時等待的時間爲 8x4 = 32 爲 32 個波特率採樣時鐘,意思就是當串口上超過 32 個波特率採樣時
鐘沒有數據的時候,就會觸發一次超時中斷,這樣可以通知用戶程序去讀取已經接收到的數據。
爲了正常啓動接收,不管有沒有數據到來,需要先啓動一次接收函數 XUartPs_Recv(&UartPs, RecvBuffer,TEST_BUFFER_SIZE)。
測試結果
爲了驗證我們實驗的正確性,我們對上面的程序進行了下班測試,測試結果如下:
從上面的結果從而驗證了實驗的正確性。
總結
創作不易,認爲文章有幫助的同學們可以關注、點贊、轉發支持。爲行業貢獻及其微小的一部分。對文章有什麼看法或者需要更近一步交流的同學,可以加入下面的羣: