/*
 * File:   main.c
 * Author: boos
 *
 * Created on February 26, 2022, 3:42 PM
 */

// CONFIG1
#pragma config FOSC = INTOSC    // Oscillator Selection Bits (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 = OFF      // MCLR Pin Function Select (MCLR/VPP pin function is digital input)
#pragma config CP = OFF         // Flash Program Memory Code Protection (Program 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 Mode (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 CPUDIV = CLKDIV6 // CPU System Clock Selection Bit (CPU system clock divided by 6)
#pragma config USBLSCLK = 48MHz // USB Low SPeed Clock Selection bit (System clock expects 48 MHz, FS/LS USB CLKENs divide-by is set to 8.)
#pragma config PLLMULT = 3x     // PLL Multipler Selection Bit (3x Output Frequency Selected)
#pragma config PLLEN = DISABLED // PLL Enable Bit (3x or 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 LPBOR = OFF      // Low-Power Brown Out Reset (Low-Power BOR is disabled)
#pragma config LVP = OFF        // Low-Voltage Programming Enable (High-voltage on MCLR/VPP must be used for programming)

#include <xc.h>

// define useful abbreviations
#define LED1 RC4
#define LED2 RC3
#define LED3 RC2
#define LED4 RC0
#define SW_ON RC1
#define SW_UP RA1
#define SW_DN RA0

// time variables
int t = 0;
int seconds = 0;
int minutes = 0;

// debouncing variables
int SW_ON_buffer = 0;
int SW_UP_buffer = 0;
int SW_DN_buffer = 0;

// status variable
int STATUS_ON = 1;

// main function
void main (void) {
        
    // configure IO pins
    TRISC0 = 0; ANSC0 = 0;
    TRISC2 = 0; ANSC2 = 0;
    TRISC3 = 0; ANSC3 = 0;
    TRISC4 = 0;
    TRISC1 = 1; ANSC1 = 0;
    TRISC5 = 0;
    	
    // set the internal oscillator frequency to 4MHz
    IRCF3 = 1; IRCF2 = 1; IRCF1 = 0; IRCF0 = 1;
    
	// enable the external interrupt
    INTE = 1;
    	
    // configure TIMER1	
    TMR1CS1 = 1; TMR1CS0 = 0; T1OSCEN = 1;	// use the external clock
    TMR1GE = 0;								// disable the gating
	nT1SYNC = 1;							// allow TIMER1 to run in SLEEP mode
	TMR1IE = 1;								// enable interrupt on overflow
	TMR1ON = 1;								// turn TIMER1 on
    
	// turn on peripheral and global interrupts
    PEIE = 1; GIE = 1;
    
	// configure TIMER2 to ~4kHz PWM mode 
    T2CKPS0 = 0; T2CKPS1 = 0;	// set TIMER2 prescaler to 1:1
	PR2 = 255; 					// set period register (to which TIMER2 value is compared)
	TMR2ON = 1; 				// turn TIMER2 on
    
    // configure PWM1 module
    PWM1DCH = 0;				// most significant 8 bits of the duty/cycle value, 0..255
    PWM1DCL = 0;				// least significant 2 bits of the duty/cycle value, 0 or 1
    PWM1EN = 1; PWM1OE = 1;		// turn PWM module on
    
    // main loop
    while (1) {
    
        // is the UP button pressed?
        // (to debounce the signal, react only if it hasn't been pressed in a while)
        if (SW_UP && (SW_UP_buffer == 0)) {
            
            // increase the minutes all the way up to 12pm
            if (minutes < 1440) {
                minutes++;
            }
            
            // refill the debounce buffer variable
            SW_UP_buffer = 10;
            
        }
        
        // is the DOWN button pressed?
        // (to debounce the signal, react only if it hasn't been pressed in a while)
        if (SW_DN && (SW_DN_buffer == 0)) {
            
            // increase the minutes all the way down to 12pm
            if (minutes > 0) {
                minutes--;
            }
            
            // refill the debounce buffer variable
            SW_DN_buffer = 10;
            
        }
        
        // debounce buttons
        if (SW_UP_buffer > 0) { SW_UP_buffer--; }
        if (SW_DN_buffer > 0) { SW_DN_buffer--; }
        if (SW_ON_buffer > 0) { SW_ON_buffer--; }
        
        // go to sleep?
        if (STATUS_ON == 0) {
            
            // turn all LEDs off
            LED1 = 0; LED2 = 0; LED3 = 0; LED4 = 0;
            
			// disable PWM module
            PWM1DCH = 0;
            PWM1DCL = 0;
            PWM1EN = 0;
			
			// reset the pushbutton debounce variable
            SW_ON_buffer = 0;
			
			// and finally, go to sleep
            SLEEP ();

		// waking up?
		} else {
			
			// turn PWM module back on
			PWM1EN = 1;
            
            // adjust panel voltage
            if (minutes <= 720) {
                PWM1DCH = (unsigned char) (minutes / 3);
            } else {
                PWM1DCH = (unsigned char) ((minutes - 720) / 3);
            }
        
            // LED animation
    		t = (TMR1H - 128) / 42;
            if (t == 0) {
                LED1 = 1; LED2 = 0; LED3 = 0;
            } else if (t == 1) {
                LED1 = 0; LED2 = 1; LED3 = 0;
            } else {
                LED1 = 0; LED2 = 0; LED3 = 1;
            }
            
            // is it AM?
    		if (minutes <= 720) {
    			LED4 = 0;
    		} else {
                LED4 = 1;
            }            
	
		}
        
    }
    
    return;
    
}

// interrupt service routine
void __interrupt () isr (void) {  
 
    // ON/OFF button has been pressed
    if (INTF) {
        
        // only react if it has just been pressed
        if (SW_ON_buffer == 0) {
            
            // toggle the clock's status from ON to OFF, or vice versa
            STATUS_ON = 1 - STATUS_ON;
            
            // debounce the pushbutton
            SW_ON_buffer = 500;
            
        }
        
        // clear external interrupt flag
        INTF = 0;
        
    }
    
    // TIMER1 overflow (occurs once per second)
    if (TMR1IF) {
     
        // update the time
		seconds++;
		if (seconds >= 60) {
			seconds = 0;
			minutes++;
			if (minutes >= 1440) {
				minutes = 0;
			}
		}
        
        // reset timer back to 127, so that an overflow occurs in another second
        TMR1H = 127; TMR1L = 0;
        
        // clear TIMER1 interrupt flag
        TMR1IF = 0;
        
    }
    
}