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 CCS Technical Support

Problems with RPM code on PIC16F877

 
Post new topic   Reply to topic    CCS Forum Index -> General CCS C Discussion
View previous topic :: View next topic  
Author Message
hp_sa



Joined: 22 May 2008
Posts: 6

View user's profile Send private message

Problems with RPM code on PIC16F877
PostPosted: Thu May 22, 2008 5:11 am     Reply with quote

I am having trouble getting the rpm reading from an engine (4cyl). I am using the PIC16F877, 20Mhz crystal, CCS PCM compiler version 3.207.

My code is as below,
Code:

setup_timer_1 (T1_INTERNAL|T1_DIV_BY_8);
setup_ccp1 (CCP_CAPTURE_RE);

#INT_TIMER1
void TIMER1_isr(void)
{
++overflow_countRPM;
}

#INT_CCP1
void CCP1_isr(void)
{
end_timeRPM = (int32)CCP_1;
period = ((int32)0xFFFF * (int32)overflow_countRPM) - (int32)start_timeRPM + (int32)end_timeRPM;
RPM = (int32)18.75E6/period;

start_timeRPM = end_timeRPM;
overflow_countRPM = 0;
CCP1IF = 0;
}

I then display the "RPM" variable via the LCD in a while(1) loop. Any suggestions/comments welcome. Thanks...
ckielstra



Joined: 18 Mar 2004
Posts: 3680
Location: The Netherlands

View user's profile Send private message

PostPosted: Thu May 22, 2008 7:28 am     Reply with quote

Basic idea looks like it should work, just some remarks:

Code:
 RPM = (int32)18.75E6/period;
How did you get this value?
Counts per minute = 20MHz/4/8 * 60sec = 37.5E6
Your value is right if the external trigger is generated every 30 seconds. My guess is you want to update the RPM reading more often.

Code:
((int32)0xFFFF * (int32)overflow_countRPM) ...
The 0xFFFF should be 0x10000. As a side effect this makes the calculation much faster as it can be replaced by a simple shift.
hp_sa



Joined: 22 May 2008
Posts: 6

View user's profile Send private message

PostPosted: Sat May 24, 2008 12:16 pm     Reply with quote

Thanks for the feedback. I have made the necessary changes and its working better now. Only problem that I am experiencing is that the RPM value has an exponential error, meaning that when testing it, if the engine is running at 3000rpm the value being displayed is 3500rpm, and at 4000rpm it shows 4800rpm and 5000rpm it reads 6000rpm. Any possible solutions to this?
ckielstra



Joined: 18 Mar 2004
Posts: 3680
Location: The Netherlands

View user's profile Send private message

PostPosted: Sun May 25, 2008 4:53 am     Reply with quote

The error is not exponential, for the first value pair it is 17% off and for the other 2 value pairs 20%. An almost linear error.

Strange is that the shown value is too high. In case of performance problems you would have gotten a too low value. Now your error is more likely to indicate a counter that is not cleared.

What is your current update rate for the RPM calculation? Did you change it from the previous 30 seconds?

Can you post a program demonstrating your problem? Make the program small (max. 1 page) and include all #fuses, etc. so we can compile the program without modifications.

Quote:
CCS PCM compiler version 3.207.
This was one of the first the v3.2xx releases and is not a stable version. It was released for testing purposes but you shouldn't use it for real development as it contains too many bugs. Please revert to the previous stable version (v3.191 I believe) or upgrade to something higher than v3.225.
RLScott



Joined: 10 Jul 2007
Posts: 465

View user's profile Send private message

PostPosted: Sun May 25, 2008 9:41 am     Reply with quote

hp_sa wrote:
Thanks for the feedback. I have made the necessary changes and its working better now. Only problem that I am experiencing is that the RPM value has an exponential error, meaning that when testing it, if the engine is running at 3000rpm the value being displayed is 3500rpm, and at 4000rpm it shows 4800rpm and 5000rpm it reads 6000rpm. Any possible solutions to this?

If you expect an answer to that question, then it would help if you posted what those "necessary changes" were. As it is, it is impossible to say what might be wrong.

Robert Scott
Real-Time Specialties
hp_sa



Joined: 22 May 2008
Posts: 6

View user's profile Send private message

PostPosted: Mon May 26, 2008 12:54 am     Reply with quote

After the latest changes, "setup_timer_1 (T1_INTERNAL|T1_DIV_BY_1)" and "RPM = (int32)150E6/period" the RPM value is now under reading the true RPM of the engine, by approximately 250rpm. The program requested (ckielstra) is below,

Code:

#include <16F877.h>
#device *=16
#include "math.h"
#include <stdlib.h>
#include <stdio.h>
#include "lcd.c"

#use delay(clock=20000000)
#fuses HS,NOWDT,NOPROTECT,NOLVP,BROWNOUT,PUT

int overflow_countRPM;
unsigned int32 start_timeRPM, end_timeRPM;
float period;
float RPM;

void main(void);
void Initialize(void);
void TIMER1_isr (void);
void CCP1_isr(void);

#INT_TIMER1
void TIMER1_isr(void)
{
++overflow_countRPM;
TMR1IF = 0;
}

#INT_CCP1
void CCP1_isr(void)
{
end_timeRPM = (int32)CCP_1;
period = ((int32)0x10000 * (int32)overflow_countRPM) - (int32)start_timeRPM + (int32)end_timeRPM;

RPM = (int32)150E6/period;

start_timeRPM = end_timeRPM;
overflow_countRPM = 0;
CCP1IF = 0;
}

void main(void)
{
Initialize();   
lcd_init();
setup_timer_1 (T1_INTERNAL|T1_DIV_BY_1);
setup_ccp1 (CCP_CAPTURE_RE);   

disable_interrupts(GLOBAL);
enable_interrupts(INT_TIMER1);
enable_interrupts(INT_CCP1);
enable_interrupts(GLOBAL);

TMR1IF = 0;
CCP1IF=0;
LCD_clrscr();

while(1)
  {
    lcd_gotoxy(0,0);
    printf(lcd_putc,"RPM: %7f",RPM);

    lcd_gotoxy(9,0);
    printf(lcd_putc,"       ");
   }
}

void Initialize(void)
{
PORT_B_PULLUPS(TRUE);
SET_TRIS_A(0Xff);
SET_TRIS_B(0x1F);
SET_TRIS_C(0xff);
SET_TRIS_D(0x00);
SET_TRIS_E(0x00);
}

ckielstra wrote:
The error is not exponential, for the first value pair it is 17% off and for the other 2 value pairs 20%. An almost linear error.

Strange is that the shown value is too high. In case of performance problems you would have gotten a too low value. Now your error is more likely to indicate a counter that is not cleared.

What is your current update rate for the RPM calculation? Did you change it from the previous 30 seconds?

Can you post a program demonstrating your problem? Make the program small (max. 1 page) and include all #fuses, etc. so we can compile the program without modifications.

Quote:
CCS PCM compiler version 3.207.
This was one of the first the v3.2xx releases and is not a stable version. It was released for testing purposes but you shouldn't use it for real development as it contains too many bugs. Please revert to the previous stable version (v3.191 I believe) or upgrade to something higher than v3.225.
hp_sa



Joined: 22 May 2008
Posts: 6

View user's profile Send private message

PostPosted: Wed May 28, 2008 3:31 am     Reply with quote

Anyone?
Guest








rpm
PostPosted: Wed May 28, 2008 5:29 am     Reply with quote

OK, I have to ask the obvious.What is your pulse source? Is it really an engine or a stable time base generator? Try hooking up to the 60HZ power and see how it runs. Car engines are bad for electrical noise so unless you have proper filtering anything can happen.Spikes can come in through the air(use metal box),power lines(both + and gnd),tach output(filters).

Please tell us what happens when tested on the bench with line 60Hz.

Jay
hp_sa



Joined: 22 May 2008
Posts: 6

View user's profile Send private message

Re: rpm
PostPosted: Wed May 28, 2008 5:47 am     Reply with quote

The input signal is from the engine itself, with a filter circuit described for filtering automotive high frequency ignition "ringing" from an article in an electronics magazine, simply consisting of 4.7V zener diode (clipping max amplitude volt to 4.7V), two resistors and a cap for the filtering.

The processor board is housed in an aluminum enclosure.


Anonymous wrote:
OK, I have to ask the obvious.What is your pulse source? Is it really an engine or a stable time base generator? Try hooking up to the 60HZ power and see how it runs. Car engines are bad for electrical noise so unless you have proper filtering anything can happen.Spikes can come in through the air(use metal box),power lines(both + and gnd),tach output(filters).

Please tell us what happens when tested on the bench with line 60Hz.

Jay
RLScott



Joined: 10 Jul 2007
Posts: 465

View user's profile Send private message

PostPosted: Wed May 28, 2008 5:58 am     Reply with quote

hp_sa wrote:

Code:

#INT_TIMER1
void TIMER1_isr(void)
{
++overflow_countRPM;
TMR1IF = 0;
}

#INT_CCP1
void CCP1_isr(void)
{
end_timeRPM = (int32)CCP_1;
period = ((int32)0x10000 * (int32)overflow_countRPM) - (int32)start_timeRPM + (int32)end_timeRPM;

RPM = (int32)150E6/period;

start_timeRPM = end_timeRPM;
overflow_countRPM = 0;
CCP1IF = 0;
}

I don't know why RPM is coming out 250 RPM too low, but I can see another problem that will eventually occur sporadically. The problem is the consistency of the overflow_countRPM and the end_timeRPM and start_time_RPM. Imagine what happens if the Timer just happens to overflow at about the same time as the CCP1 event.

What you do not see in your code is the code that the CCS compiler produces to handle all interrupts and then check the interrupt flags, one at a time, to decide which of your interrupt routines to call. That process takes a little bit of time. If the CCP1 interrupt captured a CCP_1 value that was from before the Timer overflow (like 0xFFF9), then, a few cycles later the Timer 1 overflows, the CCS interrupt dispatcher might check for Timer 1 overflows before it checks for CCP1 interrupts and call INT_TIMER1 first, which would increment overflow_countRPM. Then when the CCS interrupt dispatcher calls your INT_CCP1, you will use the incremented overflow_countRPM together with the CCP_1 value that was from before the overflow. This will give an incorrect RPM calculation.

If the CCS interrupt dispatcher checks the CCP1 interrupt flag before it checks the Timer 1 interrupt flag, then a similar problem can occur if Timer 1 overflow just before the CCP1 event. Then the CCP_1 value will be from after the overflow, but the INT_CCP1 routine will be using overflow_countRPM from before the overflow, which will again give an incorrect RPM calculation.

These problems will occur randomly, and their probability is dependent on the length of time it takes the CCS interrupt dispatcher to decide which flags are set (maybe 10-20 instruction times?) as compared to the total cycle time of Timer 1 (65536 instruction times), or approximately 0.02% of the time. If you don't mind getting an incorrect RPM reading 0.02% of the time, then don't worry about it. But if you can't afford even one wrong RPM reading, then you should check the actual value of Timer 1 inside your INT_CCP1 to see if it has just overflowed, then decide if overflow_countRPM should be used as-is, or with a +1 or -1 offset.

Robert Scott
Real-Time Specialties
hp_sa



Joined: 22 May 2008
Posts: 6

View user's profile Send private message

PostPosted: Wed May 28, 2008 6:26 am     Reply with quote

Thanks for your input RLScott, some "food for thought" and something worth looking into. I am still puzzled at the problem of the rpm reading...


RLScott wrote:

I don't know why RPM is coming out 250 RPM too low, but I can see another problem that will eventually occur sporadically. The problem is the consistency of the overflow_countRPM and the end_timeRPM and start_time_RPM. Imagine what happens if the Timer just happens to overflow at about the same time as the CCP1 event.

What you do not see in your code is the code that the CCS compiler produces to handle all interrupts and then check the interrupt flags, one at a time, to decide which of your interrupt routines to call. That process takes a little bit of time. If the CCP1 interrupt captured a CCP_1 value that was from before the Timer overflow (like 0xFFF9), then, a few cycles later the Timer 1 overflows, the CCS interrupt dispatcher might check for Timer 1 overflows before it checks for CCP1 interrupts and call INT_TIMER1 first, which would increment overflow_countRPM. Then when the CCS interrupt dispatcher calls your INT_CCP1, you will use the incremented overflow_countRPM together with the CCP_1 value that was from before the overflow. This will give an incorrect RPM calculation.

If the CCS interrupt dispatcher checks the CCP1 interrupt flag before it checks the Timer 1 interrupt flag, then a similar problem can occur if Timer 1 overflow just before the CCP1 event. Then the CCP_1 value will be from after the overflow, but the INT_CCP1 routine will be using overflow_countRPM from before the overflow, which will again give an incorrect RPM calculation.

These problems will occur randomly, and their probability is dependent on the length of time it takes the CCS interrupt dispatcher to decide which flags are set (maybe 10-20 instruction times?) as compared to the total cycle time of Timer 1 (65536 instruction times), or approximately 0.02% of the time. If you don't mind getting an incorrect RPM reading 0.02% of the time, then don't worry about it. But if you can't afford even one wrong RPM reading, then you should check the actual value of Timer 1 inside your INT_CCP1 to see if it has just overflowed, then decide if overflow_countRPM should be used as-is, or with a +1 or -1 offset.

Robert Scott
Real-Time Specialties
ckielstra



Joined: 18 Mar 2004
Posts: 3680
Location: The Netherlands

View user's profile Send private message

PostPosted: Wed May 28, 2008 6:45 pm     Reply with quote

Quote:
The program requested (ckielstra) is below,
The program has several compiling errors meaning this is not the same program you have tested with. I don't want to spend time looking into a 'fake' program but here are some general remarks:

- It's strange that the two simple changes you made to the program changed the too high value readings into too low readings. I already asked you before, when is the CCP input triggered? How many times per second or minute?

- Only set the TRIS registers if you also have the #use fast_io declaration in your program. Otherwise the compiler will overrule the TRIS settings on every I/O operation and possibly cause conflicts.

- Change variable 'period' from float into an int32. This saves about 100 words and makes your interrupt handler faster.
- Change start_timeRPM and end_timeRPM from int32 to int16, now you are wasting 4 bytes RAM.
- Reconsider if you want to have the RPM variable as a float. If you don't care the decimal value of RPM than changing it into an int32 will save another 250 words.

Code:
TMR1IF = 0;
You don't have to clear the interrupt flag, the compiler will do this for you at the end of the interrupt routine. Now it is cleared twice, a waste of time and program memory.
Same applies to the CCP interrupt flag.

My guess is your RPM deviation is partially caused by performance issues.
All the small changes above will save almost 500 words and make your interrupt handler a lot quicker.
soulraven



Joined: 08 Feb 2009
Posts: 72
Location: campulung muscel

View user's profile Send private message Send e-mail Yahoo Messenger

PostPosted: Tue Mar 03, 2009 8:31 am     Reply with quote

any news?is working?
Display posts from previous:   
Post new topic   Reply to topic    CCS Forum Index -> General CCS C Discussion All times are GMT - 6 Hours
Page 1 of 1

 
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