Der APDS9960-Sensor: Lesen und Schreiben von Registern mit I2C
Verfasst: Mo 27. Mär 2023, 12:43
Um Daten vom APDS9960-Sensor zu erhalten oder Daten an ihn zu senden, muss man auf die einzelnen Register des APDS9960 zurückgreifen. In dem Beitrag Der APDS9960-Sensor: Ein Blick hinter die Kulissen hatten wir einige dieser Register und ihre Bedeutung im Zusammenhang mit der Proximity-Engine schon vorgestellt. In diesem Beitrag wollen wir darlegen, wie man dies mit Micropython bewerkstelligt. Folgende fünf Fälle wollen wir beispielhaft behandeln:
Die Daten werden mit Hilfe des I2C-Protokolls übertragen. Dazu muss eine entsprechende I2C-Instanz definiert worden sein; wir bezeichnen sie hier mit i2c. Die Adresse des APDS9960 ist
address = 0x39
Grundlegende Informationen zum I2C-Bus erhalten Sie z. B. hier. Für die oben angegebenen Fälle geben wir gleich entsprechende Funktionen an und erläutern sie.
Doch vorher noch ein wichtiger Hinweis: Sehr viele Sensoren und Aktoren arbeiten auf ähnliche Weise; deswegen kann man die folgenden Darlegungen leicht auch auf diese anwenden.
Wert in ein Register schreiben
Die Zahl val (zwischen 0 und 255) soll in das Register mit der Adresse reg geschrieben werden. Dazu benutzen wir die writeto_mem-Methode von i2c:
Als dritten Parameter erwartet die writeto_mem-Methode einen ByteString (Typ: bytes). Nun ist val vom Typ int. Dieser muss in den Typ bytes konvertiert werden. Dazu benutzen wir die Funktion bytes. Diese konvertiert Listen bzw. Tupel in ByteStrings; auf einzelne Zahlen kann sie nicht angewandt werden. Deswegen bilden wir zunächst das Tupel (val, ). Dieses besitzt nur ein einziges Element, nämlich val. (Das Komma ist hier wichtig; würde man es weglassen, dann würde Micropython (val) als val, d. h. als Zahl, und damit nicht als Tupel deuten!) Dieses Tupel kann nun mit der bytes-Funktion in einen ByteString konvertiert werden. Alternativ könnte man auch mit einer Liste arbeiten und die Konvertierung mit bytes([val]) durchführen lassen.
Wert aus einem Register lesen
Dies geschieht mit Hilfe der readfrom_mem-Methode von i2c:
Sie liefert als Rückgabewert den Zahlenwert (Typ int) des Inhalts vom Register reg. Bei der readfrom_mem-Methode sehen wir als dritten Parameter die Zahl 1. Sie gibt die Anzahl der zu lesenden Bytes an; würde hier z. B. die Zahl 4 stehen, dann würde unsere Methode das angegebene Register und zusätzlich auch die nächsten drei Register auslesen. Die readfrom_mem-Methode liefert nun als Rückgabewert val einen Bytestring, welchen wir mit
int.from_bytes(val, 'big', True)
in einen int-Typ konvertieren. Der 2. und 3. Parameter sind in diesem Fall irrelevant (weil der ByteString nur aus einem einzigen Byte besteht); die beiden Paramter dürfen aber nicht weggelassen werden. Die Konvertierung könnte alternativ auch mit Hilfe der list-Funktion durchgeführt werden; diese verwandelt einen ByteString in eines Liste von Zahlen (Typ int). Von dieser müssten wir dann das erste Element auswählen:
list(val)[0] oder kürzer auch nur val[0]
Wert aus mehreren Registern lesen
In einigen Fällen ist es sinnvoll, direkt mehrere aufeinander folgende Register auszulesen. Ein Beispiel stellen die Register GFIFO_U, GFIFO_D, GFIFO_L und GFIFO_R dar, in denen die Messwerte für die Up-, Down-, Left- und Right-Geste bereit gestellt werden. Mit der Funktion
erhalten wir als Rückgabewert einen Bytestring mit 4 Bytes, wenn number = 4 ist. Auf die int-Werte der einzelnen Bytes kann man dann wie oben zurückgreifen, z. B.:
data = readBytes(ADDRESS_GFIFO_U, 4)
GFIFO_L_Wert = data[2]
Einzelnes Bit aus einem Register lesen
Wir wollen uns die Vorgehensweise zunächst an einem Beispiel klar machen: Wir wollen das Bit mit der Nummer 5 aus dem Register 0xA2 auslesen. Als erstes lesen wir den Wert des Registers 0xA2 aus:
val = readByte(0xA2)
Wir bilden zunächst ein Byte, welches nur an der Position 5 eine 1 hat und sonst nur Nullen. Dazu wenden wir auf die Zahl 1 einen 5-fachen Links-Shift an:
mask = 1 << 5
In binärer Darstellung sieht mask jetzt so aus:
mask = 0b00100000
Nun bilden wir die UND-Verknüpfung von val und mask:
val = val & mask
val ist nun in binärer Darstellung 0b00100000 oder 0b00000000, je nachdem ob das Bit 5 den Wert 1 oder 0 hat. Wenn wir jetzt auf val einen 5-fachen Rechts-Shift anwenden, dann gibt val den Wert des Bits mit der Nummer 5 an.
Eine passende Funktion ist nun:
Einzelnes Bit in einem Register ändern
Auch hier wollen wir uns die Vorgehensweise zunächst an einem Beispiel klar machen: Im Register 0xA2 soll das Bit mit der Nummer 3 den Wert bitVal erhalten. Dieser kann True (bzw. 1) oder False (bzw. 0) sein. Als erstes lesen wir den Wert des Registers 0xA2 aus:
val = readByte(0xA2)
Wir bilden zunächst ein Byte, welches nur an der Position 3 eine 1 hat und sonst nur Nullen. Dazu wenden wir auf die Zahl 1 einen 3-fachen Links-Shift an:
mask = 1 << 3
In binärer Darstellung sieht mask jetzt so aus:
mask = 0b00001000
Wenn nun bitVal den Wert 1 hat, dann erhalten wird den neuen Wert für val erhalten durch eine ODER-Verknüpfung:
val = val | mask
Falls nun bitVal den Wert 0 hat, dann bilden wir von diesem mask-Wert das Einer-Komplement ~mask und verknüpfen dieses mit dem ursprünglichen Wert val durch eine UND-Operation:
val = val & ~ mask
Eine zugehörige Funktion kann dann so aussehen:
- Wert in ein Register des APDS9960 schreiben
- Wert aus einem Register lesen
- Wert aus mehreren Registern lesen
- Einzelnes Bit aus einem Register lesen
- Einzelnes Bit in einem Register ändern
Die Daten werden mit Hilfe des I2C-Protokolls übertragen. Dazu muss eine entsprechende I2C-Instanz definiert worden sein; wir bezeichnen sie hier mit i2c. Die Adresse des APDS9960 ist
address = 0x39
Grundlegende Informationen zum I2C-Bus erhalten Sie z. B. hier. Für die oben angegebenen Fälle geben wir gleich entsprechende Funktionen an und erläutern sie.
Doch vorher noch ein wichtiger Hinweis: Sehr viele Sensoren und Aktoren arbeiten auf ähnliche Weise; deswegen kann man die folgenden Darlegungen leicht auch auf diese anwenden.
Wert in ein Register schreiben
Die Zahl val (zwischen 0 und 255) soll in das Register mit der Adresse reg geschrieben werden. Dazu benutzen wir die writeto_mem-Methode von i2c:
Code: Alles auswählen
def writeByte(self, reg, val):
i2c.writeto_mem(address, reg, bytes((val,)))
Wert aus einem Register lesen
Dies geschieht mit Hilfe der readfrom_mem-Methode von i2c:
Code: Alles auswählen
def readByte(reg):
val =i2c.readfrom_mem(address, reg, 1)
return int.from_bytes(val, 'big', True)
int.from_bytes(val, 'big', True)
in einen int-Typ konvertieren. Der 2. und 3. Parameter sind in diesem Fall irrelevant (weil der ByteString nur aus einem einzigen Byte besteht); die beiden Paramter dürfen aber nicht weggelassen werden. Die Konvertierung könnte alternativ auch mit Hilfe der list-Funktion durchgeführt werden; diese verwandelt einen ByteString in eines Liste von Zahlen (Typ int). Von dieser müssten wir dann das erste Element auswählen:
list(val)[0] oder kürzer auch nur val[0]
Wert aus mehreren Registern lesen
In einigen Fällen ist es sinnvoll, direkt mehrere aufeinander folgende Register auszulesen. Ein Beispiel stellen die Register GFIFO_U, GFIFO_D, GFIFO_L und GFIFO_R dar, in denen die Messwerte für die Up-, Down-, Left- und Right-Geste bereit gestellt werden. Mit der Funktion
Code: Alles auswählen
def readBytes(reg, number):
return i2c.readfrom_mem(address, reg, number)
erhalten wir als Rückgabewert einen Bytestring mit 4 Bytes, wenn number = 4 ist. Auf die int-Werte der einzelnen Bytes kann man dann wie oben zurückgreifen, z. B.:
data = readBytes(ADDRESS_GFIFO_U, 4)
GFIFO_L_Wert = data[2]
Einzelnes Bit aus einem Register lesen
Wir wollen uns die Vorgehensweise zunächst an einem Beispiel klar machen: Wir wollen das Bit mit der Nummer 5 aus dem Register 0xA2 auslesen. Als erstes lesen wir den Wert des Registers 0xA2 aus:
val = readByte(0xA2)
Wir bilden zunächst ein Byte, welches nur an der Position 5 eine 1 hat und sonst nur Nullen. Dazu wenden wir auf die Zahl 1 einen 5-fachen Links-Shift an:
mask = 1 << 5
In binärer Darstellung sieht mask jetzt so aus:
mask = 0b00100000
Nun bilden wir die UND-Verknüpfung von val und mask:
val = val & mask
val ist nun in binärer Darstellung 0b00100000 oder 0b00000000, je nachdem ob das Bit 5 den Wert 1 oder 0 hat. Wenn wir jetzt auf val einen 5-fachen Rechts-Shift anwenden, dann gibt val den Wert des Bits mit der Nummer 5 an.
Eine passende Funktion ist nun:
Code: Alles auswählen
def readBit(reg, bitPos):
val = readByte(reg)
mask = 1 << bitPos
val = val & mask
return val >> bitPos
Einzelnes Bit in einem Register ändern
Auch hier wollen wir uns die Vorgehensweise zunächst an einem Beispiel klar machen: Im Register 0xA2 soll das Bit mit der Nummer 3 den Wert bitVal erhalten. Dieser kann True (bzw. 1) oder False (bzw. 0) sein. Als erstes lesen wir den Wert des Registers 0xA2 aus:
val = readByte(0xA2)
Wir bilden zunächst ein Byte, welches nur an der Position 3 eine 1 hat und sonst nur Nullen. Dazu wenden wir auf die Zahl 1 einen 3-fachen Links-Shift an:
mask = 1 << 3
In binärer Darstellung sieht mask jetzt so aus:
mask = 0b00001000
Wenn nun bitVal den Wert 1 hat, dann erhalten wird den neuen Wert für val erhalten durch eine ODER-Verknüpfung:
val = val | mask
Falls nun bitVal den Wert 0 hat, dann bilden wir von diesem mask-Wert das Einer-Komplement ~mask und verknüpfen dieses mit dem ursprünglichen Wert val durch eine UND-Operation:
val = val & ~ mask
Eine zugehörige Funktion kann dann so aussehen:
Code: Alles auswählen
def regWriteBit(reg, bitPos, bitVal):
val=readByte(reg)
mask = 1 << bitPos
if bitVal == 1:
val=val | mask
else:
val=val & ~mask
writeByte(reg, val)