共计 1614 个字符,预计需要花费 5 分钟才能阅读完成。
编码器作为电机的 ” 眼睛 ”,能实时反馈转子位置和速度,是闭环控制的核心传感器。本文将以最常用的 310 直流电机增量式编码器为例,手把手教你在 Arduino 平台上实现精准测速。

一、硬件准备篇
1. 认识 310 编码器
310 电机通常配备正交增量式编码器,其核心参数如下:
– 分辨率:每转 500 线(500 CPR)
– 输出信号:A/ B 两相正交脉冲 + Z 相零点信号
– 工作电压:5V DC
引脚定义(面向轴端看):
1 — A 相(黄线)2 — B 相(绿线)3 — Z 相(蓝线)4 — VCC(红线)5 — GND(黑线)
2. 接线方案
推荐使用 Arduino UNO 的 D2/D3 引脚(支持外部中断)连接编码器:
编码器 Arduino
------------------
VCC → 5V
GND → GND
A 相 → D2(INT0)B 相 → D3(INT1)
硬件设计要点:
– 信号线必须使用双绞线(A/ B 相绞合)
– 每路信号加 10kΩ 上拉电阻到 5V
– 电源并联 100μF 电容抗干扰
二、软件实现篇
1. 基础代码框架
#include <Arduino.h>
// 编码器参数
constexpr uint16_t ENCODER_PPR = 500; // 每转脉冲数
volatile int32_t g_nPulseCount = 0; // 32 位脉冲计数器
// 中断服务函数
IRAM_ATTR void handleEncoderA() {
static uint32_t ulLastTime = 0;
uint32_t ulNow = micros();
// 20ms 消抖处理
if(ulNow - ulLastTime > 20000) {g_nPulseCount += digitalRead(3) ? -1 : 1;
ulLastTime = ulNow;
}
}
void setup() {pinMode(2, INPUT_PULLUP);
pinMode(3, INPUT_PULLUP);
attachInterrupt(0, handleEncoderA, RISING);
Serial.begin(115200);
}
2. 转速计算算法
在 loop() 中添加:
void loop() {
static uint32_t ulLastCalcTime = 0;
static int32_t nLastPulse = 0;
if(millis() - ulLastCalcTime >= 100) { // 100ms 采样周期
noInterrupts();
int32_t nDelta = g_nPulseCount - nLastPulse;
nLastPulse = g_nPulseCount;
interrupts();
// RPM = (Δ 脉冲×60×1000) / (PPR×采样周期 ms)
float fRPM = (nDelta * 60000.0) / (ENCODER_PPR * 100);
Serial.println(fRPM);
ulLastCalcTime = millis();}
}
三、避坑指南
1. 抗干扰措施
- 信号线必须与电机电源线分开走线
- 采用屏蔽电缆时,屏蔽层单端接地
- 在 Arduino 电源入口加 π 型滤波器(10μF+0.1μF)
2. 中断优化技巧
- 中断函数内禁止使用 Serial.print()
- 将 digitalRead() 替换为直接端口访问(如 PORTD & 0x04)
- 必要时关闭中断其他中断(timer0 等)
3. 计数器溢出预防
对于长时间运行场景:
// 在中断函数中处理溢出
if(g_nPulseCount > INT32_MAX-1000) {g_nPulseCount -= 10000; // 定期归零}
四、进阶思考
-
四倍频原理:通过同时捕获 A / B 相的上升沿和下降沿,将分辨率提升 4 倍(500CPR→2000 步 / 转)
-
方案对比:
- 中断法:实现简单但占用 CPU 资源
- 硬件计数器(如 STM32 的 TIM 编码器模式):零 CPU 开销,但需要特定硬件支持
建议尝试用 ESP32 的 PCNT 模块实现硬件计数,能同时兼顾性能和易用性。通过这个项目,你不仅能掌握编码器的基础应用,还能深入理解嵌入式系统中的实时信号处理思想。
正文完
发表至: 未分类
近一天内
