Raspberry Pi Picoで学ぶロータリーエンコーダーの使い方!基本原理と実践コード

目次

はじめに

ロータリーエンコーダーというデバイスをご存じでしょうか?
ロータリーエンコーダーは、回転方向や回転角度を検出するためのデバイスです。
ロボット制御など回転体の状態を精度よく取得したい場面でひろく使われています。

回転センサ比較

ロータリーエンコーダとは別に回転入力を検出するデバイスとしてポテンショメータがあります。
それぞれの特徴と用途を比較します。

基本的な違い

特徴ロータリーエンコーダポテンショメータ
作動原理回転をパルス信号(デジタル)に変換回転角に応じて抵抗値を変化させ、アナログ信号を出力
回転可能範囲制限なし(無限回転可能)制限あり(通常、270°や360°)
出力形式デジタル信号(パルス)アナログ信号
方向検出時計回り(CW)、反時計回り(CCW)の方向検出が可能回転方向の情報は取得不可
精度パルス数に依存。高分解能が可能回転角に応じた連続値(ADCビット数に依存)
位置保持電源オフ時に位置情報は保持されない(初期化が必要)抵抗値として物理的に保持
寿命機械的寿命が長く、摩耗が少ない摩擦や接触部の摩耗により寿命が短い場合がある
応答速度高回転の検出に適している高回転には不向き

動作原理(詳細)

ロータリーエンコーダーは、回転運動をデジタル信号に変換します。以下のように動作します

パルス信号の生成

  • 内部には光学ディスクや磁気ディスク、接触式の接点があります。
  • 回転することでクロック(CLK)とデータ(DT)の2つのパルス信号が生成されます。

方向検出

  • クロック信号とデータ信号の位相差を比較することで、時計回り(CW)か反時計回り(CCW)を判定します。

回転量の計測

パルス数をカウントすることで、回転角度や回転量を測定できます。

選択基準

ロータリーエンコーダーを選択した方が良い場合

無限回転や方向検出が必要な用途

  • ロボットの回転制御
  • メニュー選択やモーターの回転数検出
  • 回転運動を高精度で測定したい場合

ポテンショメータ選択した方が良い場合

範囲内での連続的な位置調整が必要な用途

  • ボリューム調整
  • 光の輝度や音量の調整
  • シンプルで低コストなアナログ入力が必要な場合

配線

OLEDも合わせて配線します

RaspberryPi Picoロータリーエンコーダ
VSYS(39番)VCC
GND(38番)GND
GP0(19番)DT
GP1(20番)CLK
GP2(番)SW
()内はピン番号

ロータリーエンコーダを回すとディスプレイの絵が動く

まずは以下をrotary.pyとしてpicoにアップロード

from machine import Pin
from utime import sleep
import micropython 


class Rotary:
    ROT_CW = 1
    ROT_CCW = 2
    SW_PRESS = 4
    SW_RELEASE = 8
    
    def __init__(self, dt, clk, sw):
        self.dt_pin = Pin(dt, Pin.IN)
        self.clk_pin = Pin(clk, Pin.IN)
        self.sw_pin = Pin(sw, Pin.IN)
        self.last_status = (self.dt_pin.value() << 1) | self.clk_pin.value()
        self.dt_pin.irq(handler=self.rotary_change, trigger=Pin.IRQ_FALLING | Pin.IRQ_RISING)
        self.clk_pin.irq(handler=self.rotary_change, trigger=Pin.IRQ_FALLING | Pin.IRQ_RISING)
        self.sw_pin.irq(handler=self.switch_detect, trigger=Pin.IRQ_FALLING | Pin.IRQ_RISING)
        self.handlers = []
        self.last_button_status = self.sw_pin.value()
        
    def rotary_change(self, pin):
        new_status = (self.dt_pin.value() << 1) | self.clk_pin.value()
        if new_status == self.last_status:
            return
        
        transition = (self.last_status << 2) | new_status
        try:
            if transition == 0b1110:
                micropython.schedule(self.call_handlers, Rotary.ROT_CW)
            elif transition == 0b1101:
                micropython.schedule(self.call_handlers, Rotary.ROT_CCW)
            self.last_status = new_status
        except RuntimeError:
            pass
        
    def switch_detect(self, pin):
        if self.last_button_status == self.sw_pin.value():
            return
        self.last_button_status = self.sw_pin.value()
        if self.sw_pin.value():
            micropython.schedule(self.call_handlers, Rotary.SW_RELEASE)
        else:
            micropython.schedule(self.call_handlers, Rotary.SW_PRESS)
    
    def add_handler(self, handler):
        self.handlers.append(handler)
    
    def call_handlers(self, type):
        for handler in self.handlers:
            handler(type)

main.pyは以下です

from rotary import Rotary
import utime as time
from machine import Pin, I2C 
from ssd1306 import SSD1306_I2C
import framebuf

WIDTH = 128
HEIGHT = 64

buffer = bytearray(b'\x00\x00\x00\x00\x01\xf0\x0f\x00\x06\x860\xb0\x04\x01@\x10\x04\x01\x000\x02\x10\x89 \x02\x05\xd2 \x01\x03\xe0@\x01\x87\xe0\x80\x10~?\x00\x00\x88\x11\x80\x01\x18\x08@\x02><@\x02a\xc3@C\xc1\x81\xe0\x05\x80\x90\x91\x08\x80\x80\x98\x08\x81\xc0\x88\t\x83\xe1\x98)\xe4\x1f\x98\x07\xf8\x0e0\x068\x0c \x02\x18\x080\x02\x88\x08 \x03\x0e0A\x01\x8f\xf8\x80\x00|\x1f\x00\x008\x0c\x00\x00\x0c0\x02\x00\x03\xe0\x00\x00\x00\x80\x00\x01\x00\x00\x04')

fb = framebuf.FrameBuffer(buffer, 32, 32, framebuf.MONO_HLSB)

i2c = I2C(0, scl=Pin(17), sda=Pin(16),freq=200000)

oled = SSD1306_I2C(WIDTH, HEIGHT, i2c)

# clear screen
oled.fill(0)
oled.blit(fb, 64, 32)
oled.show()
time.sleep(1)
oled.fill(0)
rotary = Rotary(0, 1, 2)

val = 0

def rotary_changed(change):
    global val
    if change == Rotary.ROT_CW:
        val += 10
        oled.blit(fb, val, 32)
        oled.show()
        time.sleep(0.2)
        oled.fill(0)
        print(val)
    elif change == Rotary.ROT_CCW:
        val -= 10
        oled.blit(fb, val, 32)
        oled.show()
        time.sleep(0.2)
        oled.fill(0)
        print(val)
    elif change == Rotary.SW_PRESS:
        val += 20
        oled.blit(fb, 64, val)
        oled.show()
        time.sleep(0.2)
        oled.fill(0)
    elif change == Rotary.SW_RELEASE:
        val -= 20
        oled.blit(fb, 64, val)
        oled.show()
        time.sleep(0.2)
        oled.fill(0)
        
rotary.add_handler(rotary_changed)


while True:
    time.sleep(0.1)
よかったらシェアしてね!
  • URLをコピーしました!
  • URLをコピーしました!

コメント

コメントする

目次