|
本帖最后由 lcw_swust 于 2015-10-10 15:42 编辑
首先上传电路和程序:
还有两个VB上位机用于调试:
---------------------------------------------------------------------------------------------
简介:
此模块是利用STM32F103CBT6驱动OV7670摄像头模组,可作简单的图像处理,打算用于四轴飞行器的悬停。
实际应用中可能会不理想,假如环境太单调,那效果就不好。
听说PX4FLOW做得挺好,查了下好像挺贵。
---------------------------------------------------------------------------------------------
OV7670驱动原理:
电源(DOVDD,AVDD)为2.5V左右(HT7333输出串1N4148降压,因为我没有HT7325),
DVDD采用OV7670内部的LDO(1.8V),外部只需接一只电容.
单片机为STM32F103CBT6,采用HSI时钟,PLL倍频到64MHz,可省去外部晶振.
由于只需要处理灰度图像,OV7670配置为YUV格式,Y在前,
TIM4_CH1的捕获分频设置为2分频,就可以只采集Y(亮度).
TIM3_CH3产生XCLK,(16MHz对应VSYNC约20HZ,实测最低约8MHZ)
TIM4_CH1检测PCLK(4MHz),下降沿捕获,滤波为1,二分频,触发DMA,采集PA0~PA7的数据至数组DMA_Buf.
TIM3_CH4检测VSYNC,下降沿捕获中断,控制DMA的关与开.
HREF忽略.
引脚连接:
PA12: LED
PA0~7: OV7670_D0~D7
PB0(TIM3_CH3): OV7670_XCLK
PB1(TIM3_CH4): OV7670_VSYNC
PB2: OV7670_RST
PB7(TIM4_CH2): OV7670_HREF(可省略)
PB6(TIM4_CH1): OV7670_PCLK
PB10: OV7670_SIO_C
PB11: OV7670_SIO_D
PB12: OV7670_PWDN
建议:
为加快速度,SIO_C与SIO_D可加外部上拉电阻
注意:
看了下SCCB时序,貌似与IIC略有区别,指定地址读数据时:
OV7670: ...IICWByte(add);IICACK(0);IICEnd();IICStart();...
普通IIC:...IICWByte(add);IICACK(0); IICStart();...
---------------------------------------------------------------------------------------------
图像处理:
上次图像中部区域(按STEP间隔)存入数组MID_Buf,在当前采集的
图像中遍历,寻找差值最小的位置,从而得到座标增量。
程序中配置摄像头为YUV格式,窗口为160*80像素,得到80*80的Y(亮度)数据;
中心区为40*40像素,STEP设置为5,也就成了8*8的稀疏点阵,处理一幅图像大约耗时45ms.
(由于接收图像还需要时间,所以程序中约100ms处理一次)
程序经KEIL3编译后:Code=12054 RO-data=358 RW-data=48 ZI-data=8608;
可以看出代码量和内存消耗都不大。
图像处理核心算法:
(注意,由于二维数组访问速度较低,真实的代码略有优化,详见附件)
- //--------------------------------------------------
- //变量定义
- //--------------------------------------------------
- U8 DMA_Buf[CAMHEIGHT][CAMWIDTH];//本次采集的摄像头数据
- U8 MID_Buf[MIDHEIGHT][MIDWIDTH];//上次中部区域的数据
- //--------------------------------------------------
- //计算差值的绝对值
- //--------------------------------------------------
- __INLINE U8 caldif(U8 v1,U8 v2)
- {
- if(v1>v2)return v1-v2;
- return v2-v1;
- }
- //--------------------------------------------------
- //图像处理:用上次图像中部区域去与当前采集图像(从x,y
- //开始的区域)求差,寻找差值最小的位置,
- //从而得到x增量(MovIncX)与y增量(MovIncY)
- //--------------------------------------------------
- void PicProcess(void)
- {
- U16 i,j,x,y;
- U32 dif,min=0xffffffff; //差值,最小值
- U16 minx,miny; //最小值处的左上角座标
- //--------遍历整个当前图像,查找与之前中部图像相差最小的位置
- for(y=0;y<CAMHEIGHT-MIDHEIGHT;y++)//从上到下
- {
- for(x=0;x<CAMWIDTH-MIDWIDTH;x++)//从左到右
- {
- dif=0;//误差清0
- for(i=0;i<MIDHEIGHT;i+=STEP)
- {
- for(j=0;j<MIDWIDTH;j+=STEP)
- {//计算当前图像中的点与上次图像中部区域的点的差值
- dif+=caldif(DMA_Buf[y+i][x+j],MID_Buf[i][j]);//误差累加
- }
- }
- if(min>dif)//误差小于当前最小值,则记最小值为当前值,且记下座标
- {
- min=dif;
- minx=x;
- miny=y;
- }
- }
- }
- //------------------------------计算座标增量
- if(min>30000)min=30000;//此句可省,是为了便于串口发送该值
- if(min<15*(MIDHEIGHT/STEP)*(MIDWIDTH/STEP))//允许平均每个点15的误差
- {
- MovIncX=minx-MIDX;//x增量
- MovIncY=miny-MIDY;//y增量
- MovX+=MovIncX;//模拟成鼠标的绝对座标,便于查看
- MovY+=MovIncY;
- if(MovX<-1000)MovX=-1000;//限幅
- if(MovX>1000)MovX=1000;
- if(MovY<-1000)MovY=-1000;
- if(MovY>1000)MovY=1000;
- }
- //------------------------------复制当前中部区域
- for(i=0;i<MIDHEIGHT;i+=STEP)
- {
- //memcpy(MID_Buf[i],DMA_Buf[MIDY+i]+MIDX,MIDWIDTH);
- for(j=0;j<MIDWIDTH;j+=STEP)
- {
- MID_Buf[i][j]=DMA_Buf[MIDY+i][MIDX+j];
- }
- }
- //------------------------------串口查看误差与座标
- UART1_SendByte(0xaa);
- UART1_SendByte(6);
- UART1_SendByte(min>>8);
- UART1_SendByte(min);
- UART1_SendByte(MovX>>8);
- UART1_SendByte(MovX);
- UART1_SendByte(MovY>>8);
- UART1_SendByte(MovY);
- }
复制代码
下面是一些照片:
模块电路和照片:
将采集的图像发送出来用上位机查看:
将误差和座标发送出来用上位机查看:
8*8点阵去匹配当前图像的效果图,为避免GIF太大,只做了前5行的效果:
发送座标时的GIF图:
---------------------------------------------------------------------------------------------
补充一下:
测了下工作电流,约47mA
“处理一幅图像大约耗时10ms",这里写错了,应为45ms.
忘了传OV7670的资料了:
|
本帖子中包含更多资源
您需要 登录 才可以下载或查看,没有帐号?注册
x
阿莫论坛20周年了!感谢大家的支持与爱护!!
你熬了10碗粥,别人一桶水倒进去,淘走90碗,剩下10碗给你,你看似没亏,其实你那10碗已经没有之前的裹腹了,人家的一桶水换90碗,继续卖。说白了,通货膨胀就是,你的钱是挣来的,他的钱是印来的,掺和在一起,你的钱就贬值了。
|