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

31 KiB
Raw Permalink Blame 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方式

你之前的方式

// 简单直观,但是功能有限
set_led_brightness(128);  // 0-255范围

现在的PWM方式

// 功能强大,但是参数复杂
pwm_config_t cfg = {
    .high_time = 40000,    // 高电平时间(计数)
    .low_time = 40000,     // 低电平时间(计数)
    .offset_time = 0,      // 相位偏移
    .repeat = true,        // 是否重复
    .cycles = 0           // 循环次数
};

**不用担心!**本文档提供了简化函数让你像以前一样使用0-255的数值。

1.3 5分钟快速上手

// 第1步初始化PWM系统
uapi_pwm_init();

// 第2步使用简化函数设置亮度就像以前一样
set_pwm_brightness_simple(0, 128);  // 通道050%亮度

// 第3步启动PWM输出
uapi_pwm_start(0);

// 完成LED就会以50%亮度点亮

简化函数(复制到你的代码里):

// 这个函数让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 驱动层次结构

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工作原理

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配置结构体

// 源码位置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函数

// 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 初始化时序图

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 初始化代码实现

// 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其实就是占空比的另一种表示方法

// 你熟悉的方式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 参数计算原理(用通俗的话解释)

// 第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 简单的参数设置函数(推荐使用)

// 简化版本像以前一样用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 高级参数详解(可选阅读)

如果你想深入了解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 频率配置示例

// 不同应用场景的频率配置
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 相位偏移控制

// 相位偏移示例两个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组管理

// 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 组同步流程

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 运行时参数更新

// 动态调整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调制模式

// 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控制

// 电机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 伺服电机控制

// 伺服电机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输出

// 简单音频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 调试代码示例

// 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 性能优化建议

// 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完整使用流程

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应用开发。