View previous topic :: View next topic |
Author |
Message |
JamesW
Joined: 23 Apr 2007 Posts: 91 Location: Rochester, England
|
PIC24 CCP : RPM "fudge factor needed" [SOLVED] |
Posted: Mon Oct 10, 2016 10:38 am |
|
|
Hi Folks,
Sorry to bug you again about CCP, but I'm seeing an anomaly that I'm unable to explain, using capture compare to measure the RPM of a spinning disk, with an opto-sensor across a slot.
I'm using a pic 24HJ128GP502, running on the internal oscillator at 30Mhz.
I'll post the relevant code here, to make it easier here
My Timer initialisation and the main code loop (excluding the #pin select which is defined elsewhere).
This includes my maths explanation of how I'm getting the timer numbers (I am trying to keep to large integer maths rather than going to floating point maths).
Code: |
/* TURN ON SPINNING DISK OPTO LED */
output_high(DISK_OPTO_PWR);
/* SETUP TIMER 2 FOR CLOCK DIVIDE BY 64, WITH OVERFLOW OF 0XFFFF (ALTHOUGH */
/* THE OVERFLOW IS TECHNICALLY REDUNDANT FOR THE TIME CALCULATION) */
/* THIS GIVES US A TIMER OVERFLOW OF 279.620mS */
/* WHICH GIVES EACH "TICK" A TIME OF 0.279620 / 65535 = 4.26673uS */
/* */
/* AFTER THE CCP HAS OCCURRED, WE WILL HAVE A "PERIOD" IN TIMER 2 TICKS */
/* MULTIPLYING BY 4.26673uS WILL GIVE A PERIOD IN MICROSECONDS */
/* WE KNOW FREQ = 1 / PERIOD = 1 / (TIMER 2 TICKS * 4.26673E-6) */
/* RPM = (FREQ * 60) = (60 / TIMER 2 TICKS * 4.26673E-6)) */
/* WHICH IS THE EQUIVALENT OF RPM = 14062298.83 / TIMER2 TICKS */
/* */
/* HOWEVER - FOR SOME UNKNOW REASON THIS IS ALWAYS REPORTING A HIGH RPM */
/* THE REAL RPM IS 96.7% OF THE CALCULATED VALUE - SO APPLYING A "FUDGE */
/* FACTOR" OF 0.967 TO THE FIGURE OF 14062298 GIVING 13598242 WHICH WORKS */
setup_timer2(TMR_INTERNAL | TMR_DIV_BY_64, 0xFFFF);
while(TRUE)
{
if (DiskCaptureState == IDLE)
{
DiskPeriod = FallTime2 - FallTime1;
DiskRPM2 = (UI_32) ( (UI_32) 14062298L / DiskPeriod);
fprintf(DEBUG_PORT,"\rWITHOUT FUDGE DISK RUN AT %lu RPM", DiskRPM2);
DiskRPM2 = (UI_32) ( (UI_32) 13598242L / DiskPeriod);
fprintf(DEBUG_PORT,"\rWITH FUDGE DISK RUN AT %lu RPM", DiskRPM2);
/* START THE CAPTURE */
Start_Disk_Speed_Capture();
}
restart_wdt();
} /* END MAIN LOOP */
|
My capture compare code is below.
In essence the calculations in main are done when the ccp process is idle
Code: |
/* ------------------------------------------------------------------------- */
UI_16 FallTime1 = 0;
UI_16 FallTime2 = 0;
UI_16 CaptureVal=0;
#INT_IC1
void Service_CCP1_Interrupt()
{
CaptureVal = get_capture(1);
switch (DiskCaptureState)
{
/* EDGE 1 */
case WAIT_FOR_L2H:
{
FallTime1 = CaptureVal;
DiskCaptureState = WAIT_FOR_H2L;
break;
}
/* EDGE 2 */
case WAIT_FOR_H2L:
{
FallTime2 = CaptureVal;
DiskCaptureState = IDLE;
disable_interrupts(INT_IC1);
break;
}
case IDLE:
default:
{
break;
}
} /* END SWITCH */
}
/* ------------------------------------------------------------------------- */
void Start_Disk_Speed_Capture()
{
/* SETUP CAPTURE COMPARE FOR RISING EDGE TIMER 2 */
setup_capture(1, CAPTURE_FE | CAPTURE_TIMER2);
set_timer2(0);
output_low(LED_2);
DiskCaptureState = WAIT_FOR_L2H;
clear_interrupt(INT_IC1);
enable_interrupts(INT_IC1);
}
/* ------------------------------------------------------------------------- */
|
In the real world, using a logic analyser - the disk has a frequency of 6.356Hz = 381 rpm.
My question is this .... Can anyone tell me why I need to apply a "fudge factor" to my calculation to get the correct RPM?
I'm sure there is a logical explanation for this , but I can't for the life of me think why.
Thanks
James
Last edited by JamesW on Tue Oct 11, 2016 3:41 am; edited 2 times in total |
|
|
JamesW
Joined: 23 Apr 2007 Posts: 91 Location: Rochester, England
|
|
Posted: Mon Oct 10, 2016 11:17 am |
|
|
One slight clarification....
ignore the names of the cases in the CCP interrupt routine, as actually both of the case statements are waiting for High to low transitions (as I am measuring the waveform period).
Cheers
James |
|
|
Ttelmah
Joined: 11 Mar 2010 Posts: 19553
|
|
Posted: Mon Oct 10, 2016 11:43 am |
|
|
Have you read the data sheet?.
What temperature and voltage are you using?.
The internal oscillator, should give +/-2% over reasonable temperatures and voltages, but can be as bad as +/-5%. Your figure is a bit bad, but may just be the tolerance of the oscillator. It'll also be worse than quoted if your supply has some instability or lack of smoothing.
Realistically if you want accuracy any better than a couple of percent, you are going to need to use an external oscillator. |
|
|
JamesW
Joined: 23 Apr 2007 Posts: 91 Location: Rochester, England
|
|
Posted: Mon Oct 10, 2016 12:00 pm |
|
|
I did contemplate that ....
But....
I'm running at ambient (about 22 degrees in here at the moment) , at 3.3V - PSU is regulated and nice and smooth,
However If I comment out the line where I kick off the ccp process, and toggle a pin on rollover of timer 2, I get an interrupt every 0.2796 Seconds at the moment.
which is bang on where I should be - so I don't think it's that (unless my crude test of accuracy isn't quite what is needed)
Cheers
James |
|
|
PCM programmer
Joined: 06 Sep 2003 Posts: 21708
|
|
Posted: Mon Oct 10, 2016 12:53 pm |
|
|
Quote: |
Sorry to bug you again about CCP, but I'm seeing an anomaly that I'm
unable to explain, using capture compare to measure the RPM of a
spinning disk, with an opto-sensor across a slot. |
When you put an oscilloscope on the signal from the opto-sensor, what
do you see ? Post the amplitude and timing of each part of the signal.
Also, does this signal go through any conditioning circuits ? If so, post
the amplitude and timing of the final signal that reaches the CCP pin. |
|
|
Ttelmah
Joined: 11 Mar 2010 Posts: 19553
|
|
Posted: Mon Oct 10, 2016 12:57 pm |
|
|
If you are just reading RPM, then why are you fiddling around with the edges?. You only need to change edges to get period measurements. RPM, can just use the time between successive edges of the same type.
I'd guess that in fact your sensor returns a narrow pulse. Because of changing the edges, you are not measuring the actual period of the signal, but the time between the falling edge and the next rising edge, which then gives the repetition period _less_ the pulse width from the sensor. Hence your error..... |
|
|
PCM programmer
Joined: 06 Sep 2003 Posts: 21708
|
|
Posted: Mon Oct 10, 2016 12:58 pm |
|
|
That's what I was hinting at. |
|
|
Ttelmah
Joined: 11 Mar 2010 Posts: 19553
|
|
Posted: Mon Oct 10, 2016 2:21 pm |
|
|
PCM programmer wrote: | That's what I was hinting at. |
Your post didn't exist when I started typing.
Two minds with the same thought here.....
Code: |
___|¯¯|____________________________|¯¯|_____
+---------------------------+
He is measuring the +---------------------------+
|
He calls the both values 'FallTiime', but on the second is testing for L2H, so is the Rise time instead..... |
|
|
JamesW
Joined: 23 Apr 2007 Posts: 91 Location: Rochester, England
|
|
Posted: Tue Oct 11, 2016 2:15 am |
|
|
Thanks for all the help I am measuring between the two falling edges of the trace.
How do I post a picture to the forum?? I assume it's using the IMG tags? |
|
|
JamesW
Joined: 23 Apr 2007 Posts: 91 Location: Rochester, England
|
|
Posted: Tue Oct 11, 2016 2:34 am |
|
|
Sorted it - The power of the internet!
Other than a pull-up resistor, there is no conditioning on the output of the opto. Cable length is about 8cm.
I've grabbed the output of the logic analyser - and added code for a pin that is raised just before enabling the interrupt at the start of the capture process , and lowered at the end of the second state in the ISR.
[img]http://imgur.com/a/QJgUr[/img]
If I zoom in on the start of the trace for the opto, I see a bit of noise (to be expected - but is only for about 10-15uS (nowhere large enough to account for the difference in speeds).
[img]http://imgur.com/a/XSNqn[/img]
Is there anything else you'd like me to monitor? |
|
|
JamesW
Joined: 23 Apr 2007 Posts: 91 Location: Rochester, England
|
|
Posted: Tue Oct 11, 2016 2:36 am |
|
|
Well that almost worked, the links are there - albeit the img tags didn't quite perform as hoped! |
|
|
JamesW
Joined: 23 Apr 2007 Posts: 91 Location: Rochester, England
|
|
Posted: Tue Oct 11, 2016 2:41 am |
|
|
For clarity I have done a little tidying up of the ISR, just to save confusion over what edges I am monitoring, and bunged the variables in a structure as well.
Also - you can now see where I am raising/lowering the pin. (code in main in unaltered)
Code: |
/* ------------------------------------------------------------------------- */
/* CAPTURE COMPARE PORT INTERRUPT SERVICE ROUTINE FOR THE DC MOTOR */
#INT_IC1
void Service_CCP1_Interrupt()
{
DCMotorSystem.CaptureVal = get_capture(1);
switch (DCMotorSystem.DiskCaptureState)
{
/* EDGE 1 */
case WAIT_FOR_EDGE_1:
{
DCMotorSystem.FallTime1 = DCMotorSystem.CaptureVal;
DCMotorSystem.DiskCaptureState = WAIT_FOR_EDGE_2;
break;
}
/* EDGE 2 */
case WAIT_FOR_EDGE_2:
{
DCMotorSystem.FallTime2 = DCMotorSystem.CaptureVal;
DCMotorSystem.DiskCaptureState = IDLE;
disable_interrupts(INT_IC1);
/* CLEAR PIN TO SAY PROCESS ENDED */
output_low(AUX_1);
break;
}
case IDLE:
default:
{
break;
}
} /* END SWITCH */
}
/* ------------------------------------------------------------------------- */
void Start_Disk_Speed_Capture()
{
/* SETUP CAPTURE COMPARE FOR FALLING EDGE TIMER 2 */
setup_capture(1, CAPTURE_FE | CAPTURE_TIMER2);
set_timer2(0);
output_low(LED_2);
DCMotorSystem.DiskCaptureState = WAIT_FOR_EDGE_1;
/* SET PIN TO SAY PROCESS STARTED */
output_high(AUX_1);
clear_interrupt(INT_IC1);
enable_interrupts(INT_IC1);
}
|
Hopefully this will save any confusion.
James |
|
|
Ttelmah
Joined: 11 Mar 2010 Posts: 19553
|
|
Posted: Tue Oct 11, 2016 3:20 am |
|
|
You are doing exactly what we describe.....
Think about it. You program the interrupt to trigger on the falling edge. Where there is the 'bounce' on the rising edge, there are also extra falling edges. So the first time into the interrupt, you may record the falling edge time, but are much more likely to be recording the time of the 'bounce', which happens the pulse width later.....
Change your code to sample of the rising edge. Then the timing difference will only be the period of the bounce, not the period of the pulse. |
|
|
JamesW
Joined: 23 Apr 2007 Posts: 91 Location: Rochester, England
|
|
Posted: Tue Oct 11, 2016 3:40 am |
|
|
Well I'll be damned, that works!
Without any form of correction on the maths, I am now getting an RPM that is absolutely bang on the money.
For anyone else reading this thread in the future, the line to change is Setup_capture in the routine below.
Code: |
void Start_Disk_Speed_Capture()
{
/* SETUP CAPTURE COMPARE FOR RISING EDGE TIMER 2 */
setup_capture(1, CAPTURE_RE | CAPTURE_TIMER2);
set_timer2(0);
output_low(LED_2);
DCMotorSystem.DiskCaptureState = WAIT_FOR_EDGE_1;
/* SET PIN TO SAY PROCESS STARTED */
output_high(AUX_1);
clear_interrupt(INT_IC1);
enable_interrupts(INT_IC1);
}
|
Thanks again for your help chaps, I'd tried just about everything else - but not that. Don't know what I'd do without you
James |
|
|
Ttelmah
Joined: 11 Mar 2010 Posts: 19553
|
|
Posted: Tue Oct 11, 2016 3:58 am |
|
|
The lesson is if you have a 'noisy' edge, strangely this is the one you must sample on.... |
|
|
|