13.5 通用定时器项目:旋转编码器控制LED亮度
# 13.5 通用定时器项目:旋转编码器控制LED亮度
在本项目中,使用STM32的TIM2定时器,配置为编码器接口模式,实现通过旋转编码器控制LED亮度的功能。 核心功能:
- 编码器计数:旋转编码器调节PWM占空比(0~100),控制LED亮度。
- 按键切换:按下编码器按键切换红、绿、蓝三色LED通道。
- OLED显示:实时显示当前计数值(Encoder_cnt)。
# 13.5.1 旋转编码器硬件电路
# 旋转编码器连接
- 引脚分配:
- A相(TIM2_CH1) → PA15
- B相(TIM2_CH2) → PB3
- 按键(KEY) → PB4(内部上拉)
- 滤波电路:C20、C22、C27用于抑制信号抖动,R15、R19为上拉电阻。
# 13.5.2 STM32CubeMX配置
# 1. 建立新工程
- 芯片型号:STM32F103C8T6
- 项目命名:13 TIM_Encoder
- 主频:72MHz
- 调试接口:SWD
# 2. 配置TIM3
参考本章节上文配置TIM3工作模式:选择TIM3内部时钟源,将“通道2、3、4”配置为“PWM Generation CH2、3、4”,并开启定时器更新中断。配置TIM3的工作参数如下:
- Counter Mode(计数模式):Up;
- Counter Period(计数周期PRD):100-1;
- Internal Clock Divison(内部时钟分频CKD):No Division;
- Pulse(自动重装载寄存器ARR):50;
- auto-reload preload(自动重装载影子寄存器):Enable;
- Mode(输出模式):PWM mode 1;
- Output compare preload(输出比较影子寄存器):Enable;
- Fast Mode(快速模式):Disable;
- CH Polarity(通道极性):High;
# 3. 配置TIM2
# 1.使能TIM2编码器模式
①点击“Pinout & Configuration”标签页→②在左侧导航栏中选择“Timers”→③双击“TIM2”进行配置→④“Combined Channels”选择“Encoder Mode”→⑤将自动配置的PA0和PA1引脚修改为PB3和PA15。

# 2.使能TIM2编码器工作参数
- 预分频器PSC:2-1;
- Encoder Mode(编码器模式):Encoder Mode TI1;
- Polarity(通道极性):Rising Edge;
- IC Selection(输入捕获选择):Direct;
- Prescaler Division Ratio(预分频):No division;
- Input Filter(输入滤波):15;
- 通道2的极性为Falling Edge,其他与通道1相同。

# 4. 配置编码器按键
参考第5章,将编码器按键引脚PB4配置为输入引脚并配置为内部上拉输入模式。
# 5. 配置I2C1
参考第9章,使能I²C。
# 6. 引脚分配与复用
- PA7自动配置为TIM3_CH2;
- PB0自动配置为TIM3_CH3;
- PB1自动配置为TIM3_CH4;
- PB3配置为TIM2_CH2;
- PA15配置为TIM2_CH1;
- PB9配置为I2C1_SDA;
- PB8配置为I2C1_SCL;
- PB4配置为GPIO_Input;

# 7. 生成代码
参考第4章,在“Project Manager”标签页中的“Code Generator”导航栏,勾选为每个外设生成单独的“.c”和“.h”文件的选项。 最后,点击“Project→Generate Code”或工具栏的齿轮图标,再或者按下“Ctrl+S”快捷键保存,生成工程代码。在弹出的对话框中勾选“Open project”选项,即可进入代码编辑界面。经过上述的操作,STM32CubeIDE软件配置基本完成。
# 13.5.3 通用定时器项目2软件设计
# 1. 初始化函数(代码清单13‑6)
首先介绍TIM2的初始化函数,初始化函数将用户定义的初始化结构体中的配置参数传递给底层硬件。该初始化函数设置TIM2寄存器,如预分频值、自动重装载值等,并使能外设时钟、配置GPIO引脚复用功能以及设置编码器模式。通过结构体参数即可完成配置。
static void MX_TIM2_Init(void)
{
// 定义并初始化编码器配置结构体
TIM_Encoder_InitTypeDef sConfig = {0};
// 定义并初始化定时器主模式配置结构体
TIM_MasterConfigTypeDef sMasterConfig = {0};
// 配置TIM2定时器的实例
htim2.Instance = TIM2;
// 设置预分频器,2-1表示时钟频率为72MHz / 2 = 36MHz
htim2.Init.Prescaler = 2-1;
// 设置计数器模式为向上计数
htim2.Init.CounterMode = TIM_COUNTERMODE_UP;
// 设置自动重装载寄存器的值为65535(16位最大值)
htim2.Init.Period = 65535;
// 设置时钟分频为不分频
htim2.Init.ClockDivision = TIM_CLOCKDIVISION_DIV1;
// 禁用自动重装载预装载功能
htim2.Init.AutoReloadPreload = TIM_AUTORELOAD_PRELOAD_DISABLE;
// 配置编码器模式为TI1模式(仅使用TI1输入)
sConfig.EncoderMode = TIM_ENCODERMODE_TI1;
// 设置输入捕获通道1的极性为上升沿触发
sConfig.IC1Polarity = TIM_ICPOLARITY_RISING;
// 设置输入捕获通道1的信号选择为直接TI1信号
sConfig.IC1Selection = TIM_ICSELECTION_DIRECTTI;
// 设置输入捕获通道1的预分频器为不分频
sConfig.IC1Prescaler = TIM_ICPSC_DIV1;
// 设置输入捕获通道1的滤波器值为15(较高的滤波值可以滤除高频噪声)
sConfig.IC1Filter = 15;
// 设置输入捕获通道2的极性为下降沿触发
sConfig.IC2Polarity = TIM_ICPOLARITY_FALLING;
// 设置输入捕获通道2的信号选择为直接TI2信号
sConfig.IC2Selection = TIM_ICSELECTION_DIRECTTI;
// 设置输入捕获通道2的预分频器为不分频
sConfig.IC2Prescaler = TIM_ICPSC_DIV1;
// 设置输入捕获通道2的滤波器值为15
sConfig.IC2Filter = 15;
// 初始化TIM2定时器的编码器模式,如果初始化失败则调用错误处理函数
if (HAL_TIM_Encoder_Init(&htim2, &sConfig) != HAL_OK)
{
Error_Handler();
}
// 配置定时器的主模式,设置主输出触发为复位
sMasterConfig.MasterOutputTrigger = TIM_TRGO_RESET;
// 禁用主从模式
sMasterConfig.MasterSlaveMode = TIM_MASTERSLAVEMODE_DISABLE;
// 应用主模式配置,如果配置失败则调用错误处理函数
if(HAL_TIMEx_MasterConfigSynchronization(&htim2,&sMasterConfig) != HAL_OK)
{
Error_Handler();
}
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
# 2.主循环代码
首先初始化硬件外设,包括GPIO、TIM2(编码器模式)、TIM3(PWM输出)、I2C1和OLED显示屏。TIM2配置为编码器接口模式,通过HAL_TIM_Encoder_Start启动编码器计数,实时读取编码器计数值Encoder_cnt,并将其限制在0到100之间,用于动态设置TIM3当前PWM通道的占空比。当检测到按键GPIO_PIN_4按下时,停止当前PWM通道,并切换到Channels数组中的下一个PWM通道,然后重新启动PWM输出。
#include "main.h"
#include "font.h"
#include <stdio.h>
#include <string.h>
#include "oled.h"
int Encoder_cnt=0; // 编码器计数值
int Channel_index=0; // 输出通道选择
char message[20];
uint32_t Channels[3]={TIM_CHANNEL_2,TIM_CHANNEL_3,TIM_CHANNEL_4};
int main(void)
{
HAL_Init();
SystemClock_Config();
MX_GPIO_Init();
MX_TIM3_Init();
MX_TIM2_Init();
MX_I2C1_Init();
HAL_Delay(20);
OLED_Init();
HAL_TIM_Encoder_Start(&htim2, TIM_CHANNEL_ALL);
HAL_TIM_PWM_Start(&htim3, Channels[Channel_index]);
while (1)
{
Encoder_cnt = __HAL_TIM_GET_COUNTER(&htim2);
if(Encoder_cnt >= 60000)
{
Encoder_cnt = 0;
__HAL_TIM_SET_COUNTER(&htim2,0);
}
else if(Encoder_cnt > 100)
{
Encoder_cnt = 100;
__HAL_TIM_SET_COUNTER(&htim2,100);
}
if(HAL_GPIO_ReadPin(GPIOB, GPIO_PIN_4) == GPIO_PIN_RESET)
{
HAL_Delay(10);
if(HAL_GPIO_ReadPin(GPIOB, GPIO_PIN_4) == GPIO_PIN_RESET)
{
HAL_TIM_PWM_Stop(&htim3, Channels[Channel_index]);
Channel_index = (Channel_index + 1) % 3;
HAL_TIM_PWM_Start(&htim3, Channels[Channel_index]);
}
while(HAL_GPIO_ReadPin(GPIOB, GPIO_PIN_4) == GPIO_PIN_RESET);
}
__HAL_TIM_SET_COMPARE(&htim3,Channels[Channel_index],Encoder_cnt);
sprintf(message,"Encoder_cnt:%d",Encoder_cnt);
OLED_NewFrame();
OLED_PrintString(1,1,message, &font16x16, OLED_COLOR_NORMAL);
OLED_ShowFrame();
}
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
# 13.5.4 通用定时器项目2下载验证
# 现象与步骤
- 烧录程序:通过ST-LINK下载代码至开发板。
- 功能验证:
- 旋转编码器:顺时针旋转增加
Encoder_cnt
,逆时针减少(范围0~100)。 - LED亮度:计数值越大,当前通道LED越亮。
- 按键切换:按下按键切换红、绿、蓝三色LED通道。
- 旋转编码器:顺时针旋转增加
- OLED显示:实时显示当前计数值(如
Encoder_cnt:77
)。

关键说明:
- 编码器计数方向:通过设置通道2极性为
Falling Edge
,确保顺时针旋转时计数值递增。 - 占空比映射:
Encoder_cnt
直接映射为PWM的CCR值,范围0~100对应亮度0%~100%。 - 消抖逻辑:按键检测加入10ms延时,避免误触发。 $$ T_{\text{PWM}} = \frac{(\text{PSC}+1) \times (\text{ARR}+1)}{\text{CLK}} = \frac{720 \times 100}{72 \times 10^6} = 1 , \text{ms} $$