I'm running out of pins!

I am working on a project that communicates via MIDI to a synth. I have just finished figuring out how to use a common anode 3-color (red, green, orange) LED bar graph, and I’ve already figured out that I’m running low on pins. It uses 12 pins; 10 for sinking the current of the LEDs, and 2 for driving 2 transistors that source the current for the LEDs. Yes, I have heard of charlieplexing; I don’t want to do that here though. I still need to add: a Wii Nunchuck (I2C), LCD display (requires asynchronous serial communication (1 pin; Tx on the Arduino, or softserial…?), or if I must, I can use it in parallel comm. mode), about two to four buttons, a potentiometer, a piezo, and I need to 2 pins for serial communication at 31,250baud for MIDI IN and OUT. I have a few HC595’s, and I have more Arduinos I can interface to. What is my best option? Is it possible to use the ‘ShiftOut()’ function with an analog pin? I know how a master/slave configuration works, but how do I communicate between the two if the master’s Tx/Rx pins are being used by MIDI/a serial LCD?? I just read up on SPI by the way, if that helps. I’ve on;y dealt with asynchronous communication before (although I have used an HC595 to control some LEDs. Help!

Have you heard of the Arduino MEGA? http://arduino.cc/en/Main/ArduinoBoardMega

And, if the price is too high for you when it comes to getting a mega, try waiting for when Liquidware has some more Illuminato’s in stock.

Welcome to the wonderful world of microcontrollers where there never seems to be quite enough pins or memory or clock speed :wink:

Now you know why women feel that they can never be too thin or too young :wink:

Lefty

i think it’s ok to use the analog pins as digital pins for shifting out to 595 shift registers.

Would it be best to use I2C? So, the master could control the LED bar graph, the MIDI data (using Tx and Rx), and the I2C bus (communicating w/ the slave and the Wii Nunchuck), and the slave will interface with the LCD display, potentiometer, piezo, and buttons. Would this work? I can include the Arduino slave on the I2C bus, along with the Wii Nunchuck. The thing is, I have absolutely no experience with I2C (except for when I fut together a project using the I2C code for the BlinkM, but I just copied and pasted that…

Perhaps an LED driver ?
http://www.arduino.cc/en/Tutorial/LEDDriver

Ok, knowing the answer to this will help me solve this problem: can the 74HC595 shift register IC SINK current as well? I.e. have the LEDs hooked up to +5V, and the 74HC595 brings them to GROUND when needed. I have only seen it with LEDs hooked up to GND, and given the supply voltage via the chip…

http://www.microchip.com/wwwproducts/Devices.aspx?dDocName=en023499

MCP23017 16 port I2C I/O expander. Provides general purpose I/O with selectable pullups, pin change interrupt, address lines for up to 8 of the devices on one I2C bus.

Cost: $1.58 for 1 to 24, $1.00 for 25 to 99 from Digikey.
http://search.digikey.com/scripts/DkSearch/dksus.dll?Detail&name=MCP23017-E/SP-ND

can the 74HC595 shift register IC SINK current as well?

Yes, it can. It has standard TTL “totem-pole” output drivers that can both source and sink current.

Yay! Thanks Anachrocomputer. So all I would need to do to get the LEDs to light is reverse the bits (i.e. 11111111 = all off assuming 8 LEDs connected, and 00000000 = all on)? I’m not sure how to tell the HC595 to set high impedance, but that won’t be needed, will it? Right now, my arduino sinks current (turns them ON) of up to 20 LEDs at once on 10 pins by driving one of the 10 LOW (it’s a common anode LED bar graph).

Yes, if you have LEDs and resistors wired from Vcc to a 595, you need to set the pin HIGH to switch off the LED and LOW to switch it on.

Ok. I have just one more question for now; it’s simple. I bought, as I mentioned before, a common anode, 10-LED (actually in the package, it has 10 red, 10 green, and of course you can create orange by lighting the two). It has two common pins, 1 for red, 1 for green. I am not sure how to wire these displays with the right values of resistors. Here’s the info on them: http://www.futurlec.com/LED/LEDBARMULTI.shtml. Can I put one resistor at each common pin, and I’ll be fine? Or do I have to put one at each cathode - —|<|— + of each of the 10 LEDs? How do I calculate these values?

FYI:
When I wired these displays right to my arduino, using a BC548 NPN transistor to turn each color ON/OFF (AKA tie the two COMMON pins to ground), and driving any of the 10 digital pins LOW to sink the current of each individual LED. The collector of each transistor was first connected to ~220ohms before +5V.

Thanks a bunch!

Can I put one resistor at each common pin, and I’ll be fine?

No, not unless you only ever light up just one LED at a time. If you want to light up more than one LED at a time, you will need one resistor per LED.

Or do I have to put one at each cathode

Yes.

How do I calculate these values?

Standard LED resistor calculation; it’s been discussed very frequently on the forums. The usual value for a 5V supply and a typical LED is 220 to 330 Ohms.

Now you know why women feel that they can never be too thin or too young

Or have to many shoes :slight_smile:

Ok. Fixed the resistance problem; thanks!

Here’s a new challenge: I now have 3 HC595’s daisy-chained together, and they are connected to TWO of the displays, and 4 transistors to control the red/green COMMON pins for each display (this totals 24 pins, which is JUST enough for the three shift registers. I have found it MIND-BOGGLINGLY difficult to address the shift registers in such a way that I can easily and efficiently turn on/off any specific LED(s) I want to. Here’s a run-down of the setup I have:

24-bit value is sent (clocked out) to the HC595’s, 8 bits (one byte) at a time, MSB first. The 4 most significant bits are the transistors. The rest of the bits (20 more) represent the LEDs. I HATE the fact that arduino can’t deal with binary constants bigger than 1 byte (or CAN it??). AKA ‘B11111111’ = DEC 255; but I guess I couldn’t go ‘B101101110011011010001111’? It like turns the text red to let you know IT knows it’s a BIN representation.

Should I just control the transistors with 4 pins on the arduino instead of having them hooked up to the shift registers and be addressed? I can see that making this much much less confusing. But then there’s the problem of using up a lot of pins… :-/

I’ll include my code, but since it’s late, I’m not going to bother cleaning it up lol. It is a MESS, I know, and it includes a bunch of irrelevant stuff, but maybe it’ll help if everyone can see it.

#include <Wire.h>   // For I2C
/* 
NOTES: Shift Register 1 (MAIN): 
       Q0-Q7: LEDs 1-8 Bar 1 
       Shift Register 2:
       Q0-Q1: LEDs 9-10 Bar 1
       Q2-Q7: LEDs 1-6 Bar 2
       Shift Register 3:
       Q0-Q3: LEDs 7-10 Bar 2
       Q4-Q7: RED1, GREEN1, RED2, GREEN2
       
       All ON: 111100000000000000000000 (BIN) OR ____ (DEC)
       MSB _   _   _   _   _   _   _   _   _   _   _   _   _   _   _   _   _   _   _   _   _   _   _   _ LSB
           2   2   2   2   2   2   2   2   1   1   1   1   1   1   1   1   0   0   0   0   R1  G1  R2  G2
var + var is the same as var (logic) AND var so... B1010 + B1111 = 1101
*/
//variables setup

byte incomingByte;
byte note;
byte velocity;

byte data[3];
long disp;
byte color;
/*
'color' variable:
MSB _      _     _     _ LSB
   Red1 Green1 Red2 Green2
NOTE: '0' = ON, '1' = OFF (Common ANODE display) applies to ALL of the HC595's outputs
*/

int statusLed = 13;   // select the pin for the LED
int pot = 2;       // Analog 2
int clockPin = 2; // Digital 2 ; SH_CP on Shift Register      
int latchPin = 3; // Digital 3 ; ST_CP on Shift Register  
int dataPin = 4;    // Digital 4 ; DATA on Shift register 

int action=2; //0 =note off ; 1=note on ; 2= nada
int i;       // Generic counter (large)
byte f;      // Generic counter (small)
int potA;
int pause;
int pin;

//boolean bit = 1;

//setup: declaring iputs and outputs and begin serial
void setup() {
  pinMode(statusLed,OUTPUT);   // declare the LED's pin as output
  pinMode(latchPin, OUTPUT);
  LEDSetup();
  //start serial with midi baudrate 31250 or 38400 for debugging
  //Serial.begin(31250); 
  Serial.begin(9600);
  digitalWrite(statusLed,HIGH);  
}

//loop: wait for serial data, and interpret the message
void loop () {
  //Clear(bit);
  //bit = !bit;
  //delay(500);
  //return;
  for(long j = 1; j <= 1048576; j = j * 2) {
    disp = disp + j;
    Write();
    delay(500);
  }
  //Clear(0);
  for(long s = 1; s <= 1048576; s = s * 2) {
    disp = disp - s;
    Write();
    delay(500);
  }
  return;
  Serial.println(disp);
  delay(10000);
   if (Serial.available() > 0) {
    blink(); //once to signal at least something arrived

    // read the incoming byte:
    incomingByte = Serial.read();

    // wait for as status-byte, channel 1, note on
    if (incomingByte == 145){ // note on message starting starting
      // yeaha, we're getting something
      blink();
      blink();
      blink();
    }
  }
  return;
  //*/
  if (Serial.available() > 0) {
    // read the incoming byte:
    incomingByte = Serial.read();

    // wait for as status-byte, channel 1, note on or off
    if (incomingByte== 144){ // note on message starting starting
      action=1;
    }else if (incomingByte== 128){ // note off message starting
      action=0;
    }else if (incomingByte== 208){ // aftertouch message starting
       //not implemented yet
    }else if (incomingByte== 160){ // polypressure message starting
       //not implemented yet
    }else if ( (action==0)&&(note==0) ){ // if we received a "note off", we wait for which note (databyte)
      note=incomingByte;
      playNote(note, 0);
      note=0;
      velocity=0;
      action=2;
    }else if ( (action==1)&&(note==0) ){ // if we received a "note on", we wait for the note (databyte)
      note=incomingByte;
    }else if ( (action==1)&&(note!=0) ){ // ...and then the velocity
      velocity=incomingByte;
      playNote(note, velocity);
      note=0;
      velocity=0;
      action=0;
    }else{
      //nada
    }
  }
}
void Write() {
  data[2] = assnbits(0, 8);
  data[1] = assnbits(8, 16);  
  data[0] = assnbits(16, 24);
  //load the light sequence you want from array
  //ground latchPin and hold low for as long as you are transmitting
    digitalWrite(latchPin, 0);
  //move 'em out
    shiftOut(dataPin, clockPin, data[0]);  
    shiftOut(dataPin, clockPin, data[1]);
    shiftOut(dataPin, clockPin, data[2]); 
  //return the latch pin high to signal chip that it 
  //no longer needs to listen for information
    digitalWrite(latchPin, 1);
  //delay(300);
}
int assnbits(int p, int q) {
  int factor = p;
  byte temp;
  for(p = p; p < q; p++) {
    bitWrite(temp, (p - factor), (bitRead(disp, p)));
  } 
  return temp;
}
void lightShift(int p) {

  //this is line uses a bitwise operator
  //shifting a bit left using << is the same
  //as multiplying the decimal number by two. 
  pin = 1<< p;

  //move 'em out
  shiftOut(dataPin, clockPin, pin);   

}
void shiftOut(int myDataPin, int myClockPin, byte myDataOut) {
  // This shifts 8 bits out MSB first, 
  //on the rising edge of the clock,
  //clock idles low

  //internal function setup
  int i=0;
  int pinState;
  pinMode(myClockPin, OUTPUT);
  pinMode(myDataPin, OUTPUT);

  //clear everything out just in case to
  //prepare shift register for bit shifting
  digitalWrite(myDataPin, 0);
  digitalWrite(myClockPin, 0);

  //for each bit in the byte myDataOut?
  //NOTICE THAT WE ARE COUNTING DOWN in our for loop
  //This means that %00000001 or "1" will go through such
  //that it will be pin Q0 that lights. 
  for (i=7; i>=0; i--)  {
    digitalWrite(myClockPin, 0);

    //if the value passed to myDataOut and a bitmask result 
    // true then... so if we are at i=6 and our value is
    // %11010100 it would the code compares it to %01000000 
    // and proceeds to set pinState to 1.
    if ( myDataOut & (1<<i) ) {
      pinState= 1;
    }
    else {      
      pinState= 0;
    }

    //Sets the pin to HIGH or LOW depending on pinState
    digitalWrite(myDataPin, pinState);
    //register shifts bits on upstroke of clock pin  
    digitalWrite(myClockPin, 1);
    //zero the data pin after shift to prevent bleed through
    digitalWrite(myDataPin, 0);
  }

  //stop shifting
  digitalWrite(myClockPin, 0);
}
//blinks both registers based on the number of times you want to 
//blink "n" and the pause between them "d"
//starts with a moment of darkness to make sure the first blink
//has its full visual effect.
void blinkAll_2Bytes(int n, int d) {
  digitalWrite(latchPin, 0);
  shiftOut(dataPin, clockPin, 0);
  shiftOut(dataPin, clockPin, 0);
  digitalWrite(latchPin, 1);
  delay(200);
  for (int x = 0; x < n; x++) {
    digitalWrite(latchPin, 0);
    shiftOut(dataPin, clockPin, 255);
    shiftOut(dataPin, clockPin, 255);
    digitalWrite(latchPin, 1);
    delay(d);
    digitalWrite(latchPin, 0);
    shiftOut(dataPin, clockPin, 0);
    shiftOut(dataPin, clockPin, 0);
    digitalWrite(latchPin, 1);
    delay(d);
  }
}
void Clear(boolean allLEDState) {
  digitalWrite(latchPin, 0);
  if(allLEDState == 1) {
    data[0] = B11110000;       // ALL RED & GREEN Transistors ON
  }
  else if(allLEDState == 0) {
    data[0] = B00001111;       // ALL RED & GREEN Transistors OFF
  }         // RGRG
  shiftOut(dataPin, clockPin, data[0]); 
  shiftOut(dataPin, clockPin, 0);
  shiftOut(dataPin, clockPin, 0);  
  digitalWrite(latchPin, 1);
}
void potentio() {
  potA = analogRead(pot);
  constrain(potA, 20, 1023);
  pause = potA * .25;
}
void blink(){
  digitalWrite(statusLed, HIGH);
  delay(100);
  digitalWrite(statusLed, LOW);
  delay(100);
}
void playNote(byte note, byte velocity){
  blink();
  int value=HIGH;
  if (velocity >10){
      value=LOW;
  }else{
   value=HIGH;
  }

 //since we don't want to "play" all notes we wait for a note between 36 & 44
 if(note>=36 && note<44){
   byte myPin=note-34; // to get a pinnumber between 2 and 9
   digitalWrite(myPin, value);
 }

}
void LEDSetup() {
  Clear(0);
  for(f = 0; f < 3; f++) {
    switch(f) {
      case 0:
        disp = 10485760;    //101000000000000000000000 green, green
        break;
      case 1:
        disp = 5242880;    //010100000000000000000000 red, red
        break;
      case 2:
        disp = 15728640;        //111100000000000000000000 orange, orange
        break;
    }
    Write();
    delay(500);
  }
  Clear(0);
}

Thanks a billion for the help!!