什么是PWM
脉冲宽度调制
(Pulse width modulation)
是一种使用数字信号模拟出模拟信号变化的方案
自然生活中的大多数信号为模拟信号
- 如亮度,速度,音量等
然而模拟电路往往会比较复杂而且抗干扰能力较差
因此脉冲宽度调制技术应运而生
通过输出固定频率,不同宽度的脉冲可以获得平滑变化的平均电压,用来近似模拟输出
PWM的应用
数字电路的应用
- 比如现在的节能灯(led)通过PWN控制灯光的亮度
- 手机充电器有可能会用到PWM
RP2040 PWM框架图
PWM流程图
- 时钟源输出时钟信号到分频器,分频器根据分频器参数进行分频
- 分频得到的信号作为计数器的时钟源,再进行计数
- 比较器通过比较输入值,输出对应的电平
- 分频器
输入信号进行分频或者降频
若输入频率为100hz,
分频参数为10,
则分频输出信号的频率为10hz
- 计数器
记录输入信号的脉冲个数
每一个时钟信号到来时,
计数值加一或者减一,其受到最大计数值的制约
PWM内部框架图
- 时钟源的选择
- 系统时钟
默认情况下,PWM通道运行在自由运行模式下此时时钟源为系统时钟
- 另3种模式
- 1.当在B引脚上检测到高电平时,以系统时钟为时钟源,低电平时则无时钟源
- 2.当B引脚检测到低电平转为高电平的上升沿时,则会发送出一个脉冲,以这个脉冲作为时钟源
- 3.当B引脚上检测到高电平转为低电平的下降沿时,则会发生出一个脉冲,以这个脉冲作为时钟源
这些模式均可在PWM通道的CSR寄存器DIVMODE字段设置
-
在自由模式下,引脚A和引脚B均是输出引脚
-
在其他任意模式下,引脚B成为输入引脚,并用于控制计数器,有关引脚B的输出寄存器将会被忽略
-
通过允许PWM通道在电平触发或者边缘触发模式下运行固定的时间可以用来测量输入信号的占空比或频率
注意:现阶段的Micropython只能设置为默认的自动运行模式
- 分频器
-
每个PWM通道都拥有独立的分数时钟分频器
-
分频器参数存放在DIVj寄存器中
-
这是一个8位整数,4位小数的时钟分频器
-
可以将输入频率降低到1/256
-
通过简单推导可以得到PWM频率计算公式和周期计算公式
- 计数器,比较器
PWM的输出是通过不断比较计数器数值和输入值(cc寄存器)实现的
当前计数值小于CC寄存器则输出高电平,反之则为低电平
- 高电平所占整个周期的时间比,称为占空比
- 计数周期由TOP寄存器控制
- 因为计数器和TOP寄存器的大小都是16位
- 所以最大的周期为65536个周期
PWM计数器默认是向上计数的,直到达到TOP寄存器的值,然后重置为0
RP2040还提供了一种相位校正模式
计数器在达到顶部后开始向下计数,直到再次达到0,才开始重新向上计数
因此无论占空比如何变化,脉冲总是以某一点为中心
注意:当相位校正模式启动时,输出频率减半
【MicroPython】machine.PWM类函数详解
- 机器.PWM(引脚):
- PWM对象构造函数
- pin:需要设置为PWM输出的GPIO对象;
其作用将指定GPIO重新初始化并设置为PWM输出模式
第一个参数pin为上期教程讲解过的Pin对象(引脚),用于指定使用GPIO,该对象会被重新初始化。
- PWM.deinit():
- 取消PWM初始化.
deinit为反初始化函数其作用为清空初始化,并停止PWM输出。
- PWM.freq([值]):
- 设置PWM输出频率函数。
- value: PWM输出频率,数值应符合PWM频率计算公式;
freq函数为PWM频率设置函数根据参数value,自动计算分频器参数(分频因子)和TOP寄存器参数(决定了计数器最大计数值)。
- PWM.duty_u16([value]):
- 设定计数器比较值,
- value: 设置占空比比例,数值应在0-65536间;
duty_u16函数用于设置占空比, 通过参数value,会自动计算出相对应的数值并赋予给CC寄存器,当计数器计数值比CC寄存器小时,PWM引脚输出高电平,反之输出低电平。
- PWM.duty_ns([value]):
- 设定高电平的时间;
- value: 设置高电平时间,单位为ns;
duty_ns函数其作用是设置一个周期输出高电平时长。 其参数value为高电平时间,单位为ns。
此文章仅针对RP2040 MicroPython固件,以源码为准,本文根据编写时官方源码编写,用于为初学者提供便利,仅供于参考,如有能力者建议自行查询MicroPython源码
代码实现
# 导入Pin,PWM
from machine import Pin,PWM
import utime
# 将GPIO25设置为PWM输出
LED = PWM(Pin(25))
# PWM的输出频率为1KHZ
LED.freq(1000)
LED_duty = 0
LED_direction = 1
while True:
LED_duty += LED_direction
if LED_duty >= 100:
LED_duty = 100
LED_direction = -1
elif LED_duty <= 0:
LED_duty = 0
LED_direction = 1
# 使用u16函数将LED_duty设置为占空比
LED.duty_u16(int(LED_duty * 655.36))
# 并将LED——duty能被5整除时,输出值
if LED_duty%5 == 0:
print(LED_duty)
utime.sleep(0.01)
# 这里实现了PWM初始输出占空比为0%递增至%100
# 然后开始递减,递减至0%开始递增,如此往复进行
# 这样就完成了LED呼吸灯程序
为什么要输出LED_duty的值?
这样是为了使用thonny的绘图器功能
选择显示绘图器即可绘制出