一.WS2812B简介
WS2812B是一种数字可编程的LED灯条,可以使用单个数据线进行通讯控制LED灯的颜色和亮度。每个WS2812B都有一个唯一的地址,可以通过单个数据线进行级联。
二.WS2812B参数简介
三. WS2812B数据通讯简介
1.级联电路
2.数据传输
该芯片是通过数据传输的时间来判断数据是0码还是1码的。
此时就会有PWM的频率计算:
3.级联的数据传输
第一个数据缓存
·第一个24位有第一个模块接收并缓存
·第二个24位会被第一个模块转发到第二个模块上并缓存
·第三个24位会被第一个和第二个转发到第三个模块上并缓存
·第四个24位......
·第N个24位......
复位信号
第二个数据缓存
·第一个24位有第一个模块接收并缓存
·第二个24位会被第一个模块转发到第二个模块上并缓存
·第三个24位会被第一个和第二个转发到第三个模块上并缓存
·第四个24位......
·第N个24位......
复位信号
............
四.WS2812B驱动程序简介
这里我们使用PWM+DMA的方式驱动WS2812B。这里我们用的江协科技的程序。
DMA驱动程序:
#include "stm32f10x.h"
void (*DMA1_Handler)(void); //也要开中断 数据转运+DMA 没用ADC
void DMA1_Init(uint32_t MemoryBaseAddr)//MemoryBaseAddr是WS2812B_Bit数组地址
{
RCC_AHBPeriphClockCmd(RCC_AHBPeriph_DMA1, ENABLE);
DMA_DeInit(DMA1_Channel2);
DMA_InitTypeDef DMA_InitStructure;
DMA_InitStructure.DMA_PeripheralBaseAddr = (uint32_t)(&TIM2->CCR1);//起始地址
DMA_InitStructure.DMA_MemoryBaseAddr = MemoryBaseAddr;//存放地址
DMA_InitStructure.DMA_DIR = DMA_DIR_PeripheralDST;//传输方向:双向 存储点到外设
DMA_InitStructure.DMA_BufferSize = 0;//缓存区大小,传输计数器大于0,触发源有触发信号,DMA使能
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;
DMA_InitStructure.DMA_Mode = DMA_Mode_Normal;//不使用自动重装
DMA_InitStructure.DMA_Priority = DMA_Priority_Medium;
DMA_InitStructure.DMA_M2M = DMA_M2M_Disable;//硬件触发
DMA_Init(DMA1_Channel2, &DMA_InitStructure);
NVIC_InitTypeDef NVIC_InitStructure;
NVIC_InitStructure.NVIC_IRQChannel = DMA1_Channel2_IRQn;
NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 1;
NVIC_InitStructure.NVIC_IRQChannelSubPriority = 1;
NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
NVIC_Init(&NVIC_InitStructure);
DMA_ITConfig(DMA1_Channel2, DMA_IT_TC, ENABLE);//DMA中断
DMA_Cmd(DMA1_Channel2, ENABLE);
}
void DMA1_SetIRQHandler(void (*IRQHandler)(void))
{
DMA1_Handler=IRQHandler;
}
void DMA1_Start(uint16_t DataNumber) //调运函数,连续转运
{
DMA_Cmd(DMA1_Channel2,DISABLE);
DMA_SetCurrDataCounter(DMA1_Channel2,DataNumber);
DMA_Cmd(DMA1_Channel2,ENABLE);
}
void DMA1_Channel2_IRQHandler(void) //DMA开中断12
{
if(DMA_GetFlagStatus(DMA1_FLAG_TC2))
{
DMA1_Handler();
DMA_ClearFlag(DMA1_FLAG_TC2);
}
}
这里我们通过定时器中断驱动pwm
PWN TIM2驱动:
#include "stm32f10x.h" //哪个函数不认识看芯片手册
void TIM2_Init(void) //TIM2开PWM的发送到DMA PA0输出
{
RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM2,ENABLE); //开启定时器2时钟
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE);//开启复用引脚时钟
GPIO_InitTypeDef GPIO_InitStructure; //GPIO
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP; //复用推挽输出 定时器控制引脚
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_0;
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_Init(GPIOA,&GPIO_InitStructure);
TIM_DeInit(TIM2); //初始化时基单元
TIM_TimeBaseInitTypeDef TIM_TimeBaseStructure;
TIM_TimeBaseStructure.TIM_Period = 90-1; //ARR周期 32视频里定时中断讲过,搞懂 90公式试出来的,然后一算
TIM_TimeBaseStructure.TIM_Prescaler = 0; //PSC预分频器
TIM_TimeBaseStructure.TIM_ClockDivision = TIM_CKD_DIV1;
TIM_TimeBaseStructure.TIM_CounterMode = TIM_CounterMode_Up;
TIM_TimeBaseInit(TIM2, &TIM_TimeBaseStructure);
TIM_OCInitTypeDef TIM_OCInitStructure; //初始化输出比较单元
TIM_OCInitStructure.TIM_OCMode = TIM_OCMode_PWM1;
TIM_OCInitStructure.TIM_OutputState = TIM_OutputState_Enable;
TIM_OCInitStructure.TIM_Pulse = 0; //CCR捕获比较器 运行中设置
TIM_OCInitStructure.TIM_OCPolarity = TIM_OCPolarity_High;
TIM_OC1Init(TIM2, &TIM_OCInitStructure);
TIM_CtrlPWMOutputs(TIM2, ENABLE);
TIM_DMAConfig(TIM2,TIM_DMABase_CCR1,TIM_DMABurstLength_1Transfer); //DMA请求传输
TIM_DMACmd(TIM2,TIM_DMA_Update,ENABLE); //寄存器位传输对象
TIM_Cmd(TIM2, DISABLE);
}
void TIM2_Cmd(FunctionalState NewState)
{
TIM_Cmd(TIM2, NewState);
}
void TIM2_SetCompare1(uint16_t Value) //主输出使能,TM2为高级定时器,所以这里加上
{
TIM_SetCompare1(TIM2,Value);
}
WS2812B驱动:
#include "stm32f10x.h"
#include "TIM2.h"
#include "DMA1.h"
//#define WS2812B_LED_QUANTITY 32 //灯珠数量
#define WS2812B_LED_QUANTITY 30 //灯珠数量
//定义数组 类型符 数组名[常量]
uint32_t WS2812B_Buf[WS2812B_LED_QUANTITY]; //0xGGRRBB 正常的数据口输入,然后配置就行,绿红蓝总共24位,只有32
uint16_t WS2812B_Bit[24*WS2812B_LED_QUANTITY+1];//灯珠数量*24bit+1 颜色位数+复位信号 根据数据手册可知还要加复位信号0(正常的0),因此需要写入 num24+reset(reset=?自己调) 位。
uint8_t WS2812B_Flag;
void WS2812B_IRQHandler(void);
void WS2812B_Init(void)
{
DMA1_SetIRQHandler(WS2812B_IRQHandler);//把WS2812B_IRQHandler赋给DMA1_Handler
DMA1_Init((uint32_t) (&WS2812B_Bit));//灯位量的地址存放到DMA 这时DMA就是灯珠的量 强制类型转换 不足的高位补0
TIM2_Init();//定时器
} //用TIM2开PWM 送给DMA
void WS2812B_ClearBuf(void)//把LED的RGB数组值清零,全部输出0码,变成黑色,即不发光
{
uint8_t i;
for(i=0;i<WS2812B_LED_QUANTITY;i++)
{
WS2812B_Buf[i]=0x000000;//24位的二进制数 6位的16进制数
}
}
void WS2812B_SetBuf(uint32_t Color) //设置显示相同的颜色
{
uint8_t i;
for(i=0;i<WS2812B_LED_QUANTITY;i++)
{
WS2812B_Buf[i]=Color;//BUF是颜色,设置成同一个颜色,初始统一设置成不发光
}
}
void WS2812B_UpdateBuf(void) //30和60对应不同的高电平时间也对应了时序的0码和1码 占空比控制亮灭 CCR和AAR+1共同决定的
{
uint8_t i,j;
for(j=0;j<WS2812B_LED_QUANTITY;j++) //遍历每个LED
{
for(i=0;i<24;i++) //遍历每个LED的颜色数据的每一位 10000000 00000000 00000000
{
if(WS2812B_Buf[j]&(0x800000>>i)){WS2812B_Bit[j*24+i+1]=60;}//0x800000为二进制的1加23个0,>>:右移
else{WS2812B_Bit[j*24+i+1]=30;} //每一位,取值范围是0到23,所以再加1
}
}
DMA1_Start(24*WS2812B_LED_QUANTITY+1);
TIM2_Cmd(ENABLE);
while(WS2812B_Flag==0);
WS2812B_Flag=0;
}
void WS2812B_IRQHandler(void) //定时器2 开PWM 直接就是根据时序占空比设置控制灯条
{
TIM2_SetCompare1(0);//pwm设置
TIM2_Cmd(DISABLE);//关闭定时器
WS2812B_Flag=1;
}
五.江协科技程序文件
程序代码 链接:https://pan.baidu.com/s/12t9pRkyJYpP5MXVNhYFELg 提取码:5gkc
原来视频 链接[WS2812B] 七彩流水灯/呼吸灯/三级调光照明 *附赠程序*_哔哩哔哩_bilibili
视频讲解 链接:2ws2812b外设配置_哔哩哔哩_bilibili
标签:TIM2,DMA,TIM,DMA1,InitStructure,WS2812b,PWM,WS2812B From: https://blog.csdn.net/m0_59247951/article/details/140227843