普通低速单片机驱动OV7670等摄像头为啥要用FIFO

Posted

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了普通低速单片机驱动OV7670等摄像头为啥要用FIFO相关的知识,希望对你有一定的参考价值。

首先你要知道,fifo是缓冲区,缓冲区顾名思义就是速度不匹配,要等一帧数据转换完后采集 参考技术A 这样读取信号快 参考技术B 速度不匹配

单片机开发OV2640在没有DCMI接口的情况下的STM32驱动

文章目录

(一)背景介绍

在之前刚学STM32的时候完成了一个ov7670的驱动
ov7670驱动
已经快要两年过去了,最近抽了一点时间又将之前搞得ov2640的驱动完善了一下
看一下效果吧。

(二)接线

GNDSCLSDAD0D2D4D6PCLKPWDN
GNDPC1PC0PA0PA2PA4PA6PB10PC3
3.3VSYNCHREFRSTD1D3D5D7NC
3.3PC13PB11PC2PA1PA3PA5PA7随便

(三)软件实现

main.c

#include "led.h"
#include "delay.h"
#include "sys.h"
#include "usart.h"
#include "KEY.h"
#include "adc.h"
#include "malloc.h"
#include "Camera.h"
#include "LCD.h"
//PWDN 掉电模式高电平有效
//HREF 行同步信号
//VSYNC 帧同步信号
//PCLK像素同步信号 OV2640自带晶振,直接设置为输入

//GND	SCL	SDA	D0  D2  D4  D6   PCLK PWDN
//GND PC1 PC0 PA0 PA2 PA4 PA6  PB10	PC3

//3.3 VSYNC HREF RST D1  D3  D5  D7   NC 
//3.3 PC13  PB11 PC2 PA1 PA3 PA5 PA7
#define JPEG_TEST

#ifdef JPEG_TEST
//JPEG尺寸支持列表
const u16 jpeg_img_size_tbl[][2]=

	176,144,	//QCIF
	160,120,	//QQVGA
	352,288,	//CIF
	320,240,	//QVGA
	640,480,	//VGA
	800,600,	//SVGA
	1024,768,	//XGA
	1280,1024,	//SXGA
	1600,1200,	//UXGA
; 
u8* jpeg_buf;
#define jpeg_buf_size 40*1024  			//定义JPEG数据缓存jpeg_buf的大小(*4字节)
volatile u32 jpeg_data_len=0; 			//buf中的JPEG有效数据长度 
/* 串口1发送数组 */
void JPEG_DMA_SendArray()

		uint16_t sendLen =jpeg_buf_size;	// 防止越界
    while (DMA_GetCurrDataCounter(DMA1_Channel4));  // 检查DMA发送通道内是否还有数据
    // DMA发送数据-要先关 设置发送长度 开启DMA
    DMA_Cmd(DMA1_Channel4, DISABLE);
    DMA_SetCurrDataCounter(DMA1_Channel4, sendLen);   // 重新写入要传输的数据数量
    DMA_Cmd(DMA1_Channel4, ENABLE);     // 启动DMA发送  

//JPEG测试
//JPEG数据,通过串口1发送给电脑.
void jpeg_test(void)

	u32 i; 
	u8 *p;
	u8 key;
	u8 effect=0,expose=0;
	u8 size=3;		//默认是QVGA 320*240尺寸
	u8 msgbuf[15];	//消息缓存区 
	mem_init();	
	jpeg_buf=mymalloc(jpeg_buf_size);//32K左右
	while(!jpeg_buf)	
		printf("MALLOC DATA\\n");
		delay_ms(200);
	
	
	LCD_Clear(WHITE);
	OV2640_JPEG_Mode();		//JPEG模式  
	OV2640_OutSize_Set(jpeg_img_size_tbl[size][0],jpeg_img_size_tbl[size][1]);//设置输出尺寸 
		while(1)
		
				key=KEY_Scan(0);
				if(key==KEY0_PRES)
					effect++;
					effect%=7;
					OV2640_Special_Effects(effect);
				
				if(key==WKUP_PRES)
					expose++;
					expose%=5;					
					OV2640_Auto_Exposure(expose);
				
				while(OV2640_VSYNC==0);//等待帧信号
					 while(OV2640_VSYNC==1)
							while(OV2640_HREF)
							 
								while(OV2640_PCLK==0);
									jpeg_buf[jpeg_data_len++]=OV2640_DATA; 
									if(jpeg_data_len>jpeg_buf_size)
										JPEG_DMA_SendArray();
										jpeg_data_len=0;	
									
								while(OV2640_PCLK==1);
							
						
		  


#endif


#ifdef RGB_TEST


u8* ov2640_framebuf;	
void rgb565_test(void)

 	u8 key;
	u8 effect=0,expose=0;
	int i=0,j=0;
	u16 pixcnt=0;				//像素统计
	u32 pix=0;
	u16 linepix=0;
	u16 linecnt=0;				//行数统计	
	mem_init();	
	ov2640_framebuf=mymalloc(128*128*2);//32K左右
	while(!ov2640_framebuf)	
		printf("MALLOC DATA\\n");
		delay_ms(200);
	
	OV2640_RGB565_Mode();
	OV2640_OutSize_Set(128,128); 
	while(1)
	
				key=KEY_Scan(0);
				if(key==KEY0_PRES)
					effect++;
					effect%=7;
					OV2640_Special_Effects(effect);
				
				if(key==WKUP_PRES)
					expose++;
					expose%=5;					
					OV2640_Auto_Exposure(expose);
				
		pix=0;
		while(OV2640_VSYNC)			//等待帧信号
			
		
		pixcnt=0;								//像素计数器清零
		linecnt=0;					//行统计清零
		while(linecnt<128)	
		
			while(OV2640_HREF)
			                                                                                                                         
				while(OV2640_PCLK==0);
				ov2640_framebuf[256*linecnt+linepix]=OV2640_DATA; 
				pix++;
				linepix++;
				while(OV2640_PCLK==1); 
				while(OV2640_PCLK==0); 
				ov2640_framebuf[256*linecnt+linepix]=OV2640_DATA; 
				pix++;
				linepix++;
				while(OV2640_PCLK==1);
			  
			if(pix!=pixcnt)
			
				pixcnt=pix;
				linepix=0;
				linecnt++;
			
			
			for(i=0;i<128;i++)
			for(j=0;j<128;j++)
			
						LCD_DrawPoint_Color(j,i,(u16)(ov2640_framebuf[2*128*i+1+2*j]<<8|ov2640_framebuf[2*128*i+2*j]));		
			
			
	

#endif


 int main(void)
 

	delay_init();	    	 //延时函数初始化	  
	uart1_init(115200); 
	LCD_Init();
	KEY_Init();
	while(OV2640_Init())			//初始化OV2640
	
		printf("Camera ERROR\\n");
		delay_ms(200);
	


	ov2640_speed_ctrl();
#ifdef RGB_TEST
	rgb565_test();
#else
	jpeg_test();
#endif



SCCB.c


#include "sys.h"
#include "SCCB.h"
#include "delay.h"
 
//初始化SCCB接口
//CHECK OK
void SCCB_Init(void)
						
 	GPIO_InitTypeDef  GPIO_InitStructure;
	
 	RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOC, ENABLE);	 //使能PC
	
	GPIO_InitStructure.GPIO_Pin = GPIO_Pin_0;				 	 	// 端口配置
 	GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IPU; 		 	//输入
 	GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
 	GPIO_Init(GPIOC, &GPIO_InitStructure);
 	GPIO_SetBits(GPIOC,GPIO_Pin_0);						 					// 输出高

	GPIO_InitStructure.GPIO_Pin = GPIO_Pin_1;				 			// 端口配置
 	GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP; 		  //输输出
 	GPIO_Init(GPIOC, &GPIO_InitStructure);
 	GPIO_SetBits(GPIOC,GPIO_Pin_1);						 						// 输出高
	
	SCCB_SDA_OUT();	   
			 

//SCCB起始信号
//当时钟为高的时候,数据线的高到低,为SCCB起始信号
//在激活状态下,SDA和SCL均为低电平
void SCCB_Start(void)

    SCCB_SDA=1;     //数据线高电平	   
    SCCB_SCL=1;	    //在时钟线高的时候数据线由高至低
    delay_us(50);  
    SCCB_SDA=0;
    delay_us(50);	 
    SCCB_SCL=0;	    //数据线恢复低电平,单操作函数必要	  


//SCCB停止信号
//当时钟为高的时候,数据线的低到高,为SCCB停止信号
//空闲状况下,SDA,SCL均为高电平
void SCCB_Stop(void)

    SCCB_SDA=0;
    delay_us(50);	 
    SCCB_SCL=1;	
    delay_us(50); 
    SCCB_SDA=1;	
    delay_us(50);
  
//产生NA信号
void SCCB_No_Ack(void)

	delay_us(50);
	SCCB_SDA=1;	
	SCCB_SCL=1;	
	delay_us(50);
	SCCB_SCL=0;	
	delay_us(50);
	SCCB_SDA=0;	
	delay_us(50);

//SCCB,写入一个字节
//返回值:0,成功;1,失败. 
u8 SCCB_WR_Byte(u8 dat)

	u8 j,res;	 
	for(j=0;j<8;j++) //循环8次发送数据
	
		if(dat&0x80)SCCB_SDA=1;	
		else SCCB_SDA=0;
		dat<<=1;
		delay_us(50);
		SCCB_SCL=1;	
		delay_us(50);
		SCCB_SCL=0;		   
				 
	SCCB_SDA_IN();		//设置SDA为输入 
	delay_us(50);
	SCCB_SCL=1;			//接收第九位,以判断是否发送成功
	delay_us(50);
	if(SCCB_READ_SDA)res=1;  //SDA=1发送失败,返回1
	else res=0;         //SDA=0发送成功,返回0
	SCCB_SCL=0;		 
	SCCB_SDA_OUT();		//设置SDA为输出    
	return res;  
	 
//SCCB 读取一个字节
//在SCL的上升沿,数据锁存
//返回值:读到的数据
u8 SCCB_RD_Byte(void)

	u8 temp=0,j;    
	SCCB_SDA_IN();		//设置SDA为输入  
	for(j=8;j>0;j--) 	//循环8次接收数据
			     	  
		delay_us(50);
		SCCB_SCL=1;
		temp=temp<<1;
		if(SCCB_READ_SDA)temp++;   
		delay_us(50);
		SCCB_SCL=0;
		
	SCCB_SDA_OUT();		//设置SDA为输出    
	return temp;
 							    
//写寄存器
//返回值:0,成功;1,失败.
u8 SCCB_WR_Reg(u8 reg,u8 data)

	u8 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;
		  					    
//读寄存器
//返回值:读到的寄存器值
u8 SCCB_RD_Reg(u8 reg)

	u8 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;






Camera.c



#include "sys.h"
#include "Camera.h"
#include "Camera_Config.h" 
#include "delay.h"
#include "usart.h"			 
#include "sccb.h"	
//OV2640速度控制
//根据LCD分辨率的不同,设置不同的参数
void ov2640_speed_ctrl(void)

	u8 clkdiv,pclkdiv;			//时钟分频系数和PCLK分频系数
	
	clkdiv=15;
	pclkdiv=4;
	SCCB_WR_Reg(0XFF,0X00);		
	SCCB_WR_Reg(0XD3,pclkdiv);	//设置PCLK分频
	SCCB_WR_Reg(0XFF,0X01);
	SCCB_WR_Reg(0X11,clkdiv);	  //设置CLK分频	

void JTAG_Set(u8 mode)

	u32 temp;
	temp=mode;
	temp<<=25;
	RCC->APB2ENR|=1<<0;     //开启辅助时钟	   
	AFIO->MAPR&=0XF8FFFFFF; //清除MAPR的[26:24]
	AFIO->MAPR|=temp;       //设置jtag模式
 
#define SWD_ENABLE         0X01
//初始化OV2640 
//配置完以后,默认输出是1600*1200尺寸的图片!! 
//返回值:0,成功
//    其他,错误代码
u8 OV2640_Init(void)
 
	u16 i=0;
	u16 reg;
	GPIO_InitTypeDef  GPIO_InitStructure;
	//设置IO 
	RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA|RCC_APB2Periph_GPIOB|RCC_APB2Periph_GPIOC|RCC_APB2Periph_AFIO, ENABLE);	 //使能相关端口时钟
 
	//PCLK
	GPIO_InitStructure.GPIO_Pin  = GPIO_Pin_10; 	//PC15 输入 上拉
	GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IPU;
	GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
 	GPIO_Init(GPIOB, &GPIO_InitStructure);
	GPIO_SetBits(GPIOB,GPIO_Pin_10);
	
	//OV2640_PWDN
	GPIO_InitStructure.GPIO_Pin  = GPIO_Pin_3; 	//PC3 输出
	GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP;
 	GPIO_Init(GPIOC, &GPIO_InitStructure);
	GPIO_SetBits(GPIOC,GPIO_Pin_3);
	
	//DATA
	GPIO_InitStructure.GPIO_Pin  = 0xff; 				//PA0~7 输入 上拉
	GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IPU;
 	GPIO_Init(GPIOA, &GPIO_InitStructure);
	 
	//OV2640_VSYNC
	GPIO_InitStructure.GPIO_Pin  = GPIO_Pin_13;  
	GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IPU;//PC13输入
 	GPIO_Init(GPIOC, &GPIO_InitStructure);
	GPIO_SetBits(GPIOC,GPIO_Pin_13);
	
	//OV2640_RST
	GPIO_InitStructure.GPIO_Pin  = GPIO_Pin_2; //PC2输出 
	GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP;
 	GPIO_Init(GPIOC, &GPIO_InitStructure);
	GPIO_SetBits(GPIOC,GPIO_Pin_2);
	
  //OV2640_HREF
	GPIO_InitStructure.GPIO_Pin  = GPIO_Pin_11; //PB1输入 
	GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IPU;
	GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
 	GPIO_Init(GPIOB, &GPIO_InitStructure);
	GPIO_SetBits以上是关于普通低速单片机驱动OV7670等摄像头为啥要用FIFO的主要内容,如果未能解决你的问题,请参考以下文章

VerilogFPGA驱动Ov7670/Ov7725搭建视频通路(RGB565灰度图)

VerilogFPGA驱动Ov7670/Ov7725搭建视频通路(RGB565灰度图)

STM32H750获取OV7670摄像头图像及上位机解码(一维码&二维码)

OV7670基于FPGA的OV7670摄像头介绍和使用

单片机开发OV2640在没有DCMI接口的情况下的STM32驱动

单片机开发OV2640在没有DCMI接口的情况下的STM32驱动