Forum: Mikrocontroller und Digitale Elektronik Atmega8 - Pin Nummer dynamisch?


Announcement: there is an English version of this forum on EmbDev.net. Posts you create there will be displayed on Mikrocontroller.net and EmbDev.net.
von Thomas F. (thomas41587)


Bewertung
-1 lesenswert
nicht lesenswert
Hallo zusammen,

in einer Funktion bräuchte ich die Möglichkeit, Pin-Nummern dynamisch zu 
adressieren. Also anstatt z.B.: "PORTB |= (1<<PB1)" -> "PORTB |= 
(1<<PB{variable})". Ich habe versucht dieses "Problem" zu googlen aber 
leider findet sich einfach nichts...

Vielen Dank jetzt schon für eure Hilfe!

von Christopher B. (chrimbo) Benutzerseite


Bewertung
0 lesenswert
nicht lesenswert
Guck dir am besten einfach mal an was PB1 ist.
Hint: Es ist 1

d.h. 1<<PB1 == 1<<1 == 2

von Jim M. (turboj)


Bewertung
0 lesenswert
nicht lesenswert
Trivialst:
1
uint8_t pin_nr = 0;
2
3
PORTB |= (1<<pin_nr);

von Dr. Sommer (Gast)


Bewertung
0 lesenswert
nicht lesenswert
PB1 ist einfach nur definiert als 1, PB7 ist 7 usw. Du kannst also 
einfach
1
PORTB |= (1 << var);
machen. Achtung dass das ggf. langsam ist (var*3 Takte oder so) weil der 
AVR keinen Barrelshifter hat.

von Thomas F. (thomas41587)


Bewertung
0 lesenswert
nicht lesenswert
Wow, so einfach :D vielen Dank für die schnelle Hilfe, es funktioniert 
:)

von chris (Gast)


Bewertung
-1 lesenswert
nicht lesenswert
Der Vorgang ist dann vermutlich auch nicht atomar, da der Compiler 
daraus wohl kein sbi bzw. cbi machen kann.
Also aufpassen wenn du in Interrupts Operationen auf dem selben PORT 
durchführst.

Das könnte man jedoch durch eine Funktion lösen:
1
void portb_set_pin(uint8_t pin)
2
{
3
  switch (pin)
4
  {
5
  case 0:
6
  PORTB |= (1<<0);
7
    break;
8
  case 1:
9
  PORTB |= (1<<1);
10
    break;
11
  case 2:
12
  PORTB |= (1<<2);
13
    break;
14
  case 3:
15
  PORTB |= (1<<3);
16
    break;
17
  case 4:
18
  PORTB |= (1<<4);
19
    break;
20
  case 5:
21
  PORTB |= (1<<5);
22
    break;
23
  case 6:
24
  PORTB |= (1<<6);
25
    break;
26
  case 7:
27
  PORTB |= (1<<7);
28
    break;
29
  default:
30
    break;
31
  }
32
}

von Frank M. (ukw) (Moderator) Benutzerseite


Bewertung
0 lesenswert
nicht lesenswert
chris schrieb:
> Der Vorgang ist dann vermutlich auch nicht atomar, da der Compiler
> daraus wohl kein sbi bzw. cbi machen kann.
> Also aufpassen wenn du in Interrupts Operationen auf dem selben PORT
> durchführst.
>
> Das könnte man jedoch durch eine Funktion lösen:

Ach, durch die Funktion wird die Operation atomar?

von chris (Gast)


Bewertung
0 lesenswert
nicht lesenswert
Frank M. schrieb:
> Ach, durch die Funktion wird die Operation atomar?

Der eigentliche Zugriff auf das PORT-Register sollte m.M. nach dadurch 
atomar sein, da in der Funktion dann lauter sbi-Befehle stehen.
Benutzt man aber
1
 PORTB |= (1<<x);
dann wird das wohl ein read-modify-write.
Und zwischen modify und write kann dann ein Interrupt zuschlagen und den 
PORT modifizieren, so dass es Probleme gibt. (Das write macht die vom 
Interrupt vorgenommene Veränderung sofort wieder rückgängig, indem es 
den gesamten PORT beschreibt.)

Allerdings gebe ich zu, dass ich gerade keine Zeit habe, meine Theorie 
anhand des asm-Codes zu überprüfen. Möglicherweise irre ich mich also.
Vielleicht schafft der Compiler es ja auch, aus einer variablen 
Pinnummer einen sbi-Befehl zu machen.

von chris (Gast)


Angehängte Dateien:

Bewertung
1 lesenswert
nicht lesenswert
So, ich habe es mal ausprobiert (gcc 4.7.2, Optimierung -Os)
Quellcode siehe Anhang.

Mit der oben von mir geposteten Funktion wird wie erwartet für jeden Pin 
ein sbi erzeugt:
1
void portb_set_pin(uint8_t pin)
2
{
3
  switch (pin)
4
  66:  e8 2f         mov  r30, r24
5
  68:  f0 e0         ldi  r31, 0x00  ; 0
6
  6a:  e8 30         cpi  r30, 0x08  ; 8
7
  6c:  f1 05         cpc  r31, r1
8
  6e:  90 f4         brcc  .+36       ; 0x94 <portb_set_pin+0x2e>
9
  70:  e6 5e         subi  r30, 0xE6  ; 230
10
  72:  ff 4f         sbci  r31, 0xFF  ; 255
11
  74:  09 94         ijmp
12
  {
13
    case 0:
14
    PORTB |= (1<<0);
15
  76:  28 9a         sbi  0x05, 0  ; 5
16
    break;
17
  78:  08 95         ret
18
    case 1:
19
    PORTB |= (1<<1);
20
  7a:  29 9a         sbi  0x05, 1  ; 5
21
    break;
22
  7c:  08 95         ret
23
    case 2:
24
    PORTB |= (1<<2);
25
  7e:  2a 9a         sbi  0x05, 2  ; 5
26
    break;
27
  80:  08 95         ret
28
    case 3:
29
    PORTB |= (1<<3);
30
  82:  2b 9a         sbi  0x05, 3  ; 5
31
    break;
32
  84:  08 95         ret
33
    case 4:
34
    PORTB |= (1<<4);
35
  86:  2c 9a         sbi  0x05, 4  ; 5
36
    break;
37
  88:  08 95         ret
38
    case 5:
39
    PORTB |= (1<<5);
40
  8a:  2d 9a         sbi  0x05, 5  ; 5
41
    break;
42
  8c:  08 95         ret
43
    case 6:
44
    PORTB |= (1<<6);
45
  8e:  2e 9a         sbi  0x05, 6  ; 5
46
    break;
47
  90:  08 95         ret
48
    case 7:
49
    PORTB |= (1<<7);
50
  92:  2f 9a         sbi  0x05, 7  ; 5
51
  94:  08 95         ret


Mit dem variablen Shift wird dagegen wild durch die Gegend gerechnet:
1
      PORTB |= (1<<pin_nr);
2
  6c:  45 b1         in  r20, 0x05  ; 5
3
  6e:  9c 01         movw  r18, r24
4
  70:  00 90 00 01   lds  r0, 0x0100
5
  74:  02 c0         rjmp  .+4        ; 0x7a <main+0x24>
6
  76:  22 0f         add  r18, r18
7
  78:  33 1f         adc  r19, r19
8
  7a:  0a 94         dec  r0
9
  7c:  e2 f7         brpl  .-8        ; 0x76 <main+0x20>
10
  7e:  24 2b         or  r18, r20
11
  80:  25 b9         out  0x05, r18  ; 5
12
  82:  eb cf         rjmp  .-42       ; 0x5a <main+0x4>


Flashverbrauch ist 202 byte (Funktion) vs. 174 byte (variabler Shift)

Ohne mich mit Assembler genauer auszukennen, vermute ich dass die 
Funktionsvariante wohl schneller ist als der variable Shift.
Und eben zusätzlich atomar.

lg
Chris

Antwort schreiben

Die Angabe einer E-Mail-Adresse ist freiwillig. Wenn Sie automatisch per E-Mail über Antworten auf Ihren Beitrag informiert werden möchten, melden Sie sich bitte an.

Wichtige Regeln - erst lesen, dann posten!

  • Groß- und Kleinschreibung verwenden
  • Längeren Sourcecode nicht im Text einfügen, sondern als Dateianhang

Formatierung (mehr Informationen...)

  • [c]C-Code[/c]
  • [code]Code in anderen Sprachen, ASCII-Zeichnungen[/code]
  • [math]Formel in LaTeX-Syntax[/math]
  • [[Titel]] - Link zu Artikel
  • Verweis auf anderen Beitrag einfügen: Rechtsklick auf Beitragstitel,
    "Adresse kopieren", und in den Text einfügen




Bild automatisch verkleinern, falls nötig
Bitte das JPG-Format nur für Fotos und Scans verwenden!
Zeichnungen und Screenshots im PNG- oder
GIF-Format hochladen. Siehe Bildformate.
Hinweis: der ursprüngliche Beitrag ist mehr als 6 Monate alt.
Bitte hier nur auf die ursprüngliche Frage antworten,
für neue Fragen einen neuen Beitrag erstellen.

Mit dem Abschicken bestätigst du, die Nutzungsbedingungen anzuerkennen.