Forum: Mikrocontroller und Digitale Elektronik MSP430F1612_2 Timer_2 Zeitbasen


von Mathias U. (munter)


Lesenswert?

Hallo, ich habe wieder mal ein kleineres Problem mit meinem MSP430.

Es ist ein MSP430F1612.
Softwareumgebung: IAR Workbench 3.21A
Hochsprache: C

Ich habe eine Uhrzeit eingebaut, mit den Bibliotheken von TI.
Das geht auch soweit ganz gut. Die eine Sekunde Abweichung alle zwei 
Tage sind verkraftbar!
Für die Uhrzeit nehme ich Timer_A

Die TA_ISR ist hier zu sehen:
1
#include   "msp430x14x.h"
2
            EXTERN  incrementSeconds
3
            PUBLIC  TA_1sec_wake
4
5
            RSEG CODE
6
TA_1sec_wake
7
            mov.w   #32768,&CCR1
8
            mov.w   #CCIE,&CCTL1            ; CCR1 interrupt enabled
9
            mov.w   #TASSEL_1+MC_2,&TACTL   ; ACLK, cont. mode
10
            bis.b   #GIE,  SR
11
            ret
12
;-----------------------------------------------------------------------------
13
TA_ISR
14
;-----------------------------------------------------------------------------
15
            tst.w   &TAIV                   ; read clears flag
16
            call    #incrementSeconds       ; tick one second
17
            add.w   #32768, &CCR1
18
            reti                            ;    
19
20
;-----------------------------------------------------------------------------
21
;           Interrupt Vectors Used MSP430x13x/14x/15x/16x
22
;-----------------------------------------------------------------------------
23
;-----------hier hab ich was ersetzt---------------------------------------
24
            COMMON  INTVEC
25
            ORG     TIMERA1_VECTOR          ; TA Vector
26
            DW      TA_ISR                  ;
27
            END

Ich habe nur das "RSEG INTVEC" durch das "COMMON INTVEC" ersetzt, weil 
jemand mir vor einer Weile hier im Forum sagte, man müsse das so machen.

Ich habe in meinem Programm noch den Timer_B in Benutzung. Weil ich mich 
mit Assambler gar nicht auskenne habe ich mir halt nen anderen Timer 
initialisiert und eine eigene ISR geschrieben.

Der Timer_B soll alle 125ms einen "SystemTick" erzeugen.
Dieser dient als Basis für meine Messungen.

Hier erstmal die Initialisierung und dann die ISR des Timer_B:
1
// ZEITBASIS wird woanders als 32768/8 definiert...
2
// er soll also bis 125ms zählen...
3
TBCCTL0 = CCIE;                  // CCR0 interrupt enabled
4
TBCCR0  = ZEITBASIS;            // wert in owndef, zaehl-zeit bis interrupt
5
TBCTL   = TBSSEL0 + MC0;         // ACLK, upmode
6
_EINT();                         // interrupt an
1
// timer_b interrupt service routine
2
#pragma vector=TIMERB0_VECTOR
3
__interrupt void Timer_B (void)
4
{
5
  if (Tick == (32768 / ZEITBASIS)) Tick = 0;        // timer zurücksetzen
6
  else Tick++;
7
}

Ich habe dann eine Routine im Programm, die alle 125ms durchlaufen wird.
Dann eine, die alle Sekunde durchlaufen wird. Und darauf aufbauend dann 
halt für 5s, 10s...usw.
In diesen Routinen werden Zähler erhöht, damit man nach der jeweiligen 
Zeit in die nächste Routine "rutscht".

Das Problem ist jetzt, dass EINE Minute nicht eine Minute ist, sondern 
schon 2 Sekunden länger dauert! Das ist schlecht, weil ich Messwerte 
über mehrere Stunden brauche. Und der Fehler setzt sich ja fort und wird 
immer größer!
Ich protokolliere die Messwerte, und wenn nach einer "realen" halben 
Stunde die halbe Stunde vom Timer_B schon lange erreicht ist, dann 
bekomme ich keine, oder falsche Messwerte.

Ich denke, es hat irgendwas mit der Initialisierung des Timers zu tun?
Könnte ich die ISR für den Timer_A so abändern, dass ich keinen zweiten 
Timer brauche?
Das Dumme ist halt, dass ich einmal den Sekunden-Takt für die Uhr und 
den 125ms-Tackt für die Messungen brauche.

Hier aber wieder das Problem, dass ich Assembler so rein gar nicht kann.
Aber theoretisch muss man doch nur alle 125 nen Tick erzeugen. Und wenn 
halt 8 Ticks vorbei sind, dann soll die Funktion increment_second() 
aufgerufen werden für die Uhrzeit.

Sieht jemand einen offensichtilichen Fehler in meiner Inititialisierung?

Falls ich irgendwas schlecht ausgedrückt habe, dann sagts mal an. Ich 
kann auch weitere Ausführungen machen, aber dann müsste man etwas weiter 
ausholen.

Es wäre super, wenn mir jemand helfen könnte!
danke

von Stefan (Gast)


Lesenswert?

>weil jemand mir vor einer Weile hier im Forum sagte,
>man müsse das so machen
Jo, das war ich :-)

Wenn ZEITBASIS als 32768/8 definiert ist, warum rechnest Du dann in der 
ISR nochmal 32768/ZEITBASIS? Das ist doch einfach 8 !
Zweitens, warum zählst Du in der TB ISR dieses Tick auf 8 hoch?
8*125ms = 1s, schon klar. Aber die 1s kriegst Du doch schon von TA ???

Also entweder steh' ich auf'm Schlauch... aber erklär mal bitte genauer, 
welcher Timer jetzt ein Problem macht und wo und wann diese ominösen 2s 
auftauchen.

von Christian R. (supachris)


Lesenswert?

Warum benutzt du nicht das TBCCR-Register, da kannst du im Upmode bis 
32767 zählen lassen und dann springt der Zähler automatisch auf Null. Da 
kannst du dann entweder beim CCR = TAR einen Int auslösen lassen, 
oder beim Überlauf, beide kommen dann im richtigen Takt. Da musst du 
nix selber gucken und auf Null setzen.

von Mathias U. (munter)


Lesenswert?

Gut, die Berechnung von 32768/ZEITBASIS kann ich mir natürlich 
sparen...hast recht!

Ich brauche alle 125ms einen Tick, damit dann eine Messung per ADC12 
durchgeführt wird.

Und ich habe halt in der Routine, die alle 125ms aufgerufen wird nen 
Zähler drin, der hochzählt, um dann irgendwann in die 1s-routine zu 
kommen...

Am besten, ich zeig mal nen Code-Schnipsel:
1
// routine zu jedem tick
2
if (Tick != tnext)
3
{
4
   tnext = Tick;
5
   
6
   //dann adc12 wandlung...
7
   //berechnungen...
8
   //aufsummieren der werte
9
   
10
   t1next++;
11
   teiler_1s = t1next;
12
}
13
14
// routine für 1s
15
if (t1next == 8)
16
{   
17
   //berechnungen...(aufsummierte werte / teiler_1s)
18
   
19
   teiler_5s = teiler_5s + teiler_1s;
20
   t5next++;
21
}
22
23
//...dann die routinen für 5s usw...bis 30min

Beim allerersten Durchlauf werden die routinen NICHT durchlaufen,
weil Tick = tnext = 0.
Danach sind sie IMMER unterschiedlich...

Jetzt zum Problem:
Die Uhrzeit mit Timer_A läuft wunderbar!

Ich lasse die Werte für 1s und für 1min auf dem Display anzeigen...
und wenn ich jetzt ne Uhr danebenstelle, dann müsste doch theoretisch
der 1min-Wert alle MINUTE neu aktualisiert werden...
Und genau DAS macht er eben nicht.

Bsp.:
Ich hab nen Wert von 400 für die letzte Minute. Nach einer Minute sollte 
sich dieser Wert auf dem Display verändert haben. Er ändert sich aber 
erst nach 1min 2s (so ungefähr...) und entsprechende kommen die 
30min-Werte auch verzögert an...

@ Christian R. : Ich versteh nicht ganz, was Du meinst. Kannst mir das 
mal bitte
etwas genauer erklären...

Also nochmal zusammenfassend:
Ich brauche einen Interrupt alle 125ms, damit dann jeweils eine Messung 
per ADC12 durchgeführt wird.
Weiterhin brauche ich einen Sekundentakt für die Uhrzeit.
Ob ich jetzt den Sekundentakt für meine Berechnungsroutinen (halt 8* die 
125ms Routine) aus dem Timer_b hole, oder aus dem Timer_a dürfte doch 
egal sein, oder?
danke

von Stefan (Gast)


Lesenswert?

>Ich brauche alle 125ms einen Tick, damit dann eine Messung per ADC12
>durchgeführt wird.
Gut... hast Du mit Timer_B aufgebaut. ISR alle 125ms, in der ISR den ADC 
triggern... fertig!

>Und ich habe halt in der Routine, die alle 125ms aufgerufen wird nen
>Zähler drin, der hochzählt, um dann irgendwann in die 1s-routine zu
>kommen...
Wozu? Ich denke Du hast dafür dem Timer A, der doch anscheinend richtig 
funktioniert!

von Christian R. (supachris)


Lesenswert?

Mathias U. wrote:

> @ Christian R. : Ich versteh nicht ganz, was Du meinst. Kannst mir das
> mal bitte
> etwas genauer erklären...

Ahso, ich nehm alles zurück. Hab nochma genau durchgelesen, und gesehn, 
dass du es ja schon machst. Ich denke der Fehler liegt in der Definition 
von ZEITBASIS. Da hast du 1 zuviel. Es kommt ja bei deiner Rechnung 4096 
raus, in den CCR muss aber 4095, damit du alle 4096 Takte einen Int 
bekommst. Schließlich braucht´s noch einen Takt, um den Timer wieder auf 
0 zu setzen, der springt ja nicht von 4096 auf 1.
Also setze mal ZEITBASIS auf 32768/8 -1 also 4095, dann sollte es 
klappen.

von Mathias U. (munter)


Lesenswert?

Gut, ich würde das jetzt also so machen:

Die ISR für Timer_B so lassen...
Die Routine für alle 125ms bleibt auch so, nur dass ich halt nicht 
hochzähle für die 1s usw. -Routinen...

Ich würde jetzt gerne meine globale Variable "t1next" in der ISR für 
Timer_A auf 1 setzen wollen...
Dann würde jede Sekunde diese Variable auf 1 stehen, und ich könnte sie 
dann in meinen Routinen abfragen...

In der Routine für 1s würde dann am Schluss die Variable wieder auf Null 
gesetzt, also zurückgesetzt...

Dann käme jetzt der Sekundentakt vom Timer_A und nicht über den Umweg 
Timer_B, obwohl das ja auch gehen müsste...

Nur wie mach ich das? Ich kenn mich halt mit Assembler nicht aus? Da 
muss doch dann noch meine my_globals.h eingebunden werden, sonst kennt 
der die Variable t1next ja nicht...

WAS muss ich WO reinschreiben?
Kannst mir das bitte kurz schreiben?
Danke


*edit: @ Christian R. : das probier ich als erstes gleich mal aus...
Dann müsste ich meine ISR für Timer_b auch so abändern???
1
// timer_b interrupt service routine
2
#pragma vector=TIMERB0_VECTOR
3
__interrupt void Timer_B (void)
4
{
5
  if (Tick == ((32768-1)/ZEITBASIS)) Tick = 0;        // timer zurücksetzen
6
  else Tick++;
7
}

von Stefan (Gast)


Lesenswert?

@Christian:
Das bezweifle ich. Korrigiere mich bitte, wenn ich falsch liege:
Bei ZEITBASIS = 4095 kommt der IRQ alle 4096 Takte (4095 + 1 Takt zum 
Nullsetzen des Timers)
Wenn ZEITBASIS  = 4096 ist, dann kommt der IRQ also alle 4097 Takte. Bei 
32768kHz wären das 125,0305176ms (anstatt 125,0ms). Hochgerechnet auf 
eine Minute ergibt sich also 125,0305176ms x 8 x 60 = 60,015s.
Also gerade mal 15ms zuviel. Fehlen also immer noch 1,985s, die irgendwo 
verbraten werden!

von Falk B. (falk)


Lesenswert?

@ Stefan (Gast)

>Also gerade mal 15ms zuviel. Fehlen also immer noch 1,985s, die irgendwo
>verbraten werden!

Hab dein Programm jetzt nicht angeschaut. Kann es ein, dass 
Timer-Interrupts verschluckt werden, weil andere Interrupts (oder der 
Interrupt selber!) länger als 125ms dauern?

MFG
Falk

von Mathias U. (munter)


Lesenswert?

Also ichhabe jetzt mal ((ZEITBASIS/8)-1) geändert --> bringt rein gar 
nichts!
Schade!
Jetzt nochmal zu der Sache, wie ich meine t1next in der ISR von Timer_A 
setzen kann: Wie macht man das in Assembler?

@Falk Brunner: also ich sag mal, dass die ISR zu lange dauern, kann ich 
mir eigentlich nicht vorstellen...da wird ja fast nichst drin gemacht!
In der ISR von Timer_A wird nur die funktion increment_seconds() 
aufgerufen, die von der Uhrzeit aus der Ti-Biblio.

Und in meiner eigenen wird auch nur eine Variable gesetzt...

von Mathias U. (munter)


Lesenswert?

Am allerschicksten wäre es natürlich, wenn ich es irgendwie hinbekäme, 
dass ich meine ISR für den Timer_A so umschreibe, dass sie alle 125ms 
nen Tick erzeugt und nach 8 Ticks (also 1s) die Funktion zum erhöhen der 
Sekunden aufruft. Damit wären zwei Fliegen mit einer Klappe geschlagen!
Dann könnten meine Routinen für die Berechnungen für 1s, und die 
weiteren so bleiben... und ich spare mir die verwendung eines zweiten 
timers.

Kann mir einer erklären, wie ich das machen könnte? (Das Problem ist die 
Assembler-programmierung...)
danke

von Christian R. (supachris)


Lesenswert?

Probier mal, ob die Geschichte genauer wird, wenn du für den Timer B 
auch den Cont-Mode nimmst und in der ISR einfach immer 4096 dann auf das 
TBCCR0 drauf addierst.
Ansosten kannste dir doch das stück TI-Asm auch in C schreiben, da is ja 
keine Hexerei dabei, die ISR von oben bekommt jeder Compiler genauso 
hin.
Für sowas gibts bei TI nen Dokument: Mixing C and Assembler.

Aber die 2 Sek sind schon bissl viel, sehr seltsam.

von Falk B. (falk)


Lesenswert?

@ Mathias U. (munter)

>Am allerschicksten wäre es natürlich, wenn ich es irgendwie hinbekäme,
>dass ich meine ISR für den Timer_A so umschreibe, dass sie alle 125ms
>nen Tick erzeugt und nach 8 Ticks (also 1s) die Funktion zum erhöhen der
>Sekunden aufruft. Damit wären zwei Fliegen mit einer Klappe geschlagen!
>Dann könnten meine Routinen für die Berechnungen für 1s, und die
>weiteren so bleiben... und ich spare mir die verwendung eines zweiten
>timers.

???
Sollte man das nicht sowieso so machen? Wo ist das Problem?

>Kann mir einer erklären, wie ich das machen könnte? (Das Problem ist die
>Assembler-programmierung...)

Warum nicht C? Du hast doch dort oben C-Fragmente drin?.

Mach einen Timer, der nach 125ms tickt. die brühmten 4096 Takte, mit 
4095 als TBCCR0 Wert. Dort wird eine Variable hochgezählt. Ist sie 8, 
wird sie auf 0 zurückgesetzt und gleichzeitig die Routine für die 1s 
Aktion ausgeführt. Besser ist es allerdings, ein Flag zu setzten, 
welches in der Hauptschleife erkannt wird und die entsprechende Routine 
aufruft. Dann werden die Interrupts nicht so lange blockiert.

MFG
Falk

von Mathias U. (munter)


Lesenswert?

Hallo Falk, so wie Du das so schreibst, klingt das total einfach.
Aber es ist leider nicht ganz so einfach für mich, da Assembler ein Buch 
mit min. 7 Siegeln für mich ist...

>Wo ist das Problem?

Die RTC_TA.s43 von TI:
1
#include   "msp430x14x.h"
2
            EXTERN  incrementSeconds
3
            PUBLIC  TA_1sec_wake
4
5
            RSEG CODE
6
TA_1sec_wake
7
            mov.w   #32768,&CCR1
8
            mov.w   #CCIE,&CCTL1            ; CCR1 interrupt enabled
9
            mov.w   #TASSEL_1+MC_2,&TACTL   ; ACLK, cont. mode
10
            bis.b   #GIE,  SR
11
            ret
12
;-----------------------------------------------------------------------------
13
TA_ISR
14
;-----------------------------------------------------------------------------
15
            tst.w   &TAIV                   ; read clears flag
16
            call    #incrementSeconds       ; tick one second
17
            add.w   #32768, &CCR1
18
            reti                            ;    
19
;-----------------------------------------------------------------------------
20
;           Interrupt Vectors Used MSP430x13x/14x/15x/16x
21
;-----------------------------------------------------------------------------
22
            COMMON  INTVEC
23
            ORG     TIMERA1_VECTOR          ; TA Vector
24
            DW      TA_ISR                  ;
25
            END

Die muss jetzt SO in C geschrieben werden, dass auch ich es verstehe...

Also klar ist die Inititialisierung des Timers_A
1
CCR0 = ZEITBASIS; // ZEITBASIS = ((32768/8)-1) = 4095
2
CCTL0= CCIE;
3
TACTL= TASSEL_1 + MC_2; // ACLK, cont.-mode
4
_EINT();
Aber wie sieht jetzt die ISR für Timer_A aus?
1
// timer_a interrupt service routine
2
#pragma vector=TIMERA0_VECTOR
3
__interrupt void Timer_A (void)
4
{
5
  Tick = Tick + 1;
6
  if (Tick = 8)
7
  {
8
     Tick = 0;
9
     incrementSeconds(); // die funktion liegt in assembler vor in der RTC_Calendar.s43 von TI
10
                         // kann man die funktion einfach so aufrufen?
11
  }
12
}
Ich kann mir nicht vorstellen, dass DAS reichen soll und kann! In der 
ISR oben steht ja noch mehr drin!

*ich glaub ich stell mich grad dämlicher an, als nötig...

von Stefan (Gast)


Lesenswert?

>*ich glaub ich stell mich grad dämlicher an, als nötig...
Jepp :-)

>In der ISR oben steht ja noch mehr drin!
TAIV Register auslesen, entfällt, da CCR0-IRQ (und nicht CCR1/2 oder 
TMRA). Und das Addieren von 32768 auf CCR1 kannst Du Dir auch sparen, 
weil die Timerperiode von CCR0 vorgegeben wird!
"reti" macht Dein Compiler für Dich.
Passt schon!

Nur:
>TACTL= TASSEL_1 + MC_2;
MC_1 ist besser. Du willst ja, dass der Timer nur bis CCR0 zählt und 
dann wieder von 0 anfängt!

von Falk B. (falk)


Lesenswert?

@ Mathias U. (munter)

>Hallo Falk, so wie Du das so schreibst, klingt das total einfach.
>Aber es ist leider nicht ganz so einfach für mich, da Assembler ein Buch
>mit min. 7 Siegeln für mich ist...

Warum programmierst du dann in Assembler? C ist hier vollkommen OK und 
genauso schnell.

>Wo ist das Problem?

>Ich kann mir nicht vorstellen, dass DAS reichen soll und kann! In der

Warum nicht? Es wird alles gemacht, was zu tun ist? Ob man die Funktion 
so aufrufen kann weiss ich nicht, dazu kenn ich mich zu wenig mit ASM/C 
Mix auf dem Compiler aus.

>ISR oben steht ja noch mehr drin!

Nöö. Lies mal die Kommentare.

>            tst.w   &TAIV                   ; read clears flag

Macht AFAIK der C-Compiler für dich

>            call    #incrementSeconds       ; tick one second

Normaler Funktionsaufruf, wie in C

>            add.w   #32768, &CCR1

Comparewert um 32768 Takte erhöhen = 1 Sekunde.

>            reti                            ;

Feierabend

>*ich glaub ich stell mich grad dämlicher an, als nötig...

Maybe. ;-)

MfG
Falk

von Christian R. (supachris)


Lesenswert?

Und wieso willst du unbedingt von CCR1 auf CCR0 wechseln? Probier doch 
erst mal, den Code für die ISR rein von ASM in C zu portieren. Das TAIV 
musst du abfragen, macht der Compiler nicht.
Wenn da geht, sehen wir weiter. Nicht immer 2 Schritte auf einmal 
versuchen ;)

von Mathias U. (munter)


Lesenswert?

Ich das mal so getestet, wie oben angegeben probiert, aber es geht 
trotzdem nicht wie gewünscht!
Die Uhrzeit läuft, soweit ich das bis jetzt sagen kann, recht genau, 
aber die Messroutinen dauern länger, als sie eigentlich sollten!
D.h. z.b. die Messroutine für 1min wird nach ca. 1min 2s wiederholt.
Das die Uhrzeit geht ist für mich auch logisch, weil in der ISR die 
Funktion zu erhöhen der Sekunden drin steht! Die hat also eine ziemlich 
hohe Priorität.

@ Christian R. : Ich hatte es auch mal mit CCR1 und CCTL1 probiert, aber 
da fängt die Uhrzeit gar nicht an zu laufen...
Wo muss ich den TAIV abfragen? Und was mach ich dann damit?

danke

von Falk B. (falk)


Lesenswert?

@ Mathias U. (munter)

>aber die Messroutinen dauern länger, als sie eigentlich sollten!
>D.h. z.b. die Messroutine für 1min wird nach ca. 1min 2s wiederholt.

Das ist ein Widerspruch. Das eine ist die Länge der Routine, das 
andere die Wiederholzeit. Was macht die Routine denn? Wie lange dauert 
das?

Poste mal vollständigen Quelltext. Als Anhang.

>Wo muss ich den TAIV abfragen? Und was mach ich dann damit?

Um das Flag zu löschen. Kommentare und das Datenblatt sollte mans chon 
ab und zu mal lesen, egal wie fir man in Assembler ist . . .

MFG
Falk

von Mathias U. (munter)


Angehängte Dateien:

Lesenswert?

Oh Mist, jetzt muss ich wohl die Hosen runter lassen...

Ich hab Dir mal das ganze Projekt gepackt drangehangen.

--> VORSICHT! Die main() ist seeehr lang!
--> Ich weiß, dass da so einiges nicht ganz optimal ist und schön ist 
der Code schon gar nicht!
--> IAR Workbench 3.21A

In den einzelnen Messroutinen werden berechnungen gemacht, die lange 
dauern könnten (log10...). Ich tippe daruaf, dass er da irgendwie aus 
dem Tackt kommt.

@Falk: Ich finds großartig, dass Du den Leuten hier so hilfts!
Lese viel mit, und Dein Name taucht oft auf! THUMBS UP!!!

von Christian R. (supachris)


Lesenswert?

Du musst den TAIV Vektor abfragen, weil sich da mehrere Ereignisse des 
Timer A einen Interrupt-Vektor teilen.
Was hindert dich, die Beispiele von TI mal anzugucken?

von Stefan (Gast)


Lesenswert?

>VORSICHT! Die main() ist seeehr lang!
wohl war!
Vielleicht hab ich's genau deshalb übersehen...?
Wo ist die Init für die Basic clock settings?
Ohne Änderungen 'dümpelt' der MSP mit ca. 800kHz vor sich hin.
Stell doch mal Deinen DCO auf Maximum (weiß jetzt nicht Auswendig, was 
der F1612 kann). Ich denke nämlich auch, dass Du ein Zeitproblem in 
Deinen unendlichen Funktionen hast, und gar nicht vor dem nächsten IRQ 
damit fertig wirst!

von Falk B. (falk)


Lesenswert?

Sooooooo.

Naja, auch wenn du dich schon in Vorfeld entschuldigt hast.

- Schon mal was von Funktionen gehört? Macht den Code WESENTLICH 
lesbarer und wartbarer. Also bitte mal die einzelnen Funktionen für die 
einzelnen Messungen/Blöcke auslagern.
- Deine wait Funktion sieht komisch aus. Das _NOP()) soll doch 
garantiert IN die for Schleife, oder?
- Warum schreibst du die ISR wieder falsch hin?

1
// timer_a interrupt service routine
2
#pragma vector=TIMERA0_VECTOR
3
__interrupt void Timer_A (void)
4
{
5
  if (Tick == 8) {Tick = 0; incrementSeconds();}
6
  else Tick = Tick + 1;
7
}

Damit wird dein incrementSeconds() alle NEUN Ticks aufgerufen! 
0,1,2,3,4,5,6,7,8,0,1, !
Einfach so

1
// timer_a interrupt service routine
2
#pragma vector=TIMERA0_VECTOR
3
__interrupt void Timer_A (void)
4
5
{
6
  if (Tick == 7) {
7
    Tick = 0;
8
    incrementSeconds();
9
  }
10
  else Tick++;
11
}

Formatierung ist WICHTIG! Durchblick!

- Wie lange dauert  incrementSeconds() ? Wenn es länger als 125ms dauert 
verlierst du Interrupts und deine Zeitbasis ist im Eimer.
- Eigene Datentypen definieren ist schlecht bis sinnlos. Nutze stdint 
mit uint8_t etc. Ist genormt, getestet und portabel.

Also, um die Sache mal in trockene Tücher zu bringen ohne nur an 
Symtomen rumzudoktern, mein Vorschlag.

- Bring sämliche Blöcke, die du schön über if(tnexte) etc. anspringst in 
eigene Funktionen, die dann in der main-Endlosschleife aufgerufen 
werden. Vor allem den Monster-SWITCH!
- Die Main-Hauptschliefe sollte dann auf 1-2 Seiten locker passen, 
bestehend aus wenigen IFs und vielen Funktionsaufrufen.
- loesche_buffer wird sinnloserweise zweimal aufgerufen. Wahrscheinlich 
ist sie sowieso überflüssig, weil ja der Buffer immer mit neuen Daten 
überschrieben wird.
- Die Funktionen thematisch geordnet in Dateien auslagern. Nicht alles 
in eine main.c!
- Deine Zeit-und Ablaufsteuerung scheint mir nicht ganz koscher. Z.B. 
auf t1next machst du sieben! Schreibzugriffe in den unterschiedlichen 
Blöcken. Ich behaupte mal, dass sich da irgendwas verhaspelt. Es darf 
nur EINEN Schreibzugriff zum setzen und EINEN Schreibzugriff zum 
Löschen geben, weil das ja über die Interrupts läuft.

Etwa so

1
while (1) {   // Endlose Main-Schleife
2
  
3
  if (tnext) {      // hier eine Fehlerausgabe rein, z.B. Port auf HIGH setzen 
4
                    // Wenn tnext schon aktiv ist, dann ist die Main-Schleife zu langsam
5
  }
6
7
  while (!tnext);       // warte auf nächsten 125ms Tick
8
9
  tnext=0;
10
  // alle 125 ms Funktion hier
11
12
  if (t1next) {
13
    t1next=0;
14
    // alle 1s Funktion hier
15
  }
16
  
17
  // etc.
18
}

- Dann kannst du wunderbar deine kleine main-Schleife strukturieren, und 
jeder (auch DU) sieht sofort, welche Funktionen alle 125ms, alle 1s und 
alle 5s etc. aufgerufen werden.
- Deine Zeitsteurung ist wie bereits gesagt ziemlich verquer. Mach es 
so.
1
// timer_a interrupt service routine
2
#pragma vector=TIMERA0_VECTOR
3
__interrupt void Timer_A (void)
4
5
{
6
  static uint8_t cnt_sec=0;
7
  static uint8_t cnt_5sec=0;
8
  static uint8_t cnt_20sec=0;
9
  static uint8_t cnt_30sec=0;
10
  static uint8_t cnt_40sec=0;
11
  static uint8_t cnt_50sec=0;
12
  static uint8_t cnt_1m=0;
13
  static uint8_t cnt_30m=0;
14
15
  tnext=1;                  // 1/8 Sekunde, Timerperiode
16
  if (Tick == 7) {          // eine Sekunde
17
    Tick = 0;
18
    t1next=1;
19
    incrementSeconds();
20
    cnt_sec++;
21
    if (cnt_sec==5) {       // fünf Sekunden
22
      cnt_sec=0;
23
      t5next=1;
24
      cnt_5sec++;
25
      if (cnt_5sec==2) {     // zehn Sekunden
26
        cnt_5sec=0;
27
        t10next=1;
28
        cnt_20sec++;
29
        cnt_30sec++;
30
        cnt_40sec++;
31
        cnt_50sec++;
32
        cnt_1m++;
33
        cnt_30m++;
34
35
        if (cnt_20sec==2) {  // zwanzig Sekunden
36
          cnt_20sec=0;
37
          t20next=1;        
38
        }
39
        if (cnt_30sec==3) {  // dreissig Sekunden
40
          cnt_30sec=0;
41
          t30next=1;        
42
        }
43
        if (cnt_40sec==4) {  // vierzig Sekunden
44
          cnt_40sec=0;
45
          t40next=1;        
46
        }
47
        if (cnt_50sec==5) {  // fünfzig Sekunden
48
          cnt_50sec=0;
49
          t50next=1;        
50
        }
51
        if (cnt_1m==6) {     // 1 Minute
52
          cnt_1m=0;
53
          t1mnext=1;        
54
        }
55
        if (cnt_30m==180) {  // Dreissig Minuten
56
          cnt_30m=0;
57
          t30mnext=1;       
58
        }
59
      }
60
    }
61
    
62
  }
63
  else Tick++;
64
}

- Da in vielen Funktionen recht viel Mathematik drinsteckt, musst du 
prüfen, wie lange die jeweiligen Funktionen dauern. Dazu setzt du vor 
dem Funktionsaufruf ein Portpin auf HIGH und direkt danach auf LOW. Das 
kann man wunderbar mit dem Oszi messen. Denn deine 125ms Funktionen 
dürfen zusammen nicht mehr als 125ms verbrauchen! Das Gleiche gilt für 
die 1s und 5s Funktionen, denn sonst werden Ticks verpennt bzw. zu spät 
aufgerufen! Sprich, deine Main-Schleife darf worst Case max. 125ms 
dauern! Ein kleiner Check ist oben im Beispiel schon eingebaut.

Bring erstmal Ordnung in deinen Code und poste nochmal. Dann sehen wir 
weiter.

MFG
Falk

von Mathias U. (munter)


Lesenswert?

Guten Morgen.

*edit: problem hat sich von selbst gelöst...

@Falk: Deine Kritik ist völlig berechtigt! Aber ich komme nicht von der 
Programmierschiene, bin also noch im Lernprozess ;-)
Ich werde aber, sovile wie möglich von Deinen Hinweisen versuchen 
einzuarbeiten. Danke
MFG
mathias

von Stefan (Gast)


Lesenswert?

Also der Hinweis mit den stdint-Typen ist zwar in Ordnung.
Meines Erachtens ist aber die Definition von BYTE, WORD, ... jetzt auch 
nicht sooooo schlimm!
Die stdint.h steht in der DLIB, das bedeutet, dass Du Dein Projekt in 
IAR von C auf C++ umstellen musst. Das jetzt aber nur wegen den 
Typdefinitionen zu tun, halte ich für übertrieben!

Was ist nun eigentlich mit der Taktrate?
Den DCO mal hochgeschraubt?

von Falk B. (falk)


Lesenswert?

@ Mathias U. (munter)

>*edit: problem hat sich von selbst gelöst...

Und wie?

MFg
Falk

von Christian R. (supachris)


Lesenswert?

Würde mich auch interessieren

von Mathias U. (munter)


Lesenswert?

Nee, ich ahtte ein riesiges Prob mit der Einbindung der stdint.h...
Das ging nicht so richtig.
Jetzt hab ich die <inttypes.h> eingebunden, und die Datentypen kann ich 
jetzt so nehmen, wie Du angegeben hast, Falk. Also uint8_t usw..

@ Stefan: Ich hab echt ne ganze Weile rumprobiert, aber irgendwie hatte 
ich immer nen Fehler mit der stdint.h...hatte auch mal DLIB eingestellt, 
da hat er mir dann andere Fehler gebracht...

Jetzt werd ich mal schauen, wie ich den DCO hochsetze. Wo kann man das 
machen? Ich hab da noch nichst verändert. Evtl. reicht das ja schon. 
*hoffe. Ansonsten werde ich dann Falks Vorschlag folgen und meinen Code 
erstmal *übersichtlicher machen...
danke

von Stefan (Gast)


Lesenswert?

>und meinen Code erstmal *übersichtlicher machen...
Das solltest Du so oder so tun!

>hatte auch mal DLIB eingestellt,
>da hat er mir dann andere Fehler gebracht...
Naja, wie ich schon sagte: Nur wegen Datentypen auf C++ zu wechseln 
macht keinen Sinn! Ausserdem sind Typdefinitionen wie BYTE, WORD, DWORD 
nicht falsch! Wer z.B. aus der Visual Studio Ecke kommt, kennt diese 
Typen auch.

>Jetzt werd ich mal schauen, wie ich den DCO hochsetze.
User Guide -> Basic Clock Module (Register: DCOCTL, BCSCTL1, BCDCTL2)

von Falk B. (falk)


Lesenswert?

@ Mathias U. (munter)

>Jetzt werd ich mal schauen, wie ich den DCO hochsetze. Wo kann man das
>machen?

In mcuinit(). Gibst in den TI-Codebeispielen.

> Ich hab da noch nichst verändert. Evtl. reicht das ja schon.
>*hoffe.

Naja, beiss mal lieber ind e "sauren Apfel" und bring dein Code in 
Ordnung. Hat viele Vorteile:

- Du lernst was
- Du hast WIRKLICh Überblick über deine Main-Schleife
- Dein Program wird sicherer

und praktisch keine Nachteile:

- dauert halt ein paar Stündchen.

Die Zeit ist sehr gut investiert.

MFG
Falk

von Mathias U. (munter)


Lesenswert?

Diese ganzen Register sind echt recht tricky...
Da ich ja an XT2 keinen Quarz dran habe, kann ich die Quelle ja auch 
ausschalten, oder?
Ich habe nur an XT1 den 32kHz Quarz dran.

Wenn ich jetzt im DCOCTL DCOx auf 111, also 7 setzen, und im BCSCTL1 
RSELx auch auf 111 setzen, dann müsste doch der DCO mit der max. 
möglichen Frequenz laufen, oder? Laut User-Guide Seite 4-7 müssten das 
dann ca. 8MHz sein.
Aber im Datenblatt zum F1612 steht auf Seite 36 für
RSEL = 7, DCO = 7, MOD = 0 und DCOR = 0; bei 3V knapp 5MHz.
Ja, was denn nun?

Meint Ihr, dass könnte SO gehen?
Vcc ist bei mir übrigens 3,3V.
1
DCOCTL = DCO0 + DCO1 + DCO2;                    // Max DCO
2
BCSCTL1 = XT2OFF + RSEL0 + RSEL1 + RSEL2;       // XT2off, max RSEL

In einem Code-Bsp. haben sie noch
1
BCSCTL2 |= SELS;                          // SMCLK = XT2
gemacht, aber da ich ja XT2 nicht benutze, brauch ich das doch nicht, 
oder?

von Stefan (Gast)


Lesenswert?

>Wenn ich jetzt im DCOCTL DCOx auf 111, also 7 setzen, und im BCSCTL1
>RSELx auch auf 111 setzen, dann müsste doch der DCO mit der max.
>möglichen Frequenz laufen, oder? Laut User-Guide Seite 4-7 müssten das
>dann ca. 8MHz sein.
max. Frequenz: JA
8MHz: NEIN, es sind max 5,4MHz (s. Datenblatt)
Der F1612 kann mit max. 8MHz betrieben werden, die müssen dann aber von 
einem externen Quarz oder Clock kommen. Der DCO kann in dem Fall nur die 
5,4MHz

>Meint Ihr, dass könnte SO gehen?
>Vcc ist bei mir übrigens 3,3V.
1
>DCOCTL = DCO0 + DCO1 + DCO2;                    // Max DCO
2
>BCSCTL1 = XT2OFF + RSEL0 + RSEL1 + RSEL2;       // XT2off, max RSEL
Ja, genau so!

>In einem Code-Bsp. haben sie noch
1
>BCSCTL2 |= SELS;                          // SMCLK = XT2
Solltest Du in Deinem Fall auch nicht machen, da sonst SMCLK nicht 
getaktet wird, da ja kein XT2-Clock vorhanden ist.

von Christian R. (supachris)


Lesenswert?

Benutz doch diese Funktion:
1
//------------------------------------------------------------------------------
2
void Set_DCO (void)                         // Set DCO to selected frequency
3
//------------------------------------------------------------------------------
4
{
5
6
#define DELTA 125              // target DCO = DELTA*(32768) = 4096000
7
  unsigned int Compare, Oldcapture = 0;
8
  
9
  CCTL2 = CM_1 + CCIS_1 + CAP;              // CAP, ACLK
10
  TACTL = TASSEL_2 + MC_2 + TACLR;          // SMCLK, cont-mode, clear
11
12
  while (1)
13
  {
14
    while (!(CCIFG & CCTL2));               // Wait until capture occured
15
    CCTL2 &= ~CCIFG;                        // Capture occured, clear flag
16
    Compare = CCR2;                         // Get current captured SMCLK
17
    Compare = Compare - Oldcapture;         // SMCLK difference
18
    Oldcapture = CCR2;                      // Save current captured SMCLK
19
20
    if (DELTA == Compare) break;            // If equal, leave "while(1)"
21
    else if (DELTA < Compare)               // DCO is too fast, slow it down
22
    {
23
      DCOCTL--;
24
      if (DCOCTL == 0xFF)
25
      {
26
        if (!(BCSCTL1 == (XT2OFF)))
27
        BCSCTL1--;                          // Did DCO roll under?, Sel lower RSEL
28
      }
29
    }
30
    else
31
    {
32
      DCOCTL++;
33
      if (DCOCTL == 0x00)
34
        {
35
          if (!(BCSCTL1 == (XT2OFF + 0x07)))
36
          BCSCTL1++;                        // Did DCO roll over? Sel higher RSEL
37
        }
38
    }
39
  }
40
  CCTL2 = 0;                                // Stop CCR2
41
  TACTL = 0;                                // Stop Timer_A
42
}
Die setzt die DCO-Frequenz auf ein ganzzahliges Vielfaches (DELTA) der 
32,768khz.

von Stefan (Gast)


Lesenswert?

@Christian:
Jetzt immer langsam... ;-)
Er hat (vermutlich) ein Zeitproblem. Deshalb sollte er den max. DCO-Takt 
verwenden, um seine Berechnungen schneller durchzuführen. Das hat er 
gemacht (einfach, übersichtlich, schnell)
Aber er braucht keine Synchronisation des DCO auf den Uhrenquarz!

von Mathias U. (munter)


Lesenswert?

Stefan wrote:
> @Christian:
> Jetzt immer langsam... ;-)

hehe...überfordert mich mal nicht. Immer langsam mit den Anfängern...

*edit: übrigens läuft jetzt meine "mess-minute" auch in etwa wie meine 
"reale" minute...
Werde jetzt mal Tests durchführen, aber der Tipp mit dem DCO hat erstmal 
weitergeholfen...

(Den Code werde ich aber trotzdem noch übersichtlicher machen...; 
jedenfalls nen bissl)

von Mathias U. (munter)


Lesenswert?

Hmm...jetzt läuft die Uhrzeit langsamer als die reale Zeit...hab ne 
Funkuhr daneben gestellt...und man merkt es deutlich! in 5minuten schon 
20s...

von Falk B. (falk)


Lesenswert?

@ Mathias U. (munter)

>Da ich ja an XT2 keinen Quarz dran habe, kann ich die Quelle ja auch
>ausschalten, oder?

Ja.

>möglichen Frequenz laufen, oder? Laut User-Guide Seite 4-7 müssten das
>dann ca. 8MHz sein.

Kann sein.

>Ja, was denn nun?

???

>In einem Code-Bsp. haben sie noch
>BCSCTL2 |= SELS;                          // SMCLK = XT2
>gemacht, aber da ich ja XT2 nicht benutze, brauch ich das doch nicht,
>oder?

Nein. Dann ist ja dein SMCLK tot. Stell den auf DCO. Bzw. Lass es wie es 
ist.

"After a PUC, MCLK and SMCLK are sourced from DCOCLK at ~800 kHz (see
device-specific datasheet for parameters) and ACLK is sourced from LFXT1
in LF mode."

Das willst du ja und hast du schon. Du must nur den DCO hochtakten, 
fertig.

Ich sag mal, Rsel=7 und DCO=4.

MFG
Falk

von Falk B. (falk)


Lesenswert?

@ Mathias U. (munter)

>(Den Code werde ich aber trotzdem noch übersichtlicher machen...;
>jedenfalls nen bissl)

Kein halben Sachen! Ein Benzintank der zu 99% dicht ist ist auch 
unbrauchbar!

MFG
Falk

von Christian R. (supachris)


Lesenswert?

Hehe, DCO-Max geht natürlich auch, aber spätestens, wenn man eine UART 
bedienen will, ist das ungünstig. Weiß ja nicht, was er mit seinen 
Messwerten da anstellt.

Ich würd ma sagen, du misst mal mit dem Oszilloskop nach, wie lange 
deine Routinen dauern. Einfach am Anfang einen Pin setzen und am Ende 
zurück setzen. Nur so kommst du dem Übeltäter auf die Schliche.

Wie siehts mit der Optimierung aus? Steht die auf höchster Stufe?

von Mathias U. (munter)


Lesenswert?

>In einem Code-Bsp. haben sie noch
>BCSCTL2 |= SELS;                          // SMCLK = XT2
>gemacht, aber da ich ja XT2 nicht benutze, brauch ich das doch nicht, oder?

>Nein. Dann ist ja dein SMCLK tot. Stell den auf DCO. Bzw. Lass es wie es >ist.

Du meinst JA, ich brauche diese Zeile nicht?!
Ich hab jetzt nur
1
DCOCTL = DCO0 + DCO1 + DCO2;                    // Max DCO
2
BCSCTL1 = XT2OFF + RSEL0 + RSEL1 + RSEL2;           // XT2off, max RSEL
drin.

von Falk B. (falk)


Lesenswert?

@ Mathias U. (munter)

>Du meinst JA, ich brauche diese Zeile nicht?!

Ja, diese Zeile ist für dich Gift.

>DCOCTL = DCO0 + DCO1 + DCO2;                    // Max DCO
>BCSCTL1 = XT2OFF + RSEL0 + RSEL1 + RSEL2;           // XT2off, max RSEL

KDF? Würde ich nicht machen. Es gibt Berichte, nachdem für maximalen 
Takt der Controller mit 3.6V betrieben werden muss. Mach mal lieber

DCOCTL = DCO2;                               // DCO=4
BCSCTL1 = XT2OFF + RSEL0 + RSEL1 + RSEL2;           // XT2off, RSEL=7

Das reicht locker.

MfG
Falk

von Christian R. (supachris)


Lesenswert?

Falk Brunner wrote:
> @ Mathias U. (munter)

> KDF? Würde ich nicht machen. Es gibt Berichte, nachdem für maximalen
> Takt der Controller mit 3.6V betrieben werden muss. Mach mal lieber

Der maximale DCO-Takt (mit internem R) ist nicht der maximale 
CPU-Takt. Die CPU kann maximal 8MHz, da muss aber dann 3,6V sein, 
externer Quarz oder der DCO mit dem externen R. Der DCO mit internem 
Widerstand macht bei maximal-Einstellung etwa 5,4MHz. Bis 7,3MHz geht 
mit 3,3V laut Diagramm im Datenblatt.

P.S. Leider funktioniert der MSP430F1611 nicht zuverlässig mit 7,3728MHz 
Baudratenquarz und 3,3V.

P.P.S.: Wieso bindest du eigentlich die msp430x14x.h ein, und nicht die 
msp430x16x.h?

von Stefan (Gast)


Lesenswert?

>P.S. Leider funktioniert der MSP430F1611 nicht zuverlässig mit 7,3728MHz
>Baudratenquarz und 3,3V.
Nicht jedes Projekt benötigt einen UART ;-)

>P.P.S.: Wieso bindest du eigentlich die msp430x14x.h ein, und nicht die
>msp430x16x.h?
Hä? Er nimmt doch msp430x16x.h

von Mathias U. (munter)


Lesenswert?

So, jetzt geht auch die Uhrzeit wieder richtig...hatte in meiner ISR für 
den Timer_A noch die 8 drin, statt der 7, die richtig ist.
1
#pragma vector=TIMERA0_VECTOR
2
__interrupt void Timer_A (void)
3
{
4
  if (Tick == 7)
5
  {
6
    Tick = 0;
7
    incrementSeconds();
8
  }
9
  else
10
  Tick++;
11
}

Ich benutze aber nen UART im SPI mode...

Ich habe einen PGA am MSP, der scheint aber mit meinen Einstellungen zu 
gehen. Ich kann per Taster die Verstärkung des PGA wie gewünscht in 
0,5dB schritten verändern...

Desweiteren habe ich eine SD-Card dran auf der ich die gemessenen 
Minutenwerte protokolliere.
Auch das scheint auf den ersten Blick zu funktionieren! Jedenfalls 
schreibt er mir die gewünschten Daten drauf...

Könnte das trotzdem noch zu Problemen führen? Weil die Taktfrequenzen 
für den SPI sind ja jetzt sicher auch höher, als sie vorher waren...

Die init für den UART0 (da hängt der PGA dran):
1
U0CTL  |= CHAR + SYNC + MM + SWRST;               // 8-bit, SPI, master, sw-reset enable
2
  U0TCTL |= CKPL + SSEL1 + STC;                     // Polarity, SMCLK, 3-wire
3
  U0BR0   = 0x002;                                  // Baud Rate Calc SPICLK = SMCLK/2 400KHz
4
  U0BR1   = 0x000;                                  // Baud Rate Calc
5
  U0MCTL  = 0x000;                                  // Modulation Control Register
6
  ME1    |= USPIE0;                                 // Module enable
7
  U0CTL  &= ~SWRST;                                 // SPI enable
Und hier die init für die SD-Card (der Code kommt von TI)
1
UCTL1 = SWRST;                            // 8-bit SPI Master **SWRST**
2
  UTCTL1 = CKPH | SSEL1 | SSEL0 | STC;      // SMCLK, 3-pin mode, clock idle low, data valid on rising edge, UCLK delayed
3
  UBR01 = 0x02;                             // 0x02: UCLK/2 (4 MHz), works also with 3 and 4
4
  UBR11 = 0x00;                             // -"-
5
  UMCTL1 = 0x00;                            // no modulation
6
  UCTL1 = CHAR | SYNC | MM | SWRST;         // 8-bit SPI Master **SWRST**
7
  UCTL1 &= ~SWRST;                          // clear SWRST
8
  ME2 |= USPIE1;                            // Enable USART1 SPI mode
9
  while (!(IFG2 & UTXIFG1));                // USART1 TX buffer ready (empty)?
Bei beiden Initialisierungen wird ja der SMCLK genommen. Dieser wird 
aber durch den DCO beeinflusst, der ja jetzt höher ist.
Sollte da jetzt noch was geändert werden,oder kann ich es so lassen?
Da es ja zu gehen scheint...

von Christian R. (supachris)


Lesenswert?

Stefan wrote:
>>P.P.S.: Wieso bindest du eigentlich die msp430x14x.h ein, und nicht die
>>msp430x16x.h?
> Hä? Er nimmt doch msp430x16x.h

In seinem allerersten Post steht in dem TI-Code noch 14x.h, aber 
vielleicht hat er´s jetzt rausgenommen, weil er das auf C umgeschrieben 
hat. Hab auch langsam den Überblick verloren...sorry, wenn´s falsch war.

Aber nun scheint ja alles zu passen. Was ne schwere Geburt ;)

von Stefan (Gast)


Lesenswert?

>Bei beiden Initialisierungen wird ja der SMCLK genommen. Dieser wird
>aber durch den DCO beeinflusst, der ja jetzt höher ist.
>Sollte da jetzt noch was geändert werden,oder kann ich es so lassen?


Da Du beide USART's im SPI-Master Mode betreibst, ist die Taktfrequenz 
(relativ) egal, denn der Master (also Dein MSP) gibt den Takt vor und 
der Slave muss sich danach richten. Es könnte nur Probleme geben, wenn 
Dein Master-Takt zu hoch für Deine Slaves ist.
Aber da es anscheinend funktioniert... :-)

von Stefan (Gast)


Lesenswert?

>Aber nun scheint ja alles zu passen. Was ne schwere Geburt ;)
Jipppiehhhaijeeeeeh!
wer schickt jetzt den Schampus reihum ? :-)

von Mathias U. (munter)


Lesenswert?

Ich bin NOCH am testen...aber BIS JETZT gehts wie gewünscht...
Die Geburt war wirklich schwer...
Aber das gute ist, ich hab wieder nen bissl was gelernt!
danke

von Stefan (Gast)


Lesenswert?

>Ich bin NOCH am testen
Du vielleicht schon... wir haben fertig ;-)

von Falk B. (falk)


Lesenswert?

@ Mathias U. (munter)

>Ich bin NOCH am testen...aber BIS JETZT gehts wie gewünscht...

Hast du mal die Zeiten gemessen? Programm aufgeräumt?

MFG
Falk

von Mathias U. (munter)


Lesenswert?

Hab ich jetzt noch nicht gemacht, aber ich werde morgen mal messen, wie 
lange die Routinen für 125ms, 1s usw so brauchen...
Und dann werd ich auf jeden Fall nochmal die ganzen Funktionen 
auslagern.
Die beiden riesigen Switch-Case-Routinen kann man auch in eine Funktion 
auslagern?
Werd ich mal probieren...
Quasi so?
1
void tastensteuerung (void)
2
{
3
switch (Seite)
4
   {
5
// seite 0 anzeige 1s-messwerte
6
      case 0:
7
      ...
8
      break;  
9
// seite 1 anzeige 5s messwerte
10
      case 1:
11
      ...
12
      break;
13
// seite 2 anzeige 30min messwerte
14
      case 2:
15
      ...
16
      usw.
17
   }
18
}

von Falk B. (falk)


Lesenswert?

@ Mathias U. (munter)

>Die beiden riesigen Switch-Case-Routinen kann man auch in eine Funktion
>auslagern?
>Werd ich mal probieren...
>Quasi so?

Ja, du machst ja alles über globale Variablen (naja), da geht das. Aber 
auch Parameter für Funktionen sind nicht erst gestern erfunden worden . 
. .

MFG
Falk

von Mathias U. (munter)


Lesenswert?

Lokale Variablen sind mir schon ein Begriff, aber warum auch immer kann 
ich mir den Wert von Lokalen Variablen NICHT im Debugger anschauen...
Als ich die Variablen global deklariert hatte, ging es. Keine ahnung 
warum...

Als ich z.B. gemacht habe:
1
void main(void)
2
{
3
4
int a = 3;
5
// hier dann nen breakpoint
6
int b = 27; 
7
}
Dann hab ich den Debugger bis zum Breakpoint laufen lassen...
Und als ich mir dann a anschauen wollte, dann war das "undefined".

von Christian R. (supachris)


Lesenswert?

Mathias U. wrote:
> Lokale Variablen sind mir schon ein Begriff, aber warum auch immer kann
> ich mir den Wert von Lokalen Variablen NICHT im Debugger anschauen...
> Als ich die Variablen global deklariert hatte, ging es. Keine ahnung
> warum...

Weil lokale Variablen (normalerweise) in den Arbeitsregistern angelegt 
werden und nicht im RAM.

von Stefan (Gast)


Lesenswert?

@Mathias:
>Als ich z.B. gemacht habe:
1
>void main(void)
2
>{
3
>int a = 3;
4
>// hier dann nen breakpoint
5
>int b = 27; 
6
>}
>Dann hab ich den Debugger bis zum Breakpoint laufen lassen...
>Und als ich mir dann a anschauen wollte, dann war das "undefined".

Ich hab Dein Besipiel mal in IAR 3.42A (Kickstart) ausprobiert. Beim 
Breakpoint angekommen, kann ich 'a' im Watchfenster sehen, 'b' ist noch 
'undefined' (klar, weil noch nicht angelegt).
Es gibt allerdings auch ein Window 'Locals', da werden die zur Zeit 
aktiven lokalen Variablen automatisch angezeigt. Probier mal das.

Allerdings glaube ich eher, dass Dein Compiler (zumindest in dem 
Beispiel oben) die Variablen einfach wegoptimiert, da sie nicht wirklich 
benutzt werden!

@Christian.
>Weil lokale Variablen (normalerweise) in den Arbeitsregistern angelegt
>werden und nicht im RAM.
Das kann sein, weiß ich nicht genau.
Aber das sollte dem Benutzer eigentlich egal sein, ob Variablen in 
Registern oder auf'm Stack liegen. Egal wo, ich sollte die Variablen 
sehen können, ansonsten ist das Debuggen (fast) sinnlos.

von Christian R. (supachris)


Lesenswert?

Stefan wrote:
>
> @Christian.
>>Weil lokale Variablen (normalerweise) in den Arbeitsregistern angelegt
>>werden und nicht im RAM.
> Das kann sein, weiß ich nicht genau.
> Aber das sollte dem Benutzer eigentlich egal sein, ob Variablen in
> Registern oder auf'm Stack liegen. Egal wo, ich sollte die Variablen
> sehen können, ansonsten ist das Debuggen (fast) sinnlos.

Theoretisch ja, aber je nach Optimierungsgrad und "Intelligenz" des 
Debuggers öfters mal nicht.....

von Mathias U. (munter)


Lesenswert?

Guten Morgen,

das Beispiel oben ist völlig frei erfunden...mir ging es nur darum diese 
komische Sache zu beschreiben...
Mit dem Fenster für lokale Variablen könnte ich mal probieren...ist aber 
auch eigentlich nicht so wichtig!

Ich bin gerade dabei, meine main() aufzuräumen...

Ich komme mit den ganzen #includes nicht klar.
bsp.:
Ich habe eine my_globals.h in der stehen haufenweise globale Variablen 
drin. (ob das jetzt gut oder schlecht ist, sei mal dahingestellt)
Diese füge ich per #include am Anfang in meine main.c ein.
Dann habe ich eine init_mcu.h in der der Prototyp der Funktion 
init_mcu() drin steht.

Ich dachte eigentlich, dass wenn ich in meiner main.c NACH dem #include 
von my_globals.h die init_mcu.h einbinde, dann kennt meine init_mcu.c, 
in der die Funktion init_mcu() definiert ist, meine globalen 
Variablen...
Dies ist aber nicht der Fall. Ich muss in meiner init_mcu.c sowohl die 
init_mcu.h (DAS ist klar!!), aber auch noch die my_globals.h einbinden.

Ist das nicht doppelt gemoppelt?
Ich dachte ein #include schreibt einfach den inhalt der eingebundenen 
Header-Datei an die Stelle des #includes. oder hab ich da was falsch 
verstanden?
Wäre schön, wenn Ihr Euch dem Thema noch ein wenig annehmen würdet.
mfg
mathias

von Falk B. (falk)


Lesenswert?

@  Mathias U. (munter)

>Ist das nicht doppelt gemoppelt?

Nein. Jede .c Datei wird VOLLKOMMEN unabhängig kompiliert. D.h. sie muss 
aber per #include alles wissen, was sie anwendet (gloable Variablen, 
Funktionsaufrufe). Danach wird alles vom Linker zusammengepackt.

>Ich dachte ein #include schreibt einfach den inhalt der eingebundenen
>Header-Datei an die Stelle des #includes. oder hab ich da was falsch
>verstanden?

Macht es auch.

MFG
Falk

von Mathias U. (munter)


Lesenswert?

Gut, danke. Dann werde ich das so machen...
Schon taucht das nächste Problem auf:
Ich mache mir eine my_functions.c in der ich dann halt einige Funktionen 
auslagern tue...
Die Funktionen brauchen meine globalen Variablen...also binde ich die 
my_globals.h dort mit ein.
Da ich aber nicht alle Funktionen gleichzeitig auslagern möchte, weil 
ich will ja schrittweis testen, obs geht, hab ich in meiner main.c auch 
noch Funktion drin, die auch globale variablen brauchen. Also hab ich 
die globals in der main.c auch noch "includiert" (blödes wort!)

Beim Compilieren der my_functions.c allein und auch der main.c geht 
alles glatt.

Will ich aber ein "make" machen, erscheit der Fehler:
Error[e27]: Entry "Gain_aktuell" in module main ( C:\...\main.r43 ) 
redefined in module my_functions ( C:\...\my_functions.r43 )

Die besagte Variable ist ein Integer, global definiert und mit 0 
initialisiert, und taucht in der main() in meiner for(;;)-Schleife nur 
an der folgenden Stelle auf.
Die wird doch gar nicht redefined, sonder einfach nur gesetzt.
1
if (Mode == 1)                                // im line-mode aus dem elektrischen signal den
2
      {                                             // schallpegel bestimmen      
3
        Gain_aktuell = LUT_GAIN_01[PGA_WERT];       // bestimmung der aktuellen verstaerkung
4
        if      (Wichtung == 1)
5
        {
6
          delta_1 = Lequ/10 - Kalib_LequA_el_5s_neu;// bestimmung der elektr. pegeldifferenz
7
          delta_2 = Gain_aktuell - Gain_eff_A;      // bestimmung der verstaerkungsdifferenz    
8
        }
9
        else if (Wichtung == 2)
10
        {
11
          delta_1 = Lequ/10 - Kalib_LequB_el_5s_neu;// bestimmung der elektr. pegeldifferenz
12
          delta_2 = Gain_aktuell - Gain_eff_B;      // bestimmung der verstaerkungsdifferenz       
13
        }
14
        else if (Wichtung == 3)
15
        {
16
          delta_1 = Lequ/10 - Kalib_LequC_el_5s_neu;// bestimmung der elektr. pegeldifferenz
17
          delta_2 = Gain_aktuell - Gain_eff_C;      // bestimmung der verstaerkungsdifferenz         
18
        }
19
...
Das ist auch die einzige Stelle im Programm (bis jetzt), an der die 
Variable benutz wird.
Was ist das jetzt wieder für ein Fehler?

*edit: witzig find ich ja, dass wenn ich die Variable aus meiner 
my_globals.h rausnehme, und dierekt in der main.c als global definiere, 
dann gehts, aber er hat bei einer anderen Variable den Fehler...

von Christian R. (supachris)


Lesenswert?

Naja, das kommt daher, dass der Linker dann mehrere Funktionen hat, die 
den gleichen Namen haben.
Du musst die Header-Dateien folgendermaßen gestalten:

Beispiel:
1
#ifndef SPI_H_
2
#define SPI_H_
3
4
void SPIADCInit(void);
5
void SPIGetADCData(void);
6
7
#endif /*SPI_H_*/

von Falk B. (falk)


Lesenswert?

@ Mathias U. (munter)

>die globals in der main.c auch noch "includiert" (blödes wort!)

Eingebunden. Es geht aus problemlos auf Deutsch. ;-)

>Will ich aber ein "make" machen, erscheit der Fehler:
>Error[e27]: Entry "Gain_aktuell" in module main ( C:\...\main.r43 )
>redefined in module my_functions ( C:\...\my_functions.r43 )

Du musst unterscheiden zwischen Deklaration (in .h Headerfiles) und 
Definition (in .c Sourcefiles). Dann passt das auch. Die Deklaration 
kanst du hundertmal einbinden, die Definition wird nur einmal 
compiliert.

>*edit: witzig find ich ja, dass wenn ich die Variable aus meiner
>my_globals.h rausnehme, und dierekt in der main.c als global definiere,
>dann gehts, aber er hat bei einer anderen Variable den Fehler...

In .h kommen KEINE VariablenDEFINITIONEN rein, nur DEKLARATION. Die 
deklarieren, dass es diese variable gibt! WO, ist was anderes. Und zwar 
nur einmal in my_globals.c.

MfG
Falk

von Mathias U. (munter)


Lesenswert?

...jetzt bin ich verwirrt.
Was eine VariablenDEKLARATION ist, ist mir klar.

Mit z.B. :
int a = 8;
wird eine Variable deklariert und gleich mit initialisiert.

Aber was bitte ist eine VariablenDEFINITION? Da gibts doch keinen 
Unterschied, oder?
Bei Funktionen ist mir die Sache klar: Deklarieren heißt, die Funktion 
ist da und in der Definition steht drin, was die Fkt. machen soll...

Wo schreib ich jetzt meine globalen Variablen rein? In eine 
my_globals.c? und die wird über den Projektmanager eingebunden?

von Christian R. (supachris)


Lesenswert?

Naja, in die h-Datei kommt einfach nur:

unsigned int test; (Deklaration)


und in irgendeiner C-Datei dann:

test = 0; (Definition)

von Falk B. (falk)


Lesenswert?

@ Mathias U. (munter)

>Was eine VariablenDEKLARATION ist, ist mir klar.

>Mit z.B. :
>int a = 8;
>wird eine Variable deklariert und gleich mit initialisiert.

OK.

>Aber was bitte ist eine VariablenDEFINITION? Da gibts doch keinen
>Unterschied, oder?

Doch. Die Definition einer Variable ist genauso wie die Definition einer 
Funktion in Headerdateien. Sie zeigt nur wie sie heisst und wie gross 
sie ist. Sie wird aber NICHT angelegt (definiert). Das ist der 
Springende Punkt.

>Bei Funktionen ist mir die Sache klar: Deklarieren heißt, die Funktion
>ist da und in der Definition steht drin, was die Fkt. machen soll...

>Wo schreib ich jetzt meine globalen Variablen rein?

1
// my_globals.c
2
uint8_t my_tmp;

1
// my_globals.h
2
extern uint8_t my_tmp;

my_globals.h wird dann überall per #include eingebunden. Sieht aus wie 
doppelt gemoppelt, ist aber schon sinnvoll.

MFG
Falk

von Falk B. (falk)


Lesenswert?

@ Christian R. (supachris)

>Naja, in die h-Datei kommt einfach nur:
>unsigned int test; (Deklaration)

>und in irgendeiner C-Datei dann:
>test = 0; (Definition)

Nein, so eben nicht. Karl Heinz oder Jörg können das sicher besser 
erklären als ich mit meinem C-Halbwissen.

MFG
Falk

von Mathias U. (munter)


Lesenswert?

Und initialisiert werden die variablen in der my_globals.c?

von Falk B. (falk)


Lesenswert?

@ Mathias U. (munter)

>Und initialisiert werden die variablen in der my_globals.c?

Ja. Wobei alle ohne explizite Initialisierung automatisch und garantiert 
auf Null gesetzt werden.

MFG
Falk

von Mathias U. (munter)


Lesenswert?

GOTT...schwere Kaiserschnittgeburt!...es ist ein Junge!

Wieder was gelernt!
@Falk: das mit der Nullinitialisierung kenn ich auch, aber ich mach es 
trotzdem immer nochmal. Ich finds übersichtlicher! (wie man an meiner 
alten main() sehen kann ;-) )

danke Euch!

von Falk B. (falk)


Lesenswert?

@ Mathias U. (munter)

>GOTT...schwere Kaiserschnittgeburt!...es ist ein Junge!

Gratulation an den stozen Papa ;-)

>trotzdem immer nochmal. Ich finds übersichtlicher! (wie man an meiner

Du hast ein komische "Logik".

MFG
Falk

von Christian R. (supachris)


Lesenswert?

Falk Brunner wrote:
> @ Christian R. (supachris)

> Nein, so eben nicht. Karl Heinz oder Jörg können das sicher besser
> erklären als ich mit meinem C-Halbwissen.

Hmm...war also wieder mal ein Schnellschuss.

Also ich deklariere grundsätzlich keine Variablen in den Headern. Da 
werden bei mir nur Funktiosdeklarationen und Deklarationen von 
Strukturen angelegt.

Variablen deklariere ich da, wo sie (für mich gesehen) logisch 
hingehören, in den C-Dateien über den Funktionen.

Überall da, wo ich auf die Variable einer anderen Funktionseinheit 
(einer anderen C-Datei) zugreifen will, hole ich mit diese Variable mit 
extern rein.

Weiß jetzt nicht, ob das die Standard-Vorgehensweise ist, aber ich 
find´s sehr übersichtlich. Weil jede Variable da deklariert und 
definiert wird, wo sie hingehört. Und extern zeigt mir an, dass sie zu 
einem anderen Funktionsblock gehört.

von Falk B. (falk)


Lesenswert?

@ Christian R. (supachris)

>Also ich deklariere grundsätzlich keine Variablen in den Headern. Da
>werden bei mir nur Funktiosdeklarationen und Deklarationen von
>Strukturen angelegt.

>Variablen deklariere ich da, wo sie (für mich gesehen) logisch
>hingehören, in den C-Dateien über den Funktionen.

Du machst dann aber eine DEFINITION mit impliziter DEKLARATION. Das ist 
ein kleiner Unterschied. Damitkannst du auf sie nur innerhalb des Files 
zugreifen. Was bei globalen Varibalen bissel schlecht ist.

>Überall da, wo ich auf die Variable einer anderen Funktionseinheit
>(einer anderen C-Datei) zugreifen will, hole ich mit diese Variable mit
>extern rein.

Super, und wenn du das in drei, vier Files machst, hast du erstens 
mehrfachen Schreibaufwand und zwetens holst du dir schnell 
Inkonsistenzen an den Hals.

>Weiß jetzt nicht, ob das die Standard-Vorgehensweise ist, aber ich
>find´s sehr übersichtlich. Weil jede Variable da deklariert und
>definiert wird, wo sie hingehört. Und extern zeigt mir an, dass sie zu
>einem anderen Funktionsblock gehört.

Nööö. Allgemein gibt es nur lokale und globale Variablen. Die lokalen 
brauchen keine expliziten Deklaration, logisch. Und die globalen sind 
wie der Name schon sagt GLOBAL. Die in verschiedenen Dateien zu 
versteuen ist Murks. Deshalb globals.h und globals.c .

MfG
Falk

von Christian R. (supachris)


Lesenswert?

Na gut, dann ist das halt nicht ganz die korrekte Art und Weise, aber da 
hat wohl jeder seinen eigenen Stil.

Der Schreibaufwand ist minimal, da die "Vorhersage" von Eclipse ja 
praktisch alles macht.

von Falk B. (falk)


Lesenswert?

@ Christian R. (supachris)

>Na gut, dann ist das halt nicht ganz die korrekte Art und Weise, aber da
>hat wohl jeder seinen eigenen Stil.

Als Hobbyprogrammierer kannst du das machen wie du willst, als Profi 
wäre es schlicht unprofessionell.

MFG
Falk

von Christian R. (supachris)


Lesenswert?

Falk Brunner wrote:
> @ Christian R. (supachris)

> Als Hobbyprogrammierer kannst du das machen wie du willst, als Profi
> wäre es schlicht unprofessionell.

Na wie gut dass ich E-Techniker und kein Informatiker bin, und nur 
"nebebei" mal die Sofware für die Elektronik schreibe. Bisher hat sich 
noch keiner beschwert. Viel wichtiger sind da Kommentare und eine klare 
Gliederung in Funktionseinheiten.

Ich kann´s ja demnächst "richtig" machen....sowas haben wir im Studium 
halt nicht gelernt.

von Mathias U. (munter)


Lesenswert?

So hab mal nen bissl aufgeräumt...
Ist zwar noch lange nicht optimal, aber man kann jetzt leichter 
durchsteigen...

Die main.c hat sich doch tatsächlich von knapp 2900 zeilen auf 85 
reduziert...
1
// ************************************************************************************************
2
// includes
3
// ************************************************************************************************
4
#include "owndef.h"                                 // eigene defines
5
#include "my_globals.h"                             // globale variablen
6
#include "init_mcu.h"                               // initialisirung des µControllers
7
#include "lcd.h"                                    // alle noetigen sachen fuer das display
8
#include "RTC_Calendar.h"                           // RealTimeClock bibliothek
9
#include "my_functions.h"                           // alle eigenen funktionen
10
#include "anzeigensteuerung.h"                      // steuerung der anzeigen auf dem display
11
#include "tastensteuerung.h"                        // steuerung der tasten
12
#include "ledsteuerung.h"                           // steuerung der leds
13
#include "kalibrierung.h"                           // routinene fuer kalibrierung
14
#include "relais_adg452_steuerung.h"                // steuerung der relais und des adg452
15
#include "protokollierung.h"                        // routine fuer die protokollierung auf sd-card
16
#include "messroutine_jeden_tick.h"                 // messroutine zu jedem systemtick (alle 125ms)
17
#include "berechnungsroutinen.h"                    // berechnungen der pegel
18
// ************************************************************************************************
19
#if( LCD_VERSION != 0x0100 )
20
  #error "Falsche LCD Version"
21
#endif
22
// ************************************************************************************************
23
// HAUPTPROGRAMM
24
// ************************************************************************************************
25
void main()
26
{
27
  WDTCTL=WDTPW+WDTHOLD;                             // stoppt den WDT
28
  init_mcu();                                       // initialisierungsachen fuer den µcontroller 
29
// ************************************************************************************************
30
// einstellen und starten der uhrzeit nach reset
31
// ************************************************************************************************
32
  TI_dayLightZone = EU_DAYLIGHT_SAVINGS;            // setzt variable für eu-sommerzeitumstellung
33
  setDate(2007, 12, 11);                            // inititialiesieren des datums auf 11.12.2007
34
  setTime(0x01, 0x00, 0x00, 0);                     // inititialisieren der zeit auf 01:00:00 AM
35
  get_date_and_time();                              // erstmaliges holen des datums und der uhrzeit
36
// ************************************************************************************************
37
  for (;;)                                          // beginn endlosschleife for(;;)
38
  {
39
    messroutine_jeden_tick();
40
    berechnung_alle_1s();
41
    berechnung_alle_5s();
42
    berechnung_alle_10s();
43
    berechnung_alle_20s();
44
    berechnung_alle_30s();
45
    berechnung_alle_40s();
46
    berechnung_alle_50s();
47
    berechnung_alle_1m();
48
    berechnung_alle_30m();
49
    relais_adg452_steuerung();
50
    ledsteuerung();
51
    kalibrierung();
52
    protokollierung();
53
    tastensteuerung();
54
    anzeigensteuerung(); 
55
// ************************************************************************************************
56
// routine fuer regelung des musiksignales
57
// ************************************************************************************************
58
  
59
  }                                                 // ende endlosschleife for (;;)
60
}                                                   // ende main
61
// ************************************************************************************************
62
63
// ************************************************************************************************
64
// interrupt routinen
65
// ************************************************************************************************
66
// timer_a interrupt service routine
67
#pragma vector=TIMERA0_VECTOR
68
__interrupt void Timer_A (void)
69
{
70
  if (Tick == 7)
71
  {
72
    Tick = 0;
73
    incrementSeconds();
74
  }
75
  else
76
  Tick++;
77
}
78
// ************************************************************************************************

Sieht doch schon wesentlich schicker aus, oder Falk? :-)
Und durch die Erhöhung des DCO-Taktes hat sich mein Zeitproblem auch 
erledigt.
Am Montag werde ich evtl. noch mal messen, wie lange die einzelnen 
Routinen so brauchen...würde mich auch so mal interessieren.
Danke

mathias

von Mathias U. (munter)


Lesenswert?

Guten Morgen.

Ich habe noch eine Frage zum DCO:

Wenn ich RSEL = 7 und DCO = 4 mache, mit welcher Frequenz arbeitet dann 
die CPU eigentlich?

Im Datenblatt gibt es ja auf Seite 4-7 diese tolle Tabelle, da steht 
aber meine Kombination leider nicht drin...
Für f(DCO73) sind es bei 3V nominell 3,2MHz. Das MEIN Takt geringfügig 
höher ist, ist klar, aber es muss doch auch irgendwo auszurechnen gehen, 
oder?
Mal abgesehen davon, dass ich 3,3Vcc hab.
Komisch finde ich auch, dass bei f(DCO47) Werte drin stehen, die mit 
f(DCO40) berechnet werden sollen. Nur steht auch f(DCO40) nicht drin. 
Wie soll man das dann ausrechnen??
Hab ich da im Datenblatt was übersehen? Auch im Family Guide hab ich 
nichts gefunden...
danke

mathias

von Stefan (Gast)


Lesenswert?

Steht in derselben Tabelle mit drin:

SDCO = fDCO+1/fDC0 = 1,12 (typ.)

D.h. wenn man DCO um eins erhöht, dann erhöht sich die Frequenz 
typischerweise um den Faktor 1,12
Dasselbe gilt für RSEL: SRSEL = fRSEL+1/fRSEL = 1,65 (typ.)

Aber Ausrechnen macht beim DCO nicht all zuviel Sinn, da die Werte doch 
sehr streuen und von der Vcc, von der Temperatur und auch vom Exemplar 
abhängen.

von Mathias U. (munter)


Lesenswert?

Argg, Ja danke...man kann natürlich auch messen...an P5.4 liegt ja der 
MCLK an...
Und wenn ich das tue, dann kommt 3,44MHz raus. Passt schon...

mathias

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.