当前位置:   article > 正文

如何使用STM32F103C8T6驱动WS2812(PWM+DMA)_stm32驱动ws2812

stm32驱动ws2812

一、WS2812概述:

WS2812 内部集成了处理芯片和3颗不同颜色的led灯(红,绿,蓝),通过单总线协议分别控制三个灯的亮度强弱,达到全彩的效果。

数据传输时间:

T0H0码,高电平时间220ns~380ns
T1H1码,高电平时间580ns~1us
T0L0码,低电平时间580ns~1us
T1L1码,低电平时间220ns~420ns
RES帧单位,低电平时间280us以上

时序波形图:

24 bit数据结构:

每一个灯需要 8 bits(1 byte) 的数据 (8个1时最亮、8个0时不亮),所以一颗 ws2812 共需要24 bits(3 bytes) **(24个1时最亮、24个0时不亮)**的数据。

注:高位先发,按照 GRB 的顺序发送数据

G7G6G5G4G3G2G1G0R7R6R5R4R3R2R1R0B7B6B5B4B3B2B1B0

绿色

111111110000000000000000

红色

000000001111111100000000

蓝色

000000000000000011111111

二、WS2812驱动的几种方式

1、使用 延时函数

直接翻转IO口产生时序,这种方式最为简单易用,只需要控制延时的时间,就可以从产生0和1码,它需要占用系统资源。
使用 SPI 数据传输产生时序
2、通过SPI控制

只需要控制在合适的波特率,在传输不同数据的时候,可以产生符合要求的0和1码,这种方式需要等同于使用了一个SPI设备
3、使用 DMA+Timer 产生时序

这种方式需要使用一个定时器,其中一个通道固定产生一个周期1.25us的PWM,占空比2/3,接着需要另一个通道,在周期的1/3处搬运数据到IO口,若为1,PWM不变,若为0,PWM则为0码,这种方式有更大的局限性,由于DMA只能搬运至少一个字节,所以每次会同时改变8个IO口的高低电平,或许使用位带操作可以解决这问题
4、使用 Timer+PWM+DMA 产生时序

本文讨论的实现方案,这种方案有2种驱动的方式,一种是直接建立一个大的数组,存放所有灯珠的数据,然后启动DMA传输,第二种是建立2个灯组数据大小的数组,当DMA传输一个灯珠数据时,改变另一个灯组数据,通过不断改变数组的方式,节约内存,相比较而言,第一种方式较为直观,第二种方式则可以解决灯珠较多的情况,本文讨论第一种的原理和程序的实现。

三、TIM+PWM+DMA驱动WS2812

定时器 TIM 用以产生一个固定周期的PWM,DMA用以改变PWM 的占空比:

如图,DMA通过不断的搬运数据到定时器调节占空比的CCR寄存器,实现ws2812时序的产生,在STM32中,通过配置外设可实现:定时器每产生一次溢出事件(即计数完成),就请求一次DMA搬运一个数据(长度:字节/半字/字可选),所以用户只需要将数据排列在数组里,就可以产生所需要的时序。

四、STM32CubeMx配置

1、选择芯片 STM32F103C8T6

2、选择下载方式

ws2812_3-1

3、配置系统时钟

4、时钟树配置

5、生成工程配置

6、生成工程配置

7、定时器配置

8、DMA配置

9、选择TIM3_CH1/TRIG

10、选择Normal Byte Byte

11、GENERATE CODE生产代码

12、打开工程

13、编译成功

五、程序设计

新建一个.c和.h文件

c文件代码如下

#include "ws2812.h"

WS28xx_TypeStruct WS28xx;
void __show()
{
	HAL_TIM_PWM_Start_DMA(&WS28xx_PWM_hTIMER,WS28xx_PWM_Chaneel,(uint32_t *)(&WS28xx.WS28xx_Data),sizeof(WS28xx.WS28xx_Data));
}
	
//设置index的颜色
void __SetPixelColor_RGB(unsigned short int index,unsigned char r,unsigned char g,unsigned char b)
{
	unsigned char j;
	if(index > WS28xx.Pixel_size)
		return;
	for(j = 0; j < 8; j++)
	{		
		WS28xx.WS28xx_Data.ColorRealData [24 * index + j]        = (g & (0x80 >> j)) ? BIT_1 : BIT_0;  //G 将高位先发
		WS28xx.WS28xx_Data.ColorRealData [24 * index + j + 8]    = (r & (0x80 >> j)) ? BIT_1 : BIT_0;  //R将高位先发
		WS28xx.WS28xx_Data.ColorRealData [24 * index + j + 16]   = (b & (0x80 >> j)) ? BIT_1 : BIT_0;  //B将高位先发
	}
}
//获取某个位置的RGB
void __GetPixelColor_RGB(unsigned short int index,unsigned char *r,unsigned char *g,unsigned char *b)
{
	unsigned char j;
	*r=0;
	*g=0;
	*b=0;
	if(index > WS28xx.Pixel_size)
		return;
	for(j = 0; j <8; j++)
	{
		(*g)|=((WS28xx.WS28xx_Data.ColorRealData [24 * index + j]     >=BIT_1)? 0x80>>j:0); 	//G
		(*r)|=((WS28xx.WS28xx_Data.ColorRealData [24 * index + j + 8] >=BIT_1)? 0x80>>j:0);  	//R
		(*b)|=((WS28xx.WS28xx_Data.ColorRealData [24 * index + j + 16]>=BIT_1)? 0x80>>j:0);   //B
	}
}

void __SetPixelColor_From_RGB_Buffer( unsigned short int pixelIndex,unsigned char pRGB_Buffer[][3],unsigned short int DataCount)
{
	unsigned short int Index,j=0;
	for(Index=pixelIndex;Index < WS28xx.Pixel_size; Index++)
	{
			WS28xx.SetPixelColor_RGB(Index,pRGB_Buffer[j][0],pRGB_Buffer[j][1],pRGB_Buffer[j][2]);
			j++;
			if(j>DataCount)
				return;
	}
}

//设置所有颜色
void __SetALLColor_RGB(unsigned char r,unsigned char g,unsigned char b)
{
	unsigned short int Index;
	for(Index=0;Index < WS28xx.Pixel_size; Index++)
	{
		WS28xx.SetPixelColor_RGB(Index,r,g,b);
	}
}

void	WS28xx_TypeStructInit()
{
	WS28xx.Pixel_size=PIXEL_SIZE;
	WS28xx.GetPixelColor_RGB=__GetPixelColor_RGB;
	WS28xx.SetPixelColor_From_RGB_Buffer=__SetPixelColor_From_RGB_Buffer;
	WS28xx.SetPixelColor_RGB=__SetPixelColor_RGB;
	WS28xx.SetALLColor_RGB=__SetALLColor_RGB;
	WS28xx.show=__show;
}

  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
  • 59
  • 60
  • 61
  • 62
  • 63
  • 64
  • 65
  • 66
  • 67
  • 68
  • 69
  • 70

h文件代码如下

#ifndef MYLIB_WS28XX
#define MYLIB_WS28XX

#include "tim.h"			
/****************************************
*Config
****************************************/
#define BIT_1               61            //1码比较值为61-->850us
#define BIT_0               28            //0码比较值为28-->400us

#define PIXEL_SIZE			    8             //灯的数量
#define WS28xx_PWM_hTIMER	  htim3         //定时器3
#define WS28xx_PWM_Chaneel  TIM_CHANNEL_1	//通道1	

//整个WS28xx_DataTypeStruct结构体将被以PWM方式发送
typedef struct
{						
	unsigned char ColorStartData[3];           //3个0等待PWM稳定			
	unsigned char ColorRealData[24*PIXEL_SIZE];//实际GRB数据(已经转换为PWM对应的值)
	unsigned char ColorEndData;             	 //结束位为0
}WS28xx_DataTypeStruct;

/****************************************
*对象化编程
****************************************/
typedef struct 
{
	//实际发送的数据
	WS28xx_DataTypeStruct WS28xx_Data;
	//灯数量
	unsigned short int Pixel_size;
	
    //单独设置index的RGB颜色
    void (*SetPixelColor_RGB)(unsigned short int index,unsigned char r,unsigned char g,unsigned char b);
    //从RGB数据读出:设置index的RGB颜色
    void (*SetPixelColor_From_RGB_Buffer)( unsigned short int pixelIndex,unsigned char pRGB_Buffer[][3],unsigned short int DataCount);
    //设置所有为RGB颜色
    void (*SetALLColor_RGB)(unsigned char r,unsigned char g,unsigned char b);
    //获取某个位置的RGB
    void (*GetPixelColor_RGB)(unsigned short int index,unsigned char *r,unsigned char *g,unsigned char *b);
    //显示(发出数据)
    void (*show)(void);
}WS28xx_TypeStruct;

extern WS28xx_TypeStruct WS28xx;

void   WS28xx_TypeStructInit(void);

#endif


  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51

:记得在main.c中加上初始化函数 WS28xx_TypeStructInit

while中代码如下

	WS28xx.SetALLColor_RGB (255,0,0);//整体红色
    WS28xx.show ();
    HAL_Delay (200);
    WS28xx.SetALLColor_RGB (0,255,0);//整体绿色
    WS28xx.show ();
    HAL_Delay (200);
    WS28xx.SetALLColor_RGB (0,0,255);//整体蓝色
    WS28xx.show ();
    HAL_Delay (200);	
    WS28xx.SetALLColor_RGB (0,0,0);  //集体熄灭
    WS28xx.show ();
	WS28xx.SetPixelColor_RGB(0,0,0,255);//第一个亮蓝灯
	WS28xx.show ();
	HAL_Delay (200);		
	WS28xx.SetALLColor_RGB (0,0,0);
	WS28xx.SetPixelColor_RGB(1,0,0,255);//第二个亮绿灯 	
	WS28xx.show ();
	HAL_Delay (200);	
	WS28xx.SetALLColor_RGB (0,0,0);
	WS28xx.SetPixelColor_RGB(2,0,0,255);//第三个亮蓝灯 	
	WS28xx.show ();
	HAL_Delay (200);
	WS28xx.SetALLColor_RGB (0,0,0);
	WS28xx.SetPixelColor_RGB(2,0,0,255);//第四个亮蓝灯 	
	WS28xx.show ();
	HAL_Delay (200);
	WS28xx.SetALLColor_RGB (0,0,0);
	WS28xx.SetPixelColor_RGB(4,0,0,255);//第五个亮蓝灯 	
	WS28xx.show ();
	HAL_Delay (200);			
	WS28xx.SetALLColor_RGB (0,0,0);
	WS28xx.SetPixelColor_RGB(5,0,0,255);//第六个亮蓝灯 	
	WS28xx.show ();
	HAL_Delay (200);	
	WS28xx.SetALLColor_RGB (0,0,0);
	WS28xx.SetPixelColor_RGB(6,0,0,255);//第七个亮蓝灯 	
	WS28xx.show ();
	HAL_Delay (200);	
	WS28xx.SetALLColor_RGB (0,0,0);
	WS28xx.SetPixelColor_RGB(7,0,0,255);//第八个亮蓝灯 	
	WS28xx.show ();
	HAL_Delay (200);		
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42

完整工程: 链接

声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/凡人多烦事01/article/detail/643439
推荐阅读
相关标签
  

闽ICP备14008679号