Firmware overview

#include <avr/io.h>
#include <avr/interrupt.h>
#include <avr/wdt.h>
#include <avr/fuse.h>
#include <string.h>


/*****************************************************************
 * Help macros
 *****************************************************************/
#define STATIC_ASSERT(expr) extern char static_assert[ (!!(expr))*2 - 1]
#define elements_of(array) (sizeof(array) / sizeof(array[0]))


FUSES =
{
  .low = LFUSE_DEFAULT, //Internal RC 1MHz
  .high = (FUSE_EESAVE & FUSE_SPIEN),
};



/*****************************************************************
 * HW defs
 *****************************************************************/
static volatile uint8_t red_led = 0;
static volatile uint8_t green_led = 0;
static volatile uint8_t red_led_1 = 0;
static volatile uint8_t green_led_1 = 0;
static volatile uint16_t volume_activated_timer = 0;
static volatile uint8_t timer1ovfl = 0;


enum EGO_X012 {
    EGO_X012_INACTIVE    = 1 * _BV(PA6) | 1 * _BV(PA5) | 1 * _BV(PA4),
    EGO_X012_PLAY_UNHOOK = 0 * _BV(PA6) | 0 * _BV(PA5) | 0 * _BV(PA4),
    EGO_X012_STOP_ONHOOK = 1 * _BV(PA6) | 0 * _BV(PA5) | 0 * _BV(PA4),
    EGO_X012_VOL_UP      = 0 * _BV(PA6) | 0 * _BV(PA5) | 1 * _BV(PA4),
    EGO_X012_VOL_DOWN    = 1 * _BV(PA6) | 0 * _BV(PA5) | 1 * _BV(PA4),
    EGO_X012_NEXT        = 0 * _BV(PA6) | 1 * _BV(PA5) | 0 * _BV(PA4),
    EGO_X012_PREV        = 1 * _BV(PA6) | 1 * _BV(PA5) | 0 * _BV(PA4),
};

#define EGO_RED_LED_IS_ON() (!(PINB & _BV(PB5)))
#define EGO_GREEN_LED_IS_ON() (!(PINA & _BV(PA7)))
#define EGO_MUTE_ON() (!(PINB & _BV(PB3)))
#define EGO_X012(x) PORTA = (PORTA & ~EGO_X012_INACTIVE) | ((x) & EGO_X012_INACTIVE)
#define EGO_INIT() DDRA &= ~(_BV(PA7));DDRA |= EGO_X012_INACTIVE;  PORTA |= _BV(PA7); DDRB &= ~(_BV(PB5) | _BV(PB3)); PORTB |= _BV(PB5);


//Secondary buttons and status LEDs

#define BTN1_LED_GREEN_ON() PORTA |= _BV(PA2)
#define BTN1_LED_GREEN_OFF() PORTA &= ~_BV(PA2)
#define BTN1_LED_GREEN_INIT() DDRA |= _BV(PA2);BTN1_LED_GREEN_OFF()

#define BTN2_LED_RED_ON() PORTA |= _BV(PA0)
#define BTN2_LED_RED_OFF() PORTA &= ~_BV(PA0)
#define BTN2_LED_RED_INIT() DDRA |= _BV(PA0);BTN2_LED_RED_OFF()

#define BTN_GREEN_PRESSED()  (!(PINA & _BV(PA3)))
#define BTN_RED_PRESSED() (!(PINA & _BV(PA1)))
#define BTN_INIT() DDRA &= ~(_BV(PA3) | _BV(PA1));PORTA |= (_BV(PA3) | _BV(PA1))



enum {
    STRW_UP_INPUT  = 7,
    STRW_DOWN_INPUT = 9
};

#define VOLTAGE_TO_ADC_VALUE(v) (uint16_t)(v * ((1UL<<16)-1) / (2.56 * 2))

#if 0
enum {
    VOLTAGE1 = VOLTAGE_TO_ADC_VALUE(5.0 * 1/6),
    VOLTAGE2 = VOLTAGE_TO_ADC_VALUE(5.0 * 3/6),
    VOLTAGE3 = VOLTAGE_TO_ADC_VALUE(5.0 * 5/6)
};
#endif
enum {
    VOLTAGE1 = VOLTAGE_TO_ADC_VALUE(0.5),
    VOLTAGE2 = VOLTAGE_TO_ADC_VALUE(1.5),
    VOLTAGE3 = VOLTAGE_TO_ADC_VALUE(3.0),
    MUTE_ON_THRES =  VOLTAGE_TO_ADC_VALUE(0.6)
};

volatile uint16_t voltages[] = {VOLTAGE1,VOLTAGE2,VOLTAGE3};

typedef enum {
    STRW_INACTIVE = 0,
    STRW_HIGH = 1,
    STRW_LOW = 2,
    STRW_GND = 3
} btn_t;

typedef enum {
    STRW_NONE       = (STRW_INACTIVE << 2) + STRW_INACTIVE,
    STRW_UP         = (STRW_LOW      << 2) + STRW_INACTIVE,
    STRW_DOWN       = (STRW_INACTIVE << 2) + STRW_LOW,
    STRW_VOL_UP     = (STRW_HIGH     << 2) + STRW_INACTIVE,
    STRW_VOL_DOWN   = (STRW_INACTIVE << 2) + STRW_HIGH,
    STRW_MODE       = (STRW_GND      << 2) + STRW_INACTIVE,
    STRW_PWR        = (STRW_INACTIVE << 2) + STRW_GND,
    STRW_DOWN_MODE  = (STRW_GND      << 2) + STRW_LOW,
} wheel_control_t;


static void adc_init(void)
{
    ADMUX = _BV(REFS2) * 1 | _BV(REFS1) * 1 | _BV(REFS0) * 0 | _BV(ADLAR); //Internal 2.56V ref, no CAP decoup. Left adjusted
    ADCSRA = _BV(ADEN) | _BV(ADPS2) | _BV(ADPS1) | _BV(ADPS0); //Set prescaler and do a dummy conversion
    ADCSRB = _BV(REFS2) * 1; //Internal 2.56V ref
}

static uint16_t measure_adc(uint8_t ch) {
    ADMUX = (ADMUX  & ~0x1F) | (ch & 0xF);
    ADCSRA |= _BV(ADEN);
    ADCSRA |= _BV(ADSC);
    while (bit_is_set(ADCSRA, ADSC))
    ;
    uint16_t value = ADC;
    return value;
}

static btn_t convert_from_voltage(uint16_t measurement) {
    if (measurement < VOLTAGE1) {
        return STRW_GND;
    }
    else if (measurement < VOLTAGE2) {
        return STRW_LOW;
    }
    else if (measurement < VOLTAGE3) {
        return STRW_HIGH;
    }
    else {
        return STRW_INACTIVE;
    }
}

#define debug_voltage(v) \
{ \
    red_led = v & 0x1;\
    green_led = v & 0x2;\
}

static wheel_control_t get_wheel_control(void)
{
    btn_t b1 = convert_from_voltage(measure_adc(STRW_UP_INPUT));
    btn_t b2 = convert_from_voltage(measure_adc(STRW_DOWN_INPUT));
    //debug_voltage(b1);
    return (b1 << 2 | b2);
}


/*****************************************************************
 * Timing
 *****************************************************************/


STATIC_ASSERT(F_CPU > 0);

#define T1_OVERFLOW_TIME_MS ((4UL*256UL*1000UL) / F_CPU)
#define MS_TO_TICKS(t) (t/T1_OVERFLOW_TIME_MS)
#define VOLUME_ACTIVATE_TIMEOUT MS_TO_TICKS(300)

STATIC_ASSERT(VOLUME_ACTIVATE_TIMEOUT > 1);


/*****************************************************************
 * Main loop
 *****************************************************************/

static void default_strwh_inactive(void)
{
    if (BTN_RED_PRESSED()) {
        EGO_X012(EGO_X012_STOP_ONHOOK);
    }
    else if (BTN_GREEN_PRESSED()) {
        EGO_X012(EGO_X012_PLAY_UNHOOK);
    }
    else {
        EGO_X012(EGO_X012_INACTIVE);
    }
}

//Interrupts are used to create a software PWM for the secondary RED and GREEN LEDs

ISR(SIG_OVERFLOW1)
{
    if (!green_led_1) BTN1_LED_GREEN_ON(); else BTN1_LED_GREEN_OFF();
    if (!red_led_1) BTN2_LED_RED_ON(); else BTN2_LED_RED_OFF();
    if (volume_activated_timer > 0) volume_activated_timer--;
    timer1ovfl++;
}

ISR(SIG_OUTPUT_COMPARE1A)
{
    if (!green_led) {
        BTN1_LED_GREEN_OFF();
    }
}


ISR(SIG_OUTPUT_COMPARE1B)
{
    if (!red_led) {
        BTN2_LED_RED_OFF();
    }
}

int main(void)
{
    wheel_control_t prev = STRW_NONE;
    uint8_t volume_activated = 0;

    //wdt_enable(WDTO_2S);
    adc_init();
    BTN2_LED_RED_INIT();
    BTN1_LED_GREEN_INIT();
    BTN_INIT();
    EGO_INIT();
    EGO_X012(EGO_X012_INACTIVE);

    /* configure timer 1 for a rate of 1M/(4 * 256) = () */
    PLLCSR = 0;
    TCCR1A = 0;
    TCCR1B = 3;
    TCCR1D = 0;
    TCCR1E = 0;
    TIMSK |= _BV(TOIE1) | _BV(OCIE1A) | _BV(OCIE1B);
    OCR1A = 2 * 256/100; //5% backlight on red
    OCR1B = 3 * 256/100; //10% backlight on green

    sei();

    // main event loop
    for (;;)
    {
        //Watchdog
        wdt_reset();
        if (timer1ovfl > 20)
        {
            wheel_control_t  ctrl = 0;
            timer1ovfl = 0;
            ctrl = get_wheel_control();

            //Active the leds
            red_led = EGO_RED_LED_IS_ON();
            green_led = EGO_GREEN_LED_IS_ON();

            if (volume_activated_timer == 0) {
                prev = STRW_NONE;
            }
            if (BTN_RED_PRESSED()) red_led_1 = 1; else red_led_1 = 0;
            if (BTN_GREEN_PRESSED()) green_led_1 = 1; else green_led_1 = 0;

            if (0) {
                default_strwh_inactive();
            }
            else {
                if (EGO_MUTE_ON() && EGO_RED_LED_IS_ON()) {
                    switch (ctrl){
                        default:
                        case STRW_NONE:      default_strwh_inactive();break;
                        case STRW_VOL_UP:    EGO_X012(EGO_X012_VOL_UP);break;
                        case STRW_VOL_DOWN:  EGO_X012(EGO_X012_VOL_DOWN);break;
                        case STRW_UP:        EGO_X012(EGO_X012_PLAY_UNHOOK);break;
                        case STRW_DOWN:      EGO_X012(EGO_X012_STOP_ONHOOK);break;
                        case STRW_DOWN_MODE: EGO_X012(EGO_X012_STOP_ONHOOK); break;
                    }
                }
                else {
                    if (EGO_GREEN_LED_IS_ON() && !EGO_RED_LED_IS_ON()) {

                        switch (ctrl){
                            default:
                            case STRW_NONE:
                                default_strwh_inactive();
                                break;
                            case STRW_VOL_UP:
                                {
                                    volume_activated_timer = VOLUME_ACTIVATE_TIMEOUT;
                                    if (prev == STRW_VOL_DOWN) {
                                        volume_activated = 1;
                                    }
                                    if (volume_activated) {
                                        EGO_X012(EGO_X012_VOL_UP);
                                    }
                                }
                                break;
                            case STRW_VOL_DOWN:
                                {
                                    volume_activated_timer = VOLUME_ACTIVATE_TIMEOUT;
                                    if (prev == STRW_VOL_UP) {
                                        volume_activated = 1;
                                    }
                                    if (volume_activated) {
                                        EGO_X012(EGO_X012_VOL_DOWN);
                                    }
                                }
                                break;

                            case STRW_UP:
                                EGO_X012(EGO_X012_NEXT);
                                break;
                            case STRW_DOWN:
                                EGO_X012(EGO_X012_PREV);
                                break;
                            case STRW_DOWN_MODE:
                                EGO_X012(EGO_X012_STOP_ONHOOK);
                                break;
                        }
                    }
                    else {
                        switch (ctrl){
                            default:
                            case STRW_NONE:
                                default_strwh_inactive();
                                break;
                            case STRW_DOWN_MODE: EGO_X012(EGO_X012_STOP_ONHOOK);
                                break;
                        }
                    }
                }
            }

            if (ctrl != STRW_NONE) {
                prev = ctrl;
            }

            if (ctrl != STRW_VOL_DOWN || ctrl != STRW_VOL_UP) {
                volume_activated = 0;
            }
        }
    }
    return 0;
}


Generated on Fri Jun 4 14:16:06 2010 for Audio and steering wheel adapter EGO Talk <=> Infiniti FX 35 by  doxygen 1.6.3