虽然我说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都用了啊,低功耗表现如何?