创建工程
这次因为没有新增功能所以新建功能部分我就先略过了,大家可以参考一下上篇文章,其中我修改Kconfig,开启PWM5 CH1/2/3的部分已经提交PR了,各位之后导入工程时可以先看一下RT-Thread Setting中是否已经显示。
PWM教程之aRGB LED灯调色
aRGB三原色
aRGB 为一种色彩模式,aRGB 分别代表了 alpha(透明度)、Red(红色)、Green(绿色)和Blue(蓝色)四个要素,一般我们给每个要素设置十进制下 0-255 的取值范围,通过 16 进制表示就是 0x00-0xFF,因此一个 aRGB 值可以通过八位十六进制数来描述,从前到后每两位依次对应 a,R,G,B。
在 aRGB 中,alpha 值越大色彩越不透明,RGB 中哪个值越大,对应的色彩就越强。比如纯红色可以用 8 位 16 进制表示为 0xFFFF0000,纯绿色可以表示为 0xFF00FF00,纯蓝色可以表示为 0xFF0000FF,黄色由蓝色和绿色合成,所以可以表示为 0xFF00FFFF。
程序设计
根据上面的描述我们就可以创建一个rt_uint32_t类型变量作为我们的aRGB值。之后每八位分别作为我们的alpha(透明度)、Red(红色)、Green(绿色)和Blue(蓝色)四个要素。我这里为了代码变量的清晰,我定义了一个aRGB结构体,用于装载计算后的aRGB值。大家可以自行尝试一下。
这里三原色的类型为rt_uint16_t的原因之后我们计算过程中需要给它们乘以alpha因此使用rt_uint8_t会溢出。
typedef struct
{
rt_uint8_t alpha; /* 透明度 最大值为FF */
rt_uint16_t red;
rt_uint16_t green;
rt_uint16_t blue;
} aRGB;
下面就是计算函数,这里我们需要把传进来的rt_uint32_t整型进行位运算,将每八位的值移入给我们的结构体成员内。然后用透明度alpha与R、G、B三者依次相乘。
后面我们就可以将R、G、B各值作为脉冲宽度。这里R、G、B三个值计算出来范围是0-65536,我们所以我们后面将PWM周期设置为65536。
static void aRGB_count(aRGB* LED_aRGB, rt_uint32_t set_aRGB)
{
LED_aRGB->alpha = (set_aRGB & 0xFF000000) >> 24;
LED_aRGB->red = ((set_aRGB & 0x00FF0000) >> 16) * LED_aRGB->alpha;
LED_aRGB->green = ((set_aRGB & 0x0000FF00) >> 8) * LED_aRGB->alpha;
LED_aRGB->blue = ((set_aRGB & 0x000000FF) >> 0) * LED_aRGB->alpha;
}
下面就放出完整代码,这里我是自定义了一个MSH命令,输入aRGB_led_sample 0xFF00FF00之类的命令即可设置aRGB值。
用于MSH命令输入的参数都是字符串,所以这里我使用了stroul()函数,这个函数可以将字符串转化为无符号长整型。
/*
Copyright (c) 2006-2021, RT-Thread Development Team
SPDX-License-Identifier: Apache-2.0
Change Logs:
Date Author Notes
2022-12-31 Goldengrandpa the first version
/
#include <rtthread.h>
#include <rtdevice.h>
#include "stdlib.h" / 使用其中的函数:strtoul(); /
#define PWM_DEV_NAME "pwm5" / PWM设备名称 /
#define PWM_DEV_LEDB_CHANNEL 1 / 蓝灯PWM通道 /
#define PWM_DEV_LEDG_CHANNEL 2 / 绿灯PWM通道 /
#define PWM_DEV_LEDR_CHANNEL 3 / 红灯PWM通道 */
struct rt_device_pwm pwm_dev; / PWM设备句柄 /
/aRGB结构体/
typedef struct
{
rt_uint8_t alpha; / 透明度 最大值为FF /
rt_uint16_t red;
rt_uint16_t green;
rt_uint16_t blue;
} aRGB;
aRGB LED_aRGB={0,0,0,0};
static void aRGB_count(aRGB LED_aRGB, rt_uint32_t set_aRGB)
{
LED_aRGB->alpha = (set_aRGB & 0xFF000000) >> 24;
LED_aRGB->red = ((set_aRGB & 0x00FF0000) >> 16) * LED_aRGB->alpha;
LED_aRGB->green = ((set_aRGB & 0x0000FF00) >> 8) * LED_aRGB->alpha;
LED_aRGB->blue = ((set_aRGB & 0x000000FF) >> 0) * LED_aRGB->alpha;
}
static int aRGB_led_sample(int argc, char argv[])
{
/ 查找设备 */
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;
}
rt_uint32_t ui32_aRGB = 0;
/ 用于字符串数据的检测与转换 */
rt_kprintf("input para is: %s %s\n",argv[0],argv[1]);
if(2!=argc)
{
rt_kprintf("error cmd! please input as: (0x7FFFFFFF) \n");
return RT_ERROR;
}
ui32_aRGB=(rt_uint32_t)strtoul(argv[1],NULL,16);/* 将字符串转换为16进制数 */
aRGB_count(&LED_aRGB, ui32_aRGB);
rt_uint32_t period=65536;
rt_pwm_set(pwm_dev, PWM_DEV_LEDB_CHANNEL, period, LED_aRGB.blue);
rt_pwm_set(pwm_dev, PWM_DEV_LEDG_CHANNEL, period, LED_aRGB.green);
rt_pwm_set(pwm_dev, PWM_DEV_LEDR_CHANNEL, period, LED_aRGB.red);
rt_pwm_enable(pwm_dev, PWM_DEV_LEDB_CHANNEL);
rt_pwm_enable(pwm_dev, PWM_DEV_LEDG_CHANNEL);
rt_pwm_enable(pwm_dev, PWM_DEV_LEDR_CHANNEL);
}
MSH_CMD_EXPORT(aRGB_led_sample, pwm aRGB sample)
实验效果如下我们在MSH控制台输入0xFF00FF00,就会亮绿灯。如果输入0x88888888,就会显示如下颜色。
PWM教程之蜂鸣器唱歌
蜂鸣器部分主要参考了RT-Thread文档中心的蜂鸣器播放器,大家有需要的话可以直接去看官方教程,我这里因为篇幅原因会有缩减。
蜂鸣器基础知识
蜂鸣器是一种能够通过电子信号控制的发声器件。在生活中,几乎所有能够发出哔哔响声的电子器件中都装有蜂鸣器。蜂鸣器能够为使用者提供直观的声音信息,是一种常见的人机交互模式。
根据是否内置震荡电路可分为有源蜂鸣器和无源蜂鸣器。有源蜂鸣器只需要提供直流电压就可以通过内部的震荡电路产生震荡电流进而发出声音,而无源蜂鸣器需要输入特定频率的方波才能发出声音。
两者比较起来,有源蜂鸣器的控制更加简单,但是只能发出单一频率的声音,而无源蜂鸣器虽然控制起来比较麻烦,但是可以通过改变输入方波的频率发出不同音调的声音,甚至可以用来演奏乐曲。
这里我们板载的是无源贴片蜂鸣器,这也是我们这个篇章的先决条件。
程序设计
本次我们需要的外设蜂鸣器,查看原理图后发现我们需要的PWM4 CH3已经开好了,我们直接去RT-Thread Setting中开启即可
之后创建beep.c文件,编写蜂鸣器相关代码
/*
Copyright (c) 2006-2021, RT-Thread Development Team
SPDX-License-Identifier: Apache-2.0
Change Logs:
Date Author Notes
2023-01-01 Goldengrandpa the first version
*/
#include "beep.h"
#include <rtdevice.h> //使用 RT-Thread 的设备需要包含此头文件
struct rt_device_pwm pwm_device = RT_NULL; //定义 pwm 设备指针
int beep_init(void)
{
/ 查找PWM设备 /
pwm_device = (struct rt_device_pwm )rt_device_find(BEEP_PWM_DEVICE);
if (pwm_device == RT_NULL)
{
rt_kprintf("pwm device %s not found!\n", BEEP_PWM_DEVICE);
return -RT_ERROR;
}
return 0;
}
int beep_on(void)
{
rt_pwm_enable(pwm_device, BEEP_PWM_CH); //使能蜂鸣器对应的 PWM 通道
return 0;
}
int beep_off(void)
{
rt_pwm_disable(pwm_device, BEEP_PWM_CH); //失能蜂鸣器对应的 PWM 通道
return 0;
}
int beep_set(uint16_t freq, uint8_t volume)
{
rt_uint32_t period, pulse;
/ 将频率转化为周期 周期单位:ns 频率单位:HZ /
period = 1000000000 / freq; //unit:ns 1/HZ10^9 = ns
/ 根据声音大小计算占空比 蜂鸣器低电平触发 /
pulse = period - period / 100 * volume;
/ 利用 PWM API 设定 周期和占空比 */
rt_pwm_set(pwm_device, BEEP_PWM_CH, period, pulse);//channel,period,pulse
return 0;
}
之后我们后面音符频率对周期进行设置,下面程序就实现了播放音符CDEFGAB的功能。
#include <rtthread.h>
#include "beep.h"
uint16_t freq_tab[12] = {262, 277, 294, 311, 330, 349, 369, 392, 415, 440, 466, 494}; //原始频率表 CDEFGAB
uint8_t beep_volume = 3;
int main(void)
{
/* user app entry */
int i;
beep_init();
for (i = 0; i < 12; i++)
{
beep_set(freq_tab[i], beep_volume);
beep_on();
rt_thread_mdelay(500);
beep_off();
rt_thread_mdelay(500);
}
return 0;
}
后面关于音乐数据编码解码方面的知识,我自己也是完全是跟着教程的复刻,所以这里就不进行教程的搬运了,大家自行跟着官方教程吧。
原作者:goldengrandpa