当前位置: 首页 > news >正文

STM标准库-TIM旋转编码器

文章目录

  • 一、编码器接口
    • 1.1简介
    • 1.2正交编码器
    • 1.3编码器接口基本结构
      • **1. 模块与 STM32 配置的映射关系**
      • **2. 设计实现步骤(核心流程)**
        • **① 硬件规划**
        • **② 时钟使能**
        • **③ GPIO 配置(对应架构图 “GPIO” 模块)**
        • **④ 时基单元配置(对应架构图 “时基单元”)**
        • **⑤ 输入滤波 + 极性配置(对应架构图 “滤波器、边沿 / 极性选择”)**
        • **⑥ 编码器接口模式(对应架构图 “编码器接口”)**
        • **⑦ 使能定时器,开始计数**
        • **⑧ 数据读取(轮询 / 中断)**
      • **3. 关键逻辑说明**
    • 1.4工作模式
    • 1.5实例(均不反相)
    • 1.6实例(TI1反相)
  • 二、编码器接口测速
    • 2.1接线图
    • 2.2相关API
      • 一、编码器接口配置 API
        • 1. `TIM_EncoderInterfaceConfig()`
      • 二、计数器操作 API
        • 2. `TIM_SetCounter()`
        • 3. `TIM_GetCounter()`
      • 三、编码器方向控制 API
        • 4. `TIM_GetCounterMode()`
      • 四、编码器溢出标志 API
        • 5. `TIM_GetFlagStatus()`
      • 五、编码器中断配置 API
        • 6. `TIM_ITConfig()`
      • 六、编码器接口状态读取 API
        • 7. `TIM_GetCapture1()` / `TIM_GetCapture2()`
    • 2.3代码
    • 2.4现象

一、编码器接口

1.1简介

  • Encoder Interface 编码器接口
  • 编码器接口可接收增量(正交)编码器的信号,根据编码器旋转产生的正交信号脉冲,自动控制CNT
  • 自增或自减,从而指示编码器的位置、旋转方向和旋转速度
  • 每个高级定时器和通用定时器都拥有1个编码器接口
  • 两个输入引脚借用了输入捕获的通道1和通道2

1.2正交编码器

在这里插入图片描述

  • 正交编码器:两个方波信号相位相差90°,超前90°或者滞后90°,分别代表正转和反转。
  • 工作流程:根据编码器旋转产生的正交信号脉冲自动控制CND自增或自减,从而指示编码器的位置、旋转方向和旋转速度。

1.3编码器接口基本结构

在这里插入图片描述

  • 编码器测速实际上就是测频法测正交脉冲的频率。

  • 要基于该架构图设计实现编码器接口,可按 “模块映射→配置步骤→代码实现” 逻辑展开,核心是将架构图的功能模块对应到 STM32的寄存器配置:

1. 模块与 STM32 配置的映射关系

架构图模块STM32 功能配置关键 API / 参数
GPIO编码器信号输入引脚初始化(上拉输入)GPIO_Mode_IPU
滤波器输入信号滤波(抗抖动)TIM_ICFilter = 0xF(高滤波)
边沿 / 极性选择信号边沿检测与极性反转TIM_ICPolarity_Rising/Falling
编码器接口正交信号计数模式(双相 / 单相)TIM_EncoderMode_TI12
时基单元计数器范围(ARR)和预分频(PSC)TIM_Period=65535TIM_Prescaler=0

2. 设计实现步骤(核心流程)

① 硬件规划
  • 选定定时器(如 TIM3),其 CH1(PA6)、CH2(PA7) 引脚连接编码器 A 相、B 相
② 时钟使能

c

RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM3, ENABLE);   // 使能TIM3时钟  
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE);  // 使能GPIOA时钟  
③ GPIO 配置(对应架构图 “GPIO” 模块)

c

GPIO_InitTypeDef GPIO_InitStructure;  
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IPU;          // 上拉输入,稳定信号  
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_6 | GPIO_Pin_7; // PA6(CH1)、PA7(CH2)  
GPIO_Init(GPIOA, &GPIO_InitStructure);  
④ 时基单元配置(对应架构图 “时基单元”)

c

TIM_TimeBaseInitTypeDef TIM_TimeBaseInitStructure;  
TIM_TimeBaseInitStructure.TIM_Period = 65535;          // ARR=65535(最大计数范围)  
TIM_TimeBaseInitStructure.TIM_Prescaler = 0;           // PSC=0(计数器由外部信号驱动,预分频无实际作用)  
TIM_TimeBaseInitStructure.TIM_CounterMode = TIM_CounterMode_Up; // 初始方向(编码器会覆盖)  
TIM_TimeBaseInit(TIM3, &TIM_TimeBaseInitStructure);  
⑤ 输入滤波 + 极性配置(对应架构图 “滤波器、边沿 / 极性选择”)

c

TIM_ICInitTypeDef TIM_ICInitStructure;  
TIM_ICStructInit(&TIM_ICInitStructure);                // 结构体默认值初始化  
TIM_ICInitStructure.TIM_ICFilter = 0xF;                // 最高滤波等级,抗抖动  
TIM_ICInit(TIM3, &TIM_ICInitStructure);                // 配置CH1  
TIM_ICInitStructure.TIM_Channel = TIM_Channel_2;       // 切换到CH2  
TIM_ICInit(TIM3, &TIM_ICInitStructure);                // 配置CH2  
⑥ 编码器接口模式(对应架构图 “编码器接口”)

c

TIM_EncoderInterfaceConfig(  TIM3,                      // 定时器  TIM_EncoderMode_TI12,      // 双相计数(TI1、TI2边沿均响应,精度×2)  TIM_ICPolarity_Rising,     // CH1不反相(上升沿触发)  TIM_ICPolarity_Rising      // CH2不反相(上升沿触发)  
);  
⑦ 使能定时器,开始计数

c

TIM_Cmd(TIM3, ENABLE);  // 启动计数器,响应编码器信号  
⑧ 数据读取(轮询 / 中断)

c

// 轮询方式:直接读取计数值  
int16_t encoderValue = TIM_GetCounter(TIM3);  // 中断方式:配合TIM2定时中断(如题目代码),周期性读取增量  

3. 关键逻辑说明

  • 架构图的信号流
    编码器 A/B 相 → GPIO 输入 → 滤波器(TIM_ICFilter)→ 边沿 / 极性处理(TIM_ICPolarity)→ 生成 TI1FP1/TI2FP2 → 编码器接口 → 驱动时基单元 CNT 计数。
  • 计数方向自动控制
    编码器接口根据 TI1、TI2 的边沿跳变顺序,自动控制 CNT 向上 / 向下计数(正转 / 反转),无需软件判断方向。

1.4工作模式

在这里插入图片描述

1.5实例(均不反相)

在这里插入图片描述

1.6实例(TI1反相)

在这里插入图片描述

二、编码器接口测速

2.1接线图

在这里插入图片描述

  • 编码器需要接入两路正交信号(A 相和 B 相),对应定时器的 通道 1(CH1)和通道 2(CH2)。
  • CH1 对应 PA6,CH2 对应 PA7(参考 STM32F103C8T6 数据手册)。
    TIM3 作为 通用定时器,支持编码器接口模式(基本定时器 TIM6/7 不支持),而高级定时器 TIM1 虽支持但通常用于更复杂功能(如互补 PWM 输出)。
  • 编码器接口模式下,TIM3 可通过硬件自动处理正交信号的计数方向(自增 / 自减),无需软件干预,符合 “节约 CPU 资源” 的设计目标。
  1. 若选用其他定时器(如 TIM2)
  • 引脚映射:TIM2 的 CH1/CH2 对应 PA0/PA1,若 PA0/PA1 已被其他外设(如 PWM 输出)占用,则优先选择 TIM3 的 PA6/PA7。
  • 功能冲突:若项目中 TIM2 用于 PWM(如电机驱动),则 TIM3 更适合编码器接口,避免资源冲突。
  1. 高级定时器 TIM1 的局限性
  • TIM1 虽支持编码器接口,但其引脚(如 PA8/PA9)常作为高级 PWM 输出(如三相电机控制),且配置复杂(需额外使能 APB2 时钟),不如通用定时器 TIM3。

2.2相关API

一、编码器接口配置 API

1. TIM_EncoderInterfaceConfig()

运行

void TIM_EncoderInterfaceConfig(TIM_TypeDef* TIMx, uint16_t TIM_EncoderMode, uint16_t TIM_IC1Polarity, uint16_t TIM_IC2Polarity);
  • 功能:配置定时器为编码器接口模式

  • 参数:

    • TIMx:定时器选择(如TIM3

    • TIM_EncoderMode
      

      :编码器模式

      • TIM_EncoderMode_TI1:仅 TI1 计数(A 相)
      • TIM_EncoderMode_TI2:仅 TI2 计数(B 相)
      • TIM_EncoderMode_TI12:TI1 和 TI2 同时计数(双相计数,精度 ×2)
    • TIM_IC1Polarity
      

      :TI1 极性配置

      • TIM_ICPolarity_Rising:不反相(上升沿触发)
      • TIM_ICPolarity_Falling:反相(下降沿触发)
    • TIM_IC2Polarity:TI2 极性配置(同上)

  • 示例

    运行

    TIM_EncoderInterfaceConfig(TIM3, TIM_EncoderMode_TI12, TIM_ICPolarity_Rising, TIM_ICPolarity_Rising);
    

二、计数器操作 API

2. TIM_SetCounter()

运行

void TIM_SetCounter(TIM_TypeDef* TIMx, uint16_t Counter);
  • 功能:直接设置定时器计数器值(CNT)

  • 参数:

    • TIMx:定时器选择
    • Counter:目标计数值(0~65535)
  • 示例:

    运行

    TIM_SetCounter(TIM3, 0);  // 清零计数器(常用于读取增量值后复位)
    
3. TIM_GetCounter()

运行

uint16_t TIM_GetCounter(TIM_TypeDef* TIMx);
  • 功能:获取当前计数器值(CNT)

  • 参数 :

    • TIMx:定时器选择
  • 返回值:当前计数值(0~65535)

  • 示例

    运行

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

三、编码器方向控制 API

4. TIM_GetCounterMode()

运行

uint16_t TIM_GetCounterMode(TIM_TypeDef* TIMx);
  • 功能:获取计数器计数模式(向上 / 向下)

  • 参数 :

    • TIMx:定时器选择
  • 返回值 :

    • TIM_CounterMode_Up:向上计数(编码器正转)
    • TIM_CounterMode_Down:向下计数(编码器反转)
  • 示例

    运行

    uint16_t mode = TIM_GetCounterMode(TIM3);  // 获取当前计数方向
    

四、编码器溢出标志 API

5. TIM_GetFlagStatus()

运行

FlagStatus TIM_GetFlagStatus(TIM_TypeDef* TIMx, uint16_t TIM_FLAG);
  • 功能:检查定时器标志位状态(如溢出标志)

  • 参数:

    • TIMx:定时器选择

    • TIM_FLAG
      

      :标志位选择

      • TIM_FLAG_Update:更新事件标志(计数器溢出 / 下溢)
  • 返回值 :

    • SET:标志位被置位
    • RESET:标志位未被置位
  • 示例:

    运行

    if (TIM_GetFlagStatus(TIM3, TIM_FLAG_Update) == SET) {// 处理计数器溢出/下溢事件TIM_ClearFlag(TIM3, TIM_FLAG_Update);  // 清除标志位
    }
    

五、编码器中断配置 API

6. TIM_ITConfig()

运行

void TIM_ITConfig(TIM_TypeDef* TIMx, uint16_t TIM_IT, FunctionalState NewState);
  • 功能:使能或禁用定时器中断

  • 参数 :

    • TIMx:定时器选择

    • TIM_IT
      

      :中断源选择

      • TIM_IT_Update:更新中断(计数器溢出 / 下溢)
    • NewState
      

      :使能状态

      • ENABLE:使能中断
      • DISABLE:禁用中断
  • 示例 :

    c

    运行

    TIM_ITConfig(TIM3, TIM_IT_Update, ENABLE);  // 使能TIM3更新中断
    

六、编码器接口状态读取 API

7. TIM_GetCapture1() / TIM_GetCapture2()

运行

uint16_t TIM_GetCapture1(TIM_TypeDef* TIMx);
uint16_t TIM_GetCapture2(TIM_TypeDef* TIMx);
  • 功能:获取编码器接口的捕获值(CCR 寄存器)

  • 参数 :

    • TIMx:定时器选择
  • 返回值 :

    • 通道 1/2 的捕获值(编码器接口中较少使用,主要用于输入捕获模式)

2.3代码

main.c

#include "stm32f10x.h"                  // Device header
#include "Delay.h"
#include "OLED.h"
#include "Timer.h"
#include "Encoder.h"int16_t Speed;			//定义速度变量int main(void)
{/*模块初始化*/OLED_Init();		//OLED初始化Timer_Init();		//定时器初始化Encoder_Init();		//编码器初始化/*显示静态字符串*/OLED_ShowString(1, 1, "Speed:");		//1行1列显示字符串Speed:while (1){OLED_ShowSignedNum(1, 7, Speed, 5);	//不断刷新显示编码器测得的最新速度}
}/*** 函    数:TIM2中断函数* 参    数:无* 返 回 值:无* 注意事项:此函数为中断函数,无需调用,中断触发后自动执行*           函数名为预留的指定名称,可以从启动文件复制*           请确保函数名正确,不能有任何差异,否则中断函数将不能进入*/
void TIM2_IRQHandler(void)
{if (TIM_GetITStatus(TIM2, TIM_IT_Update) == SET)		//判断是否是TIM2的更新事件触发的中断{Speed = Encoder_Get();								//每隔固定时间段读取一次编码器计数增量值,即为速度值TIM_ClearITPendingBit(TIM2, TIM_IT_Update);			//清除TIM2更新事件的中断标志位//中断标志位必须清除//否则中断将连续不断地触发,导致主程序卡死}
}

Encoder.c

#include "stm32f10x.h"                  // Device header/*** 函    数:编码器初始化* 参    数:无* 返 回 值:无*/
void Encoder_Init(void)
{/*开启时钟*/RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM3, ENABLE);			//开启TIM3的时钟RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE);			//开启GPIOA的时钟/*GPIO初始化*/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);							//将PA6和PA7引脚初始化为上拉输入/*时基单元初始化*/TIM_TimeBaseInitTypeDef TIM_TimeBaseInitStructure;				//定义结构体变量TIM_TimeBaseInitStructure.TIM_ClockDivision = TIM_CKD_DIV1;     //时钟分频,选择不分频,此参数用于配置滤波器时钟,不影响时基单元功能TIM_TimeBaseInitStructure.TIM_CounterMode = TIM_CounterMode_Up; //计数器模式,选择向上计数TIM_TimeBaseInitStructure.TIM_Period = 65536 - 1;               //计数周期,即ARR的值TIM_TimeBaseInitStructure.TIM_Prescaler = 1 - 1;                //预分频器,即PSC的值TIM_TimeBaseInitStructure.TIM_RepetitionCounter = 0;            //重复计数器,高级定时器才会用到TIM_TimeBaseInit(TIM3, &TIM_TimeBaseInitStructure);             //将结构体变量交给TIM_TimeBaseInit,配置TIM3的时基单元/*输入捕获初始化*/TIM_ICInitTypeDef TIM_ICInitStructure;							//定义结构体变量TIM_ICStructInit(&TIM_ICInitStructure);							//结构体初始化,若结构体没有完整赋值//则最好执行此函数,给结构体所有成员都赋一个默认值//避免结构体初值不确定的问题TIM_ICInitStructure.TIM_Channel = TIM_Channel_1;				//选择配置定时器通道1TIM_ICInitStructure.TIM_ICFilter = 0xF;							//输入滤波器参数,可以过滤信号抖动TIM_ICInit(TIM3, &TIM_ICInitStructure);							//将结构体变量交给TIM_ICInit,配置TIM3的输入捕获通道TIM_ICInitStructure.TIM_Channel = TIM_Channel_2;				//选择配置定时器通道2TIM_ICInitStructure.TIM_ICFilter = 0xF;							//输入滤波器参数,可以过滤信号抖动TIM_ICInit(TIM3, &TIM_ICInitStructure);							//将结构体变量交给TIM_ICInit,配置TIM3的输入捕获通道/*编码器接口配置*/TIM_EncoderInterfaceConfig(TIM3, TIM_EncoderMode_TI12, TIM_ICPolarity_Rising, TIM_ICPolarity_Rising);//配置编码器模式以及两个输入通道是否反相//注意此时参数的Rising和Falling已经不代表上升沿和下降沿了,而是代表是否反相//此函数必须在输入捕获初始化之后进行,否则输入捕获的配置会覆盖此函数的部分配置/*TIM使能*/TIM_Cmd(TIM3, ENABLE);			//使能TIM3,定时器开始运行
}/*** 函    数:获取编码器的增量值* 参    数:无* 返 回 值:自上此调用此函数后,编码器的增量值*/
int16_t Encoder_Get(void)
{/*使用Temp变量作为中继,目的是返回CNT后将其清零*/int16_t Temp;Temp = TIM_GetCounter(TIM3);TIM_SetCounter(TIM3, 0);return Temp;
}
模块作用
TIM3编码器接口,硬件自动计数(自增 / 自减),记录编码器旋转的增量值
TIM2定时器,周期性触发中断(如 100ms),在中断中读取 TIM3 的计数值并更新 Speed
主函数循环显示 Speed 值,依赖 TIM2 中断更新数据

Encoder.h

#ifndef __ENCODER_H
#define __ENCODER_Hvoid Encoder_Init(void);
int16_t Encoder_Get(void);#endif

2.4现象

  • OLED 显示内容

初始状态:OLED 第一行显示 Speed:0,表示编码器未旋转时速度为 0。
旋转编码器时:实时显示速度值,格式为 Speed:±XXXX(± 表示方向,XXXX 为数值)。

  • 旋转方向与数值正负

正转(向右旋转):Speed 显示正数值,旋转越快数值越大(如 Speed:+120)。
反转(向左旋转):Speed 显示负数值,旋转越快数值越小(如 Speed:-80)。
停止旋转:Speed 逐渐归零(取决于编码器机械特性和滤波配置)。

  • 旋转速度与数值大小

慢速旋转:数值绝对值小(如 ±10~±50)。
快速旋转:数值绝对值大(如 ±100~±200)。

  • 编码器接口连接

PA6(TIM3_CH1)接编码器 A 相,PA7(TIM3_CH2)接 B 相,若接线反相:
正转显示负数值,反转显示正数值(方向相反)。

相关文章:

  • Spark流水线+Gravitino+Marquez数据血缘采集
  • 1 Studying《蓝牙核心规范5.3》
  • MyBatis原理剖析(二)
  • DeepSeek10-RAG相关模型知识说明
  • 编程实验篇--线性探测哈希表
  • 5.子网划分及分片相关计算
  • Apache Spark详解
  • 三十五、面向对象底层逻辑-Spring MVC中AbstractXlsxStreamingView的设计
  • Java求职者面试:微服务技术与源码原理深度解析
  • Spring Cloud Alibaba Seata安装+微服务实战
  • SpringCloud——微服务
  • 微服务体系下将环境流量路由到开发本机
  • (五)Linux性能优化-CPU-性能优化
  • 正点原子[第三期]Arm(iMX6U)Linux移植学习笔记-12.1 Linux内核启动流程简介
  • Webworker详解应用场景大片文件Hash计算
  • Web3 借贷与清算机制全解析:链上金融的运行逻辑
  • 用 Melos 解决 Flutter Monorepo 的依赖冲突:一个真实案例
  • 使用 Melos 高效管理 Flutter/Dart Monorepo 项目
  • 【基于阿里云搭建数据仓库(离线)】使用UDTF时出现报错“FlatEventUDTF cannot be resolved”
  • Electron通信流程
  • 做同城网站赚钱吗/网络营销常见的工具
  • wordpress 字体/厦门seo关键词
  • 做任务领积分兑换别的网站上的会员/网络教学平台
  • 怎么做网站弹窗/百度推广一年大概需要多少钱
  • 做最漂亮的网站/网上在哪里打广告最有效
  • 深圳市福田区香蜜湖街道/搜索优化指的是什么