Hallo zusammen, ich programmiere seit ca. 3 Wochen einen pic18f45k20 uC
auf dem Demoboard des PICkit3 und bin jetzt zu den Interrupts gekommmen
und hab jetzt auch schon meine ersten Probleme. Ich benutze MPLABX und
den XC8 Compiler. Da ich im Internet nicht wirklich fündig geworden bin,
versuche ich hier mein Glück. :)
Mein Problem: Ich lese das A/D-Ergebnis in der Polling-Schleife ein und
hab kein Problem, sobald ich dies allerdings über eine Interruptfunktion
tun möchte, funktioniert gar nichts mehr ^^ Bitte um Hilfe :/
Mein Code:
Ich benutze zwar einen anderen Compiler, aaber da mache ich das immer
so:
#pragma origin 4 //Der folgende Code steht ab Adresse 4
(ISR)
interrupt InterruptRoutine(void)//InterruptServiceRoutine
{
GIE = 0; //Interrupts sperren
int_save_registers //W, STATUS (und PCLATH) retten
char save_FSR = FSR; //save FSR
if (INTF) //Ursache des Interrupts feststellen
{ //Interrupt an PORTA.2 (INTF)
blabla....
INTF = 0; //Interrupt wieder scharf machen
}
FSR = save_FSR; //restore FSR
int_restore_registers //W, STATUS (und PCLATH)
wiederherstellen
GIE = 1; //Interrupts wieder freigeben
}
Ich habe jetzt eine andere Lösung gefunden. Diese Interrupts erfüllen eh
keinen wirklichen Sinn und deshalb überspringe ich das Kapitel mit den
Interrupts. Dieser unnütze Kram!
Evtl. liegt das folgende Problem vor:
Wenn das Programm in die ISR springt und die if Anweisung nicht
zutrifft, wird das Interrupt Flag nicht zurückgesetzt und die Kiste
bleibt hängen.
Bei dem einsamen return in der letzten Zeile hätte ich vermutet, dass
der Compiler meckert, da die Funktion void als Rückgabetyp hat. Wird
aber wahrscheinlich einfach ignoriert.
Hallo
warum ist in main() noch das polling am ADC drin? und du startest dort
auch wieder eine ADC convertierung?
also, entweder du benutzt interrupts, und startest dort wieder den ADC.
oder in main via polling aber nicht beides.
nicht das dies dein problem seinmuss, aber so wird es sicher auch nichts
mehr tun,
da du ja in main()
auf das flag schaust,
>while(ADCON0bits.GO_DONE != 0); //auf A/D-Wandlung warten
das wirst du ev gar nie mehr erleben, hat ja dein interrupt schon
erledigt.
mfG
Peter
habe das zurücksetzen des Interrupt-Flags aus der if-Anweisung
herausgetan, sodass es nun immer zurückgesetzt wird, wenn ich in die ISR
gehe(keine Änderung), aber eigtl dürfte doch kein anderes Interrupt-Flag
gesetzt sein, da ja der A/D-Wandler enabled ist
und die 2 zeilen
ADCON0bits.GO_DONE = 1; //A/D-Wandlung starten
while(ADCON0bits.GO_DONE != 0); //auf A/D-Wandlung warten
kommentiere ich ja aus, bevor ich das mit dem Interrupt versuche.. der
Code oben funktioniert, da ich ja die Aktivierung des ADC-Interrupts
auskommentiert habe
In der main() setzt du in der while{} Schleife das Bit
ADCON0bits.GO_DONE, und hast danach eine leere while{} die solange läuft
wie dieses Bit gesetzt ist.
In deiner ISR hast du dann aber nochmals "ADCON0bits.GO_DONE = 1" wenn
der ADC Interrupt zutrifft.
Somit wird in deiner while{}-Warteschleife dieses Bit niemals 0, und
somit hängt er dann auch dort fest. Ebenso setzt du die ad_msb sowie
ad_lsb an beiden Stellen, also in der main als auch im Interrupt. Da du
aber eine neue Konvertierung im Interrupt startest, wird dann in der
main bei der Zuweisung ein ungenaues Ergebnis herauskommen.
Nimm das "ADCON0bits.GO_DONE = 1" und die Zuweisung zu ad_msb und as_lsb
in dem Interrupt raus. In der main entfernst du dann den Block
den Block
ADCON0bits.GO_DONE = 1; //A/D-Wandlung starten
while(ADCON0bits.GO_DONE != 0); //auf A/D-Wandlung warten
ad_msb = ADRESH; //A/D-Ergebnis Bit 2 bis 10
ad_lsb = ADRESL; //A/D-Ergebnis Bit 0 und
1
habe ich bereits entfernt, dieser Block war lediglich ein Stück des
alten Codes, den ich nur zu Testzwecken noch drin hatte, habe ihn aber
immer mit /**/ auskommentiert, wenn ich ISR getestet habe.
dein Vorschlag funktioniert, ist allerdings nur eine schönere
Programmierung des alten Blocks oder nicht? und meine ISR ist wieder
ohne Funktion, ich wollte eben die ad_msb = ..... Zuweisung in der ISR
mfg Chris F.
Friedrich Ch. schrieb:> den Block> ADCON0bits.GO_DONE = 1; //A/D-Wandlung starten> while(ADCON0bits.GO_DONE != 0); //auf A/D-Wandlung warten> ad_msb = ADRESH; //A/D-Ergebnis Bit 2 bis 10> ad_lsb = ADRESL; //A/D-Ergebnis Bit 0 und> 1> habe ich bereits entfernt, dieser Block war lediglich ein Stück des> alten Codes, den ich nur zu Testzwecken noch drin hatte, habe ihn aber> immer mit /**/ auskommentiert, wenn ich ISR getestet habe.>> dein Vorschlag funktioniert, ist allerdings nur eine schönere> Programmierung des alten Blocks oder nicht? und meine ISR ist wieder> ohne Funktion, ich wollte eben die ad_msb = ..... Zuweisung in der ISR> mfg Chris F.
Du kannst die Zuweisung von ad_msb und ad_lsb natürlich auch in der IRQ
Routine machen. Allerdings solltest du den ADC dann nicht mehr neu
starten im IRQ. Das Problem ist nämlich das du ja zwei Werte ausliest.
Nun kann es passieren das du grad ad_msb in der main benutzt, dann
passiert der ADC Interrupt, und als nächstes benutzt due dann ad_lsb in
der main. Da aber der ADC Interrupt dazwischen war, gehört _lsb dann zum
neuen Wert, währen der _msb dann noch vom alten Wert war.
Stell dir vor der alte ADC Wert war 0x00FF und der neue Wert ist 0x0100.
Zuerst liest du den _msb, also 0x00 vom alten Wert. ADC Interrupt kommt,
und als nächstes liest du den _lsb, der nun aber ebenfalls 0x00 ist. im
Ergebnis hast du dann also 0x0000 bekommen, was natürlich nicht dem
wirklichen ADC Ergebnis entspricht.
Grüße,
Chris
ja das versteh ich, aber habe ich eben diesen Fehler nicht durch das
sperren des ADC-Interrupts verhindert??
PIE1bits.ADIE = 0; //ADC-Interrupt disabled
CCPR2L = ad_msb;
CCPR1L = ad_msb;
CCP2CONbits.DC2B = ad_lsb; //Steigerung Duty Cycle auf
0,1%
CCP1CONbits.DC1B = ad_lsb;
PIE1bits.ADIE = 1; //ADC-Inerrupt enabled
ich denke, ich komme dem Problem schon näher, ich habe ein bisschen
probiert und bin auf das Ergebnis gekommen, dass der Code erst dann
nicht funktioniert, wenn ich in der ISR die Zuweisung
ad_msb = ADRESH;
ad_lsb = ADRESL;
mache. Wenn ich nun das mit der Zwischenspeicherung lasse und das
Ergebnis ADRESH direkt meinen LED´s in der ISR mit
LATD = ADRESH;
zuweise funktioniert dies.
Aber warum will diese Zwischenspeicherung nicht funktionieren, ich muss
doch die Möglichkeit haben, eine Variable der main in der ISR zu
bearbeiten?
Friedrich Ch schrieb:> ja das versteh ich, aber habe ich eben diesen Fehler nicht durch das> sperren des ADC-Interrupts verhindert??> PIE1bits.ADIE = 0; //ADC-Interrupt disabled> CCPR2L = ad_msb;> CCPR1L = ad_msb;> CCP2CONbits.DC2B = ad_lsb; //Steigerung Duty Cycle auf> 0,1%> CCP1CONbits.DC1B = ad_lsb;> PIE1bits.ADIE = 1; //ADC-Inerrupt enabled>> ich denke, ich komme dem Problem schon näher, ich habe ein bisschen> probiert und bin auf das Ergebnis gekommen, dass der Code erst dann> nicht funktioniert, wenn ich in der ISR die Zuweisung> ad_msb = ADRESH;> ad_lsb = ADRESL;> mache. Wenn ich nun das mit der Zwischenspeicherung lasse und das> Ergebnis ADRESH direkt meinen LED´s in der ISR mit> LATD = ADRESH;> zuweise funktioniert dies.> Aber warum will diese Zwischenspeicherung nicht funktionieren, ich muss> doch die Möglichkeit haben, eine Variable der main in der ISR zu> bearbeiten?
Inwiefern funktioniert die Zuweisung nicht? Wie sieht es aus wenn du den
oberen Block, in dem du CCPRxx behandelst, mal auskommentierst, in der
IRQ die Zuweisung drinlässt, und dafür im main Loop ein "LATD = ad_msb;"
machst? Funktioniert das dann?
Ich sehe nämlich das du irgendwas komisches in dem obigen Block machst:
CCP2CONbits.DC2B = ad_lsb;
Du versuchst hier einem einzelnen Bit ein Byte zuzuweisen. Vielleicht
verchluckt er sich ja daran? Normalerweise sollte er das Bit löschen
wenn ad_lsb = 0 ist, und ansonsten immer setzen. Wenn du aber 10 Bit PWM
willst, musst du aber die DCxB0 und DCxB1 in beiden CCPxCON Registern
von Hand setzen.
Reduziere das ganze doch erstmal, ungefähr so:
Friedrich Ch schrieb:> Aber warum will diese Zwischenspeicherung nicht funktionieren, ich muss> doch die Möglichkeit haben, eine Variable der main in der ISR zu> bearbeiten?
Ich muss zu meiner Schande gestehen das ich mir deinen Code oben jetzt
nicht genau angesehen habe. Kenne mich mit dem XC8 auch noch nicht aus.
Allerdings können Probleme mit eigendlich zulässigen Befehlen in der
Interruptschleife die verschwinden wenn diese Befehle entfernt werden im
Grunde ja nur zwei Ursachen haben:
1. Die Interruptschleife ist für die gewählten Timingparameter schlicht
zu langsam. Praktisch sofort nach dem Rücksprung ins Hauptprogramm
erfolgt direkt eine neue Interruptauslösung mit dem Ergebniss das es zu
keiner sinnvollen Abarbeitung des Hauptprogramms mehr kommt.
2.Nicht beachtete Seiteneffekte des Befehls. Diese können dabei auch auf
Grund eines Hardwarefehlers auftreten (Errata Sheet studieren)
Die erste Möglichkeit ist mit Abstand die häufigste Variante, gefolgt
von Missverständnissen hinsichtlich der Seiteneffekte. Aber auch die
Hardwarefehler sind leider bei den modernen Mikrocontrollern ALLER
Fabrikate ein Problem das immer wieder auftritt. So gut wie jeder der
beruflich mit Mikrocontrollern arbeitet wird schon das eine oder andere
Mal auf dieses Problem gestossen sein.
Daher immer daran Denken: Das Problem sitzt zwar in den meisten Fällen
vor dem Bildschirm, aber doch nicht immer.
Deshalb immer auch die Errata Sheets studieren wenn man in einem solchen
Fall nicht weiter weiss, denn da finden sich die aktuell bekannten
Fehler nebst Empfehlungen zum Workarround.
Im beruflichen Umfeld sollte das Errata Sheet natürlich schon zum
Zeitpunkt der Bauteilauswahl bekannt sein, denn ein Problem das für den
Hobbybastler nur ärgerlich ist kann da zu Problemen führen die
Zigtausende von Euros kosten können.
Bei dir glaube ich aber an den Fall 1.
lg Chris
Hallo Chis1,
habe den code schon ausprobiert, funktioniert auch nicht. Leuchten immer
zufällige LEDs, nach dem ersten brennen die binäre 2, dann 0 und jetzt
die binäre 130...
danke Chris2, für die Info, denke wohl auch, dass es wohl in die
Richtung gehen werden... wie löst man den so ein Problem? kann man die
AD-Wandlung langsamer machen, oder einfach keine ISR für ADC schreiben,
sondern in der Polling-schleife behandeln... oder sollte ich Fosc
hochschrauben, takte ja lediglich mit 1MHz
lg Chris
Friedrich Ch. schrieb:> wie löst man den so ein Problem? kann man die> AD-Wandlung langsamer machen, oder einfach keine ISR für ADC schreiben,> sondern in der Polling-schleife behandeln... oder sollte ich Fosc> hochschrauben, takte ja lediglich mit 1MHz
Das du keine ISR für die AD Wandlung verwendest würde sich das Problem
natürlich erledigen, allerdings ist das nicht immer eine Option.
Die Alternative ist das du die Dauer der AD Wandlung im Verhältniss zur
Taktfrequenz erhöhst!. Also nicht NUR die Fosc hochschrauben sondern
gleichzeitig auch die Dauer für 1 T_ad heraufsetzen.
Im Moment (Initwert) liegt die Dauer für ein T_ad bei 2µs. Die Wandlung
dauert immer elf T_ad, also im Moment 22µs.
Der PIC braucht bei 1MHz für einen Befehlszyklus 4µs. Damit kann der PIC
in dieser Zeit maximal 5 Assemblerbefehle ausführen. Je nach Art des
Befehls sogar weniger.
Ein einzelner C Befehl wird bei der Compilation ja in Assembler
übersetzt. Es kann sein das ein ASM Befehl für einen C Befehl reicht,
meistens sind es aber deutlich mehr.
Damit kann es sein das der PIC in der Zeit nach Beginn der Wandlung
gerade mal einen C Befehl schafft. eventuell nicht mal dies.
Du könntest ja mal versuchen die Taktfrequenz auf 16Mhz zu setzen und
T_ad auf Fosc/16. Ist der Fehler dann weg kannst du ja immer noch
schrittweise die Frequenz wieder absenken.
Statt der 16MHz kannst du natürlich auch gleich auf 64 MHz gehen.
lg Chris
das wars, jetzt läufts ;)
hab jetzt auf Fosc = 8MHz und T_ad = Fosc/8
danke, vielen vielen Dank!!
wäre glaub ich nie darauf gekommen, dass es das ist
mfg Chris
Wenn du sowieso einen Pickit3 hast - solltest dich zuerst in den
In-Circuit-Debugger einarbeiten. Das lohnt sich. In Summe hast du dein
Programm schneller am laufen.
Ich wollte nur mal erwähnen das die Interrupts bei der Aktuellen Version
von XC8 Compiler so aussehen müssen:
void __interrupt(high_priority) my_isr(void){
// mach was....z.Bsp.
if (INTCONbits.TMR0IF) {
INTCONbits.TMR0IF=0;
}
}
oder einfach so:
void __interrupt() my_isr_2(void){
// mach was ....
}
Norbert S. schrieb:> Ich wollte nur mal erwähnen das die Interrupts bei der Aktuellen Version> von XC8 Compiler so aussehen müssen:
Muss nicht! Sieh Handbuch....