| Author |
Message |
|
|
Posted: Jun 26, 2012 - 11:59 PM |
|


Joined: May 28, 2012
Posts: 91
|
|
Hi everyone,
I have a few Timer and UART interrupts running. Here is some preliminary info about my setup:
ATmega 1284P
20 MHz external oscillator
UART0 port used at 57600 baud, USART0_Rx interrupt
Timer0 uses OVF, COMPA and COMPB interrupts
AVR Studio 4 is the software.
Anyway, this isn't a problem yet (I haven't run into it, but I'm investigating), but what happens if say I'm inside of my Timer OVF interrupt (it's my longest one) and a UART0 Rx complete interrupt sets its flag?
I don't suppose the OVF interrupt will interrupt to let the UART0 one go? I'm worried that my data will get overwritten with the next (few) bytes while the Timer interrupt is doing its stuff.
UART0_RX is higher up the interrupt list than Timer0_OVF, but I don't think an interrupt can go off during another ISR.
As such, here are my options (I think):
1. Have a data busy line from my ATmega to the serial port. Set the line busy during Timer interrupts?
2. Minimize the time spent in OVF interrupt. But I don't know how, apart from moving all my code to a random function. Is there a way to call a function from an ISR, but have it not return to the ISR? I think I could do it in asm, but I don't know how to do it in C.
3. Send data from PC with delays?
Anything else I could try?
Thanks! |
|
|
| |
|
|
|
|
|
Posted: Jun 27, 2012 - 12:33 AM |
|

Joined: Aug 03, 2009
Posts: 162
|
|
|
Code:
Minimize the time spent in OVF interrupt.
This is a good approach. Set a flag (or flags) in the ISR and do the processing outside of the ISR in the main loop when the flag is set.
Some MCUs allow higher priority interrupts to interrupt during an ISR (e.g. 8051). You could clear the interrupt flag inside the ISR to allow subsequent interrupts. But interrupts interrupting interrupts can be risky because of reentrancy issues.
Best to stick with the "minimize time" approach if possible. |
|
|
| |
|
|
|
|
|
Posted: Jun 27, 2012 - 12:37 AM |
|

Joined: Nov 17, 2004
Posts: 13956
Location: Vancouver, BC
|
|
|
Quote:
but what happens if say I'm inside of my Timer OVF interrupt (it's my longest one) and a UART0 Rx complete interrupt sets its flag?
The Rx interrupt will be run when the timer interrupt finishes. The problem comes when some interrupt flag gets set more than once while in another interrupt (or for that matter, when global interrupts are disabled for what ever reason). When this happens, you lose an interrupt.
Quote:
but I don't think an interrupt can go off during another ISR.
Not normally, but you can re-enable the global interrupt flag while in an ISR, but you have to be sure that the first interrupt doesn't get triggered while servicing the second one. |
_________________ Regards,
Steve A.
The Board helps those that help themselves.
|
| |
|
|
|
|
|
Posted: Jun 27, 2012 - 12:41 AM |
|

Joined: Nov 09, 2011
Posts: 419
|
|
Before doing anything too complicated, it might be worth seeing if there really is a problem here.
At 57600 baud, 8 data bits, 1 stop bit, each character takes about 170us. With a 20MHz clock, that's about 3500 cycles. That's a lot of cycles. What are you doing inside the timer OVF interrupt? Do you expect it to take up to 3500 cycles?
- S |
|
|
| |
|
|
|
|
|
Posted: Jun 27, 2012 - 12:44 AM |
|


Joined: Sep 04, 2002
Posts: 21393
Location: Orlando Florida
|
|
| Dont do any subroutine calls in the interrupt handlers. That causes all 32 regs to be pushed and pulled in every handler. At 20MHz, 32 x 50 ns instructions takes 1.6usec to push and 1.6usec to pull, so your minimum interrupt time is about 4usec. Keep em short. 57600 bps is 174usec per char. If the interrupt, grab the char, and return takes 2usec (less regs are pushed... no function calls) the receive is using 2/174ths of the cpu... 1% or so. I second the suggestion of just setting a flag in the timer interrupt handlers and get out. |
_________________ Imagecraft compiler user
Last edited by bobgardner on Jun 27, 2012 - 12:57 AM; edited 2 times in total
|
| |
|
|
|
|
|
Posted: Jun 27, 2012 - 12:44 AM |
|


Joined: Nov 22, 2002
Posts: 12193
Location: Tangent, OR, USA
|
|
Your time limit is the time in which a second interrupt will come before the first one of the same kind is finished.
For example, at 57600 baud, and 10 bits (8 data + start + stop), you have 5760 characters per second. That means that an over-run error will occur if each character is not serviced within (1/5760 = 173us). At 20MHz, that gives you 3460 CPU clocks. If anything delays servicing the UART for 3460 clocks, you may well over-write what is currently in the receive buffer.
The same, however, can happen with overflow interrupts and compare interrupts. If these are not serviced within one timer period, then you have lost an event.
Thus, the generic rule: keep ISR's short. There are several things you can do to help make this happen. Minimize the number of variables updated within the interrupt. Don't use floating point operations. Set a flag and do the actual execution in your main loop.
Jim |
_________________ Jim Wagner
Oregon Research Electronics, Consulting Div.
Tangent, OR, USA
"The only thing standing between us and victory is defeat" P.G.Wodhouse in Wooster & Jeeves series
|
| |
|
|
|
|
|
Posted: Jun 27, 2012 - 12:48 AM |
|

Joined: Nov 17, 2004
Posts: 13956
Location: Vancouver, BC
|
|
Edit: Removed duplicate post
Quote:
But I don't know how, apart from moving all my code to a random function.
Set a flag inside the ISR, do the work outside the ISR when that flag is set. |
_________________ Regards,
Steve A.
The Board helps those that help themselves.
Last edited by Koshchi on Jun 27, 2012 - 06:05 AM; edited 1 time in total
|
| |
|
|
|
|
|
Posted: Jun 27, 2012 - 01:09 AM |
|


Joined: May 28, 2012
Posts: 91
|
|
|
mnehpets wrote:
At 57600 baud, 8 data bits, 1 stop bit, each character takes about 170us. With a 20MHz clock, that's about 3500 cycles. That's a lot of cycles. What are you doing inside the timer OVF interrupt? Do you expect it to take up to 3500 cycles?
Thanks, nope, I don't expect it to take 3500 cycles (I think it's about 140 within the simulator). So no need to worry about that. Now that I think about it, I don't know why I ever thought it was a problem.
Although I'm still going to make my interrupts shorter.
I'm thinking of stuffing all received data into a buffer and dealing it when it's all done within the main loop.
I just have some addition and look up tables in my OVF ISR with no loops.
Thanks for the help everyone! |
|
|
| |
|
|
|
|
|
Posted: Jun 27, 2012 - 09:51 AM |
|


Joined: Jul 18, 2005
Posts: 62940
Location: (using avr-gcc in) Finchingfield, Essex, England
|
|
|
Quote:
I'm thinking of stuffing all received data into a buffer and dealing it when it's all done within the main loop.
For UART that is the "normal" solution when using interrupts to collect received data. In fact it's so common that a search for any of the terms "UART ring buffer", "UART circular buffer" or "UART FIFO" should hit prior discussion with complete worked code examples.
Circular buffers have "head" and "tail" (sometimes "read" and "write") pointers so those could help the search too.
I know there's some good code at fourwalledcubicle.com so adding that to the search may also help to locate it. |
_________________
|
| |
|
|
|
|
|
Posted: Jun 27, 2012 - 09:54 PM |
|


Joined: May 28, 2012
Posts: 91
|
|
I have been playing with my code (changed some methods and reduced the RX interrupt). But I get some random behaviour - the servo lines work fine with default values I input in the code (no jitter).
When I'm sending the information from Tera Term though, the servos sometimes (actually fairly frequently) rotate to incorrect angles. If I keep sending the same information (pasting it into TT), the servos swerve through a variety of angles.
Here is my relevant code (I think):
Code:
ISR(USART0_RX_vect){
receivedbuffer[c_char] = UDR0;
c_char++;
if (c_char == 18){c_char = 0; UART_COMPLETE = 1;}
}
c_char is initialized to 0 during USART0 intialization.
Here is the loop in my main:
Code:
unsigned char cRB[18];
unsigned char *pRB;
pRB = cRB;
for (int i = 0 ; i < 9; i++)
{
servo_table[i] = 10000;
}
servo_table[9] = 50000;
for (int i = 0; i <18; i++)
{
receivedbuffer[i] = 66;
}
initialize_UART();
initialize_Servos();
UART_COMPLETE = 1;
// Enable global interrupts
sei();
while (1) // Runs forever, updates channels with new values
{
// make copy of info during update in case interrupt fires
if (UART_COMPLETE){
for (int i = 0; i < 18; i++)
{
cRB[i] = receivedbuffer[i]-32;
}
pRB = cRB;
update_counts(pRB);
UART_COMPLETE = 0;
}
}
and update_counts:
Code:
void update_counts(unsigned char* p)
{
int c1 = 0;
for (int i = 0; i < 9; i++)
{
char l_b = p[c1];
c1++;
char h_b = p[c1];
servo_table[i] = ((h_b << 8) + l_b);
c1++;
}
}
I'm not sure if something is not re-initializing when I'm updating with values I got from UART. I can't see it :/ So I'd appreciate other eyes looking at it to see if they can spot something illegal (probably!).
Thanks  |
|
|
| |
|
|
|
|
|
Posted: Jun 28, 2012 - 02:05 AM |
|

Joined: Dec 30, 2004
Posts: 9006
Location: Melbourne,Australia
|
|
| I find your code difficult to read. Whilst it is legal C, your block layout is inconsistant. Basically, dont try to put more than one statement on a line. Anyway, may i suggest a more conventional approach to the handling of serial input. Implement a circular buffer for the recieve side then use scanf to decode the string. The atring terminator of 18 is interesting, most would use 13 which is a carriage return. Then you can see if the receive interrupts are upsetting your servo code. My suspicion with your existing code is that you copy a pointer to your data and not the data, so when you input new data, the ecisting gets corrupted and you get weird intermediate positions. |
|
|
| |
|
|
|
|
|
Posted: Jun 28, 2012 - 06:11 AM |
|


Joined: May 28, 2012
Posts: 91
|
|
|
Kartman wrote:
I find your code difficult to read. Whilst it is legal C, your block layout is inconsistant. Basically, dont try to put more than one statement on a line.
Not sure what you are referring to here. I have one line where there is more than one statement (sorry?).
Quote:
The atring terminator of 18 is interesting, most would use 13 which is a carriage return. Then you can see if the receive interrupts are upsetting your servo code.
18 is not my string terminator. That's how many characters I have and am receiving.
Quote:
My suspicion with your existing code is that you copy a pointer to your data and not the data, so when you input new data, the existing gets corrupted and you get weird intermediate positions.
I copy a pointer to my data? I don't know where to look. |
|
|
| |
|
|
|
|
|
Posted: Jun 28, 2012 - 06:33 AM |
|


Joined: Dec 21, 2006
Posts: 1547
Location: Saar-Lor-Lux
|
|
|
aero1 wrote:
Code:
Minimize the time spent in OVF interrupt.
This is a good approach. Set a flag (or flags) in the ISR and do the processing outside of the ISR in the main loop when the flag is set.
You don't need an OVF interrupt at all then.
Just use the OVF flag in main, no need so let the program set up of a flag the hardware already handles. |
|
|
| |
|
|
|
|
|
Posted: Jun 28, 2012 - 07:01 AM |
|


Joined: Jan 14, 2008
Posts: 1157
Location: San Diego
|
|
It doesn't look like you need pRB. You could just pass in cRB to update_counts(cRB).
I would recommend using types defined in <stdint.h> like uint8_t. In your for loops you're using an int when you only need an uint8_t. |
_________________ ~~John
TWI C source code
|
| |
|
|
|
|
|
Posted: Jun 28, 2012 - 07:22 AM |
|

Joined: Feb 12, 2005
Posts: 16547
Location: Wormshill, England
|
|
|
Quote:
Anyway, may i suggest a more conventional approach to the handling of serial input. Implement a circular buffer for the recieve side then use scanf to decode the string.
I agree with kartman, although I would probably choose something simpler than scanf() e.g. gets().
Your PC works on the basis of catching all your keypresses or receiving internet traffic. Even though it may be busy with hard disk, video, USB, ...
The general principle is to let peripherals get on with their job quietly in the background. When a foreground process wants something it simply checks availability and pulls it from a buffer.
This is why a modern PC can get on with running many programs 'simultaneously'. And the 1970s Unix boxes did not have any more computational power than your AVR. Yet they would support several users.
David. |
|
|
| |
|
|
|
|
|
Posted: Jun 28, 2012 - 08:02 AM |
|

Joined: Dec 30, 2004
Posts: 9006
Location: Melbourne,Australia
|
|
Kitty, interesting how you concentrated on my comments rather than my suggestion to solve your problem!
I'm quite sure there are some arduino projects that do something similar to what you want. You might want to have a look at those and the arduino libraries to see how others do it. Or get a mega328 and use the arduino environment - it does hide some of the evil and there are lots of examples to work from. Note that the arduino uses avrgcc and spits out a hex file that you can program into a virgin AVR or use their bootloader which is convenient. This has been discussed nany times here. |
|
|
| |
|
|
|
|
|
Posted: Jun 28, 2012 - 04:44 PM |
|


Joined: May 28, 2012
Posts: 91
|
|
|
atomicdog wrote:
It doesn't look like you need pRB. You could just pass in cRB to update_counts(cRB).
I would recommend using types defined in <stdint.h> like uint8_t. In your for loops you're using an int when you only need an uint8_t.
Yup, I noticed the unnecessary pRB as well. I switched it in my code yesterday (didn't help, but still). I will change to uint8_t today.
david.prentice, Kartman,
Can you explain to me how a circular buffer is better than what I have right now? I read about it yesterday, but I don't really get the point of it for my application (hence why I haven't implemented it yet). I will write the code today and see if that makes things better. I agree that I am probably messing up my writing of data somehow, but I don't know how?
In fact, I am fairly certain I'm messing it up (writing to every 3rd element or something, it's a mess of garbage according to the LCD).
edit: I think I see how it can make life easier. I will just always read around the ring buffer (well, until the end), and UART will put new characters in as it receives them. I'm thinking of making the buffer double the length of my single data burst. Do you think that's reasonable?
Also, hardware changes are out of the questions. I will look at Arduino libraries though. I think I should be able to write code for the circular buffer if I can figure out what I want it to do.
Thanks for the help. |
|
|
| |
|
|
|
|
|
Posted: Jun 28, 2012 - 06:21 PM |
|

Joined: Feb 12, 2005
Posts: 16547
Location: Wormshill, England
|
|
I am sure that Wikipedia is better than me.
A circular buffer never runs out of space unless you fill it right up.
Think about
Code:
David Prentice<LF>Cliff Lawson<LF>Bob Marley<LF>
You simply pull off "David Prentice<LF>" from the buffer. Meanwhile new bytes are still joining after "Bob". The tail of the list follows the incoming data and wraps round into the space previously held by "David". The head of the list moves round as you remove stuff from the buffer.
With your system, as soon as "David Prentice" gets received, "Cliff Lawson" starts again at the same place. i.e. you have to pull out all of "David" before "Cliff" overwrites it.
There are several ways of managing buffers. Circular is a simple and reliable method. FIFO is another name.
Believe me. It is how your PC keyboard or internet manage incoming data. Especially when unreliable humans or telecoms may not always be ready for using it. The last thing you want is for data to be lost !
David. |
|
|
| |
|
|
|
|
|
Posted: Jun 28, 2012 - 06:54 PM |
|


Joined: May 28, 2012
Posts: 91
|
|
|
david.prentice wrote:
I am sure that Wikipedia is better than me.
A circular buffer never runs out of space unless you fill it right up.
David.
Thanks, David
I see how it should be beneficial. I started writing some code (well, I have it written, it just doesn't compile yet). I will be back after lunch with some results. Hopefully. (Or questions about why I'm getting cryptic error messages) |
|
|
| |
|
|
|
|
|
Posted: Jun 28, 2012 - 07:03 PM |
|


Joined: Mar 27, 2002
Posts: 18756
Location: Lund, Sweden
|
|
When you're through grasping ring buffers, then pop over to your uni dept and talk to the person(s) in charge of course and curriculum contents. Tell them that they need to incorporate the concept if a queue data structure, and it's implementation by using a ring buffer, into their CS101 (or 102, which I would expect to be entitled "Algorithms and Data Structures" or some such).  |
|
|
| |
|
|
|
|
|