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)。它们的基本工作原理类似:

  1. 时钟源选择:定时器可以来自内部时钟(APB总线)、外部时钟或其它定时器
  2. 预分频器(PSC):对输入时钟进行分频,降低计数频率
  3. 计数器(CNT):根据分频后的时钟进行递增或递减计数
  4. 自动重装载寄存器(ARR):定义计数器的上限值

定时器溢出时间计算公式为:

Tout = (ARR + 1) * (PSC + 1) / Tclk

其中Tclk为定时器时钟频率。

2.2 配置定时器实现1秒LED闪烁

让我们通过CubeMX配置TIM2实现1秒定时:

  1. 在Pinout & Configuration界面,选择TIM2
  2. 配置Clock Source为Internal Clock
  3. 在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,配置步骤如下:

  1. 在CubeMX中选择TIM1
  2. 配置Clock Source为Internal Clock
  3. 在Parameter Settings中设置:
    • Prescaler: 71 (得到1MHz计数频率)
    • Counter Mode: Up
    • Counter Period: 999 (PWM周期1ms)
    • PWM Generation CH1: Enable
  4. 在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的基本步骤:

  1. 配置TIM3的通道1为输入捕获模式(PWM输入模式)
  2. 设置触发源和从模式
  3. 编写中断处理代码计算频率和占空比

关键代码片段:

// 在捕获中断回调函数中
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 常见问题排查

在实际开发中,可能会遇到以下问题及解决方案:

  1. 定时器不工作

    • 检查时钟配置是否正确
    • 确认定时器使能(__HAL_RCC_TIMx_CLK_ENABLE)
    • 验证中断优先级设置
  2. PWM无输出

    • 确认GPIO已正确配置为复用功能
    • 检查PWM通道是否启用
    • 验证CCRx寄存器值是否在合理范围内
  3. 测量结果不准确

    • 确保输入信号质量良好
    • 调整预分频值以获得更好的分辨率
    • 考虑使用硬件滤波减少噪声影响

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);

这种配置在电机控制、音频合成等场景非常有用。

Logo

openvela 操作系统专为 AIoT 领域量身定制,以轻量化、标准兼容、安全性和高度可扩展性为核心特点。openvela 以其卓越的技术优势,已成为众多物联网设备和 AI 硬件的技术首选,涵盖了智能手表、运动手环、智能音箱、耳机、智能家居设备以及机器人等多个领域。

更多推荐