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

STM32实现呼吸灯效果原理

在 PWM 实现呼吸灯的过程中,核心的 “比较” 发生在定时器内部的计数器(CNT)与捕获比较寄存器(CCR)之间,这是硬件自动完成的操作。结合你提供的呼吸灯代码,具体逻辑如下:

一、谁和谁比较?

  1. 比较双方

    • 计数器(CNT):定时器内部不断递增(或递减)的数值,范围是 0 到 ARR(自动重装载值,即 htim2.Init.Period)。
    • 捕获比较寄存器(CCR):存储你通过代码设置的占空比数值(即 pwm_buffer 中的值),是一个固定值(直到下一次更新)。
  2. 比较过程:定时器工作时,CNT 会从 0 开始递增,每来一个时钟脉冲加 1,直到达到ARR后重置为 0(向上计数模式)。在这个过程中,硬件会实时比较 CNT 和 CCR 的值

    • 当 CNT < CCR 时:PWM 输出 “有效电平”(由 OCPolarity 定义,如高电平,LED 亮)。
    • 当 CNT >= CCR 时:PWM 输出 “无效电平”(如低电平,LED 暗)。

二、如何通过比较实现呼吸灯?

 pwm_buffer 数组,本质是一系列动态变化的 CCR 值。结合硬件比较逻辑,过程如下:

  1. 初始状态

    • 假设 pwm_buffer[0] = 0(CCR=0)。
    • 比较结果:CNT 始终 >= 0 → 输出无效电平(LED 暗)。
  2. 亮度上升阶段

    • pwm_buffer 中的值逐渐增大(如从 0→50→100→...→ARR)。
    • CCR 随数组值增大,CNT < CCR 的时间变长 → 有效电平持续时间增加 → LED 逐渐变亮。
  3. 亮度下降阶段

    • pwm_buffer 中的值逐渐减小(如从 ARR→100→50→...→0)。
    • CCR 随数组值减小,CNT < CCR 的时间变短 → 有效电平持续时间减少 → LED 逐渐变暗。
  4. 循环往复:当 pwm_buffer 数组遍历完成后,重新从第一个值开始,形成 “暗→亮→暗” 的循环,即呼吸效果。

三、举例说明(假设 ARR=100)

  • 若当前 CCR=30(来自pwm_buffer):

    • CNT 从 0→29 时,CNT < 30 → 输出高电平(LED 亮,持续 30 个时钟周期)。
    • CNT 从 30→100 时,CNT >= 30 → 输出低电平(LED 暗,持续 70 个时钟周期)。
    • 占空比 = 30%,LED 较暗。
  • 若当前 CCR=80:

    • 亮的时间占 80%,暗的时间占 20% → LED 较亮。

四、总结

呼吸灯的核心是定时器硬件自动比较 “计数器(CNT)” 和 “捕获比较寄存器(CCR)”,通过生成动态变化的 CCR 值(pwm_buffer),改变了 “亮 / 暗时间的比例”,最终实现了平滑的亮度变化。硬件比较是实时且自动的,代码更新 CCR 值即可控制呼吸效果。

五、代码

这个函数是STM32CubeMX自动生成的,用于初始化TIM2的基本配置

/* TIM2 init function */
// 这个函数是STM32CubeMX自动生成的,用于初始化TIM2的基本配置
void MX_TIM2_Init(void)
{/* USER CODE BEGIN TIM2_Init 0 */// 用户可以在初始化开始处添加代码/* USER CODE END TIM2_Init 0 */// 自动生成的局部变量定义TIM_ClockConfigTypeDef sClockSourceConfig = {0};TIM_MasterConfigTypeDef sMasterConfig = {0};TIM_OC_InitTypeDef sConfigOC = {0};/* USER CODE BEGIN TIM2_Init 1 */// 用户可以在初始化中间添加代码/* USER CODE END TIM2_Init 1 */// 自动生成的定时器基本配置htim2.Instance = TIM2;htim2.Init.Prescaler = 71;            /* 72MHz/(71+1) = 1MHz计数频率 */htim2.Init.CounterMode = TIM_COUNTERMODE_UP;htim2.Init.Period = 999;              /* 1MHz/(999+1) = 1kHz PWM频率 */htim2.Init.ClockDivision = TIM_CLOCKDIVISION_DIV1;htim2.Init.AutoReloadPreload = TIM_AUTORELOAD_PRELOAD_ENABLE;HAL_TIM_Base_Init(&htim2);sClockSourceConfig.ClockSource = TIM_CLOCKSOURCE_INTERNAL;HAL_TIM_ConfigClockSource(&htim2, &sClockSourceConfig);HAL_TIM_PWM_Init(&htim2);sMasterConfig.MasterOutputTrigger = TIM_TRGO_RESET;sMasterConfig.MasterSlaveMode = TIM_MASTERSLAVEMODE_DISABLE;HAL_TIMEx_MasterConfigSynchronization(&htim2, &sMasterConfig);sConfigOC.OCMode = TIM_OCMODE_PWM1;sConfigOC.Pulse = 0;sConfigOC.OCPolarity = TIM_OCPOLARITY_HIGH;sConfigOC.OCFastMode = TIM_OCFAST_DISABLE;HAL_TIM_PWM_ConfigChannel(&htim2, &sConfigOC, TIM_CHANNEL_3);/* USER CODE BEGIN TIM2_Init 2 */// 用户可以在初始化结束处添加代码/* USER CODE END TIM2_Init 2 */HAL_TIM_MspPostInit(&htim2);}

初始化tim2的底层硬件,打开tim2通道三和dma通道二。

// 这个函数是STM32CubeMX自动生成的,用于初始化TIM2的底层硬件
void HAL_TIM_Base_MspInit(TIM_HandleTypeDef* tim_baseHandle)
{if(tim_baseHandle->Instance==TIM2){/* USER CODE BEGIN TIM2_MspInit 0 */// 用户可以在初始化开始处添加代码/* USER CODE END TIM2_MspInit 0 *//* TIM2 clock enable */// 自动生成的时钟使能代码__HAL_RCC_TIM2_CLK_ENABLE();/* TIM2 DMA Init */// 自动生成的DMA配置代码/* TIM2_UP Init: 使用更新事件触发DMA写CCR3 */hdma_tim2_ch3_up.Instance = DMA1_Channel2; /* F1映射:TIM2_UP -> DMA1_Channel2 */hdma_tim2_ch3_up.Init.Direction = DMA_MEMORY_TO_PERIPH;hdma_tim2_ch3_up.Init.PeriphInc = DMA_PINC_DISABLE;hdma_tim2_ch3_up.Init.MemInc = DMA_MINC_ENABLE;hdma_tim2_ch3_up.Init.PeriphDataAlignment = DMA_PDATAALIGN_HALFWORD;hdma_tim2_ch3_up.Init.MemDataAlignment = DMA_MDATAALIGN_HALFWORD;hdma_tim2_ch3_up.Init.Mode = DMA_CIRCULAR;hdma_tim2_ch3_up.Init.Priority = DMA_PRIORITY_LOW;HAL_DMA_Init(&hdma_tim2_ch3_up);/* 启动时钟 */__HAL_RCC_DMA1_CLK_ENABLE();// 改为更新事件DMA请求__HAL_LINKDMA(tim_baseHandle,hdma[TIM_DMA_ID_UPDATE],hdma_tim2_ch3_up);/* TIM2 interrupt Init */// 自动生成的中断配置代码HAL_NVIC_SetPriority(TIM2_IRQn, 0, 0);HAL_NVIC_EnableIRQ(TIM2_IRQn);/* USER CODE BEGIN TIM2_MspInit 1 */// 用户可以在初始化结束处添加代码/* USER CODE END TIM2_MspInit 1 */}
}

生成一个平滑的呼吸灯 PWM 占空比序列,通过将该序列按时间依次输出到 LED 的 PWM 通道,即可实现 LED 从暗→亮→暗的呼吸效果。

void MX_TIM2_PWM_Init(void)
{/* 动态生成平滑呼吸曲线:duty = sin^2(0..pi) * Period */float period = (float)htim2.Init.Period;float pi = 3.1415926f;for (int i = 0; i < PWM_BUFFER_SIZE; i++) {float phase = (pi * (float)i) / (float)(PWM_BUFFER_SIZE - 1);float s = sinf(phase);float duty = s * s * period;int val = (int)(duty + 0.5f);if (val < 0) val = 0;if (val > (int)period) val = (int)period;pwm_buffer[i] = (uint16_t)val;}
}

以下这段代码 Start_Breathing_LED(void) 是启动呼吸灯功能的核心函数,通过定时器 PWM + DMA的方式实现 LED 的自动呼吸效果,无需 CPU 频繁干预

void Start_Breathing_LED(void)
{HAL_TIM_PWM_Start(&htim2, TIM_CHANNEL_3);HAL_DMA_Start_IT(&hdma_tim2_ch3_up, (uint32_t)pwm_buffer, (uint32_t)&htim2.Instance->CCR3, PWM_BUFFER_SIZE);__HAL_TIM_ENABLE_DMA(&htim2, TIM_DMA_UPDATE);__HAL_TIM_ENABLE(&htim2);
}

以下是逐行代码的详细解释:

1. HAL_TIM_PWM_Start(&htim2, TIM_CHANNEL_3);

  • 功能:启动 TIM2 定时器的通道 3(CH3)的 PWM 输出功能。
  • 细节
    • &htim2 是 TIM2 的句柄(包含定时器的配置参数,如周期、预分频等)。
    • TIM_CHANNEL_3 指定启用通道 3,该通道需提前配置为 PWM 模式(如TIM_OCMODE_PWM1),且对应引脚已配置为复用输出(通常在HAL_TIM_MspPostInit中完成)。
    • 调用后,TIM2_CH3 开始输出 PWM 波形,但此时占空比由CCR3寄存器的初始值决定,尚未进入呼吸模式。

2. HAL_DMA_Start_IT(&hdma_tim2_ch3_up, (uint32_t)pwm_buffer, (uint32_t)&htim2.Instance->CCR3, PWM_BUFFER_SIZE);

  • 功能:启动 DMA 通道,并配置其从内存(pwm_buffer)向外设(CCR3寄存器)传输数据,同时使能 DMA 中断。
  • 参数解析
    • &hdma_tim2_ch3_up:TIM2_CH3 对应的 DMA 通道句柄(需提前在 CubeMX 中配置,如 STM32F103 中 TIM2_CH3 通常映射到 DMA1_Channel2)。
    • (uint32_t)pwm_buffer源地址,即存储呼吸灯占空比序列的数组(你之前生成的sin²曲线数据)。
    • (uint32_t)&htim2.Instance->CCR3目标地址,即 TIM2 通道 3 的捕获比较寄存器(CCR3),PWM 的占空比由该寄存器的值决定。
    • PWM_BUFFER_SIZE传输次数,即需要从pwm_buffer传输到CCR3的数据总量(数组长度)。
  • 作用:DMA 会自动将pwm_buffer中的值依次写入CCR3,无需 CPU 手动调用__HAL_TIM_SET_COMPARE,减少 CPU 负担。

3. __HAL_TIM_ENABLE_DMA(&htim2, TIM_DMA_UPDATE);

  • 功能:使能 TIM2 的 “更新事件(Update Event)” 触发 DMA 传输。
  • 细节
    • 定时器的 “更新事件” 指计数器(CNT)从最大值(ARR)溢出并重置为 0 的时刻(向上计数模式)。
    • TIM_DMA_UPDATE 表示将 DMA 触发源绑定到 TIM2 的更新事件。
    • 使能后,每次 TIM2 发生更新事件(即一个 PWM 周期结束时),会自动触发 DMA 传输下一个占空比数据到CCR3

4. __HAL_TIM_ENABLE(&htim2);

  • 功能:启动 TIM2 定时器,使其开始计数。
  • 细节
    • 定时器启动后,计数器(CNT)开始从 0 递增,达到 ARR 后触发更新事件,进而触发 DMA 传输下一个占空比。
    • 结合前面的配置,此时整个流程会自动运行:TIM2计数 → 溢出触发更新事件 → DMA传输下一个占空比到CCR3 → TIM2根据新的CCR3输出PWM → 循环直到数组传输完成

整体工作流程(呼吸灯实现逻辑)

  1. 启动 PWMHAL_TIM_PWM_Start 使 TIM2_CH3 开始输出 PWM,但初始占空比可能为 0。
  2. 配置 DMAHAL_DMA_Start_IT 让 DMA 准备好从pwm_bufferCCR3传输数据,并允许 DMA 中断(方便传输完成后处理,如循环传输)。
  3. 绑定触发源__HAL_TIM_ENABLE_DMA 设定 “TIM2 更新事件” 作为 DMA 的触发信号,确保每个 PWM 周期更新一次占空比。
  4. 启动定时器__HAL_TIM_ENABLE 让 TIM2 开始工作,触发后续的计数、更新事件和 DMA 传输。

最终效果:pwm_buffer中的占空比序列会被 DMA 自动、周期性地写入CCR3,TIM2 根据实时的CCR3值输出 PWM,使 LED 按sin²曲线平滑亮暗变化,实现呼吸效果。

关键优势

  • 无 CPU 干预:DMA 自动完成占空比更新,CPU 可空闲处理其他任务。
  • 实时性强:每个 PWM 周期精准更新一次占空比,呼吸效果平滑无卡顿。
  • 低功耗:减少 CPU 频繁中断或轮询,降低系统功耗。
http://www.dtcms.com/a/512657.html

相关文章:

  • 做营销网站要多少钱网站开发平台建设
  • html css js网页制作成品——HTML+CSS仙台有树电视剧网页设计(5页)附源码
  • 开发避坑指南(64):修复IllegalArgumentException:参数值类型与期望类型不匹配
  • 企业网站怎样做seo优化 应该如何做凡科建站官网怎么样
  • 【Java进阶】GC友好的编程方式
  • 甘肃肃第八建设集团网站福州市高速公路建设指挥部网站
  • 鸿蒙NEXT媒体开发全栈解析:从播放器到录屏的一站式解决方案
  • 郑州做网站排名dede网站首页
  • python 做网站很快吗广州自助网站推广建站
  • AD22 热风焊盘在哪设置
  • CMake进阶:生成器表达式
  • 从 Vite 到现代构建范式:一个关于“快”的技术哲学
  • 2025世界智能制造大会(南京)将带来那些新技术与新体验?
  • 杭州网站建设杭州上海哪个网站好用
  • 做网站的文案是指网站怎么做才能赚钱吗
  • 完善企业能力等级评价体系 构建高质量发展新标尺
  • Vue2 封装二维码弹窗组件
  • 哪里有做网站较好的公司龙华做网站怎么样
  • 在1688做公司网站前端开发语言的特点是
  • 少儿教育网站建设价格免费看电视剧的网站在线观看
  • (四)从零学 React Props:数据传递 + 实战案例 + 避坑指南
  • 上传自己做的网站吗关键词优化百家号
  • 连云港做网站公司校园网的规划与设计
  • DeepSeek-OCR:视觉压缩的革命性突破——当OCR遇上LLM的“降维打击“
  • 盐城网站开发市场做网站怎么去工信部缴费
  • ps做游戏网站伊宁网站建设优化
  • 【高等数学笔记-极限(7)】函数连续
  • !for_each_process 命令详解
  • 住房与城乡建设网站引流客户的最快方法是什么
  • 江西网站开发的公司wordpress插件看访问者数量