hi,
ich hab mal der Übersicht halber nen neuen Zhread aufgemacht...alter:
Beitrag "UART Programm (Umwandlung von Assembler zu C)"
ich hatte die Platine jetzt mal am PC. Und wenn ich 15 (Hexa) sende
kommt nix
einmal hatte der µC "D3" gesendet, aber das war wohl nen Fehler...also
irgendwie reagiert er nicht auf den Interrupt.
noch was. Gleich am Anfang wird ja Receive1 aufgerufen:
1
intReceive0(void)
2
{
3
while(!ucUDR_valid)
4
{
5
}
6
ucUDR_valid=0;
7
returnucUDR;
8
}
und dort hängt es ja die ganze Zeit fest, bis ein Interrupt ausgelöst
wurde. Das finde ich etwas ungünstig, da ich ja mit 2 Geräten an den µC
senden möchte.
Im Anhang nochmal der C Code
> Gleich am Anfang wird ja Receive1 [korr. Receive0] aufgerufen und> dort hängt es ja die ganze Zeit fest, bis ein Interrupt ausgelöst wurde.
Genau, Receive0 wartet, bis ein Zeichen empfangen wurde. Das ist in
deinem ASM-Code eigentlich genauso.
> Das finde ich etwas ungünstig, da ich ja mit 2 Geräten an den µC> senden möchte.
Dann prüfe doch vor dem Aufruf von Receive0, ob ein Zeichen empfangen
wurde.
Speck das Programm soweit ab, bis nur die reine Kommunikation auf UART0
getestet werden kann. Ein einfaches ECHO-Programm wäre z.B.
1
#include<avr/io.h>
2
#include<avr/interrupt.h>
3
#include<inttypes.h>
4
#include<util/delay.h>
5
6
#define BAUD 9600L // Baudrate, das L am Ende ist wichtig, NICHT UL verwenden!
Ich habe mir das mit dem Makro STEFB etwas zurecht gemacht, weil ich
keinen Atmega162 zum Testen habe. Ich habe es auf einem Atmega32 (hat
nur ein UART) @ 13,560 MHz laufen lassen. Da drauf läuft es bei
Terminaleinstellung 9600,8N2
OR ;-)
Das logische ODER ist in C ein ||
Solange in nicht (UART0 gültig oder UART1 gültig) Däumchendrehen. Das
ist auf Deutsch solange UART0 ungültig und UART1 ungültig.
Das ist zwar OK, aber ein unötiger Bugfix für den folgenden vermurksten
if-Fall ;-)
Wenn (if) UART0 gültig dann
prüfe UART0 auf 0x15, ...
andernfalls (else)
prüfe UART1 auf 0x15, ...
Ich würde eher diese Programmstruktur als logisch ansehen:
Wenn UART0 gültig dann
prüfe UART0 auf 0x15, ...
Wenn UART1 gültig dann
prüfe UART1 auf 0x15, ...
Ansonsten im Rest steckt ein Schlampifehler:
gehen tuts leider immer noch nicht...es kommt sofort, wenn ich was sende
nen Error...irgendwie erkennt der den Interrupt nicht oder irgendwas
geht da schief...wie gesagt ich kann leider nicht emulieren...
noch was beim Ersten mal senden Kommt der Error sofort...beim 2. Mal
dauert es länger. D.h. beim ersten mal bekommt er schon irgendwie ne
Antwort aber irgendwas ist falsch. Und beim 2. mal hängt er irgendwo und
reagiert gar nicht mehr...
Sorry, da kann ich dir nicht helfen. Ich kann dir nur den Tipp geben,
mit einem PC das Echo Programm vom µC zu testen.
Was passiert, wenn du den Casio an den PC anhängst und in einem
Terminalprogramm für die serielle Schnittstelle die µC Seite von Hand
emulierst?
> ich kann das Programm leider auch nicht simulieren, da ich noch nix> derartiges für einen Mac gefunden habe.
Du hast keinen PC sondern einen Mac? Welchen? Hat der eine RS232
serielle Schnittstelle? Dafür gibt es Programme...
http://homepage.mac.com/dalverson/zterm/http://www.furrysoft.de/?page=goserial
Tipp: 0x06 ist CONTROL-E
http://www.cs.tut.fi/~jkorpela/chars/c0.html
Nein ich hab keine Serielle Schnittstelle. Also Rechner an PC ist nicht
wirklich interessant...da weis ich ja wie es funktioniert und was
passiert. Den µC hatte ich ja auch schon mal am Rechner (anderer PC).
Und wenn ich da 0x15 gesendet habe ist nix passiert bei der ASM Variante
schon. Also kann es eigentlich nur was formales am Code sein.
so ich hab den Fehler jetzt schonmal eingegrenzt...
1
intReceive0(void)
2
{
3
4
5
while(!ucUDR_valid0)
6
{
7
}
8
ucUDR_valid0=0;
9
returnucUDR0;
10
}
wenn ich den Port nach cdUDR_valid0 = 0; auf high setze wird er high...
1
// Header Rrecieve 0
2
for(Count=0;Count<50;Count++)
3
{
4
//Header speichern
5
Header0[Count]=Receive0();
6
}
7
8
PORTA=0x03;
hinter diese schleife kommt er nicht mehr. Also hängt es da drinn!
Das: sbis UCSR0A, RXC habe ich doch noch gar nicht umgesetzt oder
geschieht das automatisch durch den Interrupt?
es muss was beim Empfangen schiefgehen...der Controller geht zu schnell
kann eigentlich nicht sein in ASM gehts auch ohne Wait...oder wie gesagt
der µC wartet nicht, bis das byte empfangen wurde und der Rechner bricht
dann ab...und er hängt in der Schleife fest und wartet darauf, das etwas
gesendet wird...
> in ASM gehts auch ohne Wait...
Der Vergleich mit dem ASM-Programm nervt mich inzwischen etwas. Dann
beschäftige dich mit deinem ASM-Programm. Ich versuche keinesfalls den
kruden Mix aus Interrupt-Betrieb und Polling-Betrieb mit
Kreuzundquersprüngen in einem so toll kommentierten ASM-Listing
aufzudröseln. Ich helfe aber gerne die grundlegende Umsetzung auf einen
sauberen Interruptbetrieb in C-Code zu zeigen (weil das etwas ist, was
auch mich im Lernstoff "UART und Interrupts" weiter bringt). Wenn du
damit Probleme hast, sag's und ich nerve dich nicht weiter.
Das "Hängen in der Schleife" ist in der derzeitigen C-Version fast
unausweichlich. Ein Minimal-1-Byte-Puffer ist prädestiniert für
verlorene Zeichen. Ich hatte dir eine Variable eingebaut, wo du prüfen
kannst, ob Zeichen verloren gehen.
Da wie beschrieben Receive0() wartet bis ein Zeichen da ist und weil
dein for() unbedingt 50 Zeichen für den Header haben will, hängt dein
Programm, wenn der Casio keine 50 Zeichen sendet und/oder Zeichen
verloren gegangen sind.
Ich kann auch gerne eine Variante aufzeigen, mit einem grösseren Puffer.
Oder eine Variante mit einem Zähler für die empfangenen Zeichen. Hast du
am kompletten PORTA Debug-LEDs (8 Stück?), so dass man da eine Zahl
0..255 (verlorene Zeichen oder empfangene Zeichen) ausgeben kann?
Es tut mir ja leid mit dem ASM Vergleich, aber an irgend etwas muss ich
mich ja orientieren. In C habe ich wie gesagt noch nicht soviel Praxis
Erfahrung. Ich habe an PortA gar keine LED's leider ist das teil wie
gesagt schon auf einer Platine. Ich habe aber mit einem Multimeter
nachgemessen. Du kannst mir ja mal bitte deine Variante geben. Ich mess
dann einfach den Kompletten Port durch.
Florentin S. wrote:
> Es tut mir ja leid mit dem ASM Vergleich, aber an irgend etwas muss ich> mich ja orientieren.
Ja. Du sollst dir aus dem Assembler Code rausholen, welche
Bytes in welcher Reihenfolge daherkommen. Eventuell kann
man aus dem Assembler Code auch noch die Auswertung studieren
und rausfinden was in welchem Fall gemacht werden muss.
Aber ansonsten: Vergiss den Assembler Code und setzte eine
ordentliche C Struktur auf.
Blöd ist natürlich, dass du keine vernünftigen Debug-
Möglichkeiten hast. Ein paar LED an einem Port sind
nicht wirklich ein Ersatz um die Kommunikation zu
überprüfen.
Ich würde mal folgendes machen:
Ein minimal UART Programm aufsetzen, das Interrupt getrieben
ist und beim Empfang eines Zeichens einen Port Pin auf
High (oder Low) setzt. Um das Handling zu vereinfachen einfach
mal mit ein paar Drähten eine Led (samt Vorwiderstand) an
besagtem Port Pin anlöten (wenn deine Platine schon soweit
fertig ist, dass du keine Led mehr einbauen kannst. Welcher
Teufel hat dich eigentlich geritten eine fertige Platine zu
machen, noch bevor ein Testaufbau auf einem Steckbrett oder
auf Streifenraster die grundsätzliche Funktionsweise nachgewiesen
hat).
Warteschleifen sind Mist!
Du willst sie nicht haben. Die einzige Warte/Endlosschleife ist
die Schleife in main.
1
....
2
3
ISR(USART0_RXC_vect)
4
{
5
if(ucUDR_valid)
6
{
7
uiVerloreneZeichen++;
8
// Die Auswertung dieser Zahl kann spaeter helfen, wenn man den
9
// 1 Byte Puffer in einen Mehrbyte-Puffer nach dem FIFO Prinzip
10
// umruesten will. Damit kann man eine Abschaetzung der guenstigen
11
// Puffergroesse vornehmen.
12
}
13
14
ucUDR=UDR0;
15
ucUDR_valid=1;
16
// Nicht mehr!
17
}
18
19
intmain()
20
{
21
...
22
23
sei();
24
25
while(1){// dies ist die einzige Schleife, die auf etwas
26
// wartet. Sie wartet auf Arbeit
27
28
if(ucUDR_valid0){// Ein Zeichen ist auf UART 0 angekommen
29
cli();
30
c=ucUDR0;// hier ist es
31
32
PORTxxxx=.....// Led ein
33
ucUDR_valid0=0;
34
sei();
35
}
36
}
37
}
ergänze mal die fehlenden Teile für Initialisierung und sieh nach,
ob deine LED eingeschaltet wird. Wenn ja, dann hat der Interrupt
ausgelöst.
ich werd das dann mal ausprobieren. Aber der Interrupt löst aus...das
habe ich ja schon probiert es hängt definitiv in der Warteschleife in
der Reveive Routine. Es müssten eigentlich Zeichen verloren gehen...denn
die Anzhal in der Schleife stimmt laut Protokoll.
Die Platine ist fertig, weil das ASM Prog wie gesagt lief...und ich habe
mich jetzt nur entschlossen, das nochmal in C zu schreiben, weil ich von
ASM weg will und es mir zu kompliziert zu erweitern ist.
Florentin S. wrote:
> habe ich ja schon probiert es hängt definitiv in der Warteschleife in> der Reveive Routine.
Eine derartige Warteschleife willst du nicht haben.
Die ganze Funktion ist unnötig.
Dein Programm will nicht aktiv darauf warten, dass auf der
Seriellen Schnittstelle 50 Bytes eintrudeln.
Du musst deine Denkweise umstellen und anfangen Ereignis-
orientiert zu denken: Auf der UART ist ein Byte engetrudelt,
was mache ich damit?
Für diesen Gedankengang ist die adequate Programmierweise die
Hauptschleife in main(). Wenn ein Byte an der UART eintrudelt,
dann teilt die Interrupt Routine das dem Rest der Welt mit,
indem sie eine globale Variable (das berüchtigte ucUDR_valid)
auf 1 setzt. Und dein 'Eventdispatcher' (ein schönes Wort
für sowas einfaches) reagiert darauf, indem er im nächsten
Durchlauf diese 1 sieht und entsprechend reagiert.
Aber dein Programm wartet nirgends aktiv darauf, dass diese
Variable irgendwann 1 wird. Dein Programm prüft ständig in
einer Schleife ob irgendeine dieser Benachrichigungsvariablen
zu 1 geworden ist und behandelt dann diesen Fall (dieses Ereignis)
1
while(1){// diese Schleife ist dein 'Eventdispatcher'
2
3
if(ucUDR_valid){// und das ist der Event der aufgetreten
4
// ist. Wenn das hier auf 1 geht, dann
5
// ist das Ereignis: 'UART hat ein Byte
6
// empfangen' eingetreten. Jetzt heist
7
// es auf dieses Ereignis zu reagieren.
8
9
// reagieren kann zb heissen, dass das
10
// Byte in einem Buffer zwischengespeichert
11
// wird und geprüft wird ob das nicht schon
12
// das 50.te Byte war. Wenn es das 50.te
13
// Byte war, dann ist zb. der Header fertig
14
// empfangen worden und kann ausgewertet
15
// werden (nur so als Beispiel).
16
...
17
}
18
}
Das ist deine Grundstruktur. Für jedes Ereignis welches auftreten
kann, findet sich in dieser while Schleife eine if-Abfrage.
Der Code der dann ausgeführt wird, behandelt dieses Ereignis.
Nirends muss auf irgendwas gewartet werden (schon alleine
deshalb, weil es keine Warteschleifen mehr gibt) und quasi
zeitgleiche Ereignisse werden nacheinander so schnell wie möglich
abgearbeitet.
Karl heinz Buchegger wrote:
> Eine derartige Warteschleife willst du nicht haben.> Die ganze Funktion ist unnötig.
Sag' doch so was nicht ;-)
Wenn unbedingt X Bytes eingelesen werden müssen, kann man das schon
sinnvoll mit einer wartenden Funktion machen. Die Alternative zur
Funktion wäre ja eine Latte Statements direkt im in Hauptcode. Und
gewartet werden müsste ja trotzdem.
Ich gebe dir Karl heinz so weit Recht, als dass eine
"Programm-muss-jetzt warten-bis-50-Bytes-angekommen-sind" Strategie in
der Praxis ziemlich unglücklich ist. Aber das zu beheben, würde einen
Programmablauf z.B. mit Timeoutbehandlung im Protokoll voraussetzen. So
weit sind wir noch lange nicht ;-)
In the meantime... ist bloss nachzusehen ob bzw. sicherzustellen, dass
beim Empfang keine Zeichen verloren gehen. Ich mache heute abend
Beispielcode fertig.
Schade, dass Florentin immer noch keine Möglichkeit gefunden hat, um den
Datenverkehr mit dem µC ohne den Casio zu testen. Es wäre einfacher,
wenn man gleiche Testmöglichkeiten hat und auch abgespeckte Teilaspekte
lösen kann, statt mit der Meldung "Error!" irgendwo in einem
achtstufigen Protokoll stecken zu bleiben.
ADD: Karl heinz, ich sehe gerade, dass du die Antwort ergänzt hast. Der
Ansatz gefällt mir gut. Es ist doch kein so grosser Schritt es gleich
richtig zu machen.
hi,
danke nochmal die Hinweise. Die Main habe ich jetzt so umgewandelt, dass
die auch ohne die Schleife Funktioniert. Nur verstehe ich jetzt nicht
ganz, was du meinst...soll ich die Receive Funktion jetzt komplett
weglassen? Und alles in die Main integrieren? Im Anhang nochmal der
Code. Soll ich einen Zähler machen, der die Bytes zählt? Oder wie?
Mit dem UART ist mist, das ich das nicht am PC testen kann...ich werde
mir so einen USB RS232 Wandler besorgen, damit ich auch am Mac debuggen
kann.
Entweder hast du den falschen Code hochgeladen oder du hast das Prinzip
nicht verstanden.
Ich sehe im Moment nur, dass die Vorschläge von Karl heinz nicht
realisiert sind und der wichtige Bugfix (cli()-sei()-Klammer um Zugriff
auf gemeinsame Variablen zwischen ISR und Nicht-ISR-Code) wieder fehlt.
Was anderes...
Was hat es mit dem UART1 auf sich? Im Moment ist dort der gleiche Code
wie auf UART0. Willst du zwei Casio an ein Atmega162-Board hängen? Wenn
ja, was ist die Aufgabe des Atmega162-Boards, Datentransfer zwischen den
beiden Casio (würde aber so nicht funktionieren, weil beide zum
Atmega162 senden)?
Ich habe das Prinzip schon teilweise verstanden, aber wenn ich die
Interrupts deaktiviere funktioniert das Receive ja logischerweise so
nicht mehr. Ich bin noch am überlegen, wie ich das am besten umsetze. Ja
an den anderen UART kommt ein 2. Rechner ran. Der µC ermöglicht die
direkte Kommunikation
zwischen 2 Rechnern im Programm, was so nicht funktioniert, da der 1ne
Rechner ein anderes Handshake sendet, als der andere erwartet.
>while(1){// dies ist die einzige Schleife, die auf etwas
9
>// wartet. Sie wartet auf Arbeit
10
>
11
>if(ucUDR_valid0){// Ein Zeichen ist auf UART 0 angekommen
12
>cli();
13
>c=ucUDR0;// hier ist es
14
>
15
>PORTxxxx=.....// Led ein
16
>ucUDR_valid0=0;
17
>sei();
18
>}
19
>}
20
>}
21
>
Der test hatte übrigens funktioniert. Der Port war auf High.
Nur wie gesagt ich müsste ja jetzt das empfangen anders gestalten, wenn
ich nach dem ersten empfangen die Interrupts deaktiviere.
vorweg: Ich weiss nicht, wie ich es dir noch anders erklären
soll, ohne dass ich dein Programm schreibe.
> was du meinst...soll ich die Receive Funktion jetzt komplett> weglassen?
Ja
> Und alles in die Main integrieren?
Na ja. Soviel bleibt in der Receive Funktion nicht übrig,
wenn man die Warteschleife rauswirft. Im Grunde bleibt dann
ja nur eine Zuweisung übrig :-)
> Nur wie gesagt ich müsste ja jetzt das empfangen anders gestalten, wenn> ich nach dem ersten empfangen die Interrupts deaktiviere.
Die Interrupts werden eaktiviert, damit dieser Programmteil
> c = ucUDR0; // hier ist es>> PORTxxxx = ..... // Led ein> ucUDR_valid0 = 0;> sei();
nicht von einem UART Interrupt unterbrochen werden kann.
Das macht sich nämlich nicht so gut, wenn hier in diesem
Programmteil ucUDR_valid auf 0 gesetzt wird und kurz vorher
ein Interrupt versucht hat, den auf 1 zu setzen.
Aber schau doch mal ans Ende der Sequenz. Da steht ein kleiner
verträumter sei(), der nach Abschluss dieser Operation die
Interrupts wieder zulässt.
Also wird der Interrupt nur beim ersten empfangenen Byte ausgelöst und
wenn alle Bytes empfangen sind und die Übertragung komplett ist wieder
aktiviert. Meinst du das so (siehe Anhang)?
Florentin S. wrote:
> Also wird der Interrupt nur beim ersten empfangenen Byte ausgelöst und> wenn alle Bytes empfangen sind und die Übertragung komplett ist wieder> aktiviert. Meinst du das so (siehe Anhang)?
Nein.
Der Interrupt wird nur kurzzeitig abgeschaltet, während
ein (in Worten: 1) empfangenes Zeichen verarbeitet wird.
Ob die Übertragung aller Zeichen komplett ist, kann ich doch
zu diesem Zeitpunkt noch gar nicht sagen. Das weis ich doch
erst nachdem ich den Zähler hochgezählt habe, der mir sagt
wieviele Bytes jetzt schon empfangen wurden! Wenn der auf
50 steht, dann war das das letzte zu Empfangende Byte auf
dieser Schnittstelle.
1
uint8_tByteCounter0;
2
uint8_tByteBuffer0[50];
3
4
uint8_tByteCounter1;
5
uint8_tByteBuffer1[50];
6
7
intmain()
8
{
9
uint8_tc;
10
11
...
12
13
ByteCounter0=0;
14
ByteCounter1=0;
15
16
....
17
18
while(1){
19
20
if(ucUDR_valid0){// Ein Zeichen ist auf UART 0 angekommen
21
cli();
22
c=ucUDR0;// hier ist es
23
ucUDR_valid0=0;
24
sei()
25
26
ByteBuffer0[ByteCounter0]=c;
27
ByteCounter0++;
28
29
if(ByteCounter0==50){
30
// Hurra, die 50 Bytes für den Header sind
31
// allesamt beisammen
32
//
33
// mach was damit
34
35
ByteCounter0=0;
36
}
37
}
38
39
// das ganze nochmal für UART 1
40
41
if(ucUDR_valid1){// Ein Zeichen ist auf UART 1 angekommen
ok daran hatte ich auch schonmal gedacht nur wird dann die auswertung
etwas umständlicher. Da ich ja nicht nur den haeder empfangen habe. Aber
so müsste es gehen. Ich wollte wie gesagt Header Und Daten etc einzeln
abspeichern.
Obiges ist natürlich nur ein Skelett um das Prinzip aufzuzeigen.
Wenn ich mir dein Protokoll mal so ansehe, dann fällt mir dazu
ein, dass ich da noch einen Status mit einbauen würde.
Deine Empfangs-'Maschine' ist eine Machschine die in
bestimmten Stati sein kann. Zb. wartet die Maschine auf
das erste Sync byte (die 0x15), das ihr mitteilt, das
in bälde mit der Übertragung des Headers zu rechnen sein
wird.
Nun, das sind schon mal 2 Zustände. Welche gibts noch?
die Maschine ist untätig (engl. idle) und wartet auf die 0x15
was ist zu tun, wenn die 0x15 empfangen werden?
Die Maschine muss 0x13 zurücksenden und geht in den
Zustand EmpfangeHeader über
Was ist im Zustand EmpfangeHeader zu tun?
Nicht viel, die empfangenen Bytes, 50 an der Zahl sind
abzuspeichern (oder auch nicht). Wie auch immer. Auf jeden
Fall muss die Maschine 0x06 schicken und geht dann in den
Zustand EmpfangeDaten über
Was ist im Zustand EmpfangeDaten zu tun?
Bytes speichern, 16 an der Zahl und nachdem die Daten da
sind, wird ein 0x06 gesendet und die Maschine geht in
den Zustand EmpfangeFooter (als gegenstück zum 'Header')
über.
Was ist im Zustand EmpfangeFooter zu tun?
Kennen wir schon. 50 Bytes empfangen und hinten nach wieder
ein 0x06 wegschicken.
Dadurch ist ein Telegram beendet und die Maschine kann wieder
in den Zustand Idle übergehen.
1
#define IDLE 0
2
#define REC_HEADER 1
3
#define REC_DATA 2
4
#define REC_FOOTER 3
5
6
7
uint8_tDataCounter0;
8
uint8_tDataBuffer0[16];
9
uint8_tByteCounter0;
10
uint8_tByteBuffer0[50];
11
uint8_tState0;
12
13
uint8_tDataCounter1;
14
uint8_tDataBuffer1[16];
15
uint8_tByteCounter1;
16
uint8_tByteBuffer1[50];
17
uint8_tState1;
18
19
intmain()
20
{
21
uint8_tc;
22
23
...
24
25
DataCounter0=0;
26
ByteCounter0=0;
27
State0=IDLE;
28
29
DataCounter1=0;
30
ByteCounter1=0;
31
State1=IDLE;
32
33
....
34
35
while(1){
36
37
if(ucUDR_valid0){// Ein Zeichen ist auf UART 0 angekommen
38
cli();
39
c=ucUDR0;// hier ist es
40
ucUDR_valid0=0;
41
sei();
42
43
if(State0==IDLE&&c==0x15){// was hat im Zustand Idle zu geschehen?
44
Transmit0(0x13);
45
State0=REC_HEADER;
46
}
47
48
elseif(State0==REC_HEADER){
49
ByteBuffer0[ByteCounter0]=c;
50
ByteCounter0++;
51
52
if(ByteCounter0==50){
53
Transmit0(0x06);
54
ByteCounter0=0;
55
State0=REC_DATA;
56
}
57
}
58
59
elseif(State0==REC_DATA){
60
Data0[DataCounter0]=c;
61
DataCounter0++;
62
63
if(DataCounter0==16){
64
Transmit0(0x06);
65
DataCounter0=0;
66
State0=REC_FOOTER;
67
}
68
}
69
70
elseif(State0==REC_FOOTER){
71
ByteBuffer0[ByteCounter0]=c;
72
ByteCounter0++;
73
74
if(ByteCounter0==50){
75
Transmit0(0x06);
76
ByteCounter0=0;
77
State0=IDLE;
78
}
79
}
80
81
}
82
83
// das ganze nochmal für UART 1
84
85
if(ucUDR_valid1){// Ein Zeichen ist auf UART 1 angekommen
Florentin S. wrote:
> ok daran hatte ich auch schonmal gedacht nur wird dann die auswertung> etwas umständlicher.
Etwas umständlicher schon. Aber dafür funktioniert sie aber auch.
Ausserdem: so wild ist das dann auch wieder nicht.
Mit eine paar Funktionen, in denen die State-Machine ausgelagert
wird, wird das eine schöne, kleine, schnucklige Hauptschleife.
ich finde nicht raus, woran das liegen kann auf jeden Fall sendet er den
2. Handshake nicht mehr. eigentlich werden doch alle Handshakes an der
richtigen Stelle gesendet. Vlt läuft der µC zu schnell...aber durch das
Interrupt empfängt er doch eigentlich nur, wenn auch was da ist...
Wie wäre es, wenn du mal die Aufgabe deines Programmes hinschreiben
würdest?!
Dann könnten dir auch Leute helfen, die keine/wenig Ahnung von Assembler
haben.
Die Aufgabe ist schnell umrissen:
Ein Atmega162 soll mit einem "Casio GTR" (genaue Modellbezeichnung oben)
Taschenrechner Daten austauschen. Dabei sollen beide UARTS des Atmega
benutzt werden, wenn 2 Taschenrechner angesprochen werden sollen.
Die regen User dieser Casio-Reihe haben einen Teil des Protokolls bei
den Send/Receive Befehlen des Caso und der Struktur der gesendeten Daten
herausgefunden. Einen Link zu einer PDF Datei mit dieser Beschreibung
hat Florentin in dem ganz oben angegebenen Thread angegeben. Ausserdem
liegt ein funktionierendes ASM-Programm vor (auch oben angegeben).
Das derzeitige Teilziel ist, die Ausgabe des Casio-Befehls Send per
UART0 auf den Atmega162 zu schaffen. Ein UART-Empangsinterrupt-Programm
in C mit einer Teilimplementation der Funktionalität des ASM-Programms
hängt mitten in dem empfangenen Telegramm (genauer: beim Empfangen des
ersten Headers). Die sinnvollen Vorschläge von Karl heinz zur Abwicklung
des Telegramms per state /machine/ (endlicher Automat) sind allerdings
noch nicht eingebaut (Hausaufgaben machen Florentin!).
Die Hardware auf AVR Seite ist derzeit eine fertige, nicht näher
beschriebene Platine mit wenigen Zugangsmöglichkeiten fürs Debugging.
Erschwerend (neudeutsch: herausfordernd) ist, dass Florentin keine RS232
Test- und Debugmöglichkeiten hat. Ein Anhängen des AVR oder des Casio an
einen PC funktioniert bei ihm nicht, weil sein MAC keine RS232
Schnittstelle hat, sondern nur USB. Ein USB-RS232 Konverter beschafft
sich Florentin gerade. Ob der dafür erforderliche TTL-RS232-Pegelwandler
vorhanden ist und an die fertige AVR Platine angebaut werden konnte,
weiss ich im Moment nicht. Wir hatten das diskutiert, aber das Resultat
ist mir nicht präsent. AVR und Casio sind jedenfalls direkt über TTL
miteinander verbunden. Die derzeitige Debugmöglichkeit von Florentin
ist, PORTA als Output-Port zu nutzen und die Pegel an den Pins mit dem
Multimeter nachzumessen.
Mir war lange unklar, was hinter diesem Projekt stecken könnte...
...ich habe eine Seite im Netz gefunden, wo ähnliche Bauteile und auch
beide UARTS benutzt werden. Es wird damit ein Chatprogramm zwischen zwei
Taschenrechnern aufgebaut ;-)
http://www.frangenberg.info/Michael/gtr/chat/index.html
Florentins Platine und Anwendung können auch ganz anders aussehem, so
dass man nicht dortige Hardware als Grundlage für weitere
Debuggingoptionen voraussetzen kann.
Es wäre nicht schlecht, wenn Florentin näher aus die Platine eingehen
würde. Und wenn Florentin schon dabei ist: Mit welchem Programmer und
welcher Programmersoftware schaffst du die Programme in den Atmega162?
Vielleicht kann man da was in Richtung Debugging drehen...
@ Karl heinz Buchegger (kbuchegg)
>vorweg: Ich weiss nicht, wie ich es dir noch anders erklären>soll, ohne dass ich dein Programm schreibe.
Dann nimm doch einach mal alle deine sehr ausführlichen Erklärungen und
pack sie in einen Wikiartikel + Beispielcode. Denn das sicher nicht die
letzte Anfrage zum UART Interrupt.
MFG
Falk
Die Idee hatte ich so ähnlich gestern auch. Und das ist auch der Grund,
warum ich mich relativ stark in diesen Thread reinhänge. Ich will den
UART Interrupt selbst auch verstehen.
Daraufhin hatte ich genau diese Baustelle im AVR-GCC-Tutorial
angelegt (=> UART => Interruptbetrieb). Ob es dort oder hinter dem
Interruptteil des Tutorials stehen wird ist noch offen. Kommt darauf an,
was vom Lernen hersinnvoller ist.
Im Moment bin ich am Herstellen und Testen von Beispielcode. Mitarbeit
ist sehr willkommen.
Falk Brunner wrote:
> @ Karl heinz Buchegger (kbuchegg)>>>vorweg: Ich weiss nicht, wie ich es dir noch anders erklären>>soll, ohne dass ich dein Programm schreibe.>> Dann nimm doch einach mal alle deine sehr ausführlichen Erklärungen und> pack sie in einen Wikiartikel + Beispielcode. Denn das sicher nicht die> letzte Anfrage zum UART Interrupt.
Na, ja.
Sein Problem dürfte doch jetzt eigentlich nicht mehr der Interrupt
sein, sondern die Datenauswertung, wenn ich die letzten Meldungen
richtig interpretiere.
Leider ist Florian etwas hilflos wenn es zum Thema Debugging geht.
Hilft aber alles nichts, da muss er durch. Debugging hat auch
immer was mit Intuition, Ideen haben und auch ein klein wenig
Glück zu tun. Wenn ich nichts vernünftiges zum Debuggen habe,
dann hab ich meistens immer noch eine Led an einem Port, die
dann herhalten muss um mir anzuzeigen ob bestimmte Zustände
im Programm eingetreten sind. Und wenn das alles ist, was
ich als externen Indikator habe, dann muss halt das genügen
um mir ein Bild darüber zu verschaffen was im Programm abgeht.
Dazu muss ich mir aber überlegen, was ich überhaupt mit einer
Ja/Nein Entscheidung (und was anders erlaubt mir meine LED ja
nicht) als Statusinformation herausgeben kann und was ich mit
dieser Information anfangen, bzw. welchen Rückschluss ich daraus
ziehen kann.
Hi,
danke nochmal für die Hinweise. Über eine State-Maschine werde ich mich
erkundigen, was das ist. Ich habe den Assemblercode zusammen mit Michael
entwickelt (Link oben). Meine Platine ist ähnlich aufgebaut. Also nur
das nötigste und halt an den Uarts die Kabel zum Rechner. Der Serielle
Adapter ist wie gesagt bestellt und ich hoffe, dass er morgen ankommt.
Einen Pegelwandler (Max232) habe ich zur Verfügung. Das ist nicht das
Problem.
Das Problem muss wie gesagt irgendwo beim empfangen des Haders liegen.
Entweder geht was zu schnell oder was anderes läuft schief. Ich hoffe
das ich morgen mal ordentlich Debuggen kann um den Fehler noch genauer
zu lokalisieren.
Vielen Dank noch mal für die Hinweise.
Gruß Florentin
Ich hab nochmal wegen der State Maschine geschaut...soll ich damit die
Zustände schreiben wie Header wurde empfangen oder in wiefern kann ich
diese einsetzen?
Was ich noch vergessen hatte ich flashe den Atmega mittels AVRDude.
Florentin S. wrote:
> soll ich damit die> Zustände schreiben wie Header wurde empfangen oder in wiefern kann ich> diese einsetzen?
Das hat dir Karl Heinz hier schon erläutert:
Beitrag "Re: UART Problem (Assembler zu C) II"
Das Gerippe der state machine steht da schon.
Das Prinzip ist, dass du mit jedem empfangenen Byte guckst, was du
damit anfangen kannst. Wenn dieses Byte dazu führt, dass du
(basierend auf dem aktuellen Zustand des Automaten) einen bestimmten
Abschnitt des Headers jetzt erkannt hast, dann schaltet der
Automat weiter und guckt, was danach kommt. Auf diese Weise
hangelt man sich Stück für Stück weiter. Wenn irgendetwas
unerwartetes im Datenstrom ankommt, bricht der Zustandsautomat
(so der offizielle deutsche Name) ab und fällt auf den Grundzustand
zurück.
Danke den hab ich ja noch gar nicht gelesen.
@Karl Heinz hasst du den Beitrag mal ediert? Oder habe ich den schlicht
weg überlesen? Danke. Dann bau ich sowas in der Art mal noch ein.
Florentin S. wrote:
> Hi,> danke nochmal für die Hinweise. Über eine State-Maschine werde ich mich> erkundigen, was das ist.
Genau das was ich als letztes Programmfragment weiter oben
gepostet habe.
In einer Variablen wird der aktuelle Zustand (der State)
gespeichert und wenn die Maschine arbeiten soll, entscheidet
sie anhand des States und dem neuen Input (und eventuell irgendwelchen
Zusatzbedingungen) was zu geschehen hat und in welchen neuen
State (das ist dann einfach eine Zuweisung an die State-Variable)
sie überwechseln soll.
Eine Statemaschine hat den Vorteil, dass man die Logik die
dahintersteckt, sehr einfach graphisch veranschaulichen kann
(siehe beiliegendes gif). Die Umsetzung aus so einer Graphik
in die tatsächliche Implementierung ist dann reine
Routinearbeit.
Im Gif gilt:
die Kreise sind die Zustände (jweils mit dem Namen des Zustands)
die grauen Pfeile sind die Zustandsübergänge
bei Pfeil steht jeweils dabei:
in blau: welche Voraussetzung muss gelten, damit dieser
Pfad genommen wird
in rot: welche Aktion ist dabei durchzuführen
Wenn du dieses Bild mit meinem Code vergleichst, wirst du auch
feststellen, dass ich im Code ein paar Fehler gemacht habe.
Das liegt daran, dass ich den Code direkt eingetippt habe
und vor dem Posten nicht getestet habe. Durch die Grafik kannst
du die Logik deiner Maschine in einer abstrakten Art und Weise
festlegen und dich nur auf die richtigen Abläufe konzentrieren.
Du kannst dann auch auf dem Papier mal deine Maschine mit
einem simulierten Input durchspielen und die Abläufe kontrollieren.
Läuft das ganze auf dem Papier, dann setzt man das in Code um.
(und nicht so wie ich das gemacht habe: zuerst coden und dann
zeichnen. Wie du siehst schleichen sich da Fehler ein).
Nachtrag.
Da diese State Maschine nur 4 Zustände kennt, würde es sich
zb. anbieten die State Nummer auf 2 Leds auszugeben und dann
mal ganz gemütlich Zeichen in die Maschine einzuspeisen.
Die Leds zeigen dir dann ganz genau an, in welchem Zustand
die Maschine ist und du kannst kontrollieren ob wenigstens
das funktioniert.
(PS: Der Fehler von dem ich im Codeteil gesprochen habe,
betrifft die Initialisierung der diversen Counter. Da liegt
einiges im Argen)
Ja vielen Dank nochmal das mit den Fehlern habe ich schon gemerkt das da
ein paar Namen falsch waren etc. Die hab ich mal korrigiert. Ich muss
nur nochmal schauen, ob inhaltlich alles stimmt. Der Code ist im Anhang.
Muss aber nochmal alles durchprüfen.
So. Der Code funktioniert so anscheinend auch noch nicht. Wenn ich 0x15
sende sendet er wenn überhaupt "0xD3" zurück oder sendet ununterbrochen
"0xC6" Wenn ich den µC vom Strom trenne sendet er 2 Bytes genau weis ich
nicht mehr was, aber ich glaub es war "F0". Vlt könnt ihr mit den
Informationen was anfangen. Mir kommt das bald vor, wie Baudrate falsch
eingestellt.
Edit:
Ich hab auch mal den Rechner an den PC geklemmt und den µC emuliert das
Klappt wunderbar.
Ah, du bist jetzt so weit, dass du entweder den Casio oder den µC an die
serielle Schnittstelle eines PC hängen kannst?
Zur Baudrate...
Aus dem gesetzten UBRR0L Wert (0x2F bzw. 47) im LSS-File letztens ist
erkennbar, dass der µC in die UBRR_VAL Berechnung F_CPU = 7372800 Hz
eingeflossen ist. Das ist die gleiche Taktrate wie im ASM-File. Das
ASM-Programm funktionierte auch auf der gleichen Platine. D.h. die
Taktrate fürs C-Programm stimmt und dann wird die Baudrate auch stimmen.
Und den anderen UART-Parametern...
Die habe ich auch in dem Programm im Anhang verwendet. Wenn ich meinen
den PC auf 9600 Baud 8N2 einstelle, funktioniert die Kommunikation mit
dem µC.
Tipp: In vielen Terminalprogrammen auf dem PC kann man die Kommunikation
in eine Datei mitspeichern lassen. Das kann ungemein nützlich sein, um
anderen die Ergebnisse zu zeigen...
Irgend etwas ist komisch...ich habs jetzt nochmal an einem anderen
Rechner getestet und weder dein Debug Programm funktionieren noch meine
C Variante. Rechner Funktioniert und ich hab mal das ASM drauf geladen
sobald ich 15 eigebe kommt 13 zurück. Ich sende 50 Bytes danach kommt 6
und so weiter. Aber bei den C Varianten kommt nix!
ok ich habs...
ich sende 0x15
der µC antwortet mit 0xD3
dann sende ich 50 Bytes
der µC antwortet mit 0xC6
dann 16 Bytes
µC: 0xC6
dann 50
µC: 0xC6
das Programm hat also schon früher funktioniert siehe erster Beitrag. Da
antwortete der µC auch mit 0xD3 nur woran kann das liegen? Im Anhang
nochmal das Makefile:
Es muss also an dem Wert liegen, der Transmit übergeben wird oder etwas
an der Baud stimmt nicht, was aber unrealistisch ist, da der µC ja auf
0x15 antwortet.
Seltsam. Schick doch mal das LSS-File von dem jetzigen C-Code, damit man
sehen kann, was auf unterster Ebene gemacht wird. Ich bin allerdings bis
Montag nicht mehr online, da ich das schöne Schweinfurth besuche ;-)
Das ist mir total unerklärlich, denn das LSS sieht für mich OK aus.
Gut, Transmit0(int Senden) wäre besser ein Transmit0(unsigned char
Senden) sein, damit Code eingespart wird, aber daran liegt es eher
nicht.
Als echte Verzeiflungstat würde ich in Transmit0() die letzte Zeile in
UDR0 = Senden & 0x1F; ändern, um herauszufinden, ob das Phänomen (Bit 6
und 7 gesetzt) vom Hauptcode her kommt (ohne &) oder vom UART selbst
(mit &).
so ich hab jetzt mal den Rest eingespeist (Erstmal nur UART0). Ist das
so in Ordnung oder kann man noch was optimieren? Der Compiler meckert
nicht. Nur kann ichs leider noch nicht testen, bevor ich das allgemeine
Problem beseitigt habe.