/***
 *** The example has no copyright and can be used by anyone.
 *** The following example is based on Device File Package
 *** required to compile the macro definitions used.
 *** The Device File Package is available by downloading Atmel Studio 7.
 ***/

/*** 
 *** In this example,  The Timer Counter is used to generate PWM signals
 *** depending on condition set by the frequency period and duty cycle 
 *** (freq, period, duty)          
 ***/
void InitPWM(uint16_t freq, uint16_t period, uint16_t duty) 
{
 /*** 
  *** Enable GCLK2 with XOSC32K 
  *** as clock source for TC0 for ADC 1KHz Triggering
  ***/
 GCLK->GENCTRL[2].reg = GCLK_GENCTRL_DIV(1) | CURRENT_32KOSC |GCLK_GENCTRL_GENEN;
 /*** (write synchronized) ***/
 while((GCLK->SYNCBUSY.reg & GCLK_SYNCBUSY_GENCTRL2));
 GCLK->PCHCTRL[GCLK_TC0].reg = (GCLK_PCHCTRL_CHEN|GCLK_PCHCTRL_GEN_GCLK2);

 /*** 
  *** Configure PORTS PA19 in TC2 WO[1] Peripheral E
  *** for waveform generation checking
  ***/
 PORT->Group[0].WRCONFIG.reg = (uint32_t)(PORT_WRCONFIG_HWSEL|
                                          PORT_WRCONFIG_WRPINCFG|
                                          PORT_WRCONFIG_WRPMUX|
                                          PORT_WRCONFIG_PINMASK(1<<3)|
                                          PORT_WRCONFIG_PMUXEN|
                                          PORT_WRCONFIG_PMUX(4));

 /*** Enable TC2 GCLK with GCLK2 as source (XOSC32K) ***/
 GCLK->PCHCTRL[GCLK_TC2].reg = (GCLK_PCHCTRL_CHEN|GCLK_PCHCTRL_GEN_GCLK2);

 /*** Disable TC2 (write synchronized) ***/
 TC2->COUNT16.CTRLA.bit.ENABLE = 0;
 /*** (write synchronized) ***/
 while(TC2->COUNT16.SYNCBUSY.reg & TC_SYNCBUSY_ENABLE);

     /***
   ***  TC2 in 16-bit counter with GLCK for synchronization 
   *** and a DIV16 to get 1KHz event generation for the ADC
   ***/
     TC2->COUNT16.CTRLA.reg = (TC_CTRLA_MODE(TC_CTRLA_MODE_COUNT16_Val)|
                            TC_CTRLA_RUNSTDBY|
                            TC_CTRLA_ONDEMAND);    

 /*** 
  *** TC2 Wave generator is configured as 
  *** Match Pulse Width Modulation with 
  *** a period to specify
  ***/
 TC2->COUNT16.WAVE.reg = (TC_WAVE_WAVEGEN_MPWM);
 TC2->COUNT16.CC[0].reg =    period;
 /*** (write synchronized) ***/
 while(TC2->COUNT16.SYNCBUSY.reg & TC_SYNCBUSY_CC0); 

 /*** The duty cycle is updated using CC1 register ***/
 TC2->COUNT16.CC[1].reg = duty;
 /*** (write synchronized) ***/
 while(TC2->COUNT16.SYNCBUSY.reg & TC_SYNCBUSY_CC1);

 /*** enable interrupt 
  *** for every period of the PWM signal
  *** This also allows to update the duty cycle
  ***/
 TC2->COUNT16.INTENSET.bit.MC0 = 1;

 /*** Enable resready TC2 interrupt (ID 36) at core level ***/
 NVIC_EnableIRQ(TC2_IRQn);
 NVIC_SetPriority(TC2_IRQn,1);  

 Enable TC2 (write synchronized)
 TC2->COUNT16.CTRLA.bit.ENABLE = 1;
 while(TC2->COUNT16.SYNCBUSY.reg & TC_SYNCBUSY_ENABLE);

}
/***
 *** Return the current duty cycle (# of clocks defining hi/low)
 ***/
uint16_t GetPWMDuty(void) 
{
 uint32_t duty_cycle;
 /*** The duty cycle is updated using CC register ***/
    duty_cycle    = TC2->COUNT16.CC[1].reg;    //duty read        
 return (uint16_t)duty_cycle;
}

/***
 *** Set the current duty cycle (# of clocks defining hi/low)
 ***/
void SetPWMDutyCycle(uint16_t duty) 
{
 /*** The duty cycle is updated using CC register ***/
 TC2->COUNT16.CC[1].reg    =    duty;
 /*** (write synchronized) ***/
 while(TC2->COUNT16.SYNCBUSY.reg & TC_SYNCBUSY_CC1);
}

/***
 *** Turn off the PWM
 ***/
void PWMOff(void) 
{
 /*** Disable TC2 ***/
    TC2->COUNT16.CTRLA.bit.ENABLE = 0;
 /*** (write synchronized) ***/
    while(TC2->COUNT16.SYNCBUSY.reg & TC_SYNCBUSY_ENABLE);

 /*** Configure PORTA for low power ***/
 PORT->Group[0].WRCONFIG.reg = (uint32_t)(PORT_WRCONFIG_HWSEL|
                                          PORT_WRCONFIG_WRPINCFG|
                                          PORT_WRCONFIG_WRPMUX|
                                          PORT_WRCONFIG_PINMASK(1<<3)|
                                          PORT_WRCONFIG_PMUX(0));

}

/***
 *** update the duty cycle in between pulses is done by calling this
 *** interrupt, like this example.
 ***/
void TC2_Handler(void)
{
 /*** clear TC2 MC1 Interrupt FLAG register ***/
 TC2->COUNT16.INTFLAG.reg |=   TC_INTFLAG_MC0;
 updatePWMDutyCycle();
}