- LiFePO4 Speicher Test         
Ergebnis 1 bis 10 von 16

Thema: SPI Interrupt wird ZU SPÄT ausgelöst nach Sendevorgang

Hybrid-Darstellung

Vorheriger Beitrag Vorheriger Beitrag   Nächster Beitrag Nächster Beitrag
  1. #1
    Erfahrener Benutzer Begeisterter Techniker
    Registriert seit
    14.04.2005
    Ort
    Freiberg
    Alter
    41
    Beiträge
    311

    SPI Interrupt wird ZU SPÄT ausgelöst nach Sendevorgang

    Hallo zusammen,

    wie am Titel zu erkennen, habe auch ich gerade Probleme mit SPI. Es handelt sich bei mir um einen PIC18, der als SPI-Slave mit SS betrieben wird, interner Clock (16MHz).

    Problem: Der interrupt wird viel zu spät ausgelöst: es vergehen 11,5µs zwischen <jetzt müsste er auslösen> und <der Ausgang RD5 wurde umgeschalten>. RD5=0 bleibt für 10µs erhalten.

    Umgehe ich die interrupt-Routine und baue in der Main-Routine eine Abfrage des PIR1bits.SSPIF == 1 und schalte dann den Ausgang RD5=0, geschieht das mit einem Verzug von etwa 1,1-2,4µs (Jitter natürlich deutlich stärker). RD5 bleibt ca. 1,5µs auf low.

    Code:
    void spi_init(void) {
    
        INTCON = 0;         // Disable all interrupts
        PIE1   = 0;
        PIR1   = 0;
        // ----------------------------
        SSPCON1bits.SSPEN = 0;          // SSPCON1[5]; Allow Programming of serial port
        SSPCON1bits.CKP = 1;            // SSPCON1[4]; Clock Polarity Select bit; Idle state for clock is a HIGH level
        SSPCON1bits.SSPM3 = 0;          // SSPCON1[3]; Synchronous Serial Port Mode Select bits
        SSPCON1bits.SSPM2 = 1;          // SSPCON1[2]; [3-0] 0100 = SPI Slave mode, clock = SCK pin, SS pin control enabled
        SSPCON1bits.SSPM1 = 0;          // SSPCON1[1]; 
        SSPCON1bits.SSPM0 = 0;          // SSPCON1[0]; 
        // ----------------------------
        SSPSTATbits.SMP = 0;            // SSPSTAT[7]; Input data sampled at middle data output time
        SSPSTATbits.CKE = 0;            // SSPSTAT[6]; Transmit occurs on active to idle clock state
        // ----------------------------
        PIE1bits.SSPIE = 1;
        PIR1bits.SSPIF = 0;             // Master Synchronous Serial Port Interrupt Flag bit (must be cleared by Software)
                                        // 0 = Waiting to transmit/receive
        INTCONbits.PEIE   = 1;          // Enable Peripheral interupts
        INTCONbits.GIE    = 1;          // Global Interupt enable
        SSPCON1bits.SSPEN = 1;          // End programming and Start serial port
    }
    ISR:
    Code:
    void interrupt isr(void)
    {
        if (PIR1bits.SSPIF == 1)
        {
            spi_reg_addr=SSPBUF;
            SSPBUF=0x00;
            RD5=0;
            PIR1bits.SSPIF = 0;
        }
    }
    Main:
    Code:
    void main(){
        pic_init();
        spi_init();
        while(1)
        {
            PORTDbits.RD5=1;
        }
    }
    Woran kann es liegen, dass die Interrupt-Routine so spät auslöst bzw. wie kann man es beschleunigen?

    Grüß,
    NRicola
    Gurken schmecken mir nicht, wenn sie Pelz haben!

  2. #2
    Erfahrener Benutzer Roboter Genie
    Registriert seit
    05.11.2007
    Beiträge
    1.076
    Hallo NRicola,
    Du hast uns nicht verraten welchen PIC der 18er Serie Du benutzt.
    Es gibt beim 18F252 z.B. ein Problem im SPI Interface im SlaveMode
    Für deinen PIC gibt es ganz sicher auch einen "ERRATA" Sheet, weil es gibt keine funktionierenden PICs...
    Spass muss sein, ich liebe die Dinger trotzdem.
    Schau mal dort rein, eventuell hilft es dann weiter.

    hier mal der Hinweis vom PIC18F252:
    Module: MSSP (SPI, Slave Mode)
    In its current implementation, the SS (Slave
    Select) control signal, generated by an external
    master processor, may not be successfully recognized
    by the PIC® microcontroller operating in
    Slave Select mode (SSPM3:SSPM0 = 0100). In
    particular, it has been observed that faster
    transitions (those with shorter fall times) are more
    likely to be missed than than slower transitions.
    Work around
    Insert a series resistor between the source of the
    SS signal and the corresponding SS input line of
    the microcontroller. The value of the resistor is
    dependent on both the application system’s
    characteristics and process variations between
    microcontrollers. Experimentation and thorough
    testing is encouraged.
    This is a recommended solution. Others may exist.

    zudem würde ich mir mal den Assemblercode von der Interruptfunktion ansehen.
    Ich weis nicht was der Compiler dort für einen "overhead" für Register sichern usw. reinbaut.
    Bei einer Free Version von Microchip hab ich hier schon haarsträubenden code erlebt....
    Wenn er erstmal 50 Zeilen Code abarbeiten muss bis er deinen Zeile zum Bitsetzen erreicht
    kommt da natürlich einiges an Zeit zusammen.
    Geändert von Siro (03.06.2018 um 21:37 Uhr)

  3. #3
    Erfahrener Benutzer Begeisterter Techniker
    Registriert seit
    14.04.2005
    Ort
    Freiberg
    Alter
    41
    Beiträge
    311
    Hallo Siro,

    danke für den Hinweis mit den Errata-Sheets. Das hat mich wachgerüttelt. Ich hatte einen Mikrocontroller als perfektes Gerät angesehen (nennen wir es Gottesteilchen). Ist ja doch erstaunlich, was dann später noch gefummelt werden muss...

    Dennoch habe ich für meinen PIC18f45k20 nicht wirklich den passenden aller Einträge gefunden.
    Den ASM-Code anzuschauen wäre ein Ansatz. Aber zum einen ist mir nicht klar, wie ich da ran komme, wenn ich in C programmiere und mit MPLAB/XC8 arbeite. Zum anderen habe ich aber auch mal den ganz popligen Test gemacht, INT0 auszulösen. Also ein Rechtecksignal an PB0 und dann Ausgang RD5 umschalten lassen.
    Code:
        TRISB  = 1;
        ANSELH = 0x00;
        INTCON = 0;
        INTCON2 = 0;
        INTCON2bits.INTEDG0 = 0; // Flanke ist bei mir negativ
        INTCONbits.INT0IE = 1;
        INTCONbits.INT0IF = 0;
        RCONbits.IPEN     = 0;
        INTCONbits.GIE    = 1;
    Auch hier (mit den internen 16MHz Clock) habe ich einen Verzug zwischen den Flanken von 11µs (=176 Clock cycles). So etwas fundamentales kann ja eigentlich nicht mehr falsch zwischen C und ASM implementiert sein. Oder etwa doch??
    Daher wäre meine Vermutung, dass es eher noch etwas systematisches ist. Was könnte ich vergessen haben oder welches Register könnte mich noch ausbremsen? Ist dem interrupt-Prozess noch irgendein Timer oder anderer Trigger hinterlegt? Nach dem Schaubild "9-1 Intterrupt Logic" im Datenblatt eigentlich nicht.
    Ich habe bis jetzt vorwiegend mit den high/low Prioritäten des Interrupts herumgespielt. Das lief alles auf's gleiche hinaus.

    Grüß
    NRicola
    Gurken schmecken mir nicht, wenn sie Pelz haben!

  4. #4
    Erfahrener Benutzer Robotik Einstein
    Registriert seit
    07.03.2011
    Beiträge
    1.899
    Zitat Zitat von NRicola Beitrag anzeigen
    Auch hier (mit den internen 16MHz Clock) habe ich einen Verzug zwischen den Flanken von 11µs (=176 Clock cycles). So etwas fundamentales kann ja eigentlich nicht mehr falsch zwischen C und ASM implementiert sein. Oder etwa doch??
    Daher wäre meine Vermutung, dass es eher noch etwas systematisches ist. Was könnte ich vergessen haben oder welches Register könnte mich noch ausbremsen? Ist dem interrupt-Prozess noch irgendein Timer oder anderer Trigger hinterlegt? Nach dem Schaubild "9-1 Intterrupt Logic" im Datenblatt eigentlich nicht.
    Ich habe bis jetzt vorwiegend mit den high/low Prioritäten des Interrupts herumgespielt. Das lief alles auf's gleiche hinaus.
    Zwei Vorbemerkungen: Die Interruptpriorität spielt nur eine Rolle, wenn zwei Interrupte gleichzeitig eintreffen. Das ist hier aber nicht der Fall. Und der CPU-Takt ist ein Viertel des Systemtaktes. die 176 Clock cycles sind also 44 Befehlstakte.

    Aber mal ganz allgemein zu Interrupten und Interrupthandlern. Ein Interrupt kann jederzeit eintreten, daher muß der Prozessor nach abbarbeiten des Handlers 100% im gleichen Zustand sein, wie davor. Ansonsten würde das Haupprogramm nicht mehr richtig funktionieren. Ein Teil wird automatisch passieren, kost aber auch seine Zeit. So muß der Programmcounter auf dem Stack gesichert werden und die Anfangsadresse des Handlers muß geladen werden. Jeder Speicherzugriff dauert mindestens einen Prozessortakt, wieviel das in Summe bei einem PIC18 sind, weiß icht jetzt nicht. Jetzt ist der Prozessor also im Interrupthandler und muß als erstes alle Prozessorflags sichern, ohne sie dabei zu verändern. Da ein Interrupthandler eine normale C-Funktion ist, müssen dann alle Register gerettet werden, die der Compiler benutzt. Dazu gehören auch die, die in irgendwelchen Systemfunktionen für die Behandlung von longs oder für Multiplikation und Division benutzt werden, der Interrupt könnte ja gerade in der Bearbeitung einer solchen Funktion aufgetreten sein.

    Auch kann der Compiler nicht vorhersagen, welche Register eine Libraryfunktion benutzt, die später nur als Objectcode dazugelinkt wird. Ich halte also die 44 Prozessorzyklen für die Interruptpräambel und für den Code, der vor deinem Portbitsetzen abläuft für nichts ungewöhnliches. Das Wiederherstellen des Prozessorzustands am Ende des Interrupts wird ähnlich lange dauern.

    In Assembler wird das nur marginal besser. Bei ganz simplen Aufgaben des Interrupts kann man da besser sein, man muß aber bei jedem Befehl, den man schreibt, genau überlegen, was man tut. Und das auch, wenn man (oder auch ein anderer Programmierer) das Programm Monate oder Jahre später debugged. In C ist das kein Problem, ein paar Zeilen mehr im Interrupthandler und das Hauptprogramm läuft immer noch ungestört.

    Zitat Zitat von Siro Beitrag anzeigen
    zudem würde ich mir mal den Assemblercode von der Interruptfunktion ansehen.
    Ich weis nicht was der Compiler dort für einen "overhead" für Register sichern usw. reinbaut.
    Bei einer Free Version von Microchip hab ich hier schon haarsträubenden code erlebt....
    Wenn er erstmal 50 Zeilen Code abarbeiten muss bis er deinen Zeile zum Bitsetzen erreicht
    kommt da natürlich einiges an Zeit zusammen.
    Mit Begriffen wie "haarsträubenden" wäre ich zurückhaltend. Es klingt so, als wären die Compilerprogrammier Vollpfosten und hätten ihr Handwerk nicht gelernt. Ich würd mir das erst erlauben, wenn ich selbst einen Compiler geschrieben hätte, der alle Regressionstest besteht.

    Und die Vorstellung, daß ein Optimizer da viel machen kann, ist irrig. Auch der kann später hinzugelinkten Objectcode nicht wirklich analysieren. Ein 8-Bit Prozessor braucht für die Behandlung der in C gängigen Datentypen wie int oder long nun mal mehrere Register. Bei "großen" Prozessoren wird das mit den Registern besser, dafür ist der Code des Interrupthandlers wahrscheinlich nicht im Cache und muß erst aus dem langsamen Hauptspeicher geladen werden (und verdrängt dabei möglicherweise den Code des Hauptprogramms). Das ist dann zwar anders, aber nicht unbedingt besser.

    MfG Klebwax
    Strom fließt auch durch krumme Drähte !

  5. #5
    Erfahrener Benutzer Roboter Genie
    Registriert seit
    05.11.2007
    Beiträge
    1.076
    Ich will den Compiler nicht schlecht machen Klebwax,
    ich weis aber und das wird sogar angezeigt, dass im "Free" Mode der Code "äußerst" ungünstig wird, so würde ich das mal nennen.

    Dies ist meine Standard Meldung bei allen Projekten:

    You have compiled in FREE mode.
    Using Omniscient Code Generation that is available in PRO mode,
    you could have produced up to 60% smaller and 400% faster code.
    See http://www.microchip.com/MPLABXCcompilers for more information.
    Daher ist es gut möglich, dass hier die "zusätzlichen" Zeiten kommen könnten.
    400 Prozent ist doch erheblich.....

  6. #6
    Erfahrener Benutzer Robotik Einstein
    Registriert seit
    07.03.2011
    Beiträge
    1.899
    Zitat Zitat von Siro Beitrag anzeigen
    Daher ist es gut möglich, dass hier die "zusätzlichen" Zeiten kommen könnten.
    400 Prozent ist doch erheblich.....
    Ich kenne die Meldung. Aber "60% smaler and 400% faster" glaub ich erstmal nicht. Da müsste eher ein "or" stehen. Typisch dealt man zwischen "faster" and "smaler". Loop unroling macht den Code länger, aber schneller. Berechnungen über Tabellen zu machen ebenfalls. Für die Rechnungen mit ints oder longs Inlinecode zu verwenden oder Systemfunktionen zu callen genauso. Und die Spitzenwerte sind, wie bei Benchmarks so üblich, wirklich Spitzenwerte unter idealen Bedingungen. Der Optimizer arbeitet auch sicher im wesentlichen auf C-Ebene, das wird sich auf sowas wie eine Interrupt-Präambel kaum auswirken. Der Optimizer wird nicht plötzlich umschaltbare Registerbänke oder ähnliches entdecken.

    Der XC8 ist für Microchip sicher ein Verlustgeschäft. Ohne einen funktionierenden, fehlerfreien Compiler kann man aber keine CPUs für Milliarden von $ an Profis verkaufe, sie müssen sich das also antun. Ich glaube, diese Meldung soll die professionellen Kunden, die mit den "deep pockets", dazu bringen, wenigstens einen Beitrag zur Entwicklung und Wartung des Compilers beizutragen.

    MfG Klebwax
    Strom fließt auch durch krumme Drähte !

Ähnliche Themen

  1. SPI Interrupt wird nicht ausgelöst nach Sendevorgang
    Von steckplatte im Forum PIC Controller
    Antworten: 4
    Letzter Beitrag: 25.04.2018, 21:08
  2. Interrupt wird nicht ausgelöst.
    Von DarkSoldier im Forum C - Programmierung (GCC u.a.)
    Antworten: 1
    Letzter Beitrag: 28.04.2013, 15:42
  3. Interrupt wird nicht ausgelöst
    Von Michael_am32 im Forum C - Programmierung (GCC u.a.)
    Antworten: 4
    Letzter Beitrag: 02.08.2010, 01:37
  4. Es wird kein Interrupt ausgelöst
    Von MrTaco im Forum C - Programmierung (GCC u.a.)
    Antworten: 9
    Letzter Beitrag: 19.07.2010, 17:48
  5. Wann wird ein Interrupt ausgelöst?
    Von CKroll im Forum PIC Controller
    Antworten: 2
    Letzter Beitrag: 08.09.2004, 09:16

Berechtigungen

  • Neue Themen erstellen: Nein
  • Themen beantworten: Nein
  • Anhänge hochladen: Nein
  • Beiträge bearbeiten: Nein
  •  

12V Akku bauen