PWM - Pulse Width Modulation

The PWM is able to generate pulses on 4 channels independantly. The period, duty-cycle, polarity and dead-time can be configured. The channels are double buffered to prevent an unexpected wave form.

Setup

Clock - The peripheral clock needs to be enabled in the PMC. PWM uses the main clock as its clock source. Clock division can be set in the REG_PWM_CLK register. *note* This clock divisor is different than the clock divisor found in REG_PWM_CMR#. REG_PWM_CLK is the clock for the peripheral and REG_PWM_CMR# is the clock for the channel. ie: REG_PWM_CMR0 is the clock for channel 0.

As with any peripheral, we must take control of the pins from GPIO and give it to the peripheral.

For the SG90 Micro Servo, it expects a 20ms period where 0 degrees = 1ms duty and 180 = 2ms and the dead time is 5us.

Channel - Enable the channel you want to output a signal on in the REG_PWM_ENA register. To disable, set the appropriate bit in REG_PWM_DIS. The status register bits, REG_PWM_SR, are set if the channel is enabled and 0 if disabled.

Period - Set the period of the signal in REG_PWM_CPRD#, where # is the channel number you wish to update. ie: channel 0 is REG_PWM_CPRD0. The period can be set using REG_PWM_CPRDUPD#. This register acts as a double buffer to the REG_PWM_CPRD# value. Using the formula (X * CPRDUPD / fperipheral clock) we can calculate the period that want.
example:
period = (X * CPRD)/Fperipheral clock: where period is our desired time in seconds, X is MCK_DIV_X that is in REG_PWM_CMR#, Fperipheral is REG_PWM_CLK.
REG_PWM_CPRD# can be solved for:
8*CPRD/20M = 0.02s period --> CPRD = 50,000

Duty Cycle - The value in the duty cycle register is between 0 and the CPRD value for the channel. The same formula used before can be used to calculate this value.
8*CDTY/20M = 0.001s --> CDTY = 2500

Dead Time - The dead time register can be calculated using the following:
20,000,000/8*5us = 12.5~13 REG_PWM_DT0 = 13

Polarity - The SG90 servo wants high first, so we will set the bit CPOL in CMR0 Register: REG_PWM_CMR0 |= PWM_CMR_CPOL

Example


#include "sam.h"

void clock_init(){
	REG_CKGR_MOR |= CKGR_MOR_KEY_PASSWD | CKGR_MOR_MOSCXTEN| CKGR_MOR_MOSCXTST(255);; //enable external crystal, set settle wait time
	while (!(REG_PMC_SR & PMC_SR_MOSCXTS)); //wait for crystal to become ready
	REG_CKGR_MOR |= CKGR_MOR_KEY_PASSWD | CKGR_MOR_MOSCSEL; //select crystal
	REG_PMC_MCKR |= PMC_MCKR_CSS_MAIN_CLK; //master clock source selection - choose main clock
	while (!(REG_PMC_SR & PMC_SR_MCKRDY)); //wait until main clock ready
	REG_PMC_MCKR |= PMC_MCKR_PRES_CLK_1; //select processer prescaler (0 - no divisor)
	while (!(REG_PMC_SR & PMC_SR_MCKRDY)); //wait until main clock ready
}

void init_pwm(){
	//enable clock for pwm
	REG_PMC_PCER0 |= PMC_PCER0_PID31;
	
	//disable pio and give to peripheral
	REG_PIOA_PDR |= PIO_PDR_P0;
	REG_PIOA_PDR |= PIO_PDR_P19; //PWML0
	//assign secondary fucntion to P19
	REG_PIOB_ABCDSR |= 1<<19;
	//select the clock to run at (4mhz)
	//REG_PWM_CLK |= PWM_CLK_DIVA(1);
	//REG_PWM_CLK |= PWM_CLK_PREA_CLK_DIV16;
	
	//enable channel0
	REG_PWM_ENA |= PWM_ENA_CHID0;
	
	//channel mode
	//REG_PWM_CMR0 |= PWM_CMR_CPRE_CLKA;
	REG_PWM_CMR0 |= PWM_CMR_CPRE_MCK_DIV_8;
		
	//select the period
	REG_PWM_CPRD0 = 50000;
	/*
	period = (X * CPRD)/Fperipheral clock: where X is MCK_DIV_X
	8*50k/20M = 20ms period
	*/	
	
	//select the duty cycle
	REG_PWM_CDTY0 = 2500; //0 gt dutycycle gtCPRD
	/*
	servo expects 20ms period
	duty cycle
	0 degrees = 1ms = 2500
	180 degrees = 2ms = 5000
	sg90 server dead time = 5us
	deadtime register 20,000,000/8*5us = 12.5~13
	*/
	REG_PWM_DT0 = 13;
	
	//set polarity
	REG_PWM_CMR0 |= PWM_CMR_CPOL; //server wants high first then low
		
}

void pwm_Update(uint16_t pwmPeriod, uint16_t pwmDutyCycle){
	REG_PWM_CPRDUPD0 |= PWM_CPRDUPD_CPRDUPD(1);
	REG_PWM_SCUC |= PWM_SCUC_UPDULOCK;
	return;
}

int main(void)
{
    /* Initialize the SAM system */
    SystemInit();
	clock_init();
	init_pwm();
	//pwm_Update(1000,500);
    /* Replace with your application code */
    while (1) 
    {
		
    }
}