ATMEGA328 + OV7670 FIFO module (software)


As I do not have an Arduino board I am not able to put together a code running on Arduino, however, below code should run either using AVR Studio or modified to be used as an Arduino library. The code should be tailored for the schematic published previously.

For performance purposes the code should only send the Y (luminance) information, reducing the size of the data sent to half.

First, the definitions and pin settings:

#define LINEWIDTH    320
#define FRAMEHEIGHT  240

#define FIFO_DDR     DDRD
#define FIFO_PORT    PORTD
#define FIFO_PIN     PIND

#define FIFO_WEN     3
#define FIFO_RCLK    4
#define FIFO_RRST    5

/* FIFO control */
#define WEN_OUT      {FIFO_DDR|=(1<<FIFO_WEN);}
#define RCLK_OUT     {FIFO_DDR|=(1<<FIFO_RCLK);}
#define RRST_OUT     {FIFO_DDR|=(1<<FIFO_RRST);}

#define WEN_H        {FIFO_PORT|=(1<<FIFO_WEN);}
#define WEN_L        {FIFO_PORT&=~(1<<FIFO_WEN);}

#define RCLK_H       {FIFO_PORT|=(1<<FIFO_RCLK);}
#define RCLK_L       {FIFO_PORT&=~(1<<FIFO_RCLK);}

#define RRST_H       {FIFO_PORT|=(1<<FIFO_RRST);}
#define RRST_L       {FIFO_PORT&=~(1<<FIFO_RRST);}

/* Interrupt control */
#define CTRL_DDR     DDRD
#define CTRL_PORT    PORTD
#define CTRL_PIN     PIND

#define CTRL_VSYNC   2

#define VSYNC_IN     {CTRL_DDR&=~(1<<CTRL_VSYNC);}
#define VSYNC_UP     {CTRL_PORT|=(1<<CTRL_VSYNC);}

#define VSYNC_EXTINT_FALL 
        {MCUCR = (1 << ISC01);GICR |= (1 << INT0);}
#define VSYNC_EXTINT_RAIS 
        {MCUCR = ((1 << ISC01) | (1 << ISC00));GICR |= (1 << INT0);}

#define RDATA        (PINB | (PIND & 0xC0))
#define DATA_IN 
        { DDRD &= ~(1<<5); DDRD &= ~(1<<6); DDRB = 0xC0; }


/* Init ports/state */
#define INIT_PORTS 
        {WEN_OUT; RRST_OUT; RCLK_OUT; DATA_IN;MODE_OUT;TRACK_OUT;VSYNC_IN;}
#define INIT_STATE 
        {WEN_L; RRST_H; RCLK_H;MODE_L;TRACK_L;VSYNC_UP;}

Then the main code:

typedef uint8_t Line[LINEWIDTH];
unsigned int machineState = 0;

Line curLine;

ISR (INT0_vect){
  // turn off interrupts
  cli();

  if (machineState == 0){
    // start capturing frame
    WEN_H;
    machineState++;
  } else {
    // stop capturing frame
    WEN_L;
    machineState++;
  }
  // turn on interrupts
  sei();
}

int main( void ) {
  unsigned int i = 0 ;
  unsigned int j = 0 ;
  unsigned int k = 0 ;

  uint8_t pixel = 0;

  // initialise UART
  sinit();

  // initialise ports
  INIT_PORTS;
  INIT_STATE;

  OV7670_init();

  // enable external interrupt for VSYNC
  VSYNC_EXTINT_FALL;
  sei();	

  // main loop
  while(1){
    // turn ON LED
    MODE_H;
    if(machineState == 1){
    cli();

      while (savailable()){
        if (sgetc() == 'N'){
          delay_ms(1);
          // SOF - start of frame
          sputc(0x4E);
          sputc(0x00);

          // reset FIFO sequence
          RRST_L;
          RCLK_H;
          RCLK_L;
          RCLK_H;
          RRST_H;
          RCLK_L;
          RCLK_H;
          RCLK_L;

          // capture WIDTH x HEIGHT x 2 pixels
          i=0;
          while (i++ < FRAMEHEIGHT){
            j=0;
            while (j++ < LINEWIDTH){
              // capture first byte of the pixel
              RCLK_H;
              pixel = RDATA;
              RCLK_L;

              // capture second byte of the pixel
              RCLK_H;
              //pixel = RDATA;
              RCLK_L;

              curLine[j] = pixel;
            }

            // stream entire line over UART
            k = 0;
            while (k++ < LINEWIDTH){
              sputc(curLine[k]);
            }
          }
          // EOF - end of frame
          sputc(0x00);
          sputc(0xFF);
        }
      }
      sei();
      machineState = 0;
    }
    // turn OFF LED
    MODE_L;
  }
}

The OV7670 registries details:

struct OV7670regVals{
  unsigned char       reg_num;
  unsigned char       value;
};

static struct OV7670regVals ov7670_setup[] = {
{REG_COM7, COM7_RESET},
{REG_TSLB, TSLB_YLAST},
{REG_COM15, COM15_R00FF},
{REG_COM7, COM7_FMT_QVGA | COM7_RGB},
{REG_HREF, 0x80},
{REG_RGB444, R444_ENABLE},
{REG_COM9, 0x38},
{REG_HSTART, 0x16}, 
{REG_HSTOP, 0x04},
{REG_VSTART, 0x02}, 
{REG_VSTOP, 0x7b},
{REG_VREF, 0x06},

{REG_COM3, 0x0c},
{REG_COM10, COM10_VS_NEG},
{REG_COM14, 0x00},

{REG_SCALING_XSC, 0x00},  
{REG_SCALING_YSC, 0x01},
{REG_SCALING_DCWCTR, 0x11},  
{REG_SCALING_PCLK_DIV, 0x09},
{REG_SCALING_PCLK_DELAY, 0x02},
{REG_CLKRC, 0x01},
{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},

{REG_COM8, COM8_FASTAEC | COM8_AECSTEP | COM8_BFILT },
{REG_AECH, 0x00}, 
{REG_COM4, 0x40},
{REG_BD50MAX, 0x05}, 
{REG_BD60MAX, 0x07},
{REG_AEW, 0x95}, 
{REG_AEB, 0x33},
{REG_VPT, 0xe3}, 
{REG_HAECC1, 0x78},
{REG_HAECC2, 0x68}, 
{0xa1, 0x03},
{REG_HAECC3, 0xd8}, 
{REG_HAECC4, 0xd8},
{REG_HAECC5, 0xf0}, 
{REG_HAECC6, 0x90},
{REG_HAECC7, 0x94},
{REG_COM8, COM8_FASTAEC|COM8_AECSTEP|COM8_BFILT|COM8_AGC|COM8_AEC },

{REG_COM5, 0x61},
{REG_COM6, 0x4b},
{0x16, 0x02},

{REG_MVFP, 0x01},    //MVFP_MIRROR | MVFP_FLIP
{0xc9, 0x60 },       // color saturation limits
{REG_COM16, COM16_AWBGAIN},
{REG_COM13, COM13_GAMMA | COM13_UVSAT},
{REG_COM11, COM11_NIGHT | COM11_EXP | COM11_HZAUTO},
{REG_REG75, 0x0F},
{REG_REG76, 0x0F},

{0x21, 0x02},         
{0x22, 0x91},
{0x29, 0x07},         
{0x33, 0x0b},
{0x35, 0x0b},         
{0x37, 0x1d},
{0x38, 0x71},         
{0x39, 0x2a},

{REG_COM12, 0x78},
{0x4d, 0x40},         
{0x4e, 0x20},

{REG_GFIX, 0x5d},
{0x6b, 0x40},
{0x74, 0x19},
{0x8d, 0x4f},
{0x8e, 0x00},
{0x8f, 0x00},
{0x90, 0x00},
{0x91, 0x00},
{0x92, 0x00},
{0x96, 0x00},
{0x9a, 0x80},
{0xb0, 0x84},
{0xb1, 0x0c},
{0xb2, 0x0e},
{0xb3, 0x82},
{0xb8, 0x0a},

{0x43, 0x0a},
{0x44, 0xf0},
{0x45, 0x34},
{0x46, 0x58},
{0x47, 0x28},
{0x48, 0x3a},
{0x59, 0x88},
{0x5a, 0x88},
{0x5b, 0x44},
{0x5c, 0x67},
{0x5d, 0x49},
{0x5e, 0x0e},
{0x6c, 0x0a},
{0x6d, 0x55},
{0x6e, 0x11},
{0x6f, 0x9f},
{0x6a, 0x40},
{REG_BLUE, 0x40 },
{REG_RED, 0x60},

{0x64, 0x04},
{0x65, 0x20},
{0x66, 0x05},
{0x94, 0x04},
{0x95, 0x08},
{0x6c, 0x0a},
{0x6d, 0x55},
{0x4f, 0x80},
{0x50, 0x80},
{0x51, 0x00},
{0x52, 0x22},
{0x53, 0x5e},
{0x54, 0x80},
{0x6e, 0x11},
{0x6f, 0x9f},
{0x55, 0x00},
{0x56, 0x40},
{0x57, 0x80},
{0xff, 0xff},
};

Update Feb 2015: Schematic for this code is available here.

Advertisements

18 thoughts on “ATMEGA328 + OV7670 FIFO module (software)

  1. Hi There,

    I was wondering if you can help me setup the ov7670 (FIFO) with either the STM32F4 or Arduino uno? I’m trying to take images with ov7670 (FIFO) and send them through bluetooth to a mobile device.

    your advice and assistance is greatly appreciated

    • Hi Nawaf, have you tried looking at the this post and the one before? Pretty much everything is there. The only thing missing is the SCCB part which is I2C and the OV7670 registry settings to configure the camera. Several people posted their success stories together with their configurations.

  2. Hi and thanks to share your experience with ov7670,

    I want to reuse your code but i can’t find where MODE_OUT and TRACK_OUT are defined , can you help me ?

    Thanks

  3. Thanks for sharing. this is old now but can be of help for some aspects, but saying this is for Arduino is misleading. the code was not originally written for an Atmega328. the 328 does not even have a GICR – General Interrupt Control Register.

    • Hi Aj, thank you for your interest. I did not say is Arduino code anywhere in this post. This code, as explained in the post, will only work on AVR Studio. However, one can use the code to make an Arduino library.
      Btw, this code was running on Atmega168 and Atmega328 just fine.

  4. Hello,

    I tried to use this code in Atmel Studio and ATmega328p does not have a GICR register. For example, in this line:
    #define VSYNC_EXTINT_FALL {MCUCR = (1 << ISC01); GICR |= (1 << INT0);}
    i have one error: 'GICR' undeclared. Also, where is located the function OV7670_init(); and where I can find the entire code? Thank you very much!

    • Hi Daniel, the GICR registry is for Atmega8/16. For Atmega328p you will have to use EIMSK instead. You should take a look to the Atmega328p datasheet.

      The code should change from:

      MCUCR |= (1 << ISC00); // set INT0 to trigger on ANY logic change
      GICR |= (1 << INT0); // Turns on INT0

      To something like this:

      EICRA |= (1 << ISC00); // set INT0 to trigger on ANY logic change
      EIMSK |= (1 << INT0); // Turns on INT0

      • Ok, thank you very much! The code posted in this page is complete? I can’t locate the OV7670_init() function, for example. There is a header file or something like that?

  5. hi for arduino guys do u have code for it?
    do people change this code for arduino uno? its better to save it on sd card…
    tnx so much

  6. I want to sream video with only the ATMEGA328p, is there a possibility?
    Also, can i take picture and send them wireless through a zigbee?

    • Hi Chefo, what do you mean by “with only”? Without FIFO or with FIFO? What is the resolution of the frame? What is the frame rate? You know that ATMEGA328p only have 2 Kbytes or RAM, right? You can only hold one frame of 40 x 50 pixels maybe, even then have to be 8 bit / pixel. Will that work for you?

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s