改变pwm的频率和占空比的两种方式

前言

以下内容仅代表个人观点,基于有限的经验和认知整理而成。每个人的视角和背景不同,观点难免存在差异或局限。若存在疏漏或不足之处,欢迎指正与探讨,但请多一份包容。希望通过这些思考,能激发更多有益的交流。
——
观点无高下,讨论有温度
改变pwm的频率和占空比的两种方式

STM32定时器配置:直接操作寄存器 vs HAL库函数详解

引言

在STM32开发中,定时器配置是嵌入式系统设计的核心技能之一。开发人员常面临一个关键选择:直接操作寄存器还是使用HAL库函数?本文将通过对比分析TIMx->ARR/CCRx直接赋值与__HAL_TIM_SET_AUTORELOAD/__HAL_TIM_SET_COMPARE两种方式,揭示它们的底层差异与应用场景。

一、STM32定时器基础原理

1.1 关键寄存器作用

  • ARR(Auto-Reload Register):确定定时器的计数周期,当计数器达到ARR值时触发更新事件
  • CCR(Capture/Compare Register):在PWM模式下决定输出脉冲的占空比
  • PSC(Prescaler):对时钟源分频,扩展定时范围

1.2 影子寄存器机制

STM32采用双寄存器结构提升稳定性:

  • 预装载寄存器:开发者直接操作的寄存器
  • 影子寄存器:实际控制硬件的寄存器
  • 更新事件(UEV):将预装载值同步到影子寄存器的触发条件

二、直接操作寄存器方法

2.1 典型代码示例

TIM15->ARR = 10000000 / pwm_f;  // 直接设置ARR
TIM15->CCR1 = pwm_d * TIM15->ARR / 100; // 直接设置CCR1

注:如若使用此方法需在“tim.h”找到对应的结构体变量并进行声明
改变pwm的频率和占空比的两种方式_第1张图片

“extern TIM_HandleTypeDef htim15;”

2.2 底层工作机制

  • 即时生效:写入后立即更新影子寄存器
  • 无缓冲机制:操作直接作用于硬件寄存器
  • 状态依赖:需手动处理寄存器更新时序

2.3 优势与风险

优势 风险
⚡ 执行效率高(无函数调用开销) ⚠️ 易引发竞争条件(如ARR/CCR未同步)
代码精简(适合裸机开发) ⚠️ 更新冲突导致PWM波形畸变
时序控制精确 ⚠️ 可移植性差(芯片型号变更需重写)

三、HAL库函数方法

3.1 函数原型解析

// 设置自动重装载值(缓冲模式)
__HAL_TIM_SET_AUTORELOAD(&htim3, 500);

// 设置比较寄存器值(立即或缓冲)
__HAL_TIM_SET_COMPARE(&htim3, TIM_CHANNEL_2, 250);

3.2 关键机制

  1. 预装载使能(ARPE)

    • TIMx_CR1.ARPE=1时,ARR修改延迟生效
    • 新ARR值在下次更新事件(UEV) 后同步
  2. 安全写入保证

    // HAL内部实现简化
    #define __HAL_TIM_SET_AUTORELOAD(__HANDLE__, __AUTORELOAD__) \
    do { \
      __HANDLE__->Instance->ARR = (__AUTORELOAD__); \ 
    } while(0)
    

    通过硬件确保原子操作,避免中间状态

3.3 配置示例

// 启用ARR预装载(缓冲模式)
TIM_ARRPreloadConfig(TIM15, ENABLE); 

// 设置PWM频率(缓冲写入)
__HAL_TIM_SET_AUTORELOAD(&htim15, 10000000 / pwm_f);

// 设置占空比(即时更新)
__HAL_TIM_SET_COMPARE(&htim15, TIM_CHANNEL_1, 
                     pwm_d * __HAL_TIM_GET_AUTORELOAD(&htim15) / 100);

注意点:CCR寄存器通常无预装载机制,修改立即生效

四、两种方法深度对比

4.1 核心差异分析

特性 直接操作寄存器 HAL库函数
ARR更新方式 即时生效 支持缓冲模式
线程安全性 需开发者保证 原子操作保障
可移植性 芯片依赖强 跨系列兼容
实时性 极高 中等(有调用开销)
适用场景 时序敏感型操作 复杂项目维护

4.2 时序行为对比

直接操作时序

写入ARR
立即更新影子寄存器
写入CCR
即时改变输出

HAL函数时序(ARPE=1时)

__HAL_TIM_SET_AUTORELOAD
值存入预装载寄存器
更新事件UEV
预装载值同步到影子寄存器
计数器达到ARR
新周期生效

五、实战选型建议

5.1 推荐直接操作寄存器的场景

  1. 高频PWM动态调整
    电机控制等需要微秒级响应的场景

  2. 资源受限系统
    RAM不足(HAL库增加~1KB内存占用)

  3. 精确波形生成
    如红外编码等严格时序要求

5.2 推荐HAL库的场景

  1. 多定时器协同工作
    利用HAL_TIM_Base_Start_IT()简化同步

  2. 动态频率切换
    避免PWM占空比突变:

    // 安全切换频率步骤
    __HAL_TIM_SET_AUTORELOAD(&htim, new_arr); // 缓冲ARR
    __HAL_TIM_SET_COMPARE(&htim, ch, new_ccr); // 更新CCR
    while(!__HAL_TIM_GET_FLAG(&htim, TIM_FLAG_UPDATE)); // 等待UEV
    __HAL_TIM_CLEAR_FLAG(&htim, TIM_FLAG_UPDATE);
    

六、最佳实践总结

  1. 关键参数计算
    使用标准公式确保精度:

    // PWM周期 = (ARR+1) * (PSC+1) / Tclk
    uint32_t psc = SystemCoreClock / 1000000 - 1; // 1MHz计数
    uint32_t arr = 1000000 / target_freq - 1; 
    
  2. 混合编程策略

    // 高频部分直接操作
    TIM15->CCR1 = duty; 
    
    // 低频参数用HAL修改
    __HAL_TIM_SET_AUTORELOAD(&htim15, new_period);
    
  3. 错误处理
    增加超时保护:

    #define TIM_UPDATE_TIMEOUT 1000 // 1ms超时
    uint32_t tick = HAL_GetTick();
    while(!__HAL_TIM_GET_FLAG(&htim, TIM_FLAG_UPDATE)) {
      if(HAL_GetTick() - tick > TIM_UPDATE_TIMEOUT) {
        // 错误处理
        break;
      }
    }
    

小贴士:在CubeMX中启用 “Auto-Reload Preload” 可激活ARR缓冲机制,大幅提升系统稳定性。

结语

直接寄存器操作与HAL库的本质区别在于时间与空间的权衡:前者以开发效率换取执行效率,后者以少量性能开销降低系统风险。在电机控制等实时场景,直接操作仍具优势;而对于物联网设备等复杂系统,HAL库的跨平台能力和安全特性更具价值。掌握二者原理,方能在不同场景中游刃有余。

希望此文能助您在定时器编程中做出更优选择,欢迎在评论区分享您的应用案例!

总结

此文仅代表个人愚见。

你可能感兴趣的:(stm32,单片机)