Go Down

Topic: LCD Display über I2C am Mega mit UNO steuern? (Read 2949 times) previous topic - next topic

sschultewolter

Ich habs geschafft, ich hab nicht mal meine Schieberegister drauf, und dennoch ist der UNO relativ gut gefült.
Gestern habe ich mir in der Bucht ein LCD Display 16x2 bestellt mit I2C ohne Keypad. Dauert leider 1 Woche bis geliefert wird.

Ich habe noch ein normales LCD Keypad Shield, welches ich ohne I2C angeschlossen habe über die IO. (Keypad Tasten sind nicht angeschlossen.



Da ichs nun nicht abwarten kann, die Steuerung fertig zu stellen, die Frage. Kann ich das LCD an dem Arduino Mega schließen und dann I2C zum Arduino UNO herstellen? Ich würde den Mega gerne als I2C Slave ausnutzen für den einzigen Zweck, das Display zu nutzen.

Gruß Stefan
Orginal Atmel AVRISP mkII zu verkaufen. Anfrage per PN ;)

MGOS

Klar geht das. Schau dir mal die Beispiele der Wire.h Bibliothek "master_sender" und "slave_receiver" an. Die Steuerbefehle müsstest du halt selber implementieren, sodass sie sich das Mega gleich wie das LCD verhält.

Serenifly

Das Display müsste sowie ich das verstehe an den Master.

Wenn du Wire.begin() machst kann du einstellen um du Master oder Slave bist:
http://arduino.cc/en/Reference/WireBegin

Wenn du da eine Adresse als Parameter übergibst, ist dieser Arduino ein Slave. Slave-to-Slave Kommunikation geht aber glaube ich nicht, d.h. du kannst nicht den Mega als Slave haben und von ihm aus das LCD (das ein Slave ist) ansteuern.

Alternative: du suchst dir eine Software I2C Library, dann kannst du die Schnittstellen trennen

sschultewolter

Also bevor wir uns falsch verstehen, Serenifly.

Der Arduino UNO soll Master sein. Denn am Bus hängt ua. noch ein RTC Modul.

Vom RTC Modul möchte ich weiter gehen zum Arduino Mega. Der ebenfalls wie das RTC Modul ein Slave ist. Das LCD Display ist am Mega nicht über I2C verbunden, sonst hätte ich es direkt am Uno anschließen können.

Nutze dort die IO Kanäle des Megas.
Orginal Atmel AVRISP mkII zu verkaufen. Anfrage per PN ;)

Serenifly


sschultewolter

Okay, es geht auf jedenfall ;)


Code: [Select]
//LCD Display
#include <LiquidCrystal.h>
#include <LCDKeypad.h>
LCDKeypad lcd;

//I2C
#include <Wire.h>

void setup()
{
  Wire.begin(4);                // join i2c bus with address #4
  Wire.onReceive(receiveEvent); // register event
  Serial.begin(9600);           // start serial for output
  lcd.begin(16, 2);
  lcd.clear();
}

void loop()
{
  delay(100);
}

// function that executes whenever data is received from master
// this function is registered as an event, see setup()
void receiveEvent(int howMany)
{
  while(1 < Wire.available()) // loop through all but the last
  {
    char c = Wire.read(); // receive byte as a character
    Serial.print(c);         // print the character
    lcd.print(c);

   
  }
  int x = Wire.read();    // receive byte as an integer
  Serial.println(x);         // print the integer
    lcd.print(x);
}


Wie kann ich diesen Part am besten zusammenfassen, dass ich ans Display nur noch einen String weitergeben muss.
Orginal Atmel AVRISP mkII zu verkaufen. Anfrage per PN ;)

Serenifly

Meinst du um gleich einen ganzen String zu lesen? Dann fragst du ab ob wie viele Daten da sind und ließt diese in ein char array ein

So ähnlich:
Code: [Select]

int size = Wire.available();
char buf[size + 1];
int i = 0;

while(Wire.available() > 0)
{
    buf[i] = Wire.read();
    i++;
}
buf[i] = '\0';


sschultewolter

Ich wollte wenn möglich, jedes mal das komplette Display übertragen.

Code: [Select]
  for(int i = 0; i < 4; i++) {
    Wire.beginTransmission(4);
    switch(i) {
    case 1:
      Wire.write("     ARDUINO   ");
      Wire.write(i);
      Wire.write("################");
      break;
    case 2:
      Wire.write("     ARDUINO   ");
      Wire.write(i);
      Wire.write("################");
      break;
    case 3:
      Wire.write("     ARDUINO   ");
      Wire.write(i);
      Wire.write("################");
      break;
    default:
      Wire.write("                ");
      Wire.write("                ");
    }
Orginal Atmel AVRISP mkII zu verkaufen. Anfrage per PN ;)

Serenifly

Mhh, du kannst dir vielleicht ein kleines Daten-Telegram basteln:
Reihe | Spalte | String

Dann liest du die ersten zwei Bytes aus und machst damit lcd.setCursor(...). Und den Rest gibst du auf lcd.print(...)

Das wäre am flexibelsten. Dann kannst du immer noch zwei Zeilen schicken. Aber du kannst auch auf beliebige Positionen schreiben.


Aber: Wire.beginTransmission(4); <---- der Parameter ist die Adresse des Slaves! Die muss immer gleich sein
Und nachdem du mit allen write() fertig bist muss endTransmission() kommen

sschultewolter

Hab den Anfang denke mal so hinbekommen, wie deine Grundidee war.



Master
Code: [Select]
  Wire.beginTransmission(4);
  int x = 5;
  int y = 1;
  char z = {'a'};
  Wire.write(x);
  Wire.write(y);
  Wire.write(z);
  Wire.endTransmission();


Slave
Code: [Select]
  while(2 < Wire.available()) {
    int x =  Wire.read();
    int y = Wire.read();
    char z = Wire.read();
    lcd.setCursor(x, y);
    lcd.print(z);
    Serial.print("setCursor(");   //debug
    Serial.print(x);              //
    Serial.print(", ");           //
    Serial.print(y);              //
    Serial.println(");");         //
Orginal Atmel AVRISP mkII zu verkaufen. Anfrage per PN ;)

sschultewolter

Es klappt noch nicht ganz. Der Slave empfängt, sobald er an ist einen Case (je nach dem wo der Uno gerade ist). Diese bleibt dauerhaft eingespeichert und es ändert sich nichts.


Master
Code: [Select]
//I2C
#include <Wire.h>

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

void loop() {

  for(int i = 0; i < 7; i++) {
    byte x = 0, y;
    Wire.beginTransmission(4);
    switch(i) {
    case 1:
      Wire.write(x);
      Wire.write(0);
      Wire.write("Case 1 - Z1 ");
      break;

    case 2:
      Wire.write(x);
      Wire.write(0);
      Wire.write("Case 2 - Z1 ");   
      break;

    case 3:
      Wire.write(x);
      Wire.write(0);
      Wire.write("Case 3 - Z1 ");
      break;

    case 4:
      Wire.write(x);
      Wire.write(1);
      Wire.write("Case 4 - Z2 ");
      break;

    case 5:
      Wire.write(x);
      Wire.write(0);
      Wire.write("Case 5 - Z1 ");
      break;

    case 6:
      Wire.write(x);
      Wire.write(1);
      Wire.write("Case 6 - Z ");
      break;

    default:
      Wire.write(x);
      Wire.write(0);
      Wire.write("                 ");
      Wire.endTransmission();
      Wire.beginTransmission(4);
      Wire.write(x);
      Wire.write(1);
      Wire.write("                 ");

    }
    Wire.endTransmission();
    delay(2000);
    Serial.print("i : ");
    Serial.println(i);
  }
}



Slave
Code: [Select]
//LCD Display
#include <LiquidCrystal.h>
#include <LCDKeypad.h>
LCDKeypad lcd;

//I2C
#include <Wire.h>

void setup() {
  Wire.begin(4);
  Wire.onReceive(receiveEvent);
  Serial.begin(9600);
  lcd.begin(16, 2);
  lcd.clear();
  Serial.println();
}

void loop() {
  delay(100);
}

void receiveEvent(int howMany) {
  int x =  Wire.read();
  int y = Wire.read();
  lcd.setCursor(x, y);
  Serial.print(x);
  Serial.print(";");
  Serial.print(y);
  Serial.print(" | ");

  while(1 < Wire.available()) {
    char c = Wire.read();
    lcd.print(c);
    Serial.print(c);
  }
}
Orginal Atmel AVRISP mkII zu verkaufen. Anfrage per PN ;)

Serenifly

#11
Aug 01, 2013, 12:02 am Last Edit: Aug 01, 2013, 12:30 am by Serenifly Reason: 1
Mir ist noch eingefallen, dass hier wie bei Serial die Übertragung langsamer als das Lesen ist. Man kann also nicht mit while und available solange Lesen bis der ganze String da ist. Das wird vorher sicher terminieren, da der Master noch nicht alles geschickt hat. Den Fehler habe ich selbst oben gemacht. :(

Das kann man natürlich korrekt machen, aber das einfachste wäre doch deine Idee. Du schickst einfach beide Zeilen komplett. 32 Byte gehen glaube ich gerade so in den Empfangspuffer. Dann musst du dich darum nicht kümmern. Einfach warten bis Wire.available() == 32 (oder 16 für eine Zeile) :)

Genauso das:
 int x =  Wire.read();
 int y = Wire.read();

Da nimmst du an dass beide Zeichen schon da sind. Aber das zweite kann noch unterwegs sein. Deshalb muss da ein "if(Wire.available() >= 2)" herum. Und dann hat man das gleiche Problem mit dem Rest des Telegrams.

Du könntest vielleicht als 3. Parameter die Länge des Strings schicken und dann solange warten bis alles da ist. Oder ähnlich den Serial Beispielen auf ein NULL oder Linefeed warten. Dann muss man aber auch ständig mit if(Wire.available()) abfragen ob was da ist. Und bis der ganze String eingelesen ist darf der Eventhandler nicht verlassen werden, sonst versucht er die nächsten Bytes als Koordinaten zu lesen!

sschultewolter

Mir macht gerade noch zu bedenken, womit ich einmal auf den Threadbeginn zurückblicken möchte.

Ich habe ja vor, den Mega nur vorrübergehend als I2C Display Ersatz zu nehmen. Jetzt progammier ich aber schon einiges im Mega ein, was später garnicht möglich ist beim Display. Der Text und die Zuordnung muss doch direkt von Uno dann kommen.
Orginal Atmel AVRISP mkII zu verkaufen. Anfrage per PN ;)

Go Up