旋转编码器

罗大富 BigRich大约 5 分钟ESP32Python

本节课来学习使用 MicroPython 控制旋转编码器。

实验原理

旋转编码器是一种位置传感器,它将旋钮的角位置(旋转)转换为数字信号输出,可用于确定旋钮的转动方向,被广泛应用于各种领域。旋转编码器听起来虽然很陌生,但实际上在我们生活中十分常用,比如鼠标滚轮、汽车音箱的旋钮。

202504221456900

之前,我们学过一个与其类似的元件 电位计,它不同于普通电位器,电位计一般只能旋转大约 3/4 圈,而旋转编码器可以无限旋转,并能精确测量相对位置变化。旋转编码器是电位器的现代数字等效物,并且用途更广泛。

在旋转编码器内部,有一个带有均匀间隔槽孔的圆盘,圆盘与 GND 相连,另外,编码器还有另外两个金属探针 A 和 B,这些引脚将帮助我们确定旋钮的转动方向。

当我们转动编码器的旋钮时,圆盘会随之一起转动。根据转动方向的不同,探针 A 和 B 会先后接触到 GND,并先后产生两个脉冲信号,这两个信号会存在一个 90° 的相位差,形成一种正交编码。当顺时针旋转旋钮时,A 引脚先于 B 引脚接地。当逆时针旋转旋钮时,B 引脚先于 A 引脚接地,具体工作原理可以参考以下两个动图:

顺时针转动
顺时针转动
逆时针转动
逆时针转动

通过监控每个引脚何时连接或断开接地,我们可以确定旋钮旋转的方向。这可以通过简单地观察 A 的状态改变时 B 的状态来完成。

当 A 改变状态时:

  • 如果 B != A,则旋钮为 顺时针转动
  • 如果 B = A,则旋钮为 逆时针转动

旋转编码器模块的引脚排列如下:

  1. VCC 是正电源电压,通常在 3.3 至 5 伏之间。
  2. KEY 是按钮开关的输出(低电平有效)。当按下旋钮时,电压变低。
  3. S2 金属探针引脚,用于确定旋转量
  4. S1 金属探针引脚,用于确定旋转量。
  5. GND 是接地连接。

硬件电路设计

物料清单(BOM 表):

材料名称数量
旋转编码器模块1
OLED 屏幕1
杜邦线(跳线)若干
面包板1

OLED 的 SCK(D0)接开发板 D18、SDA(D1)接 D5、RES 接 D15、DC 接 D2、CS 接 D4

旋转编码器的 S2 引脚接开发板 D26、S1接 D27,KEY 按键没有用到,可以不接。

202504251552354

软件程序设计

1. 计数器

第一个程序,我们来通过代码获取旋转编码器的转动方向,做一个计数器,顺时针旋转则加,逆时针旋转则减

from machine import Pin


# 定义控制引脚
a = Pin(26, Pin.IN)
b = Pin(27, Pin.IN)


# 记录上一个 A 的电平状态
last_state_a = a.value()

# 初始化计数器数值
count = 0


while True:
    # 获取当前 a 的电平状态
    current_state_a = a.value()
    
    # 检测 a 引脚的电平变化, 为避免重复计数,只检测上升沿或者下降沿
    if not current_state_a and last_state_a:
        # 如果 AB 不相等说明顺时针转动,反之则为逆时针
        if b.value() != current_state_a:
            count += 1
            print(f' 顺时针转动, {count}')
        else:
            count -= 1
            print(f' 逆时针转动, {count}')
    
    # 更新上一个 A 的电平状态
    last_state_a = current_state_a

我们也可以使用外部中断的方法来改写以上代码,代码如下:

from machine import Pin


a = Pin(26, Pin.IN)
b = Pin(27, Pin.IN)

count = 0

# 记录上一个 a 引脚状态
last_state_a = a.value()

def a_func(a):
    global count, last_state_a
    # 获取当前的 a 引脚电平
    current_state_a = a.value()
    
    if current_state_a != last_state_a and b.value():
        if current_state_a != b.value():
            count += 1
            print(f" 顺时针旋转, count: {count}")
        else:
            count -= 1
            print(f" 逆时针旋转, count: {count}")
    last_state_a = current_state_a
        
    
a.irq(a_func, Pin.IRQ_FALLING|Pin.IRQ_RISING)

2. 旋转编码器控制菜单

最后,我们使用旋转编码器与 OLED 屏幕实现一个控制菜单的实验,该实验需要用到 SPI 驱动 OLED 液晶屏幕 中的 SSD1306.py 驱动文件,将 SSD1306.py 文件上传到 ESP32 设备中的 libs 目录中即可,代码如下:

from machine import Pin, SoftSPI
from libs.ssd1306 import SSD1306_SPI


# 定义 SOFTSPI 对象
spi = SoftSPI(sck=Pin(18), mosi=Pin(5), miso=Pin(19))
# 创建 OLED 对象
oled = SSD1306_SPI(width=128, height=64, spi=spi, dc=Pin(2), res=Pin(15), cs=Pin(4))
# 定义旋转编码器对象
a = Pin(26, Pin.IN)
b = Pin(27, Pin.IN)


# 记录上一个 A 的电平状态
last_state_a = a.value()

# 定义菜单选项
menu_items = ['Item 1','Item 2','Item 3','Item 4' ]

# 定义当前索引位置
current_item = 0

# 显示菜单的函数
def display_menu(index):
    oled.fill(0)
    oled.text('Menu', 0, 0)
    oled.text('-'*20, 0, 10)
    for i in range(len(menu_items)):
        if i == index:
            oled.text('> ' + menu_items[i], 0, 20 + i * 10)
        else:
            oled.text(menu_items[i], 0, 20 + i * 10)
    oled.show()
    
# 初始化显示屏幕的状态
display_menu(current_item)

while True:
    # 获取当前 a 的电平状态
    current_state_a = a.value()
    
    # 检测 a 引脚的电平变化, 为避免重复计数,只检测上升沿或者下降沿
    if not current_state_a and last_state_a:
        # 如果 AB 不相等说明顺时针转动,反之则为逆时针
        if b.value() != current_state_a:
            current_item = (current_item + 1) % (len(menu_items))
        else:
            current_item = (current_item - 1) % (len(menu_items))
        display_menu(current_item)
    
    # 更新上一个 A 的电平状态
    last_state_a = current_state_a
上次编辑于:
贡献者: 罗大富BigRich