![](templates/subSilver/images/CCSLogo.jpg) |
![CCS C Software and Maintenance Offers](templates/subSilver/images/forumAd6.jpg) |
View previous topic :: View next topic |
Author |
Message |
Scott Anderson Guest
|
CCP1 Capture Interrupt Latency (18F452) |
Posted: Mon Jan 20, 2003 2:58 pm |
|
|
I s'pose y'all know about this already, but I just found out and thought I'd share.
Background:
I am using the PIC to measure speed and use the TIMER1 overflow interrupt to extend the range of TIMER1. I divide the clock by 8 and capture the timer value when there is a tachometer transition on the CCP1 pin.
The effect:
If the catured timer value is within 12 counts but still less than 0xFFFF, the timer overflow interrupt has already been serviced. This happens whether I set the priority for the TIMER1 overflow interrupt higher or lower than the CCP1 interrupt.
The solution:
You can't count on this behaving in a consistent manner, so you have to mistrust the extended count any time you are near the beginning or end of the timer. I can afford to ignore some measurements, so that is how I deal with any sudden drop in speed due to an extra count in the overflow count variable.
___________________________
This message was ported from CCS's old forum
Original Post ID: 10831 |
|
![](templates/subSilver/images/spacer.gif) |
PCM programmer
Joined: 06 Sep 2003 Posts: 21708
|
Re: CCP1 Capture Interrupt Latency (18F452) |
Posted: Mon Jan 20, 2003 3:20 pm |
|
|
:=I s'pose y'all know about this already, but I just found out and thought I'd share.
:=Background:
:=I am using the PIC to measure speed and use the TIMER1 overflow interrupt to extend the range of TIMER1. I divide the clock by 8 and capture the timer value when there is a tachometer transition on the CCP1 pin.
:=
:=The effect:
:=If the catured timer value is within 12 counts but still less than 0xFFFF, the timer overflow interrupt has already been serviced. This happens whether I set the priority for the TIMER1 overflow interrupt higher or lower than the CCP1 interrupt.
------------------------------------------------------------
I recently went through this problem. Here is the solution
I came up with. There may be a better way, but this is what I
came up with in the allotted time. I did this for a 16F628.
//------------------------------------------------------------
// MACROS
// This macro allows us to insert a byte into the specified
// offset of a 16 or 32 bit variable. Example:
// *BytePtr(temp_long, 2) = 0x55;
// If temp_long is 0, the above line will make it become:
// 0x00550000
#define BytePtr(var, offset) (char *)(&var + offset)
// The Timer1 interrupt increments an 8-bit variable which
// extends the timer to 24 bits. We need this so we can
// avoid having to switch the Timer pre-scaler between "low"
// and "high" rpm ranges.
#int_timer1
void timer1_isr(void)
{
gc_timer1_extension++;
}
//------------------------------------------------------
// When we get a CCP interrupt, read the CCP register and
// save it in a global variable.
#int_ccp1
void ccp1_isr(void)
{
char timer_ext_copy;
int32 current_ccp;
static int32 old_ccp = 0;
// Set flag to indicate that we did a capture.
gc_capture_flag = TRUE;
current_ccp = (int32)CCPR1_REG; // Read the current CCP
// Get local copy of the timer ext.
timer_ext_copy = gc_timer1_extension;
// Check if a Timer1 interrupt is pending. If so, check if
// the CCP capture occurred before or after the Timer rolled
// over. We can tell if it occurred after it rolled over, if
// the CCP's MSB is zero. ie., if the CCP is somewhere between
// 0x0000 and 0x00FF.
// We know that we can just check if the MSB = 0x00, because
// it takes about 30 us to get into this ISR. (Using a 4 Mhz
// crystal on a 16F628). The timer increments at 1 us per
// count, so 0xFF = 255 us.
// Actually, to be safer, I'll give it 2 MSB counts, which is
// 511 us. That way, if I lengthen any of the other ISR's,
// we'll still be able to detect the roll-over OK.
// If the timer did roll over after we got a CCP interrupt,
// then we need to increment the timer extension byte, that we
// save. We have to do that because the CCP interrupt has
// priority, and so it executes before the Timer isr can
// execute and increment the extension.
// (Designing the code with the priority switched doesn't help.
// You still have the same type of problem. With CCP first,
// the fix is easier).
//
// Was CCP captured after Timer1 wrapped ?
// If so, increment the copy of the timer ext.
if(TMR1IF_BIT)
{
if(*BytePtr(current_ccp, 1) < 2)
timer_ext_copy++;
// Since we know a timer interrupt is pending, let's just
// handle it here and now. That saves a little load off
// the processor.
gc_timer1_extension++; // Increment the real timer extension
TMR1IF_BIT = 0; // Then clear the Timer1 interrupt
}
// Insert the timer extension into the proper place in the
// 32-bit CCP value.
// ie., Insert it into location "EE" as follows: 0x00EEnnnn
// (nnnn = the CCP).
*BytePtr(current_ccp, 2) = timer_ext_copy;
// Because we're using unsigned math, we don't have to worry
// if the current value is less than the old. The result is
// always the absolute value of the difference. The only way
// there could be a problem is if the new CCP value had rolled
// over twice. But with a 24-bit value, and a Timer
// pre-scalar of 1, that's 16.7 seconds. That's way beyond any
// practical value.
// Edited on Jan. 2, 2004: There was a bug in this routine,
// because I was promoting a 24-bit value to a 32-bit data type,
// but the upper byte was always left = 0. This caused a problem
// with the 32-bit subtraction when the 24-bit value rolled over past 0.
// Kenny spotted this error and provided a fix in a PM to me.
// I have commented out the original line, and inserted his fix, below.
//g32_ccp_delta = current_ccp - old_ccp;
g32_ccp_delta = (current_ccp > old_ccp) ? current_ccp - old_ccp : current_ccp + (0x1000000 - old_ccp);
// Save the current ccp value for next time.
old_ccp = current_ccp;
}
___________________________
This message was ported from CCS's old forum
Original Post ID: 10833
Last edited by PCM programmer on Fri Jan 02, 2004 1:56 pm; edited 1 time in total |
|
![](templates/subSilver/images/spacer.gif) |
Hans Wedemeyer Guest
|
Re: CCP1 Capture Interrupt Latency (18F452) |
Posted: Mon Jan 20, 2003 4:03 pm |
|
|
If your TIMER does an interrupt BEFORE 0XFFFF there has to be something wrong with hardware(PIC18F452)!
What you are saying is that the interrupt happens before the register overflows ?
The Interrupt can't(should not)happen before TIMER1 counter overflows.
Is there an errata sheet about this at MicroChip ?
___________________________
This message was ported from CCS's old forum
Original Post ID: 10836 |
|
![](templates/subSilver/images/spacer.gif) |
Scott Anderson Guest
|
Re: CCP1 Capture Interrupt Latency (18F452) |
Posted: Tue Jan 21, 2003 11:29 am |
|
|
The timer does not interrupt before it reaches 0xffff. The capture happens before the overflow, but the overflow interrupt sometimes happens before the capture interrupt can be processed. This is just an architectural characteristic of the chip.
:=If your TIMER does an interrupt BEFORE 0XFFFF there has to be something wrong with hardware(PIC18F452)!
:=
:=What you are saying is that the interrupt happens before the register overflows ?
:=
:=The Interrupt can't(should not)happen before TIMER1 counter overflows.
:=
:=Is there an errata sheet about this at MicroChip ?
:=
:=
___________________________
This message was ported from CCS's old forum
Original Post ID: 10856 |
|
![](templates/subSilver/images/spacer.gif) |
Hans Wedemeyer Guest
|
Re: CCP1 Capture Interrupt Latency (18F452) |
Posted: Thu Jan 23, 2003 9:00 am |
|
|
I don't know the overall design of your application and this may not be a good solution for you.
There is another solution that comes to mind, that of using a rolling Counter and don't use TIMER1 interrupts to extend the Timer1 counter.
If you need the Timer1 interrupts for something else this still works becasue we never need to bother about incrementing Timer1 count (i.e. Extending it) in the Timer1 ISR.
I've used this for years and it has never failed me.
BTW reading timers with PIC18 is a LOT faster than with PIC16.
// some unions for convenient access
struct HighLow32
{
int16 h;
int16 l;
};
union HL32
{
INT32 L;
struct HighLow32 hl;
};
int16 OldCount;
union HL32 Count;
int Update;
CCP1_isr()
{
Count.hl.l = Get_Timer1();
Update++; // just a convenient update flag
}
Somewhere else :-)
if ( Update > 0 )
{
if( OldCount > Count.hl.l ) // then Timer1 rolled over
{
Count.hl.h++;
}
OldCount = Count.hl.l;
// now do something with the Full 32 bit count value
Count.L;
Update=0; // allow another reading or whatever
}
___________________________
This message was ported from CCS's old forum
Original Post ID: 10931 |
|
![](templates/subSilver/images/spacer.gif) |
PCM programmer
Joined: 06 Sep 2003 Posts: 21708
|
Re: CCP1 Capture Interrupt Latency (18F452) |
Posted: Thu Jan 23, 2003 1:16 pm |
|
|
:=CCP1_isr()
:={
:= Count.hl.l = Get_Timer1();
:= Update++; // just a convenient update flag
:=}
:=
---------------------------------------------------
What if the timer rolls over several times between CCP
interrupts ?
___________________________
This message was ported from CCS's old forum
Original Post ID: 10946 |
|
![](templates/subSilver/images/spacer.gif) |
Hans Wedemeyer Guest
|
Re: CCP1 Capture Interrupt Latency (18F452) |
Posted: Thu Jan 23, 2003 4:41 pm |
|
|
:=What if the timer rolls over several times between CCP
:=interrupts ?
Sure as I said I did not know the application and it may not suitable for his application:-
The limitation is obvious and in practice with a Div 1 setting for Timer1 and the 4mHz clock it will roll over every 65mS, so the slowest rate on "CCP"1 should not exceed that. In practice
if there is enough time to process the comparsion in some other loop ( typically main()) the slowest CCP1 can begin to approach 2*65mS (1.99 * is better )
Use a Div8 setting for Timer1 and 4mHz clock and the roll over time is that much longer...
This will avoid Timer1 rolling over twice, which will ensure the assumption is still true when the comparison is made elsewhere.
Of course the comparison can be done in the CCP1 ISR in which case the high frequency will suffer because if the extended handling time in the ISR is too long.
All in all this does work, and avoiding any ISR is a bonus !
___________________________
This message was ported from CCS's old forum
Original Post ID: 10952 |
|
![](templates/subSilver/images/spacer.gif) |
asseel
Joined: 13 Jun 2009 Posts: 6
|
|
![](templates/subSilver/images/spacer.gif) |
PCM programmer
Joined: 06 Sep 2003 Posts: 21708
|
|
![](templates/subSilver/images/spacer.gif) |
asseel
Joined: 13 Jun 2009 Posts: 6
|
|
Posted: Sun Jun 14, 2009 2:53 pm |
|
|
thank you
i will read them |
|
![](templates/subSilver/images/spacer.gif) |
gurdeepsembi
Joined: 22 Dec 2010 Posts: 14 Location: UK
|
|
Posted: Wed Dec 22, 2010 6:11 am |
|
|
Hi PCM programmer,
I am trying to read the frequency of a square wave signal generated by a gear tooth sensor. The freq range I need to measure is 0-20 Hz. I am using a PIC18F2520 running at 8MHz.
I have used your code which extends timer1 to 24 bits, but I am getting errors when I try to compile the code. Errors are:
Undefined identifier CCPR1_REG
Undefined identifier TMR1IF_BIT
We are using the PCH compiler version 4.114
Any idea why I am getting these errors? Do I have to declare these variables somewhere? I thought that the header file would contain these variables but when I looked in 18F2520.h that is supplied with the compiler, there is no reference to these variables. Is there another header file I need to include?
My code is:
Code: |
#include <18F2520.h>
#device ICD=TRUE //disable this for production as it removes code protection
//#device adc=8
#FUSES WDT //Use Watch Dog Timer
#FUSES WDT4096 //Watch Dog Timer uses 1:4096 Postscale - 1 WD cycle is 4ms - With postscale, timeout is 16.38 secs
#FUSES INTRC_IO //Internal RC Osc, no CLKOUT
#FUSES FCMEN //Fail-safe clock monitor enabled
#FUSES BROWNOUT //Enable brownout reset
#FUSES BORV27 //Brownout reset at 2.7V
#FUSES NOPUT //No Power Up Timer
#FUSES NOCPD //No EE protection
#FUSES STVREN //Stack full/underflow will cause reset
#FUSES DEBUG //Debug mode for use with ICD
#FUSES NOLVP //No low voltage prgming, B3(PIC16) or B5(PIC18) used for I/O
#FUSES NOWRT //Program memory not write protected
#FUSES NOWRTD //Data EEPROM not write protected
#FUSES NOWRTC //configuration not registers write protected
#FUSES IESO //Internal External Switch Over mode enabled
#FUSES NOEBTR //Memory not protected from table reads
#FUSES NOEBTRB //Boot block not protected from table reads
#FUSES MCLR //Master Clear pin enabled
#FUSES NOPROTECT //Code not protected from reading
#FUSES NOCPB //No Boot Block code protection
#FUSES NOWRTB //Boot block not write protected
#use delay(clock=8000000,RESTART_WDT)
#define OP_BUZZER_OB PIN_A4 // On Board Buzzer
// This global variable holds the time interval
// between two consecutive rising edges of the
// input signal.
int32 isr_ccp_delta;
int32 current_ccp_delta;
int16 frequency;
int gc_capture_flag = 0;
int32 g32_ccp_delta;
int32 gc_timer1_extension;
// When a rising edge occurs on the input signal, the CCP1 will 'capture' the value of Timer1
// at that moment. Shortly after that, a CCP1 interrupt is generated and the following isr
// is called. In the isr, we read the 'captured' value of Timer1. We then subtract from it the
// Timer1 value that we 'captured' in the previous interrupt. The result is the time interval
// between two rising edges of the input signal. This time interval is then converted to a frequency
// value by code in main().
// code from http://www.ccsinfo.com/forum/viewtopic.php?t=906
//------------------------------------------------------------
// MACROS
// This macro allows us to insert a byte into the specified
// offset of a 16 or 32 bit variable. Example:
// *BytePtr(temp_long, 2) = 0x55;
// If temp_long is 0, the above line will make it become:
// 0x00550000
#define BytePtr(var, offset) (char *)(&var + offset)
// The Timer1 interrupt increments an 8-bit variable which
// extends the timer to 24 bits. We need this so we can
// avoid having to switch the Timer pre-scaler between "low"
// and "high" rpm ranges.
#int_timer1
void timer1_isr(void)
{
gc_timer1_extension++;
}
//------------------------------------------------------
// When we get a CCP interrupt, read the CCP register and
// save it in a global variable.
#int_ccp1
void ccp1_isr(void)
{
char timer_ext_copy;
int32 current_ccp;
static int32 old_ccp = 0;
// Set flag to indicate that we did a capture.
gc_capture_flag = TRUE;
current_ccp = (int32)CCPR1_REG; // Read the current CCP
// Get local copy of the timer ext.
timer_ext_copy = gc_timer1_extension;
// Check if a Timer1 interrupt is pending. If so, check if
// the CCP capture occurred before or after the Timer rolled
// over. We can tell if it occurred after it rolled over, if
// the CCP's MSB is zero. ie., if the CCP is somewhere between
// 0x0000 and 0x00FF.
// We know that we can just check if the MSB = 0x00, because
// it takes about 30 us to get into this ISR. (Using a 4 Mhz
// crystal on a 16F628). The timer increments at 1 us per
// count, so 0xFF = 255 us.
// Actually, to be safer, I'll give it 2 MSB counts, which is
// 511 us. That way, if I lengthen any of the other ISR's,
// we'll still be able to detect the roll-over OK.
// If the timer did roll over after we got a CCP interrupt,
// then we need to increment the timer extension byte, that we
// save. We have to do that because the CCP interrupt has
// priority, and so it executes before the Timer isr can
// execute and increment the extension.
// (Designing the code with the priority switched doesn't help.
// You still have the same type of problem. With CCP first,
// the fix is easier).
//
// Was CCP captured after Timer1 wrapped ?
// If so, increment the copy of the timer ext.
if(TMR1IF_BIT)
{
if(*BytePtr(current_ccp, 1) < 2)
timer_ext_copy++;
// Since we know a timer interrupt is pending, let's just
// handle it here and now. That saves a little load off
// the processor.
gc_timer1_extension++; // Increment the real timer extension
TMR1IF_BIT = 0; // Then clear the Timer1 interrupt
}
// Insert the timer extension into the proper place in the
// 32-bit CCP value.
// ie., Insert it into location "EE" as follows: 0x00EEnnnn
// (nnnn = the CCP).
*BytePtr(current_ccp, 2) = timer_ext_copy;
// Because we're using unsigned math, we don't have to worry
// if the current value is less than the old. The result is
// always the absolute value of the difference. The only way
// there could be a problem is if the new CCP value had rolled
// over twice. But with a 24-bit value, and a Timer
// pre-scalar of 1, that's 16.7 seconds. That's way beyond any
// practical value.
// Edited on Jan. 2, 2004: There was a bug in this routine,
// because I was promoting a 24-bit value to a 32-bit data type,
// but the upper byte was always left = 0. This caused a problem
// with the 32-bit subtraction when the 24-bit value rolled over past 0.
// Kenny spotted this error and provided a fix in a PM to me.
// I have commented out the original line, and inserted his fix, below.
//g32_ccp_delta = current_ccp - old_ccp;
g32_ccp_delta = (current_ccp > old_ccp) ? current_ccp - old_ccp : current_ccp + (0x1000000 - old_ccp);
// Save the current ccp value for next time.
old_ccp = current_ccp;
}
//--------------------------------------------------------------------------------
// Sound on board buzzer
//--------------------------------------------------------------------------------
void sound_buzzer(int no_times, int16 time_delay)
{
int i = 0;
for (i=1;i<=no_times;++i)
{
output_high(OP_BUZZER_OB);
delay_ms(time_delay);
output_low(OP_BUZZER_OB);
delay_ms(time_delay);
}
}
void initialise_pic(void)
{
// Setup ports // 76543210
SET_TRIS_A( 0xC0 ); // 11000000 1 - Input, 0 - Output
SET_TRIS_B( 0xFF ); // 11111111 1 - Input, 0 - Output
SET_TRIS_C( 0xFF ); // 11111111 1 - Input, 0 - Output
//switch off buzzer
output_low(OP_BUZZER_OB);
//set all outputs OFF
output_low(OP_STAT);
output_low(OP_SAFETY_CHK);
output_low(OP_INV_1);
output_low(OP_INV_2);
output_low(OP_INV_3);
output_low(OP_TL_ENT);
output_low(OP_TL_EXT_BS);
// Setup Internal Clock
setup_oscillator(OSC_4MHZ);
// Setup Interrupts
enable_interrupts(INT_TIMER1);
// Setup Timer1 and CCP1 for Capture mode so that
// we can measure the input signal's frequency.
// The input signal comes from the CCP2 pin, which
// is connected to the CCP1 pin with a wire.
set_timer1(0);
setup_timer_1(T1_INTERNAL | T1_DIV_BY_1);
setup_ccp1(CCP_CAPTURE_RE);
// Clear the CCP1 interrupt flag before we enable
// CCP1 interrupts, so that we don't get an unwanted
// immediate interrupt (which might happen).
clear_interrupt(INT_CCP1);
enable_interrupts(INT_CCP1);
enable_interrupts(GLOBAL);
}
void measure_speed(void)
{
//code from http://www.ccsinfo.com/forum/viewtopic.php?t=29963&postdays=0&postorder=asc&start=0
// Now calculate the frequency.
disable_interrupts(GLOBAL);
current_ccp_delta = g32_ccp_delta;;
enable_interrupts(GLOBAL);
if(gc_capture_flag == TRUE)
{
frequency = (int16)((1000000L + (current_ccp_delta >> 1)) / current_ccp_delta);
//printf("%lu Hz, delta = %lx \n\r", frequency, current_ccp_delta);
gc_capture_flag = FALSE;
}
//else
//{
// printf("No signal\n\r");
// }
}
void main()
{
int restart_on_wdt = 0;
// check if restarting on watchdog timer
if (restart_cause() == WDT_TIMEOUT)
{
restart_on_wdt = 1;
}
else
{
restart_on_wdt = 0;
}
if (start_up_sequence == 1) // check if starting up
{
//config_mode_check (); // check if config mode needs to be enabled
initialise_pic (); // initialise the uC
start_up_sequence = 0; // reset bit so on next cycle uC not re-initialised
}
//setup watchdog timer
setup_wdt(WDT_ON);
//if restarted on watchdog then remain in this loop
//keep beeping to alert that there was an abnormal shutdown
if(restart_on_wdt == 1)
{
while(1)
{
restart_wdt();
sound_buzzer(1,500);
}
}
if(restart_on_wdt == 0)
{
while(1)
{
restart_wdt();
//determine_status();
//set_status_output();
//process_inputs();
measure_speed();
}
}
}
|
Thanks for your help.
Gurdeep |
|
![](templates/subSilver/images/spacer.gif) |
PCM programmer
Joined: 06 Sep 2003 Posts: 21708
|
|
Posted: Wed Dec 22, 2010 3:06 pm |
|
|
Quote: | Errors are:
Undefined identifier CCPR1_REG
Undefined identifier TMR1IF_BIT
|
Add the lines shown in bold below to your program:
Quote: |
int32 isr_ccp_delta;
int32 current_ccp_delta;
int16 frequency;
int gc_capture_flag = 0;
int32 g32_ccp_delta;
int32 gc_timer1_extension;
#byte CCPR1_REG = getenv("SFR:CCPR1")
#byte PIR1 = getenv("SFR:PIR1")
#bit TMR1IF = PIR1.0
|
|
|
![](templates/subSilver/images/spacer.gif) |
gurdeepsembi
Joined: 22 Dec 2010 Posts: 14 Location: UK
|
|
Posted: Thu Dec 23, 2010 5:34 am |
|
|
Hi PCM Programmer,
Thanks for your help. That worked, I just had to name the variable as TMR1IF_BIT instead of TMR1IF as that is what it is referred to as in the rest of the code.
Code: | #byte CCPR1_REG = getenv("SFR:CCPR1")
#byte PIR1 = getenv("SFR:PIR1")
#bit TMR1IF_BIT = PIR1.0 |
The code now compiles and I will test it to see if I can get the frequency readings.
Once again thanks a lot for your help, much appreciated.
Gurdeep |
|
![](templates/subSilver/images/spacer.gif) |
Flowmeter
Joined: 29 Oct 2013 Posts: 3
|
Problem with BytePtr |
Posted: Tue Oct 29, 2013 1:09 am |
|
|
Hi, I have used the code presented in this thread but have problems with the BytePtr
A quick test shows:
Code: |
#define BytePtr(var, offset) (char*)(&var + offset)
INT32 test0 = 0x0000ff00;
INT32 test1 = 0x0000ff00;
INT32 test2 = 0x0000ff00;
INT32 test3 = 0x0000ff00;
INT8 addOn=0x0f;
void main(void)
{
*BytePtr(test0,0)=addOn;
*BytePtr(test1,1)=addOn;
*BytePtr(test2,2)=addOn;
*BytePtr(test3,3)=addOn;
} |
Result from watch after processing *BytePtr(test3,3)=addOn;
Symbol - Address - Value - Comment
test0 - 064 - 0x0000FF0F - as expected
test1 - 068 - 0x0000FF00 - not sure what went wrong?!
test2 - 06C - 0x0000FF0F - written into wrong byte?!
test3 - 070 - 0x0000FF00 - not sure what went wrong?!
addOn - 074 - 0X0F - as expected
I am using a PIC18F67J50, MPLAB IDE v8.92, MPLAB C18 C v3.46, MPLINK v.4.48, MPASM v5.50, MPLIB v4.48
Would appreciate any help to understand, what is going wrong with the BytePtr. |
|
![](templates/subSilver/images/spacer.gif) |
PCM programmer
Joined: 06 Sep 2003 Posts: 21708
|
|
Posted: Tue Oct 29, 2013 1:35 am |
|
|
That code was written for a very old version of the CCS compiler, in which
all pointer arithmetic automatically defaulted to byte operations.
That's incorrect, and CCS did fix it. The BytePtr macro shown above
should be replaced with the following code. This will do the pointer
arithmetic correctly:
Code: | #define BytePtr(var, offset) (&(char)var + offset) |
|
|
![](templates/subSilver/images/spacer.gif) |
|
|
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
|