# WS63 PWM 技术使用文档 ## 文档概述 本文档基于WS63平台的PWM驱动实现,为不同经验的开发人员提供PWM(脉宽调制)功能的配置与控制指导。 **适用人群**: - 🌱 **新手开发者**:只熟悉0-255亮度控制,希望快速上手PWM - 🛠️ **进阶开发者**:需要深入了解PWM参数配置和高级功能 - 🔍 **系统调试者**:需要问题诊断和性能优化 **平台基础参数**: - MCU平台:WS63 (HiSilicon) - 系统时钟:80 MHz - 计数器位宽:32-bit(u32) - 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); // 通道0,50%亮度 // 第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); // 通道0,0%亮度(关闭) // set_pwm_brightness_simple(0, 128); // 通道0,50%亮度 // set_pwm_brightness_simple(0, 255); // 通道0,100%亮度(最亮) ``` ### 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应用开发。