Schnittstellen mit dem Raspberry PiGPIO

Dieses Kapitel setzt voraus, dass das neue Raspian Jessie vom September 2016 verwendet wird.

Kurze Einführung

Mit den einzelnen Pins des Raspberry Pi (Raspi) lassen sich bequem Schalter einlesen und LEDs oder Relais einschalten. Werden die Aufgaben komplexer, so greift man auf spezialisierte Bausteine zurück. A/D- oder D/A-Wandler-Bausteine ermöglichen das Verarbeiten analoger Daten. Temperatur-, Feuchtigkeit-, Luftdruck-Sensoren liefern uns Umweltwerte. Port- Expander erweitern das Angebot an vorhandenen Pins. Farbige graphische Displays erleichtern die Kommunikation mit dem Anwender. Es existieren spezialisierte Bausteine für unterschiedliche serielle Schnittstellen. Meist trifft man hier auf I2C, SPI oder 1-Wire. Linux unterstützt diese Schnittstellen auf dem Raspi, so dass sich diese spezialisierten Bausteine nutzen lassen.

Die serielle Schnittstelle (EIA232) wird heute noch häufig zur Kommunikation (meist ASCII-Daten) verwendet. Auch sie soll mit dem Raspi in Betrieb genommen werden.

Digitale Ein-/Ausgabe (GPIOs)

Die einzelnen Pins des Raspi können unterschiedliche Aufgaben wahrnehmen (GPIO General Purpose Input Output). Oft werden sie als einfache digitale Ein- bzw. Ausgänge genutzt um LEDs einzuschalten oder Schalter abzufragen (Wiederholung 12. Klasse!).

Es ist hierbei darauf zu achten, dass die maximale Spannung von 3,3V nicht überschritten wird, und auch kein nennenswerter Strom geliefert werden kann. Sind höhere Ströme oder Spannungen nötig, so ist ein Treiber-IC wie der ULN2008 o.ä. zu nutzen.

!!! Achte beim Verdrahten darauf keine Kurzschlüsse zu verursachen. Die GPIO- Pins des Raspi sind nicht so robust wie die verschiedener Mikrocontroller! Auf http://www.weigu.lu/sb-computer/rpi_buffer_board wird eine Schutzplatine mit kurzschlussfesten Buffer-Bausteinen vorgestellt, die hier genutzt werden soll um die Raspis nicht zu zerstören.

GPIO Schaltplan
GPIO Schaltplan

Die Python-Bibliothek RPi.GPIO ermöglicht den direkten Zugriff auf die GPIOs.

Für die Beschriftung der Raspi Pins werden die Chip-Nummern des Raspi (Broadcom SOC channel numbers) genutzt die oft mit dem Vorsatz GPIO auftauchen, und nicht die Pinnummern des Raspi-Board! Dies wird mit der Methode setmode() festgelegt.

Die Methode setup() legt fest ob das jeweilige Pin als Ein- oder Ausgang verwendet wird. Die Methode output() schaltet das Pin auf HIGH bzw. LOW. Mit der Methode cleanup() wird das Pin wieder rückgesetzt (kein Ausgang mehr). Damit die Methode cleanup() auch bei einer frühzeitigen Unterbrechung des Programms mit CTRL+C aufgerufen wird, wird eine try...except-Anweisung genutzt um einen KeyboardInterruptaufzufangen.

Das folgende Programm lässt eine LED an GPIO23 10 mal blinken:

    #!/usr/bin/env python3
    # -*- coding: utf-8 -*-
    # gpio_blink.py

    import RPi.GPIO as GPIO  # sudo apt-get install rpi.gpio
    from time import sleep

    PIN_LED = 23
    GPIO.setmode(GPIO.BCM)   # Broadcom SOC channel number (numbers after GPIO)
    GPIO.setup(PIN_LED, GPIO.OUT)
    try:
        for i in range(0,10):
            GPIO.output(PIN_LED, GPIO.HIGH)
            sleep(0.5)
            GPIO.output(PIN_LED, GPIO.LOW)
            sleep(0.5)
    except KeyboardInterrupt:
        print("Keyboard interrupt by user")
    GPIO.cleanup()

Das nächste Programm fragt einen Schalter (Methode input()) ab und schaltet eine LED am GPIO23 ein, wenn der Schalter (hier Verbindungsdraht) mit Masse verbunden wird. Die Endlosschleife (while (True)) kann mit Hilfe der Tastenkombination CTRL+C sauber beendet werden, da diese mit der try...except-Anweisung abgefangen wird und bewirkt, dass GPIO.cleanup()noch aufgerufen wird.

    #!/usr/bin/env python3
    # -*- coding: utf-8 -*-
    # gpio_read.py

    import RPi.GPIO as GPIO  # sudo apt-get install rpi.gpio
    from time import sleep

    PIN_LED = 23
    PIN_SWITCH = 24
    GPIO.setmode(GPIO.BCM)   # Broadcom SOC channel number (numbers after GPIO)
    GPIO.setup(PIN_SWITCH, GPIO.IN)
    GPIO.setup(PIN_LED, GPIO.OUT)

    try:
        while (True):
            state_sw = GPIO.input(PIN_SWITCH)
            if state_sw == 0:
                GPIO.output(PIN_LED, GPIO.HIGH)
            else:
                GPIO.output(PIN_LED, GPIO.LOW)
            sleep(0.5)
    except KeyboardInterrupt:
        print("Keyboard interrupt by user")
        GPIO.cleanup()
Aufgabe I1:

Teste die beiden obigen Programme. Das zweite Programm hat keinen klaren Zustand, wenn der Schalter offen ist (magische Reaktionen :)). Dies kann durch die Verwendung eines internen Pull-Up Widerstandes verbessert werden. Suche im Netz wie die Zeile mit GPIO.setup(PIN_SWITCH, GPIO.IN) zu verändern ist und teste das veränderte Programm.

Die 1-Wire Schnittstelle

Der 1-Wire-Bus wurde von der Firma Dallas Semiconductor Corp. entwickelt. Es handelt sich um eine serielle asynchrone Schnittstelle, bei der mindestens 2 Leitungen vorhanden sind (Datenleitung und Masse). Die Spannungsversorgung (2,8-6V) kann über die Datenleitung erfolgen, da jeder Baustein einen internen Kondensator besitzt, der durch die vorhandene Spannung auf der (inaktiven) Datenleitung aufgeladen wird. Es wird über die gleiche Datenleitung im Halbduplex-Verfahren gesendet und empfangen. An der Leitung kann ein Master und bis zu 100 Slaves angeschlossen werden. Jeder Slave besitzt eine eindeutige 64-Bit-Adresse (8-Bit-Family-Code, 48-Bit-Seriennummer, 8 Bit CRC Checksumme), die fest einprogrammiert ist.

Temperaturmessung mit dem DS18B20

Wir verwenden hier den Temperatursensor DS18B20. Dieser Sensor kann für Temperaturen von -55°C bis +125°C verwendet werden. Die Genauigkeit ist hoch (weniger als ±0.5°C Fehler zwischen -10°C to +85°C). Der interne A/D-Wandler nutzt 12 Bit und die Erfassung und Wandlung des Temperaturwertes benötigt rund 750ms.

Die Datenleitung ist im inaktivem Zustand High und wird vom Master (hier unserem Raspi) auf Masse gezogen um Daten zu übertragen. Der 1-Wire Treiber des Raspi ist so konfiguriert, dass er die Datenleitung auf Pin Nummer 4 (#4) erwartet. Mit Hilfe eines externen Pull-Up Widerstandes ziehen wir die Datenleitung auf 3,3V (oder 5V). Da der typische Strom des Sensors um 1mA liegt, ist ein Widerstand von 4,7k eine gute Wahl. Der Temperatursensor wird zusätzlich extern mit Spannung versorgt (3 Leitungen), da diese Variante eine robustere Datenübertragung erlaubt.

!!! Achte beim Verdrahten darauf keine Kurzschlüsse zu verursachen. Die GPIO- Pins des Raspi sind nicht so robust wie die verschiedener Mikrocontroller! Wird der rpibuffboard-Adapter (http://www.weigu.lu/sb-computer/rpi_buffer_board) genutzt, so ist der Jumper auf 3,3V zu setzen.

1-Wire Schaltplan

Die Treiber für den 1-Wire-Bus des Raspi sind im Kernel nicht fest eingebunden, sondern müssen über so genannte Kernelmodule nachgeladen werden.

Wird ein Raspi Image mit graphischer Oberfläche verwendet, so wird die 1-Wire Schnittstelle im Menu (Himbeere) unter: Menu ⇨ Preferences ⇨ Raspberry Pi Configuration ⇨ Interfaces eingeschaltet. Bei einem Raspi ohne graphische Oberfläche geschieht die mit dem Programm sudo raspi-config (Punkt Advanced Options, AA 1-Wire). Für weitere Infos: http://weigu.lu/sb-computer/raspi_tips_tricks.

Mit dem Befehl lsmod kann man nachsehen welche Kernelmodule geladen sind. Für 1-Wire heißen die Module w1-therm, w1_gpiound wire.)

Bemerkung: Auf der Kommandozeile kann man sehr bequem mit dem midnight commander (mc) arbeiten. Sollte er nicht installiert sein, so installiere ihn mit sudo apt install mc. Mit sudo mc hat man Root-Rechte und kann mit F4 Dateien editieren. Mit CTRL+O kann man zwichen dem commander und dem Terminal hin- und herschalten.

Ist der Sensor richtig verdrahtet, so kümmert der Treiber sich um das Abfragen der eindeutigen Adresse und legt ein Verzeichnis für den Sensor mit dieser Adresse in folgendem Unterverzeichnis an:

`/sys/bus/w1/devices`

Das Verzeichnis hat als Namen die eindeutige Adresse (ohne 8 Bit CRC Checksumme) des Sensors! 0x28 ist der 8-Bit-Family-Code. Hinter dem Bindestrich folgt dann die 48-Bit-Seriennummer (6 Byte).

Der Sensor wird wie eine Datei behandelt. Diese Gerätedatei hat den Namen w1-slave und enthält zwei Textzeilen.

Den Inhalt der Datei kann man sich mit dem cat-Befehl (oder im midnight commander mit F3) anzeigen lassen.

1-Wire Gerätedatei

Das entsprechende Programm für das Erfassen der Temperatur kann dann folgendermaßen aussehen (es ist natürlich die richtige Adresse einzusetzen):

    #!/usr/bin/env python3
    # -*- coding: utf-8 -*-
    # interface_w1_ds18b20_1.py

    deviceFile = "/sys/bus/w1/devices/28-0000036086d2/w1_slave"

    try:
        f = open(deviceFile, 'r')
    except IOError:
        print("IOError: No device file!")
    line1 = f.readline()
    print(line1, end='')
    line2 = f.readline()
    print(line2, end='')
    f.close()

Das Programm kann mit der Tastenkombination CTRL+C (deutsche Tastatur STRG+C) abgebrochen werden.

Nachdem die Datei geöffnet wurde, werden 2 Zeilen eingelesen. Mit Hilfe der print()-Funktion sehen wir uns den Inhalt an:

1-Wire DS18B20 Ausgabe

Die benötigte Information befindet sich in der 2. Zeile hinter dem String t=. Mit der find()-Methode lässt sich der Temperatur-String extrahieren und in eine Zahl umwandeln. Die ganze Temperaturerfassung packen wir in eine Funktion. Eine mögliche Version des Programms könnte dann wie folgt aussehen:

    #!/usr/bin/env python3
    # -*- coding: utf-8 -*-
    # interface_w1_ds18b20_2.py

    from time import sleep

    deviceFile = "/sys/bus/w1/devices/28-0000036086d2/w1_slave"

    def readTemp():
        try:
            f = open(deviceFile, 'r')
        except IOError:
            print("IOError: No device file!")
        line1 = f.readline()
        line2 = f.readline()
        f.close()
        pos = line2.find("t=")
        if pos != -1:
            tempString = line2[pos + 2:]
            temp = round(float(tempString) / 1000.0, 1)
        else:
            print("error: temperature not found")
        return temp

    try:
        while (True):
            print(str(readTemp())+" °C")
            sleep(1)
    except KeyboardInterrupt:
        print("Keyboard interrupt by user")

Das Programm kann mit der Tastenkombination CTRL+C (deutsche Tastatur STRG+C) abgebrochen werden.

HILFE:

Das folgende Programm kann helfen die vorhandenen Sensoren auf Funktionstüchtigkeit zu testen.

Aufgabe I2:

Das manuelle Ausspähen der Gerätedatei ist lästig, wenn unterschiedliche Sensoren getestet werden sollen. Um dies zu automatisieren kann man das glob-Modul von Python verwenden, das uns ermöglicht nach Pfadnamen mit Hilfe von Wildcards zu suchen. Da die Adresse des DS18B20 immer mit 28 beginnt (Family-Code), ermitteln wir so das Verzeichnis (die Adressen des DS18S20 beginnen mit 10). Die Methode glob() gibt eine Liste zurück. Der String befindet sich im ersten Element der Liste, auf die wir mit dem Index [0] zugreifen. Erweitere dein Programm um folgende Zeilen und teste es.

    from glob import glob

    try:
        deviceFolder = glob("/sys/bus/w1/devices/28*")
        deviceFolderString = deviceFolder[0]
        deviceFile = deviceFolderString + "/w1_slave"
    except:
        print("Error: Sensor not found")
Aufgabe I3:

Erweitere dein Programm, so dass es die Temperatur von zwei Temperatursensoren erfassen und ausgeben kann. Nutze glob(); auf das zweite Verzeichnis kann mit dem Index [1] zugegriffen werden. Die Ausgabe soll folgendermaßen aussehen:

1-Wire Aufgabe I3

Aufgabe I4: (für Fleißige)

Schreibe ein Programm mit graphischer Oberfläche, das es ermöglicht die Temperatur in Grad Celsius, Kelvin oder Fahrenheit anzeigen zu lassen (Checkboxes).

Aufgabe I5: (für sehr Fleißige)

Erweitere das GUI-Programm um ein Auswahlfeld, das alle vorhandenen Sensoren mit ihrer Adresse anzeigt.

Die I²C Schnittstelle

Die von Philipps entwickelte serielle synchrone I²C-Master-Slave-Schnittstelle, (Inter-Integrated Circuit, gesprochen I-Quadrat-C) dient der Kommunikation zwischen verschiedenen integrierten Schaltkreisen (IC, Integrated Circuit, Chip). Sie wurde für die Unterhaltungselektronik entwickelt (Fernsehgeräte) und ist dort weit verbreitet (viele ansteuerbare Spezial-IC's).

Vorteile des I²C-Busses sind der geringe Verdrahtungsaufwand und die geringen Kosten bei der Entwicklung eines Gerätes. Es werden nur drei Leitungen benötigt. Ein Mikrocontroller kann so ein ganzes Netzwerk an IC's, mit nur drei Leitungen und einfacher Software kontrollieren. Dies senkt die Kosten des zu entwickelnden Gerätes. Während des Betriebes können Chips zum Bus hinzugefügt oder entfernt werden (hot-plugging).

Nachteile des I²C-Busses sind die geringe Geschwindigkeit und die geringe überbrückbare Distanz. Daten können nur abwechselnd über die Datenleitung gesendet werden (Halbduplex), und zusätzlich zu den Daten müssen die Adressen der Bausteine versendet werden.

Verwendung: Der I²C-Bus wird meist zur Übertragung von Steuer- und Konfigurationsdaten verwendet, da es dabei meist nicht auf Schnelligkeit ankommt. Er wird zum Beispiel verwendet bei Echtzeituhren, Lautstärkereglern, Sensoren, A/D- und D/A-Wandlern mit niedriger Abtastrate, EEPROM Speicherbausteinen oder bidirektionale Schaltern und Multiplexern. Große Bedeutung hatte das I²C- Protokoll in der Vergangenheit im Chipkartenbereich. Er ist nicht geeignet für größere Entfernungen, da die Störsicherheit gering ist und zu Fehlern in der Übertragung führt.

Der I²C-Bus ist als synchrone Master-Slave-Schnittstelle konzipiert (es ist allerdings auch möglich mehrere Master einzusetzen (multimaster mode)). Die Buszuteilung ist dabei in den Spezifikation geregelt und verhindert Kollisionen. Im Normalfall sendet der Master (bei uns der Raspi) und ein Slave reagiert darauf. Es können je nach verwendeten IC's vier Geschwindigkeiten eingesetzt werden:

Der Raspi arbeitet default mit 100 kHz. Es ist aber möglich die Geschwindigkeit zu verändern.

I²C Blockschaltbild

Im obigen Bild sind ein Master und drei Slaves eingezeichnet. Der synchrone I²C- Bus benötigt eine Taktleitung (serial clock line, SCL) und eine Datenleitung (serial data line, SDA). Jedes Datenbit auf der SDA- Leitung wird mit dem Takt der SCL-Leitung synchronisiert. Die Pull-Up-Widerstände an der Takt- und Datenleitung ziehen beide Leitungen im Ruhezustand auf High-Pegel. Alle am Bus angeschlossene Bausteine besitzen einen Open-Collector- oder Open- Drain-Ausgang (der Kollektor bzw. Drain eines Transistors ist unbeschaltet (offen) und wird durch den gemeinsamen Pull-Up Widerstand des Busses mit VCC verbunden). Ist der bipolare Transistoren bzw. der FET durchgeschaltet, so wird der Bus auf Masse gezogen. Man nennt einen solche Verschaltung auch noch eine Wired-And-Verknüpfung, da die Schaltung wie ein Und-Gatter wirkt.

Das I²C-Protokoll und die Adressierung

Mit einer fallenden Flanke auf SDA (SCL = High) startet der Master die Kommunikation. Nach dem Startbit sendet der Master als erstes das Adressbyte an den Slave. Das Adressbyte besteht aus einer 7‑Bit Slave-Adresse und einem Schreib-Lese-Bit, welches die Richtung der Kommunikation festlegt. Der Slave bestätigt den korrekten Empfang mit einem ACK-Bestätigungsbit (ACKnowledgement). Der Master erzeugt die 9 Taktimpulse und liest dann die Taktleitung. Hier kann dann ein langsamer Slave mit einem Low-Pegel eine Wartezeit erzwingen (clock stretching).

I²C Protokoll 1

Je nach Richtung der Kommunikation sendet jetzt der Master oder der Slave beliebig viele Datenbytes (8 Bit, MSB first). Jedes Datenbyte wird vom Gegenüber mit einem ACK-Bit (Low-Pegel) bestätigt. Die Übertragung wird durch das Senden eines NACK-Bits (Not ACKnowledge, High-Pegel) vom Master oder Slave abgebrochen. Mit einer steigenden Flanke auf SDA (SCL = High) gibt der Master den Bus wieder frei (Stoppbit).

Um Zeit zu sparen kann der Master auch den Bus nicht freigeben (kein Stoppbit) und gleich mit einem weiteren Startbit (Repeated Start) eine neue Kommunikation starten. Die Kommunikationsrichtung kann hierbei natürlich beliebig geändert werden. Das vom Master gesendete Adressbyte besteht, wie beschrieben, aus sieben Bit die die eigentliche Adresse des Slave darstellen und einem achten Bit das die Lese- oder Schreibrichtung festlegt. Die I²C-Schnittstelle nutzt einen Adressraum von 7 Bit, womit gleichzeitig 112 Bausteine auf einem Bus angesprochen werden können (16 der 128 möglichen Adressen sind für Sonderzwecke reserviert). Jeder I²C- fähige Baustein (IC) hat eine festgelegte Adresse. Bei manchen IC's kann ein Teil der Adresse hardwaremäßig mittels Steuerpins festgelegt werden. So können z.B. bis zu acht gleichartige IC's an einem I²C-Bus betrieben werden. Immer häufiger kann die Adresse aber auch softwaremäßig umprogrammiert werden (z.B. bei digitalen Sensoren). Es besteht auch noch eine neuere alternative 10 Bit-Adressierung (1136 Bausteine). Sie ist abwärtskompatibel zum 7 Bit-Standard (nutzt zusätzlich 4 der 16 reservierten Adressen).

I²C Protokoll 2

I²C mit dem Raspberry Pi

Wird ein Raspi Image mit graphischer Oberfläche verwendet, so wird die I2C Schnittstelle im Menu (Himbeere) unter: Menu ⇨ Preferences ⇨ Raspberry Pi Configuration ⇨ Interfaces eingeschaltet. Bei einem Raspi ohne graphische Oberfläche geschieht die mit dem Programm sudo raspi-config (Punkt Advanced Options, A7 I2C). Für weitere Infos: http://weigu.lu/sb-computer/raspi_tips_tricks.

Das neue Raspbian erledigt hier automatisch mehrere Schritte. Um den I²C Bus nutzen zu können werden die entsprechenden Kernelmodule geladen. Mit dem Befehl lsmod kann man nachsehen welche Kernelmodule geladen sind. Für I²C heißen die Module i2c-dev und i2c-bcm2708. Die Packete i2c-tools und python3-smbus sind schon installiert (sollten sie nicht installiert sein, so können sie mit sudo apt install i2c-tools python3-smbus nachinstalliert werden). Damit die Programme nicht mit Root-Rechten ausgeführt werden müssen, gehört der Benutzer pi jetzt zur Gruppe i2c. Der Befehl groups pi zeigt, dass dies der Fall ist (falls nicht erledigt das der Befehl sudo adduser pi i2c).

Die Echtzeituhr (RTC) DS1307

Das Senden und Empfangen von Daten über den I²C-Bus soll mit einer Echtzeituhr (Real Time Clock) getestet werden. Eine Echtzeituhr läuft auch ohne externe Spannungsversorgung mit einer Batterie (üblicherweise Lithium-Knopfzelle mit 3 V) weiter. Es wird der I²C Baustein DS1307 von Maxim als Echtzeituhr verwendet. Zur äußeren Beschaltung wird nur ein Uhrenquarz (32,768 kHz) und die Batterie benötigt. Der DS1307 besitzt auch einen Ausgang (SQW/OUT) mit dem ein quarzgenauer Takt ausgegeben werden kann. Die I²C‑Adresse des Bausteins ist 0x68. Der DS1307 arbeitet nur im Standard Mode (100 kHz).

I²C DS1307

Wichtig: Wird keine Batterie angeschlossen, so muss Pin 3 mit Masse verbunden werden, damit der Baustein angesprochen werden kann.

Der Uhrenbaustein besitzt 64 Register (RAM-Speicherzellen), welche über eine Registeradresse angesprochen werden können. Die ersten sieben Register enthalten die Daten der Uhr (Uhrzeit (3), Wochentag (1) und Datum (3)). Das achte Register dient als Kontrollregister. Die restlichen 56 Speicherzellen können beliebig beschrieben und gelesen werden (gepuffertes RAM). Uns interessieren hier besonders die ersten sieben Register. Sobald das Sekundenregister beschrieben wurde (Bit 7 (CH) = 0) läuft die Uhr. Die Daten werden im BCD-Code abgelegt. Der BCD-Code (Binary Coded Decimal) ist ein Code mit dual kodierten Dezimalziffern; 4 Bit (Nibble) stellen eine Dezimalziffer (0-9) im Dualcode dar (0b0000-0b1001)).

I²C DS1307 Register

Wir verwenden hier ein Breakout Board von Adafruits, das neben Quarz, Batterie und DS1307 auch die beiden Pull-Up-Widerstände für den I²C-Bus besitzt. Der Anschluss gestaltet sich also entsprechend einfach.

!!! Achte beim Verdrahten darauf keine Kurzschlüsse zu verursachen. Die GPIO- Pins des Raspi sind nicht so robust wie die verschiedener Mikrocontroller! Wird der rpibuffboard-Adapter http://www.weigu.lu/sb-computer/rpi_buffer_board genutzt, so ist der Jumper auf 3.3V zu setzen.

I²C DS1307 Schaltplan

Nachdem der Baustein angeschlossen wurde können wir mit Hilfe der i2c-tools testen ob alles klappt. Dazu geben wir das folgende Kommando ein:

    i2cdetect -y 1

(Bei älteren Raspi Version (Model B rev. 1) wird die Eins durch eine Null ersetzt.)

Die Ausgabe zeigt uns alle Adressen der angeschlossenen I²C-Bausteine. In unserem Fall also die Adresse 0x68 des RTC.

I²C Adresse ermitteln

Sollte dies nicht geschehen so kann man die Batterie entfernen und Batterieplus kurz mit Batterieminus verbinden, bevor man die Batterie wieder einsetzt.

Aufgabe I6:

Teste ob dein Baustein erkannt wird.

Ein Programm um den RTC-Baustein anzusprechen kann dann wie folgt aussehen:

    #!/usr/bin/env python3
    # -*- coding: utf-8 -*-
    # interface_i2c_ds1307_1.py

    from smbus import SMBus
    from time import sleep

    port = 1                # (0 for rev.1, 1 for rev 2!)
    bus = SMBus(port)
    rtcAddr = 0x68

    def bcd2str(d):         # // for integer division; % for modulo
        if (d <= 9):
            return '0' + str(d)
        else:
            return str(d // 16) + str(d % 16)

    # set clock (BCD: sec,min,hour,weekday,day,mon,year)
    td = [0x00, 0x05, 0x08, 0x01, 0x07, 0x01, 0x17]
    bus.write_i2c_block_data(rtcAddr, 0, td)

    try:
        while (True):
            rd = bus.read_i2c_block_data(rtcAddr, 0, 7)
            print (bcd2str(rd[4]) + '/' + bcd2str(rd[5]) + '/' + bcd2str(rd[6]) + \
                   '  ' + bcd2str(rd[2]) + ':' + bcd2str(rd[1]) + ':' + bcd2str(rd[0]))
            sleep(1)
    except KeyboardInterrupt:
        print("Keyboard interrupt by user")

Nachdem SMBus aus dem das Modul smbus geladen wurde, kann die Uhr gesetzt werden. Dazu wird die Methode write_i2c_block_data() verwendet um einen ganzen Block von Daten zu senden:

    bus.write_i2c_block_data(rtcAddr, 0, td)

Der erste Parameter ist die I²C-Adresse. Der zweite Parameter übergibt ist ein Kommando vom Master. In unserem Fall kann man hier die Anfangsadresse des RTC-Adresszeigers angeben (er wird bei der Übergabe der Daten automatisch inkrementiert). Wir übergeben als Anfangsadresse Null, damit der Speicher ab der Sekundenadresse geschrieben wird (mit einer Eins würden wir bei den Minuten beginnen.) Der dritte Parameter ist dann eine Sequenz mit den zu schreibenden Daten. ! Damit die Uhr anläuft muss der Sekundenwert geschrieben werden (Bit 7 = 0). Die Uhr wird natürlich nur beim ersten Aufruf des Programms gesetzt. Danach soll die Zeile zum Schreiben mit einem Kommentarzeichen versehen werden. In der folgenden Endlosschleife wird die Uhr im Sekundentakt gelesen und Datum sowie Uhrzeit ausgegeben. Dies passiert mit der Methode read_i2c_block_data():

    rd = bus.read_i2c_block_data(rtcAddr, 0, 7)

Die beiden ersten Parameter sind die gleichen wie oben. Der dritte Parameter gibt die Anzahl der zu lesenden Bytes an. Da die Daten in BCD vorliegen müssen sie noch in einen String umgewandelt werden. Dies übernimmt die Funktion einfache bcd2str(), mit Hilfe der Integer-Division und der Modulo-Operation.

Aufgabe I7:

Erweitere das Programm, so dass auch noch der Wochentag ausgegeben wird.

Aufgabe I8: (für Fleißige)
  1. Um die Uhrzeit zu setzen soll die aktuelle Uhrzeit des Raspi ermittelt werden. Dies kann mit dem Modul datetime erfolgen.
  2. Die Uhr soll ja nicht dauernd neu gesetzt werden. Erweitere dein Programm, so dass es feststellen kann ob die Uhr läuft oder nicht, und die Uhr nur setzt, wenn sie nicht schon läuft.

    Zur Umwandlung der Integer-Werte nach BCD kann folgende Funktion genutzt werden:

       def int2bcd(d):         # // for integer division; % for modulo
            return (((d // 10) << 4) + (d % 10))
    

Der LED Treiber-Chip HT16K33

Damit unsere Uhr auch ohne PC sichtbar wird, soll jetzt ein 4-stelliges 14 -Segment-Display in Betrieb genommen werden. Die Ansteuerung erfolgt mit dem LED-Treiber HT13K33 von Holtek. Er besitzt einen RAM Speicher mit 8 Byte, in dem die Information, welche LEDs leuchten sollen abgelegt wird. Die Verdrahtung bleibt einfach. C steht für CLK und D für SDA, - für Masse. Das 14-Segment Display hat anders als das 7-Segment-Display im Bild einen zweiten Plus-Pol. Auch dieser ist mit 5V zu verbinden.

I²C DS1307 HT16K33 Schaltplan

Aufgabe I9:

Ermittle mit den i2c-tools die Adresse des HT16K33.

Das folgende Programm gibt einen festen Text aus, sowie eine Laufschrift:

    #!/usr/bin/env python3
    # -*- coding: utf-8 -*-
    # interface_i2c_ht16k33_1.py
    #
    # Bit numbers for the Display: 2 Bytes per digit
    # HByte: FEDCBA98 LBYTE: 76543210
    # A = Bit 10, B = Bit 11, C = Bit 12, D = Bit 13, E = Bit 14 = decimal point
    #
    #   000000000
    #  58   9   A1
    #  5 8  9  A 1
    #  5  8 9 A  1
    #  5   89A   1
    #   6666 7777
    #  4   BCD   2
    #  4  B C D  2
    #  4 B  C  D 2
    #  4B   C   D2
    #   333333333   E

    from smbus import SMBus
    from time import sleep

    port = 1  # (0 for rev.1, 1 for rev 2!)
    bus = SMBus(port)
    dispAddr = 0x70

    d = {'0': 0x0C3F, '1': 0x0006, '2': 0x00DB, '3': 0x008F, '4': 0x00E6, \
         '5': 0x2069, '6': 0x00FD, '7': 0x0007, '8': 0x00FF, '9': 0x00EF, \
         '*': 0x3FC0, '+': 0x12C0, '-': 0x00C0, '_': 0x0008, '.': 0x4000, \
         '/': 0x0C00, "'": 0x0002, '°': 0x00E3, 'A': 0x00F7, 'B': 0x128F, \
         'C': 0x0039, 'D': 0x120F, 'E': 0x00F9, 'F': 0x0071, 'G': 0x00BD, \
         'H': 0x00F6, 'I': 0x1200, 'J': 0x001E, 'K': 0x2470, 'L': 0x0038, \
         'M': 0x0536, 'N': 0x2136, 'O': 0x003F, 'P': 0x00F3, 'Q': 0x203F, \
         'R': 0x20F3, 'S': 0x00ED, 'T': 0x1201, 'U': 0x003E, 'V': 0x0C30, \
         'W': 0x2836, 'X': 0x2D00, 'Y': 0x1500, 'Z': 0x0C09, '[': 0x00FF, \
         ']': 0x00EF, '|': 0x2D00, '=': 0x12C0, '@': 0x00FD, ' ': 0x0000}

    def dispInit():
        bus.write_byte(dispAddr, 0xE7)  # dimming 0-F (LowNibble)
        bus.write_byte(dispAddr, 0x21)  # turn system osc. on/off (Bit 0)
        bus.write_byte(dispAddr, 0x81)  # diplay on/off (Bit 0) blink (Bit2 + Bit1)

    def dispWrite(dispString):
        bus.write_word_data(dispAddr, 0, d[dispString[0]])
        bus.write_word_data(dispAddr, 2, d[dispString[1]])
        bus.write_word_data(dispAddr, 4, d[dispString[2]])
        bus.write_word_data(dispAddr, 6, d[dispString[3]])

    def dispTicker(tString, ttime):
        for i in range(len(tString) - 4 + 1):
            newString = tString[i] + tString[i + 1] + tString[i + 2] + \
                tString[i + 3]
            dispWrite(newString)
            sleep(ttime)

    dispInit()
    dispWrite('*GO*')
    sleep(2)

    try:
        while True:
            dispTicker('T3EC LOVES PYTHON    ', 0.5)
    except KeyboardInterrupt:
        print("Keyboard interrupt by user")

In der zweiten Zeile des Programms wird mit einem speziellen Kommentar dem Programm mitgeteilt welche Zeichenkodierung verwendet wird. Linux arbeitet mit utf-8.

    # -*- coding: utf-8 -*-

Wurde das Programm in Windows geschrieben und abgespeichert, so kann wegen der Zeichenkodierung eine Fehlermeldung auftreten. Man verwendet dann entweder die Kodierung iso-8859-1 oder löscht das fehlerhafte Zeichen im Python-Skript.

Das Programm arbeitet mit einem Python-Wörterbuch (dictionary). Das Dictionary ist eine Folge von Wertpaaren in geschweiften Klammern. Die Wertepaare sind durch Kommata getrennt; zwischen den Werten befindet sich ein Doppelpunkt. Der erste Wert ist der Schlüssel (key) mit dem man Zugriff auf den zweiten Wert, der auch mit Wert bezeichnet wird (value) hat. Ein typisches Anwendungsbeispiel ist ein reales Wörterbuch, z.B. Deutsch- Französisch:

    wb = {'angewandt':'appliqué','Informatik':'informatique','Schule':'école'}

Der Zugriff mit dem Schlüssel erfolgt mit eckigen Klammern. Der Befehl

    print(wb['angewandt'])

druckt das Wort "appliqué".

Wir nutzen das Dictionary hier um die Buchstaben zu kodieren. Für das 14-Segment Display werden pro Stelle (digit) 2 Byte benötigt. 14 Bit dienen der Ansteuerung der Segment-LEDS und das 15. Bit (Bit14) zur Ansteuerung des Dezimalpunkts. Um eine Eins darzustellen müssen nur die Segmente 1 (Bit1) und 2 (Bit 2) eingeschaltet werden. Dies ergibt für das HByte 0x00 und für das LByte 0b00000110 = 0x06, also findet man im Dictionary das Wertepaar '1':0x0006.

In der Funktion dispInit() werden einige Register des Chip HT16K33 initialisiert, damit dessen Oszillator schwingt und das Display eingeschaltet ist. Dazu wird die Methode

    bus.write_byte(dispAddr,0xE7)

verwendet, die nur ein Byte vom Master zum Slave schickt. Der Chip HT16K33 erkennt an den oberen 4 Bit, welches Register adressiert werden soll. Im einzelnen handelt es sich um folgende Register:

Dimming Setup Register: Kommando (HNibble) = 0xE. Mit den unteren 4 Bit kann eine Pulsweite in 16er-Schritten eingestellt werden. 0xE0: minimale Helligkeit; 0xEF: maximale Helligkeit.

System Setup Register: Kommando (HNibble) = 0x2. Bit 0 schaltet Systemtakt ein. default 0x20: system oscillator off; 0x21 system oscillator on.

Display Setup Register: Kommando (HNibble) = 0x8. Bit 0 schaltet das Display ein, und Bit 1 und Bit 2 legen die Blinkfrequenz fest. default 0x80: no blink Display off Display on: 0x81 no blink, 0x83: 2Hz, 0x85: 1Hz, 0x87: 0,5Hz

Weitere Informationen kann man dem Datenblatt entnehmen (z.B. S 30): http://www.adafruit.com/datasheets/ht16K33v110.pdf

Die Funktion dispWrite(dispString) schreibt die Daten (darzustellende Zeichen) ins RAM des Chip. Dies passiert mit der Methode

    bus.write_word_data(dispAddr,0,d[dispString[0]])

welche ein Wort (2 Byte) zum Slave schickt. Der 2 Parameter übergibt hier wie beim RTC den Adresszeiger fürs RAM. Er muss also jeweils um 2 erhöht werden. Mit Hilfe des Dictionary wird das richtige Word für jedes der 4 Zeichen des Strings ermittelt.

Die Funktion dispTicker(tString,ttime) erzeugt eine Laufschrift indem sie über die gesamte Zeichenkette iteriert und immer wieder von vier folgende Zeichen ausgibt. Mit ttime kann die Geschwindigkeit der Laufschrift eingestellt werden.

Aufgabe I10:

Ändere das obige Programm, so dass das Datum, die Uhrzeit und der Wochentag des RTC in Laufschrift ausgegeben werden.

Aufgabe I11:

Schließe den Temperatursensor (DS18B20) wieder an und gib zusätzlich die aktuelle Temperatur aus.

Aufgabe I12: (für Fleißige)

Der 8-Bit I/O Port-Expander-Chip PCF8574 ermöglicht es den Raspi um 8 digitale Ein- bzw. Ausgänge zu erweitern. Es handelt sich um einen Seriell/Parallel- Wandler. Ein über den I²C-Bus gesendetes Byte wird parallel an 8 Pins ausgegeben. Verbinde den Chip mit dem Raspi. Schließe zwei LEDs (Vorwiderstände!) und zwei Taster an. Die Taster sollen die LEDs abwechselnd ein und ausschalten. Weitere Informationen zum PCF8574: http://www.nxp.com/documents/data_sheet/PCF8574.pdf

Die asynchrone serielle Schnittstelle EIA232

Die EIA-232-Schnittstelle ist seit fast 50 Jahren standardisiert.Ebenso häufig wie die aktuelle Bezeichnung EIA-232 (EIA für Electronic Industries Alliance) findet man die alte Bezeichnung RS-232 (RS für Radio Sector bzw. Recommended Standard) oder die Bezeichnung V24. EIA-232 definiert die Verbindung zwischen einem Terminal (DTE) und einem Modem (DCE), was Timing, Spannungspegel, Protokoll und Stecker betrifft. Auch wenn diese Schnittstelle schon viele Jahre besteht, reicht ihre Geschwindigkeit für übliche Anwendungen oft aus. Sie ist äußerst robust und erlaubt auch größere Kabellängen. USB-EIA-232-Wandler ermöglichen die Verbindung mit dem PC falls keine serielle EIA-232-Schnittstelle mehr verfügbar ist.

Das asynchrone Verfahren

Bei der asynchronen Datenübertragung kann die Informationsübertragung zeichenweise zu einem beliebigen Zeitpunkt erfolgen. Ein an einem PC arbeitender Benutzer sendet z.B. diese Zeichen in zufälligen unvorhersehbaren Intervallen. Sender (Tastatur) und Empfänger (PC) sind daher nur während der Übermittlung eines einzelnen Zeichens synchronisiert. Die Synchronisation ist durch eine festgelegte Bitrate, ein festgelegtes Datenformat sowie die Verwendung von Start- und Stoppbits möglich (keine Taktleitung).

EIA232 Asynchron

Der Zeichenrahmen (SDU, Serial Data Unit)

Jedes einzelne Zeichen wird innerhalb eines Zeichenrahmens (frame, SDU, Serial Data Unit) zwischen Steuerbits eingefasst. Im inaktiven Zustand (Ruhezustand, es wird kein Zeichen übertragen) wird die Übertragungsleitung auf logisch 1 (Mark) gehalten. Der Beginn der Datenübertragung und damit auch die Synchronisation erfolgt mit Hilfe eines Startbits (logisch 0, Space), das an den Anfang eines jeden Zeichens gesetzt wird. Anschließend werden die Datenbits ausgesendet. Je nach gewähltem Code können dies 5, 6, 7 oder 8 Bits sein. Am häufigsten ist die Übertragung mit 8 Bit. Man beachte, dass das niederwertigste Datenbit (LSB, Least Significant Bit, D0) zuerst übertragen wird! Nach den Daten wird ein Paritätsbit und ein, anderthalb oder zwei Stoppbits (logisch 1) übertragen. Das Paritätsbit ist ein Kontrollbit, dient der Fehlererkennung, und bezieht sich nur auf die Datenbits. Heute wird es meist weggelassen. *Heutige Software arbeitet meist mit 8 Datenbit, ohne Parität und einem Stoppbit. Kurzschreibweise: 8N1 * Beispiel für die Übertragung des Buchstaben 'l':

EIA232 Zeichenrahmen (SDU)

Die Übertragungsgeschwindigkeit der EIA-232 Schnittstelle liegt zwischen 300 bit/s und 115200 bit/s. Bei jeder Signaländerung wird nur ein Bit übertragen. Die Übertragungsgeschwindigkeit ist somit der Baudrate (Signaländerung/Sekunde) bei EIA-232 gleichzusetzen. Heute werden durchweg höhere Bitraten als früher eingesetzt. Häufige Bitraten sind 2400, 4800, 9600, 14400, 19200, 28800, 38400, 57600 und 115200 bit/s (bzw. Baud (Bd)). Höhere Bitraten verringern die maximale Kabellänge (siehe: Die Reichweite von EIA-232)! Mit Standardkabeln ist bei einer Baudrate von 19200 Baud eine Reichweite von um die 15 m möglich1. Mit Kabeln, welche eine besonders niedriger Kapazität aufweisen (z.B. nicht geschirmtes Netzwerkkabel UTP CAT-5), lassen auch 45 m erreichen.

Empfänger und Sender müssen auf die gleiche Geschwindigkeit (Bit- bzw. Baudrate) und das gleiche Datenformat eingestellt werden (unter Datenformat versteht man die Zahl der Datenbits und Stoppbits sowie die Parität, Bsp.: 8N1).

Schnittstellensignale und Hardware

Bei der seriellen Schnittstelle können bis zu neun Daten, Steuer und Meldeleitungen verwendet werden. Es sind dabei viele unterschiedliche Kombinationen möglich. Bevor man zwei Geräte verbindet sollte man sich also genaustes Informieren wie die Schnittstelle eingesetzt wird. Die allerwichtigsten Schnittstellensignale sind die beiden Datenleitungen "Transmitter Data" (TxD) und "Receiver Data" (RxD) sowie die Masseleitung (GND). Mit diesen drei Leitungen ist eine bidirektionale Datenübertragung möglich. Heute werden glücklicherweise meist einfache Null-Modem-Verbindungen mit nur diesen drei Leitungen eingesetzt. Null-Modem bedeutet, dass die Leitungen gekreuzt werden müssen, so dass die Sendeleitung mit der gegenüberliegenden Empfangsleitung verbunden ist.

EIA232 Null-Modem

Für die EIA-232-Schnittstelle werden meist 9-polige D-Sub-Buchsen oder Stecker (Das D steht wegen der Ähnlichkeit der Buchse mit dem Großbuchstaben D) verwendet. Bei älteren PCs findet man noch den 9-poliger Stecker (männlich, Stifte). Bei neuen PCs verwendet man USB-EIA-232-Wandler-Kabel.

EIA232 Stecker DB9

Um akzeptable Reichweiten zu erhalten reicht die Spannungsdifferenz zwischen 0 un 5V bzw. 3,3V nicht aus. Auch ist Masse als logischer Low-Pegel nicht günstig, da hier ein Leitungsbruch nicht erkannt werden kann. EIA-232 arbeitet daher mit Spannungen zwischen 3V und 15V bzw. -3V un -15V. !!!!Niemals den RASPI sofort mit der seriellen Schnittstelle eines PC verbinden!!!!!! PCs arbeiten üblicherweise mit ±12 V, Notebooks mit ±7-8 V. Die Datenleitungen arbeiten mit negativer Logik! +12V entspricht also logisch Low, -12V entspricht logisch High.

EIA232 Spannungspegel

Eine digitale Schaltung mit TTL-Pegeln darf nicht ohne Pegelwandler an die serielle Schnittstelle angeschlossen werden, da sie sonst zerstört wird!

Will man mit TTL-Bausteinen die serielle Schnittstelle benutzen, so muss eine TTL/EIA-232-Pegelanpassung vorgenommen werden. Die Halbleiterindustrie bietet solche Pegelwandler als integrierte Schaltkreise an. Sie werden als "232-IC's" bezeichnet. Je nach Hersteller, sind verschiedene Buchstaben voran gesetzt (z.B. MAX232). Dies gilt natürlich nicht, wann man zum Beispiel den Raspi mit einem Mikrocontroller mit gleicher Versorgungsspannung (3,3V) verbindet.

Weitere Informationen zur seriellen Schnittstelle: http://weigu.lu/tutorials/avr-assembler/MICELB5Serielle_Schnittstelle.pdf

EIA-232 mit dem Raspberry Pi

Der Raspi besitzt nur eine richtige Hardware-UART-Schnittstelle (/dev/ttyAMA0). Beim neuen Raspi3 ist diese allerdings mit dem Bluetooth Modem verbunden. An den GPIO Pins befindet sich nur eine Mini-UART (/dev/ttyS0), die teilweise softwaremäßig bedient wird und nicht unbedingt mit einem stabilen Takt läuft.

Wir schalten deshalb bei einem Raspi 3 die Hardware-Schnittstelle wieder zurück auf die GPIO (Pin 8 TXD (BCM 14) und Pin 10 RxD (BCM 15)).

Dies tun wir indem wir die folgende Zeile

    dtoverlay=pi3-disable-bt

an die Datei/boot/config.txt anhängen.

Wird ein Raspi Image mit graphischer Oberfläche verwendet, so wird die serielle Schnittstelle im Menu (Himbeere) unter: Menu ⇨ Preferences ⇨ Raspberry Pi Configuration ⇨ Interfaces eingeschaltet. Bei einem Raspi ohne graphische Oberfläche geschieht die mit dem Programm sudo raspi-config (Punkt Advanced Options, A8 Serial).

Für weitere Infos: http://weigu.lu/sb-computer/raspi_tips_tricks.

Aufgabe I13:

Erledige alle Einstellungen damit die serielle Schnittstelle beim Raspi genutzt werden kann. Nutze dazu beim Raspi 3 einen Editor deiner Wahl mit Root-Rechten (zB: sudo nano). Boote danach den Raspi neu mit sudo reboot.

Im Normalfall nutzt der Raspberry Pi die serielle Schnittstelle als Konsole um mit der Außenwelt zu kommunizieren. Auch ohne Ethernet-Verbindung kann man sich so einloggen und Terminalbefehle ausführen.

Die Verbindung zum Raspi erfolgt über ein 3,3V-USB-EIA232-Kabel von adafruit (http://www.adafruit.com/product/954). Im Kabel ist ein Pegelwandler eingebaut (Profilic oder FTDI). Der Treiber des Betriebssystems stellt eine virtuelle serielle Schnittstelle zur Verfügung. Der weiße Draht ist mit der Sendeleitung TxD zu verbinden, der grüne Draht mit der Empfangsleitung RxD und der schwarze Draht mit GND (Masse). Der rote Draht (5V vom PC) darf nicht angeschlossen werden!! (Außer man möchte den Raspi über diese Verbindung mit Spannung versorgen (USB 2.0: max 500mA; USB 3.0: max 900mA); dann ist aber kein USB-Netzteil anzuschließen.) Beim Kauf des USB-EIA232-Kabels muss darauf geachtet werden, dass dieser auf der Sende- und Empfangsleitung einen maximalen Pegel von 3,3V liefert!

Auf dem PC wird eine Terminalsoftware benötigt. Ein quelloffenes freies Terminalprogramm findet man bei IFTOOLS https://iftools.com/download/index.en.php. Das Terminalprogramm cleverterm läuft auf Linux und Windows und bietet alles was man von einem Terminalprogramm erwartet. Nachdem man die Schnittstelle ausgewählt hat, kann man mit einem Klick auf Set die Baudrate (115200 bit/s) und das Datenformat (8N1) einstellen Es wird kein Handshake verwendet. Mit einem Klick auf den grünen Pfeil wird die Verbindung hergestellt. Oben ist das Empfangsfenster, unten das Sendefenster.

Aufgabe I14:

Verbinde den Raspi mit der seriellen Schnittstelle. Installiere das Terminalprogramm und stelle eine Verbindung mit dem Raspi her. Boote den Raspi neu und beobachte den Bootvorgang. Logge dich mit Hilfe des Terminalprogramms ein. Teste die Befehle ls -l und sudo reboot.

EIA232 CleverTerm

Um zu verhindern, dass die serielle Schnittstelle als Konsole genutzt wird und für unsere Python-Programme genutzt werden kann müssen wir in der Datei /boot/cmdline.txt den folgenden Text löschen:

console=serial0,115200 und die geänderte Datei abspeichern.

Achte darauf der der restliche Text nicht verändert wird.

Um die serielle Schnittstelle mit Python zu nutzen benötigen wir das Python Modul python-serial:

    sudo apt-get install python-serial

Jetzt lässt sich die serielle Schnittstelle für unsere Zwecke nutzen. Hier ein erstes Testprogramm:

    #!/usr/bin/env python3
    # -*- coding: utf-8 -*-
    # interface_eia232_1.py: sending text to UART
    #
    # Wiring: Raspi TxD: white Raspi RxD: green GND: black

    from serial import Serial

    ser = Serial("/dev/ttyAMA0", baudrate=115200, timeout=0.1)

    for i in range(10):
        ser.write(b"Hello T3EC\n")
    ser.close()

Zuerst wird ein serielles Objekt mit dem (beliebigen) Namen ser erstellt. Hierbei müssen als Parameter die serielle Schnittstelle (/dev/ttyAMA0), die Baudrate und das Timeout angegeben werden. Das Timeout gibt an wie viele Sekunden beim Einlesen von Daten gewartet wird ob Daten vorhanden sind. Nach dieser Zeit wird der Lese-Vorgang abgebrochen. Dann kann mit der Schreib-Methode (write()) Text über die serielle Schnittstelle ausgegeben werden. Zum Schluss muss die Schnittstelle geschlossen werden.

In Python 3 werden zwei unterschiedliche Typen für Text (string) und binäre Daten (bytes) benutzt. Bei der seriellen Schnittstelle muss man binäre Daten versenden. Es ist also nötig der Text in einen Binärstring überzuführen. Dies kann mit mit vorangestelltem b geschehen (ser.write(b"Hello T3EC\n")) oder aber mit der Methode encode() (ser.write("Hello T3EC\n".encode())). Als Parameter kann die Kodierung angegeben werden (utf-8, utf-16, latin-1). Ohne Angabe wird die default-Codierung des Laufzeit-Systems verwendet. Beim Raspi kann man diese mit folgendem Befehl ermitteln:

    locale charmap

Zum Einlesen von Daten kann man die Methoden read(n), readline(nmax) oder readlines()verwenden. In Klammern wird die Anzahl der zu lesenden Bytes (bzw. die maximale Anzahl der zu lesenden Bytes) angegeben. Wird das Timeout nicht respektiert, so bricht die Funktion ab. Die Methode decode() wandelt den Bytestring in einen Textstring zurück.

Das folgende Programm liest Daten ein:

    #!/usr/bin/env python3
    # -*- coding: utf-8 -*-
    # interface_eia232_2.py
    #
    # Wiring: Raspi TxD: white Raspi RxD: green GND: black

    from serial import Serial

    ser = Serial("/dev/ttyAMA0", baudrate=115200, timeout=0.1)

    try:
        while True:
            ser.write("Please type something:\n".encode())
            mytext = ser.read(30)
            while mytext.decode()=='':
                 mytext= ser.read(30)
            ser.write("You typed: ".encode() + mytext)
    except:
        print("Interrupted by user")
        ser.close()
Aufgabe I15:

Teste die beiden obigen Programme. Wieso wurde nicht einfach die Methode readline()verwendet?

Aufgabe I16:

Erweitere die Aufgabe I11, so dass Datum, Uhrzeit, Wochentag und Temperatur auch über die serielle Schnittstelle versendet werden.

I²C DS1307 HT16K33 EIA232 Schaltplan

Aufgabe I17:

Der Raspi soll den Text "Please type something:" zum PC senden. Der vom PC zurückgesendete Text soll dann auf dem Display (Laufschrift) dargestellt werden (Achtung! keine kleinen Buchstaben oder Sonderzeichen verwenden, die nicht auf dem Display dargestellt werden können).

Aufgabe I18:

Verbinde zwei Raspis über die serielle Schnittstelle miteinander (Sendeleitung an Empfangsleitung und umgekehrt). An beiden Raspis sollen auch 2 Schalter und 2 LEDs angeschlossen werden. Die beiden Schalter sollen jetzt die beiden LEDs des anderen Raspi schalten. Die Informationen wann welche LED ein ist soll über die serielle Schnittstelle versendet werden.

Aufgabe I19:

Die gleiche Aufgabe nur dass statt des Raspi ein Mikrocontroller verwendet wird (http://www.weigu.lu/tutorials/avr_assembler Kapitel serielle Schnittstelle).

Aufgabe I20: (für Freaks)

Statt des Terminalprogramms soll ein eigenes Python-Programm mit Tkinter für den PC programmiert werden, mit dem Daten von der seriellen Schnittstelle empfangen und gesendet werden können. Das Senden stellt kein Problem dar. Der Empfang muss aber die Mainloop von Tkinter unterbrechen können. Dies ist möglich mit der Methode after(). Die Parameter sind eine Zeit in Millisekunden und die aufzurufende Funktion.

Sie wird im Hauptprogramm aufgerufen.

    mainWin.after(10,receive)
    mainWin.mainloop()

Die Empfangsfunktion kann dann folgendermaßen aussehen:

    def receive():
       if (ser.inWaiting() != 0):
           sertextrec.set(ser.read(30).decode())
       mainWin.after(10,receive)  # reschedule event in 10 ms

Weitere Infos: http://infohost.nmt.edu/tcc/help/pubs/tkinter/web/universal.html Eine mögliche Lösung findet man unter: http://www.weigu.lu/tutorials/python_and_raspi/download

Eigenes Terminalprogramm