Skip to main content

Topics

This section allows you to view all Topics made by this member. Note that you can only see Topics made in areas you currently have access to.

Topics - Fractal

1
Project logs / Interfacing a mysterious CMOS
I found a camera in the parts bin, and wanting to use what I had, decided to try and find out how to interface it with an arduino.
With any luck this will document some of the thinking, and the mistakes, that I went through.

Alas, as of writing, this is incomplete. (2012-05-10)
Latest post: http://http://dangerousprototypes.com/forum/viewtopic.php?f=56&t=3934&p=40315#p40315
(if I remember to update this post)
[s:]Also, I can't embed pictures or links, due to new user status. Wahey.[/s:]



So, I decided it would be fun to document how I personally went about hacking this wretched camera.
1) Firstly, why does it not just work?
A: Insane mini-din connector.

What pins do what? No way to tell without disassembly.

2) Disassembly.
Remove a few screws, unsolder that massive cable.
Take care of the CMOS sensor though, don't want a scratch in the glass.



3) Find datasheet
Fine, just [s:]google[/s:] use a popular search engine to search for the numbers written on the board, easy.
One result. One!
And it's just another hacker looking to interface it, from Lithuania.
http://http://forum.elektronika.lt/viewtopic.php?t=2884(or only result for "PMG005BJTCVSN")
Mercifully, that leads to more searching, and the product page!
http://http://www.prolink-usa.com/english/product/vc/mplite.htm (or second result for "PV-M1300")
Alas, it needs a PCI card to work with a computer. Foiled?

Drat, standard approach has failed. I need a datasheet for that sensor, and it has no writing on it.
Wait..., it probably does have some lettering from the manufacturer. If only I had a lens that would focus accurately to the scale of the sensor.
Using the lens in reverse (well, light passing through the wrong way.

Those are the gold wires leading to the die.
Nigh impossible to photograph, but using a bright light, and the human eye, I got
VVL-404AB, and in much smaller writing underneath:
.....…1996 VISION LTD
Excellent! Pop them into [s:]google[/s:] search engine of your choosing, and you get... nothing.

That failing, find this manufacturer's website, and their list of datasheets ("vision limited datasheet")
Aha, there is a VV6404, and a VV5404, seemingly colour and mono versions of the same silicon chip.
The colour version has a filter over the top, and I can see a little RGBG section on the central die, probably for calibration.
Datasheet=Acquired.

4) Search for prior art

(seach for "CMOS camera as a sensor" to get a guide for a different sensor, and an arduino mega)
http://http://www.ikalogic.com/image-processing-as-a-sensor/

5) Steal prior art
Unfortunately, I've got nowhere near the programming skill to make sense of that code in any reasonable time, and the whole sensor is different. I will steal the general principle, which is:
*Lower the CMOS clock speed, so it has a reasonable chance of being processed by an atmega328
*Do the mission critical bits in assembly



6) Map out the electronics
Now is time to find out what bits connect to what on the board. Importantly, one of the pins connects to a "zr78 l05" which the experienced among you will recognize as a 5v regulator. Excellent, that's good news for my 5v arduino. Also, looking at power consumption, it can be powered off the arduino 5v regulator (after all, that's what it's there for, rapid project making, which includes power).
Also there is a "C01A" chip, and while I don't know the brand, it appears to be a I2C eeprom chip (turns out to be half a KB).
Datasheet for that is pretty standard, and mostly goes into explaining I2C. I know it's 5v anyway.
Trouble strikes- the board is 4 layer (!), and there are a whole mess of interconnects underneath the cmos.

Hacker tip: Rather than faff around, probably getting fingerprints all over an unknown board, take pictures of it, scale them to the same size, and remove skew, and print it double sided on paper. Now the interconnects line up, and you can scribble all over the chip with labels!

Time for the needles

Now, all the pins on that 9 wire DIN  (numbering from the PCB though)are mapped out to the CMOS:
1: D[0], 2: GND, 3: D[1], 4: VREGIN, 5: D[2], 6: SCL, 7: D[3], 8 SDA, 9: CLKI
There seem to be various filtering components, like little inductors, and capacitors. Usefully, the I2C lines are pulled high.
The data lines are not though - I suspect they should be pulled low...

7) Soldering iron time!

I soldered on some more sane length wires where needed, and decided to just bypass the voltage regulator, and give the camera 5v directly. Over this cable length, it's ok (probably). They interface with a protoscrewshield, so terminate in bare wire.


Basically, I want this to be all the soldering I need to do to get the hardware ready. Then it's easy to work on software when I get the time.

8) Test the basics
Well, I know how to talk to an I2C eeprom, but can I?
I found an I2CScanner.pde, by Tod E. Kurt (link), which just sends all the addresses possible over I2C, and sees if an ACK is received.
Yes. The wires work! A quick scan of the memory chip reveals it to be empty, and some write tests show it to be 512 bytes big.
However, the cmos won't respond, and a perusal of the datasheet reveals why- it needs a clock in, for 16 cycles before, during, and after I2C. Why after? It is a mystery.

9) Figure out conceptually how the eventual program will work
10) Also 'back of the envelope' maths

What must I do to get data off this thing?
Firstly guess: Store one frame? No, attempting to get all the pixels is going to be nigh impossibly, 356*292=103952 bytes, far more than the atmega328's 2kB SRAM - no holding a frame in memory!
Secondly, the 'minimum' clock speed is 0.45 Mhz, and as a newbie programmer, I am not going to be able to read, and do stuff with the 4 data lines in, what, ~35 clock cycles of the 16 Mhz atmega328. The
Code: [Select]
digitalWrite(pin, 1)
command uses more than that!
Thirdly, I can't even pass through via serial- the baud rate is max 115200 bits per second, and that's just one signal, let alone 4.

I suppose what I ought to do is find a way of booting the camera up, as detailed in the datasheet. That's progress at least.
I very much suspect that the chip can be driven a lot slower, though the image would be awful.
It does look like the I2C interface can be used entirely to boot up the camera, and that is in the realm of possibility.
Still can't tell if it's working though - could just do digitalRead(D[0]), and look for 'random' data that's more 1's than 0's when I shine a light on it. Could just read any pixels, and gradually slow the clock signal until it stops being 'random'.

11) Project dies of complications?
Much like many other projects, this is more than I can chew.
It's pretty helpful to write down what you know, then it's much easier to get back to where you left off.

12) Write up project so far
I found a website I frequented with lots of neat projects on it, and wrote a very wordy forum post.
Sadly, the images did not appear for 24 hours, but they were later added when time permitted.

13) Panic
[s:]Now, this is where I'm up to, as of 2012-04-18[/s:]
 I don't know how I am going to get my code fast enough to read even a tenth of the pixels.
Will update as progress is made.

2012-04-19:
14) Cool off, think over again. Anything is possible.
Read the helpful comments written by people reading the write up.
Make a rough plan, do some more "napkin maths" to find feasibility.

 Plan
Learn direct port manipulation!
Mercifully, the arduino platform is fairly well documented.

Plan
#1Get the camera to 'boot'. Can check status by using I2C to read the 'current line count' and seeing if it changes with time.
Requires:
?An interrupt driven or somesuch fast CLKI signal
+The Wire library
-Frankly, a Bus Pirate, but I don't have one of them!
?Some sloppy hacked together code to send two things with a delay over I2C, and then repeatedly read one register.

Serial operation:
This seems to be quite common- a CMOS chip has an I2C part, and a bit that sends ADC bytes  from every pixel in a certain sequence. This particular one uses 4 data lines, and so requires two clock cycles to clock out one byte - in two "nibbles".
Operation is obviously designed with certain specific sequences that indicate frames, lines, etc.
However, most of the time, it goes [abcd] [efgh] for each pixel, where the brightness is proportional to the byte [abcdefgh].

Therefore, for my purposes, which is a black and white eg 84*48 pixel display, I can ignore most of the data.
Thusly if I record in an array in RAM, and get the most significant bit for every 6th pixel, so every 12th clock cycle, I might stand a chance of getting a frame. Now I've got lots of clock cycles to account for crap programming, though I guess I'll need an interrupt every 12 generated cycles of the camera.

Startup
Unsurprisingly, this digital device does not "just work". It requires a clock, and two separate serial commands, thoughtfully detailed in the datasheet.

After that, though...
Note that there is a little problem - AVR's are not known for their multitasking, though sending I2C comms is slightly abstracted from the main core. Standard
Code: [Select]
analogWrite(pin, 127);
won't be fast enough, apparently. (490 Hz? vs 'minimum' of 0.5 MHz
I bet it just comes out way overexposed though.

Sending that clock
Various things are detailed in this post further down : http://http://dangerousprototypes.com/forum/viewtopic.php?f=56&t=3934#p39548
Also:
Mainly, I need a clock or something, that essentially does:
Code: [Select]
begin_clock(pin 3 probably, 0.5 MHz);  //Can just screw with timer(2)'s registers?
also_attach_interrupt(whenever_clock_triggers, Function_to_process_stuff());
Can I just, erm, use the attachinterrupt(1, FALLING, function..); command? (this ought attach a hardware interrupt to pin 3)
Will it screw it up, having a timer triggering a pin change, then an interrupt noticing that pin change?
Answer, yes, but it's a faff

Startup, getting random data
With some code
Code: [Select]
#include <Wire.h> //for sending setup
#include <avr/io.h> //Yes, I think I do want to use inputs and outputs
#include <avr/interrupt.h> //works a lot better with this one
#include <stdint.h>

int cmosAddress = 0x10; //actually 16, hex 10
//don't freak out, uint8_t is just a byte
volatile uint8_t bytenumber=0; //starts at 0 for first uint8_t.
volatile uint8_t inputbyte;
volatile boolean uint8_tunread =0;
volatile uint8_t nibble0forMSB =0;
volatile uint8_t inputArray[2][256];
uint8_t benchstart;
uint8_t benchend;
uint8_t cyclecount =32;

boolean state=0;

void blink()
{ //remarkably, this code is really slow for what it does
  state = !state;
  PORTB =  (PORTB & B11011111 )+ (state << 5);
}

void setuptimer_2B() {
  cli(); //stop interrupts. Some other code did this, seems like a plan
  TCCR2A = _BV(WGM21) |_BV(WGM20) | _BV(COM2B1); //FastPWM, non inverting.
  //Turn off COM2B1 for normal pin use
  TCCR2B = _BV(WGM22) | _BV(CS20); //no clock divider, pin high at timer2 = 0
  //TCCR2B = _BV(WGM22) | clockbits;
  OCR2A = cyclecount; //should be ~32 cycles -timer goes to 0 after hits this
  OCR2B = cyclecount/2; //when the pin flips to low- half way
  sei(); // turn on global interrupts
}

void setupinterrupt_2B(boolean offoron) {
  cli(); //just in case, turn off global interrupts
  TIMSK2 = (TIMSK2 & B11111011) + (offoron << OCIE2B); //This bit turns on/off the interrupt.
  sei(); // turn on global interrupts
}

void readbyte2() { //new and speedy (12 cycles)
  byte tempbytenum =bytenumber;
  inputArray[0][tempbytenum] =PIND;
  bytenumber = tempbytenum +  1;
}
void readbyte1() { //old and busted (!4 cycles)
  inputArray[0][bytenumber] =PIND;
  bytenumber++;
}

ISR(TIMER2_COMPB_vect) { //INTERRUPT 2B INTERRUPT 2B
  readbyte2(); //should trigger every time pin3 is set low
}

void benchmarkinterrupt() {
  uint8_t benchstart1 = TCNT2;
  // Functiongoeshere(); //probably a better way to do this, but meh
  readbyte2();
  uint8_t benchdiff = TCNT2-benchstart1;
  Serial.println(benchdiff);
}

void benchmarkinterruptempty() {
  uint8_t benchstart1 = TCNT2;
  uint8_t benchdiff = TCNT2-benchstart1;
  Serial.println(benchdiff);
}

void setup()
{
  Wire.begin(); //need this for sending commands
  Serial.begin(115200); //Fast as lightning!
  pinMode(13,OUTPUT); //debug LED
  pinMode(3,OUTPUT); //CLKI
  pinMode(2, OUTPUT); //debug voltmeter
  pinMode(7,INPUT); //Data D3
  pinMode(6,INPUT); //Data D2
  pinMode(5,INPUT); //Data D1
  pinMode(4,INPUT); //Data D0

  //start the camera clock
  setuptimer_2B(); //prescaler of 1. Note weird binary stuff
  blink();
  delay(50);

  //send the 'soft reset' command
  Wire.beginTransmission(cmosAddress);
  Wire.write( B0010000);
  Wire.write(B00001101);
  Wire.endTransmission();
  delay(50);

  //send the 'exit low power' command
  Wire.beginTransmission(cmosAddress);
  Wire.write( B0010000);
  Wire.write(B00001000);
  Wire.endTransmission();
  delay(50);

  //set clock division to 2
  Wire.beginTransmission(cmosAddress);
  Wire.write( B0100101);
  Wire.write(uint8_t( B00000000 )); // all zeros for prescaler of 2
  Wire.endTransmission();
  delay(50);
  blink();
  setupinterrupt_2B(1); //All hell beaks loose at 0.5 MHz
}

void loop()  // LOOP ENABLE LOOP ENABLE
{
  if (bytenumber == 255) { //why 0? There
    setupinterrupt_2B(0);
    blink();
    for ( uint8_t bytenumber1 =0;bytenumber1 != 255; bytenumber1++) {
      Serial.print(inputArray[0][bytenumber1]/16,HEX);
      inputArray[0][bytenumber1] = 0;
    }// outputs that stored data
    bytenumber = 0;
    Serial.println();
    Serial.println(micros());
    nibble0forMSB =0; //from trying to be too cunning
    benchmarkinterrupt();
    nibble0forMSB =1; //some legacy code bits
    benchmarkinterrupt();
    benchmarkinterruptempty(); //should be 2 cycles of poor compiling to do nothing
    blink();   
    delay(1000);  //this stops the serial port from exploding
    //setuptimer_2B();
    setupinterrupt_2B(1);

  }
}  // LOOP END
it will do some data!
eg
Code: [Select]
996669996699966669996669996699966699966699966699996699966699966699966699966699966699966699966699966699966699966699966699966699966699966699966699966699966999966699966699966699966699966999666699966999666699966999666999666999666999666999666999666999666999666 
//the signal to lock onto- not tremendously even, really.
CB9111C9C1118D91118EC111C9811BAD9111DB911187B1218DB111BB8119A811117AE119AA1111B7811F7B111ABA111A981121DB1121B1111ABA111E8911189A11198A111A861118BA1119AB11197A1116EA111BCA111D99111CEA1118A8121A88111BBB1118E8111CE9119991111B9111AB81111989111AD811A8B111ACE11
//sensor with light blocked off
EFFFFFFFFFFFFFFF0605FEEEFFFEEEFFFEEEFFFEEEFFFEEEFFFEEEFFFEEEFFFEEEFFFEEEFFFEEEFFEEEEFFFEEEFFFEEEFFFEEEFFFEEEFFFEEFFFEEEEFFFEEFFFEEEEFFFEEEFFEEEFFFEEEFFFEEEFFFEEEFFFEEEFFFEEEFFFEEEFFFEEEFFFEEEFFFEEEFFFEEEFFFEEEFFFEEEFFFEEEFFFEEEFFFEEEFFFEEEFFFEEEFFEEEEFFFE
//overexposed, and maybe a control code?
Note of course that in the interest of speed, that's just reading the bus lines every time it sets the clock low. Weirdly, it seems to do stuff every 3 cycles, or, be inconsistent upon multiple reads of the same pixel quickly.

Learn assembly
So, it has come to this. The code really needs a nice fast polling routine to check every 2 sent clock cycles (I'll keep the fast PWM to send a stable signal). The compiled code is too slow.

Alright, I've learnt some basic assembly stuff, and have proceeded. Can now get it to read a whole bunch on seeing the 'visible line' control code in the data.
This leads to shoddy pictures: http://http://dangerousprototypes.com/forum/viewtopic.php?f=56&t=3934&p=40315#p40315

As before, will update as progress is made (though that might be for my own sake, rather than anyone else's!)