【JokerのZYNQ7020】UART不定长收发。

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


之前的【JokerのZYNQ7020】UART这篇文章,只是解决了zynq7020在sdk下串口的简单收发功能,当时就发现了一些问题,还专门用红字标出来了,只是当时写完这个以后,立马有别的事情要忙,所以到现在才回来填这个坑,如下图。

对于zynq下,串口的收发方式和带来的问题再稍微描述一下,可用的串口收发主要有两种方式,一种是之前文章写过的,基于FIFO的中断方式,在这种方式下,中断的产生取决于FIFO的深度,当深度值过大时,发送小于深度值得数据时,是不引起中断的,自然也不会进一步处理,当大于或等于深度值时,中断确实会触发,也能进行数据处理,但是但是但是!只会触发满FIFO的数据,多出来的数据会等待下次FIFO满触发中断时才能处理,所以如果串口通信使用定长的帧的话倒也无所谓,如果不定长帧通讯,或者还要有串口的交互过程,那简直无法想象,不知道是不是我配置的有问题,还是大家都有这个情况。 xuartps.c文件下,s32 XUartPs_CfgInitialize(XUartPs *InstancePtr,XUartPs_Config * Config, u32 EffectiveAddr)函数中可以设置这个FIFO的大小。

这里再确切的举个例子,假如这个FIFO的值设为8,当串口接收小于8 byte的数据时是不触发FIFO满的中断,当FIFO接收到12 byte数据的时候,会触发一次串口的中断,此时只能处理12 byte中的前8个 byte,后3个 byte+1个 '\n'就要等待下一次凑够8个byte产生中断时候才能处理,3个byte到也好办,难办的是那个'\n',多次不定长交互后,你都无法确定FIFO里到底有几个字节是发送串口时候自带的'\n'。

所以也就有今天的这个填坑的过程了,这里用到的是串口中断的另外一种方法,超时中断,设定一个超时时间,如果串口在不断接收数据的时候没有超过这个超时时间,就认为数据没有接收完毕,还会一直往FIFO里面送数据,如果超过了这个超时时间没有接收到新的数据,则认为本次串口接收完毕,可以进入中断函数中进行数据处理。

话有点多了,接下来看代码,具体说怎么写。

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

     recv_total_byte = 0;

     while(1);

     return 0;
}

main还是那个main,只是添加了个recv_total_byte来计算当前总共接收了多少个字节的数据。

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);
        Xil_ExceptionEnable();
        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_TOUT /*| XUARTPS_IXR_TXEMPTY | XUARTPS_IXR_TNFUL*/ );
        Xil_ExceptionEnableMask(XIL_EXCEPTION_IRQ); //Enable interrupts in the Processor.
    }

这里要注意了,XUartPs_SetInterruptMask这个函数中,需要添加XUARTPS_IXR_TOUT监测超时中断。

接下来去xuartps.c, s32 XUartPs_CfgInitialize(XUartPs *InstancePtr, XUartPs_Config * Config, u32 EffectiveAddr)函数中,修改串口配置,将FIFO从8改到32,超时时间那里说的也比较清楚,写1是4个波特率周期,这里写4就是16个,够用了。

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


	/*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=%d\r\n",ReceivedCount);
	XUartPs_WriteReg(InstancePtr->Config.BaseAddress, XUARTPS_ISR_OFFSET,
		IsrStatus);*/

	if((IsrStatus & ((u32)XUARTPS_IXR_RXOVR)) != (u32)0)		//FIFO溢出中断
	{
		ReceivedCount = XUartPs_Recv(InstancePtr,&uart_send[recv_total_byte],512);
		recv_total_byte += ReceivedCount;

		XUartPs_WriteReg(InstancePtr->Config.BaseAddress, XUARTPS_ISR_OFFSET,
				XUARTPS_IXR_RXOVR);		//清中断标志
	}
	else if((IsrStatus & ((u32)XUARTPS_IXR_TOUT)) != (u32)0)	//接收超时中断
	{
		ReceivedCount = XUartPs_Recv(InstancePtr,&uart_send[recv_total_byte],512);
		recv_total_byte += ReceivedCount;
		XUartPs_Send(InstancePtr,uart_send,recv_total_byte);

		XUartPs_WriteReg(InstancePtr->Config.BaseAddress, XUARTPS_ISR_OFFSET,
				XUARTPS_IXR_TOUT);		//清中断标志

		recv_total_byte = 0;
	}
}

 接下来当然是重中之重的重写的串口中断函数,之前文章里的中断处理过程在这里屏蔽了,没有删除,可以做下对比。这里对溢出中断和超时中断都进行了处理,不论触发的是哪种中断,都会接收数据放在同一个unsigned char 数组uart_send里面,只在超时中断中,将整个接收到的数据发送出去,计数器recv_total_byte清零,可以更好的保证数据接收的完整性和正确性。

你说不这样搞,简单点,只用溢出中断行不行,行,当然也行,我已经试过了。

XUartPs_SetInterruptMask(UartInstancePtr, XUARTPS_IXR_TOUT);只留下XUARTPS_IXR_TOUT这一块。

中断函数只留下if((IsrStatus & ((u32)XUARTPS_IXR_TOUT)) != (u32)0) 这一个处理,也是能正常收发的,但是上面那种处理肯定更严谨没错。

中断函数中使用到的XUartPs_Recv();和XUartPs_Send();这两个函数,在xuartps.c中可以找到。

 


2020-03-31更新    完整代码贴上来,费那老鼻子劲代码库里找了半天,还是在这备份下吧

#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

unsigned char uart_send[512];
u8 recv_total_byte;

static void UartIntrHandler(void *CallBackRef)
{

    XUartPs *InstancePtr = (XUartPs *) CallBackRef;
	u32 IsrStatus;

	u32 ReceivedCount=0;
	u32 CsrRegister;

	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

	if((IsrStatus & ((u32)XUARTPS_IXR_RXOVR)) != (u32)0)		//FIFO溢出中断
	{
		ReceivedCount = XUartPs_Recv(InstancePtr,&uart_send[recv_total_byte],512);
		recv_total_byte += ReceivedCount;

		XUartPs_WriteReg(InstancePtr->Config.BaseAddress, XUARTPS_ISR_OFFSET,
				XUARTPS_IXR_RXOVR);		//清中断标志
	}
	else if((IsrStatus & ((u32)XUARTPS_IXR_TOUT)) != (u32)0)	//接收超时中断
	{
		ReceivedCount = XUartPs_Recv(InstancePtr,&uart_send[recv_total_byte],512);
		recv_total_byte += ReceivedCount;
		XUartPs_Send(InstancePtr,uart_send,recv_total_byte);

		XUartPs_WriteReg(InstancePtr->Config.BaseAddress, XUARTPS_ISR_OFFSET,
				XUARTPS_IXR_TOUT);		//清中断标志

		recv_total_byte = 0;
	}
}

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);
        Xil_ExceptionEnable();
        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_TOUT);
        Xil_ExceptionEnableMask(XIL_EXCEPTION_IRQ); //Enable interrupts in the Processor.
    }


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

     recv_total_byte = 0;

     while(1);

     return 0;
}

 

评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值