Previous Page
    /******************************************************************************
    * CompuCool.c - This program will provide the necessary software for the
    *               CompuCool project; a fan controller that is based on
    *               temperature or a users input from potentiometers.
    *
    * Spencer Black January 12, 2015
    *******************************************************************************
    * Project master header file
    ******************************************************************************/
    #include "includes.h"
    /******************************************************************************
    * Defines
    ******************************************************************************/
    //Init Macros for switch debounce using a digital filter on Port D bit 6
    #define INIT_GPIOD_PDDR() (GPIOD_PDDR &= ~0x00000040)
    #define INIT_PORTD_PCR6() (PORTD_PCR6 |= 0x00000110)
    #define INIT_PORTD_DFCR() (PORTD_DFCR |= 0x00000001)
    #define INIT_PORTD_DFWR() (PORTD_DFWR |= 0x00000008)
    #define INIT_PORTD_DFER() (PORTD_DFER |= 0x00000040)

    //Init Macros for incoming tachometer signals on Port D bits 0-5
    #define INIT_GPIOD_PDDR0() (GPIOD_PDDR &= ~0x00000001)
    #define INIT_PORTD_PCR0() (PORTD_PCR0 |= 0x00000100)
    #define INIT_GPIOD_PDDR1() (GPIOD_PDDR &= ~0x00000002)
    #define INIT_PORTD_PCR1() (PORTD_PCR1 |= 0x00000100)
    #define INIT_GPIOD_PDDR2() (GPIOD_PDDR &= ~0x00000004)
    #define INIT_PORTD_PCR2() (PORTD_PCR2 |= 0x00000100)
    #define INIT_GPIOD_PDDR3() (GPIOD_PDDR &= ~0x00000008)
    #define INIT_PORTD_PCR3() (PORTD_PCR3 |= 0x00000100)
    #define INIT_GPIOD_PDDR4() (GPIOD_PDDR &= ~0x00000010)
    #define INIT_PORTD_PCR4() (PORTD_PCR4 |= 0x00000100)
    #define INIT_GPIOD_PDDR5() (GPIOD_PDDR &= ~0x00000020)
    #define INIT_PORTD_PCR5() (PORTD_PCR5 |= 0x00000100)

    #define BUTTON 0x00000040  //Front panel push button for Port D bit 6

    #define LINE2 0xC0         //Line number 2 value

    #define ADC17 0x11         //Temp Sensor ADC channel

    #define POTMAX 0xE6        //Potentiometer max value

    //enumerated type for the user states
    typedef enum {ResetState,AutomaticState,ManualState} USER_STATE;

    /******************************************************************************
    * Constants
    ******************************************************************************/
    // Wait for slice set to 10ms
    const INT32U WaitForMs = 10;

    /******************************************************************************
    * Global Variables
    ******************************************************************************/
    //Start up and current state messages
    static INT8U StartMess1[]  = ("   Welcome to   ");
    static INT8U StartMess2[]  = ("   CompuCool    ");
    static INT8U AutoMess[]    = ("  Auto Control  ");
    static INT8U ManualMess[]  = (" Manual Control ");
    static INT8U TempMess[]    = ("   Temp     F   ");

    //One second counter
    static INT8U OneSecCount = 0;

    //Storage for the converted temperature to a fan speed. Initialized to a 100%
    volatile INT8U PercentDuty = 100;

    //Storage for returned value of the currently selected ADC and converted temp
    volatile INT32U AdcVal  = 0;
    volatile INT8U  TempVal = 0;

    //Array for ADC channels (8,9,12,13,14,15) on MCU
    static INT8U ADC_Array[] = {0x08, 0x09, 0x0C, 0x0D, 0x0E, 0x0F};

    //Storage array for converted potentiometer readings
    INT8U Pot_Array[] = {0x00, 0x00, 0x00, 0x00, 0x00, 0x00};

    USER_STATE CurrentState  = ResetState;

    /******************************************************************************
    * Prototypes
    ******************************************************************************/
    void State_Change_Task(void);
    void Fan_Control_Task(void);
    void LED_Task(void);
    INT8U Convert_Temp_DegF(INT32U val);
    INT8U Convert_Temp_Duty_Cycle(INT8U temperature);
    INT8U Convert_Pot_Duty_Cycle(INT32U potval);
    INT8U Switch_Debounce(void);
    void WaitForSlice(INT32U waitms);
    INT8U Delay_Start_Message(void);

    /*****************************************************************************/
    int main(void){

        //Initialize clocks for peripherals
        SIM_SCGC5 |= (SIM_SCGC5_PORTA_MASK |
                      SIM_SCGC5_PORTB_MASK |
                      SIM_SCGC5_PORTC_MASK |
                      SIM_SCGC5_PORTD_MASK |
                      SIM_SCGC5_PORTE_MASK);

        //Initialize front panel push button with hardware filter
        INIT_GPIOD_PDDR();             //Set data direction as input
        INIT_PORTD_PCR6();             //Set port for input with filtering
        INIT_PORTD_DFCR();             //Initialize clock source
        INIT_PORTD_DFWR();             //Initialize 10ms filter width
        INIT_PORTD_DFER();             //Initialize clock on bit 6

        //Initialize ports to read fan tachometers
        INIT_GPIOD_PDDR0();            //Set data direction as input bit 0
        INIT_PORTD_PCR0();             //Set port for input
        INIT_GPIOD_PDDR1();            //Set data direction as input bit 1
        INIT_PORTD_PCR1();             //Set port for input
        INIT_GPIOD_PDDR2();            //Set data direction as input bit 2
        INIT_PORTD_PCR2();             //Set port for input
        INIT_GPIOD_PDDR3();            //Set data direction as input bit 3
        INIT_PORTD_PCR3();             //Set port for input
        INIT_GPIOD_PDDR4();            //Set data direction as input bit 4
        INIT_PORTD_PCR4();             //Set port for input
        INIT_GPIOD_PDDR5();            //Set data direction as input bit 5
        INIT_PORTD_PCR5();             //Set port for input

        SysTickDlyInit();              //Initialize system timer for WaitForSlice
        ADC_Init();                    //Initialize ADC for temp sensor and tachs
        PWM_Init();                    //Initialize PWM output for fans
        LED_Init();                    //Initialize SPI and ports for LED driver
        LCD_Init();                    //Initialize I2C for LCD and run LCD startup
        LCD_Clr_Disp();                //Clear display for new message
        LCD_Disp_String(StartMess1,16);//Start up message to user
        LCD_New_Line(2);
        LCD_Disp_String(StartMess2,16);

        while(1){                      //Start of program
            WaitForSlice(WaitForMs);
            State_Change_Task();
            Fan_Control_Task();
            LED_Task();
        }
    }

    /******************************************************************************
    * State_Change_Task(void)
    *
    * Description: This task checks if the push button is pressed and then
    *              displays the proper message and changes the programs state.
    *
    * Parameters: None
    *
    * Spencer Black April 3, 2015
    ******************************************************************************/
    void State_Change_Task(void){
        static INT8U delayflag = FALSE;      //Flag for ResetState

        switch(CurrentState){
            case(ResetState):
                //Holds start message on the LCD for 2 seconds so the user has a
                // chance to read it.
                delayflag = Delay_Start_Message();
                if(delayflag == TRUE){
                    LCD_Clr_Line(1);             //Clear display for new message
                    LCD_Disp_String(AutoMess,16);
                    LCD_New_Line(2);
                    LCD_Disp_String(TempMess,16);
                    OneSecCount = 99;            //Reset counter so temp updates
                    CurrentState = AutomaticState;
                }else{
                    //Do nothing
                }
                break;

            case(AutomaticState):
                if(Switch_Debounce() == TRUE){
                    LCD_Clr_Line(1);             //Clear display for new message
                    LCD_Disp_String(ManualMess,16);
                    OneSecCount = 99;            //Reset counter so temp updates
                    CurrentState = ManualState;
                }else{
                    //Do nothing
                }
                break;

            case(ManualState):
                if(Switch_Debounce() == TRUE){
                    LCD_Clr_Line(1);             //Clear display for new message
                    LCD_Disp_String(AutoMess,16);
                    OneSecCount = 99;            //Reset counter so temp updates
                    CurrentState = AutomaticState;
                }else{
                    //Do nothing
                }
                break;

            default:
                break;
        }
    }

    /******************************************************************************
    * Fan_Control_Task(void)
    *
    * Description: This task is dependent on the State_Change_Task() which
    *              allows the program to control the fan speeds using
    *              temperature or it allows the user to control the fan speeds
    *              via the potentiometers connected to the following ADCs of the
    *              Kinetis K02: ADC0_SE8, ADC0_SE9, ADC0_SE12, ADC0_SE13, ADC0_SE14
    *              and ADC0_SE15. Lastly it updates the LCD with the current
    *              ambient temperature.
    *
    * Parameters: None
    *
    * Spencer Black April 6, 2015
    ******************************************************************************/
    void Fan_Control_Task(void){
        static INT8U adccount = 0;        //Value to increment through ADC channels
        static INT8U averagetemp = 0;     //Place to store averaged temperature

        switch(CurrentState){
            case(ResetState):
                //Do Nothing
                break;

            case(AutomaticState):
                //Gets the temperature value, converts it into deg F, averages the
                // previously averaged temp(s) with the new temp and then converts
                // the averaged temperature into a duty cycle.
                AdcVal = ADC_Value(ADC17);
                TempVal = Convert_Temp_DegF(AdcVal);
                averagetemp = ((averagetemp + TempVal)/2);
                PercentDuty = Convert_Temp_Duty_Cycle(averagetemp);

                //1 second delay between updates to the Flex Timers
                if(OneSecCount >= 100){
                    //Update the duty cycle with the converted temperature value
                    PWM_Set_Duty_Cycle(0,PercentDuty);
                    PWM_Set_Duty_Cycle(1,PercentDuty);
                    PWM_Set_Duty_Cycle(2,PercentDuty);
                    PWM_Set_Duty_Cycle(3,PercentDuty);
                    PWM_Set_Duty_Cycle(4,PercentDuty);
                    PWM_Set_Duty_Cycle(5,PercentDuty);

                    //Clear temperature from the display and update LCD with the
                    // new temperature value.
                    LCD_Clr_Characters(2,9,11);
                    LCD_Disp_Dec_Byte(averagetemp);
                    OneSecCount = 0;                 //Reset 1 second counter
                }else{
                    OneSecCount++;                   //Increment 1 second counter
                }
                break;

            case(ManualState):
                //Gets the temperature value, converts it into deg F, averages the
                // previously averaged temp(s) and displays the temperature
                AdcVal = ADC_Value(ADC17);
                TempVal = Convert_Temp_DegF(AdcVal);
                averagetemp = ((averagetemp + TempVal)/2);

                if (OneSecCount >= 100){
                    //Clear temperature from the display and update LCD with the
                    // new temperature value.
                    LCD_Clr_Characters(2,9,11);
                    LCD_Disp_Dec_Byte(averagetemp);
                    OneSecCount = 0;
                }else{
                    OneSecCount++;
                }

                if(adccount <= 5){
                    //Get the value of the selected ADC, convert the reading to a
                    // duty cycle and then update the Flex Timer.
                    AdcVal = ADC_Value(ADC_Array[adccount]);
                    Pot_Array[adccount] = Convert_Pot_Duty_Cycle(AdcVal);
                    PWM_Set_Duty_Cycle(adccount,Pot_Array[adccount]);
                    adccount++;           //Increment the array index
                }else{
                    adccount = 0;         //Reset the array index for the next pass
                }
                break;

            default:
                break;
        }
    }

    /******************************************************************************
    * LED_Task(void)
    *
    * Description: This function is dependent on the State_Change_Task() and the
    *              Fan_Control_Task() which allows the program to update the front
    *              panel LEDs with the relative fan speeds.
    *
    * Parameters: None
    *
    * Spencer Black May 13, 2015
    ******************************************************************************/
    void LED_Task(void){
        static INT8U ledcount = 0;   //Current channel to update in Manual Mode

        switch(CurrentState){
            case(ResetState):
                //Flashes the LED's in a preset pattern on reset
                LED_Write_Reset();
                break;

            case(AutomaticState):
                //Updating the LED colors every 1 second is acceptable due to the
                // slow increase/decrease of temperature over time. Also avoids the
                // LEDs flickering when on the edge of temperatures ranges.
                if(OneSecCount >= 100){
                    LED_Write_Auto(PercentDuty);
                }else{
                    //Do Nothing
                }
                break;

            case(ManualState):
                //Checks the current duty cycle value of the potentiometer stored
                // in Pot_Array and updates the color of the LED.
                if(ledcount <= 5){
                    LED_Write_Manual(ledcount,Pot_Array[ledcount]);
                    ledcount++;
                }else{
                    ledcount = 0;
                }
                break;

            default:
                break;
        }
    }

    /******************************************************************************
    * Convert_Temp_DegF(INT32U val)
    *
    * Description: This function will take in an ADC value from the ADC_Value()
    *              function and converts it into +/- 1 degree F value.
    *
    * Parameters: val is the un-converted temperature value
    *             returns the converted temperature value as INT8U because
    *             temperatures are not expected to go over 110 deg F.
    *
    * Spencer Black April 3, 2015
    ******************************************************************************/
    INT8U Convert_Temp_DegF(INT32U val){
        INT32U temp = val;

        //Scaled up (value - 500mV offset) by 1000
        //Scaled up 1.33(deg C/step) by 100;
        //Total scale up is 100000
        temp = (((temp * 1000) - 37500) * 133);//Converts to deg C

        //Multiply temp in Celcius by (9/5) and add 32 to convert to deg F
        temp = (((temp / 5) * 9) + 3200000);

        //Restore the value to a normal temperature reading
        //Ignores the remainder and returns +/- 1 deg F value
        temp = (temp/100000);
        return temp;
    }

    /******************************************************************************
    * Convert_Temp_Duty_Cycle(INT8U temperature)
    *
    * Description: This function will take in the converted temperature from the
    *              Convert_Temp_DegF() function and convert it into a PWM duty
    *              cycle. 20% <= 68 deg F up to 100% = 108 deg F.
    *
    * Parameters: temperature is in deg F
    *             returns a duty cycle between 20% and 100%
    *
    * Spencer Black April 3, 2015
    ******************************************************************************/
    INT8U Convert_Temp_Duty_Cycle(INT8U temperature){
        INT8U tempvalue = temperature;
        static INT8U duty = 0;

        if(tempvalue >= 108){                     //Greater than 103 deg F is 90%
            duty = 100;
        }else if(tempvalue <= 70){                //Less than 70 deg F is 20%
            duty = 20;
        }else{
            duty = (((tempvalue - 68) + 10) * 2); //Converts deg F to a duty cycle
        }
        return duty;
    }

    /******************************************************************************
    * Convert_Pot_Duty_Cycle(INT8U potvalue)
    *
    * Description: This function will take in a raw potentiometer reading with a
    *              reference voltage from 0 - 3.3V and converts it into a PWM duty
    *              cycle. 20% <= 68 deg F up to 100% = 108 deg F.
    *
    * Parameters: potval is from the ADC
    *             returns a duty cycle between 20% and 100%
    *
    * Spencer Black April 3, 2015
    ******************************************************************************/
    INT8U Convert_Pot_Duty_Cycle(INT32U potval){
        INT32U potvalue = potval;
        static INT8U potdutycycle = 0;

        if(potvalue >= POTMAX){                    //Greater than max value is 90%
            potdutycycle = 100;
        }else if(potvalue <= 0){                   //Less than 0 is 20%
            potdutycycle = 20;
        }else{
            //Converts the pot value to a duty cycle
            potdutycycle = (((potvalue * 28) / 80) + 20);
        }
        return potdutycycle;
    }

    /******************************************************************************
    * Switch_Debounce(void)
    *
    * Description: This function will check if the switch is pressed and then
    *              debounce the signal to verify that it was legitimate.
    *
    * Parameters: Returns TRUE if the switch is verified to be pressed and FALSE if
    *             the switch is not pressed or pressed less than the time slice
    *             (<10ms)
    *
    * Spencer Black April 3, 2015
    ******************************************************************************/
    INT8U Switch_Debounce(void){
        static INT8U swflag = FALSE;
        static INT8U swheld = FALSE;
        static INT8U rtn = FALSE;

        if((GPIOD_PDIR & BUTTON) == BUTTON){ //Check if the button is pressed
            if(swheld == TRUE){              //Avoid a held button
                rtn = FALSE;
            }else if(swflag == TRUE){        //Verify button press
                swheld = TRUE;
                rtn = TRUE;                  //Return valid button press
            }else{
                swflag = TRUE;
                rtn = FALSE;
            }
        }else{
            swheld = FALSE;                  //Reset flags for next button press
            swflag = FALSE;
            rtn = FALSE;
        }
        return rtn;                          //Return TRUE if valid button press
    }

    /******************************************************************************
    * INT8U Delay_Start_Message(void)
    *
    * Description: Non-blocking 2 second counter that is dependent on a 10ms time
    *              slice from WaitForSlice
    *
    * Parameters: Returns True after 2 seconds
    *
    * Spencer Black April 26, 2015
    ******************************************************************************/
    INT8U Delay_Start_Message(void){
        static INT8U done = FALSE;
        static INT16U delay = 0;

        if(delay >= 200){
            done = TRUE;
        }else{
            delay++;
            done = FALSE;
        }
        return done;
    }

    /******************************************************************************
    * WaitForSlice() - Time slicer. Uses SysTickDelay module for a time slice
    *                  period of TIME_SLICE_DB.
    * Modules: SysTickDelay
    * Member: GetmsCnt()
    * Todd Morton
    ******************************************************************************/
    void WaitForSlice(INT32U waitms){
        static INT32U LastmsCnt;
        static INT32U TSInit = TRUE;

        if(TSInit){                       //Initialize first time through
            LastmsCnt = GetmsCnt();
            TSInit = FALSE;
        }else{                            //Wait for next time slice
            while((GetmsCnt() - LastmsCnt) < waitms){}
            LastmsCnt += waitms;          //Set up for next time slice
        }
    }
    

Previous Page