Pulse Width Modulation (PWM) using the CCS Compiler

Pulse Width Modulation (PWM) signals in the embedded world are used all the time for a variety of reasons. One use is to communicate. For example, on most Ford cars and trucks the regulator communicates status of the alternator field coil back to the engine control module. If the duty cycle of the the PWM is between 10-90% it means that the alternator is working just fine and if it gets into the 0-10% or 90-100% there’s a problem. You could also control the power output of your headlights with PWM. This is how most daytime running lights are done. Some still switch into a resistor to knock down the power, but in todays world that’s just not efficient enough. We can also control the “theatre lighting” of our vehicle’s interior lights. Using PWM we can gradually brighten or darken the cars interior or we can take tricolor LEDs and change the color of our interior.

So how do we get started using PWM’s on our PICs? Well, like most things we want to do we need to look at the datasheet of the device we want to work with. If you have hardware PWM as one of the features on your device, you’re already ahead of the game. If you don’t, you may find yourself doing a little programming to create a PWM. Not a huge deal but keep in mind the code will have to periodically service your PWM which takes it away from doing other things you might rather be doing. Hardware PWM is more of a set it and forget it (until you want to change the duty cycle or turn it off) kind of thing. I’m all for simplicity so I recommend selecting a device with a hardware PWM.

I’m currently working with the PIC16F777. It’s a 40 pin PDIP that has 2 hardware PWM’s on board. These are part of the Capture/Compare/PWM module. Each one of these contains a 16 bit register which can operate as a :

* 16-bit Capture register
* 16-bit Compare register
* PWM Master/Slave Duty Cycle register

I’m going to try not to duplicate the datasheet because you can read that along with this article. What I’d like to do is communicate what the datasheet means in regards to making this happen in the CCS compiler.


Take a look at the above signal that is shown in the datasheet. This shows us much of what we need to know. The PWM signal uses TMR2 (Timer2) to clock the Period of the signal as well as the duty cycle. Now one thing that is a little confusing about the graph is the Duty Cycle. I believe it should be labled more like “Time High” rather than Duty Cycle. I think of the Duty Cycle as a percentage of time high vs. the period of the signal. So for example if the perod measured in seconds is 100ms (or .1s) and the time high 10ms (or .01s) the duty cycle is 10%.

Let’s jump into CCS mode here and talk about what we need to do. First of all, we need to let the PIC know that we intend to use the CCP modules as PWM. In CCS that looks like this…

setup_ccp1(CCP_PWM); //Config CCP1 as a PWM

Pretty simple right? The next thing we need to do is set up the timer2. This requires a little bit of math. In our example we want to generate a PWM that has a 100Hz frequency. Using the formula: Period (T) = 1/frequency, the Period will be .01 seconds long. I’d also like the time high to be adjustable from 0s to .01s which is essentially 0% to 100% duty cycle. The math is going to get a little heavy for a moment but hang in there. I’ve attached a spreadsheet which does much of this stuff for you.

OK, to set up the timer2 we need to know what speed we want the micro to run. The PIC16F777 can be set for a variety of clock speeds. We’re going to pick 1Mhz for this little project. I could go up to 8Mhz but we just don’t need that kind of speed for what we’re doing. So before we go into our Main routine we’re going to direct the the compiler to set then device to a PIC16F777, use the internal clock, turn off the watchdog timer, and leave our code unprotected. More about the watchdog and code protection as the focus in another article. Here’s the code that does this…

#include <16F777.h>
#use delay(clock=1000000)

These lines will be at the very beginning of your program. Don’t worry, because we’re going to put them all together at the end.

So back to the timer stuff. The CCS compiler guide tells us that for in order to setup the timer2 we need to understand a little bit more about it. The timer2 is 8-bit on pic16s and pic18s. It counts up and will provide an interupt when it overflows. This interupt is important to us even though we won’t have to service it. When we use CCS’s setup_timer_2 function, the compiler will take care of it for us. This function is in the form of..

setup_timer_2(mode, period,postscale)

where we provide the mode, period, and postscale we need to get what we want out of the PWM. For our requirement it looks like this…

setup_timer_2(T2_DIV_BY_16, 156, 1)

T2_DIV_BY_16 is the prescale of the clock. We could use another value here like T2_DIV_BY_8 but it would change our period and we would have to calculate another value for it because our PWM frequency would now be twice what we were designing for. 156 is the period (or cycle time) in clock cycles. Timer2 will start, make the output high, and after 156 clock cycles it will generate an interupt and raise the pin again from whatever state it’s in. This is all that we need for the timer2 because the postscale can stay at 1.

Now, on to the final CCS command which sets the time we want the PWM to be high within the period. It takes the form…


where value is the number of clock cycles we want the clock to keep the pin high. This one is pretty straight forward.

Now, putting it all together.


#include <16F777.h>
#use delay(clock=1000000)

void main(void)
unsigned int pwm1
setup_timer_2(T2_DIV_BY_16, 156, 1);
while( TRUE ) {
pwm1=80; //duty cycle

You should be able to recognize all the pieces of the above snippet and have a fully running PWM on CCP1 which is pin 17. You change the pwm1 variable to any number between 0 and 156 to change the duty cycle of your pwm signal. At the current value of 80 it’s pretty close to 50% duty.

OK, here’s the spreadsheet to help calculate these values. Keep in mind it’s not the greatest in that you have to play around with the values a bit in order to get the output you want. I didn’t set it up so you can just enter the Cycle Time and frequency and it gives you the other parameters. It works the other way where you start with a decent guess as far as the clock and prescaler goes and tweak until you get the output you want. It definately can use some improvement but it works.


5 Responses to “Pulse Width Modulation (PWM) using the CCS Compiler”

  • […] and play porject required urjent !!!! Assuming CSS is really CCS, take a look at this link: Pulse Width Modulation (PWM) using the CCS Compiler | It's all about PICs I do not have CCS so I can't give you guidance on the syntax but it looks like all you have to do […]

  • jeevan kumar:

    This is a simple and good example of generating pwm on single.

    PIC 16f777 has 3 pwm channels. Could please explain how to generate 3 pwms
    with 50Khz each at phase angle 0,120,240 degrees in ccsc code? (i.e three phase pwm)

    If possible explain how to generate three phase sine pwm in ccs c

  • AZmat:

    Hi. any idea how to setup pwm in half bridge mode using ccs. I am having difficulty.

    • Rich:

      Sorry I’m a late getting to your question. Turns out I can’t help too much anyway. I don’t do much in the way of motor control. It’s on my “to do” list to start playing with some motor projects but I haven’t gotten there yet. I found this code on the CCS forum that may help though. Check this out…

      #use delay(clock=20000000)

      void main(void) {
      unsigned int pwm1;
      setup_ccp1(CCP_PWM_HALF_BRIDGE | CCP_PWM_H_H);
      setup_timer_2(T2_DIV_BY_16, 249, 1);
      pwm1=120; //duty cycle
      do {
      } while(TRUE); //This avoids the compiler error message

  • […] http://www.8051projects.net/pulse-width-modulation/ this link is very useful to u….. http://rawmicro.com/tutorials/pulse-…-ccs-compiler/ this link is useful for pic16f877a pwm motor control….. Thanks and Regards, MANIKANDANSHINE […]