最近开了一个四轴飞行器的坑,把一直在吃灰的STM32H7的板子拿出来用了用。正好机架啥的都还没有,就打算从小模块开始,于是拿出了一块SPI显示屏。在玩H7的时候,知道它M7的内核有Cache,果断开起来,白赚的速度不要白不要。。。本应该是这样的,直到我给SPI打开了DMA。
对于SPI的控制,我开辟了一块空间做“显存”,显存数据定时通过SPI传给显示屏,而绘图都在显存内进行绘图。这个SPI屏是240*240的分辨率,SPI波特率30M,算下来RGB565的彩点,刷屏速率只有30Hz左右,还会把CPU资源给占完。这哪行啊,CPU占满了拿什么去飞控←_←。于是我打开了DMA。。。这下惨了,原本正常的显示屏上千疮百孔,总有些区域显示不全。

而且奇怪的是,只要打开DMA,必出现上述现象;而只要关闭DMA,上述现象就消失了。玩个单片机还能闹鬼了不成?作为一名程序员,发现问题首先问自己又错在哪里了。因为计算机总是忠实的按照自己的编程运行,闹鬼是不可能的。就这样折腾了老半天,在一次调试的过程中,突然发现,如果在启动DMA写数据前,先遍历访问一次“显存”区域,会使现象减弱或者消失。这时,即便对Cache了解并不深入的我,也终于意识到了问题所在,果断关闭Cache,果不其然,BUG消失了。
Cache的DMA之间会有数据一致性的问题。DMA全名Direct Memory Access,即直接内存访问,顾名思义,DMA访问总线前是不会经过Cache的。而Cache又名高速缓存,位于CPU和总线之间。Cache包括读缓冲和写缓冲,读缓冲指CPU想访问的总线地址如果已经在Cache中,则直接从Cache中读;写缓冲指CPU写总线会先写到Cache中,直到满足某些条件,Cache的数据才会写入内存。
所以问题很明显了,程序写了“显存”区域之后,有的数据还在Cache中,未写入内存;而这是DMA开始工作,直接从内存中读取数据,读到的就不是完整的数据。同理,如果DMA从外设将数据送回内存,CPU在读的时候也可能因为Cache读缓冲导致没有读到最新的数据。
解决方法也很简单:
- 关Cache。但这不是个好办法,既然有Cache,那自然是想用的,把它关了不就亏了。
- 设置Cache熟悉。Cache可以设置直接读写某块地址空间,这应该是比较好的办法。
- 手动刷新Cache。即在DMA发送前,手动刷新Cache,保证数据已经回写到内存中。
问题根本原因还是对硬件不够熟悉,想做嵌入式开发,对硬件的熟悉性真的很重要!
Read More