Gestenerkennung mit APDS-9960

Bild

Beim Lesen der Kommentare zu meinem vorherigen Artikel über APDS-9960, in dem es um Farberkennung und Lichtstärke ging, wurden mir zwei Dinge klar: 1) Das Thema Gestenerkennung ist interessant und 2) Dieses Thema wird nicht veröffentlicht.

Wenn ich die Beschreibung des APDS-9960 aufgegriffen habe, sieht die Beschreibung ohne Berücksichtigung der Gesten etwas unvollständig aus. So fand ich auch etwas Freizeit, um dieses Thema zu erkunden.

In diesem Artikel möchte ich Sie auf einen Überblick über die Gestenerkennungsfunktionen des APDS-9960-Sensors aufmerksam machen.

In diesem Artikel wird der Mechanismus zum Einrichten des Sensors, zur Datenerfassung, -verarbeitung und -präsentation behandelt. Sie können selbst sehen, wie einfach es ist, mit dem APDS-9960 mit Gesten zu arbeiten.

Wie beim letzten Mal wird der Artikel von einem Code begleitet, in dem alles, was passiert, ausführlich beschrieben wird. Die Vollversion des Codes finden Sie am Ende des Artikels.

Sofort eine kleine Bemerkung: APDS-9960 verfügt nicht über einen eingebauten automatischen Gestenerkennungsmechanismus. das heißt, so dass ich genau hier das Register lese und dort bereits die verarbeitete Geste liegt - dies ist nicht in APDS-9960; und dies bedeutet, dass Sie Ihren eigenen Gesteninterpretationsalgorithmus schreiben müssen, was wir später tun werden.

Im Allgemeinen ist dies sowohl gut als auch nicht sehr gut. Nicht wirklich - weil es das Studium dieses Sensors für Anfänger erschweren kann, aber gut, weil Sie in Verbindung mit den Approximationsdaten durch Verfeinerung sogar Ihre eigenen Gesten verschiedener Art und jeder Art entwickeln können.

Da dieser Artikel jedoch nur eine Übersichtsfunktion hat, beschränken wir uns nur auf grundlegende Gesten nach OBEN, UNTEN, LINKS, RECHTS.

Nun, fangen wir an.

Theorie


Ich werde mir ein wenig Material erlauben.

Um die erforderlichen Informationen über die Bewegung und Bewegungsrichtung zu erhalten, verwendet der APDS-9960 eine IR-LED und vier Fotodioden, die, wie in der folgenden Abbildung dargestellt, Signale im nahen Infrarotbereich (NIR) erfassen.

Bild

Die IR-LED (LED) hat die Funktion der Hintergrundbeleuchtung, und Fotodioden (UDLR) registrieren das vom "Hindernis" reflektierte Licht.

Die Fotodioden befinden sich so am Sensor, dass die entsprechende Fotodiode je nach Bewegungsrichtung des „Hindernisses“ den größten Teil des reflektierten IR-Signals am Eingang und einen kleineren Teil am Ausgang empfängt. Gleichzeitig zeigt uns die Dokumentation zum APDS-9960 eindeutig, dass Sie die Bewegungsrichtung interpretieren können, indem Sie die Amplitude und Phasendifferenz der Signale von den UDLR-Fotodioden messen und vergleichen.

Bild

Übe


Für die Arbeit mit APDS-9960 sowie beim letzten Mal verwenden wir STM32VLDISCOVERY. Die Verbindung hat sich ebenfalls nicht geändert.

Konfigurieren Sie APDS-9960

Wir machen die Ersteinrichtung des Sensors.

So ist das:

APDS9960_init
void APDS9960_init(void) { i2c1_write(APDS9960_CONTROL, DEFAULT_PGAIN); i2c1_write(APDS9960_GPENTH, DEFAULT_GPENTH); i2c1_write(APDS9960_GEXTH, DEFAULT_GEXTH); i2c1_write(APDS9960_GCONF2, DEFAULT_GGAIN); i2c1_write(APDS9960_GPULSE, DEFAULT_PULSE_LENGTH); i2c1_write(APDS9960_PPULSE, DEFAULT_PULSE_LENGTH); } 

Was ist hier los? Lass es uns richtig machen.

 i2c1_write(APDS9960_CONTROL, DEFAULT_PGAIN); 

PGAIN (Proximity Gain Control) ist ein Parameter, der die Verstärkung der Näherungsempfindlichkeit steuert. Weisen Sie ihm den Wert 2 zu, der dem Vierfachen der Verstärkung entspricht.

 i2c1_write(APDS9960_GPENTH, DEFAULT_GPENTH); i2c1_write(APDS9960_GEXTH, DEFAULT_GEXTH); 

GPENTH (Gesture Proximity Enter Threshold Register) - Dieser Parameter legt den Proximity-Schwellenwert für die Bestimmung des Starts der Gestenerkennung fest.

GEXTH (Gesture Exit Threshold Register) legt jeweils einen Schwellenwert zur Bestimmung des Endes der Gestenerkennung fest.

 i2c1_write(APDS9960_GCONF2, DEFAULT_GGAIN); 

Im GCONF2-Register (Gesture Configuration Two) setzen wir explizit nur den Parameter GGAIN (Gesture Gain Control) auf das Vierfache des Verstärkungswerts.

 i2c1_write(APDS9960_GPULSE, DEFAULT_PULSE_LENGTH); i2c1_write(APDS9960_PPULSE, DEFAULT_PULSE_LENGTH); 

Hintergrundbeleuchtung Standardmäßig ist der Wert für die Stromquelle der Infrarot-LED-Hintergrundbeleuchtung auf 0 eingestellt, was einem Strom von 100 mA entspricht. Dies passt perfekt zu uns - wir werden ihn nicht ändern.

Die IR-Beleuchtung im APDS-9960 ist eine Folge von Impulsen und wird durch die entsprechenden Registerparameter für Gesten GPULSE (Gestenimpulszahl und -länge) charakterisiert: GPLEN (Gestenimpulslänge) und GPULSE (Anzahl der Gestenimpulse) sowie die PPULSE-Näherung (Näherungsimpulszählregister) ): PPLEN (Proximity Pulse Length) und PPULSE (Proximity Pulse Count) stellen die Anzahl der Impulse und die Periode jedes einzelnen Impulses ein.

Definieren Sie, dass GPLEN und PPLEN einen Wert von 2 gleich 16 μs und GPULSE und PPULSE einen Wert von 9 annehmen, was 10 Impulsen entspricht.

Wie Sie sehen können, erwies sich das Setup als etwas komplizierter als das ähnliche für die Farberkennung und Beleuchtung aus dem vorherigen APDS-9960-Test.

Daten lesen

Jetzt gehen wir zum Hauptprogrammzyklus über, in dem wir ab und zu Daten von Fotodioden registrieren und interpretieren und lernen, wie man eine Geste von einer anderen unterscheidet.

Lassen Sie uns zuerst den APDS-9960 mit Funktionen zum Arbeiten mit Gesten und Zoom starten.

 GesturesSet(GESTURES_START); 

Und sofort beginnen wir, den GVALID-Parameter zu verfolgen. GVALID (Gesten-FIFO-Daten) ist ein Parameter des GSTATUS-Registers (Gestenstatusregister), das uns in einem Zustand ungleich Null darüber informiert, dass der Sensor über verwendbare Gesten-Daten verfügt.

Die Dokumentation lehrt uns, dass sich Gesteninformationen im Puffer im Bereich des RAM befinden, der im Allgemeinen 32 x 4 Byte groß ist.

In der Praxis kann die tatsächliche Größe dieses Puffers durch Lesen des Werts des GFLVL-Registers (Gesture FIFO Level) ermittelt werden, d. H. Nach meinen rein empirischen experimentellen Beobachtungen wird GFLVL * 4 erhalten. Irgendwie so:

Bild

Nun, wie aus dem Namen des Puffers hervorgeht, sind die darin enthaltenen Daten in der Reihenfolge First In - First Out angeordnet. Das heißt, je „früher“ das Signal von jeder der Fotodioden eingetroffen ist, desto „höher“ ist es in der GFLVL.

Daten von Fotodioden (UDLR) können aus dem entsprechenden Gesten-FIFO-Register gelesen werden:

- GFIFO_U (Gesten-FIFO-Daten, UP)
- GFIFO_D (Gesten-FIFO-Daten, AB)
- GFIFO_L (Gesten-FIFO-Daten, LINKS)
- GFIFO_R (Gesten-FIFO-Daten, RECHTS)

Nach jedem Lesen von Werten aus diesen Registern wird GFLVL dekrementiert; Daher ist es in guter Weise notwendig, den gesamten Puffer zu lesen, bis die GFLVL Null erreicht.

Um Gesten zu definieren, benötigen wir nur die ersten vier Bytes dieses Puffers, nicht mehr. Deshalb werden wir sie nur lesen.

 GestureUp = i2c1_read(APDS9960_GFIFO_U); GestureDown = i2c1_read(APDS9960_GFIFO_D); GestureLeft = i2c1_read(APDS9960_GFIFO_L); GestureRight = i2c1_read(APDS9960_GFIFO_R); 

Gestenerkennung

Um zu interpretieren, welche Art von Geste aufgetreten ist, werden wir einfache Berechnungen durchführen:

 GestUpDown = GestureUp-GestureDown; GestLeftRight = GestureLeft-GestureRight; 

Um festzustellen, welche der Gesten im Moment passiert ist, sind uns nicht die Werte von GestUpDown und GestLeftRight wichtig, sondern nur das Zeichen einer reellen Zahl.

Mit anderen Worten, wenn wir die negativen und positiven Werte der Variablen GestUpDown und GestLeftRight als Eingabe verwenden, bestimmen wir, welche Geste perfekt ist.

Die Wahrheitstabelle für die Variablen GestUpDown und GestLeftRight ist in der folgenden Abbildung dargestellt.

Bild

Setzen Sie nun die GFLVL zurück:

 GesturesSet(GESTURES_STOP); 

... und zurück zum Beginn des Hauptprogrammzyklus.

Und jetzt der ganze Code:

main.c
 #include "stm32f10x.h" #define APDS9960_I2C_ADDR 0x39 #define APDS9960_ENABLE 0x80 #define APDS9960_GSTATUS 0xAF #define APDS9960_GFLVL 0xAE //Gesture FIFO Register (0xFC – 0xFF): #define APDS9960_GFIFO_U 0xFC #define APDS9960_GFIFO_D 0xFD #define APDS9960_GFIFO_L 0xFE #define APDS9960_GFIFO_R 0xFF #define APDS9960_CONTROL 0x8F #define APDS9960_GPENTH 0xA0 #define APDS9960_GEXTH 0xA1 #define APDS9960_GCONF2 0xA3 #define APDS9960_GPULSE 0xA6 #define APDS9960_PPULSE 0x8E #define GESTURES_START 0x01 #define GESTURES_STOP 0x02 #define DEFAULT_GPENTH 40 // Threshold for entering gesture mode #define DEFAULT_GEXTH 30 // Threshold for exiting gesture mode #define DEFAULT_PGAIN 8 // Proximity Gain Control: 4X #define DEFAULT_GGAIN 0x40 // Gesture Gain Control: 4X #define DEFAULT_PULSE_LENGTH 0x89 // 16us, 10 pulses /* Bit fields */ #define APDS9960_PON 0x01 #define APDS9960_AEN 0x02 #define APDS9960_PEN 0x04 #define APDS9960_WEN 0x08 #define APSD9960_AIEN 0x10 #define APDS9960_PIEN 0x20 #define APDS9960_GEN 0x40 #define APDS9960_GVALID 0x01 int GestUpDown = 0; int GestLeftRight = 0; //----------------------------------------------------------------------- uint8_t i2c1_read(uint8_t addr); void i2c1_write(uint8_t addr, uint8_t data); void I2C1_init(void) { I2C_InitTypeDef I2C_InitStructure; GPIO_InitTypeDef GPIO_InitStructure; RCC_APB1PeriphClockCmd(RCC_APB1Periph_I2C1, ENABLE); RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB| RCC_APB2Periph_AFIO , ENABLE); GPIO_InitStructure.GPIO_Speed = GPIO_Speed_2MHz; GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_OD; GPIO_InitStructure.GPIO_Pin = GPIO_Pin_6 | GPIO_Pin_7; GPIO_Init(GPIOB, &GPIO_InitStructure); I2C_StructInit(&I2C_InitStructure); I2C_InitStructure.I2C_ClockSpeed = 100000; I2C_InitStructure.I2C_OwnAddress1 = 0x01; I2C_InitStructure.I2C_Ack = I2C_Ack_Enable; I2C_Init(I2C1, &I2C_InitStructure); I2C_Cmd(I2C1, ENABLE); } //----------------------------------------------------------------------- void APDS9960_init(void) { i2c1_write(APDS9960_CONTROL, DEFAULT_PGAIN); i2c1_write(APDS9960_GPENTH, DEFAULT_GPENTH); i2c1_write(APDS9960_GEXTH, DEFAULT_GEXTH); i2c1_write(APDS9960_GCONF2, DEFAULT_GGAIN); i2c1_write(APDS9960_GPULSE, DEFAULT_PULSE_LENGTH); i2c1_write(APDS9960_PPULSE, DEFAULT_PULSE_LENGTH); } //----------------------------------------------------------------------- uint8_t i2c1_read(uint8_t addr) { uint8_t data; while(I2C_GetFlagStatus(I2C1, I2C_FLAG_BUSY)); I2C_GenerateSTART(I2C1, ENABLE); while(!I2C_CheckEvent(I2C1, I2C_EVENT_MASTER_MODE_SELECT)); I2C_Send7bitAddress(I2C1, APDS9960_I2C_ADDR<<1, I2C_Direction_Transmitter); while(!I2C_CheckEvent(I2C1, I2C_EVENT_MASTER_TRANSMITTER_MODE_SELECTED)); I2C_SendData(I2C1, addr); while(!I2C_CheckEvent(I2C1, I2C_EVENT_MASTER_BYTE_TRANSMITTED)); I2C_GenerateSTART(I2C1, ENABLE); while(!I2C_CheckEvent(I2C1, I2C_EVENT_MASTER_MODE_SELECT)); I2C_Send7bitAddress(I2C1, APDS9960_I2C_ADDR<<1, I2C_Direction_Receiver); while(!I2C_CheckEvent(I2C1,I2C_EVENT_MASTER_BYTE_RECEIVED)); data = I2C_ReceiveData(I2C1); while(!I2C_CheckEvent(I2C1, I2C_EVENT_MASTER_BYTE_RECEIVED)); I2C_AcknowledgeConfig(I2C1, DISABLE); I2C_GenerateSTOP(I2C1, ENABLE); while(I2C_GetFlagStatus(I2C1, I2C_FLAG_BUSY)); return data; } //----------------------------------------------------------------------- void i2c1_write(uint8_t addr, uint8_t data) { I2C_GenerateSTART(I2C1, ENABLE); while(!I2C_CheckEvent(I2C1, I2C_EVENT_MASTER_MODE_SELECT)); I2C_Send7bitAddress(I2C1, APDS9960_I2C_ADDR<<1, I2C_Direction_Transmitter); while(!I2C_CheckEvent(I2C1, I2C_EVENT_MASTER_TRANSMITTER_MODE_SELECTED)); I2C_SendData(I2C1, addr); while(!I2C_CheckEvent(I2C1, I2C_EVENT_MASTER_BYTE_TRANSMITTED)); I2C_SendData(I2C1, data); while(!I2C_CheckEvent(I2C1, I2C_EVENT_MASTER_BYTE_TRANSMITTED)); I2C_GenerateSTOP(I2C1, ENABLE); while(I2C_GetFlagStatus(I2C1, I2C_FLAG_BUSY)) {}; } //----------------------------------------------------------------------- void GesturesSet(uint8_t GestSel) { switch (GestSel) { case GESTURES_START: i2c1_write(APDS9960_ENABLE, APDS9960_GEN | APDS9960_PEN | APDS9960_PON); break; case GESTURES_STOP: i2c1_write(APDS9960_ENABLE, APDS9960_PEN | APDS9960_PON); break; default: i2c1_write(APDS9960_ENABLE, APDS9960_GEN | APDS9960_PEN | APDS9960_PON); } } //----------------------------------------------------------------------- int main() { uint8_t GFLVL_buf = 0; uint8_t GSTATUS_buf = 0; uint8_t GestureUp = 0; uint8_t GestureDown = 0; uint8_t GestureLeft = 0; uint8_t GestureRight = 0; I2C1_init(); APDS9960_init(); while (1) { GFLVL_buf = 0; GSTATUS_buf = 0; GestureUp = 0; GestureDown = 0; GestureLeft = 0; GestureRight = 0; GestUpDown = 0; GestLeftRight = 0; GesturesSet(GESTURES_START); GSTATUS_buf = i2c1_read(APDS9960_GSTATUS); if(GSTATUS_buf & APDS9960_GVALID) { GFLVL_buf = i2c1_read(APDS9960_GFLVL); if(GFLVL_buf) { GestureUp = i2c1_read(APDS9960_GFIFO_U); GestureDown = i2c1_read(APDS9960_GFIFO_D); GestureLeft = i2c1_read(APDS9960_GFIFO_L); GestureRight = i2c1_read(APDS9960_GFIFO_R); //Truth table: //UP: GestUpDown(+) | GestLeftRight(+) //DOWN: GestUpDown(-) | GestLeftRight(-) //LEFT: GestUpDown(+) | GestLeftRight(-) //RIGHT: GestUpDown(-) | GestLeftRight(+) GestUpDown = GestureUp-GestureDown; GestLeftRight = GestureLeft-GestureRight; GesturesSet(GESTURES_STOP); } } } } 


Ich möchte darauf hinweisen, dass der Gestenmechanismus des APDS-9960 sehr gut funktioniert. Die Erkennung ist stabil, die im APDS-9960 UV und IR integrierten Filter funktionieren gut.

Ich hoffe, dieses Material ist für jemanden nützlich. Vielen Dank für Ihre Aufmerksamkeit.

Source: https://habr.com/ru/post/de424947/


All Articles