编码器与位置检测:从原理到实现
系列:电机控制系列 - 第 8 篇 目标平台:STM32F407ZGT6 阅读时间:25 分钟
前言
位置检测是 FOC 的导航仪:
- ❌ 位置不准 → 电流控制失败
- ❌ 速度不准 → 转速环震荡
本篇目标:
- ✅ 掌握编码器接口
- ✅ 理解位置/速度测量方法
- ✅ 实现电角度计算
一、编码器类型
1.1 增量式编码器
结构:
- A 相、B 相(正交,90°)
- Z 相(零位信号,每转一次)
输出波形:
A 相: ────┐ ┌───┐ ┌───
└───┘ └───┘
B 相: ──────┐ ┌───┐ ┌─
└───┘ └───┘
└─90°─┘
正转:A 超前 B 90°
反转:B 超前 A 90°分辨率:
- 2500 线编码器:每转 2500 个脉冲
- 四倍频后:2500 × 4 = 10000 线
1.2 绝对式编码器
特点:
- 每个位置对应唯一编码
- 无需归零
- 掉电后位置不丢失
接口:
- SSI(同步串行接口)
- BiSS(双向串行接口)
- EnDat(HEIDENHAIN)
1.3 磁编码器
常用芯片:
- AS5047(14-bit,SPI)
- MA730(21-bit,SPI)
- TLE5012(15-bit,SPI)
优点:
- ✅ 体积小
- ✅ 成本低(¥20-50)
- ✅ 抗污染
二、STM32 编码器接口
2.1 硬件编码器模式
定时器:TIM2/TIM3/TIM4/TIM5
配置步骤:
/**
* @brief TIM3 编码器模式初始化
*/
void Encoder_Init(void) {
// 1. 使能时钟
RCC->APB1ENR |= RCC_APB1ENR_TIM3EN;
RCC->AHB1ENR |= RCC_AHB1ENR_GPIOAEN;
// 2. GPIO 配置(PA6/TIM3_CH1, PA7/TIM3_CH2)
GPIOA->MODER |= (2 << GPIO_MODER_MODE6_Pos) | // 复用功能
(2 << GPIO_MODER_MODE7_Pos);
GPIOA->AFR[0] |= (2 << GPIO_AFRL_AFSEL6_Pos) | // AF2
(2 << GPIO_AFRL_AFSEL7_Pos);
// 3. 编码器模式配置
TIM3->SMCR |= (3 << TIM_SMCR_SMS_Pos); // 编码器模式 3(四倍频)
// 4. 输入捕获配置
TIM3->CCMR1 |= (1 << TIM_CCMR1_CC1S_Pos) | // IC1 映射到 TI1
(1 << TIM_CCMR1_CC2S_Pos); // IC2 映射到 TI2
// 5. 极性配置
TIM3->CCER |= TIM_CCER_CC1P | TIM_CCER_CC2P; // 上升沿触发
// 6. 计数器配置
TIM3->ARR = 0xFFFF; // 自动重装载值
TIM3->CR1 |= TIM_CR1_CEN; // 使能计数器
}2.2 读取编码器
/**
* @brief 读取编码器计数
*/
int16_t Encoder_Get_Count(void) {
return (int16_t)TIM3->CNT;
}
/**
* @brief 读取方向
*/
int Encoder_Get_Direction(void) {
return (TIM3->CR1 & TIM_CR1_DIR) ? -1 : 1;
}三、位置测量
3.1 机械角度
/**
* @brief 计算机械角度
*/
float Encoder_Get_Mech_Angle(uint16_t encoder_cnt, uint16_t ppr) {
// ppr: 每转脉冲数(四倍频后)
float angle = (float)encoder_cnt / ppr * 2.0f * M_PI;
// 归一化到 [0, 2π)
angle = fmodf(angle, 2.0f * M_PI);
if (angle < 0) angle += 2.0f * M_PI;
return angle;
}3.2 电角度
/**
* @brief 计算电角度
*/
float Encoder_Get_Elec_Angle(float mech_angle, uint8_t pole_pairs) {
float elec_angle = mech_angle * pole_pairs;
// 归一化到 [0, 2π)
elec_angle = fmodf(elec_angle, 2.0f * M_PI);
if (elec_angle < 0) elec_angle += 2.0f * M_PI;
return elec_angle;
}3.3 多圈计数
/**
* @brief 多圈计数(软件实现)
*/
int32_t Encoder_Get_Multi_Turn(int16_t current_cnt) {
static int16_t last_cnt = 0;
static int32_t total_cnt = 0;
int16_t delta = current_cnt - last_cnt;
// 处理溢出
if (delta > 32767) {
delta -= 65536; // 向下溢出
} else if (delta < -32768) {
delta += 65536; // 向上溢出
}
total_cnt += delta;
last_cnt = current_cnt;
return total_cnt;
}四、速度测量
4.1 M 法(测频法)
原理:单位时间内的脉冲数
/**
* @brief M 法测速
*/
float Encoder_Get_Speed_M(int16_t current_cnt, float dt, uint16_t ppr) {
static int16_t last_cnt = 0;
int16_t delta = current_cnt - last_cnt;
last_cnt = current_cnt;
// 速度(rpm)
float speed_rpm = (float)delta / ppr / dt * 60.0f;
return speed_rpm;
}适用:高速(脉冲数多,精度高)
4.2 T 法(测周法)
原理:脉冲间隔时间
/**
* @brief T 法测速(需要硬件定时器)
*/
float Encoder_Get_Speed_T(uint32_t timer_cnt, float timer_freq, uint16_t ppr) {
// timer_cnt: 相邻两个脉冲间的定时器计数
if (timer_cnt == 0) return 0;
float speed_rpm = timer_freq / timer_cnt / ppr * 60.0f;
return speed_rpm;
}适用:低速(测量精度高)
4.3 M/T 法(混合法)
/**
* @brief M/T 法测速
*/
float Encoder_Get_Speed_MT(int16_t delta_cnt, float dt, uint16_t ppr) {
if (dt == 0) return 0;
float speed_rpm = (float)delta_cnt / ppr / dt * 60.0f;
return speed_rpm;
}五、Z 信号校准
5.1 电角度零点对齐
目的:让编码器零点 = 电角度零点
/**
* @brief Z 信号校准
*/
void Encoder_Calibrate_Z(void) {
// 1. 通 id 电流,iq = 0(d 轴对齐)
float id_ref = 1.0f; // 1A
float iq_ref = 0.0f;
// 2. 等待稳定
HAL_Delay(1000);
// 3. 读取编码器
int16_t cal_cnt = TIM3->CNT;
// 4. 计算零点偏移
encoder_offset = cal_cnt % ENCODER_PPR;
// 5. 保存到 Flash
Flash_Write(ENCODER_OFFSET_ADDR, &encoder_offset, 1);
}5.2 自动校准流程
void Encoder_Auto_Calibrate(void) {
printf("开始编码器校准...\n");
// 1. 施加 id 电流
id_ref = 2.0f;
iq_ref = 0.0f;
// 2. 等待转子对齐
HAL_Delay(2000);
// 3. 多次采样取平均
int32_t sum = 0;
for (int i = 0; i < 100; i++) {
sum += TIM3->CNT;
HAL_Delay(10);
}
encoder_offset = (sum / 100) % ENCODER_PPR;
printf("校准完成,偏移 = %d\n", encoder_offset);
}六、总结
编码器核心要点:
- 增量式编码器 + 四倍频
- STM32 硬件编码器模式
- 电角度 = 机械角度 × 极对数
- Z 信号校准电角度零点
下一篇:PID 控制器设计