/*
 * File:   Train_ControllerwSound.c
 * Author: JaysWoodshop
 * PIC 16F1829
 * Created on Jan. 20, 2015
 * Train controller with loop counter for new board.
 * I had to rebuild this file on 12/23/15
 * Added signal lights and sound 5/1/16
 * !!!!!!!!!!!!!NOT TESTED!!!!!!!!!!!!!!!!!!
 * 
 /******************************************************************************/
/* Files to Include                                                           */
/******************************************************************************/
#include  <xc.h>

#define _XTAL_FREQ 4000000  //4MHz

// CONFIG1
#pragma config FOSC = INTOSC    // Oscillator Selection (INTOSC oscillator: I/O function on CLKIN pin)
#pragma config WDTE = OFF       // Watchdog Timer Enable (WDT disabled)
#pragma config PWRTE = OFF      // Power-up Timer Enable (PWRT disabled)
#pragma config MCLRE = ON       // MCLR Pin Function Select (MCLR/VPP pin function is MCLR)
#pragma config CP = OFF         // Flash Program Memory Code Protection (Program memory code protection is disabled)
#pragma config CPD = OFF        // Data Memory Code Protection (Data memory code protection is disabled)
#pragma config BOREN = OFF      // Brown-out Reset Enable (Brown-out Reset disabled)
#pragma config CLKOUTEN = OFF   // Clock Out Enable (CLKOUT function is disabled. I/O or oscillator function on the CLKOUT pin)
#pragma config IESO = OFF       // Internal/External Switchover (Internal/External Switchover mode is disabled)
#pragma config FCMEN = OFF      // Fail-Safe Clock Monitor Enable (Fail-Safe Clock Monitor is disabled)
// CONFIG2
#pragma config WRT = OFF        // Flash Memory Self-Write Protection (Write protection off)
#pragma config PLLEN = OFF      // PLL Enable (4x PLL disabled)
#pragma config STVREN = OFF     // Stack Overflow/Underflow Reset Enable (Stack Overflow or Underflow will not cause a Reset)
#pragma config BORV = LO        // Brown-out Reset Voltage Selection (Brown-out Reset Voltage (Vbor), low trip point selected.)
#pragma config LVP = OFF        // Low-Voltage Programming Enable (High-voltage on MCLR/VPP must be used for programming)

//===============================
//Various defines
#define BYTE unsigned char
#define DEBOUNCE_TIME 10        // 10ms debounce time
//===============================
//Define hardware
#define SW_RS   PORTAbits.RA5   // Reed Switch
#define SW_PB   PORTAbits.RA4   // Push Button Start
#define RedSig  LATCbits.LATC4  // Signal lights Red
#define GrnSig  LATCbits.LATC5  // Signal lights Green
#define Sound   LATAbits.LATA2  // Start sound added
#define START   LATBbits.LATB7  // Start relay was RB5
// Future use
#define Relay1  LATBbits.LATB5  // Relay 1
#define Relay2  LATBbits.LATB6  // Relay 2
#define Motor1A LATCbits.LATC6  // Pin 1A H-bridge
#define Motor2A LATCbits.LATC7  // Pin 2A H-bridge
#define Motor3A LATCbits.LATC3  // Pin 3A H-bridge
#define Motor4A LATCbits.LATC2  // Pin 4A H-bridge


//===============================
//Global variables
volatile bit new_start; // set by ISR when the START/STOP button has changed from "not-pressed" to "pressed"
bit prev_start; // state of the START/STOP button on previous pass
BYTE debounce_start; // debounce counter. Decremented once per ms.
volatile bit debounced_start; // state of START/STOP button when last debounced

volatile bit new_reed; // set by ISR when the reed button has changed
bit prev_reed; // state of the START/STOP button on previous pass
BYTE debounce_reed; // debounce counter. Decremented once per ms.
volatile bit debounced_reed; // state of reed switch when last debounced

volatile BYTE delay_count; // used for delays up to 255 milliseconds
BYTE TrackCircuitsCounter;
//===============================
//constants
const BYTE lookup[] = {4, 1, 2, 3}; //convert switch settings to loop count
//===============================
//Initialization

void init(void) {
    //initial state of all output pins is low
    LATA = 0b00000000;
    LATB = 0b00000000;
    LATC = 0b00000000;

    //enable all digital input buffers
    ANSELA = 0b00000000;
    ANSELB = 0b00000000;
    ANSELC = 0b00000000;

    //weak pull ups (all off as external pull-ups are used)
    WPUA = 0b00000000;  //pull-ups on RA5, RA4, RA3
    WPUB = 0; //0b00000000;  //none
    WPUC = 0b00000000;  //pull-ups on RC1, RC0

    //input thresholds
    INLVLA = 0b00111111; //all inputs use CMOS levels
    INLVLB = 0b11110000; //all inputs use CMOS levels
    INLVLC = 0b11111111; //all inputs use CMOS levels

    //set pin in/out directions. All out except where we need an input
    TRISA = 0b00110000; //RA5, RA4 are inputs
    TRISB = 0b00010000; //RB4 input for IOC sound
    TRISC = 0b00000011; //RC1,0 are inputs

    OPTION_REG = 0b00000000; //WP enabled, timer0 from instruction clock

    //TIMER2 - Set to roll over every 1ms @ 4MHz
    T2CON = 0b00000101; //Post-scaler=1:1, On, Pre-scaler=1:4
    PR2 = 250; //Pre-scaler =4 and PR2=250 => rollover every 1000 counts
    PIE1bits.TMR2IE = 1; //allow TMR2 rollover to generate interrupts

    INTCON = 0b11001000; //set GIE and PEIE so TMR2 can interrupt IOCIE set
    IOCBP = 0b00010000;  //Positive going edge on RB4
    RedSig = 1; //On at startup
    Sound = 1;
}

//===============================
//Interrupt service routine

static void interrupt isr(void) // Here be interrupt function - the name is unimportant.
{
    if (TMR2IF) // Was this a timer overflow? (don't check TMR2IE, we never clear it)
    { //here once per millisecond
        static bit curr_button; //state of the START/STOP button on this pass. Used so we only read it once

        TMR2IF = 0; // Clear interrupt flag, ready for next
        //------------------------------
        //Debounce the Start/Stop button
        curr_button = SW_PB;
        if (curr_button == prev_start) //has the state changed?
        { //no change
            if (debounce_start) //are we debouncing a change?
            { //yes
                if (--debounce_start == 0) //has the elapsed time passed?
                { //yes. Button has been in this state for 10ms
                    debounced_start = curr_button;
                    if (curr_button == 0) //is the button DOWN?
                        new_start = 1; //is down, so say a new press has been detected
                }
            }
        } else { //state of button has changed
            debounce_start = DEBOUNCE_TIME; //start a new debounce delay
        }
        prev_start = curr_button; //remember the button state for next time
        //------------------------------
        //Debounce the Reed switch
        curr_button = SW_RS; //read the reed switch state
        if (curr_button == prev_reed) //has the state changed?
        { //no change
            if (debounce_reed) //are we debouncing a change?
            { //yes
                if (--debounce_reed == 0) //has the elapsed time passed?
                { //yes. Button has been in this state for 10ms
                    debounced_reed = curr_button;
                    new_reed = 1; //is down, so say a new press has been detected
                }
            }
        } else { //state of button has changed
            debounce_reed = DEBOUNCE_TIME; //start a new debounce delay
        }
        prev_reed = curr_button; //remember the button state for next time

        //------------------------------
        //Implement ms delays
        if (delay_count)
            --delay_count; //dec the delay counter if non-zero

    } //end of TMR2 service   	
} //end if ISR

//===============================
//Start train
void start_train(void) {
    RedSig = 0; // Red off                                                          
    GrnSig = 1; // Set Green
    __delay_ms(2000); // for about 2 sec.
    Sound = 0; // Turn sound on
    __delay_ms(100); // for about 0.1 sec. short pulse required
    Sound = 1; // Sound off
    INTCONbits.GIE = 0; // prevent branches to the interrupt address (0004h).
    SLEEP();       // Put it to sleep wakeup on busy pin going high RB4
    INTCONbits.GIE = 1;
    START = 1;
}
//SLEEP();                  XC8 accepts this 
//IOCBP4 = 1                Positive going edge on RB4
//INTCONbits.IOCIE = 1      IOC enabled if set
//TRISB4 = 1                RB4 set as input

//===============================
//Stop train
void stop_train(void) {
    GrnSig = 0;
    RedSig = 1; // Set Red
    START = 0; // Stop the train
}
//===============================
//Signal change to Red
void signal_change(void) {
    __delay_ms(2000); // Allow time to get out of station
    GrnSig = 0;
    RedSig = 1; // Set signal back to Red
}
//===============================
//Flag up
void flag_up(void) 
{
    Relay1 = 1;
    __delay_ms(500);
}
//================================
//Flag down
void flag_down(void) 
{
    Relay1 = 0;
    __delay_ms(500);
}
//===============================
//Main loop    
void main(void) {
    OSCCON = 0b01101000; //Set internal oscillator to 4MHz
    init();
    while (1) {
        while (debounced_start = 1);
        //wait until the START/STOP button is pressed            
        new_start = 0; //will get set if button is pressed again
        new_reed = 0; //will get set if reed switch changes

        //start the train, and do the selected number of loops
        flag_up ();       
        start_train();
        TrackCircuitsCounter = lookup[PORTC & 0b0011]; //save number of loops (1/2/3/4)
        if (TrackCircuitsCounter == 1) // test if one loop
            signal_change();
        while (1) //start of "train_running" loop
        { //main loop while train is running  
            //if (new_start == 1)
            //{
            //    new_start = 0; //prepare for next change
            //    break;  //exit immediately if START/STOP pressed
            //}    

            if ((delay_count == 0) && (new_reed == 1)) { //reed switch has changed
                new_reed = 0; //prepare for next change
                if (debounced_reed == 0) //is the switch activated?
                {
                    if (--TrackCircuitsCounter == 0) //dec loop counter, and exit if zero
                        break; //was 0, exit loop
                    if (TrackCircuitsCounter == 1) // test if one loop
                        signal_change();
                    delay_count = 20; //ignore changes for the next 2 seconds  
                }
            }
        } //end of "train running" loop 
        flag_down();
        stop_train();
        delay_count = 200;
        while (delay_count); //wait for 200ms after stopping train before doing anything else

    } //end of main while() loop
}

