uC: PIC24FJ64GA202
Ich bin auf ein seltsames Problem gestoßen: Ich starte eine Berechnung
der Cryptographic Engine und möchte im Idle-Modus warten bis diese
fertig ist. Das scheint mir eine normale Vorgehensweise zu sein,
schließlich gibt es dafür extra einen Interrupt. Dummerweise weckt
dieser den uC nicht auf. Hier ein Stück Code:
1
CRYCONHbits.CTRSIZE=0b1111111;
2
CRYCONHbits.KEYMOD=0b00;
3
CRYCONHbits.KEYSRC=0b0000;
4
CRYCONLbits.CRYON=0b1;
5
CRYCONLbits.CRYSIDL=0b0;
6
CRYCONLbits.ROLLIE=0b0;
7
CRYCONLbits.DONEIE=0b1;// Interrupt einschalten
8
CRYCONLbits.FREEIE=0b0;
9
CRYCONLbits.OPMOD=0b0000;
10
CRYCONLbits.CPHRSEL=0b1;
11
CRYCONLbits.CPHRMOD=0b000;
12
memcpy((void*)&CRYKEY0,(constvoid*)mykey,16);
13
memset((void*)&CRYTXTA,0,16);
14
// Hier ist IFS3bits.CRYDNIF == 0
15
CRYCONLbits.CRYGO=1;// Berechnung starten
16
__builtin_disi(0x3FFF);
17
while(CRYCONLbits.CRYGO)// Warte bis Berechnung fertig
18
Idle();
19
__builtin_disi(0);
20
// Jetzt ist IFS3bits.CRYDNIF == 1 (wenn man bis hier kommt)
Wenn ich das laufen lasse, blockiert es in der Schleife. Ersetze ich die
Schleife durch "while(CRYCONLbits.CRYGO);", also ein Busy-Wait,
funktioniert es. Wenn ich einen anderen Interrupt feuern lasse (z.B. pin
change notification mit einem Taster), dann wacht er ebenfalls auf und
entkommt der Schleife.
Hat jemand eine Idee was das sein könnte?
ohne in das Datenblatt geschaut zu haben:
Nicht jede Aktion kann im IDLE-Modus laufen, wie z.B. eine A/D-Wandlung.
Steht im Datenblatt, dass die Cryptographic Engine auch unabhängig im
IDLE-Modus läuft oder benötigt sie den Systemtakt?
Kastanie schrieb:> Steht im Datenblatt, dass die Cryptographic Engine auch unabhängig im> IDLE-Modus läuft oder benötigt sie den Systemtakt?
Systemtakt (i.S.v. Peripherietakt) braucht sie schon, daher darf man
auch nicht in den Sleep-Mode gehen. Aber sie braucht keinen CPU-Takt.
Sonst wäre das CRYSIDL-Bit sinnlos. Das Datenblatt sagt:
"22.4.2 OPERATION DURING IDLE MODE
When the CRYSIDL bit (CRYCONL<13>) is ‘0’, the engine will continue any
ongoing operations without interruption when the device enters Idle
mode.
When CRYSIDL is ‘1’, the module behaves as in Sleep modes."
PS: Es tut nichts zur Sache und ich kenne mich mit dem ADC auch nicht
besonders gut aus, aber das Datenblatt sagt zum ADC "Operation During
CPU Sleep and Idle modes".
Ok, dann sollte es eigentlich tun.
Das aktuellste Errata hast du sicher schon durchgesehen?
Ansonsten würde ich im Microchip-Forum nachfragen, da gibts auch am WE
support.
Kastanie schrieb:> Das aktuellste Errata hast du sicher schon durchgesehen?
Ja, nichts zu gefunden.
> Ansonsten würde ich im Microchip-Forum nachfragen, da gibts auch am WE> support.
Bin dabei, allerdings scheinen die am WE keine neuen Accounts
freizuschalten.
Ohne mich wirklich auszukennen, nur vom Gefühl her:
Zu warten bis die Berechnung fertig ist und dann erst idle zu gehen
ist eigentlich genau das was Du nicht wolltest.
Meine Vermutung: Es wäre besser ohne Warteschleife sofort idle zu gehen.
Dann würde der Interrupt auch erst im Idle- Zustand kommen
und hätte die Chance den Prozessor aufzuwecken.
Bert 4. schrieb:> Bin dabei, allerdings scheinen die am WE keine neuen Accounts> freizuschalten
Die Administration des MCHP Forums ist leider generell mies :-(
Heinz R. schrieb:> Ohne mich wirklich auszukennen, nur vom Gefühl her:>> Zu warten bis die Berechnung fertig ist und dann erst idle zu gehen> ist eigentlich genau das was Du nicht wolltest.
Nein, aber das mache ich ja auch nicht.
> Meine Vermutung: Es wäre besser ohne Warteschleife sofort idle zu gehen.> Dann würde der Interrupt auch erst im Idle- Zustand kommen> und hätte die Chance den Prozessor aufzuwecken.
Für dieses Minimalbeispiel könnte man das so machen. Der
Crypto-Interrupt ist ein bisschen speziell dahingehend, dass er
garantiert erst eine bestimmte Zeit nach dem Start der Operation
triggert. (Irgendwo in den Datenblättern gibt es eine Tabelle wieviele
Takte jede Crypto-Operation braucht.)
Später in einem richtigen Programm brauche ich schon die Schleife,
schließlich gibt es da auch andere Interrupts. Außerdem muss ich
Interrupts global abschalten, damit die Abfrage und das Idle atomar
passieren. (Beim PIC24 ist es so, dass er aufwacht wenn ein Interrupt
individuell aktiviert ist, auch wenn Interrupts global aus sind.)
Aber wie dem auch sei, ich habe auch "CRYGO = 1; Idle();" getestet und
es hat genau das gleiche Problem.
Volker S. schrieb:> Die Administration des MCHP Forums ist leider generell mies :-(
Das habe ich befürchtet und deshalb hier gepostet.
Reicht der periphere Interrupt alleine aus, um das Idle zu überwinden.
Oder muss dazu wenigstens eine leere ISR gegeben sein? Wo ist dann der
code?
Und nicht dass du die PRIO auf 0 gesetzt hast.
Heinz R. meint, du solltest es so testen:
CRYCONLbits.CRYGO = 1; // Berechnung starten
__builtin_disi(0x3FFF);
Idle();
while(CRYCONLbits.CRYGO) // (macht vor Idle() evtl. den Ärger)
__builtin_disi(0);
neuer PIC Freund schrieb im Beitrag #5413026:
> Reicht der periphere Interrupt alleine aus, um das Idle zu überwinden.> Oder muss dazu wenigstens eine leere ISR gegeben sein? Wo ist dann der> code?
Der ISR existiert natürlich, tut aber nichts:
Ich habe auch testhalber Code eingebaut. Er wird nie ausgeführt.
> Und nicht dass du die PRIO auf 0 gesetzt hast.
Die Priorität habe ich nicht angefasst, per Default ist sie 4 (wie bei
allen Interrupts, insbesondere bei CN, was ja funktioniert).
Kastanie schrieb:> Heinz R. meint, du solltest es so testen:>> CRYCONLbits.CRYGO = 1; // Berechnung starten> __builtin_disi(0x3FFF);> Idle();> while(CRYCONLbits.CRYGO) // (macht vor Idle() evtl. den Ärger)> __builtin_disi(0);
Ändert nichts. Ist meiner Ansicht nach aber auch konzeptionell falsch
(oder zumindest riskant). Wenn ein anderer Interrupt direkt nach CRYGO=1
feuert, würde man danach Idle gehen, selbst wenn die Crypto-Sache in der
Zwischenzeit fertig ist. Du bräuchtest ein if vor dem Idle und dann
kannst du auch gleich die Schleife dort hin setzen. Deine Schleife
ergibt wenig Sinn, wieso sollte man wiederholt Interrupts einschalten?
neuer PIC Freund schrieb im Beitrag #5413105:
> Interrupt-Flag in ISR löschen.
Du hast recht, das sollte ich natürlich machen. Problem ist nur der ISR
wird gar nicht aufgerufen.
Edit: Zur Klarstellung, der ISR wird deshalb nicht aufgerufen, weil die
CPU nicht aufwacht. Es hat nichts mit den __builtin_disis zu tun. Der
folgende Code hängt auch:
Bert 4. schrieb:> Kastanie schrieb:>> Heinz R. meint, du solltest es so testen:>>>> CRYCONLbits.CRYGO = 1; // Berechnung starten>> __builtin_disi(0x3FFF);>> Idle();>> while(CRYCONLbits.CRYGO) // (macht vor Idle() evtl. den Ärger)>> __builtin_disi(0);> Ändert nichts. Ist meiner Ansicht nach aber auch konzeptionell falsch> (oder zumindest riskant). Wenn ein anderer Interrupt direkt nach CRYGO=1> feuert, würde man danach Idle gehen, selbst wenn die Crypto-Sache in der> Zwischenzeit fertig ist. Du bräuchtest ein if vor dem Idle und dann> kannst du auch gleich die Schleife dort hin setzen. Deine Schleife> ergibt wenig Sinn, wieso sollte man wiederholt Interrupts einschalten?
Sorry, ich habe natürlich den Strichpunkt bzw. eine Klammer nach der
WhileZeile vergessen und dann sollte evtl. vor dem Idle noch das
Interruptflag gelöscht werden.
Die WhileZeile kann man natürlich auch komplett entfernen, wenn nur der
CryptoInterrupt aktiv ist.
1
__builtin_disi(0x3FFF);
2
ClearCryptoEngineInterruptFlag;// (sinngemäß)
3
CRYCONLbits.CRYGO=1;// Berechnung starten
4
Idle();
5
__builtin_disi(0);
6
//while(CRYCONLbits.CRYGO) {}; // (macht vor Idle() evtl. den Ärger)
In diesem Fall sehe ich nicht wofür man die __builtin_disi()s noch
bräuchte. Aber unabhängig davon wacht er bei diesem Code-Stück nie aus
dem Idle auf, es sei denn andere Interrupts feuern.
Ach, das ist ja ein PIC24 mit einer einstellbaren "Nix-Interrupt"-Zeit.
Ich war gedanklich ganz woanders.
Dann ist das
1
__builtin_disi(0x3FFF);
natürlich in diesem Kontext falsch, da der Interrupt ab dann disabled
wird.
Und daraus kann natürlich nichts erwachen...
Dann sollte es natürlich so sein:
Das Flag erst nach dem Start zu löschen halte ich für keine gute Idee.
In diesem Minimalbeispiel macht es natürlich nichts, aber generell
sollte man nicht ohne Weiteres annehmen, dass diese beiden Anweisungen
atomar ausgeführt werden.
Nur zur Erinnerung: Beim PIC24 wecken lokal aktivierte Interrupts (will
heißen *IE=1, in diesem Fall DONEIE=1) die CPU auf, auch wenn Interrupts
global disabled sind (z.B. via __builtin_disi(0x3fff)). Letzteres
beeinflusst nur ob/wann ISR aufgerufen wird, nicht das Aufwecken an
sich.
Aber wie gesagt, das sind alles Nebenkriegsschauplätze. Solange mein uC
nichts anderes tut und keine anderen Interrupts enabled, sollte dieser
winzige Codeschnipsel funktionieren, tut es aber nicht:
1
CRYCONLbits.CRYON=0b1;
2
CRYCONLbits.CRYSIDL=0b0;
3
CRYCONLbits.DONEIE=0b1;
4
CRYCONLbits.CPHRSEL=0b1;
5
CRYCONLbits.CRYGO=1;
6
while(CRYCONLbits.CRYGO)Idle();
PS: Wo wir es vorhin noch vom schlecht administrierten Microchip-Forum
hatten - Jetzt ist es ganz down mit einem 404er...
Ein vorsichtiger Bump für diesen Thread. Ich wäre froh, wenn noch jemand
eine Idee hätte.
Im offiziellen Microchip-Forum, wo Fragen wie diese zweifellos
hingehören, ist kein Durchkommen. Es hat drei Tage gedauert, bis meine
Registrierung freigeschaltet wurde, aber Beiträge darf ich dort offenbar
trotzdem keine schreiben.
Bert 4. schrieb:> Beiträge darf ich dort offenbar> trotzdem keine schreiben.
Schreiben müsstest du können, aber die ersten Beiträge müssen von einem
Admin abgesegnet werden, bevor sie sichtbar werden.
Nach massiven Spamatacken vor einiger Zeit ist das Forum für Einsteiger
praktisch leider unbrauchbar geworden :-(
Bert 4. schrieb:> (Beim PIC24 ist es so, dass er aufwacht wenn ein Interrupt> individuell aktiviert ist, auch wenn Interrupts global aus sind.)
Na wenn Du das so sagst, dann wird es wohl auch so im Datenblatt stehen.
Ich kenne mich mit den PICs nicht aus.
Bei den 8051 oder AVRs ist das Aufwachen jedenfalls deutlich anders:
- Interrupts müssen global, als auch der Aufwachinterrupt enabled sein.
- Es muß ein Interrupthandler dafür existieren.
- Das Sleep darf nicht in einem Interrupthandler aufgerufen werden.
Peter D. schrieb:> Bert 4. schrieb:>> (Beim PIC24 ist es so, dass er aufwacht wenn ein Interrupt>> individuell aktiviert ist, auch wenn Interrupts global aus sind.)>> Na wenn Du das so sagst, dann wird es wohl auch so im Datenblatt stehen.> Ich kenne mich mit den PICs nicht aus.>> Bei den 8051 oder AVRs ist das Aufwachen jedenfalls deutlich anders:> - Interrupts müssen global, als auch der Aufwachinterrupt enabled sein.> - Es muß ein Interrupthandler dafür existieren.> - Das Sleep darf nicht in einem Interrupthandler aufgerufen werden.
Erstmal vorweg die Bemerkung, dass das nur ein Nebenkriegsschauplatz
ist. Wie oben geschrieben, ist das Hauptproblem, dass der Interrupt
nicht triggert. Auch dann nicht, wenn ich die ganzen
__builtin_disi-Aufrufe weglasse.
Nun zu deiner Anmerkung: Ich bin auch relativ neu mit dem PICs. Bei der
Aussage habe ich mich auf 10.2 im Datenblatt bezogen: "The device will
wake from Idle mode on any of these events: • Any interrupt that is
individually enabled • Any device Reset • A WDT time-out" Und natürlich
auf diverse - nicht zwingend verlässliche - Internet-Quellen.
Das Grundproblem ist, dass man eine Methode braucht, um Dinge wie
"if(!Interrupt) Sleep();" atomar zu machen. Sonst könnte der Interrupt
zwischen der Abfrage und dem Sleep-Befehl kommen. Diese Methode ist auf
jedem uC anders.
Soweit ich weiß nutzt man beim AVR aus, dass der SEI-Befehl Interrupts
erst nach der darauf folgenden Anweisung freigibt. Dadurch kannst du CLI
machen, dann testen, ob ein Interrupt vorliegt und wenn nicht "SEI;
SLEEP" aufrufen. Sollte in der Zwischenzeit ein Interrupt aufgelaufen
sein, triggert dieser erst nach Ausführen der SLEEP-Anweisung und der
AVR wacht auf. Quelle:
https://www.nongnu.org/avr-libc/user-manual/group__avr__sleep.html
Beim PIC - so verstehe ich das zumindest - macht man es so wie in meinem
obigen Betrag geschrieben. Quelle:
http://www.microchip.com/forums/m885430.aspx#885464 (Der Thread ist im
PIC32-Board, der Beitrag bezieht sich jedoch auf andere PICs.) Wenn das
falsch ist, wäre ich froh um Aufklärung.
Volker S. schrieb:> Schreiben müsstest du können, aber die ersten Beiträge müssen von einem> Admin abgesegnet werden, bevor sie sichtbar werden.> Nach massiven Spamatacken vor einiger Zeit ist das Forum für Einsteiger> praktisch leider unbrauchbar geworden :-(
Ja, so kommt es mir auch vor. Ich habe einen Thread eröffnet und bekam
die Meldung, dass er aufs Freischalten warten würde, was aber seit Tagen
nicht passiert. Außerdem sollte - so wird es zumindest irgendwo in den
Untiefen der Hilfeseiten erwähnt - mein nicht-freigeschaltener Beitrag
in meinem Profil für mich sichtbar sein, ist er aber nicht. Entsprechend
habe ich keine Ahnung, ob es nur sehr lange dauert oder ob er ganz
verschluckt wurde.
Bert 4. schrieb:> Ja, so kommt es mir auch vor. Ich habe einen Thread eröffnet und bekam> die Meldung, dass er aufs Freischalten warten würde, was aber seit Tagen> nicht passiert. Außerdem sollte - so wird es zumindest irgendwo in den> Untiefen der Hilfeseiten erwähnt - mein nicht-freigeschaltener Beitrag> in meinem Profil für mich sichtbar sein, ist er aber nicht. Entsprechend> habe ich keine Ahnung, ob es nur sehr lange dauert oder ob er ganz> verschluckt wurde.
Das sollte dann eigentlich so wie angehängt aussehen.
"Approval Pending"
Möglicherweise findest du in einem alternativen Forum Hilfe.
http://picforum.ric323.com/viewforum.php?f=66
Da ist zwar nichts los, aber Ric und Susan scheinen regelmäßig rein zu
schauen und die haben auch Ahnung ;-)
Volker S. schrieb:> Das sollte dann eigentlich so wie angehängt aussehen.> "Approval Pending"
Bei mir sieht das nach gar nichts aus, weil es keine Spur von dem
Beitrag gibt. Weder in dem entsprechenden Unterforum noch in meinem
Profil. Ich habe nach dem Absenden nur die Bestätigung gekriegt, dass
jetzt ein Admin approven müsste, seither sehe ich keine Spur mehr davon.
Ich hoffe der wurde nicht einfach verschluckt. Oder gibt es eine
spezielle Seite mit meinen verfassten aber nicht freigeschalteten
Beiträgen und ich bin nur zu dumm die zu finden?
B. P. schrieb:> Vielleicht verwandt...> Beitrag "Timer1 weckt nicht den PIC2LF1840 vom SLEEP()"
Ich glaube nicht, denn da ging es 1. um eine andere uC-Familie und 2. um
ein spezielles Problem mit Taktquellen für einen Timer. Die Timer und
deren Interrupts funktionieren bei mir aber problemlos. Es ist nur
speziell der Crypto-Interrupt, der nicht kommt.
Bert 4. schrieb:> Nun zu deiner Anmerkung: Ich bin auch relativ neu mit dem PICs. Bei der> Aussage habe ich mich auf 10.2 im Datenblatt bezogen: "The device will> wake from Idle mode on any of these events: • Any interrupt that is> individually enabled • Any device Reset • A WDT time-out" Und natürlich> auf diverse - nicht zwingend verlässliche - Internet-Quellen.
Ich würde nicht behaupten, dass das ein Nebenkriegsschauplatz ist.
Und o.a. Datenblatt-Auszug bedeutet mitnichten, dass die globale
Interruptfunktion unerheblich für das Aufwecken durch einzelne
Interrupts ist.
Dieser muss in jedem Fall für eine Auslösung aktiv sein und nur dann
gilt der o.a. Absatz.
Das Interruptflag hingegen wird immer gesetzt.
Ok, ich glaube ich habe den Preis für die dümmste Frage gewonnen.
Der Interrupt hat zwei(!) Enable-Bits. Eines in der Konfiguration der
Crypto-Engine (CRYCONL.DONEIE) und dann noch eines in den
Interrupt-Konfigurations-Registern (IEC3.CRYDNIE). Letzteres wird im
Kapitel zur CryptoEngine und im zugehörigen Family Reference Manual
natürlich genau null-mal erwähnt.
Ich hätte darauf kommen können, weil z.B. auch der CN-Interrupt zwei
Enable-Bits hat. Allerdings macht es dort mehr Sinn, denn das eine Bit
aktiviert CN-Interrupts generell, während das zweite den speziellen Pin
freigibt. Beim Crypto weiß ich nicht was sie sich dabei gedacht haben.
Vielleicht wollten sie die Option haben, mehrere Crypto-Engines zu
verbauen? Scheint mir nicht besonders sinnvoll.
Aber egal, es funktioniert jetzt. Danke an alle, die zu helfen versucht
haben!
Bert 4. schrieb:> Nur zur Erinnerung: Beim PIC24 wecken lokal aktivierte Interrupts (will> heißen *IE=1, in diesem Fall DONEIE=1) die CPU auf, auch wenn Interrupts> global disabled sind (z.B. via __builtin_disi(0x3fff)). Letzteres> beeinflusst nur ob/wann ISR aufgerufen wird, nicht das Aufwecken an> sich.
Die Idee ist recht clever und erspart einem jedwede atomic Hacks.
Man kann in aller Ruhe sleep ausführen und irgendwann danach das global
enable, ohne daß man race-conditions befürchten muß.
Man merkt, die Entwickler haben mitgedacht, chapeau.
@Peter: Ja, das hat mir auch gefallen. Der einzige Nachteil ist, dass es
keine zentrale Stelle gibt, um dieses Aufwecken abzuschalten. Wollte man
das tun, so müsste man alle individuellen Interrupt-Schalter ausmachen.
(Kann aber auch sein, dass es einen solchen Schalter gibt und ich ihn
nur nicht kenne.)
@Kastanie: Du musst beachten, dass "Interrupts global abschalten" beim
PIC24 eine etwas spezielle Bedeutung hat. Man legt nicht wie beim AVR
ein Bit um, sondern man setzt das Prioritäts-Level der CPU aufs Maximum
(ich glaube 7). Die CPU kann in ihrer Ausführung nur von Interrupts
unterbrochen werden, die ein höheres (oder gleich hohes?) Level haben.
Da alle Interrupts per Default Level 4 sind, schaltet man sie so quasi
ab. Das betrifft aber nur das Anspringen der ISR, nicht das Aufwecken.
Bert 4. schrieb:> Der einzige Nachteil ist, dass es> keine zentrale Stelle gibt, um dieses Aufwecken abzuschalten.
Ich sehe das nicht als großen Nachteil an.
Schaltet man das Aufwachen ab, kommt man doch nur noch mit einem Reset
wieder raus. Bei Batteriebetrieb hieße das, man muß das Batteriefach
öffnen und die Batterie entnehmen. Ich glaub nicht, daß ein Kunde sowas
gut finden würde.