Forum: Mikrocontroller und Digitale Elektronik Probleme beim Empfang über UART: Abhilfe FIFO?


von Urban (Gast)


Angehängte Dateien:

Lesenswert?

Hallo zusammen,

ich beschäftige mich seit kurzem mit der Schnittstellenprogrammierung.
Also bitte ich zunächst einmal um Entschuldigung, falls diese Frage sich 
für manch anderen etwas dümmlich anhört.
Ich möchte gerne 70 Zeichen lange Strings empfangen ( Gerät--> uC ---> 
Hyperterminal). Allerdings empfange ich nur jeden zweiten String. Es 
scheint, dass mein Programm einfach nicht schnell genug ist, diese 
Strings weiter zu versenden. Ich möchte die Daten gerne ohne 
Interruptsteuerung empfangen und versenden.

Ich habe nun gelesen das ein FIFO-Buffer hierbei Abhilfe schaffen 
könnte. Sprich der Datendurchsatz wird erhöht und die CPU Last wird 
verringert. Allerdings versteh ich noch immer nicht ganz wie ich das in 
das vorhandene Programm integrieren kann. Im Grunde werden die Daten ja 
in einen globalen Buffer geschrieben und das erste geschriebene Zeichen 
wird als erstes ausgelesen bzw. zur Verarbeitung weitergegeben.

Wäre wirklich klasse, wenn mir hier jemand weiterhelfen könnte.
Grüße

von Lothar M. (Firma: Titel) (lkmiller) (Moderator) Benutzerseite


Lesenswert?

Urban schrieb:
> die CPU Last wird verringert.
Das heißt auf gut Deutsch: es wird nicht unnötig gewartet...

Und das passiert jetzt aber in deinem Code immer noch:
1
void send_char(unsigned char data)
2
{
3
  while (!(UCSR0A & (1 << UDRE0))); // warten und Däumchen drehen...
4
  UDR0 = data;
5
}
6
7
8
void send_string(char *string)
9
{
10
  while(*string)                    // ein while(), das deutet auf Rechenzeitverbrauch hin...
11
  {
12
    send_char(*string);             // und jetzt kommts: Warten, warten...
Hier kann der uC nichts anderes tun, als zu warten, bis das Senden 
fertig ist. Klar, dass er in der Zwischenzeit keine ankommenden Zeichen 
verarbeiten kann. Da hilft dein ganzer hübscher Fifo nicht!

> Ich möchte die Daten gerne ohne Interruptsteuerung empfangen und
> versenden.
Warum?
Das ist, wie wenn du sagst: ich würde gerne Einparken, aber den 
Rückwärtsgang nicht verwenden (obwohl du einen hast!!!)...

Wenn du mal in dich gegangen bist, such nach P. Fleurys UART-Lib...

BTW:
#define fosc 16000000
Ich schreibe da immer ein UL hinter die Zahl, weil 16000000 nicht in 16 
Bit passen...

von cskulkw (Gast)


Lesenswert?

Die while - schleifen sind wie mein Vorgänger erwähnt hat, tödlich.

Du solltest in der Hauptschleife einen Zustandsautomaten einbauen, der 
anstatt auf While -(TUE GARNICHTS BIS DIE SCHNITTSTELLE FERTIG IST) eine 
If-Abfrage einbauen, die nur bei fertiger Schnittstelle ein Datenbyte 
nachlegt.

Wäre das Senden oder Empfangen noch nicht fertig, ist der Prozessor in 
der Lage an anderer Stelle im Programm weiter zu machen. Phasenprobleme 
werden so vermieden.




ANregung OHNE ANSPRUCH AUF COMPILIERFÄHIGKEIT !!!

define MAX_STING 70

uint8_t *SpeicherString[] = "IRGEND_ETWAS_VON_IRGENDWOHER\0"

uint8_t  SpeicherStringZaehler=0

mode = 0;

// Hier noch die Initialisierungen

Main
{
 while(1)
 {



  switch(mode)
  {
     case 0:
        if(SpeicherStringZaehler++ < MAX_STRING)
        {
          /* IF erspart das Sinnlos Warten ...*/
          if ((UCSR0A & (1 << UDRE0))
      UDR0 = *SpeicherStringe++;
        }
        mode ++;
        break;
     case 1:

        Empfangen irgendwie analog wie oben.

        mode ++;
        break;
     default:
        mode = 0;
         // tue noch irgendetwas anderres sinnvolles und warte nicht.

  }
 }
}
Ich habe mir von je her angewöhnt, so ein kooperatives Scheduling zu 
integrieren. Die Statemachines machen zwar die Funktion des Codes 
unübersichtlich. Aber es lohnt sich. CAN - Grafik-Display und Modem 
betreiben etc. , lassen sich so quasi parallel betreiben.

Und man kann so dem AVRs mit nur 16MHz richtig etwas abverlangen.

Viel erfolg.

von Urban (Gast)


Angehängte Dateien:

Lesenswert?

Hallo zusammen,
vielen Dank für die schnellen Antworten. Ich habe es nun mal mit einer 
ISR probiert und es klappt. Zunächst einmal passen die so, oder 
muss/sollte ich da etwas anders machen? Ich möchte das Programm so 
aufbauen, dass beim Auftreten eines bestimmten 
Zustandes/Zeichenkombination ein Befehl verendet wird.

Zudem hätte ich noch folgende Frage:

Ich habe zwei UARTS mit UART0 will ich die Daten kontrollieren. Mit dem 
Zweiten will ich beim Auftreten einer bestimmten Situation, eine SMS 
versenden.

Die GSM-Initialisierung habe ich ebenfalls mittels Polling realisiert. 
Diese kann ich ja nun einfach durch ISR´s ersetzen. Muss ich dabei auf 
etwas spezielles achten? Ich weiß nicht inwieweit die dann zueinander im 
Konflikt stehen.

Bin für jeden Hinweis und Tipp dankbar...
VG

von Urban (Gast)


Lesenswert?

Hallo, ich bins nochmal.

Das Empfangen und Versenden der Daten klappt auf jeden Fall besser, wie 
mit der while-Schleife. Ich empfange und versende meine 70 Zeichen lange 
Strings nun nacheinander. Allerdings wird der letzte String der nur 14 
Zeichen lang ist nicht versendet. Am obigen Programm hab ich nichts 
verändert. VG

von Karl H. (kbuchegg)


Lesenswert?

Das Problem könnte hier liegen
1
void send_string(char *data)
2
{
3
  if(uart_tx_flag == 1){
4
  strcpy(uart_tx_buffer, data);
5
  uart_tx_flag = 0;
6
  UCSR0B |= (1<<UDRIE0);  
7
  }  
8
}

wenn deine UART nicht sendebereit ist, zb. weil noch eine andere 
Übertragung läuft, dann .... lässt du den String einfach unter den Tisch 
fallen und machst gar nichts. Und das ist dann das was du siehst: Ein 
einzelner String wird nicht gesendet.

von Urban (Gast)


Lesenswert?

Hallo KH, vielen dank für die Antwort!
Wenn ich Funktion get_log_data um

void get_log_data(void)
{
  if( log_daten == 1 && uart_tx_flag == 1)
  {
    send_string(stringbuffer);
    log_daten = 0;
  }
}

uart_tx-flag == 1 erweitere, erhalte ich das gesamte Protokoll. Ich muss 
also noch festlegen: Wenn letzte Übertragung beendet, dann können neue 
Daten gesendet werden?!

Muss ich also immer, wenn ich ein Befehl senden will, mit uart_tx_flag 
== 1 die Sendebereitschaft prüfen? Beim Polling habe ich meine 
Sendefunktion,


void send_string(char *string)
{
  while(*string)
  {
    send_char(*string);
    string++;
  }
  send_char('\r');
}


mit der ich einfach die Befehle innerhalb eines Programms versenden 
kann.

if(log_data == 1)
send_string("Log Daten vorhanden");

Bspw. wenn ich nach dem Erreichen eines Counters == 12 einen Befehl 
versenden will, oder wenn ich auf die Antwort des GSM Moduls reagieren 
will. Muss ich das dann immer mittels Flags realisieren? Sprich Flag 
setzen und schauen ob uart_tx_flag auch gesetzt, wenn ja, Senden?

if(counter == 12)
counter_flag = 1;
.
.
.

if(strcmp(stringbuffer, "OK") == 0)
gsm_answer_flag = 1;
.
.
.

if(counter_flag == 1 && uart_tx_flag == 1)
{
    send_string("Counter = 12");
    counter flag = 0;
}

if(gsm_answer_flag == 1 && uart_tx_flag == 1)
{
    send_string("12345");
    gsm_answer_flag = 0;
}


Ich kapiere das immer noch nicht so richtig.
VG

von Lothar M. (Firma: Titel) (lkmiller) (Moderator) Benutzerseite


Lesenswert?

Urban schrieb:
> Ich muss also noch festlegen: Wenn letzte Übertragung beendet,
> dann können neue Daten gesendet werden?!
Nein, eigentlich mußt du nur eine etwas bessere Pufferverwaltung 
einführen. Ein Ringpuffer wäre da gut geeignet (das ist übrigens ein 
Fifo). Wenn der ausreichend groß ist, dann kannst du von deinem 
"Hauptprogramm" einfach Zeichenketten ausgeben, und die ISR 
(+Pufferverwaltung) sorgt dann schon dafür, dass die Zeichenketten 
irgendwann nacheinander auf der SIO ausgegeben werden.
Die Logik mit den Flags bringt dich irgendwann garantiert zur 
Verzweiflung.

von Karl H. (kbuchegg)


Lesenswert?

Lothar Miller schrieb:

> Die Logik mit den Flags bringt dich irgendwann garantiert zur
> Verzweiflung.

Das unterstreiche ich ganz dick und mit roter Farbe!

von Urban (Gast)


Angehängte Dateien:

Lesenswert?

Hallo,
zunächst einmal vielen Dank für eure Antworten.
Ich habe mich mal anhand den Anmerkungen von Lothar an die Arbeit 
gemacht und versucht die Pufferverwaltung zu realisieren.

Für mich als Anfänger auf jeden Fall etwas schwieriger als wie beim 
Einsatz von Polling. Es klappt nicht wirklich. Mit Hilfe meines C-Buches 
und über das Internet hab ich versucht die Fehler ausfindig zu machen, 
ohne Erfolg.

Es wäre nett, wenn der ein oder andere erfahrenen Programmierer einen 
Tipp oder Hinweis hätte...Es wäre schon mal gut zu wissen, ob das, was 
ich gemacht hab in die Richtung geht, die mit Lothar geraten hat.

Meine Strings, die ich empfange Enden immer mit '\r', also würde ich 
dieses Zeichen als Terminator verwenden. In meinem Programm möchte ich 
die bereits oben erwähnte Flag-Problematik vermeiden um im Haupt- oder 
Nebenprogramm Befehle zu versenden.

Viele Grüße,
Urban

von Karl H. (kbuchegg)


Lesenswert?

Das ist doch kein Ringbuffer, was du da hast.

Weder beim Senden noch beim Empfangen

> Meine Strings, die ich empfange Enden immer mit '\r', also würde ich
> dieses Zeichen als Terminator verwenden.

Ein Tip: Halte den Ringbuffer aus jeglicher Dateninterpretation raus.
Ein Ringbuffer soll einfach nur ein Zwischenspeicher sein, der Zeichen 
solange aufnimmt, bis die Verarbeitungseinheit Zeit hat, die 
zwischengespeicherten Zeichen abzuarbeiten. Mehr nicht.


Ein Ringbuffer heißt deswegen so, weil die Speicherfläche logisch 
gesehen zu einem Ring 'gebogen' wird.
Stell dir eine Uhr vor. An jeder Minutenposition ist eine 
Speicherstelle. Wenn Zeichen in den Ringbuffer gestellt werden, dann 
werden sie einfach reihum in das nächste Minutenfach gestellt. Wenn es 
dabei über die Stundengrenze (=60 Minuten) geht, dann wird einfach 
wieder vorne angefangen. So wie der Minutenzeiger einer Uhr auch einfach 
nur immer im Kreis läuft und trotzdem immer von 0 bis 59 zählt.
Wenn die verarbeitende Einheit Zeit dazu hat, dann holt sie sich ein 
Zeichen nach dem anderen, solange bis sie entweder genug hat, oder 
nichts mehr Abzuholendes im Ringbuffer vorliegt.

D.h. der Ringbuffer hat 2 ausgezeichnete Positionen:
* Wo wird das nächste Zeichen eingefügt, beim Einstellen in den 
Ringbuffer
* Von wo wird das nächste Zeichen gelesen, beim Wiederauslesen aus dem 
Ringbuffer


(Und ja du brauchst 2 Ringbuffer. Einen fürs Senden und einen fürs 
Empfangen)

Schau dir die UART LIb vom Peter Fleury an. Der verwendet Ringbuffer und 
interrupt gesteuertes Senden/Empfangen.


Verblüffend: Wir haben gar keinen Artikel in der Artikelsammlung zum 
Thema Ringbuffer

von Urban (Gast)


Lesenswert?

Hallo Karl-Heinz,
danke für deine Antwort.
Ufff,das war wohl ein Griff ins Klo. Ich habe mir die Lib mal angeschaut 
und bin nicht so richtig durchgestiegen. Dann werde ich mir sie nochmals 
zu Gemüte führen und hoffen das ich es kapier. Nochmals Danke!
VG,
Urban

von Karl H. (kbuchegg)


Lesenswert?

Ist ja kein Beinbruch. Deswegen hat PF die Lib ja veröffentlicht, damit 
man sie einfach nur benutzen kann, ohne sich um die Details kümmern zu 
müssen.

Es gibt eine Funktion, die ein empfangenes Zeichen holt und es gibt eine 
Funktion, die ein Zeichen versendet. Mehr braucht man nicht um daraus 
deine gewünschte Funktionalität hochzuziehen.

von Falk B. (falk)


Lesenswert?

Oder selber machen, mit Multitasking.

von Urban (Gast)


Angehängte Dateien:

Lesenswert?

Hallo,

ich habe nun versucht mit der von KH vorgeschlagenen UART Lib von Peter 
Fleury einen FIFO zu realisieren.

Ich verwende eine Arduino Board Mega mit einer 16MHz Taktfequenz. Die 
Daten die ich empfange werden mit 9600bps 8Bit, keine Parität und einem 
Stoppbit versendet. Bisher konnte ich mit Polling meine kleine Anlage 
ansteuern.

Zwar seh ich jetzt den versendeten String am Hyperterminal, meine Anlage 
reagiert allerdings nicht mehr wenn ich die Befehle über den UART 
versende.
Das lies mich vermuten, dass etwas nicht mit der Einstellung des 
Datenformates(Parität, bps, etc.) stimmt. Allerdings ist soweit alles in 
Ordnung.

Des weiteren wollte ich die empfangenen Zeichen, sofort wieder über die 
UART versenden (siehe Programm). Allerdings wird nur 0x00 versendet. Ich 
weiß nicht mehr weiter.

von Karl H. (kbuchegg)


Lesenswert?

Bevor ich mir einen Wolf suche:

Hast du die Fleury Funktionen so gelassen wie sie waren?

(Warum hast du sie nicht in ihrem eigenen File unverändert gelassen. Du 
brauchst dieses C-File doch nur ins Projekt mit aufnehmen und schon hast 
du die Funktionen zur Verfügung)

von Karl H. (kbuchegg)


Lesenswert?

In der Fleury Lib ist eine Demo dabei
1
  while( 1 )
2
  {
3
    
4
    // uart_putc( uart_getc() );   // empfangene Daten wieder versenden
5
    
6
    
7
    
8
    char c = uart_getc();
9
10
    if(c == 0x04)
11
    uart_puts("Hab ich");
12
13
  }

sieh dir dort an, wie man die uart_getc Funktion richtig verwendet. 
Achte auch auf den Datentyp der Variablen, die Peter in seiner Demo 
benutzt hat.

von Urban (Gast)


Lesenswert?

Hallo Karl-Heinz,ich wollte das nicht einfach so übernehmen, sondern 
versuchen das zu verstehen und selbst zu probieren...Klappt allerdings 
noch nicht ganz. ich bleib hartnäckig

von Karl H. (kbuchegg)


Lesenswert?

Urban schrieb:
> Hallo Karl-Heinz,ich wollte das nicht einfach so übernehmen, sondern
> versuchen das zu verstehen und selbst zu probieren...Klappt allerdings
> noch nicht ganz. ich bleib hartnäckig

OK.
Extra langsam, nur für dich:

S i e h

d i r

d i e

b e i g e l e g t e

D e m o

a n

!

Der Fleury Peter macht das nicht zum Spass, dass er eine Demo schreibt, 
an der man sieht, wie man die einzelnen Funktionen zu verwenden hat!

Sein uart_getc ist ein nicht wartendes getc. Das kommt auch dann zurück, 
wenn es kein Zeichen an der UART vorgefunden hat. Es ist aber so 
freundlich, dem Aufrufer mitzuteilen, dass genau dieser Fall vorliegt: 
UART_NO_DATA (Ich hab nichts für dich).
Nur MUSST DU DIESE INFORMATION AUCH AUSWERTEN

Und damit man sieht wie das geht, hat der Peter Fleury seiner Lib eine 
Demo beigelegt, in der er unter anderem genau das zeigt.

NUR TUN MUSST DU ES!

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
Noch kein Account? Hier anmelden.