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=;
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=;
IIC_Start();
IIC_Send_Byte(PCF8574_ADDR|0X01); //进入接收模式
IIC_Wait_Ack();
temp=IIC_Read_Byte();
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();
} //设置PCF8574某个IO的高低电平
//bit:要设置的IO编号,0~7
//sta:IO的状态;0或1
void PCF8574_WriteBit(u8 bit,u8 sta)
{
u8 data;
data=PCF8574_ReadOneByte(); //先读出原来的设置
if(sta==)data&=~(<<bit);
else data|=<<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&(<<bit))return ;
else return ;
}

(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=;
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(); //POWER ON;
delay_ms();
OV2640_RST=; //复位OV2640
delay_ms();
OV2640_RST=; //结束复位
// SCCB_Init(); //初始化SCCB 的IO口
SCCB_WR_Reg(OV2640_DSP_RA_DLMT, 0x01); //操作sensor寄存器
SCCB_WR_Reg(OV2640_SENSOR_COM7, 0x80); //软复位OV2640
delay_ms();
reg=SCCB_RD_Reg(OV2640_SENSOR_MIDH); //读取厂家ID 高八位
reg<<=;
reg|=SCCB_RD_Reg(OV2640_SENSOR_MIDL); //读取厂家ID 低八位
if(reg!=OV2640_MID)
{
printf("MID:%d\r\n",reg);
return ;
}
reg=SCCB_RD_Reg(OV2640_SENSOR_PIDH); //读取厂家ID 高八位
reg<<=;
reg|=SCCB_RD_Reg(OV2640_SENSOR_PIDL); //读取厂家ID 低八位
if(reg!=OV2640_PID)
{
printf("HID:%d\r\n",reg);
return ;
}
//初始化 OV2640,采用SXGA分辨率(1600*1200)
for(i=;i<sizeof(ov2640_sxga_init_reg_tbl)/;i++)
{
SCCB_WR_Reg(ov2640_sxga_init_reg_tbl[i][],ov2640_sxga_init_reg_tbl[i][]);
}
return 0x00; //ok
}
//OV2640切换为JPEG模式
void OV2640_JPEG_Mode(void)
{
u16 i=;
//设置:YUV422格式
for(i=;i<(sizeof(ov2640_yuv422_reg_tbl)/);i++)
{
SCCB_WR_Reg(ov2640_yuv422_reg_tbl[i][],ov2640_yuv422_reg_tbl[i][]);
} //设置:输出JPEG数据
for(i=;i<(sizeof(ov2640_jpeg_reg_tbl)/);i++)
{
SCCB_WR_Reg(ov2640_jpeg_reg_tbl[i][],ov2640_jpeg_reg_tbl[i][]);
}
}
//OV2640切换为RGB565模式
void OV2640_RGB565_Mode(void)
{
u16 i=;
//设置:RGB565输出
for(i=;i<(sizeof(ov2640_rgb565_reg_tbl)/);i++)
{
SCCB_WR_Reg(ov2640_rgb565_reg_tbl[i][],ov2640_rgb565_reg_tbl[i][]);
}
}
//自动曝光设置参数表,支持5个等级
const static u8 OV2640_AUTOEXPOSURE_LEVEL[][]=
{
{
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=;i<;i++)
{
SCCB_WR_Reg(p[i*],p[i*+]);
}
}
//白平衡设置
//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 ://auto
SCCB_WR_Reg(0XFF,0X00);
SCCB_WR_Reg(0XC7,0X10);//AWB ON
return;
case ://cloudy
regccval=0X65;
regcdval=0X41;
regceval=0X4F;
break;
case ://office
regccval=0X52;
regcdval=0X41;
regceval=0X66;
break;
case ://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+)<<)|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<<);
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 ://-2
reg7d0val=0X18;
reg7d1val=0X34;
break;
case ://-1
reg7d0val=0X1C;
reg7d1val=0X2A;
break;
case ://
reg7d0val=0X24;
reg7d1val=0X16;
break;
case ://
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 ://负片
reg7d0val=0X40;
break;
case ://黑白
reg7d0val=0X18;
break;
case ://偏红色
reg7d0val=0X18;
reg7d1val=0X40;
reg7d2val=0XC0;
break;
case ://偏绿色
reg7d0val=0X18;
reg7d1val=0X40;
reg7d2val=0X40;
break;
case ://偏蓝色
reg7d0val=0X18;
reg7d1val=0XA0;
reg7d2val=0X40;
break;
case ://复古
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&=~(<<);
if(sw)reg|=<<;
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/; //V*2
endy=sy+height/; SCCB_WR_Reg(0XFF,0X01);
temp=SCCB_RD_Reg(0X03); //读取Vref之前的值
temp&=0XF0;
temp|=((endy&0X03)<<)|(sy&0X03);
SCCB_WR_Reg(0X03,temp); //设置Vref的start和end的最低2位
SCCB_WR_Reg(0X19,sy>>); //设置Vref的start高8位
SCCB_WR_Reg(0X1A,endy>>); //设置Vref的end的高8位 temp=SCCB_RD_Reg(0X32); //读取Href之前的值
temp&=0XC0;
temp|=((endx&0X07)<<)|(sx&0X07);
SCCB_WR_Reg(0X32,temp); //设置Href的start和end的最低3位
SCCB_WR_Reg(0X17,sx>>); //设置Href的start高8位
SCCB_WR_Reg(0X18,endx>>); //设置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%)return ;
if(height%)return ;
outw=width/;
outh=height/;
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>>)&0X03;
temp|=(outh>>)&0X04;
SCCB_WR_Reg(0X5C,temp); //设置OUTH/OUTW的高位
SCCB_WR_Reg(0XE0,0X00);
return ;
}
//设置图像开窗大小
//由: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%)return ;
if(height%)return ;
hsize=width/;
vsize=height/;
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>>)&0X80;
temp|=(offy>>)&0X70;
temp|=(hsize>>)&0X08;
temp|=(offx>>)&0X07;
SCCB_WR_Reg(0X55,temp); //设置H_SIZE/V_SIZE/OFFX,OFFY的高位
SCCB_WR_Reg(0X57,(hsize>>)&0X80); //设置H_SIZE/V_SIZE/OFFX,OFFY的高位
SCCB_WR_Reg(0XE0,0X00);
return ;
}
//该函数设置图像尺寸大小,也就是所选格式的输出分辨率
//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)>>&0XFF); //设置HSIZE的10:3位
SCCB_WR_Reg(0XC1,(height)>>&0XFF); //设置VSIZE的10:3位
temp=(width&0X07)<<;
temp|=height&0X07;
temp|=(width>>)&0X80;
SCCB_WR_Reg(0X8C,temp);
SCCB_WR_Reg(0XE0,0X00);
return ;
}

(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 = {};
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\n");
/* 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(,);
LCD_WriteRAM_Prepare(); //开始写入GRAM
__HAL_DMA_ENABLE(&hdma_dcmi); //使能DMA
DCMI->CR|=DCMI_CR_CAPTURE; //DCMI捕获使能
printf("DCMI_Start OK\n");
} //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=;
uint16_t outputheight=;
/* 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(); //初始化延时函数
LCD_Init(); //初始化LCD vSCCB_Handle_Init();
PCF8574_Init(); while(OV2640_Init())
{
printf("ov2640 no\n");
}
printf("ov2640 Yse\n"); OV2640_RGB565_Mode(); //RGB565模式
OV2640_Light_Mode(); //自动模式
OV2640_Color_Saturation();//色彩饱和度0
OV2640_Brightness(); //亮度0
OV2640_Contrast(); //对比度0
MX_DCMI_Init(); DCMI_DMA_Init((u32)&LCD->LCD_RAM,,,DMA_MDATAALIGN_HALFWORD,DMA_MINC_DISABLE); //DCMI DMA配置,MCU屏,竖屏
yoffset=;
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,); //DCMI启动DMA通道
HAL_DMA_Start(&hdma_dcmi,(u32)&DCMI->DR,(u32)&LCD->LCD_RAM,);
/* USER CODE END 2 */ /* Infinite loop */
/* USER CODE BEGIN WHILE */
while ()
{
/* USER CODE END WHILE */ /* USER CODE BEGIN 3 */
}
/* USER CODE END 3 */
}

基于STM32F429和Cube的ov2640程序的更多相关文章

  1. 基于STM32F429和Cube的主从定时器多通道输出固定个数的PWM波形

    主从定时器的原理已在上篇博文: 基于STM32F429+HAL库编写的定时器主从门控模式级联输出固定个数PWM脉冲的程序 讲解了,这篇重点就讲如何实现多通道的PWM级联输出. 1.软件环境 Keil5 ...

  2. 基于 SailingEase WinForm Framework 开发客户端程序(3:实现菜单/工具栏按钮的解耦及状态控制)

    本系列文章将详细阐述客户端应用程序的设计理念,实现方法. 本系列文章以  SailingEase WinForm Framework 为基础进行设计并实现,但其中的设计理念及方法,亦适用于任何类型的客 ...

  3. 基于NSIS脚本开发的安装程序制作软件:易量安装

    原文 基于NSIS脚本开发的安装程序制作软件:易量安装 前几天“萝卜”给我推荐了一款安装程序制作工具——易量安装. 易量安装是一款安装程序制作软件,基于著名的NSIS(Nullsoft Scripta ...

  4. 一个基于ATMEGA128的直流电机抱死程序(转)

    源:一个基于ATMEGA128的直流电机抱死程序 先说一下我的硬件情况:一块ATMEGA128实验板:一个带编码器的80:1的变速电机,编码器的输出端连接到单片机的PD4和PD5引脚:一块电机驱动电路 ...

  5. asp.net core系列 58 IS4 基于浏览器的JavaScript客户端应用程序

    一. 概述 本篇探讨使用"基于浏览器的JavaScript客户端应用程序".与上篇实现功能一样,只不过这篇使用JavaScript作为客户端程序,而非core mvc的后台代码Ht ...

  6. Java:基于MD5的文件监听程序

    前述和需求说明 和之前写的 Python:基于MD5的文件监听程序 是同样的功能,就不啰嗦了,就是又写了一个java版本的,可以移步 python 版本去看一下,整个的核心思路是一样的.代码已上传Gi ...

  7. (4opencv)如何基于GOCW,创建一个实时视频程序

    直接使用提供的代码框架进行修改,是最快得到效果的方法:但是这样的灵活性较差,而且真正的程序员从来都不会停滞在这一步:我们需要的是"将框架解析到最小化.理清楚每个构建之间的关系",只 ...

  8. c++下基于windows socket的服务器客户端程序(基于UDP协议)

    前天写了一个基于tcp协议的服务器客户端程序,今天写了一个基于UDP协议的,由于在上一篇使用TCP协议的服务器中注释已经较为详细,且许多api的调用是相同的,故不再另外注释. 使用UDP协议需要注意几 ...

  9. 基于Shiro,JWT实现微信小程序登录完整例子

    小程序官方流程图如下,官方地址 : https://developers.weixin.qq.com/miniprogram/dev/framework/open-ability/login.html ...

随机推荐

  1. 洛谷P2319 [HNOI2006]超级英雄 题解

    题目链接: https://www.luogu.org/problemnew/show/P2319 分析 每错,这是一道海南不对是河南呀呀呀错了是湖南的省选题. 但是还是可以作为二分图第二题来练手的, ...

  2. SpringMVC面试题:什么是Servlet?

    一.什么是servlet? servlet是一个Java编写的程序,此程序是基于http协议的,在服务器端(如Tomcat)运行的,是按照servlet规范编写的一个Java类.客户端发送请求至服务器 ...

  3. TinycoreLinux的安装使用

    下载 http://www.tinycorelinux.net/7.x/x86_64/release/ distribution_files/ 05-Apr-2016 07:29 - src/ 24- ...

  4. [剑指offer] 23. 二叉搜索树的后序遍历序列

    题目描述 输入一个整数数组,判断该数组是不是某二叉搜索树的后序遍历的结果.如果是则输出Yes,否则输出No.假设输入的数组的任意两个数字都互不相同. 思路: 解法一:递归 二叉搜索树,后序遍历的数组中 ...

  5. Spring Cloud 之 Config与动态路由.

    一.简介  Spring Cloud Confg 是用来为分布式系统中的基础设施和微服务应用提供集中化的外部配置支持,它分为服务端与客户端两个部分.其中服务端也称为分布式配置中心,它是一个独立的微服务 ...

  6. git 必看,各种撤销操作

    场景概念说明 首先说明一个概念, git是一个分布式的版本控制工具,分布式即 git 管理的项目是有多个大致平等的仓库的.通过一个例子来说明这个东西. 举一个最简单的使用场景: 你在github 建立 ...

  7. Selenium浏览器自动化测试框架

    selenium简介 介绍 Selenium [1]  是一个用于Web应用程序测试的工具.Selenium测试直接运行在浏览器中,就像真正的用户在操作一样.支持的浏览器包括IE(7, 8, 9, 1 ...

  8. 原创:用node.js搭建本地服务模拟接口访问实现数据模拟

    前端开发中,数据模拟是必要的,这样就能等后台接口写完,我们直接把接口请求的url地址从本地数据模拟url换成后台真实地址就完成项目了.传参之类的都不用动. 之前网上找了很多类似于mock等感觉都不太实 ...

  9. Flutter学习笔记(13)--表单组件

    如需转载,请注明出处:Flutter学习笔记(13)--表单组件 表单组件是个包含表单元素的区域,表单元素允许用户输入内容,比如:文本区域,下拉表单,单选框.复选框等,常见的应用场景有:登陆.注册.输 ...

  10. thinkphp3.2使用七牛云上传文件

    最近项目中用到了七牛云服务,来分享一下thinkphp使用七牛云来进行文件上传 1.首先在七牛云创建一个空间,例如空间名为test.获取secrectKey,accessKey 2.在thinkphp ...