For all those, who have trouble with getting HAL_TIM_PWM_PulseFinishCallback() to work - You have to Enable the interrupt from corresponding DMA channel in NVIC settings category in Cube and set its priority. Otherwise, the interrupt is not fired and the function never gets called.
For mapping apparent brightness to an LED duty cycle (the 8-bits per color channel in the WS2812 protocol), take a look at gamma. It’s basically an exponential scale. That should map better to perceived brightness than the tangent.
Do you have a video tutorial on how to make it? I am interested. This video makes me want to learn more. What if I want to control more than 12 LED rings?
Great video! Thanks for the guide. I was actually reading the documentation and thought of doing something just like this, but since there was a tutorial already just followed it and it just makes a lot of sense to read when coded.
Hi, I guess most of the video is correct. But please have a look at 11:54. You want to send 24 pulses. If you count those shown in the oscilloscope recording you find 28. I had a similar issue on a STML4 with 80MHz. It seems that the DMA callback invocation is simply too slow. I tried to put all the code into RAM but without success. The only thing that cured the problem is to stop the DMA and write a 0 length pulse and after that stop the timer. Certainly to configure a 25th pulse with 0 length will do the same because this will be the repeated value after DMA is not updating anymore. So you comment 'DMA doesn't stop' at 10:56 is actually wrong. The reason why PWM continous is because the timer is running. Therefore you don't need to stop DMA (it stops reliably after 24 updates of the CCR register of the belonging timer). However, you need to stop either the timer itself or the output of the belonging timer channel.
You're quite right. The DMA callback routine takes a little time, enough to send an additional 4 pulses. I had the same issue (using an STM32F373 @ 72 MHz). My solution was to : a) put my LED code data in an array with 48 bytes offset data at the the beginning that only contain zeros, my real data at element data[48] and further. b) change the DMA mode from normal to circular. In this way, the DMA will loop back to the beginning and load the TIM OC register with '0' values (the offset stuff), therefore making the PWM output value low until the callback routine is triggered by the NVIC. My pulses are now a perfect multiple of 24 bytes. Also, you could make the DMA do data conversion, so store your led codes as bytes, then let the DMA covert them to words. This really shrinks your memory usage if you have a lot of LEDs in the string : dmaInit.PeriphDataAlignment = DMA_PDATAALIGN_WORD; /* UInt8 ==> UInt32 ! DMA doet de konversie.*/ dmaInit.MemDataAlignment = DMA_MDATAALIGN_BYTE;
Excellent video! Only issue I have encountered is the excessive RAM memory utilization when driving 300 LEDs: 14.2 kiB. The best (but hard to implement) solution would be using circular DMA, but I couldn't be bothered. Instead you can also go back to the DMA configuration screen for the timer and say you want "Byte" data size at the memory size (and leave the peripheral data width alone, still "Half Word"). Then in code you can change the DMA buffer to uint8_t instead. This will half the memory footprint. Be careful what you pass to the the length parameter of HAL_TIM_PWM_Start_DMA, I suspect this counts in "data width" units.
Thanks a lot!! After simple modification, I successfully reproduced on the bluepill board. F103C8 have 20K RAM can drive 333 LEDs (With Brightness adjustment) or 358 LEDs(Without Brightness adjustment) I think... If we use DMA's Half-Transfer Complete event (HT event) and Transfer Complete event (TC event) to DMA processing... Able to use less RAM and can drive more LEDs.
10:52 it's not DMA sending the data! It's just the timer keeps working in PWM mode with the latest value left in its CCR register. DMA only sends 24 values you specified in the code and that all, it's not circular. In this case you need to add a 25th value which is equal to 0 to get rid of extra pulses.
I successful made the project works on STM32F407, with Keil C IDE. One strange point that I noticed when porting is that the program would unexpectedly stuck at: while (!datasentflag), even this variable shows value 1 when in debugging mode. To solve this bug, I had to declare it as volatile variable like this: volatile int datasentflag=0; (with #define USE_BRIGHTNESS 0) Thanks you very much for this great tutorial!
I had the same problem. But in my case it only appears if I select a 'Release' configuration. In 'Debug' configuration it works ok. But another strange problem is that when I run the program from the IDE, it stucks in the middle of the 4th color while sending the data. Or even at the first color. But after pressing reset it works. I don't understand what's going on.
You can do it without DMA. I've had the W2812B running with PWM on an xmega32. You use the "overflow" interrupt to update the duty cycle for the next cycle since W2812B timing is essentially PWM with 33% and 66% duty cycles for 0 and 1 respectively
That's basically correct, but using DMA you are offloading the task of updating the timer's CCR-register (PWM's duty-cycle) from the CPU to hardware (namely, the DMA). This way, you save valuable processing time for other (possibly real-time-)tasks, which may otherwise become delayed themselves.
@@Sayeesh-kc5yy the golden rule is: use the DMA controller when possible, in order to free the main processor for other things. your main chip is too precious to do just dumm byte sending and receiving. Of course, this scheme is only fully functional with the usage of an RTOS.
I don't know if I translated the code properly, but in my case I had to put the pixel data from MSB to LSB into the pwm array starting from 0, not 23 as in the original code... Excellent tutorial btw
Hi I have gone through your code and worked out the same. I am using the F446"ZE" nucleo for ws2812 Dot Matrix of 8x32 matrix total 256 LEDs. I tried ur code in it. So your Set_LED and Set_Brightness(45) is not working for me. Please offer some help. I am a newbie. One thing I noticed that if I change your MAX_LED from 8 to 200 then half of my Dot matrix strip turns on. And I can turn it whole on by giving MAX_LED to 800 the entire strip turns on. But the issue is even if I downgrade back to MAX_LED 8 it doesnt turn off the remaining LEDs. Plus all the colors given in Set_LED(0,255,0,0,) or with any random LEDnum at any position it doesnt work. The LEDs that turns on after changing total LED numbers from MAX_LED to 200 or so are all the same color. Please explain in brief to solve this I will be glad. Out of all the videos yours seems to be the most easily doable yet i cant x)
Im using this method and try to control more LED strip ( 8 led strip with 64 led on each strip). Im using STM32F407VET6 with 2 different timer and DMA set like you did. The led strip now is only shining half the number of LED on each strip. I think this is because limitations of DMA. Can I try this with using DMA burst feature ? And if you could do you have any document or example of using DMA burst transfer.
Hey man, great video but I can't access your blog anymore. Everytime I get very invasive pop ups, and it only happens with your website. Can you solve this issue?
Is it possible to run with internal system clock, please reply it as i cant generate any clock using internal clock but it work fine using external clock.
Been there, tried without DMA (using just GPIO pin registers): it was barely working. And if you have also interrupts for other peripherals, forget about it.
@@Sayeesh-kc5yy I used the STM32F072RBT chip. Inside my program I converted the RGB values for each LED to a long array with PWM data that the timer peripheral would understand. Then a DMA memory transfer would send the data to that timer which would generate the pulse train on the h/w pin. I have sources if you are interested.
Great job!!! So You decidet to publish PWM solution first. :-) I have a question about peripheria clock source (1min 39s). Can You give a link STM32 clock source diagram or which secion in datasheet contain theme?
Hello, How can I make the LED strip start from a non zero. Example I want my LED to start from the forth (number 4) LED on the strip instead of number Zero. I did try to use range. The code compiled but it didnot run on the stm32. I need help.
@@ControllersTech Hello, Thank you for the feedback. I did try this it worked. The difficulty that I am having using a range to do it, instead of hard typing the whole SET_LED one by one. Please help. If there is a video tutorial on LED i will gladly use it. I just want to do more.
Thank you! Got it working on the F031K6 board, I had to use the HSI clock instead because the board dosesn't have a external xtal. I also had to modify a few of the calulations but go it working just fine in the end. The only really question I have is I ended up having to modify the effects code a little bit to get my WS2812 LEDs to work. I ended up having to increase the strip count to double +1 what I was actually using. If I went 1 over I would only get one LED to light but my board had 7 LEDs so I had to set it to x15 LEDs to get it to work. This was this line of code I had to change in the begenning of the for loop in the effects code. for(uint16_t j=0;j
effect code was just for fun. Even I was surprised when I got it to work. And obviously I don't know what's happening inside it, as I never tried to find out more about it
hi Controllers Tech, I am very new to microcontroller programming. I have question on the effect of why you initialized pwmData[24] as uint16_t type on line 67 in main.c and then when starting the HAL_TIM_PWM_Start_DMA function in line 80, you then cast it to uint32_t pointer? How does this affect the data stream? Sorry not sure I get this part.
Initialised with 16 bit because that's the maximum length of the data we can send. i used a 16 bit timer right.. Hal pwm start dma takes 32 bit parameter, that's why needed to typecast
Hello, for unknown reasons I have problem with "HAL_TIM_PWM_PulseFinishedCallback" function. I don't know why but my f429zi won't execute this callback and stop DMA. Any idea why can it be so? DMA data is sent but PWM wont shut up. :-) And HAL_TIM_RegisterCallback is disabbled in c file.
I had connected my nucleof411R E tyo check it and "HAL_TIM_RegisterCallback" is working. After that I connected nuclewof429zi and it also started to work.
@@ControllersTech Funny and strange thing. I made some additional ivestigations and tests and noticed that: void HAL_TIM_PWM_PulseFinishedCallback(TIM_HandleTypeDef *htim){ HAL_TIM_PWM_Stop_DMA(&htim1, TIM_CHANNEL_1); HAL_GPIO_WritePin(LD1_GPIO_Port, LD1_Pin, GPIO_PIN_SET); //I add this line to test callback execution } is not executed in debug mode (LD1 led is not lighting) BUT when I run program HAL_TIM_PWM_PulseFinishedCallback is executed. Do You have idea why is it so?
You can choose to do the hex color setting with this void Set_LED_HEX(int LEDnum, uint32_t colorValue) // { LED_Data[LEDnum][0] = LEDnum; LED_Data[LEDnum][2] = (colorValue >> 16) & 0xFF; // R LED_Data[LEDnum][1] = (colorValue >> 8) & 0xFF; // G LED_Data[LEDnum][3] = colorValue & 0xFF; // Blue }
You are way too fast for a "tutorial" in the section of CubeMX, expecially in the part of clock selection. What, why? Nothing really explained. And why the computer generated voice? DMA control is cool though!
For all those, who have trouble with getting HAL_TIM_PWM_PulseFinishCallback() to work - You have to Enable the interrupt from corresponding DMA channel in NVIC settings category in Cube and set its priority. Otherwise, the interrupt is not fired and the function never gets called.
I thought the interrupt is automatically enabled once you enable the DMA. I guess they have changed it.. need to check it again
You're a hero! Thanks!! Definitely the best resource on this topic I found on the internet
For mapping apparent brightness to an LED duty cycle (the 8-bits per color channel in the WS2812 protocol), take a look at gamma. It’s basically an exponential scale. That should map better to perceived brightness than the tangent.
Do you have a video tutorial on how to make it? I am interested. This video makes me want to learn more. What if I want to control more than 12 LED rings?
Great video! Thanks for the guide. I was actually reading the documentation and thought of doing something just like this, but since there was a tutorial already just followed it and it just makes a lot of sense to read when coded.
Did u buy the led strip
Hi,
I guess most of the video is correct. But please have a look at 11:54. You want to send 24 pulses. If you count those shown in the oscilloscope recording you find 28. I had a similar issue on a STML4 with 80MHz. It seems that the DMA callback invocation is simply too slow. I tried to put all the code into RAM but without success. The only thing that cured the problem is to stop the DMA and write a 0 length pulse and after that stop the timer. Certainly to configure a 25th pulse with 0 length will do the same because this will be the repeated value after DMA is not updating anymore.
So you comment 'DMA doesn't stop' at 10:56 is actually wrong. The reason why PWM continous is because the timer is running. Therefore you don't need to stop DMA (it stops reliably after 24 updates of the CCR register of the belonging timer). However, you need to stop either the timer itself or the output of the belonging timer channel.
You're quite right. The DMA callback routine takes a little time, enough to send an additional 4 pulses. I had the same issue (using an STM32F373 @ 72 MHz). My solution was to : a) put my LED code data in an array with 48 bytes offset data at the the beginning that only contain zeros, my real data at element data[48] and further. b) change the DMA mode from normal to circular. In this way, the DMA will loop back to the beginning and load the TIM OC register with '0' values (the offset stuff), therefore making the PWM output value low until the callback routine is triggered by the NVIC. My pulses are now a perfect multiple of 24 bytes.
Also, you could make the DMA do data conversion, so store your led codes as bytes, then let the DMA covert them to words. This really shrinks your memory usage if you have a lot of LEDs in the string :
dmaInit.PeriphDataAlignment = DMA_PDATAALIGN_WORD; /* UInt8 ==> UInt32 ! DMA doet de konversie.*/
dmaInit.MemDataAlignment = DMA_MDATAALIGN_BYTE;
Excellent video! Only issue I have encountered is the excessive RAM memory utilization when driving 300 LEDs: 14.2 kiB. The best (but hard to implement) solution would be using circular DMA, but I couldn't be bothered. Instead you can also go back to the DMA configuration screen for the timer and say you want "Byte" data size at the memory size (and leave the peripheral data width alone, still "Half Word"). Then in code you can change the DMA buffer to uint8_t instead. This will half the memory footprint. Be careful what you pass to the the length parameter of HAL_TIM_PWM_Start_DMA, I suspect this counts in "data width" units.
Thanks a lot!!
After simple modification, I successfully reproduced on the bluepill board.
F103C8 have 20K RAM can drive 333 LEDs (With Brightness adjustment) or 358 LEDs(Without Brightness adjustment)
I think... If we use DMA's Half-Transfer Complete event (HT event) and Transfer Complete event (TC event) to DMA processing...
Able to use less RAM and can drive more LEDs.
Hi, can you please help me with the code modification for the bluepill board? Thanks.
@@hulyjoe You just have to use TIMER2 with the same configs, for some reason there is an conflict using TIMER1 and external clock
10:52 it's not DMA sending the data! It's just the timer keeps working in PWM mode with the latest value left in its CCR register. DMA only sends 24 values you specified in the code and that all, it's not circular. In this case you need to add a 25th value which is equal to 0 to get rid of extra pulses.
I successful made the project works on STM32F407, with Keil C IDE. One strange point that I noticed when porting is that the program would unexpectedly stuck at: while (!datasentflag), even this variable shows value 1 when in debugging mode.
To solve this bug, I had to declare it as volatile variable like this: volatile int datasentflag=0; (with #define USE_BRIGHTNESS 0)
Thanks you very much for this great tutorial!
Question what files did u include with your project to run this code
I had the same problem. But in my case it only appears if I select a 'Release' configuration. In 'Debug' configuration it works ok. But another strange problem is that when I run the program from the IDE, it stucks in the middle of the 4th color while sending the data. Or even at the first color. But after pressing reset it works. I don't understand what's going on.
You can do it without DMA. I've had the W2812B running with PWM on an xmega32. You use the "overflow" interrupt to update the duty cycle for the next cycle since W2812B timing is essentially PWM with 33% and 66% duty cycles for 0 and 1 respectively
That's basically correct, but using DMA you are offloading the task of updating the timer's CCR-register (PWM's duty-cycle) from the CPU to hardware (namely, the DMA). This way, you save valuable processing time for other (possibly real-time-)tasks, which may otherwise become delayed themselves.
can you please bit elabrate the this idea
@@Sayeesh-kc5yy the golden rule is: use the DMA controller when possible, in order to free the main processor for other things. your main chip is too precious to do just dumm byte sending and receiving. Of course, this scheme is only fully functional with the usage of an RTOS.
Thank you so much for the tutorial, it was perfect!
I don't know if I translated the code properly, but in my case I had to put the pixel data from MSB to LSB into the pwm array starting from 0, not 23 as in the original code... Excellent tutorial btw
This is so freaking usefull and easy to follow.... :)
you've on the logic analyzer a decoder for WS2812 protocols.
Fantastic tutorial, thank you!
Great video.
Question:
Could you show me how to control a section (few leds) of the LED strip with a push button.
Very nicely explained ...!!!
Excellent video!
Wonderful video! Thank you very much :).
Hello
This is South Korea.
hello
Great video,
Can I expect a video on UART parsing?
Ethernet related
And TI controllers videos
No TI, at least not in near future.. but yes I'll work on ethernet probably
Hi I have gone through your code and worked out the same. I am using the F446"ZE" nucleo for ws2812 Dot Matrix of 8x32 matrix total 256 LEDs. I tried ur code in it. So your Set_LED and Set_Brightness(45) is not working for me. Please offer some help. I am a newbie. One thing I noticed that if I change your MAX_LED from 8 to 200 then half of my Dot matrix strip turns on. And I can turn it whole on by giving MAX_LED to 800 the entire strip turns on. But the issue is even if I downgrade back to MAX_LED 8 it doesnt turn off the remaining LEDs. Plus all the colors given in Set_LED(0,255,0,0,) or with any random LEDnum at any position it doesnt work. The LEDs that turns on after changing total LED numbers from MAX_LED to 200 or so are all the same color. Please explain in brief to solve this I will be glad. Out of all the videos yours seems to be the most easily doable yet i cant x)
Why do you need an extra cell 0 in LED_Data? The LED number is already clear from the array cell number :/
Im using this method and try to control more LED strip ( 8 led strip with 64 led on each strip). Im using STM32F407VET6 with 2 different timer and DMA set like you did. The led strip now is only shining half the number of LED on each strip. I think this is because limitations of DMA. Can I try this with using DMA burst feature ? And if you could do you have any document or example of using DMA burst transfer.
Hey man, great video but I can't access your blog anymore. Everytime I get very invasive pop ups, and it only happens with your website. Can you solve this issue?
Sure. But I need some more info about this. Please contact me on telegram @controllerstech
Hi, we use STM32 and we would like to connect the LED strip , any help how to do it pls
Please Watch the video, its explained in it.
Is it possible to run with internal system clock, please reply it as i cant generate any clock using internal clock but it work fine using external clock.
Yes everything is possible with internal clock.
Hi! i have problem with HAL_TIM_PWM_PulseFinishingCallback. When I call it, no pwm pulse is generated. Can you help me.
You don't call it. It will be called automatically when the pulse is finished
Thank you
what a good video, thanks I needed it
although I have a question
is it necessarily to use DMA to send data?
You can use other things like spi, or timer interrupt. But with dma, the cpu remains free for other work
@@ControllersTech OK thank you
Been there, tried without DMA (using just GPIO pin registers): it was barely working. And if you have also interrupts for other peripherals, forget about it.
hey hi can you please explain how did you do@@edwinvp
@@Sayeesh-kc5yy I used the STM32F072RBT chip. Inside my program I converted the RGB values for each LED to a long array with PWM data that the timer peripheral would understand. Then a DMA memory transfer would send the data to that timer which would generate the pulse train on the h/w pin. I have sources if you are interested.
Great job!!! So You decidet to publish PWM solution first. :-) I have a question about peripheria clock source (1min 39s). Can You give a link STM32 clock source diagram or which secion in datasheet contain theme?
I a snapshot from cubemx
Thanks a lot! I'm going to try this tomorrow. Thank you
Excelente!👍
may i ask where is the variable color at 8:44 come from?
check 9:57
Hello, How can I make the LED strip start from a non zero. Example I want my LED to start from the forth (number 4) LED on the strip instead of number Zero. I did try to use range. The code compiled but it didnot run on the stm32. I need help.
You have to keep the first 3 of them to 0. They are connected in series so you have to proceed in that manner.
@@ControllersTech Hello, Thank you for the feedback. I did try this it worked. The difficulty that I am having using a range to do it, instead of hard typing the whole SET_LED one by one. Please help. If there is a video tutorial on LED i will gladly use it. I just want to do more.
Thank you! Got it working on the F031K6 board, I had to use the HSI clock instead because the board dosesn't have a external xtal. I also had to modify a few of the calulations but go it working just fine in the end. The only really question I have is I ended up having to modify the effects code a little bit to get my WS2812 LEDs to work. I ended up having to increase the strip count to double +1 what I was actually using. If I went 1 over I would only get one LED to light but my board had 7 LEDs so I had to set it to x15 LEDs to get it to work. This was this line of code I had to change in the begenning of the for loop in the effects code.
for(uint16_t j=0;j
effect code was just for fun. Even I was surprised when I got it to work. And obviously I don't know what's happening inside it, as I never tried to find out more about it
How to use logic analyzer for debugging please let me know?
hi Controllers Tech, I am very new to microcontroller programming. I have question on the effect of why you initialized pwmData[24] as uint16_t type on line 67 in main.c and then when starting the HAL_TIM_PWM_Start_DMA function in line 80, you then cast it to uint32_t pointer? How does this affect the data stream? Sorry not sure I get this part.
Initialised with 16 bit because that's the maximum length of the data we can send. i used a 16 bit timer right..
Hal pwm start dma takes 32 bit parameter, that's why needed to typecast
Hello, for unknown reasons I have problem with "HAL_TIM_PWM_PulseFinishedCallback" function. I don't know why but my f429zi won't execute this callback and stop DMA. Any idea why can it be so? DMA data is sent but PWM wont shut up. :-) And HAL_TIM_RegisterCallback is disabbled in c file.
What is most strange sometimes this interrupts works but when I change some setting it dissapears in next compilation ... :/
I had connected my nucleof411R E tyo check it and "HAL_TIM_RegisterCallback" is working. After that I connected nuclewof429zi and it also started to work.
You are using rhat transferfinished variable right ?
Because if you load pwm with another set of data, it will keep going..
@@ControllersTech Funny and strange thing. I made some additional ivestigations and tests and noticed that:
void HAL_TIM_PWM_PulseFinishedCallback(TIM_HandleTypeDef *htim){
HAL_TIM_PWM_Stop_DMA(&htim1, TIM_CHANNEL_1);
HAL_GPIO_WritePin(LD1_GPIO_Port, LD1_Pin, GPIO_PIN_SET); //I add this line to test callback execution
}
is not executed in debug mode (LD1 led is not lighting) BUT when I run program HAL_TIM_PWM_PulseFinishedCallback is executed.
Do You have idea why is it so?
You can choose to do the hex color setting with this
void Set_LED_HEX(int LEDnum, uint32_t colorValue) //
{
LED_Data[LEDnum][0] = LEDnum;
LED_Data[LEDnum][2] = (colorValue >> 16) & 0xFF; // R
LED_Data[LEDnum][1] = (colorValue >> 8) & 0xFF; // G
LED_Data[LEDnum][3] = colorValue & 0xFF; // Blue
}
How do you think is it complicated to create FTP Server with STM32 + ENC28J60 ? This could be useful for backup files.
Thank you.
Don't know haven't worked on it... Don't have the module
This works with LED strips ?
if the driver is same, then yes
I have one extra bit in the beginning and I can't get rid of it
Great
Very nice
thanks!
Unfortunately, it didn't work with my processor stm32f030f4
If it have DMA and timer, then it should work as usual
@@ControllersTech Unfortunately i try a lot
@@ControllersTech I have question about how I can change idel from high to low
Nice video. But when I try to put LEDs in a loop it doesn't work. It stays same as start up colors. How can I fix this?
llike it
You are way too fast for a "tutorial" in the section of CubeMX, expecially in the part of clock selection. What, why? Nothing really explained.
And why the computer generated voice?
DMA control is cool though!
There is nothing to explain there. Anyone who used the STM32 mcu would know how to configure clock.
Çalışmıyor hiç boşuna uğraşmayın