Reconhecimento de cores e luzes com APDS-9960

imagem


Recentemente, um artigo apareceu no Habr.com que, entre outros, relatava um sensor de luz. Há algum tempo, descobri e adquiri uma coisa interessante - um módulo fabricado pela RobotDyn baseado no sensor APDS-9960, que também sabe como medir o nível de iluminação. Depois de pesquisar e não conseguir encontrar referências a este dispositivo neste recurso, decidi que esse era um bom motivo para escrever um artigo.


No artigo, gostaria de apresentar brevemente aos leitores as possibilidades que esse sensor oferece e examinar com mais detalhes como ele pode ser usado para determinar a cor e medir o nível de iluminação.


O APDS-9960 é um sensor da Avago, é um sensor digital combinado com várias funções interessantes e úteis.
Ele sabe reconhecer gestos, determinar a proximidade e também sabe registrar a intensidade da luz ambiente e determinar a cor.
É exatamente nisso que este artigo será discutido - com a ajuda do antigo STM32VLDISCOVERY e APDS-9960, mediremos a iluminação e determinaremos a cor em toda a sua riqueza de tons de vermelho, verde e azul.


No entanto, antes de entrarmos na parte prática, deixe-me primeiro escrever algumas palavras sobre os recursos gerais do APDS-9960.


O diagrama funcional do APDS-9960 é mostrado na figura abaixo.
imagem


Reconhecimento de gestos


A idéia de como é o reconhecimento de gestos no APDS-9960 é muito bem mostrada neste vídeo .


A documentação descreve o princípio do registro de gestos:
Para reconhecer o gesto, são utilizados quatro fotodiodos direcionais que registram a luz refletida (na faixa de infravermelho) emitida pelo LED embutido.


imagem


Função de detecção de proximidade


A julgar pela descrição da mesma documentação, o mecanismo de detecção (aproximação) funciona com o mesmo princípio exato do reconhecimento de gestos.


Reconhecimento de cores e nível de luz ambiente (Color / ALS)


De acordo com o diagrama funcional, o sensor determina o nível de cor / luz usando os fotodiodos apropriados. Também é afirmado que o APDS-9960 possui filtros embutidos que bloqueiam as faixas ultravioleta e infravermelha.
Simplificado, fica assim: os sinais gravados pelos fotodiodos são medidos usando o ADC, inseridos no buffer e, em seguida, os dados são enviados via i2c.


imagem


Os gráficos da figura acima são retirados da documentação do sensor. A resposta espectral do Color Sense (RGBC) é apresentada no canto superior esquerdo.


O sinal RGBC dos fotodiodos é acumulado durante o período definido pelo valor do registro ATIME. Para o SparkFun (no "apds9960.h"), esse valor é determinado pela constante DEFAULT_ATIME e é igual a 219, o que corresponde a 103 ms.


O ganho é ajustável de 1x a 64x e é determinado pela configuração do parâmetro CONTROL AGAIN. A constante DEFAULT_AGAIN, que, por sua vez, é igual a 1, o que corresponde a um ganho de 4 vezes.


Parte prática


Pessoalmente, eu estava interessado apenas na função Color / ALS no APDS-9960, então decidi considerá-la com mais detalhes e escrevi um pequeno código demonstrando sua operação.
Tentei intencionalmente tornar o código o mais compacto, conciso e extremamente simples de entender possível; todo o código será apresentado no final do artigo.


Portanto, toda a documentação (desenho, pinagem e diagrama de circuitos) do módulo está disponível no site do fabricante.


imagem


Conecte nosso módulo APDS-9960 ao STM32VLDISCOVERY


O APDS9960 usa a interface i2c para se comunicar com o mundo exterior; portanto, para STM32VLDISCOVERY, usamos o barramento I2C1 conectando o pino do módulo SCL ao pino PB6 e o ​​pino SDA ao pino PB7, respectivamente. Não se esqueça de conectar a energia e o fio comum. As interrupções nesse caso não serão usadas, portanto a saída de Int pode ser omitida. Na minha foto, ele está conectado, mas não é usado.


imagem


E agora um pequeno código. Como toda a comunicação com o módulo ocorre usando o i2c, criaremos a configuração necessária e definiremos as funções de leitura / gravação do i2c.


Inicialização do I2C.


Inicialização
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); } 

Lendo o registro.


Função para ler um valor de um registro
 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; } 

Escrevendo um valor em um registro


Registrar função de valor
 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)) {}; } 

Para que um módulo funcione corretamente, ele deve primeiro ser configurado corretamente. Especificamente para reconhecimento de cores e iluminação, você deve fazer o seguinte:


1) Defina o registro ATIME. Por padrão, quando o módulo é iniciado, o registro ATIME tem o valor 0xFF e, se você não alterar nada, isso afetará a sensibilidade do sensor - a sensibilidade será baixa.


 i2c1_write(APDS9960_ATIME, DEFAULT_ATIME); 

2) na próxima etapa, definimos o campo de parâmetro AGAIN (ALS e controle de ganho de cores) do registro de controle Um registro (0x8F) para o valor correspondente ao ganho igual a x4 (DEFAULT_AGAIN é igual a AGAIN_4X).


 i2c1_write(APDS9960_CONTROL, DEFAULT_AGAIN); 

3) ative a opção ALS configurando o bit AEN do registro Ativar Registro (0x80)
4) ligue a energia do módulo configurando o bit PON do mesmo registro


assim:


 i2c1_write(APDS9960_ENABLE, (APDS9960_PON | APDS9960_AEN)); 

Essa é toda a configuração. Nosso sensor está pronto para o trabalho e a defesa, você pode começar a medir todas as cores.


Mas primeiro, meça o nível de iluminação


 Colour_tmpL = i2c1_read(APDS9960_CDATAL); Colour_tmpH = i2c1_read(APDS9960_CDATAH); Colour_Clear = (Colour_tmpH << 8) + Colour_tmpL; 

E agora nosso negócio chegou às tão esperadas flores


Obter dados RGB
 //_________________________________________________________________________ // RED color Recognize: Colour_tmpL = i2c1_read(APDS9960_RDATAL); Colour_tmpH = i2c1_read(APDS9960_RDATAH); Colour_Red = (Colour_tmpH << 8) + Colour_tmpL; //_________________________________________________________________________ // GREEN color Recognize: Colour_tmpL = i2c1_read(APDS9960_GDATAL); Colour_tmpH = i2c1_read(APDS9960_GDATAH); Colour_Green = (Colour_tmpH << 8) + Colour_tmpL; //_________________________________________________________________________ // BLUE color Recognize: Colour_tmpL = i2c1_read(APDS9960_BDATAL); Colour_tmpH = i2c1_read(APDS9960_BDATAH); Colour_Blue = (Colour_tmpH << 8) + Colour_tmpL; 

E agora todo o código:


main.c
 #include "stm32f10x.h" #define APDS9960_I2C_ADDR 0x39 #define APDS9960_ATIME 0x81 #define APDS9960_CONTROL 0x8F #define APDS9960_ENABLE 0x80 #define APDS9960_CDATAL 0x94 #define APDS9960_CDATAH 0x95 #define APDS9960_RDATAL 0x96 #define APDS9960_RDATAH 0x97 #define APDS9960_GDATAL 0x98 #define APDS9960_GDATAH 0x99 #define APDS9960_BDATAL 0x9A #define APDS9960_BDATAH 0x9B /* 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 /* ALS Gain (AGAIN) values */ #define AGAIN_1X 0 #define AGAIN_4X 1 #define AGAIN_16X 2 #define AGAIN_64X 3 #define DEFAULT_ATIME 219 // 103ms #define DEFAULT_AGAIN AGAIN_4X uint8_t Colour_tmpL = 0; uint8_t Colour_tmpH = 0; uint16_t Colour_Clear = 0; uint16_t Colour_Red = 0; uint16_t Colour_Green = 0; uint16_t Colour_Blue = 0; //----------------------------------------------------------------------- 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); } //----------------------------------------------------------------------- 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)) {}; } //----------------------------------------------------------------------- int main() { I2C1_init(); i2c1_write(APDS9960_ATIME, DEFAULT_ATIME); i2c1_write(APDS9960_CONTROL, DEFAULT_AGAIN); i2c1_write(APDS9960_ENABLE, (APDS9960_PON | APDS9960_AEN)); while (1) { Colour_Clear = 0; Colour_Red = 0; Colour_Green = 0; Colour_Blue = 0; //_________________________________________________________________________ // Ambient Light Recognize: Colour_tmpL = i2c1_read(APDS9960_CDATAL); Colour_tmpH = i2c1_read(APDS9960_CDATAH); Colour_Clear = (Colour_tmpH << 8) + Colour_tmpL; //_________________________________________________________________________ // RED color Recognize: Colour_tmpL = i2c1_read(APDS9960_RDATAL); Colour_tmpH = i2c1_read(APDS9960_RDATAH); Colour_Red = (Colour_tmpH << 8) + Colour_tmpL; //_________________________________________________________________________ // GREEN color Recognize: Colour_tmpL = i2c1_read(APDS9960_GDATAL); Colour_tmpH = i2c1_read(APDS9960_GDATAH); Colour_Green = (Colour_tmpH << 8) + Colour_tmpL; //_________________________________________________________________________ // BLUE color Recognize: Colour_tmpL = i2c1_read(APDS9960_BDATAL); Colour_tmpH = i2c1_read(APDS9960_BDATAH); Colour_Blue = (Colour_tmpH << 8) + Colour_tmpL; } } 

Eu deliberadamente não fiz a definição de constantes em um cabeçalho separado por conveniência.
Constantes, a propósito, emprestadas do repositório oficial SparkFun Electronics. Daqui


Gostei muito do APDS-9960 - uma coisa interessante, foi interessante pesquisar, foi interessante escrever um artigo. Espero que alguém ache este material útil. Obrigado pela atenção.

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


All Articles