Lass uns lieber den Bus nehmen

Textausgabe auf dem LC-Display

Arduino, ESP32, Raspberrypi und viele andere Mikrocontroller bringen von Haus aus keine Anzeigemöglichkeit mit. Besäßen sie eine eingebaute Anzeige, wäre diese

  • in der Regel nicht abzulesen (weil das Controllerboard irgendwo verbaut ist und sich damit den Blicken entzieht)
  • vermutlich vom falschen Format (zu groß, zu klein, falsche Auflösung …)
  • überflüssig (weil das konkrete Projekt gut davon keinen Gebrauch macht)
  • mit zusätzlichen Kosten für das Board verbunden.

Das erste „Projekt“ befasst sich demnach mit der Darstellung einer einfachen Textausgabe, wie sie auch auf seriellen Monitor ausgegeben werden könnte.

Material:

  • Arduino UNO oder Arduino NANO
  • LCD1602
  • Potenziometer 10kOhm
  • Widerstand 220 Ohm
  • Steckbrett
  • diverse Steckbrücken

LC-Display parallel angesteuert

Das Display LCD1602 kann 16 Zeichen in 2 Zeilen ausgeben. Es besitzt 16 entsprechend ihrer Funktion Kontakte.

Die Belegung der Anschlüsse kann man zum Beispiel diesem Wiki entnehmen:

http://wiki.sunfounder.cc/index.php?title=LCD1602_Module

Pin Funktion
VSS Masse / GND
VDD + 5V
VO Kontrast
RS Register Select
R/W Read / Write – Modus
E Enable Wenn High, dann Register beschreibbar
D0-D7 Datenbits die als ASCII-Zeichen geschrieben werden sollen
A Kontrolle der Hintergrundbeleuchtung (Anode)
K Masse / GND-Verbindung der Hintergrundbeleuchtung (Kathode)

Für die Übertragung der Daten auf das Display gibt es einen 4bit-Modus und einen 8bit-Modus. Um nicht gleich 8 Datenleitungen auf dem Arduino belegen zu müssen, verwendet dieses Beispiel den 4bit-Modus. Zusätzlich zu den 4 Datenleitungen sind weitere Leitungen für die Steuerung des Lese/Schreib-Registers, die Enable-Leitung … erforderlich. Insgesamt also 11 Leitungen, wenn die Spannungsversorgung und die unvermeidbare Masseleitung mitgezählt werden.

Die Umsetzung der zu sendenden Daten in eine dem Display-Controller verständliche Form übernimmt die LiquidCrystal-Bibliothek. Ist diese Bibliothek installiert, kann unter Datei → Beispiele → LiquidCrystal → Hello World den Code in die IDE laden. In der Erläuterung des Codes finden sich die für den Anschluss des Displays an einen Arduino UNO erforderlichen Hinweise. Das Beispiel findet sich mit bebilderter Erläuterung ebenfalls hier: https://www.arduino.cc/en/Tutorial/LibraryExamples/HelloWorld

Wie das Foto im Vergleich zu den Schemazeichnungen des Tutorials der Arduino-Seite eindrucksvoll belegt, ist die Übersichtlichkeit des Aufbaus begrenzt. Die Störanfälligkeit nimmt mit jedem Bauteil / jeder Steckbrücke zu. Im Gegensatz zu vergleichbaren Aufbauten auf Funduino.de oder anderen Webseiten wird die LED der Hintergrundbeleuchtung über einen 220 Ohm Vorwiderstand betrieben, was ihrer Dauerfestigkeit sehr entgegen kommen dürfte. Das 10 kOhm-Potenziometer wurde bauartbedingt etwas von der Vorgabe des Tutorials abweichend verbaut.

Der zusätzliche Anschluss von Schrittmotoren, Tastern, Echtzeituhr, SD-Kartenlesegerät und anderer Peripherie erfordert schnell mehr digitale Ein- und Ausgänge als einem Mikrocontroller zur Verfügung stehen können. Eine mögliche Lösung kann in der Umsetzung der Kommunikation mittels BUS liegen. BUS steht für „Binary Unit System“ und organisiert die digitale Kommunikation zwischen den angeschlossenen Teilnehmern. Eine andere Wortherkunft könnte auch „Back Panel Unit Sockets“ sein. Dem BUS liegt eine lineare elektrische Grundstruktur zugrunde. Wenn mehrere Teilnehmer sich über einige wenige gemeinsame elektrische leitungen unterhalten entstehen Notwendigkeiten systematischer und elektronischer Strukturen, die ein gleichzeitiges Senden von Daten ebenso unterbinden wie elektrischen Phänomene wie Übersprechen, Spiegelung an freien Enden etc. Entweder erhalten alle Teilnehmenden eigene Adressen sowie eine hierarische Position zugewiesen oder es werden zusätzliche Leitungen implementiert, die ein Mischsystem aus paralleler und serieller Struktur schaffen. Der letzte Fall ist im SPI-Bus implementiert. Hier entscheidet ein sogenannter Master mittels einer einzelnen Select-Leitung, welchen Partner (Slave) die darauf folgende Kommunikation betrifft. Andere BUS-Systeme kennen individuelle Adressen für die Teilnehmenden. Dies trifft beispielsweise für den I²C– und den PCI-Bus zu.

Die tiefere auch elektronische Betrachtung von I²C- und SPI-Bus ist dem Abschnitt zum Logikanalysator vorbehalten. Hier soll es zunächst um die Möglichkeiten der Vereinfachung der Ansteuerung / Kommunikation durch Nutzung von Bussystemen gehen. Für das im vorangegangenen Beispiel verwendete LC-Display LCD1602 gibt es einen Logik-Baustein, der einerseits auf Befehler auf dem I²C-Bus lauscht und diese, wenn er über seine Adresse angesprochen wird, in parallele Ausgaben entsprechend der direkten Parallelansteuerung des Displays umsetzt. Um die Lesbarkeit der Beschriftung zu wahren, wurde der Baustein entgen seiner korrekten Einbaurichtung (180° verdreht) abgebildet. Der Pin mit der Bezeichnung K (Kathode) rechts auf dem Display-Modul ist demnach mit dem linken Pin des I²C-Schnittstellenmoduls zu verbinden. Das Schnittstellenmodul verfügt zudem über ein eingebautes Potenziometer zur Regelung der Kontraststärke sowie eine steckbrücke (ganz links im Bild) über welche die im Display eingebaute LED aktiviert wird.

 

Die auf der rechten Seite des Schnittstellenmoduls herausgeführten Kontakte sind mit dem I²C-Bus zu verbinden. Ihre Benennung legt zugleich die Funktion nahe. „GND“ ist mit der gemeinsamen Masse, VCC mit dem +5 V-Anschluss zu verbinden. Hinzu kommen 2 für die Kommunikation verantwortliche, neue Leitungen. „SDA“ steht dabei für die Datenleitung (Serial Data) und „SCL“ steht für die Taktleitung (Serial Clock). Auf dem gesamten Bus herrscht für alle angeschlossenen Baugruppen demnach ein gemeinsamer Takt (der sich aber ggf. von einzelnen Bausteinen bei Bedarf etwas ausbremsen lässt). Der Bus arbeitet mit positiver Logik. Ein „High“-Pegel wird erkannt, wenn das Potenzial der Datenleitung maher als das 0,7-fache der Versorgungsspannung (VCC) beträgt. Ein „LOW“-Pegel wird erkannt, wenn der Pegel der Datenleitung weniger als das 0,3-fache des Versorgungspotenzials beträgt. Diese Aussage beinhaltetzugleich, dass es KEINE einheitlich definierte Versorgungsspannung gibt. Ein 3,3 V-System wird somit andere „HIGH“-Pegel zeigen als ein 5 V-System.

Wenn Adressen über das Ansprechen eines Bauteils entscheiden, wie kommt ein Bauteil zu seiner Adresse? Muss die Adresse eine unveränderbare Größe sein? Wie erfährt man die Adresse eines Bauteils?

I²C-Adressen können fest im Bauteil im Laufe der Fertigung eingeprägt worden sein. Manche Bauteile lassen eine nachträgliche Änderung der Adresse über eine Programmierung zu, bei der die neue Adresse dann im einem nichtflüchtigen Speicher des Bauteils abgelegt wird. Auch ein Arduino kann in dieser Weise mit einer programmierbaren Adresse versehen werden, wenn er als „Slave“ in einem I²C-System programmiert wird.

Die Adresse steht oft im Datenblatt des Bauteils. Das obige LCD-Schnittstellenmodul verfügt über lötbare Konfigurationsmöglichkeiten seiner I²C-Adresse. Hierzu werden die senkrecht übereinander befindlichen Lötpunkte nach Bedarf miteinander verbunden. Auf diese Weise können besipeilsweise mehrere LCD1602 über Schnittstellenmodule von einem Arduino angesprochen werden.

Wenn es keine Dokumentation zu der genauer I²C-Adresse gibt, hilft ein I²C-Scanner. Da der I²C-Bus sowohl mit 7 Bit, mit 8 Bit als auch mit 10 Bit adressiert werden kann und auch unterschiedliche Taktraten gebräuchlich sind soll und damit zunehmend komplexe Situationen bei der Abfrage/Suche entstehen können, wird hier nur auf einen einfachen Scanner eingegangen. https://playground.arduino.cc/Main/I2cScanner/

Der Sanner ist kein besonderes Gerät sondern ein Programm / Sketch, das auf dem Arduino läuft. Es setzt die Nutzung der „Wire“-Bibliothek voraus, die standardmäßig in der Arduino-IDE enthalten ist. Achtung! Nur bestimmte Ausgangspins eines Arduino sind für die Kommunikation als I²C-Bus geeignet. Beim Aurduino UNO und NANO sind dies die Analog-Eingangspins A5 (SCL) und A4 (SDA). Der Arduino MEGA sieht dafür separate Pins vor.

Das über den Mikrocontroller mit dem zu untersuchenden Bauteil verbundene I²C-Scanner-Programm spricht die möglichen im Bus-System verfügbaren Adressen der Reihe nach an und prüft, ob es bei Auswahl einer Adresse ein „Ackowledge“ – also eine Bestätigung – und keine Fehlermeldung bekommt. Die Bestätigung kann nur von einem Slave gesendet worden sein, der die Anfrage verstanden hat. Werden also nur einzelne Slaves mit dem Scannerprogramm geprüft, kann somit deren Adresse ermittelt werden. Heir lohnt ein Blick in den oben verlinkten Quellcode. Auf gleicher Seite befindet sich auch Links zu anderen teilweise erheblich umfangreicheren I²C-Scannerprogrammen.

Mit diesem Ergebnis eines Scans vom LCD-Schnittstellen-Modul kann die Schnittstelle angesprochen werden.

Material:

  • Arduino UNO oder Arduino NANO
  • LCD1602
  • I²C-Schnittstellenmodul
  • Steckbrett
  • diverse Steckbrücken

Der folgende Sketch wurde mit der obigen I²C-Adresse des Schnittstellenmoduls verwendet:

// Von Webseite https://funduino.de/nr-19-i%C2%B2c-display
// übernommerner und angepasster Sketch. Die I²C-Adresse wurde dem Ergebnis eines I²C-Scanners
// entnommen.
// zur Verdeutlichung wurde der Text der ersten Zeile der Anzeige geändert
// die 2. Zeile wurde angepasst, um eine Anzeige zu generieren, die dem Muster
// von https://www.arduino.cc/en/Tutorial/LibraryExamples/HelloWorld
// folgt.
// Eine weitere Anpassung besteht in der Verwendung einer zusätzlichen Variablen, da die
// Funktion lcd.print() der LiquidCrystal_I2C die Funktion millis() nicht aufrufen und konvertieren kann.
// Zeiterfassung und Konvertierung wurden in ein zusätzliche Zeile vor den lcd.prin()-Befehl gelegt.
// Im anderen Fall wurde eine leere Zeile angezeigt.
// 11.06.2021 Stephan Schlote

#include <Wire.h> // Wire Bibliothek einbinden
#include <LiquidCrystal_I2C.h>     // Vorher hinzugefügte LiquidCrystal_I2C Bibliothek einbinden
LiquidCrystal_I2C lcd(0x27, 16, 2); //Hier wird festgelegt um was für einen Display es sich handelt.
//In diesem Fall eines mit 16 Zeichen in 2 Zeilen und der HEX-Adresse 0x27.
// Für ein vierzeiliges I2C-LCD verwendet man den Code „LiquidCrystal_I2C lcd(0x27, 20, 4)“
unsigned long myZeit;

void setup()
{
lcd.init();                        //Im Setup wird die LCD gestartet
lcd.backlight();              //Hintergrundbeleuchtung einschalten (lcd.noBacklight(); schaltet die Beleuchtung aus).
}

void loop()
{
lcd.setCursor(0, 0);       //Hier wird die Position des ersten Zeichens festgelegt. In diesem Fall bedeutet (0,0) das erste Zeichen in der ersten Zeile.
lcd.print(„Hallo I2C“);
lcd.setCursor(0, 1);      // In diesem Fall bedeutet (0,1) das erste Zeichen in der zweiten Zeile.
myZeit = millis();
myZeit = myZeit / 1000;
lcd.print(myZeit);
}

 

Das auf dem Bild linke I²C-LCD-Schnittstellenmodul ist mit einer Lötbrücke bei A0 versehen.

Der I²C-Scanner meint dazu:

Nach Änderung der I²C-Adresse im Quellcode des Sketches funktioniert das zweite LCD-Schnittstellenmodul einwandfrei. Einer gleichzeitigen Verwendung zweier LCDs mit I²C-Schnittstellenmodul steht nicht mehr im Wege. Eine genaue Anleitung gibt es zum Beispiel hier: https://funduino.de/nr-06-zwei-i%C2%B2c-displays-gleichzeitig

Der Uhr-Arduino (oder die Arduino-Uhr)

Beginnende Programmierer freuen sich am „Hallo Welt!“, das ihr erstes Programm auf den Bildschirm zaubert. Die Anfänge des Umgangs mit Mikrocontrollern macht oft ein Aufbau, der eine LED blinken lässt. Nachdem nun die Sache mit dem I²C-Bus prinzipiell eingeführt ist, eine Flüssigkristallanzeige entweder direktgesteuert oder über den I²C-Bus angeschlossen ist, fehlt noch ein sinnvoller Einsatz.

Material:

  • Arduino UNO oder Arduino NANO
  • Steckbrett
  • Uhrenbaustein DS1307
  • I²C-LCD-Schnittstellenmodul
  • LCD1602
  • diverse Steckbrücken

Echtzeituhren sind ebenfalls Bestandteil vieler Arduino-Starter-Kits. Die Module können mit unterschiedlichen Uhrenbausteinen bestückt sein. Die beiden abgebildeten Bausteine verwenden einen Echtzeituhrbaustein vom Typ DS1307. Diese Information gewinnt bei der Suche und Auswahl einer geeigneten Bibliothek Bedeutung, denn jeder Uhrenbaustein benötigt intern etwas andere Befehle und Adressen.

Wie die Beschriftung der herausgeführten Pins verrät, lassen sich die Bausteine über den I²C-Bus abfragen. Die RTClib von Adafruit ist in der Lage, mit unterschiedlichen Uhrenbausteinen zu kommunizieren – unter anderem auch mit dem DS1307. Für den vorliegenden Aufbau wird der obere  Baustein verwendet. Dessen Adresse ist 68. Sie ist jedoch in der verwendeten Bibliothek gekapselt, sodass sie nicht im Programm gesetzt oder unmittelbar eingegeben werden muss.

Nachdem die genannte RTClib in die Arduino-IDE eingebunden ist, steht unter den Beispielen „Datei“ -> „Beispiele“ -> „RTClib“ -> „ds1307“ zur Verfügung. Da am Ende Uhrenbaustein und LC-Display gemeinsam über den I²C-Bus verwendet werden sollen, ist es günstig, den Uhrenbaustein gleich auf das Steckbrett zu setzen. Dabei ist zur Vermeidung von Kurzschlüssen auf die richtige Positionierung zu achten. Der die Pins GND, VCC, SDA und SCL sind über Steckbrücken vom Steckbrett aus mit dem Arduino zu verbinden. Das Steckbrett liefert somit den physikalischen Bus, die Backplane.

Das unveränderte Demo-Programm ds1307.ino sollte laufen und das aktuelle Datum und die korrekte Uhrzeit im seriellen Monitor anzeigen. Es kann sein, dass die Kommunikationsgeschwindigkeit des seriellen Monitor anzupassen ist.

Eine Ausgabe auf dem seriellen Monitor ist nicht zielführend für eine Uhr. Nun kommt das LCD1602 zusammen mit dem I²C-Schnittstellenmodul zum Einsatz, damit die fertige Uhr nur mit einem Steckernetzteil betrieben werden kann.

Die RTClib bringt einige Beispiele mit, die in den für den Betrieb erforderlichen Code einfließen.

// Von Webseite https://funduino.de/nr-19-i%C2%B2c-display
// übernommerner und angepasster Sketch. Die I²C-Adresse wurde dem Ergebnis eines I²C-Scanners
// entnommen.
// Eine weitere Anpassung besteht in der Verwendung einer zusätzlichen Variablen, da die
// Funktion lcd.print() der LiquidCrystal_I2C die Funktion millis() nicht aufrufen und konvertieren kann.
// Zeiterfassung und Konvertierung wurden in ein zusätzliche Zeile vor den lcd.prin()-Befehl gelegt.
// Im anderen Fall wurde eine leere Zeile angezeigt.
// Das Handling der RTC1307 ist dem Beispielen der RTClib von Adafruit entnommen.
// Der Code wurde kopiert und die englischsprachigen Kommentare beibehalten.
// 11.06.2021 Stephan Schlote

#include <Wire.h> // Wire Bibliothek einbinden
#include <LiquidCrystal_I2C.h> // Vorher hinzugefügte LiquidCrystal_I2C Bibliothek einbinden
#include „RTClib.h“
LiquidCrystal_I2C lcd(0x27, 16, 2); //Hier wird festgelegt um was für einen Display es sich handelt. In diesem Fall eines mit 16 Zeichen in 2 Zeilen und der HEX-Adresse 0x27. Für ein vierzeiliges I2C-LCD verwendet man den Code „LiquidCrystal_I2C lcd(0x27, 20, 4)“
RTC_DS1307 rtc;

void setup()
{
lcd.init(); //Im Setup wird der LCD gestartet
lcd.backlight(); //Hintergrundbeleuchtung einschalten (lcd.noBacklight(); schaltet die Beleuchtung aus).
if (! rtc.begin()) {
Serial.println(„RTC nicht gefunden“);
Serial.flush();
abort();
}

if (! rtc.isrunning()) {
lcd.setCursor(0, 0);
lcd.print(„RTC läuft nicht!“);
// When time needs to be set on a new device, or after a power loss, the
// following line sets the RTC to the date & time this sketch was compiled
rtc.adjust(DateTime(F(__DATE__), F(__TIME__)));
// This line sets the RTC with an explicit date & time, for example to set
// January 21, 2014 at 3am you would call:
// rtc.adjust(DateTime(2014, 1, 21, 3, 0, 0));
}

// When time needs to be re-set on a previously configured device, the
// following line sets the RTC to the date & time this sketch was compiled
// rtc.adjust(DateTime(F(__DATE__), F(__TIME__)));
// This line sets the RTC with an explicit date & time, for example to set
// January 21, 2014 at 3am you would call:
// rtc.adjust(DateTime(2014, 1, 21, 3, 0, 0));
}

void loop()
{
DateTime now = rtc.now();
lcd.setCursor(0, 0);
lcd.print(„I2C „);
lcd.setCursor(5, 0);
char buf1[] = „DD.MM.YYYY“;
lcd.print(now.toString(buf1));
lcd.setCursor(0, 1);
lcd.print(„Zeit „);
lcd.setCursor(6, 1);
char buf2[] = „hh:mm:ss“;
lcd.print(now.toString(buf2));
}

Eine Echtzeituhr wird immer dann gebraucht, wenn Daten erhoben und gespeichert werden sollen. Im nächsten Abschnitt mutiert ein Arduino zum I²C-Slave und soll Befehle eines anderen Arduino ausführen. Ob das gut gehen wird? Im Netz gibt es eine Menge hervorragender Beispiele zur I²C-Kommunikation zwischen Arduinos.

Die Mutter der Seiten zu diesem Thema: https://www.arduino.cc/en/Tutorial/LibraryExamples/MasterWriter

Sehr gute Seiten mit Erklärungen zu I²C findet sich unter: „How I2C Communication Works and How To Use With Arduino“

https://howtomechatronics.com/tutorials/arduino/how-i2c-communication-works-and-how-to-use-it-with-arduino/

und „How to use I2C in Arduino: Communication between two Arduino Boards“

https://circuitdigest.com/microcontroller-projects/arduino-i2c-tutorial-communication-between-two-arduino

 

Zum Weiterlesen und Vertiefen:

DS3231 – Echtzeituhr auf „Wolles Elektronikkiste“

Setzen der Uhrzeit an einer RTC DS3231 RealTimeClock im Draeger-it.blog