MCP23017 и прерывания.
- Войдите на сайт для отправки комментариев
Помогите разобраться с прерываниями при работе с MCP23017.
Схема подключения такая к 5 - 11 входам MCP подключен энкодер и еще 4 тактовых кнопки, все они при нажатии замыкаются на землю. Нога INT_A подключена к ноге D2 Arduino NANO. Все прочие подключено в точности по даташиту (если сильно нужно – могу нарисовать). Используется библиотека Adafruit_MCP23017. В качестве теста использую немного переделанный (под несколько кнопок вместо двух) код из примера Interrupt:
#include <Adafruit_MCP23017.h> Adafruit_MCP23017 mcp; unsigned int count = 0; byte arduinoIntPin = 2; byte arduinoInterrupt = 0; volatile boolean awakenByInterrupt = false; void setup() { Serial.begin(9600); Serial.println("MCP23007 Interrupt Test"); pinMode(arduinoIntPin, INPUT); mcp.begin(); mcp.setupInterrupts(true, false, LOW); for (byte i = 5; i < 12; i++ ) { mcp.pinMode(i, INPUT); mcp.pullUp(i, HIGH); mcp.setupInterruptPin(i, FALLING); } } void intCallBack() { awakenByInterrupt = true; } void handleInterrupt() { uint8_t pin = mcp.getLastInterruptPin(); uint8_t val = mcp.getLastInterruptPinValue(); count++; Serial.print(count); Serial.print(" Pin="); Serial.print(pin); Serial.print(" Val="); Serial.println(val); boolean A = false; while (!A) { A = true; for (byte i = 5; i < 12; i++ ) A = A && mcp.digitalRead(i); } cleanInterrupts(); } void cleanInterrupts() { EIFR = 0x01; awakenByInterrupt = false; } void loop() { attachInterrupt(arduinoInterrupt, intCallBack, FALLING); while (!awakenByInterrupt); detachInterrupt(arduinoInterrupt); if (awakenByInterrupt) handleInterrupt(); }
Код работает, но виснет. Повиснуть он может практический в любой момент. В среднем у меня успевает счетчик нажатий дойти до 150-200, но может повиснуть и на первом десятке. Если уменьшить кол-во используемых кнопок, программа виснет позже, для одной кнопки (точнее энкодера) я смог дойти до 3500, для двух всего до 1100. Т.е. чем больше кнопок и чем быстрее я их нажимаю/кручу энкодер – тем раньше происходит зависание, но какой-то конкретной зависимости я не нашел. Оригинальный код из примера для двух кнопок также виснет где-то после тысячи нажатий.
В чем может быть проблема? Библиотека кривая? Пример кривой? Если я не использую прерывания, но использую ту же библиотеку и железо, то все работает стабильно очень долго (несколько дней как минимум). Еще пару лет назад пробовал использовать прерывания MCP23017 – тоже столкнулся с зависаниями (тогда использовал другое железо – MEGA2560, два энкодера, и экран LCD2004, писал что-то типа игры Змейка). Я, в принципе, могу обойтись без прерываний, но в этом случае энкодер периодически тупит, по часовой стрелке идет ровно, но против - может прыгать туда-сюда, с обычными кнопками проблем нет. Вешать энкодер на отдельные ноги ардуино нет возможности – все заняты, да и схему приделывать проблематично.
А вот этот бесконечный attachInterrupt() для чего?
Предполагаю, что виснет на цикле в строке №28. Для начала, нужно в этом убедиться. Для этого поставьте вывод чего-нибудь в сериал после строки №31. И скажите, прав ли я, что зависание происходит именно в этом цикле?
К зависанию это скорее всего не имеет отношения, но не рационально последовательно читать все пины
for (byte i = 5; i < 12; i++ ) A = A && mcp.digitalRead(i);
Для сброса флагов прерывания в MCP23017 достаточно считать один раз GPIOA и GPIOB. (Или по одному пину из А и В.)
Помогите разобраться с прерываниями при работе с MCP23017.
Сам с 23017 не работал, так что дальше только мысли.
Зависание может быть если на входе INT0 ардуины постоянно низкий уровень. Проверить легко - или тестером, или раз в несколько секунд выводить значение в Serial.
А постоянно низкий уровень может быть например если неудачно прошли отрезок
for (byte i = 5; i < 12; i++ ) A = A && mcp.digitalRead(i);
cleanInterrupts();
Когда дошли до последнего пина порта В, пин порта А уже снова стал 0, и снова выставил флаг прерывания. INT0 снова стал 0, но флаг сбросится cleanInterrupts();
К зависанию это скорее всего не имеет отношения, но не рационально последовательно читать все пины
for (byte i = 5; i < 12; i++ ) A = A && mcp.digitalRead(i);
Для сброса флагов прерывания в MCP23017 достаточно считать один раз GPIOA и GPIOB. (Или по одному пину из А и В.)
Я плохо понимаю зачем делать опрос пинов, до тех пор пока хотя бы на одном из них не пропадет сигнал, но в исходном примере так и есть:
Но даже когда я оставляю всего одну кнопку, и условие сводится к простому while( ! mcp.digitalRead(mcpPin)); то зависание рано или поздно происходит. Если бы это была обычная кнопка, то мало у кого хватило бы терпения сделать несколько тысяч нажатий, в случае энкодера докрутить до зависания становится возможным. Но когда кнопок несколько – зависание ловится почти сразу. В любом случае, мне не очень нравится этот пример. Хотя бы тем, что они там delay используют, я вывод заменил на serial.print, для большей наглядности, но где-то на форуме видел, что нежелательно его использовать при работе с прерываниями (не понял по чему).
Да и ждать отпускания кнопки, это как-то странно. А если я хочу удержание кнопки тоже как-то использовать? Получается, что так прерывания можно использовать только для энкодера?
Вы можете померить тестером, что на D2 в зависшем состоянии?
Вы можете померить тестером, что на D2 в зависшем состоянии?
Сейчас это проблематично, у меня на рука только собранное устройство, которое даже вскрывать лишний раз не хочется. К другому железу в текущий момент доступа нет.
Тогда, для проверки, вставьте в loop отправку раз в несколько секунд (или десятков секунд) состояния D2.
Тогда, для проверки, вставьте в loop отправку раз в несколько секунд (или десятков секунд) состояния D2.
Сделал вот так:
На 12-15 ногах у меня светодиоды висят. При первом же нажатии на любую кнопку загораются и остаются гореть 14 и 15 светодиоды, 12, 13 - не видно, но если быстро вращать энкодер, то 12 и 13 уже заметно мигают, а при зависании уже горят все четыре светодиода, т.е. на D2 в этот момент 0.
P.S. Если крутануть энкодер совсем быстро, то гарантированно зависает уже на втором счете.
P.P.S. Даже если использую всего ОДНУ ногу (while (!mcp.digitalRead(5));) - если энкодер крутить - виснет! I2C шина не успевает, или что?
Может, пора всё-таки понять где именно виснет? Ответ на мой вопрос из поста #2 будет? Или ну его нахрен?
Для этого поставьте вывод чего-нибудь в сериал после строки №31. И скажите, прав ли я, что зависание происходит именно в этом цикле?
Как ни странно, виснет не тут:
После зависания 14 и 15 не горят! (Из loop'a я их убрал)
(Начал писать до ответа ЕвгенийП)
Предполагаемую причину я написал в #4
По хорошему, чтобы понять можно ли реализовать на MCP23017 требуемое вам разрешение по частоте нажатий, надо прочитать описание даташит. Или найти хорошее объяснение в инете (где искать не знаю).
Если предположения #4 верны, то как заплатку можно предложить два варианта для устранения зависаний. Но пропуски нажатий возможны.
1 вариант. Перенести cleanInterrupts(); вверх
2 Вариант. Перейти на прерывание D2 по уровню
Как ни странно, виснет не тут:
Похоже, Вам помощь не нужна. Я Вам сказал поставьте ПОСЛЕ СТРОКИ №31 из кода, что в первом посте. А Вы чего сделали?
Делайте, что говорят, поставьте туда, куда я сказал и скажите, что происходит. Только выложите при этом код как он получился, т.к. доверия к тому. что Вы вставите правильно у меня нет.
Делайте. Пока Вы не поймёте где виснет, все остальные телодвижения - в пользу бедных.
Я Вам сказал поставьте ПОСЛЕ СТРОКИ №31 из кода, что в первом посте. А Вы чего сделали?
А я что сделал? Зажигаю светодиод до цикла, а гашу после – раз он моргает и тут же гаснет, значит цикл проходит, и виснет не в нем. Разве не так?
1 вариант. Перенести cleanInterrupts(); вверх
А вот это, как раз помогло. Спасибо!
Рабочий вариант теперь выглядит так:
Теперь не виснет! Ну, или у меня не хватает терпения/скорости, чтоб вызвать зависание. Но приходится фильтровать значение pin=255, которые теперь постоянно появляются.
Вариант следить только за одним из пинов из группы GPIOA и GPIOB, в принципе работает, но например, если я пишу так: while (!(mcp.digitalRead(7)&&mcp.digitalRead(8))); то программа будет ждать отпускания кнопок 7 и 8, а все остальные будут повторятся без отпускания, что не очень хорошо. В любом случае, мне не подходит такой вариант опроса для кнопок, тут не получится реализовать обработку удержания кнопок. Попробую использовать прерывания только для энкодера, остальное и так нормально работает.
А я что сделал? Зажигаю светодиод до цикла, а гашу после – раз он моргает и тут же гаснет, значит цикл проходит, и виснет не в нем. Разве не так?
Не так. Я же Вам писал номера строк. Я говорю о цикле в строках №№28-31, а Вы о цикле в строке №40 (все номера по коду из первого поста. Нельзя быть таким невнимательным!
http://arduino.ru/forum/pesochnitsa-razdel-dlya-novichkov/mcp23017-i-preryvaniya#comment-533997
Нельзя быть таким невнимательным!
Понятно. Ну, не хотите делать, что Вам говорят, трахайтесь со своими проблемами сами.
Понятно. Ну, не хотите делать, что Вам говорят, трахайтесь со своими проблемами сами.
ДА, БЛИН (пардон!) у посмотрите на ссылку то! Я же в точности тот цикл и проверил, о котором Вы говорили!
Только там опрос идет уже всего одной ноги, а нескольких (для нескольких результат тот же).
Вам трудно было взять код из первого поста и сделать, что Вас просили? Это не моя проблема.
трахайтесь со своими проблемами сами.
Вам трудно было взять код из первого поста и сделать, что Вас просили? Это не моя проблема.
Еще раз, я проверил и этот код:
И этот:
Разница только в том, что в первом случае я работаю только с ОДНОЙ кнопкой, а во втором с НЕСКОЛЬИКИМИ сразу, но это один и тот же код, с одной и той же логикой, и я их ОБА проверил, результат был ОДИНАКОВЫЙ – программа рано или поздно висла, но не в этом месте, эта процедура отрабатывала до конца, светодиоды моргали и гасли. Я не понимаю причину Вашего «недовольства». Проблема оказалась все равно не в этом месте.
Еще раз, я проверил и этот код:
Теперь это уже только Ваша проблема, мне можете ничего не писать. До свиданья.