#include <stdint.h>
#include <stdbool.h>
#include "../../typedefs.h"
#include "inc/tm4c123gh6pge.h"
#include "inc/hw_types.h"
#include "inc/hw_memmap.h"      /* peripheral addresses       */
#include "driverlib/sysctl.h"   /* system control API */
#include "driverlib/gpio.h"     /* GPIO API             */
#include "driverlib/pwm.h"
#include "../pwm.h"
#include <stdio.h>
#include <stdarg.h>

/***********************************************************************************************//**
 * @details   Initialize a PWM generator at a specific frequency and polarity.  Pass in the channel
 *            (n 0-7), frequency in hz (100Hz to 100khz), center flag, and the invert flag (Inverted
 *            0/1). If the Centered flag is 0 the PWM signal will be left justified.  If it 1 the
 *            PWM signal will be centered.  This function does not enable the PWM's after setup.
 *            This allows for the syncing in time of multiple PWM's after they are configured.  
 *
 * @param     n    Channel number of the PWM (0-7)
 * @param     hz   Operation Frequency in hertz (100Hz to 100khz)
 * @param     centered Indicates how the counter works for interrupt triggers
 * @param     inverted Indicates whether active high or low (0 or 1)
 * @param     adc_trigger True if the channel/generator should generate an ADC sequence 
 **************************************************************************************************/
void hw_PWM1_Initialize(Int32 n, Int32 hz, UInt32 centered, UInt32 inverted, Boolean adc_trigger)
{
  volatile unsigned long temp;
  int PwmGen;
  int PwmN;
  int PwmBit;

  unsigned long Config = (
    PWM_GEN_MODE_NO_SYNC         |      // If sync'd, then a manual force sync must be called
                                        //   when making changes to the period or pulse width.
                                        //   Can be sync'd across multiple PWM's.
    PWM_GEN_MODE_DBG_STOP        |
    PWM_GEN_MODE_GEN_SYNC_LOCAL  |      // Changes to the PWM are made when the Load counter
                                        //   reaches 0 at the end of the PWM cycle. Prevents
                                        //   pulses from triggering A to D sequence in the middle
                                        //   of an A to D sequence.  Errata also applies. The Global
                                        //   setting would also require a sync function to be called
                                        //   in order to update the settings.
    PWM_GEN_MODE_DB_NO_SYNC      |      // No Deadband usage in this configuration.
    PWM_GEN_MODE_FAULT_UNLATCHED |
    PWM_GEN_MODE_FAULT_NO_MINPER |
    PWM_GEN_MODE_FAULT_LEGACY);


  switch(n)
  {
    case 0: PwmGen = PWM_GEN_0; PwmN = PWM_OUT_0; PwmBit = PWM_OUT_0_BIT; break;
    case 1: PwmGen = PWM_GEN_0; PwmN = PWM_OUT_1; PwmBit = PWM_OUT_1_BIT; break;
    case 2: PwmGen = PWM_GEN_1; PwmN = PWM_OUT_2; PwmBit = PWM_OUT_2_BIT; break;
    case 3: PwmGen = PWM_GEN_1; PwmN = PWM_OUT_3; PwmBit = PWM_OUT_3_BIT; break;
    case 4: PwmGen = PWM_GEN_2; PwmN = PWM_OUT_4; PwmBit = PWM_OUT_4_BIT; break;
    case 5: PwmGen = PWM_GEN_2; PwmN = PWM_OUT_5; PwmBit = PWM_OUT_5_BIT; break;
    case 6: PwmGen = PWM_GEN_3; PwmN = PWM_OUT_6; PwmBit = PWM_OUT_6_BIT; break;
    case 7: PwmGen = PWM_GEN_3; PwmN = PWM_OUT_7; PwmBit = PWM_OUT_7_BIT; break;
    default: return;
  }

  // At the current clock rate the PWMs are limited to 100Hz to 100kHz.
  if (hz < 100)   hz = 100;
  if (hz > 100000) hz = 10000;

  // Enable the peripheral.
  SysCtlPeripheralEnable(SYSCTL_PERIPH_PWM1);

  // Do a dummy read to insert a few cycles after enabling the peripheral.
  temp = SYSCTL_RCGC2_R;
  temp = SYSCTL_RCGC2_R;

  // Set the PWM clock to the system clock / 4.
//  PWM1_PC_R = 0x00000101;                   // Direct hardware set
//  PWMClockSet(PWM1_BASE, PWM_PC_PWMDIV_4);  // Missing new TivaWare function
  SysCtlPWMClockSet(SYSCTL_PWMDIV_4);         // LM3 Legacy Function

  // The PWM can be centered or left justified.
  if (centered)
  {
    Config |= PWM_GEN_MODE_UP_DOWN;
  }
  else
  {
    Config |= PWM_GEN_MODE_DOWN;
  }

  // Configure the PWM generator with the above set options.
  PWMGenConfigure(PWM1_BASE, PwmGen, Config);

  // Set the period for the selected PWM generator.
  PWMGenPeriodSet(PWM1_BASE, PwmGen, (SysCtlClockGet()/4) / hz);

  // Set up a small dummy pulse width to keep the tied A to D sequencer from colliding.
  PWMPulseWidthSet(PWM1_BASE, PwmN, 200);

  // Invert the PWM signal.
  PWMOutputInvert(PWM1_BASE, PwmBit, (inverted != 0));

  // Set up a trigger that can be captured by the A to D to start a capture sequence.
  if (adc_trigger)
  {
    PWMGenIntTrigEnable(PWM1_BASE, PwmGen, PWM_TR_CNT_LOAD);
  }
  else
  {
    PWMGenIntTrigDisable(PWM1_BASE, PwmGen, PWM_TR_CNT_LOAD);
  }
}

/*************************************************************************/
   
SysCtlClockSet( SYSCTL_SYSDIV_4 | SYSCTL_USE_PLL | SYSCTL_OSC_MAIN | SYSCTL_XTAL_16MHZ );


SysCtlPeripheralEnable( SYSCTL_PERIPH_GPIOG );

GPIOPinConfigure(GPIO_PG2_M1PWM0);
GPIOPinTypePWM(GPIO_PORTG_BASE, GPIO_PIN_2);
hw_PWM1_Initialize( 0, 3000, 1, 0, True );

GPIOPinConfigure(GPIO_PG3_M1PWM1);
GPIOPinTypePWM(GPIO_PORTG_BASE, GPIO_PIN_3);
hw_PWM1_Initialize( 1, 3000, 1, 0, True );

GPIOPinConfigure(GPIO_PG4_M1PWM2);
GPIOPinTypePWM(GPIO_PORTG_BASE, GPIO_PIN_4);
hw_PWM1_Initialize( 2, 2000, 1, 0, True );

PWMGenEnable(PWM1_BASE, PWM_GEN_0);
PWMGenEnable(PWM1_BASE, PWM_GEN_1);

