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

1056 lines
31 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.
# 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 = 4000080MHz"" = 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**
- truePWM波形
- 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应用开发。