Forum: Mikrocontroller und Digitale Elektronik MSP430 ADC12 Problem


von Andreas D. (galdo)


Lesenswert?

Hallo,

ich habe ein Problem mit dem ADC12 des MSP430FG4618 DevBoard.

Ich habe eine Interruptroutine für den ADC-Conversion Vorgang 
programmiert, die allerdings nicht mehr verlassen wird, d.h. es wird 
nicht mehr in den Main-Loop des Programms zurückgekehrt. Hat jemand eine 
Idee, woran das liegen könnte?

Hier erst mal meine init-routine:
1
void init_adc() {
2
P6SEL = 0x0F;
3
  ADC12CTL0 = ADC12ON + MSC + SHT0_2;   // Turn on ADC12, set sampling time
4
  ADC12CTL1 = SHP + CONSEQ_1;           // Use sampling timer, single sequ
5
  ADC12MCTL0 = INCH_0;                  // ref+=AVcc, channel = A0 
6
  ADC12MCTL1 = INCH_1;                  // ref+=AVcc, channel = A1
7
  ADC12MCTL2 = INCH_2;                  // ref+=AVcc, channel = A2
8
  ADC12MCTL3 = INCH_3 + EOS;            // ref+=AVcc, channel = A3, end seq 
9
  ADC12IE = 0x08;                       // Enable interrupt
10
  ADC12CTL0 |= ENC;                     // Conversion enabled 
11
}

Die wird aufgerufen durch:
1
WDTCTL = WDTPW + WDTHOLD;  // Stop watchdog timer
2
FLL_CTL0 |= XCAP18PF;  // Set LoadCap 
3
init_adc();  
4
_BIS_SR(CPUOFF + GIE);

und hier jetzt die Routine selbst - die aber eigentlich nicht viel 
macht, außer eine Ausgabe auf die Serielle Schnittstelle zu schreiben:
1
__interrupt void ADC_ISR (void);
2
ADC12_ISR(ADC_ISR)
3
__interrupt void ADC_ISR (void) {
4
    printUART("ADC-IR\r\n",sizeof("ADC-IR\r\n"));
5
    _BIC_SR_IRQ(CPUOFF);     
6
    _NOP();
7
}

Im Main-Loop steht noch folgendes:
1
while (true){
2
  P2OUT ^= 0x02;         
3
  DC12CTL0 |= ADC12SC;          
4
  for (i = 0; i < 20000; i++);
5
}

Hat vielleicht jemand eine Idee? Wie gesagt - wird die 
Interrupt-Routine, des ADCs nicht verlassen, bzw. dauert so lange, dass 
bevor ins Hauptprogramm zurückgesprungen werden kann, wieder die Routine 
aufgerufen wird.

Ich kenn mich leider mit dem TI nicht so gut aus, daher wäre - falls es 
ein Zeitproblem wäre - die Frage, wie ich die Aufrufzeit der ISR 
bestimmen kann. Also wie kann ich einstellen, mit welcher Frequenz der 
Interrupt "gefeuert" wird?

Danke GALDO

von Stefan (Gast)


Lesenswert?

Abgesehen vom Programmierstil: Was für eine Datentyp hat den i?

von Andreas D. (galdo)


Lesenswert?

Was ist denn mit dem Programmierstil?
1
volatile unsigned int i = 0;

Galdo

von Rufus Τ. F. (rufus) Benutzerseite


Lesenswert?

"printUART" in einer Interruptroutine aufrufen? Ist das eine gute Idee?

von Andreas D. (galdo)


Lesenswert?

Mir ist schon bewusst, dass "sowas" ewíg dauert, allerdings - macht es 
auch keinen Unterschied, wenn ich das nicht verwende.

Das Problem ist in meinen Augen, dass der Interrupt viel zu hoch 
frequentiert ist und ich nicht weiß, wie ich das vernünftig einstellen 
kann. Ich habe den Code stellenweiße aus den Beispielen von TI - aber 
die Informationen dazu sind sehr gering :(

Er kommt - ob mit oder ohne UART - nicht mehr aus der ISR rauß - somit 
wird nur noch eine Wandlung abgearbeitet, ich habe aber auch noch 
anderen Code, der mal verarbeitet werden müsste - somit brauch ich 
unbedingt noch Ausführungszeit für den Main-Loop.

Hat jemand noch ne Idee?

Danke GALDO

von Andreas D. (galdo)


Lesenswert?

Ich antworte mir mal selbst:

Ich habe jetzt eine BruteForce-Lösung programmiert, allerdings wäre eure 
Meinung dazu nicht schlecht.

Ich schalte einfach
1
ADC12IE = 0x00;
 in der ADC12_ISR, mit einem Timer von etwa 4Hz bis 6Hz (müsste man mal 
mim Oszi plotten), schalte ich jetzt einfach mit dieser Frequenz die
1
ADC12IE = 0x0F;
 und damit ein.

Das funktioniert soweit ganz gut - allerdings habe ich damit noch keine 
tatsächliche Wandlung versucht - das werde ich demnächst mal testen.

Aber das wäre - sofern es funktioniert - genau das was ich brauche, AD 
auslesen, wandeln, zurückschicken - warten - AD auslesen, wandeln, 
zurückschicken - warte - etc.

Wie würdet ihr obige Anforderung implementieren?

Danke für weitere Anregungen und Hinweise
GALDO

von Karl H. (kbuchegg)


Lesenswert?

Andreas del Galdo wrote:

> Aber das wäre - sofern es funktioniert - genau das was ich brauche, AD
> auslesen, wandeln, zurückschicken - warten - AD auslesen, wandeln,
> zurückschicken - warte - etc.
>
> Wie würdet ihr obige Anforderung implementieren?

Nicht so.

Eher so:

  Interrupt kommt

  ADC wird ausgelesen
  Wert wird in einer globalen Variablen bereitgestellt
  zusätzliche globale Variable auf 1 setzen (das sog. Jobflag)
  um der ganzen Welt anzuzeigen, dass ein ADC Wert verfügbar ist
  und etwas damit gemacht werden müsste.

  Interrupt fertig


In der Hauptschleife in main()

  while( 1 ) {

   das Jobflag für den ADC abfragen
   ist es 0  -> kein Wert da, weiter wie bisher
   ist es 1  ->
        Interrupt sperren
        Wert von der globalen Variablen wegholen
        Jobflag wieder auf 0 setzen.
        Interrupt freigeben
        Wert formatieren und über UART ausgeben

  }


Auf die Art ist die Interrupt Routine so kurz wie es nur
irgendwie geht und lässt noch jede Menge Rechenzeit für
anderes übrig.

von Christian R. (supachris)


Lesenswert?

1
DC12CTL0 |= ADC12SC;

Ist das ein Schreibfehler? Da fehlt das A. Außerdem: Sobald das SC Bit 
gesetzt wird, startet der Sequenzer und wandelt die 4 Werte und löst den 
Int aus.

Außerdem muss man soweit ich mich erinnere, das ADC12IFG Register selber 
löschen, weiß nich ob das nen Bug ist oder gewollt....

von Andreas D. (galdo)


Lesenswert?

Ja - das war ein Copy&Paste Fehler - da wurd das A verschluckt!

von Andreas D. (galdo)


Lesenswert?

Ich hab jetzt mal ein Netzgerät angeschlossen (den Pluspol an einen 
Kanal) und siehe da - es kommt Käse rauß...

Ohne Irgendwas dran hätte doch eigentlich als 0 1,25V raußkommen sollen 
- und was bekomme ich:
1
Result: 0.318V, 0.923V, 0.001V, 0.732V

Sobald ich dann das Netzgerät - eingestellt auf etwa 3V anschließe 
bekomme ich so abstruße Werte wie 0V, oder 4,2V nur nicht drei - und es 
kommt auch drauf an, wo ich das Netzgerät anschließe - die Werte ändern 
sich an jedem Kanal anders.

Hier nochmal der Init-Code:
1
void init_adc() {
2
  P6SEL = 0x0F;
3
  ADC12CTL0   = ADC12ON + MSC + SHT0_2;
4
  ADC12CTL1   = SHP + CONSEQ_1;           // Use sampling timer, single sequ
5
  ADC12MCTL0   = INCH_0;                  // ref+=AVcc, channel = A0 //SREF_1
6
  ADC12MCTL1   = INCH_1;                  // ref+=AVcc, channel = A1
7
  ADC12MCTL2   = INCH_2;                  // ref+=AVcc, channel = A2
8
  ADC12MCTL3   = INCH_3 + EOS;            // ref+=AVcc, channel = A3, end seq 
9
  ADC12IE     = 0x0F;                       // Enable interrupt
10
  ADC12CTL0   |= ENC;                     // Conversion enabled 
11
}

Hat jemand vielleicht nen Tipp, woran das liegen könnte?

Vielen Dank
Galdo

von Andreas D. (galdo)


Lesenswert?

Erledigt...

War ein Denkfehler - somit ist alles korrekt!

Vielen Dank für eure Hilfe!

Galdo

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.