ICode9

精准搜索请尝试: 精确搜索
首页 > 其他分享> 文章详细

STM32F 驱动WS2812B (2) PWM+DMA

2021-04-29 15:33:31  阅读:187  来源: 互联网

标签:24 DMA pwm WORD TIM RESET PWM data WS2812B


STM32F412通过TIM PWM DMA方式驱动WS2812B


一、基本思路

原理说明在《STM32F 驱动WS2812B (1) IO口》已经说明

STEP1:

实现逻辑1跟逻辑0的脉冲,这里用PWM修改占空比方式实现。

STEP2:

将数据通过DMA发送到TIM外设,发送前面80us的低电平RESET信号以及每个灯珠的24个0 code或者1 code的脉冲。

二、实现

1.配置CUBEMX

使用的芯片是STM32F412,选用TIM2的第2个通道输出PWM信号,因为是100M的时钟,所以分频系数为0即为不分频,然后125的counter period就是125个时钟周期为一个脉冲周期,所以一个脉冲周期为(1/100M)*125=0.00000125s=0.00125ms=1.25us。

PWM的Pulse先设置成0,这个值用来控制占空比,刚才计算出来125个时钟周期就是1250ns,那么一个时钟周期就是1个时钟周期就是10ns,所以根据ws2812b的手册说明的0code的高电平时400ns,低电平850ns,对应这个PULSE应该设置成40,表示40个时钟周期即为400ns,同理1code的高电平为800ns所以这个值应该设置成80即可。

在这里插入图片描述

2.配置DMA

单击Add添加一个DMA通道,这里因为时TIM2的2通道,所以把这个通道打开,然后在Direction的地方选择memory to peripheral表示从内存到外设,这里的内存就是定义的数据数组,外设就是TIM产生PWM的比较寄存器。其他的如下图所示,这边推荐Data Width都选择Word即可。
在这里插入图片描述

3.配置GPIO口

把GPIO的上下拉改成下拉,这样在不输出PWM的时候时低电平
在这里插入图片描述

4.修改工程

点击生成代码,生成MDK的代码。每个灯珠需要24个脉冲,每个脉冲的占空比根据0,1变化,在这之前需要一个reset信号,是大于50us的低电平。我们使用80us的低电平,之前通过cubemx配置的输出PWM的周期是1.25us,所以这个80us的低电平需要使用64个占空比为0的脉冲形成,所以先定义一个数组用来存放灯的数据,这边以1个灯为例:

uint32_t pwm_data[RESET_WORD+24*LED_NUM+DUMMY_WORD]={0};

这里的RESET_WORD定义成64,LED_NUM定义成1,DUMMY_WORD定义是1,这个DUMMY_WORD如果不加波形会出现问题,这个稍后再说,即1个灯需要64+24+1个脉冲。初始化为0,这样我们修改64个数据之后的数据不会影响前面的reset的数据。

假设需要亮蓝色,蓝色的RGB为0x0000FF,WS2812B的传输顺序是GRB,并且是先传输高bit,所以要先把G的颜色值放入数组,需要一个for循环,循环7次从高到低将bit对应的占空比填入数组,0对应的应该是40,1对应的是80:

for(j=15;j>=8;j--)//G
{	
	(((color>>j)&0x01)==1)?(pwm_data[RESET_WORD+(i*24+(15-j))]=80):(pwm_data[RESET_WORD+(i*24+(15-j))]=40);
}

因为第一个传输的是G数据,所以从RBG_COLOR的第15位bit15开始判断是否为1如果是1就用占空比数值80表示,一直判断到bit8。这里从RGB_COLOR的bit15到bit8应该存放到pwm_data的对应灯的bit0-bit7。

之后是R传输,同理:

for(j=23;j>=16;j--)//R
	(((color>>j)&0x01)==1)?(pwm_data[RESET_WORD+(i*24+(31-j))]=80):(pwm_data[RESET_WORD+(i*24+(31-j))]=40);

最后是B传输,同理:

for(j=7;j>=0;j--)//B			(((color>>j)&0x01)==1)?(pwm_data[RESET_WORD+(i*24+(23-j))]=80):(pwm_data[RESET_WORD+(i*24+(23-j))]=40);

把这个数组填好之后就可以开启DMA传输了:

HAL_TIM_PWM_Start_DMA(&htim2,TIM_CHANNEL_2,&pwm_data[0], RESET_WORD+24*LED_NUM+DUMMY_WORD);

这里的传输数据的个数就是整个数组的个数。

传输已经开始了,在传输结束之后会进入到回调函数,我们引用这个回调函数来处理结束的时候停止DMA传输,否则会一直传输。

void HAL_TIM_PWM_PulseFinishedCallback(TIM_HandleTypeDef *htim)
{
  HAL_TIM_PWM_Stop_DMA(&htim2, TIM_CHANNEL_2);
}

这样一轮发送点亮等LED灯的DMA就完成了。

三、验证

1.抓取0 code波形

将LED_NUM设置成1,并且将全部的占空比的参数设置成40 也就是0 code的脉冲
在这里插入图片描述

2.抓取1 code波形

将LED_NUM设置成1,并且将全部的占空比的参数设置成80 也就是1 code的脉冲
在这里插入图片描述

3.验证发送脉冲个数以及时间

正常情况下如果将LED_NUM设置成1,并且将全部的占空比的参数设置成80 也就是1 code的脉冲,应该会发送24个脉冲,如下是抓到的波形:
在这里插入图片描述
如果在发送的数据中不加入dummy word就会出现如下的波形,通过计算脉冲个数可以看出脉冲个数不对,而且波形变得很奇怪:
在这里插入图片描述
这个原因还没搞清楚,如果有知道原因的可以在下面评论下,互相探讨下

一些想法

1:如果不设置GPIO的上下拉方式,在抓取的波形观察,会发现在没有传输的时候一直为高电平。
2:在设置DMA传输的时候一定需要注意传输的方向是Memory To Peripheral
3:DMA的Data Width要跟程序中定义的数组的类型一致。

部分关键代码

Main关键源码如下:

#define DUMMY_WORD 1
#define RESET_WORD 64
#define LED_NUM 2
uint32_t pwm_data[RESET_WORD+24*LED_NUM+DUMMY_WORD]={0};
void ws2812b_show_dot(uint8_t dot_index,uint32_t color)
{
	int i=0,j=0;
	for(i=0;i<LED_NUM;i++)
	{
		if(i==dot_index)
		{
			for(j=15;j>=8;j--)//G
				(((color>>j)&0x01)==1)?(pwm_data[RESET_WORD+(i*24+(15-j))]=80):(pwm_data[RESET_WORD+(i*24+(15-j))]=40);
			for(j=23;j>=16;j--)//R
				(((color>>j)&0x01)==1)?(pwm_data[RESET_WORD+(i*24+(31-j))]=80):(pwm_data[RESET_WORD+(i*24+(31-j))]=40);
			for(j=7;j>=0;j--)//B
				(((color>>j)&0x01)==1)?(pwm_data[RESET_WORD+(i*24+(23-j))]=80):(pwm_data[RESET_WORD+(i*24+(23-j))]=40);			
		}
		else
		{
			for(j=0;j<24;j++)
			{
				pwm_data[RESET_WORD+i*24+j]=40;
			}			
		}
	}
	HAL_TIM_PWM_Start_DMA(&htim2,TIM_CHANNEL_2,&pwm_data[0],RESET_WORD+24*LED_NUM+DUMMY_WORD);
}
int main(void)
{
  HAL_Init();
  uint32_t RGB_COLOR=0x0000ff;
  SystemClock_Config();
  MX_GPIO_Init();
  MX_DMA_Init();
  MX_TIM2_Init();
  while (1)
  {
	  ws2812b_show_dot(0,RGB_COLOR);
	  HAL_Delay(500);
	  ws2812b_show_dot(1,RGB_COLOR);
	  HAL_Delay(500);
  }
}

void HAL_TIM_PWM_PulseFinishedCallback(TIM_HandleTypeDef *htim)
{
  HAL_TIM_PWM_Stop_DMA(&htim2, TIM_CHANNEL_2);
}

标签:24,DMA,pwm,WORD,TIM,RESET,PWM,data,WS2812B
来源: https://blog.csdn.net/wangbuu/article/details/116264731

本站声明: 1. iCode9 技术分享网(下文简称本站)提供的所有内容,仅供技术学习、探讨和分享;
2. 关于本站的所有留言、评论、转载及引用,纯属内容发起人的个人观点,与本站观点和立场无关;
3. 关于本站的所有言论和文字,纯属内容发起人的个人观点,与本站观点和立场无关;
4. 本站文章均是网友提供,不完全保证技术分享内容的完整性、准确性、时效性、风险性和版权归属;如您发现该文章侵犯了您的权益,可联系我们第一时间进行删除;
5. 本站为非盈利性的个人网站,所有内容不会用来进行牟利,也不会利用任何形式的广告来间接获益,纯粹是为了广大技术爱好者提供技术内容和技术思想的分享性交流网站。

专注分享技术,共同学习,共同进步。侵权联系[81616952@qq.com]

Copyright (C)ICode9.com, All Rights Reserved.

ICode9版权所有