Reconnaissance des couleurs et de la lumière avec APDS-9960

image


Récemment, un article est apparu sur Habr.com qui, entre autres, a rendu compte d'un capteur de lumière. Il y a quelque temps, j'ai trouvé et acquis une chose intéressante - un module fabriqué par RobotDyn basé sur le capteur APDS-9960, qui sait également mesurer le niveau d'éclairage. Après avoir recherché et n'ayant pas pu trouver de références à cet appareil sur cette ressource, j'ai décidé que c'était une bonne raison d'écrire un article.


Dans l'article, je voudrais présenter brièvement aux lecteurs les possibilités offertes par ce capteur et examiner plus en détail comment il peut être utilisé pour déterminer la couleur et mesurer le niveau d'éclairage.


APDS-9960 est un capteur d'Avago, c'est un capteur numérique combiné avec un certain nombre de fonctions différentes intéressantes et utiles.
Il sait reconnaître les gestes, déterminer la proximité, et il sait aussi enregistrer l'intensité de la lumière ambiante et déterminer la couleur.
C'est exactement ce dont cet article sera discuté - avec l'aide des anciens STM32VLDISCOVERY et APDS-9960, nous mesurerons l'illumination et déterminerons la couleur dans toute sa richesse de nuances de rouge, vert et bleu.


Cependant, avant d'entrer dans la partie pratique, permettez-moi d'abord d'écrire quelques mots sur les caractéristiques générales de l'APDS-9960.


Le schéma fonctionnel de l'APDS-9960 est illustré dans la figure ci-dessous.
image


Reconnaissance des gestes


L'idée de ce à quoi ressemble la reconnaissance des gestes sur l'APDS-9960 est très bien montrée dans cette vidéo .


La documentation décrit le principe de l'enregistrement des gestes:
Pour reconnaître le geste, quatre photodiodes directionnelles sont utilisées qui enregistrent la lumière réfléchie (dans la plage infrarouge) émise par la LED intégrée.


image


Fonction de détection de proximité


A en juger par la description de la même documentation, le mécanisme de détection (approximation) fonctionne sur le même principe exact que la reconnaissance gestuelle.


Reconnaissance des couleurs et niveau de lumière ambiante (Couleur / ALS)


Selon le schéma fonctionnel, le capteur détermine le niveau de couleur / lumière à l'aide des photodiodes appropriées. Il est également indiqué que l'APDS-9960 possède des filtres intégrés bloquant les gammes ultraviolettes et infrarouges.
Simplifié, cela ressemble à ceci: les signaux enregistrés par les photodiodes sont mesurés à l'aide de l'ADC, entrés dans le tampon, puis les données sont envoyées via i2c.


image


Les graphiques de l'image ci-dessus sont tirés de la documentation du capteur, la réponse spectrale de Color Sense (RGBC) est présentée en haut à gauche.


Le signal RGBC des photodiodes est accumulé sur la période de temps fixée par la valeur du registre ATIME. Pour SparkFun (dans leur "apds9960.h"), cette valeur est déterminée par la constante DEFAULT_ATIME et est égale à 219 ce qui correspond à 103 ms.


Le gain est réglable de 1x à 64x et est déterminé en réglant le paramètre CONTROL AGAIN. La constante DEFAULT_AGAIN, qui, à son tour, est égale à 1, ce qui correspond à un gain de 4 fois.


Partie pratique


Personnellement, je n'étais intéressé que par la fonction Color / ALS dans APDS-9960, j'ai donc décidé de l'examiner plus en détail et j'ai écrit un petit code démontrant son fonctionnement.
J'ai intentionnellement essayé de rendre le code aussi compact, concis et extrêmement simple à comprendre que possible; tout le code sera présenté à la fin de l'article.


Ainsi, toute la documentation (schéma, brochage et schéma électrique) du module est disponible sur le site Internet du constructeur.


image


Connectez notre module APDS-9960 à STM32VLDISCOVERY


APDS9960 utilise l'interface i2c pour communiquer avec le monde extérieur, donc pour STM32VLDISCOVERY, nous utilisons le bus I2C1 en connectant la broche du module SCL à la broche PB6 et la broche SDA à la broche PB7, respectivement. N'oubliez pas de connecter l'alimentation et le fil commun. Les interruptions dans ce cas ne seront pas utilisées, donc la sortie de Int peut être omise. Sur ma photo, il est connecté, mais pas utilisé.


image


Et maintenant un petit code. Étant donné que toutes les communications avec le module se font à l'aide d'i2c, nous allons créer la configuration nécessaire et définir les fonctions de lecture / écriture pour i2c.


Initialisation de I2C.


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

Lecture du registre.


Fonction de lecture d'une valeur dans un registre
 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; } 

Écrire une valeur dans un registre


Fonction de valeur de registre
 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)) {}; } 

Pour qu'un module fonctionne correctement, il doit d'abord être correctement configuré. Spécifiquement pour la reconnaissance des couleurs et l'éclairage, vous devez procéder comme suit:


1) Définissez le registre ATIME. Par défaut, lorsque le module démarre, le registre ATIME a la valeur 0xFF et si vous ne changez rien, cela affectera la sensibilité du capteur - la sensibilité sera faible.


 i2c1_write(APDS9960_ATIME, DEFAULT_ATIME); 

2) à l'étape suivante, nous définissons à nouveau le champ de paramètre (ALS et contrôle de gain de couleur) du registre de registre de contrôle un (0x8F) à la valeur correspondant au gain égal à x4 (DEFAULT_AGAIN est égal à AGAIN_4X).


 i2c1_write(APDS9960_CONTROL, DEFAULT_AGAIN); 

3) activer l'option ALS en définissant le bit AEN du registre d'activation du registre (0x80)
4) mettez le module sous tension en réglant le bit PON du même registre


comme ceci:


 i2c1_write(APDS9960_ENABLE, (APDS9960_PON | APDS9960_AEN)); 

Voilà toute la configuration. Notre capteur est prêt pour le travail et la défense, vous pouvez commencer à mesurer toutes les couleurs.


Mais d'abord, mesurez le niveau d'éclairage


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

Et maintenant, notre entreprise est arrivée aux fleurs tant attendues


Obtenez des données RVB
 //_________________________________________________________________________ // 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; 

Et maintenant tout le code:


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

Je n'ai délibérément pas fait la définition des constantes dans un en-tête séparé pour plus de commodité.
Soit dit en passant, les constantes ont été empruntées au référentiel officiel de SparkFun Electronics. D'ici .


J'ai vraiment aimé APDS-9960 - une chose intéressante, c'était intéressant de rechercher, c'était intéressant d'écrire un article. J'espère que quelqu'un trouvera ce matériel utile. Merci de votre attention.

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


All Articles