基于stm32单片机的ws2812b跑马灯的代码 标准库
时间: 2023-10-10 11:06:31 浏览: 97
基于STM32的跑马灯
好的,以下是一个基于STM32标准库的WS2812B跑马灯代码:
```c
#include "stm32f10x.h"
#include "stm32f10x_gpio.h"
#include "stm32f10x_tim.h"
#define LED_NUM 8 // 一共有8个LED
#define LED_BUF_SIZE 24 // 每个LED有24位数据
uint8_t led_buf[LED_NUM * LED_BUF_SIZE]; // 存储LED数据的缓冲区
void TIM2_IRQHandler(void)
{
static uint16_t led_index = 0; // 当前LED的索引
static uint8_t bit_index = 0; // 当前LED数据的位索引
// 清除中断标志位
TIM_ClearITPendingBit(TIM2, TIM_IT_Update);
// 输出当前LED的数据的当前位
if(led_buf[led_index] & (1 << bit_index))
{
GPIO_SetBits(GPIOB, GPIO_Pin_12);
}
else
{
GPIO_ResetBits(GPIOB, GPIO_Pin_12);
}
// 移动位索引
bit_index++;
// 如果当前位索引超过了24位,则移动到下一个LED
if(bit_index >= LED_BUF_SIZE)
{
bit_index = 0;
led_index++;
// 如果当前LED索引超过了LED数量,则重置到第一个LED
if(led_index >= LED_NUM)
{
led_index = 0;
}
}
}
void ws2812b_send_data()
{
// 关闭定时器
TIM_Cmd(TIM2, DISABLE);
// 等待至少50us
for(uint32_t i = 0; i < 1000; i++);
// 开始发送数据
for(uint32_t i = 0; i < LED_NUM * LED_BUF_SIZE; i++)
{
// 发送当前位的数据
if(led_buf[i / 8] & (1 << (i % 8)))
{
// 发送1
TIM_SetCompare1(TIM2, 60); // 占空比约为0.7us
}
else
{
// 发送0
TIM_SetCompare1(TIM2, 20); // 占空比约为0.3us
}
// 等待至少250ns
for(uint32_t j = 0; j < 4; j++);
// 发送下一位数据
TIM_SetCompare1(TIM2, 0); // 占空比约为0.5us
// 等待至少250ns
for(uint32_t j = 0; j < 4; j++);
}
// 等待至少50us
for(uint32_t i = 0; i < 1000; i++);
// 重新打开定时器
TIM_Cmd(TIM2, ENABLE);
}
void set_color(uint8_t led_index, uint8_t r, uint8_t g, uint8_t b)
{
// 计算当前LED的起始索引
uint16_t index = led_index * LED_BUF_SIZE;
// 设置颜色数据
for(uint8_t i = 0; i < 8; i++)
{
led_buf[index + i] = ((g & (1 << (7 - i))) ? 0xF8 : 0x80) |
((r & (1 << (7 - i))) ? 0x07 : 0x00);
}
for(uint8_t i = 0; i < 8; i++)
{
led_buf[index + 8 + i] = ((b & (1 << (7 - i))) ? 0xF8 : 0x80) |
((g & (1 << (7 - i))) ? 0x07 : 0x00);
}
for(uint8_t i = 0; i < 8; i++)
{
led_buf[index + 16 + i] = ((r & (1 << (7 - i))) ? 0xF8 : 0x80) |
((b & (1 << (7 - i))) ? 0x07 : 0x00);
}
}
int main(void)
{
// 初始化GPIO
GPIO_InitTypeDef GPIO_InitStructure;
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_12;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_Init(GPIOB, &GPIO_InitStructure);
// 初始化定时器
TIM_TimeBaseInitTypeDef TIM_TimeBaseStructure;
TIM_OCInitTypeDef TIM_OCInitStructure;
TIM_TimeBaseStructure.TIM_Period = 84 - 1; // 计数器自动重载值
TIM_TimeBaseStructure.TIM_Prescaler = 0; // 预分频器
TIM_TimeBaseStructure.TIM_ClockDivision = 0; // 时钟分频
TIM_TimeBaseStructure.TIM_CounterMode = TIM_CounterMode_Up;
TIM_TimeBaseInit(TIM2, &TIM_TimeBaseStructure);
TIM_OCInitStructure.TIM_OCMode = TIM_OCMode_PWM1;
TIM_OCInitStructure.TIM_OutputState = TIM_OutputState_Enable;
TIM_OCInitStructure.TIM_Pulse = 0;
TIM_OCInitStructure.TIM_OCPolarity = TIM_OCPolarity_High;
TIM_OC1Init(TIM2, &TIM_OCInitStructure);
TIM_Cmd(TIM2, ENABLE);
// 初始化LED数据
for(uint8_t i = 0; i < LED_NUM; i++)
{
set_color(i, 0xFF, 0x00, 0x00); // 设置为红色
}
// 开始发送数据
while(1)
{
ws2812b_send_data();
}
}
```
这个代码使用了定时器和PWM输出来控制WS2812B的数据传输。具体来说,定时器的计数器自动重载值为84,预分频器为0,时钟分频为0,这样每个计数器周期的时长为1us。PWM输出的占空比用来控制WS2812B数据的高电平时间的长短,从而控制数据传输。在数据传输过程中,需要发送24位数据给每个LED,每位数据的高电平时间长短不同,从而控制数据传输。在代码中,使用一个缓冲区来存储LED数据,定时器中断的处理函数会按照一定的顺序输出缓冲区中的数据。发送完一帧数据后,需要等待至少50us,然后才能重新发送下一帧数据。注意到WS2812B的数据传输非常精确,需要在发送每个位数据之间添加至少250ns的延时,否则可能会出现传输错误。
阅读全文