Interesting 'overhead' using free version of XC8.

Status
Not open for further replies.

Nigel Goodwin

Super Moderator
Most Helpful Member
Recently there's been a thread about controlling a bank of satellite receivers using a common remote control, but as the OP was never even able to confirm if it used RF remotes, IR, or either, it came to nothing.

However, this reminded me of when I did the same thing years ago, using assembler, based on my tutorial IR routines.

Ian Rogers very kindly converted my tutorials to C, and provides a link to them in his signature - but (as I recall) he'd never had a Sony remote or TV to try them with - although possibly they may have worked, as he uses the commercial (expensive) XC8 compiler which is considerably more optimised, and faster.

However, when I tried the free version of XC8 a number of years ago, they wouldn't work, as the timing was FAR too slow, where as the original assembler version allowed accuracy to 1uS even with a 4MHz clock. So following the recent thread, I thought I'd make a 'proper' effort to produce a version that works with the free version of XC8 - and so far I've got it working really well. For anyone who's interested?, I'm using the SMT timer on modern PIC's (the 16F18857 in this instance) to handle receiving the incoming IR data stream.

Now the issue for transmission was still timing - as you need 600uS, 1200uS and 2400uS series of 40KHz pulses.

One of my favourite simple timing routines is:

C:
void delayMs(int x)
{
    while(x--)
        __delay_ms(1);
}

Which gives a simple variable millisecond delay function, which works perfectly, as the millisecond resolution is enough to 'lose' the XC8 overheads. So I simply wrote a similar routine for uS.

C:
void delayUs(int x)
{
    while(x--)
        __delay_us(1);
}

Utter disaster - the huge overheads made it completely unusable - I did measure them, and it was BAD!!

My solution was to use a timer to do it, and while it works, I'm still less than impressed at the overhead Free XC8 imposes.

C:
void Timer1_us(unsigned int timeval)
{
    // Clearing IF flag.
    PIR4bits.TMR1IF = 0;
    TMR1_WriteTimer(65535-timeval);
    while(PIR4bits.TMR1IF == 0){};                                              // wait for timer interrupt flag
    T1CONbits.TMR1ON = 0;                                                       // disable timer
}

The timer is configured to count every uS, and the routine simply waits for the overflow flag.

The function works fine, but it has a 7uS overhead - and this is running with a 32MHz clock - the routine below toggles an I/O pin, feeding my scope, and producing a 100uS square wave.

C:
#define  TOGGLE_LED LATBbits.LATB7=~LATBbits.LATB7

while(1)
    {
        Timer1_us(93);
        TOGGLE_LED;
    }

I used this to trim my timing values, by subtracting 7 from each value, as in this example:

C:
// send start bit
        ir_mark(SONY_HDR_MARK-7);
        ir_space(SONY_HDR_SPACE-7); // -7us to allow for timer setup time

So if you're using the free version of XC8 (or perhaps even the paid for version?), keep an eye open for timing overheads.

Before anyone asks, I used another timer and PWM module to generate the 40KHz (or 38KHz, or 36KHz) pulses, and gate them ON and OFF by setting the PWM to 50% or 0%.

Here's an example of my current debug output, when pressing ON/OFF on a Sony TV remote (actually the TV ON/OFF button on a Sony DVD remote).

Code:
******************
PulseNum = 13
******************
Start bit:
Pulse Width 0 = 2508 - Space Width 0 = 508
Command code:
Pulse Width 1 = 1283 - Space Width 1 = 509
Pulse Width 2 = 685 - Space Width 2 = 509
Pulse Width 3 = 1283 - Space Width 3 = 509
Pulse Width 4 = 686 - Space Width 4 = 509
Pulse Width 5 = 1283 - Space Width 5 = 508
Pulse Width 6 = 686 - Space Width 6 = 509
Pulse Width 7 = 686 - Space Width 7 = 509
Device code:
Pulse Width 8 = 1284 - Space Width 8 = 508
Pulse Width 9 = 686 - Space Width 9 = 509
Pulse Width 10 = 686 - Space Width 10 = 509
Pulse Width 11 = 685 - Space Width 11 = 509
Pulse Width 12 = 686 - Space Width 12 = 0
DataValue Hex = 0x095 - Command Hex = 0x015 - Device Hex = 0x01

TV On/Off pressed
 
Last edited:
When I use delays in C. I have to include the overhead.

But as you said I have a "pro version", so a while loop takes about 11 cycles (especially using an int)

So I tend to make the delayUs shorter to compensate... If I run a pic at 4 Mhz I need to shift the looping variable down a tad.
Code:
void Delay_Us( int x )
  {
  x>>=2;  //  I tend to play with this until its roughly correct
  while(x--);
  }
 
I think it may be possible to make it to work using the output compare hardware & interrupt to do the timing, in a similar way to the multi servo routine I put on for someone a while ago?

It would use a single pin output rather than the port / shift register as with the multi-servo routine, but quite similar in concept.

eg. An array of bit length indication values and at each compare add the appropriate time (corresponding to the bit or gap length) to the time tracking variable and update the compare register from that, as well as the output set / reset / toggle mode as appropriate for the bit.
Plus it could have a Busy/Idle flag bit to sync with the main program, which the servo code does not ned as it runs endlessly.

The start routine would set the time tracking variable to eg. timer + 100uS and update the compare register with that, initialise the array pointer, set the busy flag and enable the compare interrupt.

You could use actual times in multiples of the timer clock rate in the array, or eg. 1 - 4 for multiples of 600uS, with a higher bit set to indicate o/p on rather than a gap. A zero for the end of the array list, with that turning the output off and disabling the compare interrupt.

As the hardware is controlling the output, interrupt latency is not an issue as long as the new settings can be applied and the interrupt routine exit before the next compare should occur.

If it can work like that, it's also a totally transparent background system once each transmission is triggered. The array values could be predefined constants or built on the fly before transmission, depending the program requirement.
 
The free XC8 also does a lot of moving a non specific file to 'W' and back to itself again just to waste time.

I noticed it while trying to get to grips with C a some time back.
 
With the free version of XC8 make sure you're using the O2 level. Well, use whatever level you want, but O2 is the highest level that is free.

In MPLABX v5.35 it's:
Project Properties ->
XC8 Global Options ->
XC8 Compiler ->
Option categories: Optimizations ->
Optimization level: 2
 
The free XC8 also does a lot of moving a non specific file to 'W' and back to itself again just to waste time.
I thought that too but found out that all compilers do that as it's the simplest way to do it and then the optimiser takes care of the redundant instructions. I must see what optimisation level 2 does.

Mike.
 
As far as I know I'm using Level 2, unless it has altered the settings? - I'll check when I get home.
 
DONT!! Use optimization while debugging.. You will lose hair very quickly...

Stepping through code after the object file has been highly optimized is a very weird sensation.
 
Well, just got home, just checked - the optimisation level had mysteriously set itself to 0 ?.

So I've set it back to 2, just tried it, not the slightest bit of difference - still has a 7uS overhead
 
Status
Not open for further replies.
Cookies are required to use this site. You must accept them to continue using the site. Learn more…