18F4620 – 2 x I2C LCD with PCF8574

To my English language readers

The following article is in French.

Here is a summary:

How to display on a I2C LCD using a PCF8574 and a PIC 18F4620.

The Proteus project is available here.

Note: source code is commented in English.

18F4620 I2C LCD


Mise en situation

Contrairement à nos amis Arduiniens, quand vient le moment d’interconnecter des périphériques à une puce PIC, c’est souvent la recherche sans fin dans Internet.

Pour Pic, moins de librairies spécialisées comme : LiquidCrystal , Wire, Ethernet, Firmata, Servo, WiFi, TFT, EEPROM.

Arduino supportant un nombre limité de puces, cela rend la normalisation des librairies plus simple.

De plus, un grand nombre de contributeurs aident à enrichir les outils de développement.

De retour chez PIC

Parfois, interfacer une puce PIC à un périphérique est assez facile, LCD sous PIC, d’autres fois, pas vraiment.

Tentez de trouver une solution pour connecter un écran LCD en utilisant le protocole I2C via une puce PCF8574.

Le genre de circuit mille fois disponible sur eBay.

i2c lcd board

Vrai, il existe des solutions à gauche ou droite, mais quelle misère…

Pour en arriver à une solution fonctionnelle, il faut ramer!

Scénario 1

Le module PCF8574 n’est pas documenté:

ou est RS, CS, E?

et pour le data?

Dans mon cas, j’ai du passer à l’ohmmètre les broches RS, RW,E et D4 à D7 de mon afficheur I2C LCD made in China,

Résultat:

Data PCA8574: P7,P6,P5,P4
Control: P3: Back Light, P2: E-Enable, P1:RW, P0: RS

Scénario 2

Vous avez trouvez un bout de code mais misère, pas le bon compilateur, la bonne famille ou le bon langage…

Scénario 3

Vous avez la documentation en main et tout va bien, chanceux!

Scénario 4

On vous fournit une solution – Proteus –  clé en main, possible?  Voir le lien de téléchargement au bas de l’article.

Fonctionnement de la plaquette I2C vers LCD

Les plaquettes de conversion des écrans LCD de I2C vers BUS LCD 4 bits utilisent, généralement, la puce PCF8574 ou la PCF8574A.

Ce circuit a pour fonction de désérialiser les données du BUS I2C vers un octet donc, de présenter l’information en parallèle sur 8 bits.

PCF8574

Les écrans LCD HD44780 1602 pouvant être opérés en mode 4 bits, il sera alors possible d’utiliser une partie de l’octet (nibble du bas) pour les signaux de contrôle : RS, RW, E, retro éclairage et l’autre partie (nibble du haut) pour les données.

La difficulté sera de combiner correctement les signaux de contrôle et les données dans un même octet.

L’extraction des nibbles d’un octet de données est habituellement obtenu ainsi:

#define HI_NIBBLE(b) (((b) >> 4) & 0x0F)
#define LO_NIBBLE(b) ((b) & 0x0F)

Étant donné que les signaux de contrôle du LCD sont connectés sur les broches P0 à P3 du PCF8574 et les données sur P4 à P7, nous utiliseront plutôt l’approche suivante:

#define LO_NIBBLE(b) (((b) << 4) & 0xF0)
#define HI_NIBBLE(b) ((b) & 0xF0)

Pour l’ajout des signaux de contrôle à un des deux nibbles nous utiliseront des opérations bit à bit (Bitwise operation).

Par example,

lcddata = HI_NIBBLE(data) | LCD_BL | LCD_RS;

Adresse I2C de PCF8574

L’adresse I2C de PCF8574 est située dans la plage 20H à 27H et de 38H à 3Fh pour PCF8574A.

En utilisant ces deux puces dans un même circuit, il serait possible de connecter 16 écrans LCD.

Voici les tableaux d’adressage:

PCF8574 address map PCF8574A address map

Dans la cas des plaquettes que j’utilise, les broches d’adressage de PCF8674 (A0..A2) sont connectées à VCC via une résistance de tirage (pull-up resistor).  L’adresse maximale de 27H est donc obtenue par défaut.

Il suffit de souder un cavalier sur les bornes A0..A2 pour ramener les broches à 0 et faire ainsi varier l’adresse I2C du circuit.

i2c lcd board address

À bien y regarder, pas si simple

Même si vous avez bien saisie l’arithmétique des nibbles et les opérations bit à bit, ce n’est encore pas suffisant pour afficher sur l’écran LCD. Pour ce faire, il faudra aussi comprendre la mécanique de programmation et la synchronisation des signaux de la puce contrôlant l’affichage: HD44780.
Dans le code source du projet, il y a la séquence suivante :

void LCD_init(unsigned char addr)
{
__delay_ms(20); // Wait > 15 ms after power ON

LCD_putcmd(addr, LCD_INIT_BYTE,0);__delay_ms(5); // Wait > 4.1 ms
LCD_putcmd(addr, LCD_INIT_BYTE,0);
LCD_putcmd(addr, LCD_INIT_BYTE,0);
LCD_putcmd(addr, LCD_BUS_WIDTH_4Bit,0);
LCD_putcmd(addr, LCD_4BITS_2LINES_5x8FONT,1);
LCD_putcmd(addr, LCD_DISPLAY_OFF_CURSOR_OFF_BLINK_OFF,1);
LCD_putcmd(addr, LCD_CLEAR,1);
LCD_putcmd(addr, LCD_INCREMENT_NO_SHIFT,1);
LCD_putcmd(addr, LCD_DISPLAY_ON_CURSOR_OFF,1);
} // LCD_init()

Ces instructions servent à initialiser le LCD en mode 4 bits, à allumer l’écran, à masquer le curseur, …

Remarquez que des délais sont insérés ici et là.  Il y a des délais aussi dans la fonction LCD_putcmd.

Ces délais sont importants pour assurer le bon fontionnement  de l’écran.

Pour connaître les commandes de contrôles et les délais de programmation de la puce HD44780, je vous revoie à la documentation.

Le projet

Après deux jours de recherches et de prototypage, j’en suis arrivé à une solution de contrôle de deux (2) LCD via I2C pilotés par 2 puces PCF8574.

Mes recherches, dans Internet, m’ont amené à plusieurs endroits, mais la source d’information que j’ai retenue est situé à l’adresse suivante: http://paulfjujo.free.fr/_18FxxKxx/Test_LCD_I2C_PIC18F26k22.htm.

Paul Freyer propose un prototype fonctionnel de l’utilisation d’une PCF8574.

Je me suis inspiré de son approche.

Voici la solution:

18F4620 I2C LCD video2
Télécharger le projet Proteus.


Les codes sources

main.c


/*
 * File: main.c
 * By: Alain Boudreault (ve2cuy)
 * Date: 2015.04.04
 * Completed: 2015.04.05
 * ----------------------------------------------------------------------------
 * Description: Use a PCA8574 to control 2 LCD using I2C protocol.
 *
 * Version fonctionnelle terminée le 2015.04.05.
 * ----------------------------------------------------------------------------
 * standard I2C LCD from eBay
 * http://www.ebay.com/sch/i.html?_from=R40&amp;_trksid=p2050601.m570.l1313.TR4.TRC2.A0.H0.Xi2c+lcd.TRS0&amp;_nkw=i2c+lcd&amp;_sacat=0
 * LCD Data PCA8574: P7,P6,P5,P4
 * LCD Control: P3: Back Light, P2: E-Enable, P1:RW, P0: RS
 * -----------------------------------------------------------------------------
 * This program is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program. If not, see &lt;http://www.gnu.org/licenses/&gt;.
*/

#include &lt;xc.h&gt;
#include &lt;stdio.h&gt; // for sprintf()
#include "lcd.h" 

// #define proteus_simulation 1

#pragma config OSC = INTIO7 // Internal osc
#pragma config WDT = OFF // No watch dog

#define _XTAL_FREQ 8000000
#define TEXT_BUFFER 80

char text[TEXT_BUFFER]; 

#ifdef proteus_simulation
 char LCD_01_ADDRESS = 0x40;
 char LCD_02_ADDRESS = 0x42;
#else
 char LCD_01_ADDRESS = 0b1001110; // default address
 char LCD_02_ADDRESS = 0b1001100; // jumper on A0
#endif

void longDelay(int time){
 for (int i = 0 ; i &lt; time; i++) __delay_ms(50);
}

// Program entry point
void main() {

 //ADCON1 = 0xF; ADCON2 = 0xF; // No analog, all digital i/o
 OSCCON = 0b01110010; // Fosc = 8MHz
 SSPADD = 19; // SCL (i2c clock) speed: ((8 Mhz) / (4 * 100 khz)) - 1 = 19
 //TRISC = 0xFF; PORTC = 0x00; LATC = 0x00;

 OpenI2C(MASTER, SLEW_OFF);
 LCD_init(LCD_01_ADDRESS);
 LCD_init(LCD_02_ADDRESS);

 unsigned int counter = 0;

 while(1) {
 LCD_putcmd(LCD_01_ADDRESS, LCD_CLEAR,1);
 sprintf(text, "%d times", counter++);
 LCD_puts(LCD_01_ADDRESS, "I2C print on LCD 1\0");
 LCD_goto(LCD_01_ADDRESS,2,1);
 LCD_puts(LCD_01_ADDRESS, text);
 LCD_goto(LCD_01_ADDRESS,3,1);
 LCD_puts(LCD_01_ADDRESS, "--------------------\0");
 LCD_goto(LCD_01_ADDRESS,4,1);
 LCD_puts(LCD_01_ADDRESS, "18F4620, PCF6574+LCD\0");
 longDelay(10); 

 LCD_putcmd(LCD_02_ADDRESS, LCD_CLEAR,1);
 sprintf(text, "%u times", ~counter);
 LCD_puts(LCD_02_ADDRESS, "I2C print on LCD 2\0");
 LCD_goto(LCD_02_ADDRESS,2,1);
 LCD_puts(LCD_02_ADDRESS, text);
 LCD_goto(LCD_02_ADDRESS,3,1);
 LCD_puts(LCD_02_ADDRESS, "------(cl)2015------\0");
 LCD_goto(LCD_02_ADDRESS,4,1);
 LCD_puts(LCD_02_ADDRESS, "Project by VE2CUY\0");
 longDelay(10); 

 } // while(1)
} // main()


lcd.h

/*
 * File: lcd.h
 * Author: Alain Boudreault - ve2cuy
 *
 * Created on 6 avril 2015, 15:57
 *
 * -----------------------------------------------------------------------------
 * This program is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program. If not, see &lt;http://www.gnu.org/licenses/&gt;.
 */

#ifndef LCD_H
#define LCD_H

#ifdef __cplusplus
extern "C" {
#endif

#ifdef __cplusplus
}
#endif

/// ###### I2C LCD defines ######
#define LCD_WAIT_DELAY 2
#define LCD_BL 0x08
#define LCD_EN 0x04
#define LCD_RW 0x02
#define LCD_RS 0x01

// LCD Command
#define LCD_INIT_BYTE 0x30
#define LCD_BUS_WIDTH_4Bit 0x20
#define LCD_BUS_WIDTH_8Bit 0x30
#define LCD_CLEAR 0x01
#define LCD_HOME 0x02
#define LCD_ON 0x0C
#define LCD_OFF 0x08
#define LCD_LINE1 0x80
#define LCD_LINE2 0xC0
#define LCD_LINE3 0x94
#define LCD_LINE4 0xD4
#define LCD_CURSOR_OFF 0x0C
#define LCD_UNDERLINE_ON 0x0E
#define LCD_BLINK_CURSOR_ON 0x0F
#define LCD_MOVE_CURSOR_LEFT 0x10
#define LCD_MOVE_CURSOR_RIGHT 0x14
#define LCD_SHIFT_LEFT 0x18
#define LCD_SHIFT_RIGHT 0x1E 

#define LCD_DISPLAY_ON_CURSOR_OFF 0x0c
#define LCD_DISPLAY_OFF_CURSOR_OFF_BLINK_OFF 0x08
#define LCD_4BITS_2LINES_5x8FONT 0x28
#define LCD_INCREMENT_NO_SHIFT 0x06
#define Byte unsigned char

#define LO_NIBBLE(b) (((b) &lt;&lt; 4) &amp; 0xF0)
#define HI_NIBBLE(b) ((b) &amp; 0xF0)

// function prototypes
unsigned char I2C_PCF8574_Write(Byte Adr,Byte value);
void LCD_init(unsigned char addr);
void LCD_putcmd(unsigned char addr, unsigned char data,unsigned char cmdtype);
void LCD_putch(unsigned char addr, unsigned char data);
void LCD_puts(unsigned char addr, char *s);
void LCD_goto(unsigned char addr, char row, char column);

#endif /* LCD_H */


i2c_lcd.c

/*
 * File: i2c_lcd.c
 * Author: Alain Boudreault - ve2cuy
 *
 * Created on 6 avril 2015, 15:57
 * Note: Certains morceaux de code inspirés de http://paulfjujo.free.fr/_18FxxKxx/datas/18F26k22_LCD_PCF8574_I2C_Hardw_master_10Mhz.c
 * -----------------------------------------------------------------------------
 * This program is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program. If not, see &lt;http://www.gnu.org/licenses/&gt;.
 */

#include &lt;xc.h&gt;
#include "lcd.h"

#ifndef _XTAL_FREQ
#define _XTAL_FREQ 8000000
#endif
/// ##############################################################################################
/// Custom LCD_I2C functions

unsigned char I2C_PCF8574_Write(Byte addr,Byte value)
{
 unsigned char S,dummy;
 StartI2C();
 S = WriteI2C( addr );
 if(S == -1) //bus collision ?
 {
 dummy = SSPBUF; // clear the buffer,
 SSPCON1bits.WCOL=0; // clear collision status bit
 }
 S = WriteI2C(value);
 StopI2C();
 // __delay_us(LCD_WAIT_DELAY); // No impact on my project!
 return(S);
} // I2C_PCF8574_Write()

void LCD_putcmd(unsigned char addr, unsigned char data,unsigned char cmdtype)
{
 unsigned char lcddata; 

 // Write high nibble
 lcddata = HI_NIBBLE(data) |LCD_BL;
 I2C_PCF8574_Write(addr,lcddata | LCD_EN);
 I2C_PCF8574_Write(addr,lcddata &amp; ~LCD_EN); // Reset LCD bus

 // cmdtype = 0; One cycle write, cmdtype = 1; Two cycle writes
 if (cmdtype) {
 // Write low nibble
 lcddata = LO_NIBBLE(data) |LCD_BL;
 I2C_PCF8574_Write(addr,lcddata | LCD_EN);
 I2C_PCF8574_Write(addr,lcddata &amp; ~LCD_EN); // Reset LCD bus
 }
 __delay_ms(2); // For most command, Wait &gt; 100 us is ok.
} // LCD_putcmd())

// Extract data high and low nible and send it to I2C LCD
void LCD_putch(unsigned char addr, unsigned char data)
{
 unsigned char lcddata;
 lcddata = HI_NIBBLE(data)|LCD_BL|LCD_RS; // Get high nibble
 I2C_PCF8574_Write(addr,lcddata | LCD_EN); // Send it!
 I2C_PCF8574_Write(addr,lcddata &amp; ~LCD_EN); // Reset LCD bus
 lcddata = LO_NIBBLE(data)|LCD_BL|LCD_RS; // Get low nibble
 I2C_PCF8574_Write(addr,lcddata | LCD_EN); // Send it!
 I2C_PCF8574_Write(addr,lcddata &amp; ~LCD_EN); // Reset LCD bus
} // LCD_putch()

// Init the LCD: DATA bus 4 bits, cursor off, auto increment, no shift.
void LCD_init(unsigned char addr)
{
 __delay_ms(20); // Wait &gt; 15 ms after power ON

 LCD_putcmd(addr, LCD_INIT_BYTE,0);__delay_ms(5); // Wait &gt; 4.1 ms
 LCD_putcmd(addr, LCD_INIT_BYTE,0);
 LCD_putcmd(addr, LCD_INIT_BYTE,0);
 LCD_putcmd(addr, LCD_BUS_WIDTH_4Bit,0);
 LCD_putcmd(addr, LCD_4BITS_2LINES_5x8FONT,1);
 LCD_putcmd(addr, LCD_DISPLAY_OFF_CURSOR_OFF_BLINK_OFF,1);
 LCD_putcmd(addr, LCD_CLEAR,1);
 LCD_putcmd(addr, LCD_INCREMENT_NO_SHIFT,1);
 LCD_putcmd(addr, LCD_DISPLAY_ON_CURSOR_OFF,1);
} // LCD_init() 

// Goto line number. On line err, goto line 1.
void LCD_goto(unsigned char addr, unsigned char row, unsigned char column){
 switch(row){
 case 1: LCD_putcmd(addr,LCD_LINE1 + (column - 1), 1); break;
 case 2: LCD_putcmd(addr,LCD_LINE2 + (column - 1), 1); break;
 case 3: LCD_putcmd(addr,LCD_LINE3 + (column - 1), 1); break;
 case 4: LCD_putcmd(addr,LCD_LINE4 + (column - 1), 1); break;
 default: LCD_putcmd(addr,LCD_LINE1 + (column - 1), 1); break;
 }
 } // LCD_GOTO()

// Note: The string must be zero terminated!
// Example: char callSign[] = "ve2cuy\0";
void LCD_puts(unsigned char addr, char *s)
{
 int i=0;
 while(*s != 0) LCD_putch(addr, *s++);
}


Article rédigé par Alain Boudreault (ve2cuy) – Avril 2015 – Lien court

7 Responses to 18F4620 – 2 x I2C LCD with PCF8574

  1. Ping : PIC 18F – I2C LCD display using a PCF8574 | ve2cuy

  2. Jim Bixby says:

    First, thanks for posting this – it enabled me to quickly bring up a 2 LCD project I’m working on. However, I found I needed the communication to the LCDs to go much faster. This project operates at a 10Hz refresh rate, and I was spending 40ms of that just writing to the LCDs. After a bit of datasheet study, I found that one can write a stream to the PCF8574. That is: Start Addr Data Data … Stop will result in the data output being updated on each data byte without having to resend the address for each nibble. The result on a one-byte write to the LCD is 5 I2C character times instead of 8 for a single character write. A stream of many chars can be sent. For a 5-character write to the LCD, the result is 21 I2C byte times versus 40, nearly doubling the throughput to the LCD.

    The relevant code is below. I’m doing this as a hobby project – feel free to share or use this any way you like.

    I intend to make a backpack using a PIC processor instead of the PCF8574, with
    2 addresses. Communication with one address will mimic the PCF8574 but allow much higher clock rate to get around the 100khz limitation of the PCF8574, while communication with the other address will do a byte-write to the LCD in 2 I2C character times, and write N bytes in N+1 I2C character times. If this backpack is operated at 400khz instead of 100 khz, the result will be an 8x speed improvement over the original implementation.

    Thanks again – your routines were a big help.
    Best regards,
    Jim
    K6BIX

    /************************* lcd.h ********************************/
    #ifndef LCD_H
    #define LCD_H

    #include
    /***************Hardware Definitions***********************************/
    //PCF8475A
    const char LCD_01_ADDRESS = 0b1111110; // default address
    const char LCD_02_ADDRESS = 0b1111100; // jumper on A0

    /* Define t
    he MSSP port to be used.
    / 0: Default port (for devices with a single MSSP)
    / 1: MSSP1
    / 2: MSSP2 */
    #define _I2C_MSSP 2

    #if _I2C_MSSP == 1
    #define lcd_idleI2C() IdleI2C1()
    #define lcd_startI2C() StartI2C1()
    #define lcd_stopI2C() StopI2C1()
    #define lcd_writeI2C(d) WriteI2C1(d)
    #define lcd_sspbuf SSP1BUF
    #define lcd_sspcon1 SSP1CON1
    #define LCD_wcol SSP1CON1bits.WCOL
    #elif _I2C_MSSP == 2
    #define lcd_idleI2C() IdleI2C2()
    #define lcd_startI2C() StartI2C2()
    #define lcd_stopI2C() StopI2C2()
    #define lcd_writeI2C(d) WriteI2C2(d)
    #define lcd_sspbuf SSP2BUF
    #define lcd_sspcon1 SSP2CON1
    #define lcd_wcol SSP2CON1bits.WCOL
    #else
    #define lcd_idleI2C() IdleI2C()
    #define lcd_startI2C() StartI2C()
    #define lcd_stopI2C() StopI2C()
    #define lcd_writeI2C(d) WriteI2C(d)
    #define lcd_sspbuf SSPBUF
    #define lcd_sspcon1 SSPCON1
    #define LCD_wcol SSPCON1bits.WCOL
    #endif

    /**********************************************************************/

    /// ###### I2C LCD defines ######
    #define LCD_WAIT_DELAY 2
    #define LCD_BL 0x08
    #define LCD_EN 0x04
    #define LCD_RW 0x02
    #define LCD_RS 0x01

    // LCD Command
    #define LCD_INIT_BYTE 0x30
    #define LCD_BUS_WIDTH_4Bit 0x20
    #define LCD_BUS_WIDTH_8Bit 0x30
    #define LCD_CLEAR 0x01
    #define LCD_HOME 0x02
    #define LCD_ON 0x0C
    #define LCD_OFF 0x08
    #define LCD_LINE1 0x80
    #define LCD_LINE2 0xC0
    #define LCD_LINE3 0x94
    #define LCD_LINE4 0xD4
    #define LCD_CURSOR_OFF 0x0C
    #define LCD_UNDERLINE_ON 0x0E
    #define LCD_BLINK_CURSOR_ON 0x0F
    #define LCD_MOVE_CURSOR_LEFT 0x10
    #define LCD_MOVE_CURSOR_RIGHT 0x14
    #define LCD_SHIFT_LEFT 0x18
    #define LCD_SHIFT_RIGHT 0x1E

    #define LCD_DISPLAY_ON_CURSOR_OFF 0x0c
    #define LCD_DISPLAY_OFF_CURSOR_OFF_BLINK_OFF 0x08
    #define LCD_4BITS_2LINES_5x8FONT 0x28
    #define LCD_INCREMENT_NO_SHIFT 0x06
    //#define Byte unsigned char

    #define LO_NIBBLE(b) (((b) << 4) & 0xF0)
    #define HI_NIBBLE(b) ((b) & 0xF0)

    // function prototypes
    void LCD_init(unsigned char addr);
    void LCD_putcmd(unsigned char addr, unsigned char data,unsigned char cmdtype);
    void LCD_putch(unsigned char addr, unsigned char data);
    void LCD_puts(unsigned char addr, char *s);
    void LCD_putchars(unsigned char addr, char *s, unsigned char N);
    void LCD_goto(unsigned char addr, char row, char column);

    #endif /* LCD_H */

    /*****************i2c_lcd.c**********************************/
    #include
    #include « lcd.h »
    #include

    #ifndef _XTAL_FREQ
    #define _XTAL_FREQ 16000000
    #endif

    /// Custom LCD_I2C functions

    void LCD_start(uint8_t addr){
    //Start
    lcd_idleI2C();
    lcd_startI2C();
    //send address
    lcd_idleI2C();
    lcd_writeI2C(addr);
    }

    void LCD_stop(void){
    lcd_idleI2C();
    lcd_stopI2C();
    }

    //send data arg with E high then with E low
    void LCD_write_nibble(uint8_t data){
    lcd_idleI2C();
    lcd_writeI2C(data | LCD_EN);
    lcd_idleI2C();
    lcd_writeI2C(data & ~LCD_EN);
    }

    //Send a command byte to the lcd as one or two nibbles, with E strobe to LCD
    //This sends all four nibbles as a block to eliminate extra address sends
    //Sends a 2-nibble command in 5 I2C char times instead of 8
    void LCD_putcmd(uint8_t addr, uint8_t data,uint8_t cmdtype){
    LCD_start(addr);
    //write the high nibble E high then E low
    LCD_write_nibble(HI_NIBBLE(data)|LCD_BL);
    // cmdtype = 0; One cycle write, cmdtype = 1; Two cycle writes
    if (cmdtype) LCD_write_nibble(LO_NIBBLE(data)|LCD_BL);
    //and STOP
    LCD_stop();
    } // LCD_putcmd())

    //Send a character to the lcd as two nibbles, with E strobe to LCD
    //This sends all four nibbles as a block to eliminate extra address sends
    //Sends a 2-nibble byte in 5 I2C char times instead of 8
    void LCD_putch (uint8_t addr, uint8_t data){
    LCD_start(addr);
    LCD_write_nibble(HI_NIBBLE(data)|LCD_BL|LCD_RS);
    LCD_write_nibble(LO_NIBBLE(data)|LCD_BL|LCD_RS);
    LCD_stop();
    }

    // Init the LCD: DATA bus 4 bits, cursor off, auto increment, no shift.
    void LCD_init(unsigned char addr)
    {
    __delay_ms(40); // Wait > 40 ms after power ON

    LCD_putcmd(addr, LCD_INIT_BYTE,0);__delay_ms(5); // Wait > 4.1 ms
    LCD_putcmd(addr, LCD_INIT_BYTE,0);__delay_us(150); //Wait >100us
    LCD_putcmd(addr, LCD_INIT_BYTE,0);
    LCD_putcmd(addr, LCD_BUS_WIDTH_4Bit,0);
    LCD_putcmd(addr, LCD_4BITS_2LINES_5x8FONT,1);
    LCD_putcmd(addr, LCD_DISPLAY_OFF_CURSOR_OFF_BLINK_OFF,1);
    LCD_putcmd(addr, LCD_CLEAR,1); __delay_ms(2); //Wait >1.5ms for clear
    LCD_putcmd(addr, LCD_INCREMENT_NO_SHIFT,1);
    LCD_putcmd(addr, LCD_DISPLAY_ON_CURSOR_OFF,1);
    } // LCD_init()

    // Goto line number. On line err, goto line 1.
    void LCD_goto(unsigned char addr, unsigned char row, unsigned char column){
    switch(row){
    case 1: LCD_putcmd(addr,LCD_LINE1 + (column – 1), 1); break;
    case 2: LCD_putcmd(addr,LCD_LINE2 + (column – 1), 1); break;
    case 3: LCD_putcmd(addr,LCD_LINE3 + (column – 1), 1); break;
    case 4: LCD_putcmd(addr,LCD_LINE4 + (column – 1), 1); break;
    default: LCD_putcmd(addr,LCD_LINE1 + (column – 1), 1); break;
    }
    } // LCD_GOTO()

    // Write a zero-terminated string to the LCD
    void LCD_puts(unsigned char addr, char *s)
    {
    LCD_start(addr);
    while(*s != 0) {
    LCD_write_nibble(HI_NIBBLE(*s)|LCD_BL|LCD_RS);
    LCD_write_nibble(LO_NIBBLE(*s++)|LCD_BL|LCD_RS);
    }
    LCD_stop();
    }

    //Write N characters from a zero-terminated string
    //Pad with blanks at end if string is shorter than N
    void LCD_putchars(uint8_t addr, char *s, uint8_t N){
    uint8_t i = 0;;

    LCD_start(addr);

    while ( (*s != 0) && (i++ < N) ) {
    LCD_write_nibble(HI_NIBBLE(*s)|LCD_BL|LCD_RS);
    LCD_write_nibble(LO_NIBBLE(*s++)|LCD_BL|LCD_RS);
    }
    if (i < N){
    while (i++ < N){
    LCD_write_nibble(HI_NIBBLE(' ')|LCD_BL|LCD_RS);
    LCD_write_nibble(LO_NIBBLE(' ')|LCD_BL|LCD_RS);
    }
    }

    LCD_stop();
    }

  3. Abel Estevez says:

    Thanks for the basics of HD, an PCF, I f you want something about development, please contact me Im from mexico.

  4. Chico Woodman says:

    Bonjour – I just found this post and have tried to run the Proteus file.

    However, I get the following Proteus/xc8 compiler error:
    …..
    Warning at file ../main.c line 62 column 1: (361) function declared implicit int
    Error at file ../main.c line 62 column 9: (192) undefined identifier « MASTER »
    Error at file ../main.c line 62 column 17: (192) undefined identifier « SLEW_OFF »
    Warning at file ../main.c line 71 column 48: (359) illegal conversion between pointer types
    …..

    This is from the line in main.c : OpenI2C(MASTER, SLEW_OFF);

    It’s not clear to me where this routine is to found. What have I missed?

    Thanks…

    • Chico Woodman says:

      OK, I’ve discovered that Microchip no longer includes its Peripheral Libraries in xc8 (after v1.34, I believe). The peripheral libraries include their i2c libraries. So that’s why the above Proteus / xc8 project won’t compile if downloaded from here with unless an old version of xc8 is used….

  5. Alberto says:

    Hi, I found your page very interesting, and wanted to go deep into, but I saw that the latter comment says there are no more Peripheral Libraries embedded in Xc8. So I wanted at least download your Proteus project in the hope it were working as is. But when I tried, I had the damn google access page that forbid me to enter, as I am not the owner of your account. Can you please give me another address for downloading it?
    Kind regards
    Alberto

Laisser un commentaire