У нас только 2 внешних прерывания, а баба Яга - против !!!

gregoryl
Offline
Зарегистрирован: 09.09.2013

Все написанное ниже,
лишь мой путь в мире прерываний,
все это можно легко, сделать самому
покопавшись, час другой в документации

 

Заранее прошу прощения, если изложение сумбурно. В моем первом проекте «управляемая розетка» мне потребовалось сделать мониторинг наличия напряжения, как на входе, так и на выходе модуля. В модуле было 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

Michal
Michal аватар
Offline
Зарегистрирован: 26.04.2013

зачем на силовых выходах вам нужен детектор 0? Понимаю, если 3Ф сеть, то по каждой фазе ловим переход, а если 1Ф, то все управление нагрузкой пляшет от одного детектора.

gregoryl
Offline
Зарегистрирован: 09.09.2013

Michal пишет:

зачем на силовых выходах вам нужен детектор 0? Понимаю, если 3Ф сеть, то по каждой фазе ловим переход, а если 1Ф, то все управление нагрузкой пляшет от одного детектора.

Мой модуль не управлят нагрузкой. Это 3 реле которые включают или выключают 3 обычных розетки в стенке. А для мониторинга наличия или отсутствия переменного тока я просто использую схему детектора перехода через ноль. Поэтому приходиться рассматривать все линии как отдельные не зависимые.

Можно конечно добавить конденсаторы и без заморочек смотреть наличие или отсутсвие сигнала на ноге и тогда можно использовать даже сдвиговые регистры, но это не только удорожает сам модуль, но и увеличивает его размер :-)

Возможно при создании модуля на 6 или 10 розеток я бы так и сделал, но в данном случае ног хватает а значит все что можно решить в софте, будем решать в софте.

Michal
Michal аватар
Offline
Зарегистрирован: 26.04.2013

тю... так если это просто реле, используйте в развзяке оптосимистор с детектом нуля(moc3081 или аналог), как это делается в безконтактных релюхах. Отпадет всякая нужда в отслеживании перехода через 0.

gregoryl
Offline
Зарегистрирован: 09.09.2013

:-) да не ноль я отслеживаю... я отслеживаю наличие напряжения :-) все это не имеет отношение к схеме включения или выключения самих реле. К примеру это дает возможность узнать, что у вас сдохла релюха, вы ее включили а напруги на выходе нет ну или наоборот вы ее выключили а напруга все равно осталась....

axill
Offline
Зарегистрирован: 05.09.2011

если я правильно понял суть поста - вы делитесь своим опытом использование прерываний PCINT и протестуете против того, что в стандартной библиотеке ардуино поддержаны только прерывания INT? :)

или ошибаюсь?

gregoryl пишет:

По сути это прерывания, которые срабатывают когда изменяется любой бит PORTB, PORTC, PORTD.

если быть правильным, то срабатывают они не по любому пину, а только по тому пину который сконфигурирован в управляющем регистре

gregoryl пишет:
Вместо заключения. Если кому-то поможет все это будет приятно, если кто-то знает другой, более эффективный способ расширения внешних прерываний будет интересно посмотреть и пощупать. Если есть, вопросы задавайте. 

Скачать можно тут https://dl.dropboxusercontent.com/u/102291472/InterruptsExternalExtender.rar

все таки это не расширение внешних прерываний (они в железе МК и никуда не девались), а снятие некоторых ограничений стандартных библиотек, так будет корректнее

вполне нормальный, стандартный способ применения. С одним только "но", 1. что PCINT в отличии от INT не настраиваются на отдельный пин, а настраиваются на группу пинов 2. PCINT срабатывает на любое изменения пина, а INT может быть настроен на несколько вариантов изменений. Поэтому при обработке PCINT надо "фильтровать" события самому

Что касается вашей задачи, то кроме внешних прерываний можно детектор нуля подключить к входам тактовых частот таймеров и настроить таймеры на счет от внешней частоты. Тогда наличие напряжения будет определяться по наличию счета. Так же можно детектор нуля отфильтровать RC цепочкой (получив постоянное напряжение) и замерять аналоговыми входами в loop() без прерываний

gregoryl
Offline
Зарегистрирован: 09.09.2013

axill пишет:

если я правильно понял суть поста - вы делитесь своим опытом использование прерываний PCINT и протестуете против того, что в стандартной библиотеке ардуино поддержаны только прерывания INT? :)

или ошибаюсь?

Совершенно верно

axill пишет:

если быть правильным, то срабатывают они не по любому пину, а только по тому пину который сконфигурирован в управляющем регистре

Ну конечно.

axill пишет:

все таки это не расширение внешних прерываний (они в железе МК и никуда не девались), а снятие некоторых ограничений стандартных библиотек, так будет корректнее 
вполне нормальный, стандартный способ применения. С одним только "но", 1. что PCINT в отличии от INT не настраиваются на отдельный пин, а настраиваются на группу пинов 2. PCINT срабатывает на любое изменения пина, а INT может быть настроен на несколько вариантов изменений. Поэтому при обработке PCINT надо "фильтровать" события самому

 
Ну так я там приложил библиотеку которая это все за нас делает :-)
 

axill пишет:

Что касается вашей задачи, то кроме внешних прерываний можно детектор нуля подключить к входам тактовых частот таймеров и настроить таймеры на счет от внешней частоты. Тогда наличие напряжения будет определяться по наличию счета. Так же можно детектор нуля отфильтровать RC цепочкой (получив постоянное напряжение) и замерять аналоговыми входами в loop() без прерываний

 
Солгласен, про цепочку RC  я думал с самого начала, но почему-то вдруг захотелось все что можно повесить на софт и минимизировать количество компонетов, в итоге в конечной схеме остался только оптрон и резистор, диодный мост я выкинул.