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();

    }
    

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;
	}
}

 

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;
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 II