PDA

Archiv verlassen und diese Seite im Standarddesign anzeigen : LCD 16x2 I2C adresse 0x27 und 0x3F



inka
26.12.2017, 09:50
kann man noch frohe weihnachten wünschen?

schon wieder ein ganz simples problem...

ich verwende an meinen robotern das 16x2 display, alle bisher gekauften hatten die adresse 0x27. Die neue lieferung hat aber 0x3F. Und das ist bei der bestellung nicht erkennbar :-(...
edit: im "kleingedruckten" steht es manchmal drin, habe nicht drauf geachtet :-(

Die adressen lassen sich in dem jeweiligen bereich, also 0x2 und 0x3 durch schliessen der lötbrücken A0 bis A3 verändern. Ein wechsel von 0x2 zu 0x3 geht wohl nicht, oder?

im code ist dann immer diese zeile: "LiquidCrystal_I2C lcd(0x27, 2, 1, 0, 4, 5, 6, 7, 3, POSITIVE);"

lässt sich das irgendwie so einrichten, dass die adresse erstmal abgefragt und dann festgelegt wird? Sowas wie "ifndef"?

RoboHolIC
26.12.2017, 20:27
das 16x2 display, alle bisher gekauften hatten die adresse 0x27. Die neue lieferung hat aber 0x3F. Und das ist bei der bestellung nicht erkennbar :-(...
edit: im "kleingedruckten" steht es manchmal drin, habe nicht drauf geachtet :-(.
Vermutlich handelt es sich bei der I2C-Schnittstelle am LCD um einen PCF8574 oder eben PCF8574A. Nach allem was ich zu dieser Thematik bisher gelesen hatte, ist das eine regelmäßige Quelle von Frust. Ich kann nicht herauslesen, ob du das weißt. Der A-Typ wurde bewusst geschaffen, um den Adressraum von Portexpandern, also die maximale Anzahl gleicher Bausteine an ein-und-dem-selben I2C-Bus zu verdoppeln.


Ein wechsel von 0x2 zu 0x3 geht wohl nicht, oder?
Nein, die Basisadresse kannst du nicht um-basteln, die ist im Silizium fest verankert.


im code ist dann immer diese zeile: "LiquidCrystal_I2C lcd(0x27, 2, 1, 0, 4, 5, 6, 7, 3, POSITIVE);"
lässt sich das irgendwie so einrichten, dass die adresse erstmal abgefragt und dann festgelegt wird? Sowas wie "ifndef"?
ifndef ist eine Anweisung an den Preprozessor. Damit kannst du bei der Compilierung steuern, ob die Variante ... lcd(0x27... oder ...lcd(0x3F... in das Programmfile eingebaut wird. Dann hast du eben immer genau die eine ODER die andere Display-Adresse im Code.
Alternativ kannst du bei Programmbeginn (PowerUp) einen Jumper bzw. Taste abfragen oder eine Menüfunktion zur Umschaltung mit Speicherung der Displayvariante im EEPROM realisieren. Abhängig davon wird im Programm die eine oder die andere Befehlsvariante abgearbeitet. Das ist dann flexibler. Besser noch, wenn du die Konstante durch eine Variable ersetzt, die am Programmanfang EINMAL den passenden Wert zugewiesen bekommt, Dann wird eben ...lcd(my_lcd_address.... in den Code geschrieben und die Umschaltung kann an EINER Stelle im Programm erfolgen.
Kommt eben drauf an, was genau du benötigst.

Rabenauge
26.12.2017, 21:29
In speziellen Fällen kannst du dir den I2C-Scanner umstricken, so dass er die Adresse ins Programm einfügt, die er gefunden hat.
Das wird aber _nicht_ funktionieren, wenn du mehrere I2C-Geräte am Bus hast, weil er nicht rausfinden kann, wer was ist.
Eventuell aber gibts da noch die Möglichkeit, beispielsweise zwischen einer RTC und nem Display-zumindest grob- zu unterscheiden, indem du auswertest, was vom jeweiligen Busteilnehmer rein kommt.
Wackelige Geschichte, aber _kann_ funktionieren.

inka
13.03.2018, 15:15
so, die dritte bestellung der I2C adapter ist nun auch schiefgelaufen, weil sich die chinesen den teufel drum scheren ob man was zu der bestellung dazuschreibt oder nicht, die liefern irgendwas - und nun wiederholt teile mit der adresse 0x3F. Immerhin unschlagbar billig.

.ifndef ist eine Anweisung an den Preprozessor. Damit kannst du bei der Compilierung steuern, ob die Variante ... lcd(0x27... oder ...lcd(0x3F... in das Programmfile eingebaut wird. Dann hast du eben immer genau die eine ODER die andere Display-Adresse im Code.
ich würde gerne beide varianten "blind" einsetzetn und meine (geschätzt) 100 sketches jetzt nur einmal so anpassen, dass ich mir ein "modul" baue --


jetzt sieht es in etwa so aus:



#include <LCD.h>
#include <LiquidCrystal_I2C.h>


LiquidCrystal_I2C lcd(0x3F, 2, 1, 0, 4, 5, 6, 7, 3, POSITIVE);


im setup():
{
.....
lcd.begin(16, 2);
lcd.clear();
lcd.setCursor(0, 0);
lcd.setBacklight(HIGH);
lcd.print("xyz-sketch");
lcd.setCursor(0, 1);
lcd.print("xyz-sketch");
delay(2000);
lcd.clear();
.....
}




-- der überall identisch ist und automatisch erkennt, welches I2C-modul eingesetzt ist. Wie muss ich das jetzt mit dem "#ifndef" angehen? Ich bin da - wie schon oft - ahnungslos :-(
greift z.b. #ifndef auf die hardware zu? Oder muss ich irgendwie einen I2C scanner im vorfeld laufen lassen?:confused:

Klebwax
13.03.2018, 16:14
Oder muss ich irgendwie einen I2C scanner im vorfeld laufen lassen?:confused:

Na einen ganzen Scanner braucht man nicht, es können ja nur 2 Adressen vorkommen. Du schickst einfach ein Start, die erste Adresse mit R/W Bit auf 0 und ein Stop. Kommt ein ACK, hast du das Display gefunden und du merkst dir die Adresse in einer Variable und benutzt die zukünftig. Kommt kein ACK probierst du das gleiche mit der zweiten Adresse. Kommt da auch kein ACK ist kein Display angeschlossen.

MfG Klebwax

inka
13.03.2018, 16:42
Na einen ganzen Scanner braucht man nicht, es können ja nur 2 Adressen vorkommen. Du schickst einfach ein Start, die erste Adresse mit R/W Bit auf 0 und ein Stop. Kommt ein ACK, hast du das Display gefunden und du merkst dir die Adresse in einer Variable und benutzt die zukünftig. Kommt kein ACK probierst du das gleiche mit der zweiten Adresse. Kommt da auch kein ACK ist kein Display angeschlossen.MfG Klebwaxmit start, stop und ACK meinst du auf dem I2C bus, oder? Läuft die anfrage nicht in der loop? Ich muss aber die adresse bereits vor dem setup für
LiquidCrystal_I2C lcd(0x3F, 2, 1, 0, 4, 5, 6, 7, 3, POSITIVE);haben... Wie geht das zusammen?

HaWe
13.03.2018, 20:20
mit start, stop und ACK meinst du auf dem I2C bus, oder? Läuft die anfrage nicht in der loop? Ich muss aber die adresse bereits vor dem setup für
LiquidCrystal_I2C lcd(0x3F, 2, 1, 0, 4, 5, 6, 7, 3, POSITIVE);haben... Wie geht das zusammen?

Arduino hat dafür Wire() Befehle, die auch im Arduino i2c Scanner verwendet werden -
und zum Testen von Adressen brauchst du das Display nicht initialisieren, der Scanner findet aktive i2c Geräte von alleine.

inka
14.03.2018, 15:35
ich hab jetzt das hier gemacht:


#include <Wire.h>

byte I2CAdd;
byte I2CAdd_hex;
byte fehler;
boolean done = false;

//0x3F = 63
//0x27 = 39

void setup()
{
Serial.begin(115200);
Wire.begin();
}

void loop()
{
if (!done)
{
I2c_suche();
}

//weiterer code
}

void I2c_suche()
{
Wire.setClock(100000L);
for (I2CAdd = 28; I2CAdd < 64; I2CAdd++)
{
Wire.beginTransmission(I2CAdd);
fehler = Wire.endTransmission();

if (fehler == 0)
{
Serial.print("Baustein mit Adresse 0x");
Serial.print(I2CAdd, HEX);
Serial.println(" gefunden!");
I2CAdd_hex = (I2CAdd);
Serial.println(I2CAdd_hex, HEX);
done = true;
}
}
}


hier kann ich immerhin die "3F" ausdrucken, also ist eine übertragung in eine variable auch kein problem. Die LCD initislierung erwartet aber ein"0x3F" - wie mache ich das?

EDIT: es funktioniert auch mit "0x27"

HaWe
14.03.2018, 16:14
ich hab jetzt das hier gemacht:

hier kann ich immerhin die "3F" ausdrucken, also ist eine übertragung in eine variable auch kein problem. Die LCD initislierung erwartet aber ein"0x3F" - wie mache ich das?

EDIT: es funktioniert auch mit "0x27"

1 i2c Chip kann nur 1 i2c Adresse haben, es kann also nur 1 von beiden stimmen.

inka
14.03.2018, 16:19
ich habe es natürlich an zwei verschiedenen adaptern (0x27 und 0x3F) und das nacheinander bzw. getrennt getestet :-)

eine idee zu "0x"?

HaWe
14.03.2018, 16:37
ich habe es natürlich an zwei verschiedenen adaptern (0x27 und 0x3F) und das nacheinander bzw. getrennt getestet
das hättest du aber schreiben müssen!


eine idee zu "0x"?
wieso? du hast es doch oben schon selber so gemacht - nimm einfach die richtige:

LiquidCrystal_I2C lcd(0x3F, 2, 1, 0, 4, 5, 6, 7, 3, POSITIVE);

Hex-und Dezimal-Schreibweise (0x3F oder 63) sind aber nur formatierte Textstring-Schreibweisen ein-und derselben Zahl, du kannst sie schreiben wie du willst, dem Programm ist es egal.
Damit er weiß, wenn du eine Hex-Schreibweise verwendest, musst du eben 0x davor schreiben, machst du es nicht, weiß er dass eine Dezimalschreibweise gemeint ist. Die Zahl an sich wäre in einer Variablen aber immer identisch (binär) kodiert.

Eine Variable kannst du in der globalen Instantiierung allerdings nicht einsetzen, daher musst du immer beide LCD-Instanzen definieren und alle lcd-print etc. doppelt schreiben (wobei dann ein LCD von beiden unbenutzt ist und dessen Lese/Schreibvorgänge jeweils im Nirwana enden)


LiquidCrystal_I2C lcd1(0x3F, 2, 1, 0, 4, 5, 6, 7, 3, POSITIVE);

LiquidCrystal_I2C lcd2(0x27, 2, 1, 0, 4, 5, 6, 7, 3, POSITIVE);

inka
14.03.2018, 16:54
Eine Variable kannst du in der Instantiierung allerdings nicht einsetzen, daher musst du immer beide LCD-Instanzen definieren und alle lcd-print etc. doppelt schreiben (wobei dann eine von beiden unbenutzt ist und deren Lese/Schreibvorgänge jeweils im Nirwana enden)


LiquidCrystal_I2C lcd1(0x3F, 2, 1, 0, 4, 5, 6, 7, 3, POSITIVE);

LiquidCrystal_I2C lcd2(0x27, 2, 1, 0, 4, 5, 6, 7, 3, POSITIVE);

Das nützt mir aber kaum etwas, ich muss dann trotzdem immer wieder schreibzugriffe auf beide lcd's (lcd1 und lcd2) angeben...

Eigentlich dachte ich durch die suche der jeweils verbauten I2c adresse dass ich die teile einfach beliebig einsetzen kann ohne mich groß um die tatsächliche adresse kümmern zu müssen...

gibt es evtl. noch andere möglichkeiten? Z.b. wie hier von RoboHolIC (https://www.roboternetz.de/community/members/41978-RoboHolIC) ausgeführt:


#ifndef ist eine Anweisung an den Preprozessor. Damit kannst du bei der Compilierung steuern, ob die Variante ... lcd(0x27... oder ...lcd(0x3F... in das Programmfile eingebaut wird. Dann hast du eben immer genau die eine ODER die andere Display-Adresse im Code.

HaWe
14.03.2018, 16:58
Das nützt mir aber kaum etwas, ich muss dann trotzdem immer wieder schreibzugriffe auf beide lcd's (lcd1 und lcd2) angeben...

Eigentlich dachte ich durch die suche der jeweils verbauten I2c adresse dass ich die teile einfach beliebig einsetzen kann ohne mich groß um die tatsächliche adresse kümmern zu müssen...

gibt es evtl. noch andere möglichkeiten? Z.b. wie hier von RoboHolIC (https://www.roboternetz.de/community/members/41978-RoboHolIC) ausgeführt:

per #ifdef legst du es VOR der Kompilierung fest, NACH der Kompilierung ist es wieder nur eine unveränderliche Konstante, so, als ob du sie von vornherein als diese Konstante reingeschrieben hättest.
Da deine globale Instantiierung aber auch zwingend eine Konstante benötigt für die Adresse, und keine Variable, sehe ich mit dieser Lib keine andere Möglichkeit, ohne die komplette Lib umzuschreiben.

inka
14.03.2018, 17:03
ok, danke, verstanden...
:-(

HaWe
14.03.2018, 18:34
Falls deine lcd Lib aber grundsätzlich eine Variable als i2c-Adresse für die Instantiierung erlaubt und nicht immer zwingend eine Konstante fordert, dann ginge es allerdings lokal:


- eine globale Adressen-Variable lcdaddr definieren und mit 0x27 oder 0x3F vor-initialisieren
- in setup() die korrekte lcdaddr Adresse suchen und global in lcdaddr speichern (überschreiben)
- in loop() im Kopf eine Instanz mit lcdaddr erzeugen

inka
16.03.2018, 12:56
ich hab jetzt alles mögliche versucht, habe die initialisierung wie auch die I2C-suche xmal hin und her verschoben, muss mich wohl damit abfinden, dass es wirklich nicht geht...



#include <Wire.h>
#include <LCD.h>
#include <LiquidCrystal_I2C.h>


byte I2CAdd;
byte LCDadr = 0x27; //phantom_adresse
byte fehler;
boolean done = false;


//0x3F = 63
//0x27 = 39


//vorinitialisierung mit phantom_adresse
LiquidCrystal_I2C lcd(LCDadr, 2, 1, 0, 4, 5, 6, 7, 3, POSITIVE);


void setup()
{
Serial.begin(115200);
Wire.begin();
lcd.begin(16, 2);
lcd.clear();


// I2c_suche();
}


void loop()
{
if (!done)
{
I2c_suche();
Serial.print(I2CAdd);
Serial.print(" ");
Serial.println(LCDadr, HEX);
//initialisierung mit echter I2C adresse überschreiben
LiquidCrystal_I2C lcd(LCDadr, 2, 1, 0, 4, 5, 6, 7, 3, POSITIVE);

LCD_meldung();
}

//weiterer code
}


void I2c_suche()
{
Wire.setClock(100000L);
for (I2CAdd = 39; I2CAdd < 63; I2CAdd++)
{
Wire.beginTransmission(I2CAdd);
fehler = Wire.endTransmission();

if (fehler == 0)
{
LCDadr = I2CAdd;
Serial.print("Baustein mit Adresse 0x");
Serial.print(I2CAdd, HEX);
Serial.println(" gefunden!");
// I2CAdd_hex = (I2CAdd);
// Serial.println(I2CAdd_hex, HEX);
Serial.print(I2CAdd);
Serial.print(" ");
Serial.println(LCDadr, HEX);

done = true;
}
}
}


void LCD_meldung ()
{
lcd.setCursor(0, 0);
lcd.setBacklight(HIGH);
lcd.print("I2C scanner");
lcd.setCursor(0, 1);
lcd.print("LCD lokal");
delay(2000);
lcd.clear();
done = true;
}

HaWe
16.03.2018, 13:33
bei der i2c Suche in setup() musst du nur auf 0x3F testen, falls du mit 0x27 vorinitialisiert hast (oder umgekehrt), um ggf auf die andere Adresse umzuschalten, ansonsten muss es bleiben wie es war.

hingegen die Instantiierung
LiquidCrystal_I2C lcd(LCDadr, 2, 1, 0, 4, 5, 6, 7, 3, POSITIVE);
gehört lokal in die loop() ganz nach oben, NICHT global, habe ich doch geschrieben!

- - - Aktualisiert - - -

PS,
ggf darf dann auch
lcd.begin(16, 2);
nicht in setup() sondern auch nur in loop, da ja dort erst instantiiert wird.
Wiederholtes lcd.begin in jeder loop muss aber evtl über eine Variable nach dem ersten Mal verhindert werden.



void loop()
{
static bool initok=false;
LiquidCrystal_I2C lcd(LCDadr, 2, 1, 0, 4, 5, 6, 7, 3, POSITIVE);

if(!initok) { // nur beim 1. Durchlauf
lcd.begin(16, 2); // kenne deine lib nicht, ggf anpassen
lcd.setCursor(0, 0); // kenne deine lib nicht, ggf anpassen
lcd.setBacklight(HIGH); // kenne deine lib nicht, ggf anpassen
lcd.print("LCD init done."); // kenne deine lib nicht, ggf anpassen
initok=true;
Serial.println("LCD init done.");
}

// Rest der loop()-Befehle

}