初衷;将摄像头放在防盗门猫眼位置,访客到来时,给访客拍个照,然后传到房主端显示。 现在只完成了蓝牙传输,和WinForm窗体显示,后面时间来得及的话会陆续完成WiFi传输,和手机端APK显示。
常规思路:图像采集->图像处理->图像传输->图像显示
首先要做的就是图像采集:
系统采用32位单片机STM32F103ZET6,自己LayOut的PCB,中间可以直插FSMC驱动的TFT屏。 摄像头模块用的带FIFO的OV7670,最高输出是640*480@25Hz_16RGB,由于系统采用的是串口蓝牙传输,所以为了节省传输时间,决定只取320*240_4bits灰度图像传输。
OV要把一帧图像存储在FIFO中,等待MCU慢慢地读取,MCU读完一帧图片后,OV才会向FIFO写入新的一帧图片(摄像头VYSNC做FIFO读写中断即可),硬件装置如下图所示。
1、SCCB接口
初始化OV摄像头,需要通过SCCB接口配置其内部的一百多个寄存器。 SCCB就是个两线的接口协议,类似于IIC。SCL是时钟线,SDA是双向数据线。
2、调试心酸经验
刚开始调OV的时候,找了一大堆网友的程序,挨个试验,结果没一个管用的,于是耽搁了一段时间没搞,很是不甘心。 后来想着要理解理解代码,从SCCB协议开始,对照着芯片手册上的Register Map理解。
1) 先确保SCCB的时序正确,对照着DataSheet上的SCCB Timing Diagram检查
2) SCCB时序无误后,检查是否可以正常读写寄存器。先读下图的两个寄存器,地址为0X0A和0X0B,若分别返回0X76和0X73则说明可以正常读寄存器。
然后再去写一个寄存器(不要写成了ReadOnly的寄存器),写完去读它,检查是否和自己写入的值相等。若相等的话,就表示可以正常写了。
3) 在调试过程中,我遇到过几次这样的情况:读出来的数据始终是0XFF,也就是说,每次按位读的时候,SDA总为高电平。很让我苦恼,同样的程序在STM32上可以用,为什么到其他平台上就不能用了呢。后来偶然发现了原因:因为我将SCCB时序程序中的delay_us(50)改成了delay_ms(1)。也就是说,延时变长了就不能正常读写了,切记!
4) 另外,介绍两个需要注意的寄存器,地址分别是0x12 (用来配置输出格式:RGB YUV QVGA等), 0x1e (用来配置输出水平垂直镜像)
5)一帧图像在FIFO内读写的控制,程序如下,作简单解读
//ov_sta:0,开始一帧数据采集
u8 ov_sta;
//外部中断5~9服务程序 , 接OV7670_VSYNC
void EXTI9_5_IRQHandler(void)
{
if(EXTI_GetITStatus(EXTI_Line8)==SET)//是8线的中断
{
if(ov_sta<2)
{
if(ov_sta==0)
{
OV7670_WRST_0;//复位写指针
OV7670_WRST_1;
OV7670_WREN_1;//允许写入FIFO
}else
{
OV7670_WREN_0;//禁止写入FIFO
OV7670_WRST_0;//复位写指针
OV7670_WRST_1;
}
ov_sta++;
}
}
EXTI_ClearITPendingBit(EXTI_Line8); //清除EXTI8线路挂起位
}
此IO中断连接OV7670_VSYNC,状态变量ov_sta初始为0,第一个OV7670_VSYNC上升沿来到,表示OV开始输出一帧图像,此时复位FIFO写指针,允许写入FIFO,ov_sta自增至1。第二个OV7670_VSYNC上升沿来到,表示OV此帧图像输出完毕,开始输出下一帧图像,此时进制写入FIFO,ov_sta自增至2。 这样就把一帧图像锁存在FIFO中等待MCU慢慢读取了,ov_sta为2的时候,OV7670_VSYNC的中断服务是不会有任何操作的,因为此时要等待MCU读完FIFO,并将状态变量ov_sta复位,程序如下:
extern u8 ov_sta;
//更新LCD显示
void camera_refresh(void)
{
u32 i,j;
u16 color;
// ili9320_SetCursor(0,0);
// LCD_WriteRAM_Prepare();
if(ov_sta==2)
{
OV7670_RRST_0;//开始复位读指针
OV7670_RCK_0;
OV7670_RCK_1;
OV7670_RCK_0;
OV7670_RRST_1;//复位读指针结束
OV7670_RCK_1;
for(i=0;i<240;i++)
for(j=0;j<320;j++)
{
OV7670_RCK_0;
color=GPIOC->IDR&0XFF;//读数据
usart1_send_char(color);
delay_ms(20);//留时间给zigbee传输数据
OV7670_RCK_1;
color<<=8;
OV7670_RCK_0;
color|=GPIOC->IDR&0XFF;//读数据
usart1_send_char(color);
delay_ms(20);//留时间给zigbee传输数据
OV7670_RCK_1;
// LCD_WriteRAM(color);
LCD_DrawPoint(239-i,j,color);
}
EXTI_ClearITPendingBit(EXTI_Line8); //清除LINE8上的中断标志位
ov_sta=0; //开始下一次采集
}
}