09-编码器接口.md 6.5 KB


title: 编码器接口 tags: [STM32, TIM, 编码器, 正交解码]

created: 2026-07-06

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) ──→ 边沿检测 ──→                    ↑
                                        编码器模式配置

API 表格

核心函数

函数 描述
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 上升沿+下降沿

代码示例

编码器读取位置 (TIM3_CH1=PA6, TIM3_CH2=PA7)

#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,不丢步
//   缺点:需要占用定时器通道

常见坑点

  1. 读出值要转 int16_tTIM_GetCounter() 返回 uint16_t(0–65535),编码器可能递减到负数。强制转为 int16_t 才能正确体现正反转

  2. PSC 必须为 0:编码器模式下 PSC 必须为 0(不分频),因为计数器靠外部脉冲计数,不能用内部时钟分频。设 PSC>0 会导致完全不计数或计数异常

  3. 最大值 65535 → -1 回绕:反转时计数器从 0 递减到 65535,int16_t 解释为 -1。这是正常的,不要当成错误

  4. TIM_ICPolarity 含义不同:编码器模式下,极性参数 TIM_ICPolarity_Falling 的含义变为反相输入信号,而不是捕获边沿。设为 Falling 相当于交换 A/B 相

  5. GPIO 必须为浮空或上拉输入:不要配成输出模式。编码器模块输出的是开漏/推挽信号,通常用浮空输入

  6. 和外部中断的区别

    • 外部中断:需要 CPU 参与每条边沿,高频(>10kHz)必定丢步
    • 编码器接口:硬件自动处理,可应对 >100kHz 编码器信号
  7. 单次最大计数范围: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; // 自动处理溢出回绕
    }
    
  8. 多个编码器:STM32F103C8T6 只有一个定时器支持编码器模式(TIM3)。TIM2 不支持编码器模式(仅 TIM3/4 有编码器功能)

纠正:TIM2、TIM3、TIM4、TIM5 都支持编码器模式(STM32F1 通用定时器均支持)。高级定时器 TIM1/TIM8 不支持。