Sử dụng LCD 16x2 trên FPGA DE2 board

/////////////////////////////////////////////////////////////////

// Author:   Duong Computing (YouTube.com/DuongComputing)      //

// Email:    duong.uitce@gmail.com                             //

// Website:  https://sites.google.com/view/duongcomputing      //

// File:     LCD.v                                             //

// Function: Controlling LCD 16x2 on Altera DE2/DE2-115 Board  //

// Date:     Nov 11, 2024                                      //

/////////////////////////////////////////////////////////////////


`define WriteInstruction(ctr_en, ctr_rw, data, source) \

             data <= source;                           \

             ctr_en <= 1'b1;                           \

             ctr_rw <= 1'b0;


`define WriteData(ctr_en, ctr_rw, data, source) \

             data <= source;                    \

             ctr_en <= 1'b1;                    \

             ctr_rw <= 1'b1;


module LCD (

    input            CLOCK_50,

    input wire [0:0] KEY,

    output reg [7:0] LCD_DATA,

    output wire      LCD_RW,

    output reg       LCD_EN,

    output reg       LCD_RS,

    output wire      LCD_ON

);

    //wire clock = CLOCK_50;

    wire reset = KEY[0];

 

// State

    localparam [3:0] INIT             = 4'd0, // Init, need to 8 cycles to init

                     INIT_MAINTAIN    = 4'd1,

                     HOME             = 4'd2, // Move to the start point of line 1

                     HOME_MAINTAIN    = 4'd3,

                     WRDATA1          = 4'd4, // Write data to line 1, write 16 times

                     WRDATA1_MAINTAIN = 4'd5,

                     LINE2            = 4'd6, // Move to the start point of line 2

                     LINE2_MAINTAIN   = 4'd7,

                     WRDATA2          = 4'd8, // Write data to line 2, write 16 times

                     WRDATA2_MAINTAIN = 4'd9,

                     DONE             = 4'd10;


    reg [3:0] state;


    integer   count;

    integer   clockticks;

    reg       clock;

    

    localparam integer max = 50000; 

    localparam integer half = max / 2;

 

    // Clock Divider

    always @(posedge CLOCK_50) begin

        if (clockticks < max)

            clockticks <= clockticks + 1;

        else

            clockticks <= 0;


        if (clockticks < half)

            clock <= 0;

        else

            clock <= 1;

    end 


    wire [7:0] initcode [0:7];

    wire [7:0] line1    [0:15];

    wire [7:0] line2    [0:15];

    

    // Initialization procedure based on Manufacturer's datasheet

    // Reference: https://www.sparkfun.com/datasheets/LCD/HD44780.pdf, page 45

    assign initcode[0] = 8'h38; // Step 1, 2, 3, 4: 8 bit data, 2 lines, 5x8 character size (4 times)

    assign initcode[1] = 8'h38;

    assign initcode[2] = 8'h38;

    assign initcode[3] = 8'h38;

    assign initcode[4] = 8'h08; // Step 5: Display off

    assign initcode[5] = 8'h01; // Step 6: Display on

    assign initcode[6] = 8'h06; // Step 7: Entry mode set, automatic increase address

    assign initcode[7] = 8'h0c; // Function: 8 bit data, 2 lines, 5x8 character size

    

    // Assign characters to line1

    assign line1[0]  = 8'h20;

    assign line1[1]  = 8'h20;

    assign line1[2]  = 8'h20;

    assign line1[3]  = 8'h20;

    assign line1[4]  = 8'h57; //W

    assign line1[5]  = 8'h65; //e

    assign line1[6]  = 8'h6C; //l

    assign line1[7]  = 8'h63; //c

    assign line1[8]  = 8'h6F; //o

    assign line1[9]  = 8'h6D; //m

    assign line1[10] = 8'h65; //e

    assign line1[11] = 8'h20;

    assign line1[12] = 8'h20;

    assign line1[13] = 8'h20;

    assign line1[14] = 8'h20;

    assign line1[15] = 8'h20;

    

    // Assign characters to line2

    assign line2[0]  = 8'h20;

    assign line2[1]  = 8'h20;

    assign line2[2]  = 8'h20;

    assign line2[3]  = 8'h54; //T

    assign line2[4]  = 8'h6F; //o

    assign line2[5]  = 8'h20;

    assign line2[6]  = 8'h45; //E

    assign line2[7]  = 8'h53; //S

    assign line2[8]  = 8'h44; //D

    assign line2[9]  = 8'h20;

    assign line2[10] = 8'h4C; //L

    assign line2[11] = 8'h61; //a

    assign line2[12] = 8'h62; //b

    assign line2[13] = 8'h20;

    assign line2[14] = 8'h20;

    assign line2[15] = 8'h20;

    

    assign LCD_ON = 1'b1;

    assign LCD_RW = 1'b0;

    // LCD Controller

    always @(posedge clock or negedge reset) begin

        if (reset == 1'b0) begin

            count <= 0;

            state <= INIT;

        end else begin

            case (state)

                INIT: begin

                    `WriteInstruction(LCD_EN, LCD_RS, LCD_DATA, initcode[count])

                    state <= INIT_MAINTAIN;

                end

 

                INIT_MAINTAIN: begin

                    LCD_EN <= 1'b0;

                    count  <= count + 1;

                    state  <= (count < 7) ? INIT : HOME;

                end

 

                HOME: begin

                    `WriteInstruction(LCD_EN, LCD_RS, LCD_DATA, 8'h80) // Address 0x00 - start of line 1

                    state <= HOME_MAINTAIN;

                end

 

                HOME_MAINTAIN: begin

                    LCD_EN <= 1'b0;

                    count  <= 1'b0;

                    state  <= WRDATA1;

                end

 

                WRDATA1: begin

                    `WriteData(LCD_EN, LCD_RS, LCD_DATA, line1[count])

                    state <= WRDATA1_MAINTAIN;

                end

 

                WRDATA1_MAINTAIN: begin

                    LCD_EN <= 1'b0;

    count  <= count + 1;

                    state  <= (count < 16) ? WRDATA1 : LINE2;

                end

 

                LINE2: begin

    `WriteInstruction(LCD_EN, LCD_RS, LCD_DATA, 8'hC0) // Address 0x40 - start of line 2

                    state <= LINE2_MAINTAIN;

                end

 

                LINE2_MAINTAIN: begin

                    LCD_EN <= 1'b0;

    count  <= 0;

                    state  <= WRDATA2;

                end

 

                WRDATA2: begin

    `WriteData(LCD_EN, LCD_RS, LCD_DATA, line2[count])

                    state <= WRDATA2_MAINTAIN;

                end

 

                WRDATA2_MAINTAIN: begin

                    LCD_EN <= 1'b0;

    count  <= count + 1;

                    state  <= (count < 16) ? WRDATA2 : DONE;

                end

 

                DONE: begin

    state <= DONE;

end

                default: begin

    state <= DONE;

end

            endcase

        end

    end

endmodule