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.

Moderation in all things. -- ancient proverb

  • 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.