WS2812B彩灯 STM32HAL库开发:PWM+DMA(stm32f103c8t6)

本文介绍如何使用STM32F103C8T6单片机结合PWM与DMA控制WS2812B RGB LED,实现不同颜色的显示效果。文章详细解释了信号传输原理及配置步骤。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

目录

一、摘要

二、WS2812B介绍

三、CUBEMX配置

四、程序介绍(KEIL编译器)

五、数据手册


一、摘要

        1、本文使用示例单片机型号为stm32f103c8t6,RGB型号为WS2812B

        2、主要实现功能是实现用PWM+DMA使RGB_LED亮起不同颜色的灯光;

        3、目的:简单调通该型号RGB_LED,方便后续改编使用,希望各位读者可以依次做出更炫酷的效果。为了便于更好理解数据手册和单片机配置的关联,第二部分也放入了CUBEMX的部分配置图片

        4、优点:DMA转运,硬件自动数据搬运(由内存到外设),减少CPU资源占用,第一次使用DMA可以先大致看一下DMA相关知识,这会让你很容易理解下面的一些配置

二、WS2812B介绍

       ( 为了便于更好理解数据手册和单片机配置的关联,第二部分也放入了CUBEMX的部分)

 依据方向标识与单片机接好线路

注:VDD接5V

        DIN接单片机PWM输出引脚(本文中接PA8引脚,即TIM1_PWM输出通道1)

        DOUT接下一个LED的DIN引脚

此处重装值就是定时器一个周期从0计到89计90个数(后面介绍为什么是89)                                                          

通过控制PWM占空比发送0码和1码,额定周期为1.25us,则频率为800Khz

0码PWM占空比:

(0码高电平时间)/(周期)--->  0.4 / 1.25 = 0.32 

  用占空比乘以定时器重装值加一就是0码的CCR值(代表PWM高电平计数个数)--->

  0.32 * (89+1) = 28.8(取28,实测不可以高于28,但23到28都可以)

1码PWM占空比:

同理计算:(1码高电平时间)/ (周期)---> 0.8 / 1.25 = 0.64

(占空比)*(重置值+1)= CCR ---> 0.64 * 1.25 = 57.6(取58)

每一个LED的R、G、B分别由八位数据控制颜色浓度,(每种颜色浓度有0~255档,理论上RGB就可以组成256的3次方中颜色组合)即每个LED需要24BIT数据,那么需要发送数据的总长度则为(要控制LED数量 n)*(24),每个LED保存24BIT将剩余位传给后面LED。全部数据发送完成后要继续发送大于24us的低电平作为RESET_CODE等才可以点亮。

三、CUBEMX配置

 

时钟树配置

  

定时器配置

PWM频率:

Fpwm =Tclk / ((arr+1)*(psc+1))(单位:Hz)

上面提到数据传送频率为800Khz,Tclk为72Mhz,我们这里设置psc = 0,arr= 89,得到频率刚好为800Khz。

占空比:

duty circle = TIM3->CCR1 / arr(单位:%)

TIM3->CCR1  用户设定值(即上述0码和1码占空比计算所得CCR)

PWM-DMA配置

 1:DMA配置

 2:添加一个DMA

 3:选择定时器1的通道一外设

四、程序介绍(KEIL编译器)

RGB.H

#ifndef __RGB_H__
#define __RGB_H__

#include "main.h"

/*这里是上文计算所得CCR的宏定义*/
#define CODE_1       (58)       //1码定时器计数次数
#define CODE_0       (25)       //0码定时器计数次数

/*建立一个定义单个LED三原色值大小的结构体*/
typedef struct
{
	uint8_t R;
	uint8_t G;
	uint8_t B;
}RGB_Color_TypeDef;

#define Pixel_NUM 1  //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);//显示白灯


#endif

 RGB.C

#include "RGB.h"
#include "tim.h"

/*Some Static Colors------------------------------*/
const RGB_Color_TypeDef RED      = {255,0,0};   //显示红色RGB数据
const RGB_Color_TypeDef GREEN    = {0,255,0};
const RGB_Color_TypeDef BLUE     = {0,0,255};
const RGB_Color_TypeDef SKY      = {0,255,255};
const RGB_Color_TypeDef MAGENTA  = {255,0,220};
const RGB_Color_TypeDef YELLOW   = {127,216,0};
const RGB_Color_TypeDef OEANGE   = {127,106,0};
const RGB_Color_TypeDef BLACK    = {0,0,0};
const RGB_Color_TypeDef WHITE    = {255,255,255};

/*二维数组存放最终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_1)通道1,((uint32_t *)Pixel_Buf)待发送数组,
			(Pixel_NUM+1)*24)发送个数,数组行列相乘
*/
void RGB_SendArray(void)
{
	HAL_TIM_PWM_Start_DMA(&htim1, TIM_CHANNEL_1, (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();
}

//也可以继续添加其他颜色,和颜色变化函数等

 main.c

( 在此处调用RGB头文件)

( 在while(1)里写入想要的颜色变化,这里现象为红绿蓝白四种颜色1秒变化一次)

五、数据手册

百度云盘 WS2812B

阿里云盘 WS2812B

ok,简单讲到这里,希望你可以以次添加更多功能复杂的函数,做出更炫酷更有趣的LED颜色变幻,如果有什么地方感觉说的不清晰可以留言评论


<think>好的,我现在需要帮助用户找到基于STM32F103C8T6芯片使用HAL实现流水灯的程序示例。首先,我要回忆一下STM32开发的基本知识,尤其是HAL的使用。用户提到的是流水灯,也就是让多个LED依次点亮,形成流动的效果。这通常涉及到GPIO的控制,可能涉及到定时器或者简单的延时函数。 接下来,我需要考虑用户的具体需求。他们使用的是STM32F103C8T6,也就是常说的Blue Pill开发板。这款芯片属于STM32F1系列,基于Cortex-M3内核。HAL是ST官方提供的硬件抽象层,可以简化外设的配置和操作。流水灯的实现需要配置GPIO口为输出模式,然后依次控制各个引脚的电平状态,形成灯光的流动效果。 用户可能已经查阅了一些资料,比如引用中提到的资源。例如,引用[2]提到基于寄存器和固件的LED流水灯实现,而引用[4]则涉及使用STM32CubeMX配置中断控制LED。不过用户需要的是HAL的示例,所以可能需要结合这些资源中的方法,但改用HAL的函数。 首先,我需要考虑使用HAL的基本步骤。通常,使用STM32CubeMX进行初始化配置,生成代码框架,然后在生成的代码中添加用户逻辑。流水灯的关键在于循环中依次开关不同的LED,并添加适当的延时。HAL提供了HAL_Delay()函数,可以用于简单的延时,但要注意在中断中的使用可能会有问题。不过用户可能只需要基本的阻塞式延时,所以这里可以使用HAL_Delay。 接下来,需要确定GPIO的配置。假设用户将三个LED连接到GPIO引脚,例如PC13、PB14、PB15,这些引脚需要在CubeMX中配置为输出模式。然后在代码中初始化这些引脚,并在主循环中依次设置高低电平,配合延时。 可能还需要考虑是否使用推挽输出,以及是否需要配置上拉或下拉电阻。但通常LED的控制只需要推挽输出即可,具体硬件连接可能不同,需要用户根据实际电路调整。 另外,引用[3]提到示例代码,可能用户需要参考具体的代码结构。根据经验,HAL的程序结构通常包括系统初始化、外设初始化,然后进入主循环执行用户代码。因此,示例程序的结构应该包括这些部分。 可能的步骤: 1. 使用STM32CubeMX创建工程,选择STM32F103C8T6芯片。 2. 配置所需的GPIO引脚为输出模式(比如PC13、PB14、PB15)。 3. 生成初始化代码。 4. 在main函数的主循环中添加代码,依次控制各个GPIO的电平,并使用HAL_Delay进行延时。 此外,用户可能需要了解如何通过HAL_GPIO_WritePin函数来控制引脚的电平状态。例如,HAL_GPIO_WritePin(GPIOC, GPIO_PIN_13, GPIO_PIN_SET); 这样可以将PC13设置为高电平,假设LED是低电平点亮的话,可能需要设置为低电平,具体取决于硬件连接。 还需要注意时钟的配置,在CubeMX中必须确保相关GPIO端口的时钟已使能,否则无法控制引脚。不过CubeMX生成的代码会自动处理这些配置,所以用户不需要手动配置时钟。 可能出现的问题包括LED不亮,可能的原因包括引脚配置错误、时钟未使能、硬件连接问题(如限流电阻、共阳/共阴接法等)。在示例代码中,用户需要根据实际硬件调整引脚和电平设置。 总结,用户的示例代码应包括以下几个部分: - GPIO初始化(由CubeMX生成) - 主循环中的流水灯逻辑,依次开关LED并延时 此外,用户可能需要参考引用中的项目,比如引用[1]中的PWM+DMA控制WS2812B彩灯,但这里的需求更简单,只是普通流水灯,所以不需要复杂的PWMDMA配置。不过,如果用户希望更高效的定时控制,可以考虑使用定时器中断,但基本示例可能先使用HAL_Delay。 最后,用户可能需要调试和下载程序到开发板,观察流水灯效果,并根据需要调整延时时间或引脚顺序。</think>以下是为您整理的基于STM32F103C8T6芯片使用HAL实现流水灯的完整解决方案: ### 一、硬件配置要求 1. 开发板:STM32F103C8T6(Blue Pill) 2. LED连接方式:共阳/共阴均可(示例代码采用共阴接法) 3. 推荐引脚配置:PC13(LED1)、PB14(LED2)、PB15(LED3) ### 二、开发环境搭建 1. 安装STM32CubeMX(版本≥6.0) 2. 安装Keil MDK-ARM(或STM32CubeIDE) 3. 安装HAL软件包:STM32CubeF1 Firmware Package ### 三、CubeMX配置步骤 1. 新建工程选择`STM32F103C8Tx` 2. SYS配置:`Debug`选择`Serial Wire` 3. RCC配置:`High Speed Clock (HSE)`选择`Crystal/Ceramic Resonator` 4. GPIO配置: - PC13: GPIO_Output(模式:Output Push Pull,标签:LED1) - PB14: GPIO_Output(标签:LED2) - PB15: GPIO_Output(标签:LED3) 5. 时钟树配置:设置主频为72MHz(默认配置) ```c /* 自动生成的GPIO初始化代码(在gpio.c中) */ void MX_GPIO_Init(void) { GPIO_InitTypeDef GPIO_InitStruct = {0}; __HAL_RCC_GPIOC_CLK_ENABLE(); __HAL_RCC_GPIOB_CLK_ENABLE(); /* PC13配置 */ GPIO_InitStruct.Pin = GPIO_PIN_13; GPIO_InitStruct.Mode = GPIO_MODE_OUTPUT_PP; GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_LOW; HAL_GPIO_Init(GPIOC, &GPIO_InitStruct); /* PB14-PB15配置 */ GPIO_InitStruct.Pin = GPIO_PIN_14|GPIO_PIN_15; HAL_GPIO_Init(GPIOB, &GPIO_InitStruct); } ``` ### 四、核心代码实现 ```c /* 主循环代码片段 */ while (1) { // LED1亮,其他灭 HAL_GPIO_WritePin(GPIOC, GPIO_PIN_13, GPIO_PIN_RESET); HAL_GPIO_WritePin(GPIOB, GPIO_PIN_14, GPIO_PIN_SET); HAL_GPIO_WritePin(GPIOB, GPIO_PIN_15, GPIO_PIN_SET); HAL_Delay(200); // LED2亮,其他灭 HAL_GPIO_WritePin(GPIOC, GPIO_PIN_13, GPIO_PIN_SET); HAL_GPIO_WritePin(GPIOB, GPIO_PIN_14, GPIO_PIN_RESET); HAL_GPIO_WritePin(GPIOB, GPIO_PIN_15, GPIO_PIN_SET); HAL_Delay(200); // LED3亮,其他灭 HAL_GPIO_WritePin(GPIOC, GPIO_PIN_13, GPIO_PIN_SET); HAL_GPIO_WritePin(GPIOB, GPIO_PIN_14, GPIO_PIN_SET); HAL_GPIO_WritePin(GPIOB, GPIO_PIN_15, GPIO_PIN_RESET); HAL_Delay(200); } ``` ### 五、进阶优化方案 1. **定时器中断驱动**:使用TIM2定时器替代HAL_Delay(推荐) ```c // 在main.c中添加 void HAL_TIM_PeriodElapsedCallback(TIM_HandleTypeDef *htim) { if(htim->Instance == TIM2){ // 在此实现状态切换 } } ``` 2. **PWM调光**:实现呼吸灯效果(参考WS2812B彩灯控制方案[^1]) 3. **DMA控制**:适用于复杂灯光模式(参考DMA应用示例) ### 六、调试技巧 1. 使用Keil逻辑分析仪观察GPIO波形[^2] 2. 通过串口打印调试信息 3. LED限流电阻建议使用220Ω-1kΩ
评论 57
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

宏定义_

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值