🎃
ロータリーエンコーダー買ってみた
aliexpressで購入したロータリーエンコーダーは誤作動が多く使いにくかったので、先日共立電子で見かけたロータリーエンコーダーを購入しました。
追記
共立電子もクソアイテムを売ってやがります。
クソアイテムを掴まされて数時間を無駄にするよりも、多少高くても秋月電子で購入する方が良いです。
製品情報
- アルプスアルパイン社
- インクリメンタル型 25ステップ
kicad情報
シンボル
フットプリント
サンプルコード
クリックで展開
main.py
# main.py
from machine import Pin,Timer
import machine
import utime
from tm1637 import TM1637
ledPin = Pin(25,Pin.OUT)
ledPin.off()
aPin = Pin(28,Pin.IN,Pin.PULL_UP)
bPin = Pin(27,Pin.IN,Pin.PULL_UP)
preState = (1, 1) # aPinとbPinは初期状態では(HIGH,HIGH)
last = utime.ticks_ms()
direction:int = -1 #-1:左,1:右,0:回転なし
temp = 0
# TM1637
tmClkPin=Pin(19,Pin.IN)
tmDioPin=Pin(20,Pin.IN)
tm=TM1637(clk=tmClkPin,dio=tmDioPin)
# エンコーダーの状態変化時に呼ばれるコールバック関数
def encoder_callback(pin:Pin):
global preState,count,direction,last,temp
now=utime.ticks_ms()
# チャタリング防止のため、前回割り込み時との時間差が10ms以下の時はアクションしない
if utime.ticks_diff(now,last)<10:
return
# しばらく放置した後は、directionとpreStateを初期化する
if utime.ticks_diff(now,last)>500:
direction=0
preState=(1,1)
last = now
curState = (aPin.value(),bPin.value())
if preState==(0,0):
if curState==(0,0):
direction = direction+0
elif curState==(0,1):
direction = direction+1
elif curState==(1,0):
direction = direction-1
elif curState==(1,1):
return
elif preState==(0,1):
if curState==(0,0):
direction = direction-1
elif curState==(0,1):
direction = direction+0
elif curState==(1,0):
return
elif curState==(1,1):
direction = direction+1
elif preState==(1,0):
if curState==(0,0):
direction = direction+1
elif curState==(0,1):
return
elif curState==(1,0):
direction = direction+0
elif curState==(1,1):
direction = direction-1
elif preState==(1,1):
if curState==(0,0):
return
elif curState==(0,1):
direction = direction-1
elif curState==(1,0):
direction = direction+1
elif curState==(1,1):
direction = direction+0
preState = curState
if direction<0:
print('left')
temp=temp-1
elif direction>0:
print('right')
temp=temp+1
print(temp)
tm.number(temp)
aPin.irq(trigger=Pin.IRQ_FALLING|Pin.IRQ_RISING,handler=encoder_callback)
bPin.irq(trigger=Pin.IRQ_FALLING|Pin.IRQ_RISING,handler=encoder_callback)
解説
ロータリーエンコーダーからはAとB二つの端子が出ており、タクトスイッチと同様にHIGH or LOWのシグナルを取得できます。
プルアップ抵抗をつけてやる(ラズパイやarduinoなら内部プルアップ抵抗が使えます)点やチャタリング対策が必要な点などは、タクトスイッチと同じです。
回転した時に割り込みでコールバック関数を呼びたいので、aPinとbPinにはFalligとRisingでコールバック関数を呼ぶようにしました。
右回転か左回転を判別するには、直前のA相とB相の状態と今のA相とB相の状態を呼ぶ必要があります。
状態としては、(aピン,bピン)=(0,0)or(0,1)or(1,0)or(1,1)の4通り。直前状態と直後状態の組み合わせは4*4=16通りあります。
それぞれの場合において、右回転or左回転を判断してあげれば良いでしょう。(なお、(0,0)→(1,1)に移行するといった通常はありえない変化も含みます)
改良
実際はそれほど綺麗な変化をするわけではなく、動作も100%安定ではありません
そこで、「右回りor左回りのパターンを3回検知したら、右回りor左回りを1カウントする」というふうに改良しました。
これだと、反応性は多少悪くなりますが、誤動作をする確率はグッと減ります。
改良版
main.py
# main.py
from machine import Pin
import utime
from tm1637 import TM1637
aPin = Pin(28,Pin.IN,Pin.PULL_UP)
bPin = Pin(27,Pin.IN,Pin.PULL_UP)
# TM1637
tmClkPin=Pin(19,Pin.IN)
tmDioPin=Pin(20,Pin.IN)
tm=TM1637(clk=tmClkPin,dio=tmDioPin)
def encoder_callback(pin:Pin):
global preState,count,direction,last,temp
now=utime.ticks_ms()
# チャタリング防止のため、前回割り込み時との時間差が10ms以下の時はアクションしない
if utime.ticks_diff(now,last)<10:
return
# しばらく放置した後は、directionとpreStateを初期化する
if utime.ticks_diff(now,last)>500:
direction=0
preState=(1,1)
last = now
curState = (aPin.value(),bPin.value())
if preState==(0,0):
if curState==(0,0):
direction = direction+0
elif curState==(0,1):
direction = direction+1
elif curState==(1,0):
direction = direction-1
elif curState==(1,1):
return
elif preState==(0,1):
if curState==(0,0):
direction = direction-1
elif curState==(0,1):
direction = direction+0
elif curState==(1,0):
return
elif curState==(1,1):
direction = direction+1
elif preState==(1,0):
if curState==(0,0):
direction = direction+1
elif curState==(0,1):
return
elif curState==(1,0):
direction = direction+0
elif curState==(1,1):
direction = direction-1
elif preState==(1,1):
if curState==(0,0):
return
elif curState==(0,1):
direction = direction-1
elif curState==(1,0):
direction = direction+1
elif curState==(1,1):
direction = direction+0
preState = curState
if direction<0:
print('left')
temp=temp-1
elif direction>0:
print('right')
temp=temp+1
print(temp)
tm.number(temp)
'''
def encoderToggle(_):
global count, preState
curState = (aPin.value(),bPin.value())
if preState == curState:
pass
if curState == (1,1):count = count + 1
elif curState == (1,0):count = count - 1
elif curState == (0,1):count = count - 1
elif curState == (0,0):count = count + 1
print(f'{curState},{count}')
preState = curState
'''
rightPatterns:tuple = (
((1,1),(1,0),(0,0)),
((1,0),(0,0),(0,1)),
((0,0),(0,1),(1,1)),
((0,1),(1,1),(1,0))
)
leftPatterns:tuple = (
((1,1),(0,1),(0,0)),
((0,1),(0,0),(1,0)),
((0,0),(1,0),(1,1)),
((1,0),(1,1),(0,1))
)
count:int = 0
prepre:tuple = (0,0)
pre:tuple = (0,0)
def encoderToggle(_):
global count, prepre,pre
cur = (aPin.value(),bPin.value())
if cur == pre:
return
pattern = (prepre,pre,cur)
if pattern in leftPatterns:
count = count-1
print('left')
elif pattern in rightPatterns:
count = count+1
print('right')
prepre = pre
pre = cur
tm.number(count)
aPin.irq(trigger=Pin.IRQ_FALLING|Pin.IRQ_RISING,handler=encoderToggle)
bPin.irq(trigger=Pin.IRQ_FALLING|Pin.IRQ_RISING,handler=encoderToggle)
参考
Discussion