ESP32驱动OV7725摄像头 | Arduino IDE
ESP32驱动OV7725摄像头 | Arduino IDE
本文介绍如何使用微雪的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日。