본문으로 바로가기

[Zynq] ZyboZ7-20 Uart Interrupt

category 전자 이야기/Zynq 2022. 9. 15. 23:19
728x90
반응형

Zynq EV 보드인 Zybo Z7 20 보드를 사용하여 Uart Interrupt를 구현한 예제이다. 

Uart와 GPIO 정도만 살리고 나머지 기능은 다 OFF하고 진행한다.

Vitis에서도 기본으로 설정한 후 아래 코드대로 진행한다.



/***************************** Include Files *******************************/

#include "xparameters.h"
#include "xplatform_info.h"
#include "xuartps.h"
#include "xil_exception.h"
#include "xil_printf.h"
#include "xscugic.h"

/************************** Constant Definitions ***************************/

#define INTC				XScuGic
#define INTC_DEVICE_ID		XPAR_SCUGIC_SINGLE_DEVICE_ID
#define UART1_DEVICE_ID		XPAR_PS7_UART_1_DEVICE_ID
#define UART1_INT_IRQ_ID	XPAR_XUARTPS_1_INTR

#define RECV_BUFFER_SIZE    100

/**************************** Type Definitions *****************************/

/***************** Macros (Inline Functions) Definitions *******************/

/************************** Function Prototypes ****************************/

void Uart1_Handler(void *CallBackRef, u32 Event, unsigned int EventData);


/************************** Variable Definitions ***************************/

XUartPs UartPs1	;		/* Instance of the UART1 Device */

INTC InterruptController;	/* Instance of the Interrupt Controller */

volatile u8 RX_Flag=0;
static u8 RecvBuffer[RECV_BUFFER_SIZE];    /* Buffer for Receiving Data */
static int RecvDataSize;

/***************************************************************************/
/**
*
* Main function to call the Uart Echo example.
*
*
* @return	XST_SUCCESS if successful, XST_FAILURE if unsuccessful
*
* @note		None
*
****************************************************************************/
int main(void)
{
	u32 IntrMask;
	XScuGic_Config *IntcConfig;
	XUartPs_Config	*Uart1_Config;

	// UART 0
	Uart1_Config = XUartPs_LookupConfig(UART1_DEVICE_ID);
	XUartPs_CfgInitialize(&UartPs1, Uart1_Config, Uart1_Config->BaseAddress);
	XUartPs_SelfTest(&UartPs1);

	IntcConfig = XScuGic_LookupConfig(INTC_DEVICE_ID);
	XScuGic_CfgInitialize(&InterruptController, IntcConfig, IntcConfig->CpuBaseAddress);

	Xil_ExceptionRegisterHandler(XIL_EXCEPTION_ID_INT,
				(Xil_ExceptionHandler) XScuGic_InterruptHandler,
				&InterruptController);

	XScuGic_Connect(&InterruptController, UART1_INT_IRQ_ID, (Xil_ExceptionHandler) XUartPs_InterruptHandler, (void *) &UartPs1);

	XScuGic_Enable(&InterruptController, UART1_INT_IRQ_ID);

	Xil_ExceptionEnable();

	XUartPs_SetHandler(&UartPs1, (XUartPs_Handler)Uart1_Handler, &UartPs1);

	IntrMask =
		XUARTPS_IXR_TOUT | XUARTPS_IXR_PARITY | XUARTPS_IXR_FRAMING |
		XUARTPS_IXR_OVER | XUARTPS_IXR_TXEMPTY | XUARTPS_IXR_RXFULL |
		XUARTPS_IXR_RXOVR;
	XUartPs_SetInterruptMask(&UartPs1, IntrMask);
	XUartPs_SetOperMode(&UartPs1, XUARTPS_OPER_MODE_NORMAL);
	XUartPs_SetRecvTimeout(&UartPs1, 3);

	XUartPs_Recv(&UartPs1, RecvBuffer, RECV_BUFFER_SIZE);

	print("Start\r\n");

	while (1)
	{
		switch(RX_Flag)
		{
			case 1:
				RX_Flag=0;
				break;
			case 2:
				RX_Flag=0;
				XUartPs_Send(&UartPs1, RecvBuffer, RecvDataSize);
				XUartPs_Recv(&UartPs1, RecvBuffer, RECV_BUFFER_SIZE);
				break;
			case 3:
				RX_Flag=0;
				break;
			case 4:
				RX_Flag=0;
				break;
			case 5:
				RX_Flag=0;
				break;
			case 6:
				RX_Flag=0;
				break;
			case 7:
				RX_Flag=0;
				break;
			default:
				break;
		}
	}

	return XST_SUCCESS;
}


/**************************************************************************/
/**
*
* This function does a minimal test on the UART device using the hardware
* interface.
*
* @param	UartBaseAddress is the base address of the device
*
* @return	XST_SUCCESS if successful, XST_FAILURE if unsuccessful
*
* @note		None.
*
**************************************************************************/
void Uart1_Handler(void *CallBackRef, u32 Event, unsigned int EventData)
{
	switch(Event)
	{
		case XUARTPS_EVENT_RECV_DATA:				/**< Data receiving done */
			RX_Flag=1;
			break;
		case XUARTPS_EVENT_RECV_TOUT:				/**< A receive timeout occurred */
			RecvDataSize=EventData;
			RX_Flag=2;
			break;
		case XUARTPS_EVENT_SENT_DATA:				/**< Data transmission done */
			RX_Flag=3;
			break;
		case XUARTPS_EVENT_RECV_ERROR:				/**< A receive error detected */
			RX_Flag=4;
			break;
		case XUARTPS_EVENT_MODEM:					/**< Modem status changed */
			RX_Flag=5;
			break;
		case XUARTPS_EVENT_PARE_FRAME_BRKE:			/**< A receive parity, frame, break	error detected */
			RX_Flag=6;
			break;
		case XUARTPS_EVENT_RECV_ORERR:				/**< A receive overrun error detected */
			RX_Flag=7;
			break;
		default:
			break;
	}
}

 

 Uart를 Init 하고 SCUG Init 그리고 연결 후 핸들러 설정 후 mask 설정을 한다. 그리고 모드 설정 및 타임아웃 설정을 한다.

 그리고 수신 버퍼를 설정한다. 이 개념이 좀 햇갈릴 수 있다. 만약 예제에서처럼 수신 버퍼를 100개를 설정하면 인터럽트는 100개가 다 차게되면 XUARTPS_EVENT_RECV_DATA 이벤트가 발생하게 된다. 즉, 수신 완료 인터럽트는 설정된 버퍼만큼 데이터가 들어와야 발생하게 되므로 정확히 정해진 패킷의 길이를 알고 있다면 패킷 수 만큼 할당하고 인터럽트 이벤트를 XUARTPS_EVENT_RECV_DATA 를 사용하면 될 것이다.

 그러나 Uart 특성상 길이가 정해져 있지 않은 경우가 더 많은데 이때는 Timeout 이벤트를 사용해야 한다. 전송 특성상 타임아웃 시간을 고려해서 설정하고 패킷이 끊기게 되면 발생하는 XUARTPS_EVENT_RECV_TOUT 이벤트시 수신 처리를 하게 하면 된다.

 인터럽트 수신 후에는 다시 수신 버퍼를 설정하여 수신 버퍼를 클리어 하는 것이 좋다. 수신 완료의 경우야 뭐 다 차고 다시 채우는 상황이니 문제없을 수 있으나 타임아웃시에는 클리어를 안하면 버퍼를 이어서 사용하게 되기 때문에 문제가 생긴다. 뭐 애초에 통신이란게 에러도 있을 수 있으므로 인터럽트 발생시에는 버퍼를 비우는게 좋다. 

 버퍼를 비우는 명령어는 내가 살펴본 바로는 따로 없고 다시 버퍼를 재 설정하면 비워진다.

 

728x90
반응형