Reconnaissance des gestes avec APDS-9960

image

En lisant les commentaires sur mon article précédent sur APDS-9960, où il était question de reconnaissance des couleurs et du niveau de lumière, deux choses sont devenues évidentes pour moi: 1) le sujet de la reconnaissance des gestes est intéressant et 2) ce sujet n'est pas divulgué.

En effet, si j'ai repris la description de l'APDS-9960, alors sans considérer les gestes, la description semble quelque peu incomplète. J'ai donc trouvé du temps libre pour explorer ce sujet aussi.

Dans cet article, j'attire votre attention sur un aperçu des capacités de reconnaissance gestuelle fournies par le capteur APDS-9960.

L'article examinera le mécanisme de configuration du capteur, la collecte, le traitement et la présentation des données. Vous voyez vous-même combien il est facile de travailler avec des gestes à l'aide de l'APDS-9960.

Comme la dernière fois, l'article sera accompagné d'un code, tout ce qui se passe dans lequel sera décrit en détail. La version complète du code est disponible à la fin de l'article.

Immédiatement une petite remarque: APDS-9960 n'a pas de mécanisme de détection automatique de gestes intégré; c'est-à-dire que, ici, je lis, cela signifie, le registre, et là se trouve déjà le geste traité - ce n'est pas dans APDS-9960; et cela signifie que vous devez écrire votre propre algorithme d'interprétation des gestes, ce que nous ferons plus tard.

En général, c'est à la fois bon et pas très bon. Pas vraiment - car cela peut compliquer l'étude de ce capteur pour un débutant, mais bon, car, avec les données sur l'approximation, vous pouvez, par raffinement, même imaginer vos propres gestes de différentes sortes.

Mais, puisque cet article n'a qu'une fonction de vue d'ensemble, nous nous limitons aux gestes de base HAUT-BAS-GAUCHE-DROITE.

Eh bien, commençons.

Théorie


Je vais me permettre un peu de matériel.

Pour obtenir les informations nécessaires sur le mouvement et la direction du mouvement, l'APDS-9960 utilise une LED IR et quatre photodiodes qui, comme illustré dans la figure ci-dessous, détectent les signaux dans le proche infrarouge (NIR).

image

La LED IR (LED) a la fonction de rétro-éclairage et les photodiodes (UDLR) enregistrent la lumière réfléchie par "l'obstacle".

Les photodiodes sont situées sur le capteur de telle manière que, selon le sens de déplacement de «l'obstacle», la photodiode correspondante recevra la plus grande partie du signal IR réfléchi à l'entrée et une plus petite partie à la sortie. Dans le même temps, la documentation sur l'APDS-9960 nous dit sans équivoque que vous pouvez interpréter la direction du mouvement en mesurant et en comparant l'amplitude et la différence de phase des signaux des photodiodes UDLR.

image

Pratique


Pour travailler avec APDS-9960, ainsi que la dernière fois , nous utiliserons STM32VLDISCOVERY. La connexion n'a pas non plus changé.

Configurer APDS-9960

Nous faisons la configuration initiale du capteur.

Comme ça:

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); } 

Que se passe-t-il ici? Faisons les choses correctement.

 i2c1_write(APDS9960_CONTROL, DEFAULT_PGAIN); 

PGAIN (Proximity Gain Control) est un paramètre qui contrôle le gain de sensibilité de proximité. Attribuez-lui une valeur de 2, ce qui correspond à quatre fois le gain.

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

GPENTH (Gesture Proximity Enter Threshold Register) - ce paramètre définit la valeur de seuil de proximité pour déterminer le début de la reconnaissance des gestes.

GEXTH (Gesture Exit Threshold Register), respectivement, définit une valeur de seuil pour déterminer la fin de la reconnaissance des gestes.

 i2c1_write(APDS9960_GCONF2, DEFAULT_GGAIN); 

Dans le registre GCONF2 (Gesture configuration two), nous définissons explicitement uniquement le paramètre GGAIN (Gesture Gain Control) à quatre fois la valeur de gain.

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

Rétroéclairage Par défaut, la valeur de la source de courant de la LED de rétroéclairage IR est réglée sur 0, ce qui correspond à un courant de 100 mA, cela nous conviendra parfaitement - nous ne la changerons pas.

L'éclairage IR de l'APDS-9960 est une séquence d'impulsions et est caractérisé par les paramètres de registre correspondants pour les gestes GPULSE (nombre et longueur d'impulsions de geste): GPLEN (longueur d'impulsion de geste) et GPULSE (nombre d'impulsions de geste), ainsi que l'approximation PPULSE (registre de comptage d'impulsions de proximité) ): PPLEN (Proximity Pulse Length) et PPULSE (Proximity Pulse Count) définissant le nombre d'impulsions et la période de chaque impulsion individuelle.

Définissez que GPLEN et PPLEN prendront une valeur de 2 égale à 16 μs, et GPULSE et PPULSE une valeur de 9, ce qui correspond à 10 impulsions.

Comme vous pouvez le voir, la configuration s'est avérée légèrement plus compliquée que la similaire pour la reconnaissance des couleurs et l'éclairage de la précédente revue APDS-9960.

Lecture des données

Nous passons maintenant au cycle de programme principal, dans lequel nous commençons de temps à autre à enregistrer et interpréter les données des photodiodes, et à apprendre également à distinguer un geste d'un autre.

Tout d'abord, commençons l'APDS-9960 avec des fonctions pour travailler avec les gestes et le zoom.

 GesturesSet(GESTURES_START); 

Et immédiatement, nous commençons à suivre le paramètre GVALID. GVALID (Gesture FIFO Data) est un paramètre du registre GSTATUS (Gesture Status Register) qui, étant dans un état non nul, nous informe que le capteur possède des données gestuelles utilisables.

La documentation nous apprend que les informations sur les gestes sont dans le tampon, dans le domaine de la RAM, qui est en général de 32 x 4 octets.

En pratique, la taille réelle de ce tampon peut être trouvée en lisant la valeur du registre GFLVL (Gesture FIFO level), c'est-à-dire selon mes observations expérimentales purement empiriques, GFLVL * 4 est obtenu. Quelque chose comme ça:

image

Eh bien, comme il ressort du nom du tampon, les données qu'il contient sont organisées dans l'ordre First In - First Out. Autrement dit, plus le signal de chacune des photodiodes est arrivé «tôt», plus il est «élevé» dans le GFLVL.

Les données des photodiodes (UDLR) peuvent être lues à partir du registre FIFO de geste correspondant:

- GFIFO_U (données FIFO de geste, UP)
- GFIFO_D (Gesture FIFO Data, DOWN)
- GFIFO_L (Gesture FIFO Data, LEFT)
- GFIFO_R (Gesture FIFO Data, RIGHT)

Après chaque lecture des valeurs de ces registres, GFLVL est décrémenté; ainsi, dans le bon sens, il est nécessaire de lire la totalité du tampon jusqu'à ce que le GFLVL atteigne zéro.

Pour définir les gestes, nous n'avons besoin que des quatre premiers octets de ce tampon, pas plus. Par conséquent, nous ne les lirons que.

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

Reconnaissance des gestes

Pour interpréter quel genre de geste s'est produit, nous allons faire des calculs simples:

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

Pour déterminer lequel des gestes s'est produit en ce moment, ce ne sont pas les valeurs de GestUpDown et GestLeftRight qui sont importantes pour nous, mais seulement le signe, pour ainsi dire, d'un nombre réel.

En d'autres termes, en prenant les valeurs négatives et positives des variables GestUpDown et GestLeftRight en entrée, nous déterminons quel geste est parfait.

La table de vérité pour les variables GestUpDown et GestLeftRight est illustrée dans la figure ci-dessous.

image

Maintenant, réinitialisez le GFLVL:

 GesturesSet(GESTURES_STOP); 

... et retour au début du cycle de programme principal.

Et maintenant tout le 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); } } } } 


Je tiens à noter que le mécanisme gestuel de l'APDS-9960 fonctionne très bien. La reconnaissance est stable, les filtres intégrés aux APDS-9960 UV et IR fonctionnent bien.

J'espère que ce matériel est utile à quelqu'un. Merci de votre attention.

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


All Articles