Странности с прерыванием
- Войдите на сайт для отправки комментариев
Хочу написать "сниффер" для захвата команд пульта ДУ и хочу это сделать без использования соответствующих библиотек.
Придумал такую схему - подключаю ИК-приемник ко 2 цифровому пину и использую прерывания:
#define IRrec 2 int pin = 5; volatile int state = HIGH; volatile int heartbit = 0; volatile unsigned long seq[200]; void setup() { Serial.begin(115200); Serial.println("IR rec"); pinMode(pin, OUTPUT); digitalWrite(IRrec, HIGH); attachInterrupt(0, isr_pulse, CHANGE); } void loop() { digitalWrite(pin, state); if(heartbit>4){ detachInterrupt(0); Serial.println("----"); } } void isr_pulse() { state = !state; heartbit++; seq[heartbit]=micros(); }
В функции обработки прерывания делаю три вещи:
1. для диагностики меняю значение state (чтобы осциллографом видеть, что происходит "на входе" и "выходе")
2. фиксирую текущее время в текущем элементе массива
3. увеличиваю значение счетчика.
Дальше планировал вывод этого массива для дальнейшей работы.
Но тут сразу есть две странности:
1. Почему-то в Serial ничего не выводится (хотя ограничение на использование Serial только внутри isr_pulse())
2. В выделенном условии я ожидал, что тут после 5 изменений уровня сигнала, отключаю обработку прерываний, но этого не происходит:
Голубой - первый канал - выход с ИК-приемника.
Пурпурный - третий канал - D5
Подскажите, почему так?
и еще на осциллограмме видно, что "реверс" state происходит не всегда... вообще не дело :(
1. Почему-то в Serial ничего не выводится (хотя ограничение на использование Serial только внутри isr_pulse())
Что, даже "IR rec" не выводится?
если закомментировать 34 строку, то все работает так, как ожидалось...
Тогда как же фиксировать время наступления событий (CHANGE)?
1. Почему-то в Serial ничего не выводится (хотя ограничение на использование Serial только внутри isr_pulse())
Что, даже "IR rec" не выводится?
Не выводится :(
Кстати, этот скетч работает и без подключенного ИК-приемника (по крайней мере "IR rec" так же не выведется).
ха. При закоментированной 34 строке в Serial все уходит превосходно...
Как быть?
А доку на http://arduino.ru/Reference/AttachInterrupt почитать особенно раздел "Замечания по использованию"?
ха. При закоментированной 34 строке в Serial все уходит превосходно...
Как быть?
В приведенном вами коде 34-я строка - это закрывающая скобка функции loop().
Как вас понимать?
А доку на http://arduino.ru/Reference/AttachInterrupt почитать особенно раздел "Замечания по использованию"?
Это я прочитал и еще раз хочу повторить тут:
Внутри функции обработки прерывания не работает delay(), значения возвращаемые millis() не изменяются. Возможна потеря данный передаваемых по последовательному соединению (Serial data) в момент выполнения функциии обработки прерывания. Переменные, изменяемые в функции, должным быть объявлены как volatile.
Но я это учел:
1. внутри функции обработки нет delay
2. внутри функции нет Serial
3. изменяемые переменные объявлены как volatile
4. millis() тоже внутри функции нет.
Где моя ошибка?
ха. При закоментированной 34 строке в Serial все уходит превосходно...
Как быть?
В приведенном вами коде 34-я строка - это закрывающая скобка функции loop().
Как вас понимать?
Извините, 33 строка, конечно. Там где как раз время событя CHANGE фиксируется и заполняется массив.
2. внутри функции нет Serial
Ох... простите. Чего-то показалось что оно в обработчике. Бегло смотрел, а ошибка эта очень частая.
4. millis() тоже внутри функции нет.
ну вообщем-то "ее можно использовать" (и часто нужно), но просто за время одного прохода функции прерывания она, хоть 10-ть раз ее вызове - всегда будет возвращать одно и тоже время - время входа в функцию.
Вообщем, щас попробую глянуть еще раз внимательней на ваш код.
ну вообщем-то "ее можно использовать" (и часто нужно), но просто за время одного прохода функции прерывания она, хоть 10-ть раз ее вызове - всегда будет возвращать одно и тоже время - время входа в функцию.
Вообщем, щас попробую глянуть еще раз внимательней на ваш код.
Дык и ее ровно один разик дергаю. Но вот если заполняю массив в обработчике - все, кранты - ничего не работает (правильно).
Я, кажись, понял.
Одна причина - почти наверняка вызовет проблемы, вторая - возможна.
Превая: вы делаете heartbit++;, но нигде не проверяете что он у вас не вашел за границы массива. Вообщем что у вас "массив не закончился". При heartbit>200 - вы своей строкой seq[heartbit]=micros() начинаете писать в случайную область памяти. Портить данные и сам код скетча. Поэтому - глюки будет любые, не обязательно потдающиеся логике.
Вторая (пока - маловероятная): нужно смотреть что-бы seq[200] влез в оперативку. когда памяти не хватает - тоже "глюки любые".
Вообщем пока вам нужано или
if(heartbit>=200)heartbit=0;
или
if(heartbit<200)heartbit++
Первая причина вызывает проблемы, но не сразу :). Так что условие такое в обработчик, безусловно, добавляем.
Но общую картину это не меняет.
По второй догадке - тоже есть смутные сомнения (использую Атмегу168), но пока еще не проверил.. попробую уменьшить размер массива.
Завтра отчитаюсь (и выложу "актуальную" версию скетча).
Первая причина вызывает проблемы, но не сразу :). Так что условие такое в обработчик, безусловно, добавляем.
Но общую картину это не меняет.
В любом случае - нужно. А картину портить может непредсказуемо. Могут идти импульсы при включении, от ламп... вообщем "забить массив" (и искаверкать скетч) может раньше чем вы нажали кнопку пульта хотя-бы раз.
По второй догадке - тоже есть смутные сомнения (использую Атмегу168), но пока еще не проверил.. попробую уменьшить размер массива.
У нее мозгов не так уж и много. Сделайте массив меньше и посмотрите что будет.
Скорее всего памяти не хватает, вы объявили массив в 200 элементов по 4 байта, в сумме получилось 800 байт, а у 168 всего 1 кбайт.
Попробуйте сохранять не текущее значение millis(), а разницу между текущим и предыдущим, тогда можно будет тип массива изменить на unsigned int.
Если работа программы будет менее 1 минуты, то, в принципе, можно поменять тип массива unsigned long на unsigned int и так.
Непонятно, зачем массив на 200 элементов, вроде по логике программы уже на 5-м элементе прерывание отключается и индекс больше не увеличивается.
Если в приведенной программе закоментить 33 строку, то, ввиду того, что обращений к массиву в программе нет, компиллятор (при оптимизации) этот массив вообще выбрасывает - и все начинает работать. Так что проблемма именно с нехваткой памяти.
Текущий код
Это работает....
AlexFisher, в скетче ограничение на длинну исключительно для того, чтобы проверить работоспособность (дальше нужно захватывать полную последовательность). Вообще это условие предполагалось заменить на что-то типа "если после последнего CHANGE прошло более 1с", то "остановиться в записи и выдать зафиксированные времена).
Совместными усилиями разобрались, что "не взлетает" из-за того, что не хватает памяти..
Теперь модифицирую код следующим образом:
И тут уже снова "грабли" - условие останавливается в произвольном месте...
В сериал все выводится, но heartbit (несколько раз пробовал) каждый раз разное (и маленькое: 3, 16, 9 и т.п.), на осциллографе вижу соответствующую картину (в сериал вылетело 9):
В чем теперь может быть проблема?
Я бы на вашем месте убрал в 41 строке преобразование в int
В строке 27 условие подразумевает что первое измерение должно начаться не познее 5 секунд от старта скетча.
Вообще это условие странное...
Я бы заменил на
Вывод информации и окончание работы через 10 секунд после запуска скетча.
Я бы на вашем месте убрал в 41 строке преобразование в int
уберу, а почему так лучше?
В строке 27 условие подразумевает что первое измерение должно начаться не познее 5 секунд от старта скетча.
Вообще это условие странное...
Я бы заменил на
Вывод информации и окончание работы через 10 секунд после запуска скетча.
в строке 27 я ожидал отработку ситуации "уже что-то считано (heartbit увеличивался в функции обработки прерываний) и от времени последнего вызова функции обработки прерываний прошло 5e6 микросекунд".
Можно, конечно, написать так, как Вы советуете, но за 10 секунд "прилетит" еще что-нибудь (другая команда, мусор и т.п.)
Я бы на вашем месте убрал в 41 строке преобразование в int
уберу, а почему так лучше?
В конечном итоге оно не на что не влияет, кроме как лишяя оперция, но вот посмотрите что происходит при преобразовании:
Спасибо, понятно. Правда, компилятор это подкорректировал самостоятельно, похоже. Что с преобразованием, что без - размер скетча идентичный.
Теперь бы еще разобраться с условием...
С условием кажется достаточно просто, всё уже есть, можно, например, так:
А в loop проверять, если флаг установлен, значит переполнение есть, можно останавливать.
Код не сильно оптимизирован, для примера.
Я неспеша решаю ту же задачу, но через захват таймера 1, правда, когда закончу - неизвестно, хотя процентов на 85 уже готово, Ваш путь другой, тоже интересно, чем закончится. Вот только осуиллографа у меня нет, эхх.. :)
UPD: после 48 строки, нужно еще по переполнению массива закончить:
else
{
stop = true;
}
Нет тут лишней операции. Справа у нас long, слева int. Так что операция "преобразование" у нас должна быть выполнена в любом случае.
Только в одном случае мы сами явно сказали о ее необходимости, а вдругом компилятор "сам догадался". Так что " компилятор это подкорректировал" - верно. Только в обратную сторону - он не "убрал лишние преобразование", а "добавил упущенное".
В язках с более строгой типизацией (C# например) - компилятор вообще бы не пропустил код без явного указания о необходимости преобразования. Вернее из "меньшего типа в больший" - пропустил, бы (так как не происходит потери информации), а вот из "большего в меньший" - уже затребовал-бы указать явно. Подтверить что это не ошибка, а дейстивительно "мы этого хотим".
Нет тут лишней операции. Справа у нас long, слева int. Так что операция "преобразование" у нас должна быть выполнена в любом случае.
Только в одном случае мы сами явно сказали о ее необходимости, а вдругом компилятор "сам догадался". Так что " компилятор это подкорректировал" - верно. Только в обратную сторону - он не "убрал лишние преобразование", а "добавил упущенное".
В язках с более строгой типизацией (C# например) - компилятор вообще бы не пропустил код без явного указания о необходимости преобразования. Вернее из "меньшего типа в больший" - пропустил, бы (так как не происходит потери информации), а вот из "большего в меньший" - уже затребовал-бы указать явно. Подтверить что это не ошибка, а дейстивительно "мы этого хотим".
Так в том и дело что справа
unsigned
long
, а слеваunsigned
int
, а преобразовываем вint
. Зачем?Я понимаю что
unsigned
int
и int места занимают одинаково и при преобразовании содержимое не меняется, но типы то разные.не выходит с условием...
Вот такая функция. Вроде как по логике - все супер, но не работает. stop сразу же принимает true (осциллограф подтверждает).
Может, есть какие-то ограничения?.. почему ардуина разницу во времени-то не отрабатывает?
Причем, условие с millis() - работает чудесно...
Так естественно сразу, предположим через секунду как запустили дуину происходит срабатывание прерывания, prev изначально равна 0, а micros() уже больше 1000000, вот и выполняется ваше условие сразу.
так-так-так... kiksoft малость запутал :)
maxim, чуток подправил условие (добавил проверку, что у нас там что-то уже "натикало" - мы же хотим получить "конец всей последовательности")
Но все-таки, так не пойдет... мы же функцию isr_pulse() вызываем, когда происходит изменение на входе... а "ловим" конец последовательности (последнее изменение получили, зафиксировали и все.. больше вызова isr_pulse() не будет, условие никогда не сработает).
Особо не вникая, просто "общие соображения". Для декодирования, возможно, лучше было смотреть не CHANGE, а FALLING или RISING.
Вообщем сами "провалы" у вас почти всегда одинаковой длины. Зачем же тратить память на их замер (а потом при декодировании выяснить кто у нес где, где был HIGH, где был LOW). Вполне можно использовать "сумма плато и провала" что-бы отличать нули от едениц.
Вот смотрите, я объеденил, к примеру по FALLING какие интервалы вы намеряете
Красный прямоугольник явно "стартовая пульсация", белый - длинная, зеленый - коротка (не помню сейчас что означает длинная короткая 1/0 или 0/1).
Не видно что-бы вы вообще где-то проверяли этот stop.
Проверка. Зачем такая сложная? вы же строчкой выше уже посчитали интервал и положили его в seq[heartbit], зачем еще раз это вычитание? Возмите уже готовое.
В третьих я бы вообще не пытался делать каких-то проверок (кроме выхода за границы массива) в isr(). Или, в крайнем случае пытался там "ловить" не "конец", а "начало". Что-то типа
И только тогда начинал заполнять массив.
Проверку "остановки" - я бы выполнял в loop(). Или если "массив закончился" или "мы получили ожидаемое количество пульсаций" либо "что-то долго вообще prev не менялся, пора попытатся декодить что наловили, очистить массив, сбросить heartbit и ждать следующего START_PULSE"
Кстати, не подскажете что за осцилограф?
leshak, к сожалению, смотреть только FALLING не пойдет... скетч не одноразовый (пока по крайней мере, планируется). И пульты могут быть разные (это даже на осциллограммах успело проскочить: верхние две картинки это пульт от ресивера ямаха, а третья - пульт кондея) и видно, что даже "стартовые пульсации" разные и т.п.)
полный код скетча на сегодня:
а осциллограф DSO Quad. Очень удобная вещь оказалась. Правда, штатное ПО оказалось с глючком (иногда не давал "пролистать" записанный сигнал), но вчера поставил прошивку от энтузиастов - вроде работает, но реально еще потестить не успел. После обновления скриншоты станут чуть другими (но принцип - тот же).
Ударюсь немного в "предания старины" и "сам себя не похвалишь - сидишь как оплеванный" :)
Я, пару лет назад, знакомство с электроникой как раз с построение IR-декодера и начинал. Поставил себе задачу - "сделать устройство от начала до конца". Купил ардуину, освоил фоторезист, сделал исвою плату, научился камни прошивать, нашел дешовую замену FT232, сделал корпус....
Так вот. Пульт который мне приглянулся "по дизайну" был Philips. И только после того как купил его я обнаружил что IRRemote - не умеет ее декодировать. Пришлось, таки, разбиратся со всеми этими кодировками, разгадывать ребусы (без осцилографа :) и дотачивать ее до поддержки филипсовского протокола. А потом добавлять в нее возможность отключать неиспользуемые протоколы (все прототипировалось на arduino меге, а когда начал заливать в конечное устройство обнаружил что "не влазит" в atmega8 ).
Да, к чему это я:
leshak, к сожалению, смотреть только FALLING не пойдет... скетч не одноразовый (пока по крайней мере, планируется). И пульты могут быть разные (это даже на осциллограммах успело проскочить: верхние две картинки это пульт от ресивера ямаха, а третья - пульт кондея) и видно, что даже "стартовые пульсации" разные и т.п.)
Не хочется спорить, но вы все-таки попробуйте мой совет. Хотя-бы мысленно. Нарисуйте на все карнитнках разбивку по FALLING, посмотрите что выходит. Не буду утверждать что "ну прямо все пульты/кодировки знаю", всегда есть шанс на "гиперэкзотику", но общий принцип у них общий. Сама длина импульса (падение в ноль) - не важна, кодирование происходит временем между импульсами.
Да вы правы что "длина стартовой" и "длина нуля" и "длина единицы" - будут разные для разных пультов. Но принцип кодирования - общий. Различия будут проявлятся уже дальше (может быть не только "стартовый, но и финишный", сколько бит занимает сам код, есть ли биты четности, как посылаются повторные нажатия и удержания и т.п.).
Вообщем мое IMHO что FALLING тут подходит. По крайней мере ко всем примерам картинок что вы постили. Просто сделайе и выведите интервалы межуд FALLING. Это вообщем-то и будет то что в IRRemote называлось raw :)