Managing USART interrupt along scanf()

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

Hi!
I'm try to make working a code like this:

--- all initializations and other stuff
char received;
ISR(USART_RX_vect)
{
	received = UDR0; //inutile ora, solo per svuotare fifo
	if (received == 'u')
	printf("\nUnlocked.\n");
	else if (received == 'l')
	printf("\nLocked.\n");
	_delay_ms(10);
}

-- main() etc etc //received is declared out of main
if (received == 'u') {
		printf("\nCalibra? ");
	    scanf("%s", incoming);
-- somthing inside the if
received = 'l';
} //end receiv if

As expected, scanf is interferring with the interrupt, which is work correctly, printing what I ask when I press L or U. But when I press U it will enter in the if, but it remains there, no response with every input (w/ and w/out \n and/or CR characters from PC). IS possible to make them agree? Without the interrupt is working.
PS: as I already said, I'm sorry for bad english.

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

You don't have time to print in the isr. Just set a flag.

Imagecraft compiler user

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

Also scanf() via stdio calls USARTReadChar() which is presum,ably, if you use an RXC interrupt, waiting on character appearing in the ring buffer that the ISR() is feeding in to. The code will never exit from the ReadChar() as it won't get that character until the ISR() ends.

As Bob says this is poor design. Never do "heavy work" in an ISR and especially never call a function which may block on some ISR() completion.

If you are going to use RXC interrupts then do nothing but feed your ring buffer (and perhaps a small amount of error handling). Then have the main() code poll the ring buffer. It can either block on waiting for character arrival or it can just check availability now and again and get on with other work that must be done. When the ring buffer counter says there are bytes in the buffer then have the foreground code extract those.

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

clawson wrote:
Also scanf() via stdio calls USARTReadChar() which is presum,ably, if you use an RXC interrupt, waiting on character appearing in the ring buffer that the ISR() is feeding in to. The code will never exit from the ReadChar() as it won't get that character until the ISR() ends.

First of all thanks for quick reply.

As Bob said I only tried to save in UDR0 the char and stop. But nothing changed, of course, since I don't get the point of Clawson and I didn't solved it.

ISR(USART_RX_vect) 
{ 
   received = UDR0; //inutile ora, solo per svuotare fifo 

} 

I mean, AFTER the first interrupt I receive a U and so I came inside first IF. Here, I'll send another char with scanf and out of interrupt (but it call the interrupt).
Sentding a char will always exit from ISR because I just make a copy of UDR0 w/out other function calls from ISR. (and here I don get what Clawson says). But I realized that maybe it's because a read to UDR0 register from interrupt will flush the register (after ending interrupt) and that is why it doesn't react to commands anymore. It's like scanf() is reading an empty buffer. So if I disable the RXC ISR before every scanf, is a bad idea? (because in that way it works)

PS: The only issue of long ISR is the problem that another ISR can be lost since I'm not sensible to athers ISR during an ISR? Or what?

EDIT: when I send a loooong sequence of chars to atmega, when its stucked, it will exit from the IF condition and entering in normal operation (I'm talking about the first version of code)

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

I modified the code disabling the RXC int register from the ISR if I receive the U, so the scanf is working well. I reenable this at the end of the IF inside the main.

But if I receive more than 3 "U" from terminal, it will output like this:

    tj = 29 °c +/- 1°c t_amb = 22.6 °c

    Unlocked

    Calibra? ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ
    ÿÿÿÿÿÿÿÿÿÿÿÿÿt_amb = 23.5 °c
    ÿÿÿÿÿÿÿÿÿÿÿÿÿt_amb = 21.9 °c
    ÿÿÿÿÿÿÿÿÿÿÿÿÿt_amb = 18.0 °c
    ÿÿÿÿÿÿÿÿÿÿÿÿÿt_amb = 19.7 °c
    ÿÿÿÿÿÿÿÿÿÿÿÿÿt_amb = 23.9 °c
    ÿÿÿÿÿÿÿÿÿÿÿÿÿt_amb = 22.9 °c

where after "Calibra" it should wait for scanf, but it start to print Y and seem to came back to normal loop (because it receive more "U" sended and sensed from scanf, and if char at scanf() is != from "S" it will came to loop). I can play with that, but I missing at all why the string is screwed up.

The ISR is enabled as I want, but sending one U it will output 2 rows of Y without entering in scanf() so avoiding even the printf("Calibra? ");

    ÿÿÿÿÿÿÿÿÿÿÿÿÿt_amb = 22.9 °c +/- 1°c ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ
    ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ
    ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ
    ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ
    ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ
    ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ
    ÿÿÿÿÿÿÿÿÿÿÿÿÿt_amb = 18.0 °c +/- 1°c

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

Quote:

ÿ

is often shown when terminals receive 0xFF.That may give you a clue as to where it's being produced. Note that when UartReadchar is used with stdio that it may return -1 when no character is available. -1 in some interpretations will appear as 0xFF (really 0xFFFF but truncated).

If I were you I'd take a step back from the implementation and concentrate on the design. Work out on paper what you want this program to achieve then implement that. You should come up with a nice clean solution.

If you just throw code at it mixing synchronous/polled use of the UART (the USARTReadchar() being used with scanf()) with asynchronous interrupt driven code (the RXC interrupt) it is never going to work right.

Go back to the design. Decide once and for all if polled/synchronous will meet your needs. Or asynchronous interrupts. Choose one or the other, not both, then implement that.

If you choose asynchronous then use a ring buffer. The RXC ISR will do nothing but put received characters into the ring buffer. There will be a foreground, synchronous function called UARTreadChar() or whatever which will not access the UART hardware directly but will simply read waiting characters from the ring buffer and return those. There is no problem wiring this function up to stdio if you want to do that. If you do then while you can call it direct I think I'd stick to using stdio functions such as getchar() rather than calling it directly.

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

Thanks for the advice. I'll let you know..

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

case 1: no receive interrupts: kbhit() returns true if txc bit is set in status reg. getchar() returns char in udr if kbhit() was true, and putchar sends a char by polling udre.
case 2: rx interrupt is on. rx interrupt handler reads char from udr, adds char to rxbuffer[rxindx], increments the index. kbhit() returns true if rxindx != rxondx getchar() returns char from rxbuffer[rxondx], increments the index. Putchar sends a char by polling udre.
There is a way of telling stdio to use putchar() and getchar().

Imagecraft compiler user

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

bobgardner wrote:
case 1: no receive interrupts: kbhit() returns true if txc bit is set in status reg. getchar() returns char in udr if kbhit() was true, and putchar sends a char by polling udre.
case 2: rx interrupt is on. rx interrupt handler reads char from udr, adds char to rxbuffer[rxindx], increments the index. kbhit() returns true if rxindx != rxondx getchar() returns char from rxbuffer[rxondx], increments the index. Putchar sends a char by polling udre.
There is a way of telling stdio to use putchar() and getchar().

I suppose that in that way you build a circular FIFO... am I right?

BTW, I solved those buggy behaviours in this way

usartwritestring("\nCalibra? > ");

usartflush();
usartreadstring(incoming);

and let the terminal to freely send \n and or \r inputs (there's a check of them inside usartreadstring() ).
I'm conscious that I can use a ring buffer, but for now it's ok, I can freely manage also more than one char. And it works as expected, disabling/enabling UART interrupt when entering in first (Unlocked) if condition. So only setting a flag and disable the USART int from the USART ISR.

And using printf() when I need to manage numbers (expecially larger than 255... UDR0 8bit mode max lenght) avoiding any headache.

OT: The PC terminal send some chars, i.e. I send s\n where \n is a char, not '\'+'n', so 2 chars. I need to include managing of those, and memorize them in a vector (incoming[MAX_CHAR], static, not circular) and I told the function to exit after "\n" or "\r" or keep recording until MAX_CHAR is reached. Now, I noticed that more chars are sent and more RX led is blink, so since there's no handshake I need to be in sinc (or detect my speed ecc) and collect those chars.

After this check:

while(!(UCSR0A & (1<<RXC0)));

and memorisation from UDR, If I do some long stuff, taking more time than there's between one char to another (@9600baud), I'll lose data? That means that if I didn't lose any data until now, I've been too lucky because 16Mhz>>>>9600baud? :D ops :D

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

Quote:

I suppose that in that way you build a circular FIFO... am I right?

Yup, search terms here are "ring buffer" or "circular buffer". Dean Camera has a nice, lightweight implementation for ring buffers that's perfect for use with an RXC or TXC/UDRE interrupt.
Quote:

I'll lose data? T

Which is the whole reason to use an interrupt and a buffer. On "grown up" UARTs (such as the 16550's you used to find in desktop PCs) the UART itself has a 16 character buffer so you could just service it from time to time and still not lose anything. AVR UARTs are "bargain basement" and don't have any of the fancy features of that kind of UART. So if you want buffering you do it in software. And you do it by using the receive or transmit interrupt and a ring buffer.

Your higher level code then just puts or gets characters to the buffer and doesn't touch the hardware.

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

The receive buffer is just an array of chars. To me a one dimensional array is a line or vector, not a circle. If you increment the array index every time you write a char into the array, after a while you will have written into the last element. The obvious choice at this point is to reset the input array index to zero. A very simple concept.

Imagecraft compiler user

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

bobgardner wrote:
The receive buffer is just an array of chars. To me a one dimensional array is a line or vector, not a circle. If you increment the array index every time you write a char into the array, after a while you will have written into the last element. The obvious choice at this point is to reset the input array index to zero. A very simple concept.

Sure, but the structure or the concept now was different: I want to chech the real first char and ignore later data, making "light" on a single portion of this linear vector. With circular one I will lose the concept of first char received. In that way my purpose is to avoid reading of more than N chars, and make analisys inside of these N. With other implementations the ring buffer can be useful.

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

I you clear the input index to zero, the char in array element zero is the first character received. By definition. We on the same wavelength?

Imagecraft compiler user

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

thexeno, I wonder if you are missing the point of using receive interrupt and a ring buffer? Ultimately it adds nothing more to your program than a guarantee that no received character will be lost (assuming you don't disable interrupts for long periods and you consume buffered characters before the buffer overflows). Otherwise just treat the foreground function that retrieves characters from the ring as a direct hardware interfacing, polling routine. You still have a single UartReadChar() that is the heart of all your Uart input and just as before it can be connected to stdin in which case you never call it direct but use the stdio function getchar() whenever you want a character or call gets() if you want a string or scanf() if you want the input to fill some variables etc. It's simply that the routine gets its characters from a buffer not directly from the hardware.

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

What actually determines the 'first' character? Time or the actual value? If you're really constrained for ram, the use a finite state machine in the interrupt service routine, otherwise the circular buffer is a nice, general method.

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

bobgardner wrote:
I you clear the input index to zero, the char in array element zero is the first character received. By definition. We on the same wavelength?

Maybe I am wrong, but if I understand well, a ring buffer (implemented in an interrput, allowing the mcu to do other stuff) when reaches the end it will start from the beginning. Yes, I can use a long buffer, but I want to avoid the index reset (I don't know, maybe "filter" a slow input that send a lot of one type of char for a single command, i.e. a second system that implement the usart tx sending data at every ck cycle as a consequence of button held by user).
Right now I know that I can disable the reset of buffer (so linear, not a ring) keeping firts chars. That because with a ring you always know -when- is sent the LAST char. But the first (what I'm looking for) can be cancelled, and this is not safe for my application. For sure, it will work with a big buffer, but with a static input PC terminal that wait your "enter", can be "cracked" sending more chars than buffer and the terminal will sent all those chars saturating buffer even more than once, avoiding correct data at input. Is it clear my purpose? Or did you have some tips about that?
So, your

Quote:
the char in array element zero is the first character received
is not true if I receive more chars than the max lenght of buffer. And now this is possible.

clawson wrote:
Ultimately it adds nothing more to your program than a guarantee that no received character will be lost (assuming you don't disable interrupts for long periods and you consume buffered characters before the buffer overflows). Otherwise just treat the foreground function that retrieves characters from the ring as a direct hardware interfacing, polling routine.

In my case I can ovflw the buffer. (read above)
Your second option is usually valid, but for now I can't spend cpu occupation in polling, but is preferable slowing in a statistic way the system, not predictable, only when there's real input. That because a poll can insert a time error (periodic error) to a signal generated from a shift of bit (i.e. stepper driver): sending a char it means that I want to change the actual situation, so I can stop everything at first interrupt, interpreting data, and start again with the new command received. Everything with low latency and no polling. Maybe it will work even with a poll of the flag, but why I should do in your way? Can you show to me that what you are saying is more safe and less cpu expensive? I can do what I'm looking for using fgets() but if I can do this avoiding stdio function using, is optimal. Because even 2 calls to scanf() use more than 600b/1Kb of code. Too much in this case.

Kartman wrote:
What actually determines the 'first' character? Time or the actual value?
Time. I mean, the first (or a short sequence from the first) char is the important, but I can receive ten thousand different dummy chars. And during those chars are flushed, the system can be, by specification, not predictable in terms of timing (so I can use interrupts).
About the FSM: an FSM in C can be done with if() and other controls. Can you be a bit clear? Are you talking about polling?(I don't think that you were talking about FSM in hw implemented in a FPGA :D)

PS: After this, btw, I want to implement a joystick controlled mcu, so reading a stream of data -and- doing stuff. But in this case polling, as said from Clawson, should be enough.

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

Surely what sets the speed is the baud rate? Even if you use 115,200 baud then with 11 bits per character frame you receive 10472 bytes per second. That means you have 95us to process each byte. If the CPU runs at 16MHz the cycle time is 0.0625us so you have 1520 cycles to process a byte. In this case do the 4 machine cycles to wrap the circular pointer really matter?

Remember that the reason we always pick a binary length (16,32,64) for the buffer is so the wrap can be nothing more than an AND operation. With the LDI of the 15,31,63 mask value I wouldn't have thought it would be more than 4 cycles in total.

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

A FSM is a concept. As to how you implement it varies. In C you would normally use a switch() statement. You can do it via interrupts or polling.

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

clawson wrote:
Surely what sets the speed is the baud rate? Even if you use 115,200 baud then with 11 bits per character frame you receive 10472 bytes per second. That means you have 95us to process each byte. If the CPU runs at 16MHz the cycle time is 0.0625us so you have 1520 cycles to process a byte. In this case do the 4 machine cycles to wrap the circular pointer really matter?

Remember that the reason we always pick a binary length (16,32,64) for the buffer is so the wrap can be nothing more than an AND operation. With the LDI of the 15,31,63 mask value I wouldn't have thought it would be more than 4 cycles in total.

How many clock cycles takes a dummy if()?
I've done this in the past activating the poll from a flag setted from UART interrupt. The if(flag) introduction has introduced an error that can be felt even from my hears.

But the security problem that I explained remains. And since I do this for learn something more, and since I want to manage efficiently interrupts, I can modify the goal of my application. That because I want use INT if possible. For now, I've found that this works with functions self made (at least for usart_read_char/string(), due to their simplicity).
PS: I like interrupts, maybe because I've used them few times.

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

Quote:

I've done this in the past activating the poll from a flag setted from UART interrupt. The if(flag) introduction has introduced an error that can be felt even from my hears.

You are misled if you think an if(flag) test were significant. It should be no more that an LD, CP, BREQ or similar. Again about 4 CPU cycles. The only place I can think of where that may be significant is in something really tight like video generation and apart from the fact that you'd probably doing something time critical like that in Asm if you did it in C you'd be studying the generated Asm and counting the cycles anyway.

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

clawson wrote:
Quote:

I've done this in the past activating the poll from a flag setted from UART interrupt. The if(flag) introduction has introduced an error that can be felt even from my hears.

You are misled if you think an if(flag) test were significant. It should be no more that an LD, CP, BREQ or similar. Again about 4 CPU cycles. The only place I can think of where that may be significant is in something really tight like video generation and apart from the fact that you'd probably doing something time critical like that in Asm if you did it in C you'd be studying the generated Asm and counting the cycles anyway.

I dont have this code now (and the stepper setup)... But I'm sure that I was able to felt the difference. Maybe it something else that I don't remember (or discovered). I'll open a new thread about that if necessary.

Now, closing the OT, for now I'll try to implement this https://www.avrfreaks.net/index.php?name=PNphpBB2&file=viewtopic&p=679896 to my application. I'll see how it works. With a good C writing I can avoid the safety issue in a smart way, and my code can be "nice/standard".

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

Quote:

(and the stepper setup)... But I'm sure that I was able to felt the difference.

No way. In MCU terms stepper motors are like a three toed sloth on Mogadon. They work in terms of milliseconds as they contain great lumps of iron with huge amounts of inertia. Meanwhile even a lowly 8 bit micro is capable of doing sixteen million things in a second. If something takes 17 of those million cycles instead of 12 or whatever the motor has only moved about 3microns in the time difference. You are looking at orders and orders of magnitude in the difference.

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

clawson wrote:
Quote:

(and the stepper setup)... But I'm sure that I was able to felt the difference.

No way. In MCU terms stepper motors are like a three toed sloth on Mogadon. They work in terms of milliseconds as they contain great lumps of iron with huge amounts of inertia. Meanwhile even a lowly 8 bit micro is capable of doing sixteen million things in a second. If something takes 17 of those million cycles instead of 12 or whatever the motor has only moved about 3microns in the time difference. You are looking at orders and orders of magnitude in the difference.

I got what you saying, I'm agree with you, that is why I've said that there's something else that I don't remember, for sure, other stuff. It wasn't a bare shift that code, maybe I remember it in a wrong way. Could be something else. For sure if I'll post the code everything will be clear, but I don't have the code right now and I'll open a new thread in this case. :)

Last Edited: Sun. Jul 28, 2013 - 01:43 PM
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

I think many of your correspondents might be curious about the country in which you do your programming. This correspondent is, at least.

Imagecraft compiler user

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

bobgardner wrote:
I think many of your correspondents might be curious about the country in which you do your programming. This correspondent is, at least.

That is because is a weird english? :) I'm italian, I'm sorry for any mistake or misunderstanding, be patient. :)

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

Your English is easily understandable. OK in my opinion. I wish I knew even a little of another language. Spanish would help down here in Florida. If you ever meet Massimo the Arduino Guy, tell him you know a guy from Florida that thinks he is a great man. I hope to sell an electronic gizmo invented by me someday.

Imagecraft compiler user

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

bobgardner wrote:
Your English is easily understandable. OK in my opinion. I wish I knew even a little of another language. Spanish would help down here in Florida. If you ever meet Massimo the Arduino Guy, tell him you know a guy from Florida that thinks he is a great man. I hope to sell an electronic gizmo invented by me someday.

Of course you could write to him and/or interact with arduino Forums (international, so English is the way in that ambient) :D
Btw, I still don't understand why you ask to me from where I program. :)

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

This is the most interesting most active forum I've ever seen. I like electronics and microcontrollers and c programming, and brainstorming on how to make things, and even though I know a bunch of professional programmers at several big defense/simulation/training outfits, none of them want to go home a write programs for personal projects. So the fact that there are dudes all over the world that seem to have the same interest in electronics is encouraging. It might be a dying art in Florida and the US, but it seems to be flourishing in India, Iran, and various other spreadout locales.

Imagecraft compiler user

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

Cool :)
But dudes with this kind of interests always exist (at least in Italy). But thanks to internet in the last 10 years the world is simply more connected.