Code:
#include <Arduino.h>
void cameraBegin();
bool cameraRead();
extern uint16_t cameraImage[160 * 120]; // QQVGA image buffer in RGB565 format
static int cameraWidth() { return 160; }
static int cameraHeight() { return 120; }
// Note: it is important for the pins to be #defined here to allow the fast reads to work properly as the need to know the pins at compile time
#define PinCamVsync 2 // EMC_04
#define PinCamHref 3 // EMC_05
#define PinCamPpclk 4 // EMC_06
#define PinCamXclk 5 // EMC_08
#define PinCamD0 14 // AD_B1_02
#define PinCamD1 15 // AD_B1_03
#define PinCamD3 16 // AD_B1_07
#define PinCamD2 17 // AD_B1_06
#define PinCamD6 20 // AD_B1_10
#define PinCamD7 21 // AD_B1_11
#define PinCamD4 22 // AD_B1_08
#define PinCamD5 23 // AD_B1_09
#include "Camera.h"
#include <Wire.h>
static int const cameraAddress= 0x21; // i2c address of camera's sccb device
static struct { uint8_t reg, val; } const OV7670Registers[] = {
{0x12, 0x80}, // Reset to default values
{0x11, 0x80}, // Set some reserved bit to 1!
{0x3B, 0x0A}, // Banding filter value uses BD50ST 0x9D as value. some reserved stuff + exposure timing can be less than limit on strong light
{0x3A, 0x04}, // output sequense elesection. Doc too lmited to be sure
{0x3A, 0x04},
{0x12, 0x04}, // Output format: rgb
{0x8C, 0x00}, // Disable RGB444
{0x40, 0xD0}, // Set RGB565
{0x17, 0x16}, // Y window start msb (3-11) I think
{0x18, 0x04}, // Y window end msb (3-11)
{0x32, 0x24}, // Y window lsb end= 100b start= 100b
{0x19, 0x02}, // X window start msb (2-10) I think
{0x1A, 0x7A}, // X window end msb (2-10) I think
{0x03, 0x0A}, // X window lsb (10 and 10)
{0x15, 0x02}, // VSync negative
{0x0C, 0x04}, // DCW enable?
{0x3E, 0x1A}, // Divide by 4
{0x1E, 0x27}, // mirror image black sun enabled and more reserved!
{0x72, 0x22}, // Downsample by 4
{0x73, 0xF2}, // Divide by 4
{0x4F, 0x80}, // matrix coef
{0x50, 0x80},
{0x51, 0x00},
{0x52, 0x22},
{0x53, 0x5E},
{0x54, 0x80},
{0x56, 0x40}, // contracts
{0x58, 0x9E}, // matrix
{0x59, 0x88}, // AWB
{0x5A, 0x88},
{0x5B, 0x44},
{0x5C, 0x67},
{0x5D, 0x49},
{0x5E, 0x0E},
{0x69, 0x00}, // gain per channel
{0x6A, 0x40}, // more gain
{0x6B, 0x0A}, // pll reserved stuff!
{0x6C, 0x0A}, // AWB
{0x6D, 0x55},
{0x6E, 0x11},
{0x6F, 0x9F},
{0xB0, 0x84}, // reserved!
{0xFF, 0xFF} // End marker
};
// Read a single uint8_t from address and return it as a uint8_t
static uint8_t cameraReadRegister(uint8_t address)
{
Wire.beginTransmission(cameraAddress);
Wire.write(address);
Wire.endTransmission();
Wire.requestFrom(cameraAddress, 1);
uint8_t data = Wire.read();
Wire.endTransmission();
return data;
}
// Writes a single uint8_t (data) into address
static int cameraWriteRegister(uint8_t address, uint8_t data)
{
Wire.beginTransmission(cameraAddress);
Wire.write(address); Wire.write(data);
return Wire.endTransmission();
}
// Reset all OV7670 registers to their default values
static void cameraReset()
{
cameraWriteRegister(0x12, 0x80); delay(10);
cameraWriteRegister(0x12, 0); delay(10);
}
// Read and display all of the OV7670 registers
static void cameraReadI2C()
{
for (int i = 0; i <= 0xC9; i ++)
{
Serial.print("Register: "); Serial.print(i, HEX); Serial.print(" Value: "); uint8_t reg = cameraReadRegister(i); Serial.println(reg, HEX);
}
}
// Slow the frame rate enough for camera code to run with 8 MHz XCLK. Approximately 1.5 frames/second
static void cameraSlowFrameRate()
{
// CLKRC register: Prescaler divide by 31
uint8_t reg = cameraReadRegister(0x11); cameraWriteRegister(0x11, (reg & 0b1000000) | 0b00011110);
// DBLV register: PLL = 6
reg = cameraReadRegister(0x6B); cameraWriteRegister(0x6B, (reg & 0b00111111) | 0b10000000);
}
uint16_t cameraImage[160 * 120]; // QQVGA image buffer in RGB565 format
//#define dointerrupt
static void cameraSubRead();
static int camCount= 0;
static int camIrqCount= 0;
static void cameraCount() { camIrqCount++; }
void cameraBegin()
{
// Setup all GPIO pins as inputs
pinMode(PinCamVsync, INPUT); pinMode(PinCamHref, INPUT); pinMode(PinCamPpclk, INPUT);
pinMode(PinCamD0, INPUT); pinMode(PinCamD1, INPUT); pinMode(PinCamD2, INPUT); pinMode(PinCamD3, INPUT); pinMode(PinCamD4, INPUT); pinMode(PinCamD5, INPUT); pinMode(PinCamD6, INPUT); pinMode(PinCamD7, INPUT);
analogWriteFrequency(PinCamXclk, 8000000); analogWrite(PinCamXclk, 127); delay(100);
// Configure the camera for operation
Wire.begin();
Serial.println("init camera");
int i= 0; while (OV7670Registers[i].reg!=0xff) { cameraWriteRegister(OV7670Registers[i].reg, OV7670Registers[i].val), i++; }
cameraWriteRegister(0x3a, 0x14);cameraWriteRegister(0x67, 0xc80);cameraWriteRegister(0x68, 0x80); // B&W mode...
delay(100);
#ifdef dointerrupt
attachInterrupt(digitalPinToInterrupt(PinCamVsync), cameraSubRead, RISING);
#else
attachInterrupt(digitalPinToInterrupt(PinCamVsync), cameraCount, RISING);
#endif
}
// Read a uint8_t of the pixel data
static inline uint8_t cameraReadPixel()
{
uint8_t b = 0;
if (digitalReadFast(PinCamD7)) b |= 0x80;
if (digitalReadFast(PinCamD6)) b |= 0x40;
if (digitalReadFast(PinCamD5)) b |= 0x20;
if (digitalReadFast(PinCamD4)) b |= 0x10;
if (digitalReadFast(PinCamD3)) b |= 0x08;
if (digitalReadFast(PinCamD2)) b |= 0x04;
if (digitalReadFast(PinCamD1)) b |= 0x02;
if (digitalReadFast(PinCamD0)) b |= 0x01;
return b;
}
static bool cameraNewFrame= false;
static uint32_t readtime= 0, readtime1= 0;
static void cameraSubRead()
{
int i= 0;
#ifdef dointerrupt
camIrqCount++;
#endif
camCount++;
uint32_t t1= micros();
if (!digitalReadFast(PinCamVsync)) return;
for (int j=cameraHeight(); --j>=0;) //while (digitalReadFast(PinCamVsync))
{
while (digitalReadFast(PinCamVsync) && !digitalReadFast(PinCamHref)); // Wait for a line to start
if (!digitalReadFast(PinCamVsync)) break; // Line didn't start, but frame ended
for (int k=cameraWidth(); --k>=0;) //while (digitalReadFast(PinCamHref)) // Wait for a line to end
{
while (!digitalReadFast(PinCamPpclk)); // Wait for clock to go high
uint8_t hByte = cameraReadPixel();
// int r= hByte>>3*76;
while (digitalReadFast(PinCamPpclk)); // Wait for clock to go back low
while (!digitalReadFast(PinCamPpclk)); // Wait for clock to go high
uint8_t lByte = cameraReadPixel();
// 0.3R + 0.59G + 0.11B = 76R+151G+28B // msb is red
// r+= (lByte&0x1f)*28 + ((((hByte<<8)|lByte)>>5)&0x3f)*75;
// r>>= 8;
// cameraImage[i++]= (r<<11)+(r<<5)+(r);
cameraImage[i++] = (hByte << 8) | lByte;
while (digitalReadFast(PinCamPpclk)); // Wait for clock to go back low
}
}
cameraNewFrame= true;
readtime+= micros()-t1;
}
// Acquire and display RGB565 image from the camera
bool cameraRead()
{
#ifndef dointerrupt
uint32_t t1= millis();
while (digitalReadFast(PinCamVsync)); // Wait for the old frame to end
while (! digitalReadFast(PinCamVsync)); // Wait for a new frame to start
uint32_t t2= micros();
noInterrupts();
cameraSubRead();
interrupts();
readtime1+= micros()-t2;
uint32_t t3= millis();
Serial.print(readtime1/camCount); Serial.println(" micros / frame");
#else
uint32_t t3= millis();
Serial.print(readtime/camCount); Serial.println(" micros / frame");
#endif
//Serial.print(camIrqCount); Serial.println(" camera irq");
//Serial.print(camCount); Serial.println(" frame count");
//Serial.print(camCount/(float(t3)/1000)); Serial.println(" frame /s");
bool r= cameraNewFrame; cameraNewFrame= false; return r;
}