[TUT] [C] Bit manipulation (AKA "Programming 101")

Go To Last Post
232 posts / 0 new

Pages

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

Quote:

I know the indentation sucks.

Consider using indent.exe to fix it. It is vital for a C program to be indented correctly. It can raise the legibility by 100%.

As to the design of your code, clearly you only want to make output when the state of PINB.0 changes. For that consider how you can use the ^ operator. It is very good for spotting when a bit is "different from last time".

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

Thanks for the tips Cliff I will look into both.

If you want a career with a known path - become an undertaker. Dead people don't sue! - Kartman

Please Read: Code-of-Conduct

Atmel Studio6.2/AS7, DipTrace, Quartus, MPLAB user

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

did #define made different cycle/mileage/time/memory usage or not?

what about void somthin(void); ?

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

What #define?

Regards,
Steve A.

The Board helps those that help themselves.

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

A #define has nothing to do with code generation so I don't see how it could influence cycles or memory usage. Of course after something is #define'd then where that macro is then used I guess it might have some influence on those things. But the answer to "did #define made different.." is "no".

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

what is the difference

#define CLEARBITMASK(x,y) (x &= (~y))

with

void CLEARBITMASK(x,y) {x &= (~y)}

which the best one?

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

As you've written it there the first is best because wherever you use that preprocessor macro it'll just generate a few opcodes to do the AND operation. For the function version it'll (potentially) add a CALL/RET to each invocation. However if you made the function "static inline" there'd be no such overhead and you may benefit from C parameter type checking (talking of which, your function version forgot to specify types).

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

The macro version looks dangerous - move the inversion outside of the inner braces.

- Dean :twisted:

Make Atmel Studio better with my free extensions. Open source and feedback welcome!

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

clawson wrote:
As you've written it there the first is best because wherever you use that preprocessor macro it'll just generate a few opcodes to do the AND operation. For the function version it'll (potentially) add a CALL/RET to each invocation. However if you made the function "static inline" there'd be no such overhead and you may benefit from C parameter type checking (talking of which, your function version forgot to specify types).

so in another word
#define is like this

#define CLEARBITMASK(x,y) (x &= (~y))

while(1)
{
_delay_us(50);
CLEARBITMASK(1,2);
_delay_ms(10);
CLEARBITMASK(1,3);
}

will change to this by uC

while(1)
{
_delay_us(50);
(1 &= (~2)
_delay_ms(10);
(1 &= (~3)
}

meanwhile void CLEARBITMASK(int x,int y) {x &= (~y)}

while(1)
{
mov pB.1,#11101101b;
CLEARBITMASK(1,2);
mov pB.1,#11101101b;
CLEARBITMASK(1,3);
}

will change to

while(1)
{
mov pB.1,#11101101b;
acall CLEARBITMASK(1,2);
mov pB.1,#11101101b;
acall CLEARBITMASK(1,3);
}

CLEARBITMASK(int x,int y):
x &= (~y);
ret

is that like that? :?

Quote:
The macro version looks dangerous - move the inversion outside of the inner braces.

- Dean Twisted Evil


why dangerous? because we need to do "function prototype"?

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

Quote:

why dangerous? because we need to do "function prototype"?

No, because the y parameter can be any, potentially complex thing.

Assume you have

#define CLEARBITMASK(x,y) (x &= (~y)) 
char a, b, c;

and you

CLEARBITMASK(a, b&c);

This results in the following code actually being passed to the compiler:

(a &= (~b&c));

If the macro was instead defined as

#define CLEARBITMASK(x,y) (x &= ~(y))

this would be passed to the compiler

(a &= ~(b&c));

Quite some difference, right?

I suspect that you do not at all understand what macros are.

They are handled by the C preprocessor (CPP), which goes through the source code and does replacements in it before the compiler goes to work. It is nothing at all like writing e.g. a function and calling it.

There will be more on this in your C textbook. (If not you need to buy a better C textbook.)

"He used to carry his guitar in a gunny sack, or sit beneath the tree by the railroad track. Oh the engineers would see him sitting in the shade, Strumming with the rhythm that the drivers made. People passing by, they would stop and say, "Oh, my, what that little country boy could play!" [Chuck Berry]

 

"Some questions have no answers."[C Baird] "There comes a point where the spoon-feeding has to stop and the independent thinking has to start." [C Lawson] "There are always ways to disagree, without being disagreeable."[E Weddington] "Words represent concepts. Use the wrong words, communicate the wrong concept." [J Morin] "Persistence only goes so far if you set yourself up for failure." [Kartman]

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

ok tq, i know that, but i miss what Dean's means, because of my english, i coppy-paste frome Dean's tutorial code in 1st page.

so what about the diff. between void and define, am i right with my code up there (i scared that i miss again because of my english, so perhaps ex.code will clear it)

one more question
why i have to used

foo &= ~0x01;

can i used

foo &= 0xFE;

i understand exactly why i have to used

foo &= ~(1<<2);

because if we used

foo &= ~(0<<2);

because it will nothing change has made if we shift 0. but with

foo &= 0xFE;

its mean 0xnnnnnnnn AND with 0x11111110

but i found this not work, why?

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

Quote:

so what about the diff. between void and define

They are just totally different things!

The void is specifying a return value from a function.

The #define is a directive to the preprocessor to modify certain text in the C source before the compilation.

Just because they are in the seemingly corresponding places in your two examples does not mean that they are variants of something. Again, they are totally unrelated.

I still suspect that you do not have any (correct) concept of what the preprocessor is and does, and what a macro is. And again, any decent C textbook will give you the details on such subjects.

Quote:

why i have to used

foo &= ~0x01;

can i used

foo &= 0xFE;


Yes. The outcome should be the same.

Quote:
but with

foo &= 0xFE;

its mean 0xnnnnnnnn AND with 0x11111110

but i found this not work, why?


It should work just fine. If you don't say anything more detailed than "not work" then we can not say anything more detailed either.

"He used to carry his guitar in a gunny sack, or sit beneath the tree by the railroad track. Oh the engineers would see him sitting in the shade, Strumming with the rhythm that the drivers made. People passing by, they would stop and say, "Oh, my, what that little country boy could play!" [Chuck Berry]

 

"Some questions have no answers."[C Baird] "There comes a point where the spoon-feeding has to stop and the independent thinking has to start." [C Lawson] "There are always ways to disagree, without being disagreeable."[E Weddington] "Words represent concepts. Use the wrong words, communicate the wrong concept." [J Morin] "Persistence only goes so far if you set yourself up for failure." [Kartman]

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

Quote:
It should work just fine. If you don't say anything more detailed than "not work" then we can not say anything more detailed either.

ops, i found my code seems like something false (typoo) its supposed to 0bNNNNNNNN instead of 0xNNNNNNNN..my bad, it works just fine..

umm one more thing...

this is has same cycle or not?

PORTB |= 0x00001111;

with

PORTB |= (1<<1);
PORTB |= (1<<2);
PORTB |= (1<<3);
PORTB |= (1<<4);

i guess the 2nd one have more cycle :roll:

Last Edited: Sun. Oct 6, 2013 - 10:06 AM
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Quote:
i guess the 2nd one have more cycle

Stop guessing. Look at the generated code. Get the timings for the individual instructions in the "AVR Instruction Set" document available at www.atmel.com .

"He used to carry his guitar in a gunny sack, or sit beneath the tree by the railroad track. Oh the engineers would see him sitting in the shade, Strumming with the rhythm that the drivers made. People passing by, they would stop and say, "Oh, my, what that little country boy could play!" [Chuck Berry]

 

"Some questions have no answers."[C Baird] "There comes a point where the spoon-feeding has to stop and the independent thinking has to start." [C Lawson] "There are always ways to disagree, without being disagreeable."[E Weddington] "Words represent concepts. Use the wrong words, communicate the wrong concept." [J Morin] "Persistence only goes so far if you set yourself up for failure." [Kartman]

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

JohanEkdahl wrote:
Quote:
i guess the 2nd one have more cycle

Stop guessing. Look at the generated code. Get the timings for the individual instructions in the "AVR Instruction Set" document available at www.atmel.com .

ok, tq for the link

i guess because i read in forum that

Quote:

PORTB |= 0b00001111;

means PORT B is OR with 0b00001111;
and
Quote:
PORTB |= (1<<1);

is shift 1 to the left once and OR to the PORT B, so thats the reason why i guess it has more cycle because every change the bit need to shift the bit one by one and do OR operation for each bit.

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

Quote:
i guess it has more cycle

You're still guessing. The facts are available to you - if you study the generated assembly/machine code.

"He used to carry his guitar in a gunny sack, or sit beneath the tree by the railroad track. Oh the engineers would see him sitting in the shade, Strumming with the rhythm that the drivers made. People passing by, they would stop and say, "Oh, my, what that little country boy could play!" [Chuck Berry]

 

"Some questions have no answers."[C Baird] "There comes a point where the spoon-feeding has to stop and the independent thinking has to start." [C Lawson] "There are always ways to disagree, without being disagreeable."[E Weddington] "Words represent concepts. Use the wrong words, communicate the wrong concept." [J Morin] "Persistence only goes so far if you set yourself up for failure." [Kartman]

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

Can you take this discussion to a separate thread please? You aren't really giving feedback about the 101 post but asking basic questions about your misunderstanding of the C language.

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

no need clawson it is done, thank you for the link btw...

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

i use this simple macro in my code :

#define hc164datapin    PORTB0
#define hc164data_high  PORTB|=(1<<hc164datapin)
#define hc164data_low   PORTB&=~(1<<hc164datapin)
#define Puthc164data(x) (x ?  hc164data_high :hc164data_low)

but the result is error :
"lvalue required as left operand of assignment "

the problen is "Puthc164data(x) (x ? hc164data_high :hc164data_low)"
but i don't know why !

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

Add -save-temps to your build options and you will see why:

 #define hc164datapin    PORTB0
 #define hc164data_high  PORTB|=(1<<hc164datapin)
 #define hc164data_low   PORTB&=~(1<<hc164datapin)
 #define Puthc164data(x) (x ?  hc164data_high :hc164data_low)

int main(void)
{
    Puthc164data(1);    

This creates:

int main(void)
{
    (1 ? (*(volatile uint8_t *)((0x18) + 0x20))|=(1<<0) :(*(volatile uint8_t *)((0x18) + 0x20))&=~(1<<0));

What you really want it something more like:

if (1) {
  (*(volatile uint8_t *)((0x18) + 0x20))|=(1<<0)
}
else {
  (*(volatile uint8_t *)((0x18) + 0x20))&=~(1<<0));
}

Or perhaps:

  (*(volatile uint8_t *)((0x18) + 0x20)) |= (1 ? (1<<0) : 0);
  (*(volatile uint8_t *)((0x18) + 0x20)) &= (1 ? 0xFF : (1<<0));

When you use the ternary operator you can only use it to deliver a value to be used not to delimit two complete statements. So either give up on the ternary and just use compound if/else or do use the ternary but use it to do both an AND and an OR. In one case make the OR do nothing (OR with 0 has no effect) and in the other case make the AND do nothing (AND with 0xFF has no effect on an 8bi target).

I'll leave you to work out how to re-jig the macros to achieve this.

Oh and PLEASE give the macros upper case names. Most programmers expect mixed/one case to be symbol names and all upper case to be #define'd macros.

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

Quote:

but the result is error :
"lvalue required as left operand of assignment "

the problen is "Puthc164data(x) (x ? hc164data_high :hc164data_low)"


Yes. Let's assume you've coded

Puthc164data(1);

The preprocessor will expand the macros. First step will yield this:

(1 ?  hc164data_high :hc164data_low);

There are still macros to be expanded. The result will be:

(1 ?  PORTB|=(1<<hc164datapin) : PORTB&=~(1<<hc164datapin));

Does that make sense to you? Are you understanding how the ternary operator (?:) is to be used?

Are you aware that ?: and &= have the same precedence (and in those cases left to right evaluation will occur), effectively making your expression this (note the parenthesis I added):

( ( 1 ?  PORTB|=(1<<hc164datapin) : PORTB ) &=~(1<<hc164datapin));

Now take a look at what the &= operator has to the left.

EDIT: Ow. I left the computer before I got to press Submit, Cliff. You are free to issue your "Gee, I wish I said that"

EDIT2: On close inspection I believe I am more correct than you on this. A closer inspection might very well prove me wrong. I will return..

EDIT3: What I am sceptical about is: "When you use the ternary operator you can only use it to deliver a value to be used not to delimit two complete statements". AFAIK the second and third operators should be expressions. An assignment statement is an expression. Add to this that the whole ternary is an expression, and that you are free to form an expression and let it fall on the floor so-to-say. Methinks it is quite ok to code e.g.

x ? y = 1 : y = 42;

It's a stupid way to do what should be an if-else, but I think it will work. The coder might think it will be more efficient since the source code is more compact but he would likely be wrong on that assumption. I will return...

EDIT4: Yup, tested. I have this snippet:

uint8_t x;
x = PORTB;
x ? (PORTD |= 1) : (PORTD &= ~1);

Compiles just fine.

The error message the "OP" sees is because he has missed parenthesis. For my more transparent example just above this would be

x ? PORTD |= 1 : PORTD &= ~1;

If we add parenthesis for the implied left-to-right-evaluation-for-same-precedence-operators we get

(x ? PORTD |= 1 : PORTD) &= ~1;

I have verified that this generates the same error.

So, the solution would simply be to follow the rule that you should always follow. Put parenthesis around the whole macro:

Example:
BAD:

#define yes PORTD |= 1
#define no PORTD &= ~1

void foo(void)
{
   uint8_t x;
   x = PORTB;
   x ? yes : no;
   .
   .

For the above I get exactly the same error.

GOOD:

#define yes (PORTD |= 1)
#define no (PORTD &= ~1)

void foo(void)
{
   uint8_t x;
   x = PORTB;
   x ? yes : no;
   .
   .

No error.

Again: I concur with Cliff that you should not use the ternary operator to disguise an if-statement. The result of the complete ternary expression should go somewheere, or you should not use it. Do not assume that shorter source code will necessarily lead to more compact or faster machine code.

"He used to carry his guitar in a gunny sack, or sit beneath the tree by the railroad track. Oh the engineers would see him sitting in the shade, Strumming with the rhythm that the drivers made. People passing by, they would stop and say, "Oh, my, what that little country boy could play!" [Chuck Berry]

 

"Some questions have no answers."[C Baird] "There comes a point where the spoon-feeding has to stop and the independent thinking has to start." [C Lawson] "There are always ways to disagree, without being disagreeable."[E Weddington] "Words represent concepts. Use the wrong words, communicate the wrong concept." [J Morin] "Persistence only goes so far if you set yourself up for failure." [Kartman]

Last Edited: Wed. Feb 19, 2014 - 07:46 AM
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

OK my hands up i was wrong about ternary and precedence and off-course use upper case in macro
but i just looking for clear and simple way to do something
like this:
PORTX.X=X;
I know that bit is not access in c so i use that macro....

thanks

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

Quote:

but i just looking for clear and simple way to do something
like this:

Then you haven't read all this thread have you? There is a technique given that allows you to do pretty much that.

hint: look for posts by "danni".

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

abcminiuser wrote:
Programming 101 - By Eric Weddington
Quote:
These operators work on bits and not logical values. Take two 8 bit bytes, combine with any of these operators, and you will get another 8 bit byte according to the operator's function.
Strictly speaking, that is not true.
In C, such operations always produce at least 16 bits.
More than 16 if int is larger than 16 bits.

International Theophysical Year seems to have been forgotten..
Anyone remember the song Jukebox Band?

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

Excellent tutorial. This has helped me understand bit operations a lot better .I used to have to rewrite the whole port or register with a new hex value. This makes it easy.

Are there any good tutorials like this on fuses. I find them somewhat confusing?

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

Sorry somehow my comments ended up in the wrong place so I deleted them but I am not allowed to delete the post entirely

It is better to light a candle than curse the darkness.

Last Edited: Wed. Feb 24, 2016 - 06:22 PM
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

This looks like a really nice way of doing things! So readable and straight forward. I am learning micro-controller C  from 

a book that references the AVR compiler.

 

They use

 

sfrb PORTB=0x25;

 

then later in the program they can say

 

PORTB.1=1;

 

I tried to do this but sfrb doesn't seem to be defined

 

 

In your solution

 

 

 

Does LED0=1;

 

take any more time at run time  than

 

 PORTB=PORTB|0b00000010

 

Thanks

 

 

It is better to light a candle than curse the darkness.

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

hess59 wrote:
They use sfrb PORTB=0x25; then later in the program they can say PORTB.1=1; I tried to do this but sfrb doesn't seem to be defined

That syntax is only supported by Codevision. While it is a very elegant syntax for microcontroller programming it is a violation of the C standard which says that symbol names after "." must start with an alpha. The closest you can get with most C compilers is PORT.b0 where 'b' stands for "bit" or something like that.

 

If you read back through this entire thread you will find posts from a user "danni" with an attachment called "sbit.h". That uses this syntax but then goes on to define symbols such as:

#define PORT_B5 SBIT( PORTB, 5)

which in turn is defined as:

struct bits {
  uint8_t b0:1, b1:1, b2:1, b3:1, b4:1, b5:1, b6:1, b7:1;
} __attribute__((__packed__));
#define SBIT_(port,pin) ((*(volatile struct bits*)&port).b##pin)
#define SBIT(x,y) SBIT_(x,y)

so in effect it says:

#define PORT_B5 ((*(volatile struct bits*)&PORTB).b5)

None of this matters because it just means you can write:

#include "sbit.h"

#define LED0 PORT_B5

int main(void) {
    LED0 = 1; // switch it on
    LED0 = 0; // switch it off
}

and you don't need to care how this is really achieved.

 

EDIT: The sbit.h you need is in post 134: http://www.avrfreaks.net/comment...

Last Edited: Wed. Feb 24, 2016 - 07:00 PM
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Tried it and it works quite well!. Thanks Clawson . I find the code much more readable using this style.

It is better to light a candle than curse the darkness.

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

ka7ehk wrote:

You are mixing up true/false with numeric value.

 

In C, 0, (that is, 0x00 or 0x0000 or whatever) is taken to be false.

ANYTHING else is taken as true.

 

So  0x80 combined using a bitwise-AND with 0x81 = 0x80 and will be TRUE.

 

On the other hand, 0x80 combined using a logical AND with 0x81 is also TRUE because both 0x80 and 0x81 are logically TRUE.

 

You are also confusing yourself when you talk about "digits".  in the expression 0b10000000, there are 8 BITS, not 8 digits.

 

Jim

 

I take your point about the use of "digit" but the poor choice of wording isn't what was causing my confusion.  "Digit" is the decimal equivalent of "bit" to a binary numeric system.  That's the sense I meant to convey.  The AND bitwise math is NOT what is confusing me.  Subtle distinction I know, but the wording in the OP implied to me that only one bit was being evaluated and the rest was being ignored.  It was but only by virtue of the mask value being 0x80.  But, if you want to see how I was looking at it (wrong as it was), the if(foo & 0x80) to me looked like if(byte & bit) because 0x80 represents a single bit being 1.  Convoluted I know, but it's how my brain processed what I read.  That incorrect thinking led to me assuming that the second operand could only represent a single bit, though it was itself a byte.... meaning, there would only be 8 possible choices for the second operand, namely 0x80, 0x40, 0x20, 0x10 etc.  So, when I asked about 0x81, a byte with two 'on' bits, it was an effort to try and clear up the understanding I had which didn't make sense to me.

 

My point about logical AND was based on the previous incorrect assumption.  If we look at the mask 0x81 with two bits, either one evaluating to true would result in the if() being executed.  THAT would be a logical OR.  I was taking the bitwise AND in the if(0x80 & 0x81) to mean that both the LSB and the MSB in the result would need to evaluate as true in order for the if() to execute.  It only takes one, though.  I get it now.

 

<edit> MODERATOR: PLEASE MOVE THIS POST TO THE SPLIT THREAD.  APPARENTLY, I WAS IN THE MIDDLE OF POSTING WHEN YOU SPLIT IT.  SORRY FOR THE CONFUSION.

Last Edited: Thu. Jul 14, 2016 - 12:37 PM
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Just to add to your confusion. Things would be quite different if the & was, in fact &&. That would effectively reduce everything on each side to a simple 0/1, false/true and then perform AND on the two.

 

I do think it's worth a bit of time reading around about boolean logic (my favourite subject at university in fact) as it is pretty fundamental to the embedded C programmer.

Pages