Ich habe einen PIC18LF25K50. Spannungsversorgung kommt von Batterie.
Nach Initialisierung des Mikrocontrollers, UART Schnittstelle, LCD und
gesamter Peripherie würde ich um die Batterie zu schonen den PIC gerne
in den Sleep Modus schicken.
Der Trigger, um den PIC aus diesem wieder herauszuholen solle wenn
möglich die UART Receive Leitung sein.
Wie schickt man den PIC in den Sleep Modus? Was macht das Macro...
1
#define Sleep() {_asm sleep _endasm}
Wie sieht das Programmkonstrukt in etwa aus?
1
intmain()
2
{
3
// Initialisierung PIC, UART etc.
4
while(1)
5
{
6
// setze PIC in Schlafmodus
7
// Überprüfe ob Daten von extern über UART "reinkommen"
8
// Ja dann mache etwas
9
// Sonst bleibe im Sleep Modus
10
}
11
}
Wie definiert man den UART als Trigger für das Erwachen aus dem Sleep
Modus?
Ich weiss das ist alles etwas wage formuliert.
Ein einfaches Programmbeispiel für den obigen Fall würde mir sehr
helfen.
Danke
ich erinnere mich nur dunkel aber du musst auf jeden fall darauf achten
dass das GIE(Global Interrupt Enable)bit gesetzt ist, das PIE(Peripheral
Interrupt Enable)Bit und das Interruptbit für die Uart wenn ein byte
empfangen wurde (vergessen wies hieß).
Wenn du das GIE zB nicht gesetzt hast sind alle interrupts ausgeschaltet
obwohl die im anderen alle an geschaltet sind.
Und ich glaube dannn musst du noch aufpassen das der nicht in der
Interrupt routine hängen bleibt weil du das Interruptbit für die UART
per software glaub ich löschen musst indem du den Register liest.
Vertrackt an der Sache ist, dass beim Sleep kein Clock da ist, aber die
UART einen Clock für die Baudrate braucht. Von daher ist das Aufwachen
bei UART-Tätigkeit nicht so selbstverständlich wie man denken sollte.
Aber dein PIC kann das günstigerweise. Das Feature nennt sich "Auto wake
up on break". Das müsstest du einschalten. Siehe auch 17.4.3 im
Datenblatt.
Laut Datenblatt sollte dein µC dann bei jedem UART Receive automatisch
aufwachen.
Du musst aber beachten, dass du ein oder mehrere Byte verlierst, weil
der Clock des PIC erst einmal starten muss, das dauert manchmal etwas.
Speziell wenn ein Quarz dran ist auch mal etwas länger.
Sende also ein "wakeup"-Paket vor Start der Kommunikation.
Über das INTCON Register aktiviere ich die Interrupts. In der Interrupt
Routine selbst muss ich das auslösende Interrupt - Flag am Ende wieder
löschen, damit Interrupts wieder möglich sind. Dies mache ich über
1
PIR1bits.RCIF=0;// make sure the RX flag is clear
Ist dies das richtige Bit?
Das deaktivieren von Interrupts am Anfang der ISR über PIE1bits.RCIE =
0; kann ich mir sparen? Dies geschieht automatisch bei Springen in die
ISR, damit keine weiteren Interrupts ausgelöst werden können?
Eine weitere Frage: Woher was der PIC was den Interrupt Triggerns soll
(nämlich genau wenn UART Daten anliegen)??
Beim PIC24 kenn ich das in etwa über:
Damit ist mir klar, dass der Interrupt über U1RX getriggert wird. Woher
weiss der PIC das in diesem Fall?
Mir ist wichtig, dass ich das einmal sauber runterprogrammiert bekommn
und auch nachvollziehen kann. Deswegen bin ich über jeglichen Input
dankbar. Sleep Modus kümmere ich mich dann danach.
Danke
bzw. ich bleibe nur NICHT hängen, wenn ich in der ISR
[c]
if(PIR1bits.RCIF == 1)
{}
[c]
abfrage.
Wie kann das sein? Hier checke ich doch nur die TX Leitung und schicke
das empfangene Character wieder zurück?
Was ich in der ISR ausführe dürfte doch eigentlich keinen Einfluß haben?
Das hier:
>> PIR1bits.RCIF = 0; // make sure the RX flag is clear>
bewirkt gar nichts.
Das RCIF kann nicht manuell gelöscht werden sondern nur indem man den
RX-Puffer ausliest.
Zitat aus dem DB : .....The RCIF interrupt flag bit is read-only, it
cannot be set or cleared by software.
Schläfer schrieb:> bzw. ich bleibe nur NICHT hängen, wenn ich in der ISR>> [c]>> if(PIR1bits.RCIF == 1)> {}> [c]
Das RC-Interrupt Flag (RCIF) wird zurückgesetzt, wenn es gelesen wird
(siehe S.117 im Datenblatt). Wenn du in der ISR das RCIF nicht liest,
bleibt es gesetzt und nach verlassen der ISR wird sofort wieder
hineingesprungen.
Also: In der ISR als erstes das RCIF auslesen (was du mit der IF-Abfrage
machst), um es zu löschen.
Generell ist es gut, am Anfang jeder ISR das jeweilige IR-Flag zu
prüfen. Dadurch kann geprüft werden, ob die ISR auch wirklich durch
einen Interrupt aufgerufen wurde und nicht etwa durch einen Fehler.
OK, danke sehr.
Ich habe jetzt einmal folgendes probiert:
1
voidisr(void)
2
{
3
4
if(PIR1bits.RCIF==1)// if the USART receive interrupt flag has been set
5
{
6
7
8
}
9
10
11
}
Dies führt ebenfalls zum Daueraufenthalt in der Schleife, da das RCIF
Bit nicht wieder gelöscht wird.
Jedoch...
1
voidisr(void)
2
{
3
unsignedcharc;
4
5
if(PIR1bits.RCIF==1)// if the USART receive interrupt flag has been set
6
{
7
c=RCREG;// read Dummy
8
9
}
funktioniert, da ich durch das Lesen des RX Buffers automatisch (und nur
dann!!!) das RCIF bit wieder gelöscht wird?
Soweit richtig verstanden?
Schönen Dank für die schnellen Hinweise.
Ja soweit ist das richtig...
ich will ja nicht klugscheißen aber
Ludwig S. schrieb:> Und ich glaube dannn musst du noch aufpassen das der nicht in der> Interrupt routine hängen bleibt weil du das Interruptbit für die UART> per software glaub ich löschen musst indem du den Register liest.
ich hatte das bereits geschrieben... wenn auch zugegebener maßen etwas
eilig aber es stand die ganze zeit da^^
Ludwig S. schrieb:> Ja soweit ist das richtig...> ich will ja nicht klugscheißen aber>> Ludwig S. schrieb:>> Und ich glaube dannn musst du noch aufpassen das der nicht in der>> Interrupt routine hängen bleibt weil du das Interruptbit für die UART>> per software glaub ich löschen musst indem du den Register liest.>> ich hatte das bereits geschrieben... wenn auch zugegebener maßen etwas> eilig aber es stand die ganze zeit da^^
Ja ok, Du hast natürlich Recht.
Per Software löschen habe ich irgendwie damit in Verbindung gebracht das
Bit manuell zu löschen (Dies habe ich mit PIR1bits.RCIF = 0; probiert).
Das es durch das Lesen des Registers gelöscht wird hast Du zwar
geschrieben, war mir leider nicht ganz klar bzw. habe ich nicht
verstanden.
Danke auf jeden Fall bis zu diesem Punkt!
Nochmal eine weitere Frage bzgl. Interrupts:
- Darf man in Interruptroutinen weitere Funktionen aufrufen (kompilier
bar ist es)?
- Darf man es, es wird jedoch nicht empfohlen?
- wie sieht es mit delays in ISR aus?
Schläfer schrieb:> - Darf man in Interruptroutinen weitere Funktionen aufrufen (kompilier> bar ist es)?> - Darf man es, es wird jedoch nicht empfohlen?> - wie sieht es mit delays in ISR aus?
Man darf alles.
Man muß nur berücksichtigen, daß dabei die Ausführung des Main und aller
anderen Interrupts nicht höherer Priorität angehalten wird.
Gegen ein Delay von max 10..100µs spricht fast nichts.
Ich würde jetzt gerne noch den kompletten string, der über UART
geschickt wird in der ISR in einem Buffer abspeichern.
Die geschickten Strigs werden in der Regel mit einem carriage Return
abgeschlossen.
Hier ist mein bisheriger Code
1
unsignedcharrx_char;
2
unsignedinti=0;
3
4
5
charbuffer_rx[100];
6
7
8
9
// start ISR code
10
#pragma code isr = 0x08 // store the below code at address 0x08
11
#pragma interrupt isr // let the compiler know that the function isr() is an interrupt handler
12
13
voidisr(void)
14
{
15
Nop();
16
Nop();
17
Nop();
18
if(PIR1bits.RCIF==1)// if the USART receive interrupt flag has been set
19
{
20
while(rx_char!='/r')
21
{
22
rx_char=getcUSART1();
23
buffer_rx[i]=rx_char;
24
if(rx_char!='/n')
25
i++;
26
}
27
}
28
Nop();
29
Nop();
30
Nop();
31
32
}
33
34
#pragma code // return to the default code section
35
// end ISR code
Leider wird der zweite Buchstabe immer mehrmals geschrieben. Ich springe
also nicht nur im Array weiter wenn einer neuer Buchstabe anliegt,
sondern mit jedem Step. Wenn das carriage Return '/r' kommt soll aus der
ISR herausgesprungen werden.
Fällt der Fehler direkt ins Auge?
Eigentlich will ja nur über ein empfangenes UART Signal ein Interrupt
auslösen (das klappt ja bis dato) und in dieser Routine den kompletten
gesendeten UART String (nicht nur 1 Byte) in einem char Array
abspeichern. Die Länge des Strings soll dabei variable sein (nämlich so
lang wie der gesendete UART String).
Hätte nicht gedacht dass dies so kompliziert ist :)
Schläfer schrieb:> Eigentlich will ja nur über ein empfangenes UART Signal ein Interrupt> auslösen (das klappt ja bis dato) und in dieser Routine den kompletten> gesendeten UART String (nicht nur 1 Byte) in einem char Array> abspeichern. Die Länge des Strings soll dabei variable sein (nämlich so> lang wie der gesendete UART String).
Regel: Mach' so wenig wie möglich in einer ISR.
In Hauptprogramm
Puffer für String reservieren
Zeiger auf das erste Zeichen setzen
In der ISR
Empfagenes Zeichen in den Puffer schreiben
Puffer voll? -> Flag setzen
Ende-Kenung gekommen? -> Flag setzen
Zeiger auf nächsten Pufferplatz setzen
ISR verlassen
Im Hauptprogramm die Flags auswerten ung ggfls. den String verarbeiten
und wieder Sleep aufrufen.
Keinesfalls in der ISR auf den ganzen String warten. Was machst du sonst
z. b. wenn die Ende-Kennung nicht kommt?
PICler schrieb:> Schläfer schrieb:>> Eigentlich will ja nur über ein empfangenes UART Signal ein Interrupt>> auslösen (das klappt ja bis dato) und in dieser Routine den kompletten>> gesendeten UART String (nicht nur 1 Byte) in einem char Array>> abspeichern. Die Länge des Strings soll dabei variable sein (nämlich so>> lang wie der gesendete UART String).>> Regel: Mach' so wenig wie möglich in einer ISR.>> In Hauptprogramm> Puffer für String reservieren> Zeiger auf das erste Zeichen setzen>> In der ISR> Empfagenes Zeichen in den Puffer schreiben> Puffer voll? -> Flag setzen> Ende-Kenung gekommen? -> Flag setzen> Zeiger auf nächsten Pufferplatz setzen> ISR verlassen>> Im Hauptprogramm die Flags auswerten ung ggfls. den String verarbeiten> und wieder Sleep aufrufen.>> Keinesfalls in der ISR auf den ganzen String warten. Was machst du sonst> z. b. wenn die Ende-Kennung nicht kommt?
Super!!!
Genau sowas habe ich gesucht und hat mir extrem weitergeholfen.
Danke dafür.
Das einlesen des UART Strings über die ISR Routine funktioniert jetzt
tadellos. Danke dafür an Alle.
Jetzt möchte ich ja nach erfolgreich übertragenen String den MCU wieder
in den Sleep Modus versetzten. Das funktioniert in etwa wie folgt:
1
...
2
voidmain(void)
3
{
4
while(1)
5
{
6
if(flag==1)// Wenn Endekennung oder Buffer voll
7
{
8
// verarbeite UART string irgendwie
9
clean_buffer();// Lösche Buffer wieder
10
i=0;// zeige wieder auf erstes Element im Buffer
11
flag=0;// Lösche Flag, da Wort erfolgreich erkannt
12
Sleep();// Schicke PIC18 über Makro wieder in Sleep Modus
13
}
14
}
15
}
So weit so gut. Wie funktioniert das jetzt genau mit dem Erwachen aus
dem Sleep Modus. Ich würde ja gerne NICHT nach Irgendeiner Zeit den PIC
wieder erwachen, sondern immer genau dann wenn neue UART Daten anliegen.
Laut dem Reference Manual ("Section 28 Watchdog Timer and Sleep Mode")
soll dies auch möglich sein.
Wenn ich einen UART RX Interruptroutine implementiert habe über
Erwacht der PIC dann automatisch genau dann wenn die ISR Routine
ausgelöst wird oder muss ich das Erwachen "händisch" in der ISR Routine
angeben??
Was genau hat es mit dem Watchdog auf sich? Ich habe gelesen, dass
dieser gelöscht bzw. disablet sein muss?
In den Config Bits sieht das bei mir bis dato wie folgt aus:
So wie ich es jetzt habe (siehe oben) scheint der PIC in den Sleep Modus
zu springen, aber danach nie wieder hinaus.
Die von WehohWeh angesprochene Problematik mit der disableten Clock (und
der damit fehlenden Baudrate) während des Sleep Modus scheint mir auch
noch nicht ganz klar und muss vermutlich beachtet werden.
Hat da jemand ein paar Codezeilen für mich?
Danke
Ich zitiere mal aus dem von WehohWeh genannten Abschnitt Auto Wake Up
Break" -> 17.4.3 im datasheet PIC1825K50:
"The Auto-Wake- up Feature is enabled by Setting the WUE bit of the
BAUDCONx Register. Once set, the normal receive sequence on RX/DT is
disabled, and the EUSART remains in an idle state, Monitoring for a
wake-up Event Independent of the CPU mode."
Ich habe es also wie folgt (ohne Erfolg versucht):
1
...
2
voidmain(void)
3
{
4
while(1)
5
{
6
if(flag==1)// Wenn Endekennung oder Buffer voll
7
{
8
// verarbeite UART string irgendwie
9
clean_buffer();// Lösche Buffer wieder
10
i=0;// zeige wieder auf erstes Element im Buffer
11
flag=0;// Lösche Flag, da Wort erfolgreich erkannt
12
BAUDCONbits.WUE=1;
13
Sleep();// Schicke PIC18 über Makro wieder in Sleep Modus