0
  • 聊天消息
  • 系统消息
  • 评论与回复
登录后你可以
  • 下载海量资料
  • 学习在线课程
  • 观看技术视频
  • 写文章/发帖/加入社区
会员中心
创作中心

完善资料让更多小伙伴认识你,还能领取20积分哦,立即完善>

3天内不再提示

使用STM32CubeMx驱动WS2812B实现幻彩灯

CHANBAEK 来源:小李的创客实验室 作者:小李的创客实验室 2023-08-21 11:46 次阅读

STM32CubeMx驱动WS2812B实现幻彩(超详)

1.创建基于STM32F03C8T6工程

1.1配置时钟

  • 选择外部高速时钟源HSE

图片
在这里插入图片描述

1.2配置系统时钟树使其达到最大时钟72MHz(最大系统时钟)

图片
在这里插入图片描述

由时钟树可以知道APB1上定时器时钟频率是72MHz,实验使用的硬件接的是PA2,用的定时器TIM2_CH3, 查阅数据手册可知

TIM2外设在APB1上所以TIM2的定时器频率是72MHz。

图片
在这里插入图片描述

图片
在这里插入图片描述

3.理论分析

之前写过一篇文章,文章链接在此:创客实验第一弹之驱动WS2812B彩灯;不了解的可以直接跳转,今天主要讲解如何使用STM32CubeMx驱动WS2812B实现幻彩,这里做部分内容截取,完整可以跳转文章阅读。

3.1WS2812B的逻辑“1”和逻辑“0”

图片
图片

由上图可知“0”码和“1”码都是既有高电平又有低电平不过高电平和低电平的比例不同,这点很好理解,重点是分析一下它的特点,首先直观的特点就是编码“0”的电平高电平时间短一些低电平时间长一些,这也恰好符合我们的逻辑毕竟它还是低电平多一些的,编码“1”的电平高电平时间就长一些,而低电平就短一些。

但是不管是高电平还是低电平他们占用整个时间长度是一样的,这里还有一个很长的低电平这个代表复位信号

3.2WS2812B控制波形的精准描述如何?

这里涉及到严格的数学描述了,长一点是多长?短一点是多短?这个肯定是有标准或者是约束的

图片
图片

理论上来说,高电平时长和低电平时长加起来应该是0.4us+0.85us或者0.85us+0.4us也就是说总共要占用1.25us的时间才可以编码出来一个“0”或者“1”出来。复位是要求50us以上,显然是要比编码的0或者1占用的时间要多的。

当然既然是电路的高低电平时长就会引入误差这个在误差允许的范围内我们可以接受,这个范围就是上下不超过150ns这里是ns比us还要小的时间,这个其实时间要求还是很严格的。

3.3分析结果

定时器主频是72MHz 要想实现1.25us 周期的波形也就是频率是800KHz波形我们可以采用分频为1分频,计数值为90来实现具体原因如下

  • PWM频率计算公式:

Fpwm =Tclk / ((arr+1)*(psc+1))(单位:Hz)

上面提到数据传送频率为800KHz,TCLK为72Mhz,我们这里设置psc = 0,arr= 89,得到频率刚好为800KHz,也就是计数器计数90个数,每个数计数时间是1/72us,1/72us*90 = 1.25us;

通过控制PWM占空比发送0码和1码,额定周期为1.25us,则频率为800Khz

  • 0码占空比计算

(0码高电平时间)/(周期)---> 0.4us / 1.25 us = 0.32

用占空比乘以定时器重装值加一就是0码的CCR值(代表PWM高电平计数个数)--->

0.32 * (90) = 28.8(取28,网上网友实测不可以高于28,但23到28都可以,我这里选28)

  • 1码占空比计算
    同理计算:(1码高电平时间)/ (周期)---> 0.8 / 1.25 = 0.64
    (占空比)*(90)= CCR ---> 0.64 * 90 = 57.6(取57左右即可这里取58)

4.配置定时器的参数

图片
在这里插入图片描述

5.WS2812B灯带的数据是什么样的呢?

图片
图片

在说数据格式之前先来补充一下关于色彩的知识点,就是三原色,红绿蓝,也就是我们常说的RGB,R就是RED,G就是GREEN,B就是BLUE,一个彩色可以用这三种颜色的比例来混合出来。

每一个LED的R、G、B分别由八位数据控制颜色浓度,(每种颜色浓度有0~255档,理论上RGB就可以组成256的3次方中颜色组合)即每个LED需要24BIT数据,那么需要发送数据的总长度则为(要控制LED数量 n)*(24),每个LED保存24BIT将剩余位传给后面LED。全部数据发送完成后要继续发送大于24us的低电平作为RESET_CODE等才可以点亮。

既然是24bit数据代表三种颜色,我们就要首先知道一个bit的意义是什么,我们传统意义上来说1个bit代表一个数据位,但是对于数据位bit的理解好像就是“1”或者“0”在数电里我们很容易把高低电平跟逻辑1和逻辑0对应起来,但是表示灯珠的逻辑电平不是简单的高低电平。在数值上0xFFFFFF就是24bit的1,0x000000就是24bit的0.这里有8个bit代表颜色G分量,G7G6G5G4G3G2G1G0,有8个bit代表R分量R7R6R5R4R3R2R1R0,有8个bit代表B分量B7B6B5B4B3B2B1B0,当不同分量组合时候就会有不同的数据产生,这个数据背后其实是逻辑电平,这里要说明的是彩灯的逻辑“1”并不是简简单单的高电平,彩灯的逻辑“0”也不是简简单单的低电平。而是上面分析的占空比不同的PWM波形。

4.配置定时器的DMA参数

这里解释下为什么要就一颗灯也要用DMA,这里仅仅是拿一个灯做,其实多个灯串起来数据量还是很大的使用DMA才可以充分释放其性能优势。

  • 内存向外设传输
  • 内存递增递增
  • DMA普通模式
  • 32为字长传输

图片
在这里插入图片描述

4.生产代码修改完善

生成工程配置可参考上篇博客

STM32CubeMx定时器编码器模式(超详)

4.1添加自定义ws2812b.c文件跟ws2812b.h文件

图片
在这里插入图片描述

图片
在这里插入图片描述

4.2修改自定义ws2812b.c文件跟ws2812b.h文件

  • ws2812.c文件内容
#include "ws2812b.h"
#include "tim.h"

/*Some Static Colors------------------------------*/
const RGB_Color_TypeDef RED      = {255,0,0};   //显示红色RGB数据
const RGB_Color_TypeDef ORANGE   = {127,106,0};
const RGB_Color_TypeDef YELLOW   = {127,216,0};
const RGB_Color_TypeDef GREEN    = {0,255,0};
const RGB_Color_TypeDef CYAN	   = {0,255,255};
const RGB_Color_TypeDef BLUE     = {0,0,255};
const RGB_Color_TypeDef PURPLE	 = {238,130,238};
const RGB_Color_TypeDef BLACK    = {0,0,0};
const RGB_Color_TypeDef WHITE    = {255,255,255};
const RGB_Color_TypeDef MAGENTA  = {255,0,220};


/*二维数组存放最终PWM输出数组,每一行24个
数据代表一个LED,最后一行24个0代表RESET码*/
uint32_t Pixel_Buf[LED_NUM+1][24];       

/*
功能:设定单个RGB LED的颜色,把结构体中RGB的24BIT转换为0码和1码
参数:LedId为LED序号,Color:定义的颜色结构体
*/
void RGB_SetColor(uint8_t LedId,RGB_Color_TypeDef Color)
{
	uint8_t i; 
	if(LedId > LED_NUM)return; //avoid overflow 防止写入ID大于LED总数
	
	for(i=0;i< 8;i++)   Pixel_Buf[LedId][i]   = ( (Color.G & (1 < < (7 -i)))? (CODE_1):CODE_0);//数组某一行0~7转化存放G
	for(i=8;i< 16;i++)  Pixel_Buf[LedId][i]   = ( (Color.R & (1 < < (15-i)))? (CODE_1):CODE_0);//数组某一行8~15转化存放R
	for(i=16;i< 24;i++) Pixel_Buf[LedId][i]   = ( (Color.B & (1 < < (23-i)))? (CODE_1):CODE_0);//数组某一行16~23转化存放B
}

/*
功能:最后一行装在24个0,输出24个周期占空比为0的PWM波,作为最后reset延时,这里总时长为24*1.2=30us > 24us(要求大于24us)
*/
void Reset_Load(void)
{
	uint8_t i;
	for(i=0;i< 24;i++)
	{
		Pixel_Buf[LED_NUM][i] = 0;
	}
}

/*
功能:发送数组
参数:(&htim1)定时器1,(TIM_CHANNEL_2)通道2,((uint32_t *)Pixel_Buf)待发送数组,
			(Pixel_NUM+1)*24)发送个数,数组行列相乘
*/
void RGB_SendArray(void)
{
	HAL_TIM_PWM_Start_DMA(&htim2, TIM_CHANNEL_3, (uint32_t *)Pixel_Buf,(LED_NUM+1)*24);
}

/*
功能:显示红色
参数:Pixel_Len为显示LED个数
*/
void RGB_RED(uint16_t Pixel_Len)
{
	uint16_t i;
	for(i=0;i< Pixel_Len;i++)//给对应个数LED写入红色
	{
		RGB_SetColor(i,RED);
	}
	Reset_Load();
	RGB_SendArray();
}

/*
功能:显示绿色
参数:Pixel_Len为显示LED个数
*/
void RGB_GREEN(uint16_t Pixel_Len)
{
	uint16_t i;
	for(i=0;i< Pixel_Len;i++)//给对应个数LED写入绿色
	{
		RGB_SetColor(i,GREEN);
	}
	Reset_Load();
	RGB_SendArray();
}

/*
功能:显示蓝色
参数:Pixel_Len为显示LED个数
*/
void RGB_BLUE(uint16_t Pixel_Len)
{
	uint16_t i;
	for(i=0;i< Pixel_Len;i++)//给对应个数LED写入蓝色
	{
		RGB_SetColor(i,BLUE);
	}
	Reset_Load();
	RGB_SendArray();
}

/*
功能:显示白色
参数:Pixel_Len为显示LED个数
*/
void RGB_WHITE(uint16_t Pixel_Len)
{
	uint16_t i;
	for(i=0;i< Pixel_Len;i++)//给对应个数LED写入白色
	{
		RGB_SetColor(i,WHITE);
	}
	Reset_Load();
	RGB_SendArray();
}

//用户自定义API接口可根据实际拓展

/*******************************************************************************/
/*									添加部分									   */

//显示指定颜色
static void rgb_show(uint32_t Pixel_Len, RGB_Color_TypeDef rgb)
{
	uint16_t i;
	for(i=0;i< Pixel_Len;i++)
	{
		RGB_SetColor(i,rgb);
	}
	Reset_Load();
	RGB_SendArray();
}

//颜色循环转换
static RGB_Color_TypeDef Wheel(uint8_t WheelPos)
{
	RGB_Color_TypeDef rgb;
	WheelPos = 255 - WheelPos;
	if (WheelPos < 85)
	{
		rgb.R = 255 - WheelPos * 3;
		rgb.G = 0;
		rgb.B = WheelPos * 3;
		return rgb;
	}
	if (WheelPos < 170)
	{
		WheelPos -= 85;
		rgb.R = 0;
		rgb.G = WheelPos * 3;
		rgb.B = 255 - WheelPos * 3;
		return rgb;
	}
	WheelPos -= 170;
	rgb.R = WheelPos * 3;
	rgb.G = 255 - WheelPos * 3;
	rgb.B = 0;
	return rgb;
}

//彩虹呼吸灯
static void rainbow(uint8_t wait)
{
	uint32_t timestamp = HAL_GetTick();
	uint16_t i;
	staticuint8_t j;
	staticuint32_t next_time = 0;

	uint32_t flag = 0;
	if (next_time < wait)
	{
		if ((uint64_t)timestamp + wait - next_time > 0)
			flag = 1;
	}
	elseif (timestamp > next_time)
	{
		flag = 1;
	}
	if (flag)    // && (timestamp - next_time < wait*5))
	{
		j++;
		next_time = timestamp + wait;
		for (i = 0; i < LED_NUM; i++)
		{
			RGB_SetColor(i, Wheel((i + j) & 255));
		}
	}
	RGB_SendArray();
}

//彩虹灯旋转
static void rainbowCycle(uint8_t wait)
{
	uint32_t timestamp = HAL_GetTick();
	uint16_t i;
	staticuint8_t j;
	staticuint32_t next_time = 0;

	staticuint8_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_NUM; i++)
		{
			RGB_SetColor(i, Wheel(((i * 256 / (LED_NUM)) + j) & 255));
		}
	}
	RGB_SendArray();
}
  • ws2812.h文件内容
#ifndef _WS2812B_H_
#define _WS2812B_H_

#include "main.h"

/*这里是上文计算所得CCR的宏定义*/
#define CODE_1       (58)       //1码定时器计数次数
#define CODE_0       (28)       //0码定时器计数次数

/*建立一个定义单个LED三原色值大小的结构体*/
typedefstruct
{
	uint8_t R;
	uint8_t G;
	uint8_t B;
}RGB_Color_TypeDef;

#define LED_NUM 1                            //LED数量宏定义,这里我使用一个LED

void RGB_SetColor(uint8_t LedId,RGB_Color_TypeDef Color);//给一个LED装载24个颜色数据码(0码和1码)
void Reset_Load(void);             //该函数用于将数组最后24个数据变为0,代表RESET_code
void RGB_SendArray(void);          //发送最终数组
void RGB_RED(uint16_t Pixel_Len);  //显示红灯
void RGB_GREEN(uint16_t Pixel_Len);//显示绿灯
void RGB_BLUE(uint16_t Pixel_Len); //显示蓝灯
void RGB_WHITE(uint16_t Pixel_Len);//显示白灯


#endif

4.3修改主函数实现测试功能

/* USER CODE BEGIN Includes */
#include "ws2812b.h"
/* USER CODE END Includes */


/* Infinite loop */
  /* USER CODE BEGIN WHILE */
  while (1)
  {
    RGB_RED(1);
    HAL_Delay(1000);
    RGB_GREEN(1);
    HAL_Delay(1000);
    RGB_BLUE(1);
    HAL_Delay(1000);
    RGB_WHITE(1);
    HAL_Delay(1000);
    /* USER CODE END WHILE */

    /* USER CODE BEGIN 3 */
  }
  /* USER CODE END 3 */

5.下载验证

颜色好像有点色差,基本没啥问题。OK 到这如何使用STM32CubeMx驱动WS2812B实现幻彩基本搞定,后面有机会再更新幻彩灯的实现部分。

声明:本文内容及配图由入驻作者撰写或者入驻合作网站授权转载。文章观点仅代表作者本人,不代表电子发烧友网立场。文章及其配图仅供工程师学习之用,如有内容侵权或者其他违规问题,请联系本站处理。 举报投诉
  • PWM
    PWM
    +关注

    关注

    114

    文章

    5123

    浏览量

    213253
  • 定时器
    +关注

    关注

    23

    文章

    3234

    浏览量

    114371
  • 时钟树
    +关注

    关注

    0

    文章

    53

    浏览量

    10729
  • stm32cubemx
    +关注

    关注

    5

    文章

    280

    浏览量

    14735
  • WS2812B
    +关注

    关注

    1

    文章

    38

    浏览量

    2141
收藏 人收藏

    评论

    相关推荐

    什么是WS2812BWS2812B灯带是怎么连接的呢?

    WS2812B是一款全彩LED控制IC,单总线控制,何为单总线我的理解就是数据线在一根线上传输的控制方式就是单总线。
    的头像 发表于 07-08 14:53 9985次阅读
    什么是<b class='flag-5'>WS2812B</b>?<b class='flag-5'>WS2812B</b>灯带是怎么连接的呢?

    哪位大神有完整的STM32F205驱动WS2812B灯带的程序

    哪位大神有完整的STM32F205驱动WS2812B灯带的程序
    发表于 09-03 18:08

    WS2812B RGB全彩LED灯珠

    WS2812B RGB全彩LED灯珠,只需通过一根信号线控制多个灯显示,本教程使用X-CTR100控制器驱动WS2812灯环。原理WS2812B是一个集控制电路与发光电路于一体的智能外
    发表于 08-06 08:15

    怎样给WS2812B发送数据呢

    1. 对于写驱动来说WS2812B芯片手册主要就是看通讯。简单来说就是给WS2812B发送数据,数据就是RGB对应的值,先发高位,按照GRB的顺序发送数据。通常我们用高电平表示数字信号“1”,低电平
    发表于 02-22 06:51

    怎样使用PWM+DMA的方式去驱动WS2812B

    PWM+DMA 驱动 WS2812一、项目要求(主要目的)使用 PWM+DMA 的方式驱动 WS2812B, 并每隔 800ms 随机显示不同颜色。(附加目的)探索一下
    发表于 02-22 06:42

    WS2812B RGB 彩灯 51单片机 STC15W 驱动代码

    WS2812B RGB 彩灯 51单片机 STC15W 驱动代码
    发表于 04-04 15:29

    基于GD32F427开发板点亮WS2812B彩灯环的设计实现

    1、如何点亮WS2812B彩灯环呢  我有一个WS2812B彩灯环,搭配精选的背景  在玩过的板子上,我都要把它点亮。  关于WS2812B
    发表于 11-25 15:51

    使用ESP8266驱动WS2812B型RGB LED简单3步打造智能灯

    使用ESP8266驱动WS2812B型RGB LED简单3步打造智能灯
    发表于 03-26 17:15 140次下载

    【有趣的全彩LED | 编程】用STM32 HAL库让WS2812B为你所动

    【有趣的全彩LED | 编程】用STM32 HAL库让WS2812B为你所动
    发表于 12-05 15:06 35次下载
    【有趣的全彩LED | 编程】用<b class='flag-5'>STM32</b> HAL库让<b class='flag-5'>WS2812B</b>为你所动

    STM32F1 DMA+PWM 控制WS2812B LED灯

    1. 对于写驱动来说WS2812B芯片手册主要就是看通讯。简单来说就是给WS2812B发送数据,数据就是RGB对应的值,先发高位,按照GRB的顺序发送数据。通常我们用高电平表示数字信号“1”,低电平
    发表于 12-28 19:11 76次下载
    <b class='flag-5'>STM32</b>F1 DMA+PWM 控制<b class='flag-5'>WS2812B</b> LED灯

    Arduino WS2812B LED灯带设计方案

    Arduino WS2812B LED灯带可单独控制每个LED的亮度和颜色,提供了一种简单而酷炫的LED效果体验。
    发表于 04-24 08:36 1.2w次阅读
    Arduino <b class='flag-5'>WS2812B</b> LED灯带设计方案

    WS2812B远程区域面板控制器

    电子发烧友网站提供《WS2812B远程区域面板控制器.zip》资料免费下载
    发表于 07-18 09:07 3次下载
    <b class='flag-5'>WS2812B</b>远程区域面板控制器

    ESP32 WS2812b分线板开源分享

    电子发烧友网站提供《ESP32 WS2812b分线板开源分享.zip》资料免费下载
    发表于 07-29 10:04 3次下载
    ESP32 <b class='flag-5'>WS2812b</b>分线板开源分享

    WS2812B遥控灯控制器

    电子发烧友网站提供《WS2812B遥控灯控制器.zip》资料免费下载
    发表于 11-25 09:16 8次下载
    <b class='flag-5'>WS2812B</b>遥控灯控制器

    适用于Azure Sphere的WS2812B LED灯带驱动

    电子发烧友网站提供《适用于Azure Sphere的WS2812B LED灯带驱动器.zip》资料免费下载
    发表于 12-06 14:42 0次下载
    适用于Azure Sphere的<b class='flag-5'>WS2812B</b> LED灯带<b class='flag-5'>驱动</b>器