嵌入式外设集 -- RGB灯珠模块(WS2812B)
WS2812B是一种流行的可编程LED模块,通常被称为“NeoPixel”。它是一种智能LED灯模块,具有以下主要特点和功能:1. 集成性:WS2812B模块集成了一个RGB LED和一个控制芯片,这意味着每个模块都包含了完整的LED驱动电路,而不需要外部元件。2. 可编程:每个WS2812B模块都可以通过单个数据线进行编程和控制。通过发送适当的数据信号,您可以精确地控制LED的颜色和亮度。3.
目录
关注微信公众号--星之援工作室 发送关键字(WS2812B)
一、模块介绍
WS2812B是一种流行的可编程LED模块,通常被称为“NeoPixel”。它是一种智能LED灯模块,具有以下主要特点和功能:
1. 集成性:WS2812B模块集成了一个RGB LED和一个控制芯片,这意味着每个模块都包含了完整的LED驱动电路,而不需要外部元件。
2. 可编程:每个WS2812B模块都可以通过单个数据线进行编程和控制。通过发送适当的数据信号,您可以精确地控制LED的颜色和亮度。
3. 链接性:多个WS2812B模块可以链接在一起,形成一个LED灯带、LED矩阵或其他形状,以创建更大的光效。
4. 低电压工作:WS2812B模块通常在5V电源下工作,但也有3.3V版本。这使得它们适用于各种不同的电子项目。
5. 简化控制:WS2812B模块的控制相对简单,因为它们只需要一个数据信号线来控制。这使得它们易于与微控制器(如Arduino)或单片机等嵌入式系统集成。
6. RGB控制:每个WS2812B模块都包含一个红色(R)、绿色(G)和蓝色(B)LED,允许您混合这些颜色来创建各种颜色效果。
7. 亮度控制:您可以通过调整数据信号中的亮度值来控制LED的亮度级别,从而创建不同的光效。
WS2812B模块广泛用于各种应用,如彩色LED照明、LED艺术装置、LED显示屏和互动装置。它们的灵活性和易于控制使它们成为电子爱好者和制造商的热门选择,用于创建令人惊叹的LED项目。要使用WS2812B模块,您通常需要了解如何编程控制它们,以及如何提供适当的电源和连接。
二、资料获取
关注微信公众号--星之援工作室 发送关键字(WS2812B)
➡️🫡🫡🫡🫡🫡🫡🫡🫡➡️
三、 实验器材
1.单片机:STM32F103C8T6
2.元器件:8灯珠的WS2812B
3.端口占用:WS2812B -- PA2
四、实现效果
跑马灯效果,渐变色
五、主要代码
WS2812B.c
主要使用 PWM和DMA 进行控制
#include "WS2812B.h"
uint16_t LED_BYTE_Buffer[24]; //发送数组缓存
uint32_t LED_WORD_Buffer[8];
void WS2812B_TIM_init(void)
{
TIM_TimeBaseInitTypeDef TIM_TimeBaseStructure;
TIM_OCInitTypeDef TIM_OCInitStructure;
GPIO_InitTypeDef GPIO_InitStructure;
DMA_InitTypeDef DMA_InitStructure;
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE);
/* GPIOA Configuration: TIM2 Channel 1 as alternate function push-pull */
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_2;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_Init(GPIOA, &GPIO_InitStructure);
RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM2, ENABLE);
/* Compute the prescaler value */
//PrescalerValue = (uint16_t) (SystemCoreClock / 24000000) - 1;
/* Time base configuration */
TIM_TimeBaseStructure.TIM_Period = 89; // 800kHz
TIM_TimeBaseStructure.TIM_Prescaler = 0;
TIM_TimeBaseStructure.TIM_ClockDivision = 0;
TIM_TimeBaseStructure.TIM_CounterMode = TIM_CounterMode_Up;
TIM_TimeBaseInit(TIM2, &TIM_TimeBaseStructure);
/* PWM1 Mode configuration: Channel1 */
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_OC3Init(TIM2, &TIM_OCInitStructure);
/* configure DMA */
/* DMA clock enable */
RCC_AHBPeriphClockCmd(RCC_AHBPeriph_DMA1, ENABLE);
/* DMA1 Channel6 Config */
DMA_DeInit(DMA1_Channel2);
DMA_InitStructure.DMA_PeripheralBaseAddr = (uint32_t)&TIM2->CCR3; // physical address of Timer 3 CCR1
DMA_InitStructure.DMA_MemoryBaseAddr = (uint32_t)LED_BYTE_Buffer; // this is the buffer memory
DMA_InitStructure.DMA_DIR = DMA_DIR_PeripheralDST; // data shifted from memory to peripheral
DMA_InitStructure.DMA_BufferSize = 24;
DMA_InitStructure.DMA_PeripheralInc = DMA_PeripheralInc_Disable;
DMA_InitStructure.DMA_MemoryInc = DMA_MemoryInc_Enable; // automatically increase buffer index
DMA_InitStructure.DMA_PeripheralDataSize = DMA_PeripheralDataSize_HalfWord;
DMA_InitStructure.DMA_MemoryDataSize = DMA_MemoryDataSize_HalfWord;
DMA_InitStructure.DMA_Mode = DMA_Mode_Normal; // stop DMA feed after buffer size is reached
DMA_InitStructure.DMA_Priority = DMA_Priority_High;
DMA_InitStructure.DMA_M2M = DMA_M2M_Disable;
DMA_Init(DMA1_Channel2, &DMA_InitStructure);
/* TIM3 CC1 DMA Request enable */
/* 只能使用通道1 TIMx_UP */
TIM_DMACmd(TIM2, TIM_DMA_Update, ENABLE);
}
void send_Data(uint32_t rgb)
{
uint8_t r = (rgb&0xff0000)>>16;
uint8_t g = (rgb&0x00ff00)>>8;
uint8_t b = (rgb&0xff);
for(uint16_t i=0;i<8;i++){
LED_BYTE_Buffer[i] = (0x80&g)>0?TIMING_ONE:TIMING_ZERO;g <<= 1;
}
for(uint16_t i=0;i<8;i++){
LED_BYTE_Buffer[8 + i] = (0x80&r)>0?TIMING_ONE:TIMING_ZERO;r <<= 1;
}
for(uint16_t i=0;i<8;i++){
LED_BYTE_Buffer[16 + i] = (0x80&b)>0?TIMING_ONE:TIMING_ZERO;b <<= 1;
}
DMA_SetCurrDataCounter(DMA1_Channel2, 24); // load number of bytes to be transferred
DMA_Cmd(DMA1_Channel2, ENABLE); // enable DMA channel 6
TIM_Cmd(TIM2, ENABLE); // enable Timer 3
while(!DMA_GetFlagStatus(DMA1_FLAG_TC2)) ; // wait until transfer complete
TIM_Cmd(TIM2, DISABLE); // disable Timer 3
DMA_Cmd(DMA1_Channel2, DISABLE); // disable DMA channel 6
DMA_ClearFlag(DMA1_FLAG_TC2); // clear DMA1 Channel 6 transfer complete flag
}
WS2812B.h
#ifndef __WS2812B_H
#define __WS2812B_H
#include "stm32f10x.h"
#define TIMING_ONE 50 //T1H 1码
#define TIMING_ZERO 25 //T0L 0码
void WS2812B_TIM_init(void);
void send_Data(uint32_t rgb);
#endif //__WS2812B_H
main.c
#include "sys.h"
#include "delay.h"
#include "usart.h"
#include "WS2812B.h"
#include <stdlib.h>
uint32_t *RGB;//颜色列表数组
void LED_Init(void);
void PWM_Init(void);
uint32_t set_Color_Loop(uint8_t color_length);
void out_RGB(uint16_t data_Max_Length,uint16_t colorwidth);
int main(void)
{
NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2);
uart_init(115200);
delay_init();
LED_Init();
WS2812B_TIM_init();
uint32_t len = set_Color_Loop(32);//每两种颜色之间的位数
while(1)
{
out_RGB(len,8);//8个灯,每次输出8个数据
delay_ms(80);
}
}
/**
* colorwidth 每次获取多少位
*/
void out_RGB(uint16_t data_Max_Length,uint16_t colorwidth)
{
static uint32_t rgb_position = 0;
for(uint32_t i = 0;i < colorwidth; i++){
uint16_t c = (rgb_position + i) % data_Max_Length;//末尾颜色数据结束时将首位的收据填充到数组
send_Data(RGB[c]);
}
rgb_position ++;
rgb_position %= data_Max_Length;
}
/**
* color_length 每种颜色渐变最大长度,总颜色长度:color_length * 3
* return 颜色数据总长度
*/
uint32_t set_Color_Loop(uint8_t color_length)
{
RGB = (uint32_t*)malloc(color_length*3*sizeof(uint32_t));//分配数组大小,(所有渐变色颜色长度)
color_length -= 1;
for(uint8_t i=0;i<=color_length;i++)
{
RGB[i] = (((0xff/color_length)*i)<<8)|((0xff/color_length)*(color_length-i)); //蓝到绿
RGB[color_length + 1 +i] = (((0xff/color_length)*i)<<16)|((0xff/color_length)*(color_length-i))<<8; //绿到红
RGB[((color_length+1)*2) +i] = ((0xff/color_length)*i)|((0xff/color_length)*(color_length-i))<<16; //红到蓝
}
return color_length*3;
}
void LED_Init()
{
GPIO_InitTypeDef GPIO_InitTypeStruct;
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOE|RCC_APB2Periph_GPIOB,ENABLE);
GPIO_InitTypeStruct.GPIO_Mode = GPIO_Mode_Out_PP;
GPIO_InitTypeStruct.GPIO_Pin = GPIO_Pin_5;
GPIO_InitTypeStruct.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_Init(GPIOE,&GPIO_InitTypeStruct);
GPIO_InitTypeStruct.GPIO_Pin = GPIO_Pin_5;
GPIO_Init(GPIOB,&GPIO_InitTypeStruct);
GPIOB->ODR ^= GPIO_Pin_5;
GPIOE->ODR ^= GPIO_Pin_5;
}
六、参考
完整代码请关注卫星公众号进行获取和咨询
开放原子开发者工作坊旨在鼓励更多人参与开源活动,与志同道合的开发者们相互交流开发经验、分享开发心得、获取前沿技术趋势。工作坊有多种形式的开发者活动,如meetup、训练营等,主打技术交流,干货满满,真诚地邀请各位开发者共同参与!
更多推荐
所有评论(0)