5
\$\begingroup\$

I have a simple program, which receives data from UART interface and drives a WS2812b led strip. So, I'm using USART3 to receive data (in IRQ mode) and T3C2 (timer 3, channel 2) to drive LEDs. Here is the code:

https://hastebin.com/rucumovero.cpp

If I use the uart interface without enabling a timer, everything works just perfect, but when I enable the timer I start losing data. For example, here the log if I send 5 similar 32 byte packets [0x41..0x60]

read 29 byte(s): 41 42 43 44 45 46 47 48 49 4A 4B 4C 4D 4E 4F 52 53 54 55 56 57 58 59 5A 5B 5C 5D 5E 5F 
read 29 byte(s): 41 42 43 44 45 46 47 48 49 4A 4B 4C 4D 4E 51 52 53 54 55 56 57 58 59 5A 5B 5C 5D 5E 60 
read 28 byte(s): 41 42 43 44 45 46 47 48 49 4A 4D 4E 4F 50 51 52 53 54 55 56 57 58 59 5A 5D 5E 5F 60 
read 29 byte(s): 41 42 43 44 45 46 47 48 4B 4C 4D 4E 4F 50 51 52 53 54 55 56 57 58 5A 5B 5C 5D 5E 5F 60

every time 3~4 bytes are lost.

I tried to use different baud rate, different usart port - the result is the same. The timer interval is set in line 142:

timer_set_period(TIM3, WSP);

where

#define TICK_NS (1000/72)
#define WSP (1300 / TICK_NS)

If I comment out this line, the problem will disappear, but, of course, I won't be able to drive LEDs without it.

So, it looks like I can't drive WS2812b leds and receive data from UART at the same time. Is it supposed to behave like this? Any idea how can I fix it?

\$\endgroup\$
3
  • 3
    \$\begingroup\$ First determine if you are looking at hardware glitch (eg: corrupt frame due to LED's PWM) or software glitch (eg: somewhere something halts too long). Hint: USART_SR->ORE. \$\endgroup\$
    – Jeroen3
    Commented Dec 28, 2017 at 10:49
  • \$\begingroup\$ @Jeroen3 Thank you! Yes, I'm getting USART_SR_ORE flag when I'm loosing data. Is there any way to find the reason of overrun? \$\endgroup\$ Commented Dec 28, 2017 at 11:11
  • 1
    \$\begingroup\$ I’ve had some experience with data loss and (high baud rate) UART. The only good solution seems to be to use the highest interrupt priority for the UART (and for the UART only) and to make sure that the UART IRQ is only disabled for very short durations (e.g. when evaluating the receive buffer). In my case I couldn’t use the DMA because I didn’t know how many bytes I would receive. \$\endgroup\$
    – Michael
    Commented Dec 28, 2017 at 15:27

2 Answers 2

6
\$\begingroup\$

Looking at this

#define TICK_NS (1000/72)
#define WSP (1300 / TICK_NS)

your timer takes about 100 steps (if i got it right from the specs) and then runs whatever routine you told it to. I couldn't clearly see how much time it takes for your program to make one step, but it seems as if your timer routine should run very often (couple 1000 times per second or more).

Now if your uart routine works as expected without the timer, but loses data when you introduce it, it strongly suggests that the timer disrupts your serial communication.

This could be because:

  1. your timer routine runs to often
  2. the timer routine is too long
  3. the timer interrupt has a higher priority than the uart interrupt.

Or maybe its all three put together.

Possible solutions:

  1. An STM32 has something called the NVIC (Nested Vector Interrupt Controller), which you could use to change the the interrupt-priorities.

  2. When you use two or more interrupts you have to consider that they may run at nearly the same time(this happens more often when both interrupts run at similar frequencies), and one routine may loose information because of this (especially with uart). So you have to think of a way to handle this explicit case -> locking mechanism or altering the priorities.

  3. The longer your routine takes, the higher is the chance that you enter the case described in point two. It's usually good practise to make interrupt routines as short as possible, and never ever use delays or loops inside an interrupt.

  4. Consider if there is a way to run your timer routine considerably less often. This way you at least minimize the chance of data loss, but to completly rule it out, you will have to do the things described in point 1-3.

\$\endgroup\$
6
  • 3
    \$\begingroup\$ Possible solution 5: Consider not using interrupts to drive the LED strip. Especially if timing is not critical it should be enough to wait until a certain point in time in the main loop. \$\endgroup\$
    – Michael
    Commented Dec 28, 2017 at 14:35
  • \$\begingroup\$ @Michael Isn't the same as increasing interrupt priority? \$\endgroup\$ Commented Dec 28, 2017 at 20:33
  • \$\begingroup\$ @user8837440 Thank you for your answer! What is the best way to measure time intervals to debug timer frequency and to evaluate how long my interrupt handler routine runs? \$\endgroup\$ Commented Dec 28, 2017 at 20:37
  • \$\begingroup\$ Actually it should be very easy to find out how fast your "timerclock" is ticking, if you didn't change the settings you should find the value in the datasheet, it's propably the same as your quarz or external clock frequency (or whatever you use), maybe with a set prescaler. However if you do want to measure it, I suggest to toggle an IO-pin everytime your timerroutine is called and connect the pin to an oscilloscope. The debugger may also tell you how long a command in your program takes but since I use a different programming tool I sadly can't give you any usefull tip on how to proceed \$\endgroup\$ Commented Dec 28, 2017 at 21:00
  • 1
    \$\begingroup\$ @Tutankhamen: In a way it’s the same because in either case nothing ever blocks your interrupt. However, trying to use as few IRQs as possible and to make them as small as possible (e.g. only wake up the μC and set a flag for the actual processing in the main loop) has the added benefit of removing potential for race condition. This way you can also always trust in your processing to happen in the same order (in the main loop). It’s obvious when I mention it like that, but just imagine what would happen if you’d suddenly decide to use the UART debug print in an IRQ (but also somewhere else). \$\endgroup\$
    – Michael
    Commented Dec 29, 2017 at 7:38
6
\$\begingroup\$

I think the problem is the time spent by timer interrupt routine and you have not configured the priorities. Either make the timer routine efficient enough, or make a DMA buffer for the UART, or raise the priority (lower priority number) of the uart interrupt (which may mean visual glitches on the display, in this case make critical parts of timer code non-interruptable and see if it is sufficient to help the UART)

Also you can unify all of them in one timer interrupt routine. You either make the timer frequency fast enough for the UART without DMA buffer and poll it & run the normal timer code with a lower frequency, or, make a DMA buffer and run the timer as slow as the normal code needs and poll the UART buffer. This makes things more deterministic in contrast of polling in the process mode.

\$\endgroup\$
1
  • \$\begingroup\$ Thank you for you answer! It looks like priority adjustment works for me, but I'm going to make some changes in interrupt routine (this is not my code - I just use it as is) \$\endgroup\$ Commented Dec 28, 2017 at 20:40

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.