STM32 HAL库 PWM+DMA 驱动WS2812B彩灯(STM32F030F4P6)

这篇具有很好参考价值的文章主要介绍了STM32 HAL库 PWM+DMA 驱动WS2812B彩灯(STM32F030F4P6)。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

谁不想拥有炫酷的小彩灯呢?WS2812B可以给你机会......

博主使用STM32驱动WS2812B主要参考了这位佬的文章,因为需求问题,采用了Cortex-M0的stm32f030f4p6(16k的flash,4k的sram)来驱动,原文中写的是stm32f103c8t6,个人认为其实区别并不是很大,需要修改部分参数即可移植(cv战士申请出战)。

STM32 HAL库 PWM+DMA 驱动WS2812B彩灯(STM32F030F4P6)

上图是我的一圈灯,一共8个,第一个LED的数据输入端接的是定时器1的通道2,想看底层原理可以去看佬的文章,本文只介绍如何移植。

CUBEMX配置

STM32 HAL库 PWM+DMA 驱动WS2812B彩灯(STM32F030F4P6)

首先选择外部石英晶振,我用的是外部12M晶振,然后配置时钟树:

STM32 HAL库 PWM+DMA 驱动WS2812B彩灯(STM32F030F4P6)

然后打开串行调试:

STM32 HAL库 PWM+DMA 驱动WS2812B彩灯(STM32F030F4P6)

 找到使用的定时器(输出PWM的),并打开定时器时钟,选定输出PWM的通道以及模式:

STM32 HAL库 PWM+DMA 驱动WS2812B彩灯(STM32F030F4P6)

重点来了:

佬的文章中说的0码PWM占空比(CCR值)算到约为28,1码PWM占空比(CCR值)算到约为58;但人家用的是f1啊,72M时钟频率的,咱的是f0,自然低人一等,只有48M时钟频率。所以在满足灯带频率固定为800KHz的前提下,略微修改即可,设置参数如下:

STM32 HAL库 PWM+DMA 驱动WS2812B彩灯(STM32F030F4P6)

STM32 HAL库 PWM+DMA 驱动WS2812B彩灯(STM32F030F4P6)

下面来简单说一下如何修改

因为PWM频率F需要为800KHz,而时钟频率固定为48MHz,根据Fpwm=Fclk / (arr+1)(psc+1),我取psc为0,取arr=59,恰好可以得到 800KHz的频率,所以arr取值为59。因此,计算出PWM0码的CCR值为:0.32 × (59+1)=19.2 ≈ 19;PWM1码的CCR值为:0.64 × (59+1)=38.4 ≈ 38;

接下来配置DMA:

STM32 HAL库 PWM+DMA 驱动WS2812B彩灯(STM32F030F4P6)

 再由于我后面切换显示需要用到定时器中断,所以用了一个定时器3的1ms中断:

STM32 HAL库 PWM+DMA 驱动WS2812B彩灯(STM32F030F4P6)

STM32 HAL库 PWM+DMA 驱动WS2812B彩灯(STM32F030F4P6) 配置好生成路径后直接生成代码即可

代码部分:

主函数头文件加个#include "ws2812b.h"即可

int main(void)
{
  /* USER CODE BEGIN 1 */

  /* USER CODE END 1 */

  /* MCU Configuration--------------------------------------------------------*/

  /* Reset of all peripherals, Initializes the Flash interface and the Systick. */
  HAL_Init();

  /* USER CODE BEGIN Init */

  /* USER CODE END Init */

  /* Configure the system clock */
  SystemClock_Config();

  /* USER CODE BEGIN SysInit */

  /* USER CODE END SysInit */

  /* Initialize all configured peripherals */
  MX_GPIO_Init();
  MX_DMA_Init();
  MX_TIM1_Init();
  MX_TIM3_Init();
  /* USER CODE BEGIN 2 */

  /* USER CODE END 2 */

  /* Infinite loop */
  /* USER CODE BEGIN WHILE */
  while (1)
  {
    /* USER CODE END WHILE */

    /* USER CODE BEGIN 3 */
	  led_loop();	//循环显示
  }
  /* USER CODE END 3 */
}

 ws2812b.h文件如下:

#ifndef __WS2812B_H__
#define __WS2812B_H__
 
#include "main.h"
 
/*这里是上文计算所得CCR的宏定义*/
#define CODE_1       (38)       //1码定时器计数次数
#define CODE_0       (19)       //0码定时器计数次数
 
/*建立一个定义单个LED三原色值大小的结构体*/
typedef struct
{
	uint8_t R;
	uint8_t G;
	uint8_t B;
}RGB_Color_TypeDef;
 
#define Pixel_NUM 8  //LED数量宏定义,这里我使用一个LED,(单词pixel为像素的意思)
 
void RGB_SetColor(uint8_t LedId,RGB_Color_TypeDef Color);//给一个LED装载24个颜色数据码(0码和1码)
void Reset_Load(void); //该函数用于将数组最后24个数据变为0,代表RESET_code
void RGB_SendArray(void);          //发送最终数组
void RGB_RED(uint16_t Pixel_Len);  //显示红灯
void RGB_GREEN(uint16_t Pixel_Len);//显示绿灯
void RGB_BLUE(uint16_t Pixel_Len); //显示蓝灯
void RGB_WHITE(uint16_t Pixel_Len);//显示白灯

void led_loop(void);
 
#endif





ws2812b.c文件如下:

#include "ws2812b.h"
#include "tim.h"
 
/*Some Static Colors------------------------------*/
const RGB_Color_TypeDef RED      = {255,0,0};   //显示红色RGB数据
const RGB_Color_TypeDef ORANGE   = {127,106,0};
const RGB_Color_TypeDef YELLOW   = {127,216,0};
const RGB_Color_TypeDef GREEN    = {0,255,0};
const RGB_Color_TypeDef CYAN	 = {0,255,255};
const RGB_Color_TypeDef BLUE     = {0,0,255};
const RGB_Color_TypeDef PURPLE	 = {238,130,238};
const RGB_Color_TypeDef BLACK    = {0,0,0};
const RGB_Color_TypeDef WHITE    = {255,255,255};
const RGB_Color_TypeDef MAGENTA  = {255,0,220};

 
/*二维数组存放最终PWM输出数组,每一行24个
数据代表一个LED,最后一行24个0代表RESET码*/
uint32_t Pixel_Buf[Pixel_NUM+1][24];       
 
/*
功能:设定单个RGB LED的颜色,把结构体中RGB的24BIT转换为0码和1码
参数:LedId为LED序号,Color:定义的颜色结构体
*/
void RGB_SetColor(uint8_t LedId,RGB_Color_TypeDef Color)
{
	uint8_t i; 
	if(LedId > Pixel_NUM)return; //avoid overflow 防止写入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
}

/*
功能:最后一行装在24个0,输出24个周期占空比为0的PWM波,作为最后reset延时,这里总时长为24*1.2=30us > 24us(要求大于24us)
*/
void Reset_Load(void)
{
	uint8_t i;
	for(i=0;i<24;i++)
	{
		Pixel_Buf[Pixel_NUM][i] = 0;
	}
}
 
/*
功能:发送数组
参数:(&htim1)定时器1,(TIM_CHANNEL_2)通道2,((uint32_t *)Pixel_Buf)待发送数组,
			(Pixel_NUM+1)*24)发送个数,数组行列相乘
*/
void RGB_SendArray(void)
{
	HAL_TIM_PWM_Start_DMA(&htim1, TIM_CHANNEL_2, (uint32_t *)Pixel_Buf,(Pixel_NUM+1)*24);
}
 
/*
功能:显示红色
参数:Pixel_Len为显示LED个数
*/
void RGB_RED(uint16_t Pixel_Len)
{
	uint16_t i;
	for(i=0;i<Pixel_Len;i++)//给对应个数LED写入红色
	{
		RGB_SetColor(i,RED);
	}
	Reset_Load();
	RGB_SendArray();
}
 
/*
功能:显示绿色
参数:Pixel_Len为显示LED个数
*/
void RGB_GREEN(uint16_t Pixel_Len)
{
	uint16_t i;
	for(i=0;i<Pixel_Len;i++)//给对应个数LED写入绿色
	{
		RGB_SetColor(i,GREEN);
	}
	Reset_Load();
	RGB_SendArray();
}
 
/*
功能:显示蓝色
参数:Pixel_Len为显示LED个数
*/
void RGB_BLUE(uint16_t Pixel_Len)
{
	uint16_t i;
	for(i=0;i<Pixel_Len;i++)//给对应个数LED写入蓝色
	{
		RGB_SetColor(i,BLUE);
	}
	Reset_Load();
	RGB_SendArray();
}
 
/*
功能:显示白色
参数:Pixel_Len为显示LED个数
*/
void RGB_WHITE(uint16_t Pixel_Len)
{
	uint16_t i;
	for(i=0;i<Pixel_Len;i++)//给对应个数LED写入白色
	{
		RGB_SetColor(i,WHITE);
	}
	Reset_Load();
	RGB_SendArray();
}
 
//也可以继续添加其他颜色,和颜色变化函数等

/*******************************************************************************/
/*									添加部分									   */

//显示指定颜色
static void rgb_show(uint32_t Pixel_Len, RGB_Color_TypeDef rgb)
{
	uint16_t i;
	for(i=0;i<Pixel_Len;i++)
	{
		RGB_SetColor(i,rgb);
	}
	Reset_Load();
	RGB_SendArray();
}

//颜色循环转换
static RGB_Color_TypeDef Wheel(uint8_t WheelPos)
{
	RGB_Color_TypeDef rgb;
	WheelPos = 255 - WheelPos;
	if (WheelPos < 85)
	{
		rgb.R = 255 - WheelPos * 3;
		rgb.G = 0;
		rgb.B = WheelPos * 3;
		return rgb;
	}
	if (WheelPos < 170)
	{
		WheelPos -= 85;
		rgb.R = 0;
		rgb.G = WheelPos * 3;
		rgb.B = 255 - WheelPos * 3;
		return rgb;
	}
	WheelPos -= 170;
	rgb.R = WheelPos * 3;
	rgb.G = 255 - WheelPos * 3;
	rgb.B = 0;
	return rgb;
}

//彩虹呼吸灯
static 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++)
		{
			RGB_SetColor(i, Wheel((i + j) & 255));
		}
	}
	RGB_SendArray();
}

//彩虹灯旋转
static 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++)
		{
			RGB_SetColor(i, Wheel(((i * 256 / (Pixel_NUM)) + j) & 255));
		}
	}
	RGB_SendArray();
}


static uint8_t rainbow_change_flag = 0;
void led_loop(void)
{
	int i;
	rgb_show(8, BLACK);		HAL_Delay(300);
	for(i = 1; i <= 8 ;i++) {	//红
		rgb_show(i, RED);		
		HAL_Delay(50);		
	}
	for(i = 1; i <= 8 ;i++) {	//橙
		rgb_show(i, ORANGE);		
		HAL_Delay(50);		
	}
	for(i = 1; i <= 8 ;i++) {	//黄
		rgb_show(i, YELLOW);		
		HAL_Delay(50);		
	}
	for(i = 1; i <= 8 ;i++) {	//绿
		rgb_show(i, GREEN);		
		HAL_Delay(50);		
	}
	for(i = 1; i <= 8 ;i++) {	//青
		rgb_show(i, CYAN);		
		HAL_Delay(50);		
	}
	for(i = 1; i <= 8 ;i++) {	//蓝
		rgb_show(i, BLUE);		
		HAL_Delay(50);		
	}
	for(i = 1; i <= 8 ;i++) {	//紫
		rgb_show(i, PURPLE);		
		HAL_Delay(50);		
	}
	
	HAL_TIM_Base_Start_IT(&htim3);	//使能定时器中断->时间:1ms
	while(1) {
		if(!rainbow_change_flag)
			rainbow(5);
		else 
			rainbowCycle(2);
	}
	
}


//定时器3中断回调函数
void HAL_TIM_PeriodElapsedCallback(TIM_HandleTypeDef *htim)
{
  /* Prevent unused argument(s) compilation warning */
  UNUSED(htim);

  /* NOTE : This function should not be modified, when the callback is needed,
            the HAL_TIM_PeriodElapsedCallback could be implemented in the user file
   */
	static uint32_t time_counter = 0;
	if(htim->Instance == TIM3) {
		if(++time_counter == 10000) {	//10s后切换
			rainbow_change_flag = ~rainbow_change_flag;
			time_counter = 0;
		}
	}
}






工程链接:https://pan.baidu.com/s/1QXB_ALAOFp-71YSm94Po-g 
提取码:0xFF

The END文章来源地址https://www.toymoban.com/news/detail-462253.html

到了这里,关于STM32 HAL库 PWM+DMA 驱动WS2812B彩灯(STM32F030F4P6)的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • [HAL]STM32 SPI+DMA驱动WS2812

    该程序是纯手敲,非Cube生成!所有代码均注释。 源码在文章后面获取 Keyword: 单线通讯、归零码、Reset、RGB顺序 RGB一共有24bit位 -相当于驱动一个灯要24bit位 -驱动若干个灯要24* n bit位,通过Reset码决定数据终止(保持) 24bit位应该如何发送? 可见: 表示低电平需要 T0H和T0L的配

    2024年02月09日
    浏览(17)
  • 【STM32F4系列】【HAL库】【自制库】WS2812(软件部分)(PWM+DMA)

    硬件介绍(PCB设计方案) 模拟时序发送 WS2812是一种异步串行通信,它每一位数据时间是ns级别的 默认是高电平状态 0码:220-380ns高电平+580-1600ns低电平 1码:580-1600ns高电平+220-380ns低电平 复位码:280us低电平 24Bit数据来代表GRB的亮度值 从高位到低位发送,分别按照G-R-B的顺序发送

    2024年02月15日
    浏览(58)
  • STM32G0 定时器PWM DMA输出驱动WS2812配置 LL库

    优点:不消耗CPU资源 缺点:占用内存较大 定时器配置 定时器通道:TIM3 CH2 分频:0 重装值:79,芯片主频64Mhz,因此PWM输出频率:64Mhz/79 ≈ 800Khz,满足芯片要求。 auto-reload preload 要关闭 output compare preload 要打开 DMA配置 外设一定要选择TIM3_UP,不要选TIM_CHx 方向是内存到外设,

    2024年02月10日
    浏览(18)
  • 关于STM32利用TIM+PWM+DMA控制WS2812

    MCU:STM32F103c8t6 开发工具:STM32CubeMX  使用板子参考原理图:STM32F103C8T6最小系统板开源链接 脉宽调制(PWM)基本原理:控制方式就是对逆变电路开关器件的通断进行控制,使输出端得到一系列幅值相等但宽度不一致的脉冲,用这些脉冲来代替正弦波或所需要的波形。也就是在输

    2024年02月01日
    浏览(19)
  • 使用STM32F103的SPI+DMA驱动ws2812 LED

    目录 前言 一、WS2812协议 1.1 数据传输编码方式:  1.2 传输的数据结构 二、驱动方式:SPI+DMA 2.1 原理介绍 2.2 SPI+DMA操作  2.3 编写代码 2.4 使用 三 总结 参考文章 主要使用的STM32F103C8T6芯片的SPI+DMA方式实现WS2812的驱动协议,总体可以看作是使用SPI来实现一种通信协议来发送信号。

    2024年02月09日
    浏览(31)
  • 软件STM32cubeIDE下STM32F1xx使用定时器(TIM8)+DMA+PWM点亮灯带WS2812-基础样例

    好长时间不调试灯带ws2812了,最近项目上,要在STM32F1上进行点灯带ws2812,虽然自己之前做了很多了,但是人有个性质,一旦某个事情做完了,你不在去惦记它了,基本会完全抛在脑后。所以才体现记录的重要性,本次在做STM32F1上验证时,即使之前有经验的情况下,还是掉坑

    2024年02月01日
    浏览(36)
  • 讲解STM32驱动WS281x灯珠的多种实现方式:普通IO、SPI+DMA、以及PWM+DMA驱动方法

    STM32作为一款高性能、功能丰富的单片机,其丰富的外设和强大的性能,使其在嵌入式领域得到了广泛的应用。本篇文章将介绍如何利用STM32驱动WS281x系列的LED灯珠。我们会使用三种不同的驱动方式进行实现:一种是普通IO方式驱动,一种是SPI+DMA方式驱动,最后一种是PWM+DMA方

    2024年02月11日
    浏览(15)
  • WS2812B 驱动程序

    : WS2812 ,STM32G030F6 说 明: 本代码用于驱动 16 个RGB LED(WS2812 )模块,可以按16个预定颜色的流水显示, 使用RT-Thread RTOS 基于STM32G030F6 最小系统板的 ws2812 驱动工程。         WS2812是一个集控制电路与发光电路于一体的外控LED光源,外形一般为5050封装,每个LED灯珠为

    2024年02月11日
    浏览(23)
  • WS2812B灯带驱动实验

    KEIL5、正点原子阿波罗STM32F767开发板 过多介绍我这里不赘述了,想了解详情的可以看看手册,直接开干。 1.1、灯带连接方式 我们只要控制DIN端就好了。 1.2、传输数据方式 简单理解就是单总线发了一串数据,第一个灯拿了第一个字节的数据,第二个灯拿个第二个字节的数据,

    2024年02月08日
    浏览(16)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包