![](templates/subSilver/images/CCSLogo.jpg) |
![CCS C Software and Maintenance Offers](templates/subSilver/images/forumAd6.jpg) |
View previous topic :: View next topic |
Author |
Message |
SoBot
Joined: 20 Apr 2010 Posts: 9
|
Measuring speed with QEI |
Posted: Thu Jul 29, 2010 4:43 am |
|
|
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;
} |
|
|
![](templates/subSilver/images/spacer.gif) |
PCM programmer
Joined: 06 Sep 2003 Posts: 21708
|
|
Posted: Thu Jul 29, 2010 10:48 am |
|
|
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. |
|
![](templates/subSilver/images/spacer.gif) |
Kenny
Joined: 07 Sep 2003 Posts: 173 Location: Australia
|
|
Posted: Thu Jul 29, 2010 6:33 pm |
|
|
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
|
|
|
![](templates/subSilver/images/spacer.gif) |
psicko275
Joined: 21 Mar 2013 Posts: 12
|
|
Posted: Thu Apr 04, 2013 3:38 pm |
|
|
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](images/smiles/icon_sad.gif) |
|
![](templates/subSilver/images/spacer.gif) |
asmboy
Joined: 20 Nov 2007 Posts: 2128 Location: albany ny
|
|
Posted: Thu Apr 04, 2013 5:21 pm |
|
|
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 ?? |
|
![](templates/subSilver/images/spacer.gif) |
psicko275
Joined: 21 Mar 2013 Posts: 12
|
|
Posted: Fri Apr 05, 2013 7:22 am |
|
|
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! |
|
![](templates/subSilver/images/spacer.gif) |
asmboy
Joined: 20 Nov 2007 Posts: 2128 Location: albany ny
|
|
Posted: Fri Apr 05, 2013 7:50 am |
|
|
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 ?? |
|
![](templates/subSilver/images/spacer.gif) |
Ttelmah
Joined: 11 Mar 2010 Posts: 19369
|
|
Posted: Fri Apr 05, 2013 8:11 am |
|
|
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 |
|
![](templates/subSilver/images/spacer.gif) |
psicko275
Joined: 21 Mar 2013 Posts: 12
|
|
Posted: Fri Apr 05, 2013 8:13 am |
|
|
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! |
|
![](templates/subSilver/images/spacer.gif) |
Ttelmah
Joined: 11 Mar 2010 Posts: 19369
|
|
Posted: Fri Apr 05, 2013 8:28 am |
|
|
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 |
|
![](templates/subSilver/images/spacer.gif) |
psicko275
Joined: 21 Mar 2013 Posts: 12
|
|
Posted: Fri Apr 05, 2013 8:52 am |
|
|
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](images/smiles/icon_sad.gif) |
|
![](templates/subSilver/images/spacer.gif) |
asmboy
Joined: 20 Nov 2007 Posts: 2128 Location: albany ny
|
|
Posted: Fri Apr 05, 2013 2:19 pm |
|
|
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 ??? |
|
![](templates/subSilver/images/spacer.gif) |
psicko275
Joined: 21 Mar 2013 Posts: 12
|
|
Posted: Thu Apr 11, 2013 6:38 am |
|
|
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 ? |
|
![](templates/subSilver/images/spacer.gif) |
asmboy
Joined: 20 Nov 2007 Posts: 2128 Location: albany ny
|
|
Posted: Thu Apr 11, 2013 1:15 pm |
|
|
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.
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. |
|
![](templates/subSilver/images/spacer.gif) |
psicko275
Joined: 21 Mar 2013 Posts: 12
|
|
Posted: Thu Apr 11, 2013 1:57 pm |
|
|
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.
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! |
|
![](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
|