| 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: 13814
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: 395
|
|
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: 21248
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: 12031
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: 13814
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: 62203
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: 8721
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: 1483
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: 1147
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: 16254
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: 8721
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: 16254
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: 18505
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).  |
|
|
| |
|
|
|
|
|
Posted: Jun 28, 2012 - 07:50 PM |
|


Joined: May 28, 2012
Posts: 91
|
|
|
JohanEkdahl wrote:
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).
Well, I'm not a computer science student (nor am I a software engineer). What I'm learning is kind of my own initiative, I'm sure they cover buffers and such in relevant courses, but suggesting it to my own department would probably not end up being very productive  |
|
|
| |
|
|
|
|
|
Posted: Jun 28, 2012 - 09:44 PM |
|


Joined: May 28, 2012
Posts: 91
|
|
Alright, I don't know what to do.
I put a circular buffer in (which seems to work in simulation at least). Except the damn thing doesn't seem to want to accept my UART commands. I can see on the scope that the value changes momentarily, but always goes back to the default value.
I can also get it to glitch out and set lines high forever.
Could my chip be resetting? I remember a cause for this could be an enabled interrupt that doesn't have an ISR. Maybe this is what is going on? I'll have to scout the datasheet to see if I enabled anything by accident.
I don't have mad servo flinging anymore, it just doesn't respond to commands.
Or maybe I'm doing something in code? I used Dean's (Four walled cubicle) code for the circle buffer as a basis, I only changed the variable names. I watched the memory in simulation and things are updated as I think they should.
Anyway, here is the new snippet of my main loop:
Code:
ring_init((Ring_t*)&UARTbuf);
for (int i = 0; i <20; i++)
{
ring_insert((Ring_t*)&UARTbuf, (i+32));
}
while(1){
if (ring_has18((Ring_t*)&UARTbuf))
{
ATOMIC_BLOCK(ATOMIC_RESTORESTATE)
{
for (int i = 0; i < 18; i++)
{
cRB[i] = ring_retrieve((Ring_t*)&UARTbuf) - 32;
}
}
update_counts(cRB);
}
}
And the Rx interrupt:
Code:
ISR(USART0_RX_vect)
{
uint8_t ch_r = UDR0;
ring_insert((Ring_t*)&UARTbuf, ch_r);
}
And ring_has18:
Code:
bool ring_has18(Ring_t* const buffer)
{
return (ring_getcount(buffer) >= 18);
}
I can post the other functions too, but they are all from here: http://www.fourwalledcubicle.com/AVRCodeSamples.php |
|
|
| |
|
|
|
|
|
Posted: Jun 28, 2012 - 11:03 PM |
|


Joined: May 28, 2012
Posts: 91
|
|
I found some stuff out.
First of all, it doesn't like this:
Code:
cRB[i] = ring_retrieve((Ring_t*)&UARTbuf) - 32;
That does something, I think.
Secondly, I am pretty certain it is resetting. I have it hooked up to my LCD screen right now with servos turned off. It should display received data on the screen so that I can make sure it's working. The data most of the time doesn't get displayed, instead the LCD blinks (like power cycling blinking) and shows the default values. Sometimes it will display a series of 18 characters. If I get really lucky, after about 3 sends (and no resets), I finally see my characters.
Thirdly, I'm sending different characters to see if there is a pattern (yet again). What is the meaning of this character that looks like 4 parallel bars? I don't think I'm retrieving the right characters from the buffer.
Fourthly, I'm a moron for initializing 20 characters into the buffer. |
|
|
| |
|
|
|
|
|
Posted: Jun 28, 2012 - 11:22 PM |
|

Joined: Oct 29, 2006
Posts: 2638
|
|
|
mewakitty wrote:
Secondly, I am pretty certain it is resetting.
More probable is an interrupt without an ISR.
By any chance did you get rid of the overflow ISR, but keep the interrupt enabled?
You don't need to keep the interrupt enabled to use the interrupt flag. |
_________________ Michael Hennebry
Iluvatar is the better part of Valar.
|
| |
|
|
|
|
|
Posted: Jun 28, 2012 - 11:40 PM |
|


Joined: May 28, 2012
Posts: 91
|
|
|
skeeve wrote:
mewakitty wrote:
Secondly, I am pretty certain it is resetting.
More probable is an interrupt without an ISR.
By any chance did you get rid of the overflow ISR, but keep the interrupt enabled?
That's what I'm thinking as well. I looked through my code, but I don't see anything that I knowingly enabled.
I commented out my Timer 1 initializations, but that didn't help. So I ran a simulation and paused it once all interrupts were enabled. I found I have Input Capture Flag set on Timer 1, but not the interrupt for it. Does that matter? I don't think so.
I cannot find any other stray interrupts. |
|
|
| |
|
|
|
|
|
Posted: Jun 29, 2012 - 02:13 AM |
|


Joined: Sep 04, 2002
Posts: 21248
Location: Orlando Florida
|
|
| Some folks write handlers for every interrupt that prints out 'unexpected capture interrupt' then hangs in a while(1) loop. Good for debug but bad for unattended operation. |
_________________ Imagecraft compiler user
|
| |
|
|
|
|
|
Posted: Jun 29, 2012 - 05:33 AM |
|

Joined: Dec 30, 2004
Posts: 8721
Location: Melbourne,Australia
|
|
| Apart from your resetting problem, how do maintain synchronisation between your pc and the AVR? Your data packet has 18 chars, but how do we know the start and end of this? Simply expecting the 18 chars to magically line up probably wont work too well in the real world. ou'll find most protocols have a start token or sequence, an end token or sequence and probably some form of error check. Look at NMEA0183 which is what most GPS receivers output - it has a start token of $ and the end is a carriage return char. It also implements a simple checksum. There is plenty of example code on how to generate and decode this data. first up I'd suggest you skip the checksum to make testing a little easier. Again, Arduino would be a good source for ideas. Also have a look at setting up the streams in avrgcc so you can use printf.Carefully placed printf statements can help debugging immensely. Debug your serial and protocol first, then look to integrating this with your servo code - don't try to fight the war on a number of fronts. |
|
|
| |
|
|
|
|
|
Posted: Jun 29, 2012 - 05:44 AM |
|


Joined: May 28, 2012
Posts: 91
|
|
|
Kartman wrote:
Apart from your resetting problem, how do maintain synchronisation between your pc and the AVR? Your data packet has 18 chars, but how do we know the start and end of this? Simply expecting the 18 chars to magically line up probably wont work too well in the real world.
I wrote the GUI on the PC. 18 characters is all it will send at a time, 100% of the time.
Quote:
Debug your serial and protocol first
I'm trying. I will look into streams in avrgcc. |
|
|
| |
|
|
|
|
|
Posted: Jun 29, 2012 - 05:46 AM |
|


Joined: May 28, 2012
Posts: 91
|
|
|
bobgardner wrote:
Some folks write handlers for every interrupt that prints out 'unexpected capture interrupt' then hangs in a while(1) loop. Good for debug but bad for unattended operation.
I might have to do that tomorrow morning and see if that helps narrow it down. Right now I'm completely clueless as to what is causing the resetting. I'm kind of certain it's my servo code. |
|
|
| |
|
|
|
|
|
Posted: Jun 29, 2012 - 08:31 AM |
|

Joined: Dec 30, 2004
Posts: 8721
Location: Melbourne,Australia
|
|
| Whilst your app may only send 18 chars at a time, what about a stray character when you plug in or power up? Your code must be able to synchronise with your packet - if you blindly send out 18 chars with no way on the receiving end to determine where it starts and ends, then your protocol will be unreliable. For this method to work you would need to use a timer to reset your receiver after a period of no data. Then your protocol has a chance of working. Let's assume you have received your 18 chars and the AVR code has them nicely aligned in your array. How do you know you've received the correct data? You could blindly accept that it is correct as that is what the PC sent. Reality is that may not be the case for a number of real world reasons. |
|
|
| |
|
|
|
|
|
Posted: Jun 29, 2012 - 10:51 AM |
|


Joined: Jul 18, 2005
Posts: 62203
Location: (using avr-gcc in) Finchingfield, Essex, England
|
|
|
Code:
cRB[i] = ring_retrieve((Ring_t*)&UARTbuf) - 32;
What is the significance of the 32 in that? I know it's the ASCII value for ' ' but I'm not sure why you would be subtracting that from the received characters? If the intention is to turn ASCII digits into 0..9 binary values then you would use
Code:
cRB[i] = ring_retrieve((Ring_t*)&UARTbuf) - '0';
|
_________________
|
| |
|
|
|
|
|
Posted: Jun 29, 2012 - 11:09 AM |
|

Joined: Oct 18, 2001
Posts: 352
Location: Eastern England.
|
|
|
mewakitty wrote:
I wrote the GUI on the PC. 18 characters is all it will send at a time, 100% of the time.
As Kartman says, that's such a bad assumption. It guarantees an unreliable comms link which precludes any recovery from errors without a reset. |
|
|
| |
|
|
|
|
|
Posted: Jun 29, 2012 - 04:40 PM |
|


Joined: May 28, 2012
Posts: 91
|
|
|
clawson wrote:
Code:
cRB[i] = ring_retrieve((Ring_t*)&UARTbuf) - 32;
What is the significance of the 32 in that? I know it's the ASCII value for ' ' but I'm not sure why you would be subtracting that from the received characters? If the intention is to turn ASCII digits into 0..9 binary values then you would use
Code:
cRB[i] = ring_retrieve((Ring_t*)&UARTbuf) - '0';
It's to counteract what I'm doing in my code on the PC. I didn't want to send things like "tab" or "null" through, so I add 32 to every value before I send, and subtract when I receive. Anyway, that's not so important right now and I can deal with this later.
For other stuff, I will look into verification characters or w/e later. Once I get this part working to the extent I'd like (no resetting). While I appreciate the concern and advice, it is quite off topic right now. |
|
|
| |
|
|
|
|
|
Posted: Jun 29, 2012 - 05:36 PM |
|


Joined: May 28, 2012
Posts: 91
|
|
New developments:
I found that the TX interrupt fires for some reason if I call USARTWrite within the while loop. I think it might have something to do with atomic being around there and the whole enabling/disabling business. This doesn't happen if I put USARTWrite elsewhere in the code. I wrote an empty ISR and now it doesn't reset. I can just remove the USARTWrite command from that loop (it was for debugging) later on and it should solve the problem.
While scrambling around in the disassembler, I found that it says it can't find "delay_basic.h" and "atomic.h". Which seems bad? |
|
|
| |
|
|
|
|
|
Posted: Jun 29, 2012 - 05:40 PM |
|


Joined: Jul 18, 2005
Posts: 62203
Location: (using avr-gcc in) Finchingfield, Essex, England
|
|
| Often a simple solution is to use a FIFOd RXC interrupt for data received by the AVR but just because you do RX by interrupt doesn't mean Tx has to be done by interrupt as well. Often for output it's fine just to sit in a loop doing some kind of polling UART_putchar() function so maybe consider a solution with RXCIE but no TXCIE/UDRIE. |
_________________
|
| |
|
|
|
|
|
Posted: Jun 29, 2012 - 06:01 PM |
|


Joined: May 28, 2012
Posts: 91
|
|
|
clawson wrote:
Often a simple solution is to use a FIFOd RXC interrupt for data received by the AVR but just because you do RX by interrupt doesn't mean Tx has to be done by interrupt as well. Often for output it's fine just to sit in a loop doing some kind of polling UART_putchar() function so maybe consider a solution with RXCIE but no TXCIE/UDRIE.
I'm not trying to do Tx by interrupt. I don't know why it enables at a certain point in time. I don't think I have it enabled by default:
Code:
UCSR0B = (0 << UDRIE0) | (0 << TXCIE0) | (1 << RXEN0) | (1 << TXEN0) | (1 << RXCIE0);
As I said, nowhere else in my code do I have this problem. Only around where I use ATOMIC. |
|
|
| |
|
|
|
|
|
Posted: Jun 29, 2012 - 06:38 PM |
|

Joined: Feb 12, 2005
Posts: 16254
Location: Wormshill, England
|
|
There are many RX interrupt example programs. e.g. CodeVision, Procyon, Fleury, Atmel app notes, ...
I suggest that you simply use one of these 'library' implementations. Quite honestly, there are several points to code carefully e.g. array sizes, indices, volatile, atomicity, ...
Once you have gained experience, you can simply add the proven code to each of your projects. Personally, I always suggest using <stdio.h> functions. They can use your primitive interrupt functions. (Now you end up like any PC program --- no missing data)
David. |
|
|
| |
|
|
|
|
|
Posted: Jun 29, 2012 - 06:55 PM |
|


Joined: May 28, 2012
Posts: 91
|
|
Fine.
I was trying to ask advice to learn, and not to go to these external libraries. The perception I'm getting in this forum is that:
a) People like to advise stuff that wasn't asked about (I get it. You're all amazing programmers).
b) Either you learn without asking, or you just face the fact that you will be told to use Arduino, external libraries, or both.
I like learning, and this was my goal. Any turkey can copy a library someone already made and put it in a project. There is no challenge, no opportunity to pick up something new. It works because everyone says it works, not because I wrote it. |
|
|
| |
|
|
|
|
|
Posted: Jun 29, 2012 - 07:11 PM |
|

Joined: Feb 12, 2005
Posts: 16254
Location: Wormshill, England
|
|
Please do not misunderstand. I am just being honest.
My first UART serial interrupts used the ASM code from a 6502 or 6800 textbook. I have no shame.
This does not mean that you do not follow the algorithm and understand how to write it from scratch. This is why textbooks were invented, and why people disseminate public domain source code.
Having said that I am quite capable of writing code from scratch, I will confess to making errors. Just the same as every other human being.
So I will compare my code with known proven code. In fact, I did exactly that when MY serial code was not working quite right.
So my general advice is to use respected libraries whenever possible. And make improvements / modifications as required. Then perhaps 'my' code will be better / more reliable. After all, the world progresses by building on prior knowledge.
David. |
|
|
| |
|
|
|
|
|
Posted: Jun 29, 2012 - 07:58 PM |
|


Joined: Sep 04, 2002
Posts: 21248
Location: Orlando Florida
|
|
| I just kills me when I work and work and work on something and its getting bigger and uglier and more complicated, then I see how someone else did it, and its smaller, simpler, easier to read. Crap. Ruins my day. Embedded programs is certainly an area where you can start right from a clean screen and type in every line of code that will execute on the microcontroller. If the program messes up, you know who to call. |
_________________ Imagecraft compiler user
|
| |
|
|
|
|
|
Posted: Jun 29, 2012 - 08:20 PM |
|


Joined: Jul 18, 2005
Posts: 62203
Location: (using avr-gcc in) Finchingfield, Essex, England
|
|
People contribute here because they like to be helpful. Nothing more. You can choose to take advice or you can ignore it's your choice.
Often a lateral suggestion will lead the OP on a path they might never have thought of exploring. Unless of course they already know everything already that is. |
_________________
|
| |
|
|
|
|
|
Posted: Jul 04, 2012 - 04:06 PM |
|


Joined: May 28, 2012
Posts: 91
|
|
I didn't say I know everything... It's just irritating to be told to use someone else's stuff instead of trying things yourself. Sorry for the outburst, this problem is driving me insane.
Long story short, I tried Fleury's UART code (although it has no support for my atmega, so I had to change a lot of register names and such) with same resetting problem.
I then got Procyon UART with buffer code, and again, the problem remains. I wrote an ISR for every interrupt that exists in the iom1284p list. I also added ISR(BADISR_vect), but perhaps that's incorrect syntax?
The problem seems to occur when I do this:
Code:
uint8_t myReceivedByte;
int check;
while(1)
{
check = uartReceiveByte( &myReceivedByte );
if (check == TRUE)
{
uartSendByte('1');
}
}
However, this is okay:
Code:
while(1){uartSendByte('8');}
It resets after a random number of received characters. Am I somehow running out of memory or something? I don't seem to be able to simulate this problem, however. Or at least I don't know how.
Can it reset due to framing error? |
|
|
| |
|
|
|
|
|
Posted: Jul 04, 2012 - 04:28 PM |
|


Joined: Jul 18, 2005
Posts: 62203
Location: (using avr-gcc in) Finchingfield, Essex, England
|
|
|
Quote:
I also added ISR(BADISR_vect), but perhaps that's incorrect syntax?
Depends on exactly how you did it but something like:
Code:
ISR(BADISR_vect) {
while(1); // don't go away
}
should do the trick if you have an on chip debugger. You then put a break point on the while(1) loop and wait. Sadly all this tells you is that it is an un-handled interrupt that is occurring but not which one. If you don't have a debugger do something like:
Code:
ISR(BADISR_vect) {
DDRx = (1 << n);
while(1) {
PORTX ^= (1 << n);
_delay_ms(100);
}
}
(fill in "x" and "n" as appropriate for your LED) then an LED will flash if the unhandled condition occurs. Or as you have UART working:
Code:
ISR(BADISR_vect) {
uartWriteString("Bad interrupt");
while(1);
}
But before assuming it is an unhandled vector that leads to resets start by finding out what the reset cause actually is. Near the top of main() take a copy of MCUSR and either feed it up the UART or write it into EEPROM. Then set that register to 0. After the next reset occurs either see what came out of the UART or what was programmed into EEPROM (read it out using your ISP) and the bit(s) that are set will tell you what kind of reset is occuring. Only if the register is apparently 0x00 should you then consider that it might be the BADISR_vect thing. If it is that then probably quickest is to add ISR()s for every ISR source the chip has and have each one do something like sending a unique code up UART or to an LCD or out to 8 LEDs on a PORT or something to identify which one is occurring. |
_________________
|
| |
|
|
|
|
|
Posted: Jul 04, 2012 - 04:52 PM |
|


Joined: May 28, 2012
Posts: 91
|
|
|
clawson wrote:
But before assuming it is an unhandled vector that leads to resets start by finding out what the reset cause actually is. Near the top of main() take a copy of MCUSR and either feed it up the UART or write it into EEPROM. Then set that register to 0. After the next reset occurs either see what came out of the UART or what was programmed into EEPROM (read it out using your ISP) and the bit(s) that are set will tell you what kind of reset is occuring. Only if the register is apparently 0x00 should you then consider that it might be the BADISR_vect thing. If it is that then probably quickest is to add ISR()s for every ISR source the chip has and have each one do something like sending a unique code up UART or to an LCD or out to 8 LEDs on a PORT or something to identify which one is occurring.
I made a copy of MCUSR, put it into a character, and added 32 go get into the letter region.
What came out was an apostrophe -> ', which converts back (-32) to 7, bit 3 of MCUSR.
This appears to be the watchdog timer reset. Is this logical? This would explain why my silly field of ISRs doesn't work.
Thanks a lot for the idea! I don't know when I'm enabling it (simulations says I'm not), but maybe I'll just disable it in the main and see what happens. |
|
|
| |
|
|
|
|
|
Posted: Jul 04, 2012 - 05:09 PM |
|


Joined: Jul 18, 2005
Posts: 62203
Location: (using avr-gcc in) Finchingfield, Essex, England
|
|
|
Quote:
to 7, bit 3 of MCUSR.
7 is bits 2, 1 and 0 all set. That suggests Brown Out and External and Power on.
But the way you have to use MCUSR is to make sure the register is 0 then let the reset occur then take a copy. Without clearing it the bits will accumulate after various kinds of reset.
WDRF would have been 8 or character '('
Cliff |
_________________
|
| |
|
|
|
|
|
Posted: Jul 04, 2012 - 05:29 PM |
|


Joined: May 28, 2012
Posts: 91
|
|
|
clawson wrote:
Quote:
to 7, bit 3 of MCUSR.
7 is bits 2, 1 and 0 all set. That suggests Brown Out and External and Power on.
But the way you have to use MCUSR is to make sure the register is 0 then let the reset occur then take a copy. Without clearing it the bits will accumulate after various kinds of reset.
WDRF would have been 8 or character '('
Cliff
Whoa, that was a major derp on my part. Excuse me.
Anyway. So can I do this then?
Code:
ph1 = MCUSR;
MCUSR = 0x00;
I store MCUSR value in ph1, then print it to USART later. The datasheet says MCUSR is cleared by writing zeroes.
I did this (because I'm impatient), and the result is now 0 (I get a space - 32, or !-33). But that would suggest ISR again?
Pressing the reset button gives 2, which is external, right? |
|
|
| |
|
|
|
|
|
Posted: Jul 04, 2012 - 06:19 PM |
|


Joined: May 28, 2012
Posts: 91
|
|
I put a print to UART function into every ISR. Apparently a lot of them fire. By far the majority is due to INT2 and INT1. I saw a few Timer related interrupts (like Timer3_Capture), and a few that reset without an ISR (so I'm missing something?).
I'm not sure why they are all active? Should I go through them individually at the beginning and disable everything? I would have thought it'd be by default. |
Last edited by mewakitty on Jul 04, 2012 - 06:20 PM; edited 1 time in total
|
| |
|
|
|
|
|
Posted: Jul 04, 2012 - 06:20 PM |
|


Joined: Sep 04, 2002
Posts: 21248
Location: Orlando Florida
|
|
| Just put a bfc on vcc. That will get rid of the brownout. HW fix for SW prob. |
_________________ Imagecraft compiler user
|
| |
|
|
|
|
|
Posted: Jul 04, 2012 - 06:48 PM |
|


Joined: May 28, 2012
Posts: 91
|
|
bob, do you think what's happening is a result of brownout? I put a cap there, but I don't think it helped (or I did it wrong).
More news, I did this, to try to disable INT2 and INT1:
Code:
EIMSK = 0x00;
PCICR = 0x00;
But it doesn't work still. What in the world am I doing that enables all this stuff in the loop?
But that still doesn't help. |
|
|
| |
|
|
|
|
|
Posted: Jul 04, 2012 - 07:10 PM |
|

Joined: Feb 12, 2005
Posts: 16254
Location: Wormshill, England
|
|
|
Quote:
Long story short, I tried Fleury's UART code (although it has no support for my atmega, so I had to change a lot of register names and such) with same resetting problem.
I then got Procyon UART with buffer code, and again, the problem remains. I
I would choose one or the other. Then make sure that I get the mega1284P port correct.
Then investigate the Watchdog. INT0, INT1 and PCINTn interrupts.
I am assuming that you have a proper pcb with stable power supply and proper capacitors. And you have set fuses properly.
David. |
|
|
| |
|
|
|
|
|
Posted: Jul 04, 2012 - 08:07 PM |
|


Joined: May 28, 2012
Posts: 91
|
|
|
david.prentice wrote:
Quote:
Long story short, I tried Fleury's UART code (although it has no support for my atmega, so I had to change a lot of register names and such) with same resetting problem.
I then got Procyon UART with buffer code, and again, the problem remains. I
I would choose one or the other. Then make sure that I get the mega1284P port correct.
Of course. I am using the Procyon one right now. I went through the registers and pin names, I think they're correct.
Quote:
Then investigate the Watchdog. INT0, INT1 and PCINTn interrupts.
Don't know how to investigate further. Basically what I said recently is what I know is happening. I don't know why anything is happening.
Watchdog isn't firing, that was a mistake on my part. I am getting random interrupts going off (mostly INT2), but I cannot tell why. I don't know when they get enabled. If I try to disable them in the main (as I showed above), they fire anyways (so something enables them later).
Quote:
I am assuming that you have a proper pcb with stable power supply and proper capacitors. And you have set fuses properly.
I am on a breadboard. Probably as stable of a power supply as I can get. 1uF capacitors on Vcc pins.
Fuses: extended 0xFF, high 0xD9, low 0xFF. |
|
|
| |
|
|
|
|
|
Posted: Jul 04, 2012 - 08:38 PM |
|


Joined: Sep 04, 2002
Posts: 21248
Location: Orlando Florida
|
|
| There is a checklist of schtuff to look for in a breadboard setup. AVCC needs to attach to VCC. You need .1uf bypass caps in addition to the electrolytic caps, because they suck up the hi freq spikes better. All gnds need to be attached to the gnd bus. How about showing us a nice picture of the 1284 setup? I'm sure someone will pipe up if they dont like the looks of something. |
_________________ Imagecraft compiler user
|
| |
|
|
|
|
|
Posted: Jul 04, 2012 - 09:15 PM |
|


Joined: Jul 18, 2005
Posts: 62203
Location: (using avr-gcc in) Finchingfield, Essex, England
|
|
|
Quote:
I am getting random interrupts going off (mostly INT2), but I cannot tell why.
Can only be because INT2 is set in EIMSK. Could you have a rogue pointer that is leading to a write through to the address of EIMSK? I'd add some code that watches EIMSK and if it is ever seen to have unexpected values STOP. |
_________________
|
| |
|
|
|
|
|
Posted: Jul 04, 2012 - 09:17 PM |
|

Joined: Feb 12, 2005
Posts: 16254
Location: Wormshill, England
|
|
As Uncle Bob says, 1uF electrolytics are no good for digital circuits. You need 100nF ceramic capacitors. You also need reasonable physical layout with 20MHz.
From your other thread, the 20MHz clock will be absolutely perfectly stable. And quite capable of producing 115200 baud with U2X. There is little point in going for high bauds anyway. 9600 baud does me fine for any human interaction.
Your Procyon example program should work 100% perfectly. It will also be efficient for CPU cycles.
If you are having difficulties, post a link to your code. (or paste it in a message)
David. |
|
|
| |
|
|
|
|
|
Posted: Jul 04, 2012 - 09:19 PM |
|


Joined: May 28, 2012
Posts: 91
|
|
Here you go. I forgot to switch the oscillator back before I took the picture, but the connections are the same nonetheless.
Let the criticism begin! I tried to color code it, but it's hard (sorry). In general orange is Vcc and black is Vss. Also, the reset button cap is missing because... I don't know where it went. It works without it.
Green flat caps are 0.1uF, yellow "flat" is 10uF, blue bucket cap is 1uF, black buckets are 10uF. The lone resistor is 100k, I think.
Vcc rail measures at 4.97V.
 |
|
|
| |
|
|
|
|
|
Posted: Jul 04, 2012 - 09:25 PM |
|


Joined: May 28, 2012
Posts: 91
|
|
|
clawson wrote:
Quote:
I am getting random interrupts going off (mostly INT2), but I cannot tell why.
Can only be because INT2 is set in EIMSK. Could you have a rogue pointer that is leading to a write through to the address of EIMSK? I'd add some code that watches EIMSK and if it is ever seen to have unexpected values STOP.
The only pointer I'm aware of that could wander off is the buffer pointer. Although... I'm not sure. Anyway, I will add some code to watch EIMSK.
david.prentice wrote:
As Uncle Bob says, 1uF electrolytics are no good for digital circuits. You need 100nF ceramic capacitors. You also need reasonable physical layout with 20MHz.
Okay, I will find some 100nF caps.
Edit: I found them, they're in the shot. Should I remove the teal colored 1uF one?
Quote:
From your other thread, the 20MHz clock will be absolutely perfectly stable. And quite capable of producing 115200 baud with U2X. There is little point in going for high bauds anyway. 9600 baud does me fine for any human interaction.
Yes, I am trying to work at 19200 right now, it's the same as 9600 behavior wise.
Quote:
Your Procyon example program should work 100% perfectly. It will also be efficient for CPU cycles.
Well, given that I had problems with Fleury's and this one, I think it is definitely something I am causing. A loose wire, a bad connection (or bad design, eep).
[quote]If you are having difficulties, post a link to your code. (or paste it in a message).[quote]
I will by the end of the day (2 hours) if I don't get it working. For now I will try the EIMSK thing and see what happens there. Plus double check the code I wrote in the main. |
|
|
| |
|
|
|
|
|
Posted: Jul 04, 2012 - 10:05 PM |
|


Joined: Sep 04, 2002
Posts: 21248
Location: Orlando Florida
|
|
| Looks to me like the oscillator is plugged into xtal2. Datasheet says on page 35 to use xtal1. Tell me if I found the problem. |
_________________ Imagecraft compiler user
|
| |
|
|
|
|
|
Posted: Jul 04, 2012 - 10:17 PM |
|


Joined: May 28, 2012
Posts: 91
|
|
|
bobgardner wrote:
Looks to me like the oscillator is plugged into xtal2. Datasheet says on page 35 to use xtal1. Tell me if I found the problem.
Yes!
I wonder how long it's been like that for... I think I assumed (never assume) XTAL1 would be before XTAL2 in pin numbering. Hmm.
I'm surprised anything worked at all! Do you have any ideas how this could result in resetting? I'm honestly curious now how the uC did anything, especially send things correctly to UART.
However, the 115200 baud rate is still giving me problems. I will get a crystal with a baud frequency and see if that helps.
Any other comments on the circuit layout? |
|
|
| |
|
|
|
|
|
Posted: Jul 04, 2012 - 10:28 PM |
|

Joined: Feb 12, 2005
Posts: 16254
Location: Wormshill, England
|
|
You have posted an excellent photo. I wish that others could be as neat as you. And if they could use several different wire colours like you have !!
Uncle Bob's razor sharp eyesight spotted the XTAL2 immediately.
You should be ok with the 100nF caps. The blue cap is fine too.
Double check the Procyon code. Everything will be better after a nice cup of tea.
David. |
|
|
| |
|
|
|
|
|
Posted: Jul 04, 2012 - 11:16 PM |
|


Joined: Sep 04, 2002
Posts: 21248
Location: Orlando Florida
|
|
| I have heard that the 'old style' max232s that use the 1uf caps dont run faster than 115200, but the newer ones run the charge pump faster, use .1uf caps, and probably run faster. Check the datasheet. |
_________________ Imagecraft compiler user
|
| |
|
|
|
|
|