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.

PIC18F2450 PWM Help

Status
Not open for further replies.

psecody

Member
So I was going to start tinkering with building a robot just to try to understand various pic things I don't quite grasp. I'm using the easyPIC4 with MikroC and a PIC18F2450. I was planning on simply building a chassis with 2 servos for differential drive but after trying to figure out how to send pwm signals I realized I need to send two separate signals, one for each servo. My problem is the 2450 only has 1 ccp pin so I was wondering how I could either send signal to both servos from that one pin or how could I use two other I/O pins to send out pwm. I don't really 100% get how to do pwm though I've read a ton of tutorials they are all pretty much how to control 1 servo so they don't really apply here. So if anyone could help to explain kind of how this works I would appreciate it. Thanks again.
 
Start by having a read of this thread. You should be able to modify the code to work with your hardware. To extend it to drive 2 servos is pretty simple.

Mike.
 
ok so I looked at the code and there are parts I'm not familiar with and there are some parts that look similar. Sorry I'm not really used to using C on microcotrollers and its kind of different than just making apps in C and I'm pretty new to microcontrollers anyway so I hope you will all bear with me and my stupid questions. I started programming PIC's in asm but I don't like it because it takes longer to write everything and I can't really just look at the code and see whats going on as easily.

Code:
#include <p18f1320.h>
#pragma config WDT = OFF, LVP = OFF, OSC = INTIO2
#define ServoPin LATBbits.LATB3
#define ServoTris TRISBbits.TRISB3

void main(void){
int ServoPos;
    OSCCON=0x70;                //Osc=8MHz
    ADCON0=0b00000101;          //A2D on and select AN1
    ADCON1=0x7d;                //A1 = analogue
    ADCON2=0b10110101;          //Right justify - Fosc/16
    ServoTris=0;                //make servo pin output
    ServoPin=0;                 //Servo output off
    CCP1CON=0b00001011;         //Special event trigger
    T1CON=0b10010001;           //Timer 1 on with Pre=2
    ServoPos=1500;              //set servo to mid position
    CCPR1=ServoPos;             //set CCP initial value   
    while(1){
        while(!PIR1bits.CCP1IF);    //wait for CCP interrupt bit
        ServoPin=0;                 //end pulse
        CCPR1=20000-ServoPos;       //Off time = 20mS - Servo Time
        PIR1bits.CCP1IF=0;          //clear int flag
        ADCON0bits.GO=1;            //start conversion
        while(ADCON0bits.GO);       //Wait for it to complete
        ServoPos=ADRES+1000;        //Pos will be 1mS to 2.023mS
        while(!PIR1bits.CCP1IF);    //wait for int flag
        ServoPin=1;                 //start pulse
        CCPR1=ServoPos;             //Servo time in uS
        PIR1bits.CCP1IF=0;          //clear int flag
    }
}

Pommie from what I understand in your description of what this does is that a potentiometer is hooked up to the pic as well as a servo and the pic reads the position of the pot and makes the servo replicate its position. Is that correct?
So for my purposes, which is to directly control the servo from the code, I don't need the ADCON stuff do I?
#define ServoPin LATBbits.LATB3
#define ServoTris TRISBbits.TRISB3
These two statements set the names of portB right (bit3?)? I've never seen these used.

Also now that I look at it the code is outputting from the CCP pin is it not? My question was more of, is there a way that I can send signal to two separate servos for differential drive with only 1 CCP pin? I was thinking it should be possible to output a pulse from a regular I/O pin, or do I have to have 2 ccp modules for this?
 
You have understood the code correctly. It does output on the ccp pin but that is just coincidence, it can just as easily output on rb0.

For controlling two servos I posted some code over on the SourceBoost forum just yesterday **broken link removed**.

I'll repeat it here anyway,
Code:
#include <system.h>
#include <boostc.h>

#pragma DATA _CONFIG, _CP_OFF & _CCP1_RB0 & _DEBUG_ON 
        & _WRT_PROTECT_OFF & _CPD_OFF & _LVP_OFF & _BODEN_OFF 
        & _MCLR_ON & _PWRTE_OFF & _WDT_OFF & _INTRC_IO

int Servo1Pos,Servo2Pos;
char state;

void interrupt(void) {
int Time;
    switch (state++)
    {
        case 0:
        case 5:
            portb.1=1;
            Time=Servo1Pos;
            state=1;   
            break;
        case 1:
            portb.1=0;
            Time=2500-Servo1Pos;   
            break;
        case 2:
            portb.2=1;
            Time=Servo2Pos;   
            break;
        case 3:
            portb.2=0;
            Time=2500-Servo2Pos;   
            break;
        case 4:
            Time=15000;   
            break;
        default:
    }
    ccpr1l=Time&255;
    ccpr1h=Time/256; 
    pir1.CCP1IF=0;              //clear CCP1 interrupt
}

void main(void){
    osccon=0x60;                //4meg
    cmcon=7;
    ansel=0;
    trisb=0b11111001;
    ccp1con=0b00001011;         //Special event trigger
    t1con=0b10000001;           //Timer 1 on with Pre=1
    Servo1Pos=1500;             //set first servo to mid position
    Servo2Pos=2000;             //and second servo fully right
    pie1.CCP1IE=1;              //enable CCP1 interrupt
    intcon.PEIE=1;              //enable peripheral interrupts
    intcon.GIE=1;               //enable glogal interrupts
    while(1){
    }
}

BoostC is free and very good.

Mike.
 
ok so I think I'm understanding it a little bit more but there is still some stuff I'm not sure about so I'm going to try and break the code down and understand it.
Code:
#include <system.h>
#include <boostc.h>

#pragma DATA _CONFIG, _CP_OFF & _CCP1_RB0 & _DEBUG_ON 
        & _WRT_PROTECT_OFF & _CPD_OFF & _LVP_OFF & _BODEN_OFF 
        & _MCLR_ON & _PWRTE_OFF & _WDT_OFF & _INTRC_IO

int Servo1Pos,Servo2Pos;  
char state;      

void interrupt(void) {
int Time;
    switch (state++)
    {
        case 0:
        case 5:
            portb.1=1;
            Time=Servo1Pos;
            state=1;   
            break;
        case 1:
            portb.1=0;
            Time=2500-Servo1Pos;   
            break;
        case 2:
            portb.2=1;
            Time=Servo2Pos;   
            break;
        case 3:
            portb.2=0;
            Time=2500-Servo2Pos;   
            break;
        case 4:
            Time=15000;   
            break;
        default:        //what is the default for?
    }
    ccpr1l=Time&255;    //set the low byte to int time but what about the &255?
    ccpr1h=Time/256;  //this is like the low byte to me. I can read them and know what they're doing but I don't understand why. Where did you get the 255 and the 256 from?
    pir1.CCP1IF=0;              //clear CCP1 interrupt
}

void main(void){
    osccon=0x60;                //4meg
    cmcon=7;                       //I don't know what this is and can't find it on the datasheet.
    ansel=0;                     //I don't know what this one is either
    trisb=0b11111001;  
    ccp1con=0b00001011;         //I realize this is setting it to compare mode but could you explain how the compare mode functions?
    t1con=0b10000001;           //Timer 1 on with Pre=1
    Servo1Pos=1500;             //set first servo to mid position
    Servo2Pos=2000;             //and second servo fully right
    pie1.CCP1IE=1;              //enable CCP1 interrupt
    intcon.PEIE=1;              //enable peripheral interrupts
    intcon.GIE=1;               //enable glogal interrupts
    while(1){
    }
}

Sorry for so many questions that I'm sure are really simple and are slapping me in the face when I look at the data sheet but I can't seem to grasp it. Thanks for the help so far, theres at least a few things I'm starting to understand better.
 
If anything you should get an idea of some subjects which deserve additional study, such as the CCP module and its special event trigger mode.

Here's excerpts from my version of a soft 8 channel Servo controller which also uses the CCP module special event trigger mode. If you only need two channels then I would leave it set up as an 8 channel controller but I would AND the LATB assignment with a two channel mask (0b00000011 for example).

Regards, Mike

Code:
/*****************************************************************
 *  K8LH Crazy-8 'Soft' 8-channel Servo Controller variables     *
 *****************************************************************/

static unsigned char n = 0;
static unsigned int Pulse [] = { 1500, 1500, 1500, 1500,
                                 1500, 1500, 1500, 1500,
                                 20000 };
static unsigned char Servo = 1;

void isr_hi ()
{
 /****************************************************************
  *  K8LH Crazy-8 Hi-Rez Soft 8-channel (PORTB) Servo Algorithm  *
  ****************************************************************/
  if (PIR1bits.CCP1IF == 1)     // if "special event trigger"
  { PIR1bits.CCP1IF = 0;        // clear CCP1 interrupt flag
    LATB = Servo;               // output new Servo pulse and
    CCPR1 = Pulse[n++];         // setup next CCP1 "match" value

    Pulse[8] -= CCPR1;          // adjust end-of-cycle off time
    Servo <<= 1;                // and prep for next channel

    if (n == 9)                 // if end of a 20-msec cycle
    { n = 0;                    // reset array index
      Pulse[8] = 20000;         // reset the 20.0-msec period
      Servo++;                  // reset Servo shadow to Servo 1
    }
  }
}
/*****************************************************************
 *  MAIN                                                         *
 *****************************************************************/

void main (void)
{
  // setup INTOSC for 8-MHz operation
  OSCCON = 0b01110000;

  LATB = 0;                     // all PORT B servo outputs '0'
  TRISB = 0;                    // set PORT B all outputs
  ADCON1 = 15;                  // all digital I/O (no analog)

  // setup TMR1 and CCP1 module "special event trigger" interrupts
  CCPR1 = 1000;                 // first "compare" interrupt
  T1CON = 0b00010000;           // pre 2, post 1, 1 usec 'ticks'
  CCP1CON = 0b00001011;         // "special event trigger" mode

  PIR1 = 0;                     // clear peripheral int flags
  PIR2 = 0;                     //
  PIE1 = 0;                     // clear peripheral int enables
  PIE2 = 0;                     //

  PIE1bits.CCP1IE = 1;          // enable CCP1 interrupts
  INTCONbits.PEIE = 1;          // enable peripheral interrupts
  INTCONbits.GIE = 1;           // enable global interrupts
  T1CONbits.TMR1ON = 1;         // turn on Timer 1
  while(1)
  {
  }
}
 
Last edited:
thanks I appreciate the help a lot. It's starting to make a little more sense to me the more examples I'm seeing of the CCP module. I'll go do some more reading on the ccp module and hopefully get it so I don't have to bug ya'll with anymore questions. Thanks again
 
Mike (Pommie),

Just wondering why you didn't reset the 'state' variable in case 4? Seems more intuitive to me compared to the dual case 0/case 5 example.

Mike

Code:
void interrupt(void) {
int Time;
    switch (state++)
    {
        case 0:
            portb.1=1;
            Time=Servo1Pos;
            break;
        case 1:
            portb.1=0;
            Time=2500-Servo1Pos;   
            break;
        case 2:
            portb.2=1;
            Time=Servo2Pos;   
            break;
        case 3:
            portb.2=0;
            Time=2500-Servo2Pos;   
            break;
        case 4:
            Time=15000;   
            [COLOR=Red]state=0;[/COLOR]
            break;
        default:
    }
    ccpr1l=Time&255;
    ccpr1h=Time/256; 
    pir1.CCP1IF=0;              //clear CCP1 interrupt
}
 
Mike (Pommie),

Just wondering why you didn't reset the 'state' variable in case 4? Seems more intuitive to me compared to the dual case 0/case 5 example.

Mike

Hi Mike,

I originally had some other code in the case zero that was only executed once and I just deleted it:eek:. Without that code it makes much more sense to reset the state in case 4.

Mike.
 
Status
Not open for further replies.

Latest threads

New Articles From Microcontroller Tips

Back
Top