I2C LCD String Module
// LCD 문자열 출력
module i2c_lcd_string (
input clk, reset_p,
input lcd_start,
input lcd_row,
input [8*16-1:0] lcd_txt,
output scl, sda,
output reg lcd_init_flag,
output busy,
output [15:0] led
);
integer cnt_sysclk;
reg cnt_sysclk_e;
// System Clock Counter
always @(negedge clk, posedge reset_p) begin
if (reset_p) cnt_sysclk <= 0;
else if (cnt_sysclk_e) cnt_sysclk <= cnt_sysclk + 1;
else cnt_sysclk <= 0;
end
// Edge Detection of LCD Start-bit
wire start_pedge, start_nedge;
edge_detector_pos start_ed (clk, reset_p, lcd_start, start_pedge, start_nedge);
reg [7:0] send_buffer;
reg send, rs;
// Using Module, Byte Unit Transmission
i2c_lcd_send_byte send_byte (clk, reset_p, 7'h27, send_buffer,
send, rs, scl, sda, busy);
localparam LCD_IDLE = 4'b0001;
localparam LCD_INIT = 4'b0010;
localparam MOVE_CURSOR = 4'b0100;
localparam SEND_STRING = 4'b1000;
// Change State in Negative Edge
reg [3:0] state, next_state;
always @(negedge clk, posedge reset_p) begin
if (reset_p) state <= LCD_IDLE;
else state <= next_state;
end
assign led[3:0] = state;
assign led[4] = lcd_init_flag;
reg [4:0] cnt_data;
always @(posedge clk, posedge reset_p) begin
if (reset_p) begin
next_state <= LCD_IDLE;
lcd_init_flag <= 0;
send_buffer <= 0;
send <= 0;
rs <= 0;
cnt_data <= 0;
end
else begin
case (state)
LCD_IDLE : begin
if (lcd_init_flag) begin
if (start_pedge) next_state <= MOVE_CURSOR;
end
else begin
if (cnt_sysclk < 32'd80_000_00) begin // Wait 80ms
cnt_sysclk_e <= 1; // System Clock Count Start
end
else begin
next_state <= LCD_INIT; // Change State I2C_INIT
cnt_sysclk_e <= 0; // System Clock Count Stop, Clear
end
end
end
LCD_INIT : begin
if (busy) begin // Communicating
send <= 0; // Wait Transmission
if (cnt_data >= 6) begin
cnt_data <= 0; // LCD Initialization 6-Step Complete
next_state <= LCD_IDLE; // Change State I2C_IDLE
lcd_init_flag <= 1;
end
end
else if (!send) begin
case (cnt_data) // Step-by-Step Command Transmission
0 : send_buffer <= 8'h33; // Function Set & Function Set
1 : send_buffer <= 8'h32; // Function Set & Return Home
2 : send_buffer <= 8'h28; // Function Set Data 4-bit, 2-Lines, 5×8 Dots
3 : send_buffer <= 8'h0C; // Display On, Cursor Off, Blinking Cursor Off
4 : send_buffer <= 8'h01; // Clear Display
5 : send_buffer <= 8'h06; // Entry Mode Cursor Move Increment
endcase
send <= 1; // Request Transmission
cnt_data <= cnt_data + 1; // Step Change
end
end
MOVE_CURSOR : begin
if (busy) begin // Communicating
send <= 0; // Wait Transmission
next_state <= SEND_STRING;
end
else if (!send) begin
rs <= 0; // Instruction Register Select
send_buffer <= {1'b1, lcd_row, 6'b00_0000}; // 저장 위치 설정
send <= 1; // Request Transmission
end
end
SEND_STRING : begin
if (busy) begin
send <= 0;
if (cnt_data >= 16) begin
cnt_data <= 0;
next_state <= LCD_IDLE;
end
end
else if (!send) begin
rs <= 1; // Data Register Select
send_buffer <= lcd_txt[8*(16-cnt_data)-1 -: 8]; // 왼쪽 글자부터 차례로 전송
send <= 1;
cnt_data <= cnt_data + 1;
end
end
endcase
end
end
endmodule
I2C LCD Send Byte Module
// Sending to LCD Using I2C Communication in Nibble(4-bit) Unit
module i2c_lcd_send_byte (
input clk, reset_p,
input [6:0] addr, // Slave Address
input [7:0] send_buffer, // Transmission Data Buffer
input send, rs, // Send Start-bit, Register Select
output scl, sda, // Serial Clock , Serial Data
output reg busy, // Communication Situation
output [15:0] led // for Debugging
);
// Change State Using Shift
localparam I2C_IDLE = 6'b00_0001; // Standby State
localparam SEND_HIGH_NIBBLE_DISABLE = 6'b00_0010; // High Nibble(4-bit) Transmit in Enable Clear
localparam SEND_HIGH_NIBBLE_ENABLE = 6'b00_0100; // Enable Set
localparam SEND_LOW_NIBBLE_DISABLE = 6'b00_1000; // Low Nibble(4-bit) Transmit in Enable Clear
localparam SEND_LOW_NIBBLE_ENABLE = 6'b01_0000; // Enable Set
localparam SEND_DISABLE = 6'b10_0000; // Byte(8-bit) Transmission Complete
reg [7:0] data; // 4-bit Unit Transmission, D7 ~ D4, BL, E, RW, RS
reg comm_start; // Communication Start-bit
// Clock Divide 100, 10ns x 100 = 1us
wire clk_usec_nedge, clk_usec_pedge; // Divide Clock 1us
clock_div_100 us_clk (.clk(clk), .reset_p(reset_p),
.nedge_div_100(clk_usec_nedge), .pedge_div_100(clk_usec_pedge));
// Edge Detection of Send Signal
wire send_pedge, send_nedge;
edge_detector_pos comm_start_ed (.clk(clk), .reset_p(reset_p),
.cp(send), .p_edge(send_pedge), .n_edge(send_nedge));
// us Unit Count
reg [21:0] cnt_usec; // us Count
reg cnt_usec_e; // us Count Enable
always @(negedge clk, posedge reset_p) begin
if (reset_p) cnt_usec = 0; // Count Clear
else if (clk_usec_nedge && cnt_usec_e) begin // Count Start when Enable & us Negative Edge
cnt_usec = cnt_usec + 1; // Count During Enable
end
else if (!cnt_usec_e) cnt_usec = 0; // Count Clear when Disable
end
// Using Module, Address & Data Transmission, I2C Communication
i2c_master master (clk, reset_p, addr, data, 1'b0, comm_start, scl, sda);
reg [5:0] state, next_state; // Current & Next State
always @(negedge clk, posedge reset_p) begin
if (reset_p) state = I2C_IDLE; // Basic Standby
else state = next_state; // Change State in Negative Edge
end
always @(posedge clk, posedge reset_p) begin
if (reset_p) begin
next_state = I2C_IDLE; // Basic Standby
comm_start = 0; // Communication Disable
cnt_usec_e = 0; // us Count Disable, Clear
data = 0; // Transmission Data Reset
busy = 0; // Communication Available
end
else begin
case (state)
I2C_IDLE : begin // Standby State
if (send_pedge) begin // Send Start
next_state = SEND_HIGH_NIBBLE_DISABLE; // Change State SEND_HIGH_NIBBLE_DISABLE
busy = 1; // Communicating
end
end
SEND_HIGH_NIBBLE_DISABLE : begin // High Nibble(4-bit) Transmit in Enable Clear
if (cnt_usec < 22'd200) begin // About 200us Required to Complete Transmission
data = {send_buffer[7:4], 3'b100, rs}; // High Nibble Transmission
comm_start = 1; // Communication Start
cnt_usec_e = 1; // us Count Enable
end
else begin
cnt_usec_e = 0; // us Count Disable, Clear
comm_start = 0; // Communication Start-bit Clear
next_state = SEND_HIGH_NIBBLE_ENABLE; // Change State SEND_HIGH_NIBBLE_ENABLE
end
end
SEND_HIGH_NIBBLE_ENABLE : begin // Enable Set
if (cnt_usec < 22'd200) begin // About 200us Required to Complete Transmission
data = {send_buffer[7:4], 3'b110, rs}; // E Pin Enable
comm_start = 1; // Communication Start
cnt_usec_e = 1; // us Count Enable
end
else begin
cnt_usec_e = 0; // us Count Disable, Clear
comm_start = 0; // Communication Start-bit Clear
next_state = SEND_LOW_NIBBLE_DISABLE; // Change State SEND_LOW_NIBBLE_DISABLE
end
end
SEND_LOW_NIBBLE_DISABLE : begin // Low Nibble(4-bit) Transmit in Enable Clear
if (cnt_usec < 22'd200) begin // About 200us Required to Complete Transmission
data = {send_buffer[3:0], 3'b100, rs}; // Low Nibble Transmission
comm_start = 1; // Communication Start
cnt_usec_e = 1; // us Count Enable
end
else begin
cnt_usec_e = 0; // us Count Disable, Clear
comm_start = 0; // Communication Start-bit Clear
next_state = SEND_LOW_NIBBLE_ENABLE; // Change State SEND_LOW_NIBBLE_ENABLE
end
end
SEND_LOW_NIBBLE_ENABLE : begin // Enable Set
if (cnt_usec < 22'd200) begin // About 200us Required to Complete Transmission
data = {send_buffer[3:0], 3'b110, rs}; // E Pin Enable
comm_start = 1; // Communication Start
cnt_usec_e = 1; // us Count Enable
end
else begin
cnt_usec_e = 0; // us Count Disable, Clear
comm_start = 0; // Communication Start-bit Clear
next_state = SEND_DISABLE; // Change State SEND_DISABLE
end
end
SEND_DISABLE : begin // Byte(8-bit) Transmission Complete
if (cnt_usec < 22'd200) begin // About 200us Required to Complete Transmission
data = {send_buffer[7:4], 3'b100, rs}; // E Pin Disable
comm_start = 1; // Communication Start
cnt_usec_e = 1; // us Count Enable
end
else begin
cnt_usec_e = 0; // us Count Disable, Clear
comm_start = 0; // Communication Start-bit Clear
next_state = I2C_IDLE; // Change State I2C_IDLE
busy = 0; // Communication Available
end
end
default : begin
cnt_usec_e = 0; // us Count Disable, Clear
comm_start = 0; // Communication Start-bit Clear
next_state = I2C_IDLE; // Basic Standby
busy = 0; // Communication Available
end
endcase
end
end
endmodule
I2C LCD Master Module
// Input Address or Data, 100㎑ I2C Communication When Start-bit High
module i2c_master (
input clk, reset_p,
input [6:0] addr, // Slave Address
input [7:0] data, // Transmission Data
input rd_wr, comm_start, // Read & Write Select-bit, Communication Start-bit
output reg scl, sda, // Serial Clock , Serial Data
// Originally SDA Input + Output, but Here only Output
output [15:0] led // for Debugging
);
// Change State Using Shift
localparam I2C_IDLE = 7'b000_0001; // Standby State
localparam COMM_START = 7'b000_0010; // Communication Start
localparam SEND_ADDR = 7'b000_0100; // Address Transmission
localparam READ_ACK = 7'b000_1000; // Read ACK-bit, Assume Read
localparam SEND_DATA = 7'b001_0000; // Data Transmission
localparam SCL_STOP = 7'b010_0000; // Stop Generate Serial Clock
localparam COMM_STOP = 7'b100_0000; // Communication Stop
// Clock Divide 100, 10ns x 100 = 1us
wire clk_usec_nedge, clk_usec_pedge; // Divide Clock 1us
clock_div_100 us_clk (.clk(clk), .reset_p(reset_p),
.nedge_div_100(clk_usec_nedge), .pedge_div_100(clk_usec_pedge));
// Edge Detection of Command Signal
wire comm_start_pedge, comm_start_nedge;
edge_detector_pos comm_start_ed (.clk(clk), .reset_p(reset_p),
.cp(comm_start), .p_edge(comm_start_pedge), .n_edge(comm_start_nedge));
// Edge Detection of SCL Signal
wire scl_pedge, scl_nedge;
edge_detector_pos scl_ed (.clk(clk), .reset_p(reset_p),
.cp(scl), .p_edge(scl_pedge), .n_edge(scl_nedge));
reg [2:0] cnt_usec5; // 5us Count
reg scl_e; // SCL Enable
always @(posedge clk, posedge reset_p) begin
if (reset_p) begin
cnt_usec5 = 0; // 5us Count Reset
scl = 1; // SCL Reset, Pull-Up
end
else if (scl_e) begin // when SCL Enable
if (clk_usec_nedge) begin // when us Negative Edge
if (cnt_usec5 >= 4) begin // Every 5us
cnt_usec5 = 0; // 5us Count Reset
scl = ~scl; // Generate Serial Clock
end
else begin
cnt_usec5 = cnt_usec5 + 1; // Count During Enable
end
end
end
else if (!scl_e) begin // SCL Disable
cnt_usec5 = 0; // 5us Count Reset
scl = 1; // SCL Reset, Pull-Up
end
end
reg [6:0] state, next_state; // Current & Next State
always @(negedge clk, posedge reset_p) begin
if (reset_p) state = I2C_IDLE; // Basic Standby
else state = next_state; // Change State in Negative Edge
end
wire [7:0] addr_rd_wr;
assign addr_rd_wr = {addr, rd_wr}; // Address + RW Select-bit
reg [2:0] cnt_bit; // Count for Transmit 1-bit at a Time
reg stop_flag; // Check Data Transmission
always @(posedge clk, posedge reset_p) begin
if (reset_p) begin
next_state = I2C_IDLE; // Basic Standby
scl_e = 0; // SCL Disable
sda = 1; // SDA Reset, Pull-Up
cnt_bit = 7; // bit Count Reset, from Upper bit
stop_flag = 0; // Before Data Transmission
end
else begin
case (state)
I2C_IDLE : begin // Standby State
scl_e = 0; // SCL Disable
sda = 1; // SDA Reset, Pull-Up
if (comm_start_pedge) begin // Communication Start
next_state = COMM_START; // Change State COMM_START
end
end
COMM_START : begin // Communication Start
sda = 0; // SDA Start
scl_e = 1; // SCL Enable
next_state = SEND_ADDR; // Change State SEND_ADDR
end
SEND_ADDR : begin // Address Transmission
if (scl_nedge) begin // when SCL Negative Edge
sda = addr_rd_wr[cnt_bit]; // Enter Address into SDA
end
if (scl_pedge) begin // when SCL Positive Edge
if (cnt_bit == 0) begin // Read to LSB
cnt_bit = 7; // bit Count Reset
next_state = READ_ACK; // Change State READ_ACK
end
else begin
cnt_bit = cnt_bit - 1; // Read from MSB
end
end
end
READ_ACK : begin // Read ACK-bit, Assume Read
if (scl_nedge) begin // when SCL Negative Edge
sda = 'bz; // Enter Inpedance Value into SDA, Disconnect
end
else if (scl_pedge) begin // when SCL Positive Edge
if (stop_flag) begin // Complete Data Transmission
stop_flag = 0; // Data Transmission Flag Clear
next_state = SCL_STOP; // Change State SCL_STOP
end
else begin // Before Data Transmission
stop_flag = 1; // Data Transmission Flag Set
next_state = SEND_DATA; // Change State SEND_DATA
end
end
end
SEND_DATA : begin // Data Transmission
if (scl_nedge) begin // when SCL Negative Edge
sda = data[cnt_bit]; // Enter Data into SDA
end
if (scl_pedge) begin // when SCL Positive Edge
if (cnt_bit == 0) begin // Read to LSB
cnt_bit = 7; // bit Count Reset
next_state = READ_ACK; // Change State READ_ACK
end
else begin
cnt_bit = cnt_bit - 1; // Read from MSB
end
end
end
SCL_STOP : begin // Stop Generate Serial Clock
if (scl_nedge) sda = 0; // SDA Low when SCL Negative Edge
if (scl_pedge) next_state = COMM_STOP; // Change State COMM_STOP when SCL Positive Edge
end
COMM_STOP : begin // Communication Stop
if (cnt_usec5 >= 3) begin // Wait 4us
scl_e = 0; // SCL Disable
sda = 1; // SDA Stop
next_state = I2C_IDLE; // Change State I2C_IDLE
end
end
default : begin
scl_e = 0; // SCL Disable
sda = 1; // SDA Reset, Pull-Up
next_state = I2C_IDLE; // Basic Standby
end
endcase
end
end
endmodule