/*** *** 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(); }