Hallo, ich hoffe mir kann jemand helfen. Ich bin noch nicht sehr fit im programmieren mit C. Ich will auftretende peaks mit einem µController zählen, schaffe es aber nicht ene Variable zu deklarieren die "überall" bekannt ist. Ich sollte es mit Zeigern machen, hab aber keine Ahnung wie das funktioniert. Hat jemand eine Idee? Momentaner Quellcode, welcher eine LED die an P1.0 hängt und toggelt, bei einer fallenden Flanke an P2.0: #include <msp430xG43x.h> void main(void) { WDTCTL = WDTPW + WDTHOLD; // Stop watchdog timer P1DIR = 0x01; // Set P1.0 to output direction P2IE |= 0x01; // P2.0 interrupt enabled P2IES |= 0x01; // P2.0 Hi/lo edge P2IFG &= ~0x01; // P2.0 IFG cleared _BIS_SR(LPM4_bits + GIE); // Enter LPM4 w/interrupt for(; ; ; ) // Endlosschleife, das Programm auf µC nicht { //beendet wird } } // Port 2 interrupt service routine #pragma vector=PORT2_VECTOR __interrupt void Port_2(void) { P1OUT ^= 0x01; // Toggle P1.0 P2IFG &= ~0x01; // P2.0 IFG clearedf } Vielen Dank schon mal.
eins der zauberwörter heißt globale variable ;-) ist unter der "sauberen" programmierung verpöhnt (wie auch goto in c/c++). wird aber oft (besonders bei uc`s) benutzt da man ohne großen aufwand mit ihnen arbeiten kann.
1 | #include <dingsdabums.h> |
2 | |
3 | int i; //globale variable |
4 | |
5 | int main(void) |
6 | {
|
7 | for(;;) |
8 | {
|
9 | while(i<100) //solange i < 100 |
10 | {
|
11 | i++; // i = i + 1; |
12 | }
|
13 | i = 0; // i = 0; |
14 | }
|
15 | return 0; |
16 | }
|
oder aber du definierst dir eine struct und schreibst dir die zugriffsoperationen. ist zwar aufwendig, aber es ist wesentlich sauberer
1 | typedef struct _zahl |
2 | {
|
3 | int i; |
4 | char c; |
5 | }zahl; |
6 | |
7 | zahl* set_i(int i) |
8 | {
|
9 | zahl z; |
10 | z = (zahl*)malloc(sizeof(*z)); |
11 | z->i = i; |
12 | return z; |
13 | }
|
14 | zahl* get_i(void) |
15 | {
|
16 | zahl z; |
17 | return(zahl->i); |
18 | }
|
mfg KoF
Hi Leute Ob Struktur oder Integer, beide sind global definiert. Die Zugriffsroutinen sind meiner Meinung nach übertrieben, aber das kommt halt stark auf den Einsatzzweck an. Viel wichtiger ist, und das sollte sich jeder Codeschreiberling für jede Programmiersprache gut merken, für alle Bezeichner selbsterklärende Namen zu verwenden. Auch Präfixe, die den Datentyp anzeigen sind sehr zu empfehlen. So zum Beispiel: int iHerzImpulsZaehler = 0; // oder float fFrequenz = (float)iHerzImpulsZaehler / MESSZEIT; iHerzImpulsZaehler = 0; Daruch verlieren globale Variablen besonders in kleinen Programmen ihre Bösheit. Genau so wichtig sind viel und gute Kommentare die deine Überlegungen oder grössere Abläufe beschreiben. Zeiger sind nichts anderes als Variablen für Speicheradressen. Weil an diesen Speicheradressen selbst wieder ein Wert steht, der einen bestimmten Datentyp hat, muss der Zeiger auch immer einen Datentyp haben. Zudem gibt es Operatoren, die dir die Speicheradresse einer Variablen zurückliefern (Adresse= &Variable) oder umgekehrt den Wert an einer Speicheradresse zurückliefern (Wert = *Adresse). Mit den Adressen kannst du auch rechnen, aber das ist ein fortgeschrittenes Thema und bedarf ein wenig Experimentierfreude. char sText[] = "Dies ist mein Text"; // String char* pText = &(sText[0]); // Ein Pointer auf das erste Zeichen while (*pText != '\0') { // Solange das Zeichen nicht 0 ist putchar(*(pText++)); // Zeichen ausgeben und zum nächsten Zeichen springen (Postinkrement!) } while (*pText != &(sText[0])) { // und das ganze nochmal rückwärts putchar(*pText--)); } Wobei putchar() deinem MSP natürlich unbekannt ist. HTH Tom PS: Kennt der MSP430 malloc() und free()? Das muss ich ja gleich mal ausprobieren!
Hallo @Harry nebenbei ne Frage: Wie detektierst Du die Herzfrequenz? Welcher Sensor? Brustgurt? MfG Achim
Naja, das Goto ist zur Fehlerbehandlung häufig unersätzlich, so wie longjmp, das auch zum Wechsel in einen früheren Modus (AM, LPM3, ...) zweckmässig ist. Und globale Variablen sind besser als malloc, da die allermeisten Stackcheck-Routinen nicht funktionieren, wenn malloc verwendet wird und man mit mallock leicht Speicherlecks einbauen kann. Wenn man unbedingt eine Kapselung um quasi-globale Variablen haben will, kann man mit Pointern auf Variablen von main arbeiten oder eine Funktion mit einer static-Variablen nehmen, die auch alle Zugriffe darauf macht; quasi eine Klasse sozusagen. Sauberer wäre aber in dem Fall gleich C++ oder Java zu nehmen.
Hi, wow vielen Dank! Das hilft mir ein ganzes Stück weiter. Zur Frage, ich greife das Signal eines handelsüblichen Brustgurts mit einem Schwingkreis ab und filtere das Signal dann mit Bandpässen. Ich habe volgende Seite zur Hilfe genommen: http://rick.mollprojects.com/hrm/
Rolf schrieb: "Naja, das Goto ist zur Fehlerbehandlung häufig unersätzlich..." Warum? Ich habe in allen meinen Programmen Fehlerbehanlungen aber noch nie Goto verwendet!? Kannst du das bitte genauer erläutern? Gruß Tenner
Hallo, ich hab schon wieder ein Problem, denn ich schaffs nicht aus der Interrupt Routine heraus zu kommen. Schreibe ich den Code folgendermaßen funzt es, mach ich aber die if-Abfrage in die main, geht nichts mehr. Im User Guide steht was von RETI (return from an interrupt service routine). Bei mir geht das aber nicht :( . Funktionierender Code: #include <msp430xG43x.h> #include <stdio.h> int zaehler = 0; void main(void) { WDTCTL = WDTPW + WDTHOLD; // Stop watchdog timer P1DIR = 0x01; // Set P1.0 to output direction P2IE |= 0x01; // P2.0 interrupt enabled P2IES |= 0x00; // P2.0 Hi/lo edge P2IFG &= ~0x01; // P2.0 IFG cleared _BIS_SR(LPM4_bits + GIE); // Enter LPM4 w/interrupt while(1) { } } // Port 2 interrupt service routine #pragma vector=PORT2_VECTOR __interrupt void Port_2(void) { zaehler = zaehler + 1; P2IFG &= ~0x01; // P2.0 IFG clearedf if(zaehler >100) { P1OUT = 0x01; } else { P1OUT = 0x00; } } DieIf Anweisung will ich in die main (innerhalb der Endlosschleife) machen, geht aber nicht. Vielen Dank schon mal.
1 | void main(void) |
2 | ...
|
3 | while(1) |
4 | {
|
5 | if(zahl => 100) //wenn zahl gleich oder größer 100 |
6 | P1OUT = 0x01; // P1.0 high |
7 | else if(zahl < 100) // sonst |
8 | P1OUT &=~ 0x01; //P1.0 low |
9 | }
|
10 | ...
|
du solltest es aber bei der interruproutine lassen, da du ja in den LPM4 mode gehst...
Hi, danke. Was ist eigentlich der LMP4 Mode, das hab ich immer noch nicht verstanden. Ich wollte in der Interruptroutine nur den Zähler hochzählen und alles andere wie Berechnungen usw. ausserhalb, z.B. in der main. Auch deine Lösung klappt nicht. Ich glaube das der Rücksprung aus der Interrupt Routine nicht klappt. Dies hab ich m it verschiedenen printf Befehlen kontrolliert. Ich hab im Datenblatt was mit RETI gelesen, es klappt bei mir aber nicht?? Hast du ne Idee?
@Tenner: Bei einer umfangreichen Funktion kann man dutzende Stellen haben, an denen ein Feher auftritt, der zu immer demselben Abbruch führen soll; dafür ist ein goto optimal. Alternativ kann man denselben Code auch dutzende Male mit Copy+Paste reinkopieren und darf dann bei einer Änderung dutzende Male ändern. Wenn man aber nach Code-Zeilen oder Code-Grösse bezahlt wird, braucht man natürlich kein Goto. Letztlich ist eine Goto-Phobie aber Unsinn und Scheinheiligkeit, da der Assembler-Code viele jmps enthält, also Goto in Assembler.
Hi @Harry LMP4 ist ein Standby-Modus des Mirkocontrollers. Was dabei genau läuft und was ausgeschalten ist, erfährtst du im User's Guide oder im Datenblatt. Versuch's doch mal mit diesem Pseudocode: - Zähler als globale Variable deklarieren - main - Alles initialisieren und Timer starten - Wiederhole - Wenn Zähler grösser als 100 - Wenn LED AN - LED AUS - Sonst - LED AN - Zähler auf 0 setzen - Gehe in Standby-Mode (Achtung: Der Timer muss weiterlaufen!) - Interruptvektor - Erwache aus Standby - Zähler inkrementieren Du kannst den Zähler auch in einer For-Schleife im main-Loop inkrementieren, dann musst du den uC nur noch aufwecken mit der Interrupt-Routine und du hast die maximale Standby-Zeit. RETI würde ich mal bei Google oder in den Assembler- und C-Hilfen nachschlagen. @Rolf Was eine Funktion erledigt sollte immer in nur einem Satz beschrieben werden können. Wenn man dann auch noch aussagekräftige Bezeichner verwendet wird der Code sehr gut nachvollziehbar. In solch eher kleinen Funktionen, max. 2 A4-Seiten als Faustregel, sind normalerweise so wenige Fehlerfälle abzufangen, dass sich dies auch gut mit den gängigen Bedingungs-Konstrukten (if-else / switch) abfangen lassen. Im Gegensatz zu den leicht unübersichtlich werdenden GoTo-Konstrukten stellen Bedingungen eine klare Entscheidung dar, will heissen der Programmablauf ist leichter nachvollziehbar. Funktions-Monolithe und GoTo-Geschick können performanter sein, jedoch auf Kosten der Verständlichkeit. FG Tom
also ich habe programmieren bei einem ada-menschen gelernt. der kennt keine fehler im program ;-) wird der code compiliert, so ist er fehlerfrei (abgesehen von logischen fehlern). und von ada habe ich auch strenge typesierung & co gelernt. es ist zwar wesentlich umständlicher so zu programmieren ( ich tippe mal so auf 3-5 mal mehr code) aber wenn er mla läuft/oft bearbeitet wird/von vielen bearbeitet wird, so zahlt sich diese art der programmierung wieder aus. zu goto. mir wurde es so beigebracht... ein sprungbefehl in c/c++ ist unschön, da man ggf nicht nachvollziehen kann, wohin gesprungen wird. programmierer sollten daher im sinne der nachvollziehbarkeit darauf verzichten. was der compiler daraus macht ist in erster linie egal... jeder compiler übersetzt/optimiert anders. mfg KoF
@ Harry, lass mal _BIS_SR(LPM4_bits + GIE); // Enter LPM4 w/interrupt weg. solange du nicht darauf angewisen bist Strom zu sparen und nur experimentierst lass die Low Power Modes aussen vor. Damit kann man sich immer noch beschäftigen, wenn der Code läuft. ansonsten sollte es so funktionieren ... int zaehler = 0; void main(void) { WDTCTL = WDTPW + WDTHOLD; // Stop watchdog timer P1DIR = 0x01; // Set P1.0 to output direction P1DIR &= ~0x01; // Set P2.0 to input direction P1IES |= 0x00; // P2.0 Hi/lo edge P1IFG &= ~0x01; // P2.0 IFG cleared P1IE |= 0x01; // P2.0 interrupt enabled // _BIS_SR(LPM4_bits + GIE); // Enter LPM4 w/interrupt while(1) { if(zaehler >= 100) { P1OUT |= 0x01; } else { P1OUT &= ~0x01; } } } // Port 2 interrupt service routine #pragma vector=PORT2_VECTOR __interrupt void Port_2(void) { zaehler = zaehler + 1; P2IFG &= ~0x01; // P2.0 IFG clearedf } @Rolf eine saubere Lösung ohne GOTO sähe dann zB. so aus ... #define NO_ERROR 0 #define ERROR_1 1 ... int rc; int sub() { while(1) { rc = foo1(); if( rc ) break; rc = foo2(); if( rc ) break; ... break; } if( rc <> NO_ERROR ) { switch(rc){ case ERROR_1: ... break; ... } return rc; } Gruß Tenner
@Tenner er kann ruhig im lpm4 bleiben, wenn er die vergleiche/setzten der pins in der interruptroutine macht (wie oben schonmal erwähnt)
Hallo, der Code war OK, ich hatte eine zu alte Version von IAR drauf. Jetzt funktioniert alles, mit oder ohne lmp4. Ohne muss aber GIE gesetzt sein. Danke nochmals.
Hiho, ist vielleicht ein bissl spaet, aber ich muss mich zu dem thema nochmal aeussern. Das eigentliche Problem, warum: include <msp430xG43x.h> #include <stdio.h> int zaehler = 0; void main(void) { WDTCTL = WDTPW + WDTHOLD; // Stop watchdog timer P1DIR = 0x01; // Set P1.0 to output direction P2IE |= 0x01; // P2.0 interrupt enabled P2IES |= 0x00; // P2.0 Hi/lo edge P2IFG &= ~0x01; // P2.0 IFG cleared _BIS_SR(LPM4_bits + GIE); // Enter LPM4 w/interrupt while(1) { } } // Port 2 interrupt service routine #pragma vector=PORT2_VECTOR __interrupt void Port_2(void) { zaehler = zaehler + 1; P2IFG &= ~0x01; // P2.0 IFG clearedf if(zaehler >100) { P1OUT = 0x01; } else { P1OUT = 0x00; } } nicht funktioniert, ist das Fehlen des Zauberwortes volatile. Sofern Du beim Anlegen der Variable: volatile int zaehler = 0; verwendest, wird das Programm funktionieren (ohne dass ich das ausprobiere). Bei einigen Compilerversionen wird es ohne volatile funktionieren, bei anderen nicht, insbesondere bei verschiedenen Optimierungsstufen wird der Compiler einmal die Variable ins Register laden und dann (staendig) das Register mit dem Wert vergleichen, egal, ob die Variable im interrupt geaendert wird oder nicht. Volatile zwingt den Compiler, bei jedem Vergleich die Variable neu einzulesen und dann zu vergleiche. Frohes Fest Zimmi
Auch wenn das ein alter Beitrag ist: fuer den Fall, dass ihn jemand ueber Google findet moechte ich noch etwas hinzufuegen: Wird beim MSP ein low power mode (LPM) aktiviert, so geschieht das ueber das Setzen von entsprechenden Bits im Statusregister. Wird eine Interrupt-routine aufgerufen, so wird zwar der LPM deaktiviert, da aber beim Aufruf der Interruptroutine das Statusregister auf dem Stack landet und beim Beenden wieder geladen wird, sind auch die LPM-Bits wieder gesetzt. Das bedeutet, der Prozessor versinkt unmittelbar nach der ISR wieder im Tiefschlaf. Will man, dass der code in Main nach Auftreten des Interrupts weiter ausgefuehrt wird, der Prozessor also dauerhaft aufwacht, so muss man das abgespeicherte Statusregister auf dem Stack manipulieren, so dass die LPM-Bits nicht mehr gesetzt sind. Das ist leider nicht ganz trivial und mit nacktem C nicht zu machen. Die Beispiele von TI benutzen alle Assembler-Anweisungen wie etwa BIC #CPUOFF,0(SP) RETI Was bedeutet, dass in dem word, auf das der Stackpointer zeigt, das CPUOFF bit geloescht wird. Dummerweise generieren Compiler in der Regel einen Prolog/Epilog und einen Stackframe, was einerseits den Stack um einen dem C Code unbekannten Betrag vollstopft, andererseits unmittelbar vor der Rueckkehr aus der ISR den Code unterbringt, um das wieder zu korrigieren. Man hat also als Programmierer in C keine Moeglichkeit, direkt vor dem Ruecksprung die Korrektur vorzunehmen. Beim MSPGCC gibt es fuer diesen Zweck ein paar Macros, die Assemblercode in die ISR einbetten, der auf den vom Compiler generierten Assemblercode angepasst ist und sich dort die informationen des generierten Stackframes besorgt. Das sieht dann so aus: #define _BIC_SR_IRQ(x) \ _asm__ __volatile_ ( \ "bic %0, .L__FrameOffset_" _FUNCTION_ "(r1)" \ : : "ir" ((uint16_t) x) \ ) Das Macro kann dann an jeder Stelle in der ISR verwendet werden und den Prozessor auch nur unter bestimmten Bedingungen aufwecken. p.s.: natuerlich gibt es fuer den MSP auch malloc (schliesslich ist das nichts Hardwareabhaengiges sodnern einfach eine Libraryfunktion, die sich auch jeder selber schreiben kann). Und malloc an sich ist nicht gefaehrlicher als ein statisch reservierter Speicher. Eher weniger, da der statische immer an derselben Stelle liegt und daher fuer Exploits besser auszunutzen ist. Es ist bei malloc nur schwieriger, die Bugs zu bemerken (die der Programmierer ohnehin nicht machen sollte), da bei statisch reserviertem Speicher bei Bereichsueberschreitungen in der Regel andere globale Variablen ueberschrieben werden und man sofort merkt, dass es irgendwie nicht laeuft. Wer also behauptet, malloc sei unsicher, der hat offensichtlich keine wirkliche Ahnung, wovon er redet. Jegliche Verwendung von Pointern (oder Typecasts, was das angeht) ist gleichermassen 'unsicher', das schliesst auch statische Arrays und Puffer ein. Und was das Goto angeht, so geht es durchaus auch ohne, wenn man seine Funktionen entsprechend anlegt. In der Regel sollte eine FUnktion bei einem Fehler mit einem entsprechenden Rueckgabewert zur Aufrufenden zurueckkehren, anstatt die Fehlermeldungen selber vorzunehmen. Dann braucht man goto nicht mal ansatzweise. Aber da sich die meisten darueber keine Gedanken machen wollten, wurden in C++ die Exceptions eingefuehrt. Quasi ein Goto mit Parameter.
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
Mit Google-Account einloggen
Noch kein Account? Hier anmelden.