Select Page

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:

PORT A0 PWM1
PORT A1 PWM2

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

ANSELAbits.ANSA0 = 0; //Disable Analog for port A0
ANSELAbits.ANSA1 = 0; //Disable Analog for port A1
TRISAbits.TRISA0 = 0; //Set Port A0 Output
TRISAbits.TRISA1 = 0; //Set Port A1 Output

Notes About PWM Terms


You will hear the following terms mentioned. Period and Duty Cycle.

Period is total length of the PWM signal. Also referred to as the frequency. It is how often the cycle repeats in a given amount of time.

Duty cycle is the amount of time a digital signal is in the “active” state relative to the period of the signal. Duty cycle is usually given as a percentage.

They work like this – during the start of the period – the output signal will be high, for a certain amount of time. The time that the output is high in relationship to the period gives the duty cycle. Lets just say we have a 1 second period – the output is on for 500ms – this will give a 50% duty cycle. When the time reaches the end of the period, we start again.

About The PIC10F322 PWM Module


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. Where x is – is the PWM module number. The PIC10F322 has two.

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

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% Duty Cycle. 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.

The Code

/*
 * File:  pwm_example.c
 * Author: Jamie Starling - GizoFoundry.com 
 *
 * Created on:  September 8, 2021, 7:45 AM
 * 
 * Code/Circuit provided as-is.
 */

#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%
    
    //100% example
    //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
}

Have a technology project in mind? Let’s talk!

I am currently available for hire

Need a custom technology solution for your business? Please send me an email (jamie@jamiestarling.com) to discuss your project needs.

I can help you get started on the road of building a simple and well-tested working solution, together, with your team.