Hallo an alle,
hab ein Programm für ein E-Kart geschrieben. MPLAB im C code.
Läuft soweit gut aber nach einiger Zeit >5 min frieren die Werte im
Display und PWM ein. Eine LED in verschieden Teilen -auch ISR- blinkt
weiter. Das Programm läuft somit.
Hab mal das Gas auf x Wert geestzt und nach dem einfrieren war der x
Wert der PWM auch fest was sehr ungünstig wäre ;-).
Anbei der wesentliche Code. Hat jemand Ideen woran es hängen könnte?
Vielen Dank
1
volatileunsignedintAN0;// 0 -> 65535
2
volatileunsignedcharAN0_offset=185;// 0 -> 255 Offset wert immer nur minimal wert nehmen
3
volatileunsignedintAN0_max=876;// 0 -> 65535
4
volatileunsignedshortERG;// 0 -> 65535 ANO entspricht DC
danny_0678 schrieb:> Das Programm läuft somit.> Hat jemand Ideen woran es hängen könnte?
Wenn das Programm läuft kanns eigentlich nur die Hardware sein...
> Anbei der wesentliche Code.
Zeigt genau dieser wesentliche Code den wesentlichen Fehler?
> while( BusyADC() ){}
Hups, sowas in der Main-Loop artet schnell mal ein einen Deadlock aus.
Hast du noch mehr davon?
> Anbei der wesentliche Code.
Bitte wie in der Bedienungsanleitung über jeder Textbox beschrieben die
[c]-Tags verwenden.
Hallo und danke,
Die Zeile
while( BusyADC() ){}
Steht in so jeder Zeile von PIC18 Code beim ADC im Netz. Der Controller
muss ja warten, bis das Ergebnis vorliegt.
Hardware schließ ich eigentlich aus, die läuft ja 5 -10 min, da kann es
ja nur der Code sein denk ich. Bloß was ? im Code ?
Danke.
danny_0678 schrieb:> Hardware schließ ich eigentlich aus, die läuft ja 5 -10 min, da kann es> ja nur der Code sein denk ich. Bloß was ? im Code ?> ...> Delay10KTCYx(250); // 0,5 s delay> ...
Sorry, aber wenn ich sowas lese, bekomme ich immer Plaque.
Du läßt den µC eine halbe Sekunde warten nur um was auf das Display zu
schreiben? Das muss schiefgehen. Ich bin insgesamt kein Freund von
Delay. Alles was über ein paar NOPs hinausgeht mache ich über Timer oder
frage in der Main-Loop das entsprechende Flag oder Register ab.
Die Idee mit der LED als Heartbeat kann man machen, wenn die aber in der
ISR steckt, zeigt die nur, dass die ISR angesprungen wird.
danny_0678 schrieb:> while (1)
Hallo, ja das ist 0,5 s nur einmalig, da es vor der
while (1)
Schleife ist und nur nach dem Start 1x durchlaufen wird.
Ansonsten ist im Hauptprogramm eigentlich kein Delay weiter als das vom
ADC.
Grüße
> Die Idee mit der LED als Heartbeat kann man machen, wenn die aber in der> ISR steckt, zeigt die nur, dass die ISR angesprungen wird.
Die LED war auch im Hauptprogramm vorhanden, welches beim Fehler
durchlaufen wird.
Mir wäre die Interrupt Routine zu lang. Kann sein das der nächste schon
ansteht wenn du Sie.verlassen willst. Dann funzt alles in der Routine
aber wenig außerhalb.
Wozu ist das asm Goto am Anfang der Routine?
Toby P. schrieb:> Mir wäre die Interrupt Routine zu lang. Kann sein das der nächste schon> ansteht wenn du Sie.verlassen willst. Dann funzt alles in der Routine> aber wenig außerhalb.
Das wäre kein wirkliches Problem, da wird dann ein Interrupt
ausgelassen. Aber gegen Ende des Handlers werden die Interrupte enabled,
wenn also inzwischen ein weiterer Interrupt passiert ist, geht er wieder
in den Handler. Und das solange, bis der Stack aufgeraucht ist. Es
sollte also sowohl das sinnlose Disablen der Interrupte am Anfang als
auch schädliche Enablen gegen Ende des Handlers raus.
Toby P. schrieb:> Wozu ist das asm Goto am Anfang der Routine?
Ich kenne zwar die PIC18 nicht, aber weder bei den PIC16 noch bei den
PIC24 habe ich je irgendwas in Assembler im Interruptkontext gebraucht.
Das erledigt alles der Compiler. Ich denke, das ist bei den PIC18
genauso.
MfG Klaus
Klaus schrieb:> Ich kenne zwar die PIC18 nicht, aber weder bei den PIC16 noch bei den> PIC24 habe ich je irgendwas in Assembler im Interruptkontext gebraucht.> Das erledigt alles der Compiler. Ich denke, das ist bei den PIC18> genauso
Kommt auf den Compiler an. Das hier ist wohl für den C18 geschrieben und
da passt das so.
danny_0678 schrieb:> while( BusyADC() ){}>> Steht in so jeder Zeile von PIC18 Code beim ADC im Netz. Der Controller> muss ja warten, bis das Ergebnis vorliegt.
Die machen oft einfache Beispiele. Ist nicht immer der "Beste" Weg.
Du hast jetzt nicht den genauen Controller Typ genannt, aber
normalerweise hat jeder Controller einen Freerunning / Auto-Conversion
oder ähnlich genannten Mode. Du triggerst den ADC in der Main. Du hast
also keine spezielle Anforderung an eine äquidistante Abtastung oder
Synchronisierung. Wieso lässt Du den ADC also nicht einfach
selbsttriggernd laufen und greifst in deiner Endlosschleife einfach ins
Ergebnisregister? Dann brauchst Du an der Stelle nicht warten.
Da der Controller keine FPU zu haben scheint würde ich mir die unnötige
Float Rechnung sparen. Hat zwar nichts mit Deinem Problem zu tun, aber
ist unnötig.
Der Heart-Beat wurde ja schon erwähnt. In der ISR macht der keinen Sinn.
Mach ihn mal in die Main, evtl. runtergeteilt. Dann siehst Du ob deine
Main wirklich noch durchlaufen wird.
Evtl. könntest Du mal noch im Watchdog ISR eine LED anschalten oder ein
Pin Toggeln. Dann würde man auch sehen ob der Zuschlägt. Dann würde ich
aber auch gleich noch die Watchdog Zeit auf eine kleinere Zeit als 65
Sekunden setzen.
Grüße
MaNi
Volker S. schrieb:> Kommt auf den Compiler an. Das hier ist wohl für den C18 geschrieben und> da passt das so.
Ok, jetzt habe ich noch mal genauer in meine alten Unterlagen geschaut.
Das passt doch nicht ganz ;-)
Beim low-priority IR sollte das pragma interrupt*low* stehen
1
#pragma interruptlow InterruptHandlerLow
Das könnte natürlich fatale Folgen auf das Context Saving haben...
Klaus schrieb:> habe ich je irgendwas in Assembler im Interruptkontext gebraucht.
Hab da noch mal drüber nachgedacht, macht Sinn weil durch das goto die
Routine in einen sinnvollen Speicherbereich verlegt werden kann.
Ob das Interrupt enablen nötig ist weiß ich nicht. Streng genommen muss
das der RTI Befehl machen da sonst die Routine mehrfach aufgerufen
werden kann und der Stack überläuft.
MaNi schrieb:> Wieso lässt Du den ADC also nicht einfach selbsttriggernd laufen
Der er dann auf das ready flag warten muss ist das imo unwichtig. Der
ADC hängt sich ja nicht auf und hat seine max. conversion time.
Ich würde als erstes die Interrupts vorm LCD schreiben diasablen und
danach wieder freigeben.
LCD's haben fast immer Timingprobleme und was mit einen Typ läuft geht
mit dem anderer nicht.
danny_0678 schrieb:> Hab mal das Gas auf x Wert geestzt und nach dem einfrieren war der x> Wert der PWM auch fest was sehr ungünstig wäre ;-).
So etwas würde ich immer eigensicher bauen. Also min. ein Monoflop das
durch die Main getriggert wird.
So als Idee ist auch eine Variable ganz nett die durch jeden
Programmteil hochgezählt wird. Wenn dann am Ende der Wert falsch ist was
aus dem Ruder gelaufen.
Toby P. schrieb:> Hab da noch mal drüber nachgedacht, macht Sinn weil durch das> goto die Routine in einen sinnvollen Speicherbereich verlegt> werden kann.
Das erledigt das #pragma, das setzt den JMP an die Interruptadresse.
Volker S. schrieb:> Beim low-priority IR sollte das pragma interrupt*low* stehen> #pragma interruptlow InterruptHandlerLowToby P. schrieb:> Ob das Interrupt enablen nötig ist weiß ich nicht. Streng genommen> muss das der RTI Befehl machen da sonst die Routine mehrfach> aufgerufen werden kann und der Stack überläuft.
Es macht auch der Return from Interrupt (RETI) und das enablen ist nicht
nur nicht nötig sondern schädlich.
MfG Klaus
danny_0678 schrieb:> Steht in so jeder Zeile von PIC18 Code beim ADC im Netz.
Trotzdem Murks, seine Rechenzeit so totzuschlagen (wie bei einem delay()
im ms-Bereich auch).
> Der Controller muss ja warten, bis das Ergebnis vorliegt.
Nein, er muss nur nachschauen, ob der Wandler fertig ist. Das ist ganz
grundlegend was anderes.
Bei meinen Programmen läuft der Prozessor immer schnellstmöglich durch
die Hauptschleife. Mehr als 10ms darf er dafür niemals brauchen. Und
abhängig von den gerade herrschenden Bedingungen führt er Aktionen aus:
wenn der ACD grade frei ist, wird die nächste Wandlung gestartet, wenn
grad wieder eine Sekunde um ist, dann wird das vom Timerinterrupt mit
einem Flag angezeigt und ich mache das, was jede Sekunde nötig ist. Und
dann werden die entprellten Eingänge abgefragt, und entsprechend
reagiert. Und wenn eine Aktion länger dauern oder gar Abwarten nötig
machen würde, dann wird sie in mehrere Schritte aufgeteilt (Stichwort
FSM, Zustandsautomat).
Toby P. schrieb:> Der ADC hängt sich ja nicht auf und hat seine max. conversion time.
Wenn ihn nicht zufällig irgendwo in einer ISR wer abschaltet...
Klaus schrieb:> Es macht auch der Return from Interrupt (RETI) und das enablen ist nicht> nur nicht nötig sondern schädlich.
Ja, da sollte man die Finger weg lassen ;-)
Volker S. schrieb:> Beim low-priority IR sollte das pragma interrupt*low* stehen:> #pragma interruptlow InterruptHandlerLow>> Das könnte natürlich fatale Folgen auf das Context Saving haben...
Der PIC18 hat zwei IR-Vektoren aber nur einen Satz Shadow Register zum
automatischen Sichern des Context. Beim HighPriority IR werden
Arbeitsregister W, STATUS und das Bankselect Register BSR automatisch in
diese Shadow Register gesichert. Beim LowPriority nicht, da muss man das
selbst (der Compiler) machen.
Der PIC18 Befehl RETFIE hat noch einen Parameter, der bestimmt ob beim
Rücksprung ein Restore aus den Shadowregistern stattfindet. Deshalb muss
man (der Compiler) auch beim Rücksprung wissen, ob es eine low- oder
eine high-Priority Routine war.
Hallo an alle Und danke für die zahlreichen Rückmeldung. Ich muss mir
erst einmal in Ruhe alles anschauen und verdauen was ihr so kommentiert
habt ? und würde mich melden. Vielen Dank noch einmal für die
zahlreichen Postings. Grüße
Toby P. schrieb:> MaNi schrieb:>> Wieso lässt Du den ADC also nicht einfach selbsttriggernd laufen>> Der er dann auf das ready flag warten muss ist das imo unwichtig.
Ich weiß jetzt nicht wie das bei genanntem Pic ist, aber die neueren
Controller die ich benutze Puffern das meist doppelt. Sprich, man kann
da jederzeit reinfassen. Ohne warten.
Viele gute Hinweise gab es ja schon ..., bei der bescheidenen Code
Quality und geringen Code Size würde ich unbedingt die Structure
überarbeiten!
1. nach xc8 portieren, dazu brauchst du vielleicht einen Tag, also
machbar, ... ich würde das in 20 Minuten bei dem Spielcode hinbiegen
können.
2. den Simulator nutzen und die Hinweise einarbeiten, sprich die viele
Redundance beseitigen, ich bekomme hier fast Augenkrebs.
3. z.B. den ungünstigen itoa Code, da mit Division, in die Tonne hauen
und mein Beispiel (ungetestet :-)) mal anschauen.
4. eine serielle Ausgabe einbauen, damit du besser beobachten kannst,
z.b. ob das LCD sich aufhängt oder der Code.
5. die vielen guten Hinweise hier, auch komplett umsetzten. Die sind im
Prinzip eigentlich alle nur Basic, sprich dein Code ist weit darunter
...
... der Hinweis kam ja schon, der Code ist voll von Mursks, noch so ein
Beispiel, wo du den Überblick verloren hast, nested Interrupts kann der
Pic18 nicht und höher als High Prio gehts auch nicht mit den ISR.
??? INTCONbits.TMR0IF = 0; //clear interrupt flag
??? INTCONbits.GIEH = 1; //enable interrupts
Der Code ist grundsätzlich ohne Struture, einfach nur geradeaus runter
geschrieben, ... du brauchst mehr Verständnis vom PIC18 und den HW
Resourcen - Datenblatt intensive lesen!
1
InterruptHandlerHigh()
2
{
3
4
if(INTCONbits.TMR0IF)// Timer 0
5
6
{
7
INTCONbits.TMR0IF=0;//clear interrupt flag
8
INTCONbits.GIEH=0;//disable interrupts
9
// LED = !LED;
10
WriteTimer0(26475);// war 80 , bei 3 h 2 sek zu schnell