/*
 * pwm.c
 *
 *  Created on: 16 Jun 2020
 *      Author: tm
 */

#include "system.h"
#include "pwm.h"
#include "board_util.h"
#include <stdbool.h>
#include <stdint.h>
#include "inc/hw_gpio.h"
#include "inc/hw_memmap.h"
#include "inc/hw_types.h"
#include "driverlib/gpio.h"
#include "driverlib/pin_map.h"
#include "driverlib/rom.h"
#include "driverlib/ROM_map.h"
#include "driverlib/sysctl.h"
#include "driverlib/pwm.h"
//#include "pinout.h"

volatile uint32_t ui32Load;
volatile uint32_t ui32PWMClock;

unsigned int pwmSoln_initFlg = 0;

struct pwmDriver pwmSoln;
struct pwmDriver pwmPump;

void pwm_init(void)
{
    // First call to pwmSoln_set needs to init the pwm peripheral
    // It's done this way to keep power consumption low, no need to initialize pwm if the solenoid is not used.
    pwmSoln_initFlg = 0;
}

void pwm_test(void)
{
#if BOARD_MPHOX == 1

    unsigned int  pwmVal;

    // //PG1, PG3 on MpHOx

    // using a statemachine run by SysTickHandler()
    pwmSoln.state = 0;      // PG1,PG3 0=off, 1=on, 2=permil, 3=hiZ(future)
    pwmSoln.cnt = 0;
    pwmSoln.gpio = 0;           // variable holding state of gpio PG1
    pwmSoln.period = 1000;      // period in microseconds (tbd) 1msec hardcoded in first implementation. inverse of frequency.
    pwmSoln.percent = 50;       // 0 to 100

    pwmPump.state = 0;          // PG1,PG3 0=off, 1=on, 2=permil, 3=hiZ(future)
    pwmPump.cnt = 0;
    pwmPump.gpio = 0;           // variable holding state of gpio PG1
    pwmPump.period = 1000;      // period in microseconds (tbd) 1msec hardcoded in first implementation. inverse of frequency.
    pwmPump.percent = 50;       // 0 to 999

    //SysCtlClockSet(SYSCTL_SYSDIV_1 | SYSCTL_USE_OSC |   SYSCTL_OSC_MAIN | SYSCTL_XTAL_16MHZ);
    //MAP_SysCtlClockSet(SYSCTL_SYSDIV_4 | SYSCTL_USE_PLL | SYSCTL_XTAL_16MHZ | SYSCTL_OSC_MAIN);

    //Configure PWM Clock as System Clock 50Mhz div by 8 (PWMClock = 6.25MHZ)
    MAP_SysCtlPWMClockSet(SYSCTL_PWMDIV_8);        //was (SYSCTL_PWMDIV_2) when using 16MHz timebase

    // Enable the peripherals used by this program.
    //SysCtlPeripheralEnable(SYSCTL_PERIPH_GPIOG);   //PG3 is used.  Periph enable for GPIOG is done in init.c
    MAP_SysCtlPeripheralEnable(SYSCTL_PERIPH_PWM1);        // PG3 uses M1PWM1

    //GPIOPinTypePWM(GPIO_PORTG_BASE, GPIO_PIN_2 | GPIO_PIN_3);
    MAP_GPIOPinTypePWM(GPIO_PORTG_BASE, GPIO_PIN_3);

    //Configure PG3 Pin as PWM, commented out shows PG2
    //GPIOPinConfigure(GPIO_PG2_M1PWM0);
    MAP_GPIOPinConfigure(GPIO_PG3_M1PWM1);

    ui32PWMClock = 6250000;         // 6.25MHz
#define PWM_FREQUENCY   10000       // 10Khz
    ui32Load = (ui32PWMClock / PWM_FREQUENCY) - 1;  // 6249

    //Configure PWM Options
    //PWM0 and PWM1 are associated with Gen0
    //See page 20x TivaWare Peripheral Driver Library User's Guide
    //MAP_PWMGenConfigure(PWM1_BASE, PWM_GEN_0, PWM_GEN_MODE_DOWN);
    MAP_PWMGenConfigure(PWM1_BASE, PWM_GEN_0, PWM_GEN_MODE_DOWN | PWM_GEN_MODE_NO_SYNC);

    //Set the Period (expressed in clock ticks) for 100usec (10Khz)
    MAP_PWMGenPeriodSet(PWM1_BASE, PWM_GEN_0, ui32Load);      //ui32Load); = 6249

    //Set PWM duty-50% (Period /2) in percent (100 steps)
    pwmVal = 50;        // init for 50% pwm
    MAP_PWMPulseWidthSet(PWM1_BASE, PWM_OUT_1, (pwmVal * ui32Load / 100));  // TODO: Thom check PWM_OUT_1, 3124 is about 50%

    // Enable the PWM generator
    MAP_PWMGenEnable(PWM1_BASE, PWM_GEN_0);

    // Turn on the Output pins
    MAP_PWMOutputState(PWM1_BASE, PWM_OUT_1_BIT, true);

    while(1)
    {
         led_green_on();
         pwmVal = 90;
         MAP_PWMPulseWidthSet(PWM1_BASE, PWM_OUT_1, (pwmVal * ui32Load / 100));
         delay_msec(1000);
         led_green_off();

         led_red_on();
         pwmVal = 10;
         MAP_PWMPulseWidthSet(PWM1_BASE, PWM_OUT_1, (pwmVal * ui32Load / 100));
         delay_msec(1000);
         led_red_off();
    }

#endif

}




/*
 * pwmSoln_init accepts 0 to 100 PWM value
 */
void pwmSoln_set(unsigned int pwmVal)
{
#if BOARD_MPHOX == 1
    //pwmSoln.percent = pwmVal;
    //pwr_soln_off();       //PG1/PG3
    //pwmSoln.cnt = 0;
    //pwmSoln.state = ST_PWM_PERMIL_LO;
    if(pwmSoln_initFlg != 1)
    {
        pwmSoln_initFlg = 1;

        // Assumes system clock is set in init.c for 50MHz (400Mhz / 2 / 4)   THOM: not sure how the extra divide by 2 comes in, but that's what it is.
        //MAP_SysCtlClockSet(SYSCTL_SYSDIV_4 | SYSCTL_USE_PLL | SYSCTL_XTAL_16MHZ | SYSCTL_OSC_MAIN);

        //Configure PWM Clock as System Clock 50Mhz div by 8 (PWMClock = 6.25MHZ)
        MAP_SysCtlPWMClockSet(SYSCTL_PWMDIV_8);        //was (SYSCTL_PWMDIV_2) when using 16MHz timebase

        // Enable the peripherals used by this program.
        //SysCtlPeripheralEnable(SYSCTL_PERIPH_GPIOG);   //PG3 is used.  Periph enable for GPIOG is done in init.c
        MAP_SysCtlPeripheralEnable(SYSCTL_PERIPH_PWM1);        // PG3 uses M1PWM1

        //GPIOPinTypePWM(GPIO_PORTG_BASE, GPIO_PIN_2 | GPIO_PIN_3);
        MAP_GPIOPinTypePWM(GPIO_PORTG_BASE, GPIO_PIN_3);

        //Configure PG3 Pin as PWM, commented out shows PG2
        //GPIOPinConfigure(GPIO_PG2_M1PWM0);
        MAP_GPIOPinConfigure(GPIO_PG3_M1PWM1);

        ui32PWMClock = 6250000;         // 6.25MHz
#define PWM_FREQUENCY   10000       // 10Khz
        ui32Load = (ui32PWMClock / PWM_FREQUENCY) - 1;  // 6249

        //Configure PWM Options
        //PWM0 and PWM1 are associated with Gen0
        //See page 20x TivaWare Peripheral Driver Library User's Guide
        //MAP_PWMGenConfigure(PWM1_BASE, PWM_GEN_0, PWM_GEN_MODE_DOWN);
        MAP_PWMGenConfigure(PWM1_BASE, PWM_GEN_0, PWM_GEN_MODE_DOWN | PWM_GEN_MODE_NO_SYNC);

        //Set the Period (expressed in clock ticks) for 100usec (10Khz)
        MAP_PWMGenPeriodSet(PWM1_BASE, PWM_GEN_0, ui32Load);      //ui32Load); = 6249

        //Set PWM duty-50% (Period /2) in percent (100 steps)
        if(pwmVal >= 100)
            pwmVal = 99;
        MAP_PWMPulseWidthSet(PWM1_BASE, PWM_OUT_1, (pwmVal * ui32Load / 100));  // TODO: Thom check PWM_OUT_1, 3124 is about 50%

        // Enable the PWM generator
        MAP_PWMGenEnable(PWM1_BASE, PWM_GEN_0);

        // Turn on the Output pins
        MAP_PWMOutputState(PWM1_BASE, PWM_OUT_1_BIT, true);
    }
    else
    {
        if(pwmVal >= 100)       // TODO: figure out why pwmVal=100 doesn't work
            pwmVal = 99;
        MAP_PWMPulseWidthSet(PWM1_BASE, PWM_OUT_1, (pwmVal * ui32Load / 100));
    }

#endif
}


/*
 * pwmSoln_stop
 */
void pwmSoln_stop(unsigned int gpioState)
{
#if BOARD_MPHOX == 1
    // THOM todo: test the pwmSoln_stop

    if(pwmSoln_initFlg == 1)
    {
        // Disable the PWM generator
        MAP_PWMGenDisable(PWM1_BASE, PWM_GEN_0);

        // Turn off the Output pins
        MAP_PWMOutputState(PWM1_BASE, PWM_OUT_1_BIT, false);

        // reconfigure the output as GPIO
        MAP_GPIOPinTypeGPIOOutput(GPIO_PORTG_BASE, GPIO_PIN_3); // PG3, pin 52

    }

    pwmSoln_initFlg = 0;   // variable for tracking init of PWM module

    if(gpioState == 0)
        MAP_GPIOPinWrite(GPIO_PORTG_BASE, GPIO_PIN_3, 0x00);    // Clear
    else
        MAP_GPIOPinWrite(GPIO_PORTG_BASE, GPIO_PIN_3, 0xFF);    // Set

    //pwmSoln.state = ST_PWM_IDLE; // stop the pwm state machine

    //if(gpioState == 0)
    //    solenoid_off();     //pwr_soln_off();       //PG1/PG3
    //else
    //    solenoid_on();     //pwr_soln_on();

    //pwmSoln.cnt = 0;
#endif
}


/*
 * pwm_stateMachine (temporary hack as the PWM peripheral was a chore to setup)
 */
int pwmSoln_stateMachine(int pwmState)
{

    if(pwmState <= -1)
    {
        //use global state variables
    }
    else
    {
        pwmSoln.state = (uint32_t)pwmState;       // allow external mediation of stateMachine
    }

    switch(pwmSoln.state)
    {
        case ST_PWM_IDLE:
            // do nothing
            break;

        case ST_PWM_LO:
            //GPIO is clear / lo
            pwr_soln_off();       //PG1/PG3
            pwmSoln.cnt = 0;
            break;

        case ST_PWM_HI:
            //GPIO is set / hi
            pwr_soln_on();       //PG1/PG3
            pwmSoln.cnt = 0;
            break;

        case ST_PWM_PERMIL_LO:
            //GPIO is low and counting to toggle hi. low time is 1000 - permil
            pwmSoln.cnt++;
            if(pwmSoln.cnt > (100 - pwmSoln.percent))
            {
                pwmSoln.state = ST_PWM_PERMIL_HI;
                pwr_soln_on();       //PG1/PG3
                pwmSoln.cnt = 0;

            }
            break;

        case ST_PWM_PERMIL_HI:
            //GPIO is high and counting to toggle low (period is 1msec), high time is equal permil.
            pwmSoln.cnt++;
            if(pwmSoln.cnt > pwmSoln.percent)
            {
                pwmSoln.state = ST_PWM_PERMIL_LO;
                pwr_soln_off();       //PG1/PG3
                pwmSoln.cnt = 0;

            }
            break;

        default:
            pwr_soln_off();       //PG1/PG3
            pwmSoln.cnt = 0;
            break;
    }


    return(pwmSoln.state);
}



int pwmPump_stateMachine(int pwmState)
{

    if(pwmState <= -1)
    {
        //use global state
    }
    else
    {
        pwmPump.state = (uint32_t)pwmState;       // allow external mediation of stateMachine
    }

    switch(pwmPump.state)
    {
        case ST_PWM_LO:
            //GPIO is clear / lo
            pump_off();       //PG1/PG3
            pwmPump.cnt = 0;
            break;

        case ST_PWM_HI:
            //GPIO is set / hi
            pump_on();       //PG1/PG3
            pwmPump.cnt = 0;
            break;

        case ST_PWM_PERMIL_LO:
            //GPIO is low and counting to toggle hi. low time is 1000 - permil
            pwmPump.cnt++;
            if(pwmPump.cnt > (100 - pwmPump.percent))
            {
                pwmPump.state = ST_PWM_PERMIL_HI;
                pump_on();       //PG1/PG3
                pwmPump.cnt = 0;

            }
            break;

        case ST_PWM_PERMIL_HI:
            //GPIO is high and counting to toggle low (period is 1msec), high time is equal permil.
            pwmPump.cnt++;
            if(pwmPump.cnt > pwmPump.percent)
            {
                pwmPump.state = ST_PWM_PERMIL_LO;
                pump_off();       //PG1/PG3
                pwmPump.cnt = 0;

            }
            break;

        default:
            break;
    }


    return(pwmPump.state);
}











