Go Down

Topic: Bitshift help. (Read 1 time) previous topic - next topic

Pavilion1984

Sep 30, 2012, 07:01 pm Last Edit: Sep 30, 2012, 07:13 pm by Pavilion1984 Reason: 1
Hello all,

Code: [Select]
//Main.
void loop()
{
 byte b = 0;
 unsigned long temp = 0;
 unsigned long result = 0;
 char str[32];
 
 Ignition_State_Check();
 
 get_PID(ERPM, &result, str);
 temp = result/1000;
 for(byte i = 0; i < temp; i++)
 {
   b = 1 << 1 | 1;
 }
 Serial.print(b, BIN);
 delay(1000);
}


Am a bit stuck for ideas and not sure if am trying to do this correctly. I have a value in unsigned long result then that gets divided by 1000 to give me either a 0 or a 1 or a 2 or a 3 or a 4 or a 5 or a 6 or a 7 or a 8 depending whats in unsigned long temp. I then am trying to use a for loop to give me for example in byte b 00111111. This will then be used to light up 6 out of 8 LED's but i ony get is 00000010. Can anyone tell me what am doing wrong?

Thanks.

WizenedEE

Code: [Select]

   b = 1 << 1 | 1;


Here you're assigning b to 0... temp times.

Try this:
Code: [Select]

for(byte i = 0; i < temp; i++)
  {
    b = b | (1 << i);
  }


There, every time the loop runs, b gets a 1 in one more place over.

Pavilion1984

#2
Sep 30, 2012, 08:18 pm Last Edit: Sep 30, 2012, 08:27 pm by Pavilion1984 Reason: 1
Thank you, could please explain what this is doing b = b | (1 << i); because i cant get my head around it and if i had a value of 6 i would get 111111 but do i need to add 2 0's at the end to make up the full 8 bits like 11111100? Also is this a good way to do what am trying to do or would using a array like this be a better way ?
Code: [Select]
byte ArrayRPM[8] =
{
 B00000000,
 B10000000,
 B11000000,
 B11100000,
 B11110000,
 B11111000,
 B11111100,
 B11111110,
 B11111111,
};

WizenedEE

Code: [Select]
[code]
1 << i;

makes the values:

00000001
00000010
00000100
00001000
00010000
00100000
01000000
10000000


assuming that it runs 8 times. Then by using a bitwise OR on each one, the one's just add together. So
00000001
00000010
00000100
00001000
00010000

all OR'd together  makes
00011111

if you want to make the output look like
11110000 for an input of 4, then you could do this:
Code: [Select]

for(byte i = 0; i < temp; i++)
 {
   b = b | (1 << (8-i));
 }

That makes it go in reverse order.


I'm almost certain there is a better way to do this than using a loop or a lookup table though.[/code]

Pavilion1984

Thank you, you have been most helpfull and i understand now.
Quote
I'm almost certain there is a better way to do this than using a loop or a lookup table though.
If anyone knows then that would be great.

Grumpy_Mike

To replace:-
Code: [Select]
temp = result/1000;
  for(byte i = 0; i < temp; i++)
  {
    b = 1 << 1 | 1;
  }

How about
Code: [Select]
temp = result/1000;
b = (1 << temp) -1; 

Pavilion1984

Yes Grumpy_Mike but for example if result was 4 then i need that code to create 11110000.

WizenedEE

Does this work?

Code: [Select]

b = ~((1 << (8-temp)) -1);

Pavilion1984

I have just tested that and yes it does work, great. What is the -1 doing in this if you don't mind me asking (i have not used these functions before so this is new to me)? If am going to use it i need to know what it is doing lol but thanks a bunch to WizenedEE and Grumpy_Mike.

dhenry

Code: [Select]
  b = 1 << 1 | 1;

use instead:

Code: [Select]
  b = (b << 1) | 1;

to produce 0b00000001 -> 0b00000011 -> 0b00000111 ...

or
Code: [Select]
  b = (b >> 1) | 0x80;

to produce 0b10000000 -> 0b11000000 -> 0b11100000 ...;

This is also considerably faster.

Pavilion1984

Quote
Code:
  b = 1 << 1 | 1;
use instead:


Code:
  b = (b << 1) | 1;
to produce 0b00000001 -> 0b00000011 -> 0b00000111 ...

or

Code:
  b = (b >> 1) | 0x80;
to produce 0b10000000 -> 0b11000000 -> 0b11100000 ...;

This is also considerably faster.
dhenry, i have tested what you have said and it doen't work.

WizenedEE


I have just tested that and yes it does work, great. What is the -1 doing in this if you don't mind me asking (i have not used these functions before so this is new to me)? If am going to use it i need to know what it is doing lol but thanks a bunch to WizenedEE and Grumpy_Mike.


Code: [Select]

1<<5 = 00100000
00100000 -1 = 00011111
~00011111 = 11100000

Grumpy_Mike

Quote
What is the -1 doing

Subtracting one.

Any power of two can be expressed as a 1 followed by several zeros, the number depending on the power. If you subtract one from the resulting number you get zero followed by several ones.
If you want the inverse of this, for example you have 0001 1111 and you want 1110 0000 then just take the inverse of the number. There are many ways of doing this, here are some, assume the number you want to invert is in a vaiable called b

Code: [Select]

b = ~b; // note this is a tilda not a minius
b = b ^ 0xff // exclusive or inverts all bits in a variable that have a matching one in the number you are exoring it with
b = not(b);

Pavilion1984

Ok thanks alot. So i have 2 options of code to use

1 - the lookup table
or
2 - the bitshift function

is one better then the other or just personal preference? I will be using the i2c driver for my display and this RPM stuff is just the start of it. I now need to find out the best way to flash the last "on" LED in 11111000 if a variable has been set to 5 or if the variable has been set to 6 flash the last "on" LED in 11111100.

Grumpy_Mike

It is always better to use an algorithm than a look up table in my opinion.

For flashing create a number with a one in the bit you want to flash using bit shift operations. Then exclusive or this number with your byte save and output.
Every exclusive or operation will toggle the bit you set in the number.

Go Up