Topic: tech dcc pc main

tech dcc pc main > controller.c

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

#include "cfg.h"
#include "dcc.h"
#include "lcd.h"
#include "led.h"
#include "manual.h"
#include "menu.h"
#include "millis.h"
#include "packet.h"
#include "pins.h"
#include "program_loco.h"
#include "settings.h"
#include "sizeof_array.h"
#include "wdt.h"

#define START_SCREEN_MILLIS 750

bool g_show_current = false;
volatile unsigned char g_mcusr_cache  __attribute__ ((section (".noinit")));

void startup_screen()
{
    typedef struct
    {
        bool show_version;
        bool show_reset;
        millis_type start_time;
    } state;

    void draw(/*void *v*/)
    {
        /*LCD_PutS((g_mcusr_cache & (1 << WDRF)) ? " WD " : " wd ");*/
        /*LCD_PutS((g_mcusr_cache & (1 << BORF)) ? " BO " : " bo ");*/
        /*LCD_PutS((g_mcusr_cache & (1 << EXTRF)) ? " EX " : " ex ");*/
        /*LCD_PutS((g_mcusr_cache & (1 << PORF)) ? " PO " : " po ");*/
        LCD_PutS("    PC10 DCC    ");
        LCD_Move(0, 1);
        LCD_PutS("   Controller   ");
        Led_Off();
    }

    void finish(state *context_)
    {
        if(context_->show_version)
        {
            Menu_Notice("PC10 v0.1");

            g_show_current = Menu_Question("Debug?");

            if(Menu_Question("Reset?"))
                Cfg_Reset();
        }

        Menu_Finish();
    }

    void update(void *v)
    {
        state *context_ = (state*)v;
        if(millis() - context_->start_time > START_SCREEN_MILLIS)
            finish(context_);
    }

    void button(void *v, const switch_type sw)
    {
        state *context_ = (state*)v;

        if(HERMES_SWITCH_NO(sw) == HERMES_RE_SEL)
            context_->show_version = true;
    }

    state context = {
        .show_version = false,
        .start_time = millis()
    };

    Menu_EventLoop(button, &draw, &update, &context);
}

void main_menu()
{
    static const char *items[] = {
        "Start",
        "Program Loco",
        "Settings",
        0
    };

    int option = Menu_GetOption("Main Menu", items, 0);

    switch(option)
    {
        case 0:
            manual_mode(g_show_current);
            break;
        case 1:
            program_loco();
            break;
        case 2:
            Settings_Menu();
            break;
    }
}

ISR(PCINT1_vect)
{
    Menu_Pcint();
}

ISR(TIMER2_COMPA_vect)
{
    DCC_SignalUpdateIsr();
}

int main()
{
    g_mcusr_cache = MCUSR;
    MCUSR = 0;

    // The watchdog timer can be used to reset the microntroller; disable it
    // immediately to prevent it being left on and firing on startup.
    Wdt_Off();

    sei();

    DCC_Setup();

    init_millis();

    // Set up the ADC to produce left adjusted results and use the internal 5V
    // reference.
    ADMUX = (unsigned char)(ADMUX | (1 << REFS0));

    // Read the current sensor.
    ADMUX = (unsigned char)(ADMUX | HERMES_MUX_CURRENT);

    // Set ADC prescaler to 128.
    // 16 MHz / 128 = 125 KHz, inside the desired 50-200 KHz range.
    ADCSRA = /*(1 << ADATE) |*/ (1 << ADPS0) | (1 << ADPS1) | (1 << ADPS2);

    // enable a2d conversions
    ADCSRA |= (1 << ADEN);

    // Read and discard.
    ADCSRA |= (1 << ADSC);

    // Enable pin change interrupts on port C.
    PCICR |= (1 << PCIE1);
    PCMSK1 |= (unsigned char)HERMES_PCMSK1;

    // Pull up resistors for the rotary encoder can be left on (these pins have
    // no other function).
    HERMES_RE_PORT |= (unsigned char)(
            (1 << HERMES_RE_A_PIN) |
            (1 << HERMES_RE_B_PIN) |
            (1 << HERMES_RE_C_PIN)
            );

    // Reset the configuration if there are no locomotives or accessories.
    {
        cfg_address_type addresses[CFG_LOCO_LIST_LENGTH + 1];
        Cfg_Addresses(addresses);

        cfg_address_type accessories[CFG_ACCESSORY_LIST_LENGTH + 1];
        Cfg_Accessories(accessories);

        if(
                Cfg_AddressCount(addresses) == 0 &&
                Cfg_AddressCount(accessories) == 0
                )
            Cfg_Reset();
    }

    // Switch down to a 8MHz clock using a /2 prescaler.  This makes the system
    // more stable than it would be with a 16MHz clock.
    cli();
    CLKPR = (1 << CLKPCE);
    CLKPR = (1 << CLKPS0);
    sei();

    // Wait for the LCD to power up.
    _delay_ms(100);

    LCD_Init();

    startup_screen();

    for(;;)
        main_menu();

    return 0;
}