Hallo
Ich versuche einen ADUC7000 (ARM7) zu programmieren.
Im Moment hänge ich an den Uart-Funktionen.
Ich habe ein FIFO im SRam zum Senden und Empfangen angelegt.
Wenn Daten gesendet werden sollen, landen diese im FIFO.
So bald das Uart Senderegister leer ist, wird ein IRQ
ausgelöst. In der IRQ-Funktion wird ein Byte aus dem FIFO
ins Senderegister kopiert.
Wenn Daten in das FIFO schneller geschrieben werden, als die
Daten über die IRQ-Funktion gesendet werden können, wird eine
Statusvariable auf 2 gesetzt, damit im Programm erkannt werden
kann, dass gewartet werden muss, bis wieder Platz im Puffer ist.
Das funktioniert fast.
Dies ist meine Sendefunktion:
char UartWrite (char *ptr, char wait) { // Sende String ueber uart, Wenn
wait==1, wird gewartet, bis der String vollstaendig
// gesendet werden konnte. Soll nicht ewartet werden und der
TX-Puffer laeuft ueber, wird die Position des Zeichen im
// String zurueckgegeben, die nicht mehr gesendet werden konnte
char *origptr = ptr;
char CharSent;
while (*ptr!='\0') {
CharSent = UartPutChar (*ptr); // Uebergebe einzelnes Zeichen an
Sendefunktion
ptr++;
if (CharSent==0) { // Puffer ist voll. Warte oder gebe Position des
letzten gesendeten Zeichen zurueck
if (wait){
while (uarttxbufferstatus==2) {
int bla1 = 7;
int bla2 = 80;
} // Warte bis Platz in TX-Puffer ist
}
else
return *ptr; // Gebe Position des noch zu sendenden Zeichen aus
}
}
return *origptr; // Gebe Pointer auf String zurueck, wenn alles
gesendet wurde.
}
Was ich nicht ganz verstehe ist, dass der Code so funktioniert. Ich
musste
in die inntere while-Schleife Variablen definieren. Eigentlich hätte
folgender
Code gereicht:
while (uarttxbufferstatus==2) {
} // Warte bis Platz in TX-Puffer ist
Das Problem ist, dass dann der IRQ nicht mehr ausgelöst wird.
Hat einer von euch eine Idee wieso?
Beim Empfangen von Zeichen habe ich ebenfalls IRQ-Probleme. So bald
Daten
im Empfangsregister liegen, wird ein IRQ ausgelöst. Die Daten werden
dann
in einen FIFO kopiert. Einzelne Zeichen werden mit der Funktion
UartRXGet()
empfangen:
char UartGetChar(void) { // Lese Zeichen aus Empfangspuffer
char RETURN = 0;
if (uartrxbufferstatus!=0) { // Zeichen in Empfangsringspeicher
RETURN = uartrxbuffer[readuartrxbuffer];
readuartrxbuffer++; // Aendere Position im Empfangsringspeicher
readuartrxbuffer %= sizeof(uartrxbuffer);
if (uartrxbufferstatus==2) uartrxbufferstatus = 1;
if (writeuarttxbuffer == readuarttxbuffer) { // Empfangspuffer ist
nun leer, setzte auf 0 zurueck
uartrxbufferstatus = 0;
}
}
return RETURN; // Gebe 0 zurueck, wenn kein Zeichen empfangen wurde
}
char UartRXGet(void) { // Warte bis Zeichen empfangen wurde und gebe
dieses aus
char ReturnChar = 0;
while (!ReturnChar) {
ReturnChar = UartGetChar();
UartIrq(); // Warum muss hier diese Funktion aufgerufen werden?
}
return ReturnChar;
}
Hier habe ich ein ähnliches Problem. Der IRQ wird nicht immer ausgelöst.
Mein Programm hängt ewig in der while-Schleife fest. Desshalb habe ich
in
die while-Schleife meine IRQ-Funktion für den Uart eingefügt. So
funktioniert
der Code dann, aber ich weiß nicht wieso.
Dies ist meine IRQ-Funktion:
void UartIrq (void) {
if (COMSTA0 & 0x01) { // Zeichen in RX Puffer
if (uartrxbufferstatus!=2) { // Es ist noch Platz im Puffer
uartrxbuffer[writeuartrxbuffer] = COMRX;
writeuartrxbuffer++; // Aendere Position im Empfangsringspeicher
writeuartrxbuffer %= sizeof(uartrxbuffer);;
if (writeuartrxbuffer == readuartrxbuffer) { // Sendepuffer ist
nun voll -> Gebe als Fehler 0 zurueck
uartrxbufferstatus=2; // Puffer ist voll
}
else
uartrxbufferstatus = 1; // Im Puffer liegen nun Daten
}
else { // Ueberschreibe aeltesten Wert in Puffer, damit das aelteste
Zeichen verloren geht
uartrxbuffer[writeuartrxbuffer] = COMRX;
writeuartrxbuffer++; // Aendere Position im Empfangsringspeicher
writeuartrxbuffer %= sizeof(uartrxbuffer);;
readuartrxbuffer++; // Aendere Position im Empfangsringspeicher.
Leseposition muss ebenfalls geaendert werden
readuartrxbuffer %= sizeof(uartrxbuffer);;
}
}
if (COMSTA0 & 0x40) { // TX Puffer leer.
if (uarttxbufferstatus!=0) { // Zeichen in Senderingspeicher
COMTX = uarttxbuffer[readuarttxbuffer]; // Sende Zeichen
readuarttxbuffer++; // Aendere Position im Senderingspeicher
readuarttxbuffer %= sizeof(uarttxbuffer);
if (readuarttxbuffer==writeuarttxbuffer) { // Puffer ist nun leer
COMIEN0 = 0x01; // Schalte TX-IRQ ab, Senderingspeicher leer.
uarttxbufferstatus = 0; // Setzte Status = 0, also keine Daten
mehr in Puffer
}
else
uarttxbufferstatus = 1; // Puffer kann nicht mehr voll sein, da
ein Element gesendet wurde
}
if (uarttxbufferstatus==0) { // Keine Zeichen in Puffer, Schalte IRQ
ab.
COMIEN0 = 0x01;
}
}
}
void IRQ_Function (void) {
if (IRQSIG & UART_BIT) { // Uart
UartIrq(); // Zeichen wurde empfangen
}
}
Ich habe versucht herauszufinden, warum der IRQ nicht ausgelöst wird.
Für den Uart gibt es die Statusregister COMSTA0 und COMIEN0. In COMIEN0
wird eingestellt, ob ein volles RX oder ein leeres TX Register einen IRQ
auslöst. Mit COMSTA0 kann abgefragt werden, ob RX voll oder TX leer ist.
Ich habe in Eclipse das Programm an den entsprechenden Stellen
angehalten
und die Register COMSTA0 und COMIEN0 angeschaut. Laut diesen Registern
müsste
ein IRQ ausgelöst werden.
Ich kenne mich mit der Materie noch nicht so gut aus.
Hat einer von euch einen Tip für mich, wie ich weitermachen könnte?
Ich habe mir überlegt die Register abzufragen, in denen gespeichert
wird,
ob sich die CPU gerade im Interrupt Mode befindet. DAs könnte ein Grund
sein, warum kein IRQ mehr ausgelöst wird. Ich habe nur leider keine
Ahnung,
wie man so etwas machen könnte.
Bis dann, Tilo
Neine habe ich nicht. Ich wusste nicht einmal, dass es so etwas
gibt. Ich habe den Text durchgelesen aber noch nicht verstanden,
worum es genau geht. Da werde ich mich noch ein wenig reinhängen müssen.
Ich bins nochmal.
Also ich habe versucht den Text zu verstehen.
Der ARM7 hat eine Piepline, die abgearbeitet wird.
Erst wenn die Pipeline abgearbeitet ist, wird er IRQ
ausgeführt. Wenn nun einer der Befehle in der Pipeline
den IRQ abschaltet, wird er trotzdem ausgeführt?
Jedenfalls habe ich den Text so verstanden, dass genau dies ein
"surprise interrupt" ist.
Müsste in diesem Fall die IRQ-Funktion zu oft aufgerufen werden?
spurious interrupts:
Zitat aus deinem Link: "On page 2-2 it states VIC does not handle
interrupt sources with transient behaviour. An interrupt exhibits
transient behaviour if it is asserted and then deasserted before the
software can clear the interrupt source."
Das ganze verstehe ich auf deutsch so, dass ein IRQ nicht abgearbeitet
wird, wenn der IRQ gesetzt und gelöscht wurde, bevor die IRQ-Quelle
zurückgesetzt werden konnte.
Was bedeutet das in meinem Fall?
Ich habe eine Vermutung: So bald keine Daten mehr im FIFO liegen, soll
der TX-Empty IRQ abgeschaltet werden. Der RX-Full IRQ soll aber an
bleiben. Hierfür setzte ich das entsprechende Register COMIEN0 in der
IRQ-Funktion auf 0x01. Nun vergeht zwischen "Schiebe letzte Daten in
TX-Senderegister" und deaktiviere TX-Empty IRQ ein wenig Zeit.
Während dieser Zeit kann es sein, dass das TX-Senderegister wieder leer
ist und das entsprechende Bit in COMSTA0 gesetzt ist, also bereits in
der
IRQ-Funktion der nächste IRQ ausgelöst wird. Setze ich nun COMIEN0=0x1,
würde dies einen "spurious interrupt" auslösen.
Da der Uart nur eine Interruptquelle darstellt, egal was am Uart den IRQ
ausgelöst hat, reagiert der Interruptcontroller gar nicht mehr auf den
Uart.
Was haltet ihr von der Vermutung?
Vielen Dank,
Tilo
Das Thema Spurious Interrupts ist in diesem Kontext schnell abgehandelt,
denn m.W. hat der ADUC7000 keinen Interrupt-Controller an Bord. Und ohne
den gibt es die nicht, jedenfalls nicht in der beschriebenen Form.
Suprise Interrupts wirken sich unangenehm auf FIRQs aus. Wenn man die
nicht benutzt, kann man auch dieses Thema vorerst auf Halde legen.
Quelltexte bindet man besser mit den vorgesehene Tags ein, dann bleiben
sie einigermassen lesbar.
Arg, Sorry. Ich brauche dringend eine neue Brille. Ich hab überall
nachgesehen nur nicht dort, wo es offensichtlich ist.
Ich denke gerade viel zu kompliziert.
Also nochmal den entsprechenden Code unten angehängt. Den ersten
Beitrag kann ich leider nicht mehr editieren.
1
charUartPutChar(charch){
2
charRETURN;
3
if(uarttxbufferstatus!=2){// Puffer ist nicht voll
4
uarttxbuffer[writeuarttxbuffer]=ch;// Schreibe Zeichen in Sendepuffer
5
writeuarttxbuffer++;
6
writeuarttxbuffer%=sizeof(uarttxbuffer);
7
uarttxbufferstatus=1;
8
if(writeuarttxbuffer==readuarttxbuffer){// Sendepuffer ist nun voll -> Gebe als Fehler 0 zurueck
9
uarttxbufferstatus=2;
10
RETURN=0;// Puffer voll
11
}
12
elseRETURN=1;// Alles OK
13
}
14
elseRETURN=0;// Puffer voll
15
COMIEN0=0x03;// Aktiviere TX-Interrupt
16
returnRETURN;
17
}
18
19
charUartGetChar(void){// Lese Zeichen aus Empfangspuffer
20
charRETURN=0;
21
if(uartrxbufferstatus!=0){// Zeichen in Empfangsringspeicher
22
RETURN=uartrxbuffer[readuartrxbuffer];
23
readuartrxbuffer++;// Aendere Position im Empfangsringspeicher
24
readuartrxbuffer%=sizeof(uartrxbuffer);
25
if(uartrxbufferstatus==2)uartrxbufferstatus=1;
26
if(writeuarttxbuffer==readuarttxbuffer){// Empfangspuffer ist nun leer, setzte auf 0 zurueck
27
uartrxbufferstatus=0;
28
}
29
}
30
returnRETURN;// Gebe 0 zurueck, wenn kein Zeichen empfangen wurde
31
}
32
33
charUartRXGet(void){// Warte bis Zeichen empfangen wurde und gebe dieses aus
34
charReturnChar=0;
35
while(!ReturnChar){
36
ReturnChar=UartGetChar();
37
UartIrq();// Warum muss hier diese Funktion aufgerufen werden?
38
}
39
returnReturnChar;
40
}
41
42
voidUartIrq(void){
43
if(COMSTA0&0x01){// Zeichen in RX Puffer
44
if(uartrxbufferstatus!=2){// Es ist noch Platz im Puffer
45
uartrxbuffer[writeuartrxbuffer]=COMRX;
46
writeuartrxbuffer++;// Aendere Position im Empfangsringspeicher
47
writeuartrxbuffer%=sizeof(uartrxbuffer);;
48
if(writeuartrxbuffer==readuartrxbuffer){// Sendepuffer ist nun voll -> Gebe als Fehler 0 zurueck
49
uartrxbufferstatus=2;// Puffer ist voll
50
}
51
else
52
uartrxbufferstatus=1;// Im Puffer liegen nun Daten
53
}
54
else{// Ueberschreibe aeltesten Wert in Puffer, damit das aelteste Zeichen verloren geht
55
uartrxbuffer[writeuartrxbuffer]=COMRX;
56
writeuartrxbuffer++;// Aendere Position im Empfangsringspeicher
57
writeuartrxbuffer%=sizeof(uartrxbuffer);;
58
readuartrxbuffer++;// Aendere Position im Empfangsringspeicher. Leseposition muss ebenfalls geaendert werden
59
readuartrxbuffer%=sizeof(uartrxbuffer);;
60
}
61
}
62
if(COMSTA0&0x40){// TX Puffer leer.
63
if(uarttxbufferstatus!=0){// Zeichen in Senderingspeicher
uarttxbufferstatus=0;// Setzte Status = 0, also keine Daten mehr in Puffer
70
}
71
else
72
uarttxbufferstatus=1;// Puffer kann nicht mehr voll sein, da ein Element gesendet wurde
73
}
74
if(uarttxbufferstatus==0){// Keine Zeichen in Puffer, Schalte IRQ ab.
75
COMIEN0=0x01;
76
}
77
}
78
79
}
80
81
charUartWrite(char*ptr,charwait){// Sende String ueber uart, Wenn wait==1, wird gewartet, bis der String vollstaendig
82
// gesendet werden konnte. Soll nicht ewartet werden und der TX-Puffer laeuft ueber, wird die Position des Zeichen im
83
// String zurueckgegeben, die nicht mehr gesendet werden konnte
84
char*origptr=ptr;
85
charCharSent;
86
while(*ptr!='\0'){
87
CharSent=UartPutChar(*ptr);// Uebergebe einzelnes Zeichen an Sendefunktion
88
ptr++;
89
if(CharSent==0){// Puffer ist voll. Warte oder gebe Position des letzten gesendeten Zeichen zurueck
90
if(wait){
91
while(uarttxbufferstatus==2){
92
intbla1=7;
93
intbla2=80;
94
}// Warte bis Platz in TX-Puffer ist
95
}
96
else
97
return*ptr;// Gebe Position des noch zu sendenden Zeichen aus
98
}
99
}
100
return*origptr;// Gebe Pointer auf String zurueck, wenn alles gesendet wurde.
101
}
Wie bereits oben geschrieben mein Code hängt an 2 Stellen.
1. Wenn in UartRXGet() UartIrq() auskommentiert ist
2. Wenn in UartWrite() die innere while-Schleife leer ist.
@ Tilo Lutz (katagia)
>Arg, Sorry. Ich brauche dringend eine neue Brille. Ich hab überall>nachgesehen nur nicht dort, wo es offensichtlich ist.
Un doch nur die Hälfte gelesen :-( Solche langen Quelltexte gehören in
den Anhang! Als *.c Dateien. Könnte kaum einfacher sein.
MFG
Falk
Du solltest dir mal in den Get/Put Routinen Zeile für Zeile überlegen,
was passiert, wenn exakt in/nach dieser Zeile ein UART-Interrupt
erfolgt. Dann wirst du vielleicht darauf kommen, dass es fatal ist, wenn
direkt nach dem Test der Statusvariablen aber vor dem übrigen Code der
Interrupt erfolgt und sich der Status ändert. Fachwort dafür: "race
condition".
Einfachste Abhilfe: Um den entsprechenden Code in den Get/Put-Routinen
herum den jeweiligen UART-Interrupt zeitweilig abschalten.
Danke für den Hinweis. Ich habe den Code angepasst.
Leider funktioniert es immer noch nicht.
Beim Empfangen von Daten hängt das Programm immer noch in
UartRXGet(). In der Funktion werden folgende Register in
lokalen Variablen gesichert:
int bla1 = COMIEN0;
int bla2 = COMSTA0;
int bla3 = IRQEN;
int bla4 = IRQSIG;
Wenn das Programm "hängt" halt es über JTAG in Eclipse an
und debugge schrittweise. In COMATA0 ist der Urart IRQ aktiviert,
wenn RX-Register voll oder TX-Register leer ist. Laut den Variablen
ist IQRSIG gesetzt und ein IRQ sollte ausgelöst werden. Es sollte also
zumindest die IRQ-Routine ausgeführt werden.
Wenn ein Haltepunkt in der IRQ-Routine gesetzt wird, wird dieser nie
erreicht.
Woran kann das liegen?
Wo kann ich weitere Informationen zu diesem Thema finden?
Bei Analog selbst scheint es da nicht viel zu geben.
Bei ARM bin ich gerade am suchen, habe aber bisher noch nichts
gefunden.
Ich habe das Projekt vollständig angehängt.
Ich habe das Problem gefunden!
Im Programm aktiviere ich bei Bedarf den IRQ für das
RX-Register. Wenn dieses voll ist, wird ein IRQ ausgelöst.
Ist das Register schon voll und ich aktiviere den IRQ, wird
dieser nicht ausgelöst.
Die Lösung war, nach dem aktivieren des IRQ die IRQ Funktion
1x auszuführen.