×
单片机 > 单片机程序设计 > 详情

STM32直接驱动ov7670 开发笔记

发布时间:2020-06-09 发布时间:
|
很久没更新技术日志了,最近在调试OV7670摄像头,之中遇见了不少的麻烦。什么花屏啦,彩条不正常呀,等等,很多很多。经过一周的调试可算是调试出来了;期间在网上也查了很多,我想大家都会发现尽然都不能用,网上配置的寄存器表都是有错误的(对于没有fifo的,有fifo的我没试过),我花了一天时间认认真真的把OV7670的英文手册看了一遍,发现中文手册错的的太多了,就算是英文手册里有几个寄存器也没说清楚,实验和手册说的不统一。直接驱动摄像头的话STM32可以做到一帧(网上说用DMA方式,我是没办法实现),虽然做出来意义不大,但是可以学习摄像头这方面很多知识。
经过我的调试发现,要成功的关键:首先是时钟的初始化,直接驱动时时钟设置和寄存器设定常理不一样(这有可能是因为直接驱动太慢的缘故)下面代码我会详细说明;第二个是图像数据的读取很容易丢失掉第一个字节,(这是很多花屏的原因,很重要很重)最后就会出现反色呀,有规律的花屏呀,图像不正常等等(有fifo的肯定就不会有这些情况);第三是增益控制,因为直接驱动速度是相当慢的所以得把增益关掉,如果图像全是白的,或者很红有可能就是这原因。第四就是图像窗口和图像格式的设置,下面我把寄存器配置贴出来,注意下面的代码只适合直接驱动方式(时钟由STM32产生)。
 
/**********很重要内部时钟分频如果是直接驱动必须为0x80(不知道为何 直接使用外部时钟颜色不对ox40时)原因是数据起始读取不对   (0x40是容易把第一个数据丢掉,要特别注意)****/    
{0x11,(0x040|0x00)}, 
  /********0x3a 和 0x3d  *************/   
  /***0x3d com13 [1]  0x3a TSLB [3]
   *             1            1        VYUY
   ********************************************/
{0x3a,0x04},//yuv输出顺序
{0x3d,0xc2},//110000010 位[6]:UV饱和度标准-UV自动调整
 
/********0X12 AND 0X40*************
*这两个决定了输出图像的格式
**********************************/
{0x12,0x14},//输出图片模式为QVGA RGB    
{0X40,0xd0},//输出数据范围 rgb565使能
 
{0x1e,0x17},//水平镜像/竖直翻转使能     //0x17为正面  37是镜面
 
/****0x17 18 32  0x19 1a 0a *******
*开窗口的大小设置
***********************************/
{0x17,0x16},//hstart
{0x18,0x04},//hstop
{0x32,0x80},//href
{0x19,0x02},
{0x1a,0x7b},//0x7a
{0x03,0x0a},//帧竖直方向控制 0x0a
 /**********************************/
{0x0c,0x00},//
{0x3e,0x00},//pclk分频  
 
/***********
*图像和测试开关
************************/
{0x70,0x3a},//测试图案
{0x71,0x35},//测试图案 35
{0x72,0x11},//dcw控制 设置亚抽样率 320*240
{0x73,0xf0},//dsp缩放时钟f0                           f0
 
 
{0x55,0x00},//亮度控制 0x00最亮 00
{0x56,0x80},//对比度控制                              80
 
{0xa2,0x02},//像素始终延迟
{0x7a,0x20},//伽马校正
{0x7b,0x10},
{0x7c,0x1e},
{0x7d,0x35},
{0x7e,0x5a},
{0x7f,0x69},
{0x80,0x76},
{0x81,0x80},
{0x82,0x88},
{0x83,0x8f},
{0x84,0x96},
{0x85,0xa3},
{0x86,0xaf},
{0x87,0xc4},
{0x88,0xd7},
{0x89,0xe8},
{0x13,0xef},//agc/aec/awb使能 条纹滤波
 
/*********增益控制********
*慢速是要关掉
*******************************/
{0x00,0x00},//自动增益控制  ff
/****曝光值 该寄存器提供[9:2] 07 AEGHH[15:10] 04COM1[1:0]******/
{0x07,0x00},
{0x10,0x00},
{0x04,0x00},
  
{0x0d,0x00},//different 0x60 10
{0x42,0x40},//aec窗口  0x80
{0x14,0x18},//自动增益限度 固定aec/agc
{0xa5,0x08},//50Hz条纹滤波器步长限制 0x08
{0xab,0x08},//60Hz条纹滤波器步长限制 0x08
{0x24,0x75},//AGC/AEC-稳定运行区域(上限)
{0x25,0x63},//AGC/AEC-稳定运行区域(下限)
{0x26,0xd4},//AGC/AEC快速运行区域
{0x9f,0x78},
{0xa0,0x68},
{0xa1,0x03},
{0xa6,0xd8},
{0xa7,0xd8},
{0xa8,0xf0},
{0xa9,0x90},
{0xaa,0x14},
{0x13,0xe5},//打开agc aec                             
{0x0e,0x61},
{0x0f,0x4b},
{0x16,0x02},
{0x21,0x02},
{0x22,0x91},
{0x29,0x07},
{0x33,0x0b},
{0x35,0x0b},
{0x37,0x1d},
{0x38,0x71},
{0x39,0x2a},
{0x3c,0x78},
{0x4d,0x40},
{0x4e,0x20},
{0x69,0x00},//固定增益控制
 
/*****这个很重要 直接驱动时不能设为0x00***/
{0x6b,0x0a},//pll 内部ld0
 
{0x74,0x10},
{0x8d,0x4f},
{0x8e,0x00},
{0x8f,0x00},
{0x90,0x00},
{0x91,0x00},
{0x92,0x19},
{0x96,0x00},
{0x9a,0x80},
{0xb0,0x84},
{0xb1,0x0c},
{0xb2,0x0e},
{0xb3,0x82},
{0xb8,0x0a},
{0x43,0x0a},
{0x44,0xf0},
{0x45,0x34},
{0x46,0x58},
{0x47,0x28},
{0x48,0x3a},
{0x59,0x88},
{0x5a,0x88},
{0x5b,0x44},
{0x5c,0x67},
{0x5d,0x49},
{0x5e,0x0e},
{0x64,0x04},
{0x65,0x20},
{0x66,0x05},
{0x94,0x04},
{0x95,0x08},
{0x6c,0x0a},
{0x6d,0x55},
{0x6e,0x11},
{0x6f,0x9f},//0x9e for advanced AWB
{0x6a,0x40},
{0x01,0x40},
{0x02,0x40},
{0x13,0xe7},
{0x4f,0x86},//80/86/8c/93/99
{0x50,0x86},//80/86/8c/93/99
{0x51,0x00},//00/00/00/00/00
{0x52,0x23},//22/23/25/27/28
{0x53,0x62},//5e/62/67/6c/70
{0x54,0x86},//80/86/8c/93/99
{0x58,0x9e},
{0x41,0x08},
{0x3f,0x1f},//边缘增强调整 位[4:0]:边缘增强系数
{0x75,0x10},//位[4:0]:边缘增强下限 0x0f
{0x76,0xc0},//位[4:0]:边缘增强上限 0x01
{0x4c,0xff},//噪声抑制强度
{0x77,0x01},
{0x3d,0xc2},//110000010 位[6]:UV饱和度标准-UV自动调整
{0x4b,0x09},
{0xc9,0xf0},//饱和度控制
 
{0x41,0x3a},//针对YUV边缘增强阈值自动调整 AWB增益使能
 
{0x34,0x11},
{0x3b,0x0a},
{0xa4,0x88},                                               
{0x96,0x00},
{0x97,0x30},
{0x98,0x20},
{0x99,0x30},
{0x9a,0x84},
{0x9b,0x29},
{0x9c,0x03},
{0x9d,0x4b},
{0x9e,0x3f},
        {0x78,0x04},
{0x79,0x01},
{0xc8,0xf0},
{0x79,0x0f},
{0xc8,0x00},
{0x79,0x10},
{0xc8,0x7e},
{0x79,0x0a},
{0xc8,0x80},
{0x79,0x0b},
{0xc8,0x01},
{0x79,0x0c},
{0xc8,0x0f},
{0x79,0x0d},
{0xc8,0x20},
{0x79,0x09},
{0xc8,0x80},
{0x79,0x02},
{0xc8,0xc0},
{0x79,0x03},
{0xc8,0x40},
{0x79,0x05},
{0xc8,0x30},
{0x79,0x26},
{0x2d,0x00},
{0x2e,0x00},
 
这个配置表一般就可以出图像了,或许你还出不来的话就得看看你用的STM32是不是fsmc接口驱动的lcd,如果不是那就99%不行了。有了配置表我在把主函数贴出来,大家可以改进算法,最近也没时间再改进了(因为我最终的任务是驱动CCD所以cmos只是一个过场);想要提高水品还得靠自己。
 
主程序(关键代码):
 
void main(void)
{
 uint32_t PCnt=0;
 static uint8_t  Cnt= 0;
 u16 value=0,val1=0,val2=0,REG=0;
/**************************/
SysTick_Init();
TIME2_Init( );
START_TIME;
Init_CCD_OV7670( );
LCD_Init();
Delay_us(20);
  LCD_REG=R34;
CCD_EXTI_Init( );
  while(1) 
   {
    PCnt = 0;
        Cnt  = 1;
    XClock_Init( );     //OV7670 XCLK 开 (我用的是TIM2产生PWM波,开关只要改变相应引脚状态就好了)
        while(value & 0x0004)    value = GPIOE->IDR;   // Vsync=H  
        while(~value & 0x0004)   value = GPIOE->IDR;   // Vsync=L   
      XClock_OUT();           //OV7670 XCLK 关
 /*******直接使用时钟时要加一个低电平*************
 *不然会把第一个字节丢掉,产生反色或者是花纹的现象
 *正常数据    (R G B) (R G B) (R G B )
 *丢失第一字节  (G B R) (G B R) (G B
 ************************************************/
    XCLK_L;       
    while(PCnt<76800)
                   {
XCLK_H;
XCLK_L;
REG = GPIOE->IDR;         
if(REG&0x0002)     // HREF = H 水平信号高时有数据
{
if(REG&0x0001){    //PCLK = H   再次判断像素时钟是不是高
 value = GPIOC->IDR;
 if((Cnt == 1))// 高字节
{
val1=(value<<8);
Cnt=0;
}
else // 低字节
{
 val2=(value&0x00ff); 
 Cnt = 1;
 LCD_RAM =val1 |val2; //(我用的是fsmc接口所以才可以这样写)
 PCnt ++;
}
     }
  } 
 }
  }
}
 
//快的时钟一定要用定时器的PWM功能,定时器中断翻转电平是无法实现的,切记。
好了就写这么多,应该对大家有帮助。

『本文转载自网络,版权归原作者所有,如有侵权请联系删除』

热门文章 更多
STM32中断向量表的位置.重定向