STM Урок 90. Датчик освещённости VL6180X. Часть 5



 

Урок 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));

 

В даташите даны некоторые рассчеты

 

index34

 

index35

 

Коэффициент 0,56 был взят из другого документа, который был написан для платы с наличием такого же датчика

 

index36

 

Смещение на 8 мы вернём потом обратно. Это всё для точности при дробных вычислениях. Все рассчёты вязты в код были строго из официального примера, так что я не думаю, что будут какие-то ошибки.

Хотя в даташите идёт речь о коэффициенте 0,32.

Также это видно из таблицы диапазонов в зависимости от коэффициента усиления

 

index37

 

Я так думаю, что в случае применения коэффициента 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

 

index38

 

Продолжим наши расчеты

 

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

 

Соберём код, прошьём контроллер и посмотрим результат при разных положениях переключателя

 

 index05 index04

 

Отлично!

Теперь давайте ограничим диапазон измерений согласно вышепоказанной таблице, только раз уж мы используем коэффициент 0,56 то и персчитаем её под него

 

image39

Теперь давайте в функции 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'.

 

index06  index07

 

Впоследствии, когда мы будем использовать в качестве мониторинга ещё и дисплей платы F746, то нам хотя бы не придётся ограничиваться четырьмя разрядами. А пока и так неплохо.

Таким образом, используя отличный датчик VL6180X, нам сегодня удалось сочинить отличный бытовой люксометр.

Спасибо всем за внимание!

 

 

Предыдущая часть Программирование МК STM32 Следующий урок

 

Исходный код

 

Техническая документация:

Документация на датчик VL6180X

Документация на микросхему STMPE1600

Руководство пользователя на оценочную плату X-NUCLEO-6180XA1

 

 

Отладочную плату можно приобрести здесь Nucleo STM32F401RE

Оценочную плату можно приобрести здесь X-NUCLEO-6180XA1

 

 

Смотреть ВИДЕОУРОК (нажмите на картинку)

 

STM Name