Pages: [1]   Go Down
Author Topic: Arduino Mega and image acquisition - my experience  (Read 954 times)
0 Members and 1 Guest are viewing this topic.
0
Offline Offline
Newbie
*
Karma: 0
Posts: 31
Arduino rocks
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset

Dear all,
A while ago I was involved in some stuff that required to connect an Arduino  board with a Image sensor, the Omnivision C3088 camera module.

I have successfully done that and I'd like to report that the Arduino boardis capable of acquiring up to 5 frame per second (or maybe more ?) of B&W image where, because of the low available memory I had to resize.

I'd like to share with you the code for it and naturally offer my humble help on this topic.

Best regards,
Fabrizio

All souce code (plus a video and a java app to read images coming via the serial port) is available here:
http://topo.epfl.ch/research/indoor_nav/prj_rep/index.php?n=LookupPrj.Main

additionally I copy and paste here some of the C++ code

Code:
/*
image_read

this program works for the Arduino Mega board connected to the C3088 camera module.
the program pulls images from the CCD sensor using port Y(0-7) and send them to the serial port.
If you want to see the images piped via the serial port, you can use the simple java GUI included.
It was made using Experiments and compiled under Apple OS X.
The routine as it is can aquire approximately 3 Images per seconds. images are B&W and, in order to be piped to the serial port, are scaled to a resolution of 30 X 30. Higher frame rate is also possible especially if you dont use the serial port.

Program written by: Fabrizio Tappero, zonkyu[at]gmail.com
date: 10 July 2009
License: GNU license

NOTE: some of the I2C code comes from the web (see reference at the end of this file)

Arduino Mega and C3088 connections:

Arduino Mega Board:
pin 22 ->
pin 23 -> (C3088 pin31)
pin 24 ->
pin 25 ->
pin 26 ->
pin 27 ->
pin 28 ->
pin 29 ->
pin 30 ->
pin 31 ->
pin 32 -> (C3088 pin22)
pin 33 -> (C3088 pin21)
pin 34 -> (C3088 pin20)
pin 35 ->
pin 36 -> (C3088 pin18) PCLK
pin 37 ->
pin 38 -> (C3088 pin16) VSYNC
pin 39 -> (C3088 pin15)
pin 40 -> (C3088 pin14) HREF
pin 41 -> (C3088 pin13) SLC(I2C)
pin 42 ->
pin 43 -> (C3088 pin11) SDA(I2C)
pin 44 -> (C3088 pin10) RST
pin 45 -> (C3088) pin9 PWDN
pin 46 -> Y7 (C3088 pin8)
pin 47 -> Y6 (C3088 pin7)
pin 48 -> Y5 (C3088 pin6)
pin 49 -> Y4 (C3088 pin5)
pin 50 -> Y3 (C3088 pin4)
pin 51 -> Y2 (C3088 pin3)
pin 52 -> Y1 (C3088 pin2)
pin 53 -> Y0 (C3088 pin1)


C3088 Board:
pin 20,22 -> Vcc(+5V)
pin 31,21,15 -> GND
pin 11 SDA(I2C)
pin 13 SLC(I2C)
pin 1,2,3,4,5,6,7,8 Ychannel(output)
pin 14 HREF (output)
pin 16 VSYNC    (output)
pin 18 PCLK (output)
pin 10 RST (input)
pin 9 PWDN (input)
*/

// #### DEFINITIONS ####

// system clock
#define SYSCLK 16

// CCD sensor control pins
#define HREF 40
#define VSYNC 38
#define PCLK 36
#define RST 44
#define PWDN 45

// CCD sensor power pins
#define VCC_A 32
#define VCC_B 34
#define GND_A 23
#define GND_B 33
#define GND_C 39

#define LED 13

// i2c registers used to talk to the ccd sensor

#define CCD_COM_A 0x12 // address register A
#define CCD_RESET  0x80 // reset command
#define CCD_CLKRC 0x11 // address for clock register
#define CCD_FRQ 0x10 // frequency command
#define CCD_COM_C 0x14 // address register A
#define CCD_QCIF  0x20 // reset command

// maybe not necessary
#define camAddRead 193
#define camAddWrite 192

#define camRegClock 0x11
#define camRegControlA 0x12
#define camRegControlC 0x14

#define camDatReset 0x80
#define camDatbarras 0x26
#define camDatEspejo 0x40
#define camDatEspejoNo 0xBF

#define camDatFrecVeryLo 0x3F  // very low frame rate, PCLK=69KHz, about 1 frame every 1.2 s
#define camDatFrecLo 0x10    // low frame rate
#define camDatFrecMed 0x01   // medium frame rate
#define camDatFrecUp 0x00    // high frame rate

#define camDatQCIF 0x20    // define size image
                           // QCIF 176x144
                           // CIF  352x292

#ifndef UCSRB
#ifdef UCSR1A          /* ATmega128 */
#define UCSRA UCSR1A
#define UCSRB UCSR1B
#define UBRR UBRR1L
#define UDR UDR1
# else /* ATmega8 */
#define UCSRA USR
#define UCSRB UCR
# endif
#endif
#ifndef UBRR
#define UBRR UBRRL
#endif
#define TWI_SLA_CAM   0xc0  //Cam C0    sensor 90
#define MAX_ITER        200
#define PAGE_SIZE       8 // before 8

// #### local variables ####
#include "WProgram.h"
void setup();
void loop();
void reset();
int i2c_write_page(uint16_t eeaddr, int len, uint8_t *buf);
int i2c_write_bytes(uint16_t eeaddr, int len, uint8_t *buf);
int write_register(uint16_t numregister, uint8_t value);
void i2c_init(int clk);
int count=0;
int ind=0;
unsigned int i=0,ii=0,iii=0;
byte ccd_pixel=B00000000;
byte ccd_frame[6401]={0};
byte ccd_line[176]={0};
int pixel_number = 0;
int pixel_number_global = 0;
int line_number = 0;


// #### external libraries ####
// avr lib reference: http://www.nongnu.org/avr-libc/user-manual/modules.html
#include <avr/io.h>
#include <util/twi.h>
#include <inttypes.h>


// #### SETUP ####
void setup(){

  Serial.begin(115200);    // start serial port
  reset();                 // reset the CCD sensor  
  i2c_init(SYSCLK);        // initialize the i2c
 
 
  //Serial.print("Reset sensor(1=ok): ");
  write_register(0x12, 0x80);
  //Serial.print(".");
  delay(1000);
  write_register(0x11, 0x14);
  //Serial.print(write_register(0x11, camDatFrecVeryLo));    //set the ccd sensor frame rate to very low PCLK=69KHz
  //Serial.print(".");
  delay(1000);
  //Serial.print(write_register(0x14, camDatQCIF));  //set the ccd sensor image size to QCIF (equivalent to 176x144)
  write_register(0x14, camDatQCIF);
  //Serial.print(", ");
  delay(1000);
  write_register(0x28, 0x41);  // set B/W mode
  //Serial.print(write_register(0x12, 0x26));  //set the ccd sensor color bar test pattern
  delay(1000);
}


// #### LOOP ####
void loop(){
  while((PIND & 0x80)==0){}
  while((PIND & 0x80)==128){}  // detect falling edge of VSYNC
  // begin of a frame
  line_number=0;
  pixel_number_global =0;
  //Serial.print("frame: ");
  while(line_number < 30){  // repeat line acquisition ONLY 30 times
    while((PING & 0x02)==2){}
    while((PING & 0x02)==0){}  // detect rising edge of HREF
    // begin of a line
    pixel_number = 0;
    while(pixel_number < 30){  // repeat pixel acquisition ONLY 30 times
      while((PINC & 0x02)==2){}
      while((PINC & 0x02)==0){}  // detect rising edge of PCLK
      
      // perform a 8-bit pixel acquisitions
      //PORTF=0xFF;  // pull up all port F pins - debugging purposes
      ccd_frame[pixel_number_global] = PINK;  // fast Y[0-7] data reading via PORTK
      pixel_number++;  // number of pixel in a line
      pixel_number_global++;  // number of pixel in a frame
      //PORTF= 0x00;  // pull up all port F pins - debugging purposes
  }
  line_number++;
  // one LINE has been acquired and save in  in ccd_frame[], the other lines will follow it
  }
  // ONE PARTIAL FRAME (30X30 pixels) has been acqired, let's print it
  for (i = 0; i < 900; i++){
      Serial.print(ccd_frame[i], HEX); Serial.print(",");
    }
  Serial.println();
}


// #### RESET ####
void reset() {
  int i=0;
  
  //set pin22~53 to high impedence
  for(int i = 22;i<=53;i++) pinMode(i, INPUT);
  for(int i = 22;i<=45;i++) digitalWrite(i, HIGH); // set all pins (except Y0~7) to high impedence
  for(int i = 46;i<=53;i++) digitalWrite(i, HIGH); // set pins Y0~7 to high impedence
  
  // et PORTK of Arduino Mega ANLOG IN 8-15 as input for FAST Y0~7 reading
  DDRK = 0b00000000;
  
  // set PORTF, Arduino Mega ANALOG IN 0-7 as output for some DEBUGGING PURPOSES
  DDRF = 0b11111111;
  
  pinMode(HREF, INPUT);
  pinMode(VSYNC, INPUT);
  pinMode(PCLK, INPUT);
  pinMode(RST, OUTPUT);
  pinMode(PWDN, OUTPUT);
  
  // some interrupt pins, set them as input and put a pullup resistor
  for(int i = 14;i<=19;i++) pinMode(i, INPUT);
  for(int i = 14;i<=19;i++) digitalWrite(i, HIGH);
  for(int i = 2;i<=13;i++) pinMode(i, INPUT);
  for(int i = 2;i<=13;i++) digitalWrite(i, HIGH);  
  
  //let's power up the CCD sensor
  pinMode(VCC_A, OUTPUT);
  pinMode(VCC_B, OUTPUT);  
  pinMode(GND_A, OUTPUT);
  pinMode(GND_B, OUTPUT);
  pinMode(GND_C, OUTPUT);
  
  digitalWrite(VCC_A, HIGH);
  digitalWrite(VCC_B, HIGH);
  digitalWrite(GND_A, LOW);
  digitalWrite(GND_B, LOW);
  digitalWrite(GND_C, LOW);

  digitalWrite(PWDN, LOW);
  delay(100);  
  
  //let's reset the CCD sensor
  digitalWrite(RST, HIGH);
  delay(300);
  digitalWrite(RST, LOW);
  
}


// #### i2c write page ####
// this function was originaly written by Joerg Wunsch 2002/12/18 joerg@FreeBSD.ORG
// modified by Jaakko Ala-Paavola 2003/08/20 jap@iki.fi
// modified by Ioaki Navarro Oiza March 2004
resto of the code in the next post or for the whole think follow the link at the top
« Last Edit: June 10, 2010, 08:45:29 am by Vincent_ch » Logged

Nowhere
Offline Offline
God Member
*****
Karma: 3
Posts: 852
|-\ |\|\
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset

Wow.  That's a lot of scrolling.  And it doesn't even show the whole thing!
Use the code brackets, it's a button that looks like a # in the posting editor.  Also you could put the code in two separate posts if it does not fit in one.
Logged

Soundcloud page: http://soundcloud.com/beefinator-2
Youtube channel: http://www.youtube.com/user/beefinator14
Old soundcloud page (ran out o

0
Offline Offline
Newbie
*
Karma: 0
Posts: 31
Arduino rocks
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset

the rest of the code

Code:


// #### i2c write page ####
// this function was originaly written by Joerg Wunsch 2002/12/18 joerg@FreeBSD.ORG
// modified by Jaakko Ala-Paavola 2003/08/20 jap@iki.fi
// modified by Ioaki Navarro Oiza March 2004

int i2c_write_page(uint16_t eeaddr, int len, uint8_t *buf){
  uint8_t sla, n = 0;
  int rv = 0;
  uint16_t endaddr;
  if (eeaddr + len < (eeaddr | (PAGE_SIZE - 1)))
    endaddr = eeaddr + len;
  else
    endaddr = (eeaddr | (PAGE_SIZE - 1)) + 1;
  len = endaddr - eeaddr;
  /* patch high bits of EEPROM address into SLA */
  sla = TWI_SLA_CAM | (((eeaddr >> 8) & 0x07) << 1);
  restart:
  if (n++ >= MAX_ITER)
    return -1;
  begin:
  /* Note 13 */
  TWCR = _BV(TWINT) | _BV(TWSTA) | _BV(TWEN); /* send start condition */
  while ((TWCR & _BV(TWINT)) == 0) ; /* wait for transmission */
  switch ((TW_STATUS))
    {
    case TW_REP_START:          /* OK, but should not happen */
    case TW_START:
      break;
    case TW_MT_ARB_LOST:
      goto begin;
    default:
      return -1;                /* error: not in start condition */
                                /* NB: do /not/ send stop condition */
    }
  /* send SLA+W */
  TWDR = sla | TW_WRITE;
  TWCR = _BV(TWINT) | _BV(TWEN); /* clear interrupt to start transmission */
  while ((TWCR & _BV(TWINT)) == 0) ; /* wait for transmission */
  switch ((TW_STATUS))
    {
    case TW_MT_SLA_ACK:
      break;
    case TW_MT_SLA_NACK:        /* nack during select: device busy writing */
      goto restart;
    case TW_MT_ARB_LOST:        /* re-arbitrate */
      goto begin;
    default:
      goto error;               /* must send stop condition */
    }
  TWDR = eeaddr;                /* low 8 bits of addr */
  TWCR = _BV(TWINT) | _BV(TWEN); /* clear interrupt to start transmission */
  while ((TWCR & _BV(TWINT)) == 0) ; /* wait for transmission */
  switch ((TW_STATUS))
    {
    case TW_MT_DATA_ACK:
      break;
    case TW_MT_DATA_NACK:
      goto quit;
    case TW_MT_ARB_LOST:
      goto begin;
    default:
      goto error;               /* must send stop condition */
    }
  for (; len > 0; len--)
    {
      TWDR = *buf++;
      TWCR = _BV(TWINT) | _BV(TWEN); /* start transmission */
      while ((TWCR & _BV(TWINT)) == 0) ; /* wait for transmission */
      switch ((TW_STATUS))
        {
        case TW_MT_DATA_NACK:
          goto error;           /* device write protected -- Note [14] */
        case TW_MT_DATA_ACK:
          rv++;
          break;
        default:
          goto error;
        }
    }
  quit:
  TWCR = _BV(TWINT) | _BV(TWSTO) | _BV(TWEN); /* send stop condition */

  return rv;
  error:
  rv = -1;
  goto quit;
}


// #### i2c write bytes ####
// this function was originaly written by Joerg Wunsch 2002/12/18 <joerg@FreeBSD.ORG
// modified by Jaakko Ala-Paavola 2003/08/20 jap@iki.fi
// modified by Ioaki Navarro Oiza March 2004

int i2c_write_bytes(uint16_t eeaddr, int len, uint8_t *buf){
  int rv, total;
  total = 0;
  do{
      rv = i2c_write_page(eeaddr, len, buf);
      if (rv == -1)
        return -1;
      eeaddr += rv;
      len -= rv;
      buf += rv;
      total += rv;
    }
  while (len > 0);
  return total;
}


// #### write register ####
// this function was originaly written by Joerg Wunsch 2002/12/18 <joerg@FreeBSD.ORG
// modified by Jaakko Ala-Paavola 2003/08/20 jap@iki.fi
// modified by Ioaki Navarro Oiza March 2004

int write_register(uint16_t numregister, uint8_t value){
  uint8_t *pvalue;
  int num;
  pvalue = &value;
  num = i2c_write_bytes(numregister, 1, pvalue);
  if(num!=1) return -1;
  else return 1;
}


// #### write register ####
// this function was originaly written by Joerg Wunsch 2002/12/18 <joerg@FreeBSD.ORG
// modified by Jaakko Ala-Paavola 2003/08/20 jap@iki.fi
// modified by Ioaki Navarro Oiza March 2004
void i2c_init(int clk){
  /* initialize TWI clock: 100 kHz clock, TWPS = 0 => prescaler = 1 */
#if defined(TWPS0)
  /* has prescaler (mega128 & newer) */
  TWSR = 0;
#endif
  TWBR = (clk*1000000 / 100000UL - 16) / 2;
}

Logged

Wigan, UK
Offline Offline
God Member
*****
Karma: 18
Posts: 814
View Profile
WWW
 Bigger Bigger  Smaller Smaller  Reset Reset

Awesome project!

Logged

Pages: [1]   Go Up
Jump to: