Become a subscriber (Free)

Join 29,000 other subscribers to receive subscriber sale discounts and other free resources.
:
:
Don't worry -- your e-mail address is totally secure. I promise to use it only to send you MicroZine.

PIC PWM Interrupt

How to generate a PIC PWM using an interrupt.

Some PIC Microcontrollers have built in PWM internal peripherals that are easy to set up and use and once initialized will continue running on their own.  All you do is send data to the duty cycle register to change the pulse width.

Sometimes you need a Pulse Width Modulation signal because either the device does not have an internal PWM peripheral or you need an extra one.  

This page shows you how to add a software controlled interrupt driven PWM signal.  

There are two methods presented - the first uses one timer and does not have as good performance as the second.  The second uses two timers but has extremely good performance.  Which one you use depends on

  1. How many free timers you have left.
  2. How much  performance you need (this uses more modules i.e. more timers)..

Single Timer method : PWM PIC Interrupt driven timer

The key to creating a PIC PWM is to use an interrupt from one of the timers which is used as the resolution timer for the PWM period.  In the example below Timer 0 is used as the resolution timer and has a frequency of 33kHz or a period of 30us.

Note:This is not the PWM frequency.  It is just the period of one step of the pwm.

Although 20 steps does not sound a lot if you use it as a light dimmer its more than enough.  It depends on the application you are using.  The final output has a frequency of:

   PIC PWM Interrupt: Frequency = (Timer Frequency/No. steps)

so here Frequency = 1.65kHz

Single Timer method : PIC PWM Interrupt - Timer initialization

This example is from a 16F88 running an internal oscillator at 8MHz.

void init_timer (void) {

   // Timer 0 initialize  
   OPTION_REG = 0x00 | (1<<7);// | (1<<3);

   // Timer 0 interrupt enable
   // Osc off, Internal clk, On, zero T0IF,
   // Enable global interrupts
   INTCON = (1<<T0IE) | (1<<GIE);
}

Single Timer method : PIC PWM Interrupt

Here is the code for the PIC PWM interrupt:

void interrupt(void) {

   ///////////////////////////////////////////////
   // Timer 0
   if (INTCON & (1<<T0IF) ) { // T0 overflowed ?
      INTCON &= ~(1<<T0IF);   // clear timer0 overflow bit.

      TMR0 = 228;  // 30us

      time++;

      do_pwm();

   }   
}

Single Timer method : PWM PIC Interrupt - pwm code

This code is called from the interrupt to do the PWM action.

Depending on the value set in pwm_val the output will be turned on for a set number of periods and when the period couinter

void do_pwm(void) {


   if (time>20) time=0;

   if (time<pwm_val)
      setBit(PORTB,SOFT_PWM_PORTB_PIN);
   else
      resBit(PORTB,SOFT_PWM_PORTB_PIN);

}

Description Single interrupt timed PWM

With this method a timer is operated in quite a fast mode and this means the ISR (Interrupt Service Routine) has to be very small. Since interrupts are firing fast the main code can also not be very big.

With this method you will not be able to get a lot of processing done at all.

There are three functions involved:

  1. init_timer() - As it sounds initialises the hardware timer.
  2. interrupt() - Updates the global variable 'timer' at Timer 0 intervals.
  3. do_pwm() - Where the PWM signal is generated.

The first two routines setup and control the hardware timer to generate an approximate 30us update time.

Note: Interrupts are never absolutely accurate but are very close and good enough.

The last rountine creates the PWM signal output and limits the timervalue for restart.

Note: do_pwm() is called from within interrupt() - you could put it all within the interrupt routine as it is part of the interrupt routine. Sepoarating it out is a matter of choice i.e. code readability but remember to keep it short and sweet as it is still "interrupt" code.

When you put everything in an interrupt code - the whole thing can get huge as in big code systems you have to do alot of different interrupts to service.

Performance

There is a trade off between how much the microcontroller is asked to do outside the interrupt and the size of the interrupt (including called subroutines).  As you must allow enough time between interrupts for the processor to do its 'main' job.

In this example if a 16F88 is used with a 8MHz internal oscillator that means 0.5us per instruction so there is a maximum of 30/0.5 = 60 instructions that can be executed before another interrupt fires (and that includes the ISR code as well..

You probably have about 40 instructions before another interrupt comes along since you have to execute the ISR instructions and the code that saves registers and restores them before and after the interrupt (~20 instructions?). You can find out exactly by examining the code assembly output.

Solutions to this problem are:

  • Increase the main clock (adding an external xtal of 20MHz allows many more intsructions but you lose 2 pins).
  • Use a different processor with a higher internal clock.
  • Use a different method that uses more timers (see below).

The method below shows use of two timers which reduces the processing burden.

Double Timer method : PIC PWM Interrupt driven timer

If you do not have an internal PWM module then this method can give an accurate (high resolution) output without using up too much processor time.  

It works by using the first timer as the PWM frequency generator. This then loads up the second timer with a time that is the period of the required pulse width.

It does this by calculating the number of timer clock cycles that result in the required period (which you set from within the program elsewhere).

A timer usually generates an interrupt when it reaches zero (having passed through the highest value) so all you do is work out the period in timer clock cycles (n) and subtract this value from the largest value that the timer can have (max-n).  So when the timer starts counting up from this value it rolls over to zero having counted n clock cycles.

When the first timer interrupts you set the PWM output high and when
the second timer interrupts you set the PWM output low.  This creates a high resolution PWM PIC Interrupt driven signal without consuming processing power.

Double Timer method : PIC PWM Interrupt

Here's the code for the PIC PWM Interrupt generator that implements the high resolution method using Timer 0 as the frequency generator (here 1/18ms) and Timer 1 as the period timer (resolution Fosc/4).  

Note: For different resolutions change the Timer 1 prescalers etc.

This example is from an 12F675 running at 4MHz (internal).

void interrupt(void) {
unsigned int val;

   ///////////////////////////////////////////////
   // Timer 0
   if (INTCON & (1<<T0IF) ) { // T0 overflowed ?
      INTCON &= ~(1<<T0IF);   // clear timer0 overflow bit.

      // Fosc/4 x (Prescale) x (count to overflow) = repeat rate.
      // 1us    x 128        x 140                 = 18ms repeat rate.
      TMR0 = 256-143;  // need 141 but looses TMR0 looses 2 so use 142

      time++; // Count 18ms periods for general use.

      // Now set up timer1 as a 1 shot timer
      // Set overflow for Timer 1
      val = 65535-servoVal;

      // Use these vars for reporting debug
      st_TMR1L =  val & 0x00ff;
      st_TMR1H = (val & 0xff00)>>8;

      TMR1H = st_TMR1H; // set high 1st so does not overflow immediately
      TMR1L = st_TMR1L;

      st_val = val;

      // Enable Timer 1.- every Timer 0 interrupt or 18ms = 1 shot.
      PIE1   |= (1<<TMR1IE);

      GPIO  |= (1<<SERVO_BIT);   // Set Servo on
   }

   ///////////////////////////////////////////////
   // Timer 1
   if (PIR1 & (1<<TMR1IF) ) {
      PIR1 &= ~(1<<TMR1IF); // Clear flag.

      GPIO  &= ~(1<<SERVO_BIT);   // Reset servo
   }

   INTCON |= (1<<T0IE);   // Enable Timer 0.

   // Note GIE set by RETFIE instruction (see assembler output).
}




Jump from pic pwm interrupt
to Best Microcontroller Projects Home Page.

New! Comments

Have your say about what you just read! Leave me a comment in the box below.



Privacy Policy | Contact | About Me

Site Map | Terms of Use



Visit our Facebook Page:

   Click Here



Recent Articles

  1. Arduino Battery Charger: How to save the planet one battery at a time; Recycle and re-use Alkaline battries

    Arduino Battery Charger: A very useful project that lets you charge 'un-rechargeable' alkaline batteries!

    Read more

  2. [Arduino Tutorial] : How to use the 74HC595 shift register.

    The Essential Guide to the 74HC595; What it is and how you can easily use one in any of your projects.

    Read more

  3. How to use the MCP23017 I/O Expander on the Arduino

    How to use the MCP23017 to increase your I/O by 16 pins (or more) and use its interrupt system.

    Read more

Sign up for MicroZine
''The'' Microcontroller Newsletter

Enter your first Name and primary email address in the form below:


And receive absolutely FREE a full project for:

"Measuring Analogue Voltages
Without An ADC"

(Using only one pin).

Instant Download:
You Can
Get It Right Now

Warning: This project could be  Removed 
at any time.  

It will  NOT be 
available indefinitely SO
To avoid 
disappointment  get it:

Now



:
:
Don't worry -- your e-mail address is totally secure. I promise to use it only to send you MicroZine
Remember this is a project with full description and fully debugged C Source code - and it's not available from the main website.

You can only get it through this newsletter.

To get exclusive access Enter your first name Name and primary email address Now in the form above.:



But wait !

There's more...

You'll receive more
free and exclusive reports as well as site information and site product updates


Scroll up to the form above and sign up NOW. Don't forget it's FREE and if you don't like it, you can unsubscribe at any time.

Click Here Now to use the form above to get your Valuable information absolutely free.



Readers Comments

"I wanted to thank
you so so so much
for all the information
you have provided in
your site it's

SUPERB and FANTASTIC."

- Ranish Pottath

"This site really is
the best and my favorite.
I find here many useful
projects and tips."

- Milan

bursach<at>gmail.com<

"Awesome site,
very, very easy and nice
to navigate!"


- Matt
matt_tr<at>
wolf359.cjb.net


Learn Microcontrollers

"Interested in
Microcontrollers?"

Sign up for The
Free 7 day guide:

FREE GUIDE : CLICK HERE


"I am a newbie to PIC
and I wanted to say
 how great your
site has been for me."


- Dave

de_scott<at>bellsouth.net

"Your site is a great
and perfect work.
congratulations."


- Suresh

integratredinfosys<at>
yahoo.com

"I couldn't find the correct
words to define
yourweb site.

Very useful, uncovered,
honest and clear.

Thanks so much for
your time and works.
Regards."


- Anon

Back to Top