Two Wire Interface: Projekt-1

Aus Wikibooks
Zur Navigation springen Zur Suche springen
WTFPL-2
Hinweis: Wenn du diese Seite bearbeitest, stimmst du zu, dass deine Bearbeitungen zusätzlich unter den Bedingungen der WTF Public License veröffentlicht werden.
WTFPL-2


Worum geht's?[Bearbeiten]

In diesem Kapitel soll ein sainsmart LCD2004 LCD Display über den TWI Bus mit dem Microcontroller verbunden und in Betrieb genommen werden.

Für die Ansteuerung des TWI Bus kann wahlweise die TWI Funktionseinheit des Microcontrollers oder die Software-Lösung des vorangegangenen Kapitels verwendet werden.

Hardware[Bearbeiten]

sainsmart LCD2004 Frontansicht
sainsmart LCD2004 Rückansicht

Das LCD Display des sainsmart LCD2004 wird von von einem HD44780 kompatiblen Chip angesteuert. Ein direkter Zugriff auf die Pins des HD44780 ist nicht möglich.

Der HD44780 ist fest mit einem PCF8574 I2C I/O Expander verdrahtet, der über die TWI Schnittstelle angesprochen werden kann.

Um den Schritten folgen zu können, ist es gut die zugehörigen Datenblätter [HD44780] und [PCF8574] der genannten Komponenten griffbereit zu haben.

I/O Expander[Bearbeiten]

Eine Kommunikation mit dem für die Steuerung des LCD Displays verantwortlichen Chip ist nur über den I/O Expander möglich. Bevor wir uns daran machen können das Display zu steuern müssen wir uns also zunächst mit der Ansteuerung des I/O Expanders vertraut machen.

Slave Adresse[Bearbeiten]

Die wichtigste Information, die wir benötigen, um über den TWI Bus mit dem PCF8574 zu kommunizieren, ist die zugehörige Slave Adresse. Die Bits 6...3 der Slave Adresse des PCF8574 sind fest vorgegeben. Die Bits 2...0 können durch die Verdrahtung über die Pins A0 A1 und A2 vorgegeben werden.[1] In unserem Fall sind die Pins A2 A1 und A0 alle auf 1 gelegt. Die Slave Adresse ist damit also 0x27.

0 1 0 0 A2 A1 A0 0 1 0 0 1 1 1 = 0x27

Der PCF8574 reagiert nicht auf General Call Adresse.[2]

I/O Pins[Bearbeiten]

Der PCF8574 verfügt über 8 I/O Pins (P0–P7), deren Zustand über den TWI Bus sowohl gesetzt als auch abgefragt werden kann.

Um den Zustand der Pins zu setzen kann folgender Code Schnipsel verwendet werden.

#define IO_EXPANDER_ADDR    0x27

static void io_expander_write(uint8_t data) {
  twi_start();

  twi_sla_w(IO_EXPANDER_ADDR);
  twi_write(data);
  
  twi_stop();
}

Verdrahtung[Bearbeiten]

Mit einer Ausnahme ist jeder der Daten Pins P7..P0 des PCF8574 mit einem Pin des HD44780 verdrahtet.

Einzig der Datenpin P3 ist nicht mit der LCD Steuerung verdrahtet. Über ihn kann stattdessen die Hintergrundbeleuchtung des LCD Displays ein- und ausgeschaltet werden.

Die Verdrahtung der Pins des I/O Expanders mit den Pins der LCD Steuerung sind in folgender Tabelle wiedergegeben. Der Eintrag (bl*) steht dabei für den nicht mit der LCD Steuerung verdrahteten Pin, der für die Hintergrundbeleuchtung (backlight) zuständig ist.

I/O Expander P7 P6 P5 P4 P3 P2 P1 P0
Display Steuerung D7 D6 D5 D4 (bl*) E RW RS

Display Steuerung[Bearbeiten]

Das Display wird von einem HD44780 kompatiblen Chip angesteuert.

Das Display verfügt über zwei Speicherbereiche.

  • Display Data RAM (DDRAM)
  • Character Generator RAM (CGRAM)

Register und Befehlssatz[Bearbeiten]

Das Verhalten des HD44780 kann über ein Befehlsregister IR (Instruction Register) und ein Datenregister DR (Data Register) gesteuert werden. [3] Beide Register haben eine Größe von 8-Bit.

Daten, die in das Datenregister geschrieben werden, werden automatisch in den Speicher DDRAM /CGRAM übernommen.

Daten, die in das Befehls Register geschrieben werden, dienen der Steuerung des LCD Displays. Die folgende Tabelle enthält eine Übersicht über die Befehle, die zur Verfügung stehen. Eine vollständige Liste findet sich im Datenblatt.[4]

DB7 DB6 DB5 DB4 DB3 DB2 DB1 DB0 Instruction
0 0 0 0 0 0 0 1 Clear Display 0x01
0 0 0 0 0 0 1 * Return Home 0x02
0 0 0 0 0 1 I/D S Entry Mode Set flags
0 0 0 0 1 D C B Display On/Off flags
0 0 0 1 S/C R/L * * Cursor/Display Shift flags
0 0 1 DL N F * * Function set flags
0 1 aaa aaa aaa aaa aaa aaa Set CGRAM Address addr
1 aaa aaa aaa aaa aaa aaa aaa Set DRAM Address addr
FLAG 0 1
I/D decrement increment
S don't shift display shift display
D display off display on
C cursor off cursor on
B cursor blink off cursor blink on
S/C cursor move display shift
R/L shift left shift right
DL data length 4-bit data length 8-bit
N 1 line 2 lines
F 5x8 dots 5x10 dots

Kommunikation[Bearbeiten]

Für einen Transport von Daten nötige Paramter können wie folgt über die Pins des HD44780 vorgegeben werden.

  • mit dem Pin RS (Register Select) wird das Register ausgewählt, mit dem kommuniziert werden soll
  • mit dem Pin RW (Read/Write) wird die Richtung des Datentransfers vorgegeben
Pin 0 1
RS Instruction Register (IR) Data Register (DR)
RW write read

Daten können sowohl im 8-bit Modus, als auch im 4-bit Modus gesendet und empfangen werden.

Im 8-Bit Modus können Daten über die Datenpins D0...D7 gesendet und empfangen werden.

Im 4-Bit Modus wird ein Byte mit 2 aufeinander folgenden 4-Bit Transfers durchgeführt. Das high-Nibble muss dabei zuerst übertragen werden, danach das low-Nibble.

Der Datentransfer kann über eine fallende Flanke am E (Enable) Pin initiiert werden. Dabei ist es erforderlich, dass die gewünschten Parameter und Daten vor und nach fallender Flanke des Enable Pins anliegen.

Um ein Byte zum LCD transportieren müssen somit also insgesamt vier Bytes zum IO Expander gesendet werden.

#define E_HIGH   0b00000100

#define BACKLIGHT_OFF  0
#define BACKLIGHT_ON   8

static uint8_t backlight_flag = BACKLIGHT_ON;

void lcd_write(uint8_t flags, uint8_t data) {

  flags |= backlight_flag;

  uint8_t n_high = (uint8_t) ( (data & 0xF0) | flags );
  uint8_t n_low  = (uint8_t) ( (data << 4)   | flags );

  io_expander_write(n_high | E_HIGH);
  io_expander_write(n_high);

  io_expander_write(n_low  | E_HIGH);
  io_expander_write(n_low);
}

4-Bit Modus[Bearbeiten]

Nach einem Reset befindet sich der HD44780 im 8-Bit Modus. Bevor er im 4-Bit Modus betrieben werden kann, muss er zunächst mit einer speziellen Sequenz von Befehlen in diesen Modus gebracht werden. Eine Anleitung hierzu findet sich im Datenblatt. [5]

Der erste Schritt besteht im Versenden der beiden folgenden Spezialpakete

Paket RS RW DB7 DB6 DB5 DB4
magic_1 0 0 0 0 1 1
magic_2 0 0 1 0 0 0

Anzahl Wiederholungen und Timing müssen sich dabei an folgende Prozedur halten:

  1. warte: mehr als 40 ms (sichere Variante)
  2. schreibe: magic_1
  3. warte: mehr als 4.1 ms
  4. schreibe: magic_1
  5. warte: mehr als 100us
  6. schreibe: magic_1
  7. schreibe: magic_2

Wurden alle Spezialpakete versendet, muss mit den folgenden Befehlen fortgefahren werden:

  1. FUNCTION_SET
  2. DISPLAY_ON_OFF
  3. CLEAR_DISPLAY
  4. ENTRY_MODE_SET
  5. DISPLAY_ON_OFF

Regulär muss vor jedem versenden darauf gewartet werden, dass die LCD Steuerung mit dem Busy Flag Bereitschaft signalisiert. Bei direkter Ansteuerung der LCD Steuerung sollte dieser Punkt unbedingt beachtet werden.

Der Datentransport über den TWI Bus ist langsam genug, dass in der Regel davon ausgegangen werden kann, dass die LCD Steuerung die Abarbeitung des vorangegangenen Befehls bereits abgeschlossen hat, wenn der nächste Befehl eintrifft. Eine Überprüfung des Busy Flags kann aus diesem Grund in der Regel ausgelassen werden.

Genaue Angaben zu den garantierten Ausführungszeiten der einzelnen Befehle finden sich im Datenblatt. In unserem Fall ist einzig der Befehl CLEAR_DISPLAY kritisch. Um auch hier eine Überprüfung des Busy Flags auslassen zu können, muss vor dem Versenden des nächsten Befehls eine geeignete Wartezeit eingehalten werden.

Für die Initialisierung der LCD Steuerung kann folgender Code Schnipsel verwendet werden

#define CLEAR_DISPLAY  0x01
#define ENTRY_MODE_SET 0x04
#define DISPLAY_ON_OFF 0x08
#define FUNCTION_SET   0x20

FILE* lcd_init() {

  twi_init();

  // Initializing by Instruction (page 46)

  uint8_t magic_1 = 0b00110000;
  uint8_t magic_2 = 0b00100000; 

  _delay_ms(50);
  
  io_expander_write(magic_1 | E_HIGH);
  io_expander_write(magic_1);

  _delay_ms(5);

  io_expander_write(magic_1 | E_HIGH);
  io_expander_write(magic_1);

  _delay_ms(1);

  io_expander_write(magic_1 | E_HIGH);
  io_expander_write(magic_1);
  
  io_expander_write(magic_2 | E_HIGH);
  io_expander_write(magic_2);

  /* ----------------------------------------- */

  lcd_write(IR_W, FUNCTION_SET   | 0x08);
  lcd_write(IR_W, DISPLAY_ON_OFF);
  lcd_write(IR_W, CLEAR_DISPLAY);

  _delay_ms(100);
  lcd_write(IR_W, ENTRY_MODE_SET | 0x02);
  lcd_write(IR_W, DISPLAY_ON_OFF | 0x04);

  return &the_lcd;
}


Ausgabe[Bearbeiten]

Textausgabe[Bearbeiten]

static FILE the_lcd = FDEV_SETUP_STREAM(lcd_put, NULL, _FDEV_SETUP_WRITE);

static int lcd_put(char c, FILE *stream) {
  lcd_write(DR_W, (uint8_t) c);
  return c;
}

Positionierung[Bearbeiten]

void lcd_goto(uint8_t addr) {
  lcd_write(IR_W, SET_DRAM_ADDR | addr);
}

Hello World[Bearbeiten]

#include <stdio.h>

#include "twi.h"
#include "lcd.h"

// Forward Declarations
void setup(void);
void loop(void);

/* ---------------------------------------------------------------------- */
FILE *lcd;

void setup() {
   lcd = lcd_init();

   fprintf(lcd, "hello world");
}

void loop() {
   ; // do nothing
}

#ifndef ARDUINO
int main(void) {
   setup();
   while(1)
      loop();
}
#endif
sainsmart LCD2004 Verkabelung
Ergebnis mit interner TWI Funktionseinheit
Ergebnis mit Software TWI Lösung

Fußnoten[Bearbeiten]

  1. (siehe [PCF8574]: 8.3.2 Interface Definition S.12)
  2. (siehe [PCF8574]: 8.3.2 Interface Definition S.12)
  3. (siehe [HD44780]: Registers S.9)
  4. (siehe [HD44780]: Table 6 Instructions S.24)
  5. (siehe [HD44780]: Figure 24 4-Bit Interface, S.46)

WTFPL-2
Du hast das Recht unter den Bedingungen der WTF Public License mit diesem Dokument anzustellen was zum Teufel auch immer Du willst.