/*

MegaRotate - Show fancy LED-Stuff while rotating the avr like hell

Copyright (C) 2008 Claas Anders Rathje <admiralcascade@web.de>

This program is free software; you can redistribute it and/or modify it under
the terms of the GNU General Public License as published by the Free Software
Foundation; either version 3 of the License, or (at your option) any later
version.

This program 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 General Public License
for more details.

You should have received a copy of the GNU General Public License along
with this program; if not, see <http://www.gnu.org/licenses/>.


Additional Information:
Current Date: 200901311250
Look at http://forum.mikrokopter.de/topic-5794.html for demonstration,
shematics, videos and whatever.
Discussions, Improvements and Hints are welcome.

This is the author's first program for AVRs, so please: don't be too hard :)

 */

// lets use interrupts
#include <avr/interrupt.h>
// for atoi we need the following
#include <stdlib.h>

// global overflow counter
volatile unsigned long overflows = 0;
// how many cpu cycles we can spend for ONE splitted-round-part?
volatile unsigned long ticksperpart = 0;
// did we pass another round?
volatile unsigned char roundpassed = 0;
// global knowledge of the rpm we made
volatile unsigned int rpm = 0;

const unsigned char bilder[5][128] = {//{124,4,24,4,120,0,56,84,84,84,24,0,12,82,82,82,62,0,32,84,84,84,120,0,124,8,4,4,8,0,56,68,68,68,56,0,4,63,68,64,32,0,32,84,84,84,120,0,4,63,68,64,32,0,56,84,84,84,24,0,0,0,0,0,0,0,0,0,0,0,0,0,124,4,24,4,120,0,127,16,40,68,0,0,8,8,8,8,8,0,56,84,84,84,24,0,124,20,20,20,8,0,0,68,125,64,0,0,0,96,96,0,0,0,56,68,68,72,127,0,56,84,84,84,24,0,0,0},	// megarotate mk-epi.de
    {192, 192, 192, 224, 224, 240, 240, 248, 248, 252, 252, 252, 254, 254, 254, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 254, 254, 254, 252, 252, 248, 248, 248, 240, 240, 240, 240, 224, 224, 224, 224, 224, 224, 224, 224, 224, 224, 224, 224, 224, 224, 224, 224, 224, 224, 240, 240, 240, 240, 248, 240, 240, 240, 240, 224, 224, 224, 224, 224, 224, 224, 224, 224, 224, 224, 224, 224, 224, 224, 224, 224, 224, 240, 240, 240, 240, 248, 248, 248, 252, 252, 254, 254, 254, 254, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 254, 254, 254, 252, 252, 252, 248, 248, 240, 240, 224, 224, 192, 192}, //heart
    {1, 1, 1, 1, 1, 1, 49, 121, 121, 121, 121, 121, 121, 241, 241, 241, 97, 97, 65, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 33, 33, 49, 25, 25, 25, 25, 17, 17, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 17, 17, 17, 17, 25, 25, 25, 25, 49, 33, 33, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 65, 97, 97, 241, 241, 241, 121, 121, 121, 121, 121, 121, 49, 1, 1, 1, 1, 1}, // smiley
    //{170,85,170,85,170,85,170,85,170,85,170,85,170,85,170,85,170,85,170,85,170,85,170,85,170,85,170,85,170,85,170,85,170,85,170,85,170,85,170,85,170,85,170,85,170,85,170,85,170,85,170,85,170,85,170,85,170,85,170,85,170,85,170,85,170,85,170,85,170,85,170,85,170,85,170,85,170,85,170,85,170,85,170,85,170,85,170,85,170,85,170,85,170,85,170,85,170,85,170,85,170,85,170,85,170,85,170,85,170,85,170,85,170,85,170,85,170,85,170,85,170,85,170,85,170,85,170,85}, // mesh
    {255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 193, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 127, 63, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 3, 31, 127, 255, 255, 255, 255, 255, 255, 255}, // atom
    {241, 241, 249, 249, 225, 193, 193, 129, 129, 129, 129, 133, 133, 141, 141, 157, 189, 253, 253, 253, 253, 253, 253, 253, 253, 253, 253, 253, 253, 253, 253, 253, 253, 253, 253, 253, 253, 253, 253, 253, 253, 253, 253, 221, 205, 205, 205, 205, 193, 193, 129, 129, 128, 129, 193, 225, 241, 225, 193, 129, 128, 129, 129, 193, 193, 205, 205, 205, 205, 221, 253, 253, 253, 253, 253, 253, 253, 253, 253, 253, 253, 253, 253, 253, 253, 253, 253, 253, 253, 253, 253, 253, 253, 253, 253, 253, 189, 157, 141, 141, 133, 133, 129, 129, 129, 129, 193, 193, 225, 249, 249, 241, 65, 65, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1}, // batman
    {255, 252, 248, 240, 224, 224, 192, 192, 128, 128, 128, 128, 0, 0, 0, 0, 0, 128, 128, 128, 128, 192, 192, 224, 240, 248, 252, 255, 254, 252, 248, 240, 240, 224, 224, 224, 192, 192, 192, 192, 128, 128, 128, 128, 128, 192, 192, 224, 224, 240, 248, 254, 255, 255, 254, 252, 248, 248, 240, 240, 224, 224, 224, 192, 192, 192, 224, 224, 224, 240, 240, 248, 252, 254, 255, 255, 254, 252, 248, 240, 224, 224, 192, 192, 192, 128, 128, 128, 192, 192, 192, 192, 192, 224, 224, 224, 240, 248, 248, 252, 254, 255, 252, 240, 224, 224, 192, 192, 128, 128, 128, 128, 0, 0, 0, 0, 128, 128, 128, 128, 192, 192, 192, 224, 224, 240, 248, 252} // star
};

// how many pictures are there? easiest way to differ between image and rpm display
const unsigned char bilder_num = 5;

// ____ RPM:
volatile unsigned char rpmtext[128] = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 62, 65, 65, 65, 34, 0, 60, 64, 64, 32, 124, 0, 124, 8, 4, 4, 8, 0, 124, 8, 4, 4, 8, 0, 56, 84, 84, 84, 24, 0, 124, 8, 4, 4, 120, 0, 4, 63, 68, 64, 32, 0, 0, 0, 0, 0, 0, 0, 127, 9, 25, 41, 70, 0, 127, 9, 9, 9, 6, 0, 127, 2, 12, 2, 127, 0, 0, 54, 54, 0, 0};


// number stubs so they can be inserted as RPM value
unsigned char numbers[10][5] = {
    {62, 81, 73, 69, 62},
    {0, 66, 127, 64, 0},
    {66, 97, 81, 73, 70},
    {33, 65, 69, 75, 49},
    {24, 20, 18, 127, 16},
    {39, 69, 69, 69, 57},
    {60, 74, 73, 73, 48},
    {3, 1, 113, 9, 7},
    {54, 73, 73, 73, 54},
    {6, 73, 73, 41, 30}
};

/**
 * handle interrupts caused by the hall sensor
 */
ISR(SIG_INPUT_CAPTURE1) {
    // calculate the ticks per circle-part: 128 parts mult 8 pixel = 1024 pixel
    ticksperpart = (((unsigned long) TCNT1) + (overflows * 65536UL)) >> 7;
    // try to calculate the rpm are doing based on the cycles we spent
    rpm = (int) ((60UL * (unsigned long) F_CPU) / (((unsigned long) TCNT1) + (overflows * 65536UL)));
    // reset the timer
    TCNT1 = 0;
    // reset overflows
    overflows = 0;
    // flag up, the round is over
    roundpassed = 1;

    // test-stup for hall sensor, one round toggles all LEDs
    /*if (PORTD == 0x00) PORTD = 0xFF;
    else PORTD = 0x00;*/
}

/**
 * count the overflows caused by the timer
 */
ISR(SIG_OVERFLOW1) {
    overflows++;
}

/**
 * Main
 */
int main(void) {
    // Input Capture Mode: rising flank, no prescaler
    TCCR1B = (1 << ICES1) | (1 << CS10);
    // set up interrupts: Capture + Overflow
    TIMSK = (1 << TICIE1) | (1 << TOIE1);

    // set up PortB as input
    DDRB = 0x00;
    // PortD as output
    DDRD = 0xFF;
    // all LEDs ON
    PORTD = 0xFF;

    // ennable interrupts
    sei();

    // which part are we currently displaying
    unsigned char part = 0;
    // which animation is currently running?
    unsigned char which = 0;
    // cpu-ticks left till we are reaching the next part
    unsigned long tickstopass = 0;
    // rounds is the current picture already running
    short rounds = 0, oldrounds = 500;
    // string for RPM
    char drehzahl[11] = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0};
    // counting stubs
    int i, k;

    while (1) {
        if (roundpassed == 1) {
            // flag down
            roundpassed = 0;
            rounds++;
            // enough rounds shown, switch to next image
            if (rounds > 800) {
                rounds = 0;
                which++;
                //which = (++which)%bilder_num;
                if (which > bilder_num) which = 0;
            }
            // first part!
            part = 0;
            // first barrier at 1*ticksperpart
            tickstopass = ticksperpart;

            // rpm or image to display?
            if (which == bilder_num) {
                // do not update the array every round, easier to read
                if (oldrounds > 20) {
                    // convert rpm to a string
                    itoa(rpm, drehzahl, 10);
                    // who needs more that 99k?
                    for (i = 0; i < 6; i++) {
                        // in case we did not get a number blank the digits
                        // 48 is the 0 in ascii
                        if ((drehzahl[i] - 48) < 0 || (drehzahl[i] - 48) > 9) {
                            k = i * 6;
                            rpmtext[k++] = 0;
                            rpmtext[k++] = 0;
                            rpmtext[k++] = 0;
                            rpmtext[k++] = 0;
                            rpmtext[k++] = 0;
                            rpmtext[k] = 0;
                        } else {
                            // copy the current number-patterns
                            for (k = 0; k < 6; k++) {
                                if (k == 5) rpmtext[(i * 6) + k] = 0;
                                else rpmtext[(i * 6) + k] = numbers[(int) drehzahl[i] - 48][k];
                            }
                        }
                    }
                    oldrounds = 0;
                } else {
                    oldrounds++;
                }
                // show part of the text
                PORTD = rpmtext[part];
            } else {
                // show part of the image
                PORTD = bilder[which][part];
            }
        } else if (((unsigned long) TCNT1 + (overflows * 65536UL)) > tickstopass) {
            // if reached next part

            part++;
            // just in case the round too longer do not count part higher than we have data
            // last part maybe gets stretched but no datagarbage is displayed
            if (part > 127) part = 127;
            if (which != bilder_num) {
                // next image part
                PORTD = bilder[which][part];
            } else {
                // next text part
                PORTD = rpmtext[part];
            }
            // next barrier
            tickstopass += ticksperpart;
        } else if (((unsigned long) TCNT1 + (overflows * 65536UL)) > (tickstopass - (ticksperpart >> 2))) {
            PORTD = 0x00; // LEDs off again, solid lines do not look that pretty
        }
    }
}

