Multiple Bit Manipulation within Single Macro

Go To Last Post
12 posts / 0 new
Author
Message
#1
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

I'd like to simplify code that I'm working on by using bit manipulation macros similar to:

 

#define BIT_MACRO       PORTD |= (1 << 5)

However, I'd like to combine multiple bit manipulations into a single macro:

 

#define MULTIBIT_MACRO       PORTD |= (1 << 5) && PORTB |= (1 << 2)

From there I'd like to use the Macro in a statement such as:

 

void Function(void)
{
    // Turn on multiple bits at once
    MULTIBIT_MACRO;
}

Is this possible?

 

Thanks,

Ben

This topic has a solution.
This reply has been marked as the solution. 
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Why would you put && in there? Why not simply:

#define MULTIBIT_MACRO       PORTD |= (1 << 5) ; PORTB |= (1 << 2)

In C ';' is the usual statement separator though I guess you could use ',' in this case too?

 

But why with a macro anyway? Why not a function:

static inline multibit(void) {
    PORTD |= (1 << 5);
    PORTB |= (1 << 2);
}

...


int main(void) {
    multibit();
}

The "call" in main() will almost certainly generate two inlined opcodes:
 

SBI PORTD, 5
SBI PORTB, 2

which is about as efficient as it gets.

 

By making the function "static inline" you are saying "I don't need a callable copy that might be called from another file - the only use is in this file and I'd like you to just put the code inline not do a CALL/RET to this". In fact this is the only occasion on which you can put such "code" into a header file. Of itself it will not generate any code but any places multibit() is "called" the two SBI's will be generated.

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

clawson wrote:
Why would you put && in there?

Confusing 'C' with the MS command line?

Top Tips:

  1. How to properly post source code - see: https://www.avrfreaks.net/comment... - also how to properly include images/pictures
  2. "Garbage" characters on a serial terminal are (almost?) invariably due to wrong baud rate - see: https://learn.sparkfun.com/tutorials/serial-communication
  3. Wrong baud rate is usually due to not running at the speed you thought; check by blinking a LED to see if you get the speed you expected
  4. Difference between a crystal, and a crystal oscillatorhttps://www.avrfreaks.net/comment...
  5. When your question is resolved, mark the solution: https://www.avrfreaks.net/comment...
  6. Beginner's "Getting Started" tips: https://www.avrfreaks.net/comment...
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

zipfactor wrote:
I'd like to combine multiple bit manipulations into a single macro:

 

#define MULTIBIT_MACRO       PORTD |= (1 << 5) && PORTB |= (1 << 2)

So not just multiple bits - but multiple bits in multiple ports!?

Top Tips:

  1. How to properly post source code - see: https://www.avrfreaks.net/comment... - also how to properly include images/pictures
  2. "Garbage" characters on a serial terminal are (almost?) invariably due to wrong baud rate - see: https://learn.sparkfun.com/tutorials/serial-communication
  3. Wrong baud rate is usually due to not running at the speed you thought; check by blinking a LED to see if you get the speed you expected
  4. Difference between a crystal, and a crystal oscillatorhttps://www.avrfreaks.net/comment...
  5. When your question is resolved, mark the solution: https://www.avrfreaks.net/comment...
  6. Beginner's "Getting Started" tips: https://www.avrfreaks.net/comment...
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Ah, excellent suggestion clawson.

 

One question, however:  Would the macro be less overhead intensive versus using a static inline function, as there is still a function call? 

 

As you can tell I'm very rusty.  It seems when I get a chance to get into electronics projects, I get pulled away from them again for a long bit.

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 1

You can make macros as complicated as you like.    Multi-line macros are fiddly to type.    The backslash must be the last character on a line.

 

I try to put multiple statements on a single line with braces.

 

With any macro,   you must write very carefully.    Extra parentheses can keep expansions 'safe'.

 

As Cliff has suggested.    The GCC inline function behaves almost like a macro but with type checking.    And you don't get the backslash problem.

OTOH,   inline functions are not handled as well by all Compilers.    Sometimes a macro does a better job.

 

Oh,   your use of && looks misplaced.    It does have a use in logical expressions.

 

David.

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Thanks all for the input, quick responses and to the point.
 

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

One question, however:  Would the macro be less overhead intensive versus using a static inline function, as there is still a function call? 

Proof if proof were needed:

$ cat avr.c
#include <avr/io.h>

static inline multibit(void) {
    PORTD |= (1 << 5);
    PORTB |= (1 << 2);
}

int main(void) {
    multibit();
}
$ avr-gcc -g -Os -mmcu=atmega16 avr.c -o avr.elf
$ avr-objdump -S avr.elf

avr.elf:     file format elf32-avr


Disassembly of section .text:

00000000 <__vectors>:
   0:	0c 94 2a 00 	jmp	0x54	; 0x54 <__ctors_end>
   4:	0c 94 34 00 	jmp	0x68	; 0x68 <__bad_interrupt>
   8:	0c 94 34 00 	jmp	0x68	; 0x68 <__bad_interrupt>
   c:	0c 94 34 00 	jmp	0x68	; 0x68 <__bad_interrupt>
  10:	0c 94 34 00 	jmp	0x68	; 0x68 <__bad_interrupt>
  14:	0c 94 34 00 	jmp	0x68	; 0x68 <__bad_interrupt>
  18:	0c 94 34 00 	jmp	0x68	; 0x68 <__bad_interrupt>
  1c:	0c 94 34 00 	jmp	0x68	; 0x68 <__bad_interrupt>
  20:	0c 94 34 00 	jmp	0x68	; 0x68 <__bad_interrupt>
  24:	0c 94 34 00 	jmp	0x68	; 0x68 <__bad_interrupt>
  28:	0c 94 34 00 	jmp	0x68	; 0x68 <__bad_interrupt>
  2c:	0c 94 34 00 	jmp	0x68	; 0x68 <__bad_interrupt>
  30:	0c 94 34 00 	jmp	0x68	; 0x68 <__bad_interrupt>
  34:	0c 94 34 00 	jmp	0x68	; 0x68 <__bad_interrupt>
  38:	0c 94 34 00 	jmp	0x68	; 0x68 <__bad_interrupt>
  3c:	0c 94 34 00 	jmp	0x68	; 0x68 <__bad_interrupt>
  40:	0c 94 34 00 	jmp	0x68	; 0x68 <__bad_interrupt>
  44:	0c 94 34 00 	jmp	0x68	; 0x68 <__bad_interrupt>
  48:	0c 94 34 00 	jmp	0x68	; 0x68 <__bad_interrupt>
  4c:	0c 94 34 00 	jmp	0x68	; 0x68 <__bad_interrupt>
  50:	0c 94 34 00 	jmp	0x68	; 0x68 <__bad_interrupt>

00000054 <__ctors_end>:
  54:	11 24       	eor	r1, r1
  56:	1f be       	out	0x3f, r1	; 63
  58:	cf e5       	ldi	r28, 0x5F	; 95
  5a:	d4 e0       	ldi	r29, 0x04	; 4
  5c:	de bf       	out	0x3e, r29	; 62
  5e:	cd bf       	out	0x3d, r28	; 61
  60:	0e 94 36 00 	call	0x6c	; 0x6c <main>
  64:	0c 94 39 00 	jmp	0x72	; 0x72 <_exit>

00000068 <__bad_interrupt>:
  68:	0c 94 00 00 	jmp	0	; 0x0 <__vectors>

0000006c <main>:
#include <avr/io.h>

static inline multibit(void) {
    PORTD |= (1 << 5);
  6c:	95 9a       	sbi	0x12, 5	; 18
    PORTB |= (1 << 2);
  6e:	c2 9a       	sbi	0x18, 2	; 24
}

int main(void) {
    multibit();
}
  70:	08 95       	ret

00000072 <_exit>:
  72:	f8 94       	cli

00000074 <__stop_program>:
  74:	ff cf       	rjmp	.-2      	; 0x74 <__stop_program>
$

As you can see (as I confidently predicted) main() consists of two SBIs and a RET and that is all. There is no "overhead" here.

Last Edited: Fri. Mar 20, 2015 - 04:29 PM
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

clawson wrote:

One question, however:  Would the macro be less overhead intensive versus using a static inline function, as there is still a function call? 

Proof if proof were needed:

$ cat avr.c
#include <avr/io.h>

static inline multibit(void) {
    PORTD |= (1 << 5);
    PORTB |= (1 << 2);
}

int main(void) {
    multibit();
}
$ avr-gcc -g -Os -mmcu=atmega16 avr.c -o avr.elf
$ avr-objdump -S avr.elf

avr.elf:     file format elf32-avr


Disassembly of section .text:

00000000 <__vectors>:
   0:	0c 94 2a 00 	jmp	0x54	; 0x54 <__ctors_end>
   4:	0c 94 34 00 	jmp	0x68	; 0x68 <__bad_interrupt>
   8:	0c 94 34 00 	jmp	0x68	; 0x68 <__bad_interrupt>
   c:	0c 94 34 00 	jmp	0x68	; 0x68 <__bad_interrupt>
  10:	0c 94 34 00 	jmp	0x68	; 0x68 <__bad_interrupt>
  14:	0c 94 34 00 	jmp	0x68	; 0x68 <__bad_interrupt>
  18:	0c 94 34 00 	jmp	0x68	; 0x68 <__bad_interrupt>
  1c:	0c 94 34 00 	jmp	0x68	; 0x68 <__bad_interrupt>
  20:	0c 94 34 00 	jmp	0x68	; 0x68 <__bad_interrupt>
  24:	0c 94 34 00 	jmp	0x68	; 0x68 <__bad_interrupt>
  28:	0c 94 34 00 	jmp	0x68	; 0x68 <__bad_interrupt>
  2c:	0c 94 34 00 	jmp	0x68	; 0x68 <__bad_interrupt>
  30:	0c 94 34 00 	jmp	0x68	; 0x68 <__bad_interrupt>
  34:	0c 94 34 00 	jmp	0x68	; 0x68 <__bad_interrupt>
  38:	0c 94 34 00 	jmp	0x68	; 0x68 <__bad_interrupt>
  3c:	0c 94 34 00 	jmp	0x68	; 0x68 <__bad_interrupt>
  40:	0c 94 34 00 	jmp	0x68	; 0x68 <__bad_interrupt>
  44:	0c 94 34 00 	jmp	0x68	; 0x68 <__bad_interrupt>
  48:	0c 94 34 00 	jmp	0x68	; 0x68 <__bad_interrupt>
  4c:	0c 94 34 00 	jmp	0x68	; 0x68 <__bad_interrupt>
  50:	0c 94 34 00 	jmp	0x68	; 0x68 <__bad_interrupt>

00000054 <__ctors_end>:
  54:	11 24       	eor	r1, r1
  56:	1f be       	out	0x3f, r1	; 63
  58:	cf e5       	ldi	r28, 0x5F	; 95
  5a:	d4 e0       	ldi	r29, 0x04	; 4
  5c:	de bf       	out	0x3e, r29	; 62
  5e:	cd bf       	out	0x3d, r28	; 61
  60:	0e 94 36 00 	call	0x6c	; 0x6c <main>
  64:	0c 94 39 00 	jmp	0x72	; 0x72 <_exit>

00000068 <__bad_interrupt>:
  68:	0c 94 00 00 	jmp	0	; 0x0 <__vectors>

0000006c <main>:
#include <avr/io.h>

static inline multibit(void) {
    PORTD |= (1 << 5);
  6c:	95 9a       	sbi	0x12, 5	; 18
    PORTB |= (1 << 2);
  6e:	c2 9a       	sbi	0x18, 2	; 24
}

int main(void) {
    multibit();
}
  70:	08 95       	ret

00000072 <_exit>:
  72:	f8 94       	cli

00000074 <__stop_program>:
  74:	ff cf       	rjmp	.-2      	; 0x74 <__stop_program>
$

As you can see (as I confidently predicted) main() consists of two SBIs and a RET and that is all. There is no "overhead" here.

 

Duly noted.  Thanks for sharing this.  Calling a function instead of a macro is better form in this case it seems, anyhow.

Last Edited: Fri. Mar 20, 2015 - 05:51 PM
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Calling a function instead of a macro is better form in this case it seems, anyhow.

Why would you say that?

Regards,
Steve A.

The Board helps those that help themselves.

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Koshchi wrote:

Calling a function instead of a macro is better form in this case it seems, anyhow.

Why would you say that?

For one thing, a lot of people find the semantics of function call a lot less interesting than that of macro use.

For one thing, with function call one can see the number of times each argument is evaluated.

Iluvatar is the better part of Valar.

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Actually, I read that wrong. I thought that the OP was saying that a macro was better form.

Regards,
Steve A.

The Board helps those that help themselves.