Skip to content

Commit

Permalink
Add support for ov5647 (only 640x480 right now)
Browse files Browse the repository at this point in the history
  • Loading branch information
sameer committed Apr 19, 2020
1 parent fed8e58 commit 3c8ec50
Show file tree
Hide file tree
Showing 3 changed files with 306 additions and 2 deletions.
3 changes: 2 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -22,4 +22,5 @@
These documents are not hosted here! They are available on Library Genesis and at other locations.

* [MIPI CCS v1.1](https://b-ok.cc/book/5437872/02a689)
* [IMX219 Datasheet](https://github.com/rellimmot/Sony-IMX219-Raspberry-Pi-V2-CMOS/blob/master/RASPBERRY%20PI%20CAMERA%20V2%20DATASHEET%20IMX219PQH5_7.0.0_Datasheet_XXX.PDF)
* [IMX219 Datasheet](https://github.com/rellimmot/Sony-IMX219-Raspberry-Pi-V2-CMOS/blob/master/RASPBERRY%20PI%20CAMERA%20V2%20DATASHEET%20IMX219PQH5_7.0.0_Datasheet_XXX.PDF)
* [OV5647 Datasheet](https://cdn.sparkfun.com/datasheets/Dev/RaspberryPi/ov5647_full.pdf)
3 changes: 2 additions & 1 deletion src/Manifest.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
files = [
"imx219.sv"
"imx219.sv",
"ov5647.sv"
]


Expand Down
302 changes: 302 additions & 0 deletions src/ov5647.sv
Original file line number Diff line number Diff line change
@@ -0,0 +1,302 @@
module ov5647 #(
parameter INPUT_CLK_RATE = 48000000,
parameter TARGET_SCL_RATE = 400000,
// Some ov5647 modules have a different address, change this if yours does
parameter ADDRESS = 8'h6c
) (
input logic clk_in,
inout wire scl,
inout wire sda,
// 0 = Power off
// 1 = Software standby
// 2 = Streaming
input logic [1:0] mode,

// 0 = 3280x2464
// 1 = 1920x1080
// 2 = 1640x1232
// 3 = 640x480
input logic [1:0] resolution,
// 0 = RAW8
// 1 = RAW10
input logic format,
// input logic horizontal_flip,
// input logic vertical_flip,
// input logic [7:0] analog_gain,
// input logic [15:0] digital_gain,
// input logic [15:0] exposure, // aka integration time

// Goes high when inputs match sensor state
// Changing inputs when the sensor isn't ready could put the sensor into an unexpected state
output logic ready,
output logic power_enable,
// ov5647 Model ID did not match
output logic model_err = 1'b0,
output logic nack_err = 1'b0
);

logic bus_clear;

logic transfer_start = 1'b0;
logic transfer_continues = 1'b0;
logic [7:0] address;
logic [7:0] data_tx = 8'd0;

logic transfer_ready;
logic interrupt;
logic transaction_complete;
logic nack;
logic [7:0] data_rx;
logic address_err;

i2c_master #(.INPUT_CLK_RATE(INPUT_CLK_RATE), .TARGET_SCL_RATE(TARGET_SCL_RATE)) i2c_master (
.scl(scl),
.clk_in(clk_in),
.bus_clear(bus_clear),
.sda(sda),
.address(address),
.transfer_start(transfer_start),
.transfer_continues(transfer_continues),
.data_tx(data_tx),
.transfer_ready(transfer_ready),
.interrupt(interrupt),
.transaction_complete(transaction_complete),
.nack(nack),
.data_rx(data_rx),
.address_err(address_err)
);

logic [15:0] MODEL_ID = 16'h5647;

logic [24:0] PRE_STANDBY [0:2];
assign PRE_STANDBY = '{
{1'b1, 16'h300a, MODEL_ID[15:8]}, // Read module_model_id high
{1'b1, 16'h300b, MODEL_ID[7:0]}, // Read module_model_id low
{1'b0, 16'h0100, 8'd0} // mode_select <= standby
};

logic [24:0] PRE_STREAM [0:88];
assign PRE_STREAM = '{
{1'b0, 16'h3034, 8'h08},
{1'b0, 16'h3035, 8'h41},
{1'b0, 16'h3036, 8'h46},
{1'b0, 16'h303c, 8'h11},
{1'b0, 16'h3106, 8'hf5},
{1'b0, 16'h3821, 8'h07},
{1'b0, 16'h3820, 8'h41},
{1'b0, 16'h3827, 8'hec},
{1'b0, 16'h370c, 8'h0f},
{1'b0, 16'h3612, 8'h59},
{1'b0, 16'h3618, 8'h00},
{1'b0, 16'h5000, 8'h06},
{1'b0, 16'h5001, 8'h00},
{1'b0, 16'h5002, 8'h40},
{1'b0, 16'h5003, 8'h08},
{1'b0, 16'h5a00, 8'h08},
{1'b0, 16'h3000, 8'h00},
{1'b0, 16'h3001, 8'h00},
{1'b0, 16'h3002, 8'h00},
{1'b0, 16'h3016, 8'h08},
{1'b0, 16'h3017, 8'he0},
{1'b0, 16'h3018, 8'h44},
{1'b0, 16'h301c, 8'hf8},
{1'b0, 16'h301d, 8'hf0},
{1'b0, 16'h3a18, 8'h00},
{1'b0, 16'h3a19, 8'hf8},
{1'b0, 16'h3c01, 8'h80},
{1'b0, 16'h3b07, 8'h0c},
{1'b0, 16'h380c, 8'h07},
{1'b0, 16'h380d, 8'h68},
{1'b0, 16'h380e, 8'h03},
{1'b0, 16'h380f, 8'hd8},
{1'b0, 16'h3814, 8'h31},
{1'b0, 16'h3815, 8'h31},
{1'b0, 16'h3708, 8'h64},
{1'b0, 16'h3709, 8'h52},
{1'b0, 16'h3808, 8'h02},
{1'b0, 16'h3809, 8'h80},
{1'b0, 16'h380a, 8'h01},
{1'b0, 16'h380b, 8'he0},
{1'b0, 16'h3800, 8'h00},
{1'b0, 16'h3801, 8'h00},
{1'b0, 16'h3802, 8'h00},
{1'b0, 16'h3803, 8'h00},
{1'b0, 16'h3804, 8'h0a},
{1'b0, 16'h3805, 8'h3f},
{1'b0, 16'h3806, 8'h07},
{1'b0, 16'h3807, 8'ha1},
{1'b0, 16'h3811, 8'h08},
{1'b0, 16'h3813, 8'h02},
{1'b0, 16'h3630, 8'h2e},
{1'b0, 16'h3632, 8'he2},
{1'b0, 16'h3633, 8'h23},
{1'b0, 16'h3634, 8'h44},
{1'b0, 16'h3636, 8'h06},
{1'b0, 16'h3620, 8'h64},
{1'b0, 16'h3621, 8'he0},
{1'b0, 16'h3600, 8'h37},
{1'b0, 16'h3704, 8'ha0},
{1'b0, 16'h3703, 8'h5a},
{1'b0, 16'h3715, 8'h78},
{1'b0, 16'h3717, 8'h01},
{1'b0, 16'h3731, 8'h02},
{1'b0, 16'h370b, 8'h60},
{1'b0, 16'h3705, 8'h1a},
{1'b0, 16'h3f05, 8'h02},
{1'b0, 16'h3f06, 8'h10},
{1'b0, 16'h3f01, 8'h0a},
{1'b0, 16'h3a08, 8'h01},
{1'b0, 16'h3a09, 8'h27},
{1'b0, 16'h3a0a, 8'h00},
{1'b0, 16'h3a0b, 8'hf6},
{1'b0, 16'h3a0d, 8'h04},
{1'b0, 16'h3a0e, 8'h03},
{1'b0, 16'h3a0f, 8'h58},
{1'b0, 16'h3a10, 8'h50},
{1'b0, 16'h3a1b, 8'h58},
{1'b0, 16'h3a1e, 8'h50},
{1'b0, 16'h3a11, 8'h60},
{1'b0, 16'h3a1f, 8'h28},
{1'b0, 16'h4001, 8'h02},
{1'b0, 16'h4004, 8'h02},
{1'b0, 16'h4000, 8'h09},
{1'b0, 16'h4837, 8'h24},
{1'b0, 16'h4050, 8'h6e},
{1'b0, 16'h4051, 8'h8f},
{1'b0, 16'h503d, 8'h00},
{1'b0, 16'h0100, 8'h01},
{1'b0, 16'h4800, 8'h04}
// {1'b0, 16'h4202, 8'h00},
// {1'b0, 16'h300d, 8'h00}
};

logic [24:0] POST_STREAM [0:0];
assign POST_STREAM = '{
{1'b0, 16'h0100, 8'h00} // Send to standby
// TODO: standby spinlock
};


// 0 = Off
// 1 = Pre-Standby
// 2 = Standby
// 3 = Pre-Stream
// 4 = Stream
// 5 = Modify Stream
// 6 = Post Stream (shutting down)
// 7 = Error
logic [2:0] sensor_state = 3'd0;

logic [7:0] rom_counter = 8'd0;
logic [1:0] byte_counter = 2'd0;

// Uninit, Standby, or Stream
assign ready = sensor_state == 3'd0 || sensor_state == 3'd2 || sensor_state == 3'd4;

assign power_enable = sensor_state != 3'd0;

logic [7:0] rom_end;
assign rom_end = sensor_state == 3'd1 ? 8'd2 : sensor_state == 3'd3 ? 8'd88 : sensor_state == 3'd6 ? 8'd0 : 8'd0;

logic [24:0] current_rom;
assign current_rom = sensor_state == 3'd1 ? PRE_STANDBY[rom_counter] : sensor_state == 3'd3 ? PRE_STREAM[rom_counter] : sensor_state == 3'd6 ? POST_STREAM[rom_counter] : 25'd0;

always @(posedge clk_in)
begin
case (sensor_state)
3'd0: begin
if (mode != 2'd0)
sensor_state <= 3'd1;
end
3'd1, 3'd3, 3'd6: begin
if (interrupt || transfer_ready)
begin
if (interrupt && (address_err || (!address[0] && nack))) // Catch write nacks
begin
transfer_start <= 1'b0;
transfer_continues <= 1'b0;
byte_counter <= 2'd0;
rom_counter <= 8'd0;
nack_err <= 1'd1;
sensor_state <= 3'd7;
end
else if (transfer_ready && byte_counter == 2'd0) // Write address MSB
begin
transfer_start <= 1'd1;
transfer_continues <= 1'd1;
address <= {ADDRESS[7:1], 1'b0};
data_tx <= current_rom[23:16];
byte_counter <= 2'd1;
end
else if (interrupt && byte_counter == 2'd1) // Write address LSB
begin
transfer_start <= 1'd0;
transfer_continues <= !current_rom[24];
data_tx <= current_rom[15:8];
byte_counter <= 2'd2;
end
else if (interrupt && byte_counter == 2'd2) // Write/Read register
begin
transfer_start <= current_rom[24];
transfer_continues <= 1'd0;
if (current_rom[24])
address <= {ADDRESS[7:1], 1'b1};
data_tx <= current_rom[7:0];
byte_counter <= 2'd3;
end
else if (interrupt && byte_counter == 2'd3) // Readback
begin
transfer_start <= 1'd0;
transfer_continues <= 1'd0;
byte_counter <= 2'd0;

if (current_rom[24] && current_rom[7:0] != data_rx) // Read did not match expected
begin
rom_counter <= 8'd0;
if (sensor_state == 3'd1) // was a model error
model_err <= 1'd1;
sensor_state <= 3'd7;
end
else if (rom_counter == rom_end) // This was the last operation
begin
rom_counter <= 8'd0;
if (sensor_state == 3'd5)
sensor_state <= 3'd4; // Modifications complete
else if (sensor_state == 3'd6)
sensor_state <= mode == 2'd1 ? 3'd2 : 3'd0; // Either go to standby or power off
else
sensor_state <= sensor_state + 1'd1; // Pre-standby and Pre-stream
end
else
rom_counter <= rom_counter + 1'd1;
end
end
end
3'd2: begin
if (mode == 2'd0)
sensor_state <= 3'd0;
else if (mode == 2'd2)
sensor_state <= 3'd3;
else
sensor_state <= 3'd2;
end
3'd4: begin
if (mode != 2'd2)
sensor_state <= 3'd6;
else
sensor_state <= 3'd4;
end
3'd5: begin // Not entered, modify support still WIP
end
3'd7: begin
if (mode == 2'd0)
begin
model_err <= 1'd0;
nack_err <= 1'd0;
sensor_state <= 3'd0;
end
end
endcase
end

endmodule

0 comments on commit 3c8ec50

Please sign in to comment.