Continue to Site

Welcome to our site!

Electro Tech is an online community (with over 170,000 members) who enjoy talking about and building electronic circuits, projects and gadgets. To participate you need to register. Registration is free. Click here to register now.

  • Welcome to our site! Electro Tech is an online community (with over 170,000 members) who enjoy talking about and building electronic circuits, projects and gadgets. To participate you need to register. Registration is free. Click here to register now.

PIC16F777 3 PWM at 50hz

Status
Not open for further replies.

pixman

Member
I want to use a pic16f777 of pic 16f767 to drive 3 rc servo motors.
The servo motors use a pulse every 20ms and the time that it is on can be 1 - 2ms.
is it possible to run a pwm at 50hz.
This chip has a built in oscillator and it seems you can change the frequency in
OSCCON register to:
31.25khz
125 khz
250khz
500khz
1mhz
2mhz
4mhz
8mhz
Have i understood the data sheet correct.

Thanxs in advance.
David
 
//C Compiler hi tech c
//PIC16F877A

#include <htc.h>
#include <pic.h>

void main ()
{
int a=0xFF;
TRISC=0x00;
CCP1CON=0x0C;
PR2=0xFF; // set PWM period for approximately 19.53KHz
for(CCPR1L=0xFF,CCPR1H=0xFF;CCPR1L>0x00,CCPR1H>0x00;CCPR1L--,CCPR1H--)
_delay(100000);
T2CON=0x04;
}
 
You don't state which language you will be working with so it's hard to advise on actual code. However, regardless of language, you can use the PWM module in "Special Event Trigger" mode to generate the signals completely automatically (well via a very short interrupt). I posted sample C code for an 18 series in this thread, for 16 series the method is the same.

Mike.
 
Last edited:
If you do it that way you will have a very slow processor and can only drive 1 pin. You need to use the method I outlined earlier.

Mike.
 
I know you mentioned you're using the "PIC16F777" and a C Compiler, though you might be interested in a Swordfish module written by AndyO from Digital DIY. Swordfish is probably one of the easiest languages to convert to C, so perhaps the module is useful in some way?

It uses the Timer1 and CCP1 modules on the PIC. After initialising it, you can control up to 8 servo's on-the-fly like this:

Code:
Servo.ServoPosition(Servo_Num) = Postion_Val
Where:

  • Servo_Num (target servo to control) = 0:7
  • Postion_Val (uS duty on time) = 600:2400

Here's an example of moving the servo connected to PORTB.0 from min to max deflection:

Code:
Device = 18F2520
Clock = 8
 
#option Servo_NumberOfServos = 1
 
Include "InternalOscillator.bas"
Include "Servo.bas"
 
Dim Counter As Word
 
 // main program start
Servo.On                                // Start sending servo control pulses
 
While True 
    For Counter = 600 To 2400 
        Servo.ServoPosition(0) = Counter
        DelayMS(1) 
    Next
    For Counter = 2400 To 600 Step -1
        Servo.ServoPosition(0) = Counter
        DelayMS(1) 
    Next
Wend
 
Hi MIKE
I AM USING ASSEMBLE CODE, AND DO NOT UNDERSTAND C.
I AM GOING TO TRY AND LEARN C AT SOME POINT.

DAVID
 
Hi David,

If it will help, here's a variation of the code that Andy used for that Swordfish module. This method, which uses the CCP module "special event trigger" mode, is just one of many different ways you might implement three Servo outputs on your 16F777.

The code is setup for 8 servo outputs on Port B but it can be easily modified for three outputs. You'll need to setup the CCP module in "special event trigger" mode, initialize the Servo[] array elements with values in the range of 600..2400 (usecs), and enable CCP interrupts. Let us know if you have any questions...

Cheerful regards, Mike

Code:
;
;  K8LH Crazy-8 Hi-Rez 8-channel (PORTB) Servo Algorithm 
;
;  1 usec steps, 600-2400 usecs range, using prescaler setting 
;  of 1, 2, or 4 with a 4, 8, or 16-MHz clock, respectively.
;
;
;   unsigned char n = 0;
;   unsigned int Servo [] = { 1500, 1500, 1500, 1500, 1500,
;                             1500, 1500, 1500, 20000 };
;   unsigned char Channel = 1;
;
;   void interrupt()            // special event interrupts
;   { pir1.CCP1IF = 0;          // clear CCP1 interrupt flag
;     portb = Channel;          // output new Servo pulse
;     ccpr1 = Servo[n++];       // update "match" period value
;     Servo[8] -= CCPR1;        // adj end-of-period off time
;     Channel <<= 1;            // prep for next channel
;     if(n = 9)                 // if end of 20-msec period
;     { n = 0;                  // reset array index
;       Servo[8] = 20000;       // reset the 20 msec period
;       Channel = 1;            // reset Shadow to '00000001'
;     }
;   }
;
        org     0x004
        radix   dec
v_interrupt
        movwf   W_ISR           ; save main program context       |B?
        swapf   STATUS,W        ;                                 |B?
        movwf   S_ISR           ; save STATUS reg                 |B?
        clrf    STATUS          ; force bank 0                    |B0
        movf    FSR,W           ;                                 |B0
        movwf   F_ISR           ; save FSR                        |B0
;
;  clear the CCP1 interrupt flag and output new servo pulse
;
        bcf     PIR1,CCP1IF     ; clear CCP1 interrupt flag       |B0
        movf    Channel,W       ; get channel output bit          |B0
        movwf   PORTB           ; output new Servo pulse          |B0
;
;  put current Servo Servo[n] "on-time" into the CCPR1L:CCPR1H
;  compare registers for next "special event" match interrupt
;  and subtract the "on-time" from Servo[8] "period"
;
        clrc                    ; clr carry                       |B0
        rlf     n,W             ; n index (0..8) * 2              |B0
        addlw   Servo           ; add @Servo[0] base address      |B0
        movwf   FSR             ; setup indirect address          |B0
        movf    INDF,W          ; Servo[n] pulse lo, 600..2400    |B0
        movwf   CCPR1L          ; set new CCP compare time lo     |B0
        subwf   Servo+16,F      ; adjust Servo[8] Period lo       |B0
        incf    FSR,F           ;                                 |B0
        movf    INDF,W          ; Servo[n] pulse hi, 600..2400    |B0
        movwf   CCPR1H          ; set new CCP compare time hi     |B0
        skpc                    ; borrow?  no, skip, else         |B0
        incf    INDF,W          ; increment sutrahend             |B0
        subwf   Servo+17,F      ; adjust Servo[8] Period hi       |B0
;
;  increment Servo mask and 'n' index for next interrupt cycle
;
        clrc                    ;                                 |B0
        rlf     Channel,F       ; shift channel output bit        |B0
        incf    n,F             ; inc n                           |B0
        movf    n,W             ;                                 |B0
        xorlw   9               ; all 9 intervals?                |B0
        bnz     ISR_XIT         ; no, branch, else                |B0
;
;  reset Channel, 'n' index, and Servo[8] 'period' variables
;
        clrf    n               ; reset index to 0                |B0
        movlw   low  20000      ; reset Period to 20000 usecs     |B0
        movwf   Servo+16        ;                                 |B0
        movlw   high 20000      ;                                 |B0
        movwf   Servo+17        ;                                 |B0
        bsf     Channel,0       ; reset channel to '00000001'     |B0
;
;  restore main program context
;
ISR_XIT
        movf    F_ISR,W         ;                                 |B0
        movwf   FSR             ; restore FSR                     |B0
        swapf   S_ISR,W         ;                                 |B0
        movwf   STATUS          ; restore STATUS                  |B?
        swapf   W_ISR,F         ; don't screw up STATUS           |B?
        swapf   W_ISR,W         ; restore W-reg                   |B?
        retfie                  ; return from interrupt           |B?
;
 
Hi Mike
I would like to use the 3 hardware pwm modules to drive 3 servo motors.
not use a software pwm.
David
 
Hi David,

Then you might want to consider setting up the PWM module with a period of 250-usecs (prescale 4, pr2 = 249, 16-MHz clock) and use 80 of these 250-usec PWM period "frames" to form the much longer 20000-usec Servo period.

Code:
unsigned char frame = 80;       // frame counter, 0..80
unsigned int servo1 = 1500;     // 600..2400 usecs
unsigned int servo2 = 1500;     //
unsigned int servo3 = 1500;     //

void interrupt()
{
   /**************************************************************
    *  K8LH 3-channel CCP module 'PWM' Servo algorithm ISR code  *
    *                                                            */
    if(frame == 80)             // if end of 20-msec Servo period
    { frame = 0;                // reset frame number
      width1 = servo1;          // setup work variables
      width2 = servo2;          // 
      width3 = servo3;          //
    }
    frame++;                    // increment frame number

   /**************************************************************
    *  setup CCP1 PWM duty cycle for next 250-usec PWM 'frame'   *
    *                                                            */
    if(width1 > 250)            // if width1 > 250-usecs
    { CCPR1L = 250;             // do a 100% duty cycle frame
      width1 -= 250;            // subtract 250-usecs
    }
    else                        // do a variable or 0% frame
    { CCPR1L = width1;          //
      width1 = 0;               // remaining frames are %0
    }
   /**************************************************************
    *  setup CCP2 PWM duty cycle for next 250-usec PWM 'frame'   *
    *                                                            */
    if(width2 > 250)            // if width2 > 250-usecs
    { CCPR2L = 250;             // do a 100% duty cycle frame
      width2 -= 250;            // subtract 250-usecs
    else                        // do a variable or 0% frame
    { CCPR2L = width2;          //
      width2 = 0;               // remaining frames are %0
    }
   /**************************************************************
    *  setup CCP3 PWM duty cycle for next 250-usec PWM 'frame'   *
    *                                                            */
    if(width3 > 250)            // if width3 > 250-usecs
    { CCPR3L = 250;             // do a 100% duty cycle frame
      width3 -= 250;            // subtract 250-usecs
    }
    else                        // do a variable or 0% frame
    { CCPR3L = width3;          //
      width3 = 0;               // remaining frames are %0
    }
}
 
Last edited:
speed control 3 phase induction motor using pic16f777

i am doing speed control of 3 phase induction motor for my final year project...i have the source code in assembly language,the code is written for 60hz frequency according to the US standards.can anyone help me in changing it to 50hz (INDIAN standard).
I have attached asm files of v/f control of induction motor..plz help me
 

Attachments

  • adc operation.asm
    9.9 KB · Views: 202
  • vf_control_with_7x7.asm
    27.2 KB · Views: 240
Status
Not open for further replies.

Latest threads

New Articles From Microcontroller Tips

Back
Top