Go Down

Topic: Multi I/O Library (16 Inputs and Outputs) (Read 6068 times) previous topic - next topic

WilliamK Govinda

Dear All, I just created this free library that allows you to have 16 inputs and/or outputs with 3 I/O pins and a few extra cheap chips.

Here's the download URL:
http://www.wusik.com/download/arduino/Multi_IO.zip

And here's the code: (header, there's no cpp file)

Code: [Select]
/*

www.Wusik.com - Created by WilliamK @ Wusik Dot Com (c) 2010

*/

#ifndef MULTIIO_h
#define MULTIIO_h

#include <inttypes.h>
#include "HardwareSerial.h"

// 16 Inputs with only 3 pins on the Arduino using the following chip: 74HC165N //
// http://www.sparkfun.com/products/9519 //
#define BTploadPin 25      // Buttons Input C165N - Connects to Parallel load pin the 165
#define BTdataPin 27      // Buttons Input C165N - Connects to the Q7 pin the 165
#define BTclockPin 26      // Buttons Input C165N - Connects to the Clock pin the 165

// 16 Outputs with only 3 pins on the Arduino using the following chip: 74HC595 //
// http://www.sparkfun.com/products/733 //
#define LEDlatchPin 22      // LEDs Output C595N //
#define LEDclockPin 24      // LEDs Output C595N //
#define LEDdataPin 23      // LEDs Output C595N //

class Buttons
{
public:
   Buttons()
     {
           pinMode(BTploadPin, OUTPUT);
           pinMode(BTclockPin, OUTPUT);
           pinMode(BTdataPin, INPUT);
           digitalWrite(BTclockPin, LOW);
           digitalWrite(BTploadPin, HIGH);

           memset(Clicked,false,sizeof(Clicked));
           memset(prevHigh,false,sizeof(prevHigh));
     }

     bool Tick() // Returns True if something changed - check the Clicked[] variable and call Reset() after you do //
     {
           SomethingChanged = false;

           digitalWrite(BTploadPin, LOW);
           digitalWrite(BTploadPin, HIGH);
           for(int i = 0; i < 16; i++)
           {
                 tempButton = digitalRead(BTdataPin);
                 if (tempButton == LOW && prevHigh[15-i] == HIGH) { Clicked[15-i] = true; SomethingChanged = true; }
                 prevHigh[15-i] = tempButton;
                 digitalWrite(BTclockPin, HIGH);
                 digitalWrite(BTclockPin, LOW);
           }

           return SomethingChanged;
     }

     void Reset(void) { memset(Clicked,false,sizeof(Clicked)); }

     boolean Clicked[16];

private:
     boolean SomethingChanged;
     unsigned int tempButton;
     unsigned int prevHigh[16];
};

// ======================================================================================= //
class LEDs
{
public:
     LEDs()
     {
           pinMode(LEDlatchPin, OUTPUT);
           pinMode(LEDclockPin, OUTPUT);
           pinMode(LEDdataPin, OUTPUT);    

           Reset();
     }

     void Tick(void) // use the Value[] variable to turn outputs on and off //
     {
           digitalWrite(LEDlatchPin, LOW);
           digitalWrite(LEDdataPin, LOW);
           digitalWrite(LEDclockPin, LOW);
           for (int i=15; i>=0; i--)  
           {
             digitalWrite(LEDclockPin, LOW);
             if (Value[i]) digitalWrite(LEDdataPin, HIGH); else digitalWrite(LEDdataPin, LOW);
             digitalWrite(LEDclockPin, HIGH);
             digitalWrite(LEDdataPin, LOW);
           }
           digitalWrite(LEDclockPin, LOW);
           digitalWrite(LEDlatchPin, HIGH);
     }

     void Reset(void) { memset(Value,false,sizeof(Value)); }

     boolean Value[16];
};

#endif

robtillaart

Good to publish your code here, but some questions arise. It seems to me the lib is for the MEGA (based upon the defines) and unfortunately it has fixed pinnumbers. It would be great if I could define the three pins myself in a constructor.

Furthermore I think the names Buttons and Leds imply a purpose which doesn't need to be the case. iso a led I maybe want to connect relays or so. So a rename to something more generic would be appriciated.

I suggest to call the classes HC165N and HC595 and you could define the pinnumbers in the constructor, something like this:
(not tested)
Code: [Select]

class HC165N
{
public:
  HC165N(int load, int data, int clock)   // byte would be large enough
 {
   _load = load;  // copy to internal vars
   _data = data;
   _clock = clock;

   pinMode(_load, OUTPUT);
   pinMode(_clock, OUTPUT);
   pinMode(_data, INPUT);

   digitalWrite(_clock, LOW);
   digitalWrite(_load, HIGH);

   Reset();  // should also reset prevHigh array I think
   // memset(prevHigh, false, sizeof(prevHigh));
}

etc


If you can define the pins during initialization it becomes possible to have multiple HC165N objects and therfore even more pins!

Similar for the HC595.

But a very good start for a library and I hope you will take time to enhance it and maybe even write a playground article how the lib is used and how to connect the IC's etc.

Rob
Rob Tillaart

Nederlandse sectie - http://arduino.cc/forum/index.php/board,77.0.html -
(Please do not PM for private consultancy)

WilliamK Govinda

Thanks Rob, great feedback. Yes, I will make the changes and upload it again later next week.

How do I start a playground article?

I guess I could use Fritzing for the images?

Wk

robtillaart

#3
Dec 12, 2010, 07:12 pm Last Edit: Dec 12, 2010, 07:13 pm by robtillaart Reason: 1
Quote
How do I start a playground article?

You need to ask for a separate account - you cannot use the forum account - but you may use the same name and password

Quote
I guess I could use Fritzing for the images?

Yes sure

And when redoing the lin consider split current version in a .cpp and a .h file
Rob Tillaart

Nederlandse sectie - http://arduino.cc/forum/index.php/board,77.0.html -
(Please do not PM for private consultancy)


robtillaart


Looks much better, well done!

I still don't understand why you are using BT (button?) and LED prefixes as they assume a usage that not allways is true. On the other hand as these are private I will never have to "see" them in my code.
Rob Tillaart

Nederlandse sectie - http://arduino.cc/forum/index.php/board,77.0.html -
(Please do not PM for private consultancy)

WilliamK Govinda

Ah, doh! indeed, sorry, I will fix that. ;-) (I was a bit tired yesterday when I did those...)

Wk

WilliamK Govinda

Another thing I changed is int to byte, as it uses less RAM and has the same effect.  8-)

int takes 2 bytes while byte takes 1 byte. (as the name suggests)

Wk

WilliamK Govinda

Could some Moderator remove the code from the first post or let me modify it? As I have a new code now that I think is nearly finished. ;-)

Wk

WilliamK Govinda

Here's the official page on our site:

http://www.wusik.com/ww/open-wusik/arduino/download-files

And here are the files.

C165.h
Code: [Select]
/*

     www.Wusik.com - Created by WilliamK @ Wusik Dot Com (c) 2010

     8 to 16 Inputs with only 3 pins on the Arduino using the following chip: 74HC165N
     http://www.sparkfun.com/products/9519

     Typical Usage:

           Setup:

                 C165 Inputs = C165(25,27,26);

                 C165(      Connects to Parallel load pin the 165,
                             Connects to the Q7 pin the 165,
                             Connects to the Clock pin the 165);      

           Loop:

                 if (Inputs.Tick()) // Returns True if something changed //
                 {
                       for (int xc=0; xc<16; xc++)
                       {
                             if (Inputs.isOn(xc))
                             {
                                   // Do Something //
                             }
                       }
                 }

*/

#ifndef C165_h
#define C165_h

#define NumberOfChips 2

#include <inttypes.h>
#include "HardwareSerial.h"

class C165
{
public:
   C165(byte _loadPin, byte _dataPin, byte _clockPin);
     boolean Tick();
     void Reset(void);
     boolean isOn(byte Pos);

private:
     byte loadPin;
     byte dataPin;
     byte clockPin;

     boolean Changed;
     boolean On[NumberOfChips*8];
     byte tempInputs;
     byte prevHigh[NumberOfChips*8];
};

#endif


C165.cpp
Code: [Select]
/*

     www.Wusik.com - Created by WilliamK @ Wusik Dot Com (c) 2010
     Check the C165.h file for instructions

*/

#include "WConstants.h"
#include "C165.h"

// ------------------------------------------------------------------------------------------- //
C165::C165(byte _loadPin, byte _dataPin, byte _clockPin)
{
     Changed = false;

     loadPin = _loadPin;
     dataPin = _dataPin;
     clockPin = _clockPin;

     pinMode(loadPin, OUTPUT);
     pinMode(clockPin, OUTPUT);
     pinMode(dataPin, INPUT);
     digitalWrite(clockPin, LOW);
     digitalWrite(loadPin, HIGH);

     Reset();            
}

// ------------------------------------------------------------------------------------------- //
boolean C165::Tick()
{
     Changed = false;

     digitalWrite(loadPin, LOW);
     digitalWrite(loadPin, HIGH);
     for(int i = 0; i < (NumberOfChips*8); i++)
     {
           tempInputs = digitalRead(dataPin);
           if (tempInputs == LOW && prevHigh[((NumberOfChips*8)-1)-i] == HIGH) { On[((NumberOfChips*8)-1)-i] = true; Changed = true; }
           prevHigh[((NumberOfChips*8)-1)-i] = tempInputs;
           digitalWrite(clockPin, HIGH);
           digitalWrite(clockPin, LOW);
     }

     return Changed;
}

// ------------------------------------------------------------------------------------------- //
void C165::Reset()
{
     memset(On,false,sizeof(On));
     memset(prevHigh,false,sizeof(prevHigh));
}

// ------------------------------------------------------------------------------------------- //
boolean C165::isOn(byte Pos)
{
     if (On[Pos])
     {
           On[Pos] = false;
           return true;
     }

     return false;
}


C595.h
Code: [Select]
/*

     www.Wusik.com - Created by WilliamK @ Wusik Dot Com (c) 2010

     8 to 16 Outputs with only 3 pins on the Arduino using the following chip: 74HC595
     http://www.sparkfun.com/products/733

     Typical Usage:

           Setup:

                 C595 Outputs = C595(22,24,23);

                 C595(Latch Pin, Clock Pin, Data Pin)

           Loop:

                 Outputs.setOutput(0, true);
                 Outputs.setOutput(1, true);
                 Outputs.setOutput(2, false);
                 ...
                 Outputs.setOutput(15, false);
                 
                 Outputs.Tick();

*/

#ifndef C595_h
#define C595_h

#define NumberOfChips 2

#include <inttypes.h>
#include "HardwareSerial.h"

// ======================================================================================= //
class C595
{
public:
     C595(byte _latchPin, byte _clockPin, byte _dataPin);
     void Tick(void);
     void setOutput(byte Pos, boolean Value);
     void Reset(void);

private:
     byte latchPin;
     byte clockPin;
     byte dataPin;
     boolean Output[NumberOfChips*8];
};

#endif


C595.cpp
Code: [Select]
/*

     www.Wusik.com - Created by WilliamK @ Wusik Dot Com (c) 2010
     Check the C595.h file for instructions

*/

#include "WConstants.h"
#include "C595.h"

// ------------------------------------------------------------------------------------------- //
C595::C595(byte _latchPin, byte _clockPin, byte _dataPin)
{
     latchPin = _latchPin;
     clockPin = _clockPin;
     dataPin = _dataPin;

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

     Reset();
}

// ------------------------------------------------------------------------------------------- //
void C595::Tick()
{
     digitalWrite(latchPin, LOW);
     digitalWrite(dataPin, LOW);
     digitalWrite(clockPin, LOW);
     for (int i=15; i>=0; i--)  
     {
       digitalWrite(clockPin, LOW);
       if (Output[i]) digitalWrite(dataPin, HIGH); else digitalWrite(dataPin, LOW);
       digitalWrite(clockPin, HIGH);
       digitalWrite(dataPin, LOW);
     }
     digitalWrite(clockPin, LOW);
     digitalWrite(latchPin, HIGH);
}

// ------------------------------------------------------------------------------------------- //
void C595::Reset(void)
{
     memset(Output,false,sizeof(Output));
}

// ------------------------------------------------------------------------------------------- //
void C595::setOutput(byte Pos, boolean Value)
{
     Output[Pos] = Value;
}

WilliamK Govinda

I'm trying to improve the code so it uses less memory. Please, could you guys take a look at the following thread? Thanks.

http://www.arduino.cc/cgi-bin/yabb2/YaBB.pl?num=1293470091/0

robtillaart

#11
Dec 28, 2010, 09:02 am Last Edit: Dec 28, 2010, 09:16 am by robtillaart Reason: 1
think these first two LOW's can be ommitted,
- clockpin low is also done in the begin of the for loop so not needed.
- datapin is only relevant when clockpin gets high, so no need to init it too

Code: [Select]

void C595::Tick()
{
     digitalWrite(latchPin, LOW);
[glow]      // digitalWrite(dataPin, LOW);
     // digitalWrite(clockPin, LOW);
[/glow]      for (int i=15; i>=0; i--)
     {
       digitalWrite(clockPin, LOW);
       if (Output[i]) digitalWrite(dataPin, HIGH); else digitalWrite(dataPin, LOW);
       digitalWrite(clockPin, HIGH);
       digitalWrite(dataPin, LOW);
     }
     digitalWrite(clockPin, LOW);
     digitalWrite(latchPin, HIGH);
}


Furthermore I notice that you hardcode 15 in the loop while that should be NumberOfChips * 8 -1 ???

Furthermore I would change the boolean array to an unsigned int and use bitset bitclear code

(code not tested but to get an impression)
Code: [Select]

#define NumberOfChips 2
boolean Output[NumberOfChips*8];
==>
unsigned int Output;

void reset()
{
 Output = 0;
}

void setOutput(byte Pos, boolean Value)
{
     Value ? Output | (1UL << Pos) : Output & ~(1UL << Pos);
}

// optional function
boolean getOutput(byte Pos)
{
 return Output & (1UL << Pos);
}
Rob Tillaart

Nederlandse sectie - http://arduino.cc/forum/index.php/board,77.0.html -
(Please do not PM for private consultancy)

WilliamK Govinda

Thanks, I was already taking a look at bitRead and the whole gang. ;-) But thanks again. I already updated my C165 code to use bitRead and now it uses much less memory than before. I will post updated code soon.

Wk


WilliamK Govinda

I still wonder if I could drive all my C165 and C595 with the same clock port?

Wk

Go Up