TASK : Write a device-driver for Omnivision ov3640 camera
Timeline : A.S.A.P (is there any other way? :P)
For the aptitude champs out there, here is a quick one:Q. If you are sitting facing west and running I2C at 0.00000000055kbps and a bear appears in front of you, what color is the bear?
Not sure? Read on...
DAY 1 : Initial study
A bit about ov3640: The ov3640 (color) image sensor is a 1/4-inch 3.2-megapixel CMOS image sensor that is capable of QXGA(2048x1536)@15FPS using OmniPixel3™ technology in a small footprint package. It provides full-frame, sub-sampled, windowed or arbitrarily scaled 8-bit/10-bit images in various formats via the control of the Serial Camera Control Bus (SCCB) interface or MIPI interface. It supports both a digital video parallel port and a serial MIPI port.
Searching the "internets", an old "v4l2-int" styled driver for ov3640 is available for the linux-kernel. This will have to do for now. Can scavenge the camera configuration register-settings from it.
The Omnivision ov3640 product-brief contains the following functional block-diagram of ov3640:
The camera is controlled using the SCCB bus. Again back to the "internets". SCCB is an i2c-clone i.e a two-wire serial protocol that has significant differences from I2C to merits its own specification.
- According to spec, SCCB supports only upto 100Khz (not more).
- I2C spec requires pullups with open-collector(drain) drivers everywhere. SCCB requires CMOS-like drivers which are always either +VDD or GND i.e no pullups.
- In I2C, after every 8bits transferred, the 9th bit is designated ACK. The slave pulls SDA low to ack. SCCB designates the 9th bit "dont-care". SCCB spec states that the master continues regardless of ACK/NACK in the 9th bit.
DAY 2 : First attempt
Following the omnivision product-brief and the datasheet, the ov3640 camera-module is connected with the CPU as follows:
So far SCCB looked to be a simpler less restrictive version of I2C. Having worked extensively on I2C previously, was under the impression that setting up the basic comunication between the CPU and ov3640 would be a walk in the park. Wrote a simple skeleton i2c-driver and registered it with the kernel. Scavenged the I2C read/write routines from the old ov3640 driver and booted-up the device...
...and the driver failed to load as I2C-read failed. The ID register of ov3640 did NOT match the expected ID. Inserting logs in the code showed that the I2C-read routine was failing. The CPU was NOT getting an ACK from the ov3640 sensor. A true WTF moment as the I2C routines in the driver were tested to be working properly in earlier devices.
Oh well, maybe should really not expect an ACK i suppose. What with ov3640 being an SCCB device and not I2C. Started digging into the I2C core driver. found a provision to ignore this NACK(absense of ACK from slave). Updated the ov3640 driver to set the IGNORE_NACK flag and tried again. Now the I2C-read routine completed successfully despite there being no ACK from the slave. But still the driver failed to load. Turns out the contents of the ID register, read over I2C, did NOT match the expected value. The I2C-read routine was returning the contents of the ID register as "0". Further debugging showed that attempting to read any register of ov3640 over I2C gave the same result- a nice big ZERO. It was evident now that something was terribly wrong.
DAY 3 : Challenge Accepted
Time to bring out the big guns. Switched to multimeter and oscilloscope. Tested the lines from CPU to the ov3640 connecters for proper continuity. Booted-up the device and probed the I2C lines. The master was sending in the right values alright. But ov3640 was simply not responding. Suspect no.1 ? the I2C slave-id.
Ov3640 spec mentions that it responds to 2 I2C slave-IDs. 0x78 & 0x79. Had tried 0x78 so far. 0x79 also makes no difference - still no data from ov3640. Further digging through the docs i find one interesting line which mentions that the addresses are special in the sense that 0x78 is used to write and 0x79 to read the device. Hmmm... interesting. Lokks like these are 8bit addresses including the read/write bit of I2C. Which means the actual device slave-id is just the 7MSBs (common to 0x78 & 0x79) i.e. 0x3C. Face-palm!
Changed the slave-id of the ov3640-driver and booted-up the device, but still no dice. It would be easier to light a fire with 2 stones and twig.
DAY 4 : ...and let there be light
Lost all hopes of getting this to work. Swapped other camera modules. Tried a couple of other boards. But the ov3640 just does not seem to respond to anything. It is as if the module is not even powered-on.
Maybe, mayyyybe thats wat it IS!
Maybe, mayyyybe thats wat it IS!
Back to the schematics.
I2C-CLK? check.
I2C-DATA? check.
CAM_XCLK? check.
CAM-IO? check.
CAM-digital? check.
CAM-Analog? Do we really need to power the sensor array at this stage?
Well what the heck nothing else seems to work anyway. Might as well try this. So quickly pulled down a line from an existing 3.3V power-rail on the board. Placed a diode along it to drop it down a bit and powered-on the board.
And VOILA! It worked. The driver was able to read the ov3640 module properly.
The ov3640 even responded to the default settings (QXGA@15FPS). Pretty neat eh?
Oh well... sure makes me look foolish, now that it works. :-)
Ah well there's always the first time for everything. ;-) ;-)
Ah well there's always the first time for everything. ;-) ;-)
And now that it works, was able to summarise the following
Hardware connections:
- CAM-Analog (2.8V for powering-on the module)
- CAM-IO 1.8V (1.8V for i2c-communication)
- CAM-Digital (1.5V generated by module)
- I2C_CLK (1.8V 400Khz-MAX for i2c-communication)
- I2C_DATA (1.8V bi-directional)
- CAM_XCLK (24Mhz reqd. for internal PLL)
- CAM_PCLK (generated by module)
Software configurations:
- I2C slave-id 0x3c
- ov3640 DOES provide an ACK (its I2C, NOT sccb)
- Works on I2C@400KHz
With the above specs, surely this begs the question that why does someone go all the way to define their own bus-specs when the hardware obviously works on I2C!!!! WHY Omnivision? WHY????
Designing you own serial-bus? == $1,000,000
Not using it in your own products? == $0
The smile on my face when i finally figure it out? PRICELESS
Somethings, money can't buy. For everything else , there is... Ah wait, i'm forgetting something now, right? Well here goes...
Q. If you are sitting facing west and running I2C at 0.00000000055kbps and a bear appears in front of you, what color is the bear?Ans. If it takes 4 days to transfer a byte, do you REALLY think i care!!
NOTE:
[i]. No bears were harmed in the development of this camera-module.
[ii]. The image captured above did NOT appear out of the blue with only the driver in place. Several days of tweaking the exposure/white-balance settings and an earthquake later, managed to get the Kernel-driver, Android camera-HAL and app to work together.