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(垂直同步)信號硬件同步, 或者通 過數據流中嵌入的同步碼同步。curl
(1)打開Cube F429芯片上的DCMI功能,模式和配置以下:函數
(2)DCMI的複用GPIO口以下,這樣就能夠直接使用板載的DCMI接口oop
(3)打開DCMI的DMA通道測試
(4)選擇PB3和PB4爲SCCB協議的引腳,用來和OV2640通訊ui
(1)SCCB協議差很少至關於IIC協議,只是應當信號有點區別url
SCCB_HandleTypedef hSCCBx;
spa
////初始化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);
//} 3d
/*******************************************************************************
* 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;
}
}code
//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\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 } //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\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(0,0); 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 */
該例程是用開發板自帶的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\n"); } printf("ov2640 Yse\n"); 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 */ }