Forum: Mikrocontroller und Digitale Elektronik MSP430 Low Power Mode wechseln?


von Maximum Security Prison 430 (Gast)


Lesenswert?

msp430g2xxx.h definiert einige intrinsics zu den "low power modes":
1
#define LPM0_bits              (CPUOFF)
2
...
3
#define LPM4_bits              (SCG1+SCG0+OSCOFF+CPUOFF)
4
#define LPM0      __bis_SR_register(LPM0_bits)         /* Enter Low Power Mode 0 */
5
#define LPM0_EXIT __bic_SR_register_on_exit(LPM0_bits) /* Exit Low Power Mode 0 */
6
...
7
#define LPM4      __bis_SR_register(LPM4_bits)         /* Enter Low Power Mode 4 */
8
#define LPM4_EXIT __bic_SR_register_on_exit(LPM4_bits) /* Exit Low Power Mode 4 */

zum Aktivieren und Verlassen bei Rückkehr aus einer ISR.
Wie kann jedoch der Modus gewechselt werden, also z.B. von LPM4 zu 
LPM1 (am Ende des pin-change Interrupts)?

Reicht evtl. ein
1
__bic_SR_register_on_exit(SCG1+OSCOFF);
?

von more power (Gast)


Lesenswert?

Mit den defines kannst du den Lpm4 nur im normalen Programm ändern. Also 
ISR ohne Lpm verlassen und den neuen einstellen.

Oder du musst das SR auf dem Stack in der ISR von Hand anpassen.

von Maximum Security Prison 430 (Gast)


Lesenswert?

Ich dachte, die _on_exit builtins machen genau dies: das auf dem Stack 
gesicherte SR modifizieren.

https://gcc.gnu.org/onlinedocs/gcc/MSP430-Built-in-Functions.html
> This clears the indicated bits in the saved copy of the status register 
currently residing on the stack. This only works inside interrupt handlers and the 
changes to the status register will only take affect once the handler returns.

http://www.rowleydownload.co.uk/msp430/documentation/__bic_SR_register_on_exit.htm
> __bic_SR_register_on_exit clears the bits specified in mask in the saved status 
register of an interrupt function (i.e. it bitwise ands the complement of mask 
into the saved status register). This allows you to change the operating mode of 
the MSP430 on return from the interrupt service routine, such as changing the low 
power mode.
> __bic_SR_register_on_exit can only be used in interrupt functions—an error is 
reported if it is used outside an interrupt function.

Ich brauch's aber gar nicht mehr, da ich den Code aus dem Interrupt 
herausgeholt habe und daher die CPU aktiv brauche.

von more power (Gast)


Lesenswert?

on_exit clears...
Und nicht set! Zum wechseln brauchst du beides.

von Clemens L. (c_l)


Lesenswert?

more power schrieb:
> Zum wechseln brauchst du beides.
1
__bic_SR_register_on_exit(LPM4_bits);
2
__bis_SR_register_on_exit(LPM1_bits);
3
/*  ^ */

Maximum Security Prison 430 schrieb:
> Ich brauch's aber gar nicht mehr, da ich den Code aus dem Interrupt
> herausgeholt habe

Das wird empfohlen; auch, weil mit einem kürzeren Interrupt-Handler 
andere Interrupts schneller behandelt werden können.

von Maximum Security Prison 430 (Gast)


Lesenswert?

Nicht in diesem Fall (LPM4->LPM1), es müssen doch nur die 
"überflüssigen" bits gelöscht werden um die entsprechende Hardware 
wieder zu aktivieren.
Genau das war ja die Ausgangsfrage, ob man "LPM4_EXIT" (bits löschen) 
gefolgt von "LPM1" (bits schreiben) vermeiden kann.

Probiert habe ich es aber nicht.

Anwendung ist das Aufwachen und anschließendes, timergesteuertes Pollen 
(Entprellen etc.) bei Tastendruck. Man könnte so bei Bedarf fast ohne 
CPU-Einsatz (im Interrupt läuft sie) durch den pin-change Interrupt den 
Timer aktivieren.

von wv (Gast)


Lesenswert?

wenn ich das richtig verstanden habe, willst Du nicht nach dem Verlassen 
der ISR die CPU aufwecken, sondern in einem anderen Low-Power-Mode 
weiterschlafen lassen. Dann darfst Du nicht die .._on_exit() intrinsics 
verwenden, denn die wirken sich erst bei Verlassen der ISR aus. Ich 
würde da direkt in der ISR mit dem __bic_SR_register() Befehl arbeiten:

Auszug aus der Header-Datei:
1
#define LPM1_bits              (SCG0+CPUOFF)
2
#define LPM4_bits              (SCG1+SCG0+OSCOFF+CPUOFF)

also sollte
1
 
2
__bic_SR_register(SCG1+OSCOFF)

von LPM4 nach LPM1 umschalten.

-- ungetestet

--wv

von Maximum Security Prison 430 (Gast)


Lesenswert?

Die ISR wird in jedem Fall verlassen und das SR wiederhergestellt. D.h. 
deine vorgeschlagene Manipulation auf das SR (= Active Mode in der ISR) 
ändert nicht viel und wird sofort wieder überschrieben...

von Clemens L. (c_l)


Lesenswert?

Maximum Security Prison 430 schrieb:
> Genau das war ja die Ausgangsfrage, ob man "LPM4_EXIT" (bits löschen)
> gefolgt von "LPM1" (bits schreiben) vermeiden kann.

"LPM1;" würde die CPU sofort schlafen legen. Innerhalb des 
Interrupt-Handlers ist das keine gute Idee.

Ein
1
__bic_SR_register_on_exit(SCG1+OSCOFF); // LPM4 -> LPM1
sollte in der Tat funktionieren.

von JW (Gast)


Lesenswert?

Im Family Data sheet steht im Abschnitt 2.2.3.1 "Interrupt Acceptance" 
ganz klar, daß das SR zurückgesetzt wird; das heißt, CPUOFF=0, also 
Active Mode.

Du mußt für den neuen Modus das SR aus dem Active Mode heraus 
entsprechend neu setzen.

von Mickael (Gast)


Lesenswert?

JW schrieb:
> Family Data sheet

von was? TI hat diverse Family User Guides für die verschiedenen MSPs.

JW schrieb:
> ganz klar, daß das SR zurückgesetzt wird

um die ISR auszuführen! Das SR wird beim Eintritt gepushed und mit dem 
RETI gepopped. Nach der ISR isr vor der ISR.

Beitrag #5070420 wurde von einem Moderator gelöscht.
von JW (Gast)


Lesenswert?

@ Mikael:

Bist ein ganz schlauer, gell?
ad 1: Im Eröffnungs-Thread steht MSP430G2xxxx - also gibt's dazu einen 
Family Datasheet.

ad 2: Um von einem LPM-Zustand in einen anderen zu wechseln, muß die MCU 
zuerst in den AM gehen - was für ein Wunder, natürlich ist das eine ISR.



Ergänzung zum ursprünglichen Thema:
Willst Du in einer ISR den Zustand wechseln, dann könnte das z.B. so 
aussehen:


add   #4,SP                  // Korrigiere die Stackpointer-Position
                             // (also das, was auch bei "reti" 
geschieht)
bis   #SCG1+OSCOFF,SR        // Wieder in den gewünschten LPM

von Mickael (Gast)


Lesenswert?

JW schrieb:
> also gibt's dazu einen
> Family Datasheet

Nö, entweder Family User Guide oder Baustein Datenblatt,
aber kein Family Datenblatt. ;-)

JW schrieb:
> Willst Du in einer ISR den Zustand wechseln

Was fürn Schwachsinn? Wenn du den MSP430 innerhalb der ISR Schlafen 
schickst, wer soll ihn wieder wecken?

Da du den FUG nicht liest oder verstehst, schreib ich dirs noch einmal 
auf:

Beim Eintritt in die ISR rettet der uC das SR auf dem Stack. Dann kannst 
du deinen Kram machen. Und mit dem RETI restauriert der uC das SR (mit 
dem Wert vom Stack).
-> Dein Gefummele innerhalbb der ISR ist für die (Speicher-)Tonne.

JW schrieb:
> Bist ein ganz schlauer, gell?

Wenns um uns Beiden beim Thema MSP geht: JA!

von Maximum Security Prison 430 (Gast)


Lesenswert?

Kinder, Zeit für den Mittagsschlaf!

von Mickael (Gast)


Lesenswert?

Maximum Security Prison 430 schrieb:
> Kinder, Zeit für den Mittagsschlaf!

Geht ja leider nicht, bei dem Blödsinn. Über Geschmack läßt sich 
streiten und trotzdem sollte man jedem seine Meinung gönnen. Aber bei 
dem Thema hier ...

JW schrieb:
> add   #4,SP                  // Korrigiere die Stackpointer-Position
>                              // (also das, was auch bei "reti"
> geschieht)
> bis   #SCG1+OSCOFF,SR        // Wieder in den gewünschten LPM

Puhhh, das wird ja immer besser :'(

Probier das mal aus und berichte über deine Ergebnisse. :-P

von Mickael (Gast)


Lesenswert?

So, hatte gerade mal das launchpad in der Hand. Mit 
__bis_SR_register_on_exit läßt sich der LPM beliebig wechseln oder 
auch verlassen. Getestet habe ich das mit folgendem Programm
1
// launchpad V line, G2553
2
3
#include "io430.h"
4
#include <stdlib.h>
5
6
7
#define LED_RED     BIT0
8
#define LED_GREEN   BIT6
9
10
11
int main( void )
12
{ 
13
  // Stop watchdog timer to prevent time out reset
14
  WDTCTL = WDTPW + WDTHOLD;
15
  
16
  // clock source DCO, set to 1MHz
17
  BCSCTL1 = CALBC1_1MHZ;
18
  DCOCTL = CALDCO_1MHZ;
19
 
20
  // output for LED
21
  P1SEL |= LED_GREEN;
22
  P1DIR |= (LED_GREEN | LED_RED);
23
  
24
  // f(PWM) 1kHz
25
  TACCR0 = 1000;
26
  // PWM value
27
  TACCR1 = 100;
28
  // outmode 7 for TA0.1
29
  TACCTL1 = OUTMOD_7;
30
  // init timer
31
  TACTL = TASSEL1 | MC0 | TACLR;
32
 
33
  // WDT Timer
34
  WDTCTL = WDT_MDLY_8;
35
36
  // enable interrupts
37
  IE1 |= WDTIE;
38
  
39
  // sleep until intr
40
  __bis_SR_register (LPM0_bits | GIE);
41
      
42
  // never reached
43
  return 0;
44
}
45
46
47
//Watchdog Timer interrupt service routine
48
#pragma vector=WDT_VECTOR
49
__interrupt void WDT_ISR (void)
50
{
51
  static unsigned int cnt = 0;
52
  static int pwm_speed = 0;
53
  static unsigned int rand_cnt = 5;
54
55
  // ca. 100ms (<- 96ms)
56
  if (cnt++ >= 12)
57
  {
58
    cnt  = 0;
59
    //
60
    // do every 100ms
61
    pwm_speed++;
62
    if (pwm_speed >= 50)
63
      pwm_speed = -49;
64
    TACCR1 = abs (pwm_speed) * 20;    
65
66
    if (rand_cnt-- == 0) 
67
    {
68
      rand_cnt = rand () % 10;
69
      // LED invers
70
      if (P1OUT & LED_RED)
71
        P1OUT &= ~LED_RED;
72
      else
73
        P1OUT |= LED_RED;
74
    }
75
  }
76
77
  __bis_SR_register_on_exit (LPM1_bits | GIE);
78
  
79
}

Bitte melde dich an um einen Beitrag zu schreiben. Anmeldung ist kostenlos und dauert nur eine Minute.
Bestehender Account
Schon ein Account bei Google/GoogleMail? Keine Anmeldung erforderlich!
Mit Google-Account einloggen
Noch kein Account? Hier anmelden.