Forum: Mikrocontroller und Digitale Elektronik MSP430F2012 LED Blinken lassen


von m. S. (marek)


Lesenswert?

Hi,

ich mal wider diesmal mit nem anderen Problem, ich arbiete mit nem 
Handbuch wo nicht wirklich beispiele drin sind deswegen probiere ich ein 
bischen rum aber komme gerade nicht weiter. Mein Problem ist folgendes 
hab ne LED an P1.2 die soll mithilfe des Timers Blinken ich hab mir was 
Überlegt aber das funktioniert nicht und ich weis nicht wiso. Vielleicht 
könnt ihr ja mal nen blick rauf werfen und mir unter die Arme greifen 
danke schon mal hier der Code:

#include <msp430x20x2.h>

int main()
{

  WDTCTL = WDTPW + WDTHOLD;
  TACTL = TASSEL1 + TACLR;    //  ????
  TACCR1 = 30000;  //obere grenze bis zu der hochgezählt werden  soll
  TACTL |= MC1;  // das hochgezählt werden soll
  TACCTL1 = OUTMOD_7;// stzen rücksetzen (mener meinung dann von 30000 
auf 0 ???)
  TACCR0 = 0;  // nuntere Grenze

  P1DIR &= 0x00;   //
  P1DIR |= 0x04;  //P1.2 Ausgang rest Eingang
  P1OUT &= 0x00;    //Ausgänge auf 0
  P1IN &= 0x00;    //Eingänge auf 0
  P1SEL &= 0x00;   //I/O funktion festgelegt
  P1REN |=0x02;  //Internen Widerstand zuschalten

  while(1){
  if(TACCR0 == 0){P1OUT = ~0x04;}  //LED an wenn 0
  else{
  if(TACCR1 == 30000){P1OUT = 0x04;}//LED aus wenn 30000
       }
        }

}

von Dennis (Gast)


Lesenswert?

Marek S. schrieb:
> int main()
> {
>
>   WDTCTL = WDTPW + WDTHOLD;
>   TACTL = TASSEL1 + TACLR;    //  ????
>   TACCR1 = 30000;  //obere grenze bis zu der hochgezählt werden  soll
>   TACTL |= MC1;  // das hochgezählt werden soll
>   TACCTL1 = OUTMOD_7;// stzen rücksetzen (mener meinung dann von 30000
> auf 0 ???)
>   TACCR0 = 0;  // nuntere Grenze
>
>   P1DIR &= 0x00;   //
>   P1DIR |= 0x04;  //P1.2 Ausgang rest Eingang
>   P1OUT &= 0x00;    //Ausgänge auf 0
>   P1IN &= 0x00;    //Eingänge auf 0
>   P1SEL &= 0x00;   //I/O funktion festgelegt
>   P1REN |=0x02;  //Internen Widerstand zuschalten
>
>   while(1){
>   if(TACCR0 == 0){P1OUT = ~0x04;}  //LED an wenn 0
>   else{
>   if(TACCR1 == 30000){P1OUT = 0x04;}//LED aus wenn 30000
>        }
>         }
>
> }

Also so geht das leider überhaupt nicht!
Du solltest mit den Beispielen von Ti üben, da ist zu jeder Anwendung 
etwas dabei.

Als Buch kann ich dir die "MSP430 Micrcontroller Basics" von John H. 
Davies sehr empfehlen. Es gibt noch ein anderes Buch von Matthias Sturm, 
aber das ist meiner Meinung nach nicht sehr gut gelungen, vor allem 
nicht, wenn man in C programmiert.

Das hier:
1
while(1)
2
{
3
  if (TACCR0 == 0)
4
  {
5
    P1OUT = ~0x04;}  //LED an wenn 0
6
  else
7
  {
8
    if(TACCR1 == 30000)
9
    {
10
      P1OUT = 0x04;
11
    } //LED aus wenn 30000
12
  }
13
}

TACCRx ist nicht zum vergleichen, der Wert wird sich nicht ändern, wenn 
du ihn einmal gesetzt hast. Du kannst das TAR-Register vergleichen, aber 
auch dafür ist der Timer nicht gedacht.

Wenn du mit CCR0 und CCR1 arbeitest, dann läuft deine Anwendung eher auf 
PWM hinaus, das hast du ja wahrscheinlich noch garnicht vor. Deswegen 
bleib bei einem CCR-Register. Und mach dir z.b. nen Interrupt, in dem du 
die LED togglest.
1
#include <msp430x20x2.h>
2
3
void main (void)
4
{
5
6
  WDTCTL = WDTPW + WDTHOLD;
7
  
8
  P1SEL  = 0x00;  // I/O funktion festgelegt
9
  P1DIR |= 0x04;  // P1.2 Ausgang, Rest Eingang
10
  P1REN |= 0x02;  // Internen Widerstand zuschalten
11
  P1OUT  = 0x00;  // Ausgänge auf 0
12
13
  TACTL = TASSEL_2 + ID_3 + MC_1;  // SMCLOCK + Teiler 8 + Up-Mode
14
  TACCTL0 |= CCIE;
15
  TACCR0 = 65000;  // obere grenze bis zu der hochgezählt werden  soll
16
  
17
  _BIS_SR (GIE);  // Globaler Interrupt aktiviert
18
  
19
  while (1) {}
20
}
21
22
#pragma vector=TIMERB0_VECTOR    // ISR Timer
23
__interrupt void seconds (void)
24
{
25
  P1OUT ^= 0x04;
26
}

Einen Wert an P1IN kannst du eh nicht schreiben, der soll ja von außen 
abgefragt werden.

Gruß, Dennis

von Dennis (Gast)


Lesenswert?

Ups...:
1
#pragma vector=TIMERA0_VECTOR

von Dennis (Gast)


Lesenswert?

Naja und "seconds" muss deine ISR natürlich auch nicht heißen!

von m. S. (marek)


Lesenswert?

Danke dafür schon mal aber es läuft nicht, es kommt aber auch keine 
Fehler meldung, auser das bei #pragma ien ausrufe zeichen vor steht bei 
ausführen. Auserdem würde es bei dir nicht auch nur ein mal blinken und 
nicht kontinuierlich da du ja bis 65000 hochzählst und dann wenn das 
erreicht wird solte es blinken un ddann nie wider, meinem veständnis 
nach.  werde mal rumprobieren hab mir auch gerade was überlegt mal 
schauen ob das klappen wird.

von Dennis (Gast)


Lesenswert?

Was steht denn bei dem Ausrufezeichen für ne Meldung?

Deine LED soll nur einmal kurz blinken? Jetzt in dem Quelltext wird sie 
ständig blinken, klar.

von Dennis (Gast)


Lesenswert?

Das läuft so:

CCR0 ist mit 65000 belegt, also fängt der Controller an, mit jedem Takt 
einen hochzuzählen. Wenn die 65000 erreicht sind, dann wird ein 
Interrupt ausgelöst und in der ISR die LED getoggled. Also wenn die an 
war, geht sie jetzt aus und umgekehrt.

Nach der ISR ist der Zähler wieder auf 0 und beginnt von vorne.

von Dennis (Gast)


Lesenswert?

Also der inter Zähler ist auf 0, nicht der CCR0, der bleibt die ganze 
Zeit auf 65000!

Zähler  CCR0

0       65000 -> loszählen
...
10540   65000 -> nicht gleich, weiterzählen...
...
49000   65000 -> nicht gleich, weiterzählen...
...
65000   65000 -> GLEICH!!! Interrupt -> LED togglen -> Zähler wieder auf 
0
...
0       65000 -> nicht gleich, weiterzählen...

USW.


Verstehst? : )

Gruß, Dennis

von m. S. (marek)


Lesenswert?

Also wenn ich auf das Ausrufe zeichen klike steht da so was
 in nem Fenster wo steht add Bookmark #pragma vector=TIMERA0_VECTOR 
// ISR Timer. Ne die soll schon öfters blinken aber das tut sie gerade 
nicht keine ahnung wiso nicht.

von Dennis (Gast)


Lesenswert?

Ok, womit programmierst du ihn denn? Ich programmier mit IAR, weiß 
natürlich nicht, ob es überall gleich ist.

von m. S. (marek)


Lesenswert?

Das verstehe ich. Ich mach das mit Code Composer Essentials v2 da höhere 
versionen auf der ollen Win 2000 version auf den Rechner wo ich dran 
arbeite nicht laufen.

von m. S. (marek)


Lesenswert?

Ich hab mir mal was neues einfallen lassen ich. Laufen tut es trotzdem 
nicht. Vielleicht weil ich den Timer falsch initialisiere aber wie das 
richtig geht weis ich nicht weil das User Manuell mach mich nicht gerade 
schlauer.
Hier kommt der code:

#include <msp430x20x2.h>

void main(void)
{
  WDTCTL = WDTPW + WDTHOLD;
  TACTL = TASSEL1 + TACLR;    //  ????
  TACTL |= MC_2;// das kontinuirlich gezählt werden soll von 0 bis
                      // ffff dann von 0 bis fffff usw.
  TACTL |= 0x02;  //interupt freigabe


  P1DIR &= 0x00;   //
  P1DIR |= 0x04;  //P1.2 Ausgang rest Eingang
  P1OUT &= 0x00;    //Ausgänge auf 0
  P1SEL &= 0x00;   //I/O funktion festgelegt

  while(1){
  if((TACTL & TAIFG)==1){P1OUT = ~0x04;}//TAIFG das überlauf  forhanden?
  else{P1OUT = 0x04;}
  }
}

von Dennis (Gast)


Lesenswert?

Dann mach es mit dem OUTMOD_7, dann brauchste aber halt noch den CCR1 
den legste dann auf den halben Wert von CCR0.

Musst aber dann gucken mit dem Pin, wo die LED dran ist, der muss 
natürlich zum Timer gehören und dann musste noch die Sonderfunktion mit 
P1SEL |=0x__ einstellen.

Guck mal ins Handbuch von dem Controller!

von Dennis (Gast)


Lesenswert?

Achja, ISR kannste dann natürlich löschen, die Interruptfreigabe auch 
und das CCIE ebenfalls.

von m. S. (marek)


Lesenswert?

So funst das auch aber ich weis noch nicht wiso muß ich mir mal 
überlegen und schauen wiso das geht. Hab das gerade kopiert aber 
verstehe das nicht wirklich schaue ich mir mal und lese mal weiter das 
Manuel aber danke für deine Hilfe.

#include <msp430x20x2.h>

void main (void)
{

  WDTCTL = WDTPW + WDTHOLD;
  TACTL = TASSEL_2 + TACLR + TAIE;  //SMCLK + Timer löschen

  CCTL0 = OUTMOD_4;      //up mode
  CCR0 = 0xFFFF;

  CCTL1 = OUTMOD_7;      //reset/set
  CCR1 = 0x7FFF;

  P1DIR |= 0x04;        //P1.2 ausgang
  P1SEL |= 0x04;        //P1.2 = TA1 ausgang

  TACTL |= MC1;        // up mode

  while(1);
}


mfg
Marek

von Stefan (Gast)


Lesenswert?

Marek S. schrieb:
> So funst das auch aber ich weis noch nicht wiso muß ich mir mal
> überlegen und schauen wiso das geht.

Ich würde Dir den User-Guide trotzdem nochmal eindringlich empfehlen!
Besonders das Thema "Basic Clocks" des MSP430.

Es macht nämlich einen entscheidenden Unterschied, ob der Timer mal mit 
ACLK oder mit SMCLK gespeist wird...

von m. S. (marek)


Lesenswert?

@Stefan(Gast)

Danke für den hinweis. Das hab ich die ganze Zeit vor mir aber, woher 
soll ich denn wissen wann ich welche zu benutzen habe wenn mir das 
keiner sagt und aus dem was da drinne steht wird man nicht so recht 
schlau. Ich beschäftige mich erst seit gestern damit zumindest mit dem 
mikrocontroller davor einen anderen gehabt der war komplexer. Da mußte 
ich aber auch nicht alleine dran sitzen und da wurde wir halb drauf 
gestoßen wo wir nachschauen sollen und jetzt sitzt du da hast keine 
Ahnung und hast ne idee und fängst einfach an weil du denkst das könnte 
gehen geht dann aber nicht und dann ist man verzweifelt warum das nicht 
geht. Woran kanst du denn erkennnen welcher Timer besser geeignet ist 
für die aufgabe die ich lösen sollte. Da ich das gerne verstehen würde 
wiso das so geht in dem von mir zuletzt eingefügten code. So in der 
schreib weise sehe ich das auch zum ertsen mal z.b.
 TACTL = TASSEL_2 + TACLR + TAIE; das steht da auch nirgends drinne das 
man das so machen soll. Ich danke dir schon jetzt für deine antwort.

mfg

marek

von Stefan (Gast)


Lesenswert?

Marek S. schrieb:
> Danke für den hinweis. Das hab ich die ganze Zeit vor mir aber, woher
> soll ich denn wissen wann ich welche zu benutzen habe wenn mir das
> keiner sagt
Das kann dir keiner sagen, weil die Anforderungen für jeden anders sind!
Deshalb mein Hinweis auf die "Basic Clocks" im User Guide. Man sollte 
verstehen, dass es verschiedene Clocks gibt, die man aus 
unterschiedlichen Quellen und mit unterschiedlichen Frequenzen takten 
kann!

> und aus dem was da drinne steht wird man nicht so recht
> schlau.
Aller Anfang ist schwer... aber ich finde den User Guide sooo schlecht 
nicht?!

> So in der
> schreib weise sehe ich das auch zum ertsen mal z.b.
>  TACTL = TASSEL_2 + TACLR + TAIE; das steht da auch nirgends drinne das
> man das so machen soll. Ich danke dir schon jetzt für deine antwort.
Nun, das ist lediglich das Setzen der einzelnen Bits im Register TACTL. 
Das ist aber alles genau im User Guide beschrieben!
Das einzig "ungewöhliche" ist die am Anfang vielleicht etwas verwirrende 
Schreibweise TASSEL_2 im Unterschied zu TASSEL1
Das eine sind die einzelnen Bits (TASSEL0 und TASSEL1), während TASSEL_x 
die Kombination beider Bits repräsentiert:
1
TASSEL_0 =     0
2
TASSEL_1 =     0   + TASSEL0
3
TASSEL_2 = TASSEL1 +    0
4
TASSEL_3 = TASSEL1 + TASSEL0

[... im Übrigen hättest Du somit schon erkennen können/müssen, dass ich 
oben Schmarrn geschrieben habe, denn Dein Timer wurde immer mit SMCLK 
betrieben, da TASSEL_2 = TASSEL1 ;-) ]

von Stefan (Gast)


Lesenswert?

Marek S. schrieb:
> Ich hab mir mal was neues einfallen lassen ich. Laufen tut es trotzdem
> nicht.
>
> #include <msp430x20x2.h>
>
> void main(void)
> {
>   WDTCTL = WDTPW + WDTHOLD;
>   TACTL = TASSEL1 + TACLR;    //  ????
>   TACTL |= MC_2;// das kontinuirlich gezählt werden soll von 0 bis
>                       // ffff dann von 0 bis fffff usw.
>   TACTL |= 0x02;  //interupt freigabe
>
>
>   P1DIR &= 0x00;   //
>   P1DIR |= 0x04;  //P1.2 Ausgang rest Eingang
>   P1OUT &= 0x00;    //Ausgänge auf 0
>   P1SEL &= 0x00;   //I/O funktion festgelegt
>
>   while(1){
>   if((TACTL & TAIFG)==1){P1OUT = ~0x04;}//TAIFG das überlauf  forhanden?
>   else{P1OUT = 0x04;}
>   }
> }

Das funzt nicht, weil:

1.) TACTL |= 0x02;  //interupt freigabe
Du gibst einen Interrupt frei, der auch eintreten wird.... ABER Du 
hast keine Interrupt-Service-Routine definiert... Dein Controller hängt 
sich auf!

2.) if((TACTL & TAIFG)==1)
TAIFG muss hier per Software zurückgesetzt werden.

von Dennis (Gast)


Lesenswert?

Zieh dir mal die Code-Examples von Ti, da sind ne Menge Anwendungen drin 
beschrieben.

Wir mussten uns während des Studiums das "ez430F2013"-Entwicklungs-Tool 
besorgen und ab da gabs leider keine große Hilfe mehr. Aber unsere 
Praktikumsaufgaben mussten wir auch abgeben. Und ne Klausur mit 
Programmieraufgabe kam dann natürlich auch noch. Vom Unterricht her 
keine wirkliche Hilfe zur Programmierung...:\ War ziemlich blöd.

Uns wurde angeraten, mit den Ti-Examples zu arbeiten, das waren unsere 
einzigen Hilfen dazu, aber da ist echt ne Menge dabei.

Wir sollten z.B. in einer Aufgabe ne PWM programmieren und haben einfach 
mal drauf losgelegt...vieeel zu umständlich - hier meine Aufgabenlösung, 
nur mal zur Veranschaulichung:
1
/*********************************************************
2
 * Embedded Systems: MSP430F2013 Education Tool          *
3
 *********************************************************
4
 * Dennis Eichmann (11049644) SS09                       *
5
 *                                                       *
6
 * Aufgabe 3.2: PWM / Analogausgabe                      *
7
 * --------------------------------                      *
8
 *                                                       *
9
 * Ausgabe eines stetig an- und absteigenden 8Bit        *
10
 * PWM-Signals am Port P1.0 (LED)                        *
11
 * Die LED soll damit gedimmt, bzw sanft ein- und aus-   *
12
 * geschaltet werden                                     *
13
 *                                                       *
14
 *********************************************************
15
 * Konfiguration des MSP430F2013:                        *
16
 * ------------------------------                        *
17
 *                                                       *
18
 * P1.0: LED gegen Masse                                 *
19
 * P1.1: Taster gegen Masse                              *
20
 * P1.4: Schleifer Potentiometer (3V3 / Masse)           *
21
 *       -> Bei Benutzung S1 schliessen!                 *
22
 * P1.5: Schalter gegen Masse                            *
23
 * P1.6: Draht zur freien Benutzung                      *
24
 *       -> Fuer Ausgang S1 oeffnen!                     *
25
 *                                                       *
26
 *********************************************************
27
 * Port-Verwendung:                                      *
28
 * ----------------                                      *
29
 *                                                       *
30
 * P1IN:  Auslesen gibt logischen Wert zurueck           *
31
 * P1OUT: Schreiben legt logischen Wert an               *
32
 * P1DIR: logische '0' setzt Pin auf Eingang (defaut)    *
33
 *        logische '1' setzt Pin auf Ausgang             *
34
 * P1REN: '1' aktiviert Widerstand (dafault: inaktiv)    *
35
 *        Bitmuster P1OUT: Pull- Up('1') / Down('0')     *
36
 * P1SEL: '0' fuer Standard I/O (default)                *
37
 *        '1' fuer alternative Funktionen                *
38
 * P1IE:  Interrupt bei Pegelwechsel; default '0' (aus)  *
39
 *        aktiv bei '1'                                  *
40
 * P1IES: Interrupt positive ('0') / negative ('1')      *
41
 *        Flanke                                         *
42
 * P1IFG: Interrupt-Flag-Bit - nach IRQ loeschen         *
43
 *        Auch fuer Software-Interrupt                   *
44
 *                                                       *
45
 * IRV: #pragma vector=PORT1_VECTOR                      *
46
 *                                                       *
47
 *********************************************************
48
 * Maskierungen von Bits:                                *
49
 * ----------------------                                *
50
 *                                                       *
51
 * OR:  Verknuepfung mit '0' laesst Wert unveraendert    *
52
 *      Verknuepfung mit '1' aendert Wert zu '1'         *
53
 *      P1OUT = P1OUT | BIT1 oder P1OUT |= BIT1          *
54
 * AND: Verknuepfung mit '1' laesst Wert unveraendert    *
55
 *      Verknuepfung mit '0' aendert Wert zu '0'         *
56
 *      P1OUT &= ~BIT1 -> '~' AND mit '0'                *
57
 * XOR: (TOGGLE) Wechsel von '0' nach '1' oder umgekehrt *
58
 *      P1OUT ^= BIT1                                    *
59
 *                                                       *
60
 *********************************************************
61
 * 16-Bit-Sigma-Delta-ADC Einstellungen:                 *
62
 * -------------------------------------                 *
63
 *                                                       *
64
 * SD16 INput ConTroL Register:                          *
65
 * SD16INCTL = SD16INCH_ 1 (Eingang des Multiplexers)    *
66
 *                       2 ... (6 fuer Temperatursensor) *
67
 *                                                       *
68
 * SD16 ConTroL Register:                                *
69
 * SD16CTL = SD16SSEL_ + SD16REFON (fuer interne 1V2)    *
70
 *                                                       *
71
 * SD16SSEL_0 MCLK  Master Clock                         *
72
 *          1 SMCLK Subsystem Master Clock               *
73
 *          2 ACLK  Auxiliary Clock                      *
74
 *          3 TACLK Externes Clock-Signal                *
75
 * (1,1MHz max)                                          *
76
 *                                                       *
77
 * SD16 Capture ConTroL Register:                        *
78
 * SD16CCTL0 = SD16IE + SD16SNGL + SD16UNI               *
79
 *             Interrupt Enable (Interrupt wenn fertig)  *
80
 *             Single Conversion (einfache Wandlung)     *
81
 *             Unipolar (Spannung gegen GND)             *
82
 *                                                       *
83
 * Wandlung starten mit: SD16CCTL0 |= SD16SC             *
84
 *                       SD16 Start Conversion           *
85
 *                                                       *
86
 * Ergebnis auslesen:    Variable = SD16MEM0             *
87
 *                       SD16 Memory 0                   *
88
 *                                                       *
89
 * IRV: #pragma vector=SD16_VECTOR                       *
90
 *                                                       *
91
 *********************************************************
92
 * 16-Bit-Timer Einstellungen:                           *
93
 * ---------------------------                           *
94
 *                                                       *
95
 * Timer A ConTroL-Register:                             *
96
 * TACTL = TASSEL_ + ID_ + MC_                           *
97
 *                                                       *
98
 * Timer A Source SELect:                                *
99
 * TASSEL_0 TACLK  Externes Clock-Signal                 *
100
 *        1 ACLK   Auxiliary Clock (externer Quarz)      *
101
 *        2 SMCLK  Subsystem Master Clock                *
102
 *        3 INCLK  Invertierter TACLK                    *
103
 *                                                       *
104
 * Input Divider:                                        *
105
 * ID_0 Teiler durch 1 (keine Teilung)                   *
106
 *    1 Teiler durch 2                                   *
107
 *    2 Teiler durch 4                                   *
108
 *    3 Teiler durch 8                                   *
109
 *                                                       *
110
 * Mode Control:                                         *
111
 * MC_0 Stop Mode (Timer haelt ohne Aenderung an)        *
112
 *    1 Up-Mode (Zaehlt bis zu angegebenen Wert CCR0)    *
113
 *    2 Continous Mode (Zaehlt immer wieder bis Maximum) *
114
 *    3 Up-/Down-Mode (Zaehlt bis CCR0, dann zurueck)    *
115
 *                                                       *
116
 * CCTL0 = CCIE: Interrupt durch Capture / Compare       *
117
 * IRV: #pragma vector=TIMAERA0_VECTOR                   *
118
 *                                                       *
119
 *********************************************************
120
 * Low-Power-Modi:                                       *
121
 * ---------------                                       *
122
 *                                                       *
123
 *        | LPM0  LPM1  LPM2  LPM3  LPM4                 *
124
 * -------------------------------------                 *
125
 * CPU    | aus   aus   aus   aus   aus                  *
126
 * MCLK   | aus   aus   aus   aus   aus                  *
127
 * SMCLK  | an    an    aus   aus   aus                  *
128
 * ACLK   | an    an    an    an    aus                  *
129
 * DCOdc  | an    aus   an    aus   aus                  *
130
 * DCOosc | an    an    aus   aus   aus                  *
131
 *                                                       *
132
 * Im LPM4 nurnoch Interrupts durch Portpins             *
133
 *                                                       *
134
 * Low-Power-Mode aktivieren: _BIS_SR(LPM0_bits + GIE)   *
135
 * Low Power Mode verlassen:  _BIC_SR_IRQ(LPM0_bits)     *
136
 *                                                       *
137
 *********************************************************
138
 * Globale Interrupts zulassen: _BIS_SR(GIE)             *
139
 *********************************************************
140
 * HEX - binaer:                                         *
141
 * -------------                                         *
142
 *                                                       *
143
 * 0x00 0000 0000     0x06 0000 0110     0x0C 0000 1100  *
144
 * 0x01 0000 0001     0x07 0000 0111     0x0D 0000 1101  *
145
 * 0x02 0000 0010     0x08 0000 1000     0x0E 0000 1110  *
146
 * 0x03 0000 0011     0x09 0000 1001     0x0F 0000 1111  *
147
 * 0x04 0000 0100     0x0A 0000 1010     0x10 0001 0000  *
148
 * 0x05 0000 0101     0x0B 0000 1011     0x11 0001 0001  *
149
 * ...                                                   *
150
 * 0x22 0010 0010     0xDD 1101 1101     0xFF 1111 1111  *
151
 *                                                       *
152
 *********************************************************/
153
154
155
#include "msp430x20x3.h"
156
157
int periodendauer = 255;                    // 8Bit Periodendauer
158
int pulsweite = 1;                          // Pulsweite wird in Schritten von 1 geaendert
159
int anaus = 0;                              // Variable zum Wechsel zwischen an- und aus-Zeit
160
int hochrunter = 0;                         // '0' fuer heller, '1' fuer dunkler
161
162
163
void aenderung (void)                       // Routine fuer heller / dunkler und Pulsweitenaenderung
164
{
165
  switch (hochrunter)                       // Abfrage ob heller oder dunkler werden
166
  {
167
    case 0:                                 // hochrunter ist '0' -> heller werden
168
    {
169
      pulsweite = pulsweite + 1;            // Pulsweite um 1 erhoehen -> laengere an- / kuerzere aus-Zeit
170
      
171
      if (pulsweite >= 255)                 // Wenn pulsweite 255 erreicht hat (an maximal / aus minimal)
172
      {
173
        hochrunter = 1;                     // hochrunter auf '1' -> ab jetzt dunkler werden
174
      }
175
      
176
      break;
177
    }
178
179
    case 1:                                 // hochrunter ist '1' -> dunkler werden
180
    {
181
      pulsweite = pulsweite - 1;            // Pulsweite um 1 verringern -> laengere aus- / kuerzere an-Zeit
182
      
183
      if (pulsweite <= 1)                   // Wenn pulsweite 1 erreicht hat (an minimal / aus maximal)
184
      {
185
        hochrunter = 0;                     // hochrunter auf '0' -> ab jetzt heller werden
186
      }
187
      
188
      break;
189
    }
190
    
191
    default: break;
192
  }
193
}
194
195
196
int main( void )
197
{
198
  WDTCTL = WDTPW + WDTHOLD;                 // Watchdog-Timer ausschalten
199
200
  P1DIR |= 0x01;                            // Setzt P1.0 auf Ausgang (LED)
201
  
202
  CCTL0 = CCIE;                             // Interrupt durch CCR0 moeglich
203
  CCR0 = periodendauer;                     // 
204
  TACTL = TASSEL_2 + ID_3 + MC_1;           // SMCLK + Teiler 8 + UpMode
205
  CCR0 = periodendauer;                     // Startet Capture-Compare und zaehlt bis 255 -> dann Interrupt
206
  
207
  _BIS_SR(LPM1_bits + GIE);                 // Low-Power-Mode 1 + Globaler Interrupt aktiviert
208
}  
209
210
211
#pragma vector=TIMERA0_VECTOR               // Interrupt-Service-Routine beim Erreichen von CCR0
212
__interrupt void timer (void)
213
{
214
  
215
  switch(anaus)                             // Abfrage: LED an oder aus
216
  {
217
    case 0:                                 // LED an-Zeit
218
    {
219
      CCR0 = pulsweite;                     // CCR0 bekommt Wert von pulsweite (steigt und faellt)
220
      P1OUT |= 0x01;                        // LED anschalten
221
      anaus = 1;                            // anaus jetzt '1' -> naechste Runde LED aus-Zeit
222
      break;
223
    }
224
    
225
    case 1:                                 // LED aus-Zeit
226
    {
227
      CCR0 = periodendauer - pulsweite;     // CCR0 bekommt Wert von periodendauer - pulsweite (steigt und faellt)
228
      P1OUT &= ~0x01;                       // LED ausschalten
229
      anaus = 0;                            // anaus jetzt '0' -> naechste Runde LED an-Zeit
230
      
231
      aenderung();                          // Aufruf der Routine zuer Aenderung der Richtung und der Pulsweite
232
      
233
      break;
234
    }
235
  
236
    default: break;
237
  }
238
}

So, der Anfang ist natürlich nur Kommentar, damit ich mir nicht immer 
alles wieder raussuchen musste, daher habe ich das in jede Aufgabe mit 
reinkopiert.

Aber guck dir mal den Teil für die PWM an, Horror! Mit dem "OUTMOD" 
kannste das ganze z.B. bequem in Hardware lösen und der Quelltext würde 
fast keine Zeile an Code mehr beeinhalten, außer die Konfiguration 
natürlich - nur die CCR-Register müsste man periodisch ändern. Aber die 
verbesserte Version finde ich grad nicht.

Nur worauf ich hinaus will: Wie man es am einfachsten macht, hat man 
dann in den Code-Examples gefunden - muss man halt nurmal reingucken :-)

Nichts desto trotz, vielleicht hilft dir ja auch mein Kommentar am 
Anfang des Codes, du fängst ja quasi auch gerade genau da an - dann 
musst du vorerst nicht immer für jede Info das Manual wälzen...aber das 
wirst du später noch oft genug :P Frohes Schaffen!


Gruß, Dennis

von Dennis (Gast)


Lesenswert?

Stefan schrieb:
> TACTL |= 0x02;  //interupt freigabe

Sowas ist eh ne Katastrophe! Du weisst vielleicht jetzt, dass das das 
Bit für TAIE ist, aber niemand anderes kann das im nachhinein 
nachvollziehen - ganz ehrlich ich weiß grad auch nicht, ob es das Bit 
ist, das müsste ich jetzt selber nachgucken...OK, ich hab grad 
nachgeguckt, es stimmt! Aber benutz doch die Synonyme, wenn es sie schon 
gibt, sonst wird da keiner mehr Lust haben, drüber zu gucken.

von m. S. (marek)


Lesenswert?

Ales klar ich danke euch für die Unterstützung ich beschäftige mich 
weiter hin damit vielleicht lege ich mir doch mal ein Buch für zu um 
dann einiges besser zu verstehen investitionen müßen halt auch mal getan 
werden aber viel dank für eure anregungen. Bei weiteren problem werde 
ich mich bestimmt noch mal melden. Werde mir jetzt noch mal das blinken 
zu gemüte führen wiso das funktioniert wie es funktioniert mann soll das 
ja auch verstehen können. Die beispiele von TI hab ich mir gestern 
runter geladen und werde mir die auch mal heute anschauen. Ansonsten 
genist die Sonne heut ebei 30°C sollen es in Berlin werden und ich hänge 
vor dem Rechner naaj es gibt schlimmeres. Bis denn.

mfg

MArek

von Dennis (Gast)


Angehängte Dateien:

Lesenswert?

Sei froh, ich muss nach der Arbeit noch meine Terrasse im Garten weiter 
bauen, da hoffe ich, dass es keine 30° gibt :)

Pass auf, ich lad dir noch mein anderen Praktikumsaufgaben hoch, 
vielleicht kannste da ja mal drauf gucken, immerhin haben wir ja genau 
da angefangen, wo du grad bist.

Die sind zwar für den 2013, aber du benutzt ja nen 2012, die sind ja 
nahezu gleich, nur dass der 2013 nen 16Bit AD hat.


Viel Spaß noch!


Dennis

von Dennis (Gast)


Lesenswert?

Sorry, ist ein bisschen groß, hab grad gesehen, dass in einer Aufgabe 
ein Video von der PWM drin war.

von m. S. (marek)


Lesenswert?

Kein Problem ich werde das auch schon noch ihrgend wie hin kriegen ander 
haben das ja auch vor mir geschaft dann solt eich das auch ihrgend wie 
schafen. Dir auch noch viel spaß beim Bauen.

mfg

von m. S. (marek)


Lesenswert?

Ich hab mich mal wider beschäftigt mit dem Blinken. Ich hab hir mal was 
zusammen geschrieben was meiner meinung nach gehen solte aber ich glaube 
er geht nicht in die interrupt service routine ich weis aber nicht wiso. 
Wenn wer kurz zeit hat mal nen blick drauf zu werfen und mir vielleicht 
nen hinweis geben kann oder sagen kann wo da der fehler ist würde mir 
helfen. Danke schon mal.

#include <msp430x20x2.h>

int main( void )
{
  WDTCTL = WDTPW + WDTHOLD;           // Watchdog-Timer ausschalten
  TACTL = TASSEL_2 + MC_1;           // SMCLK + UpMode

  P1DIR |= 0x04;                      // Setzt P1.2 auf Ausgang
  CCTL0 = CCIE;                      // Interrupt durch CCR0
  CCR0 = 0xFFFF; // Startet Capture-Compare und zählt bis FFFF
  _BIS_SR(GIE);                 // Globaler Interrupt enable
}
// Timer interrupt service routine
__interrupt void Timer(void)
{
   P1OUT ^= 0x04;                          // Toggle P1.2
}

von Dennis (Gast)


Lesenswert?

Marek S. schrieb:
> CCTL0 = CCIE;                      // Interrupt durch CCR0
>   CCR0 = 0xFFFF; // Startet Capture-Compare und zählt bis FFFF

Er geht da rein, weil du es so wolltest!

von test (Gast)


Lesenswert?

setz mal einen Breakpoint dann siehst du ob er da rein geht.

und InterruptFlag muss wieder gelöscht werden...

von Dennis (Gast)


Lesenswert?

Aso, sorry, hab das nicht überlesen.

Dir fehlt der Interrupt Vektor!

#pragma vector=timera0_vector

von Dennis (Gast)


Lesenswert?

test schrieb:
> und InterruptFlag muss wieder gelöscht werden...

Das Flag löscht sich von selber in der ISR.

von m. S. (marek)


Lesenswert?

Das ist ne Tierisch schwere Geburt mit dem Ding. Ich hab das jetzt mal 
eingefügt den Vektor woher weis ich denn wie ich den Zu bennen habe nur 
mal so neben bei?
#include <msp430x20x2.h>

int main( void )
{
  WDTCTL = WDTPW + WDTHOLD;           // Watchdog-Timer ausschalten
  TACTL = TASSEL_2 + MC_1;           // SMCLK + UpMode

  P1DIR |= 0x04;                      // Setzt P1.2 auf Ausgang
  CCTL0 = CCIE;                      // Interrupt durch CCR0
  CCR0 = 0xFFFF; // Startet Capture-Compare und zählt bis FFFF
  _BIS_SR(GIE);                 // Globaler Interrupt enable
}
// Timer interrupt service routine
#pragma VECTOR=TIMERA0_VECTOR
__interrupt void Timer(void)
{
   P1OUT ^= 0x04;                          // Toggle P1.2
}

Funktionieren tut es trotzdem nicht. Im Datenblat steht an dem PIN1.2 
noch TA1 also dachte ich mal machste aus dem VECTOR=TIMERA0_VECTOR ein 
VECTOR=TIMERA1_VECTOR aber gehen tut es dann trotzdem nicht. Langsam 
gehen mir die ideen aus.

mfg

von Dennis (Gast)


Lesenswert?

Marek S. schrieb:
> #pragma VECTOR=TIMERA0_VECTOR

Das hat mit TA1 nichts zu tun - der TimerA0 bezieht sich auf CCR0!

Deine main() braucht noch eine
1
while (1) {}

von Stefan (Gast)


Lesenswert?

Dennis schrieb:
> Deine main() braucht noch einewhile (1) {}
Wäre ein "schöner" Programmierstil braucht's aber nicht, weil das der 
IAR automatisch macht.

Marek S. schrieb:
> Funktionieren tut es trotzdem nicht.
Was bedeutet das jetzt mal ganz genau?
Springt er in der IRQ oder nicht?
Nur mal so als Hinweis... Deine LED würde in deiner jetzigen 
Konfiguration mit ca. 7Hz blinken...

von Dennis (Gast)


Lesenswert?

Stefan schrieb:
> Wäre ein "schöner" Programmierstil braucht's aber nicht, weil das der
> IAR automatisch macht.

Besser wär noch ihn schlafen zu schicken, aber das is wohl erstmal nicht 
wichtig. Macht IAR das? Wusste ich garnicht, dachte er meckert dann mit 
"program exit reached" oder so ähnlich rum...nun ja, er benutzt aber eh 
CCE.

Stefan schrieb:
> Konfiguration mit ca. 7Hz blinken...

Das kommt hin, das sollte er noch sehen mit bloßem Auge. Ansonsten noch 
nen Divider rein.

von Dennis (Gast)


Lesenswert?

Marek S. schrieb:
> int main( void )

Meckert er eigentlich nicht, wegen fehlendem Rückgabewert?
1
#include <msp430x20x2.h>
2
3
void main (void )
4
{
5
  WDTCTL = WDTPW + WDTHOLD;          // Watchdog-Timer ausschalten
6
  P1SEL  = 0x00;                     // Port 1 komplett GPIO
7
  P1OUT  = 0x00;                     // Port 1 Ausgaenge auf 'low'
8
  P1DIR  = 0x04;                     // P1.2 Ausgang, Rest Eingang
9
10
  CCTL0 |= CCIE;                     // CCR0 kann Interrupt ausloesen
11
  CCR0   = 0xFFFF;                   // CCR0 laden
12
13
  TACTL = TASSEL_2 + MC_1;           // SMCLK + UpMode
14
  
15
  unsigned long int i;
16
17
  for (i=0; i<10000000; i++)         // Schleife zur LED-Kontrolle
18
  {
19
    P1OUT |= 0x04;                   // LED an
20
  }
21
22
  P1OUT &= ~0x04;                    // LED aus
23
24
25
  _BIS_SR (GIE);                     // Globale Interrupts aktiv
26
27
  while (1)                          // Nichts tun...
28
  {}
29
}
30
31
#pragma vector=timera0_vector
32
__interrupt void Timer(void)
33
{
34
   P1OUT ^= 0x04;                    // Toggle LED
35
}

Kopier das mal bitte in deinen Compiler! Und sag mal Bescheid, ob die 
LED am Anfang wenigstens kurz angeht.

Dennis

von m. S. (marek)


Lesenswert?

Also ich machen step bei step und das geht nur bis while(1) und bleibt 
dann da stehen ohne das ein step in die Routine rein geht. Ich weis auch 
nicht wiso, es kommt auch keine Fehlermeldung und ich hab keine ahnung 
wiso. Die LED funktioniert aber.

#include <msp430x20x2.h>

int main( void )
{
  WDTCTL = WDTPW + WDTHOLD;           // Watchdog-Timer ausschalten
  TACTL = TASSEL_2 + MC_1;           // SMCLK + UpMode

  P1DIR |= 0x04;                      // Setzt P1.2 auf Ausgang
  CCTL0 = CCIE;                      // Interrupt durch CCR0
  CCR0 = 0xFFFF; // Startet Capture-Compare und zählt bis FFFF
  _BIS_SR(GIE);                 // Globaler Interrupt enable
 while(1);
}
// Timer interrupt service routine
#pragma VECTOR=TIMERA0_VECTOR
__interrupt void Timer(void)
{
   P1OUT ^= 0x04;                          // Toggle P1.2
}

von Dennis (Gast)


Lesenswert?

Dennis schrieb:
> WDTCTL = WDTPW + WDTHOLD;          // Watchdog-Timer ausschalten

Mach daraus mal:
1
WDTCTL = (WDTPW | WDTHOLD);

und hier:

TACTL = TASSEL_2 + MC_1;
1
TACTL = (TASSEL_2 | MC_1);

Vielleicht ist das auch Compiler-abhängig...

von m. S. (marek)


Lesenswert?

So jetzt geht das ich hab rausgefunden woran das ligt schau dir mal den 
unteren teil an also da wo die ISR ist. So wie es da steht geht das 
jetzt. Aber viellen viellen dank trotzdem.

#include <msp430x20x2.h>

int main( void )
{
  WDTCTL = (WDTPW | WDTHOLD);           // Watchdog-Timer ausschalten
  TACTL = (TASSEL_2 | MC_1);           // SMCLK + UpMode

  P1DIR |= 0x04;                      // Setzt P1.2 auf Ausgang
  CCTL0 = CCIE;                      // Interrupt durch CCR0
  CCR0 = 0xFFFF; // Startet Capture-Compare und zählt bis FFFF
  _BIS_SR(GIE);                 // Globaler Interrupt enable
 while(1){}
}
// Timer interrupt service routine
__interrupt void Timer(void);
TIMERA0_ISR(Timer)
__interrupt void Timer(void)
{
   P1OUT ^= 0x04;                          // Toggle P1.2
}

mfg

von Dennis (Gast)


Lesenswert?

Marek S. schrieb:
> TIMERA0_ISR(Timer)

Also liegt es nur daran? Wegen dem CCE?

von Dennis (Gast)


Lesenswert?

Übrigens: Ich hoffe, du hast da nicht ne normale LED mit 20mA dran! Das 
tut dem MSP nicht so gut :) 2mA solltest du für den Dauerbetrieb nicht 
überschreiten.

von m. S. (marek)


Lesenswert?

Jo ich denke mal ja da die Version so alt ist kennst der Debbuger und so 
das mit #pragma ... nicht aber auf dem rechner wo ich nun mal dranssitze 
läuft nichts besseres. Vielleicht holle ich mir mal so einen 
Programieradapter und nen microcontroller und werde mich zuhause damit 
gescheit beschäftigen.

mfg

von Stefan (Gast)


Lesenswert?

Marek S. schrieb:
> Jo ich denke mal ja da die Version so alt ist kennst der Debbuger und so
> das mit #pragma ... nicht

Nun ja, jede Toolchain hat da ihre eigene Art der Definition.
Bin allerdings (fälschlicherweise) die ganze Zeit davon ausgegangen, 
dass Du auch IAR benutzt... da hätte 'pragma... gepasst ;-)

von m. S. (marek)


Lesenswert?

Na da bin ich mal wider. Ich hab gerade Probiert das blinken so zu 
steuern wenn ein Interrupt an P1.1 soll es blinken. Ich hab an P1.1 
einen Taster.
Es kommt keine Fehler meldung und es Blinkt auch fleisich aber die 
ganzezeit egal ob ich den Taster drücke oder nicht. Weil mit der do 
while schleife sage ich ihm doch blos das er den Interupt auslösen soll 
wenn ich den taster gedruckt habe oder verstehe ich da was falsch. Danke 
mal wider. Ist es sin voll das so zu machen ich hab auch an einen 
Interupt in der Interupt routine des Tasters gedacht von wo aus das dan 
getoggelt wird. Man hat mir aber davon abgeraten. Was meint ihr.


#include <msp430x20x2.h>

int main( void )
{
  WDTCTL = WDTPW + WDTHOLD;           // Watchdog-Timer ausschalten
  TACTL = TASSEL_2 + ID_3 + MC_1;    // SMCLK + Teiler durch 8  + UpMode

  P1DIR |= 0x04;  // Setzt P1.2 auf Ausgang

  P1IES = 0x02;    //P1.1 An dem der Taster hängt (L -> H wechsel)
  P1IE = 0x02;    // Interrupt enable für P1.1

  do{
  CCTL0 = CCIE;                  // Interrupt durch CCR0
  CCR0 = 0xFFFF; // Startet Capture-Compare und zählt bis FFFF
  _BIS_SR(GIE);}                // Globaler Interrupt enable
  while((P1IFG & 0x02)==1);
}

// Timer interrupt service routine
__interrupt void Timer(void);
TIMERA0_ISR(Timer)
__interrupt void Timer(void)
{
      P1OUT ^= 0x04;                          // Toggle P1.2
}

von Stefan (Gast)


Lesenswert?

Marek S. schrieb:
> wenn ein Interrupt an P1.1 soll es blinken.
Dein Polling des P1IFG-Flags kann man nicht wirklich als Interrupt 
bezeichnen ;-)  Wenn schon Interrupt, dann aktiviere ihn auch und 
schreibe eine Interrupt-Routine dazu.
ABER: Bei Tasten ist ein Interrupt keine gute Wahl!
Du musst den Taster entprellen!

> while schleife sage ich ihm doch blos das er den Interupt auslösen soll
> wenn ich den taster gedruckt habe oder verstehe ich da was falsch.
Ja, allerdings.
Zuerst musst Du Deine ganze Peripherie (hier der Timer)einmal 
initialisieren. Danach würde ich den Timer je nach Zustand des Tasters 
entweder aktivieren oder stoppen.
Deine do-while ist kompletter Murks!

> ich hab auch an einen
> Interupt in der Interupt routine des Tasters gedacht von wo aus das dan
> getoggelt wird. Man hat mir aber davon abgeraten. Was meint ihr.
Derjenige, der Dir abgeraten hat ist ein kluger Kopf!
Selbst sehr Erfahrene Programmierer meiden verschachtelte Interrupts so 
gut wie immer! Kann sehr unübersichtlich werden...

von m. S. (marek)


Lesenswert?

Stefan schrieb:
> Zuerst musst Du Deine ganze Peripherie (hier der Timer)einmal
> initialisieren.

Hab ich das nicht schon damit gemacht Hab ich ihn nicht schon damit
TACTL = TASSEL_2 + ID_3 + MC_1;. Muß ich das dann noch mal machen in der 
while schleife?

von Stefan (Gast)


Lesenswert?

Marek S. schrieb:
> Muß ich das dann noch mal machen in der
> while schleife?
Na eben nicht!

In while() überprüfst Du nur noch, ob die Taste gedrückt ist oder 
nicht.

Hier eine Möglichkeit von zig tausend...:
1
volatile char  LED_Enable;
2
3
void main( void )
4
{
5
  WDTCTL = WDTPW + WDTHOLD;
6
7
  P1DIR |= 0x04;  
8
9
  CCTL0 = CCIE;   
10
  CCR0 = 0xFFFF;
11
  TACTL = TASSEL_2 + ID_3 + MC_1;
12
13
  LED_Enable = 0;
14
15
  _BIS_SR(GIE);
16
17
  while(1)
18
  {                     // (Tastenabfrage eventuell noch entprellen...)
19
    if (P1IN & 0x02)    // Taster abfragen (hier high-aktiv)
20
      LED_Enable = 1;   // Blinken aktivieren
21
22
    else
23
    {
24
      LED_Enable = 0;   // Blinken deaktivieren
25
      P1OUT &= ~0x04;   // LED aus 
26
    }
27
  }
28
}
29
30
// Timer interrupt service routine
31
__interrupt void Timer(void);
32
TIMERA0_ISR(Timer)
33
__interrupt void Timer(void)
34
{
35
  if(LED_Enable)
36
    P1OUT ^= 0x04;
37
}

von m. S. (marek)


Lesenswert?

Dan war das mein Fehler dann hab ich deine Antwort falsch verstanden 
sorry, ich dachte das solt eich dann noch mal in der While schleife 
machen was mir aber komisch vorkam aber trotzdem danke.

mfg

von Dennis (Gast)


Lesenswert?

Stefan schrieb:
> while(1)
>   {                     // (Tastenabfrage eventuell noch entprellen...)
>     if (P1IN & 0x02)    // Taster abfragen (hier high-aktiv)
>       LED_Enable = 1;   // Blinken aktivieren
>
>     else
>     {
>       LED_Enable = 0;   // Blinken deaktivieren
>       P1OUT &= ~0x04;   // LED aus
>     }
>   }

Kannst auch:
1
while (1)
2
{
3
  if (P1IN & 0x02)
4
    TACTL0 |= CCIE;
5
  else
6
    TACTL0 &= ~CCIE;
7
}

Dann behält die LED ihren Status vor dem Tastendruck.
Wie schon gesagt wurde, es gibt zich Möglichkeiten.

von m. S. (marek)


Lesenswert?

@Dennis (Gast)

Du sagst ein Blick in das MSP430 Microcontroller Basiccs Buch lohnt 
sich. Kann man daraus gut was lernen und mit arbeiten? Dan würde ich mir 
das mal kaufen. Englisch solte ja nicht das Problem sein im notfall 
überstetzt man mal paar Wörter.

mfg

von Dennis (Gast)


Lesenswert?

Marek S. schrieb:
> Du sagst ein Blick in das MSP430 Microcontroller Basiccs Buch lohnt
> sich.

Ja, also ich habe ein paar Bücher "durchgearbeitet" und meiner Meinung 
nach ist das das beste von allen über den MSP430. Es ist komplett in 
Englisch, klar, aber wer mit Elektronik zu tun hat, der kommt an 
englisch sowieso nicht vorbei. Da sind viele Programme drin, mit 
Anwendungen, wie man sie auch immer mal wieder braucht.

Von meiner Seite aus jedenfalls absolute Kaufempfehlung. Leider ist der 
MSP ja nicht so der gebräuchlichste Controller, das siehst du hier ja 
auch, die meisten programmieren auf Atmel. Das habe ich noch nie 
gemacht, kann daher nur vom MSP sprechen. Aber für den ist Davies Buch 
auf jedenfall die Investition wert.

Plus natürlich sie Code-Examples, da kann man ebenfalls ne Menge finden!


Gruß, Dennis

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.