The PIC10F322 has two PWM modules. The period or frequency of the PWM output is common to all PWM modules, whereas the duty cycle is independently controlled.

The two pins where the PWM output is present are:

A.0 PWM1
A.1 PWM2

Be sure to set the associated TRIS register and disable analog for the pins or you will not get an PWM output.

The PWM module produces up to a 10-bit resolution output depending on the period selection. Timer2 and PR2 set the period of the PWM. The PWMxDCL and PWMxDCH registers configure the duty cycle.

The PWM period is specified by the PR2 register of Timer2. When TMR2 is equal to PR2, the following three events occur on the next increment cycle:

TMR2 is cleared

The PWM output is active. (Exception: When the PWM duty cycle = 0%, the PWM output will remain inactive.)

The PWMxDCH and PWMxDCL register values are latched into the buffers

So, what do we need to do to get it going…

We are going to run at 16Mhz, and get a PWM Frequency of 15.7 kHz and a 10bits resolution on the duty.

PR2 = 0xFF;
T2CONbits.T2CKPS = 0b00;
T2CONbits.TMR2ON = 0x01;
PWM1CONbits.PWM1OE = 0x01; //PWM1
PWM1CONbits.PWM1EN = 0x01; //PWM1

Here is the code below – I will cover the calculations of the PWM resolution in another article.

One thing to note is the function set_dutycycle – it takes pointers to the duty registers and does the necessary bit shifting to get the value in the right places. Because PWMxDCH take the 8 MSbs and PWMxDCL takes the 2 LSbs.

Using the 10bit resolution a valid range is 0 – 1023. Where 512 will be 50%. TO convert percent to value, lets say we want 33%. 1023 x .33 = 337.59 round it to 338. 338 would be the value we enter for the duty.

Here is the logic analyzer on the 33% duty cycle.

 

PIC10F322- XC8 Code - PWM Functions

and now for the code…..

#include <xc.h>
#include <stdint.h>

//Device Configuration
#pragma config FOSC = INTOSC  // Oscillator Selection 
#pragma config BOREN = ON    // Brown-out Reset
#pragma config WDTE = OFF    // Watchdog Timer
#pragma config PWRTE = ON    // Power-up Timer
#pragma config MCLRE = OFF   // MCLR Pin Function Select bit->MCLR pin function is digital input, MCLR internally tied to VDD
#pragma config CP = OFF      // Code Protection 
#pragma config LVP = OFF     // Low-Voltage Programming 
#pragma config LPBOR = ON    // Brown-out Reset Selection bits
#pragma config BORV = LO    // Brown-out Reset Voltage Selection
#pragma config WRT = OFF    // Flash Memory Self-Write Protection

//Used to calculate the delay time - Change depending on processor Speed
#define _XTAL_FREQ 16000000  //16Mhz



//Prototypes
void setup(void);
void set_dutycycle(volatile uint8_t* pDutyCycleHigh, volatile uint8_t* pDutyCycleLow, uint16_t dutyValue);


void main(void)
{
    setup();
    
    while(1)
    {
     
    }
}

void setup(void)
{    
    //Set the System Clock - You can change this to match the setting you are looking for
    OSCCONbits.IRCF = 0b111;  //Set System Clock to 16Mhz
    
    //disable analog - Set A.0 and A.1 as output 
    ANSELAbits.ANSA0 = 0;
    ANSELAbits.ANSA1 = 0;
    TRISAbits.TRISA0 = 0; 
    TRISAbits.TRISA1 = 0;

    
    //Clear out the duty cycle registers
    set_dutycycle(&PWM1DCH,&PWM1DCL,0);
    set_dutycycle(&PWM2DCH,&PWM2DCL,0);
    
    PR2 = 0xFF;   
    T2CONbits.T2CKPS = 0b00;
    T2CONbits.TMR2ON = 0x01;
    PWM1CONbits.PWM1OE = 0x01;   //PWM1 Turn on 
    PWM1CONbits.PWM1EN = 0x01;  //PWM1 Enable Output

    PWM2CONbits.PWM2OE = 0x01;   //PWM2 Turn on 
    PWM2CONbits.PWM2EN = 0x01;  //PWM2 Enable Output


    set_dutycycle(&PWM1DCH,&PWM1DCL,338); //This will be 33%
    set_dutycycle(&PWM2DCH,&PWM2DCL,1023); //This will be 100%
    
}


void set_dutycycle(volatile uint8_t* pDutyCycleHigh, volatile uint8_t* pDutyCycleLow, uint16_t dutyValue)
/*Sets the duty value of the supplied registers. 
 *Usage set_dutycycle(&PWM_duty_register_high,&PWM_duty_register_low, duty_value)
 *Valid range for 10bit resolution is 0 - 1023
 *TO convert percent to value, lets say we want 33%.  1023 x .33 = 337.59  round it to 338.
 *338 would be the value we enter for the duty.  */
{
    *pDutyCycleLow =  (uint8_t)((dutyValue & 0b11) << 0x06);  //Get the LSB
    *pDutyCycleHigh = (uint8_t)(dutyValue >> 0x02);    //Get the MSB
}