Reconocimiento de gestos con APDS-9960

imagen

Al leer los comentarios en mi artículo anterior sobre APDS-9960, donde se trataba del reconocimiento de color y el nivel de luz, dos cosas se hicieron obvias para mí: 1) el tema del reconocimiento de gestos es interesante y 2) este tema no se revela.

De hecho, si tomé la descripción del APDS-9960, sin considerar los gestos, la descripción parece algo incompleta. Así que también encontré algo de tiempo libre para explorar este tema.

En este artículo, traigo a su atención una descripción general de las capacidades de reconocimiento de gestos que proporciona el sensor APDS-9960.

El artículo considerará el mecanismo de configuración del sensor, recopilación de datos, procesamiento y presentación. Usted mismo puede ver lo fácil que es trabajar con gestos utilizando el APDS-9960.

Como la última vez, el artículo irá acompañado de un código, todo lo que ocurra en el que se describirá en detalle. La versión completa del código está disponible al final del artículo.

Inmediatamente un pequeño comentario: APDS-9960 no tiene un mecanismo de detección de gestos automático incorporado; es decir, aquí mismo, leí, significa, el registro, y allí yace el gesto procesado, esto no está en APDS-9960; y esto significa que debe escribir su propio algoritmo de interpretación de gestos, que haremos más adelante.

En general, esto es bueno y no muy bueno. En realidad no, porque puede complicar el estudio de este sensor para un principiante, pero bueno, porque, junto con los datos de aproximación, puede, por refinamiento, incluso crear sus propios gestos de varios tipos y de cualquier tipo.

Pero, dado que este artículo solo tiene una función de descripción general, nos restringimos a solo los gestos básicos ARRIBA-ABAJO-IZQUIERDA-DERECHA.

Bueno, empecemos.

Teoría


Me permitiré un poco de material.

Para obtener la información necesaria sobre el movimiento y la dirección del movimiento, el APDS-9960 utiliza un LED IR y cuatro fotodiodos que, como se ilustra en la figura a continuación, detecta señales en el rango del infrarrojo cercano (NIR).

imagen

El LED IR (LED) tiene la función de luz de fondo, y los fotodiodos (UDLR) registran la luz reflejada por el "obstáculo".

Los fotodiodos están ubicados en el sensor de tal manera que, dependiendo de la dirección del movimiento del "obstáculo", el fotodiodo correspondiente recibirá la mayor parte de la señal IR reflejada en la entrada y una parte más pequeña en la salida. Al mismo tiempo, la documentación del APDS-9960 nos dice inequívocamente que puede interpretar la dirección del movimiento midiendo y comparando la amplitud y la diferencia de fase de las señales de los fotodiodos UDLR.

imagen

Practica


Para trabajar con APDS-9960, así como la última vez , utilizaremos STM32VLDISCOVERY. La conexión tampoco ha cambiado.

Configurar APDS-9960

Realizamos la configuración inicial del sensor.

Así:

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

¿Qué está pasando aquí? Vamos a hacerlo bien.

 i2c1_write(APDS9960_CONTROL, DEFAULT_PGAIN); 

PGAIN (Control de ganancia de proximidad) es un parámetro que controla la ganancia de sensibilidad de proximidad. Asigne un valor de 2, que corresponde a cuatro veces la ganancia.

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

GPENTH (Registro de umbral de entrada de proximidad de gesto): este parámetro establece el valor de umbral de proximidad para determinar el comienzo del reconocimiento de gestos.

GEXTH (Registro de umbral de salida de gestos), respectivamente, establece un valor umbral para determinar el final del reconocimiento de gestos.

 i2c1_write(APDS9960_GCONF2, DEFAULT_GGAIN); 

En el registro GCONF2 (configuración de gestos dos), establecemos explícitamente solo el parámetro GGAIN (control de ganancia de gestos) en cuatro veces el valor de ganancia.

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

Luz de fondo De forma predeterminada, el valor de la fuente actual del LED de retroiluminación IR se establece en 0, que corresponde a una corriente de 100 mA, esto nos conviene perfectamente, no lo cambiaremos.

La iluminación IR en el APDS-9960 es una secuencia de pulsos y se caracteriza por los parámetros de registro correspondientes para gestos GPULSE (conteo y longitud de pulso de gesto): GPLEN (longitud de pulso de gesto) y GPULSE (número de pulsos de gesto), así como la aproximación PPULSE (registro de conteo de pulso de proximidad ): PPLEN (longitud de pulso de proximidad) y PPULSE (conteo de pulso de proximidad) configuran el número de pulsos y el período de cada pulso individual.

Defina que GPLEN y PPLEN tomarán un valor de 2 igual a 16 μs, y GPULSE y PPULSE un valor de 9, que corresponde a 10 pulsos.

Como puede ver, la configuración resultó ser un poco más complicada que la similar para el reconocimiento de color y la iluminación de la revisión anterior APDS-9960.

Lectura de datos

Ahora pasaremos al ciclo principal del programa, en el que comenzaremos a registrar e interpretar datos de fotodiodos de vez en cuando, y también aprenderemos a distinguir un gesto de otro.

Primero lo primero, comencemos el APDS-9960 con funciones para trabajar con gestos y zoom.

 GesturesSet(GESTURES_START); 

E inmediatamente comenzamos a rastrear el parámetro GVALID. GVALID (Gesture FIFO Data) es un parámetro del registro GSTATUS (Gesture Status Register) que, al estar en un estado distinto de cero, nos informa que el sensor tiene datos gestuales utilizables.

La documentación nos enseña que la información de gestos está en el búfer, en el área de RAM, que en general tiene un tamaño de 32 x 4 bytes.

En la práctica, el tamaño real de este búfer se puede encontrar leyendo el valor del registro GFLVL (nivel de gesto FIFO), es decir. De acuerdo con mis observaciones experimentales puramente empíricas, se obtiene GFLVL * 4. Algo como esto:

imagen

Bueno, como se desprende del nombre del búfer, los datos que contiene están ordenados en el orden Primero en entrar - Primero en salir. Es decir, más o menos, cuanto más "temprano" llegó la señal de cada uno de los fotodiodos, más "alto" estará en el GFLVL.

Los datos de los fotodiodos (UDLR) se pueden leer del correspondiente registro de gestos FIFO:

- GFIFO_U (Gesto FIFO Datos, ARRIBA)
- GFIFO_D (Datos FIFO de gestos, ABAJO)
- GFIFO_L (Datos FIFO de gestos, IZQUIERDA)
- GFIFO_R (Gesto FIFO Datos, DERECHA)

Después de cada lectura de valores de estos registros, se disminuye GFLVL; por lo tanto, en el buen sentido, es necesario leer todo el búfer hasta que el GFLVL llegue a cero.

Para definir gestos, solo necesitamos los primeros cuatro bytes de este búfer, no más. Por lo tanto, solo los leeremos.

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

Reconocimiento de gestos

Para interpretar qué tipo de gesto ocurrió, haremos cálculos simples:

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

Para determinar cuáles de los gestos ocurrieron en el momento, no son los valores de GestUpDown y GestLeftRight los que son importantes para nosotros, sino solo el signo de, por así decirlo, un número real.

Es decir, tomando los valores negativos y positivos de las variables GestUpDown y GestLeftRight como entrada, determinamos qué gesto es perfecto.

La tabla de verdad para las variables GestUpDown y GestLeftRight se muestra en la figura a continuación.

imagen

Ahora reinicie el GFLVL:

 GesturesSet(GESTURES_STOP); 

... y de vuelta al comienzo del ciclo del programa principal.

Y ahora todo el código:

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


Quiero señalar que el mecanismo de gestos del APDS-9960 funciona muy bien. El reconocimiento es estable, los filtros integrados en el APDS-9960 UV e IR funcionan bien.

Espero que este material sea útil para alguien. Gracias por su atencion

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


All Articles