//#define PWM_TEST

// Import Libraries
#include <S12C32bits.h>
#include <hidef.h>
#include <mc9s12e128.h>
#include <stdio.h>
#include "PWM.h"

/*----------------------------- Module Defines ----------------------------*/
// Define Maximum channels to be 3
#define MAX_CHANNELS   5

// Define Min Duty Cycle to 0
#define PWMS12_MIN_DC   0
// Define Max Duty Cycle to 100
#define PWMS12_MAX_DC   100
// Define PWM Error/OK Constants
#define PWMS12_ERR     -1
#define PWMS12_OK       0

/*---------------------------- Module Variables ---------------------------*/
// Create a module variable containing the Duty Cycle register of each PWM channel
static unsigned char * const DutyRegisters[] = { &PWMDTY0, &PWMDTY1, &PWMDTY2, &PWMDTY3, &PWMDTY4};

/*------------------------------ Module Functions ------------------------------*/
// PWMPeriod: Takes in an unsigned long value representing the frequency, returns nothing
void PWMFrequency(unsigned char group, unsigned long frequency){
	unsigned char PreScalingPower = 0;
	unsigned char PreScaleValue = 1;
	unsigned char ScalerValue;
	unsigned long ScalingFactor;

	// Multiply input frequency by 2
	frequency *= 2;

  // Create a scaling factor number which will be the desired product of the prescaler and scaler
	ScalingFactor = 120000/frequency;

  // While Scaling factor can be divided by 2
	while (ScalingFactor % 2 == 0){
    // Divide the scaling factor by 2 and store it into the scaling facor
    ScalingFactor = ScalingFactor/2;
    // Count how many times the scaling factor can be divided by 2
    PreScalingPower++;
	}

  // Set the Scaler Value to be the current value of the ScalingFactor
	ScalerValue = (unsigned char)ScalingFactor;

	if(group == 0) {
    // Set pre-scaler for clock A
    PWMPRCLK &= ~(_S12_PCKA0 | _S12_PCKA1 | _S12_PCKA2);
	}
	else if(group == 1) {
    // Set pre-scaler for clock B
    PWMPRCLK &= ~(_S12_PCKB0 | _S12_PCKB1 | _S12_PCKB2);
	}

    if (group == 0) {

	    // Take the the prescaling power and apply the correct bit values to set to the prescaler register
	    switch (PreScalingPower){

	    	case 0: // divide by 1
	    	break;

	    	case 1: // divide by 2
	    	PWMPRCLK |= _S12_PCKA0;
	    	break;

	    	case 2: // divide by 4
	    	PWMPRCLK |= _S12_PCKA1;
	    	break;

	    	case 3: // divide by 8
	    	PWMPRCLK |= (_S12_PCKA0|_S12_PCKA1);
	    	break;

	    	case 4: // divide by 16
	    	PWMPRCLK |= _S12_PCKA2;
	    	break;

	    	case 5: // divide by 32
	    	PWMPRCLK |= (_S12_PCKA0|_S12_PCKA2);
	    	break;

	    	case 6: // divide by 64
	    	PWMPRCLK |= (_S12_PCKA1|_S12_PCKA2);
	    	break;

	    	case 7: // divide by 128
	    	PWMPRCLK |= (_S12_PCKA0 | _S12_PCKA1 | _S12_PCKA2);
	    	break;
	    }
    }
    else if (group == 1) {

    	switch (PreScalingPower){

	    	case 0: // divide by 1
	    	break;

	    	case 1: // divide by 2
	    	PWMPRCLK |= _S12_PCKB0;
	    	break;

	    	case 2: // divide by 4
	    	PWMPRCLK |= _S12_PCKB1;
	    	break;

	    	case 3: // divide by 8
	    	PWMPRCLK |= (_S12_PCKB0|_S12_PCKB1);
	    	break;

	    	case 4: // divide by 16
	    	PWMPRCLK |= _S12_PCKB2;
	    	break;

	    	case 5: // divide by 32
	    	PWMPRCLK |= (_S12_PCKB0|_S12_PCKB2);
	    	break;

	    	case 6: // divide by 64
	    	PWMPRCLK |= (_S12_PCKB1|_S12_PCKB2);
	    	break;

	    	case 7: // divide by 128
	    	PWMPRCLK |= (_S12_PCKB0 | _S12_PCKB1 | _S12_PCKB2);
	    	break;
	    }
    }

    if(group == 0) {
    // Use scaled clock A for group A
    PWMCLK |= (_S12_PCLK0 | _S12_PCLK1 | _S12_PCLK4);
    // Set the ScalerValue for the Scaled Clock
    PWMSCLA = ScalerValue;
    }
    else if(group == 1) {
    // Use scaled clock B for group B
    PWMCLK |= (_S12_PCLK2 | _S12_PCLK3);
    // Set the ScalerValue for the Scaled Clock
    PWMSCLB = ScalerValue;
    }
}

// PWMDutyCycle: Takes in two chars representing DutyCycle and channel,
//               returns a char representing error or OK
signed char PWMDutyCycle(unsigned char channel, unsigned char dutyCycle){
    // If DutyCycle and Channel Number exceed the maximum, return error
    if ((channel >= MAX_CHANNELS) || (dutyCycle > PWMS12_MAX_DC))
        return PWMS12_ERR;
    else
    // Otherwise, change the Duty Cycle in the DutyCycle register for the respective channel, return OK
        *DutyRegisters[channel] = dutyCycle;
    return PWMS12_OK;
}

// PWM Polarity: Takes a char representing the channel, and a char representing the direction of polarity and sets the polarity
void PWMPolarity(char Ch, char Dir){
	switch (Dir){
		case 1: // If Active HI desired
			if (Ch == 0) // If Channel 0 (LEFTWHEEL)
				PWMPOL |= _S12_PPOL0; // Set Channel 0 to active HI
			else if (Ch == 1) // If Channel 1 (RIGHTWHEEL)
				PWMPOL |= _S12_PPOL1; // Set Channel 1 to active HI
			else if (Ch == 2) // If Channel 1 (RIGHTWHEEL)
				PWMPOL |= _S12_PPOL2; // Set Channel 2 to active HI
			else if (Ch == 3) // If Channel 1 (RIGHTWHEEL)
				PWMPOL |= _S12_PPOL3; // Set Channel 3 to active HI
			else if (Ch == 4) // If Channel 1 (RIGHTWHEEL)
				PWMPOL |= _S12_PPOL4; // Set Channel 4 to active HI
		break;

		case 0: // If Active LO desired
			if (Ch == 0) // If Channel 0 (LEFTWHEEL)
				PWMPOL &= ~_S12_PPOL0;  // Set Channel 0 to active LO
			else if (Ch == 1) // If Channel 1 (RIGHTWHEEL)
				PWMPOL &= ~_S12_PPOL1; // Set Channel 1 to active LO
			else if (Ch == 2) // If Channel 1 (RIGHTWHEEL)
				PWMPOL &= ~_S12_PPOL2; // Set Channel 2 to active LO
			else if (Ch == 3) // If Channel 1 (RIGHTWHEEL)
				PWMPOL &= ~_S12_PPOL3; // Set Channel 3 to active LO
			else if (Ch == 4) // If Channel 1 (RIGHTWHEEL)
				PWMPOL &= ~_S12_PPOL4; // Set Channel 4 to active LO
		break;

		default:
		break;
	}
}

// PWMOnOff: Takes 3 chars corresponding to channel 0,1,2.
// 			 A value of 0 disables the PWMsubsystem on that channel
//			 A value of 1 enables the PWMsubsystem on that channel
//			 Sets PWMsubsystem to an initial period
//			 Returns nothing.
void PWMOnOff(char Ch0, char Ch1, char Ch2, char Ch3, char Ch4){
	unsigned char i;

	// Turn off PWM and don't map any ports to PWM
	PWME = 0;
	MODRR = 0;

	// Set Polarity for each PWM channel (output initially high)
	PWMPOL = (_S12_PPOL0 | _S12_PPOL1 | _S12_PPOL2 | _S12_PPOL3| _S12_PPOL4);
	// Ser PWMPeriod to have 100 count resolution
	PWMPER0 = PWMPER1 = PWMPER2 = PWMPER3 = PWMPER4 = PWMS12_MAX_DC;

  // For each PWM channel
	   for ( i=0; i< MAX_CHANNELS; i++)
   {
        // Initialize the channel to 0% Duty Cycle
        (void)PWMDutyCycle(i, 0);
   }

	// If Ch0 is to be turned on
	if (Ch0 == 1){
		// Enable Map of PWM to PortT0
		MODRR |= _S12_MODRR0;
		// Enable PWM subsystem on Channel 0
		PWMCAE |= _S12_CAE0;
		PWME |= _S12_PWME0;
	}
	// If Ch0 is to be turned off
	else if (Ch0 == 0){
		// Disable Map of PWM to PortT0
		MODRR &= ~_S12_MODRR0;
		// Disable PWM subsystem on Channel 0
		PWME &= ~_S12_PWME0;
	}

	// If Ch1 is to be turned on
	if (Ch1 == 1){
		MODRR |= _S12_MODRR1;
		PWMCAE |= _S12_CAE1;
		PWME |= _S12_PWME1;
	}
	// If Ch1 is to be turned off
	else if (Ch1 == 0){
		MODRR &= ~_S12_MODRR1;
		PWME &= ~_S12_PWME1;
	}

	// If Ch2 is to be turned on
	if (Ch2 == 1){
		MODRR |= _S12_MODRR2;
		PWMCAE |= _S12_CAE2;
		PWME |= _S12_PWME2;
	}
	// If Ch2 is to be turned off
	else if (Ch2 == 0){
		MODRR &= ~_S12_MODRR2 ;
		PWME &= ~_S12_PWME2;
	}

	// If Ch3 is to be turned on
	if (Ch3 == 1){
		MODRR |= _S12_MODRR3;
		PWMCAE |= _S12_CAE3;
		PWME |= _S12_PWME3;
	}
	// If Ch3 is to be turned off
	else if (Ch3 == 0){
		MODRR &= ~_S12_MODRR3 ;
		PWME &= ~_S12_PWME3;
	}

	// If Ch4 is to be turned on
	if (Ch4 == 1){
		MODRR |= _S12_MODRR4;
		PWMCAE |= _S12_CAE4;
		PWME |= _S12_PWME4;
	}
	// If Ch4 is to be turned off
	else if (Ch2 == 0){
		MODRR &= ~_S12_MODRR4;
		PWME &= ~_S12_PWME4;
	}
}

#ifdef PWM_TEST
void main(){
	PWMOnOff(1,1,1,1,1);
	PWMFrequency(0,50);
	PWMFrequency(1,5000);
	PWMDutyCycle(10,0);
	PWMDutyCycle(25,1);
	PWMDutyCycle(50,2);
	PWMDutyCycle(75,3);
	PWMDutyCycle(95,4);

	while(1);
}
#endif // PWM_TEST