Forum: Mikrocontroller und Digitale Elektronik Empfangen und Übertragung von Daten über USART


von Luffy M. (monkeydluffy)


Angehängte Dateien:

Lesenswert?

Hallo,

Ich verwende den Mikrocontroller Atmega644p und er hat zwei USARTs 
(USART0 und USART1). Ich habe eine Routine mit Interruptsfunktionen für 
den Atmega 644p geschrieben, so dass der Atmega 644p Byte über USART0 
empfängt und dann die empfangenen Byte wieder über USART1 sendet. Leider 
funktioniert meine Interruptsroutine nicht gut. Ich bitte sie um Hilfe. 
Ich weiße nicht, was schief läuft. Ich bin auf  Korrekturen offen.

PS: Das Programm für das Senden der Byte (Charakter 0, 1, 2, ...)von PC 
zum Atmega644p liegt im Anhang und wurde in c++ geschrieben. Das 
Programm c++ habe ich getestet und er läuft einwandfrei.

Danke im Voraus.

Monkeydluffy.

von Stefan E. (sternst)


Lesenswert?

Wozu soll die ISR(USART1_TX_vect) gut sein?
Insbesondere mit einem Inhalt, der eher an eine Empfangsroutine 
erinnert.

von Luffy M. (monkeydluffy)


Lesenswert?

Hi Stefan Ernst,

was schlägst du mich vor ?

mfg

von Karl H. (kbuchegg)


Lesenswert?

Luffy Monkey schrieb:
> Hi Stefan Ernst,
>
> was schlägst du mich vor ?

Schrittweise vorgehen.

Erstmal alles ohne Interrupt.

Dann den Empfangsinterrupt in Betrieb nehmen und das Senden an die 
andere USART weiterhin konventionell erledigen.

Und erst dann den 2.ten Interrupt zum Senden dazu nehmen.


Ein hinreichend komplexes Programm in einem Zug ohne Zwischenstufen zu 
schreiben, ist ein ziemlich sichere Garantie dafür, dass es nicht 
funktionieren wird. Und das Problem ist dann, dass man mit haufenweise 
Code dasitzt und nicht weiß, wo man mit der Fehlersuche anfangen soll, 
weil der/die Fehler überall stecken kann.

von Stefan E. (sternst)


Lesenswert?

Luffy Monkey schrieb:
> was schlägst du mich vor ?

Ich schlage dir vor, dass du uns erklärst, was deine Absicht war, als du 
diese ISR geschrieben hast. Nur dann können wir wissen, ob sie 
umgeschrieben werden muss, oder einfach entfernt werden kann.

Und wenn du schon beim Erklären bist, dann erkläre auch gleich, was die 
Hauptschleife eigentlich machen soll. Die ist in ihrer jetzigen Form 
auch schlicht Unsinn.

von monkeydluffy (Gast)


Lesenswert?

Hallo nochmal.

Okay, ich habe für das Empfangen und Übertragung von Byte drei ISR 
verwendet.

Der ISR(USART0_RX_vect) dient zum empfangen von Byte mit dem Puffer UDRO 
über USART0.

Der ISR(USART1_UDRE_vect) dient zum Transfer der Daten von UDRO in UDR1.

Der ISR(USART1_TX_vect) dient zum komplet Transfer von Daten über 
USART1.

Die Funktion usart_Transfer sendet die Daten Byteweise zu zwei 
Motorsteuerungen

Die Routine der While-Schleife des Main-Programms dient zur Übertragung 
von Byte zu zwei Motorsteuerungen.

Die Beide Motorsteuerung verstehen ein bestimmtes Protokoll und können 
nur Daten in Form von ASCii-Zeichen übernehmen.

Beispiel ---> "#2A\r"
Bedeutung: # ----> Anfang der Anweisung
           2 ----> Nummer des Motors
           A ----> Befehl zu erledigen(Motor anfahren)
          \r ----> Ende der Anweisung

Ich hoffe meine Informationen sind genug für euch. Ich warte auf ihre 
Rückmeldung.

Danke im Voraus.

von Karl H. (kbuchegg)


Lesenswert?

Eine Frage noch:

Hat der µC neben der UART-Splittung noch etwas anderes zu tun?

Wenn 'nein' ist mir nämlich nicht wirklich klar, was du dir von einer 
Interrupt-Steuerung versprichst. Das verkompliziert insbesondere das 
Senden enorm.

von Karl H. (kbuchegg)


Lesenswert?

Irgendwie ist mir da in deinem Programm einiges nicht klar.

Da hast 3 UART Partner. Den PC und die beiden Motorsteuerungen.

Von der USART 0   benutzt du nur den Empfänger
Von der USART 1   benutzt du nur den Sender

das sind 2 Anschlüsse. Wo hängt der 3.te Gesprächspartner drann?

von monkeydluffy (Gast)


Lesenswert?

Hallo,


>>Irgendwie ist mir da in deinem Programm einiges nicht klar.

>>Da hast 3 UART Partner. Den PC und die beiden Motorsteuerungen.

>>Von der USART 0   benutzt du nur den Empfänger
>>Von der USART 1   benutzt du nur den Sender

>>das sind 2 Anschlüsse. Wo hängt der 3.te Gesprächspartner drann?

Es gibt kein dritter Anschluss.

Erster Anschluss: Der Mikrocontroller ist mit der seriellen Port RS232 
des PCs über USART0 verbunden.

Zweiter Anschluss: Der Mikrocontroller ist mit den seriellen Port RS485 
von zwei identischen Motorsteuerungen verbunden. Jede Motorsteuerung 
besitzt ein Anschluss RS485.

Für die Kommunikation zwischen dem Mikrocontroller und den beiden 
Motorsteuerungen habe ich ein Kabel angefertigt, so dass ein Ende des 
kabels ein Anschluss RS485 besitzt und das andere Ende des Kabels hat 
zwei Anschlüsse RS485. Das Ende des Kabels mit einem Anschluss RS485 
wird mit dem USART1 des Mikrocontrollers verbunden. Das andere Ende des 
Kabels, das zwei Anschlüsse RS485 besitzt, wird mit den beiden 
Motorsteuerungen verbunden.

USART0 (RS232): Kommunikation zwischen Mikrocontroller und PC

USART1(RS485): Kommunikation zwischen  Mikrocontroller und beiden 
Motorsteuerungen.

von amateur (Gast)


Lesenswert?

RS485 ein differentieller Sender an zwei differentielle Empfänger.
Geht das eigentlich elektrisch?

von Luffy M. (monkeydluffy)


Lesenswert?

Moin,

>>RS485 ein differentieller Sender an zwei differentielle Empfänger.
>>Geht das eigentlich elektrisch?

 Ich denke, es ist elektrisch machbar.

von Luffy M. (monkeydluffy)


Lesenswert?

Moin,

Karl Heinz Buchegger schrieb:

> Schrittweise vorgehen.

> Erstmal alles ohne Interrupt.

> Dann den Empfangsinterrupt in Betrieb nehmen und das Senden an die
> andere USART weiterhin konventionell erledigen.

> Und erst dann den 2.ten Interrupt zum Senden dazu nehmen.

Ich versuche Momentan schrittweise vorzugehen. Ich habe erstmal meine 
Routine ohne Interrupte geschrieben. Aber ich komme nicht klar, wenn ich 
in die while-Schleife des Main-Programms die Daten von USART0 zum USART1 
übertrage.

Das main Programm lautet :
1
int main (void)
2
{ 
3
  
4
  USART_Init(UBRR_VAL) ;  // Initializierung USART0 und USART1 
5
  char c ;
6
while(1)
7
{
8
   c = USART0_Receive();
9
   USART1_Receive() = c ;
10
   usart_Transfer("#3sc\r"); // Sendung der Fahrstrecke
11
   usart_Transfer("#3A\r"); // Motor anfahren
12
     
13
}
14
}

Ich kriege folende Fehlermeldung :

../test_ohne_interrupt.c:104: error: lvalue required as left operand of 
assignment

Wie kann ich besser die Daten von USART0 zum USART1 ohne Interrupt 
senden.

Danke im Voraus.

von Karl H. (kbuchegg)


Lesenswert?

Luffy Monkey schrieb:

> Das main Programm lautet :
> [c]
> int main (void)
> {
>
>   USART_Init(UBRR_VAL) ;  // Initializierung USART0 und USART1
>   char c ;
> while(1)
> {
>    c = USART0_Receive();
>    USART1_Receive() = c ;

Schlimmer als ich dachte.
Du musst erst mal C lernen.

     USART1_Transmit( c );

von enblat (Gast)


Lesenswert?

>   USART1_Receive() = c ;


Was soll diese Zeile machen?

Du braucht ein C-bUch. Dringend.

von Luffy M. (monkeydluffy)


Lesenswert?

Hi,


> Schlimmer als ich dachte.
> Du musst erst mal C lernen.


>   USART1_Receive() = c ;

> Was soll diese Zeile machen?

> Du braucht ein C-bUch. Dringend.


Sorry für meine blöde Frage. Man lernt durch Fehler. Ich habe sogar 
schon die Sprache c für Anfänger gelesen und mir ist nicht aufgeffallen, 
dass ich eine dumme Frage gestellt habe.

Danke für die schnelle Antwort.

von Karl H. (kbuchegg)


Lesenswert?

Luffy Monkey schrieb:
> Hi,
>
>
>> Schlimmer als ich dachte.
>> Du musst erst mal C lernen.
>
>
>>   USART1_Receive() = c ;
>
>> Was soll diese Zeile machen?
>
>> Du braucht ein C-bUch. Dringend.
>
>
> Sorry für meine blöde Frage. Man lernt durch Fehler.

Im Prinzip: ja
In diesem konkreten Fall: nein

Der Fehler zeigt, dass dir grundlegende Kentnisse fehlen, wie 
Programmiersprachen (zumindest C) grundsätzlich funktionieren.

> Ich habe sogar
> schon die Sprache c für Anfänger gelesen und mir ist nicht aufgeffallen,
> dass ich eine dumme Frage gestellt habe.

Das ist das eigentlich bedenkliche!

Durchlesen reicht nicht. Du musst das Buch durcharbeiten! Die Übungen 
machen. Es hat keinen Sinn, den Eifelturm bauen zu wollen, wenn die 
einfachsten physikalischen Prinzipien, wie die Newtonschen Gesetze, 
nicht klar sind. Alles weitere, was auch noch notwendig ist um den Turm 
bauen zu können, baut auf diesen Fundamenten auf. Die MÜSSEN sitzen. Im 
Schlaf!

Und daher: Ja, man lernt durch seine Fehler.
Aber es gibt Fehler, die dürfen eigentlich nicht passieren. Deiner ist 
von dieser Kategorie. So etwas DARF für einen Lernenden, der noch dazu 
ein Lehrbuch gelesen hat, kein Problem sein. Wenn doch, dann ist da 
etwas grundlegend schief gegangen und man fragt sich: was von den 
absoluten Grundlagen (die mit den minimalsten Anforderungen an Können) 
beherrscht er ausserdem noch nicht?

von Luffy M. (monkeydluffy)


Lesenswert?

Hi,

Meine Routine ohne interrupt läuft einwandfreie.

Das Programm lautet:
1
#include <avr/io.h>
2
#include <avr/interrupt.h> 
3
#include <stdio.h>
4
#include <stdlib.h>
5
#include <string.h>
6
7
#define FOSC 8000000       // Clock Speed
8
#define BAUD 115200UL
9
// Berechnungen zur Baudrate:
10
#define UBRR_VAL ((FOSC+BAUD*8)/(BAUD*16)-1)   // clever runden
11
12
  
13
/* USART Initialization */
14
void USART_Init (unsigned int ubrr)
15
{
16
   /* Set baud rate */
17
  UBRR0H = (unsigned char)(ubrr>>8);
18
  UBRR0L = (unsigned char) ubrr;
19
  UBRR1H = (unsigned char)(ubrr>>8);
20
  UBRR1L = (unsigned char) ubrr;
21
  
22
/* Enable receiver and transmitter and set frame format: 8data, 2stop bit  */
23
24
  UCSR0B = (1<<RXEN0)|(0<<TXEN0)|(0<<RXCIE0)|(0<<TXCIE0)|(0<<UCSZ02);
25
  UCSR0C = (1<<USBS0) | (1<<UCSZ01)|(1<<UCSZ00);
26
27
  UCSR1B = (0<<RXEN1)|(1<<TXEN1)|(0<<RXCIE1)|(0<<TXCIE1)|(0<<UCSZ12);
28
  UCSR1C = (1<<USBS1) |(1<<UCSZ11)|(1<<UCSZ10);
29
  
30
}
31
32
/*-- function receive USART0 --*/
33
34
unsigned char USART0_Receive (void)
35
{
36
    while(!(UCSR0A & (1<<RXC0)) ); // Wait for data to be received 
37
    
38
     return UDR0; // Get and return received data from buffer 
39
}
40
41
/*-- function transmit USART1 --*/  
42
43
void USART1_Transmit (unsigned char data1)
44
{
45
  while ( !(UCSR1A & (1<<UDRE1)) ); // Wait for empty transmit buffer  
46
    
47
   UDR1 = data1; // Put data into buffer, sends the data  
48
}
49
50
int main (void)
51
52
{ 
53
  USART_Init(UBRR_VAL) ;  // Initializierung USART0 und USART1 
54
  char c ;
55
56
  while(1)
57
58
  {
59
        c = USART0_Receive();
60
        USART1_Transmit(c);  
61
  }
62
}

Die Routine mit Empfangsinterrupt lautet:
1
/* USART Initialization */
2
void USART_Init (unsigned int ubrr)
3
{
4
   /* Set baud rate */
5
  UBRR0H = (unsigned char)(ubrr>>8);
6
  UBRR0L = (unsigned char) ubrr;
7
  UBRR1H = (unsigned char)(ubrr>>8);
8
  UBRR1L = (unsigned char) ubrr;
9
  
10
/* Enable receiver and transmitter and set frame format: 8data, 2stop bit  */
11
12
  UCSR0B = (1<<RXEN0)|(0<<TXEN0)|(1<<RXCIE0)|(0<<TXCIE0)|(0<<UCSZ02);
13
  UCSR0C = (1<<USBS0) | (1<<UCSZ01)|(1<<UCSZ00);
14
15
  UCSR1B = (0<<RXEN1)|(1<<TXEN1)|(0<<RXCIE1)|(0<<TXCIE1)|(0<<UCSZ12);
16
  UCSR1C = (1<<USBS1) |(1<<UCSZ11)|(1<<UCSZ10);
17
  
18
}
19
20
/*-- function receive USART0 --*/
21
22
unsigned char USART0_Receive (void)
23
{
24
    while(!(UCSR0A & (1<<RXC0)) ); // Wait for data to be received 
25
    
26
     return UDR0; // Get and return received data from buffer 
27
}
28
29
/*-- function transmit USART1 --*/  
30
void USART1_Transmit (unsigned char data1)
31
{
32
  while ( !(UCSR1A & (1<<UDRE1)) ); // Wait for empty transmit buffer  
33
    
34
   UDR1 = data1; // Put data into buffer, sends the data  
35
}
36
 
37
38
ISR(USART0_RX_vect)
39
{
40
  char data;
41
  data = UDR0; // Byte lesen
42
//  UDR0 = data ; // echo
43
  UDR1 = data; // Byte schreiben
44
}
45
46
int main (void)
47
48
{ 
49
  USART_Init(UBRR_VAL) ;  // Initializierung USART0 und USART1 
50
  sei();
51
 
52
  while(1)
53
54
  {
55
      
56
  }
57
58
}

>>  Karl Heinz Buchegger schrieb:

> Und erst dann den 2.ten Interrupt zum Senden dazu nehmen.

Meinst du in diesem Fall ISR(USART1_UDRE_vect) ?

mfg

von Karl H. (kbuchegg)


Lesenswert?

1
ISR(USART0_RX_vect)
2
{
3
  char data;
4
  data = UDR0; // Byte lesen
5
//  UDR0 = data ; // echo
6
  UDR1 = data; // Byte schreiben
7
}

No.

Du hast an dieser Stelle keine Garantie dafür, dass du das Zeichen 
welches du aus UDR0 geholt hast auch in die UDR1 reinstopfen darfst.

Bei dir funktioniert das momentan, weil die beiden UART mehr oder 
weniger synchron laufen. Aber spätestens dann, wenn die beiden 
unterschiedliche Baudraten haben und der µC ein paar Zeichen 
zwischenspeichern muss, gibt das Probleme.


Keine Annahmen treffen, die nicht unbedingt notwendig sind!
Die Annahme hier lautet: Die UART1 ist zur Übertragung bereit.
Und diese Annahme ist durch nichts gerechtfertigt. Die Annahme muss man 
auch nicht treffen:
Der Empfangsinterrupt schreibt sein Zeichen in eine FIFO und in der 
Hauptschleife werden die Zeichen in der FIFO sukzessive über die UART1 
ausgegeben, SOFERN die UART1 zur Ausgabe bereit ist.

Das Programm hat in der Hauptschleife sowieso nichts zu tun. Da kann es 
genausogut in der FIFO nachsehen, ob vielleicht ein oder mehrere Zeichen 
zur Ausgabe bereit sind, ob die UART1 zur Ausgabe bereit ist und wenn 
beides der Fall ist, wird dann 1 Zeichen über die UART1 ausgebeben. Und 
im nächsten Durchlauf durch die Hauptschleife das nächste usw. usw. bis 
die FIFO abgearbeitet wurde und wieder leer ist.

von Luffy M. (monkeydluffy)


Lesenswert?

Hallo Karl Heinz Buchegger,

ich habe vesucht, deine Anweisung zu befolgen und habe folger Code 
entwickelt:
1
 #define usart_buffer_size 10
2
 uint8_t counter = 0;
3
 char usart0_rx_buffer[usart_buffer_size];
4
5
6
/* USART Initialization */
7
void USART_Init (unsigned int ubrr)
8
{
9
   /* Set baud rate */
10
  UBRR0H = (unsigned char)(ubrr>>8);
11
  UBRR0L = (unsigned char) ubrr;
12
  UBRR1H = (unsigned char)(ubrr>>8);
13
  UBRR1L = (unsigned char) ubrr;
14
  
15
/* Enable receiver and transmitter and set frame format: 8data, 2stop bit  */
16
17
  UCSR0B = (1<<RXEN0)|(0<<TXEN0)|(1<<RXCIE0)|(0<<TXCIE0)|(0<<UCSZ02);
18
  UCSR0C = (1<<USBS0) | (1<<UCSZ01)|(1<<UCSZ00);
19
20
  UCSR1B = (0<<RXEN1)|(1<<TXEN1)|(0<<RXCIE1)|(0<<TXCIE1)|(0<<UCSZ12);
21
  UCSR1C = (1<<USBS1) |(1<<UCSZ11)|(1<<UCSZ10);
22
  
23
}
24
25
/*-- function receive USART0 --*/
26
27
unsigned char USART0_Receive (void)
28
{
29
    while(!(UCSR0A & (1<<RXC0)) ); // Wait for data to be received 
30
    
31
     return UDR0; // Get and return received data from buffer 
32
}
33
34
/*-- function transmit USART1 --*/  
35
36
void USART1_Transmit (unsigned char data1)
37
{
38
  while ( !(UCSR1A & (1<<UDRE1)) ); // Wait for empty transmit buffer  
39
    
40
   UDR1 = data1; // Put data into buffer, sends the data  
41
}
42
43
ISR(USART0_RX_vect)
44
{
45
  char data;
46
  data = UDR0; // Byte lesen
47
 
48
 if (counter < usart_buffer_size)
49
 {
50
  usart0_rx_buffer[counter] = data;
51
 
52
53
 // USART1 zur Ausgabe in Bereitschaft
54
  while ( !(UCSR1A & (1<<UDRE1)) ); 
55
  USART1_Transmit(usart0_rx_buffer[counter]);
56
  counter++;
57
 }
58
}
59
60
int main (void)
61
62
{  
63
  USART_Init(UBRR_VAL) ;  // Initializierung USART0 und USART1 
64
  sei();
65
  while(1)
66
  {     
67
  }
68
}

ich habe das obene Programm getestet und es läuft einwandfrei.
Gibt es noch was auf meine Seite zu verbessern ?

mfg

von Karl H. (kbuchegg)


Lesenswert?

Luffy Monkey schrieb:
> Hallo Karl Heinz Buchegger,
>
> ich habe vesucht, deine Anweisung zu befolgen

Du hast es aber nicht gemacht.
1
ISR(USART0_RX_vect)
2
{
3
  char data;
4
  data = UDR0; // Byte lesen
5
 
6
 if (counter < usart_buffer_size)
7
 {
8
  usart0_rx_buffer[counter] = data;
9
 
10
11
 // USART1 zur Ausgabe in Bereitschaft
12
  while ( !(UCSR1A & (1<<UDRE1)) ); 
13
  USART1_Transmit(usart0_rx_buffer[counter]);
14
  counter++;
15
 }
16
}

genau das willst du nämlich nicht machen!
Du willst nicht in der ISR für Empfang darauf warten, dass die andere 
UART zum Senden frei wird! Genau darum geht es. Die beiden sollen 
unabhängig voneinander sein.

Ich skizziere mal
1
ISR(USART0_RX_vect)
2
{
3
  char data;
4
  data = UDR0; // Byte lesen
5
6
  data in den Puffer stellen
7
}
8
9
10
....
11
12
13
int main()
14
{
15
   ....
16
17
18
  while( 1 )
19
  {
20
21
     if( Zeichen sind im Puffer  &&
22
         UCSR1A & (1<<UDRE1) )  // die UART1 kann ein Zeichen ausgeben
23
     {
24
       USART1_Transmit( das Zeichen aus dem Puffer );
25
     }
26
  }
27
}

Das Senden erfolgt NICHT mittels Interrupt (zumindest jetzt noch nicht).

Nur so ist sichergestellt, dass von der UART0 Zeichen kommen können (bis 
der Puffer voll ist) selbst dann, wenn die UART1 nicht bereit ist. Die 
ISR stellt sie einfach in den Puffer. Mehr nicht.
Und in der Hauptschleife wird der Puffer sukzessive abgearbeitet. 
Zeichen für Zeichen. Solange bis er wieder leer ist.

von Luffy M. (monkeydluffy)


Lesenswert?

Hallo,

ich hoffe, ich habe deine Erklärungen jetzt kapiert. Ich habe die Lücke 
ergänzt und der Code lautet :
1
ISR(USART0_RX_vect)
2
{
3
  char data;
4
  data = UDR0; // Byte lesen
5
  UDR1 = data; // Byte schreiben
6
}
7
8
int main (void)
9
10
{ 
11
  
12
  USART_Init(UBRR_VAL) ;  // Initializierung USART0 und USART1 
13
  sei();
14
  char c ;
15
  
16
17
  while(1)
18
  {
19
   c = USART1_Receive();
20
21
   if( (UCSR1A & (1<<RXC1))  &&
22
        (UCSR1A & (1<<UDRE1)) )  // die UART1 kann ein Zeichen ausgeben
23
     {
24
       USART1_Transmit(c);
25
     }      
26
  }
27
28
}

Stimmt jetzt ??

mfg

von Karl H. (kbuchegg)


Lesenswert?

> ISR(USART0_RX_vect)
> {
>   char data;
>   data = UDR0; // Byte lesen
>   UDR1 = data; // Byte schreiben
> }


Das sendet ja schon wieder das Zeichen sofort ohne Rücksicht auf 
Verluste.


Ich gebs auf.
Programmieren durch "vorhandenen fremden Code solange zusammenwürfeln, 
bis da irgendwas rauskommt" funktioniert nun mal nicht.

von Luffy M. (monkeydluffy)


Lesenswert?

Moin  Karl Heinz Buchegger,

> Ich gebs auf.

Bitte, gebe nicht auf. Ich habe der Wille, um zu verstehen, was ich 
falsch mache. Glaubt mir, ich bemühe mich, um deine Anweisungen zu 
befolgen. Aber ich enttäusche dich jedesmal mit schlechter Antwort. 
Sorry.


> ISR(USART0_RX_vect)
> {
>   char data;
>   data = UDR0; // Byte lesen
>   UDR1 = data; // Byte schreiben
> }

> Das sendet ja schon wieder das Zeichen sofort ohne Rücksicht auf
> Verluste.

Du meinst hier bestimmt der Datenverlust !!!

Wie kann ich die Verluste vermeiden ? ich bin verwirrt und möchte ganz 
herzlich vorankommen.

von Florian H. (heeen)


Lesenswert?

Was du möchtest, nennt sich Ring-Puffer oder abstrakter: Queue oder 
Warteschlange.
Das heisst es gibt eine Stelle an der sich neue Zeichen anreihen um aufs 
gesendet werden warten, und eine stelle an der Zeichen abgeholt werden 
um gesendet zu werden.
pseudocode:
1
#define BUF_LEN 64
2
short readpos=0;
3
short writepos=0;
4
5
char buffer[BUF_LEN];
6
7
onRead() {
8
  char c= UDR0;
9
10
  if(ready to write) {
11
    UDR1=c;
12
  } else {
13
    buffer[writepos]=c;
14
    writepos++;
15
    if(writepos==BUF_LEN)
16
      writepos=0;
17
  }
18
}
19
20
onWriteReady() {
21
  if(readpos != writepos) { //es gibt etwas zu schreiben
22
    UDR1=buffer[readpos];
23
    readpos++;
24
    if(readpos==BUF_LEN)
25
      readpos=0;
26
  }
27
}

Ich behandle hier nicht den Fall wenn der Ringpuffer überläuft, also 
wenn die writepos die readpos überholt.

von Frank M. (ukw) (Moderator) Benutzerseite


Lesenswert?

Karl Heinz Buchegger schrieb:
> Nur so ist sichergestellt, dass von der UART0 Zeichen kommen können (bis
> der Puffer voll ist) selbst dann, wenn die UART1 nicht bereit ist. Die
> ISR stellt sie einfach in den Puffer. Mehr nicht.
> Und in der Hauptschleife wird der Puffer sukzessive abgearbeitet.
> Zeichen für Zeichen. Solange bis er wieder leer ist.

Muss man gar nicht in der Hauptschleife machen, sondern:

ISR(USART0_RX_vect)
   schreibt das von UART0 empfangene Zeichen in einen Ringbuffer

ISR(USART1_TXCIE_vect)
   liest das Zeichen aus dem Ringbuffer und schreibt es in den UART1,
   denn hier ist garantiert, dass der UART1 zum Senden frei ist.

Damit tauschen sich beide ISRs über einen gemeinsamen Ringbuffer aus. 
Die Hauptschleife kann sich derweil um andere Dinge kümmern, z.B. ein 
paar LEDs effektvoll blinken lassen oder was weiß ich ;-)

von monkeydluffy (Gast)


Lesenswert?

Hallo nochmal,

Danke  Frank und Florian für eure Rückmeldungen.

Ziel: Übertragung der empfangenen Daten von USART0 zum USART1 mit 
Verwendung von Empfangsinterrupt ISR(USART0_RX_vect).

Monkeydluffy schrieb:

> ISR(USART0_RX_vect)
> {
>   char data;
>   data = UDR0; // Byte lesen
>   UDR1 = data; // Byte schreiben
> }

Karl meinte:

> Das sendet ja schon wieder das Zeichen sofort ohne Rücksicht auf
> Verluste.

Was wäre dann, wenn ich meine Routine folgendermaße umstelle:
1
ISR(USART0_RX_vect)
2
{
3
  char data ;
4
  data = UDR0;
5
6
  if(!(UCSR1A & (1<<UDRE1))) // ready to write
7
  {
8
    UDR1 = data;
9
   }
10
}
11
12
int main (void)
13
{  
14
  USART_Init(UBRR_VAL) ;  // Initializierung USART0 und USART1 
15
  sei();
16
  char c ;
17
  while(1)
18
  {
19
   c = USART1_Receive();
20
   if( (UCSR1A & (1<<RXC1))  &&
21
        (UCSR1A & (1<<UDRE1)) )  // die UART1 kann ein Zeichen ausgeben
22
     {
23
       USART1_Transmit(c);
24
     }      
25
  }
26
}

Stimmt jetzt der Code ?

mfg

von Florian H. (heeen)


Lesenswert?

nein, schau dir nochmal den beispielcode an den ich geschrieben hab und 
versuche ihn zu verstehen.

in der ISR:
was passiert mit data wenn nicht ready to write ist? es wird einfach 
verworfen -> datenverlust.
in main:
das gleiche, wenn nicht ready to write ist, wird das zeichen 
verlorengehen.

von Karl H. (kbuchegg)


Lesenswert?

Frank M. schrieb:

> Muss man gar nicht in der Hauptschleife machen, sondern:
...
> ISR(USART1_TXCIE_vect)
>    liest das Zeichen aus dem Ringbuffer und schreibt es in den UART1,
>    denn hier ist garantiert, dass der UART1 zum Senden frei ist.
>
> Damit tauschen sich beide ISRs über einen gemeinsamen Ringbuffer aus.

Die Interrupt Steuerung, die er dafür braucht, ist noch viel zu 
kompliziert für ihn. Daher hätte ich ihn die Ausgabe in der 
Hauptschleife machen lassen. Das kostet auch in der Hauptschleife nicht 
viel und ist für ihn noch handhabbar.

(Mit dem TXCIE Vektor wirds unangenehm, weil der ja nicht immer kommt, 
bzw. weil da erste Zeichen ja konventionell auf den Weg gebracht werden 
muss. Sagt ja keine, dass bei einem Transmit Complete schon das nächste 
Zeichen zum Senden bereit steht.)

> > Ich gebs auf.
> Bitte, gebe nicht auf.

Doch tu ich. Die einzige Möglichkeit dir noch weiter zu helfen ist es, 
dir den Code zu schreiben. Und das will ich nicht.

von Luffy M. (monkeydluffy)


Lesenswert?

Moin ,

>>Karl Heinz Buchegger schrieb:

>Doch tu ich. Die einzige Möglichkeit dir noch weiter zu helfen ist es,
>dir den Code zu schreiben. Und das will ich nicht.

Ich weiße nicht mehr, was ich dir sagen kann, um dich meine Bemühungen 
zu beweisen. Aber ich gebe nicht auf und bin bereit meine Schwäche zu 
beseitigen.

Ich habe was gemacht, aber es läuft nicht. Irgendwie habe ich wieder 
etwas nicht berücksichtgt. Kann jemand mir darüber was sagen.

Ich bitte um eure Hilfe.

Danke im Voraus.

Der code lautet:
1
char buffer[buffer_size];
2
uint8_t writepos = 0;
3
uint8_t readpos = 0;
4
5
ISR(USART0_RX_vect)
6
{
7
  char data;
8
  data = UDR0; // read Byte
9
10
   if(!(UCSR1A & (1<<UDRE1))) // ready to write
11
  {
12
    UDR1 = data; // write data
13
   }
14
   else // if not ready to write
15
   {
16
     if(writepos < buffer_size)
17
       buffer[writepos] = data; // write into circular buffer
18
        writepos++;
19
      if(writepos == buffer_size)
20
        writepos = 0;
21
    }
22
}
23
24
int main (void)
25
26
{ 
27
  
28
  
29
  USART_Init(UBRR_VAL) ;  // Initializierung USART0 und USART1 
30
  sei();
31
 
32
  
33
34
  while(1)
35
36
  {
37
    char c ;
38
  c = USART1_Receive();
39
40
   if( (UCSR1A & (1<<RXC1))  &&
41
         (UCSR1A & (1<<UDRE1)) )  // die UART1 kann ein Zeichen ausgeben
42
     {
43
       USART1_Transmit(c);
44
     }
45
   
46
  else
47
     {
48
       USART1_Transmit(buffer[writepos]);
49
        writepos++;
50
     }
51
 }
52
}

von Frank M. (ukw) (Moderator) Benutzerseite


Lesenswert?

Luffy Monkey schrieb:
>
1
> ISR(USART0_RX_vect)
2
> {
3
>     ....
4
>     UDR1 = data; // write data
5
>

Hier sendest Du aus der ISR raus (wo Dir Karl Heinz schon seit 
Ewigkeiten vorbetet, dass Du bei auch nur bei knapp unterschiedlichen 
Empfangs- und Sende-Geschwindigkeiten sofort ein (Überlauf-)Problem 
bekommst)....

Und hier sendest Du nochmal aus der main-Funktion raus:

>
1
> int main (void)
2
> {
3
>   ...
4
>        USART1_Transmit(c);
5
>

Diesen Faux-Pas hast Du Dir jetzt schon 3 mal geleistet durch mehrfaches 
Zeigen Deines Codes - seitdem Dir Karl Heinz gesagt hast, Du sollst das 
Senden aus der ISR tunlichst unterlassen.

Was soll man also mit Dir machen? Karl Heinz hat Dir mehrfach 
Alternativen vorgestellt. Statt sie zu nutzen, hältst Du an dem Senden 
aus der ISR fest.

von Karl H. (kbuchegg)


Lesenswert?

Frank M. schrieb:
> Luffy Monkey schrieb:
>>
1
>> ISR(USART0_RX_vect)
2
>> {
3
>>     ....
4
>>     UDR1 = data; // write data
5
>>
>
> Hier sendest Du aus der ISR raus (wo Dir Karl Heinz schon seit
> Ewigkeiten vorbetet, dass Du bei auch nur bei knapp unterschiedlichen
> Empfangs- und Sende-Geschwindigkeiten sofort ein (Überlauf-)Problem
> bekommst)....

Ich glaub er versteht gar nicht, dass die reine Zuweisung an das UDR 
Register das Senden auslöst.

Das andere Problem besteht darin, dass er keine vernünftigen FIFO 
Routinen hat.

von Frank M. (ukw) (Moderator) Benutzerseite


Lesenswert?

Karl Heinz Buchegger schrieb:
> Ich glaub er versteht gar nicht, dass die reine Zuweisung an das UDR
> Register das Senden auslöst.

Ja, er versteht das wohl als Umsetzung Deiner "Skizze" (s.o):
1
ISR(USART0_RX_vect)
2
{
3
  char data;
4
  data = UDR0; // Byte lesen
5
6
  data in den Puffer stellen
7
}

"Puffer" ist für Ihn wohl UDR1 ;-)

> Das andere Problem besteht darin, dass er keine vernünftigen FIFO
> Routinen hat.

Doch, er hat ein FIFO mit der Länge 1 ;-)))
"Vernünftig" ist da jedoch was anderes...

von Luffy M. (monkeydluffy)


Lesenswert?

Hallo,

>> Frank M. schrieb:

>Was soll man also mit Dir machen? Karl Heinz hat Dir mehrfach
>Alternativen vorgestellt. Statt sie zu nutzen, hältst Du an dem Senden
>aus der ISR fest.

Danke schön für deine Mitteilung. Ich glaube, ich habe endlich 
verstanden, was ich falsch mache.

>> Karl Heinz Buchegger schrieb:

>Ich glaub er versteht gar nicht, dass die reine Zuweisung an das UDR
>Register das Senden auslöst.

Ganz genau, ich wusste das nicht und ich habe trotz eurer Erklärungen 
dieser Fehler nicht korrigiert. Ich verstehe jetzt, warum Karl Heinz 
Buchegger aufgegeben hat. Sorry nochmal.

>> Frank M. schrieb:

>Ja, er versteht das wohl als Umsetzung Deiner "Skizze" (s.o):

>ISR(USART0_RX_vect)
>{
>  char data;
>  data = UDR0; // Byte lesen

>  data in den Puffer stellen
>}

>"Puffer" ist für Ihn wohl UDR1 ;-)

Genau ... In meinem Kopf war UDR1 = puffer. Jetzt weiße ich, dass Karl 
Heinz ein anderer puffer als Zwischenspeicher meinte.

Ich habe der Code geändernt und er sieht folgendermaße:
1
ISR(USART0_RX_vect)
2
{
3
  char data;
4
  data = UDR0; // read Byte
5
6
 if (counter < buffer_size)
7
   {
8
     buffer[counter1] = data; // write into buffer
9
    counter++;
10
   }
11
 else
12
    UCSR1B = (0<<RXEN0)|(0<<RXCIE0); // Stop receive byte
13
    
14
}
15
16
int main (void)
17
18
{ 
19
  
20
  USART_Init(UBRR_VAL) ;  // Initializierung USART0 und USART1 
21
  RS485_Init (); // Serieller Port USART1
22
  sei();
23
  unsigned int counter1 = 0;
24
  char c ;
25
  while(1)
26
27
  {
28
     UDR1 = buffer[counter1]; // write into Buffer UDR1  
29
     c = USART1_Receive() ;
30
      
31
   if( (counter1 < buffer_size)  &&
32
         (UCSR1A & (1<<UDRE1)) )  // die UART1 kann ein Zeichen ausgeben
33
     {
34
       USART1_Transmit(c);
35
     }
36
   
37
   else
38
     {
39
        UCSR1B =(0<<TXEN1) ; 
40
     }  
41
       
42
        counter1++;      
43
  }
44
45
}

Ich hoffe diesmal, dass ich keine Scheiße gebaut habe. Hat zumindest 
jetzt mein aktueller Code ein Sinn?


danke im Voraus.

von Luffy M. (monkeydluffy)


Lesenswert?

Korrektur (falscher Register UCSR0B statt UCSR1B) :
1
ISR(USART0_RX_vect)
2
{
3
  char data;
4
  data = UDR0; // read Byte
5
6
 if (counter < buffer_size)
7
   {
8
     buffer[counter1] = data; // write into buffer
9
    counter++;
10
   }
11
 else
12
    UCSR0B = (0<<RXEN0)|(0<<RXCIE0); // Stop receive byte from USART0
13
    
14
}

mfg

von Karl H. (kbuchegg)


Lesenswert?

Luffy Monkey schrieb:

> Genau ... In meinem Kopf war UDR1 = puffer. Jetzt weiße ich, dass Karl
> Heinz ein anderer puffer als Zwischenspeicher meinte.

Genau.
Eine FIFO, und zwar eine richtige FIFO! Zb Ausgeführt als Ringbuffer-
Das was du da hast ist nämlich keine. Bei dir ist es nicht möglich, 
wahlfrei Zeichen in die Datenstruktur reinzustellen bzw. rauszuholen. 
Und zwar nicht nur streng abwechseln lesend und schreibend sondern 
beliebig zwischen reinstellen und rauslesen wechselnd.



 while(1)

  {
     UDR1 = buffer[counter1]; // write into Buffer UDR1

Du kannst doch nicht einfach so tun, als ob da irgendwelche Zeichen im 
buffer sind! Da können welche sein oder auch nicht. Je nachdem, ob im 
der ISR(USART0_RX_vect) welche reingestellt wurden oder nicht.

     c = USART1_Receive() ;

Tu dir selbst einen Gefallen und lass den 'Rückkanal' von USART 1 erst 
mal aussen vor. Du hast auch ohne dieses Detail noch massive Probleme!


> Hat zumindest jetzt mein aktueller Code ein Sinn?

Nein.
Ich weiß nicht, warum du das machen musst. Aber eigentlich müsste man 
sagen: Gib die Aufgabe an jemanden ab, der wenigstens die einfachsten 
Grundzüge der Programmierung beherrscht. Bei dir fehlt es an allen Ecken 
und Enden (und zwar schon beginnend bei den Grundlagen). Man muss es 
einfach so sehen: Programmieren ist nicht dein Ding. Das liegt dir 
nicht. Ist kein Problem und ist auch keine Schande. Aber für dich ist es 
eine Qual und jemand anderer hat das in 15 Minuten geschrieben.

von Frank M. (ukw) (Moderator) Benutzerseite


Lesenswert?

Okay, jetzt hast Du einen Buffer, in dem Du das empfangene Zeichen in 
der ISR ablegst. Schon besser.

Aber das was da in der main-Funktion steht, ist immer noch Unsinn.

UDR1 = buffer[counter1]; // write into Buffer UDR1

Hier sendest Du das gespeicherte Zeichen bereits raus! Die Zuweisung 
sorgt dafür! Hast Du denn geprüft, ob der UART zum Senden schon wieder 
frei ist? NEIN!

     c = USART1_Receive() ;

Warum liest Du das was vom UART1??? Ich dachte Du wolltest von UART0 
lesen und auf UART1 schreiben. Was ist hier c? Ich dachte, das Zeichen 
steht in Deinem Buffer, den Du in der ISR gefüllt hast?

     USART1_Transmit(c);

Ach, hier sendest Du. Komischerweise nicht das, was Du im Deinem Buffer 
hast, sondern das, was Du vom UART1(!) plötzlich liest.

     counter1++;

Dein counter wird größer und größer....

So funktioniert kein FIFO. Ein FIFO heisst "First IN, First OUT". Zudem 
brauchst Du noch einen speziellen FIFO, da Dein Speicher endlich ist, 
nämlich einen Ringbuffer, wo sich die Katze in den Schwanz beissen kann. 
Dafür brauchst Du 2 Zählervariablen, die den Anfang und das Ende Deines 
Ringbuffers speichern.

Du rotzt hier einfach auf "Gut Glück" irgendeinen Code hin, der so 
überhaupt nicht funktionieren kann. Lies Dich erstmal in die Materie ein 
(im Speziellen Ringbuffer), dann nimm ein Stück Papier und einen 
Bleistift und versuche, das Szenario auf dem Blatt Papier 
durchzuspielen. Überlege, wie Dein Buffer organisiert ist und wie/wann 
Du Anfang- und Endemarkierung änderst und wann (trotzdem immer noch) ein 
Buffer-Overflow auftreten kann.

So einfach ist das nicht. Du bist Lichtjahre von einer Lösung entfernt.

von Luffy M. (monkeydluffy)


Lesenswert?

>> Karl Heinz Buchegger schrieb;

>Man muss es einfach so sehen: Programmieren ist nicht dein Ding.Das liegt

>dir nicht. Ist kein Problem und ist auch keine Schande. Aber für dich ist

>es eine Qual und jemand anderer hat das in 15 Minuten geschrieben.

Ich weiße, dass meine Kenntnisse in Programmierung und Mikrocontroller 
sehr schwach sind. Aber ich möchte  mich unbedingt verbessern und werde 
trotzdem nicht aufgeben. Trotzdem danke für deine ausfürliche 
Erklärungen.

>> Frank M. schrieb:

> So einfach ist das nicht. Du bist Lichtjahre von einer Lösung entfernt.

Das stimmt, ich war und bin wahrscheinlich noch  Lichtjahre von einer 
Lösung entfernt. Aber ich habe die Wille, um meine Aufgabe zu verstehen 
und letztendlich zu lösen. Ich danke dir auch für deine Ratschläge, 
Erklärungen und verlasse mich auf dich für weitere Hilfe.

Ich habe das Prinzip des Ringpuffers gelesen und habe die in meiner 
Routine eingesetzt.

Die Routine lautet:
1
ISR(USART0_RX_vect)
2
{
3
 char data = UDR0; // read data.
4
  if(writepos + 1 == readpos || readpos == 0 && writepos + 1 == buffer_size )
5
    {
6
   return fail; // Ringbuffer is full. we can not write into ringbuffer
7
    }
8
 buffer[writepos] = data; // write into ringbuffer
9
10
 writepos = writepos + 1;
11
 if (writepos >= buffer_size)
12
   {
13
    writepos = 0;
14
     return sucess;
15
   }
16
   
17
}
18
19
int main (void)
20
{ 
21
  USART_Init(UBRR_VAL) ; 
22
  char c ;
23
  sei();
24
  
25
  while(1)
26
  { 
27
    
28
    if(readpos == writepos)
29
   {
30
    return fail; // Ringbuffer is empty, we read nothing from ringbuffer 
31
     }
32
   if((readpos != writepos) && (UCSR1A & (1<<UDRE1)))
33
    {     
34
        c = buffer[readpos];
35
     USART1_Transmit(c); // USART1 send byte
36
        readpos = readpos + 1;
37
       }
38
   if (readpos >= buffer_size )
39
     {
40
       readpos = 0;
41
          return sucess;
42
         }
43
  }
44
}

Bin ich noch entfernt von der Lösung ? Wenn ja, bitte sagen sie mir 
bescheid, was ich unbedingt in meiner Routine ändern muss.

Danke im Voraus.

von Frank M. (ukw) (Moderator) Benutzerseite


Lesenswert?

Du hast wieder einmal Code irgendwo im Internet gefunden und einfach so 
kopiert.

Anders kann ich mir die beiden Zeilen ...

    return fail; // Ringbuffer is empty, we read nothing from ringbuffer
    ...
    return sucess;

... in der main-Funktion nicht erklären.

Wenn Du mittels return die main-Funktion verlässt, ist

               ENDE DES PROGRAMMS!

Danach dreht sich der µC nur noch im Kreis und tut nichts mehr.

Es hat keinen Zweck. Lern erstmal C mit einem guten Buch an einem PC, 
danach arbeite hier das AVR-GCC-Tutorial durch.

Komm dann in einem Jahr wieder. Nicht früher.

von Programmierer (Gast)


Lesenswert?

Luffy Monkey schrieb:
> Bin ich noch entfernt von der Lösung ? Wenn ja, bitte sagen sie mir
> bescheid, was ich unbedingt in meiner Routine ändern muss.

Schreib dir einige kleine Funktionen, welche

- einen Ringpuffer anlegen,
- Elemente hinzufügen,
- Elemente abholen,
- rückgeben, wie viele Plätze im Ringpuffer noch frei bzw. schon belegt 
sind.

Beginn doch mal mit statischen String-Puffern und statischen Indizes. 
Wenn das mal hinhaut, verwende eine struct dafür. Dann verwende diese 
Library in deinem Programm.

von Georg G. (df2au)


Lesenswert?

Die Fleury Lib zum Thema UART ist auch ein guter Ansatz. Lesen und 
nachvollziehen/verstehen.

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.