PWM via Shift Register: ShiftOut 16 Bits?

Hey guys;

OK, the code is pretty simple, it follows the normal PWM pattern for the most part, the one problem is, my code freezes up when I try to shiftOut a (2 byte) Short with a slightly modified shiftOut() function. Can anybody help me with this?

Also, if you see any way to let the ISR run more faster, could you point it out?

Thanks guys! If you need any more info, feel free to ask. Ill do my best!

#define __dataPin 9
#define __clockPin 10
#define __latchPin 11
#define __numLedsInArray 4
#define __numPinsPerLed 3

#define __maxBrightness 100
int ledPin =  13;    // LED connected to digital pin 13
int i=0;
int tcnt2;
int isrCount = 0;
int loopCount=2;
int ledStates[__numLedsInArray][__numPinsPerLed];
int ledNumber = 0;
int ledLoopNum = 0;

short currBuffer = 0;

void setup()   {                
  // initialize the shift reg control pins as outputs:
  pinMode(__dataPin, OUTPUT);
  pinMode(__clockPin, OUTPUT);
  pinMode(__latchPin, OUTPUT);
  Serial.begin(9600);
  
  setupTimer();
  
}

ISR(TIMER2_OVF_vect) {
  /* Reload the timer */
  TCNT2 = 160; //This seems to not flicker very much.
  while(ledNumber <= __numLedsInArray-1)
  {
    while(loopCount<=2)
    {
      if(ledStates[ledNumber][loopCount] <= isrCount)
      {
        currBuffer |= (1<<loopCount+(ledNumber*3));
      }
      else
      {
        currBuffer &= ~(1<<loopCount+(ledNumber*3));
      }
      
      loopCount++;
    }
    loopCount=0;
    ledNumber++;
  }
  ledNumber=0;

  digitalWrite(__latchPin, LOW);
  shiftOutTwoBytes(__dataPin, __clockPin, LSBFIRST, currBuffer);
  digitalWrite(__latchPin, HIGH);
  isrCount++;
  
  if(isrCount >= 100)
  {
    isrCount=0;
  }
  
}

void setupTimer() //Set up the timer. 
{
  unsigned int tcnt2;

  TIMSK2 &= ~(1<<TOIE2);


  TCCR2A &= ~((1<<WGM21) | (1<<WGM20));
  TCCR2B &= ~(1<<WGM22);

  ASSR &= ~(1<<AS2);

  TIMSK2 &= ~(1<<OCIE2A);

  TCCR2B |= (1<<CS22)  | (1<<CS21) | (1<<CS20); // Set bits
  //
  //TCCR2B &= ~(1<<CS21);            // Clear bit
  //TCCR2B &= ~(1<<CS20);
  TCCR2B &= ~(1<<CS22); //Clearing only this gives best timing.
  
  TIMSK2 |= (1<<TOIE2);
}

void loop()
{

  //This fades in and out the individual colors in 2 LED's. 
  while(ledLoopNum <=2) //Do first LED
  {
    i=0;
    
    while(i<=100)
    {
      ledStates[0][ledLoopNum]=i;
      i++;
      delay(3);
    }
    i=100;
    while(i>=0)
    {
      ledStates[0][ledLoopNum]=i;
      
      i--;
      delay(3);
    }
    ledLoopNum++;
  }
  ledLoopNum=0;
  while(ledLoopNum <=2) //Do second LED
  {
    i=0;
    
    while(i<=100)
    {
      ledStates[1][ledLoopNum]=i;
      i++;
      delay(3);
    }
    i=100;
    while(i>=0)
    {
      ledStates[1][ledLoopNum]=i;
      i--;
      delay(3);
    }
    ledLoopNum++;
  }
  ledLoopNum=0;
  
}

void shiftOutTwoBytes(uint8_t dataPin, uint8_t clockPin, uint8_t bitOrder, short val)
{
      int i;

      for (i = 0; i < 16; i++)  {
              digitalWrite(dataPin, !!(val & (1 << i)));
            digitalWrite(clockPin, HIGH);
            digitalWrite(clockPin, LOW);            
      }
}

Also, if you see any way to let the ISR run more faster, could you point it out?

Use direct port manipulation. digitalwrite() can be slow.

This link may help .. hardware based SPI PWM control over shift registers.

http://www.arduino.cc/cgi-bin/yabb2/YaBB.pl?num=1278251011/6#6

Also ...

If you want / need more details on direct port write .. this may help ..

//-- update for your pins .. add latch as needed
int clockPin = 12;
int dataPin = 11;

//--- Important: All Pins must be 8 or higher (in PORTB range)
int clockPinPORTB = clockPin - 8;
int dataPinPORTB = dataPin - 8;

void dataTo(byte val){
  bitWrite(PORTB,dataPinPORTB,val) ;
}
void dataOn(){
  bitSet(PORTB,dataPinPORTB);
}
void dataOff(){
  bitClear(PORTB,dataPinPORTB);
}

void clockOn(){
  bitSet(PORTB,clockPinPORTB);
}
void clockOff(){
  bitClear(PORTB,clockPinPORTB);
}

void pulseClock(){
  clockOn(); 
  clockOff(); 
}


void shiftOutVal(byte val) {
  //Send each bit of the myDataOut byte MSBFIRST
  dataOff();
  for (int i=7; i>=0; i--)  {
    clockOff();
    //--- Turn data on or off based on value of bit
    dataTo(bitRead(val,i));
    clockOn();
  }
  clockOff();
}

Thanks guys!

Yea, I looked around on the forums and found a way to shift out 3 bytes, which is essentially what I need. Also, I hardcoded the PORTB bits, and the ISR runs MUCH faster (I can get 8 LEDS running with almost no visable flicker)

The codes a little sloppy, if you have any pointers, I'd love to hear them.
Heres my code:
PS: I have it shift out a byte beforehand as a placeholder for a third shift register. It takes up just as much time to shift out as if there was a shift register after the 2 i have set up)

#define __dataPin 9
#define __clockPin 10
#define __latchPin 11
#define __numLedsInArray 8
#define __numPinsPerLed 3
#define __maxBrightness 100
int ledPin =  13;    // LED connected to digital pin 13
int i=0;
int tcnt2;
int isrCount = 0;
int loopCount=2;
int ledStates[__numLedsInArray][__numPinsPerLed];
int ledNumber = 0;
int ledLoopNum = 0;

int currBuffer = 0;

void setup()   {                
  // initialize the shift reg control pins as outputs:
  pinMode(__dataPin, OUTPUT);
  pinMode(__clockPin, OUTPUT);
  pinMode(__latchPin, OUTPUT);
  Serial.begin(9600);
  
  setupTimer();
  
}

ISR(TIMER2_OVF_vect) {
  /* Reload the timer */
  TCNT2 = 148; //This seems to not flicker very much. 205 for 4 LEDS, 148 for for 8 LEDS (Must shift out 3 bytes, 
  while(ledNumber <= __numLedsInArray-1)
  {
    while(loopCount<=2)
    {
      if(ledStates[ledNumber][loopCount] <= isrCount)
      {
        currBuffer |= (1<<loopCount+(ledNumber*3));
      }
      else
      {
        currBuffer &= ~(1<<loopCount+(ledNumber*3));
      }
      
      loopCount++;
    }
    loopCount=0;
    ledNumber++;
  }
  ledNumber=0;

  //digitalWrite(__latchPin, LOW);
  PORTB &= ~(1<<3);
  shiftOutDedicated((byte)(currBuffer>>16)); //A placeholder for a third byte, so I can time this just right. 
  shiftOutDedicated((byte)currBuffer);
  shiftOutDedicated((byte)(currBuffer>>8));
  //digitalWrite(__latchPin, HIGH);
  PORTB |= (1<<3);
  isrCount++;
  
  if(isrCount >= 100)
  {
    isrCount=0;
  }
  
}

void shiftOutDedicated(byte val) //All hardcoded PORTS. I will add #defines that are golbaly changeable later. 
{
      for (int i = 0; i < 8; i++)  {
          if(!!(val & (1<<i))) //Is there any way I can evaluate wether to turn the bit on and off without an IF statement? That is, evaluate it while declaring PORTB
          {
            PORTB |= (1<<1); //Set the dataPin bit
          }
          else
          {
            PORTB &= ~(1<<1); //Clear the dataPin bit
          }

          PORTB |= (1<<2); //Clock pin high
          PORTB &= ~(1<<2); //Clear clock pin to low
  }
}

void setupTimer() //Set up the timer. 
{
  unsigned int tcnt2;

  TIMSK2 &= ~(1<<TOIE2);


  TCCR2A &= ~((1<<WGM21) | (1<<WGM20));
  TCCR2B &= ~(1<<WGM22);

  ASSR &= ~(1<<AS2);

  TIMSK2 &= ~(1<<OCIE2A);

  TCCR2B |= (1<<CS22)  | (1<<CS21) | (1<<CS20); // Set bits
  //
  //TCCR2B &= ~(1<<CS21);            // Clear bit
  //TCCR2B &= ~(1<<CS20);
  TCCR2B &= ~(1<<CS22); //Clearing only this gives best timing.
  
  TIMSK2 |= (1<<TOIE2);
}

void loop()
{
  int ledNumberID = 0;
  //This fades in and out the individual colors in 2 LED's. 
  while(ledLoopNum <=2)
  {
      i=0;
      while(i<=100)
      {
        ledStates[0][ledLoopNum]=i;
        i++;
        delay(1);
      }
      i=100;
      while(i>=0)
      {
        ledStates[0][ledLoopNum]=i;
        
        i--;
        delay(1);
      }
      ledLoopNum++;
    }
    ledLoopNum=0;
    
    while(ledLoopNum <=2) 
      {
      i=0;
      while(i<=100)
      {
        ledStates[1][ledLoopNum]=i;
        i++;
        delay(1);
      }
      i=100;
      while(i>=0)
      {
        ledStates[1][ledLoopNum]=i;
        
        i--;
        delay(1);
      }
      ledLoopNum++;
    }
    ledLoopNum=0;
    
    while(ledLoopNum <=2)
      {
      i=0;
      while(i<=100)
      {
        ledStates[2][ledLoopNum]=i;
        i++;
        delay(1);
      }
      i=100;
      while(i>=0)
      {
        ledStates[2][ledLoopNum]=i;
        
        i--;
        delay(1);
      }
      ledLoopNum++;
    }
    ledLoopNum=0;
    while(ledLoopNum <=2) 
      {
      i=0;
      while(i<=100)
      {
        ledStates[3][ledLoopNum]=i;
        i++;
        delay(1);
      }
      i=100;
      while(i>=0)
      {
        ledStates[3][ledLoopNum]=i;
        
        i--;
        delay(1);
      }
      ledLoopNum++;
    }
    ledLoopNum=0;

}
i=0;
while (i<=100)
{
    ledStates[0][ledLoopNum]=i;
    i++;
    delay(1);
}

This would look nicer with a for-loop

Thanks for your input. I changed the code actually, it works great. A lot cleaner than what it was.

A while loop, as you originally had it, and the for loop produce the exact same results. The for loop is generally preferred when the number of times to loop is known in advance. The while loop is generally used when the number of iterations is not known in advance (you are waiting for some event to occur, and you have no idea if, or when, that event will occur).

I agree with drhex that the for loop is easier to understand, when the loop count is known. But, the while loop (while(Serial.available() > 0) has it's uses, too.

Thanks, Ill remember that. I code in C++/C a lot, but hardware programming is a little bit different in its style.

Revised code:

#define __dataPin 9
#define __clockPin 10
#define __latchPin 11
#define __numLedsInArray 8
#define __numPinsPerLed 3
#define __maxBrightness 100
#define __delayMultiplier .5
int ledPin =  13;    // LED connected to digital pin 13
int i=0;
int tcnt2;
int isrCount = 0;
int loopCount=2;
int ledStates[__numLedsInArray][__numPinsPerLed];
int ledNumber = 0;
int ledLoopNum = 0;

int currBuffer = 0;

void setup()   {                
  // initialize the shift reg control pins as outputs:
  pinMode(__dataPin, OUTPUT);
  pinMode(__clockPin, OUTPUT);
  pinMode(__latchPin, OUTPUT);
  Serial.begin(9600);
  
  setupTimer();
  
}

ISR(TIMER2_OVF_vect) {
  /* Reload the timer */
  TCNT2 = 148; //This seems to not flicker very much. 205 for 4 LEDS, 148 for for 8 LEDS (Must shift out 3 bytes)
  //New update code:
  for(ledNumber; ledNumber <= __numLedsInArray-1; ledNumber++)
  {
    for(loopCount; loopCount<=2; loopCount++)
    {
      if(ledStates[ledNumber][loopCount] <= isrCount)
      {
        currBuffer |= (1<<loopCount+(ledNumber*3));
      }
      else
      {
        currBuffer &= ~(1<<loopCount+(ledNumber*3));
      }
      
    }
    loopCount=0;
   }
  ledNumber=0;

  /* //Old update code
  while(ledNumber <= __numLedsInArray-1)
  {
    while(loopCount<=2)
    {
      if(ledStates[ledNumber][loopCount] <= isrCount)
      {
        currBuffer |= (1<<loopCount+(ledNumber*3));
      }
      else
      {
        currBuffer &= ~(1<<loopCount+(ledNumber*3));
      }
      
      loopCount++;
    }
    loopCount=0;
    ledNumber++;
  }
  ledNumber=0;
  */
  //digitalWrite(__latchPin, LOW);
  PORTB &= ~(1<<3);
  shiftOutDedicated((byte)currBuffer);
  shiftOutDedicated((byte)(currBuffer>>8));
  shiftOutDedicated((byte)(currBuffer>>16));
  //digitalWrite(__latchPin, HIGH);
  PORTB |= (1<<3);
  isrCount++;
  
  if(isrCount >= 100)
  {
    isrCount=0;
  }
  
}

void shiftOutDedicated(byte val)
{
      for (int i = 0; i < 8; i++)  {
          if(!!(val & (1<<i)))
          {
            PORTB |= (1<<1); //Set the dataPin bit
          }
          else
          {
            PORTB &= ~(1<<1); //Clear the dataPin bit
          }
         //digitalWrite(dataPin, !!(val & (1 << i)));
          PORTB |= (1<<2); //Clock pin high
          PORTB &= ~(1<<2); //Clear clock pin to low
            //digitalWrite(clockPin, HIGH);
            //digitalWrite(clockPin, LOW);            
  }
}

void setupTimer() //Set up the ISR. 
{
  unsigned int tcnt2;

  TIMSK2 &= ~(1<<TOIE2);


  TCCR2A &= ~((1<<WGM21) | (1<<WGM20));
  TCCR2B &= ~(1<<WGM22);

  ASSR &= ~(1<<AS2);

  TIMSK2 &= ~(1<<OCIE2A);

  TCCR2B |= (1<<CS22)  | (1<<CS21) | (1<<CS20); // Set bits
  //
  //TCCR2B &= ~(1<<CS21);       
  //TCCR2B &= ~(1<<CS20);
  TCCR2B &= ~(1<<CS22); //Clearing only this gives best timing.
  
  TIMSK2 |= (1<<TOIE2);
}

void loop()
{
  //pattern_ledDebug();
  //pattern_rainbow();
  /*
  easeAllRed(100, 0);
  easeAllGreen(0, 0);
  delay(0);
  easeAllRed(50, 0);
  easeAllGreen(50, 0);
  delay(0);
  easeAllGreen(100, 0);
  easeAllRed(0, 0);
  delay(0);
  easeAllRed(50, 0);
  easeAllGreen(50, 0);
  delay(0);
  */
  easeAllBlue(100, 0);
  easeAllBlue(0, 0);
}    
/*
void pattern_rainbow() //These values will give a roy-g-biv effect. 
{
  
  easeAll(100, 0, 0, 100, 0); //Have not implemented easeAll yet. 
  easeAll(100, 64, 0, 100, 0);
  easeAll(100, 100, 0, 100, 0);
  easeAll(00, 50, 0, 100, 0);
  easeAll(0, 0, 100, 100, 0);
  easeAll(93, 32, 93, 100, 0);
  
}
*/
void pattern_ledDebug() {
   int ledNumberID = 0;
  //This fades in and out the individual colors on the LED's. 
 for(ledNumberID; ledNumberID <= 4; ledNumberID++)
 { 
  while(ledLoopNum <=2) //Do first LED
  {
      i=0;
      while(i<=100)
      {
        ledStates[ledNumberID][ledLoopNum]=i;
        i++;
        delay(1);
      }
      i=100;
      while(i>=0)
      {
        ledStates[ledNumberID][ledLoopNum]=i;
        
        i--;
        delay(1);
      }
      ledLoopNum++;
    }
    ledLoopNum=0;
 }
}

void easeAllRed(int red, int ledToUsePrevValue)
{   
    int redDiff = red - ledStates[ledToUsePrevValue][0];
    int loopsToRun = 100;
    float redStep = (float)redDiff/(float)loopsToRun;
    float redBuffer = ledStates[ledToUsePrevValue][0];
    /* //Uncomment this to debug
    Serial.print("Red is currently: ");
    Serial.println(ledStates[ledToUsePrevValue][0]);
    Serial.print("Red will ease to: ");
    Serial.println(red);
    Serial.print("The number of loops to run is: ");
    Serial.println(loopsToRun);
    Serial.print("redStep: ");
    Serial.println(redStep);
    Serial.print("redDiff: ");
    Serial.println(redDiff);
    */
    for(int i = 0; i<=loopsToRun; i++)
    {
      redBuffer += redStep;
      //Serial.println(redBuffer);
      for(int b = 0; b <= __numLedsInArray; b++)
      {
      ledStates[b][0] = redBuffer;
      //Serial.print("The red LED is at state: ");
      //Serial.println(ledStates[0][0]);
      delayMicroseconds(15);
      }
  }
  for(int b = 0; b <= __numLedsInArray; b++)
  {
  ledStates[b][0] == red;
  }
  //Serial.println("-------");
  return;
}

void easeAllGreen(int green, int ledToUsePrevValue)
{   
    int greenDiff = green - ledStates[ledToUsePrevValue][1];
    int loopsToRun = 100;
    float greenStep = (float)greenDiff/(float)loopsToRun;
    float greenBuffer = ledStates[ledToUsePrevValue][1];
    for(int i = 0; i<=loopsToRun; i++)
    {
      greenBuffer += greenStep;

      for(int b = 0; b <= __numLedsInArray; b++)
      {
      ledStates[b][1] = greenBuffer;
      delayMicroseconds(15);
      }
  }
  for(int b = 0; b <= __numLedsInArray; b++)
  {
  ledStates[b][1] == green;
  }
  //Serial.println("-------");
  return;
}

void easeAllBlue(int blue, int ledToUsePrevValue)
{   
    int blueDiff = blue - ledStates[ledToUsePrevValue][2];
    int loopsToRun = 100;
    float blueStep = (float)blueDiff/(float)loopsToRun;
    float blueBuffer = ledStates[ledToUsePrevValue][2];
    for(int i = 0; i<=loopsToRun; i++)
    {
      blueBuffer += blueStep;
      //Serial.println(blueBuffer);
      for(int b = 0; b <= __numLedsInArray; b++)
      {
      ledStates[b][2] = blueBuffer;
      delayMicroseconds(15);
      }
  }
  for(int b = 0; b <= __numLedsInArray; b++)
  {
  ledStates[b][2] == blue;
  }
  return;
}

Did a little clean up. Completed the R, G, and B easeAll functions.
A little flicker, but what can ya do. I'm going to substitute the 16mHz crystal for a 20mHz one, possibly higher as 20 is the guaranteed max, not the complete top out ;D

If you see anything I can clean up (especially in the ISR), any feedback would be more than great!

Thanks guys!