title: 编码器接口 tags: [STM32, TIM, 编码器, 正交解码]
正交编码器输出两路相位差 90° 的脉冲信号:
A相 ──┬───┬───┬───┬───
│ │ │ │
└───┴───┴───┴───
B相 ────┬───┬───┬───┬───
│ │ │ │ │
└───┴───┴───┴───┘
正转时 A 领先 B 90°:
A: ──┬──┬──┬──
│ │ │ │
└──┴──┴──┘
B: ──┴──┬──┬──┬─
│ │ │ │
└──┴──┴──┘
反转时 B 领先 A 90°:
A: ──┴──┬──┬──┬─
│ │ │ │
└──┴──┴──┘
B: ──┬──┬──┬──
│ │ │ │
└──┴──┴──┘
STM32 定时器自带编码器接口,自动解码正交信号:
| 模式 | 计数方式 | 倍频 | 描述 |
|---|---|---|---|
TIM_EncoderMode_TI1 |
仅在 TI1 边沿计数 | ×2 | 只根据 A 相计数 |
TIM_EncoderMode_TI2 |
仅在 TI2 边沿计数 | ×2 | 只根据 B 相计数 |
TIM_EncoderMode_TI12 |
TI1+TI2 双边沿计数 | ×4 | 最高分辨率 |
计数值的正负由两相信号相位差自动判断:
正转 → 计数器递增
反转 → 计数器递减
停止 → 计数器不变
定时器内部结构:
A相(TI1) ──→ 边沿检测 ──→ 编码器控制逻辑 ──→ CNT(递增/递减)
B相(TI2) ──→ 边沿检测 ──→ ↑
编码器模式配置
| 函数 | 描述 |
|---|---|
TIM_EncoderInterfaceConfig(TIMx, TIM_EncoderMode_TI12, TIM_ICPolarity_Rising, TIM_ICPolarity_Rising) |
配置编码器接口模式 |
TIM_GetCounter(TIMx) |
读取当前计数值(返回 uint16_t) |
TIM_SetCounter(TIMx, uint16_t) |
设置计数器值 |
TIM_Cmd(TIMx, ENABLE) |
使能定时器 |
TIM_EncoderInterfaceConfig 参数:
void TIM_EncoderInterfaceConfig(
TIM_TypeDef* TIMx,
uint16_t TIM_EncoderMode, // TIM_EncoderMode_TI1/TI2/TI12
uint16_t TIM_IC1Polarity, // TIM_ICPolarity_Rising/Falling
uint16_t TIM_IC2Polarity
);
TIM_EncoderMode 可选项:
| 宏 | 倍频 | 计数更新条件 |
|---|---|---|
TIM_EncoderMode_TI1 |
×2 | TI1 上升沿+下降沿 |
TIM_EncoderMode_TI2 |
×2 | TI2 上升沿+下降沿 |
TIM_EncoderMode_TI12 |
×4 | TI1+TI2 上升沿+下降沿 |
#include "stm32f10x.h"
void Encoder_Init(void)
{
RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM3, ENABLE);
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE);
GPIO_InitTypeDef GPIO_InitStructure;
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_6 | GPIO_Pin_7;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN_FLOATING; // 浮空输入
GPIO_Init(GPIOA, &GPIO_InitStructure);
// 编码器模式—定时器时基无需PSC分频
TIM_TimeBaseInitTypeDef TIM_TimeBaseStructure;
TIM_TimeBaseStructure.TIM_Prescaler = 0; // 不分频
TIM_TimeBaseStructure.TIM_Period = 65535; // 最大值
TIM_TimeBaseStructure.TIM_ClockDivision = TIM_CKD_DIV1;
TIM_TimeBaseStructure.TIM_CounterMode = TIM_CounterMode_Up;
TIM_TimeBaseInit(TIM3, &TIM_TimeBaseStructure);
// 配置编码器接口: TI1+TI2 ×4 模式
TIM_EncoderInterfaceConfig(TIM3,
TIM_EncoderMode_TI12,
TIM_ICPolarity_Rising, // TI1 不反相
TIM_ICPolarity_Rising // TI2 不反相
);
TIM_Cmd(TIM3, ENABLE);
}
int16_t Encoder_GetCount(void)
{
// 转 int16_t 才能体现正负
return (int16_t)TIM_GetCounter(TIM3);
}
int main(void)
{
Encoder_Init();
while (1) {
int16_t pos = Encoder_GetCount();
// 此处可用串口打印 pos
// 正转 → pos 正数递增
// 反转 → pos 负数递减
Delay_ms(10);
}
}
// 编码器归零
void Encoder_Reset(void)
{
TIM_SetCounter(TIM3, 0);
}
// 读取并自动清零(用于周期性采样)
int16_t Encoder_GetCountAndReset(void)
{
int16_t val = (int16_t)TIM_GetCounter(TIM3);
TIM_SetCounter(TIM3, 0);
return val;
}
// 外部中断方式(不推荐):
// 分别配置 A/B 相为外部中断,在中断中判断另一相电平
// 缺点:占用 CPU,高频时丢步
// 编码器接口方式(推荐):
// 硬件自动加减,不占用 CPU,不丢步
// 缺点:需要占用定时器通道
读出值要转 int16_t:TIM_GetCounter() 返回 uint16_t(0–65535),编码器可能递减到负数。强制转为 int16_t 才能正确体现正反转
PSC 必须为 0:编码器模式下 PSC 必须为 0(不分频),因为计数器靠外部脉冲计数,不能用内部时钟分频。设 PSC>0 会导致完全不计数或计数异常
最大值 65535 → -1 回绕:反转时计数器从 0 递减到 65535,int16_t 解释为 -1。这是正常的,不要当成错误
TIM_ICPolarity 含义不同:编码器模式下,极性参数 TIM_ICPolarity_Falling 的含义变为反相输入信号,而不是捕获边沿。设为 Falling 相当于交换 A/B 相
GPIO 必须为浮空或上拉输入:不要配成输出模式。编码器模块输出的是开漏/推挽信号,通常用浮空输入
和外部中断的区别:
单次最大计数范围:16位定时器 ±32767(int16_t)。如果需要更大范围,在代码中扩展为 int32_t:
volatile int32_t encoder_pos = 0;
volatile int16_t last_cnt = 0;
void Encoder_Poll(void)
{
int16_t cur = (int16_t)TIM_GetCounter(TIM3);
int16_t diff = cur - last_cnt;
last_cnt = cur;
encoder_pos += diff; // 自动处理溢出回绕
}
多个编码器:STM32F103C8T6 只有一个定时器支持编码器模式(TIM3)。TIM2 不支持编码器模式(仅 TIM3/4 有编码器功能)
纠正:TIM2、TIM3、TIM4、TIM5 都支持编码器模式(STM32F1 通用定时器均支持)。高级定时器 TIM1/TIM8 不支持。