[活动] 【G32R501 Micro-EVB测评】 2、正弦呼吸灯效果

[复制链接]
57|9
xu@xupt 发表于 2026-1-30 22:18 | 显示全部楼层 |阅读模式

1、 正弦呼吸灯实现方法

本次正弦呼吸灯采用软件模拟PWM(脉冲宽度调制)方案实现,无需配置硬件定时器和引脚复用,代码更简洁、上手更快速,核心是通过正弦函数动态控制软件PWM的占空比,让LED亮度按照正弦曲线平滑变化,最终实现自然柔和的呼吸效果。
软件PWM的亮度控制逻辑。PWM的核心是“固定周期内,高、低电平的时间比例(占空比)决定LED亮度”,占空比越高(LED点亮时间越长),亮度越亮;反之则越暗。 与硬件PWM不同,软件PWM无需依赖外设定时器,而是通过

`DELAY_US()`函数精准控制LED的“点亮时长”和“熄灭时长”,两者之和为一个固定的PWM周期(本次设置为400μs,对应2.5kHz频率),该频率远超于人眼的视觉暂留阈值,不会产生明显频闪。另外,需注意本硬件的LED为低电平点亮、高电平熄灭(对应代码中`GPIO_WritePin(DEVICE_GPIO_PIN_LED1, 0)`点亮LED)。

正弦函数的呼吸循环映射。 正弦函数

`sin(θ)`在`0~π`(弧度)区间内,输出值从0.0平滑递增至1.0,再平滑递减至0.0,这个变化过程与呼吸灯“暗→亮→暗”的循环完全匹配:- θ=0:sin(0)=0 → 占空比0% → LED完全熄灭(最暗);- θ=π/2:sin(π/2)=1 → 占空比100% → LED完全点亮(最亮); - θ=π:sin(π)=0 → 占空比0% → LED完全熄灭(回到最暗)。 通过实时更新角度`θ`并计算对应的正弦值,即可得到平滑变化的占空比,进而实现无“台阶感”的正弦呼吸效果。

2、 程序

//
// Included Files
//
#include "g32r501_project.h"
#include <math.h> // 引入数学库以使用sin函数

//
// Defines
//
#define DEVICE_GPIO_PIN_LED1    23
#define PI                      3.1415926f

// --- 呼吸灯核心参数调整 ---
// PWM周期:400us (对应2.5kHz频率),人眼无法察觉频闪
#define PWM_PERIOD_US           400   
// 呼吸速度步进:数值越小越慢。
// 计算:3.14 / 0.0005 = 6280次循环; 6280 * 400us ≈ 2.5秒
#define BREATH_SPEED            0.0005f 

//
// Main
//
void example_main(void)
{
    // 变量定义
    float angle = 0.0f;     // 当前角度 (弧度)
    float duty_cycle = 0.0f;// 当前占空比 (0.0 - 1.0)
    uint32_t time_on = 0;   // 亮灯时长 (us)
    uint32_t time_off = 0;  // 灭灯时长 (us)

    //
    // Initialize device clock and peripherals
    //
    InitSysCtrl();

    //
    // Initialize GPIO and configure the GPIO pin as a push-pull output
    //
    InitGpio();
    GPIO_SetupPinMux(DEVICE_GPIO_PIN_LED1, GPIO_MUX_CPU0, 0);
    GPIO_SetupPinOptions(DEVICE_GPIO_PIN_LED1, GPIO_OUTPUT, GPIO_PUSHPULL);

    //
    // Initialize NVIC and clears NVIC registers. Disables CPU interrupts. 
    //
    DINT;
    Interrupt_initModule();
    Interrupt_initVectorTable();
    EINT;
    ERTM;

    //
    // Loop Forever
    //
    for(;;)
    {
        // 1. 计算正弦占空比
        // sin(angle) 结果为 0.0 到 1.0
        duty_cycle = sinf(angle);

        // 保护范围
        if(duty_cycle < 0.0f) duty_cycle = 0.0f;

        // 2. 根据占空比计算 亮/灭 持续时间
        time_on  = (uint32_t)(duty_cycle * PWM_PERIOD_US);
        time_off = PWM_PERIOD_US - time_on;

        // 3. 执行软件PWM周期 (低电平点亮,高电平熄灭)
      
        // --- 亮灯阶段 ---
        if(time_on > 0)
        {
            GPIO_WritePin(DEVICE_GPIO_PIN_LED1, 0); // 点亮
            DELAY_US(time_on);
        }

        // --- 灭灯阶段 ---
        if(time_off > 0)
        {
            GPIO_WritePin(DEVICE_GPIO_PIN_LED1, 1); // 熄灭
            DELAY_US(time_off);
        }

        // 4. 更新角度
        angle += BREATH_SPEED;
      
        // 5. 周期结束处理
        if(angle > PI)
        {
            angle = 0.0f; // 重置角度
          
            // 强制熄灭LED,防止浮点误差导致微亮
            GPIO_WritePin(DEVICE_GPIO_PIN_LED1, 1); 
          
            // 添加“屏息”时间:呼吸结束后的自然停顿 (500ms)
            // 这样效果更有节奏感,不像机器那样一刻不停
            DELAY_US(500000); 
        }
    }
}

3、效果


image.png

elsaflower 发表于 2026-2-3 19:51 | 显示全部楼层
相比于普通的“渐亮渐灭”(线性变化),正弦呼吸灯的亮度变化遵循正弦曲线,开启和关闭的过程更加柔和、自然,模拟生物真实的呼吸节奏。
belindagraham 发表于 2026-2-4 19:40 | 显示全部楼层
正弦呼吸灯 = PWM + 正弦表 + 定时更新
1988020566 发表于 2026-2-5 09:06 | 显示全部楼层
核心是利用正弦函数的平滑特性让 LED 亮度从暗到亮、再从亮到暗循环变化
空灵回声 发表于 2026-2-5 09:56 | 显示全部楼层
学习了。
有机会我也试试,就是不知道我这眼睛能不能看出来差别
嘿嘿
kmzuaz 发表于 2026-2-5 11:48 | 显示全部楼层
正弦呼吸灯 = PWM 占空比 + 正弦表映射 + 循环更新
adolphcocker 发表于 2026-2-6 11:11 | 显示全部楼层
电源稳定,避免电压波动影响LED亮度。
albertaabbot 发表于 2026-2-6 13:16 | 显示全部楼层
预计算正弦表并存储于Flash,避免实时浮点运算
mmbs 发表于 2026-2-6 15:10 | 显示全部楼层
在单片机中实时运行 sin() 浮点函数会消耗大量 单片机 资源。为了效率,我们通常预先计算好一个正弦查找表,存储在数组中。
rosemoore 发表于 2026-2-6 17:49 | 显示全部楼层
比普通线性呼吸灯更自然,关键在于 “精准的 PWM 占空比控制 + 正弦值查表 / 计算”
您需要登录后才可以回帖 登录 | 注册

本版积分规则

143

主题

785

帖子

3

粉丝
快速回复 在线客服 返回列表 返回顶部
0