Files
LPT26x-HSF-4MB-Hilink_14.2.…/LPT262_PWM_user_guide.md

1056 lines
31 KiB
Markdown
Raw Normal View History

# WS63 PWM 技术使用文档
## 文档概述
本文档基于WS63平台的PWM驱动实现为不同经验的开发人员提供PWM脉宽调制功能的配置与控制指导。
**适用人群**
- 🌱 **新手开发者**只熟悉0-255亮度控制希望快速上手PWM
- 🛠️ **进阶开发者**需要深入了解PWM参数配置和高级功能
- 🔍 **系统调试者**:需要问题诊断和性能优化
**平台基础参数**
- MCU平台WS63 (HiSilicon)
- 系统时钟80 MHz
- 计数器位宽32-bitu32
- PWM版本CONFIG_PWM_USING_V151
- 支持特性多通道、PWM组、相位控制、运行时更新
## 文档导航
### 🌱 新手必读(建议阅读顺序)
1. **章节1 - 快速入门**: 5分钟了解PWM和快速上手
2. **章节5 - 参数详解**: 从0-255到PWM参数的转换和理解
3. **章节11 - 错误处理**: 常见问题解决
### 🛠️ 进阶内容(可选阅读)
- **章节2-4**: PWM系统架构、API和初始化流程
- **章节6-10**: 高级功能频率配置、相位控制、PWM组等
- **章节12**: 完整流程图和技术参考
---
## 1. 快速入门(新手必读)
### 1.1 PWM是什么
PWM就像一个非常快的开关通过控制"开"和"关"的时间比例来调节LED的亮度、电机的速度等。
**简单理解**
- 如果一直开着 = 100%亮度
- 如果一直关着 = 0%亮度
- 如果一半时间开、一半时间关 = 50%亮度
### 1.2 你之前的使用方式 vs 现在的PWM方式
**你之前的方式**
```c
// 简单直观,但是功能有限
set_led_brightness(128); // 0-255范围
```
**现在的PWM方式**
```c
// 功能强大,但是参数复杂
pwm_config_t cfg = {
.high_time = 40000, // 高电平时间(计数)
.low_time = 40000, // 低电平时间(计数)
.offset_time = 0, // 相位偏移
.repeat = true, // 是否重复
.cycles = 0 // 循环次数
};
```
**不用担心!**本文档提供了简化函数让你像以前一样使用0-255的数值。
### 1.3 5分钟快速上手
```c
// 第1步初始化PWM系统
uapi_pwm_init();
// 第2步使用简化函数设置亮度就像以前一样
set_pwm_brightness_simple(0, 128); // 通道050%亮度
// 第3步启动PWM输出
uapi_pwm_start(0);
// 完成LED就会以50%亮度点亮
```
**简化函数**(复制到你的代码里):
```c
// 这个函数让PWM像以前一样简单
errcode_t set_pwm_brightness_simple(uint8_t channel, uint8_t brightness_0_255)
{
uint32_t frequency = 1000; // 固定1000Hz适合LED调光
float duty_percent = (brightness_0_255 / 255.0f) * 100.0f;
uint32_t period_cnt = 80000000 / frequency; // 80000
uint32_t high_time = (uint32_t)(period_cnt * duty_percent / 100.0f);
uint32_t low_time = period_cnt - high_time;
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);
}
```
---
## 2. PWM系统架构进阶内容
### 2.1 驱动层次结构
```mermaid
graph TB
A[应用层] --> B[PWM驱动接口 pwm.h]
B --> C[HAL层 hal_pwm_v151.h]
C --> D[寄存器操作层 hal_pwm_v151_regs_op.h]
D --> E[硬件寄存器层]
A --> F[GPIO配置]
F --> G[PWM输出引脚]
A --> I[PWM组控制]
I --> J[多通道同步控制]
style A fill:#e1f5fe
style B fill:#f3e5f5
style C fill:#e8f5e8
style D fill:#fff3e0
```
### 2.2 PWM工作原理
```mermaid
graph LR
A[频率配置] --> B[周期计算]
B --> C[占空比设置]
C --> D[high_time/low_time计算]
D --> E[PWM波形输出]
F[相位控制] --> E
G[PWM组同步] --> E
style B fill:#e3f2fd
style D fill:#f1f8e9
style E fill:#fff3e0
```
---
## 3. PWM核心数据结构与API进阶内容
### 3.1 PWM配置结构体
```c
// 源码位置include/driver/pwm.h
typedef struct pwm_config {
uint32_t low_time; // 低电平持续的计数数
uint32_t high_time; // 高电平持续的计数数
uint32_t offset_time; // PWM相位偏移时间
uint16_t cycles; // PWM重复周期范围0~32767
bool repeat; // 连续输出标志
} pwm_config_t;
```
### 3.2 关键API函数
```c
// PWM系统初始化和控制
errcode_t uapi_pwm_init(void); // 初始化PWM系统
errcode_t uapi_pwm_open(uint8_t channel, const pwm_config_t *cfg); // 打开PWM通道
errcode_t uapi_pwm_start(uint8_t channel); // 启动单个PWM通道
errcode_t uapi_pwm_close(uint8_t channel); // 关闭PWM通道
uint32_t uapi_pwm_get_frequency(uint8_t channel); // 获取PWM工作频率
// PWM组控制
errcode_t uapi_pwm_set_group(uint8_t group, const uint8_t *channel_set, uint32_t channel_set_len);
errcode_t uapi_pwm_start_group(uint8_t group); // 同步启动PWM组
errcode_t uapi_pwm_stop_group(uint8_t group); // 同步停止PWM组
errcode_t uapi_pwm_clear_group(uint8_t group); // 清理PWM组
// 运行时占空比更新
errcode_t uapi_pwm_update_duty_ratio(uint8_t channel, uint32_t low_time, uint32_t high_time);
// 中断处理
errcode_t uapi_pwm_register_interrupt(uint8_t channel, pwm_callback_t callback);
errcode_t uapi_pwm_unregister_interrupt(uint8_t channel);
```
---
## 4. PWM初始化流程进阶内容
### 4.1 初始化时序图
```mermaid
sequenceDiagram
participant App as 应用层
participant PWM as PWM驱动
participant HAL as HAL层
participant GPIO as GPIO系统
App->>PWM: uapi_pwm_init()
PWM->>HAL: hal_pwm_v151初始化
HAL-->>PWM: 返回success
App->>PWM: uapi_pwm_get_frequency(channel)
PWM-->>App: 返回基础频率(80MHz)
App->>App: 计算周期计数值
App->>GPIO: 配置GPIO引脚为PWM功能
GPIO-->>App: GPIO配置完成
App->>PWM: uapi_pwm_open(channel, config)
PWM-->>App: PWM通道配置完成
App->>PWM: uapi_pwm_start(channel)
PWM-->>App: PWM开始输出
Note over App: PWM波形输出
```
### 4.2 初始化代码实现
```c
// PWM初始化示例
void pwm_init_example(uint8_t gpio_pin, uint32_t target_frequency)
{
pwm_config_t cfg = {0};
// 1. 初始化PWM驱动
errcode_t ret = uapi_pwm_init();
if (ret != ERRCODE_SUCC) {
printf("PWM初始化失败: %d\n", ret);
return;
}
// 2. 获取PWM基础频率 (通常为80MHz)
uint8_t channel = gpio_pin % 8; // GPIO转PWM通道
uint32_t base_frequency = uapi_pwm_get_frequency(channel);
// 3. 计算PWM周期计数值
uint32_t period_cnt = base_frequency / target_frequency;
// 4. 配置PWM参数初始50%占空比)
cfg.high_time = period_cnt / 2;
cfg.low_time = period_cnt - cfg.high_time;
cfg.offset_time = 0; // 无相位偏移
cfg.repeat = true; // 连续输出
cfg.cycles = 0; // 无限循环
// 5. 打开PWM通道
ret = uapi_pwm_open(channel, &cfg);
if (ret != ERRCODE_SUCC) {
printf("PWM通道打开失败: %d\n", ret);
return;
}
// 6. 启动PWM输出
ret = uapi_pwm_start(channel);
if (ret != ERRCODE_SUCC) {
printf("PWM启动失败: %d\n", ret);
return;
}
printf("PWM初始化成功: 频率=%dHz, 周期=%d计数\n",
target_frequency, period_cnt);
}
```
---
## 5. PWM参数详解小白入门篇
### 5.1 什么是PWM
PWM就像开关灯一样快速地开关来控制亮度。想象你手里有个开关如果一直开着灯就是100%亮如果一直关着灯就是0%亮。但如果你快速地开开关关,开的时间长一点,灯就亮一点;关的时间长一点,灯就暗一点。
**关键概念**
- **频率**:一秒钟开关多少次(就像你手开关灯的频率)
- **占空比**:一个开关周期中,"开"的时间占多少比例
- **周期**:完成一次"开-关"动作需要的时间
### 5.2 从0-255到实际参数的转换
你熟悉的0-255其实就是占空比的另一种表示方法
```c
// 你熟悉的方式0-255的数值
uint8_t brightness = 128; // 相当于50%亮度
// 转换成PWM需要的参数
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% (最亮)
```
### 5.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;
```
### 5.4 简单的参数设置函数(推荐使用)
```c
// 简化版本像以前一样用0-255设置亮度
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);
}
// 使用示例:
// set_pwm_brightness_simple(0, 0); // 通道00%亮度(关闭)
// set_pwm_brightness_simple(0, 128); // 通道050%亮度
// set_pwm_brightness_simple(0, 255); // 通道0100%亮度(最亮)
```
### 5.5 高级参数详解(可选阅读)
```c
如果你想深入了解PWM的各个参数这里详细解释每个参数的含义
**pwm_config_t结构体中的参数**
1. **high_time高电平时间**
- 就是"开关"中"开"的时间,用时钟计数表示
- 例如如果high_time = 40000系统时钟80MHz那么"开"的时间 = 40000 ÷ 80000000 = 0.5ms
2. **low_time低电平时间**
- 就是"开关"中"关"的时间,用时钟计数表示
- 例如如果low_time = 40000那么"关"的时间也是0.5ms
- high_time + low_time = 一个完整周期的时间
3. **offset_time相位偏移**
- 用来控制多个PWM通道之间的时间差
- 大多数情况下设为0即可
- 高级应用比如多色LED可以让红绿蓝三色错开时间启动
4. **cycles重复次数**
- 设为0表示无限重复常用
- 设为具体数字表示输出多少个周期后停止
5. **repeat连续输出标志**
- true持续输出PWM波形常用
- false只输出指定次数后停止
```c
// 标准的占空比设置函数
errcode_t set_pwm_duty_cycle(uint8_t channel, float duty_cycle_percent)
{
// 参数检查
if (duty_cycle_percent < 0.0f) duty_cycle_percent = 0.0f;
if (duty_cycle_percent > 100.0f) duty_cycle_percent = 100.0f;
// 使用固定的1000Hz频率
uint32_t period_cnt = 80000; // 80MHz ÷ 1000Hz = 80000
// 计算高低电平时间
uint32_t high_time = (uint32_t)(period_cnt * duty_cycle_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);
}
// 运行时更新占空比V151版本
errcode_t update_pwm_duty_runtime(uint8_t channel, float duty_cycle_percent)
{
// V151需要重新配置整个通道来更新占空比
return set_pwm_duty_cycle(channel, duty_cycle_percent);
}
```
```
### 5.6 完整的PWM使用示例推荐新手使用
```c
// 完整的PWM初始化和使用流程
void pwm_simple_example(void)
{
// 第1步初始化PWM系统
if (uapi_pwm_init() != ERRCODE_SUCC) {
printf("PWM初始化失败\n");
return;
}
// 第2步配置GPIO引脚为PWM功能这里假设已经配置好
// 第3步设置PWM通道0的亮度
uint8_t channel = 0; // 使用PWM通道0
// 设置不同亮度测试
printf("开始PWM亮度测试...\n");
// 0% 亮度(关闭)
set_pwm_brightness_simple(channel, 0);
uapi_pwm_start(channel);
printf("当前亮度: 0%% (关闭)\n");
// delay_ms(1000); // 延时1秒
// 25% 亮度
set_pwm_brightness_simple(channel, 64);
printf("当前亮度: 25%%\n");
// delay_ms(1000);
// 50% 亮度
set_pwm_brightness_simple(channel, 128);
printf("当前亮度: 50%%\n");
// delay_ms(1000);
// 75% 亮度
set_pwm_brightness_simple(channel, 192);
printf("当前亮度: 75%%\n");
// delay_ms(1000);
// 100% 亮度(最亮)
set_pwm_brightness_simple(channel, 255);
printf("当前亮度: 100%% (最亮)\n");
printf("PWM测试完成\n");
}
// 渐变效果示例
void pwm_fade_example(void)
{
uint8_t channel = 0;
// 从暗到亮的渐变
printf("渐亮效果...\n");
for (int brightness = 0; brightness <= 255; brightness += 5) {
set_pwm_brightness_simple(channel, brightness);
// delay_ms(50); // 每步延时50ms实现平滑渐变
}
// 从亮到暗的渐变
printf("渐暗效果...\n");
for (int brightness = 255; brightness >= 0; brightness -= 5) {
set_pwm_brightness_simple(channel, brightness);
// delay_ms(50);
}
printf("渐变效果完成!\n");
}
```
---
## 6. PWM频率与周期配置进阶内容
### 6.1 常用频率配置表
**基于80MHz系统时钟的配置参数**
| 目标频率 | 周期计数值 | 周期时间 | 最小占空比步进 | 应用场景 | 说明 |
|----------|------------|----------|----------------|----------|------|
| 100 Hz | 800,000 | 10.0 ms | 0.000125% | 低频控制 | 人眼可见闪烁 |
| 1 kHz | 80,000 | 1.0 ms | 0.00125% | LED调光 | **推荐频率** - 无闪烁,适合照明 |
| 10 kHz | 8,000 | 100 μs | 0.0125% | 音频PWM | 超出听觉范围 |
| 20 kHz | 4,000 | 50 μs | 0.025% | 电机控制 | 无噪音,适合电机驱动 |
| 100 kHz | 800 | 10 μs | 0.125% | 高频开关 | 开关电源应用 |
| 1 MHz | 80 | 1 μs | 1.25% | 快速响应 | 高速信号生成 |
### 6.2 频率配置示例
```c
// 不同应用场景的频率配置
typedef struct {
uint32_t frequency;
uint32_t period_cnt;
const char* application;
} pwm_freq_preset_t;
const pwm_freq_preset_t pwm_presets[] = {
{100, 800000, "低频控制信号(有闪烁)"},
{1000, 80000, "LED调光推荐使用"},
{20000, 4000, "电机控制(无噪音)"},
{100000, 800, "高频开关电源"},
{1000000, 80, "高速信号生成"}
};
// 快速配置函数
errcode_t pwm_quick_setup(uint8_t channel, uint8_t preset_index, float duty_percent)
{
if (preset_index >= sizeof(pwm_presets)/sizeof(pwm_presets[0])) {
return ERRCODE_INVALID_PARAM;
}
const pwm_freq_preset_t* preset = &pwm_presets[preset_index];
uint32_t high_time = (uint32_t)(preset->period_cnt * duty_percent / 100.0f);
uint32_t low_time = preset->period_cnt - high_time;
pwm_config_t cfg = {
.high_time = high_time,
.low_time = low_time,
.repeat = true
};
printf("配置PWM: %s\n", preset->application);
printf(" - 频率: %dHz\n", preset->frequency);
printf(" - 占空比: %.1f%%\n", duty_percent);
printf(" - 周期时间: %.2fms\n", 1000.0f / preset->frequency);
return uapi_pwm_open(channel, &cfg);
}
```
---
## 7. PWM波形与相位控制进阶内容
### 7.1 PWM波形分析
```
基本PWM波形 (以1kHz频率60%占空比为例):
周期 = 1ms (80000计数)
高电平时间 = 0.6ms (48000计数)
低电平时间 = 0.4ms (32000计数)
████████████████████████████████ ████████████████████████████████
____ ________ ____
|←─────── 48000 cnt ────────→| |←─────── 48000 cnt ────────→|
|←─────────────── 80000 cnt (1ms) ──────────→|←──────────── 80000 cnt ──────→|
```
### 7.2 相位偏移控制
```c
// 相位偏移示例两个PWM通道错相输出
void setup_phase_shifted_pwm(uint8_t ch1, uint8_t ch2, uint32_t frequency, float duty_percent)
{
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;
// 通道1正常输出
pwm_config_t cfg1 = {
.high_time = high_time,
.low_time = low_time,
.offset_time = 0, // 无偏移
.repeat = true
};
// 通道2相位偏移180度
pwm_config_t cfg2 = {
.high_time = high_time,
.low_time = low_time,
.offset_time = period_cnt / 2, // 半周期偏移
.repeat = true
};
uapi_pwm_open(ch1, &cfg1);
uapi_pwm_open(ch2, &cfg2);
uapi_pwm_start(ch1);
uapi_pwm_start(ch2);
}
```
### 7.3 互补输出波形
```
互补PWM输出 (避免直通,添加死区时间):
Channel A: ████████ ████████ ████████
Channel B: ████████ ████████ ████████
|dt| |dt| |dt| |dt| |dt| |dt|
死区 死区 死区 死区 死区 死区
死区时间计算:
dead_time_ns = 500; // 500ns死区
dead_time_cnt = (500 * 80000000) / 1000000000 = 40计数
```
---
## 8. PWM组同步控制进阶内容
### 8.1 PWM组管理
```c
// PWM组配置和同步控制
#define PWM_GROUP_0 0
#define MAX_CHANNELS_PER_GROUP 8
errcode_t setup_pwm_group(uint8_t group_id, uint8_t* channels, uint8_t channel_count)
{
errcode_t ret;
// 1. 清除现有组配置
ret = uapi_pwm_clear_group(group_id);
if (ret != ERRCODE_SUCC) return ret;
// 2. 设置新的PWM组
ret = uapi_pwm_set_group(group_id, channels, channel_count);
if (ret != ERRCODE_SUCC) return ret;
printf("PWM组%d配置完成包含%d个通道\n", group_id, channel_count);
return ERRCODE_SUCC;
}
// 同步启动PWM组
errcode_t start_pwm_group_sync(uint8_t group_id)
{
return uapi_pwm_start_group(group_id);
}
// 同步停止PWM组
errcode_t stop_pwm_group_sync(uint8_t group_id)
{
return uapi_pwm_stop_group(group_id);
}
```
### 8.2 组同步流程
```mermaid
sequenceDiagram
participant App as 应用层
participant Group as PWM组控制
participant CH0 as 通道0
participant CH1 as 通道1
participant CH2 as 通道2
App->>Group: uapi_pwm_set_group(0, [0,1,2])
Group->>CH0: 加入组0
Group->>CH1: 加入组0
Group->>CH2: 加入组0
App->>CH0: uapi_pwm_open(0, config0)
App->>CH1: uapi_pwm_open(1, config1)
App->>CH2: uapi_pwm_open(2, config2)
App->>Group: uapi_pwm_start_group(0)
Group->>CH0: 同步启动
Group->>CH1: 同步启动
Group->>CH2: 同步启动
Note over CH0,CH2: 所有通道同时开始PWM输出
```
---
## 9. 动态PWM调整进阶内容
### 9.1 运行时参数更新
```c
// 动态调整PWM参数
errcode_t dynamic_pwm_update(uint8_t channel, uint32_t new_frequency, float new_duty_percent)
{
// 计算新参数
uint32_t period_cnt = 80000000 / new_frequency;
uint32_t high_time = (uint32_t)(period_cnt * new_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,
.repeat = true
};
errcode_t ret = uapi_pwm_open(channel, &cfg);
if (ret != ERRCODE_SUCC) {
return ret;
}
return uapi_pwm_start(channel);
}
// 渐变调节函数
typedef struct {
uint8_t channel;
float current_duty;
float target_duty;
float step_size;
uint32_t interval_ms;
} pwm_fade_t;
void pwm_fade_step(pwm_fade_t* fade)
{
if (abs(fade->current_duty - fade->target_duty) < fade->step_size) {
fade->current_duty = fade->target_duty;
return; // 渐变完成
}
if (fade->current_duty < fade->target_duty) {
fade->current_duty += fade->step_size;
} else {
fade->current_duty -= fade->step_size;
}
set_pwm_duty_cycle(fade->channel, fade->current_duty);
}
```
### 9.2 PWM调制模式
```c
// PWM调制模式示例
typedef enum {
PWM_MODE_CONSTANT, // 恒定输出
PWM_MODE_BREATHING, // 呼吸模式
PWM_MODE_BLINKING, // 闪烁模式
PWM_MODE_SAWTOOTH, // 锯齿波模式
} pwm_modulation_mode_t;
typedef struct {
uint8_t channel;
pwm_modulation_mode_t mode;
uint32_t period_ms; // 调制周期
float min_duty; // 最小占空比
float max_duty; // 最大占空比
uint32_t step_count; // 步数
} pwm_modulation_t;
void pwm_modulation_update(pwm_modulation_t* mod, uint32_t current_step)
{
float duty = mod->min_duty;
switch (mod->mode) {
case PWM_MODE_BREATHING:
// 正弦波呼吸
duty = mod->min_duty + (mod->max_duty - mod->min_duty) *
(1.0f + sinf(2 * 3.14159f * current_step / mod->step_count)) / 2.0f;
break;
case PWM_MODE_SAWTOOTH:
// 锯齿波
duty = mod->min_duty + (mod->max_duty - mod->min_duty) *
current_step / mod->step_count;
break;
case PWM_MODE_BLINKING:
// 方波闪烁
duty = (current_step < mod->step_count/2) ? mod->max_duty : mod->min_duty;
break;
default:
duty = mod->max_duty;
break;
}
set_pwm_duty_cycle(mod->channel, duty);
}
```
---
## 10. PWM应用示例进阶内容
### 10.1 电机PWM控制
```c
// 电机PWM控制示例
void motor_pwm_control(uint8_t motor_channel, float speed_percent, bool direction)
{
// 电机PWM频率通常选择20kHz以避免听到噪音
uint32_t motor_frequency = 20000;
// 限制速度范围
if (speed_percent < 0) speed_percent = 0;
if (speed_percent > 100) speed_percent = 100;
// 计算周期和占空比
uint32_t period_cnt = 80000000 / motor_frequency;
uint32_t high_time = (uint32_t)(period_cnt * speed_percent / 100.0f);
uint32_t low_time = period_cnt - high_time;
pwm_config_t cfg = {
.high_time = high_time,
.low_time = low_time,
.repeat = true
};
uapi_pwm_open(motor_channel, &cfg);
uapi_pwm_start(motor_channel);
// 方向控制需要额外的GPIO控制
// gpio_set_direction_pin(direction);
printf("电机PWM: 速度=%.1f%%, 方向=%s\n",
speed_percent, direction ? "正转" : "反转");
}
```
### 10.2 伺服电机控制
```c
// 伺服电机PWM控制 (50Hz, 1-2ms脉宽)
void servo_pwm_control(uint8_t servo_channel, float angle_degrees)
{
// 伺服PWM频率固定为50Hz (20ms周期)
uint32_t servo_frequency = 50;
uint32_t period_cnt = 80000000 / servo_frequency; // 1,600,000计数 (20ms)
// 角度范围限制 (-90° 到 +90°)
if (angle_degrees < -90) angle_degrees = -90;
if (angle_degrees > 90) angle_degrees = 90;
// 脉宽计算1ms(-90°) 到 2ms(+90°)
float pulse_width_ms = 1.5f + (angle_degrees / 90.0f) * 0.5f;
uint32_t high_time = (uint32_t)(80000 * pulse_width_ms); // 80000计数/ms
uint32_t low_time = period_cnt - high_time;
pwm_config_t cfg = {
.high_time = high_time,
.low_time = low_time,
.repeat = true
};
uapi_pwm_open(servo_channel, &cfg);
uapi_pwm_start(servo_channel);
printf("伺服控制: 角度=%.1f°, 脉宽=%.2fms\n", angle_degrees, pulse_width_ms);
}
```
### 10.3 音频PWM输出
```c
// 简单音频PWM输出
void audio_pwm_tone(uint8_t audio_channel, uint32_t tone_frequency, uint32_t duration_ms)
{
// 音频PWM载波频率选择高于20kHz
uint32_t carrier_frequency = 44100; // 44.1kHz载波
uint32_t period_cnt = 80000000 / carrier_frequency;
// 音频调制简单50%占空比)
uint32_t high_time = period_cnt / 2;
uint32_t low_time = period_cnt - high_time;
pwm_config_t cfg = {
.high_time = high_time,
.low_time = low_time,
.repeat = true
};
uapi_pwm_open(audio_channel, &cfg);
uapi_pwm_start(audio_channel);
// 持续时间控制
// delay_ms(duration_ms);
// uapi_pwm_stop(audio_channel);
printf("音频输出: 载波=%dHz, 持续=%dms\n", carrier_frequency, duration_ms);
}
```
---
## 11. 错误处理与调试
### 11.1 常见问题诊断
| 问题现象 | 可能原因 | 解决方案 | 验证方法 |
|----------|----------|----------|----------|
| PWM无输出 | 通道未启动 | 调用uapi_pwm_start() | 示波器检查引脚 |
| 频率不正确 | 计算错误 | 检查period_cnt计算 | 频率计测量 |
| 占空比异常 | high_time设置错误 | 检查占空比计算公式 | 占空比测量 |
| 多通道不同步 | 未使用PWM组 | 使用uapi_pwm_start_group() | 示波器多通道观察 |
| PWM抖动 | 中断影响 | 关闭不必要中断 | 波形稳定性分析 |
### 11.2 调试代码示例
```c
// PWM状态监控和调试
void pwm_debug_info(uint8_t channel)
{
uint32_t frequency = uapi_pwm_get_frequency(channel);
printf("=== PWM通道%d调试信息 ===\n", channel);
printf("基础频率: %d Hz\n", frequency);
printf("系统时钟: 80MHz\n");
// 假设当前配置
uint32_t target_freq = 1000;
uint32_t period_cnt = 80000000 / target_freq;
float duty_cycle = 60.0f;
uint32_t high_time = (uint32_t)(period_cnt * duty_cycle / 100.0f);
uint32_t low_time = period_cnt - high_time;
printf("目标频率: %d Hz\n", target_freq);
printf("周期计数: %d\n", period_cnt);
printf("高电平计数: %d (%.2fms)\n", high_time, (float)high_time/80000);
printf("低电平计数: %d (%.2fms)\n", low_time, (float)low_time/80000);
printf("占空比: %.1f%%\n", duty_cycle);
printf("实际周期: %.2fms\n", (float)period_cnt/80000);
}
// PWM波形验证
bool verify_pwm_output(uint8_t channel, uint32_t expected_freq, float expected_duty)
{
// 这里需要硬件支持或外部测量设备
// 返回验证结果
printf("验证PWM通道%d: 期望频率=%dHz, 期望占空比=%.1f%%\n",
channel, expected_freq, expected_duty);
return true; // 简化示例
}
```
### 11.3 性能优化建议
```c
// PWM性能优化建议
void pwm_performance_tips(void)
{
// 1. 使用PWM组进行批量操作
uint8_t channels[] = {0, 1, 2, 3};
uapi_pwm_set_group(0, channels, 4);
uapi_pwm_start_group(0); // 比逐个启动效率高
// 2. 避免频繁的参数更改
// 缓存计算结果,批量更新
// 3. 合理选择PWM频率
// 过高频率会增加功耗,过低可能产生可见闪烁
// 4. 使用合适的占空比精度
// 不需要过高精度时使用整数运算
// 5. 中断优化
// 只在必要时注册PWM中断回调
}
```
---
## 12. 完整PWM控制流程
### 12.1 PWM完整使用流程
```mermaid
flowchart TD
A[系统启动] --> B[uapi_pwm_init 初始化]
B --> C[GPIO配置为PWM功能]
C --> D[计算PWM参数]
D --> E{单通道?}
E -->|是| F[uapi_pwm_open 配置通道]
E -->|否| G[uapi_pwm_set_group 配置组]
F --> H[uapi_pwm_start 启动单通道]
G --> I[批量配置各通道]
I --> J[uapi_pwm_start_group 同步启动]
H --> K[PWM波形输出]
J --> K
K --> L{需要调整?}
L -->|是| M[更新参数]
L -->|否| N[持续运行]
M --> O[uapi_pwm_open 重新配置]
O --> K
N --> P{停止?}
P -->|否| L
P -->|是| Q[uapi_pwm_close 关闭]
Q --> R[结束]
style B fill:#e3f2fd
style D fill:#f1f8e9
style K fill:#fff3e0
style O fill:#e8f5e8
```
---
## 附录:技术参考
### A.1 WS63 PWM技术规格
| 规格项 | 参数值 | 说明 |
|--------|--------|------|
| 系统时钟 | 80 MHz | PWM计数基准 |
| 计数器位宽 | 32-bit | 支持超长周期 |
| PWM通道数 | 8 | 每组最多8通道 |
| PWM组数 | 多组 | 支持同步控制 |
| 最高频率 | 40 MHz | 理论最大值 |
| 最低频率 | 1.2 Hz | 32位计数器极限 |
| 占空比精度 | 0.0000012% | 32位分辨率 |
| 相位控制 | 支持 | offset_time参数 |
### A.2 应用场景频率推荐
| 应用场景 | 推荐频率 | 原因 |
|----------|----------|------|
| LED调光 | 1-20 kHz | 避免频闪,超出人眼感知 |
| 电机控制 | 10-50 kHz | 超出听觉范围,降低噪音 |
| 伺服控制 | 50 Hz | 伺服电机标准频率 |
| 开关电源 | 100-500 kHz | 减小磁性元件尺寸 |
| 音频输出 | 20-100 kHz | 超出人类听觉范围 |
| 数字通信 | 1-10 MHz | 高速数据传输 |
---
**文档版本**V3.0
**更新日期**2025年
**适用平台**WS63 (HiSilicon)
**PWM版本**CONFIG_PWM_USING_V151
**系统时钟**80MHz
本文档提供通用PWM技术指导适用于各种基于WS63平台的PWM应用开发。