基於STM32F429和Cube的ov2640程式

  • 2019 年 10 月 3 日
  • 筆記

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:%drn",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:%drn",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 Yesn");    /* 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 OKn");  }    //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  non");      }      printf("ov2640  Ysen");        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 */  }