使用DMA+PWM实现的WS2812

/ 1评 / 1

虽然我说SPI是很投机取巧办法,但是DMA+PWM其实也可以的.相对还省点资源,就是吃个定时器.使用的是STM32L011.

#define TIMING_ONE  26

#define TIMING_ZERO 10



uint8_t TIM_LED_Timing[384 + 1]; /* 每灯24个字节.384字节也只能存16灯(优信家的灯环). */





uint8_t WS2812_LED_Data[] = {0x10, 0x10, 0x10, /* 每一行都是RGB. */

                             0x00, 0x10, 0x00,

                             0x00, 0x00, 0x10,



                             0x10, 0x00, 0x00,

                             0x00, 0x10, 0x00,

                             0x00, 0x00, 0x10,



                             0x10, 0x00, 0x00,

                             0x00, 0x10, 0x00,

                             0x00, 0x00, 0x10,



                             0x10, 0x00, 0x00,

                             0x00, 0x10, 0x00,

                             0x00, 0x00, 0x10,



                             0x10, 0x00, 0x00,

                             0x00, 0x10, 0x00,

                             0x00, 0x00, 0x10,



                             0x10, 0x00, 0x00, /* 这是最后一个颜色 */

                            };



static void WS2812_TIM_Init(void)

{

    LL_AHB1_GRP1_EnableClock(LL_AHB1_GRP1_PERIPH_DMA1);

    LL_DMA_ConfigTransfer(DMA1, LL_DMA_CHANNEL_2, LL_DMA_DIRECTION_MEMORY_TO_PERIPH |

                          LL_DMA_PRIORITY_HIGH         	    |

                          LL_DMA_MODE_NORMAL	              |

                          LL_DMA_PERIPH_NOINCREMENT         |

                          LL_DMA_MEMORY_INCREMENT           |

                          LL_DMA_MDATAALIGN_BYTE            |

                          LL_DMA_PDATAALIGN_HALFWORD);

    LL_DMA_SetPeriphRequest(DMA1, LL_DMA_CHANNEL_2, LL_DMA_REQUEST_8);

    LL_DMA_ConfigAddresses(DMA1, LL_DMA_CHANNEL_2, (uint32_t)&TIM_LED_Timing, (uint32_t)&TIM2->CCR1, LL_DMA_GetDataTransferDirection(DMA1, LL_DMA_CHANNEL_2));



    LL_GPIO_SetPinMode(GPIOA, LL_GPIO_PIN_0, LL_GPIO_MODE_ALTERNATE);

    LL_GPIO_SetPinPull(GPIOA, LL_GPIO_PIN_0, LL_GPIO_PULL_DOWN);

    LL_GPIO_SetPinSpeed(GPIOA, LL_GPIO_PIN_0, LL_GPIO_SPEED_FREQ_VERY_HIGH);

    LL_GPIO_SetAFPin_0_7(GPIOA, LL_GPIO_PIN_0, LL_GPIO_AF_2);



    LL_APB1_GRP1_EnableClock(LL_APB1_GRP1_PERIPH_TIM2);

    LL_TIM_SetPrescaler(TIM2, 0);

    LL_TIM_SetAutoReload(TIM2, 39);/* 32MHz / 40 = 800kHz	 */

    LL_TIM_OC_SetMode(TIM2,  LL_TIM_CHANNEL_CH1,  LL_TIM_OCMODE_PWM1);

    LL_TIM_OC_ConfigOutput(TIM2, LL_TIM_CHANNEL_CH1, LL_TIM_OCPOLARITY_HIGH);

    LL_TIM_OC_SetCompareCH1(TIM2, 0); /* 开始要默认低. */

    LL_TIM_OC_EnablePreload(TIM2, LL_TIM_CHANNEL_CH1);

    LL_TIM_EnableDMAReq_UPDATE(TIM2);

    LL_TIM_CC_EnableChannel(TIM2, LL_TIM_CHANNEL_CH1);

}



static void WS2812_TIM_DMA_Send(const uint8_t *data, uint8_t len)

{



    uint8_t i;

    uint16_t index = 0; /* 如果是uint8_t,灯上限是10个. */

    uint8_t r_index = 0;



    while (len)

    {

        /* 下面INDEX没有反,不是BUG! */

        for(i = 0; i < 8; i++) /* GREEN */

        {

            TIM_LED_Timing[index] = ((data[r_index * 3 + 1] << i) & 0x0080) ? TIMING_ONE : TIMING_ZERO;

            index++;

        }

        for(i = 0; i < 8; i++) /* RED */

        {

            TIM_LED_Timing[index] = ((data[r_index * 3 + 0] << i) & 0x0080) ? TIMING_ONE : TIMING_ZERO;

            index++;

        }

        for(i = 0; i < 8; i++) /* BLUE */

        {

            TIM_LED_Timing[index] = ((data[r_index * 3 + 2] << i) & 0x0080) ? TIMING_ONE : TIMING_ZERO;

            index++;

        }

        r_index++;

        len--;

    }



    TIM_LED_Timing[index] = 0; /* 最后的补0,是为了让电平变低. */



    LL_DMA_SetDataLength(DMA1, LL_DMA_CHANNEL_2, r_index * 24 + 1);

    LL_DMA_EnableIT_TC(DMA1, LL_DMA_CHANNEL_2);

    LL_DMA_EnableChannel(DMA1, LL_DMA_CHANNEL_2);



    LL_TIM_EnableCounter(TIM2);

    LL_TIM_GenerateEvent_UPDATE(TIM2);



    NVIC_EnableIRQ(DMA1_Channel2_3_IRQn);

}



int main(void)

{

    SystemClock_Config();

    WS2812_TIM_Init();

    while (1)

    {

        WS2812_TIM_DMA_Send(WS2812_LED_Data, 16); /* 抓波形一定要在发送前抓. */

        LL_mDelay(1000);

    }

}

首先得到800kbps的频率,通过重装计数器获得,然后在UPDATE事件输入下一次PWM的数值,达到最后目的.

  1. 井底添蛙说道:

    L011都用了啊,低功耗表现如何?

发表回复

您的电子邮箱地址不会被公开。 必填项已用 * 标注