Forum: Compiler & IDEs UART Zeichen Empfangen und nutzen


von R. B. (britzi)


Lesenswert?

Hallo zusammen,

ich möchte per UART ein Zeichen als String empfangen und je nach Zeichen 
einen Pin High oder Low setzen.

Leider funktioniert mein Programm nicht und ich weiß nicht warum.
UART selbst funktioniert, da ich an mein Terminalprogramm testweise 
Strings senden konnte.

Wo liegt der Fehler?

Vielen Dank :-)
1
 #include <avr/io.h>
2
#include <avr/interrupt.h>
3
#define BAUD 9600
4
#define UBRR_VAL F_CPU/16/BAUD-1
5
 
6
volatile unsigned char receive_char;
7
 
8
void init_uart(unsigned int ubrr) {
9
       UBRRH = (unsigned char)(ubrr>>8);
10
       UBRRL = (unsigned char)(ubrr);
11
       UCSRB |= (1<<TXEN | 1<<RXEN | 1<<RXCIE);
12
}
13
 
14
ISR(USART_RXC_vect) {
15
       receive_char = (UDR);
16
}
17
 
18
int main(void) {
19
       init_uart(UBRR_VAL);
20
21
     DDRB = (1 << DDB0);
22
    sei();
23
       while (1) {
24
       if(receive_char=='A') {PORTB &= ~(1<<PB0);}
25
       else {PORTB|= (1<<PB0);}
26
              
27
       }
28
}

von Dussel (Gast)


Lesenswert?

Ich gehe jetzt mal von einem ATMega8 aus. Sendest du im 5-Bit Format? 
Sonst solltest du im UCRSC auch was ändern.

von Stefan E. (sternst)


Lesenswert?

Dussel schrieb:
> Ich gehe jetzt mal von einem ATMega8 aus. Sendest du im 5-Bit Format?
> Sonst solltest du im UCRSC auch was ändern.

Schon mal im Datenblatt den Default-Inhalt von UCSRC angeschaut?

von R. B. (britzi)


Lesenswert?

Dussel schrieb:
> Ich gehe jetzt mal von einem ATMega8 aus. Sendest du im 5-Bit Format?
> Sonst solltest du im UCRSC auch was ändern.


Danke für die Antwort :-)

Nein, es war 8-Bit eingestellt und nun habe ich das Terminalprogramm auf 
5-Bit eingestellt und es funktioniert auf anhieb.


Vielen Dank :-)

Das mit dem USRC werde ich mal nachlesen :)

von Stefan E. (sternst)


Lesenswert?

R. B. schrieb:
> Wo liegt der Fehler?

Was genau wird denn überhaupt gesendet?
Dass der Pin nur sehr kurz Low ist, wenn du im Terminalprogramm A und 
danach Return drückst, ist dir klar, oder?

von Stefan E. (sternst)


Lesenswert?

R. B. schrieb:
> Das mit dem USRC werde ich mal nachlesen :)

Prinzipiell eine gute Idee, hat mit deinem aktuellen Problem aber nichts 
zu tun.

von Dussel (Gast)


Lesenswert?

Stefan Ernst schrieb:
> Schon mal im Datenblatt den Default-Inhalt von UCSRC angeschaut?
Nein, tatsächlich nicht, aber
> Nein, es war 8-Bit eingestellt und nun habe ich das Terminalprogramm auf
> 5-Bit eingestellt und es funktioniert auf anhieb.
?

Was ist denn jetzt los?

von R. B. (britzi)


Lesenswert?

Dussel schrieb:
> Stefan Ernst schrieb:
>> Schon mal im Datenblatt den Default-Inhalt von UCSRC angeschaut?
> Nein, tatsächlich nicht, aber
>> Nein, es war 8-Bit eingestellt und nun habe ich das Terminalprogramm auf
>> 5-Bit eingestellt und es funktioniert auf anhieb.
> ?
>
> Was ist denn jetzt los?



Ich meinte natürlich UCSRC ;-)

Das Sendeformat im Terminalprogramm war falsch eingestellt.

Und das beim nächsten Zeichen alles wieder high ist, ist mir klar!
Ich muss mir noch überlegen, wie ich es hinbekomme, das es low bleibt.
Vielleicht mit einer Switch-Anweisung, mal sehen.

Im Moment geht es nur darum, ein bisschen zu spielen und zu lernen :)


Danke :-)

von Stefan E. (sternst)


Lesenswert?

Dussel schrieb:
> Was ist denn jetzt los?

Reines Glück. Wenn 5 Bits gesendet werden, der AVR aber 8 erwartet, wird 
anscheinend das A trotzdem noch erkannt, das nachfolgende \r aber 
verschluckt, und schon scheint sein Code zu funktionieren.

von Stefan E. (sternst)


Lesenswert?

R. B. schrieb:
> Das Sendeformat im Terminalprogramm war falsch eingestellt.

Nein, war es nicht. Dein AVR ist auf 8N1 eingestellt.

R. B. schrieb:
> Und das beim nächsten Zeichen alles wieder high ist, ist mir klar!

Aber ist dir auch klar, dass wahrscheinlich(*) sofort ein "nächstes 
Zeichen" kommt, und der Pin damit nur ca 1 ms lang low ist?

(*) man kann hier nur mutmaßen, denn du hast die Frage danach, was wie 
gesendet wird, ja nicht beantwortet.

von R. B. (britzi)


Lesenswert?

Stefan Ernst schrieb:
> Dussel schrieb:
>> Was ist denn jetzt los?
>
> Reines Glück. Wenn 5 Bits gesendet werden, der AVR aber 8 erwartet, wird
> anscheinend das A trotzdem noch erkannt, das nachfolgende \r aber
> verschluckt, und schon scheint sein Code zu funktionieren.

Ich habe gerade alles zum UCRSC nachgelesen und wenn ich da keine Bits 
setze, wird doch erwartet, dass ich 5 Bits sende, also passt doch alles, 
wenn ich im Terminalprogramm nun 5 Bits sende :)

von Stefan E. (sternst)


Lesenswert?

R. B. schrieb:
> Ich habe gerade alles zum UCRSC nachgelesen und wenn ich da keine Bits
> setze, wird doch erwartet, dass ich 5 Bits sende

Welcher AVR soll das sein? Ich habe noch keinen gesehen, beim dem der 
Inhalt des Registers nach einem Reset 0 ist.

von R. B. (britzi)


Lesenswert?

Stefan Ernst schrieb:
> R. B. schrieb:
>> Ich habe gerade alles zum UCRSC nachgelesen und wenn ich da keine Bits
>> setze, wird doch erwartet, dass ich 5 Bits sende
>
> Welcher AVR soll das sein? Ich habe noch keinen gesehen, beim dem der
> Inhalt des Registers nach einem Reset 0 wäre.

Ok... möglicherweise bin ich auf dem Holzweg ;)

Das hier ist ein Atmega 8 und ich dachte bisher, das alle bits 
standardmäßig nicht gesetzt seien, aber anscheinend ist dem nicht so :)

Wie müsste man denn dann meinen Code ändern, damit alles in UCRSC 
wirklich sicher 0 gesetzt ist? Einfach UCRSC=0x00?

von Stefan E. (sternst)


Lesenswert?

R. B. schrieb:
> Das hier ist ein Atmega 8 und ich dachte bisher, das alle bits
> standardmäßig nicht gesetzt seien, aber anscheinend ist dem nicht so :)

Im Datenblatt steht bei den Registerbeschreibungen der Default-Inhalt 
nach einem Reset dabei.

R. B. schrieb:
> Wie müsste man denn dann meinen Code ändern, damit alles in UCRSC
> wirklich sicher 0 gesetzt ist? Einfach UCRSC=0x00?

Nö, damit schreibst du eine 0 nach UBRRH. Sagtest du nicht "ich habe 
gerade alles zum UCRSC nachgelesen"?
Und warum überhaupt willst du denn jetzt den AVR auf 5-Bit umstellen? 
Stell einfach dein Terminalprogramm wieder richtig ein.

von R. B. (britzi)


Lesenswert?

Stefan Ernst schrieb:
> R. B. schrieb:
>> Das hier ist ein Atmega 8 und ich dachte bisher, das alle bits
>> standardmäßig nicht gesetzt seien, aber anscheinend ist dem nicht so :)
>
> Im Datenblatt steht bei den Registerbeschreibungen der Default-Inhalt
> nach einem Reset dabei.
>
> R. B. schrieb:
>> Wie müsste man denn dann meinen Code ändern, damit alles in UCRSC
>> wirklich sicher 0 gesetzt ist? Einfach UCRSC=0x00?
>
> Nö, damit schreibst du eine 0 nach UBRRH. Sagtest du nicht "ich habe
> gerade alles zum UCRSC nachgelesen"?
> Und warum überhaupt willst du denn jetzt den AVR auf 5-Bit umstellen?
> Stell einfach dein Terminalprogramm wieder richtig ein.

Ok.... ich habe nicht im Datenblatt, sondern in der UART-Beschreibung 
auf dieser Seite hier nachgesehen :)

Das Terminalprogramm steht nun auf 8-Bits und es funktioniert.

Da ich echt ein kompletter Neuling in E-Technik und Programmierung bin, 
bitte ich etwas um Nachsicht :)

Ich weiß nicht, warum es vorhin nicht funktioniert hat.
Weil standardmäßig stand es auf 8-Bit, aber ging vorhin nicht.
Keine Ahnung warum!


Und zum Thema richtig speichern habe ich gerade keine elegante Idee.
Wie realisiert man es elegant, wenn man verschiedene Pins low oder high 
setzten will und die ihren Status behalten sollen, solange der Status 
nicht explizit geändert wird?

Vielen Dank :-)

von R. B. (britzi)


Lesenswert?

So,

ich habe nun über eine Lösung nachgedacht, um den High- bzw. Low-Pegel 
zu erhalten.
1
 int main(void) {
2
       init_uart(UBRR_VAL);
3
4
int erhalt=0;
5
6
     DDRB = (1 << DDB0);
7
    sei();
8
       while (1) {
9
       if(receive_char=='A' || erhalt != 0){PORTB &= ~(1<<PB0); erhalt++;}
10
       if(receive_char=='B' || erhalt == 0){PORTB|= (1<<PB0); erhalt=0;}
11
       }
12
}

Leider funktioniert dies auch wieder nicht wie es soll.

Meine Idee ist, dass ich bei dem ersten if ein XOR bräuchte statt einem 
normalen Oder. Aber ein richtiges logisches XOR gibt es in C nicht, 
oder?

Habe ich mit meiner Vermutung recht oder liegt mein Fehler mal wieder wo 
anders? ;-)

Danke! :)

von Peter II (Gast)


Lesenswert?

ich versteht gar nicht was du mit der Variabel "erhalt" erreichen 
willst. Lass sie einfach weg. Dann geht die LED bei 'A' und bei 'B' aus.

Man darf code übrigens auch auf mehr also eine Zeile schreiben, das ist 
dann allgemein besser lesbar.

von R. B. (britzi)


Lesenswert?

Peter II schrieb:
> ich versteht gar nicht was du mit der Variabel "erhalt" erreichen
> willst. Lass sie einfach weg. Dann geht die LED bei 'A' und bei 'B' aus.
>
> Man darf code übrigens auch auf mehr also eine Zeile schreiben, das ist
> dann allgemein besser lesbar.


Das Ziel ist, wenn ich nach dem "A" z.B. ein "X" sende, dass der 
Zustand, den A ausgelöst hat, solange bleibt, bis wirklich ein "B" 
kommt.

von Peter II (Gast)


Lesenswert?

R. B. schrieb:
> Das Ziel ist, wenn ich nach dem "A" z.B. ein "X" sende, dass der
> Zustand, den A ausgelöst hat, solange bleibt, bis wirklich ein "B"
> kommt.

dafür braucht man keine Variable.

Wenn 'A'
  Lampe AN

Wenn 'B'
   Lampe AUS

wenn weder 'A' noch 'B' kommt, dann ändert sich einfach die lampe nicht.

von Malte S. (maltest)


Lesenswert?

Stefan Ernst schrieb:
> Wenn 5 Bits gesendet werden, der AVR aber 8 erwartet, wird
> anscheinend das A trotzdem noch erkannt, das nachfolgende \r aber
> verschluckt

Welches \r?

> dass wahrscheinlich(*) sofort ein "nächstes
> Zeichen" kommt, und der Pin damit nur ca 1 ms lang low ist?

Zwar wissen wir es nicht explizit, doch sieht alles danach aus, dass der 
TO einzelne Zeichen sendet. Von irgendetwas Zeilenorientiertem und den 
dazugehörigen Zeilenendzeichen war nun wirklich nie die Rede.

Vorschlag: pack das Lesen von receive_char in main() in einen 
interruptsperrenden Block und setze receive_char dort auf 0. Dann 
bedeutet receive_char !=0 dass ein neues Zeichen eingetroffen ist. 
Zumindest wenn du kein NUL über den UART empfangen möchtest.

von R. B. (britzi)


Lesenswert?

Peter II schrieb:
> R. B. schrieb:
>> Das Ziel ist, wenn ich nach dem "A" z.B. ein "X" sende, dass der
>> Zustand, den A ausgelöst hat, solange bleibt, bis wirklich ein "B"
>> kommt.
>
> dafür braucht man keine Variable.
>
> Wenn 'A'
>   Lampe AN
>
> Wenn 'B'
>    Lampe AUS
>
> wenn weder 'A' noch 'B' kommt, dann ändert sich einfach die lampe nicht.



Tatsächlich....
Ich glaube es ist schon zu spät für mich, um noch klar zu denken ;-)

von R. B. (britzi)


Lesenswert?

Malte S. schrieb:
> Stefan Ernst schrieb:
>> Wenn 5 Bits gesendet werden, der AVR aber 8 erwartet, wird
>> anscheinend das A trotzdem noch erkannt, das nachfolgende \r aber
>> verschluckt
>
> Welches \r?
>
>> dass wahrscheinlich(*) sofort ein "nächstes
>> Zeichen" kommt, und der Pin damit nur ca 1 ms lang low ist?
>
> Zwar wissen wir es nicht explizit, doch sieht alles danach aus, dass der
> TO einzelne Zeichen sendet. Von irgendetwas Zeilenorientiertem und den
> dazugehörigen Zeilenendzeichen war nun wirklich nie die Rede.
>
> Vorschlag: pack das Lesen von receive_char in main() in einen
> interruptsperrenden Block und setze receive_char dort auf 0. Dann
> bedeutet receive_char !=0 dass ein neues Zeichen eingetroffen ist.
> Zumindest wenn du kein NUL über den UART empfangen möchtest.

Im Moment sind es einfach nur einzelne Zeichen.
An mehrere Zeichen traue ich mich erstmal nicht ran.

Da durchschaue ich noch nicht, wie das mit dem Interrupt geht, weil ja 
pro Interrupt immer nur ein Zeichen abgeholt wird und ich sie dann 
irgendwie in einem Array wieder zusammenfügen müsste.

So stelle ich es mir zumindest vor ;)

von Malte S. (maltest)


Lesenswert?

Da bietet sich ein ring buffer an. Du nimmst ein Array, das z.B. 16 char 
groß ist und dazu zwei Indizes: einen Lese- und einen Schreibzeiger. In 
der ISR vergleichst du die Zeiger: sind sie identisch, hat du einen 
Überlauf, den zu behandeln je nach Anwendung verschieden sinnvoll ist. 
Sonst schreibst du das empfangene Zeichen am Schreibzeiger in den Puffer 
und erhöhst den Zeiger modulo Puffergröße. In der konsumierenden Routine 
vergleichst du auch die Zeiger, ist der Leseindex eins hinter dem 
Schreibindex (mod Größe), ist der Puffer leer. Ansonsten liest du am 
Leseindex und erhöhst diesen mod Größe.
Ist der Puffer groß genug, kannst du ihn auch direkt nehmen, um z.B. 
eine ganze Befehlszeile darin zu speichern (nur immer an den Umbruch 
denken).

Sorry für die Prosa, macht sich am Handy besser als Code reinzutouchen.

von Karl H. (kbuchegg)


Lesenswert?

PS
Wenn du es so schreibst
1
...
2
3
  while (1) {
4
    if( receive_char == 'A' || receive_char == 'a' )
5
      PORTB &= ~(1<<PB0);
6
7
    if( receive_char == 'B' || receive_char == 'b' )
8
      PORTB |= (1<<PB0);
9
  }
10
}

dann spielt es keine Rolle, ob du in deinem Terminal-Programm vergisst 
oder nicht vergisst, die Shift-Taste zu drücken um dann auch wirklich 
ein großes 'A' oder 'B' zu senden.

Und es ist auch meistens eine ziemlich gute Idee, wenn man das 
empfangene Zeichen wieder an den Sender zurückschickt, damit der eine 
Rückmeldung hat, was er eigentlich gedrückt hat.

von Stefan E. (sternst)


Lesenswert?

Malte S. schrieb:
> Stefan Ernst schrieb:
>> Wenn 5 Bits gesendet werden, der AVR aber 8 erwartet, wird
>> anscheinend das A trotzdem noch erkannt, das nachfolgende \r aber
>> verschluckt
>
> Welches \r?
>
>> dass wahrscheinlich(*) sofort ein "nächstes
>> Zeichen" kommt, und der Pin damit nur ca 1 ms lang low ist?
>
> Zwar wissen wir es nicht explizit, doch sieht alles danach aus, dass der
> TO einzelne Zeichen sendet.

Ja, jetzt, aber nicht nach den ersten Beiträgen.

> Von irgendetwas Zeilenorientiertem und den
> dazugehörigen Zeilenendzeichen war nun wirklich nie die Rede.

Davon, was er wie sendet, war GAR NICHT nicht die Rede. Es hieß nur 
"funktioniert nicht", ohne dass ich ein Problem im Code sah. Die 
Annahme, dass er in ein x-beliebiges Terminalprogramm A tippt, und dann 
Return drückt, und damit sein "funktioniert nicht" lediglich ein "der 
Pin ist so kurz low, dass ich es nicht mitbekomme" ist, ist ja nun nicht 
soo weit hergeholt, oder?

von Malte S. (maltest)


Lesenswert?

Nur, dass von Return Drücken keine Rede war :-)

von Karl H. (kbuchegg)


Lesenswert?

Könnt ihr bitte den Blödsinn bleiben lassen?

Es kommt doch nun wirklich jeden Tag x-fach vor, dass gerade Neulinge 
maximal die Hälfte (meistens noch viel weniger) von dem erzählen was 
eigentlich wichtig ist.

Die Hypothese, dass er 'A' - <Return> tippt ist genausowenig von der 
Hand zu weisen, wie die Hypothese, dass er es nicht tut. Bei Neulingen 
würde ich erst mal nichts, aber auch gar nichts, von vorne herein 
aussschliessen. Und das inkludiert auch unbeabsichtigte Fehlbedienung.

von Malte S. (maltest)


Lesenswert?

Klar ist das nicht von der Hand zu weisen, aber

R. B. schrieb:
> Und das beim nächsten Zeichen alles wieder high ist, ist mir klar!

sprach m.E. doch eher dagegen, sich darauf als Ursache so festzulegen. 
Nur gegen das Festlegen, nicht die grundsätzliche Option.

von R. B. (britzi)


Lesenswert?

Vielen Dank an alle für die super Hilfe.

Ich finde es besser, wenn man bei Anfängern auch ein bisschen mehr 
zwischen den Zeilen liest ;-)

Es war nur ein A ohne Return ;-)

Und der Tipp kleine und große Buchstaben als Eingabe für eine Aktion zu 
nehmen ist auch super :)

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.