High-Resolution PWM Using CCP Compare And Timer Module

High-Resolution PWM Thumbnail



High-Resolution (16-Bit) PWM Using CCP Compare & Timer1 Module



In this article, I’d like to introduce you to such an amazing technique that you can implement on a small 8-Bit microcontroller, which will push the hardware CCP PWM module beyond its fundamental limits in terms of resolution. A 10-Bit PWM module can be enhanced using this technique to reach up to 16-Bit in resolution and that’s obviously insane, especially for control systems applications where high-precision is a necessity.

Without further ado, let’s get right into it!



The Mechanics of This Technique


High-Resolution PWM


Typical Configurations

First of all, we’ll be using a 16-Bit Timer module (Timer1) and a CCPx module in compare mode (CCP1).

Timer1 is configured to operate in timer mode, with specific frequency & Prescaler settings. Counting starts from 0 up to 65535. And timer interrupt on overflow should be also enabled! This will define the final PWM Frequency

CCP1 is configured to operate in compare mode, with CCPR1 register pair (16-bit) is preloaded with a value that will determine the final PWM Duty Cycle. The CCP compare mode should be configured to take the following action: drive LOW the CCP1 pin on compare match event.

PWM Signal Generation

1 Timer1 is constantly compared against the value in the CCPR1 register pair. When a match occurs, the CCPx pin will be driven LOW (by hardware).

2 When Timer1 reaches the overflow state (at 65535), in the ISR you should drive HIGH the CCP1 pin (by software).

Step1 -> defines the Duty Cycle of the resulting PWM signal

Step2 -> defines the frequency of the resulting PWM signal

Therefore, we’ll end up having a PWM signal with a 16-Bit of resolution to precisely control its duty cycle. And the possible output frequencies are as follows (using the formula below)

16bit PWM high resolution

This means: using Fosc=48MHz, the resulting possible Fpwm options are { 183.1Hz, 91.5Hz, 45.8Hz, 22.9Hz }. For different options of Prescaler values (1, 2, 4, or 8).

Obviously this isn’t very useful for almost any situation, even with a 16-Bit of resolution! we still need to somehow control the frequency of the PWM signal. Consider motor control applications for example (e.g. servo motors or so) which need specific frequency for the PWM signal (e.g. 50Hz or so). That’s what we’ll discuss in a moment.


How To Control The PWM Output Frequency?


Well, it turns out to be the timer preloading technique is a good fit for this situation. You can check out that article if you’ve not yet. And by preloading the Timer1 module it’ll start counting from a specific value (not zero) up to the 65535 maximum limit. This results in a flexible precise control over the output PWM frequency, which will, therefore, follow the equation down below.


High Resolution PWM 16bit Frequency Equation

Now, you can vary the Prescaler value and start counting from the value TMR1 instead of zero, to find yourself nearly able to get whatever output frequency value you want. Consider the examples down below:

ex1) using an Fosc=48MHz, what configurations would you set to get an output FPWM of 100Hz?

well, I’d try a high Prescaler at first. let prescaler=2 and solve the above equation in TMR1 value. Which will be 5536. That’s it! just preload the Timer1 module with this value and set Prescaler to 2 and you’ll get a 100Hz PWM output.

ex2) using an Fosc=48MHz, what configurations would you set to get an output FPWM of 2kHz?

well, I’d try a reasonably low Prescaler at first. let prescaler=1 and solve the above equation in TMR1 value. Which will be 59536. That’s it! just preload the Timer1 module with this value and set Prescaler to 1 and you’ll get a 2kHz PWM output.


Note that: by implementing timer preloading, you’ll end up having a controllable output frequency. But the Duty Cycle control rule will now change. Before preloading, it was a little bit more straight forward. You just had to write the value you want (from 0 up to 65535) in the CCPR1 register pair and that’s it. But now, the value to be written in the CCPR1 register must be in the range (from TMR1 up to 65535).

Note Also: a 100% or 0% duty cycle is not easily achievable by using this technique. But it’s not impossible to get as near as you can.


Implementation Steps For This Technique




1 Decide on the required Fpwm for your application and use the formula below to choose the convenient Prescaler option for you and work out the preload value as well.

2 Configure the CCPx pin to be an output pin.

3 Configure the CCPx module to operate in Compare mode with drive LOW to CCPx pin on compare match event.

4 Configure the Timer1 module (or any 16-bit timer) with the specific Prescaler and preload it with TMR1 value from the equation in step1. And also enable the timer overflow interrupt.

5 On each timer overflow interrupt, drive the CCPx pin High and preload the timer with the value you got in step1.

6 Change the duty cycle as you want by writing to the CCPRx register pair. For a 0% duty cycle, you should set it to the timer preload value (the initial counting value) and for a theoretical 100% duty cycle, you should set the CCPRx register pair to 65535. And any Duty Cycle (DC) ratio in between can be obtained by mapping!


PWM General Example – LAB


This quick LAB showcases the technique which we’ve discussed in this article. Here I’m using Fosc=48MHz, and I wanna get a 100Hz PWM output signal. So, by using the formula below & assuming prescaler=2, solve for TMR1 to get the preload value

High Resolution PWM 16bit Frequency Equation

100 = 48000000 / 4 x 2 x (65536-TMR1)

TMR1 = 5536 ( The Value To Be Preloaded in the Timer1 Register Pair)!

Therefore, the duty cycle full range starts from 5536 up to 65535. This means you’ve got 60000 levels of resolution for the duty cycle. Do you want to find out how many bits of resolution it equals to?

Well, resolution = log2(60000) = 15.87 bits (very close to 16-bits)

And here is the code for you to check out and you can also make your own adjustments to meet your application design requirements.


Code Listing


Results On Scope

The period is exactly 10ms and the frequency output is exactly 100Hz as what we’ve designed for!

HighResolution PWM Result


And the duty cycle sweep is very clean and extremely rich in the number of levels about 60000 of discrete DC levels. Here is an animation for it


HighResolution PWM Result Simulation


PWM DC Sweep From 50% Up to To 51% – LAB


This quick LAB is intended to highlight how accurate the duty cycle can be using the proposed technique in this article. In the main function, I’ll perform a duty cycle sweep from 50% DC up to 51% DC. And let you see how deep we can go & precisely set the duty cycle to whatever we want.

This may not be useful for some applications but for others, it’s just an amazing feature to have.


Code Listing


Results On Scope

Sweeping From 50% To 51% Duty Cycle. There are nearly 600 Discrete Levels Between Them!

DC Sweep For HighResolution PWM

Test On Real Target MCU & My DSO

“Using The Zoom Function”


Needed Components For The Above LABs


Qty. Component Name Buy On Amazon.com
1 PIC18F2550 Add
1 BreadBoard Add
1 LED Add    Add
1 Resistors Kit Add    Add
1 Capacitors Kit Add    Add
1 Jumper Wires Pack Add    Add
1 LM7805 Voltage Regulator (5v) Add
1 Crystal Oscillator Add
1 PICkit2 or 3 Programmer Add
2 9v Battery or DC Power Supply Add    Add    Add

My DSO Siglent-SDS1104 (on Amazon.com)

My Function Generator Fy-6900 (on Amazon.com)

*Affiliate Links Disclosure: links will make me a small commission at no additional cost to you*

The Prototyping Board Setup Tutorial


At the end of this article, I hope you’ve learned something new. And if you don’t have an application in mind that can benefit from this high-resolution PWM technique, then stay tuned for the Servo Motor Control Tutorial – PART3. As it’s going to be implemented using the proposed technique in this article to obtain a very precise servo control over the angular position.



If you like this article, hit the like button and please support this work by sharing it on social networks! That keeps me motivated for publishing more articles and tutorials. Cheers ^^


Share This Page With Your Network!
Join Our +25,000 Newsletter Subscribers!

Stay Updated With All New Content Releases. You Also Get Occasional FREE Coupon Codes For Courses & Other Stuff!

Photo of author
Khaled Magdy
Embedded systems engineer with several years of experience in embedded software and hardware design. I work as an embedded SW engineer in the Automotive & e-Mobility industry. However, I still do Hardware design and SW development for DSP, Control Systems, Robotics, AI/ML, and other fields I'm passionate about.
I love reading, writing, creating projects, and teaching. A reader by day and a writer by night, it's my lifestyle. I believe that the combination of brilliant minds, bold ideas, and a complete disregard for what is possible, can and will change the world! I will be there when it happens, will you?

2 thoughts on “High-Resolution PWM Using CCP Compare And Timer Module”

  1. Ben fatto e di facile comprensione…complimenti spiegato così si possono raggiungere ottimi livelli.
    Ciao e un grazie


Leave a Comment