Author Topic: Odd Timing Behaviour - PIC18F  (Read 2125 times)

0 Members and 1 Guest are viewing this topic.

Offline ed_reardonTopic starter

  • Regular Contributor
  • *
  • Posts: 131
  • Country: gb
Odd Timing Behaviour - PIC18F
« on: June 14, 2017, 06:47:51 pm »
Hello all,

PIC18F1330 - XC8 (Vanilla flavour)

It's been a long day,  I'm doing something daft here.

I'm trying to write a delay function,  I need to be able to run it with a variable as the argument so I've created a prototype which I've just set as a single Nop during testing, initially it was using a defined __delay_us(5) (hence the name) however I'm getting odd frequencies.

I just want to bit-bang the pins, now by my reasoning 4MHz FOSC/4 should yeild 1uS per instruction,  however on the scope I'm struggling to break in to kHz,  so I've got a timing issue,  with my frequency counter it looks as if the PIC (PIC18F) is operating with a 31kHz clock,  I've tried all manner of ways of forcing it on to the 4MHz scaled clock. I appreciate there will be some loop overhead, but not enough to reduce the speed by an order of magnitude?

Code: [Select]



void delayfive (int loop)
{
    int i;
    for (i=0; i<loop; i++)
    {
       Nop();   
       
           
}
   
}



int main () {
//Set device clock - 4MHz operation//
    OSCCONbits.IRCF2 = 1;  //Make the
    OSCCONbits.IRCF1 = 1;  //internal clock
    OSCCONbits.IRCF0 = 1; //8MHz
   
#define PULSE LATAbits.LATA0  //define pin RA0 as PULSE pin
#define DIR LATAbits.LATA1    //define pin RA1 as DIRECTION pin
#define SPEED PORTBbits.RB0 //Define SPEED H/L Pin
    TRISAbits.RA0 = 0; //define A0 as OUTPUT
    TRISBbits.RB0 = 1; //define B0 as INPUT
     CMCON = 0xFF; //switch OFF comparitors
    PWMEN1 = 0xFF; //switch OFF PWM module
     ADCON1 = 0xFF; //switch OFF Port-A ADC
     
     if (SPEED == 1) {
     delayfive(30);
     PULSE= ~PULSE;
    }
     else{
         delayfive(4);
         PULSE= ~PULSE;
     }
}

INTIO is set, and the memory view during programming claims INTIO is set?

It's been a long day, I'm doing something silly!

Cheers,
Ed

* I know this code says 8MHz,  I Was just fiddling!
« Last Edit: June 14, 2017, 06:49:36 pm by ed_reardon »
 

Offline StillTrying

  • Super Contributor
  • ***
  • Posts: 2850
  • Country: se
  • Country: Broken Britain
Re: Odd Timing Behaviour - PIC18F
« Reply #1 on: June 14, 2017, 08:58:24 pm »
"by my reasoning 4MHz FOSC/4 should yeild 1uS per instruction"

The 'nop' itself is only 1 clk 1us, but even the 'goto start' after it is 2us, and by the time you add the incrementing and comparing of the count values the whole for loop can easily get to 20 to 30us.

The very shortest loop in ASM is something like 3 clks, 3us.

loop  decfsz  count,f
     goto loop
     return

There are other options to get 1us resolution above about 8us in ASM but not in C that I know of...
« Last Edit: June 14, 2017, 09:00:09 pm by StillTrying »
.  That took much longer than I thought it would.
 
The following users thanked this post: ed_reardon

Offline AndyC_772

  • Super Contributor
  • ***
  • Posts: 4277
  • Country: gb
  • Professional design engineer
    • Cawte Engineering | Reliable Electronics
Re: Odd Timing Behaviour - PIC18F
« Reply #2 on: June 14, 2017, 09:31:13 pm »
View the disassembly of your code. How many instructions are actually in your loop?
 
The following users thanked this post: ed_reardon

Offline ed_reardonTopic starter

  • Regular Contributor
  • *
  • Posts: 131
  • Country: gb
Re: Odd Timing Behaviour - PIC18F
« Reply #3 on: June 14, 2017, 09:57:10 pm »
Code: [Select]
!void delayfive (int loop)
!{
!    int i;
!    for (i=0; i<loop; i++)
0x1F90: MOVLW 0x0
0x1F92: MOVWF 0x5, ACCESS
0x1F94: MOVLW 0x0
0x1F96: MOVWF i, ACCESS
0x1F98: BRA 0x1FA0
0x1F9C: INFSNZ i, F, ACCESS
0x1F9E: INCF 0x5, F, ACCESS
0x1FA0: MOVF __pcstackCOMRAM, W, ACCESS
0x1FA2: SUBWF i, W, ACCESS
0x1FA4: MOVF 0x5, W, ACCESS
0x1FA6: XORLW 0x80
0x1FA8: MOVWF 0x3, ACCESS
0x1FAA: MOVF 0x2, W, ACCESS
0x1FAC: XORLW 0x80
0x1FAE: SUBWFB 0x3, W, ACCESS
0x1FB0: BTFSC STATUS, 0, ACCESS
0x1FB2: RETURN 0
0x1FB4: BRA 0x1F9A
!    {
!       Nop();   
0x1F9A: NOP
0x1F9C: INFSNZ i, F, ACCESS
0x1F9E: INCF 0x5, F, ACCESS
0x1FA0: MOVF __pcstackCOMRAM, W, ACCESS
0x1FA2: SUBWF i, W, ACCESS
0x1FA4: MOVF 0x5, W, ACCESS
0x1FA6: XORLW 0x80
0x1FA8: MOVWF 0x3, ACCESS
0x1FAA: MOVF 0x2, W, ACCESS
0x1FAC: XORLW 0x80
0x1FAE: SUBWFB 0x3, W, ACCESS
0x1FB0: BTFSC STATUS, 0, ACCESS
0x1FB2: RETURN 0
0x1FB4: BRA 0x1F9A

I'm afraid to my shame assembler is all Greek to me.

I guessing a lot of this is overhead,  I'm not using the optimised compiler and I have heard that it can seriously over-complicate quite simple routines?

I can get where I need to get adjusting the numbers (just) so it's not critical for my application however I'm quite staggered either how unoptimised the code is OR how many operations things that appear quite superfically simple take.

It's to my shame I don't understand this,  I'm not making out that it's a fault of anybody but me!


« Last Edit: June 14, 2017, 09:59:28 pm by ed_reardon »
 

Offline Bruce Abbott

  • Frequent Contributor
  • **
  • Posts: 627
  • Country: nz
    • Bruce Abbott's R/C Models and Electronics
Re: Odd Timing Behaviour - PIC18F
« Reply #4 on: June 15, 2017, 03:20:13 am »
The 'unoptimized' compiler  doesn't always produce the tightest code, but there are things you can do to make it better:-

1. Forget the nop. There will be plenty of other instructions in the loop.

2. Use 8 bit variables. An 8 bit MCU needs more instructions to process 16 bit variables.

3. Inline the code. This eliminates call-return and stack manipulation overhead.

4. Count down, not up. Testing for zero is quicker than comparing to a number.

The core counting code in your delay function is this:-
Code: [Select]
0x1F9A: NOP
0x1F9C: INFSNZ i, F, ACCESS
0x1F9E: INCF 0x5, F, ACCESS
0x1FA0: MOVF __pcstackCOMRAM, W, ACCESS
0x1FA2: SUBWF i, W, ACCESS
0x1FA4: MOVF 0x5, W, ACCESS
0x1FA6: XORLW 0x80
0x1FA8: MOVWF 0x3, ACCESS
0x1FAA: MOVF 0x2, W, ACCESS
0x1FAC: XORLW 0x80
0x1FAE: SUBWFB 0x3, W, ACCESS
0x1FB0: BTFSC STATUS, 0, ACCESS
0x1FB2: RETURN 0
0x1FB4: BRA 0x1F9A

Using this:-
Code: [Select]
    uint8_t loop;

    loop = 10;
    while (--loop);
I get:-
Code: [Select]
1FF4    0E0A  MOVLW 0xa           
1FF6    6E02  MOVWF 0x2, ACCESS   
1FF8    2E02  DECFSZ 0x2, F, ACCESS
1FFA    D7FE  BRA 0x1ff8           
The first two instructions load the variable, and the last 2 instructions are the counting loop. That's the same code I would write in assembler!

 

Offline NorthGuy

  • Super Contributor
  • ***
  • Posts: 3239
  • Country: ca
Re: Odd Timing Behaviour - PIC18F
« Reply #5 on: June 15, 2017, 04:21:19 am »
Set up a timer, start it and wait until it reaches the desired value. It'll be more accurate and will be less distracted by interrupts.
 

Offline ed_reardonTopic starter

  • Regular Contributor
  • *
  • Posts: 131
  • Country: gb
!
« Reply #6 on: June 15, 2017, 07:27:46 am »
Ah that's great all.

I've removed the Nop,  the for loop has now been adjusted and I've switched to 8 bit char.

I've now got a 4x increase in speed and that's much more flexible for my needs.

The assembly now looks a lot 'leaner' (well there's about 40% less of it!)

Cheers,
Ed
 


Share me

Digg  Facebook  SlashDot  Delicious  Technorati  Twitter  Google  Yahoo
Smf