前面已经实现了通过DMA方式改变脉冲PWM占空比和个数。本次就利用这个实现WS2812彩灯输出。 
首先我们要知道WS2812的驱动方式。WS2812彩灯是通过不同脉冲高电平的占空比来标识0和1的BIT位的。 
BIT1:是高低占空比时间为1us-0.25us(800K速率);BIT1:是高低占空比时间为2us-0.5us(400K速率); 
BIT0:是高低占空比时间为0.25us-1us(800K速率);BIT0:是高低占空比时间为0.5us-2us(400K速率); 
这里要注意,BIT0或者BIT1主要是高电平脉冲要精准,误差要小。而占空比中低电平时间不需要特别精准。意思就是说BIT1的高电平要在1us左右,误差+-0.2us,但是低电平可以0.25us到2us左右都可以识别为BIT1.BIT0也一样。主要是靠高电平时间宽度识别。 
WS2812一个RGB彩灯需要3个字节24BIT,RGB3种颜色各1字节8BIT。 
这里我初始化了PWM的频率为500K。TIM定时器系统频率48MHz,计数96个为一周期500KHz。 
  
下面定义WS2812的BIT位占空比数值: 
#define WS_BIT1             48          //1us-1.25us
#define WS_BIT0             12          //0.25us-0.5us
#define WS2812_PIXELS       8         // WS2812 LEDs 数量
#define WS_BIT              24u            //一个RGB灯的BIT位数
#define WS_TRANSFER_SIZE   ((WS2812_PIXELS) * (WS_BIT))   // Transfer dataSize
__ALIGNED(4)
volatile uint8_t g_WS2812TxBuf[WS_TRANSFER_SIZE];   // WS2812 Strip transmit data buffer
发送数据:
void WS2812_SendData(void)
{
  HAL_TIM_PWM_Start_DMA(&htim3,TIM_CHANNEL_1,(uint32_t *)g_WS2812TxBuf,WS_TRANSFER_SIZE);
}
发送数据完成中断回调:
void WS2812_SendDataCpl_cb(void)
{
  TIM3->CCR1 = 0;
  if(ws2812_send_lock)   ws2812_send_lock = 0;
}
static void _WS2812_SetPointColor(uint8_t *ptr, uint8_t r, uint8_t g, uint8_t b,uint8_t val)
{
    if(r)
    {
        if( ((r>>7)&0x01))  ptr[0+0] = val;
        if( ((r>>6)&0x01))  ptr[0+1] = val;
        if( ((r>>5)&0x01))  ptr[0+2] = val;
        if( ((r>>4)&0x01))  ptr[0+3] = val;
        if( ((r>>3)&0x01))  ptr[0+4] = val;
        if( ((r>>2)&0x01))  ptr[0+5] = val;
        if( ((r>>1)&0x01))  ptr[0+6] = val;
        if( ((r>>0)&0x01))  ptr[0+7] = val;
    }
    if(g)
    {
        if( ((g>>7)&0x01))  ptr[8+0] = val;
        if( ((g>>6)&0x01))  ptr[8+1] = val;
        if( ((g>>5)&0x01))  ptr[8+2] = val;
        if( ((g>>4)&0x01))  ptr[8+3] = val;
        if( ((g>>3)&0x01))  ptr[8+4] = val;
        if( ((g>>2)&0x01))  ptr[8+5] = val;
        if( ((g>>1)&0x01))  ptr[8+6] = val;
        if( ((g>>0)&0x01))  ptr[8+7] = val;
    }
    if(b)
    {
        if( ((b>>7)&0x01))  ptr[16+0] = val;
        if( ((b>>6)&0x01))  ptr[16+1] = val;
        if( ((b>>5)&0x01))  ptr[16+2] = val;
        if( ((b>>4)&0x01))  ptr[16+3] = val;
        if( ((b>>3)&0x01))  ptr[16+4] = val;
        if( ((b>>2)&0x01))  ptr[16+5] = val;
        if( ((b>>1)&0x01))  ptr[16+6] = val;
        if( ((b>>0)&0x01))  ptr[16+7] = val;
    }
}
void WS2812_PixelSetIndexRGB(uint16_t n, uint8_t r, uint8_t g, uint8_t b)
{  
    if(n < WS2812_PIXELS)
    {
        uint32_t i;
        for(i=0;i<WS_BIT;i++)
        {
            g_WS2812TxBuf[n*WS_BIT + i] = WS_BIT0;
        }
        _WS2812_SetPointColor((uint8_t *)&g_WS2812TxBuf[n*WS_BIT],r,g,b,WS_BIT1);
    }
}
void HAL_TIM_PWM_PulseFinishedCallback(TIM_HandleTypeDef *htim)
{
    if(htim->Instance == TIM3)
    {
        if(pwm_lock)   pwm_lock = 0;
        WS2812_SendDataCpl_cb();
    }
} 
这样就可以驱动WS2812彩灯了。 
在main中调用WS2812驱动接口就可以驱动彩灯了。 
  
  
引脚脉冲: 
  
  
  
下面就是驱动8个RGB彩灯效果: 
  
 
  |