区块链技术博客
www.b2bchain.cn

STM32使用HAL_DMA_PWM方式驱动WS2812全彩灯珠

这篇文章主要介绍了STM32使用HAL_DMA_PWM方式驱动WS2812全彩灯珠的讲解,通过具体代码实例进行17100 讲解,并且分析了STM32使用HAL_DMA_PWM方式驱动WS2812全彩灯珠的详细步骤与相关技巧,需要的朋友可以参考下https://www.b2bchain.cn/?p=17100

本文实例讲述了2、树莓派设置连接WiFi,开启VNC等等的讲解。分享给大家供大家参考文章查询地址https://www.b2bchain.cn/7039.html。具体如下:

文章目录

  • 一、WS2812全彩灯珠原理与驱动方式
  • 二、STM32使用HAL库配置DMA方式输出PWM控制WS2812

一、WS2812全彩灯珠原理与驱动方式

关于WS2812系列灯珠的介绍这里就不讲了,网上资源一大堆,还没找到教程的可以参考这篇文章:STM32驱动WS2812D全彩LED。
这里还是把时序波形图附上,方便后面讲解。

STM32使用HAL_DMA_PWM方式驱动WS2812全彩灯珠
驱动WS2812的关键就在于一是要输出800KHz的方波,二是要精确控制每个周期内高低电平时间从而输出能够被WS2812识别的逻辑1和0。之后只需要定义一个输出缓冲数组,往数组里边填数字就行了。关键就在于选择何种方式输出方波。总结一下:

(1)最简单最笨的方法,软件延时实现高低电平翻转,STM32F1系列都有72MHz的主频,F7系列达到216MHz,输出800KHz的方波还是绰绰有余,使用的时候需要用示波器或逻辑分析仪去监视,毕竟软件延时不准。

(2)使用STM32的PWM连续传送模式,也就是DMA输出PWM,可以输出高精度的方波,且脉冲数量、占空比可调。

(3)使用STM32的硬件SPI传送数据,同样可以达到高精度。

显然是不推荐方法一的,精度无法保证不说,还极大的浪费CPU资源,如果程序就只是单独驱动WS2812还好说,如果有其他任务的话CPU不能啥也不干光在那延时吧。复杂系统中WS2812必然只是作为指示灯或者附加的炫彩效果,这样独占CPU资源?项目可以黄了。

所以推荐使用方法二和方法三。本文采用HAL库DMA输出PWM方式控制WS2812。

关于DMA输出PWM的原理与工程配置请参考在下的上一篇文章:STM32F1/F7使用HAL库DMA方式输出PWM详解(输出精确数量且可调周期与占空比)

二、STM32使用HAL库配置DMA方式输出PWM控制WS2812

在上一篇STM32F1/F7使用HAL库DMA方式输出PWM详解(输出精确数量且可调周期与占空比)工程的基础上,添加WS2812.c与WS2812.h文件到工程中。

首先是PWM的配置,本例程使用的STM32F767芯片,主频216MHz,配置T4_CH2来驱动WS2812。分频为0即不分频,周期为 1/108M = 9.26ns,自动重装载值为135,则周期为1.25us,频率800KHz。刚好为WS2812的控制频率。

WS2812.h

#ifndef _WS2812_H #define _WS2812_H #endif  #include "main.h" #include "delay.h"  #define PIXEL_NUM  64 #define NUM (24*PIXEL_NUM + 300)        // Reset 280us / 1.25us = 224 #define WS1 100 #define WS0  35  extern uint16_t send_Buf[NUM];  void WS_Load(void); void WS_WriteAll_RGB(uint8_t n_R, uint8_t n_G, uint8_t n_B); void WS_CloseAll(void);  uint32_t WS281x_Color(uint8_t red, uint8_t green, uint8_t blue); void WS281x_SetPixelColor(uint16_t n, uint32_t GRBColor); void WS281x_SetPixelRGB(uint16_t n ,uint8_t red, uint8_t green, uint8_t blue);  uint32_t Wheel(uint8_t WheelPos); void rainbow(uint8_t wait); void rainbowCycle(uint8_t wait); 

在WS2812.c中定义一个发送缓冲区uint16_t send_Buf[NUM],NUM的值为单个灯珠的位宽(24)* 灯珠数量(PIXEL_NUM)+ 复位脉冲数。这里我给的300, 300*9.26ns > 280us, 只要满足复位时间就行了。

宏WS1和WS0分别表示输出1和0所需要设置的PWM比较值,设置为100和35可以满足时序要求。

在WS2812.h就可以写WS2812的驱动函数了,首先定义一个启动传输函数:

void WS_Load(void) { 	HAL_TIM_PWM_Start_DMA(&htim4, TIM_CHANNEL_2, (uint32_t *)send_Buf, NUM); } 

功能就是启动DMA-PWM传输。

关闭所有灯:

void WS_CloseAll(void) { 	uint16_t i; 	for (i = 0; i < PIXEL_NUM * 24; i++) 		send_Buf[i] = WS0; // 写入逻辑0的占空比 	for (i = PIXEL_NUM * 24; i < NUM; i++) 		send_Buf[i] = 0; // 占空比比为0,全为低电平 	WS_Load(); } 

设置所有灯为同一颜色:

void WS_WriteAll_RGB(uint8_t n_R, uint8_t n_G, uint8_t n_B) { 	uint16_t i, j; 	uint8_t dat[24]; 	// 将RGB数据进行转换 	for (i = 0; i < 8; i++) 	{ 		dat[i] = ((n_G & 0x80) ? WS1 : WS0); 		n_G <<= 1; 	} 	for (i = 0; i < 8; i++) 	{ 		dat[i + 8] = ((n_R & 0x80) ? WS1 : WS0); 		n_R <<= 1; 	} 	for (i = 0; i < 8; i++) 	{ 		dat[i + 16] = ((n_B & 0x80) ? WS1 : WS0); 		n_B <<= 1; 	} 	for (i = 0; i < PIXEL_NUM; i++) 	{ 		for (j = 0; j < 24; j++) 		{ 			send_Buf[i * 24 + j] = dat[j]; 		} 	} 	for (i = PIXEL_NUM * 24; i < NUM; i++) 		send_Buf[i] = 0; // 占空比比为0,全为低电平 	WS_Load(); } 

在main函数中调用WS_WriteAll_RGB()就可以点亮所有灯珠了,例如WS_WriteAll_RGB(0xFF,0,0)将所有灯设置为绿色。

下面可以实现一些酷炫的灯光效果。

设置单个灯的颜色(两个函数分别以不同格式设置颜色):

uint32_t WS281x_Color(uint8_t red, uint8_t green, uint8_t blue) { 	return green << 16 | red << 8 | blue; }  void WS281x_SetPixelColor(uint16_t n, uint32_t GRBColor) { 	uint8_t i; 	if (n < PIXEL_NUM) 	{ 		for (i = 0; i < 24; ++i) 			send_Buf[24 * n + i] = (((GRBColor << i) & 0X800000) ? WS1 : WS0); 	} }  void WS281x_SetPixelRGB(uint16_t n, uint8_t red, uint8_t green, uint8_t blue) { 	uint8_t i;  	if (n < PIXEL_NUM) 	{ 		for (i = 0; i < 24; ++i) 			send_Buf[24 * n + i] = (((WS281x_Color(red, green, blue) << i) & 0X800000) ? WS1 : WS0); 	} } 

这里我只移植了两个函数,实现彩虹灯效果:

uint32_t Wheel(uint8_t WheelPos) { 	WheelPos = 255 - WheelPos; 	if (WheelPos < 85) 	{ 		return WS281x_Color(255 - WheelPos * 3, 0, WheelPos * 3); 	} 	if (WheelPos < 170) 	{ 		WheelPos -= 85; 		return WS281x_Color(0, WheelPos * 3, 255 - WheelPos * 3); 	} 	WheelPos -= 170; 	return WS281x_Color(WheelPos * 3, 255 - WheelPos * 3, 0); }  void rainbow(uint8_t wait) { 	uint32_t timestamp = HAL_GetTick(); 	uint16_t i; 	static uint8_t j; 	static uint32_t next_time = 0;  	uint32_t flag = 0; 	if (next_time < wait) 	{ 		if ((uint64_t)timestamp + wait - next_time > 0) 			flag = 1; 	} 	else if (timestamp > next_time) 	{ 		flag = 1; 	} 	if (flag) // && (timestamp - next_time < wait*5)) 	{ 		j++; 		next_time = timestamp + wait; 		for (i = 0; i < PIXEL_NUM; i++) 		{ 			WS281x_SetPixelColor(i, Wheel((i + j) & 255)); 		} 	} 	WS_Load(); }  void rainbowCycle(uint8_t wait) { 	uint32_t timestamp = HAL_GetTick(); 	uint16_t i; 	static uint8_t j; 	static uint32_t next_time = 0;  	static uint8_t loop = 0; 	if (loop == 0) 		next_time = timestamp; 	loop = 1; //首次调用初始化  	if ((timestamp > next_time)) // && (timestamp - next_time < wait*5)) 	{ 		j++; 		next_time = timestamp + wait; 		for (i = 0; i < PIXEL_NUM; i++) 		{ 			WS281x_SetPixelColor(i, Wheel(((i * 256 / (PIXEL_NUM)) + j) & 255)); 		} 	} 	WS_Load(); } 

还有其他好玩的灯光效果大家自己开发叭。

本文转自互联网,侵权联系删除STM32使用HAL_DMA_PWM方式驱动WS2812全彩灯珠

赞(0) 打赏
部分文章转自网络,侵权联系删除b2bchain区块链学习技术社区 » STM32使用HAL_DMA_PWM方式驱动WS2812全彩灯珠
分享到: 更多 (0)

评论 抢沙发

  • 昵称 (必填)
  • 邮箱 (必填)
  • 网址

b2b链

联系我们联系我们