问小白 wenxiaobai
资讯
历史
科技
环境与自然
成长
游戏
财经
文学与艺术
美食
健康
家居
文化
情感
汽车
三农
军事
旅行
运动
教育
生活
星座命理

ESP32驱动OV7725摄像头 | Arduino IDE

创作时间:
作者:
@小白创作中心

ESP32驱动OV7725摄像头 | Arduino IDE

引用
CSDN
1.
https://blog.csdn.net/fucingman/article/details/138249131

本文介绍如何使用微雪的ESP32-S3-Pico开发板和摄像头模块OV7725,在Arduino的IDE上编写驱动程序,采用上位机对图像进行显示。摄像头模块带FIFO,型号AL422B,模块如图所示1。其中WEN引脚不是FIFO的写使能WE,而是用于与摄像头工作时产生的HREF信号进行与非,与非后的结果作为FIFO的WE。FIFO模块的RE在摄像头电路中被接地,因此恒处于低电平。

1 前言

使用微雪的ESP32-S3-Pico开发板和摄像头模块OV7725,在Arduino的IDE上编写驱动程序,采用上位机对图像进行显示。
摄像头模块带FIFO,型号AL422B,模块如图所示1。其中WEN引脚不是FIFO的写使能WE,而是用于与摄像头工作时产生的HREF信号进行与非,与非后的结果作为FIFO的WE。FIFO模块的RE在摄像头电路中被接地,因此恒处于低电平。
开发板链接如下:https://www.waveshare.net/wiki/ESP32-S3-Pico。
上位机软件如图2,可以将RGB565格式的数据转化为图像,但是要在每一帧数据前后加帧头(0x01 0xfe)和帧尾(0xfe 0x01)。

2 接线

程序采用了Wire库,开发板的Pin9和Pin10作为SCCB(或IIC)通信引脚,分别与摄像头的SDA、SCL相连。其余引脚连接方式见宏定义。

3 程序代码

3.1 宏定义

摄像头的地址为0X42,但是wire库向从机写入数据时,会先发送一个7位的从机地址,然后发送一个写入符0。因此此处的摄像头地址定义为0X21(0X42>>1)。

#define D0 8
#define D1 14
#define D2 7
#define D3 15
#define D4 6
#define D5 16
#define D6 5
#define D7 17
#define FIFO_RCLK 4
#define FIFO_WEN 2
#define FIFO_WRST 11
#define FIFO_RRST 12
#define FIFO_OE 13
#define VSYNC 40
#define OV7725_ADDRESS 0x21

3.2 引脚初始化

void IO_Init()//OV7725引脚初始化
{
  pinMode(D0,INPUT);
  pinMode(D1,INPUT);
  pinMode(D2,INPUT);
  pinMode(D3,INPUT);
  pinMode(D4,INPUT);
  pinMode(D5,INPUT);
  pinMode(D6,INPUT);
  pinMode(D7,INPUT);
  pinMode(FIFO_RCLK,OUTPUT);
  pinMode(FIFO_WEN,OUTPUT);
  pinMode(FIFO_WRST,OUTPUT);
  pinMode(FIFO_RRST,OUTPUT);
  pinMode(FIFO_OE,OUTPUT);
  pinMode(VSYNC,INPUT_PULLUP);
  digitalWrite(FIFO_RCLK,LOW);
  digitalWrite(FIFO_WEN,LOW);
  digitalWrite(FIFO_WRST,HIGH);
  digitalWrite(FIFO_RRST,HIGH);
  digitalWrite(FIFO_OE,HIGH);
}

3.3 寄存器读写函数

//根据函数介绍,Wire.endTransmission()返回0时表示从机应答成功。
unsigned char WriteReg(unsigned char regID, unsigned char regDat)
{
  unsigned char flag=5;
  Wire.beginTransmission(OV7725_ADDRESS);
  Wire.write(regID);
  Wire.write(regDat);
  flag=Wire.endTransmission();
  delayMicroseconds(500);
  return flag;
}   
unsigned char ReadReg(unsigned char regID)
{
  unsigned char regDat=0;
  int cnt=0;
  Wire.beginTransmission(OV7725_ADDRESS);
  Wire.write(regID);
  Wire.endTransmission();
  Wire.requestFrom(OV7725_ADDRESS, 1);
  while(Wire.available())
  {
    cnt++;
    if(cnt>10000)
    {
      break;
    }
    regDat=Wire.read();
  }
  return regDat;
}
//仅对摄像头几个关键的寄存器进行修改,其他寄存器采用手册上的默认值。
//寄存器0x12可以设置图像输出格式。这里设置为RGB565,分辨率QVGA(320x240)
//寄存器0x0c最低位设为1时可以控制输出测试彩条,用来检查摄像头是否能正常功能工作。
void CameraReg_Set(void) //camera寄存器设置
{ 
  WriteReg(0x12,0x46);
  WriteReg(0x17,0x3f);
  WriteReg(0x18,0x50);
  WriteReg(0x19,0x03);
  WriteReg(0x1a,0x78);
  WriteReg(0x29,0x50);
  WriteReg(0x2a,0x00);
  WriteReg(0x2b,0x00);
  WriteReg(0x2c,0x78);
  WriteReg(0x32,0x00);
  WriteReg(0x0c,0x00);
}
unsigned char CameraReg_Init(void)//camera寄存器初始化
{
  unsigned char RegInit_Flag=1;  
  RegInit_Flag=WriteReg(0x12,0x80);//IIC通信check
  if(RegInit_Flag)
  {
    return 0;
  }
  CameraReg_Set();
  return 1; 
} 

3.4 FIFO操作函数

FIFO的操作时序可在技术手册中查到。

void FifoWrite_Reset()//fifo写复位
{
  digitalWrite(FIFO_WRST,LOW);
  digitalWrite(FIFO_WRST,HIGH);
}
void FifoRead_Reset()//fifo读复位
{
  FifoRead_CLK();
  FifoRead_CLK();
  digitalWrite(FIFO_RRST,LOW);
  FifoRead_CLK();
  FifoRead_CLK();
  digitalWrite(FIFO_RRST,HIGH);
}
void FifoRead_Enable()//fifo读使能
{
  digitalWrite(FIFO_OE,LOW);
}
void FifoRead_Disable()//fifo读禁止使能
{
  digitalWrite(FIFO_OE,HIGH);
}
void FifoWrite_Enable()//fifo写使能
{
  digitalWrite(FIFO_WEN,HIGH);
}
void FifoWrite_Disable()//fifo写禁止使能
{
  digitalWrite(FIFO_WEN,LOW);
}
void FifoRead_CLK()//fifo一个RCLK时钟
{
  digitalWrite(FIFO_RCLK, HIGH);
  digitalWrite(FIFO_RCLK, LOW);
}
unsigned char Fifo_Output(void)//fifo输出数据
  {
    unsigned char tmp=0;
    unsigned char DATA7=0;
    unsigned char DATA6=0;
    unsigned char DATA5=0;
    unsigned char DATA4=0;
    unsigned char DATA3=0;
    unsigned char DATA2=0;
    unsigned char DATA1=0;
    unsigned char DATA0=0;
    DATA7=(unsigned char)digitalRead(D7);
    DATA6=(unsigned char)digitalRead(D6);
    DATA5=(unsigned char)digitalRead(D5);
    DATA4=(unsigned char)digitalRead(D4);
    DATA3=(unsigned char)digitalRead(D3);
    DATA2=(unsigned char)digitalRead(D2);
    DATA1=(unsigned char)digitalRead(D1);
    DATA0=(unsigned char)digitalRead(D0);
    tmp=(DATA7 << 7) | (DATA6 << 6) | (DATA5 << 5) | (DATA4 << 4)\
        |(DATA3 << 3) | (DATA2 << 2) | (DATA1 << 1) | (DATA0 << 0);
    return tmp;
  }

3.5 中断函数

利用VSYNC的下降沿,作为采集一帧图像时的中断条件。由于前几帧图像会有些暗,因此从第10帧开始采集。

当第10帧图像的VSYNC下降沿来临时,禁止FIFO的读使能,然后将写指针复位,最后开启写使能(此时只是拉高WEN,并不是真正的写使能)。

当随着PCLK有图像数据输出后,HREF处于高电平。此时HREF和WEN与非后,WE低电平有效,FIFO才真正可以写入。每个PCLK周期内会往FIFO写入一个字节的数据,两个字节构成一个像素。

当第11帧图像的VSYNC下降沿来临时,FIFO中已经存储了一帧图像。此时关闭中断,同时禁止FIFO的写使能(即拉低WEN,WE将一直处于高电平),不再往FIFO里写入数据。然后读指针复位,随即开启读使能,就可以将FIFO中存储的一帧图像依次读出。

void IRQ_Enable(void)
{
  attachInterrupt(digitalPinToInterrupt(VSYNC),IRQ_Handler, FALLING);
}
void IRQ_Disable(void)
{
  detachInterrupt(digitalPinToInterrupt(VSYNC));
}
void IRQ_Handler(void)//中断函数
{ 
  Vsync_Flag+=1;  
  if(Vsync_Flag==10)//前几帧会有些暗,因此舍弃
  {
    FifoRead_Disable();
    FifoWrite_Reset();
    FifoWrite_Enable(); 
  }
  if(Vsync_Flag==11)
  {
    IRQ_Disable();
    FifoWrite_Disable();
    FifoRead_Reset();
    FifoRead_Enable();
  }   
}

3.6 摄像头和串口初始化

void Camera_Init()//camera初始化
{
  IO_Init();
  while(!CameraReg_Init());
  delayMicroseconds(100);
}
void Serial_Init()//串口初始化
 {
  Serial.begin(115200);
  Serial1.begin(256000,SERIAL_8N1,36,37);
}

3.7 主函数

首先进行初始化,然后开启中断。

当第11帧图像的VSYNC下降沿来临后,在中断函数里进行相关复位和使能,此时就可以在主函数读取图像数据。

每读取两个字节,就将数据发送给上位机。当320x240x2个字节以及帧头帧尾全部发送完成后,关闭读使能。

开启中断,并修改Vsync_Flag,使得代码能够连续采集图像。

void setup() 
{
  Serial_Init();
  Wire.begin(9,10,400000);
  Camera_Init();
  IRQ_Enable();
}
unsigned char t1,t2;
int Vsync_Flag=0;
void loop() 
{
  if(Vsync_Flag==11)
  {
    Serial1.write(0x01);
    Serial1.write(0xfe);
    for(int i=0;i<240;i++)
     {  
      for(int j=0;j<320;j++)
        { 
          FifoRead_CLK();
          t1=Fifo_Output();
          FifoRead_CLK();
          t2=Fifo_Output();
          Serial1.write(t1);
          Serial1.write(t2);
        }
     }
    FifoRead_Disable();
    Serial1.write(0xfe);
    Serial1.write(0x01);
    Vsync_Flag=9;
    IRQ_Enable();
  }
}

3.8

一些函数在.h文件中的声明

//【串口】
extern void Serial_Init();
//Camera
extern void IO_Init(void);
extern unsigned char WriteReg(unsigned char regID, unsigned char regDat);
extern unsigned char ReadReg(unsigned char regID);
extern void Set_Reg(void);
extern unsigned char CameraReg_Init(void);
extern void Camera_Init(void);
extern unsigned char Fifo_Output(void);
extern void FifoRead_Reset(void);
extern void FifoRead_Enable(void);
extern void FifoRead_Disable(void);
extern void FifoRead_CLK(void);
extern void FifoWrite_Reset(void);
extern void FifoWrite_Enable(void);
extern void FifoWrite_Disable(void);
extern void IRQ_Handler(void);
extern void IRQ_Enable(void);
extern void IRQ_Disable(void);
extern int Vsync_Flag;

4 运行效果

由于一帧图像(RGB565,QVGA)加帧头帧尾共153604个字节,串口向上位机每秒发送的字节远小于这个数量,因此大概需要5~6秒才能刷新一幅图像。

运行效果如下,通过摄像头的镜头可以改变焦距:

运行效果

本文原文来自CSDN,原始发布于2024年4月27日。

© 2023 北京元石科技有限公司 ◎ 京公网安备 11010802042949号