Hallo zusammen,
ich komme gerade irgendwie nicht weiter, ich würde gerne eine Methode
schreiben, die einen Tastendruck von der Tastatur registriert und davon
abhängig dann eben etwas macht. Das ganze soll über die USART
Schnittstelle vom Mikrocontroller laufen, ich habe mir testweise auch
schon ein Echo Programm geschrieben, das funktioniert soweit auch schon,
allerdings wird da eben immer auf eine Eingabe gewartet und ich möchte
eben, dass das Programm weiterläuft wenn es mal keine Eingabe gibt und
dann nicht weiter wartet.
Da ich dabei nicht wirklich weitergekommen bin, wollte ich mir Schritt
für Schritt anschauen was mit dem UCSR1A Register passiert, wenn ich
Tasten drücke, allerdings kommt mir hier auch etwas komisch vor.
Und zwar habe ich folgende Methode mal erstellt:
1
unsignedchargetc_USART1()
2
{
3
while((UCSR1A&(1<<RXC1))==0);
4
returnUDR1;
5
}
Wenn ich das dann in der folgenden Funktion so ausführe funktioniert es
auch, das Programm bleibt an der while Schleife von getc_USART1() stehen
bis ich in der Konsole eine Taste drücke.
1
intmain_test()
2
{
3
init_USART1();
4
unsignedchartemp=0;
5
while(1)
6
{
7
temp=UCSR1A;
8
getc_USART1();
9
}
10
}
Wenn ich das allerdings so ausführe bleibt das Programm im Debugger in
der while((UCSR1A & (1<<RXC1)) == 0); Schleife hängen, es sollte doch
das gleiche sein wie wenn ich die Funktion getc_USART1() aufrufe?
Was hier eventuell noch wichtig zu erwähnen ist, ich benutze einen
Simulator für den Mikrocontroller. Nennt sich AVRORA gdbserver in den
Debug Einstellungen, falls das jemanden was sagt.
1
intmain_test()
2
{
3
init_USART1();
4
unsignedchartemp=0;
5
while(1)
6
{
7
while((UCSR1A&(1<<RXC1))==0);
8
returnUDR1;
9
}
10
}
Aber das ist eigentlich nebensächlich, wichtiger wäre mir wenn ihr mir
weiterhelfen könnt, wie ich immer nur einfach prüfen kann, ob eine Taste
gedrückt wurde an der Tastatur oder nicht, ich habe das auch schon wie
folgt versucht, nur funktioniert das nicht, egal ob ich eine Taste
drücke oder nicht, es wird immer 255 zurückgegeben.
Peter M. schrieb:> Was hier eventuell noch wichtig zu erwähnen ist, ich benutze einen> Simulator für den Mikrocontroller. Nennt sich AVRORA gdbserver in den> Debug Einstellungen, falls das jemanden was sagt.
Nicht wirklich.
Aber ich traue Simulatoren an dieser Stelle auch nicht wirklich über den
Weg. Speziell wenn es darum geht, externen Input korrekt in die
Simulation einfliessen zu lassen
>
1
intmain_test()
2
>{
3
>init_USART1();
4
>unsignedchartemp=0;
5
>while(1)
6
>{
7
>while((UCSR1A&(1<<RXC1))==0);
8
>returnUDR1;
9
>
und warum breakst du dich aus main raus, sobald du ein Character von der
UART kriegst?
> weiterhelfen könnt, wie ich immer nur einfach prüfen kann, ob eine Taste> gedrückt wurde an der Tastatur oder nicht,
vergiss die Tastatur. Dein µC weiss nichts von einer Tastatur. Der hat
eine UART, eine serielle Schnittstelle. An der ist entweder ein byte
angekommen oder es ist keines angekommen. Wer auf der Gegenseite die
Bytes auf den Weg bringt, spielt keine Rolle. Das kann eine Tastatur
sein, muss es aber nicht.
> if((UCSR1A & (1<<RXC1)) == 1)
Ein Vergleich auf 1 ist nicht dasselbe wie die Forderung, dass das
Ergebnis der Und-Verknüpfung ungleich 0 sein muss. Auch eine 4 wäre
ungleich 0. Insbesondere hängt es von der genauen Bitposition des Bits
RXC1 innerhalb des Registers ab, welche Zahl dann konkret übrig bleibt,
sollte das Bit tatsächlich auf 1 sein.
Du kennst doch die Technik schon.
Hier
1
unsignedchargetc_USART1()
2
{
3
// warte solange bis auf der UART ein Zeichen reinkommt
4
while((UCSR1A&(1<<RXC1))==0)
5
;
6
...
D.h. du hast hier gefragt, ob der Inhalt des Registers UCSR1A zu 0 wird,
nachdem man alle anderen Bits bis auf das Bit RXC1 ausblendet.
Und jetzt drehst du das eben um. Nur darfst du eben nicht vergleichen ob
das Ergebnis von dieser Und-Operation gleich 1 wäre.
Das Gegenteil von
1
...==0
lautet
1
...!=0
und nicht
1
...==1
Steht links vom Vergleich beispielsweise ein Ausdruck, der ausgerechnet
4 ergibt, dann ist die Bedingung
1
...4==0
nicht erfüllt. Das gegenteil davon muss daher erfüllt sein.
1
4!=0
ist auch tatsächlich richtig. Aber
1
4==1
ist es nicht.
Wenn du forderst, dass der ausgerechnete Wert 1 ergeben muss, dann muss
da auch wirklich 1 rauskommen und nichts anderes. Es gibt aber viele
Zahlen, die nicht 0 sind, aber allerdings auch nicht 1.
Ja so langsam habe ich auch fast die Befürchtung, dass mir der Simulator
einen Streich spielt.
Ich hätte das ganze in der Uni mal ausprobieren sollen mit dem "realen"
Mikrocontroller...
Der break aus der Main hat nicht wirklich einen Sinn ja, ich habe da im
Prinzip nur den Code aus der einen Methode kopiert um zu sehen, ob da
das gleiche rauskommt wenn ich das in der main direkt ausführe, was ja
nicht passiert ist, wieso auch immer, ist jetzt aber auch erstmal nicht
so wichtig.
Die Tastatur habe ich explizit erwänht, damit ich deutlich machen kann,
dass es hier nicht um die Taste auf dem Mikrocontroller geht, aber hätte
man sich wahrscheinlich auch schon denken können, wenn es um die UART
Schnitstelle geht.
Der Erklärung bezüglich der Vergleiche von dir konnte ich nicht folgen.
ein Bit hat doch immer nur den Zustand 0 oder 1, dementsprechend wenn
ich jetzt sage, dass Und-Operation ungleich 0 ist, kann ich doch auch
sagen, dass die 1 sein soll, weil etwas anderes kann ja "ein" Bit nicht
sein, wenn es ungleich 0 sein soll, jetzt mal abgesehen von den
undefinierten Zuständen.
Ich habe jetzt aber auch nochmal ein bisschen recherchiert und auch noch
bei euch hier ein Tutorial bei euch zu dem Thema gefunden:
https://www.mikrocontroller.net/articles/AVR-GCC-Tutorial/Der_UART
Ich habe das dann jetzt mal für meinen Anwendungszweck angepasst, aber
ich komme nach wie vor nur in den else Zweig, egal wie oft ich eine
Taste vorher drücke...
1
intmain_test()
2
{
3
init_USART1();
4
unsignedchartemp=0;
5
while(1)
6
{
7
8
if((UCSR1A&(1<<RXC1)))
9
{
10
// Zeichen wurde empfangen, jetzt abholen
11
uint8_tc;
12
c=getc_USART1();
13
// hier etwas mit c machen z.B. auf PORT ausgeben
14
temp=c;
15
}
16
else
17
{
18
temp='f';// Kein Zeichen empfangen, Restprogramm ausführen...
19
}
20
21
}
22
}
Dann habe ich es auch noch mit der folgenden if-Abfrage versucht
if ( (UCSR1A & (1<<RXC1)) != 0 )
Das war ja dein Vorschlag, wenn ich dich richtig verstanden habe, hier
komme ich aber ebenfalls auch immer nur in den else Zweig.
Peter M. schrieb:> Der Erklärung bezüglich der Vergleiche von dir konnte ich nicht folgen.> ein Bit hat doch immer nur den Zustand 0 oder 1
wir arbeiten in C aber nicht mit einzelnen Bits. Wir arbeiten immer mit
einem Byte. Das besteht aus 8 Bit.
Allerdings kann man in einem Byte bestimte Bits auf 1 setzen oder auf 0
setzen.
Genau das passiert hier auch. Angenommen das Bit mit dem Namen RXC wäre
das Bit3 im Register
1
RXC1
2
+---+---+---+---+---+---+---+---+
3
|||||||||
4
+---+---+---+---+---+---+---+---+
dann kriegst du mittels Zugriff auf das Register
1
UCSRA1
das komplette Register - alle 8 Bit. Die können zb so stehen
1
01101001
da sind einige Bits auf 0 und einige auf 1.
Gut.
Die anderen interessieren dich nicht, dich interessiert nur das Bit 3,
welches das RXC ist. Um dessen Wert festzustellen, möchtest du also die
anderen weg haben. Damit die weg sind, zwingst du sie auf 0. Mit einer
Und Operation und einer entsprechenden Mask
1
if(UCSRA1&(1<<RXC1))
rechnerisch machst du auf Bitebene
1
UCSRA1 01101001
2
(1<<RXC1) 00001000 &
3
-------------
4
00001000
du zwingst also alle Bits ausser dem Bit RXC1 auf 0. An der Position
RXC1 bleibt damm im Ergebnis ein 1 Bit zurück (so wie hier) oder eben
ein 0 Bit.
Aber das ganze Byte hat nicht den Zahlenwert 1! Das Bitmuster 00001000
entspricht dezimal dem Zahlenwert 8 und nicht 1. Da wird das ganze
entstehende Byte dem Vergleich unterzogen.
Du solltest dich eigentlich schon mit den sog. Bitoperationen beschäftgt
haben! LEd einschalten, LED ausschalten, Taster abfragen oder eben auch
feststellen welchen Zustand ein Bit in einem Register hat. Das alles
sind immer nur Variationen von ein und demselben: Mittels Und und Oder
Operationen bzw. entsprechenden Masken einzelne Bits in einem Byte zu
manipulieren. In der µC-Programmierung ist das eine Grundtechnik, die du
im Schlaf beherrschen musst.
Bitmanipulation>
1
intmain_test()
2
>{
3
>init_USART1();
4
>unsignedchartemp=0;
5
>while(1)
6
>{
7
>
8
>if((UCSR1A&(1<<RXC1)))
9
>{
10
>// Zeichen wurde empfangen, jetzt abholen
11
>uint8_tc;
12
>c=getc_USART1();
13
>// hier etwas mit c machen z.B. auf PORT ausgeben
14
>temp=c;
15
>}
16
>else
17
>{
18
>temp='f';// Kein Zeichen empfangen, Restprogramm ausführen...
19
>}
20
>
21
>}
22
>}
Zeig bitte gaze Programme und wenn du 'irgendetwas machen' willst, dann
lass dein Programm etwas machen, was der Compiler nicht wegoptimieren
kann. Zum Beispiel einen wirklichen Port Zugriff
1
...
2
if((UCSR1A&(1<<RXC1)))
3
{
4
uint8_tc=getc_USART1();
5
PORTB=c;
6
}
> Dann habe ich es auch noch mit der folgenden if-Abfrage versucht> if ( (UCSR1A & (1<<RXC1)) != 0 )
das ist dasselbe.
In C verlangt ein if keinen Vergleich. In C verlangt ein if einen
Ausdruck, den es auswertet. Entweder ergibt der Ausdruck ungleich 0,
dann wird der then Teil genommen, oder der Ausdruck ergibt 0, dann wird
der else Teil genommen. Ja, in C ist auch ein Vergleich einfach nur eine
Operation, die ein Ergebnis liefert und nichts besonderes.
Ja du hast natürlich recht mit der Byte Betrachtung, da stand ich wohl
ganz schön auf dem Schlauch.
Welchen Teil willst du den noch vom Programm, es fehlt ja eigentlich nur
noch die init_USART1 Methode, aber an der kanns ja nicht liegen, weil
meine Echo Programm, also warte auf Eingabe und gib diese über USART auf
der Konsole aus funktioniert tadellos.
Im then Zweig ist doch auch schon die getc_USART1 Methode ein
Portzugriff, aber ich probiere nachher den Vorschlag von dir aus, nur
habe ich da nicht besonders viele Hoffnungen...
Peter M. schrieb:> Ja du hast natürlich recht mit der Byte Betrachtung, da stand ich wohl> ganz schön auf dem Schlauch.>> Welchen Teil willst du den noch vom Programm,
die Uart Initialisierung. Gibst du den Empfänger überhaupt frei? Hast du
Interrupts eingeschaltet?
> noch die init_USART1 Methode, aber an der kanns ja nicht liegen, weil> meine Echo Programm, also warte auf Eingabe und gib diese über USART auf> der Konsole aus funktioniert tadellos.
Darauf gebe ich nichts.
Im Laufe der Jahre habe ich gelernt, alles in Frage zu stellen ehe es
nicht überprüft wurde. Und sei es noch so trivial. Und in einer
erklicklichen Anzahl von Fällen habe ich damit durchschlagenden Erfolg.
//UCSR1C |= (1<<UCSZ11); //8 Datenbits; schon per Default auf 8 Datenbits eingestellt
9
//UCSR1C |= (1<<UCSZ10);
10
11
UCSR1C|=(1<<USBS1);//2 Stopbits
12
13
//UCSR1C &= ~(1<<UPM11); //keine Parität; schon per Default eingeschaltet
14
//UCSR1C &= ~(1<<UPM10);
15
16
//UCSR1C &= ~(1<<UMSEL1); //asynchroner Mode; per Default schon eingeschaltet
17
}
Ich weiß zwar nicht wie da was falsch sein kann, wenn das Echo Programm
funktioniert, aber vielleicht werde ich ja jetzt eines besseren belehrt.
Empfänger habe ich wie in den Kommentaren auch beschrieben aktiviert und
funktioniert ja auch im Echo Programm, Interrupts nutze ich nicht,
zumindestens nicht bewusst, die setzen wir erst aktiv im nächsten
Versuch ein, daher gehe ich davon aus, dass es wohl hier auch gehen muss
ohne das man die Interrupts selbst aktiviert oder selber welche nutzt.
Ich habe es eben noch mit deinem Vorschlag ausprobiert, das folgende in
den then Zweig einzufügen
PORTB = c;
Das Programm springt aber nach wie vor immer nur in den else Zweig,
unabhängig davon ob ich eine Taste drücke oder nicht.
Ich liebe es, wenn ich mir aus mehreren Postings die Einzelteile
zusammensuchen muss.
Brenn das mal. Brenne es genau so und nicht anders. Die einzigen
Anpassungen sind bei F_CPU an deine tatsächliche Taktfrequenz und bei
BAUD auf die zu verwendende Baudrate erlaubt.
So dann melde ich mich mal zurück.
Zunächst erst einmal Danke! Ich habe den Fehler nun gefunden, bzw. es
funktioniert jetzt wenigstens.
Also zuerst einmal habe ich den Programmcode von dir getestet und es hat
auf Anhieb funktioniert.
Dann habe ich nochmal meinen Code probiert und es hat plötzlich auch
funktioniert.
Allerdings habe ich jetzt etwas anders gemacht und zwar habe ich im
Debugger die Endlosschleife jetzt automatisch immer durchlaufen lassen,
bei meinen letzten Versuchen bin ich immer Zeile für Zeile mit Step Into
oder Step over im Debugger durchgegangen und da hat es nicht
funktioniert, selbst wenn ich um die 20 Schleifendurchgänge gemacht
habe.
Zudem habe ich auch festgestellt, wenn ich im else Zweig einen Zugriff
auf den UART Port mache, also z.B. UDR1 = c; dann springt er beim
manuellen durchgehen im Debugger nach einem weiteren Schleifendurchgang
tatsächlich in den then Zweig, wenn ich allerdings im else Zweig keinen
Zugriff auf den UART Port mache, beispielsweise einfach nur temp = c
dann springt er nicht in den then Zweig, selbst nach 20-30
Schleifendurchgängen nicht, allerdings wenn ich das Programm im
Debugger wieder automatisch laufen lasse, funktioniert es auch mit temp
= c im else Zweig.
Ich schätze mal mir hat dann wohl entweder der Simulator oder der
C-Compiler, oder auch beide zusammen einen Streich gespielt.
Der Vergleich mit ==1 wie in meinem ersten Post dargestellt war
natürlich auch ein ziemlich dämlicher Fehler :P
Zum Abschluss möchte ich mich nochmal herzlich bedanken und vielleicht
hat ja noch jemand eine Antwort darauf, wodurch der Fehler verursacht
wurde.
Peter M. schrieb:> tatsächlich in den then Zweig, wenn ich allerdings im else Zweig keinen> Zugriff auf den UART Port mache, beispielsweise einfach nur temp = c> dann springt er nicht in den then Zweig, selbst nach 20-30> Schleifendurchgängen nicht, allerdings wenn ich das Programm im> Debugger wieder automatisch laufen lasse, funktioniert es auch mit temp> = c im else Zweig.
Ich denke was du hier siehst, das sind Auswirkungen des OPtimizers
deines Compilers, der dein Programm umstellen darf, solange die nach
aussen sichtbaren Effekte dieselben bleiben. D.h. zum Beispiel dass der
OPtimizer Variablen hinauswerfen darf, wenn von ihnen nichts abhängt.
Wenn du nur zu, "Spass an der Freude" an eine Variable x etwas zuweist,
aber mit diesem x weiter nichts machst, dann hat diese Zuweisung an x ja
keine Auswirkungen ausser das sie Zeit verbraucht. Also kann der
Optimizer die Zuweisung rauswerfen. Womit dann unter Umständen die ganze
Variable sinnlos geworden ist. Denn eine Variable an die nichts
zugewiesen wird und deren Wert nirgends verwendet wird, die braucht auch
keiner. Wenn aber diese hinausgeworfene Zuweisung das einzige war, was
in einem else (beispielsweise) passiert, dann ist auch das else sinnlos
geworden usw. usw.
Compiler sind recht gut darin, derartige Dinge zu entdecken und deinen
Programmtext entsprechend umzubauen.
Ich denke, dass so etwas in dieser Richtung dir passiert ist.
ok hört sich sinnvoll an, also sollte ich mir für das nächste mal
merken, lieber immer auf einen PORT zugreifen und eben etwas über UART
ausgeben, LEDS blinken lassen, etc.
Ist dann die Begründung, dass irgendwann doch der then Zweig ausgeführt
wird, dass der Compiler dann feststellt, dass er immer und immer wieder
an den gleichen Punkt kommt und dann eben doch diese sinnlose Zuweisung
durchführt?
Peter M. schrieb:> ok hört sich sinnvoll an, also sollte ich mir für das nächste mal> merken, lieber immer auf einen PORT zugreifen und eben etwas UART> ausgeben lassen, oder LEDS blinken lassen, etc.
genau.
> Ist dann die Begründung, dass irgendwann doch der then Zweig ausgeführt> wird, dass der Compiler dann feststellt, dass er immer und immer wieder> an den gleichen Punkt kommt und dann eben doch diese sinnlose Zuweisung> durchführt?
Nein.
Das Problem ist, dass das was du im Debugger siehst nicht mehr viel mit
dem tatsächlich übersetzten Programm zu tun hat. Sprich: du kannst
deinem Debugger nicht mehr vertrauen, dass seine Anzeige, wo das
Programm gerade steht auch der Realität entspricht. Denn das was du
geschrieben hast, stimmt nicht mehr 1:1 mit dem überein, was der
Compiler/Optimizer dann daraus gemacht hat. Die Zeilen deines Programmes
finden sich nicht mehr im tatsächlich laufenden Programm wieder. Der
Debugger muss aber seinen Pfeil der gerade abzuarbeitenden
Anweisung/Zeile irgendwo hinstellen. Und genausogut kann es im Programm
dann durch Umstellungen durch den Optimizer soweit kommen, dass es für
das was gerade abgearbeitet wird überhaupt an dieser Stelle keine
Entsprechung in deinem ursprünglichen Programm gibt.
D.h. in Kurzform: Wenn der Optimizer aktiv war, dann sind derartige
Dinge im Debugger mit Vorsicht zu geniessen. Da stimmt nicht alles, was
du angezeigt bekommst. D.h. bei der Aussage "funktioniert nicht" ist es
auch immer gut zu wissen: Siehst du im Debugger etwas überraschendes
oder verhält sich die tatsächliche Hardware nicht so wie erwartet. Wenn
im Debugger einzelne Anweisungen scheinbar übersprungen werden, dann ist
das noch kein Grund zur Panik - bei aktivem Optimizer. Ist der Optimizer
abgeschaltet, dann allerdings besteht Grund zur Panik :-)
ah ok jetzt bin ich schon ein ganzes Stück schlauer, Danke!
Ich denke ich sollte mich dann mal schlau machen wo ich den Optimizer in
Eclipse deaktivieren kann, macht das ganze ja dann beim Debuggen doch um
einiges leicher, oder habe ich dann andere/neue Probleme, wenn ich den
Optimizer deaktiviere?