使用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

作者:Cherishalfy

物联沃分享整理
物联沃-IOTWORD物联网 » 使用STM32 PWM驱动WS2812 RGB灯珠

发表回复