作者:圆宵,来源:FPGA那点事儿
串口是很常用的通信端口,其速率比较慢,控制也比较简单,一般来说,使用时不会出现太大的问题。但笔者前一阵调试一块zynq的板子,用CPU通过串口和板卡上的一款芯片进行通信时,却也碰到了不大不小的麻烦。现在把解决问题的过程分享出来,虽然犯的都是很low的错误,但是也希望能对读者有一些借鉴。
整个系统中,涉及串口通信的部分大概是这样的:
PL中使用的是Xilinx提供的标准串口IP,串口的波特率可调,应用中用的是115200 bps。整个系统裸跑,使用标准的驱动。调试时,基本的串口通信没有遇到什么障碍,很快就通了。但是把所有的功能整合到一起,在整个系统联调时,却在串口通信碰到了稀奇古怪的问题。
问题1:串口接收会偶尔丢数。
外部IC传过来的数据是规律的一帧一帧的,每帧固定长度,而且每帧数据会有一个包头。在系统全速运行时,经常会发现,在逻辑上本应该接收到下一帧包头的时候,却没有收到包头,由此导致这一整帧数据的丢失。(这里要提一点,Uart接收数据是采用的轮询的方式。每当查询到Uart的fifo中有数据,就读取一个byte,然后判断其是否是包头。接收完一整帧的数据后,就对数据进行处理,在此期间如果串口有数据进来,会暂存到Uart的内部FIFO中,FIFO深度默认为16。)
由于CPU的速度很快,而要处理的负荷又很轻,所以首先判断不可能是由于CPU的处理能力不够,而导致丢失数据。其次,用示波器和PL测的逻辑分析仪都测量了外部IC的串口输入端,没有抓到包头丢失的情况,所以数据肯定有送进来,也不是外部IC的问题。
那么难道是UartIP的问题吗?这是很成熟的IP,也不大可能会出问题。那么问题很可能出在IP的配置和使用上。由此仔细排查了Uart的初始化配置过程,也没有发现异常。
难道程序运行中,有别的地方改动了配置?由此在c代码里加入了一些冗余测试逻辑,当检测到包头不正常时,打印出Uart的配置信息,这次测试发现了的异常。Uart内部的FIFO深度是16,但是有一个开关控制FIFO是开启还是关闭,在包头丢失的时候,这个FIFO使能的选项不知何故被关闭了。接下来顺藤摸瓜,发现在一处中断函数里,对这个寄存器的操作有错误,修正后,Uart接收数据就正常了!
问题2:串口发送,在程序全速运行时,有时候数据会发送不出去。
程序运行中,经常会根据需要,通过串口命令实时调整外部IC的的工作模式。但是在程序全速运行时,却发现偶尔会出现模式修改不起作用。在程序单步执行调试时,从来没有发现这个问题。
该查的地方基本都查过了,为此还在逻辑里也专门加了相应的逻辑分析仪和测试统计代码,最后查到串口这里,发现出问题的时候,串口的命令根本就没有发送出去。
仔细分析代码的逻辑,找到了问题的原因:由于CPU的运行速度很快,串口发送很慢。所以串口发送时,CPU只是把要发送的数据写入了Uart的tx FIFO中,然后就去执行别的代码了。在某个中断函数里面,也用到这个串口去发送数据,不过这个中断函数在发送数据之前,会对tx FIFO进行复位清零操作。如果这个中断刚好在CPU发送模式修改命令后不久进入,则很可能在命令还未发送完整的时候,就把整个tx FIFO清空,导致命令发布出去。
找到问题后,改正就很简单了。
文章来源:FPGA那点事儿