PWM控制LED颜色
# PWM 相关
# 什么是 PWM
PWM 即脉冲宽度调制,是英文“Pulse Width Modulation”的缩写,简称脉宽调试。是利用微处理器的数字输出来对模拟电路进行控制的一种非常有效的技术。广泛应用在从测量、通信到功率控制与变换的许多领域中。
例如上图中,矩形脉冲是 STM32 输出的数字信号,当这个信号接到外设上时,效果可以等效为这个正弦波
一个周期内高电平的持续时间占总周期的比例成为占空比,通过修改占空比,可以改变输出的等效模拟电压。
例如输出占空比为50%,频率为10 Hz的脉冲,高电平为3.3 V.则其输出的模拟效果相当于输出一个1.65 V的高电平
# PWM 的两个参数
输出频率
PWM 输出的频率会影响最终的 PWM 输出效果,PWM 输出的频率越高,最终输出的“连续性”越好,越接近模拟信号的效果,频率低则会增强离散性,最终的输出效果会有比较强的“突变”感。所以频率越高,则模拟的效果越好
占空比
占空比就是改变输出模拟效果的电压大小。占空比越大则模拟出的电压越大
# 定时器输出 PWM
PWM 输出是 STM32 的定时器的功能之一,为了实现PWM功能,需要使用定时器中的比较寄存器(TIMx_CCRx)
在使用定时器输出 PWM 的时候,TIMx_CNT 内的计数值将不断的与储存在 TIMx_CCRx 内的数值进行比较,当其值小于 TIMx_CCRx 内的值时,将输出高电平;当不小于其值时,将输出低电平。所以通过修改 TIMx_ARR 与 TIMx_CCRx 内存储的值可以修改一个周期的长短与占空比
符号 | 意义 |
---|---|
TIMx_CCRx 内存储的值 | |
TIM_ARR 内存储的值(重装载值减一 ) | |
占空比 |
所以占空比公式就是:
例如以下情况:
TIMx_ARR 的值为8,即周期长度是9,而TIMx_CCRx 内部储存的数为4。但其实高电平只输出到3,但是输出到3是4个单位,所以分子为4,分母为9:
# 定时器输出 PWM 的通道
一个定时器工作在 PWM 输出模式下时,有4个通道可以进行 PWM 信号的输出,每一个定时器都有对应标号的比较寄存器,比如5号定时器的1号通道对应的比较寄存器为TIM5_CCR1
我们就是通过修改这个寄存器的值来达到修改占空比的目的
# aRGB 色彩模式
aRGB 为一种色彩模式,aRGB 分别代表了alpha(透明度)Red(红色)Green(绿色)和Blue(蓝色)四个要素,一般我们给每个要素设置十进制下 0-255 的取值范围,通过16进制表示就是0x00~0xFF
,因此一个aRGB值可以通过八位十六进制数来描述,从前到后每两位依次对应a,R,G,B
aRGB 中,alpha 值越大色彩越不透明,RGB 中哪个值越大,对应的色彩就越强
提示
- 一个要素是:两位十六进制 --> 2 × 四位二进制 --> 八位二进制(8 bit)
- 总共四个要素:4 × 八位二进制 --> 32位二进制
这个有什么用呢?等会你就知道了
# 实践配置
# CubeMX
首先不用设置引脚,而是设置定时器:
将 TIM5 的三个频道都设置为 PWM 输出,可以看到这三个频道对应的三个引脚正好就是 LED 的三个引脚。然后将 TIM_ARR 的值修改为 65535
提示
为什么是 65535,这就需要刚刚的颜色小知识了:我们知道了每个要素是2位16进制,用十进制表示其范围就是0~255
。而我们对每一种颜色的表示是通过不仅仅是对这个要素自己的程度,而是要使用 alpha 透明度要素对其“加权”,这里指的加权是指将两个要素的值相乘
这么一来如果想使用 PWM 来对颜色深浅进行控制,我们需要一个 PWM 的周期长短为加权后的值,也就是
提示2
我们在 CubeMX 中对寄存器赋的值,包括预分频值、重装载值和每个频道的比较寄存器内的值,都是初值,就是生成代码的时候给你设定的,在程序中都是可以修改的。
比较寄存器的初值是在每个频道的Pulse
出输入的
然后修改时钟设定为我i们熟悉的设定:
随后生成代码
# 工程代码
主要代码是这样的一个函数:
void aRGB_led_show(uint32_t aRGB)
{
static uint8_t alpha;
static uint16_t red,green,blue;
alpha = (aRGB & 0xFF000000) >> 24;
red = ((aRGB & 0x00FF0000) >> 16) * alpha;
green = ((aRGB & 0x0000FF00) >> 8) * alpha;
blue = ((aRGB & 0x000000FF) >> 0) * alpha;
__HAL_TIM_SetCompare(&htim5, TIM_CHANNEL_1, blue);
__HAL_TIM_SetCompare(&htim5, TIM_CHANNEL_2, green);
__HAL_TIM_SetCompare(&htim5, TIM_CHANNEL_3, red);
}
2
3
4
5
6
7
8
9
10
11
12
13
14
传入的参数决定了 LED 的颜色。这个算法主要是:
- 将传入的 aRGB 值与
0xFF000000
取与运算,使只剩下 alpha 要素的十六进制值 - 随后将这个值右移24位,使表示 alpha 的八位二进制值处于末位
- 然后将这个值赋给 alpha 变量
其他的三个变量也是同样操作,但是在最后需要乘以 alpha 进行“加权”
而函数__HAL_TIM_SetCompare
则就是将比较值赋给 TIMx_CCRx 寄存器的函数。它的三个变量分别是:定时器的句柄地址、定时器内的信道号、要赋给寄存器的值,关于此函数详细信息请见这里
除了这个主要的函数,还有上节提到的开启时钟而不开启中断的HAL_TIM_Base_Start
,除了它还有一个初始化 PWM 的函数HAL_TIM_PWM_Start
,它只有两个参数,就是定时器的句柄地址与定时器的频道数(详细信息请见这里):
HAL_TIM_Base_Start(&htim5); // 开启时钟
HAL_TIM_PWM_Start(&htim5, TIM_CHANNEL1); // 初始化 PWM
HAL_TIM_PWM_Start(&htim5, TIM_CHANNEL2);
HAL_TIM_PWM_Start(&htim5, TIM_CHANNEL3);
2
3
4
实际上点亮 LED 的那个主要函数aRGB_led_show
只需要执行一遍就可以,但是放在while(0)
中也可以,那么什么时候会将此类函数放进循环里呢?
类似于呼吸灯的,能从一种状态平缓过渡到另一种这类实时改变寄存器状态的时候
提示
在前面讲过占空比与输出频率都会影响 PWM 的效果,但是这个所谓的“输出频率”到底在指谁?是我们的时钟频率预分频后到定时器内的频率吗?有一定影响,但是这个频率与一个固定的占空比只能决定一种状态!
时钟的频率无法动态修改,但是占空比是可以修改的,所以在循环中高频的动态修改占空比就可以实现一些平滑过渡的效果,比如呼吸灯
实现呼吸灯的代码是在上面点亮的基础上,在while(1)
中不断地修改占空比实现的,其代码在这里 (opens new window)
具体如何实现请看注释或参考《Robomaster 开发板 C 型教程》5.6 节,且这个模型中数组color_states[4]
中的三个颜色状态可以随意修改,变为不同的呼吸灯
# 蜂鸣器与舵机
蜂鸣器与舵机也是常见的 PWM 控制元件,其二者也是通过像流水灯一样的 PWM 占空比不断变化产生状态的平滑过渡。
对于蜂鸣器,是音调与响度。其中占空比会改变其音调而脉冲周期(由预分频与重装载数控制)改变其响度
对于舵机,则是在固定的频率(一般为50 Hz)下,通过修改每个周期高电平的时间在0.5ms~2.5ms
之间变化,来控制舵机的桨所在的角度,也就是固定的占空比对应一个不会变化的角度,其对应图如下:
详细的怎样控制蜂鸣器与舵机,请参考《Robomaster 开发板 C 型教程》第五章,其源码在这里:
# 参考
[1] Robomaster 开发板 C 型教程