mikrocontroller.net

Forum: Mikrocontroller und Digitale Elektronik MSP430 ADC10 mehrere Kanäle


Autor: Sebastian (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Ich habe ein Problem mit dem ADC10 meines MSP430.

Und zwar möchte ich die Kanäle A0 bis A3 (also 4 Stück) auslesen, um 
dann weiterzuverarbeiten.
Aber irgendwie habe ich glaube ich mit dem DTC ein Problem damit, dass 
er nach jeder Konversion den Wert vom ADC10MEM in den ADC10SA speichern 
soll.

Den Code habe ich mehr oder weniger von TI, nur halt auf meine 
Applikation angepasst.

Könnt ihr mir da weiterhelfen?

Hier mein Code und im Anhang der von TI.
#include <msp430x22x2.h>              // Header Datei des verwendeten uC

// #include <system.h>                   // Definition des Systems -> Hier: PC
// #include <compiler.h>                 // Variabelbegriffe



void main(void)
{
  WDTCTL = WDTPW + WDTHOLD;           // Watchdog Timer (WDT) anhalten; Passwort benötigt

  BCSCTL1= CALBC1_16MHZ;              // DCO Clock auf 16 MHz einstellen
  DCOCTL = CALDCO_16MHZ;

  P1DIR &= ~0x0F;                     // Port 1 Pin 0-3 Input
  P3DIR |=  0x80;                     // Port 3 Pin 7 Output
  P3DIR &= ~0x40;                     // Port 3 Pin 6 Input
  P4DIR |=  0x18;                     // Port 4 Pin 4,3 Output
  P4DIR &= ~0x04;                     // Port 4 Pin 2 Input

  P1SEL &= ~0x0F;                     // Port 1 Pin 0-3 I/O
  P2SEL  =  0x0F;                     // Port 2 Pin 0-3 ADC, Rest I/O
  P3SEL  =  0x3E;                     // Port 3 Pin 1-3 DAC, Pin 4-5 UART, Rest I/O
  P4SEL  =  0x20;                     // Port 4 Pin 5 ADC, Rest I/O

  ADC10CTL0 =  0x0018;                // ADC erlauben, Interrupt erlauben
  ADC10CTL1 =  0x3012;                // channel A0..A3
  ADC10DTC1 = 0x04;                   // 4 Konversionen
  ADC10AE0  = 0x0F;


  for (;;)                            // Endlosschleife [== while (1) ]
  {
     //unsigned int i;


     if (P3IN & 0x40)                  // Wenn Taster gedrückt ...
     {
        P3OUT &= ~0x80;                // LED einschalten
     } else
     {
        P3OUT |=  0x80;                // LED ausschalten
     }

     ADC10CTL0 &= ~ENC;
     while (ADC10CTL1 & BUSY);         // Warte wenn ADC10 core aktive ist
     ADC10SA = 0x200;                  // Data buffer start
     ADC10CTL0 |= ADC10SC + ENC;       // Starte AD-Konversion
     __bis_SR_register(CPUOFF + GIE);  // LPM0, Interrupt erlauben

     if (ADC10SA > 0x0010)
     {
        P4OUT |= 0x10;
     } else
     {
        P4OUT &= ~0x10;
     }


     /*
     i = 50000;                        // Wartezeit durch Schleife...
     do (i--);                         // ...mit 50.000 Durchläufen
     while (i != 0);
     */

  }
}



#pragma vector=ADC10_VECTOR            // ADC ISR
__interrupt void ADC10_ISR(void)
{
  __bic_SR_register_on_exit(CPUOFF);   // Lösche CPUOFF Bit in 0(SR)
}

Autor: Sebastian (Gast)
Datum:
Angehängte Dateien:

Bewertung
0 lesenswert
nicht lesenswert
hier TI code

Autor: Jörg S. (joerg-s)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
>dass er nach jeder Konversion den Wert vom ADC10MEM in den ADC10SA
>speichern soll.
Vorsicht! Er speichert den Wert nicht in ADC10SA, sondern in die 
Speicherstelle dessen ADRESSE in ADC10SA steht. Das ist also ein 
Pointer.

Lösung äre also z.B. (ungetestet!)
unsigned int ADC10_Buffer[4]; // 4 ADC Ergebnisse
:
ADC10SA = (unsigned int)&ADC10_Buffer[0];    // Zielspeicheradresse zuweisen  
:

Autor: Sebastian (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Hallo jörg

Ich habe das mal so eingefügt:
     ADC10CTL0 &= ~ENC;
     while (ADC10CTL1 & BUSY);         // Warte wenn ADC10 core aktive ist
     ADC10SA = 0x200;                  // Data buffer start
     ADC10CTL0 |= ADC10SC + ENC;       // Starte AD-Konversion
     __bis_SR_register(CPUOFF + GIE);  // LPM0, Interrupt erlauben
     ADC10SA = (unsigned int)&ADC10_Buffer[0];

Aber es funktioniert nicht. Wenn ich debugge und dann der schritt mit 
dem ADC10SA kommt dann bleibt er einfach stehen danach.

Ich verstehe auch nicht, ob die ersten drei Zeilen von obigem code so 
überhaupt stimmen und wo ich dann wirklich meine 4 AD-Werte habe (am 
anfang oder am schluss).

Autor: Sebastian (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Ich habe es mit ein bisschen rumprobieren in den Registern ADC10CTL0 und 
ADC10CTL1 geschafft, dass er die 4 AD Werte (A0 bis A3) in die 
Buffervariable ADC10_Buffer speichert.
Aber wirklich normal funktioniert es nicht, denn die LED kann ich ausser 
im aller ersten Debug durchgang nicht mehr einschalten, d.h. er bleibt 
nach einem durchgang bei der ersten if-Schleife hängen und wenn ich dann 
F10 drücke für einen nächsten Schritt, macht er das nicht und ich muss 
mit break abbrechen. Dann aber hat er wieder neue AD-Werte geladen.

Also irgendetwas ist immer noch nicht so ganz funktionstüchtig.

Er soll die main schleife ja immer wieder wiederholen, so dass ich z.B. 
die LED jederzeit ein und aus schalten kann.
Am besten sollte er die Konversion starten, damit man dann noch anderes 
machen kann (noch nicht implementiert) und am Anfang der funktion werden 
die ad-werte abgefragt.

Hier noch der aktuelle c-code:
/* Includes */
#include <msp430x22x2.h>              // Header Datei des verwendeten uC
#include "system.h"
#include "compiler.h"

#include "ioTeach.h"                  // Led und Taster


/* Globale Variablen */
unsigned int ADC10_Buffer[4];         // AD Werte Buffer



void main(void)
{
  WDTCTL = WDTPW + WDTHOLD;           // Watchdog Timer (WDT) anhalten; Passwort benötigt

  BCSCTL1= CALBC1_16MHZ;              // DCO Clock auf 16 MHz einstellen
  DCOCTL = CALDCO_16MHZ;

  P1DIR &= ~0x0F;                     // Port 1 Pin 0-3 Input
  P3DIR |=  0x80;                     // Port 3 Pin 7 Output
  P3DIR &= ~0x40;                     // Port 3 Pin 6 Input
  P4DIR |=  0x18;                     // Port 4 Pin 4,3 Output
  P4DIR &= ~0x04;                     // Port 4 Pin 2 Input

  P1SEL &= ~0x0F;                     // Port 1 Pin 0-3 I/O
  P2SEL  =  0x0F;                     // Port 2 Pin 0-3 ADC, Rest I/O
  P3SEL  =  0x3E;                     // Port 3 Pin 1-3 DAC, Pin 4-5 UART, Rest I/O
  P4SEL  =  0x20;                     // Port 4 Pin 5 ADC, Rest I/O

  ADC10CTL0 =  0x0098;                // ADC erlauben, Interrupt erlauben
  ADC10CTL1 =  0x3016;                // channel A0..A3
  ADC10DTC1 = 0x04;                   // 4 Konversionen
  ADC10AE0  = 0x0F;


  for (;;)                            // Endlosschleife [== while (1) ]
  {
     if (TasterGet())                 // Wenn Taster gedrückt ...
     {
        LedSet(1);                    // LED einschalten
     } else
     {
        LedSet(0);                    // LED ausschalten
     }

     ADC10CTL0 &= ~ENC;
     while (ADC10CTL1 & BUSY);         // Warte wenn ADC10 core aktive ist
     ADC10SA = (unsigned int)&ADC10_Buffer[0];      // Data buffer start
     ADC10CTL0 |= ADC10SC + ENC;       // Starte AD-Konversion
     __bis_SR_register(CPUOFF + GIE);  // LPM0, Interrupt erlauben


     /*
     i = 50000;                        // Wartezeit durch Schleife...
     do (i--);                         // ...mit 50.000 Durchläufen
     while (i != 0);
     */
  }
}



#pragma vector=ADC10_VECTOR            // ADC ISR
__interrupt void ADC10_ISR(void)
{
  __bic_SR_register_on_exit(CPUOFF);   // Lösche CPUOFF Bit in 0(SR)
}

Autor: Jörg S. (joerg-s)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Er bleibt also bei "if (TasterGet())" stehen? Das dürfte dann wohl daran 
liegen das er nicht mehr aus dem LPM raus kommt. Allerdings sehe ich auf 
anhieb jetzt nicht woran es liegt.

Autor: Stefan (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Ich habe zwar noch nie DTC benutzt, aber wenn ich mir den User-Guide so 
anschaue, denke ich, Du hast zwei dicke Probleme in Deinem Code:

1.) Du verwendest den 16MHz Clock ohne Vorteiler für den ADC10.
    Der ADC10 verträgt aber nur max. 6,3MHz! Alles darüber kann sich
    in undefiniertem Verhalten wiederspiegeln!

2.) Du willst die ADC-Wandlung jeweils "per Hand" in Deiner for-Schleife
    starten:
ADC10CTL0 |= ADC10SC + ENC;
    In Deiner ADC-Initialisierung setzt Du aber MSC (in CTL0) und
    die repeated-sequence-of-channels (CONSEQ_3 in CTL1).
    Damit läuft der ADC eigentlich immer durch, bis Du irgendwann mal
    wieder ENC=0 setzt. Sicher kein gewolltes Verhalten?!

Außerdem solltest Du Dir die magic numbers abgewöhnen:
...
ADC10CTL1 =  0x3016;
...
und statt dessen die im Headerfile definierten Bit-Masken benutzten.
Ist zum einen weniger Fehleranfällig und auch leichter zu lesen und zu
interpretieren!
ADC10CTL1 =  INCH_3 + ADC10SSEL_2 + CONSEQ_3;

Autor: Sebastian (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Hallo
Ich habe es noch immer noch hingekriegt.
Die Control Register habe ich jetzt mal mit den Masken eingestellt und 
einen ADC10CLK Vorteiler habe ich mal auf "/3" gesetzt.
Das mit dem MSC bit und dem CONSEQ_1 bzw. CONSEQ_3 habe ich alles mal 
durchprobiert aber er bleibt immer noch irgendwie hängen.

Hier nochmals der C-Code: Ich hoffe, dass ich es mit eurer Hilfe doch 
noch hinkriegen werde.
/* Includes */
#include <msp430x22x2.h>              // Header Datei des verwendeten uC
#include "system.h"
#include "compiler.h"

#include "ioTeach.h"                  // Led und Taster


/* Globale Variablen */
unsigned int ADC10_Buffer[4];         // AD Werte Buffer



void main(void)
{
  WDTCTL = WDTPW + WDTHOLD;           // Watchdog Timer (WDT) anhalten; Passwort benötigt

  BCSCTL1= CALBC1_16MHZ;              // DCO Clock auf 16 MHz einstellen
  DCOCTL = CALDCO_16MHZ;

  P1DIR &= ~0x0F;                     // Port 1 Pin 0-3 Input
  P3DIR |=  0x80;                     // Port 3 Pin 7 Output
  P3DIR &= ~0x40;                     // Port 3 Pin 6 Input
  P4DIR |=  0x18;                     // Port 4 Pin 4,3 Output
  P4DIR &= ~0x04;                     // Port 4 Pin 2 Input

  P1SEL &= ~0x0F;                     // Port 1 Pin 0-3 I/O
  P2SEL  =  0x0F;                     // Port 2 Pin 0-3 ADC, Rest I/O
  P3SEL  =  0x3E;                     // Port 3 Pin 1-3 DAC, Pin 4-5 UART, Rest I/O
  P4SEL  =  0x20;                     // Port 4 Pin 5 ADC, Rest I/O

  ADC10CTL0 =  ADC10SHT_2 + MSC + ADC10ON + ADC10IE;                // ADC erlauben, Interrupt erlauben
  ADC10CTL1 =  INCH_3 + ADC10SSEL_2 + CONSEQ_1 + ADC10DIV_2;        // channel A0..A3
  ADC10DTC1 = 0x04;                   // 4 Konversionen
  ADC10AE0 |= 0x0F;


  for (;;)                            // Endlosschleife [== while (1) ]
  {
     if (TasterGet())                 // Wenn Taster gedrückt ...
     {
        LedSet(1);                    // LED einschalten
     } else
     {
        LedSet(0);                    // LED ausschalten
     }

     ADC10CTL0 &= ~ENC;
     while (ADC10CTL1 & BUSY);         // Warte wenn ADC10 core aktive ist
     ADC10SA = (unsigned int)&ADC10_Buffer[0];      // Data buffer start
     ADC10CTL0 |= ENC + ADC10SC;       // Starte AD-Konversion
     __bis_SR_register(CPUOFF + GIE);  // LPM0, Interrupt erlauben


     /*
     i = 50000;                        // Wartezeit durch Schleife...
     do (i--);                         // ...mit 50.000 Durchläufen
     while (i != 0);
     */
  }
}



#pragma vector=ADC10_VECTOR            // ADC ISR
__interrupt void ADC10_ISR(void)
{
  __bic_SR_register_on_exit(CPUOFF);   // Lösche CPUOFF Bit in 0(SR)
}

Autor: Stefan (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Arrrgghhh....

Hätte ich auch früher sehen können!!!
Du benutzt MCLK als Clock für den ADC10.
Sobald Du aber in LPM0 gehst, wird die CPU und damit auch MCLK 
deaktiviert!
Das kann nicht funktionieren!
Nehm den SMCLK oder den internen ADC10OSC!

Autor: Sebastian (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Also ich verwende ja den DTC mit 16 MHz.
Macht das keinen Unterschied, ob ich MCLK oder SMCLK verwende?

Ich hatte vorhin das mit dem LMP0 einfach auskommentiert und es 
funktionierte.
Zurzeit habe ich den MSC aktiviert und CONSEQ_3, ohne das hat es nicht 
funktioniert. Ist das schon richtig so?

Ich möchte es ja so handhaben, dass ich am Anfang von main die AD-Werte 
hole (siehe ADC10_Calc) und dann den AD-Wandler starte und dann noch 
andere Dinge mache (hier mal das mit dem Taster und LED, später kommt da 
anderes).
Ist das so wie ich es jetzt implementiert habe richtig oder stimmt da 
mit der Reihenfolge oder so noch was nicht?

Muss ich denn LMP0 und Interrupt eigentlich benützen, weil ich warte ja 
sowieso bis die Konversion fertig ist bevor ich mit einer neuen starte?

Ich hoffe du kannst mir auch noch bei den letzten Unklarheiten helfen, 
vielen Dank bis jetzt schonmal.

  ADC10CTL0 =  ADC10SHT_2 + MSC + ADC10ON + ADC10IE;                // ADC erlauben, Interrupt erlauben
  ADC10CTL1 =  INCH_3 + ADC10SSEL_2 + CONSEQ_3 + ADC10DIV_2;        // channel A0..A3
  ADC10DTC1 = 0x04;                   // 4 Konversionen
  ADC10AE0 |= 0x0F;



  for (;;)                             // Endlosschleife [== while (1) ]
  {
     ADC10_Calc[0] = ADC10_Buffer[0];  // Buffer Werte zur Sicherheit speichern
     ADC10_Calc[1] = ADC10_Buffer[1];
     ADC10_Calc[2] = ADC10_Buffer[2];
     ADC10_Calc[3] = ADC10_Buffer[3];

     ADC10CTL0 &= ~ENC;
     while (ADC10CTL1 & BUSY);         // Warte wenn ADC10 core aktive ist
     ADC10SA = (unsigned int)&ADC10_Buffer[0];      // Data buffer start
     ADC10CTL0 |= ENC + ADC10SC;       // Starte AD-Konversion
     // __bis_SR_register(CPUOFF + GIE);  // LPM0, Interrupt erlauben

     if (TasterGet())                  // Wenn Taster gedrückt ...
     {
        LedSet(1);                     // LED einschalten
     } else
     {
        LedSet(0);                     // LED ausschalten
     }

  }
}



#pragma vector=ADC10_VECTOR            // ADC ISR
__interrupt void ADC10_ISR(void)
{
  __bic_SR_register_on_exit(CPUOFF);   // Lösche CPUOFF Bit in 0(SR)
}

Autor: Jörg S. (joerg-s)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
>Muss ich denn LMP0 und Interrupt eigentlich benützen, weil ich warte ja
>sowieso bis die Konversion fertig ist bevor ich mit einer neuen starte?
Nein musst du nicht. Es ist alles auch immer ohne Interrupt möglich.

Autor: Stefan (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
>Also ich verwende ja den DTC mit 16 MHz.
>Macht das keinen Unterschied, ob ich MCLK oder SMCLK verwende?
Der ADC10 will nur einen Clock innerhalb der Spezifikation haben, wo der 
herkommt ist wurscht.... ausser Du nimmst z.B. MCLK und schaltest MCLK 
dann ab ;-)

>Ich hatte vorhin das mit dem LMP0 einfach auskommentiert und es
>funktionierte.
Klar, damit hast Du ja MCLK nicht deaktiviert!

>Zurzeit habe ich den MSC aktiviert und CONSEQ_3, ohne das hat es nicht
>funktioniert. Ist das schon richtig so?
Von der Funktionsweise schon, aber nur weil Du
ADC10CTL0 &= ~ENC;
eingefügt hast!

Von der Logik her würde ich CONSEQ_1 nehmen, denn Du willst ja A0-A3 
einmal sampeln. Eine neue Sequenz startest Du ja jedesmal manuell und 
nicht automatisch:
ADC10CTL0 |= ENC + ADC10SC;       // Starte AD-Konversion

Dann kannst Du Dir auch das Rücksetzten von ENC sparen.
Also so etwa:
//  ADC10CTL0 &= ~ENC;
    while (ADC10CTL1 & BUSY);         // Warte wenn ADC10 core aktive ist
    ADC10SA = (unsigned int)&ADC10_Buffer[0];      // Data buffer start
    ADC10CTL0 |= ADC10SC;            // Starte AD-Konversion
// __bis_SR_register(CPUOFF + GIE);  // LPM0, Interrupt erlauben

    if (TasterGet())                  // Wenn Taster gedrückt ...
    {
       LedSet(1);                     // LED einschalten
    } 
    else
    {
       LedSet(0);                     // LED ausschalten
    }

Autor: Stefan (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Ich würde mir an Deiner Stelle im Übrigen mal das "Design" überdenken.
Deine Konstruktion steht und fällt mit TasterGet().
Wenn TasterGet() sehr schnell erledigt ist, kann es sein, dass Du Deine 
for(;;) erneut aufrufst und die Buffer sicherst, obwohl vielleicht noch 
gar nicht alle ADC-Werte gesampelt wurden!
Vielleicht hilft es schon
while (ADC10CTL1 & BUSY);         // Warte wenn ADC10 core aktive ist
ganz an den Anfang der for(;;) zu platzieren ?!

Wobei ich mir nicht 100%ig sicher bin, ob BUSY während der gesamten 
Sequenz gesetzt bleibt, oder nur während jeder einzelnen Wandlung. Der 
User-Guide sagt "...Sequence, sample or conversion is active...". Dann 
sollte es schon gehen, eigentlich...

Autor: Sebastian (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Also es funktioniert jetzt soweit alles.
Vielen Dank nochmals für eure Hilfe.

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.