2062|0

1140

帖子

0

TA的资源

纯净的硅(初级)

楼主
 

MSP430F5438A的OV7670简单驱动程序 [复制链接]

      首先简单的介绍一下所使用的摄像头OV7670。 OV7670是OV(OmniVision)公司生产的一颗1/6寸的CMOS VGA图像传感器。该传感器体积小、工作电压低,提供单片VGA摄像头和影像处理器的所有功能。通过SCCB 总线控制,可以输出整帧、子采样、取窗口等方式的各种分辨率8位影像数据。该产品VGA图像最高达到30帧/秒。用户可以完全控制图像质量、数据格式和传输方式。所有图像处理功能过程包括伽玛曲线、白平衡、度、色度等都可以通过SCCB接口编程。OmmiVision 图像传感器应用独有的传感器技术,通过减少或消除光学或电子缺陷如固定图案噪声、托尾、浮散等,提高图像质量,得到清晰的稳定的彩色图像。其具有高灵敏度、低电压适合嵌入式应用,标准的SCCB接口以及支持RGB565格式输出。因为摄像头的像素时钟非常高,直接通过MSP430的IO口读取数据非常困难,也是十分占耗CPU。采用带FIFO模块的OV7670,通过对FIFO的读取,就可以轻松的读取摄像头采集到图像数据。从而满足速度要求,节省CPU。本次采用的OV7670自带有源晶振,不需要外部再提供时钟。一个FIFO芯片的容量是384K字节,可以存储两帧QVGA的图像数据,所以本次设计采用QVGA模式  RGB565格式传输图像数据。接下来介绍具体的驱动程序。

 

宏定义:

//SCL-P1.3,SDA-P1.6
#define SCCB_SIC_H()     P1OUT|=BIT3 
#define SCCB_SIC_L()     P1OUT&=~BIT3  
 
#define SCCB_SID_H()     P1OUT|=BIT6
#define SCCB_SID_L()     P1OUT&=~BIT6
 
#define SCCB_SID_IN      P1DIR &= ~BIT6    
#define SCCB_SID_OUT     P1DIR |= BIT6   
#define SCCB_SID_STATE     P1IN&BIT6
 
#define OE_L    P4OUT &= ~BIT3
#define OE_H    P4OUT |= BIT3
#define RCLK_L  P4OUT &= ~BIT4
#define RCLK_H  P4OUT |= BIT4
#define WEN_L   P4OUT &= ~BIT5
#define WEN_H   P4OUT |= BIT5
#define WRST_L  P4OUT &= ~BIT6
#define WRST_H  P4OUT |= BIT6
#define RRST_L  P4OUT &= ~BIT7
#define RRST_H  P4OUT |= BIT7
 
//像素存储
#define piexl_w 320
#define piexl_h 240
OV7670初始化程序:

unsigned char ov7670_init(void)
{
 
  unsigned int i=0;
  unsigned char temp;
  //VSYNC-P1.0
  //上拉输入,外部中断
  P1DIR &= ~BIT0;
  P1REN |= BIT0;
  P1OUT |= BIT0;//上拉输入
  
  //FIFO数据输入引脚
  //D0-D3--P6.4-P6.7,D4-D7--P7.4--P7.7
  //上拉输入
  P6DIR &= 0x0f;
  P6REN |= 0xf0;
  P6OUT |= 0xf0;
  P7DIR &= 0x0f;
  P7REN |= 0xf0;
  P7OUT |= 0xf0;
  
  //OE-P4.3,RCLK-P4.4,WEN-P4.5,WRST-P4.6,RRST-P4.7
  //输出
  P4DIR |= 0xf8;
  P4OUT |= 0xf8;
  SCCB_init();
  
        //读写寄存器函数出现错误
    if(wr_Sensor_Reg(0x12,0x80)!= 0 ) //Reset SCCB
    {
          return 1;//错误返回
    }
        
        delay_ms(50);
        
    if(rd_Sensor_Reg(0x0b, &temp) != 0)//读ID
    {
        return 2 ;//错误返回
    }
        
        if(temp==0x73)//OV7670
     {
       for(i=0;i<OV7670_REG_NUM;i++)
       {
        if(wr_Sensor_Reg(OV7670_reg[i][0],OV7670_reg[i][1]) != 0)
        {
                    return 3;//错误返回
        }
       }
         
    }
        return 0; //ok
}
写寄存器操作函数如下: 

//功能:读OV7660寄存器
//返回:0-成功    其他失败
unsigned char rd_Sensor_Reg(unsigned char regID,unsigned char *regDat)
{
    //通过写操作设置寄存器地址
    startSCCB();
    if(SCCBwriteByte(0x42)==0)//写地址
    {
        
        return 1;//错误返回
    }
    delay_us(100);
      if(SCCBwriteByte(regID)==0)//积存器ID
    {
        
        return 2;//错误返回
    }
        delay_us(100);
    stopSCCB();//发送SCCB 总线停止传输命令
    
    delay_us(100);
    
    //设置寄存器地址后,才是读
    startSCCB();
    if(SCCBwriteByte(0x43)==0)//读地址
    {
        
        return 3;//错误返回
    }
    delay_us(100);
      *regDat=SCCBreadByte();//返回读到的值
      noAck();//发送NACK命令
      stopSCCB();//发送SCCB 总线停止传输命令
      return 0;//成功返回
}
 读寄存器操作如下:

//功能:读OV7660寄存器
//返回:0-成功    其他失败
unsigned char rd_Sensor_Reg(unsigned char regID,unsigned char *regDat)
{
    //通过写操作设置寄存器地址
    startSCCB();
    if(SCCBwriteByte(0x42)==0)//写地址
    {
        
        return 1;//错误返回
    }
    delay_us(100);
      if(SCCBwriteByte(regID)==0)//积存器ID
    {
        
        return 2;//错误返回
    }
        delay_us(100);
    stopSCCB();//发送SCCB 总线停止传输命令
    
    delay_us(100);
    
    //设置寄存器地址后,才是读
    startSCCB();
    if(SCCBwriteByte(0x43)==0)//读地址
    {
        
        return 3;//错误返回
    }
    delay_us(100);
      *regDat=SCCBreadByte();//返回读到的值
      noAck();//发送NACK命令
      stopSCCB();//发送SCCB 总线停止传输命令
      return 0;//成功返回
}
简单的SCCB总线控制协议如下:

 
/*
-----------------------------------------------
   功能: 初始化SCCB端口,SCL-P1.3,输出,SCL-P1.3,输出
   参数: 无
 返回值: 无
-----------------------------------------------
*/
void SCCB_init(void)
{
  //SDA-P1.6,上拉输入
  P1DIR &= ~BIT6;
  P1REN |= BIT6;
  P1OUT |= BIT6;
  //SCL-P1.3,输出
  P1DIR |= BIT3;
  P1OUT |= BIT3;
  
  SCCB_SID_OUT;
}
/*
-----------------------------------------------
   功能: start命令,SCCB的起始信号
   参数: 无
 返回值: 无
-----------------------------------------------
*/
void startSCCB(void)
{
    SCCB_SID_H();     //数据线高电平
    SCCB_SIC_H();     //在时钟线高的时候数据线由高至低
    delay_us(50);
    SCCB_SID_L();
    delay_us(50);
    SCCB_SIC_L();     //时钟恢复低电平,单操作函数必要
}
/*
-----------------------------------------------
   功能: stop命令,SCCB的停止信号
   参数: 无
 返回值: 无
-----------------------------------------------
*/
void stopSCCB(void)
{
    SCCB_SID_L();
    delay_us(50);
    SCCB_SIC_H();    
    delay_us(50);
    SCCB_SID_H();    
    delay_us(50);
}
/*
-----------------------------------------------
   功能: noAck,用于连续读取中的最后一个结束周期
   参数: 无
 返回值: 无
-----------------------------------------------
*/
void noAck(void)
{
    delay_us(50);
    SCCB_SID_H();    
    SCCB_SIC_H();    
    delay_us(50);
    SCCB_SIC_L();    
    delay_us(50);
    SCCB_SID_L();    
    delay_us(50);
}
/*
-----------------------------------------------
   功能: 写入一个字节的数据到SCCB
   参数: 写入数据
 返回值: 发送成功返回1,发送失败返回0
-----------------------------------------------
*/
unsigned int SCCBwriteByte(unsigned int m_data)
{
    unsigned char j,tem;
 
    for(j=0;j<8;j++) //循环8次发送数据
    {
        if(m_data&0x80)
        {
                    SCCB_SID_H();    
        }
        else
        {
                    SCCB_SID_L();    
        }
                m_data<<=1;
        delay_us(50);
        SCCB_SIC_H();    
        delay_us(50);
        SCCB_SIC_L();    
    }
    SCCB_SID_IN;/*设置SDA为输入*/
    delay_us(50);
    SCCB_SIC_H();    
    delay_us(50);
        
    if(SCCB_SID_STATE){tem=0;}   //SDA=1发送失败,返回0}
    else {tem=1;}   //SDA=0发送成功,返回1
    SCCB_SIC_L();        
        SCCB_SID_OUT;/*设置SDA为输出*/
 
    return tem;  
}
/*
-----------------------------------------------
   功能: 一个字节数据读取并且返回
   参数: 无
 返回值: 读取到的数据
-----------------------------------------------
*/
unsigned char SCCBreadByte(void)
{
    unsigned char read,j;
    read = 0x00;
    
    SCCB_SID_IN;/*设置SDA为输入*/
    for(j=8;j>0;j--) //循环8次接收数据
    {             
        delay_us(50);
        SCCB_SIC_H();
        read=read<<1;
        if(SCCB_SID_STATE) 
        {
                    read++;
        }
                delay_us(50);
        SCCB_SIC_L();
        
    }    
        SCCB_SID_OUT;/*设置SDA为输出*/
    return read;
}
 
        图像采集最重要的就是FIFO模块如何存储图像数据以及单片机如何读取FIFO模块中的图像数据。其具体的实现步骤如下:

摄像头模块存储图数据的过程为:等待OV7670同步信号、FIFO写指针复位、FIFO写使能、等待第二个OV7670同步信号、FIFO写禁止。

        在存储完一帧图像以后,就可以开始读取图像数据了。读取过程为:FIFO读指针复位、给FIFO读时钟(FIFO_RCLK)、读取第一个像素高字节、给FIFO读时钟、读取第一个像素低字节、给FIFO读时钟、读取第二个像素高字节、循环读取剩余像素、结束。

         因此使用一个外部中断(设计中为P1.0),来捕获帧同步信号,在中断服务函数中开始将OV7670的图像数据存储在FIFO芯片中。然后在一个帧同步信号到来之后,关闭数据存储。这样一帧数据就存储完成。中断服务函数源码如下:

//FIFO模块存储摄像头数据
#pragma vector = PORT1_VECTOR
__interrupt void PORT1_B0_ISR(void)
{
  if(P1IV == 2)
  {
      WRST_L;//开始复位写指针
      WRST_H;//写指针复位结束
      if(ov_sta == 0) 
      {
        WEN_H;
        ov_sta = 1;
      }
      else if(ov_sta == 1) 
      {
        WEN_L;
        ov_sta = 2;
      }
  }
  P1IFG = 0;     //清除标志位
}
      当FIFO芯片中一帧图像数据存储完毕就可以在主函数中进行430对FIFO芯片中数据的读取,特别注意,在读取FIFO芯片中的图像数据时,CS位要置低,否则输入管脚为高阻态。根据时序要求编写程序,源码如下:

  

  
/* OE  AL422 FIFO的输出使能引脚 ,OE为低电平时,允许数据输出 ,
  高电平时,数据输出高阻态*/
   OE_L;
if(ov_sta == 2)//读数据
      {
        P1IE &= ~BIT0;//关外部中断
        //设置图像分辨率
        OV7670_Window_Set(180,10,piexl_w,piexl_h);
        
        RRST_L;//开始复位读指针
        RCLK_L;
        RCLK_H;
        RCLK_L;
        RRST_H;//读指针复位结束
        RCLK_H;
       
        for(unsigned int p=0;p < piexl_h;p++)//传输图像piexl_w*piexl_h
        {
          for(unsigned int j=0;j < piexl_w;j++)
          {
            RCLK_L;
            FIFO_1 = P6IN&0xf0;
            FIFO_2 = P7IN&0xf0;
            FIFO_data = (FIFO_1>>4)|FIFO_2;
            data_fifo[0] = FIFO_data;//读取高字节
            RCLK_H;  
            RCLK_L;
            FIFO_1 = P6IN&0xf0;//读取低字节
            FIFO_2 = P7IN&0xf0;
            FIFO_data = (FIFO_1>>4)|FIFO_2;
            data_fifo[1] = FIFO_data;
            RCLK_H; 
          }  
        }
        ov_sta = 0;
        P1IE |= BIT0;
     }
        因为采用的是MSP430F5438a单片机,以QVGA模式传输图像数据还是很多,速度很慢。为降低传输的图像数据量,提高数据,借鉴了网上一个设置图像分辨率的函数,源码如下:

//QVGA 分辨率设置
void OV7670_Window_Set(unsigned int sx,unsigned int sy,unsigned int width,unsigned int height)
{
    unsigned int endx;
    unsigned int endy;
    unsigned char temp; 
    endx=(sx+width*2)%784;    //   sx:HSTART endx:HSTOP    
     endy=sy+height*2;        //   sy:VSTRT endy:VSTOP        
 
        rd_Sensor_Reg(0x32,&temp);   
    temp&=0Xc0;
    temp|=((endx&0X07)<<3)|(sx&0X07);    
    wr_Sensor_Reg(0X032,temp);
    wr_Sensor_Reg(0X17,sx>>3);            
    wr_Sensor_Reg(0X18,endx>>3);            
 
        rd_Sensor_Reg(0x03,&temp);   
    temp&=0Xf0;
    temp|=((endy&0X03)<<2)|(sy&0X03);
    wr_Sensor_Reg(0X03,temp);                
    wr_Sensor_Reg(0X19,sy>>2);            
    wr_Sensor_Reg(0X1A,endy>>2);            
}
 
 

 
点赞 关注

回复
举报
您需要登录后才可以回帖 登录 | 注册

随便看看
查找数据手册?

EEWorld Datasheet 技术支持

相关文章 更多>>
关闭
站长推荐上一条 1/7 下一条

 
EEWorld订阅号

 
EEWorld服务号

 
汽车开发圈

About Us 关于我们 客户服务 联系方式 器件索引 网站地图 最新更新 手机版

站点相关: 国产芯 安防电子 汽车电子 手机便携 工业控制 家用电子 医疗电子 测试测量 网络通信 物联网

北京市海淀区中关村大街18号B座15层1530室 电话:(010)82350740 邮编:100190

电子工程世界版权所有 京B2-20211791 京ICP备10001474号-1 电信业务审批[2006]字第258号函 京公网安备 11010802033920号 Copyright © 2005-2025 EEWORLD.com.cn, Inc. All rights reserved
快速回复 返回顶部 返回列表