## Writing a variable value to a variable position in a port register

8 posts / 0 new
Author
Message

Let's say I read a certain bit in PIND and assign it to variable val

`uint8_t val = (PIND & (1<<PD7));`

Suppose I want to take the bit I just read and write it to PB5 (or to a dynamic bit in PORTB specified by a variable). One way to do it is:

```  if (val) PORTB |= 1<<PB5;
else PORTB &= ~(1<<PB5);
```

I've tested it, and it works. There seems to be another way to do it:

`PORTB = (PORTB & (~(1<<PB5))) | ((val>>PD7)<<PB5);`

It works on paper, but something goes wrong when I actually run this code. I suspect I'm missing something trivial, but I just can't figure it out. So my question is twofold: what is wrong with this code, and whether it even makes sense to do it any other way than through the if statement.

This topic has a solution.
Last Edited: Tue. Sep 12, 2017 - 11:45 PM
This reply has been marked as the solution.

Both methods should work. The first approach will probably be smaller and quicker. It is more intuitive.
.
Compilers are pretty clever. GCC might see through your obfuscation in method #2.
Method #2 is effectively shifting val two places to the right i.e. 7-5
.
Compare the generated ASM code. Or just step through the disassembled code in the Simulator,
.
David.

Last Edited: Tue. Sep 12, 2017 - 06:55 AM

whether it even makes sense to do it any other way than through the if statement.

No, probably not.  Multibit shifts (val>>PD7) are expensive on an AVR (although the compiler might do something clever...)

```int main() {
uint8_t val = (PIND & (1<<PD7));
0:	80 b3       	in	r24, 0x10	; 16
2:	80 78       	andi	r24, 0x80	; 128

if (val) PORTB |= 1<<PB5;
4:	01 f0       	breq	.+0      	; 0x6 <main+0x6>
6:	c5 9a       	sbi	0x18, 5	; 24
8:	00 c0       	rjmp	.+0      	; 0xa <main+0xa>
else PORTB &= ~(1<<PB5);
a:	c5 98       	cbi	0x18, 5	; 24

PORTB = (PORTB & (~(1<<PB5))) | ((val>>PD7)<<PB5);
c:	98 b3       	in	r25, 0x18	; 24
e:	88 1f       	adc	r24, r24
10:	88 27       	eor	r24, r24
12:	88 1f       	adc	r24, r24
14:	82 95       	swap	r24
16:	88 0f       	add	r24, r24
18:	80 7e       	andi	r24, 0xE0	; 224
1a:	9f 7d       	andi	r25, 0xDF	; 223
1c:	89 2b       	or	r24, r25
1e:	88 bb       	out	0x18, r24	; 24
}```

```#include <avr/io.h>

typedef struct {
int b0:1;
int b1:1;
int b2:1;
int b3:1;
int b4:1;
int b5:1;
int b6:1;
int b7:1;
} bits_t;

#define PortB (*((volatile bits_t *)&PORTB))
#define DdrB (*((volatile bits_t *)&DDRB))
#define PinD (*((volatile bits_t *)&PIND))

int main(void) {
DdrB.b5 = 1;
while (1) {
PortB.b5 = PinD.b7;
}
}```

Gives:

```    DdrB.b5 = 1;
6c:   bd 9a           sbi     0x17, 5 ; 23
while (1) {
PortB.b5 = PinD.b7;
6e:   80 b3           in      r24, 0x10       ; 16
70:   88 0f           add     r24, r24
72:   88 0b           sbc     r24, r24
74:   98 b3           in      r25, 0x18       ; 24
76:   80 fb           bst     r24, 0
78:   95 f9           bld     r25, 5
7a:   98 bb           out     0x18, r25       ; 24
7c:   f8 cf           rjmp    .-16            ; 0x6e <main+0x2>```

Bill's result shows 4/5 cycles execution.   10 byte of Flash.

```  if (val) PORTB |= 1<<PB5;
4:	01 f0       	breq	.+0      	; 0x6 <main+0x6>
6:	c5 9a       	sbi	0x18, 5	; 24
8:	00 c0       	rjmp	.+0      	; 0xa <main+0xa>
else PORTB &= ~(1<<PB5);
a:	c5 98       	cbi	0x18, 5	; 24
```

Cliff's method is 7 cycles and uses 14 bytes of Flash.

```        PortB.b5 = PinD.b7;
6e:   80 b3           in      r24, 0x10       ; 16
70:   88 0f           add     r24, r24
72:   88 0b           sbc     r24, r24
74:   98 b3           in      r25, 0x18       ; 24
76:   80 fb           bst     r24, 0
78:   95 f9           bld     r25, 5
7a:   98 bb           out     0x18, r25       ; 24
```

As far as I can see,  you could:

```   in r24, 0x10    //PIND
bst r24, 7
in r24, 0x18    //PORTB
bld r24, 5
out 0x18, r24   //PORTB
```

which is 5 cycles and 10 bytes.   So actually,   Bill's method is marginally faster.   In practice you might be happier with the constant execution time.

Quite honestly,  you might just as well choose the statement(s) that seems most intuitive.   Let the Compiler do the business.

David.

Mine wasn't so much about the size/speed but the clarity of the code ;-)

`        PortB.b5 = PinD.b7;`

That's pretty self explanatory.

+1

Clarity of code is more important than anything else.   Let the Compiler do the work.   It does a pretty good job.

Yes,  I like the "bitfield" syntax.   And GCC produces efficient code.

But the if-else sequence is intuitive too.

David.