基于STM32F407定时器5输入捕获的双编码器波形捕捉程序

基于STM32F407定时器5输入捕获的双编码器波形捕捉程序

一、系统概述

基于STM32F407ZGT6(Cortex-M4,168MHz),利用定时器5(TIM5)的四个输入捕获通道,实现对两个数字编码器(每个编码器输出A/B两相正交脉冲)的波形捕捉与脉冲计数。核心功能包括:

二、硬件设计

2.1 核心组件

模块 型号/参数 功能说明
主控 STM32F407ZGT6(TIM5,4通道输入捕获) 编码器信号捕获、脉冲计数、方向判断
编码器 2×EC11旋转编码器(A/B两相,5V) 输出正交脉冲(每圈20~30脉冲,可扩展)
接口电路 光耦隔离(TLP521)+ 上拉电阻(4.7kΩ) 隔离编码器与MCU,防止干扰

2.2 硬件连接

信号 STM32F407引脚(TIM5_CHx) 编码器接口 说明
编码器1_A PA0(TIM5_CH1) 编码器1 A相 正交脉冲输入(双边沿捕获)
编码器1_B PA1(TIM5_CH2) 编码器1 B相 正交脉冲输入(双边沿捕获)
编码器2_A PA2(TIM5_CH3) 编码器2 A相 正交脉冲输入(双边沿捕获)
编码器2_B PA3(TIM5_CH4) 编码器2 B相 正交脉冲输入(双边沿捕获)
电源 3.3V/GND 编码器VCC/GND 编码器供电(需与MCU共地)

三、软件设计(STM32 HAL库)

3.1 开发环境

3.2 核心原理

数字编码器输出A/B两相正交脉冲(相位差90°),通过捕获A/B相的边沿并判断相位关系,可实现:

3.3 核心代码实现

3.3.1 头文件 encoder_capture.h(寄存器定义与全局变量)

#ifndef __ENCODER_CAPTURE_H
#define __ENCODER_CAPTURE_H

#include "stm32f4xx_hal.h"

// ==================== TIM5通道定义(对应编码器引脚) ====================
#define ENCODER1_A_CH   TIM_CHANNEL_1  // PA0(TIM5_CH1)
#define ENCODER1_B_CH   TIM_CHANNEL_2  // PA1(TIM5_CH2)
#define ENCODER2_A_CH   TIM_CHANNEL_3  // PA2(TIM5_CH3)
#define ENCODER2_B_CH   TIM_CHANNEL_4  // PA3(TIM5_CH4)

// ==================== 编码器状态结构体 ====================
typedef struct {
    int32_t count;       // 脉冲计数(正转+1,反转-1)
    uint8_t direction;   // 方向:0=停止,1=正转,2=反转
} Encoder_TypeDef;

// ==================== 全局变量 ====================
extern TIM_HandleTypeDef htim5;
extern Encoder_TypeDef encoder1;  // 编码器1状态
extern Encoder_TypeDef encoder2;  // 编码器2状态

// ==================== 函数声明 ====================
void MX_TIM5_Init(void);                          // TIM5初始化(输入捕获模式)
void HAL_TIM_IC_CaptureCallback(TIM_HandleTypeDef *htim);  // 输入捕获中断回调
void Encoder_Process(Encoder_TypeDef *enc, uint32_t channel, GPIO_TypeDef *gpio_port, uint16_t gpio_pin_a, uint16_t gpio_pin_b);  // 编码器数据处理

#endif

3.3.2 TIM5初始化(输入捕获模式)

#include "encoder_capture.h"
#include "gpio.h"

TIM_HandleTypeDef htim5;
Encoder_TypeDef encoder1 = {0};  // 编码器1初始计数0
Encoder_TypeDef encoder2 = {0};  // 编码器2初始计数0

// TIM5初始化(4通道输入捕获,双边沿触发)
void MX_TIM5_Init(void) {
    TIM_IC_InitTypeDef sConfigIC = {0};

    htim5.Instance = TIM5;
    htim5.Init.Prescaler = 0;                  // 不分频(168MHz时钟,计数频率168MHz)
    htim5.Init.CounterMode = TIM_COUNTERMODE_UP; // 向上计数
    htim5.Init.Period = 0xFFFFFFFF;             // 最大计数值(4通道独立捕获,无需ARR限制)
    htim5.Init.ClockDivision = TIM_CLOCKDIVISION_DIV1;
    HAL_TIM_IC_Init(&htim5);

    // 配置通道1(编码器1_A相):双边沿触发,无滤波
    sConfigIC.ICPolarity = TIM_ICPOLARITY_BOTHEDGE;  // 双边沿触发
    sConfigIC.ICSelection = TIM_ICSELECTION_DIRECTTI; // 直接映射
    sConfigIC.ICPrescaler = TIM_ICPSC_DIV1;           // 不分频(每个边沿捕获)
    sConfigIC.ICFilter = 0x00;                        // 无滤波(如需抗噪可设为0x0F)
    HAL_TIM_IC_ConfigChannel(&htim5, &sConfigIC, ENCODER1_A_CH);

    // 配置通道2(编码器1_B相):同上
    HAL_TIM_IC_ConfigChannel(&htim5, &sConfigIC, ENCODER1_B_CH);

    // 配置通道3(编码器2_A相):同上
    HAL_TIM_IC_ConfigChannel(&htim5, &sConfigIC, ENCODER2_A_CH);

    // 配置通道4(编码器2_B相):同上
    HAL_TIM_IC_ConfigChannel(&htim5, &sConfigIC, ENCODER2_B_CH);

    // 使能TIM5中断(输入捕获中断)
    HAL_TIM_IC_Start_IT(&htim5, ENCODER1_A_CH);  // 启动通道1捕获
    HAL_TIM_IC_Start_IT(&htim5, ENCODER1_B_CH);  // 启动通道2捕获
    HAL_TIM_IC_Start_IT(&htim5, ENCODER2_A_CH);  // 启动通道3捕获
    HAL_TIM_IC_Start_IT(&htim5, ENCODER2_B_CH);  // 启动通道4捕获
}

3.3.3 GPIO初始化(TIM5通道引脚配置)

void MX_GPIO_Init(void) {
    GPIO_InitTypeDef GPIO_InitStruct = {0};

    // TIM5通道引脚(PA0~PA3):复用功能输入
    __HAL_RCC_GPIOA_CLK_ENABLE();
    GPIO_InitStruct.Pin = GPIO_PIN_0 | GPIO_PIN_1 | GPIO_PIN_2 | GPIO_PIN_3;
    GPIO_InitStruct.Mode = GPIO_MODE_AF_PP;       // 复用推挽(输入捕获)
    GPIO_InitStruct.Pull = GPIO_PULLUP;           // 上拉电阻(编码器输出开漏时需上拉)
    GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_HIGH;
    GPIO_InitStruct.Alternate = GPIO_AF2_TIM5;    // TIM5复用功能(AF2)
    HAL_GPIO_Init(GPIOA, &GPIO_InitStruct);
}

3.3.4 输入捕获中断回调(核心处理逻辑)

// 输入捕获中断回调函数(HAL库自动调用)
void HAL_TIM_IC_CaptureCallback(TIM_HandleTypeDef *htim) {
    if (htim->Instance != TIM5) return;  // 仅处理TIM5中断

    // 编码器1_A相(CH1)捕获事件
    if (htim->Channel == ENCODER1_A_CH) {
        Encoder_Process(&encoder1, ENCODER1_A_CH, GPIOA, GPIO_PIN_0, GPIO_PIN_1);
    }
    // 编码器1_B相(CH2)捕获事件(可选:若仅需A相触发,可忽略B相中断)
    else if (htim->Channel == ENCODER1_B_CH) {
        // 可在此处补充B相处理逻辑(如冗余校验)
    }
    // 编码器2_A相(CH3)捕获事件
    else if (htim->Channel == ENCODER2_A_CH) {
        Encoder_Process(&encoder2, ENCODER2_A_CH, GPIOA, GPIO_PIN_2, GPIO_PIN_3);
    }
    // 编码器2_B相(CH4)捕获事件(可选)
    else if (htim->Channel == ENCODER2_B_CH) {
        // 可在此处补充B相处理逻辑
    }
}

// 编码器数据处理函数(判断方向+计数)
void Encoder_Process(Encoder_TypeDef *enc, uint32_t channel, GPIO_TypeDef *gpio_port, uint16_t gpio_pin_a, uint16_t gpio_pin_b) {
    uint8_t a_level = HAL_GPIO_ReadPin(gpio_port, gpio_pin_a);  // A相当前电平
    uint8_t b_level = HAL_GPIO_ReadPin(gpio_port, gpio_pin_b);  // B相当前电平

    // 仅处理A相捕获事件(通过A相边沿判断方向)
    if (channel == ENCODER1_A_CH || channel == ENCODER2_A_CH) {
        if (a_level) {  // A相上升沿(从高到低?需根据硬件确认,此处假设捕获上升沿)
            enc->direction = (b_level) ? 1 : 2;  // B相高→正转(1),低→反转(2)
        } else {  // A相下降沿
            enc->direction = (b_level) ? 2 : 1;  // B相高→反转(2),低→正转(1)
        }
        // 更新计数(正转+1,反转-1)
        enc->count += (enc->direction == 1) ? 1 : -1;
    }
}

3.3.5 主函数(读取编码器数据)

#include "main.h"
#include "encoder_capture.h"

int main(void) {
    HAL_Init();
    SystemClock_Config();  // 168MHz时钟配置
    MX_GPIO_Init();        // GPIO初始化(TIM5引脚)
    MX_TIM5_Init();        // TIM5输入捕获初始化

    while (1) {
        // 读取编码器1数据(示例:通过串口打印)
        printf("Encoder1: Count=%ld, Direction=%d\r\n", encoder1.count, encoder1.direction);
        printf("Encoder2: Count=%ld, Direction=%d\r\n", encoder2.count, encoder2.direction);
        HAL_Delay(100);  // 100ms刷新一次
    }
}

// printf重定向到USART1(调试用)
int fputc(int ch, FILE *f) {
    HAL_UART_Transmit(&huart1, (uint8_t *)&ch, 1, 100);
    return ch;
}

参考代码 基于stm32f407,利用定时器5的四个输入捕获通道,来捕捉两个数字编码器输出的波形 www.youwenfan.com/contentcss/182710.html

四、关键问题与解决方案

4.1 编码器信号干扰

4.2 中断优先级冲突

4.3 方向判断错误

五、测试与验证

  1. 硬件连接:按2.2节连接编码器与STM32,确保光耦隔离电路正常;
  2. 功能测试:手动旋转编码器1,观察encoder1.count是否递增/递减(正转+1,反转-1);
  3. 精度测试:编码器旋转1圈(如20脉冲),计数应为20×4=80(双边沿触发,4倍频),误差<±1%。

六、总结

基于STM32F407的TIM5四通道输入捕获,实现了双编码器的波形捕捉与脉冲计数。核心是双边沿触发提高分辨率、相位关系判断方向、中断回调实时处理。通过模块化设计,可扩展至多编码器系统或结合PID算法实现闭环控制。

专注于matlab/simulink,电子电路,编程