Forum: Compiler & IDEs USART: Das übliche Empfangsproblem


von Wheels C. (wheels)


Lesenswert?

Hallo,

bin schon eine ganze Weile am Fehlersuchen (auch hier im Forum). Leider 
hab bisher ich keinen Erfolg gehabt. Vielleicht könnt Ihr mir ja 
weiterhelfen.

Mein Programm soll Daten über die serielle Schnittestelle des USART0 des 
ATmegas1284P empfangen und wieder zurücksenden. Um das zu testen habe 
ich im AVR Studio Simulator das Programm durchlaufen lassen. Natürlich 
bleibt er dann bei

while ( !(UCSR0A & (1<<RXC0)) );

hängen, sodass ich in das UDR0 Register einen Wert schreiben und 
anschließend RXC0 auf 1 setzen musste. Das Programm läuft dann auch 
weiter allerdings verwirft er den Wert von UDR0 auch gleich wieder. 
Gesendet wird dann immer der Wert 0. Der unten stehende Code ist direkt 
kopiert, also original. Wenn jemand da ne Idee hat wär super.



// Mikrocontroller:   ATmega1284P
// Externer Quarz:  20MHz
//
// Compiler:    AVR-GCC

/**********************Inkludierung der Headerdateien*****************/

#include <stdlib.h>
#include <avr/io.h>
#include <stdint.h>
#include <util/delay.h>

/*******************************Definitionen**************************/

#define LED_R (1<<PB0)
#define LED_G (1<<PB1)
#define PLED_L_R (1<<PD6)
#define FLED (1<<PD2)
#define BLED (1<<PD7)
#define Summer (1<<PB5)

/***********************Deklaration globaler Variablen****************/

char buffer;

/**************************Unterprogramme*****************************/

void usart_init(unsigned int baud);
void usart_transmit (unsigned char data);
unsigned char usart_receive( void );

/******************************Hauptprogramm**************************/

int main ( void ){

 DDRA  = 0x00;
 DDRB  = 0xFF;

 DDRC  = 0x51;
 DDRD  = 0xFE;

 usart_init(21);


  while (1)
    {
      usart_receive();
      buffer = UDR0;
      usart_transmit(buffer);
      _delay_ms(1000);
  }

}


/***************************USART-Initialisierung*********************/

void usart_init( unsigned int baud )
{

  UCSR0A = 0x22;  //00100010 -> Setzen des Double Speed Mode

  UCSR0B = 0x18;  //Aktiviert den Receiver und Transmitter

  UCSR0C = 0x26;  //Festlegung des Frames:
                  //1 Startbit
                  //8 Datenbits
                  //1 Paritätsbit even
                  //1 Stoppbit

  UBRR0H = (unsigned char)(baud>>8); //Setzen der Baud Rate -> im
                                     //Double Speed Mode
  UBRR0L = (unsigned char)baud;
}

/**************************USART-Transmitter*************************/

void usart_transmit (unsigned char data)
{
  while ( !(UCSR0A & (1<< UDRE0)) );
  UDR0 = data;
}

/****************************USART-Receiver**************************/

unsigned char usart_receive ( void )
{
  while ( !(UCSR0A & (1<<RXC0)) );
  buffer = UDR0;
  return buffer;
}

von Guru (Gast)


Lesenswert?

>... allerdings verwirft er den Wert von UDR0 auch gleich wieder.

Das wundert mich nicht. Das ist ja genau das, wozu Du ihn angewiesen 
hast.

Schau Dir noch einmal genau Deinen Code an. Welche Schritte werden 
ausgeführt?

...
      usart_receive();
-->
           while ( !(UCSR0A & (1<<RXC0)) );
           buffer = UDR0;         // hier liest Du also das empfangene 
Byte
           return buffer;         // da koennte man auch noch was zu 
sagen

<---   zurück in main

      buffer = UDR0;             // und was liest Du hier?

Schrott!

Der Fifo hat nämlich schon seinen Zustand geändert.
Siehe Datasheet S. 190

"The receive buffer consists of a two level FIFO. The FIFO will change 
its state whenever the
receive buffer is accessed. Due to this behavior of the receive buffer, 
do not use Read-Modify-
Write instructions (SBI and CBI) on this location. Be careful when using 
bit test instructions
(SBIC and SBIS), since these also will change the state of the FIFO."

von Wheels C. (wheels)


Lesenswert?

Oh Mann. Ja du hast recht. Hab nun den Teil im Hauptprogramm geändert.

Er lautet nun:

while (1)
    {
      usart_receive();
      usart_transmit(buffer);
     _delay_ms(1000);
    }

So müsste es ja eigentlich funktionieren. Allerdings verwirft er den 
Wert von UDR0 immer noch. So bin  ich weiterhin ratlos.

von Guru (Gast)


Lesenswert?

Hm.
Das ist seltsam.

Dann müssen wir mal von den Deutungen wegkommen.
Zunächstmal, was meinst Du mit "er verwirft" den Wert von UDR? Woran 
siehst Du das? Wie gehst Du genau vor? Immer noch im Debugger? (Das UDR 
Register wird nicht notwendigerweise korrekt simuiert) Beschreibe 
möglichst genau was Du tust. Nimm mal Hapsim. Da kannst Du ein Terminal 
simulieren.

von Wheels C. (wheels)


Angehängte Dateien:

Lesenswert?

Hier hab ich mal von der Problemstelle zwei Screenshots gemacht.

Bild 1 zeigt rechts den Wert UDR0 = 0x40 bei gesetztem RXC0. Die beiden 
Werte musste ich manuell setzen (bin schließlich im Simulator).

Bild 2 zeigt die Register nach dem der Empfangsprozess abgeschlossen 
wurde. Wie man sieht steht in UDR0 nichts mehr drin. Kein Wunder das ich 
also auch nichts als eine 0 zurück gesendet bekomme. Frage ist nur warum 
er das macht.

Geschrieben habe ich das Programm um meine Verbindung zwischen zwei 
XBees zu testen. Der Datenverkehr wird mir dabei über zwei LEDs 
visualisiert. Das funktioniert auch. Die Led für den Dateneingang 
leuchtet auf wenn Daten eingehen und die Led für den Datenausgang 
leuchtet auf wenn Daten ausgehen. Die Hardware scheint also zu 
funktionieren. Nur der Mikrocontroller löscht (verwirft) den Wert von 
UDR0 sofort nach dem vollständig empfangen wurde.

von Guru (Gast)


Lesenswert?

Hmm.

Also mein letzter Stand ist,

"UART/USART
The UART/USART UDR register can only be modified from the application. 
Input via stimuli files or by modifying the I/O view etc is not 
possible."

d.h. das die UART Register garnicht von Hand geändert werden können. Man 
kann da also im Simulator nichts simulieren. Deswegen auch mein Tip mit 
dem Hapsim.

Aber: Welche Version/Build vom AVRStudio verwendest Du? Ist unter Help 
zu sehen. Irgendwie scheinst Du das UDR ja geändert zu haben. Wie 
genau hast Du das gemacht? Bitte Schritt für Schritt erklären.

von Wheels C. (wheels)


Lesenswert?

AVR Studio Version 4.18 Build 684

Das UDR hab durch einfaches anklicken der weissen/schwarzen Kästchen 
(siehe Bild 1 und Bild 2 rechts) verändern können. Das geht allerdings 
nur im Running-Modus, also bei laufender Simulation.

Den Transmitter habe ich so auch getestet. Das lief einwandfrei.

1. Schritt: auf Build+Run klicken
2. Schritt: Step into solange bis in die Funktion "unsigned char
    usart_receive ( void )" gesprungen wird.
3. Schritt: Programm bleibt bei "while ( !(UCSR0A & (1<<RXC0)) );" 
hängen
   da  nichts in UDR0 steht und das RXC0-Bit nicht gesetzt ist. Also 
setze
   ich wie oben beschrieben die entsprechenden Bits. -> UDR0=irgendein 
Wert
   RXC=1
4. Schritt: Step into
5. Schritt: Jetzt sind man dass UDR0 wider gelöscht worden ist. Bevor es
   durch die Variable buffer gesichert werden konnte.

von Guru (Gast)


Lesenswert?

Ich habe die selbe Version.

>Das UDR hab durch einfaches anklicken der weissen/schwarzen Kästchen
(siehe Bild 1 und Bild 2 rechts) verändern können. Das geht allerdings
nur im Running-Modus, also bei laufender Simulation.

Aha. Ja, gut. Dann ist das klar. Ich wollte schon nach nem kompletten 
Screenshot fragen. Das sind leider bloss alles Fata Morganas. Du kannst 
das im Simulator nicht so machen. Das das Kästchen auf eins steht ist 
bloss der Tatsache zu verdanken, das die Anzeige nur sporadisch 
aufgefrischt wird.

Was immer für ein Problem Du lösen willst: Das geht in Bezug auf den 
UART nicht im Simulator.

Ich habe Dir ja schon geraten Hapsim zu verwenden. Mach das mal.

von Wheels C. (wheels)


Lesenswert?

Ich danke Dir für deine Zeit. Ich weiss zwar immer noch nicht was das 
Problem war. Aber es funktioniert auf einmal.

von Guru (Gast)


Lesenswert?

>Ich weiss zwar immer noch nicht was das
>Problem war. Aber es funktioniert auf einmal.

Naja. Entweder Du analysierst es jetzt oder es kommt wieder.

von Rufus Τ. F. (rufus) Benutzerseite


Lesenswert?

Das Problem liegt in der hier völlig unsinnigen globalen Variable 
"buffer".

Ein Paradebeispiel dafür, daß globale Variablen problematisch sind.

"buffer" sollte nur lokal in main() deklariert werden, und nochmal 
lokal in usart_receive.

Dazu kommt noch erschwerend der mehrfache Lesezugriff auf das 
USART-Empfangsregister - der sollte ausschließlich in usart_receive 
erfolgen und sonst nirgends.
1
int main(void)
2
{
3
  unsigned char buffer;
4
5
  // diverse Initialisierungen ausgelassen
6
  usart_init(21);
7
8
  while (1)
9
  {
10
    buffer = usart_receive();
11
    usart_transmit(buffer);
12
    _delay_ms(1000);
13
  }
14
}

Hier stellt sich noch die Frage, was um alles in der Welt 21 ist. Wo 
kommt der Wert her?
1
unsigned char usart_receive(void)
2
{
3
  while (!(UCSR0A & (1<<RXC0)));
4
  return UDR0;
5
}

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.