Skip to main content
Beartama
Associate II
September 3, 2025
Solved

PWM + DMA not changing the color of WS2812B LED strip

  • September 3, 2025
  • 1 reply
  • 920 views

I have the following code trying to turn 5 pixels of the LED strip to red. The setup I have is the LED strip is connected to PC6.

#include <stdint.h>
// STM32F103C8 is a medium density device, hence we use the xB header
#include <stm32f103xb.h>

#if !defined(__SOFT_FP__) && defined(__ARM_FP)
#warning \
 "FPU is not initialized, but the project is compiling for an FPU. Please initialize the FPU before use."
#endif

void enable_clock() {
 RCC->CR |= RCC_CR_HSION;
 while ((RCC->CR & RCC_CR_HSIRDY) == 0);
}

// @formatter:off
#define HI 6
#define LO 3
uint32_t buffer[] = {
 LO, LO, LO, LO, LO, LO, LO, LO,
 HI, HI, HI, HI, HI, HI, HI, HI,
 LO, LO, LO, LO, LO, LO, LO, LO,
 LO, LO, LO, LO, LO, LO, LO, LO,
 HI, HI, HI, HI, HI, HI, HI, HI,
 LO, LO, LO, LO, LO, LO, LO, LO,
 LO, LO, LO, LO, LO, LO, LO, LO,
 HI, HI, HI, HI, HI, HI, HI, HI,
 LO, LO, LO, LO, LO, LO, LO, LO,
 LO, LO, LO, LO, LO, LO, LO, LO,
 HI, HI, HI, HI, HI, HI, HI, HI,
 LO, LO, LO, LO, LO, LO, LO, LO,
 LO, LO, LO, LO, LO, LO, LO, LO,
 HI, HI, HI, HI, HI, HI, HI, HI,
 LO, LO, LO, LO, LO, LO, LO, LO,
 0, 0, 0, 0, 0, 0, 0, 0,
 0, 0, 0, 0, 0, 0, 0, 0,
 0, 0, 0, 0, 0, 0, 0, 0,
 0, 0, 0, 0, 0, 0, 0, 0,
 0, 0, 0, 0, 0, 0, 0, 0,
 0, 0, 0, 0, 0, 0, 0, 0,
};
// @formatter:on

void blink_dma_to_gpio() {
 enable_clock();

 // GPIO C configuration
 RCC->APB2ENR |= RCC_APB2ENR_IOPCEN; // Enable IO port C

 GPIOC->CRL &= ~(GPIO_CRL_CNF6 | GPIO_CRL_MODE6); // GPIO C: Clear pin 6 configuration
 GPIOC->CRL |= GPIO_CRL_CNF6 | GPIO_CRL_MODE6; // GPIO C: Set pin 6 to alternate function push-pull 50MHz output

 // TIM3 configuration
 RCC->APB1ENR |= RCC_APB1ENR_TIM3EN; // Enable TIM3
 RCC->APB2ENR |= RCC_APB2ENR_AFIOEN; // Enable alternate function
 AFIO->MAPR |= AFIO_MAPR_TIM3_REMAP; // Full remap TIM3

 TIM3->PSC = 1 - 1;
 TIM3->ARR = 10 - 1;

 TIM3->BDTR |= TIM_BDTR_MOE;

 TIM3->CCMR1 |= TIM_CCMR1_OC1M_2 | TIM_CCMR1_OC1M_1; // PWM mode 1
 TIM3->CCER |= TIM_CCER_CC1E; // Enable capture compare 1

 TIM3->CCMR2 |= TIM_CCMR2_OC3M_0; // Active on match
 TIM3->CCER |= TIM_CCER_CC3E; // Enable capture compare 3
 TIM3->DIER |= TIM_DIER_CC3DE; // Enable DMA1 request

 // DMA1 configuration
 RCC->AHBENR |= RCC_AHBENR_DMA1EN; // Enable DMA1

 DMA1_Channel2->CPAR = (uint32_t) &TIM3->CCR1;
 DMA1_Channel2->CMAR = (uint32_t) &buffer;
 DMA1_Channel2->CNDTR = 168;

 DMA1_Channel2->CCR = DMA_CCR_MSIZE_1 // 32 bit memory
 | DMA_CCR_PSIZE_1 // 32 bit peripheral
 | DMA_CCR_MINC // Increment memory
 | DMA_CCR_CIRC // Circular memory
 | DMA_CCR_DIR // Memory to peripheral
 | DMA_CCR_HTIE // Half transfer interrupt
 | DMA_CCR_TCIE // Transfer complete interrupt
 ;

 // Enable peripherals
 DMA1_Channel2->CCR |= DMA_CCR_EN;
 TIM3->CR1 |= TIM_CR1_CEN;
}

int main() {
 blink_dma_to_gpio();
 for (;;);
}

I already had the output hooked up to an oscilloscope and the output waves look correct. However, when I run this code, nothing changes on the LED strip. I just want to ask if there is something wrong with my code here, or could there be any potential hardware issues I did not think of.

Best answer by Beartama

Thank you for the comments, I checked again and I had the configuration for the GPIO wrong. I configured it to be open-drain while it should be push-pull instead. Changing to push-pull makes the edges cleaner and the LEDs now have the expected colors.

1 reply

Andrew Neil
Super User
September 3, 2025

Please show your schematic.

 


@Beartama wrote:

I already had the output hooked up to an oscilloscope and the output waves look correct.


Please share scope screenshots - someone may be able to spot something...

If your scope has a screenshot facility, please use it - this will be far better than trying to photograph the screen!

 


@Beartama wrote:

when I run this code, nothing changes on the LED strip.


Do you see changes on the scope?

 

Do you have a known-good, working implementation to compare against; eg, Arduino?

A complex system that works is invariably found to have evolved from a simple system that worked.A complex system designed from scratch never works and cannot be patched up to make it work.
Beartama
BeartamaAuthor
Associate II
September 3, 2025

Please show your schematic.


Screenshot from 2025-09-03 12-52-06.png

RM STATUS LIGHT CTRL LV is the LED strip.

 


Please share scope screenshots - someone may be able to spot something...


3f111fbc-a605-429d-b2cc-a1566c35f3bc.jpeg

The high spike is for bit 1 to be sent to the LED, and the low spike is for bit 0. The high spike is high for 0.75us and the low spike is high for 0.375us, these are according to the spec of WS2812B.


Do you have a known-good, working implementation to compare against; eg, Arduino?


No

 

Andrew Neil
Super User
September 3, 2025

That doesn't show how the STM32 is actually connected to the LED strip, nor the LED power supply.

 


@Beartama wrote:


Please share scope screenshots - someone may be able to spot something...



you missed:


@Andrew Neil wrote:
If your scope has a screenshot facility, please use it - this will be far better than trying to photograph the screen!

Your scope does have that facility:

AndrewNeil_0-1756893878101.png

 

Anyhow, that waveform looks terrible: there should be clear pulses with clean edges, and consistent height; eg,

 

AndrewNeil_2-1756894069004.png

from: https://www.pjrc.com/2017/10/

 

PS:

From the LED datasheet:

AndrewNeil_3-1756894444768.png

You haven't shown what your VDD is for the LEDs but, if it's 3.3V, that would mean that the minimum 'High' voltage is 2.3V.

In your scope trace, the short spikes aren't even reaching 2V, and even the tall ones are marginal.

 

A complex system that works is invariably found to have evolved from a simple system that worked.A complex system designed from scratch never works and cannot be patched up to make it work.