
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.

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.

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.

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.

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.

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çãovoid 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
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 #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 #define AGAIN_1X 0 #define AGAIN_4X 1 #define AGAIN_16X 2 #define AGAIN_64X 3 #define DEFAULT_ATIME 219
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.