selecting a bit position using a variable

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

Hello:

 

I'm doing some simple status bit manipulation, no problem.  However, occasionally I need to use a variable to select which bit to change.  I was wondering if there was something more compiler friendly than calculating a power of 2 (which of course is rather easy).

 


void example (void){
 uint8_t Valve_channels_selected=0; //initialize...these are not to have anything to do with I/O pins at all
 
 Valve_channels_selected |= WATER|JUICE;   // indicate something ON
 Valve_channels_selected &= ~WATER;  // indicate something OFF
  // etc
 Valve_channels_selected=OLIVEOIL|LARD;
  // etc
  
  uint8_t index=3;  //set to 3 as an example
  
  Valve_channels_selected |=(2^index);  // turn on chan # specified by index (0-7)
  Valve_channels_selected &= ~(2^index);  //turn off chan # specified by index 0-7
  
}

 

 

When in the dark remember-the future looks brighter than ever.   I look forward to being able to predict the future!

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

If the compiler actually calculates (2^n), then (1<<n) would probably end up better.

 

In tight full AVR8 apps, I try to avoid such constructs whenever I can, as the AVR8 instruction set just does not lend itself to doing it well.  Extrapolating on your example of a series of possibilities in the mask, I'd spend more editing time creating WATER and JUICE and so forth.  And then perhaps test for each and OR into some kind of mask or the port pin directly.  YMMV.
 

You can put lipstick on a pig, but it is still a pig.

I've never met a pig I didn't like, as long as you have some salt and pepper.

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

2^index does not indicate exponentiation, it indicates bitwise exclusive or.

The usual expression is (1<<index).

C has no exponentiation operator.

Often, instead of computing a mask from an index,

one can just put the mask in the variable.

If one needs to compute one from the other,

index to mask is the way to go.

The reverse is harder.

"SCSI is NOT magic. There are *fundamental technical
reasons* why it is necessary to sacrifice a young
goat to your SCSI chain now and then." -- John Woods

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

2^index does not indicate exponentiation, it indicates bitwise exclusive or...Thanks--tis true , a mental lapse of mine, since I was overloading myself as to whether:

 

 (1<<index) could programmatically use a variable.  I'd previously only used this notation with fixed defines ex :(1<<ADC_READY).  I finally just typed it in and the GCC compiler didn't groan.

 

I'm not sure if this is more code efficient than simply declaring an array & using its indexing properties:

 

bit_basket[index)=0;

bit_basket[index)=1;

    ...to store 8 flags (bits) thus wastes 7 extra bytes of ram, but might be faster & smaller code

 

I suppose there is a way to use the GPIO register with the  (1<<index) approach.

 

 

 

When in the dark remember-the future looks brighter than ever.   I look forward to being able to predict the future!

Last Edited: Mon. Aug 17, 2015 - 09:33 PM
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Doing a shift takes a variable amount of time based on the number of shifts, whereas the table approach is constant. On an Arm, i'd use the shift as it has a barrel shifter so n shifts take 1 clock.

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

Just to note that if you really did want to use 2^n then in C that would be:

#include <math.h>

float f = pow(2, n);

but this is a Very Bad Idea(tm).

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

Kartman wrote:
an Arm ... has a barrel shifter

Really - all of them?

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

Really - all of them?

Turning that around - what model of ARM does not have a barrel shifter? It's fundamental to the core design isn't it?

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

clawson wrote:
It's fundamental to the core design isn't it?

So it is: http://infocenter.arm.com/help/i...

 

 

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

Am I the only one that finds that ARM infocenter a little bit annoying in that they will watermark pages as "superceded" with no link to the current version?

 

Eventually I found the latest version of that page that, as far as I can see, says the same...

 

http://infocenter.arm.com/help/i...

 

BTW is this thread drifting off-topic? OP never mentioned ARM, only Kartman did.

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

clawson wrote:
Am I the only one that finds that ARM infocenter a little bit annoying in that they will watermark pages as "superceded" with no link to the current version?

No you are not!!

 

angry

 

Strangely, I also spent a while digging around to find the latest version, and I thought that was the link I posted.

 

But now, when I click it, it goes to the  "superceded" page.

 

angry

 

In fact, even if I click on your link, that also goes to the  "superceded" page.

 

angry

 

 

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

Well, I'm glad we got that sorted. I feel validated now. All we need is someone to ask what a barrel shifter is.

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

All we need is someone to ask what a barrel shifter is.

You might hope that all micro engineers might already know what a barrel shifter is just as they know what an ALU and a half/full carry adder are. Sadly, though, it maybe seems that such detail is no longer taught to budding engineers these days :-(

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

If I understand OP correct, then he want a fast/small code that can select (either set/clr or check) an, at compile time unknown, bit.

So I guess what I will suggest are to try to make a way to make a mask with one bit set (0x01,0x02,0x04,0x08,0x10,0x20,0x40,0x80) and use that to & or | with.

An easy way are a lookup table, or make it with shift (small but slow code). It can be done in ASM in 7 clk if real speed are needed.(14byte) 

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

i asked that very question about 35 years ago - what a barrel shifter is. My friend was building one for his bitslice processor. I built the 32x32 bit multiplier. It did it in around 100ns which was bleeding edge for the day.

Now we are right off topic.

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

All we need is someone to ask what a barrel shifter is.

Stevedore.  You'd get a stevedore when you wanted to shift a barrel, wouldn't you?

 

 

 

But if I use the term "shift the barrel", then it would be a "docker" for y'all across the pond?

In the United Kingdom, men who load and unload ships are usually called dockers, in Australia wharfies, while in the United States and Canada the term longshoreman, derived from man-along-the-shore, is used.[

You can put lipstick on a pig, but it is still a pig.

I've never met a pig I didn't like, as long as you have some salt and pepper.

Last Edited: Tue. Aug 18, 2015 - 01:34 PM
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

If this thread goes any more off topic I'll be forced to tell my "Diesel Fitter" joke!

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

I'm sure that'll be a barrel o' laughs ...

 

laugh

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

The best is probably to avoid converting between index and mask at all:

for(uint8_t mask=1;  mask;  mask<<=1) { ... }
for(uint8_t mask=0, index=0;  mask;  ++index, mask<<=1) {  if index needed  }

For speed, table lookup is next.   Six cycles the first time.  In a loop, the next is probably only two cycles.

If one needs to do the conversion and can afford the SRAM, this is probably best.

Placing the table in flash would cost another cycle and require using the Z register.

That might defeat other optimizations.

Evaluating 1<<index would probably involve a 4-cycle loop.

With inline assembly, it could be done in 7 cycles.

 

When passing an index to a function that will also need the mask,

consider passing it the mask as well.

"SCSI is NOT magic. There are *fundamental technical
reasons* why it is necessary to sacrifice a young
goat to your SCSI chain now and then." -- John Woods

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

I don't think that we can count on than OP use them in order.

Other ways could be a union and as OP suggest an array of char and then store 0 or 1 (or just non zero).

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

 

 

I know (or recently learned) that you can define bit flags in a structure & use them up, so as not to use up too many sram bytes.  While that looks great on paper, I'm not sure if that generates small/fast code, or a half page of slow bloat.  Of course sram access is 3x-4x slower than keeping flags in , say, r18.

Apparently there may be some methods to use the few general purpose registers (GPIO1...), possibly resulting in efficient use of sbr /cbr (sbi?/cbi?) instructions for flags.  I just haven't seen a post that clearly explains the details.

When in the dark remember-the future looks brighter than ever.   I look forward to being able to predict the future!

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

I'm not sure if that generates small/fast code, ...

 Well, I guess I'd have to go back to your first post.  I personally don't see how (1<<n) comes into it, if the bits are WATER, LARD, JUICE, etc.  Please expound.

 

Similar with your last post above--give some examples on what you are trying to solve.

 

Indeed, there are certain instances where cycle count for repetitive operations may be of primary importance.  E.g. if one is generating/rendering a 128x128 image, 16384 pixels, then if the per-pixel loop iteration can be cut in half it may make a tremendous difference in the app.

 

Contrast with my industrial control apps, that may have the outputs scattered among ports.  So indeed I may build a logical mask during the app, and then apply it with an unrolled loop...

// **************************************************************************
// *	O U T P U T   C O N T R O L
// **************************************************************************
//
//	Active-high outputs

	if (valve_mask & VALVE_1)
		{
		OUT_1 = 1;
		}
	else
		{
		OUT_1 = 0;
		}

	if (valve_mask & VALVE_2)
		{
		OUT_2 = 1;
		}
	else
		{
		OUT_2 = 0;
		}
...

Will that be fully optimal?  Perhaps not, but close enough for repeating every few milliseconds.

 

                 ; 0000 033E 	if (valve_mask & VALVE_1)
0000e5 fe40      	SBRS R4,0
0000e6 c002      	RJMP _0x1D
                 ; 0000 033F 		{
                 ; 0000 0340 		OUT1_ON();
0000e7 9a41      	SBI  0x8,1
                 ; 0000 0341 		}
                 ; 0000 0342 	else
0000e8 c001      	RJMP _0x1E
                 _0x1D:
                 ; 0000 0343 		{
                 ; 0000 0344 		OUT1_OFF();
0000e9 9841      	CBI  0x8,1
                 ; 0000 0345 		}
                 _0x1E:

6 cycles per bit?  Something like that.  96 cycles for 16 bits. 80 words?  Indeed if in a tight Mega48 app I might explore further.

 

Of course sram access is 3x-4x slower than keeping flags in , say, r18.

You might note that the fragment above looked for the bit in R4.  That leads to toolchain choice.  I use CodeVision and valve_mask is a 16-bit "register" variable.  Valves 9-16 peek at R5.  Your toolchain may well detect that valve_mask is repeatedly accessed and it may be parked in a register for access.  Or you could make a function or a block with a copy in a local variable that is then register-based.

 

As always, "it depends".

 

You can put lipstick on a pig, but it is still a pig.

I've never met a pig I didn't like, as long as you have some salt and pepper.

Last Edited: Tue. Aug 18, 2015 - 07:24 PM
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0
#include <avr/io.h>

unsigned int		valve_mask;
	// Mask for the bit position in the command from the master
	#define	VALVE_1				0x0001
	#define	VALVE_2				0x0002
	#define	VALVE_3				0x0004
	#define	VALVE_4				0x0008

	#define	VALVE_5				0x0010
	#define	VALVE_6				0x0020
	#define	VALVE_7				0x0040
	#define	VALVE_8				0x0080

	#define	VALVE_9				0x0100
	#define	VALVE_10			0x0200
	#define	VALVE_11			0x0400
	#define	VALVE_12			0x0800

	#define	VALVE_13			0x1000
	#define	VALVE_14			0x2000
	#define	VALVE_15			0x4000
	#define	VALVE_16			0x8000

// Politically-correct macros that do bit-manipulation
#define	OUT1_PORT	PORTC
#define	OUT1_PIN	1
#define	OUT1_ON()	(OUT1_PORT |= (1<<OUT1_PIN))
#define	OUT1_OFF()	(OUT1_PORT &= ~(1<<OUT1_PIN))
#define	OUT2_PORT	PORTC
#define	OUT2_PIN	0
#define	OUT2_ON()	(OUT2_PORT |= (1<<OUT2_PIN))
#define	OUT2_OFF()	(OUT2_PORT &= ~(1<<OUT2_PIN))
#define	OUT3_PORT	PORTC
#define	OUT3_PIN	2
#define	OUT3_ON()	(OUT3_PORT |= (1<<OUT3_PIN))
#define	OUT3_OFF()	(OUT3_PORT &= ~(1<<OUT3_PIN))
#define	OUT4_PORT	PORTC
#define	OUT4_PIN	5
#define	OUT4_ON()	(OUT4_PORT |= (1<<OUT4_PIN))
#define	OUT4_OFF()	(OUT4_PORT &= ~(1<<OUT4_PIN))

int main(void) {
	// **************************************************************************
	// *	O U T P U T   C O N T R O L
	// **************************************************************************
	//
	//	Active-high outputs

	if (valve_mask & VALVE_1)
	{
		OUT1_ON();
	}
	else
	{
		OUT1_OFF();
	}

	if (valve_mask & VALVE_2)
	{
		OUT2_ON();
	}
	else
	{
		OUT2_OFF();
	}

	if (valve_mask & VALVE_3)
	{
		OUT3_ON();
	}
	else
	{
		OUT3_OFF();
	}

	if (valve_mask & VALVE_4)
	{
		OUT4_ON();
	}
	else
	{
		OUT4_OFF();
	}


}

 

Studio 6.2 default app, with valve_mask global variable -- it is loaded from SRAM each time even though not volatile:

	if (valve_mask & VALVE_1)
  8a:	80 91 00 01 	lds	r24, 0x0100
  8e:	80 ff       	sbrs	r24, 0
  90:	02 c0       	rjmp	.+4      	; 0x96 <main+0xc>
	{
		OUT1_ON();
  92:	41 9a       	sbi	0x08, 1	; 8
  94:	01 c0       	rjmp	.+2      	; 0x98 <main+0xe>
	}
	else
	{
		OUT1_OFF();
  96:	41 98       	cbi	0x08, 1	; 8
	}

	if (valve_mask & VALVE_2)
  98:	80 91 00 01 	lds	r24, 0x0100
  9c:	81 ff       	sbrs	r24, 1
...

But make it a local (and load with a volatile so that the optimizer won't throw out the whole program):

int main(void) {
	unsigned int		valve_mask;
	
	valve_mask = TCNT1;
  8a:	80 91 84 00 	lds	r24, 0x0084
  8e:	90 91 85 00 	lds	r25, 0x0085
	// *	O U T P U T   C O N T R O L
	// **************************************************************************
	//
	//	Active-high outputs

	if (valve_mask & VALVE_1)
  92:	80 ff       	sbrs	r24, 0
  94:	02 c0       	rjmp	.+4      	; 0x9a <main+0x10>
	{
		OUT1_ON();
  96:	41 9a       	sbi	0x08, 1	; 8
  98:	01 c0       	rjmp	.+2      	; 0x9c <main+0x12>
	}
	else
	{
		OUT1_OFF();
  9a:	41 98       	cbi	0x08, 1	; 8
	}

	if (valve_mask & VALVE_2)
  9c:	81 ff       	sbrs	r24, 1
  9e:	02 c0       	rjmp	.+4      	; 0xa4 <main+0x1a>
...

As AVR8 has no way to "copy" a bit with a value to an I/O register bit, any of the above sequences are a bit clumsy for non-contiguous, arbitrary port/pin assignments.  However, one can do the below, and GCC really really wants to generate the jumpity-jump:

 

	if (valve_mask & VALVE_1) OUT1_ON();
  92:	80 ff       	sbrs	r24, 0
  94:	02 c0       	rjmp	.+4      	; 0x9a <main+0x10>
  96:	41 9a       	sbi	0x08, 1	; 8
  98:	01 c0       	rjmp	.+2      	; 0x9c <main+0x12>
	if (!(valve_mask & VALVE_1)) OUT1_OFF();
  9a:	41 98       	cbi	0x08, 1	; 8

...whereas in CV I can get:

                 ; 0000 0037     if (valve_mask & VALVE_1) OUT1_ON();
                 ;	valve_mask -> R16,R17
00003d fd00      	SBRC R16,0
00003e 9a41      	SBI  0x8,1
                 ; 0000 0038     if ((valve_mask & VALVE_1)==0) OUT1_OFF();
00003f ff00      	SBRS R16,0
000040 9841      	CBI  0x8,1
                 ; 0000 0039 

... which I think is near optimal for AVR8 conditional set/clear of a low (SBI/CBI reachable) I/O register bit.  Lessee, 4 words and 5 cycles?

 

 

 

You can put lipstick on a pig, but it is still a pig.

I've never met a pig I didn't like, as long as you have some salt and pepper.

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

 Well, I guess I'd have to go back to your first post.  I personally don't see how (1<<n) comes into it, if the bits are WATER, LARD, JUICE, etc.  Please expound.

 

Generally I've use the define names/constants, but very recently I needed to use a variable to select the bit.  I was not aware that (1<<variable name) was legitimate (I think I tried it long ago with AVR asm), but it makes sense.

Aside from that, I was wondering how "efficiently" the code is generated & what methods were viable...You are lucky--I certainty hope GCC could/would put status flags somewhere in the r00-r31 registers (especially in the upper ones).  There has been some mention of getting GCC to use the extra GPIO registers, but I'm just delving into that.    My code works fine as-is, but I'm always on the lookout for new incarnations.

When in the dark remember-the future looks brighter than ever.   I look forward to being able to predict the future!

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

Aside from that, I was wondering how "efficiently" the code is generated & what methods were viable...

Why don't you just try some test programs, as I did in a few minutes, with two toolchains, and a few different variations on mask placement and comparison syntax?

 

I always thought the below "looks" like the best, but...

	(valve_mask & VALVE_2) ? OUT2_ON() : OUT2_OFF();
  98:	81 ff       	sbrs	r24, 1
  9a:	02 c0       	rjmp	.+4      	; 0xa0 <main+0x16>
  9c:	40 9a       	sbi	0x08, 0	; 8
  9e:	04 c0       	rjmp	.+8      	; 0xa8 <main+0x1e>
  a0:	40 98       	cbi	0x08, 0	; 8
  a2:	02 c0       	rjmp	.+4      	; 0xa8 <main+0x1e>
                 ; 0000 0037 	(valve_mask & VALVE_1) ? OUT1_ON() : OUT1_OFF();
                 ;	valve_mask -> R16,R17
00003d ff00      	SBRS R16,0
00003e c003      	RJMP _0x3
00003f b1e8      	IN   R30,0x8
000040 60e2      	ORI  R30,2
000041 c002      	RJMP _0xF
                 _0x3:
000042 b1e8      	IN   R30,0x8
000043 7fed      	ANDI R30,0xFD
                 _0xF:
000044 b9e8      	OUT  0x8,R30

 

You can put lipstick on a pig, but it is still a pig.

I've never met a pig I didn't like, as long as you have some salt and pepper.

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

Generally I've use the define names/constants, but very recently I needed to use a variable to select the bit. 

Example?  Like, you are in "WATER mode" or JUICE mode"?  Then instead of having a variable called e.g. "mode" with value 1 for WATER and 2 for JUICE, you make the values as a mask instead.

You can put lipstick on a pig, but it is still a pig.

I've never met a pig I didn't like, as long as you have some salt and pepper.

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

Like, you are in "WATER mode" or JUICE mode"? 

Well, they are just some names I picked for flags, not modes....but at some point I needed to instead scan through them or select one based on a variable.  Just looking at the options & what is most efficient.  These tradeoffs are not usually discussed in any text that I've seen.

When in the dark remember-the future looks brighter than ever.   I look forward to being able to predict the future!

Last Edited: Wed. Aug 19, 2015 - 12:21 AM
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 1

As discussed you have two main options - either using a shift or using a table. As with most optimisations - get the code working then measure. Decide if it is efficient enough or needs optimising. You could look at the Arduino port code as they implement a layer of abstraction. Is it 'efficient'? Hell no! But it solves a problem cleanly. For the most part the inefficiency is not a problem. If all you are controlling is a led or relay, then you'd not notice the difference, but if you wanted to bit bash a high speed protocol, then it might be a problem.

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

Silly question but the original post mentions "valves". If we are talking about mechanical valves being opened/closed to control the flow of liquids does the efficiency of the Asm generated from the C actually matter. A valve probably takes 10's or 100's of millisecond to operate. 10's of thousands of CPU cycles. So whether a bit shift takes 10 machine cycles or 100 to implement doesn't really matter does it? As such I would write the code to be clear and obvious for the benefit of the maintainer. If you were really fussed look at the generated Asm and if it's truly horrendous consider a different implementation. But otherwise just write clear code.

 

As to run-time calculated shifts versus table lookup, I think we probably all agree that table lookup is going to "win" in terms of speed of execution (and predictable cycle count):

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

int main(void) {
    while (1) {
	PORTB |= (1 << (PINB & 7));
    }
}

$ avr-gcc -mmcu=atmega16 -Os -g 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>

                  [SNIP]

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 44 00 	jmp	0x88	; 0x88 <_exit>

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

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

int main(void) {
    while (1) {
	PORTB |= (1 << (PINB & 7));
  6c:	81 e0       	ldi	r24, 0x01	; 1
  6e:	90 e0       	ldi	r25, 0x00	; 0
  70:	48 b3       	in	r20, 0x18	; 24
  72:	26 b3       	in	r18, 0x16	; 22
  74:	27 70       	andi	r18, 0x07	; 7
  76:	bc 01       	movw	r22, r24
  78:	02 c0       	rjmp	.+4      	; 0x7e <main+0x12>
  7a:	66 0f       	add	r22, r22
  7c:	77 1f       	adc	r23, r23
  7e:	2a 95       	dec	r18
  80:	e2 f7       	brpl	.-8      	; 0x7a <main+0xe>
  82:	46 2b       	or	r20, r22
  84:	48 bb       	out	0x18, r20	; 24
  86:	f4 cf       	rjmp	.-24     	; 0x70 <main+0x4>

So it's not optimized away I use a volatile input (PINB & 7) to deliver an effectively random 0..7 value as input. This is then used to shift a 1 bit and then write to the volatile PORTB output (yeah both B - but it's only an example!). The table look up alternative might be:

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

uint8_t masks[] = { 0x01, 0x02, 0x04, 0x08, 0x10, 0x20, 0x40, 0x80 };

int main(void) {
    while (1) {
	PORTB |= masks[PINB & 7];
    }
}

$ avr-gcc -mmcu=atmega16 -Os -g 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 3f 00 	jmp	0x7e	; 0x7e <__bad_interrupt>

               [SNIP]

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

00000060 <__do_copy_data>:
  60:	10 e0       	ldi	r17, 0x00	; 0
  62:	a0 e6       	ldi	r26, 0x60	; 96
  64:	b0 e0       	ldi	r27, 0x00	; 0
  66:	ec e9       	ldi	r30, 0x9C	; 156
  68:	f0 e0       	ldi	r31, 0x00	; 0
  6a:	02 c0       	rjmp	.+4      	; 0x70 <__do_copy_data+0x10>
  6c:	05 90       	lpm	r0, Z+
  6e:	0d 92       	st	X+, r0
  70:	a8 36       	cpi	r26, 0x68	; 104
  72:	b1 07       	cpc	r27, r17
  74:	d9 f7       	brne	.-10     	; 0x6c <__do_copy_data+0xc>
  76:	0e 94 41 00 	call	0x82	; 0x82 <main>
  7a:	0c 94 4c 00 	jmp	0x98	; 0x98 <_exit>

0000007e <__bad_interrupt>:
  7e:	0c 94 00 00 	jmp	0	; 0x0 <__vectors>

00000082 <main>:

uint8_t masks[] = { 0x01, 0x02, 0x04, 0x08, 0x10, 0x20, 0x40, 0x80 };

int main(void) {
    while (1) {
	PORTB |= masks[PINB & 7];
  82:	88 b3       	in	r24, 0x18	; 24
  84:	e6 b3       	in	r30, 0x16	; 22
  86:	f0 e0       	ldi	r31, 0x00	; 0
  88:	e7 70       	andi	r30, 0x07	; 7
  8a:	f0 70       	andi	r31, 0x00	; 0
  8c:	e0 5a       	subi	r30, 0xA0	; 160
  8e:	ff 4f       	sbci	r31, 0xFF	; 255
  90:	90 81       	ld	r25, Z
  92:	89 2b       	or	r24, r25
  94:	88 bb       	out	0x18, r24	; 24
  96:	f5 cf       	rjmp	.-22     	; 0x82 <main>

Hopefully it's immediately obvious how much less work is required to use the lookup table. In fact I guess I should put it in flash to same RAM:

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

const uint8_t masks[] PROGMEM = { 0x01, 0x02, 0x04, 0x08, 0x10, 0x20, 0x40, 0x80 };

int main(void) {
    while (1) {
	PORTB |= pgm_read_byte(&masks[PINB & 7]);
    }
}

$ avr-gcc -mmcu=atmega16 -Os -g 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 2e 00 	jmp	0x5c	; 0x5c <__ctors_end>
   4:	0c 94 38 00 	jmp	0x70	; 0x70 <__bad_interrupt>

                 [SNIP]

00000054 <masks>:
  54:	01 02 04 08 10 20 40 80                             ..... @.

0000005c <__ctors_end>:
  5c:	11 24       	eor	r1, r1
  5e:	1f be       	out	0x3f, r1	; 63
  60:	cf e5       	ldi	r28, 0x5F	; 95
  62:	d4 e0       	ldi	r29, 0x04	; 4
  64:	de bf       	out	0x3e, r29	; 62
  66:	cd bf       	out	0x3d, r28	; 61
  68:	0e 94 3a 00 	call	0x74	; 0x74 <main>
  6c:	0c 94 45 00 	jmp	0x8a	; 0x8a <_exit>

00000070 <__bad_interrupt>:
  70:	0c 94 00 00 	jmp	0	; 0x0 <__vectors>

00000074 <main>:

const uint8_t masks[] PROGMEM = { 0x01, 0x02, 0x04, 0x08, 0x10, 0x20, 0x40, 0x80 };

int main(void) {
    while (1) {
	PORTB |= pgm_read_byte(&masks[PINB & 7]);
  74:	88 b3       	in	r24, 0x18	; 24
  76:	e6 b3       	in	r30, 0x16	; 22
  78:	f0 e0       	ldi	r31, 0x00	; 0
  7a:	e7 70       	andi	r30, 0x07	; 7
  7c:	f0 70       	andi	r31, 0x00	; 0
  7e:	ec 5a       	subi	r30, 0xAC	; 172
  80:	ff 4f       	sbci	r31, 0xFF	; 255
  82:	e4 91       	lpm	r30, Z+
  84:	e8 2b       	or	r30, r24
  86:	e8 bb       	out	0x18, r30	; 24
  88:	f5 cf       	rjmp	.-22     	; 0x74 <main>

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

yes there is, I usually use a bit fields for that


union st
{
	uint8_t statusFlags; // acess status flag as a 8bit variable
	struct
	{
		uint8_t flagBit7:1, flag2:1, flag3:1, flag4:1, flag5:1, flag6:1, flag7:1, flag1:1;
	};
};

union st status;

// Writes to all bits in once like a regular variable
status.statusFlags = some_thing;
// write to each individual bit
status.flag1 = either 1 or 0;

 

Last Edited: Wed. Aug 19, 2015 - 03:02 PM
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 1

But the problem with bitfields is that given n (0..7) how do you programmatically select the bit? Say I just read "3" from somewhere how would you arrange to write dest.flag3?

 

BTW your bitfield there looks a bit suspicious the bits are:

uint8_t flagBit7:1, 
        flag2:1, 
        flag3:1, 
        flag4:1, 
        flag5:1, 
        flag6:1, 
        flag7:1, 
        flag1:1;

I think 0..7 would be more "normal" ;-)

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

yes lol, it is 0...7 and not 7...0, my bad.

 

if by programmatically you mean by using an index number like if accessing an element of an array you can't, or not that I know.

 

but in the case of accessing/storing the state of a valve it is pretty easy to use a bit field.

 

instead of doing status |= (1<<STATUS_FLAG); which depends on the number of shifts it is pretty slow, or status |= STATUS_FLAG;

you can use the bit field

 

status.bit1 = true or false;

 

If you need to access it whit an index number then you can make a function, I don't know if it is very efficient (code size wise)

uint8_t get_statusFlags(uint8_t flagNumber)

{

      switch (flagNumber)

      {

               case 0:

                       return status.bit0;

               case 1:

       ..... etc. etc.

}

depends on what you need to do.

 

whit the union I get the advantage to read all the bits in once like a regular variable, or access each individual bits, depend on what the need is.

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

 

instead of doing status |= (1<<STATUS_FLAG); which depends on the number of shifts it is pretty slow, or status |= STATUS_FLAG;

you can use the bit field

 

status.bit1 = true or false;

 

Have you looked at the output? (the generated ASM code)

A mega AVR has a 8 bit barrel shifter (the MUL instruction), and sometimes the compiler use that for shifts!

If you know the bit at compile time then it don't do any shifts.

your code 

status |= (1<<STATUS_FLAG)

will generat the same code as

status |=0x40

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

yes lol, it is 0...7 and not 7...0, my bad.

You missed my point. What you actually had was:

 

7,2,3,4,5,6,7,1 (with the duplicate 7's having slightly different names).

 

As I'm sure you realise it should be:

 

0,1,2,3,4,5,6,7

if by programmatically you mean by using an index number like if accessing an element of an array you can't, or not that I know.

Yes, that's exactly what I mean. The whole point of this thread is that OP started out with:

Valve_channels_selected |=(2^index); 

now we all know that XOR is the wron operation here and what he really wanted was:

Valve_channels_selected |= (1 << index); 

but that implies selecting the bit to set AT RUNTIME using the variable "index" to select the bit position. You can do that with (1<<n) or masks[n] but you cannot using Valve_channels_selected.bitn. As you say the only way is a horrendous 8 part case/if..else/whatever.

 

The use of bitfields can be very powerful but only really when the target bit number is known at compile time so the compiler can convert a:

portB.bit3 = 1;

into

SBI portB, 3

or whatever. it does not lend itself to run time determination of the bit position done programmatically.

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

Yes I agree, in his case "variable |= (1<<n)" or "variable &= ~(1<<n)" is the best option, a bit field is not going to work if in the application it is need to access it whit an index. A function whit a 8 cases switch statement is an horrible way to get/set a bit whit an index number. And yes (2^index) it's wrong, and sorry for the typo there, lol, I just noticed it now that you pointer that out to me lol blush.

Well I though it was asking for a easy/alternative way of doing bits |= (1<<n) that are used as status bits for something in his application, and a bit field usually are an elegant and easy to track way of storing status bits where the only value is going to be either true or false or anyway not a full 8 bits needed.

I don't kind of like having many #define SOMETHING 1 ....... 2 ...... 3, since if many status variables are used, it may get confusing, my personal preference is "status.something", but yes, understanding your point now, I do fully agree whit you, sorry miss understood.

 

@clawson you seem to be very knowledgeable in AVR MCU, I know this is a bit outside topic here, but I am working on a project, where I am building some sensors that need to be operating on a battery, (ideally 3V cell, or 2 AAA standard 1.5V alkaline battery) that part is not define yet, but what would be the best MCU that will consume as low as possible current from the battery? I would love anything that is under or equal 500uA at 1MHz cpu clock from internal osc. I need something with 2CH ADC(sampling the system V to estimate battery status, and 1 for sensor readings), I2C, and SPI, what could I use?

 

Thanks smiley.

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

nikinicolosi01 wrote:
I know this is a bit outside topic here,
It is >>entirely<< off topic.  Please start another thread.

"Experience is what enables you to recognise a mistake the second time you make it."

"Good judgement comes from experience.  Experience comes from bad judgement."

"Wisdom is always wont to arrive late, and to be a little approximate on first possession."

"When you hear hoofbeats, think horses, not unicorns."

"Fast.  Cheap.  Good.  Pick two."

"We see a lot of arses on handlebars around here." - [J Ekdahl]