![](templates/subSilver/images/CCSLogo.jpg) |
![CCS C Software and Maintenance Offers](templates/subSilver/images/forumAd6.jpg) |
View previous topic :: View next topic |
Author |
Message |
prayami
Joined: 22 Dec 2004 Posts: 78
|
Calculating wrong RPM..... |
Posted: Wed Jul 05, 2006 1:21 am |
|
|
Hi...
I am using 18F452 and CCS PCH C Compiler, Version 3.249 . Crystal
oscillator is 10Mhz.
Thus frequency = 10MHzX4 = 40MHz.
Here I am running a simple program which is sometime calculating
the wrong RPM.
If I continuos change the input frequency on pin CCP1 from 1600rpm
to 1900 rpm (i.e. frequency from 107Hz to 124Hz) then once approximately within 3 minutes it is calculating rpm over 2000rpm. And turns PIN_D4 on.
My frequency generator is good and circuit is simple, nothing xtra stuff
connected, just processor and oscillator circuitry...
As I have calculated TimerCycle for above frequency range then it should not match following condition within 3minutes but is happening.....Don't know how it can be possible.....
Quote: |
//this condition should not match when RPM < 2000 or (frequency<124Hz>=107Hz)
if((tempcount1_1==4)&&(temptimer1_1<=37856)&&(Once==0))
{
output_high(PIN_D6);
Once = 1;
}
else if(Once==0)
{
output_low(PIN_D6);
}
|
Please give me any idea or clue if you can.....thanks....
Code: |
#CASE
#include <18F452>
#fuses H4,NOWDT,NOLVP,NOBROWNOUT,PUT,PROTECT,NOCPD,STVREN,NOWRT,NOWRTD,NOWRTC,NOWRTB,EBTR,NOCPB,EBTRB,NODEBUG
#use delay(clock=40000000)
int1 TestingStatus=0, Once=0, isCCP1=0;
int16 NewRPM=0,OnRPM=5000,OffRPM=4900;
unsigned int16 temptimer1=0,tempcount1=0;
unsigned int16 temptimer1_1=0,tempcount1_1=0;
unsigned int32 TimerCycle=0;
float tempf1=4.0;
int16 counttimer1=0;
#int_ccp1
void ccp1_isr()
{
temptimer1 = get_timer1();
tempcount1 = counttimer1;
set_timer1(0);
counttimer1=0;
if(isCCP1 == 0)
{
temptimer1_1 = temptimer1;
tempcount1_1 = tempcount1;
}
isCCP1=1;
/* //Tried to check whether it is interrupting everytime or not....
if(TestingStatus==0)
{
output_high(PIN_D6);
TestingStatus = 1;
}
else
{
output_low(PIN_D6);
TestingStatus = 0;
}
*/
}//#int_ccp1
#INT_TIMER1
void timer1_isr() {
if(counttimer1<65534)
counttimer1++;
}
void main() {
delay_ms(5000);
set_tris_a(0x00); //output PIN A1-A5
set_tris_b(0x00); //All output
set_tris_d(0x00); //All output
set_tris_c(0x85); //C5-C6 output
set_tris_e(0x00); //All output
//setup_ccp1(CCP_CAPTURE_RE);
setup_ccp1(CCP_CAPTURE_DIV_4); //For Motor RPM
setup_timer_1(T1_INTERNAL | T1_DIV_BY_1); //For Motor RPM
set_timer1(0);
enable_interrupts(INT_TIMER1); //For RPM
enable_interrupts(INT_CCP1); //For RPM
enable_interrupts(GLOBAL);
output_low(PIN_C1);
output_low(PIN_C3);
output_low(PIN_C4);
output_low(PIN_C5);
output_low(PIN_C6);
output_low(PIN_A1);
output_low(PIN_A2);
output_low(PIN_A3);
output_low(PIN_A4);
output_low(PIN_A5);
output_low(PIN_D0);
output_low(PIN_D1);
output_low(PIN_D2);
output_low(PIN_D3);
output_low(PIN_D5);
OnRPM=2000;
OffRPM=1000;
NewRPM=0;
while(TRUE) {
if(isCCP1==1)
{
TimerCycle = make32(tempcount1_1,temptimer1_1);
//---For RPM=2000 according to below formula, temptimer1_1 can not be less than
//37856 at tempcount1_1=4.
//That means, it is calculating wrong TimerCycle.
if((tempcount1_1==4)&&(temptimer1_1<37856> 0)&&(tempf1>0.0))
{
NewRPM = (unsigned int16)(600000000.0/( ((float)TimerCycle)*tempf1) );
}
else
{
NewRPM = 0;
}
isCCP1=0;
}//if(isCCP1==1)
//-----------End CCP1--------------------------------------------
if(NewRPM>=OnRPM)
{
output_high(PIN_D4);
}
else if(NewRPM<OffRPM>=OnRPM)
}//while(TRUE)
}//main
|
Last edited by prayami on Wed Jul 05, 2006 4:01 pm; edited 4 times in total |
|
![](templates/subSilver/images/spacer.gif) |
asmallri
Joined: 12 Aug 2004 Posts: 1634 Location: Perth, Australia
|
|
Posted: Wed Jul 05, 2006 3:56 am |
|
|
You are incrementing 16 bit variables inside your interrupt handler. Outside the handler you are testing and changing there variables without disabling interrupts during the process. Each line of C can translate into many instructions. It is possible and not uncommon for an interrupt to occur inside the mainline test conditions in which case the most significant byte and the least signficant byte may be unrelated. For example lets say the value of a 16 bit variable was 0x01ff at the start of the test in mainline. The first part of the test sees 0x01 as the most significant byte. Now an interrupt occurs and the value is incremented by the handler (the new value seen by the handler is 0x0200). The handler returns to the mainline test code which now tests the least significant byte and sees that the value is 0x00. So the mainline interprets the value as 0x0100 whereas the interrupt handler sees the value as 0x0200.
You get the idea. _________________ Regards, Andrew
http://www.brushelectronics.com/software
Home of Ethernet, SD card and Encrypted Serial Bootloaders for PICs!! |
|
![](templates/subSilver/images/spacer.gif) |
prayami
Joined: 22 Dec 2004 Posts: 78
|
|
Posted: Wed Jul 05, 2006 4:05 pm |
|
|
Thanks for reply asmallri....
I tried as you suggested and thus modified my original post with that
change you have suggested....But I have handled the disable_interrupt that you suggested in different way...
But it is still counting Timer Cycle less than frequency 124Hz....that is
calculating more than 124Hz once every 2 to 3 minutes....
Can it be bug some where..??!!!
I have also modified my original post with more details......
Thanks in advanced........plz...... |
|
![](templates/subSilver/images/spacer.gif) |
asmallri
Joined: 12 Aug 2004 Posts: 1634 Location: Perth, Australia
|
|
Posted: Wed Jul 05, 2006 7:25 pm |
|
|
You need to disable interrupts in the main line..
Code: | if(isCCP1==1)
{
disable_interrupts(GLOBAL);
mycount = tempcount1_1;
mytimer = temptimer1_1;
isCCP1=0;
enable_interrupts(GLOBAL);
TimerCycle = make32(mycount1_1,mytimer1_1);
//---For RPM=2000 according to below formula, temptimer1_1 can not be less than
//37856 at tempcount1_1=4.
//That means, it is calculating wrong TimerCycle.
if((mycount1_1==4)&&(mytimer1_1<37856> 0)&&(tempf1>0.0))
{
NewRPM = (unsigned int16)(600000000.0/( ((float)TimerCycle)*tempf1) );
}
else
{
NewRPM = 0;
}
}//if(isCCP1==1) |
_________________ Regards, Andrew
http://www.brushelectronics.com/software
Home of Ethernet, SD card and Encrypted Serial Bootloaders for PICs!! |
|
![](templates/subSilver/images/spacer.gif) |
prayami
Joined: 22 Dec 2004 Posts: 78
|
|
Posted: Thu Jul 06, 2006 3:26 pm |
|
|
Thanks....
I tried as per you suggested...but same thing....I minimized the code
as under but it is definately calculating wrong TimerCycle.
I am applying varing frequency between 107Hz to 125Hz
I don't know what are the possibilities for that....
I tried to change new processor but same.....
But it is calculating
Quote: |
if((TimerCycle<78700)&&(Once==0))
{
output_high(PIN_D4);
Once = 1;
}
else if(Once==0)
{
output_low(PIN_D4);
}
|
Here is the simplified code....
It is going high even though I have not applied above 127Hz=78700cycle.
Code: |
#CASE
#include <18F452>
#fuses H4,NOWDT,NOLVP,NOBROWNOUT,PUT,PROTECT,NOCPD,STVREN,NOWRT,NOWRTD,NOWRTC,NOWRTB,EBTR,NOCPB,EBTRB,NODEBUG
#use delay(clock=40000000)
int1 TestingStatus=0, Once=0, isCCP1=0;
unsigned int16 temptimer1=0,tempcount1=0;
unsigned int16 temptimer1_1=0,tempcount1_1=0;
unsigned int32 TimerCycle=0;
int16 counttimer1=0;
#int_ccp1
void ccp1_isr()
{
temptimer1 = get_timer1();
tempcount1 = counttimer1;
set_timer1(0);
counttimer1=0;
isCCP1=1;
}//#int_ccp1
#INT_TIMER1
void timer1_isr() {
if(counttimer1<65534)
counttimer1++;
}
void main() {
delay_ms(5000);
set_tris_a(0x00); //output PIN A1-A5
set_tris_b(0x00); //All output
set_tris_d(0x00); //All output
set_tris_c(0x85); //C5-C6 output
set_tris_e(0x00); //All output
//setup_ccp1(CCP_CAPTURE_RE);
setup_ccp1(CCP_CAPTURE_DIV_4); //For Motor RPM
setup_timer_1(T1_INTERNAL | T1_DIV_BY_1); //For Motor RPM
set_timer1(0);
enable_interrupts(INT_TIMER1); //For RPM
enable_interrupts(INT_CCP1); //For RPM
enable_interrupts(GLOBAL);
output_low(PIN_C1);
output_low(PIN_C3);
output_low(PIN_C4);
output_low(PIN_C5);
output_low(PIN_C6);
output_low(PIN_A1);
output_low(PIN_A2);
output_low(PIN_A3);
output_low(PIN_A4);
output_low(PIN_A5);
output_low(PIN_D0);
output_low(PIN_D1);
output_low(PIN_D2);
output_low(PIN_D3);
output_low(PIN_D5);
while(TRUE) {
if(isCCP1==1)
{
disable_interrupts(GLOBAL);
temptimer1_1 = temptimer1;
tempcount1_1 = tempcount1;
isCCP1=0;
enable_interrupts(GLOBAL);
TimerCycle = make32(tempcount1_1,temptimer1_1);
TimerCycle = (unsigned int32)((float)TimerCycle/4.0);
//78700 = 127Hz
//And I am applying varing frequency between 107Hz to 125Hz
if((TimerCycle<78700)&&(Once==0))
{
output_high(PIN_D4);
Once = 1;
}
else if(Once==0)
{
output_low(PIN_D4);
}
}//if(isCCP1==1)
}//while(TRUE)
}//main
|
|
|
![](templates/subSilver/images/spacer.gif) |
asmallri
Joined: 12 Aug 2004 Posts: 1634 Location: Perth, Australia
|
|
Posted: Thu Jul 06, 2006 8:07 pm |
|
|
prayami wrote: |
But it is calculating
Code: |
if((TimerCycle<78700)&&(Once==0))
{
output_high(PIN_D4);
Once = 1;
}
else if(Once==0)
{
output_low(PIN_D4);
}
|
Here is the simplified code....
It is going high even though I have not applied above 127Hz=78700cycle.
|
I don't understand what you are trying to say. The way this code fragment is written, the very first time TimerCycle is < 78700 then the output will be set high and there is no code to set it low again. Perhaps this is your intention. _________________ Regards, Andrew
http://www.brushelectronics.com/software
Home of Ethernet, SD card and Encrypted Serial Bootloaders for PICs!! |
|
![](templates/subSilver/images/spacer.gif) |
prayami
Joined: 22 Dec 2004 Posts: 78
|
|
Posted: Thu Jul 06, 2006 10:23 pm |
|
|
Hi...
I have written delay_ms(5000); on the beginning of the main loop.
And it is not doing at very beginning. It is doing every 2 to 3 minutes.
This if loop is to check whether it is going >127Hz or not....
My frequency generator is good and input signal is also ok.....
It is really hard for me to figure out...what are the possible problem...
Thanks.... |
|
![](templates/subSilver/images/spacer.gif) |
Ttelmah Guest
|
|
Posted: Fri Jul 07, 2006 10:22 am |
|
|
A number of minor comments.
First, clear the interrupts before enabling them. Otherwise it is quite likely that before the timer is setup, it can run at full frequency, and an interrupt may already have occured. Shouldn't give your random behaviour but is worth doing.
Second, why are you reading the timer in the INT_CCP?. The whole point of the CCP capture mode, is that _it_ captures the count at the moment the hardware event occurs. This count will be available in the CCP registers inside the interrupt. By reading the timer, the extra time needed to enter the interrupt will be incurred, and also if there was a delay (because perhaps you were inside int_timer1), there will be an errpneous reading.
The big one though is the third.
What happens if the timer wraps after the CCP event triggers, but before it reaches the CCP routine?. What happens if the timer event triggers, but the CCP event triggers before the timer routine is reached?. The answer is that in both cases it will go to the CCP handler routine, and arrive without the timer overflow having been incremented, resulting in a massively wrong reading (out by 65536 counts). There are a number of possible solutions. The first is to add a test in the CCP routine, and in the next instruction, test if the Timer1 interrupt flag is set. If it is, carry out an extra increment to the carry bit. The second is to get rid of the timer1 overflow handling completely. Just set the CCP, to capture the counter on each edge. Then in the CCP interrupt, record the value stored in the CCP registers. In the main, calculate this minus the last count, and while this result is less than 80000, keep adding 65536. Since your results should always be in the range 80000 to 93457, this will generate the right result.
Finally, don't fiddle around converting a value to a float, dividing by four, and then converting back, this takes a huge amount of time, and gives _less_ accurate results than working in 32bit integer. A 'float', only has 24 bit basic accuracy, and since you are working with an integer +ve value, you gain nothing by this.
Best Wishes |
|
![](templates/subSilver/images/spacer.gif) |
prayami
Joined: 22 Dec 2004 Posts: 78
|
|
Posted: Mon Jul 10, 2006 10:06 pm |
|
|
Thanks...Ttelmah....you are greate....
I have tried the one you have suggested. And that seems working. The only thing is if I use CCP_1 for reading timer as under then it is doing what it was. But with "temptimer1 = get_timer1();" it is fine.
Is there any thing else left for using CCP_1 ?
Code: |
#int_ccp1
void ccp1_isr()
{
//temptimer1 = CCP_1;
temptimer1 = get_timer1();
if(TMR1IF==1)
{
TMR1IF = 0;
if(counttimer1<65534)
counttimer1++;
}
tempcount1 = counttimer1;
set_timer1(0);
counttimer1=0;
isCCP1=1;
}//#int_ccp1
|
|
|
![](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
|