Saturday, 10 March 2012

My DIY Arduino MIDI controller - 3. Finding more pins, part I: the 4051

The first thing I tried when I understood I would need more pins than what I have on my Arduino Uno, was the 4051 multiplexer. The way this chip works is rather simple, the Arduino playground explains it better than I could:

If you use the 4051 as a Multiplexer: You can choose between 8 different inputs and select just one you want to read at the time.
If you use the 4051 as a Demultiplexer you can choose between 8 different outputs and select just one you want to write at the time.

3 of its pins are connected to the Arduino, and by writing these pins HIGH or LOW you select which of the 4051 in-/output pins you are going to read or write. It's a simple binary to decimal conversion. For example if you want to select pin 5 of the 4051, you can refer to the following table:

000 => pin 0
001 => pin 1
010 => pin 2
011 => pin 3
100 => pin 4
101 => pin 5
110 => pin 6
111 => pin 7

That is to say you will write the select pin 1 HIGH, the second LOW and the third HIGH (101 is the binary for 5). Once you've done that you can write or read the common pin.

I first used the 4051 to connect 7 switches & pots to the 8 in-/outputs of the 4051, and I seemed to successfully read from them, but then I tried to use 4 switches (reading) and 4 LEDs (writing) with the same chip and it didn't work the way I expected.

This is the wiring...

The blue wires connect the Arduino to the 4051 select pins. By writing these HIGH or LOW we will tell the 4051 which of its 8 in-/outputs we want to play with.
The orange wires connect the switches to the pins 0-3 of the 4051.
The brown wires connect the red LEDs to the pins 4-7 of the 4051.
The white wire is the one we will use to read/write to the selected 4051 pin.
The green wires connect the yellow LEDs to Arduino pins.
In real life it looks like this



...and the code I used:

int r0;
int r1;
int r2;

//  4051 pins
int commonPin = 8;
int s0 = 2;
int s1 = 4;
int s2 = 7;

//  LEDs directly connected to Arduino pins
int regularLed[] = {9, 10, 11, 12};

boolean switchVal[] = {LOW, 0, 0, 0};
boolean lastSwitchVal[] = {0, 0, 0, 0};
boolean ledState[] = {0, 0, 0, 0};

//  setup
void setup() {
  pinMode(s0, OUTPUT);
  pinMode(s1, OUTPUT);
  pinMode(s2, OUTPUT);

  for (int i = 0; i <= 3; i++) {
    pinMode(regularLed[i], OUTPUT);
  }
  
  Serial.begin(9600);
}

//  loop
void loop () {

  for (int i=0; i<=7; i++) {

    // select the bit  
    r0 = bitRead(i,0);
    r1 = bitRead(i,1);
    r2 = bitRead(i,2);
    digitalWrite(s0, r0);
    digitalWrite(s1, r1);
    digitalWrite(s2, r2);

    //  Read from switches on pins 0 to 3
    pinMode(commonPin, INPUT);
    if (i <= 3) {
      
      //  Read switch
      switchVal[i] = digitalRead(commonPin);

      //  Light Arduino LEDs up accordingly to switch state
      digitalWrite(regularLed[i], switchVal[i]);

      //  If the state of the switch has changed
      if (switchVal[i] != lastSwitchVal[i]) {

        //  Toggle middle LED state if switch is pressed (not if released)
        if (switchVal[i] == 1) toggleLed(i);  //  1 = HIGH
        
        /*  Debug data
        Serial.println("s"+String(i)+" = "+String(int(lastSwitchVal[i]))+">"+String(int(switchVal[i])));
        for (int j = 0; j <= 3; j++) {
          if (j != 3)
            Serial.print(int(ledState[j]));
          else
            Serial.println(int(ledState[j]));
        }*/
        
        //  Save new switch state
        lastSwitchVal[i] = switchVal[i];
      }
    }

   //  As we will be reading from pins 4 to 7 we change the common pin state
   pinMode(commonPin, OUTPUT);

    //  Write to LEDs on pins 4 to 7
    if (i >= 4) {
      digitalWrite(commonPin, ledState[i - 4]);
      /*if (i != 7)
        Serial.print(String(int(ledState[i-4])));
      else
        Serial.println(String(int(ledState[i-4])));*/
      //delay(125);
    }
  }
}

//  toggles ledState[x] 0 / 1
void toggleLed(int l) {
  ledState[l] = -ledState[l] + 1; //  -0 + 1 = 1  |  -1 + 1 = 0
}

What this code does is that when a switch is pressed:
  • The corresponding yellow LED is on (and off when the switch is not pressed), I'm just writing the switch state to the LED.
  • The corresponding middle LED state is toggled on/off (nothing happens on switch release).


The yellow LEDs light up as I press switches 1 & 2 (works with all switches ;)

It worked fine at first when the code was sending serial data to the monitor, but when I commented the serial commands out, it started behaving strange. The reading seemed to be normal but the LEDs were lit in a strange fashion. When I pressed the first switch, both first & second LED turned on, but I knew from my previous try that the LED states were written correctly, because I had send them over the serial port. So it was not due to a mistake in my code.


Switch 1 has been pressed and LEDs 1 & 2 are lit, instead of only LED 1.

How come? I got the answer from the Arduino forums: Grumpy_Mike said
This is because you are writing the digital select lines one at a time and in the process you put false data briefly on the the chip. You need to write all these bits at the same time using direct port access or bit manipulation. But as I said it is totally the wrong chip anyway, use a shift register and don't put in that capacitor they have in the tutorial.
So I guess I'll try to use the 4051 for reading only (pots and LDRs), and I ordered a couple of 74HC595 shift registers from Sparkfun (along with pots, SPDT switches, RGB LEDs, MIDI connectors, resistors, a thumb joystick with its breakout board & solderless headers, wire & heat shrink!).

So next post will be about the 74HC595 8 bit shift register, unless they take too much time to arrive, in which case I could post about the LDR calibration.

<< Previous: 2. The first problems

No comments:

Post a Comment