/*********************************************************************
 *
 *                Thermo Sensor
 *
 *********************************************************************
 * FileName:        temperature.c
 * Dependencies:    See INCLUDES section below
 * Processor:       PIC18
 * Compiler:        C18 2.30.01+
 * Company:         Microchip Technology, Inc.
 *
 * Software License Agreement
 *
 * The software supplied herewith by Microchip Technology Incorporated
 * (the “Company”) for its PICmicro® Microcontroller is intended and
 * supplied to you, the Company’s customer, for use solely and
 * exclusively on Microchip PICmicro Microcontroller products. The
 * software is owned by the Company and/or its supplier, and is
 * protected under applicable copyright laws. All rights are reserved.
 * Any use in violation of the foregoing restrictions may subject the
 * user to criminal sanctions under applicable laws, as well as to
 * civil liability for the breach of the terms and conditions of this
 * license.
 *
 * THIS SOFTWARE IS PROVIDED IN AN “AS IS” CONDITION. NO WARRANTIES,
 * WHETHER EXPRESS, IMPLIED OR STATUTORY, INCLUDING, BUT NOT LIMITED
 * TO, IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
 * PARTICULAR PURPOSE APPLY TO THIS SOFTWARE. THE COMPANY SHALL NOT,
 * IN ANY CIRCUMSTANCES, BE LIABLE FOR SPECIAL, INCIDENTAL OR
 * CONSEQUENTIAL DAMAGES, FOR ANY REASON WHATSOEVER.
 *
 ********************************************************************/

/** I N C L U D E S **********************************************************/
#include "Compiler.h"
#include "GenericTypeDefs.h"
#include "HardwareProfile.h"
#include "temperature.h"

#if defined(__18CXX)
    #include <spi.h>
#endif

/** V A R I A B L E S ********************************************************/
#pragma udata
unsigned int I2CStateVariable;  // Used for keeping track of the I2C state
                                                                // machine when using the HPC Explorer board's
                                                                // I2C based TC74 temperature sensor.
signed char TempAccumulator;    // Used for averaging temperature samples
signed char TempSave;
WORD_VAL temperature;           // Raw data format
char tempString[10];        // Buffer for storing data in ASCII format

/** P R I V A T E  P R O T O T Y P E S ***************************************/

/** D E C L A R A T I O N S **************************************************/
#pragma code
/******************************************************************************
 * Function:        void InitTempSensor(void)
 *
 * PreCondition:    None
 *
 * Input:           None
 *
 * Output:          None
 *
 * Side Effects:    None
 *
 * Overview:        Initializes SPI interface & chip select line
 *
 * Note:            None
 *****************************************************************************/
void InitTempSensor(void)
{
    #if defined(PICDEM_FS_USB)
    cs_temp_sensor = 1;
    tris_cs_temp_sensor = OUTPUT_PIN;
    OpenSPI(SPI_FOSC_64, MODE_11, SMPMID);
    
    // Initialize readable values - default to room temperature
    temperature.Val = 0x0C87;       // 25 degree celsius
    UpdateCelsiusASCII();

        #elif defined(PIC18F87J50_PIM)
    temperature.Val = 0x0C87;       // 25 degree celsius
    UpdateCelsiusASCII();

        //Need to initialize I2C Module to prepare for communication with
        //TC74 temperature sensor on the HPC Explorer board.
        mInitI2CPins();                         // See io_cfg.h
        SSP1STAT = 0xC0;                        // Slew rate control disabled, SMBus
        SSP1CON1 = 0x08;                        // I2C Master mode
        SSP1CON2 = 0x00;
        SSP1ADD = 0x7D;                         // Just under 100kHz at 48MHz core frequency
        SSP1CON1bits.SSPEN = 1;         // Enable MSSP module   

        I2CStateVariable = 0;           // Initial state for I2C state machine

    #endif    
}//end InitTempSensor

/******************************************************************************
 * Function:        void AcquireTemperature(void)
 *
 * PreCondition:    None
 *
 * Input:           None
 *
 * Output:          None
 *
 * Side Effects:    None
 *
 * Overview:        None
 *
 * Note:            None
 *****************************************************************************/
BOOL AcquireTemperature(void)
{
    #if defined(PICDEM_FS_USB)
                //The PICDEM FS USB Demo Board uses a TC77 (13 bit) temperature sensor and
                //communicates with it through the SPI interface.
                cs_temp_sensor = 0;
                temperature.v[1] = ReadSPI();
                temperature.v[0] = ReadSPI();
                cs_temp_sensor = 1;
            
                if(temperature.bits.b2 == 0)
                        return FALSE;

        #elif defined(__C30__) || defined(__C32__)

                //Create temp variables to store the conversion data
                float temp;

                //get ready to sample the A/D

        #if defined(__C30__)
                AD1CHS = 0x4;           //MUXA uses AN4
                AD1PCFGLbits.PCFG4 = 0;
                for(temp=0;temp<1000;temp++); //Sample delay
                // Get an ADC sample
                AD1CON1bits.SAMP = 1;           //Start sampling
                for(temp=0;temp<1000;temp++); //Sample delay, conversion start automatically
                AD1CON1bits.SAMP = 0;           //Start sampling
                for(temp=0;temp<1000;temp++); //Sample delay, conversion start automatically
                while(!AD1CON1bits.DONE);       //Wait for conversion to complete

        #else
            AD1PCFGbits.PCFG4 = 0;

            AD1CON1 = 0x0000; // SAMP bit = 0 ends sampling ...
            // and starts converting
            AD1CHS = 0x00040000; // Connect RB4/AN4 as CH0 input ..
            // in this example RB2/AN2 is the input
            AD1CSSL = 0;
            AD1CON3 = 0x0002; // Manual Sample, Tad = internal 6 TPB
            AD1CON2 = 0;
            AD1CON1SET = 0x8000; // turn ADC ON

            AD1CON1SET = 0x0002; // start sampling ...
            for(temp=0;temp<1000;temp++); //Sample delay, conversion start automatically
            AD1CON1CLR = 0x0002; // start Converting
            while (!(AD1CON1 & 0x0001));// conversion done?
        #endif
   
                //convert the results to a float
                temp = (float)ADC1BUF0;

                // voltage = A2D_reading * 3.3v / 1024
                temp *= 3.3;
                temp /= 1024;

                // align to 0C (subtracting -.65v)
                temp -= .55;

                //convert to TC77 style output and store to temperature
                temp *= 12800;
                temperature.Val = (WORD)temp;
                temperature.Val |= 0x7;

    //#elif defined(__C32__)
        //#warning "TODO"
        #elif defined(PIC18F87J50_PIM)  // Uses TC74 (8 bit) 
                //The PIC18F87J50 FS USB Plug-In Module (PIM) does not have a temperature 
                //sensor, but there is a TC74 (8 bit) I2C based temperature sensor on the
                //HPC Explorer demo board.  In order for this temperature demo code to do 
                //anything useful, the PIM should be used in conjunction with the HPC Explorer.
                //The TC74 comes in 5V and 3.3V optimized versions.  If a 5V part is run at
                //3.3V (as with the PIM installed), it may have relatively large offsets.
                return TRUE;    // Don't need to do anything in this function, temperature
                                                // polling with the PIC18F87J50 FS USB Plug-In Module is
                                                // done with the PollTempOnHPCExplorer() function.
                                                // This is done so the I2C communication can be done with
                                                // a non-blocking approach.
    #elif defined(LOW_PIN_COUNT_USB_DEVELOPMENT_KIT)
        temperature.Val = 0x0000;
        return TRUE;
    #elif defined(PIC18F46J50_PIM)
                //Create temp variables to store the conversion data
                float temp;

                //get ready to sample the A/D
        ADCON0bits.CHS = 0x07;
        for(temp=0;temp<1000;temp++){}
        ADCON0bits.GO = 1;              // Start AD conversion
        while(ADCON0bits.NOT_DONE);     // Wait for conversion

                //convert the results to a float
                temp = (float)ADRES;

                // voltage = A2D_reading * 3.3v / 1024
                temp *= 3.3;
                temp /= 1024;

                // align to 0C (subtracting -.65v)
                temp -= .55;

                //convert to TC77 style output and store to temperature
                temp *= 12800;
                temperature.Val = (WORD)temp;
                temperature.Val |= 0x7;

        return TRUE;
        #else
                #error "Unknown temperature acquire configuration.  See AcquireTemperature function in __FILE__"
    #endif
    return TRUE;
}//end AcquireTemperature

/******************************************************************************
 * Function:        void PollTempOnHPCExplorer(void)
 *
 * PreCondition:    None
 *
 * Input:           None
 *
 * Output:          Temperature data from TC74 on HPC Explorer, but formatted 
 *                                      like the TC77, stored in the "temperature" variable.
 * Side Effects:    None
 *
 * Overview:        None
 *
 * Note:            None
 *****************************************************************************/
#if defined(PIC18F87J50_PIM)
void PollTempOnHPCExplorer(void)
{
        //The PIC18F87J50 FS USB Plug-In Module (PIM) does not have a temperature 
        //sensor, but there is a TC74 (8 bit) I2C based temperature sensor on the
        //HPC Explorer demo board.  In order for this temperature demo code to do 
        //anything useful, the PIM must be used in conjunction with the HPC Explorer.
        //The TC74 comes in 5V and 3.3V optimized versions.  If a 5V part is run at
        //3.3V (as with the PIM installed), it may have relatively large offsets.                
        #define TC74AddressWrite        0b10011010      // This is the default address for the TC74, use this for writes
        #define TC74AddressRead         0b10011011      // This is the default address for the TC74, use this for reads
        #define RTR                             0x00            // This is the read temp command for TC74

        // Should not use blocking functions in USB code.  Therefore, these I2C
        // communications are done with a state machine as shown below.
        switch(I2CStateVariable)
        {
                case 0x00:
                        PIR1bits.SSP1IF = 0;
                        SSP1CON2bits.SEN = 1;                           // Send Start Bit
                        I2CStateVariable = 0x01;
                        break;
                case 0x01:
                        if(PIR1bits.SSP1IF == 0) {break;}
                        PIR1bits.SSP1IF = 0;
                        SSP1BUF = TC74AddressWrite;                     // Begin sending the actual address
                        I2CStateVariable = 0x02;
                        break;
                case 0x02:
                        if(PIR1bits.SSP1IF == 0) {break;}
                        PIR1bits.SSP1IF = 0;
                        SSP1BUF = RTR;                                          // Send command to select the TEMP register
                        I2CStateVariable = 0x03;
                        break;
                case 0x03:
                        if(PIR1bits.SSP1IF == 0) {break;}
                        PIR1bits.SSP1IF = 0;
                        SSP1CON2bits.SEN = 1;                           // Send another start bit
                        I2CStateVariable = 0x04;
                        break;
                case 0x04:
                        if(PIR1bits.SSP1IF == 0) {break;}
                        PIR1bits.SSP1IF = 0;
                        SSP1BUF = TC74AddressRead;                      // Send the address again, but this time "read"
                        I2CStateVariable = 0x05;
                        break;
                case 0x05:
                        if(PIR1bits.SSP1IF == 0) {break;}
                        PIR1bits.SSP1IF = 0;
                        SSP1CON2bits.RCEN = 1;                          // Initiate read from device
                        I2CStateVariable = 0x06;
                        break;
                case 0x06:
                        if(PIR1bits.SSP1IF == 0) {break;}
                        PIR1bits.SSP1IF = 0;
                        TempSave = SSP1BUF;                                     // Finally got the result, need to save it
                        SSP1CON2bits.ACKDT = 1;                         // Prepare I2C NACK handshake
                        SSP1CON2bits.ACKEN = 1;                         // Send the acknowledge bit
                        I2CStateVariable = 0x07;
                        break;
                case 0x07:
                        if(PIR1bits.SSP1IF == 0) {break;}
                        PIR1bits.SSP1IF = 0;
                        SSP1CON2bits.PEN = 1;                           // Now send a stop bit
                        I2CStateVariable = 0x08;
                        break;
                case 0x08:
                        if(PIR1bits.SSP1IF == 0) {break;}
                        PIR1bits.SSP1IF = 0;
                        I2CStateVariable = 0x09;
                        break;
                case 0x09:
                        TempAccumulator = ((TempAccumulator + TempSave) / 2);   // Get an average
                        I2CStateVariable = 0x10;
                        break;
                case 0x10:
                        //Now format data like that of the TC77, which is what the PC side code expects,
                        //since it was originally intended to be used with the TC77.
                        temperature.v[1] = TempAccumulator;
                        if(temperature.bits.b15 == 0)           // == 0 when positive temperature
                        {
                            temperature.Val = temperature.Val >> 1;
                            temperature.bits.b15 = 0;                   // Positive temperature, upper MSb clear
                        }
                        else
                        {
                            temperature.Val = temperature.Val >> 1;
                            temperature.bits.b15 = 1;                   // Negative temperature, upper MSb set
                        }
                        temperature.bits.b0 = 1;                        // Lower three LSbs = 1 on TC74 format
                        temperature.bits.b1 = 1;                        // Lower three LSbs = 1 on TC74 format
                        temperature.bits.b2 = 1;                        // Lower three LSbs = 1 on TC74 format

                        I2CStateVariable = 0x11;                        // Could go back to 0, but don't want to poll that fast
                        break;
                case 0x5000:                                                    // This slows down the sample rate of the device.
                        I2CStateVariable = 0x00;                        // The temp sensor itself only updates around 8 times/sec.
                        break;
        default:
                        I2CStateVariable++;
            break;      
    }//end switch
}//end PollTempOnHPCExplorer
#endif

/******************************************************************************
 * Function:        void UpdateCelsiusASCII(void)
 *
 * PreCondition:    None
 *
 * Input:           None
 *
 * Output:          None
 *
 * Side Effects:    None
 *
 * Overview:        This routine converts data output to ASCII string
 *
 * Note:            None
 *****************************************************************************/
void UpdateCelsiusASCII(void)
{
    WORD_VAL temp;
    BYTE i;
        
    temp.Val = temperature.Val >> 3;
    
    if(temp.bits.b12 == 0)
    {
        temp.byte.HB &= 0x1F;
        tempString[0] = '+';
    }
    else
    {
        temp.byte.HB |= 0xE0;
        tempString[0] = '-';
        temp.Val = temp.Val ^ 0xFFFF;   // Negate
        temp.Val++;
    }//end if

    temp.Val = (temp.Val*10U) >> 4;     // Turn into celsius xx.x
    
    /* Populate string */
    for(i=4;i>0;i--)
    {
        tempString[i] = (((char)(temp.Val % 10)) & 0x0F) | 0x30;
        temp.Val /= 10;
    }//end for
    
    /* Turn leading zeros into spaces */
    if(tempString[1] == '0')
    {
        tempString[1] = ' ';
        if(tempString[2] == '0')
            tempString[2] = ' ';
    }//end if
    
    /* Adjust decimal digit */
    tempString[5] = tempString[4];
    tempString[4]='.';
    tempString[6]=0xF8;     // Degree symbol
    tempString[7]='C';
    tempString[8]='\r';
    tempString[9]=0x00;     // Null-Terminated
    
}//end UpdateCelsiusASCII

/** EOF temperature.c ********************************************************/