Не касаясь других вопросов, по поводу "а на какие ноги можно навесить прерывание" замечу, что схема распиновки Arduino содержит весь список функций, доступных на той или иной ноге:
Пользоваться схемой просто: стоит у ноги в скобочках функция PCINTx, значит можно, не стоит - низззя.
UPD: ATMega328 по-большому отличается от ATMega168 лишь размером памяти.
1) ...или данное прерывание относится только к ногам этого порта и не более?
Именно так. PCINT0 - порт B, PCINT1 - порт С, PCINT2 - порт D.
2) В чем отличие PCINTx от INTx ?
Прерывание INTx можно настроить по: переднему фронту импульса, заднему фронту импульса, по переднему и заднему одновременно (любое изменение уровня) и на низкий уровень, это когда на ноге низкий уровень все время будет выполняться обработчик INTx. А прерывание PCINTx срабатывает только на любое изменение уровня и изменить это никак нельзя.
3) ...будет ли пропуск этого события?
Если во время выполнения обработчика какого-либо прерывания сгенерилось какоенить другое прерывание, то после выполнения текущего обработчика, это другое прерывание, будет выполнено, но только один раз, вне зависимости от того сколько раз оно сгенерилось. Никакого буфера для отслеживания нет. Есть флаг готовности этого прерывания (а он только один), который показывает что оно произошло и что надо надо срочно прыгнуть на обработчик этого прерывания. Более подробно как это работает тут, раздел прерывания easyelectronics.ru/avr-uchebnyj-kurs-podprogrammy-i-preryvaniya.html
4) ...Будет ли корректно так раскидать ноги..
Да так можно сделать, но, что-бы INT0/INT1 не накладывались на PCINT2, надо в регистре PCMSK2 назначить только одну ногу (PCINT23 - PD7 на МК) которая будет вызывать обработчик прерывания ISR(PCINT2_vect){}. Это можно сделать вот так PCMSK2 = 128.
>> Или можно вообще использовать все ноги порта D...
Одинадцатую строчку (в сетапе) можно было вообще не писать. Т.к. прерывание PCINT1 не разрешено не важно как настроен регистр PCMSK1, выполяться оно не будет. Но на правильность выполнения это, конечно, не повлияет.
Вроде работает все как надо, НО при подаче сигнала на ноги ардуина д2/д3/д7/д8 статус флагов в переменных обработчиков меняется на 1 моментально, а вот при отключении сигнала флаг становится нулевым довольно долго.
Каковы могут быть причины? Возможно надо принудительно внутри обработчиков опускать флаги прерываний, но они и сами через время должны обнуляться... :(
без ущерба для конечного результата можно сократить до
PCMSK2 |= (1<<PCINT23);
ибо, во-первых, выражение вида (0<<PCINT16) это то же самое, что просто 0, и, во-вторых, в выражениях побитового ИЛИ (|) 0 никак на результат не влияет.
Цитата:
Нужно ли еще в регистре SREG взвести I-бит чтобы разрешить прерывания глобально, или этого не надо и он по дефолту = 1?
"Нужно/не нужно" где, в какой момент? Однозначного ответа на этот вопрос дать нельзя - на то этот флаг и доступен, чтобы в критических секциях программы сбрасывать его, а затем снова устанавливать.
Ну а по дефолту он = 0. Если можно верить дейташитам от Атмеля.
По дефолту он скорее = 1, т.к. в коде он нигде не включается, а прерывания работают.
Проблема оказалась несколько иной природы: судя по этой статье проблема связана с "дребезгом контактов".
И действительно, при замыкании входов на +5В даже пальцами (токи достаточно малы, но достаточны) диод на 13 ноге загорается, но гаснет весьма лениво. А если же ногу закоротить на землю (или между собой) пальцем, то диод отрабатывает довольно адекватно в обе стороны.
Возможно изменение триггера с любого изменения логического уровня на стабильный лоу уровень решит эту проблему, а может и нет.
Правда в этом случае придется уже анализировать внутри PCINTх-обработчиков состояние ноги, но все же задержки... :(
По поводу сокращений я специально указал на то, что это для наглядности изложения проблемы. :)
По дефолту он скорее = 1, т.к. в коде он нигде не включается, а прерывания работают.
"-Видишь суслика?
-Нет.
-И я не вижу. А он там есть..."
По умолчанию таки 0:
Просто скрытая от глаз простого пользователя Arduino обертка функции setup устанавливает этот флаг, а также делает некоторые другие необходимые подготовительные операции.
Monster пишет:
По поводу сокращений я специально указал на то, что это для наглядности изложения проблемы. :)
В полном соответствии с афоризмом "Язык дан человеку для того, чтобы скрывать свои мысли" ;)
Пытаюсь INT0 активировать при переходе с 0 на 1. Пин2.
Как вы думаете - для чего служит строка 20 вашего скетча? Правильно - для выбора режима работы прерывания INT0 (кстати и INT1 тоже). После этого я открываю даташит на ATMega328 (у вас этот микроконтроллер?), нахожу в нем главу 12 (External Interrupts), нахожу там табличку 12-2 (Interrupt 0 Sense Control). Ну и выясняю, что комбинация ICS01=0 & ICS00=1 задает режим "Any logical change on INT0 generates an interrupt request.", то бишь - "прерывание при любом изменении логического уровня"
Цитата:
Но при отсоединении пина от земли прерывание всеравно вызывается. Не понимаю.
PS: "переход с 0 на 1" это "The rising edge of INT0 generates an interrupt request.". Режим задаваемый комбинацией ICS01=1 & ICS00=1. Все та же таблица 12-2. Все того же даташита.
Всем привет. Можно понтять темку? я пока только учусь :) потому не все из данной темы осилил.
прошу подсказать туда я или не туда :)
сейчас использую ардуину нано и ее одно прерывание для фиксации срабатывания кнопки (например концевик двери). Считать внутри loop() не подходит, та как раз в 5мин я отправляю результаты счетчика на сервер, в это время отследить нажатие в loop() не реально, только использовать прерывание, сейчас использую его в режиме FALLING. Так вот задача инкрементить(считать) одной ардуинкой около 5-10 счетчиков(нажатий кнопок),частота нажатий каждой не чаще чем 1 в 5с.
Так как в одном корпусе находится и GSM модуль для GPRS соединения, а плодить в этом корпусе по ардуине на 2 счетчика(по 2 прерывания) не совсем красивый вариант. Использовать arduino zero или mega где больше прерываний казалось бы решит мою задачу, но они дороже и больше по размеру. Возможно есть более грамотное решение?
ISR (PCINT0_vect) // handle pin change interrupt for D8 to D13 here
2
{
3
digitalWrite(13,digitalRead(8) and digitalRead(9));
4
}
верно ли я понимаю из данного мануала (http://playground.arduino.cc/Main/PinChangeInterrupt) что когда на одном из цифровых пинов от 8го до13го появиться логическая единица, то сработает прерывание и код остановиться? Далее мне нужно пройтись по этим пинам и проверить у кого еденица, в моем случае - это какой счетчик инкрементить.
Не совсем. Когда на одном из ваших пинов появится изменение состояния код не остановится, а произойдет передача управления на обработчик прерывания PCINT0. Далее, какая из ног "сработала" можно решить чтением всего порта сразу и операцией XOR с сохраненным предыдущим значением. В 1 остануться только те биты, которые изменились. Пробегая их в цикле (сдвигая например вправо делением на 2) и проверяя исходный бит можно получить ответ "куда сработало" в ноль или 1.
Примерно так сделано у меня в "общем обработчике" PCINTx:
temp = temp >> 1; // готовим его пред. состояние (raising|failing)
268
}while(reg);
269
}
270
271
// And only this wa are defining blocking semaphore
272
// И только теперь определяем блокирующую защелку
273
#ifndef _PCINT_H_
274
#define _PCINT_H_ 1
275
#endif
276
277
#endif // PCINT blocking for twice parsing
Смотрите строки 250..271. Генератор процедуры ISR() собственно обработчик прерывания. В нем выделяется какая нога сработала и вызывается та подпрограмма, которая привязана к этой ноге с передачей её текущего значения ноги прерывания. Она уже внутри себя может посмотреть какое изменение (0->1 или 1->0) и что-то сделать по этому поводу.
Вот, как-то так. Тут файл-темплейт для генерации функций по мере надобности для разных случаев и плат.
Но по факту лучше использовать 2 External + 3 PinChange прерывания, как описано выше в теме, что бы не тратить ресурс на определение ноги инициатора, верно? Или данный вариант необходим только при высоких частотах, что бы не пропустить срабатывания? Если в моем случае частота срабатывания по каждому прерыванию будет не чаще чем 1с то можно смело использовать вариант с определением ног?
А можно, для альтернативно одаренных, поподробнее? ReadPortD изначально равен 0. Условие не выполняется. Пока не сработает прерывание и не запишет в эту переменную значение порта D. Если при замыкании PD6 на землю срабатывает прерывание, то условие отрабатывается как истинное - и это правильно и логично.
Но почему срабатывает прерывание pcint 21, когда оно отключено маской PCMSK2?
Если у вас включен PCINT только на одном выводе порта, вы можете предположить, что прерывание PCINT в PortX связано с единственным, которое вы включили.Но, если вы используете два или более на одном и том же порту, вам необходимо прочитать регистр, чтобы определить, какое из них вызвано.
Опять непонятно. Я точно так же в прерывании записываю состояние всех пинов порта D и в основном цикле вывожу их. Оттуда и вижу, что идет срабатывание на PD6 (на выводе четко виден 6й бит Pind: 10100111), который отключен маской PCMSK2 (которую я тоже проверил выводом в конце setup 10100000).
Зачем лишняя переменная StatePCINT2 если значение ReadPortD может изменится только при вызове прерывания?
И какое отношение это имеет к вызову прерывания по падению PD6?
Как честный человек, я проверил предлагаемый код. И очень удивился - нет ложных срабатываний на PD6!
Не касаясь других вопросов, по поводу "а на какие ноги можно навесить прерывание" замечу, что схема распиновки Arduino содержит весь список функций, доступных на той или иной ноге:
Пользоваться схемой просто: стоит у ноги в скобочках функция PCINTx, значит можно, не стоит - низззя.
UPD: ATMega328 по-большому отличается от ATMega168 лишь размером памяти.
Итого, более 5 (2+3) прерываний без анализа инициатора на Нано иметь невозможно?
1) ...или данное прерывание относится только к ногам этого порта и не более?
Именно так. PCINT0 - порт B, PCINT1 - порт С, PCINT2 - порт D.
2) В чем отличие PCINTx от INTx ?
Прерывание INTx можно настроить по: переднему фронту импульса, заднему фронту импульса, по переднему и заднему одновременно (любое изменение уровня) и на низкий уровень, это когда на ноге низкий уровень все время будет выполняться обработчик INTx. А прерывание PCINTx срабатывает только на любое изменение уровня и изменить это никак нельзя.
3) ...будет ли пропуск этого события?
Если во время выполнения обработчика какого-либо прерывания сгенерилось какоенить другое прерывание, то после выполнения текущего обработчика, это другое прерывание, будет выполнено, но только один раз, вне зависимости от того сколько раз оно сгенерилось. Никакого буфера для отслеживания нет. Есть флаг готовности этого прерывания (а он только один), который показывает что оно произошло и что надо надо срочно прыгнуть на обработчик этого прерывания. Более подробно как это работает тут, раздел прерывания easyelectronics.ru/avr-uchebnyj-kurs-podprogrammy-i-preryvaniya.html
4) ...Будет ли корректно так раскидать ноги..
Да так можно сделать, но, что-бы INT0/INT1 не накладывались на PCINT2, надо в регистре PCMSK2 назначить только одну ногу (PCINT23 - PD7 на МК) которая будет вызывать обработчик прерывания ISR(PCINT2_vect){}. Это можно сделать вот так PCMSK2 = 128.
>> Или можно вообще использовать все ноги порта D...
Нет, нельзя.
Итого, более 5 (2+3) прерываний без анализа инициатора на Нано иметь невозможно?
В яблочко!
Настройка всех 4 прерываний на любое изменение логического уровня (чтобы условия для входов были одинаковы):
01
void
setup()
02
{
03
// разрешение прерываний INT0 и INT1
04
EIMSK = (1<<INT0) | (1<<INT1);
05
// настройка срабатывания прерываний на любому изменению
06
EICRA = (0<<ISC11) | (1<<ISC10) | (0<<ISC01) | (1<<ISC00);
07
// разрешение прерываний с портов B (PCINT[7:0]) и D (PCINT[23:16]), и запрет с порта C (PCINT[14:8])
08
PCICR |= (1<<PCIE2) | (0<<PCIE1) | (1<<PCIE0);
09
// маскирование всех ног, кроме PB0 и PD7 - по одной на PCINT0 и PCINT2
10
PCMSK0 |= (0<<PCINT7) | (0<<PCINT6) | (0<<PCINT5) | (0<<PCINT4) | (0<<PCINT3) | (0<<PCINT2) | (0<<PCINT1) | (1<<PCINT0);
11
PCMSK1 |= (0<<PCINT14) | (0<<PCINT13) | (0<<PCINT12) | (0<<PCINT11) | (0<<PCINT10) | (0<<PCINT9) | (0<<PCINT8);
12
PCMSK2 |= (1<<PCINT23) | (0<<PCINT22) | (0<<PCINT21) | (0<<PCINT20) | (0<<PCINT19) | (0<<PCINT18) | (0<<PCINT17) | (0<<PCINT16);
13
}
Ну и сами обработчики:
01
// обработчик прерывания INT0
02
ISR(INT0_vect)
03
{
04
PassiveLeftIntFlag = !PassiveLeftIntFlag;
05
}
06
07
// обработчик прерывания INT1
08
ISR(INT1_vect)
09
{
10
PassiveRightIntFlag = !PassiveRightIntFlag;
11
}
12
13
// обработчик прерывания PCINT2
14
ISR(PCINT2_vect)
15
{
16
ActiveLeftIntFlag = !ActiveLeftIntFlag;
17
}
18
19
// обработчик прерывания PCINT0
20
ISR(PCINT0_vect)
21
{
22
ActiveRightIntFlag = !ActiveRightIntFlag;
23
}
Теперь всё верно? :)
П.С. Избыточность в масках - для наглядности.
>> Теперь всё верно? :)
Да, все так.
Одинадцатую строчку (в сетапе) можно было вообще не писать. Т.к. прерывание PCINT1 не разрешено не важно как настроен регистр PCMSK1, выполяться оно не будет. Но на правильность выполнения это, конечно, не повлияет.
Вроде работает все как надо, НО при подаче сигнала на ноги ардуина д2/д3/д7/д8 статус флагов в переменных обработчиков меняется на 1 моментально, а вот при отключении сигнала флаг становится нулевым довольно долго.
Каковы могут быть причины? Возможно надо принудительно внутри обработчиков опускать флаги прерываний, но они и сами через время должны обнуляться... :(
>> а вот при отключении сигнала...
Это что значит? Переход с высокого уровня на низкий?
>> Возможно надо принудительно внутри обработчиков опускать флаги прерываний...
Флаг прерывания автоматически сбрасывантся в 0 как только начинает выполняться обработчик этого прерывания.
Весь скетч:
01
// флаги прерываний
02
volatile
int
PassiveLeftIntFlag = LOW;
03
volatile
int
PassiveRightIntFlag = LOW;
04
volatile
int
ActiveLeftIntFlag = LOW;
05
volatile
int
ActiveRightIntFlag = LOW;
06
07
volatile boolean x=
false
;
08
09
// инициализация
10
void
setup()
11
{
12
// разрешение прерываний INT0 и INT1
13
EIMSK = (1<<INT0) | (1<<INT1);
14
// настройка срабатывания прерываний на любому изменению
15
EICRA = (0<<ISC11) | (1<<ISC10) | (0<<ISC01) | (1<<ISC00);
16
// разрешение прерываний с портов B (PCINT[7:0]) и D (PCINT[23:16]), и запрет с порта C (PCINT[14:8])
17
PCICR |= (1<<PCIE2) | (0<<PCIE1) | (1<<PCIE0);
18
// маскирование всех ног, кроме PB0 и PD7 - по одной на PCINT0 и PCINT2
19
PCMSK0 |= (0<<PCINT7) | (0<<PCINT6) | (0<<PCINT5) | (0<<PCINT4) | (0<<PCINT3) | (0<<PCINT2) | (0<<PCINT1) | (1<<PCINT0);
20
PCMSK1 |= (0<<PCINT14) | (0<<PCINT13) | (0<<PCINT12) | (0<<PCINT11) | (0<<PCINT10) | (0<<PCINT9) | (0<<PCINT8);
21
PCMSK2 |= (1<<PCINT23) | (0<<PCINT22) | (0<<PCINT21) | (0<<PCINT20) | (0<<PCINT19) | (0<<PCINT18) | (0<<PCINT17) | (0<<PCINT16);
22
23
// настройка таймера на период = 1с
24
TIMSK1=0x01;
// enabled global and timer overflow interrupt
25
TCCR1A = 0x00;
// normal operation page 148 (mode0)
26
TCNT1=0x0BDC;
// 16bit counter register
27
TCCR1B = 0x04;
// start timer / set clock
28
29
pinMode(13, OUTPUT);
30
digitalWrite(13,LOW);
31
}
32
33
// обработчик прерывания INT0
34
ISR(INT0_vect)
35
{
36
PassiveLeftIntFlag = !PassiveLeftIntFlag;
37
}
38
39
// обработчик прерывания INT1
40
ISR(INT1_vect)
41
{
42
PassiveRightIntFlag = !PassiveRightIntFlag;
43
}
44
45
// обработчик прерывания PCINT2
46
ISR(PCINT2_vect)
47
{
48
ActiveLeftIntFlag = !ActiveLeftIntFlag;
49
}
50
51
// обработчик прерывания PCINT0
52
ISR(PCINT0_vect)
53
{
54
ActiveRightIntFlag = !ActiveRightIntFlag;
55
}
56
57
ISR (TIMER1_OVF_vect)
58
{
59
TCNT1=0x0BDC;
// set initial value to remove time error (16bit counter register)
60
x=
true
;
61
}
62
63
void
loop()
64
{
65
if
(x==
true
)
66
{
67
digitalWrite(13,PassiveLeftIntFlag | PassiveRightIntFlag | ActiveLeftIntFlag | ActiveRightIntFlag);
68
x=
false
;
69
}
70
}
Нужно ли еще в регистре SREG взвести I-бит чтобы разрешить прерывания глобально, или этого не надо и он по дефолту = 1?
Эту (и ей подобные) инструкцию:
PCMSK2 |= (1<<PCINT23) | (0<<PCINT22) | (0<<PCINT21) | (0<<PCINT20) | (0<<PCINT19) | (0<<PCINT18) | (0<<PCINT17) | (0<<PCINT16);
без ущерба для конечного результата можно сократить до
PCMSK2 |= (1<<PCINT23);
ибо, во-первых, выражение вида (0<<PCINT16) это то же самое, что просто 0, и, во-вторых, в выражениях побитового ИЛИ (|) 0 никак на результат не влияет.
Нужно ли еще в регистре SREG взвести I-бит чтобы разрешить прерывания глобально, или этого не надо и он по дефолту = 1?
"Нужно/не нужно" где, в какой момент? Однозначного ответа на этот вопрос дать нельзя - на то этот флаг и доступен, чтобы в критических секциях программы сбрасывать его, а затем снова устанавливать.
Ну а по дефолту он = 0. Если можно верить дейташитам от Атмеля.
По дефолту он скорее = 1, т.к. в коде он нигде не включается, а прерывания работают.
Проблема оказалась несколько иной природы: судя по этой статье проблема связана с "дребезгом контактов".
И действительно, при замыкании входов на +5В даже пальцами (токи достаточно малы, но достаточны) диод на 13 ноге загорается, но гаснет весьма лениво. А если же ногу закоротить на землю (или между собой) пальцем, то диод отрабатывает довольно адекватно в обе стороны.
Возможно изменение триггера с любого изменения логического уровня на стабильный лоу уровень решит эту проблему, а может и нет.
Правда в этом случае придется уже анализировать внутри PCINTх-обработчиков состояние ноги, но все же задержки... :(
По поводу сокращений я специально указал на то, что это для наглядности изложения проблемы. :)
По дефолту он скорее = 1, т.к. в коде он нигде не включается, а прерывания работают.
"-Видишь суслика?
-Нет.
-И я не вижу. А он там есть..."
По умолчанию таки 0:
Просто скрытая от глаз простого пользователя Arduino обертка функции setup устанавливает этот флаг, а также делает некоторые другие необходимые подготовительные операции.
По поводу сокращений я специально указал на то, что это для наглядности изложения проблемы. :)
В полном соответствии с афоризмом "Язык дан человеку для того, чтобы скрывать свои мысли" ;)
Это все хорошо, но как решить задачу исключив дребезг?
Емкость на вход или есть еще варианты?
Прошу помощи по прерыванию.
Пытаюсь INT0 активировать при переходе с 0 на 1. Пин2.
Но при отсоединении пина от земли прерывание всеравно вызывается. Не понимаю.
01
#define LEDPIN 13 // Вывод светодиода
02
#define BTNPIN 2 // Вывод кнопки
03
04
volatile word count = 0, voda = 0;
05
06
//boolean clapan = 0;
07
//int count_vody=0, obor_in_litr=100;
08
//float litry=0;
09
10
ISR(INT0_vect)
11
{
12
count = 1000;
// Инициализировать счётчик
13
}
14
15
void
setup
(){
16
17
pinMode(LEDPIN, OUTPUT);
// Вывод светодиода в режим вывода
18
pinMode(2, INPUT);
// Вывод кнопки в режим ввода
19
EIMSK = (0<<INT0);
20
EICRA = (0<<ISC11) | (0<<ISC10) | (0<<ISC01) | (1<<ISC00);
21
EIMSK = (1<<INT0);
22
23
interrupts();
// Разрешить прерывания глобально
24
Serial
.begin (9600);
25
}
26
27
void
loop
(){
28
29
if
(count>0) {
30
digitalWrite(LEDPIN, HIGH);
31
count=count-1;
32
}
33
else
{
34
digitalWrite(LEDPIN, LOW);
35
}
36
Serial
.println (count);
37
delay (5);
38
}
Прошу помощи по прерыванию.
Пытаюсь INT0 активировать при переходе с 0 на 1. Пин2.
Как вы думаете - для чего служит строка 20 вашего скетча? Правильно - для выбора режима работы прерывания INT0 (кстати и INT1 тоже). После этого я открываю даташит на ATMega328 (у вас этот микроконтроллер?), нахожу в нем главу 12 (External Interrupts), нахожу там табличку 12-2 (Interrupt 0 Sense Control). Ну и выясняю, что комбинация ICS01=0 & ICS00=1 задает режим "Any logical change on INT0 generates an interrupt request.", то бишь - "прерывание при любом изменении логического уровня"
Но при отсоединении пина от земли прерывание всеравно вызывается. Не понимаю.
1
EICRA = (0<<ISC11) | (0<<ISC10) | (0<<ISC01) | (1<<ISC00);
Вам по-прежнему непонятно? ;)
PS: "переход с 0 на 1" это "The rising edge of INT0 generates an interrupt request.". Режим задаваемый комбинацией ICS01=1 & ICS00=1. Все та же таблица 12-2. Все того же даташита.
Во первых вы не правильно включаете прерывания. В 20 строке вы настроили прерывание INT0 на ЛЮБОЕ изменение уровня. Вот так надо:
1
20 EICRA=(1<<ISC01);
// настроили прерывание INT0 по переднему фронту
19 строку изменяем на:
1
19 digitalWrite(2, HIGH);
// подтягиваем пин 2 к +5в на всякий случай
И во вторых, в обработчике прерывания, в 12 строке, желательно добавить паузу, чтобы исключить дребезг контактов.
1
12 count=1000; delay(50);
В обработчике прерывания задержку?
Крайне не рекомендую этого делать и работать она корректно все равно не будет.
В обработчике прерывания задержку?
Крайне не рекомендую этого делать и работать она корректно все равно не будет.
Кстати да, не надо. с IAR-ом перепутал, там __delay_cycles в прерываниях нормально работает.
Спасибо. Буду пробовать.
Даташит пока не скачивал.
Второй день только ковыряюсь с ардуинкой.
Нужно контроллировать датчик холла+ сигналы с купюроприемника принимать+монетоприемник.
Еще нужно одно прерывание, блин. И как то все это дело считывать.
С помехами вообще засада.
Тогда можно было прерывания 0 и 1 настроить дуиновскими функциями.
Написал библиотеку которая добавляет Ардуине три дополнительных внешних прерывания, может кому пригодится.
Огромное спасибо за библиотеку.
Всем привет. Можно понтять темку? я пока только учусь :) потому не все из данной темы осилил.
прошу подсказать туда я или не туда :)
сейчас использую ардуину нано и ее одно прерывание для фиксации срабатывания кнопки (например концевик двери). Считать внутри loop() не подходит, та как раз в 5мин я отправляю результаты счетчика на сервер, в это время отследить нажатие в loop() не реально, только использовать прерывание, сейчас использую его в режиме FALLING. Так вот задача инкрементить(считать) одной ардуинкой около 5-10 счетчиков(нажатий кнопок),частота нажатий каждой не чаще чем 1 в 5с.
Так как в одном корпусе находится и GSM модуль для GPRS соединения, а плодить в этом корпусе по ардуине на 2 счетчика(по 2 прерывания) не совсем красивый вариант. Использовать arduino zero или mega где больше прерываний казалось бы решит мою задачу, но они дороже и больше по размеру. Возможно есть более грамотное решение?
заранее спасибо.
1
ISR (PCINT0_vect)
// handle pin change interrupt for D8 to D13 here
2
{
3
digitalWrite(13,digitalRead(8) and digitalRead(9));
4
}
верно ли я понимаю из данного мануала (http://playground.arduino.cc/Main/PinChangeInterrupt) что когда на одном из цифровых пинов от 8го до13го появиться логическая единица, то сработает прерывание и код остановиться? Далее мне нужно пройтись по этим пинам и проверить у кого еденица, в моем случае - это какой счетчик инкрементить.
Не совсем. Когда на одном из ваших пинов появится изменение состояния код не остановится, а произойдет передача управления на обработчик прерывания PCINT0. Далее, какая из ног "сработала" можно решить чтением всего порта сразу и операцией XOR с сохраненным предыдущим значением. В 1 остануться только те биты, которые изменились. Пробегая их в цикле (сдвигая например вправо делением на 2) и проверяя исходный бит можно получить ответ "куда сработало" в ноль или 1.
Примерно так сделано у меня в "общем обработчике" PCINTx:
001
/**
002
* Определения и реализация с применением конечных автоматов для измерения длительностей сигналов и/или подсчета импульсов
003
* по фронту/спаду на пинах прерывания PCINT 0..2.
004
*
005
* Подключаются, только если до вызова #include "pcint.h" определено количество пинов прерывания: MAX_PULSES
006
* и определен используемый уровень прерываний 0,1,2 - реализация TEMPLATE на С, чтобы исключить принудительную
007
* вставку в скетчи обработчиков прерываний.
008
*
009
* Допускается повторное включение файла для обработки нескольких уровней прерываний PCINT в одном скетче.
010
*
011
* Доступные прерывания в Ардуино Мега 2560:
012
* PC_INT0[0..7] отключает SPI:SS(53),SPI:SCK(52),SPI:MOSI(51),SPI:MISO(50),pwm10(T2outA),pwm11(T1outA),pwm12(T1outB),
013
* (pwm13 - нельзя использовать! Только на выход!)
014
* PC_INT1[8..10] отключает USART0:RX(0), USART3:RX(15), USART3:TX(14) -- не реализовано для Мега2560 из-за разных регистров данных
015
* PC_INT2[16..23] (Analog8..Analog15)
016
* .. остальные ножки прерываний контроллера ATmega2560 в Ардуино - отсутствуют.
017
*
018
* Реализован "интерфейс" из ООП: методы обработки прерываний могут разрабатываться и расширяться без
019
* существенных изменений кода. Пока есть 2 метода:
020
* pcint_micros() -- измеряет длительность импульса или до таймаута
021
* pcint_encoder() -- подсчитывает прерывания по фронту/спаду/оба до таймаута или до запрета извне
022
*
023
* @example Примеры подключения:
024
* 1. Подключение 1 узв. датчика для замеров расстояний (длительности импульса) на прерывание PCINT2:
025
* #define PCINT 2
026
* #define MAX_PULSES 1
027
* include "pcint.h"
028
*
029
* 2. Подключение 4 энкодеров для подсчета импульсов на прерывание PCINT0:
030
* #define PCINT 0
031
* #define MAX_PULSES 4
032
* #include "pcint.h"
033
*
034
* 3. Одновременное использование прерываний в скетче уровня 0 (8шт) и 1(2шт) (подключаем дважды! и имеем 2 обработчика в скетче):
035
* #define MAX_PULSES 10 // 8+2 -- всего 10 структур обработчиков!
036
* #define PCINT 0
037
* #include "pcint.h"
038
* #define PCINT 1
039
* #include "pcint.h"
040
*
041
* Примечания:
042
* 1. Номер уровня прерывания PCINT - только константа #define! изменяет результирующий код обработчиков и функций
043
* 2. Уровень прерываний 1 для Ардуино Мега 2560 - не реализован: их всего 3 из 8 и они из разных регистров.
044
* 3. При подключении файла #include "hcsr04.h" этот файл подключается автоматически, но все предопределения должны
045
* выполняться также в скетче ПЕРЕД включением.
046
* 4. Файл arhat.h тут подключается автоматически, и если режим компиляции может быть указан точно также "до" включения этого файла.
047
*
048
* @author Arhat109-20151125. <a href="mailto:arhat109@mail.ru">arhat109@mail.ru</a>
049
* @license:
050
* 1. This is a free software for any using and distributing without any warranties.
051
* 2. You should keep author tag with any changes. May be with adding.
052
* Это свободное ПО для любого использования без каких-либо гарантий и претензий.
053
* Требуется сохранять тег @author при любых изменениях. Можете дописать свой.
054
*
055
* This is free software, not any pay. But you may donate some money to phone +7-951-388-2793
056
* Бесплатно. Но автор принимает пожертвования на тел. +7-951-388-2793.
057
*
058
*/
059
#if defined(PCINT) && ( (PCINT==0 && !defined(PCINT_0))||(PCINT==1 && !defined(PCINT_1))||(PCINT==2 && !defined(PCINT_2)) )
060
061
#if !defined(MAX_PULSES)
062
#error *** pcint.h::ERROR! not defined how much pins will be used for pcints data
063
#endif
064
065
#if PCINT==0
066
#define PCINT_0 1
067
068
#elif PCINT==1
069
#define PCINT_1 1
070
071
#elif PCINT==2
072
#define PCINT_2 1
073
#endif
074
075
// Защелка для исключения повторных переопределений
076
#ifndef _PCINT_H_
077
078
#include "arhat.h"
079
080
Pulse pulses[MAX_PULSES];
// One array for all measures! Один список для всех обработчиков измерений!
081
082
// template генерация названий функций для текущего уровня прерываний PCINT:
083
#define __pcint_init(p) uint8_t pcint##p##_init ( \
084
uint8_t pulseID, uint8_t pin, uint8_t state, \
085
PcintMethod method, uint16_t timeout \
086
)
087
#define _pcint_init(p) __pcint_init(p)
088
089
#define __pcint_start(p) void pcint##p##_start (uint8_t intNumber)
090
#define _pcint_start(pcint) __pcint_start(pcint)
091
092
#define __pulsein(p) uint16_t pulseIn##p (uint8_t pulseId, void (*action)(void))
093
#define _pulsein(p) __pulsein(p)
094
095
#endif // _PCINT_H_
096
#if PCINT==0
097
098
uint8_t pcint0old = 0;
// состояние пинов прерываний "предыдущее"
099
uint8_t pcint0numbers[8];
// текущие номера КА из pulses[], измеряющие на пинах 62..69 = 8шт!
100
101
#elif PCINT==1
102
103
uint8_t pcint1old = 0;
// состояние пинов прерываний "предыдущее"
104
uint8_t pcint1numbers[8];
// текущие номера КА из pulses[], измеряющие на пинах 62..69 = 8шт!
105
106
#elif PCINT==2
107
108
uint8_t pcint2old = 0;
// состояние пинов прерываний "предыдущее"
109
uint8_t pcint2numbers[8];
// текущие номера КА из pulses[], измеряющие на пинах 62..69 = 8шт!
110
111
#endif // PCINT
112
113
/**
114
* Замена типовой pulseIn(). В отличии от типовой - замеряет длительность сигнала по прерыванию.
115
* Структура pulsesX[p] должна быть настроена на обработку ЗАРАНЕЕ.
116
* Позволяет исполнять функцию без параметров, пока идет замер.
117
*
118
* template: uint16_t pulseIn{0,1,2}(uint8_t pulseId, void (*action)(void))
119
* calls:
120
* pulseIn0(pulseId, callback); // for PCINT0 level
121
* pulseIn1(pulseId, callback);
122
* pulseIn2(pulseId, callback);
123
*/
124
_pulsein(PCINT)
125
{
126
#if PCINT==0
127
pcint0_start(pulses[pulseId].pin & 0x3f);
128
#elif PCINT==1
129
pcint1_start(pulses[pulseId].pin & 0x3f);
130
#elif PCINT==2
131
pcint2_start(pulses[pulseId].pin & 0x3f);
132
#endif
133
uint16_t startTime = (uint16_t)getOvfCount();
134
while
(
135
(pulses[pulseId].state == PULSE_BUSY)
136
|| (pulses[pulseId].state == PULSE_SECOND)
137
){
138
if
( !((uint16_t)getOvfCount() - startTime > pulses[pulseId].timeout) ){
139
action();
140
}
else
{
141
pulses[pulseId].state = PULSE_TIMER;
142
}
143
}
144
return
(pulses[pulseId].state == PULSE_OK? pulses[pulseId].res : 0);
145
}
146
147
/**
148
* Настройка структуры замера на работу что и как измерять динамически. До вызова pcintX_start()!
149
* Предпочтительнее статическая настройка в setup():
150
*
151
* @example Статическая настройка структуры pulses2[1]: для PCINT2, номер в структурах замера 1:
152
*
153
* pulses2[1] = {0, pcint_micros, 28, 0, PULSE_BUSY, ((2<<6)|8) };
154
*
155
* , что означает, данные КА:
156
* {res=0, "замер длительности", ждать < 28*1024мксек, 0, "занят", "PCINT2, на 8 аналоговом входе"
157
*
158
* @return uint8_t pcint_number -- номер вектора прерываний в уровне [0..7]
159
*
160
* template: uint8_t pcint{0|1|2}_init(
161
* uint8_t pulseID, uint8_t pin, uint8_t state,
162
* PcintMethod method, uint16_t timeout
163
* )
164
* calls:
165
* pcint0_init(pulseID, pin, state, method, timeout);
166
* pcint1_init(pulseID, pin, state, method, timeout);
167
* pcint2_init(pulseID, pin, state, method, timeout);
168
*/
169
_pcint_init(PCINT)
170
{
171
uint8_t intNumber = PCINT_pin2number(PCINT, pin);
// макрос от уровня PCINT! ищем номер прерывания по общему номеру пина
172
173
PCINT_numbers(PCINT)[intNumber] = pulseID;
// сохраняем номер активной структуры
174
pulses[pulseID].state = state;
// закрываем доступ "идет замер"
175
pulses[pulseID].pin = (PCINT<<6) | intNumber;
// пригодится для записи ошибки по таймауту
176
pulses[pulseID].method = method;
// метод обработки - подсчет длительности импульса/количества прерываний/..
177
pulses[pulseID].timeout = timeout;
// включаем таймаут для этого замера
178
179
return
intNumber;
180
}
181
182
/**
183
* Запуск измерения длительности сигнала на ножке через прерывания PCINT2
184
*
185
* @param uint8_t intNumber -- номер запускаемого прерывания этого уровня 0..7
186
*
187
* template: void pcint{0|1|2}_start(uint8_t intNumber)
188
* calls:
189
* pcint0_start( intNumber );
190
* pcint1_start( intNumber );
191
* pcint2_start( intNumber );
192
* or call:
193
* pcintX_start( pcintX_init(...));
194
*
195
* К моменту вызова структура замеров должна быть настроена на конкретную работу через pcintX_init()
196
* @see pcintX_init();
197
*/
198
_pcint_start(PCINT)
199
{
200
uint8_t mask;
201
202
mask = (1<<intNumber);
// получаем маску для битовых операций {0}1{0}
203
PCINT_DDR(PCINT) |= mask;
// ?надо Pullup? нога "на выход" {0}1{0}
204
PCINT_PORT(PCINT) |= mask;
// ?надо Pullup? сброс ножки прерывания в 1 ("echo")
205
206
mask = ~mask;
// инвертируем маску для битовых операций {1}0{1}
207
PCINT_old(PCINT) &= mask;
// бит в "было раньше" = 0
208
PCINT_DDR(PCINT) &= mask;
// нога "на вход"
209
PCINT_MSK(PCINT) |= ~mask;
// и только теперь разрешаем прерывание с этой ноги
210
211
pulses[pulseID].start = getOvfCount();
// фиксируем время старта
212
213
PCICR |= (1<<PCINT);
// и разрешаем вектор PCINT0..2
214
}
215
216
#ifndef _PCINT_H_
217
/**
218
* One dispatcher for all pcint-workers for control pcint_timeout
219
* Единый обработчик таймаутов для всех элементов структуры pulses
220
* Если требуется проверка таймаут, проверяем и изменяем статус этой структуры
221
*
222
* for loop() as everyMillis()
223
*/
224
void
pcint_timeout()
225
{
226
Pulse *ptr = &pulses[MAX_PULSES-1];
227
uint16_t time = (uint16_t)getOvfCount();
228
229
do
{
230
if
( ptr->timeout && (time - ptr->start > ptr->timeout) )
231
pcint_end( ptr, PULSE_TIMER);
232
ptr--;
233
}
while
(ptr != pulses);
234
}
235
#endif // _PCINT_H_
236
237
/**
238
* Обработчик прерывания для прерываний PCINT0..2
239
*
240
* При срабатывании - ищет сработавшие ножки и для каждой из них вызывает
241
* свой метод замера, передавая ему предыдущее состояние пина.
242
*
243
* ISR( PCINT2_vect ) после подстановок формирует это:
244
*
245
* void __vector_ 11(void) __attribute__ ((signal, used, externally_visible));
246
* void __vector_ 11(void)
247
*
248
* Время исполнения около 15+(15*N+8*M), где N-сработало ног, M-просмотрено бит (N+M<8!)
249
* Оптимизация: шустрые ноги - размещать в начало уровня!
250
*/
251
ISR( PCINT_NAME(PCINT) )
252
{
253
uint8_t reg = PCINT_PIN(PCINT);
// Первым делом читаем ноги регистра К (Analog8..15)
254
uint8_t temp = PCINT_old(PCINT);
// и предыдущее их состояние
255
uint8_t * ptrNumbers = PCINT_numbers(PCINT);
256
257
PCINT_old(PCINT) = reg;
// сразу сохраняем новое состояние
258
reg = (temp ^ reg) & PCINT_MSK(PCINT);
// ищем разрешенные И сработавшие ножки (нормально 1шт)
259
do
{
260
if
( reg & 0x01 )
// Если прерывание от этого бита
261
{
262
Pulse * ptrPulse = pulses+(*ptrNumbers);
// получаем адрес структуры замера
263
ptrPulse->method(ptrPulse, temp & 0x01);
// исполняем собственно обработчик
264
}
265
ptrNumbers++;
// следующий номер прерывания
266
reg = reg >> 1;
// готовим следующую проверку "кто сработал"
267
temp = temp >> 1;
// готовим его пред. состояние (raising|failing)
268
}
while
(reg);
269
}
270
271
// And only this wa are defining blocking semaphore
272
// И только теперь определяем блокирующую защелку
273
#ifndef _PCINT_H_
274
#define _PCINT_H_ 1
275
#endif
276
277
#endif // PCINT blocking for twice parsing
Смотрите строки 250..271. Генератор процедуры ISR() собственно обработчик прерывания. В нем выделяется какая нога сработала и вызывается та подпрограмма, которая привязана к этой ноге с передачей её текущего значения ноги прерывания. Она уже внутри себя может посмотреть какое изменение (0->1 или 1->0) и что-то сделать по этому поводу.
Вот, как-то так. Тут файл-темплейт для генерации функций по мере надобности для разных случаев и плат.
Понял Вас, спасибо за код.
Но по факту лучше использовать 2 External + 3 PinChange прерывания, как описано выше в теме, что бы не тратить ресурс на определение ноги инициатора, верно? Или данный вариант необходим только при высоких частотах, что бы не пропустить срабатывания? Если в моем случае частота срабатывания по каждому прерыванию будет не чаще чем 1с то можно смело использовать вариант с определением ног?
А возможно же еще использовать и вот эту библиотеку http://playground.arduino.cc/Main/PinChangeInt верно?
Попробую реализовать это в живую, что бы задавать меньше глупых вопросов :) и ужесли не получиться приду запомощью.
Интересно ж не только реализовать, а и понять логику процесса.
Помогите разобратся с PCINT2
01
#define set_bit(reg,value) reg |= (_BV(value))
02
#define unset_bit(reg,value) reg &= ~(_BV(value))
03
volatile uint8_t ReadPortD;
//тут храним состояние порта D Пины с 0-7 на ардуино UNO
04
05
06
ISR(PCINT2_vect){
// сработало прерывание
07
unset_bit (PCICR,PCIE2);
//отключаем прерывание до отработки прерывания, во избежании повторных срабатываний
08
ReadPortD=PIND;
//записываем состояние порта D
09
}
10
11
void
setup
()
12
{
13
pinMode(5,INPUT_PULLUP);
// Clock Подключаем к INT1, нельзя переназначать
14
pinMode(6, INPUT_PULLUP);
// второй вывод энкодера
15
pinMode(7, INPUT_PULLUP);
// кнопка энкодера
16
17
Serial
.begin(9600);
18
while
(!
Serial
);
19
set_bit (PCICR,PCIE2);
//Включаем прерывание PCINT16-23 (PortD) PCIE2, регистр PCICR принимает значение 0b00000100;
20
PCMSK2 |= (_BV(PCINT23) | _BV(PCINT21));
//Указываем, что надо реагировать только на пины 5,7
21
// для порта D соответствуют pcint 16-23
22
Serial
.println(PCMSK2,BIN);
23
sei();
//включаем прерывания
24
Serial
.println(
"ready"
);
25
}
26
27
28
29
void
loop
() {
30
if
(ReadPortD) {
// если сюда что-то записано, значит было прерывание. Прерывания отключены, надо бы не забыть их включить.
31
Serial
.println(
"int detect"
);
32
Serial
.print(
"Pind: "
);
33
Serial
.println(ReadPortD,BIN);
34
ReadPortD=0;
//мы все отработали, значит можно обнулить переменную
35
set_bit (PCICR,PCIE2);
//включаем прерывание. Вроде не забыл
36
}
37
38
}
Запускаю, сначала замыкаю 6-й пин на землю, потом 5й пин на землю. Получаю вывод:
стр#
30 условие не правильное
А можно, для альтернативно одаренных, поподробнее? ReadPortD изначально равен 0. Условие не выполняется. Пока не сработает прерывание и не запишет в эту переменную значение порта D. Если при замыкании PD6 на землю срабатывает прерывание, то условие отрабатывается как истинное - и это правильно и логично.
Но почему срабатывает прерывание pcint 21, когда оно отключено маской PCMSK2?
можете видеть изменение состояние выводов в прерывании
01
volatile uint8_t ReadPortD = 0;
02
volatile boolean StatePCINT2 =
false
;
03
04
ISR(PCINT2_vect) {
05
PCICR &= ~_BV(PCIE2);
06
ReadPortD = PIND;
07
PCICR |= _BV(PCIE2);
08
StatePCINT2 =
true
;
09
}
10
11
void
setup
() {
12
Serial
.begin(9600);
13
pinMode(5, INPUT_PULLUP);
14
pinMode(6, INPUT_PULLUP);
15
pinMode(7, INPUT_PULLUP);
16
PCICR |= _BV(PCIE2);
17
PCMSK2 |= _BV(PCINT21) | _BV(PCINT23);
18
Serial
.print(
"PCMSK2 > "
);
19
Serial
.println(PCMSK2, BIN);
20
Serial
.print(
"PIND > "
);
21
Serial
.println(PIND, BIN);
22
}
23
24
void
loop
() {
25
if
(StatePCINT2) {
26
Serial
.println(
"PCINT2 > "
);
27
Serial
.println(ReadPortD, BIN);
28
StatePCINT2 =
false
;
29
}
30
}
Если у вас включен PCINT только на одном выводе порта, вы можете предположить, что прерывание PCINT в PortX связано с единственным, которое вы включили. Но, если вы используете два или более на одном и том же порту, вам необходимо прочитать регистр, чтобы определить, какое из них вызвано.
Опять непонятно. Я точно так же в прерывании записываю состояние всех пинов порта D и в основном цикле вывожу их. Оттуда и вижу, что идет срабатывание на PD6 (на выводе четко виден 6й бит Pind: 10100111), который отключен маской PCMSK2 (которую я тоже проверил выводом в конце setup 10100000).
Зачем лишняя переменная StatePCINT2 если значение ReadPortD может изменится только при вызове прерывания?
И какое отношение это имеет к вызову прерывания по падению PD6?
Как честный человек, я проверил предлагаемый код. И очень удивился - нет ложных срабатываний на PD6!
Для проверки убрал из кода лишнюю переменную:
01
volatile uint8_t ReadPortD = 0;
02
volatile boolean StatePCINT2 =
false
;
03
04
ISR(PCINT2_vect) {
05
PCICR &= ~_BV(PCIE2);
06
ReadPortD = PIND;
07
}
08
09
void
setup
() {
10
Serial
.begin(9600);
11
pinMode(5, INPUT_PULLUP);
12
pinMode(6, INPUT_PULLUP);
13
pinMode(7, INPUT_PULLUP);
14
PCICR |= _BV(PCIE2);
15
PCMSK2 |= _BV(PCINT21) | _BV(PCINT23);
16
Serial
.print(
"PCMSK2 > "
);
17
Serial
.println(PCMSK2, BIN);
18
Serial
.print(
"PIND > "
);
19
Serial
.println(PIND, BIN);
20
}
21
22
void
loop
() {
23
if
(ReadPortD) {
24
Serial
.print(
"PCINT2 > "
);
25
Serial
.println(ReadPortD, BIN);
26
ReadPortD =
false
;
27
PCICR |= _BV(PCIE2);
28
}
29
}
Код все равно продолжает работать правильно. Скомпилировал свой код (там где "условие не правильное") - оно опять работает!
Повторить проблему не удалось. Что за волшебство?