Der APDS9960-Sensor: Ein Blick hinter die Kulissen

Hier werden einzelne Projekte mit MicroPython vorgestellt
Antworten
Heinrichs
Beiträge: 183
Registriert: Do 21. Okt 2010, 18:31

Der APDS9960-Sensor: Ein Blick hinter die Kulissen

Beitrag von Heinrichs » Di 7. Mär 2023, 18:46

Der APDS9960-Sensor kann verschiedene Aufgaben ausführen: Er kann Abstände messen, Farbwerte ermitteln und Gesten erkennen. Für diese Aufgaben besitzt er die folgenden “Engines”:
  • die Proximity Engine
  • die Gesture Engine
  • die Color Engine
In dem Beitrag Abstandsmessung mit dem APDS9960-Sensor haben wir schon mit der Proximity-Engine gearbeitet. Diese Engine kann nach Bedarf konfiguriert werden: So kann man z. B. den Verstärkungsgrad des von den Photodioden erzeugten Signals einstellen oder Schwellen für Interrupt-Signale festlegen. Diese Einstellungen haben wir mit Hilfe von Methoden oder Properties vorgenommen; diese nehmen dazu passende Änderungen an den Registern des APDS9960 vor. Um z. B. dafür zu sorgen, dass der APDS9960 Interrupt-Signale bei der Abstandsmessung abgibt, muss man das Bit 5 im Register 0x80 (kurz: 0x80<5>) auf 1 setzen (vgl. S. 10 im Datasheet des APDS9960). Man hat den Registern und den einzelnen Bits Namen gegeben; so hat unser Register mit der Adresse 0x80 den Namen ENABLE und dessen Bit 5 die Bezeichnung PIEN (Proximity Interrupt ENable). Kurz gesagt wurde ENABLE<PIEN> auf 1 gesetzt.

Auch die Proximity-Messwerte werden einem Register entnommen: Der aktuelle ProximityLevel (vgl. Abstandsmessung mit dem APDS9960-Sensor) befindet sich im PDATA-Register (Adresse 0x9C). Ob überhaupt gerade ein Messwert vorliegt, auch das kann mit Hilfe eines Registers in Erfahrung gebracht werden. In diesem Fall ist es das Bit 1 (PVALID) des Registers STATUS (0x93), kurz: STATUS<PVALID>. Den Wert dieses Registers können wir mit dem Property statusRegister ermitteln. Mit der Schleife

Code: Alles auswählen

while (apds9960.statusRegister & 0b00000010) == 0:
    pass
kann man daher auf einen gültigen Wert im Register PDATA warten.


Die Proximity Engine

In Abb. 1 sehen wir ein vereinfachtes Flussdiagramm für die Proximity Engine. Mit der enableSensor()-Methode von apds9960.prox wird ENABLE<PEN> (Proximity Enable) auf 1 gesetzt. Damit kann die Proximity Engine ihre Arbeit beginnen. Daten werden gesammelt und im Register PDATA abgelegt. Nun wird PVALID auf 1 gesetzt - ein Zeichen dafür, dass ein Messwert vorliegt.

Danach wird kontrolliert, ob sich der Messwert innerhalb des Proximity-Thresholds (vgl. Abstandsmessung mit dem APDS9960-Sensor) befindet. Liegt er innerhalb, also zwischen den Werten in den Registern PILT und PIHT, dann wird zum Ausgang (EXIT) gesprungen. Andernfalls wird das STATUS<PINT> (Proximity Interrupt) auf 1 gesetzt. Wenn zuvor STATUS<PIEN> (s. o.) auf 1 gesetzt worden ist, dann wird der INT-Ausgang auf Low gelegt; damit kann dann ein Interrupt beim TTGO ausgelöst werden. Ansonsten wird wieder zum Ausgang gesprungen (und der INT-Ausgang bleibt auf High).

Man beachte: Der INT-Ausgang bleibt auf Low, solange PINT = 1 und PIEN = 1 sind. Deswegen muss innerhalb des Interrupt-Handlers PINT wieder auf 0 gesetzt werden, z. B. mit Hilfe der Methode clearInterrupt(). Auch durch einen Neustart des Programms ändert sich nichts am Zustand von PINT.
prox_engine_simplified_small_1.jpg
Abb. 1: Vereinfachtes Flussdiagramm für die Proximity Engine
prox_engine_simplified_small_1.jpg (14.37 KiB) 23177 mal betrachtet


Das Zustandsdiagramm des APDS9960

Im Beitrag Abstandsmessung mit dem APDS9960-Sensor hatten wir schon gesehen, dass mit wenigen Programmzeilen permanent Abstandswerte geliefert werden:

Code: Alles auswählen

s = apds9960.prox                            # mit apds9960 werden auch apds9960.prox (und apds9960.als) erzeugt
s.enableSensor()                             # Proximity Sensor einschalten
sleep_ms(7) # EXIT
while True:
    sleep_ms(200) # auf nächsten Messwert warten
    print(s.proximityLevel)
Im vorigen Abschnitt haben wir schonn angemerkt, dass ggf. mit Hilfe des PVALID-Bits auf das Vorliegen eines gültigen Proximity-Messwerts gewartet werden kann.

Folgende Fragen müssen wir nun noch klären:
  1. Welche Bedeutung hat die Zeile sleep_ms(7) # EXIT SLEEP?
  2. Wie haben wir eigentlich dafür gesorgt, dass die Proximity Engine und nicht eine andere Engine zur Ausführung kommt?
  3. Offensichtlich wird die Proximity Engine (ohne weitere Aufforderung durch unser Programm) immer wieder gestartet; ansonsten könnten wir mit s.proximityLevel nicht immer wieder neue Abstandswerte erhalten. Wie ist das zu erklären?
operational_states_prox_small.jpg
Abb. 2: Zustands-Diagramm
operational_states_prox_small.jpg (68.53 KiB) 23186 mal betrachtet

Alle drei Fragen können mit Hilfe des Zustandsdiagramm für den APDS9960 erklärt werden (Abb. 2). In diesem Bild sehen wir unsere drei Engines Proximity, Gesture und Color. Welche dieser Engines zur Ausführung kommt, wird wieder durch entsprechende Bits gesteuert. Im Wesentlichen entscheiden die Bits ENABLE<PEN> (Proximity ENable), ENABLE<GEN> (Gesture ENable) und ENABLE<AEN> (ALS ENable) darüber.

Wir klären jetzt die obigen Fragen, indem wir das Zustand-Diagramm aus Abb. 2 beginnend bei POR (Power Up) Schritt für Schritt durchlaufen. Nach einer internen Initialisierung (welche ca. 5,7 ms) dauert, geht der APDS9960 zunächst in einen (stromsparenden) Schlafzustand (SLEEP) über. Die Zeitspanne von 5,7 ms wird man außer Acht lassen können, solange zwischen dem Einschalten der Stromversorgung und dem Starten des Programms mehr als 5,7 ms vergehen.

Erst wenn ENABLE<PON> auf 1 gesetzt wird (Nebenbei: Dies wird bei der Instanziierung von apds9960 erledigt!), erwacht das Modul und kontrolliert, ob eine der Engines aktiviert worden ist. In unserem Fall ist es die Proximity Engine. Jetzt legt das Modul eine Pause von 7 ms ein; in dieser Zeit steht es uns nicht zur Verfügung. Das beantwortet die erste Frage.

Nun geht der Baustein in den IDLE-Zustand (Leerlauf) über. In unserem Fall verbleibt er aber nicht lange in diesem Zustand, sondern wechselt zur Proximity Engine, denn es sind PEN = 1 und GMODE = 0. Damit ist die 2. Frage beantwortet.

Wir folgen weiter den roten Pfeilen: Weil GMODE = 0, AEN = 0 und SAI = 0 sind (Sleep After Interrupt ist nicht aktiviert!), geht der Baustein wieder in den IDLE-Zustand über, um von dort erneut in die Proximity Engine einzusteigen... Die Proximity Engine wird jetzt also endlos durchlaufen, es sei denn, PEN wird zwischendurch auf 0 gesetzt (z. B. durch s.enableSensor(False)). Damit ist auch die dritte Frage beantwortet.

Die Color Engine und die Gesture Engine funktionieren übrigens nach dem gleichen Prinzip wie die Proximity Engine; allerdings sind sie deutlich komplexer. Für manche Zwecke müssen die Engines auch “vereint” tätig werden; deswegen sind die drei Engines auch nicht einfach “parallel geschaltet”.

.
Dateianhänge
Avago-APDS-9960-datasheet.pdf
(1.06 MiB) 2030-mal heruntergeladen

Antworten