Das MSP430-Programm funktioniert nur im Debug-Modus

7

Ich versuche, die Werte vom ADC auf meinem MSP430F5529 abzurufen und über USB an meinen Computer zu senden, aber ich fange klein an. Alles, was ich gerade habe, ist etwas, das den ADC-Wert abruft und in ADCResults speichert. Wenn der Lesewert mehr als die Hälfte von Vcc beträgt, leuchtet eine LED auf.

Ich habe Pin 6.0 an einen Kraftsensor angeschlossen, damit ich sehen kann, wie er sich aus- und wieder einschaltet, wenn ich meinen Finger darauf lege oder loslasse.

Das Programm funktioniert einwandfrei, wenn ich es im Debug-Modus ausführe. Wenn ich jedoch versuche, es außerhalb des Debug-Modus auszuführen (nur die Karte vom Computer aus mit Strom zu versorgen, nachdem der Code darauf heruntergeladen wurde), passiert nichts, wenn ich meinen Finger auf den Kraftsensor lege.

Was extrem seltsam ist, ist, wenn ich den Reset gedrückt halte, während ich meinen Finger auf den Kraftsensor lege (wenn ich meinen Finger nach unten lege, leuchtet die LED auf) und die Reset-Taste loslässt, bleibt die LED an, bis ich erneut auf Reset drücke, während ich den Finger davon abdrücke. Es scheint also, dass das Zurücksetzen ein Problem verursacht, aber ich bin mir nicht sicher, wie.

Zuerst dachte ich, dass das Zurücksetzen konstant hoch (oder niedrig, was auch immer das Gerät zurücksetzt) ​​gezogen wird, aber das kann nicht wahr sein, weil dann das Programm funktionieren sollte, wenn ich das Zurücksetzen gedrückt halte, aber es nicht!

Hier ist mein Code:

#include "driverlib.h"

volatile uint16_t ADCResults = 0;

void main(void)
{
    //Stop Watchdog Timer
    WDT_A_hold(WDT_A_BASE);

    //P6.0 ADC option select
    GPIO_setAsPeripheralModuleFunctionOutputPin(
        GPIO_PORT_P6,
        GPIO_PIN0
        );

    GPIO_setAsOutputPin(
        GPIO_PORT_P1,
        GPIO_PIN0
        );

    //Initialize the ADC12_A_A Module
    /*
     * Base address of ADC12_A_A Module
     * Use internal ADC12_A_A bit as sample/hold signal to start conversion
     * USE MODOSC 5MHZ Digital Oscillator as clock source
     * Use default clock divider of 1
     */
    ADC12_A_init(ADC12_A_BASE,
                 ADC12_A_SAMPLEHOLDSOURCE_SC,
                 ADC12_A_CLOCKSOURCE_ADC12OSC,
                 ADC12_A_CLOCKDIVIDER_1);

    ADC12_A_enable(ADC12_A_BASE);

    /*
     * Base address of ADC12_A_A Module
     * For memory buffers 0-7 sample/hold for 64 clock cycles
     * For memory buffers 8-15 sample/hold for 4 clock cycles (default)
     * Disable Multiple Sampling
     */
    ADC12_A_setupSamplingTimer(ADC12_A_BASE,
                               ADC12_A_CYCLEHOLD_64_CYCLES,
                               ADC12_A_CYCLEHOLD_4_CYCLES,
                               ADC12_A_MULTIPLESAMPLESDISABLE);

    //Configure Memory Buffer
    /*
     * Base address of the ADC12_A_A Module
     * Configure memory buffer 0
     * Map input A0 to memory buffer 0
     * Vref+ = AVcc
     * Vr- = AVss
     * Memory buffer 0 is not the end of a sequence
     */
    ADC12_A_configureMemoryParam param = {0};
    param.memoryBufferControlIndex = ADC12_A_MEMORY_0;
    param.inputSourceSelect = ADC12_A_INPUT_A0;
    param.positiveRefVoltageSourceSelect = ADC12_A_VREFPOS_AVCC;
    param.negativeRefVoltageSourceSelect = ADC12_A_VREFNEG_AVSS;
    param.endOfSequence = ADC12_A_NOTENDOFSEQUENCE;
    ADC12_A_configureMemory(ADC12_A_BASE,&param);

    //Enable memory buffer 0 interrupt
    ADC12_A_clearInterrupt(ADC12_A_BASE,
                           ADC12IFG0);
    ADC12_A_enableInterrupt(ADC12_A_BASE,
                            ADC12IE0);

    while(1)
    {
        //Enable/Start sampling and conversion
        /*
         * Base address of ADC12_A_A Module
         * Start the conversion into memory buffer 0
         * Use the single-channel, single-conversion mode
         */
        ADC12_A_startConversion(ADC12_A_BASE,
                                ADC12_A_MEMORY_0,
                                ADC12_A_SINGLECHANNEL);

        //LPM0, ADC12_A_ISR will force exit
        __bis_SR_register(LPM0_bits + GIE);
        //for Debugger
        __no_operation();
    }
}

#if defined(__TI_COMPILER_VERSION__) || defined(__IAR_SYSTEMS_ICC__)
#pragma vector=ADC12_VECTOR
__interrupt
#elif defined(__GNUC__)
__attribute__((interrupt(ADC12_VECTOR)))
#endif
void ADC12_A_ISR(void)
{
    switch(__even_in_range(ADC12IV,34))
    {
    case  0: break;       //Vector  0:  No interrupt
    case  2: break;       //Vector  2:  ADC overflow
    case  4: break;       //Vector  4:  ADC timing overflow
    case  6:              //Vector  6:  ADC12IFG0
        //Is Memory Buffer 0 = A0 > 0.5AVcc?

          ADCResults = ADC12_A_getResults(ADC12_A_BASE,
                                        ADC12_A_MEMORY_0);
        if(ADCResults
           >= 0x7ff)
        {
            //set P1.0
            GPIO_setOutputHighOnPin(
                GPIO_PORT_P1,
                GPIO_PIN0
                );
        }
        else
        {
            //Clear P1.0 LED off
            GPIO_setOutputLowOnPin(
                GPIO_PORT_P1,
                GPIO_PIN0
                );
        }

        //Exit active CPU
        __bic_SR_register_on_exit(LPM0_bits);
    case  8: break;       //Vector  8:  ADC12IFG1
    case 10: break;       //Vector 10:  ADC12IFG2
    case 12: break;       //Vector 12:  ADC12IFG3
    case 14: break;       //Vector 14:  ADC12IFG4
    case 16: break;       //Vector 16:  ADC12IFG5
    case 18: break;       //Vector 18:  ADC12IFG6
    case 20: break;       //Vector 20:  ADC12IFG7
    case 22: break;       //Vector 22:  ADC12IFG8
    case 24: break;       //Vector 24:  ADC12IFG9
    case 26: break;       //Vector 26:  ADC12IFG10
    case 28: break;       //Vector 28:  ADC12IFG11
    case 30: break;       //Vector 30:  ADC12IFG12
    case 32: break;       //Vector 32:  ADC12IFG13
    case 34: break;       //Vector 34:  ADC12IFG14
    default: break;
    }
}

AKTUALISIEREN

Ich habe versucht, die gleiche Funktionalität ohne Verwendung der Peripherietreiberbibliothek herzustellen, und es scheint perfekt außerhalb des Debuggers zu funktionieren. Dies lässt mich glauben, dass etwas mit der Texas Instruments Peripheral Driver Library nicht stimmt.

Hier ist der Code, der außerhalb des Debuggers einwandfrei zu funktionieren schien und die Peripheral Driver Library nicht verwendet.

#include <msp430.h>

int main(void)
{
  WDTCTL = WDTPW + WDTHOLD;                 // Stop WDT
  ADC12CTL0 = ADC12SHT02 + ADC12ON;         // Sampling time, ADC12 on
  ADC12CTL1 = ADC12SHP;                     // Use sampling timer
  ADC12IE = 0x01;                           // Enable interrupt
  ADC12CTL0 |= ADC12ENC;
  P6SEL |= 0x01;                            // P6.0 ADC option select
  P1DIR |= 0x01;                            // P1.0 output

  while (1)
  {
    ADC12CTL0 |= ADC12SC;                   // Start sampling/conversion

    __bis_SR_register(LPM0_bits + GIE);     // LPM0, ADC12_ISR will force exit
    __no_operation();                       // For debugger
  }
}

#if defined(__TI_COMPILER_VERSION__) || defined(__IAR_SYSTEMS_ICC__)
#pragma vector = ADC12_VECTOR
__interrupt void ADC12_ISR(void)
#elif defined(__GNUC__)
void __attribute__ ((interrupt(ADC12_VECTOR))) ADC12_ISR (void)
#else
#error Compiler not supported!
#endif
{
  switch(__even_in_range(ADC12IV,34))
  {
  case  0: break;                           // Vector  0:  No interrupt
  case  2: break;                           // Vector  2:  ADC overflow
  case  4: break;                           // Vector  4:  ADC timing overflow
  case  6:                                  // Vector  6:  ADC12IFG0
    if (ADC12MEM0 >= 0x7ff)                 // ADC12MEM = A0 > 0.5AVcc?
      P1OUT |= BIT0;                        // P1.0 = 1
    else
      P1OUT &= ~BIT0;                       // P1.0 = 0

    __bic_SR_register_on_exit(LPM0_bits);   // Exit active CPU
  case  8: break;                           // Vector  8:  ADC12IFG1
  case 10: break;                           // Vector 10:  ADC12IFG2
  case 12: break;                           // Vector 12:  ADC12IFG3
  case 14: break;                           // Vector 14:  ADC12IFG4
  case 16: break;                           // Vector 16:  ADC12IFG5
  case 18: break;                           // Vector 18:  ADC12IFG6
  case 20: break;                           // Vector 20:  ADC12IFG7
  case 22: break;                           // Vector 22:  ADC12IFG8
  case 24: break;                           // Vector 24:  ADC12IFG9
  case 26: break;                           // Vector 26:  ADC12IFG10
  case 28: break;                           // Vector 28:  ADC12IFG11
  case 30: break;                           // Vector 30:  ADC12IFG12
  case 32: break;                           // Vector 32:  ADC12IFG13
  case 34: break;                           // Vector 34:  ADC12IFG14
  default: break; 
  }
}
Aaron
quelle
Tun Sie dies auf einem LaunchPad oder einem anderen TI-Entwicklungsboard? Oder haben Sie das entworfen?
DigitalNinja
1
Ich bin mit MSP430s nicht vertraut genug, um sicher zu sein, aber: Könnte es sein, dass der WDT durch ein Konfigurationsbit erzwungen wird? Bei einigen uC können Sie die WDT erzwingen, die möglicherweise vom DBGU-Modul überschrieben wird. Gleiches gilt für alle anderen (Schutz-) Interrupts. Wenn ich vertrauter wäre, würde ich Ihren Code überprüfen, aber Interrupts sind eine sehr häufige Quelle für diese Art von Verhalten. (Genau wie der Divisionsüberlauf-Interrupt: Wird auf vielen Geräten nicht im Debug-Modus ausgelöst, wird jedoch erzwungen ausgelöst, wenn er nicht im Debug-Modus ist.)
Asmyldof
@ DigitalNinja Ja, ich bin auf einem Launchpad das MSP430F5529 Launchpad
Aaron
@Asmyldof Ich bin nicht sicher, ob der WDT es tut, der Code deaktiviert den WDT eindeutig, aber ich bin immer noch ein Neuling, also habe ich keine Ahnung.
Aaron
1
@Aaron Die Art und Weise, wie Sie den Vorgang mit der Reset-Taste beschrieben haben, klingt so, als würde er einfach nur zurückgesetzt. Wenn Sie den Reset beenden, wird Ihre Eingabe angezeigt und der Reset wird wiederhergestellt, wenn Sie den Tastendruck entfernen. Durch erneutes Drücken wird es herausgenommen und die LED neu initialisiert. Normalerweise kümmert sich der Debugger um das Zurücksetzen für Sie, da er die Rücksetzzeile für die Programmierung verwendet. Haben Sie Ihre Jumper-Konfiguration überprüft? Es gibt einen Jumper zum Zurücksetzen mit der Bezeichnung (RST).
DigitalNinja

Antworten:

3

Manchmal liegt der Grund für ein solches Verhalten darin, dass die Optimierungseinstellungen im Debug-Modus unterschiedlich sind und eine Variable, die der Compiler für nicht erforderlich hält, sofort optimiert wird.

Die Korrekturen hierfür bestehen darin, solchen Variablen "flüchtige" Qualifikationsmerkmale hinzuzufügen oder die Optimierung zu deaktivieren (oder zumindest zu verringern).

Ich weiß nicht, ob dies Ihre Antwort ist (der Thread ist TL; DR), aber dieser Leckerbissen sollte auf jeden Fall als mögliche Lösung für die Suchmaschinen erscheinen.

Scott Seidman
quelle
volatileQualifikation war dort im ersten Code, also obwohl oft ein Problem, in diesem Fall eher nicht
Arsenal
@Arsenal - das ist die Variable kann er sehen . Vielleicht ist etwas in einer Bibliothek begraben, von der er nichts weiß. Der erste Durchgang für mich wäre, die Optimierung auszuschalten. Das Lesen dauert zehn Minuten, um herauszufinden, wie Sie es tun, und dann wissen Sie es.
Scott Seidman
@ ScottSeidman Vielen Dank für den Vorschlag, Optimierungen waren von Anfang an aus, ich arbeite so ziemlich immer mit ihnen zu Beginn
Aaron
Die Bibliothek wandelt jeden Zugriff auf die Hardware in einen flüchtigen Zeiger um, der dann dereferenziert wird. Ich habe nicht gesehen, dass sie eine bemerkenswerte Variable verwenden, die wegoptimiert werden könnte.
Arsenal
2

Haftungsausschluss: Ich bin kein Experte für MSP430.

Ich schlage die Verwendung von vor

ADC12_A_disableConversions()

nach

ADC12_A_setupSamplingTimer() 

Ausschnitt aus MSP430 DriverLib for MSP430F5xx_6xx Devices


void ADC12_A_startConversion (uint16_t baseAddress, uint16_t tartingMemoryBufferIndex, uint8_t convertSequenceModeSelect)

Diese Funktion aktiviert / startet den Konvertierungsprozess des ADC. Wenn die während der Initialisierung ausgewählte Sample / Hold-Signalquelle ADC12OSC war, wird die Umwandlung sofort gestartet, andernfalls startet die ausgewählte Sample / Hold-Signalquelle die Umwandlung durch eine ansteigende Flanke des Signals. Beachten Sie bei der Auswahl der Konvertierungsmodi, dass für sequenzierte und / oder wiederholte Modi, um den Sample / Hold-and-Convert-Prozess ohne Trigger von der Sample / Hold-Signalquelle fortzusetzen , die mehreren Samples mit dem ADC12_A_setupSamplingTimer ( ) Funktion. Beachten Sie, dass nach dem Aufrufen dieser Funktion ADC12_A_disableConversions () muss aufgerufen werden, um den ADC neu zu initialisieren, eine Speicherpuffersteuerung neu zu konfigurieren, den Abtastzeitgeber zu aktivieren / deaktivieren oder die interne Referenzspannung zu ändern.

Hinweis: Es gibt auch einige gute kostenlose Online-Kurse zum Erlernen des Designs eingebetteter Systeme. Einer von ihnen verwendet MSP430. Ich habe unten einige davon aufgelistet.


Verweise:

Mahendra Gunawardena
quelle
1

Ich frage mich, warum es im Debug-Modus funktioniert. Es ist eine Weile her, seit ich mit MSP430 gearbeitet habe und ich bin mit der Treiberlib nicht vertraut. Aber:

GPIO_setAsPeripheralModuleFunctionOutputPin(
        GPIO_PORT_P6,
        GPIO_PIN0
        );

Ist das nicht die Funktion, die Sie verwenden möchten, um diesen Pin auf einen analogen Eingang umzuschalten, oder? Ich würde versuchen:

GPIO_setAsPeripheralModuleFunctionIntputPin(
        GPIO_PORT_P6,
        GPIO_PIN0
        );

Wie aus der Beschreibung der Pin-Funktionen hervorgeht (danke @CL.), Wird deutlich, dass das Einstellen des Pins auf die Peripheriefunktion tatsächlich ausreicht und die Richtung ignoriert wird. Es ist also nur irreführend, aber kein Deal Breaker.

Dann gibt es eine kleine Sache, param.endOfSequence = ADC12_A_NOTENDOFSEQUENCE;die wahrscheinlich sein sollte, param.endOfSequence = ADC12_A_ENDOFSEQUENCE;aber da Sie nur eine Einkanal-Konvertierung durchführen, sollte es keine Rolle spielen (es ist nur ein bisschen klarer). Und wahrscheinlich ändern ADC12IFG0und ADC12IE0zu ADC12_A_IFG0und ADC12_A_IE0(aber sie sind nur zu den anderen Werten definiert, also kein funktionales Problem)

Und Sie vermissen ein break;Nach-Ihrem-Fall in der Interrupt-Vektortabelle, aber das hat auch keinen großen Einfluss auf das Programm, sondern nur auf eine Quelle für zukünftige Fehler.

Aus Firmware-Sicht habe ich also nur kleine Nitpicks.

Aufgrund der Kommentare und des Durchlesens des Errata-Blattes frage ich mich, ob eine einzelne __no_operation();nach dem __bic_SR_register_on_exit(LPM0_bits);im ISR das Problem lösen würde. Die Errata erwähnen den hier vorliegenden Fall nicht explizit, aber es gibt Probleme beim Einstellen von Energiesparmodi, beim Verlassen von Energiesparmodi und beim Beschädigen des Programmzählers. Vielleicht ist es ein anderer Fall. Diese Effekte sind beim Debuggen möglicherweise nicht vorhanden, da das Emulationsmodul die normale Ausführung des Kerns beeinträchtigt.

Sie haben aber auch erwähnt, dass Ihr Programm auch funktioniert, wenn Sie die globale Interrupt-Aktivierung löschen. Das lässt mich denken, dass Ihr Programm in einem Interrupt stecken bleibt, aber nicht im ADC. Sie müssen das Interrupt-Flag des ADC nicht löschen, da dies beim Lesen des Speichers des ADC automatisch erfolgt.

Nur ein weiterer Hinweis zur Programmierung: Ich würde die Analyse des ADC-Werts aus dem ISR entnehmen. Halten Sie sie so klein wie möglich, lesen Sie einfach den Wert in Ihre globale Variable und lassen Sie den Energiesparmodus beim Beenden. Machen Sie alle anderen Dinge in Ihrer Hauptanwendung. Es gibt Fälle, in denen Sie eine möglichst kurze Interrupt-Latenz benötigen und wenn Sie Dinge innerhalb des ISR tun, blockieren Sie andere Interrupts (außer Sie aktivieren verschachtelte Interrupts, aber diese sind auch böse).

Arsenal
quelle
Wie die Tabelle auf der nächsten Seite zeigt, reicht es aus, den Pin für die Modulfunktion (P6SEL.0 = 1) zu konfigurieren. Die Richtung spielt keine Rolle.
CL.
Vielen Dank für die Tipps, @Arsenal Ich glaube, ich habe das Symptom eingegrenzt, aber nicht sicher, was die Ursache ist. Es scheint, dass es den ADC nur einmal direkt nach dem Zurücksetzen liest
Aaron
@ Arsenal Ich weiß jetzt, dass das Problem mit Verzögerungen und intrinsischen Funktionen zu tun hat, aber ich weiß nicht genug über intrinsische Funktionen, um es zu beheben
Aaron
@ Aaron Ich habe meine Antwort geändert, vielleicht hilft es ein bisschen, zum Spaß habe ich auch einige allgemeine Richtlinien für ISRs hinzugefügt.
Arsenal
@ Arsenal danke, ich habe es mir angesehen. Ich versuche immer noch neue Dinge und ich habe einen Durchbruch, wenn Sie mein aktualisiertes OP sehen wollen
Aaron