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;
}