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,¶m);
//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;
}
}
Antworten:
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.
quelle
volatile
Qualifikation war dort im ersten Code, also obwohl oft ein Problem, in diesem Fall eher nichtHaftungsausschluss: Ich bin kein Experte für MSP430.
Ich schlage die Verwendung von vor
nach
Ausschnitt aus
MSP430 DriverLib for MSP430F5xx_6xx Devices
void ADC12_A_startConversion (uint16_t baseAddress, uint16_t tartingMemoryBufferIndex, uint8_t convertSequenceModeSelect)
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:
quelle
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:
Ist das nicht die Funktion, die Sie verwenden möchten, um diesen Pin auf einen analogen Eingang umzuschalten, oder? Ich würde versuchen:
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 ändernADC12IFG0
undADC12IE0
zuADC12_A_IFG0
undADC12_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).
quelle