Note: this post is still a work in progress.


  • add an AXI IIC block to the design and run automation
  • run synthesis and generate the bitstream

Image Sensor Bringup

As a sanity check that the image sensor is working, let’s integrate an AXI i2c peripheral and make sure we can snag the camera sensor ID values. We will write some firmware to run on the PS (Zynq speak for the ARM cores). Here are the general steps to follow:

  • Make a new block design with the Zynq PS included.
  • Add an AXI IIC (Xilinx for I2C) block and run automation.
  • create a quick block to generate an xclk signal for the image sensor. I just divided my main clock by 4 to hit 12.5 MHz.
  • I tweaked the IO pins of the I2C block to use some pins on my board that are unencumbered by pullup resistors. The ArduCam module and the Cora board that I’m using both try to be helpful by including the I2C pullup resistors, but I don’t want any pull issues so I will rely on just the ones on the camera module.
  • Ensure the I2C block is seen by the Zynq. You may need to go into the Zynq block and enable it after it’s added to the block diagram.
  • Generate bitstream and export hardware to SDK.


I created a new board support package. Assuming the block design is set up correctly, the BSP will include the Xilinx IIC (I2C) driver code. Then it’s time to look at the example code provided by the I2C driver to get the basic usage pattern down.

Examples are located in TODO FILE PATH for me.

A Few Gotchas

It took a little while to get the sensor talking. Here were some of the things I overlooked.

  • The camera sensor needs xclk to begin replying over I2C. This became obvious after looking at the camera module pinout and wondering what xclk was for.

  • The device IDs in the OV2640 datasheet are in hex! And they include the read/write bit. The read/write bit part was obvious in that two addresses were given, the radix was not obvious. It seems that Omnivision uses hex by default in their datasheets, without a 0x prefix or an h to be seen.

    I only uncovered this after blasting requests to all possible I2C addresses and using a logic analyzer to find the ACK.

    Note that the Xilinx I2C drivers will add a read/write bit accordingly, so only supply the 7-bit address and make sure the radix is correct. For my OV2640, this is 0x60 or 96.

  • Despite what the datasheet says, the OV2640 seems to have 2 options for the product ID - 0x42 or 0x41. I get 0x42 but 0x41 is in the datasheet. Given that I can read/write other registers and the ArduCam driver also lists these, it’s clearly not a problem.

Slave addr = 60 write 61 read

COM10 [4] will set the positive edge of pclk as the active edge

Register Set 1

Register FF controls which register page is accessable. When register FF is 0x00, the addresses do this: Reg F7 = slave ID MCU in the camera has BIST.

Register Set 2

When register FF equals 0x01 0A = product ID number (PIDH) 0B = product ID number (PIDL)

Set device addr = 60 write
Write 01 to FF 

Read (61) from 0x0A
Read (61) from 0x0B

Designing the Camera Interface Core

There are several clocks in this design.

  1. clk - the main system clock
  2. xclk - generated by the FPGA from the main clock, fed to the image sensor
  3. vclk - output from the camera, this is what the video stream is synchronized to

Therefore, we have some clock domain crossing to handle. First, we need to transition from the vclk domain into the clk domain. This occurs when we load in a pixel from the camera (synchronized to vclk), and need to handle it within the FPGA (the exent of the ‘handling’ changes).

How can we handle this? Let’s look at some examples.

A few Good Links