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
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
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);
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.
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!
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.
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
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?
@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.
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
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!
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.
1
;LDRPC,IRQ_Addr
2
LDRPC,[PC,#-0xF20];VectorFromAIC_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/doc1168.pdf
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
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.
1
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:
1
irq_dispatch:
2
push {lr}
3
mrs lr, SPSR
4
push {r0, lr}
5
6
ldr lr, =AIC_BASE
7
ldr r0, [lr, #0x100] @ load AIC_IVR
8
msr CPSR_c, #MODE_SVC
9
10
push {r1-r3, r12, lr}
11
mov lr, pc
12
bx r0
13
pop {r1-r3, r12, lr}
14
15
msr CPSR_c, #MODE_IRQ|I_BIT
16
str r0, [lr, #0x130] @ write anything to AIC_EOICR
17
18
pop {r0, lr}
19
msr SPSR, lr
20
pop {lr}
21
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:
1
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:
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:
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.
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).