Odd Timing Behaviour - PIC18F - Page 1
Forum section: electronics / general

author: ed_reardon
author_rank: Regular Contributor
timestamp: June 14, 2017, 06:47:51 pm
content: 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:void delayfive (int loop){int i;for (i=0; i<loop; i++){Nop();}}int main () {//Set device clock - 4MHz operation//OSCCONbits.IRCF2 = 1; //Make theOSCCONbits.IRCF1 = 1; //internal clockOSCCONbits.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 PinTRISAbits.RA0 = 0; //define A0 as OUTPUTTRISBbits.RB0 = 1; //define B0 as INPUTCMCON = 0xFF; //switch OFF comparitorsPWMEN1 = 0xFF; //switch OFF PWM moduleADCON1 = 0xFF; //switch OFF Port-A ADCif (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!

author: StillTrying
author_rank: Super Contributor
timestamp: June 14, 2017, 08:58:24 pm
content: "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,fgoto loopreturnThere are other options to get 1us resolution above about 8us in ASM but not in C that I know of...

author: AndyC_772
author_rank: Super Contributor
timestamp: June 14, 2017, 09:31:13 pm
content: View the disassembly of your code. How many instructions are actually in your loop?

author: ed_reardon
author_rank: Regular Contributor
timestamp: June 14, 2017, 09:57:10 pm
content: Code:!void delayfive (int loop)!{! int i;! for (i=0; i<loop; i++)0x1F90: MOVLW 0x00x1F92: MOVWF 0x5, ACCESS0x1F94: MOVLW 0x00x1F96: MOVWF i, ACCESS0x1F98: BRA 0x1FA00x1F9C: INFSNZ i, F, ACCESS0x1F9E: INCF 0x5, F, ACCESS0x1FA0: MOVF __pcstackCOMRAM, W, ACCESS0x1FA2: SUBWF i, W, ACCESS0x1FA4: MOVF 0x5, W, ACCESS0x1FA6: XORLW 0x800x1FA8: MOVWF 0x3, ACCESS0x1FAA: MOVF 0x2, W, ACCESS0x1FAC: XORLW 0x800x1FAE: SUBWFB 0x3, W, ACCESS0x1FB0: BTFSC STATUS, 0, ACCESS0x1FB2: RETURN 00x1FB4: BRA 0x1F9A! {! Nop();0x1F9A: NOP0x1F9C: INFSNZ i, F, ACCESS0x1F9E: INCF 0x5, F, ACCESS0x1FA0: MOVF __pcstackCOMRAM, W, ACCESS0x1FA2: SUBWF i, W, ACCESS0x1FA4: MOVF 0x5, W, ACCESS0x1FA6: XORLW 0x800x1FA8: MOVWF 0x3, ACCESS0x1FAA: MOVF 0x2, W, ACCESS0x1FAC: XORLW 0x800x1FAE: SUBWFB 0x3, W, ACCESS0x1FB0: BTFSC STATUS, 0, ACCESS0x1FB2: RETURN 00x1FB4: BRA 0x1F9AI'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!

author: Bruce Abbott
author_rank: Frequent Contributor
timestamp: June 15, 2017, 03:20:13 am
content: 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:0x1F9A: NOP0x1F9C: INFSNZ i, F, ACCESS0x1F9E: INCF 0x5, F, ACCESS0x1FA0: MOVF __pcstackCOMRAM, W, ACCESS0x1FA2: SUBWF i, W, ACCESS0x1FA4: MOVF 0x5, W, ACCESS0x1FA6: XORLW 0x800x1FA8: MOVWF 0x3, ACCESS0x1FAA: MOVF 0x2, W, ACCESS0x1FAC: XORLW 0x800x1FAE: SUBWFB 0x3, W, ACCESS0x1FB0: BTFSC STATUS, 0, ACCESS0x1FB2: RETURN 00x1FB4: BRA 0x1F9AUsing this:-Code:uint8_t loop;loop = 10;while (--loop);I get:-Code:1FF4 0E0A MOVLW 0xa1FF6 6E02 MOVWF 0x2, ACCESS1FF8 2E02 DECFSZ 0x2, F, ACCESS1FFA D7FE BRA 0x1ff8The 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!

author: NorthGuy
author_rank: Super Contributor
timestamp: June 15, 2017, 04:21:19 am
content: 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.

author: ed_reardon
author_rank: Regular Contributor
timestamp: June 15, 2017, 07:27:46 am
content: 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