实验环境:
KEIL5、正点原子阿波罗STM32F767开发板
一、灯带介绍
过多介绍我这里不赘述了,想了解详情的可以看看手册,直接开干。
1.1、灯带连接方式
我们只要控制DIN端就好了。
1.2、传输数据方式
简单理解就是单总线发了一串数据,第一个灯拿了第一个字节的数据,第二个灯拿个第二个字节的数据,以此类推。
1.3、控制时序
代码就照这个时序写,因为例程中没有没有纳秒级别的延迟函数,所以我们要根据自己芯片去写。
我用的晶振是216M,一个 __nop() 计算方式为:1 / 216MHz ≈ 0.0046us = 4.6ns 。
根据这个就可以写纳秒(ns)级别的函数了。
二、程序介绍
2.1、WC2812B.c
/************************************************************
@Description: WS2812B全彩LED灯驱动
***********************************************************/
#include "WS2812B.h"
#include "delay.h"
Color_TypeDef PixelBuf[PIXEL_NUM]; //像数数据
uint8_t flag;
Color_TypeDef Color_Prar;
//晶振216M,1/216≈0.004629us=4.6ns
#define delay_4_6ns __nop() //1个nop是4.6ns
#define delay_23ns delay_4_6ns;delay_4_6ns;delay_4_6ns;delay_4_6ns;delay_4_6ns
#define delay_115ns delay_23ns;delay_23ns;delay_23ns;delay_23ns;delay_23ns
//T0H
#define delay_391ns delay_115ns;delay_115ns;delay_115ns;delay_23ns;delay_23ns
//T0L
#define delay_851ns delay_805ns;delay_23ns;delay_23ns
//T1L
#define delay_460ns delay_115ns;delay_115ns;delay_115ns;delay_115ns
//T1H
#define delay_805ns delay_460ns;delay_115ns;delay_115ns;delay_115ns
/*---------------------------------------------------------------------------
@Function :WS2812B_Init
@Description:初始化
----------------------------------------------------------------------------*/
void WS2812B_Init(void)
{
GPIO_InitTypeDef GPIO_Initure;
__HAL_RCC_GPIOB_CLK_ENABLE(); //开启GPIOA时钟
GPIO_Initure.Pin=WS2812B_PIN; //PB1
GPIO_Initure.Mode=GPIO_MODE_OUTPUT_PP; //推挽输入
GPIO_Initure.Pull=GPIO_PULLUP; //上拉
GPIO_Initure.Speed=GPIO_SPEED_HIGH; //高速
HAL_GPIO_Init(WS2812B_PORT,&GPIO_Initure);
HAL_GPIO_WritePin(WS2812B_PORT,WS2812B_PIN,GPIO_PIN_SET); //PB1置1
}
/*---------------------------------------------------------------------------
@Function :WS2812B_Reset
@Description:复位
----------------------------------------------------------------------------*/
void WS2812B_Reset(void) //复位函数
{
WS2812B_Low();
delay_us(300);
}
/*---------------------------------------------------------------------------
@Function :WS2812B_WriteByte
@Description:写一个字节
----------------------------------------------------------------------------*/
void WS2812B_WriteByte(uint8_t dat)
{
u8 i;
for (i=0;i<8;i++)
{
//先发送高位
if (dat & 0x80) //1
{
WS2812B_Hi();
delay_805ns; //T1H
WS2812B_Low();
delay_460ns; //T1L
}
else //0
{
WS2812B_Hi();
delay_391ns; //T0H
WS2812B_Low();
delay_851ns; //T0L
}
dat<<=1;
}
}
/*---------------------------------------------------------------------------
@Function :RGB_LED_Write_24Bits
@Description:写入1个24bit颜色数据
----------------------------------------------------------------------------*/
void RGB_LED_Write_24Bits(uint8_t green, uint8_t red, uint8_t blue)
{
WS2812B_WriteByte(green);
WS2812B_WriteByte(red);
WS2812B_WriteByte(blue);
WS2812B_Reset();
}
/*---------------------------------------------------------------------------
@Function :RGB_LED_Red
@Description:写入1红色数据
@Input :无
@Retrun :无
@Others :
----------------------------------------------------------------------------*/
void RGB_LED_Red(void)
{
uint8_t i;
for (i = 0; i < 1; i++)
{
RGB_LED_Write_24Bits(0, 0xff, 0);
}
}
/*---------------------------------------------------------------------------
@Function :RGB_LED_Green
@Description:写入1绿色数据
----------------------------------------------------------------------------*/
void RGB_LED_Green(void)
{
uint8_t i;
for (i = 0; i < 1; i++)
{
RGB_LED_Write_24Bits(0xff, 0, 0);
}
}
/*---------------------------------------------------------------------------
@Function :RGB_LED_Blue
@Description:写入1蓝色数据
----------------------------------------------------------------------------*/
void RGB_LED_Blue(void)
{
uint8_t i;
for (i = 0; i < 1; i++)
{
RGB_LED_Write_24Bits(0, 0, 0xff);
}
}
/*---------------------------------------------------------------------------
@Function :WS2812B_WriteColor
@Description:写入1个24bit颜色数据
----------------------------------------------------------------------------*/
void WS2812B_WriteColor(Color_TypeDef *pColor)
{
WS2812B_WriteByte(pColor->G);
WS2812B_WriteByte(pColor->R);
WS2812B_WriteByte(pColor->B);
}
/*---------------------------------------------------------------------------
@Function :WS2812B_RefreshPixel
@Description:更新显示
@Input :无
@Retrun :无
@Others :
----------------------------------------------------------------------------*/
void WS2812B_RefreshPixel(void)
{
u8 i;
for(i=0;i<PIXEL_NUM;i++)
{
WS2812B_WriteColor(&PixelBuf[i]);
}
}
//============================================================================
void Copy_Color(Color_TypeDef *pDst,Color_TypeDef *pScr)
{
pDst->R = pScr->R;
pDst->G = pScr->G;
pDst->B = pScr->B;
}
/*---------------------------------------------------------------------------
@Function :WS2812B_FillColor
@Description:填充颜色
@Input :start:开始位置;end:结束信置;pColor:颜色值
@Retrun :无
@Others :
----------------------------------------------------------------------------*/
void WS2812B_FillColor(u16 start,u16 end,Color_TypeDef *pColor)
{
if (start > end) //交换位置
{
u16 temp;
temp = start;
start = end;
end = temp;
}
if (start >= PIXEL_NUM)return; //超出范围
if (end >= PIXEL_NUM)end = PIXEL_NUM-1;
//填充颜色值
while(start <= end)
{
Copy_Color(&PixelBuf[start],pColor);
start++;
}
}
/*---------------------------------------------------------------------------
@Function :WS2812B_All_Blue
@Description:点亮全部灯带显示为蓝色
----------------------------------------------------------------------------*/
void WS2812B_All_Blue(u8 num)
{
Color_Prar.G = 0x00;
Color_Prar.R = 0x00;
Color_Prar.B = 0xff;
WS2812B_FillColor(0,num,&Color_Prar);
}
void WS2812B_All_Red(u8 num) //全红
{
Color_Prar.G = 0x00;
Color_Prar.R = 0xff;
Color_Prar.B = 0x00;
WS2812B_FillColor(0,num,&Color_Prar);
}
void WS2812B_All_Green(u8 num) //全绿
{
Color_Prar.G = 0xff;
Color_Prar.R = 0x00;
Color_Prar.B = 0x00;
WS2812B_FillColor(0,num,&Color_Prar);
}
void WS2812B_All_Yellow(u8 num) //全黄
{
Color_Prar.G = 0xff;
Color_Prar.R = 0xff;
Color_Prar.B = 0x00;
WS2812B_FillColor(0,num,&Color_Prar);
}
2.2、WS2812.h
/************************************************************
@Description: WS2812B全彩LED灯驱动
***********************************************************/
#ifndef __WS2812B_H
#define __WS2812B_H
#include "sys.h"
//WS2812B IO 定义 注意初始化APB时钟也要修改
#define WS2812B_PORT GPIOB
#define WS2812B_PIN GPIO_PIN_1
#define WS2812B_RCC_AHB __HAL_RCC_GPIOB_CLK_ENABLE() //开启GPIOB时钟
#define WS2812B_Hi() HAL_GPIO_WritePin(WS2812B_PORT,WS2812B_PIN,GPIO_PIN_SET)
#define WS2812B_Low() HAL_GPIO_WritePin(WS2812B_PORT,WS2812B_PIN,GPIO_PIN_RESET)
//颜色
typedef struct color{
u8 G;
u8 R;
u8 B;
}Color_TypeDef;
//------------------------
#define PIXEL_NUM 60 //LED灯的个数
void WS2812B_Init(void);
void WS2812B_Reset(void);
void WS2812B_WriteColor(Color_TypeDef *pColor);
void RGB_LED_Write_24Bits(uint8_t green, uint8_t red, uint8_t blue);
void RGB_LED_Red(void);
void RGB_LED_Green(void);
void RGB_LED_Blue(void);
void WS2812B_RefreshPixel(void);
void WS2812B_FillColor(u16 start,u16 end,Color_TypeDef *pColor);
void WS2812B_All_Blue(u8 num);
void WS2812B_All_Red(u8 num);
void WS2812B_All_Green(u8 num);
void WS2812B_All_Yellow(u8 num);
#endif
2.3、KEY.c
#include "key.h"
#include "delay.h"
#include "WS2812B.h"
//按键初始化函数
void KEY_Init(void)
{
GPIO_InitTypeDef GPIO_Initure;
__HAL_RCC_GPIOA_CLK_ENABLE(); //开启GPIOA时钟
__HAL_RCC_GPIOC_CLK_ENABLE(); //开启GPIOC时钟
__HAL_RCC_GPIOH_CLK_ENABLE(); //开启GPIOH时钟
GPIO_Initure.Pin=GPIO_PIN_0; //PA0
GPIO_Initure.Mode=GPIO_MODE_INPUT; //输入
GPIO_Initure.Pull=GPIO_PULLDOWN; //下拉
GPIO_Initure.Speed=GPIO_SPEED_HIGH; //高速
HAL_GPIO_Init(GPIOA,&GPIO_Initure);
GPIO_Initure.Pin=GPIO_PIN_13; //PC13
GPIO_Initure.Mode=GPIO_MODE_INPUT; //输入
GPIO_Initure.Pull=GPIO_PULLUP; //上拉
GPIO_Initure.Speed=GPIO_SPEED_HIGH; //高速
HAL_GPIO_Init(GPIOC,&GPIO_Initure);
GPIO_Initure.Pin=GPIO_PIN_2|GPIO_PIN_3; //PH2,3
HAL_GPIO_Init(GPIOH,&GPIO_Initure);
}
//按键处理函数
//返回按键值
//mode:0,不支持连续按;1,支持连续按;
//0,没有任何按键按下
//1,WKUP按下 WK_UP
//注意此函数有响应优先级,KEY0>KEY1>KEY2>WK_UP!!
u8 KEY_Scan(u8 mode)
{
static u8 key_up=1; //按键松开标志
if(mode==1)key_up=1; //支持连按
if(key_up&&(KEY0==0||KEY1==0||KEY2==0||WK_UP==1))
{
delay_ms(10);
key_up=0;
if(KEY0==0) return KEY0_PRES;
else if(KEY1==0) return KEY1_PRES;
else if(KEY2==0) return KEY2_PRES;
else if(WK_UP==1) return WKUP_PRES;
}else if(KEY0==1&&KEY1==1&&KEY2==1&&WK_UP==0)key_up=1;
return 0; //无按键按下
}
void KEY_LED_Control(void)
{
switch(KEY_Scan(0))
{
case KEY0_PRES:
{
WS2812B_All_Blue(30);
delay_ms(100);
WS2812B_RefreshPixel(); //更新显示
}break;
case KEY1_PRES:
{
WS2812B_All_Red(30);
delay_ms(100);
WS2812B_RefreshPixel(); //更新显示
}break;
case KEY2_PRES:
{
WS2812B_All_Green(30);
delay_ms(100);
WS2812B_RefreshPixel(); //更新显示
}break;
case WKUP_PRES:
{
WS2812B_All_Yellow(30);
delay_ms(100);
WS2812B_RefreshPixel(); //更新显示
}break;
}
}
2.4、KEY.h
#ifndef _KEY_H
#define _KEY_H
#include "sys.h"
#define KEY0 HAL_GPIO_ReadPin(GPIOH,GPIO_PIN_3) //KEY0按键PH3
#define KEY1 HAL_GPIO_ReadPin(GPIOH,GPIO_PIN_2) //KEY1按键PH2
#define KEY2 HAL_GPIO_ReadPin(GPIOC,GPIO_PIN_13) //KEY2按键PC13
#define WK_UP HAL_GPIO_ReadPin(GPIOA,GPIO_PIN_0) //WKUP按键PA0
#define KEY0_PRES 1
#define KEY1_PRES 2
#define KEY2_PRES 3
#define WKUP_PRES 4
void KEY_Init(void);
u8 KEY_Scan(u8 mode);
void KEY_LED_Control(void);
#endif
2.5、main.c
#include "sys.h"
#include "delay.h"
#include "key.h"
#include "WS2812B.h"
int main(void)
{
Cache_Enable(); //打开L1-Cache
HAL_Init(); //初始化HAL库
Stm32_Clock_Init(432,25,2,9); //设置时钟,216Mhz
delay_init(216); //延时初始化
KEY_Init(); //按键初始化
WS2812B_Init(); //灯带控制IO口初始化
while(1)
{
KEY_LED_Control(); //由此按钮函数控制切换红、绿、蓝、黄四种颜色
}
}
遇到的问题
灯带是点亮了,也能控制切换,但是,灯带受外部干扰也很强,不控制的情况下,也可能会不受控制的点亮。
文章参考:
闲来无事,搞了条WS2812B全彩灯带玩一下,驱动程序借助以下两篇文章的大佬的程序修改。
https://blog.csdn.net/Lennon8_8/article/details/108980808
https://blog.csdn.net/weixin_53411486/article/details/127246513
做实验前结合这两位仁兄的贴子,感谢两位大佬。文章来源:https://www.toymoban.com/news/detail-480018.html
附带代码连接
有遇到相同实验环境的情况的伙伴,也可以直接使用这份代码实验一下。
https://download.csdn.net/download/weixin_41569625/87452923文章来源地址https://www.toymoban.com/news/detail-480018.html
到了这里,关于WS2812B灯带驱动实验的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!