Подключение фоторезистора к STM32 (измерение освещенности)

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

Купить на Aliexpress

20 pcs 5539 Photresistor
Цена: 0.62$ + 0.25$ = 0.87$

20 pcs 5516 5537 5528 5549 5539 Photresistor
Цена: 0.61$ + 0.25$ = 0.86$

Проверка

Просто подключите любое фотосопротивление к омметру и посмотрите как изменяется его сопротивление при изменении освещенности.

Освещенность R, Ом
Пасмурная погода 1.5 кОм
Темнота (прикрыто рукой) 500 кОм
Яркий свет (фонарик) 100 Ом

Выбор фоторезистора

Первое что нужно это знать это название используемого фоторезистора, чтобы выяснить его электрические параметры, например у меня GL5539 100 кОм, вот данные на него:

Нам нужны две величины: сопротивление при освещенности 10 лк и коэффициент гамма γ (логарифм соотношения значения сопротивления при 10 люкс 100 люкс).

γ = 0.8

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

Вывод освещенности из формулы

Остается вывести отсюда одну, нужную нам неизвестную - освещенность.

Также построим идеальную характеристику, по ней видно что при низкой освещенности сопротивление больше, а при высокой - маленькое.

Схема подключения

Также очень советую параллельно фоторезистору поставить конденсатор 100 нФ.

Создание проекта в STM32CubeMX

Файл, новый проект: File -> New Project

Выбираем нужный МК: STM32F103C8

Включаем отладчик: System Core -> SYS -> Debug: Serial Wire

Включаем АЦП, Канал 0: ADC1 -> IN0
Настраиваем работу ПДП: ADC1 -> DMA Settings -> Add -> ADC1 (DMA Request) -> Mode: Circular

Включаем тактирование Таймера 4: TIM4 -> Internal Clock
Включаем прерывание: NVIC Settings -> TIM4 global interrupt
И для периода вызова этого прерывания 100 мс:
Предделитель (Prescaler): 800-1
Регистр автоперезагрузки (Auto-reload register):  1000-1

Нужен период T = 100 мс (f = 10 Гц).

Частота шины, от которой тактируется таймер (смотреть в Clock Configuration) делится на необходимую.

PSC*ARR = fAHB/f = 8000000/10 = 800000

Распределяем полученное число (800000) между предделителем и регистром автоперезагрузки:

PSC = 800-1; ARR = 1000-1;

Отлично, 800*1000=800000. Почему минус один? Счет идет с нуля, когда в предделитель записано ноль, то частота делится конечно же не на ноль, а на единицу (не изменяется), когда 1, то на два. Получается нужная частота (формула из RF):

fTIM4 = fAHB/(PSC+1)*(ARR+1) = 8000000/(800-1+1)*(1000-1+1) = 10 Гц

Таким образом в регистры PSC, ARR, CCRx записывайте за вычетом единицы.

Во вкладке настройки тактирования ничего не меняем HCLK = 8 MHz:

Любое название и для Keil:
Project Name: photocell
Toolchain/IDE: MDK-ARM, Min Version: V5.27

Генерируем код и открываем проект: GENERATE CODE -> Open Project

Определение сопротивления фоторезистора

Здесь нужно просто узнать сопротивление фоторезистора, при известном сопротивлении верхнего плеча R1, напряжении питания Vdd и падении напряжения собственно на датчике ADCR2 (данные с АЦП).

*Vdd берется равным 4096, чтобы не получить ноль в знаменатели.

Если сопротивление очень мало, то падение напряжение также маленькое и может случится так, что АЦП выдаст ноль, что недопустимо, как и максимум 4095 при большом сопротивлении, поэтому от этого нужно застраховаться.

Программирование

Ввиду того, что данные в моем случае будут переданы на телефон, то тяжелые математические вычисления конечно же будет выполнять он, а передаваться будут сырые данные с АЦП.

Но конечно же делать это можно и на МК, но сожрется куча памяти (годится в целях обучения).

Создаем глобальные переменные:

/* Photocell vars */ uint32_t Pht_R; float Pht_Div; float Pht_Temp; uint32_t Pht_Lux; #define PHT_UP_R 10000.0F #define PHT_10LX_R 50000.0F #define PHT_GAMMA 0.8F /* ADC vars */ uint16_t ADC_Raw[1]; /* Sheduler vars */ uint8_t Sch_100ms = 255;

Запускаем прерывание от таймера и преобразования от АЦП:

/* USER CODE BEGIN 2 */ /* adc calibration */ HAL_ADCEx_Calibration_Start(&hadc1); /* adc start */ HAL_ADC_Start_DMA(&hadc1, (uint32_t*)ADC_Raw, 1); HAL_ADC_Start_IT(&hadc1); /* timer 4 start it (100ms) */ HAL_TIM_Base_Start_IT(&htim4); /* USER CODE END 2 */

В прерывании от таймера присваиваем переменной-флагу 255.

/* USER CODE BEGIN 0 */ /************** timer 4 irqhandler *************/ void HAL_TIM_PeriodElapsedCallback(TIM_HandleTypeDef *htim) { if(htim->Instance == TIM4) { /* set every 100ms */ Sch_100ms = 255; } } /* USER CODE END 0 */

В главном цикле проверяем выставлен ли флаг T_100ms, если да, то вычисляем текущее сопротивление фоторезистора (по значению АЦП), ну а далее уже находим значение освещенности.

/* Infinite loop */ /* USER CODE BEGIN WHILE */ while (1) { if(Sch_100ms) { /* get adc value */ HAL_ADC_Start_IT(&hadc1); /* calc photoresistor resistance */ Pht_R = ((PHT_UP_R)/((4095.0)/(ADC_Raw[0])-1)); /* internim calcs */ Pht_Div = PHT_10LX_R/Pht_R; Pht_Temp = ((0.42*log(Pht_Div))/(PHT_GAMMA)) + 1; /* illuminance calc */ Pht_Lux = pow(10, Pht_Temp); /* nulify */ Sch_100ms = 0; } /* USER CODE END WHILE */

Получение показаний в отладчике

Как всегда добавляем все в окошко для просмотра (ПКМ на переменной -> Add to Watch 1), убираем представление в HEX и запускаем работу:

Но гораздо лучше (оперативней и наглядней) выводить значение так:

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

Также пока игрался облака пару раз закрыли солнце, вот так это выглядело:

Сравнение точности

Если проводить измерения при небольших значениях, то совпадение очень неплохое, а вот на ярком Солнце, где должно зашкаливать больших значений получить не получается. Это связано с тем, что коэффициент γ был взят в диапазоне 10-100 люкс, таким образом для повышения точности на всем диапазоне возможно введение нескольких значений для разных участков.

Также при сравнении еще стоит учитывать направление света, т.к. фоторезистор чувствителен практически на 180 градусов, а вот у сенсора в телефоне очень направленная чувствительность (как раз хорошо для определения препятствия).

Доказательство закона Вебера-Фехнера

Измерив величины освещенности в солнечную погоду и в комнате видим огромную разницу в цифрах, но глаз легко перестраивается, вот доказательство того, что ощущение логарифмическое.

Получение и отображение освещенности на телефоне

Используя  bluetooth модуль JDY-23 и конструктор приложений App Invertor 2 несложно создать приложение, которое получает переданные данные с АЦП и делает нужные нам расчеты с удобным заданием постоянных величин:

Калибровка

В современных телефонах есть встроенный датчик света, с которого с можно легко получить показания используя различные приложения ( Lux Meter). Таким образом можно откалибровать нашу систему.

*диапазон измерения у моего датчика 0-32767 (15-бит)

А при использовании самодельного приложения это вообще можно встроить как функцию, что-то вроде: положите телефон и нажмите кнопку. Нужный множитель сам подберется!

Моделька

3D модель фоторезистора также думаю не помешает при визуализации ПП.
D = 4.1 мм, H = 2.1 мм, Lвыводов = 10 мм, dмежду выводами = 3 мм.

Скачать

photocell.step
Модель фоторезистора.

https://github.com/Egoruch/LDR-STM32-HAL
(ссылка на GitHub)

LDR-STM32-HAL-master.zip
(прямая ссылка)
Рабочий проект STM32CubeMX + Keil.

Видос

Итого

Понятное дело, что с фоторезистором измерительного прибора у нас не получится сотворить, но все же применить этот показометр для некоторых целей можно. Например, для отслеживания зависимости освещения от времени суток, где важно не конкретное значение, а именно изменение, к тому же низкая скорость изменения сопротивления никак не помешает.

97
RSS
Нет комментариев. Ваш будет первым!
Загрузка...