关于S5P4418使用SPI+DMA传输时出现的超时问题
一.问题背景
在使用nanopi2进行连接一个SPI接口的2.8寸TFT彩屏,在使用过程中发现了两个问题,第一个问题是友善提供的linux3.4内核SPI默认没使用DMA传输。第二个问题是 DMA传输会出现超时 ,超时还会导致linux内核发生崩溃。
通过debugfs可以看到SPI的DMA传输发生了堵塞:
本人使用的TFT屏是2.8寸的分别率是 240x320 ,色彩深度是RGB565,每 个像素点2个字节,一帧图像是153600字节,大约153KB,如果不使用DMA传输,发现即使使用50MHZ,4418传输一帧图像仍然需要160毫秒左右,如果使用DMA一帧大概25毫秒,因此需要做到40HZ刷新率,必须要使用DMA!!!
附上nanopi2的内核源码的github:
https://github.com/friendlyarm/linux-3.4.y.git
编译内核的方法:
make nanopi2_linux_defconfig
make uImage
详情请参考维基: https://wiki.friendlyelec.com/wiki/index.php/NanoPi_M2/zh
点击此处进入维基: https://wiki.friendlyelec.com/wiki/index.php/NanoPi_M2/zh
经过反复阅读代码,发现代码并无逻辑问题,编码测试发现核心的BUG出现在SPI上,在高速DMA传输过程中,会导致SPI的接收FIFO出现数据丢失,比如通过SPI传输5000字节,SPI的MOSI发送完毕这5000字节,MISO收到的数据理论也是5000字节,但是DMA把MISO数据搬到内存过程有概率不及时,从而导致RX FIFO堆满出现了溢出,最后导致DMA无法从RX FIFO搬出指定长度的数据,接收DMA中断就无法产生,进而SPI主机驱动的回调函数也无法被调用,从底层到顶层一步步阻塞,最后挤压的DMA请求导致linux内核崩溃。
原因大概率是ARM处理器的DMA和SPI控制器相性问题所致,属于硬件问题吧,因此仅能通过软件弥补,保护内核不崩溃,对于显示屏确保MOSI数据流正常,就算得上可靠了!!
二.启用SPI的DMA传输
默认从git中克隆下来的内核,友善官方是禁用了SPI主机驱动中DMA功能,需要自己来打开。
打开DMA方式如下:
2.1 修改cfg_main.h 文件
arch/arm/plat-s5p4418/nanopi2/include/cfg_main.h
把cfg_main.h 文件中传输方式改成2,默认内核支持轮询,中断,DMA,传输,其中DMA传输最快对CPU负荷最低,时钟源频率改成100MHZ,最低2分频,即SPI传输最高可以得到50MHZ。
具体可以参考SPI的章节,时钟源最大100MHZ,主机模式下SPI最大50MHZ。
2.2 make menuconfig 配置SPI
Device Drivers —>
[*] SPI support —>
修改上面的菜单,以SPI2总线为例子勾选中port 2,和使用DMA模式传输。
2.3 修改SPI主机驱动代码
主机驱动
drivers/spi/spi-pl022.c
把上图的触发条件改成下图所示:
注意:由于S5P4418的SPI外设固定是以4字节为突发传输长度,这个是不能修改的,因此SPI必须以4字节为一次DMA突发传输,比如用户希望用DMA传输3个字节将会导致DMA控制器和SPI不协调出错。
由于DMA支持分散形式的链表传输,因此DMA单次传输长度理论上是不限的,因为我们一帧图像大概也是接近200KB,所以我们需要删除少于4096的条件。
2.4 增加设备驱动的代码
上面步骤过后主机驱动已经修改完成了,对于设备驱动(如显示屏)就不展示了,比如我用的一个st7789的显示屏。
三.修改DMA控制器驱动程序
经过反复的裸机程序测试,定位到主要是DMA驱动的中断出现异常,因此修改了S5P4418内核中的DMA驱动程序。
四.效果
使用DMA传输图像SPI总线上是很紧凑的,字节和字节的间隙大概40ns如下图:
使用中断或轮询的方式的SPI总线是很疏松,字节和字节的间隙大概25us,甚至50us不等,而且消耗CPU:
因为在50MHZ主频下传输4个字节仅大概需要760ns,如下图所示,又因SPI中断模式是每发送4个字节中断一次,每760ns让CPU响应并处理一次中断,这样是不可能的,所以响应不及时往往导致数据不能及时填入发送FIFO,导致发送数据疏松。
在50MHZ的SPI下中断模式发送最终必然导致发送效果不尽人意,因此DMA模式下可以大大降低CPU中断的触发,提高传输效率。
修改前,使用DMA传输,每5帧左右就会导致内核崩溃或者卡死,修改后,本人经过2天晚上测试,一晚上跑20万帧,一点问题都没有,S5P4418 SOC还是挺强了,但是对于低速的SPI外设需要用到全双工,尽量控制主频在20MHZ下使用DMA收发都能正常,超过40M通常无法保证接收正常,但可以保证发送是正常的!!!
五.补丁包和补丁包的使用
由于修改的过程比较复杂,很多老哥可能看不懂,或者跟不上,下面给出最简单的修复步骤,附件中会提供补丁包。
克隆内核源码:
git clone https://github.com/friendlyarm/linux-3.4.y.git
cp 0001-Fixed-__spi_sync-timeout-when-using-50MHZ-and-DMA.patch linux-3.4.y
将补丁包0001-Fixed-__spi_sync-timeout-when-using-50MHZ-and-DMA.patch下载到内核源码目录下:
补丁包下载链接: https://download.csdn.net/download/weixin_43536180/87364903
使用补丁包:
git am < 0001-Fixed-__spi_sync-timeout-when-using-50MHZ-and-DMA.patch
查看是否生成新的提交:
git log
重新编译内核
make nanopi2_linux_defconfig
make uImage
编译后会生成新的uImage 镜像文件!!
更新内核到nanopim2开发板:
mount mount /dev/mmcblk0p1 /mnt/
拷贝新镜像到开发,有些时候更新的是uImage.hdmi,因为默认不接LCD屏幕是启动这个HDMI显示的内核!!
cp uImage /mnt/uImage