Posts Tagged ‘CCS’

After writing about the PIC hardware PWM module it occurred to me that a little more information about the onboard timers would be in order.  Just about every PIC has a timer and many have several.  The PIC16F777 that we I used in the hardare PWM article has three of them called TMR0, TMR1, TM2

The three timers are similar to each other in many ways but are different in some respects.  Below is a list of each of the timer features from the datasheet.

TRM0

  • 8-bit timer/counter
  • Readable and writable
  • 8-bit software programmable prescaler
  • Internal or external clock select
  • Interrupt on overflow from FFh to 00h
  • Edge select for external clock

TMR1

  • 16-bit timer/counter which consists of two 8-bit registers
  • Readable and writable (TMR1H:TMR1L)
  • 4-bit software programmable prescaler
  • Internal or external clock select

 

TMR2

  • 8-bit timer/counter
  • Readable and writable
  • 4-bit software programmable prescalerInternal or external clock select
  • Prescale options include 1:1, 1:4, 1:16
  • Postcale options include 1:1, 1:2, 1:3, 1:16
  • Internal clock only
  • Output (before prescaler) is fed to the SSP module for an optional shift clock.
  • Can be set to automatically compare it to a value you determine.

Each of the timers can be configured separately and can be used for different purposes within your application.  One of the nice things about these is that they run on their own in the background while your code is off doing something else.  They “tap you on the shoulder” in the form of an interrupt to remind you to go and do something.  You can also read them or reset them at any time.  These timers are essentially binary counters that start at a value you determine and keep running until you shut them off.  If you’re not familiar with a binary counter it looks much like this…

clock pulse 1  00000000
clock pulse 2  00000001
clock pulse 3  00000010
clock pulse 4  00000011
clock pulse 5  00000100
clock pulse 6  00000101

and so on

The counter keeps increasing after every clock pulse until it “overflows”.  Overflows means that when the counter is all 1′s and another 1 is added, there is no place for the carry bit to go.  This generates and interrupt and the timer is reset to all 0′s again.

Now lets write a little routine that uses one of our timers to blink an LED at 1 second intervals.  We’re going to use the CCS compiler for this example and once again use the PIC16F777.  We’ll use TMR1 for this task and take I’ll take a moment to point out a couple of things before we get to coding.

- TMR1 counts from 0 to 65535

- On the next count it resets back to zero and sets a bit in the TMR1IF (Timer 1 Interupt Flag) on the PIR1 register.

- If we run the internal clock at 20Mhz we know that the program counter and the pulses that drive the clock are equal to 1/4 of the internal clock speed of each clock count.  That means that the TMR1 clock runs a 20Mhz/4=5Mhz.

- A little more math and we find that each clock pulse take 1/5Mhz seconds to comple.  That’s .2us for each count of TMR1

- Another piece we need is how long until the timer overflows.  From what we know so far it’s .2us x 65536 = 0.0131

- Since the last piece resulted in such a short time until overflow, we could probably use a prescaler to slow it down a bit. Using the 1:8 prescale which divides our clock we get 5Mz/8 = 625KMz which is 1.6us per count of TMR1.  With that we have 1.6us x 65536 = 0.104856s wich is how long it takes to overflow the clock.

Now, with that information we know that if it takes 0.104856s to overflow the clock.  If we let the clock overflow 10 times we will have taken up 1.04856s.  Well that’s pretty close to what we wanted but lets say we had to get that close to 1s.  What could we do?  Well, remember that TMR one is writable.  That means we can preload a value into TMR to get us a little close.  I’m not going to show all the math on this part but if you solve this equation …

clock cycles = (0.1s)/((20Mhz)(1/4)(1/8)) = 62500

You’ll see that if we could overflow the clock after 62500 cycles rather than 65536 we would have consumed exactly .10 seconds.  So the value we need to preload in is (65335-62500) = 3035.

The code that puts it all together… 

#include <16F777.h>
#fuses INTRC,NOWDT,NOPROTECT
#use delay(clock=2000000)
unsigned int counter = 0;
setup_port_b (ALL_DIGITAL);
#int_timer1

void timer1_isr() {
disable_interrupts(INT_TIMER1); //Turn off interrupts
set_timer1(3035);                                 //Load preset
counter = counter +1;                           //Increment Counter
clear_interrupt(INT_TIMER1);
enable_interrupts(INT_TIMER1);  //Enable the TMR1 Interrupt
}

void main(void){
 SET_TRIS_B( 0×00 );                       //Set Port B to all Outputs
 setup_timer_1(T1_INTERNAL|T1_DIV_BY_8);  //Timer with internal clock and 1:8 prescaler
 enable_interrupts(GLOBAL);
 enable_interrupts(INT_TIMER1);

test: 
if (counter == 10){
 counter = 0;                                              //Reset the counter
 OUTPUT_TOGGLE(PIN_B1);
}

goto test; 

We start out telling the CCS compiler which device we’re using and set some of the config bits.  We also set port B to all outputs, initalize the variable COUNTER, and declare out timer1 interrupt.  The main part of the program sets up the timer with the internal clock and the prescaler we talked about.  The interrupt is enabled and then we begin the testing of the variable COUNTER.  Remember that the TMR1 clock is running all the time now that we’ve set it up.  The test loop checks to see if we’ve overflowed it 10 times.  We know how many times it overflowed because we’ve set up an interrupt routine which increments COUNTER each time if overflows.  When COUNTER reaches ten, we toggle B1.  So with an LED and appropriately sized resister on B1 we would see the LED light up for 1s and then turn off for 1s.

In CCS there are simpler ways of accomplishing this task of lighting the LED for 1s on and 1s off.  We could use the delay function build into CCS to simplify our code.  We only did it this way to demonstrate how the TMR1 operates. Hopefully I’ve shed a little light into the world of PIC timers using CCS and you can feel comfortable using them in your designs.

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.

 

pwm

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>
#fuses INTRC,NOWDT,NOPROTECT
#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…

set_pwm1_duty=(value)

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>
#fuses INTRC,NOWDT,NOPROTECT
#use delay(clock=1000000)

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

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.

mplab-sim-screenshot