虽然我说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的数值,达到最后目的.
L011都用了啊,低功耗表现如何?