[MSP430] Probleme mit dem Mikrocontroller

#875830
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
}
Gast #876020
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
#877823
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.
Gast #877975
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
#878025
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?
Gast #880302
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?
Gast #880311
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
Gast #880334
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!
Gast #880432
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!
#880710
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
Gast #880751
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?
#880782
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
Gast #880878
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)
#880991
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
#881050
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.

Antwort schreiben

Bitte melde dich an, um einen Beitrag zu schreiben.

oder

Mit Google-Account einloggen

Die Registrierung ist kostenlos und dauert nur eine Minute.

Jetzt registrieren