06 PWM
作者:沈园园
一、PWM 概述
1.1 pwm 是什么
脉冲宽度调制(PWM),是英文“Pulse Width Modulation”的缩写,简称脉宽调制,是利用微处理器的数字输出来对模拟电路进行控制的一种非常有效的技术,广泛应用在从测量、通信到功率控制与变换的许多领域中。
要想使用 pwm 还需了解 PWM 的相关参数:
- PWM 的频率: 是指 1 秒钟内信号从高电平到低电平再回到高电平的次数(一个周期),也就是说一秒钟 PWM 有多少个周期 单位:Hz
- PWM 的周期: T=1/f 周期=1/频率 例:如果频率为 50Hz ,也就是说一个周期是 20ms,那么一秒钟就有 50 次 PWM 周期
- PWM 的占空比: 是一个脉冲周期内,高电平的时间占整个周期时间的比例 单位: % (0%-100%) 例:一个周期 10ms,高电平占 8ms,那么此占空比就是 8/10=80%
1.2 pwm 原理
单片机的 IO 引脚输出的是数字信号,且只能输出 1 和 0,那如果 TTL 电平中,高电平为 5V,低电平为 0V,但是我们想要输出不同的模拟电压,比如输出 3.75V 应该怎么操作?
此时要用到 PWM,通过改变 IO 口输出方波的占空比,得到不同的模拟电压。3.75/5=0.75 ,也就是高电平时间占整个周期的 75%,即占空比为 75%,可得到模拟电压 3.75V。

注意上图中蓝线,代表着输出的模拟电压,占空比越大,则模拟电压也越大。
1.3 pwm 通道说明
查阅Air8101 GPIO引脚复用表 可知Air8101支持 6 路 PWM,ID 为 0 - 5。
调用 PWM 库的 API 时,填 PWM ID 即可,通过GPIO引脚复用表查阅PWM ID 与硬件管脚的对应关系。
二、演示模块概述
1、main.lua:主程序入口;
2、pwm_app.lua:PWM 输出功能模块;
注意事项:
1、本 demo 演示所使用的是 Air8101 模组的 PWM2 通道(GPIO24,PIN33)
2、PWM 功能需要使用 V2xxx 版本固件,固件下载链接:https://docs.openluat.com/air8101/luatos/firmware/
三、演示功能概述
使用 Air8101 核心板搭配 PWM 库演示 PWM 输出功能;
PWM 库目前有两套 API 风格:
1、旧风格 PWM 演示:
- 使用 pwm.open() 函数一次性完成 PWM 通道的配置与启动
- 使用 pwm.close() 函数关闭 PWM 通道
- 旧风格 PWM 接口不支持动态调整参数
2、新风格 PWM 演示:
- 使用 pwm.setup() 函数进行 PWM 参数配置
- 使用 pwm.start() 函数开启 PWM 通道进行 PWM 输出
- 使用 pwm.setDuty() 函数动态调整占空比,支持在开启 PWM 通道后调用
- 使用 pwm.setFreq() 函数动态调整信号频率,支持在开启 PWM 通道后调用
- 新风格 PWM 接口支持实时动态调整占空比和信号频率
四、准备硬件环境

1、Air8101核心板一块
2、TYPE-C USB数据线一根
3、Air8101核心板和数据线的硬件接线方式为
- Air8101核心板通过TYPE-C USB口连接TYPE-C USB 数据线,数据线的另外一端连接电脑的USB口;
- Air8101核心板通过TYPE-C USB口供电(核心板背面的功耗测试开关拨到OFF一端);
4、杜邦线若干
5、逻辑分析仪或者示波器,用于观察 PWM 输出的波形
6、代码中选用的 PWM 通道是 Air8101 模组的 PWM2 通道(GPIO24,PIN33)
五、准备软件环境
5.1 软件环境
在开始实践本示例之前,先筹备一下软件环境:
1、烧录工具:Luatools 下载调试工具
2、内核固件(需要使用 V2xxx 版本固件):Air8101 最新版本的内核固件
本demo开发测试时使用的固件为Air8101 V2010 版本固件,(需要使用 V2xxx 版本固件),所以你如果要测试本demo时,可以直接使用最新版本的内核固件;如果发现最新版本的内核固件测试有问题,可以使用我们开发本demo时使用的内核固件版本来对比测试;
3、脚本文件:https://gitee.com/openLuat/LuatOS/tree/master/module/Air8101/demo/pwm
4、lib脚本文件:使用Luatools烧录时,勾选 添加默认lib 选项,使用默认lib脚本文件
准备好软件环境之后,接下来查看 Air8101 核心板使用说明,将本篇文章中演示使用的项目文件烧录到 Air8101 核心板中
5.2 API 介绍
pwm 库:https://docs.openluat.com/osapi/core/pwm/
六、程序结构
pwm/
│── main.lua
│── pwm_app.lua
│── readme.md
6.1 文件说明
main.lua:主程序入口文件。pwm_app.lua:PWM 输出功能模块。
七、代码详解
7.1 main.lua
主程序文件 main.lua 是整个项目的入口点。它负责初始化系统环境。
7.2 pwm_app.lua
PWM 输出功能模块。
7.2.1 旧风格 PWM 演示
local result = pins.setup(33, "PWM2")
if result then
log.info("PWM", "PWM2 通道配置成功(GPIO24,PIN33)")
else
log.error("PWM", "PWM2 通道配置失败(GPIO24,PIN33)")
end
--[[
旧风格 PWM 演示函数
使用 pwm.open() 一次性完成配置和启动
适合固定频率/占空比、无需中途调整的场景
]]
local function task1_old_pwm()
log.info("PWM", "旧风格 PWM 示例开始")
-- 选择 PWM 通道 2
-- 注意:本 demo 演示所使用的是 Air8101 模组的 PWM2 通道(GPIO24,PIN33);
local pwm_channel = 2
-- 第一次输出:1 kHz,45% 占空比,分频精度 100
local pwm_success = pwm.open(pwm_channel, 1000, 45, 0, 100)
if pwm_success then
log.info("PWM", "PWM2 通道开启成功: 信号频率 1000 Hz, 分频精度 100, 占空比 45%")
else
log.info("PWM", "PWM2 通道开启失败")
end
-- 持续 1 s 后关闭
sys.wait(1000)
pwm.close(pwm_channel)
log.info("PWM", "PWM2 通道已关闭")
-- 增加 1 秒的间隔时间
sys.wait(1000)
-- 第二次输出:500 Hz,60% 占空比,分频精度 100
local pwm_success = pwm.open(pwm_channel, 500, 60, 0, 100)
if pwm_success then
log.info("PWM", "PWM2 通道开启成功: 信号频率 500 Hz, 分频精度 100, 占空比 60%")
else
log.info("PWM", "PWM2 通道开启失败")
end
-- 持续 2 s 后关闭
sys.wait(2000)
pwm.close(pwm_channel)
log.info("PWM", "PWM2 通道已关闭")
-- 增加 1 秒的间隔时间
sys.wait(1000)
-- 第三次输出:300 Hz,80% 占空比,分频精度 100
local pwm_success = pwm.open(pwm_channel, 300, 80, 0, 100)
if pwm_success then
log.info("PWM", "PWM2 通道开启成功: 信号频率 300 Hz, 分频精度 100, 占空比 80%")
else
log.info("PWM", "PWM2 通道开启失败")
end
-- 持续 3 s 后关闭
sys.wait(3000)
pwm.close(pwm_channel)
log.info("PWM", "PWM2 通道已关闭")
log.info("PWM", "旧风格 PWM 示例结束")
end
7.2.2 新风格 PWM 演示
--[[
新风格 PWM 演示函数
使用 pwm.setup() 分步完成配置与启动,支持运行中动态修改频率和占空比
适合需要实时调节输出参数的场景
]]
local function task2_new_pwm()
log.info("PWM", "新风格 PWM 示例开始")
-- 选择 PWM 通道 2
-- 注意:本 demo 演示所使用的是 Air8101 模组的 PWM2 通道(GPIO24,PIN33);
local pwm_channel = 2
-- 配置 PWM 参数:频率 1000 Hz、占空比 50%、分频精度 100
local setup_success = pwm.setup(pwm_channel, 1000, 50, 0, 100)
if setup_success then
log.info("PWM", "PWM2 配置成功: 信号频率 1000 Hz, 分频精度 100, 占空比 50%")
else
log.info("PWM", "PWM2 配置失败")
end
-- 启动 PWM 输出
local pwm_success = pwm.start(pwm_channel)
if pwm_success then
log.info("PWM", "PWM2 启动成功")
else
log.info("PWM", "PWM2 启动失败")
end
-- 持续输出 2 秒
sys.wait(2000)
-- 动态调整占空比至 25%
local setduty_success = pwm.setDuty(pwm_channel, 25)
if setduty_success then
log.info("PWM", "PWM2 占空比更新为 25%")
else
log.info("PWM", "PWM2 占空比设置失败")
end
-- 持续输出 2 秒
sys.wait(2000)
-- 动态调整信号频率为 2000 Hz
local setfreq_success = pwm.setFreq(pwm_channel, 2000)
if setfreq_success then
log.info("PWM", "PWM2 频率更新为 2000 Hz")
else
log.error("PWM", "PWM2 频率设置失败")
end
-- 持续输出 2 秒
sys.wait(2000)
-- 停止 PWM 输出
local pwm_success = pwm.stop(pwm_channel)
if pwm_success then
log.info("PWM", "PWM2 停止成功")
else
log.info("PWM", "PWM2 停止失败")
end
log.info("PWM", "新风格 PWM 示例结束")
end
7.2.3 主演示任务
--[[
主演示任务:
顺序调用旧风格与新风格 PWM 示例函数
并在两者之间插入 3 秒间隔方便区分新旧风格示例输出情况
]]
local function pwm_demo_task()
log.info("PWM", "PWM 综合演示任务开始")
-- 运行旧风格 PWM 示例
task1_old_pwm()
-- 间隔 3 秒
sys.wait(3000)
-- 运行新风格 PWM 示例
task2_new_pwm()
log.info("PWM", "PWM 综合演示任务结束")
end
-- 创建并启动一个 task
-- 用于运行 pwm_demo_task 函数
sys.taskInit(pwm_demo_task)
八、运行结果展示
出现类似于下面的日志,使用逻辑分析仪或者示波器观察 PWM 输出波形是否与配置的参数一致,如一致,就表示运行成功:
8.1 打印日志
[2025-12-10 09:38:25.280] luat:U(2057):I/user.main PWM 001.000.000
[2025-12-10 09:38:25.287] luat:U(2082):I/user.PWM PWM2 通道配置成功(GPIO24,PIN33)
[2025-12-10 09:38:25.287] luat:U(2083):I/user.PWM PWM 综合演示任务开始
[2025-12-10 09:38:25.287] luat:U(2083):I/user.PWM 旧风格 PWM 示例开始
[2025-12-10 09:38:25.287] luat:D(2084):pwm:pwm(2) gpio init : pwm_pin 24
[2025-12-10 09:38:25.287] luat:U(2085):I/user.PWM PWM2 通道开启成功: 信号频率 1000 Hz, 分频精度 100, 占空比 45%
[2025-12-10 09:38:26.288] luat:U(3088):I/user.PWM PWM2 通道已关闭
[2025-12-10 09:38:27.314] luat:D(4089):pwm:pwm(2) gpio init : pwm_pin 24
[2025-12-10 09:38:27.314] luat:U(4089):I/user.PWM PWM2 通道开启成功: 信号频率 500 Hz, 分频精度 100, 占空比 60%
[2025-12-10 09:38:29.321] luat:U(6091):I/user.PWM PWM2 通道已关闭
[2025-12-10 09:38:30.321] luat:D(7093):pwm:pwm(2) gpio init : pwm_pin 24
[2025-12-10 09:38:30.321] luat:U(7094):I/user.PWM PWM2 通道开启成功: 信号频率 300 Hz, 分频精度 100, 占空比 80%
[2025-12-10 09:38:33.323] luat:U(10095):I/user.PWM PWM2 通道已关闭
[2025-12-10 09:38:33.323] luat:U(10096):I/user.PWM 旧风格 PWM 示例结束
[2025-12-10 09:38:36.320] luat:U(13096):I/user.PWM 新风格 PWM 示例开始
[2025-12-10 09:38:36.320] luat:U(13097):I/user.PWM PWM2 配置成功: 信号频率 1000 Hz, 分频精度 100, 占空比 50%
[2025-12-10 09:38:36.320] luat:D(13098):pwm:pwm(2) gpio init : pwm_pin 24
[2025-12-10 09:38:36.320] luat:U(13098):I/user.PWM PWM2 启动成功
[2025-12-10 09:38:38.302] luat:U(15099):I/user.PWM PWM2 占空比更新为 25%
[2025-12-10 09:38:40.328] luat:U(17100):I/user.PWM PWM2 频率更新为 2000 Hz
[2025-12-10 09:38:42.326] luat:U(19102):I/user.PWM PWM2 停止成功
[2025-12-10 09:38:42.326] luat:U(19103):I/user.PWM 新风格 PWM 示例结束
[2025-12-10 09:38:42.326] luat:U(19103):I/user.PWM PWM 综合演示任务结束
8.2 波形图
以下为部分截图展示,详细波形图,可以通过Kingst VIS软件打开2025-12-04_13-46-48.kvdat文件查看。
Kingst VIS软件下载地址:https://www.qdkingst.com/cn
kvdat文件: 2025-12-04_13-46-48.kvdat
旧风格波形图
信号频率1000HZ,占空比为45%波形图:

信号频率500HZ,占空比为60%波形图:

信号频率300HZ,占空比为80%波形图:

新风格波形图
信号频率1000HZ,占空比为50%波形图:

信号频率1000HZ,占空比为25%波形图:

信号频率2000HZ,占空比为25%波形图:

完整波形图

九、总结
本文通过合宙核心板展示 PWM(脉冲宽度调制)的实现方法,帮助读者深入理解如何在项目中应用 PWM 技术。PWM 作为一种灵活且高效的信号调制手段,在电气设备的性能控制和调节中发挥着重要作用。通过精确控制信号的占空比,PWM 能够实现电能的有效管理。
PWM 的主要应用:
- 电机控制:通过调节电机供电的 PWM 信号,控制电机转速和扭矩。
- 灯光调光:用于调节 LED 灯的亮度,改变状态的快慢来实现不同亮度效果。
- 音频信号生成:在音频电子设备中生成不同的声音频率和音量。
- 加热控制:在加热元件中使用 PWM 来调整输出功率,从而实现精确的温度控制。
PWM 的优点:
- 高效性:PWM 有效降低能量损耗,因为其开关操作使功率元件始终处于全导通或全关闭状态。
- 控制精度高:可以通过调节占空比进行非常精确的控制。
- 实现简便:可以通过简单的数字电路或微控制器轻松实现。
PWM 的缺点:
- 高频噪声:由于快速切换,PWM 信号可能产生高频噪声,需要滤波以减小干扰。
- 硬件要求:某些应用需要特定的硬件支持,如支持 PWM 输入的微控制器或电路。
十、常见问题
- 如何验证所产生的 PWM 信号?
可以使用示波器检测 PWM 输出,查看频率、占空比和波形的完整性。确保波形按照设计参数正常工作,必要时进行调整。
- PWM 的信号的频率如何选择?
频率的选择通常取决于具体应用,根据自己的项目需要来选择:
(1)电机控制:常用频率为 1 kHz 到 20 kHz,以确保足够的反应速度和控制精度。
(2)LED 调光:频率通常在 1 kHz 以上,以避免人眼感知到闪烁。
(3)音频信号:频率设置应高于音频信号的最高频率(例如,至少为 20 kHz)。
- 什么是占空比,如何计算 PWM 的占空比?
占空比是指在周期性脉冲信号的一个周期内,高电平(或有效状态)的持续时间占整个周期时间的比例(通常用百分比表示)。 例如,如果 PWM 信号的高电平时间为 2 ms,周期为 10 ms,占空比 = (2 / 10) × 100 = 20%。