Register retten bei Interrupt
Mittwoch, 23. Mai 2012
 
 

PIC Mikrocontroller Forum  |  PIC Mikrocontroller  |  PIC Mikrocontroller Allgemein  |  Register retten bei Interrupt « vorheriges nächstes »
Seiten: [1] Nach unten Drucken
Autor Thema: Register retten bei Interrupt  (Gelesen 1218 mal)
 
BL1
Jr. Member
**
Offline Offline

Beiträge: 82


Profil anzeigen
« am: September 21, 2011, 22:06:57 »

Moinsen,

wie ist denn das eigentlich beim 18er PIC?
Wenn ich meinen HighPriotityInt habe, geht der Controller auf 0x08 und findet dort seine Routine. Diese verweist ihn in der Regel auf eine ISR und hier werden zunächst die Systemregister gerettet und nach der ISR wieder hergestellt.

Nur, wenn ich einen Bootlader habe und der Bootlade-Speicherbereich nicht für die ISR reicht, bzw. gar nicht reichen soll(damit man da später bei der Firmware noch eingreifen kann), dann muß man doch die Speicherrettung/wiederhesrstellung in den Bereich ab 0x08 schreiben. Das aber geht doch gar nicht, wenn man ab 0x18 die LowISR stehen hat. Das paßt doch da nicht hin.
Alternative wäre natürlich, man macht zwei Levels von ISR. Eine auf 0x08, die springt z.B. nach 0x0900, dort werden dann die Register gerettet und dann einen Call nach 0xXXXX, wo man später mit der Firmware noch eingreifen kann. Dann aber hat man doch schon drei Levels von 8(?) im Stack verschenkt.


Wie macht man denn sowas elegant, so im guten Programmierstil?




BL
Gespeichert
Stampede
Globaler Moderator
Hero Member
*****
Offline Offline

Beiträge: 969



Profil anzeigen WWW
« Antworten #1 am: September 22, 2011, 01:25:06 »

Hi,

Zitat
Wenn ich meinen HighPriotityInt habe, geht der Controller auf 0x08 und findet dort seine Routine.
Ja.
Zitat
Diese verweist ihn in der Regel auf eine ISR und hier werden zunächst die Systemregister gerettet und nach der ISR wieder hergestellt.
Ja, fuer die High Prio ISR gibt es beim PIC18 ein sogenanntes Shadow Register Set, das automatisch die Inhalte von STATUS, W und das BANKSEL speichert. Ein sichern ist nur bei der Low Prio ISR noetig, und auch nur wenn man in Assembler schreibt. Bei C kuemmert sich der Compiler drum, man kann ihn aber auch anweisen, was genau gesichert werden soll.

Zitat
Nur, wenn ich einen Bootlader habe und der Bootlade-Speicherbereich nicht für die ISR reicht, bzw. gar nicht reichen soll(damit man da später bei der Firmware noch eingreifen kann), dann muß man doch die Speicherrettung/wiederhesrstellung in den Bereich ab 0x08 schreiben. Das aber geht doch gar nicht, wenn man ab 0x18 die LowISR stehen hat. Das paßt doch da nicht hin.
Es ist richtig, ab 0x08 hat man nur 16 Instruktions zur Verfuegung. Daher wird normalerweise von dort auch an eine andere Stelle im Code gesprungen oder eine Funktion aufgerufen.
Zitat
Alternative wäre natürlich, man macht zwei Levels von ISR. Eine auf 0x08, die springt z.B. nach 0x0900, dort werden dann die Register gerettet und dann einen Call nach 0xXXXX, wo man später mit der Firmware noch eingreifen kann.
Zitat
Dann aber hat man doch schon drei Levels von 8(?) im Stack verschenkt.
So wird es auch gemacht, wenn der Bootloader am Anfang des Speichers steht (manche werden ans Ende des Flash geschrieben). Wenn man mit goto springt, dann verbraucht man gar keinen Stack (ausser dem Aufruf selber natuerlich), man handelt sich nur eine zusaetzliche Latenz ein. Wenn man mit einem call arbeitet, verbraucht man 2 Stackadressen, bei goto nur eine. Btw, die PIC18 haben einen 31 Level Stack.
Zitat
Wie macht man denn sowas elegant, so im guten Programmierstil?
Ich gehe davon aus dass du Assembler benutzt, daher hier ein Beispiel fuer den relocatable Mode:
Code:
;------------------------------------------------------------------------------
; RESET VECTOR
;------------------------------------------------------------------------------

RES_VECT  CODE    0x0000            ; processor reset vector
          GOTO    START             ; go to beginning of program

;------------------------------------------------------------------------------
; HIGH PRIORITY INTERRUPT VECTOR
;------------------------------------------------------------------------------

ISRHV     CODE    0x0008

          ; Run the High Priority Interrupt Service Routine
          GOTO    HIGH_ISR            

;------------------------------------------------------------------------------
; LOW PRIORITY INTERRUPT VECTOR
;------------------------------------------------------------------------------

ISRLV     CODE    0x0018
          
          ; Run the High Priority Interrupt Service Routine
          GOTO    LOW_ISR            

;------------------------------------------------------------------------------
; HIGH PRIORITY INTERRUPT SERVICE ROUTINE
;------------------------------------------------------------------------------

ISRH      CODE                        ; let linker place high ISR routine

HIGH_ISR  

          ; Insert High Priority ISR Here

          RETFIE  FAST

;------------------------------------------------------------------------------
; LOW PRIORITY INTERRUPT SERVICE ROUTINE
;------------------------------------------------------------------------------

ISRL      CODE                        ; let linker place low ISR routine

LOW_ISR
          ; Context Saving for Low ISR
          MOVWF   W_TEMP              ; save W register
          MOVFF   STATUS, STATUS_TEMP ; save status register
          MOVFF   BSR, BSR_TEMP       ; save bankselect register

          ; Insert Low Priority ISR Here

          ; Context Saving for Low ISR
          MOVFF   BSR_TEMP, BSR       ; restore bankselect register
          MOVF    W_TEMP, W           ; restore W register
          MOVFF   STATUS_TEMP, STATUS ; restore status register
          RETFIE

Gruesse,
Stefan
Gespeichert

BL1
Jr. Member
**
Offline Offline

Beiträge: 82


Profil anzeigen
« Antworten #2 am: September 22, 2011, 09:30:56 »

Danke für die Antwort, Stefan.

Da lag ich also zumindest nicht um Welten falsch.

Gechrieben ist das Ganze in CC8e, wobei normale GOTO's m.M. da nicht funktionieren, auch nicht, wenn man diese als Assembler-Statements schreibt. Oder ich bin einfach nur zu blöd, das hinzukriegen.

Aber generell ist hab ich das Problem noch zu lösen, wie der eigentliche Bootlader mit dem Interrupt umgeht. Denn so oder so, irgendwann kommt man via Interrupt in den Bereich, der auch mal gelöscht, bzw. neu geschrieben werden kann.

Ein andere Geschichte, die ich mir nicht erklären kann, ist folgende: Ich habe den Bootlader eigentlich fertig. Der spielt die Daten 16Byte-weise rüber, antwortet in einer Art Handshake, gibt pro 64er Block eine CRC-Summe zurück, die nach dem Schreibvorgang aus dem Flash den Block wieder ausgelesen wurde, alles prima. Er programmiert sogar und dann auch an die richtigen Stellen. Jedenfalls habe ich noch keinen Fehler entdeckt, wobei das 31kB sind und da kann ich nur stichprobenartig gucken. Aber wo ich auch hingucke, steht jedes Bit dort, wo es hinsoll.
Alles Klasse - und trotzdem schmiert das Sch..ding ab. Meistens jedenfalls, einmal lief es sogar.

Wenn jedes Bit da steht, wo es hinsoll, kann ich mir das Abschmieren nicht mehr erklären. Spiele ich die hex-Datei über den ICD3 rein, geht's. Über den Bootlader via I2C (meistens)nicht.




BL
Gespeichert
Edson
Globaler Moderator
Sr. Member
*****
Offline Offline

Beiträge: 373



Profil anzeigen
« Antworten #3 am: September 22, 2011, 10:54:29 »

Hallo BL,

Dann aber hat man doch schon drei Levels von 8(?) im Stack verschenkt.

die PIC18 haben einen Hardware-Stack mit 32 Ebenen, nicht nur 8.

Zitat
Spiele ich die hex-Datei über den ICD3 rein, geht's. Über den Bootlader via I2C (meistens)nicht.

Kann es sein, dass du die Hex-Datei in Debug-Konfiguration erstellst?

Grüße,
Edson

(Edit: Tippfehler korrigiert, es sind 32 Ebenen)
Gespeichert
BL1
Jr. Member
**
Offline Offline

Beiträge: 82


Profil anzeigen
« Antworten #4 am: September 23, 2011, 09:45:57 »

Im Debug läuft das Ding nicht, das kann es also nicht sein.

Aber folgendes: Der Bootlader beinhaltet auch das Ansprechen des Displays. Dadurch habe ich da Konstantentabellen drin, die den Text beinhalten. Der Compiler schreibt konstant boshaft alle solche Tabellen ans Ende des beschriebenen Sperichers. Compiliere ich also nur den Bootlader, bleibt alles schon unter der 0x1000. Compiliert man aber die Firmware mit, stehen die Text-Tabellen auch des Bootladers ab 0x77irgendwas. Das wird später beim <<<updaten natürlich gelöscht und anders wieder reingeschrieben, so dass der Bootlader dann seine Sprungadressen nicht mehr findet.

Jetzt gibt es im CC8e ein pragma insertConst, mit dem man das verhindern und in Zusammenhang mit pragma origin ADR die Konstanten an eine bestimmte Stelle setzen kann. Das Problem dabei: Dann stehen alle Konstanten ab diese Stelle, also auch die der Firmware. Nun habe ich mir gedenkt, ich setze die Konstanten.-Start-Adresse auf 0x93C, damit die tabelle des Bootladetextes genau auf 0x1000 endet. Dann können die Konstanten der Firmware ja schadensfrei ab 0x1002 stehen.
Schön, nur dass alles was in der/den Quelldatei/en nach den Konstantenvereinbarungen steht im Speicher dann auch dahinter landet. Die Header-Datei des Bootladetextes muß ich vor der diesen Text aufrufenden Funktion einbinden, sonst findet der Compiler das nicht. Dann aber steht eben diese aufrufende Funktion nach der 0x1000.

Der Compiler-Hersteller empfiehlt nun, den Boootlader und Firmware getrennt zu compilieren. Nur nutzen die beiden teilweise gleiche Funktionen. Wenn ich nun die Frimware compilieren will und die Funktionen des Bootladers sind nicht da, dann geht's doch nicht, oder?




BL
Gespeichert
Stampede
Globaler Moderator
Hero Member
*****
Offline Offline

Beiträge: 969



Profil anzeigen WWW
« Antworten #5 am: September 26, 2011, 01:15:33 »

Hi,

Zitat
Nun habe ich mir gedenkt
Grinsend
Zitat
Der Compiler-Hersteller empfiehlt nun, den Boootlader und Firmware getrennt zu compilieren.
Microchip auch.
Zitat
Nur nutzen die beiden teilweise gleiche Funktionen. Wenn ich nun die Frimware compilieren will und die Funktionen des Bootladers sind nicht da, dann geht's doch nicht, oder?
Es spricht wirklich nichts dagegen, einfach die Funktionen in beide Projekte einzufuegen und dann getrennt zu kompilieren, mache ich bei meinem USB Bootloader auch so. Man verbraucht zwar mehr Speicher, macht das Ganze aber deutlich einfacher und du kannst dir all den Aerger den du jetzt hast sparen.
Ist denn der Programmspeicher so knapp dass du diesen Weg gehen musst?

Gruss
Stefan
Gespeichert

BL1
Jr. Member
**
Offline Offline

Beiträge: 82


Profil anzeigen
« Antworten #6 am: September 28, 2011, 13:50:45 »

Microchip auch. ... einfach die Funktionen in beide Projekte einzufuegen und dann getrennt zu kompilieren...

Speicher wäre nicht das Problem. Ich habe 32KWord und bis jetzt 17 verbraucht.
Ich denke nur, das ist unsäglich umständlich, oder ich kapier's nicht wie das geht.

Wenn ich den Bootlader kompiliere, habe ich auf 0x0000 meinen GOTO main(), also z.B. auf 0x00A0 oder so. Kompiliere ich die Application, springt der ich den GOTO main() auf z.B. 0x1000. Das soll das Ding ja aber nicht machen. Er soll ja immer erst in den Bootlader gehen und gucken, ob was "bootzuladen" ist. Springt er gleich auf 0x1000, tut er mir diesen Gefallen nicht.
Da fiele mir ja nur ein, die Application als Hex-File ab 0x1000 immer nur via Bootlader rein zu spielen.
Um Himmels willen, wenn ich das beim normalen Programmieren und Debuggen mache(bei 17K und wachsend kommt das erdenklich oft vor), wird Griechenland schuldenfrei, bis ich fertig werde.

Oder gibt's da 'n Trick?



BL
Gespeichert
Stampede
Globaler Moderator
Hero Member
*****
Offline Offline

Beiträge: 969



Profil anzeigen WWW
« Antworten #7 am: September 29, 2011, 01:48:21 »

Hi,

Zitat
Wenn ich den Bootlader kompiliere, habe ich auf 0x0000 meinen GOTO main(),
Nein.
Zitat
Er soll ja immer erst in den Bootlader gehen und gucken, ob was "bootzuladen" ist. Springt er gleich auf 0x1000, tut er mir diesen Gefallen nicht.
Eben.

Den Bootloader bekommt ein eigenes Projekt mit allem, was er zum Laufen braucht. Darin werden dann auch die Reset- und Interruptvektoren umgebogen (Beispiele fuer den C18, den CC8e kenne ich nicht):

Code:
/** V E C T O R  R E M A P P I N G *******************************************/

#pragma code _HIGH_INTERRUPT_VECTOR = 0x000008
void _high_ISR (void)
{
    _asm goto RM_HIGH_INTERRUPT_VECTOR _endasm
}

#pragma code _LOW_INTERRUPT_VECTOR = 0x000018
void _low_ISR (void)
{
    _asm goto RM_LOW_INTERRUPT_VECTOR _endasm
}

#pragma code

In der main() des Bootloader musst du nun abfragen, wann der Loader und wann die eigentliche Applikation gestartet werden soll:
Code:
/******************************************************************************
 * Function:        void main(void)
 *
 * PreCondition:    None
 *
 * Input:           None
 *
 * Output:          None
 *
 * Side Effects:    None
 *
 * Overview:        Main program entry point.
 *
 * Note:            None
 *****************************************************************************/
void main(void)
{
    Init();
    //Check Bootload Mode Entry Condition
    if(PORTAbits.RA1 == 1)      // If not pressed, value set by setup or
    {
        ADCON1 = temp;          // Restore reset value
        _asm goto RM_RESET_VECTOR _endasm
    }//end if

    //Bootload Mode
    mInitAllLEDs();
mLED_1_Off();
mLED_2_On();
    led_count = 0; //Initialize blink rate counter
    mInitializeUSBDriver();     // See usbdrv.h
    USBCheckBusStatus();        // Modified to always enable USB module
    while(1)
    {
        USBDriverService();     // See usbdrv.c
        BootService();          // See boot.c
    }//end while
}//end main

#pragma code user = RM_RESET_VECTOR
Wie du siehst ist das in meinem Fall RA4, der bestimmt, was gestartet wird. Das kannst du auf beliebige Sachen (EEPROM, etc.) erweitern.

Deine eigentliche Anwendung erstellst du ganz normal in einem eigenen Projekt, nur dass du sie halt an die Adresse 0x1000 (oder was auch immer) packst. Logischerweise ist diese (also die Anwendung) dann ohne den Loader nicht lauffaehig, da an der Stelle 0x0000 nur Kaese steht und der Controller die Abarbeitung verweigert.
Daher solltest du zum Debuggen ganz auf den Loader verzichten und das Programm normal entwickeln. Ist es fertig, auf die gewuenschte Adresse verschieben und Kompilieren. Das Hex dann entweder mit dem Loader einspielen oder du erstellest ein eiziges Hex aus Loader und Programm (z.B mit Hexmate).

Gruss
Stefan

* hexmate.exe (59 KB - runtergeladen 19 Mal.)
« Letzte Änderung: September 29, 2011, 01:53:03 von Stampede » Gespeichert

BL1
Jr. Member
**
Offline Offline

Beiträge: 82


Profil anzeigen
« Antworten #8 am: September 29, 2011, 11:08:46 »

Danke!

Klappt. Nur mit dem Hexmate, da muß ich mich erst mal reinfummeln. Irgendwann.



VL
Gespeichert
Seiten: [1] Nach oben Drucken 
« vorheriges nächstes »
Gehe zu:  

Powered by MySQL Powered by PHP Made for Mozilla (Firefox) Made for Internet Explorer
Seite erstellt in 0.038 Sekunden mit 17 Zugriffen.
 
Top! Top!