Skip to content

Instantly share code, notes, and snippets.

@darrena092
Created March 17, 2017 20:04
Show Gist options
  • Save darrena092/d46cf531d7bce093b1c6a3b58965c32d to your computer and use it in GitHub Desktop.
Save darrena092/d46cf531d7bce093b1c6a3b58965c32d to your computer and use it in GitHub Desktop.
module LCDFPGA(
clk, //Clock input (50MHz), rising edge.
LCD_RS, //Register select. 0 for command bytes. 1 for data bytes.
LCD_RW, //Read/Write. 0 to write. 1 to read.
LCD_E, //Enable. Active high.
LCD_DataBus //Data bus.
);
//Port declarations
input clk;
output LCD_RS, LCD_RW, LCD_E;
output [7:0] LCD_DataBus;
wire clk;
reg LCD_RS;
wire LCD_RW; //Wire as it will be permanently low. We're only ever writing.
reg LCD_E;
reg [7:0] LCD_DataBus;
//State definitions
parameter S_IDLE = 4'h0;
parameter S_WRITE = 4'h1;
parameter S_WAIT = 4'h2;
parameter S_HALT = 4'h3;
parameter P_IDLETIME = 16'h073A; //1,850 clocks (37us).
parameter P_WAITTIME = 16'h2710; //10,000 clocks (2ms).
parameter P_WRITETIME = 16'h003C; //60 clocks (1200ns).
//Internal Signals.
reg [15:0] counter = 8'b00000000; //Used for counting cycles.
reg [3:0] state = S_IDLE; //State register.
reg writeStarted = 1'b0;
reg [3:0] currentByteIndex = 4'b0;
//Data.
wire [7:0] testData [0:3]; //Has to be a wire as not synchronous.
wire testRS [0:3]; //RS setting for each byte.
//Initialisation bytes and an uppercase 'T' in ASCII encoding.
//(0x38 - Set to 8 bit mode)
//(0x0F - Initialize display (blinking cursor)).
//(0x01 - Clear screen) 2ms wait required after this.
//(0x54 - Uppercase 'T' ASCII).
assign {testData[0], testData[1], testData[2], testData[3]} = {8'h38, 8'h0F, 8'h01, 8'h54};
assign {testRS[0], testRS[1], testRS[2], testRS[3]} = {1'b0, 1'b0, 1'b0, 1'b1};
//Assignments.
assign LCD_RW = 1'b0; //Always low as we're always writing.
always @(posedge clk)
begin
//Increment the counter.
counter <= counter + 1;
//FSM which controls the LCD.
case(state)
//Stay idle for 10 clocks and keep the enable line low.
S_IDLE: begin
if(counter >= P_IDLETIME) begin
//Time has elapsed, go to next byte.
counter <= 0; //Zero the counter for the next step.
state <= S_WRITE;
end else begin
//Make sure that the LCD enable signal is low.
LCD_E <= 0;
LCD_RS <= 0;
LCD_DataBus <= 0;
state <= S_IDLE;
end
end
//Write a byte out to the display.
S_WRITE: begin
if(writeStarted == 0) begin
//We haven't started writing yet. Start now.
//Check we still have data to write.
if(currentByteIndex >= 4) begin
//Halt.
state <= S_HALT;
end else begin
LCD_E <= 1; //Enable the LCD.
LCD_DataBus <= testData[currentByteIndex];
LCD_RS <= testRS[currentByteIndex];
writeStarted <= 1;
state <= S_WRITE;
end
end else begin
//We've already written. Wait.
if(counter >= P_WRITETIME) begin
//Cycle is finished.
//Start by incrementing the current byte.
currentByteIndex <= currentByteIndex + 1;
writeStarted <= 0; //Reset write started.
//We need to wait longer when doing 0x01 (screen refresh).
if(LCD_DataBus == 8'h01) begin
counter <= 0; //Zero the counter for the next step.
state <= S_WAIT;
end else begin
counter <= 0; //Zero the counter for the next step.
state <= S_IDLE;
end
end else begin
//Just wait.
state <= S_WRITE;
end
end
end
//A state specifically for long waits where required.
S_WAIT: begin
if(counter >= P_WAITTIME) begin
//Wait is over. Go back to write.
state <= S_WRITE;
end else begin
//Still waiting...
//Make sure that the LCD enable signal is low.
LCD_E <= 0;
LCD_RS <= 0;
state <= S_WAIT;
end
end
//Halt after completion.
S_HALT: begin
LCD_DataBus <= 0;
LCD_RS <= 0;
LCD_E <= 0;
state <= S_HALT;
end
endcase
end
endmodule
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment