Files
LPT26x-HSF-4MB-Hilink_14.2.…/PWM参数计算详解.md
ekko.bao 21c6d05bad 1. 解决PWM 更新导致周期混乱继而亮度抖动的问题
2. 增加无论何种状态都能强制复位的机制
3. 添加设备注册检查机制,如果设备已经处于配网但是还是注册的状态,强制进行一次复位
4. 添加串口控制协议
2025-10-26 17:49:12 +08:00

11 KiB
Raw Blame History

PWM参数计算详解通俗易懂版

文档说明

本文档专门解释PWM的各个参数计算原理用通俗的语言和图表帮助理解复杂的PWM概念。适合只熟悉0-255亮度控制的开发者快速理解PWM参数。


1. 什么是PWM

PWM就像开关灯一样快速地开关来控制亮度。想象你手里有个开关如果一直开着灯就是100%亮如果一直关着灯就是0%亮。但如果你快速地开开关关,开的时间长一点,灯就亮一点;关的时间长一点,灯就暗一点。

PWM波形图解

100%亮度 (一直开着):
████████████████████████████████████████████████████

50%亮度 (一半时间开,一半时间关):
████████        ████████        ████████        ████

25%亮度 (1/4时间开3/4时间关):
████    ████    ████    ████    ████    ████    ████

0%亮度 (一直关着):
________________________________________________

关键概念图解

graph LR
    A[频率<br/>每秒开关多少次] --> B[周期<br/>一次开关的总时间]
    B --> C[占空比<br/>开的时间占比例]
    C --> D[亮度效果<br/>人眼感知的亮度]
    
    style A fill:#e3f2fd
    style B fill:#f1f8e9
    style C fill:#fff3e0
    style D fill:#fce4ec

2. 从0-255到实际参数的转换

2.1 对应关系表

你熟悉的数值 百分比 PWM术语 实际效果
0 0% 0%占空比 完全不亮
64 25% 25%占空比 1/4亮度
128 50% 50%占空比 一半亮度
192 75% 75%占空比 3/4亮度
255 100% 100%占空比 最亮

2.2 转换公式

// 从你熟悉的0-255转换到百分比
uint8_t brightness = 128;  // 你设置的亮度值
float duty_percent = (brightness / 255.0f) * 100.0f;  // 转换成百分比

// 例子:
// brightness = 0   → duty_percent = 0%    (完全不亮)
// brightness = 64  → duty_percent = 25%   (1/4亮度)
// brightness = 128 → duty_percent = 50%   (一半亮度)
// brightness = 192 → duty_percent = 75%   (3/4亮度)
// brightness = 255 → duty_percent = 100%  (最亮)

3. PWM参数计算原理

3.1 基础概念图解

系统就像一个超快的时钟(80MHZ),每秒"滴答(cycle)"8000万次每个滴答节奏所对应的时间是固定的。
也就是这个PWM的最高精度就是(1/80*1000*1000)s = 

如果我们要让PWM 1000HZ也就是每秒开关1000次
那么每次开关需要80M ÷ 1000 = 80000 cycle所对应的时间
所以我们控制PWM本质上是控制这个PWM一个周期的cycle总个数以及比例分配。

比如我要 PWM 是1KHZ
首先计算1KHZ 一个PWM周期就是1/1000s 也就是1ms
然后计算1ms 对应多少个cycle: 0.001 / (1/80*1000*1000) = 80,000
然后再根据占空比去分配这 80,000 个数字就行了
只要确保high_time low_time加起来是8000就行

3.2 时间轴图解

一个PWM周期 (1000Hz = 1ms):

0        20000      40000      60000      80000
|---------|---------|---------|---------|
         25%        50%        75%       100%

如果要50%亮度:
████████████████████████████████████████        
|←----- 开40000-----→|←---- 关40000 ----|

如果要75%亮度:
████████████████████████████████████████████████████████        
|←-------- 开 (60000滴答) ---------→|←- 关 (20000滴答) -|

3.3 计算步骤详解

// 第1步确定系统时钟这是硬件固定的不用改
uint32_t system_clock = 80000000;  // 80MHz = 每秒8000万次计数

// 第2步设定PWM频率就是每秒开关多少次
uint32_t pwm_frequency = 1000;     // 1000Hz = 每秒开关1000次

// 第3步计算一个开关周期需要多少个时钟计数
// 打个比方如果系统每秒有8000万下你要让它每秒开关1000次
// 那么每次开关就需要8000万 ÷ 1000 = 80000个计数
uint32_t period_count = system_clock / pwm_frequency;

// 第4步根据亮度(占空比)计算开和关的时间
// 如果要60%亮度那么80000个计数中48000个用来"开"32000个用来"关"
float brightness_percent = 60.0;  // 60%亮度
uint32_t on_time = (uint32_t)(period_count * brightness_percent / 100.0);
uint32_t off_time = period_count - on_time;

// 结果:
// on_time = 48000   (开的时间)
// off_time = 32000  (关的时间)

4. PWM参数详解图表

4.1 pwm_config_t结构体参数图解

typedef struct pwm_config {
    uint32_t high_time;      // 高电平时间("开"的时间,用计数表示)
    uint32_t low_time;       // 低电平时间("关"的时间,用计数表示)
    uint32_t offset_time;    // 相位偏移时间(延迟启动时间)
    uint16_t cycles;         // 重复次数0=无限重复)
    bool repeat;             // 连续输出标志true=一直输出)
} pwm_config_t;

4.2 参数关系图

graph TB
    A[一个PWM周期] --> B[high_time<br/>开的时间]
    A --> C[low_time<br/>关的时间]
    
    B --> D[占空比 = high_time / high_time + low_time]
    C --> D
    
    D --> E[最终亮度效果]
    
    F[offset_time<br/>延迟启动] --> G[多通道同步控制]
    H[cycles<br/>重复次数] --> I[输出控制]
    J[repeat<br/>连续标志] --> I
    
    style A fill:#e3f2fd
    style D fill:#f1f8e9
    style E fill:#fff3e0

4.3 各参数的通俗解释

high_time高电平时间

  • 通俗理解:就是"开关"中"开"的时间,用时钟计数表示
  • 计算方法high_time = 周期计数 × 亮度百分比
  • 例子如果high_time = 40000系统时钟80MHz那么"开"的时间 = 40000 ÷ 80000000 = 0.5ms

low_time低电平时间

  • 通俗理解:就是"开关"中"关"的时间,用时钟计数表示
  • 计算方法low_time = 周期计数 - high_time
  • 例子如果low_time = 40000那么"关"的时间也是0.5ms

offset_time相位偏移

  • 通俗理解延迟多长时间再开始PWM输出
  • 使用场景多个LED需要错开时间启动避免同时启动造成电流冲击
  • 大多数情况设为0即可

cycles重复次数

  • 通俗理解PWM波形重复多少次后停止
  • 常用设置
    • 0:无限重复(最常用)
    • 具体数字:输出指定次数后停止

repeat连续输出标志

  • 通俗理解是否持续输出PWM波形
  • 常用设置
    • true:持续输出(最常用)
    • false:只输出指定次数后停止

5. 实际计算示例

5.1 示例1设置50%亮度

// 目标PWM频率1000Hz50%亮度

// 第1步计算周期计数
uint32_t period_cnt = 80000000 / 1000;  // = 80000

// 第2步计算高低电平时间
uint32_t high_time = 80000 * 50 / 100;  // = 40000
uint32_t low_time = 80000 - 40000;      // = 40000

// 第3步配置PWM
pwm_config_t cfg = {
    .high_time = 40000,     // "开" 0.5ms
    .low_time = 40000,      // "关" 0.5ms
    .offset_time = 0,       // 不延迟
    .repeat = true,         // 持续输出
    .cycles = 0            // 无限重复
};

5.2 示例2设置75%亮度

// 目标PWM频率1000Hz75%亮度

uint32_t period_cnt = 80000;
uint32_t high_time = 80000 * 75 / 100;  // = 60000
uint32_t low_time = 80000 - 60000;      // = 20000

pwm_config_t cfg = {
    .high_time = 60000,     // "开" 0.75ms (更长)
    .low_time = 20000,      // "关" 0.25ms (更短)
    .offset_time = 0,
    .repeat = true,
    .cycles = 0
};

5.3 波形对比图

50%占空比 (high_time=40000, low_time=40000):
████████████████████████████████████████        ████████████████████████████████████████
|←-------- 0.5ms ------→|←-- 0.5ms --→|      |←-------- 0.5ms ------→|←-- 0.5ms --→|

75%占空比 (high_time=60000, low_time=20000):
████████████████████████████████████████████████████████        ████████████████████████████████████████████████████████
|←-------------- 0.75ms ------------→|0.25ms|      |←-------------- 0.75ms ------------→|0.25ms|

25%占空比 (high_time=20000, low_time=60000):
████████████████████        ████████████████████
|←-- 0.25ms --→|←---- 0.75ms ----→|      |←-- 0.25ms --→|←---- 0.75ms ----→|

7. 简化使用函数

7.1 推荐的简化函数

// 这个函数让PWM像以前一样简单
errcode_t set_pwm_brightness_simple(uint8_t channel, uint8_t brightness_0_255)
{
    // 固定使用1000Hz频率适合LED调光
    uint32_t frequency = 1000;
    
    // 将0-255转换为百分比
    float duty_percent = (brightness_0_255 / 255.0f) * 100.0f;
    
    // 计算周期计数80MHz ÷ 1000Hz = 80000
    uint32_t period_cnt = 80000000 / frequency;
    
    // 计算高低电平时间
    uint32_t high_time = (uint32_t)(period_cnt * duty_percent / 100.0f);
    uint32_t low_time = period_cnt - high_time;
    
    // 配置PWM
    pwm_config_t cfg = {
        .high_time = high_time,     // "开"的时间(计数值)
        .low_time = low_time,       // "关"的时间(计数值)
        .offset_time = 0,           // 不需要相位偏移
        .repeat = true,             // 持续输出
        .cycles = 0                 // 无限循环
    };
    
    return uapi_pwm_open(channel, &cfg);
}

8. 常见问题解答

Q1: 为什么不能直接用0-255要用这些复杂参数

A: PWM硬件需要知道具体的时钟计数不是百分比。就像你告诉司机"开快点",司机需要知道具体开多少码一样。

Q2: high_time和low_time的单位是什么

A: 单位是时钟计数次数不是时间。1个计数 = 1/80000000秒 = 12.5纳秒。

Q3: 为什么推荐1000Hz频率

A:

  • 高于100Hz人眼看不到闪烁
  • 低于20kHz不会产生高频噪音
  • 1000Hz刚好在这个范围内且计算简单

Q4: offset_time什么时候用

A: 主要用于多通道同步比如RGB灯的三个颜色错开启动避免电流冲击。

Q5: 如何实现渐变效果?

A: 在循环中逐步改变brightness_0_255的值每次改变后调用set_pwm_brightness_simple()。