stm32标准库驱动ws2812灯带
·
板子是野火指南者(其他stm32也适用)
使用的库是标准库(标准库太麻烦,我要转战hal)
具体的思路是tim输出pwm波+dma内存到外设
注意:tim的dma是需要对着表选的,tim输出的pwm口也是需要看着表选的。我在这里选了tim3的ch1(A6),以及其对应的dma1通道5


我们通过查询可以得知ws2812B灯带的频率是800khz,stm32的晶振频率是72mhz,所以我们72/90等于0.8,我们对定时器进行一个分频。
我购买的1m灯带是有60个灯,所以我们创建一个数组,每个灯有三种颜色grb,每种颜色需要8位来进行控制其亮灭。
宏定义:
#define WS2812B_LED_NUM 60
#define WS2812B_RESET_LEN 42
#define WS2812B_DMA_BUF_LEN (WS2812B_LED_NUM * 24 + WS2812B_RESET_LEN)
uint16_t WS2812B_DMA_Buffer[WS2812B_DMA_BUF_LEN];
#define CODE_1 54 // 高电平时间:0.9 μs (54/72 MHz)
#define CODE_0 18 // 高电平时间:0.35 μs (18/72 MHz)
RGB_Color_TypeDef clear={0,0,0};
RGB_Color_TypeDef table[16] =
{
{254,67,101},
{76,0,10},
{249,15,173},
{128,0,32},
{158,46,36},
{184,206,142},
{227,23,13},
{178,34,34},
{255,99,71},
{99,38,18},
{255,97,0},
{21,161,201},
{56,94,15},
{50,205,50},
{160,32,240},
{218,60,90}
};
定时器处理+dma代码:
void TIM3_DMA_Init(void)
{
// 启用时钟
RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM3, ENABLE); // 启用 TIM3 时钟
RCC_AHBPeriphClockCmd(RCC_AHBPeriph_DMA1, ENABLE); // 启用 DMA1 时钟
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE); // 启用 GPIOA 时钟
// 配置 GPIOA6 为复用功能输出 (TIM3 CH1)
GPIO_InitTypeDef GPIO_InitStructure;
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_6;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP; // 复用推挽输出
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz; // 50 MHz
GPIO_Init(GPIOA, &GPIO_InitStructure);
// 配置 TIM3
TIM_TimeBaseInitTypeDef TIM_TimeBaseStructure;
TIM_TimeBaseStructure.TIM_Period = 89; // 1.25us (72 MHz / 800 kHz - 1)
TIM_TimeBaseStructure.TIM_Prescaler = 0; // 不分频
TIM_TimeBaseStructure.TIM_ClockDivision = TIM_CKD_DIV1;
TIM_TimeBaseStructure.TIM_CounterMode = TIM_CounterMode_Up;
TIM_TimeBaseInit(TIM3, &TIM_TimeBaseStructure);
// 配置 TIM3 通道 1 为 PWM 输出
TIM_OCInitTypeDef TIM_OCInitStructure;
TIM_OCInitStructure.TIM_OCMode = TIM_OCMode_PWM1;
TIM_OCInitStructure.TIM_OutputState = TIM_OutputState_Enable;
TIM_OCInitStructure.TIM_Pulse = 50; // 初始占空比为 0
TIM_OCInitStructure.TIM_OCPolarity = TIM_OCPolarity_High;
TIM_OC1Init(TIM3, &TIM_OCInitStructure);
// 启用 TIM3 的 PWM
TIM_OC1PreloadConfig(TIM3, TIM_OCPreload_Enable);
TIM_ARRPreloadConfig(TIM3, ENABLE);
TIM_Cmd(TIM3, ENABLE);
// 配置 DMA
DMA_InitTypeDef DMA_InitStructure;
DMA_DeInit(DMA1_Channel6); // TIM3_CH1 对应 DMA1 通道 6
DMA_InitStructure.DMA_PeripheralBaseAddr = (uint32_t)&TIM3->CCR1; // 外设地址
DMA_InitStructure.DMA_MemoryBaseAddr = (uint32_t)WS2812B_DMA_Buffer; // 内存地址
DMA_InitStructure.DMA_DIR = DMA_DIR_PeripheralDST; // 搬运方向:内存到外设
DMA_InitStructure.DMA_BufferSize = WS2812B_DMA_BUF_LEN; // 缓冲区大小
DMA_InitStructure.DMA_PeripheralInc = DMA_PeripheralInc_Disable; // 外设地址不自增
DMA_InitStructure.DMA_MemoryInc = DMA_MemoryInc_Enable; // 内存地址自增
DMA_InitStructure.DMA_PeripheralDataSize = DMA_PeripheralDataSize_HalfWord; // 外设数据大小 16 位
DMA_InitStructure.DMA_MemoryDataSize = DMA_MemoryDataSize_HalfWord; // 内存数据大小 16 位
DMA_InitStructure.DMA_Mode = DMA_Mode_Circular; // 循环模式
DMA_InitStructure.DMA_Priority = DMA_Priority_High; // 优先级高
DMA_InitStructure.DMA_M2M = DMA_M2M_Disable; // 禁用存储器到存储器模式
DMA_Init(DMA1_Channel6, &DMA_InitStructure);
// 启用 TIM3 的 DMA 请求
TIM_DMACmd(TIM3, TIM_DMA_CC1, ENABLE);
}
灯带代码:
void WS2812B_SetPixel(uint16_t index, RGB_Color_TypeDef color)
{
if (index >= WS2812B_LED_NUM) return;
uint16_t i, offset = index * 24;
for (i = 0; i < 8; i++)
{
WS2812B_DMA_Buffer[offset + i] = (color.G & (1 << (7 - i))) ? CODE_1 : CODE_0;
WS2812B_DMA_Buffer[offset + 8 + i] = (color.R & (1 << (7 - i))) ? CODE_1 : CODE_0;
WS2812B_DMA_Buffer[offset + 16 + i] = (color.B & (1 << (7 - i))) ? CODE_1 : CODE_0;
}
}
void WS2812B_SetAll(RGB_Color_TypeDef color)
{
uint16_t i;
for (i = 0; i < WS2812B_LED_NUM; i++)
{
WS2812B_SetPixel(i, color);
}
}
void WS2812B_Clear(void)
{
uint16_t i;
for (i = 0; i < WS2812B_DMA_BUF_LEN; i++)
{
WS2812B_DMA_Buffer[i] = 0;
}
WS2812B_TurnOnAll(clear);
}
void WS2812B_Show(void)
{
uint32_t timeout = 1000000; // 超时计数
DMA_Cmd(DMA1_Channel6, ENABLE); // 启用 DMA
TIM_Cmd(TIM3, ENABLE); // 启动 TIM3
// 检查 TIM3 和 DMA 状态
if (!(TIM3->CR1 & TIM_CR1_CEN))
{
printf("TIM3 is not running!\n");
return;
}
if (!(TIM3->DIER & TIM_DIER_CC1DE))
{
printf("TIM3 DMA request not enabled!\n");
return;
}
// 等待 DMA 传输完成或超时
while (!DMA_GetFlagStatus(DMA1_FLAG_TC6) && --timeout);
if (timeout == 0)
{
printf("DMA transfer timeout!\n");
return;
}
// 清除 DMA 标志位
DMA_ClearFlag(DMA1_FLAG_TC6);
// 禁用 DMA 和 TIM3
DMA_Cmd(DMA1_Channel6, DISABLE);
TIM_Cmd(TIM3, DISABLE);
}
void WS2812B_RunningLight(void)
{
static uint8_t position = 0;
WS2812B_Clear();
//WS2812B_SetPixel(position, 0, 255, 0); // 设置当前 LED 为绿色
position = (position + 1) % WS2812B_LED_NUM;
WS2812B_Show();
}
void WS2812B_TurnOnAll(RGB_Color_TypeDef color)
{
WS2812B_SetAll(color);
WS2812B_Show();
}
void WS2812B_TurnONE(int num,RGB_Color_TypeDef color)//前num个灯亮color
{
WS2812B_Clear();
WS2812B_Show();
for(int i=0;i<num;i++)
WS2812B_SetPixel(i,color);
WS2812B_Show();
}
灯带.h
typedef struct RGB_Color
{
int R;
int G;
int B;
}RGB_Color_TypeDef;
extern RGB_Color_TypeDef table[16];
extern RGB_Color_TypeDef bian;
void TIM3_DMA_Init(void); // 初始化 TIM3 和 DMA
void TIM5_1ms_Init(void);
void WS2812B_Clear(void); // 清空灯带
void WS2812B_Show(void); // 显示复位信号
void WS2812B_RunningLight(void);
void WS2812B_TurnOnAll(RGB_Color_TypeDef color);
void WS2812B_TurnONE(int num,RGB_Color_TypeDef color);
void liushui(int num,RGB_Color_TypeDef color);
void DelayMs(__IO uint32_t hwMill);
魔乐社区(Modelers.cn) 是一个中立、公益的人工智能社区,提供人工智能工具、模型、数据的托管、展示与应用协同服务,为人工智能开发及爱好者搭建开放的学习交流平台。社区通过理事会方式运作,由全产业链共同建设、共同运营、共同享有,推动国产AI生态繁荣发展。
更多推荐


所有评论(0)