跳转至

pwm开发指南

本文档以 pwm_demo.c 为例,带你在 HiDiTing 开发板上快速跑通第一个 PWM(脉冲宽度调制)控制应用,并了解如何基于它构建自己的应用。


PWM 驱动背景知识

PWM工作原理

PWM(脉冲宽度调制)是一种通过调节脉冲宽度(占空比)来控制输出功率的数字调制技术。PWM 信号由高电平和低电平两部分组成,通过调整高电平时间与周期的比例(即占空比)来控制输出效果。

参数 说明 应用场景
占空比 高电平时间与总周期的比例 调节 LED 亮度、电机转速、蜂鸣器音量
频率 每秒脉冲周期数 决定 PWM 信号的基频
相位偏移 信号的起始相位偏移量 多通道 PWM 同步控制
连续/非连续模式 是否持续输出 PWM 信号 连续模式用于持续控制,非连续模式用于单次触发

占空比说明:

  • 占空比 = high_time / (high_time + low_time) × 100%
  • 本示例中,1KHz 频率对应的时钟周期数为 32000,因此:
    • 80% 占空比:high_time = 25600,low_time = 6400
    • 20% 占空比:high_time = 6400,low_time = 25600

操作流程

本示例通过 AT 命令触发 PWM 输出测试,演示了完整的 PWM 操作流程:

PWM 初始化流程:

  1. 引脚配置:调用 uapi_pin_set_mode() 将引脚配置为 PWM 功能模式。
  2. PWM 初始化:调用 uapi_pwm_init() 初始化 PWM 模块。
  3. 打开通道:调用 uapi_pwm_open() 打开指定 PWM 通道并配置初始参数。
  4. 配置组:调用 uapi_pwm_set_group() 将通道添加到指定组。

PWM 输出流程(循环 5 次):

  1. 预加载配置:调用 uapi_pwm_config_preload() 设置新的占空比参数。
  2. 启动输出:调用 uapi_pwm_start_group() 启动 PWM 组输出。
  3. 延时输出:调用 uapi_tcxo_delay_ms(500) 持续输出 500ms。
  4. 停止输出:调用 uapi_pwm_stop_group() 停止 PWM 组输出。

PWM 资源清理流程:

  1. 关闭通道:调用 uapi_pwm_close() 关闭 PWM 通道。
  2. 去初始化:调用 uapi_pwm_deinit() 释放 PWM 资源。

关键参数说明

宏定义 说明
PWM_FREQ_1KHZ 32000 1KHz 对应的时钟周期数
PWM_DUTY_80PCT 25600 80% 占空比的高电平时间
PWM_DUTY_20PCT 6400 20% 占空比的高电平时间
PWM_OFFSET_0 0 相位偏移为 0
PWM_CYCLES_CONTINUOUS 0 0 表示连续输出模式
CONFIG_PWM_CHANNEL 0 PWM 通道号
CONFIG_PWM_GROUP_ID 0 PWM 组 ID
CONFIG_PWM_PIN 34 PWM 输出引脚
CONFIG_PWM_PIN_MODE 1 PWM 功能对应的引脚复用模式

API 接口列表

本文档示例代码中使用的接口:

接口函数 说明
uapi_pwm_init 初始化 PWM 模块
uapi_pwm_deinit 去初始化 PWM
uapi_pwm_open 打开 PWM 通道
uapi_pwm_close 关闭 PWM 通道
uapi_pwm_set_group 将通道添加到 PWM 组
uapi_pwm_start_group 启动 PWM 组输出
uapi_pwm_stop_group 停止 PWM 组输出
uapi_pwm_config_preload 预加载 PWM 配置

完整 API 列表

更多 PWM 接口请参考:PWM API 参考

注意事项

  • 在调用uapi_pwm_deinit接口之前,需要先调用uapi_pwm_close接口。
  • PWM调用uapi_pwm_stop/uapi_pwm_close时不支持在中断中调用。
  • PWM不支持占空比为0。
  • PWM不支持多路互补输出。
  • 深睡唤醒之后需要先配置复用关系,再调用uapi_pwm_deinit接口去初始化,之后调用uapi_pwm_init接口初始化。
  • PWM分为M_PWM和A_PWM两种。M_PWM的使用请参考如下,A_PWM通过调用“/src/drivers/chips/3322/pwm_aon/drivers/pwm_aon.h”文件中的接口实现,虽命名存在差异,但用法相同。

M_PWM的使用请参考(PWM利用微处理器的数字输出对模拟电路进行控制)

  • 将IO复用为PWM功能。
  • 调用uapi_pwm_init对PWM进行初始化。
  • 调用uapi_pwm_open,配置PWM参数,打开指定通道。
  • 调用uapi_pwm_register_interrupt接口,注册PWM中断的回调函数。
  • 调用uapi_pwm_start接口,开启指定ID的PWM信号输出。
  • 调用uapi_pwm_close接口,停止指定ID的PWM信号输出。
  • 调用uapi_pwm_deinit接口,去初始化指定ID的PWM。

快速跑通 PWM Demo

功能说明

PWM Demo 通过 AT 命令方式提供交互式控制,支持以下命令:

AT 命令 功能 参数说明
AT+PWMSAMPLE=0 执行 PWM 输出测试 参数固定为 0,无需实际解析

编译

完成一站式 CLI 环境配置后执行:

# 编译固件,编译生成的固件从 output/3322/fwpkg 中获取 diting-community.fwpkg
fbb set-target pack_diting_community
fbb build

构建成功后,使用一站式 CLI 烧写固件并打开 UART2 串口监视器。以下为 Windows USB DFU 示例;将 COM3 替换为实际日志串口,其他平台和串口烧写参数参见一站式 CLI 开发环境使用指南

fbb flash -f "$env:FBB_SDK_DIR\output\3322\fwpkg\diting-community.fwpkg" --chip 3322 -d --timeout 180
fbb monitor --port COM3 --baud 750000

使用方式

  1. 烧录固件并启动设备
  2. 通过串口发送 AT 命令,格式如下:

    # 执行 PWM 输出测试
    AT+PWMSAMPLE=0
    

通过串口发送 AT 命令,格式如下

预期结果

PWM 输出测试(AT+PWMSAMPLE)

执行后,设备会依次输出 5 次不同占空比的 PWM 信号,每次持续 500ms:

执行后,设备会依次输出 5 次不同占空比的 PWM 信号,每次持续 500ms


文件结构与代码走读

文件职责

文件 作用
samples/native_samples/pwm/CMakeLists.txt 定义 pwm_sample 组件及驱动依赖。
samples/native_samples/pwm/pwm_demo.h 集中定义通道、组、引脚、周期和占空比等 Sample 参数。
samples/native_samples/pwm/pwm_demo.c 实现 PWM 初始化、预加载、分组启停和清理。
samples/native_samples/pwm/README.md 给出完整命令和预期波形。

核心数据与常量

名称 作用 开发注意事项
pwm_config_t 描述高、低电平时间、相位偏移、周期数和重复方式。 high_time + low_time 决定周期,占空比由两者比例决定。
CONFIG_PWM_PIN / CONFIG_PWM_PIN_MODE 选择 PWM 输出引脚及复用模式。 必须与板级连线和目标通道匹配。
CONFIG_PWM_CHANNEL / CONFIG_PWM_GROUP_ID 选择通道并将其加入输出组。 预加载和启停均应使用同一组、通道组合。
PWM_DUTY_80PCT / PWM_DUTY_20PCT Sample 使用的两组高低电平时间。 这些数值基于当前 PWM 时钟,修改时钟后需重新计算。
PWM_CYCLES_CONTINUOUS 取 0 时表示连续输出。 业务需要有限脉冲时应设置明确周期数。

核心业务流程

  1. 配置 PWM 引脚复用并调用 uapi_pwm_init()
  2. 填写 pwm_config_t,调用 uapi_pwm_open() 打开通道,再用 uapi_pwm_set_group() 建立通道组。
  3. 需要无毛刺更新参数时,先调用 uapi_pwm_config_preload(),再启动对应组。
  4. 调用 uapi_pwm_start_group() 输出波形;达到业务条件后调用 uapi_pwm_stop_group()
  5. 退出时关闭通道并去初始化 PWM,错误分支同样执行资源回收。

现有 AT Sample 只是依次切换 80% 和 20% 占空比并统计执行结果,完整日志和异常处理可直接查看源文件。自定义应用应通过业务入口驱动同一流程,下一节保留了可构建的 app_run 示例,不再重复 AT 处理代码。

基于 PWM Demo 开发自己的应用

上面的demo是使用AT指令触发运行,HiDiTing还支持app_run方式触发应用在系统启动时自动运行,以下示例将以app_run的方式开发一个开发者自己的应用

  • app_run(func) 是HiDiTing中应用层注册应用函数的宏,基于 GCC 编译器属性和自定义段区(section)自动注册来实现集中调用应用函数,系统启动时会自动遍历所有用 app_run 注册过的函数并执行,无需在 系统 main 函数里逐个调用函数。

代码清单

新建一个 PWM 控制应用(以 my_pwm_demo 为例)通常只需要以下改动:

  • 新建 my_pwm_demo.c 源文件
  • 新建 my_pwm_demo.h 头文件
  • 新建 CMakeLists.txt 源文件
  • my_pwm_demo.c 中实现 PWM 操作函数
  • my_pwm_demo.h 中定义宏常量和函数声明
  • CMakeLists.txt 源文件编译规则

my_pwm_demo文件结构:

/samples/native_samples/my_pwm_demo/
├── my_pwm_demo.c
├── my_pwm_demo.h
└── CMakeLists.txt

CMakeLists.txt 修改示例

# 在新建的 CMakeLists.txt 中添加应用的源文件
set(SOURCES
    ${CMAKE_CURRENT_SOURCE_DIR}/my_pwm_demo.c
)

set(PUBLIC_HEADER
    ${CMAKE_CURRENT_SOURCE_DIR}
)

set(PRIVATE_HEADER

)

set(PUBLIC_DEFINES
    MY_PWM_DEMO_ENABLE
)

build_component()

关键代码片段

my_pwm_demo.c —— PWM 控制实现

#include "my_pwm_demo.h"
#include "pwm.h"
#include "pinctrl.h"
#include "pinctrl_porting.h"
#include "errcode.h"
#include "debug_print.h"
#include "driver/pinctrl.h"
#include "soc_osal.h"
#include "app_init.h"
#include "tcxo.h"

#define MY_PWM_PIN          34      // PWM 输出引脚
#define MY_PWM_PIN_MODE     1       // PWM 功能对应的引脚复用模式
#define MY_PWM_CHANNEL      0       // PWM 通道号
#define MY_PWM_GROUP_ID     0       // PWM 组 ID
#define TASK_PRIO           26
#define TASK_STACK_SIZE     0x1000

static void *my_pwm_task(const char *arg)
{
    unused(arg);
    printf("my_pwm_demo start\n");

    // 初始化
    uapi_pin_set_mode(MY_PWM_PIN, MY_PWM_PIN_MODE);
    uapi_pwm_init();

    // 配置 PWM 参数(50% 占空比)
    pwm_config_t pwm_cfg = {
        .low_time = 16000,          // 50% 占空比
        .high_time = 16000,         // 50% 占空比
        .offset_time = 0,
        .cycles = 0,                // 连续输出
        .repeat = true
    };

    // 打开通道
    uapi_pwm_open(MY_PWM_CHANNEL, &pwm_cfg);

    // 配置组
    uint8_t channel_id = MY_PWM_CHANNEL;
    uapi_pwm_set_group(MY_PWM_GROUP_ID, &channel_id, 1);

    // 启动输出
    uapi_pwm_start_group(MY_PWM_GROUP_ID);
    printf("pwm started with 50%% duty cycle\n");

    // 输出 2 秒
    uapi_tcxo_delay_ms(2000);

    // 停止输出
    uapi_pwm_stop_group(MY_PWM_GROUP_ID);
    printf("pwm stopped\n");

    // 清理资源
    uapi_pwm_close(MY_PWM_CHANNEL);
    uapi_pwm_deinit();

    printf("my_pwm_demo done\n");
    return NULL;
}

static void my_pwm_entry(void)
{
    osal_task *task_handle = NULL;
    osal_kthread_lock();
    task_handle = osal_kthread_create((osal_kthread_handler)my_pwm_task, 0, "MyPwmTask", TASK_STACK_SIZE);
    if (task_handle != NULL) {
        osal_kthread_set_priority(task_handle, TASK_PRIO);
    }
    osal_kthread_unlock();
}

app_run(my_pwm_entry);

测试验证

完成一站式 CLI 环境配置后执行:

# 编译固件,编译生成的固件从 output/3322/fwpkg 中获取 diting-community.fwpkg
fbb set-target pack_diting_community
fbb build
1. 烧录固件并启动设备 2. 通过串口观察输出日志:

通过串口观察输出日志

说明

  • 因为app_run是在系统启动时运行应用,所以应用的运行日志会被夹在开机启动日志中。

  • 为使该pwm应用参与编译,还需在上层CMakeLists.txt文件中以宏MY_PWM_DEMO_ENABLE为条件添加该my_pwm_demo文件。

  • MY_PWM_DEMO_ENABLE默认关闭。如需启用新建PWM示例功能,需要在config.py中的diting-community配置项下将其打开。

app_run运行配置

app_run应用默认是关闭的,如需启用此应用,需用户手动在acore.prelds文件中添加 KEEP (*(SORT(.zinitcall.app_run*.init))) 具体参考示意图如下:

apprun运行配置

acore.prelds


注意事项

引脚配置注意事项:

  • PWM 输出引脚为 34,具体引脚对应的物理引脚请参考芯片手册。
  • 使用前必须调用 uapi_pin_set_mode() 将引脚配置为 PWM 功能模式。
  • 引脚复用模式 CONFIG_PWM_PIN_MODE 设置为 1。

PWM 初始化注意事项:

  • 必须依次调用 uapi_pwm_init()uapi_pwm_open() 完成初始化。
  • 打开通道时必须提供有效的 pwm_config_t 配置结构体。
  • 配置组时,通道 ID 必须与打开时使用的通道号一致。

PWM 输出注意事项:

  • 每次改变占空比前,必须先调用 uapi_pwm_config_preload() 预加载新配置。
  • 启动输出后,需要通过延时函数控制输出时长。
  • 输出完成后,必须调用 uapi_pwm_stop_group() 停止输出。

资源清理注意事项:

  • 使用完成后,必须调用 uapi_pwm_close() 关闭通道。
  • 最后调用 uapi_pwm_deinit() 释放 PWM 资源。

AT 命令注意事项:

  • 命令名称为大写字母,如 PWMSAMPLE
  • 参数固定为 0,无需实际解析。

常见错误

错误现象 原因 解决方法
undefined reference to 'uapi_pwm_init' 未链接 PWM 驱动库 检查 CMakeLists.txt 中是否正确添加了头文件路径
undefined reference to 'uapi_at_cmd_table_register' 未链接 AT 命令库 检查 CMakeLists.txt 中是否正确添加了 AT 相关头文件路径
编译报 at_ret_t 未定义 未包含 at.h 头文件 添加 #include "at.h"
串口无输出 AT 命令未注册 确认调用了 at_diting_pwm_example_cmd_register()
引脚无响应 引脚未正确配置 确认调用了 uapi_pin_set_mode() 配置为 PWM 模式
PWM 输出异常 占空比参数配置错误 确认 high_timelow_time 之和等于时钟周期数
资源泄漏 未调用资源清理函数 确认调用了 uapi_pwm_close()uapi_pwm_deinit()