Nochmals Frage zu UART

Gast #1664204
Lesenswert?

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 ?
Gast #1664219
Lesenswert?

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.
Gast #1664227
Lesenswert?

> 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.
#1664263
Lesenswert?

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.
Gast #1664269
Lesenswert?

>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 :-)
#1664287
Lesenswert?

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).
Gast #1664795
Lesenswert?

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
#1664800
Lesenswert?

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.
#1664819
Lesenswert?

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.
#1664824
Lesenswert?

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>
#1664837
Lesenswert?

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)
Gast #1664849
Lesenswert?

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 
?
#1664853
Lesenswert?

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
#1664854
Lesenswert?

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.
Gast #1664861
Lesenswert?

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
Gast #1664884
Lesenswert?

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 :)
#1664890
Lesenswert?

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.
Gast #1664893
Lesenswert?

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.
#1664906
Lesenswert?

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.
Gast #1664926
Lesenswert?

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 :).
Gast #1664956
Lesenswert?

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 ?
Gast #1664964
Lesenswert?

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".
Gast #1665355
Lesenswert?

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 !
Angehängte Dateien:
#1665365
Lesenswert?

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.
#1665375
Lesenswert?

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.

Antwort schreiben

Bitte melde dich an, um einen Beitrag zu schreiben.

oder

Mit Google-Account einloggen

Die Registrierung ist kostenlos und dauert nur eine Minute.

Jetzt registrieren