Reputation: 27
I am trying to compile this code in order to generate a PWM signal 15us HIGH and 9us LOW.
#include <xc.h>
void g()
{
for(char val=0; val<20; val++)
{
GPIO = 0xFF;
NOP(); NOP(); NOP(); NOP(); NOP();
NOP(); NOP(); NOP(); NOP(); NOP();
NOP(); NOP(); NOP(); NOP();
// Write all GPIO pins LOW
GPIO = 0;
NOP(); NOP(); NOP();
}
}
void main(void) {
// Set all pins at output
TRIS = 0x00;
GPIO = 0;
while(1) {
g();
}
}
the problem is that the returning from FOR loop takes too many instructions. I was expecting to take less. In this case, using only 3 NOP();
functions there are still 15 ticks.
CLRF GPIO // GPIO = 0;
NOP
NOP
NOP
MOVLW 0x1
MOVWF wtemp1
MOVF wtemp1, W
ADDWF __pcstackBANK0, F
MOVLW 0x14
SUBWF __pcstackBANK0, W
BTFSC STATUS, 0x0
RETLW 0x0
GOTO 0x4 // Return to beginning of loop
Compiler XC8:
What should I do in order to squeeze the opcode? Is it something related to the mplab?
p.s I am not able to use -O3
flag as it is not included in my license.
Upvotes: 0
Views: 155
Reputation: 2749
I don't have your toolchain so I can't cycle count but this code should give you the basis to do a fine tuned version that generates 15us high, 9 us low reliably. I have also included sample variable delays that I think should be PIC friendly machine code if you intended to do PWM at some stage.
The first and most fundamental trick as suggested by @KonstantinMurugov is to turn the inner for
loop to countdown (which has hardware support on a PIC in the form of DECFSZ). I have made it into a while loop.
Then the next trick is to move the short delay into the middle of the loop where you have precise control of it and tweak the padding for the longer high delay until you get the desired times for both. My initial guess is based on some assumptions about the code generation and overheads so you may have to tweak it somewhat to get both the time constants exact. Also for testing purposes set val
to 1 so that the systematic errors show up in the first couple of cycles. Then retest with 2 and 3 to make sure it generates a correct waveform as it goes around each of the nested loops. To get it exactly right you may have to decrease the NOP()
count in the innermost loop and add them elsewhere.
#include <xc.h>
void g()
{
char val = 20;
// for fine tuning set this to 1 so that the loop exits each time (then recheck with val = 2,3)
do
{
NOP(); NOP(); NOP(); NOP();
GPIO = 0;
NOP(); NOP(); NOP(); NOP();
GPIO = 0xff;
NOP(); NOP(); NOP(); NOP();
} while (val--);
// this form of countdown loop is friendly to PIC DECFSZ instruction
}
// since `PWM` was mentioned in the original question here are two primitives.
// call overheads are disastrous so allow maximum inlining for speed
// in some ways cycle counting is easier in assembler where you can have
// precise control of the instructions inside the various loops.
void delay(char x)
{ // delay by individual NOPs (a variant on Duff's device)
// it should be PIC friendly but I don't have the toolchain to check.
switch (x)
{
case 7: NOP();
case 6: NOP();
case 5: NOP();
case 4: NOP();
case 3: NOP();
case 2: NOP();
case 1: NOP();
case 0:;
default:;
}
}
void delay2(char x)
{ // delay by countdown loop 2 cycles at a time
while (x--)
;
}
void main(void) {
// Set all pins at output
TRIS = 0x00;
GPIO = 0xff;
NOP(); NOP(); NOP(); NOP(); // so that first cycle is correct length
while (1) {
g();
}
// NB you may have to tweak it again if while(1) becomes a conditional test
}
You will have to run it in the simulator and then adjust the exact number of NOPs to obtain your desired waveform. It should be not too far off though.
Upvotes: 1