/* --COPYRIGHT--,BSD
 * Copyright (c) 2015, Texas Instruments Incorporated
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 *
 * *  Redistributions of source code must retain the above copyright
 *    notice, this list of conditions and the following disclaimer.
 *
 * *  Redistributions in binary form must reproduce the above copyright
 *    notice, this list of conditions and the following disclaimer in the
 *    documentation and/or other materials provided with the distribution.
 *
 * *  Neither the name of Texas Instruments Incorporated nor the names of
 *    its contributors may be used to endorse or promote products derived
 *    from this software without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
 * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
 * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
 * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
 * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
 * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
 * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
 * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 * --/COPYRIGHT--*/
/**********************************************************************
 *
 * RTC_B_with_Backup.c
 *
 * This program demonstrates proper usage of RTC_B with Battery Backup on
 * F66xx family devices. The code distinguishes between a BOR event, and
 * a VBAT switching event, to determine if the RTC calendar registers
 * should contain accurate time information and initialize the RTC
 * accordingly.
 *
 * Time information is relayed to the Demo_Host F5529 Launchpad over UART
 * to be passed back to the PC and viewed by the user. This shows that
 * time is retained if VBAT is present, and is lost if VBAT becomes
 * drained.
 *
 **********************************************************************/

#include "driverlib.h"
#include "UART.h"

volatile Calendar newTime;
Calendar currentTime;

//P2.0 = TXD, P2.1 = RXD
const uint8_t port_mapping[] = {
    //Port P2:
    PM_UCA0TXD,
    PM_UCA0RXD,
    PM_NONE,
    PM_NONE,
    PM_NONE,
    PM_NONE,
    PM_NONE,
    PM_NONE
};

//strings to send to host to display switching/reset detection
#define TIME_LOST_STRING_LENGTH 30
#define TIME_RETAINED_STRING_LENGTH 37
#define OSC_FAULT_STRING_LENGTH 35

const unsigned char timeLostString[TIME_LOST_STRING_LENGTH] = 			"Reset event. Time was reset.\r\n";
const unsigned char timeRetainedString[TIME_RETAINED_STRING_LENGTH] = 	"Switching event. Time was retained.\r\n";
const unsigned char oscFaultString[OSC_FAULT_STRING_LENGTH] = 			"Oscillator Fault. Time was reset.\r\n";

unsigned char oscFault = 0;

void resetTime(void);
void clearOscFault(void);

void main(void) {
	unsigned char timeLost;

    WDT_A_hold(WDT_A_BASE);	//stop the watchdog

    PMM_setVCore( PMM_CORE_LEVEL_3); //change this VCORE to test with different levels/settings

    //We have to re-init some crystal and RTC settings before clearing LOCKBAK so that crystal/RTC won't stop if we're coming out of VBat
    //init crystal
	UCSCTL6 = XT1DRIVE_3 | XCAP_3;	//set up the crystal load capacitors for 12pF
	//init RTC
	RTCCTL01 |= RTCBCD;		//use RTC in BCD mode
	RTCCTL01 &= ~RTCHOLD; //keep RTC running when we clear LOCKBAK

    //erase LOCKBAK bit for crystal + RTC settings to take effect
	//you must erase LOCKBACK or you won't be able to check RTC flag etc
    while(BAKCTL & LOCKBAK)
    {
    	BAKCTL &= ~(LOCKBAK);
    }

    //check if backup supply was drained or not by checking BAKMEM0, RTCOFIFG, and RTCCALS
    //BAKMEM0 we are checking for backup memory retention
    //RTCOFIFG we are checking if an oscillator fault occurred while we were on the backup supply
    //RTCCALS will be retained through switching event but cleared on brownout, so also indicates if RTC calendar registers retained
    //NOTE: RTCCTL2 setting and checking should use whatever calibration values are required for the specific device/application. RTCCALS is used as an example in this case.
    if((BAKMEM0 == 0xDEAD) && !(RTCCTL01 & RTCOFIFG) && (RTCCTL2 & RTCCALS)) //backup not lost
    {
    	timeLost = 0; 	//we retained the RTC data

        P1DIR |= BIT0;	//RTC LED re-initialize to be output (GPIO are not retained)
    }
    else //either first startup or we lost backup RAM
    {
    	timeLost = 1;

        P1OUT &= ~BIT0;
        P1DIR |= BIT0;	//RTC LED

        //Clear faults
        clearOscFault();

		//reinitialize the RTC time
        resetTime();

        RTCCTL2 |= RTCCALS; //this bit is retained so can be used as a check for VBAT switching
        BAKMEM0 = 0xDEAD; 	//write in the signature
    }

    //Enable interrupt for RTC Ready Status, which asserts when the RTC
    //Calendar registers are ready to read.
    RTC_B_clearInterrupt(RTC_B_BASE, RTC_B_CLOCK_READ_READY_INTERRUPT | RTC_B_OSCILLATOR_FAULT_INTERRUPT); //clear flag before enabling interrupt
    RTC_B_enableInterrupt(RTC_B_BASE, RTC_B_CLOCK_READ_READY_INTERRUPT | RTC_B_OSCILLATOR_FAULT_INTERRUPT);	//enable ready interrupt and fault interrupt
    RTC_B_startClock(RTC_B_BASE);	//start clock

    //CONFIGURE PORTS- pass the port_mapping array, start @ P2MAP01, initialize
    //a single port, do not allow run-time reconfiguration of port mapping
    PMAP_initPortsParam initPortsParam = {0};
    initPortsParam.portMapping = port_mapping;
    initPortsParam.PxMAPy = (uint8_t  *)&P2MAP01;
    initPortsParam.numberOfPorts = 1;
    initPortsParam.portMapReconfigure = PMAP_DISABLE_RECONFIGURATION;
    PMAP_initPorts(PMAP_CTRL_BASE, &initPortsParam);

    P2SEL = BIT0 | BIT1;	//P2.0 = TXD, P2.1 = RXD

    //init UART for sending time data
    initUART();

    //Enable UART module for operation
    USCI_A_UART_enable(USCI_A0_BASE);

    //output whether a reset or a switching event occurred, time lost/retained
    if(timeLost)
    {
    	transmitString((unsigned char*)timeLostString, TIME_LOST_STRING_LENGTH);
    }
    else
    {
    	transmitString((unsigned char*)timeRetainedString, TIME_RETAINED_STRING_LENGTH);
    }

    while(1)
    {
    	__bis_SR_register(LPM3_bits + GIE);
    	__no_operation();

    	if(oscFault)
    	{
    		//check until fault clears
    		clearOscFault();
    		oscFault = 0;

    		//reset time
    		resetTime();

    		//print message about osc fault and time resetting
    		transmitString((unsigned char*)oscFaultString, OSC_FAULT_STRING_LENGTH);

    		//restart RTC
    	    //Enable interrupt for RTC Ready Status, which asserts when the RTC
    	    //Calendar registers are ready to read.
    	    RTC_B_clearInterrupt(RTC_B_BASE, RTC_B_CLOCK_READ_READY_INTERRUPT | RTC_B_OSCILLATOR_FAULT_INTERRUPT); //clear flag before enabling interrupt
    	    RTC_B_enableInterrupt(RTC_B_BASE, RTC_B_CLOCK_READ_READY_INTERRUPT | RTC_B_OSCILLATOR_FAULT_INTERRUPT);	//enable ready interrupt and fault interrupt
    	    RTC_B_startClock(RTC_B_BASE);	//start clock
    	}
    	else
    	{
			//when we wake, output the new time on the UART
			currentTime = newTime;	//we're buffering the data in case RTC interrupts again while still sending over UART
			transmitString(calendarToString(currentTime), CALENDAR_STRING_LENGTH);
    	}
    }
}

#if defined(__TI_COMPILER_VERSION__) || defined(__IAR_SYSTEMS_ICC__)
#pragma vector=RTC_VECTOR
__interrupt
#elif defined(__GNUC__)
__attribute__((interrupt(RTC_VECTOR)))
#endif
void RTC_B_ISR(void)
{
	 while(BAKCTL & LOCKBAK)                    // Unlock backup system
	        BAKCTL &= ~(LOCKBAK);

	switch (__even_in_range(RTCIV, 16)) {
	case 0: break;  //No interrupts
	case 2: //RTCRDYIFG
			P1OUT ^= BIT0;	//Toggle P1.0 every second
			newTime = RTC_B_getCalendarTime(RTC_B_BASE);
			__bic_SR_register_on_exit(LPM3_bits);
			__no_operation();	//put breakpoint here to check on time if desired
			break;
	case 4: break;	//RTCEVIFG
	case 6: break;	//RTCAIFG
	case 8: break;  //RT0PSIFG
	case 10: break; //RT1PSIFG
	case 12: //RTCOFIFG
		oscFault = 1;
		RTC_B_disableInterrupt(RTC_B_BASE, RTC_B_OSCILLATOR_FAULT_INTERRUPT); //disable further fault interrupts
		__bic_SR_register_on_exit(LPM3_bits); //wake to handle fault
		__no_operation();
		break;
	case 14: break; //Reserved
	case 16: break; //Reserved
	default: break;
	}
}

//reset the time to our starting time
void resetTime(void)
{
    //Setup Current Time for Calendar
    currentTime.Seconds = 0x00;
    currentTime.Minutes = 0x30;
    currentTime.Hours = 0x04;
    currentTime.DayOfWeek = 0x01;
    currentTime.DayOfMonth = 0x04;
    currentTime.Month = 0x05;
    currentTime.Year = 0x2015;

    RTC_B_initCalendar(RTC_B_BASE,
		&currentTime,
		RTC_B_FORMAT_BCD);
}

//clear oscillator faults
void clearOscFault(void)
{
    //Clear OFIFG fault flag
    SFRIFG1 &= ~OFIFG;
    //wait for crystal to stabilize
    while (UCSCTL7 & XT1LFOFFG)
    {
        //Clear OSC flaut Flags fault flags
        UCSCTL7 &= ~(XT1LFOFFG);

        //Clear OFIFG fault flag
        SFRIFG1 &= ~OFIFG;
    }
    UCSCTL6 &= ~XT1OFF;

    RTC_B_clearInterrupt(RTC_B_BASE, RTC_B_OSCILLATOR_FAULT_INTERRUPT);
}
