IO Expander

Kommen wir nun zum IO Expander, welcher auf unserem Mainboard liegt. Der IO Expander ist eine I2C-Schnittstelle an dem unsere Sensoren hauptsächlich angeschlossen sind. Der IO Expander erweitert die Pinne des micro:bit, sodass es überhaupt möglich ist all die Sensoren des Joy-Cars zu verwenden.

Hier findest du mehr über den IO Expander.

Im folgenden Beispiel könnt ihr den IO Expander auslesen und die verfügbaren Pins entsprechend beschreiben. Dabei ist es wichtig, die Unterschiede zwischen den verschiedenen Revisionen des IO Expanders zu beachten. Weitere Informationen zu den Revisionen findet ihr hier.

Unterschiede zwischen den Revisionen:

  • Revision 1.3: Die Pins 0, 1, und 7 sind frei und können beschrieben werden.
  • Ältere Revisionen: Nur Pin 7 ist frei und kann verwendet werden.

 

Im Beispiel wird zunächst Pin 7 des IO Expanders auf LOW geschaltet und anschließend wieder auf HIGH zurückgesetzt. Im Anschluss daran wird der IO Expander in einer Endlosschleife jede Sekunde ausgelesen, um den aktuellen Zustand der Pins kontinuierlich zu überwachen.

Revision

Dieser Codeblock definiert die Revisionsnummer deines Joy-Cars, die du auf der Unterseite des Mainboards finden kannst. Weitere Informationen dazu findest du hier.

Durch die Definition der Revisionsnummer wird sichergestellt, dass der Code für unterschiedliche Revisionen des Joy-Cars verwendet werden kann, indem er sich automatisch an die spezifischen Unterschiede der jeweiligen Hardwareversion anpasst. Dadurch wird der gleiche Codeblock universell nutzbar und erleichtert die Handhabung verschiedener Joy-Car Versionen.

IO Expander auslesen

Dieser Codeblock liest den IO Expander aus und gibt die ermittelten Werte als String zurück. In diesem String repräsentiert die erste Zahl den Status von Pin 0. Die restlichen Zahlen im String entsprechen den Zuständen der weiteren Pins in der Reihenfolge ihrer Nummerierung.

Die zurückgegebenen Daten haben folgende Struktur:

  1. Linker Geschwindigkeitssensor
  2. Rechter Geschwindigkeitssensor
  3. Linker Linetracker
  4. Mittlerer Linetracker
  5. Rechter Linetracker
  6. Linker Hindernissensor
  7. Rechter Hindernissensor
  8. Freier Pin 7
  9. Freier Pin 0 (nur ab Revision 1.3 verfügbar)
  10. Freier Pin 1 (nur ab Revision 1.3 verfügbar)

Dies ermöglicht eine einfache Überprüfung und Verarbeitung der Pin-Zustände in einer lesbaren und strukturierten Form.

 

IO Expander beschreiben

 

Mit diesem Codeblock können die freien Pins des IO Expanders beschrieben werden, indem sie auf HIGH oder LOW gesetzt werden. Dabei ist zu beachten, dass die Verfügbarkeit der freien Pins von der Revision des Joy-Cars abhängt. Weitere Informationen zu den Unterschieden zwischen den Revisionen findest du hier. Der Code berücksichtigt diese Unterschiede und stellt sicher, dass nur die Pins beschrieben werden, die für die jeweilige Revision freigegeben sind.

Codebeispiel

JoyCar.initJoyCar(RevisionMainboard.OnepThree)
JoyCar.writeIOExpander(IOExpanderPin.Pin7, 0)
serial.writeLine(JoyCar.readIOExpander())
basic.pause(1000)
JoyCar.writeIOExpander(IOExpanderPin.Pin7, 1)
serial.writeLine(JoyCar.readIOExpander())
basic.pause(1000)
basic.forever(function () {
    serial.writeLine(JoyCar.readIOExpander())
    basic.pause(1000)
})

Revision & Initialisierung

Zunächst muss die Revision des Joy-Cars definiert werden, um sicherzustellen, dass die nachfolgenden Methoden korrekt auf die jeweilige Version des Joy-Cars angepasst sind. Die Revisionsnummer deines Joy-Cars findest du hier. Diese Revisionsnummer wird einfach in die Variable joycar_rev eingetragen.

Anschließend wird der IO Expander initialisiert, sodass er für die weiteren Schritte im Programm genutzt werden kann. Die Initialisierung ermöglicht die Kommunikation mit dem IO Expander und stellt sicher, dass die Pins entsprechend der definierten Revision konfiguriert werden können.

# Definiere deine Joy Car Mainboard Revision
joycar_rev = 1.3

# Initialisierung der I2C-Schnittstelle für das Joy Car Mainboard
i2c.init(freq=400000, sda=pin20, scl=pin19)

IO Expander auslesen

Die Methode fetchSensorData dient dazu, den IO Expander auszulesen und die erfassten Daten in ein Array namens bol_data_dict zu schreiben. Dieses Array wird anschließend zurückgegeben. Die Methode passt die Zuordnung der Daten an die Revision des Joy-Cars an.

Bei Revisionen ab 1.3 sind die Geschwindigkeitssensoren nicht mehr an den IO Expander angeschlossen. Stattdessen repräsentieren die 8. und 9. Position im Array die Zustände der freien Pins Pin 0 und Pin 1. Die 0. und 1. Position bleiben weiterhin den Geschwindigkeitssensoren zugeordnet.

Die zurückgegebenen Daten im Array haben folgende Struktur:

  1. Linker Geschwindigkeitssensor
  2. Rechter Geschwindigkeitssensor
  3. Linker Linetracker
  4. Mittlerer Linetracker
  5. Rechter Linetracker
  6. Linker Hindernissensor
  7. Rechter Hindernissensor
  8. Freier Pin 7
  9. Freier Pin 0 (nur ab Revision 1.3 verfügbar)
  10. Freier Pin 1 (nur ab Revision 1.3 verfügbar)

Diese Struktur stellt sicher, dass alle Sensordaten und freien Pins klar und eindeutig zugeordnet sind, unabhängig von der verwendeten Revision.

# alle Sensordaten abrufen
def fetchSensorData():
    # Da die zfill-Funktion nicht in micro:bit Micropython enthalten ist, 
    # muss sie als Funktion eingefügt werden
    def zfill(s, width):
        return '{:0>{w}}'.format(s, w=width)

    # Hexadezimale Daten lesen und in binäre Daten umwandeln
    data = "{0:b}".format(ord(i2c.read(0x38, 1)))
    # Füllen Sie die Daten bei Bedarf auf 8 Stellen auf.
    data = zfill(data, 8)
    # bol_data_dict as dictionary deklarieren
    bol_data_dict = {}
    # Zähler für die Schleife, die die Daten aus data in bol_data_dict einträgt
    bit_count = 7
    # Übertragen Sie die Daten von data nach bol_data_dict
    for i in data:
        if i == "0":
            bol_data_dict[bit_count] = False
            bit_count -= 1
        else:
            bol_data_dict[bit_count] = True
            bit_count -= 1

    # Ab Mainboard-Revision 1.3 sind die Geschwindigkeitssensoren auf separaten Pins
    if joycar_rev >= 1.3:
        bol_data_dict[8], bol_data_dict[9] = bol_data_dict[0], bol_data_dict[1]
        bol_data_dict[0] = bool(pin14.read_digital())
        bol_data_dict[1] = bool(pin15.read_digital())

    # Bit 0 = GeschwindigkeitLinks, Bit 1 = GeschwindigkeitRechts, Bit 2 = LineTrackerLinks,
    # bit 3 = LineTrackerMitte, bit 4 = LineTrackerRechts,
    # bit 5 = HindernisLinks, bit 6 = HindernisRechts, bit 7 = freier Pin(7)
    # (bit 8 = frei (pin0) bit 9 = frei (pin1)) - nur mit Revision 1.3 oder neuer
    return bol_data_dict

IO Expander beschreiben

 

Die Methode writeIOExpander ermöglicht das gezielte Setzen der freien Pins des IO Expanders auf HIGH oder LOW. Dabei wird:

  • pin: verwendet, um den spezifischen Pin auszuwählen, der beschrieben werden soll.
  • value: gibt an, ob der Pin auf HIGH (1) oder LOW (0) gesetzt wird.

 

Die Methode berücksichtigt die jeweilige Revision des Joy-Cars, um sicherzustellen, dass nur die zulässigen Pins beschrieben werden können:

  • Ab Revision 1.3: Pins 0, 1, und 7 können beschrieben werden.
  • Vor Revision 1.3: Nur Pin 7 ist verfügbar.

 

Durch diese Methode wird sichergestellt, dass keine unzulässigen Pins verändert werden, und gleichzeitig ermöglicht sie eine flexible und sichere Steuerung der Pins unabhängig von der Joy-Car-Revision.

# Daten auf IO-Expander als Byte schreiben
def writeIOExpander(pin, value):
    # Lesen des aktuellen Zustands vom IO-Expander
    expander_state = ord(i2c.read(0x38, 1))
    # Neuere Versionen als 1.3 haben 3 mögliche Pins zu verwenden (0, 1 und 7)
    # Bei älteren Versionen kann nur Pin 7 frei verwendet werden.
    # Eingangspins sind auf den Wert 1 eingestellt.
    if joycar_rev < 1.3:
        if pin == 7:
            if value == 1:
                i2c.write(0x38, bytes(str(chr(expander_state | 0x80 | 0x7F)), 'ascii'))
            elif value == 0:
                i2c.write(0x38, bytes(str(chr((expander_state & 0x7F) | 0x7F))
                                      , 'ascii'))
    if joycar_rev >= 1.3:
        if pin == 0:
            if value == 1:
                i2c.write(0x38, bytes(str(chr(expander_state | 0x01 | 0x7C)), 'ascii'))
            elif value == 0:
                i2c.write(0x38, bytes(str(chr((expander_state & 0xFE) | 0x7C))
                                      , 'ascii'))
        elif pin == 1:
            if value == 1:
                i2c.write(0x38, bytes(str(chr(expander_state | 0x02) | 0x7C), 'ascii'))
            elif value == 0:
                i2c.write(0x38, bytes(str(chr((expander_state & 0xFD) | 0x7C))
                                      , 'ascii'))
        elif pin == 7:
            if value == 1:
                i2c.write(0x38, bytes(str(chr(expander_state | 0x80 | 0x7C)), 'ascii'))
            elif value == 0:
                i2c.write(0x38, bytes(str(chr((expander_state & 0x7F) | 0x7C))
                                      , 'ascii'))

Codebeispiel

# Notwendige Bibliotheken importieren
from microbit import *

# Definiere deine Joy Car Mainboard Revision
joycar_rev = 1.3

# Initialisierung der I2C-Schnittstelle für das Joy Car Mainboard
i2c.init(freq=400000, sda=pin20, scl=pin19)

# alle Sensordaten abrufen
def fetchSensorData():
    # Da die zfill-Funktion nicht in micro:bit Micropython enthalten ist, 
    # muss sie als Funktion eingefügt werden
    def zfill(s, width):
        return '{:0>{w}}'.format(s, w=width)

    # Hexadezimale Daten lesen und in binäre Daten umwandeln
    data = "{0:b}".format(ord(i2c.read(0x38, 1)))
    # Füllen Sie die Daten bei Bedarf auf 8 Stellen auf.
    data = zfill(data, 8)
    # bol_data_dict as dictionary deklarieren
    bol_data_dict = {}
    # Zähler für die Schleife, die die Daten aus data in bol_data_dict einträgt
    bit_count = 7
    # Übertragen Sie die Daten von data nach bol_data_dict
    for i in data:
        if i == "0":
            bol_data_dict[bit_count] = False
            bit_count -= 1
        else:
            bol_data_dict[bit_count] = True
            bit_count -= 1

    # Ab Mainboard-Revision 1.3 sind die Geschwindigkeitssensoren auf separaten Pins
    if joycar_rev >= 1.3:
        bol_data_dict[8], bol_data_dict[9] = bol_data_dict[0], bol_data_dict[1]
        bol_data_dict[0] = bool(pin14.read_digital())
        bol_data_dict[1] = bool(pin15.read_digital())

    # Bit 0 = GeschwindigkeitLinks, Bit 1 = GeschwindigkeitRechts, Bit 2 = LineTrackerLinks,
    # bit 3 = LineTrackerMitte, bit 4 = LineTrackerRechts,
    # bit 5 = HindernisLinks, bit 6 = HindernisRechts, bit 7 = freier Pin(7)
    # (bit 8 = frei (pin0) bit 9 = frei (pin1)) - nur mit Revision 1.3 oder neuer
    return bol_data_dict

# Daten auf IO-Expander als Byte schreiben
def writeIOExpander(pin, value):
    # Lesen des aktuellen Zustands vom IO-Expander
    expander_state = ord(i2c.read(0x38, 1))
    # Neuere Versionen als 1.3 haben 3 mögliche Pins zu verwenden (0, 1 und 7)
    # Bei älteren Versionen kann nur Pin 7 frei verwendet werden.
    # Eingangspins sind auf den Wert 1 eingestellt.
    if joycar_rev < 1.3:
        if pin == 7:
            if value == 1:
                i2c.write(0x38, bytes(str(chr(expander_state | 0x80 | 0x7F)), 'ascii'))
            elif value == 0:
                i2c.write(0x38, bytes(str(chr((expander_state & 0x7F) | 0x7F))
                                      , 'ascii'))
    if joycar_rev >= 1.3:
        if pin == 0:
            if value == 1:
                i2c.write(0x38, bytes(str(chr(expander_state | 0x01 | 0x7C)), 'ascii'))
            elif value == 0:
                i2c.write(0x38, bytes(str(chr((expander_state & 0xFE) | 0x7C))
                                      , 'ascii'))
        elif pin == 1:
            if value == 1:
                i2c.write(0x38, bytes(str(chr(expander_state | 0x02) | 0x7C), 'ascii'))
            elif value == 0:
                i2c.write(0x38, bytes(str(chr((expander_state & 0xFD) | 0x7C))
                                      , 'ascii'))
        elif pin == 7:
            if value == 1:
                i2c.write(0x38, bytes(str(chr(expander_state | 0x80 | 0x7C)), 'ascii'))
            elif value == 0:
                i2c.write(0x38, bytes(str(chr((expander_state & 0x7F) | 0x7C))
                                      , 'ascii'))

# Zusätzlichen Pin 7 auf low setzen
writeIOExpander(7, 0)
print(fetchSensorData())
sleep(1000)

# Zusätzlichen Pin 7 auf high setzen
writeIOExpander(7, 1)
print(fetchSensorData())
sleep(1000)

while True:
    # Abgerufene Daten ausgeben
    print(fetchSensorData())
    sleep(1000)