STM32F103RCT6定时器实战:从LED闪烁到PWM呼吸灯全流程解析
STM32F103RCT6定时器实战:从LED闪烁到PWM呼吸灯全流程解析
在嵌入式开发领域,STM32系列微控制器因其出色的性能和丰富的外设资源而广受欢迎。作为入门级开发者的首选,STM32F103RCT6凭借其强大的定时器功能,能够实现从简单的时间控制到复杂的PWM波形生成等多种应用。本文将带您深入探索这款芯片的定时器模块,通过两个经典案例——LED闪烁和PWM呼吸灯,手把手教您掌握定时器的配置与使用技巧。
1. 硬件准备与环境搭建
在开始编码之前,我们需要确保开发环境准备就绪。对于STM32F103RCT6开发,通常需要以下硬件和软件:
-
硬件清单:
- STM32F103RCT6开发板(如正点原子MiniSTM32或野火指南者)
- USB转串口调试工具
- LED模块(或开发板自带LED)
- 杜邦线若干
- 示波器(可选,用于观察PWM波形)
-
软件工具:
- STM32CubeMX(图形化配置工具)
- Keil MDK或IAR Embedded Workbench(IDE)
- ST-Link Utility(烧录工具)
- 串口调试助手(如Putty或SecureCRT)
提示:初学者建议使用开发板自带的LED进行实验,避免因外部电路连接问题导致调试困难。
安装好开发环境后,我们需要在STM32CubeMX中创建新项目。选择正确的芯片型号STM32F103RCT6,配置系统时钟为72MHz(这是该芯片的最高主频),然后保存生成初始化代码。
2. 定时器基础与LED闪烁实现
2.1 定时器工作原理
STM32F103RCT6包含多个定时器,分为高级定时器(TIM1/TIM8)、通用定时器(TIM2-TIM5)和基本定时器(TIM6/TIM7)。它们的基本工作原理类似:
- 时钟源选择:定时器可以来自内部时钟(APB总线)、外部时钟或其它定时器
- 预分频器(PSC):对输入时钟进行分频,降低计数频率
- 计数器(CNT):根据分频后的时钟进行递增或递减计数
- 自动重装载寄存器(ARR):定义计数器的上限值
定时器溢出时间计算公式为:
Tout = (ARR + 1) * (PSC + 1) / Tclk
其中Tclk为定时器时钟频率。
2.2 配置定时器实现1秒LED闪烁
让我们通过CubeMX配置TIM2实现1秒定时:
- 在Pinout & Configuration界面,选择TIM2
- 配置Clock Source为Internal Clock
- 在Parameter Settings中设置:
- Prescaler (PSC): 7199
- Counter Mode: Up
- Counter Period (ARR): 9999
- auto-reload preload: Enable
这样配置下,定时器溢出时间为:
Tout = (9999 + 1) * (7199 + 1) / 72,000,000 = 1秒
生成代码后,我们需要添加中断处理逻辑。在stm32f1xx_it.c中找到TIM2_IRQHandler函数,它会调用HAL_TIM_IRQHandler。我们只需重写回调函数即可:
void HAL_TIM_PeriodElapsedCallback(TIM_HandleTypeDef *htim) {
if (htim->Instance == TIM2) {
HAL_GPIO_TogglePin(GPIOB, GPIO_PIN_5); // 假设LED连接在PB5
}
}
最后,在主函数中启动定时器中断:
int main(void) {
HAL_Init();
SystemClock_Config();
MX_GPIO_Init();
MX_TIM2_Init();
HAL_TIM_Base_Start_IT(&htim2); // 启动定时器中断
while (1) {
// 主循环为空,所有工作由中断处理
}
}
3. PWM原理与呼吸灯实现
3.1 PWM技术基础
脉冲宽度调制(PWM)是一种通过调节脉冲占空比来模拟不同电压水平的技术。在STM32中,PWM通过定时器的输出比较功能实现,主要涉及以下寄存器:
| 寄存器 | 功能描述 |
|---|---|
| CCRx | 捕获/比较寄存器,决定PWM占空比 |
| CNT | 计数器当前值 |
| ARR | 自动重装载值,决定PWM周期 |
PWM占空比计算公式为:
占空比 = CCRx / ARR * 100%
3.2 配置TIM1实现呼吸灯效果
我们使用TIM1的通道1输出PWM,配置步骤如下:
- 在CubeMX中选择TIM1
- 配置Clock Source为Internal Clock
- 在Parameter Settings中设置:
- Prescaler: 71 (得到1MHz计数频率)
- Counter Mode: Up
- Counter Period: 999 (PWM周期1ms)
- PWM Generation CH1: Enable
- 在GPIO Settings中确认TIM1_CH1对应的引脚(通常是PA8)
生成代码后,编写呼吸灯效果的主循环:
int main(void) {
HAL_Init();
SystemClock_Config();
MX_GPIO_Init();
MX_TIM1_Init();
HAL_TIM_PWM_Start(&htim1, TIM_CHANNEL_1); // 启动PWM输出
uint16_t pwmVal = 0;
uint8_t dir = 1; // 方向标志,1为增加,0为减少
while (1) {
HAL_Delay(10); // 10ms调整一次占空比
if (dir) {
pwmVal++;
if (pwmVal >= 1000) dir = 0;
} else {
pwmVal--;
if (pwmVal == 0) dir = 1;
}
__HAL_TIM_SET_COMPARE(&htim1, TIM_CHANNEL_1, pwmVal);
}
}
这段代码实现了PWM占空比从0%到100%的平滑变化,再回到0%,循环往复,形成呼吸灯效果。
4. 进阶技巧与调试方法
4.1 使用输入捕获测量PWM参数
STM32的定时器不仅可以生成PWM,还能测量外部PWM信号的频率和占空比。以下是使用TIM3测量PWM的基本步骤:
- 配置TIM3的通道1为输入捕获模式(PWM输入模式)
- 设置触发源和从模式
- 编写中断处理代码计算频率和占空比
关键代码片段:
// 在捕获中断回调函数中
void HAL_TIM_IC_CaptureCallback(TIM_HandleTypeDef *htim) {
static uint32_t IC1Value, IC2Value;
if (htim->Channel == HAL_TIM_ACTIVE_CHANNEL_1) {
// 上升沿捕获,测量周期
IC1Value = HAL_TIM_ReadCapturedValue(htim, TIM_CHANNEL_1);
}
else if (htim->Channel == HAL_TIM_ACTIVE_CHANNEL_2) {
// 下降沿捕获,测量脉宽
IC2Value = HAL_TIM_ReadCapturedValue(htim, TIM_CHANNEL_2);
// 计算频率和占空比
uint32_t period = IC1Value;
uint32_t pulse = IC2Value;
float frequency = 72000000.0 / (htim3.Init.Prescaler + 1) / period;
float dutyCycle = (float)pulse / period * 100;
}
}
4.2 常见问题排查
在实际开发中,可能会遇到以下问题及解决方案:
-
定时器不工作:
- 检查时钟配置是否正确
- 确认定时器使能(__HAL_RCC_TIMx_CLK_ENABLE)
- 验证中断优先级设置
-
PWM无输出:
- 确认GPIO已正确配置为复用功能
- 检查PWM通道是否启用
- 验证CCRx寄存器值是否在合理范围内
-
测量结果不准确:
- 确保输入信号质量良好
- 调整预分频值以获得更好的分辨率
- 考虑使用硬件滤波减少噪声影响
5. 性能优化与高级应用
5.1 使用DMA减轻CPU负担
对于需要频繁更新PWM参数的场景,可以使用DMA自动传输数据到定时器寄存器:
// 配置DMA循环传输PWM值数组
uint16_t pwmValues[] = {0, 100, 200, ..., 1000, 900, ..., 0};
HAL_TIM_PWM_Start_DMA(&htim1, TIM_CHANNEL_1, (uint32_t*)pwmValues, sizeof(pwmValues)/sizeof(uint16_t));
5.2 多定时器同步应用
STM32支持定时器主从同步,可以实现复杂波形生成。例如,使用TIM2作为主定时器触发TIM3:
// TIM2配置
htim2.Instance = TIM2;
htim2.Init.RepetitionCounter = 0;
htim2.Init.AutoReloadPreload = TIM_AUTORELOAD_PRELOAD_ENABLE;
// TIM3配置为从模式
sSlaveConfig.SlaveMode = TIM_SLAVEMODE_TRIGGER;
sSlaveConfig.InputTrigger = TIM_TS_ITR1; // TIM2作为触发源
HAL_TIM_SlaveConfigSynchro(&htim3, &sSlaveConfig);
这种配置在电机控制、音频合成等场景非常有用。
openvela 操作系统专为 AIoT 领域量身定制,以轻量化、标准兼容、安全性和高度可扩展性为核心特点。openvela 以其卓越的技术优势,已成为众多物联网设备和 AI 硬件的技术首选,涵盖了智能手表、运动手环、智能音箱、耳机、智能家居设备以及机器人等多个领域。
更多推荐


所有评论(0)