• 基于STM32F429和Cube的ov2640程序


    1.ov2640和DCMI介绍

      OV2640 是 OV(OmniVision)公司生产的一颗 1/4 寸的 CMOS UXGA(1632*1232)图 像传感器。该传感器体积小、工作电压低,提供单片 UXGA 摄像头和影像处理器的所有功 能。通过 SCCB 总线控制,可以输出整帧、子采样、缩放和取窗口等方式的各种分辨率 8/10 位影像数据。该产品 UXGA 图像最高达到 15 帧/秒(SVGA 可达 30 帧,CIF 可达 60 帧)。 用户可以完全控制图像质量、数据格式和传输方式。所有图像处理功能过程包括伽玛曲线、 白平衡、对比度、色度等都可以通过 SCCB 接口编程。OmmiVision 图像传感器应用独有的 传感器技术,通过减少或消除光学或电子缺陷如固定图案噪声、拖尾、浮散等,提高图像质 量,得到清晰的稳定的彩色图像。

      DCMI接口是一个同步并行接口,能够 接收外部 8 位、10 位、12 位或 14 位 CMOS 摄像头模块发出的高速数据流。可支持不同 的数据格式:YCbCr4:2:2/RGB565 逐行视频和压缩数据 (JPEG)。 STM32F4 DCM 接口特点: ● 8 位、10 位、12 位或 14 位并行接口 ● 内嵌码/外部行同步和帧同步 ● 连续模式或快照模式 ● 裁剪功能 ● 支持以下数据格式: 1,8/10/12/14 位逐行视频:单色或原始拜尔(Bayer)格式 2,YCbCr 4:2:2 逐行视频 3,RGB 565 逐行视频 4,压缩数据:JPEG DCMI 接口包括如下一些信号: 1, 数据输入(D[0:13]),用于接摄像头的数据输出,接 OV2640 我们只用了 8 位数据。
    2, 水平同步(行同步)输入(HSYNC),用于接摄像头的 HSYNC/HREF 信号。 3, 垂直同步(场同步)输入(VSYNC),用于接摄像头的 VSYNC 信号。 4, 像素时钟输入(PIXCLK),用于接摄像头的 PCLK 信号。 DCMI 接口是一个同步并行接口,可接收高速(可达 54 MB/s)数据流。该接口包含多 达 14 条数据线(D13-D0)和一条像素时钟线(PIXCLK)。像素时钟的极性可以编程,因此可以 在像素时钟的上升沿或下降沿捕获数据。 DCMI 接收到的摄像头数据被放到一个 32 位数据寄存器(DCMI_DR)中,然后通过通用 DMA 进行传输。图像缓冲区由 DMA 管理,而不是由摄像头接口管理。 从摄像头接收的数据可以按行/帧来组织(原始 YUV/RGB/拜尔模式),也可以是一系列 JPEG 图像。要使能 JPEG 图像接收,必须将 JPEG 位(DCMI_CR 寄存器的位 3)置 1。
    数据流可由可选的 HSYNC(水平同步)信号和 VSYNC(垂直同步)信号硬件同步, 或者通 过数据流中嵌入的同步码同步。

     

    2.Cube配置

    (1)打开Cube F429芯片上的DCMI功能,模式和配置如下:

    (2)DCMI的复用GPIO口如下,这样就可以直接使用板载的DCMI接口

    (3)打开DCMI的DMA通道

    (4)选择PB3和PB4为SCCB协议的引脚,用来和OV2640通信

     

    3.程序介绍

    (1)SCCB协议差不多相当于IIC协议,只是应当信号有点区别

    SCCB_HandleTypedef hSCCBx;

    ////初始化SCCB接口
    //void SCCB_Init(void)
    //{
    // GPIO_InitTypeDef GPIO_Initure;
    // __HAL_RCC_GPIOB_CLK_ENABLE(); //使能GPIOB时钟
    //
    // //PB3.4初始化设置
    // GPIO_Initure.Pin=GPIO_PIN_3|GPIO_PIN_4;
    // GPIO_Initure.Mode = GPIO_MODE_OUTPUT_PP;
    // GPIO_Initure.Pull = GPIO_PULLUP;
    // GPIO_Initure.Speed = GPIO_SPEED_FREQ_LOW;
    // HAL_GPIO_Init(GPIOB,&GPIO_Initure);
    // SCCB_GPIO_MODE_Opt (&hSCCBx);
    //}


    /*******************************************************************************
    * Function Name : vIIC_Handle_Init
    * Description : Initialization handle
    * Input : iicHandle SCL PORT SCL PIN SDA PORT SDA PIN
    * Output : None
    * Return : None
    ****************************************************************************** */
    void vSCCB_Handle_Init(void)
    {
    //引脚 Pin:
    hSCCBx.pSCL_Port = SCCB_SCL_GPIO_Port ;
    hSCCBx.uSCL_Pin = SCCB_SCL_Pin ;
    hSCCBx.pSDA_Port = SCCB_SDA_GPIO_Port ;
    hSCCBx.uSDA_Pin = SCCB_SDA_Pin ;
    hSCCBx.uWR = NULL ;


    switch(SCCB_SDA_Pin)
    {
    case GPIO_PIN_0 : hSCCBx.uSDA_Mode_Pin_Position = 0 ;break;
    case GPIO_PIN_1 : hSCCBx.uSDA_Mode_Pin_Position = 2 ;break;
    case GPIO_PIN_2 : hSCCBx.uSDA_Mode_Pin_Position = 4 ;break;
    case GPIO_PIN_3 : hSCCBx.uSDA_Mode_Pin_Position = 6 ;break;
    case GPIO_PIN_4 : hSCCBx.uSDA_Mode_Pin_Position = 8 ;break;
    case GPIO_PIN_5 : hSCCBx.uSDA_Mode_Pin_Position = 10;break;
    case GPIO_PIN_6 : hSCCBx.uSDA_Mode_Pin_Position = 12;break;
    case GPIO_PIN_7 : hSCCBx.uSDA_Mode_Pin_Position = 14;break;
    case GPIO_PIN_8 : hSCCBx.uSDA_Mode_Pin_Position = 16;break;
    case GPIO_PIN_9 : hSCCBx.uSDA_Mode_Pin_Position = 18;break;
    case GPIO_PIN_10: hSCCBx.uSDA_Mode_Pin_Position = 20;break;
    case GPIO_PIN_11: hSCCBx.uSDA_Mode_Pin_Position = 22;break;
    case GPIO_PIN_12: hSCCBx.uSDA_Mode_Pin_Position = 24;break;
    case GPIO_PIN_13: hSCCBx.uSDA_Mode_Pin_Position = 26;break;
    case GPIO_PIN_14: hSCCBx.uSDA_Mode_Pin_Position = 28;break;
    case GPIO_PIN_15: hSCCBx.uSDA_Mode_Pin_Position = 30;break;
    }
    }

    //SCCB起始信号
    //当时钟为高的时候,数据线的高到低,为SCCB起始信号
    //在激活状态下,SDA和SCL均为低电平

    void SCCB_Start()
    {

    SCCB_SDA_1 (&hSCCBx); //拉高数据线
    SCCB_SCL_1 (&hSCCBx); //拉高时钟线
    delay_us (50); //延时
    SCCB_SDA_0 (&hSCCBx); //拉低数据线
    delay_us (50); //延时
    SCCB_SCL_0 (&hSCCBx); //拉低时钟线
    delay_us (50); //延时
    }

    //SCCB停止信号
    //当时钟为高的时候,数据线的低到高,为SCCB停止信号
    //空闲状况下,SDA,SCL均为高电平
    void SCCB_Stop(void)
    {

    SCCB_SDA_0 (&hSCCBx); //拉低数据线
    delay_us (50); //延时
    SCCB_SCL_1 (&hSCCBx); //拉高时钟线
    delay_us (50); //延时
    SCCB_SDA_1 (&hSCCBx); //拉高数据线
    delay_us (50); //延时

    }


    ////产生NA信号
    void SCCB_No_Ack(void)
    {

    SCCB_SDA_1 (&hSCCBx); //SDA拉高 不应答对方
    delay_us (50);
    SCCB_SCL_1 (&hSCCBx);
    delay_us (50);
    SCCB_SCL_0 (&hSCCBx);
    delay_us (50);

    }


    ////SCCB,写入一个字节
    ////返回值:0,成功;1,失败.
    uint8_t SCCB_WR_Byte(uint8_t dat)
    {

    uint8_t i,res;

    for (i=0; i<8; i++)
    {
    if(dat & 0X80)
    SCCB_SDA_1 (&hSCCBx);
    else
    SCCB_SDA_0 (&hSCCBx);
    dat <<= 1;
    delay_us (50);
    SCCB_SCL_1 (&hSCCBx);
    delay_us (50);
    SCCB_SCL_0 (&hSCCBx);


    }
    SCCB_GPIO_MODE_Ipt (&hSCCBx);
    delay_us (50);
    SCCB_SCL_1 (&hSCCBx);
    delay_us (50);
    if(SCCB_SDA_R(&hSCCBx))
    res=1;
    else
    res=0;
    SCCB_SCL_0 (&hSCCBx);
    SCCB_GPIO_MODE_Opt (&hSCCBx);
    return res;
    }


    //SCCB 读取一个字节
    //在SCL的上升沿,数据锁存
    //返回值:读到的数据
    uint8_t SCCB_RD_Byte(void)
    {
    uint8_t i,uReceiveByte = 0;

    SCCB_GPIO_MODE_Ipt (&hSCCBx);
    SCCB_SDA_1 (&hSCCBx);
    for(i=0;i<8;i++)
    {
    uReceiveByte <<= 1;

    delay_us (50);
    SCCB_SCL_1 (&hSCCBx);
    delay_us (50);

    if(SCCB_SDA_R (&hSCCBx))
    {
    uReceiveByte |=0x01;
    }

    delay_us (50);
    SCCB_SCL_0 (&hSCCBx);
    delay_us (50);
    }
    SCCB_GPIO_MODE_Opt (&hSCCBx);

    return uReceiveByte;

    }


    //写寄存器
    //返回值:0,成功;1,失败.
    uint8_t SCCB_WR_Reg(uint8_t reg,uint8_t data)
    {
    uint8_t 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;
    }


    //读寄存器
    //返回值:读到的寄存器值
    uint8_t SCCB_RD_Reg(uint8_t reg)
    {
    uint8_t 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;
    }

     (2)PCF8574是用来给ov2640上电的

    //初始化PCF8574
    u8 PCF8574_Init(void)
    {
        u8 temp=0;
        GPIO_InitTypeDef GPIO_Initure;
        __HAL_RCC_GPIOB_CLK_ENABLE();           //使能GPIOB时钟
        
        GPIO_Initure.Pin=GPIO_PIN_12;           //PB12
        GPIO_Initure.Mode=GPIO_MODE_INPUT;      //输入
        GPIO_Initure.Pull=GPIO_PULLUP;          //上拉
        GPIO_Initure.Speed=GPIO_SPEED_HIGH;     //高速
        HAL_GPIO_Init(GPIOB,&GPIO_Initure);     //初始化
        IIC_Init();                                //IIC初始化     
        //检查PCF8574是否在位
        IIC_Start();                
          IIC_Send_Byte(PCF8574_ADDR);            //写地址               
          temp=IIC_Wait_Ack();                    //等待应答,通过判断是否有ACK应答,来判断PCF8574的状态
        IIC_Stop();                                //产生一个停止条件
        PCF8574_WriteOneByte(0XFF);                //默认情况下所有IO输出高电平
        return temp;
    }
    
    //读取PCF8574的8位IO值
    //返回值:读到的数据
    u8 PCF8574_ReadOneByte(void)
    {                  
          u8 temp=0;                                                                                   
        IIC_Start();                
            IIC_Send_Byte(PCF8574_ADDR|0X01);   //进入接收模式               
            IIC_Wait_Ack();     
        temp=IIC_Read_Byte(0);           
        IIC_Stop();                            //产生一个停止条件        
          return temp;
    }
    //向PCF8574写入8位IO值  
    //DataToWrite:要写入的数据
    void PCF8574_WriteOneByte(u8 DataToWrite)
    {                                                                                                  
        IIC_Start();  
        IIC_Send_Byte(PCF8574_ADDR|0X00);   //发送器件地址0X40,写数据      
          IIC_Wait_Ack();                                                             
          IIC_Send_Byte(DataToWrite);             //发送字节                               
          IIC_Wait_Ack();      
        IIC_Stop();                            //产生一个停止条件 
        delay_ms(10);     
    }
    
    //设置PCF8574某个IO的高低电平
    //bit:要设置的IO编号,0~7
    //sta:IO的状态;0或1
    void PCF8574_WriteBit(u8 bit,u8 sta)
    {
        u8 data;
        data=PCF8574_ReadOneByte(); //先读出原来的设置
        if(sta==0)data&=~(1<<bit);     
        else data|=1<<bit;
        PCF8574_WriteOneByte(data); //写入新的数据
    }
    
    //读取PCF8574的某个IO的值
    //bit:要读取的IO编号,0~7
    //返回值:此IO的值,0或1
    u8 PCF8574_ReadBit(u8 bit)
    {
        u8 data;
        data=PCF8574_ReadOneByte(); //先读取这个8位IO的值 
        if(data&(1<<bit))return 1;
        else return 0;   
    }  
     

    (3) ov2640驱动程序(改自原子例程)

    //设置摄像头模块PWDN脚的状态
    //sta:0,PWDN=0,上电.
    //    1,PWDN=1,掉电
    void OV2640_PWDN_Set(u8 sta)
    {
        PCF8574_WriteBit(DCMI_PWDN_IO,sta);
    }
    
    //初始化OV2640 
    //配置完以后,默认输出是1600*1200尺寸的图片!! 
    //返回值:0,成功
    //    其他,错误代码
    u8 OV2640_Init(void)
    { 
        u16 i=0;
        u16 reg,a;
        //设置IO                   
         GPIO_InitTypeDef GPIO_Initure;
        __HAL_RCC_GPIOA_CLK_ENABLE();            //开启GPIOA时钟
        
        GPIO_Initure.Pin=GPIO_PIN_15;           //PA15
        GPIO_Initure.Mode=GPIO_MODE_OUTPUT_PP;  //推挽输出
        GPIO_Initure.Pull=GPIO_PULLUP;          //上拉
        GPIO_Initure.Speed=GPIO_SPEED_HIGH;     //高速
        HAL_GPIO_Init(GPIOA,&GPIO_Initure);     //初始化
     
        a =  PCF8574_Init();            //初始化PCF8574
        printf("PCF8574: %d",a);
      OV2640_PWDN_Set(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
    ",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
    ",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
    } 
    //OV2640切换为JPEG模式
    void 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]);  
        }  
    }
    //OV2640切换为RGB565模式
    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]); 
        } 
    } 
    //自动曝光设置参数表,支持5个等级
    const static u8 OV2640_AUTOEXPOSURE_LEVEL[5][8]=
    {
        {
            0xFF,0x01,
            0x24,0x20,
            0x25,0x18,
            0x26,0x60,
        },
        {
            0xFF,0x01,
            0x24,0x34,
            0x25,0x1c,
            0x26,0x00,
        },
        {
            0xFF,0x01,    
            0x24,0x3e,    
            0x25,0x38,
            0x26,0x81,
        },
        {
            0xFF,0x01,
            0x24,0x48,
            0x25,0x40,
            0x26,0x81,
        },
        {
            0xFF,0x01,    
            0x24,0x58,    
            0x25,0x50,    
            0x26,0x92,    
        },
    }; 
    //OV2640自动曝光等级设置
    //level:0~4
    void OV2640_Auto_Exposure(u8 level)
    {  
        u8 i;
        u8 *p=(u8*)OV2640_AUTOEXPOSURE_LEVEL[level];
        for(i=0;i<4;i++)
        { 
            SCCB_WR_Reg(p[i*2],p[i*2+1]); 
        } 
    }  
    //白平衡设置
    //0:自动
    //1:太阳sunny
    //2,阴天cloudy
    //3,办公室office
    //4,家里home
    void OV2640_Light_Mode(u8 mode)
    {
        u8 regccval=0X5E;//Sunny 
        u8 regcdval=0X41;
        u8 regceval=0X54;
        switch(mode)
        { 
            case 0://auto 
                SCCB_WR_Reg(0XFF,0X00);     
                SCCB_WR_Reg(0XC7,0X10);//AWB ON 
                return;      
            case 2://cloudy
                regccval=0X65;
                regcdval=0X41;
                regceval=0X4F;
                break;    
            case 3://office
                regccval=0X52;
                regcdval=0X41;
                regceval=0X66;
                break;    
            case 4://home
                regccval=0X42;
                regcdval=0X3F;
                regceval=0X71;
                break;    
        }
        SCCB_WR_Reg(0XFF,0X00);     
        SCCB_WR_Reg(0XC7,0X40);    //AWB OFF 
        SCCB_WR_Reg(0XCC,regccval); 
        SCCB_WR_Reg(0XCD,regcdval); 
        SCCB_WR_Reg(0XCE,regceval);  
    }
    //色度设置
    //0:-2
    //1:-1
    //2,0
    //3,+1
    //4,+2
    void OV2640_Color_Saturation(u8 sat)
    { 
        u8 reg7dval=((sat+2)<<4)|0X08;
        SCCB_WR_Reg(0XFF,0X00);        
        SCCB_WR_Reg(0X7C,0X00);        
        SCCB_WR_Reg(0X7D,0X02);                
        SCCB_WR_Reg(0X7C,0X03);            
        SCCB_WR_Reg(0X7D,reg7dval);            
        SCCB_WR_Reg(0X7D,reg7dval);         
    }
    //亮度设置
    //0:(0X00)-2
    //1:(0X10)-1
    //2,(0X20) 0
    //3,(0X30)+1
    //4,(0X40)+2
    void OV2640_Brightness(u8 bright)
    {
      SCCB_WR_Reg(0xff, 0x00);
      SCCB_WR_Reg(0x7c, 0x00);
      SCCB_WR_Reg(0x7d, 0x04);
      SCCB_WR_Reg(0x7c, 0x09);
      SCCB_WR_Reg(0x7d, bright<<4); 
      SCCB_WR_Reg(0x7d, 0x00); 
    }
    //对比度设置
    //0:-2
    //1:-1
    //2,0
    //3,+1
    //4,+2
    void OV2640_Contrast(u8 contrast)
    {
        u8 reg7d0val=0X20;//默认为普通模式
        u8 reg7d1val=0X20;
          switch(contrast)
        {
            case 0://-2
                reg7d0val=0X18;          
                reg7d1val=0X34;          
                break;    
            case 1://-1
                reg7d0val=0X1C;          
                reg7d1val=0X2A;          
                break;    
            case 3://1
                reg7d0val=0X24;          
                reg7d1val=0X16;          
                break;    
            case 4://2
                reg7d0val=0X28;          
                reg7d1val=0X0C;          
                break;    
        }
        SCCB_WR_Reg(0xff,0x00);
        SCCB_WR_Reg(0x7c,0x00);
        SCCB_WR_Reg(0x7d,0x04);
        SCCB_WR_Reg(0x7c,0x07);
        SCCB_WR_Reg(0x7d,0x20);
        SCCB_WR_Reg(0x7d,reg7d0val);
        SCCB_WR_Reg(0x7d,reg7d1val);
        SCCB_WR_Reg(0x7d,0x06);
    }
    //特效设置
    //0:普通模式    
    //1,负片
    //2,黑白   
    //3,偏红色
    //4,偏绿色
    //5,偏蓝色
    //6,复古        
    void OV2640_Special_Effects(u8 eft)
    {
        u8 reg7d0val=0X00;//默认为普通模式
        u8 reg7d1val=0X80;
        u8 reg7d2val=0X80; 
        switch(eft)
        {
            case 1://负片
                reg7d0val=0X40; 
                break;    
            case 2://黑白
                reg7d0val=0X18; 
                break;     
            case 3://偏红色
                reg7d0val=0X18; 
                reg7d1val=0X40;
                reg7d2val=0XC0; 
                break;    
            case 4://偏绿色
                reg7d0val=0X18; 
                reg7d1val=0X40;
                reg7d2val=0X40; 
                break;    
            case 5://偏蓝色
                reg7d0val=0X18; 
                reg7d1val=0XA0;
                reg7d2val=0X40; 
                break;    
            case 6://复古
                reg7d0val=0X18; 
                reg7d1val=0X40;
                reg7d2val=0XA6; 
                break;     
        }
        SCCB_WR_Reg(0xff,0x00);
        SCCB_WR_Reg(0x7c,0x00);
        SCCB_WR_Reg(0x7d,reg7d0val);
        SCCB_WR_Reg(0x7c,0x05);
        SCCB_WR_Reg(0x7d,reg7d1val);
        SCCB_WR_Reg(0x7d,reg7d2val); 
    }
    //彩条测试
    //sw:0,关闭彩条
    //   1,开启彩条(注意OV2640的彩条是叠加在图像上面的)
    void OV2640_Color_Bar(u8 sw)
    {
        u8 reg;
        SCCB_WR_Reg(0XFF,0X01);
        reg=SCCB_RD_Reg(0X12);
        reg&=~(1<<1);
        if(sw)reg|=1<<1; 
        SCCB_WR_Reg(0X12,reg);
    }
    //设置图像输出窗口 
    //sx,sy,起始地址
    //width,height:宽度(对应:horizontal)和高度(对应:vertical)
    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位
    }
    //设置图像输出大小
    //OV2640输出图像的大小(分辨率),完全由改函数确定
    //width,height:宽度(对应:horizontal)和高度(对应:vertical),width和height必须是4的倍数
    //返回值:0,设置成功
    //    其他,设置失败
    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;
    }
    //设置图像开窗大小
    //由:OV2640_ImageSize_Set确定传感器输出分辨率从大小.
    //该函数则在这个范围上面进行开窗,用于OV2640_OutSize_Set的输出
    //注意:本函数的宽度和高度,必须大于等于OV2640_OutSize_Set函数的宽度和高度
    //     OV2640_OutSize_Set设置的宽度和高度,根据本函数设置的宽度和高度,由DSP
    //     自动计算缩放比例,输出给外部设备.
    //width,height:宽度(对应:horizontal)和高度(对应:vertical),width和height必须是4的倍数
    //返回值:0,设置成功
    //    其他,设置失败
    u8 OV2640_ImageWin_Set(u16 offx,u16 offy,u16 width,u16 height)
    {
        u16 hsize;
        u16 vsize;
        u8 temp; 
        if(width%4)return 1;
        if(height%4)return 2;
        hsize=width/4;
        vsize=height/4;
        SCCB_WR_Reg(0XFF,0X00);    
        SCCB_WR_Reg(0XE0,0X04);                    
        SCCB_WR_Reg(0X51,hsize&0XFF);        //设置H_SIZE的低八位
        SCCB_WR_Reg(0X52,vsize&0XFF);        //设置V_SIZE的低八位
        SCCB_WR_Reg(0X53,offx&0XFF);        //设置offx的低八位
        SCCB_WR_Reg(0X54,offy&0XFF);        //设置offy的低八位
        temp=(vsize>>1)&0X80;
        temp|=(offy>>4)&0X70;
        temp|=(hsize>>5)&0X08;
        temp|=(offx>>8)&0X07; 
        SCCB_WR_Reg(0X55,temp);                //设置H_SIZE/V_SIZE/OFFX,OFFY的高位
        SCCB_WR_Reg(0X57,(hsize>>2)&0X80);    //设置H_SIZE/V_SIZE/OFFX,OFFY的高位
        SCCB_WR_Reg(0XE0,0X00);    
        return 0;
    } 
    //该函数设置图像尺寸大小,也就是所选格式的输出分辨率
    //UXGA:1600*1200,SVGA:800*600,CIF:352*288
    //width,height:图像宽度和图像高度
    //返回值:0,设置成功
    //    其他,设置失败
    u8 OV2640_ImageSize_Set(u16 width,u16 height)
    { 
        u8 temp; 
        SCCB_WR_Reg(0XFF,0X00);            
        SCCB_WR_Reg(0XE0,0X04);            
        SCCB_WR_Reg(0XC0,(width)>>3&0XFF);        //设置HSIZE的10:3位
        SCCB_WR_Reg(0XC1,(height)>>3&0XFF);        //设置VSIZE的10:3位
        temp=(width&0X07)<<3;
        temp|=height&0X07;
        temp|=(width>>4)&0X80; 
        SCCB_WR_Reg(0X8C,temp);    
        SCCB_WR_Reg(0XE0,0X00);                 
        return 0;
    }

     (4)Cube自动生成的DCMI配置,以及手动添加DMA通道配置

    DCMI_HandleTypeDef hdcmi;
    DMA_HandleTypeDef hdma_dcmi;
    
    /* DCMI init function */
    void MX_DCMI_Init(void)
    {
    
      hdcmi.Instance = DCMI;
      hdcmi.Init.SynchroMode = DCMI_SYNCHRO_HARDWARE;
      hdcmi.Init.PCKPolarity = DCMI_PCKPOLARITY_RISING;
      hdcmi.Init.VSPolarity = DCMI_VSPOLARITY_LOW;
      hdcmi.Init.HSPolarity = DCMI_HSPOLARITY_LOW;
      hdcmi.Init.CaptureRate = DCMI_CR_ALL_FRAME;
      hdcmi.Init.ExtendedDataMode = DCMI_EXTEND_DATA_8B;
      hdcmi.Init.JPEGMode = DCMI_JPEG_DISABLE;
      if (HAL_DCMI_Init(&hdcmi) != HAL_OK)
      {
        Error_Handler();
      }
    
    }
    
    void HAL_DCMI_MspInit(DCMI_HandleTypeDef* dcmiHandle)
    {
    
      GPIO_InitTypeDef GPIO_InitStruct = {0};
      if(dcmiHandle->Instance==DCMI)
      {
      /* USER CODE BEGIN DCMI_MspInit 0 */
    
      /* USER CODE END DCMI_MspInit 0 */
        /* DCMI clock enable */
        __HAL_RCC_DCMI_CLK_ENABLE();
      
        __HAL_RCC_GPIOA_CLK_ENABLE();
        __HAL_RCC_GPIOH_CLK_ENABLE();
        __HAL_RCC_GPIOC_CLK_ENABLE();
        __HAL_RCC_GPIOD_CLK_ENABLE();
        __HAL_RCC_GPIOB_CLK_ENABLE();
        /**DCMI GPIO Configuration    
        PA6     ------> DCMI_PIXCK
        PH8     ------> DCMI_HSYNC
        PC6     ------> DCMI_D0
        PC7     ------> DCMI_D1
        PC8     ------> DCMI_D2
        PC9     ------> DCMI_D3
        PC11     ------> DCMI_D4
        PD3     ------> DCMI_D5
        PB7     ------> DCMI_VSYNC
        PB8     ------> DCMI_D6
        PB9     ------> DCMI_D7 
        */
        GPIO_InitStruct.Pin = GPIO_PIN_6;
        GPIO_InitStruct.Mode = GPIO_MODE_AF_PP;
        GPIO_InitStruct.Pull = GPIO_NOPULL;
        GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_LOW;
        GPIO_InitStruct.Alternate = GPIO_AF13_DCMI;
        HAL_GPIO_Init(GPIOA, &GPIO_InitStruct);
    
        GPIO_InitStruct.Pin = GPIO_PIN_8;
        GPIO_InitStruct.Mode = GPIO_MODE_AF_PP;
        GPIO_InitStruct.Pull = GPIO_NOPULL;
        GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_LOW;
        GPIO_InitStruct.Alternate = GPIO_AF13_DCMI;
        HAL_GPIO_Init(GPIOH, &GPIO_InitStruct);
    
        GPIO_InitStruct.Pin = GPIO_PIN_6|GPIO_PIN_7|GPIO_PIN_8|GPIO_PIN_9 
                              |GPIO_PIN_11;
        GPIO_InitStruct.Mode = GPIO_MODE_AF_PP;
        GPIO_InitStruct.Pull = GPIO_NOPULL;
        GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_LOW;
        GPIO_InitStruct.Alternate = GPIO_AF13_DCMI;
        HAL_GPIO_Init(GPIOC, &GPIO_InitStruct);
    
        GPIO_InitStruct.Pin = GPIO_PIN_3;
        GPIO_InitStruct.Mode = GPIO_MODE_AF_PP;
        GPIO_InitStruct.Pull = GPIO_NOPULL;
        GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_LOW;
        GPIO_InitStruct.Alternate = GPIO_AF13_DCMI;
        HAL_GPIO_Init(GPIOD, &GPIO_InitStruct);
    
        GPIO_InitStruct.Pin = GPIO_PIN_7|GPIO_PIN_8|GPIO_PIN_9;
        GPIO_InitStruct.Mode = GPIO_MODE_AF_PP;
        GPIO_InitStruct.Pull = GPIO_NOPULL;
        GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_LOW;
        GPIO_InitStruct.Alternate = GPIO_AF13_DCMI;
        HAL_GPIO_Init(GPIOB, &GPIO_InitStruct);
    
        /* DCMI DMA Init */
        /* DCMI Init */
        hdma_dcmi.Instance = DMA2_Stream1;
        hdma_dcmi.Init.Channel = DMA_CHANNEL_1;
        hdma_dcmi.Init.Direction = DMA_PERIPH_TO_MEMORY;
        hdma_dcmi.Init.PeriphInc = DMA_PINC_DISABLE;
        hdma_dcmi.Init.MemInc = DMA_MINC_ENABLE;
        hdma_dcmi.Init.PeriphDataAlignment = DMA_PDATAALIGN_WORD;
        hdma_dcmi.Init.MemDataAlignment = DMA_MDATAALIGN_WORD;
        hdma_dcmi.Init.Mode = DMA_NORMAL;
        hdma_dcmi.Init.Priority = DMA_PRIORITY_LOW;
        hdma_dcmi.Init.FIFOMode = DMA_FIFOMODE_ENABLE;
        hdma_dcmi.Init.FIFOThreshold = DMA_FIFO_THRESHOLD_FULL;
        hdma_dcmi.Init.MemBurst = DMA_MBURST_SINGLE;
        hdma_dcmi.Init.PeriphBurst = DMA_PBURST_INC4;
        if (HAL_DMA_Init(&hdma_dcmi) != HAL_OK)
        {
          Error_Handler();
        }
    
        __HAL_LINKDMA(dcmiHandle,DMA_Handle,hdma_dcmi);
    
      /* USER CODE BEGIN DCMI_MspInit 1 */
      printf("DCMI GPIO Init Yes
    ");
      /* USER CODE END DCMI_MspInit 1 */
      }
    }
    
    void HAL_DCMI_MspDeInit(DCMI_HandleTypeDef* dcmiHandle)
    {
    
      if(dcmiHandle->Instance==DCMI)
      {
      /* USER CODE BEGIN DCMI_MspDeInit 0 */
    
      /* USER CODE END DCMI_MspDeInit 0 */
        /* Peripheral clock disable */
        __HAL_RCC_DCMI_CLK_DISABLE();
      
        /**DCMI GPIO Configuration    
        PA6     ------> DCMI_PIXCK
        PH8     ------> DCMI_HSYNC
        PC6     ------> DCMI_D0
        PC7     ------> DCMI_D1
        PC8     ------> DCMI_D2
        PC9     ------> DCMI_D3
        PC11     ------> DCMI_D4
        PD3     ------> DCMI_D5
        PB7     ------> DCMI_VSYNC
        PB8     ------> DCMI_D6
        PB9     ------> DCMI_D7 
        */
        HAL_GPIO_DeInit(GPIOA, GPIO_PIN_6);
    
        HAL_GPIO_DeInit(GPIOH, GPIO_PIN_8);
    
        HAL_GPIO_DeInit(GPIOC, GPIO_PIN_6|GPIO_PIN_7|GPIO_PIN_8|GPIO_PIN_9 
                              |GPIO_PIN_11);
    
        HAL_GPIO_DeInit(GPIOD, GPIO_PIN_3);
    
        HAL_GPIO_DeInit(GPIOB, GPIO_PIN_7|GPIO_PIN_8|GPIO_PIN_9);
    
        /* DCMI DMA DeInit */
        HAL_DMA_DeInit(dcmiHandle->DMA_Handle);
      /* USER CODE BEGIN DCMI_MspDeInit 1 */
    
      /* USER CODE END DCMI_MspDeInit 1 */
      }
    } 
    
    /* USER CODE BEGIN 1 */
    //DCMI DMA配置
    //mem0addr:存储器地址0  将要存储摄像头数据的内存地址(也可以是外设地址)
    //mem1addr:存储器地址1  当只使用mem0addr的时候,该值必须为0
    //memblen:存储器位宽,可以为:DMA_MDATAALIGN_BYTE/DMA_MDATAALIGN_HALFWORD/DMA_MDATAALIGN_WORD
    //meminc:存储器增长方式,可以为:DMA_MINC_ENABLE/DMA_MINC_DISABLE
    void DCMI_DMA_Init(u32 mem0addr,u32 mem1addr,u16 memsize,u32 memblen,u32 meminc)
    { 
        __HAL_RCC_DMA2_CLK_ENABLE();                                    //使能DMA2时钟
        __HAL_LINKDMA(&hdcmi,DMA_Handle,hdma_dcmi);        //将DMA与DCMI联系起来
        hdma_dcmi.Instance=DMA2_Stream1;                          //DMA2数据流1                     
        hdma_dcmi.Init.Channel=DMA_CHANNEL_1;                     //通道1
        hdma_dcmi.Init.Direction=DMA_PERIPH_TO_MEMORY;            //外设到存储器
        hdma_dcmi.Init.PeriphInc=DMA_PINC_DISABLE;                //外设非增量模式
        hdma_dcmi.Init.MemInc=meminc;                             //存储器增量模式
        hdma_dcmi.Init.PeriphDataAlignment=DMA_PDATAALIGN_WORD;   //外设数据长度:32位
        hdma_dcmi.Init.MemDataAlignment=memblen;                  //存储器数据长度:8/16/32位
        hdma_dcmi.Init.Mode=DMA_CIRCULAR;                         //使用循环模式 
        hdma_dcmi.Init.Priority=DMA_PRIORITY_HIGH;                //高优先级
        hdma_dcmi.Init.FIFOMode=DMA_FIFOMODE_ENABLE;              //使能FIFO
        hdma_dcmi.Init.FIFOThreshold=DMA_FIFO_THRESHOLD_HALFFULL; //使用1/2的FIFO 
        hdma_dcmi.Init.MemBurst=DMA_MBURST_SINGLE;                //存储器突发传输
        hdma_dcmi.Init.PeriphBurst=DMA_PBURST_SINGLE;             //外设突发单次传输 
        HAL_DMA_DeInit(&hdma_dcmi);                               //先清除以前的设置
        HAL_DMA_Init(&hdma_dcmi);                                    //初始化DMA
        
        //在开启DMA之前先使用__HAL_UNLOCK()解锁一次DMA,因为HAL_DMA_Statrt()HAL_DMAEx_MultiBufferStart()
        //这两个函数一开始要先使用__HAL_LOCK()锁定DMA,而函数__HAL_LOCK()会判断当前的DMA状态是否为锁定状态,如果是
        //锁定状态的话就直接返回HAL_BUSY,这样会导致函数HAL_DMA_Statrt()和HAL_DMAEx_MultiBufferStart()后续的DMA配置
        //程序直接被跳过!DMA也就不能正常工作,为了避免这种现象,所以在启动DMA之前先调用__HAL_UNLOC()先解锁一次DMA。
       if (HAL_DMA_Init(&hdma_dcmi) != HAL_OK)
        {
          Error_Handler();
        }
    
        __HAL_LINKDMA(&hdcmi,DMA_Handle,hdma_dcmi);
            
            __HAL_UNLOCK(&hdma_dcmi);
            HAL_DMA_Start(&hdma_dcmi,(u32)&DCMI->DR,mem0addr,memsize);
    
    }
    
    //DCMI,启动传输
    void DCMI_Start(void)
    {  
        LCD_SetCursor(0,0);  
        LCD_WriteRAM_Prepare();                //开始写入GRAM
        __HAL_DMA_ENABLE(&hdma_dcmi); //使能DMA
        DCMI->CR|=DCMI_CR_CAPTURE;          //DCMI捕获使能
        printf("DCMI_Start OK
    ");
    }
    
    //DCMI,关闭传输
    void DCMI_Stop(void)
    { 
        DCMI->CR&=~(DCMI_CR_CAPTURE);       //关闭捕获
        while(DCMI->CR&0X01);               //等待传输完成
        __HAL_DMA_DISABLE(&hdma_dcmi);      //关闭DMA
         
    } 
    /* USER CODE END 1 */

     4.程序检测

        该例程是用开发板自带的MCU屏显示摄像头拍摄的影像,初始化的时候记得加上下面两个函数,要不然会无图像显示

        HAL_DCMI_Start_DMA(&hdcmi,DCMI_MODE_CONTINUOUS,(u32)&LCD->LCD_RAM,1);        //DCMI启动DMA通道
        HAL_DMA_Start(&hdma_dcmi,(u32)&DCMI->DR,(u32)&LCD->LCD_RAM,1);
    int main(void)
    {
      /* USER CODE BEGIN 1 */
      uint8_t x=0;
        uint16_t outputheight=0;
      /* USER CODE END 1 */
      
    
      /* MCU Configuration--------------------------------------------------------*/
    
      /* Reset of all peripherals, Initializes the Flash interface and the Systick. */
      HAL_Init();
    
      /* USER CODE BEGIN Init */
      
      /* USER CODE END Init */
    
      /* Configure the system clock */
      SystemClock_Config();
    
      /* USER CODE BEGIN SysInit */
    //   Stm32_Clock_Init(360,25,2,8);   //设置时钟,180Mhz
      /* USER CODE END SysInit */
    
      /* Initialize all configured peripherals */
      MX_GPIO_Init();
      MX_DMA_Init();
      MX_USART1_UART_Init();
      MX_DCMI_Init();
    //  MX_FMC_Init();
      /* USER CODE BEGIN 2 */
        delay_init(180);                //初始化延时函数
      LCD_Init();                        //初始化LCD 
        
        
        vSCCB_Handle_Init();
        PCF8574_Init();
    
          while(OV2640_Init())
        {
            printf("ov2640  no
    ");
        }
        printf("ov2640  Yse
    ");
        
        OV2640_RGB565_Mode();    //RGB565模式 
        OV2640_Light_Mode(1);    //自动模式
        OV2640_Color_Saturation(3);//色彩饱和度0
        OV2640_Brightness(4);    //亮度0
        OV2640_Contrast(3);        //对比度0
      MX_DCMI_Init();
        
    
      DCMI_DMA_Init((u32)&LCD->LCD_RAM,0,1,DMA_MDATAALIGN_HALFWORD,DMA_MINC_DISABLE);            //DCMI DMA配置,MCU屏,竖屏
        yoffset=0;
        outputheight=lcddev.height;
            
        curline=yoffset;        //行数复位
        OV2640_OutSize_Set(lcddev.width,outputheight);    //满屏缩放显示
        LCD_Clear(BLACK);
        DCMI_Start();             //启动传输 
        HAL_DCMI_Start_DMA(&hdcmi,DCMI_MODE_CONTINUOUS,(u32)&LCD->LCD_RAM,1);        //DCMI启动DMA通道
        HAL_DMA_Start(&hdma_dcmi,(u32)&DCMI->DR,(u32)&LCD->LCD_RAM,1);
      /* USER CODE END 2 */
    
      /* Infinite loop */
      /* USER CODE BEGIN WHILE */
      while (1)
      {
        /* USER CODE END WHILE */
    
        /* USER CODE BEGIN 3 */
      }
      /* USER CODE END 3 */
    }
  • 相关阅读:
    Spring Boot 自定义属性 以及 乱码问题
    IDEA 修改文件编码
    Gojs简单例子
    无法转换json问题 Error: Model.nodeDataArray value is not an instance of Array or NodeList or HTMLCollection
    java json转换
    git设置HTTP代理
    thymeleaf中的日期格式化
    thymeleaf:字符串Strings常见的使用方法
    thymeleaf+bootstrap,onclick传参实现模态框中遇到的错误
    Thymeleaf教程 (十二) 标签内,js中使用表达式
  • 原文地址:https://www.cnblogs.com/feiniaoliangtiangao/p/11407787.html
Copyright © 2020-2023  润新知