Двухканальный диммер на AtTiny85 (UART)

Тема в разделе "Глядите, что я сделал", создана пользователем SergeiL, 13 апр 2018.

  1. SergeiL

    SergeiL Гуру

    Собственно то, что в заголовке. :)
    Идея избитая, реализаций много. Сделал под себя, как, казалось удобней и правильней.
    В принципе, не любитель диммеров, люблю, когда светло, редко когда хочется убавить, всегда хочется добавить, но ради ночного режима, когда есть минимальное освещение в коридоре, чтобы дойти до кухни или …, не сшибая стен, была воплощена данная тема.

    У меня это ESP, подключенный, к OpenHAB по MQTT. А диммер подключен к ESP по UART.

    Желающие могут воплотить подключение диммера по i2C. Изменения только в setup() и loop().

    Диммер на симисторах, с детектором нуля. Задержка управляющего импульса включения симистора реализована с использованием прерываний таймера 1. См код.

    Будьте осторожными с регистрами , TIMSK управляет как таймером 1, так и таймером 0, на котором реализован millis().


    Cхема: scheme.png

    Код:
    Код (C++):
    #include <avr/io.h>
    #include <avr/interrupt.h>
    #include <SoftwareSerial.h>          

    // AtTiny85 16MHz internal

    #define RX    4   // *** P4, Pin 2 (RX from Tiny need to connect to <- TX)
    #define TX    3   // *** P3, Pin 3 (TX from Tiny need to connect to -> RX)

    SoftwareSerial TinySerial(RX, TX);

    #define AC_DETECT 2       // Пин B2 zero cross detect, interrupt 0, физический pin 7
    #define AC_LOAD_0 0       // Пин B0 выход на нагрузку 1 физический pin 5
    #define AC_LOAD_1 1       // Пин B1 выход на нагрузку 2 физический pin 6

    #define MIN_TCNT_VAL 5    // Минимальное  значение TCNT1   Dimming level (5-150)  5 = ON, 150 = OFF
    #define MAX_TCNT_VAL 150  // Максимальное значение TCNT1   Dimming level (5-150)  5 = ON, 150 = OFF

    #define PULS_COUNT   4    // Длина импульса на Gate симистора в тиках таймера 1

    #define MAX_BUFF_LENG 5   // Размер буфера под команду

    volatile uint8_t dimming_0 = MAX_TCNT_VAL;  // Переменная для уровня диммирования нагрузки 1
    volatile uint8_t dimming_1 = MAX_TCNT_VAL;  // Переменная для уровня диммирования нагрузки 2
    volatile uint8_t dimming_0_cur = MAX_TCNT_VAL; // Переменная для хранения текущего уровня диммирования нагрузки 1
    volatile uint8_t dimming_1_cur = MAX_TCNT_VAL; // Переменная для хранения текущего уровня диммирования нагрузки 2

    volatile uint8_t Errflag=1;                 // Флаг ошибки  0 - штатная работа zero cross detect, 1 - не работает zero cross detect

    volatile uint8_t test_val;                  // переменные для отладки

    ISR(TIMER1_COMPA_vect)             // Отработчик Timer1 прерывание по сравнению с регистром OC1A
    {          
        static uint8_t dimming_0_pulse=0;
       
        if(dimming_0_pulse)
        {
          PORTB &= ~( 1 << AC_LOAD_0 );            // Выключим оптопару
          dimming_0_pulse=0;
        }
        else
        {
          if ( dimming_0_cur < MAX_TCNT_VAL && Errflag==0 )    // Если уровень диммирования больше минимального и нет ошибки от  zero cross detect
          {
            PORTB |= ( 1 << AC_LOAD_0 );                   // Включение нагрузки 0
            OCR1A=OCR1A+PULS_COUNT;
            dimming_0_pulse=1;
          }
        }
    }

    ISR(TIMER1_COMPB_vect)            // Отработчик Timer1 прерывание по сравнению с регистром OC1B
    {
        static uint8_t dimming_1_pulse=0;

        if(dimming_1_pulse)
        {
          PORTB &= ~( 1 << AC_LOAD_1 );            // Выключим оптопару
          dimming_1_pulse=0;
        }
        else
        {
          if ( dimming_1_cur < MAX_TCNT_VAL && Errflag==0 )    // Если уровень диммирования больше минимального и нет ошибки от  zero cross detect
          {
            PORTB |= ( 1 << AC_LOAD_1 );                   // Включение нагрузки 1
            OCR1B=OCR1B+PULS_COUNT;
            dimming_1_pulse=1;
          }
        }
    }

    ISR(TIMER1_OVF_vect)             // Обработчик Таймер 1 переполнение. Что то пошло не так, возможно не работает zero cross detection или нет напряжения сети
    {
        PORTB &= ~( 1 << AC_LOAD_0 );            // Выключим нагрузку 1
        PORTB &= ~( 1 << AC_LOAD_1 );            // Выключим нагрузку 2
        Errflag=1;                               // взведем флаг ошибки в работе zero cross detect.
    }

    ISR(INT0_vect)             // Отработчик INT0, zero cross detect, срабатывает по фронту (спад сетевого напряжения)
    {

        if (TCNT1 < 120 && Errflag==0)           // минимизируем помехи в рабочем режиме.  10мс - 156,25 отсчетов таймера. Должны попадать сюда на отсчетах таймера 155-158
          return;
        GTCCR |= 2;                              // сбросим счетчики предделителей
        TCNT1 = 0;   // сбросим таймер

        test_val=TCNT1;                          // отладка ( для отображения отсчета на котором сюда попали)
     
        if (dimming_0_cur>dimming_0)            
        {
          OCR1A=dimming_0_cur--;                // пропишем в регистр сравнения значение уровня диммирования нагрузки 0
        }
        else if(dimming_0_cur<dimming_0)
        {
          OCR1A=dimming_0_cur++;                // пропишем в регистр сравнения значение уровня диммирования нагрузки 0
        }
        else
        {
          OCR1A=dimming_0_cur;                  // пропишем в регистр сравнения значение уровня диммирования нагрузки 0
        }

       
        if (dimming_1_cur>dimming_1)
        {
          OCR1B=dimming_1_cur--;                // пропишем в регистр сравнения значение уровня диммирования нагрузки 1
        }
        else if (dimming_1_cur<dimming_1)  
        {
          OCR1B=dimming_1_cur++;                // пропишем в регистр сравнения значение уровня диммирования нагрузки 1
         
        }
        else
        {
          OCR1B=dimming_1_cur;                  // пропишем в регистр сравнения значение уровня диммирования нагрузки 1
        }

        PORTB &= ~( 1 << AC_LOAD_0 );            // Выключим оптопару
        PORTB &= ~( 1 << AC_LOAD_1 );            // Выключим оптопару
        Errflag=0;                               // рабочий режим, сбросим режим ошибки
    }


    void setup()
    {
        DDRB &= ~(1 << AC_DETECT);                       // Настроим как вход
        PORTB |= (1 << AC_DETECT);                       // Включим подтяжку
       
        DDRB |= 1 << AC_LOAD_0;                          // Настроим как выход
        DDRB |= 1 << AC_LOAD_1;                          // Настроим как выход
       
        PORTB &= ~( 1 << AC_LOAD_0 );                    // Выключим оптопару
        PORTB &= ~( 1 << AC_LOAD_1 );                    // Выключим оптопару
        GIMSK |= 1 << INT0;                              // Hазрешим прерывание от INT0
        MCUCR |= 1 << ISC01  | 1 << ISC00;               // Настроим прерывание от INT0 по фронту.
         
        TIMSK |= 1 << OCIE1A | 1 << OCIE1B | 1 << TOIE1;  // Разрешим прерывание для Таймера 1 по сравненю с OCR1A, OCR1B, и переполнению таймера
        TCNT1 = 0;                                        // сбросим таймер
        TCCR1 = B00001011;                                // включим таймер с предделителем на 1024
       
        OCR1A=MAX_TCNT_VAL;                               // пропишем в регистр сравнения значение для выключенной нагрузки
        OCR1B=MAX_TCNT_VAL;                               // пропишем в регистр сравнения значение для выключенной нагрузки
        GTCCR=0x00;
       
        TinySerial.begin(38400);                          // оптимальный вариант, (в SoftwareSerial есть cli() на отправке)
    }

    void loop()                                           // отладочный вариант loop() с получением информациии из Arduino IDE:  Канал + Мощность от 0 до 100. Например:  190  первый канал, мощность 90; 020 нулевой канал, мощность 20.
    {
       static char      buff[5];
       static byte      pos=0;
       static uint32_t  last_ms=0;
       static uint32_t  disp_ms=0;

       uint32_t         tmp_ms=millis();
       int              result = 0;
       byte             processing_flag = 0;
       char             c;

       if (TinySerial.available() > 0)   // если что то приняли
       {
          c=TinySerial.read();           // прочитаем байт
         
          if (c >= '0' && c <= '9' && pos < MAX_BUFF_LENG)      // если цифра
          {
            buff[pos++]=c;               // положим в буфер и сдвинем позицию
            last_ms = tmp_ms;            // запомним время получения байта
          }
          else
            processing_flag=1;           // если не символ - взведем флаг
         
       }
     
       if( pos >= 1 && ((tmp_ms - last_ms > 30) || processing_flag) )  // если пауза больше 30 милисекунд или не цифра
       {
          buff[pos]=0;
          result=atoi(buff+1);  //
          pos=0;

          if (buff[0] <= '1' && result >= 0 && result <= 100)  // если канал 0-1, значение 0-100
          {
                                       // ------ потом убрать  вместе с принтами ----
            TinySerial.println();              
         
            if (buff[0]=='0')
              TinySerial.print(" 0: <");
            else
              TinySerial.print(" 1: <");
     
            TinySerial.print(result);
            TinySerial.print("> (");
                                        //-------------------------
            if (buff[0]=='0')
            {
              dimming_0=map(result, 0, 100, MAX_TCNT_VAL, MIN_TCNT_VAL);
              TinySerial.print(dimming_0);
            }
            else  
            {
              dimming_1=map(result, 0, 100, MAX_TCNT_VAL, MIN_TCNT_VAL);
              TinySerial.print(dimming_1);
            }
           
            TinySerial.println(") ");
         }
       }
    }

    Управляем каналами диммера из монитора порта командами:
    Commands.png

    без пробелов
    020 = 0 канал , мощность 20%
    175 = 1 канал , мощность 75%


    Как добавить поддержку AtTiny85 Arduino IDE:


    В Сетингах добавляем дополнительные платы:

    Settings.png

    добавляемая ссылка:
    https://raw.githubusercontent.com/d...ds-manager/package_damellis_attiny_index.json

    В настройках платы вибираем параметры:

    BoardManager.png
     
    Последнее редактирование: 2 окт 2018
    ZAZ-965, Пушной звер и ИгорьК нравится это.
  2. SergeiL

    SergeiL Гуру

    Программатор использовал AVRISP mkII, фьюзы, по привычке, прошивал в AVR Studio4.
    В принципе, код в текущем исполнении лезет и в AtTiny45, но без дополнений... :(
     
    Последнее редактирование: 13 апр 2018
  3. SergeiL

    SergeiL Гуру

    Сегодня добавил в код сброс предделителей таймера (перед сбросом таймера, в прерывании от Zero-Cross). Теперь даже намека нет на мизерные (и до этого почти не ощутимые на вгляд) подрагивания яркости. :)
    Раньше грешил на нестабильность работы Zero-Cross, но все оказалось гораздо проще! :)
     
    atompost и ИгорьК нравится это.
  4. SergeiL

    SergeiL Гуру

    Сегодня затестил работу диммера со светодиодной, диммируемой лампой Навигатор 10Вт GX53.
    На втором канале обычная галогенка на 220В.
    Галогенки в квартире, в принципе, диммируются идеально.

    В работе со светодиодной лампой замечены следующие особенности:
    1. Диапазон заметного диммирования LED лампы 20%-70%. ( у галогенки 5%-95%)
    2. В диапазоне 20%-25% яркость увеличивается очень быстро. (галогенки на 25% еле горят)
    3. В диапазоне 20%-25% видно небольшое хаотичное изменение яркости. Похоже в пределах одного - двух отсчетов таймера. Сказывается быстрое нарастание яркости, указанное в предыдущем пункте.
    В общем хотел использовать в качестве ночного режима в санузлах (в ночное время, чуть подсветить заметив движение, дабы не сшибать предметы, но и не будить ярким светом).
    Из-за пункта 3, даже и не знаю теперь.
    Может галогенки оставлю, или ленту, под подвесную тумбочку раковины.
    Еще попробую с другой LED лампой.

    Или стоит попробовать поднять частоту таймера. Но просто поднять не получится, таймер 8-ми бтиный.

    Вот, а Дядя Витя спрашивал, зачем нужно такое космически точное диммирование.
    Представляю, что было бы с 10% погрешностью.
     
    Последнее редактирование: 30 сен 2018
    vvr и ИгорьК нравится это.
  5. DIYMan

    DIYMan Гуру

    Во второй строке должно же быть AC_LOAD_1, не?
     
    SergeiL нравится это.
  6. SergeiL

    SergeiL Гуру

    Да, все правильно! Спасибо!
    Конечно должно быт AC_LOAD_1
    Как всегда, у себя давно исправил, сюда не перенес. Поправил в первом сообщении.
     
  7. parovoZZ

    parovoZZ Гуру

    С 8-ми битным ловить нечего. Только 12-ти или 16-ти битный.
    И не увидел - длительность управляющего импульса как регулируется/задаётся?
     
  8. SergeiL

    SergeiL Гуру

    У меня прерывание - по спаду сетевого напряжения, поэтому выключаю там.
    То есть - включил из таймера, напряжение падает - выключил.
     
  9. parovoZZ

    parovoZZ Гуру

    Не, я имею ввиду импульс, который подаётся на ногу gain симистора для его открытия - не во всех квадрантах ток с этой ноги попадает в нагрузку, что приводит к неоправданному перерасходу электроэнергии.
     
  10. SergeiL

    SergeiL Гуру

    Да я понял, о чем речь, но пристраивать выключение не стал.
    Выключаю оптопару по спаду в прерывании ZeroCross.
    Попробовал и так и так, по ослику разницы не увидел.
     
  11. parovoZZ

    parovoZZ Гуру

    Себе в комнату сделал 160 Вт светодиодного света. Убавить - единственное желание. Сейчас примерно 30 Вт всего - хочется добавить, но пока не реализовано. Блок питания управляемый - хошь резистором, хошь ШИМ, хошь напругой 0-10В.
     
  12. SergeiL

    SergeiL Гуру

    А какие лампы? Или ленты?
    Второй второй день бьюсь с LED лампами GX35, диммируемыми - нормально не диммируются.
    Я понимаю, можно на постоянку с ШИМ перейти, но какие лампы использовать?

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

    Может кто-то лампы посоветует.
    Пробовал диммируемые Navigator, Gauss GX35.
     
  13. parovoZZ

    parovoZZ Гуру

    Arlight CRI 98%
    с внешним драйвером. Но они будут токовые, 99%.
    Споты и те с ШИМ стабилизатором внутри.
    Ничего себе(((
    Эти бы брал в последнюю очередь.

    Вообще дешманский светодиодный свет очень сильно режет глаз. В командировке снимали квартиру с такими светильниками - за 2 недели так и не привык.
    Приехал к себе - вообще ляпота.
    Как ни странно, но лампа из икеи на 10 вт горит уже 5 лет. Очень приятный свет даёт, но не диммируется.
     
  14. SergeiL

    SergeiL Гуру

    А что выбрать-то?
    Лента вроде не в тему, ищу светильники,можно с регулятором 0-10В.
    Вариантов много, но как работает... - не факт, что нормально.

    Нужен свет 2700К, диммируемый, без пульсаций :). Помещение 7 кв.м. Что выбрать?
     
  15. SergeiL

    SergeiL Гуру

    Подключил параллельно LED лампе галогенку, Gauss начала прекрасно диммироваться...
    У кого есть практический опыт??? Советуйте! :)
     
  16. parovoZZ

    parovoZZ Гуру

    Что-то отдельное от драйвера. У novolampa хороший ассортимент - я там затаривался.
    это да - кпд под вопросом. У ламп должно быть поболее.
    кухня?
    Очевидно, что не хватает тока для удержания симистора в открытом состоянии. Но это всего лишь догадки. Не знаю, как устроена диммируемая лампочка.
     
  17. SergeiL

    SergeiL Гуру

    Сейчас проверил параллельно Navigator, Gauss и галогенка.
    При наличии параллельной галогенки, Gauss диммируется прекрасно, без пульсаций и хаотичного изменения яркости.
    Зажигается на 3% и нормально диммируется до 80% (100% освещения). Вопрос, как решить проблему параллельной галогенки?
    Novigator - однозначно не айс :)!
    20-25% мощности очень большое изменение яркости на процент. Яркость заметно колбасит в пределах отсчета.
     
  18. SergeiL

    SergeiL Гуру

    Не, ванная.

    Вроде все стало работать стабильно. Нужно было добавить ламп. :) Маловата нагрузка была.
    Но на диммируемой LED лампе Navigator осталась небольшая болтанка яркости в диапазоне 20-25%.
    Даже с параллельно подключенной галогенкой.

    С Gauss 8W LED Dimmable начиная с двух параллельно подключенных ламп все стабильно. Понравилось! :)

    Добавил константу длительности управляющего импульса. Разницы не увидел, но пусть будет.
    Добавил плавное нарастание снижение мощности, при изменении уровня диммирования.
    Выглядит поинтереснее.
     
    Последнее редактирование: 2 окт 2018