Attiny85 слабо диммирует

Тема в разделе "Arduino & Shields", создана пользователем Ariadna-on-Line, 25 дек 2023.

  1. Ariadna-on-Line

    Ariadna-on-Line Гуру

    #include <avr/io.h>
    #include <avr/interrupt.h>

    // AtTiny85 16.5MHz internal

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

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

    Serial.begin(38400); // оптимальный вариант, (в SoftwareSerial есть cli() на отправке)
    Serial.println(F("Ariadna")); // дает 38400
    }

    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 (Serial.available() > 0) // если что то приняли
    {
    c=Serial.read(); // прочитаем байт
    Serial.print(F("Received-"));
    Serial.println(c);

    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 > 5000) || processing_flag) ) // если пауза больше 5 секунд или не цифра
    { // значит ввод закогчен и идет в обработку
    buff[pos]=0;
    result=atoi(buff+1); //
    pos=0;

    if (buff[0] <= '1' && result >= 0 && result <= 100) // если канал 0-1, значение 0-100
    {
    // ------ потом убрать вместе с принтами ----
    Serial.println();

    if (buff[0]=='0')
    Serial.print(" 0: <");
    else
    Serial.print(" 1: <");

    Serial.print(result);
    Serial.print("> (");
    //-------------------------
    if (buff[0]=='0')
    {
    dimming_0=map(result, 0, 100, MAX_TCNT_VAL, MIN_TCNT_VAL);
    Serial.print(dimming_0);
    }
    else
    {
    dimming_1=map(result, 0, 100, MAX_TCNT_VAL, MIN_TCNT_VAL);
    Serial.print(dimming_1);
    }
    Serial.println(") ");
    }
    }
    }
    Захотел диммер на Диджиспарк (ATtiny85). Решил компилить на платформе ATtinyCore. Взял код для УАРТ. Немного изменил.
    1. Поскольку платформа создает типа "аппаратный Сериал - нужды в СофтСериале - нет.
    2. Не все терминалы работают в пакетном режиме, а сразу шлют нажатую клавишу. Увеличил таймаут до 5 секунд для ручного ввода (str. 170).
    3. Ввел пару сервисных сообщений.
    Тестю в протеусе. Желтым - импульсы Ноль-перехода. Красный/Зеленый - Управление симистором. Регулируется, но фаза открытия симистора не желает приближаться к концу периода. На скрине задан - 1% мощности. Фаза показывает около 80-ти процентов на обоих каналах. Получается что диапазон диммирования всего 100 - 75 %
    Вопрос - в чем может быть причина ? Обязателен ли СофтСериал ? С уважением.
     

    Вложения:

    Последнее редактирование: 26 дек 2023
  2. parovoZZ

    parovoZZ Гуру

    не понятно, про что вопрос. Про сериал и его баги или про диммер, алгоритм работы которого никак не описан.
     
  3. Ariadna-on-Line

    Ariadna-on-Line Гуру

    А ну да. Не написал. Взято отсюда же из Амперки. Раздел "Глядите, что я сделал", тема
    "Двухканальный диммер на АТтини85 (I2C)".
    По-моему в вопросе все сформулировано исчерпывающе. Остальное видно в приложенном коде.

    ПС. Скомпилировал для пробы код в авторском варианте (с СофтСериалом). Единственно что изменил - таймауты ввода цифр до 5 секунд. Код на треть больше размером, но диммирует те же 100-75% оба канала. Печалька. В чем может быть проблема ? Может ли таймаут так повлиять? Хотелось бы услышать мнение начальника транспортного цеха знатоков таймеров...
     
    Последнее редактирование: 26 дек 2023
  4. Ariadna-on-Line

    Ariadna-on-Line Гуру

    Изменил значение прескалера таймера с 1024 на 4096 и все заработало нормально. Странно это. Явно я что-то упускаю из вида. Или это шутка ТС-а ?!
    135 TCCR1 = B00001011; // включим таймер с предделителем на 1024
    на
    135 TCCR1 = B00001101; // включим таймер с предделителем на 4096
    По осциллографу протеуса заметен небольшой джиттер фазы.
    ПС. Время - проверять в железе.
    Короче говоря - получил желаемое и даже сверх. Тема исчерпана.
     

    Вложения:

    • Pr4096.png
      Pr4096.png
      Размер файла:
      41,2 КБ
      Просмотров:
      53
    Последнее редактирование: 28 дек 2023
  5. Ariadna-on-Line

    Ariadna-on-Line Гуру

    //----------------------------------------------------
    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;
    }
    }
    }

    //----------------------------------------------------
    Не - тема не исчерпана. Тема еще не началась. Диммер в данном виде работает прекрасно. Но. - Места в памяти МК еще дофига. Замена Serial на picoUART (см. соседние ветки) высвободила один пин. Меня всегда тянет впихивать невпихуемое. А всякие цветомузыки и бегущие огни требуют ТРИ канала. Захотел третий канал.

    Тупо скопипастил один из работающих обработчиков прерывания по компаратору Таймера1, чуток подпилил под свободный пин МК - и упёрся в отсутствие 3-го компаратора. Первый раз взялся за прерывания и обломился.)))))))))

    Разбиралово с кодом обработчика показало - что он сам подгружает компаратор (по сути фазовый угол в полупериоде) и этим вызывает сам себя. "Доделывать недоделки первого захода". Хотя я не гуру в программировании. Может ошибаюсь ((((((

    Возникает идея - использовать эту фишку для обслуживания вообще всех каналов одним обработчиком. То есть - обработчик начинает с меньшего угла и перед завершением - грузит в компаратор следующий "угол" по возрастанию и нужный номер канала обслуживания. И так хоть 256 каналов. Но тут возникают коллизии одинакового угла для нескольких каналов одновременно. Ткните носом - может кто уже реализовал такую идею ? Или есть технология проще ? Думаю идея рабочая, но превратить ее в код у меня мозгов не хватит. Особенно с разруливанием коллизий. С уважением.
     
    Последнее редактирование: 12 янв 2024
  6. Asper Daffy

    Asper Daffy Иксперд

    А в соседней теме
    Страдаете амбивалентностью? Не запускайте, это небезобидно. Амбивалентность - основной симптом шизофрении и множества других расстройств шизоидной природы. Обратитесь к доктору.
     
  7. parovoZZ

    parovoZZ Гуру

    каким образом? Такое ощущение, что ты не понимаешь, как исполняется машинный код в процессоре микроконтроллера.

    как минимум, есть два подхода - машина состояний или тупым линейным кодом.

    тебя кто-то заставляет этим заниматься?
     
  8. Ariadna-on-Line

    Ariadna-on-Line Гуру

    Цель - переделать фазовый диммер 2-канальный на 3-х.
    Гуру из РадиоКот-а подсказали идею. Заменил прерываниЯ по компараторам, на прерываниЕ по переполнению. Обработчик управляет всеми тремя каналами. Код максимально сохранен в исходнике. Однопроводный Сериал высвободил один пин. В Протеусе работает, но пока не "причесан".
    ПС. Однопроводный Сериал picoUART, в отличие от стандартного - здОрово экономит память (ок. 1Кб ПЗУ, 200 б ОЗУ)
     

    Вложения:

    • 3Ch-dimmer.png
      3Ch-dimmer.png
      Размер файла:
      54 КБ
      Просмотров:
      39
    Последнее редактирование: 27 янв 2024