IOShieldFrom SPCoastThis is a work in progress In particular, the board design is incomplete because of unanswered questions.
[edit] IntroductionA simple I2C based 32-pin Arduino I/O Shield with monitoring LEDs on each pin (Active-LOW inputs, LED is on when pin is grounded, writing a "1" to the port pulls it to ground). Up to 4 boards can be used together, for a total of 128 i/o points, though a larger (1A @ 9v) external power supply could be used to power the I2C IO32 expander/shield stack if things are maxed out. Note: Need to understand 4-pin Grove buckle system, but we need 5 pins to handle both I2C and /INT feedback... [edit] Features
Shield or Daisy chained configurations [edit] CautionsThe total current draw from "Vin" can become excessive if many LEDs are connected and driven - 256 LEDs * 5mA is 1.25A! Each IOShield has its own onboard regulators for IO Port power and onboard LED feedback power which are fed from the Vin Arduino shield pin or the onboard power jack. Note: Need to protect power jack from injecting wrong polarity! [edit] Schematic
[edit] SpecificationThis shield is based on the 8574 I2C IO Expander and the 74LS244 buffer/driver chip connected to a bank of LEDs that show the status of the I/O lines in real time. With 4 sets of chips on board, it provides a latched set of 32 IO points, with each point being software selectable to be either an input or an output. The individual points are reasonable protected from the environment, and can drive or sink 10 (???) mA each. By using active low inputs and outputs, the effects of environmental noise are reduced - remote sensors only need to ground an I/O point to register activity.
[edit] Communication ProtocolSimple I2C Reads and Writes:
int I2Cextender::read8(int i2caddr) {
int _data = -1;
Wire.requestFrom(i2caddr, 1);
if(Wire.available()) {
_data = Wire.receive();
}
return _data;
}
void I2Cextender::write8(int i2caddr, int data)
{
Wire.beginTransmission(i2caddr);
Wire.send(0xff & data);
Wire.endTransmission();
}
[edit] Hardware Installation[edit] ProgrammingLibrary header:
/*
I2Cexpander.h - Library for flashing Morse code.
Created by John Plocher
Released into the public domain.
*/
#ifndef I2Cextender_h
#define I2Cextender_h
#include "WProgram.h"
class I2Cextender {
public:
I2Cextender(void);
void init(int address, int type, int config);
int get(void);
void put(int data);
int getSize(void);
enum {
B_UNKNOWN = 0,
B8 = 8,
B16 = 16,
T_UNKNOWN = 0
};
enum {
PCA9555 = 1,
MCP23016 = 2,
PCF8574 = 3,
PCF8574A = 4
};
enum {
REGISTER_INPUT = 0,
REGISTER_OUTPUT = 2,
REGISTER_INVERT = 4,
REGISTER_CONFIG = 6
};
enum {
base9555 = 0x20,
base23016 = 0x20,
base8574A = 0x38,
base8574 = 0x20
};
private:
int _size;
int _chip;
int _address;
int _config;
void input8(int i2caddr, int dir);
void input16(int i2caddr, int dir);
void write8(int i2caddr, int data);
int read8(int i2caddr);
void write16(int i2caddr, int data);
int read16(int i2caddr);
};
#endif // I2Cextender_h
I2C Library:
#include "WProgram.h"
#include "Wire.h"
#include "I2Cextender.h"
I2Cextender::I2Cextender() {
_address = -1;
_chip = -1;
_config = -1;
}
void I2Cextender::init(int address, int chip, int config) {
_address = address;
_chip = chip;
_config = config;
switch (_chip) {
case I2Cextender::PCA9555:
_size = B16;
input16(base9555 + _address, _config);
break;
case I2Cextender::MCP23016:
_size = B16;
input16(base23016 + _address, _config);
break;
case I2Cextender::PCF8574A:
_size = B8;
input8(base8574A + _address, _config);
break;
case I2Cextender::PCF8574:
_size = B8;
input8(base8574 + _address, _config);
break;
default:
_size=0;
}
}
int I2Cextender::getSize() {
return _size;
}
int I2Cextender::get() {
switch (_chip) {
case I2Cextender::PCA9555: return read16(base9555 + _address);
case I2Cextender::MCP23016: return read16(base23016 + _address);
case I2Cextender::PCF8574A: return read8(base8574A + _address);
case I2Cextender::PCF8574: return read8(base8574 + _address);
default: return -1;
}
}
void I2Cextender::put(int data) {
switch (_chip) {
case I2Cextender::PCA9555: write16(base9555 + _address, data); break;
case I2Cextender::MCP23016: write16(base23016 + _address, data);break;
case I2Cextender::PCF8574A: write8(base8574A + _address, data | _config);break;
case I2Cextender::PCF8574: write8(base8574 + _address, data | _config);break;
default: break;
}
}
int I2Cextender::read8(int i2caddr) {
int _data = -1;
Wire.requestFrom(i2caddr, 1);
if(Wire.available()) {
_data = Wire.receive();
}
return _data;
}
void I2Cextender::write8(int i2caddr, int data)
{
Wire.beginTransmission(i2caddr);
Wire.send(0xff & data);
Wire.endTransmission();
}
void I2Cextender::input8(int i2caddr, int dir) {
}
void I2Cextender::input16(int i2caddr, int dir) {
Wire.beginTransmission(i2caddr);
Wire.send(REGISTER_CONFIG);
Wire.send(0xff & dir); // low byte
Wire.send(dir >> 8); // high byte
Wire.endTransmission();
}
void I2Cextender::write16(int i2caddr, int data) {
Wire.beginTransmission(i2caddr);
Wire.send(REGISTER_OUTPUT);
Wire.send(0xff & data); // low byte
Wire.send(data >> 8); // high byte
Wire.endTransmission();
}
int I2Cextender::read16(int i2caddr) {
int data = 0;
Wire.beginTransmission(i2caddr);
Wire.send(REGISTER_INPUT);
Wire.endTransmission();
// Wire.beginTransmission(i2caddr);
Wire.requestFrom(i2caddr, 2);
if(Wire.available()) {
data = Wire.receive();
}
if(Wire.available()) {
data |= (Wire.receive() << 8);
}
// Wire.endTransmission();
return data;
}
</syntaxhighlight> [edit] ExampleDemo Program:
<syntaxhighlight lang="Arduino">
/*
* Demo of I2C Interface Library usage.
*
* This version simply walks thru the boards/ports and flashes the lights
*/
#include "Wire.h"
#include <I2Cextender.h>
#define INITVAL 0x0000 // Default to all outputs
#define NUMBOARDS 2 // How many boards (1..4)
#if ((NUMBOARDS < 1) || (NUMBOARDS > 4))
#error NUMBOARDS must be a value from 1 to 4
#endif
#define NUMPORTS (NUMBOARDS * 4)
I2Cextender m[NUMPORTS];
int i[4];
void setup()
{
randomSeed(343446); // Note that random() is a ferocious consumer of resources - use a lookup table for psuedorandom stuff is performance matters.
for (int x = 0; x < NUMPORTS; x++) {
m[x] = I2Cextender();
i[x] = random(0,0xFFFF);
}
Wire.begin();
switch (NUMBOARDS) {
case 4: m[15].init(7, I2Cextender::PCF8574A, INITVAL);
m[14].init(6, I2Cextender::PCF8574A, INITVAL);
m[13].init(7, I2Cextender::PCF8574, INITVAL);
m[12].init(6, I2Cextender::PCF8574, INITVAL);
case 3: m[11].init(5, I2Cextender::PCF8574A, INITVAL);
m[10].init(4, I2Cextender::PCF8574A, INITVAL);
m[ 9].init(5, I2Cextender::PCF8574, INITVAL);
m[ 8].init(4, I2Cextender::PCF8574, INITVAL);
case 2: m[ 7].init(3, I2Cextender::PCF8574A, INITVAL);
m[ 6].init(2, I2Cextender::PCF8574A, INITVAL);
m[ 5].init(3, I2Cextender::PCF8574, INITVAL);
m[ 4].init(2, I2Cextender::PCF8574, INITVAL);
case 1: m[ 3].init(1, I2Cextender::PCF8574A, INITVAL);
m[ 2].init(0, I2Cextender::PCF8574A, INITVAL);
m[ 1].init(1, I2Cextender::PCF8574, INITVAL);
m[ 0].init(0, I2Cextender::PCF8574, INITVAL);
}
}
void loop() {
on();
delay(1000);
off();
cylon(8);
off();
walk(8);
off();
rand(64);
off();
delay(500);
}
void walk(int iterations) {
for (int iter = 0; iter <= iterations; iter++) {
for (int x = 0; x < NUMPORTS; x++) {
for (int b = 0; b <= 7; b++) {
m[x].put( ~(1 << b));
delay(50);
}
m[x].put( 0xFFFF);
}
}
}
void cylon(int iterations) {
for (int iter = 0; iter <= iterations; iter++) {
for (int b = 0; b <= 7; b++) {
for (int x = 0; x < NUMPORTS; x++) {
m[x].put( ~(1 << b));
}
delay(50);
}
for (int b = 7; b >= 0; b--) {
for (int x = 0; x < NUMPORTS; x++) {
m[x].put( ~(1 << b));
}
delay(50);
}
}
}
void onoff(int iterations) {
for (int iter = 0; iter <= iterations; iter++) {
for (int x = 0; x < NUMPORTS; x++) {
m[x].put(0);
}
delay(500);
for (int x = 0; x < NUMPORTS; x++) {
m[x].put(0xFFFF);
}
delay(500);
}
}
void off() {
for (int x = 0; x < NUMPORTS; x++) {
m[x].put(0xFFFF);
}
}
void on() {
for (int x = 0; x < NUMPORTS; x++) {
m[x].put(0x0000);
}
}
void rand(int iterations) {
for (int iter = 0; iter <= iterations; iter++) {
int r = random(0,8);
for (int x = 0; x < NUMPORTS; x++) {
if (bitRead(i[x], r)) bitClear(i[x], r); else bitSet(i[x], r);
m[x].put(i[x]);
}
delay(random(80,200));
}
}
[edit] Bill of Materials (BOM) /parts list(These are through hole parts, need to update with SMD parts!)
[edit] Version Tracker
[edit] Original IdeaThis board was originally produced to help me build control panels for my model railroad - I needed to connect many different buttons, switches and lights to an Arduino, and also connect the Arduino to a communications bus (Ethernet, CAN or Loconet), and I couldn't find anything that had both high IO point density AND visual feedback. [edit] Resources
[edit] See AlsoOther related products and resources. [edit] LicensingThis documentation is licensed under the Creative Commons Attribution-ShareAlike License 3.0 Source code and libraries are licensed under GPL/LGPL, see source code files for details. The Eagle CAD files are licensed under the Creative Commons Attribution-NonCommercial-ShareAlike 3.0 Unported License |

