Reconhecimento de gestos com APDS-9960

imagem

Lendo os comentários no meu artigo anterior sobre o APDS-9960, onde se tratava de reconhecimento de cores e nível de luz, duas coisas se tornaram óbvias para mim: 1) o tópico do reconhecimento de gestos é interessante e 2) esse tópico não é divulgado.

De fato, se eu peguei a descrição do APDS-9960, sem considerar os gestos, a descrição parece um pouco incompleta. Então encontrei algum tempo livre para explorar esse tópico também.

Neste artigo, chamo a atenção para você uma visão geral dos recursos de reconhecimento de gestos que o sensor APDS-9960 fornece.

O artigo considerará o mecanismo de configuração do sensor, coleta de dados, processamento e apresentação. Você pode ver como é fácil trabalhar com gestos usando o APDS-9960.

Como última vez, o artigo será acompanhado por um código, tudo o que acontecer, será descrito em detalhes. A versão completa do código está disponível no final do artigo.

Imediatamente uma pequena observação: o APDS-9960 não possui um mecanismo automático de detecção de gestos; isto é, tal que aqui mesmo, eu li, significa, o registro, e já existe o gesto processado - isso não está no APDS-9960; e isso significa que você deve escrever seu próprio algoritmo de interpretação de gestos, o que faremos mais adiante.

Em geral, isso é bom e não muito bom. Na verdade, não - porque pode complicar o estudo desse sensor para iniciantes, mas bom, porque, juntamente com os dados de aproximação, você pode, por refinamento, até criar seus próprios gestos de vários tipos e qualquer outro.

Porém, como este artigo tem apenas uma função de visão geral, nos restringimos apenas a gestos básicos UP-DOWN-LEFT-RIGHT.

Bem, vamos começar.

Teoria


Vou me permitir um pouco de material.

Para obter as informações necessárias sobre o movimento e a direção do movimento, o APDS-9960 usa um LED IR e quatro fotodiodos que, conforme ilustrado na figura abaixo, detectam sinais na faixa de infravermelho próximo (NIR).

imagem

O LED IR (LED) tem a função de luz de fundo e os fotodiodos (UDLR) registram a luz refletida no "obstáculo".

Os fotodiodos estão localizados no sensor de forma que, dependendo da direção do movimento do “obstáculo”, o fotodiodo correspondente receberá a maior parte do sinal infravermelho refletido na entrada e uma parte menor na saída. Ao mesmo tempo, a documentação do APDS-9960 indica inequivocamente que você pode interpretar a direção do movimento medindo e comparando a amplitude e a diferença de fase dos sinais dos fotodiodos UDLR.

imagem

Prática


Para trabalhar com o APDS-9960, bem como da última vez , usaremos STM32VLDISCOVERY. A conexão também não mudou.

Configurar APDS-9960

Fazemos a configuração inicial do sensor.

Assim:

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

O que está acontecendo aqui? Vamos acertar.

 i2c1_write(APDS9960_CONTROL, DEFAULT_PGAIN); 

PGAIN (Proximity Gain Control) é um parâmetro que controla o ganho de sensibilidade de proximidade. Atribua a ele um valor 2, que corresponde a quatro vezes o ganho.

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

GPENTH (Registro de limite de entrada por proximidade de gestos) - esse parâmetro define o valor do limite de proximidade para determinar o início do reconhecimento de gestos.

GEXTH (Registro de limite de saída de gestos), respectivamente, define um valor limite para determinar o final do reconhecimento de gestos.

 i2c1_write(APDS9960_GCONF2, DEFAULT_GGAIN); 

No registro GCONF2 (Gesture configuration two), definimos explicitamente apenas o parâmetro GGAIN (Gesture Gain Control) para quatro vezes o valor do ganho.

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

Luz de fundo Por padrão, o valor da fonte atual da luz de fundo do LED infravermelho é definido como 0, o que corresponde a uma corrente de 100 mA, isso nos convém perfeitamente - não vamos alterá-lo.

A iluminação IR no APDS-9960 é uma sequência de pulsos e é caracterizada pelos parâmetros de registro correspondentes para os gestos GPULSE (contagem e comprimento de pulsos de gestos): GPLEN (comprimento dos pulsos de gestos) e GPULSE (número de pulsos de gestos), bem como a aproximação PPULSE (registro de contagem de pulsos de proximidade) ): PPLEN (Proximity Pulse Length) e PPULSE (Proximity Pulse Count) definem o número de pulsos e o período de cada pulso individual.

Defina que GPLEN e PPLEN terão um valor de 2 igual a 16 μs e GPULSE e PPULSE um valor de 9, que corresponde a 10 pulsos.

Como você pode ver, a configuração acabou sendo um pouco mais complicada do que a semelhante para reconhecimento de cores e iluminação da revisão anterior do APDS-9960.

Lendo dados

Agora, passaremos para o ciclo principal do programa, no qual começamos a registrar e interpretar dados de fotodiodos de vez em quando, e também aprenderemos a distinguir um gesto de outro.

Para começar, vamos começar o APDS-9960 com funções para trabalhar com gestos e zoom.

 GesturesSet(GESTURES_START); 

E imediatamente começamos a rastrear o parâmetro GVALID. GVALID (Gesture FIFO Data) é um parâmetro do registro GSTATUS (Gesture Status Register), que, estando em um estado diferente de zero, informa que o sensor possui dados de gestos utilizáveis.

A documentação nos ensina que as informações dos gestos estão no buffer, na área da RAM, que geralmente tem 32 x 4 bytes de tamanho.

Na prática, o tamanho real desse buffer pode ser encontrado lendo o valor do registro GFLVL (Gesture FIFO level), ou seja, de acordo com minhas observações experimentais puramente empíricas, é obtido o GFLVL * 4. Algo assim:

imagem

Bem, como segue o nome do buffer, os dados nele são organizados na ordem Primeiro a entrar - Primeiro a sair. Ou seja, grosso modo, quanto "mais cedo" o sinal de cada um dos fotodiodos chegava, mais "alto" ele fica na GFLVL.

Os dados dos fotodiodos (UDLR) podem ser lidos no registro FIFO gestual correspondente:

- GFIFO_U (dados FIFO de gestos, UP)
- GFIFO_D (dados FIFO de gestos, DOWN)
- GFIFO_L (dados FIFO de gestos, ESQUERDA)
- GFIFO_R (dados FIFO do gesto, DIREITO)

Após cada leitura dos valores desses registros, o GFLVL é diminuído; assim, de uma maneira boa, é necessário ler todo o buffer até que o GFLVL atinja zero.

Para definir gestos, precisamos apenas dos quatro primeiros bytes desse buffer, e não mais. Portanto, apenas os leremos.

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

Reconhecimento de gestos

Para interpretar que tipo de gesto ocorreu, faremos cálculos simples:

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

Para determinar qual dos gestos ocorreu no momento, não são os valores de GestUpDown e GestLeftRight que são importantes para nós, mas apenas o sinal de, por assim dizer, um número real.

Ou seja, tomando os valores negativos e positivos das variáveis ​​GestUpDown e GestLeftRight como entrada, determinamos qual gesto é perfeito.

A tabela verdade para as variáveis ​​GestUpDown e GestLeftRight é mostrada na figura abaixo.

imagem

Agora redefina o GFLVL:

 GesturesSet(GESTURES_STOP); 

... e de volta ao início do ciclo principal do programa.

E agora todo o 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); } } } } 


Quero observar que o mecanismo de gesto do APDS-9960 funciona muito bem. O reconhecimento é estável, os filtros incorporados no APDS-9960 UV e IR funcionam bem.

Espero que este material seja útil para alguém. Obrigado pela atenção.

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


All Articles