1
\$\begingroup\$

I'm making an obstacle avoiding robot and using the mentioned PIC controller. I'm currently not at the obstacle avoiding part so what i'm doing is writing code to control the movement, e.g. making it go in a rectangular loop. The way it works is that I have made functions for going straight, turning left/right and one for a small 2 second halt on both wheels called pause. In the straight function I pass an RPM value and a Distance, the function then has to figure out how long this velocity has to be maintained to travel the specified distance. It does this by passing the data to a counts_cal function which returns the value of the counts to be used and then the wheel speeds are set and the counts are sent to a timer function to produce the desired timing. Now the problem is that the counts_cal function isn't working and I think its because of the floats I have used and somehow the conversion from float to long isn't producing what its supposed to. The timer function and the rest works since when I directly passed the counts from straight to timer and bypassed the counts_cal, it all worked fine. But bypassing it is not really an option and I need help because I'm out of my depth with this floating point stuff in micro controllers. When i try this code as it is shown here, what happens is that it acts as if there is no delay on the straight and turn left functions but works fine for the pause since I have passed the counts manually there.

I'm showing code for only the straight function and ignoring the turn_right/left and pause because they are exactly similar in their function.

Here is the relevant code part :

//----------FUNCTION DECLARATIONS-----------//

void send_pos_right (int data);              //Transmit sequence to right wheel(forward)
void send_pos_left (int data);               //Transmit sequence to left wheel(forward)
void straight(int rpm, float dist);         //go straight at certain rpms for certain dist
void turn_left(void);                       //make 90 degree left
void turn_right(void);                      //make 90 degree right
void pause(void);                           //halt both wheels for a brief time (2sec)

void timer(unsigned long counts);                 //produces required time delay based on counts
unsigned long counts_cal (int rpm, float dist);   //calculates number of counts required for travel

//..........GLOBAL VARIABLES...........//

float circum = 0.3989823;           //circumference of wheels
float mc = 0.0000512;               //timer period with 256 prescaler
int turning_rpm = 30;               //standard turning velocity
float turning_dist = 0.44;          //distance to make a 90 degree turn

//............MAIN CODE.............//

void main(void)
{

while(1)
{
    straight(60,4);
    pause();
    turn_left();
    pause();

   }
}

void straight(int rpm, float dist)
{
    unsigned long counts = counts_cal(rpm,dist);
    send_pos_right(rpm);
    send_pos_left(rpm);
    timer(counts);
}

void timer(unsigned long counts)
{
    unsigned char loader_H, loader_L;
    int reps = counts/65536;                        //calculates number of complete reloads required
    unsigned long sub_reps = counts-(reps*65536);   //calculates number of counts required after the complete reloads (semi relaod)
    long value = 65536 - sub_reps;                  //calculates value to load into TMR0H & TMR0L for the semi reload
    T0CON = 0x07;                                   //timer off, 256 prescaler

    while(reps>0)                   //performs the number of complete 16bit timer reloads
    {
        TMR0H = 0x00;
        TMR0L = 0x00;
        T0CONbits.TMR0ON=1;
        while(INTCONbits.TMR0IF==0);
        T0CONbits.TMR0ON=0;
        INTCONbits.TMR0IF=0;
        reps-=1;
    }

    if ((reps==0) && (sub_reps>0))  //performs the semi 16bit timer reload
    {
        loader_H = (value&0xFF00)>>8;
        loader_L = (value&0x00FF);
        TMR0H = loader_H;
        TMR0L = loader_L;

        T0CONbits.TMR0ON=1;
        while(INTCONbits.TMR0IF==0);
        T0CONbits.TMR0ON=0;
        INTCONbits.TMR0IF=0;
    }

}

unsigned long counts_cal (int rpm, float dist)
{
    float rps = rpm/60.0;
    float v = circum * rps;
    float t = dist/v;
    float temp = t/mc;
    unsigned long counts = (unsigned long)(temp);
    return(counts);                                 //returns counts which are required to produce desired time delay
}
\$\endgroup\$
  • 1
    \$\begingroup\$ Is this the free version of XC8? It's been my observation that non-Pro versions of XC8 handle floating point and implement anything in math.h about as well as molasses flows in Minot in January. \$\endgroup\$ – Matt Young Mar 29 '15 at 16:06
  • \$\begingroup\$ yes it's the free version of XC8 but I can't really get the pro version right now. \$\endgroup\$ – Hameem Mar 29 '15 at 16:09
  • \$\begingroup\$ I wouldn't anyway, it's not really worth it. If you really want to get around it, you can choose a vendor that doesn't cripple their already buggy compiler. CCS and MikroC seem to be popular. I'm sure there are others. \$\endgroup\$ – Matt Young Mar 29 '15 at 16:16
  • 1
    \$\begingroup\$ Btw, is there any fixed point method of dealing with this specific situation if floating point is not possible ? \$\endgroup\$ – Hameem Mar 29 '15 at 16:16
  • \$\begingroup\$ There's always a way to use fixed point. I wouldn't entirely give up hope of somebody having a work around though. We have some PIC experts here. \$\endgroup\$ – Matt Young Mar 29 '15 at 16:19
1
\$\begingroup\$

So I sort of figured out how to get this running myself. What I had done previously was this;

unsigned long counts_cal (int rpm, float dist)
{
    float rps = rpm/60.0;
    float v = circum * rps;
    float t = dist/v;
    float temp = t/mc;
    unsigned long counts = (unsigned long)(temp);
    return(counts);                                 //returns counts which are required to produce desired time delay

}

But then I noticed that floats were causing problems when I equated the floats to ints and I thought it might be due to errors of truncation when converting to int or long, so I tried something a little different and boiled down the first 5 lines into 1 line after some algebraic manipulation and it worked.I think this worked because now all the multiplications are happening before the division and then when the division + conversion does take place, the truncated value has a very forgivable error (off by less than 100 ms from what I've observed).The PWMs are being maintained for the required timing and all is well. The working method was this;

unsigned long counts_cal (int rpm, float dist)
{
    unsigned long counts = (dist*60*78125)/(4*circum*rpm);
    return(counts);

}
\$\endgroup\$
  • \$\begingroup\$ You can simply it further. Condense all the constants into a single number 2937160.4 (=60*78125/(4*circum), and don't put the calculation result into counts (since you do that anyway after returning). Then it can all go on one line like this:- return((unsigned long)(dist*2937160.4/rpm));. This is almost twice as fast as your original code! \$\endgroup\$ – Bruce Abbott Mar 30 '15 at 4:58
  • 2
    \$\begingroup\$ @BruceAbbott No need to calculate the numbers in advance, simply make sure that the constants are grouped together and then have the pre-processor do the calculation. Gives far more readable code. 60*78215 for example is never computed in run-time, the compiler will put the number 4692900 in the machine code. Of course you shouldn't be writing "magic numbers" in the source code in the first place, declare them as constants somewhere. \$\endgroup\$ – Lundin Mar 30 '15 at 15:27

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.