C-Programm auf XC866 'verzählt' sich
Hallo zusammen,
ich habe in etwas komplexeres Problem.
Ich möchte einem Microcontroller vom Typ Infineon XC866 von einem Rechner mit W2k über eine 8-Bit-Verbindung insgesamt 5 Byte übermitteln. Dazu hat der Rechner eine GPIB-Interfacekarte. Vor dem Controller hängt das Gegenstück zu dieser Karte. Sprich, wenn ich im Programm auf dem Rechner sage, ich möchte das 2. Bit im 5.Byte der GPIB-Nachricht setzen wird eine bestimmte Leitung an der Karte vor dem Controller auf high gezogen.
So habe ich also meine 8 Bitleitungen von der Karte an den Controller verdrahtet. Eine 9. Leitung benutze ich, um dem Controller mit einer steigenden Flanke zu singalisieren, dass jetzt 8 Bit anliegen und er diese bitte 'abholen' soll. Auf die steigende Flanke reagiere ich mit einem Interrupt.
Im folgenden also meine Interrupt Service Routine (ISR) für die 9. Leitung:
Code:
void TriggerISR() interrupt 8
{
IRCON0 &= 0xfb; //reset IR-Bit of trigger
switch (counter)
{
case 0:
time1 = P3_DATA; //get LSB of time
if (time1 == 0)
{
P0_DATA &= 0xf8;
P0_DATA |= 1;
}
else
{
if (time1 == 1)
{
P0_DATA &= 0xf8;
P0_DATA |= 2;
}
}
break;
case 1:
time2 = P3_DATA; //get 2nd byte of time
if (time2 == 0)
{
P0_DATA &= 0xf8;
P0_DATA |= 0x3;
}
else
{
if (time2 == 1)
{
P0_DATA &= 0xf8;
P0_DATA |= 0x4;
}
}
break;
case 2:
time3 = P3_DATA; //get MSB of time
if (time3 == 0)
{
P0_DATA &= 0xf8;
P0_DATA |= 0x1;
}
else
{
if (time3 == 13)
{
P0_DATA &= 0xf8;
P0_DATA |= 0x7;
}
}
break;
case 3:
phase = P3_DATA; //get phase information
if (phase == 0)
{
P0_DATA &= 0xf8;
P0_DATA |= 0x6;
}
else
{
if (phase == 128)
{
P0_DATA &= 0xf8;
P0_DATA |= 0x1;
}
}
break;
case 4:
switch (P3_DATA)
{
case 0:
usePhase = 0; //-> phase ignored
mainc = 0; //use serial contactors
break;
case 1:
usePhase = 1; //-> phase important
mainc = 0; //use serial contactors
break;
case 2:
usePhase = 0; //-> phase ignored
mainc = 1; //use main contactor
break;
case 3:
usePhase = 1; //-> phase important
mainc = 1; //use main contactor
break;
}
break;
}
counter++;
}
Dabei stellt P3_DATA meine 8 Eingänge dar, an denen die Bytes empfangen werden sollen. An P0.0 bis P0.2 hängen drei high-aktive Dioden, die ich angebracht habe, damit ich mir anzeigen lassen kann, an welcher Stelle im Programm ich bin (debuggen geht leider nicht!).
Und nun mein Problem:
Wenn ich eine Byte-Kombination übergebe, die in den if-else-Blöcken abgefangen wird funktioniert alles wunderbar. Wenn ich dagegen eine Kombination übergebe, die nicht in den ersten if-else-Block reingeht springt er mir anscheinend über den zweiten break drüber (Vermutung!) und macht auch gleich noch den case 2 mit.
Anschaulich heißt das:
1. Byte = 1
2. Byte = 0
3. Byte = 13
4. Byte = 0
5. Byte = 2
--> alles passt!
1. Byte = 2
alle anderen Bytes sind egal, er macht mir, sobald ich das zweite Byte übermittle (z.B. ne 0) zuerst ganz kurz die Dioden für die 3 an, anschließend sofort die Diode für die 1!
1. Byte = 1
2. Byte = 2
3. Byte = 13
4. Byte = 0
5. Byte = 2
--> alles passt!!!
Ich hoffe mal, ihr versteht was ich meine. Wenn nicht fragt bitte nach, dann versuche ich es auf eine andere Art zu erklären.
Was ich schon alles versucht habe:
1. In den verschiedenen cases jeweils den counter auf die nächste Zahl zu setzen (statt counter++ am Ende)
2. In den if-else-Blöcken eine else im if, mit einem return (damit er auch sicher aus der Funktion aussteigt)
3. einen anderen Interrupt zu nehmen
4. den switch durch 5-if-Abfragen zu ersetzen
5. die if-else-Blöcke durch switch mit default-Zweig zu ersetzen
Hat leider alles nichts gebracht!
Wieso reagiert das Programm falsch, wenn der erste Wert nicht im if-else-Block abgefangen wird???
Ich hoffe mal, ihr könnt mir helfen, bin echt schon am verzweifeln!
Danke fürs lesen und viele Grüße
Jetzt wirds richtig toll....
Guten Morgen,
danke für die schnelle Antwort! Bin gestern nur nicht mehr dazu gekommen noch weiter zu basteln.
Hier also meine Erkenntnisse von heute:
Ich habe die ganzen if-else-Abfragen auskommentiert, das IR-Bit erst am Ende der ISR rückgesetzt und den Counter an P0 ausgegeben. Und jetzt wirds richtig mysteriös:
Wenn ich eine Kombination übergebe, die VORHER im if-else des 1. Bytes abgefangen wurde geht alles gut, er zählt ganz normal bis 5 hoch und ich bin glücklich.
Wenn ich eine Kombination übergebe, die nicht im if-else des 1. Bytes enthalten war (s. Anfangsthread), dann zählt er 1 - 2 und sofort 3 - 4 - 5 - 6!
Ich habe also ein Oszilloskop drangehängt und mir mal angeschaut, was auf der Interruptleitung passiert. Fazit: Egal, was ich für Werte übergebe, es schaut immer gleich aus (okay, mit gewissen Toleranzen, die von der 'Echtzeitfähigkeit' von Windows kommen)! Sprich, es wird definitiv nur ein Interrupt ausgelöst!!!
Hab auch bei beiden switches einen default: break eingefügt, hat auch nix gebracht.
Hast du vielleicht noch Ideen??? Hast ja geschrieben fürs erste :-)
Im folgenden nochmal mein Code (in der neuen Fassung):
Code:
void TriggerISR() interrupt 8
{
P0_DATA &= 0xf8;
switch (counter)
{
case 0:
time1 = P3_DATA; //get LSB of time
break;
case 1:
time2 = P3_DATA; //get 2nd byte of time
break;
case 2:
time3 = P3_DATA; //get MSB of time
if (time3 == 0)
break;
case 3:
phase = P3_DATA; //get phase information
break;
case 4:
switch (P3_DATA)
{
case 0:
usePhase = 0; //-> phase ignored
mainc = 0; //use serial contactors
break;
case 1:
usePhase = 1; //-> phase important
mainc = 0; //use serial contactors
break;
case 2:
usePhase = 0; //-> phase ignored
mainc = 1; //use main contactor
break;
case 3:
usePhase = 1; //-> phase important
mainc = 1; //use main contactor
break;
default:
break;
}
break;
default:
break;
}
counter++;
P0_DATA = counter;
IRCON0 &= 0xfb; //reset IR-Bit of trigger
}
Fehler liegt bei dem Interrupt
Hallo Robert,
ich habe es zwar etwas anders angestellt als du, aber ich bin mir mittlerweile definitiv sicher, dass ein 2. Interrupt ausgelöst wird, wenn ich die 'falschen' Werte übergebe.
Ich habe das ganze herausgefunden, indem ich in der ISR nacheinander in jeden case ein EA = 0 (verbietet global ALLE Interrupts) eingefügt habe. Wenn ich den in case 1 reinschreibe und Werte übergebe geht zunächst die erste LED an (für case 0), dann die 2. und dann is Ruhe. Wenn ich die Zeile in case 2 reinschreibe 'verzählt' er sich wieder. Das heißt doch, dass definitiv ein zweiter Interrupt ausgelöst wird, oder?
Dann ist nur noch die Frage woher...
Ich würde dir ja gerne ein Bild von meinem Oszilloskop schicken, aber leider kann man hier ja keine Anhänge an den Thread hängen und auf ftp-Server darf ich von hier scheinbar nicht. Also muss ich dir halt so versichern, dass es nur insgesamt 5 steigende Flanken am Eingang des Controllers gibt. Und die 3. kommt ganz sicher erst nachdem er bereits den counter auf 3 erhöht hat.
Das mit dem Disassembly wird vermutlich relativ schwierig, weil das wieder nur über Screenshot gehen wird, befürchte ich. Da hab ich aber auch schonmal reingeschaut und finde da ehrlich gesagt keinen Fehler. In einer Simulation (ohne Hardware) funktioniert das Programm auch einwandfrei.
Im Anschluß nochmal ein Originallisting meines aktuellen Programms:
Datei main.c:
Code:
//declaration of functions
extern void PortInit(void); //declared in ports.c
extern void InterruptInit(void); //declared in int.c
//declaration of variables
volatile unsigned char counter; //counter variable for number of trigger events
volatile unsigned char time1, time2, time3; //time in ms (LSB to MSB)
volatile unsigned char phase; //phase information
volatile bit usePhase; //0 if phase is not used, 1 if phase is used
volatile bit mainc; //1 if main contactor is used, else 0
void main (void)
{
PortInit(); //initialize the ports
InterruptInit(); //initialize the trigger-interrupts
counter = 0; //set starting value for counter
EA = 1; //enable all interrupts globaly
while(counter < 5); //wait for 5 transmissions from the computer
/*if (mainc == 1)
{
P0_DATA &= 0xf8; //close the serial contactors (shoot is performed with main contactor)
}*/
while(1);
}
Datei ports.c:
Code:
void PortInit(void)
{
P0_DIR = 0xb; //P0.0, P0.1 and P0.3 are outputs
P1_DIR = 0x0; //P1.1 is an input
P3_DIR = 0x0; //P3.0 to P3.7 are inputs
//for testing:
P1_DIR |= 0xc0; //P1.6 and P1.7 are outputs
P0_DIR |= 0x4; //P0.2 is an output
}
Datei int.c:
Code:
extern volatile unsigned char counter; //declared in main.c
extern volatile unsigned char time1, time2, time3, phase; //declared in main.c
extern volatile bit usePhase; //declared in main.c
extern volatile bit mainc; //declared in main.c
void InterruptInit(void)
{
//set priorities of interrupts (shoot = 2, trigger and phase = 1)
IP1 = 0x8; //reset bit for shoot, set bit for trigger
IPH1 = 0x4; //set bit for shoot, reset bit for trigger
IP |= 0x4; //set bit for phase
IPH = 0x3b; //reset bit for phase
EXICON0 = 0x14; //edge selection for the three signals
IEN0 &= 0xfb; //disable interrupt for phase (individually)
IEN1 |= 0x4; //enable interrupt for the shoot(individually)
IEN1 |= 0x8; //enable interrupt for trigger (individually)
}
void TriggerISR() interrupt 8
{
P0_DATA &= 0xf8;
switch (counter)
{
case 0:
time1 = P3_DATA; //get LSB of time
/*if (time1 == 0)
{
P0_DATA |= 1;
}
else
{
if (time1 == 2)
{
P0_DATA |= 2;
}
}*/
break;
case 1:
time2 = P3_DATA; //get 2nd byte of time
/*if (time2 == 0)
{
P0_DATA |= 0x3;
}
else
{
if (time2 == 1)
{
P0_DATA |= 0x4;
}
}*/
break;
case 2:
time3 = P3_DATA; //get MSB of time
EA = 0; //disable all interrupts
/*if (time3 == 0)
{
P0_DATA |= 0x1;
}
else
{
if (time3 == 13)
{
P0_DATA |= 0x7;
}
}*/
break;
case 3:
phase = P3_DATA; //get phase information
/*if (phase == 0)
{
P0_DATA |= 0x6;
}
else
{
if (phase == 128)
{
P0_DATA |= 0x1;
}
}*/
break;
case 4:
switch (P3_DATA)
{
case 0:
usePhase = 0; //-> phase ignored
mainc = 0; //use serial contactors
break;
case 1:
usePhase = 1; //-> phase important
mainc = 0; //use serial contactors
break;
case 2:
usePhase = 0; //-> phase ignored
mainc = 1; //use main contactor
break;
case 3:
usePhase = 1; //-> phase important
mainc = 1; //use main contactor
break;
default:
break;
}
break;
default:
break;
}
counter++;
P0_DATA = counter;
IRCON0 &= 0xfb; //reset IR-Bit of trigger
}
Wie gesagt, das EA=0 in int.c deaktiviert nur alle Interrupts (war zum Testen).
Vielen Dank für deine Bemühung!!! und viele Grüße
Habs gelöst, aber warum es passiert weiß ich immer noch nich
Hallo Robert,
ich muss ehrlich zugeben, ich habe auch ne Zeit gebraucht, bis ich verstanden habe, wozu der switch umgebaut wurde.
Hab nochmal nachgeschaut, ich habe tatsächlich nur auf positive Flanke getriggert.
Mittlerweile habe ich das Problem auch eingegrenzt und behoben (zumindest von der Software-Seite)!
Es war ehrlich gesagt nicht ganz so, wie ich es am Anfang gepostet hatte. Der 2. Interrupt wird nur ausgelöst, wenn im 1. Byte das höchstwertige Bit gesetzt ist! Dann und nur dann wird bei der Übertragung des 2. Bytes ein 2. Interrupt generiert und er macht Schrott.
Hab das jetzt einfach so abgefangen:
Code:
case 1:
time2 = P3_DATA;
if (time1 > 127 && error == 1)
{
error = 0;
counter--;
}
break;
error ist nochmal ein Bit, damit ich weiß, ob ich das erste mal in den case reinlaufe oder nicht.
Das funktioniert zumindest mal - auch wenn ich nach wie vor nicht weiß, warum der 2. Interrupt ausgelöst wird. Das wird wohl das Geheimnis des Controllers bleiben...
Vielen Dank für deine tatkräftige Unterstützung und viele Grüße
Michael
das hab dann wohl ich unterschlagen
Hallo Robert,
hm, wenn ich das mal geahnt hätte...
Ich habe - wie von Infineon mit ihrem Easy-Kit empfohlen - eine StartUp-Routine in mein Projekt integriert. Diese wird natürlich zum Code dazugebunden und vor meiner main ausgeführt. Dort habe ich auch einen Eintrag für den SP gefunden.
Weiter unten findest du zum einen den Code der StartUp, zum anderen eine Übersicht über die Adresse, wo der SP hininitialisiert wird (0x20, was direkt in meinen Interrupt-Vektoren liegt!). Wie kommt Keil drauf, den einfach mal so dahin zu legen? Die müssten doch auch wissen, dass in der Gegend die IR-Vektoren liegen, oder??
Ich weiß ja nicht, ob es wirklich daran liegt, aber kann ich den Eintrag einfach so verändern - ohne, dass mein Programm dann gar nicht mehr läuft?
Wobei ich dann immer noch nicht wirklich den Grund erkennen kann, warum es nur beim 1. Byte schiefgeht und warum er nur den einen Interrupt zweimal ausführt (schließlich liegen auf Adressen näher an 0x20 noch 2 andere IR-Vektoren)!?
Startup:
Code:
STARTUP1:
C:0x09BD 787F MOV R0,#0x7F
C:0x09BF E4 CLR A
IDATALOOP:
C:0x09C0 F6 MOV @R0,A
C:0x09C1 D8FD DJNZ R0,IDATALOOP(C:09C0)
C:0x09C3 90F000 MOV DPTR,#0xF000
C:0x09C6 7F00 MOV R7,#0x00
C:0x09C8 7E02 MOV R6,#0x02
C:0x09CA E4 CLR A
XDATALOOP:
C:0x09CB F0 MOVX @DPTR,A
C:0x09CC A3 INC DPTR
C:0x09CD DFFC DJNZ R7,XDATALOOP(C:09CB)
C:0x09CF DEFA DJNZ R6,XDATALOOP(C:09CB)
C:0x09D1 758120 MOV SP(0x81),#0x20
C:0x09D4 02086F LJMP main(C:086F)
Memory:
Code:
C:0x0020 00 NOP
C:0x0021 00 NOP
C:0x0022 00 NOP
C:0x0023 00 NOP
C:0x0024 00 NOP
C:0x0025 00 NOP
C:0x0026 00 NOP
C:0x0027 00 NOP
C:0x0028 00 NOP
C:0x0029 00 NOP
C:0x002A 00 NOP
C:0x002B 02092A LJMP T2ISR(C:092A)
C:0x002E 00 NOP
C:0x002F 00 NOP
C:0x0030 00 NOP
C:0x0031 00 NOP
C:0x0032 00 NOP
C:0x0033 00 NOP
C:0x0034 00 NOP
C:0x0035 00 NOP
C:0x0036 00 NOP
C:0x0037 00 NOP
C:0x0038 00 NOP
C:0x0039 00 NOP
C:0x003A 00 NOP
C:0x003B 00 NOP
C:0x003C 00 NOP
C:0x003D 00 NOP
C:0x003E 00 NOP
C:0x003F 00 NOP
C:0x0040 00 NOP
C:0x0041 00 NOP
C:0x0042 00 NOP
C:0x0043 0208D3 LJMP ShootISR(C:08D3)
C:0x0046 00 NOP
C:0x0047 00 NOP
C:0x0048 00 NOP
C:0x0049 00 NOP
C:0x004A 00 NOP
C:0x004B 020800 LJMP TriggerISR(C:0800)
Außerdem sind in der Startup folgende Dinge eingestellt:
- IDATA memory length 0x0080
XDATA memory start address 0xF000
XDATA memory length 0x0200
PDATA memory start address 0x0000
PDATA memory length 0x00
Und es gäbe einen Eintrag "Reentrant Stack Initialization", bei dem man für das SMALL, LARGE und COMPACT-Modell jeweils "top of stack" einstellen kann.
In den Projekteinstellungen habe ich noch folgende Zeile gefunden:
Code(C:0x0-c:0x2fff, c:0xa000-c:0xafff), XDATA(X:0xf000-X:0xf1ff)
Wenn du jetzt sagst "Mensch, warum schreibt der das denn nicht gleich, dann hätten wir das Problem in 5 Minuten gelöst gehabt!", dann muss ich mich entschuldigen, aber auf solche Zusammenhänge wäre ich wahrscheinlich in 5 Jahren nicht gekommen. Zu meiner Verteidigung sei gesagt, dass das erst der 2. Microcontroller ist, den ich bislang programmiere. Wir hatten das Fach an der Schule und davor habe ich noch nie was von C - oder gar Assembler! - gehört gehabt...
Viele Grüße
Michael
Das Mysterium ist gelöst!!!
Hallo Robert,
es war natürlich doch die Hardware...
Moment, mir fällt gerade was ein.
Erstmal was ich gerade noch gedacht habe: P3.7 ist gleichzeitig als Alternativbelegung der externe Interrupt 4. Die ext. IR 3-6 teilen sich einen IR-Vektor und damit eine ISR!! Man kann auch nur alle 4 IR freigeben oder sperren, nicht einzeln. Deswegen dachte ich, würde er die gleiche ISR nochmal anspringen.
Dann ist mir aber der Knackpunkt aufgefallen: Ich habe ja auch versucht einen anderen Interrupt zu nehmen. Und das war der ext. IR 2! Der hat aber ein extra Enable bit und teilt sich mit niemandem seine ISR! Und das Phänomen ist trotzdem aufgetreten.
Also kann es das doch fast auch nicht sein, oder?
Außerdem habe ich mal gemacht, was du vorgeschlagen hast. Es wird jedesmal (!) definitiv das IR-Bit des ext. IR 3 gesetzt (hab ich mit IRCON0 & 8 ausprobiert).
Das ist dann wohl der Todesstoß für diese Theroie...
Hab auch das mit <127 und so ausprobiert.
Wenn ich >127 schicke und nicht nach time1 schreibe ruft er trotzdem zweimal die ISR
Schicke ich <127, schreibe aber 128 in time1 ruft er die ISR nur einmal. Heißt also wohl oder übel doch die HW. (insofern stimmt wenigstens der erste Satz noch :-))
Leider muss ich mich jetzt auch schon wieder auf den Heimweg machen (ich würde wirklich gerne weiterbasteln, aber ich hab noch was vor...). Bin aber natürlich weiterhin dankbar für jede Anregung und setze sie gleich morgen früh um (nehm mir für morgen abend mal nichts vor, damit ich für den Fall der Fälle auch länger machen kann!).
Viele Grüße nach Wien und einen schönen Abend noch
Michael
Hab da noch was vergessen
kannst du laut sagen. Wie gesagt, morgen nehme ich mir nix vor. (Schreib jetzt noch kurz von daheim, weil es mich ned loslässt!)
Zu deiner Frage: Wenn es mal fertig läuft kommt der Strobe relativ schnell (so ein paar ms Abstand). Im Moment kommt er dann, wenn ich sage, dass er kommen soll. Also durchaus mit mehreren Sekunden dazwischen.
Mir ist noch was eingefallen, was ich schreiben wollte:
Durch die Standardbelegung von IRCON0 mit lauter 0en wird IR 4 so konfiguriert, dass er auf negative Flanke triggert! Und das Problem kommt auch, wenn er nach dem zweiten Byte eine negative Flanke hat - wie ich jetzt weiß. Ich habe nur nie eine Kombination versucht, die nach dem ersten Byte von high auf low im 8. Bit wechselt... Deshalb waren die anderen Bytes 'beliebig'. Nachher ist man halt immer gescheiter, gell.
Habs jetzt auch mal mit so einer Abfangschleife (in der ISR) versucht:
Code:
if (IRCON & 0x10)
{
IRCON &= 0xef;
return;
}
else
{
//der ganze switch auf den counter
}
hat aber leider auch nix gebracht, kommt trotzdem noch zweimal...
Schönen Abend und bis morgen