Недавно мелькнуло на Хабре.
com статья о чем, помимо прочего, сообщил и датчик освещенности.
Некоторое время назад я нашел и приобрел интересную вещь — модуль производства RobotDyn на базе датчика APDS-9960, который также умеет измерять уровень освещенности.
Порывшись и не найдя упоминаний об этом устройстве на этом ресурсе, я решил, что это подходящий повод написать статью.
В этой статье я хотел бы в общих чертах познакомить читателей с возможностями, которые предоставляет этот датчик, и более подробно рассмотреть, как с его помощью можно определять цвет и измерять уровень освещенности.
APDS-9960 — это датчик от Avago, комбинированный цифровой датчик с рядом различных интересных и полезных функций.
Он может распознавать жесты, определять близость, а также определять интенсивность окружающего света и определять цвет. Именно об этом мы и поговорим в этой статье — с помощью старого STM32VLDISCOVERY и APDS-9960 мы будем измерять освещенность и определять цвет во всем его богатстве оттенков красного, зеленого и синего.
Однако прежде чем мы перейдем к практической части, позвольте мне сначала написать несколько слов об общих возможностях APDS-9960. Функциональная схема APDS-9960 представлена на рисунке ниже.
Распознавание жестов Представление о том, как выглядит распознавание жестов на APDS-9960, показано здесь очень хорошо.
видео .
В документации описан принцип регистрации жестов: Для распознавания жеста используются четыре направленных фотодиода, которые регистрируют отраженный свет (в ИК-диапазоне), излучаемый встроенным светодиодом.
Функция обнаружения приближения Судя по описанию из той же документации, механизм обнаружения (близости) работает точно по тому же принципу, что и распознавание жестов.
Распознавание цвета и уровень окружающего освещения (Color/ALS) Согласно функциональной схеме датчик определяет уровень цвета/освещенности с помощью соответствующих фотодиодов.
Также заявлено, что APDS-9960 имеет встроенные фильтры, блокирующие ультрафиолетовый и инфракрасный диапазоны.
Упрощенно это выглядит так: сигналы, записанные фотодиодами, измеряются с помощью АЦП, заносятся в буфер, а затем данные отправляются по i2c.
Графики на картинке выше взяты из документации к датчику; спектральная характеристика Color Sense (RGBC) показана вверху слева.
Сигнал фотодиода RGBC накапливается в течение периода времени, установленного значением регистра ATIME. В SparkFun (в их «apds9960.h») это значение определяется константой DEFAULT_ATIME и равно 219, что соответствует 103 мс.
Усиление регулируется от 1x до 64x и определяется настройкой параметра CONTROL AGAIN. Константа DEFAULT_AGAIN равна 1, что соответствует выигрышу в 4 раза.
Практическая часть Лично меня интересовала только функция Color/ALS в APDS-9960, поэтому я решил рассмотреть ее подробнее и написал небольшой код, демонстрирующий ее работу.
Я сознательно старался сделать код максимально компактным, лаконичным и предельно простым для понимания; весь код будет представлен в конце статьи.
Итак, вся документация (чертеж, распиновка и принципиальная схема) на модуль доступна на сайте.
Веб-сайт производитель.
Подключим наш модуль APDS-9960 к STM32VLDISCOVERY. APDS9960 использует интерфейс i2c для связи с внешним миром, поэтому для STM32VLDISCOVERY мы используем шину I2C1, подключив вывод модуля SCL к выводу PB6, а вывод SDA соответственно к выводу PB7. Не забудьте подключить питание и общий провод. Прерывания в этом случае использоваться не будут, поэтому вывод Int подключать не нужно.
На моем фото он подключен, но не используется.
А теперь немного кода.
Поскольку вся связь с модулем происходит с помощью i2c, мы создадим необходимую конфигурацию и определим функции чтения/записи для i2c. Инициализация I2C. Инициализация
Читать реестр.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)) {};
}
Для корректной работы модуля его необходимо сначала правильно настроить.
В частности, чтобы распознать цвет и освещение, вам необходимо сделать следующее: 1) Определите регистр ATIME. По умолчанию при запуске модуля регистр ATIME имеет значение 0xFF и если ничего не изменить, это повлияет на чувствительность датчика — чувствительность будет низкой.
i2c1_write(APDS9960_ATIME, DEFAULT_ATIME);
2) следующим шагом является установка поля параметра AGAIN (ALS и Color Gain Control) регистра Control Register One (0x8F) в значение, соответствующее коэффициенту усиления, равному х4 (DEFAULT_AGAIN равен AGAIN_4X).
i2c1_write(APDS9960_CONTROL, DEFAULT_AGAIN);
3) включить опцию ALS, установив бит AEN регистра Enable Register (0x80)
4) включить питание модуля, установив бит PON того же регистра так: i2c1_write(APDS9960_ENABLE, (APDS9960_PON | APDS9960_AEN));
Вот и все настройки.
Наш датчик готов к работе и обороне, можно приступать к измерению всех цветов.
Но сначала давайте измерим уровень освещенности Colour_tmpL = i2c1_read(APDS9960_CDATAL);
Colour_tmpH = i2c1_read(APDS9960_CDATAH);
Colour_Clear = (Colour_tmpH << 8) + Colour_tmpL;
И вот наше дело дошло до долгожданных цветов.
Давайте получим данные 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;
А теперь весь код: 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;
}
}
Определения констант я намеренно не вынес в отдельный шапку для удобства.
Константы, кстати, я позаимствовал из официального репозитория SparkFun Electronics. Здесь отсюда .
Мне очень понравился APDS-9960 - интересная штука, было интересно исследовать, интересно было написать статью.
Надеюсь, этот материал будет кому-то полезен.
Спасибо за внимание.
Теги: #Программирование микроконтроллеров #stm32 #Сделай сам или Сделай сам #Электроника для начинающих #поделки #APDS-9960
-
Тестирование Видеоигр 101
19 Oct, 24 -
Приложения Для Смартфонов И Их Преимущества
19 Oct, 24 -
Jabber Id В Собственном Домене — Это Просто
19 Oct, 24