mikrocontroller.net

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


Autor: Bernd Krause (berndk)
Datum:

Bewertung
0 lesenswert
nicht 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..
#include <msp430x16x.h>

#include <msp430def.h>

void timera_init();
void timerb_init();
void mittel();


#pragma interrupt_handler timera_isr: TIMERA0_VECTOR  timera_isr: TIMERA1_VECTOR
#pragma interrupt_handler timerb_isr: TIMERB0_VECTOR  timerb_isr: TIMERB1_VECTOR
#pragma interrupt_handler    adc_isr: ADC_VECTOR


unsigned int temp;
unsigned int val_DAC;
unsigned char n_tb = 0;
unsigned char n_led = 0;
unsigned char n_mw = 0;
unsigned long Messwerte_LED0[5];
unsigned long Messwerte_LED1[5];
unsigned long Messwerte_LED2[5];
unsigned long Summe;
unsigned char Ziffern[4];

void main(void)

{

    _STOP_WATCHDOG();    //definiert in "msp430def.h"
   
   P1SEL = 0x00;      //Std.IO
   P1DIR = 0xFF;      //alles Ausgänge
   P6SEL = 0xFF;        //Peripheral Modul Function (ADC12)
   timera_init();
   timerb_init();
   init_DAC();
   init_ADC();


   //Interruptfreigaben
   ADC12IE = 0x00FF;  //Int-Freigabe für das Ergebnisregister ADC12MEM0
   
   TACCTL0 |= CCIE;   //Timer A C/C Int Enable (für TACCR0)

   TBCCTL0 |= CCIE;   //Tiemr_B C/C Int Enable (für TBCCR0)

   _BIS_SR(GIE);     //GIE = 1, globale Interruptfreigabe


   TACTL |= MC_1;    //Timer_A starten
   
     

   while(1);  //Endlosschleife - Warten auf Interrupts

}



void timera_init()

{
     
   TACTL = TASSEL_1 | ID_0;   //SourceClock = ACLK

   TACCR0 = 0x200;    //f_timer = 1000Hz ( ACLK = 32,687kHz)
   
}



void timerb_init()

{
     
    TBCTL = TBSSEL_1 | ID_0;    //SourceClock = ACLK

    TBCCR0 = 0x90;          //f_timer = 1000Hz (für 5 Abtastwerte in 1ms)
   
}

void init_DAC(void)
{
  DAC12_0CTL = DAC12IR + DAC12SREF_2 + DAC12AMP_5 + DAC12ENC;   // 1 reference voltage, medium speed 
}

void init_ADC(void)
{
   ADC12CTL0 = ADC12ON;
  ADC12CTL1 = SHS_0 + SHP + ADC12DIV_0 + ADC12SSEL_1 + CONSEQ_0;
  ADC12MCTL0 = SREF_0 + INCH_2;    //ADC12MEM0, A2
  ADC12MCTL1 = SREF_0 + INCH_3;    //ADC12MEM0, A3
  ADC12MCTL2 = SREF_0 + INCH_4;    //ADC12MEM0, A4
  ADC12MCTL3 = SREF_0 + INCH_5;    //ADC12MEM0, A5
  ADC12CTL0 |= ENC;  //ADC startfähig machen

}

 
void write_DAC(unsigned int val_DAC)
{
  DAC12_0DAT = val_DAC;
}

void timera_isr()

{   
    //P1OUT ^= 0x01;
   if(n_led == 0)
   {
   P1OUT = 0x04;    //P1.2 einschalten
   write_DAC(0x666); 
   }
    if(n_led == 1)
   {
   P1OUT = 0x02;    //P1.1 einschalten
   write_DAC(0xffff); 
   }
    if(n_led == 2)
   {
   P1OUT = 0x01;    //P1.0 einschalten
   write_DAC(0x111);  
   }
   
   TBCCR0 = 0x90 ;    //Timer_B starten (wirksam erst nach dem ersten

               //Löschen von TBCCR0 ind der TimerB_ISR)

   TBCTL |= MC_1;    //Timer_B erstmalig starten
//   P1OUT &= 0xf7;
}



void timerb_isr()

{   
    //P1OUT ^= 0x02;
    n_tb++;       //Inkrement des Timer_B Periodenzählers
   
    if(n_tb == 6)
   {
      
      TBCCR0 = 0;    //Timer_B stoppen

      n_tb = 0;    //Rücksetzen des Timer_B Periodenzählers
    
    //P1OUT = 0x00;    //P1 ausschalten
    n_led++;          // Zaehler naechste LED
    if(n_led >=3)
    { 
    n_led=0;
    }
    
    
   }

   else

   {
    ADC12CTL0 |= ADC12SC; //SC = Start Conversion
    
   }
//   P1OUT &= 0xef;
}

void adc_isr()
{   
  //P1OUT ^= 0x03;
   if(n_led == 0){ Messwerte_LED0[n_mw] = ADC12MEM0;  }
  if(n_led == 1){ Messwerte_LED1[n_mw] = ADC12MEM0;  }
  if(n_led == 2){ Messwerte_LED2[n_mw] = ADC12MEM2;  }
  n_mw++;
  
  if(n_mw == 5);
  {
    n_mw = 0;
    mitteln();
    
  }
  
  ADC12IFG = 0x0000;
//  P1OUT &= 0xdf;
}


void mitteln()
{
  unsigned char i = 0;
  
  Summe = 0;
  
  for(i=0; i<5; i++)
  {  
    if(n_led == 0){ Summe += Messwerte_LED0[i];  }   
    if(n_led == 1){ Summe += Messwerte_LED1[i];  }
    if(n_led == 2){ Summe += Messwerte_LED2[i];  }      
  }

  Summe /= i;
  i = 0;
  //P1OUT ^= 0x04;
}
 

Autor: Alex (Gast)
Datum:

Bewertung
0 lesenswert
nicht 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

Autor: Bernd Krause (berndk)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Hallo Alex,

vielen Dank fuer deine Hilfe. Die Variablen habe ich jetzt auf 
'volatile' umgestellt.
Die Timer Interrupt Bits habe ich so gesetzt:
  
TACCTL0 |= CCIE;   
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.

Autor: Alex (Gast)
Datum:

Bewertung
0 lesenswert
nicht 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

Autor: Bernd Krause (berndk)
Datum:

Bewertung
0 lesenswert
nicht 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?

Autor: Bernd Krause (berndk)
Datum:

Bewertung
0 lesenswert
nicht 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

Autor: Bernd Krause (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
kann mir sonst jemand generell erklaeren, wie ich den ADC12MEM2 
initialisieren muss?

Danke
Bernd

Autor: Stefan (Gast)
Datum:

Bewertung
0 lesenswert
nicht 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?

Autor: Bernd Krause (Gast)
Datum:

Bewertung
0 lesenswert
nicht 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

Autor: Stefan (Gast)
Datum:

Bewertung
0 lesenswert
nicht 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!

Autor: Stefan (Gast)
Datum:

Bewertung
0 lesenswert
nicht 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!

Autor: Bernd Krause (berndk)
Datum:

Bewertung
0 lesenswert
nicht 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:
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

Autor: Stefan (Gast)
Datum:

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

Also:
ADC12MCTL0 = SREF_0 + INCH_2;          //ADC12MEM0, A2
ADC12MCTL1 = SREF_0 + INCH_3;          //ADC12MEM0, A3
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!
ADC12IE = ADC12IE2;   // der letzte Kanal, wird auch zuletzt gewandelt
Durch
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?

Autor: Bernd Krause (berndk)
Datum:

Bewertung
0 lesenswert
nicht 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

Autor: Stefan (Gast)
Datum:

Bewertung
0 lesenswert
nicht 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)

Autor: Bernd Krause (berndk)
Datum:
Angehängte Dateien:

Bewertung
0 lesenswert
nicht 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

Autor: Christian R. (supachris)
Datum:

Bewertung
0 lesenswert
nicht 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:
ADC12CTL0 = ADC12ON + SHT0_DIV32 + SHT1_DIV32 + MSC + REFON + REF2_5V;  // Turn on and set up ADC12
Wait(30000);
ADC12CTL1 = SHP + CONSEQ_1 +ADC12SSEL_1;     // Use sampling timer
ADC12MCTL0 = SREF_1 + INCH_0;        //ext. Ref, Kanal 0, ext. Spannung
ADC12MCTL1 = SREF_1 + INCH_11;        // VCC/2
ADC12MCTL2 = SREF_1 + INCH_10 + EOS;    //temp-diode, ende sequenz
ADC12CTL0 |= ENC;

uint32_t UBatt = 0;
uint32_t VCC = 0;
uint32_t Temp= 0;
unsigned char seq_count;
  
ADC12CTL0 |= ADC12SC;                   // Start conversion
  
for (seq_count = 0; seq_count < 64; seq_count++)
{
    ADC12CTL0 |= ADC12SC;                   // Start conversion
    while ((ADC12IFG & 0x04)==0);
    UBatt += ADC12MEM0;            //Werte mitteln
    VCC += ADC12MEM1;
    Temp += ADC12MEM2;
}
  
ADC12CTL0 &= ~ADC12SC;                   // Stop conversion
ADC12CTL0 &= ~ENC;            //Stopp Encode

Autor: Bernd Krause (berndk)
Datum:
Angehängte Dateien:

Bewertung
0 lesenswert
nicht 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.

Autor: Christian R. (supachris)
Datum:

Bewertung
0 lesenswert
nicht 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.

Autor: Bernd Krause (berndk)
Datum:

Bewertung
0 lesenswert
nicht 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 :)

Antwort schreiben

Die Angabe einer E-Mail-Adresse ist freiwillig. Wenn Sie automatisch per E-Mail über Antworten auf Ihren Beitrag informiert werden möchten, melden Sie sich bitte an.

Wichtige Regeln - erst lesen, dann posten!

  • Groß- und Kleinschreibung verwenden
  • Längeren Sourcecode nicht im Text einfügen, sondern als Dateianhang

Formatierung (mehr Informationen...)

  • [c]C-Code[/c]
  • [avrasm]AVR-Assembler-Code[/avrasm]
  • [code]Code in anderen Sprachen, ASCII-Zeichnungen[/code]
  • [math]Formel in LaTeX-Syntax[/math]
  • [[Titel]] - Link zu Artikel
  • Verweis auf anderen Beitrag einfügen: Rechtsklick auf Beitragstitel,
    "Adresse kopieren", und in den Text einfügen




Bild automatisch verkleinern, falls nötig
Bitte das JPG-Format nur für Fotos und Scans verwenden!
Zeichnungen und Screenshots im PNG- oder
GIF-Format hochladen. Siehe Bildformate.
Hinweis: der ursprüngliche Beitrag ist mehr als 6 Monate alt.
Bitte hier nur auf die ursprüngliche Frage antworten,
für neue Fragen einen neuen Beitrag erstellen.

Mit dem Abschicken bestätigst du, die Nutzungsbedingungen anzuerkennen.