Attiny85 и прерывание.
- Войдите на сайт для отправки комментариев
Ср, 07/09/2016 - 07:51
Доброго дня други! Предыстория:
Решил собрать автономный диммер на Тиньке с детектором ноля и поэтэссами. Нарыл код, немного допилил - работает. Рулится аналоговым напряжением.
// Voltage controlled dimmer with ATtiny85 //http://forum.arduino.cc/index.php?topic=314773.0 #include <avr/io.h> #include <avr/interrupt.h> #define DETECT 2 //zero cross detect, interrupt 0, is physical pin 7 #define GATE 3 //triac gate is physical pin 2 #define PULSE 2 //trigger pulse width (counts) #define INSTELPIN 2 // =A2 (digital pin4) is physical pin 3 const byte averageFactor = 10; // коэффициент сглаживания показаний (0 = не сглаживать) // чем выше, тем больше "инерционность" int sensorValue = 0; void setup() { // set up pins pinMode(DETECT, INPUT); //zero cross detect digitalWrite(DETECT, HIGH); //enable pull-up resistor pinMode(GATE, OUTPUT); //triac gate control // set up Timer1 TCCR1 = 0; // stop timer OCR1A = 50; //initialize the comparator TIMSK = _BV(OCIE1A) | _BV(TOIE1); //interrupt on Compare Match A | enable timer overflow interrupt sei(); // enable interrupts // set up zero crossing interrupt attachInterrupt(0, zeroCrossingInterrupt, FALLING); } //Interrupt Service Routines void zeroCrossingInterrupt() { TCNT1 = 0; //reset timer - count from zero TCCR1 = B00001011; // prescaler on 1024, see table 12.5 of the tiny85 datasheet } ISR(TIMER1_COMPA_vect) { //comparator match digitalWrite(GATE, HIGH); //set triac gate to high TCNT1 = 255 - PULSE; //trigger pulse width, when TCNT1=255 timer1 overflows } ISR(TIMER1_OVF_vect) { //timer1 overflow digitalWrite(GATE, LOW); //turn off triac gate TCCR1 = 0; //disable timer stop unintended triggers } void loop() { // use analog input to set the dimmer int oldsensorValue = sensorValue; sensorValue = analogRead(INSTELPIN); if (averageFactor > 0) // усреднение показаний для устранения "скачков" { sensorValue = (oldsensorValue * (averageFactor - 1) + sensorValue) / averageFactor; } OCR1A = OCR1A = map(sensorValue, 0, 1023, 125, 2); } С ШИМа мерцает, даже с программным сглаживанием. Прикрутил отдельный ЦАП с управлением по i2c - так работает хорошо.
Дальше естественно захотелось добавить сделать из самого диммера i2c slave девайс. Нашел либу tinyWireS - работает. Обмен данными с Ардуиной без проблем.
Теперь собственно проблема. Диммер и i2c по отдельности работают, а вместе нет. Потому что пин SDA используется и для прерывания под детектор ноля. Переназначить на другой пин у меня не получается. Не заработал ни этот метод, ни другой.
Вот код диммера с i2c:
// Voltage controlled dimmer with ATtiny85 //http://forum.arduino.cc/index.php?topic=314773.0 #include <PinChangeInterrupt.h> #include <avr/io.h> //#include <avr/interrupt.h> #include <TinyWireS.h> #define I2C_SLAVE_ADDR (0x41) #define DETECT 1 //zero cross detect, interrupt 0, is physical pin 6 #define GATE 3 //triac gate is physical pin 2 #define PULSE 2 //trigger pulse width (counts) // I2C pins: PB2 - SCL, PB0 - SDA volatile byte i2cValue; void setup() { // set up pins TinyWireS.begin(I2C_SLAVE_ADDR); pinMode(DETECT, INPUT); //zero cross detect digitalWrite(DETECT, HIGH); //enable pull-up resistor pinMode(GATE, OUTPUT); //triac gate control // set up Timer1 attachPCINT(DETECT, zeroCrossingInterrupt, FALLING); OCR1A = 50; //initialize the comparator TIMSK = _BV(OCIE1A) | _BV(TOIE1); //interrupt on Compare Match A | enable timer overflow interrupt sei(); // enable interrupts // set up zero crossing interrupt //attachInterrupt(0, zeroCrossingInterrupt, FALLING); TinyWireS.onRequest(requestEvent); } //Interrupt Service Routines void zeroCrossingInterrupt() { TCNT1 = 0; //reset timer - count from zero TCCR1 = B00001011; // prescaler on 1024, see table 12.5 of the tiny85 datasheet } ISR(TIMER1_COMPA_vect) { //comparator match digitalWrite(GATE, HIGH); //set triac gate to high TCNT1 = 255 - PULSE; //trigger pulse width, when TCNT1=255 timer1 overflows } ISR(TIMER1_OVF_vect) { //timer1 overflow digitalWrite(GATE, LOW); //turn off triac gate TCCR1 = 0; //disable timer stop unintended triggers } void loop() { // use analog input to set the dimmer if (TinyWireS.available()) i2cValue = TinyWireS.receive(); OCR1A = OCR1A = map(230, 0, 254, 125, 2); TinyWireS_stop_check(); } void requestEvent() { TinyWireS.send(i2cValue); }
Подскажите куда копать? Регистры и ассемблерные втавки для меня сложновато пока.
Umka, вторая ссылка всё правильно рассказывает. Нужно полностью отказаться от функции аттачинтеррапт и сделать прерывание PCINT как там описано для другой ноги. Только вам для сохранения прежней логики нужно в прерывании перечитать нужный вход, если LOW То обрабатывать дальше, если HIGH то выйти из прерывания ничего не делая.
Если до выходных не решите проблему, "апните" тему в субботу, чтобы я её увидел, ладно.
нужно в прерывании перечитать нужный вход, если LOW То обрабатывать дальше, если HIGH то выйти из прерывания ничего не делая.
В данном случае, достаточно разрешить прерывания только с одного пина, при помощи PCMSK. Тогда и проверять ничего не нужно будет.
Все понимаю, а немного не понимаю. Ведь в ISR() у нас идет слежение за компаратором и переполнением таймера. Как туда добавить слежение за регистрами этими? В PCINT0_vect
Umka,
Вот этот кусок и не понятен. Тут же 2 вызова ISR по разным событиям. Не догоняю пока. Или ISR(TIMER1_COMPA_vect) и ISR(TIMER1_OVF_vect) не зависимо от дрыга пином будут вызываться по своему прерыванию?
Просто добавляем
TCNT1 = 0; //reset timer - count from zero
Тут же 2 вызова ISR по разным событиям. Не догоняю пока. Или ISR(TIMER1_COMPA_vect) и ISR(TIMER1_OVF_vect) не зависимо от дрыга пином будут вызываться по своему прерыванию?
Ну да. У вас же рабочий скечт, значит одно прерывание поднимает, другое опускает ногу.
Просто добавляем TCNT1 = 0; //reset timer - count from zeroTCCR1 = B00001011; // prescaler on 1024, see table 12.5 of the tiny85 datasheet в ISR (PCINT0_vect) да?
да, я же в предыдущем сообщении написал уже, зачем переспрашивать-то?
Вот этот кусок и не понятен. Тут же 2 вызова ISR по разным событиям. Не догоняю пока.
Это макрос
Так как параметров нет, то будет развернут как
а в конце концов так:
соответственно
Разобрался. Не выключил либу PinChangeInterrupt.h
Umka, и чего? Компмилятор вам говорит что много определений для вектора2. По русски говоря
ISR (PCINT0_vect)
у вас в программе указан более чем 1 разИ прерывания на PB1 не ловит. Диммер не светит. :(
Вот полный код:
Umka, 43 строку отключать не нужно, если бы она была лишней я бы её не писал. Если не в этом дело, то у вас где-то ошибка, и она не в настроке прерывания. ищите.
Это я пробовал так и этак. Не работает. Вот так работает:
Umka, вы рано совместили рабочий код с i2c. Верните старый код, и модифицируйте прерывание На нём. Если всё ок, то только тогда можно пробовать организовать i2c.
Не диммит. И похоже этот метод RISING вызов прерывания не поддерживает. Может в этом загвоздка?
Umka, опять вы выбросили условие проверки на единицу. Последний раз говорю -это не лишнее условие, оно определяет алгоритм "Falling" обработки прерывания
Пардон, пропустил. Но и так не диммит.
Umka, а сигнал то переключали на B1 ? ) Можно для диагностики внутрь прерывания запихать мигание светодиодом, к примеру если он на PB0, то назначить его выходом в сетапе и:
должен мигать 2 раза в секунду.
Стоп. Тут плата клон digispark rev3 с потертым загрузчиком. На 1 пине там как раз светодиод. Может в этом причина. Щас перекину детектор на другой пин.
Ну едрить его в качель! Работает. Надо же таким балбесом быть. Забыл про светодиод. И с i2c все работает.
Вот финальный код:
Вот код мастера дла Ардуино, он рандомное значение шлет в Тини по i2c и выводит в порт ответ.
Вот схема. Плату могу показать, но буду переделывать.
Всем спасибо за помощь. Может кому пригодится моя поделка.
Umka, в финальном коде ошибка. в 11 и 43 строке измените входной порт на новый.
Да, точно. Но оно почему-то и так работает. Чудо!
https://www.youtube.com/watch?v=F2Bw54-joQc
Мои поздравления!
Продолжение https://www.youtube.com/watch?v=t1mEsbQYO4Q
И... появилась неприятная фигня. На средних уровнях яркости периодически начинается хаотичное мерцание лампы. Будто помеха какая мешает. Это не ресет, уровень диммирования в целом держит. По ресету сбрасывает. Это не в канале детектора ноля. Осциллограмма ровная. Либо что-то с таймером, либо в железе и монтаже проблема.
Вот видео заболевания https://www.youtube.com/watch?v=07iGUiV968A
По наблюдениям заметил что мигание начинается после работы железки на максимальной яркости или близкой к максимальной и перезагрузкой не лечится.
Добрые други! У кого есть двухлучевой осциллоскоп дома или на работе? Я могу готовую железку - диммер выслать безвозмездно (то есть даром!) для поимки глюка защелкивания симистора. Такое случается когда управляющий импульс на затворе симистора приходит не в нужный момент и симистор не закрывается. Иногда ровно светит все, а иногда появляется мерцание на средней яркости. Без двух лучей не отловить.
https://goo.gl/photos/s94cSRHdN7XR7X2X9 вот он красвевчик. Управляется по I2C по адресу 41 с чего угодно.
У меня такая ерунда "иногда появляется мерцание на средней яркости" была, когда импульс открытия симмистора переваливал за 0.
В вашей программе он может перевалить, когда пропишется неправильное значение в регистр таймера.
Попробуйте окружить строчку OCR1A = map(i2cValue, 0, 254, 125, 2); запрещением - разрешением прерывания. Хорошобы таймер перед этим останавливать.
Я бы даже сказал что некорректно менять OCR1 на ходу. Его можно рассчитать в лупе, положить в временную переменную, а в пррывании детектора ноля переложить в OCR1 .
Боюсь такое не осилю. Не силен я в таймерах и прерываниях настолько. Как это лучше сделать?
Как-то так:
Спасибо. Заменил DigitalWritы на запись в порты. Скетч стал компактнее. Диммер пока диммит ровно, будем испытывать.
тогда уж
Оно конечно для байта излишне, но в общем случае полезно.
Вроде лучше, но всеравно подмигивает на 70-80% яркости.
А с запретом прерывания в лупе не пашет. Пытается лампу зажечь на уровне тления едва заметного и только.
Библиотека i2c перехватывает прерывания. Приоретета прерывний в тинке нет. Поэтому иногда моргает. Возьмите полностью софтовую библиотеку i2c. Напрмер из темы про тинку тринадцатую.
Киньте ссылку плиз.
Во первых. С запретом прерываний очень даже понятно. МАР функция самая затратная по времени и соответственно программа живёт почти всё время в режиме запрета прерываний.
Во вторых. Аппаратного i2c в этом процессоре нет. Софтверного слейва без прерываний я нагуглить не смог. Может быть кто то из товарищей поможет? А с прерываниями, из за отсутствия приорететов, решить проблему со слейвом i2c управления димером без глюков на этом процессоре невозможно. Нужен или аппаратный i2c или вообще процессор, где прерывание можно прервать прерыванием.
Можно попробывать обойтись вообще без прерываний и таймеров. В цикле с помощью millis просто дёргать ноги.
Umka, восстановите первоначальное подключение и программу с ардуино.сс. Проверьте, нет ли глюков. Если нет, то поменяйте ногу прерывания как в вашем скечте, снова проверьте. Итд. Методом исключения всегда можно вычислить после чего появился глюк. Если конечно его не было в исходном коде.
Был в исходном тексте к сожалению.
Dimax, у Вас есть осциллограф? Может правда готовый блок выслать?
I2C тут практически аппаратный, http://www.atmel.com/images/atmel-2561-using-the-usi-module-as-a-i2c-master_ap-note_avr310.pdf
Да, я в курсе, сам использую. Вот только слейв без прерываний не работает. Софтверно только мастера в доступе.
Ну, значит будем бороть. Оно не всегда и не на всех уровнях проявляется. Для инкубаторов конечно и так сойдет, но хочется до ума довести. Кстати, а многоканального диммера на Меге никому не попадалос? Ну кроме киберлибовского разумеется.
Dimax, у Вас есть осциллограф? Может правда готовый блок выслать?
Да в принципе у меня всё это есть, только время нужно. Сегодня совсем никак, а завтра ближе к вечеру наверное получится посмотреть :)
Ну, тогда подарок от Санты под елочку :)
Umka, нужного оптрона для симистора не оказалось, так что повторить полностью эксперимент не вышло. Впрочем это и не нужно, я залил в тиньку скетч того товарища с аруино.сс, в качестве детектора ноля подал 50 герц с генератора, и очень чётко наблюдается глюк. Это даже не совсем глюк, а просто косяк автора, счетный регистр становится меньше чем регистр сравнения, фактически шкала регулировки должна кончаться чуть раньше, чем позволяет сама регулировка. Простейшее решение -ограничить значение OCR1A, это и будет фактический конец шкалы. Но по большому счёту скетч -говно, и лучше бы просто найти другой, написанный более опытным человеком.
Собрал схему, посмотрел осцилографом. Сигнал синхронизации идёт существенно позже пересечения нуля напряжения. Что бы получить нормальное регулирование во всём диапазоне надо перепрыгивать через период - задержка должна быть длиннее чем период следования импульсов синхронизации. Или смирится с тем, что полную мощность не получить. И иметь глюки рядом с максимально возможной мощностью или переделать схему синхронизации.
Нашёл схему дактчика пересечения нуля. http://www.fritzler-avr.de/HP/tipps/dimm.php
nik182, зацепила тема? :-) Интересно бы посмотреть в протеусе, что за сигнал на выходе оптрона будет, что-то сомнения берут что лучше чем просто два резистора. Но набивать лень. В идеале нужен прямоугольник с фронтом восхождения точно по нулю.
Я смотрю и скетчик там алгоритмически точно такой-же как в этом топике. По-моему лучше отказаться от прерывания OVF, а в прерывание COMPA вставить всё что было в прерывании OVF, только перед этим вставить паузу микросекунд на 10. Что б симистр успел открыться. Эта мера должна предотвратить глюки.
Давно хотел маленький на стол для всяких нужд. Сейчас мучаю tiny25. Я ей прямо на компаратор 220 подаю. Импульс синхронизации отличный. Тик в тик с пересечением нуля. Эта схема тоже для того что бы тик в тик синхроимпульс выдавать.
Там эта, можно 817 оптрон на вход детектора, с диодным мостом, 817-х много, в любой зарядке для мобилки есть.
Вот бы на Меге8 4-канальный диммер раскачать!