Log in

View Full Version : Vex Encoder Code


CircularLogic
02-08-2006, 15:39
Im just getting started on programming Vex with mplab. I have already worked with simple limit switches to control actions but now I would like to branch out into the more difficult realm of encoders.

I wrote a program that today that works but isnt correct.

void StartRightEncoder(void)
{

oldstate = rc_dig_in05;
state = rc_dig_in05;
if(oldstate != state)
{
rtick++;
}
oldstate = state;
}


It works to the extent that rtick increases, but I have a feeling that due to condition of incrementing rtick, I am missing many ticks from the encoder because it simply doesnt get the ticks quick enough. My emperical evidence is that when I set my motors to go when rtick is less than 270 (3 complete revolutions and with a wheel diam of approximately 3 inches, the code should make the bot go 9 inches give or take for round off error), the bot went more like 3 feet.

How does everyone else actually detect all the ticks? I checked the sample code on vexlabs but the encoder code isnt ready yet in the sample code download.

Mark McLeod
02-08-2006, 17:01
Couple of issues.

The code you posted seems to be fundamentally flawed, because "oldstate" always equals "state".
Essentially, oldstate = state = rc_dig_in05;
rtick will only increase if the encoder happens to "tick" exactly between those first two lines of the setting of oldstate and the setting of state. That can happen sometimes, but it would be pretty random. You want to avoid "windows" like this in your logic where all the ticks happening outside those first two lines are being ignored.

I assume you are polling the encoder, but are maybe polling it from the code slow loop? It should be polled from the fast loop. If you are running autonomously that would mean you need to call your function from User_Autonomous_Code() right before the line:

if (statusflag.NEW_SPI_DATA) /* 18.5ms loop area */


Make sure oldstate is a global variable (I imagine it is since it's not declared within your function), or declared static (meaning it sticks around), and it gets initialized to something, e.g.,
void StartRightEncoder(void)
{
static unsigned char oldstate=0;

state = rc_dig_in05;
if(oldstate != state)
{
rtick++;
}
oldstate = state;
}
There are two ways an encoder can be implemented: polling or interrupt.
You should try both ways to get the experience.

- Polling is just checking the encoder every software loop.
Polling works if the encoder updates are slow enough. To be safe you should calculate how many ticks per second you expect and compare that to how fast your code is polling. Polling the encoder should be performed in the fast loop, so you are less likely to miss a tick. Polling too slow will cause you to miss ticks and go further than you wanted. As a rough rule of thumb you want to poll at least twice as fast as the ticks will come in.
For instance, the slow loop in the vex controller runs at 18.5ms, so it makes 54 loops per second. The vex encoder is 90 ticks per revolution, so if it's more than a quarter revolution per second you'll be dropping counts as it moves. For your case if the robot covers the 9 inches in less than 11 seconds you'd be getting faulty counts. On the other hand the fast loop might give you on the order of a hundred thousand polls per second, so you could safely cover that same distance in 5.4 milliseconds without losing count!

-Interrupts are more reliable for higher speed ticks in that whenever the encoder state changes the PIC hardware immediately notifies your software - sort of like a good poke in the side. No matter what else your code is doing it will stop, add the tick, and go back to what it was doing before.
P.S. You still must calculate how fast you expect the ticks to arrive to be sure the interrupts don't happen too fast to handle. The danger to watch out for here is getting so many interrupts per second that the PIC gets overwhelmed.

CircularLogic
04-08-2006, 12:21
Alright, so I have fixed my StartEncoder functions to eliminate the window in my logic and I placed those function definitions in User_Routine_Fast. I actually want the effect of this function to happen during user control, so I have the call to StartRightEncoder() placed in User_Routine.

I placed all function definitions right before

void Process_Data_From_Local_IO(void)

After compiling all of this and running it, it still doesnt work as intended. If the motor is ran slowly (pwm = 154) then it goes about a quarter of a turn too far. However, if I put it at 255, it goes about three times too far (this is with the wheel coming directly off the motor).

I suspect that I am still not polling fast enough (due to the fact that I dont actually have anything in Process_Data_From_Local_IO) but when I placed my function definitions in Process_Data_From_Local_IO, I recieved a whole bunch of errors.

When I placed my call to StartRightEncoder() in Process_Data_From_Local_IO, I got a very odd pulsing of the motors (meaning they would run for a second, stop for a second, and then continue on in that pattern) when combined with the function

if(GetRightTick() <= 180)
{


pwm04 = 255;


}



Thanks for your help Mark, I know I ask a lot.

Mark McLeod
06-08-2006, 10:52
Your function definitions cannot be placed inside another function such as Process_Data_From_Local_IO(), so that's why you saw all those errors when you tried it. The function definitions can actually go in any file you like (even your own new file). Declaring your functions is like setting up a library of stuff you can call on to do. It's where and how you call them that's important.

Your description of the robot's behavior sounds clear enough. You're right that you have to call your polling function StartRightEncoder() from within Process_Data_From_Local_IO() to get the faster polling during regular user driving. So concentrate on fixing the odd pulsing you're seeing.

Offhand I'd say odd pulsing like that is the symptom of a variable overflow. pwm04=255 while GetRightTick() returns values of 0 to 179, then I assume you set pwm04=127 somewhere else. Because of momentum the robot continues to roll forward (or the wheels continue to spin) even though the motors are off. The momentum means you still get encoder counts, so rtick continues to get bigger and bigger. If rtick is declared as an unsigned char, for instance, then when the count tries to increase to 256, rtick will suddenly wrap around to be 0 again and your code will set pwm04=255. Hence, pulsing motors. The same thing will occur eventually with larger variable types as well, e.g., a signed int would buy you 91 feet of travel.

Your variable rtick needs to be a pretty large variable type. Large enough to hold the distances you're planning to cover. What type are all your variables?

CircularLogic
07-08-2006, 18:44
Both, rtick and ltick (for the StartLeftEncoder function) are unsigned ints.

If I use GetRightTick() (which returns rtick), the function works fine. However, if I use GetLeftTick(), I get the odd pulsing again. I cant see any differences in my code between the two functions besides the names and the variable returned.


void StartRightEncoder(void)
{
state = rc_dig_in05;
if(oldstate != state)
{
rtick ++;
}
oldstate = state;
}

unsigned int GetRightTick(void)
{
return rtick;
}

void ClearRightTick(void)
{
rtick = 0;
}

void ClearLeftTick(void)
{
ltick = 0;
}

unsigned int GetLeftTick(void)
{
return ltick;
}

void StartLeftEncoder(void)
{


state1 = rc_dig_in06;
if(oldstate1 != state)
{
ltick++;
}
oldstate1 = state1;
}



void Process_Data_From_Local_IO(void)
{
StartRightEncoder();
StartLeftEncoder();

/* Add code here that you want to be executed every program loop. */

}




Those are all my functions and are all placed in User_Routines_Fast


if(GetRightTick() <= 300)
{


pwm04 = 255;
pwm03 = 0;

}

And that is the code that uses those functions in User_Routines. If I use the code posted directly above, it works fine (and by that i mean the wheel spins and stops at logical points). But if I change the If statement to ahve GetLeftTick <= 300, the motors pulse again. I had that problem with GetRightTick(), but that problem was resolved for some reason unknown to me.




And then when I use GetRightTick(), if i want my wheel to spin twice (which should be 180 ticks), I have to set the tick number to 308 to get close to two spins.


Its times like these that I wish someone had a vex encoder function posted on the internet.

Mark McLeod
07-08-2006, 19:23
There's a typo in StartLeftEncoder.
state gets used in the if check instead of state1.
I'd consider standardizing your naming convention, e.g., ltick and lstate and loldstate or all the other way. The typos might make themselves more obvious that way.

Consider adding an else to your "<= 300" of pwm04=127;pwm03=127; so it's explicit that the motors are being turned off.

I'd try adding a printf to watch what ltick and rtick do when you turn the encoder by hand one revolution.

You're learning so much more writing this yourself!

CircularLogic
09-08-2006, 19:34
Doh, darn typos.

I fixed that and the Left encoder functions work just as well as the right encoder functions.

I have tried to use some printf()'s but they dont actually print to anything. Nothing shows up in the ifi loader terminal. Am i missing something?

Mark McLeod
09-08-2006, 22:12
I have tried to use some printf()'s but they dont actually print to anything. Nothing shows up in the ifi loader terminal. Am i missing something?

Every file that uses a printf() somewhere must also have

#include <stdio.h>

at the top. Otherwise, you won't see any output.