A weird problem - Lost chars in USART

Go To Last Post
25 posts / 0 new
Author
Message
#1
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Hi,

 

I'm programming AT90CAN128 microcontroller to communicate a module via USART peripheral.

I'm using a ringbuffer (buf) for storing the receive bytes from the module and I manage it using http://www.fourwalledcubicle.com/files/LightweightRingBuff.h 

My ISR looks like :

 

ISR(USART1_RX_vect)
{     
    //code to be executed when the rx pin of the USART receives a char
    char c = UDR_N;
    RingBuffer_Insert(&buf,c);
    if (c == '\n')
        RingBuffer_Insert(&buf,NULL);
}

What happens is when I send an "AT" (a command from an AT commands of the module) from the MCU to the module, I receive in the ringbuffer, as a reply from the module, just : 13 , 10 ,79 , 10 instead of receiving : 13,10,79,75,13,10.

 

where are those missing characters gone ?

any help will be appreciated

This topic has a solution.

Last Edited: Wed. Apr 20, 2016 - 11:53 AM
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

forgot to mention that the baudrate is 250000 Kbps and the clock is 8Mhz --> 0% error.

Last Edited: Tue. Apr 19, 2016 - 01:39 PM
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0
(ISR(USART1_RX_vect
}

Really?? Don't you think you'd get more mileage from:

ISR(USART1_RX_vect)
{

?

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

The code you have shown will not even compile. Please do not type code into the forum. Use your coding editor to actually type, compile and test a minimal but complete program that demonstrates the problem. Then copy it VERBATIM into a post here.

 

If you want me to guess then I think your program spends too much time somewhere with interrupts disabled and incoming data at the UART is missed because the ISR gets no chance to execute.

As of January 15, 2018, Site fix-up work has begun! Now do your part and report any bugs or deficiencies here

No guarantees, but if we don't report problems they won't get much of  a chance to be fixed! Details/discussions at link given just above.

 

"Some questions have no answers."[C Baird] "There comes a point where the spoon-feeding has to stop and the independent thinking has to start." [C Lawson] "There are always ways to disagree, without being disagreeable."[E Weddington] "Words represent concepts. Use the wrong words, communicate the wrong concept." [J Morin] "Persistence only goes so far if you set yourself up for failure." [Kartman]

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

I'm sorry for that, it's because my language is right to left and english is the oposite so everything mixed up.

I'll fix it.

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

"baudrate is 250000 Kbps"

Well, that is fast (I only dared 115 200 bps with a 16 Mhz Xal : I am 4 (E3???) times more prudent)...

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

kobidon wrote:

forgot to mention that the baudrate is 250000 Kbps and the clock is 8Mhz --> 0% error.

That's just a sloppy error I hope? Either 250 Kbps or 250000 bps. You can't get 250000 Kbps on an AVR. ;-)

 

Anyway.. That's about 25 K chars per second. At 8 MHz that gives you on average​ 320 CPU clocks per character. That could be quite tight (or even un-doable) depending on how much processing is needed.

As of January 15, 2018, Site fix-up work has begun! Now do your part and report any bugs or deficiencies here

No guarantees, but if we don't report problems they won't get much of  a chance to be fixed! Details/discussions at link given just above.

 

"Some questions have no answers."[C Baird] "There comes a point where the spoon-feeding has to stop and the independent thinking has to start." [C Lawson] "There are always ways to disagree, without being disagreeable."[E Weddington] "Words represent concepts. Use the wrong words, communicate the wrong concept." [J Morin] "Persistence only goes so far if you set yourself up for failure." [Kartman]

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

In an Arduino (clock is twice faster ), at 155.2 Kbit/s (2 (E3) times slower : this leads to 320*4 cycles per character), Arduino's ring buffer is very stable and does not lose anything....

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

I bet that if you do

cli();
_delay_ms(1);
sei();

in your loop() function then even the fantastic Arduino ring buffer will lose characters. This was the point I was trying to make above. Missing characters is not to be blamed on the ring buffer, but on surrounding code that blocks the ring buffer from accessing the port in time before an overrun occurs.

As of January 15, 2018, Site fix-up work has begun! Now do your part and report any bugs or deficiencies here

No guarantees, but if we don't report problems they won't get much of  a chance to be fixed! Details/discussions at link given just above.

 

"Some questions have no answers."[C Baird] "There comes a point where the spoon-feeding has to stop and the independent thinking has to start." [C Lawson] "There are always ways to disagree, without being disagreeable."[E Weddington] "Words represent concepts. Use the wrong words, communicate the wrong concept." [J Morin] "Persistence only goes so far if you set yourself up for failure." [Kartman]

Last Edited: Tue. Apr 19, 2016 - 03:55 PM
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

I never use _delay_ms (nor its Arduino brother) with Arduino -except at init , never in the main loop- (check the 32- bits elapsed milliseconds, which use interrupts....)...

 

Did OP use optimiser options (else, code might be slow)?

Can he ... change the Xal?

Can he ... slow the incoming flow (on the PC side, say)?

Last Edited: Tue. Apr 19, 2016 - 04:17 PM
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

My _delay_me() was of course just for demonstration. Any long(ish) executing code, e.g. printf(), will do.

 

Same for the explicit cli()/sei(). Having the time-consuming code in an ISR will do the same trick.

As of January 15, 2018, Site fix-up work has begun! Now do your part and report any bugs or deficiencies here

No guarantees, but if we don't report problems they won't get much of  a chance to be fixed! Details/discussions at link given just above.

 

"Some questions have no answers."[C Baird] "There comes a point where the spoon-feeding has to stop and the independent thinking has to start." [C Lawson] "There are always ways to disagree, without being disagreeable."[E Weddington] "Words represent concepts. Use the wrong words, communicate the wrong concept." [J Morin] "Persistence only goes so far if you set yourself up for failure." [Kartman]

Last Edited: Tue. Apr 19, 2016 - 05:38 PM
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

The OP code should fit within 320 cycles. There can be another interrupt that takes more than 3 * 320 cycles.

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Well, I learnt (and understood, at least I hope) that sequences like cli() ... instructions..sei() should be **very** short (typically copying global, volatile structures in a consistent way) and never -for me- call functions (non inlined ones : RingBuffer.h's are inlined and fast)...

 

Another explanation would be that OP's buffer is full (no need to block interrupts) and empties slowly (last character(s) is/are discarded): if his program is fast enough in the average -but a portion of the program is slow and does other things -send debugging messages, say?- than consuming input data, doubling -say- the size of the ringbuffer may be enough. Anyway, trying to have a faster clock and a  slower data flow (if it is possible) could hide/cure this weird behavior.

Having  Reading all OP's ISR (which are mutually blocking, and should be short, making reading easy ) and at least portions of the main thread with interrupt-protected (with ATOMIC BLOCKs or cli()/sei()) sequences would be very useful...

Last Edited: Tue. Apr 19, 2016 - 06:06 PM
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

JohanEkdahl wrote:
Having the time-consuming code in an ISR will do the same trick.

We only see an ersatz fragment, right?  But note that that fragment calls the ring buffer function.  If not "always inline" then the full save-restore overhead will put extra pressure on cycle counts.

 

We also don't see things like pertinent buffer sizes and whether the functions are blocking or not.  E.g. if blocking and the ring buffer fills up and is not consumed fast enough, there may be no place to put new characters.

 

All speculation as we have no code and no numbers on how often stuff is being processed nor on the width of these messages.

You can put lipstick on a pig, but it is still a pig.

I've never met a pig I didn't like, as long as you have some salt and pepper.

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

I fixed the problem with the code above.

 

I'm working with a 250000 bps. It was a sloppy mistake above :/

also checked other baudrates with error < 3% http://wormfood.net/avrbaudcalc.php  (thought maybe it will fix the problem) but nothing, same problem.

 

Maybe the call to RingBuffer_Insert() in the ISR takes too long ? for example, if the RingBuffer_Insert() is taking care of 1 received char and suddenly another USART interrupt occurs, the first char handled will be lost... 

what do you think ? maybe I need to write the ISR in a more efficient way ?

 

RingBuffer size is too big to hold the message, the lost chars occurs in the beginning of the program so size is not the problem. 

 

Last Edited: Wed. Apr 20, 2016 - 08:20 AM
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

You need to measure how long it takes to service the rx data. The studio simulator is handy for this. From that you can determine what percentage of cpu time is spent. If it spends 50% of its time handling rx data, then everything else runs half as fast.

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

"for example, if the RingBuffer_Insert() is taking care of 1 received char and suddenly another USART interrupt occurs, the first char handled will be lost... "

 

Well,  RingBuffer_Insert() is "called" (I verified it was inlined) from your ISR receive interrupt, and no other interrupt is allowed. Is is unlikely that incrementing an index, checking it and indexing an array takes 300 cycles (I have no simulator to verify it) ... therefore I do not think your UART receive is too slow; but maybe there are other ISRs? "debugging" prints  -can be very greedy- ?

 

There are two things which puzzle me :

 

a) you edited the code of the ISR in the 1rst post (fixed typos; hope you copied and pasted) , and, when \n is met, you insert NULL into your ringbuffer.

For me, NULL is a pointer towards ...nothing (a 16 bit -for avrs- integer, but RingBuffer_insert is meant to insert ... 8 bit integers). I would simply insert 0 -or \0 - this does not make things more difficult to read

 

b) You insert \n, then a "null" but is \n that useful?

 

Therefore, I might rewrite your ISR like that :   

 

ISR(USART1_RX_vect)
{     
    //code to be executed when the rx pin of the USART receives a char
    char c = UDR_N;
    if (c != '\n'){
        RingBuffer_Insert(&buf,c);
    } 
    else {
    
        RingBuffer_Insert(&buf,0);
    }
}

 

 

 

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

dbrion0606 wrote:
Well,  RingBuffer_Insert() is "called" (I verified it was inlined) from your ISR receive interrupt, and no other interrupt is allowed

As I know, other interrupts are allowed if they have precedence on the USART ISR in the ISR table (like Timer Interrupt...) 

 

dbrion0606 wrote:
but maybe there are other ISRs?

 

There are other ISRs i my code like Timer interrupts...

 

dbrion0606 wrote:
"debugging" prints  -can be very greedy- ?

 

what do you mean by "debugging" prints ? can you give me an example ?

 

dbrion0606 wrote:
Therefore, I might rewrite your ISR like that :   

 

You'r right. change accepted 

This reply has been marked as the solution. 
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 1

"debugging" prints ? can you give me an example ?

 

No, because I wrote it was a bad idea (and I do not wish good ideas be mixed with bad ones).  But you can figure out that formatted printing can eat many cycles  -even sending 18 cars "line xxx reached"  at 9600 bps needs 18 ms (and therefore these cycles are not used to consume/decode the ringbuffer, say) and make the program slower..... (when it is already slow), just to know a given state/line has been reached. ...

 

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

A good hint! 

I used the breakpoint option "When hit..." then printf to the output. 

That's exactly what you said to avoid of.

thanks!

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

kobidon wrote:
As I know, other interrupts are allowed if they have precedence on the USART ISR in the ISR table (like Timer Interrupt...)

Err NO. What you are thinking of is what happens when two interrupt conditions have happened at once. If the I bit in SREG is set and the AVR makes an opcode fetch it will check the interrupt flags and for any that are set it will pick the one with the lowest vector table entry to handle first. But this is ONLY if the I bit in SREG is set. There is no "interrupt priority" system in tiny/mega (there is when you reach Xmega) so once a vector is made (and the I bit in SREG is cleared during the vector process) then no other interrupt can interrupt the handling of the first one.

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Well, you (and I : using a bit banged -ala Arduino, I fear- LCD with gothical fonts, great sized for displaying debugging messages would have been a very bad idea) should thank JohanEkdahl (in post 9) .

Last Edited: Wed. Apr 20, 2016 - 01:04 PM
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Sure, Also thanks to JohanEkdahl !!

 

JohanEkdahl wrote:
Anyway.. That's about 25 K chars per second. At 8 MHz that gives you on average​ 320 CPU clocks per character. That could be quite tight (or even un-doable) depending on how much processing is needed.

 

can you please explain me how did you get to this number ? cause 8M/25K = 32 ...

also, what do you mean by "quite tight" ? what could be the implications on my program from choosing this baud rate ?

Last Edited: Wed. Apr 20, 2016 - 01:22 PM
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

kobidon wrote:
cause 8M/25K = 32

You might want to pick out a calculator and punch in 8000000 / 25000. On my calculator it comes out as 320.

 

Note that it's 25K (not 250K). It's the characters per second that is interesting, and you get roughly 1 char per 10 bits (incl start and stop bits) in the absolute worst burst-mode case.

250 K bps is approximately 25 K cps.

 

kobidon wrote:
what could be the implications on my program from choosing this baud rate ?

(Sigh.. What have we been talking about through these almost endless threads?) If the processing per char takes more time than there is time available then you will miss some chars.

As of January 15, 2018, Site fix-up work has begun! Now do your part and report any bugs or deficiencies here

No guarantees, but if we don't report problems they won't get much of  a chance to be fixed! Details/discussions at link given just above.

 

"Some questions have no answers."[C Baird] "There comes a point where the spoon-feeding has to stop and the independent thinking has to start." [C Lawson] "There are always ways to disagree, without being disagreeable."[E Weddington] "Words represent concepts. Use the wrong words, communicate the wrong concept." [J Morin] "Persistence only goes so far if you set yourself up for failure." [Kartman]

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

250 Kbits per second == 25 K cars per second == 25 cars per millisecond

At 8 Mhz, you have 8000 cycles per millisecond

My best calculator, bc, says :

 

sh-4.1$ bc
8000/25
bc 1.06
Copyright 1991-1994, 1997, 1998, 2000 Free Software Foundation, Inc.
This is free software with ABSOLUTELY NO WARRANTY.
For details type `warranty'.
8000/25
320

 

i.e it computes 320 cycles per character second (if it had been only 32 cycles, it would have been a desparate case).

"quite tight" means you should be very careful to avoid eating too many cycles and blocking your ISR (if it is unavalaible for 2 or 3 caracters, i.e 640/960 cycles -less than one millisecond-, you will lose characters...but it is this topic....

 

 

Last Edited: Wed. Apr 20, 2016 - 01:53 PM