Русский
Русский
English
Статистика
Реклама

Ещё одна фитолампа на Arduino

Пришла зима, короткий день, домашней пальме мало света. Нужно организовать подсветку. Готовую лампу покупать как-то неловко, да и надо ж чем-то заняться долгими зимними вечерами. Поехали ;)

  1. Купил на Али мощные светодиоды с питанием в 220В. Вот такие:

Отзывы оказались верными: греются, и очень сильно. Нужно огранизовать охлаждение. Пассивного (прикрутил к дюралевому профилю) недостаточно, значит, нужно придумать что-то активное. Например, радиатор с кулером от старого процессора - это недорого, и их как грязи. Правда, где кулер - там питание 12В. Ок, это тоже несложно. Но при питании от 12 В кулер (особенно недорогой бу) будет достаточно громко шуметь.

2. Регулятор оборотов кулера.

Пусть это будет step-down конвертор. Хороший КПД, меньше помех в цепях питания.

Ключевой транзистор и диод выпаял из покойной материнской платы, дроссель оттуда же. Оригинальную обмотку из толстого провода срезал, намотал свою проводом 0.3мм - сколько поместилось. Транзистор Q1 - какой-то маломощный npn из закромов. Подаем на вход импульсы разной скважности - на выходе получаем напряжение от 0 до 12В. Удобно, но нужен генератор импульсов с достаточно высокой частотой (десятки кГц и выше).

3 . Как сделать генератор импульсов со скважностью от 0 до 100%? Причем с возможностью автоматической регулировки? Проще всего взять микроконтроллер, который умеет в PWM. Я склоняюсь к AtMega8 - простой, отлично документированный и дешевый. Но использовать микроконтроллер только для вращения кулера - это из пушки по воробьям. Можно на этом же железе решить и дополнительные задачи.

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

  • подсветка работает 8 часов и отключается по таймеру или кнопкой;

  • включается подсветка по кнопке или с наступлением рассвета;

  • рассвет детектируется светодиодом. Для этого функционала достаточно фоторезистора и АЦП.

Кулер желательно крутить помедленнее, но не допуская ни полной остановки, ни перегрева светодиода. Для детектирования скорости вращения у кулера есть встроенный датчик, для измерения температуры возьмем терморезистор. Мне попался NTC MF52A1, с сопротивлением при 25С 10кОм. В даташите есть таблица с зависимостью сопротивления от температуры; так что можно сделать простой и очень дешевый измеритель температуры с приемлемой точностью. Итого, от контроллера нужно:

  • АЦП - 2 входа

  • PWM - 1 выход

  • прерывание датчика вращения кулера - 1 вход

  • кнопка вкл/выкл - 1 вход

  • управление подсветкой - 1 выход

  • UART - удобно для диагностики в железе

4. Конфигурирование микроконтроллера

Fuse bits.

Чтобы не получить окирпиченный МК, проще всего воспользоваться готовым калькулятором fuse битов. Например, таким. Главное, что нужно - выбрать синхронизацию от внешнего кварца. Остальные биты можно не трогать ;) У меня получилось

lfuse

0xFF

hfuse

0xD9

Таймеры.

Во первых, PWM. Для этого пригодны 2 таймера: 16-битный Timer1 и 8-битный Timer2. Timer1 будет считать системное время (об этом позже), Timer2 (с 256 уровнями PWM) - отлично подходит для регулятора напряжения. При тактовой частоте 16 МГц максимальная частота PWM будет

16000000/256=62500 Гц

Длительность самого короткого импулься

1/16МГц=62 нС

Думаю, это приемлемо. В даташите на ключевой транзистор есть Rise Time 33 nS. В окончательном варианте устройства можно использовать встроенный генератор на 8МГц - это тоже сработает.

Настройка Timer2

См https://sites.google.com/site/qeewiki/books/avr-guide/pwm-atmega8

Нам нужны следующие функции:

  • No Prescaling - чтобы получить частоту повыше

  • Fast PWM

  • Inverted mode (LOW at bottom, HIGH on Match) - преобразователь напряжения с инвертором; когда напряжение на выходе прямо зависит от значения счетчика - это удобно.

// PWM// Выход жестко приколочен к пину PB3#define PWM_FAN_PIN _BV(PB3)DDRB |= PWM_FAN_PIN;OCR2 = 255;    // set PWM for 100% duty cycle    TCCR2 |= (1 << COM21);    TCCR2 |= (1 << COM20);    // set inverting mode    TCCR2 |= (1 << WGM21) | (1 << WGM20);    // set fast PWM Mode    TCCR2 |= (1 << CS20);    // set prescaler to 0 and starts PWM

Измерение времени.

В Arduino принято измерять время в миллисекундах - с такой частотой тикает таймер. Такая точность для наших целей не нужна, да и миллисекундный счетчик переполняется через 49 суток. Мы будем считать интервалы по 100 мС, этот счетчик переполнится очень не скоро.

16000000/256/6250 = 10 Гц

Чтобы получить такие временные интервалы с кварцем 16 МГц, нужен предделитель 256 и счетчик до 6250. Прекрасно.

Настройка Timer1

См https://sites.google.com/site/qeewiki/books/avr-guide/timer-on-the-atmega8

// 16000000/256/6250 = 10 hz// timer 1, CTC, 256 prescalerTCCR1B |= (1 << CS12) | (1 << WGM12);TIMSK |= (1 << OCF1A); // enable interrupt OCR1A = 6250;

Код счетчика времени очень простой:

uint32_t uptime_x_0_1s = 0;ISR (TIMER1_COMPA_vect){    // action to be done every 0.1 sec    uptime_x_0_1s++;}

Переполнится счетчик через

2^{32}/3600/24/365/10 = 13 лет

Больше чем достаточно ;)

Измерение частоты вращения кулера

В кулере есть встроенный датчик, который замыкается на землю 1 раз (или 2 раза - разные источники пишут разное) за оборот. Для измерения частоты вращения подключим датчик к пину, умеющему генерировать прерывания, сконфигурируем его как вход с внутренним pull-up резистором.

Прерывания от кулера

См http://www.atmega8.ru/wiki/view/doc.9.html

// FAN interrupt  #define TAHO_FAN_PIN _BV(PD3)SREG |= (1<<7);PORTD |= TAHO_FAN_PIN; // pull-upGIMSK |= _BV(INT1);MCUCR |= _BV(ISC11); // Прерывание вызывается по возрастающему фронту сигнала на входе INT

Счетчик прерываний совсем простой:

uint16_t ts_fan_interrupt;ISR (INT1_vect){    /* interrupt code here */    // убираем дребезг - на совсем малых оборотах     // он почему-то наблюдался    if (TCNT1 - ts_fan_interrupt <10) return;    ts_fan_interrupt = TCNT1;    fan_cnt ++;}

Функция do_fan_rps срабатывает не чаще чем 1 раз в секунду и подсчитывает число прерываний за 1 секунду.

unsigned int rps = 0;void do_fan_rps(){static uint32_t rps_ts;unsigned int fan_cnt_;int dt = uptime_x_0_1s - rps_ts;if ( dt < 10) return;cli();fan_cnt_ = fan_cnt;fan_cnt = 0;sei();rps = fan_cnt_ * 10 /dt ;rps_ts = uptime_x_0_1s;}

АЦП будем использовать для измерения температуры и освещенности. АЦП в AtMega 10-разрядный, и он может измерять 1024 уровня от 0 до опорного напряжения Vref. 2 младших разряда можно отбросить, и работать с результатом в uint8_t - это экономит немного памяти. Имеется также встроенный источник опорного напряжения 2.56 В.

Обработка АЦП

См https://narodstream.ru/avr-urok-22-izuchaem-acp-chast-1/

 ADCSRA |= (1<<ADEN);  // Разрешение использования АЦПADCSRA |=(1<<ADPS2)|(1<<ADPS1)|(1<<ADPS0);//Делитель 128 = 125 кГц ADMUX  |= (1<<REFS1)|(1<<REFS0)|(1<<ADLAR); //Внутренний Источник ОН 2,56в, adc0, use high 8b (ADCH)

Чтение из АЦП процесс медленный, лучше им не злоупотреблять

uint8_t ADC_read(uint8_t channel){ADMUX &= ~7;ADMUX |= (channel & 7);ADCSRA |= (1<<ADSC); //Начинаем преобразованиеwhile((ADCSRA & (1<<ADSC))); //проверим закончилось ли аналого-цифровое преобразованиеreturn ADCH; }

Измеритель температуры представляет собой резисторный делитель напряжения. Зависимость напряжения от температуры, скорее всего, можно представить в виде формулы. Но проще составить таблицу соответствия температуры и напряжения с точностью 1 С - размер ее не будет слишком велик. Я взял datasheet на терморезистор, librecalc, закон Ома и получил что-то такое:

uint8_t adc_measures_base24[] = { 255, 250, 245, 239, 234, 228, 223, 218, 213, 207, 202, 197, 192, 188, 183, 178, 173, 169, 164, 160, 156, 151, 147, 143, 139, 135, 132, 128, 124, 121, 117, 114, 111, 108, 105, 102, 99, 96, 93, 90, 88, 85, 83, 80, 78, 76, 73, 71, 69, 67, 65, 63, 61, 60, 58, 56, 55, 53, 52, 50, 49, 47, 46, 45, 43, 42, 41, 40, 39, 38, 36, 35, 34, 33, 33, 32, 31, 30, 29, 28, 28, 27, 26, 25, 25, 24, 23, 23, 22, 22, 21, 20, 20, 19, 19, 18, 18, 18, 17, 17, 16, 16};

Температуру ниже 24С и выше 120С измерять не имеет смысла.

UART. Как оказалость, здесь тоже имеются свои тонкости. Передача данных через UART требует высокой точности временных интервалов; поэтому использовать UART без внешнего кварца не стоит. Также, не любую частоту передачи можно получить с помощью встроенных делителей частоты. Очень удобно, что есть готовая таблица, из которой можно найти скорость с минимальной погрешностью. У меня увереннно заработала передача на 38400bps.

Настройка UART
#define BAUD 38400#define MY_UBRR (F_CPU/16/BAUD - 1)// UARTUBRRH = (unsigned char) ((MY_UBRR) >> 8);UBRRL = (unsigned char) (MY_UBRR);UCSRB = (1<<TXEN);UCSRC = (1<<URSEL)|(1<<UCSZ0)|(1<<UCSZ1);  

PID-регулятор

Что ж за самоделка без PID-регулятора? ;) Здесь из 2. Нужно поддерживать температуру около 40С, и обороты около 30 rps. 2 датчика (цифровой и аналоговый), и одно управляющее воздействие - скважность PWM.

Код регулятора
#define GOAL_T 40#define GOAL_RPS 30// K_T = 1/2 #define K_T_A 1#define K_T_B 2// K_RPS = 1/5#define K_RPS_A 1#define K_RPS_B 5 void do_pid(){static uint32_t pid_ts;if (uptime_x_0_1s-pid_ts < 10) return; // не чаще 1 раз/секpid_ts = uptime_x_0_1s;int pwm_val = OCR2;int dt = t1 - GOAL_T;int d_rps = GOAL_RPS - rps;int d_pwm_t = (dt * K_T_A ) / K_T_B;if (d_pwm_t < -5) {d_pwm_t = -5;};int d_pwm_rps = (d_rps * K_RPS_A) / K_RPS_B;int pwm_val_t = pwm_val + d_pwm_t;int pwm_val_rps = pwm_val + d_pwm_rps;int d_pwm;if (pwm_val_t > pwm_val_rps){d_pwm = d_pwm_t;} else {d_pwm = d_pwm_rps;};pwm_val += d_pwm;if (pwm_val > 255) pwm_val = 255;if (pwm_val <0 ) pwm_val = 0;OCR2 = pwm_val;}

Коеффициенты для обоих регуляторов заданы в виде натуральной дроби, чтобы не использовать float вычисления.

Про обработку кнопки с подавлением дребезга и прочее мигание светодиодом, пожалуй, писать не нужно ;)

В результате получилась вот такая конструкция на макетной плате.

Шакальное фото

Выводы.

Поставленная задача решается: светодиод светится, кулер крутится, температура 40С держится с достаточно низкими оборотами кулера; можно брать и более мощные светодиоды.

Код прошивки и схема устройства выложены на github.

TODO:

  • Научиться разводить платы в KiCad и сделать более изящную конструкцию.

  • Избавиться от кварца; точности встроенного генератора должно хватить для измерения интервала 8ч.

  • Вместо step-down конвертора использовать источник питания 5В и step-up конвертор.

З: Пожалуй, нужно упомянуть о использованном софте. При работе над устройством ни один правообладатель не пострадал.

ОС - Ubuntu 18

Компилятор - avr-gcc

IDE - geany

Программатор - Arduino Uno с прошивкой ArduinoISP

Схема электрическая принципиальная рисовалась в KiCad

Прототип паялся на макетной плате.

Источник: habr.com
К списку статей
Опубликовано: 14.12.2020 08:18:37
0

Сейчас читают

Комментариев (0)
Имя
Электронная почта

C

Программирование микроконтроллеров

Умный дом

Diy или сделай сам

Электроника для начинающих

Arduino

Atmega

Растения

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

Категории

Последние комментарии

  • Имя: Макс
    24.08.2022 | 11:28
    Я разраб в IT компании, работаю на арбитражную команду. Мы работаем с приламы и сайтами, при работе замечаются постоянные баны и лаги. Пацаны посоветовали сервис по анализу исходного кода,https://app Подробнее..
  • Имя: 9055410337
    20.08.2022 | 17:41
    поможем пишите в телеграм Подробнее..
  • Имя: sabbat
    17.08.2022 | 20:42
    Охренеть.. это просто шикарная статья, феноменально круто. Большое спасибо за разбор! Надеюсь как-нибудь с тобой связаться для обсуждений чего-либо) Подробнее..
  • Имя: Мария
    09.08.2022 | 14:44
    Добрый день. Если обладаете такой информацией, то подскажите, пожалуйста, где можно найти много-много материала по Yggdrasil и его уязвимостях для написания диплома? Благодарю. Подробнее..
© 2006-2024, personeltest.ru