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


von Thomas F. (thomas41587)


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


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)


Lesenswert?

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

von Dr. Sommer (Gast)


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)


Lesenswert?

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

von chris (Gast)


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


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)


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:

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

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.