Forum: Mikrocontroller und Digitale Elektronik Anfängerprogramm TimerA MSP430


von Fabian H. (Firma: Technische Universität Berlin) (brein)


Angehängte Dateien:

Lesenswert?

Hallo Leute,

ich versuche gerade auf meinem LaunchPad mit einem MSP430G2231 den Timer 
A so einzustellen, dass eine LED an P1.0 mit 1Hz blinkt. Nettes Beispiel 
um die Peripherie kennen zu lernen.

Normalerweise schreibe ich nicht sofort und studiere erstmal die 
Datasheets. Aber ich wusste gar nicht, was man alles falsch machen kann, 
oder wie überhauptnicht man die Architektur kapieren kann.
Ich hoffe, Ihr könnt mich der Sache etwas näher bringen.

Generell blinkt die LED schon mal, wenn ich eine Zählschleife bis 20.000 
oder so laufen lasse.

Aber um auf 1Hz zu kommen wollte ich den Timer benutzen.
Wenn man auf das LaunchPad schaut, sieht man einen 12MHz Oszillator, ich 
hoffe, dass dann auf ACLK undgeteilt rausläuft, ob dem so ist, konnte 
ich bisher auch im Kapitel Basic Clock Module+ nicht genau herauslesen.

Ich hoffe, Ihr könnt an meinem Beispielprogramm sagen was und warum es 
falsch ist.

Interrupts
Polling ist bei Timer recht unpraktisch, aber Interrupts funktionieren 
hier nicht richtig. signal.h habe ich zwar inkludiert, aber dennoch kann 
mein CCSv5 nichts mit dem Schlüsselwort interrupt anfangen.
Und das obwohl ich es direkt aus den Codebeispielen kopiert hatte: 
http://www.mikrocontroller.net/articles/MSP430_Codebeispiele#Initialisierung_des_Timers_A 
.
Auch mit dem Vektor komme ich nicht ganz klar.
Der TIMERA0_VECTOR = 9*1u, also 0x09. Dabei kann es gar keine 
ungeraden Zahlen annehmen, wenn man sich mal den TAIV-Register (Seite 
381) ansieht.
Welcher der 3 Vektoren ist das nun und wie lauten die anderen beiden?
Wo steht das im Datenblatt?

Code
1
//***************************************************************************************
2
//  MSP430 Blink the LED Demo - Software Toggle P1.0
3
//
4
//  Description; Toggle P1.0 by xor'ing P1.0 inside of a software loop.
5
//  ACLK = n/a, MCLK = SMCLK = default DCO
6
//
7
//                MSP430x5xx
8
//             -----------------
9
//         /|\|              XIN|-
10
//          | |                 |
11
//          --|RST          XOUT|-
12
//            |                 |
13
//            |             P1.0|-->LED
14
//
15
//  J. Stevenson
16
//  Texas Instruments, Inc
17
//  July 2011
18
//  Built with Code Composer Studio v5
19
//***************************************************************************************
20
21
#include <msp430.h>
22
#include <signal.h>
23
24
25
int main(void) {
26
  volatile int i = 0;       // counter variable
27
  WDTCTL = WDTPW + WDTHOLD;    // Stop watchdog timer
28
  P1DIR |= 0x01;          // Set P1.0 to output direction
29
30
  // Timer A setup
31
  TACTL = TACLR;          // Clear Timer A setup
32
  TACTL = TASSEL_1 + ID_3 + MC_0;  // ACLK; Prescaler 8; Timer Stop;
33
//  _enable_interrupts();
34
  TAR = 0;
35
  TACCR0 = 62500;
36
  TACTL |= MC_2;  // start; Counts TAR from 0 up to TACCR0;
37
38
39
  while(1) {
40
41
    for(i = 0; i < 12; i++)
42
    {
43
      while( !(TACTL & TAIFG));  // till TAIFG is set
44
      TACTL &= ~TAIFG;  // reset TAIFG;
45
46
    }
47
    P1OUT ^= 0x01;        // Toggle P1.0 using exclusive-OR
48
  }
49
}
Im wesentlichen läuchtet P1.0 nur konstant. Was ist falsch?

Es gibt ja neben STOP drei Modi in dem der Timer laufen kann.
UP.. Der Timer Register TAR wird hochgezählt bis es den Wert von TACCR0 
erreicht, danach wird der Interrupt ausgelöst und wieder auf 0 zurück 
gesetzt. Genau das nutze ich.
CONTINUOUS.. Zählt TAR von 0x0000-0xFFFF. Könnte ich auch nutzen, wenn 
ich TAR nach jedem interrupt preinitialisiere.
UP/DOWN.. Ist ganz nett. Aber hier nicht verwendbar.

Macro
Aus den Macros werde ich auch nicht ganz schlau.
Für das TACTL zum Beispiel gibt es die Macros:
TASSEL0:   0x0100
TASSEL1:   0x0200
TASSEL_0:  0*0x100u
TASSEL_1:  1*0x100u
TASSEL_2:  2*0x100u
TASSEL_3:  3*0x100u
Warum gibt es zwei verschiedene Nomenklaturen und warum bedeuten sie 
nicht das selbe? Warum bedeutet TASSEL0 == TASSEL_1 und TASSEL1 == 
TASSEL_2?

Das gleiche gild für IDx:
ID0:   0x0040
ID1:   0x0080
ID_0:  0*0x0040u
ID_1:  1*0x0040u
ID_2:  2*0x0040u
ID_3:  3*0x0040u



So, das ist wieder mal ausschweifend geworden. Aber ich wollte, wenn 
dann schon, alle Klarheiten beseitigt haben.
Wer mir aber einen einfachen Code mit Erklärungen posten kann der mit 
1Hz arbeitet, dem wäre ich auch sehr dankbar.

Vielen Dank
Fabian

von Fabian H. (Firma: Technische Universität Berlin) (brein)


Lesenswert?

Aber ist schon krass.
Da will man mal was anderes sehen außer einen ATMEL schon findet man 
kaum noch gute Tuts und kommt gänzlich durcheinander. :(

Dabei will ich nur ein Timer nutzen um eine LED blinken zu lassen.
Ich bin schon gespannt, wenn ich das UART nutzen will. Denn das habe ich 
in dem Datasheet gar nicht gefunden.

Uff. Dabei ist es doch nur so ein kleines Steinchen.

von Dennis (Gast)


Lesenswert?

TASSEL0 und 1 brauchst du nicht nutzen. Das ist intern. Für dich ist 
hierbei nur der Wert mit dem "_" interessant. Und das _0 und _1 das 
selbe sind steht doch auch im User's Guide. Ist beides SMCLK (wenn ich 
mich richtig erinnere, hab's jetzt nicht nachgeguckt). Die Werte mit dem 
Unterstrich erleichtern dir die Sache nur, weil sie mit den 
Datenblattangaben übereinstimmen. Dahinter verbergen sich halt nur die 
zwei Bits 0x0100 und 0x0200, welche durch TASSEL0 und 1 definiert sind. 
Die kannst du auch selber direkt setzen, z.B. durch (TASSEL0 | TASSEL1). 
Aber das brauchst du nicht, indem du einfach TASSEL_3 setzt.

Analog dazu mit ID und FAST allen anderen Bitkombinationen. Es gibt 
manche, bei denen man wirklich selber die einzelnen Bits setzen muss, 
z.B. beim SVS (istbaber vielleicht auch abhängig von der Header-Datei).

von Dennis (Gast)


Lesenswert?

Der 12MHz Quarz ist natürlich NICHT für deinen Controller, sondern für 
den anderen MSP auf dem Launchpad, welcher über USB die Verbindung zu 
deinem eigentlichen Controller herstellt. Einen 1Hz Takt bekommst du am 
ehesten, indem du den mitgelieferten 32k Quarz einlötest.

von Dennis (Gast)


Lesenswert?

Und ja: Pollen bei nem Timer ist Schwachsinn :)

von Fabian H. (Firma: Technische Universität Berlin) (brein)


Lesenswert?

Dennis schrieb:
> TASSEL0 und 1 brauchst du nicht nutzen. Das ist intern. Für dich ist
> hierbei nur der Wert mit dem "_" interessant.
Danke.

> Und das _0 und _1 das
Du meinst sicher 0 und _1, oder? Denn das hatte ich gesagt.
> selbe sind steht doch auch im User's Guide. Ist beides SMCLK (wenn ich
> mich richtig erinnere, hab's jetzt nicht nachgeguckt).
Nein! (Seite 378)
00 TACLK
01 ACLK
10 SMCLK
11 INCLK
Was diese bedeuten kann ich mir bis jetzt noch nicht erklären.
Ich dachte ja erst, das sei sowas die die Generelle Clock, die CPU 
Clock, die Periphery Clock, usw. Aber so scheint das nicht zu sein.
Das einzige, dass ich glaube verstanden zu haben aus dem Kapitel "Basic 
Clock Module+" ist, dass die Generelle Clock das MCLK ist. Das ist aber 
nicht Thema hier.
> Die Werte mit dem
> Unterstrich erleichtern dir die Sache nur, weil sie mit den
> Datenblattangaben übereinstimmen. Dahinter verbergen sich halt nur die
> zwei Bits 0x0100 und 0x0200, welche durch TASSEL0 und 1 definiert sind.
Moment!
Wieso ergibt sich z.B. der Wert des TASSEL_0 durch TASSEL0? Zumal die 
auch inhaltlich nicht die gleichen sind, da 0x0100 != 0*0x100u!
In TASSEL0 steht 512 und in TASSEL_0 steht 0.
> Die kannst du auch selber direkt setzen, z.B. durch (TASSEL0 | TASSEL1).
> Aber das brauchst du nicht, indem du einfach TASSEL_3 setzt.
Stimmt, so mache ich das ja auch bei den ATmegas und ATtinys.
Jetzt habe ich grundsätzlich den Unterschied zwischen X und _X 
verstanden.
>
> Analog dazu mit ID und FAST allen anderen Bitkombinationen. Es gibt
> manche, bei denen man wirklich selber die einzelnen Bits setzen muss,
> z.B. beim SVS (istbaber vielleicht auch abhängig von der Header-Datei).

Dennis schrieb:
> Der 12MHz Quarz ist natürlich NICHT für deinen Controller, sondern für
> den anderen MSP auf dem Launchpad, welcher über USB die Verbindung zu
> deinem eigentlichen Controller herstellt. Einen 1Hz Takt bekommst du am
> ehesten, indem du den mitgelieferten 32k Quarz einlötest.
Der ist eingelötet. Aber mit welcher Frequenz wird denn der MSP430 im 
Werkszustand betrieben/wie kann ich das einstellen?
Was ist ACLK/TACLK?
Wie wenn nötig kann ich denn auf den externen Quarz zugreifen, der 
längst eingelötet ist?


Erstmal vielen Dank
Fabian

von Fabian H. (Firma: Technische Universität Berlin) (brein)


Lesenswert?

Dennis schrieb:
> Und ja: Pollen bei nem Timer ist Schwachsinn :)

Klar, das weiß ich. Wie aber ändern, wenn der Beispielcode nicht 
funktioniert?
Der Witz ist, auf http://inventortown.com wo man seine Projekte online 
compilieren lassen kann, funktioniert es. Mit 8 Warnings zwar, aber es 
geht. Wenn auch die Seite gerade down ist.

Muss ich sonst noch was inkludieren außer der signal.h?

Gruß und Danke
Fabian

von Rufus Τ. F. (rufus) Benutzerseite


Lesenswert?

Fabian Hoemcke schrieb:
> Dennis schrieb:
>> Der 12MHz Quarz ist natürlich NICHT für deinen Controller, sondern für
>> den anderen MSP auf dem Launchpad, welcher über USB die Verbindung zu
>> deinem eigentlichen Controller herstellt. Einen 1Hz Takt bekommst du am
>> ehesten, indem du den mitgelieferten 32k Quarz einlötest.
> Der ist eingelötet. Aber mit welcher Frequenz wird denn der MSP430 im
> Werkszustand betrieben/wie kann ich das einstellen?

Der MSP430 im Launchpad arbeitet nach einem Reset immer mit dem internen 
RC-Oszillator (DCO) und einer Taktfrequenz von etwa 1 MHz. Der DCO lässt 
sich auch auf andere Frequenzen einstellen, wie das geht, steht im 
Family User's Guide und ist auch in den Codebeispielen von TI zu finden.

Dort (also an beiden Stellen) ist auch beschrieben, wie mit einem 
externen Quarz vorzugehen ist.

Der DCO ist bei einigen MSP430-Varianten werksseitig auf verschiedene 
Frequenzen kalibriert, die Kalibrierdaten dafür sind im Info-Memory 
abgelegt. Werden diese in die zugehörigen Register übertragen, läuft der 
Controller halbwegs genau mit der jeweiligen Taktfrequenz.

Auch wenn ich sie schon erwähnt habe:

Sieh Dir die Codebeispiele von TI an, und sieh Dir dazu passend genau 
die Dokumentation der jeweiligen Peripheriebausteine im Family User's 
Guide an, insbesondere die Registerbeschreibung.

Mit den Informationen solltest Du Deine Fragen zum Timer_A selbst klären 
können.

von Fabian H. (Firma: Technische Universität Berlin) (brein)


Lesenswert?

Danke rufus.

Müsste dann aber nicht einfach die LED mit 6Hz blinken anstatt mit 1Hz 
wenn die Frequenz nur ein 12tel ist?

Warum leuchtet sie bei mir einfach konstant?

von Nichtschläfer (Gast)


Lesenswert?


von Franz (Gast)


Lesenswert?

1/12tel? Bist du dir sicher, dass du weißt, was in deinem Programm 
passiert?

von Fabian H. (Firma: Technische Universität Berlin) (brein)


Lesenswert?

Franz schrieb:
> 1/12tel? Bist du dir sicher, dass du weißt, was in deinem Programm
> passiert?

Nein, sonst würde ich ja nicht fragen.
Aber wenn ich von einer Frequenz von 12MHz ausgegangen bin und mir aber 
plausibel erklärt wurde dass und warum es aber 1MHz sind, dann dürfte 
das Programm wohl nur mit 1/12tel der Geschwindigkeit laufen, oder?
Also die LED, die mit 1Hz blinken sollte wechselt aller 6s ihren 
zustand.

Was ich versucht hatte der Maschine klar zu machen war folgendes:
1. Port1 Pin1 als Ausgang
2. TimerA Einstellungen löschen
3. ACLK als Taktquelle auswählen; ihn durch 8 teilen und den Timer im 
StandBy halten
4. Den Counterregister TAR auf 0 setzen
5. Den Vergleichswert auf 62500 setzen (TACCR0)
6. Den Timer im UP Modus starten. TAR wird bis zum TACCR0 hochgezählt 
der Interrupt ausgelöst und auf 0 zurückgesetzt.
7. 12 Mal wird der Interrupt abgewartet und dann die LED getoggelt.
Denn

Wenn dem nicht so ist, dann bitte nicht einfach süffisant fragen, 
sondern sagen wo der Fehler ist.

Danke Nichtschläfer, schaue ich mir Morgen an.

Gruß
Fabian

von Fabian H. (Firma: Technische Universität Berlin) (brein)


Lesenswert?

Nichtschläfer schrieb:
> http://processors.wiki.ti.com/index.php/MSP430_LaunchPad_LED_Timer

Dazu habe ich erstmal 2 Fragen:
1. Muss das mit Capture&Compare gemacht werden? Oder kann ich das nicht 
einfach so machen wie ich es mir dache? (Oder wäre das, so wie ich es 
mir dachte CC?)
2, Warum heißt im Beispiel das TACCTL0 Register nur CCTL0? Also anders 
als im Datenblatt?

Danke
Fabian

von Fabian H. (Firma: Technische Universität Berlin) (brein)


Lesenswert?

Ich habe es hinbekommen:
1
#include <msp430.h>
2
3
4
int main(void) {
5
  volatile int i = 0;       // counter variable
6
  WDTCTL = WDTPW + WDTHOLD;    // Stop watchdog timer
7
  P1DIR |= 0x01;          // Set P1.0 to output direction
8
9
  // Timer A setup
10
  TACTL = TACLR;          // Clear Timer A setup
11
  TACTL = TASSEL_2 + ID_3 + MC_0;  // SMCLK; Prescaler 8; Timer Stop;
12
//  _enable_interrupts();
13
  TAR = 0;
14
  TACCR0 = 62500;
15
  TACTL |= MC_2;  // start; Counts TAR from 0 up to TACCR0;
16
17
18
  while(1) {
19
20
    while( !(TACCTL0 & CCIFG));  // till CCIFG is set
21
    TACCTL0 &= ~CCIFG;  // reset CCIFG;
22
23
    P1OUT ^= 0x01;        // Toggle P1.0 using exclusive-OR
24
  }
25
}

Hatte wohl CC und Overflow vermischt.
Und mit ACLK die falsche (da viel langsamere) Clock ausgewählt.

Wird eigentlich nach einem Compare TAR automatisch zurück gesetzt oder 
muss man das noch von Hand machen?
(Ich glaube nein, denn ich glaube aus diesem Grund funktionierte meine 
Flagabfrage gar nicht.)
Wenn nein, könnte ich bei Modus Continuous sowohl Compare als auch 
Overflow nutzen?

Danke alle
Fabian

von MSP430 (Gast)


Lesenswert?

Fabian Hoemcke schrieb:
> Ich habe es hinbekommen

Wie? Wo? Was? Ohne Interrupt?
Da geht doch noch etwas. Also bitte noch einmal einen Blick auf das 
Beispiel im TI Wiki werfen.

von Fabian H. (Firma: Technische Universität Berlin) (brein)


Lesenswert?

MSP430 schrieb im Beitrag #2855769:
> Fabian Hoemcke schrieb:
>> Ich habe es hinbekommen
>
> Wie? Wo? Was? Ohne Interrupt?
> Da geht doch noch etwas. Also bitte noch einmal einen Blick auf das
> Beispiel im TI Wiki werfen.

Ja klar, würde ich ja gerne.
Immerhin bin ich jetzt generell durch den TimerA durchgestiegen und was 
die Value Line da so bietet.

Was die Interrupts angeht, bin ich echt verwirrt.
Das Beispiel hier auf Mikrocontroller.net funktioniert leider nicht:
1
void init_TimerA(unsigned int cycles )
2
{
3
  TACTL = TASSEL1 + TACLR;              // SMCLK, clear TAR
4
  CCTL0 = CCIE;                         // CCR0 interrupt enabled
5
  CCR0 = cycles;
6
  TACTL |= MC_2;                         // Start Timer_A in continuous mode
7
  _EINT();                              // interrupt enable
8
}
9
 
10
// Timer A0 interrupt service routine
11
interrupt (TIMERA0_VECTOR) Timer_A(void)
12
{
13
  P1OUT ^= 0x01;                        // Toggle P1.0
14
  CCR0 += 50000;                        // Add Offset to CCR0
15
}
http://www.mikrocontroller.net/articles/MSP430_Codebeispiele#Initialisierung_des_Timers_A

Da CCDv5 das Schlüsselwort interrupt nicht kennt, trotz #include 
<signal.h>.

Das andere Beispiel auf 
http://processors.wiki.ti.com/index.php/MSP430_LaunchPad_(MSP-EXP430G2)
verstehe ich im Grunde gar nicht:
1
#include <msp430g2231.h>
2
3
4
#define LED_0 BIT0 
5
#define LED_1 BIT6
6
#define LED_OUT P1OUT
7
#define LED_DIR P1DIR
8
9
10
11
unsigned int timerCount = 0;
12
void main(void)
13
{
14
WDTCTL = WDTPW + WDTHOLD; // Stop watchdog timer
15
LED_DIR |= (LED_0 + LED_1); // Set P1.0 and P1.6 to output direction
16
LED_OUT &= ~(LED_0 + LED_1); // Set the LEDs off
17
18
CCTL0 = CCIE;
19
TACTL = TASSEL_2 + MC_2; // Set the timer A to SMCLCK, Continuous
20
// Clear the timer and enable timer interrupt
21
22
__enable_interrupt();
23
24
__bis_SR_register(LPM0 + GIE); // LPM0 with interrupts enabled
25
26
} 
27
28
29
// Timer A0 interrupt service routine
30
#pragma vector=TIMERA0_VECTOR
31
__interrupt void Timer_A (void)
32
{
33
timerCount = (timerCount + 1) % 8;
34
if(timerCount ==0)
35
P1OUT ^= (LED_0 + LED_1);
36
}
http://processors.wiki.ti.com/index.php/MSP430_LaunchPad_LED_Timer

Das funktioniert zwar, aber ich kenne #pragma nicht und ich weiß auch 
nicht, wie das mit dem Vector zusammen spielt.
Noch nicht, zu mindest.
Auch wenn ich es noch nicht verstehe, die generelle Struktur ist wohl 
diese:
1
#pragma vector=INTERRUPT_VECTOR
2
__interrupt void IrgendeinFunktionsname (void)
 ?

Und da bin ich wieder bei einer Frage vom Anfang des Threads, wenn diese 
auch nicht so wichtig ist, wie ist es möglich, dass TIMERA0_VECTOR = (9 
* 1u) also 0x0009 ist?
Dieser Wert ist doch gar nicht in der TAIV auslesbar!?

Gruß und Danke
Fabian

von Rufus Τ. F. (rufus) Benutzerseite


Lesenswert?

Fabian Hoemcke schrieb:
> Das funktioniert zwar, aber ich kenne #pragma nicht und ich weiß auch
> nicht, wie das mit dem Vector zusammen spielt.

Das ist die zu Deinem Compiler passende Variante, eine Interrupt-Routine 
zu deklarieren. C kennt keine Interrupts, und daher erfindet jeder 
Compilerhersteller eine andere Variante, so etwas zu behandeln. Für den 
MSP430 sind drei Compiler relevant, IAR, CC und gcc.

Wenn Du Dir Codebeispiele ansehen willst, nimm die, die TI für die 
'G2xxx-Familie veröffentlicht hat, die liegen sowohl in IAR- als auch in 
CC-Syntax vor und sie sind auf die Peripheriemodule der 'G2xxx-Reihe 
angepasst.

Die findest Du hier:
(für MSP430G2x53, MSP430G2x33, MSP430G2x13 und MSP430G2x03)
http://www.ti.com/litv/zip/slac485a

und hier:
(für MSP430G2x02, MSP430G2x12, MSP430G2x32 und MSP430G2x52)
http://www.ti.com/litv/zip/slac467a

von Fabian H. (Firma: Technische Universität Berlin) (brein)


Lesenswert?

Danke rufus,

ich bin aber einer, der es genau wissen muss.
Danke erstmal, für den Hinweis, dass das vom Compilerhersteller zu 
Compilerhersteller unterschiedlich ist.
Die Direktive #pragma gibt es doch aber im ANSI-C. Das heißt, so könnte 
mir das doch auch mit anderen Compilern so funktionieren oder?

Da Du ja viel darüber weißt, kannst Du mir folgende Fragen beantworten?
1: Wie genau funktioniert die Interruptlösung mit #pragma?
2: Hast Du einen guten Link zur Erklärung von #pragma?
3: Wird die ISR von TIMERA0_VECTOR immer aufgerufen wenn bei TimerA ein 
Interrupt ausgelöst wird? Egal ob Overflow, CaptureCompare1 oder 2?
4: Diese Interruptdefinition bezieht sich auf welchen Compiler?
In den Beispielen steht ja IAR, ich kompiliere aber mit CCSv5 und da 
steckt meines Wissens MSPGCC in der Toolchain.

Im Zweifel kann ich ja einfach die Struktur übernehmen, egal ob ich sie 
verstehe. Aber ich würde es halt gerne nachvollziehen können.

Vielen Dank
Fabian

von Rufus Τ. F. (rufus) Benutzerseite


Lesenswert?

Fabian Hoemcke schrieb:
> Die Direktive #pragma gibt es doch aber im ANSI-C. Das heißt, so könnte
> mir das doch auch mit anderen Compilern so funktionieren oder?

Nein. Die Direktive #pragma ist eingeführt worden, damit jeder 
Compilerhersteller damit sein eigenes Süppchen kochen kann.

#pragma heißt soviel wie "Achtung, hier kommt was compilerspezifisches 
nichtportables"

Fabian Hoemcke schrieb:
> 1: Wie genau funktioniert die Interruptlösung mit #pragma?

Das weist den Compiler an, Informationen für den Linker im Objektcode zu 
hinterlassen, die wiederum den Linker dazu bringen, die Adresse der auf 
das #pragma folgenden ISR in die Interrupttabelle einzutragen.

Fabian Hoemcke schrieb:
> 3: Wird die ISR von TIMERA0_VECTOR immer aufgerufen wenn bei TimerA ein
> Interrupt ausgelöst wird?

Ja. In der ISR kann die genaue Interruptquelle noch weiter 
aufgeschlüsselt werden, indem das Register TAIV untersucht wird.
Siehe "Family User's Guide", und siehe Codebeispiele von TI -- da steht 
das alles drin.



Nachtrag:
Weitere Codebeispiele:
(für MSP430G2x01, MSP430G2x11, MSP430G2x21 und MSP430G2x31)
http://www.ti.com/litv/zip/slac463a

von Fabian H. (Firma: Technische Universität Berlin) (brein)


Lesenswert?

Rufus Τ. Firefly schrieb:
> Das weist den Compiler an, Informationen für den Linker im Objektcode zu
> hinterlassen, die wiederum den Linker dazu bringen, die Adresse der auf
> das #pragma folgenden ISR in die Interrupttabelle einzutragen.

Kurz um, wenn ein Interrupt ausgelöst wird, wird in die entsprechende 
Stelle des Programmspeichers (Interruptvektor?) gesprungen, in der der 
Call zu dieser Funktion steht.
Und damit das dort eingetragen wird, diese Struktur?
Und genauer kann man es nicht verstehen?
Entschuldige bitte diese Frage. Den restlichen Code und anderen Code 
kann ich ja verstehen. Bei dieser Funktion weiß ich zwar was sie macht, 
aber warum sie es macht ist mir noch nicht ganz klar.
Was ist vector = genau? Was bedeutet __interrupt? Oder ist das so von 
außen gar nicht zu verstehen, weil sich der Hersteller einfach was dabei 
gedacht hat und im Compiler/Linker implementiert hat wie das zu lesen 
und zu verstehen ist und mich das im Grunde gar nichts angeht?

Rufus Τ. Firefly schrieb:
> Ja. In der ISR kann die genaue Interruptquelle noch weiter
> aufgeschlüsselt werden, indem das Register TAIV untersucht wird.
> Siehe "Family User's Guide", und siehe Codebeispiele von TI -- da steht
> das alles drin.

Das heißt also, die Flags (TAIFG, CCIFG, ...) werden zurückgesetzt, in 
der ISR kann man aber die Interrupts in der TAIV Bit1-Bit3 auslesen? Und 
wie das geht, steht im User's Guide? Das habe ich dort leider nicht 
gefunden. Hast Du ein gutes Beispiel (also von TI) dass das mal zeigt? 
Denn
1
__interrrupt void ISR ()
2
{
3
    if( TAIV & TIMERA0_VECTOR == TIMERA0_VECTOR ) // geht sicher viel besser
4
    {
5
        // do
6
    }
7
}
kann nicht funktionieren, da TAIV Bit0 immer 0 ist und das untere 
Nibble des TIMERA0_VECTOR (0x0009) /1001/b ist. Somit ergibt die Abfrage 
immer false.
Oder verstehe ich das falsch?

Danke aber soweit.
Hast mir richtig geholfen.

Gruß
Fabian

von Rufus Τ. F. (rufus) Benutzerseite


Lesenswert?

Fabian Hoemcke schrieb:
> Kurz um, wenn ein Interrupt ausgelöst wird, wird in die entsprechende
> Stelle des Programmspeichers (Interruptvektor?) gesprungen, in der der
> Call zu dieser Funktion steht.

So funktionieren Interrupts nun mal. Auf jedem Prozessor, der so etwas 
hat.

> Und damit das dort eingetragen wird, diese Struktur?

Genau.

> Und genauer kann man es nicht verstehen?

Was soll man da genauer verstehen?

Der Prozessor hat eine hardwaremäßig vorgegebene Tabelle mit 
Interruptvektoren, und wenn ein Interrupt ausgelöst wird, wird die dort 
gespeicherte Adresse angesprungen.

So macht das (annähernd) jeder Prozessor, völlig unabhängig von der 
verwendeten Programmiersprache.

> in
> der ISR kann man aber die Interrupts in der TAIV Bit1-Bit3 auslesen? Und
> wie das geht, steht im User's Guide? Das habe ich dort leider nicht
> gefunden. Hast Du ein gutes Beispiel (also von TI) dass das mal zeigt?

Beispielsweise der Code in msp430g2xx1_ta03.c (aus den von mir 
verlinkten TI-Beispielen) zeigt das:
1
#pragma vector=TIMERA1_VECTOR
2
__interrupt void Timer_A(void)
3
{
4
  switch (TAIV)
5
  {
6
    case 2: 
7
      break;           // CCR1 not used
8
9
    case 4: 
10
      break;           // CCR2 not used
11
12
    case 10: 
13
      P1OUT ^= 0x01;   // overflow
14
      break;
15
  }
16
}

Die Bedeutung von TAIV ist im "Family User's Guide" (slau144i.pdf) auf 
Seite 381 aufgeschlüsselt.

Da ist eine Tabelle der möglichen Inhalte und der zugeordneten 
Bedeutungen.
In TAIV steht also nicht der Interruptvektor drin (wozu sollte das 
auch gut sein?), sondern eben ein numerischer Wert. Und wenn Du Dir 
obiges Codefragment ansiehst, wirst Du die drei Werte, die hier eine 
Bedeutung haben, wiedererkennen.

Wird es jetzt klarer?

von Fabian H. (Firma: Technische Universität Berlin) (brein)


Lesenswert?

Rufus Τ. Firefly schrieb:
> Wird es jetzt klarer?

Ja, woran ich mich nur gestoßen hatte war, wie sind denn alle Interrupts 
dahinein codiert. Aber anscheinend steht da immer nur einer, der höchst 
priorisierte Interrupt drin.

Danke!
Ich glaube, jetzt habe ich alles.

von Märchen Man (Gast)


Lesenswert?

Fabian Hoemcke schrieb:
> Ja, woran ich mich nur gestoßen hatte war, wie sind denn alle Interrupts
> dahinein codiert. Aber anscheinend steht da immer nur einer, der höchst
> priorisierte Interrupt drin.

Der Interrupt wird durch ein Ereignis (z. B. Timer Overflow) ausgelöst. 
Die CPU sichert ein paar Register, ändert ein paar interne Stati und 
springt an die vorgesehene Adresse in der Interruptvektortabelle. Dort 
steht meistens ein Sprung zur Interrupt Service Routine. Mit dem 
besonderen Rücksprung - RETI - wird alles wieder aufgeräumt, alte Stati 
eingestellt, Register restauriert und das Programm fortgesetzt.

Manchmal teilen sich verschiedene Ereignisse einen Interruptvektor. Dann 
wird über Flags unterschieden, was die Ursache war. Die Flags werden oft 
sinnvoll in einem Register zusammen gefasst. So wie im TAIV...

...und wenn sie nicht gestorben sind...

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.