CH582 CH592 用PWM+DMA方式驱动WS2812灯带

要想驱动WS2812灯带,必须弄明白其工作原理。这点我们从厂家提供的规格书中很容易整明白。

需要注意的是,不同的厂家,这些参数会有较大的差异,很可能导致无法点亮灯珠,所以我们需要根据不同厂家的产品,调整相应的参数。但数据传输方式是相同的,也就是说我们的代码应该是不用修改的,只是有必要的时候,需要调整相应的参数,比如0码、1码,RES时间等。

WS2812灯珠对时序要求还是比较严格的,虽然原则上可以通过直接写IO口,并用冗余代码的方式来控制时间从而达到控制WS2812灯带的目的,但这种方式几乎没有任何实用性,不同的主频需要修改代码,甚至只是简单的代码优化都需要修改代码,否则就没办法点亮灯珠。尤其是对CH582/CH592这种芯片,我们采用这种芯片的理由大多都是需要BLE功能,而要保留BLE功能就更不可能采用这种驱动方式。

正是因为上述原因,我们采用PWM+DMA的方式来驱动WS2812灯珠,先上代码。

ws2812.h

#include "CH59x_common.h"

#ifndef _WS2812_DEFINE
#define _WS2812_DEFINE

#define			WS2812_GPIO_PIN				GPIO_Pin_11
#define WS2812_RESET_TIME  280
#define LED_NUMS  60

#define WS2812_LED_BIT 24
#define PIXEL_NUM       LED_NUMS
#define WS2812_RST_NUM	240
#define NUM             (WS2812_LED_BIT*PIXEL_NUM + WS2812_RST_NUM)        // Reset 280us / 1.25us = 224


#define WS1             45
#define WS0             30
#define WS2812_LED_DATA_0	 30
#define WS2812_LED_DATA_1  45

#define WS2812_LED_DATA_LEN NUM

enum {
	WS2812_LED_LEVEL_0 = 0,
	WS2812_LED_LEVEL_1,
	WS2812_LED_LEVEL_2,
	WS2812_LED_LEVEL_3,
	WS2812_LED_LEVEL_4,
	WS2812_LED_LEVEL_5,
	WS2812_LED_LEVEL_6,
	WS2812_LED_LEVEL_7,
	WS2812_LED_LEVEL_8,
	WS2812_LED_LEVEL_9,
	WS2812_LED_LEVEL_10,
	WS2812_LED_LEVEL_11,
	WS2812_LED_LEVEL_12,
	WS2812_LED_LEVEL_13,
	WS2812_LED_LEVEL_14,
	WS2812_LED_LEVEL_15,
	WS2812_LED_LEVEL
};

typedef struct
{
	uint8_t G;
	uint8_t R;
	uint8_t B;
	uint8_t phase;
}WS2812_LED_CTRL;

void ws281x_rainbowCycle(void);
void ws2812_Init(void);
void WS2812_LED_Circle(void);

#endif

ws2812.c

#include "ws2812.h"

__attribute__((aligned(4))) uint32_t LED_Data[NUM];
WS2812_LED_CTRL ws2812_led = {0xff,0,0,0};

void ws2812_Init(void)
{

    GPIOB_ModeCfg(GPIO_Pin_11, GPIO_ModeOut_PP_5mA);

    GPIOPinRemap(ENABLE, RB_PIN_TMR2);

    TMR2_PWMCycleCfg(75); // 周期 1.25us

    TMR2_DMACfg(ENABLE, (uint16_t)(uint32_t)&LED_Data[0], (uint16_t)(uint32_t)&LED_Data[WS2812_LED_DATA_LEN], Mode_LOOP);

    TMR2_PWMInit(High_Level, PWM_Times_1);

    /* 开启计数溢出中断,计满1000个周期进入中断 */

    TMR2_ClearITFlag(TMR1_2_IT_DMA_END);

    PFIC_EnableIRQ(TMR2_IRQn);

    TMR2_ITCfg(ENABLE, TMR1_2_IT_DMA_END);
    TMR2_PWMEnable();
    TMR2_Enable();

   for (uint8_t idx = 0; idx < PIXEL_NUM ;idx ++)
		LED_Data[idx] = WS0;
}

uint32_t WS281x_Color(uint8_t red, uint8_t green, uint8_t blue) { return red << 16 | green << 8 | blue; }
uint32_t Wheel(uint8_t WheelPos)
{
    WheelPos = 255 - WheelPos;
    if (WheelPos < 85)
    {
        return WS281x_Color(255 - WheelPos * 3, 0, WheelPos * 3);
    }
    if (WheelPos < 170)
    {
        WheelPos -= 85;
        return WS281x_Color(0, WheelPos * 3, 255 - WheelPos * 3);
    }
    WheelPos -= 170;
    return WS281x_Color(WheelPos * 3, 255 - WheelPos * 3, 0);
}
void WS281x_SetPixelColor(uint16_t n, uint32_t GRBColor)
{
    uint8_t i;
    if (n < PIXEL_NUM)
    {
        for (i = 0; i < WS2812_LED_BIT; ++i)
            LED_Data[WS2812_LED_BIT * n + i] = (((GRBColor << i) & 0X800000) ? WS1 : WS0);
    }
}

uint16_t phase = 0;
void ws281x_rainbowCycle(void)
{
  uint16_t i, j;
  j = phase ++;
  if (phase > 256) 
    phase = 0;
     for(i=0; i< PIXEL_NUM; i++) 
	 {
       WS281x_SetPixelColor(i,Wheel(((i * 256 / PIXEL_NUM) + j) & 255));
    }
 //   DelayMs(wait);
}

void Fill_WS2812_LED_Data(uint8_t G,uint8_t R,uint8_t B,uint16_t Pos)
{
	uint32_t indexx=(Pos*WS2812_LED_BIT);
 
	if (Pos > LED_NUMS)
	{
		for (uint8_t idx = 0; idx < LED_NUMS ;idx ++)
			LED_Data[idx] = WS2812_LED_DATA_0;
	}
	else
	{
		for (uint8_t i = 0;i < 8;i++)
		{
			LED_Data[indexx+i]      = (G << i) & (0x80)?WS2812_LED_DATA_1:WS2812_LED_DATA_0;
			LED_Data[indexx+i + 8]  = (R << i) & (0x80)?WS2812_LED_DATA_1:WS2812_LED_DATA_0;
			LED_Data[indexx+i + 16] = (B << i) & (0x80)?WS2812_LED_DATA_1:WS2812_LED_DATA_0;
		}
	}
}

void WS2812_LED_PowerOff(void)
{
	for (uint8_t idx = 0; idx < LED_NUMS ;idx ++)
		LED_Data[idx] = WS2812_LED_DATA_0;
}

void WS2812_LED_Circle(void)
{
	uint16_t idx,i;
//	uint8_t level;
	
	for (idx = LED_NUMS - 1; idx > 0; idx --)
	{
		for (i = 0; i < WS2812_LED_BIT; i ++)
			LED_Data[idx* WS2812_LED_BIT  + i] = LED_Data[(idx-1) * WS2812_LED_BIT + i];
	}
	if (ws2812_led.phase < WS2812_LED_LEVEL)
	{
		ws2812_led.G -= (0xff / WS2812_LED_LEVEL);
		ws2812_led.R += (0xff / WS2812_LED_LEVEL);
		ws2812_led.phase ++;
		if (ws2812_led.phase == WS2812_LED_LEVEL)
		{
			ws2812_led.G = 0;
			ws2812_led.R = 0xff;
		}
	}
	else if (ws2812_led.phase < WS2812_LED_LEVEL * 2)
	{
		ws2812_led.R -= (0xff / WS2812_LED_LEVEL);
		ws2812_led.B += (0xff / WS2812_LED_LEVEL);
		ws2812_led.phase ++;
		if (ws2812_led.phase == WS2812_LED_LEVEL * 2)
		{
			ws2812_led.R = 0;
			ws2812_led.B = 0xff;
		}
	}
	else if (ws2812_led.phase < WS2812_LED_LEVEL * 3)
	{
		ws2812_led.B -= (0xff / WS2812_LED_LEVEL);
		ws2812_led.G += (0xff / WS2812_LED_LEVEL);
		ws2812_led.phase ++;
		if (ws2812_led.phase == WS2812_LED_LEVEL * 3)
		{
			ws2812_led.B = 0;
			ws2812_led.G = 0xff;
			ws2812_led.phase = 0;
		}
	}
	Fill_WS2812_LED_Data(ws2812_led.G,ws2812_led.R,ws2812_led.B,0);
//	Change_WS2812LED_Status();
}


/*********************************************************************
 * @fn      TMR2_IRQHandler
 *
 * @brief   TMR2中断函数
 *
 * @return  none
 */
__INTERRUPT
__HIGH_CODE
void TMR2_IRQHandler(void)
{
    if(TMR2_GetITFlag(TMR1_2_IT_DMA_END))
    {
        TMR2_ClearITFlag(TMR1_2_IT_DMA_END);
        PRINT("DMA end\n");
        /* DMA 结束 */
        /* 用户可自行添加需要的处理 */
    }
}

增加WS2812灯带显示事件:

#define HAL_RGBLED_EVENT      0x0008
添加初始化代码:

void HAL_Init()
{
    halTaskID = TMOS_ProcessEventRegister(HAL_ProcessEvent);
    HAL_TimeInit();

    ws2812_Init();

#if(defined HAL_SLEEP) && (HAL_SLEEP == TRUE)
    HAL_SleepInit();
#endif
#if(defined HAL_LED) && (HAL_LED == TRUE)
    HAL_LedInit();
#endif
#if(defined HAL_KEY) && (HAL_KEY == TRUE)
    HAL_KeyInit();
#endif
#if(defined BLE_CALIBRATION_ENABLE) && (BLE_CALIBRATION_ENABLE == TRUE)
    tmos_start_task(halTaskID, HAL_REG_INIT_EVENT, 800); // 添加校准任务,500ms启动,单次校准耗时小于10ms
#endif
    tmos_start_task(halTaskID, HAL_RGBLED_EVENT,MS1_TO_SYSTEM_TIME(1000));
//    tmos_start_task( halTaskID, HAL_TEST_EVENT, 1600 );    // 添加一个测试任务
}

添加显示事件处理代码:

    if(events & HAL_RGBLED_EVENT)
    {
        WS2812_LED_Circle();
        tmos_start_task(halTaskID, HAL_RGBLED_EVENT, MS1_TO_SYSTEM_TIME(100));
        return events ^ HAL_RGBLED_EVENT;
    }

从规格书中,我们知道TH+TL大概1.1us,这样我们设置PWM的周期为1.25us,频率800K。我们主频为60M,也就是60个脉冲为1us,1.25us需要75个脉冲。   

TMR2_PWMCycleCfg(75); // 周期 1.25us
 

两次数据传输必须发送RESET CODE,实际上就是保持数据线为低电平300us左右,所以我们DMA的数据buff中,需要加上这段时间的传输数据,#define WS2812_RESET_TIME  240,240 * 1.25 = 300。这样我们可以设置DMA方式为循环方式,这样设置后,我们不再需要代码控制WS2812灯珠了。如果需要修改不同的显示效果,只需要修改DMA的数据buff就可以。

灯珠的显示效果大家可以根据自己的需求重写代码,我们这里只是实现了一种跑马灯的效果。代码提供了两种算法,效果略有差异。一种通过调用WS2812_LED_Circle()实现,一种调用ws281x_rainbowCycle()实现。

另外有一点需要注意的是,虽然WS2812灯珠的数据是8位,但CH592的DMA传输DataSize好象只能是32位的,所以DMA的DataSize不能设置为byte,而要设置为WORD,所以DMA的数据buff的类型设置为uint32_t 。

最后需要完整工程文件的可以参考:

CH592Peripheral设备PWM+DMA驱动WS2812灯带资源-优快云文库

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

永远的元子

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

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

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

打赏作者

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

抵扣说明:

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

余额充值