The Camera module is designed for the MT9V022 & 034 cameras from On Semiconductor, formerly Aptina. Internally there is a flexible configuration and control system, however only a tiny fraction of the camera’s features are surfaced.

The Module connects to the camera in two ways: via I2C for configuration and control. Other modules can connect to the Camera module’s data port and control lines.

The camera natively is 752 x 482 pixels, and can present a subwindow. The subwindow is set via the set_window line (where column_start, row_start, window_width and window_height have been set up. The origin can be independently set via set_origin (using column_start, row_start again).

Surprising in the age of LCD panels, blanking is still important! Both horizontal and vertical blanking are set via set_blanking (with the correct values on horizontal_blanking and vertical_blanking) Horizontal blanking is a critical means to slow the presentation of data to the system. The camera is incredibly fast, so a long horizontal blanking interval allows a FIFO to be filled and emptied without stalling the camera. Vertical blanking can give the sensor extra time for exposure.

Two tasks remain to camera_core users: 1) to wire the I2C lines up appropriately and 2) to provide the xclk.


There are several Camera Modules

  • camera_core (internally camera_config, including i2c_master_core)
  • camera_proxy

Camera Core

Camera connects to the panel, sends the initialization command stream, then sits and waits for commands.


Name Description Default Comment
Width   752 Fixed for the camera, of course, but handy to be able to reduce it for simulation
Height   482 As above
CoordinateWidth   10 0-1023
BlankingWidth   16 0-65536
CameraPixelWidth   10 10 bits
I2CClockCount   200 Clock divider for I2C bit clock (eg. 48MHz/200 = 240kbps)
I2CGapCount   256 Cycles between I2C messages


Name Dir Width Description Comment
clock In 1 Module clock  
reset In 1 Module reset  
configure In 1 Configure the hardware Takes the module into configuring mode. idle when complete.
start In 1 Goes from idle to running  
stop In 1 Go from running to idle  
configuring Out 1 Device is configuring  
error Out 1 Hardware error Communication timeout
idle Out 1 Idle - not started  
running Out 1 In a running state  
busy Out 1 Device Busy  
column_start In CoordinateWidth Left corner of capture  
row_start In CoordinateWidth Top corner of capture  
window_width In CoordinateWidth Capture window width  
window_height In CoordinateWidth Capture window height  
set_origin In 1 Update Top Left corner  
set_window In 1 Set the whole capture window  
horizontal_blanking In BlankingWidth Clocks requested for horizontal blanking Try 700
vertical_blanking In BlankingWidth Clocks requested for vertical blanking Try 500
set_blanking In 1 Sets the specified intervals  
snapshot_mode In 1 T/F  
set_snapshot_mode In 1 Sets the above mode  
snapshot In 1 Take a snapshot This mode is hard to use because it turns AGC off
out_vs Out 1   Vertical Sync
out_hs Out 1   Horizontal Sync
out_valid Out 1   Data is Valid
out_d Out CameraPixelWidth   Data
scl_out Out 1   I2C Lines
scl_in In 1    
sda_out Out 1    
sda_in In 1    
vs In 1   Video hardware lines from camera
hs In 1    
pclk In 1    
d In CameraPixelWidth    
rst Out 1   Hardware reset control line
pwdn Out 1   Shut the camera down
led In 1   Flash control - exposure indicator
trigger In 1   Trigger exposure then data output for snapshot mode

External Requirements

For the camera clock, one solution is to divide the 48MHz system clock

	reg [2:0] clock_divided;

	always @( posedge clock_48mhz ) begin
		if ( reset )
			clock_divided <= 0;
			clock_divided <= clock_divided + 1;

	assign xclk = clock_divided[ 0 ];

For the I2C, use the ECP5 primitives

    // Tristate Ports - Clock is pure output, data needs to be bidirectional (Open Drain)
	// T : Tristate, not Transmit!
	BB  clock_io( .I( camera_scl_out ), .T( 0 ), .O( scl ), .B( genio[18] ) );
	BB   data_io( .I( 0 ), .T( camera_sda_out), .O( sda ), .B( genio[19] ) );

Pixel Data

Simultaneous Master Mode - Continuous frames, exposure happens during the previous frame. Exposure is indicated by LED_Out. Makes sense.

    LED_Out   _/              \____________________________________________..._____________________________
              ___...___        ____________________________________________..._________________________
    Frame              \______/                                                                        \___
                                          _________         ________                _________
    Line      ___...___|______|__________/         \_______/        \______..._____/         \________|____
                       |      |          |         |       |        |              |         |        |
               P2      |  V   |    P1    |    A    |   Q   |   A    |   Q       Q  |    A    |   P2   |

Frame Blanking in this mode may be extended when the exposure time is longer than the frame time

Snapshot Mode - Exposure is triggered, when complete the frame is sent

    Exposure  __/ \__..._________________________________________________..._______________________________
    LED_Out   ____/        \_____________________________________________..._______________________________
    Frame     _______...____/                                                                         \____
                                        _________         ________                _________
    Line      _______...____|__________/         \_______/        \______..._____/         \________|______
                            |          |         |       |        |              |         |        |
                            |    P1    |    A    |   Q   |   A    |   Q       Q  |    A    |   P2   |
    V     = Vertical Blanking     = R06
    P1    = Frame Start Blanking  = R05 - 23
    A     = Active Data Time      = R04
    Q     = Horizontal Blanking   = R05
    P2    = Frame End Blanking    = 23 (fixed)

    R     = Rows

    A + Q = Row Time

    F     = Total Frame Time = V + R x ( A + Q )

Hardware Connection

The Arducam module used here has the following pinout

1 VCC POWER 3.3v Power supply
2 GND Ground Power ground
3 SCL Input Two-Wire Serial Interface Clock
4 SDA(SDATA) Bi-directional Two-Wire Serial Interface Data I/O
5 VS(VSYNC) Output Active High: Frame Valid; indicates active frame
6 HS(HREF) Output Active High: Line/Data Valid; indicates active pixels
7 PCLK Output Pixel Clock output from sensor
8 XCLK Input Master Clock into Sensor (13MHz - 27MHz)
9 D9 Output Pixel Data Output 9(MSB)
10 D8 Output Pixel Data Output 7(MSB)
11 D7 Output Pixel Data Output 7(MSB)
12 D6 Output Pixel Data Output 6
13 D5 Output Pixel Data Output 5
14 D4 Output Pixel Data Output 4
15 D3 Output Pixel Data Output 3
16 D2 Output Pixel Data Output 2
17 D1 Output Pixel Data Output 1
18 D0 Output Pixel Data Output 0 (LSB)
19 RST Input Sensor Reset
20 PDN(PWDN) Input Power Down
21 Trigger(EXP) Input External trigger Input
22 LED Output LED Control, Exposure indicator



The module is tested in the project camera_ic by connecting to a peer module camera_proxy which incorporates an i2c_slave module and can present as a partial standin for a real camera in simulation.

    camera_proxy #(
            .Width( CameraWidth ),
            .Height( CameraHeight )
        ) cam_proxy (
            .clock( clock ),
            .reset( reset ),

            .scl_in( scl ),
            .scl_out( camera_proxy_scl_out ),
            .sda_in( sda ),
            .sda_out( camera_proxy_sda_out ),

            .vs( vs ),
            .hs( hs ),
            .pclk( pclk ),
            .xclk( xclk ),
            .d( d ),

            .rst( rst ),
            .pwdn( pwdn ),

            .led( led ),
            .trigger( trigger )

When the test bed is run, the simulated camera (camera_proxy) gets configured, then sends appropriate data.