Прерывания - программа зависает
- Войдите на сайт для отправки комментариев
Вс, 02/12/2012 - 13:10
Есть простенький код -
int steps = 0; void setup() { attachInterrupt(4, ticker, CHANGE); pinMode(19, INPUT); Serial.begin(9600); } void ticker() { steps++; Serial.println(steps); } void loop() { }
на прерыванческий пин повешена оптопара, внутри которой крутится колесико с прорезями. Код работает нормально, но в какой-то момент (кажется, произвольный) цифры в окошке мониторнига порта перестают появляться. То есть, если следующее число должно быть, к примеру, 124, печатается 12 и следующие числа не появляются. Что это может быть?
P.S. датчик при этом продолжает работать - на нем есть светодиод, дублирующий индикацию, он исправно мигает.
P.S. датчик при этом продолжает работать - на нем есть светодиод, дублирующий индикацию, он исправно мигает.
Читаем attachInterrupt() в особенности вот этот обзац
Замечание по использованию
Внутри функции обработки прерывания не работает delay(), значения возвращаемые millis() не изменяются. Возможна потеря данный передаваемых по последовательному соединению (Serial data) в момент выполнения функциии обработки прерывания. Переменные, изменяемые в функции, должным быть объявлены как volatile.
Там не написано об этом, но функция Serial.println на равне с delay() и millis() не будет работать в обработчике прерывания ее нужно вынести в основной цикл.
Большое спасибо, я понял!
Лучше все же переписать третью строку вот так:
иначе смысла в проверке нет - условие будет выполняться всегда после первого (даже единственного) приращения steps.
Да, точно, просмотрел.
Переписал программу в таком виде:
После этого вывод зависать перестал, но непонятки остались - почему-то steps ооочень быстро увеличивается, за один оборот колеса, по идее, переменная должна увеличиваться на 8, но приращение сразу в тысячах измеряется. Я чего-то еще не знаю о прерываниях?
А как подключаете энкодер? подтягивающий резистор стоит?
Там датчик вот такой http://dvrobot.ru/shop/i148.datchik_skorosti.htm, думаю, обвязка вся есть.
Кстати, пока я делал неправильно - Serial.print вызывал из процедуры прерывания, он считал точнее.
Перенос функции никак не мог повлиять на счет, просто до переноса вы скорее всего не видили этого. Попробуйте в setup добавить строку
но врядли это поможет
Кстати, пока я делал неправильно - Serial.print вызывал из процедуры прерывания, он считал точнее.
Кстати, приведите кусочек вывода в терминал - интересно узнать, сколько инкрементов успевает произойти за один цикл loop'а.
Все описанное вами весьма похоже на шум датчика и в "неправильном" варианте два "минуса" (шум датчика и блокирование прерываний на время, необходимое для вывода в Serial), накладываясь друг на друга, давали - пусть и хилый - но плюс (фильтрацию шумов).
Были мысли про шум - но я ведь пробовал и просто выводить значения с датчика в монитор порта, без использования прерываний - там четко - пока в прорезь светит диод, но мониторе 1, закрывается лепестком - 0, ничего никуда не прыгает не скачет. Вот вывод - это за один оборот колеса столько набегает.
Видимо, всё-таки прав step962, и это шум - поставил колесо в граничное положение, где оно помаргивало и поймал сразу несколько тысяч приращения. Тогда есть ли способ более-менне снизить влияние шума на счетчик?
И еще вопрос - следуя логике программы, она должна выводить ВСЕ значения переменной step, правильно? И если да, то почему она этого не делает?
Так может это только когда вы его вручную крутите? Попродуйте как можно ближе подвинуть колесо к фототранзистору и соответственно как можно дальще от ИК-светодиода. Там где точка - это фототранзистор.
Я так и собрал, колесо близко к тразистору. А вручную я кручу за второй конец оси, там обычное колесо, чтобы ездить. За него и кручу. Я вечером конструкцию сфотографирую, если надо.
А двигатель куда подключен?
Схема в двух словах такая - есть драйвер двигателей, к нему подключены двигатели, но не подключено питание (на время экспериментов с энкодером), драйвер двигателей подключен к ардуине мега 2560, и к ней же подключен вот этот самый датчик с оптопарой.
А светодиод на датчике нормально мигает? Это пробовали?
Перенос функции никак не мог повлиять на счет, просто до переноса вы скорее всего не видили этого. Попробуйте в setup добавить строку
но врядли это поможет
За светодиод на датчие прям ручаюсь - отлично мигает, всё четко. По поводу digitalWrite(19, 1); - ещё не пробовал, и у меня вопрос - зачем писать в пин, настроенный на чтение? Что это даст? Не поломается ли чего-нибудь?
http://arduino.ru/Reference/DigitalWrite
Вход будет "подтянут" через резистор к +5В
Пробуйте.
это одно из самых первых что нужно было пробовать.
И еще вопрос - следуя логике программы, она должна выводить ВСЕ значения переменной step, правильно? И если да, то почему она этого не делает?
Вы настроили свой UART/USART на скорость 9600. Это около 800 символов в секунду. Чтобы вывести очередное значение steps (предположим, "уже" трехзначное число), функции println потребуется передать 5 символов (в конце еще CR+LF), для этого потребуется около 6 миллисекунд (5/800=0,00625). Время, достаточное для завершения переходного процесса (в случае кнопки называемого обычно "дребезгом"). Сколько раз за это время ваш датчик включится/выключится - дело весьма непредсказуемое. Судя по приведенному выводу в терминал - от 5 до 60 раз.
Как с этим бороться? Снижать чувствительность системы. Для этого либо выключать обработчик прерывания на несколько миллисекунд после регистрации первого фронта, либо интерпретировать пачку фронтов как один переход с высокого уровня на низкий:
Чтобы быть уверенным, что при таком подходе не происходит потери "настоящих" импульсов, прикиньте, какова максимальная скорость вращения вашего колеса, сколько импульсов в секунду должно при этом выдаваться и не превышает ли длительность между двумя регистрируемыми импульсами этот минимальный период следования импульсов. Не превышает - спим спокойно. Превышает - начинаем думать, как организовать глушение датчика после первого пойманного фронта.
PS: для приведенного варианта вывода превышение бует получено почти наверняка - слишком много букофф выводится. Увеличьте скорость UART до 57600, чтобы подстраховаться...
step962, вы крутой:) всё работает офигенски!
да вы тут все, в общем, крутые!
хм... а еще можно было припаять на ногу 4 и gnd конденсатор порядка 0.1 мкф и было бы щастье.
или при входе в прерывание сделать cli() , потом некоторое ожидание и sei ()...
ожидание можно реализовать
Насколько я понял эти правила относятся к обработчикам любых прерываний? Тоесть не важно - внешнее это прерывание или по таймеру?
Столкнулся с такойже проблемой зависания на Меге. Убрал все что связано с millis(), micros() и тп из обработчика. Про volatile и Serial.print() не знал - сегодня попробую с учетом этого. Заметил что зависание происходит в одно и то же время. Помимо прерываний в программе происходит отслеживание интервала через обьект Metro. Не конфликтует ли Metro с прерываниями (по таймеру MsTimer2), ктонибудь в курсе?
Пару советов ....
1. Если переменая инкрементируется от 0 и до.... я обычно использую unsigned int.
Те-же 4 байта, но зато какого размера! ;)
2. Обычно вешаю на вход прерывания хотя-бы 1000 пик .
unsigned int - 2 байта
1. Если переменая инкрементируется от 0 и до.... я обычно использую unsigned int.
У меня обработчик прерывания сравнивает значение аналогового входа с эталонным и при превышении ставит флаг в true. В главном цикле этот флаг кушает другая функция.
а вообще тут дело не в типе данных (понятное дело если переменная никогда не будет или не должна быть отрицательной то мы добавим unsigned), а в области видимости.
Программа перестала виснуть когда я обьявил все переменные которые меняет обработчик прерывания как volatile.
Решил освоить прерывания-сделать счетчик об/мин по готовым примерам с отображением на ЛСД16х2.
Взял готовый пример ,добавил отображение на лсд, на вход подал импульсы (с генератора,пока возьму датчики Холла).
Все запустилось замечательно.но потом заметил что вывод на экран начал зависать(и в сериал тоже).Прочитал про volatile и Serial.print().Переделал.
Если закоментировать все что касается lcd и запустить скетч-работает все ок при частоте следования импульсов до 14кГц и в сериал выводит.
А при отображении на лсд -клинит . Почему?
И чем выше частота (200 Гц и выше)тем быстрее клинит.
попробуйте вместо отключения прерывания использовать функции noInterrupts() и interrupts()
подозреваю что detachInterrupt(...) выполняется значительно дольше чем хотелось бы тратить на это времени.
Насчет времени беру свои слова обратно. Проверил с пустым обработчиком прерывания. Разница всего 500 микросекунд. Но правильнее всеравно было бы использовать noInterrupts() и interrupts()
А при отображении на лсд -клинит
Как именно клинит? Просто останавливается и не показывает?
Кстати, заметил кое что еще. НА выводе на ЛСД присутствуют статические данные?
Лучше убрать lcd.clear() , а остальное разбить на две части:
- перенести в модуль setup(), а в loop() получится :
Вообще при работе с прерываниями их выключают только для критически важных операций. например для копирования переменной которую модифицирует обработчик в переменную с которой будет работать основная программа.
Да ,просто остается последняя информация и все - при изменении частоты не реагирует.Ресет ардуины и опять какое-то время нормально.
Подключил 7 сегментный с МАХ7219-то же самое . Поначалу еденички скачут.Через некоторое время замирает и отображает частоту но не реагирует на ее изменение.
звыняйте за говнокод...
Всетаки попробуйте заменить:
detachInterrupt(0) на noInterrupts() и второй attachInterrupt(0, ... RISING) на interrupts()
и напишите, может чтото изменится?
Всетаки попробуйте заменить:
detachInterrupt(0) на noInterrupts() и второй attachInterrupt(0, ... RISING) на interrupts()
и напишите, может чтото изменится?
Ну что получится...
Кроме своего обработчика выключится также обработчик таймера/счетчика0 и перестанут обрабатываться переменные, используемые в millis() и micros(). Пусть и ненадолго, но все же весьма неприятный побочный эффект.
Кроме этого выключатся и обработчики прерываний последовательного порта и именно в тот момент, когда в порт засылается информация. К чему это приведет? А х.з.
Какие там еще прерывания присутствуют в ядре Arduino?
В общем, вместо целенаправленного маскирования одного-единственного прерывания (см. исходники в WInterrupts.c) вы остановите весь механизм.
Не смертельно...
Но чревато...
Я бы обратил внимание на то, на какой скорости инициализируется последовательный порт.
Потом посчитал бы, сколька букафф выводится в каждой посылке.
Рассчитал бы, сколько времени требуется для вывода такого количества букафф и соспоставли это с частотой вывода.
М.б. вы банально засираете выходной поток порта и программа останавливается в ожидании, пока там рассосется?
М.б. просто на 19200 перейти? или на 57600?
Я бы также обратил внимание на первую строку функции loop:
и вместо проверки на равенство использовал проверку >=. В конкретной стадии разработки конкретной программы очень маловероятно, что вышеупомянутая проверка будет выполняться реже, чем один раз в миллисекунду, но в более сложных проектах пролететь момент точного равенства проще простого. А после такого пролета программа входит в вечный пустой цикл.
И таки да: послушал бы разъяснения, что же все-таки это такое - "клинит"?
и вместо проверки на равенство использовал проверку >=. В конкретной стадии разработки конкретной программы очень маловероятно, что вышеупомянутая проверка будет выполняться реже, чем один раз в миллисекунду, но в более сложных проектах пролететь момент точного равенства проще простого. А после такого пролета программа входит в вечный пустой цикл.
Да, действительно. Нужно использовать >=, ибо даже в небольшой программе есть вероятность пропустить такое равенство.
Я бы обратил внимание на то, на какой скорости инициализируется последовательный порт.
Ну так как в последовательный порт данные просто дублируются - его можно вообще отключить и посмотреть как это повлияет на ситуацию. Но отображение на LCD занимает куда больше времени чем вывод в последовательный порт.
Да, я был не прав. Отключение прерываний вызыывает остановку таймера, что в данном случае неприемлимо.
последовательный порт (пост 36) и все лишнее я отключил . А до этого эксперементировал с скоростью.Добавлял в скетч и блинк без делея-показания на индикаторах замирали а лед продолжал мигать.
Попробую поменять == на >=.
Но это вечерком.
Поменял == на >= и все заработало как надо.
Спасибо всем за помощь!