8TIM编码器接口

STM32 定时器(TIM)的编码器接口模式(Encoder Interface)

把旋转编码器的 A、B 两路正交脉冲直接交给 TIM,TIM 自动判断方向并让 CNT(Counter,计数器)加一或减一。

你之前学的"外部时钟模式"是来了一个脉冲就数一下,而编码器接口是在此基础上进一步做到:

不仅数,还能判断你往哪边转。

一、先把旋转编码器想象成什么

你之前应该已经接触过这种旋转编码器:

复制代码
        旋转编码器
       ┌──────────┐
       │          │
       │    ○     │ ← 旋钮
       │          │
       └──────────┘
          │    │
          A    B

它通常会输出两路数字信号:

复制代码
A: ────┐    ┌────┐    ┌────
       └────┘    └────┘

B: ─────┐    ┌────┐    ┌────
         └────┘    └────┘

重点来了:

A 和 B 不是同时变化的,而是错开四分之一周期。

这就叫:

正交信号(Quadrature Signal,正交信号)

这个"错开"非常重要,因为:

错开的先后顺序可以用来判断旋转方向。


二、为什么两根线就能知道方向?

先假设顺时针旋转。

可能出现:

复制代码
A: 0 → 1 → 1 → 0 → 0
B: 0 → 0 → 1 → 1 → 0

可以看到:

A 先变化,B 后变化。

而反方向可能变成:

复制代码
A: 0 → 0 → 1 → 1 → 0
B: 0 → 1 → 1 → 0 → 0

这次:

B 先变化,A 后变化。

所以 MCU 根本不需要"猜"。

它只需要观察:

复制代码
A领先B → 一个方向
B领先A → 另一个方向

于是就可以实现:

复制代码
顺时针:
CNT + 1
CNT + 1
CNT + 1
CNT + 1

逆时针:
CNT - 1
CNT - 1
CNT - 1
CNT - 1

这就是你图片里面说的:

"根据编码器旋转产生的正交信号脉冲,自动控制 CNT 自增或自减"


三、这就是"编码器接口"的核心

你图片第一句:

Encoder Interface 编码器接口

它实际上就是 TIM 专门给旋转编码器准备的一种工作模式。

以前你学:

  1. 定时器内部时钟

    内部时钟

    TIM

    CNT不断计数

例如:

复制代码
72MHz → PSC → CNT

用来做:

  • 定时

  • 延时

  • PWM

  • 定时中断


  1. 外部时钟模式

你前面刚学过:

复制代码
外部脉冲
   ↓
 TIM
   ↓
 CNT++

比如红外传感器:

复制代码
红外传感器
     ↓
   一个脉冲
     ↓
   TIM2
     ↓
   CNT + 1

  1. 编码器接口模式

现在进一步:

复制代码
        A ─────┐
               ↓
            TIM编码器接口
               ↑
        B ─────┘
               ↓
              CNT
          ↙           ↘
     顺时针             逆时针
      +1                  -1

所以你可以把它理解成:

编码器接口 = "两个输入 + 定时器自动判断方向 + 自动修改 CNT"


四、你的图片第二句话非常重要

图片说:

编码器接口可接收增量(正交)编码器的信号

这里的关键词:

增量编码器

Incremental Encoder,增量式编码器

它不是直接告诉 STM32:

复制代码
"我现在位于 125°"

而是告诉 STM32:

复制代码
"我又转了一点"

所以 MCU 自己通过计数:

复制代码
转一下 → CNT + 1
再转一下 → CNT + 1
......

得到当前位置。

所以:

复制代码
CNT = 0

转一格

复制代码
CNT = 1

再转一格:

复制代码
CNT = 2

逆方向:

复制代码
CNT = 1
CNT = 0
CNT = -1

于是:

CNT 就可以间接表示编码器的相对位置。


五、为什么说"每个高级定时器和通用定时器都有1个编码器接口"?

STM32F103里面,TIM基本上可以理解成:

复制代码
TIM1 → 编码器接口
TIM2 → 编码器接口
TIM3 → 编码器接口
TIM4 → 编码器接口

也就是说:

编码器接口不是一个独立外设。

它本质上就是:

TIM内部提供的一种特殊计数模式。

这和你之前学的东西联系起来就非常清楚了。

TIM实际上就是一个:

复制代码
"超级计数器"

它可以数:

复制代码
内部时钟
外部脉冲
编码器脉冲

然后根据不同模式决定:

复制代码
CNT怎么变化

六、最重要的一句话:编码器接口使用了输入捕获通道1和通道2

你图片最后一句:

两个输入引脚借用了输入捕获的通道1和通道2

这个特别值得你理解。

因为你之前已经学过:

复制代码
CH1 → 输入捕获通道1
CH2 → 输入捕获通道2

例如:

复制代码
PA0 → TIM2_CH1
PA1 → TIM2_CH2

那么编码器模式下:

复制代码
编码器A → CH1
编码器B → CH2

也就是说:

复制代码
          TIM2
     ┌─────────────┐
A ──→│ CH1         │
     │             │
B ──→│ CH2         │
     │             │
     │   编码器逻辑 │
     │      ↓      │
     │     CNT     │
     └─────────────┘

七、注意:这里不是让你真的进行"输入捕获"

这里很容易产生一个误区。

你可能会想:

"既然用了输入捕获通道,那是不是跟我之前学习的输入捕获一样,会把 CCR1、CCR2 里面装时间?"

不是。

编码器接口虽然:

借用了 CH1 和 CH2 输入

但它们承担的任务变了。

普通输入捕获:

复制代码
外部边沿
   ↓
输入捕获
   ↓
记录 CNT 当前值
   ↓
CCR1

这是为了:

测时间 / 测周期 / 测频率

而编码器模式:

复制代码
A、B两路信号
      ↓
  编码器接口逻辑
      ↓
判断方向
      ↓
CNT++
或
CNT--

这是为了:

测位置、方向、转动量

所以一定要区分。


八、编码器模式最核心的东西其实只有三个

你以后看到编码器接口代码,可以先抓这三个:

① CH1

接编码器 A

② CH2

接编码器 B

③ CNT

记录旋转位置

比如:

复制代码
A → TIM2_CH1
B → TIM2_CH2

然后:

复制代码
顺时针旋转

CNT:
100
101
102
103
104

逆时针:

复制代码
104
103
102
101
100

所以你最后甚至可以直接:

复制代码
TIM_GetCounter(TIM2);

把当前位置读出来。


九、那么"速度"怎么来的?

这里还有一个很漂亮的地方。

假设:

复制代码
1秒钟 CNT增加100

说明:

复制代码
转速 = 100个计数单位 / 秒

如果:

复制代码
1秒钟 CNT增加500

说明:

复制代码
转得更快

所以:

位置可以通过 CNT 得到,速度可以通过单位时间内 CNT 的变化量得到。

例如:

复制代码
当前 CNT = 500

过 100 ms:

复制代码
CNT = 550

那么:

复制代码
100ms增加50

就可以进一步计算转速。

所以编码器接口非常适合:

  • 电机位置控制

  • 电机测速

  • 旋钮

  • 云台

  • 小车轮子

  • 机械臂

  • 编码器电机


十、和你之前学的外部时钟模式对比一下

这两个你现在一定要分清。

外部时钟模式

例如你之前的红外传感器:

复制代码
传感器
   │
   │ 脉冲
   ↓
 TIM
   ↓
 CNT++

它回答的问题是:

"来了多少个脉冲?"


编码器接口

复制代码
A ───┐
     ├──→ TIM
B ───┘
      ↓
   判断方向
      ↓
 CNT++ / CNT--

它回答的问题是:

"转了多少,以及往哪个方向转?"


十一、你可以把 TIM 的几个模式串起来了

你学到这里,其实 TIM 的知识已经形成一个很完整的体系:

复制代码
                TIM
                 │
       ┌─────────┼──────────┐
       ↓         ↓          ↓
   内部时钟    外部时钟    编码器接口
       │         │          │
       ↓         ↓          ↓
    定时/PWM    数脉冲     数脉冲+方向
       │         │          │
       ↓         ↓          ↓
      CNT       CNT       CNT++/CNT--

再往里面细分:

复制代码
TIM
│
├── 基本定时
│
├── 输出比较
│
├── PWM
│
├── 输入捕获
│
├── 外部时钟
│
└── 编码器接口 ← 你现在学到这里

所以你现在真正应该建立起来的一个认识是:

TIM不是单纯的"定时器",它本质上是一个带有各种输入输出功能的计数系统。

正交编码器

STM32到底凭什么知道旋转编码器是在正转还是反转?

看 A 相和 B 相谁先变化。


1.先看左边

编码器有两路信号:

复制代码
A相
B相

它们长这样

复制代码
正转:

A:  ────┐    ┌────┐    ┌────
         └────┘    └────┘

B:  ───────┐    ┌────┐    ┌───
            └────┘    └────┘

你仔细看:

正转的时候,A相比B相先变化。

反转则反过来:

复制代码
反转:

A:  ───────┐    ┌────┐
            └────┘

B:  ────┐    ┌────┐    ┌────
         └────┘    └────┘

这时候:

B相比A相先变化。

所以编码器接口其实就在干一件事:

观察 A、B 两路信号的先后关系。

  1. 右边那个表就是"判断规则"

比如正转的第一个:

A相 ↑,B相低电平

这里的:

  • ↑ = 上升沿

  • ↓ = 下降沿

所以:

复制代码
A相发生上升沿
↓
此时B相是低电平
↓
说明正在正转

再比如:

A相 ↓,B相高电平

意思是:

复制代码
A相发生下降沿
↓
此时B还是高电平
↓
仍然说明正转

所以你会发现:

正转时

复制代码
A↑ + B=0
A↓ + B=1

B↑ + A=1
B↓ + A=0

这些组合都可以作为:

"这是正转"的证据。

  1. 反转就是完全相反

反转表:

复制代码
A↑ + B=1
A↓ + B=0

B↑ + A=0
B↓ + A=1

也就是说:

同样是一个边沿,但另一相处于什么电平不同,就能判断方向。


  1. 为什么一定要"两相"?

这就是正交编码器最牛的地方。

假设只有 A 相:

复制代码
A:

0 → 1 → 0 → 1 → 0 → 1

你只能知道:

"转了一格。"

但你不知道:

到底顺时针还是逆时针?

因为正转和反转都可能产生一样的 A 波形。

所以加上 B 相:

复制代码
A
B

就有了"谁领先谁"的信息。

于是:

复制代码
A领先B → 正转
B领先A → 反转

  1. 那它和 STM32 的 TIM 有什么关系?

这就终于和你刚刚学的编码器接口模式联系起来了。

STM32 的 TIM 不需要你自己写这种代码:

复制代码
if (A ...)
{
    if (B ...)
        Count++;
    else
        Count--;
}

TIM硬件自己就能干。

也就是:

复制代码
编码器A ──→ TIM_CH1
编码器B ──→ TIM_CH2
               ↓
          编码器接口逻辑
               ↓
        判断A、B先后关系
               ↓
        ┌─────────────┐
        ↓             ↓
      正转            反转
        ↓             ↓
      CNT++          CNT--

所以你前面那张 PPT 说:

"自动控制 CNT 自增或自减"


  1. 这张图实际上就是一个"查表"

你可以把它想成:

复制代码
检测到一个边沿
       ↓
看另一相现在是什么电平
       ↓
┌───────────────┐
│ A↑,B=0       │ → 正转
│ A↑,B=1       │ → 反转
│ A↓,B=1       │ → 正转
│ A↓,B=0       │ → 反转
│ ...           │
└───────────────┘

所以右边那个表不是让你背的。

它是在告诉你:

编码器接口判断方向时,到底观察什么。

编码器接口基本结构

简单来说,它的核心作用就是通过读取外部旋转编码器(A相和B相)的脉冲信号,自动完成计数(加/减),从而测量物体的速度、位置或角度 。这个模块最大的好处是纯硬件自动计数,不需要CPU干预

左->右边

  1. 信号输入与调理部分(左侧绿色方块)
  • GPIO(通用输入输出):物理引脚,连接外部编码器的A相和B相输出信号。

  • 滤波器 :外部电机或机械环境有大量电磁干扰,滤波器的作用就是滤除高频毛刺和噪声,保证信号的纯净度。

  • 边沿检测/极性选择 :识别信号的上升沿和下降沿。这里的"极性选择"可以通过软件配置,让你决定是检测上升沿、下降沿,还是双边沿,以及是否需要将信号反相。经过这步处理后,信号变成了 TI1FP1(TI1FP1 的全称是 Filtered Timer Input 1 ,即 经过滤波处理的定时器输入 1)TI2FP2

  1. 核心逻辑处理部分(中间橙色方块)
  • 编码器接口 :这是整个模块的"大脑"。它接收 TI1FP1 和 TI2FP2 两组信号(正交信号)。它会通过比较这两路信号谁先到达、谁后到达,来判断旋转的方向

    • 比如,A相超前B相90度,计数器就向上计数(正转);

    • B相超前A相90度,计数器就向下计数(反转)。

    • (注:这个接口通常支持1倍频、2倍频或4倍频计数模式,具体取决于边沿检测的配置)。

  1. 时基单元与计数部分(右侧粉色/橙色方块)

这部分负责具体的"数值累加或累减"。

  • PSC 预分频器 :在常规定时器里用于分频内部时钟,但在编码器模式下,PSC通常设置为1(即不分频)。因为此时计数器的时钟源是外部编码器脉冲,而不是内部时钟。

  • CNT 计数器 :这是一个随时更新的寄存器,记录当前的计数值。编码器转一格,CNT就会加1或减1。

  • ARR 自动重装器:决定了计数的上限(溢出值)。例如:如果编码器转一圈输出400个脉冲,你设置了4倍频(1600个计数),那么你可以将ARR设为1600-1。当CNT数到ARR时,就会溢出并重新从0开始(或者产生中断,提示单片机"转了一圈了")。

  • (图中的双向箭头表示CNT和ARR之间是关联的,用于实现循环计数或溢出判断)

💡 总结工作流程:

外部编码器转动 ➡️ GPIO接收A/B相脉冲 ➡️ 滤波器去噪 ➡️ 边沿检测 ➡️ 编码器接口判断旋转方向并生成计数脉冲 ➡️ 驱动CNT计数器执行加/减法 ➡️ 单片机通过读取CNT寄存器的值,就能知道转动的角度和速度。

在软件配置时,你只需要 :开启定时器的"编码器接口模式",设置好输入滤波和极性,然后读取 CNT 的值即可,硬件会自动完成所有繁琐的相位判别和计数工作。

工作模式

这张表(表77)是STM32定时器编码器接口模式的核心逻辑表 ,它定义了硬件如何根据A相(TI1)和B相(TI2)的脉冲关系,自动决定计数器(CNT)是向上加还是向下减

这张表最核心的规律就是:在一个通道发生边沿跳变时,另一个通道的电平状态决定了"方向"。(这就是正交编码的物理原理:A超前B 90度时正转,B超前A 90度时反转)。

表格中的 "相对信号的电平" 里的括号注释很关键:TI1FP1对应TI2,TI2FP2对应TI1 。也就是说,当检测TI1的跳变时,要看TI2此时是高电平还是低电平;反之亦然。

下面我们逐行来看这三种工作模式(对应软件里配置的三种编码器模式):

  1. 仅在TI1计数(相当于 2倍频/2x 模式)
  • 只看A相信号(TI1)的边沿(上升沿或下降沿),B相(TI2)完全不计数(显示为"不计数")。

  • 如何判断方向? 看TI2的电平:

    • 如果TI1上升沿到来时,TI2是 电平 ➡️ 向下计数

    • 如果TI1上升沿到来时,TI2是 电平 ➡️ 向上计数

    • (下降沿的逻辑与上升沿相反)。

  • 特点:分辨率较低,一圈产生的计数值是物理脉冲数的2倍。

  1. 仅在TI2计数(相当于 2倍频/2x 模式)
  • 与上一种完全对称,只看B相信号(TI2)的边沿,A相(TI1)不计数。

  • 如何判断方向? 看TI1的电平:

    • 如果TI2上升沿到来时,TI1是 电平 ➡️ 向上计数

    • 如果TI2上升沿到来时,TI1是 电平 ➡️ 向下计数

  • 特点:分辨率同样是2倍。

  1. 在TI1和TI2上计数(相当于 4倍频/4x 模式,最常用)
  • A相和B相的每一个上升沿和下降沿都会触发计数。

  • 如何判断方向? 规则非常规律:

    • 当A相或B相发生上升沿 时:如果相对信号是 电平 ➡️ 向下计数 ;是 电平 ➡️ 向上计数

    • 当A相或B相发生下降沿 时:如果相对信号是 电平 ➡️ 向上计数 ;是 电平 ➡️ 向下计数

  • 特点:这是分辨率最高的模式(4倍频)。如果编码器一圈输出400个脉冲,在这个模式下,计数器一圈会加/减1600次,能测出更精细的角度。绝大多数电机控制都选用这种模式。

💡 总结与实操建议:

在STM32的库函数(如HAL库)中,你不需要自己写逻辑去判断这些,只需要配置寄存器(比如 TIM_EncoderMode_TI1TIM_EncoderMode_TI2TIM_EncoderMode_TI12),硬件电路就会按照这张表自动完成所有的计数方向判断 。你只需要定时读取 CNT 寄存器的值,就知道电机往哪个方向转了多少步了。

注意:如果你发现电机正转时计数器却反向减,通常只需要在软件里把极性选择(也就是配置 TI1FP1/TI2FP2 的极性)反一下,或者把A、B两根线对调一下即可。

图1:实例(均不反相)------ 标准正逻辑

  • 向前(正转) :此时 TI1 超前 TI2 90度。根据表格逻辑,每一个边沿(TI1的上下沿、TI2的上下沿)都会让计数器阶梯式向上增加。你可以看到最左侧和最右侧的"向前"段,计数器是稳步向上的。

  • 向后(反转) :此时 TI2 超前 TI1 90度。触发逻辑完全反过来,计数器变成阶梯式向下递减。

  • 毛刺(抗干扰特性) :图中有一段毛刺,这是编码器停在临界点附近时的机械抖动。这个毛刺只触发了一两次加计数和减计数。这就是硬件编码器接口的好处:它通过"相对电平"判断方向,哪怕有噪声毛刺,也只能产生微小的加加减减,不会出现严重的乱跳。如果有一个高电平毛刺,而当时另一相是高电平,它就会先加一个,再减一个,净计数不变。


图2:实例(TI1反相)------ 硬件逻辑反转

这里的区别在于:在软件配置里,把 TI1 的极性设为"反相"了 。也就是说,外部引脚的真实 TI1 波形,进入硬件逻辑前被上下翻转了

  • 物理动作是"向前",但计数器却在"向下"减 :观察波形图最左侧的"向前"段,外部信号依然是 TI1 超前 TI2,但是因为 TI1 被内部翻转了,触发的边沿性质全部变反了。原本上升沿变成下降沿,下降沿变成上升沿。这导致硬件按照表逻辑判断,每来一个边沿都在做减法。结果就是:物理向前,计数器向下。

  • 物理动作是"向后",计数器却变成"向上"加:中间段的"向后"波形,由于翻转逻辑,变成了向上计数。

  • 为什么有毛刺?:中间那段毛刺,原本是抖动,但是因为逻辑反了,变成了先大幅减,再小幅加,净效果是往下掉。

编码器接口测速

cpp 复制代码
#include "stm32f10x.h"
#include "Delay.h"
#include "OLED.h"
#include "Encoder.h"

int main(void)
{
	OLED_Init();
	EncoderInit();
	
	OLED_ShowString(1, 1, "Speed:");
	
	while (1)
	{
		OLED_ShowSignedNum(1, 7,Encoder_Get(), 5);
		Delay_ms(100);
	}
}


#include "stm32f10x.h"                  // Device header
void EncoderInit()
{
  RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM3,ENABLE);
  RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA,ENABLE);
  GPIO_InitTypeDef GPIO_InitStructure;
  GPIO_InitStructure.GPIO_Mode= GPIO_Mode_IPU;
	GPIO_InitStructure.GPIO_Pin=GPIO_Pin_6|GPIO_Pin_7;
	GPIO_InitStructure.GPIO_Speed=GPIO_Speed_50MHz;
	GPIO_Init(GPIOA,&GPIO_InitStructure);
	TIM_TimeBaseInitTypeDef TIM_TimeBaseInitStructure;
	TIM_TimeBaseInitStructure.TIM_ClockDivision=TIM_CKD_DIV1;
	TIM_TimeBaseInitStructure.TIM_CounterMode=0;
	TIM_TimeBaseInitStructure.TIM_Period=65536-1;    //ARR拉满
	TIM_TimeBaseInitStructure.TIM_Prescaler=0;   //不分频
	TIM_TimeBaseInitStructure.TIM_RepetitionCounter=0;//不记次
	TIM_TimeBaseInit( TIM3,&TIM_TimeBaseInitStructure);
	TIM_ICInitTypeDef   TIM_ICInitStructure1;
	TIM_ICStructInit(&TIM_ICInitStructure1);
	TIM_ICInitStructure1.TIM_Channel=TIM_Channel_1;
	TIM_ICInitStructure1.TIM_ICFilter=0xF;
	TIM_ICInit(TIM3, &TIM_ICInitStructure1);
	
	TIM_ICInitTypeDef   TIM_ICInitStructure2;
	TIM_ICStructInit(&TIM_ICInitStructure2);
	TIM_ICInitStructure2.TIM_Channel=TIM_Channel_2;
	TIM_ICInitStructure2.TIM_ICFilter=0xF;
	TIM_ICInit(TIM3, &TIM_ICInitStructure2);

	
	
	TIM_EncoderInterfaceConfig(TIM3, TIM_EncoderMode_TI12,
                                TIM_ICPolarity_Rising, TIM_ICPolarity_Rising);
	
	
	TIM_Cmd(TIM3, ENABLE);
   



}

int16_t Encoder_Get(void)
{
	int16_t Temp;
	Temp = TIM_GetCounter(TIM3);
	TIM_SetCounter(TIM3, 0);
	return Temp;
}

代码解析:

这段代码能够测量旋转编码器的速度,核心在于 STM32 定时器的编码器接口模式。它利用硬件自动对编码器输出的 A/B 两相脉冲进行计数,并通过定时读取并清零计数器,得到单位时间内的脉冲增量,从而换算出转速。

下面详细解释原理和代码的配合。

  1. 编码器接口的基本原理

旋转编码器通常输出两路相位相差 90° 的方波信号(A 相和 B 相)。

  • 正转时,A 相超前 B 相 90°;

  • 反转时,B 相超前 A 相 90°。

STM32 定时器的编码器接口模式可以自动检测这两路信号的边沿和电平关系,并据此自动增减计数器

边沿/电平组合 计数动作
检测到 A 或 B 的边沿,且另一相电平决定方向 正向则计数器加 1,反向则减 1

如果配置为 TI1+TI2 模式(TIM_EncoderMode_TI12) ,则在 A 和 B 两路信号的每个边沿 都会触发计数,相当于对每个物理脉冲进行了 4 倍频,提高了测量分辨率。

  1. 代码配置解析

2.1 GPIO 配置

c

RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE);

GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IPU; // 上拉输入

GPIO_InitStructure.GPIO_Pin = GPIO_Pin_6 | GPIO_Pin_7;

  • PA6 → TIM3_CH1,PA7 → TIM3_CH2,对应编码器的 A、B 相。

  • 上拉输入是为了保证无信号时电平确定,同时适配开漏输出的编码器。

2.2 定时器基本配置

c

RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM3, ENABLE);

TIM_TimeBaseInitStructure.TIM_Period = 65536 - 1; // ARR = 65535

TIM_TimeBaseInitStructure.TIM_Prescaler = 0; // 不分频

  • 计数器为 16 位,最大计数到 65535 后回绕。

  • 在编码器模式下,计数时钟来自外部编码器脉冲,内部时钟预分频不直接影响计数速度,但时基配置仍需要。

2.3 输入捕获配置

c

TIM_ICInitStructure1.TIM_Channel = TIM_Channel_1;

TIM_ICInitStructure1.TIM_ICFilter = 0xF; // 数字滤波

TIM_ICInit(TIM3, &TIM_ICInitStructure1);

// 对通道2同样配置

  • 输入滤波可以消除编码器信号上的毛刺,提高稳定性。

2.4 编码器接口模式

c

TIM_EncoderInterfaceConfig(TIM3, TIM_EncoderMode_TI12,

TIM_ICPolarity_Rising, TIM_ICPolarity_Rising);

TIM_Cmd(TIM3, ENABLE);

  • 选择 TI12 模式 :在 TI1 和 TI2 的所有边沿都计数(4 倍频)。

  • 极性设为 Rising,表示不反相,正常检测边沿。

  • 使能定时器后,硬件自动根据 A/B 信号增减计数器,无需中断或软件干预。


  1. 速度测量的实现

在主循环中:

c

while (1)

{

OLED_ShowSignedNum(1, 7, Encoder_Get(), 5);

Delay_ms(100);

}

Encoder_Get() 函数:

c

int16_t Encoder_Get(void)

{

int16_t Temp;

Temp = TIM_GetCounter(TIM3); // 读取当前计数值

TIM_SetCounter(TIM3, 0); // 清零计数器

return Temp;

}

  • 每 100ms 调用一次,读取并清零计数器。

  • 返回的 Temp 就是在这 100ms 内编码器产生的净脉冲数(正转时为正,反转为负)。

  • 这个脉冲增量与转速成正比:

    速度∝脉冲数时间速度∝时间脉冲数​

    例如,如果编码器每圈输出 100 个脉冲,100ms 内计数值为 50,则转速约为 50×10/100=550×10/100=5 圈/秒。

  • 显示函数 OLED_ShowSignedNum 直接显示带符号的数值,可以反映正反转方向。


  1. 关键点总结
  • 硬件自动计数:STM32 定时器编码器模式无需 CPU 频繁中断,计数精度高,实时性好。

  • 单位时间读取差值:通过定时清零并读取计数器,得到固定时间内的脉冲增量,即速度。

  • 四倍频提高分辨率:TI12 模式在每个输入边沿都计数,物理分辨率提高 4 倍。

  • 滤波和上拉:保证信号质量,避免误计数。

  • 带符号计数:硬件根据相位关系自动判断方向,因此返回值有正负,可区分正反转。

因此,这段代码能稳定、高效地测量旋转编码器的速度,并实时显示在 OLED 上。


相关推荐
jianqiang.xue5 小时前
收官:从v0.1到“研发新常态“,一套嵌入式智能体的落地与边界
stm32·单片机·物联网·架构·esp32
笨笨饿6 小时前
#137_死机恢复现场_Armino平台AP系统swd调试
git·python·stm32·单片机·嵌入式硬件·物联网·vim
玩转单片机与嵌入式10 小时前
深入浅出TinyML 24:剪枝、聚类、蒸馏和算子替换是否真正适合MCU?
人工智能·stm32·机器学习·tinyml
the sun3410 小时前
嵌入式易错点:ARM指令架构、IP核、MCU厂商关系+BOOTLoader刷机原理
stm32·嵌入式
wuyk55512 小时前
104.C 语言字节对齐:从底层原理到工程实践的完整指南
c语言·开发语言·stm32·单片机·嵌入式硬件
北域码匠19 小时前
高通滤波算法深度解析(High-Pass Filter)
stm32·算法·c#·数字信号处理·嵌入式开发·滤波算法·高通滤波
star learning white19 小时前
STM32入门学习5
stm32·嵌入式硬件·学习
新晨单片机设计20 小时前
S006A-基于STM32单片机温湿度检测系统【Proteus仿真+Keil程序+原理图】
stm32·单片机·proteus
星河单片机1 天前
STM32驱动HS‑S37A水位传感器+OLED完整实验讲解
stm32·单片机·嵌入式硬件