项目场景:
使用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】有介绍这种方式,感兴趣的可以尝试下。
本文设计方式:
- 计算定时器ARR值:本项目使用的芯片型号是STM32F405RGT6,使用了TM3来进行PWM生成。TIM3挂载在APB1总线上,主频为84M。由于RGB灯一个码元周期为1.25us,故计算的ARR=105,PSC=0。
- 确定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
- 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);}}
- 将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