• 28.04.2024, 06:18
  • Registrieren
  • Anmelden
  • Sie sind nicht angemeldet.

 

Lieber Besucher, herzlich willkommen bei: Aqua Computer Forum. Falls dies Ihr erster Besuch auf dieser Seite ist, lesen Sie sich bitte die Hilfe durch. Dort wird Ihnen die Bedienung dieser Seite näher erläutert. Darüber hinaus sollten Sie sich registrieren, um alle Funktionen dieser Seite nutzen zu können. Benutzen Sie das Registrierungsformular, um sich zu registrieren oder informieren Sie sich ausführlich über den Registrierungsvorgang. Falls Sie sich bereits zu einem früheren Zeitpunkt registriert haben, können Sie sich hier anmelden.

Problem mit C und switch-case (avr + gcc)

Samstag, 27. Juni 2009, 14:42

Servus zusammen!

Ich habe leider ein "kleines" Problem mit einer switch-case Anweisung.

Quellcode

1
2
3
4
5
6
7
8
9
10
11
12
13
int mode = 1;

switch ( mode )
{
case 1:
   mode_1();
case 2:
   mode_2();
case 3:
   mode_3();
default:
   mode_0();
}


Der springt mir hierbei jedesmal in den "default" rein...?
Habs auch schon so versucht:

Quellcode

1
2
3
...
case '2':
...


Was mache ich denn hier bitte falsch?

Dieser Beitrag wurde bereits 1 mal editiert, zuletzt von »Freakmaster« (27. Juni 2009, 14:44)

Da steht nix :-P

Samstag, 27. Juni 2009, 14:46

Muss da nicht noch überall ein "break;" rein?
Ich weiß schon, warum ich lieber viele if-Statements schreibe, anstatt switch zu benutzen.

Samstag, 27. Juni 2009, 15:05

das muß überall ein break rein, es werden sonst alle statements vom einsprunggpunkt bis zum ende abgearbeitet.

Samstag, 27. Juni 2009, 15:55

gnarf -.-
Man sollte doch mal zwischendrin ne Pause machen...

danke!

Jetzt hab ich allerdings noch ein weiteres Problem:

Ich habe einen 3-fach DIP-Schalter am µC der jeweils einen PIN gegen GND schaltet wenn er eingeschalten ist. Pull-ups sind aktiviert.
Damit möchte ich 6 verschiedene Intervalle einstellen können.

Ich frage so den Zustand der einzelnen Pins ab:

Zitat

if (!(PIND&(1<<PD0)))
{
pin1 = 1;
}

if (!(PIND&(1<<PD1)))
{
pin2 = 2;
}

if (!(PIND&(1<<PD2)))
{
pin3 = 4;
}

char mode = (pin1 + pin2 + pin3);


Und hier dann die switch-case Konstruktion:

Zitat

switch ( mode )
{
case 0:
dauerleuchten(); //funktioniert
break;
case 1:
eineinhalb_hertz();//funktioniert nicht - läuft mit 3 hertz
break;
case 2:
drei_hertz(); //funktioniert nicht - läuft mit 5 hertz
break;
case 3:
fuenf_hertz(); //funktioniert nicht - läuft mit 20 hertz
break;
case 4:
fuenf_hertz_blink(); //funktioniert nicht - führt 1 aus
break;
case 5:
zwanzig_hertz(); //kann ich grad nicht testen
break;
case 6:
vierzig_hertz(); //funktioniert nicht - führt 1 aus
break;
default:
PORTD &= ~(1<< PD6);
break;
}



Scheinbar wird irgendwie falsch zusammengezählt oder?
nur: wie soll man die variablen sonst addieren???
achja: hier noch eine beispielfunktion - es ist jeweils nur die zeit vom _delay_ms() geändert:

Zitat

void eineinhalb_hertz () //Blinken bei 1,5 Hz
{
PORTD ^= (1<< PD6);
_delay_ms(666);
}

Dieser Beitrag wurde bereits 3 mal editiert, zuletzt von »Freakmaster« (27. Juni 2009, 16:00)

Da steht nix :-P

Samstag, 27. Juni 2009, 21:58

das ist niccht gerade übersichtlich was du da zusammprogrammierst.
ich würde das so machen:

Quellcode

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
#define DIP1 	0x01
#define DIP2    0x02
#define DIP3    0x04

#define DIP1IN	!(PIND&DIP1)
#define DIP2IN	!(PIND&DIP2)
#define DIP3IN	!(PIND&DIP3)

#define MODE1 (DIP1&DIP2)
#define MODE2 (DIP1)
#define MODE3 (DIP3&DIP1)

uchar temp=0;

if(DIP1IN)
   temp |= DIP1;
if(DIP2IN)
    temp |= DIP2;
if(DIP3IN)
    temp |= DIP3;

switch(temp){
   case MODE1:
   break;

}

Samstag, 27. Juni 2009, 23:35

das ist niccht gerade übersichtlich was du da zusammprogrammierst.


Ich lerns ja auch gerade erst ;)

Danke für den Tipp! Ich werd mal versuchen etwas aufzuräumen :)
Da steht nix :-P

Sonntag, 28. Juni 2009, 07:26

ich lerns ja auch gerade erst ;)
Das war auch nicht böse gemeint, aber versuche immer so zu Programmieren das Code für sich sebst spricht. Also Kommentare mehr oder weniger bei den einfachen sachen überflüssig sind.
Dann ist der code auch in 1/2 Jahr wenn du mal was ändern mußst vernünftig lesbar und du weisst sofort ohne suchen was du da machst, das geht gerade um Variablennamen und IO definitionen.
IO Definitionen kapsle ich immer als define aus. so kann man auch mal den code auf eine andere HW portieren und muß nur seine Defines ändern und nicht den ganzen code durchsuchen.

Meine Variante ist spart dir zb auch gleich mal 3 Variablen ein und ist kompliert kleiner und schneller.

Quellcode

1
char mode = (pin1 + pin2 + pin3);

wird bei mir alles mit temp geamcht.

Und wichtig, temp jedes mal auf 0 neu initialsieren. Das müsstest du auch mit pin1 + pin2 + pin3 machen.

EDIT:

mal ein Beispiel:

Quellcode

1
2
3
4
5
void eineinhalb_hertz ()//Blinken bei 1,5 Hz
 {
 PORTD ^= (1<< PD6);
 _delay_ms(666);
 }


ich mach das so:

Quellcode

1
2
3
4
5
6
7
8
9
10
//LED
#define LED_PORT PORTD
#define LED 6
#define ledToggle() LED_PORT ^= (1<<LED)

void eineinhalb_hertz ()//Blinken bei 1,5 Hz
 {
ledToggle();
_delay_ms(666);
 }


ABER du hast noch einen Fehler drin: siehe AVR-LIBC

Zitat


Function Documentation

void _delay_ms ( double __ms )

Perform a delay of __ms milliseconds, using _delay_loop_2().
The macro F_CPU is supposed to be defined to a constant defining the CPU clock frequency (in Hertz).
The maximal possible delay is 262.14 ms / F_CPU in MHz.


du msst also _delay_ms kapseln,

Quellcode

1
2
3
4
5
6
void delayms(uint time)
{
while(time){
time--;
_delay_ms(1);
}


aber um ein blinken zu erzeugen musst du nicht mit der Delay funktion rechenzeit verbrennen.
Das geht auch deutlich besser wenn du dir aus einem Timer eine Zeitbasis holst und diese nimmst. Mit _delay_xx blockiert dir deine komplette Anwendung (bis auf die IRQs).

Sonntag, 28. Juni 2009, 13:09

ich lerns ja auch gerade erst ;)
Das war auch nicht böse gemeint,...


Das dachte ich mir schon - deswegen auch der Smiley :)
Für Verbesserungsvorschläge bin ich selbstverständlich immer offen!

Ich habe leider derzeit noch ein gewisses Problem mit der Syntax von Variablen :S
Java, shell, Bash, ksh, csh und tsh - da blickt man irgendwann nicht mehr durch wenn man zwar alles aber viel zu selten benutzt :thumbdown:

Sollte man eigentlich so viel wie möglich zwegs Performance in Funktionen auslagern oder sind die eher für die Sauberkeit des Codes von Wert weil der Compiler die Funktionen an den Stellen wo sie aufgerufen werden komplett "ausschreibt"?

Na dann werde ich mich mal mit Timern spielen...
Da steht nix :-P

Sonntag, 28. Juni 2009, 13:16

Also defines wirken sich nicht auf die Performance aus. Beim Kompilieren werden die defines sozusagen ersetzt.
Echte Funktionen dagegen können soweit ich weiß schon Performance kosten. Allerdings ist das denk ich bei dir im Moment sowas von vernachlässigbar. Versuch doch erstmal mit nem Timer zu arbeiten, anstelle von den sleeps.

Sonntag, 28. Juni 2009, 13:31

Also defines wirken sich nicht auf die Performance aus. Beim Kompilieren werden die defines sozusagen ersetzt.
Echte Funktionen dagegen können soweit ich weiß schon Performance kosten. Allerdings ist das denk ich bei dir im Moment sowas von vernachlässigbar. Versuch doch erstmal mit nem Timer zu arbeiten, anstelle von den sleeps.


Sprich die defines sind nur für die Lesbarkeit da.
Dass die Performance bei dieser Anwendung noch keine Rolle spielt ist mir schon klar aber wenn ich schon lerne, dann doch gleich richtig :)

Wie gesagt den Timer werde ich mir mal zu gemüte führen.
Da steht nix :-P

Sonntag, 28. Juni 2009, 14:10

Das ist jetzt mal explizit auf den AVR und teileweise auch andere µController bezogen.

Defines bingen 1. einen Vorteil weil man sie besser lesen kann und sie zentral in *.h files verwaltet werden können, somit hat man halbwegs portablen code.

so etwas wie das hier: temp |= DIP1; in eine funktion auszulagen macht keien sinn, da es im assembler code nur eine zeile ist.
aber ein _delay_ms() zu kapseln macht sinn, da der compiler das inline immer wieder neu einsetzt.
Ich habe mit angewöhnt funktionen/berechnungen die mehr als 2x gebraucht werden in eine funktion zu packen.
Defines können mitunter einen recht guten Geschwindigkeitsvorteil bieten.
Zb würde ich die die Dip Schalter abfrage kapseln.

Quellcode

1
uchar getDipSwitch(void);

Somit bist du dann in der Implementation der Funktion unabhängig und mußt nur noch an der Funktion selbst rumoptimieren oder etwas ändern.
Genau so handhabe ich es mit den Hardware Routinen. Die Initialsierung wird in void init(void) ausgelagert.
Nix ist stressiger als ne aufgeblasene Main.c mit ein paar 1000 zeilen wo man kaum noch die eigentliche funktion erkennen kann.

Montag, 29. Juni 2009, 08:19

das ist niccht gerade übersichtlich was du da zusammprogrammierst.
ich würde das so machen:

Quellcode

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
#define DIP1 	0x01
#define DIP2    0x02
#define DIP3    0x04

#define DIP1IN	!(PIND&DIP1)
#define DIP2IN	!(PIND&DIP2)
#define DIP3IN	!(PIND&DIP3)

#define MODE1 (DIP1&DIP2)
#define MODE2 (DIP1)
#define MODE3 (DIP3&DIP1)

uchar temp=0;

if(DIP1IN)
   temp |= DIP1;
if(DIP2IN)
    temp |= DIP2;
if(DIP3IN)
    temp |= DIP3;

switch(temp){
   case MODE1:
   break;

}


ich würde das übrigens noch geringfügig verändern:

btw.: mir fiel das gerade auf: Du hast da sogar nen gravierenden Fehler drin ;)

Quellcode

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
#define DIP1 	0x01
#define DIP2    0x02
#define DIP3    0x04

#define DIP1IN	(!PIND)&DIP1
#define DIP2IN	(!PIND)&DIP2
#define DIP3IN	(!PIND)&DIP3

#define MODE1 (DIP1&DIP2)
#define MODE2 (DIP1)
#define MODE3 (DIP3&DIP1)

uchar temp = DIP1IN | DIP2IN | DIP3IN;


switch(temp){
   case MODE1:
   break;

}


Spart 3 if statements und sowas erfreut den Prozessor ;)

und ist sogar noch zu kürzen auf:

Quellcode

1
temp = (!PIND)&(DIP1|DIP2|DIP3);


gerade bei Microcontroller sachen ist es mE zielführend gleich sprungoptimiert zu programmieren (also möglichst wenig IF- Abfragen einzubauen).

Dieser Beitrag wurde bereits 2 mal editiert, zuletzt von »Clark« (29. Juni 2009, 08:29)

c++: The power, elegance and simplicity of a hand grenade.

Montag, 29. Juni 2009, 08:35

Sollte man bei defines nicht sogar immer ne Klammer um den Gesamtausdruck machen?

Quellcode

1
2
3
4
5
6
7
#define DIP1  (0x01)
#define DIP2 (0x02)
#define DIP3 (0x04)

#define DIP1IN ((!PIND)&DIP1)
#define DIP2IN ((!PIND)&DIP2)
#define DIP3IN ((!PIND)&DIP3)

E: Ahh, hab gerade nochmal nachgelesen. Das macht eigentlich nur bei Makros mit Parametern Sinn.

Dieser Beitrag wurde bereits 1 mal editiert, zuletzt von »hurra« (29. Juni 2009, 09:04)

Montag, 29. Juni 2009, 09:38

Sollte man bei defines nicht sogar immer ne Klammer um den Gesamtausdruck machen?

Quellcode

1
2
3
4
5
6
7
#define DIP1  (0x01)
#define DIP2 (0x02)
#define DIP3 (0x04)

#define DIP1IN ((!PIND)&DIP1)
#define DIP2IN ((!PIND)&DIP2)
#define DIP3IN ((!PIND)&DIP3)

E: Ahh, hab gerade nochmal nachgelesen. Das macht eigentlich nur bei Makros mit Parametern Sinn.


Naja, es macht sinn, sobald es nicht nur eine einzelne Variable ist.

also auch

Quellcode

1
#define DIP1IN ((!PIND)&DIP1)

sollte so geklammert sein.
reichen tut aber auch:

Quellcode

1
#define DIP1IN (!PIND&DIP1)

die obere Schreibweise hat den Vorteil, dass sie jeder sofort versteht.

Wenn man nicht klammert kann das Probleme geben:

Quellcode

1
2
3
#define DIP1IN !PIND&DIP1

int x = 5+ DIP1IN * 7;


was kommt raus?
erwarten sollte man ja 5 + WertvomPin mal sieben.
Aber was kommt raus?

Lösen wirs mal auf:

Quellcode

1
5+!PIND&DIP1*7

und leider sind die Präferenzen wie folgt (hier jetzt absichtlich geklammert):

Quellcode

1
(5+(!PIND))&(DIP1*7)


Uups, das ist ja was ganz anderes...
c++: The power, elegance and simplicity of a hand grenade.

Montag, 29. Juni 2009, 10:23

So! Hier nochmal überarbeitet - ich hasse es, wenn ich code einfach so tippen muss und ihn nicht ausprobieren kann ;D



Ich habs jetzt mal die PINs und die Werte der DIPs aufgetrennt um das ganze etwas übersichtlicher und portabel vom experimentierboard auf die fertige Platine zu machen:
(gibt es bei C eigentlich eine Konvention, dass man Variablen komplett groß, klein oder sonstirgendwie schreibt?)

Zitat


#define port PORTD
#define pin PIND

#define pin_dip1 0x01 //PD0
#define pin_dip2 0x02 //PD1
#define pin_dip3 0x04 //PD2
#define pin_active 0x08 //PD3
#define pin_led 0x20 //PD5

#define dip1val 1 //wert von dip1 (1)
#define dip2val 2 //wert von dip2 (2)
#define dip3val 4 //wert von dip3 (4)

#define dip1in (!(pin)&pin_dip1)
#define dip2in (!(pin)&pin_dip2)
#define dip3in (!(pin)&pin_dip3)

#define active (!(pin)&pin_active)

#define led_toggle() port ^= (1<<pin_led);
#define led_off() port &= ~(1<<pin_led);
#define led_on() port |= (1<<pin_led);


Die Abfrage habe ich vom Sebastian übernommen:

Zitat


if(dip1in)
temp |= dip1val;
if(dip2in)
temp |= dip2val;
if(dip3in)
temp |= dip3val;


Der switch/case schaut dann so aus:

Zitat

switch(temp){
case 0:
break;
case 1:
break;
...
}



Aktuell les ich mich grad in Timer und interrupts ein um die _delay_ms(); rauswerfen zu können...

Dieser Beitrag wurde bereits 8 mal editiert, zuletzt von »Freakmaster« (29. Juni 2009, 10:50)

Da steht nix :-P

Montag, 29. Juni 2009, 10:58

So! Hier nochmal überarbeitet - ich hasse es, wenn ich code einfach so tippen muss und ihn nicht ausprobieren kann ;D



Ich habs jetzt mal die PINs und die Werte der DIPs aufgetrennt um das ganze etwas übersichtlicher und portabel vom experimentierboard auf die fertige Platine zu machen:
(gibt es bei C eigentlich eine Konvention, dass man Variablen komplett groß, klein oder sonstirgendwie schreibt?)


es gibt so ca. hunderte von Konventionen.

Normalerweise werden defines gross geschrieben:
#define PORT PORTD
(was eigentlich blödsinn ist: du definierst eine Konstante nach einer Konstante...)

Zitat



Zitat


#define port PORTD
#define pin PIND

#define pin_dip1 0x01 //PD0
#define pin_dip2 0x02 //PD1
#define pin_dip3 0x04 //PD2
#define pin_active 0x08 //PD3
#define pin_led 0x20 //PD5

#define dip1val 1 //wert von dip1 (1)
#define dip2val 2 //wert von dip2 (2)
#define dip3val 4 //wert von dip3 (4)

#define dip1in (!(pin)&pin_dip1)
#define dip2in (!(pin)&pin_dip2)
#define dip3in (!(pin)&pin_dip3)

#define active (!(pin)&pin_active)

sicher, dass active low aktiv ist?

Zitat

Zitat


#define led_toggle() port ^= (1<<pin_led);
#define led_off() port &= ~(1<<pin_led);
#define led_on() port |= (1<<pin_led);


Die Abfrage habe ich vom Sebastian übernommen:

Zitat


if(dip1in)
temp |= dip1val;
if(dip2in)
temp |= dip2val;
if(dip3in)
temp |= dip3val;


Ich würde dir die Variante empfehlen, die ich oben geschrieben habe, sie ist a) schneller b) für den Prozessor "angenehmer" ;)

Quellcode

1
temp = (!PIND)&(DIP1|DIP2|DIP3);

das reduziert die gesamte rechenlast an der Stelle auf ein einziges nicht und eine binäre und verknüpfung. (Den Rest drüfte der Compiler dort optimieren).
(oder gleich manuell: temp = !PIND&0x7; //hat den Nachteil, dass es sich nicht mehr so leicht anpassen lässt...)

Zitat


Der switch/case schaut dann so aus:

Zitat

switch(temp){
case 0:
break;
case 1:
break;
...
}



Aktuell les ich mich grad in Timer und interrupts ein um die _delay_ms(); rauswerfen zu können...

Viel Erfolg ;)

Dieser Beitrag wurde bereits 1 mal editiert, zuletzt von »Clark« (29. Juni 2009, 11:01)

c++: The power, elegance and simplicity of a hand grenade.

Montag, 29. Juni 2009, 12:07

es gibt so ca. hunderte von Konventionen.


Na wenn das so ist, dann halt ich mich mal an meine Konvention ;D

sicher, dass active low aktiv ist?

Jup ist low aktiv ->

Quellcode

1
2
portregister &= ~(1<<pin_active)[...];	//Pins als Eingänge
port |= (1<<pin_active)[...];	//pull-up Widerstände einschalten


Zitat


Ich würde dir die Variante empfehlen, die ich oben geschrieben habe, sie ist a) schneller b) für den Prozessor "angenehmer" ;)

Quellcode

1
temp = (!PIND)&(DIP1|DIP2|DIP3);

das reduziert die gesamte rechenlast an der Stelle auf ein einziges nicht und eine binäre und verknüpfung.


Hier habe ich aber doch das Problem, dass wenn ich statt den Pins PD0 PD1 und PD2 die Pins PD4, PD5 und PD6 als Eingänge nutze ich falsche Werte herausbekomme...?
Deswegen habe ichs aufgeteilt.

Die Rechenlast hätte ich ja außerdem nur ein einziges mal und zwar beim anlegen der Versorgungsspannung an den Controller, da sie bei mir in meiner init-funktion steht die vor der while(1) aufgerufen wird.
In der while(1) steht dann der switch/case.
Um die Modis dann umzuschalten muss ich natürlich die Spannungsversorgung trennen das ist mir klar - aber das ist Absicht so :)

Dieser Beitrag wurde bereits 1 mal editiert, zuletzt von »Freakmaster« (29. Juni 2009, 12:08)

Da steht nix :-P

Ähnliche Themen