项目一:基于STM32定时器输出PWM波实现WS2812B的RGB全彩控制

一、WS2812B芯片说明

WS2811B概述

数据协议采用单线归零码的通讯方式,芯片在上电复位以后,DIN端接受从控制器传输过来的数据,首先送过来的24bit数据被第一个芯片提取后,送到芯片内部的数据锁存器,剩余的数据经过内部整形处理电路整形放大后通过DO端口开始转发输出给下一个级联的芯片,每经过一个芯片的传输,信号减少24bit。芯片采用自动整形转发技术,使得该芯片的级联个数不受信号传送的限制,仅仅受限信号传输速度要求。

芯片内部的数据锁存器根据接收到的24bit数据,在OUTR、OUTG、OUTB控制端产生不同的占空比控制信号, 等待DIN端输入RESET信号时,所有芯片同步将接收到的数据送到各个段,芯片将在该信号结束后重新接收的数据,在接收完开始的24bit数据后,通过DO口转发数据口,芯片在没有接收到RESET码前,OUTR、OUTG、OUTB管脚原输出保持不变,当接受到280μs以上低电平RESET码后,芯片将刚才接收到的24bit PWM数据脉宽输出到OUTR、OUTG、OUTB引脚上。

数据传输协议

数据传输时间

在这里插入图片描述

时序图及连接方法

时序图

数据传输方法

在这里插入图片描述
注意:这里的RESET码,在每一次发送灯组颜色信号之前都要发送一遍RESET信号,否则会产生时序错误,导致灯组显示异常

24bit数据结构

在这里插入图片描述

  1. 每一个灯需要 8 bits(1 byte) 的数据 (8个1时最亮、8个0时不亮),所以一颗 ws2812 共需要24 bits(3 bytes) **(24个1时最亮、24个0时不亮)**的数据。
  2. 灯组的24bit数据的发送顺序是根据芯片手册定义的,可能是RGB,也可能是GRB。

WS2812驱动的几种方式

1.GPIO普通输出方式模拟时序

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

2.通过SPI控制

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

3.使用 Timer+PWM+DMA 产生时序


定时器 TIM 用以产生一个固定周期的PWM,DMA用以改变PWM 的占空比。如图,DMA通过不断的搬运数据到定时器调节占空比的CCR寄存器,实现ws2812时序的产生,在STM32中,通过配置外设可实现:定时器每产生一次溢出事件(即计数完成),就请求一次DMA搬运一个数据(长度:字节/半字/字可选),所以用户只需要将数据排列在数组里,就可以产生所需要的时序。方法是直接建立一个大的数组,存放所有灯珠的数据,然后启动DMA传输。

二、RGB颜色原理

1.颜色基础

RGB概念
RGB指的是R(red)红色、G(green)绿色、B(blue)蓝色,三种颜色。
所有的颜色都可以用这三种颜色配出来。通常情况下,RGB各有256级亮度,用数字表示为从0、1、2…直到255。按照计算,256级的RGB色彩总共能组合出约1678万种色彩,即256×256×256=16777216。通常也被简称为1600万色或千万色。也称为24位色(2的24次方)
在这里插入图片描述
RGB格式的内部排列
在这里插入图片描述

2.颜色网站链接

千通彩色库
颜色渐变工具

3.RGB的256级亮度

4.颜色模型

表示颜色的模型HSV,H:色调、S:饱和度、V:亮度。只要H和S不变就能够保证颜色不改变,从而调整亮度。首先将RGB空间颜色转换成HSV空间颜色,然后调节V的值,调整之后,再将HSV空间颜色转回到RGB空间颜色这样就实现了RGB亮度的调节。

1)HSV模型概述

RGB图像与相机传感器输出的原始数据相对应,HSV图像则与我们人类的直观视觉更相符。HSV图像也包含相同尺寸的三通道数据:H通道、S通道、V通道。下面分别介绍这三个通道:
H通道:H通道的像素值表示色调,取值范围0~360,我们可以把这个取值范围理解为角度,也即一个闭环的取值范围,如下图:
在这里插入图片描述
S通道:S通道的像素值表示图像的饱和度。饱和度是指图片彩色的纯度——图像的混合颜色越少,其饱和度越高,直观看起来就越鲜艳鲜明、视觉效果越强烈;反之图像的混合颜色越多,其饱和度越低,视觉效果越弱。S通道像素值的取值范围是0~1,值越大表示饱和度越高。

比如在所有可视色彩中纯红色的饱和度是最高的,也即纯红色看起来最鲜艳,但是如果在纯红色中混入其它颜色,那么其饱和度将会降低,这时看起来就没那么鲜艳了。

V通道:V通道像素值表示图像的明亮程度,取值范围也是0~1,值越大表示越亮

2)RGB与HSV图像互相转换原理

RGB图像、HSV图像中各通道像素值的取值范围,对于图像中任意坐标点,其取值范围如下:
在这里插入图片描述
将HSV像素值转换到0~255之间:
在这里插入图片描述

3)RGB转HSV原理

对于图像中任意坐标点,其RGB颜色空间为(R,G,B),HSV颜色空间为(H,S,V),首先需要将R、G、B值转换到0~1之间:
在这里插入图片描述
然后计算H、S、V值:
4b963f5000db111f99a75809fa455fb1.png
如果计算得到的H值小于0,将该值再加上360,得到最终的H值:H=H+360
由于Opencv需要做HSV图像的可视化,因此最后还需要将各个值转换到0~255之间:
a025b7173973184c3478f76606beb211.png

4)HSV转RGB原理

对于图像中任意坐标点,其RGB颜色空间为(R,G,B),HSV颜色空间为(H,S,V)。首先将可视化图像的H、S、V值分别转换到0~360, 0 ~1, 0 ~1的范围:

1fb9e1c490e4b1c328bf76cd7363651a.png
那么R、G、B的计算如以下公式,其中floor表示向下取整运算:
430b61b0e960f657f1eac34dd71be19c.png

5)模型转换代码实现
// 求最大值、最小值函数
static float min(float a, float b, float c)
{
  float m;
  
  m = a < b ? a : b;
  return (m < c ? m : c); 
}

static float max(float a, float b, float c)
{
  float m;
  
  m = a > b ? a : b;
  return (m > c ? m : c); 
}
//结构体定义
typedef struct
{
	unsigned char R;
	unsigned char G;
	unsigned char B;
	unsigned char L;
}RGB_Color_TypeDef;

typedef struct
{
	 float H;
	 float S;
	 float V;
}HSV_Color_TypeDef;

//函数实现
static void RGB_TO_HSV(RGB_Color_TypeDef* input,HSV_Color_TypeDef* output) 
 {
     float r,g,b,minRGB,maxRGB,deltaRGB;
 
     r = input->R/255.0f;
     g = input->G/255.0f;
     b = input->B/255.0f;
        minRGB = min(r,g,b);
        maxRGB = max(r,g,b);
     output->V = maxRGB;
	 deltaRGB = maxRGB - minRGB;
     if(maxRGB != 0.0f)
      output->S = deltaRGB / maxRGB;
     else
      output->S = 0.0f;	 
     if (output->S <= 0.0f)
     {
      output->H = 0.0f;
     }
     else
     {
      if (r == maxRGB)
      {
       output->H = (g-b)/deltaRGB;
      }
      else
      {
       if (g == maxRGB)
       {
        output->H = 2.0f + (b-r)/deltaRGB;
       }
       else
       {
        if (b == maxRGB)
        {
         output->H = 4.0f + (r-g)/deltaRGB;
        }
       }
      }
      output->H = output->H * 60.0f;
      if (output->H < 0.0f)
      {
       output->H += 360;
      }
      output->H /= 360;
     }
 
 }

static void HSV_TO_RGB(HSV_Color_TypeDef* input,RGB_Color_TypeDef* output)  
 {
     float R,G,B;
     int k;
     float aa,bb,cc,f;
     if (input->S <= 0.0f)
      R = G = B = input->V;
     else
     {
      if (input->H == 1.0f)
       input->H = 0.0f;
      input->H *= 6.0f;
      k = (int)floor(input->H);
      f = input->H - k;
      aa = input->V * (1.0f - input->S);
      bb = input->V * (1.0f - input->S * f);
      cc = input->V * (1.0f -(input->S * (1.0f - f)));
      switch(k)
      {
      case 0:
       R = input->V; 
       G = cc; 
       B =aa;
       break;
      case 1:
       R = bb; 
       G = input->V;
       B = aa;
       break;
      case 2:
       R =aa;
       G = input->V;
       B = cc;
       break;
      case 3:
       R = aa;
       G = bb;
       B = input->V;
       break;
      case 4:
       R = cc;
       G = aa;
       B = input->V;
       break;
      case 5:
       R = input->V;
       G = aa;
       B = bb;
       break;
      }
     }
     output->R = (unsigned char)(R * 255);
     output->G = (unsigned char)(G * 255);
     output->B = (unsigned char)(B * 255);
 }
//亮度调节函数
//step:亮度调节参数,范围1-100
void Adjust_Brightness(unsigned long color,int step) 
{
	 HSV_Color_TypeDef HSV_V;
     RGB_Color_TypeDef RGB_V;
     uint32_t RGB;

     RGB_V.R=color<<16;
     RGB_V.G=color<<8;
     RGB_V.B=color;

     RGB_TO_HSV(&RGB_V,&HSV_V);
     RGB_V.L += step;
     if(RGB_V.L <= 0)
     {
    	 RGB_V.L = 1;
     }else if(RGB_V.L >= 100)
     {
    	 RGB_V.L = 100;
     }
     HSV_V.V = RGB_V.L /100.0;

     HSV_TO_RGB(&HSV_V,&RGB_V);

     RGB= RGB_V.R<<16 |RGB_V.G <<8 | RGB_V.B; // 合成颜色
    return RGB;
}

5.渐变函数

unsigned long ColorToColor(uint8_t Pixel_Len,unsigned long color0, unsigned long color1,unsigned int speed,int step)
{
	unsigned char Red0, Green0, Blue0;// 起始三原色

	unsigned char Red1, Green1, Blue1;// 结果三原色

	int RedMinus, GreenMinus, BlueMinus; // 颜色差(color1 - color0)

	unsigned char NStep; // 需要几步

	unsigned long  RGB[NStep];//RGB渐变颜色数组

	float RedStep, GreenStep, BlueStep; // 各色步进值

	unsigned long color; // 结果色

	unsigned char i,j;

	// 绿 红 蓝 三原色分解

	Green0= color0>>8;

	Red0 = color0>>16;

	Blue0 = color0;

	Green1= color1>>8;

	Red1 = color1>>16;

	Blue1 = color1;

	// 绿 红 蓝 三原色分解? 若输入的颜色非上述三种

	Green0=( color0&0x00FF00)>>8;

	Red0= (color0&0xFF0000)>>16;

	Blue0= color0;

	Green1= (color1&0x00FF00)>>8;

	Red1 =(color1&0xFF0000)>>16;

	Blue1= color1;

	// 计算需要多少步(取差值的最大值)

	RedMinus= Red1 - Red0;

	GreenMinus = Green1 - Green0;

	BlueMinus= Blue1 - Blue0;

	//判断两个绝对值大小。。a>b为真,则=a;否则=b

	NStep = ( abs0(RedMinus) > abs0(GreenMinus) ) ? abs0(RedMinus):abs0(GreenMinus);

	NStep = ( NStep > abs0(BlueMinus) ) ? NStep:abs0(BlueMinus);

	// 计算出各色步进值

	RedStep= (float)RedMinus/ NStep;

	GreenStep = (float)GreenMinus / NStep;

	BlueStep = (float)BlueMinus/ NStep;

	// 渐变开始

	for(i=0; i<NStep; i++)

	{

		Red1= Red0+ (int)(RedStep* i);

		Green1 = Green0 + (int)(GreenStep * i);

		Blue1 = Blue0 + (int)(BlueStep* i);

		color= Red1<<16 |Green1 <<8 | Blue1; // 合成颜色

		//添加自定义函数:将颜色发给各个灯珠

		HAL_Delay(speed);//控制渐变速度
	}

	return color;

三、WS2812B代码实现

1. WS2812B.h

注意:High 和Low的值根据所选STM32芯片的定时器时钟计算,这里的值不做参考。

#define Low       (19)	    //0编码高电平时间
#define High       (40)	    //1编码高电平时间

typedef struct
{
	unsigned char R;
	unsigned char G;
	unsigned char B;
	unsigned char L;
}RGB_Color_TypeDef;

typedef struct
{
	 float H;
	 float S;
	 float V;
}HSV_Color_TypeDef;

//R-G-B 定义颜色24bits
//标准色
#define  RainbowWHITE   0xFFFFFF    // 白色
#define  RainbowBLACK   0x000000    // 黑色 (不亮)
#define  RainbowRED     0xFF0000    // 红色
#define  RainbowORANGE  0xFFA500   // 橙色
#define  RainbowYELLOW  0xFFFF00   //黄色
#define  RainbowGREEN   0x00FF00    // 绿色
#define  RainbowBLUE    0x0000FF    // 蓝色
#define  RainbowCyan    0x00FFFF      //青色
#define  RainbowPURPLE  0x800080   //紫色

 //灯组参数定义
   
#define Pixel_NUM  20    //联级灯珠数量

#define RGB_NUM  24		  //灯珠RGB数据3*8

#define Lightness  20		  //亮度范围:0~100

extern void Color_show_1(int mode,uint8_t Pixel_Len,unsigned long dat,int step);//连续控制几个灯 模式0;逆向,模式1:顺向

extern void Color_show_2(uint8_t Pixel_Len, unsigned long dat,int step);控制某一个灯(顺向)

extern unsigned long ColorToColor(uint8_t Pixel_Len,unsigned long color0, unsigned long color1,unsigned int speed,int step);//颜色渐变函数

extern uint32_t Adjust_Brightness(unsigned long color,int step);

extern void Rainbow_lamp(void);//彩虹灯效

2. WS2812B.c

RGB_Color_TypeDef  Color;
const uint32_t WS2812_Rst[300] = {0}; //复位码缓冲区
/*二维数组存放最终PWM输出数组,每一行24个数据代表一个LED*/
uint32_t Pixel_Buf[Pixel_NUM][RGB_NUM];
/*********************************WB2812驱动函数**************************************************/
/*
功能:设定单个RGB LED的颜色,把结构体中RGB的24BIT转换为0码和1码
参数:LedId为LED序号,dat:指定灯的颜色
*/
void RGB_SetColor(uint8_t LedId,unsigned long dat)
{
	uint8_t i,j;
	RGB_Color_TypeDef  Color;
	Color.G= dat>>8;
	Color.R= dat>>16;
	Color.B=dat;

	if(LedId > Pixel_NUM)return; // 防止写入ID大于LED总数

	for(i=0;i<8;i++)
	{
		Pixel_Buf[LedId][i] = ( (Color.G & (1 << (7 -i)))? (CODE_1):CODE_0 );//数组某一行0~7转化存放G
	}
	for(i=8;i<16;i++)
	{
		Pixel_Buf[LedId][i] = ( (Color.R & (1 << (15-i)))? (CODE_1):CODE_0 );//数组某一行8~15转化存放R
	}
	for(i=16;i<24;i++)
	{
		Pixel_Buf[LedId][i] = ( (Color.B & (1 << (23-i)))? (CODE_1):CODE_0 );//数组某一行16~23转化存放B
	}
}

/*
功能:发送数组
参数:(&htim1)定时器1,(TIM_CHANNEL_1)通道1,WS2812_Rst:复位码,
*/
extern uint8_t g_Send_ok;
void RGB_SendArray(void)
{
	uint8_t i;
	if(g_Send_ok == 0)
	{
		g_Send_ok = 1;
		HAL_TIM_PWM_Start_DMA(&htim1, TIM_CHANNEL_1,WS2812_Rst,300);
	}
}


/*********************************WB2812基本函数**************************************************/
/*
功能:控制连续的几个灯 0;逆向(靠进按钮),模式1:顺向(远离按钮)
参数:mode为模式选择,0:逆向,1:顺向;
     Pixel_Len为LED数量,1-20
     dat:指定灯的颜色
*/
void Color_show_1(int mode,uint8_t Pixel_Len, unsigned long dat,int step)
{
	int16_t i,j;
	uint32_t  RGB;
    HSV_Color_TypeDef HSV_V;
    RGB_Color_TypeDef RGB_V;
    RGB_V.R=dat>>16;
    RGB_V.G=dat>>8;
    RGB_V.B=dat;

    RGB_TO_HSV(&RGB_V,&HSV_V);
    RGB_V.L = step;
    if(RGB_V.L <= 0)
    {
   	 RGB_V.L = 1;
    }else if(RGB_V.L >= 100)
    {
   	 RGB_V.L = 100;
    }
    HSV_V.V = RGB_V.L /100.0;

    HSV_TO_RGB(&HSV_V,&RGB_V);

    RGB= RGB_V.R<<16 |RGB_V.G <<8 | RGB_V.B; // 合成颜色
	if(mode==0)
	{
		for(i=0;i<Pixel_Len;i++)
		{
			RGB_SetColor(i,RGB);
		}
		RGB_SendArray();
	}
	else if(mode==1)
	{
		for(j=0;j<Pixel_Len;j++)
		{
			RGB_SetColor((Pixel_NUM-1-j),RGB);
		}
		RGB_SendArray();
	}
}

/*
功能:控制某一个灯顺向(远离按钮)
参数:Pixel_Len为顺向第几个灯的序号,灯的顺序:1-20
      dat:指定灯的颜色
*/
void Color_show_2(uint8_t Pixel_Len, unsigned long dat,int step)
{
	uint32_t  RGB;
    HSV_Color_TypeDef HSV_V;
    RGB_Color_TypeDef RGB_V;
    RGB_V.R=dat>>16;
    RGB_V.G=dat>>8;
    RGB_V.B=dat;

    RGB_TO_HSV(&RGB_V,&HSV_V);
    RGB_V.L = step;
    if(RGB_V.L <= 0)
    {
   	 RGB_V.L = 1;
    }else if(RGB_V.L >= 100)
    {
   	 RGB_V.L = 100;
    }
    HSV_V.V = RGB_V.L /100.0;

    HSV_TO_RGB(&HSV_V,&RGB_V);

    RGB= RGB_V.R<<16 |RGB_V.G <<8 | RGB_V.B; // 合成颜色

	RGB_SetColor((Pixel_NUM-Pixel_Len),RGB);
	RGB_SendArray();
	
}

//RGB亮度调节
uint32_t Adjust_Brightness(unsigned long color,int step)
 {

	 HSV_Color_TypeDef HSV_V;
     RGB_Color_TypeDef RGB_V;
     uint32_t RGB;

     RGB_V.R=color<<16;
     RGB_V.G=color<<8;
     RGB_V.B=color;

     RGB_TO_HSV(&RGB_V,&HSV_V);
     RGB_V.L += step;
     if(RGB_V.L <= 0)
     {
    	 RGB_V.L = 1;
     }else if(RGB_V.L >= 100)
     {
    	 RGB_V.L = 100;
     }
     HSV_V.V = RGB_V.L /100.0;

     HSV_TO_RGB(&HSV_V,&RGB_V);

     RGB= RGB_V.R<<16 |RGB_V.G <<8 | RGB_V.B; // 合成颜色
    return RGB;
 }
 //彩虹灯效
void Rainbow_lamp(void)
{
	int i;
	Color_show_1(1,20, BLACK);
	HAL_Delay(50);
		for(i = 1; i <= 20 ;i++) {	//红
			Color_show_1(1,i, RainbowRED,Lightness);
			HAL_Delay(50);
		}
		for(i =  1; i <= 20 ;i++) {	//橙
			Color_show_1(1,i, RainbowORANGE,Lightness);
			HAL_Delay(50);
		}
		for(i = 1; i <= 20 ;i++) {	//黄
			Color_show_1(1,i, RainbowYELLOW,Lightness);
			HAL_Delay(50);
		}
		for(i = 1; i <= 20 ;i++) {	//绿
			Color_show_1(1,i, RainbowGREEN,Lightness);
			HAL_Delay(50);
		}
		for(i = 1; i <= 20 ;i++) {	//青
			Color_show_1(1,i, RainbowCyan,Lightness);
			HAL_Delay(50);
		}
		for(i = 1; i <= 20 ;i++) {	//蓝
			Color_show_1(1,i, RainbowBLUE,Lightness);
			HAL_Delay(50);
		}
		for(i = 1; i <= 20 ;i++) {	//紫
			Color_show_1(1,i, RainbowPURPLE,Lightness);
			HAL_Delay(50);
		}

}

3. main.c

uint8_t g_Send_ok = 0;//DMA发送标志位,0:灯组颜色发送前的复位码;1:发送灯组颜色编码;2:灯组颜色发送完成后的复位码;3:暂停DMA发送并将g_Send_ok置0,准备下一次颜色编码发送。

extern uint32_t WS2812_Rst[300]; //复位码缓冲区

extern uint32_t Pixel_Buf[Pixel_NUM][RGB_NUM];

void HAL_TIM_PWM_PulseFinishedCallback(TIM_HandleTypeDef *htim)
{
	if(g_Send_ok==1)
	{
		HAL_TIM_PWM_Start_DMA(&htim1, TIM_CHANNEL_1,Pixel_Buf,21*24);
		g_Send_ok = 2;
	}
	else if(g_Send_ok == 2)
	{
		g_Send_ok = 3;
		HAL_TIM_PWM_Start_DMA(&htim1, TIM_CHANNEL_1,WS2812_Rst,300);
	}
	else if(g_Send_ok == 3)
	{
		g_Send_ok = 0;
		HAL_TIM_PWM_Stop_DMA(&htim1, TIM_CHANNEL_1);
	}
}

四、总结

  1. 网上查找资料。
    比如借鉴很多其他与本文相关的文章,总之目的只有一个:解决遇到的问题。

  2. 学会分析芯片手册,找关键,会看图,会分析时序。
    比如本次用到的灯珠芯片就是手册有问题,导致灯组的第一个灯显示异常,最后发现其原因是因为每一次发送灯组颜色编码时没有发送复位码,所以导致第一个灯珠接收信号异常;其次灯珠芯片的时序也有问题,从目前网上找到的最新手册上看。
    WS2812B芯片手册在这里插入图片描述在这里插入图片描述

  3. 针对时序芯片的调试一定要会用示波器,实在不行一定要有逻辑分析仪帮助调试,要不然很难查找问题根源。时序真的很麻烦,不是难,是麻烦!

五、PWM波形模拟发送和定时器接收方法

1.PWM波形模拟发送

将下面注释的两条语句打开,则可以只发送一次相同数量的PWM波形;不打开可以一直发送相同数量的PWM波形,每次间隔10S。时间和PWM波占空比可以根据自己的需求进行更改。

#define Led_Delay  10  //状态响应延时
#define PWM_FINISH 20 //定时周期为50ms时,发送完成时间为5s

typedef struct {
	uint8_t Pwm_Wave_Num;//PWM波数量(需要扩大两倍)
	uint8_t Change_New_Stauts;//新的状态
	uint8_t Change_Old_Stauts;//上一个状态
	uint8_t Pwm_Wave_Count;PWM波计数
	uint8_t Change_Stauts_time;//
	uint16_t Pwm_Send_Finish_Count;
}S_LED_CTRL;

if(htim->Instance==TIM1)
	{
		if(gLed_Ctrl.Pwm_Send_Finish_Count > 0)
			gLed_Ctrl.Pwm_Send_Finish_Count--;
		else gLed_Ctrl.Change_Stauts_time++;
//		if((gLed_Ctrl.Pwm_Wave_Num!=0)&&(gLed_Ctrl.Change_Old_Stauts!=gLed_Ctrl.Change_New_Stauts))
		if(gLed_Ctrl.Pwm_Wave_Num!=0)
		{
			gLed_Ctrl.Pwm_Wave_Count++;
			if(gLed_Ctrl.Pwm_Wave_Count==gLed_Ctrl.Pwm_Wave_Num)
			{
				HAL_GPIO_WritePin(gLed_Ctrl.Dir_Pin.pGpio,gLed_Ctrl.Dir_Pin.Gpio_Pin,GPIO_PIN_RESET);
				gLed_Ctrl.Pwm_Wave_Num = 0;
				gLed_Ctrl.Pwm_Wave_Count = 0;
//				gLed_Ctrl.Change_Old_Stauts= gLed_Ctrl.Change_New_Stauts;
			}
			else
			{
				if(gLed_Ctrl.Pwm_Wave_Count%2)
				{
					HAL_GPIO_WritePin(gLed_Ctrl.Dir_Pin.pGpio,gLed_Ctrl.Dir_Pin.Gpio_Pin,GPIO_PIN_SET);
				}
				else
				{
					HAL_GPIO_WritePin(gLed_Ctrl.Dir_Pin.pGpio,gLed_Ctrl.Dir_Pin.Gpio_Pin,GPIO_PIN_RESET);
				}
			}
		}
	}

2.定时器接收PWM波

typedef struct {
	uint8_t PwmNum_Counter;//PWM计数接收值
	uint8_t PwmNum_Record;//PWM计数中间记录值
	uint8_t Pwm_Code;//PWM计数最终值
}S_SYSTEM_RUN_CTRL;


void HAL_GPIO_EXTI_Falling_Callback(uint16_t GPIO_Pin)
{
	if(GPIO_Pin==GPIO_PIN_4)//PWM波形接收
	{
		System_Run_Ctrl.PwmNum_Counter++;
		TimerPWM_Counter=0;
	}
	__HAL_GPIO_EXTI_CLEAR_FALLING_IT(GPIO_PIN_4);
}

void HAL_TIM_PeriodElapsedCallback(TIM_HandleTypeDef *htim)
{		
	if(htim->Instance == TIM3)//定时器定时50ms
	{
		TimerPWM_Counter++;
		if((TimerPWM_Counter>3)&&(System_Run_Ctrl.PwmNum_Counter==System_Run_Ctrl.PwmNum_Record))
		{
			System_Run_Ctrl.Pwm_Code=System_Run_Ctrl.PwmNum_Record;
			System_Run_Ctrl.PwmNum_Counter=0;
			System_Run_Ctrl.PwmNum_Record=0;
			TimerPWM_Counter=0;
		}
		else
		{
			System_Run_Ctrl.PwmNum_Record=System_Run_Ctrl.PwmNum_Counter;
		}
   }
}

参考博客

如何使用STM32F103C8T6驱动WS2812(PWM+DMA)
STM32驱动WS2812B-2020 RGB彩灯(二)
【STM32F4系列】【HAL库】【自制库】WS2812(软件部分)(PWM+DMA)
RGB亮度调节

  • 28
    点赞
  • 50
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
您好!要使用STM32定时器输出PWM来驱动无源蜂鸣器,可以按照以下步骤进行操作: 1. 配置定时器:选择一个合适的定时器和通道,将定时器配置为PWM输出模式。 2. 初始化定时器:设置定时器的预分频值和重载值,以确定PWM的频率和占空比。 3. 配置GPIO:将蜂鸣器连接到相应的GPIO引脚上。 4. 启动定时器:使能定时器开始产生PWM。 下面是一个简单的示例代码,用于在STM32上配置定时器2通道1输出PWM来驱动无源蜂鸣器: ```c #include "stm32f4xx.h" void TIM2_PWM_Init(void) { TIM_TimeBaseInitTypeDef TIM_TimeBaseStructure; TIM_OCInitTypeDef TIM_OCInitStructure; GPIO_InitTypeDef GPIO_InitStructure; // 使能定时器2和GPIOA的时钟 RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM2, ENABLE); RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOA, ENABLE); // 配置GPIO引脚为复用功能 GPIO_InitStructure.GPIO_Pin = GPIO_Pin_0; 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_NOPULL; GPIO_Init(GPIOA, &GPIO_InitStructure); // 配置GPIO引脚与定时器2通道1的映射关系 GPIO_PinAFConfig(GPIOA, GPIO_PinSource0, GPIO_AF_TIM2); // 配置定时器2的基本参数 TIM_TimeBaseStructure.TIM_Period = 1000 - 1; // 设置重载值,决定PWM的频率 TIM_TimeBaseStructure.TIM_Prescaler = 84 - 1; // 设置预分频值,决定定时器的时钟频率 TIM_TimeBaseStructure.TIM_ClockDivision = TIM_CKD_DIV1; TIM_TimeBaseStructure.TIM_CounterMode = TIM_CounterMode_Up; TIM_TimeBaseInit(TIM2, &TIM_TimeBaseStructure); // 配置定时器2通道1为PWM输出模式 TIM_OCInitStructure.TIM_OCMode = TIM_OCMode_PWM1; TIM_OCInitStructure.TIM_OutputState = TIM_OutputState_Enable; TIM_OCInitStructure.TIM_Pulse = 500; // 设置脉冲宽度,决定PWM的占空比 TIM_OCInitStructure.TIM_OCPolarity = TIM_OCPolarity_High; TIM_OC1Init(TIM2, &TIM_OCInitStructure); // 启动定时器2 TIM_Cmd(TIM2, ENABLE); } ``` 以上代码假设您的无源蜂鸣器连接到了STM32的PA0引脚上,使用的是STM32F4系列芯片。您可以根据实际情况进行相应的修改。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值