当前位置:   article > 正文

[STM32]驱动WS2812b(TIM+DMA)_ws2812b驱动stm32程序

ws2812b驱动stm32程序

WS2812B简介:

● 智能反接保护,电源反接不会损坏IC。
● IC控制电路与LED点光源公用一个电源。
● 控制电路与RGB芯片集成在一个5050封装的元器件中,构成一个完整的外控像素点。
● 内置信号整形电路,任何一个像素点收到信号后经过波形整形再输出,保证线路波形畸变不会累加。
● 内置上电复位和掉电复位电路
● 每个像素点的三基色颜色可实现256级亮度显示,完成16777216种颜色的全真色彩显示,扫描频率不低于
400Hz/s。
● 串行级联接口,能通过一根信号线完成数据的接收与解码。
● 任意两点传传输距离在不超过5米时无需增加任何电路。
● 当刷新速率30帧/秒时,级联数不小于1024点。
● 数据发送速度可达800Kbps。

WS2812b协议

在这里插入图片描述

帧单位:

这个信号是一帧数据传输完成的标志。当你完成对灯珠的写入,需要给灯珠一个帧单位信号,灯珠才会按写入的数据亮灯,否则,灯珠只会按上一回写入的数据亮灯。(可以理解为输出更新)。

占空比:

在WS2812b中,零码的占空比为30%,一码的占空比为60%

stm32驱动WS2812b(TIMPWM+DMA)

测试环境:

使用TIM2输出比较通道1,GPIOA_Pin0

基本步骤:

● 初始化DMA,TIM,GPIO外设
● 将PWM输出GPIO设为复用推挽输出
● 将TIM时基单元的时钟来源设为内部时钟(72M),预分频器(PSC)设为1-1,自动重装器(ARR)设为90-1。

800k=72M/90
  • 1

● 开启输出比较寄存器(CRR)的影子寄存器,开启DMA请求。

示意图:

请添加图片描述

第一个灯初始化后是绿色的:

解决办法:把第一个灯单独再灭一次(给第一个灯写入:0x000000)

代码:

支持

#define Led_Num 30//灯珠数量
uint16_t WS2812_Value[24*Led_Num];//WS2812b数据存储区
  • 1
  • 2

WS2812b发送帧单位/输出更新信号函数:

void WS2812_rest()
{
	TIM_Cmd(TIM2,DISABLE);//关闭PWM输出
	GPIO_ResetBits(GPIOA,GPIO_Pin_0);//拉低DIN
	Delay_ms(1);//等待一毫秒
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6

WS2812b数据清零函数:

void WS2812_Clear()
{
	uint16_t i=0;
	for(i=0;i<(24*Led_Num);i++)//Led_Num是灯珠数量
	{
		WS2812_Value[i]=30;//把占空比数据设为零码
	}
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8

WS2812b发送数据

Num:要传输的灯珠数据的数量
void WS2812_Show(uint8_t Num)
{
	WS2812_rest();//WS2812b发送帧单位信号
	DMA_SetCurrDataCounter(DMA1_Channel5, 24*(Num+1));//(设置待转移数据个数为(24*Num+1)
	DMA_Cmd(DMA1_Channel5, ENABLE);//开启DMA转运
	TIM_Cmd(TIM2, ENABLE);//开启TIM2
	while (DMA_GetFlagStatus(DMA1_FLAG_TC5) != SET);//等待ws2812b数据传输完成
	DMA_ClearFlag(DMA1_FLAG_TC5);//清除DMA转运完成标志位
	DMA_Cmd(DMA1_Channel5, DISABLE);//关闭DMA转运
	TIM_Cmd(TIM2, DISABLE);//关闭TIM2
    WS2812_rest();//WS2812b发送帧单位信号(更新显示)
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13

WS2812b初始化函数:

void WS2812_Init()
{
	/*开启RCC时钟*/
	RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM2, ENABLE);
	RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE);
	RCC_AHBPeriphClockCmd(RCC_AHBPeriph_DMA1,ENABLE);
	/*配置GPIO*/
	GPIO_InitTypeDef GPIO_InitStructure;
	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);//将GPIOA_Pin0配置为复用推挽输出
	/*TIM2配置时基单元*/
	TIM_InternalClockConfig(TIM2);//TIM2时钟来源设为内部时钟
	
	TIM_TimeBaseInitTypeDef TIM_TimeBaseInitStructure;
	TIM_TimeBaseInitStructure.TIM_ClockDivision = TIM_CKD_DIV1;//时钟分频,不分频
	TIM_TimeBaseInitStructure.TIM_CounterMode = TIM_CounterMode_Up;//计数器模式,向上计数模式
	TIM_TimeBaseInitStructure.TIM_Period = 90 - 1;		//设为自动重装寄存器(ARR)的值为90
	TIM_TimeBaseInitStructure.TIM_Prescaler = 1 - 1;		//设为预分频器(PSC)的值为1
	TIM_TimeBaseInitStructure.TIM_RepetitionCounter = 0;//没用
	TIM_TimeBaseInit(TIM2, &TIM_TimeBaseInitStructure);
	/*配置TIM2输出比较通道一*/
	TIM_OCInitTypeDef TIM_OCInitStructure;
	TIM_OCStructInit(&TIM_OCInitStructure);//先把不需要的参数填入缺省值
	TIM_OCInitStructure.TIM_OCMode = TIM_OCMode_PWM1;//输出比较模式,PWM模式1
	TIM_OCInitStructure.TIM_OCPolarity = TIM_OCPolarity_High;//有效电平为高电平
	TIM_OCInitStructure.TIM_OutputState = TIM_OutputState_Enable;//输出使能,使能
	TIM_OCInitStructure.TIM_Pulse = 60;		//这里设置输出比较寄存器(CRR)的值无所谓
	TIM_OC1Init(TIM2, &TIM_OCInitStructure);//配置输出比较通道一
	TIM_DMACmd(TIM2, TIM_DMA_CC1, ENABLE);//输出比较通道一DMA请求开启
	TIM_OC1PreloadConfig(TIM2,TIM_OCPreload_Enable);//使用输出比较寄存器(CRR)的影子寄存器
	TIM_CtrlPWMOutputs(TIM2, ENABLE);//高级定时器才有用
	/*配置DMA1*/
	DMA_InitTypeDef DMA_InitStructure;
	DMA_InitStructure.DMA_PeripheralBaseAddr=(uint32_t)&(TIM2->CCR1);//设置&(TIM2->CCR1)为外设基地址
	DMA_InitStructure.DMA_PeripheralDataSize=DMA_PeripheralDataSize_HalfWord;//外设站点的数据宽度为16bits
	DMA_InitStructure.DMA_PeripheralInc=DMA_PeripheralInc_Disable;//外设站点的地址不自增
	DMA_InitStructure.DMA_MemoryBaseAddr=(uint32_t)WS2812_Value;//设置WS2812_Value为存储器基地址
	DMA_InitStructure.DMA_MemoryDataSize=DMA_MemoryDataSize_HalfWord;//存储器站点的数据宽度为16bits
	DMA_InitStructure.DMA_MemoryInc=DMA_MemoryInc_Enable;//存储器站点的地址自增
	DMA_InitStructure.DMA_DIR=DMA_DIR_PeripheralDST;//将外设站点设为传输目的地
	DMA_InitStructure.DMA_BufferSize=24;//没用,填什么无所谓
	DMA_InitStructure.DMA_Mode=DMA_Mode_Normal;//不使用自动重装
	DMA_InitStructure.DMA_M2M=DMA_M2M_Disable;//使用硬件触发
	DMA_InitStructure.DMA_Priority=DMA_Priority_Medium;//优先级中等
	DMA_Init(DMA1_Channel5,&DMA_InitStructure);//初始化DMA1通道5使用DMA_Init初始化。

	DMA_Cmd(DMA1_Channel5,DISABLE);//先不使能DMA,需要时再使能
	
	TIM_Cmd(TIM2, DISABLE);//先不使能TIM2,需要时再使能
	/*WS2812灯带清零*/
	DMA_SetCurrDataCounter(DMA1_Channel5, (24*Led_Num));//设置待转移数据个数为(24*灯珠个数)
	WS2812_Clear();//WS2812b数据清零
	WS2812_rest();//WS2812b发送帧单位信号
	DMA_Cmd(DMA1_Channel5,ENABLE);//开启DMA转运
	TIM_Cmd(TIM2, ENABLE);//开启TIM2
	while(DMA_GetFlagStatus(DMA1_FLAG_TC5)!= SET);//等待ws2812b数据传输完成
	DMA_Cmd(DMA1_Channel5,DISABLE);//关闭DMA转运
	DMA_ClearFlag(DMA1_FLAG_TC5);//清除DMA转运完成标志位
	TIM_Cmd(TIM2, DISABLE);//关闭TIM2
		WS2812_rest();//WS2812b发送帧单位信号
	/*解决绿灯问题*/
	DMA_SetCurrDataCounter(DMA1_Channel5, 24);/设置待转移数据个数为(24*1WS2812_Clear();//WS2812b数据清零
	WS2812_rest();//WS2812b发送帧单位信号
	DMA_Cmd(DMA1_Channel5,ENABLE);//开启DMA转运
	TIM_Cmd(TIM2, ENABLE);//开启TIM2
	while(DMA_GetFlagStatus(DMA1_FLAG_TC5)!= SET);//等待ws2812b数据传输完成
	DMA_Cmd(DMA1_Channel5,DISABLE);//关闭DMA转运
	DMA_ClearFlag(DMA1_FLAG_TC5);//清除DMA转运完成标志位
	TIM_Cmd(TIM2, DISABLE);//关闭TIM2
	WS2812_rest();//WS2812b发送帧单位信号
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
  • 59
  • 60
  • 61
  • 62
  • 63
  • 64
  • 65
  • 66
  • 67
  • 68
  • 69
  • 70
  • 71
  • 72
  • 73
  • 74
声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/你好赵伟/article/detail/643444
推荐阅读
相关标签
  

闽ICP备14008679号