STM32F103C8T6+PWM+DMA驱动 WS2812灯带

#STM32+PWM+DMA驱动 WS2812灯带

#文章目录

  • 1.理论:
  • 2代码:

理论:

1.WS2812参考数据手册:https://wenku.baidu.com/view/0925958fba68a98271fe910ef12d2af90342a80e.html
在这里插入图片描述

在这里插入图片描述
在这里插入图片描述

2.使用STM32F103C8T6驱动WS2812灯带,每个灯由24位组成三种颜色RGB,每8位控制一种颜色,颜色值0x00-0xFF。想要点亮一个灯需要将24位颜色值写入WS2812寄存器,如若点亮num个灯则需要写入 num24 位。根据数据手册可知还要加复位信号0(正常的0),因此需要写入 num24+reset(reset=?自己调) 位。

3.怎样发送num24+reset位数据呢? 由于WS2812的 0码 和 1码 并不是我们正常的 0 和 1,他是高低电平占不同的占空比,因此需要用PWM来模拟 0码 和 1码。当需要发送 0码 或 1码 时,我们只需要改变定时器的CCRx寄存器,来改变占空比,PWM频率800KHZ。
4.DMA则是搬运,将事先弄好的 num
24+reset位 对应的占空比值传送给 TIMx的CCRx寄存器。这样PWM输出的就是对应的颜色值了。

在这里插入图片11111111111111111111111111111111111111111111111111111111111描述
5.我之前使用的是TIM1_CH2对应的DMA1_Channel3通道行不通,因为这个找了好久原因。后面试了TIM1_UP对应的DMA1_Channel7通道就可以了。我也不知道为什么一定要用TIM1_UP事件,看网上也有用了非TIMx_UP事件的。

6.一般会出现的问题 1.PWM配置那里没配必须的那两句。2.DMA配置传输长度与定义的数组类型不符。3.DMA事件得要用TIMx_UP事件和其对应得通道。4.复位信号时间给的不够

代码:

/*-----------------------------------------  timer1 输出pwm --------------------------------------------------------*/
u16 LED_Buffer[2400];  //存放CCR值得数组
u8  color_buff[3] = {0x00,0x00,0xFF}//定义颜色GRB

#define    ONE     44    //我的对应CCR=44 为1码
#define    ZERO    22   //我的对应CCR=22 为0码

void Timer1_init(void)
{
	    TIM_TimeBaseInitTypeDef          TIM_TimeBaseStructure;
	    TIM_OCInitTypeDef                TIM_OCInitStructure;
	    GPIO_InitTypeDef                 GPIO_InitStructure;
	    DMA_InitTypeDef                  DMA_InitStructure;
		
		//clock
	    RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA | RCC_APB2Periph_GPIOB, ENABLE);
	    RCC_APB2PeriphClockCmd(RCC_APB2Periph_TIM1, ENABLE);
		RCC_AHBPeriphClockCmd(RCC_AHBPeriph_DMA1, ENABLE);
		
		//GPIO
	    GPIO_InitStructure.GPIO_Pin    = GPIO_Pin_9;
	    GPIO_InitStructure.GPIO_Mode   = GPIO_Mode_AF_PP;
	    GPIO_InitStructure.GPIO_Speed  = GPIO_Speed_50MHz;
	    GPIO_Init(GPIOA, &GPIO_InitStructure);	
	    
	    //TIME
	    TIM_TimeBaseStructure.TIM_Period = 80-1; // 800kHZ   我时钟是64MHZ  64/80=800KHZ
	    TIM_TimeBaseStructure.TIM_Prescaler = 0;
	    TIM_TimeBaseStructure.TIM_ClockDivision = 0;
	    TIM_TimeBaseStructure.TIM_CounterMode = TIM_CounterMode_Up;
	    TIM_TimeBaseInit(TIM1, &TIM_TimeBaseStructure);
	    
		//PWM
	    TIM_OCInitStructure.TIM_OCMode = TIM_OCMode_PWM1;
	    TIM_OCInitStructure.TIM_OutputState = TIM_OutputState_Enable;
		TIM_OCInitStructure.TIM_OutputNState=TIM_OutputNState_Disable;//高级定时器这句一定要配
	    TIM_OCInitStructure.TIM_Pulse = 0; //CCRX寄存器值
	    TIM_OCInitStructure.TIM_OCPolarity = TIM_OCPolarity_High;
	    TIM_OC2Init(TIM1, &TIM_OCInitStructure);
		TIM_CtrlPWMOutputs(TIM1,ENABLE);
		TIM_OC2PreloadConfig(TIM1,TIM_OCPreload_Enable);//使能预加载值,这句一定要配,不然时序会乱
	
	    //DMA
	    DMA_DeInit(DMA1_Channel5);
	    DMA_InitStructure.DMA_PeripheralBaseAddr = (u32)&(TIM1->CCR2);//设置CCR值
	    DMA_InitStructure.DMA_MemoryBaseAddr = (u32)LED_Buffer;	//存放CCR值的数组
	    DMA_InitStructure.DMA_DIR = DMA_DIR_PeripheralDST;						
	    DMA_InitStructure.DMA_BufferSize = 240;
	    DMA_InitStructure.DMA_PeripheralInc = DMA_PeripheralInc_Disable;
	    DMA_InitStructure.DMA_MemoryInc = DMA_MemoryInc_Enable;					
	    DMA_InitStructure.DMA_PeripheralDataSize = DMA_PeripheralDataSize_HalfWord;//这里传输的是多少位,根据你定义的存放CCR值得数据类型,我是u16
	    DMA_InitStructure.DMA_MemoryDataSize = DMA_MemoryDataSize_HalfWord;//这里传输的是多少位,根据你定义的存放CCR值得数据类型,我是u16
	    DMA_InitStructure.DMA_Mode = DMA_Mode_Normal;							
	    DMA_InitStructure.DMA_Priority = DMA_Priority_High;
	    DMA_InitStructure.DMA_M2M = DMA_M2M_Disable;
	    DMA_Init(DMA1_Channel5, &DMA_InitStructure);
	   
	   	//这里我之前用的是TIM_DMA_CC2  对应的DMA1_Channel3通道行不通 ,具体为何我也不知道,后面使用TIM_DMA_Update 对应的DMA1_Channel5
	    TIM_DMACmd(TIM1, TIM_DMA_Update, ENABLE);
		TIM_Cmd(TIM1, DISABLE);
}


/*---------- 组包 将颜色位 转变成CCR值  发送颜色顺序GRB高位先发   我这里所有的灯都一样的颜色要不一样的自己改下面的-----------*/
void WS2812_led_send(uint8_t *color, uint16_t len)
{
    uint8_t i;
    uint16_t memaddr;
    uint16_t buffersize;
	memset(LED_Buffer,0,2400);
    buffersize = (len*24)+400;	//颜色位数+复位信号
    memaddr = 0;				
    while(len)
    {
        for(i=0; i<8; i++) // GREEN 
        {
            LED_Buffer[memaddr] = ((color[0]<<i) & 0x80) ? ONE:ZERO;
            memaddr++;
        }

        for(i=0; i<8; i++) // RED
        {
            LED_Buffer[memaddr] = ((color[1]<<i) & 0x80) ? ONE:ZERO;
            memaddr++;
        }

        for(i=0; i<8; i++) // BLUE
        {
            LED_Buffer[memaddr] = ((color[2]<<i) & 0x80) ? ONE:ZERO;
            memaddr++;
        }

        len--;
    }

    DMA_SetCurrDataCounter(DMA1_Channel5, buffersize); 	//下面这几句的顺序,不同型号MCU可能也会不同
    DMA_Cmd(DMA1_Channel5, ENABLE); 			
    TIM_Cmd(TIM1, ENABLE); 					
    while(!DMA_GetFlagStatus(DMA1_FLAG_TC5)) ; 	
    TIM_Cmd(TIM1, DISABLE); 	
    DMA_Cmd(DMA1_Channel5, DISABLE); 			
    DMA_ClearFlag(DMA1_FLAG_TC5); 			
}


int  main(void)
{
	Timer1_init();
		
	while(1)
	{
	  WS2812_led_send(color_buff,10);
	  delay_ms(5);
	}
}

  • 15
    点赞
  • 142
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值