Continue to Site

Welcome to EDAboard.com

Welcome to our site! EDAboard.com is an international Electronics Discussion Forum focused on EDA software, circuits, schematics, books, theory, papers, asic, pld, 8051, DSP, Network, RF, Analog Design, PCB, Service Manuals... and a whole lot more! To participate you need to register. Registration is free. Click here to register now.

[SOLVED] Delay Function in MPLAB X - XC8 compiler is not accurate

Status
Not open for further replies.

str4code

Newbie level 4
Joined
Nov 1, 2016
Messages
5
Helped
0
Reputation
0
Reaction score
0
Trophy points
1
Activity points
45
Dear Members,

I am trying to control a servo motor with PWM using delay functions of XC8 compiler. This is the circuit configuration in Proteus.
2.png

i used the following code:
Code:
#define _XTAL_FREQ 8000000
// PIC16F887 Configuration Bit Settings
#pragma config FOSC = INTRC_NOCLKOUT
#pragma config WDTE = OFF      
#pragma config PWRTE = OFF      
#pragma config MCLRE = OFF     
#pragma config CP = OFF       
#pragma config CPD = OFF      
#pragma config BOREN = OFF     
#pragma config IESO = OFF     
#pragma config FCMEN = OFF 
#pragma config LVP = OFF      
// CONFIG2
#pragma config BOR4V = BOR40V   
#pragma config WRT = OFF      

#include <xc.h>

void delay_ms(int x) {
    for (int i = 0; i < x; i++) {
        __delay_ms(1);
    }
}

void delay_us(int x) {
    for (int i = 0; i < x; i++) {
        __delay_us(1);
    }
}

void Soft_PWM(int freq, float percent) 
{
    unsigned int period = (1.0 / freq) * 1000 * 1000; // us
    unsigned int duty = period * (percent / 100.0); // us
    for (int i = 0; i < 50; i++) {
        PORTCbits.RC1 = 1;
        delay_us(duty); 
        PORTCbits.RC1 = 0;
        delay_us(period - duty);
    }
}

void main() {
    OSCCON = 0x70; 
    TRISC = 0; // PORTB as Ouput Port
    do {
       // Soft_PWM(50, 5); // 1ms = 0 Degree
       // delay_ms(2000);
        Soft_PWM(50, 7.5); // 1.5ms = 90 Degree
      //  delay_ms(2000);
      //  Soft_PWM(50, 10); // 2ms = 180 Degree
    } while (1);
}

This is the output:
1.png

I have compared it with a pulse generator at 50Hz with 1.5ms duty. The out put from PIC seems to be much bigger than 1.5ms.
What am I missing?

Thanks in advance
 

See Proteus output message windows, there is allways a warning with a message like "Simulation is not being performed in real time". Moreover, you're not doing the right way, a soft generated PWM as you did will change if you add more routines to run in the main() function.
 
See Proteus output message windows, there is allways a warning with a message like "Simulation is not being performed in real time". Moreover, you're not doing the right way, a soft generated PWM as you did will change if you add more routines to run in the main() function.

There is no warning at Proteus.
**broken link removed**

Then i tried using shareware PWM from:
**broken link removed**

My code:
Code:
#define _XTAL_FREQ 8000000
#define TMR2PRESCALE 1

// PIC16F887 Configuration Bit Settings
#pragma config FOSC = INTRC_NOCLKOUT
#pragma config WDTE = OFF      
#pragma config PWRTE = OFF      
#pragma config MCLRE = OFF     
#pragma config CP = OFF       
#pragma config CPD = OFF      
#pragma config BOREN = OFF     
#pragma config IESO = OFF     
#pragma config FCMEN = OFF 
#pragma config LVP = OFF      
// CONFIG2
#pragma config BOR4V = BOR40V   
#pragma config WRT = OFF      

#include <xc.h>

void delay_ms(int x) {
    for (int i = 0; i < x; i++) {
        __delay_ms(1);
    }
}

void delay_us(int x) {
    for (int i = 0; i < x; i++) {
        __delay_us(1);
    }
}

void Soft_PWM(int freq, float percent) {
    unsigned int period = (1.0 / freq) * 1000 * 1000; // us
    unsigned int duty = period * (percent / 100.0); // us
    for (int i = 0; i < 50; i++) {
        PORTCbits.RC1 = 1;
        delay_us(duty);
        PORTCbits.RC1 = 0;
        delay_us(period - duty);
    }
}

PWM1_Init(long fre) {
    PR2 = (_XTAL_FREQ / (fre * 4 * TMR2PRESCALE)) - 1;
}

PWM1_Duty(unsigned int duty) {
    if (duty < 1024) {
        CCP1X = duty & 2;
        CCP1Y = duty & 1;
        CCPR1L = duty >> 2;
    }
}

PWM1_Start() {
    CCP1M3 = 1;
    CCP1M2 = 1;
#if TMR2PRESCALAR == 1
    T2CKPS0 = 0;
    T2CKPS1 = 0;
#elif TMR2PRESCALAR == 4
    T2CKPS0 = 1;
    T2CKPS1 = 0;
#elif TMR2PRESCALAR == 16
    T2CKPS0 = 1;
    T2CKPS1 = 1;
#endif
    TMR2ON = 1;
    TRISC2 = 0;
}

PWM1_Stop() {
    CCP1M3 = 0;
    CCP1M2 = 0;
}

void main() {
    OSCCON = 0x70;
    TRISC = 0; // PORTB as Ouput Port
    PWM1_Init(50);
    PWM1_Duty(77); // From 0 to 1024 (10bit range)
    PWM1_Start();
    do {
        // Soft_PWM(50, 5); // 1ms = 0 Degree
        // delay_ms(2000);
        Soft_PWM(50, 7.5); // 1.5ms = 90 Degree
        //  delay_ms(2000);
        //  Soft_PWM(50, 10); // 2ms = 180 Degree
    } while (1);
}

The output (Blue Hardware PWM):
2.png

The frequency is much higher than required... :cry:
 
Last edited by a moderator:

No, just misunderstanding how the microcontroller works. On 1MHz frequency each cycle takes 1us. Add here 'goto' instruction which is take 2 cycles, add here other asm instructions...
 
No, just misunderstanding how the microcontroller works. On 1MHz frequency each cycle takes 1us. Add here 'goto' instruction which is take 2 cycles, add here other asm instructions...

You are right, I am trying to use a variable in __delay_us(delay) but i am getting the error "inline delay argument must be constant". This is because the __delay_us is MACRO and not a function. So i thought of writing my own function:
Code:
void delay_us(int x){
   for(int i = 0; i < x ; i++){
      __delay_us(1);
   }
}
The problem with this is that i get a time bigger that it should beacuse each iteration of a for loop has:
1- delay
2- increment a variable
3- assign incremented value to a variable
4- check if condition is met
Each statement requires at least one clock cycle which has big impact on required time.

In other using the build in MACRO:
__delay_us(1500); // is almost accurate
the function i write
delay_us(1500); // much bigger than it should because of the for loop

Any suggestions on how to overcome this?
 

Hi,

Using the delay function is never very accurate.

If you want precise timing then
* use the PWM hardware (most precise, needs no processing power, only setup)
* use hardware timer/counter (polling counter value in main loop. Needs 100% processing power)
* use interrupts (needs only a part of processing power. Relatively low jitter)

Klaus
 
Any suggestions on how to overcome this?
Just take account for total instruction execution time when writing software delay. In case of doubt use MPLAB simulator stopwatch to check it.

The main limitation of software delays is that they only work correctly with disabled interrupts. The may be suitable for very simple, not to say trivial code, but hardly for real applications.
 
It can be accurate if you will use a timer. Start the timer, get it value, calculate necessary delay and wait before it will reach this value. Simple and accurate.
 

It seems that with 8MHz oscillator PWM frequency range is (Min: 488Hz , Max: 1MHz)
there is a tool which can be downloaded "PicMultiCalc" which has all the calculations done. Anyone interested can find it in the attainments.

**broken link removed**

It seems that using a timer would be a solution worth testing.
Thanks all.

(Solved)
 

Status
Not open for further replies.

Similar threads

Part and Inventory Search

Welcome to EDABoard.com

Sponsor

Back
Top