Go Down

Topic: [SOLVED]Problem with binary and shift register (Read 1 time) previous topic - next topic


Sep 05, 2012, 12:05 am Last Edit: Sep 06, 2012, 02:10 pm by samwhiteUK Reason: 1
Hi all, figured this was PROBABLY the right place for this, but it could probably also fall under Programming Questions.

Basically I am trying to create some sound-reactive LEDs in the form of an LED equalizer, or that is the aim of the final project.

At the moment, I am just experimenting with using a shift register to turn on the correct number of lights determined by a number passed over serial to the arduino.

Here is the code I have so far:

Code: [Select]

int data = 2;
int clock = 3;
int latch = 4;
int v;
int lights = 0;

void setup(){
  pinMode(latch, OUTPUT);
  pinMode(clock, OUTPUT);
  pinMode(data, OUTPUT);

void loop(){
  v = getSerial();
  lights = calculate(v);
  lights = 0;

//processes serial input 
int getSerial(){
  if (Serial.available()) {
    //convert char to int
    v = int(Serial.read()-'0');
  return v;

//works out the value to be passed to light() 
int calculate(int v){
  lights = (pow(2,v)-1);

//turns on the required number of LEDs (supposedly) 
void light(int lights){
  digitalWrite(latch, LOW);
  shiftOut(data, clock, MSBFIRST, lights);
  digitalWrite(latch, HIGH);

Now my problem arises when it comes to understanding how the binary is processed.

I have 8 LEDs in a row. If the "volume" is three, I need to turn 3 LEDs on. Because I am using a shift register, and because binary uses the formula 2^x, or in my case 2^v, 2^3 would be 8, which would turn on 4 LEDs, but seeing as I only need 3, I have to take 1 away.

However, this does not work as expected. For one LED, it works perfectly, but for more than one, the value passed to light() is 1 too small. If I change the line

Code: [Select]
lights = (pow(2,v)-1);


Code: [Select]
lights = (pow(2,v));

then it works fine EXCEPT for the case where the "volume" is 1, as obviously, the arduino works out 2^1, which is ovbiously 2, so it turns on the second LED.

I am guessing here, but does the issue arise when converting the char from the serial input to an int? Or is this not the problem?

If any more detail is needed, I will be happy to provide.




Sep 05, 2012, 01:29 am Last Edit: Sep 05, 2012, 05:16 am by marco_c Reason: 1
You should just use the logical bit functions. So you can either use the form (1<<n) - one shifted right by n positions - or the bit set/reset functions that are defined in the Arduino header file. Personally I usually use the shifting functions. Pow() will pull in the floating point functions and is rather slow.

Look at bit shft operands <<, >> in any C or C++ manual
Arduino Libraries https://github.com/MajicDesigns?tab=Repositories
Parola for Arduino https://github.com/MajicDesigns/Parola
Arduino++ blog https://arduinoplusplus.wordpress.com


Sep 05, 2012, 04:57 am Last Edit: Sep 06, 2012, 02:56 pm by tmd3 Reason: 1
pow() isn't just slow and bulky.  In this application, it'll give you unexpected results.  pow() returns a double result - identical to float in the Arduino.  float values are always almost exact, but they often differ from the expected value by a very small amount.  In this program, the results of pow() are sometimes just a little bit short of the theoretical values.  When the results are stored in the int variable lights, they're truncated rather than rounded.

If you want to see this in action, just Serial.print(lights).  To see it in action even more clearly, declare a float variable, set it to pow(2,v), and print it to too many decimal places, like 6.  You'll see that lights comes up short for v = 2, 3, 4, 5, 6, and 7.  

float is great for performing scientific calculations, where the inputs themselves usually aren't known with absolute accuracy.  But that's not what this program does - it generates a bit pattern using a numerical calculation as a handy way of generating that pattern.  You don't need the extended range or the deep precision of float.  You need the accuracy of integer arithmetic.  

You have options.  You could write a function to multiply 1 by 2 an arbitrary number of times, using integer arithmetic, and it would work, certainly for arguments less than 8.   But, the straightforward way is to use the shift operator.  Compared to pow(), it generates a whole lot less code, runs very fast, and never misses.


Thankyou very much for the replies, I will look into bit functions and operators as suggested.

Also, thankyou tmd3 for using examples that I can actually follow in my code, I've seen all too many people of my experience become completely confused by replies as they don't see how it fits into their problem.



Bit functions worked very well, thanks for the help.


Go Up