STM32CUBEMX使用PWM+DMA驱动WS2812
- 首先在stm32cubemx中设置pwm和dma。我设置了TIM1的CH1为PWM引脚
- 编写DMA响应函数,即PWM DMA完成数据发送后的回调函数
// PWM DMA 完成回调函数
void HAL_TIM_PWM_PulseFinishedCallback(TIM_HandleTypeDef *htim)
{
HAL_TIM_PWM_Stop_DMA(&htim1, TIM_CHANNEL_1);
}
- 编写DMA载入数据的代码,运行后将会把数组(内存)中的数据写入到WS2812中。要更新WS2812的数据,只需要更改数组的内容即可。在此处LED_N为led数量
uint16_t led_buffer[LED_N*24 + 50]; // 最后50为reset信号,全设置为0
extern TIM_HandleTypeDef htim1;
// 启动DMA载入数据
void WS_Load(void)
{
HAL_TIM_PWM_Start_DMA(&htim1, TIM_CHANNEL_1, (uint32_t *)led_buffer, LED_N*24+50);
}
- 编写一些改变数组数据的函数,例如全红,全蓝之类的。
/ 关闭所有LED灯
void WS_Clear(void)
{
uint16_t i;
for(i=0; i<LED_N*24; i++)
led_buffer[i] = LED_0; // 写入逻辑0的占空比
for(; i<LED_N*24+50; i++)
led_buffer[i] = 0; // 占空比比为0,全为低电平
WS_Load();
}
// 全部led灯设置成一样的亮度,其中RGB分别设置亮度
// WS2812的写入顺序是GRB,高位在前面
void WS_Write_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++)
{
if((n_G&0x80) == 0)
dat[i] = LED_0;
else
dat[i] = LED_1;
n_G <<= 1;
}
for(i=0; i<8; i++)
{
if((n_R&0x80) == 0)
dat[i+8] = LED_0;
else
dat[i+8] = LED_1;
n_R <<= 1;
}
for(i=0; i<8; i++)
{
if((n_B&0x80) == 0)
dat[i+16] = LED_0;
else
dat[i+16] = LED_1;
n_B <<= 1;
}
for(i=0; i<LED_N; i++)
{
for(j=0; j<24; j++)
{
led_buffer[i*24 + j] = dat[j];
}
}
for(i=LED_N*24; i<LED_N*24+50; i++)
{
led_buffer[i] = 0; // 占空比比为0,全为低电平
}
WS_Load();
}
// 单独打开所有的LED R,亮度为n
void WS_Write_R(uint8_t n_R)
{
WS_Write_RGB(n_R, 0, 0);
}
void WS_Write_G(uint8_t n_G)
{
WS_Write_RGB(0, n_G, 0);
}
void WS_Write_B(uint8_t n_B)
{
WS_Write_RGB(0, 0, n_B);
}
- 在main.c中调用这些函数即可。可以将改变WS2812数组的代码放入到定时器中,以固定的频率进行亮度和颜色改变
最后,WS2812的工作电压最好超过3.5V,当使用3.3V电压时,亮度和颜色已经不准确(正)了,在5V的时候亮度很正
使用PWM+DMA的可以使STM32不必一直处理这个,只需改变数组内容即可。同时时序控制也非常方便