У нас только 2 внешних прерывания, а баба Яга - против !!!
- Войдите на сайт для отправки комментариев
Все написанное ниже,
лишь мой путь в мире прерываний,
все это можно легко, сделать самому
покопавшись, час другой в документации
Заранее прошу прощения, если изложение сумбурно. В моем первом проекте «управляемая розетка» мне потребовалось сделать мониторинг наличия напряжения, как на входе, так и на выходе модуля. В модуле было 3 коммутируемых линии, это значит, что мониторинг должен будет работать для всех 3 линий + 1 линия вход, итого 4 линии. Схематически решение очень простое и было подсказано уважаемым товарищем axill в его проекте «SPI модуль для управления мощностью сетевой нагрузки». Я просто скопировал схему детектора нуля ( это та часть которая с диодным мостом и оптроном ). Замечу, что одно из самых неоспоримых достоинств этого решения это гальваническая развязка. Теперь можно обратиться к программной части.
Посмотрим, что же твориться на выходах МК, для этого просто цепляем logic analyzer к нашим лапкам и в путь.
Итак, мы видим, что у нас при переходе через ноль на цифровом выходе появляется единичка. Вообще надо отметить, что logic analyzer просто не заменим, если вы собрались работать с чем-то большим, чем игры с LED на 13 пине. На картинке сверху вниз отображены следующие параметры:
* Активность обработчика внешних прерываний
* Сигналы детекторов от 3 коммутируемых линий
* Детектор основного входа модуля
* Аналоговый сигнал, который мы детектируем
Я использовал Arduino PRO Mini. Конечно, можно было бы просто организовать полинг на 4 ножках, ибо это дешево и сердито. Но у этого пути есть недостаток. Основная проблема этого подхода в том, что нам придется реализовывать весь остальной функционал, с учетом того, что у нас имеется почти real-time задача, а это сделать все сложнее чем больший функционал мы будем добавлять в модуль. Поэтому мы не будем искать легких путей, а обратимся к прерываниям, вдруг там жизнь будет более легкой и приятной.
На этом пути нас ожидает другая засада. Официально в документации написано, что у нас есть только 2 внешних прерывания, а у нас целых 4 импульсных сигнала. Немного покапав документацию, находим 3 очень забавных прерывания
* PCINT0_vect
* PCINT1_vect
* PCINT2_vect
По сути это прерывания, которые срабатывают когда изменяется любой бит PORTB, PORTC, PORTD. Это как раз то, что нам и надо. Быстренько соберем платку на макетке и подключим 4 наших детектора к ножкам 4,5,6,7 нашего МК. Путь написания кода и сам код я, пожалуй, пропущу, ровно как и разборку того как работают прерывания, это не столь интересно, тому кто просто хочет это использовать, а если кому интересно посмотреть, как оно устроено, то они могу просто скачать и посмотреть код, по прерываниям смотрим 12 главу даташита.
Рассмотрим цифровую часть более подробно. Первый вопрос который может прийти в голову, «А почему на всех 4 детекторах появлений импульса происходит в разное время и сам импульс различной длительности, если исходный сигнал только один ?» Тут все очень просто, это конструктивная особенность и связано это с погрешностями электронных компонентов использованных при создании детекторов.
Рассмотрим вкратце алгоритм работы:
* Перехватываем нужное прерывание
* Смотрим, какой бит изменился
* Вызываем нужную функцию, которая обновляет соответствующий детектор
* Запоминаем текущее состояние битов для последующей работы.
Теперь о том, как всем этим пользоваться ( код урезан и приведен только в качестве примера )
#include <InterruptsExternalExtender.h> // // User defined external interrup class // // This class should implement method // - void Throw( int n, bool status ) // // Notice !!! This library include virtual interface IInterrupt. // if you want to use different classes with one extender, // you can inherit your classes from this interface // and implement interface in your classes // class ExternalInterruptDemo { public: volatile T _Marker; public: ExternalInterruptDemo() { ; } public: void Throw( int n, bool status ) { this->_Marker = millis(); } }; // // Declaration new external interrup extender on PORTD // Interrupts::External::Extender<ExternalInterruptDemo> iExtender( PCIE2 ); // // Interrupt Handling // ISR( PCINT2_vect ) { __SET_PERFORMANCE_BIT_ON__; iExtender.Handle(); __SET_PERFORMANCE_BIT_OFF__; }; // // // void setup() { pinMode( 13, OUTPUT ); iExtender.Attach( new ExternalInterruptDemo(), 0, true, true ); // Pin 8 iExtender.Attach( new ExternalInterruptDemo(), 1, true, false ); // Pin 9 iExtender.Attach( new ExternalInterruptDemo(), 2, false, true ); // Pin 10 iExtender.Attach( new ExternalInterruptDemo(), 3, true, true ); // Pin 11 iExtender.Enable(); } // // // void loop() { }
Несколько слов о быстродействии. В обработчик прерывания, я вставил включение и выключение пина А0. И в итоге получил некоторое представление и накладных расходах. Итого вы можете видеть это на картинке, накладные расходы составили примерно 28,5мкс. Много это или мало, сказать трудно, но меня это устраивает.
Вместо заключения. Если кому-то поможет все это будет приятно, если кто-то знает другой, более эффективный способ расширения внешних прерываний будет интересно посмотреть и пощупать. Если есть, вопросы задавайте.
Скачать можно тут https://dl.dropboxusercontent.com/u/102291472/InterruptsExternalExtender.rar
зачем на силовых выходах вам нужен детектор 0? Понимаю, если 3Ф сеть, то по каждой фазе ловим переход, а если 1Ф, то все управление нагрузкой пляшет от одного детектора.
зачем на силовых выходах вам нужен детектор 0? Понимаю, если 3Ф сеть, то по каждой фазе ловим переход, а если 1Ф, то все управление нагрузкой пляшет от одного детектора.
Мой модуль не управлят нагрузкой. Это 3 реле которые включают или выключают 3 обычных розетки в стенке. А для мониторинга наличия или отсутствия переменного тока я просто использую схему детектора перехода через ноль. Поэтому приходиться рассматривать все линии как отдельные не зависимые.
Можно конечно добавить конденсаторы и без заморочек смотреть наличие или отсутсвие сигнала на ноге и тогда можно использовать даже сдвиговые регистры, но это не только удорожает сам модуль, но и увеличивает его размер :-)
Возможно при создании модуля на 6 или 10 розеток я бы так и сделал, но в данном случае ног хватает а значит все что можно решить в софте, будем решать в софте.
тю... так если это просто реле, используйте в развзяке оптосимистор с детектом нуля(moc3081 или аналог), как это делается в безконтактных релюхах. Отпадет всякая нужда в отслеживании перехода через 0.
:-) да не ноль я отслеживаю... я отслеживаю наличие напряжения :-) все это не имеет отношение к схеме включения или выключения самих реле. К примеру это дает возможность узнать, что у вас сдохла релюха, вы ее включили а напруги на выходе нет ну или наоборот вы ее выключили а напруга все равно осталась....
если я правильно понял суть поста - вы делитесь своим опытом использование прерываний PCINT и протестуете против того, что в стандартной библиотеке ардуино поддержаны только прерывания INT? :)
или ошибаюсь?
По сути это прерывания, которые срабатывают когда изменяется любой бит PORTB, PORTC, PORTD.
если быть правильным, то срабатывают они не по любому пину, а только по тому пину который сконфигурирован в управляющем регистре
Скачать можно тут https://dl.dropboxusercontent.com/u/102291472/InterruptsExternalExtender.rar
все таки это не расширение внешних прерываний (они в железе МК и никуда не девались), а снятие некоторых ограничений стандартных библиотек, так будет корректнее
вполне нормальный, стандартный способ применения. С одним только "но", 1. что PCINT в отличии от INT не настраиваются на отдельный пин, а настраиваются на группу пинов 2. PCINT срабатывает на любое изменения пина, а INT может быть настроен на несколько вариантов изменений. Поэтому при обработке PCINT надо "фильтровать" события самому
Что касается вашей задачи, то кроме внешних прерываний можно детектор нуля подключить к входам тактовых частот таймеров и настроить таймеры на счет от внешней частоты. Тогда наличие напряжения будет определяться по наличию счета. Так же можно детектор нуля отфильтровать RC цепочкой (получив постоянное напряжение) и замерять аналоговыми входами в loop() без прерываний
если я правильно понял суть поста - вы делитесь своим опытом использование прерываний PCINT и протестуете против того, что в стандартной библиотеке ардуино поддержаны только прерывания INT? :)
или ошибаюсь?
Совершенно верно
Ну конечно.
Что касается вашей задачи, то кроме внешних прерываний можно детектор нуля подключить к входам тактовых частот таймеров и настроить таймеры на счет от внешней частоты. Тогда наличие напряжения будет определяться по наличию счета. Так же можно детектор нуля отфильтровать RC цепочкой (получив постоянное напряжение) и замерять аналоговыми входами в loop() без прерываний