【GD32】TIMER+PWM+DMA 驱动 WS2812B灯带

使用芯片GD32F303CBT6,使用TIMER+PWM+DMA的方式驱动WS2812B灯带,之前一篇文章已经讲过WS2812B灯带的原理,和简单的驱动过程。(这篇文章就不重复讲这部分)那个驱动方法用在项目上比较low,后面使用了这种新的方式,更简单,也能更好的实现想要的灯效。

上一篇讲WS2812B灯带原理,请点击此处


1、定时器Timer

	timer_oc_parameter_struct timer_ocintpara;  //定时器比较输出参数
	timer_parameter_struct timer_initpara;      //定时器参数
	dma_parameter_struct dma_init_struct;       //DMA参数
	
	  //打开时钟
	rcu_periph_clock_enable(RCU_DMA1);
    rcu_periph_clock_enable(RCU_GPIOA);
    rcu_periph_clock_enable(RCU_AF);
    rcu_periph_clock_enable(RCU_TIMER4);
	
	  //GPIO配置
    gpio_init(GPIOA,GPIO_MODE_AF_PP,GPIO_OSPEED_50MHZ,GPIO_PIN_2);   //GPIO复用推挽输出配置
	  
	  //TIME 初始化 80K
	timer_deinit(TIMER4);
	timer_initpara.prescaler         = 0;                  //预分频器配置为0
	timer_initpara.alignedmode       = TIMER_COUNTER_EDGE; //边缘对齐方式
	timer_initpara.counterdirection  = TIMER_COUNTER_UP;   //向上计数
	timer_initpara.period            = 150;                //自动重装载寄存器的值
	timer_initpara.clockdivision     = TIMER_CKDIV_DIV1;   //时钟分频
	timer_initpara.repetitioncounter = 0;                  //重复计数
	timer_init(TIMER4,&timer_initpara);                    //初始化             
		
		//通道输出控制
	timer_ocintpara.outputstate      = TIMER_CCX_ENABLE;            //比较输出使能 
	timer_ocintpara.outputnstate     = TIMER_CCXN_DISABLE;          //比较互补输出失能
	timer_ocintpara.ocpolarity       = TIMER_OC_POLARITY_HIGH;      //输出极性为高
	timer_ocintpara.ocnpolarity      = TIMER_OCN_POLARITY_HIGH;     //互补通道输出极性
	timer_ocintpara.ocidlestate      = TIMER_OC_IDLE_STATE_LOW;     //空闲状态下比较输出状态
	timer_ocintpara.ocnidlestate     = TIMER_OCN_IDLE_STATE_LOW;    //空闲状态下互补输出状态
    timer_channel_output_config(TIMER4,TIMER_CH_2,&timer_ocintpara);//初始化比较输出

2、定时器PWM

//PWM0
	timer_channel_output_pulse_value_config(TIMER4,TIMER_CH_2,0);                 //初始化比较输出寄存器的值
    timer_channel_output_shadow_config(TIMER4,TIMER_CH_2,TIMER_OC_SHADOW_ENABLE); //使能影子寄存器
    timer_channel_output_mode_config(TIMER4,TIMER_CH_2,TIMER_OC_MODE_PWM0);       //PWM0模式

3、定时器DMA

led1_rgb_buf是用来存放每个灯珠的24bit的占空比(高低电平)

u16 led1_rgb_buf[LED_RGB_NUM+3][RGB_BIT];
		//DMA配置
		dma_deinit(DMA1,DMA_CH1);
		dma_init_struct.periph_addr  = (uint32_t)(&TIMER_DMATB(TIMER4));  //外设数据寄存器的地址
		dma_init_struct.periph_inc   = DMA_PERIPH_INCREASE_DISABLE;       //外设寄存器地址不自增
		dma_init_struct.memory_addr  = (uint32_t)(led1_rgb_buf);         //内部存储器地址
		dma_init_struct.memory_inc   = DMA_MEMORY_INCREASE_ENABLE;        //内存地址自增
		dma_init_struct.periph_width = DMA_PERIPHERAL_WIDTH_16BIT;        //外设数据宽度
		dma_init_struct.memory_width = DMA_MEMORY_WIDTH_16BIT;            //内存数据宽度
		dma_init_struct.direction    = DMA_MEMORY_TO_PERIPHERAL;          //传输方向为内存到外设
		dma_init_struct.number       = RGB_ARRAY_SIZE;                   //DMA需要输出的数据
		dma_init_struct.priority     = DMA_PRIORITY_ULTRA_HIGH;           //DMA优先级
		dma_init(DMA1,DMA_CH1,&dma_init_struct);//DMA通道初始化

 最后需要对这些定时器,DMA,中断进行一下使能

    dma_circulation_disable(DMA1,DMA_CH1);       //禁止循环发送                 
	dma_memory_to_memory_disable(DMA1,DMA_CH1);	 //内存to内存失能    
		
	timer_primary_output_config(TIMER4,ENABLE);  //定时器输出使能
		
	timer_dma_transfer_config(TIMER4, TIMER_DMACFG_DMATA_CH2CV, TIMER_DMACFG_DMATC_1TRANSFER); //定时器DMA输出配置
    timer_dma_enable(TIMER4,TIMER_DMA_CH2D);//定时器DMA使能
	timer_channel_dma_request_source_select(TIMER4,TIMER_DMAREQUEST_UPDATEEVENT);
    timer_auto_reload_shadow_enable(TIMER4);
		
		
	dma_channel_disable(DMA1, DMA_CH1);
    timer_enable(TIMER4);
		
	dma_interrupt_enable(DMA1,DMA_CH1,DMA_INT_FTF);  //DMA中断使能
    
    nvic_irq_enable(DMA1_Channel1_IRQn, 2, 1);

4、DMA中断

void DMA1_Channel1_IRQHandler(void)
{
	if(dma_interrupt_flag_get(DMA1,DMA_CH1,DMA_INT_FLAG_FTF))
    {
        dma_interrupt_flag_clear(DMA1,DMA_CH1,DMA_INT_FLAG_FTF);//清除中断标志     
        dma_channel_disable(DMA1, DMA_CH1);
        dma_transfer_number_config(DMA1, DMA_CH1, RGB_ARRAY_SIZE);//传输DMA数据
	    timer_disable(TIMER4);
    }
}

5、灯带应用

 通过上面的配置,基本上就弄好了驱动,现在要如何灯带亮出想要的颜色。

这部分讲2种:一种是全部灯都是同一种颜色,一种是流水灯

5.1、固定颜色

void Fixed_Color_Display(u32 grb, u8 luminance) //grb为颜色值,例如绿色为0xff0000,红色为0x00ff00。luminance为灯珠亮度
{
    u16 i=0,j=0;
    u32 grb_temp1=0,grb_temp2=0;
    u32 g_temp=0,r_temp=0,b_temp=0;
    
    //颜色数组填充
    for(i=0; i<LED_RGB_NUM; i++) //LED_RGB_NUM 为灯珠数目
    {
        //亮度等级
        r_temp = (grb>>16) & 0xff;
        g_temp = (grb>>8) & 0xff;
        b_temp = grb& 0xff;
      
        g_temp = g_temp * luminance / 100; 
        r_temp = r_temp * luminance / 100; 
        b_temp = b_temp * luminance / 100; 
        grb_temp2 = (g_temp <<16) | (r_temp <<8) | b_temp;
        grb_temp1 = grb_temp2;
      
        for(j=0; j<RGB_BIT; j++) //RGB_BIT 为24bit,一个灯珠有24bit
        {
            if(grb_temp1 & 0x800000) led1_rgb_buf[i][j] = 112;// 此处为高占空比
            else                     led1_rgb_buf[i][j] = 38;
          
            grb_temp1 = grb_temp1<<1;
        }
    }
    //发送 48*1.25us = 60us 复位
    for(i=LED_RGB_NUM; i<LED_RGB_NUM+3; i++)
    {
        for(j=0; j<RGB_BIT; j++)
        {
            led1_rgb_buf[i][j] = 0;
					
        }
    }
    //开启DMA发送    
		
    dma_channel_enable(DMA1,DMA_CH1);
	timer_enable(TIMER4);
    
}

第二种流水灯,在第一种的基础上进行封装,有更多的扩展性

static void serializeRGBbit(uint8_t red, uint8_t green, uint8_t blue, uint16_t ledNo)
{
    if (ledNo < LED_RGB_NUM)
    {
        uint32_t rgbDatTemp = 0;
        rgbDatTemp = (green << 16) | (red << 8) | blue;
        for (uint8_t i = 0; i < RGB_BIT; i++)
        {
            if (rgbDatTemp & 0x800000)
            {
                led1_rgb_buf[ledNo][i] = 102;
            }
            else
            {
                led1_rgb_buf[ledNo][i] = 48;
            }
            rgbDatTemp = rgbDatTemp << 1;
        }
    }
}
static void ledSetColourLum(uint8_t red, uint8_t green, uint8_t blue, uint8_t lum, uint8_t ledNo)
{
    if (lum <= 100)
    {
        red = red * lum / 100; // 亮度百分比转换
        green = green * lum / 100;
        blue = blue * lum / 100;
        serializeRGBbit(red, green, blue, ledNo);
    }
}
static void ledForOne(uint8_t startCunt, uint8_t ledNum, uint8_t red, uint8_t green, uint8_t blue, uint8_t luminance)
{
    for (uint8_t i = 0; i < LED_RGB_NUM; i++)
    {
        if (i < startCunt || i > startCunt)
        {
            ledSetColourLum(0, 0, 0, 0, i);
        }
        else
        {
            ledSetColourLum(red, green, blue, luminance, i);
        }
    }
}
void Waterfall_Color_Display(u8 luminance)
{
    static uint8_t startCunt = 0;
	uint8_t red = 0;
	uint8_t green = 255;
	uint8_t blue = 0;
	ledRandomSeriesLightUpForOne(startCunt++, 10, red, green, blue, luminance);
	if (startCunt  >= LED_RGB_NUM)
	{
		startCunt = 0;
	}
        
    Time_PWM_Init_Config();

    //开启DMA发送    
    dma_channel_enable(DMA0,DMA_CH4);
    timer_enable(TIMER3); 
	
}

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

一叶舞澎湃

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值