Guten Morgen, ich habe da mal ein kleines Anliegen. Zurzeit arbeite ich an meiner Abschlussprüfung als Systeminformatiker. Das Programm, welches wir programmieren sollten, ist eine Überwachungskamerasteuerung. Das Programm an sich ist nicht die Problematik, es ist schon fertig geschrieben und die gewünschten Funktionen sind alle einwandfrei. (OSD Sensoren UART ...) Allerdings hat der Mikrocontroller (ATmega32) Probleme bei Langzeitbetrieb. Sobald der Mikrocontroller länger als 20 Minuten arbeitet hört die main-Routine auf zu arbeiten. In dieser Routine sollen die Kameras im 5-Sekunden Takt wechseln. Als Funktion sollte eine Heartbeat-Anzeige über einen Timer laufen. Diese funktioniert einwandfrei, selbst nach stundenlangem Betrieb. Die Sensoren haben allerdings dann keinen Einfluss mehr auf das Programm, ebenso hört der automatische Wechsel der Kameras auf. Hier also die Frage: Was ist da sinnvoller: Den µC über den Timer immer wieder neustarten zu lassen, oder den Speicher hzu säubern im Betrieb? Bei zweiterem wüsste ich nicht wie das funktionieren sollte... Meinen Code poste ich noch nicht, da ich nicht weiß, ob es auch wirklich erlaubt ist. Das Programm wurde schon zur Bewertung abgegeben, nächste Woche erfolgt ein Änderungsauftrag des Programms. Vielen dank für eure Hilfe und Grüße aus dem Norden!
:
Bearbeitet durch User
Timm S. schrieb: > Was ist da sinnvoller: Den µC über den Timer immer wieder neustarten zu > lassen, oder den Speicher hzu säubern im Betrieb? Weder noch. Du mußt schon den Fehler suchen und beseitigen. Vermutlich ein Atomicity Problem oder ein Deadlock.
Das Programm so schreiben, dass nicht nach 20 Minuten der Speicher voll laeuft? Das muss ja irgendeinen Grund haben. Da du den Code nicht posten willst/kannst, musst du dich wohl selber auf die Suche nach dem Problem begeben.
Hi >Was ist da sinnvoller: Den µC über den Timer immer wieder neustarten zu >lassen, oder den Speicher hzu säubern im Betrieb? Keins von beiden. Dein Programm hat Fehler. Die musst du finden und beheben. MfG Spess
Ihr habt moeglicherweise ein Software problem, moeglicherweise ein EMV Problem. Ich verwende auch Mega32, und die laufen jahrelang durch, ohne reboot. Boevor ich eine reboot massnahmen andenken und testen wuerde, wuerde ich versuchen den Fehler beheben. Das einzige was man periodisch neu initialisierent kann/sollte ist allenfalls ein LCD, da diese EMV empfindlich sind. Sobald die eingestrahlte Welle in den LCD Ausschnitt passt., dh gegen 1GHz und drueber. Ja, die EMV Vorschriften gehen bis 3GHz. Jeden will ja mit einem Mobiltelephon rumlaufen ... das sendet wahlweise bei 800, 900, 1800, 1900MHz. Dann kommt WLAN bei 2.4GHz und 5GHz. Als ersten nimmt man den Watchdog raus. Mit dem sieht man nichts. Dann baut man eine Laufzeit messung ein, dh einen Zaehler, der per timer incrementiert, und den fragt man per kommunikation periodisch ab. Und dann sucht man den Zustand, wo der Controller abstuerzt.
1 | #ifndef F_CPU
|
2 | #define F_CPU 16000000UL
|
3 | #endif
|
4 | |
5 | #include <avr/interrupt.h> |
6 | #include <avr/io.h> |
7 | #include <stdlib.h> |
8 | #include <stdbool.h> |
9 | #include <util/delay.h> |
10 | #include "pal.h" |
11 | #include "uart.h" |
12 | |
13 | //----------------------------------------------------------------------
|
14 | // Eigene Definitionen
|
15 | //----------------------------------------------------------------------
|
16 | |
17 | #define UART_BAUD_RATE 9600
|
18 | |
19 | //----------------------------------------------------------------------
|
20 | // Globale Variablen
|
21 | //----------------------------------------------------------------------
|
22 | |
23 | volatile bool CamSelect = true; // true = Kamera 2, false = Kamera 1 |
24 | volatile bool Heartbeat = true; |
25 | volatile bool Interrupted = false; |
26 | |
27 | //----------------------------------------------------------------------
|
28 | // Interrupt-Routinen
|
29 | //----------------------------------------------------------------------
|
30 | ISR(INT0_vect) |
31 | {
|
32 | Interrupted = true; // Setzt den Schalter "Interrupted" auf HIGH für Delay Funktion (siehe unten) |
33 | }
|
34 | |
35 | ISR(INT1_vect) |
36 | {
|
37 | Interrupted = true; |
38 | }
|
39 | |
40 | ISR(TIMER1_COMPA_vect) |
41 | {
|
42 | Heartbeat = !Heartbeat; // Invertiert den Schalter "Heartbeat" bei jedem Timer-Überlauf |
43 | display_memory_address = 393; // Position auf dem Bildschirm für OSD |
44 | if (Heartbeat == true) |
45 | pal_write_char(0xF8); // Zeichne das gewählte Heartbeat-Symbol auf dem Bildschirm wenn Heartbeat ein ist... |
46 | else
|
47 | pal_write(" "); // ... ansonsten lösche es |
48 | }
|
49 | |
50 | //----------------------------------------------------------------------
|
51 | // Initialisieren der Eingänge/Ausgänge und Interrupts
|
52 | //----------------------------------------------------------------------
|
53 | |
54 | void init() |
55 | {
|
56 | DDRA = 0xA7; // Kamera |
57 | DDRC |= (1<<PC0); // Ausgangspin für Aufnahmegerät |
58 | DDRD = 0x00; // Eingang für Sensoren |
59 | |
60 | PORTC |= (1<<PC0); // Setze Aufnahme auf HIGH (Low-Aktiv) |
61 | PORTD |= (1<<PD2); // Setze internen Pull-Up |
62 | |
63 | GICR |= (1<<INT0) | (1<<INT1); |
64 | MCUCR |= (1<<ISC00) | (1<<ISC10); |
65 | }
|
66 | |
67 | //----------------------------------------------------------------------
|
68 | // Initialisieren des Timers
|
69 | //----------------------------------------------------------------------
|
70 | |
71 | void initTimer() |
72 | {
|
73 | TCCR1B |= (1<<WGM12) | (1<<CS12); // Compare-Match-Mode (CTC), Prescaler 256 |
74 | TIMSK |= (1<<OCIE1A); // Aktivierung CTC |
75 | OCR1A = 62499; // Setze Zeit, bis Timer überlaufen ist (1 Sekunde) |
76 | }
|
77 | |
78 | //----------------------------------------------------------------------
|
79 | // Anzeige Variable über UART
|
80 | //----------------------------------------------------------------------
|
81 | |
82 | void uart_putv(int Variable) |
83 | {
|
84 | char Buffer[20]; |
85 | itoa( Variable, Buffer, 10 ); |
86 | uart_puts( Buffer ); |
87 | }
|
88 | |
89 | //----------------------------------------------------------------------
|
90 | // VT100 Code über UART
|
91 | //----------------------------------------------------------------------
|
92 | |
93 | void uart_vt100(const char *s ) |
94 | {
|
95 | //-------------------------------------------------------------------------
|
96 | // VT100-Code. Dieser wird mit der ESC-Taste initialisiert (ASCII-Wert: 27)
|
97 | // Referenz: http://thoughtmountain.com/VT100_codes.html
|
98 | //-------------------------------------------------------------------------
|
99 | |
100 | uart_putc(27); |
101 | while (*s) |
102 | uart_putc(*s++); |
103 | }
|
104 | |
105 | //----------------------------------------------------------------------
|
106 | // Delay, der mit Interrupt abgebrochen werden kann
|
107 | //----------------------------------------------------------------------
|
108 | |
109 | void myDelay(int ms) |
110 | {
|
111 | for (int i = 0; i < ms; i++) |
112 | {
|
113 | _delay_ms(1); |
114 | if(Interrupted == true) // Wird durch Interrupt ausgelöst |
115 | break; |
116 | }
|
117 | }
|
118 | |
119 | //======================================================================
|
120 | // Main-Funktion
|
121 | //======================================================================
|
122 | |
123 | int main (void) |
124 | {
|
125 | // Initialisierungen durchführen (I/O, OSD,...)
|
126 | init(); |
127 | pal_init(); |
128 | initTimer(); |
129 | // Freigabe der Interrupts/Timer
|
130 | sei(); |
131 | |
132 | // Initialisierung von UART
|
133 | uart_init( UART_BAUD_SELECT(UART_BAUD_RATE,F_CPU) ); |
134 | uart_vt100("[2J"); // [UART] Lösche aktuellen Terminal-Bildschirm |
135 | |
136 | while(1) |
137 | {
|
138 | Interrupted = false; // Rücksetzung des Interrupt-Schalters |
139 | |
140 | if ((PIND & (1<<PD2) ) && !(PIND & (1<<PD3) )) // Wenn Sensor 1 HIGH und Sensor 2 LOW ist, schalte auf Kamera 2 (halten) |
141 | CamSelect = true; |
142 | if (!(PIND & (1<<PD2) ) && (PIND & (1<<PD3) )) // Wenn Sensor 1 LOW und Sensor 2 HIGH ist, schalte auf Kamera 1 |
143 | CamSelect = false; |
144 | |
145 | if (CamSelect == true) // Wenn Kamera 2 geschaltet |
146 | {
|
147 | PORTA |= 0x20; // Schalte Kamera 2 auf Video Kanal -X6 |
148 | display_memory_address = 413; |
149 | pal_write("Cam2 "); // [OSD] Gebe "Cam2" auf Bildschirm aus |
150 | }
|
151 | else if (CamSelect == false) // Wenn Kamera 1 geschaltet |
152 | {
|
153 | PORTA &= ~(0x20); // Schalte Kamera 1 auf Video Kanal -X6 |
154 | display_memory_address = 413; |
155 | pal_write("Cam1 "); // [OSD] Gebe "Cam1" auf Bildschirm aus |
156 | }
|
157 | |
158 | if ((PIND & (1<<PD2) ) || (PIND & (1<<PD3) )) |
159 | {
|
160 | uart_vt100("[31m"); // [UART] Schrift Rot |
161 | uart_vt100("[2;3f"); // [UART] Zeile 2, Position 3 |
162 | uart_puts(" Aufnahme! "); // [UART] Ausgabe "Aufnahme" |
163 | display_memory_address = 43; |
164 | pal_write("ALARM"); // [OSD] Gebe "ALARM" auf Bildschirm aus |
165 | PORTC &= ~(1<<PC0); // Aufnahmepin LOW (Low-Aktiv!!!) |
166 | }
|
167 | else
|
168 | {
|
169 | uart_vt100("[0m"); // [UART] Schrift zuruecksetzen |
170 | uart_vt100("[2;3f"); |
171 | uart_puts("Einsatzbereit"); |
172 | display_memory_address = 43; |
173 | pal_write("Ruhe "); |
174 | PORTC |= (1<<PC0); // Aufnahmepin HIGH (Low-Aktiv!!!) |
175 | }
|
176 | uart_vt100("[1;1f"); |
177 | uart_vt100("[?25l"); // [UART] Cursor unsichtbar schalten |
178 | uart_vt100("[33m"); // [UART] Gelbe Schrift |
179 | uart_puts("-----------------"); // [UART] Zeichne Rahmen |
180 | uart_vt100("[3;1f"); |
181 | uart_puts("-----------------"); |
182 | myDelay(5000); // Warte 5 Sekunden (Kann per Interrupt abgebrochen werden) |
183 | CamSelect = !CamSelect; // Wechsle aktuelle Kamera |
184 | }
|
185 | }
|
Das wäre der aktuelle Code ohne Treiber... Die Sensoren sind hierbei an den beiden Interrupts angeschlossen.
:
Bearbeitet durch User
die variable interrupted wird von zwei interrupts gespiesen ? Uart ist blockierend ? Sollte man nie machen... ist nicht mehr debugbar. In welchem Zustand ist das programm? Nimm eine Zustandsmaschine.
Von Zustandsmaschine hatte ich vorher noch nichts gehört, habe eigentlich nur mit Struktogrammen und PAP's gearbeitet. Das 2 Interrupts für 1 Varriable verwendet wird ist bedingt durch die vordefinierte Hardware, zu der wir das Programm schreiben sollten. (Jeder Interrupt = 1 Sensor) UART ist hier allerdings Nebensache, habe das Programm auch ohne UART laufen lassen und das gleiche Resultat erhalten.
:
Bearbeitet durch User
Was mir im Programm auffällt: PD2 und PD3 werden als Eingang benutzt. Aber nur für PD2 wird ein Pullup eingeschaltet. Dann natürlich und nicht zu übversehen: pal_write bzw. die Adressumschaltung wird von main UND von der ISR aufgerufen. Ist das Zulässig? ist pal_write reentrant? Was passiert, wenn gerade die Funktion pal_write läuft und der Interrupt ausgelöst wird, der seinersseits pal_write aufruft? Auch interessant: was passier, wenn hier
1 | display_memory_address = 413; |
2 | pal_write("Cam2 "); // [OSD] Gebe "Cam2" auf Bildschirm aus |
genau nach dem Zuweisen der neuen Adresse und vor dem Aufruf von pal_write ein Interrupt auftritt, der seinerseits
1 | ISR(TIMER1_COMPA_vect) |
2 | {
|
3 | Heartbeat = !Heartbeat; // Invertiert den Schalter "Heartbeat" bei jedem Timer-Überlauf |
4 | display_memory_address = 393; // Position auf dem Bildschirm für OSD |
5 | ...
|
die Adresse wieder umsetzt? Alles das sind genau die Dinge, die bei ersten Tests gut gehen, die aber irgendwann zeitlich sich genau so ausgehen, dass es zu Fehlsituationen kommt. Warum habt ihr euch denn nicht an eine der Grundregeln gehalten: In einer ISR werden keine Ausgaben gemacht. Oder, wenn schon, dann NUR in dieser ISR. Bei "Shared Resourcen" also Dingen, die sich mehrere Programmteile 'teilen', muss man ein wenig vorsichtig sein! Das führt sonst ganz schnell zu massiven Synchronisations bzw. Reentrance Problemen.
:
Bearbeitet durch User
Genau, darum klassische mit Flags, siehe Interrupt. Und eine Statemachine MUSS man auch als Systeminformatiker kennen! Das ist das KLEINE 1x1!
Das hier
1 | if ((PIND & (1<<PD2) ) && !(PIND & (1<<PD3) )) // Wenn Sensor 1 HIGH und Sensor 2 LOW ist, schalte auf Kamera 2 (halten) |
2 | CamSelect = true; |
3 | if (!(PIND & (1<<PD2) ) && (PIND & (1<<PD3) )) // Wenn Sensor 1 LOW und Sensor 2 HIGH ist, schalte auf Kamera 1 |
4 | CamSelect = false; |
ist sowieso seltsam. Wenn PD2 immer das Gegenteil von PD3 sein muss, dann frage ich mich: wozu überhaupt 2 Eingänge? Offenbar tut es ja auch einer. Dann erhebt sich dann auch nicht die Frage: was passiert, wenn PD2 und PD3 den gleichen Pegel haben? (ok, in dem Fall ändert dann CamSelect seinen Zustand nicht. Aber: ist das auch so gewollt?)
Zuerst mal danke für die konstruktiven Beiträge! Der Pullup war eigentlich nicht nötig, da Hardwaremäßig ein Widerstand verbaut wurde. Wird also bei meiner nächsten Revision entfernt. Bei den rare occurrences wie du beschrieben hast ist die Formatierung teils durcheinander... bin schon dabei das zu beheben. Ich probiere mal den Code umzuschreiben und dies aus der Timer-Routine zu entfernen, melde mich wenn es geklappt hat.
Hast du einen Debugger? Wo bleibt der stehen? Ich erkenne hier kein Speicherleck. Dein Heartbeat kommt weiter regelmäßig? Hast du sonst irgendwo eine While Schleife oder ähnliches in den Treibern? Wenn du keinen Debugger hast, dann erstelle einen Zähler in der Hauptschleife. Stürzt das Programm dann immer exakt nach xxx durchläufen ab? Erstelle an verschiedenen Punkten der Hauptschleife eine UART Ausgabe. Wo stürzt er ab. Wie sehen die PAL-write Rountinen aus. Kann es sein dass er dort hängen bleibt?
Die ganze Sache mit dem 5 Sekunden Delay, der durch Interrupt abgebrochen werden kann ... man kann es so machen, mit etwas Bauchweh. Aber so richtig 'nach den Regeln der Kunst' ist das nicht wirklich. Letzten Endes ist das doch ein: wenn einer von 2 Eingängen eine Flanke aufweist, dann bewerte die Schalterstellungen (bzw. Sensorstellung) neu und schalte gegebenenfalls die Kameras um, spätestens aber alle 5 Sekunden. Man würde das anders programmieren. Das Stichwort Statemachine ist ja schon gefallen. Der Grund dafür ist einfach: mit dem delay verbaut ihr euch zukünftige Erweiterungen massiv. Damit da in diesen Zeittakt noch was anderes reinpasst, muss man sowieso das Programm umbauen. Da kann man das auch jetzt gleich nach den Regeln der Kunst machen.
Karl Heinz schrieb: > Das hier ist sowieso seltsam. > ist das auch so gewollt? Das ist so gewollt. Wenn exklusiv ein Sensor aktiv ist, soll die Kamera explizit auf den zugewiesenen Sensor zugewiesen werden. Sind beide Sensoren aktiv, soll wie bei inaktiven Sensoren hin- und hergeschaltet werden. Nur die Aufnahme soll dann noch erfolgen.
Habe nun relativ simpel die Heartbeat-Funktion in meine Delay-Funktion eingebaut. Bisher läuft die Platine einwandfrei. (15 Minuten läuft er bereits) Wenn nicht mit Delay, wie dann? Wie soll ich "in den Regeln der Kunst" einen 5-Sekunden-Takt erzeugen, wobei die Kamerastellung wechselt ? Groß erweitert soll das Programm eh nicht mehr. (Auch wenn diese Einstellung nicht gerade löblich ist, das weiß ich) Ich denke, dass beim Änderungsauftrag nur noch zusätzlich der Watchdog-Sensor angeschlossen werden soll, der fehlt noch bei der fertigen Platine. Den Code dafür habe ich mir auch schon vorgeschrieben.
@ Timm S. (atrocty) >Wenn nicht mit Delay, wie dann? Wie soll ich "in den Regeln der Kunst" >einen 5-Sekunden-Takt erzeugen, wobei die Kamerastellung wechselt ? Siehe Statemachine >Groß erweitert soll das Programm eh nicht mehr. (Auch wenn diese >Einstellung nicht gerade löblich ist, das weiß ich) Mag sein, aber du willst ja was Gescheites für deine berufliche Zukunft lernen und nicht in BASCOM-Arduino-Style was zusammenfrickeln.
Timm S. schrieb: > Habe nun relativ simpel die Heartbeat-Funktion in meine Delay-Funktion > eingebaut. Bisher läuft die Platine einwandfrei. (15 Minuten läuft er > bereits) > > Wenn nicht mit Delay, wie dann? Wie soll ich "in den Regeln der Kunst" > einen 5-Sekunden-Takt erzeugen, wobei die Kamerastellung wechselt ? Du hast einen Timer laufen, der den Heartbeat erzeugt. Die 5 Sekunden kann man auch als 'Anzahl von aufgetretenen Heartbeats' ausdrücken. Wann immer in einem Programm Zeiten (hinreichender Größe, alles über ein paar µs) vorkommen, dann kannst du deinen Allerwertesten darauf verwetten, dass die Lösung über Timer führt und nicht über delays. Delays sind oft nicht die Lösung, sie sind aber oft das Problem. In deinem Fall musstest du zu einem Kunstgriff greifen, indem du den Delay mittels Interrupt von hinten durch die Brust ins Auge abwürgen musstest, nur damit du auch zeitnah auf das Ansprechen eines Sensors reagieren kannst. Drückst die die Umschaltzeiten als Anzahl von Heartbeats aus und schaltest du in main nur dann um, wenn diese Anzahl erreicht ist, dann fällt der delay flach und damit auch die ganze Interrupt Geschichte, die du nur brauchtest um den Delay vorzeitig abwürgen zu können. Das Programm wird also eigentlich sogar einfacher, auch wenn sich das bei dir nicht viel schenken wird. Aber: da du die externen Interrupts gar nicht mehr brauchst, hast du auch die Einschränkung auf lediglich 2 von ihnen weg. Du kannst also beliebig viele (im Rahmen der Pins deines µC) derartige Kameras und Sensoren anhängen, ohne dass sich an der Programmstruktur groß was ändert. So arbeiten Profis: sie lassen sich Optionen für die Zukunft offen, wenn ihnen das in der Programmstruktur praktisch nichts kostet. > Groß erweitert soll das Programm eh nicht mehr. (Auch wenn diese > Einstellung nicht gerade löblich ist, das weiß ich) Ist kein Argument. Du bist kein Bastler, sondern das ist deine Abschlussarbeit, die dir bescheinigt, dass du eine Fachkraft bist. Schlimm genug, dass dir der Begriff Statemachine (oder das deutsche Zustandsautomat) nichts sagt.
:
Bearbeitet durch User
Karl Heinz schrieb: > Ist kein Argument. Du bist kein Bastler, sondern das ist deine > Abschlussarbeit, die dir bescheinigt, dass du eine Fachkraft bist. > Schlimm genug, dass dir der Begriff Statemachine (oder das deutsche > Zustandsautomat) nichts sagt. Dem stimme ich auch vollkommen zu. Leider wird meist nur nach IHK-Richtlinien gelehrt, und dabei haben wir hier noch das Glück, dass wir einige Sachen lernen, die nicht unbedingt direkt was mit dem Berufsfeld zu tun haben. Und in den IHK-Richtlinien gabs wohl die Zustandsautomaten nicht. (Auch nicht in vielen Jahren vorher laut den Prüfungen, haben Sommer/Winter von 2004-2013 durchgearbeitet) Mal schauen ob sich einiges ändert sobald das Studium losgeht... Zum Programm: Ich gehe davon aus, dass du meinst, wenn ich ohne delays arbeite die Interrupts nicht brauche. Bin am überlegen: Gehen wir mal davon aus ich entferne den delay. Der Timer ist auf exakt 1 Hz getaktet. Um ein Flackern durch OSD und UART zu verhindern wird ein Schalter als Bedingung gesetzt, der immer nur bei Änderungen schaltet. Ist das so der richtige Ansatz?
@ >Zum Programm: Ich gehe davon aus, dass du meinst, wenn ich ohne delays >arbeite die Interrupts nicht brauche. Nein, den Timer-Interrupt brauchst du auf jeden Fall. Aber die Externen Interrupts nicht. >Gehen wir mal davon aus ich entferne den delay. Der Timer ist auf exakt >1 Hz getaktet. Kann man machen, ist aber eher zu langsam. Praktisch wird man eher 10-1000Hz anstreben. >Um ein Flackern durch OSD und UART zu verhindern wird ein Schalter als >Bedingung gesetzt, der immer nur bei Änderungen schaltet. >Ist das so der richtige Ansatz? So in etwa.
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.