summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--.gitignore2
-rw-r--r--CMakeLists.txt36
-rw-r--r--Charliplexing.c503
-rw-r--r--Charliplexing.h33
-rw-r--r--lolshield.c182
5 files changed, 756 insertions, 0 deletions
diff --git a/.gitignore b/.gitignore
new file mode 100644
index 0000000..dee53c5
--- /dev/null
+++ b/.gitignore
@@ -0,0 +1,2 @@
+build
+*~
diff --git a/CMakeLists.txt b/CMakeLists.txt
new file mode 100644
index 0000000..c713b85
--- /dev/null
+++ b/CMakeLists.txt
@@ -0,0 +1,36 @@
+cmake_minimum_required(VERSION 2.8.3)
+
+
+SET(BOARD "atmega328p" CACHE STRING "AVR CPU to build for")
+SET(CLOCK "16000000" CACHE STRING "CPU clock")
+
+SET(FLASH_FLAGS "-patmega328p" "-carduino" "-P/dev/ttyUSB0" "-b57600" CACHE STRING "avrdude flags")
+
+
+find_program(AVR_GCC avr-gcc)
+find_program(AVRDUDE avrdude)
+
+SET(CMAKE_SYSTEM_NAME Generic)
+
+
+SET(CMAKE_C_COMPILER ${AVR_GCC})
+
+project(ARDKBD C)
+
+set(CMAKE_MODULE_PATH ${ARDKDB_SOURCE_DIR})
+
+
+add_executable(lolshield.elf
+ lolshield.c
+ Charliplexing.c
+)
+set_target_properties(lolshield.elf PROPERTIES
+ COMPILE_FLAGS "-Wall -funsigned-char -funsigned-bitfields -fpack-struct -fshort-enums -Os -mmcu=${BOARD}"
+ LINK_FLAGS "-Wall -funsigned-char -funsigned-bitfields -fpack-struct -fshort-enums -Os -mmcu=${BOARD}"
+ COMPILE_DEFINITIONS "F_CPU=${CLOCK}"
+)
+
+add_custom_command(OUTPUT lolshield.hex COMMAND ${CMAKE_OBJCOPY} -O ihex -R .eeprom lolshield.elf lolshield.hex DEPENDS lolshield.elf)
+add_custom_target(lolshield ALL DEPENDS lolshield.hex)
+
+add_custom_target(flash COMMAND ${AVRDUDE} ${FLASH_FLAGS} -D -Uflash:w:lolshield.hex:i DEPENDS lolshield)
diff --git a/Charliplexing.c b/Charliplexing.c
new file mode 100644
index 0000000..fcd3bd0
--- /dev/null
+++ b/Charliplexing.c
@@ -0,0 +1,503 @@
+/*
+ Charliplexing.cpp - Using timer2 with 1ms resolution
+
+ Alex Wenger <a.wenger@gmx.de> http://arduinobuch.wordpress.com/
+ Matt Mets <mahto@cibomahto.com> http://cibomahto.com/
+
+ Timer init code from MsTimer2 - Javier Valencia <javiervalencia80@gmail.com>
+ Misc functions from Benjamin Sonnatg <benjamin@sonntag.fr>
+
+ History:
+ 2009-12-30 - V0.0 wrote the first version at 26C3/Berlin
+ 2010-01-01 - V0.1 adding misc utility functions
+ (Clear, Vertical, Horizontal) comment are Doxygen complaints now
+ 2010-05-27 - V0.2 add double-buffer mode
+ 2010-08-18 - V0.9 Merge brightness and grayscale
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Lesser General Public
+ License as published by the Free Software Foundation; either
+ version 2.1 of the License, or (at your option) any later version.
+
+ This library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Lesser General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public
+ License along with this library; if not, write to the Free Software
+ Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+*/
+
+#include <math.h>
+#include <avr/interrupt.h>
+#include <util/delay.h>
+#include "Charliplexing.h"
+
+volatile unsigned int LedSign_tcnt2;
+
+
+typedef struct _videoPage {
+ uint8_t pixels[SHADES][48]; // TODO: is 48 right?
+} videoPage;
+
+/* ----------------------------------------------------------------- */
+/** Table for the LED multiplexing cycles
+ * Each frame is made of 24 bytes (for the 24 display cycles)
+ * There are SHADES frames per buffer in grayscale mode (one for each brigtness)
+ * and twice that many to support double-buffered grayscale.
+ */
+videoPage leds[2];
+
+/// Determines whether the display is in single or double buffer mode
+uint8_t displayMode = SINGLE_BUFFER;
+
+/// Flag indicating that the display page should be flipped as soon as the
+/// current frame is displayed
+volatile bool videoFlipPage = false;
+
+/// Pointer to the buffer that is currently being displayed
+videoPage* displayBuffer;
+
+/// Pointer to the buffer that should currently be drawn to
+videoPage* workBuffer;
+
+/// Flag indicating that the timer buffer should be flipped as soon as the
+/// current frame is displayed
+volatile bool videoFlipTimer = false;
+
+
+// Timer counts to display each page for, plus off time
+typedef struct timerInfo {
+ uint8_t counts[SHADES];
+ uint8_t prescaler[SHADES];
+} timerInfo;
+
+// Double buffer the timing information, of course.
+timerInfo* frontTimer;
+timerInfo* backTimer;
+
+timerInfo* tempTimer;
+
+timerInfo timer[2];
+
+// Record a slow and fast prescaler for later use
+typedef struct prescalerInfo {
+ uint8_t relativeSpeed;
+ uint8_t TCCR2;
+} prescalerInfo;
+
+// TODO: Generate these based on processor type and clock speed
+prescalerInfo slowPrescaler = {1, 0x03};
+//prescalerInfo fastPrescaler = {32, 0x01};
+prescalerInfo fastPrescaler = {4, 0x02};
+
+static bool initialized = false;
+
+/// Uncomment to set analog pin 5 high during interrupts, so that an
+/// oscilloscope can be used to measure the processor time taken by it
+//#define MEASURE_ISR_TIME
+//#ifdef MEASURE_ISR_TIME
+//uint8_t statusPIN = 19;
+//#endif
+
+typedef struct LEDPosition {
+ uint8_t high;
+ uint8_t low;
+} LEDPosition;
+
+
+/* ----------------------------------------------------------------- */
+/** Table for LED Position in leds[] ram table
+ */
+
+const LEDPosition ledMap[126] = {
+ {13, 5}, {13, 6}, {13, 7}, {13, 8}, {13, 9}, {13,10}, {13,11}, {13,12},
+ {13, 4}, { 4,13}, {13, 3}, { 3,13}, {13, 2}, { 2,13},
+ {12, 5}, {12, 6}, {12, 7}, {12, 8}, {12, 9}, {12,10}, {12,11}, {12,13},
+ {12, 4}, { 4,12}, {12, 3}, { 3,12}, {12, 2}, { 2,12},
+ {11, 5}, {11, 6}, {11, 7}, {11, 8}, {11, 9}, {11,10}, {11,12}, {11,13},
+ {11, 4}, { 4,11}, {11, 3}, { 3,11}, {11, 2}, { 2,11},
+ {10, 5}, {10, 6}, {10, 7}, {10, 8}, {10, 9}, {10,11}, {10,12}, {10,13},
+ {10, 4}, { 4,10}, {10, 3}, { 3,10}, {10, 2}, { 2,10},
+ { 9, 5}, { 9, 6}, { 9, 7}, { 9, 8}, { 9,10}, { 9,11}, { 9,12}, { 9,13},
+ { 9, 4}, { 4, 9}, { 9, 3}, { 3, 9}, { 9, 2}, { 2, 9},
+ { 8, 5}, { 8, 6}, { 8, 7}, { 8, 9}, { 8,10}, { 8,11}, { 8,12}, { 8,13},
+ { 8, 4}, { 4, 8}, { 8, 3}, { 3, 8}, { 8, 2}, { 2, 8},
+ { 7, 5}, { 7, 6}, { 7, 8}, { 7, 9}, { 7,10}, { 7,11}, { 7,12}, { 7,13},
+ { 7, 4}, { 4, 7}, { 7, 3}, { 3, 7}, { 7, 2}, { 2, 7},
+ { 6, 5}, { 6, 7}, { 6, 8}, { 6, 9}, { 6,10}, { 6,11}, { 6,12}, { 6,13},
+ { 6, 4}, { 4, 6}, { 6, 3}, { 3, 6}, { 6, 2}, { 2, 6},
+ { 5, 6}, { 5, 7}, { 5, 8}, { 5, 9}, { 5,10}, { 5,11}, { 5,12}, { 5,13},
+ { 5, 4}, { 4, 5}, { 5, 3}, { 3, 5}, { 5, 2}, { 2, 5},
+};
+
+
+/* ----------------------------------------------------------------- */
+/** Constructor : Initialize the interrupt code.
+ * should be called in setup();
+ */
+void LedSignInit(uint8_t mode)
+{
+//#ifdef MEASURE_ISR_TIME
+// pinMode(statusPIN, OUTPUT);
+// digitalWrite(statusPIN, LOW);
+//#endif
+
+ float prescaler = 0.0;
+
+#if defined (__AVR_ATmega168__) || defined (__AVR_ATmega48__) || defined (__AVR_ATmega88__) || defined (__AVR_ATmega328P__) || (__AVR_ATmega1280__)
+ TIMSK2 &= ~(1<<TOIE2);
+ TCCR2A &= ~((1<<WGM21) | (1<<WGM20));
+ TCCR2B &= ~(1<<WGM22);
+ ASSR &= ~(1<<AS2);
+ TIMSK2 &= ~(1<<OCIE2A);
+
+ if ((F_CPU >= 1000000UL) && (F_CPU <= 16000000UL)) { // prescaler set to 64
+ TCCR2B |= ((1<<CS21) | (1<<CS20));
+ TCCR2B &= ~(1<<CS22);
+ prescaler = 32.0;
+ } else if (F_CPU < 1000000UL) { // prescaler set to 8
+ TCCR2B |= (1<<CS21);
+ TCCR2B &= ~((1<<CS22) | (1<<CS20));
+ prescaler = 8.0;
+ } else { // F_CPU > 16Mhz, prescaler set to 128
+ TCCR2B |= (1<<CS22);
+ TCCR2B &= ~((1<<CS21) | (1<<CS20));
+ prescaler = 64.0;
+ }
+#elif defined (__AVR_ATmega8__)
+ TIMSK &= ~(1<<TOIE2);
+ TCCR2 &= ~((1<<WGM21) | (1<<WGM20));
+ TIMSK &= ~(1<<OCIE2);
+ ASSR &= ~(1<<AS2);
+
+ if ((F_CPU >= 1000000UL) && (F_CPU <= 16000000UL)) { // prescaler set to 64
+ TCCR2 |= (1<<CS22);
+ TCCR2 &= ~((1<<CS21) | (1<<CS20));
+ prescaler = 64.0;
+ } else if (F_CPU < 1000000UL) { // prescaler set to 8
+ TCCR2 |= (1<<CS21);
+ TCCR2 &= ~((1<<CS22) | (1<<CS20));
+ prescaler = 8.0;
+ } else { // F_CPU > 16Mhz, prescaler set to 128
+ TCCR2 |= ((1<<CS22) && (1<<CS20));
+ TCCR2 &= ~(1<<CS21);
+ prescaler = 128.0;
+ }
+#elif defined (__AVR_ATmega128__)
+ TIMSK &= ~(1<<TOIE2);
+ TCCR2 &= ~((1<<WGM21) | (1<<WGM20));
+ TIMSK &= ~(1<<OCIE2);
+
+ if ((F_CPU >= 1000000UL) && (F_CPU <= 16000000UL)) { // prescaler set to 64
+ TCCR2 |= ((1<<CS21) | (1<<CS20));
+ TCCR2 &= ~(1<<CS22);
+ prescaler = 64.0;
+ } else if (F_CPU < 1000000UL) { // prescaler set to 8
+ TCCR2 |= (1<<CS21);
+ TCCR2 &= ~((1<<CS22) | (1<<CS20));
+ prescaler = 8.0;
+ } else { // F_CPU > 16Mhz, prescaler set to 256
+ TCCR2 |= (1<<CS22);
+ TCCR2 &= ~((1<<CS21) | (1<<CS20));
+ prescaler = 256.0;
+ }
+#endif
+
+ LedSign_tcnt2 = 256 - (int)((float)F_CPU * 0.0005 / prescaler);
+
+ // Record whether we are in single or double buffer mode
+ displayMode = mode;
+ videoFlipPage = false;
+
+ // Point the display buffer to the first physical buffer
+ displayBuffer = &leds[0];
+
+ // If we are in single buffered mode, point the work buffer
+ // at the same physical buffer as the display buffer. Otherwise,
+ // point it at the second physical buffer.
+ if( displayMode & DOUBLE_BUFFER ) {
+ workBuffer = &leds[1];
+ }
+ else {
+ workBuffer = displayBuffer;
+ }
+
+ // Set up the timer buffering
+ frontTimer = &timer[0];
+ backTimer = &timer[1];
+
+ videoFlipTimer = false;
+ LedSignSetBrightness(127);
+
+ // Clear the buffer and display it
+ LedSignClear(0);
+ LedSignFlip(false);
+
+ // Then start the display
+ TCNT2 = LedSign_tcnt2;
+#if defined (__AVR_ATmega168__) || defined (__AVR_ATmega48__) || defined (__AVR_ATmega88__) || defined (__AVR_ATmega328P__) || (__AVR_ATmega1280__)
+ TIMSK2 |= (1<<TOIE2);
+#elif defined (__AVR_ATmega128__) || defined (__AVR_ATmega8__)
+ TIMSK |= (1<<TOIE2);
+#endif
+
+ // If we are in double-buffer mode, wait until the display flips before we
+ // return
+ if (displayMode & DOUBLE_BUFFER)
+ {
+ while (videoFlipPage) {
+ _delay_ms(1);
+ }
+ }
+
+ initialized = true;
+}
+
+
+/* ----------------------------------------------------------------- */
+/** Signal that the front and back buffers should be flipped
+ * @param blocking if true : wait for flip before returning, if false :
+ * return immediately.
+ */
+void LedSignFlip(bool blocking)
+{
+ if (displayMode & DOUBLE_BUFFER)
+ {
+ // Just set the flip flag, the buffer will flip between redraws
+ videoFlipPage = true;
+
+ // If we are blocking, sit here until the page flips.
+ while (blocking && videoFlipPage) {
+ _delay_ms(1);
+ }
+ }
+}
+
+
+/* ----------------------------------------------------------------- */
+/** Clear the screen completely
+ * @param set if 1 : make all led ON, if not set or 0 : make all led OFF
+ */
+void LedSignClear(int set) {
+ int x, y;
+ for(x=0;x<14;x++)
+ for(y=0;y<9;y++)
+ LedSignSet(x,y,set);
+}
+
+
+/* ----------------------------------------------------------------- */
+/** Clear an horizontal line completely
+ * @param y is the y coordinate of the line to clear/light [0-8]
+ * @param set if 1 : make all led ON, if not set or 0 : make all led OFF
+ */
+void LedSignHorizontal(int y, int set) {
+ int x;
+ for(x=0;x<14;x++)
+ LedSignSet(x,y,set);
+}
+
+
+/* ----------------------------------------------------------------- */
+/** Clear a vertical line completely
+ * @param x is the x coordinate of the line to clear/light [0-13]
+ * @param set if 1 : make all led ON, if not set or 0 : make all led OFF
+ */
+void LedSignVertical(int x, int set) {
+ int y;
+ for(y=0;y<9;y++)
+ LedSignSet(x,y,set);
+}
+
+
+/* ----------------------------------------------------------------- */
+/** Set : switch on and off the leds. All the position #for char in frameString:
+ * calculations are done here, so we don't need to do in the
+ * interrupt code
+ */
+void LedSignSet(uint8_t x, uint8_t y, uint8_t c)
+{
+ uint8_t pin_high = ledMap[x+y*14].high;
+ uint8_t pin_low = ledMap[x+y*14].low;
+ // pin_low is directly the address in the led array (minus 2 because the
+ // first two bytes are used for RS232 communication), but
+ // as it is a two byte array we need to check pin_high also.
+ // If pin_high is bigger than 8 address has to be increased by one
+
+ uint8_t bufferNum = (pin_low-2)*2 + (pin_high / 8) + ((pin_high > 7)?24:0);
+ uint8_t work = _BV(pin_high & 0x07);
+
+ // If we aren't in grayscale mode, just map any pin brightness to max
+ if (c > 0 && !(displayMode & GRAYSCALE)) {
+ c = SHADES-1;
+ }
+
+ int i;
+ for (i = 0; i < SHADES-1; i++) {
+ if( c > i ) {
+ workBuffer->pixels[i][bufferNum] |= work; // ON
+ }
+ else {
+ workBuffer->pixels[i][bufferNum] &= ~work; // OFF
+ }
+ }
+}
+
+
+/* Set the overall brightness of the screen
+ * @param brightness LED brightness, from 0 (off) to 127 (full on)
+ */
+
+void LedSignSetBrightness(uint8_t brightness)
+{
+ // An exponential fit seems to approximate a (perceived) linear scale
+ float brightnessPercent = ((float)brightness / 127)*((float)brightness / 127);
+ uint8_t difference = 0;
+
+ /* ---- This needs review! Please review. -- thilo */
+ // set up page counts
+ // TODO: make SHADES a function parameter. This would require some refactoring.
+ int start = 15;
+ int max = 255;
+ float scale = 1.5;
+ float delta = pow( max - start , 1.0 / scale) / (SHADES - 1);
+ uint8_t pageCounts[SHADES];
+
+ pageCounts[0] = max - start;
+ uint8_t i;
+ for (i=1; i<SHADES; i++) {
+ pageCounts[i] = max - ( pow( i * delta, scale ) + start );
+ }
+ //Serial.end();
+
+ if (! initialized) {
+ // set front timer defaults
+ int i;
+ for (i = 0; i < SHADES; i++) {
+ frontTimer->counts[i] = pageCounts[i];
+ // TODO: Generate this dynamically
+ frontTimer->prescaler[i] = slowPrescaler.TCCR2;
+ }
+ }
+
+ // Wait until the previous brightness request goes through
+ while( videoFlipTimer ) {
+ _delay_ms(1);
+ }
+
+ // Compute on time for each of the pages
+ // Use the fast timer; slow timer is only useful for < 3 shades.
+ for (i = 0; i < SHADES - 1; i++) {
+ uint8_t interval = 255 - pageCounts[i];
+
+ backTimer->counts[i] = 255 - brightnessPercent
+ * interval
+ * fastPrescaler.relativeSpeed;
+ backTimer->prescaler[i] = fastPrescaler.TCCR2;
+ difference += backTimer->counts[i] - pageCounts[i];
+ }
+
+ // Compute off time
+ backTimer->counts[SHADES - 1] = 255 - difference;
+ backTimer->prescaler[SHADES - 1] = slowPrescaler.TCCR2;
+
+ /* ---- End of "This needs review! Please review." -- thilo */
+
+ // Have the ISR update the timer registers next run
+ videoFlipTimer = true;
+}
+
+
+/* ----------------------------------------------------------------- */
+/** The Interrupt code goes here !
+ */
+ISR(TIMER2_OVF_vect) {
+ DDRD = 0x0;
+ DDRB = 0x0;
+ //#ifdef MEASURE_ISR_TIME
+ // digitalWrite(statusPIN, HIGH);
+ //#endif
+
+ // For each cycle, we have potential SHADES pages to display.
+ // Once every page has been displayed, then we move on to the next
+ // cycle.
+
+ // 24 Cycles of Matrix
+ static uint8_t cycle = 0;
+
+ // SHADES pages to display
+ static uint8_t page = 0;
+
+ TCCR2B = frontTimer->prescaler[page];
+ TCNT2 = frontTimer->counts[page];
+
+ if ( page < SHADES - 1) {
+
+ if (cycle < 6) {
+ DDRD = _BV(cycle+2) | displayBuffer->pixels[page][cycle*2];
+ PORTD = displayBuffer->pixels[page][cycle*2];
+
+ DDRB = displayBuffer->pixels[page][cycle*2+1];
+ PORTB = displayBuffer->pixels[page][cycle*2+1];
+ } else if (cycle < 12) {
+ DDRD = displayBuffer->pixels[page][cycle*2];
+ PORTD = displayBuffer->pixels[page][cycle*2];
+
+ DDRB = _BV(cycle-6) | displayBuffer->pixels[page][cycle*2+1];
+ PORTB = displayBuffer->pixels[page][cycle*2+1];
+ } else if (cycle < 18) {
+ DDRD = _BV(cycle+2-12) | displayBuffer->pixels[page][cycle*2];
+ PORTD = displayBuffer->pixels[page][cycle*2];
+
+ DDRB = displayBuffer->pixels[page][cycle*2+1];
+ PORTB = displayBuffer->pixels[page][cycle*2+1];
+ } else {
+ DDRD = displayBuffer->pixels[page][cycle*2];
+ PORTD = displayBuffer->pixels[page][cycle*2];
+
+ DDRB = _BV(cycle-6-12) | displayBuffer->pixels[page][cycle*2+1];
+ PORTB = displayBuffer->pixels[page][cycle*2+1];
+ }
+ }
+ else {
+ // Turn everything off
+ DDRD = 0x0;
+ DDRB = 0x0;
+ }
+
+ page++;
+
+ if (page >= SHADES) {
+ page = 0;
+ cycle++;
+ }
+
+ if (cycle > 24) {
+ cycle = 0;
+
+ // If the page should be flipped, do it here.
+ if (videoFlipPage && (displayMode & DOUBLE_BUFFER))
+ {
+ // TODO: is this an atomic operation?
+ videoFlipPage = false;
+
+ videoPage* temp = displayBuffer;
+ displayBuffer = workBuffer;
+ workBuffer = temp;
+ }
+
+ if (videoFlipTimer) {
+ videoFlipTimer = false;
+
+ tempTimer = frontTimer;
+ frontTimer = backTimer;
+ backTimer = tempTimer;
+ }
+ }
+
+ //#ifdef MEASURE_ISR_TIME
+ // digitalWrite(statusPIN, LOW);
+ //#endif
+}
diff --git a/Charliplexing.h b/Charliplexing.h
new file mode 100644
index 0000000..3a912d6
--- /dev/null
+++ b/Charliplexing.h
@@ -0,0 +1,33 @@
+/*
+ Charliplexing.h - Library for controlling the charliplexed led board
+ from JimmiePRodgers.com
+ Created by Alex Wenger, December 30, 2009.
+ Modified by Matt Mets, May 28, 2010.
+ Released into the public domain.
+*/
+
+#ifndef Charliplexing_h
+#define Charliplexing_h
+
+#include <stdint.h>
+#include <stdbool.h>
+
+#define SINGLE_BUFFER 0
+#define DOUBLE_BUFFER 1
+#define GRAYSCALE 2
+
+#define DISPLAY_COLS 14 // Number of columns in the display
+#define DISPLAY_ROWS 9 // Number of rows in the display
+#define SHADES 8 // Number of distinct shades to display, including black, i.e. OFF
+
+extern volatile unsigned int LedSign_tcnt2;
+
+void LedSignInit(uint8_t mode);
+void LedSignSet(uint8_t x, uint8_t y, uint8_t c);
+void LedSignSetBrightness(uint8_t brightness);
+void LedSignFlip(bool blocking);
+void LedSignClear(int set);
+void LedSignHorizontal(int y, int set);
+void LedSignVertical(int x, int set);
+
+#endif
diff --git a/lolshield.c b/lolshield.c
new file mode 100644
index 0000000..fe9559f
--- /dev/null
+++ b/lolshield.c
@@ -0,0 +1,182 @@
+/*
+ Basic LoL Shield Test
+
+ Writen for the LoL Shield, designed by Jimmie Rodgers:
+ http://jimmieprodgers.com/kits/lolshield/
+
+ This needs the Charliplexing library, which you can get at the
+ LoL Shield project page: http://code.google.com/p/lolshield/
+
+ Created by Jimmie Rodgers on 12/30/2009.
+ Adapted from: http://www.arduino.cc/playground/Code/BitMath
+
+ History:
+ December 30, 2009 - V1.0 first version written at 26C3/Berlin
+
+ This is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Version 3 General Public
+ License as published by the Free Software Foundation;
+ or (at your option) any later version.
+
+ This library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Lesser General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public
+ License along with this library; if not, write to the Free Software
+ Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+*/
+
+#include <avr/io.h>
+#include <avr/interrupt.h>
+#include <avr/pgmspace.h> //AVR library for writing to ROM
+#include <util/delay.h>
+#include "Charliplexing.h" //Imports the library, which needs to be
+ //Initialized in setup.
+
+const int blinkdelay = 100; //Sets the time each frame is shown
+
+/*
+The BitMap array is what contains the frame data. Each line is one full frame.
+Since each number is 16 bits, we can easily fit all 14 LEDs per row into it.
+The number is calculated by adding up all the bits, starting with lowest on
+the left of each row. 18000 was chosen as the kill number, so make sure that
+is at the end of the matrix, or the program will continue to read into memory.
+
+Here PROGMEM is called, which stores the array into ROM, which leaves us
+with our RAM. You cannot change the array during run-time, only when you
+upload to the Arduino. You will need to pull it out of ROM, which is covered
+below. If you want it to stay in RAM, just delete PROGMEM
+*/
+const uint16_t BitMap[][9] PROGMEM = {
+//Diaganal swipe across the screen
+{1, 0, 0, 0, 0, 0, 0, 0, 0},
+{3, 1, 0, 0, 0, 0, 0, 0, 0},
+{7, 3, 1, 0, 0, 0, 0, 0, 0},
+{15, 7, 3, 1, 0, 0, 0, 0, 0},
+{31, 15, 7, 3, 1, 0, 0, 0, 0},
+{63, 31, 15, 7, 3, 1, 0, 0, 0},
+{127, 63, 31, 15, 7, 3, 1, 0, 0},
+{255, 127, 63, 31, 15, 7, 3, 1, 0},
+{511, 255, 127, 63, 31, 15, 7, 3, 1},
+{1023, 511, 255, 127, 63, 31, 15, 7, 3},
+{2047, 1023, 511, 255, 127, 63, 31, 15, 7},
+{4095, 2047, 1023, 511, 255, 127, 63, 31, 15},
+{8191, 4095, 2047, 1023, 511, 255, 127, 63, 31},
+{16383, 8191, 4095, 2047, 1023, 511, 255, 127, 63},
+{16383, 16383, 8191, 4095, 2047, 1023, 511, 255, 127},
+{16383, 16383, 16383, 8191, 4095, 2047, 1023, 511, 255},
+{16383, 16383, 16383, 16383, 8191, 4095, 2047, 1023, 511},
+{16383, 16383, 16383, 16383, 16383, 8191, 4095, 2047, 1023},
+{16383, 16383, 16383, 16383, 16383, 16383, 8191, 4095, 2047},
+{16383, 16383, 16383, 16383, 16383, 16383, 16383, 8191, 4095},
+{16383, 16383, 16383, 16383, 16383, 16383, 16383, 16383, 8191},
+{16383, 16383, 16383, 16383, 16383, 16383, 16383, 16383, 16383},
+{16383, 16383, 16383, 16383, 16383, 16383, 16383, 16383, 16383},
+{16382, 16383, 16383, 16383, 16383, 16383, 16383, 16383, 16383},
+{16380, 16382, 16383, 16383, 16383, 16383, 16383, 16383, 16383},
+{16376, 16380, 16382, 16383, 16383, 16383, 16383, 16383, 16383},
+{16368, 16376, 16380, 16382, 16383, 16383, 16383, 16383, 16383},
+{16352, 16368, 16376, 16380, 16382, 16383, 16383, 16383, 16383},
+{16320, 16352, 16368, 16376, 16380, 16382, 16383, 16383, 16383},
+{16256, 16320, 16352, 16368, 16376, 16380, 16382, 16383, 16383},
+{16128, 16256, 16320, 16352, 16368, 16376, 16380, 16382, 16383},
+{15872, 16128, 16256, 16320, 16352, 16368, 16376, 16380, 16382},
+{15360, 15872, 16128, 16256, 16320, 16352, 16368, 16376, 16380},
+{14336, 15360, 15872, 16128, 16256, 16320, 16352, 16368, 16376},
+{12288, 14336, 15360, 15872, 16128, 16256, 16320, 16352, 16368},
+{8192, 12288, 14336, 15360, 15872, 16128, 16256, 16320, 16352},
+{0, 8192, 12288, 14336, 15360, 15872, 16128, 16256, 16320},
+{0, 0, 8192, 12288, 14336, 15360, 15872, 16128, 16256},
+{0, 0, 0, 8192, 12288, 14336, 15360, 15872, 16128},
+{0, 0, 0, 0, 8192, 12288, 14336, 15360, 15872},
+{0, 0, 0, 0, 0, 8192, 12288, 14336, 15360},
+{0, 0, 0, 0, 0, 0, 8192, 12288, 14336},
+{0, 0, 0, 0, 0, 0, 0, 8192, 12288},
+{0, 0, 0, 0, 0, 0, 0, 0, 8192},
+{0, 0, 0, 0, 0, 0, 0, 0, 0},
+
+//Horizontal swipe
+{1, 1, 1, 1, 1, 1, 1, 1, 1} ,
+{3, 3, 3, 3, 3, 3, 3, 3, 3},
+{7, 7, 7, 7, 7, 7, 7, 7, 7},
+{15, 15, 15, 15, 15, 15, 15, 15, 15},
+{31, 31, 31, 31, 31, 31, 31, 31, 31},
+{63, 63, 63, 63, 63, 63, 63, 63, 63},
+{127, 127, 127, 127, 127, 127, 127, 127, 127},
+{255, 255, 255, 255, 255, 255, 255, 255, 255},
+{511, 511, 511, 511, 511, 511, 511, 511, 511},
+{1023, 1023, 1023, 1023, 1023, 1023, 1023, 1023, 1023},
+{2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047},
+{4095, 4095, 4095, 4095, 4095, 4095, 4095, 4095, 4095},
+{8191, 8191, 8191, 8191, 8191, 8191, 8191, 8191, 8191},
+{16383, 16383, 16383, 16383, 16383, 16383, 16383, 16383, 16383},
+{16382, 16382, 16382, 16382, 16382, 16382, 16382, 16382, 16382},
+{16380, 16380, 16380, 16380, 16380, 16380, 16380, 16380, 16380},
+{16376, 16376, 16376, 16376, 16376, 16376, 16376, 16376, 16376},
+{16368, 16368, 16368, 16368, 16368, 16368, 16368, 16368, 16368},
+{16352, 16352, 16352, 16352, 16352, 16352, 16352, 16352, 16352},
+{16320, 16320, 16320, 16320, 16320, 16320, 16320, 16320, 16320},
+{16256, 16256, 16256, 16256, 16256, 16256, 16256, 16256, 16256},
+{16128, 16128, 16128, 16128, 16128, 16128, 16128, 16128, 16128},
+{15872, 15872, 15872, 15872, 15872, 15872, 15872, 15872, 15872},
+{15360, 15360, 15360, 15360, 15360, 15360, 15360, 15360, 15360},
+{14336, 14336, 14336, 14336, 14336, 14336, 14336, 14336, 14336},
+{12288, 12288, 12288, 12288, 12288, 12288, 12288, 12288, 12288},
+{8192, 8192, 8192, 8192, 8192, 8192, 8192, 8192, 8192},
+{0, 0, 0, 0, 0, 0, 0, 0, 0},
+{18000}
+};
+
+void DisplayBitMap()
+{
+ bool run=true; //While this is true, the screen updates
+ uint8_t frame = 0; //Frame counter
+ uint8_t line = 0; //Row counter
+ uint8_t led;
+ unsigned long data; //Temporary storage of the row data
+
+ while(run == true) {
+ for(line = 0; line < 9; line++) {
+
+ //Here we fetch data from program memory with a pointer.
+ data = pgm_read_word_near (&BitMap[frame][line]);
+
+ //Kills the loop if the kill number is found
+ if (data==18000){
+ run=false;
+ }
+
+ //This is where the bit-shifting happens to pull out
+ //each LED from a row. If the bit is 1, then the LED
+ //is turned on, otherwise it is turned off.
+ else for (led=0; led<14; ++led) {
+ if (data & (1<<led)) {
+ LedSignSet(led, line, 1);
+ }
+ else {
+ LedSignSet(led, line, 0);
+ }
+ }
+
+ }
+
+ //Delays the next update
+ _delay_ms(blinkdelay);
+ frame++;
+ }
+}
+
+
+int main() {
+ LedSignInit(SINGLE_BUFFER); //Initializes the screen
+
+ sei();
+
+ while (true) {
+ DisplayBitMap(); //Displays the bitmap
+ }
+
+ return 0;
+}