专家
公告
财富商城
电子网
旗下网站
首页
问题库
专栏
标签库
话题
专家
NEW
门户
发布
提问题
发文章
STM32
【ALIENTEK 战舰STM32开发板例程系列连载+教学】第四十一章 摄像头实验
2019-08-16 20:51
发布
×
打开微信“扫一扫”,打开网页后点击屏幕右上角分享按钮
站内问答
/
STM32/STM8
5515
33
1249
第四十一章
摄像头实验
ALIENTEK
战舰
STM32
开发板板载了一个摄像头接口(
P8
),该接口可以用来连接
ALIENTEK OV7670
摄像头模块。本章,我们将使用
STM32
驱动
ALIENTEK OV7670
摄像头模块,实现摄像头功能。本章分为如下几个部分:
41.1 OV7670
简介
41.2
硬件设计
41.3
软件设计
41.4
下载验证
41.1 OV7670
简介
OV7670
是
OV
(
OmniVision
)公司生产的一颗
1/6
寸的
CMOS VGA
图像传感器。该传感器体积小、工作电压低,提供单片
VGA
摄像头和影像处理器的所有功能。通过
SCCB
总线控制,可以输出整帧、子采样、取窗口等方式的各种分辨率
8
位影像数据。该产品
VGA
图像最高达到
30
帧
/
秒。用户可以完全控制图像质量、数据格式和传输方式。所有图像处理功能过程包括伽玛曲线、白平衡、度、 {MOD}度等都可以通过
SCCB
接口编程。
OmmiVision
图像传感器应用独有的传感器技术,通过减少或消除光学或电子缺陷如固定图案噪声、托尾、浮散等,提高图像质量,得到清晰的稳定的彩 {MOD}图像。
OV7670
的特点有:
l
高灵敏度、低电压适合嵌入式应用
l
标准的
SCCB
接口,兼容
IIC
接口
l
支持
RawRGB
、
RGB(GBR4:2:2
,
RGB565/RGB555/RGB444)
,
YUV(4:2:2)
和
YCbCr
(
4:2
:
2
)输出格式
l
支持
VGA
、
CIF
,和从
CIF
到
40*30
的各种尺寸输出
l
支持自动曝光控制、自动增益控制、自动白平衡、自动消除灯光条纹、自动黑电平校准等自动控制功能。同时支持 {MOD}饱和度、 {MOD}相、伽马、锐度等设置。
l
支持闪光灯
l
支持图像缩放
OV7670
的功能框图图如图
41.1.1
所示:
图
41.1.1 OV7670
功能框图
OV7670
传感器包括如下一些功能模块。
1.
感光整列(
Image Array
)
OV7670
总共有
656*488
个像素,其中
640*480
个有效(即有效像素为
30W
)。
2.
时序发生器(
Video Timing Generator
)
时序发生器具有的功能包括:整列控制和帧率发生(
7
种不同格式输出)、内部信号发生器和分布、帧率时序、自动曝光控制、输出外部时序(
VSYNC
、
HREF/HSYNC
和
PCLK
)。
3.
模拟信号处理(
Analog Processing
)
模拟信号处理所有模拟功能,并包括:自动增益(
AGC
)和自动白平衡(
AWB
)。
4.A/D
转换(
A/D
)
原始的信号经过模拟处理器模块之后
,分
G
和
BR
两路进入一个
10
位的
A/D
转换器,
A/D
转换器工作在
12M
频率,与像素频率完全同步(转换的频率和帧率有关)。
除
A/D
转换器外,该模块还有以下三个功能:
l
黑电平校正(
BLC)
l
U/V
通道延迟
l
A/D
范围控制
A/D
范围乘积和
A/D
的范围控制共同设置
A/D
的范围和最大值,允许用户根据应用调整图片的亮度。
5.
测试图案发生器(
Test Pattern Generator
)
测试图案发生器功能包括:八 {MOD}彩 {MOD}条图案、渐变至黑白彩 {MOD}条图案和输出脚移位“
1
”。
6.
数字处理器(
DSP
)
这个部分控制由原始信号插值到
RGB
信号的过程,并控制一些图像质量:
l
边缘锐化(二维高通滤波器)
l
颜 {MOD}空间转换(
原始信号到
RGB
或者
YUV/YCbYCr)
l
RGB
{MOD}彩矩阵以消除串扰
l
{MOD}相和饱和度的控制
l
黑
/
白点补偿
l
降噪
l
镜头补偿
l
可编程的伽玛
l
十位到八位数据转换
7.
缩放功能(
Image Scaler
)
这个模块按照预先设置的要求输出数据格式,能将
YUV/RGB
信号从
VGA
缩小到
CIF
以下的任何尺寸。
8.
数字视频接口(
Digital Video Port
)
通过寄存器
COM2[1:0]
,调节
IOL/IOH
的驱动电流,以适应用户的负载。
9.SCCB
接口(
SCCB Interface
)
SCCB
接口控制图像传感器芯片的运行,详细使用方法参照光盘的《
OmniVision Technologies Seril Camera Control Bus(SCCB) Specification
》这个文档
10.LED
和闪光灯的输出控制(
LED and Storbe Flash Control Output
)
OV7670
有闪光灯模式,可以控制外接闪光灯或闪光
LED
的工作。
OV7670
的寄存器通过
SCCB
时序访问并设置,
SCCB
时序和
IIC
时序十分类似,在本章我们不做介绍,请大家参考光盘的相关文档。
接下来我们介绍一下
OV7670
的图像数据输出格式。首先我们简单介绍几个定义:
VGA
,即分辨率为
640*480
的输出模式;
QVGA
,即分辨率为
320*240
的输出格式,也就是本章我们需要用到的格式;
QQVGA
,即分辨率为
160*120
的输出格式;
PCLK
,即像素时钟,一个
PCLK
时钟,输出一个像素
(
或半个像素
)
。
VSYNC
,即帧同步信号。
HREF /HSYNC
,即行同步信号。
OV7670
的图像数据输出(通过
D[7:0]
)就是在
PCLK
,
VSYNC
和
HREF/ HSYNC
的控制下进行的。首先看看行输出时序,如图
41.1.2
所示:
图
41.1.2 OV7670
行输出时序
从上图可以看出,图像数据在
HREF
为高的时候输出,当
HREF
变高后,每一个
PCLK
时钟,输出一个字节数据。比如我们采用
VGA
时序,
RGB565
格式输出,每
2
个字节组成一个像素的颜 {MOD}(高字节在前,低字节在后),这样每行输出总共有
640*2
个
PCLK
周期,输出
640*2
个字节。
再来看看帧时序(
VGA
模式),如图
41.1.3
所示:
图
41.1.3 OV7670
帧时序
上图清楚的表示了
OV7670
在
VGA
模式下的数据输出,注意,图中的
HSYNC
和
HREF
其实是同一个引脚产生的信号,只是在不同场合下面,使用不同的信号方式,我们本章用到的是
HREF
。
因为
OV7670
的像素时钟(
PCLK
)最高可达
24Mhz
,我们用
STM32F103ZET6
的
IO
口直接抓取,是非常困难的,也十分占耗
CPU
(可以通过降低
PCLK
输出频率,来实现
IO
口抓取,但是不推荐)。所以,本章我们并不是采取直接抓取来自
OV7670
的数据,而是通过
FIFO
读取,
ALIENTEK OV7670
摄像头模块自带了一个
FIFO
芯片,用于暂存图像数据,有了这个芯片,我们就可以很方便的获取图像数据了,而不再需要单片机具有高速
IO
,也不会耗费多少
CPU
,可以说,只要是个单片机,都可以通过
ALIENTEK OV7670
摄像头模块实现拍照的功能。
接下来我们介绍一下
ALIENTEK OV7670
摄像头模块。该模块的外观如图
41.1.4
:
图
41.1.4 ALIENTEK OV7670
摄像头模块外观图
模块原理图如图
41.1.5
所示:
图
41.1.5 ALIENTEK OV7670
摄像头模块原理图
从上图可以看出,
ALIENTEK OV7670
摄像头模块自带了有源晶振,用于产生
12M
时钟作为
OV7670
的
XCLK
输入。同时自带了稳压芯片,用于提供
OV7670
稳定的
2.8V
工作电压,并带有一个
FIFO
芯片(
AL422B
),该
FIFO
芯片的容量是
384K
字节,足够存储
2
帧
QVGA
的图像数据。模块通过一个
2*9
的双排排针(
P1
)与外部通信,与外部的通信信号如表
41.1.1
所示:
信号
作用描述
信号
作用描述
VCC3.3
模块供电脚,接
3.3V
电源
FIFO_WEN
FIFO
写使能
GND
模块地线
FIFO_WRST
FIFO
写指针复位
OV_SCL
SCCB
通信时钟信号
FIFO_RRST
FIFO
读指针复位
OV_SDA
SCCB
通信数据信号
FIFO_OE
FIFO
输出使能(片选)
FIFO_D[7:0]
FIFO
输出数据(
8
位)
OV_VSYNC
OV7670
帧同步信号
FIFO_RCLK
读
FIFO
时钟
表
41.1.1 OV7670
模块信号及其作用描述
下面我们来看看如何使用
ALIENTEK OV7670
摄像头模块(以
QVGA
模式,
RGB565
格式为例)。对于该模块,我们只关心两点:
1
,如何存储图像数据;
2
,如何读取图像数据。
首先,我们来看如何存储图像数据。
ALIENTEK OV7670
摄像头模块存储图像数据的过程为:等待
OV7670
同步信号
à
FIFO
写指针复位
à
FIFO
写使能
à
等待第二个
OV7670
同步信号
à
FIFO
写禁止。通过以上
5
个步骤,我们就完成了
1
帧图像数据的存储。
接下来,我们来看看如何读取图像数据。
在存储完一帧图像以后,我们就可以开始读取图像数据了。读取过程为:
FIFO
读指针复位
à
给
FIFO
读时钟(
FIFO_RCLK
)
à
读取第一个像素高字节
à
给
FIFO
读时钟
à
读取第一个像素低字节
à
给
FIFO
读时钟
à
读取第二个像素高字节
à
循环读取剩余像素
à
结束。
可以看出,
ALIENTEK OV7670
摄像头模块数据的读取也是十分简单,比如
QVGA
模式,
RGB565
格式,我们总共循环读取
320*240*2
次,就可以读取
1
帧图像数据,把这些数据写入
LCD
模块,我们就可以看到摄像头捕捉到的画面了。
OV7670
还可以对输出图像进行各种设置,详见光盘《
OV7670
中文数据手册
1.01
》和《
OV7670 software application note
》这两个文档,对
AL422B
的操作时序,请大家参考
AL422B
的数据手册。
了解了
OV7670
模块的数据存储和读取,我们就可以开始设计代码了,本章,我们用一个外部中断,来捕捉帧同步信号(
VSYNC
),然后在中断里面启动
OV7670
模块的图像数据存储,等待下一次
VSHNC
信号到来,我们就关闭数据存储,然后一帧数据就存储完成了,在主函数里面就可以慢慢的将这一帧数据读出来,放到
LCD
即可显示了,同时开始第二帧数据的存储,如此循环,实现摄像头功能。
本章,我们将使用摄像头模块的
QVGA
输出(
320*240
),刚好和战舰
STM32
开发板使用的
LCD
模块分辨率一样,一帧输出就是一屏数据,提高速度的同时也不浪费资源。注意:
ALIENTEK OV7670
摄像头模块自带的
FIFO
是没办法缓存一帧的
VGA
图像的,如果使用
VGA
输出,那么你必须在
FIFO
写满之前开始读
FIFO
数据,保证数据不被覆盖。
41.2
硬件设计
本章实验功能简介:开机后,初始化摄像头模块(
OV7670
),如果初始化成功,则在
LCD
模块上面显示摄像头模块所拍摄到的内容。我们可以通过
KEY0
设置光照模式(
5
种模式)、通过
KEY1
设置 {MOD}饱和度,通过
KEY2
设置亮度,通过
WK_UP
设置对比度,通过
TPAD
设置特效(总共
7
种特效)。通过串口,我们可以查看当前的帧率(这里是指
LCD
显示的帧率,而不是指
OV7670
的输出帧率),同时可以借助
USMART
设置
OV7670
的寄存器,方便大家调试。
DS0
指示程序运行状态。
本实验用到的硬件资源有:
1)
指示灯
DS0
2)
5
个按键(包括
TPAD
触摸按键)
3)
串口
4)
TFTLCD
模块
5)
摄像头模块
ALIENTEK OV7670
摄像头模块在
41.1
节已经有详细介绍过,这里我们主要介绍该模块与
ALIETEK
战舰
STM32
开发板的连接。
在开发板的左下角的
2*9
的
P8
排座,是摄像头模块
/OLED
模块共用接口,在第十七章,我们曾简单介绍过这个接口。本章,我们只需要将
ALIENTEK OV7670
摄像头模块插入这个接口即可,该接口与
STM32
的连接关系如图
41.2.1
所示:
图
41.2.1
摄像头模块接口与
STM32
连接图
从上图可以看出,
OV7670
摄像头模块的各信号脚与
STM32
的连接关系为:
OV_SDA
接
PG13
;
OV_SCL
接
PD3
;
FIFO_RCLK
接
PB4
;
FIFO_WEN
接
PB3
;
FIFO_WRST
接
PD6
;
FIFO_RRST
接
PG14
;
FIFO_OE
接
PG15
;
OV_VSYNC
接
PA8
;
OV_D[7:0]
接
PC[7:0]
;
这些线的连接,战舰
STM32
的内部已经连接好了,我们只需要将
OV7670
摄像头模块插上去就好了。实物连接如图
41.2.2
所示:
图
41.2.2 OV7670
摄像头模块与开发板连接实物图
41.3
软件设计
打开上一章的工程,首先在
HARDWARE
文件夹下新建一个
OV7670
的文件夹。然后新建如下文件:
ov7670.c
、
sccb.c
、
ov7670.h
、
sccb.h
、
ov7670cfg.h
等
5
个文件,将他们保存在
OV7670
文件夹下,并将这个文件夹加入头文件包含路径。
本章总共新增了
5
个文件,代码比较多,我们就不一一列出了,仅挑两个重要的地方进行讲解。首先,我们来看
ov7670.c
里面的
OV7670_Init
函数,该函数代码如下:
u8 OV7670_Init(void)
{
u8 temp; u16 i=0;
//
设置
IO
RCC->APB2ENR|=1<<2; //
先使能外设
PORTA
时钟
RCC->APB2ENR|=1<<3; //
先使能外设
PORTB
时钟
RCC->APB2ENR|=1<<4; //
先使能外设
PORTC
时钟
RCC->APB2ENR|=1<<5; //
先使能外设
PORTD
时钟
RCC->APB2ENR|=1<<8; //
先使能外设
PORTG
时钟
GPIOA->CRH&=0XFFFFFFF0;
GPIOA->CRH|=0X00000008; //PA8
输入
GPIOA->ODR|=1<<8;
GPIOB->CRL&=0XFFF00FFF;
GPIOB->CRL|=0X00033000; //PB3/4
输出
GPIOB->ODR|=3<<3;
GPIOC->CRL=0X88888888; //PC0~7
输入
GPIOC->ODR|=0x00ff;
GPIOD->CRL&=0XF0FFFFFF; //PD6
输出
GPIOD->CRL|=0X03000000;
GPIOD->ODR|=1<<6;
GPIOG->CRH&=0X00FFFFFF;
GPIOG->CRH|=0X33000000;
GPIOG->ODR=7<<14; //PG14/15
输出高
JTAG_Set(SWD_ENABLE);
SCCB_Init(); //
初始化
SCCB
的
IO
口
if(SCCB_WR_Reg(0x12,0x80))return 1; //
复位
SCCB
delay_ms(50);
//
读取产品型号
temp=SCCB_RD_Reg(0x0b);
if(temp!=0x73)return 2;
temp=SCCB_RD_Reg(0x0a);
if(temp!=0x76)return 2;
//
初始化序列
for(i=0;i<sizeof(ov7670_init_reg_tbl)/sizeof(ov7670_init_reg_tbl[0]);i++)
{
SCCB_WR_Reg(ov7670_init_reg_tbl
[0],ov7670_init_reg_tbl
[1]);
delay_ms(2);
}
return 0x00; //ok
}
此部分代码先初始化
OV7670
相关的
IO
口(包括
SCCB_Init
),然后最主要的是完成
OV7670
的寄存器序列初始化。
OV7670
的寄存器特多(百几十个),配置特麻烦,幸好厂家有提供参考配置序列(详见
《
OV7670 software application note
》
),本章我们用到的配置序列,存放在
ov7670_init_reg_tbl
这个数组里面,该数组是一个
2
维数组,存储初始化序列寄存器及其对应的值,该数组存放在
ov7670cfg.h
里面。
接下来,我们看看
ov7670cfg.h
里面
ov7670_init_reg_tbl
的内容,
ov7670cfg.h
文件的代码如下:
//
初始化寄存器序列及其对应的值
const u8 ov7670_init_reg_tbl[][2]=
{
//
以下为
OV7670 QVGA RGB565
参数
{0x3a, 0x04},//
{0x40, 0x10},
{0x12, 0x14},//QVGA,RGB
输出
……
省略部分设置
{0x6e, 0x11},//100
{0x6f, 0x9f},//0x9e for advance AWB
{0x55, 0x00},//
亮度
{0x56, 0x40},//
对比度
{0x57, 0x80},//0x40, change according to Jim's request
};
以上代码,我们省略了很多(全部贴出来太长了),我们大概了解下结构,每个条目的第一个字节为寄存器号(也就是寄存器地址),第二个字节为要设置的值,比如
{0x3a, 0x04}
,就表示在
0X03
地址,写入
0X04
这个值。
通过这么一长串(
110
多个)寄存器的配置,我们就完成了
OV7670
的初始化,本章我们配置
OV7670
工作在
QVGA
模式,
RGB565
格式输出。
在完成初始化之后,我们既可以开始读取
OV7670
的数据了。
OV7670
文件夹里面的其他代码我们就不逐个介绍了,请大家参考光盘该例程源码。
因为本章我们还用到了帧率(
LCD
显示的帧率)统计和中断处理,所以我们还需要修改
timer.c
、
timer.h
、
exti
.c
及
exti
.h
这几个文件。
在
timer.c
里面,我们新增
TIM6_Int_Init
和
TIM6_IRQHandler
两个函数,用于统计帧率,增加代码如下:
u8 ov_frame; //
统计帧数
//
定时器
6
中断服务程序
void TIM6_IRQHandler(void)
{
if(TIM6->SR&0X0001)//
溢出中断
{
printf("frame:%dfps ",ov_frame); //
打印帧率
ov_frame=0;
}
TIM6->SR&=~(1<<0);//
清除中断标志位
}
//
基本定时器
6
中断初始化
//
这里时钟选择为
APB1
的
2
倍,而
APB1
为
36M
//arr
:自动重装值。
//psc
:时钟预分频数
//
这里使用的是定时器
3!
void TIM6_Int_Init(u16 arr,u16 psc)
{
RCC->APB1ENR|=1<<4;//TIM6
时钟使能
TIM6->ARR=arr; //
设定计数器自动重装值
//
刚好
1ms
TIM6->
SC=psc; //
预分频器
7200,
得到
10Khz
的计数时钟
TIM6->DIER|=1<<0; //
允许更新中断
TIM6->CR1|=0x01; //
使能定时器
3
MY_NVIC_Init(1,3,TIM6_IRQChannel,2);//
抢占
1
,子优先级
3
,组
2
}
这里,我们用到基本定时器
TIM6
来统计帧率,也就是
1
秒钟中断一次,打印
ov_frame
的值,
ov_frame
用于统计
LCD
帧率。
再在
timer.h
里面添加
TIM6_Int_Init
函数的定义,就完成对
timer.c
和
timer.h
的修改了。
在
exti
.c
里面添加
EXTI8_Init
和
EXTI9_5_IRQHandler
函数,用于
OV7670
模块的
FIFO
写控制,
exti
.c
文件新增部分代码如下:
u8 ov_sta;
//
外部中断
5~9
服务程序
void EXTI9_5_IRQHandler(void)
{
if(EXTI->
R&(1<<8))//
是
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->
R=1<<8; //
清除
LINE8
上的中断标志位
}
//
外部中断
8
初始化
void EXTI8_Init(void)
{
Ex_NVIC_Config(GPIO_A,8,RTIR); //
任意边沿触发
MY_NVIC_Init(0,0,EXTI9_5_IRQChannel,2); //
抢占
0,
子优先级
0
,组
2
}
因为
OV7670
的帧同步信号(
OV_VSYNC
)接在
PA8
上面,所以我们这里配置
PA8
作为中端输入,因为
STM32
的外部中断
5~9
共用一个中端服务函数(
EXTI9_5_IRQHandler
),所以在该函数里面,我们需要先判断中断是不是来自中断线
8
的,然后再做处理。
中断处理部分很简单,通过一个
ov_sta
来控制
OV7670
模块的
FIFO
写操作。当
ov_sta=0
的时候,表示
FIFO
存储的数据已经被成功读取了(
ov_sta
在读完
FIFO
数据的时候被清零),然后只要
OV_VSYNC
信号到来,我们就先复位一下写指针,然后
ov_sta=1
,标志着写指针已经复位,目前正在往
FIFO
里面写数据。再等下一个
OV_VSYNC
到来,也就表明一帧数据已经存储完毕了,此时我们设置
OV7670_WREN
为
0
,禁止再往
OV7670
写入数据,此时
ov_sta
自增为
2
。其他程序,只要读到
ov_sta
为
2
,就表示一帧数据已经准备好了,可以读出,在读完数据之后,程序设置
ov_sta
为
0
,则开启下一轮
FIFO
数据存储。
再在
exti.h
里面添加
EXTI8_Init
函数的定义,就完成对
exti
.c
和
exti
.h
的修改了。
最后,打开
test.c
文件,修改代码如下:
const u8*LMODE_TBL[5]={"Auto","Sunny","Cloudy","Office","Home"}; //5
种光照模式
const u8*EFFECTS_TBL[7]={"Normal","Negative","B&W","Redish","Greenish","Bluish",
"Antique"};//7
种特效
extern u8 ov_sta; //
在
exit.c
里面定义
extern u8 ov_frame; //
在
timer.c
里面定义
//
更新
LCD
显示
void camera_refresh(void)
{
&
友情提示:
此问题已得到解决,问题已经关闭,关闭后问题禁止继续编辑,回答。
33条回答
ilikerome
2019-08-17 04:01
回复【楼主位】正点原子:
---------------------------------
请教原子大侠,我的显示只有2fps,直接刷你的源程序也是这样,可能是什么原因啊?怎么排查?谢谢!
加载中...
查看其它33个回答
一周热门
更多
>
相关问题
STM32F4上I2C(在PROTEUS中模拟)调试不通的问题
6 个回答
芯片供应紧张,准备换个MCU,MM32L系列替换STM32L系列的怎么样?
7 个回答
STM32同时使用两个串口进行数据收发时数据丢包的问题
5 个回答
STM32F103串口通信死机问题
4 个回答
STM32WLE5CC连接SX1268在LoRa模式下能与 SX1278互通吗?
2 个回答
相关文章
ST公司第一款无线低功耗单片机模块有效提高物联网设计生产效率
0个评论
如何实现对单片机寄存器的访问
0个评论
通过USB用STM32片内自带Bootloader下载程序及注意事项
0个评论
欲练此功必先自宫之STM32汇编启动,放慢是为了更好的前行
0个评论
×
关闭
采纳回答
向帮助了您的知道网友说句感谢的话吧!
非常感谢!
确 认
×
关闭
编辑标签
最多设置5个标签!
STM32
保存
关闭
×
关闭
举报内容
检举类型
检举内容
检举用户
检举原因
广告推广
恶意灌水
回答内容与提问无关
抄袭答案
其他
检举说明(必填)
提交
关闭
×
打开微信“扫一扫”,打开网页后点击屏幕右上角分享按钮
×
付费偷看金额在0.1-10元之间
确定
×
关闭
您已邀请
0
人回答
查看邀请
擅长该话题的人
回答过该话题的人
我关注的人
---------------------------------
请教原子大侠,我的显示只有2fps,直接刷你的源程序也是这样,可能是什么原因啊?怎么排查?谢谢!
一周热门 更多>