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;
}
}
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);
}
///////////////////////////////////////////////////////////////////////////////
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);
}
////////////////////////////////////////////////////////////////////////////////