普通低速单片机驱动OV7670等摄像头为啥要用FIFO
Posted
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了普通低速单片机驱动OV7670等摄像头为啥要用FIFO相关的知识,希望对你有一定的参考价值。
首先你要知道,fifo是缓冲区,缓冲区顾名思义就是速度不匹配,要等一帧数据转换完后采集 参考技术A 这样读取信号快 参考技术B 速度不匹配单片机开发OV2640在没有DCMI接口的情况下的STM32驱动
文章目录
(一)背景介绍
在之前刚学STM32的时候完成了一个ov7670的驱动
ov7670驱动
已经快要两年过去了,最近抽了一点时间又将之前搞得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 | 随便 |
(三)软件实现
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摄像头图像及上位机解码(一维码&二维码)