I2CLCD GREETBoard – Das I2C Display

Ein wichtiger Bestandteil des GREETBoard Projektes ist die Anbindung von Modulen über den I2C Bus. Dabei handelt es sich um einen zwei-drahtigen Bus, der über eine Takt- und eine Datenleitung verfügt.

Das I2CLCD Modul

Dieses Modul habe ich bereits in einem separatem Post beschrieben (Link). Dazu habe ich das LCD Display von Reichelt (Bestellnr: LCD 162C BL) verwendet, das von mir mit einer Steckerleiste versehen wurde. Damit passt es auch auf ein Steckbrett, was mit der Buchsenleiste nicht möglich wäre.

Das Programm

Im vorangegangenen Post zum Thema „Interrupt gesteuerte LED“ habe ich bereits ein Programm geschrieben, das eine LED ein- und ausschaltet. Dieses möchte ich nun erweitern, so dass es über das I2CLCD Modul den Zustand der LED und die durchlaufenen Blinksequenzen anzeigt.

Der I2CLCD-Treiber

Um das Display Modul zu betreiben muss es angesteuert werden könen. Für diesen Zweck wurde bereits eine Library entwickelt, die hier zum Einsatz kommt und ein paar LCD Befehle zur Verfügung stellt – die I2CLCD-Library (Link). Zusätzlich ist auch noch der bewährte I2C-Treiber von Peter Fleury (Link) nötig, da er in der I2CLCD Library eingebunden ist.

Der Code

Dem vorhandenen Programm werden nun die notwendigen Anweisungen hinzugefügt, um das Modul über I2C ansprechbar zu machen. Ich werde die neuen Anweisungen farblich markieren und die alten „ausgrauen“. Nach den Codeabschnitte erkläre ich, was sich geändert hat und warum.

#ifndef F_CPU
#define F_CPU 16000000
#endif

Diese Passage habe ich hinzugefügt, damit im Programm auch mit der richtigen Prozessorgeschwindigkeit gerechnet wird. Möglicherweise sind diese Anweisungen nicht so wichtig, aber im Zweifel füge ich sie hinzu.

#include <avr/io.h>        // Header-Datei f. IO-Register
#include <avr/interrupt.h> // Header-Datei f. Interruptfunktion
#include <stdint.h>        // Header-Datei f. standard Datentypen
#include "i2clcd.h"        // Header-Datei f. I2CLCD

Zu den Header-Dateien kommt nun die I2CLCD Library. Wie diese (und die i2cmaster.h) Datei im AVRStudio 5.1 eingebunden wird, erkläre ich weiter unten. Gerade das hat mich unglaublich viel Zeit gekostet.

volatile uint8_t tcount; // Globale Variable (volatile)
uint8_t x;               // Lokale Variable zum Schalter des Ports
int loops = 0;           // Zähler der gemachten Blinksequenzen

Da ich die Anzahl der Blinksequenzen auf dem Display sehen möchte, brauche ich hierfür einen Zähler, der zu Beginn auf 0 steht.

ISR (TIMER0_OVF_vect) {
  tcount++;             // tcount +1 pro Timer Interrupt (ca. 4ms)
}
int main(void) {        // Hauptprogramm ab hier
  x = 0;                // Flag für LED Zustand (0/1)
  char s[10];           // String-Variable für Ausgabe der Sequenzanzahl

Diese Variable im Stringformat soll später auf dem Display ausgegeben werden und die Anzahl der Blinkdurchläufe angeben. Leider kann das Display nicht direkt die Variable loops anzeigen, da es sich hier um eine Integer-Variable handelt. Diese muss vorher in einen String umgewandelt werden, wie das geht, zeige ich später.

  DDRD = (1 << DDD7);      // PortD7 als Ausgang für LED1

  TCCR0 = (1<<CS02);       // Prescaler 256
  TIMSK |= (1<<TOIE0);     // Overflow Interrupt erlauben
  sei();                   // Global Interrupts aktivieren

  i2c_init();              // I2C initialisieren
  lcd_init();              // LCD initialisieren
  lcd_command(LCD_CLEAR);  // LCD-Anzeige löschen

  // Text auf LCD ausgeben
  lcd_printlc(1,1,(unsigned char *)"GREETBoard V0.6");
  lcd_printlc(2,1,(unsigned char *)"I2C Test");

  lcd_wait_ms(3000);       // Text 3s stehen lassen
  lcd_command(LCD_CLEAR);  // Display löschen

  // weiteren Text ausgeben
  lcd_printlc(1,1,(unsigned char *)"LED Blinker");
  lcd_printlc(2,1,(unsigned char *)"LED: ");

In diesem Block werden viele neue Befehle aus der i2clcd.h und der i2cmaster.h verwendet. Um die I2C Kommunikation überhaupt durchführen zu können, benötigt der Bus eine Initialisierung durch i2c_init();. Gleich danach kann das Display mit lcd_init(); gestartet werden. Der Befehl lcd_command(LCD_CLEAR); löscht das gesamte Display, so dass alle Bildpunkte ausgeschaltet werden. Um einen Text auf das Display schreiben zu können wird der Befehl lcd_printlc(y,x,(unsigned char *)“TEXT“); verwendet. Dabei steht y für die Zeile und x für die Textstelle. „Text“ steht für den auszugebenden Text. Sollte der Text aus einer String-Variablen kommen, werden die Anführungszeichen weggelassen. Schlussendlich lässt lcd_wait_ms(x); das Display x Millisekunden warten, bevor der nächste Befehl ausgeführt wird.

  while (1) {                // Endlosschleife im Hauptprogramm
    if ( tcount > 243 ) {    // 244*4,096ms Warten (~1s)
      if ( x == 0 ) {        // Wenn x gleich 0 ist
      PORTD &= ~( (1<<PD7)); // ...deaktiviere Port D7
      lcd_printlc(2,6,(unsigned char *)"AUS");
      x = 1;                 // Setze X auf 1
     }

Da die Blinkfrequenz ein wenig schnell war (ca. 500ms) habe ich diese verdoppelt um dem Display die Chance zu geben, den LED Zustand richtig anzuzeigen. Da der Timer alle ca. 4,1ms die Variable tcount um eins hochzählt, werden die 1000ms nach ca. 244 Zyklen erreicht. Die nächste Änderung zum alten Code besteht in einer Meldung auf dem Display, dass die LED aus ist.

    else {              // Ansonsten
      loops++;            // loops +1
      itoa(loops, s, 10); // loops in String umwandeln
      PORTD |= (1<<PD7);  // ...aktiviere Port D7
      lcd_printlc(2,6,(unsigned char *)"AN ");
      lcd_printlc(2,10,(unsigned char *)s);
      x = 0;              // Setze X auf 0
    }

loops wird um eins hochgezählt, da dies die Variable ist um die Blinksequenzen zu zählen. Da diese Zahl aber nicht direkt in einem LCD-Befehl genutzt werden kann, wird sie mit itoa() in eine String-Variable verwandelt. Innerhalb der Klammer wird zuerst die umzuwandelnde Variable benannt und danach der neue String-Variablenname, gefolgt von der String-Zeichenlänge. Die beiden lcd_printlc() Befehle zeigen 1. den Zustand der LED (hier „AN“) und 2. die Anzahl der durchlaufenen Blinksequenzen, durch die umgewandelte Variable s.
Für den Zustand wurde bewusst ein Leerzeichen hinter die beiden Buchstaben gesetzt, damit dieses das S von „Aus“ abdeckt, das vor der Zustandsänderung auf dem Display steht. Dies ist deshalb wichtig, da eine Displayzeile gefüllt mit „XXXXXXXXXXXXXXXX“ (16x X) von einem lcd_printlc Befehl mit „YYY“ das folgende Ergebniss liefern würde: „YYYXXXXXXXXXXXXX“. Alte Zeichen bleiben also stehen und werden mit einem neuen Print-Befehl nicht gelöscht.

    tcount = 0; // Zähler wieder auf 0 setzen
    }
  }
}

Nichts neues im letzten Abschnitt.

Das Ergebnis

In dem Video starte ich das GREETBoard ATMega32 mit dem aufgespielten Programm. Nach einer kurzen Meldung des Boards, startet der Timer und die LED im Ausgabe-Modul beginnt zu blinken. Parallel dazu wird über das Display der aktuelle Zustand der LED (AN/AUS) und die Anzahl der Blinksequenzen mitgeteilt.

Das komplette Programm

#ifndef F_CPU
#define F_CPU 16000000
#endif

#include <avr/io.h>         // Header-Datei f. IO-Register
#include <avr/interrupt.h>  // Header-Datei f. Interruptfunktion
#include <stdint.h>         // Header-Datei f. standard Datentypen
#include "i2clcd.h"         // Header-Datei f. I2CLCD

volatile uint8_t tcount;    // Globale Variable (volatile)
uint8_t x;                  // Lokale Variable zum Schalter des Ports
int loops = 0;              // Zähler der gemachten Blinksequenzen

ISR (TIMER0_OVF_vect){
  tcount++;                 // tcount +1 pro Timer Interrupt (ca. 4ms)
}

int main(void) {                // Hauptprogramm ab hier
  x = 0;                        // Flag für LED Zustand (0/1)
  char s[10];                   // String-Variable für Ausgabe der Sequenzanzahl
  DDRD = (1 << DDD7);           // PortD7 als Ausgang für LED1
  TCCR0 = (1<<CS02);            // Prescaler 256
  TIMSK |= (1<<TOIE0);          // Overflow Interrupt erlauben
  sei();                        // Global Interrupts aktivieren
  i2c_init();                   // I2C initialisieren
  lcd_init();                   // LCD initialisieren
  lcd_command(LCD_CLEAR);       // LCD-Anzeige löschen

  // Text auf LCD ausgeben
  lcd_printlc(1,1,(unsigned char *)"GREETBoard V0.6");
  lcd_printlc(2,1,(unsigned char *)"I2C Test");
  lcd_wait_ms(3000);            // Text 3s stehen lassen
  lcd_command(LCD_CLEAR);       // Display löschen

  // weiteren Text ausgeben
  lcd_printlc(1,1,(unsigned char *)"LED Blinker");
  lcd_printlc(2,1,(unsigned char *)"LED: ");

  while (1) {                   // Endlosschleife im Hauptprogramm
    if ( tcount > 243 ) {       // 244*4,096ms Warten (~1s)
      if ( x == 0 ) {           // Wenn x gleich 0 ist
        PORTD &= ~( (1<<PD7));  // ...deaktiviere Port D7
        lcd_printlc(2,6,(unsigned char *)"AUS");
        x = 1;                  // Setze X auf 1
      }
      else {                    // Ansonsten
        loops++;
        itoa(loops, s, 10);
        PORTD |= (1<<PD7);      // ...aktiviere Port D7
        lcd_printlc(2,6,(unsigned char *)"AN ");
        lcd_printlc(2,10,(unsigned char *)s);
        x = 0;                 // Setze X auf 0
      }
      tcount = 0;              // Zähler wieder auf 0 setzen
    }
  }
}

Ein Gedanke zu „I2CLCD GREETBoard – Das I2C Display

Schreibe einen Kommentar

Deine E-Mail-Adresse wird nicht veröffentlicht. Erforderliche Felder sind mit * markiert.