Pages: [1] 2   Go Down
Author Topic: shiftOutFast - much faster than normal shiftOut  (Read 5821 times)
0 Members and 1 Guest are viewing this topic.
Florida
Offline Offline
Sr. Member
****
Karma: 4
Posts: 431
hookedup!
View Profile
WWW
 Bigger Bigger  Smaller Smaller  Reset Reset

In working to get 16 levels of PWM brightness levels via a normal shift register (74HC595), I quickly hit limits on the number of shiftOut calls in a timer process before the loop fails due to timer interrupt activity.

I created a faster version that writes directly to the PORTD bit and that allowed MANY more shiftOut calls in the interrupt .. which basically means it is much faster.  There may be faster ways .. but this one proves to work better than the built in one.  So if anyone is having speed issues with the shiftOut .. this may help.

Why I do this.  I want to run whatever in the loop and not have my loop attempting to have perfect timing for PWMing.  Putting a long loop in a timer process .. not advised .. hence needing a faster shiftOut.  I do each of the 16 steps in it's own loop and then start over .. not looping the entire thing inside the timer process .. so a very fast process is needed for a tight loop.

This example shows the simples use of a shift register in action and where it hit limits.  Notice the shiftOut can run 6 shift registers

Code:
/***************************************************************
  Name    : shiftOutFast
  By      : Joseph Francis

  Shows the difference between shiftOut and shiftOutFast
  
  Date    : 02 Oct, 2009                                      
  Version : 1.0                                              
  Notes   : Code for using a 74HC595 Shift Register          
          : to count from 0 to 255 Really Fast                
****************************************************************/

#include <TimerOne.h>

int tempCounter = 0;
//--- Using standard shiftOut:
// at 1 Shift Register - 225 works
// at 2 Shift Registers - 225 fails, 275 works ..
// at 3 Shift Registers - 275 fails, 325 fails, 375 works ..
// at 4 Shift Registers - 375 fails, 425 fails, 475 fails, 525 works ..
// at 5 Shift Registers - 525 fails, 625 works ..
// at 6 Shift Registers - 625 fails, 725 works ..
// --- Rough Pattern Developing Here ...

//--- Using shiftOutFast:
// at 1 Shift Register - 25 fails, 50 works
// at 2 Shift Register - 50 fails, 75 works
// at 3 Shift Register - 75 fails, 100 works
//...
// at 6 Shift Registers - 200 fails, 225 works ..
// --- Rough Pattern Developing Here ...

int timerDelay = 225;

//Pin connected to ST_CP of 74HC595
int latchPin = 8;
//Pin connected to SH_CP of 74HC595
int clockPin = 12;
////Pin connected to DS of 74HC595
int dataPin = 11;

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



void setup() {
  randomSeed(analogRead(0));
  Serial.begin(9600);
  //set pins to output because they are addressed in the main loop
  pinMode(latchPin, OUTPUT);
  pinMode(clockPin, OUTPUT);
  pinMode(dataPin, OUTPUT);

  Timer1.initialize(timerDelay); // Timer for updating pwm pins
  Timer1.attachInterrupt(iProcess);
}

void iProcess(){
  byte data1 = B11111111;
  byte data2 = B11111110;
  byte data3 = B11111110;
  byte data4 = B11111111;
  byte data5 = B11111111;
  byte data6 = B11111111;
  byte data7 = B11111111;
  byte data8 = B11111111;

// This took a timer of 725 to work
/*  
  digitalWrite(latchPin, LOW);
  shiftOut(dataPin, clockPin, MSBFIRST, data6);  
  shiftOut(dataPin, clockPin, MSBFIRST, data5);  
  shiftOut(dataPin, clockPin, MSBFIRST, data4);  
  shiftOut(dataPin, clockPin, MSBFIRST, data3);  
  shiftOut(dataPin, clockPin, MSBFIRST, data2);  
  shiftOut(dataPin, clockPin, MSBFIRST, data1);  
  digitalWrite(latchPin, HIGH);
*/

//--- This code can run using a 200 timer delay
  latchOff();
/*
*/
  shiftOutFast(dataPin, clockPin, data6);  
  shiftOutFast(dataPin, clockPin, data5);  
  shiftOutFast(dataPin, clockPin, data4);  
  shiftOutFast(dataPin, clockPin, data3);  
  shiftOutFast(dataPin, clockPin, data2);  
  shiftOutFast(dataPin, clockPin, data1);  
  latchOn();
}

void loop() {
  tempCounter++;
  if( tempCounter > 100)
    tempCounter = 0;
    
  Serial.print("Hi am here ");
  Serial.println(tempCounter, DEC);
  
  delay(500);
}

//--- shiftOutFast - Shiftout method done in a faster way .. needed for tighter timer process
void shiftOutFast(int myDataPin, int myClockPin, byte myDataOut) {
  //=== This function shifts 8 bits out MSB first much faster than the normal shiftOut function by writing directly to the memory address for port
  //--- clear data pin
  dataOff();

  //Send each bit of the myDataOut byte MSBFIRST
  for (int i=7; i>=0; i--)  {
    clockOff();
    //--- Turn data on or off based on value of bit
    if ( bitRead(myDataOut,i) == 1) {
      dataOn();
    }
    else {      
      dataOff();
    }
    //register shifts bits on upstroke of clock pin  
    clockOn();
    //zero the data pin after shift to prevent bleed through
    dataOff();
  }
  //stop shifting
  digitalWrite(myClockPin, 0);
}

void dataOff(){
 bitClear(PORTB,dataPinPORTB);
}

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

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

void dataOn(){
 bitSet(PORTB,dataPinPORTB);
}

void latchOn(){
 bitSet(PORTB,latchPinPORTB);
}

void latchOff(){
 bitClear(PORTB,latchPinPORTB);
}



Logged

0
Offline Offline
God Member
*****
Karma: 0
Posts: 593
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset

sounds like you should be using hardware SPI instead, software SPI is not good for time critical applications like PWM
Logged

Freelance engineer, consultant, contractor. Graduated from UW in 2013.

Florida
Offline Offline
Sr. Member
****
Karma: 4
Posts: 431
hookedup!
View Profile
WWW
 Bigger Bigger  Smaller Smaller  Reset Reset

Can you provide actual code that runs faster?  Or are you saying I should not be using shift registers?

The sample application is as simple as it gets and I am able to PWM 16 levels with perfect colors on quite a few SRs using this technique.

If you can modify that super simple application to explain what you mean and show it run faster - that would help alot.  Thanks for the pointer.  I need a tighter loop .. I'll look into it more if no one can create a simple example for me to explain.


« Last Edit: October 02, 2009, 10:29:42 am by marklar » Logged

Florida
Offline Offline
Sr. Member
****
Karma: 4
Posts: 431
hookedup!
View Profile
WWW
 Bigger Bigger  Smaller Smaller  Reset Reset

THANKS SO MUCH!  Your tip helped a ton.

This link helped alot:
http://www.arduino.cc/cgi-bin/yabb2/YaBB.pl?num=1215096929

Using that code, I was able to update the code to work.  I think this is a great example of the difference between the speed if digitalWrite vs Direct Port writing and software vs hardware speed differences.

Here is the final code - now I can shift in 6 SR of data with a 20 timer delay - WOW! Thanks man!
Code:
/**************************************************************
  Name    : shiftOutFast
  By      : Joseph Francis

  Shows the difference between shiftOut and shiftOutFast
  
  Date    : 02 Oct, 2009                                      
  Version : 1.0                                              
  Notes   : Code for using a 74HC595 Shift Register          
          : to shove bits into a shift register really fast
          : .. no make that REALLY REALLY fast using hardware SPI
****************************************************************/
#include <TimerOne.h>

int tempCounter = 0;
//--- Using standard shiftOut:
// at 1 Shift Register - 225 works
// at 2 Shift Registers - 225 fails, 275 works ..
// at 3 Shift Registers - 275 fails, 325 fails, 375 works ..
// at 4 Shift Registers - 375 fails, 425 fails, 475 fails, 525 works ..
// at 5 Shift Registers - 525 fails, 625 works ..
// at 6 Shift Registers - 625 fails, 725 works ..
// --- Rough Pattern Developing Here ...

//--- Using shiftOutFast:
// at 1 Shift Register - 25 fails, 50 works
// at 2 Shift Register - 50 fails, 75 works
// at 3 Shift Register - 75 fails, 100 works
//...
// at 6 Shift Registers - 200 fails, 225 works ..
// --- Rough Pattern Developing Here ...

//--- Using shiftOutFast:

int timerDelay = 25;
// at 6 Shift Registers - 10 fails, 25 works well!

//Pin connected to ST_CP of 74HC595
int latchPin = 10;
//Pin connected to SH_CP of 74HC595
int clockPin = 13;
////Pin connected to DS of 74HC595
int dataPin = 11;

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



void setup() {
  randomSeed(analogRead(0));
  Serial.begin(9600);
  //set pins to output because they are addressed in the main loop
  pinMode(latchPin, OUTPUT);
  pinMode(clockPin, OUTPUT);
  pinMode(dataPin, OUTPUT);

  digitalWrite(latchPin,LOW);
  digitalWrite(dataPin,LOW);
  digitalWrite(clockPin,LOW);


  setupSPI();
  
  Timer1.initialize(timerDelay); // Timer for updating pwm pins
  Timer1.attachInterrupt(iProcess);
}

void iProcess(){
  byte data1 = B11111111;
  byte data2 = B11111110;
  byte data3 = B11111110;
  byte data4 = B11111111;
  byte data5 = B11111111;
  byte data6 = B11111111;
  byte data7 = B11111111;
  byte data8 = B11111111;

// This took a timer of 725 to work
/*  
  digitalWrite(latchPin, LOW);
  shiftOut(dataPin, clockPin, MSBFIRST, data6);  
  shiftOut(dataPin, clockPin, MSBFIRST, data5);  
  shiftOut(dataPin, clockPin, MSBFIRST, data4);  
  shiftOut(dataPin, clockPin, MSBFIRST, data3);  
  shiftOut(dataPin, clockPin, MSBFIRST, data2);  
  shiftOut(dataPin, clockPin, MSBFIRST, data1);  
  digitalWrite(latchPin, HIGH);
*/

//--- This code can run using a 200 timer delay :/
/*
  latchOff();
  shiftOutFast(dataPin, clockPin, data6);  
  shiftOutFast(dataPin, clockPin, data5);  
  shiftOutFast(dataPin, clockPin, data4);  
  shiftOutFast(dataPin, clockPin, data3);  
  shiftOutFast(dataPin, clockPin, data2);  
  shiftOutFast(dataPin, clockPin, data1);  
  latchOn();
*/

//--- This code can run using a 20 timer delay! :)
  latchOff();
  spi_transfer(data6);  
  spi_transfer(data5);  
  spi_transfer(data4);  
  spi_transfer(data3);  
  spi_transfer(data2);  
  spi_transfer(data1);  
  latchOn();

}

void loop() {
  tempCounter++;
  if( tempCounter > 100)
    tempCounter = 0;
    
  Serial.print("Hi am here ");
  Serial.println(tempCounter, DEC);
  
  delay(500);
}

//--- shiftOutFast - Shiftout method done in a faster way .. needed for tighter timer process
void shiftOutFast(int myDataPin, int myClockPin, byte myDataOut) {
  //=== This function shifts 8 bits out MSB first much faster than the normal shiftOut function by writing directly to the memory address for port
  //--- clear data pin
  dataOff();

  //Send each bit of the myDataOut byte MSBFIRST
  for (int i=7; i>=0; i--)  {
    clockOff();
    //--- Turn data on or off based on value of bit
    if ( bitRead(myDataOut,i) == 1) {
      dataOn();
    }
    else {      
      dataOff();
    }
    //register shifts bits on upstroke of clock pin  
    clockOn();
    //zero the data pin after shift to prevent bleed through
    dataOff();
  }
  //stop shifting
  digitalWrite(myClockPin, 0);
}

void dataOff(){
 bitClear(PORTB,dataPinPORTB);
}

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

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

void dataOn(){
 bitSet(PORTB,dataPinPORTB);
}

void latchOn(){
 bitSet(PORTB,latchPinPORTB);
}

void latchOff(){
 bitClear(PORTB,latchPinPORTB);
}

void setupSPI(){
  byte clr;
  SPCR |= ( (1<<SPE) | (1<<MSTR) ); // enable SPI as master
  //SPCR |= ( (1<<SPR1) | (1<<SPR0) ); // set prescaler bits
  SPCR &= ~( (1<<SPR1) | (1<<SPR0) ); // clear prescaler bits
  clr=SPSR; // clear SPI status reg
  clr=SPDR; // clear SPI data reg
  SPSR |= (1<<SPI2X); // set prescaler bits
  //SPSR &= ~(1<<SPI2X); // clear prescaler bits

  delay(10);
}
byte spi_transfer(byte data)
{
  SPDR = data;                    // Start the transmission
  while (!(SPSR & (1<<SPIF)))     // Wait the end of the transmission
  {
  };
  return SPDR;                    // return the received byte, we don't need that
}



« Last Edit: October 02, 2009, 03:19:39 pm by marklar » Logged

Offline Offline
Edison Member
*
Karma: 3
Posts: 1001
Arduino rocks
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset

I see you're getting excited about progressing from "super slow" to fast.

Still however I think you could improve this further if you consider the following:

- In the timer interrupt routine (ISR) you should only shift out a single byte (using SPI).
- When sending a single byte there is no need to wait for SPI to complete transfer (i.e. assign data to SPDR directly in the ISR).
- First step in the ISR is to latch the previous byte shifted out.
- If you service more than one shift register (SR) you should do so sequentially.

If you take this on, it would be interesting to get feedback on what PWM frequency you would be able to achieve. The formula for PWM frequency with above logic would be:

PWM_Freq = TimerFreq / Number_of_SRs / Number_of_PWM_levels
Logged

0
Offline Offline
God Member
*****
Karma: 0
Posts: 593
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset

replace
Code:
while (!(SPSR & (1<<SPIF)))     // Wait the end of the transmission
  {
  };

with something much more readable

Code:
while (!(SPSR & (1<<SPIF))); // exact same thing, looks neater

or even better

Code:
loop_until_bit_is_set(SPSR, SPIF);

if that macro is not available, then add #include <avr/sfr_defs.h>

if your program doesn't get too complicated, i think it's ok to have many SPI transmissions inside a timer ISR, provided that you disable the ones built into Arduino's core
« Last Edit: October 02, 2009, 10:05:29 pm by frank26080115 » Logged

Freelance engineer, consultant, contractor. Graduated from UW in 2013.

Florida
Offline Offline
Sr. Member
****
Karma: 4
Posts: 431
hookedup!
View Profile
WWW
 Bigger Bigger  Smaller Smaller  Reset Reset

frank - I had just copied code to see how it worked - cleaner code is nicer .. the following code worked good - thank you.
 
Code:
loop_until_bit_is_set(SPSR, SPIF);


BenF - I am able to easily get to 64 levels of PWM control - for LEDs this is 262K colors (fine for my purpose).  

As for the latch .. are you saying I should be doing this ...?

Code:
   latchOn();
    latchOff();

    spi_transfer(mySR3);
    spi_transfer(mySR2);
    spi_transfer(mySR1);

not ...

Code:
   latchOff();
    spi_transfer(mySR3);
    spi_transfer(mySR2);
    spi_transfer(mySR1);
    latchOn();

?

I never send just one SR and send them back to back as far as I know.  Are you saying spi_transfer .. three times is wrong?  If so, what would be correct?

Thank you both for your replies.
Logged

Offline Offline
Edison Member
*
Karma: 3
Posts: 1001
Arduino rocks
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset

 
Quote
SPDR = data;                    // Start the transmission
while (!(SPSR & (1<<SPIF)))     // Wait the end of the transmission


In the spi_transfer function you wait for the shift out to complete. If you change to only do a single spi_transfer for every timer interrupt you would spend less time in the ISR and could trade this for a higher frequency.

Next time you enter the ISR however you would have to latch the previous value sent. So the sequence in the ISR would be:

- latch values (toggle latch pin on/off)
- spi_transfer without wait  (SPDR = data;)

In your example you do multiple spi_transfers, but only latch the last one. This doesn't make sense to me - what's your intention here? I would have expected to see the following:

Code:
   spi_transfer(mySR3);
    latchOn(SR3);
    latchOff(SR3);
    spi_transfer(mySR2);
    latchOn(SR2);
    latchOff(SR2);
    spi_transfer(mySR1);
    latchOn(SR1);
    latchOff(SR1);

For all practical puproses you may have the speed and level control you need for LED's, but I was interested in seeing the "ultimate" potential. Doing the math however it looks as it would be quite possible to serve 4 8-bit SR's (32 pins) with independent 256 level PWM output on each pin.

Logged

Florida
Offline Offline
Sr. Member
****
Karma: 4
Posts: 431
hookedup!
View Profile
WWW
 Bigger Bigger  Smaller Smaller  Reset Reset

Here is the code .. all libraries disabled and classes needed moved to the main file for easy usage.   This is WAY early in development .. but this code works for me and I am posting for someone that may use it.

That said - comments, errors found, "your nuts" comments, etc .. all welcome. TY all for your help to date.

I will post this as the next (x) responses .. just pull them all into a single file and run (trying to make easy for newbies like me)....

Logged

Florida
Offline Offline
Sr. Member
****
Karma: 4
Posts: 431
hookedup!
View Profile
WWW
 Bigger Bigger  Smaller Smaller  Reset Reset

FIRST PART OF CODE
Code:
// ADD THIS TO RGBCommonLED.h
/*
  RGBCommonLED.h - Common RBG LED Functions
  Joseph Francis

  This library is free software; you can redistribute it and/or
  modify it under the terms of the GNU Lesser General Public
  License as published by the Free Software Foundation; either
  version 2.1 of the License, or (at your option) any later version.

  This library is distributed in the hope that it will be useful,
  but WITHOUT ANY WARRANTY; without even the implied warranty of
  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
  Lesser General Public License for more details.

  You should have received a copy of the GNU Lesser General Public
  License along with this library; if not, write to the Free Software
  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
*/

#ifndef RGBCommonLED_H
#define RGBCommonLED_H

#include "WProgram.h"


class RGBCommonLED {
public:
  RGBCommonLED(int rpin, int gpin, int bpin);
  ~RGBCommonLED();

  //--- methods
  void setRGB(byte r, byte g, byte b);
  void setHSV(byte h, byte s, byte v);
  void setColor(unsigned long v);
  void setHue(byte level);
  void setSat(byte level);
  void setVal(byte level);

  void adjHue(int level);
  void adjSat(int level);
  void adjVal(int level);

  void setRandomColor();
  void setRandomHue();
  void setRandomSat();
  void setRandomVal();

  void (*cmdCallback)(RGBCommonLED &led);
  void initialize(void (*refreshCmd)(RGBCommonLED &led));

  //--- easy for the chip / code
  void setHSVLevel(int section, byte level);
  void adjHSVLevel(int section, int level);
  void setRandomHSVLevel(int section);
  
  //--- Used to update the led pins (or related vars that update on scan)
  void updateLED();

  //--- Conversion routine that loads the "o"s with a value from 0 to 255 for RGVB
  void HSVToRGB( unsigned int inHue, unsigned int inSaturation, unsigned int inValue,
    unsigned int *oR, unsigned int *oG, unsigned int *oB );

  //--- Conversion routine that loads the "o"s with a value from 0 to 255 for S and V
  //    and load oH with a value from 0 to 360 .. use map(returnval,0,360,0,255) to convert
  void RGBToHSV( unsigned int inRed, unsigned int inGreen, unsigned int inBlue,
    unsigned int *oH, unsigned int *oS, unsigned int *oV );

  //--- When needed - this will update the HSV value by converting from the RGB value
  //   currently set
  void updateHSVFromRGB();

  //--- pins for this led, in order R G B
  int pins[3];
  //--- colors for this led, in order R G B
  int rgb[3];
  //--- colors in HSV format, in order H S V
  int hsv[3];

private:
  //--- used to keep from converting when we dont have to (already did - no change);
  boolean hsvSet;
  void setRGBFromHSV();
  void setHSVFromRGB();
  boolean autoRefresh;
};

#endif


// ADD THIS TO RGBCommonLED.cpp
// and unrem this in that file :)
// #include <RGBCommonLED.h>

//<<constructor>>
RGBCommonLED::RGBCommonLED(int rpin, int gpin, int bpin){
  pins[0] = rpin;
  pins[1] = gpin;
  pins[2] = bpin;
  hsvSet = false;
}

void RGBCommonLED::initialize(void (*refreshCmd)(RGBCommonLED &led)){
  this->cmdCallback = refreshCmd;
  autoRefresh = true;
}

//<<destructor>>
RGBCommonLED::~RGBCommonLED(){/*nothing to destruct*/
}

void RGBCommonLED::setColor(unsigned long v)
{
  this->setRGB((v & 255),((v >> 8) & 255),(v >> 16) & 255);
}


void RGBCommonLED::setRGB(byte r, byte g, byte b){
  rgb[0] = r;
  rgb[1] = g;
  rgb[2] = b;
  hsvSet = false;
  if (this->autoRefresh)
     this->cmdCallback(*this);
}

void RGBCommonLED::setHSV(byte h, byte s, byte v){
  hsv[0] = h;
  hsv[1] = s;
  hsv[2] = v;
  this->setRGBFromHSV();
  hsvSet = true;
}


void RGBCommonLED::setHSVLevel(int section, byte level){
  updateHSVFromRGB();
  hsv[section] = level;
  this->setRGBFromHSV();
}

void RGBCommonLED::setHue(byte level){
  this->setHSVLevel(0,level);
}

void RGBCommonLED::setSat(byte level){
  this->setHSVLevel(1,level);
}

void RGBCommonLED::setVal(byte level){
  this->setHSVLevel(2,level);
}

void RGBCommonLED::adjHSVLevel(int section, int level){
  updateHSVFromRGB();
  int th = hsv[section] + level;

  if( th < 0 ){
    th = 255 + th;    
  }
  else if( th > 255 ) {
    th = 255 - th;    
  }

  th = constrain(th,0,255); //just in case?
  hsv[section] = th;
  this->setRGBFromHSV();
}

void RGBCommonLED::adjHue(int level){
  this->adjHSVLevel(0,level);
}

void RGBCommonLED::adjSat(int level){
  this->adjHSVLevel(1,level);
}

void RGBCommonLED::adjVal(int level){
  this->adjHSVLevel(2,level);
}



void RGBCommonLED::RGBToHSV( unsigned int inRed, unsigned int inGreen, unsigned int inBlue,
unsigned int *oH, unsigned int *oS, unsigned int *oV )
{
  //struct Color_HSV HSV; //structure to return
  double vals[3]; //thue = temporary var (linked to hue)
  unsigned char maxc=0, minc=0; //red=0, green=1, blue=2
  double hue, sat, val;

  vals[0]=inRed;
  vals[1]=inGreen;
  vals[2]=inBlue;
  //red is set as maximum and minimum
  if(vals[1]>vals[maxc]) maxc=1; //if green is greater, make green the max.
  if(vals[2]>vals[maxc]) maxc=2; //if blue is greater, make blue the max
  if(vals[1]<vals[minc]) minc=1; //if green is less, make green the min.
  if(vals[2]<vals[minc]) minc=2; //if blue is less, make blue the min.
  val = vals[maxc]; //set the HSV.val to the maximum component value (this is a final value)
  if(vals[maxc]==0) //if maximum component is 0, color must be black
    sat = hue = 0; //so set values to 0 manually to avoid div by 0 errors
  else
  { //otherwise, calculate the values
    sat=255*(1-(vals[minc]/vals[maxc])); //compressed equation - original: HSV.sat=((vals[maxc]-vals[minc])/vals[maxc])*255;
    hue = 60 * ((maxc*2) + (vals[(maxc+1)%3] - vals[(maxc+2)%3])/(vals[maxc] - vals[minc])); //this cannot be simplified - and i havn't got time to explain it
  }
  if(hue < 0) hue += 360; //corrector for hues in -60 to 0 range
  *oH = hue; //map(hue,0,360,0,255);
  *oS = sat;
  *oV = val;
}

void RGBCommonLED::HSVToRGB( unsigned int inHue, unsigned int inSaturation, unsigned int inValue,
unsigned int *oR, unsigned int *oG, unsigned int *oB )
{
  if( inSaturation == 0 )
  {
    // achromatic (grey)
    *oR = *oG = *oB = inValue;
  }
  else
  {
    unsigned int scaledHue = (inHue * 6);
    unsigned int sector = scaledHue >> 8; // sector 0 to 5 around the color wheel
    unsigned int offsetInSector = scaledHue - (sector << 8);      // position within the sector
    unsigned int p = (inValue * ( 255 - inSaturation )) >> 8;
    unsigned int q = (inValue * ( 255 - ((inSaturation * offsetInSector) >> 8) )) >> 8;
    unsigned int t = (inValue * ( 255 - ((inSaturation * ( 255 - offsetInSector )) >> 8) )) >> 8;

    switch( sector ) {
    case 0:
      *oR = inValue;
      *oG = t;
      *oB = p;
      break;
    case 1:
      *oR = q;
      *oG = inValue;
      *oB = p;
      break;
    case 2:
      *oR = p;
      *oG = inValue;
      *oB = t;
      break;
    case 3:
      *oR = p;
      *oG = q;
      *oB = inValue;
      break;
    case 4:
      *oR = t;
      *oG = p;
      *oB = inValue;
      break;
    default:            // case 5:
      *oR = inValue;
      *oG = p;
      *oB = q;
      break;
    }
  }
}

void RGBCommonLED::setRandomColor(){
  this->setColor((unsigned long)random(0x01000000));
}

void RGBCommonLED::setRandomHue(){
  this->setRandomHSVLevel(0);
}
void RGBCommonLED::setRandomSat(){
  this->setRandomHSVLevel(1);
}
void RGBCommonLED::setRandomVal(){
  this->setRandomHSVLevel(2);
}

void RGBCommonLED::setRandomHSVLevel(int section){
  this->setHSVLevel(section, (unsigned int)random(0x0100));
}


void RGBCommonLED::updateHSVFromRGB(){
  //   if (hsvSet)
  //     return;
  this->setHSVFromRGB();  
  hsvSet = true;
}


void RGBCommonLED::updateLED(){
  analogWrite(this->pins[0], rgb[0]);
  analogWrite(this->pins[1], rgb[1]);
  analogWrite(this->pins[2], rgb[2]);
}


void RGBCommonLED::setRGBFromHSV(){
  unsigned int h = hsv[0];
  unsigned int s = hsv[1];
  unsigned int v = hsv[2];
  unsigned int r = 0;
  unsigned int g = 0;
  unsigned int b = 0;
  HSVToRGB(h,s,v,&r,&g,&b);
  this->setRGB(r,g,b);
}

void RGBCommonLED::setHSVFromRGB(){
  unsigned int r = rgb[0];
  unsigned int g = rgb[1];
  unsigned int b = rgb[2];
  unsigned int h;
  unsigned int s;
  unsigned int v;

  this->RGBToHSV(r,g,b,&h,&s,&v);
  hsv[0] = map(h,0,360,0,255);
  hsv[1] = s;
  hsv[2] = v;
  hsvSet = true;
}

« Last Edit: October 03, 2009, 08:17:55 am by marklar » Logged

Florida
Offline Offline
Sr. Member
****
Karma: 4
Posts: 431
hookedup!
View Profile
WWW
 Bigger Bigger  Smaller Smaller  Reset Reset

SECOND PART OF CODE
* Updated at 8:50AM EST US

Code:
/**************************************************************
  Name    : RGB LED Controller Via Shift Registers
  By      : Joseph Francis

  * Open Source

  LED Control with Command Interface
  
  Date    : 02 Oct, 2009                                      
  Version : 1.0                                              
  Notes   : Code for using a 74HC595 Shift Register          
          : to run RGB LEDs
****************************************************************/
#include <TimerOne.h>
//--CMD * REMOVED all command interface stuff so you don't have to pull in that library .. but kept here to see - see forum for the library (search my junk)
//--CMD #include <MsTimer2.h>

//-- LED - Moved LED Class into this file.

//--LED #include <RGBCommonLED.h>
//--CMD #include <SerialCommandReader.h>

int timerDelay = 100;

//--- Pin connected to ST_CP of 74HC595
int latchPin = 10;
//--- Pin connected to SH_CP of 74HC595
int clockPin = 13;
//--- Pin connected to DS of 74HC595
int dataPin = 11;

//--- Used for faster latching
int latchPinPORTB = latchPin - 8;

//======================
//Set this to 0 if not doing grounding multiplexing
int groundMax = 0;

//int isAllowSingleFocus = false;
int cycleHueDelay = 5;
int cycleHueOffset = 20;

boolean isForward = true;
boolean isRandomMode = true;

int patternUpdateMode = 0;
int patternQueue[1][100];
int patternQueuePos = 0;
int patternQueueCount = 0;
int patternQueueDelay = 150;
int patternQueueEndDelay = 0;
boolean patternQueueSlideIn = true;
boolean patternQueueSlideOut = true;
boolean patternQueueMethodAutoIncrement = true;
boolean patternColorAutoIncrement = true;

int patternQueueMethod = 0;

int TICKER_MAX = 64;
int TICKER_STEP = 4;
int ticker = 0;

int patternPos = 0;
const int PATTERN_LED_COUNT = 8;
const int PATTERN_ENTRY_COUNT = 8;
const int PATTERN_ENTRY_MAX = PATTERN_ENTRY_COUNT - 1;

const int PATTERN_APPLY_TO_HUE = 0;
const int PATTERN_APPLY_TO_SAT = 1;
const int PATTERN_APPLY_TO_VALUE = 2;

int PATTERN_COUNT = 2;
byte patterns[][PATTERN_LED_COUNT] = {
    0,50,80,100,140,180,220,255,
    255,0,50,80,100,140,180,220,
    220,255, 0,50,80,100,140,180,
    180,220,255, 0,50,80,100,140,
    140,180,220,255, 0,50,80,100,
    100,140,180,220,255, 0,50,80,
    80,100,140,180,220,255, 0,50,
    50,80,100,140,180,220,255, 0
,
    50,80,100,120,140,180,220,255,
    100,150,200,230,230,200,150,100,
    50,80,100,120,140,180,220,255,
    100,150,200,230,230,200,150,100,
    50,80,100,120,140,180,220,255,
    100,150,200,230,230,200,150,100,
    50,80,100,120,140,180,220,255,
    100,150,200,230,230,200,150,100
,
    255,0,255,0,255,0,255,0,
    0,255,0,255,0,255,0,255,
    255,0,255,0,255,0,255,0,
    0,255,0,255,0,255,0,255,
    255,0,255,0,255,0,255,0,
    0,255,0,255,0,255,0,255,
    255,0,255,0,255,0,255,0,
    0,255,0,255,0,255,0,255
}

;


int groundAt = 0;

int showMode = 0;  //1 for top, 2 for bottom, 0/any for all
int iColor = 0;


// THIS IS THE CODE (UNTESTED) UPDATED TO RUN 8 LEDS RGB's hookedup in order on SR's ...


//--- Here you can write to any of these pins .. a value from 0 to 255 (a PWM value that will be automatically stepped down / adjusted for PWM trimming (i.e. 64 levels only))
byte srPins[24] = {
0,0,0,0,0,0,0,0,0,0,0,0,
0,0,0,0,0,0,0,0,0,0,0,0
};

// THIS IS MY CRAZY FIRST LAYOUT FOR MULTI_GROUND MULTI_PLEX
// This uses two grounds in the shift registers and they are turned on and off back and forth (multiplexed) .. so I have twice as many LEDs available (minus the ground pin count)
// This allows me to reference multiplexed pins as though they were normal - abstracting the usage to a simple "port".

//--- We have 44 virtual pins (0-43) for 2 shift registers - multiplexed via the final two (16 - 2 pins for multiplexing = 22 pins (x2 multiplexed) = 44 virtual pins
//--- broken out into RGB LEDs: 6 leds * 2 grounds = 12 RGB LEDs (plus 8 extra vpins - 4x2)
//--- Connection Details
//---
//--- All SR pins x 2 .. do not use the ones listed

//helmet layout
// S6 = 0,1,2
// S5 = 17,18,19
// S4 = 6,7,9
// S3 = 3,4,5
// S2 = 13,14,15
// S1 = 10,11,12

// T6 = 24,25,26
// T5 = 41,42,43
// T4 = 30,31,33
// T3 = 27,28,29
// T2 = 37,38,39
// T1 = 34,35,36
/*

byte srPins[48] = {
    255,0,0, 255,0,0, 255,0,  
         0,   //do not use - Ground 1
                            0,
    255,0,0, 255,0,0,
         0,   //do not use - Ground 2
    255, 0,255, 0,0,0,0,  

    255,0,0, 255,0,0, 255,0,
         0,   //do not use - Ground 1
                            0,
    255,0,0, 255,0,0,
         0,   //do not use - Ground 2
    255,0,0, 0,0,0,0
};

*/
RGBCommonLED currRGB = RGBCommonLED(0,0,0);

// UNTESTED Setup for 8 LEDS /// RGB Setup
//  This may have to be adjusted depending on actual connections
RGBCommonLED allLEDs[] = {
  RGBCommonLED(0,8,16),
  RGBCommonLED(1,9,17),
  RGBCommonLED(2,10,18),
  RGBCommonLED(3,11,19),
  RGBCommonLED(4,12,20),
  RGBCommonLED(5,13,21),
  RGBCommonLED(6,14,22),
  RGBCommonLED(7,15,23)
};

//--- was a setup to use some off my test "helmet" LOL
/*
RGBCommonLED allLEDs[] = {
  RGBCommonLED(34,35,36),
  RGBCommonLED(37,38,39),
  RGBCommonLED(27,28,29),
  RGBCommonLED(30,31,33),
  RGBCommonLED(41,42,43),
  RGBCommonLED(24,25,26),
  RGBCommonLED(10,11,12),
  RGBCommonLED(13,14,15)
};
*/


//--- Command Processing variables
int mode = 0;

const byte CMD_CHANGE_MODE = 1;
const byte CMD_VAR_SET = 2;
//const byte CMD_PATTERN_DETAIL_SET = 2;

const byte MODE_COLOR = 1;
const byte MODE_RGB_TEST = 2;
const byte MODE_CYCLE_HUE = 3;
const byte MODE_PATTERN_QUEUE = 4;
const byte MODE_PATTERN_ENTRY = 5;
const byte MODE_CYCLE_MODES = 250;

byte currR = 0;
byte currG = 0;
byte currB = 0;

void refreshCurrColor(){
 currRGB.setRGB(currR,currG,currB);
 currRGB.updateHSVFromRGB();
 iColor = currRGB.hsv[0];
}

void changeVars(int eofm){
//--CMD
/*
  switch (SerialCommander.buffer[1]){
    case (1):
      currR = SerialCommander.buffer[2];
      currG = SerialCommander.buffer[3];
      currB = SerialCommander.buffer[4];
      refreshCurrColor();
      break;
    case (2):
      patternQueueMethod = SerialCommander.buffer[2];
      break;
  }
*/
}

void changeMode(int eofm){
//--CMD   mode = SerialCommander.buffer[1];
}

//=================

//--- Passed into the LED on initialize for autorefresh when setRGB used (which is automatically called by setting HSV, HUE, etc as well)
void refreshLED(RGBCommonLED &theLED){
  for (int i = 0 ; i < 3 ; i++ ){
     srPins[theLED.pins[i]] = theLED.rgb[i];
  }
}

void setup() {
  randomSeed(analogRead(0));
  Serial.begin(9600);
  //set pins to output because they are addressed in the main loop
  pinMode(latchPin, OUTPUT);
  pinMode(clockPin, OUTPUT);
  pinMode(dataPin, OUTPUT);

  digitalWrite(latchPin,LOW);
  digitalWrite(dataPin,LOW);
  digitalWrite(clockPin,LOW);

  //--CMD SerialCommander.initialize(runCommands);
  setupSPI();

  //--CMD MsTimer2::set(10, processSerial); // Very fast period for reading serial port data as it reads a byte at a time
  //--CMD MsTimer2::start();
  
  Timer1.initialize(timerDelay); // Timer for updating pwm pins
  Timer1.attachInterrupt(iProcess);

  for ( int i = 0 ; i < PATTERN_LED_COUNT ; i++ ){
    allLEDs[i].initialize(refreshLED);
  }

  setupDefaultPatternQueue();

  iColor = 125; // Start with some color .. no cmd interface running
  mode = MODE_CYCLE_MODES;
}

void processSerial(){
//--CMD     SerialCommander.getSerial();
}

« Last Edit: October 03, 2009, 07:50:28 am by marklar » Logged

Florida
Offline Offline
Sr. Member
****
Karma: 4
Posts: 431
hookedup!
View Profile
WWW
 Bigger Bigger  Smaller Smaller  Reset Reset

THIRD AND FINAL PART OF CODE
* Updated 8:50 AM EST US
* Update was to comment out ground swapping .. this setup has all grounds connected to common ground.  Also updated the max to 0 in the prev code chunk .. since there is no multiplexing going on in this version.

Code:
void runCommands(int cmd, int eob){
 int maxv = eob;
  switch (cmd){
  case CMD_CHANGE_MODE:
    changeMode(maxv);
    break;
  case CMD_VAR_SET:
    changeVars(maxv);
    break;
  default:
    Serial.print("E:CMD:");
    Serial.println(cmd,DEC);
    for (int i = 1 ; i<maxv ; i++ ){
      Serial.print(" - ");
//--CMD       Serial.print(SerialCommander.buffer[i],DEC);
    }
    break;
  }
}

void commandProcessing()
{
  //--- Run anything here you don't want to do in serial timer
}

void iProcess(){
    ticker++;
    if( ticker > TICKER_MAX )
      ticker = 0;
    int myPos = ticker * TICKER_STEP;

    if (showMode == 1){
      groundAt = 0;
    } else if (showMode == 2){
      groundAt = 1;
    } else {
      groundAt++;
      if( groundAt > groundMax )
        groundAt = 0;
    }

    byte mySR1  = 0;
    byte mySR2  = 0;
    byte mySR3  = 0;


    //--- Setup for multiple grounds
    // have groundMax set to 0
    // also have to adjust the commented out code below .. and understand to multiplex
    for (int i = 0 ; i < 8; i++ ){
      int iLED = i;
      int myLev = 0;
      
      if (groundAt == 0){
        myLev = 0;
        if (srPins[i] > myPos)
          myLev = 1;
        bitWrite(mySR1,i,myLev );

        myLev = 0;
        if (srPins[i+8] > myPos)
          myLev = 1;
        bitWrite(mySR2,i,myLev );

        myLev = 0;
        if (srPins[i+16] > myPos)
          myLev = 1;
        bitWrite(mySR3,i,myLev );
          
      } else {
        myLev = 0;
        if (srPins[i+24] > myPos)
          myLev = 1;
        bitWrite(mySR1,i,myLev );

        myLev = 0;
        if (srPins[i+32] > myPos)
          myLev = 1;
        bitWrite(mySR2,i,myLev );

        myLev = 0;
        if (srPins[i+40] > myPos)
          myLev = 1;
        bitWrite(mySR3,i,myLev );

      }
    }


//--- THIS WAS SWAPPING GROUNDS
/*
    //--- I had written over the bits above to whatever value was in the srPort .. but it does not matter
    //--- just set the ground flag as needed
    
    //--- Bit Clear turns GROUND ON - so bitClear is ON and bitSet is OFF
    if (groundAt == 0){
       bitSet(mySR2,0);
       bitClear(mySR3,0);
    } else if (groundAt == 1){
       bitClear(mySR2,0);
       bitSet(mySR3,0);
    } else {
       bitSet(mySR2,0);
       bitSet(mySR3,0);
    }
*/

    latchOn();
    latchOff();

    spi_transfer(mySR3);
    spi_transfer(mySR2);
    spi_transfer(mySR1);

}


void loop() {
  commandProcessing();
  
  int loopTime = 20;

//  mode = MODE_COLOR;
  
  if ( mode == MODE_COLOR ){
    setCurrentColor();
  } else if ( mode == MODE_RGB_TEST ){
    testRGBAll();
  } else if ( mode == MODE_CYCLE_HUE){
    cycleHueAll();
  } else if ( mode == MODE_PATTERN_QUEUE){
    processPatternQueue();
  } else if ( mode == MODE_CYCLE_MODES){
    for (int i = 0 ; i < loopTime * 10 ; i++){
       setCurrentColor();
    }
    testRGBAll();
    for (int i = 0 ; i < loopTime ; i++){
      processPatternQueue();
    }
    for (int i = 0 ; i < loopTime ; i++){
      cycleHueAll();
    }
  } else {
    
  };

  return ;

  for (int i = 0 ; i < loopTime ; i++ ){
  commandProcessing();
    showMode++;
    if( showMode > 2)
      showMode = 0;
    processPatternQueue();
  }

  commandProcessing();
  testRGBAll();
  commandProcessing();

  for (int i = 0 ; i < loopTime ; i++ ){
  commandProcessing();
    showMode++;
    if( showMode > 2)
      showMode = 0;
    cycleHueAll();
  }

}

void latchOn(){
 bitSet(PORTB,latchPinPORTB);
}

void latchOff(){
 bitClear(PORTB,latchPinPORTB);
}

void setupSPI(){
  byte clr;
  SPCR |= ( (1<<SPE) | (1<<MSTR) ); // enable SPI as master
  //SPCR |= ( (1<<SPR1) | (1<<SPR0) ); // set prescaler bits
  SPCR &= ~( (1<<SPR1) | (1<<SPR0) ); // clear prescaler bits
  clr=SPSR; // clear SPI status reg
  clr=SPDR; // clear SPI data reg
  SPSR |= (1<<SPI2X); // set prescaler bits
  //SPSR &= ~(1<<SPI2X); // clear prescaler bits

  delay(10);
}
byte spi_transfer(byte data)
{
  SPDR = data;                    // Start the transmission
  loop_until_bit_is_set(SPSR, SPIF);
  return SPDR;                    // return the received byte, we don't need that
}


void testRGBAll(){
      for ( int iLED = 0 ; iLED < PATTERN_LED_COUNT ; iLED++ ){
       allLEDs[iLED].setRGB(255,0,0);
     };
     delay(1000);
     for ( int iLED = 0 ; iLED < PATTERN_LED_COUNT ; iLED++ ){
       allLEDs[iLED].setRGB(0,255,0);
     };
     delay(1000);
     for ( int iLED = 0 ; iLED < PATTERN_LED_COUNT ; iLED++ ){
       allLEDs[iLED].setRGB(0,0,255);
     };
     delay(2000);

  return;
 
}


void setupDefaultPatternQueue(){
  patternQueue[0][0] = 0;
  patternQueue[0][1] = 1;
  patternQueue[0][2] = 2;
  patternQueue[0][3] = 1;
  patternQueue[0][4] = 0;
  patternQueue[0][5] = 2;

  patternQueueCount = 6;
  patternQueuePos = 0;
 
}



void processPatternQueue(){
  if (patternQueueCount == 0){
    patternQueuePos = 0;
    return;
  };
  patternQueuePos++;

  if ( patternQueuePos > patternQueueCount - 1 ){
    patternQueuePos = 0;
    if( patternQueueMethodAutoIncrement ){
      patternQueueMethod++;
      if (patternQueueMethod > 2)
        patternQueueMethod = 0;
      
    }
  };
  //Currently at this position in the list of pattern - now apply the pattern
  int ledAt = 0;
  int quePattern1 = patternQueue[0][patternQueuePos];
  int quePattern2 = patternQueue[1][patternQueuePos];
  for (int iP = 0 ; iP < PATTERN_LED_COUNT ; iP++){
    if (patternQueueSlideOut && iP != PATTERN_LED_COUNT-1 ){
      for( int iRemain = PATTERN_LED_COUNT - 1 ; iRemain > iP ; iRemain--){
        allLEDs[iRemain].setHSV(allLEDs[iRemain-1].hsv[0],allLEDs[iRemain-1].hsv[1],allLEDs[iRemain-1].hsv[2]);
      }
    }

    int patternSpot = iP;
    int ledUpdateSpot = iP;
    if (patternQueueSlideIn & iP > 0){
      patternSpot = 6 - iP;
      ledUpdateSpot = 0;
      for( int iRemain = iP ; iRemain > 0 ; iRemain--){
        allLEDs[iRemain].setHSV(allLEDs[iRemain-1].hsv[0],allLEDs[iRemain-1].hsv[1],allLEDs[iRemain-1].hsv[2]);
      }
    };


    if( patternColorAutoIncrement ){
      iColor += 10;
      if( iColor > 255 )
        iColor = 0;
    }

    if (patternQueueMethod == PATTERN_APPLY_TO_HUE){
      allLEDs[ledUpdateSpot].setHSV(patterns[patternPos+(quePattern1*8)][patternSpot],255,255);
    } else if (patternQueueMethod == PATTERN_APPLY_TO_SAT){
      allLEDs[ledUpdateSpot].setHSV(iColor,patterns[patternPos+(quePattern1*8)][patternSpot], 255);
    } else {
      allLEDs[ledUpdateSpot].setHSV(iColor,255,patterns[patternPos+(quePattern1*8)][patternSpot]);
    }

    
    delay(patternQueueDelay);
  }
  delay(patternQueueEndDelay);
}


void updateHueOffset(RGBCommonLED &theLED, byte theHue, int theOffset){
    int offset = theHue+theOffset;
    if( offset > 255)
      offset = offset - 255;
    theLED.setHSV(theHue+offset, 255, 255);
}

void cycleHueAll(){
  for( int i = 0 ; i < 256 ; i += 1 ){
  commandProcessing();
     for ( int iLED = 0 ; iLED < PATTERN_LED_COUNT ; iLED++ ){
        updateHueOffset(allLEDs[iLED], i, iLED*cycleHueOffset);
     }
     delay(cycleHueDelay);
   }
};

void setCurrentColor(){
     for ( int iLED = 0 ; iLED < PATTERN_LED_COUNT ; iLED++ ){
        allLEDs[iLED].setRGB(currR,currG,currB);
     }
};



« Last Edit: October 03, 2009, 08:03:21 am by marklar » Logged

Offline Offline
Edison Member
*
Karma: 3
Posts: 1001
Arduino rocks
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset

Looking at your code briefly, I realize you have wired the SR'rs sequentually (output from SR1 shifts to input of SR2 etc.). I envisioned you had the CLOCK and DATA pin for every shift register shared (wired together), but using the ENABLE pin's to control the SR's individually.

Either way would work with a single no wait spi_transfer in the ISR, but in your case you would need a counter to only latch every 3rd time through the ISR.
Logged

Florida
Offline Offline
Sr. Member
****
Karma: 4
Posts: 431
hookedup!
View Profile
WWW
 Bigger Bigger  Smaller Smaller  Reset Reset

BenF - I follow you now.  You can just "fill" one SR in the loop (VERY FAST LOOP) and then latch as needed when all SR's are all full .. as to not need to adjust my loop timing for number of SR's .. and to do the smallest workload I can in the loop (I think smiley )  Also, using this method I can assume the process is going to complete before my next timer loop hits .. so no need to wait for the SPI as well?

At the same time I am computing if the PWM value is in range to turn on or off based on the srPort .. but I don't think that would effect much if anything the eye would see.  And in fact .. that added load means chunking each SR's load into another timer interval may make more sense.  I really need to do that check in the timer as I need perfect timing for correct colors (which I have now .. and can control perfectly).  I found I could add ground intervals that are blank making for a nice "power saver" feature.  So my mobile LED suit can have 4 or 5 levels of power saver modes.  I have tested multiple modes and it works good.

Just another finding in my first cpl months playing with this stuff .. meant for newbies really .. lots of ppl seem to want to control LEDs and using shift registers seems popular .. hope this helps others.


Thanks again for reading through the code and providing me with GREAT details .. it makes lots of sense and I'll play with that more.
« Last Edit: October 03, 2009, 08:00:29 am by marklar » Logged

Offline Offline
Edison Member
*
Karma: 3
Posts: 1001
Arduino rocks
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset

Quote
Also, using this method I can assume the process is going to complete before my next timer loop hits .. so no need to wait for the SPI as well?

Exactly - no we're in sync.

Diving into optimizing for speed, you may also want to stop using int's (16 bit signed) where a char (signed 8 bit) or byte (unsigned 8 bit) is suffcient. For an 8-bit mcu this actually makes a difference (half the code, twice the speed). Also using the "const " prefix (or #define) for declarations that are indeed constant consumes less resources and makes the code more readable.

Have fun!   smiley
« Last Edit: October 03, 2009, 08:18:59 am by borref » Logged

Pages: [1] 2   Go Up
Jump to: