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


von Sebastian (Gast)


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.
1
#include <msp430x22x2.h>              // Header Datei des verwendeten uC
2
3
// #include <system.h>                   // Definition des Systems -> Hier: PC
4
// #include <compiler.h>                 // Variabelbegriffe
5
6
7
8
void main(void)
9
{
10
  WDTCTL = WDTPW + WDTHOLD;           // Watchdog Timer (WDT) anhalten; Passwort benötigt
11
12
  BCSCTL1= CALBC1_16MHZ;              // DCO Clock auf 16 MHz einstellen
13
  DCOCTL = CALDCO_16MHZ;
14
15
  P1DIR &= ~0x0F;                     // Port 1 Pin 0-3 Input
16
  P3DIR |=  0x80;                     // Port 3 Pin 7 Output
17
  P3DIR &= ~0x40;                     // Port 3 Pin 6 Input
18
  P4DIR |=  0x18;                     // Port 4 Pin 4,3 Output
19
  P4DIR &= ~0x04;                     // Port 4 Pin 2 Input
20
21
  P1SEL &= ~0x0F;                     // Port 1 Pin 0-3 I/O
22
  P2SEL  =  0x0F;                     // Port 2 Pin 0-3 ADC, Rest I/O
23
  P3SEL  =  0x3E;                     // Port 3 Pin 1-3 DAC, Pin 4-5 UART, Rest I/O
24
  P4SEL  =  0x20;                     // Port 4 Pin 5 ADC, Rest I/O
25
26
  ADC10CTL0 =  0x0018;                // ADC erlauben, Interrupt erlauben
27
  ADC10CTL1 =  0x3012;                // channel A0..A3
28
  ADC10DTC1 = 0x04;                   // 4 Konversionen
29
  ADC10AE0  = 0x0F;
30
31
32
  for (;;)                            // Endlosschleife [== while (1) ]
33
  {
34
     //unsigned int i;
35
36
37
     if (P3IN & 0x40)                  // Wenn Taster gedrückt ...
38
     {
39
        P3OUT &= ~0x80;                // LED einschalten
40
     } else
41
     {
42
        P3OUT |=  0x80;                // LED ausschalten
43
     }
44
45
     ADC10CTL0 &= ~ENC;
46
     while (ADC10CTL1 & BUSY);         // Warte wenn ADC10 core aktive ist
47
     ADC10SA = 0x200;                  // Data buffer start
48
     ADC10CTL0 |= ADC10SC + ENC;       // Starte AD-Konversion
49
     __bis_SR_register(CPUOFF + GIE);  // LPM0, Interrupt erlauben
50
51
     if (ADC10SA > 0x0010)
52
     {
53
        P4OUT |= 0x10;
54
     } else
55
     {
56
        P4OUT &= ~0x10;
57
     }
58
59
60
     /*
61
     i = 50000;                        // Wartezeit durch Schleife...
62
     do (i--);                         // ...mit 50.000 Durchläufen
63
     while (i != 0);
64
     */
65
66
  }
67
}
68
69
70
71
#pragma vector=ADC10_VECTOR            // ADC ISR
72
__interrupt void ADC10_ISR(void)
73
{
74
  __bic_SR_register_on_exit(CPUOFF);   // Lösche CPUOFF Bit in 0(SR)
75
}

von Sebastian (Gast)


Angehängte Dateien:

Lesenswert?

hier TI code

von Jörg S. (joerg-s)


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!)
1
unsigned int ADC10_Buffer[4]; // 4 ADC Ergebnisse
2
:
3
ADC10SA = (unsigned int)&ADC10_Buffer[0];    // Zielspeicheradresse zuweisen  
4
:

von Sebastian (Gast)


Lesenswert?

Hallo jörg

Ich habe das mal so eingefügt:
1
     ADC10CTL0 &= ~ENC;
2
     while (ADC10CTL1 & BUSY);         // Warte wenn ADC10 core aktive ist
3
     ADC10SA = 0x200;                  // Data buffer start
4
     ADC10CTL0 |= ADC10SC + ENC;       // Starte AD-Konversion
5
     __bis_SR_register(CPUOFF + GIE);  // LPM0, Interrupt erlauben
6
     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).

von Sebastian (Gast)


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:
1
/* Includes */
2
#include <msp430x22x2.h>              // Header Datei des verwendeten uC
3
#include "system.h"
4
#include "compiler.h"
5
6
#include "ioTeach.h"                  // Led und Taster
7
8
9
/* Globale Variablen */
10
unsigned int ADC10_Buffer[4];         // AD Werte Buffer
11
12
13
14
void main(void)
15
{
16
  WDTCTL = WDTPW + WDTHOLD;           // Watchdog Timer (WDT) anhalten; Passwort benötigt
17
18
  BCSCTL1= CALBC1_16MHZ;              // DCO Clock auf 16 MHz einstellen
19
  DCOCTL = CALDCO_16MHZ;
20
21
  P1DIR &= ~0x0F;                     // Port 1 Pin 0-3 Input
22
  P3DIR |=  0x80;                     // Port 3 Pin 7 Output
23
  P3DIR &= ~0x40;                     // Port 3 Pin 6 Input
24
  P4DIR |=  0x18;                     // Port 4 Pin 4,3 Output
25
  P4DIR &= ~0x04;                     // Port 4 Pin 2 Input
26
27
  P1SEL &= ~0x0F;                     // Port 1 Pin 0-3 I/O
28
  P2SEL  =  0x0F;                     // Port 2 Pin 0-3 ADC, Rest I/O
29
  P3SEL  =  0x3E;                     // Port 3 Pin 1-3 DAC, Pin 4-5 UART, Rest I/O
30
  P4SEL  =  0x20;                     // Port 4 Pin 5 ADC, Rest I/O
31
32
  ADC10CTL0 =  0x0098;                // ADC erlauben, Interrupt erlauben
33
  ADC10CTL1 =  0x3016;                // channel A0..A3
34
  ADC10DTC1 = 0x04;                   // 4 Konversionen
35
  ADC10AE0  = 0x0F;
36
37
38
  for (;;)                            // Endlosschleife [== while (1) ]
39
  {
40
     if (TasterGet())                 // Wenn Taster gedrückt ...
41
     {
42
        LedSet(1);                    // LED einschalten
43
     } else
44
     {
45
        LedSet(0);                    // LED ausschalten
46
     }
47
48
     ADC10CTL0 &= ~ENC;
49
     while (ADC10CTL1 & BUSY);         // Warte wenn ADC10 core aktive ist
50
     ADC10SA = (unsigned int)&ADC10_Buffer[0];      // Data buffer start
51
     ADC10CTL0 |= ADC10SC + ENC;       // Starte AD-Konversion
52
     __bis_SR_register(CPUOFF + GIE);  // LPM0, Interrupt erlauben
53
54
55
     /*
56
     i = 50000;                        // Wartezeit durch Schleife...
57
     do (i--);                         // ...mit 50.000 Durchläufen
58
     while (i != 0);
59
     */
60
  }
61
}
62
63
64
65
#pragma vector=ADC10_VECTOR            // ADC ISR
66
__interrupt void ADC10_ISR(void)
67
{
68
  __bic_SR_register_on_exit(CPUOFF);   // Lösche CPUOFF Bit in 0(SR)
69
}

von Jörg S. (joerg-s)


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.

von Stefan (Gast)


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:
1
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:
1
...
2
ADC10CTL1 =  0x3016;
3
...
und statt dessen die im Headerfile definierten Bit-Masken benutzten.
Ist zum einen weniger Fehleranfällig und auch leichter zu lesen und zu
interpretieren!
1
ADC10CTL1 =  INCH_3 + ADC10SSEL_2 + CONSEQ_3;

von Sebastian (Gast)


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.
1
/* Includes */
2
#include <msp430x22x2.h>              // Header Datei des verwendeten uC
3
#include "system.h"
4
#include "compiler.h"
5
6
#include "ioTeach.h"                  // Led und Taster
7
8
9
/* Globale Variablen */
10
unsigned int ADC10_Buffer[4];         // AD Werte Buffer
11
12
13
14
void main(void)
15
{
16
  WDTCTL = WDTPW + WDTHOLD;           // Watchdog Timer (WDT) anhalten; Passwort benötigt
17
18
  BCSCTL1= CALBC1_16MHZ;              // DCO Clock auf 16 MHz einstellen
19
  DCOCTL = CALDCO_16MHZ;
20
21
  P1DIR &= ~0x0F;                     // Port 1 Pin 0-3 Input
22
  P3DIR |=  0x80;                     // Port 3 Pin 7 Output
23
  P3DIR &= ~0x40;                     // Port 3 Pin 6 Input
24
  P4DIR |=  0x18;                     // Port 4 Pin 4,3 Output
25
  P4DIR &= ~0x04;                     // Port 4 Pin 2 Input
26
27
  P1SEL &= ~0x0F;                     // Port 1 Pin 0-3 I/O
28
  P2SEL  =  0x0F;                     // Port 2 Pin 0-3 ADC, Rest I/O
29
  P3SEL  =  0x3E;                     // Port 3 Pin 1-3 DAC, Pin 4-5 UART, Rest I/O
30
  P4SEL  =  0x20;                     // Port 4 Pin 5 ADC, Rest I/O
31
32
  ADC10CTL0 =  ADC10SHT_2 + MSC + ADC10ON + ADC10IE;                // ADC erlauben, Interrupt erlauben
33
  ADC10CTL1 =  INCH_3 + ADC10SSEL_2 + CONSEQ_1 + ADC10DIV_2;        // channel A0..A3
34
  ADC10DTC1 = 0x04;                   // 4 Konversionen
35
  ADC10AE0 |= 0x0F;
36
37
38
  for (;;)                            // Endlosschleife [== while (1) ]
39
  {
40
     if (TasterGet())                 // Wenn Taster gedrückt ...
41
     {
42
        LedSet(1);                    // LED einschalten
43
     } else
44
     {
45
        LedSet(0);                    // LED ausschalten
46
     }
47
48
     ADC10CTL0 &= ~ENC;
49
     while (ADC10CTL1 & BUSY);         // Warte wenn ADC10 core aktive ist
50
     ADC10SA = (unsigned int)&ADC10_Buffer[0];      // Data buffer start
51
     ADC10CTL0 |= ENC + ADC10SC;       // Starte AD-Konversion
52
     __bis_SR_register(CPUOFF + GIE);  // LPM0, Interrupt erlauben
53
54
55
     /*
56
     i = 50000;                        // Wartezeit durch Schleife...
57
     do (i--);                         // ...mit 50.000 Durchläufen
58
     while (i != 0);
59
     */
60
  }
61
}
62
63
64
65
#pragma vector=ADC10_VECTOR            // ADC ISR
66
__interrupt void ADC10_ISR(void)
67
{
68
  __bic_SR_register_on_exit(CPUOFF);   // Lösche CPUOFF Bit in 0(SR)
69
}

von Stefan (Gast)


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!

von Sebastian (Gast)


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.

1
  ADC10CTL0 =  ADC10SHT_2 + MSC + ADC10ON + ADC10IE;                // ADC erlauben, Interrupt erlauben
2
  ADC10CTL1 =  INCH_3 + ADC10SSEL_2 + CONSEQ_3 + ADC10DIV_2;        // channel A0..A3
3
  ADC10DTC1 = 0x04;                   // 4 Konversionen
4
  ADC10AE0 |= 0x0F;
5
6
7
8
  for (;;)                             // Endlosschleife [== while (1) ]
9
  {
10
     ADC10_Calc[0] = ADC10_Buffer[0];  // Buffer Werte zur Sicherheit speichern
11
     ADC10_Calc[1] = ADC10_Buffer[1];
12
     ADC10_Calc[2] = ADC10_Buffer[2];
13
     ADC10_Calc[3] = ADC10_Buffer[3];
14
15
     ADC10CTL0 &= ~ENC;
16
     while (ADC10CTL1 & BUSY);         // Warte wenn ADC10 core aktive ist
17
     ADC10SA = (unsigned int)&ADC10_Buffer[0];      // Data buffer start
18
     ADC10CTL0 |= ENC + ADC10SC;       // Starte AD-Konversion
19
     // __bis_SR_register(CPUOFF + GIE);  // LPM0, Interrupt erlauben
20
21
     if (TasterGet())                  // Wenn Taster gedrückt ...
22
     {
23
        LedSet(1);                     // LED einschalten
24
     } else
25
     {
26
        LedSet(0);                     // LED ausschalten
27
     }
28
29
  }
30
}
31
32
33
34
#pragma vector=ADC10_VECTOR            // ADC ISR
35
__interrupt void ADC10_ISR(void)
36
{
37
  __bic_SR_register_on_exit(CPUOFF);   // Lösche CPUOFF Bit in 0(SR)
38
}

von Jörg S. (joerg-s)


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.

von Stefan (Gast)


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
1
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:
1
ADC10CTL0 |= ENC + ADC10SC;       // Starte AD-Konversion

Dann kannst Du Dir auch das Rücksetzten von ENC sparen.
Also so etwa:
1
//  ADC10CTL0 &= ~ENC;
2
    while (ADC10CTL1 & BUSY);         // Warte wenn ADC10 core aktive ist
3
    ADC10SA = (unsigned int)&ADC10_Buffer[0];      // Data buffer start
4
    ADC10CTL0 |= ADC10SC;            // Starte AD-Konversion
5
// __bis_SR_register(CPUOFF + GIE);  // LPM0, Interrupt erlauben
6
7
    if (TasterGet())                  // Wenn Taster gedrückt ...
8
    {
9
       LedSet(1);                     // LED einschalten
10
    } 
11
    else
12
    {
13
       LedSet(0);                     // LED ausschalten
14
    }

von Stefan (Gast)


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
1
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...

von Sebastian (Gast)


Lesenswert?

Also es funktioniert jetzt soweit alles.
Vielen Dank nochmals für eure Hilfe.

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.