当前位置: 首页 > news >正文

STM32 HAL库实战:PWM输出在写Flash时如何避免舵机抖动?一个真实案例的解决思路

STM32 HAL库实战:PWM输出在写Flash时如何避免舵机抖动?一个真实案例的解决思路

在嵌入式开发中,PWM(脉冲宽度调制)信号的控制精度直接影响到舵机等执行机构的稳定性。当STM32在进行Flash写入操作时,由于中断被临时关闭,可能导致PWM信号异常,进而引发舵机抖动。本文将深入分析这一问题的根源,并提供几种经过验证的解决方案。

1. 问题现象与根源分析

最近在一个舵机控制项目中遇到了一个棘手的问题:当STM32执行Flash写入操作时,舵机会出现明显的抖动现象。通过逻辑分析仪捕获的波形显示,正常情况下稳定的PWM信号在写Flash期间变成了20ms高电平、20ms低电平的异常波形。

问题根源可以归结为以下几点:

  1. 中断关闭的影响:Flash写入操作需要关闭全局中断(__disable_irq()),而PWM的比较值更新通常是在定时器中断中完成的。当中断被关闭,比较值无法及时更新,导致PWM信号异常。

  2. 硬件特性限制:STM32的定时器在中断被禁用期间会继续运行,但无法响应比较匹配事件,造成输出信号锁定在最后状态或出现不可预测的跳变。

  3. 舵机控制特性:舵机对PWM信号的稳定性非常敏感,特别是信号中的高电平持续时间决定了舵机位置。异常信号会导致舵机不断尝试调整位置,表现为抖动。

2. HAL库中PWM控制的几种方式

在STM32 HAL库中,控制PWM输出有多种方法,每种方法适用于不同的场景:

方法函数调用适用场景优点缺点
修改比较值__HAL_TIM_SET_COMPARE()动态调整占空比响应快,不中断PWM输出需要中断配合
停止定时器HAL_TIM_OC_Stop()完全停止PWM输出彻底停止输出重启需要时间
强制输出电平HAL_TIM_OC_ConfigChannel()紧急状态控制立即生效配置复杂
禁用通道TIM_CCxCmd(TIMx, TIM_CHANNEL_x, DISABLE)临时关闭特定通道精确控制需要直接寄存器操作

提示:在选择解决方案时,需要考虑系统实时性要求、中断关闭时间以及对其他外设的影响。

3. 解决方案一:等待低电平点同步

这种方法的核心思想是在执行Flash写入前,确保PWM信号处于低电平状态,然后锁定输出。

3.1 实现步骤

  1. 检测当前电平状态

    while(HAL_GPIO_ReadPin(TIMCH_Buff[i].GPIOx, TIMCH_Buff[i].GPIO_Pin) != GPIO_PIN_RESET) { // 等待直到引脚变为低电平 }
  2. 设置比较值为最大值

    __HAL_TIM_SET_COMPARE(&TIMCH_Buff[i].htim, TIMCH_Buff[i].channel, 64000);
  3. 执行Flash写入

    __disable_irq(); Write_Flash_Buf(FLASH_RF_PIN_MODE_ADR, (uint8_t *)modebuff, sizeof(modebuff)); Write_Flash_Buf(FLASH_RF_PIN_DATA_ADR, Slave_re_Pindata_temp, sizeof(Slave_re_Pindata_temp)); __enable_irq();

3.2 定时器中断中的处理

为了配合这种方法,需要在定时器中断中添加特殊处理:

if(HAL_GPIO_ReadPin(timch->GPIOx, timch->GPIO_Pin) == GPIO_PIN_SET) { // 高电平时的处理 timch->count = __HAL_TIM_GET_COMPARE(&timch->htim, timch->channel) + (4864+Buf[rps[timch->rpspin].mode]-2048); timch->count = (timch->count > TIM_PERIOD) ? (timch->count-TIM_PERIOD) : timch->count; __HAL_TIM_SET_COMPARE(&timch->htim, timch->channel, timch->count); } else { // 低电平时的处理 __HAL_TIM_SET_COMPARE(&timch->htim, timch->channel, 64000); }

4. 解决方案二:定时器启停控制

这种方法不依赖中断,而是直接控制定时器的启停:

// 等待低电平 while(HAL_GPIO_ReadPin(TIMCH_Buff[i].GPIOx, TIMCH_Buff[i].GPIO_Pin) != GPIO_PIN_RESET){} // 停止定时器输出 HAL_TIM_OC_Stop_IT(&TIMCH_Buff[i].htim, TIMCH_Buff[i].channel); // 执行Flash写入 Write_Flash_Buf(FLASH_RF_PIN_MODE_ADR,(uint8_t *) modebuff, sizeof(modebuff)); Write_Flash_Buf(FLASH_RF_PIN_DATA_ADR, Slave_re_Pindata_temp, sizeof(Slave_re_Pindata_temp)); // 重新启动定时器 HAL_TIM_OC_Start_IT(&TIMCH_Buff[i].htim, TIMCH_Buff[i].channel);

优点

  • 不依赖中断状态
  • 实现简单直接

缺点

  • 定时器重启需要一定时间
  • 可能引入微小的时序偏差

5. 进阶优化:DMA辅助的Flash写入

对于更高要求的应用,可以考虑使用DMA来减少CPU干预和中断关闭时间:

  1. 配置DMA通道

    hdma_flash.Instance = DMA1_Channel1; hdma_flash.Init.Direction = DMA_MEMORY_TO_MEMORY; hdma_flash.Init.PeriphInc = DMA_PINC_ENABLE; hdma_flash.Init.MemInc = DMA_MINC_ENABLE; hdma_flash.Init.PeriphDataAlignment = DMA_PDATAALIGN_BYTE; hdma_flash.Init.MemDataAlignment = DMA_MDATAALIGN_BYTE; hdma_flash.Init.Mode = DMA_NORMAL; hdma_flash.Init.Priority = DMA_PRIORITY_HIGH; HAL_DMA_Init(&hdma_flash);
  2. 使用DMA写入Flash

    HAL_DMA_Start(&hdma_flash, (uint32_t)src, (uint32_t)dst, size); HAL_DMA_PollForTransfer(&hdma_flash, HAL_DMA_FULL_TRANSFER, 100);

这种方法可以显著减少中断关闭时间,但需要更复杂的配置和对DMA资源的合理管理。

6. 实际项目中的经验分享

在多个实际项目中应用这些解决方案后,总结出以下几点经验:

  • 等待时间优化:在等待低电平的循环中,可以加入超时机制,避免死锁:

    uint32_t timeout = 1000; // 1ms超时 while(HAL_GPIO_ReadPin(GPIOx, GPIO_Pin) != GPIO_PIN_RESET && timeout--) { // 适当延时 }
  • 多通道协调:当需要控制多个PWM通道时,应该:

    1. 按顺序等待每个通道进入低电平
    2. 批量设置所有通道的比较值
    3. 执行Flash操作
    4. 恢复所有通道
  • 性能考量:对于高频PWM信号,等待低电平可能引入不可接受的延迟,此时应考虑:

    • 使用更高优先级的定时器
    • 采用硬件PWM生成方式
    • 优化Flash写入频率和大小

在最近的一个机械臂控制项目中,采用方案一结合DMA辅助写入,成功将舵机抖动控制在不可感知的范围内,同时保证了系统响应速度。

http://www.cnnetsun.cn/news/2039842.html

相关文章:

  • 如何处理.NET中的Oracle Number溢出_OracleDecimal与C# decimal数据类型对应
  • Ubuntu 24.04下MT7922蓝牙驱动问题解决方案
  • Python 容器类型判断与类型转换
  • MFlow02-项目学习指南
  • 量子计算噪声与误差边界:理论与工程实践
  • STM32G474与F334系列HRTIM实战:从CubeMX配置到移相全桥PWM生成
  • epoll_event
  • 单入射方向光波导耦合光栅的优化
  • Go语言如何发GET请求_Go语言HTTP GET请求教程【总结】
  • AI优化电动汽车充电:PSO算法与GPU加速实践
  • Foundry IQ + Foundry Agent:从知识流到企业级 Agent
  • 联合概率、边缘概率与条件概率:机器学习基础解析
  • 第7篇:抽象基类(ABC)与接口设计
  • 如何用CoolProp在7天内掌握免费热力学物性计算?
  • ESP32多伺服控制器HAT:树莓派与无线控制实战
  • 基于全域数学的宇宙螺旋场统一结构研究【乖乖数学】
  • 如何永久保存微信聊天记录?免费工具WeChatMsg完整使用指南
  • 1×1卷积:深度学习中的通道操作利器
  • 自然语言处理四大核心技术路径解析与实践
  • 2026年移动应用动态发布,不发版怎么做?一站式方案解析
  • 别再只会用K-Means了!用Python的Scikit-learn实战DBSCAN和谱聚类,搞定非球形数据
  • 【Blazor 2026终极配置指南】:零基础30分钟完成生产级WebAssembly+Auto-Render混合部署
  • 树莓派4B串口通信实战:从蓝牙占用解除到PC双向通信(附完整代码)
  • LSTM时间序列预测中的特征工程实践与优化
  • 云从科技第一季营收2323万:同比降37% 净亏1301万
  • 006、PCIE物理层基础:通道、速率与编码
  • Genesis IoT Discovery Lab模块化开发平台解析与应用
  • 缓存基础知识:缓存策略、过期、击穿与雪崩
  • 考研线代救命指南:行最简形矩阵和标准形到底怎么化?看完这篇就够了
  • Vue 转 React:揭秘样式语言是如何被 VuReact 编译的?