Search code examples
interruptuartzynq

ZYNQ-7000 UART issue - no interrupt raised when receiving data from PC


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.

enter image description here


Solution

  • There are few problems of your program.

    Receive function

    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);
    

    Interrupt Handler

    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.

    Custom Callback

    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.

    Addition

    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);
    

    Reference

    Please refer the official example by Xilinx on GitHub.