diff --git a/Doc/OmniVision OV7670 Camera/OV7670_CAMERA_CHIP_ADVANCED.pdf b/Doc/OmniVision OV7670 Camera/OV7670_CAMERA_CHIP_ADVANCED.pdf new file mode 100644 index 0000000..72ed889 Binary files /dev/null and b/Doc/OmniVision OV7670 Camera/OV7670_CAMERA_CHIP_ADVANCED.pdf differ diff --git a/src/OV7670_Camera/I2C_Interface.v b/src/OV7670_Camera/I2C_Interface.v index e1ed1fb..55117af 100644 --- a/src/OV7670_Camera/I2C_Interface.v +++ b/src/OV7670_Camera/I2C_Interface.v @@ -9,11 +9,17 @@ // -------------------------------------------------------------------------------------- -module I2C_INTERFACE( +module I2C_INTERFACE +#( + parameter write_ID = 8'h42, + parameter read_ID = 8'h43 +) +( input wire GLOBAL_CLK , input wire START_TRANSFER , - input wire [7:0] SDATA , - inout reg SIOD , + input wire [7:0] SUBADDRESS , + input wire [7:0] VALUE , + inout wire SIOD , output reg SIOC , output reg READY ); @@ -21,52 +27,63 @@ module I2C_INTERFACE( reg transmitting = 1'b0; reg [5:0] counter = 6'b0; -reg [3:0] bits_sent = 4'b0; -reg [7:0] q_data; +reg [4:0] bits_sent = 4'b0; +reg [26:0] q_data; + +reg SIOD_temp; +assign SIOD = SIOD_temp; always @(*) begin if (!transmitting) begin READY = 1'b1; counter = 6'b0; - bits_sent = 4'b0; + bits_sent = 5'b0; SIOC = 1'b1; - SIOD = 1'bz; + SIOD_temp = 1'b1; if (START_TRANSFER) begin transmitting = 1'b1; - SIOD = 1'b0; - q_data = SDATA; + SIOD_temp = 1'b0; + q_data = {write_ID, 1'b0 ,SUBADDRESS, 1'b0 , VALUE , 1'b0}; READY = 1'b0; end end end +always @(posedge GLOBAL_CLK) +begin + if (transmitting) begin + if (counter == 6'b111111) + counter = 6'b0; + else + counter = counter + 1; + end +end -always @(negedge SIOC) +reg loaded = 1'b0; +always @(negedge GLOBAL_CLK) begin - if (bits_sent == 4'b1000) + if (bits_sent == 5'b11011 && SIOC == 1'b1) begin // 27 bits are all sent out through SIOD + SIOD_temp = 1'b1; transmitting = 1'b0; - READY = 1'b1; - else begin + end + else if (SIOC == 1'b0 && loaded == 1'b0) begin if (transmitting) begin - SIOD = q_data[0]; - q_data = q_data >> 1; + SIOD_temp = q_data[26]; + q_data = q_data << 1; bits_sent = bits_sent + 1; + loaded = 1'b1; end end end - -always @(posedge GLOBAL_CLK) +always @(posedge SIOC) begin - if (transmitting) begin - if (counter == 6'b111111) - counter = 6'b0; - else - counter = counter + 1; - end + loaded <= 1'b0; end + + // Create a 187.5 KHz SIOC serial clock in the following block always @(*) begin diff --git a/src/OV7670_Camera/camera_config.v b/src/OV7670_Camera/camera_config.v index 2dc6b8a..1d13b91 100644 --- a/src/OV7670_Camera/camera_config.v +++ b/src/OV7670_Camera/camera_config.v @@ -13,33 +13,43 @@ module CAMERA_CONFIG( input wire NEXT , output reg [7:0] CTRL_ADDR , // specify the control register address in the camera chip output reg [7:0] CTRL_VALUE , // specify the control value in the camera chip + output reg READY , // specify whether the CURRENT control value is ready or not + output reg FINISHED // specify whether the ENTIRE configuration if finished or not ); reg [7:0] address; always @(posedge NEXT) begin - if (START_CONFIG == 1'b1) + + READY = 1'b0; + + if (START_CONFIG == 1'b1) begin address = 8'b0; - else + FINISHED = 1'b0; + end + else if (!FINISHED) begin address = address + 1; + end case (address) 0: begin - CTRL_ADDR <= 8'h12; - CTRL_VALUE <= 8'h80; + CTRL_ADDR = 8'h12; + CTRL_VALUE = 8'h80; end - /* More Control Features can be added here if wanted */ + /* More Control Commands can be added here if wanted */ default: begin - CTRL_ADDR <= 8'hFF - CTRL_VALUE <= 8'hFF + CTRL_ADDR = 8'hFF; + CTRL_VALUE = 8'hFF; + FINISHED = 1'b1; end + endcase - + READY = 1'b1; end diff --git a/src/OV7670_Camera/camera_controller.v b/src/OV7670_Camera/camera_controller.v index b6e8636..510c52c 100644 --- a/src/OV7670_Camera/camera_controller.v +++ b/src/OV7670_Camera/camera_controller.v @@ -12,11 +12,9 @@ module CAMERA_CONTROLLER( input wire GLOBAL_CLK , input wire RESET , - input wire VSYNC , - input wire HREF , - input wire PCLK , inout wire SIOD , // Serial Data output wire SIOC , // Serial Clock + output wire CONFIG_FINISHED ); @@ -26,44 +24,67 @@ wire [7:0] ctrl_addr; wire [7:0] ctrl_value; reg next; reg start_config; -CAMERA_CONFIG cfg_inst -( +wire config_ready; +wire CONFIG_FINISHED; +CAMERA_CONFIG cfg_inst( .START_CONFIG(start_config), .NEXT(next), .CTRL_ADDR(ctrl_addr), - .CTRL_VALUE(ctrl_value) + .CTRL_VALUE(ctrl_value), + .READY(config_ready), + .FINISHED(CONFIG_FINISHED) ); + + reg start_transfer; -wire ready; -I2C_INTERFACE i2c_inst -( +wire I2C_ready; +I2C_INTERFACE i2c_inst( .GLOBAL_CLK(GLOBAL_CLK), .START_TRANSFER(start_transfer), - .SDATA(), + .SUBADDRESS(ctrl_addr), + .VALUE(ctrl_value), .SIOD(SIOD), .SIOC(SIOC), - .READY(ready) + .READY(I2C_ready) ); + + reg resetting = 1'b0; always @(*) begin next = 1'b0; - if (!resetting and RESET) + start_config = 1'b0; + start_transfer = 1'b0; + if (!resetting && RESET) begin start_config = 1'b1; - start_transfer = 1'b1; + next = 1'b1; resetting = 1'b1; - else if (resetting) - start_config = 1'b0; - start_transfer = 1'b0; + end end -always @(posedge ready) +always @(posedge config_ready) begin - + start_config <= 1'b0; + next <= 1'b0; + start_transfer <= 1'b1; end +always @(negedge I2C_ready) +begin + start_transfer = 1'b0; +end + +always @(posedge I2C_ready) +begin + if (!CONFIG_FINISHED) begin + next <= 1'b1; + end +end + + + endmodule \ No newline at end of file diff --git a/src/OV7670_Camera/camera_image.v b/src/OV7670_Camera/camera_image.v new file mode 100644 index 0000000..e419ed6 --- /dev/null +++ b/src/OV7670_Camera/camera_image.v @@ -0,0 +1,37 @@ +// -------------------------------------------------------------------------------------- +// Organization: CALPLUG-FPGA +// Project Name: +// Date: Winter 2019 +// FPGA Board: iCE40 UltraPlus SG48I +// -------------------------------------------------------------------------------------- +// File Name: camera_image.v +// File Description: This is the camera module that implements the algorithm for capturing image from the camera +// -------------------------------------------------------------------------------------- + +module CAMERA_IMAGE +#( + parameter RESOLUTION_W = 640; + parameter RESOLUTION_H = 480; +) +( + input wire [7:0] PIXEL , // 8 bits pixel data + input wire VSYNC , // if VSYNC is high, that means all ENTIRE image pixels have been captured + input wire HREF , // if HREF is high, that means ONE SEPARATE ROW of the image pixels have been captured + input wire PCLK +); + +wire capturing_image = !VSYNC; + + + + + + + + + + + + + +endmodule; \ No newline at end of file diff --git a/src/baudgen.vh b/src/baudgen.vh deleted file mode 100644 index 0cb4191..0000000 --- a/src/baudgen.vh +++ /dev/null @@ -1,26 +0,0 @@ -//----------------------------------------------------------------------------- -//-- Constants for the serial asinchronous communication modules -//------------------------------------------------------------------------------ -//-- (C) BQ. December 2015. Written by Juan Gonzalez (Obijuan) -//------------------------------------------------------------------------------ -// These constans have been calculated for the ICESTICK board which have -// a 12MHz clock -// -//-- The calculation for the icestick board is: -//-- Divisor = 12000000 / BAUDRATE (and the result is rounded to an integer number) -//-------------------------------------------------------------------------------- -//-- The python3 script: baudgen.py contains the function for generating this table -//----------------------------------------------------------------------------------- - -//-- Constants for obtaining standard BAURATES: -`define B115200 104 -`define B57600 208 -`define B38400 313 - -`define B19200 625 -`define B9600 1250 -`define B4800 2500 -`define B2400 5000 -`define B1200 10000 -`define B600 20000 -`define B300 40000 diff --git a/src/baudgen_tx.v b/src/baudgen_tx.v deleted file mode 100644 index e47ac96..0000000 --- a/src/baudgen_tx.v +++ /dev/null @@ -1,64 +0,0 @@ -//----------------------------------------------------------------------------- -//-- Baudrate generator -//-- It generates a square signal, with a frequency for communicating at the given -//-- given baudrate -//-- The output is set to 1 only during one clock cycle. The rest of the time is 0 -//-------------------------------------------------------------------------------- -//-- (c) BQ. December 2015. written by Juan Gonzalez (obijuan) -//----------------------------------------------------------------------------- -//-- GPL license -//----------------------------------------------------------------------------- -`default_nettype none -`include "baudgen.vh" - -//---------------------------------------------------------------------------------------- -//-- baudgen module -//-- -//-- INPUTS: -//-- -clk: System clock (12 MHZ in the iceStick board) -//-- -clk_ena: clock enable: -//-- 1. Normal working: The squeare signal is generated -//-- 0: stoped. Output always 0 -//-- OUTPUTS: -//-- - clk_out: Output signal. Pulse width: 1 clock cycle. Output not registered -//-- It tells the uart_tx when to transmit the next bit -//-- __ __ -//-- __| |________________________________________________________| |________________ -//-- -> <- 1 clock cycle -//-- -//--------------------------------------------------------------------------------------- -module baudgen_tx #( - parameter BAUDRATE = `B115200 //-- Default baudrate -)( - input wire rstn, //-- Reset (active low) - input wire clk, //-- System clock - input wire clk_ena, //-- Clock enable - output wire clk_out //-- Bitrate Clock output -); - -//-- Number of bits needed for storing the baudrate divisor -localparam N = $clog2(BAUDRATE); - -//-- Counter for implementing the divisor (it is a BAUDRATE module counter) -//-- (when BAUDRATE is reached, it start again from 0) -reg [N-1:0] divcounter = 0; - -always @(posedge clk) - - if (!rstn) - divcounter <= 0; - - else if (clk_ena) - //-- Normal working: counting. When the maximum count is reached, it starts from 0 - divcounter <= (divcounter == BAUDRATE - 1) ? 0 : divcounter + 1; - else - //-- Counter fixed to its maximum value - //-- When it is resumed it start from 0 - divcounter <= BAUDRATE - 1; - -//-- The output is 1 when the counter is 0, if clk_ena is active -//-- It is 1 only for one system clock cycle -assign clk_out = (divcounter == 0) ? clk_ena : 0; - - -endmodule diff --git a/src/top.v b/src/top.v index c141f2d..26cb193 100644 --- a/src/top.v +++ b/src/top.v @@ -13,7 +13,10 @@ // /* arachne-pnr -d 5k -p ice40_top.pcf -o top.txt top.blif */ // -------------------------------------------------------------------------------------- -`include "baudgen.vh" +`include "OV7670_Camera/camera_config.v" +`include "OV7670_Camera/I2C_Interface.v" +`include "OV7670_Camera/camera_controller.v" + module top( @@ -32,7 +35,9 @@ wire hfosc_clk; // hfosc_clk frequency = 48 MHz if CLKHF_DIV = "0b00" // 24 MHz if CLKHF_DIV = "0b01" // 12 MHz if CLKHF_DIV = "0b10" -// 6 MHz if CLKHF_DIV = "0b11" +// 6 MHz if CLKHF_DIV = "0b11" +// WARNING: Other clock in the modules has dependency on the hfosc_clk. +// WARNING: BE CAREFUL if attempt to change the hfosc_clk. SB_HFOSC #( .CLKHF_DIV("0b10") @@ -44,7 +49,6 @@ inthosc .CLKHF(hfosc_clk) ); - wire global_hfosc_clk; SB_GB gbu_hfosc( .USER_SIGNAL_TO_GLOBAL_BUFFER(hfosc_clk), @@ -53,6 +57,8 @@ SB_GB gbu_hfosc( + + wire output_clk_global; wire output_clk_core; wire pll_locked; @@ -69,17 +75,26 @@ SB_PLL40_CORE #( .FILTER_RANGE(3'b010) ) pll ( .REFERENCECLK(global_hfosc_clk), - .PLLOUTGLOBAL(output_clk_global), - .PLLOUTCORE(output_clk_core), + .PLLOUTGLOBAL(global_clk), + .PLLOUTCORE(core_clk), .LOCK(pll_locked), .BYPASS(1'b0), .RESETB(1'b1) ); +assign XCLK = global_clk; -CAMERA_CONTROLLER cam -( -) +wire reset_cam = 1'b0; +wire config_finished; +CAMERA_CONTROLLER cam( + .GLOBAL_CLK(global_clk), + .RESET(reset_cam), + .SIOD(SIOD), + .SIOC(SIOC), + .CONFIG_FINISHED(config_finished) +); + + endmodule diff --git a/src/uart_tx.v b/src/uart_tx.v deleted file mode 100644 index 779642d..0000000 --- a/src/uart_tx.v +++ /dev/null @@ -1,167 +0,0 @@ -//---------------------------------------------------------------------------- -//-- Asynchronous serial transmitter Unit -//------------------------------------------ -//-- (C) BQ. December 2015. Written by Juan Gonzalez (Obijuan) -//-- GPL license -//---------------------------------------------------------------------------- -//-- Tested at all the standard baudrates: -//-- 300, 600, 1200, 2400, 4800, 9600, 19200, 38400, 57600, 115200 -//---------------------------------------------------------------------------- -//-- Although this transmitter has been written from the scratch, it has been -//-- inspired by the one developed in the swapforth proyect by James Bowman -//-- -//-- https://github.com/jamesbowman/swapforth -//-- -//---------------------------------------------------------------------------- -`default_nettype none - -`include "baudgen.vh" - -//--- Serial transmitter unit module -//--- TX output is not registered -module uart_tx #( - parameter BAUDRATE = `B115200 //-- Default baudrate -)( - input wire clk, //-- System clcok (12MHz in the ICEstick) - input wire rstn, //-- Reset (Active low) - input wire start, //-- Set to 1 for starting the transmission - input wire [7:0] data, //-- Byte to transmit - output reg tx, //-- Serial data output - output reg ready //-- Transmitter ready (1) / busy (0) - -); - - -//-- Transmission clock -wire clk_baud; - -//-- Bitcounter -reg [3:0] bitc; - -//-- Registered data -reg [7:0] data_r; - -//--------- control signals -reg load; //-- Load the shifter register / reset -reg baud_en; //-- Enable the baud generator - -//------------------------------------- -//-- DATAPATH -//------------------------------------- - -//-- Register the input data -always @(posedge clk) - if (start == 1 && state == IDLE) - data_r <= data; - -//-- 1 bit start + 8 bits datos + 1 bit stop -//-- Shifter register. It stored the frame to transmit: -//-- 1 start bit + 8 data bits + 1 stop bit -reg [9:0] shifter; - -//-- When the control signal load is 1, the frame is loaded -//-- when load = 0, the frame is shifted right to send 1 bit, -//-- at the baudrate determined by clk_baud -//-- 1s are introduced by the left -always @(posedge clk) - //-- Reset - if (rstn == 0) - shifter <= 10'b11_1111_1111; - - //-- Load mode - else if (load == 1) - shifter <= {data_r,2'b01}; - - //-- Shift mode - else if (load == 0 && clk_baud == 1) - shifter <= {1'b1, shifter[9:1]}; - -//-- Sent bit counter -//-- When load (=1) the counter is reset -//-- When load = 0, the sent bits are counted (with the raising edge of clk_baud) -always @(posedge clk) - if (!rstn) - bitc <= 0; - - else if (load == 1) - bitc <= 0; - else if (load == 0 && clk_baud == 1) - bitc <= bitc + 1; - -//-- The less significant bit is transmited through tx -//-- It is a registed output, because tx is connected to an Asynchronous bus -//-- and the glitches should be avoided -always @(posedge clk) - tx <= shifter[0]; - -//-- Baud generator -baudgen_tx #( .BAUDRATE(BAUDRATE)) -BAUD0 ( - .rstn(rstn), - .clk(clk), - .clk_ena(baud_en), - .clk_out(clk_baud) - ); - -//------------------------------ -//-- CONTROLLER -//------------------------------ - -//-- fsm states -localparam IDLE = 0; //-- Idle state -localparam START = 1; //-- Start transmission -localparam TRANS = 2; //-- Transmitting data - -//-- Registers for storing the states -reg [1:0] state; -reg [1:0] next_state; - -//-- Transition between states -always @(posedge clk) - if (!rstn) - state <= IDLE; - else - state <= next_state; - -//-- Control signal generation and next states -always @(*) begin - - //-- Default values - next_state = state; //-- Stay in the same state by default - load = 0; - baud_en = 0; - - case (state) - - //-- Idle state - //-- Remain in this state until start is 1 - IDLE: begin - ready = 1; - if (start == 1) - next_state = START; - end - - //-- 1 cycle long - //-- turn on the baudrate generator and the load the shift register - START: begin - load = 1; - baud_en = 1; - ready = 0; - next_state = TRANS; - end - - //-- Stay here until all the bits have been sent - TRANS: begin - baud_en = 1; - ready = 0; - if (bitc == 11) - next_state = IDLE; - end - - default: - ready = 0; - - endcase -end - -endmodule