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.

CCP PIC18F2580

Status
Not open for further replies.

Cantafford

Member
Hello,

I'm trying to control a motor via PWM pulses with PIC18f2580.
My input frequency is 8Mhz and I'm trying to implement a 50HZ(20ms time period) pulse with 75% duty cycle.
Preescaler was set to 1:1. I have calculated that for a 75% duty cycle the on time should be 15ms and off time 5ms.

Number of instruction cycles in 15ms is equal to 30.000 according to my calculations.
So I set(or tried to), the CCP1 module to generate an interrupt on match(when TMR1 reaches 30.000 counting from 0) so I have 15ms elapsed by then. When the interrupt is generated, the program is supposed to drive the pin low and keep it there for 5ms.

This is the program that I wrote:
Code:
/*
* File:   pwm.c
* Author: Paul
*
* Created on November 19, 2015, 10:30 AM
*/

#include <stdio.h>
#include <stdlib.h>
#include "pwm.h"

void interrupt CCP1int()
{
    if(CCP1IF == 1)
    {
        __delay_ms(5);
        CCP1IF = 0;
    }
}

void main()
{
    OSCCON = 0x76; // frequency is 8Mhz
    TRISCbits.RC2 = 0; // motor is here(made output)
    CCP1IE = 1;
    GIE = 1;
    PEIE = 1;
    CCP1CON = 0b00001001; // initialize CCP pin high. on compare match, force CCP pin low(CCPIF set)
    CCPR1H = 0x75; // 30000 in CCPR1
    CCPR1L = 0x30;
    T1CON = 0b11000001; // 1:1 prescaler()

    while(1)
    {

    }

}

And the header:
Code:
/*
* File:   pwm.h
* Author: Paul
*
* Created on November 19, 2015, 10:30 AM
*/

// PIC18F2580 Configuration Bit Settings

// 'C' source line config statements

#include <xc.h>

// #pragma config statements should precede project file includes.
// Use project enums instead of #define for ON and OFF.

// CONFIG1H
#pragma config OSC = IRCIO67    // Oscillator Selection bits (Internal oscillator block, port function on RA6 and RA7)
#pragma config FCMEN = OFF      // Fail-Safe Clock Monitor Enable bit (Fail-Safe Clock Monitor disabled)
#pragma config IESO = OFF       // Internal/External Oscillator Switchover bit (Oscillator Switchover mode disabled)

// CONFIG2L
#pragma config PWRT = OFF       // Power-up Timer Enable bit (PWRT disabled)
#pragma config BOREN = OFF      // Brown-out Reset Enable bits (Brown-out Reset disabled in hardware and software)
#pragma config BORV = 3         // Brown-out Reset Voltage bits (VBOR set to 2.1V)

// CONFIG2H
#pragma config WDT = OFF        // Watchdog Timer Enable bit (WDT disabled (control is placed on the SWDTEN bit))
#pragma config WDTPS = 32768    // Watchdog Timer Postscale Select bits (1:32768)

// CONFIG3H
#pragma config PBADEN = OFF     // PORTB A/D Enable bit (PORTB<4:0> pins are configured as digital I/O on Reset)
#pragma config LPT1OSC = OFF    // Low-Power Timer 1 Oscillator Enable bit (Timer1 configured for higher power operation)
#pragma config MCLRE = OFF      // MCLR Pin Enable bit (RE3 input pin enabled; MCLR disabled)

// CONFIG4L
#pragma config STVREN = ON      // Stack Full/Underflow Reset Enable bit (Stack full/underflow will cause Reset)
#pragma config LVP = ON         // Single-Supply ICSP Enable bit (Single-Supply ICSP enabled)
#pragma config BBSIZ = 1024     // Boot Block Size Select bit (1K words (2K bytes) boot block)
#pragma config XINST = OFF      // Extended Instruction Set Enable bit (Instruction set extension and Indexed Addressing mode disabled (Legacy mode))

// CONFIG5L
#pragma config CP0 = OFF        // Code Protection bit (Block 0 (000800-001FFFh) not code-protected)
#pragma config CP1 = OFF        // Code Protection bit (Block 1 (002000-003FFFh) not code-protected)
#pragma config CP2 = OFF        // Code Protection bit (Block 2 (004000-005FFFh) not code-protected)
#pragma config CP3 = OFF        // Code Protection bit (Block 3 (006000-007FFFh) not code-protected)

// CONFIG5H
#pragma config CPB = OFF        // Boot Block Code Protection bit (Boot block (000000-0007FFh) not code-protected)
#pragma config CPD = OFF        // Data EEPROM Code Protection bit (Data EEPROM not code-protected)

// CONFIG6L
#pragma config WRT0 = OFF       // Write Protection bit (Block 0 (000800-001FFFh) not write-protected)
#pragma config WRT1 = OFF       // Write Protection bit (Block 1 (002000-003FFFh) not write-protected)
#pragma config WRT2 = OFF       // Write Protection bit (Block 2 (004000-005FFFh) not write-protected)
#pragma config WRT3 = OFF       // Write Protection bit (Block 3 (006000-007FFFh) not write-protected)

// CONFIG6H
#pragma config WRTC = OFF       // Configuration Register Write Protection bit (Configuration registers (300000-3000FFh) not write-protected)
#pragma config WRTB = OFF       // Boot Block Write Protection bit (Boot block (000000-0007FFh) not write-protected)
#pragma config WRTD = OFF       // Data EEPROM Write Protection bit (Data EEPROM not write-protected)

// CONFIG7L
#pragma config EBTR0 = OFF      // Table Read Protection bit (Block 0 (000800-001FFFh) not protected from table reads executed in other blocks)
#pragma config EBTR1 = OFF      // Table Read Protection bit (Block 1 (002000-003FFFh) not protected from table reads executed in other blocks)
#pragma config EBTR2 = OFF      // Table Read Protection bit (Block 2 (004000-005FFFh) not protected from table reads executed in other blocks)
#pragma config EBTR3 = OFF      // Table Read Protection bit (Block 3 (006000-007FFFh) not protected from table reads executed in other blocks)

// CONFIG7H
#pragma config EBTRB = OFF      // Boot Block Table Read Protection bit (Boot block (000000-0007FFh) not protected from table reads executed in other blocks)

#define _XTAL_FREQ 8000000

4sz56s.png


As the program starts the motor gets energized just for a short time then it stays off forever.
Basically if there were a led instead of the motor it would flash for a quick period(probably 15ms) then stay off forever. Please help me in correcting this issue.
 
Hello,

Preescaler was set to 1:1. I have calculated that for a 75% duty cycle the on time should be 15ms and off time 5ms.

Timer 2 pre and post scalar is disabled by hardware when using it as the CCP PWM timer and is always 1:1 regardless of the pre and post scalar bit settings.

The value written to the PR2 register sets the period. The equation for calculating this value is -

((Fosc / PWM Freq) / 4) - 1

That said, the PWM hardware cannot generate a PWM frequency that low with an 8MHz oscillator. The lowest you can get with 8MHz is 7.8kHz. If you wanted 50Hz, you'd have to use a much slower oscillator frequency...something in the kHz range.

Now for the more important question...why is it a requirement for the PWM frequency to be 50Hz?
 
Hi Paul,

You've selected CCP module 'compare' mode '1001' which forces the CCP1 pin low on a compare match. Once you force the pin low it's just going to stay low.

You might consider switching between 'compare' mode '1001' (force CCP1 low on match) and mode '1000' (force CCP1 high on match) in your interrupt routine.
Code:
 void interrupt CCP1int()       //
 { if(CCP1IF == 1)              // if CCP interrupt
   { CCP1IF = 0;                // clear CCP interrupt flag
     CCP1M0 ^= 1;               // toggle mode '1000' <> '1001'
     if(CCP1M0)                 // if CCP1 is hi
       CCPR1 += 30000;          // set hi match
     else                       // if CCP1 is lo
       CCPR1 += (40000-30000);  // set lo match
   }                            //
 }                              //
You might also consider using 'compare' mode '0010' which toggles the output on a compare match;
Code:
/*                                                               *
 *  compare mode '0010'                                          *
 *                                                               */
 void interrupt CCP1int()       //
 { if(CCP1IF == 1)              // if CCP interrupt
   { CCP1IF = 0;                // clear interrupt flag and
     if(CCP1 == 1)              // if CCP1 is hi
       CCPR1 += 30000;          // set hi compare match value
     else                       // if CCP1 is lo
       CCPR1 += (40000-30000);  // set lo compare match value
   }                            //
 }                              //
Some caveats with these modes so be careful. There's a subtle bug in the examples. That is, you probably should do an intermediate calculation then write the hi byte into CCPR1H first, followed by the lo byte into CCPR1L. Almost all compilers write the lo byte first which might cause a false interrupt trigger.

The PWM module actually provides more flexibility and higher resolution (using an ISR 'helper'). I'd be happy to elaborate or provide a link to threads which show how it's done, if anyone is interested.

Good luck on your project.

Cheerful regards, Mike
 
Last edited:
Please ignore the erroneous responses and good luck on your project.
Yes! Please ignore my response, I didn't read your code properly!!.. Jpanhalt did something similar recently... I am getting too old!! I still have the code we worked on!!! He used the interrupt and manually forced the pin high!!!

Its a good job Mike is still here to pick up the pieces!!!
 
I think we're all guilty of being "too quick on the trigger" sometimes. While Paul's original query has little to do with the CCP module in PWM mode, I'm still curious about the credibility of the advice that was offered...
Timer 2 pre and post scalar is disabled by hardware when using it as the CCP PWM timer and is always 1:1 regardless of the pre and post scalar bit settings.
It's my understanding that when using the CCP module in PWM mode, neither the TMR2 pre-scaler nor the post-scaler are "disabled by hardware". I thought the pre-scaler was integral in determining PWM frequency and period. And while the TMR2 post-scaler is not used for determining PWM frequency and period, isn't it used to provide a TMR2 interrupt flag at multiples (equal to the post-scaler value) of the PWM period? I apologize in advance if I've misinterpreted the Datasheets.

Cheerful regards, Mike
 
Just for the help it may give.... Here is the CCP module in capture mode providing PWM... I think this was purley a servo motor so It's design will be 1mS pulse to 2mS pulse, but everything is there
C:
#include <xc.h>
#include <stdlib.h>
#include <stdio.h>
#include <string.h>

#define XMAX 605
#define XMIN 405
#define YMAX 605
#define YMIN 405

#define ABS(N) ( (N) >= 0 ? (N) : -(N) )
#pragma config LVP = OFF, BOREN = OFF,  FOSC = INTOSC, MCLRE = OFF, WDTE = OFF, PWRTE = OFF, PLLEN = OFF
volatile unsigned int RES;

void delayUs(int x)
   {
   x>>=3;
   while(x--)
     {
     NOP();
     NOP();
     }
   }

void delayMs(int x)
   {
   while(x--)
     delayUs(1000);
   }
   
void interrupt ISR(void)
   {
   if (TMR2IF)
     {
     CCPR2L = RES & 0xFF;
     CCPR2H = (RES>>8)&0xFF;
     TMR1L = TMR1H = 0;
     CCP2CON = 0x09;   
     TMR2IF = 0;
     }   
   else
     {
     TMR1L = TMR1H = 0;
     CCP2CON = 0x0;
     CCP2IF = 0;     
     }
   }

unsigned int GetFromAdc(unsigned char Channel)
   {
   unsigned int res;
   ADON = 1;
   delayUs(100);
   GO = 1;
   while(GO);
   res = (unsigned int)ADRESH<<8;
   res += ADRESL;
   //res<<=1;
   return res;
   }

void SetPwm(void)
   {
   RES = GetFromAdc(0);
   RES += 0x0800;
   }

void main(void)
   {
  OSCCON = 0x70;
   ADCON1 = 0xB0;
   ANSELA = ANSELB = ANSELC = 0;
   TRISA = TRISC = 0x0;
   TRISB = 0xAF;
   T2CON = 0x4E;
   PR2 = 250;
   TMR2IE = 1;
   INTCON = 0xC0;   
   T1CON = 1;
   CCP2SEL = 1;
   CCP2IE = 1;
   TMR1L = TMR1H = 0;
   ANSELA = 0x01;
   ADCON1 = 0xb0;
   CCPR2L = 0;
   CCPR2H = 0x08;
   while(1)
     {
     SetPwm();
     }

   }
 
Hi Paul,

You've selected CCP module 'compare' mode '1001' which forces the CCP1 pin low on a compare match. Once you force the pin low it's just going to stay low.

You might consider switching between 'compare' mode '1001' (force CCP1 low on match) and mode '1000' (force CCP1 high on match) in your interrupt routine.
Right, that fixed the problem. I must've not understood the CCP well because I thought that on compare and drive low when match mode the pin was going to stay high for some time then on match go low then out of the interrupt and go high again. I know better now :D

Hi Paul,
The PWM module actually provides more flexibility and higher resolution (using an ISR 'helper'). I'd be happy to elaborate or provide a link to threads which show how it's done, if anyone is interested.
Yes, I know but this was not a practical application it was just something I did to help me understand the CCP module. Please DO PROVIDE that link I'm always willing to learn more :D


Hi Paul,
Good luck on your project.
Thanks a lot for the reply! :D
 
Ah yes you are correct Mike. Sorry...it's the post scalar that's disabled in PWM mode.

However, the lowest PWM frequency you can get even with a 1:16 pre scalar when Fosc = 8MHz is 488Hz.

Hence my original question - why is it a requirement for the PWM to be 50Hz?
 
If you are controlling a motor at that low a Freq (50Hz) you may as well gone triac control into a bridge for the DC motor.
50hz is considered low for PWM as the advantage is a higher frequency for noiseless operation.
See Fairchild App note AN-3006.
Max.
 
Ok...another odd problem and I have no ideea why this is occuring:

I have added this to the routine interrupt to the program and it initially worked fine:
Code:
void interrupt CCP1int()
{
    if(CCP1IF == 1)              // if CCP interrupt
   {
     CCP1IF = 0;                // clear interrupt flag and
     if(CCP1 == 1)              // if CCP1 is hi
     CCPR1 += 30000;          // set hi compare match value
     else                       // if CCP1 is lo
     CCPR1 += (40000-30000);  // set lo compare match value
   }                            //
}

I have modified something in it then switched it back to the form you see above and now the motor won't stay on for long...basically the first problem I had...which is really really odd because first time I put it in the code it worked fine now it doesn't anymore.

This is the whole code(nothing was modified in it):
Code:
/*
* File:   pwm.c
* Author: Paul
*
* Created on November 19, 2015, 10:30 AM
*/

#include <stdio.h>
#include <stdlib.h>
#include "pwm.h"


void interrupt CCP1int()
{
    if(CCP1IF == 1)              // if CCP interrupt
   {
     CCP1IF = 0;                // clear interrupt flag and
     if(CCP1 == 1)              // if CCP1 is hi
     CCPR1 += 30000;          // set hi compare match value
     else                       // if CCP1 is lo
     CCPR1 += (40000-30000);  // set lo compare match value
   }                            //
}


void main()
{
    OSCCON = 0x76; // frequency is 8Mhz
    TRISCbits.RC2 = 0; // motor is here(made output)
    CCP1IE = 1;
    GIE = 1;
    PEIE = 1;
    CCP1CON = 0b00001001; // initialize CCP pin high. on compare match, force CCP pin low(CCPIF set)
    CCPR1H = 0x75; // 30000 in CCPR1
    CCPR1L = 0x30;
    T1CON = 0b11000001; // 1:1 prescaler()

    while(1)
    {

    }

}
 
Hey Paul, I think you want the '0010' compare mode with that ISR code, Sir.

Hey Ian, I'd love to study that code. You got a version with comments, please?
 
Hey Paul, I think you want the '0010' compare mode with that ISR code, Sir.

Hey Ian, I'd love to study that code. You got a version with comments, please?
It was ported to C... I used Jpanhault's ASM code so I could simulate the problems he was having..

I'll try and remember and comment it for you!!
 
Here goes... I still don't know the reason for the PR2 setting

C:
#include <xc.h>
#include <stdlib.h>
#include <stdio.h>
#include <string.h>

#define XMAX 605
#define XMIN 405
#define YMAX 605
#define YMIN 405

#define ABS(N) ( (N) >= 0 ? (N) : -(N) )
#pragma config LVP = OFF, BOREN = OFF,  FOSC = INTOSC, MCLRE = OFF, WDTE = OFF, PWRTE = OFF, PLLEN = OFF
volatile unsigned int RES;

void delayUs(int x)
   {
   x>>=3;                           // 125 * 8uS = 1000uS
   while(x--)                         // a little bit out... 16 cylcles @ 0.5uS = 8uS
     {
     NOP();                         // this means the lowest uS delay is 12uS ( including the call  and the shift )
     NOP();
     }
   }

void delayMs(int x)
   {
   while(x--)
     delayUs(1000);                   //  Self explanitory
   }
   
void interrupt ISR(void)
   {
   if (TMR2IF)                           // End of period! start again
     {
     CCPR2L = RES & 0xFF;             // set  CCP2
     CCPR2H = (RES>>8)&0xFF;         // finish position
     TMR1L = TMR1H = 0;               // reset timer
     CCP2CON = 0x09;                 // turn on CCP2  pin
     TMR2IF = 0;                       // Clear timer flag
     }   
   else
     {
     TMR1L = TMR1H = 0;               // Okay!  CCP interrupt..  reset timer
     CCP2CON = 0x0;                   // Force Pin low
     CCP2IF = 0;                       // Clear CCP interrupt
     }
   }

unsigned int GetFromAdc(unsigned char Channel)
   {
   unsigned int res;
   ADON = 1;                           // Start ADC module
   delayUs(100);
   GO = 1;
   while(GO);                           // wait for conversion
   res = (unsigned int)ADRESH<<8;
   res += ADRESL;
   //res<<=1;                           // if 11 bit is wanted!!
   return res;                           // 10 bit result
   }

void SetPwm(void)
   {
   RES = GetFromAdc(0);               // Set duty
   RES += 0x0800;                     // according to ADC reading on channel 0
   }                                   // Add in 1mS

void main(void)
   {
  OSCCON = 0x70;                     // 8Mhz
   ADCON1 = 0xB0;                     // set ADC TAD
   ANSELA = ANSELB = ANSELC = 0;   // All digital to start
   TRISA = TRISC = 0x0;                 // Prep ports
   TRISB = 0xAF;
   T2CON = 0x4E;                     // Timer2 on
   PR2 = 250;                         //
   TMR2IE = 1;                         // interrupt on timer 2 required
   INTCON = 0xC0;                     // GIE and PEIE on
   T1CON = 1;                         // Turn on timer 1
   CCP2SEL = 1;                       
   CCP2IE = 1;                         // interrupt on CCP match required!!
   TMR1L = TMR1H = 0;                 // reset timer
   ANSELA = 0x01;                     // AN0 selected
   ADCON1 = 0xb0;                     // Again!! Christ knows why!!
   CCPR2L = 0;
   CCPR2H = 0x08;                     // start at 1mS
   while(1)
     {
     SetPwm();                         // Super loop and read pot!
     }

   }
 
Are you talking about the capture or compare project? For the compare projects ( https://www.electro-tech-online.com/threads/pic-ccp-module-in-compare-mode.145506/#post-1230065 ), the answer and/or workaround was found on the Microchip forum:
Source = loc cit
After doing more Goggling, I came across this inquiry from 2002: https://www.microchip.com/forums/m20541.aspx If you read through the OP's deliberations, it appears that sequential compares with the CCP module in the same mode (i.e., CCPxM<3:0>) cannot be done. One option is to switch modes, e.g., go alternately from b'1000' to b'1001', or just reset the module. Considering the age of the comment attributed to Microchip and failure by MC to revise or put it into writing as best I can tell gives me a small concern about whether that "issue" has been fixed or is described accurately.

I would be interested, if there is another solution.

John

Ian, I didn't see your post. I take a very long time to write anything.
 
Mike,

I am always interested your your solutions. You probably notice that I use a lot of the code you have so kindly given.

John
 
OK since he won't disclose why it has to be 50Hz I'm going to venture a guess. Are you thinking it has to be 50Hz because the motor happens to require 50Hz AC power?
 
OK since he won't disclose why it has to be 50Hz I'm going to venture a guess. Are you thinking it has to be 50Hz because the motor happens to require 50Hz AC power?
Sorry didn't see your message.
As I stated before it is not a practical application, just something I did to learn how to use the CCP module. I put a motor there instead of an LED because in proteus, an LED would simply light up having only one intensity no matter the duty cycle whereas a motor would rotate at different speeds for different duty cycles.
So the frequency that I chose here shouldn't really matter as there is no hardware implementation of this only for learning purposes.
 
Status
Not open for further replies.

Latest threads

New Articles From Microcontroller Tips

Back
Top