Initial commit
This commit is contained in:
410
FrameBuffer.cpp
Normal file
410
FrameBuffer.cpp
Normal file
@@ -0,0 +1,410 @@
|
||||
/*
|
||||
* Copyright (c) 2018, Lionel Adrien Sambuc
|
||||
* All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are met:
|
||||
*
|
||||
* 1. Redistributions of source code must retain the above copyright notice,
|
||||
* this list of conditions and the following disclaimer.
|
||||
*
|
||||
* 2. Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the distribution.
|
||||
*
|
||||
* 3. Neither the name of the copyright holder nor the names of its
|
||||
* contributors may be used to endorse or promote products derived from
|
||||
* this software without specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
||||
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
||||
* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
|
||||
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
|
||||
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
|
||||
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
||||
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
|
||||
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
||||
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
||||
* POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
#include <Arduino.h>
|
||||
#include <MsTimer2.h>
|
||||
|
||||
#include "FrameBuffer.h"
|
||||
|
||||
/* All the code assumes dimensions are below 127, as signed chars are used to
|
||||
* store positions and indices. */
|
||||
#define MAX_X 4
|
||||
#define MAX_Y 4
|
||||
#define MAX_Z 4
|
||||
|
||||
#define BRIGHTNESS_LEVELS 16
|
||||
#define BRIGHTNESS_MAX 256
|
||||
|
||||
// Positions (x,y) to pin, same for all layers.
|
||||
static char position2pin[MAX_X][MAX_Y] = {
|
||||
{13, 12, 11, 10},
|
||||
{ 9, 8, 7, 6},
|
||||
{ 5, 4, 3, 2},
|
||||
{ 1, 0, A5, A4}
|
||||
};
|
||||
|
||||
// z-coordinate position to pin.
|
||||
static char layer[MAX_Z] = { A0, A1, A2, A3 };
|
||||
|
||||
static unsigned char font[][4][4] = {
|
||||
// a
|
||||
{
|
||||
{0,0,0,0},
|
||||
{1,1,1,0},
|
||||
{1,0,1,0},
|
||||
{1,1,1,1},
|
||||
},
|
||||
// b
|
||||
{
|
||||
{1,0,0,0},
|
||||
{1,1,1,0},
|
||||
{1,0,1,0},
|
||||
{1,1,1,0},
|
||||
},
|
||||
// c
|
||||
{
|
||||
{0,0,0,0},
|
||||
{1,1,1,0},
|
||||
{1,0,0,0},
|
||||
{1,1,1,0},
|
||||
},
|
||||
// d
|
||||
{
|
||||
{0,0,1,0},
|
||||
{1,1,1,0},
|
||||
{1,0,1,0},
|
||||
{1,1,1,0},
|
||||
},
|
||||
// e
|
||||
{
|
||||
{1,1,1,0},
|
||||
{1,1,1,0},
|
||||
{1,0,0,0},
|
||||
{1,1,1,0},
|
||||
},
|
||||
// f
|
||||
{
|
||||
{1,1,1,0},
|
||||
{1,0,0,0},
|
||||
{1,1,0,0},
|
||||
{1,0,0,0},
|
||||
},
|
||||
// g
|
||||
{
|
||||
{1,1,1,1},
|
||||
{1,0,0,0},
|
||||
{1,0,1,1},
|
||||
{1,1,1,1},
|
||||
},
|
||||
// h
|
||||
{
|
||||
{1,0,0,0},
|
||||
{1,0,0,0},
|
||||
{1,1,1,0},
|
||||
{1,0,1,0},
|
||||
},
|
||||
// i
|
||||
{
|
||||
{0,1,0,0},
|
||||
{0,0,0,0},
|
||||
{0,1,0,0},
|
||||
{0,1,0,0},
|
||||
},
|
||||
// j
|
||||
{
|
||||
{0,0,1,0},
|
||||
{0,0,1,0},
|
||||
{1,0,1,0},
|
||||
{1,1,1,0},
|
||||
},
|
||||
// k
|
||||
{
|
||||
{1,0,0,0},
|
||||
{1,0,1,0},
|
||||
{1,1,0,0},
|
||||
{1,0,1,0},
|
||||
},
|
||||
// l
|
||||
{
|
||||
{0,1,0,0},
|
||||
{0,1,0,0},
|
||||
{0,1,0,0},
|
||||
{0,1,1,0},
|
||||
},
|
||||
// M
|
||||
{
|
||||
{1,0,0,1},
|
||||
{1,1,1,1},
|
||||
{1,0,0,1},
|
||||
{1,0,0,1},
|
||||
},
|
||||
// n
|
||||
{
|
||||
{0,0,0,0},
|
||||
{1,0,1,1},
|
||||
{0,1,0,1},
|
||||
{0,1,0,1},
|
||||
},
|
||||
// o
|
||||
{
|
||||
{0,0,0,0},
|
||||
{1,1,1,0},
|
||||
{1,0,1,0},
|
||||
{1,1,1,0},
|
||||
},
|
||||
// p
|
||||
{
|
||||
{1,1,1,0},
|
||||
{1,0,1,0},
|
||||
{1,1,1,0},
|
||||
{1,0,0,0},
|
||||
},
|
||||
// q
|
||||
{
|
||||
{1,1,1,0},
|
||||
{1,0,1,0},
|
||||
{1,1,1,0},
|
||||
{0,0,1,0},
|
||||
},
|
||||
// r
|
||||
{
|
||||
{1,1,1,0},
|
||||
{1,0,1,0},
|
||||
{1,1,1,0},
|
||||
{1,0,0,1},
|
||||
},
|
||||
// s
|
||||
{
|
||||
{1,1,1,0},
|
||||
{0,1,0,0},
|
||||
{0,0,1,0},
|
||||
{1,1,1,0},
|
||||
},
|
||||
// T
|
||||
{
|
||||
{1,1,1,0},
|
||||
{0,1,0,0},
|
||||
{0,1,0,0},
|
||||
{0,1,0,0},
|
||||
},
|
||||
// u
|
||||
{
|
||||
{0,0,0,0},
|
||||
{1,0,1,0},
|
||||
{1,0,1,0},
|
||||
{1,1,1,0},
|
||||
},
|
||||
// v
|
||||
{
|
||||
{0,0,0,0},
|
||||
{1,0,1,0},
|
||||
{1,0,1,0},
|
||||
{0,1,0,0},
|
||||
},
|
||||
// Ww
|
||||
{
|
||||
{1,0,0,1},
|
||||
{1,0,0,1},
|
||||
{1,1,1,1},
|
||||
{1,0,0,1},
|
||||
},
|
||||
// x
|
||||
{
|
||||
{0,0,0,0},
|
||||
{1,0,1,0},
|
||||
{1,1,1,0},
|
||||
{1,0,1,0},
|
||||
},
|
||||
// y
|
||||
{
|
||||
{0,0,0,0},
|
||||
{1,0,1,0},
|
||||
{0,1,0,0},
|
||||
{0,1,0,0},
|
||||
},
|
||||
// z
|
||||
{
|
||||
{1,1,1,1},
|
||||
{0,0,1,0},
|
||||
{0,1,0,0},
|
||||
{1,1,1,1},
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
#define FRAMEBUFFER_POINTERS 1 /* Either 1 (Enabled) or 0 (disabled) */
|
||||
#if FRAMEBUFFER_POINTERS
|
||||
struct Frame frames[2];
|
||||
|
||||
struct FrameBuffer {
|
||||
unsigned char intensity = 0;
|
||||
Frame *front = &frames[0];
|
||||
Frame *back = &frames[1];
|
||||
} FrameBuffer;
|
||||
#else
|
||||
struct FrameBuffer {
|
||||
unsigned char visible = 0;
|
||||
unsigned char intensity = 0;
|
||||
Frame frames[2];
|
||||
} FrameBuffer;
|
||||
#endif
|
||||
|
||||
extern char position2pin[MAX_X][MAX_Y];
|
||||
extern char layer[MAX_Z];
|
||||
|
||||
void FrameBufferSwitch(void) {
|
||||
#if FRAMEBUFFER_POINTERS
|
||||
Frame * t = FrameBuffer.back;
|
||||
FrameBuffer.back = FrameBuffer.front;
|
||||
FrameBuffer.front = t;
|
||||
#else
|
||||
FrameBuffer.visible = (FrameBuffer.visible + 1) & 0x1;
|
||||
#endif
|
||||
}
|
||||
|
||||
Frame *FrameBufferGetFront(void) {
|
||||
#if FRAMEBUFFER_POINTERS
|
||||
return FrameBuffer.front;
|
||||
#else
|
||||
return &FrameBuffer.frames[FrameBuffer.visible];
|
||||
#endif
|
||||
}
|
||||
|
||||
Frame *FrameBufferGetBack(void) {
|
||||
#if FRAMEBUFFER_POINTERS
|
||||
return FrameBuffer.back;
|
||||
#else
|
||||
return &FrameBuffer.frames[(FrameBuffer.visible + 1) & 0x1];
|
||||
#endif
|
||||
}
|
||||
|
||||
void FrameBufferWrite(char x, char y, char z, unsigned char val) {
|
||||
FrameBufferGetBack()->data[x][y][z] = val;
|
||||
}
|
||||
|
||||
static void letter(char l, char brightness) {
|
||||
memset(FrameBufferGetBack(), 0, FRAME_SIZE());
|
||||
for (char x = 0; x < MAX_X; x++) {
|
||||
for (char y = 0; y < MAX_Y; y++) {
|
||||
FrameBufferGetBack()->data[3][y][3-x] =
|
||||
font[l - 'a'][x][y] * brightness;
|
||||
}
|
||||
}
|
||||
FrameBufferSwitch();
|
||||
delay(200);
|
||||
}
|
||||
|
||||
void printAllSymbols() {
|
||||
for(char c = 0; c < sizeof(font)/sizeof(font[0]); c++) {
|
||||
letter(c+'a', 127);
|
||||
delay(500);
|
||||
}
|
||||
}
|
||||
|
||||
void FrameBufferWriteStr(char * str, const short delayPerLetter,
|
||||
const unsigned char brightness) {
|
||||
unsigned char *c = str;
|
||||
while(*c != 0) {
|
||||
if (*c == ' ') {
|
||||
memset(FrameBufferGetBack(), 0, FRAME_SIZE());
|
||||
FrameBufferSwitch();
|
||||
} else {
|
||||
letter(*c, brightness);
|
||||
}
|
||||
|
||||
delay(delayPerLetter);
|
||||
|
||||
// Make sure there is a white space between font
|
||||
memset(FrameBufferGetBack(), 0, FRAME_SIZE());
|
||||
FrameBufferSwitch();
|
||||
delay(delayPerLetter/4);
|
||||
|
||||
c++;
|
||||
}
|
||||
}
|
||||
|
||||
void FrameBufferRefresh(void) {
|
||||
Frame *f = FrameBufferGetFront();
|
||||
#if BRIGHTNESS_INCREMENT
|
||||
FrameBuffer.intensity =
|
||||
(FrameBuffer.intensity + BRIGHTNESS_INCREMENT) % BRIGHTNESS_MAX;
|
||||
#else
|
||||
FrameBuffer.intensity = 128;
|
||||
#endif
|
||||
|
||||
#if DEBUG_FB_REFRESH
|
||||
static int timeStartRefresh = micros();
|
||||
static int timeLastRefresh = 0;
|
||||
#endif
|
||||
|
||||
for (char z = 0; z < MAX_Z; z++) {
|
||||
// 1. Set up the layer leds to be turned on / off
|
||||
for (char x = 0; x < MAX_X; x++) {
|
||||
for (char y = 0; y < MAX_Y; y++) {
|
||||
if (f->data[x][y][z] > FrameBuffer.intensity) {
|
||||
digitalWrite(position2pin[x][y], HIGH);
|
||||
} else {
|
||||
digitalWrite(position2pin[x][y], LOW);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 2. Turn the power on on the layer
|
||||
digitalWrite(layer[z], LOW);
|
||||
|
||||
// Let it display
|
||||
#if BRIGHTNESS_LEVELS
|
||||
delayMicroseconds(FRAME_PERIOD / (MAX_Z * BRIGHTNESS_LEVELS));
|
||||
#else
|
||||
delayMicroseconds(FRAME_PERIOD / MAX_Z);
|
||||
#endif
|
||||
|
||||
// 3. Turn the power off on the layer
|
||||
digitalWrite(layer[z], HIGH);
|
||||
}
|
||||
|
||||
#if DEBUG_FB_REFRESH
|
||||
Serial.print("Time Since last refresh: ");
|
||||
Serial.println((timeStartRefresh - timeLastRefresh), DEC);
|
||||
|
||||
timeLastRefresh = micros();
|
||||
|
||||
Serial.print("Period Refresh Time: ");
|
||||
Serial.println((timeLastRefresh - timeStartRefresh), DEC);
|
||||
#endif
|
||||
}
|
||||
|
||||
void FrameBufferInit(void) {
|
||||
// Setting rows to ouput
|
||||
for (char x = 0; x < MAX_X; x++) {
|
||||
for (char y = 0; y < MAX_Y; y++) {
|
||||
pinMode(position2pin[x][y], OUTPUT);
|
||||
digitalWrite(position2pin[x][y], LOW);
|
||||
}
|
||||
}
|
||||
|
||||
// Setting layers to output
|
||||
for (char z = 0; z < MAX_Z; z++) {
|
||||
pinMode(layer[z], OUTPUT);
|
||||
digitalWrite(layer[z], HIGH);
|
||||
}
|
||||
|
||||
// Initialize FrameBuffers
|
||||
memset(FrameBufferGetFront(), 0, FRAME_SIZE());
|
||||
memset(FrameBufferGetBack(), 0, FRAME_SIZE());
|
||||
|
||||
#if BRIGHTNESS_LEVELS
|
||||
MsTimer2::set(FRAME_PERIOD / (1000 * BRIGHTNESS_LEVELS),
|
||||
FrameBufferRefresh);
|
||||
#else
|
||||
MsTimer2::set(FRAME_PERIOD / (1000), FrameBufferRefresh);
|
||||
#endif
|
||||
MsTimer2::start();
|
||||
}
|
||||
115
FrameBuffer.h
Normal file
115
FrameBuffer.h
Normal file
@@ -0,0 +1,115 @@
|
||||
#ifndef FRAMEBUFFER_H
|
||||
#define FRAMEBUFFER_H
|
||||
/*
|
||||
* Copyright (c) 2018, Lionel Adrien Sambuc
|
||||
* All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are met:
|
||||
*
|
||||
* 1. Redistributions of source code must retain the above copyright notice,
|
||||
* this list of conditions and the following disclaimer.
|
||||
*
|
||||
* 2. Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the distribution.
|
||||
*
|
||||
* 3. Neither the name of the copyright holder nor the names of its
|
||||
* contributors may be used to endorse or promote products derived from
|
||||
* this software without specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
||||
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
||||
* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
|
||||
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
|
||||
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
|
||||
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
||||
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
|
||||
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
||||
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
||||
* POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
/* Either 1 (Enabled) or 0 (disabled) */
|
||||
#define DEBUG 0
|
||||
#if DEBUG
|
||||
#define DEBUG_FB_REFRESH 1
|
||||
#endif
|
||||
|
||||
/* All the code assumes dimensions are below 127, as signed chars are used to
|
||||
* store positions and indices. */
|
||||
#define MAX_X 4
|
||||
#define MAX_Y 4
|
||||
#define MAX_Z 4
|
||||
|
||||
#define BRIGHTNESS_LEVELS 16
|
||||
#define BRIGHTNESS_MAX 256
|
||||
|
||||
struct Frame {
|
||||
unsigned char data[MAX_X][MAX_Y][MAX_Z];
|
||||
};
|
||||
|
||||
extern struct FrameBuffer FrameBuffer;
|
||||
|
||||
#define FRAME_SIZE() sizeof(struct Frame)
|
||||
|
||||
#if BRIGHTNESS_LEVELS
|
||||
#define BRIGHTNESS_INCREMENT (BRIGHTNESS_MAX / BRIGHTNESS_LEVELS)
|
||||
#define FRAME_PERIOD (40000 / BRIGHTNESS_LEVELS) /* [us] */
|
||||
#else
|
||||
#define FRAME_PERIOD (40000) /* [us] */
|
||||
#endif
|
||||
|
||||
/**
|
||||
* Switch the back and front buffers.
|
||||
*
|
||||
*/
|
||||
void FrameBufferSwitch(void);
|
||||
|
||||
/**
|
||||
* Returns the current front (visible) frame buffer
|
||||
*/
|
||||
Frame *FrameBufferGetFront(void);
|
||||
|
||||
|
||||
/**
|
||||
* Returns the current back (invisible) frame buffer
|
||||
*/
|
||||
Frame *FrameBufferGetBack(void);
|
||||
|
||||
/**
|
||||
* Write the brightness ``val'' to the led at position (x,y,z), in the back
|
||||
* buffer.
|
||||
*
|
||||
* In order to make the value visible, a call to FrameBufferSwitch is needed,
|
||||
* once the drawing of the frame is complete.
|
||||
*/
|
||||
void FrameBufferWrite(char x, char y, char z, unsigned char val);
|
||||
|
||||
/**
|
||||
* Print the string, a letter at a time, showing each for delayPerLetter time,
|
||||
* with the associated brightness.
|
||||
*
|
||||
* This has minimal checks, only lowercase font, space and zero to terminate
|
||||
* the string.
|
||||
*/
|
||||
void FrameBufferWriteStr(char * str, const short delayPerLetter,
|
||||
const unsigned char brightness);
|
||||
|
||||
/**
|
||||
* Print all symbols available, one after the other.
|
||||
*/
|
||||
void printAllSymbols(void);
|
||||
|
||||
/**
|
||||
* Draw the front frame on the ``screen''.
|
||||
*/
|
||||
void FrameBufferRefresh(void);
|
||||
|
||||
/**
|
||||
* Initialize the framebuffer, and start the timer to refresh the ``screen''.
|
||||
*/
|
||||
void FrameBufferInit(void);
|
||||
|
||||
#endif /* FRAMEBUFFER_H */
|
||||
BIN
LEDCube.jpg
Normal file
BIN
LEDCube.jpg
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 1.2 MiB |
BIN
LEDCubeBottomWired.jpg
Normal file
BIN
LEDCubeBottomWired.jpg
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 1.8 MiB |
BIN
LEDCubeRessitors.jpg
Normal file
BIN
LEDCubeRessitors.jpg
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 1.1 MiB |
BIN
LEDCubeWireTooBig.jpg
Normal file
BIN
LEDCubeWireTooBig.jpg
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 1.3 MiB |
BIN
LEDCubeWired.jpg
Normal file
BIN
LEDCubeWired.jpg
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 1.5 MiB |
20
LICENSE
20
LICENSE
@@ -1,21 +1,19 @@
|
||||
BSD 3-Clause License
|
||||
|
||||
Copyright (c) 2018, Lionel Sambuc
|
||||
Copyright (c) 2018, Lionel Adrien Sambuc
|
||||
All rights reserved.
|
||||
|
||||
Redistribution and use in source and binary forms, with or without
|
||||
modification, are permitted provided that the following conditions are met:
|
||||
|
||||
* Redistributions of source code must retain the above copyright notice, this
|
||||
list of conditions and the following disclaimer.
|
||||
1. Redistributions of source code must retain the above copyright notice,
|
||||
this list of conditions and the following disclaimer.
|
||||
|
||||
* Redistributions in binary form must reproduce the above copyright notice,
|
||||
this list of conditions and the following disclaimer in the documentation
|
||||
and/or other materials provided with the distribution.
|
||||
2. Redistributions in binary form must reproduce the above copyright
|
||||
notice, this list of conditions and the following disclaimer in the
|
||||
documentation and/or other materials provided with the distribution.
|
||||
|
||||
* Neither the name of the copyright holder nor the names of its
|
||||
contributors may be used to endorse or promote products derived from
|
||||
this software without specific prior written permission.
|
||||
3. Neither the name of the copyright holder nor the names of its
|
||||
contributors may be used to endorse or promote products derived from
|
||||
this software without specific prior written permission.
|
||||
|
||||
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
||||
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
|
||||
268
LedCube.ino
Normal file
268
LedCube.ino
Normal file
@@ -0,0 +1,268 @@
|
||||
/*
|
||||
* Copyright (c) 2018, Lionel Adrien Sambuc
|
||||
* All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are met:
|
||||
*
|
||||
* 1. Redistributions of source code must retain the above copyright notice,
|
||||
* this list of conditions and the following disclaimer.
|
||||
*
|
||||
* 2. Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the distribution.
|
||||
*
|
||||
* 3. Neither the name of the copyright holder nor the names of its
|
||||
* contributors may be used to endorse or promote products derived from
|
||||
* this software without specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
||||
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
||||
* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
|
||||
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
|
||||
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
|
||||
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
||||
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
|
||||
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
||||
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
||||
* POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
/**
|
||||
* This program was created from a mix of several sources for the inspiration
|
||||
* on how to implement the system. I especially followed:
|
||||
* http://www.instructables.com/id/4x4x4-LED-Cube-Arduino-Uno/ for the
|
||||
* construction of the hardware.
|
||||
*
|
||||
* The main idea is to keep two memory frame (3d) buffer. In order to refresh
|
||||
* those we use a handler triggered through a timer, set for a period of
|
||||
* ``FRAME_PERIOD''. Every period the whole cube needs to be redrawn, but as
|
||||
* we have multiplexing, this means that each layer has only a fourth of the
|
||||
* frame period to be turned on for the maximum brightness. In order to
|
||||
* provide multiple brightness levels, a PWM-like signal is manually
|
||||
* generated, per led, per layer, by turning it on for a percentage of the
|
||||
* layer time only.
|
||||
*/
|
||||
|
||||
/* Either 1 (Enabled) or 0 (disabled) */
|
||||
#define DEBUG 0
|
||||
#if DEBUG
|
||||
#define DEBUG_SETUP 1
|
||||
#define DEBUG_LOOP 1
|
||||
#endif
|
||||
|
||||
#include "FrameBuffer.h"
|
||||
|
||||
|
||||
/******************************* ANIMATIONS ********************************/
|
||||
|
||||
/* Taken from PWMallPins.pde by Paul Badger, 2007;
|
||||
* formatted by Seth Wolf, 2015 */
|
||||
// Preset 256 values of a binary conversions of a sine wave
|
||||
unsigned char sinewave[] = {
|
||||
0x80,0x83,0x86,0x89,0x8c,0x8f,0x92,0x95,0x98,0x9c,0x9f,0xa2,0xa5,0xa8,0xab,0xae,
|
||||
0xb0,0xb3,0xb6,0xb9,0xbc,0xbf,0xc1,0xc4,0xc7,0xc9,0xcc,0xce,0xd1,0xd3,0xd5,0xd8,
|
||||
0xda,0xdc,0xde,0xe0,0xe2,0xe4,0xe6,0xe8,0xea,0xec,0xed,0xef,0xf0,0xf2,0xf3,0xf5,
|
||||
0xf6,0xf7,0xf8,0xf9,0xfa,0xfb,0xfc,0xfc,0xfd,0xfe,0xfe,0xff,0xff,0xff,0xff,0xff,
|
||||
0xff,0xff,0xff,0xff,0xff,0xff,0xfe,0xfe,0xfd,0xfc,0xfc,0xfb,0xfa,0xf9,0xf8,0xf7,
|
||||
0xf6,0xf5,0xf3,0xf2,0xf0,0xef,0xed,0xec,0xea,0xe8,0xe6,0xe4,0xe2,0xe0,0xde,0xdc,
|
||||
0xda,0xd8,0xd5,0xd3,0xd1,0xce,0xcc,0xc9,0xc7,0xc4,0xc1,0xbf,0xbc,0xb9,0xb6,0xb3,
|
||||
0xb0,0xae,0xab,0xa8,0xa5,0xa2,0x9f,0x9c,0x98,0x95,0x92,0x8f,0x8c,0x89,0x86,0x83,
|
||||
0x80,0x7c,0x79,0x76,0x73,0x70,0x6d,0x6a,0x67,0x63,0x60,0x5d,0x5a,0x57,0x54,0x51,
|
||||
0x4f,0x4c,0x49,0x46,0x43,0x40,0x3e,0x3b,0x38,0x36,0x33,0x31,0x2e,0x2c,0x2a,0x27,
|
||||
0x25,0x23,0x21,0x1f,0x1d,0x1b,0x19,0x17,0x15,0x13,0x12,0x10,0x0f,0x0d,0x0c,0x0a,
|
||||
0x09,0x08,0x07,0x06,0x05,0x04,0x03,0x03,0x02,0x01,0x01,0x00,0x00,0x00,0x00,0x00,
|
||||
0x00,0x00,0x00,0x00,0x00,0x00,0x01,0x01,0x02,0x03,0x03,0x04,0x05,0x06,0x07,0x08,
|
||||
0x09,0x0a,0x0c,0x0d,0x0f,0x10,0x12,0x13,0x15,0x17,0x19,0x1b,0x1d,0x1f,0x21,0x23,
|
||||
0x25,0x27,0x2a,0x2c,0x2e,0x31,0x33,0x36,0x38,0x3b,0x3e,0x40,0x43,0x46,0x49,0x4c,
|
||||
0x4f,0x51,0x54,0x57,0x5a,0x5d,0x60,0x63,0x67,0x6a,0x6d,0x70,0x73,0x76,0x79,0x7c
|
||||
};
|
||||
|
||||
/**
|
||||
* Increase by steps the luminosity, then dim by steps the whole cube.
|
||||
*
|
||||
* Mainly useful as a test of the led cube.
|
||||
*/
|
||||
void levels(void) {
|
||||
for(int i = 0; i < BRIGHTNESS_MAX; i++) {
|
||||
memset(FrameBufferGetBack(), i, FRAME_SIZE());
|
||||
FrameBufferSwitch();
|
||||
delay(100);
|
||||
}
|
||||
|
||||
for(int i = BRIGHTNESS_MAX; i >= 0; i--) {
|
||||
memset(FrameBufferGetBack(), i, FRAME_SIZE());
|
||||
FrameBufferSwitch();
|
||||
delay(100);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Increase by steps the luminosity, then dim by steps the whole cube.
|
||||
*
|
||||
* Mainly useful as a test of the led cube.
|
||||
*/
|
||||
void testfreq(void) {
|
||||
memset(FrameBufferGetBack(), 255, FRAME_SIZE());
|
||||
FrameBufferSwitch();
|
||||
delay(200);
|
||||
|
||||
memset(FrameBufferGetBack(), 1, FRAME_SIZE());
|
||||
FrameBufferSwitch();
|
||||
delay(200);
|
||||
}
|
||||
|
||||
/**
|
||||
* Blinks the whole cube on and off with a period `period`.
|
||||
*/
|
||||
void blinkWholeCube(int period /* [ms] */) {
|
||||
memset(FrameBufferGetBack(), 255, FRAME_SIZE());
|
||||
FrameBufferSwitch();
|
||||
delay(period >> 1);
|
||||
|
||||
memset(FrameBufferGetBack(), 0, FRAME_SIZE());
|
||||
FrameBufferSwitch();
|
||||
delay(period >> 1);
|
||||
}
|
||||
|
||||
/**
|
||||
* Fade in and out following a sine curve with a period `period`.
|
||||
*/
|
||||
void sine(int period /* [ms] */) {
|
||||
for(short int i = 0; i < BRIGHTNESS_MAX; i++) {
|
||||
memset(FrameBufferGetBack()->data, sinewave[i], FRAME_SIZE());
|
||||
FrameBufferSwitch();
|
||||
delay(period / BRIGHTNESS_MAX);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Fade in and out following a sine curve with a period `period`, but only a
|
||||
* small 2x2x2 cube in the center.
|
||||
*/
|
||||
void sine2(int period /* [ms] */) {
|
||||
for(short int i = 0; i < BRIGHTNESS_MAX; i++) {
|
||||
memset(FrameBufferGetBack(), 0, FRAME_SIZE());
|
||||
|
||||
for (char x = 1; x < 3; x++) {
|
||||
for (char y = 1; y < 3; y++) {
|
||||
for (char z = 1; z < 3; z++) {
|
||||
FrameBufferGetBack()->data[x][y][z] = sinewave[i];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
FrameBufferSwitch();
|
||||
delay(period / BRIGHTNESS_MAX);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Fade in and out following a sine curve with a period `period` the entire
|
||||
* cube, but with the center 2x2x2 cube dephased by 180°.
|
||||
*/
|
||||
void sine3(int period /* [ms] */) {
|
||||
for(int i = 0; i < 256; i++) {
|
||||
memset(FrameBufferGetBack()->data, 0, FRAME_SIZE());
|
||||
for (char x = 1; x < 3; x++) {
|
||||
for (char y = 1; y < 3; y++) {
|
||||
for (char z = 1; z < 3; z++) {
|
||||
FrameBufferGetBack()->data[x][y][z] = sinewave[i];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for (char x = 0; x < MAX_X; x++) {
|
||||
for (char y = 0; y < MAX_Y; y++) {
|
||||
for (char z = 0; z < MAX_Z; z++) {
|
||||
if ( ((x == 0) || (x == 3))
|
||||
||((y == 0) || (y == 3))
|
||||
||((z == 0) || (z == 3))) {
|
||||
FrameBufferGetBack()->data[x][y][z] = sinewave[(i+128)%256];
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
FrameBufferSwitch();
|
||||
delay(period / BRIGHTNESS_MAX);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Random rain drpos fall to the bottom of the cube
|
||||
*/
|
||||
void randomRain() {
|
||||
for (char a = MAX_Z; a > 0; a--) {
|
||||
// animation of 4 steps, requiring computing 4 full frames
|
||||
for (char x = 0; x < MAX_X; x++) {
|
||||
for (char y = 0; y < MAX_Y; y++) {
|
||||
FrameBufferGetBack()->data[x][y][3] = (random(0, 4) == 0) ? 1 : random(0, BRIGHTNESS_MAX);
|
||||
FrameBufferGetBack()->data[x][y][2] = FrameBufferGetFront()->data[x][y][3];
|
||||
FrameBufferGetBack()->data[x][y][1] = FrameBufferGetFront()->data[x][y][2];
|
||||
FrameBufferGetBack()->data[x][y][0] = FrameBufferGetFront()->data[x][y][1];
|
||||
}
|
||||
}
|
||||
|
||||
FrameBufferSwitch();
|
||||
delay(500);
|
||||
}
|
||||
}
|
||||
|
||||
/********************************** BODY ***********************************/
|
||||
|
||||
void setup()
|
||||
{
|
||||
#if DEBUG_SETUP
|
||||
static long timeStartSetup = micros();
|
||||
#endif
|
||||
|
||||
// Seeding random for random pattern
|
||||
randomSeed(analogRead(10));
|
||||
|
||||
#if DEBUG
|
||||
Serial.begin(9600);
|
||||
Serial.println("\nSetup DONE");
|
||||
#endif
|
||||
|
||||
FrameBufferInit();
|
||||
|
||||
#if DEBUG_SETUP
|
||||
Serial.print("Setup Time: ");
|
||||
Serial.println((micros() - timeStartSetup), DEC);
|
||||
#endif
|
||||
}
|
||||
|
||||
void loop() {
|
||||
static char a = -1;
|
||||
|
||||
#if DEBUG_LOOP
|
||||
static long timeStartLoop = micros();
|
||||
#endif
|
||||
|
||||
a = ((a+1) % 9);
|
||||
for (int j = 0; j < 10; j++) {
|
||||
switch(a) {
|
||||
case 0: sine2(2000); break;
|
||||
case 1: randomRain(); break;
|
||||
case 2: sine3(2000); break;
|
||||
case 3: blinkWholeCube(1000); break;
|
||||
case 4: sine(2000); break;
|
||||
case 5: printAllSymbols(); break;
|
||||
case 6: FrameBufferWriteStr("hello world", 250, 255);
|
||||
delay(1000);
|
||||
break;
|
||||
case 7: testfreq(); break;
|
||||
case 8: levels(); break;
|
||||
default: break;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
#if DEBUG_LOOP
|
||||
Serial.print("Loop Time: ");
|
||||
Serial.println((micros() - timeStartLoop), DEC);
|
||||
#endif
|
||||
}
|
||||
|
||||
33
README.md
33
README.md
@@ -1,2 +1,33 @@
|
||||
# LedCube
|
||||
Arduino Sketch for a LED cubes
|
||||
Arduino Sketch for LED cubes
|
||||
|
||||
The following sketch is currently split into a (3D) FrameBuffer implementation and the main "Sketch", which contains a couple of simple animations.
|
||||
|
||||
The goal is for the FrameBuffer implementation to grow and allow in the future to support multi-color leds, as well as bigger LED Cubes.
|
||||
|
||||
It is implemented in such away to allow for now about 16 levels of brightness
|
||||
|
||||
## Hardware
|
||||
|
||||
1. Arduino UNO
|
||||
2. a home-made LED cube :)
|
||||
|
||||
To be improved for the next model:
|
||||
|
||||
* Use thinner wiring cables, this has unnecessarily complicated the soldering job
|
||||
* Improve my soldering technic oO
|
||||
|
||||
### Led Cube alone.
|
||||

|
||||
|
||||
### Bottom, before wiring.
|
||||

|
||||
|
||||
### Bottom after wiring.
|
||||

|
||||
|
||||
### My wiring cables where too big, AWG 18, which is about the same width as the hole pitch of my board.
|
||||

|
||||
|
||||
### Led Cube wired to the Arduino UNO
|
||||

|
||||
|
||||
Reference in New Issue
Block a user