OpenCores
URL https://opencores.org/ocsvn/forwardcom/forwardcom/trunk

Subversion Repositories forwardcom

[/] [forwardcom/] [trunk/] [lcd.sv] - Rev 4

Compare with Previous | Blame | View Log

//////////////////////////////////////////////////////////////////////////////////
// Engineer: Agner Fog
// 
// Create Date:    2020-06-22
// Last modified:  2020-06-29
// Module Name:    decoder
// Project Name:   ForwardCom soft core
// Target Devices: Artix 7
// Tool Versions:  Vivado v. 2020.1
// License:        CERN-OHL-W v. 2 or later
// Description:    Driver for LCD displays
// Two LCD displays with each 4 lines x 20 characters
//////////////////////////////////////////////////////////////////////////////////

`include "defines.vh"

module lcd 
  #(parameter numrows = 8,               // number of lines of combined displays (2 - 8)
    parameter numcolumns = 20,           // number of characters per line
    parameter rows_per_display = 4)      // number of rows per display unit
 (input       clock,                     // system clock 100 MHz
  input       reset,                     // reset and clear
  input [4:0] x,                         // column number (0 = left)
  input [2:0] y,                         // row number (0 = top)
  input [7:0] text[0:numcolumns-1],      // text for one line
  input [4:0] text_length,               // length of text
  input       start,                     // start writing
  input       eol,                       // pad with spaces until end of line
  output reg  lcd_rs,                    // LCD RS pin
        output reg  [1:0] lcd_e,               // enable pins for two LCD displays
        output reg  [3:0] lcd_data,            // LCD data, 4 bit bus
        output reg  ready                      // finished writing. ready for next line
);

localparam count_bits = 14;              // number of bits in clock divider counter
localparam count_max = (2**count_bits)-1;// maximum count

logic [7:0] rowaddress;                  // command for setting row address
reg [count_bits-1:0] counter = 0;        // clock divider
reg [7:0] delay;                         // delay counter
reg [3:0] state = 0;                     // state machine for initialization
                                         // 0  -  9: initialization sequence
                                         // 10 - 11: set x,y position
                                         // 12 - 13: write characters
                                         // 15:      finished
reg [7:0] text_buffer [0:numcolumns-1];  // copy of input text
reg [4:0] column;                        // text column
reg [2:0] row;                           // text row
reg [4:0] text_count;                    // count down characters in text
reg       eol_save;                      // copy of eol

/* initialization sequence:
The display can receive data in 4-bit or 8-bit mode. We are using 4-bit mode,
sending 4 bits at a time, with only the upper four bits connected.
First, we send 8'H3x three times to get into 8-bit mode. Then 8'H2x to get
into 4-bit mode. The remaining numbers are 8-bit pairs:
8'H28: multi-line mode (the first needs a long delay)
8'H01: reset (needs long delay)
8'H0C: display on, no cursor, no blink
//8'H06 forward direction  
*/
reg [3:0] initialization_sequence [10] = {3, 3, 3, 2, 2, 8, 0, 1, 0, 12 };

always_comb begin
    // command to set row address
    if (rows_per_display == 4) begin
        case (row[1:0]) // 4 lines per display
        0: rowaddress = 8'H80 + column;
        1: rowaddress = 8'HC0 + column;
        2: rowaddress = 8'H80 + numcolumns + column;
        3: rowaddress = 8'HC0 + numcolumns + column;    
        endcase
    end else begin
        case (row[0])   // 2 lines per display
        0: rowaddress = 8'H80 + column;
        1: rowaddress = 8'HC0 + column;
        endcase    
    end
end

always_ff @(posedge clock) begin
    lcd_e <= 0;
    
    if (reset) begin            
        // reset
        counter <= 0;
        delay <= 8'H80;
        state <= 0;
        
    end else if (start && state == 15) begin
        // write command received
        text_buffer <= text;
        column <= x;
        row <= y;
        text_count <= text_length;
        eol_save <= eol;
        counter <= 0;
        state <= 10;
            
    end else begin        
        if (state < 10) begin
        
            // initialization sequence
            lcd_rs <= 0;
            if (delay > 0) begin
                if (counter == count_max) delay <= delay - 1;
            end else begin
                lcd_data <= initialization_sequence[state];
                lcd_e[0] <= counter[count_bits-1:count_bits-2] == 2'b01; // generate pulse for display unit 0
                lcd_e[1] <= counter[count_bits-1:count_bits-2] == 2'b01; // generate pulse for display unit 1
                if (counter == count_max) begin
                    if (state == 9) state <= 15; // finished
                    else state <= state + 1;     // next state
                    delay <= 8'H10;
                end
            end
            
        end else if (state < 12) begin
        
            // set (x,y) position
            lcd_rs <= 0;
            if (delay > 0) begin
                if (counter == count_max) delay <= delay - 1;
            end else begin
                lcd_data <= ~state[0] ? rowaddress[7:4] : rowaddress[3:0];
                if (row < rows_per_display)
                     lcd_e[0] <= counter[count_bits-1:count_bits-2] == 2'b01; // generate pulse
                else lcd_e[1] <= counter[count_bits-1:count_bits-2] == 2'b01; // generate pulse
                if (counter == count_max) begin
                    state <= state + 1;
                    delay <= 8'H10;
                end
            end
            
        end else if (state < 15) begin

            // write characters
            lcd_rs <= 1;
            if (delay > 0) begin
                if (counter == count_max) delay <= delay - 1;
            end else begin
                if (text_count > 0) begin
                    // write character
                    lcd_data <= ~state[0] ? text_buffer[0][7:4] : text_buffer[0][3:0];
                    if (row < rows_per_display)
                         lcd_e[0] <= counter[count_bits-1:count_bits-2] == 2'b01; // generate pulse
                    else lcd_e[1] <= counter[count_bits-1:count_bits-2] == 2'b01; // generate pulse
                end else if (eol_save) begin
                    lcd_data <= ~state[0] ? 2 : 0;                                // write space
                    if (row < rows_per_display)
                         lcd_e[0] <= counter[count_bits-1:count_bits-2] == 2'b01; // generate pulse
                    else lcd_e[1] <= counter[count_bits-1:count_bits-2] == 2'b01; // generate pulse
                end                        
                
                if (counter == count_max) begin
                    if (state[0]) begin
                        column <= column + 1;                                     // count up column index
                        if (text_count != 0) text_count <= text_count - 1;        // count down number of characters
                        for (int i = 0; i < numcolumns-1; i++) begin
                            text_buffer[i] <= text_buffer[i+1];                   // shift down to get next character
                        end                        
                    end                
                    if (state[0] && (column == numcolumns-1 || (text_count == 0 && !eol_save))) begin
                        state <= 15;                                              // finished                
                    end else begin
                        state[0] <= ~state[0];
                    end
                    // delay <= 8'H01;
                end
            end
        end else begin 
            // state = 15. finished
            lcd_e <= 0;
        end
    end
    if (state == 15) ready <= 1;
    else ready <= 0;
       
end 


endmodule

Compare with Previous | Blame | View Log

powered by: WebSVN 2.1.0

© copyright 1999-2024 OpenCores.org, equivalent to Oliscience, all rights reserved. OpenCores®, registered trademark.