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

之前,我们学过一个与其类似的元件 电位计,它不同于普通电位器,电位计一般只能旋转大约 3/4 圈,而旋转编码器可以无限旋转,并能精确测量相对位置变化。旋转编码器是电位器的现代数字等效物,并且用途更广泛。
在旋转编码器内部,有一个带有均匀间隔槽孔的圆盘,圆盘与 GND 相连,另外,编码器还有另外两个金属探针 A 和 B,这些引脚将帮助我们确定旋钮的转动方向。

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


通过监控每个引脚何时连接或断开接地,我们可以确定旋钮旋转的方向。这可以通过简单地观察 A 的状态改变时 B 的状态来完成。
当 A 改变状态时:
- 如果
B != A
,则旋钮为顺时针转动

- 如果
B = A
,则旋钮为逆时针转动

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

VCC
是正电源电压,通常在 3.3 至 5 伏之间。KEY
是按钮开关的输出(低电平有效)。当按下旋钮时,电压变低。S2
金属探针引脚,用于确定旋转量S1
金属探针引脚,用于确定旋转量。GND
是接地连接。
硬件电路设计
物料清单(BOM 表):
材料名称 | 数量 |
---|---|
旋转编码器模块 | 1 |
OLED 屏幕 | 1 |
杜邦线(跳线) | 若干 |
面包板 | 1 |
OLED 的 SCK(D0)接开发板 D18、SDA(D1)接 D5、RES 接 D15、DC 接 D2、CS 接 D4
旋转编码器的 S2 引脚接开发板 D26、S1接 D27,KEY 按键没有用到,可以不接。

软件程序设计
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