上一期学会了如何使用定时器,本期来学习跟定时器相关的一个设备——PWM。
1.PWM简介
PWM(Pulse Width Modulation , 脉冲宽度调制) 是一种对模拟信号电平进行数字编码的方法,通过不同频率的脉冲使用方波的占空比用来对一个具体模拟信号的电平进行编码,使输出端得到一系列幅值相等的脉冲,用这些脉冲来代替所需要波形的设备。
上图是一个简单的 PWM 原理示意图,假定定时器工作模式为向上计数,当计数值小于阈值时,则输出一种电平状态,比如高电平,当计数值大于阈值时则输出相反的电平状态,比如低电平。当计数值达到最大值是,计数器从0开始重新计数,又回到最初的电平状态。高电平持续时间(脉冲宽度)和周期时间的比值就是占空比,范围为0~100%。上图高电平的持续时间刚好是周期时间的一半,所以占空比为50%。
这里就简单介绍一下要想要了解更多可以去官方手册内查看,路径如下:
2.工程配置
2.1硬件设置
注意:一定要确定连接LED的引脚能输出PWM波,不然无法看到呼吸灯效果。
2.2Kconfig添加(官方的BSP没有将所有定时器及其通道都添加到设置中)
Kconfig在之前也跟大家引入过,他的作用是通过添加Settings的选项来使能单片机的外部设备或者使能板载的一些设备(例如:FLASH,EEPROM,SDCard,LED,BEEP等等)。本次如果使用的是正点原子家战舰V3板子的话这一部分就可以不添加,因为刚好BSP就有TIM3_CH2(LED的引脚对应的PWM通道)的选项,直接选择就行。配置如下:
右图就是Kconfig的内容,我们如果想要用TIM2的话怎么办呢,很简单,咱们就抄,参考前面写好的Kconfig来添加咱们需要的其他TIM,例如下图我添加了TIM2及其通道123:
添加完后一定要关闭之前打开的Settings然后鼠标点击左边工程空白选择同步scons配置至项目。然后再去打开Settings,如果能正常加载进去就没有问题,如果无法正常进入的话就说明添加的Kconfig有问题。一般报错的原因为:
1.endif个数与if 的个数不匹配
2.漏粘贴某些语句
添加完后的界面如下图,刚好和我们添加的TIM2及其123通道相对应了(是不是很神奇!!!)
2.3CubeMX来开启TIM时钟
上面的基本工作都做好了,现在就差开启时钟了,打开CubeMX使能TIM3_CH2。
配置好后右上角生成代码即可。
3.代码实现
完成上面的操作后就完事具备只欠东风了,我们只需要创建pwm.c文件进行编程即可。
/*
* Copyright (c) 2006-2021, RT-Thread Development Team
*
* SPDX-License-Identifier: Apache-2.0
*
* Change Logs:
* Date Author Notes
* 2022-11-13 ZXY the first version
*/
/*
* 程序清单:这是一个 PWM 设备使用例程
* 例程导出了 pwm_led_sample 命令到控制终端
* 命令调用格式:pwm_led_sample
* 程序功能:通过 PWM 设备控制 LED 灯的亮度,可以看到LED不停的由暗变到亮,然后又从亮变到暗。
*/
#include <rtthread.h>
#include <rtdevice.h>
#define PWM_DEV_NAME "pwm3" /* PWM设备名称 */
#define PWM_DEV_CHANNEL 2 /* PWM通道 PB5*/
struct rt_device_pwm *pwm_dev; /* PWM设备句柄 */
static int pwm_led_sample(int argc, char *argv[])
{
rt_uint32_t period, pulse, dir;
period = 500000; /* 周期为0.5ms,单位为纳秒ns */
dir = 1; /* PWM脉冲宽度值的增减方向 */
pulse = 5000; /* PWM脉冲宽度值,单位为纳秒ns */
/* 查找设备 */
pwm_dev = (struct rt_device_pwm *)rt_device_find(PWM_DEV_NAME);
if (pwm_dev == RT_NULL)
{
rt_kprintf("pwm sample run failed! can't find %s device!\n", PWM_DEV_NAME);
return RT_ERROR;
}
/* 设置PWM周期和脉冲宽度默认值 */
rt_pwm_set(pwm_dev, PWM_DEV_CHANNEL, period, pulse);
/* 使能设备 */
rt_pwm_enable(pwm_dev, PWM_DEV_CHANNEL);
while (1)
{
rt_thread_mdelay(50);
if (dir)
{
pulse += 5000; /* 从0值开始每次增加5000ns */
}
else
{
pulse -= 5000; /* 从最大值开始每次减少5000ns */
}
if (pulse >= period)
{
dir = 0;
}
if (0 >= pulse)
{
dir = 1;
}
/* 设置PWM周期和脉冲宽度 */
rt_pwm_set(pwm_dev, PWM_DEV_CHANNEL, period, pulse);
}
}
/* 导出到 msh 命令列表中 */
MSH_CMD_EXPORT(pwm_led_sample, pwm sample);