Forum: Mikrocontroller und Digitale Elektronik ATtiny13 - PB0 bis PB5 verhalten sich unterschiedlich/"inkonsistent"?


von Michael Becker (Gast)


Angehängte Dateien:

Lesenswert?

Hallo zusammen,

bin gerade dabei, mich mit der Funktionsweise des ATtiny vertraut zu 
machen. Dazu habe ich eine einfache Schaltung (siehe Anhang) und 
folgendes kleine Programm geschrieben:

<c>#include <avr/io.h>
#include <avr/delay.h>

int main(void)
{
  DDRB = 0x00111111;

  while (1)
  {
    PORTB = 0x00111111;
    _delay_ms(500);
    PORTB = 0x00000000;
    _delay_ms(500);
  }
}</c>

Ziel ist es, an BP0 bis PB5 LEDs zum Blinken zu bringen. Dazu wechsele 
ich das gelbe Kabel einfach an die einzelnen PINs. Seltsamerweise blinkt 
die LED nur an PB0 und PB4. An BP1, PB2 und PB3 bleibt die LED aus. 
Woran kann das liegen? Kann mir jemand helfen? Habe schon verschiedene 
Variationen im Programm und in der Schaltung probiert, aber komme der 
Sache nicht auf den Grund - was übersehe ich da?

Und noch eine kurze Frage: In vielen Beispielen wird der Reset-PIN über 
einen 10kOhm Widerstand mit VCC verbunden - ist das unbedingt notwendig? 
Und was ist der Grund dafür?

VG & danke für Eure Hilfe
Michael

von Eumel (Gast)


Lesenswert?

Michael Becker schrieb:
> Und noch eine kurze Frage: In vielen Beispielen wird der Reset-PIN über
> einen 10kOhm Widerstand mit VCC verbunden - ist das unbedingt notwendig?
> Und was ist der Grund dafür?

Weil der interne Pullup recht schwach ist. Notwendig ist das beim 
Attiny13 meines Wissens nach nicht. Genaueres sagt dir das Datenblatt. 
Darüber hinaus solltest du wissen, dass der PB5 für dich nicht nutzbar 
ist. Oder hast du die entsprechende Fuse gesetzt?

Und was dein Problem angeht. Du benutzt Hexadezimale Zahlen willst aber 
Binäre verwenden.

von Floh (Gast)


Lesenswert?

Vorwiderstand für die LED drin?

Die Programm ist falsch.

PORTB = 0x00011111 ist hexadezimal nicht binär !!

Probiers mal mit PORTB = 0b00011111;

Wenn du ISP verwendest, kannst du PB5 nicht nutzen, da er als Reset 
arbeitet. Dieser wird normalerweise auch über einen Widerstand auf Vcc 
gelegt, damit der Controller nicht durch äußere Einflüsse resetet wird.

von Michael Becker (Gast)


Lesenswert?

Ahh, vielen Dank für die schnelle Hilfe - jetzt klappt's (natürlich) 
:-)

Bzgl. Reset-PIN: nein, Fuse habe ich nicht gesetzt. Ich habe davon 
gehört, aber was bedeutet das eigentlich? Und ISP benutze ich auch 
nicht, ich mache das über ein myAVR MultiProg - bin absoluter Anfänger 
(muss ich wohl nicht sagen...)

von Eumel (Gast)


Lesenswert?

Michael Becker schrieb:
> Und ISP benutze ich auch
> nicht, ich mache das über ein myAVR MultiProg - bin absoluter Anfänger

Das ist ISP, was auch sonst?

Michael Becker schrieb:
> Bzgl. Reset-PIN: nein, Fuse habe ich nicht gesetzt. Ich habe davon
> gehört, aber was bedeutet das eigentlich?

Der Reset bin kann nur duch setzen einer bestimmten Fuse als normaler 
I/O Pin benutzt werden. Danach kannst du den Controller NICHT mehr 
programmieren.

Hier ein Link zum Datenblatt, wieso liest du das nicht?
http://www.atmel.com/Images/doc2535.pdf

von Michael Becker (Gast)


Lesenswert?

> Das ist ISP, was auch sonst?

Ok, aber ich wechsel den Controller dann immer zwischen Programmierboard 
und eigener Schaltung hin und her - dachte ISP wäre, wenn man den Chip 
bei der Programmierung in der Schaltung drin lässt. Gibt's dann 
überhaupt eine andere Möglichkeit neben ISP den Code auf den Controller 
zu bringen?

> Hier ein Link zum Datenblatt, wieso liest du das nicht?
Das hatte ich teilweise gelesen - aber lesen und verstehen sind zwei 
paar Schuhe (habe bisher nur grobes Grundverständnis...)

von Thomas E. (thomase)


Lesenswert?

Michael Becker schrieb:
> Ok, aber ich wechsel den Controller dann immer zwischen Programmierboard
> und eigener Schaltung hin und her - dachte ISP wäre, wenn man den Chip
> bei der Programmierung in der Schaltung drin lässt.
Ein Sofa ist ein Wohnzimmermöbelstück. Wenn du es jetzt in den Garten 
stellst, was ist es dann? Eine Hollywoodschaukel oder immer noch ein 
Sofa?

Dein Programmer ist ein ISP-Programmer.

Michael Becker schrieb:
> Gibt's dann
> überhaupt eine andere Möglichkeit neben ISP den Code auf den Controller
> zu bringen?
Ja. Mit HV-Programming. Das kann dein Programmer aber nicht.
Also musst du, wie die meisten anderen auch, auf den Reset-Pin als Port 
verzichten. Das ist einfach so.

mfg.

von Michael Becker (Gast)


Angehängte Dateien:

Lesenswert?

ok, verstehe...

Habe noch eine Frage/Problem: Eigentlich möchte ich eine kleine 
Alarmanlage für die LEGO-Eisenbahn meines Sohnes basteln (hab's 
versprochen...) - bei Druck auf einen Taster sollen 3 LEDs abwechselnd 
blinken und Sound über Speaker.

Ich habe dann gestern Abend eifrig gelötet (siehe Foto). Leider 
funktioniert das auch nicht so, wie ich es mir vorstelle. Das Programm 
scheint soweit ok zu sein (läuft auf myAVR MK2 und auch der Einbau auf 
der Steckplatine zeigt Symptome, dass es prinzipiell gehen sollte). Bei 
Anschluss an die selbst zusammengeschweißte Platine passiert aber nix, 
kein Leuchten, kein Sound.

Wie kann ich prüfen, ob die Anschlüsse richtig gepatched sind? Meine 
Idee (siehe Foto) war, den Socket von GND mit (z. B.) PB2 zu verbinden 
(gelbes Kabel), um zu sehen, ob die Verbindung im Prinzip richtig 
gelötet ist... das funktioniert bei allen Pins. Oder ist das kein 
valider Testansatz, bin ich auf dem Holzweg?

von Eumel (Gast)


Lesenswert?

Mal was ganz anderes. Benutzt du LEDs mit eingebautem Widerstand?

von Michael Becker (Gast)


Lesenswert?

nein, aber habe auf der Platine passende Vorwiderstände (blau) eingebaut 
(und auch einen Spannungswandler von 9 auf 5 V). Auf der 
Test-Steckplatine lass ich sie manchmal (aus Faulheit) weg - wohl nicht 
so gut für die Lebensdauer der Dinger...

von Thomas E. (thomase)


Lesenswert?

Michael Becker schrieb:
> Wie kann ich prüfen, ob die Anschlüsse richtig gepatched sind? Meine
> Idee (siehe Foto) war, den Socket von GND mit (z. B.) PB2 zu verbinden
> (gelbes Kabel), um zu sehen, ob die Verbindung im Prinzip richtig
> gelötet ist... das funktioniert bei allen Pins. Oder ist das kein
> valider Testansatz, bin ich auf dem Holzweg?
Wenn GND am Pin die Led zum leuchten bringen soll und sie mit Brücke an 
GND auch leuchtet, ist das schon OK.
Die 3 blauen Widerstände sind wohl die Vorwiderstände für die Leds und 
der braune der Basiswiderstand vom Transistor?
In der Schaltung fehlt noch ein 100nF-Stützkondensator zwischen Vcc und 
GND so nah wie geht am Controller. Das ist das mit Abstand wichtigste 
Bauteil der Digitaltechnik.

Zeig mal den Schaltplan und das Programm. Sonst kann man da nichts 
weiter zu sagen.

mfg.

von Thomas E. (thomase)


Lesenswert?

Michael Becker schrieb:
> Test-Steckplatine lass ich sie manchmal (aus Faulheit) weg - wohl nicht
> so gut für die Lebensdauer der Dinger...
Die können einiges ab. Aber der Controller muß den Strom dann auch 
liefern können. Und dadei wird er dann recht warm.

mfg.

von Eumel (Gast)


Lesenswert?

Michael Becker schrieb:
> nein, aber habe auf der Platine passende Vorwiderstände (blau) eingebaut
> (und auch einen Spannungswandler von 9 auf 5 V). Auf der
> Test-Steckplatine lass ich sie manchmal (aus Faulheit) weg - wohl nicht
> so gut für die Lebensdauer der Dinger...

Stimmt, auf der Platine sind welche. Aber du weißt ja anscheinend um die 
Problematik, dann ist es natürlich deine Sache. Wollte nur nicht, dass 
dir aus Unwissenheit was kaputt geht ;)

von Michael Becker (Gast)


Lesenswert?

> Die 3 blauen Widerstände sind wohl die Vorwiderstände für die Leds

ja genau

> und der braune der Basiswiderstand vom Transistor?

Der braune Widerstand ist 10kOhm am Reset-PIN. Das schwarze Ding ist der 
Spannungswandler 9V > 5V.

> In der Schaltung fehlt noch ein 100nF-Stützkondensator zwischen Vcc und
GND so nah wie geht am Controller. Das ist das mit Abstand wichtigste
Bauteil der Digitaltechnik.

Das sagt mir jetzt gar nix... gibt's dazu einen Link / Beitrag im Forum 
/ Beispielschaltplan? Ich hab mir vor allem die Beispiele hier 
angeschaut:
http://www.avr-asm-tutorial.net/avr_de/index.html
Kann das ein Grund sein, dass die Schaltung gar nicht geht? Zumal es ja 
auf der Steckplatine soweit klappt (auch ohne dieses wichtige Teil)

> Zeig mal den Schaltplan und das Programm.

Programm siehe Anhang. Schaltplan hatte ich noch nicht aufgezeichnet, 
sondern mir nur so überlegt und dann losgelötet - ist wohl etwas 
"hemdsärmlig". Aber prinzipieller Aufbau ist so:

- Nach der Batterie kommt zunächst der Spannungswandler.
- Dann geht es von + Pol weiter an 5 der 10 bunten Kabel:
--> für die LEDs Vorwiderstände
--> für Speaker direkt ohne Widerstand
--> einer an VCC (auch ohne Widerstand)
- Die anderen 5 Kabel sind mit PB0 bis PB4 verbunden.
- GND führt an den - Pol der Batterie
- und dann noch der 10KOhm Widerstand zwischen VCC und Reset

Werde mal einen Schaltplan zeichnen, oder kannst Du mit der Beschreibung 
was anfangen? Danke!!

von Michael Becker (Gast)


Angehängte Dateien:

Lesenswert?

Hier noch die Datei mit dem Coding...

von Michael Becker (Gast)


Angehängte Dateien:

Lesenswert?

und hier noch eine Skizze zum Aufbau - gibt's für Schaltpläne eigentlich 
eine empfehlenswerte Software/Freeware? Und auch etwas für 
Platinenlayout?  Wäre natürlich cool, wenn so ein Programm auf "grobe 
Fehler" in der Schaltung hinweisen würde... Oder einfach mit Visio 
zeichnen?

von Michael Becker (Gast)


Angehängte Dateien:

Lesenswert?

hier die richtige Datei - mann, ist schon spät.... ;-)

von Thomas E. (thomase)


Lesenswert?

Michael Becker schrieb:
> Hier noch die Datei mit dem Coding...
Ist das das ganze Programm?
Wo ist F_CPU definiert?
Das ist wichtg für delay. Gibt auch Mecker, wenn das fehlt.
1
#ifndef F_CPU
2
  #error F_CPU not defined.
3
#endif
4
#include <avr/io.h>          
5
#include <avr/delay.h>          
6
          
7
int main(void)          
8
{          
9
  //PIN Belegung        
10
  //        
11
  //PB0    LED grün    
12
  //PB1    LED rot    
13
  //PB2    LED blau    
14
  //PB3    Speaker    
15
  //PB4    Taster    
16
          
17
  DDRB = 0b00001111;        
18
  PORTB = 0b00010000;
19
  //Gewöhn dir das ab        
20
  DDRB |= (1 << 3) |(1 << 2) | (1 << 1) | (1 << 0);
21
  //Da sieht man sofort was Sache ist. Mit Copy&paste ist das kaum mehr Schreibaufwand und man kann sich nicht verzählen.
22
          
23
  int i;        
24
  int alarmOnOff;        
25
  int count;        
26
     //Recht verschwenderisch. Da keine Zahl >255 ist, reicht unsigned char. 
27
//Du hast einen RAM-Mickerling vor dir.
28
//Und Zählvariablen immer lokal. 
29
//Sonst suchst du dich irgendwann tot, wenn das Programm etwas größer wird.
30
  while (1)        
31
  {        
32
    while (!(PINB & 0b00010000))  //Was soll das ein? Entprellung?    
33
    {      
34
      count++;    
35
    }      
36
          
37
//    if (count >> 200)      //Count 200 mal nach rechts schieben? Sportlich!
38
//    Soll wohl das sein
39
    if(count > 200)        
40
    {      
41
//      if(alarmOnOff == 1) alarmOnOff = 0; else alarmOnOff=1;    
42
//      Das macht das selbe
43
      alarmOnOff = !alarmOnOff;
44
//      Und jeder sieht, was das soll.
45
      count = 0;    
46
    }      
47
          
48
//    if (alarmOnOff == 1)      
49
    if (alarmOnOff)    //Is nun mal C
50
    {      
51
      PORTB = 0b00000001;  //siehe oben  
52
      
53
      //Diett.    
54
      for(i=1;i<100;i++)    
55
      {    
56
        _delay_ms(2);  
57
//        PORTB = PORTB ^ (1<<PB3);  
58
59
//        Macht keinen Unterschied in der Funktion, hat sich aber als Schreibweise so eingebürgert.
60
//        Sonst muß man immer dreimal hingucken, ob das richtig ist
61
62
        PORTB ^= (1<<PB3);  
63
64
      }    
65
          
66
      PORTB = 0b00000010;  //siehe oben  
67
      
68
      //Dött
69
      for(i=1;i<100;i++)    
70
      {    
71
        _delay_ms(3);  
72
        PORTB = PORTB ^ (1<<PB3);  //siehe oben
73
      }
74
      
75
          
76
      PORTB = 0b00000100;    //siehe oben
77
          
78
      //Diett
79
      for(i=1;i<100;i++)    
80
      {    
81
        _delay_ms(2);  
82
        PORTB = PORTB ^ (1<<PB3);  //etc.pp
83
      }
84
      
85
    }      
86
    else      
87
    {      
88
      PORTB = 0b00000000;    
89
    }      
90
  }        
91
}



Michael Becker schrieb:
> Werde mal einen Schaltplan zeichnen, oder kannst Du mit der Beschreibung
> was anfangen? Danke!!
Nee ist schon klar. Ist ja kein ganzes Stellwerk. Vielleicht auch besser 
so. War ja gestern in Kiel wieder so weit. Totalabsturz. So gesehen bist 
du jetzt in bester Gesellschaft.


http://www.mikrocontroller.net/articles/Entprellung
http://www.mikrocontroller.net/articles/Timer
Noch was zum Lesen. Denn weder ohne Entprellung noch mit delay wirst du 
lange glücklich sein. Willst ja nicht ewig mit deinem Sohn 
Schienenersatzverkehr spielen.

Die Schaltung ist  nichts großartiges. Nochmal alles durchmessen und 
dann wird das schon. Und dann machst du Version 2 bei der Software. Dann 
läuft das auch. Abgesehen von der merkwürdigen Entprellung, sollte das 
auch jetzt schon piepsen. Manche Piezos brauchen ein paar KOhm parallel, 
damit sie was von sich geben.

Michael Becker schrieb:
> Das sagt mir jetzt gar nix... gibt's dazu einen Link / Beitrag im Forum
> / Beispielschaltplan? Ich hab mir vor allem die Beispiele hier
> angeschaut:
> http://www.avr-asm-tutorial.net/avr_de/index.html
> Kann das ein Grund sein, dass die Schaltung gar nicht geht? Zumal es ja
> auf der Steckplatine soweit klappt (auch ohne dieses wichtige Teil)
Der verhindert, daß beim Schalten im Controller und das passiert min. 
1Mio/s, die Spannung kurz zusammenbricht, da ein hoher Strom gezogen 
wird. Ist extrem kurz aber immer noch lang genug, um Fehlfunktionen 
auszulösen. Da kommt kein Netzteil hinterher. Deshalb 100nF, Keramik. 
Gilt für alle digitalen Bauteile.

mfg.

von F. F. (foldi)


Lesenswert?

Wieso schreibt ihr alle 8 Bit auf den Port B?
Das ist ein 6 Bit Port.

von Michael Becker (Gast)


Lesenswert?

Vielen lieben Dank für die Tipps zum Coding - da gibt es sicher noch 
Verbesserungspotenzial für eine Version 2, 3, 4 usw. :-) ich beschäftige 
mich erst seit ein paar Tagen mit C - und Assembler hatte ich in der 
Jugendzeit mal auf dem C64 etwas programmiert. Ansonsten kenne ich eher 
die höheren Programmiersprachen (C#, Java, VisualBasic) - und da kümmert 
man sich um viele Aspekte ja gar nicht so. Generell wird das mit dem 
Coding aber schon hinhauen - viel schwerer tue ich mir bei der Hardware 
und den Grundlagen der Elektrotechnik...

Ich hatte das Programm (da es nicht gelaufen ist) testweise um die 
ganzen Teile für den Taster und den Sound verkürzt - also nur noch 3 
LEDs sollen blinken/leuchten. Das komische ist, dass das Programm auf 
meinem Test-Steckboard funktioniert, aber auf der selbst gebastelten 
Platine nicht. Ok, ich hatte auch vorgestern zum ersten Mal einen 
Lötkolben in der Hand... da ich mir nicht 100% sicher bin, dass alles 
richtig verbunden ist, habe ich über die Brücke getestet - und dann geht 
die LED ja auch an. Kann mir einfach nicht erklären, warum das Programm 
einmal funktioniert und einmal nicht (also wo genau der Unterschied 
zwischen Steckplatine und verlöteter Platine steckt...)

> Nochmal alles durchmessen und dann wird das schon.

Was/wie kann bzw. soll ich messen? Mir ist nur die Brücke eingefallen 
(habe auch kaum Gerätschaften..)

> Willst ja nicht ewig mit deinem Sohn Schienenersatzverkehr spielen.

Das war der Auslöser, mich in die Thematik einzuarbeiten. Mein 
eigentliches "Projekt" ist eine Steuerung der Weichen über 
IR-Fernsteuerung und Servo. Dazu gibt es ja eine gute Vorlage:

http://www.avr-asm-tutorial.net/avr_de/ir/ir_tx/ir_tx.html

Habe auch einen Plan im Kopf, wie das theoretisch gehen sollte, aber ist 
wohl noch ein weiter Weg... jetzt muss ich erst mal eine LED auf meiner 
selbstgebastelten Platine zum Strahlen bringen...

VG Michael

von Thomas E. (thomase)


Lesenswert?

Michael Becker schrieb:
> Was/wie kann bzw. soll ich messen? Mir ist nur die Brücke eingefallen
>
> (habe auch kaum Gerätschaften..)
1
#include <avr/io.h>          
2
3
#define F_CPU 1000000UL  // Oder je nachdem
4
#include <util/delay.h>          
5
          
6
7
#define PPP 0
8
9
int main(void)          
10
{          
11
  DDRB |= (1 << PPP);
12
13
  while(1)
14
  {
15
    PORTB ^= (1 << PPP);
16
    _delay_ms(1000);
17
  }
18
}
19
20
//Damit blinkt EIN Ausgang im Sekundentakt.
21
//Voltmeter, Led, ausnahmsweise mit Vorwiderstand
22
//PPP ändern, compilieren, nächster Test

von Michael Becker (Gast)


Lesenswert?

Erst mal nochmal vielen Dank für die nächtliche Unterstützung!!

Ich habe heute früh alles neu gelötet - und es klappt (fast alles) :-))
Lauflicht und Speaker funktionieren (alle Outputs). Der Taster zum Ein- 
und Ausschalten leider noch nicht auf der selbst gelöteten Platine. 
Vermutlich habe ich einen Fehler drin (?):

Der Taster hängt zwischen PB4 und VCC, und nicht (wie ich jetzt in 
einigen Beispielen gesehen habe) zwischen PB4 und GND... zwischen PB4 
und GND geht der Taster mit folgendem Code (auf Steckplatine getestet):
1
int main(void)
2
{
3
  DDRB = 0b00000001; // LED an PB0
4
  PORTB = 0b00010000; // Taster an PB4
5
6
  while (1)
7
  {
8
    if ((PINB & 0b00010000) != 0)
9
    {
10
      // Taste nicht gedrückt --> LED aus
11
      PORTB = 0b00010001;
12
    }
13
    else
14
    {
15
      // Taste gedrückt --> LED an
16
      PORTB = 0b00010000;
17
    }
18
  }  
19
}

Frage: Wenn der Taster zwischen PB4 und VCC (statt GND) hängt, kann ich 
da softwaremäßig noch etwas retten? Oder muss ich umlöten?

Noch eine Frage: Nach meinem Verständnis können LEDs zwischen Pin und 
VCC oder zwischen Pin und GND hängen - die LED geht dann in einem Fall 
bei gesetztem Bit an und im anderen Fall bei gelöschtem Bit. Stimmt das 
so?

PS: Sorry, dass ich mir die 0bxxxxxx Schreibweise noch nicht abgewöhnt 
habe ;-) die Syntax (0 << 2) versteh ich noch nicht ganz - bleiben da 
alle Bits, die nicht explizit gesetzt/gelöscht werden, unverändert? Und 
wie würde ich dieses Statement (Prüfen ob PB4 nicht gesetzt ist) in der 
"eleganteren" Syntax formulieren:

if ((PINB & 0b00010000) != 0)

VG Michael

von Eumel (Gast)


Lesenswert?

Entweder du benutzt die internen Pullups (Nach Vcc) dann muss der Taster 
nach GND schalten oder du benutzt externe Pull up/down Widerstände, dann 
kannst du dir aussuchen wohin der Taster schaltet.

von Karl H. (kbuchegg)


Lesenswert?

Michael Becker schrieb:

> Der Taster hängt zwischen PB4 und VCC, und nicht (wie ich jetzt in
> einigen Beispielen gesehen habe) zwischen PB4 und GND...

Dafür gibt es einen Grund, warum der Taster üblicherweise nach GND 
geschaltet wird: Auf die Art kann man die eingebauten Pullup Widerstände 
benutzen, und braucht keine externen Pulldown-Widerstände.

Hast du eigentlich die Tutorien
AVR-Tutorial
AVR-GCC-Tutorial
schon entdeckt?
Fast alle deine Fragen, die du seit gestern vorgebracht hast, sind da 
drinnen abgehandelt.


> Frage: Wenn der Taster zwischen PB4 und VCC (statt GND) hängt, kann ich
> da softwaremäßig noch etwas retten? Oder muss ich umlöten?

Löte es um.
Ist einfacher. Denn löten musst du sowieso. Egal ob du jetzt einen 
zusätzlichen Pulldown-Widerstand einlötest oder den Taster umlötest.

von Karl H. (kbuchegg)


Lesenswert?

> Noch eine Frage: Nach meinem Verständnis können LEDs zwischen Pin
> und VCC oder zwischen Pin und GND hängen - die LED geht dann in einem
> Fall bei gesetztem Bit an und im anderen Fall bei gelöschtem Bit.
> Stimmt das so?

Du hast das Steckbrett vor dir.
Steck die LED entsprechend und probier es aus. Da beißt nichts.

von Karl H. (kbuchegg)


Lesenswert?

Frank O. schrieb:
> Wieso schreibt ihr alle 8 Bit auf den Port B?
> Das ist ein 6 Bit Port.

Weil die kleinste Einheit in der Programmierung nun mal ein Byte ist. 
Und 1 Byte besteht aus 8 Bit.

Manche Controller, wie zb dein Tiny, kennen auch nocht Einzelbitbefehle. 
Aber die kennt der Compiler auch und ersetzt einen Byte-Portzugriff 
durch diese Befehle, wenn das möglich ist (und du das in C so 
geschrieben hast)

von Karl H. (kbuchegg)


Lesenswert?

Michael Becker schrieb:

> PS: Sorry, dass ich mir die 0bxxxxxx Schreibweise noch nicht abgewöhnt
> habe ;-)

Solltest du aber. Ganz schnell!

> die Syntax (0 << 2) versteh ich noch nicht ganz

Die ist auch unsinnig. Eine 0 kann man nach links oder rechts schieben, 
so oft man will. Das Ergebnis ist wieder 0.

Die Syntax um die es geht ist

    1 << 5    (wobei die 5 nur ein mögliches Beispiel sind)


> - bleiben da
> alle Bits, die nicht explizit gesetzt/gelöscht werden, unverändert?


Ist doch ganz simpel.
<< ist der Verschiebeoperator. D.h.

   1        nimm eine binäre 1, also binär       00000001
   1 <<     diese 1 wird nach links geschoben
   1 << 5   zum Beispiel 5 mal nach links        00100000


Das Ergebnis ist ein Byte, bei dem das Bit an der Bitposition 5 auf 1 
steht und alle anderen Bits sind 0.

   1 << 3     ergibt ein Byte, bei dem das Bit an Position 3 auf 1 ist
   1 << 6     ergibt ein Byte, bei dem das Bit an Position 6 auf 1 ist
   1 << n     ergibt ein Byte, bei dem das Bit an Position n auf 1 ist


Vielleicht siehst du jetzt, warum diese Schreibweise besser ist.

  1 << x
sagt mir auf einen Blick, an welcher Position das eine 1 Bit ist. Bei
  00100000
muss ich erst mal die 0-en links von der 1 zählen(*), damit ich dieselbe 
Information habe.

  1 << PB4

ergibt also ein Byte, das wenn ich es ausgebe, den kompletten Port B auf 
0 setzt, mit Ausnahme des Pins PB4, der dann 1 ist. Da muss ich nicht 
großartig Bits zählen. Das richtet mir der Compiler alles entsprechend 
richtig her.


(*) mit der latenten Gefahr, dass ich mich verzähle.

von F. F. (foldi)


Lesenswert?

Karl Heinz Buchegger schrieb:
> Frank O. schrieb:
>> Wieso schreibt ihr alle 8 Bit auf den Port B?
>> Das ist ein 6 Bit Port.
>
> Weil die kleinste Einheit in der Programmierung nun mal ein Byte ist.
> Und 1 Byte besteht aus 8 Bit.
>
> Manche Controller, wie zb dein Tiny, kennen auch nocht Einzelbitbefehle.
> Aber die kennt der Compiler auch und ersetzt einen Byte-Portzugriff
> durch diese Befehle, wenn das möglich ist (und du das in C so
> geschrieben hast)

Dachte ich mir schon, dass das der Compiler macht. Stand so im Buch und 
ich hatte auch erst mit acht Bit geschrieben (klappte ja auch), nur 
nachdem ich im Buch viel weniger Stellen sah, da hab ich im Datenblatt 
gelesen, dass es ein sechs Bit Port ist und war mir nicht mehr ganz 
sicher, ob es mit acht Bit nicht zu Fehlern kommen könnte.
Dann fülle ich die fehlenden Bits für den Port mit Nullen auf?
Ist das richtig?
Hab bis jetzt nur Arduino gemacht und gerade erst mi C begonnen.

von Karl H. (kbuchegg)


Lesenswert?

Frank O. schrieb:

> nachdem ich im Buch viel weniger Stellen sah, da hab ich im Datenblatt
> gelesen, dass es ein sechs Bit Port ist

Der Port ist intern genauso 8 Bit breit.
Nur kannst du eben 2 Bits so nicht benutzen, weil sie nicht auf Pins 
herausgeführt sind.
Das ist alles.

von F. F. (foldi)


Lesenswert?

Karl Heinz Buchegger schrieb:
> Frank O. schrieb:
>
>> nachdem ich im Buch viel weniger Stellen sah, da hab ich im Datenblatt
>> gelesen, dass es ein sechs Bit Port ist
>
> Der Port ist intern genauso 8 Bit breit.
> Nur kannst du eben 2 Bits so nicht benutzen, weil sie nicht auf Pins
> herausgeführt sind.
> Das ist alles.

Karl-Heinz,

schreib doch mal ein Buch! Ich bin der erste Käufer!!!!

Du kannst das alles immer so klasse erklären. Gut, dass wir dich 
haben!:-)

Danke!

von Karl H. (kbuchegg)


Lesenswert?

Noch weiter zur Syntax.

Den bisher war das ja nur die halbe Miete.
Denn in einem realen Programm werde ich nicht schreiben
1
   PORTB |= ( 1 << PB4 );

um den Portpin PB4 auf 1 zu setzen.

In einem realen Projekt hängt an diesem Pin eine Hardware. Zb eine LED. 
Zb. eine rote LED.
Also definiere ich mir dafür ein #define
1
#define LED_ROT  PB4

und verwende das entsprechend
1
#define LED_ROT  PB4
2
...
3
4
5
  PORTB |= ( 1 << LED_ROT );

und holla, jetzt kann ich ganz plötzlich im Code ablesen, was da 
eigentlich an dieser Stelle passiert! In der Anweisung ist da jetzt 
enthalten, dass da etwas mit der roten Led passiert. Und nicht mit der 
gelben!
Dass diese rote LED am Pin PB4 hängt, ist zwar nett, aber es ist nichts 
was mich beim Ein- Ausschalten der Led großartig interessieren müsste
1
#define F_CPU 1000000
2
3
#include <avr/io.h>
4
#include <utils/delay.h>
5
6
7
#define LED_ROT   PB4
8
9
10
int main()
11
{
12
  DDRB |= ( 1 << LED_ROT );
13
14
  while( 1 )
15
  {
16
    PORTB |= ( 1 << LED_ROT );
17
    _delay_ms( 500 );
18
19
    PORTB &= ~( 1 << LED_ROT );
20
    _delay_ms( 500 );
21
  }
22
}

Innerhalb des Programm-Codes arbeite ich nur noch mit der 'roten LED' 
und nciht mehr mit PB4. Muss ich die LED von PB4 auf PB2 umlöten, dann 
ändere ich genau 1 Stelle
1
#define LED_ROT   PB2
und die restliche Programmanpassung erledigt mein C-Compiler.


(Ein #define macht einfach nur eine Textersetzung. Dort wo ich LED_ROT 
geschrieben habe, wird dieser Text durch den Präprozessor dann durch PB4 
ausgetauscht. D.h. Obwohl ich
1
      PORTB |= ( 1 << LED_ROT );
geschrieben habe, kriegt der Compiler nach der Textersetzung wieder
1
      PORTB |= ( 1 << PB4 );
zu sehen. Dadurch ist beides gleichwertig. Aber der Gewinn an 
Dokumenation, den ich durch LED_ROT habe (oder LED_ERROR oder SUMMER 
oder ...) hebt das Programm von der Verstehbarkeit her gleich in eine 
ganz neue Kategorie.

von F. F. (foldi)


Lesenswert?

Karl Heinz Buchegger schrieb:

> Innerhalb des Programm-Codes arbeite ich nur noch mit der 'roten LED'
> und nciht mehr mit PB4. Muss ich die LED von PB4 auf PB2 umlöten, dann
> ändere ich genau 1 Stelle
>
1
> #define LED_ROT   PB2
2
>
> und die restliche Programmanpassung erledigt mein C-Compiler.

Das wäre schon mal ein (ganz kurzes) prägnantes Kapitel für dein neues 
Buch.

Das hab ich in Arduino dann ja auch schon so gemacht.

Hier wollte ich mich erstmal an die Schreibweise des Buches halten und 
auch so allmählich kapieren wie das in dem µC so richtig funktioniert.
Das war bei Arduino nicht erfoderlich.
Datenblatt liegt neben mir und ich hatte gestern schon mal von vorne 
angefangen zu lesen.
Ich mache immer alles so crossover; mal hier ein wenig löten, gestern 
den LA zum laufen gebracht, wieder etwas programmieren. Halt wie es 
meine Zeit erlaubt. Außerdem lese ich ununterbrochen hier im 
Mikrocontrollernet mit, damit ich dadurch vieles lerne.
Aber nun will ich nicht länger den Thread kapern, da sich das nicht 
gehört.

Wirklich Karl-Heinz, schreib ein Buch! Du wirst viele Anfänger glücklich 
machen.

von Michael Becker (Gast)


Lesenswert?

Hallo Karl-Heinz,

vielen Dank für die sehr verständlichen Erläuterungen - langsam fällt 
der Groschen bei mir, hoffe ich...

Mit der "kompakten" Schreibweise tue ich mir noch etwas schwer:

<c>    PORTB |= ( 1 << LED_ROT );
    _delay_ms( 500 );

    PORTB &= ~( 1 << LED_ROT );
    _delay_ms( 500 );</c>

In der ersten Zeile wird das LED_ROT Bit auf 1 gesetzt und der Rest des 
Ports bleibt unberührt, da vor dem "=" dieser Strich "|" steht, oder? 
Wenn ich PORTB = ( 1 << LED_ROT ); schreiben würde, würden allen anderen 
Bits außer LED_ROT gelöscht, oder?

In der dritten Zeile wird das LED_ROT Bit auf 0 gesetzt, vermute ich - 
aber verstehe nicht genau, warum...
--> (1 << LED_ROT) schiebt ein gesetztes Bit an die richtige Stelle
--> ~(..) --> was macht das? Aus 1 eine 0?
--> &= macht dann eine UND-Verknüpfung, damit der Rest des Ports 
unverändert bleibt?

Ich weiß, dass diese Fragen in verschiedenen Tutorials etc. sicherlich 
ausführlich erklärt werden. Habe auch die von Dir genannten Tutorials 
(teilweise) gelesen, aber als Anfänger ist es oft schwierig, die 
"theoretischen" Erklärungen auf den konkreten Anwendungsfall zu 
übertragen...

Bei meinem kleinen Projekt "Alarmanlage", die per Tastendruck ein- und 
ausgeschaltet werden soll, habe ich noch ein (hoffentlich letztes) 
Problem. Ich habe mir die Anleitung zur Entprellung angeschaut 
(http://www.mikrocontroller.net/articles/Entprellung) und in mein 
Programm eingebaut.

entprellung( &PINB, (1<<PINB4) ); // da hängt der Taster dran

if( PINB & (1<<PINB4) ) // falls Taste gedrückt, Alarm an oder aus 
(switch)
{if(alarmOnOff == 1) alarmOnOff = 0; else alarmOnOff=1;}

if (alarmOnOff == 1)
// LEDs und Speaker an
else
// alles aus

void entprellung( volatile uint8_t *port, uint8_t maske ) {
  uint8_t   port_puffer;
  uint8_t   entprellungs_puffer;

  for( entprellungs_puffer=0 ; entprellungs_puffer!=0xff ; ) {
    entprellungs_puffer<<=1;
    port_puffer = *port;
    _delay_us(150);
    if( (*port & maske) == (port_puffer & maske) )
    entprellungs_puffer |= 0x01;
  }
}

Der Alarm soll über ersten Tastendruck eingeschaltet werden, über 
nächsten Tastendruck wieder aus usw. (also immer abwechselnd). Leider 
ist bei o. g. Code das Verhalten ziemlich unberechenbar (manchmal 
passiert gar nix bei Tastendruck, dann geht der Alarm eine Zeitlang an 
und ohne Tastendruck wieder aus etc.)

Hast Du eine Idee? Vermutlich ja :-))

Freue mich echt sehr über die Hilfsbereitschaft und Geduld hier im 
Forum. Natürlich probier ich auch selbst immer wieder neue Korrekturen, 
aber komme teilweise nicht weiter (nur damit nicht der Eindruck 
entsteht, ich wäre zu "faul" zum selber lesen und probieren ;-)

VG

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.