野火STM32_HAL库版课程笔记-TIM通道捕获应用之编码器模式

前置介绍

TIM-输入捕获之编码器模式
什么是输入捕获?

通过定时器记录输入引脚信号到达时刻的功能, 常用于测量外部信号的频率, 周期, 脉宽.

什么是编码器?

用来测量旋转或位移的传感器, 能测量机械部件旋转或直线运动时的位移位置或速度等信息, 并转换成一系列电信号

TIM 高级定时器框图-输入捕获
阶段 1:外部输入通道(橙色区域)

左侧的 TIMx_CH1~TIMx_CH4 是定时器的 4 个输入通道,负责接收外部信号。

  • 信号可通过 直连 (直接进入通道)或 异或逻辑(通过 XOR 选择性组合通道信号,比如多路信号逻辑运算后输入)进入后续处理。
阶段 2:输入滤波与边沿检测(紫色区域)

每个通道的信号进入 "输入滤波器 + 边沿检测器" 模块:

  • 滤波器:过滤信号中的高频噪声,避免误触发;
  • 边沿检测器 :检测信号的 上升沿/下降沿 (比如脉冲的起始/结束),并将边沿事件转化为触发信号(TI1FP1/2TI2FP1/2 等)。
阶段 3:通道选择与预分频(粉色+灰色区域)

通过 通道选择逻辑 (如 TIxFPxTRC 等),选择要捕获的信号源(比如从多个通道中选 1 路,或组合多路信号)。

  • 选中的信号进入 预分频器(灰色模块):对信号进行分频(比如 1/2 分频、1/4 分频等),调整捕获频率。
阶段 4:捕获/比较寄存器(蓝色区域)

预分频后的信号(ICxPS)触发 "捕获/比较寄存器"CC1~CC4):

  • 当边沿事件发生时,计数器(CNT)的当前值会被"捕获"并存入对应通道的寄存器 (比如通道 1 捕获到边沿,CNT 值存入 捕获/比较 1 寄存器)。
  • 这些寄存器可读取,用于计算 脉冲宽度、频率、占空比 等(比如两次捕获的时间差 = 脉冲周期)。
阶段 5:计数与基准(黄色区域)

右侧黄色模块是定时器的 核心计数单元

  • PSC(预分频器):对定时器时钟 CK_PSC 分频,得到计数时钟 CK_CNT
  • CNT(计数器):在 CK_CNT 驱动下递增/递减/停止,提供"时间基准";
  • 自动重装载寄存器:决定计数器的溢出周期(比如计数到 1000 后自动归零,形成固定周期)。
EC11 编码器模块

在 STM32 中自带编码器模式, 其就可以采集例如 A 相的下降沿, 就会自动判断 B 相此时如果是高电平, 那么就能判断出其此时是顺时针, (判断哪一项在前, 哪一项在后, 得出 顺时针 / 逆时针 旋转)

STM32F10x - 中文参考手册
TIM-编码器模式所用函数

HAL_TIM_Encoder_Start()

__HAL_TIM_GET_COUNTER()

__HAL_TIM_IS_TIM_COUNTING_DOWN()

项目配置

TIM3

Combined Channels : Encoder Mode

Prescaler : 4 - 1

Encoder Mode : Encoder Mode TI1 and TI2

注意: 在编码器模式下,极性设置 (Encoder - Polarity) 不是用来选择只检测上升沿,而是用来定义信号的反相(Inversion)。如果设置为 Rising,表示信号不反相;如果设置为 Falling,表示信号经过反相器。硬件依然会自动检测双边沿。

GPIO

选择 PA8 为 GPIO_Input

设置为 Pull-up (上拉模式), User Label 为 EC11_SW

代码部分

记得勾选 Use MicroLIB

复制代码
/* Private includes ----------------------------------------------------------*/
/* USER CODE BEGIN Includes */

#include <stdio.h>  // 使用 printf 函数
#include <string.h> // 使用 strncmp

/* USER CODE END Includes */

/* Private typedef -----------------------------------------------------------*/
/* USER CODE BEGIN PTD */

/* USER CODE END PTD */

/* Private define ------------------------------------------------------------*/
/* USER CODE BEGIN PD */

/* USER CODE END PD */

/* Private macro -------------------------------------------------------------*/
/* USER CODE BEGIN PM */

/* USER CODE END PM */

/* Private variables ---------------------------------------------------------*/

/* USER CODE BEGIN PV */
int16_t encoder_cnt = 0;      // 编码器当前计数值 (用于获取旋转变化)
int16_t encoder_last = 0;     // 上一次编码器计数值 (用于比较判断旋转方向和增量)
uint32_t no_move_time = 0;    // 编码器未移动的时间 (用于检测是否静止)

uint8_t encoder_static = 0;   // 0: 运动, 1: 静止

/* USER CODE END PV */

/* Private function prototypes -----------------------------------------------*/
void SystemClock_Config(void);
/* USER CODE BEGIN PFP */

/* USER CODE END PFP */

/* Private user code ---------------------------------------------------------*/
/* USER CODE BEGIN 0 */

/* USER CODE END 0 */

/**
  * @brief  The application entry point.
  * @retval int
  */
int main(void)
{

  /* USER CODE BEGIN 1 */

  /* USER CODE END 1 */

  /* MCU Configuration--------------------------------------------------------*/

  /* Reset of all peripherals, Initializes the Flash interface and the Systick. */
  HAL_Init();

  /* USER CODE BEGIN Init */

  /* USER CODE END Init */

  /* Configure the system clock */
  SystemClock_Config();

  /* USER CODE BEGIN SysInit */

  /* USER CODE END SysInit */

  /* Initialize all configured peripherals */
  MX_GPIO_Init();
  MX_TIM3_Init();
  MX_USART1_UART_Init();
  /* USER CODE BEGIN 2 */
  
  HAL_TIM_Encoder_Start(&htim3, TIM_CHANNEL_ALL);

  /* USER CODE END 2 */

  /* Infinite loop */
  /* USER CODE BEGIN WHILE */
  while (1)
  {
    
    encoder_cnt = __HAL_TIM_GET_COUNTER(&htim3);
    
    // 检测是否移动
    if (encoder_cnt != encoder_last)
    {
      
      encoder_static = 0;  // 有动作, 等待静止
      no_move_time = HAL_GetTick(); // 记录最后动作事件
      
      int16_t diff = encoder_cnt - encoder_last;
      
      if (__HAL_TIM_IS_TIM_COUNTING_DOWN(&htim3))
        printf("逆时针移动: %d, 当前位置: %d \r\n", diff, encoder_cnt);
      else 
        printf("顺时针移动: %d, 当前位置: %d \r\n", diff, encoder_cnt);
      
      encoder_last = encoder_cnt;
    }
    else
    {
      // 超过两秒静止
      if (HAL_GetTick() - no_move_time > 2000 && encoder_static == 0)
      {
        encoder_static = 1;
        printf("当前编码器静止超过2秒, 当前位置: %d \r\n", encoder_cnt);
      }
    }
    
    // 检测 SW 按钮是否被按下
    if (HAL_GPIO_ReadPin(GPIOA, EC11_SW_Pin) == GPIO_PIN_RESET)
    {
      HAL_Delay(20); // 消抖
      if (HAL_GPIO_ReadPin(GPIOA, EC11_SW_Pin) == GPIO_PIN_RESET)
      {
        printf("SW按钮被按下 \r\n");
        while(HAL_GPIO_ReadPin(GPIOA, EC11_SW_Pin) == GPIO_PIN_RESET);
      }
    }
    
    __HAL_TIM_SET_COUNTER(&htim3, 0);
    
    HAL_Delay(500);
    /* USER CODE END WHILE */

    /* USER CODE BEGIN 3 */
  }

/* USER CODE BEGIN 4 */

/**
  * @brief  重定向 printf 的输出到串口
  * @param  ch: 要发送的字符
  * @param  f:  文件指针 (标准库要求的参数, 一般不使用)
  * @retval 返回发送的字符
  */
int fputc(int ch, FILE *f)
{
  HAL_UART_Transmit(&huart1, (uint8_t *)&ch, 1, HAL_MAX_DELAY);
  return ch;
}

/* USER CODE END 4 */

程序现象

分别顺时针 / 逆时针 旋转编码器, 输出移动量与当前位置.

按下按钮, 触发 "SW 按钮已被按下"

两秒时间没有旋转编码器, 输出 "当前编码器已静止超过 2 秒" 和当前位置.

相关推荐
岑梓铭3 小时前
考研408《操作系统》复习笔记,第二章《2.3.3 + 2.3.4 经典同步问题、管程》
笔记·考研·操作系统·408·os
前端小马3 小时前
PTE考试笔记
笔记·英语
不会武功的火柴3 小时前
ModelSim入门实战(三): 批处理一键仿真与波形调试
嵌入式硬件·fpga·仿真·modelsim·ic验证·rtl
m0_46644103詹湛4 小时前
FPGA时序优化与高速接口实战手册
笔记·学习·fpga开发·硬件架构·verilog
问心无愧05135 小时前
ctf show web 入门39
android·前端·笔记
Yeh2020585 小时前
Mybatis笔记一
java·笔记·mybatis
羊群智妍5 小时前
2026 AI搜索优化技术:GEO监测工具选型与应用
笔记
半导体守望者5 小时前
MKS elite 300 600 750W RF Plasma Generator 射频电源 OPERATIONMANUAL
经验分享·笔记·机器人·自动化·制造
23124_805 小时前
【无标题】
单片机·嵌入式硬件
05候补工程师5 小时前
【线性代数笔记】初等变换、正交化与特殊矩阵性质核心总结
经验分享·笔记·线性代数·考研·矩阵