Forum: Mikrocontroller und Digitale Elektronik Anfänger fragen zur Programmierung des Atmega32A in C


von Martin K. (thereallife)


Lesenswert?

Hallo zusammen,
ich habe für mein Projekt bisher einen Arduino benutzt, da ich dort 
langsam sehr gut zurecht gekommen bin, wollte ich den Umstieg auf einen 
Atmega wagen.

Da ich kein gelernter C Programmierer bin und Arduino die Programmierung 
ja wie ich nun gesehen habe relativ einfach gestaltet hat. Habe ich 
einige fragen, ich denke es sind einfach so ein paar 
Grundverständnisfragen zur Programmierung in C, sind diese gelöst, komme 
ich glaube ich auch selbstständig gut weiter.

Ich habe zuerst mit einem simplen Blink programm angefangen.
1
// Testprogramm: Blinken auf Pin PC0
2
//
3
#ifndef MCU             // Welcher AVR genutzt wird, wird i.A. im Makefile definiert
4
#define MCU  atmega32
5
#endif
6
7
#ifndef F_CPU           // kann auch im Makefile definiert sein
8
#define F_CPU 8000000UL // Takt als LONG definieren, da zu groß für Integer
9
#endif
10
11
#include <avr/io.h>     // Namen der IO Register
12
#include <util/delay.h> // Funktionen zum warten
13
// Achtung, damit delay richtig funktioniert muß mit Optimierung compiliert werden
14
15
int main(void)
16
{
17
  DDRC = _BV(0);         // Nur PC0 als output,  _BV(0) = (1<<0) = 1
18
  PORTC = 254;           // Pullups auf allen anderen Pins
19
  
20
  while (1)
21
  {
22
    PORTC &= 255-_BV(0); //  0 auf Bit 0 Ausgeben, Rest so lassen
23
    _delay_ms(500);      //  100 ms Warten
24
    PORTC |= _BV(0);     //  1 auf Bit 0 Ausgeben, Rest so lassen
25
    _delay_ms(500);
26
  }
27
}

Hier stellen sich für mich jedoch schon die ersten fragen die im 
Tutorial nicht richtig erläutert wurden.
1
#include <avr/io.h>
Ich habe mir die io.h mal angeschaut, diese verweist ja eigentlich nur 
auf die einzelnen AVR's. Da ich den Atmega32A habe, kann ich dann auf 
diese Zeile verzichten ?
1
#define MCU  atmega32
und dafür statt
1
#include <avr/io.h>
1
#  include <avr/iom32a.h
einsetzen?
Wenn ich das richtig verstanden habe dient das define MCU ja nur der 
definierung für die io.h oder?

Frage 2:
1
DDRC = _BV(0);         // Nur PC0 als output,  _BV(0) = (1<<0) = 1
2
  PORTC = 254;           // Pullups auf allen anderen Pins
DDRC = _BV(0); // Diese Zeile versteh ich leider garnicht, warum 
verweist das DDRC auf PC0 ?
Bzw nehme ich an das DDRC auf Port C verweißt und _BV(0) und die 0 auf 
meinen jeweiligen PIN, stimmt das? Wofür steht dann das _BV?

PORTC = 254; versteh ich ebenfalls nicht, wie kommt es dazu das die 
Pullups auf allen anderen pins reingeschaltet werden?
Oder ist das nur auf Port C? Wenn es nur Port C wäre, kann meine 
vermutung das DDRC auf Port C verweist ja nicht stimmen...
und wofür steht die 254?
1
PORTC &= 255-_BV(0); //  0 auf Bit 0 Ausgeben, Rest so lassen
2
    _delay_ms(500);      //  100 ms Warten
3
    PORTC |= _BV(0);     //  1 auf Bit 0 Ausgeben, Rest so lassen
4
    _delay_ms(500);
PORTC &= 255-_BV(0);
Das müsste ja die kurzfassung von
PORTC = PORTC & 255-_BV(1); sein oder?
was wiederum Bitmanipulation sein müsste?! Doch was genau tut diese? und 
wie kommt es dazu das dadurch mit Pin 0 auf 0 gesetzt wird?
PORTC |= _BV(0) ist eine invertierung oder? Die 0 wird somit auf die 1 
gedreht wenn ich das richtig verstehe oder?

Um die beantwortung einfacher zu gestalten, habe ich meine Fragen hier 
nocheinmal versucht zusammen zu fassen:
Frage 1: Kann ich anstelle von <avr.io.h>und define MCU atmega32 nur 
ein<avr/iom32a.h>setzen?
  Frage 1.1: das define MCU, wird das nur für die definierung benötigt 
damit <avr.io.h> weiß   welchen AVR ich nutze? Oder dient diese 
definition noch einem anderen Zweck?
Frage 2: verweist DDRC auf Port C? Wenn nicht, worauf dann?
  Frage 2.1: Was bewirkt _BV(0); genau?
  Frage 2.1.1 : Steht die 0 in _BV(0) für die Pin Nummer?
  Frage 2.2 : Verweißt PORTC nur auf den Port C?
  Frage 2.2.1: Wofür steht die 254?
  Frage 2.2.1.1: Wenn die 254 für etwas bestimmtes steht, wo finde ich 
ein entsprechendes   Nachschlagewerk was mir erläutert, welche zahl für 
was steht?
Frage 3: Ist PORTC &= 255-_BV(0); die kurzfassung von PORTC = PORTC & 
255-_BV(1);
  Frage 3.1: Was macht diese Bitmanipulation? Und wie kommt es dazu das 
dadurch der pin   auf 0 gesetzt wird?
  Frage 3.2: Ist PORTC |= _BV(0) eine Invertierung, die dafür sorgt das 
die 0 zur 1 wird?

Ich danke euch im voraus vielmals für eure Hilfe, solltet ihr an der ein 
oder anderen Stelle der Meinung sein, mensch das steht doch dort in 
diesem Tutorials, sendet mir einfach nen link, dann arbeite ich mir das 
auch selber raus. Problem war nur das ich bisher trotz google relativ 
ratlos dasteh...

Beste Grüße
Martin

von Peter D. (peda)


Lesenswert?

Martin Kathke schrieb:
> #ifndef MCU             // Welcher AVR genutzt wird, wird i.A. im
> Makefile definiert

Nicht i.A., sondern nur da.
Der Compiler muß wissen, wofür er compilieren soll. Im Quelltext ist es 
zu spät.

Auch das F_CPU-Gerödel schmeiß raus, das gehört nicht in den Quelltext.
F_CPU auch nur im Make definieren.

Und diese #ifdefs gewöhn Dir schleunigst ab, die taugen nur, um sich 
sauber ins Knie zu schießen.

von g457 (Gast)


Lesenswert?

> Und diese #ifdefs gewöhn Dir schleunigst ab, die taugen nur, um sich
> sauber ins Knie zu schießen.

Nicht, wenn man sie richtig benutzt. Hier z.B. so:
1
#ifndef F_CPU
2
    #error F_CPU fehlt..
3
#endif

Und zum TO: Lies ein Buch über C.

von San L. (zwillingsfreunde)


Lesenswert?

Hallo Martin

Vorne weg: Ich programmiere sonst eher PIC's, kenne mich mit Atmel nicht 
sonderlich gut aus. Daher keine Garantie für die Antworten.

Martin Kathke schrieb:
> Frage 2: verweist DDRC auf Port C? Wenn nicht, worauf dann?

Mit einem Blich in das Datenblatt des entsprechenden Controllers hättest 
du das bestimmt auch alleine gelösst bekommen.

DDRC = Data Direction Register C

Mit diesem Register kannst du einen PIN des PORT C entweder als Eingang 
oder als Ausgang definieren. Passend dazu gibt es natürlich DDRA für 
PORT A, DDRB für PORT B und so weiter.

Auszug aus dem Datenblatt:
"The DDxn bit in the DDRx Register selects the direction of this pin. If 
DDxn is written logic one,
Pxn is configured as an output pin. If DDxn is written logic zero, Pxn 
is configured as an input
pin."

Martin Kathke schrieb:
> Frage 2.1: Was bewirkt _BV(0); genau?

Das kommt von deinem Compiler. Definiert dürfte das ganze in der 
avr-libc header sein.
1
#define _BV(bit) (1 << (bit))
Ich denke, somit dürfte sich der Befehl von alleine erklären. ;)

Martin Kathke schrieb:
> Frage 2.1.1 : Steht die 0 in _BV(0) für die Pin Nummer?

Ja und nein. Die 0 bzw. die 1 die dort stehen würde steht lediglich für 
den Wert des Bits, welchen du an irgendeine Variable / Register zuweisen 
willst.
1
DDRC = _BV(0);         // Nur PC0 als output,  _BV(0) = (1<<0) = 1
An dieser Stelle vermute ich einen falschen Kommentar. Eher passen 
würde:
// Nur PC0 als input
Somit würde nämlich die darauffolgende Zeile auch wieder mehr sinn 
machen:
1
PORTC = 254;           // Pullups auf allen anderen Pins
Da ein Port in diesem Falle aus 8 Bits besteht und 254 einem Binärwert 
von "1111 1110" entspricht, gäbe dies durchaus sinn. Ganz sicher bin ich 
mir da aber auch nicht.

Martin Kathke schrieb:
> Frage 2.2 : Verweißt PORTC nur auf den Port C?

Ja.

Martin Kathke schrieb:
> Frage 2.2.1: Wofür steht die 254?
>   Frage 2.2.1.1: Wenn die 254 für etwas bestimmtes steht, wo finde ich
> ein entsprechendes   Nachschlagewerk was mir erläutert, welche zahl für
> was steht?

Wie oben beschrieben. Wenn es darum geht eine Dezimalzahl aus einem Port 
auszugeben, musst du immer Binär denken.
1
// Alle diese Befehle machen genau das selbe!
2
PORTC = 255;        // Dezimale Schreibweise
3
PORTC = 0xFF;       // Hexadezimale Schreibweise
4
PORTC = 0b11111111; // Binäre Schreibweise

Das Tool hier wird dir bei solchen Anwendungen helfen:
http://www.langeneggers.ch/nuetzliches/umrechner-hex-dez.html

Die letzten Fragen lasse ich mal offen, ich denke das hier zu erklären 
wäre verschwendete Zeit, denn dafür gibt es Zahlreiche Informationen im 
Internet. Habe dir hier mal ein paar Links zusammengetragen:

http://de.wikibooks.org/wiki/C-Programmierung:_Ausdr%C3%BCcke_und_Operatoren

http://www.mikrocontroller.net/articles/Bitmanipulation
http://www.c-howto.de/tutorial-variablen-bitmanipulation.html

Gut möglich dass da das ein oder andere mal das selbe steht. Sind nur 
gerade die Google einträge, die mir ins Auge schossen, da ich die Seiten 
selbst auch kenne und damit gelernt habe.

Im allgemeinen Rate ich dir zu folgendem: Beginne noch einmal mit der 
"Basis." Es ist super, dass du den Umstieg von Arduino auf, nennen wir 
es richtige mikrocontroller machen willst. Wenn ich mir deinen Code 
ansehe, machst du dir das Leben aber selbst zu kompliziert. Gerade diese 
ganzen _BV Befehle könntest du Problemlos anders schreiben. Kaum einer 
schreibt seinen Code noch auf diese Art und Weise.

Ich hoffe ich konnte dir ein wenig weiterhelfen.

Gruss

von Felix G. (felixg)


Lesenswert?

Welches Tutorial hast du denn durchgearbeitet?

Das Tutorial von diesem Forum ist recht verständlich.

http://www.mikrocontroller.net/articles/AVR-GCC-Tutorial

von Martin K. (thereallife)


Lesenswert?

Entschuldigt, da habe ich mich wohl etwas missverständlich ausgedrückt.
Der Code selbst stammt nicht von mir sondern rn-wissen
http://www.rn-wissen.de/index.php/AVR-Einstieg_leicht_gemacht

Danke erstmal für eure Hilfe.
@San, ich hab natürlich überall nachgeschaut außer im Datenblatt... Das 
muss ich mir noch angewöhnen...

Ich werd mich mal durch eure Links fuchsen,
sollten noch fragen bestehn melde ich mich nochmal ;)

Felix G. schrieb:
> Welches Tutorial hast du denn durchgearbeitet?
>
> Das Tutorial von diesem Forum ist recht verständlich.
>
> http://www.mikrocontroller.net/articles/AVR-GCC-Tutorial

Felix G. schrieb:
> Welches Tutorial hast du denn durchgearbeitet?
>
> Das Tutorial von diesem Forum ist recht verständlich.
>
> http://www.mikrocontroller.net/articles/AVR-GCC-Tutorial

das ist klasse danke

: Bearbeitet durch User
von Martin K. (thereallife)


Lesenswert?

Hey Leute,
also ich glaube ich bin habs größtenteils verstanden, jetzt wollte ich 
mir aber ne funktion schreiben mit der ich meine pins direkt ansteuern 
kann und habs so versucht
1
#ifndef MCU             // Welcher AVR genutzt wird, wird i.A. im Makefile definiert j
2
#define MCU  atmega32
3
#endif
4
5
#ifndef F_CPU           // kann auch im Makefile definiert sein
6
#define F_CPU 8000000UL // Takt als LONG definieren, da zu groß für Integer
7
#endif
8
#include <avr/pgmspace.h>
9
#include <stdint.h>
10
#include <stdlib.h>
11
#include <avr/io.h>
12
#include <util/delay.h>
13
14
15
void PinWrite(uint8_t portnr, uint8_t pin, uint8_t val)
16
{
17
  if(portnr==1)
18
  {
19
    if (val == 0)
20
    {
21
      PORTC&=~_BV(pin);
22
    }
23
    else 
24
    {
25
      PORTC|=_BV(pin);
26
    }
27
  }  
28
}
29
30
31
int main() {
32
  PORTC = 0xFF;
33
  PinWrite(1, 0, 1);
34
  while(1)
35
  {
36
  }
37
  return 0;
38
}
funktioniert aber leider nicht, kann mir jmd sagen woran das liegt

von Route_66 H. (route_66)


Lesenswert?

Hallo!
Sind nach Reset nicht alle Portpins Eingänge?

von San L. (zwillingsfreunde)


Lesenswert?

Ist das der komplette Code?

Funktioniert denn das PORTC = 0xFF ?
Falls nicht, bzw auch wenn es funktioniert, solltest du die DDRC 
Register mal Konfigurieren. Solltest schon deklarieren ob die Pins Ein 
oder Ausgänge sind.

: Bearbeitet durch User
von spess53 (Gast)


Lesenswert?

Hi

>Funktioniert denn das PORTC = 0xFF ?

Wenn das JTAG-Interface aktiv ist, nicht.

MfG Spess

von San L. (zwillingsfreunde)


Lesenswert?

spess53 schrieb:
> Wenn das JTAG-Interface aktiv ist, nicht.
>
> MfG Spess

Ist wenn ich mich recht erinnere Standartgemäss aktiviert, oder?

Dann müsstest das Teil vielleicht auch ausschalten.

von spess53 (Gast)


Lesenswert?

Hi

>Ist wenn ich mich recht erinnere Standartgemäss aktiviert, oder?

Ja.

MfG Spess

von Route_66 H. (route_66)


Lesenswert?

Martin Kathke schrieb:
> PinWrite(1, 0, 1);

spess53 schrieb:
> Wenn das JTAG-Interface aktiv ist,

JTAG hat aber mit PORTC.0 nichts zu tun.

von San L. (zwillingsfreunde)


Lesenswert?

Route 66 schrieb:
> JTAG hat aber mit PORTC.0 nichts zu tun.

Dann liegt es wohl doch am fehlen von DDRC

von Martin K. (thereallife)


Lesenswert?

Danke jungs, das DDRC ist mir wohl durch die lappen gegangen...

von Martin K. (thereallife)


Lesenswert?

Hey Leute,
ich hoffe es ist in Ordnung das ich den Thread hier hin und wieder 
nochmal aufkommen lasse,
also ich habe nen merkwürdigen fehler,
ich habe die oben schon geschriebene funktion. 2 Leds und ein taster,
taster gedrückt = Led 2 an Led 1 aus
taster nicht gedrückt = Led 2 aus Led 1 an.
Soweit sogut,
LED 1 liegt an PC1 an led 2 eigentlich PC 2
das ganze funktioniert bei mir aber auf Port C pin 2&3 nicht ( 
weiterhoch habe ich nicht ausprobiert) jedoch funktioniort die Funktion 
ohne Probleme wenn ich die LED 2 an port a schalte (da gehen auch pin 2 
&3 )

vill sieht ja jmd den fehler,
oder habe ich mir vill pins vom meinem atmega zerschossen?
1
int pinWrite(uint8_t portnr, uint8_t pin, uint8_t val) // Pin HIGH oder LOW
2
{
3
  if(portnr==0)
4
  {
5
    if (val == 1)
6
    {
7
      PORTA&=~_BV(pin);
8
    }
9
    else
10
    {
11
      PORTA|=_BV(pin);
12
    }
13
  }
14
  if(portnr==1)
15
  {
16
    if (val == 1)
17
    {
18
      PORTB&=~_BV(pin);
19
    }
20
    else
21
    {
22
      PORTB|=_BV(pin);
23
    }
24
  }
25
  if(portnr==2)
26
  {
27
    if (val == 1)
28
    {
29
      PORTC&=~_BV(pin);
30
    }
31
    else
32
    {
33
      PORTC|=_BV(pin);
34
    }
35
  }
36
  if(portnr==3)
37
  {
38
    if (val == 1)
39
    {
40
      PORTD&=~_BV(pin);
41
    }
42
    else
43
    {
44
      PORTD|=_BV(pin);
45
    }
46
  }
47
}
48
49
int main()
50
{
51
    uint8_t i;
52
    DDRC = (0 << 0) | (1 << 1) | (1 << 2) | (1 << 3) | (1 << 4) | (1 << 5) | (1 << 6) | (1 << 7);
53
    DDRA = (1 << 0) | (1 << 1) | (1 << 2) | (1 << 3) | (1 << 4) | (1 << 5) | (1 << 6) | (1 << 7);
54
    pinWrite(2, 2, 1);
55
  while(1)
56
  {   
57
    i = PINC;
58
    i = i & 0x01;
59
    if (i != 1)
60
    {
61
      pinWrite(2, 1, 1);
62
      pinWrite(2, 2, 0);
63
    }
64
    else if (i != 0)
65
    {
66
      pinWrite(2, 2, 1);
67
      pinWrite(2, 1, 0);
68
    }
69
  }
70
  return 0;
71
}

von Cyblord -. (cyblord)


Lesenswert?

Dein "Programmierstil" wenn man diesen Saustall so nennen darf, ist 
grausig. Willst du nicht mal daran arbeiten? Guck dir mal ordentliche 
Programme an und vergleiche die mit deinem.

1.) diese vielen if-konstrukte durch switch oder besser durch 
Arrayzugriffe ersetzen. Am besten den ganzen Ansatz überdenken. Was soll 
dir deine pinWrite bringen? Deren Funktionalität kannst du direkt 
abbilden. Kapsele lieber höhere Funktionalität mit sprechenden Namen. 
Also eine Funktion tueDiesUndDas();
Diese low-level-pseudo-abstraktion bringt gar nichts.

2.) Symbolische Bitnamen verwenden

3.) Sinnvole Variablennamen

4.) Konstanten mit define sinnvoll benenen

5.) Entscheide dich, entweder (1<<BIT) oder _BV(BIT). Warum mischst du 
das? Je nach Laune, Tageszeit, Temperatur, Zufall? Wann nutzt du was?


Dann kannst du am Ende auch irgendwann sinnvoll auf Fehlersuche gehen. 
In diesem Krautsalat macht das keinen Sinn.

: Bearbeitet durch User
von spess53 (Gast)


Lesenswert?

Hi

>das ganze funktioniert bei mir aber auf Port C pin 2&3 nicht (
>weiterhoch habe ich nicht ausprobiert)

JTAG abgeschaltet?

MfG Spess

von Martin K. (thereallife)


Lesenswert?

cyblord ---- schrieb:
> Dein "Programmierstil" wenn man diesen Saustall so nennen darf, ist
> grausig. Willst du nicht mal daran arbeiten? Guck dir mal ordentliche
> Programme an und vergleiche die mit deinem.
>
> 1.) diese vielen if-konstrukte durch switch oder besser durch
> Arrayzugriffe ersetzen. Am besten den ganzen Ansatz überdenken. Was soll
> dir deine pinWrite bringen? Deren Funktionalität kannst du direkt
> abbilden. Kapsele lieber höhere Funktionalität mit sprechenden Namen.
> Also eine Funktion tueDiesUndDas();
> Diese low-level-pseudo-abstraktion bringt gar nichts.
>
> 2.) Symbolische Bitnamen verwenden
>
> 3.) Sinnvole Variablennamen
>
> 4.) Konstanten mit define sinnvoll benenen
>
> 5.) Entscheide dich, entweder (1<<BIT) oder _BV(BIT). Warum mischst du
> das? Je nach Laune, Tageszeit, Temperatur, Zufall? Wann nutzt du was?
>
>
> Dann kannst du am Ende auch irgendwann sinnvoll auf Fehlersuche gehen.
> In diesem Krautsalat macht das keinen Sinn.

Mein "Programmierstil" ist keine Woche alt, daher sieht er 
wahrscheinlich einfach noch aus wie sau, da bevor ich ihn schick machen 
kann ihn erstmal richtig verstehen muss ;)

1) meine pinwrite funktion habe ich mir geschrieben, weil ich von 
arduino komme, fürs verständnis wollte ich es hinbekommen eine funktion 
zu schreiben die einen einzelnen pin an und auschalten kann.
Wo liegt deiner meinung nach der vorteil von switch? ist etwas kürzer, 
aber ansonsten seh ich da keinen Vorteil.

2) ok
3) gut normalerweise mache ich das aber bei einer variablen und einem 
"ich probiere das jetzt einfach mal aus Code" war mir das relativ egal.
4) Alles klar
5) Ich bin wiegesagt neu in der Materie und wollte beide möglichkeiten 
anwenden um dafür ein "gespür" zu bekommen

@spess nein noch nicht, mache ich mal

//edit
JTAG ausgemacht, jetzt funktionierts ;)

: Bearbeitet durch User
von San L. (zwillingsfreunde)


Lesenswert?

Martin Kathke schrieb:
> Wo liegt deiner meinung nach der vorteil von switch? ist etwas kürzer,
> aber ansonsten seh ich da keinen Vorteil.

Es wird übersichtlicher. Viel übersichtlicher.

zudem besteht ein kleiner Unterschied zwischen If/else und einer Switch 
case abfrage: Während bei einer If/Else Abfrage beliebig viele boolsche 
Ausdrücke geprüft werden können, wird bei einer Switch Case immer nur 
GENAU der Ausdruck in der Klammer geprüft.

Zudem wage ich es mal zu behaupten, ein Compiler wandelt den Switch-Case 
Code besser um als eine If/Else Abfrage, da er weiss dass immer die 
selbe Variable geprüft wird. (Korrigiert mich, falls falsch!)

Gruss

von Karl H. (kbuchegg)


Lesenswert?

Martin Kathke schrieb:

> Mein "Programmierstil" ist keine Woche alt, daher sieht er
> wahrscheinlich einfach noch aus wie sau, da bevor ich ihn schick machen
> kann ihn erstmal richtig verstehen muss ;)

Die Sache ist die, das du im Grunde jetzt schon versuchst, ihn 'schick 
zu machen. Denn wenn du das nicht versucht hättest, dann hättest du 
geschrieben
1
int main()
2
{
3
  DDRC = (1 << PC1) | (1 << PC2);
4
5
  PORTC |= ( 1 << PC0 );    // Pullup am Taster einschalten
6
7
  while(1)
8
  {   
9
    if( PINC & ( 1 << PC0 ) )
10
    {
11
      PORTC &= ~( 1 << PC1 );
12
      PORTC |=  ( 1 << PC2 );
13
    }
14
    else
15
    {
16
      PORTC |=  ( 1 << PC1 );
17
      PORTC &= ~( 1 << PC2 );
18
    }
19
  }
20
21
  return 0;
22
}
und hättest die Funktion pinWrite überhaupt nicht gebraucht.

Um das ein wenig 'schöner' zu machen, musst du in die richtige Richtung 
gehen. Der Schlüssel für die richtige Richtung ist nicht die Funktion 
pinWrite, sondern die Beobachtung, dass du die Pinnummern nicht direkt 
im Code stehen haben willst, sondern an einer Stelle 'sammeln' willst. 
An dieser Stelle kommt dann der Präprocessor ins Spiel, der dir eine 
Textersetzung vornehmen kann, so dass du in deinem Code die Pinnummern 
nicht direkt im Code stehen hast, der eigentliche C Compiler das aber 
genau so präsentiert bekommt, weil der Präprozessor die entsprechenden 
Texte ersetzt hat
1
#define LED1  PC1
2
#define LED2  PC2
3
4
int main()
5
{
6
  DDRC = (1 << LED1) | (1 << LED2);
7
8
  PORTC |= ( 1 << PC0 );    // Pullup am Taster einschalten
9
10
  while(1)
11
  {   
12
    if( PINC & ( 1 << PC0 ) )
13
    {
14
      PORTC &= ~( 1 << LED1 );
15
      PORTC |=  ( 1 << LED2 );
16
    }
17
    else
18
    {
19
      PORTC |=  ( 1 << LED1 );
20
      PORTC &= ~( 1 << LED2 );
21
    }
22
  }
23
24
  return 0;
25
}

Und was mit den Pinbezeichnungen funktioniert, an denen die LEDs hängen, 
funktioniert genausogut mit der kompletten Portbezeichnung ('PORTC'), 
die man per Präprozessor abstrahieren kann, so dass sie nur noch in Form 
eines #define auftaucht und im Programmierer-lesbaren Code nicht mehr 
vorkommt.
1
#define LED_DDR  DDRC
2
#define LED_PORT PORTC
3
#define LED1     PC1
4
#define LED2     PC2
5
6
int main()
7
{
8
  LED_DDR = (1 << LED1) | (1 << LED2);
9
10
  PORTC |= ( 1 << PC0 );    // Pullup am Taster einschalten
11
12
  while(1)
13
  {   
14
    if( PINC & ( 1 << PC0 ) )
15
    {
16
      LED_PORT &= ~( 1 << LED1 );
17
      LED_PORT |=  ( 1 << LED2 );
18
    }
19
    else
20
    {
21
      LED_PORT |=  ( 1 << LED1 );
22
      LED_PORT &= ~( 1 << LED2 );
23
    }
24
  }
25
26
  return 0;
27
}

Mit dem Schalter kann man genau das gleiche machen, und so das Programm 
immer weiter verbessern, ohne dass man sich hier seltsam zu benutzende 
Funktionen schreiben muss, bei denen man jedesmal wieder aufs neue 
nachsehen muss, welcher der 3 Werte jetzt was bedeutet und bei denen man 
durch das ganze Programm gehen muss, um alle Aufrufe zu finden die 
angepasst werden müssen, wenn die LED auf einen anderen Port 'umziehen'.

Hier, mit den #define, brauchst du nicht viel machen. Sollen die LED auf 
den PORTA, und zwar auf die Pins 4 und 7, dann passt du einfach die 
#define an
1
#define LED_DDR  DDRA
2
#define LED_PORT PORTA
3
#define LED1     PA4
4
#define LED2     PA7
und den Rest macht der Präprozessor. Und zwar quer durchs ganze 
Programm, so dass du dich nicht zum Bleistift nicht mehr darum kümmern 
musst, dass die LED bei den beiden Schalterstellungen gegenläufig 
leuchten sollen. Du stellst in den #define nur noch ein, an welchen Pins 
an welchem Port die LED angeschlossen sind. Der Präprozessor verteilt 
diese Information dann im kompletten Programmtext, in dem er den Text 
'LED1' durch den jeweils von dir angegebenen anderen Text 'PA4' ersetzt, 
welcher dann durch den C Compiler geht.

Programmtext schön zu machen ist schon ok. Aber in deinem Fall 
verschleierst du mit der Funktion viel zu viel ohne dafür etwas zu 
kriegen.

> 1) meine pinwrite funktion habe ich mir geschrieben, weil ich von arduino komme,
Ja. Und die Funktion ist auch auf dem Arduino schon scheiße. Viel zu 
langsam.
Zumal deine pinWrite Funktion noch nicht mal ein müder Abklatsch der 
digitalWrite eines Arduino ist. Bei digitalWrite muss ich mir keine 
Gedanken machen, welcher Port zu einer Nummer gehört, die auf der 
Klemmleiste aufgedruckt ist, digitalWrite findet das selber raus. Bei 
dir hingegen muss ich in pinWrite erst recht wieder den Port angeben. 
Ich hab dadurch also nichts gewonnen, ausser das die eigentliche Absicht 
verschleiert wird.

digitalWrite hat auf einem Ardunio schon seine Berechtigung, vor allem 
in Hinblick darauf, sich damit von einer bestimmten Architektur zu 
lösen. Wenn du der Funktion aber die Fähigkeit klaust, die Pin-Nummern 
einfach von 0 beginnend durchzunummerieren ohne mich um die Ports 
kümmern zu müssen, dann ist ein wesentliches Feature dieser Funktion auf 
der Strecke geblieben und du hast die Funktion ad absurdum geführt.

: Bearbeitet durch User
von Martin K. (thereallife)


Lesenswert?

Ok,
vielen dank schonmal für eure Tipps und erklärungen, ich werde mein Code 
mal weitestgehend verbessern und überarbeiten.

Ich wüsste jedoch gerne warum du
die Pins so auschaltest
1
LED_PORT |=  ( 1 << LED2 );
anstatt so:
1
LED_PORT =  ( 0 << LED2 );
ich Persönlich finde variante 2 besser weil mir die 0 mehr ins auge 
springt als das |
Ist das geschmackssache wie man das macht? oder hat das gewisse vor bzw 
nachteile?

von Cyblord -. (cyblord)


Lesenswert?

Martin Kathke schrieb:
> Ok,
> vielen dank schonmal für eure Tipps und erklärungen, ich werde mein Code
> mal weitestgehend verbessern und überarbeiten.
>
> Ich wüsste jedoch gerne warum du
> die Pins so auschaltest
>
1
> LED_PORT |=  ( 1 << LED2 );
2
>
> anstatt so:
>
1
> LED_PORT =  ( 0 << LED2 );
2
>
> ich Persönlich finde variante 2 besser weil mir die 0 mehr ins auge
> springt als das |
> Ist das geschmackssache wie man das macht? oder hat das gewisse vor bzw
> nachteile?

Weil die Variante 2 nicht funktioniert und wenn ihr mal nicht nur blind 
abschreiben würdet, sondern euch mal 2 sekunden überlegen würdet, was 
genau diese Anweisung tut, dann wüsstet ihr auch warum.

Löschen tut man so: PORTB &= ~(1<<BIT)

Also verUNDEN mit der INVERTIERTEN Bitmaske, der Bits die man löschen 
will.

: Bearbeitet durch User
von Karl H. (kbuchegg)


Lesenswert?

Martin Kathke schrieb:

> anstatt so:
>
1
> LED_PORT =  ( 0 << LED2 );
2
>


weil 2 mal 0 immer noch 0 ist, genauso wie 3 mal 0 oder 5 mal 0.
Was hier steht ist also nichts anderes als
1
    LED_PORT = 0;

das kannst du auch einfacher haben :-). Macht allerdings nicht das 
Beabsichtigte: einen Pin und NUR DIESEN EINEN Pin auf 0 zu setzen.


> Ist das geschmackssache wie man das macht? oder hat das gewisse vor bzw
> nachteile?

Wenn du verstanden hast, was die OPeration << eigentlich macht, dann 
stellt sich diese Frage nicht mehr.
VErschieb doch mal das Bitmuster
1
   00000000
um zb 4 Bits nach links. Was kommt dabei raus?

Und was kommt raus, wenn du das Bitmuster
1
   00000001
um 4 Bits nach links verschiebst? Was ist der fundamentale UNterschied 
zwischen den beiden Ergebnissen?
Antwort: in dem einen Fall kriegst du ein Ergebnis, in dem genau an der 
BItposition 4 ein 1 Bit landet. Im anderen Fall kannst du schieben so 
viel du willst, das Ergebnis besteht immer nur aus einem Bitmuster 
welches aus lauter 0 Bits besteht. (Denn mathematisch ist 0 
mulitpliziert mit n immer 0, egal wie groß n ist)

: Bearbeitet durch User
von Martin K. (thereallife)


Lesenswert?

Danke für eure Geduld und eure erklärungen,
ich habe das << anscheinend falsch verstanden, mir war nicht klar das 
hier multipliziert wird, dachte hier wird einfach nur verschoben... Habs 
jetzt verstanden. Vielen dank euch!

von Cyblord -. (cyblord)


Lesenswert?

Martin Kathke schrieb:
> Danke für eure Geduld und eure erklärungen,
> ich habe das << anscheinend falsch verstanden, mir war nicht klar das
> hier multipliziert wird, dachte hier wird einfach nur verschoben...

Es wird nur verschoben, aber was bewirkt denn ein bitweises schieben?
Was bewirkt das Schieben einer Stelle im Dezimalsystem? Was passiert 
wenn du 30 (dez.) hast und dann eine Stelle nach links schiebst? Na?
Mann denk doch mal selber nach und rechne mal nach. Ist ja nicht zum 
aushalten.

von San L. (zwillingsfreunde)


Lesenswert?

cyblord ---- schrieb:
> Mann denk doch mal selber nach und rechne mal nach. Ist ja nicht zum
> aushalten.

Wenn dich solche Dinge bereits an die Grenzen deiner nerven treiben, 
dann respekt von mir dass du bis heute durchs Leben gekommen bist.

von Karl H. (kbuchegg)


Lesenswert?

Martin Kathke schrieb:
> Danke für eure Geduld und eure erklärungen,
> ich habe das << anscheinend falsch verstanden, mir war nicht klar das
> hier multipliziert wird, dachte hier wird einfach nur verschoben... Habs
> jetzt verstanden.

Echt? Sieht noch nicht so aus.


Lass uns mal ein paar Bitmuster ansehen und ihre zugehörigen 
Dezimalzahlen
1
  Binär                  Dezimal
2
3
  0000 0001                 1
4
  0000 0010                 2
5
  0000 0100                 4
6
  0000 1000                 8
7
  0001 0000                16
8
  0010 0000                32

na, klingelts?

Binär schiebt sich das 1 Bit von rechts nach links um 1 Stelle weiter. 
Als Dezimalzahl ausgedrückt, ist jede Zeile das Doppelte der 
vorhergehenden Zeile.
1 mal nach links schieben ist also equivalent mit einer Multiplikation 
mit 2.

Anderes Beispiel. Wir haben ein Bitmuster
1
  0000 0101
das ist das Bitmuster für Dezimal 5

Jetzt schieb das mal 1 Stelle nach links
1
  0000 1010

das ist das Bitmuster für dezimal 10 (oder Hexadezimal A). 10 ist aber 
'zufällig' genau das Doppelte von 5.


Bei Binärzahlen entspricht also eine Verschiebung um 1 Stelle nach links 
(und anhängen einer 0) einer Multiplikation mit 2. Das ist völlig 
analog, wie in deinem gewohnten Dezimalsystem eine Verschiebung nach 
links und Anhängen einer 0 einer Multiplkation mit 10 entspricht. Und so 
wird dann im Dezimalsytem aus 0015 das Ergebnis 0150. Das kann man 
erhalten, in dem man schiebt und eine 0 anhängt, oder in dem man mit 10 
multipliziert. Nur das Schieben viel einfacher ist, weil man im Grunde 
gar nicht rechnen muss.
Und das funktioniert in allen Stellenwert-Systemen. Denn die sind ja 
genau so definiert, dass die Stelle weiter links das jeweils n-fache 
ist, wenn n die Basis des Zahlensystems ist.

: Bearbeitet durch User
von Quack (Gast)


Lesenswert?

San Lue schrieb:
> Zudem wage ich es mal zu behaupten, ein Compiler wandelt den Switch-Case
> Code besser um als eine If/Else Abfrage, da er weiss dass immer die
> selbe Variable geprüft wird. (Korrigiert mich, falls falsch!)

Ist falsch, kann auch mal genau andersrum sein.

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
Noch kein Account? Hier anmelden.