Дополнительные внешние прерывания
- Войдите на сайт для отправки комментариев
Пнд, 18/06/2012 - 20:46
Стоит задача обрабатывать 4 логических сигнала.
Использовать для этого циклический опрос входов довольно неудобно и сильно нагружает Ардуину.
Поскольку задача решается на базе Нано, то то ног для внешних прерываний всего 2 (Д2 и Д3).
Подскажите, пожалуйста, есть ли способ переопределить еще пару ног (пусть Д4 и Д5, или еще какие-то) под внешние прерывания по аналогии с Д2/Д3? Ну либо аналоговые входы Ах...
Спасибо за советы. :)
ну если почитать даташит на atmega328 на котором и посроен нано,то можно подключить (если не изменяет память) до 23 вншених прерываний.только нужно правильно отконфигурировать : вот ссылка на ДШ :
www.atmel.com/devices/atmega328.aspx
Спасибо, спеки мне известны, я думал есть какие-то готовые библиотеки с функциями для подобных работ.
ну если почитать даташит на atmega328 на котором и посроен нано,то можно подключить (если не изменяет память) до 23 вншених прерываний.только нужно правильно отконфигурировать : вот ссылка на ДШ :
www.atmel.com/devices/atmega328.aspx
Непонятно почему тогда стандартный attachInterrupt так ограничен в выборе ног. Или он юзает какие-то другие прерывания? Тот же SoftwareSerial гораздо больше ног заюзать может. Хотя вроде тоже прерывания аппаратные использует, так как накладывает ограничения на каких ногах можно RX пин делать. Но, все равно, намного больше их чем у attachInterrupt.
P.S. Я понимаю что ответы в даташите и исходниках, может просто "уже кто-то разбирался".
Спасибо, спеки мне известны, я думал есть какие-то готовые библиотеки с функциями для подобных работ.
А погуглить ""arduino additional external interrupt library"
Первая ссылка http://code.google.com/p/arduino-pinchangeint/
Да и на офф сайте она упоминается внизу http://arduino.cc/playground/Code/Interrupts
P.S. Кстати там же нашел и ответ на свой вопрос. Таки действительно есть два вида прерываний external и pinchange
extrenal - более умные (точнее можно сконфигурить когда они должны срабатывать), но их мало.
Спасибо, прерывание на порт1 (PCINT1) повесил, маской выделил ноги А0-А3 как входы для вызова прерываний.
Но обработчик обрабатывает событие сразу с 4 ног.
Каким образом определить инициатора прерывания?
analogRead(pin) читает состояние образно сразу с 4 маскированных ног - негодно.
Как быть?
PCINT1 это не прерывание на порт 1. Как написано в ДШ на мегу328 если бит PCIE1 регистра PCICR установлен в 1, ноги микросхемы помеченные как PCINT8 - PCINT14 начинают "генерить" прерывания при любом изменении уровня на них. Обработчик прерывания на ногах PCINT8 - PCINT14 только один. Если нам не надо (а нам не надо) что-бы все ноги "генерили" прерывания то регисторм PCMSK1 выбираем какие именно ноги из PCINT8 - PCINT14 нам нужны.
Так же если бит PCIE0 регистра PCICR установлен в один, то все ноги PCINT0 - PCINT7 (у них уже свой обработчик прерывания) начинают "генерить" прерывания при любом изменении уровня, а регистром PCMSK0 выбираем нужные.
Прерывание ноги отлично генерят!
Вопрос в селекции (выборе) той ноги которая "привела" к прерыванию.
Можно ли как-то ее определить?
а вам прирывание по какому признаку нужно?
ну "1" на конретной ноге,"0" на конретной ноге или перход от 0->1 или 1->0 на конретной ноге ?
если еденица то возможно вам поможет использование маски:
пояснения :
PB - PortB -> читайте даташит какая нога сидит на каком порте.
в данном примере маска это 0х80 в шеснадцетиричной системе исчисления,если перевести в бинарную то "10000000"
& - логическое побитное "И"
проверка в if проверяет если самый верхий бит на PortB являеться еденицей.при этом никак не влияя на остальные биты порта
единственное что,не совсем уверен можно ли так в IDE обращаться к портам(то есть PA-PortA,PB-PortB...ит.д) где то видел примеры где похожим способом (как в CodeVision) обращались,сам не пробовал.
Прерывания INT0 и INT1 (external) повешаны каждый на свою ногу микросхемы (на рис. отмечены зеленым). А есть прерывания PCINT0, PCINT1, PCINT2 (отмечены красным). Так вот эти прерывания закреплены за ГРУППОЙ ног микросхемы. PCINT0 это ноги отмеченные как PCINT0 - PCINT7, PCINT1 это ноги отмеченные как PCINT8 - PCINT14 и PCINT2 это ноги отмеченные как PCINT16 - PCINT23 (это не пины ардуино, а ноги микросхемы) . И реагируют они только на ЛЮБОЕ изменение уровня.
Узнать точно какая нога сгенерила прерывание, например PCINT1, нельзя потому, что это может быть любая нога PCINT8 - PCINT14. Но при помощи регистра PCMSK1 можно отключить ненужные ноги и оставить нужные или вообще только одну, тогда прерывание PCINT1 будет генериться только выбранной ногой микросхемы.
Если не понятно пиши какие ноги должны генерить прерывания я напишу пример кода.
Хоспаде, да прерывания УЖЕ генерят нужные мне 4 ноги!
Нет селекции оных в рамках ОДНОГО прерывания!!!
А вешать их на разные ноги пока нет желания в силу определенных причин.
Я думал возможно в каком-то регистре есть нужная инфа "кто сгенерил из замаскированных"... Видимо такое сделать невозможно... :(
Иначе говоря надо генерить 4 независимых прерывания - по одной ноге на каждое прерывание.
Больше 3 прерываний выйдет сгенерить, если ИНТ0 и ИНТ1 накладываются на ноги РСИНТх?
Я думал возможно в каком-то регистре есть нужная инфа "кто сгенерил из замаскированных"... Видимо такое сделать невозможно... :(
Возможно - в ISR следует сравнивать текущее состояние ног с состоянием, записанным во время предыдущего прерывания. У какой ноги состояние поменялось, та и "виновница".
Спасибо, уже разобрался.
Просто вчера не подумав повесил на 4 ноги из аналогового набора - там чтение состояний ни к чему адекватному не приводило.
На цифроногах все ОК, еще раз спасибо.
Иначе говоря надо генерить 4 независимых прерывания - по одной ноге на каждое прерывание.
Скорее всего - никак. Самому, руцями.
Для каждой ноги делать digitalRead (или читать из порта сразу все). Запоминать куду-нибудь в переменные прочитанное.
Тогда при следующем прерывании, опять сделав digitalRead и сравнив с предыдущим состоянием вы сможете понять кто из этих четырех ног изменил свое состояние (и в ка кукаю сторону).
Уже разобрался, спасибо.
Можно ли в обработчике прерывания использовать millis() или micros() ?
Создается впечатление, что оно как-то криво в нем работает...
А справку по attachInterrupt читали? Там есть ответы на оба ваших вопроса. И можно ли, и в чем кривость будет проявляется. Даже шрифтом выделенно.
Note
Inside the attached function, delay() won't work and the value returned by millis() will not increment. Serial data received while in the function may be lost. You should declare as volatile any variables that you modify within the attached function.
Если Вы об этом, то там речь о миллис(), о микрос() там нету. В любом случае как быть при замере времени между циклами вызова прерывания? Внутри прерывания поднимать флаги какая нога прервала, а затем вне обработчика уже опрашивать микрос() для вычисления периода?
Таймеры не катят, т.к. их разрядность куда ниже, чем требует задача - будет постоянное переполнение.
>Если Вы об этом, то там речь о миллис(), о микрос() там нету.
Дык они принципиально не отличаются. ЧИтают из одного счетчика, разница только в том что millis() возвращаю на тысячу делит и округляет.
>В любом случае как быть при замере времени между циклами вызова прерывания?
А какие проблемы с замером времени между прерываниями? millis/micros-то работает.... просто пока вы в обработчике - она не меняет свое значение. Если вы дернете, внутри обработчика несколько раз - она каждый раз вернет одно и тоже же значение. То которое было в момент "входа в обработчик". Так что если вам нужно знать "когда сработало прерывание" - спокойно вызываете и "отвеченному можно верить". При слудеющей сработке - опять вернет правильно значение. А в от если вы удумаете внутри самого обработчкика какой-то цикл задержку крутить или замерять время выполнения обработчика - тогда уже танцы с бубуном. Вообщем пока выполняется обработчик "время стоит".
P.S. И не забываем про valatile если меняем глобальные переменные внутри обработчика.
Вынес за пределы обработчика ВСЕ, кроме IntFlag, куда побитово сваливаю инициатора(-ов) прерывания:
IntFlag = 0;
if (RL_Pin_In_State != digitalRead(RL_Pin_In)) IntFlag |= (1 << 0);
if (RR_Pin_In_State != digitalRead(RR_Pin_In)) IntFlag |= (1 << 1);
if (FL_Pin_In_State != digitalRead(FL_Pin_In)) IntFlag |= (1 << 2);
if (FR_Pin_In_State != digitalRead(FR_Pin_In)) IntFlag |= (1 << 3);
А уже в лупе():
if (IntFlag != 0)
{
CountIntervals();
IntFlag = 0;
}
Но все равно периоды почему-то плавают, особенно с ростом частоты входящих сигналов (которые эмулируют 4 ноги в том же лупе циклически опрашивая микрос() и замеряя не вылезли ли мы за нужный период - а если вылезли меняет сигнал на ноге на инверсный).
Возможно эмулятор частот сильно нагружает проц и гадит в картину.
Кроме того в лупе есть вывод в СОМ данных:
if ((micros()-t) > 1000000)
{Serial.println(ActiveAxleInterval);
t=micros();}
В идеале надо внешний генератор частот, и посмотреть что из этого выйдет без вредителей...
http://arduino.ru/forum/obshchii/vstavka-programmnogo-koda-v-temukommentarii
Если вы пытаетесь еще и программно эмулировать сигнал - не получится. Как-бы то нибыло настоящей ОДНОВРЕМЕННОСТИ никогда не будет. Обработчик прерывания не запускается "в отдельном потоке". На время его выполнения основной скетч - останавливается.
Можете попробовать генерить сигнал PWM ногами. Они аппаратно работают и выдают пилу не занимая мозг проца. По крайней мере я так тестил, когда мне нужно было логику для захвата длинны импульсов писать.
И замер времени, точнее, все-таки, делать внутри обработчика. Там же где вы выставляете флаги.
P.S. И что за тяга показывать куски скетча, а часть объяснять словами. Вот где у вас объявлен (и как) IntFlag - не видно. Если скетч большой - дык можно (и нужно) его просто свернуть.
ШИМ не подходит из-за скудного набора базовых частот и доступных делителей, я уже пробовал использовать его.
Переменная объявлена в сааааааааааааааааааааамой первой строке скетча.
Попробую по приезду еще одного Нано сделать его генератором нужных частот, а второй будет их считать.
Кстати, Сериал.принт судя по замеру микрос() сжирает времени около десятых долей мс - это нормально? И можно ли как-то изменить настройкой скорости связи?
Можно при пощи прерываний таймера сделать ШИМ с требуемой частотой
Да генерирование сигналов - не есть решение задачи. Это просто на скорую руку.
У прерываний таймера базовая частота = 16 МГц, верно?
Есть ли пример?
ШИМ не подходит из-за скудного набора базовых частот и доступных делителей, я уже пробовал использовать его.
"может быть просто вы не умеете их готовить"?
Чего добиваетесь-то? Какой частоты? Какой скважности? Что важнее - частота или скважность?
Кстати, Сериал.принт судя по замеру микрос() сжирает времени около десятых долей мс - это нормально? И можно ли как-то изменить настройкой скорости связи?
1. Абсолютно - на скорости 9600 бод один символ "пропихивается" примерно за одну миллисекунду. Спасает буферизация. Однако, буфер USART имеет вполне себе конечную длину - как бы не соврать - 128 байт. Если вы пытаетесь передать строку в 130-150 символов, то вполне можете получить и пару десятков мс обработки. Ну а если ваш мк имеет RAM менее 1 кБ, то буфер ограничен 32 байтами и затор начинается гораздо раньше...
2. Как-то изменить можно. Вопрос - чего вы хотите добиться. Сокращения этого времени на 10-20% или в десять раз.
Может и не умею.
Скважность = 50%. Частоты расчетные - до 5КГц.
50% - это меандр.
Зачем вам ШИМ?
А мне он и не нужен.
Просто датчики которые надо сэмулировать - датчики Холла с прямоугольным сигналом и скважностью ОКОЛО 50%.
Режим "Clear Timer on Compare Match (CTC)" не позволяет решить эту задачу?
Вы загнали меня в угол... :)
Я не работал с таймерами, да и ваще только начинаю разбираться...
А как вы собирались генерировать ШИМ? с помощью analogWrite()? Да, там действительно выбор частот маловат - всего одна...
Не одна...
Базовая - одна. Делителей - 5шт.
В analogWrite() все уже поделено - на выходе "приблизительно 490 Гц" (теоретически - 488,28 при 16-МГц резонаторе).
А если напрямую программировать, то да - 5 делителей в нулевом и первом таймере, 7 во втором ...
Мне не подходит базовая частота 62.5/31.25КГц и скудный набор делителей.
Образно говоря дискретность частот от 0 до 5000 Гц должна быть = 1Гц. Можно меньше.
Сейчас одна из ног висит на частоте 31250Гц (делитель = 1). Но это к делу отношения не имеет.
Образно говоря дискретность частот от 0 до 5000 Гц должна быть = 1Гц. Можно меньше.
Сейчас одна из ног висит на частоте 31250Гц (делитель = 1). Но это к делу отношения не имеет.
Ни таймер0, ни таймер2 в этом вам не помогут в силу своей 8-разрядности.
Ну, а на базе таймера1 вы вполне можете получить частоты до килогерца с шагом Гц. Дальше могут начаться проблемы - возможно, не весь диапазон 1-5 кГц удастся пройти с заявленным шагом (чем выше частота, тем проблематичнее).
Засада... :(
Скачал Таймер1. Там заявлена точность до 1мкс. Но там где диод должен мигать на 1Гц, 50/50, он мигает более 2 секунд....
А почему вы не хотите использовать tone()
Если сигнал уже воспроизводится на одном порту, то вызов Tone() с номером другого порта в качестве параметра ни к чему не приведет...
Частот надо хотя бы 2, разные. В идеале - 4.
Засада... :(
Скачал Таймер1. Там заявлена точность до 1мкс. Но там где диод должен мигать на 1Гц, 50/50, он мигает более 2 секунд....
Используя ардуиновские функции , Вы не сможете добится частоты 5000Гц и скважности 1/99
Попробуйте переключать порт вот так :
#define D9_High PORTB |=B00000010
#define D9_LOW PORTB &= B11111101
Кстати, не прокомментируете ли строчку 15:
digitalWrite(9, digitalRead(9)^1);
- почему именно бинарная операция "побитовое исключающее или" там, где с делом справляется обычное унарное отрицание (!)?
- почему хранение значения триггера именно в регистре ввода/вывода и связанные с этим накладные расходы на чтение из регистра (digitalRead выполняется раз в десять-двадцать медленнее чтения из переменной)?
Попробуйте переключать порт вот так :
#define D9_High PORTB |=B00000010
#define D9_LOW PORTB &= B11111101
К этому еще необходимо добавить логику, правильно выбирающую тот или иной макрос, т.е. запрограммировать триггер.
//****************обработчик прерывания********************
ISR(TIMER2_OVF_vect)
{
TCNT2 = tcnt2;
pwm_time++;
if(skvazhnost > pwm_time ) D9_High; else D9_LOW;
}
Написал библиотеку которая добавляет Ардуине три дополнительных внешних прерывания, может кому пригодится.
Качаем отсюда: narod.ru/disk/53744121001.58813742217ad60714e1f724d43c4b60/Pcint.rar.html.
Извлекаем в "...\куда установлен Ардуино\libraries". Как использовать читаем ReadMe.txt.
Проверялось на Arduino Duemilanove + IDE Arduino 22, но на других duino/IDE по идее тоже должно работать.
//****************обработчик прерывания********************
ISR(TIMER2_OVF_vect)
{
TCNT2 = tcnt2;
pwm_time++;
if(skvazhnost > pwm_time ) D9_High; else D9_LOW;
}
И в самом конце добавить нечто вроде:
Написал библиотеку которая добавляет Ардуине три дополнительных внешних прерывания, может кому пригодится.
Качаем отсюда: narod.ru/disk/53744121001.58813742217ad60714e1f724d43c4b60/Pcint.rar.html.
Извлекаем в "...\куда установлен Ардуино\libraries". Как использовать читаем ReadMe.txt.
Проверялось на Arduino Duemilanove + IDE Arduino 22, но на других duino/IDE по идее тоже должно работать.
А у нее есть какие-то дополнительные преимущества по сравнению с библиотекой из сообщения #4?
Преимущество у нее только одно - ее гораздо проще использовать.
Подскажите, почему на выбранных ногах не работает прерывание INT1? C INT0 никаких проблем нет.
Что-то вопрос не совсем понятен. Оно и не должно работать. Прерывание INT1 жестко привязано к третьему биту порта D (PD3 или пин 3 ардуины) и ни на какие другие ноги мы его назначить не можем. А вот для прерывания PCINTx мы можем указать нужные ноги, которые будут его генерировать.
В первой строке вы разрешили прерывание PCINT0, во второй строке настроили ноги PCINT1 - PCINT4 которые будут генерить это прерывание, в третьей строке вы настроили прерывание INT1 на любое изменение уровня и в четвертой разрешили его.
Это значит, что после выполнения этого кода, при любом изменении уровня на ногах PCINT1 - PCINT4 (пины 9 - 12 ардуины), будет выполняться обработчик прерывания ISR(PCINT0_vect){}, и при любом изменении уровня на ноге микросхемы помеченной как INT1 (пин 3 ардуины), будет выполняться обработчик прерывания ISR(INT1_vect){}.
Как-то так.
Спасибо за ответ.
Но есть еще несколько вопросов:
1) Можно ли генерить прерывание PCINT0 ногами не порта В, а другими, или данное прерывание относится только к ногами этого порта и не более?
2) В чем тогда отличие PCINTx от INTx кроме возможности у PCINTx менять ноги (и маскировать их в рамках определенного порта B/C/D) и большего приоритета у INTx в случае одновременной подачи сигнала на ногу PD3 и PCINT1 (порт В)?
3) Если прерывание с ноги PCINT1 будет вызвано в момент выполнения обработчика INT1, будет ли пропуск этого события, а если нет, то какой глубины буфер для отслеживания подобных событий (т.е. какая "вложенность" событий прерываний допустима для работы без пропусков)?
4) Для решения задачи надо 4 независимых прерывания: в наличии ноги Ардуины D2 (INT0 - PD2 на МК), D3 (INT1 - PD3 на МК), D7 (PCINT23 - PD7 на МК) и D8 (PCINT0 - PB0 на МК). Будет ли корректно так раскидать ноги, или ногу PD7 лучше не использовать, а взять одну из ног порта С? Или можно вообще использовать все ноги порта D и настроить по одному прерыванию на каждую ногу (например, вместо PB0 использовать PD6)?
Использование ноги PD6 не катит, т.к. тогда придется анализировать какая из ног D7 или D8 вызвала прерывание. А это лишнее...