Вопрос про внешние прерывания

Тема в разделе "Arduino & Shields", создана пользователем DKo, 30 янв 2016.

  1. DKo

    DKo Нерд

    Добрый вечер!
    Помогите, пожалуйста, разобраться с вопросом.
    Ардуино Уно имеет два входа, которые можно настроить на обработку внешних прерываний, при этом как известно функция, вызываемая в результате прерывания не должна быть длинный и не должна вызывать другие прерывания (я веду к том, что delay() не получится использовать).

    Хочется сделать управление одной кнопкой, которая будет управлять режимом работы контроллера. Для выбора режима необходимо удерживать кнопку в течение разных интервалов времени: 3 сек, 5 сек, 7 сек.
    Как правильно это сделать? Если при вызванном прерывании, первой строчкой кода функции "обработки выбора режима", сделать detachInterrupt(), то куда попадет указатель программы при завершении обработки прерывания? В то же место, куда бы он попал при неотключенном прерывании?

    Заранее спасибо!
     
  2. Limoney

    Limoney Гик

    Программа продолжит выполнение ровно в том же участке кода, в котором было вызвано прерывание. Например, если прерывание произошло в функции loop, то выполнение последовательности команд продолжиться с новой команды, перед которой было вызвано прерывание.

    detachInterrupt() отключит выбранное прерывание
     
    DKo нравится это.
  3. Megakoteyka

    Megakoteyka Оракул Модератор

    На самом деле входов с прерываниями гораздо больше, смотрите PCINT.
    Правильно при помощи прерывания отслеживать моменты нажатия и отпускания кнопки, а все остальное делать в основном цикле программы.
     
  4. DKo

    DKo Нерд

    Это я понимаю, но само исполнение не очень укладывается в голове, хотя это, наверно, 100 % очень просто(
    Каким образом поймать момент отпускания кнопки? Переделать режим прерывания не с 0 на 1, а с 1 на 0? Если да, то можно ли это сделать в теле функции function: attachInterrupt(interrupt,function, mode); использовать еще раз
    attachInterrupt(interrupt,function, mode);
    Но уже с другим режимом mode?
     
  5. Sindbad

    Sindbad Гик

    Невозможность использовать delay() означает невозможность реализовать программную защиту от дребезга. Значит нужно реализовывать аппаратную. Ради чего все это? Что мешает отслеживать нажатие / отпускание кнопки простым опросом из loop()?
     
  6. Onkel

    Onkel Гуру

    некошерно. У нас есть функция обработки прерывания. примерно так
    1 при нажатии кнопки вызывается программа прерывания
    2 в программе обработки прерывания это прерывание по конкретному пину кнопки запрещается и запускается таймер задержки ,сработка выставляется на десяток- другой или сотню - другаю мс
    3 при сработке таймера проверяется состояние пина. если 0 (кнопка нажата), то считается что кнопка нажата,если 1, то сброс - кнопка не нажата и считаем что была помеха, разрешаем опять прерывание по нажатию кнопки.
     
  7. Onkel

    Onkel Гуру

    а разве delay() нельзя использовать в функциях прерывания? Вроде я не видел прямого запрета. delay() вроде тупо крутит nop без всяких прерываний, разве не так?
    а ее нужно (поверьте, нужно) всегда реализовывать и(!) аппаратную тоже. Несмотря на громкое название это (делов-то) кондер 0.1 uF
     
  8. DKo

    DKo Нерд

    Если цикл loop() занимает достаточно длительное время, то нажатие на кнопку во время выполнения той части кода, где нажатие не обрабатывается, приведет к пропуску нажатия.
    Следовательно необходимо сопроводить нажатие прерыванием.
     
  9. Sindbad

    Sindbad Гик

    Это и есть delay() по сути.
     
  10. Sindbad

    Sindbad Гик

    В первых сообщениях вот этой темы высказывется другое мнение http://forum.amperka.ru/threads/arduino-uno-зависает.6126/#post-62618
     
  11. AlexU

    AlexU Гуру

    Все сигнальные пины Arduino UNO поддерживают внешние прерывания по изменению состояния пина: нажали кнопку -- сработало прерывание, отпустили -- опять сработало. В функции обработчике прерывания проверяете состояние пина, если состояние соответствует нажатой кнопке, то считываете время (функция millis()) и запоминаете его, если -- отжатой, то считываете время и сравниваете со временем нажатия. Если прошло столько сколько нужно, то устанавливаете определенный флаг (например, переменная типа bool). В основном теле программы проверяете флаг и при его установке выполняете нужные действия и сбрасываете флаг.
    Вы не правы. Программно можно бороться с дребезгом при использовании прерываний.
    В прерываниях delay() использовать нельзя -- это приведёт к зависанию котроллера. Суть в том, что delay() использует millis(). Но millis() сама по себе ничего не подсчитывает, она всего лишь возвращает значение переменной, которое хранит время с момента запуска контроллера. За обновление этой переменной отвечает 'таймер 0' и его обработчик прерывания. А так как при возникновении прерывания и вызове его обработчика по-умолчанию все прерывания запрещаются, то и подсчёт времени останавливается пока не завершит свою работу обработчик прерывания или обработчик не разрешит прерывания явно.
     
    Vetrinus и DKo нравится это.
  12. Sindbad

    Sindbad Гик

    Так "если" или действительно занимает? Если я правильно понимаю, в обычных случаях loop() вызывается несколько тысяч раз в секунду.

    По теме: можно сделать два обработчика - один срабатывает на нажатие кнопки, второй на отпускание.
     
  13. DKo

    DKo Нерд

    И для этого, прерывание, исходя из синтаксиса ардуино, должно работать в режиме "CHANGE to trigger the interrupt whenever the pin changes value"

    Спасибо огромное за ответ!)
     
  14. geher

    geher Гуру

    "Если".
    Все зависит от того, что выполняется в loop.
    Если в loop имеется, например, вызов delay(10000), то он будет вызываться реже, чем раз в 10 секунд. А за это время может много чего произойти, в том числе может быть нажата и отпущена кнопка.
    Вместо delay может быть все, что угодно, занимающее длительное время: очень сложные математические рассчеты, ожидание события (например, смены состояния пина с постоянным опросом) и т.п.
    Другой вопрос, что такое в большинстве случаев может быть разбито на более мелкие атомарные операции, которые и будут вызываться по очереди при каждом запуске loop, и между которыми можно будет проверить кнопку, но прерывания надежнее.
     
  15. Onkel

    Onkel Гуру

    отнюдь. delay() тупо тормозит и стоит на месте, а если я выставляю таймер, мк возвращается к тому месту кода, откуда он ушел на прерывание, и выполняет программу далее до тех пор, пока не тикнет таймер. Так что это две совершенно разные вещи, по таймеру правильно а по delay() неправильно.
     
  16. Onkel

    Onkel Гуру

    отнюдь. Вот что говорит оригинал официал:
    Certain things do go on while the delay() function is controlling the Atmega chip however, because the delay function does not disable interrupts. Serial communication that appears at the RX pin is recorded, PWM (analogWrite) values and pin states are maintained, and interrupts will work as they should.

    Я не нашел явного описания именно ардуинской delay(), но обычно в мк-шных С это многократное выполнение nop в цикле.
    Другой вопрос что применять эту delay() некомильфо, ясен пень.
     
    Vetrinus нравится это.
  17. Onkel

    Onkel Гуру

    "другое мнение" основано на заблуждении будто бы delay() запрещает прерывания. А вот не запрещает, см. официальный текст официального сайта.
     
  18. DKo

    DKo Нерд

    С прерываниями разобрался, а можно ли сделать так, чтобы при выходе из прерывания функция возвращала указатель не в исходное положение, а в начало цикла loop()?
     
  19. Onkel

    Onkel Гуру

    наверное не указатель, а программный счетчик? можно, но это делается не в прерывании, а теле loop(). В прерывании же выставляете какой- либо параметр, и в теле loop() делаете проверку по этому параметру и ветвление. Но это некомильфо. Желательно, чтобы в алгоритме не было goto всяких.
     
    DKo нравится это.
  20. DKo

    DKo Нерд

    Если у меня долгие вычисления в теле цикла loop(), то рационально в алгоритме вычислений периодически делать проверку параметра и при удачном "попадании" вызывать команду continue, которая вернет в начало цикла loop()?