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!
Guck dir am besten einfach mal an was PB1 ist. Hint: Es ist 1 d.h. 1<<PB1 == 1<<1 == 2
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.
Wow, so einfach :D vielen Dank für die schnelle Hilfe, es funktioniert :)
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 | }
|
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?
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.
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
Mit Google-Account einloggen
Noch kein Account? Hier anmelden.