初出茅庐的小李博客之STM32CubeMx驱动WS2812B实现幻彩(超详)

news2024/11/23 7:39:02

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还要小的时间,这个其实时间要求还是很严格的。

1.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.生产代码修改完善

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

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;
	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)    // && (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;
	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_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三原色值大小的结构体*/
typedef struct
{
	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实现幻彩基本搞定,后面有机会再更新幻彩灯的实现部分。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/904785.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

人脸老化预测(Python)

本次项目的文件 main.py主程序如下 导入必要的库和模块&#xff1a; 导入 TensorFlow 库以及自定义的 FaceAging 模块。导入操作系统库和参数解析库。 定义 str2bool 函数&#xff1a; 自定义函数用于将字符串转换为布尔值。 创建命令行参数解析器&#xff1a; 使用 argparse.A…

linux驱动学习3-外部中断

在做中断试验时&#xff0c;发现中断驱动总是insmod失败&#xff0c;之后定位到 gpio_request 失败&#xff0c;之后是想到使用的野火做好的系统&#xff0c;在uEnv.txt中会加载大量设备树插件&#xff0c;将key相关的设备树插件屏蔽即可。 linux中断API函数 中断号 每个中断…

【管理运筹学】第 5 章 | 整数规划 (3,隐枚举法计算步骤)

文章目录 引言四、0-1 整数规划4.2 0-1 整数规划的解法4.2.1 0-1 规划模型标准型4.2.2 隐枚举法计算步骤 写在最后 引言 经过前文&#xff0c;了解以及体会到 0-1 变量的特性后&#xff0c;我们来研究该如何去求解这类特殊的 0-1 整数规划模型。 四、0-1 整数规划 4.2 0-1 整…

【Maven教程】(二)安装配置篇:手把手教你安装及配置Maven环境~

Maven安装配置篇 1️⃣ 在 Windows 上安装 Maven1.1 下载及安装 Maven1.2 升级 Maven 2️⃣ 在基于UNIX 的系统上安装 Maven2.1 下载和安装2.2 升级 Maven2.3 安装目录分析2.4 设置 HTTP 代理 3️⃣ 在 IDE中安装Maven插件 1️⃣ 在 Windows 上安装 Maven 在安装 Maven 之前&a…

【面试专题】Java核心基础篇②

&#x1f4c3;个人主页&#xff1a;个人主页 &#x1f525;系列专栏&#xff1a;Java面试专题 目录 1.接口和抽象类有什么区别&#xff1f; 2.两个对象的 hashCode() 相同&#xff0c;则 equals()也一定为 true&#xff0c;对吗&#xff1f; 3.说一说hashCode()和equals()的…

一般文章让你了解mybatis,以及如今在Java开发的地位!

一.了解mybatis&#xff01; A.什么是mybatis&#xff1f; MyBatis是一个开源的持久层框架&#xff0c;它简化了在Java应用程序中使用关系型数据库的开发工作。MyBatis提供了将SQL语句和Java代码进行解耦的能力&#xff0c;使得应用程序可以通过简单的配置来访问数据库&#x…

【AI视频教程】只需5步,AI作出鸡你太美视频

1.视频效果 黄昏见证虔诚的信徒 2.准备工作 制作视频效果&#xff0c;需要准备下面3个条件&#xff1a; 准备stable diffusion的环境剪辑一段【鸡你太美】原版视频stable diffusion安装sd-webui-IS-NET-pro插件 2.1部署stable diffusion环境 部署步骤参考制作ikun图片的文章…

软件开发中常用数据结构介绍:C语言队列

工作之余来写写C语言相关知识&#xff0c;以免忘记。今天就来聊聊C语言实现循环队列&#xff0c;我是分享人M哥&#xff0c;目前从事车载控制器的软件开发及测试工作。 学习过程中如有任何疑问&#xff0c;可底下评论&#xff01; 如果觉得文章内容在工作学习中有帮助到你&…

基于springboot+vue的考研资讯平台(前后端分离)

博主主页&#xff1a;猫头鹰源码 博主简介&#xff1a;Java领域优质创作者、CSDN博客专家、公司架构师、全网粉丝5万、专注Java技术领域和毕业设计项目实战 主要内容&#xff1a;毕业设计(Javaweb项目|小程序等)、简历模板、学习资料、面试题库、技术咨询 文末联系获取 项目介绍…

Python中Setup.py的作用是什么

Python中Setup.py的作用是什么 今天就跟大家聊聊有关Python中Setup.py的作用是什么&#xff0c;可能很多人都不太了解&#xff0c;为了让大家更加了解. 1. 为什么需要对项目分发打包? 平常我们习惯了使用 pip 来安装一些第三方模块&#xff0c;这个安装过程之所以简单&#…

高项4.项目管理核心技术.

第一部分 项目管理概论 价值驱动的项目管理知识体系: 十二项原则;生命周期四个阶段;五个过程组;十大PM知识领域;八大绩效域;外加价值交付系统; 自1987 年以来, PMBOK 一直是基于过程的项目管理标准的重要代表,项目管理从业者一 直坚持基于过程的项目管理方法。随着…

用python从零开始做一个最简单的小说爬虫带GUI界面(1/3)

目录 前言 三节博客内容概要 PyQt5的配置 设置软件的快捷启动方式 1. 用于设计界面的程序 2. 将Qt Designer设计出来的ui文件转化为py文件 3. 可以把py文件打包成可执行的exe文件 4. 将ico图片放在qrc文件中&#xff0c;再将qrc文件转换成py…

Axios跨域请求处理

问题背景&#xff1a; vue 项目用 axios 进行请求的时候&#xff0c;总是报“Access to XMLHttpRequest at ‘http://localhost:8889/api/login’ from origin ‘http://localhost:8080……’”的错误 实际上就是前后端分离的情况下&#xff0c;发生了跨域的问题 跨域定义&…

AWS SDK 3.x for .NET Framework 4.0 可行性测试

前言 为了应对日益增长的网络安全挑战, 越来越多的互联网厂商已经陆续开始或者已经彻底停止了对 SSL 3 / TLS 1.0 / TLS1.1 等上古加密算法的支持. 而对于一些同样拥有悠久历史的和 AWS 服务相关联的应用程序, 是否可以通过仅更新 SDK 版本的方式来适应新的环境. 本文将以 Win…

PyTorch模型性能分析与优化

动动发财的小手&#xff0c;点个赞吧&#xff01; 训练深度学习模型&#xff0c;尤其是大型模型&#xff0c;可能是一项昂贵的支出。我们可以使用的管理这些成本的主要方法之一是性能优化。性能优化是一个迭代过程&#xff0c;我们不断寻找提高应用程序性能的机会&#xff0c;然…

基于Java+SpringBoot+vue前后端分离在线动漫信息系统设计实现

基于JavaSpringBootvue前后端分离在线动漫信息系统设计实现&#xff08;程序源码毕业论文&#xff09; 大家好&#xff0c;今天给大家介绍基于JavaSpringBootvue前后端分离在线动漫信息系统设计与实现&#xff0c;本论文只截取部分文章重点&#xff0c;文章末尾附有本毕业设计完…

基于单片机串口控制直流电机调速

一、系统方案 (2)本设计采用STC89C5单片机作为主控器&#xff0c;串口控制直流电机调速&#xff0c;串口助手发送1-8&#xff0c;改变电机速度&#xff0c;数码管显示对应速度。 二、硬件设计 原理图如下&#xff1a; 三、单片机软件设计 1、首先是系统初始化 TMOD0x21;//定…

Linux学习之ssh和scp

ls /etc/ssh可以看到这个目录下有一些文件&#xff0c;而/etc/ssh/ssh_config是客户端配置文件&#xff0c;/etc/ssh/sshd_config是服务端配置文件。 cat -n /etc/ssh/sshd_config | grep "Port "可以看一下sshd监听端口的配置信息&#xff0c;发现这个配置端口是22…

代码随想录算法训练营之JAVA|第三十三天|738. 单调递增的数字

今天是第33天刷leetcode&#xff0c;立个flag&#xff0c;打卡60天&#xff0c;如果做不到&#xff0c;完成一件评论区点赞最高的挑战。 算法挑战链接 738. 单调递增的数字https://leetcode.cn/problems/monotone-increasing-digits/ 第一想法 题目理解&#xff1a;找到一个…

2023国赛数学建模B题思路模型代码 高教社杯

本次比赛我们将会全程更新思路模型及代码&#xff0c;大家查看文末名片获取 之前国赛相关的资料和助攻可以查看 2022数学建模国赛C题思路分析_2022国赛c题matlab_UST数模社_的博客-CSDN博客 2022国赛数学建模A题B题C题D题资料思路汇总 高教社杯_2022国赛c题matlab_UST数模社…