【JokerのZYNQ7020】UART。

软件环境:vivado 2017.4        硬件平台:XC7Z020 


内部结构大概如图所示,PL这边跟上节中断配置的基本一样,其实主要还是在SDK程序这边。

 Vivado 2017.4 Create Block Design后,添加ZYNQ7 Processing system,然后自动连接就行,Generate the output products,Create a HDL wrapper,Generate Bitstream,Export Hardware(注意勾选include biestream),最后launch SDK,加入如下代码。

#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中所有数据
			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=%ld\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);
        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*/ );
        Xil_ExceptionEnableMask(XIL_EXCEPTION_IRQ); //Enable interrupts in the Processor.
    }


int main()
{
	 XUartPs_Config *UartConfigPtr;     //uart config
     UartConfigPtr = XUartPs_LookupConfig(UART_DEVICE_ID);
     XUartPs_CfgInitialize(&Uart,UartConfigPtr,UartConfigPtr->BaseAddress);
     SetupInterruptSystem(&Intc,&Uart,UART_IRPT_INTR);
     while(1);

     return 0;
}

接下来对程序中的一些函数做一些解释说明:

XUartPs_Config串口配置结构体,包括设备号、基址、输入式中频率、引脚链接方式。

typedef struct {
	u16 DeviceId;	 /**< Unique ID  of device */
	u32 BaseAddress; /**< Base address of device (IPIF) */
	u32 InputClockHz;/**< Input clock frequency */
	s32 ModemPinsConnected; /** Specifies whether modem pins are connected
				 *  to MIO or FMIO */
} XUartPs_Config;

XScuGic要配置中断,Gic结构体自然是不必说的啊。

typedef struct
{
	XScuGic_Config *Config;  /**< Configuration table entry */
	u32 IsReady;		 /**< Device is initialized and ready */
	u32 UnhandledInterrupts; /**< Intc Statistics */
} XScuGic;

XUartPs结构体,包括配置项、输入时钟频率、初始化状态、波特率、接收发送buffer、中断函数、中断函数参数等等。

typedef struct {
	XUartPs_Config Config;	/* Configuration data structure */
	u32 InputClockHz;	/* Input clock frequency */
	u32 IsReady;		/* Device is initialized and ready */
	u32 BaudRate;		/* Current baud rate */

	XUartPsBuffer SendBuffer;
	XUartPsBuffer ReceiveBuffer;

	XUartPs_Handler Handler;
	void *CallBackRef;	/* Callback reference for event handler */
	u32 Platform;
	u8 is_rxbs_error;
} XUartPs;

程序最开始处宏定义的UART_DEVICE_ID是设备ID、INTC_DEVICE_ID中断的设备ID和UART_IRPT_INTR是串口的中断号。

#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

不断的open declaration可以看到#define XPS_UART1_INT_ID        82U至于这个82怎么来的。查ug585。

XUartPs_LookupConfig()可以看到,是串口专用的初始化配置函数。

******************************************************************************/
XUartPs_Config *XUartPs_LookupConfig(u16 DeviceId)
{
	XUartPs_Config *CfgPtr = NULL;

	u32 Index;

	for (Index = 0U; Index < (u32)XPAR_XUARTPS_NUM_INSTANCES; Index++) {
		if (XUartPs_ConfigTable[Index].DeviceId == DeviceId) {
			CfgPtr = &XUartPs_ConfigTable[Index];
			break;
		}
	}

	return (XUartPs_Config *)CfgPtr;
}

其中,XUartPs_ConfigTable表直接跟UART1挂钩,设备号、基址、时钟频率、引脚方式全通过这个结构体配置。

XUartPs_Config XUartPs_ConfigTable[XPAR_XUARTPS_NUM_INSTANCES] =
{
	{
		XPAR_PS7_UART_1_DEVICE_ID,
		XPAR_PS7_UART_1_BASEADDR,
		XPAR_PS7_UART_1_UART_CLK_FREQ_HZ,
		XPAR_PS7_UART_1_HAS_MODEM
	}
};

XUartPs_CfgInitialize()是串口相关参数的初始化函数,例如数据位数、停止位数、校验方式、波特率等。

s32 XUartPs_CfgInitialize(XUartPs *InstancePtr,
				   XUartPs_Config * Config, u32 EffectiveAddr)
{
	s32 Status;
	u32 ModeRegister;
	u32 BaudRate;

	/* Assert validates the input arguments */
	Xil_AssertNonvoid(InstancePtr != NULL);
	Xil_AssertNonvoid(Config != NULL);

	/* Setup the driver instance using passed in parameters */
	InstancePtr->Config.BaseAddress = EffectiveAddr;
	InstancePtr->Config.InputClockHz = Config->InputClockHz;
	InstancePtr->Config.ModemPinsConnected = Config->ModemPinsConnected;

	/* Initialize other instance data to default values */
	InstancePtr->Handler = XUartPs_StubHandler;

	InstancePtr->SendBuffer.NextBytePtr = NULL;
	InstancePtr->SendBuffer.RemainingBytes = 0U;
	InstancePtr->SendBuffer.RequestedBytes = 0U;

	InstancePtr->ReceiveBuffer.NextBytePtr = NULL;
	InstancePtr->ReceiveBuffer.RemainingBytes = 0U;
	InstancePtr->ReceiveBuffer.RequestedBytes = 0U;

	/* Initialize the platform data */
	InstancePtr->Platform = XGetPlatform_Info();

	InstancePtr->is_rxbs_error = 0U;

	/* Flag that the driver instance is ready to use */
	InstancePtr->IsReady = XIL_COMPONENT_IS_READY;

	/*
	 * Set the default baud rate here, can be changed prior to
	 * starting the device
	 */
	BaudRate = (u32)XUARTPS_DFT_BAUDRATE;
	Status = XUartPs_SetBaudRate(InstancePtr, BaudRate);
	if (Status != (s32)XST_SUCCESS) {
		InstancePtr->IsReady = 0U;
	} else {

		/*
		 * Set up the default data format: 8 bit data, 1 stop bit, no
		 * parity
		 */
		ModeRegister = XUartPs_ReadReg(InstancePtr->Config.BaseAddress,
					  XUARTPS_MR_OFFSET);

		/* Mask off what's already there */
		ModeRegister &= (~((u32)XUARTPS_MR_CHARLEN_MASK |
						 (u32)XUARTPS_MR_STOPMODE_MASK |
						 (u32)XUARTPS_MR_PARITY_MASK));

		/* Set the register value to the desired data format */
		ModeRegister |=	((u32)XUARTPS_MR_CHARLEN_8_BIT |
						 (u32)XUARTPS_MR_STOPMODE_1_BIT |
						 (u32)XUARTPS_MR_PARITY_NONE);

		/* Write the mode register out */
		XUartPs_WriteReg(InstancePtr->Config.BaseAddress, XUARTPS_MR_OFFSET,
				   ModeRegister);

		/* Set the RX FIFO trigger at 8 data bytes. */
		XUartPs_WriteReg(InstancePtr->Config.BaseAddress,
				   XUARTPS_RXWM_OFFSET, 0x08U);

		/* Set the RX timeout to 1, which will be 4 character time */
		XUartPs_WriteReg(InstancePtr->Config.BaseAddress,
				   XUARTPS_RXTOUT_OFFSET, 0x01U);

		/* Disable all interrupts, polled mode is the default */
		XUartPs_WriteReg(InstancePtr->Config.BaseAddress, XUARTPS_IDR_OFFSET,
				   XUARTPS_IXR_MASK);

		Status = XST_SUCCESS;
	}
	return Status;
}

下图中,第一个框是默认波特率的设置,系统默认值是115200,第二个红框设置的是8个数据位数、1个停止位数、无校验方式。第三个红框是接收自带FIFO(看最顶上那张图,接收和发送自带了FIFO)多少字节触发接收中断,这三个红框的设置比较关键,一般改串口初始化配置也就改这几项,剩下要改的都在接收中断函数,就是那个Handle里面。

 

Xil_ExceptionInit()在这实际是个空函数,里面直接return的,据函数前的说明意思是这函数是个通用API,用于在所有支持的arm处理器上初始化异常处理程序,还放在这里主要是为了避免与之前软件版本产生意外的兼容性问题。

XScuGic_LookupConfig()和XScuGic_CfgInitialize()是与通用中断控制器相关的查找设备ID和获取设备配置表基址的函数,TIMER那一章里也有说过,设备的LookupConfig()和CfgInitialize()配设备的,中断的LookupConfig()和CfgInitialize()配中断的。

Xil_ExceptionRegisterHandler()处理特定异常时用的。通用函数。

void Xil_ExceptionRegisterHandler(u32 Exception_id,
				    Xil_ExceptionHandler Handler,
				    void *Data)
{
	XExc_VectorTable[Exception_id].Handler = Handler;
	XExc_VectorTable[Exception_id].Data = Data;
}

XScuGic_Connect()将串口中断的中断函数和中断控制器绑定,当串口产生中断时,找UartIntrHandler()中断处理函数。

s32  XScuGic_Connect(XScuGic *InstancePtr, u32 Int_Id,
                      Xil_InterruptHandler Handler, void *CallBackRef)
{
	/*
	 * Assert the arguments
	 */
	Xil_AssertNonvoid(InstancePtr != NULL);
	Xil_AssertNonvoid(Int_Id < XSCUGIC_MAX_NUM_INTR_INPUTS);
	Xil_AssertNonvoid(Handler != NULL);
	Xil_AssertNonvoid(InstancePtr->IsReady == XIL_COMPONENT_IS_READY);

	/*
	 * The Int_Id is used as an index into the table to select the proper
	 * handler
	 */
	InstancePtr->Config->HandlerTable[Int_Id].Handler = Handler;
	InstancePtr->Config->HandlerTable[Int_Id].CallBackRef = CallBackRef;

	return XST_SUCCESS;
}

XScuGic_Enable()使能串口中断。

void XScuGic_Enable(XScuGic *InstancePtr, u32 Int_Id)
{
	u32 Mask;

	/*
	 * Assert the arguments
	 */
	Xil_AssertVoid(InstancePtr != NULL);
	Xil_AssertVoid(Int_Id < XSCUGIC_MAX_NUM_INTR_INPUTS);
	Xil_AssertVoid(InstancePtr->IsReady == XIL_COMPONENT_IS_READY);

	/*
	 * The Int_Id is used to create the appropriate mask for the
	 * desired bit position. Int_Id currently limited to 0 - 31
	 */
	Mask = 0x00000001U << (Int_Id % 32U);

	/*
	 * Enable the selected interrupt source by setting the
	 * corresponding bit in the Enable Set register.
	 */
	XScuGic_DistWriteReg(InstancePtr, (u32)XSCUGIC_ENABLE_SET_OFFSET +
						((Int_Id / 32U) * 4U), Mask);
}

XUartPs_SetInterruptMask()是设置中断方式的函数,当前设置的XUARTPS_IXR_RXOVR是接收FIFO来触发中断。

void XUartPs_SetInterruptMask(XUartPs *InstancePtr, u32 Mask)
{
	u32 TempMask = Mask;
	/* Assert validates the input arguments */
	Xil_AssertVoid(InstancePtr != NULL);

	TempMask &= (u32)XUARTPS_IXR_MASK;

	/* Write the mask to the IER Register */
	XUartPs_WriteReg(InstancePtr->Config.BaseAddress,
		 XUARTPS_IER_OFFSET, TempMask);

	/* Write the inverse of the Mask to the IDR register */
	XUartPs_WriteReg(InstancePtr->Config.BaseAddress,
		 XUARTPS_IDR_OFFSET, (~TempMask));

}

除此之外可以看到,可设置的其他中断触发方式还有很多,在这里备注如下。

#define XUARTPS_IXR_RBRK	0x00002000U /**< 接收FIFO中断探测中断(这个啥意思目前不知=。=) */
#define XUARTPS_IXR_TOVR	0x00001000U /**< 发送FIFO溢出中断 */
#define XUARTPS_IXR_TNFUL	0x00000800U /**< 发送FIFO快满时中断 */
#define XUARTPS_IXR_TTRIG	0x00000400U /**< 发送触发中断 */
#define XUARTPS_IXR_DMS		0x00000200U /**< 应该是外引脚方式改变时触发中断 */
#define XUARTPS_IXR_TOUT	0x00000100U /**< 超时错误中断 */
#define XUARTPS_IXR_PARITY 	0x00000080U /**< 校验错误中断 */
#define XUARTPS_IXR_FRAMING	0x00000040U /**< 框架错误中断 */
#define XUARTPS_IXR_OVER	0x00000020U /**< 来不及处理overrun中断 */
#define XUARTPS_IXR_TXFULL 	0x00000010U /**< 发送FIFO满中断 */
#define XUARTPS_IXR_TXEMPTY	0x00000008U /**< 发送FIFO空中断 */
#define XUARTPS_IXR_RXFULL 	0x00000004U /**< 接收FIFO满中断 */
#define XUARTPS_IXR_RXEMPTY	0x00000002U /**< 接收FIFO空中断 */
#define XUARTPS_IXR_RXOVR  	0x00000001U /**< 接收FIFO到达指定字节个数中断 */
#define XUARTPS_IXR_MASK	0x00003FFFU /**< Valid bit mask应该是个综合设置(这个啥意思目前不知=。=) */

感觉用的比较多的应该还是最后的这几个。

 手册上还有更加直观的图,看一眼就明白。

Xil_ExceptionEnableMask()初始化最后当然是中断的使能函数。

#define Xil_ExceptionEnableMask(Mask)	\
		mtcpsr(mfcpsr() & ~ ((Mask) & XIL_EXCEPTION_ALL))

初始化到这基本就算结束了,接下来瞅瞅串口中断里都干了点啥。

第一句,看下IMR, 看看你都使能了哪些中断标志,也就是哪些中断可以被触发,可以被系统响应;第二句,看下ISR,当前产生了哪些中断;两个进行与运算,其结果决定了接下来是否进入中断处理和进入哪个中断处理。

 然后判断,如果被触发的中断是接收相关的FIFO到达指定字节数、FIFO空、FIFO满其中一种,就需要继续进行相应处理。

上面那个CsrRegister存的是当前中断标志,看看哪些中断被置起来了,然后在下面的while里比较,看看被置起来的标志是不是XUARTPS_SR_RXEMPTY,这里需要说明一下,==0意味着读取FIFO不是空的,不信看E000102C寄存器。

 所以整个while的意思就是,如果RX的FIFO不是空的,就把接收FIFO中的数拿出来,写到发送寄存器里去,然后接收/发送的数据计数加1,重新再读取中断状态寄存器,看看还有没有数据需要在继续往外搬,以此循环直到所有数据处理完。

这里特别需要注意两点,也是我实验以后得到的结论:

1.用SDK中自带串口终端发数,按send以后,数据末尾自动添加\n,也就是说如果你发送123,实际发出的是123\n,4个字节。

2.如果你在最开始XUartPs_CfgInitialize()中XUARTPS_RXWM_OFFSET设置的是8字节触发,那么当你接收字节数小于8时,这个while是进不去的,只有当接收字节数大于等于8时,才会依次进while(8次),所以对于FIFO虽然只有8个bit宽度,但深度应该是XUARTPS_RXWM_OFFSET设置的最大64(我猜的。。。)。

3.读取数据和写入数据都是通过操作XUARTPS_FIFO_OFFSET这个偏移量的寄存器来完成的。

当所有数据处理完,最后往XUARTPS_ISR_OFFSET写入之前的ISR状态标志,将相应的中断状态写1清0。

最后小声哔哔一下,本来我以为串口中断两三行就写完了,结果没想到这么多东西。。。


所以在最后总结一下UART,在SDK中大致的流程:

通过XUartPs_LookupConfig函数,找到UART的基址------------>

通过XUartPs_CfgInitialize函数,初始化UART配置------------>

通过Xil_ExceptionInit函数,避免与前版本有兼容性问题------------>

通过XScuGic_LookupConfig函数,找到通用中断控制器基址------------>

通过XScuGic_CfgInitialize函数,初始化通用中断控制器------------>

通过Xil_ExceptionRegisterHandler函数,使能异常中断处理------------>

通过XScuGic_Connect函数,绑定UART中断处理函数------------>

通过XScuGic_Enable函数,在通用中断控制器中使能UART中断------------>

通过XUartPs_SetInterruptMask函数,设置UART的中断触发方式------------>

通过Xil_ExceptionEnableMask函数,使能中断。
 

  • 6
    点赞
  • 35
    收藏
    觉得还不错? 一键收藏
  • 9
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 9
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值