/* Name: main.c
 * Author: <insert your name here>
 * Copyright: <insert your copyright message here>
 * License: <insert your license reference here>
 */

#include <avr/io.h>
#include <avr/interrupt.h>
#include <util/delay.h>



#define output(directions,pin) (directions |= pin) // set port direction for output
#define set(port,pin) (port |= pin) // set port pin
#define clear(port,pin) (port &= (~pin)) // clear port pin
#define pin_test(pins,pin) (pins & pin) // test for port pin
#define bit_test(byte,bit) (byte & (1 << bit)) // test for bit set
#define bit_delay_time 100 // bit delay for 9600 with overhead
#define bit_delay() _delay_us(bit_delay_time) // RS232 bit delay
#define half_bit_delay() _delay_us(bit_delay_time/2) // RS232 half bit delay
#define char_delay() _delay_ms(10) // char delay

#define serial_port PORTA
#define serial_direction DDRA
#define serial_pin_out (1 << PA1)

#define nsamples 1 // number of samples to accumulate



#define ENABLE_TIMER1_INTERRUPT( )       ( TIMSK1 |= ( 1<< OCIE1A ) )
#define DISABLE_TIMER1_INTERRUPT( )      ( TIMSK1 &= ~( 1<< OCIE1A ) )
#define CLEAR_TIMER1_INTERRUPT( )        ( TIFR1 |= ((1 << OCF1A) ) )


enum eState
{
    eIDLE = 0,
    eINPULSE,
    eINMESSAGE,
};


typedef struct { unsigned char data[5]; } tSymbol;

// Font, uppercase and numbers only
tSymbol font[] = {
    {0x0e, 0x11, 0x11, 0x0e, 0x00}, // 0 4
    {0x02, 0x1f, 0x00, 0x00, 0x00}, // 1
    {0x12, 0x19, 0x15, 0x12, 0x00}, // 2
    {0x0a, 0x11, 0x15, 0x0a, 0x00}, // 3
    {0x0f, 0x08, 0x1c, 0x08, 0x00}, // 4
    {0x17, 0x15, 0x15, 0x08, 0x00}, // 5
    {0x0e, 0x15, 0x15, 0x08, 0x00}, // 6
    {0x11, 0x09, 0x05, 0x03, 0x00}, // 7
    {0x0e, 0x15, 0x15, 0x0e, 0x00}, // 8
    {0x06, 0x15, 0x15, 0x0e, 0x00}, // 9
    
    {0x1f, 0x02, 0x04, 0x1f, 0x00}, // M 4
    {0x1f, 0x05, 0x05, 0x02, 0x00}, // P 4
    {0x1f, 0x05, 0x0d, 0x12, 0x00}, // R 4
};
/*
    {0x10, 0x0c, 0x0b, 0x0c, 0x10}, // A
    {0x1f, 0x15, 0x15, 0x0a, 0x00}, // B 4
    {0x0e, 0x11, 0x11, 0x0a, 0x00}, // C 4
    {0x10, 0x08, 0x04, 0x02, 0x01}, // D
    {0x10, 0x08, 0x04, 0x02, 0x01}, // E
    {0x10, 0x08, 0x04, 0x02, 0x01}, // F
    {0x10, 0x08, 0x04, 0x02, 0x01}, // G
    {0x10, 0x08, 0x04, 0x02, 0x01}, // H
    {0x10, 0x08, 0x04, 0x02, 0x01}, // I
    {0x10, 0x08, 0x04, 0x02, 0x01}, // J
    {0x10, 0x08, 0x04, 0x02, 0x01}, // K
    {0x10, 0x08, 0x04, 0x02, 0x01}, // L
    {0x1f, 0x02, 0x04, 0x1f, 0x00}, // M 4
    {0x10, 0x08, 0x04, 0x02, 0x01}, // N
    {0x10, 0x08, 0x04, 0x02, 0x01}, // O
    {0x1f, 0x05, 0x05, 0x02, 0x00}, // P 4
    {0x10, 0x08, 0x04, 0x02, 0x01}, // Q
    {0x1f, 0x05, 0x0d, 0x12, 0x00}, // R 4
    {0x10, 0x08, 0x04, 0x02, 0x01}, // S
    {0x10, 0x08, 0x04, 0x02, 0x01}, // T
    {0x10, 0x08, 0x04, 0x02, 0x01}, // U
    {0x10, 0x08, 0x04, 0x02, 0x01}, // V
    {0x10, 0x08, 0x04, 0x02, 0x01}, // W
    {0x10, 0x08, 0x04, 0x02, 0x01}, // X
    {0x10, 0x08, 0x04, 0x02, 0x01}, // Y
    {0x10, 0x08, 0x04, 0x02, 0x01}, // Z
};

*/

volatile uint32_t ticks = 0;



void put_char(volatile unsigned char *port, unsigned char pin, char txchar) {
    //
    // send character in txchar on port pin
    //    assumes line driver (inverts bits)
    //
    // start bit
    //
    clear(*port,pin);
    bit_delay();
    //
    // unrolled loop to write data bits
    //
    if bit_test(txchar,0)
        set(*port,pin);
    else
        clear(*port,pin);
    bit_delay();
    if bit_test(txchar,1)
        set(*port,pin);
    else
        clear(*port,pin);
    bit_delay();
    if bit_test(txchar,2)
        set(*port,pin);
    else
        clear(*port,pin);
    bit_delay();
    if bit_test(txchar,3)
        set(*port,pin);
    else
        clear(*port,pin);
    bit_delay();
    if bit_test(txchar,4)
        set(*port,pin);
    else
        clear(*port,pin);
    bit_delay();
    if bit_test(txchar,5)
        set(*port,pin);
    else
        clear(*port,pin);
    bit_delay();
    if bit_test(txchar,6)
        set(*port,pin);
    else
        clear(*port,pin);
    bit_delay();
    if bit_test(txchar,7)
        set(*port,pin);
    else
        clear(*port,pin);
    bit_delay();
    //
    // stop bit
    //
    set(*port,pin);
    bit_delay();
    //
    // char delay
    //
    bit_delay();
}


void put_string(volatile unsigned char *port, unsigned char pin, char *str) {
    static int index;
    //
    // print a null-terminated string
    //
    // JWF !! Sending an empty string will fail !!
    index = 0;
    do {
        put_char(port, pin, str[index]);
        ++index;
    } while (str[index] != 0);
}



unsigned char nibble_to_hex(unsigned char b)
{
    unsigned char ret = b & 0x0f;
    if(ret>9)
        ret += ('A'-10);
    else
        ret += '0';
    return ret;
}


void put_hex(unsigned char in)
{
    put_char(&serial_port, serial_pin_out, nibble_to_hex(in>>4));
    put_char(&serial_port, serial_pin_out, nibble_to_hex(in));
}



void led_display(unsigned char v)
{
    unsigned char out;
    
    v = ~v;
    
    // bits 0, 1
    out = (v & 0x03) << 2;
    // bit 2
    out |= ((v & 0x04) << 5);
    PORTA = out;
    
    // bits 3, 4
    out = ((v & 0x18) >> 3);
    PORTB = out;
}




ISR(TIM1_COMPA_vect)
{
    TCNT1 = 0;
    ticks++;
}



int main(void)
{
    int i;
    int count = 0;
    uint32_t accum = 0;
    int accum_count = 0;
    uint32_t average;
    
    uint32_t last_pulse_time=0;
    uint32_t pulse_width = 0;
    char state = eIDLE;
    
    int rpm;
    
    uint32_t last_ticks = 0;
    uint32_t sym_col_step = 1;
    unsigned char sym_col = 0;  // column of symbol we're displaying
    unsigned char message_index = 0;
    
    char message[16];

    unsigned char pattern = 0;

    
    PORTA = 0;
    DDRA = (1<<PA2) | (1<<PA3) | (1<<PA7);
    
    PORTB = 0;
    DDRB = (1<<PB0) | (1<<PB1);

    set(serial_port, serial_pin_out);
    output(serial_direction, serial_pin_out);

    //
    // init A/D
    //
    ADMUX = (0 << REFS1) | (0 << REFS0) // Vcc ref
    | (0 << ADLAR) // right adjust
    | (0 << MUX4) | (0 << MUX3) | (0 << MUX2) | (0 << MUX1) | (0 << MUX0); // ADC0
    ADCSRA = (1 << ADEN) // enable
    | (1 << ADPS2) | (1 << ADPS1) | (1 << ADPS0); // prescaler /128

    
    //
    // Init Timer1
    //
    DISABLE_TIMER1_INTERRUPT( );
    TCCR1A = 0x00;    //Init.
    TCCR1B = 0x00;    //Init.
    TCCR1B = (1 << WGM12);			// Timer in CTC mode.
    TCCR1B |= 0x03;                 // Set prescaler. 1/64
    TCNT1 = 0;
    OCR1A = 125;                    // 1ms ticks
    ENABLE_TIMER1_INTERRUPT( );
    
    sei();
    
    for(;;)
    {
        
        //
        // initiate conversion
        //
        ADCSRA |= (1 << ADSC);
        //
        // wait for completion
        //
        while (ADCSRA & (1 << ADSC))
            ;
        //
        // add result
        //
        accum += ADC;
        accum_count++;
        
        average = accum / accum_count;

        if(ADC > average+3)
        {
            if(state != eINPULSE)
            {
                // new pulse detected
                state = eINPULSE;
                pulse_width = ticks - last_pulse_time;
                last_pulse_time = ticks;
                
                //rpm = (60*1000) / pulse_width;
                rpm = 1000 / pulse_width;
                sym_col = 0;
                
                sprintf(message, "%d", rpm);
                message_index = 0;
            }
        }
        else
        {
            if(state == eINPULSE)
                state = eINMESSAGE;
        }
        
        
        
        // Display message
        if(state != eIDLE)
        {
            // Move to next pixel column
            if(ticks > (last_ticks+sym_col_step))
            {
                unsigned char symbol;
            
                last_ticks = ticks;
            
                // convert message char to symbol index in font
                symbol = message[message_index] - '0';
                if(symbol > 9)
                    symbol = message[message_index] - 'A' + 10;
            
                if( sym_col >= 5)
                    pattern = 0; // inter symbol space
                else
                    pattern = font[symbol].data[sym_col];
        
                // Move to next column in symbol, or to next symbol in string
                sym_col++;
                if(sym_col >= 7)
                {
                    sym_col = 0;
                    message_index++;
                    if(message_index >= strlen(message))
                    {
                        state = eIDLE;
                    }
                }
        
            }
        }
        
        led_display(pattern);
        
        

#if 0
        for(i = 0; i < 20; i++){
            _delay_ms(25);
        }
        
        //pattern++;
        //if(pattern>32)
        //    pattern = 0;
        
        led_display(pattern);
        
        put_char(&serial_port, serial_pin_out, ' ');
        char_delay();
        put_char(&serial_port, serial_pin_out, 'C');
        char_delay();
        put_char(&serial_port, serial_pin_out, ':');
        char_delay();

        accum = 0;
        for (count = 0; count < nsamples; ++count) {
            //
            // initiate conversion
            //
            ADCSRA |= (1 << ADSC);
            //
            // wait for completion
            //
            while (ADCSRA & (1 << ADSC))
                ;
            //
            // add result
            //
            accum += ADC;
        }

        //accum = pattern;
        
        //
        // send result
        //
//        put_hex((accum >> 16) & 255);
//        char_delay();
//        put_hex((accum >> 8) & 255);
//        char_delay();
//        put_hex(accum & 255);
//        char_delay();
        
//        put_dec(accum&0xff);
//        char_delay();

//        put_char(&serial_port, serial_pin_out, 10);
//        char_delay();
//        put_char(&serial_port, serial_pin_out, 13);
//        char_delay();
        
        {
            sprintf(temp, " %ld\r\n", pattern);
            put_string(&serial_port, serial_pin_out, temp);
        }
#endif

        
    }
    return 0;   /* never reached */
}
