Bit Angle Modulation and Shift Registers

Hello,

Firstly I am only new to Arduino so please be kind...

I am trying a simple project of hooking up 8x red LEDs to a shift register and having being able to control each of their intensities in an animation type function.

I am hoping this will pave the way to my understanding of how to incorporate different levels of BAM into my future projects (led interactive table, rgb cube etc etc).

I understand the concept of BAM as well as the shift register. My issue is, I cannot for the life of me understand how to shift out the correct bytes to display even a fading effect on ANY of the LEDs.

I am after that light bulb moment where something clicks in my brain lol. Will someone please provide me of such information??????

I will attach my code (don't laugh as I am very tired and it probably doesn't even make much sense!!)

Any help is much appreciated,

Dave

int latchPin = 8;   // Latch pin connected to pin 12 RCLK on the 74HC595
int clockPin = 12;  // Clock pin connected to pin 11 SRCLK on the 74HC595
int dataPin = 11;   // Data pin connected to pin 14 SER on the 74HC595

byte BAM_Counter = 0;   //Counter used in BAM_Handler function
byte brightness;        //4-Bit desired brightness for leds
byte shift_out;         //Byte shifted out to shift registers

byte red[8];
byte which_led = 4;

unsigned long current_time;
unsigned long last_updated_time;

/*------------------------------------------------------------*/

void setup() {

 pinMode(latchPin,OUTPUT);
 pinMode(clockPin,OUTPUT);
 pinMode(dataPin,OUTPUT);
 
}

/*~*~*~*~*~*~*~*~*~*~*~*~*~*~*~*~*~*~*~*~*~*~*~*~*~*~*~*~*~*~*~*~*~*/

void loop() {

current_time = millis();
last_updated_time = 0;

if(brightness > B1111) { brightness == B0001; }

if((current_time - last_updated_time) > 49) 
    {
      last_updated_time = current_time;
      brightness++;
    }

 BAM_Handler();
 }

/*~*~*~*~*~*~*~*~*~*~*~*~*~*~*~*~*~*~*~*~*~*~*~*~*~*~*~*~*~*~*~*~*~*/

void BAM_Handler() {
       
  if(BAM_Counter > B1111) { BAM_Counter = B0001; }      //Once counter reaches 16, it resets back to 1
        
       if(BAM_Counter == B0001)   { Update_leds(0); }   //4-Bit binary code modulation handler.
  else if(BAM_Counter == B0010)   { Update_leds(1); }
  else if(BAM_Counter == B0100)   { Update_leds(2); }
  else if(BAM_Counter == B1000)   { Update_leds(3); }

  BAM_Counter++;
  }

/*~*~*~*~*~*~*~*~*~*~*~*~*~*~*~*~*~*~*~*~*~*~*~*~*~*~*~*~*~*~*~*~*~*~*/

void Update_leds(byte bitPosition) {

red[8] = bitWrite(red[8], which_led, bitRead(brightness, bitPosition));

digitalWrite(latchPin, LOW);
shiftOut(dataPin, clockPin, MSBFIRST, red[which_led]);
digitalWrite(latchPin, HIGH);

}

Hi,

Can you please post a copy of your sketch, using code tags?
They are made with the </> icon in the reply Menu.
See section 7 http://forum.arduino.cc/index.php/topic,148850.0.html

Thanks..Tom..... :slight_smile:

Hi,

Yes that is the correct way to post your code. Now edit your first post and put them in.

If you are tired and you know your code does not make sense, why post it? Why not wait until you have slept and reviewed it?

Here are some questions for you to think about. Where is latchPin used? If you have only one shift register, why are you shifting out 32 bits of data?

I do not wish to be insulting, but you say you understand BAM and shift registers, but right now your code says you understand neither. When you have slept and reviewed it, post it again.

Paul

Hi Paul,

Thank you for the heads up. I was copying and pasting things like no tomorrow between sketches and obviously forgot the latch pin part of it. Perhaps I don't fully understand shift registers after all as it is more the way to implement the BAM through the shift registers.
I am sorry I know I am being lazy but I have been on this for weeks now and I was hoping a nudge in the right direction would help.
Thanks anyway mate
Dave

Ok, its going to need a few nudges. I won't have time until later today to get the laptop out.

When using BAM, the idea is that the delay period between updates of the shift register doubles on each phase of the BAM cycle, for example in a 4-bit BAM cycle, phases might last 1ms, then 2ms, 4ms, 8ms and then the cycle is repeated. Right now you have no code to time the phases.

Another newbie mistake in you code: in C, an array with 8 elements has indexes 0 to 7. Index 8 is actually outside the array and that memory might belong to another variable.

Ok that's good to know about the timing having to be written separately. I assumed the BAM counter would go as fast as the clock rate, more or less checking the brightness byte, which updates every 50ms, then update accordingly.

I can get it working with a digitalWrite function to the LED on pin 13. This is what is doing my head in, I am so close but I can't seem to figure out how to do it via shifting out byte at a time

Dave

Try this Dave. It compiles but I can't test it for you.

int latchPin = 8;   // Latch pin connected to pin 12 RCLK on the 74HC595
int clockPin = 12;  // Clock pin connected to pin 11 SRCLK on the 74HC595
int dataPin = 11;   // Data pin connected to pin 14 SER on the 74HC595

byte red[8] = {0, 2, 4, 6, 8, 10, 12, 14}; //4-Bit desired brightness for each led

unsigned long last_updated_time = 0;
unsigned long last_BAM_time = 0;
unsigned long BAM_delay = 500UL;
byte BAM_phase = 1;

/*------------------------------------------------------------*/

void setup() {

  pinMode(latchPin, OUTPUT);
  pinMode(clockPin, OUTPUT);
  pinMode(dataPin, OUTPUT);

}

/*~*~*~*~*~*~*~*~*~*~*~*~*~*~*~*~*~*~*~*~*~*~*~*~*~*~*~*~*~*~*~*~*~*/

void loop() {

  if (millis() - last_updated_time >= 50) {

    for (byte led = 0; led < 8; led++) {
      if(red[led] == 15) {
        red[led] = 0;
      }
      else {
        red[led]++;
      }
    }

    last_updated_time += 50;
  }

  BAM_Handler();
}

/*~*~*~*~*~*~*~*~*~*~*~*~*~*~*~*~*~*~*~*~*~*~*~*~*~*~*~*~*~*~*~*~*~*/

void BAM_Handler() {

  if (micros() - last_BAM_time >= BAM_delay) {

    if (BAM_phase == 4) {
      BAM_phase = 1;
      BAM_delay = 500UL;
    }
    else {
      BAM_phase++;
      BAM_delay = BAM_delay * 2;
    }
    
    Update_leds(BAM_phase);
    last_BAM_time += BAM_delay;

  }
}

/*~*~*~*~*~*~*~*~*~*~*~*~*~*~*~*~*~*~*~*~*~*~*~*~*~*~*~*~*~*~*~*~*~*~*/

void Update_leds(byte bitPosition) {

  byte led_data;

  for (byte led = 0; led < 8; led++) {
    bitWrite(led_data, led, bitRead(red[led], bitPosition));
  }

  digitalWrite(latchPin, LOW);
  shiftOut(dataPin, clockPin, MSBFIRST, led_data);
  digitalWrite(latchPin, HIGH);

}

Ask any questions about the changes I made. I want you to understand every line.

Thank you Paul for the time and effort you put into this. It does work as I wanted thank you very much. I do have a few questions:

  • With the red[8] array, you have stipulated the values for each byte in the array as the brightness. When you enter the for loop, in void loop, it says when the last byte is reached (which is 14 in the array), if it is equal to 15 then reset it back to 0. Though I would have thought with the last byte being 14 it could never equal 15?

  • With the unsigned long variables declared, you put a UL only after the BAM_Delay? I have removed this and it still compiles all good. What is the purpose of this, I'm assuming it stands for unsigned long?

  • Does the millis and micros functions run off the same internal timer? And do you use the micros for the BAM_Handler for a fast update time?

  • When I update the BAM_delay to something faster, I can't really see any change. I would have thought this would have a change of rate of brightness? Or as long as this value is faster than the brightness it has no effect?

That's all the questions for the code but I have a general programming question. How do you think about the structure of your sketch? Do you work backwards, ie think about what you want to happen, then break it into bits and pieces and write code for each? Maybe that's what I'm struggling with a little, where to start you know. I think with this sketch the main issue was I didn't realise there needed to be a timing part of the BAM_Handler. It makes sense now that I see it in front of me :/.

Thank you again for your time and effort into this Paul, much appreciated.

I'd like to set it up so I can get some animations happening :).

Dave

daveymit:

  • With the red[8] array, you have stipulated the values for each byte in the array as the brightness. When you enter the for loop, in void loop, it says when the last byte is reached (which is 14 in the array), if it is equal to 15 then reset it back to 0. Though I would have thought with the last byte being 14 it could never equal 15?

Not sure I understand the question. The logic is the same for all the elements of the array, not just the last one. The idea is to reset the brightnesses back to zero and stop them reaching the value 16.

daveymit:

  • With the unsigned long variables declared, you put a UL only after the BAM_Delay? I have removed this and it still compiles all good. What is the purpose of this, I'm assuming it stands for unsigned long?

Yes, the UL tells the compiler to treat the number as an unsigned long (32-bits). By default it would treat it as an int (16 bits, signed). In these cases it makes no difference, but in some situations its important, so I am in the habbit of typing it anyway.

daveymit:

  • Does the millis and micros functions run off the same internal timer? And do you use the micros for the BAM_Handler for a fast update time?

Same timer, yes. With millis(), the shortest time period you could measure would be, obviously, 1ms. This would give an overall BAM cycle of 1+2+4+8=15ms, so 66Hz, which in practice is probably fast enough to avoid flickering. However, when you get into more complex multiplexing, or you want more BAM depth/levels, then you will probably need to go sub-1ms and use micros() instead if millis().

daveymit:

  • When I update the BAM_delay to something faster, I can't really see any change. I would have thought this would have a change of rate of brightness? Or as long as this value is faster than the brightness it has no effect?

The BAM cycle is 66Hz, already too fast to see, so you won't notice any difference making it even faster. The animation is timed independently of the BAM cycle, using a 50ms period, so it won't be affected by changing the BAM cycle.

daveymit:
How do you think about the structure of your sketch?

Yes, you start out by describing what you want to happen in English, then break that down and refine it until each step is simple and unambiguous. Then you convert each step into code. As you get more experienced, more of those stages happen in your head, especially for short programs.

Anyway.,.

Just realized I made a mistake in the code I gave you! BAM_phase should be in range 0 to 3. I have it in range 1 to 4. Do you want to have a go at fixing that?

Thank you Paul.

Yes the 0-3 BAM phase fixed a slight issue that was happening in the animation. Although the leds slightly flicker through the dimming stage, but this is probably due to no filtering on the supply side.

So the idea with giving those initial values for each element in the array is to initially create a stagger for the animation... I've tried a few things around this and want to make some different animations somehow.

I will try and get into the habit of using UL where I can :wink: Good to know...

I suppose its like the old saying practice makes perfect.... Thanks again for all your time on this its been a great help :wink:

Dave

daveymit:
the leds slightly flicker through the dimming stage

Do you have some caps in there? You should have a 0.1uF decoupler close to the '595's power pins, and perhaps a 10uF cap also to smooth the current.

daveymit:
So the idea with giving those initial values for each element in the array is to initially create a stagger for the animation... I've tried a few things around this and want to make some different animations somehow.

Yes you are correct. You can do anything you want with the animation by manipulating the values in the array. The BAM code operates indepentantly of that.

No I'm still waiting on some caps to be delivered but I did have intentions of doing this when they arrived.

Thank's again mate

So i have rewritten the code to better suit what i am trying to do (and to try it in a new way!! :)). I am happy with how this works but there is one problem. Here is the code:

int latchPin = 8;   // Latch pin connected to pin 12 RCLK on the 74HC595
int clockPin = 12;  // Clock pin connected to pin 11 SRCLK on the 74HC595
int dataPin = 11;   // Data pin connected to pin 14 SER on the 74HC595

byte shift_out;             //Used as the byte to shift out to shift registers
byte BAM_counter = 0;       //Counter for BAM Handler
byte bitPosition = 0;       //Used to update the bit position of the BAM counter

unsigned long BAM_delay = 500UL;    // BAM Delay used in BAM handler

void setup() {

  pinMode(latchPin, OUTPUT);
  pinMode(clockPin, OUTPUT);
  pinMode(dataPin, OUTPUT);

}

void loop() {

for(byte x=0; x<8; x++)
led(x,15);
}

void led(byte which_led, byte brightness) {
  
  // Constrain the values for a range
  constrain(which_led, 0, 7);
  constrain(brightness, 0, 15);  


  //Code for shifting out the byte to the shift register
  byte leds;
  shift_out = bitWrite(leds, which_led, bitRead(brightness, BAM_counter));

    digitalWrite(latchPin, LOW);
    shiftOut(dataPin, clockPin, MSBFIRST, shift_out);
    digitalWrite(latchPin, HIGH);
    
  BAM_handler();
}

void BAM_handler()  {
 // Bit Angle Modulation Handler
  if(micros() - BAM_delay > BAM_delay)  {
    if (BAM_counter == 4) {
     BAM_counter = 0;
     BAM_delay = micros() + 500UL; }
    else  {
     BAM_counter++;
     BAM_delay = BAM_delay * 2; }
  }

}

So the issue is in the void loop when i am trying to manipulate the the two led values. The way it is now with the for loop for the 'which_led' byte, it works fine, all led's light up at said brightness. But as soon as i try to animate the brightness byte, it doesn't work. Everything compiles but im obviously doing something wrong. Even just trying to do something basic like fading all the leds up to full brightness then repeating. Here is what i mean:

byte x;
byte b;
unsigned long current_time = millis();
if(millis() - current_time >49) {
    b++;
    current_time += 50;
}

if(b == 16)
b = 0;

for(x=0; x<8; x++)
led(x,b);

What am i missing?

Cheers
Dave

daveymit:
So i have rewritten the code to better suit what i am trying to do (and to try it in a new way!!

Dave, I'm sorry, but I just think you broke it. I don't think you really understood what the function of each part of the sketch was doing, and you changed them and now they make no sense to me.

My advice would be to go back to the sketch I gave you and work forwards from there again. Make the correction for BAM_phase in range 0 to 3. Then check it still works. But after that don't make any further changes to the BAM_handler() and Update_leds() functions. They don't need to change again to achieve any animation you want.