Camera Board

Fall 2023

You are not logged in.

Please Log In for full access to the web site.
Note that this link will take you to an external site (https://shimmer.mit.edu) to authenticate, and then you will be redirected back to this page.

This is the default code we put on the SAMD21 microcontroller to start-up and prep the camera. I'm sure there are better values and settings, but these get it going good enough for our purposes. Feel free to use

To program the board, you'll need a USB-C cable and the SAMD-21 Xiao board files installed in regular Arduino (the only aspect of Arduino that is really used is the general Arduino framework and the Wire library...these could be easily ported to vanilla C or whatever API you need to use).

/*
   Wire - I2C Scanner
   D1 = SCL
   D2 = SDA
*/

#include <Wire.h>

/*These are settings some of which have been found empirically and/or found
  from random internet sites. When you see that there's a "magic" number it
  isn't a magic number like in comp sci or something...it just means we have
  no idea why this register value seems to help since the data sheet doesn't
  give a ton of guidance.  I'm sure there's rational explanations for many of
  these numbers, but sometimes I've just got bills to pay and life to live
  and don't have time to figure out why. You know the deal.
*/

const byte ADDR = 0x21; //name of the camera on I2C

uint8_t settings[][2] = {
  {0x12, 0x80}, //reset
  {0x01, 0xFF}, //blue gain (default 80)
  {0x02, 0x40}, //reg gain (defaul 80)
  {0xFF, 0xF0}, //delay
  {0x12, 0x14}, // COM7,     set RGB color output (QVGA and test pattern 0x6...for RGB video 0x4)
  {0x11, 0x80}, // CLKRC     internal PLL matches input clock
  {0x0C, 0x00}, // COM3,     default settings
  {0x3E, 0x00}, // COM14,    no scaling, normal pclock
  {0x04, 0x00}, // COM1,     disable CCIR656
  {0x40, 0xd0}, //COM15,     RGB565, full output range
  {0x3a, 0x04}, //TSLB       set correct output data sequence (magic)
  {0x14, 0x18}, //COM9       MAX AGC value x4
  {0x4F, 0xB3}, //MTX1       all of these are magical matrix coefficients
  {0x50, 0xB3}, //MTX2
  {0x51, 0x00}, //MTX3
  {0x52, 0x3d}, //MTX4
  {0x53, 0xA7}, //MTX5
  {0x54, 0xE4}, //MTX6
  {0x58, 0x9E}, //MTXS
  {0x3D, 0xC0}, //COM13      sets gamma enable, does not preserve reserved bits, may be wrong?
  {0x17, 0x14}, //HSTART     start high 8 bits
  {0x18, 0x02}, //HSTOP      stop high 8 bits //these kill the odd colored line
  {0x32, 0x80}, //HREF       edge offset
  {0x19, 0x03}, //VSTART     start high 8 bits
  {0x1A, 0x7B}, //VSTOP      stop high 8 bits
  {0x03, 0x0A}, //VREF       vsync edge offset
  {0x0F, 0x41}, //COM6       reset timings
  {0x1E, 0x00}, //MVFP       disable mirror / flip //might have magic value of 03
  //{0x33, 0x0B}, //CHLF       //magic value from the internet
  {0x3C, 0x78}, //COM12      no HREF when VSYNC low
  {0x69, 0x00}, //GFIX       fix gain control
  {0x74, 0x00}, //REG74      Digital gain control
  {0xB0, 0x84}, //RSVD       magic value from the internet *required* for good color
  {0xB1, 0x0c}, //ABLC1
  {0xB2, 0x0e}, //RSVD       more magic internet values
  {0xB3, 0x80}, //THL_ST
  //begin mystery scaling numbers. Thanks, internet!
  {0x70, 0x3a},
  {0x71, 0x35},
  {0x72, 0x11},
  {0x73, 0xf0},
  {0xa2, 0x02},
  //gamma curve values
  {0x7a, 0x20},
  {0x7b, 0x10},
  {0x7c, 0x1e},
  {0x7d, 0x35},
  {0x7e, 0x5a},
  {0x7f, 0x69},
  {0x80, 0x76},
  {0x81, 0x80},
  {0x82, 0x88},
  {0x83, 0x8f},
  {0x84, 0x96},
  {0x85, 0xa3},
  {0x86, 0xaf},
  {0x87, 0xc4},
  {0x88, 0xd7},
  {0x89, 0xe8},
  //AGC and AEC
//  {0x13, 0xe0}, //COM8, disable AGC / AEC
//  {0x00, 0x00}, //set gain reg to 0 for AGC
//  {0x10, 0x00}, //set ARCJ reg to 0
//  {0x0d, 0x40}, //magic reserved bit for COM4
//  {0x14, 0x18}, //COM9, 4x gain + magic bit
//  {0xa5, 0x05}, // BD50MAX
//  {0xab, 0x07}, //DB60MAX
//  {0x24, 0x95}, //AGC upper limit
//  {0x25, 0x33}, //AGC lower limit
//  {0x26, 0xe3}, //AGC/AEC fast mode op region
//  {0x9f, 0x78}, //HAECC1
//  {0xa0, 0x68}, //HAECC2
//  {0xa1, 0x03}, //magic
//  {0xa6, 0xd8}, //HAECC3
//  {0xa7, 0xd8}, //HAECC4
//  {0xa8, 0xf0}, //HAECC5
//  {0xa9, 0x90}, //HAECC6
//  {0xaa, 0x94}, //HAECC7
//  {0x13, 0xe7}, //COM8, enable AGC //AEC (was 0xe5) (try this at 0xe7)

  {0x00, 0x00}, //set gain reg to 0 for AGC
  {0x01, 0x8F}, //blue gain (default 80)
  {0x02, 0x70}, //reg gain (default 80)
  {0x6a, 0x4F}, //green gain (default not sure!)
    {0x13, 0x00}, //disable all automatic features!! (including automatic white balance)

};


//uint8_t settings[][2] = {
//  {0x12, 0x80}, //reset
//  {0xFF, 0xF0}, //delay
//  {0x12, 0x14}, // COM7,     set RGB color output (QVGA and test pattern 0x6...for RGB video 0x4)
//  {0x11, 0x80}, // CLKRC     internal PLL matches input clock
//  {0x0C, 0x00}, // COM3,     default settings
//  {0x3E, 0x00}, // COM14,    no scaling, normal pclock
//  {0x04, 0x00}, // COM1,     disable CCIR656
//  {0x40, 0xd0}, //COM15,     RGB565, full output range
//  {0x3a, 0x04}, //TSLB       set correct output data sequence (magic)
//  {0x14, 0x18}, //COM9       MAX AGC value x4
//  {0x4F, 0xB3}, //MTX1       all of these are magical matrix coefficients
//  {0x50, 0xB3}, //MTX2
//  {0x51, 0x00}, //MTX3
//  {0x52, 0x3d}, //MTX4
//  {0x53, 0xA7}, //MTX5
//  {0x54, 0xE4}, //MTX6
//  {0x58, 0x9E}, //MTXS
//  {0x3D, 0xC0}, //COM13      sets gamma enable, does not preserve reserved bits, may be wrong?
//  {0x17, 0x14}, //HSTART     start high 8 bits
//  {0x18, 0x02}, //HSTOP      stop high 8 bits //these kill the odd colored line
//  {0x32, 0x80}, //HREF       edge offset
//  {0x19, 0x03}, //VSTART     start high 8 bits
//  {0x1A, 0x7B}, //VSTOP      stop high 8 bits
//  {0x03, 0x0A}, //VREF       vsync edge offset
//  {0x0F, 0x41}, //COM6       reset timings
//  {0x1E, 0x00}, //MVFP       disable mirror / flip //might have magic value of 03
//  {0x33, 0x0B}, //CHLF       //magic value from the internet
//  {0x3C, 0x78}, //COM12      no HREF when VSYNC low
//  {0x69, 0x00}, //GFIX       fix gain control
//  {0x74, 0x00}, //REG74      Digital gain control
//  {0xB0, 0x84}, //RSVD       magic value from the internet *required* for good color
//  {0xB1, 0x0c}, //ABLC1
//  {0xB2, 0x0e}, //RSVD       more magic internet values
//  {0xB3, 0x80}, //THL_ST
//  //begin mystery scaling numbers. Thanks, internet!
//  {0x70, 0x3a},
//  {0x71, 0x35},
//  {0x72, 0x11},
//  {0x73, 0xf0},
//  {0xa2, 0x02},
//  //gamma curve values
//  {0x7a, 0x20},
//  {0x7b, 0x10},
//  {0x7c, 0x1e},
//  {0x7d, 0x35},
//  {0x7e, 0x5a},
//  {0x7f, 0x69},
//  {0x80, 0x76},
//  {0x81, 0x80},
//  {0x82, 0x88},
//  {0x83, 0x8f},
//  {0x84, 0x96},
//  {0x85, 0xa3},
//  {0x86, 0xaf},
//  {0x87, 0xc4},
//  {0x88, 0xd7},
//  {0x89, 0xe8},
//  //WB Stuff (new stuff!!!!)
//  {0x00, 0x00}, //set gain reg to 0 for AGC
//  {0x01, 0x8F}, //blue gain (default 80)
//  {0x02, 0x8F}, //reg gain (default 80)
//  {0x6a, 0x4F}, //green gain (default not sure!)
//  {0x13, 0x00}, //disable all automatic features!! (including automatic white balance)
//};

uint8_t output_state;

void setup()
{
  pinMode(10, OUTPUT);
  pinMode(9, OUTPUT);
  digitalWrite(9, 1);
  digitalWrite(10, 0);
  pinMode(0, INPUT_PULLUP);
  pinMode(4, INPUT_PULLUP);
  output_state = 0;
  pinMode(LED_BUILTIN, OUTPUT);
  Wire.begin();
  Serial.begin(115200);
  Serial.println("Starting");
  //  digitalWrite(10,1);
  //  delay(1000);
  //  digitalWrite(10,0);
  //  delay(1000);
  //  digitalWrite(9,0);
  //  delay(1000);
  //  digitalWrite(9,1);
  //  delay(10000);
  digitalWrite(LED_BUILTIN, 1);
  while (!digitalRead(0)){
    Serial.println("waiting for clock"); //wait until locked clock
  }
  delay(1000);
  program();
  digitalWrite(LED_BUILTIN, 0);

}


void loop()
{
  Serial.println("working");
  delay(100);
  digitalWrite(LED_BUILTIN, 0);
  delay(100);
  digitalWrite(LED_BUILTIN, 1);
  if (digitalRead(0) == 0) {
    digitalWrite(LED_BUILTIN, 1);
    while (digitalRead(0) == 0); //wait
    delay(1000);
    program(); //reprogram!
    digitalWrite(LED_BUILTIN, 0);
  }

}

void program() {
  digitalWrite(10, 1);
  delay(100);
  digitalWrite(10, 0);
  delay(100);
  digitalWrite(9, 0);
  delay(100);
  digitalWrite(9, 1);
  delay(100);
  Wire.beginTransmission(ADDR);
  Wire.write(0x0A);
  Wire.requestFrom(ADDR, 2);
  byte LSB = Wire.read();
  byte MSB = Wire.read();
  uint16_t val = ((MSB << 8) | LSB);
  Wire.endTransmission();
  Serial.println(val);
  for (int i = 0; i < sizeof(settings) / 2; i++) {
    Wire.beginTransmission(ADDR);
    Wire.write(settings[i][0]);
    Wire.write(settings[i][1]);
    //    Wire.write(RegValues[i][1]);
    //    Wire.write(RegValues[i][2]);
    Wire.endTransmission();
  }
  Serial.println("OV7670 Setup Done");
}


void writeByte(uint8_t target_reg, uint8_t val) {
  Wire.beginTransmission(ADDR);
  Wire.write(target_reg);
  Wire.write(val);
  Wire.endTransmission();
}

void readBytes(uint8_t target_reg, uint8_t* val_out, uint8_t num_bytes) {
  Wire.beginTransmission(ADDR);
  Wire.write(target_reg);
  Wire.requestFrom(ADDR, num_bytes);
  uint8_t* ptr_to_out;
  ptr_to_out = val_out;
  for (int i = 0; i < num_bytes; i++) {
    *ptr_to_out = Wire.read();
    ptr_to_out++;
  }
}