OV2460摄像头
简介
OV2460 是 OmniVision 推出的一款 200 万像素的 CMOS 图像传感器,像头中的图像传感器是一款型号为OV2640的CMOS类型数字图像传感器。该传感器支持输出最大为200万像素的图像 (1600x1200分辨率), 支持使用VGA时序输出图像数据,输出图像的数据格式支持YUV(422/420)、YCbCr422、RGB565以及JPEG格式,若直接输出JPEG格式的图像时可大大减少数据量, 方便网络传输。它还可以对采集得的图像进行补偿,支持伽玛曲线、白平衡、饱和度、色度等基础处理。根据不同的分辨率配置, 传感器输出图像数据的帧率从15-60帧可调,工作时功率在125mW-140mW之间。
OV2460功能框图
控制寄存器
- 标号1处是OV2460的控制寄存器,它由外部控制器通过SIO_C和SIO_D引脚写入。而SIO_C和SIO_D使用的协议是SCCB协议,而SCCB和IIC协议大致类似,在软件中完全可以用IIC驱动来控制。
通信、时钟、控制信号
- 通信、控制信号
- PCLK:像素同步时钟,适用于输出数据的同步信号,是由OV2460产生的。
- HREF:行同步信号
- VSYNC:帧同步信号
- RESETB:引脚为低电平时,复位传感器
- PWDN:控制芯片进入低功耗模式
- 时钟
- XCLK:于PCLK不同,XCLK是驱动整个传感器的时钟信号。由外部晶振输入的。
- 通信、控制信号
感光矩阵
- 光信号转化为电信号,经过处理后,这些信号存储呈给一个个的像素点表示的图像。
数据输出信号
其中的DSP,会根据控制寄存器中设置进行图像处理。最终处理数据通过Y0-9引脚输出,一般来说使用8根数据线进行传输。
SCCB(串行摄像头控制总线)
SCCB概述:
- SCCB是由OV公司定义和发展的一种串行总线协议。
- 它主要用于控制和配置图像传感器。
- 通过SCCB,可以设置摄像头的各种数据。
SCCB特点:
- SCCB的结构和IIC相似。
- SCCB有两种模式:一主多从,一主一从。
- 引脚:两根线:SIO_C、SIO_D.
SCCB的工作原理:
- SCCB通信可以主机发送命令和数据给从机,还可以从从机中读取数据。
- 由主机通过SIO_C发送时钟信号,通过SIO_D线传输数据。
- 数据传输包括起始位、从机地址、读/写位、寄存器地址、数据、停止位。
SCCB和IIC的不同
- 应答位:SCCB的应答位是 Don’t care,而iIC是ACK。
- 这里的Don’t care,表示当从机发送应答位,主机可以不进行判断,不管从机发送的啥,都会进行后续的操作。
- IIC的ACK应答位是主机明确说从机只有发送应答为逻辑0,才会进行后续的操作。
- 读写位操作:SCCB只能进行单次读取,而IIC可以进行单次和连续读取。
- SCCB协议主要适用于配置寄存器,所以SCCB单次读取,每次只读取一个寄存器的值。
- 读写中间的stop信号:SCCB读操作中间由stop信号。
SCCB时序:
起始信号、停止信号
- 起始信号:在SIO_C为高电平时,SIO_D由低变为高电平。
- 停止信号:在SIO_C为高电平时,SIO_D由高变为低电平。
- 数据有效位:除去起始信号和停止信号,在数据传输过程中,当SIO_C为高电平期间,SIO_D为有效数据。
SCCB数据读写过程
SCCB有两种写操作:
三步写操作:
第一步:发送从设备的ID地址+W标志
第二部:发送从设备的目标寄存器的8位地址
第三步:发送写入寄存器的8位数据。
两步操作:
- 第一步:发送从设备的ID地址和W标志
- 第二步:发送目标寄存器的地址
两步写操作是用来配合后面的读寄存器的操作。
SCCB读操作:
- 在两部写操作的基础上,在加上发送从机地址和R读标志。
- 开始读取数据。
SCCB传输一个字节数据会有一个无效位(don‘t care),无论从机发送的什么都不管,继续执行代码。
OV2640的寄存器
在控制OV2640中涉及到了很多的寄存器,详细的可以字节去找资料。但是注意的两组寄存器。这两组寄存器的地址有些重合,通过配置为0xFF的RA_DLMT寄存器可以切换寄存器组,当RA_DLMT为0时,使用式DSP相关的寄存器组;当RA_DLMT为1时,使用是Sensor相关的寄存器组。
像素输出时序
主控制控制OV2640时,采用的是SCCB协议读写其寄存器,而它输出图像时则使用VGA时序(还有SVGA、UXGA,都差不多)。OV2640输出图像时,一帧一帧的输出,从左到右,从上到下。
如果我们使用Y2-9的8根数据线输出图像格式为RGB565,进行数据输出时,Y2-9数据线会在一个像素同步时钟PCLK的驱动下发送一个字节的数据信号。两个PCLK时钟信号可以发送一个RGB565格式的像素数据。依次传输,每传输一行的像素数据,行同步信号就会跳变一次。当一帧图像传输完成后,VSYNC会输出一个一个电平跳变的信号。
DCMI接口
STM32F4系列的控制器包含了DCMI数字摄像头接口(Digital cameraInterface), 它支持使用上述类似VGA的时序获取图像数据流,支持原始的按行、帧格式来组织的图像数据,如YUV、RGB, 也支持接收JPEG格式压缩的数据流。接收数据时,主要使用HSYNC及VSYNC信号来同步。
DCMI框图
标号1出的是DCMI向外部引出的信号线。DCMI提供的外部接口的方向都是输入的。
- 其中DCMI_D数据线的数量可选为8、10、12、14位。
标号2处,HCLK是由控制器所提供的时钟源,提供给DCMI外设。从DCMI外设引出DCMI_IT中断信号线。并且可以通过DMA_REQ信号发送DMA请求。
DCMI内部结构
- 1、同步器
- 同步器主要用于管理DCMI接收数据的时序,它根据外部信号提取输入的数据,也就是OV2640处来的数据。
- 2、FIFO/数据格式化器
- 对传输的数据进行管理,DCMI接口提供四个字(4 * 32bit)深度的FIFO,用于缓冲接收的数据。
- 3、AHB接口
- DCMI挂载在AHB总线上,在AHB总线上有一个DCMI接口的数据寄存器,当我们读取该寄存器是,它会从FIFO中获取数据。
- 4、控制寄存器
- 可以检测状态寄存器来获取DCMI的当前运行状态。
- 5、DMA接口
- 由于DCMI采集的数据量很大,我们一般使用DMA来将采集的数据搬运到内存中。
DCMI同步方式
DCMI支持两种同步方式:
- 硬件同步
- 使用HSYNC和VSYNC作为同步信号的的方式。(OV2640就是使用这个方式)
- 在DCMI实现中,摄像头与为控制的同步问题需要额外的注意。
- 内嵌码同步方式
- 使用数据信号线传输中的特定编码来表示同步信息。
DCMI的捕获模式和捕获率
DCMI还支持两种数据捕获模式,分别为快照模式和连续采集模式。快照模式时只采集一帧的图像数据,连续采集模式会一直采集多个帧的数据, 并且可以通过配置捕获率来控制采集多少数据,如可配置为采集所有数据或隔1帧采集一次数据或隔3帧采集一次数据。
DCMI的同步问题
HSYNC和VSYNC是两个同步信号,分别代表着新行和新帧的开始。他们的极性必须和OV2640的配置相匹配。
DCMI—PIXCLK是像素时钟,代表着传输数据的速率。
DCMI传输数据
- DCMI从OV2640接收到数据,通过并行接口将其暂存在内部FIFO(先进先出)的缓冲区中。
- 当启用 DMA 并配置好触发条件(如 FIFO 满或半满)时,DCMI 会发起一次 DMA 请求。
- DMA会将缓存区中的内容搬运到内存中。
- FIFO缓冲区大小为4个字,16个字节,148位。在DCMI外设内部。
操作流程
- 微控制器初始化其I/O引脚和通信外设。
- 然后,微控制器使用SCCB协议配置OV2640,设置分辨率、输出格式(例如,YUV422、RGB565、JPEG)、帧率和图像处理设置等参数。
- 接下来,微控制器配置DCMI接口以匹配OV2640的输出格式以及时钟/同步信号极性。
- 通常会配置DMA将传入的像素数据从DCMI FIFO传输到指定的内存区域(帧缓冲区)。
- 摄像头开始流式传输数据,DCMI捕获数据并通过DMA将其传输到内存。
- 最后,微控制器可以处理帧缓冲区中的图像数据(例如,用于显示、存储或进一步分析)。
代码分析
SCCB代码
初始化SCCB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16void SCCB_Init(void)
{
GPIO_InitTypeDef GPIO_InitStructure;
RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOD, ENABLE);//使能GPIOD时钟
//GPIOF9,F10初始化设置
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_6|GPIO_Pin_7;//PD6,7 推挽输出
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_OUT; //PD6,7 推挽输出
GPIO_InitStructure.GPIO_OType = GPIO_OType_PP;//推挽输出
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;//100MHz
GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_UP;//上拉
GPIO_Init(GPIOD, &GPIO_InitStructure);//初始化
GPIO_SetBits(GPIOD,GPIO_Pin_6|GPIO_Pin_7);
SCCB_SDA_OUT();
}- 初始化PD6、7为推挽输出。
起始信号
1
2
3
4
5
6
7
8
9void SCCB_Start(void)
{
SCCB_SDA=1; //数据线高电平
SCCB_SCL=1; //在时钟线高的时候数据线由高至低
delay_us(50);
SCCB_SDA=0;
delay_us(50);
SCCB_SCL=0; //数据线恢复低电平,单操作函数必要
}- 在SIO_C高电平期间,SIO_D由高变为低电平。
停止信号
1
2
3
4
5
6
7
8
9void SCCB_Stop(void)
{
SCCB_SDA=0;
delay_us(50);
SCCB_SCL=1;
delay_us(50);
SCCB_SDA=1;
delay_us(50);
}- 在SIO_C高电平期间,SIO_D由低变为高电平。
don’t care信号
1
2
3
4
5
6
7
8
9
10
11void SCCB_No_Ack(void)
{
delay_us(50);
SCCB_SDA=1;
SCCB_SCL=1;
delay_us(50);
SCCB_SCL=0;
delay_us(50);
SCCB_SDA=0;
delay_us(50);
}写操作
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23u8 SCCB_WR_Byte(u8 dat)
{
u8 j,res;
for(j=0;j<8;j++) //循环8次发送数据
{
if(dat&0x80)SCCB_SDA=1;
else SCCB_SDA=0;
dat<<=1;
delay_us(50);
SCCB_SCL=1;
delay_us(50);
SCCB_SCL=0;
}
SCCB_SDA_IN(); //设置SDA为输入
delay_us(50);
SCCB_SCL=1; //接收第九位,以判断是否发送成功
delay_us(50);
if(SCCB_READ_SDA)res=1; //SDA=1发送失败,返回1
else res=0; //SDA=0发送成功,返回0
SCCB_SCL=0;
SCCB_SDA_OUT(); //设置SDA为输出
return res;
}- 循环发送8位数据。
- 设置SDA位输入,读取SDA,但是不影响接下来的操作。
读操作
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16u8 SCCB_RD_Byte(void)
{
u8 temp=0,j;
SCCB_SDA_IN(); //设置SDA为输入
for(j=8;j>0;j--) //循环8次接收数据
{
delay_us(50);
SCCB_SCL=1;
temp=temp<<1;
if(SCCB_READ_SDA)temp++;
delay_us(50);
SCCB_SCL=0;
}
SCCB_SDA_OUT(); //设置SDA为输出
return temp;
}- 设置SDA为输入。
- 循环接收数据。
写寄存器
1
2
3
4
5
6
7
8
9
10
11
12u8 SCCB_WR_Reg(u8 reg,u8 data)
{
u8 res=0;
SCCB_Start(); //启动SCCB传输
if(SCCB_WR_Byte(SCCB_ID))res=1; //写器件ID
delay_us(100);
if(SCCB_WR_Byte(reg))res=1; //写寄存器地址
delay_us(100);
if(SCCB_WR_Byte(data))res=1; //写数据
SCCB_Stop();
return res;
}- 起始信号。
- 发送从机地址。
- 发送数据。
读寄存器
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19u8 SCCB_RD_Reg(u8 reg)
{
u8 val=0;
SCCB_Start(); //启动SCCB传输
SCCB_WR_Byte(SCCB_ID); //写器件ID
delay_us(100);
SCCB_WR_Byte(reg); //写寄存器地址
delay_us(100);
SCCB_Stop();
delay_us(100);
//设置寄存器地址后,才是读
SCCB_Start();
SCCB_WR_Byte(SCCB_ID|0X01); //发送读命令
delay_us(100);
val=SCCB_RD_Byte(); //读取数据
SCCB_No_Ack();
SCCB_Stop();
return val;
}- 起始信号
- 发送从机地址、寄存器地址、停止信号。
- 发送从机地址和读命令
- 读取数据。
OV2640代码
初始化OV2640
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48u8 OV2640_Init(void)
{
u16 i=0;
u16 reg;
//设置IO
GPIO_InitTypeDef GPIO_InitStructure;
RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOG, ENABLE);
//GPIOG9,15初始化设置
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_9|GPIO_Pin_15;//PG9,15推挽输出
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_OUT; //推挽输出
GPIO_InitStructure.GPIO_OType = GPIO_OType_PP;//推挽输出
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;//100MHz
GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_UP;//上拉
GPIO_Init(GPIOG, &GPIO_InitStructure);//初始化
OV2640_PWDN=0; //POWER ON
delay_ms(10);
OV2640_RST=0; //复位OV2640
delay_ms(10);
OV2640_RST=1; //结束复位
SCCB_Init(); //初始化SCCB 的IO口
SCCB_WR_Reg(OV2640_DSP_RA_DLMT, 0x01); //操作sensor寄存器
SCCB_WR_Reg(OV2640_SENSOR_COM7, 0x80); //软复位OV2640
delay_ms(50);
reg=SCCB_RD_Reg(OV2640_SENSOR_MIDH); //读取厂家ID 高八位
reg<<=8;
reg|=SCCB_RD_Reg(OV2640_SENSOR_MIDL); //读取厂家ID 低八位
if(reg!=OV2640_MID)
{
printf("MID:%d\r\n",reg);
return 1;
}
reg=SCCB_RD_Reg(OV2640_SENSOR_PIDH); //读取厂家ID 高八位
reg<<=8;
reg|=SCCB_RD_Reg(OV2640_SENSOR_PIDL); //读取厂家ID 低八位
if(reg!=OV2640_PID)
{
printf("HID:%d\r\n",reg);
//return 2;
}
//初始化 OV2640,采用SXGA分辨率(1600*1200)
for(i=0;i<sizeof(ov2640_sxga_init_reg_tbl)/2;i++)
{
SCCB_WR_Reg(ov2640_sxga_init_reg_tbl[i][0],ov2640_sxga_init_reg_tbl[i][1]);
}
return 0x00; //ok
}- 设置GPIO引脚,打开电源和复位摄像头。
- 初始化SCCB接口。
- 设置DSP_RA_DLMT 寄存器为0x01,操作传感器寄存器。
- 读取厂商和产品ID
- 设置SXGA,设置分辨率输出。
- SXGA是一种分辨率标准,通常定义为1280* 1024.
- 还有VGA、SVGA、UXGA等。
- 加载ov2640_sxga_init_reg_tbl表,是配置OV2640的初始捕获率,也就是读取和处理的图像大小。
- 加载这个表相当于对摄像头进行一次完整的初始化,设定为 1600x1200 的捕获分辨率。
设置为jpeg模式
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15oid OV2640_JPEG_Mode(void)
{
u16 i=0;
//设置:YUV422格式
for(i=0;i<(sizeof(ov2640_yuv422_reg_tbl)/2);i++)
{
SCCB_WR_Reg(ov2640_yuv422_reg_tbl[i][0],ov2640_yuv422_reg_tbl[i][1]);
}
//设置:输出JPEG数据
for(i=0;i<(sizeof(ov2640_jpeg_reg_tbl)/2);i++)
{
SCCB_WR_Reg(ov2640_jpeg_reg_tbl[i][0],ov2640_jpeg_reg_tbl[i][1]);
}
}设置为RGB565模式
1
2
3
4
5
6
7
8
9void OV2640_RGB565_Mode(void)
{
u16 i=0;
//设置:RGB565输出
for(i=0;i<(sizeof(ov2640_rgb565_reg_tbl)/2);i++)
{
SCCB_WR_Reg(ov2640_rgb565_reg_tbl[i][0],ov2640_rgb565_reg_tbl[i][1]);
}
}设置图像输出窗口
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23void OV2640_Window_Set(u16 sx,u16 sy,u16 width,u16 height)
{
u16 endx;
u16 endy;
u8 temp;
endx=sx+width/2; //V*2
endy=sy+height/2;
SCCB_WR_Reg(0XFF,0X01);
temp=SCCB_RD_Reg(0X03); //读取Vref之前的值
temp&=0XF0;
temp|=((endy&0X03)<<2)|(sy&0X03);
SCCB_WR_Reg(0X03,temp); //设置Vref的start和end的最低2位
SCCB_WR_Reg(0X19,sy>>2); //设置Vref的start高8位
SCCB_WR_Reg(0X1A,endy>>2); //设置Vref的end的高8位
temp=SCCB_RD_Reg(0X32); //读取Href之前的值
temp&=0XC0;
temp|=((endx&0X07)<<3)|(sx&0X07);
SCCB_WR_Reg(0X32,temp); //设置Href的start和end的最低3位
SCCB_WR_Reg(0X17,sx>>3); //设置Href的start高8位
SCCB_WR_Reg(0X18,endx>>3); //设置Href的end的高8位
}参数:
- sx:水平起始坐标
- sy:垂直起始坐标
- width:窗口宽度
- height:窗口高度
endx、y是结束坐标。
为什么除去2?
- OV2640的串口设置寄存器使用一种特殊的表示方法,将其转换为一半的值存储。
配置垂直窗口
1
2
3
4
5
6
7SCCB_WR_Reg(0xFF, 0x01); // 选择传感器寄存器组
temp = SCCB_RD_Reg(0x03); // 读取 Vref 寄存器的当前值
temp &= 0xF0; // 保留高 4 位,清低 4 位
temp |= ((endy & 0x03) << 2) | (sy & 0x03); // 设置 Vref 的 start 和 end 低 2 位
SCCB_WR_Reg(0x03, temp); // 写入更新后的 Vref 值
SCCB_WR_Reg(0x19, sy >> 2); // 设置 Vref start 的高 8 位
SCCB_WR_Reg(0x1A, endy >> 2); // 设置 Vref end 的高 8 位- 清除第四位的值,在向其写入新的垂直的sy、endy值。
- Vref(垂直参考):
- 定义读取的起始行(sy)和结束行(endy),控制垂直范围。
- 10 位分辨率(2 位在 0x03,8 位在 0x19/0x1A)。
配置水平窗口
1
2
3
4
5
6temp = SCCB_RD_Reg(0x32); // 读取 Href 寄存器的当前值
temp &= 0xC0; // 保留高 2 位,清低 6 位
temp |= ((endx & 0x07) << 3) | (sx & 0x07); // 设置 Href 的 start 和 end 低 3 位
SCCB_WR_Reg(0x32, temp); // 写入更新后的 Href 值
SCCB_WR_Reg(0x17, sx >> 3); // 设置 Href start 的高 8 位
SCCB_WR_Reg(0x18, endx >> 3); // 设置 Href end 的高 8 位- 清除第四位的值,在向其写入新的垂直的sx、endx值。
- Href(水平参考):
- 定义读取的起始列(sx)和结束列(endx),控制水平范围。
- 11 位分辨率(3 位在 0x32,8 位在 0x17/0x18)。
输出图像大小
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19u8 OV2640_OutSize_Set(u16 width,u16 height)
{
u16 outh;
u16 outw;
u8 temp;
if(width%4)return 1;
if(height%4)return 2;
outw=width/4;
outh=height/4;
SCCB_WR_Reg(0XFF,0X00);
SCCB_WR_Reg(0XE0,0X04);
SCCB_WR_Reg(0X5A,outw&0XFF); //设置OUTW的低八位
SCCB_WR_Reg(0X5B,outh&0XFF); //设置OUTH的低八位
temp=(outw>>8)&0X03;
temp|=(outh>>6)&0X04;
SCCB_WR_Reg(0X5C,temp); //设置OUTH/OUTW的高位
SCCB_WR_Reg(0XE0,0X00);
return 0;
}- 为何要求 4 的倍数?
- OV2640 的输出数据通常以 4 字节对齐(可能是由于 JPEG 编码或 DMA 传输的需求)。
- 硬件或 DSP 处理时,要求宽度和高度能被 4 整除,以确保数据块对齐,避免零填充或传输错误。
- 为何要求 4 的倍数?
捕获 vs 输出:
- 捕获分辨率:由 OV2640_Window_Set() 设置,定义传感器读取的区域(如 800x600)。
- 输出分辨率:由 OV2640_OutSize_Set() 设置,定义最终输出的大小(如 320x240)。
DCMI外设代码
DCMI初始化
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62//DCMI初始化
void My_DCMI_Init(void)
{
GPIO_InitTypeDef GPIO_InitStructure;
NVIC_InitTypeDef NVIC_InitStructure;
RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOA|RCC_AHB1Periph_GPIOB|RCC_AHB1Periph_GPIOC|RCC_AHB1Periph_GPIOE, ENABLE);//使能GPIOA B C E 时钟
RCC_AHB2PeriphClockCmd(RCC_AHB2Periph_DCMI,ENABLE);
//GPIOF9,F10初始化设置
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_4|GPIO_Pin_6;//PA4/6 复用功能输出
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF; //复用功能输出
GPIO_InitStructure.GPIO_OType = GPIO_OType_PP;//推挽输出
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_100MHz;//100MHz
GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_UP;//上拉
GPIO_Init(GPIOA, &GPIO_InitStructure);//初始化
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_7|GPIO_Pin_6;//PA4/6 //PB6/7 复用功能输出
GPIO_Init(GPIOB, &GPIO_InitStructure);//初始化
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_6|GPIO_Pin_7|GPIO_Pin_8|GPIO_Pin_9|GPIO_Pin_11;//PC6/7/8/9/11 复用功能输出
GPIO_Init(GPIOC, &GPIO_InitStructure);//初始化
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_5|GPIO_Pin_6;//PE5/6 复用功能输出
GPIO_Init(GPIOE, &GPIO_InitStructure);//初始化
GPIO_PinAFConfig(GPIOA,GPIO_PinSource4,GPIO_AF_DCMI); //PA4,AF13 DCMI_HSYNC
GPIO_PinAFConfig(GPIOA,GPIO_PinSource6,GPIO_AF_DCMI); //PA6,AF13 DCMI_PCLK
GPIO_PinAFConfig(GPIOB,GPIO_PinSource7,GPIO_AF_DCMI); //PB7,AF13 DCMI_VSYNC
GPIO_PinAFConfig(GPIOC,GPIO_PinSource6,GPIO_AF_DCMI); //PC6,AF13 DCMI_D0
GPIO_PinAFConfig(GPIOC,GPIO_PinSource7,GPIO_AF_DCMI); //PC7,AF13 DCMI_D1
GPIO_PinAFConfig(GPIOC,GPIO_PinSource8,GPIO_AF_DCMI); //PC8,AF13 DCMI_D2
GPIO_PinAFConfig(GPIOC,GPIO_PinSource9,GPIO_AF_DCMI); //PC9,AF13 DCMI_D3
GPIO_PinAFConfig(GPIOC,GPIO_PinSource11,GPIO_AF_DCMI);//PC11,AF13 DCMI_D4
GPIO_PinAFConfig(GPIOB,GPIO_PinSource6,GPIO_AF_DCMI); //PB6,AF13 DCMI_D5
GPIO_PinAFConfig(GPIOE,GPIO_PinSource5,GPIO_AF_DCMI); //PE5,AF13 DCMI_D6
GPIO_PinAFConfig(GPIOE,GPIO_PinSource6,GPIO_AF_DCMI); //PE6,AF13 DCMI_D7
DCMI_DeInit();//清除原来的设置
DCMI_InitStructure.DCMI_CaptureMode=DCMI_CaptureMode_Continuous;//连续模式
DCMI_InitStructure.DCMI_CaptureRate=DCMI_CaptureRate_All_Frame;//全帧捕获
DCMI_InitStructure.DCMI_ExtendedDataMode= DCMI_ExtendedDataMode_8b;//8位数据格式
DCMI_InitStructure.DCMI_HSPolarity= DCMI_HSPolarity_Low;//HSYNC 低电平有效
DCMI_InitStructure.DCMI_PCKPolarity= DCMI_PCKPolarity_Rising;//PCLK 上升沿有效
DCMI_InitStructure.DCMI_SynchroMode= DCMI_SynchroMode_Hardware;//硬件同步HSYNC,VSYNC
DCMI_InitStructure.DCMI_VSPolarity=DCMI_VSPolarity_Low;//VSYNC 低电平有效
DCMI_Init(&DCMI_InitStructure);
DCMI_ITConfig(DCMI_IT_FRAME,ENABLE);//开启帧中断
DCMI_Cmd(ENABLE); //DCMI使能
NVIC_InitStructure.NVIC_IRQChannel = DCMI_IRQn;
NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority=0;//抢占优先级1
NVIC_InitStructure.NVIC_IRQChannelSubPriority =0; //子优先级3
NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE; //IRQ通道使能
NVIC_Init(&NVIC_InitStructure); //根据指定的参数初始化VIC寄存器、
}- 配置引脚:PA4、PA6、PB6、PB7、PC6-PC9、PC11、PE5、PE6。
- DCMI配置
- 捕获模式:连续模式
- 捕获速率:全帧
- 数据个数:8位
- 信号极性
- HSYNC:低电平有效
- VSYNC低电平有效
- PCLK上升沿有效
- 同步模式:硬件同步
- 使能帧中断
- 配置NVIC
DMA配置
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42void DCMI_DMA_Init(u32 DMA_Memory0BaseAddr,u32 DMA_Memory1BaseAddr,u16 DMA_BufferSize,u32 DMA_MemoryDataSize,u32 DMA_MemoryInc)
{
DMA_InitTypeDef DMA_InitStructure;
NVIC_InitTypeDef NVIC_InitStructure;
RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_DMA2,ENABLE);//DMA2时钟使能
DMA_DeInit(DMA2_Stream1);//等待DMA2_Stream1
while (DMA_GetCmdStatus(DMA2_Stream1) != DISABLE){}//等待DMA2_Stream1可配置
/* 配置 DMA Stream */
DMA_InitStructure.DMA_Channel = DMA_Channel_1; //通道1 DCMI通道
DMA_InitStructure.DMA_PeripheralBaseAddr = (u32)&DCMI->DR;//外设地址为:DCMI->DR
DMA_InitStructure.DMA_Memory0BaseAddr = DMA_Memory0BaseAddr;//DMA 存储器0地址
DMA_InitStructure.DMA_DIR = DMA_DIR_PeripheralToMemory;//外设到存储器模式
DMA_InitStructure.DMA_BufferSize = DMA_BufferSize;//数据传输量
DMA_InitStructure.DMA_PeripheralInc = DMA_PeripheralInc_Disable;//外设非增量模式
DMA_InitStructure.DMA_MemoryInc = DMA_MemoryInc;//存储器增量模式
DMA_InitStructure.DMA_PeripheralDataSize = DMA_PeripheralDataSize_Word;//外设数据长度:32位
DMA_InitStructure.DMA_MemoryDataSize = DMA_MemoryDataSize;//存储器数据长度
DMA_InitStructure.DMA_Mode = DMA_Mode_Circular;// 使用循环模式
DMA_InitStructure.DMA_Priority = DMA_Priority_High;//高优先级
DMA_InitStructure.DMA_FIFOMode = DMA_FIFOMode_Enable; //FIFO模式
DMA_InitStructure.DMA_FIFOThreshold = DMA_FIFOThreshold_Full;//使用全FIFO
DMA_InitStructure.DMA_MemoryBurst = DMA_MemoryBurst_Single;//外设突发单次传输
DMA_InitStructure.DMA_PeripheralBurst = DMA_PeripheralBurst_Single;//存储器突发单次传输
DMA_Init(DMA2_Stream1, &DMA_InitStructure);//初始化DMA Stream
if(DMA_Memory1BaseAddr)
{
DMA_DoubleBufferModeCmd(DMA2_Stream1,ENABLE);//双缓冲模式
DMA_MemoryTargetConfig(DMA2_Stream1,DMA_Memory1BaseAddr,DMA_Memory_1);//配置目标地址1
DMA_ITConfig(DMA2_Stream1,DMA_IT_TC,ENABLE);//开启传输完成中断
NVIC_InitStructure.NVIC_IRQChannel = DMA2_Stream1_IRQn;
NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority=0;//抢占优先级0
NVIC_InitStructure.NVIC_IRQChannelSubPriority =0; //子优先级0
NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE; //IRQ通道使能
NVIC_Init(&NVIC_InitStructure); //根据指定的参数初始化VIC寄存器、
}
}- 传输方向:(DCMI-DR)外设到存储器
- 双缓冲模式可选。
- 启用FIFO模式、阈值为满
DCMI的中断服务函数
1
2
3
4
5
6
7
8
9
10
11//DCMI中断服务函数
void DCMI_IRQHandler(void)
{
if(DCMI_GetITStatus(DCMI_IT_FRAME)==SET)//捕获到一帧图像
{
jpeg_data_process(); //jpeg数据处理
DCMI_ClearITPendingBit(DCMI_IT_FRAME);//清除帧中断
LED1=!LED1;
ov_frame++;
}
}- 当DCMI捕捉到一帧图像时触发。
DMA中断服务函数
1
2
3
4
5
6
7
8void DMA2_Stream1_IRQHandler(void)
{
if(DMA_GetFlagStatus(DMA2_Stream1, DMA_FLAG_TCIF1)==SET) // 检查传输完成标志
{
DMA_ClearFlag(DMA2_Stream1, DMA_FLAG_TCIF1); // 清除标志
dcmi_rx_callback(); // 调用回调函数处理数据
}
}- 当传输一帧数据完成时触发。
- 调用回调函数处理数据。