Удаленное управление. События. Энкодер инкрементальный.
-
И снова здравствуйте!
Отрыл сегодня инкрементальный енкодер на полке. Попытаюсь описать как с ним работать и как снимать направление вращения.
Что такое этот энкодер, думаю, смысла говорить нет. Это легко гуглится. Выглядит эта штука вот так:
У него есть 3 пина и если посмотреть на железку сверху, то по часовой стрелке можно эти пины обозначить как A, O, B.
Пин O - пин, работающий на выход, пины A и B работают на вход, то есть "читают" состояние пина O.
Теперь ситуация:
Поворачиваем енкодер по часовой стрелке и ...
Сначала меняется состояние пина A
потом B
потом меняется состояние пина A обратно
затем снова B возвращает свое первоначальное значение. Картина, в общем такая:
(Картинка не моя; взял с интернета)
Можно заметить, что в один момент времени пины принимают одинаковое значение, НО при этом имеют разный расклад на предыдущем шаге. Таким образом мы можем понять в какую сторону крутится ручка енкодера.
Покажу на примере ferro-remote как можно это обработать и сделать простейший счетчик, который при повороте по часовой стрелке увеличивает значение, а против - уменьшает.
Тут я описывал как можно ловить события (сигналы, прерывания ... называть можно как угодно) с пинов GPIO, работающих в режиме IN. Думаю повторить тоже самое для данного примера.
Возьмем 3 пина на RPi: GPIO 2, 3, 4.
2 будет A
3 будет O
4 будет BДля начала простой скрипт, который их откроет и расставит им верное направление
-- gpio-encoder.lua gpio = fr.client.gpio -- алиас current = 0 -- сам счетчик. Просто глобальная переменная function main( argv ) fr.run( ) -- нужно ждать события, скажем не завершать исполнение после main -- откроем 3 пина 2, 4 - IN 3 - OUT local A = assert(gpio.export( 2, "in" )) local O = assert(gpio.export( 3, "out" )) local B = assert(gpio.export( 4, "in" )) -- для пинов A и B установим edge в значение both assert(A:set( "edge", "both" )) assert(B:set( "edge", "both" )) end
Если такой скрипт исполднить lua_client'ом, то делать он не будет ничего, кроме того, что получит объекты для работы с пинами, а потом прибьет их сборщиком мусора.
Добавлю-ка я ему обработку события. Тут сделаю так (я видел в интернетах, что у народа довольно большие проблемы с энкодерами и логикой работы с ними. Хотя ничего страшного и сложного нет):
Оба события будет обрабатывать один и тот же обработчик. Можно сделать ДВА разных обработчика, но получится длинее и не уверен, что проще. В параметры обработчику будет передаваться признак того, какой именно пин сработал. false - сработал пин A, true - пин B. Мысль дальше: Чтобы понять как вращается ручка, в момент установки пина на 1, нужно уметь получить состояние другого пина, если оно установлено в 1, значит мы должны увеличить или уменьшить счетчик.
Если мы в обработчике пина B и пин A изменен, то мы вращаем по часовой стрелке и нужно прибавить и наоборот. добавим это в код:
- pin - false(А) или true(B)
- states - таблица, которая изначально имеет такой вид: {[false] = 0, [true] = 0}
- то есть оба состояние выставлены на 0
function change_handler( data, pin, states ) -- directions это просто вспомогательная таблица -- суть ее в том, что в случае вращения по часовй стрелке -- к счетчику будет прибавляться 1, а против -- -1 local directions = { [false] = -1, [true] = 1 } states[pin] = data.value -- установит состояние текущего пина в текущее значение -- states[not pin] - состояние соседнего пина -- если текущий пин установлен и соседний тоже установлен if data.value == 1 and states[not pin] == 1 then current = current + directions[pin] -- прибавим или отнимем print( current ) end end и того полный скрипт выглядит так: -- gpio-encoder.lua gpio = fr.client.gpio current = 0 function change_handler( data, pin, states ) local directions = { [false] = -1, [true] = 1 } states[pin] = data.value if data.value == 1 and states[not pin] == 1 then current = current + directions[direct] print( current ) end end function main( argv ) fr.run( ) local states = {[true] = 0, [false] = 0} local A = assert(gpio.export( 2, "in" )) local O = assert(gpio.export( 3, "out" )) local B = assert(gpio.export( 4, "in" )) assert(A:set( "edge", "both" )) assert(B:set( "edge", "both" )) -- подпишемся на события от GPIO assert(A:subscribe( "on_changed", change_handler, false, states, A )) -- пин А false assert(B:subscribe( "on_changed", change_handler, true, states, B )) -- пин B true end
Теперь код можно сохранить (например как gpio-encoder.lua) и выполнить lua_client'ом. 192.168.1.11:12345 - адрес и порт малины. Если при открытии не вылетело никаких ошибок, то можно покрутить ручку после запуска.
$ lua_client -s 192.168.1.11:12345 -e gpio-encoder.lua 1 2 3 4 5 4 3 2 1 0 -1 -2 -3 -4 -3 -2 -1 0 1 2 1 0 -1
Работает! Можно использовать.
PS: а нет никакого PS.