Last active
December 24, 2015 01:59
-
-
Save c00kiemon5ter/6727747 to your computer and use it in GitHub Desktop.
controlling a 3x3 led cube through mcp23017 with arduino. Included effects: blink, cube slices, up down, cube sides, zig zag, spin and rain.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/* Cube project throug mcp23017 */ | |
#include <Wire.h> | |
static const byte MCP_ADDR = 0x20; // I2C Address of MCP23017 Chip | |
static const byte GPIO_A = 0x12; // Register Address of Port A | |
static const byte GPIO_B = 0x13; // Register Address of Port B | |
/* global state of gpio ports */ | |
static byte gpio_a; | |
static byte gpio_b; | |
/* Working with a 3x3 Cube */ | |
static const unsigned CUBE_SIDE = 3; | |
/* each pin belongs in a bank and has a mask */ | |
struct pin { | |
const byte gpio; | |
const byte mask; | |
}; | |
/* 3 Levels of 3x3=9 leds each */ | |
static const struct pin LVL_0 = { GPIO_B, 0b00000001 }; | |
static const struct pin LVL_1 = { GPIO_B, 0b00000100 }; | |
static const struct pin LVL_2 = { GPIO_B, 0b00000010 }; | |
static const struct pin levels[CUBE_SIDE] = { LVL_0, LVL_1, LVL_2 }; | |
/* 3x3=9 columns in a grid - COL_<y><x> */ | |
static const struct pin COL_00 = { GPIO_B, 0b00100000 }; | |
static const struct pin COL_01 = { GPIO_B, 0b00001000 }; | |
static const struct pin COL_02 = { GPIO_B, 0b00010000 }; | |
static const struct pin COL_10 = { GPIO_A, 0b00000100 }; | |
static const struct pin COL_11 = { GPIO_A, 0b00010000 }; | |
static const struct pin COL_12 = { GPIO_A, 0b00001000 }; | |
static const struct pin COL_20 = { GPIO_A, 0b00100000 }; | |
static const struct pin COL_21 = { GPIO_A, 0b01000000 }; | |
static const struct pin COL_22 = { GPIO_A, 0b10000000 }; | |
static const pin columns[CUBE_SIDE][CUBE_SIDE] = { | |
{ COL_00, COL_01, COL_02 }, | |
{ COL_10, COL_11, COL_12 }, | |
{ COL_20, COL_21, COL_22 }, | |
}; | |
/* --- --- --- --- --- --- --- --- --- --- --- --- --- --- --- --- */ | |
/* DEBUG HELPERS */ | |
/* --- --- --- --- --- --- --- --- --- --- --- --- --- --- --- --- */ | |
static void check_mcp(void) { | |
byte error, address; | |
int nDevices; | |
Serial.println("Scanning for I2C..."); | |
nDevices = 0; | |
for(address = 1; address < 127; address++ ) { | |
// The i2c_scanner uses the return value of | |
// the Write.endTransmisstion to see if | |
// a device did acknowledge to the address. | |
Wire.beginTransmission(address); | |
error = Wire.endTransmission(); | |
if (error == 0) { | |
Serial.print("I2C device found at address 0x"); | |
if (address < 16) Serial.print("0"); | |
Serial.println(address, HEX); | |
nDevices++; | |
} else if (error == 4) { | |
Serial.print("Unknow error at address 0x"); | |
if (address < 16) Serial.print("0"); | |
Serial.println(address, HEX); | |
} | |
} | |
if (nDevices == 0) Serial.println("No I2C devices found\n"); | |
else Serial.println("done\n"); | |
} | |
static void print_gpio_state(const byte gpio) { | |
Serial.print("gpio state: "); | |
Serial.println(gpio, BIN); | |
} | |
/* --- --- --- --- --- --- --- --- --- --- --- --- --- --- --- --- */ | |
/* INIT STATE */ | |
/* --- --- --- --- --- --- --- --- --- --- --- --- --- --- --- --- */ | |
void setup(void) | |
{ | |
Wire.begin(); | |
Serial.begin(9600); | |
Wire.beginTransmission(MCP_ADDR); | |
Wire.write(0b00000000); | |
Wire.write(0b00000000); // set all of GPIO A to outputs | |
Wire.write(0b00000000); // set all of GPIO B to outputs | |
Wire.endTransmission(); | |
/* clear all GPIO A pins */ | |
Wire.beginTransmission(MCP_ADDR); | |
Wire.write(GPIO_A); | |
Wire.write(0b00000000); | |
Wire.endTransmission(); | |
/* clear all GPIO B pins */ | |
Wire.beginTransmission(MCP_ADDR); | |
Wire.write(GPIO_B); | |
Wire.write(0b00000000); | |
Wire.endTransmission(); | |
/* initialize randomizer */ | |
randomSeed(analogRead(0)); | |
} | |
/* --- --- --- --- --- --- --- --- --- --- --- --- --- --- --- --- */ | |
/* MAIN EXECUTION */ | |
/* --- --- --- --- --- --- --- --- --- --- --- --- --- --- --- --- */ | |
static inline const byte bit_clear(byte * const gpio, const struct pin pin) { return (*gpio &= ~pin.mask); } | |
static inline const byte bit_set (byte * const gpio, const struct pin pin) { return (*gpio |= pin.mask); } | |
static const byte (* const operation[2])(byte * const gpio, const struct pin pin) = { bit_clear, bit_set }; | |
static void set_pin(const struct pin pin, const bool state) { | |
Wire.beginTransmission(MCP_ADDR); | |
Wire.write(pin.gpio); | |
switch (pin.gpio) { | |
case GPIO_A: Wire.write((operation[state](&gpio_a, pin))); break; | |
case GPIO_B: Wire.write((operation[state](&gpio_b, pin))); break; | |
} | |
Wire.endTransmission(); | |
} | |
/* --- --- --- --- --- --- --- --- --- --- --- --- --- --- --- --- */ | |
static void set_led(const unsigned z, const unsigned y, const unsigned x, const bool state) { | |
set_pin(levels[z], state); | |
set_pin(columns[y][x], state); | |
} | |
static void set_all_lvls(const bool state) { | |
for (unsigned lvl=0; lvl<CUBE_SIDE; ++lvl) | |
set_pin(levels[lvl], state); | |
} | |
static void set_all_cols(const bool state) { | |
for (unsigned y=0; y<CUBE_SIDE; ++y) | |
for (unsigned x=0; x<CUBE_SIDE; ++x) | |
set_pin(columns[y][x], state); | |
} | |
static void set_all(const bool state) { | |
set_all_lvls(state); | |
set_all_cols(state); | |
} | |
/* --- --- --- --- --- --- --- --- --- --- --- --- --- --- --- --- */ | |
static void blink_all(const unsigned d = 500) { | |
set_all(HIGH); | |
delay(d); | |
set_all(LOW); | |
delay(d); | |
} | |
static void zig_zag(const unsigned d = 500) { | |
const struct pin order[CUBE_SIDE * CUBE_SIDE] = { | |
COL_00, COL_01, COL_02, | |
COL_12, COL_11, COL_10, | |
COL_20, COL_21, COL_22, | |
}; | |
set_all_cols(LOW); | |
set_all_lvls(HIGH); | |
for (unsigned col=0; col<CUBE_SIDE*CUBE_SIDE; ++col) { | |
set_pin(order[col], HIGH); | |
delay(d); | |
} | |
} | |
static void spin(const unsigned d = 500) { | |
const struct { | |
struct pin led[CUBE_SIDE]; | |
} order[CUBE_SIDE+1] = { | |
{ { COL_10, COL_11, COL_12 } }, | |
{ { COL_00, COL_11, COL_22 } }, | |
{ { COL_01, COL_11, COL_21 } }, | |
{ { COL_02, COL_11, COL_20 } }, | |
}; | |
set_all_lvls(HIGH); | |
for (unsigned i = 0; i<CUBE_SIDE+1; ++i) { | |
for (unsigned l = 0; l<CUBE_SIDE; ++l) | |
set_pin(order[i].led[l], HIGH); | |
delay(d); | |
for (unsigned l = 0; l<CUBE_SIDE; ++l) | |
set_pin(order[i].led[l], LOW); | |
} | |
} | |
static void spin_outer(const unsigned d = 500) { | |
const struct { | |
struct pin led[2]; | |
} order[CUBE_SIDE+1] = { | |
{ { COL_10, COL_12 } }, | |
{ { COL_00, COL_22 } }, | |
{ { COL_01, COL_21 } }, | |
{ { COL_02, COL_20 } }, | |
}; | |
set_all_lvls(HIGH); | |
for (unsigned i = 0; i<CUBE_SIDE+1; ++i) { | |
for (unsigned l=0; l<2; ++l) | |
set_pin(order[i].led[l], HIGH); | |
delay(d); | |
for (unsigned l=0; l<2; ++l) | |
set_pin(order[i].led[l], LOW); | |
} | |
} | |
static void rain(const unsigned d = 500) { | |
for (unsigned i=0; i<CUBE_SIDE*CUBE_SIDE; ++i) { | |
long x1 = random(CUBE_SIDE); | |
long y1 = random(CUBE_SIDE); | |
long x2 = random(CUBE_SIDE); | |
long y2 = random(CUBE_SIDE); | |
for (int lvl=CUBE_SIDE-1; lvl>=0; --lvl) { | |
set_pin(levels[lvl], HIGH); | |
set_pin(columns[y1][x1], HIGH); | |
set_pin(columns[y2][x2], HIGH); | |
delay(d); | |
set_pin(levels[lvl], LOW); | |
set_pin(columns[y1][x1], LOW); | |
set_pin(columns[y2][x2], LOW); | |
} | |
} | |
} | |
/* --- --- --- --- --- --- --- --- --- --- --- --- --- --- --- --- */ | |
static void set_slice(const unsigned slice, const char axis, const uint8_t state) { | |
switch (axis) { | |
case 'x': | |
set_all_lvls(state); | |
for (unsigned x=0; x<CUBE_SIDE; ++x) | |
set_pin(columns[slice][x], state); | |
break; | |
case 'y': | |
set_all_lvls(state); | |
for (unsigned y=0; y<CUBE_SIDE; ++y) | |
set_pin(columns[y][slice], state); | |
break; | |
case 'z': | |
set_all_cols(state); | |
set_pin(levels[slice], state); | |
break; | |
} | |
} | |
static void cube_slices(const unsigned d = 500) { | |
for (char *p = "xyz"; *p; ++p) { | |
for (unsigned slice=0; slice<CUBE_SIDE; ++slice) { | |
set_slice(slice, *p, HIGH); | |
delay(d); | |
set_slice(slice, *p, LOW); | |
} | |
} | |
} | |
static void up_down(const unsigned d = 500) { | |
const unsigned lvl_order[CUBE_SIDE+1] = { 0, 1, 2, 1, }; | |
/* light all columns */ | |
set_all_cols(HIGH); | |
set_all_lvls(LOW); | |
/* light each level in order */ | |
for (unsigned lvl=0; lvl<CUBE_SIDE+1; ++lvl) { | |
set_slice(lvl_order[(CUBE_SIDE+lvl)%(CUBE_SIDE+1)], 'z', LOW); | |
set_slice(lvl_order[lvl], 'z', HIGH); | |
delay(d); | |
} | |
} | |
static void cube_sides(const unsigned d = 500) { | |
const struct { | |
unsigned slice; | |
char axis; | |
} order[CUBE_SIDE*2] = { | |
{0, 'x'}, {0, 'y'}, {2, 'z'}, | |
{2, 'x'}, {2, 'y'}, {0, 'z'}, | |
}; | |
set_all(LOW); | |
for (unsigned i=0; i<CUBE_SIDE*2; ++i) { | |
set_slice(order[i].slice, order[i].axis, HIGH); | |
delay(d); | |
set_slice(order[i].slice, order[i].axis, LOW); | |
} | |
} | |
/* --- --- --- --- --- --- --- --- --- --- --- --- --- --- --- --- */ | |
void loop(void) | |
{ | |
static const unsigned fast = 150; | |
static const unsigned norm = 400; | |
static const unsigned slow = 600; | |
static struct effect { | |
const unsigned repeat; | |
void (* const effect)(const unsigned interval); | |
const unsigned interval; | |
} effects[] = { | |
{ 2, blink_all, slow }, | |
{ 3, blink_all, norm }, | |
{ 5, blink_all, fast }, | |
{ 2, cube_slices, norm }, | |
{ 3, up_down, norm }, | |
{ 2, cube_sides, norm }, | |
{ 3, zig_zag, fast }, | |
{ 6, spin, fast }, | |
{ 5, spin_outer, fast }, | |
{ 4, rain, fast }, | |
/* -- end of effects -- */ | |
{ 0, NULL, 0 }, | |
}; | |
for (struct effect *e = effects; e; ++e) | |
for (unsigned i=0; i<e->repeat; ++i) | |
e->effect(e->interval); | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment