cancel
Showing results for 
Search instead for 
Did you mean: 

WS2812 library does not work on STM32F40

AD_716
Associate III

Hello,

I've been trying to find a solution on the forums for several hours now, to no avail!
I have just put my programs on STM32F401, I compile with MBED Studio. I used to light my WS2812 LEDs with a KL25Z (NXP) always using Mbed Studio and my library worked perfectly!

But with the STM32, I couldn't get the LEDs to work, so I did some tests on different pins, with no success.
that the signal wasn't square...
I concluded that the code was wrong. After several searches I see that you have to activate certain functions to use the WS2812 with STM32 but I don't understand what's missing from my code.

Can you please help me?

Thank you in advance,
Sincerely
Antoine

Here is the program:

WS2812.h

#ifndef MBED_WS2812_H #define MBED_WS2812_H #include "mbed.h" class WS2812 { private: void int_to_binary(int value, int* buffer, int bufferSize); DigitalOut Data; int nLed; public: void WriteDataOut(int dataLed[]); WS2812(PinName _data, int _nLed); }; #endif

WS2812.cpp

#include <mbed.h> #include "WS2812.h" WS2812::WS2812(PinName _data, int _nLed): Data(_data), nLed(_nLed) {} void WS2812::int_to_binary(int value, int* buffer, int bufferSize) { int *nextChar = buffer + bufferSize-1;//................location to write the least significant bit for (int i = 0; i<(bufferSize); i++) { // for each bit if(value & (1<<i)) {*nextChar = 1;} else{*nextChar = 0;} // if set set to '1' else '0' nextChar --; } } void WS2812::WriteDataOut(int dataLed[]) { const int nBit8 = 8, nBytePerLed = 3, nBit = nLed * (nBytePerLed * nBit8); int data[nBit+1]; int buff[nBit8]; for(int x = 0; x < nLed; x ++) { int_to_binary(dataLed[x * nBytePerLed], buff, nBit8); for(int bit = 0; bit < 8; bit ++){data[bit + (x * (nBit8 * nBytePerLed))] = buff[bit];} int_to_binary(dataLed[(x * nBytePerLed)+1], buff, nBit8); for(int bit = 0; bit < 8; bit ++){data[bit + (x * (nBit8 * nBytePerLed)) + nBit8] = buff[bit];} int_to_binary(dataLed[(x * nBytePerLed)+2], buff, nBit8); for(int bit = 0; bit < 8; bit ++){data[bit + (x * (nBit8 * nBytePerLed)) + (2 * nBit8)] = buff[bit];} } __disable_irq(); for (int i = 0; i < (nLed * (nBytePerLed * nBit8)); i++) { int j = 0; if (data[i]) { Data = 1; for (; j < 5; j++) {__nop();} Data = 0; for (; j < 0; j++) {__nop();} } else { Data = 1; for (; j < 0; j++) {__nop();} Data = 0; for (; j < 5; j++) {__nop();} } } __enable_irq(); }
View more

Main.cpp

#include "mbed.h" #include "WS2812.h" DigitalIn bpUser(PA_10); WS2812 WSLed(PA_14, 2); int buff[6] = {50, 100, 150, 200, 127, 255}; int main() { WSLed.WriteDataOut(buff); while(1) { if (bpUser == 0) { } }

 

13 REPLIES 13
AD_716
Associate III

Good evening,

Thanks for your answer,
I'm using chips on a custom board already soldered and assembled, I can't change pins because they are all used / assigned. That's why DMA is the only option.

I've looked but I can't find a way to do DMA without using CubeProgrammer (I code and compile in Mbed studio).

Do you have any examples that I can test and work on?

Thank you in advance for your valuable answers

Best regards,
Antoine

I found some code I wrote few years ago for STM32F4 controlling few thousands of WS2812 via 8 outputs; some from SPI, others from timers.

static void wsenc_tim(uint16_t *enc_wsdata, uint8_t *buf, uint16_t nleds) { for (uint16_t l = 0; l < nleds * 3; l++) for (uint8_t i = 0; i < 8; i ++) enc_wsdata[WS_RESET_BITS + l * 8 + i] = (buf[l] >> (7 - i) & 1) + 1; enc_wsdata[WS_RESET_BITS + nleds * 8 * 3] = 0; enc_wsdata[WS_RESET_BITS + nleds * 8 * 3 + 1] = 0; enc_wsdata[WS_RESET_BITS + nleds * 8 * 3 + 2] = 0; } static void dmastream_start(uint8_t ch) { const struct chinfo_ *p = &chinfo[ch]; NVIC_DisableIRQ(p->irqn); p->dmastr->CR = 0; p->dmastr->M0AR = (uint32_t)p->buf; if (p->tim_channel) { p->dmastr->NDTR = TIM_VALUES(cd.nleds[ch]); p->dmastr->CR = DMA_SxCR_CHSELV(p->dmarqchan) | DMA_SxCR_DIR_M2P | DMA_SxCR_MINC // | DMA_SxCR_TCIE | DMA_SxCR_EN; | DMA_SxCR_MSIZE16 | DMA_SxCR_PSIZE16 | DMA_SxCR_TCIE | DMA_SxCR_EN; } else { p->dmastr->NDTR = SPI_FRAMES(cd.nleds[ch]); p->dmastr->CR = DMA_SxCR_CHSELV(p->dmarqchan) | DMA_SxCR_DIR_M2P | DMA_SxCR_MINC | DMA_SxCR_MSIZE16 | DMA_SxCR_PSIZE16 | DMA_SxCR_TCIE | DMA_SxCR_EN; } }
View more

 

Two important remarks: DMA channel  PAR register should be initialized once with the address of TIM CCRx register. All interrupt flags of a DME channel must be cleared in LIFCR oor HIFCR before calling dmastream_start();

TIM initialization sequence:

// WS2812 TIM+DMA, timer clock = 2 * bus clock WS_TIM->PSC = APB1_FREQ * 2 / 2500000 - 1; // prescaled clock period 400 ns WS_TIM->ARR = 3 - 1; WS_TIM->CCMR1 = TIM_CCMR1_OC1M_PWM1 | TIM_CCMR1_OC1PE | TIM_CCMR1_OC2M_PWM1 | TIM_CCMR1_OC2PE; WS_TIM->CCMR2 = TIM_CCMR2_OC4M_PWM1 | TIM_CCMR2_OC4PE; WS_TIM->CCER = TIM_CCER_CC1E | TIM_CCER_CC2E | TIM_CCER_CC4E; WS_TIM->BDTR = TIM_BDTR_MOE; WS_TIM->DIER = TIM_DIER_CC1DE | TIM_DIER_CC2DE | TIM_DIER_UDE; WS_TIM->CR1 = TIM_CR1_CEN;
My STM32 stuff on github - compact USB device stack and more: https://github.com/gbm-ii/gbmUSBdevice
AD_716
Associate III

Hello,

Thanks a lot for the feedback!

However, I'm still having trouble understanding how this code works?
Where is the function to assign a particular pin?

Do you have an example in main.cpp if it's not too much to ask... so that it's meaningful?

How do I set cycle times according to the WS2812 datasheet?

Thanks in advance for your help

Best regards
Antoine

nima.askari
Associate III