[F4] Neopixel driver using hardware timers

What are you developing?
flyboy74
Posts: 31
Joined: Fri Jan 03, 2020 10:12 am

Re: [F4] Neopixel driver using hardware timers

Post by flyboy74 »

ozcar wrote: Fri May 08, 2020 1:15 pm The other bad news, is that my explanation is a bit long...

Normally, let's say somewhere in the middle of generating the whole long stream of pulses, the execution of the interrupt routine is synchronised with the timer. That is, the interrupt routine always gets called just after the timer update event, when the count starts back at zero. The previous CCR value has just been latched, so it is then "safe" to set a new CCR value, without overlaying the previous value before it could get used, and you also have a (relatively) long time available to do this before the next update event. I'm mentioning CCR, same thing applies to ARR.

Things are different right at the start of the pulse stream kicked off in show_neopixels by turning on the UIE bit. The timer is already enabled at that point, and has been running, still generating update events which will have set the UIF flag. So, at soon as you clear UIE the interrupt will occur, and the interrupt routine will be called. It is not so much the fact that the interrupt routine gets called, but that this call to the interrupt routine is not synchronised with what is happening in the timer, it just depends on when you happen to call show_neopixels.

Usually, this does not cause a problem, but this is a sequence that might happen, with the original interrupt routine, which cleared UIF and then wrote to CCR1:

Interrupt occurs as soon as show_neopixels clears UIE.
Interrupt routine clears UIF.
Update event happens to occur now - this latches previously set CCR1 (0 for no pulse), and sets UIF again.
Interrupt routine sets CCR1 according to first data bit, to be latched at next update event.
As soon as interrupt routine exits, interrupt occurs again as UIF is set.
Interrupt routine clears UIF.
Interrupt routine sets CCR1 according to second data bit, overwriting the value for the first data bit, which has not been latched yet.
Result - first data bit is simply lost, pulse train starts with the second bit.
I couldn't work out how the interrupt is firing incorrectly but now this makes sense.

A simple fix I think is to reset the timer and clear the UIF flag just before enabling the interrupt flag in void show_neopixels(). Please test the updated version on my github :)

Another thing that would interest me is that if I left the pin enabled but disabled the timer would the pin still be pulled low??? I know if I disable the pin then it floats.
ozcar
Posts: 144
Joined: Wed Apr 29, 2020 9:07 pm
Answers: 5

Re: [F4] Neopixel driver using hardware timers

Post by ozcar »

flyboy74 wrote: Fri May 08, 2020 9:54 pm A simple fix I think is to reset the timer and clear the UIF flag just before enabling the interrupt flag in void show_neopixels(). Please test the updated version on my github :)
That is exactly what I already tried, and it seemed to work. Well, I did try to think whether there was still some tiny chance of going wrong.

It could happen that show_neopixels clears UIF, but the update event happens to occur before it gets to enable the interrupts. I think that is no big deal, starting of the transmission is just delayed for one timer period.

What about the update event occurs slightly after show-neopixels enables the interrupts? I tried to work through that too, and came to the conclusion that it was still OK, but hey, that was all of a few days ago, and I've forgotten the details now!

If you are still worried, then maybe set the counter to zero, clear UIF, then set UIE.

Or if you really wanted to be sure, to be sure, you could clear EN in CR1, set the counter to zero, clear UIF, set UIE, and finally turn on EN again. I guess it depends on how hard you want to have to think whether you have covered all bases.
flyboy74 wrote: Fri May 08, 2020 9:54 pm Another thing that would interest me is that if I left the pin enabled but disabled the timer would the pin still be pulled low??? I know if I disable the pin then it floats.
Turning off EN in CR1 just stops the counter, the output pin will still be driven by the timer. If you just did that at some random point during the transmission, you might happen to catch it when the pin was driven high rather than low, but depending on where you do it you might "know" that it was low at that time.
flyboy74
Posts: 31
Joined: Fri Jan 03, 2020 10:12 am

Re: [F4] Neopixel driver using hardware timers

Post by flyboy74 »

ozcar wrote: Fri May 08, 2020 10:56 pm If you are still worried, then maybe set the counter to zero, clear UIF, then set UIE.

Or if you really wanted to be sure, to be sure, you could clear EN in CR1, set the counter to zero, clear UIF, set UIE, and finally turn on EN again. I guess it depends on how hard you want to have to think whether you have covered all bases.
flyboy74 wrote: Fri May 08, 2020 9:54 pm Another thing that would interest me is that if I left the pin enabled but disabled the timer would the pin still be pulled low??? I know if I disable the pin then it floats.
Turning off EN in CR1 just stops the counter, the output pin will still be driven by the timer. If you just did that at some random point during the transmission, you might happen to catch it when the pin was driven high rather than low, but depending on where you do it you might "know" that it was low at that time.
If you check my code I do set the timer count to 0 before clearing the UIF flag so the counter will be at the beginning of the count before the UIF flag is cleared.

If you check my latest code I have also added the timing for Treset so that if someone was trying to refresh the LEDs at a high rate there will always be a long enough low before sending the data. I also added a function to return the status of the transmission so you know when it is finished to send the next update.
ozcar
Posts: 144
Joined: Wed Apr 29, 2020 9:07 pm
Answers: 5

Re: [F4] Neopixel driver using hardware timers

Post by ozcar »

flyboy74 wrote: Fri May 08, 2020 11:19 pm If you check my code I do set the timer count to 0 before clearing the UIF flag so the counter will be at the beginning of the count before the UIF flag is cleared.
You are right, I missed that you said you reset the counter - so what you are doing was my "slightly more cautious" suggestion in my last post. I must go back and see if I can work out again why I came to the conclusion that just clearing UIF would be good enough. There is no practical benefit in saving an instruction or two, but I like tinkering with things like this to get a better understanding.

I certainly did not see it ever misbehave when I tried just clearing UIF, but that does not prove that there is not still some small chance for it to do so. I'm also sort-of surprised that you never saw the original problem yourself. This got me to thinking about the possibility of running a test with another processor reading the pulse stream and yelling loudly if the data received is not as expected.
flyboy74
Posts: 31
Joined: Fri Jan 03, 2020 10:12 am

Re: [F4] Neopixel driver using hardware timers

Post by flyboy74 »

ozcar wrote: Sat May 09, 2020 2:45 am I certainly did not see it ever misbehave when I tried just clearing UIF, but that does not prove that there is not still some small chance for it to do so. I'm also sort-of surprised that you never saw the original problem yourself. This got me to thinking about the possibility of running a test with another processor reading the pulse stream and yelling loudly if the data received is not as expected.
Testing is always a pain. What I sort of do is turn my code loose on the open source community and if it can be broken they will break it :)

I have been thinking of advantages of your DMA vs my timer only methods.

DMA saves CPU and possibly if you use a larger buffer it may save even more CPU but it is at the expense of using DMA

Timer leaves the DMA free to do other things like upodate the buffer holding the LED data. During the timer interrupt it can set multiple channels of multiple synced timers using 1 timer as a master timer and others as slave timers. This means you could drive large displays at good refresh rates. Using just 2 timers you can output 8 bits (whole byte) at once. If you had a 160x120 display broken up into 8 lines then you have a total number of bytes of 160x120x3 = 57,600 at a data rate of 800KHz per byte it is certainly possible to get over 13FPS and you could use DMA to be streaming in the data into the LED buffer from either SPI or DMCI (camera)
ozcar
Posts: 144
Joined: Wed Apr 29, 2020 9:07 pm
Answers: 5

Re: [F4] Neopixel driver using hardware timers

Post by ozcar »

So, after thinking again about it again for the situation if show_neopixels were to just clear UIF and then set UIE (without resetting counter):

A few posts back I looked at what might happen if the update event were to happen between clearing of UIF and setting UIE. Because UIE is still not set, UIF would be set by the timer, but the interrupt routine would not be called at that point. However, one timer period later and the interrupt routine is called as per normal, synchronised with the update event.

So what if the update event happens shortly after setting UIE? Seems to me that is also "business as usual" because the calling of the interrupt is synchronised with the update event, so I don't see how that could cause the interrupt routine to be called twice in quick succession which was part of the cause of losing the first bit.

If you are still not convinced, well, just clear the counter as well like you were doing. (Reminds me of the line "These are my principals. If you don’t like them, I have others." attributed to Groucho Marx.)

With two DMA controllers with 8 streams each, I don't think I would be too worried about using one of the streams. But you seem to have some very big arrays of LEDs in mind.

I still have not looked as using uint8_t values in the DMA buffer. It may not be much different to what I am already doing in using uint16_t, but I have an uneasy feeling that there might be some gottcha to it. I also thought about using a table of all possible sets of values for a byte's worth of data. That could speed up the interrupt processing a lot. If the uint8_t thing works, the downside would be 2k ram (or 2k flash if the timing is set when you compile the program), but at least this would not increase to handle more LEDs.
flyboy74
Posts: 31
Joined: Fri Jan 03, 2020 10:12 am

Re: [F4] Neopixel driver using hardware timers

Post by flyboy74 »

ozcar wrote: Sat May 09, 2020 4:12 am If you are still not convinced, well, just clear the counter as well like you were doing. (Reminds me of the line "These are my principals. If you don’t like them, I have others." attributed to Groucho Marx.)

With two DMA controllers with 8 streams each, I don't think I would be too worried about using one of the streams. But you seem to have some very big arrays of LEDs in mind.
Yer I don't think the counter needs to be reset and it probably just slows down the possible fastest refresh rate.

Yer when I was dreaming about driving matrix array I was thinking that is crazy big but as a concept of driving a matrix display it isn't exactly a high res display and would be possible for the STM32F4 be a slave to drive the display with another processor feeding it the data via SPI or being feed via a camera.
ag123
Posts: 1668
Joined: Thu Dec 19, 2019 5:30 am
Answers: 25

Re: [F4] Neopixel driver using hardware timers

Post by ag123 »

SPI i'd think is 'the other' easy method besides the timers. though for that matter stm32 has pretty elaborate timers.
i'd guess is the varying period lengths which makes fixed periods timer implementation more troublesome.
of course the other way is to do 100% and 0% duty cycle in the interrupts and provided we can have fixed time segments.
that would still take an interrupt controller and so spi + dma may after all be easier.
flyboy74
Posts: 31
Joined: Fri Jan 03, 2020 10:12 am

Re: [F4] Neopixel driver using hardware timers

Post by flyboy74 »

ag123 wrote: Sat May 09, 2020 11:54 am SPI i'd think is 'the other' easy method besides the timers. though for that matter stm32 has pretty elaborate timers.
i'd guess is the varying period lengths which makes fixed periods timer implementation more troublesome.
of course the other way is to do 100% and 0% duty cycle in the interrupts and provided we can have fixed time segments.
that would still take an interrupt controller and so spi + dma may after all be easier.
The timers can create varying period lengths by changing the value of the ARR, it is SPI with DMA that can't do that as it is set to a freq then the DMA just toggles the pin at the set freq.

The main down side to SPI is that you have to create a buffer 3 times the size of the original buffer so all up it takes 4 times the amount of RAM of using timers. SPI also can only do LEDs where the timing are 1/3 and 2/3 of the total freq. SPI doesn't get exact timings either it just gets what ever SPI freq is closest to the prescaler creating but a timer uses a prescaler and a ARR so can create more precise timings. Timers can also create multiple channels so if you want to drive a large matrix you can break it up into several small sections that get updated in parallel.

The main upside to SPI is that once setup it doesn't use any CPU while doing the update because it uses SPI and DMA
ozcar
Posts: 144
Joined: Wed Apr 29, 2020 9:07 pm
Answers: 5

Re: [F4] Neopixel driver using hardware timers

Post by ozcar »

I can see you are not a fan of using SPI for this task. The granularity of the available timing is an issue, but 1/3 2/3 pulses does suit a lot of these type of LEDs, even if you also need to mess with the clock configuration to get the overall period within spec.

As for the increase ram usage - the WS2812B library included with Roger's core (which uses SPI) does not use 4 times more ram, it is "only" 3 times more, because the data is not retained in the "raw" format, only in the "expanded" format. At least, that is how I believe it works.

To be fair, you are also expanding the data, and actually by a factor of at least 8 times - one bit of raw LED data becomes an at least 8 bit value to be put in the timer CCR. Of course, you don't have to create an array 8 times the size of the raw LED data, but that is only because you do the conversion, for one bit at a time, in the interrupt routine. I don't see why the same could not be done when using SPI, and that would not require processing one interrupt per bit of LED data. Even without using DMA, it would be just under 1 interrupt per 5 bits of LED data (with 16-bit frame size), and with DMA it could be 1 interrupt per LED or less depending on size of DMA buffer.
Post Reply

Return to “Projects”