mikrocontroller.net

Forum: Mikrocontroller und Digitale Elektronik Interrupt PWM beim Sam7s256


Autor: mikronoob (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Hallo,

will bei meiner PWM jedes mal am ende einer Periode den duty Cycle 
ändern. Dises soll in der ISR geschehen. Habe nur das Problem das die 
ISR nur einmal, nach der essten Periode, aufgerufen wird und dann nie 
wieder. Programm spring auf jeden Fall zurück in die Endlosschleife. 
Muss ich in der ISR noch irgenwas am ende machen oder so? Meine ISR:

static void ISR_Pwmc(void)
{
      static int i = MIN_DUTY_CYCLE;
      volatile unsigned int dummy;

      if((AT91C_BASE_PWMC->PWMC_ISR & AT91C_PWMC_CHID0) == 
AT91C_PWMC_CHID0);
      i=i+1;
      if ( i>= MAX_DUTY_CYCLE){
      i = MIN_DUTY_CYCLE;
      }
        PWMC_SetDutyCycle(CHANNEL_PWM_LED0, i);

  AT91C_BASE_AIC->AIC_EOICR=0x01;   //end of Interrupt


}

Schon mal vielen Danke für euer Hilfe und Tipps

Autor: mikronoob (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Keiner eine Idee oder Tip?

Autor: Stephan W. (sir_wedeck)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Hi,
kennst du die Beispiele von Atmel zum Thema PWM???

Wie sieht den dein ASM-INT Handler aus das du den EOI in der C-Funktion 
machen mußt?

Kannst du ein minimal Projekt erstellen und hier mal anhängen?

Stephan

Autor: mikronoob (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Hey, ja kenne die Beispiele! Mache eigentlich nicht viel anderes als wie 
in dem PWM Beispiel von Atmel. Großer Unterschied ist, dass ich einen 
anderen Start up code nutze. Den von uvision vorgegeben code sam7.s für 
mein Board Sam7s256-ek.

Habe mir das ganze noch mal langsam im Debugger angeschaut und er 
scheint nach der ISR Routine gar nich mehr zurück zu springen. Als wenn 
er immer in der ISR bleibt.

Ansonsten initialisiere ich meinen interrupt genau wie im Atmel 
Beispiel:

AIC_ConfigureIT(AT91C_ID_PWMC, 0, ISR_Pwmc);
AIC_EnableIT(AT91C_ID_PWMC);
PWMC_EnableChannelIt(CHANNEL_PWM_LED0);

Autor: Andreas B. (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
mikronoob schrieb:
> AT91C_BASE_AIC->AIC_EOICR=0x01;   //end of Interrupt

Also das ist sehr daneben. Wenn man den Interrupt Dispatcher aus dem 
Atmel Code verwendet (und wenn nicht, würde man den selbst meist genau 
so implementieren), darf man nicht in EOICR schreiben. Erstens nicht, 
weil das der Dispatcher an der richtigen Stelle macht und zweitens 
nicht, solange Interrupts aktiviert sind (was der Dispatcher vor dem 
Aufruf der ISR macht).

Wegen letzterem kann dann ein anderer Interrupt auch derselben oder 
niedriger Priorität reinplatzen und dann gibts Register- und Stacksalat. 
Beim Single Step im Debugger wird wohl das passiert sein, die PWM ISR 
wird nochmal angesprungen während sie läuft. Schließlich läuft die PWM 
und erzeugt Interrupts auch wenn die CPU durch den Debugger angehalten 
ist.

Autor: mikronoob (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Ok, habe es entfernt. Bleibt aber trotzdem noch das selbe Problem das 
die ISR nur einmal aufgerufen wird. Sieht so aus als wenn die ISR doch 
wohl verlassen wird, aber dann halt kein weiteres mal aufgerufen wird!

Autor: Stephan W. (sir_wedeck)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Hi

kannst du die Beispiele von Atmel für den Keil-Compieler bei dir laufen 
lassen? Wenn ja, dann stimmt was nicht in deinem Programm.

Hab mal etwas gegoogelt und verschiedene Versionen von der sam7.s 
gefunden.
Einige von denen hatte kein ASM INT-handler eingebaut. Bei den 
Beispielen ist einer drin.
;------------------------------------------------------------------------------
; Handles incoming interrupt requests by branching to the corresponding
; handler, as defined in the AIC. Supports interrupt nesting.
;------------------------------------------------------------------------------
irqHandler
        ;  Save interrupt context on the stack to allow nesting */
        SUB     lr, lr, #4
        STMFD   sp!, {lr}
        MRS     lr, SPSR
        STMFD   sp!, {r0,r1,lr}

        ; Write in the IVR to support Protect Mode */
        LDR     lr, =AT91C_BASE_AIC
        LDR     r0, [r14, #AIC_IVR]
        STR     lr, [r14, #AIC_IVR]

        ; Branch to interrupt handler in Supervisor mode */
        MSR     CPSR_c, #ARM_MODE_SVC
        STMFD   sp!, {r1-r4, r12, lr}
        MOV     lr, pc
        BX      r0
        LDMIA   sp!, {r1-r4, r12, lr}
        MSR     CPSR_c, #ARM_MODE_IRQ | I_BIT

        ; Acknowledge interrupt */
        LDR     lr, =AT91C_BASE_AIC
        STR     lr, [r14, #AIC_EOICR]

        ; Restore interrupt context and branch back to calling code
        LDMIA   sp!, {r0,r1,lr}
        MSR     SPSR_cxsf, lr
        LDMIA   sp!, {pc}^

und hier wird auch AIC_EOICR beeinflusst, also raus aus dem C-Code 
damit!!!!

Test doch noch mal die Beispiele von Atmel und dann Vergleich die 
Software.

Stephan

Autor: Andreas B. (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
mikronoob schrieb:
> if((AT91C_BASE_PWMC->PWMC_ISR & AT91C_PWMC_CHID0) ==
> AT91C_PWMC_CHID0);

Ist mir gerade noch aufgefallen, das if macht überhaupt nichts. Es 
sollte wohl der Rest der Funktion in dem if-Block eingeschlossen werden.

Wegoptimiert wird das wohl dennoch nicht, wegen dem volatile in der 
Definition von AT91C_BASE_PWMC. Ansonsten würde das PWM_ISR Register 
nicht gelesen werden und solange würde auch kein neuer Interrupt 
ausgelöst werden.


Wir brauchen hier noch mehr tatsächlichen Quelltext, sonst wird das ein 
Rätselraten. Sicher, dass die PWM überhaupt läuft?

Autor: mikronoob (Gast)
Datum:
Angehängte Dateien:

Bewertung
0 lesenswert
nicht lesenswert
@Stephan
Wo hast du denn die des Sam7.s gefunden. Denn meine hat auf jeden fall 
nicht einen solchen irqHandler

Andreas B.
> Ist mir gerade noch aufgefallen, das if macht überhaupt nichts. Es
> sollte wohl der Rest der Funktion in dem if-Block eingeschlossen werden.

Oh ja, da hast du recht, danke.

Anonsten funktioniert die PWM einwandfrei. Habe mal das Bild von dem 
Logic Analyzer aus der Simulation aus uvision angehängt. Dort kann man 
auch sehen das beim ersten mal am ende der Periode der Interrupt 
ausgelößt wird. Es wird dann auch in der ISR der duty Cycle verändert. 
Beim zweiten mal wird zwar auch das Bit gesetzt, aber die ISR wird nicht 
aufgerufen.

Autor: mikronoob (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Welechen Start up code verwendet ihr denn so?

Autor: Stephan W. (sir_wedeck)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Hi,

>Wo hast du denn die des Sam7.s gefunden. Denn meine hat auf jeden fall
>nicht einen solchen irqHandler
also irgend was muss ja bei dir drin sein, sonst würde kein INT 
ausgeführt werden!!!!

Was ist nun mit den Beispielen von Atmel für den Keil-Compiler?
Laufen die oder nicht?

Kannst du uns dein Programm, bzw ein minimal Beispiel-Programm zeigen?
(Komplettes Projekt -> mit allen Dateien!)

Stephan

Autor: mikronoob (Gast)
Datum:
Angehängte Dateien:
  • pwm.7z (4,59 KB, 70 Downloads)

Bewertung
0 lesenswert
nicht lesenswert
Also die Beispiele von Atmel laufen bei mir nich, da ich kein Jtag habe. 
Habe beim kompelieren eine Menge Fehlermeldungen. Programmiere über 
Samba und Usb.
Also meine PWM läuft ja soweit, auch auf dem Board. Habe das ganze auch 
schon mal mir einem Oszi nachgemessen. Auch wenn ich die PWM auf 1 Hz 
einstelle blinken die LED mit der Frequenz. Also meine PWM läuft auch 
auf dem Board.

Will jetzt halt eine Sinusbewerte PWM machen und den Duty Cycle nach 
jeder Periode ändern. Habe jetzt mal meine main und den sam7.s start up 
code hoch geladen den ich nutze. Der rest der Funktionen stammt aus der 
lib von atmel. Vielleicht findest du ja einen Fehler, warum meine ISR 
immer nur einmal aufgerufen wird.

Danke schonmal!

Autor: Stephan W. (sir_wedeck)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Hi,

ich lehne mich mal weit aus dem Fenster und versuche es zu schildern
was ich meine. Kann auch falsch sein!

Also...
Dein Startup-Code benutzt Auto-Vectoring.
;               LDR     PC,IRQ_Addr
                LDR     PC,[PC,#-0xF20]        ; Vector From AIC_IVR
Die erste Zeile ist auskommentiert und würde auch nur in einen Endless 
Loop enden. siehe weiter unten im Code.

Für diese Art wird von Atmel die Note gereicht:
http://www.atmel.com/dyn/resources/prod_documents/...

Schau dir dies mal an, dort wird gezeigt das du einmal beim Beginn eines 
INTs und beim Exit eines INTs, 2 Makros benötigst.

Ich glaube aber nicht das Keil so was nicht schon vorbereitet hat, schau 
mal ins "Handbuch" deines Compilers, bitte nach.
Beim den Beispielen von Atmel, GCC und CrossWorks wird das anders 
gemacht, wie oben beschrieben, haben diese einen Handler der die 
Register Sicherungen durchführt. siehe oben.

Wenn es nicht stimmt bitte nicht hauen. ;-)

Aber das die Beispiele von Atmel nicht laufen sollen, kann ich fast 
nicht glauben.

Stephan

Autor: Andreas B. (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Also gut, ich nahm an, wenn man die ganzen Funktionen von Atmel 
verwendet, dass man dann auch die Initialisierung und den Interrupt 
Dispatcher so wie sie sind verwendet.
LDR     PC,[PC,#-0xF20]        ; Vector From AIC_IVR

Das Problem hiermit ist, das dann die mit AIC_ConfigureIT() 
konfigurierte C-Funktion direkt angesprungen wird. Anders als ich oben 
geschrieben habe, wäre dann das Schreiben in EOICR tatsächlich nötig, 
aber nicht genug. Und einen Interrupt kann man in C nicht korrekt 
beenden, außer der Compiler bietet spezielle Erweiterungen dafür.

Der Interrupt Dispatcher, wie ich ihn meine, sieht so aus:
irq_dispatch:
        push    {lr}
        mrs     lr, SPSR
        push    {r0, lr}

        ldr     lr, =AIC_BASE
        ldr     r0, [lr, #0x100]        @ load AIC_IVR
        msr     CPSR_c, #MODE_SVC
        
        push    {r1-r3, r12, lr}
        mov     lr, pc
        bx      r0
        pop     {r1-r3, r12, lr}

        msr     CPSR_c, #MODE_IRQ|I_BIT
        str     r0, [lr, #0x130]        @ write anything to AIC_EOICR

        pop     {r0, lr}
        msr     SPSR, lr
        pop     {lr}
        subs    pc, lr, #4

Das steht auch in einer App Note, welche weiß ich grad nicht. In der 
Vektortabelle für IRQ macht man dann einfach das:
        b       irq_dispatch

Damit kann man dann Adressen von reinen C-Funktionen in die 
AIC-Vektorregister schreiben. Außerdem reaktiviert es die Interrupts, so 
dass ein Interrupt mit höherer Priorität denn aktuell laufenden 
unterbrechen kann.

Das wären noch die Definitionen für die Statusbits, falls nötig:
        .equ    MODE_IRQ, 0x12
        .equ    MODE_SVC, 0x13
        .equ    I_BIT, 0x80

Autor: Andreas B. (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Nachtrag: Man kann durchaus auch das mit dem direkt AIC_IVR laden 
machen, nur muss dann die ISR selber die korrekten Operation machen um 
alle nötigen Register zu speichern und wiederherzustellen sowie den 
Interrupt korrekt zu beenden. Das benötigt aber die Hilfe des Compilers, 
da das im C-Standard nicht vorgesehen ist.

Bei gcc mit ARM als Ziel funktioniert das so:
static void ISR_Pwmc(void) __attribute__((interrupt("IRQ")));

static void ISR_Pwmc(void)
{
...

In diesem Fall muss dann am Ende auch AIC_EOICR geschrieben werden. 
Aber dann werden die Interrupts nicht wieder aktiviert und die ISR kann 
nicht zugunsten eines Interrupts mit höherer Priorität unterbrochen 
werden.

Auch das könnte man einrichten, aber nur mit Inline Assembler. Halt die 
beiden "msr CPSR_c" aus dem Dispatcher am Anfang und Ende (vor dem 
Schreiben in EOICR) einfügen. Dann muss man allerdings auch aufpassen, 
dass nirgends ein return steht und so die Wiederherstellung des MODE_IRQ 
umgangen wird.

Zumindest müsste es so funktionieren, ich habe es nicht probiert bisher.

Autor: Andreas B. (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Andreas B. schrieb:
> Auch das könnte man einrichten, aber nur mit Inline Assembler. Halt die
> beiden "msr CPSR_c" aus dem Dispatcher am Anfang und Ende (vor dem
> Schreiben in EOICR) einfügen.

Nachtrag zum Nachtrag: Man darf aber nur Interrupts aktivieren, nicht in 
den Supervisor-Modus wechseln. Denn das wechselt ja das aktive 
Stackpointer-Register und davon weiß der Compiler nichts. Bleibt man im 
IRQ-Modus, muss dessen Stack halt auch alles fassen können.

Wenn man mit dem Dispatcher aber in den Supervisor wechselt (und der 
Rest auch im Supervisor läuft) braucht man nur einen großen Stack und im 
IRQ-Stack nur 12 Bytes für jeden möglicherweise gleichzeitig 
auftretenden Interrupt (= 96 Bytes bei Nutzung aller 8 Prioritäten).

Antwort schreiben

Die Angabe einer E-Mail-Adresse ist freiwillig. Wenn Sie automatisch per E-Mail über Antworten auf Ihren Beitrag informiert werden möchten, melden Sie sich bitte an.

Wichtige Regeln - erst lesen, dann posten!

  • Groß- und Kleinschreibung verwenden
  • Längeren Sourcecode nicht im Text einfügen, sondern als Dateianhang

Formatierung (mehr Informationen...)

  • [c]C-Code[/c]
  • [avrasm]AVR-Assembler-Code[/avrasm]
  • [code]Code in anderen Sprachen, ASCII-Zeichnungen[/code]
  • [math]Formel in LaTeX-Syntax[/math]
  • [[Titel]] - Link zu Artikel
  • Verweis auf anderen Beitrag einfügen: Rechtsklick auf Beitragstitel,
    "Adresse kopieren", und in den Text einfügen




Bild automatisch verkleinern, falls nötig
Bitte das JPG-Format nur für Fotos und Scans verwenden!
Zeichnungen und Screenshots im PNG- oder
GIF-Format hochladen. Siehe Bildformate.
Hinweis: der ursprüngliche Beitrag ist mehr als 6 Monate alt.
Bitte hier nur auf die ursprüngliche Frage antworten,
für neue Fragen einen neuen Beitrag erstellen.

Mit dem Abschicken bestätigst du, die Nutzungsbedingungen anzuerkennen.