Hallo zusammen,
für ein Projekt muss ich ein Rechtecksignal im Frequenzbereich von 50 –
100Hz kontinuierlich überwachen. Das ganze soll möglichst energiesparend
laufen. Als Microcontroller verwende ich den MSP430G2553. Ich habe zu
Testzwecken ein kleines Programm geschrieben. Darin definiere ich zu
Beginn eine Periodendauer (100Hz = 327). Timer_A im Capture Mode
überwacht das externe Signal und falls die Periodendauer über bzw.
unterschritten wird, wird P1.0 high (rote LED auf dem Launchpad). Das
Programm funktioniert soweit wie gewollt allerdings ist der
Stromverbrauch viel zu hoch (4,7 mA). Im LPM3 mit 32kHz Quarz als ACKL
habe ich mir einige µA vorgestellt. Wenn ich das im User’s guide richtig
verstanden habe, sollte die Ausführung doch mit ausgeschalteter CPU
möglich sein:
"Use low-power integrated peripheral modules in place of software driven
functions. For example Timer_A and Timer_B can automatically generate
PWM and capture external timing, with no CPU resources."
Oder wird für die Ausführung der ISR die CPU angeworfen? Gäbe es in
diesem Fall einen alternativen Lösungansatz?
Würde mich über eure Tipps und Vorschläge freuen! Beschäftige mich
leider erst seit kurzem mit dem MSP430.
Viele Grüße
Simon
Wo fangen wir an? :-*
Für die ISR wird natürlich die CPU angeworfen. Den PWM Ausgang kannst du
über den Outmode ohne ISR, ohne CPU, erledigen. Das steht doch schon in
deinen anderen Threads!
Lies the f... Manual: family user guide, Kapitel Timer
Wenn du Strom sparen willst, solltest du dir einen sparsamen CPU clock
aussuchen.
Lies the f... Manual: family user guide, Kapitel Basic Clock System. Am
Anfang vom Kapitel gibt es ein schönes Bild dazu.
Probiert dein Glück und stell das Ergebnis hier ein. Dann können wir
weiter sehen.
Danke für deine Antwort! Der Titel ist vielleicht etwas irreführend,
hier geht es ja eher um eine Überwachung als um eine Messung. Ich will
ja quasi nur wissen ob die Periode sich gegenüber dem Refernzwert
geändert hat oder nicht. Ich habe gehofft, dass das irgendwie ohne die
CPU und nur mit dem Timer-Modul geht. Bei einem 100Hz Signal läuft die
CPU ja quasi ständig. Da wird man mit einer langsamen Taktrate vom
Stromverbrauch auch nicht in den 10µA Bereich kommen. Was meintest du
mit Output-Mode, den compare-Mode? Mit dem kann ich doch nur ein Signal
mit einem bestimmten Timing erzeugen aber keine Eingänge überwachen.
Simon Heller schrieb:> Ich will> ja quasi nur wissen ob die Periode sich gegenüber dem Refernzwert> geändert hat oder nicht.
Das lese ich aus dem Code etwas anders. Du definierst ein Fenster mit
zwei Konstanten und zählst einen Fehlerzähler hoch. Der Fehlerzähler hat
einen konstanten Grenzwert ...
Dafür brauchst du die CPU.
Wenn das die einzige Aufgabe ist, geht das auch mit 3 sparsamen CMOS IC.
Simon Heller schrieb:> Bei einem 100Hz Signal läuft die> CPU ja quasi ständig.
Warum das? Je niedriger die Frequenz, je länger schläft die CPU. Sie
läuft ja nur bei der ISR.
Simon Heller schrieb:> Was meintest du> mit Output-Mode
Dabei dachte ich an die PWM aus deinen anderen Threads. Wird die nicht
mehr benötigt?
Simon Heller schrieb:> den compare-Mode?
Na jetzt kommt endlich eine interessante Alternative ins Spiel. Wozu
nimmt man Capture und wozu Compare?
Wie misst Du den Stromverbrauch? Ist der msp430 noch im Launchpad drin
und der ganze andere Krams noch angeschlossen? Der msp430 selbst
braucht nie und nimmer so viel Strom (ich würde eher 1-2 µA schätzen).
An dem Code kannst Du noch einiges optimieren: Es gibt keinen Grund,
warum die Variablen volatile sein sollten. Außerdem würde ich
minPeriod/maxPeriod als const deklarieren. stoptime und period können
lokale Variablen sein, error ist nicht initialisiert.
@ Thomas: Danke für die Tipps, ich dachte ich mach mit den Informationen
aus der ISR vlt. noch was ;-) Stromverbrauch liegt bei 38µA wenn man den
Vcc Jumper entfernt und dann extern mit 3.3V speise ;-) Wenn ich das
runterrechne auf Vcc = 1,8V komme ich wohl auf etwa 11µA raus!
Kann man denn außer alle Pins als Ausgang und low zu setzen sonst noch
was fürs stromsparen tun?
@Vorkäuer: den Teil mit der PWM brauch ich auch für das Projekt
allerdings zur Ansteuerung eines Aktors der die Frequenz mechanisch
nachregelt. Der hier diskutierte Programmteil dient dazu mögliche
Abweichungen zu erkennen. Meiner Meinung nach dient der Compare-Mode um
einen Ausgang zu setzen bzw. reseten wenn der Timer einen bestimmten
Wert erreicht hat. Verrätst du mir noch deine Idee mit den CMOS ICs?
Simon Heller schrieb:> @ Thomas: Danke für die Tipps, ich dachte ich mach mit den Informationen> aus der ISR vlt. noch was ;-)
Jeden Zugriff auf eine volatile Variable muss der Compiler aus dem RAM
vornehmen, das kostet ordentlich Cycles. Ich würde zumindest
sicherstellen, dass auf jede volatile Variable in der ISR höchstens
einmal lesend und einmal schreibend zugegriffen wird.
Simon Heller schrieb:> Kann man denn außer alle Pins als Ausgang und low zu setzen sonst noch> was fürs stromsparen tun?
TI empfielt ungenutzte Pins als Input mit Pulldown zu konfigurieren.
Port 3 nicht vergessen!
Simon Heller schrieb:> Meiner Meinung nach dient der Compare-Mode um> einen Ausgang zu setzen bzw. reseten wenn der Timer einen bestimmten> Wert erreicht hat.
Meiner Meinung nach wird bei Compare-Match ein Flag gesetzt. Das
boolsche Ergebnis beinhaltet bereits den Vergleich und erfolgt ohne
(oder auch bei schlafender) CPU. Interrupt und Ausgänge können optional
aktiviert werden, sind aber kein "muss".
Du könntest einen Compare auf die untere Grenze und einen auf die obere
setzen. Bei Periodenbeginn - z. B. steigende Flanke - wertest du die
Flags aus und setzt sie und den Timer zurück. CCR1 und CCR2 stehen im
gleichen Register. ;-)
Mit CMOS ICs geht das das genauso gut. Ein Zähler und ein paar Gatter
für Compare und Auswertung.
Guten Morgen /Vorkäuer,
danke das du dich noch meldest ;-) Wollte deine Idee gerade umsetzen,
hänge aber gerade noch daran wie die ISR von TA1CCR0 funktioniert. Wird
da auch #pragma vector = TIMER1_A1_VECTOR aufgerufen? In welchem
Register ist denn dann die Flag zu finden? Im User Guide steht ja das es
für CCR0 ein eigenes Register gibt aber irgendwie hab ich jetzt nicht
rausgefunden wie das heißt :-/
Bei der vorgeschlagenen Compare Variante brauchst du keinen Timer
Interrupt, sondern den Portpin Interrupt. Die Periode beginnt immer mit
steigender (oder immer mit fallender) Flanke.
Ich habe den family user guide SLAU144J–December 2004–Revised July 2013
offen. Im Kapitel 12.3.5 sind die Flags für CCR1 und CCR2 zu finden.
Unter 12.3 gibt es die Register-Übersicht. Der CCR0 Interruppt steht in
12.2.6.1, er hat einen eigenen Intr-Vektor. Die anderen Intr-Quellen
teilen sich den anderen Intr-Vektor. Das steht in 12.2.6 und ein
Beispiel in 12.2.6.3.
Die Quellenangaben kannst du in deine Abschlussarbeit übernehmen. Dann
gibt es nachher keine "Doktortitelaberkennung".
Soooo... Interrupt am Pin 1.4 funktioniert aber der Vergleich mit CCR1
und CCR2 wohl noch nicht. Muss ich denn das CCIE Bit setzen um im TAIV
Register die Flag zu sehen? Wenn ich das mache scheint der Pin-Interrupt
nicht mehr zu funktionieren?! Kann man man den Timer einfach mit
1
TA1R=0
"reseten" oder muss man ihn über
1
2
TACTL&=~MC_02;
3
TACTL|=MC_2;
neu starten?
Danke für die Referenzen im Users Guide! Habe ich auch gelesen und
glaube auch verstanden. Ich habe mich nur gefragt wenn ich TA1CCR0 im
Capture-Mode hätte:
1
TA1CCTL0=CM_1+CCIS_0+SCS+CAP+CCIE;
wie sähe dann die ISR dazu aus? Habe vorhin mal versuch einfach die LED1
zu toggeln aber mit dem Code hat es nicht funktioniert:
1
2
#pragma vector = TIMER1_A1_VECTOR
3
__interruptvoidTimer1_A1_ISR(void){
4
P1OUT^=0x01;
5
}
Deshalb auch mein Frage ob es für CCR0 auch so ein Register wie TAIV
für CCR1 und CCR2 gibt.
Aktueller Code:
1
#include<msp430.h>
2
3
intmaxPeriod=347;
4
intminPeriod=307;
5
6
voidmain(void){
7
WDTCTL=WDTPW|WDTHOLD;// Stop watchdog timer
8
9
10
P1DIR|=0x01+0x40;// P1.0 + P1.6 als Ausgang
11
P1OUT&=~0x01+0x40;// P1.0 + P1.6 low
12
P1IE|=0x10;// Interrupt bei steigender Flanke an P1.4
13
TA1CTL=TASSEL_1+MC_2;// ACLK + Continuous Mode
14
TA1CCR1=minPeriod;//
15
TA1CCR2=maxPeriod;//
16
_BIS_SR(LPM3_bits+GIE);// LPM3 und globale Interrupts aktivieren
17
}
18
19
#pragma vector=PORT1_VECTOR
20
__interruptvoidPort_1(void)
21
{
22
P1OUT^=0x01;// toggle LED1
23
P1IFG&=~0x10;// P1.4 IFG cleared
24
if(TA1IV!=0x02){// Bei unveränderter Frequenz ist CCR1 high und CCR2 negativ. Falls nicht...
Neuer Versuch war auch nicht erfolgreich. Vergleich sieht jetzt so aus:
1
if((TA1CCTL1&CCIFG)&&(!(TA1CCTL2&CCIFG)))
Ich glaube es liegt einfach daran, dass der Timer nicht resetet wird.
Wenn ich mir im Debugger den Wert von TA1R anschaue ist der immer weit
über 330, den sollte er ja eig. nicht erreichen :-/
Am Anfang vom Times Kapitel gibt es ein Bild mit dem Aufbau und der
internen Verknüpfung. Dort sind die Flags und die Steuerbits alle
eingezeichnet. Man sieht schnell, was man einstellen muss und was ein
Steuerbit bewirkt.
-> mit xyIE wird der Interrupt scharf geschaltet und beim Eintreffen in
die ISR verzweigt.
Die Flags werden immer gesetzt, egal ob xyIE oder nicht.
Dein Chip hat zwei Timer, timer0 und timer1. Beide sind vom Typ A. Jeder
Timer hat zwei Interrupt-Vektoren.
timer0_a0 für capture compare unit 0
timer0_a1 für die andern Quellen
timer1_a0 für capture co mpare unit 0
timer1_a1 für die anderen Quellen
minP... und maxP... dürfen echte Konstanten sein. ;-)
Danke für die Info!!! Das werd ich sicher mal brauchen können! Also das
Problem scheint jetzt wirklich zu sein, dass der Timer sich nicht
reseten lässt. Hab jetzt glaub ich so ziemlich jede Variante durch. Wäre
nett wenn mir da noch auf die Sprünge geholfen werden könnte ;-) Im
Internet steht, dass es mit
1
TA1CTL|=TACLR
bzw.
1
TA1R=0
funktionieren soll. Klappt aber beides nicht! ISR sieht aktuell so aus:
1
#pragma vector=PORT1_VECTOR
2
__interruptvoidPort_1(void)
3
{
4
P1OUT^=0x01;// toggle LED1
5
P1IFG&=~0x10;// P1.4 IFG cleared
6
if((TA1CCTL1&CCIFG)&&(!(TA1CCTL2&CCIFG))){// Bei unveränderter Frequenz ist CCR1 high und CCR2 negativ. Falls nicht...
Um hier ein wenig aufzuräumen, stelle ich einen Code für die
Compare-Interrupts und beide Timer-Interrupt Vektoren ein. Das läuft auf
dem Launchpad.
Bei CCR1-Match wird die rote LED eingeschaltet (und die grüne aus). Bei
CCR2-Match die grüne ein. CCR0 ist der Endwert und löscht beide LED.
Mit S2 wird TAR mit Null beschrieben. Mit S2 kann vor Überlauf der Timer
neu gestartet werden.
-> TAR = 0 ist also kein Problem.
1
#include"io430.h"
2
3
#define LED_RED BIT0
4
#define LED_GREEN BIT6
5
#define KEY_S2 BIT3
6
7
intn=0;
8
9
voidmain(void)
10
{
11
// stop watchdog timer to prevent time out reset
12
WDTCTL=WDTPW+WDTHOLD;
13
// clock source DCO 150kHz
14
BCSCTL1=XT2OFF|RSEL0;
15
DCOCTL=DCO1|DCO0;
16
// init key
17
P1REN|=KEY_S2;// pullup
18
P1OUT|=KEY_S2;
19
P1IES|=KEY_S2;// key down
20
P1IFG&=~KEY_S2;// clear flag
21
P1IE|=KEY_S2;// enable intr
22
// init LEDs
23
P1OUT&=~(LED_GREEN|LED_RED);
24
P1DIR|=LED_GREEN|LED_RED;
25
// init Timer0_A, SMCLK, up CCR0, div 8 -> 18.75 kHz
26
TA0CCR0=56250;// 3s
27
TA0CCR1=18750;// 1s
28
TA0CCR2=37500;// 2s
29
TA0CTL=TASSEL_2|MC_1|ID_3|TACLR;
30
TA0CCTL0=CCIE;
31
TA0CCTL1=CCIE;
32
TA0CCTL2=CCIE;
33
34
__bis_SR_register(LPM1_bits+GIE);// sleep until intr
35
36
}
37
38
39
#pragma vector=PORT1_VECTOR
40
__interruptvoidPort_1(void){
41
TA0R=0;
42
P1IFG&=~KEY_S2;// clear flag
43
P1OUT&=~LED_RED;
44
}
45
46
47
#pragma vector=TIMER0_A0_VECTOR
48
__interruptvoidTimer0_A0_ISR(void){
49
P1OUT&=~(LED_RED|LED_GREEN);
50
}
51
52
53
#pragma vector=TIMER0_A1_VECTOR
54
__interruptvoidTimer0_A1_ISR(void){
55
56
switch(__even_in_range(TA0IV,0x0A)){
57
caseTA0IV_TACCR1:
58
P1OUT|=LED_RED;
59
P1OUT&=~LED_GREEN;
60
break;
61
caseTA0IV_TACCR2:
62
P1OUT|=LED_GREEN;
63
break;
64
caseTA0IV_TAIFG:// debug only
65
// break;
66
default:
67
for(;;)
68
n+=4;
69
}
70
}
Vielleicht hilft dir das Beispiel zum Verstehen der Interrupts.
Danke für den Beispiel-Code! Nach dem Mittagessen hats es jetzt dann
endlich funktioniert! Hatte die Flags in der ISR nicht resetet :-(
1
#include<msp430.h>
2
3
// AnregungsFrequenz 100Hz
4
5
constintmaxPeriod=347;
6
constintminPeriod=307;
7
8
voidmain(void){
9
WDTCTL=WDTPW|WDTHOLD;// Stop watchdog timer
10
P1DIR|=0x01+0x40;// P1.0 + P1.6 als Ausgang
11
P1OUT&=~0x01+0x40;// P1.0 + P1.6 low
12
P1IE|=0x10;// Interrupt bei steigender Flanke an P1.4
13
TA1CTL=TASSEL_1+MC_2;// ACLK + Continuous Mode
14
TA1CCR1=minPeriod;// Compare-Wert setzen
15
TA1CCR2=maxPeriod;// Compare-Wert setzen
16
_BIS_SR(LPM3_bits+GIE);// LPM3 und globale Interrupts aktivieren
17
}
18
19
#pragma vector=PORT1_VECTOR
20
__interruptvoidPort_1(void)
21
{
22
P1IFG&=~0x10;// P1.4 IFG cleared
23
TA1R=0;// Timer reseten
24
if((TA1CCTL1&CCIFG)&&(!(TA1CCTL2&CCIFG))){// Wenn compare mit CCR1 true und CCR2 false, Frequenz unverändert
25
P1OUT|=0x40;
26
P1OUT&=~0x01;
27
}else{
28
P1OUT|=0x01;
29
P1OUT&=~0x40;
30
}
31
TA1CCTL1&=~0x01;
32
TA1CCTL2&=~0x01;
33
}
Ich kann jetzt nur nicht sagen ob diese Variante energiesparender ist
als die Vorherige...Wenn ich den Code aufspiele und extern speiße um zu
messen scheint der µC nicht wirklich zu laufen (LEDs leuchten nicht).
Simon Heller schrieb:> Ich kann jetzt nur nicht sagen ob diese Variante energiesparender ist> als die Vorherige...Wenn ich den Code aufspiele und extern speiße um zu> messen scheint der µC nicht wirklich zu laufen (LEDs leuchten nicht).
47kΩ Widerstand zwischen VCC und Reset vergessen?
Nicht vergessen, die ungenutzten Pins als Input mit Pulldown
(P1REN=~(BIT0|BIT6); P2REN=0xFF; P3REN=0xFF) zu konfigurieren, das spart
ordentlich Strom.
Simon Heller schrieb:> Ich kann jetzt nur nicht sagen ob diese Variante energiesparender ist> als die Vorherige...
Erstelle ein Listfile und zähle die Befehle.
Danke für die Tipps! Auf dem Launchpad ist ja ein 47kOhm Widerstand
zwischen Vcc und RST. Im Users Guide zum Launchpad steht das man für
eine Strommessung am besten alle Jumper bei J3 entfernen soll. Oder
müssen irgendwelche Jumper gesetzt bleiben? Kann das auch was mit dem
Debugging zu tun haben? Wenn ich das Programm über "Build - Release"
uploade sollte der Debugger ja nicht aktiv sein, oder?