STM32驱动OV7670(无FIFO)相关问题的解决

STM32驱动OV7670(无FIFO)相关问题的解决



前言

之前入手了一块OV7670,直到最近才想着研究研究。首先是直接驱动摄像头显示在LCD上,然后通过串口输出到上位机显示。发现用了串口之后,串口占用CPU时间过长,导致图像刷新就很慢,于是用了DMA与串口进行改进。

后来听说HAL库扩展性很好,而且官方已经停止维护标准库了,所以HAL库可能才是未来的方向。于是又利用HAL库与STM32CUBEMX重新实现了一遍。这样我对HAL库也有了开发的经验了,总之收获很大。

最后把资源传上来了,大家自取吧。
https://download.csdn.net/download/wing_man/85251101?spm=1001.2014.3001.5501


一、OV7670大体介绍与需要关注的点

首先,我们第一步应当对其管脚做相应的了解。看下图
在这里插入图片描述
3.3V和GND: 不必多说,供电用。

SCL与SDA: 并不是I2C总线,这里是 SCCB总线所用管脚,与I2C总线很像。主要是用来配置OV7670内部寄存器用的。这个网上资料很多,这里只是说明以下管脚的用途。

VS和HS: VSYNC与HSYNC,图像输出的同步信号。

PCLK与MCLK: PCLK是摄像头输出给32的,MCLK是32输入给摄像头的。

D7-D0: 图像信号输出所用的数据线。

二、遇到的问题及解决

1.引脚冲突

我是在别人代码上做的更改,而且用的标准库。所以对引脚的定义并没有太过深究。下面说明问题, STM32精英版的LCD背光引脚为PB0,而在代码中PB0-7已经被定义为了数据引脚D0-D7。PB0本来在初始化LCD是定义为推挽输出高电平, 后来对OV7670时又被配置成了下拉输入。 于是LCD背光就很闪。

后来转HAL库时利用STM32CUBEMX可视化引脚定义时,发现了这个问题。后来把数据引脚初始化为PC0-PC7时就没有了这个现象。

2.DMA只能发送一次的问题

这个比较坑,初始化DMA后,开启DMA传送后只能传送一次,然后就卡住了。这是因为DMA没有被重新使能,解决方法也很简单,开启一次DMA传送后,在下一次将要开启DMA传送前重新失能使能,然后再开启DMA传送即可。

3.DMA发送数据对齐问题

我定义的采集到的图像数组为16为无符号整型,而串口的发送寄存器为八位的。
在这里插入图片描述
然而上位机只能接收灰度图(不是自己编写的,只能如此),OV7670输出的为RGB565格式。所以将RGB565格式转为灰度图后,每一个地址上的数据高8位均为0。比如0xabcd转成0xfa00, 0x1234转为0xba00(并不是实际准确的数值,只是说明格式而已)。

那么按照上卖弄的数据,我们DMA发送时应当发送0xfa, 0xba。高8位的00被舍掉。利用这个特性,引出DMA的数据对齐问题。看下图

在这里插入图片描述
按道理来说,我们memory设为半字,peripheral设为byte刚刚好,此时DMA应当传送0xfa,0xba,这样才符合逻辑。然而DMA传送的实际上位0xfa,0x00,0xba,0x00。即高8位并没有舍去,这点很蹊跷。于是只能在要传送的数据上动手脚。

算法:将0xaa00, 0xbb00, 0xcc00, 0xdd00转为0xaabb,0xccdd(类似格式)

void pic_reject(void)
{
	u16 *addr = &Image_Use[0][0];   //要处理的图片数组 
	int i,j;
	for(i=0;i<PIC_WIDTH/2;i++)     //Iamge_Use[PIC_WIDTH][PIC_LENGTH]
	{
		for(j=0;j<PIC_LENGTH;j++)
		{
			*(addr+i*PIC_LENGTH+j) = (*(addr+(2*(i*PIC_LENGTH+j))) ) | ((*(addr + (2*(i*PIC_LENGTH+j))+1)) << 8); 
		}
	}
}

只能先这样解决,具体也没有什么更好的办法。 后续看看能不能找到DMA不能对齐的原因。

4.OV7670图片发白

这可能是输入到OV7670 MCLK引脚的时钟速度不对。 必须满足OV7670操作时序才能得到正确的结果,因此MCLK不论过快还是过慢都会导致OV7670操作时序不匹配,导致图像出现各种各样的问题。 我利用STM32CUBEMX将PA8的速度配置为36MHz解决了这个问题,(8MHz的时候出现的这个问题)。

5.上位机图片下方模糊

这个我不知道是什么原因,我猜测应该是上位机软件设计的原因。毕竟我将串口速度在提高一点的时候,上位机软件就不行了,所以我很怀疑上位机软件设计是否合理。
在这里插入图片描述
下方有明显的横条纹。

6.一个比较细节的问题

看代码

int count = 0;
while(1)
{
	if(count == 0)
	{
		xxx;
		count++;
	}
	else
	{
		xxx;
	}
	count=count+1;
}
	

请思考,在第一循环执行完xxx后,程序会不会进入else中执行yyy呢?他惊醒我要时刻注意细节。答案是不会。
好,那么在思考第二次循环,程序会不会进入else中执行xxx呢?答案很简单,会进入,但是调试的时候则很不明显。

if中的xxx和else中的xxx是相同的。在keil中,若你设置编译器优化程度高,那么编译器对你的代码进行优化后。执行到else中,他会转到if中的xxx去执行,因为这样减小程序的大小,毕竟STM32的SRAM就512K。

那么我是怎么发现的呢? 碰巧看到了keil代码窗口上方的汇编结果。 汇编代码直接BL到if中的xxx处。 所以学会ARM汇编还是挺有用的,可以不精通,但一定能看懂。否则你连32启动前初始化的程序都搞不明白。

7.帧率提高的一些思考

STM32虽然性能相对较高,但想要达到实时的效果还是不太现实的。想要提高帧率,可以配置主时钟到72M,AHB,APB都配置到stm32所能承受的最高速度。如果有可能的话,尽可能提高输送给OV7670的MCLK的速度,但要注意OV7670的操作时序图,在能满足时序要求的情况下尽可能提高MCLK的速度。

其次对于一些硬件能实现的功能尽量用硬件去实现,若要软件实现那么必然会占用CPU资源,导致速度变慢。

其次时间分配要合理,比如我的串口速度为115200波特,DMA发送。即使我启动DMA发送后不去管它,立刻去读取OV7670的数据,显示在LCD上,仍然要等上1秒左右DMA才能发送完。 那么在提高一倍或者多倍串口速度将会比较均衡。 但是这时候要考虑误码率的因素,如果硬件支持的话还是尽量提高串口速度。


总结

收获蛮多,简单总结一下。
1,操作一个设备之前,要对这个设备有大致的了解。我这次成功只是运气好,乱改一通恰好问题解决。若要问题没有解决呢?那么必须看时序操作手册,看寄存器配置,借助示波器看看时序是否符合等等。实际是很复杂的,所以尽量先看设别手册对设备有了具体的了解之后在进行操作。
2,能用硬件尽量用硬件,尽量少占用CPU资源,各个任务的时间要分配好,尽量均衡一点。
3,不要固步自封,比如标准库我用着很顺手,但是官方已经停止维护了,某些32芯片也不支持标准库了,那么我们可以早点转HAL库。当然这并不意味着要抛弃标准库,只是我们要尽早的接触新的东西,比如HAL库。
4,理清思路再去进行操作,不要边操作边想办法,这样很容易走入死胡同。
5,实在想不出问题可以先出去走走,否则也容易走入死胡同。
6,问题解决后想一想它的深层原因,为什么这样就解决了。

技术要点和感悟均已总结完毕,期待我们大家都能共同进步。

  • 37
    点赞
  • 188
    收藏
    觉得还不错? 一键收藏
  • 43
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 43
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值