
本文共 7750 字,大约阅读时间需要 25 分钟。
软件环境: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_INTRstatic XScuGic Intc; //GICstatic XUartPs Uart;//uartunsigned 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;}
发表评论
最新留言
关于作者
