0%

OV2460摄像头

OV2460摄像头

简介

OV2460 是 OmniVision 推出的一款 200 万像素的 CMOS 图像传感器,像头中的图像传感器是一款型号为OV2640的CMOS类型数字图像传感器。该传感器支持输出最大为200万像素的图像 (1600x1200分辨率), 支持使用VGA时序输出图像数据,输出图像的数据格式支持YUV(422/420)、YCbCr422、RGB565以及JPEG格式,若直接输出JPEG格式的图像时可大大减少数据量, 方便网络传输。它还可以对采集得的图像进行补偿,支持伽玛曲线、白平衡、饱和度、色度等基础处理。根据不同的分辨率配置, 传感器输出图像数据的帧率从15-60帧可调,工作时功率在125mW-140mW之间。

OV2460功能框图

OV2640功能框图

  • 控制寄存器

    • 标号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根数据线进行传输。

      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时序:
  • 起始信号、停止信号

    SCCB停止信号

    • 起始信号:在SIO_C为高电平时,SIO_D由低变为高电平。
    • 停止信号:在SIO_C为高电平时,SIO_D由高变为低电平。
    • 数据有效位:除去起始信号和停止信号,在数据传输过程中,当SIO_C为高电平期间,SIO_D为有效数据。
  • SCCB数据读写过程

    • SCCB有两种写操作:

      • 三步写操作:

        • 第一步:发送从设备的ID地址+W标志

        • 第二部:发送从设备的目标寄存器的8位地址

        • 第三步:发送写入寄存器的8位数据。

          SCCB的三步写操作

      • 两步操作:

        • 第一步:发送从设备的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框图

DCMI接口整体框图

  • 标号1出的是DCMI向外部引出的信号线。DCMI提供的外部接口的方向都是输入的。

    DCMI的信号线说明

    • 其中DCMI_D数据线的数量可选为8、10、12、14位。
  • 标号2处,HCLK是由控制器所提供的时钟源,提供给DCMI外设。从DCMI外设引出DCMI_IT中断信号线。并且可以通过DMA_REQ信号发送DMA请求。

DCMI内部结构

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支持两种同步方式:

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
    16
    void 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
    9
    void 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
    9
    void 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
    11
    void 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
    23
    u8 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
    16
    u8 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
    12
    u8 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
    19
    u8 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
    48
    u8 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
    15
    oid 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
    9
    void 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
    23
    void 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
      7
      SCCB_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
      6
      temp = 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
    19
    u8 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 整除,以确保数据块对齐,避免零填充或传输错误。
  • 捕获 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
    42
    void 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
    8
    void DMA2_Stream1_IRQHandler(void)
    {
    if(DMA_GetFlagStatus(DMA2_Stream1, DMA_FLAG_TCIF1)==SET) // 检查传输完成标志
    {
    DMA_ClearFlag(DMA2_Stream1, DMA_FLAG_TCIF1); // 清除标志
    dcmi_rx_callback(); // 调用回调函数处理数据
    }
    }
    • 当传输一帧数据完成时触发。
    • 调用回调函数处理数据。