sabato 23 marzo 2013

How to use 4 HARDWARE PWM on a PIC @ 50hz


simplified block diagram of PWM operation 
(from PIC 16F1509 datasheet)
Do you need to use a servomotor with a PIC (with the PWM modules integrated in it)? it could be a problem, since servomotor works around 50Hz, which is a really-slow frequency.

I'll use the PIC 16F1509, teach you the basics, so you can arrange the code to other PICs too.

My new project (lucky me) needs 4 hardware implemented PWM source, but I noticed that the pic microcontrollers usually have just 2 or 3 PWM modules. then I discovered this useful page: http://www.microchip.com/maps/Microcontroller.aspx which allows you to perform a parametric search over the complex pic-world. I ended up with the PIC 16F1509, which seems to be perfect: USART, 4PWM, comparator, 18 I/O pins, ADCs AND software selectable frequency for the internal oscillator!







first of all, you have to know how the PWM (for a servo motor) works. You can refer to the page http://arduino.cc/en/Tutorial/PWM which is simple and complete.

ok so let's talk about the HARDWARE PWM on pic 16f1509. You can find many tutorials online on how to control servos with different tecniques around timer2 interrupt, the reality is that methods are not "REAL" hardware pwm, so if you need to use the pic for other purpose while controlling servos, simply youy cant, because the pic are busy controlling PWM via software.

I started with the pic running @ 500Khz, but I found that is possible to control 4 servos + USART with a 2Mhz clocked pic (using the internal oscillator @ 2Mhz: you can use USART whith a baudrate of 9600 and 50Hz PWM without errors).

now some coding :) in a better world, you could write:
PWM1_init(50);
PWM1_start();
PWM1_set_duty(50);

to get the module working. The problem is, due to a bug in the MikroC compiler, the PWM functions don't work. So I had to re-write some functions... I'll report the code for PWM1, other modules are the same.

Let's enable to module (should be the PWM1_init() of MikroC):
//disable CLC and set C port
       CLC1CON=0;
            CWG1CON0=0;
            PORTC = 0;
            TRISC = 0;
//enable PWN and output
            PWM1EN_bit=1;
            PWM1OE_bit=1;
            PWM1POL_bit=0; 
 Note that I used the inverse polarity (PWM1POL=0) because of the limited amount of time spent in the HIGH state (from 0 to 2mS). 
Now set the correct register (should be the call PWM1_init(50) in MikroC):
            //set PR2 (the more you set, the less frequency you obtain)
            PR2=0xA9;
            
            //timer2 on with 64 prescaler
            TMR2ON_bit=1;
            T2CKPS0_bit=1;
            T2CKPS1_bit=1;

Now, whit this set of paramters you obtain a PWM frequency of (according to the pic datasheet):

 period = (PR2+1)*4*Tosc*TMR2_prescale_value
            = 170*4*(1/2000000)*64
            = 0.02176 s

so the frequency is:
1/0.02176=45.95Hz
a bit less than the 50Hz required by the servo RC, but even close to use it. I tested this code with several Servos and i noticed that it works great!


For the resolution:
resolution = log(PR2+1)/log(4)= 9 bit

BTW I'll use only 8 bit (for the sake of simplicity). To set the duty cycle the PIC uses 2 different register: 6bit in the PWM1DHC and 2 bit in PWM1DCL.

the sketch of code for dividing a byte 6-2 (in C) is:
#include <stdio.h>

//*********binary representation of a number (recursive)*******/////
void printBin_r(int n,int cont){
    if (cont<7)
        printBin_r(n>>1,cont+1);

    printf("%d ",n&1);
}
void printBin(int n){
    printBin_r(n,0);
}
///////*******************************************/////////////////

int main(){

    unsigned char a=0b10000111; //just an example of a number to divide
    unsigned char f=0,s=0; //simulating 2 8-bit registers

    printBin(a);
    printf("\n");

    //******* divide a byte -> 6 bit in f and 2 bit in s ******////
    f=a>>2;
    s=a&0b11;
    /////////************************************/////////////

    printBin(f);
    printf("\n");

    printBin(s);
    printf("\n");

    return 0;
}

 it should be simple for you to convert it in MikroC :) So you now have the PWM1_set_duty as well :)

enjoy the PWM hardware moduleS
 ;)

2 commenti:

  1. Please help. I'm trying to produce pulses with widths varying from 1ms - 2ms and I need to make the variations in fine increments (i.e. 1.1 ms, 1.2 ms, 1.3 ms, etc...). Also, I need the frequency to be around 50 or 60 Hz (the servo controller outputs 66.31 Hz). It isn't possible using the regular pwm function in MikroC. I have to recreate the pulses using PIC16F877a. Thanks in advance!

    RispondiElimina
  2. why not using a value of 156, that will give you a freq. of 50.08 Hz, better than the 45 right??

    RispondiElimina