Reconocimiento de color y luz con APDS-9960

imagen


Recientemente, apareci贸 un art铆culo en Habr.com que, entre otras cosas, inform贸 sobre un sensor de luz. Hace alg煤n tiempo encontr茅 y adquir铆 algo interesante: un m贸dulo fabricado por RobotDyn basado en el sensor APDS-9960, que tambi茅n sabe c贸mo medir el nivel de iluminaci贸n. Despu茅s de buscar y no poder encontrar referencias a este dispositivo en este recurso, decid铆 que esta era una buena raz贸n para escribir un art铆culo.


En el art铆culo, me gustar铆a presentar brevemente a los lectores las posibilidades que ofrece este sensor y examinar con m谩s detalle c贸mo se puede usar para determinar el color y medir el nivel de iluminaci贸n.


APDS-9960 es un sensor de Avago, es un sensor digital combinado con varias funciones diferentes interesantes y 煤tiles.
脡l sabe c贸mo reconocer los gestos, determinar la proximidad, y tambi茅n sabe c贸mo registrar la intensidad de la luz ambiental y determinar el color.
Esto es exactamente lo que se discutir谩 en este art铆culo: con la ayuda de los antiguos STM32VLDISCOVERY y APDS-9960, mediremos la iluminaci贸n y determinaremos el color en toda su riqueza de tonos de rojo, verde y azul.


Sin embargo, antes de entrar en la parte pr谩ctica, perm铆tanme escribir algunas palabras sobre las caracter铆sticas generales del APDS-9960.


El diagrama funcional de APDS-9960 se muestra en la figura a continuaci贸n.
imagen


Reconocimiento de gestos


La idea de c贸mo se ve el reconocimiento de gestos en el APDS-9960 se muestra muy bien en este video .


La documentaci贸n describe el principio de registro de gestos:
Para reconocer el gesto, se utilizan cuatro fotodiodos direccionales que registran la luz reflejada (en el rango infrarrojo) emitida por el LED incorporado.


imagen


Funci贸n de detecci贸n de proximidad


A juzgar por la descripci贸n de la misma documentaci贸n, el mecanismo de detecci贸n (aproximaci贸n) funciona seg煤n el mismo principio exacto que el reconocimiento de gestos.


Reconocimiento de color y nivel de luz ambiental (Color / ALS)


Seg煤n el diagrama funcional, el sensor determina el nivel de color / luz utilizando los fotodiodos apropiados. Tambi茅n se afirma que el APDS-9960 tiene filtros incorporados que bloquean los rangos ultravioleta e infrarrojo.
Simplificado, se ve as铆: las se帽ales grabadas por los fotodiodos se miden con el ADC, se ingresan en el b煤fer y luego los datos se env铆an a trav茅s de i2c.


imagen


Los gr谩ficos en la imagen de arriba est谩n tomados de la documentaci贸n del sensor, la respuesta espectral de Color Sense (RGBC) se presenta en la parte superior izquierda.


La se帽al RGBC de los fotodiodos se acumula durante el per铆odo de tiempo establecido por el valor del registro ATIME. Para SparkFun (en su "apds9960.h") este valor est谩 determinado por la constante DEFAULT_ATIME y es igual a 219 que corresponde a 103 ms.


La ganancia es ajustable de 1x a 64x y se determina configurando el par谩metro CONTROL OTRA VEZ. La constante DEFAULT_AGAIN, que, a su vez, es igual a 1, que corresponde a una ganancia de 4 veces.


Parte pr谩ctica


Personalmente, solo estaba interesado en la funci贸n Color / ALS en APDS-9960, as铆 que decid铆 considerarlo con m谩s detalle y escrib铆 un peque帽o c贸digo que demostraba su funcionamiento.
Intent茅 intencionalmente hacer que el c贸digo fuera lo m谩s compacto, conciso y extremadamente simple de entender posible; Todo el c贸digo se presentar谩 al final del art铆culo.


Por lo tanto, toda la documentaci贸n (dibujo, pinout y diagrama de circuito) para el m贸dulo est谩 disponible en el sitio web del fabricante.


imagen


Conecte nuestro m贸dulo APDS-9960 a STM32VLDISCOVERY


APDS9960 usa la interfaz i2c para comunicarse con el mundo exterior, por lo que para STM32VLDISCOVERY usamos el bus I2C1 conectando el pin del m贸dulo SCL al pin PB6 y el pin SDA al pin PB7, respectivamente. No olvides conectar la alimentaci贸n y el cable com煤n. Las interrupciones en este caso no se utilizar谩n, por lo que se puede omitir la salida de Int. En mi foto est谩 conectado, pero no se usa.


imagen


Y ahora un peque帽o c贸digo. Dado que toda la comunicaci贸n con el m贸dulo ocurre usando i2c, crearemos la configuraci贸n necesaria y definiremos las funciones de lectura / escritura para i2c.


Inicializaci贸n de I2C.


Inicializaci贸n
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); } 

Leyendo el registro.


Funci贸n para leer un valor de un 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; } 

Escribir un valor en un registro


Funci贸n de valor de registro
 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 un m贸dulo funcione correctamente, primero debe configurarse correctamente. Espec铆ficamente para el reconocimiento del color y la iluminaci贸n, debe hacer lo siguiente:


1) Definir el registro ATIME. De forma predeterminada, cuando se inicia el m贸dulo, el registro ATIME tiene el valor 0xFF y si no cambia nada, esto afectar谩 la sensibilidad del sensor; la sensibilidad ser谩 baja.


 i2c1_write(APDS9960_ATIME, DEFAULT_ATIME); 

2) en el siguiente paso establecemos el campo de par谩metro OTRA VEZ (ALS y Control de ganancia de color) del registro Control Register One (0x8F) en el valor correspondiente a la ganancia igual a x4 (DEFAULT_AGAIN es igual a AGAIN_4X).


 i2c1_write(APDS9960_CONTROL, DEFAULT_AGAIN); 

3) active la opci贸n ALS configurando el bit AEN del registro de registro de habilitaci贸n (0x80)
4) encienda la alimentaci贸n del m贸dulo configurando el bit PON del mismo registro


as铆:


 i2c1_write(APDS9960_ENABLE, (APDS9960_PON | APDS9960_AEN)); 

Esa es toda la configuraci贸n. Nuestro sensor est谩 listo para el trabajo y la defensa, puede comenzar a medir todos los colores.


Pero primero, mida el nivel de iluminaci贸n


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

Y ahora nuestro negocio ha llegado a las flores tan esperadas


Obtener datos 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; 

Y ahora todo el 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; } } 

Deliberadamente, no hice la definici贸n de constantes en un encabezado separado por conveniencia.
Las constantes, por cierto, fueron tomadas del dep贸sito oficial de SparkFun Electronics. Desde aqu铆


Realmente me gust贸 el APDS-9960, algo interesante, fue interesante investigar, fue interesante escribir un art铆culo. Espero que alguien encuentre 煤til este material. Gracias por su atencion

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


All Articles