2023-10-30 10:32 AM
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)
{
}
}
Solved! Go to Solution.
2023-10-31 04:49 PM
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
2023-11-01 01:06 PM
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;
2023-11-02 02:00 AM
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
2024-03-20 06:40 AM
The best solution is using my pack
https://github.com/nimaltd/ws28xx
https://github.com/nimaltd/STM32-PACK