Forum: Compiler & IDEs UART-Senden fehlgeschlagen


von JKK (Gast)


Lesenswert?

#include <avr/io.h>

void main (void)
{



UCSRB |= (1<<RXEN);                         //Empfangen aktivieren
UCSRC |= (1<<URSEL)|(1<<UCSZ0)|(1<<UCSZ1);

  UBRRH = 00;                         //Baudrate einstellen 9600 bei 8 
MHz
  UBRRL = 51;


while (!(UCSRA & (1<<RXC)));    //warten bis senden fertig


  PORTB = UDR;        //schicke ein a über die schnittstelle

}



Warum leuchten die an PortB angeschlossenen Leuchtdioden nicht bis auf 
eine auf, wenn ich     0x7F über mein Terminal schicke?

Danke!

von Jörg X. (Gast)


Lesenswert?

Was passiert, wenn du 0x80 (d.h. ~0x7F) schickst? ;)

von Andreas K. (a-k)


Lesenswert?

Wie wär's wenn du die Port-Bits überhaupt erst einmal als Ausgang 
konfigurierst?

von JKK (Gast)


Lesenswert?

ok danke erstmal.

Sorry, die PortBins habe ich bereits als Ausgänge definiert, habe 
allerdings vergessen diese neuere version hier zu posten ;)

Also ich hatte wohl einen denkfehler, denn ich kann ja nicht in das 
Terminal 0x7F eingeben, da jedes einzeln eingegebene Zeichen: 0 und x 
und 7 und F jeweils das UDR (welches ja nur 8 Bit hat) belegt. Das 
heißt, ich kann nur einen wert eingeben.
Aber: wie kann ich dann andere werte eingeben (also nicht nur 1 Ziffer)?


danke

von Stefan B. (stefan) Benutzerseite


Lesenswert?

@ JKK

Erzähl' mal etwas mehr...

Woher weiss der ???, dass er mit 8 MHz laufen soll bzw. tut er das auch 
sicher?
http://www.mikrocontroller.net/articles/AVR_Checkliste#.C3.9Cbertragungsprobleme_wegen_falschem_Takt.3F

Wie ist die physikalische Verbindung zwischen µC und PC, d.h. liegt 
kommt das Signal TX vom PC irgendwann auf den Eingang RX vom µC? Ist auf 
dem PC auch 9600 8N1 kein Handshake eingestellt?
http://www.mikrocontroller.net/articles/AVR_Checkliste#Sonstige_Fehlerquellen_bei_UART.2FUSART

Und ein paar Feinheiten zu deinem Programm...
1
#include <avr/io.h>
2
3
int main (void)                   // <=== (1)
4
{
5
  DDRB = 0xFF;                    // PORTB Ausgang <=== (2)
6
7
  UCSRB |= (1<<RXEN);             //Empfangen aktivieren
8
  UCSRC |= (1<<URSEL)|(1<<UCSZ0)|(1<<UCSZ1); // 8N1
9
  UBRRH = 0;                      //Baudrate einstellen 9600 bei 8 MHz
10
  UBRRL = 51;
11
12
  while (1)                       // <=== (3)
13
  {
14
     while (!(UCSRA & (1<<RXC))); //warten bis senden fertig
15
     PORTB = UDR;                 //Zeichen auf PORTB-LEDs anzeigen
16
  }
17
}

(1) siehe C Standard

(2) siehe Antwort von Andreas Kaiser und 
http://www.mikrocontroller.net/articles/AVR-GCC-Tutorial#Datenrichtung_bestimmen

(3) siehe 
http://www.mikrocontroller.net/articles/AVR-GCC-Tutorial#Einf.C3.BChrungsbeispiel 
Punkt (5)

von JKK (Gast)


Lesenswert?

außerdem wenn ich "9" ins terminal ausgebe, leuchten die leds nicht 
binär 9. Warum?

von Karl H. (kbuchegg)


Lesenswert?

JKK wrote:
> außerdem wenn ich "9" ins terminal ausgebe, leuchten die leds nicht
> binär 9. Warum?


Weil, wenn du auf deiner Tastatur "9" drückst, der ASCII Code
für "9" übertragen wird. Vergiss dass auf deiner Taste eine 9
steht, die eine Zahl darstellt. Das sind einfach nur Buchstaben
die übertragen werden. Und für jeden 'Buchstaben' wird sein
ASCII Code übertragen. Für den Buchstaben "9" eben der
Code 0x39 oder eben binär 00111001

Such dir im Web eine ASCII Tabelle und kontrollier mal mit
deinen LED, ob das was du drückst auch angezeigt wird.

von JKK (Gast)


Lesenswert?

Ok, das habe ich mir vorhin auch schon gedacht, dass es mit dem 
ASCII-Code zutun hat.

Aber wie kann ich dann einen String empfangen? Sprich Zeicheneingabe und 
sobald ENTER gedrückt wird (am Terminal) soll er es auf den Port 
ausgeben.


Danke!

von Karl H. (kbuchegg)


Lesenswert?

JKK wrote:
> Aber wie kann ich dann einen String empfangen?

Indem du (zb. in einer Schleife) Zeichen für Zeichen
empfängst und jedes empfangene Zeichen zb in einem
Array ablegst.

> Sprich Zeicheneingabe und
> sobald ENTER gedrückt wird (am Terminal)

Das ENTER wird genauso übertragen. Ist ein Zeichen wie
jedes andere. Aber dein µC Programm erkennt daran, dass
der String jetzt komplett übertragen wurde, dass also
das Array alle Zeichen enthält.

In guter alter C-Manier sollte jetzt im Array noch ein
'\0' Character angehängt werden, damit das Ganze auch
formal zu einem C-String wird.

> soll er es auf den Port
> ausgeben.

Was soll er auf den Port ausgeben?
Einen String kannst du nicht auf einem Port ausgeben. Ein
String besteht aus mehreren Zeichen.
(OK. Man könnte das Ganze Zeichen für Zeichen ausgeben.
Technisch kein Problem, aber trotzdem relativ sinnlos).

Was du wahrscheinlich willst:
Du übertragst den String "123"
und der µC soll das Bitmuster für die Zahl 123 am Port
ausgeben.

Du musst zwischen den Dingen trennen: Übertragen wird
immer ein Text. Den fängst du in einem char-Array auf
und hast daher einen String.

Deine nächste Frage ist daher:
Wie kann ich einen String, der eine Zahl beinhaltet, in
die Zahl umwandeln.

Und die Antwort: Schau dir mal die Funktion atoi() an.

Übrigens würde ich dir empfehlen, anders weiter zu machen:
Fang mal damit an, eine Sendefunktion zu schreiben, die
dir einen C-String zum PC schicken kann.
Diese Funktion benutzt du dann, um zu kontrollieren, ob dein
empfangener String korrekt ist.
Erst dann, wenn du weist, dass der empfangene String korrekt
ist, beschäftigst du dich dann mit der Umsetzung in eine Zahl.

von JKK (Gast)


Lesenswert?

Vielen Dank für den Tipp, aber irgendwie verstehe ich noch nicht so 
ganz, wie ich nun atoi in mein Programm einbaue. Auch die Beispiele, die 
man im Netz dazu findet, bringen mich nicht wirklich weiter.

von Stefan B. (stefan) Benutzerseite


Lesenswert?

Dann schreib dein Programm doch mal soweit du kommst. Um das atoi() kann 
man sich kümmern, wenn es soweit ist.

von JKK (Gast)


Lesenswert?

#include <avr/io.h>
#include <stdlib.h>

int main (void)                   // <=== (1)
{

  char Input ;
  int i=0;
  DDRB = 0xFF;                    // PORTB Ausgang <=== (2)

  UCSRB |= (1<<RXEN);             //Empfangen aktivieren
  UCSRC |= (1<<URSEL)|(1<<UCSZ0)|(1<<UCSZ1); // 8N1
  UBRRH = 0;                      //Baudrate einstellen 9600 bei 8 MHz
  UBRRL = 51;

  while (1)                       // <=== (3)
  {

   while (!(UCSRA & (1<<RXC))); //warten bis senden fertig
                      //Zeichen auf PORTB-LEDs anzeigen

    Input = UDR;
    PORTB = atoi(Input);



  }
}


die atoi heißt doch  "Ascii-to-integer"....Aber ich verstehe den 
Zusammenhang noch nicht ganz. ich möchte wie ihr oben beschribene habt, 
bis zum Enter-Eingeben den Zahlenstring (z.b. dez.128) binär auf den 
PortC ausgeben.
Danke!

von Karl H. (kbuchegg)


Lesenswert?

Ich sagte doch, du benötigst ein char-ARRAY um dort den
String zwischenzuspeichern.

Wenn du mit String - Verarbeitung in C auf Kriegsfuss stehst,
dann solltest du den Ankauf von Literatur in Erwägung ziehen.
Das sind absolute C-Grundkenntnisse.
Solange du auf dein Buch wartest, kannst du das allernotwendigste
auch hier nachlesen:
http://www.mikrocontroller.net/articles/FAQ#Wie_funktioniert_String-Verarbeitung_in_C.3F
1
#include <avr/io.h>
2
#include <stdlib.h>
3
4
int main (void)                   // <=== (1)
5
{
6
  char c;
7
  char Input[200];
8
  int i=0;
9
  DDRB = 0xFF;                    // PORTB Ausgang <=== (2)
10
11
  UCSRB |= (1<<RXEN);             //Empfangen aktivieren
12
  UCSRC |= (1<<URSEL)|(1<<UCSZ0)|(1<<UCSZ1); // 8N1
13
  UBRRH = 0;                      //Baudrate einstellen 9600 bei 8 MHz
14
  UBRRL = 51;
15
16
  while (1)                       // <=== (3)
17
  {
18
19
    while (!(UCSRA & (1<<RXC))); //warten bis senden fertig
20
                      //Zeichen auf PORTB-LEDs anzeigen
21
    c = UDR;
22
23
    if( c != '\n' )
24
    {
25
      Input[i] = c;
26
      i++;
27
    }
28
29
    else
30
    {
31
      Input[i] = '\0';
32
      PORTB = atoi(Input);
33
      i = 0;
34
    }
35
  }
36
}

von BlauZahn (Gast)


Lesenswert?

hmm anscheinend funktioniert das beispiel von k.h.buchegger auch 
nicht...

Habe es nun reingeladen und wenn ich beispielsweise ins Terminal "193" + 
ENTER eingebe, ändert sich nix an meinen LEDs (die nachwievor alle 
leuchten)

danke

von Stefan B. (stefan) Benutzerseite


Lesenswert?

Funktionieren ist, was du definierst. Was machen tut es hoffentlich, 
oder?

Du hast immer noch nicht diese Fragen beantwortet:

Woher weiss der ???, dass er mit 8 MHz laufen soll bzw. tut er das auch
sicher?
http://www.mikrocontroller.net/articles/AVR_Checkl...

Wie ist die physikalische Verbindung zwischen µC und PC, d.h. liegt
kommt das Signal TX vom PC irgendwann auf den Eingang RX vom µC? Ist auf
dem PC auch 9600 8N1 kein Handshake eingestellt?
http://www.mikrocontroller.net/articles/AVR_Checkl...

Nur wenn beide Fragen positiv beantwortet sind, hat der µC überhaupt 
eine Chance, das Programm sinnvoll auszuführen.

Und selbst dann gilt:

"Die Hälfte seines Lebens debuggt der Programmierer vergebens!"

Debuggen kann heissen, an strategischen Stellen zusätzliche, markante 
Aktionen ausführen lassen. Dadurch sieht man, bis wohin sich der µC im 
Programmcode vorgehangelt hat.
1
#include <avr/io.h>
2
#include <stdlib.h>
3
4
// 0 Debugging AUS
5
// 1 Debugging AN
6
#define DEBUG 1
7
8
#if DEBUG
9
#ifndef F_CPU
10
/* Definiere F_CPU, wenn F_CPU nicht bereits vorher definiert 
11
   (z.B. durch Übergabe als Parameter zum Compiler innerhalb 
12
   des Makefiles). Zusätzlich Ausgabe einer Warnung, die auf die
13
   "nachträgliche" Definition hinweist */
14
#warning "F_CPU war noch nicht definiert, wird nun mit 8000000 definiert"
15
#define F_CPU 8000000UL /* Quarz mit 8 Mhz */
16
#endif
17
18
// siehe AVR-GCC-Tutorial 
19
// Abschnitt Warteschleifen
20
#include <util/delay.h> 
21
22
// 10*25ms = 250ms warten
23
#define DELAY_250MS()           \
24
   for (int j = 0; j < 10; j++)
25
     delay_ms(25);
26
27
#define DELAY_1000MS()          \
28
   for (int j = 0; j < 40; j++) \
29
     delay_ms(25);
30
   
31
32
static void fehlersuchen(unsigned char x)
33
{
34
   static unsigned char lx = 0;
35
36
   switch(x)
37
   {
38
      case 0:
39
         // Effekt 1
40
         // LED Lauflicht, wenn ein '\n' erkannt wurde
41
         for(int i = 0; i < 8; i++)
42
         {
43
            PORTB = ~(1 << i);
44
            // 10*25ms = 250ms warten
45
            for (int j = 0; j < 10; j++)
46
               delay_ms(25);
47
         }
48
         for(int i = 7; i >= 0; i--)
49
         {
50
            PORTB = ~(1 << i);
51
            for (int j = 0; j < 10; j++)
52
               delay_ms(25);
53
         }
54
         // 40*25ms = 1000ms warten
55
         for (int j = 0; j < 40; j++)
56
            delay_ms(25);
57
         break;
58
      case 255:
59
         // Effekt 2
60
         // Eine LED blinkt, wenn auf ein Zeichen gewartet wird
61
         for (int j = 0; j < 10; j++)
62
           delay_ms(25);
63
         PORTB ^= 0x80;
64
         for (int j = 0; j < 10; j++)
65
           delay_ms(25);
66
         break;
67
      default:
68
         // Effekt 3
69
         // LED zeigt die Zahl der empfangenen Zeichen im Puffer Input an
70
         for (int j = 0; j < 10; j++)
71
           delay_ms(25);
72
         PORTB = ~x;
73
         for (int j = 0; j < 10; j++)
74
           delay_ms(25);
75
         break;
76
   }
77
}
78
#else
79
#define fehlersuchen(x)
80
#endif
81
82
int main (void)                   // <=== (1)
83
{
84
  char c;
85
  char Input[200];
86
  int i=0;
87
  DDRB = 0xFF;                    // PORTB Ausgang <=== (2)
88
  PORTB = 0xFF;                   // alle LED aus #1
89
90
  UCSRB |= (1<<RXEN);             //Empfangen aktivieren
91
  UCSRC |= (1<<URSEL)|(1<<UCSZ0)|(1<<UCSZ1); // 8N1
92
  UBRRH = 0;                      //Baudrate einstellen 9600 bei 8 MHz
93
  UBRRL = 51;
94
95
  while (1)                       // <=== (3)
96
  {
97
    while (!(UCSRA & (1<<RXC)))
98
    {
99
       // warten bis senden fertig
100
       fehlersuchen(255);
101
    }
102
    c = UDR;
103
104
    if( c != '\n' )
105
    {
106
      Input[i] = c;
107
      i++;
108
      fehlersuchen(i);
109
    }
110
    else
111
    {
112
      fehlersuchen(0);
113
      Input[i] = '\0';
114
      PORTB = ~atoi(Input); // #1
115
      i = 0;
116
    }
117
  }
118
}

Da hier im Beispiel im Debugmodus einfache Warteschleifen benutzt 
werden, muss man jetzt zwischen den einzelnen Tastendrücken 1-2 Sekunden 
warten, damit keine Zeichen verloren gehen. Dafür sieht es lustig aus 
;-)

#1

> ...ändert sich nix an meinen LEDs (die nachwievor alle leuchten)

Deine LEDs scheinen wie auf dem STK500 beschaltet zu sein, d.h. LED an, 
wenn PORT-Pin LOW ist.

von JKK (Gast)


Lesenswert?

also...ich verwende ein STK500. Einen ATMega8515 und ich habe die 
Taktfrequenz des Controllers über die Fuses (Internal RC Osc.) auf 8 MHz 
gesetzt.

Kurze Zwischenfrage, zwecks verunsicherung:  welchen Unterschied gibt es 
zwischen "Taktfrequenz durch Fuses setzen" und "Project -> Configuration 
Options -> frequenzy"?


Die LEDs leuchten am STK500 anfangs alle. Dann stelle ich im 
HyperTerminal ein 9600 baud, usw. Flusssteuerung "keine", und lokales 
Echo "an".

Dann tut sich nix! D.h. die LEDs leuchten genauso wie vorher, nämlich 
alle.


danke

von Stefan B. (stefan) Benutzerseite


Lesenswert?

JKK wrote:
> also...ich verwende ein STK500. Einen ATMega8515 und ich habe die
> Taktfrequenz des Controllers über die Fuses (Internal RC Osc.) auf 8 MHz
> gesetzt.

Internal RC Osc. ist keine gute Ausgangsvoraussetzung für eine 
funktionierende UART-Kommunikation zumindest nicht bei 9600 Baud. 
Näheres kannst du im obigen Link nachlesen und in den Tutorialartikeln 
über UART und in der Baudratentabelle im Datenblatt (max. Fehler sollte 
< 2% sein).

> Kurze Zwischenfrage, zwecks verunsicherung:  welchen Unterschied gibt es
> zwischen "Taktfrequenz durch Fuses setzen" und "Project -> Configuration
> Options -> frequenzy"?

Damit der µC mit einer bestimmten Taktquelle läuft, muss diese 
Taktquelle körperlich vorhanden sein. Das stellst du durch das Design 
des Boards (externer Quarz etc.) oder Jumpereinstellungen etc. sicher.

Und die Taktquelle muss vom µC angesprochen werden z.B. muss sie 
definiert zum Schwingen angeregt werden. Das stellst du durch die 
Fuses sicher.

Und letztendlich kann man den Wert mit dem die Taktquelle schwingt an 
verschiedenen Stellen im Programmcode benötigen. Z.B. um genaue 
Warteschleifen zu machen.

Da man den Wert i.d.R. nicht umständlich messen will sondern ihn 
normalerweise vom Boarddesign und den Fuses her ganz genau kennt, setzt 
man den Wert direkt oder indirekt über "Project -> Configuration Options 
-> frequency" als vorher definiertes Makro F_CPU im Programmcode ein.

> Die LEDs leuchten am STK500 anfangs alle. Dann stelle ich im
> HyperTerminal ein 9600 baud, usw. Flusssteuerung "keine", und lokales
> Echo "an".

Mit dem neuen Programmcode sollten die LEDs ziemlich schnell ausgehen 
und dann wild blinken, während auf Zeichen gewartet wird.

> Dann tut sich nix! D.h. die LEDs leuchten genauso wie vorher, nämlich
> alle.

Das richtige Programm ist übersetzt und in den ATMega8515 geladen und 
ein RESET hast du auch schon gemacht?

von EinsteigÄr (Gast)


Lesenswert?

ähm von welchem Programmcode sprechen wir eigentlich? Ich rede von dem 
von Karl-Heinz Buchegger um 16:34 Uhr.

von Karl H. (kbuchegg)


Lesenswert?

BlauZahn wrote:
> hmm anscheinend funktioniert das beispiel von k.h.buchegger auch
> nicht...

Das kann gut sein.
Ich habe den Code mehr oder weniger blind getippt, also ohne
ausprobieren zu können.

Genau aus diesem Grund hab ich dir auch weiter vorne einen
Stufenplan gegeben, wie man sowas systematisch angeht:

* zuerst eine Ausgabefunktion schreiben, die Texte ausgeben
  kann
  Vorausssetzungen, die funktionieren muessen: keine
  für sich alleine testbar: ja

* dann die String Empfangsfunktion schreiben, die einen
  String empfängt und den empfangenen String wieder
  zurückschickt
  Voraussetzungen: Die Stringsendefunktion muss funktionieren, aber
  das wurde ja bereits im vorhergehenden Schritt sicher gestellt

* und erst dann eine Zahl umwandeln und am Port ausgeben lassen
  Voraussetzungen: die Empfangsfunktion muss funktionieren
  aber das wurdee im vorhergehenden Schritt sicher gestellt

Du musst anfangen solche Schritt-für-Schritt Entwicklungen ernst
zu nehmen bzw. deine Arbeit selbst in solchen Schritten zu
organisieren. Jeder Schritt baut immer auf vorhegehenden,
getesteten Schritten auf. Dadurch hast du immer eine Möglichkeit
mit kleinen Veränderungen bzw. Ergänzungen von einem funktionierenden
Schritt zum nächsten zu kommen. Dadurch weist du aber auch immer,
wo der Fehler in etwa zu suchen ist, falls einer auftaucht.

Einfach ein Programm runterzuklopfen hat den Nachteil, dass
du dann mit einem nicht funktionierenden Programm dastehst
und nicht weist wo du zu suchen anfangen sollst.

>
> Habe es nun reingeladen und wenn ich beispielsweise ins Terminal "193" +
> ENTER eingebe, ändert sich nix an meinen LEDs (die nachwievor alle
> leuchten)

Das kann zb. die Ursache haben, dass dein Terminal beim Druck
auf ENTER einen CR/LF oder nur ein LF oder nur ein CR wegschickt.
Hängt von der Konfiguration des Terminals ab.

Teste doch mal mit deinem ersten Programm (Tastendruck direkt
auf die LEDs) welches Zeichen gesendet wird, wenn du auf
ENTER drückst.

von Karl H. (kbuchegg)


Lesenswert?

JKK wrote:
> also...ich verwende ein STK500. Einen ATMega8515 und ich habe die
> Taktfrequenz des Controllers über die Fuses (Internal RC Osc.) auf 8 MHz
> gesetzt.

Keine gute Idee

>
> Die LEDs leuchten am STK500 anfangs alle. Dann stelle ich im
> HyperTerminal ein 9600 baud, usw. Flusssteuerung "keine", und lokales
> Echo "an".

Das lokale Echo schaltest du gleich wieder mal aus.
Dafür lässt du jetzt erst mal den µC das Echo machen.
Dadurch siehst du nämlich, ob die Übertragung funktioniert.
Der µC soll alles was er empfängt direkt wieder zurückschicken ...

1
#include <avr/io.h>
2
3
void main (void)
4
{
5
  char c;
6
7
  UCSRB |= (1<<RXEN) | (1<<TXEN);
8
  UCSRC |= (1<<URSEL)|(1<<UCSZ0)|(1<<UCSZ1);
9
10
  UBRRH = 00;                 //Baudrate einstellen 9600 bei 8 Mhz
11
  UBRRL = 51;
12
13
  while( 1 ) {
14
    while (!(UCSRA & (1<<RXC)))
15
      ;                         //warten bis ein Zeichen ankommt
16
17
    c = UDR;
18
    PORTB = c;                  //emfpangenes Zeichen ausgeben ...
19
20
    while (!(UCSRA & (1<<UDRE)))  //ist die Sendestufe bereit?
21
      ;
22
    UDR = c;                    //... und wieder zurückschicken
23
  }
24
}

(Kann jemand anderer den Code mal testen. Ich hab hier kein
Entwicklungssystem damit ich den Test selbst machen kann).

von EinSteigÄr (Gast)


Lesenswert?

ok danke!

Also dein Code funktioniert bis auf die LEDs. Der Controller scickt also 
das eingegebene Zeichen sofort wieder zurück. (Habe es mit einem Oszi 
getestet)

von EinSteigÄr (Gast)


Lesenswert?

ähm....liegt wohl am Fehlenden Data Direction Register ;)

von Karl H. (kbuchegg)


Lesenswert?

EinSteigÄr wrote:
> ähm....liegt wohl am Fehlenden Data Direction Register ;)

Jep.
Hab nicht drauf geachtet und einfach das erste Pgm im
Thread genommen und umgeändert.

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.