360度旋转编码器模块 — 代码例程
例程一:Arduino 平台(轮询法 + 状态机消抖)
#define PIN_A 2 // A相
#define PIN_B 3 // B相
#define PIN_BTN 4 // 按键
volatile int encoderCount = 0;
int lastCount = 0;
void setup() {
Serial.begin(115200);
pinMode(PIN_A, INPUT_PULLUP);
pinMode(PIN_B, INPUT_PULLUP);
pinMode(PIN_BTN, INPUT_PULLUP);
Serial.println("EC11 Encoder Demo Ready");
}
void loop() {
static uint8_t lastState = 0b00;
uint8_t a = digitalRead(PIN_A);
uint8_t b = digitalRead(PIN_B);
uint8_t state = (a << 1) | b;
if (state != lastState) {
switch ((lastState << 2) | state) {
case 0b0001:
case 0b0111:
case 0b1110:
case 0b1000:
encoderCount++;
break;
case 0b0010:
case 0b1011:
case 0b1101:
case 0b0100:
encoderCount--;
break;
}
lastState = state;
if (encoderCount != lastCount) {
Serial.print("Encoder: ");
Serial.println(encoderCount);
lastCount = encoderCount;
}
}
static unsigned long lastBtnTime = 0;
static bool lastBtnState = HIGH;
bool btn = digitalRead(PIN_BTN);
if (btn != lastBtnState) {
lastBtnTime = millis();
lastBtnState = btn;
}
if ((millis() - lastBtnTime) > 30) { // 30ms消抖
if (btn == LOW && lastBtnState == LOW) {
Serial.println("Click!");
while (digitalRead(PIN_BTN) == LOW);
lastBtnState = HIGH;
}
}
}
例程二:Arduino 平台(中断法,响应更快)
#define PIN_A 2 // 必须接中断引脚 (UNO: D2=INT0)
#define PIN_B 3
#define PIN_BTN 4
volatile int count = 0;
void setup() {
Serial.begin(115200);
pinMode(PIN_A, INPUT_PULLUP);
pinMode(PIN_B, INPUT_PULLUP);
pinMode(PIN_BTN, INPUT_PULLUP);
attachInterrupt(digitalPinToInterrupt(PIN_A), encoderISR, CHANGE);
Serial.println("EC11 Interrupt Demo Ready");
}
void loop() {
static int lastCount = 0;
if (count != lastCount) {
Serial.print("Count: ");
Serial.println(count);
lastCount = count;
}
if (digitalRead(PIN_BTN) == LOW) {
delay(20); // 消抖
if (digitalRead(PIN_BTN) == LOW) {
Serial.println("Click!");
while (digitalRead(PIN_BTN) == LOW); // 等释放
}
}
}
void encoderISR() {
if (digitalRead(PIN_A) == digitalRead(PIN_B)) {
count++; // 同相 → CW
} else {
count--; // 异相 → CCW
}
}
例程三:STM32 HAL 库(定时器编码器模式)
#include "main.h"
TIM_HandleTypeDef htim2;
UART_HandleTypeDef huart2;
int32_t encoderCount = 0;
uint8_t buttonPressed = 0;
void HAL_GPIO_EXTI_Callback(uint16_t GPIO_Pin) {
if (GPIO_Pin == GPIO_PIN_2) {
HAL_Delay(30);
if (HAL_GPIO_ReadPin(GPIOA, GPIO_PIN_2) == GPIO_PIN_RESET) {
buttonPressed = 1;
}
}
}
int main(void) {
HAL_Init();
SystemClock_Config();
MX_GPIO_Init();
MX_TIM2_Init();
MX_USART2_UART_Init();
HAL_TIM_Encoder_Start(&htim2, TIM_CHANNEL_ALL);
printf("STM32 EC11 Encoder Demo Ready\r\n");
int32_t lastCount = 0;
while (1) {
encoderCount = (int32_t)__HAL_TIM_GET_COUNTER(&htim2);
if (encoderCount != lastCount) {
printf("Encoder: %ld\r\n", encoderCount);
lastCount = encoderCount;
}
if (buttonPressed) {
buttonPressed = 0;
printf("Button Clicked!\r\n");
}
HAL_Delay(10);
}
}
例程四:STM32 HAL 库(GPIO轮询 + 状态机,无定时器占用)
#include "main.h"
typedef struct {
GPIO_TypeDef* portA;
uint16_t pinA;
GPIO_TypeDef* portB;
uint16_t pinB;
uint8_t lastState; // 上次AB状态 (bit1=A, bit0=B)
int32_t count;
} EC11_Encoder;
EC11_Encoder encoder = {
.portA = GPIOA, .pinA = GPIO_PIN_0,
.portB = GPIOA, .pinB = GPIO_PIN_1,
.lastState = 0,
.count = 0
};
int8_t EC11_Poll(EC11_Encoder* enc) {
uint8_t a = (HAL_GPIO_ReadPin(enc->portA, enc->pinA) == GPIO_PIN_SET) ? 1 : 0;
uint8_t b = (HAL_GPIO_ReadPin(enc->portB, enc->pinB) == GPIO_PIN_SET) ? 1 : 0;
uint8_t state = (a << 1) | b;
int8_t result = 0;
if (state != enc->lastState) {
switch ((enc->lastState << 2) | state) {
case 0b0001: case 0b0111: case 0b1110: case 0b1000:
enc->count++;
result = 1;
break;
case 0b0010: case 0b1011: case 0b1101: case 0b0100:
enc->count--;
result = -1;
break;
default:
break;
}
enc->lastState = state;
}
return result;
}
int32_t EC11_GetCount(const EC11_Encoder* enc) {
return enc->count;
}
void EC11_ResetCount(EC11_Encoder* enc) {
enc->count = 0;
}
int main(void) {
HAL_Init();
SystemClock_Config();
MX_GPIO_Init();
MX_USART2_UART_Init();
printf("EC11 Polling Demo Ready\r\n");
int32_t lastCount = 0;
while (1) {
EC11_Poll(&encoder);
int32_t cnt = EC11_GetCount(&encoder);
if (cnt != lastCount) {
printf("Count: %ld\r\n", cnt);
lastCount = cnt;
}
HAL_Delay(1); // 1ms 轮询周期
}
}
例程五:MicroPython (ESP32 / Raspberry Pi Pico)
"""
360°旋转编码器 EC11 — MicroPython 例程
接线:A→GPIO14, B→GPIO15, BTN→GPIO16, VCC→3.3V, GND→GND
"""
from machine import Pin
import time
PIN_A = 14
PIN_B = 15
PIN_BTN = 16
a = Pin(PIN_A, Pin.IN, Pin.PULL_UP)
b = Pin(PIN_B, Pin.IN, Pin.PULL_UP)
btn = Pin(PIN_BTN, Pin.IN, Pin.PULL_UP)
count = 0
last_state = (a.value() << 1) | b.value()
print("EC11 MicroPython Demo Ready")
def handle_button(pin):
"""按键中断回调"""
time.sleep_ms(30) # 消抖
if btn.value() == 0:
print("Click!")
# 配置按键中断(下降沿)
btn.irq(trigger=Pin.IRQ_FALLING, handler=handle_button)
while True:
state = (a.value() << 1) | b.value()
if state != last_state:
transition = (last_state << 2) | state
if transition in (0b0001, 0b0111, 0b1110, 0b1000):
count += 1
elif transition in (0b0010, 0b1011, 0b1101, 0b0100):
count -= 1
last_state = state
print(f"Count: {count}")
time.sleep_ms(1)
常见问题排查
| 现象 |
可能原因 |
解决方法 |
| 旋转无反应 |
上拉电阻缺失/VCC未接 |
检查接线;确认模块板载上拉是否正常 |
| 方向时对时错 |
软件消抖不足 |
使用状态机法替代简单延时 |
| 转一圈计数不是20 |
不同批次脉冲数不同 |
实测后调整每圈脉冲系数 |
| 中断法频繁误触发 |
触点抖动引入噪声 |
A/B引脚各对地加100nF电容 |
| 按键偶尔不响应 |
消抖时间不当 |
适当增加消抖延时至30~50ms |