/*
 * 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 "pinout.h"




struct pwmDriver pwmSoln;
struct pwmDriver pwmPump;

void pwm_init(void)
{
    unsigned long ulPeriod;

    // //PG1, PG3 on MpHOx

    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.delta = 0;      // diff between old and new pwm settings
    pwmSoln.prevVal = 0;    // place to store previous value (speculative need)
    pwmSoln.period = 1000;  // period in microseconds (tbd) 1msec hardcoded in first implementation. inverse of frequency.
    pwmSoln.permil = 500;         // 0 to 999

    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.delta = 0;      // diff between old and new pwm settings
    pwmPump.prevVal = 0;    // place to store previous value (speculative need)
    pwmPump.period = 1000;  // period in microseconds (tbd) 1msec hardcoded in first implementation. inverse of frequency.
    pwmPump.permil = 500;         // 0 to 999

#ifdef NOCODE

    https://microcontrollerslab.com/pwm-tm4c123-example-codes-tiva-c-launchpad/

    1) Enable Clock to PWM module
        SYSCTL->RCGCPWM |= (1<<0); /*enable clock source to PWM0 module */   TODO figure out how to do this for PWM1

    2) PWM Clock Configuration  RCGCPWM

    3) Enable TM4C123 PWM Generator  PWMnCTL registers

    4) Setting TM4C123 PWM Frequency
        PWM0 Module Clock Period = 1/8Mhz = 0.125µs
        M0PWM4 Timer Period = 1/10KHz = 100µs
        PWM4LOAD Register value = 100µs / 0.125µs = 800

    5) Setting PWM Duty Cycle
        PWM Module Clock Period = 1 / 2MHz = 0.5µs
        PWM Signal Period = 1 / 50Hz = 20ms
        Load register value = 20ms/0.5µs = 40,000

        PWMxCMPx = (100% - required duty cycle in %) of PWMxLOAD

    6) Enable TM4C123 PWM Output
        Enable system clock to PWM module using SYSCTL->RCGCPWM by setting D0 bit for PWM0 and D1 bit for PWM1 module.
        Enable the clock to GPIO port through which PWM output appears on the related GPIO pin (   SYSCTL->RCGCGPIO) .
        Select the system clock divisor function before feeding it to PWM module. Disable it if you don’t want scale down frequency for PWM module (SYSCTL->RCC).
        Set the PWM output GPIO pin as a digital output pin, select its alternate function for PWM module and assign PWM pin to applicable GPIO pin using GPIOF->AFSEL, GPIOF->PCTL and GPIOF->DEN registers, respectively.
        Disable PWM generator counter before configuring PWM channel by clearing bit D0 of PWMn->_x_CTL register. Here n is module number which is 0 or 1 and x is generator block which is x=0,1,2,3.
        After that, select the counter mode either count-up or count down mode by setting or clearing bit1 of PWMn->_x_CTL register.
        Select PWM channel and select the actions of output signal when counter reloaded or counter matches with PWMxCMPx register.
        Calculate load value according to required frequency and load this value to  PWMx->_x_LOAD register.
        Calculate compare register value for required duty cycle and place the value in  PWMx->_x_CMPx
        At the end enable the counter of PWM generator by setting D0 bit of PWMx->_x_CTL register and also enable the PWM output on GPIO pin with PWMx->ENABLE register.

#define SYSCTL_RCGCGPIO_R6      0x00000040  // GPIO Port G Run Mode Clock
                                            // Gating Control

    /* Enable Peripheral Clocks */
    SYSCTL_RCGCPWM_R |= 2;       /* enable clock to PWM0 is 1, PWM1 is 2 */
    SYSCTL_RCGCGPIO_R |= SYSCTL_RCGCGPIO_R6;   /* enable clock to PORTG */
    SYSCTL_RCC_R &= ~0x00100000; /* no pre-divide for PWM clock */

    /* Enable port PG3 for PWM1 M1PWM7 */
    GPIO_PORTG_AFSEL_R = 0x08;      /* PG3 uses alternate function */
    GPIO_PORTG_PCTL_R &= ~0x00000000; /* make PG3 PWM output pin */
    GPIO_PORTG_PCTL_R |= 0x00000020;            //M1PWM1 is bit.5 of GPIO_PORTG_PCTL_R Digital Function (GPIOPCTL PMCx Bit Field Encoding)
    GPIO_PORTG_DEN_R |= 8;       /* pin digital */



    //
    // Configure the GPIO Pin Mux for PG3
    // for M1PWM1
    //
    MAP_GPIOPinConfigure(GPIO_PG3_M1PWM1);
    MAP_GPIOPinTypePWM(GPIO_PORTG_BASE, GPIO_PIN_3);


    //Configure PWM Clock to match system
    SysCtlPWMClockSet(SYSCTL_PWMDIV_1);

    // Enable the peripherals used by this program.
    SysCtlPeripheralEnable(SYSCTL_PERIPH_GPIOG);
    SysCtlPeripheralEnable(SYSCTL_PERIPH_PWM1);  //The Tiva Launchpad has two modules (0 and 1). Module 1 covers the LED pins
    ulPeriod = SysCtlClockGet() / 200; //PWM frequency 200HZ

    //Configure PF1,PF2,PF3 Pins as PWM
    GPIOPinConfigure(GPIO_PG3_M1PWM1);

    GPIOPinTypePWM(GPIO_PORTG_BASE, GPIO_PIN_3);  // GPIOPinTypePWM(GPIO_PORTF_BASE, GPIO_PIN_1 | GPIO_PIN_2 | GPIO_PIN_3);



    //Configure PWM Options
    //PWM_GEN_2 Covers M1PWM4 and M1PWM5
    //PWM_GEN_3 Covers M1PWM6 and M1PWM7 See page 207 4/11/13 DriverLib doc
    //PWMGenConfigure(PWM1_BASE, PWM_GEN_2, PWM_GEN_MODE_UP_DOWN | PWM_GEN_MODE_NO_SYNC);
    //PWMGenConfigure(PWM1_BASE, PWM_GEN_3, PWM_GEN_MODE_UP_DOWN | PWM_GEN_MODE_NO_SYNC);

    //Set the Period (expressed in clock ticks)
    PWMGenPeriodSet(PWM1_BASE, PWM_GEN_2, ulPeriod);
    PWMGenPeriodSet(PWM1_BASE, PWM_GEN_3, ulPeriod);

    //Set PWM duty-50% (Period /2)
    PWMPulseWidthSet(PWM1_BASE, PWM_OUT_5,ulPeriod/2);
    PWMPulseWidthSet(PWM1_BASE, PWM_OUT_6,ulPeriod/2);
    PWMPulseWidthSet(PWM1_BASE, PWM_OUT_7,ulPeriod/2);

    // Enable the PWM generator
    PWMGenEnable(PWM1_BASE, PWM_GEN_2);
    PWMGenEnable(PWM1_BASE, PWM_GEN_3);

    // Turn on the Output pins
    PWMOutputState(PWM1_BASE, PWM_OUT_5_BIT |PWM_OUT_6_BIT|PWM_OUT_7_BIT, true);

#endif

#ifdef INPROGRESS

    Notes:
    PG3 uses M1PWM1

    //Configure PWM Clock to match system
    SysCtlPWMClockSet(SYSCTL_PWMDIV_1);

    // Enable the peripherals used by this program.
     SysCtlPeripheralEnable(SYSCTL_PERIPH_GPIOG);
     SysCtlPeripheralEnable(SYSCTL_PERIPH_PWM1);  //The Tiva Launchpad has two modules (0 and 1). Module 1 covers the LED pins
     ulPeriod = SysCtlClockGet() / 200; //PWM frequency 200HZ

     //Configure PF1,PF2,PF3 Pins as PWM
     GPIOPinConfigure(GPIO_PG3_M1PWM1);



#define SYSCTL_RCGCGPIO_R6      0x00000040  // GPIO Port G Run Mode Clock
                                            // Gating Control

    /* Enable Peripheral Clocks */
    SYSCTL_RCGCPWM_R |= 2;       /* enable clock to PWM0 is 1, PWM1 is 2 */
    SYSCTL_RCGCGPIO_R |= SYSCTL_RCGCGPIO_R6;   /* enable clock to PORTG */
    SYSCTL_RCC_R &= ~0x00100000; /* no pre-divide for PWM clock */

    /* Enable port PF3 for PWM1 M1PWM7 */
    GPIO_PORTG_AFSEL_R = 0x08;      /* PG3 uses alternate function */
    GPIO_PORTG_PCTL_R &= ~0x00000000; /* make PG3 PWM output pin */
    GPIO_PORTG_PCTL_R |= 0x00000020;            //M1PWM1 is bit.5 of GPIO_PORTG_PCTL_R Digital Function (GPIOPCTL PMCx Bit Field Encoding)
    GPIO_PORTG_DEN_R |= 8;       /* pin digital */



    //
    // Configure the GPIO Pin Mux for PG3
    // for M1PWM1
    //
    MAP_GPIOPinConfigure(GPIO_PG3_M1PWM1);
    MAP_GPIOPinTypePWM(GPIO_PORTG_BASE, GPIO_PIN_3);



    -----------------------------------------------------



    /* Generates 10KHz and 50% duty cycle on PF2 pin of TM4C123 Tiva C Launchpad */
    /* PWM1 module and PWM generator 3 of PWM1 module is used */
    #include "TM4C123GH6PM.h"

    int main(void)
    {

    void Delay_ms(int n);
    /* Clock setting for PWM and GPIO PORT */
    SYSCTL->RCGCPWM |= 2;       /* Enable clock to PWM1 module */
    SYSCTL->RCGCGPIO |= 0x20;   /* Enable system clock to PORTF */
    SYSCTL->RCC &= ~0x00100000; /* Directly feed clock to PWM1 module without pre-divider */

    /* Setting of PF2 pin for M1PWM6 channel output pin */
    GPIOF->AFSEL |= (1<<2);     /* PF2 sets a alternate function */
    GPIOF->PCTL &= ~0x00000F00; /*set PF2 as output pin */
    GPIOF->PCTL |= 0x00000500; /* make PF2 PWM output pin */
    GPIOF->DEN |= (1<<2);      /* set PF2 as a digital pin */

    /* PWM1 channel 6 setting */
    PWM1->_3_CTL &= ~(1<<0);   /* Disable Generator 3 counter */
    PWM1->_3_CTL &= ~(1<<1);   /* select down count mode of counter 3*/
    PWM1->_3_GENA = 0x0000008C; /* Set PWM output when counter reloaded and clear when matches PWMCMPA */
    PWM1->_3_LOAD = 16000;   /* set load value for 1kHz (16MHz/16000) */
    PWM1->_3_CMPA = 8000-1;  /* set duty cyle to 50% by loading of 16000 to PWM1CMPA */
    PWM1->_3_CTL = 1;         /* Enable Generator 3 counter */
    PWM1->ENABLE = 0x40;      /* Enable PWM1 channel 6 output */

        while(1)
        {
                //do nothing
        }
    }


    /* This function generates delay in ms */
    /* calculations are based on 16MHz system clock frequency */

    void Delay_ms(int time_ms)
    {
        int i, j;
        for(i = 0 ; i < time_ms; i++)
            for(j = 0; j < 3180; j++)
                {}  /* excute NOP for 1ms */
    }

    void SystemInit(void)
    {
        /* use this only if you are using old versions of Keil uvision */
        SCB->CPACR |= 0x00f00000;
    }




    --------------------------------------------------------------


    /* Generates 50HZ and variable duty cycle on PF2 pin of TM4C123 Tiva C Launchpad */
    /* PWM1 module and PWM generator 3 of PWM1 module is used. Hence PWM channel*/
    #include "TM4C123GH6PM.h"

    int main(void)
    {
    void Delay_ms(int n);
    int duty_cycle = 4999;

    /* Clock setting for PWM and GPIO PORT */
    SYSCTL->RCGCPWM |= 2;       /* Enable clock to PWM1 module */
    SYSCTL->RCGCGPIO |= 0x20;  /* Enable system clock to PORTF *
    SYSCTL->RCC |= (1<<20);    /* Enable System Clock Divisor function  */
    SYSCTL->RCC |= 0x000E0000; /* Use pre-divider valur of 64 and after that feed clock to PWM1 module*/

     /* Setting of PF2 pin for M1PWM6 channel output pin */
    GPIOF->AFSEL |= (1<<2);  /* PF2 sets a alternate function */
    GPIOF->PCTL &= ~0x00000F00; /*set PF2 as output pin */
    GPIOF->PCTL |= 0x00000500; /* make PF2 PWM output pin */
    GPIOF->DEN |= (1<<2);      /* set PF2 as a digital pin */

    PWM1->_3_CTL &= ~(1<<0);   /* Disable Generator 3 counter */
    PWM1->_3_CTL &= ~(1<<1);   /* select down count mode of counter 3*/
    PWM1->_3_GENA = 0x0000008C;  /* Set PWM output when counter reloaded and clear when matches PWMCMPA */
    PWM1->_3_LOAD = 5000;     /* set load value for 50Hz 16MHz/65 = 250kHz and (250KHz/5000) */
    PWM1->_3_CMPA = 4999;     /* set duty cyle to to minumum value*/
    PWM1->_3_CTL = 1;           /* Enable Generator 3 counter */
    PWM1->ENABLE = 0x40;      /* Enable PWM1 channel 6 output */

        while(1)
        {
            duty_cycle = duty_cycle - 10;
            if (duty_cycle <= 0)
           duty_cycle = 5000;
            PWM1->_3_CMPA = duty_cycle;
            Delay_ms(100);
        }
    }


    /* This function generates delay in ms */
    /* calculations are based on 16MHz system clock frequency */

    void Delay_ms(int time_ms)
    {
        int i, j;
        for(i = 0 ; i < time_ms; i++)
            for(j = 0; j < 3180; j++)
                {}  /* excute NOP for 1ms */
    }

    void SystemInit(void)
    {
        /* use this only if you are using old versions of Keil uvision */
        SCB->CPACR |= 0x00f00000;
    }
#endif
}


/*
 * pwmSoln_set accepts 0 to 100 PWM value
 */
void pwmSoln_set(unsigned int pwmVal)
{
    pwmSoln.permil = pwmVal * 10;
    pwr_soln_off();       //PG1/PG3
    pwmSoln.cnt = 0;
    pwmSoln.state = ST_PWM_PERMIL_LO;

}

/*
 * pwmSoln_stop accepts 0 to 100 PWM value
 */
void pwmSoln_stop(unsigned int val)
{
    pwmSoln.state = ST_PWM_IDLE; // stop the pwm state machine
    if(val == 0)
        pwr_soln_off();       //PG1/PG3
    else
        pwr_soln_on();
    pwmSoln.cnt = 0;

}


/*
 * pwm_stateMachine
 */
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 > (1000 - pwmSoln.permil))
            {
                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.permil)
            {
                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
            pwr_pump_off();       //PG1/PG3
            pwmPump.cnt = 0;
            break;

        case ST_PWM_HI:
            //GPIO is set / hi
            pwr_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 > (1000 - pwmPump.permil))
            {
                pwmPump.state = ST_PWM_PERMIL_HI;
                pwr_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.permil)
            {
                pwmPump.state = ST_PWM_PERMIL_LO;
                pwr_pump_off();       //PG1/PG3
                pwmPump.cnt = 0;

            }
            break;

        default:
            break;
    }


    return(pwmPump.state);
}

//-----------------------------------------
#ifdef EXAMPLECODE
#include "driverlib/pin_map.h"
#include <stdint.h>
#include <stdbool.h>
#include "inc/hw_gpio.h"
#include "inc/hw_types.h"
#include "inc/hw_memmap.h"
#include "driverlib/sysctl.h"
#include "driverlib/pin_map.h"
#include "driverlib/gpio.h"
#include "driverlib/pwm.h"

void delayMS(int ms) {
    SysCtlDelay( (SysCtlClockGet()/(3*1000))*ms ) ;
}

int
main(void)
{
    //Set the clock
   SysCtlClockSet(SYSCTL_SYSDIV_1 | SYSCTL_USE_OSC |   SYSCTL_OSC_MAIN | SYSCTL_XTAL_16MHZ);

   //Configure PWM Clock to match system
   SysCtlPWMClockSet(SYSCTL_PWMDIV_1);

   // Enable the peripherals used by this program.
    SysCtlPeripheralEnable(SYSCTL_PERIPH_GPIOF);
    SysCtlPeripheralEnable(SYSCTL_PERIPH_PWM1);  //The Tiva Launchpad has two modules (0 and 1). Module 1 covers the LED pins

    //Configure PF1,PF2,PF3 Pins as PWM
    GPIOPinConfigure(GPIO_PF1_M1PWM5);
    GPIOPinConfigure(GPIO_PF2_M1PWM6);
    GPIOPinConfigure(GPIO_PF3_M1PWM7);
    GPIOPinTypePWM(GPIO_PORTF_BASE, GPIO_PIN_1 | GPIO_PIN_2 | GPIO_PIN_3);

    //Configure PWM Options
    //PWM_GEN_2 Covers M1PWM4 and M1PWM5
    //PWM_GEN_3 Covers M1PWM6 and M1PWM7 See page 207 4/11/13 DriverLib doc
    PWMGenConfigure(PWM1_BASE, PWM_GEN_2, PWM_GEN_MODE_DOWN | PWM_GEN_MODE_NO_SYNC);
    PWMGenConfigure(PWM1_BASE, PWM_GEN_3, PWM_GEN_MODE_DOWN | PWM_GEN_MODE_NO_SYNC);

    //Set the Period (expressed in clock ticks)
    PWMGenPeriodSet(PWM1_BASE, PWM_GEN_2, 320);
    PWMGenPeriodSet(PWM1_BASE, PWM_GEN_3, 320);

    //Set PWM duty-50% (Period /2)
    PWMPulseWidthSet(PWM1_BASE, PWM_OUT_5,100);
    PWMPulseWidthSet(PWM1_BASE, PWM_OUT_6,100);
    PWMPulseWidthSet(PWM1_BASE, PWM_OUT_7,100);

    // Enable the PWM generator
    PWMGenEnable(PWM1_BASE, PWM_GEN_2);
    PWMGenEnable(PWM1_BASE, PWM_GEN_3);

    // Turn on the Output pins
    PWMOutputState(PWM1_BASE, PWM_OUT_5_BIT | PWM_OUT_6_BIT | PWM_OUT_7_BIT, true);

    //Fade
    bool fadeUp = true;
    unsigned long increment = 10;
    unsigned long pwmNow = 100;
    while(1)
    {
        delayMS(20);
        if (fadeUp) {
            pwmNow += increment;
            if (pwmNow >= 320) { fadeUp = false; }
        }
        else {
            pwmNow -= increment;
            if (pwmNow <= 10) { fadeUp = true; }
        }
        PWMPulseWidthSet(PWM1_BASE, PWM_OUT_5,pwmNow);
        PWMPulseWidthSet(PWM1_BASE, PWM_OUT_6,pwmNow);
        PWMPulseWidthSet(PWM1_BASE, PWM_OUT_7,pwmNow);
    }

}
#endif









//----------------------------------------

#ifdef NOCODE



SYSCTL->RCGCPWM |= (1<<0); /*enable clock source to PWM0 module */

//PG1, PG3 on MpHOx

//What about Pump PD1 or PD2 or PD3 not sure

#include <stdint.h>
#include "inc/tm4c123gh6pm.h"

void delayMs(int n);

int main(void)
{
    int x = 15999;

    /* Enable Peripheral Clocks */
    SYSCTL_RCGCPWM_R |= 2;       /* enable clock to PWM1 */
    SYSCTL_RCGCGPIO_R |= 0x20;   /* enable clock to PORTF */
    SYSCTL_RCC_R &= ~0x00100000; /* no pre-divide for PWM clock */

    /* Enable port PF3 for PWM1 M1PWM7 */
    GPIO_PORTF_AFSEL_R = 8;      /* PF3 uses alternate function */
    GPIO_PORTF_PCTL_R &= ~0x0000F000; /* make PF3 PWM output pin */
    GPIO_PORTF_PCTL_R |= 0x00005000;
    GPIO_PORTF_DEN_R |= 8;       /* pin digital */

    PWM1_3_CTL_R = 0;            /* stop counter */
    PWM1_3_GENB_R = 0x0000008C;  /* M1PWM7 output set when reload, */
                                 /* clear when match PWMCMPA */
    PWM1_3_LOAD_R = 16000;       /* set load value for 1kHz (16MHz/16000) */
    PWM1_3_CMPA_R = 15999;       /* set duty cycle to min */
    PWM1_3_CTL_R = 1;            /* start timer */
    PWM1_ENABLE_R = 0x80;        /* start PWM1 ch7 */

    for(;;) {
        x = x - 100;

        if (x <= 0)
            x = 16000;

        PWM1_3_CMPA_R = x;

        delayMs(20);
    }
}


/* delay n milliseconds (16 MHz CPU clock) */
void delayMs(int n)
{
    int i, j;

    for(i = 0 ; i < n; i++)
        for(j = 0; j < 3180; j++)
            {}  /* do nothing for 1 ms */
}
#endif
