Hallo, ich musste für die FH einen Datenlogger für 10 Solarzellen bauen. Es sollte die Spannung, der Strom, der Kurzschlussstrom und zwei Temperaturen auf SD Karte aufgezeichnet werden. Das funktioniert auch soweit, nur macht mir der Controller jedes mal nach 32 - 34 Messzyklen (á 10Sek) einen reset. Ich habe schon das MCUCSR Register mit aufgezeichnet und nach dem RESET auf LCD ausgegeben. Das Register ist auch nach dem Reset 0. Ich teste jetzt schon seit Tagen und finde den Fehler einfach nicht. Ich vermute, es ist ein Stack overflow, der mir den reset verursacht (Sprung auf 0 da die Rückspungadresse nicht geladen werden kann). Ich habe den Schaltplan und die Software mal angehängt. Vielleicht kann mir da ja jemand helfen. Vielen Dank schon im Voraus!
MAKE.EXE: *** No rule to make target `main.o', needed by `main.elf'. Stop.
Hi, schreib dir doch eine Testfunktion für den Stack. Bevor deine Vars ins SRAM kopiert werden, schreibst du von RAM_END an eine Anzahl von Bytes mit irgend ein Pattern ins RAM. (zb. 0xFFFF -> 0xFF00 = 0xA5) In der Main oder wenn du deine Werte Logs schreibst du die Anzahl der Bytes die verändert wurden mit weg. So kannst du schnell prüfen wie weit dein Stack anwächst. Beispiel: (kein CODE!!!!)
1 | count= 0 |
2 | for 0xFFFF to 0xFF00 Step -1 |
3 | if (*0xFFFF != 0xA5) then |
4 | count++; |
5 | next
|
6 | |
7 | Print "Der Stack ist " + count + " Bytes gross!" |
Vielleicht hilft es dir. Stephan
Danke Stefan für die Info. Werd ich mir ansehen, wie das am einfachsten möglich ist. Kurti schrieb: > Wo ist die main.c ? sorry, wollte nicht mit in das zip. Kurz zur Erklärung: Im Prinzip wird alle 10 Sekunden die ISR von Timer1 ausgeführt welche die funktion "measure_and_save() aufruft.
1 | ISR (TIMER1_OVF_vect) |
2 | {
|
3 | cli(); // disable interrupts |
4 | TCNT1 = 0x676A; // preload timer for timebase 10s |
5 | sei(); |
6 | |
7 | if (count < interval) { |
8 | count++; |
9 | }
|
10 | else { |
11 | measure_and_save(); |
12 | count = 1; |
13 | }
|
14 | sleep_mode(); |
15 | }
|
16 | |
17 | ...
|
18 | |
19 | |
20 | //******************************
|
21 | // get ADC values and save to SD
|
22 | //******************************
|
23 | |
24 | void measure_and_save (void) { |
25 | |
26 | toggle_led(); |
27 | unsigned char str[30]; |
28 | unsigned char file_name[13]="DATA DAT"; |
29 | unsigned int i, j, mux_channel; |
30 | RTC_DATE_TIME rtc; |
31 | |
32 | unsigned int adc_value; |
33 | double temp; |
34 | double voltage; |
35 | double current; |
36 | double current_short; |
37 | |
38 | |
39 | lcd_home(); |
40 | char test[5]; |
41 | sprintf (test, "%u", MCUCSR); |
42 | lcd_string(test); |
43 | |
44 | |
45 | //if(MMC_FILE_NEW == ffopen(file_name)){;}
|
46 | |
47 | |
48 | if (rtc_read(&rtc) != RTC_ERROR){ |
49 | sprintf(str, "%u$1;1;%d%d.%d%d.%d%d%d%d %d%d:%d%d:%d%d;", |
50 | MCUCSR, rtc.date[0], rtc.date[1], rtc.date[3], rtc.date[4], rtc.date[6], rtc.date[7], rtc.date[8], rtc.date[9], |
51 | rtc.time[0], rtc.time[1], rtc.time[3], rtc.time[4], rtc.time[6], rtc.time[7]); |
52 | sprintf(file_name, "_%d%d%d%d%d%d DAT", |
53 | rtc.date[8], rtc.date[9], rtc.date[3], rtc.date[4], rtc.date[0], rtc.date[1]); |
54 | }
|
55 | else { |
56 | sprintf(str, "$1;1;error"); |
57 | sprintf(file_name, "_ERROR DAT"); |
58 | }
|
59 | |
60 | if(MMC_FILE_EXISTS == ffopen(file_name)){ |
61 | ffseek(file.length); // Spult bis zum Dateiende vor um anzuhaengen, geht auch ohne Option MMC_OVER_WRITE ! |
62 | }
|
63 | |
64 | ffwrites(str); // write date to file |
65 | |
66 | // read temp ADC values
|
67 | adc_value = ADC_Read_Avg(2,3); |
68 | temp = ((VREF*100*(long)adc_value)/(float)ADC_MAX_VALUE)-330; |
69 | sprintf(str, "%3.1f;", temp); |
70 | j=0; |
71 | do { |
72 | if (str[j] == '.') |
73 | str[j]=','; |
74 | j++; |
75 | } while (str[j]!=0); |
76 | ffwrites(str); |
77 | |
78 | adc_value = ADC_Read_Avg(3,3); |
79 | temp = ((VREF*100*(long)adc_value)/(float)ADC_MAX_VALUE)-330; |
80 | sprintf(str, "%3.1f;", temp); |
81 | j=0; |
82 | do { // dots to commas |
83 | if (str[j] == '.') |
84 | str[j]=','; |
85 | j++; |
86 | } while (str[j]!=0); |
87 | ffwrites(str); |
88 | |
89 | for (i=0;i<10;i++) { |
90 | mux_channel = i; |
91 | |
92 | PORTA =((PORTA & 0x0F) | (mux_channel<<4)); |
93 | _delay_us(10); |
94 | |
95 | adc_value = ADC_Read_Avg(0,3); |
96 | voltage = (VREF*adc_value)/(float)ADC_MAX_VALUE; |
97 | |
98 | adc_value = ADC_Read_Avg(1,3); |
99 | current = (VREF*10*adc_value)/(float)ADC_MAX_VALUE; |
100 | |
101 | PORTB |= (1<<PB3); // set PB3 |
102 | _delay_us(10); |
103 | adc_value = ADC_Read_Avg(1,3); |
104 | current_short = (VREF*10*adc_value)/(float)ADC_MAX_VALUE; |
105 | PORTB &= ~(1<<PB3); // reset PB3 |
106 | |
107 | sprintf(str, "%1.3f;%2.2f;%2.2f;", voltage, current, current_short); |
108 | |
109 | j=0; |
110 | do { // dots to commas |
111 | if (str[j] == '.') |
112 | str[j]=','; |
113 | j++; |
114 | } while (str[j]!=0); |
115 | ffwrites(str); |
116 | }
|
117 | |
118 | // new line
|
119 | ffwrite(0x0D); |
120 | ffwrite(0x0A); |
121 | ffclose(); //close file |
122 | |
123 | cycle++; |
124 | |
125 | PORTC &= ~(1<<PC5); // reset PC5 |
126 | |
127 | return; |
128 | }
|
Roman Dissauer schrieb: > Im Prinzip wird alle 10 Sekunden die ISR von Timer1 ausgeführt welche > die funktion "measure_and_save() aufruft. Schlechter Programmierstil. Eine ISR macht man kurz und flink. Die sollte nur ein Flag setzen, und die main loop wertet dieses Flag aus und triggert die weiteren Aktionen. Ist allerdings in deinem Falle möglicherweise nicht ernstlich relevant, da du ja nur einen Interrupt hast. > ISR (TIMER1_OVF_vect) > { > cli(); // disable interrupts Du solltest mit dem Studium des Controller-Handbuchs beginnen. Während einer ISR sind weitere Interrupts gesperrt ... > TCNT1 = 0x676A; // preload timer for timebase 10s Dafür nimmt man den CTC-Modus, statt hier manuell mit dem Timerwert rumzufummeln. > sei(); Sowas mancht man in einer ISR nur, wenn man sich wirklich sicher ist, was man tut. Andernfalls riskiert man einen rekursiven Interruptaufruf, und genau das könnte dein Problem sein. Falls es deine Funktion measure_and_save() nämlich nicht schafft, zwischen zwei Interrupts fertig zu werden, ist der Teufel los. Die oben genannte Variante mit einem Flag und der Auswertung in der main loop hat dieses Risiko nicht; die würde dann nur eine Messung verpassen, aber nicht in die (möglicherweise unendliche) Rekursion laufen.
sprintf(str, "%u$1;1;%d%d.%d%d.%d%d%d%d %d%d:%d%d:%d%d;", MCUCSR, rtc.date[0], rtc.date[1], rtc.date[3], rtc.date[4], rtc.date[6], rtc.date[7], rtc.date[8], rtc.date[9], rtc.time[0], rtc.time[1], rtc.time[3], rtc.time[4], rtc.time[6], rtc.time[7]); Bist du sicher das dieser Rattenschwanz in das hier passt? unsigned char str[30]; Da kommt möglicherweise dein Stacküberlauf her. Verwende besser snprintf(str, 30, "..",..); Dann gibts keinen Arrayüberlauf.
Danke Jörg für deine Kommentare! Jetzt läuft es schon über 40min! Zuvor gab es alle 5min einen reset. Jörg Wunsch schrieb: > Roman Dissauer schrieb: >> Im Prinzip wird alle 10 Sekunden die ISR von Timer1 ausgeführt welche >> die funktion "measure_and_save() aufruft. > > Schlechter Programmierstil. Eine ISR macht man kurz und flink. > Die sollte nur ein Flag setzen, und die main loop wertet dieses > Flag aus und triggert die weiteren Aktionen. Dass mein Programmierstil nicht der beste ist kann ich mir gut vorstellen. Ist mein erstes uC Projekt. :) > Während einer ISR sind weitere Interrupts gesperrt ... Wenn doch während einer ISR alle weiteren Interrupts gesperrt sind, muss doch egal sein, wie lange diese ausgeführt wird. Bei mir ist der Interrupt alle 10sek gekommen und das Ausführen der ISR dauerte keine 100ms. Ich hab den Code jetzt mal im ersten Schritt folgendermaßen geändert:
1 | int measure = TRUE; |
2 | |
3 | ISR (TIMER1_OVF_vect) |
4 | {
|
5 | cli(); // disable interrupts |
6 | TCNT1 = 0x676A; // preload timer for timebase 10s |
7 | sei(); |
8 | |
9 | if (count < interval) { |
10 | count++; |
11 | }
|
12 | else { |
13 | measure = TRUE; |
14 | count = 1; |
15 | }
|
16 | return; |
17 | }
|
18 | |
19 | int main (void) { |
20 | |
21 | ...
|
22 | |
23 | while (1) { |
24 | if (measure == TRUE) { |
25 | measure_and_save(); |
26 | measure = FALSE; |
27 | }
|
28 | else { |
29 | nop(); |
30 | }
|
31 | }
|
32 | |
33 | return 0; // never reached |
34 | }
|
Wie gesagt, ich versteh das zwar nicht ganz warum es jetzt funktioniert, aber ich werds mir merken, dass ISR kurz und bündig gemacht werden müssen. >> TCNT1 = 0x676A; // preload timer for timebase 10s > > Dafür nimmt man den CTC-Modus, statt hier manuell mit dem Timerwert > rumzufummeln. Danke für die Info, werd ich auch noch ändern. > Sowas mancht man in einer ISR nur, wenn man sich wirklich sicher > ist, was man tut. Andernfalls riskiert man einen rekursiven > Interruptaufruf, und genau das könnte dein Problem sein. Falls > es deine Funktion measure_and_save() nämlich nicht schafft, zwischen > zwei Interrupts fertig zu werden, ist der Teufel los. Wie schon gesagt, meine ISR hat ca. 100ms zum ausführen gebraucht und der Interrupt ist alle 10 sek gekommen. Da es jetzt funktioniert, wird es wohl daran gelegen sein, aber verstanden hab ich es nicht ganz. Eine Sache, die ich auch noch nicht ganz verstanden hab: wenn ich in der Endlosschleife das
1 | else { |
2 | nop(); |
3 | }
|
weglasse, wird die funktion measure_and_save() nie ausgeführt!
holger schrieb: > Bist du sicher das dieser Rattenschwanz in das hier passt? > > unsigned char str[30]; ja, da war schon viel mehr drin! :) Ich hatte im Anfangsstadium der Software die komplette Zeile aus measure_and_save() in einem Array unsigned char str[256]; Nachdem ich Verdacht auf einen Stack overflow hatte, hab ich das dann in einzelne kleine abschnitte unterteilt und zum Schluss erst \n\r gemacht. > Verwende besser snprintf(str, 30, "..",..); vielen Dank für den Input, ich werd das auch noch aufnehmen.
Roman Dissauer schrieb: > weglasse, wird die funktion measure_and_save() nie ausgeführt! Hast du "measure" mittlerweile als volatile deklariert? Diese Variable änderst du in der ISR, das muss der Compiler wissen, damit er den Check beim if() keinesfalls wegoptimiert.
Tom M. schrieb: > Hast du "measure" mittlerweile als volatile deklariert? nachdem ich das gemacht hab, hat es auch ohne else{ nop(); } funktioniert. Vielen Dank! Ich hab nun auch den Timer auf CTC umgestellt, funktioniert alles blendend! Ich lass das ding mal über Nacht laufen, mal schauen was passiert. Vielen Dank allen nochmal für die schnelle Hilfe!
Roman Dissauer schrieb: >> Während einer ISR sind weitere Interrupts gesperrt ... > Wenn doch während einer ISR alle weiteren Interrupts gesperrt sind, muss > doch egal sein, wie lange diese ausgeführt wird. Jein. Bei dir wäre es in der Tat egal, allerdings hast du ja gleich am Anfang selbst die Interrupts freigegeben. Wenn man mehr als einen Interrupt hat, dann führt eine lang laufende ISR jedoch zu riesigen (und schwankenden) Latenzen in der Interruptannahme, daher vermeidet man das.
Roman Dissauer schrieb: >> Während einer ISR sind weitere Interrupts gesperrt ... > Wenn doch während einer ISR alle weiteren Interrupts gesperrt sind, muss > doch egal sein, wie lange diese ausgeführt wird. Bei mir ist der > Interrupt alle 10sek gekommen und das Ausführen der ISR dauerte keine > 100ms. Naja, während die ISR läuft, ist halt alles andere blockiert. Jörg Wunsch schrieb: > Wenn man mehr als einen Interrupt hat, dann führt eine lang > laufende ISR jedoch zu riesigen (und schwankenden) Latenzen > in der Interruptannahme, daher vermeidet man das. Wenn während der Zeit ein anderer Interrupt mehrmals ankommt, verliert man auch welche. Wenn du z.B. beschließt, noch für irgendeine Zeitstempelung einen weiteren Timer im Millisekunden-Takt laufen zu lassen und dort einen Counter zu inkrementieren, dann verlierst du während deiner 100 ms halt 99 von den Inkrementierungen.
Bitte melde dich an um einen Beitrag zu schreiben. Anmeldung ist kostenlos und dauert nur eine Minute.
Bestehender Account
Schon ein Account bei Google/GoogleMail? Keine Anmeldung erforderlich!
Mit Google-Account einloggen
Mit Google-Account einloggen
Noch kein Account? Hier anmelden.