Batron LCD sur I2C

bonjour tout le monde,

d'abord merci pour la mine d'info dispo ici, qui m'a valu de rester longtemps 'guest' tellement quasi toute l'info possible est dispo :slight_smile:
je me suis donc inscrit car là je cale dur sur le démarrage d'un LCD en I2C et je tourne sans trouver :frowning:
comme j'ai déjà une horloge I2C, ça permet de n'utiliser aucune pin de plus, sacré avantage !
le LCD en cause est un Batron BT21605 de 2x16 caractères, sa doc est ici, il est basé sur un PCF2119
le câblage est 'classique' I2C avec les ports analog 4 et 5 de la Duemilanove, montés sans pull-up (j'ai vérifié que la librairie Wire enclenchait bien les pull-up internes). longueur des fils 20cm
son adresse I2C est 0x76 (write) pour le LCD, il peut être affublé d'un mini-clavier accessible par l'adresse 0x77 (read)
j'ai peu à peu viré le code qui n'était pas indispensable pour ne garder que l'essentiel...

voici donc le prog principal :

#include <Wire.h>
#include "LCDI2C.h"

#define LCDI2C_WRITEADDRESS  0x76 // found on www.datasheetarchive.com/datasheet-pdf/09/DSA00151661.html

LCDI2C lcd = LCDI2C(2,16,LCDI2C_WRITEADDRESS);             // Number of lines, cols and i2c address of the display

void setup() {
  Serial.begin(9600);

  // start I2C master
  Wire.begin();
   
  lcd.init();            // Init the display
  lcd.write(0x30);       // 0
  lcd.write(0x31);       // 1
  lcd.write(0x32);       // 2
  lcd.write(0x33);       // 3
  lcd.write(0x34);       // 4
  lcd.write(0x35);       // 5
  lcd.write(0x36);       // 6
  lcd.write(0x37);       // 7
  lcd.write(0x38);       // 8
  lcd.write(0x39);       // 9
  lcd.write(0x41);       // A
  lcd.write(0x42);       // B
  lcd.write(0x43);       // C
  lcd.write(0x44);       // D
  lcd.write(0x45);       // E
  lcd.write(0x46);       // F
}

void loop()
{
}

la 'librairie' LCDI2C :

#include <Wire.h>

#include "LCDI2C.h"

#include <WConstants.h>  //all things wiring / arduino

byte g_num_lines = 2;
byte g_num_col = 16;
byte g_i2caddress;

// constructor
LCDI2C::LCDI2C (int num_lines, int num_col, int i2c_address){
   g_num_lines = num_lines;
   g_num_col = num_col;
   g_i2caddress = i2c_address;
   if (g_num_lines < 1 || g_num_lines > 4){
      g_num_lines = 2;
   }
   if (g_num_col < 1 || g_num_col > 40){
      g_num_col = 16;
   }
}

// init the LCD
void LCDI2C::init () {
      on();
      clear();
      home();
}

//Turn the LCD ON
void LCDI2C::on(){
      Wire.beginTransmission(g_i2caddress | false);
      Wire.send(0x00); // control byte
      Wire.send(0x34); // command: functions (2x16, basic set)
      Wire.send(0x00); // control byte
      Wire.send(0x0C); // command: display control (display on, cursor off, blink off)
      Wire.send(0x00); // control byte
      Wire.send(0x06); // command: entry mode (increment, no shift)
      Wire.send(0x00); // control byte
      Wire.send(0x35); // command: functions (2x16, extended instruction set)
      Wire.send(0x00); // control byte
      Wire.send(0x04); // command: left-to-right, top-to-bottom
      Wire.send(0x00); // control byte
      Wire.send(0x10); // command: TC1=0, TC2=0
      Wire.send(0x00); // control byte
      Wire.send(0x42); // command: HV stage 3
      Wire.send(0x00); // control byte
      Wire.send(0x9F); // command: set VLCD, store VA
      Wire.send(0x00); // control byte
      Wire.send(0x34); // command: functions (2x16, basic set)
      Wire.send(0x00); // control byte
      Wire.send(0x80); // command: DDRAM address = 0x00
      Wire.send(0x00); // control byte
      Wire.send(0x02); // command: return home
      Wire.endTransmission();
}

//send the clear screen command to the LCD
void LCDI2C::clear(){
      Wire.beginTransmission(g_i2caddress | false);
      Wire.send(0x00);  // control byte
      Wire.send(0x01);  // command : clear display
      Wire.endTransmission();
      delay(CLRDELAY);  
}

//send the Home Cursor command to the LCD
void LCDI2C::home(){
      setCursor(0,0);// The command to home the cursor
}

void LCDI2C::setCursor(int line_num, int col_num){
      Wire.beginTransmission(g_i2caddress | false);
      Wire.send(0x00);
      if (line_num==0)
        Wire.send(0x80+col_num);
      else
        Wire.send(0xC0+col_num);
      Wire.endTransmission();
      delay(STDDELAY);
}

//get information to the display
void LCDI2C::write(unsigned char value) {
  Wire.beginTransmission(g_i2caddress | false);
  Wire.send(0x40);    // control byte for data
  Wire.send(ASCIItoLCD(value));
  Wire.endTransmission();
  delay(5);
}

unsigned char LCDI2C::ASCIItoLCD(unsigned char ch){
  unsigned char c;
  if ( ((ch >= 0x20) && (ch <= 0x3F)) || ((ch >= 0x41) && (ch <= 0x5A)) || ((ch >= 0x61) && (ch <= 0x7A)) )
    c = 0x80 + ch;
  else
    c = 0x40;
  return c;
}

le .h contient en plus ces 2 définitions :
#define CLRDELAY 165 // Delay to wait after sending clear command
#define STDDELAY 3 // normal delay

voilà...
je dois faire une erreur basique, car je semble être dans la logique de tous les autres exemples que j'ai épluché, à commencer par celui-ci
si l'un de vous maîtrise l'I2C ou utilise cet écran, je serais bien heureux d'un conseil pour décoincer :slight_smile:
donc merci d'avance :wink:

arghhhhh
toujours pas décoincé :-/

Hi TontonJules, pas facile de t'aider si on a pas le même matériel ...

Mais, utilises-tu la librairie LCDi2C d'Arduino ?
http://www.arduino.cc/playground/Code/LCDi2c
et plus exactement :
http://www.wentztech.com/radio/arduino/files/LCDI2C.html

Aussi pour tester, un montage similaire :
http://www.robot-amateur.com/Articles/Realisation_afficheur_LCD_I2C-p43.html

A+ Teiva

www.MicroDuino.fr : Votre source Arduino en France

salut Teiva,

et merci pour ta réponse :slight_smile:

je suis effectivement parti de celle-ci, et j'ai croisé les infos avec l'exemple de prog AVR245 d'Atmel pour obtenir l'adresse I2C et les codes de commande, l'écran étant le même sur cet exemple
comme ça ne marchait pas, j'ai épuré peu à peu le code pour, dans un premier temps, ne faire que l'init et une impression 'basique'...
et malgré cette simplification presque extrême, je n'ai toujours pas de résultat probant !

la librairie lcdi2c utilise la librairie wire et ce que j'ai du mal à comprendre, c'est que l'adresse n'est jamais envoyée en début de commande (à moins que ce soit fait par wire ?) et quelle est la 'position' de l'adresse dans l'octet car celui-ci contient aussi le R/W : faut-il décaler l'adresse d'un bit vers la gauche (x2) ou est-ce que wire s'en charge ?

autre point pratique : wire initialise bien les résistances pull-up sur SDA et SCL, mais faut-il quand même les câbler, le lcd demandant trop ?
en l'écrivant, je me dis que ça coute pas cher d'essayer avec... je te tiendrai au courant !
et je vais éplucher le dernier lien que tu m'as fourni :wink:
à+
Tonton Jules

La librairie Wire est ancienne et bien débugger, tu peux lui faire confiance.
C'est dans les fonctions avec adress que tu passes l'adress :

# begin()
# begin(address)
# requestFrom(address, count)
# beginTransmission(address)

Et pour être sur de ton adresse, un petit coup d'I2C Scanner :
http://todbot.com/blog/2009/11/29/i2cscanner-pde-arduino-as-i2c-bus-scanner/

A+ Teiva

www.MicroDuino.fr : Votre source Arduino en France

Teiva, je te dois un coup :wink:
un coup de scanI2C, et voila-t-y pas que c'est le 59 (dec) qui sort du chapeau, alors qu'ils parlent partout de x76
et comme par hasard, j'ai des caractères à l'écran :stuck_out_tongue:
bon, pas ceux attendus, mais ça, ça va le faire vite !
bon, je vais pouvoir reprendre le cours du projet :slight_smile:
merci, merci encore :slight_smile:

Ça fait toujours plaisir un merci
Merci

Amuse toi bien ...

A+ Teiva

www.MicroDuino.fr : Votre source Arduino en France

bon, j'ai réduit la librairie pour limiter la place (je veux que le projet tienne sur une Duemilanove), et je n'ai pas gardé les finesses graphiques...

voilà ce que donne LCDI2C.h :

// library for Batron BTHQ21605 LCD display on I2C bus
// from Digid at https://public.me.com/digid/fr/

#ifndef LCDI2C_h
#define LCDI2C_h

#define CMDDELAY   165        // Delay to wait after sending commands;
#define POSDELAY   10         // Long delay required by Position command
#define CHARDELAY  0

#include <inttypes.h>
#include "Print.h"

class LCDI2C : public Print {

public: 
   LCDI2C(int,int,int);
   void init();
   virtual void write(unsigned char ch);
   void clear();
   void home();
   void on();
   void left();
   void right();
   void printstr(const char[]);
   void setCursor(int line_num, int x);
  
private:
   unsigned char ASCIItoLCD(unsigned char ch);

};

#endif

LCDI2C.cpp :

// library for Batron BTHQ21605 LCD display on I2C bus
// from Digid at https://public.me.com/digid/fr/

#include <Wire.h>
#include <string.h> //needed for strlen()
#include <inttypes.h>
#include "WConstants.h"  //all things wiring / arduino

#include "LCDI2C.h"
  
#define LCDI2C_MAX_STRLEN      40
#define LCDI2C_PRINT_STR_DELAY 20

int g_num_lines;
int g_num_col;
int g_i2caddress;

// constructor
LCDI2C::LCDI2C (int num_lines,int num_col,int i2c_address){
   g_num_lines = num_lines;
   g_num_col = num_col;
   g_i2caddress = i2c_address;
   if (g_num_lines < 1 || g_num_lines > 4){
      g_num_lines = 2;
   }
   if (g_num_col < 1 || g_num_col > 40){
      g_num_col = 16;
   }
}

// initialisation of LCD
void LCDI2C::init () {
   on();
   clear();
}

// Turn the LCD ON
void LCDI2C::on(){
   Wire.beginTransmission(g_i2caddress | false<<0);
   Wire.send(0x00); // control byte
   Wire.send(0x34); // command: functions (2x16, basic set)
   Wire.send(0x0C); // command: display control (display on, cursor off, blink off)
   Wire.send(0x06); // command: entry mode (increment, no shift)
   Wire.send(0x35); // command: functions (2x16, extended instruction set)
   Wire.send(0x04); // command: left-to-right, top-to-bottom
   Wire.send(0x10); // command: TC1=0, TC2=0
   Wire.send(0x42); // command: HV stage 3
   Wire.send(0x9F); // command: set VLCD, store VA
   Wire.send(0x34); // command: normal instruction set
   Wire.send(0x80); // command: DDRAM address = 0x00
   Wire.send(0x02); // command: return home
   Wire.endTransmission();
   delay(CMDDELAY);  
}

//send the clear screen command to the LCD
void LCDI2C::clear(){
   setCursor(0,0);
   printstr("                ");
   setCursor(1,0);
   printstr("                ");
   setCursor(0,0);
}

//Used by the print library to get information to the display
void LCDI2C::write(unsigned char value) {
   Wire.beginTransmission(g_i2caddress | false);
   Wire.send(0x40);
   Wire.send(ASCIItoLCD(value));
   Wire.endTransmission();
   delay(CHARDELAY);
}

//send the Home Cursor command to the LCD      ********** Not Working ***************
void LCDI2C::home(){
   setCursor(0,0);
}

//Move the cursor left 1 space
void LCDI2C::left(){
   Wire.beginTransmission(g_i2caddress | false);
   Wire.send(0x00);
   Wire.send(0x10);
   Wire.endTransmission();
   delay(CMDDELAY);
}

//Move the cursor right 1 space
void LCDI2C::right(){
   Wire.beginTransmission(g_i2caddress | false);
   Wire.send(0x00);
   Wire.send(0x11);
   Wire.endTransmission();
   delay(CMDDELAY);
}

void LCDI2C::setCursor(int line_num, int x){
   Wire.beginTransmission(g_i2caddress | false);
   Wire.send(0x00);
   if (line_num==0)
      Wire.send(0x80+x);
   else
      Wire.send(0xC0+x);
   Wire.endTransmission();
   delay(POSDELAY);
}

unsigned char LCDI2C::ASCIItoLCD(unsigned char ch){
   unsigned char c;
   if ( ((ch >= 0x20) && (ch <= 0x3F)) || ((ch >= 0x41) && (ch <= 0x5A)) || ((ch >= 0x61) && (ch <= 0x7A)) )
      c = 0x80 + ch;
   else
      c = 0x40;
   return c;
}

// print a string 
void  LCDI2C::printstr(const char c[])
{
   byte len;
   while (*c)
   {
      len = min(strlen(c), LCDI2C_MAX_STRLEN);
      Wire.beginTransmission(g_i2caddress | false<<0);
      Wire.send(0x40);
      while (len--)
         Wire.send(ASCIItoLCD(*c++));
      Wire.endTransmission();
      if (*c)
         delay(LCDI2C_PRINT_STR_DELAY);      // More to send.  Wait a bit
   }
}

et le prog principal de test :

#include <Wire.h>
#include "LCDI2C.h"

#define LCDI2C_ADDRESS 0x3B // found by I2C bus scanner

LCDI2C lcd = LCDI2C(2,16,LCDI2C_ADDRESS);             // Number of lines and i2c address of the display

void setup() {
  Serial.begin(9600);
  Wire.begin();
  lcd.init();                          // Init the display, clears the display
  lcd.printstr("Hello");
  lcd.setCursor(1,0);
  lcd.printstr("world");
}

void loop()
{
}