I'm trying to UART transceiver on my ZYNQ-7000 board using interrupts. Basically, it just take data from the serial terminal and send back to it.
After initialization, a message Uart Initialization Successful!
is sent to and shown on the terminal which confirms that the ZYNQ can send data to the PC. But whenever I input some random letters, it seems that interrupt handler function, called void Handler()
, was never called. It seems there is no interrupt generated at all. I've looked up a lot but cannot slove the problem. Could any one please help me with this, please.
Here is my code,
#include "xparameters.h"
#include "xuartps.h"
#include "xil_printf.h"
#include "xscugic.h"
#include "stdio.h"
// serial device ID
#define UART_DEVICE_ID XPAR_PS7_UART_1_DEVICE_ID
// interrupt ID
#define INTC_DEVICE_ID XPAR_SCUGIC_SINGLE_DEVICE_ID
// serial port interrupt id
#define UART_INT_IRQ_ID XPAR_XUARTPS_1_INTR
// interrupt controller driver instance
XScuGic Intc;
// serial port driver instance
XUartPs Uart_Ps;
// data buffer size
#define MAX_LEN 512
u8 ReceivedBuffer[MAX_LEN];
volatile u32 ReceivedByteNum;
XUartPsFormat UartFormat = {
115200,
XUARTPS_FORMAT_8_BITS,
XUARTPS_FORMAT_NO_PARITY,
XUARTPS_FORMAT_1_STOP_BIT
};
// function declaration
int UartInit(XUartPs *uart_ps);
// interrupt handler
void Handler(void *call_back_ref);
int UartIntrInit(XScuGic *intc, XUartPs *uart_ps);
// main function
int main(void){
int status;
// initialize the serial port
status = UartInit(&Uart_Ps);
if(status == XST_FAILURE){
xil_printf("Uart Initialization Failed\r\n");
return XST_FAILURE;
}
// interrupt initialization
status = UartIntrInit(&Intc, &Uart_Ps);
if(status == XST_FAILURE){
xil_printf("Uart Initialization Failed\r\n");
return XST_FAILURE;
}
xil_printf("Uart Initialization Successful!\r\n");
// main loop
while (1) {};
return status;
}
int UartInit(XUartPs *uart_ps){
int status;
XUartPs_Config *uart_cfg;
uart_cfg = XUartPs_LookupConfig(UART_DEVICE_ID);
if(NULL == uart_cfg) return XST_FAILURE;
status = XUartPs_CfgInitialize(uart_ps, uart_cfg, uart_cfg->BaseAddress);
if(status != XST_SUCCESS) return XST_FAILURE;
// UART self test
status = XUartPs_SelfTest(uart_ps);
if(status != XST_SUCCESS) return XST_FAILURE;
XUartPs_SetOperMode(uart_ps, XUARTPS_OPER_MODE_NORMAL);
XUartPs_SetDataFormat(uart_ps, &UartFormat);
XUartPs_SetFifoThreshold(uart_ps, 32);
XUartPs_SetRecvTimeout(uart_ps, 8);
return XST_SUCCESS;
};
// UART Interrupt handler service
void Handler(void *call_back_ref){
xil_printf("Enter INTR\r\n");
XUartPs *uart_instance_ptr = (XUartPs *) call_back_ref;
u32 ReceivedCount = 0;
u32 IsrValue;
IsrValue = XUartPs_ReadReg(uart_instance_ptr->Config.BaseAddress, XUARTPS_IMR_OFFSET);
IsrValue &= XUartPs_ReadReg(uart_instance_ptr->Config.BaseAddress, XUARTPS_ISR_OFFSET);
// if interrupt is asserted
if( IsrValue & ((u32) XUARTPS_IXR_RXOVR) ){
XUartPs_WriteReg(uart_instance_ptr->Config.BaseAddress, XUARTPS_ISR_OFFSET, XUARTPS_IXR_RXOVR);
ReceivedCount = XUartPs_Recv(&Uart_Ps, ReceivedBuffer, MAX_LEN);
ReceivedByteNum += ReceivedCount;
}
else if( IsrValue & ((u32) XUARTPS_IXR_TOUT) ){
// Rx FIFO timeout / idle
XUartPs_WriteReg(uart_instance_ptr->Config.BaseAddress, XUARTPS_ISR_OFFSET, XUARTPS_IXR_TOUT);
ReceivedCount = XUartPs_Recv(&Uart_Ps, ReceivedBuffer, MAX_LEN);
ReceivedByteNum += ReceivedCount;
// send out
for(u32 sendByte=0;sendByte<ReceivedByteNum;sendByte++){
XUartPs_SendByte(XPAR_PS7_UART_1_BASEADDR, ReceivedBuffer[sendByte]);
}
ReceivedByteNum = 0;
}
}
// UART Interrupt init
int UartIntrInit(XScuGic *intc, XUartPs *uart_ps){
int status;
// initialize the interrupt controller
XScuGic_Config *intc_cfg;
intc_cfg = XScuGic_LookupConfig(INTC_DEVICE_ID);
if(NULL == intc_cfg) return XST_FAILURE;
status = XScuGic_CfgInitialize(intc, intc_cfg, intc_cfg->CpuBaseAddress);
if(status != XST_SUCCESS) return XST_FAILURE;
// set and enable interrupt exception handle function
Xil_ExceptionInit();
Xil_ExceptionRegisterHandler(XIL_EXCEPTION_ID_INT,
(Xil_ExceptionHandler) XScuGic_InterruptHandler, (void *) intc);
// set interrupt handler for interrupt
XScuGic_Connect(intc, UART_INT_IRQ_ID, (Xil_ExceptionHandler) Handler, (void *) uart_ps);
// set interrupt trigger mode
XUartPs_SetInterruptMask(uart_ps, XUARTPS_IXR_RXOVR | XUARTPS_IXR_TOUT);
Xil_ExceptionEnable();
XScuGic_Enable(intc, UART_INT_IRQ_ID);
return XST_SUCCESS;
}
I'm using UART1 controller at MIO 48, 49 which is confirmed at both the Vivado and the schematic.
There are few problems of your program.
It seems you have not called the UART receive function (XUartPs_Recv). In interrupt mode, the UART controller will start receiving after you called XUartPs_Recv, this function is non-blocking. When all data received, the UART controller will generate an interrupt, and all data has been written to the receive buffer by the interrupt handler function.
XUartPs_Recv(uart_ps, RecvBuffer, SIZE_IN_BYTE);
UARTPS library provided the interrupt handler function (XUartPs_InterruptHandler). So you need to bind it to the XScuGic
XScuGic_Connect(intc, UART_INT_IRQ_ID,
(XInterruptHandler) XUartPs_InterruptHandler, uart_ps);
This interrupt handler can help you receive the data from UART FIFO and write to the receive buffer.
If you want to do something when the interrupt occoured, you don't need to write a new handler function by yourself, but a Callback function is needed.
XUartPs_SetHandler(uart_ps, (XUartPs_Handler)Handler, uart_ps);
Use this function, your custom handler will be called from the interrupt context (XUartPs_InterruptHandler) when data has been sent or received.
You may set the receiver timeout. If it is not set, and the last few bytes of data do not trigger the over-water or full interrupt, the bytes will not be received. By default it is disabled.
XUartPs_SetRecvTimeout(uart_ps, 8);
Please refer the official example by Xilinx on GitHub.