Hallo zusammen,
in meinem nächsten Projekt möchte ich gerne eine Alarmmeldeanlage
realisieren, die mir via GSM-Modem den Alarmstatus per SMS übermittelt.
Mein ATmega 1280 untersucht kontinuierlich die seriellen Daten der
Maschine und soll bei einer Fehlermeldung eine Nachricht versenden.
Zudem möchte ich per SMS über mein Mobilfunktelefon Befehle an mein
GSM-Modem schicken, der uC soll diesen verarbeiten und über den USART an
die Maschine weiterleiten
Im Forum habe ich bereits ähnliche Beiträge gefunden, allerdings haben
sie mir bisher nur bedingt weiter geholfen.
Mein erster Gedanke war, dass ich das überprüfen der RS232-Daten mittels
While-Schleife realisiere. Also in etwa so:
uint8_t UART_GETC (void)
{
while (!(UCSR0A & (1<<RXC0)));
return UDR0;
}
void main()
{
uint8_t char c;
while( 1 ) {
c = UART_GETC();
if( c == Terminator ) // Terminator bspw. 0x0D
// es folgt eine Reihe an If-else-Anweisungen, die die Zeichen
untersuchen
}
}
Allerdings gehört in eine Hauptprogrammschleife nichts, was die
Ausführung dieser grundsätzlich verhindern kann. Und genau das mache ich
ja beim verwenden des Interrupts.
Das Programm soll ja nun beim Auftreten einer Fehlermeldung reagieren
und mittels einer Funktion Send_SMS() eine Benachrichtigung an mein
Handy versenden. Zudem will ich ja auch die Maschine ansteuern. Wenn ich
auf zwei USARTs angewiesen bin, wäre es da nicht sinnvoller mit zwei
Interruptroutinen zu arbeiten?
Also…
ISR (USART0_RXC0_vect) // Maschine uC
{
unsigned char data = UDR0;
}
und…
ISR (USART1_RXC1_vect) // uC Modem
{
unsigned char data = UDR1;
}
Ich empfange kontinuierlich Daten von der seriellen Schnittstelle und
werte diese aus. Sende ich jetzt ein Befehl mittels Handy, wird diese
Routine unterbrochen und ich lese am USART1 Zeichen für Zeichen des
Befehls ein, speichere ihn zwischen, oder leite ihn einfach an den UART0
weiter.
// Zeichen von Modem empfangen
ISR (USART1_RXC1_vect) {
unsigned char x = UDR1;
void putch() = x;
// void putch (unsigned char x) {
// while (!(UCSR0A & (1 << UDRE0)));
// UDR0 = x;
// }
In meiner Main würde ich also zunächst die entsprechenden AT-Kommandos
an mein Modem zur Initialisierung schicken (aufwecken, Pin-Eingabe,
etc). Das Sperren und Freigeben der globalen Interrupts wäre hier noch
nicht nötig.
Wenn das Modem Sende und Empfangsbereit ist, initialisiere ich USART0
und USART1 und springe in meine Hauptprogrammschleife, die die seriellen
Daten am USART0 untersucht. Wenn bspw. eine Fehlermeldung detektiert
wird sende ich mit Hilfe der Funktion Send_SMS() eine Nachricht. Hierbei
müsste ich vor dem Ausführen der Funktion die globalen Interrupts
deaktivieren und nach Ausführung wieder aktivieren, oder? Nicht das mir
da der USART1 dazwischenfunkt.
Ich bin mir nicht sicher was ich in diesem Fall tun soll! Das
Untersuchen der Daten mittels Polling und Empfangen von Befehlen mittels
Interrupt, löst das nicht ne Art Kollision aus?
Wie ihr seht, verwirrt mich das Ganze etwas und ich wäre froh, wenn mir
diesbezüglich jemand weiterhelfen könnte. Über Tipps und Hinweise würde
ich mich freuen!
Viele Grüße,
Matze
Hi
Also, ich habe bisher nur mit Atmega8 und Assembler gearbeitet, aber
vielleicht hilft dir trotzdem diese Info.
Ich habe einen kleinen Speicherbereich von ca. 30 Bytes als Buffer
deklariert. Kommt in der ISR - Empfang ein Zeichen an, wird er in den
Buffer eingetragen und ein Schreibzeiger erhöht. Erwischt er die
Buffergrenze setze ich ihn wieder auf 0, damit habe ich einen
Ringspeicher. Im Programm vergleiche ich nun diesen Schreibzeiger mit
einem Lesezeiger. Sind diese beiden unterschiedlich, weiß ich, das ein
Zeichen angekommen ist. Also werte ich es aus und führe den
Schreibzeiger nach. Das Programm hat alle Zeit der Welt für die
Bearbeitung und die Routinen werden auch nur aktiv, wenn es notwendig
ist.
Ich denke, das auch du in ähnlicher Form vorgehen kannst.
Gruß oldmax
Matze schrieb:> Allerdings gehört in eine Hauptprogrammschleife nichts, was die> Ausführung dieser grundsätzlich verhindern kann. Und genau das mache ich> ja beim verwenden des Interrupts.
Dann wäre ja jeder Interrupt sinnlos ...
Man muss die Interrupts natürlich immer intelligent maskieren. Das gilt
gerade dann, wenn man zeitkritische Anwendung hat. Es ist ja nicht so,
dass Interrupts die während der ISR auftreten verloren gehen. Diese
werden dann halt leicht zeitverzögert nach Ablauf der ersten ISR
ausgeführt.
Alles von dir ansgesprochene solle in ISR(s) von statten gehen.
Es ist ein bischen schwierig Dir bei diesem Stand Deiner Kenntnisse
weiterzuhelfen.
Da sind: Der globale Interrupt, die einzelnen Interrupts,
Initialisierungen, Texterkennung, Nebenläufigkeit.
Es ist ratsam erstmal mit einzelnen Aspekten zu beginnen um die
Kenntnisse zu erwerben die Du für das Gesamtprojekt brauchst.
Um Dir aber eine Sorge zu nehmen: An sich ist es möglich das Projekt zu
verwirklichen. Wenn man es "richtig" macht ist das kein Problem.
Um gleich auf Interrupts und ihren Einfluss auf das Hauptprogramm
einzugehen:
Interrupts werden prinzipiell so kurz wie irgend möglich programmiert.
Stell Dir vor, dass es so etwas wie einen Zyklus gibt. Er besteht aus
dem Ereignis, das den Interrupt auslöst, dessen Bearbeitung im Interrupt
(so kurz wie möglich, wie gesagt) und der Bearbeitung der Daten wie sie
im Interrupt hereingekommen sind im eigentlichen Hauptprogramm.
Die Summe der Verarbeitungszeiten im Interrupt und im Hauptprogramm darf
höchstens gross sein wie die Ereignisse auftreten.
Dabei hat man aber verschiedene Variationsmöglichkeiten um die Zeit
günstig zu verteilen. Buffer usw.
Im Interrupt selbst verarbeitet man in der Regel keine Daten, vergleicht
nichts oder macht sonst irgendwas mit den Daten. (Das ist mehr eine
Faustregel als ein Gesetz. Für den Anfänger aber sollte es erstmal
Gesetz sein).
Der Interrupt soll eigentlich nur Eingangsdaten wegspeichern und dem
Hauptprogramm mitteilen, das solch ein Interrupt aufgetreten ist. Das
Hauptprogramm selbst liest dann die vorher gespeicherten Daten und
interpretiert sie.
Durch die Regel, das die "Zykluszeit" ausreichen muss ist auch
sichergestellt, das eine Unterbrechung durch den Interrupt die
Gesamtfunktion nicht negativ beeinflusst. Selbst wenn das Hauptprogramm
gerade einen Text vergleicht, schadet das nichtst, denn es hat ja Zeit
genug, die gerade eingetroffenen Daten erstmal_ _vorläufig zu
ignorieren und mit denen weiterzumachen die es gerade am Wickel hat.
Dieser kurze Überblick soll Dir zeigen, dass man systematisch an das
Problem herangehen kann, das es aber andererseits auch nicht wirklich
trivial ist.
Ich würde Dir daher wärmstens empfehlen, erstmal einfache Dinge zu
programmieren und damit herumzuspielen. Z.B. könntest Du mal in einem
Interrupt eine LED blinken lassen. Im Hauptprogramm die Anzahl zu zählen
und evtl. auszugeben.
Anfänger neigen dazu im Interrupt der Texte empfängt, schon die Texte
oder wenigstens das Textende zu erkennen. Das erste ist nicht sinnvoll,
das zweite kann man machen ist aber meist unnötig.
Der zweite Schritt wäre einfach mal einen Textempfang und Senden via
Interrupt zu programmieren und einfache Befehle im Hauptprogramm zu
erkennen.
Eine Rundumerklärung wäre an dieser Stelle einfach viel zu umfangreich.
Du musst Dich dem Thema Schritt für Schritt nähern. Bei den
Einzelschritten sind wir Dir hier gerne behilflich.
Viel Erfolg.
Ich sehe gerade, das Du anscheinend Deinen ersten Post wesentlich
editiert hast, während ich dabei war die Antwort zu schreiben. Das ist
ungünstig, da es die Antwort entwertet.
Ich hoffe ich habe mich da nicht geirrt. Aber irgendwie kam mir der Post
verändert vor.
>Allerdings gehört in eine Hauptprogrammschleife nichts, was die>Ausführung dieser grundsätzlich verhindern kann. Und genau das mache ich>ja beim verwenden des Interrupts.
Das ist überhaupt kein Problem, solange die Bedingung der "Zykluszeit"
nicht verletzt wird.
Deine skizzierten Interruptroutinen sind soweit OK. Aber die hier von
olmdax angeregte Verwendung eines Ringpufferst ist zu empfehlen.
Hallo zusammen,
vielen Dank für die Antworten. Vor allem der Beitrag von Huch, hat mir
doch sehr weiter geholfen. Ok, die Interrupts verwende ich also nur um
die Eingangsdaten wegzuspeichern.
ISR(USART1_RXC_vect) {
char array [20];
int counter = 0;
uint_8 data;
uint_8 StringLaeng = 20;
data = UDR;
array [counter] = data;
counter++;
If (counter < StringLaeng – 1)
counter = 0;
}
Ich empfange also den ankommenden Befehl (20 Zeichen lang), im Falle
einer empfangenen SMS, und speichere die einzelnen Zeichen in einem
Buffer. Das geschieht alles noch in der IR. Nun soll eine Funktion das
Array bzw. die einzelnen Zeichen auslesen und und an den UART0
weiterleiten. Wäre für die Analyse der seriellen Daten eine IR ebenfalls
sinnvoll? Verständnisfrage: Wenn ich aber permanent Zeichen via
Interrupt empfange, gibts da nicht Probleme wenn eine SMS eintrifft?
> Es ist ja nicht so,> dass Interrupts die während der ISR auftreten verloren gehen. Diese> werden dann halt leicht zeitverzögert nach Ablauf der ersten ISR> ausgeführt.
Ich meine, wir haben dann ja ne extrem hohe Zeitverzögerung, wenn ich
meine Prüfroutine anstatt Polling mit IR durchführen würde. Ich empfange
ja permanent Zeichen. Denkfehler meinerseits?
Wie ich das jetzt verstanden habe soll ich sowohl für die Prüfroutine
(Polling), als auch für die IR (Empfangen eines Befehls) eine Art
Ringpuffer anlegen, um bei einer IR, getriggert durch eine SMS, ich in
meiner Main die restlichen Zeichen noch abarbeiten kann.
Eine Frage habe ich noch: Wie kann ich den bspw. die SMS am besten
Versenden? Wenn ich in meiner Main eine Fehlermeldung entdecke, muss ich
ja ebenfalls ein Art Interrupt verwenden, oder? D.h., ich muss dies
mittels Software-Interrupt einläuten. Wie provoziere ich so ein Software
Interrupt?
Vielen Dank nochmal für die Antworten, ich weiß das wirklich sehr zu
schätzen. VG
Könnte mir zum letzten Beitrag vielleicht nochmal jemand weiterhelfen?
Vielen Dank!
Da muss natürlich noch das data = UDR1 rein.
> ISR(USART1_RXC_vect) {> char array [20];> int counter = 0;> uint_8 data;> uint_8 StringLaeng = 20;> data = UDR;
data = UDR1
> array [counter] = data;> counter++;> If (counter < StringLaeng – 1)> counter = 0;> }
Matze schrieb:> Könnte mir zum letzten Beitrag vielleicht nochmal jemand weiterhelfen?> Vielen Dank!
Das was in deinem Beitrag steht ist schon einmal grundsätzlich falsch -
sry.
All deine Variablen sind lokal in der ISR angelegt. Das heißt, das bei
jedem Aufruf der ISR diese "neu erstellt" werden, da sie beim Verlassen
der ISR wieder freigegeben werden. Heißt also, dass ein Großteil deiner
Variablen global angelegt werden muss und zudem auch noch volatile.
Schau dir doch in der Artikelsammlung einfach mal die Verwendung des
UART an (AVR-GCC-Tutorial/Der UART: Interruptbetrieb). Da hatte ich
auch einmal ein Beispiel zum Empfang von Zeichen via Interrupt
eingestellt.
Hi Remote one,
Danke für die Antwort...Kurze Frage, hab ich das vom vorletzten Beitrag
grundsätzlich richtig verstanden?
> Ich empfange also den ankommenden Befehl (20 Zeichen lang), im Falle> einer empfangenen SMS, und speichere die einzelnen Zeichen in einem> Buffer. Das geschieht alles noch in der IR. Nun soll eine Funktion das> Array bzw. die einzelnen Zeichen auslesen und und an den UART0> weiterleiten. Wäre für die Analyse der seriellen Daten eine IR ebenfalls> sinnvoll? Verständnisfrage: Wenn ich aber permanent Zeichen via> Interrupt empfange, gibts da nicht Probleme wenn eine SMS eintrifft?> Es ist ja nicht so,> dass Interrupts die während der ISR auftreten verloren gehen. Diese> werden dann halt leicht zeitverzögert nach Ablauf der ersten ISR> ausgeführt.> Ich meine, wir haben dann ja ne extrem hohe Zeitverzögerung, wenn ich> meine Prüfroutine anstatt Polling mit IR durchführen würde. Ich empfange> ja permanent Zeichen. Denkfehler meinerseits?> Wie ich das jetzt verstanden habe soll ich sowohl für die Prüfroutine> (Polling), als auch für die IR (Empfangen eines Befehls) eine Art> Ringpuffer anlegen, um bei einer IR, getriggert durch eine SMS, ich in> meiner Main die restlichen Zeichen noch abarbeiten kann.
Eine Frage habe ich noch: Wie kann ich den bspw. die SMS am besten
Versenden? Wenn ich in meiner Main eine Fehlermeldung entdecke, muss ich
ja ebenfalls ein Art Interrupt verwenden, oder? D.h., ich muss dies
mittels Software-Interrupt einläuten. Wie provoziere ich so ein Software
Interrupt?
Also, ich hab jetzt nicht alles genau gelesen, aber 20 Zeichen sind
schon sehr wenig. Wenn eine SMS ankommt, merkst du das ja durch den
Empfang von entsprechenden Steuerstrings des Mobiltelefons oder des GSM
Modems. D.h. du sammelst via Interrupt alle empfangenen Zeichen ein und
vergleichst bei vollständigem Empfang in einer State Machine und führst
entsprechende Aktionen durch, z.B. SMS abrufen. Je nach Typ des
Mobiltelefons / GSM Modems kommen da aber nicht nur die Klartextzeichen
an, sondern noch jede Menge Overhead (TelNr. etc.). Also schon einmal
mehr als 20 Zeichen.
Das Senden einer SMS ist schon wieder eine neue Aufgabe. D.h., wenn du
in deiner State Machine festgestellt hast, dass ein Fehler aufgetreten
ist, dann wird eine Textnachricht versendet. Das kann eigentlich im
Hauptprogramm geschehen. Es ist ja egal, ob du dafür 100ms mehr oder
weniger benötigst. Die größte Zeit geht bei der Vermittlung verlogen. Zu
Silvester kann das auch mal einen halben Tag dauern ;)
Du solltest dir also erst einmal einen Plan machen, was du alles willst.
Danach musst du abschätzen wie schnell das erledigt werden muss (Empfang
so schnell wie mgl., damit neues Zeichen eintreffen kann, Senden kann
auch mal dauern - also Hauptprogramm).
Wenn ich das richtig verstanden habe, dann willst du einfach auf
einkommende SMS in einer gesonderten Art und Weise reagieren
Auch wenn ich den ein oder anderen jetzt nerve, aber ich habe zu einem
ähnlichen Thema mal einen Artikel verfasst:
Versenden von SMS mittels Mobiltelefon
Da du lernbegierig bist, solltest du das nicht einfach so verwenden,
sondern versuchen das selbst zu machen.
Fang am Besten mit mehreren kleinen Programmen an:
Zeichen empfangen via Interrupt
SMS Versenden
SMS dekodieren
etc.
Am Ende spielt dann alles schön zusammen ;)
Hallo Remote One,
vielen Dank für deine Antwort, ich werde mich rein knien und deine
hilfreichen Tipps/Hinweise aufarbeiten. Ich hab halt immer noch das
Problem, dass ich mir die Ausführung der SMS-Routine zu lange vorstelle.
Ich meine, wenn ich ein Fehlermeldung detektiere und dann eine SMS
versenden will, müssen ja weiterhin die seriellen Daten untersucht
werden. Und genau dieser "Konflikt" macht mir bisher noch etwas
Kopfzerbrechen.
Bei Fragen werde ich mich natürlich nochmals melden. Vielen Dank nochmal
für die Antworten!
Euch und Dir natürlich einen guten Rutsch ins neue Jahr!
Gruß,
Matze
Matze schrieb:> Ich meine, wenn ich ein Fehlermeldung detektiere und dann eine SMS> versenden will, müssen ja weiterhin die seriellen Daten untersucht> werden. Und genau dieser "Konflikt" macht mir bisher noch etwas> Kopfzerbrechen.
Angenommen du arbeitest mit 19200baud / 8n1. Dann überträgst du 1920
Zeichen pro Sekunde. Ein Zeichen benötigt also ca. 0,5ms.
Wenn ein kompletter Datensatz angekommen ist, wird dieser ausgewertet
und dann ein neuer erwartet. Während der Auswertung dürfen keine neuen
Daten mehr ankommen oder werden einfach ignoriert (zumindest bei FIFO).
Du musst also gewährleisten, dass deine State Machine so schnell wie
möglich durcharbeitet und den Zeichenpuffer (FIFO) wieder freigibt für
einen neuen Datensatz. Wenn der Vergleich erst einmal durch ist und der
Puffer wieder freigegeben ist, hast du parallel zum Empfang des neuen
Datensatzes ja "unendlich" viel Zeit deine SMS zu versenden. Das Senden
der SMS wird dann eben manchmal durch den Empfang eines neuen Zeichens
unterbrochen - ist ja aber nicht weiter schlimm.
Nun ist es so, dass viele Mobiltelefone einen Datensatz mit \r\n
abschließen. Du weißt ja aber schon bei dem \r, dass das Ende des
Datensatzes erreicht ist. Also wäre es nicht so schlimm, wenn du das
nachfolgende Zeichen nicht empfängst. Du hast also ca. 1ms Zeit um den
Vergleich durchzuführen. Angenommen du arbeitest mit einem Atmel auf
16MHz - dann dauert ein Takt 62,5ns. Bei einer Befehlsausführungszeit
von 1 Befehl je Takt (zumindest die einfachen) wären dass 16000 Befehle
die du überschlagsmäßig ausführen kannst. Selbst wenn du "nur" von den
0,5ms ausgehst und evtl. von 2 Takten je Befehl - ist das immer noch
jede Menge.
Sollte das nicht reichen, kannst du immer noch die Baudrate runter
drehen (9800 / 8n1 ist auch noch weit verbreitet).
Wenn ich mich nicht verrechnet habe und völligen Stuss erzählt habe,
dann brauchst du dir da keine Gedanken machen. Ich konnte bei mir bis
dato zumindest auch noch nie ein solches Problem erkennen.
In dem Sinne, gesundes Neues und lass es dir mal durch den Kopf gehen ;)
Hallo zusammen,
zunächst einmal, wenn auch etwas verspätet, noch ein frohes Neues!
ich hab mein Projekt nun in kleinere Aufgaben zerstückelt, so wie es ihr
gesagt habt. GSM-Initialisierung, SMS-Senden, etc.
Ich hätte da noch eine Frage: Wenn eine Alarmmeldung auftritt möchte ich
gerne in die SMS den Alarmtyp+die Uhrzeit in der er aufgetreten ist.
Also ich hätte das jetzt per AT+CCLK? vom Modem abgefragt. Hat von euch
vielleicht jemand ne andere Idee, wie man das am geschicktesten lösen
könnte? In C gibts ja noch die Headerdatei <time.h>, die allerdings
nicht in der libc ist.
Matze schrieb:> Hat von euch> vielleicht jemand ne andere Idee, wie man das am geschicktesten lösen> könnte? In C gibts ja noch die Headerdatei <time.h>, die allerdings> nicht in der libc ist.
Die geschickteste Antwort hast du selbst geliefert (zumindest wenn die
Zeit im Mobiltelefon richtig eingestellt ist). Alles andere (DCF77, RTC
...) wäre viel zu aufwendig.
Die <time.h> kannst du glaube ich für eine richtige Zeitangabe
vergessen. Ich kenne die jetzt nicht unbedingt, aber
höchstwahrscheinlich liefern die dortigen Funktionen lediglich die
aktuelle Rechenzeit. Also die Systemzeit beim PC. So etwas funktioniert
natürlich nicht bei einem µC.
Das blöde ist nur, dass nach einem Reset oder Unterbrechen der
Stromversorgung ich die Uhrzeit immer wieder neu einstellen muss und
mein Programm ja nicht weiß, wie viel Uhr es gerade ist. Wenn ich mein
Programm starte, muss ich ja immer die Uhrzeit via AT-Befehl AT+CCLK=...
eingeben. Gibt es da ein Befehl der die Uhrzeit aktualisiert bzw.
updatet?
VG
Matze schrieb:> Wenn eine Alarmmeldung auftritt möchte ich> gerne in die SMS den Alarmtyp+die Uhrzeit in der er aufgetreten ist.
Ist eigentlich unsinnig, da der Empfänger ja sieht, wann die SMS
eingegangen ist.
Matze schrieb:> Das blöde ist nur, dass nach einem Reset oder Unterbrechen der> Stromversorgung ich die Uhrzeit immer wieder neu einstellen muss und> mein Programm ja nicht weiß, wie viel Uhr es gerade ist. Wenn ich mein> Programm starte, muss ich ja immer die Uhrzeit via AT-Befehl AT+CCLK=...> eingeben. Gibt es da ein Befehl der die Uhrzeit aktualisiert bzw.> updatet?
Dann schalte dein Modem eben nicht komplett ab. Bei den Mobiltelefonen
läuft beim Ausschalten auch die Uhr weiter. Du musst einfach etwas an
deinem Konzept ändern. Bring doch erst einmal dein Projekt voran. Die
Zeiteinstellung ist glaube ich dein geringstes Problem.