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

PIC24 CCP : RPM "fudge factor needed" [SOLVED]

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



Joined: 23 Apr 2007
Posts: 91
Location: Rochester, England

View user's profile Send private message Visit poster's website

PIC24 CCP : RPM "fudge factor needed" [SOLVED]
PostPosted: Mon Oct 10, 2016 10:38 am     Reply with quote

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

View user's profile Send private message Visit poster's website

PostPosted: Mon Oct 10, 2016 11:17 am     Reply with quote

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: 19537

View user's profile Send private message

PostPosted: Mon Oct 10, 2016 11:43 am     Reply with quote

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

View user's profile Send private message Visit poster's website

PostPosted: Mon Oct 10, 2016 12:00 pm     Reply with quote

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

View user's profile Send private message

PostPosted: Mon Oct 10, 2016 12:53 pm     Reply with quote

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: 19537

View user's profile Send private message

PostPosted: Mon Oct 10, 2016 12:57 pm     Reply with quote

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

View user's profile Send private message

PostPosted: Mon Oct 10, 2016 12:58 pm     Reply with quote

That's what I was hinting at.
Ttelmah



Joined: 11 Mar 2010
Posts: 19537

View user's profile Send private message

PostPosted: Mon Oct 10, 2016 2:21 pm     Reply with quote

PCM programmer wrote:
That's what I was hinting at.


Your post didn't exist when I started typing. Smile

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

View user's profile Send private message Visit poster's website

PostPosted: Tue Oct 11, 2016 2:15 am     Reply with quote

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

View user's profile Send private message Visit poster's website

PostPosted: Tue Oct 11, 2016 2:34 am     Reply with quote

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

View user's profile Send private message Visit poster's website

PostPosted: Tue Oct 11, 2016 2:36 am     Reply with quote

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

View user's profile Send private message Visit poster's website

PostPosted: Tue Oct 11, 2016 2:41 am     Reply with quote

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: 19537

View user's profile Send private message

PostPosted: Tue Oct 11, 2016 3:20 am     Reply with quote

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

View user's profile Send private message Visit poster's website

PostPosted: Tue Oct 11, 2016 3:40 am     Reply with quote

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 Smile

James
Ttelmah



Joined: 11 Mar 2010
Posts: 19537

View user's profile Send private message

PostPosted: Tue Oct 11, 2016 3:58 am     Reply with quote

The lesson is if you have a 'noisy' edge, strangely this is the one you must sample on.... Laughing
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