Guten Abend ! Wie lang darf denn ein String maximal sein, welchen ich an den µC (Attiny2313) per UART senden darf ? Meine ISR Routine packt alle empfangen Bystes in ein char Array und das solange bis ein ! ankommt, wenn das ! ankommt ist der Befehl vollständig. Wenn ich nun: hi! sende bekomme ich auch hi! zurück. wenn ich allerdings etwas längeres sende z.b. hihallo! dann kommt es abgehackt wieder zurück. Was kann ich dagegen tun ? Gibt es irgendwelche Strategien ?
Die erste Frage ist, benutzt du einen Quarz als Taktgeber für den Controller? Da ich davon ausgehe, dass du so weit schon gedacht hast, ist die nächste Frage: Wie groß ist der Fehler zwischen eingestellter und tatsächlicher Baudrate am Controller? Wenn der Fehler zu groß ist und der Controller zwischen den Byte keine Zeit hat, sich neu zu synchronisieren, addiert sich meines Wissens der Fehler, so dass er irgendwann so groß ist, dass der Controller nichts Sinnvolles mehr empfängt.
> Wie lang darf denn ein String maximal sein, welchen ich an den µC > (Attiny2313) per UART senden darf ? So lang wie Dein Speicher groß ist, d.h. wenn Du die empfangenen Daten ins Backup-Device umleitest, dann beliebig lang. Sonst zweckmäßigerweise nur so viel wie Du (ggf. zwischenspeichern und) verarbeiten(!) kannst :-) > [..] dann kommt es abgehackt wieder zurück. Was kann ich dagegen tun ? Den Fehler finden ;-) 'Abgehackt' klingt nach zu wenig Speicher reserviert oder nach zu wenig Zeit für den Datenempfang. Zeich mal den Code, dann kann Dir vielleicht geholfen werden.
Dussel schrieb: > Wenn der Fehler zu groß ist und der Controller zwischen den Byte keine > Zeit hat, sich neu zu synchronisieren, hat er aber. Genau das ist der Zweck von Start- und ganz besonders Stopbit. > addiert sich meines Wissens der > Fehler, so dass er irgendwann so groß ist, dass der Controller nichts > Sinnvolles mehr empfängt. Das stimmt so nicht. Solange die Fehler nicht so gross sind, dass es innerhalb eines Bytes zu Fehlern kommt, ist alles in Butter. Mit jedem neuen Startbit synchronisiert sich der Empfänger wieder auf den Sender.
So ist das. Wenn du nicht direkt "online" schon was weiterverarbeitest, musst du den gesamten Zeichestring irgendwo zwischenspeichern. Wieviel Platz du dafür hast, hängt vom Controller und vom Compiler (und dessen Einstellungen) und deinem sonstigen RAM-Bedarf ab. Also: Frage so nicht beantwortbar.
>Genau das ist der Zweck von Start- und ganz besonders Stopbit.
Ok, wenn du das sagst. Ich glaubte nur, mal gelesen zu haben, dass der
Controller eventuell das Start- und Stopbit nicht mehr richtig erkennt,
wenn die Daten zu schnell hintereinander kommen, so dass es praktisch
fast nur noch ein einziger Datenstrom ist.
Dann fällt das aber als Fehlerquelle schonmal weg :-)
Dussel schrieb: >>Genau das ist der Zweck von Start- und ganz besonders Stopbit. > Ok, wenn du das sagst. Ich glaubte nur, mal gelesen zu haben, dass der > Controller eventuell das Start- und Stopbit nicht mehr richtig erkennt, > wenn die Daten zu schnell hintereinander kommen, so dass es praktisch > fast nur noch ein einziger Datenstrom ist. Das ist nur beim ersten Anschalten der UART problematisch, wenn die UART auf einen bereits laufenden Datenstrom aufgeschaltet wird. Dann kann es passieren, dass die UART nie das Startbit identifizieren kann. Hat sie aber das Startbit erst einmal UND ist der Taktfehler klein genug, dass es innerhalb eines Bytes zu keinem Fehler kommt, dann findet sie auch das nächste Startbit wieder zuverlässig. Es beginnt mit der ersten Flanke nachdem das letzte Byte vollständig empfangen wurde. Die Pause zwischen dem letzten Bit des letzten Bytes und dem Beginn des Startbits ist nicht informationstragend sondern einfach nur eine Pause unbestimmter Länge (für den Empfänger).
Guten Morgen ! Ich habe leider jetzt keine Zeit um die notwendigen Informationen zu schreiben, aber sobald ich zu Hause bin, werde ich mich wieder melden. Danke erstmal ! MfG, MC_AVR
Guten Tag ! Ich benutze einen externen Quarz mit 11.059200 MHz. Meine BAUD Rate ist 9600. Nach den Berechnungsgleichungen in dem AVR GCC Tutorial folgt daraus: UBRR_VAL = 71.5 (Clever gerundet ;) ) BAUD_REAL = 9533.79 BAUD_Error = 0.993103 das wäre doch ein Fehler von: 0.689%, also 1-0.993103 eben. Damit bin ich ja unter der kritischen 1% Grenze. Mein Code für den Datenempfang:
1 | // Globale Variable für den Daten-Puffer
|
2 | volatile char received_data[20]; |
3 | |
4 | /* Interrupt Routine für Datenempfang */
|
5 | ISR(USART_RX_vect) |
6 | {
|
7 | |
8 | |
9 | |
10 | received_data[i] = UDR; // Lade die empfangenen Bystes in den Daten-Puffer |
11 | |
12 | |
13 | // send_uart("interrupt\n");
|
14 | |
15 | if(received_data[i]==0x21) // !-Zeichen erhalten, Befehl fertig => Flag setzen. |
16 | {
|
17 | // send_uart("!-Angekommen\n");
|
18 | rx_flag = 1; |
19 | |
20 | i=0; |
21 | }
|
22 | else
|
23 | {
|
24 | i++; |
25 | }
|
26 | }
|
27 | /*----ENDE----*/
|
Ein Datenpuffer von 20 Char, sollte aber eigentlich für meine Strings reichen. MfG, MC_AVR
Schöne Routine. Der Fehler sitzt wieder mal in den Teilen, die du nicht zeigst. Häng doch ganz einfach deinen Source Code komplett als Attachment an. Dann suchen wir uns die Teile raus, die wir brauchen. Ist für dich einfacher und für uns auch. PS: Was ist der Unterschied zwischen if(received_data[i]==0x21) und if( received_data[i]== '!' ) Richtig! Im zweiten sieht auch ein Blinder mit Krückstock, worauf du vergleichst ohne zuerst im Web nach 5 ASCII Tabellen suchen zu müssen und zu vergleichen, ob die Kommentare neben dem Code noch zeitgemäss sind. Ausserdem kann man sich dann den Kommentar komplett sparen, weil alles Wissenswerte im Code steht.
Ja ich habe immer Angst so fiese Kommentare immer zu bekommen :-(. Ist die Routine wirklich schön oder ist das nur wiedermal "ironisch" gemeint ;). Der Code ist im Anhang !
MC_AVR schrieb: > Ja ich habe immer Angst so fiese Kommentare immer zu bekommen :-(. > > Ist die Routine wirklich schön oder ist das nur wiedermal "ironisch" > gemeint ;). Sieh sie dir an. Hast du Einrückungen? Sind die konsistent? Wieviele tatsächlich arbeitende Code-Zeilen hat die Funktion? Wieviele Code-Zeilen hast du im Editor dafür verbraucht? Wieviel Leerraum umfasst die Funktion? Bringt der irgendwas ausser den Code in die Länge ziehen? > > Der Code ist im Anhang ! mal reinschauen.
Ein Fehler besteht schon mal darin, dass du für die Auswertung (und sei es nur für das momentan auskommentierte Zurücksenden) davon ausgehst, dass in deinem Buffer ein C-String steht. Das tut er aber nicht. Du hast in deiner Empfangsroutine den Buffer nirgends mit einem \0 Zeichen abgeschlossen, welches das Ende des Strings anzeigt. Deine ganze Stringverarbeitung wird alles Mögliche machen, weil jede Funktion über das Ende des Strings hinausknallt bzw. du höllisch darauf aufpassen musst, dass bei dir ! das Ende eines Strings anzeigt. Erfinde nicht deine eigenen Konventionen. Das du den ! benötigst um auf der Übertragungsstrecke das Ende eines Befehls zu kennzeichnen ist eine Sache. Aber in deinem Programm solltest du auf die übliche C-Konvention zurückgreifen: Jeder String hat als letztes Zeichen ein '\0'. Dort endet der String. <weiterschau>
D.h. ich sollte wenn das Ausrufungszeichen angekommen ist einfach received_data[20] = '\0'; ausführen ?
MC_AVR schrieb: > D.h. ich sollte wenn das Ausrufungszeichen angekommen ist einfach > received_data[20] = '\0'; ausführen ? Wieso 20? Welches ist dein letztes Zeichen im Buffer, das noch gültig war und zum String gehört?
MC_AVR schrieb: > D.h. ich sollte wenn das Ausrufungszeichen angekommen ist einfach > received_data[20] = '\0'; ausführen ? nö, deine Strings sind ja schließlich nicht immer 19 Zeichen lang...
Ja das ! zeichen, aber dann müsste ich ja mit einer String Funktion das ! suchen und dann einfach +1 von dieser Stelle das '\0' addieren. Meinst du es so ?
1 | ISR(USART_RX_vect) |
2 | {
|
3 | received_data[i] = UDR; // Lade die empfangenen Bystes in den Daten-Puffer |
4 | |
5 | if( received_data[i] == '!' ) // !-Zeichen erhalten, Befehl fertig => Flag setzen. |
6 | {
|
7 | // send_uart("!-Angekommen\n");
|
8 | rx_flag = 1; |
9 | received_data[i] = '\0'; |
10 | // oder wenn du drauf stehst, dass das ! erhalten bleibt
|
11 | // received_data[i+1] = '\0';
|
12 | i = 0; |
13 | }
|
14 | else
|
15 | {
|
16 | i++; |
17 | }
|
18 | }
|
Du schreibst das ! an die Stelle i im Buffer. Welches ist daher die nächste Position an die das \0 Zeichen muss? (Jetzt kannst du dir aussuchen, ob du das ! im String lassen willst, oder ob du es gleich entfernst, weil es ja keine Information trägt)
Aha ! Dann muss ich später beim Parsen nicht extra das ! rausfiltern, d.h. ich kann mir ein strtok_r ersparen !
Zurück zum Problem. > Wenn ich nun: hi! sende bekomme ich auch hi! zurück. wenn > ich allerdings etwas längeres sende z.b. hihallo! dann kommt > es abgehackt wieder zurück. Was heißt in dem Zusammenhang 'abgehackt'?
Ich gebe zu die Definition abgehackt war etwas ungünstig gewählt. Ein Beispiel Output wäre sowas: hallo!Karo@Karo hallo! hallo!karo@KaroKaro so sah es aus. Mal kam die gesendete Zeichenkette richtig zurück und mal nicht. Liegt das wahrscheinlich daran, dass ich das nicht mit einem \0 abgeschlossen habe ?
Noch was:
1 | volatile char received_data[20]; // Buffer wo die empfangenen Daten gespeichert werden... |
2 | volatile int rx_flag; // Status-Flag für vollständig übertragene Befehle... |
3 | int i=0; // Index-Variable für den Daten-Puffer |
i ist dür diese Variable so ziemlich der ungünstigste Name, den du dir hast aussuchen können. i wird gerne für for Schleifen benutzt. Nimm einfach mal an, du würdest programmieren
1 | void Auswert( int irgendwas ) |
2 | {
|
3 | for( i = 0; i < irgendwas; ++i ) |
4 | mach_was; |
5 | }
|
und dabei vergessen, für dieses i eine lokale Variable anzulegen. Was glaubst du wohl wird mit deiner UART Bufferverwaltung passieren. Defensiv programmieren! Der Compiler soll Fehler finden können. Dazu gehört auch, Variablennamen zu wählen, bei denen Tippfehler oder Unterlassungen sofort auffallen! Globale Variablen nennt man niemals i, j, k, l, x, y, oder sonstige Namen, die in einem Programm häufig vorkommen
MC_AVR schrieb: > Ich gebe zu die Definition abgehackt war etwas ungünstig gewählt. Ein > Beispiel Output wäre sowas: > > hallo!Karo@Karo hallo! hallo!karo@KaroKaro so sah es aus. Mal kam die > gesendete Zeichenkette richtig zurück und mal nicht. Liegt das > wahrscheinlich daran, dass ich das nicht mit einem \0 abgeschlossen habe > ? Ja. Höchst wahrscheinlich. Deine Strings waren keine Strings.
Lieber Karl Heinz Buchegger, ich habe nun die Strings mit einem \0 beendet und der UART sendet bei längeren Zeichenketten genau das zurück was ich gesendet habe ! Ich werde und mein Code etwas überarbeiten. Ich bedanke mich herzlich bei dir und den anderen für die freundliche Unterstützung. Bei Fragen werde ich mich wieder melden :) MfG, MC_AVR
Ich bekomme eine Warnmeldung und zwar in dieser Zeile: dir_com = strtok_r(received_data,";",&tmp); Compiler Warnung: main.c:133: Warnung: Übergabe des Arguments 1 von »strtok_r« entfernt Kennzeichner von Zeiger-Ziel-Typ Kann ich dieser Warnmeldung ignorieren ? Ich habe gelernt, dass man Warnmeldungen wie Fehler betrachten soll :)
MC_AVR schrieb: > Ich bekomme eine Warnmeldung und zwar in dieser Zeile: > > dir_com = strtok_r(received_data,";",&tmp); > > Compiler Warnung: main.c:133: Warnung: Übergabe des Arguments 1 von > »strtok_r« entfernt Kennzeichner von Zeiger-Ziel-Typ gemeint ist das volatile. > Kann ich dieser Warnmeldung ignorieren ? Ich habe gelernt, dass man > Warnmeldungen wie Fehler betrachten soll :) In deinem Fall kannst du die erst mal ignorieren, auch wenns nicht ganz sauber ist.
Ich habe noch eine Frage ! Ich möchte nun ein Stopp Befehl einführen, das sobald ein bestimmtes Zeichen eintrifft, soll der Motor gestoppt werden. Wenn ich nun den Motor den Befehl gebe x Schritte auszuführen und dieser dann quasi in einer Schleife ist, ja den Stopp Befehl ignorieren. Wie könnte ich das lösen ? Läuft die ISR Routine immer parallel zu anderen Funktionen bzw. Schleifen ? Wenn ja könnte ich ja von der ISR Routine das CLK Signal für den L297 sofort lahmlegen und der Motor wäre dann im Haltemoment.
MC_AVR schrieb: > Ich habe noch eine Frage ! > > Ich möchte nun ein Stopp Befehl einführen, das sobald ein bestimmtes > Zeichen eintrifft, soll der Motor gestoppt werden. Wenn ich nun den > Motor den Befehl gebe x Schritte auszuführen und dieser dann quasi in > einer Schleife ist, ja den Stopp Befehl ignorieren. Wie könnte ich das > lösen ? Läuft die ISR Routine immer parallel zu anderen Funktionen bzw. > Schleifen ? Wenn ja könnte ich ja von der ISR Routine das CLK Signal für > den L297 sofort lahmlegen und der Motor wäre dann im Haltemoment. Ganz ehrlich: Die beste Lösung wäre es, das ganze Konzept zu verändern. Du darfst nirgends Arbeitsschleifen haben, die längere Zeit brauchen. Von daher darf es keine 'Schleife in der der Motor läuft' geben. Ein derartiger Ansatz führt praktisch immer zu deinen Problemen und noch viel schlimmeren. Wenn dein Programm mehrere Dinge 'gleichzeitig' machen soll, dann macht man das so: Im Hauptprogramm gibt es eine einzige Schleife - die Endlossschleife. In dieser Schleife wird geprüft, ob es eine bestimmte Aktion zu tun gibt, so wie du das mit dem UART Empfang gemacht hast. Aber du darfst von dort nicht turn_stepper aufrufen bzw. turn_stepper darf nicht die komplette Bewegung abfahren, sondern immer nur einen Schritt. Durch die Endlosschleife in main() ist sowieso sichergestellt, dass irgendwann alle Schritte abgearbeitet sind, einer nach dem anderen. So ungefähr
1 | void turn_stepper_once(int direc, int steps) |
2 | {
|
3 | if(direc==1) |
4 | {
|
5 | PORTD |= (1<<DIR); |
6 | }
|
7 | else if(direc==0) |
8 | {
|
9 | PORTD &=~ (1<<DIR); |
10 | }
|
11 | |
12 | PORTD |= (1<<CLK); |
13 | _delay_ms(1); |
14 | PORTD &=~ (1<<CLK); |
15 | _delay_ms(1); |
16 | }
|
17 | |
18 | |
19 | int main() |
20 | {
|
21 | ....
|
22 | |
23 | |
24 | while( 1 ) { |
25 | if( rx_flag == 1 ) { |
26 | // auf der UART ist etwas eingetroffen, werte es aus
|
27 | parser(); |
28 | }
|
29 | |
30 | else if( stepsToGo > 0 ) { |
31 | // hat der Motor noch etwas zu tun, dann lass ihn einen Schritt
|
32 | // machen
|
33 | turn_stepper_once( direction, stepsToGo ); |
34 | stepsToGo--; |
35 | }
|
36 | |
37 | ..... vieleicht gibt es noch andere Dinge zu tun |
38 | ..... aber Achtung: nichts davon darf längere Zeit brauchen |
39 | |
40 | } // das wars .. nächster Durchgang um alle möglichen Arbeiten zu erledigen |
Im Grunde sollte man die 1ms _delay_ms in turn_stepper_once auch noch loswerden. So etwas will man eigentlich auf keinen Fall haben. parser setzt dann ganz einfach die Werte in den globalen Variablen direction bzw. stepsToGo und durch die Hauptschleife wird diese Schrittanzahl nach und nach abgearbeitet. Soll der Motor sofort stehen, dann genügt es stepsToGo einfach auf 0 zu setzen.
d.h. ich muss an jedem Ende der while(1) Schleife das rx_flag auf 0 setzen, so das quasi neue Daten ankommen können. Wichtig ist, dass ich nur eine einzige Schleife habe und zwar die while(1) Schleife in main. Wenn ich 1000 Schritte drehen soll, parse ich den Befehl gib die Anzahl der Schritte an StepsToGo (Globale Variable) durch. Das mit der ISR Routine kann aber so bleiben ?. Ich werde meinen Code nun von neu aus schreiben bzw. das Konzept ändern und das hier posten. Wenn du dann noch Zeit hast, kannst du ja mal einen oder mehrere Blicke drauf werden :).
MC_AVR schrieb: > d.h. ich muss an jedem Ende der while(1) Schleife das rx_flag auf 0 > setzen, so das quasi neue Daten ankommen können. Nein. Du musst das rx_flag am Ende von parser() auf 0 zurück setzen. Denn dann wurde ja der übertragene String bearbeitet.
Ach ja, stimmt ! Ich werde nun erstmal so ein Ablaufplan (Struktogramm) auf Papier mit sämtlichen Funktionen machen um einen Überblick zu erschaffen. Ich programmiere öfters Sachen viel zu sehr kompliziert und auch leider falsch, wie man sieht. Wie "seit" ihr denn so gut im Coden geworden ? Ist es nur die reine Intelligenz oder einfach die Praxis ?
MC_AVR schrieb: > Ich werde nun erstmal so ein Ablaufplan (Struktogramm) > auf Papier mit sämtlichen Funktionen machen um einen Überblick zu > erschaffen. Bei grösseren Programmen macht man das Design zweckmäßigerweise "vorher".
Hallo ! Ich habe das Programm etwas überarbeitet und es funktioniert einwandfrei ! Das Stoppen funktioniert auch! Das delay habe ich nur drin, da der Schrittmotor normalerweise ohne dem delay sich zwar dreht, aber sehr langsam und dabei summt. Gibt es dafür abhilfe ? Der Code ist im Anhang. Ich wäre über Verbesserungsvorschläge und Tipps sehr dankbar ! Eine Frage hätte ich noch da: Gibt es für Linux ein Programm mit dem ich viele Befehle Zeile für Zeile nacheinander senden kann ? Ich benutze für Linux zwar das Programm HTerm, was auch super ist, wenn ich aber die Funktion: send file benutze und in die Datei z.b. sowas rein schreibe: d:r;100! d;l;200! und das schicke passiert nichts. Wie muss ich so eine Textdatei denn gestalten ? Oder sollte ich für mein Vorhaben ein kleines Konsolen Programm schreiben, welches aus einer Datei Zeile für Zeile die Befehle sendet ? Danke !
MC_AVR schrieb: > Das delay habe ich nur drin, da der Schrittmotor normalerweise ohne dem > delay sich zwar dreht, aber sehr langsam und dabei summt. Gibt es dafür > abhilfe ? Ja natürlich. Aber dann müsstest du nochmal alles umdrehen. Du müsstest dann mit einem Timer einen Zeittakt in einer ISR erzeugen Die 2ms sind, denk ich verschmerzbar, zumal ja die UART sowieso unabhängig weiterläuft und du gar nicht so schnell das Stopp-Kommando schicken kannst, als das dir die 2ms weh tun. > Funktion: send file benutze und in die Datei z.b. sowas rein schreibe: > > d:r;100! > d;l;200! > > und das schicke passiert nichts. Hast du berücksichtigt, dass du über die UART dann auch alle Zeilenumbrüche bekommst? Die musst du entweder ausfiltern oder in deiner Parserei berücksichtigen Da du aber hier if(dir_com_s[2]=='l') fix davon ausgehst, dass die Richtungsinformation immer das 3.Zeichen ist, wirft dich jegliche Abweichung von deinem Schema aus der Bahn :-), selbst wenn es eine harmlose Abweichung ist. d:r;100! kann dein Parser zb auch nicht richtig auflösen, obwohl ich einfach nur einen Haufen Leerzeichen gemacht habe.
Wenn's ohne Zeilentrenner sein muss: while read line; do printf "%s" "$line" sleep .1 done < infile >/dev/ttyS1 Überträgt zeilenweise aus infile, ohne den Zeilentrenner zu senden. Es wartet ca. 100 ms nach jeder Zeile. EDIT: /dev/ttyS1 als Schnittstelle angenommen.
kann man die 100ms pause länger machen ? das geht glaub ich alles zu schnell... der Motor dreht dann nur hin und her...
Guten Abend ! Wie meinst du das mit den Leerzeichen ? :). Ich muss mir noch mal ein bessere Parserei ausdenken oder was könntest du mir empfehlen ?. Danke und gute Nacht ;-).
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.