历史上的今天
今天是:2025年04月22日(星期二)
2020年04月22日 | 基于MSP430F5438A的OV7670简单驱动程序
2020-04-22 来源:eefocus
作为一个新手,因为课程设计要求,接触到了OV7670。因为课程设计要求使用MSP430F5438A驱动OV7670,而网上大多数都是使用STM32进行驱动。上网查找了很多资料,也仔细看过网上大佬发的相关寄存器的帖子,最后成功使用430单片机完成了设计。本着来源于网络,回馈于网络的原则,在此介绍一下,自己使用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 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;
上一篇:MSP430F5438A中断问题
下一篇:msp430F5438A 定时器
史海拾趣
|
我新手想学习嵌入式开发,现在想搭建一个CE6的开发平台,有一些疑问望高手们不吝指教! 需要定制CE6操作系统内核+驱动程序编写+应用程序开发! 1、工具功能理解 VS2005/2008:驱动程序开发+应用程序开发 PB6:ce6内核定制+模拟调试环境 不知道 ...… 查看全部问答> |
|
4G SD卡开机识别问题~ 有时候能显示盘符,有时候显示不了. 平台 pxa270+wince6.0 显示使用4G SD卡,存在一个奇怪识别问题 有时候开机能显示盘符,有时候显示不了, 如果显示不了. 不过进入系统后再重新插拔一次, 又可以显示及正常使用 2G卡不存在这个问题 看开机过程输出信息, 已经检测到4G卡插入 … 查看全部问答> |
|
修改files下的platform.reg,但是在release下的platform.reg没有的到正确更新。sysgen和copy files都试过了。 D:\\1afc58e7.bmp 这是files下的注册表信息。 但通过copy files 以后得到的结果却是 D:\\8e911ff8.bmp… 查看全部问答> |
|
我是个初学者,看了一本关于WINCE的书,但还有很多疑问 第一个问题,想问下,PB4.2到底是用来做什么的?它的应用步骤是什么啊? 第二个问题想请问大家,如何让2410上的WINCE旋转90度? 网上都说的很笼统,而且都是一样的,我操作上比较困难 … 查看全部问答> |




