CCS C Software and Maintenance Offers
FAQFAQ   FAQForum Help   FAQOfficial CCS Support   SearchSearch  RegisterRegister 

ProfileProfile   Log in to check your private messagesLog in to check your private messages   Log inLog in 

CCS does not monitor this forum on a regular basis.

Please do not post bug reports on this forum. Send them to support@ccsinfo.com

Interrupt during delay_ms
Goto page 1, 2  Next
 
Post new topic   Reply to topic    CCS Forum Index -> General CCS C Discussion
View previous topic :: View next topic  
Author Message
pumazzz



Joined: 19 Oct 2011
Posts: 12

View user's profile Send private message

Interrupt during delay_ms
PostPosted: Thu Oct 20, 2011 3:02 am     Reply with quote

I am using PIC16f88 (PIC16f88 to drive a RF transceiver), and the CCS version is 4.032.

I have a question about timing when an external interrupt happens during the delay_ms function.

Here lists part of my code for the reference.

Code:
#include <16F88.h>
#FUSES INTRC, NOWDT, NOPROTECT ,NOLVP,NOBROWNOUT
#use delay(clock=8000000)
#use fast_io (A)
#use fast_io (B)

#int_ext
void interrupt()
{
irq_clear_transceiver(); // about 80us, by using SPI communication
}

void main()
{
setup_oscillator(OSC_8MHZ);
ext_int_edge(H_TO_L);
enable_interrupts(GLOBAL);
enable_interrupts(INT_EXT);

PIC_init();
RF_transceiver_init();
while(1)
{
   write_tx_payload_to_transceiver(pData, 5, true); //about 190us
   output_high(PIN_A1); //at this moment the time is T_A1
   output_low(PIN_A1);
   delay_ms(3);
}
}


The function write_tx_payload_to_transceiver() is used to send payload to transceiver’s TXFIFO and trigger the transceiver to transmit packet. After executing this function, the PIC will enter into delay_ms function for 3ms. During this period, an interrupt will happen at T1 (T1<3ms, so the time interval between T_A1 and T1 equals T=T_A1-T1), in the interrupt service routine, a SPI communication function is used to clear interrupt bit flag on transceiver’s register, which will last about 80us.

Then I measure the time interval between the two PIN_A1, the theory value should be as the following:
T + 80 (interrupt service routine) + 3000 – T + 190 = 3270 us
But the measurements show that the time between the two PIN_A1 is about 3340 us.

As I am doing the accurate simulation to compare the measurements, so it is necessary for me to know why this phenomenon happens. Thanks very much your reply.
FvM



Joined: 27 Aug 2008
Posts: 2337
Location: Germany

View user's profile Send private message

PostPosted: Thu Oct 20, 2011 4:06 am     Reply with quote

You are asking why your assumptions are apparently wrong. But where do you get the numbers of expected execution time?
Did you consider the total overhead of interrupt processing?
temtronic



Joined: 01 Jul 2010
Posts: 9174
Location: Greensville,Ontario

View user's profile Send private message

PostPosted: Thu Oct 20, 2011 5:23 am     Reply with quote

To actually KNOW as to WHY it takes x us instead of y us, all you need do is dump out the listing of the program and 'play computer'.
First, compile the code with a 4MHz clock that way every machine cycle is one microsecond(makes for easy math while playing computer !).
On the printout ( the listing), draw a couple of columns on the right side.
Read every line of assembled code,lookup the instruction(every data sheet has a chapter on them) and write beside that line the number of cycles that line takes.Really, it doesn't take too long !!
When all done, just add up the column of 'cycles' and that's the time it takes.Keep in mind some instructions take 2 cycles(jump, goto,etc. they're all very well documented) so you'll have to play 'computer' to fine tune the total number of cycles,hence overall time.

Yes, you could use some 'program' to do this BUT if YOU do the work, you'll KNOW WHY it takes the time it does.By educating yourself, you'll see ways to speed up the program, cut tighter code, in the end LEARN more than the other guy !

BTW, your '//about 190us' comment... is not accurate,kinda like saying 'sortof pregnent'. You can figure out exactly how long that function takes.'About' could mean 180, 199,192,201,+ or - 3,14,7 us ????
pumazzz



Joined: 19 Oct 2011
Posts: 12

View user's profile Send private message

PostPosted: Thu Oct 20, 2011 5:42 am     Reply with quote

FvM wrote:
You are asking why your assumptions are apparently wrong. But where do you get the numbers of expected execution time?
Did you consider the total overhead of interrupt processing?




Thanks very much for you quick reply. The following is some complementary information.

The expected execution time written in the comment is tested by the oscilloscope, and the time when the interrupt happens during the delay_ms(3) function is from the datasheet of the transceiver.

According to the datasheet of transceiver, there are five states going to happen after the write_tx_payload_to_transceiver() function, they are TX_Settling(fixed time)--->TX(depend on the number of bytes to transmit)--->RX_ACK_Settling(fixed time)--->RX_ACK(fixed time)--->ISR_Settling(after 6us the ISR bit flag on transceiver will be automatically set and as the external interrupt for PIC))

I have no idea of the overhead interrupt processing, it seems that PIC only needs several instruction cycles to response to the interrupt, so I think the difference about 70us~80us between my assumption and the measurements can’t be the overhead of interrupt.

Maybe what I think was wrong, and thank you for pointing out, as this question makes me confused for some times.
temtronic



Joined: 01 Jul 2010
Posts: 9174
Location: Greensville,Ontario

View user's profile Send private message

PostPosted: Thu Oct 20, 2011 8:58 am     Reply with quote

You don't give the info on the 'transceiver' or the actual code of the 'transmission' function.
We have NO way of knowing that the numbers you've supplied, from the transceiver mfr. are accurate. You hint that it's an SPI interface though. There can be 'delays' due to board layout, trace dimensions, power supply levels, etc.
Also the 'simulation' and the REAL World will NOT be the same. Too many factors are not considered in a simulation, so the numbers 'calculated' there can easily be off by 20-50%.

You should grab real hardware, cut code and test/measure what happens.
Have the PIC do all the work, setting up a simple counter/timer function to measure the time between two 'transmissions'. The 'transmission' can be replaced by a simple 'delay_us(190)'. That would give you a consistent 'transmission' time, unlike the real hardware.
There are examples that CCS supplies, that could be easily modified to time and display the interval.
PCM programmer



Joined: 06 Sep 2003
Posts: 21708

View user's profile Send private message

PostPosted: Thu Oct 20, 2011 1:36 pm     Reply with quote

Quote:
irq_clear_transceiver();

What's in this routine ? Are there any delay statements in it ?


Quote:

T + 80 (interrupt service routine) + 3000 – T + 190 = 3270 us

But the measurements show that the time between the two PIN_A1 is about 3340 us.

So your delta is 70 us. (3340 - 3270 = 70) That's what you want to
explain. Does the "80" include the CCS interrupt handler ? I looked at
the .LST file, and including the latency of the hardware interrupt, I count a
total of 58 instruction cycles for interrupt overhead. That's 29 us at 8 MHz.
That's just the overhead. It does not include any user code that you put
inside the #int_ext routine. So your remaining delta that is unaccounted
for so far, is 41 us (70 - 29 = 41). This is with vs. 4.032.

You didn't show all your code. But my advice would be to simplify the
problem by substituting "dummy" routines for the functions in your post.
I've done that in the code below. Notice how I put in delay_cycles()
statements instead of delay_us() statements. I'm not using any CCS
library routines to do the emulation delays. That way, there won't be
any effects from the compiler "disabling interrupts to prevent re-entrancy".
You can do a more pure analysis this way.

So run your timing tests again, using this code. See what you get.
I've set the internal delays inside each dummy routine at the nominal
values from your post. In fact, I don't know how you measured the
duration of those routines, so you may want to adjust the delay_cycles()
parameters slightly so they match your original test delay times that you
measured. Each parameter is "times 2" because at 8 MHz, there are 2
cycles per us. So if I want to delay 80 us, then I must delay for 160
cycles.

Also, there is some function overhead to pass parameters to the functions.
Are you measuring that ? I wonder. You didn't really show or tell your
method for measuring execution times.

Also, one more thought. Did you measure the 3 ms delay ? With usec
accuracy ? Do you know for a fact, that it's really exactly 3000 us long ?

Code:

#include <16F88.h>
#FUSES INTRC, NOWDT, NOPROTECT ,NOLVP,NOBROWNOUT
#use delay(clock=8000000)
#use fast_io (A)
#use fast_io (B)


void irq_clear_transceiver(void)
{
delay_cycles(80 *2);   // 80 us delay   
}

//-------------------------------------
int8 write_tx_payload_to_transceiver(char *pData, int8 len, int8 option)
{
delay_cycles(190 *2);   // 190 us delay
}

//-------------------------------------
#int_ext
void interrupt()
{
irq_clear_transceiver();
}

void main()
{
char data[20];
char *pData;

pData = data;

setup_oscillator(OSC_8MHZ);
ext_int_edge(H_TO_L);
enable_interrupts(GLOBAL);
enable_interrupts(INT_EXT);

//PIC_init();
//RF_transceiver_init();

while(1)
{
   write_tx_payload_to_transceiver(pData, 5, true); //about 190us
   output_high(PIN_A1); //at this moment the time is T_A1
   output_low(PIN_A1);
   delay_ms(3);
}
}
temtronic



Joined: 01 Jul 2010
Posts: 9174
Location: Greensville,Ontario

View user's profile Send private message

PostPosted: Thu Oct 20, 2011 1:43 pm     Reply with quote

also...
I just noticed..

you're using the internal rc oscillator

NOT a good choice for precise timing.

Read the datasheet section on oscillators, check the charts,....more homework !!

For any real accuracy, you'll need to use an external crystal,faster is better.
pumazzz



Joined: 19 Oct 2011
Posts: 12

View user's profile Send private message

PostPosted: Fri Oct 21, 2011 2:49 am     Reply with quote

I post my code in the following in order that you can have a better analysis

Code:

///clear all interrupt flag bit in transceiver’s status register
irq_clear_transceiver()
{
   unsigned char data = transceiver_STATUS_RX_DR | transceiver_STATUS_TX_DS | transceiver_STATUS_MAX_RT;
   transceiver_write_register_interrupt(transceiver_STATUS, &data, 1);
}

unsigned char transceiver_write_register_interrupt(unsigned char regnumber, unsigned char * data, unsigned int len)
{
        return transceiver_execute_command_interrupt(transceiver_W_REGISTER | (regnumber & transceiver_W_REGISTER_DATA), data, len, false);
}

unsigned char transceiver_execute_command_interrupt(unsigned char instruction, unsigned char * data, unsigned int len, bool copydata)
{
        unsigned char status;
        transceiver _clear_csn(); // output_low(PIN_A4);
        status = instruction;
        transceive_spi_send_read_interrupt(&status, 1, true);
        transceiver_spi_send_read_interrupt(data, len, copydata);
        transceiver_set_csn(); // output_high(PIN_A4);
        return status;
}

void transceive_spi_send_read_interrupt(unsigned char * data, unsigned int len, bool copydata)

        unsigned int count;
        unsigned char tempbyte;

        for(count = 0; count < len; count++)
        {
                if(copydata != false)
                        data[count] = spi_read(data[count]);
                else
                {
                        tempbyte = data[count];
                        spi_write(tempbyte);
                }
        }
}


unsigned char write_tx_payload_to_transceiver(unsigned char * data, unsigned int len, bool transmit)
{
        unsigned char status;
        status = transceiver_execute_command(transceiver_W_TX_PAYLOAD, data, len, false);
        if(transmit == true)
{
                transceiver_set_ce(); // output_high(PIN_A2);
                delay_us(10);
                transceiver_clear_ce(); // output_low(PIN_A2);
}
        return status;
}



transceiver_execute_command() and transceive_spi_send_read()
are the same with transceiver_execute_command_interrupt() and transceive_spi_send_read(), which is to avoid Interrupts disabled to prevent re-entrancy warning.

The way of measuring execution time for irq_clear_transceiver() and write_tx_payload_to_transceiver() is by measuring the interval of PIN_A1 (the CSN signal, this signal must be set low in order to do the SPI communication between PIC and transceiver).
pumazzz



Joined: 19 Oct 2011
Posts: 12

View user's profile Send private message

PostPosted: Fri Oct 21, 2011 2:54 am     Reply with quote

PCM Programmer wrote:
I looked at
the .LST file, and including the latency of the hardware interrupt, I count a
total of 58 instruction cycles for interrupt overhead. That's 29 us at 8 MHz.
That's just the overhead. It does not include any user code that you put
inside the #int_ext routine. So your remaining delta that is unaccounted
for so far, is 41 us (70 - 29 = 41). This is with vs. 4.032.


When I refer to the .LST file, I have no idea which part is the instruction cycle for the interrupt overhead, and thanks very much for pointing out.
pumazzz



Joined: 19 Oct 2011
Posts: 12

View user's profile Send private message

PostPosted: Fri Oct 21, 2011 3:11 am     Reply with quote

temtronic wrote:
also...
I just noticed..

you're using the internal rc oscillator

NOT a good choice for precise timing.

Read the datasheet section on oscillators, check the charts,....more homework !!

For any real accuracy, you'll need to use an external crystal,faster is better.



I tried to test the accuracy of internal clock in different ways, the first way is as the following


Code:
while(1)
{
   ///Ten times, using 8MHz internal clock
output_high(PIN_A1);
   output_low(PIN_A1);
   ………………………
   ………………………
   output_high(PIN_A1);
   output_low(PIN_A1);
}



I find that the time for a high level and a low level is 1us.

Then I tested delay_us(10) by using 25us scale on the oscilloscope, but found out the time is 12us

Then by testing delay_ms(1) with 250us scale on the oscilloscope, I found out the time is 1.010ms.
temtronic



Joined: 01 Jul 2010
Posts: 9174
Location: Greensville,Ontario

View user's profile Send private message

PostPosted: Fri Oct 21, 2011 5:03 am     Reply with quote

So your numbers mean that delay_us(1) is off by +2 us, or +20% !!
and delay_ms(1) is off by .01ms or +1%!

Given those numbers, in a simple program, executing 5 delay_us(1); in a row would yield an overall delay of 6us not the desired 5us.
10 delay_ms(1) in a row would give an error of 100us.

These compounded errors can and will seriously affect the performance of the 'system',especially any serial data transfers(bit banging UARTs !),over long lines at high data rates.
bkamen



Joined: 07 Jan 2004
Posts: 1611
Location: Central Illinois, USA

View user's profile Send private message

PostPosted: Fri Oct 21, 2011 7:56 am     Reply with quote

temtronic wrote:
To actually KNOW as to WHY it takes x us instead of y us, all you need do is dump out the listing of the program and 'play computer'.



Or use the 'stopwatch' feature in MPSIM. (MPLAB's Simulator)
_________________
Dazed and confused? I don't think so. Just "plain lost" will do. :D
PCM programmer



Joined: 06 Sep 2003
Posts: 21708

View user's profile Send private message

PostPosted: Fri Oct 21, 2011 12:32 pm     Reply with quote

Quote:
Then I tested delay_us(10) by using 25us scale on the oscilloscope, but found out the time is 12us

Then by testing delay_ms(1) with 250us scale on the oscilloscope, I found out the time is 1.010ms

Post your test programs for this. Complete (but short).
pumazzz



Joined: 19 Oct 2011
Posts: 12

View user's profile Send private message

PostPosted: Sat Oct 22, 2011 7:22 am     Reply with quote

bkamen wrote:
temtronic wrote:
To actually KNOW as to WHY it takes x us instead of y us, all you need do is dump out the listing of the program and 'play computer'.



Or use the 'stopwatch' feature in MPSIM. (MPLAB's Simulator)


I wish I can use stopwatch in MPLAB, but hardware SPI communication is used in the function, so MPLAB's stopwatch feature can't be used.
pumazzz



Joined: 19 Oct 2011
Posts: 12

View user's profile Send private message

PostPosted: Sat Oct 22, 2011 7:39 am     Reply with quote

PCM programmer wrote:
Post your test programs for this. Complete (but short).



For delay_ms(1)
It seems that I use the following test program:
Code:
while(1)
{
output_high(PIN_A1);
delay_ms(1);
output_low(PIN_A1);
delay_us(100);
}



For delay_us(10)
Code:
while(1)
{
output_high(PIN_A1);
delay_us(10);
output_low(PIN_A1);
delay_us(500); //I can’t remember clearly the accurate time
}


And today, I test the following program:
Code:
while(1)
{
   output_high(PIN_A1);
   output_low(PIN_A1);
   
   transceiver_write_tx_payload(pData, 5, false);
   
   output_high(PIN_A1);
   output_low(PIN_A1);
   
   delay_ms(3);
}


Here if I use false as the third parameter in transceiver_write_tx_payload() function, PIC will only send payloads to transceiver’s TXFIFO, but will not change transceiver’s state, so the interrupt won’t happen. Then the measurement showed that 3.020ms for the delay_ms(3), 2.020ms for delay_ms(2), and 1.010ms for the delay_ms(1).

And another thing is that can you show me how to find the interrupt overhead instruction cycle in .LST file? Your last post told me that 58 instruction cycles were found, but I had no idea how to find out it, and thank you very much for pointing out.[/u]
Display posts from previous:   
Post new topic   Reply to topic    CCS Forum Index -> General CCS C Discussion All times are GMT - 6 Hours
Goto page 1, 2  Next
Page 1 of 2

 
Jump to:  
You cannot post new topics in this forum
You cannot reply to topics in this forum
You cannot edit your posts in this forum
You cannot delete your posts in this forum
You cannot vote in polls in this forum


Powered by phpBB © 2001, 2005 phpBB Group