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
volatilecharreceived_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>
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 ?
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)
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
?
volatilecharreceived_data[20];// Buffer wo die empfangenen Daten gespeichert werden...
2
volatileintrx_flag;// Status-Flag für vollständig übertragene Befehle...
3
inti=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
voidAuswert(intirgendwas)
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
voidturn_stepper_once(intdirec,intsteps)
2
{
3
if(direc==1)
4
{
5
PORTD|=(1<<DIR);
6
}
7
elseif(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
intmain()
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
elseif(stepsToGo>0){
31
// hat der Motor noch etwas zu tun, dann lass ihn einen Schritt
}// 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.
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 ;-).