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

321 lines
11 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# PWM参数计算详解通俗易懂版
## 文档说明
本文档专门解释PWM的各个参数计算原理用通俗的语言和图表帮助理解复杂的PWM概念。适合只熟悉0-255亮度控制的开发者快速理解PWM参数。
---
## 1. 什么是PWM
PWM就像开关灯一样快速地开关来控制亮度。想象你手里有个开关如果一直开着灯就是100%亮如果一直关着灯就是0%亮。但如果你快速地开开关关,开的时间长一点,灯就亮一点;关的时间长一点,灯就暗一点。
### PWM波形图解
```
100%亮度 (一直开着):
████████████████████████████████████████████████████
50%亮度 (一半时间开,一半时间关):
████████ ████████ ████████ ████
25%亮度 (1/4时间开3/4时间关):
████ ████ ████ ████ ████ ████ ████
0%亮度 (一直关着):
________________________________________________
```
### 关键概念图解
```mermaid
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 转换公式
```c
// 从你熟悉的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 计算步骤详解
```c
// 第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结构体参数图解
```c
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 参数关系图
```mermaid
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%亮度
```c
// 目标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%亮度
```c
// 目标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 推荐的简化函数
```c
// 这个函数让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()。
---