This is a project to run WS2812B LEDs with an STM32 microcontroller. This library is written in register code and uses double-buffer DMA and PWM to produce the data signal with a minimal processor and memory consumption.
While this is written for an STM32F030K6T6 MCU, it can be configured to work with other STM32 MCUs as well. I believe it can be used for other WS variants by changing the timing and sequence of the data signal. The IDE used is Keil MDK Version 5.
Firstly, for the data pin, choose an I/O pin having a timer capable of generating PWM signals.
Specify the pin number and port of your data pin.
#define RGB_DATA_Pin 6U
#define RGB_DATA_GPIO_Port GPIOA
Specify the number of LEDs.
#define LED_NUM 6
Enable the peripheral clock of the data pin port.
RCC->AHBENR |= RCC_AHBENR_GPIOAEN; /* Enable GPIOA clock */
Find the alternate function corresponding to the timer on the data pin. For example, in the corresponding datasheet under Alternate functions selected through GPIOA_AFR:
Pin name | AF0 | AF1 | AF2 | AF3 | AF4 | AF5 | AF6 |
---|---|---|---|---|---|---|---|
PA5 | SPI1_MISO | TIM3_CH1 | TIM1_BKIN | - | USART3_CTS | TIM16_CH1 | EVENTOUT |
Set the alternate function to be the timer on the pin.
RGB_DATA_GPIO_Port->AFR[0] |= 5U << (RGB_DATA_Pin << 2); /* Alternate function (TIM16_CH1 AF5) */
Replace TIM16
with the timer used.
Find the DMA channel corresponding to TIMx_UP. For example, in the corresponding reference manual under DMA interrupts:
Pin Peripherals | Channel 1 | Channel 2 | Channel 3 | Channel 4 | Channel 5 |
---|---|---|---|---|---|
TIM16 | - | - | TIM16_CH1(1) TIM16_UP(1) |
TIM16_CH1(2) TIM16_UP(2) |
- |
Replace DMA1_Channel3
with the new DMA channel.
Enable the peripheral clock of the timer.
RCC->APB2ENR |= RCC_APB2ENR_TIM16EN; /* Enable TIM16 clock */
Enable the timer channel corresponding to the data pin.
TIM16->CCER |= TIM_CCER_CC1E; /* Enable capture/compare 1 output */
Enable the peripheral clock of the DMA.
RCC->AHBENR |= RCC_AHBENR_DMA1EN;
First queue all the changes that should happen at once using WS_Queue(uint8_t index, uint8_t r, uint8_t g, uint8_t b)
.
WS_Queue(0, 140, 0, 0);
WS_Queue(1, 0, 140, 0);
WS_Queue(2, 0, 0, 140);
To apply the changes, call WS_Send()
.
WS_Send();
In addition, the TIM_Delay(uint32_t ms)
function can be used to create delays in ms.
TIM_Delay(500);