今天我们用STM32G070RB NUCLEO开发板 和WS2812灯带制作一个七彩星空灯。
《工作原理》
1, 上电时,WS2812数据IO为低电平保持。
2, 空闲时,IO为低电平。
3, 数据发送完毕后,保持电平,超过规格书上定义的RESET时间(只有低电平时间超过280us,就可以认为是RESET.)
先看下数据的时序
如下是每24bit的组成。注意,顺序不是RGB888,而是GRB888。一般我们取颜色的数值,都是RGB顺序,所以这里在代码里实现的时候,会需要做一下移位。另外,需要注意的是,需要高位先发(MSB)。
时序波形图。关键在于用什么方法去表示Bit的波形,网络上的方法有很多。例如PWM,也有用SPI。今天我们就先用一种简单方法实行吧,IO口模拟是不错的选择。
《新建工程》
1、万事从新建工程开始,打开STM32CubeMX
2、 在搜索框内搜索 我们的开发板型号,也就是STM32G070RB,好的这样工程就新建好的,
3、配置时钟树,如下图所示。
4、接下来配置引脚 ,这里我们采用的是PWM+DMA的方式来驱动WS2812,通过WS2812的手册可以得知驱动需要800KHZ的频率好的我们现在来配置定时器,这里以定时器1为例来配置。如下图所示。计算方法79=(64M/800K)-1得出。下面的Pulse是指一个周期的脉冲数
5、下面我们开始配置DMA,如下图所示配置
6、好的到这里就可以生成代码
好的到这里我们就已经把我们需要用到的资源初始化完成了
《开始写代码》
1、打开我们上次教程生成的代码;我们打开工程,将一下代码 复制到下图所在位置
uint16_t static RGB_buffur[RESET_PULSE + WS2812_DATA_LEN] = { 0 };
2、接下来就是DMA传输完成回调函数(根据你使用的定时器配置),以下函数都复制到main.c 的/* USER CODE BEGIN 4 */代码区
void HAL_TIM_PWM_PulseFinishedCallback(TIM_HandleTypeDef *htim)
{
HAL_TIM_PWM_Stop_DMA(&htim1,TIM_CHANNEL_1);
}
3、下面就是今天的最后一步WS2812的驱动函数了,以下函数的作用是根据WS2812的数量将灯的GRB颜色数据写到需要DMA传送的数组中
void WS281x_SetPixelColor(uint16_t n, uint32_t GRBColor)
{
uint8_t i;
if (n < LED_NUMS)
{
for (i = 0; i < 24; ++i)
RGB_buffur[24 * n + i] = (((GRBColor << i) & 0X800000) ? ONE_PULSE : ZERO_PULSE);
}
}
4、将三个颜色的数据合并成GRB数据
uint32_t WS281x_Color(uint8_t red, uint8_t green, uint8_t blue)
{
return green << 16 | red << 8 | blue;
}
5、这是一个简单的颜色渐变算法 ,感兴趣的可以研究研究
uint32_t Wheel(uint8_t WheelPos)
{
WheelPos = 255 - WheelPos;
if (WheelPos < 85)
{
return WS281x_Color(255 - WheelPos * 3, 0, WheelPos * 3);
}
if (WheelPos < 170)
{
WheelPos -= 85;
return WS281x_Color(0, WheelPos * 3, 255 - WheelPos * 3);
}
WheelPos -= 170;
return WS281x_Color(WheelPos * 3, 255 - WheelPos * 3, 0);
}
6、这里我简单的写了两个演示程序
void Mode2_LED(uint8_t wait)
{
uint32_t timestamp = HAL_GetTick();
uint16_t i;
static uint8_t j;
static uint32_t next_time = 0;
uint32_t flag = 0;
if (next_time < wait)
{
if ((uint64_t)timestamp + wait - next_time > 0)
flag = 1;
}
else if (timestamp > next_time)
{
flag = 1;
}
if (flag)
{
j++;
next_time = timestamp + wait;
for (i = 0; i < LED_NUMS; i++)
{
WS281x_SetPixelColor(i, Wheel((i + j) & 255));
}
}
HAL_TIM_PWM_Start_DMA(&htim1,TIM_CHANNEL_1,(uint32_t *)RGB_buffur,RESET_PULSE + WS2812_DATA_LEN);
}
void Mode1_LED(uint8_t wait)
{
uint32_t timestamp = HAL_GetTick();
uint16_t i;
static uint8_t j;
static uint32_t next_time = 0;
static uint8_t loop = 0;
if (loop == 0)
next_time = timestamp;
loop = 1; //首次调用初始化
if ((timestamp > next_time)) // && (timestamp - next_time < wait*5))
{
j++;
next_time = timestamp + wait;
for (i = 0; i < LED_NUMS; i++)
{
WS281x_SetPixelColor(i, Wheel(((i * 256 / (LED_NUMS)) + j) & 255));
}
}
HAL_TIM_PWM_Start_DMA(&htim1,TIM_CHANNEL_1,(uint32_t *)RGB_buffur,RESET_PULSE + WS2812_DATA_LEN);
}
7、在主函数中直接调用Mode1_LED和Mode2_LED函数即可。
OK 到这里就结束了,点亮之后相当炫酷。,大家可以借鉴 ,修改出自己独特的风格。
审核编辑 :李倩
全部0条评论
快来发表一下你的评论吧 !