| Author |
Message |
|
|
Posted: Apr 05, 2010 - 01:44 AM |
|

Joined: Apr 05, 2010
Posts: 3
|
|
Hi guys, I'm working on a team developing a data logger using an AtMega168. It accepts an RS-232 serial stream and writes it to an SD card as a .csv file. The firmware was originally created for an Arduino board using the Arduino C++ tool, but it is now on a custom circuit.
The problem is that the microprocessor keeps resetting. A new file is created on the card every time the processor powers up, and over a 24 hour period it will create 30-100 files. Strangely, it will collect only one or two lines of data during the first 45 minutes, during which time it will do the majority of its resetting, and then will log data perfectly for many hours. Also curiously, while the microprocessor is resetting, lines of data can be transmitted incompletely, but the one big file always has perfect data in it.
This behavior is very puzzling to me, and I'm not really sure where to go with debugging. One theory is that too much current is coming through the RS-232 connection, another is that it's a firmware issue. Here's an image of the schematic and a copy of my code. If anyone is willing to take the time and help out, It'd really be appreciated.
[img]http://5832613776636000249-a-1802744773732722657-s-sites.googlegroups.com/site/falasolarsignworksite/documents/newcircuit2.png?attachauth=ANoY7cp52rymfQnpkQy9D1zcK2LpwNgJa5vu13046X2OWFwjMXO6q2RrUehVEhhXkE71WiLHtEHP-Tkzb1YGunwJbpxsidLB4mmKku5vIkwiFBjlCJA7A4v_xusgP0pxTd-HjwGcL4bDkLKq0YRrwV-tWhuKcVQE9wYwMD2TsX4IjdoY5mN_07eX1JXmd8fLItGS7fBY3TtPBK4XWwHk-rd8bn37uJxLrSZQttedrpyCvqjQu6z3uO0%3D&attredirects=0[/img] |
|
|
| |
|
|
|
|
|
Posted: Apr 05, 2010 - 02:36 AM |
|


Joined: Feb 19, 2001
Posts: 25905
Location: Wisconsin USA
|
|
The first thing to do is to trap MCUSR and look at the reset causes. (Don't forget to clear after trapping/logging.)
The results of that exercise will help to establish a direction for further exploration. |
|
|
| |
|
|
|
|
|
Posted: Apr 05, 2010 - 02:36 AM |
|


Joined: Nov 22, 2002
Posts: 12045
Location: Tangent, OR, USA
|
|
There are several common causes of reset, and several less common ones.
The big one is use of the watchdog, and not resetting it when needed. Another is use the brownout detector and experience power "sags" or "drops".
Other causes include improperly initialized stack, a stack that "runs away", over-writing other data, It can also be caused by exceeding array bounds. These causes do NOT create a normal "reset" event, I don't believe.
You can check the contents of the MCUSR, though it is tricky in C because of code initialization; there have been descriptions about how to do it.
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: Apr 05, 2010 - 03:24 AM |
|


Joined: Feb 19, 2001
Posts: 25905
Location: Wisconsin USA
|
|
|
Quote:
though it is tricky in C because of code initialization;
Huh? Perhaps with your infinite-value toolchain. You know the one: It's good; for nothing.
But mine happily "serves" up the value and will generate a code fragment if you ask the Wizard nicely.
[OK, Jim, I'll bite: Which toolchain wipes out I/O register contents at startup before app code gets a crack at them?]
Lee |
|
|
| |
|
|
|
|
|
Posted: Apr 05, 2010 - 03:54 AM |
|


Joined: Nov 22, 2002
Posts: 12045
Location: Tangent, OR, USA
|
|
Well, I know that gcc pundits recommend a special "pre-initialization" code block to read MCUSR (and retain its value) though c-initialization. Yes, I know, its the one that's good (generally) for nothing (free).
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: Apr 05, 2010 - 05:25 AM |
|


Joined: Jun 22, 2004
Posts: 3849
Location: South West Utah, USA
|
|
|
|
|
|
|
Posted: Apr 05, 2010 - 06:08 AM |
|

Joined: Apr 05, 2010
Posts: 3
|
|
I'm not finding much documentation on how to access the MCUSR. Would one of you be kind enough to point me in the direction of a tutorial or something?
We have tried tying the reset pin to VCC (active low), so that shouldn't be the problem. |
|
|
| |
|
|
|
|
|
Posted: Apr 05, 2010 - 08:09 AM |
|


Joined: Mar 27, 2002
Posts: 18550
Location: Lund, Sweden
|
|
|
Quote:
I'm not finding much documentation on how to access the MCUSR. Would one of you be kind enough to point me in the direction of a tutorial or something?
Access it just like any other I/O register?
Code:
uint8_t theRegisterContents = MCUSR;
(not tested) |
|
|
| |
|
|
|
|
|
Posted: Apr 05, 2010 - 01:12 PM |
|

Joined: Apr 05, 2010
Posts: 3
|
|
| Yea I guess my problem is that the firmware is written using the Arduino's higher level language. I'm not sure how to directly access these registers with arduino code. I guess I may have to dive into the libraries and figure it out, but is there anything else I can be doing in the mean time that might be productive? |
|
|
| |
|
|
|
|
|
Posted: Apr 05, 2010 - 05:31 PM |
|


Joined: Jul 18, 2005
Posts: 62292
Location: (using avr-gcc in) Finchingfield, Essex, England
|
|
|
Quote:
I'm not sure how to directly access these registers with arduino code.
EXACTLY as show by Johan. Arduino sketches ARE C programs (well, OK, C++ programs). It's just as valid in Arudino code to do direct register access such as:
Code:
DDRB |= (1<<PB3);
PORTB |= (1<<PB3);
to set pin 11 as it is to use Arduino library functions:
Code:
pinMode( 11, OUTPUT );
digitalWrite( 11, HIGH );
Both those pieces of code achieve the same (the former much quicker and in less space than the latter of course).
You may want to visit www.smileymicros.com and read Joe's Arduino articles 9 and 10 from Nuts & Volts magazine which explain how to make the switch to "real C". You may want to consider Joe's new book about programming Arduino which covers all this.
Quote:
Well, I know that gcc pundits recommend a special "pre-initialization" code block to read MCUSR (and retain its value) though c-initialization.
I've read that several times. While there are reasons to preempt the C preamble by coercing routines into the .init3 memory section to do things like enablingthe ext-mem interface before it is accessed by the C preamble I've never followed the logic of folks saying this is needed for MCUSR. They presumably have never looked at GCC's preamble - the only SFR registers it accesses are the SP? ones. As such there's nothing wrong with a C program starting:
Code:
int main(void) {
uint8_t copyCR = MCUCR;
or an Arduino sketch starting:
Code:
uint8_t copyCR;
setup() {
copyCR = MCUCR;
}
I just made the var global here in case it needs to be used within loop() |
_________________
|
| |
|
|
|
|
|
Posted: Apr 05, 2010 - 06:39 PM |
|

Joined: Sep 21, 2005
Posts: 2297
|
|
The only need for 'special' considerations in saving reset status is when you are 'worried' that your initialization code may take longer than the default 15ms timeout of the watchdog. This is assuming you have an 'enhanced watchdog' (I assume all avr's used by Arduino variants have the enhanced watchdog- 168/328/etc).
Unless you run on a slow clock, I suspect 99% of the time this does not need to be anything to worry about.
You will notice that the datasheet recommends disabling the watchdog in your app on startup (whether you use it or not), so simply read the reset status register (if needed), clear the register, then disable the watchdog.
Code:
//...
#include <avr/wdt.h>
int main(){
//do this first
uint8_t temp_mcusr = MCUSR;
MCUSR = 0;
wdt_disable();
//reset cause now in temp_mcusr
//...
|
|
|
| |
|
|
|
|
|
Posted: Apr 05, 2010 - 06:52 PM |
|


Joined: Feb 19, 2001
Posts: 25905
Location: Wisconsin USA
|
|
|
Quote:
when you are 'worried' that your initialization code may take longer than the default 15ms timeout of the watchdog.
Quote:
GCC's preamble - the only SFR registers it accesses are the SP? ones.
And IMO that's why the CodeVision's preambles do touch on some other "important" registers. One is to WDR and then disable the watchdog (yeah, yeah, it can be WDTON). An excerpt for a Mega640:
Code:
__RESET:
000feb 94f8 CLI
000fec 27ee CLR R30
000fed bbef OUT EECR,R30
;INTERRUPT VECTORS ARE PLACED
;AT THE START OF FLASH
000fee e0f1 LDI R31,1
000fef bff5 OUT MCUCR,R31
000ff0 bfe5 OUT MCUCR,R30
000ff1 93e0 0074 STS XMCRA,R30
000ff3 93e0 0075 STS XMCRB,R30
;DISABLE WATCHDOG
000ff5 e1f8 LDI R31,0x18
000ff6 95a8 WDR
000ff7 b7a4 IN R26,MCUSR
000ff8 7fa7 CBR R26,8
000ff9 bfa4 OUT MCUSR,R26
000ffa 93f0 0060 STS WDTCSR,R31
000ffc 93e0 0060 STS WDTCSR,R30
But of course I hear nyah-nyah 'cause the null program is n words shorter.
Thanks, but I'll take a "standard" preamble that does the things a competent microcontroller programmer would address.
But that's why we have entertaining Compiler Wars.
(and I don't know of any toolchains that touch BORF or those other bits with interesting names--Arduino unknown) |
|
|
| |
|
|
|
|
|
Posted: Apr 05, 2010 - 07:23 PM |
|

Joined: Sep 21, 2005
Posts: 2297
|
|
|
Quote:
Thanks, but I'll take a "standard" preamble that does the things a competent microcontroller programmer would address.
...except in your case, (if I read correctly) you will never be able to 'see' if a watchdog reset occurred. I'm sure you have an alternate plan if that was needed.
I think the gcc/libc startup code could simply throw a 'wdr' in the data/bss init code, which would take care of any case- even if you had the watchdog fused on. But nothing prevents me from doing that if I wanted it that way. I wouldn't need it, though.
In any case, its good to know what your compiler does or does not do in the startup code. |
|
|
| |
|
|
|
|
|
Posted: Apr 05, 2010 - 07:27 PM |
|


Joined: Jul 18, 2005
Posts: 62292
Location: (using avr-gcc in) Finchingfield, Essex, England
|
|
|
Quote:
Thanks, but I'll take a "standard" preamble that does the things a competent microcontroller programmer would address.
As long as the C run time author makes provision for the programmer to replace or change the preamble then those who aren't happy with what's been provided can modify it as they like. Especially to allow code to be "injected" BEFORE the potentially long LPM (.data) and .bss wipe routines. But I don't see why everyone should pay the WDT price when not everyone's using it?
In GCC one merely needs:
Code:
void early(void) __attribute__((section(".init3"), naked));
void early(void) {
// this happens before .data and .bss loops
// could be asm("...") if you like
// such as...
wdt_reset();
MCUSR &= ~(1<<WDRF);
WDTCSR |= (1<<WDCE) | (1<<WDE);
WDTCSR = 0;
}
|
_________________
|
| |
|
|
|
|
|
Posted: Apr 05, 2010 - 07:58 PM |
|


Joined: Feb 19, 2001
Posts: 25905
Location: Wisconsin USA
|
|
|
Quote:
But I don't see why everyone should pay the WDT price when not everyone's using it?
Quote:
...a competent microcontroller programmer ...
Consider one of your million set-top boxes, and for the sake of discussion the WDT or other feature addressed in my snippet isn't used.
Now you start getting calls that after a lightning storm it ain't coming up right. And investigation reveals that the WDT inadvertently got enabled... |
|
|
| |
|
|
|
|
|
Posted: Apr 05, 2010 - 08:18 PM |
|

Joined: Oct 29, 2006
Posts: 2650
|
|
| Clawson's code doesn't allow usefully checking the MCUSR in main.
clawson wrote:
As long as the C run time author makes provision for the programmer to replace or change the preamble then those who aren't happy with what's been provided can modify it as they like. Especially to allow code to be "injected" BEFORE the potentially long LPM (.data) and .bss wipe routines. But I don't see why everyone should pay the WDT price when not everyone's using it?
In GCC one merely needs:
Code:
// The lines that mention MCUSR_mirror are Michael Hennebry's
// The rest, except this one are Clawson's
unsigned char MCUSR_mirror __attribute__((section(".noinit")));
void early(void) __attribute__((section(".init3"), naked));
void early(void) {
// this happens before .data and .bss loops
// could be asm("...") if you like
// such as...
wdt_reset();
MCUSR_mirror=MCUSR;
MCUSR &= ~(1<<WDRF);
WDTCSR |= (1<<WDCE) | (1<<WDE);
WDTCSR = 0;
}
Now main can check and clear MCUSR_mirror at its leisure.
It's nice to know that a reset was a reset and
not an unhandled interrupt or something. |
_________________ Michael Hennebry
Iluvatar is the better part of Valar.
|
| |
|
|
|
|
|
Posted: Apr 05, 2010 - 10:59 PM |
|


Joined: Aug 04, 2004
Posts: 1822
Location: Davie, FL
|
|
The possible causes of a 'restart' are (at least)
power on reset (power fail and restore), brownout (power dip and restore), watch dog, reset pin toggle, and jump/call to zero. BUT note that the MCUSR will have nothing to say about the last case as it isn't really a restart of the processor, just your code. The latter is probably a stack over/under flow or a pointer programing error. |
|
|
| |
|
|
|
|
|
Posted: Apr 05, 2010 - 11:14 PM |
|


Joined: Feb 19, 2001
Posts: 25905
Location: Wisconsin USA
|
|
Just a note on the last: It might not be explicit. If your code runs amok in the vast wasteland of unused flash it will end up back there.
You also need to be aware of what your toolchain might do with an uncaught interrupt vector.
I think when applicable JTRF or something like that for JTAG. But interesting, nothing corresponding for debugWire. |
|
|
| |
|
|
|
|
|