本篇为沉浸式DEBUG记录(
废话比较多,纯流水账和我自己的内心戏,大家看个乐,最后的总结就是两点:
- 好好写tb,测试数据尽量多
- 用杜邦线的时候要保证连接的稳定性,因为杜邦线很容易松动,导致传输出错
BUG1:
数码管上显示的ROLL角度值目测是正确的,但是经过data_packing之后就传输错误,具体错误是上位机解码错误,通过串口调试器查看接收到的数据之后,发现经过一段时间的传输后,每一帧的数据量并不是13字节,如下:
从220行开始就错位了。
- 尝试不同UART速率,问题仍存在
- UART改为每次间隔一个空闲位传输,问题仍存在
同时可以发现,前面正确的每一帧,最后两个checksum计算存在问题,例如可以带入208行的数据验算一下,发现checksum就是错的,这个问题的原因很简单,就是我代码截位因为马虎写错了。
经过ILA的波形抓取分析,发现FIFO在一段时间后一直处于满的状态,这将导致后面的数据写不进去,进而导致丢数据。
根本原因是I2C的读取速率与UART发送的速率不匹配。
下面来分析一下,I2C配置的分频后的时钟是400K,但是根据I2C ip中的verilog,实际的SCL频率要除以2,也就是200K,1s能传输200K bit,一组数据大约是7x16+=128bit,1s能读取3125组数据,一组数据经过处理会变成13字节的一帧,也就是1s产生3125帧。
UART的发送速率假设为115200,那么就是1s发送115200个bit,发送一个字节要1+1+8+1=11bit,1s就只能发送大约10472个字节,再除以一帧13个字节,1s最多只能发送805帧。
产生数据的速度大约是发送数据的4倍,这很不行。所以,我们要限制数据产生的速率(或者方案2就是当FIFO不能保存一帧的时候就丢弃整个帧,不过这要判断FIFO还差多少个数据就满,能不能保存一帧)。
具体方案是,写一个定时器,定时频率是800Hz,每次读取一组。
增加了timer之后,发现FIFO仍然会存满,同时发现,在CALCULATE状态下,再按一下key,理论上应该是WAIT模式,但是角度数据还在更新,于是怀疑是不是I2C的WAIT并不是真的WAIT。
原来是Kalman_Filter的calib_done没有输出连到Kalman_Ctrl上,wtm裂开
连好后,问题仍存在o(≧口≦)o
继续找bug,发现将I2C切换为Wait模式时,前一个连续读取的停止信号没有发送完就进入了等待状态,可能导致I2C从机一直以为没有停止,从而导致后续数据读取错误。
修复这个bug后(如下图),上位机接收的误码率从100%降到了30%左右!!!说明之前的主要问题就是这个!
BUG2:
接下来观察传过来的数据:
发现其中还是有个别数据传输错误,例如把帧开头的AA、FF传成53、F7,不过这次有个好消息,就是没有掉数据或莫名多数据的情况发生(因为FIFO一直没有满)。
同时,debug发现计算出的最优角度值也有问题,例如下图:
此时MPU是平放状态,但是放大一百倍后的数值仍然有-2295,那么原值就是-22.95°,这很离谱。暂时先不管,我们先看UART传输错误的问题。
通过计算一行数据的校验和1(例如截图中的第一行,即29957行),把53、F7当作原来的AA和FF来计算checksum1,发现开头两个字节确实是由于误码造成的:
但是!发现checksum2还是有问题的,发现是少加了一次,但是修改后,上位机接收的checksum2还是有问题!应该也属于误码
分析一下可能的原因:
波特率产生不稳从而导致误码,因为是通过任意分频器产生的,有误差
波特率过大导致误码率增大
第一次尝试,增加任意分频器的精度(以及增加bps_en对baud_gen进行重置),稍微降低了一点误码率,但还是在25%左右,失败
第二次尝试,降低波特率,首先降低一半,到57600
这次可喜的是,checksum1和checksum2传输都完全正确,且最开始的几十组数据都没有出现误码!!!
但是到了后面仍然出现错位(下图),查看一下FIFO的full信号,原来是FIFO又满了呀。
重新计算一下需要的mpu timer频率,57600/11≈5236 Bytes,5236/13≈402帧,所以要降低timer的频率到400Hz
误码率很不稳定,有时15%,有时99%。
直接降到9600,9600/11/13=67………wtf????为什么误码率又飙升到100%,麻了
修复粗心bug:
读取acc和gyro的数据量是14字节,不是7字节
acc_rdy和gyro_rdy产生的时间早了一个周期
现在重新分析原因:
- UART模块传输数据不稳定的原因
- UART中FIFO的写入和读出不一致的原因(时序导致的)
3.23更新!!!
死活也没想到,还是因为我的代码写的有问题(つ﹏⊂),但是我之前是有单独验证这个模块的,只不过没有模拟实际的情况来写testbench,今天想着单独测试一下UART模块,为此甚至买了一个逻辑分析仪!然后想着还是模拟实际传输情况写一个testbench吧,结果就!发现了问题!
主要问题出在波特率发生器的使能信号上:
1 | assign bps_clk_en = ((!fifo_empty_in) & bps_en); //这是有bug的版本 |
原来的写法导致了在读取FIFO中最后一个数据之后,由于FIFO_EMPTY
立马变成了1,导致bps_clk_en
变为0,进而产生不了波特率,一直卡在START状态,从而导致后续的数据传输出错。
修改后的时序如下(主要区别在bps_clk
、bps_clk_en
两个信号):
经测试,可以正常发送,误码率为0%!!!太不容易了/(ㄒoㄒ)/~~
通过这个bug,得到一个教训,就是写testbench,不要觉得麻烦而随便写一个,尽量按照实际的数据产生流程来写,以便发现BUG。
BUG3:
解决掉数据传输的BUG后,还剩一个数据计算的BUG,这还算是一个正常的BUG。
BUG3的现象是,ROLL和PITCH极不稳定,反复横跳,不过经过一段时间后,ROLL会稳定下来,PITCH则仍然反复横跳。
测试计划:
- 查看I2C读出的原始数据观察是否正常
调试过程中还出现了一个小故障,FPGA开发板和插着MPU6050的面包板是通过杜邦线连接的,有时一动面包板就会导致I2C读不出数据,数码管显示卡住,估计是因为杜邦线的接触不良,毕竟很松动,于是改为直接插在开发板上,故障解决。
PS:这个经验很重要!!!一定要要保证数据传输的正确性!!!
然后惊奇地发现,ROLL数据变得正常了?!!不再反复横跳,能正确计算ROLL角度的实时变化。然后就只剩PITCH角度需要调试了。
等等!!!麻了,遇到一个更诡异的现象,就是,经过一段时间之后,ROLL和PITCH的情况交换了,也就是,ROLL变得反复横跳,PITCH则是能正常测量。然后。。。再过一段时间,又会交换,绝了。算了,一步一步DEBUG吧。
经过观察,MPU6050读出的原始数据没有问题。
接下来进一步DEBUG。
- 观察原始的,未经滤波的ROLL和PITCH
在经过Kalman_Iter_Unit之前,roll、pitch、gyro_x_fp、gyro_y_fp都是正常的。所以问题出在迭代部分,这次我准备先写一个tb来对Iter模块进行仿真。
果然,tb的结果出错了,这次的情况是,开始的一段时间并没有异常,随着计算的轮数增加,PITCH越来越偏,开始反复横跳。
按照数据流向观察计算过程,发现UPDATE模块计算的divident1和K1的值很奇怪,仔细一看,发现由于K_0和K_1晚K_done一个周期,而后续的angle_temp和bias_temp的计算都会用到新的K_0、和K_1的结果,但是这里采集的仍然是老的K_0和K_1(step的上升沿),所以导致了后续结果的错误。
要解决这个问题就是把K_done延迟一个周期,修改后的结果如图:
同时发现一个事实:每经过一轮更新,ROLL和PITCH的P矩阵的值都是一样的。因为P的初始值都相同,P的更新过程都是一样的。
从这次的经验来看,好好写tb是多么重要!!!