Forum: Mikrocontroller und Digitale Elektronik [MSP430] Probleme mit dem Mikrocontroller


von Bernd K. (berndk)


Lesenswert?

Hallo,

ich braeuchte mal die Hilfe von euch MSP Experten.
Mein Programm soll folgendes machen:
TimerA loest TimerB aus, die TimerRoutineB schaltet dann einen Ausgang1 
an und der AD Wandler holt 5Werte die dann gemittelt werden. Danach wird 
Ausgang1 abgeschaltet, Ausgang2 eingeschalet und wieder Werte mit dem 
AD-Wanlder eingelesen. So war zumindest mein Plan :)
Wenn ich mir mit dem Debugger meine Variablen anschaue, stelle ich fest, 
dass die nur max. 3 Messwerte in den jeweiligen Array 
(Messwerte_LED1[n_mw]) geschrieben werden und der Zaehler "n_mw" sich 
gar nicht ruehrt bei 0 bleibt.
Kann jemand den Fehler erkennen?

Vielen Dank
Bernd

P.S Fuer weitere Verbesserungsforschlaege etc. bin ich ebenfalls offen, 
beschaeftige mich noch nicht so lange mit der Programmierung..
1
#include <msp430x16x.h>
2
3
#include <msp430def.h>
4
5
void timera_init();
6
void timerb_init();
7
void mittel();
8
9
10
#pragma interrupt_handler timera_isr: TIMERA0_VECTOR  timera_isr: TIMERA1_VECTOR
11
#pragma interrupt_handler timerb_isr: TIMERB0_VECTOR  timerb_isr: TIMERB1_VECTOR
12
#pragma interrupt_handler    adc_isr: ADC_VECTOR
13
14
15
unsigned int temp;
16
unsigned int val_DAC;
17
unsigned char n_tb = 0;
18
unsigned char n_led = 0;
19
unsigned char n_mw = 0;
20
unsigned long Messwerte_LED0[5];
21
unsigned long Messwerte_LED1[5];
22
unsigned long Messwerte_LED2[5];
23
unsigned long Summe;
24
unsigned char Ziffern[4];
25
26
void main(void)
27
28
{
29
30
    _STOP_WATCHDOG();    //definiert in "msp430def.h"
31
   
32
   P1SEL = 0x00;      //Std.IO
33
   P1DIR = 0xFF;      //alles Ausgänge
34
   P6SEL = 0xFF;        //Peripheral Modul Function (ADC12)
35
   timera_init();
36
   timerb_init();
37
   init_DAC();
38
   init_ADC();
39
40
41
   //Interruptfreigaben
42
   ADC12IE = 0x00FF;  //Int-Freigabe für das Ergebnisregister ADC12MEM0
43
   
44
   TACCTL0 |= CCIE;   //Timer A C/C Int Enable (für TACCR0)
45
46
   TBCCTL0 |= CCIE;   //Tiemr_B C/C Int Enable (für TBCCR0)
47
48
   _BIS_SR(GIE);     //GIE = 1, globale Interruptfreigabe
49
50
51
   TACTL |= MC_1;    //Timer_A starten
52
   
53
     
54
55
   while(1);  //Endlosschleife - Warten auf Interrupts
56
57
}
58
59
60
61
void timera_init()
62
63
{
64
     
65
   TACTL = TASSEL_1 | ID_0;   //SourceClock = ACLK
66
67
   TACCR0 = 0x200;    //f_timer = 1000Hz ( ACLK = 32,687kHz)
68
   
69
}
70
71
72
73
void timerb_init()
74
75
{
76
     
77
    TBCTL = TBSSEL_1 | ID_0;    //SourceClock = ACLK
78
79
    TBCCR0 = 0x90;          //f_timer = 1000Hz (für 5 Abtastwerte in 1ms)
80
   
81
}
82
83
void init_DAC(void)
84
{
85
  DAC12_0CTL = DAC12IR + DAC12SREF_2 + DAC12AMP_5 + DAC12ENC;   // 1 reference voltage, medium speed 
86
}
87
88
void init_ADC(void)
89
{
90
   ADC12CTL0 = ADC12ON;
91
  ADC12CTL1 = SHS_0 + SHP + ADC12DIV_0 + ADC12SSEL_1 + CONSEQ_0;
92
  ADC12MCTL0 = SREF_0 + INCH_2;    //ADC12MEM0, A2
93
  ADC12MCTL1 = SREF_0 + INCH_3;    //ADC12MEM0, A3
94
  ADC12MCTL2 = SREF_0 + INCH_4;    //ADC12MEM0, A4
95
  ADC12MCTL3 = SREF_0 + INCH_5;    //ADC12MEM0, A5
96
  ADC12CTL0 |= ENC;  //ADC startfähig machen
97
98
}
99
100
 
101
void write_DAC(unsigned int val_DAC)
102
{
103
  DAC12_0DAT = val_DAC;
104
}
105
106
void timera_isr()
107
108
{   
109
    //P1OUT ^= 0x01;
110
   if(n_led == 0)
111
   {
112
   P1OUT = 0x04;    //P1.2 einschalten
113
   write_DAC(0x666); 
114
   }
115
    if(n_led == 1)
116
   {
117
   P1OUT = 0x02;    //P1.1 einschalten
118
   write_DAC(0xffff); 
119
   }
120
    if(n_led == 2)
121
   {
122
   P1OUT = 0x01;    //P1.0 einschalten
123
   write_DAC(0x111);  
124
   }
125
   
126
   TBCCR0 = 0x90 ;    //Timer_B starten (wirksam erst nach dem ersten
127
128
               //Löschen von TBCCR0 ind der TimerB_ISR)
129
130
   TBCTL |= MC_1;    //Timer_B erstmalig starten
131
//   P1OUT &= 0xf7;
132
}
133
134
135
136
void timerb_isr()
137
138
{   
139
    //P1OUT ^= 0x02;
140
    n_tb++;       //Inkrement des Timer_B Periodenzählers
141
   
142
    if(n_tb == 6)
143
   {
144
      
145
      TBCCR0 = 0;    //Timer_B stoppen
146
147
      n_tb = 0;    //Rücksetzen des Timer_B Periodenzählers
148
    
149
    //P1OUT = 0x00;    //P1 ausschalten
150
    n_led++;          // Zaehler naechste LED
151
    if(n_led >=3)
152
    { 
153
    n_led=0;
154
    }
155
    
156
    
157
   }
158
159
   else
160
161
   {
162
    ADC12CTL0 |= ADC12SC; //SC = Start Conversion
163
    
164
   }
165
//   P1OUT &= 0xef;
166
}
167
168
void adc_isr()
169
{   
170
  //P1OUT ^= 0x03;
171
   if(n_led == 0){ Messwerte_LED0[n_mw] = ADC12MEM0;  }
172
  if(n_led == 1){ Messwerte_LED1[n_mw] = ADC12MEM0;  }
173
  if(n_led == 2){ Messwerte_LED2[n_mw] = ADC12MEM2;  }
174
  n_mw++;
175
  
176
  if(n_mw == 5);
177
  {
178
    n_mw = 0;
179
    mitteln();
180
    
181
  }
182
  
183
  ADC12IFG = 0x0000;
184
//  P1OUT &= 0xdf;
185
}
186
187
188
void mitteln()
189
{
190
  unsigned char i = 0;
191
  
192
  Summe = 0;
193
  
194
  for(i=0; i<5; i++)
195
  {  
196
    if(n_led == 0){ Summe += Messwerte_LED0[i];  }   
197
    if(n_led == 1){ Summe += Messwerte_LED1[i];  }
198
    if(n_led == 2){ Summe += Messwerte_LED2[i];  }      
199
  }
200
201
  Summe /= i;
202
  i = 0;
203
  //P1OUT ^= 0x04;
204
}

von Alex (Gast)


Lesenswert?

Hallo,

hab nur mal so drübergesehen:

1) Meines wissens nach musst du nach jeder Conversion das ENC bit 
toggeln
2) Willst du nicht eine sequenz von Kanälen samplen??? -> Sample Mode 
wecheln zu 'Sequence of Channels' und das 'EOS - End of Sequence' Bit 
setzen.
3) Das Interrupt Enable Bit der Timer ist nicht gesetzt!!!.

Zudem würde ich die globalen Variablen mal als 'volatile' deklarieren, 
damit der Compiler diese nicht optimieren kann.

mfg alex

von Bernd K. (berndk)


Lesenswert?

Hallo Alex,

vielen Dank fuer deine Hilfe. Die Variablen habe ich jetzt auf 
'volatile' umgestellt.
Die Timer Interrupt Bits habe ich so gesetzt:
1
  
2
TACCTL0 |= CCIE;   
3
TBCCTL0 |= CCIE;

Wie genau toggel ich das ENC Bit?
Das samplen einer Sequenz bringt mir leider nichts, da ich vor dem 
Auslesen eines anderen Kanals den DAC noch einen anderen werden geben 
muss etc.

von Alex (Gast)


Lesenswert?

Hallo,

ich weiß nicht welches Device du verwendest, normalerweise musst du noch 
die Interrupt Enable Bits im Control Register der Timer Setzen.

Also TACTL, TBCTL bzw. teste mal ob deine Interrupt behandelt werden.

mfg

von Bernd K. (berndk)


Lesenswert?

Die Timer arbeiten wunderbar, jedoch zaehlt der Zaehler "n_mw" nicht 
hoch und die werte des ADC werden auch nicht richtig in die Arrays 
uebernommen. Komischerweise wird in den Array (Messwerte_LED0) nach dem 
ersten Durchlauf 3 Werte eingetragen, nach jedem weiteren durchlauf, 
veraendern sich diese dann. Ich weiss einfach nicht mehr weiter, woran 
koennte es liegen?

von Bernd K. (berndk)


Lesenswert?

Der Zaehler laeuft endlich, schuld war ein Semikolon was dort nicht 
hingehoerte..

Das Programm laeuft soweit auch, bis zur Abfrage von ADC12MEM2. Habe ich 
dort etwas falsch initalisiert? Kann sich das mal jemand von euch 
Experten angucken?

Vielen Dank
Bernd

von Bernd Krause (Gast)


Lesenswert?

kann mir sonst jemand generell erklaeren, wie ich den ADC12MEM2 
initialisieren muss?

Danke
Bernd

von Stefan (Gast)


Lesenswert?

Das einzige, was ausserhalb der Spezifikationen läuft, ist der ADC12CLK.
Wenn ich das richtig interpretiere, dann benutzt Du ACLK (=32,768kHz) 
als Clock für den ADC12?!
Der braucht aber lt. Datenblatt mind. 450kHz um korrekt zu 
funktionieren.
Vielleicht liegt da der Fehler...
... aber was genau geht denn jetzt nicht?

von Bernd Krause (Gast)


Lesenswert?

Hallo Stefan,

nach dem Sprung in die adc_isr lese ich zwei ADC-Register aus (ADC12MEM0 
oder ADC12MEM2). Das Abarbeiten des ADC12MEM0 funzt noch funderbar, doch 
noch auslesen des ADCMEM2 geht alles kreuz und quer (der Array wird 
vollstaendig gefuellt)
Ich kann es mir nur so erklaeren, dass ADC12MEM0 nicht richtig 
intialisiert wird.

Bernd

von Stefan (Gast)


Lesenswert?

Ich glaube Dein ganzes Timing stimmt hinten und vorne nicht!
Es ist bei solchen verschachtelten IRQ's immer gut, mal ein Blatt Papier 
und einen Stift zu nehmen und sich das Timing aufzumalen:

1.) Timer A braucht 15,625ms pro Durchlauf (512/32768Hz) = 64Hz und 
nicht 1000Hz!
2.) Timer B braucht 144/32768Hz = 4,39ms
D.h. in der timerb_isr() können nur 3 Messungen gemacht werden, bevor 
timera_isr() erneut angesprungen wird, und nicht 5! Ist das gewollt?
Ich vermute mal, da überholt sich Dein Programm selbst und kommt ausser 
Tritt!

Ausserdem solltest Du immer über eine Zweierpotenz an Messwerten mitteln 
(2,4,8,...). Dann vereinfacht sich die Division zu einer 
Shift-Operation, die wesentlich schneller ausgeführt wird, als die 
Divisionsroutine, die Dein Compiler für /5 einsetzen muss! 
Möglicherweise ist diese langsame Division auch ein Grund für den 
Fehler!

An der Init für ADC12MEM sehe ich jetzt nichts auszusetzen!

von Stefan (Gast)


Lesenswert?

Also nach etwas genauerem Drübergucken habe ich doch noch'n Fehler 
gefunden:

Du verwendest single channel-single conversion für den ADC12. D.h. aber 
mit jedem Setzen von ADC12SC wird immer ADC12MEM0 gewandelt, zu ...MEM2 
kommst Du nie, da Du es nie auswählst, denn CSTARTASSx steht immer auf 
0x00.
Du solltest darüber nachdenken, den sequence-of-channels zu verwenden. 
Mit jedem ADC12SC also MEM0 bis MEM2 sampeln. Der ADC12CLK muss 
natürlich schnell genug sein, dass alle Wandlungen innerhalb einer 
timerb_isr-Periode fertiggestellt werden können!

von Bernd K. (berndk)


Lesenswert?

Hallo Stefan,

zunaechst erstmal vielen Dank fuer deine Unterstuetzung!
Die Timer habe ich korrigiert, Timer A arbeitet weiterhin mit 64Hz und 
TimerB mit 333Hz, die Anzahl der Abtastungen habe ich auf 4 reduziert.
Darauf haette ich auch wirklich selbst kommen muessen.
Den ADC initialiere ich nun wie folgt:
1
ADC12CTL1 = SHS_0 + SHP + ADC12DIV_0 + ADC12SSEL_1 + CONSEQ_1

Dabei stosse ich aber auf folgendes Problem. Nachdem das erste Mal ein 
Sprung in die ADC ISR erfolgt ist, wird ADC12MEM0 ausgelesen und in den 
Array "Messwerte_LED0[n_mw]" gelegt. Beim naechsten Sprung in die ISR 
werden alle 3 Arrays mit "-0xFE00FF" voll geschrieben und es passiert 
nichts mehr.
Hast du eine Ahnung was das sein kann?

Bernd

von Stefan (Gast)


Lesenswert?

Du musst auf jeden Fall beim letzten Channel das EOS-Bit setzen, sonst 
wandelt der ADC12 alle 16 Kanäle durch!

Also:
1
ADC12MCTL0 = SREF_0 + INCH_2;          //ADC12MEM0, A2
2
ADC12MCTL1 = SREF_0 + INCH_3;          //ADC12MEM0, A3
3
ADC12MCTL2 = SREF_0 + INCH_4 + EOS;    //ADC12MEM0, A4, letzter Kanal, danach stop

Ausserdem solltest Du "sicherheitshalber" nur einen IRQ freigeben, 
denn Du wandelst ja eine Sequenz mit 3 Kanälen! Wenn Du mehrere frei 
gibst, dann bekommst Du pro Sequenz auch mehrere IRQ's, obwohl du nur 
einen haben willst!
1
ADC12IE = ADC12IE2;   // der letzte Kanal, wird auch zuletzt gewandelt
Durch
1
ADC12IFG = 0x0000;
sollte das zwar verhindert werden, aber man weiß ja nie (soll ja auch 
Bugs geben...)

Den ADC12CLK hast Du auf mind. 450kHz erhöht?

von Bernd K. (berndk)


Lesenswert?

Wie kann ich den ADC12CLK auf min. 450kHz erhoehen? Ich habe im 
Datenblatt des MSP nichts finden koennen, dass der so hoch sein muss.
Das EOS-Bit habe ich gesetzt, jedoch immer noch der gleiche Fehler. Nach 
dem zweiten Sprung in die ISR werden Felder aller drei Array mit 
"-0xFE00FF" beschrieben.

Bernd

von Stefan (Gast)


Lesenswert?

>Wie kann ich den ADC12CLK auf min. 450kHz erhoehen?
Entweder den internen Clock des ADC12 benutzen, oder MCLK bzw. SMCLK als 
Source benutzen (solange MCLK bzw. SMCLK innerhalb der geforderten 
Spezifikation sind)

von Bernd K. (berndk)


Angehängte Dateien:

Lesenswert?

Ich habe es jetzt mit ADC12OSC und MCLK probiert. Mit ADC120SC wird die 
ADC ISR gar nicht mehr ausgeloest. Ich weiss einfach nicht mehr weiter, 
kann es auch ein Fehler des Debuggers sein, dass die zu ueberwachenden 
Variablen nicht richtig ausgelesen werden?
Im Anhang befindet sich der aktuelle Quelltext..

Bernd

von Christian R. (supachris)


Lesenswert?

Im ADC12CTL0 Register fehlt auf jeden Fall noch das MSC Bit, sonst macht 
der keine Mehfachen Wandlungen hintereinander. Dann musst du mal im User 
Guide schauen, ob das Sample-Timing hinhaut bei dir, dass du wirklich 
mit Sampeln schneller fertig bist, wie dein Timer.

Ein funktionierender Beispielcode:
1
ADC12CTL0 = ADC12ON + SHT0_DIV32 + SHT1_DIV32 + MSC + REFON + REF2_5V;  // Turn on and set up ADC12
2
Wait(30000);
3
ADC12CTL1 = SHP + CONSEQ_1 +ADC12SSEL_1;     // Use sampling timer
4
ADC12MCTL0 = SREF_1 + INCH_0;        //ext. Ref, Kanal 0, ext. Spannung
5
ADC12MCTL1 = SREF_1 + INCH_11;        // VCC/2
6
ADC12MCTL2 = SREF_1 + INCH_10 + EOS;    //temp-diode, ende sequenz
7
ADC12CTL0 |= ENC;
8
9
uint32_t UBatt = 0;
10
uint32_t VCC = 0;
11
uint32_t Temp= 0;
12
unsigned char seq_count;
13
  
14
ADC12CTL0 |= ADC12SC;                   // Start conversion
15
  
16
for (seq_count = 0; seq_count < 64; seq_count++)
17
{
18
    ADC12CTL0 |= ADC12SC;                   // Start conversion
19
    while ((ADC12IFG & 0x04)==0);
20
    UBatt += ADC12MEM0;            //Werte mitteln
21
    VCC += ADC12MEM1;
22
    Temp += ADC12MEM2;
23
}
24
  
25
ADC12CTL0 &= ~ADC12SC;                   // Stop conversion
26
ADC12CTL0 &= ~ENC;            //Stopp Encode

von Bernd K. (berndk)


Angehängte Dateien:

Lesenswert?

jetzt sieht es schon mal besser aus... alle 3 Arrays werden befuellt, 
doch wenn der letzte Werte des 3 Array geschrieben wird, werden alle 
Felder aller drei Arrays auf "0xFF03FF03" gesetzt.

von Christian R. (supachris)


Lesenswert?

Mach mal alle Variablen auf Volatile. Vielleicht klappts dann. Ich denke 
mal, die Variablen verlieren nach dem letzten Schreiben ihre Gültigkeit, 
da muss ein volatile vor alle Arrays. Schließlich machst du sonst nix 
damit, da legt der Compiler die auf den Arbeitsregistern an (soweit 
möglich).
Wo ist denn jetzt der Unterschied zum letzten Programmcode? Ich seh das 
MSC-Bit immer noch nicht gesetzt im ADC12CTL0. Außerdem solltest du nur 
beim letzten der 3 Eingangskanäle den Interrupt setzen lassen, sonst 
verhaspelt sich der Controller ja, wenn du nach jedem Kanal den IRQ 
bekommst.
ADC12IE müsste dann auf 0x04, damit du nach dem 3. Kanal (nachdem 
ADC12MEM2 geschrieben wurde) einen Int bekommst.

von Bernd K. (berndk)


Lesenswert?

@Christian und Stefan

Endlich laueft es so wie es soll! Vielen Dank an euch beide und an 
dieses grossartige Forum. Gerade fuer einen Anfaenger wie mich, gebt ihr 
einfach super Tips.

Bernd

P.S meine naechsten Fragen kommen bestimmt bald :)

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.