STM32使用PWM驱动WS2812_RGB灯珠

项目场景:


使用STM32标准库产生PWM实现RGB灯珠控制。
芯片型号:stm32f405rgt6
设计优点:不需要使用定时器中断资源,可以使得STM32在驱动RGB灯珠的同时能够执行其他任务。


RGB灯珠简介


项目所使用的RGB灯珠如下所示,封装为5050。
在这里插入图片描述
串联结构如下所示:
在这里插入图片描述
每个灯珠采用24bit数据结构进行显示驱动,其数据结构如下所示:
在这里插入图片描述
发送顺序为高位在前,按照GRB格式进行发送
由于RGB灯珠采用单线归零码方进行数据传输,所以具有严格的时许要求,通过单个周期内高低电平持续的时间来判断bit位为0、1或reset。三种数据类型的高低电平时间如下:
在这里插入图片描述


目前已有的实现方式:


参考:https://blog.csdn.net/qq_40102829/article/details/106030934

第一种是使用延时函数在特定延时时间内对输出管脚进行翻转操作,这种方式非常占用单片机资源,而且实现1.25us延时的准确度不高。

第二种是使用定时器进行PWM输出,输出频率设置为800kHz即可实现1.25us周期循环,通过改变周期内占空比来实现0码和1码输出,但是用到了定时器中断,1.25us的中断非常快,会导致STM32几乎不能处理其他事务。

第三种是使用SPI,这种方式比较巧妙,使用SPI的clk线和mosi线,通过8分频可以设置clk时钟线的输出频率,然后采用16byte数据模拟0码和1码,这样输出频率为562.5kHz,理论上也是可以驱动的,该up主的文章【SPI驱动ws2812】有介绍这种方式,感兴趣的可以尝试下。


本文设计方式:


  1. 计算定时器ARR值:本项目使用的芯片型号是STM32F405RGT6,使用了TM3来进行PWM生成。TIM3挂载在APB1总线上,主频为84M。由于RGB灯一个码元周期为1.25us,故计算的ARR=105,PSC=0。
  2. 确定0码和1码的占空比:
 T0H_Pulse = (T0H/1.25us) * arr = 0.32/1.25 * arr = 27T1H_Pulse = (T1H/1.25us) * arr = 0.64/1.25 * arr = 54
  1. reset码设置:RGB灯珠需要保持至少80us以上的低电平才能复位,为了实现reset功能,本文的设计方式是通过将pwm占空比设置为0,让其保持多个周期来实现80us以上的低电平达到复位效果。具体实现代码如下:
void ledReset()
{u8 i;//设置保持周期数,达到复位效果for(i=0;i<120;i++) //150us/1.25us = 120{TIM_SetCompare4(TIM3, 0); //让占空比为0while(TIM3->CNT < TIM3_ARR);}}
  1. 将RGB颜色格式转换为GRB格式:由于颜色生成网站上的颜色格式都是RGB格式,直接复制过来没法直接使用,需要去手动交换颜色顺序,本文设计了颜色格式转换函数,可以自动将RGB格式转换为GRB格式,具体实现如下:
//将RGB格式转换为GRB格式
u32 dataPrecess(u32 RGBData)
{u32  GRBData;u8 R, G, B;B = RGBData & (u8)0xff;G = (RGBData >> 8) & (u8)0xff;R = (RGBData >> 16) & (u8)0xff;GRBData = 0;//先置零,保证数据干净GRBData |= G;GRBData <<= 8;GRBData |= R;GRBData <<= 8;GRBData |= B;return GRBData;
}

完整代码:


led.c

#include "led.h" 
#include "delay.h"
#include "usart.h"const u8 N = 12;
u32 colors[N] = {0x84fab0, 0x8fd3f4,0xfccb90, 0xd57eeb,0xfa709a, 0xfee140,0x5ee7df, 0xb490ca,0xcfd9df, 0xe2ebf0,0xff0000, 0x00ff00};//PC9->TIM3_CH4
void TIM3_PWM_Init(u16 arr,u16 psc)
{GPIO_InitTypeDef GPIO_InitStructure;TIM_TimeBaseInitTypeDef TIM_TimeBaseInitStructure;TIM_OCInitTypeDef TIM_OCInitStructure;RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM3, ENABLE);RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOC,ENABLE);GPIO_InitStructure.GPIO_Pin = GPIO_Pin_9;GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF;       GPIO_InitStructure.GPIO_Speed = GPIO_Speed_100MHz;      GPIO_InitStructure.GPIO_OType = GPIO_OType_PP;     GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_UP;       GPIO_Init(GPIOC,&GPIO_InitStructure); GPIO_PinAFConfig(GPIOC,GPIO_PinSource9,GPIO_AF_TIM3);    TIM_TimeBaseInitStructure.TIM_ClockDivision = TIM_CKD_DIV1;  //分频因子TIM_TimeBaseInitStructure.TIM_CounterMode = TIM_CounterMode_Up;TIM_TimeBaseInitStructure.TIM_Period = arr;		//ARRTIM_TimeBaseInitStructure.TIM_Prescaler = psc;		//PSCTIM_TimeBaseInitStructure.TIM_RepetitionCounter = 0;TIM_TimeBaseInit(TIM3, &TIM_TimeBaseInitStructure);TIM_OCStructInit(&TIM_OCInitStructure);TIM_OCInitStructure.TIM_OCMode = TIM_OCMode_PWM1;//TIM_OCMode_PWM1TIM_OCInitStructure.TIM_OCPolarity = TIM_OCPolarity_High;//TIM_OCPolarity_HighTIM_OCInitStructure.TIM_OutputState = TIM_OutputState_Enable;TIM_OCInitStructure.TIM_Pulse = 0;		//CCRTIM_OC4Init(TIM3, &TIM_OCInitStructure);TIM_OC4PreloadConfig(TIM3,TIM_OCPreload_Enable);TIM_CtrlPWMOutputs(TIM3, ENABLE);TIM_ARRPreloadConfig(TIM3,ENABLE); //使能TIM3在ARR上的预装载寄存器TIM_Cmd(TIM3, ENABLE);
}//将RGB格式转换为GRB格式
u32 dataPrecess(u32 RGBData)
{u32  GRBData;u8 R, G, B;B = RGBData & (u8)0xff;G = (RGBData >> 8) & (u8)0xff;R = (RGBData >> 16) & (u8)0xff;GRBData = 0;//先置零,保证数据干净GRBData |= G;GRBData <<= 8;GRBData |= R;GRBData <<= 8;GRBData |= B;return GRBData;
}void ledReset()
{u8 i;//设置保持周期数,达到复位效果for(i=0;i<120;i++) //150us/1.25us = 120{TIM_SetCompare4(TIM3, 0); //让占空比为0while(TIM3->CNT < TIM3_ARR);}}void singleLedShow(u32 GRBData)
{u8 i;u32 pulse;for(i=0;i<24;i++){pulse = (GRBData & 0x00800000) ? T1H_Pulse : T0H_Pulse;TIM_SetCompare4(TIM3, pulse); while(TIM3->CNT < TIM3_ARR);GRBData = GRBData<<1;}
}void ledControl()
{u8 i;ledReset();for(i=0;i<N;i++){singleLedShow(dataPrecess(colors[i]));delay_ms(300);ledReset();}
}

led.h

#ifndef __led_h
#define __led_h#include "sys.h"#define T0H_Pulse 27
#define T0L_Pulse 78#define T1H_Pulse 54
#define T1L_Pulse 51#define TIM3_ARR 105void TIM3_PWM_Init(u16 arr,u16 psc);
void ledControl();	
u32 dataPrecess(u32 oData);#endif

效果展示:

stm32驱动RGB

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

如若内容造成侵权/违法违规/事实不符,请联系长河编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

面试-javaIO机制

1.BIO BIO&#xff1a;是传统的javaIO以及部分java.net下部分接口和类。例如&#xff0c;socket,http等&#xff0c;因为网络通信同样是IO行为。传统IO基于字节流和字符流进行操作。提供了我们最熟悉的IO功能&#xff0c;譬如基于字节流的InputStream 和OutputStream.基于字符流…

program writer 这个名称好久 没有听到了

一、引言 在数字化时代的浪潮中&#xff0c;编程已成为一项不可或缺的技能。从简单的自动化任务到复杂的人工智能系统&#xff0c;编程无处不在&#xff0c;其应用范围之广泛令人叹为观止。 二、编程的基本概念 在深入讨论编程的用途之前&#xff0c;我们首先需要了解编程的…

在 Postman 中使用 Body 进行 POST 请求

Postman 是开发者日常工具箱中不可缺少的一部分&#xff0c;特别是在 API 开发和调试环节中。 为什么使用 POST 请求 POST 请求用于向服务器发送数据&#xff0c;这些数据通常被处理后存储。与 GET 请求不同&#xff0c;POST 请求将数据嵌入请求体&#xff08;Body&#xff0…

不能创建第三个变量,实现两个数的交换

目录 常规实现两个数的交换&#xff08;如&#xff1a;交换变量a和变量b&#xff09; 方法一&#xff1a;加减法 方法二&#xff1a;异或操作符 常规实现两个数的交换&#xff08;如&#xff1a;交换变量a和变量b&#xff09; 创建一个临时变量tmp&#xff0c;先将其中一个…

DP(动态规划)【2】 最大连续子列和 最长不降子序列

1.最大连续子列和 #include <iostream> #include <vector> #include <cmath> #include <string> #include <cstring> #include <queue> using namespace std; const int N10002,maxn10;int n,m,k,f[N]{0},dp[N]{0};int main() {scanf(&quo…

QT QThread 线程类的使用及示例

QThread 是 Qt 框架提供的一个用于处理多线程的类&#xff0c;它允许开发者编写具有并发功能的应用程序&#xff0c;提高程序的响应速度、执行效率和用户体验。 在操作系统中&#xff0c;线程是进程内的执行单元&#xff0c;拥有独立的执行路径。每个线程有自己独立的栈空间&a…

华为仓颉编程语言正式发布,仓颉编程教程

目录 前言 基本概念 标识符 变量 类型 基础数据类型 表达式 if 表达式 while 表达式 for-in 表达式 程序结构 函数 定义函数 调用函数 lambda表达式 应用实例&#xff08;遍历目录&#xff09; 枚举 定义与实例化 成员访问规则 match表达式 应用实例&…

The difference between Manhattan distance and Cosine Distance

题意&#xff1a;为什么即使返回了相同的文本块&#xff0c;曼哈顿距离&#xff08;Manhattan Distance&#xff09;和余弦距离&#xff08;Cosine Distance&#xff09;之间还是存在差异&#xff1f; 问题背景&#xff1a; I am using the qdrant DB and client for embeddin…

Websocket解析及用法(封装一个通用订阅发布主题的webSocket类)

1、什么是WebSocket? websocket的目标是通过一个长连接实现与服务器全双工&#xff0c;双向的通信。是一种在单个TCP连接上进行全双工通信的协议&#xff0c;使得客户端和服务器之间的数据交换变得更加简单&#xff0c;允许服务端主动向客户端推送数据。在 js中创建websocket…

LabVIEW在机器人研究所中的应用

机器人研究所致力于机器人技术的研究与开发&#xff0c;涵盖工业机器人、服务机器人、医疗机器人等多个领域。研究所需要一个高效、灵活的实验控制和数据采集系统&#xff0c;以进行复杂的机器人实验&#xff0c;并对实验数据进行实时处理和分析。 项目需求 实时控制与监控&am…

使用面向对象方式编写ROS2节点

1.使用c方式创建节点 在d2lros2/chapt2/chapt2_ws/src/example_cpp/src下新建node_03.cpp&#xff0c;接着输入下面的代码。 #include "rclcpp/rclcpp.hpp" /* 创建一个类节点&#xff0c;名字叫做Node03,继承自Node. */ class Node03 : public rclcpp::Node {…

完全离线的本地问答模型LocalGPT如何实现无公网IP远程连接提问

文章目录 前言环境准备1. localGPT部署2. 启动和使用3. 安装cpolar 内网穿透4. 创建公网地址5. 公网地址访问6. 固定公网地址 前言 本文主要介绍如何本地部署LocalGPT并实现远程访问&#xff0c;由于localGPT只能通过本地局域网IP地址端口号的形式访问&#xff0c;实现远程访问…

java+mysql通讯录管理

完整代码地址如果控制台打印出现乱码&#xff0c;进行如下设置

【Linux】Wmware Esxi磁盘扩容

目录 一、概述 1.1 磁盘分区概念 1.2 LVM概念 二、扩容步骤 二、报错 一、概述 1.1 磁盘分区概念 在 Linux 中&#xff0c;每一个硬件设备都映射到一个系统的文件&#xff0c;对于硬盘、光驱等 IDE 或 SCSI 设备也不例外。Linux把各种 IDE 设备分配了一个由 hd 前缀组成的文…

django admin添加自己的页面

建立模型 如果要单独建一个页面&#xff0c;用于展示model的数据&#xff0c;可以新建一个model&#xff0c;继承自要展示的那个类 class ViewsByDayModel(ViewsByDay): # 父类为要展示的model类class Meta:proxy True # 使用代理verbose_name 每日浏览次数统计verbose_nam…

嫦娥六号平安回家,Smartbi非常荣幸参与中国航天项目

“小时不识月&#xff0c;呼作白玉盘。”李白的这句诗&#xff0c;承载了古人对月亮的美好想象与纯真童趣。今天&#xff0c;当我们仰望夜空&#xff0c;那轮明月不仅是诗词中的意象&#xff0c;更是科学探索的目标和梦想的寄托。 2024年6月25日14时07分&#xff0c;嫦娥六号返…

Redis-实战篇-什么是缓存-添加redis缓存

文章目录 1、什么是缓存2、添加商户缓存3、前端接口4、ShopController.java5、ShopServiceImpl.java6、RedisConstants.java7、查看Redis Desktop Manager 1、什么是缓存 缓存就是数据交换的缓冲区&#xff08;称为Cache&#xff09;&#xff0c;是存贮数据的临时地方&#xff…

深入解析链表:解锁数据结构核心奥秘

一. 链表的定义 链表是一种线性数据结构&#xff0c;由一系列节点组成。每个节点包含两个部分&#xff1a; 数据域&#xff08;Data&#xff09;&#xff1a;存储节点的数据。指针域&#xff08;Pointer&#xff09;&#xff1a;存储指向下一个节点的地址。 链表的第一个节点…

SpringBoot 3.3.1 + Minio 实现极速上传和预览模式

统一版本管理 <properties><minio.version>8.5.10</minio.version><aws.version>1.12.737</aws.version><hutool.version>5.8.28</hutool.version> </properties><!--minio --> <dependency><groupId>io.m…

算法金 | 决策树、随机森林、bagging、boosting、Adaboost、GBDT、XGBoost 算法大全

大侠幸会&#xff0c;在下全网同名「算法金」 0 基础转 AI 上岸&#xff0c;多个算法赛 Top 「日更万日&#xff0c;让更多人享受智能乐趣」 决策树是一种简单直观的机器学习算法&#xff0c;它广泛应用于分类和回归问题中。它的核心思想是将复杂的决策过程分解成一系列简单的决…