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 = 27
 T1H_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); //让占空比为0
		while(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;		//ARR
	TIM_TimeBaseInitStructure.TIM_Prescaler = psc;		//PSC
	TIM_TimeBaseInitStructure.TIM_RepetitionCounter = 0;
	TIM_TimeBaseInit(TIM3, &TIM_TimeBaseInitStructure);
	TIM_OCStructInit(&TIM_OCInitStructure);
	
	TIM_OCInitStructure.TIM_OCMode = TIM_OCMode_PWM1;//TIM_OCMode_PWM1
	TIM_OCInitStructure.TIM_OCPolarity = TIM_OCPolarity_High;//TIM_OCPolarity_High
	TIM_OCInitStructure.TIM_OutputState = TIM_OutputState_Enable;
	TIM_OCInitStructure.TIM_Pulse = 0;		//CCR
	
	TIM_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); //让占空比为0
		while(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 105

void TIM3_PWM_Init(u16 arr,u16 psc);
void ledControl();	
u32 dataPrecess(u32 oData);

#endif


效果展示:

stm32驱动RGB

  • 28
    点赞
  • 15
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
引用\[1\]和\[2\]提到了使用SPI来控制WS2812灯。在STM32F103单片机上,可以通过配置SPI外设来模拟WS2812的通信时序。SPI的速率需要设置为足够快,以便在发送数据时能够满足WS2812的要求。可以使用SPI的MOSI接口的一个字节来模拟WS2812的一个编码,其中发送的数据0xF8表示编码1,发送的数据0xC0表示编码0。通过硬件SPI和SPI的DMA进行控制,可以保证数据发送的稳定性和效率。在具体实现中,可以选择使用SPI1或SPI2,根据需要调整分频系数以满足通信频率要求。在代码中,需要定义灯珠的个数和0码1码的数值,根据不同的灯珠数量进行修改。\[1\]\[2\] 引用\[3\]提到了WS2812灯的结构,每颗灯内部包含三个LED灯,分别是红、绿、蓝三个颜色的LED灯。每个LED灯使用PWM驱动,发送的数据即为PWM的宽度。一颗WS2812 RGB灯共需要24位(3字节)的数据来控制。\[3\] 因此,要在STM32F103上驱动WS2812灯,可以通过配置SPI外设来模拟WS2812的通信时序,并使用PWM来控制每个LED灯的亮度。具体实现中,需要设置SPI的速率和分频系数,定义灯珠的个数和0码1码的数值,并发送相应的数据来控制WS2812灯的亮灭和颜色。 #### 引用[.reference_title] - *1* *3* [使用STM32F103的SPI+DMA驱动ws2812 LED](https://blog.csdn.net/xia233233/article/details/129810137)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v91^insertT0,239^v3^insert_chatgpt"}} ] [.reference_item] - *2* [[STM32]硬件太丑,B格不够,只好彩灯来凑--STM32F103 SPI驱动WS2812](https://blog.csdn.net/ic2121/article/details/126797846)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v91^insertT0,239^v3^insert_chatgpt"}} ] [.reference_item] [ .reference_list ]

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值