Урок 90
Часть 5
Датчик освещённости VL6180X
В предыдущей части нашего занятия мы написали ещё ряд фунций по работе с датчиком освещённости.
Над функцией vl6180_AlsGetMeasurement добавим функцию снятия сырых показаний с датчика и преобразования их непосредственно в значение в люксах
//-------------------------------------------------
int vl6180_AlsGetLux(uint32_t *pLux)
{
int status;
uint16_t RawAls;
uint32_t luxValue = 0;
uint32_t IntPeriod;
uint32_t AlsAnGain;
uint32_t GainFix;
uint32_t AlsScaler;
return status;
}
//-------------------------------------------------
В файле vl6180.h добавим некоторые макросы, нужные для пересчёта в люксы
//-------------------------------------------------
#define DEF_INT_PEFRIOD 100
#define LUXRES_FIX_PREC 8
#define GAIN_FIX_PREC 8 /* ! if not sme as LUX_PREC then :( adjust GetLux */
#define AN_GAIN_MULT (1 << GAIN_FIX_PREC)
//-------------------------------------------------
Вернёмся в файл vl6180.c и в функции vl6180_AlsGetLux вычислим предварительный множитель
uint32_t AlsScaler;
const uint32_t LuxResxIntIme = (uint32_t)(0.56f * DEF_INT_PEFRIOD * (1 << LUXRES_FIX_PREC));
В даташите даны некоторые рассчеты
Коэффициент 0,56 был взят из другого документа, который был написан для платы с наличием такого же датчика
Смещение на 8 мы вернём потом обратно. Это всё для точности при дробных вычислениях. Все рассчёты вязты в код были строго из официального примера, так что я не думаю, что будут какие-то ошибки.
Хотя в даташите идёт речь о коэффициенте 0,32.
Также это видно из таблицы диапазонов в зависимости от коэффициента усиления
Я так думаю, что в случае применения коэффициента 0,56 и диапазоны также будут соответствующие.
После функции считывания байта из регистра добавим также функцию считывания из регистра слова
//-------------------------------------------------
int vl6180_ReadWord(uint16_t index, uint16_t *data)
{
int status;
uint8_t buffer[2];
buffer[0] = index>>8;
buffer[1] = index&0xFF;
status = vl6180_I2C_Write(buffer, (uint8_t)2);
if( !status ){
status=vl6180_I2C_Read(buffer,2);
if( !status ){
*data=((uint16_t)buffer[0]<<8)|(uint16_t)buffer[1];
}
}
return status;
}
//-------------------------------------------------
Вернёмся в функцию vl6180_AlsGetLux и прочитаем пока что сырой результат из датчика
const uint32_t LuxResxIntIme = (uint32_t)(0.56f * DEF_INT_PEFRIOD * (1 << LUXRES_FIX_PREC));
status = vl6180_ReadWord(RESULT_ALS_VAL, &RawAls);
Для считывания результата измерения мы используем регистр, расположенный по адресу 0x50
Продолжим наши расчеты
status = vl6180_ReadWord(RESULT_ALS_VAL, &RawAls);
if (!status) {
IntPeriod = VL6180_DevData.IntegrationPeriod;
AlsScaler = VL6180_DevData.AlsScaler;
IntPeriod++; // what stored is real time ms -1 and it can be 0 for or 0 or 1ms
luxValue = (uint32_t)RawAls * LuxResxIntIme; // max # 16+8bits + 6bit (0.56*100)
luxValue /= IntPeriod; // max # 16+8bits + 6bit 16+8+1 to 9 bit
if (new_switch_state)
{
luxValue *=10;
}
//between 29 - 21 bit
AlsAnGain = VL6180_DevData.AlsGainCode;
}
В случае определённого положения переключателя мы увеличиваем показания в десять раз. Точность мы тут никакую не теряем, так как мы просто получаем возможность увидеть десятые доли.
Добавим некоторый инициализированный глобальный массив для рассчета при определённых коэффициентах усиления
VL6180x_AlsData_t Als; /* ALS measurement */
//-------------------------------------------------
static const uint16_t AlsGainLookUp[8] = {
(uint16_t)(20.0f * AN_GAIN_MULT),
(uint16_t)(10.0f * AN_GAIN_MULT),
(uint16_t)(5.0f * AN_GAIN_MULT),
(uint16_t)(2.5f * AN_GAIN_MULT),
(uint16_t)(1.67f * AN_GAIN_MULT),
(uint16_t)(1.25f * AN_GAIN_MULT),
(uint16_t)(1.0f * AN_GAIN_MULT),
(uint16_t)(40.0f * AN_GAIN_MULT),
};
//-------------------------------------------------
Вот здесь мы и скорректировали наш сдвиг на 8 обратно.
Вернёмся в функцию vl6180_AlsGetLux и закончим наши преобразования в люксы
AlsAnGain = VL6180_DevData.AlsGainCode;
GainFix = AlsGainLookUp[AlsAnGain];
luxValue = luxValue / (AlsScaler * GainFix);
*pLux = luxValue;
}
return status;
Теперь вызовем данную функцию в функции vl6180_AlsGetMeasurement
uint8_t ErrStatus;
status = vl6180_AlsGetLux(&pAlsData->lux);
if (!status) {
status = vl6180_ReadByte(RESULT_ALS_STATUS, &ErrStatus);
pAlsData->errorStatus = ErrStatus >> 4;
}
return status;
А эту функцию вызовем в функции vl6180_AlsPollMeasurement
vl6180_PollDelay();
};
if (!status) {
status = vl6180_AlsGetMeasurement(pAlsData);
}
ClrStatus = vl6180_AlsClearInterrupt();
if (ClrStatus) {
if (!status) {
status = ClrStatus; /* leave previous if already on error */
}
}
over:
А эту функцию вызовем в функции l6180_AlsState (какая вложенность!)
int status;
status = vl6180_AlsPollMeasurement(&Als);
if (status) {
HAL_UART_Transmit(&huart2, (uint8_t*)"Er 4",6,0x1000);
}
}
В файле main.c подключим глобальную переменную
volatile int8_t new_switch_state;
extern VL6180x_AlsData_t Als; /* ALS measurement */
В функции main() в бесконечном цикле отобразим наши данные на индикаторе, перед этим ограничив их максимум, чтобы они не ушли за рамки диапазона четырёх разрядов
vl6180_ReadData();
if(Als.lux>9999) Als.lux =9999;
ledprint(Als.lux);
Соберём код, прошьём контроллер и посмотрим результат при разных положениях переключателя
Отлично!
Теперь давайте ограничим диапазон измерений согласно вышепоказанной таблице, только раз уж мы используем коэффициент 0,56 то и персчитаем её под него
Теперь давайте в функции vl6180_AlsGetLux ограничим минимальные значения. Почему именно только минимальные, сейчас разберёмся.
Так как при поднятом вверх переключателе мы используем коэффициент усиления 20 (значние регистра 0), а при опущенном — 2,5 (значение регистра 3). Получается, что при поднятом переключателе у нас диапазон будет от 0,28 до 1820 люксов, а при опущенном — от 2,24 до 14560 люксов. Также надо будет учесть, что мы уже полученное значение заранее умножили на 10 при поднятом переключателе. Поэтому при поднятом переключателе мы будем ограничивать только нижний порог, а верхний у нас и так ограничен значением 9999 из-за разрешения нашего индикатора, что соответствует 999,9 люкс, что ниже максимального порога. То же самое в случае опущенного переключателя. Хоть мы на 10 уже значение и не умножаем, но предел 14560 также уходит за рамки разрешения индикатора. Вот поэтому мы ограничим только минимум, соответственно учитывая множитель на 10.
Но прежде чем всё это делать, давайте в файле led.c добавим функцию для отображения символа на разряде индикатора. Пока нам нужны будут для отображения только символы «L» и «H» для информирования о слишком низком значении или о слишком высоком
//-------------------------------------------------
void led_char(char c)
{
//Первые три разряда погасим
R1 = 10; R2 = 11; R3 = 12;
//а в четвертом отобразим символ
R4 = c;
}
//-------------------------------------------------
Сделаем также для данной функции прототип.
Теперь в функции segchar добавим ещё два варианта для символов
case 10: //Пробел
gpio_data &= ~(0x00);
break;
case 'L':
gpio_data &= ~(0x38);
break;
case 'H':
gpio_data &= ~(0x76);
break;
В файле vl6180.h добавим в структуру данных датчика VL6180x_AlsData_t ещё одно поле для отслеживания превышений и принижений значений
uint32_t lux; /**< Light measurement (Lux) */
uint8_t valueStatus; /* Value status */
uint32_t errorStatus; /**< Error status of the current measurement. \n
Также добавим несколько макросов для состояния показаний датчика
} VL6180x_AlsData_t;
//-------------------------------------------------
#define ValueNormal 0
#define ValueLow 1
#define ValueHigh 2
//-------------------------------------------------
В файле vl6180.c в функции vl6180_AlsGetLux добавим следующий код для установки сосотояния слишом низких значений освещённости
luxValue = luxValue / (AlsScaler * GainFix);
//Сначала установим нормальное состояние показаний
Als.valueStatus = ValueNormal;
//Ограничим минимальные значения
if (new_switch_state)
{
if(luxValue<28) Als.valueStatus=ValueLow;
}
else
{
if(luxValue<2) Als.valueStatus=ValueLow;
}
*pLux = luxValue;
Немного исправим код в бесконечном цикле в функции main() главного модуля
vl6180_ReadData();
if(Als.lux>9999) led_char('H');
else if(Als.valueStatus==ValueLow) led_char('L');
else ledprint(Als.lux);
}
/* USER CODE END 3 */
Соберем код, прошьём контроллер, и выключив всё освещение убедимся, что теперь при освещённости ниже заданного диапазона у нас на индикаторе отображается символ 'L', а когда выше — 'H'.
Впоследствии, когда мы будем использовать в качестве мониторинга ещё и дисплей платы F746, то нам хотя бы не придётся ограничиваться четырьмя разрядами. А пока и так неплохо.
Таким образом, используя отличный датчик VL6180X, нам сегодня удалось сочинить отличный бытовой люксометр.
Спасибо всем за внимание!
Предыдущая часть Программирование МК STM32 Следующий урок
Техническая документация:
Документация на датчик VL6180X
Документация на микросхему STMPE1600
Руководство пользователя на оценочную плату X-NUCLEO-6180XA1
Отладочную плату можно приобрести здесь Nucleo STM32F401RE
Оценочную плату можно приобрести здесь X-NUCLEO-6180XA1
Смотреть ВИДЕОУРОК (нажмите на картинку)