传感器 · EC11 旋转编码器(带按键 SW)

EC11 旋转编码器 STM32F103 接线与代码 实测通过

旋转编码器就是一个「带卡点的手摇旋钮」——转一格咔哒一下,单片机就 +1 或 -1,正转加、反转减,按下去还能当按钮用。音量旋钮、菜单滚轮、机床手轮都是这类东西。 本页是 STM32F103C8T6 + EC11 + 0.96 OLED 的完整实机验证记录:接线表、完整代码、工程 ZIP 全部实测通过。 最值得看的是「一个卡点为什么不能靠每个相位沿计数」和「快转为什么会漏计」两段——这两个坑几乎人人会踩。

Quick Facts

  • 型号:EC11 旋转编码器(带按键 SW)
  • 平台:STM32F103C8T6 + VS Code + PlatformIO + Arduino(STM32duino)
  • SKU:待补充
  • 更新:2026-08-16

Verified

实机验证结果

编译与烧录:PlatformIO 输出 Programming Finished → Verified OK → [SUCCESS],烧录成功。

测试动作预期实测结果
上电不动COUNT 保持 0COUNT 一直 0
顺时针转一格COUNT +1,DIR 显示 CW每格 +1
逆时针转一格COUNT -1,DIR 显示 CCW每格 -1
快速连续甩旋钮不漏计慢转快转都不漏
按 SW显示 PRESSED按下 PRESSED,松开 UP
停止 500 msDIR 恢复 ---自动恢复

串口输出示例(115200):

Rotary encoder demo v1.5 starting...
Count: 1 | Dir: CW | SW: UP
Count: 2 | Dir: CW | SW: UP
Count: 3 | Dir: CW | SW: UP
Count: 2 | Dir: CCW | SW: UP
Count: 2 | Dir: --- | SW: PRESSED
Count: 2 | Dir: --- | SW: UP

实机验证环境(每一项都是真正用过的):

项目本项目使用
主控STM32F103C8T6(BluePill)
模块EC11 旋转编码器(带按键 SW)
显示0.96 寸 OLED,SSD1306,128×64,0x3C
开发工具VS Code + PlatformIO
框架Arduino / STM32duino
烧录ST-Link
串口115200
依赖库Adafruit SSD1306、Adafruit GFX Library

未测试的环境不当成支持:本项目没有在 Arduino IDE、Keil、STM32CubeIDE 下编译或验证过,不提供这些环境的支持说明。

About

这个模块干什么

转动旋钮时,内部两个触点 A、B 一前一后地通断,形成一组「正交信号」。STM32 靠 A、B 谁先谁后判断方向,靠转到定位点才计一格,再显示到 OLED 和串口。按键 SW 是常开型,按下接通。

三个术语:

  • 卡点(detent):转一下「咔哒」一下的那个位置,一格就是一个卡点。
  • 正交(quadrature):A、B 两根信号错开四分之一周期,靠先后顺序判断方向。
  • 定位态:旋钮停下时 A、B 同为高或同为低的状态,卡点计数就靠它。

和电位器怎么选:

旋转编码器 EC11电位器
输出数字脉冲,可无限旋转、可计数模拟电压,只能转到头
用途计数、菜单滚轮、音量档位连续调节(如调亮度)
要不要 ADC不要,走 GPIO要接 ADC 读电压

Reference

实用参数与资料

模块参数(本项目实测 / 厂家资料):

项目说明来源
输出A、B 两相正交方波,相位差约 90°资料参考
卡点数常见 20 或 30 卡点 / 圈,以手上模块实际为准资料参考
方向判断A 领先 B 是一种方向,B 领先 A 是另一种方向资料参考
按键 SW旋钮按下时接通,常开型资料参考
上拉多数模块板已带上拉电阻;本项目用 STM32 内部上拉本项目实测
供电3.3 V,与主控共地本项目实测
计数精度一个机械卡点稳定 ±1本项目实测
具体卡点数 / 引脚顺序 / 丝印待确认(按手上模块批次为准)待确认

配套 OLED:0.96 寸 SSD1306,128×64,I2C 地址 0x3C。

Wiring

接线

编码器和 OLED 分开接,一共 9 根线。

编码器:

信号编码器丝印STM32F103C8T6
A 相CLK / APB0
B 相DT / BPB1
按键SWPA0
电源+ / VCC3.3V
地GNDGND

OLED:

信号OLEDSTM32F103C8T6
电源VCC / VDD3.3V
地GNDGND
时钟SCL / SCKPB8
数据SDAPB9

最容易接错的三处(必须单独强调):

  1. OLED 的 I2C 用的是重映射引脚 PB8/PB9,不是默认的 PB7/PB6。 代码里要先 Wire.setSDA(PB9)、Wire.setSCL(PB8) 再 Wire.begin(),接错就读不到屏幕。
  2. 按键不要接 PB2。 PB2 在 STM32F103 上兼作 BOOT1,接东西可能影响启动,所以本项目 SW 改接 PA0。
  3. 必须共地。 编码器、OLED、STM32 的 GND 要接在一起,否则信号对不上。
  4. PA13 / PA14 留给 ST-Link 调试,不要占用。

How it works

工作原理

一个卡点为什么不能靠每个相位沿计数:一个完整的正交周期里有 4 次电平变化。如果每个沿都计一次,转一个卡点就会 +4 或 +2,而不是 +1。要一个卡点只 +1,必须只挑「到达定位点」的那一下来计。本项目用的是「到达定位态(A 和 B 同为高或同为低)才计一次」,配合 2 ms 去抖,实测一个卡点稳定 ±1。

快速旋转为什么会漏计:如果编码器靠主循环轮询,OLED 整屏刷新那几十毫秒里 CPU 读不到 A/B,快转时跳变就会漏掉。解决办法是改用引脚中断——A/B 一跳变就进中断计数,刷屏也挡不住。

为什么读数不会被显示拖慢:中断里只改数字、置一个「脏」标记;主循环里 OLED 至少隔 40 ms 才整屏刷一次,串口至少隔 100 ms 且有变化才打一行,把时间都让给编码器。

Code notes

关键代码说明

1. 把 I2C 指到 PB8 / PB9。 STM32duino 默认 I2C 是 PB7/PB6,必须手动指到重映射引脚,并升到 400 kHz 缩短整屏刷新时间。

Wire.setSDA(PB9);
Wire.setSCL(PB8);
Wire.begin();
Wire.setClock(400000);   // 400kHz

2. 编码器走中断,不在主循环轮询。 A、B 任一引脚跳变就进中断,OLED 刷屏占了几十毫秒也不会漏掉旋钮跳变——这是解决「快转漏计」的关键。

attachInterrupt(ENCODER_A_PIN, encoderISR, CHANGE);
attachInterrupt(ENCODER_B_PIN, encoderISR, CHANGE);

3. 只挑「到达定位态」计一次。 中断里读 A/B 拼成状态,只在到达定位态(A==B)的跳变上计数,并用 2 ms 去抖。转一个卡点正好 ±1。

switch (transition) {
  case 0b0100:  // 01 -> 00  顺时针
  case 0b1011:  // 10 -> 11  顺时针
    if (now - g_lastCountMs < 2UL) break;
    g_state.count += 1;
    g_state.direction = Direction::CW;
    break;
  case 0b1000:  // 10 -> 00  逆时针
  case 0b0111:  // 01 -> 11  逆时针
    if (now - g_lastCountMs < 2UL) break;
    g_state.count -= 1;
    g_state.direction = Direction::CCW;
    break;
}

4. 中断里写、主循环里读的字段全部标 volatile,否则编译器优化后主循环可能一直读到旧值。

5. 按键消抖用轮询 + 20 ms 稳定判定,机械按键抖动期内不改状态。

Code

完整代码

完整代码见模块工程 src/main.cpp(v1.5,实测通过 2026-08-16)。网站页面上贴的就是这一份,一字未改。 依赖库 Adafruit SSD1306 和 Adafruit GFX Library 由 PlatformIO 按 platformio.ini 自动下载,不用手动装。

Troubleshooting

常见问题排查

OLED 完全不亮 / 串口打印 SSD1306 allocation failed → 十有八九是 I2C 引脚或地址。确认 SDA 接 PB9、SCL 接 PB8(不是默认的 PB7/PB6),代码里 setSDA/setSCL 必须在 Wire.begin() 之前调用。OLED 地址常见 0x3C,少数是 0x3D。

转一格 COUNT 跳 2 下或 4 下 → 计数写法不对。用了「每个相位沿都计数」,一个卡点里有 4 次电平变化。改成只在到达定位态(A==B)的跳变上计数。

慢转正常,快速甩旋钮会少计几格 → 编码器在主循环里轮询,被 OLED 刷屏阻塞了。改用 attachInterrupt(..., CHANGE) 把 A/B 都挂中断。

方向反了,顺时针在减 → A、B 两根线对调,或者代码里把 CW/CCW 两组 case 对调。本项目已验证:顺时针 = CW = 加。

按键按下没反应 / 一按就乱跳 → 先确认 SW 接的是 PA0 不是 PB2(PB2 兼作 BOOT1)。乱跳是没消抖,本项目用 20 ms 稳定判定。

串口看不到数据 → 先查当前串口:pio device list,找 CP2102 / CP210x / CH340 对应的那个 COM。需要看串口时在自己电脑的 platformio.ini 里加 monitor_port = COMx 和 monitor_speed = 115200,COMx 换成自己电脑实际识别到的端口,不要照抄我们截图里的 COM 号。正常用 ST-Link 烧录不需要加这两行。

Photos

实机图片

Download

下载

ec11-rotary-encoder-stm32-platformio.zipPlatformIO 工程(platformio.ini + src/main.cpp + README.md),已去掉本机构建路径,解压后 VS Code 打开即可 Build。5 KB
下载 ZIP(0.00 MB)

Video

视频演示

成片制作中。B 站原片 素材\旋转编码器_实机演示.MOV(274 MB)不上站,做好网页版(<5 MB,带 poster)后再补。

Sources

资料来源

  • 接线、代码、实测结果、编译烧录记录:本项目实测,2026-08-16 实机测试
  • EC11 正交输出、卡点数、方向判断、按键类型、模块上拉:资料参考,来自公开资料与常规工程实践,由我们重新整理表述
  • 具体卡点数、引脚顺序:待确认,以手上模块实际批次和丝印为准
  • SKU:待补充