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.