Спойлер: Код #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 % Вопрос - в чем может быть причина ? Обязателен ли СофтСериал ? С уважением.
не понятно, про что вопрос. Про сериал и его баги или про диммер, алгоритм работы которого никак не описан.
А ну да. Не написал. Взято отсюда же из Амперки. Раздел "Глядите, что я сделал", тема "Двухканальный диммер на АТтини85 (I2C)". По-моему в вопросе все сформулировано исчерпывающе. Остальное видно в приложенном коде. ПС. Скомпилировал для пробы код в авторском варианте (с СофтСериалом). Единственно что изменил - таймауты ввода цифр до 5 секунд. Код на треть больше размером, но диммирует те же 100-75% оба канала. Печалька. В чем может быть проблема ? Может ли таймаут так повлиять? Хотелось бы услышать мнение начальника транспортного цеха знатоков таймеров...
Изменил значение прескалера таймера с 1024 на 4096 и все заработало нормально. Странно это. Явно я что-то упускаю из вида. Или это шутка ТС-а ?! 135 TCCR1 = B00001011; // включим таймер с предделителем на 1024 на 135 TCCR1 = B00001101; // включим таймер с предделителем на 4096 По осциллографу протеуса заметен небольшой джиттер фазы. ПС. Время - проверять в железе. Короче говоря - получил желаемое и даже сверх. Тема исчерпана.
Спойлер: Обработчик //---------------------------------------------------- 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 каналов. Но тут возникают коллизии одинакового угла для нескольких каналов одновременно. Ткните носом - может кто уже реализовал такую идею ? Или есть технология проще ? Думаю идея рабочая, но превратить ее в код у меня мозгов не хватит. Особенно с разруливанием коллизий. С уважением.
А в соседней теме Страдаете амбивалентностью? Не запускайте, это небезобидно. Амбивалентность - основной симптом шизофрении и множества других расстройств шизоидной природы. Обратитесь к доктору.
каким образом? Такое ощущение, что ты не понимаешь, как исполняется машинный код в процессоре микроконтроллера. как минимум, есть два подхода - машина состояний или тупым линейным кодом. тебя кто-то заставляет этим заниматься?
Цель - переделать фазовый диммер 2-канальный на 3-х. Гуру из РадиоКот-а подсказали идею. Заменил прерываниЯ по компараторам, на прерываниЕ по переполнению. Обработчик управляет всеми тремя каналами. Код максимально сохранен в исходнике. Однопроводный Сериал высвободил один пин. В Протеусе работает, но пока не "причесан". ПС. Однопроводный Сериал picoUART, в отличие от стандартного - здОрово экономит память (ок. 1Кб ПЗУ, 200 б ОЗУ)