4. 编码器接口读取
任务目标
使用STM32定时器的编码器接口模式(Encoder Mode)连接增量式编码器,通过定时器硬件对A/B相脉冲进行正交解码,获取电机转子位置(累计脉冲数)与转速,并通过串口周期性输出。本案例以STM32F103系列为例,使用TIM4的CH1/CH2作为编码器输入。
硬件连接与依赖
- 编码器A相 → PA6 (TIM4_CH1),B相 → PA7 (TIM4_CH2)
- 编码器为推挽输出或开漏输出(需外部上拉),电压匹配3.3V
- 依赖HAL库(stm32f1xx_hal_tim.h、stm32f1xx_hal_uart.h)
- 串口:USART1(PA9/PA10),波特率115200
核心实现
定时器编码器模式利用硬件对A/B相信号进行正交解码,TIM4的计数器CNT在每对边沿自动增减,无需软件中断。位置通过读取CNT获得,转速通过定时采样CNT差值除以采样周期计算。以下代码完成初始化、位置/转速计算及串口输出。
// 编码器接口读取 - 基于STM32F103 HAL库
#include "stm32f1xx_hal.h"
TIM_HandleTypeDef htim4;
UART_HandleTypeDef huart1;
// 编码器参数
#define ENCODER_PPR 400 // 编码器物理分辨率(线/圈)
#define GEAR_RATIO 30 // 减速比(电机轴:输出轴)
#define SAMPLE_PERIOD_MS 100 // 转速采样周期(ms)
#define TIM4_PRESCALER 0 // 编码器模式不分频
// 位置与速度变量
volatile int32_t last_count = 0;
volatile int32_t position = 0; // 累计脉冲数(带方向)
volatile float speed_rpm = 0.0f; // 输出轴转速(RPM)
// 定时器编码器模式初始化
void Encoder_Init(void) {
__HAL_RCC_TIM4_CLK_ENABLE();
__HAL_RCC_GPIOA_CLK_ENABLE();
__HAL_RCC_USART1_CLK_ENABLE();
// GPIO: PA6=CH1, PA7=CH2
GPIO_InitTypeDef gpio = {0};
gpio.Pin = GPIO_PIN_6 | GPIO_PIN_7;
gpio.Mode = GPIO_MODE_AF_PP;
gpio.Pull = GPIO_PULLUP; // 编码器输出为开漏时需上拉
gpio.Speed = GPIO_SPEED_FREQ_HIGH;
HAL_GPIO_Init(GPIOA, &gpio);
// TIM4 编码器模式配置
htim4.Instance = TIM4;
htim4.Init.Prescaler = TIM4_PRESCALER;
htim4.Init.CounterMode = TIM_COUNTERMODE_UP;
htim4.Init.Period = 0xFFFF; // 16位自动重载
htim4.Init.ClockDivision = TIM_CLOCKDIVISION_DIV1;
HAL_TIM_Encoder_Init(&htim4, &(TIM_Encoder_InitTypeDef){
.EncoderMode = TIM_ENCODERMODE_TI12, // 双沿计数(4倍频)
.IC1Polarity = TIM_ICPOLARITY_RISING,
.IC2Polarity = TIM_ICPOLARITY_RISING
});
// 启动编码器接口
HAL_TIM_Encoder_Start(&htim4, TIM_CHANNEL_ALL);
__HAL_TIM_SET_COUNTER(&htim4, 0);
}
// 串口初始化(115200-8-N-1)
void UART1_Init(void) {
GPIO_InitTypeDef gpio = {0};
gpio.Pin = GPIO_PIN_9 | GPIO_PIN_10;
gpio.Mode = GPIO_MODE_AF_PP;
gpio.Speed = GPIO_SPEED_FREQ_HIGH;
HAL_GPIO_Init(GPIOA, &gpio);
huart1.Instance = USART1;
huart1.Init.BaudRate = 115200;
huart1.Init.WordLength = UART_WORDLENGTH_8B;
huart1.Init.StopBits = UART_STOPBITS_1;
huart1.Init.Parity = UART_PARITY_NONE;
huart1.Init.Mode = UART_MODE_TX_RX;
HAL_UART_Init(&huart1);
}
// 读取当前累计位置(单位:脉冲数,带方向)
int32_t Encoder_GetPosition(void) {
return (int32_t)__HAL_TIM_GET_COUNTER(&htim4);
}
// 计算转速:基于定时采样差值
void Encoder_UpdateSpeed(void) {
int32_t current = Encoder_GetPosition();
int32_t delta = current - last_count; // 采样周期内脉冲增量
last_count = current;
// 转速计算:delta / (4*PPR) 为电机轴圈数,除以采样时间得RPM
// 再除以减速比得到输出轴转速
float motor_rpm = (float)delta * 60000.0f / (4.0f * ENCODER_PPR * SAMPLE_PERIOD_MS);
speed_rpm = motor_rpm / GEAR_RATIO;
}
// 主循环示例
int main(void) {
HAL_Init();
UART1_Init();
Encoder_Init();
uint32_t last_tick = HAL_GetTick();
char msg[64];
while (1) {
if (HAL_GetTick() - last_tick >= SAMPLE_PERIOD_MS) {
last_tick = HAL_GetTick();
Encoder_UpdateSpeed();
position = Encoder_GetPosition();
// 串口输出:位置(脉冲) 转速(RPM)
int len = snprintf(msg, sizeof(msg), "Pos:%ld Speed:%.2f RPM\r\n",
(long)position, (double)speed_rpm);
HAL_UART_Transmit(&huart1, (uint8_t*)msg, len, 100);
}
}
}
编译与运行
- 使用STM32CubeMX生成工程时,将TIM4的Combined Channels配置为
Encoder Mode,并确认PA6/PA7已分配。 - 编译下载后,连接编码器A/B相至PA6/PA7,共地并确保供电匹配。
- 打开串口助手(115200-8-N-1),观察输出。
验证方法
- 位置验证:手动缓慢旋转电机输出轴一整圈,串口输出的Pos值应变化
4 × PPR × GEAR_RATIO = 4×400×30 = 48000个脉冲(正转增加,反转减少)。 - 转速验证:用已知转速的电机驱动(或手摇匀速),观察Speed值是否在合理范围。例如输出轴1 RPM时,Speed应显示约1.0。
- 方向验证:反转电机,Pos值递减,Speed显示负值。
若读数异常,检查编码器A/B相是否接反(交换PA6/PA7)、上拉电阻是否匹配、编码器供电电压是否在逻辑高电平范围内。