Ich versuche, die Werte vom ADC auf meinem MSP430F5529 abzurufen und sie ü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 gelesene Wert über der Hälfte von Vcc liegt, leuchtet eine LED auf.
Ich habe Pin 6.0 an einen Kraftsensor angeschlossen, damit ich sehen kann, wie er sich ein- und ausschaltet, wenn ich meinen Finger darauf lege oder loslasse.
Das Programm funktioniert perfekt, wenn ich es im Debug-Modus ausführe, aber wenn ich versuche, es außerhalb des Debug-Modus auszuführen (nur das Board 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, wenn ich Reset gedrückt halte, während ich meinen Finger auf den Kraftsensor lege (wenn ich meinen Finger auflege, schaltet sich die LED ein) und die Reset-Taste loslassen, bleibt die LED an, bis ich mit meinem Finger wieder auf Reset drücke. Es scheint also, dass das Zurücksetzen ein Problem verursacht, aber ich bin mir nicht sicher, wie.
Zuerst dachte ich, Reset würde konstant hoch gezogen (oder niedrig, was auch immer das Gerät zurücksetzt), aber das kann nicht wahr sein, denn dann sollte das Programm funktionieren, wenn ich Reset gedrückt halte, aber das tut 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 zu erstellen, ohne die Peripherietreiberbibliothek zu verwenden, und es scheint perfekt außerhalb des Debuggers zu funktionieren. Dies lässt mich glauben, dass etwas mit der Peripheral Driver Library von Texas Instruments nicht stimmt.
Hier ist der Code, der außerhalb des Debuggers gut 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;
}
}
Manchmal liegt der Grund für ein solches Verhalten darin, dass die Optimierungseinstellungen im Debug-Modus anders sind und eine Variable, die der Compiler für unnötig hält, dann gleich optimiert wird.
Die Korrekturen dafür bestehen darin, "flüchtige" Qualifizierer zu solchen Variablen hinzuzufügen oder die Optimierung auszuschalten (oder zumindest abzusenken).
Ich weiß nicht, ob das Ihre Antwort ist (der Thread wurde TL;DR), aber dieser Leckerbissen sollte sicherlich als mögliche Lösung für die Suchmaschinen erscheinen.
volatile
Qualifier war im ersten Code dabei, also auch wenn oft ein Thema, in diesem Fall eher nichtHaftungsausschluss: Ich bin kein Experte für MSP430.
Ich empfehle die Verwendung von
ADC12_A_disableConversions()
nach
ADC12_A_setupSamplingTimer()
Ausschnitt ausMSP430 DriverLib for MSP430F5xx_6xx Devices
Diese Funktion aktiviert/startet den Konvertierungsprozess des ADC. Wenn die bei der Initialisierung gewählte Sample/Hold-Signalquelle ADC12OSC war, wird die Wandlung sofort gestartet, andernfalls startet die gewählte Sample/Hold-Signalquelle die Wandlung durch eine steigende Flanke des Signals. Denken Sie bei der Auswahl von Konvertierungsmodi daran, dass für sequenzierte und/oder wiederholte Modi mehrere Samples mit dem ADC12_A_setupSamplingTimer( ) Funktion. Beachten Sie, dass nach dem Aufruf dieser Funktion die 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 einige davon unten aufgeführt.
Verweise:
Ich frage mich, warum es im Debug-Modus funktioniert, es ist schon eine Weile her, seit ich mit MSP430 gearbeitet habe, und ich bin mit der Treiberbibliothek nicht vertraut. Aber:
GPIO_setAsPeripheralModuleFunctionOutputPin(
GPIO_PORT_P6,
GPIO_PIN0
);
Sicherlich ist die Funktion, mit der Sie diesen Pin auf einen analogen Eingang umschalten möchten, nicht oder doch? Ich würde versuchen:
GPIO_setAsPeripheralModuleFunctionIntputPin(
GPIO_PORT_P6,
GPIO_PIN0
);
Aber wie in der Beschreibung der Pin-Funktionen (Danke @CL.) zu sehen ist, wird klar, dass das Setzen 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 einzelne Kanalkonvertierung durchführen, sollte es keine Rolle spielen (es ist nur ein bisschen klarer). Und wahrscheinlich ändern ADC12IFG0
und ADC12IE0
zu ADC12_A_IFG0
und ADC12_A_IE0
(aber sie sind nur für die anderen Werte definiert, also kein funktionales Problem)
Und Ihnen fehlt break;
in der Interrupt-Vektortabelle ein nach Ihrem Fall, aber das wird das Programm auch nicht sehr beeinflussen, sondern nur eine Quelle für zukünftige Fehler.
Aus Firmware-Sicht habe ich also nur kleinere Nitpicks.
Basierend auf den Kommentaren und dem Durchlesen des Errata-Blatts frage ich mich, ob eine Single __no_operation();
nach dem __bic_SR_register_on_exit(LPM0_bits);
in der ISR das Problem lösen würde. Die Errata erwähnt den hier vorliegenden Fall nicht explizit, aber es gibt Probleme beim Einstellen von Energiesparmodi, Verlassen von Energiesparmodi und Beschädigen des Programmzählers. Vielleicht ist es also ein anderer Fall. Diese Effekte sind während des Debuggens möglicherweise nicht vorhanden, da das Emulationsmodul die normale Ausführung des Kerns stört.
Aber Sie haben auch erwähnt, dass Ihr Programm auch funktioniert, wenn Sie die globale Interrupt-Freigabe löschen. Was mich zu der Annahme bringt, dass Ihr Programm in einem Interrupt hängen bleibt, aber nicht der ADC. Sie müssen das Interrupt-Flag des ADC nicht löschen, da dies automatisch beim Lesen des Speichers des ADC erfolgt.
Nur noch ein Hinweis zur Programmierung, ich würde die Analyse des ADC-Wertes aus dem ISR herausholen. Halten Sie sie so klein wie möglich, lesen Sie einfach den Wert in Ihre globale Variable ein und verlassen Sie den Energiesparmodus beim Beenden. Erledigen 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 der ISR tun, blockieren Sie andere Interrupts (außer Sie aktivieren verschachtelte Interrupts, aber diese sind ebenfalls böse).
DigitalNinja
Asmyldof
Aaron
Aaron
Asmyldof
DigitalNinja
DigitalNinja
WDTCTL = WDTPW | WDTHOLD; // Stop watchdog timer
, um den Watchdog zu stoppen.Mahendra Gunawardena
Passant
Passant
Mahendra Gunawardena
Aaron
CL.
ADC12_A_clearInterrupt()
undADC12_A_enableInterrupt()
verwenden Sie nicht die Symbole, die Sie laut Dokumentation verwenden sollten, aber das spielt eigentlich keine Rolle.CL.
Aaron
Aaron
Aaron
Aaron
ADC12_A_getResults
Aufruf einschalte. Ich schalte sie ein, egal was passiert (was bedeutet, dass sie nicht Teil der if-Anweisung ist). und es schaltet sich gut ein.Ich weiß also, dass es zumindest die ISR auslöstAaron
DigitalNinja
__bis_SR_register(LPM0_bits + GIE);
tut. Müssen Sie den Interrupt jedoch nicht löschen, bevor Sie eine weitere Konvertierung starten? Rufen Sie wie in auf,ADC12_A_clearInterrupt(ADC12_A_BASE,ADC12IFG0);
nachdem die Konvertierung abgeschlossen ist. Vielleicht liest es den ADC deshalb nur einmal direkt nach dem Zurücksetzen.Aaron
__bis_SR_register(LPM0_bits + GIE)
genau, was es tut, da es bereits im Beispielcode enthalten war, aber ich glaube, es setzt die MCU in den Energiesparmodus und ermöglicht Interrupts, sie aus dem Energiesparmodus zu nehmen. Interessanterweise gelangt der Code außerhalb des Debug-Modus in die ISR. Ich teste dies, indem ich bei jedem Eintritt in die ISR eine LED umschalte und einige Verzögerungszyklen einsetze, damit ich sie tatsächlich blinken sehen kann. Dadurch funktioniert das gesamte Programm korrekt!Aaron
Aaron
+ GIE
das Argument des Anrufs angeben,__bic_SR_register_on_exit
der direkt vor dem Beenden der ISR aufgerufen wird! Ich weiß nur nicht, was diese Funktionen tunDigitalNinja
GIE
globale Interrupts aktiviert sind. Es scheint also, als würden Interrupts ohne das+ GIE
in dieser Zeile (__bic_SR_register_on_exit
) deaktiviert. Das Hinzufügen aktiviert Interrupts und daher erhalten Sie den nächsten ADC-Interrupt, wenn Sie wieder in die Hauptschleife eintreten.Aaron
+ GIE
das Programm auch ohne funktionierte, solange ich eine Verzögerung in die ISR einfügteDigitalNinja
+ GIE
nicht in dieser Zeile in der ISR steht, es in der Hauptschleife (__bis_SR_register(LPM0_bits + GIE)
) aufgerufen und globale Interrupts wieder aktiviert würde, wenn sie deaktiviert worden wären. Also, es ist wirklich nur die Verzögerung, die es behoben hat, wie Sie gerade gesagt haben. Wo haben Sie die Verzögerung im ISR eingetragen und wie lang ist die Verzögerung?DigitalNinja
Aaron
__bis_SR_register(LPM0_bits + GIE)
Leitung anrief, nachdem die ISR zurückgekehrt war und somit den GIE eingestellt hatte. Warum sollte die Verzögerung dazu führen, dass main sie erneut aktiviert, aber keine Verzögerung verursacht main, um es nicht wieder zu aktivieren. Die Verzögerung befand sich direkt vor derif(ADCResults >= 0x7ff)
Anweisung im ISRAaron
CL.
ADC12IV
Registers gelöscht.Aaron
DigitalNinja
__bis_SR_register(LPM0_bits + GIE)
globale Interrupts in Ihrer Hauptschleife ermöglicht, unabhängig von der Verzögerung oder der anderen Zeile in der ISR. Ich weiß nicht, warum eine Verzögerung es behebt. Ich denke nicht, dass du es brauchen solltest. Es passiert also etwas anderes, vielleicht mit der ADC-Konfiguration. Haben Sie in den TI-Foren nach Hilfe gesucht oder Ihren Code so geändert, dass er genau einem Beispiel entspricht?CL.
DigitalNinja
ADC12_A_CYCLEHOLD_64_CYCLES
inADC12_A_setupSamplingTimer
zu ändern,ADC12_A_CYCLEHOLD_4_CYCLES
und nehmen Sie Ihren Verzögerungscode heraus.Aaron
Aaron
+ GIE
den ISR-Aufruf von hinzufüge__bic_SR_register_on_exit
. Ich weiß also, dass die zusätzlichen Bibliotheken das Problem nicht verursachen, ich kann nur nicht herausfinden, warum die Verzögerung den gleichen Effekt hat wie das Hinzufügen+GIE
Aaron
CL.
DigitalNinja
Aaron
Passant
__bis_SR_register(LPM0_bits + GIE)
setzt die Low-Power-Mode-0- und Global-Interrupt-Enable-Bits in das Spezialregister, das den Schlafmodus und globale Interrupts steuert. Die Verzögerung nach dem Einstellen des Interrupts und des Ruhezustands soll verhindern, dass Probleme im Ruhezustand stecken bleiben.