描述
这篇文章延续上一篇文件,上一篇贴出了我写的两个源码文件,这一篇会介绍一下源码的注释,并介绍如何进行代码移植
- 研发平台:淘宝上购买的野火店铺的一款单片机,名字叫骄阳开发板,核心芯片是stm32F407IGT6
- 灯带:WS2812型号的灯带
在这一篇文章的工作之前,我们已经通过上一篇文章,成功的配置了PWM管脚,并成功构建了整个工程。
代码移植
- 将
ws2812.c
文件添加到整个工程中。添加方式:打开你的工程,右侧有整个工程的文件树,右键“Application/User/Core”选择“Add Existing Files to Group Application/..” - 将
ws2812.h
文件所在的文件夹路径,以添加include路径的方式添加到整个工程中。选择上方菜单栏的魔法棒,左键点击后,选择上方的“C/C++”一栏,出现以下界面 -
在以上界面中,找到“Include Path”一项,后方跟着一个小“…”按钮,点击这个按钮,可以进到以下界面。
-
我们在这个界面,点击长得像虚线框的那个按钮,会出现一个新行,在新行的右侧再次出现“…”按钮,我们点击这个按钮,选择
ws2812.h
的所在路径,点击确定。以下截图是我添加后的样子,我将ws2812.h
放在了Core文件夹下,以“fuxi”为文件夹名。 - 以上我们完成了路径的配置,接下来就是添加代码了
main.c
中的include代码部分添加#include "ws2812.h"
main.c
中的while循环开始之前添加以下代码
led_init();
int direction = 6;
uint8_t g_value_r[LED_NUM] = {0xFF};
uint8_t g_value_b[LED_NUM] = {0x00};
uint8_t g_value_g[LED_NUM] = {0x00};
for (uint8_t i = 0 ; i < LED_NUM; i++)
{
led_set(i, g_value_r[i], g_value_g[i], g_value_b[i]);
}
uint8_t g_value_delta = 0x08;
main.c
中的while循环中添加以下代码
for (uint8_t i = 0 ; i < LED_NUM; i++)
{
led_set(i, g_value_r[i], g_value_g[i], g_value_b[i]);
}
led_on();
HAL_Delay(100);
if (((g_value_r[0] >= 0xFF - g_value_delta) || (g_value_b[0] <= g_value_delta/2) )&& g_value_g[0] == 0x00 && direction == 6)
{
direction = 1;
g_value_r[0] = 0xFF;
g_value_g[0] = 0x00;
g_value_b[0] = 0x00;
}
else if (g_value_r[0] == 0xFF && g_value_g[0] >= 0xFF - g_value_delta && g_value_b[0] == 0x00 && direction == 1)
{
g_value_g[0] = 0xFF;
direction = 2;
}
else if (g_value_r[0] <= g_value_delta && g_value_g[0] == 0xff && g_value_b[0] == 0x00 && direction == 2)
{
g_value_r[0] = 0x00;
direction = 3;
}
else if (g_value_r[0] == 0x00 && g_value_g[0] == 0xff && g_value_b[0] >= (0xFF - g_value_delta) && direction == 3)
{
g_value_b[0] = 0xff;
direction = 4;
}
else if (g_value_r[0] == 0x00 && g_value_g[0] <= g_value_delta && g_value_b[0] == 0xff && direction == 4)
{
g_value_g[0] = 0x00;
direction = 5;
}
else if (g_value_r[0] >= (0x8B - g_value_delta) && g_value_g[0] == 0x00 && g_value_b[0] == 0xff && direction == 5)
{
direction = 6;
g_value_r[0] = 0x8B;
g_value_g[0] = 0x00;
g_value_b[0] = 0xFF;
}
if (direction == 1)
{
g_value_g[0] = g_value_g[0] + g_value_delta;
}
else if (direction == 2)
{
g_value_r[0] = g_value_r[0] - g_value_delta;
}
else if (direction == 3)
{
g_value_b[0] = g_value_b[0] + g_value_delta;
}
else if (direction == 4)
{
g_value_g[0] = g_value_g[0] - g_value_delta;
}
else if (direction == 5)
{
g_value_r[0] = g_value_r[0] + g_value_delta;
}
else if (direction == 6)
{
g_value_r[0] = g_value_r[0] + g_value_delta/2;
g_value_b[0] = g_value_b[0] - g_value_delta;
}
for (int i = 29 ; i >0; i-- )
{
g_value_r[i] = g_value_r[i-1];
g_value_g[i] = g_value_g[i-1];
g_value_b[i] = g_value_b[i-1];
}
- 以下截图是添加代码的具体位置,可以用于参考。
ws2812.h
和ws2812.c
文件,我在上一篇文章中写了,可以参考。- 以上就是全部的代码移植过程,依次完成以上的步骤,我们就可以成功的让灯带进行跑马灯的效果了
代码详解
在这里我们来对之前的代码进行一定的剖析。其中,我们只对一些核心的代码进行了解释。
ws2812.h
#define ONE_PULSE 140
#define ZERO_PULSE 70
#define LED_NUM 30
#define LED_DATA_LEN 24
#define RESET_PULSE_LEN 80
#define DATA_BUFFER_LEN RESET_PULSE_LEN+(LED_DATA_LEN*LED_NUM)
-
上一篇我们设置了重装载数是209,显然PWM波的频率为800kHz。什么意思呢,我们在1.25us中计算了210个重载值。
根据之前的ws2812知识的分析,0码的高电平时间为1/3,1码的高电平时间为2/3。显然210的1/3就是70,210的2/3就是140。这就是ZERO_PULSE
和ONE_PULSE
的含义 -
LED_NUM
代表的是我使用的灯带所含的led灯的个数,LED_DATA_LEN
是每个灯RGB颜色的24位数据,RESET_PULSE_LEN
代表的就是最开始的Trst时间,这里我们设置了80个码元周期,1.25us就是100us的数据reset时间。DATA_BUFFER_LEN
就是上面三个参数形成的灯带控制数据总长度 -
ws2812.h
文件中的所有参数,你都可以根据自己的实际需要进行相应的调整
ws2812.c
该文件有两个核心函数,分别进行解释
void led_set(uint8_t led_id, uint8_t value_r, uint8_t value_g, uint8_t value_b)
{
uint16_t* p = (data_buffur + RESET_PULSE_LEN) + (LED_DATA_LEN * led_id);
for (uint16_t i = 0; i < 8; i++)
{
p[i] = (value_g << i) & (0x80)? ONE_PULSE: ZERO_PULSE;
p[i + 8] = (value_r << i) & (0x80)? ONE_PULSE: ZERO_PULSE;
p[i + 16] = (value_b << i) & (0x80)? ONE_PULSE: ZERO_PULSE;
}
}
这个函数的功能:将整个控制数据中,属于第i个led灯的24位RGB数据进行赋值
这之中最核心的一句话p[i] = (value_g << i) & (0x80)? ONE_PULSE: ZERO_PULSE;
什么意思呢,value_g代表绿颜色的通道值,是一个uint8_t型的数。我们假设它是255,把它写成二进制就是11111111,(value_g << i) & (0x80)
代表左移1位后与二进制数0x80进行按位与运算(每个位上都是1才是1),如果最高位是1的话就赋值为140,否则就赋值为70。
简单来说,这就是一句能将uint8_t数据类型很简单的变成二进制的一句话
void led_on(void)
{
HAL_TIM_PWM_Start_DMA(&htim1, TIM_CHANNEL_1, (uint32_t *)data_buffur, DATA_BUFFER_LEN);
}
这个函数就一句话,就是将已经准备好的数据,用TIM1_CH1管脚发出去。我们将这个函数命名成了“开灯”
main.c
简单来说,我将30个灯分开设置颜色,每个灯的颜色是不一样且相邻的,从红、橙…到紫再从红重新开始,形成一个跑马灯的效果
主函数的核心部分是以下
for (uint8_t i = 0 ; i < LED_NUM; i++)
{
led_set(i, g_value_r[i], g_value_g[i], g_value_b[i]);
}
led_on();
将每一个灯分别设置为不同的颜色,最后将数据一起发出去。设置的部分就是后面的一大部分代码,实现跑马灯的效果,简单来说就是如下的样子:
灯号1、2、3、4、5、6、7……30
第一个时间点它们发出的颜色:
颜色a、b、c、d、e、f、g……#、@、%
第二个时间点它们发出的颜色:
颜色Z、a、b、c、d、e、f、g……#、@
第三个时间点它们发出的颜色:
颜色Y、Z、a、b、c、d、e、f、g……#
彩虹每个颜色对应的RGB颜色是这样的
16进制码:
红色 #FF0000
橙色 #FF7F00
黄色 #FFFF00
绿色 #00FF00
青色 #00FFFF
蓝色 #0000FF
紫色 #8B00FF
我的代码中,我使用用direction来表明当前灯的颜色究竟是从哪个颜色转变成哪个颜色,比如direction==1
我用来表示从红色到黄色的过程、direction==2
我用来表示从黄色到绿色的过程,g_value_delta
用来表示两个相邻灯的颜色差值。这就是整个代码的逻辑和思路,我就不具体将我的具体代码了,代码很不优雅,但我没有什么时间用在这上面啦哈哈。当然你有更简单的方法去实现同样的效果,写好后可以发给我,我来学习学习哈哈。
总结
从D4.1到D4.3的三篇文章,我们讲解了WS2812灯带的控制过程,完成了利用CubeMX设置的学习。最终还贴上了可以随时使用的原创代码
评论(3)
您还未登录,请登录后发表或查看评论