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 support@ccsinfo.com

Measuring speed with QEI
Goto page 1, 2  Next
 
Post new topic   Reply to topic    CCS Forum Index -> General CCS C Discussion
View previous topic :: View next topic  
Author Message
SoBot



Joined: 20 Apr 2010
Posts: 9

View user's profile Send private message

Measuring speed with QEI
PostPosted: Thu Jul 29, 2010 4:43 am     Reply with quote

I'm trying to measure speed using the QEI. My encoder has 512 tics per rotation. QEI is setup for 4x mode, that means the encoder has effectively 512x4=2048 tics per rotation. One tic is 360/2048 = 0.1758 deg. The velocity postscaler is set to 1, so every tic will cause a velocity interrupt. Now I read the value of VREG in the velocity capture interrupt and subtract the previous VREG value from it and put it in delta_t. The period of TMR5 is 1/5Mhz = 0.0000002 sec (when running at 20Mhz and timer 5 clock is divided by 4). Then the speed should be: speed = 0.1758/(delta_t*0.0000002). But I get wrong speed values like 5000 deg/sec when turning it by hand. The printf also gives gibberish when it goes too fast.

Can someone please give me some advice on how to do this?
Thanks

Here is my basic code:
Code:


#include <18f2431.h>
#fuses HS,NOWDT,NOBROWNOUT,NOPUT,NOPROTECT
#use delay(clock=20M)
#use rs232(baud=9600,parity=N, xmit=PIN_A2,rcv=PIN_B5,ERRORS)

int main()
{
      long time0,time1;
      int delta_t;
      float speed;

      InitQEI();
      enable_interrupts(GLOBAL);

While(1)
  {
      printf("speed=%f\n\r",speed);
      delay_ms(100);
  }
}


void InitQEI(void)
{
   QEICON = 0b00111000;//  QEI 4x mode,velocity postscaler 1:1
               DFLTCON = 0b1110100;
   POSCNT = 0;            
   MAXCNT = 2047;               bit_set(CAP1CON, 6);             //set the CAP1REN bit to reset the TMR5 after velocity event capture

   setup_timer_5(T5_INTERNAL|T5_DIV_BY_4);
   set_timer5(0);

   enable_interrupts(INT_IC1);
//   enable_interrupts(INT_IC2QEI);
//   enable_interrupts(INT_IC3DR);
   enable_interrupts(INT_TIMER5);
   
return;
}



#int_IC1
//  Velocity capture interrupt
// delta_t = (current timer value - previous timer value) 
// speed = (degrees per tik * velocity post scaler)/(delta_t * (1/(Fc/TMR5_DIV)))
//
void IC1_isr()
{
   
   time1 = VREG;
   delta_t = time1 - time0;
   speed = 0.1758/((delta_t)*0.0000002);
   time0 = time1;   

}
PCM programmer



Joined: 06 Sep 2003
Posts: 21708

View user's profile Send private message

PostPosted: Thu Jul 29, 2010 10:48 am     Reply with quote

Without looking at the QEI aspects at all, and just looking at coding
issues I can see at least one thing.
Quote:

printf("speed=%f\n\r",speed);

Here you're printing a float value. A float consists of 4 bytes. But you
have interrupts enabled while you're printing it, so you easily could get an
interrupt in the middle of the (ASM) printf code. The ASM code might
have fetched 1, 2, or 3 bytes of 'speed', and then the interrupt changes
the remaining byte(s) and then you print that, which will likely look like
garbage.
Kenny



Joined: 07 Sep 2003
Posts: 173
Location: Australia

View user's profile Send private message

PostPosted: Thu Jul 29, 2010 6:33 pm     Reply with quote

There are several other problems without looking at the code too closely.

1. The input to the timer 5 prescaler is Fosc/4 not Fosc.

2. T5 will overflow at low speeds. Define the minimum rotational speed that you need it to work, and set the T5 prescaler and velocity event postscaler so that T5 doesn't overflow.

3. Measuring instantaneous speed every 0.1758 degrees of rotation will keep the processor very busy.
Increase the interval so that the processor can cope at the highest rotational speed expected.
Since T5 will be reset after each capture, the T5 count will be the measurement.
Either set a flag in the IC1 isr and poll for it in main(), or much faster poll the hardware flag bit IC1IF and clear it after getting the captured T5 count.
If conversion to rpm is needed, to speed things up precalculate some values and use scaled integer arithmetic.

From one of my old projects:

Code:

Using 18F2331 QEI mode to measure velocity by capturing timer 5 counts
(actually measuring period.
Using a 360 slot encoder. Speed control range required is 10 RPM to 100 RPM.
QEI index input connected to com. - not required for velocity measurement.
     
Speed RPM:                                10 RPM               100 RPM
Speed Hz:                                 0.1666Hz             1.6666Hz
Rotational Period:                        6 Sec.               0.6 Sec.
QEI x2 mode, pulse interval (0.5 deg.)    8.333mS              0.8333mS
Vel event div 16 interval (8 deg.)      133.333mS             13.3333mS
T5 steps, 4MHz xtal, prescaler 8,     16666.66              1666.66

This fits within 16 bit T5 range. To calculate RPM from T5 value:
 
Time for one rotation is t5_count * (360/8)* 8uS  = t5_count * 360 uS.
RPM is (60Sec * 1,000,000)/(t5_count * 360) = 166,667/t5_count.
With rounding (166,667 + (t5_count >> 1))/t5_count
psicko275



Joined: 21 Mar 2013
Posts: 12

View user's profile Send private message

PostPosted: Thu Apr 04, 2013 3:38 pm     Reply with quote

Kenny wrote:
There are several other problems without looking at the code too closely.

1. The input to the timer 5 prescaler is Fosc/4 not Fosc.

2. T5 will overflow at low speeds. Define the minimum rotational speed that you need it to work, and set the T5 prescaler and velocity event postscaler so that T5 doesn't overflow.

3. Measuring instantaneous speed every 0.1758 degrees of rotation will keep the processor very busy.
Increase the interval so that the processor can cope at the highest rotational speed expected.
Since T5 will be reset after each capture, the T5 count will be the measurement.
Either set a flag in the IC1 isr and poll for it in main(), or much faster poll the hardware flag bit IC1IF and clear it after getting the captured T5 count.
If conversion to rpm is needed, to speed things up precalculate some values and use scaled integer arithmetic.

From one of my old projects:

Code:

Using 18F2331 QEI mode to measure velocity by capturing timer 5 counts
(actually measuring period.
Using a 360 slot encoder. Speed control range required is 10 RPM to 100 RPM.
QEI index input connected to com. - not required for velocity measurement.
     
Speed RPM:                                10 RPM               100 RPM
Speed Hz:                                 0.1666Hz             1.6666Hz
Rotational Period:                        6 Sec.               0.6 Sec.
QEI x2 mode, pulse interval (0.5 deg.)    8.333mS              0.8333mS
Vel event div 16 interval (8 deg.)      133.333mS             13.3333mS
T5 steps, 4MHz xtal, prescaler 8,     16666.66              1666.66

This fits within 16 bit T5 range. To calculate RPM from T5 value:
 
Time for one rotation is t5_count * (360/8)* 8uS  = t5_count * 360 uS.
RPM is (60Sec * 1,000,000)/(t5_count * 360) = 166,667/t5_count.
With rounding (166,667 + (t5_count >> 1))/t5_count


Hi, You could explain more about this?

I work in this topic but I don't read the correct speed. Sad
asmboy



Joined: 20 Nov 2007
Posts: 2128
Location: albany ny

View user's profile Send private message AIM Address

PostPosted: Thu Apr 04, 2013 5:21 pm     Reply with quote

you say
"turning it by hand"

what is "IT" ??

got a schematic ??

#1-
has this actually been built, or are we discussing a simulation??

#2
WHAT is rotating and how fast as a top speed ??

that has a great bearing on the situation.

care 2 share what this design is ABOUT ??
psicko275



Joined: 21 Mar 2013
Posts: 12

View user's profile Send private message

PostPosted: Fri Apr 05, 2013 7:22 am     Reply with quote

Quote:
what is "IT" ??


I have a DC motor with encoder of 1200 PPR in specific, this: http://www.pololu.com/catalog/product/1442

Quote:
has this actually been built, or are we discussing a simulation??


This is a real problem, I connected this motor in a PCB with a PIC18F4431.

Quote:
WHAT is rotating and how fast as a top speed ??


The top speed of this application is 100 RPM.

Quote:
care 2 share what this design is ABOUT ??


sorry I don't understand your question!
asmboy



Joined: 20 Nov 2007
Posts: 2128
Location: albany ny

View user's profile Send private message AIM Address

PostPosted: Fri Apr 05, 2013 7:50 am     Reply with quote

with what you reveal,
that works out to a top speed of 200 HZ coming from a single encoder,
and
you are NOT using the quadrature signal to detect direction, correct? -

we can also assume the minimum speed is zero hz -
and this further implies your code,
if this is for a real product -
needs to be trapped.
SO....
what sort of data UNITS are you trying to capture and return to the host ??

HZ ?? RPM ?? other ??
Ttelmah



Joined: 11 Mar 2010
Posts: 19369

View user's profile Send private message

PostPosted: Fri Apr 05, 2013 8:11 am     Reply with quote

and of course we have the old one of doing a float calculations in the interrupt.
One multiplication, and a division. Total time at 20MHz, 361.4uSec. Add the other code in the interrupt, and the time to get in/out, and you have a maximum count rate of about 2500ints/second. With 2048 triggers per rev, just about 75RPM.....

Never ever ever (unless you know exactly what you are doing), do anything other than the simplest integer arithmetic inside an ISR.
Read the time, and get out. The ISR should concentrate on being as simple as possible.

Then in the main loop store a copy of the time verify it hasn't changed, and work on this. Do the maths outside, using this value.

Best Wishes
psicko275



Joined: 21 Mar 2013
Posts: 12

View user's profile Send private message

PostPosted: Fri Apr 05, 2013 8:13 am     Reply with quote

I try to return RPM, correct in this moment I'm not intersted in the direction, only speed.

the speed 0-100 RPM and I try to convert with this equation:

RPM=( (Operating Frequency/4) / (PPR x Velocity Update Rate x Pulse Reduction Ratio x Timer5 Prescale x VELR<H:L>)) x 60

this equation is: EQUATION 5: CALCULATING SPEED FROM VELOCITY REGISTER VALUE in the note: AN899: Brushless DC Motor Control Using PIC18FXX31 MCUs, page 15.

so
Ttelmah wrote:
and of course we have the old one of doing a float calculations in the interrupt.
One multiplication, and a division. Total time at 20MHz, 361.4uSec. Add the other code in the interrupt, and the time to get in/out, and you have a maximum count rate of about 2500ints/second. With 2048 triggers per rev, just about 75RPM.....

Never ever ever (unless you know exactly what you are doing), do anything other than the simplest integer arithmetic inside an ISR.
Read the time, and get out. The ISR should concentrate on being as simple as possible.

Then in the main loop store a copy of the time verify it hasn't changed, and work on this. Do the maths outside, using this value.

Best Wishes


in the interruption only get the count of the REGVEL?, this:

Code:
#int_IC1
/*
   Velocity capture interrupt
*/
void IC1_isr()
{
   count=qei_get_count(QEI_GET_VELOCITY_COUNT);
   set_timer5(0);
}


thanks for answer me!
Ttelmah



Joined: 11 Mar 2010
Posts: 19369

View user's profile Send private message

PostPosted: Fri Apr 05, 2013 8:28 am     Reply with quote

Yes. Then in the main code have code like:
Code:

    int16 count_copy;

    //your main display loop
    while
    {
         do
             count_copy=count;
         while (count_copy!=count);

         //Now do the arithmetic on count_copy;
   
         //and display this
     }

Key is that if 'count' changes at the moment it is copied or tested, it'll be copied again. Now you can work on the copy and even if the count changes a dozen times while you are doing the maths and displaying the result, the value will be 'right' for the moment the reading was taken.
Like this you should be able to go about 15* faster without problems, but you will still run out of time if the speed pushes upwards.....

Best Wishes
psicko275



Joined: 21 Mar 2013
Posts: 12

View user's profile Send private message

PostPosted: Fri Apr 05, 2013 8:52 am     Reply with quote

I done this:

Code:

int16 ad=0, count=0, count_copy=0;
int16 ENCODER_PPR = 1200;
float RPM_CONSTANT_QEI = 0;

#int_IC1
/*
   Velocity capture interrupt
*/
void IC1_isr()
{
   count=qei_get_count(QEI_GET_VELOCITY_COUNT);
   ad=get_timer5();
   set_timer5(0);
}

void main()
{
   setup_spi(spi_slave | SPI_MODE0 | SPI_SS_DISABLED);
   /*setup_power_pwm(modes, postscale, time_base, period, compare, compare_postscale, dead_time)*/
   setup_power_pwm( PWM_CLOCK_DIV_4| PWM_FREE_RUN | PWM_UPDATE_ENABLE, 1, time_base, period, 0, 1, dead_time);
   
   /*setup_power_pwm_pins(module0,module1,module2,module3) .- PWM_OFF, PWM_ODD_ON, PWM_BOTH_ON, PWM_COMPLEMENTARY*/
   setup_power_pwm_pins(PWM_BOTH_ON,PWM_BOTH_ON,PWM_OFF,PWM_OFF);
   setup_adc_ports(sAN0);
   setup_adc(ADC_CLOCK_INTERNAL | VSS_VDD);
   /*setup QEI*/
   setup_qei(QEI_MODE_X4_RESET_ON_MATCH | QEI_VELOCITY_MODE_ENABLED | QEI_VELOCITY_PULSE_DIV_4 ,QEI_FILTER_ENABLE_QEB | QEI_FILTER_ENABLE_QEA, 4800);
   SETUP_TIMER_5(T5_INTERNAL | T5_DIV_BY_8);      //overflow 13.1ms
   clear_interrupt(int_ssp);
//!   enable_interrupts(INT_TIMER5);
   enable_interrupts(INT_SSP);
   enable_interrupts(INT_IC1);
   enable_interrupts(INT_IC3DR);
   enable_interrupts(INT_IC2QEI);
   enable_interrupts(GLOBAL);

   while(true)
   {
      set_adc_channel(0);     // Select ADC Channel 1
      if (adc_done()==1)
         adc_val = read_adc();   // Read Current ADC Value
      output_toggle(LED);
      set_power_pwm0_duty(power_pwm_0);
      set_power_pwm2_duty(power_pwm_1);
     
      cte_velocity_rps = 625000/ENCODER_PPR;    //cte_rps = (Operating Frequency/4) / (Timer5 Prescale x PPR)
      cte_velocity_rpm = cte_velocity_rps*60;
      count_copy=count;
     
//!      RPM_CONSTANT_QEI =((INSTRUCTION_CYCLE)/(ENCODER_PPR*2048*count))*60 ; //In RPM
//!      RPM_CONSTANT_QEI =cte_velocity_rps/ad ;      //In RPS revolutions per second
//!      RPM_CONSTANT_QEI =cte_velocity_rpm/ad ;      //In RPM revolutions per minute
      while (count_copy!=count);
      RPM_CONSTANT_QEI = 1953.125/count_copy;
//!      RPM_CONSTANT_QEI=-10.12;

   }  /*End to while(true)*/

}  /*End to main(void)*/


and not work, I'm desperate, no know I'm doing wrong. Sad
asmboy



Joined: 20 Nov 2007
Posts: 2128
Location: albany ny

View user's profile Send private message AIM Address

PostPosted: Fri Apr 05, 2013 2:19 pm     Reply with quote

Code:

#int_IC1

void IC1_isr()
{ static int16 count;
   count=qei_get_count(QEI_GET_VELOCITY_COUNT);
     set_timer5(0);
}

void main() {
//#use232 9600 baud ..... for your part
  int16 count_copy,oldcopy=0;
   /*setup QEI*/
   setup_qei(QEI_MODE_X4_RESET_ON_MATCH | QEI_VELOCITY_MODE_ENABLED | QEI_VELOCITY_PULSE_DIV_4 ,QEI_FILTER_ENABLE_QEB | QEI_FILTER_ENABLE_QEA, 4800);
   SETUP_TIMER_5(T5_INTERNAL | T5_DIV_BY_8);      //overflow 13.1ms
 
   enable_interrupts(INT_IC1);
    enable_interrupts(GLOBAL);
   
   while(1)   {
               do {
                   count_copy=count;
             }while (count_copy!=count);
      if (copy_count)!=oldcopy){
         printf("%Lu \r\n",copy_count);
         delay_ms(100);
         oldcopy=copycount);
      }

   }  /*End to while(true)*/

}  /*End to main(void)*/


KISS my friend
can you receive serial and run on your system
what do you see now ???
psicko275



Joined: 21 Mar 2013
Posts: 12

View user's profile Send private message

PostPosted: Thu Apr 11, 2013 6:38 am     Reply with quote

asmboy wrote:


KISS my friend
can you receive serial and run on your system
what do you see now ???


I tried the code and resolved errors and I see the speed register, what more we try ?
asmboy



Joined: 20 Nov 2007
Posts: 2128
Location: albany ny

View user's profile Send private message AIM Address

PostPosted: Thu Apr 11, 2013 1:15 pm     Reply with quote

Quote:

I see the speed register, what more we try ?


Is not my project, so how will I know ??
BUT -- if it was my project, i would begin to build up and test my FP math
on the register data you are getting,
to create the displayed value range you wish to show to the world. Very Happy

Work incrementally and slowly when you are not sure of what you are doing. That's how I approach such things.

Build on small successful code changes to get your overall project to work accurately and with stability.
psicko275



Joined: 21 Mar 2013
Posts: 12

View user's profile Send private message

PostPosted: Thu Apr 11, 2013 1:57 pm     Reply with quote

asmboy wrote:
Quote:

I see the speed register, what more we try ?


Is not my project, so how will I know ??
BUT -- if it was my project, i would begin to build up and test my FP math
on the register data you are getting,
to create the displayed value range you wish to show to the world. Very Happy

Work incrementally and slowly when you are not sure of what you are doing. That's how I approach such things.

Build on small successful code changes to get your overall project to work accurately and with stability.


So, why you write the past code?, This has always worked, I thought that you have a idea!
And still do not know how to read speed with the velreg
Thanks for the advices!
Display posts from previous:   
Post new topic   Reply to topic    CCS Forum Index -> General CCS C Discussion All times are GMT - 6 Hours
Goto page 1, 2  Next
Page 1 of 2

 
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