Найти тему

Усмиряем кнопки при помощи assемблера.

Всем привет.

Прежде чем мастерить что-то электронное при помощи кнопок и контроллера следует понять, что кнопки не такие простые как кажутся на первый взгляд.

Как только мы нажимаем на кнопку и сигнал прорывается на вход контроллера происходит настоящая магия. Если мы просто пропустим сигнал через кнопку на обычный светодиод, то он просто загорится при зажатой кнопке и погаснет при ее отпускании (Вот это да!!). Хотя можно и проинвертировать данную логику для тех кому скучно живется, но речь не об этом.

Если вспомнить нашу первую программку, то при нажатии на кнопку светодиод также загорался (удивительно), но это только для нашего медленного глаза так, а на самом деле при нажатии на вход контроллера сыпится целый шквал нулей и единиц в течении нескольких микросекунд, таким образом светодиод успевает за очень короткое время несколько раз свенить свое состояние, причем в режиме "ниндзя" для нас.

Объяснение тут довольно простое, так как контакт чисто механический, то по нашим дурацким законом мира обладает инерцией. И в момент замыкания контакты по инерции еще несколько раз колеблются между состоянием "круто работает" и состоянием "Опять свет выключили".

Можно бороться аппаратно с данной проблемой, но зачем нам всякие эти конденсаторы, триггеры и прочие страшные слова. Легче добавить пару строчек в исполняемый код программы.

Итак довольно слов, начнем!

Но для начала немного слов. Для решения данной проблемы программный путем следует ввести некую задержку после распознавания первого "истинного" нажатия перед последующим дребезгом. Хотя в этом есть кое-что ужасное, ведь во время задержки наш контроллер просто тупит и ничего не делает (прямо как я), а ведь за это время можно было бы сделать столько полезного (ничего).

Для примера будем использовать схему и алгоритм с первой программы.

Для борьбы с дребезгом стоит добавить следующие строчки после команды считывания входа с кнопкой:

: -------------------------------Небольшие приготовления-----------------------
.def loop = R17; Во-первых в начале такой программы определим еще один регистр для работы с задержкой и дадим ему кличку loop
; ------------------------------- Программа -------------------------------
main: in temp, PIND ;считываем вход с кнопкой
srbc temp, 0 ;пропускаем следующую команду, если кнопка не нажата, т.е. бит не сброшен
rjmp main ;возвращаемся на метку main
rcall delay ;вызов подпрограммы обработки дребезга
out PORTB, temp ;зажигаем светодиод

check: in temp, PIND ;считываем состояние кнопки
sbrs temp, 0 ;если кнопка отпущена, т.е. бит установлен, то пропускаем следующую команду
rjmp check ;возврат на проверку, если кнопка еще не отжата
rcall delay ;вызов подпрограммы обработки дребезга

out PORTB, temp ;гасим светодиод
rjmp main ;возврат к началу программы

delay: ldi loop, 200 ;загружаем константу 200 в дополнительный регистр

wait: dec loop ;вычитаем единицу из регистра loop
brne wait ;возврат к метке wait, если не нет нулевого результата
ret ;возврат из подпрограммы

Объясним непонятные команды:

  1. rcall - оператор вызова подпрограммы. Имеет один операнд - это относительный адрес (название метки) подпрограммы. При этом адрес следующей команды записывается в память контроллера для последующего возврата.
  2. dec - оператор декремента (уменьшения на 1) содержимого регистра общего назначения. Имеет один операнд, собственно, это регистр.
  3. brne - оператор условного перехода по относительному адресу (метке). Переход осуществляется при условие, что результат операции не 0. Немного подробнее что это значит. Есть такой специальный статус регистр у контроллера SREG ,в котором биты называются флагами, которые устанавливаются (в 1) по конкретным результатам операций. В данном случае нас интересует флаг Z, который устанавливается если результат операции 0. Таким образом, пока данный флаг не установится, оператор brne будет производит переход по указанному нами адресу. Если флаг Z устанавливается, то перехода не происходит.
  4. ret - команда возврата из подпрограммы. При этом восстанавливается из памяти адрес следующий команды после команды вызова подпрограммы и основная программа продолжает выполняться.

Теперь немножко помощи от разъяснительной бригады.

В начале определили дполнительный регистр R17, который поможет нам для формирования цикла задержки. Вначале все как обычно считываем состояние кнопки и как только определили что кнопка нажата запускаем подпрограмму обработки дребезга (rcall delay). В начале подпрограммы загружаем константу 200 в регистр loop (R17). Далее вычитаем 1 из регистра (dec loop) и если результат не ноль (brne wait), то повторяем снова. Так до 200 раз наш цикл повторится, пока результат операции не станет 0, тогда произойдет возврат (ret) из подпрограммы. Можно определить насколько мы задержались в этом месте. Команда dec занимает 1 такт, комнада brne если выполняет переход, то занимает 2 такта, если не выполняется переход, то тратится 1 цикл. При частоте 1 МГц (1 мкс на такт), получается что контроллер "пускает сопли" в течении 600 мкс (какой ужас!). Это с головой хватит, чтобы пропустить весь дребезг. Дальше я думаю все должно быть понятно, вторая обработка вызывается при отпускании кнопки.

Всем спасибо за внимание, если тут еще кто-нибудь остался.

Пока!