[Resolu] affichage LCD par PCF8576 sur I2C

Bonjour

J'ai récupéré par hasard un écran LCD piloté par un CI PCF8576T.
J'ai eu la drole d'idée de vouloire le faire fonctionner, et malgré beaucoup de temps passé dessus, je n'arrive pas à avoir un résultat satisfaisant.

Voilà où j'en suis :

L'écran LCD comporte deux lignes, avec un total de 71 segments (10 caractères et un point décimal). D'après la doc du PCF8576, cela correspond à un écran multiplexé à deux backpanes, et cela concorde avec ce que je vois sur le circuit : les broches 14 et 16 (BP2 et BP3) ne sont pas connectées.

La doc du PCF 8576 est disponible par exemple ici : PCF8576T datasheet(1/40 Pages) PHILIPS | Universal LCD driver for low multiplex rates

Je crois me cOnformer strictement à la doc, j'ai essayé plusieurs valeurs, et le meilleur résultat que j'ai obtenu est de pouvoir afficher les 7 segments du caractère droit de la ligne supérieure. Dès l'envoi du premier bit du seocnd octet, les segments ne s'affichent plus dans l'ordre.

Je me pose ces questions :
-Qu'est ce que j'ai raté ou mal compris avec le I2C, ou dans la prog du PCF8576 ?
-Pourquoi suis-je obligé de commencer avec le DATA POINTER à 3 pour avoir un résultat à peu près cohérent ?
-Dois je obligatoirement avoir la doc de l'écran LCD pour pouvoir l'utiliser ? (il s'agissait d'un compteur journalier/kilométrique de 406 peugeot).

Qui pourrait m'aider ?
Merci.

Voilà le code, dans son expression la plus simple :

/* 
Affichage LCD contrôllé opar PCF8576 chips sur bus I2C.

*/

#define LIAISON 128
#define NOLIAISON 0

//MODE SET
#define MODESET 64

#define MODE_NORMAL 0
#define MODE_POWERSAVING 16

#define DISPLAY_DISABLED 0
#define DISPLAY_ENABLED 8

#define BIAS_THIRD 0
#define BIAS_HALF 4

#define DRIVE_STATIC 1
#define DRIVE_2 2
#define DRIVE_3 3
#define DRIVE_4 0

int set_modeset = MODESET | MODE_POWERSAVING | DISPLAY_ENABLED | BIAS_HALF | DRIVE_2; //

//BLINK
#define BLINK  112

#define BLINKING_NORMAL 0
#define BLINKING_ALTERNATION 4

#define BLINK_FREQUENCY_OFF 0
#define BLINK_FREQUENCY2 1
#define BLINK_FREQUENCY1 2
#define BLINK_FREQUENCY05 3

int set_blink = BLINK | BLINKING_NORMAL | BLINK_FREQUENCY_OFF; //

//LOADDATAPOINTER
#define LOADDATAPOINTER  0

int set_datapointer = LOADDATAPOINTER | 3; //3 pour le départ.


//BANK SELECT
#define BANKSELECT 120

#define BANKSELECT_O1_RAM0 0
#define BANKSELECT_O1_RAM2 2

#define BANKSELECT_O2_RAM0 0
#define BANKSELECT_O2_RAM2 1

int set_bankselect = BANKSELECT | BANKSELECT_O1_RAM0 | BANKSELECT_O2_RAM0;

#define PCF8576 B111000

#include <Wire.h>

void setup(){
  Wire.begin(); 
  delay(1000); //allow lcd to wake up.
 }


void loop(){
  
      Wire.beginTransmission(PCF8576);
      //COMMAND
      Wire.write(LIAISON | set_modeset); //MODE SET 
      Wire.write(LIAISON | set_bankselect); //BANK SELECT 
      Wire.write(LIAISON | set_blink); //BLINK
      Wire.write(NOLIAISON | set_datapointer); //LOAD DATA POINTER
      //DATA
      Wire.write(B11111110); //affiche les 7 premiers segments, dans l'ordre dbcagfe (voir doc PCF8576 page 18).
      //Wire.write(B1001); //L'envoi de cet octet n'affiche plus les segments dans leur suite 'logique'.  Why ?  
      Wire.endTransmission();
      delay(1000);
     
}

IMAGES :
http://img96.xooimage.com/views/5/b/f/dscf0397-42ec4fc.jpg/
http://img98.xooimage.com/views/b/5/f/dscf0340-42ec4e7.jpg/

Salut,

Tu étais tombé la dessus ? http://www.blog.zapro.dk/wp-content/I2C_display_0_9_A_H.pde

Bonjour

Merci de ta réponse.

Oui, j'ai vu ça. Dans cet exemple (avec les photos ici : http://www.blog.zapro.dk/?p=121), il adresse deux PCF8576. D'abord, il effectue les réglages dans le setup, puis dans le loop il envoie des octets successivement à ses deux PICS.

Si je prend par exemple la première partie de l'envoi des données (sur l'adresse B0111001), l'envoi de chaque octet avec Wire.send semble afficher son LCD dans l'ordre (en l'occurenc,e la ligne du haut). or, mon problème c'est que l'envoi du second octet donne un affichage anarchique des segments.
Autre curiosité : dans son exemple, il place le datapointer à 0 ( Wire.send(B00000000);), si je fais la même chose, j'ai un résultat complètement incohérent. Je dois commencer à 3 (je ne sais pas pourquoi)

Et les autres modes de multiplexage ne donnent rien ?

Bonsoir

Merci de ta réponse. J'ai essayé à tatons, avec différents réglages et valeurs (notamment le multiplexage), pas mieux.

Néanmoins, le multiplexage avec deux backplanes et une des choses dont je suis le plus sûr : ça concorde avec le nombre de segments. De plus, les broches BP2 et BP3 n'étant pas connectée, ce n'est pas possible que ce soit du 1:3 ou 1:4.

J'ai essayé de changer aussi d'autres valeurs (p.exemple le BIAS), sans grand résultat.

Oui normalement bias et le reste n'ont pas d'influence sur l'affichage en lui même, ils permettent juste d'améliorer la lisibilité.

La j'avoue que entre Noël et le départ au ski imminent j'ai pas trop le temps d'étudier la datasheet ^^'

Mais ce que je ferais si j'étais toi, c'est de faire la brute : j'ai pas exactement tout compris au fonctionnement du PCF8576, mais de ce je comprend à la volée, le pointeur est automatiquement incrémenté. Donc fait des boucles en envoyant les données au lcd et stream les en même temps via série. Tu auras peut-être des affichages et il te suffira de noter quelle donnée a donné quelque chose et ensuite de réfléchir à comprendre pourquoi

Bonsoir

D'accord. Merci de ta réponse.

J'ai fait comme tu dis, une boucle avec toutes les valeurs que le pointeur peut accepter : 6 bits : 64 valeurs -1 (il ne veut pas de la dernière) et chaque fois j'envoie le maximum que le PCF8576 peut stocker dans sa RAM: 20 octets, chacun à sa valeur max.
Résultat : seul le pointeur paramétré avec l'adresse 0 affiche la totalité des segments.
Ca semblait bien parti :slight_smile:

http://img95.xooimage.com/views/2/2/f/010-42f6cca.jpg/

Mais lorsque j'ai voulu noter pour établir la correspondance entre chaque bit et son segment, j'ai vu que :
-le premier bit (LSB) du premier octet correspond au segment f du premier caractère (ok).
-le second bit du premier octet correspond au segment c du premier caractère (ok).
-le troisième bit du premier octet correspond au segment b du second caractère (pas ok) :astonished:

Je vais continuer à chercher. En attendant, bonne vacances. :wink:

ps : le code utilisé pour établir la boucle :

/*  PCF8576 over I2C bus.  */

#define LIAISON 128
#define NOLIAISON 0

//MODE SET
#define MODESET 64

#define MODE_NORMAL 0
#define MODE_POWERSAVING 16

#define DISPLAY_DISABLED 0
#define DISPLAY_ENABLED 8

#define BIAS_THIRD 0
#define BIAS_HALF 4

#define DRIVE_STATIC 1
#define DRIVE_2 2
#define DRIVE_3 3
#define DRIVE_4 0

byte set_modeset = MODESET | MODE_NORMAL | DISPLAY_ENABLED | BIAS_HALF | DRIVE_2; //

//BLINK
#define BLINK  112

#define BLINKING_NORMAL 0
#define BLINKING_ALTERNATION 4

#define BLINK_FREQUENCY_OFF 0
#define BLINK_FREQUENCY2 1
#define BLINK_FREQUENCY1 2
#define BLINK_FREQUENCY05 3

byte set_blink = BLINK | BLINKING_ALTERNATION | BLINK_FREQUENCY_OFF; //

//LOADDATAPOINTER
#define LOADDATAPOINTER  0

byte set_datapointer = LOADDATAPOINTER | 0; //max : 63 


//BANK SELECT
#define BANKSELECT 120

#define BANKSELECT_O1_RAM0 0
#define BANKSELECT_O1_RAM2 2

#define BANKSELECT_O2_RAM0 0
#define BANKSELECT_O2_RAM2 1

byte set_bankselect = BANKSELECT | BANKSELECT_O1_RAM0 | BANKSELECT_O2_RAM0;

#define DEVICE_SELECT 96
byte set_deviceselect = DEVICE_SELECT;

#define PCF8576 B111000

#include <Wire.h>

byte efface[20]={0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0};

void setup(){
  Serial.begin(9600);
  Wire.begin(); 
  delay(1000); //allow lcd to wake up.

  //Efface BANK SELECT 2.
  Wire.beginTransmission(PCF8576);
  Wire.write(LIAISON | set_modeset); //MODE SET 
  Wire.write(LIAISON | set_datapointer); //LOAD DATA POINTER
  Wire.write(LIAISON | set_deviceselect); //DEVICE SELECT
  Wire.write(NOLIAISON | BANKSELECT | BANKSELECT_O1_RAM2 | BANKSELECT_O2_RAM2 ); //BANK SELECT 2
  Wire.write(efface,20);
  Wire.endTransmission();

  //Efface BANK SELECT 1.
  Wire.beginTransmission(PCF8576);
  Wire.write(LIAISON | set_modeset); //MODE SET 
  Wire.write(LIAISON | set_deviceselect); //DEVICE SELECT
  Wire.write(LIAISON | set_bankselect); //BANK SELECT 1
  Wire.write(LIAISON | set_blink); //BLINK
  Wire.write(NOLIAISON | set_datapointer); //LOAD DATA POINTER
  Wire.write(efface,20);
  Wire.endTransmission();  
}

void loop(){
  for (byte pt = 0; pt < 64; pt++){

    //Efface les segments précédents
    Wire.beginTransmission(PCF8576);
    Wire.write(LIAISON | set_deviceselect); //DEVICE SELECT
    Wire.write(NOLIAISON | set_datapointer); //LOAD DATA POINTER
    Wire.write(efface,20);
    Wire.endTransmission();

    Serial.print ("Data pointer :");
    Serial.println (pt);


    Wire.beginTransmission(PCF8576);
    Wire.write(LIAISON | set_deviceselect);
    Wire.write(NOLIAISON | set_datapointer | pt); //LOAD DATA POINTER

    //DATAS
    //mémoire : 40x4 bits, soit 160 bits, soit 20 octets
    Wire.write(255);
    Wire.write(255); 
    Wire.write(255); 
    Wire.write(255);  
    Wire.write(255);      
    Wire.write(255);
    Wire.write(255);
    Wire.write(255);
    Wire.write(255);
    Wire.write(255);

    Wire.write(255);
    Wire.write(255); 
    Wire.write(255); 
    Wire.write(255);  
    Wire.write(255);      
    Wire.write(255);
    Wire.write(255);
    Wire.write(255);
    Wire.write(255);
    Wire.write(255);

    Wire.endTransmission();
    delay(1000);
  }
}

Bonjour

Effectivement, tu avais raison : le pointeur s'incrémente tout seul à chaque octet envoyé. Le PCF8576 possède deux banques, lors d'un multiplexage 1:2, seule une banque de 20 octets est utilisée.
Donc, en écrivant successivement 20 octets, on "fait la boucle".

Voilà ci-dessous mon code, modifié pour envoyer successivement les 20 octets, avec chaque fois un bit supplémentaire. Cela pour voir l'ordre d'affichage des segments.

Comme tu pourras voir sur la vidéo, l'ordre d'affichage des segments n'est pas "naturel" et je ne vois pas d'autre solution que de mapper chaque segments afin d'écrire à quoi correspond chaque chiffre selon sa position. Ca parait quand même curieux comme conception, mais peut être que les contraintes de câblage empechaient de les relier d'une façon plus logique. (c'est la seule explication que j'ai trouvé).

Voilà la vidéo, on voit sur l'écran, derrière, les octest/bits envoyés.

Le code :

#define LIAISON 128
#define NOLIAISON 0

//MODE SET
#define MODESET 64

#define MODE_NORMAL 0
#define MODE_POWERSAVING 16

#define DISPLAY_DISABLED 0
#define DISPLAY_ENABLED 8

#define BIAS_THIRD 0
#define BIAS_HALF 4

#define DRIVE_STATIC 1
#define DRIVE_2 2
#define DRIVE_3 3
#define DRIVE_4 0

byte set_modeset = MODESET | MODE_NORMAL | DISPLAY_ENABLED | BIAS_HALF | DRIVE_2; //

//BLINK
#define BLINK  112

#define BLINKING_NORMAL 0
#define BLINKING_ALTERNATION 4

#define BLINK_FREQUENCY_OFF 0
#define BLINK_FREQUENCY2 1
#define BLINK_FREQUENCY1 2
#define BLINK_FREQUENCY05 3

byte set_blink = BLINK | BLINKING_ALTERNATION | BLINK_FREQUENCY_OFF; //

//LOADDATAPOINTER
#define LOADDATAPOINTER  0

byte set_datapointer = LOADDATAPOINTER | 0; //11 pour le départ. 15 max : 63 (58?)


//BANK SELECT
#define BANKSELECT 120

#define BANKSELECT_O1_RAM0 0
#define BANKSELECT_O1_RAM2 2

#define BANKSELECT_O2_RAM0 0
#define BANKSELECT_O2_RAM2 1

byte set_bankselect = BANKSELECT | BANKSELECT_O1_RAM0 | BANKSELECT_O2_RAM0;

#define DEVICE_SELECT 96
byte set_deviceselect = DEVICE_SELECT;

#define PCF8576 B111000

#include <Wire.h>

void setup(){
  Serial.begin(9600);
  Wire.begin();
  delay(1000); //allow lcd to wake up.

  //COMMANDS
  Wire.beginTransmission(PCF8576);
  Wire.write(LIAISON | set_modeset); //MODE SET 
  Wire.write(LIAISON | set_deviceselect); //DEVICE SELECT
  Wire.write(LIAISON | set_bankselect); //BANK SELECT 1
  Wire.write(NOLIAISON | set_blink); //BLINK
  Wire.endTransmission();  
}

void loop(){
    //Envoi de 10 octets
    for (byte j = 0; j < 10; j++){
      
      byte envoi = 1;
      //8 bits / octets 
      for (byte i = 0; i < 8; i++){   
        
        //Monitoring
        Serial.print ("Envoi ");
        Serial.print (j+1);
        Serial.print (" octet(s) : ");
        for (byte k = 0; k <= j-1; k++){  Serial.print ("11111111 "); }
        Serial.println (envoi,BIN);
               
        //Envoi       
        Wire.beginTransmission(PCF8576);
        Wire.write(NOLIAISON | set_deviceselect);
        //datas
        for (byte k = 0; k <= j-1; k++){  Wire.write(0xFF); }
        Wire.write(envoi);
        for (byte k = j; k <= 8; k++){  Wire.write(0); }
        Wire.endTransmission();
        
        envoi = envoi << 1 | 1; //rotation à gauche et ajout d'un bit  (ou * 2 + 1;)
        delay(250);
      }
    }
}

Bonjour

Puisque manifestement les segments n'étaient pas cablés dans un ordre "naturel", j'ai écrit un petit code pour affficher des chiffres sur l'ecran.

http://img99.xooimage.com/views/b/d/e/005-431044c.jpg/

Je pense qu'il n'y avait que ça à faire, et j'ai mis résolu dans le titre.

Voilà le code, si qqn a un pb similaire.

/*
PCF8576 chips over I2C bus.
*/

#define LIAISON 128
#define NOLIAISON 0

//MODE SET
#define MODESET 64

#define MODE_NORMAL 0
#define MODE_POWERSAVING 16

#define DISPLAY_DISABLED 0
#define DISPLAY_ENABLED 8

#define BIAS_THIRD 0
#define BIAS_HALF 4

#define DRIVE_STATIC 1
#define DRIVE_2 2
#define DRIVE_3 3
#define DRIVE_4 0

byte set_modeset = MODESET | MODE_NORMAL | DISPLAY_ENABLED | BIAS_HALF | DRIVE_2; //

//BLINK
#define BLINK  112

#define BLINKING_NORMAL 0
#define BLINKING_ALTERNATION 4

#define BLINK_FREQUENCY_OFF 0
#define BLINK_FREQUENCY2 1
#define BLINK_FREQUENCY1 2
#define BLINK_FREQUENCY05 3

byte set_blink = BLINK | BLINKING_ALTERNATION | BLINK_FREQUENCY_OFF; //

//LOADDATAPOINTER
#define LOADDATAPOINTER  0

byte set_datapointer = LOADDATAPOINTER | 0; //11 pour le départ. 15 max : 63 (58?)


//BANK SELECT
#define BANKSELECT 120

#define BANKSELECT_O1_RAM0 0
#define BANKSELECT_O1_RAM2 2

#define BANKSELECT_O2_RAM0 0
#define BANKSELECT_O2_RAM2 1

byte set_bankselect = BANKSELECT | BANKSELECT_O1_RAM0 | BANKSELECT_O2_RAM0;

#define DEVICE_SELECT 96
byte set_deviceselect = DEVICE_SELECT;

#define PCF8576 B111000

#include <Wire.h>

/*chaque ligne représente :
-position dans l'afficheur 
-segment 0 à 7
-mappage de chaque segment
codage [position][segment a à g][donnees du segment]
*/
byte codage[10][7][10]={
  { {0,64},{0,16},{0,32},{0,8},{2},{1},{0,128} },
  { {16},{4},{8},{0,4},{128},{64},{32} },
  { {0,0,0,16},{0,0,0,64},{0,0,0,128},{0,0,0,0,4},{0,0,0,8},{0,0,0,4},{0,0,0,32} },
  { {0,0,0,0,64},{0,0,0,1},{0,0,0,2},{0,0,0,0,8},{0,0,0,0,32},{0,0,0,0,16},{0,0,0,0,128} },
  { {0,0,128},{0,0,32},{0,0,16},{0,0,4},{0,0,1},{0,0,2},{0,0,8} },
  { {0,0,64},{0,0,0,0,0,0,0,0,0,2},{0,0,0,0,0,0,0,0,0,1},{0,0,0,0,0,0,0,0,0,4},{0,0,0,0,0,0,0,0,0,16},{0,0,0,0,0,0,0,0,0,32},{0,0,0,0,0,0,0,0,0,8} },
  { {0,2},{0,0,0,0,0,0,0,0,0,128},{0,0,0,0,0,0,0,0,0,64},{0,0,0,0,0,0,0,0,1},{0,0,0,0,0,0,0,0,4},{0,0,0,0,0,0,0,0,8},{0,0,0,0,0,0,0,0,2} },
  { {0,0,0,0,2},{0,0,0,0,0,0,0,0,32},{0,0,0,0,0,0,0,0,16},{0,0,0,0,0,0,0,0,64},{0,0,0,0,0,0,16},{0,0,0,0,0,0,32},{0,0,0,0,0,0,0,0,128} },
  { {0,0,0,0,1},{0,0,0,0,0,0,128},{0,0,0,0,0,0,64},{0,0,0,0,0,1},{0,0,0,0,0,4},{0,0,0,0,0,8},{0,0,0,0,0,2} }
};

//Ordres d'affichage des segments pour chaque chiffre de 0 à 9
byte chiffre[10][8]={ {0,1,2,3,4,5,7}, {1,2,7}, {0,1,3,4,6,7}, {0,1,2,3,6,7}, {1,2,5,6,7}, {0,2,3,5,6,7}, {0,2,3,4,5,6,7}, {0,1,2,7}, {0,1,2,3,4,5,6,7}, {0,1,2,3,5,6,7} };

byte affichage_sortie[10]; //buffer d'octets à envoyer

void setup(){
  Wire.begin();
  delay(1000); //allow lcd to wake up.
 initialise();
}

void loop(){
  //affichage("yyyyyxxxx");  yyyyy Chiffres du bas. xxxx : chiffres du haut  
  affichage("401234");
 
};

void initialise(){
  Wire.beginTransmission(PCF8576);
  Wire.write(LIAISON | set_modeset); //MODE SET
  Wire.write(LIAISON | set_deviceselect); //DEVICE SELECT
  Wire.write(LIAISON | set_bankselect); //BANK SELECT 1
  Wire.write(LIAISON | set_blink); //BLINK
  Wire.write(NOLIAISON | LOADDATAPOINTER); //
  Wire.endTransmission();
}

void affichage(char chaine[]){
  for (int i=strlen(chaine)-1; i > -1; i-- ){
    int pos = strlen(chaine)-i-1;
    int chf = char(chaine[i])-48; 
    if (chf>-1 && chf<10){
      for (int j=0; j < sizeof(chiffre[chf]);j++ ){
        int segment = chiffre[chf][j];
        if (segment==7) break;
        for (int k=0; k < sizeof(codage[pos][segment]);k++ ){
          for (int l=0; l < sizeof(codage[pos][segment][k]);l++ ){
            int ordre_segment = codage[pos][segment][k];
            affichage_sortie[k] = affichage_sortie[k] | codage[pos][segment][k];
          }
        }
      }
    }
  }
  envoi();
  memset (affichage_sortie,0,10); //efface le buffer
}

void envoi(){
  Wire.beginTransmission(PCF8576);
  Wire.write(LIAISON | set_deviceselect); //DEVICE SELECT
  Wire.write(NOLIAISON | LOADDATAPOINTER); //BLINK
  Wire.write(affichage_sortie,10);
  Wire.endTransmission();
}

Bonjour,

Tout d'abord, je souhaite vous remercier d'avoir rendu public votre code.

J'utilise le même type d'écran et je n'arrive pas à afficher le segment f du premier chiffre en bas à gauche (le chiffre des centaines de milliers de km).

Avez-vous réussi à afficher ce segment? Car sur votre vidéo, j'ai aussi l'impression que le segment ne s'allume pas non plus.