|
|
View previous topic :: View next topic |
Author |
Message |
PCM programmer
Joined: 06 Sep 2003 Posts: 21708
|
|
Posted: Sun Jan 06, 2008 3:36 pm |
|
|
The following code works. I orginally had the fuses set for a 20 MHz
crystal, but I changed it to a 12 MHz crystal since that's what you're using.
I don't have an 18F2550, but I do have a 18F4550, which is similar.
I used a B&K Function Generator, with a 5v p/p square wave going into
pin C2. The PIC is running at 5v. I put the B&K on the 5 Hz scale and
it worked OK at the very low input frequencies:
Quote: | 4 Hz
4 Hz
4 Hz
4 Hz
3 Hz
3 Hz
3 Hz
3 Hz
2 Hz
2 Hz
2 Hz
1 Hz
1 Hz
1 Hz
1 Hz
|
Then setting it for the 50 Hz range setting and
turning the knob:
Quote: |
4 Hz
4 Hz
4 Hz
4 Hz
6 Hz
10 Hz
14 Hz
17 Hz
19 Hz
21 Hz
23 Hz
25 Hz
28 Hz
29 Hz
31 Hz
33 Hz
38 Hz
41 Hz
51 Hz
51 Hz
51 Hz
|
I also set it for about 86 Hz and let it sit there. It doesn't ever display
"0 Hz". It works.
Quote: |
87 Hz
87 Hz
87 Hz
87 Hz
87 Hz
87 Hz
87 Hz
87 Hz
87 Hz
87 Hz
87 Hz
87 Hz
87 Hz
87 Hz
87 Hz
87 Hz
87 Hz
87 Hz
87 Hz
87 Hz
87 Hz
87 Hz
|
This program was tested with vs. 4.064:
Code: |
#include <18F4550.H>
#fuses HSPLL, PLL3, CPUDIV1, NOWDT, PUT, BROWNOUT, NOLVP // 12 MHz xtal
//#fuses HSPLL, PLL5, CPUDIV1, NOWDT, PUT, BROWNOUT, NOLVP // 20 MHz xtal
#use delay(clock=48000000)
#use rs232(baud=9600, xmit=PIN_C6, rcv=PIN_C7, ERRORS)
#priority CCP1, TIMER1
#define BytePtr(var, offset) (char *)(&(char *)var + offset)
#byte PIR1 = 0xF9E
#bit TMR1IF = PIR1.0
int8 gc_timer1_extension = 0;
int8 gc_capture_flag = FALSE;
int32 g32_ccp_delta;
//------------------------------------------------------
#int_timer1
void timer1_isr(void)
{
gc_timer1_extension++;
}
//------------------------------------------------------
#int_ccp1
void ccp1_isr(void)
{
char timer_ext_copy;
int32 current_ccp;
static int32 old_ccp = 0;
gc_capture_flag = TRUE;
current_ccp = (int32)CCP_1;
// Get local copy of the timer ext.
timer_ext_copy = gc_timer1_extension;
if(TMR1IF)
{
if(*BytePtr(current_ccp, 1) < 2) // Was CCP captured after Timer1 wrapped?
timer_ext_copy++; // If so, inc the copy of the timer ext.
// 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 = 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;
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;
}
//=======================
void main()
{
int16 frequency;
int32 current_ccp_delta;
set_timer1(0);
setup_timer_1(T1_INTERNAL | T1_DIV_BY_1);
setup_ccp1(CCP_CAPTURE_RE);
// Enable interrupts.
clear_interrupt(INT_TIMER1);
enable_interrupts(INT_TIMER1);
clear_interrupt(INT_CCP1);
enable_interrupts(INT_CCP1);
enable_interrupts(GLOBAL);
while(1)
{
disable_interrupts(GLOBAL);
current_ccp_delta = g32_ccp_delta;;
enable_interrupts(GLOBAL);
if(gc_capture_flag == TRUE)
{
frequency = (int16)((12000000L + (current_ccp_delta >> 1)) / current_ccp_delta);
printf("%lu Hz\n\r", frequency);
// printf("%lu Hz, delta = %lx \n\r", frequency, current_ccp_delta);
gc_capture_flag = FALSE;
}
else
{
printf("No signal\n\r");
}
delay_ms(500);
}
} |
|
|
|
ELCouz
Joined: 18 Jul 2007 Posts: 427 Location: Montreal,Quebec
|
|
Posted: Sun Jan 06, 2008 9:46 pm |
|
|
Dear PCM Programmer,
Gave me this with my pic18f2553...in less than 10 sec...
http://pages.infinit.net/ironz/capture.txt
but now with the 4550 i have a perfect working CCP function wow thanks !!
Maybe your code is pic dependant (some register are missing in the pic18f2553 or 2550?)
Anyway, you solve my problem!
I owe you one !
You should setup a paypal account, so we can all donate you some money for solving our problems hehe
Laurent
Mtl,Quebec[/url] |
|
|
deepakomanna
Joined: 06 Mar 2007 Posts: 92 Location: Pune,India
|
getting incorrect frequency... |
Posted: Mon Jan 07, 2008 5:24 am |
|
|
Dear Sir,
here i am using 16f913, MPLAB 7.5 Ver. & CCS PCM C Compiler, Version 3.249, 34534.Also i am testing this using ICD 2.
in my application external crystal is 32.768khz for timer1(Div by 1 prescalar) & 8 Mhz is used for internal
clock.As per previous threads i checked everything but still now
i am not getting exact freq. I calculate freq. as,
Quote: | isr_ccp_delta = (current_ccp > old_ccp) ? current_ccp - old_ccp : current_ccp + (0x100000 - old_ccp);
frequency = (int16)((32768L + (isr_ccp_delta >> 1)) / isr_ccp_delta);
|
So my timer 1 is running at 1 count= 30.52 usec &
consider current i/p frequency is 360 Hz then count == 91(2.78 msec) &
previous frequency count is 93 (350 Hz = 2.86 msec).
then,
isr_ccp_delta = 91 + (0x1000000 - 93);
it will be FF FFFE =>(16777214);
Then,
frequency = (32768+(16777214/2))/16777214;
it will be "0 Hz";
But it should be 360 Hz _________________ Thank You,
With Best Regards,
Deepak.
Last edited by deepakomanna on Wed Jan 09, 2008 7:36 am; edited 1 time in total |
|
|
deepakomanna
Joined: 06 Mar 2007 Posts: 92 Location: Pune,India
|
getting incorrect frequency... |
Posted: Tue Jan 08, 2008 6:05 am |
|
|
can anybody tell me how to calculate frequency by using 16f913 with external crystal for timer1 using CCP1 interrupt & internal oscillator for internal processing. _________________ Thank You,
With Best Regards,
Deepak. |
|
|
RLScott
Joined: 10 Jul 2007 Posts: 465
|
Re: getting incorrect frequency... |
Posted: Tue Jan 08, 2008 1:15 pm |
|
|
deepakomanna wrote: |
..my timer 1 is running at 1 count= 30.52 usec &
consider current i/p frequency is 360 Hz then count == 91(2.78 msec) &
previous frequency count is 93 (350 Hz = 2.86 msec).
then,
isr_ccp_delta = 91 + (0x1000000 - 93);
it will be FF FFFE =>(16777214);
Then,
frequency = (32768+(16777214/2))/16777214;
it will be "0 Hz";
But it should be 91 Hz |
If your input frequency is in the range of 10 Hz to 700 Hz, then the period of one cycle of the input will range from 3276 Timer 1 counts to 47 Timer 1 counts. I don't know if you are resetting Timer 1 on every input cycle or if you are letting it run free. The subtraction implied by your calculation would indicate that it is running free. Then the problem is subtracting two 24-bit numbers so that the result works across a wrap-around at 0x??ffffff. Several solutions to that have been proposed, including mine of just doing:
delta_ccp = (current_ccp - old_ccp) & 0xffffff;
This assumes that immediately after doing this calculation you will then set:
old_ccp = current_ccp;
in preparation for the next cycle. If all that is done properly, then the calculation for frequency is:
freq = 32768 / delta_ccp;
Robert Scott
Real-Time Specialties |
|
|
RLScott
Joined: 10 Jul 2007 Posts: 465
|
|
Posted: Tue Jan 08, 2008 1:20 pm |
|
|
ELCouz wrote: |
Does my problem (0hz reading) because my timer is not syncronised with the input?
|
It may be due to incorrectly handling 24-bit wrap-around, which will give a very large (incorrect) period, which would calculate out to 0 Hz.
Quote: | Code: |
delta_ccp = current_ccp - old_ccp;
if(bit_test(delta_ccp,23))
delta_ccp |= 0xff000000;
else
delta_ccp &= 0x00ffffff;
old_ccp = current_ccp;
|
then declare delta_ccp as a int32 in the header ?
|
Not only delta_ccp, but also current_ccp and old_ccp must be 32 bits.
Robert Scott
Real-Time Specialties |
|
|
deepakomanna
Joined: 06 Mar 2007 Posts: 92 Location: Pune,India
|
getting incorrect frequency... |
Posted: Wed Jan 09, 2008 9:22 pm |
|
|
RLScott wrote: | Then the problem is subtracting two 24-bit numbers so that the result works across a wrap-around at 0x??ffffff. Several solutions to that have been proposed, including mine of just doing:
delta_ccp = (current_ccp - old_ccp) & 0xffffff;
This assumes that immediately after doing this calculation you will then set:
old_ccp = current_ccp;
in preparation for the next cycle. If all that is done properly, then the calculation for frequency is:
freq = 32768 / delta_ccp;
|
I gone through your reply,but this solution also gives "0 Hz" output.
Look this,
isr_ccp_delta = 0x000091 - 0x000093;
isr_ccp_delta &= 0xffffff;
it will be FFFFFE =>(16777214);
so, freq = 32768 / 16777214;
And this will gives "0 Hz" insted of "360 Hz"
Below is the my code for your kind reference,
Code: | #include<16F913.h>
#fuses INTRC_IO,NOWDT,PUT,NOMCLR,PROTECT,NOCPD,BROWNOUT,NOIESO,NOFCMEN //external MCLR bcz of MPLAB ICD2
#use delay(clock=8000000)
/////////////////////////////////////////////////////////////////////////////////////////
// LCD Configuration //
/////////////////////////////////////////////////////////////////////////////////////////
// Digit segments A B C D E F G DP
// b7 b6 b5 b4 b3 b2 b1 b0
#define DIGIT1 COM0+0, COM0+1, COM2+1, COM3+0, COM2+0, COM1+0, COM1+1, COM3+3 // DISPALYS FROM RIGHT SIDE
#define DIGIT2 COM0+2, COM0+3, COM2+3, COM3+2, COM2+2, COM1+2, COM1+3 // DISPALYS FROM RIGHT SIDE
#define DIGIT3 COM0+4, COM0+5, COM2+5, COM3+4, COM2+4, COM1+4, COM1+5 // DISPALYS FROM RIGHT SIDE
#define DIGIT4 COM0+6, COM0+7, COM2+7, COM3+6, COM2+6, COM1+6, COM1+7 // DISPALYS FROM RIGHT SIDE
#define DIGIT5 COM0+8, COM0+9, COM2+9, COM3+8, COM2+8, COM1+8, COM1+9 // DISPALYS FROM RIGHT SIDE
#define DIGIT6 COM0+13, COM0+11, COM2+11, COM3+13, COM2+13, COM1+13, COM1+11 // DISPALYS FROM RIGHT SIDE
#define DIGIT7 COM0+12, COM0+14, COM2+14, COM3+12, COM2+12, COM1+12, COM1+14 // DISPALYS FROM RIGHT SIDE#define VLCD_ENABLE 0x10
#define DUMBEL COM3+14
#define VLCD_ENABLE 0x10
#define eeprom_minute_address 0
#define eeprom_number_address 253
#define store_min_address 254
#define store_number_address 255
#BYTE OSCCON = 0x8F
#BYTE LCDDATA10 = 0X11A
//character 0 1 2 3 4 5 6 7 8 9
byte const Digit_Map[10] = {0xFD,0x61,0xDB,0xF3,0x67,0xB7,0xBF,0xE1,0xFF,0xF7};
byte lcd_pos;
byte segments;
void init_CPU();
void ee_write32(int8,int32,int8);
void ee_write16(int8,int16,int8);
int32 ee_read32(int8,int8);
int16 ee_read16(int8,int8);
int8 inc_min_address;
int8 dec_number_address;
int8 wr_inc_dec_addr;
int32 number ;
int16 min_count;
int1 BLINK_DUMBEL= 0; // this flag for blinking the decimal point
int1 stop_min_wr;
int8 no_freq;
#int_ccp1
void ccp1_isr(void)
{
int32 current_ccp;
static int32 old_ccp = 0;
int16 frequency;
int32 isr_ccp_delta;
static int16 sec_counter =0;
int16 TIMER1_VALUE;
no_freq = 0;
if(stop_min_wr== 0)
{
stop_min_wr = 1;
clear_interrupt(INT_TIMER0);
ENABLE_INTERRUPTS(INT_TIMER0);
SETUP_TIMER_0(RTCC_INTERNAL|RTCC_DIV_256);
}
current_ccp = (int32)CCP_1;
isr_ccp_delta = (current_ccp - old_ccp) ;
isr_ccp_delta &= 0xFFFFFF;
old_ccp = current_ccp;
frequency = 32768 / isr_ccp_delta;
//if(isr_ccp_delta <= 111) // if freq >=297 Hz ==(<= 111 count)
if(frequency >= 297) // if freq >=297 Hz ==(<= 111 count)
{
sec_counter++;
if(sec_counter >=297) // 1sec == 298 count
{
sec_counter = 0;
min_count++;
BLINK_DUMBEL =~BLINK_DUMBEL; // compliment flag for decimal point.
}
}
else
{
SETUP_TIMER_1(T1_DISABLED); // TIMER_1 count OFF
TIMER1_VALUE = GET_TIMER1();
if(TIMER1_VALUE >= 32766) // (65536/2)=32766 1SEC timer1 count
{
min_count++;
set_timer1(0);
BLINK_DUMBEL =~BLINK_DUMBEL; // compliment flag for decimal point.
}
setup_timer_1(T1_EXTERNAL_SYNC|T1_DIV_BY_1|T1_CLK_OUT);
}
}
int32 ee_read32(int8 base_address,int8 RW_cycle)
{
int8 j;
int32 data_read=0;
for(j=0; j<=RW_cycle; j++)
*((int8 *)&data_read+j) = read_eeprom(base_address - j);
return (data_read);
}
void ee_write32(int8 base_address ,int32 data,int8 WR_cycle)
{
int8 j;
for(j=0; j<WR_cycle; j++)
write_eeprom(base_address - j,*((int8 *)&data+j));
}
int16 ee_read16(int8 base_address,int8 RW_cycle)
{
int8 j;
int16 data_read=0;
for(j=0; j< RW_cycle; j++)
*((int8 *)&data_read+j) = read_eeprom(base_address + j);
return (data_read);
}
void ee_write16(int8 base_address,int16 data,int8 WR_cycle)
{
int8 j;
for(j=0; j<WR_cycle; j++)
write_eeprom(base_address + j,*((int8 *)&data+j));
}
#INT_TIMER0
void timer0_isr(void)
{
int16 stored_minute;
static int16 sec_count = 0;
// TIMER0 overflows after every 30.464 msec
sec_count++;
no_freq++;
if(sec_count >= 197)
{ //write data every 6 sec to eeprom.
sec_count = 0;
write_min_again :
ee_write16((eeprom_minute_address + inc_min_address), min_count,2); //write data to EEPROM loc 0x00
stored_minute=ee_read16((eeprom_minute_address + inc_min_address),2);
if(stored_minute != min_count)
{
inc_min_address++;
wr_inc_dec_addr = 1;
if(inc_min_address == 144)
inc_min_address = 0;
goto write_min_again;
}
}
if(no_freq >=16) //1 SEC =16
{
no_freq = 0;
DISABLE_INTERRUPTS(INT_TIMER0);
stop_min_wr = 0;
}
}
void lcd_putc(char c)
{
if(c=='\f')
lcd_pos=0;
else {
if((c>='0')&&(c<='9'))
{
segments=Digit_Map[c-'0'];
}
else
segments=0;
switch(lcd_pos)
{
case 1: lcd_symbol(segments,DIGIT7); break; // fill 1s place
case 2: lcd_symbol(segments,DIGIT6); break; // fill 1s place
case 3: lcd_symbol(segments,DIGIT5); break; // fill 10s place
case 4: lcd_symbol(segments,DIGIT4); break; // fill 100s place
case 5 : lcd_symbol(segments,DIGIT3); break; // fill 1000s place
case 6 :
if(number<10)
segments = Digit_Map[0];
lcd_symbol(segments,DIGIT2); break; // fill 10000splace
case 7 :
lcd_symbol(segments,DIGIT1); // fill 100000s place
switch(BLINK_DUMBEL)
{
case 1 : #asm
BSF LCDDATA10,6
#endasm
break;
case 0 : #asm
BCF LCDDATA10,6
#endasm
}
}
}
lcd_pos++;
}
void init_CPU()
{
setup_oscillator(OSC_8MHZ | OSC_INTRC);
/************** PORT SETTINGS ****************/
PORT_B_PULLUPS(0X00); // RB7 & RB6 i/p for programming devices.
SET_TRIS_A(0XC0); // i/p OSC1 & OSC2
SET_TRIS_B(0X00);
SET_TRIS_C(0X27); //VLCD 1,2,3 i/p,FREQ i/p.
SET_TRIS_E(0X08); // RE3 i/p for RESETING the device EEPROM.
/*************** LCD SETTINGS ********************/
SETUP_LCD( LCD_MUX14 |LCD_INTRC|VLCD_ENABLE, 2);
/**************** COMPARATOR SETTINGS ***************/
SETUP_COMPARATOR(NC_NC_NC_NC);
/**************** INTERRUPT SETTINGS *****************/
ENABLE_INTERRUPTS(GLOBAL);
clear_interrupt(INT_TIMER0);
ENABLE_INTERRUPTS(INT_TIMER0); //enable timer1 interrupt
SET_TIMER0(18); // timer0 interrupt for 30.464 msec.
// 1 Count=128 Usec,So 256 - 238 ==18.
set_timer1(0);
setup_ccp1(CCP_CAPTURE_RE); //Capture on rising edge
clear_interrupt(INT_CCP1);
enable_interrupts(INT_CCP1);
setup_timer_1(T1_EXTERNAL_SYNC|T1_DIV_BY_1|T1_CLK_OUT);
}
void main()
{
int32 stored_number;
int8 wr_rw_cycle;
int8 read_erase;
int16 clear_ee = 0;
init_CPU();
OSCCON = 0x70;
BLINK_DUMBEL = 0;
wr_inc_dec_addr = 0;
stop_min_wr = 0;
inc_min_address = read_eeprom(store_min_address);
dec_number_address = read_eeprom(store_number_address);
min_count = ee_read16((eeprom_minute_address + inc_min_address),2);
number = ee_read32((eeprom_number_address - dec_number_address),4);
while(TRUE)
{
printf(lcd_putc,"\f%7lu",number);
if(INPUT_STATE(PIN_E3) == 0)
{
for(clear_ee = 0; clear_ee<= 255;clear_ee++)
{
read_erase = read_eeprom(clear_ee);
if(read_erase != 0)
write_eeprom(clear_ee,0x00);
}
}
if(min_count >= 360)//360 // if frequency is 297 Hz, min_count == 360, becomes after 6 min.
{ // so number is incremented once after 6min.
number++;
min_count = 0;
if(number <= 255)
wr_rw_cycle = 1;
else if((number >255) &&(number < 65025))
wr_rw_cycle = 2;
else
wr_rw_cycle = 3;
write_number_again:
ee_write32((eeprom_number_address - dec_number_address), number,wr_rw_cycle); //write data to EEPROM loc 0x00
stored_number = ee_read32((eeprom_number_address - dec_number_address),wr_rw_cycle);
if(stored_number != number)
{
dec_number_address++;
wr_inc_dec_addr = 2;
if(dec_number_address == 150)
dec_number_address = 0;
goto write_number_again;
}
no_freq = 0;
if(number == 999999)
number = 0;
}
switch(wr_inc_dec_addr)
{
case 1 : write_eeprom(store_min_address,inc_min_address);
wr_inc_dec_addr = 0;
break;
case 2 : write_eeprom(store_number_address,dec_number_address);
wr_inc_dec_addr = 0;
break;
}
}
}
|
Plz helped me i stucked only in this point.... _________________ Thank You,
With Best Regards,
Deepak. |
|
|
RLScott
Joined: 10 Jul 2007 Posts: 465
|
Re: getting incorrect frequency... |
Posted: Thu Jan 10, 2008 6:10 am |
|
|
deepakomanna wrote: | RLScott wrote: | Then the problem is subtracting two 24-bit numbers so that the result works across a wrap-around at 0x??ffffff. Several solutions to that have been proposed, including mine of just doing:
delta_ccp = (current_ccp - old_ccp) & 0xffffff;
This assumes that immediately after doing this calculation you will then set:
old_ccp = current_ccp;
in preparation for the next cycle. If all that is done properly, then the calculation for frequency is:
freq = 32768 / delta_ccp;
|
I gone through your reply,but this solution also gives "0 Hz" output.
Look this,
isr_ccp_delta = 0x000091 - 0x000093;
isr_ccp_delta &= 0xffffff;
it will be FFFFFE =>(16777214);
so, freq = 32768 / 16777214;
And this will gives "0 Hz" insted of "360 Hz".... |
How do you know that old_ccp = 0x93 and current_ccp = 0x91? You code does not show how you could know this. I think that is the basic problem. It does not make sense that CCP_1 is 0x93 at one time and then it is 0x91 a short time later. CCP_1 is supposed to capture the value of Timer 1 on the rising edge of your signal. Timer 1 is counting up, right? It might appear that Timer 1 is counting down if it actually did count up 16777214 times. This might happen if the signal frequency is very low, but I don't think so. Put in some code to check what CCP_1 is reading on each capture event. Perhaps you actually have ccp_old and ccp_current reversed?
Robert Scott
Real-Time Specialties
Embedded Systems Consulting |
|
|
deepakomanna
Joined: 06 Mar 2007 Posts: 92 Location: Pune,India
|
getting incorrect frequency... |
Posted: Thu Jan 10, 2008 7:33 am |
|
|
After your reply i made one pin (RA0) toggle at fixed 480 Hz freq. in the following routine in my code (see earlist my thread)
Quote: |
if(frequency >= 297) // if freq >=297 Hz ==(<= 111 count)
{
output_toggle(pin_a0);
sec_counter++;
if(sec_counter >=297) // 1sec == 298 count
{
sec_counter = 0;
min_count++;
BLINK_DUMBEL =~BLINK_DUMBEL; // compliment flag for decimal point.
}
} |
then i observed on the oscilloscope that some times it misses to toggle the pin, this means that the sometimes control is not comming inside the loop. & follwoing the else statement. this is not meaning that it is giving 0 Hz, but i want to say is if freq is fixed 480 Hz then why it misses to toggle the pin. _________________ Thank You,
With Best Regards,
Deepak. |
|
|
SET
Joined: 15 Nov 2005 Posts: 161 Location: Glasgow, UK
|
|
Posted: Fri Jan 11, 2008 9:55 am |
|
|
As PCM posted, you have to handle the case where the current capture is smaller than the last capture:
Quote: | Code: | g32_ccp_delta = (current_ccp > old_ccp) ? current_ccp - old_ccp : current_ccp + (0x1000000 - old_ccp); |
|
So your code:
Code: | current_ccp = (int32)CCP_1;
isr_ccp_delta = (current_ccp - old_ccp) ;
isr_ccp_delta &= 0xFFFFFF;
old_ccp = current_ccp;
frequency = 32768 / isr_ccp_delta; |
Should be changed to:
Code: |
current_ccp = (int32)CCP_1;
if (current_ccp > old_ccp)
isr_ccp_delta = (current_ccp - old_ccp) ;
else
isr_ccp_delta = current_ccp + (0x1000000 - old_ccp);
old_ccp = current_ccp;
frequency = 32768 / isr_ccp_delta; |
PS I changed from the ? operator to explicit 'if' in case peeps here arent familiar with it. |
|
|
Kenny
Joined: 07 Sep 2003 Posts: 173 Location: Australia
|
|
Posted: Fri Jan 11, 2008 2:14 pm |
|
|
Deepakomanna:
The 24 bit code is not being used. Also, if you want to use the timer 1 oscillator rather than use a crystal on the main oscillator, then could get a more precise result counting over more cycles of the input using either
setup_ccp1(CCP_CAPTURE_DIV_4);
or
setup_ccp1(CCP_CAPTURE_DIV_16);
The tradeoff is that the update rate will be lower.
RLScott:
I think that the method that tests bit 23 limits the range to 0x7FFFF, which I guess is not a problem for a tachometer application.
I thought about testing bit 31, and if it is set clear the top byte of the int32, with PCM's macro for example.
Your other method
delta_ccp = (current_ccp - old_ccp) & 0xffffff;
works over the full range and is much more efficient than what I was using.
It's two instructions shorter this way ( the scratch area is not needed for the intermediate value):
delta_ccp = current_ccp - old_ccp;
delta_ccp &= 0xffffff;
I shall use that from now on.
Thanks
Ken |
|
|
RLScott
Joined: 10 Jul 2007 Posts: 465
|
|
Posted: Sat Jan 12, 2008 8:10 am |
|
|
SET wrote: |
So your code:
Code: | current_ccp = (int32)CCP_1;
isr_ccp_delta = (current_ccp - old_ccp) ;
isr_ccp_delta &= 0xFFFFFF;
old_ccp = current_ccp;
frequency = 32768 / isr_ccp_delta; |
Should be changed to:
Code: |
current_ccp = (int32)CCP_1;
if (current_ccp > old_ccp)
isr_ccp_delta = (current_ccp - old_ccp) ;
else
isr_ccp_delta = current_ccp + (0x1000000 - old_ccp);
old_ccp = current_ccp;
frequency = 32768 / isr_ccp_delta; |
|
Can you give one example of when the first code gives different results from the second code? It seems to me that for 24-bit values of CCP_1, these two code blocks give the same result, and the first one is smaller and faster.
Robert Scott
Real-Time Specialties
Embedded Systems Consulting |
|
|
SET
Joined: 15 Nov 2005 Posts: 161 Location: Glasgow, UK
|
|
Posted: Mon Jan 14, 2008 6:08 am |
|
|
Quote: | Can you give one example of when the first code gives different results from the second code? It seems to me that for 24-bit values of CCP_1, these two code blocks give the same result, and the first one is smaller and faster. |
Yes your right, for 2-s complement subtraction this works. Mind you, the efficiency gains are moot compared to the following division! |
|
|
Salenko
Joined: 08 Sep 2008 Posts: 84
|
|
Posted: Thu Jun 11, 2009 11:10 am |
|
|
Hi,
compiler version : 4.057
I read this thread and tested the code above with a 16f628 running at 10Mhz. The input signal, about 1.4 Hz, is generated from a PIC 16f877 using the following code:
Code: |
void main()
{
while(1)
{
output_high(PIN_E1);
delay_ms(100);
output_low(PIN_E1) ;
delay_ms(600);
}
} |
I connected the 16F877's pin RE2 to the CCP1 pin of 16F628 (RB3) through a pull-down resistance. and here is what I got in the terminal :
Code: |
....
53512 Hz
53512 Hz
53512 Hz
0 Hz
53512 Hz
53512 Hz
48100 Hz
53512 Hz
53512 Hz
53512 Hz
53512 Hz
53512 Hz
53512 Hz
53512 Hz
53512 Hz
53512 Hz
...
|
I was expecting to get 1 or 2 Hz
here is my code :
Code: |
#include <16F628.h>
#fuses HS, NOWDT, NOPROTECT, BROWNOUT, PUT, NOLVP
#use delay(clock = 10000000)
#use rs232 (baud=9600,parity=N,xmit=PIN_B2,rcv=PIN_B1,bits=8)
#priority CCP1, TIMER1
#byte PortB = 6 // Address of PortB for 16F-series PICs
#byte PIR1 = 0x0C // PIR1 : REGISTERS ASSOCIATED WITH TIMER1 (contains TMR1IF in bit0)
#byte CCPR1_REG = 0x15 // Adress of CCPR1L
#bit TMR1IF = PIR1.0 // TMR1IF
#define BytePtr(var, offset) (char *)(&var + offset)
int8 gc_timer1_extension = 0;
int8 gc_capture_flag = FALSE;
int32 g32_ccp_delta;
//------------------------------------------------------------
// 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
// 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)
{
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 = 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;
}
void main()
{
int16 frequency;
int32 current_ccp_delta;
set_timer1(0); // for capture mode CCP1
setup_timer_1(T1_INTERNAL|T1_DIV_BY_1);
setup_ccp1(CCP_CAPTURE_RE);
// Clear the CCP1 interrupt flag before we enable
// CCP2 interrupts, so that we don't get an unwanted
// immediate interrupt (which might happen).
clear_interrupt(INT_TIMER1);
enable_interrupts(INT_TIMER1);
clear_interrupt(INT_CCP1);
enable_interrupts(INT_CCP1);
enable_interrupts(GLOBAL);
while(1)
{
// Now calculate the frequency.
// Get a local copy of the latest ccp delta from the isr.
// We have to disable interrupts when we read a global
// isr variable that is larger than a single byte.
disable_interrupts(GLOBAL);
current_ccp_delta = g32_ccp_delta;
enable_interrupts(GLOBAL);
// To calculate the frequency of the input signal,
// we take the number of clocks that occurred
// between two consecutive edges of the input signal,
// and divide that value into the number of Timer1
// clocks per second. Since we're using a 4 MHz
// crystal, the Timer1 clock is 1 MHz (Timer1 runs
// at the instruction cycle rate, which is 1/4 of the
// crystal frequency). For example, suppose the
// the input waveform has a frequency of 244 Hz.
// 244 Hz has a period of about 4098 usec.
// Timer1 is clocked at 1 MHz, so between two
// consecutive rising edges of the input signal,
// it will count up by 4098 clocks. To find the
// frequency, we divide 4098 into the number of
// clocks that occur in 1 second, which is 1000000.
// This gives 1000000 / 4098 = 244 Hz.
if(gc_capture_flag == TRUE)
{
frequency = (int16)((2500000L + (current_ccp_delta >> 1)) / current_ccp_delta);
printf("%lu Hz\n\r", frequency);
gc_capture_flag = FALSE;
}
else
{
printf("\n\r"); // instead of displaying "no signal"
}
delay_ms(500);
}
}
|
The code seems to be correct, but I'm receiving incorrect frequency...
Is there any explanation ?
Your help will be much appreciated.
Salenko. |
|
|
PCM programmer
Joined: 06 Sep 2003 Posts: 21708
|
|
Posted: Thu Jun 11, 2009 11:52 am |
|
|
I can see one problem. This is fairly old code. It was written for vs 3
of the compiler. At that time, CCS assumed that all pointer arithmetic
was done as if the pointer was a byte pointer (And I wrote code like that).
But in vs. 4, they changed to use the C standard, which is that pointer
arithmetic will use the declared size of the pointer. In other words, if
you add 1 to an int32 pointer, the pointer will be advanced by 4 bytes,
which is the size of an int32. That's the correct way.
So, to fix this old code, you need to cast the &var address to a char
pointer. Add the (char*) text as shown in bold below:
Quote: |
#define BytePtr1(var, offset) (char *)((char*)&var + offset) |
|
|
|
|
|
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
|