Large LCDs 4X40 Displays

Discussion on projects that are created by users and posted on mikroElektronika website.
Post Reply
Author
Message
Bill Legge
Posts: 235
Joined: 28 Oct 2007 03:16
Location: West Australia

Large LCDs 4X40 Displays

#1 Post by Bill Legge » 02 Feb 2019 08:53

Every now and again someone runs into the problem of driving a 4X40 LCD display.
The difficulty is that there are two ENABLEs - one for lines 1/2 and another for 3/4.
I've found two ways of solving this:
1. By re-writing the usual LCD driver for ENABLE_1 and ENABLE_2. So each old function
now has two new ones. e.g Lcd_Init() becomes Lcd_Init1() and LCD_Init2()
2. Some extra wining to combine the two ENABLES and use new functions Use_Lcd1() and Use_Lcd2() along with 'floating inputs'.
See the attached wiring diagram.
Both methods are in the code that follows - use the #define to compile the one you want.
Anyone spotting any mistakes - please let me know. Both compile OK for my EasyPICv7 Connectivity board with the LCD on PORTD.
The test file:

Code: Select all

////////////////////////////////////////////////////////////////////////////////
// Project:         EasyPICv7_LCD_4X40                                        //
// File:            EasyPICv7_LCD_4X40_Test                                   //
// Function:        Test write to 4X40 LCD with two ENABLE pins               //
// MCU:             PIC18F46K22                                               //
// Board:           EasyPICv7 Connectivity                                    //
// Power            5V                                                        //
// Compiler:        mikroC PRO for PIC version 7.2.0                          //
// Programmer:      On-board Mikro or ICD3                                    //
// Author:          WVL                                                       //
// Date:            2 February 2019                                           //
////////////////////////////////////////////////////////////////////////////////

////////////////////////////////////////////////////////////////////////////////
// Displaytech 404A series version 1                                          //                                                 
// WVL adaptor with E1 and E2 connected to EN via resistors                   //
// See attached file below for wiring diagram                                 //
// PIN 0    D4  for both LCDs                                                 //
// PIN 1    D5  for both LCDs                                                 //
// PIN 2    D6  for both LCDs                                                 //
// PIN 3    D7  for both LCDs                                                 //
// PIN 4    R/S for both LCDs H=data, L=instruction                           //
// PIN 5    ENABLE LCD1 and LCD2 via resistors                                //
// PIN 6    ENABLE LCD1 direct                                                //
// PIN 7    ENABLE LCD2 direct                                                //
////////////////////////////////////////////////////////////////////////////////

////////////////////////////////////////////////////////////////////////////////
// There are two ways to use 4X40 LCDs. These have two ENABLE pins one for the//
// .. top two lines and one for the bottom two lines.                         //
// .. it's like two different LCDs in one package - with common data pins     //
// Can be used in two different ways:                                         //
// 1. Enable one display and then use normal LCD code on two of the four lines//
// 2. Use code that has two of everything e.g Lcd_Init1, LCD_Init2 and so on. //
// Driver file LCD_4X40_DD has two commands for each LCD.                     //
// Driver file LCD_4X40_EE has one set of commands and separate ENABLE select //
////////////////////////////////////////////////////////////////////////////////

// Select one of the following for conditional compilation by commenting out
//#define USE_DRIVER_EE
#define USE_DRIVER_DD

////////////////////////////////////////////////////////////////////////////////
#ifdef USE_DRIVER_EE                                                          //
    #include "LCD_4X40_EE.c"   // with functions Use_Lcd1() and Use_Lcd2()    //
#endif                                                                        //
#ifdef USE_DRIVER_DD                                                          //
    #include "LCD_4X40_DD.c"   // with using Lcd_Putc1(),lcd_putc2 etc        //
#endif                                                                        //
////////////////////////////////////////////////////////////////////////////////

void main(){
    // Block variables
    char                    x = 0;
    unsigned int            a = 0;
    float             f = 1.23456;   // 4 bytes
    char test_string[] = "String";
    char              mystring[5];   // ByteToStr conversion needs fixed 5 chrs
    // Ports
    TRISD = 0b00000000;             // LCD port set to outputs
    TRISE = 0b00000000;             // Looping LCD
    // Peripherals
    #ifdef USE_DRIVER_EE
        Use_Lcd1();
        lcd_init();
        delay_ms(1);
        Use_Lcd2();
        lcd_init();
    #endif
    #ifdef USE_DRIVER_DD
        lcd_init1();
        lcd_init2();
    #endif
    delay_ms(15);

    while(1){
        ByteToStr(x,mystring);
        #ifdef USE_DRIVER_EE
            // Using functions Use_Lcd1() and Use_Lcd2() in LCD_4X40_EE.c
            Use_Lcd1();
            Printout(lcd_putc,"\fLCD1 LINE ONE   %u %s",a,mystring);
            Printout(lcd_putc,"\nLCD2 LINE TWO   %6.1f",f);
            LCD_String(30,1,test_string);
            LCD_Place(40,1,'X');
            Use_Lcd2();
            Printout(lcd_putc,"\fLCD1 LINE THREE %u %s",a+1,mystring);
            Printout(lcd_putc,"\nLCD2 LINE FOUR  %6.1f",f);
            LCD_String(30,1,test_string);
            LCD_Place(40,1,'X');
        #endif
        #ifdef USE_DRIVER_DD
            // Using function lcd_init1(), lcd_init2() etc in LCD_4X40_DD.c
            Printout(lcd_putc1,"\fLCD1 LINE ONE   %u %s",a,mystring);
            Printout(lcd_putc1,"\nLCD2 LINE TWO   %6.1f",f);
            LCD_String1(30,1,test_string);
            LCD_Place1(40,1,'X');;
            Printout(lcd_putc2,"\fLCD1 LINE THREE %u %s",a+1,mystring);
            Printout(lcd_putc2,"\nLCD2 LINE FOUR  %6.1f",f);
            LCD_String2(30,1,test_string);
            LCD_Place2(40,1,'X');
        #endif
        
        // housekeeping
        x++;
        a++;
        f = f +1;
        delay_ms(1000);
        LATE0_bit = ~LATE0_bit;
    }
}
And the code for one method:

Code: Select all

////////////////////////////////////////////////////////////////////////////////
// Project:         EasyPICv7_LCD_4X40                                        //
// File:            LCD_4X40_DD.c                                             //
// Function:        Test write to 4X40 LCD with two ENABLE pins               //
//                  By different functions for top and bottom LCDs            //
// MCU:             PIC18F46K22                                               //
// Board:           EasyPICv7 Connectivity                                    //
// Power            5V                                                        //
// Compiler:        mikroC PRO for PIC version 7.2.0                          //
// Programmer:      On-board Mikro or ICD3                                    //
// Author:          WVL                                                       //
// Date:            2 February 2019                                           //
////////////////////////////////////////////////////////////////////////////////

////////////////////////////////////////////////////////////////////////////////
// Displaytech 404A series version 1                                          //
// wait 15mS after power-up                                                   //
// use Printout library                                                       //
////////////////////////////////////////////////////////////////////////////////

////////////////////////////////////////////////////////////////////////////////
// set ports to WVL adaptor for 4X40 display                                  //
#define LCD_DB4   LATD0_bit                                                   //
#define LCD_DB5   LATD1_bit                                                   //
#define LCD_DB6   LATD2_bit                                                   //
#define LCD_DB7   LATD3_bit                                                   //
#define LCD_RS    LATD4_bit         // H=data, L=instruction                  //
#define LCD_E1    LATD6_bit         // active high then H to L for lines 1,2  //
#define LCD_E2    LATD7_bit         // active high then H to L for lines 3,4  //
////////////////////////////////////////////////////////////////////////////////

////////////////////////////////////////////////////////////////////////////////
// defines for most 4x20 LCDs.                                                //
#define LCD_LINE_1_ADDRESS 0x00                                               //
#define LCD_LINE_2_ADDRESS 0x40                                               //
#define lcd_type 2                  // 0=5x7, 1=5x10, 2=2 lines(or more)      //
////////////////////////////////////////////////////////////////////////////////

// BOTH DISPLAYS ///////////////////////////////////////////////////////////////
char lcd_line;
char const LCD_INIT_STRING[4] ={
    0x20 | (lcd_type << 2),     // Set mode: 4-bit, 2+ lines, 5x8 dots
    0xc,                        // Display on
    1,                          // Clear display
    6                           // Increment cursor
};
// DISPLAY 1 ///////////////////////////////////////////////////////////////////
void Lcd_Send_Nibble1(char nibble){
    // !! converts an integer expression to a boolean (1 or 0).
    LCD_DB4 = !!(nibble & 1);
    LCD_DB5 = !!(nibble & 2);
    LCD_DB6 = !!(nibble & 4);
    LCD_DB7 = !!(nibble & 8);
    delay_us(2);
    LCD_E1 = 1;
    delay_us(2);                // enable by H to L transition
    LCD_E1 = 0;
}
// DISPLAY 2 ///////////////////////////////////////////////////////////////////
void Lcd_Send_Nibble2(char nibble){
    //!! converts an integer expressionto a boolean (1 or 0).
    LCD_DB4 = !!(nibble & 1);
    LCD_DB5 = !!(nibble & 2);
    LCD_DB6 = !!(nibble & 4);
    LCD_DB7 = !!(nibble & 8);
    delay_us(2);
    LCD_E2 = 1;
    delay_us(2);                // enable by H to L transition
    LCD_E2 = 0;
}
// DISPLAY 1 ///////////////////////////////////////////////////////////////////
// Send a byte to the LCD.
void Lcd_Send_Byte1(char address, char n){
    LCD_RS = 0;
    delay_us(60);
    if(address)
        LCD_RS = 1;             // if RS = 1 instruction follows
    else
        LCD_RS = 0;             // if RS = 0 data follows
    delay_us(2);
    LCD_E1 = 0;
    Lcd_Send_Nibble1(n >> 4);
    Lcd_Send_Nibble1(n & 0xf);
}
//DISPLAY 2 ////////////////////////////////////////////////////////////////////
// Send a byte to the LCD.
 void Lcd_Send_Byte2(char address, char n){
    LCD_RS = 0;
    delay_us(60);
    if(address)
        LCD_RS = 1;             // if RS = 1 instruction follows
    else
        LCD_RS = 0;             // if RS = 0 data follows
    delay_us(2);
    LCD_E2 = 0;
    Lcd_Send_Nibble2(n >> 4);
    Lcd_Send_Nibble2(n & 0xf);
}
// DISPLAY 1 ///////////////////////////////////////////////////////////////////
 void Lcd_Init1(void){
    char i;
    lcd_line = 1;
    LCD_RS = 0;
    LCD_E1 = 0;
    delay_ms(35);

    for(i=0 ;i < 3; i++){
       Lcd_Send_Nibble1(0x03);
       delay_ms(5);
    }

    Lcd_Send_Nibble1(0x02);

    for(i=0; i < sizeof(LCD_INIT_STRING); i++){
        Lcd_Send_Byte1(0, LCD_INIT_STRING[i]);
        delay_ms(5);
    }
}
// DISPLAY 2 ///////////////////////////////////////////////////////////////////
void Lcd_Init2(void){
    char i;
    lcd_line = 1;
    LCD_RS = 0;
    LCD_E2 = 0;
    delay_ms(35);

    for(i=0 ;i < 3; i++){
       Lcd_Send_Nibble2(0x03);
       delay_ms(5);
    }

    Lcd_Send_Nibble2(0x02);         // 4 bit interface sent first

    for(i=0; i < sizeof(LCD_INIT_STRING); i++){
       Lcd_Send_Byte2(0, LCD_INIT_STRING[i]);
       delay_ms(5);
    }
}
////////////////////////////////////////////////////////////////////////////////
void Lcd_GoToxy(char x, char y){
    char address;
    switch(y){
        case 1:
            address = LCD_LINE_1_ADDRESS;
            break;
        case 2:
            address = LCD_LINE_2_ADDRESS;
            break;
        default:
            address = LCD_LINE_1_ADDRESS;
            break;
    }
    address += x-1;
    Lcd_Send_Byte1(0, 0x80 | address);
    Lcd_Send_Byte2(0, 0x80 | address);
}
// DISPLAY 1 ///////////////////////////////////////////////////////////////////
void Lcd_Putc1(char c){
// Print a single character at curren position. Check for \f and \n and \b
    switch(c){
        case '\f':
            Lcd_Send_Byte1(0,1);
            lcd_line = 1;
            delay_ms(2);
            break;
        case '\n':
            Lcd_GoToxy(1, ++lcd_line);
            break;
        case '\b':
            Lcd_Send_Byte1(0,0x10);
            break;
        default:
            Lcd_Send_Byte1(1,c);
            break;
    }
}
// DISPLAY 2 ///////////////////////////////////////////////////////////////////
// Print a single character at curren position. Check for \f and \n and \b
void Lcd_Putc2(char c){
    switch(c){
        case '\f':
            Lcd_Send_Byte2(0,1);
            lcd_line = 1;
            delay_ms(2);
            break;
        case '\n':
            Lcd_GoToxy(1, ++lcd_line);
            break;
        case '\b':
            Lcd_Send_Byte2(0,0x10);
            break;
        default:
            Lcd_Send_Byte2(1,c);
            break;
    }
}
// DISPLAY 1 ///////////////////////////////////////////////////////////////////
// Print a null terminated string[]  at x,y
void Lcd_String1(char x, char y, char string[]){
    char i=0;
    Lcd_Gotoxy(x,y);
    while(string[i]!=0){
        Lcd_Putc1(string[i]);
        i++;
    }
}
// DISPLAY 1 ///////////////////////////////////////////////////////////////////
// Put a single character c at x,y
void Lcd_Place1(char x, char y, char c){
    Lcd_Gotoxy(x,y);
    Lcd_Putc1(c);
}
// DISPLAY 2 ///////////////////////////////////////////////////////////////////
// Print a null terminated string[]  at x,y
void Lcd_String2(char x, char y, char string[]){
    char i=0;
    Lcd_GoToxy(x,y);
    while(string[i]!=0){
        Lcd_Putc2(string[i]);
        i++;
    }
}
// DISPLAY 2 ///////////////////////////////////////////////////////////////////
// Put a single character c at x,y
void Lcd_Place2(char x, char y, char c){
    Lcd_GoToxy(x,y);
    Lcd_Putc2(c);
}
///////////////////////////////////////////////////////////////////////////////
And the other:

Code: Select all

////////////////////////////////////////////////////////////////////////////////
// Project:         EasyPICv7_LCD_4X40                                        //
// File:            LCD_4X40_EE.c                                             //
// Function:        Write to 4X40 LCD with two ENABLE pins                    //
//                  By selecting E1 or E2 then using normal LCD commands      //
// MCU:             PIC18F46K22                                               //
// Board:           EasyPICv7 Connectivity                                    //
// Power            5V                                                        //
// Compiler:        mikroC PRO for PIC version 7.2.0                          //
// Programmer:      On-board Mikro or ICD3                                    //
// Author:          WVL                                                       //
// Date:            2 February 2019                                           //
////////////////////////////////////////////////////////////////////////////////

////////////////////////////////////////////////////////////////////////////////
// Displaytech 404A series version 1 - wait 15mS after power-up               //
// Need some special wiring E1 and E2 connected to EN via resistors - see att //
////////////////////////////////////////////////////////////////////////////////

////////////////////////////////////////////////////////////////////////////////
// set ports to WVL adaptor for 4X40 display                                  //
#define LCD_DB4   LATD0_bit                                                   //
#define LCD_DB5   LATD1_bit                                                   //
#define LCD_DB6   LATD2_bit                                                   //
#define LCD_DB7   LATD3_bit                                                   //
#define LCD_RS    LATD4_bit         // H=data, L=instruction                  //
#define LCD_EN    LATD5_bit         // active high then H to L for both LCDs  //
#define LCD_EN1   LATD6_bit         // active high then H to L for lines 1,2  //
#define LCD_EN2   LATD7_bit         // active high then H to L for lines 3,4  //
#define EN1_DIR   TRISD6_bit
#define EN2_DIR   TRISD7_bit
////////////////////////////////////////////////////////////////////////////////

////////////////////////////////////////////////////////////////////////////////
// Addresses/defines for most 4x20 LCDs.                                      //
#define LCD_LINE_1_ADDRESS 0x00                                               //
#define LCD_LINE_2_ADDRESS 0x40                                               //
#define lcd_type 2              // 0=5x7, 1=5x10, 2=2 lines(or more)          //
////////////////////////////////////////////////////////////////////////////////

////////////////////////////////////////////////////////////////////////////////
// Select display first - then use normal LCD functions                       //
// use LCD1 or LCD2 by floating the unused EN pin and enabling the other      //
void Use_Lcd1(){                                                              //
    EN1_DIR = 1;            // is a floating input                            //
    EN2_DIR = 0;            // is an output                                   //
    LCD_EN2 = 0;            // is reset                                       //
    delay_ms(10);                                                             //
}                                                                             //
void Use_Lcd2(){                                                              //
    EN2_DIR = 1;            // is a floating input                            //
    EN1_DIR = 0;            // is an output                                   //
    LCD_EN1  = 0;           // reset                                          //
    delay_ms(10);                                                             //
}                                                                             //
////////////////////////////////////////////////////////////////////////////////

// BOTH DISPLAYS ///////////////////////////////////////////////////////////////
char lcd_line;
char const LCD_INIT_STRING[4] ={
    0x20 | (lcd_type << 2),     // Set mode: 4-bit, 2+ lines, 5x8 dots
    0xc,                        // Display on
    1,                          // Clear display
    6                           // Increment cursor
};
// BOTH DISPLAYS ///////////////////////////////////////////////////////////////
void Lcd_Send_Nibble(char nibble){
    // !! converts an integer expression to a boolean (1 or 0).
    LCD_DB4 = !!(nibble & 1);
    LCD_DB5 = !!(nibble & 2);
    LCD_DB6 = !!(nibble & 4);
    LCD_DB7 = !!(nibble & 8);
    delay_us(2);
    LCD_EN = 1;
    delay_us(2);                // enable by H to L transition
    LCD_EN = 0;
}
// BOTH DISPLAYS ///////////////////////////////////////////////////////////////
void Lcd_Send_Byte(char address, char n){
    LCD_RS = 0;
    delay_us(60);
    if(address)
        LCD_RS = 1;             // if RS = 1 instruction follows
    else
        LCD_RS = 0;             // if RS = 0 data follows
    delay_us(2);
    LCD_EN = 0;
    Lcd_Send_Nibble(n >> 4);
    Lcd_Send_Nibble(n & 0xf);
}
// BOTH DISPLAYS ///////////////////////////////////////////////////////////////
void Lcd_Init(void){
    char i;
    lcd_line = 1;
    LCD_RS = 0;
    LCD_EN = 0;
    delay_ms(35);

    for(i=0 ;i < 3; i++){
       Lcd_Send_Nibble(0x03);
       delay_ms(5);
    }

    Lcd_Send_Nibble(0x02);

    for(i=0; i < sizeof(LCD_INIT_STRING); i++){
        Lcd_Send_Byte(0, LCD_INIT_STRING[i]);
        delay_ms(5);
    }
}
// BOTH DISPLAYS ///////////////////////////////////////////////////////////////
void Lcd_GoToxy(char x, char y){
    char address;
    switch(y){
        case 1:
            address = LCD_LINE_1_ADDRESS;
            break;
        case 2:
            address = LCD_LINE_2_ADDRESS;
            break;
        default:
            address = LCD_LINE_1_ADDRESS;
            break;
    }
    address += x-1;
    Lcd_Send_Byte(0, 0x80 | address);
}
// BOTH DISPLAYS ///////////////////////////////////////////////////////////////
// Print a single character at curren position. Check for \f and \n and \b
void Lcd_Putc(char c){
    switch(c){
        case '\f':
            Lcd_Send_Byte(0,1);
            lcd_line = 1;
            delay_ms(2);
            break;
        case '\n':
            Lcd_GoToxy(1, ++lcd_line);
            break;
        case '\b':
            Lcd_Send_Byte(0,0x10);
            break;
        default:
            Lcd_Send_Byte(1,c);
            break;
    }
}
// BOTH DISPLAYS ///////////////////////////////////////////////////////////////
// Print a null terminated string[] at x,y
void Lcd_String(char x, char y, char string[]){
    char i=0;
    Lcd_GoToxy(x,y);
    while(string[i]!=0){
        Lcd_Putc(string[i]);
        i++;
    }
}
// BOTH DISPLAYS ///////////////////////////////////////////////////////////////
// Put a single character c at x,y
void Lcd_Place(char x, char y, char c){
    Lcd_GoToxy(x,y);
    Lcd_Putc(c);
}
////////////////////////////////////////////////////////////////////////////////
Good luck - Bill Legge in Australia
Attachments
WVL_4X40_Adapter_2019.pdf
(100.71 KiB) Downloaded 111 times
WVL_4X40_Adapter.pdf
(88.47 KiB) Downloaded 126 times

Post Reply

Return to “User Projects”