I ahve a little GPS module that I'm trying to read with a PIC and convert

the data and display on an LCD screen.

The GPS outputs speed as a 16 bit word where 1 bit - 0.1 km/h and I'd like

to convert that to MPH before displaying it on a screen. I need to multiply

the word received from the GPS by a fractional number before having

something to display on the screen.

How do I do that in a 8 bit PIC18 series part using assembly? I know there

are some routines for dealing with floating point numbers in a pIC but I

don't quite know how to start. How do you convert a 16 bit word to a float

so you can do the multiply?

In assembly, I'd just avoid floating point entirely.

I don't know for certain what you'd like to output. You mention just

converting to miles/hr, but since your input is in 1/10ths of km/hr

let's say you wanted to generate an integer that was in 1/10ths of

miles/hr. That way, you could just convert this binary integer into

ASCII output and insert a period just before the last digit. So let

me assume that so you can see.

Let's call your value 'x'. It is an integer in tenths of km/hr. To

convert this to km/hr, it is (x/10). To convert km/hr to miles/hr,

you need to:

(x/10) / [ 5280 ft/mile * 12 in/ft * 25.4 mm/in * 10^-6 km/mm ]

That is:

100000

x * -------

1609344

But to convert to 1/10ths of a mile/hr, multiply that constant by 10,

so you actually need to compute this:

1000000

x * -------

1609344

In the first case above, you can see why some suggested just dividing

by 16. Looks close enough. But let's go with the 2nd case I

mentioned and compute tenths of a mile/hr, as in integer.

Let's first remove prime factors:

15625

x * -----

25146

That helps. You could, if you have the routines handy, just multiply

your 16-bit 'x' by 15625 to compute a 32-bit numerator, then use a

32-by-16 divide routine to divide that result by 25146.

But let's say you want to look a little further. Use continued

fractions (look it up, if you want) to approximate that fraction. The

continued fraction for the above fraction is:

[ 0, 1, 1, 1, 1, 1, 3, 1, 2, 7, 1, 1, 15 ]

In terms of possible ratios, in decreasing accuracy, they are then

formed from the above continued fraction by removing final terms:

Term Fraction Decimal value

15: 15625/25146 0.621371192237334

1: 1006/1619 0.6213712168004941

1: 535/861 0.6213704994192799

7: 471/758 0.6213720316622692

2: 64/103 0.6213592233009708

1: 23/37 0.6216216216216216

3: 18/29 0.6206896551724138

1: 5/8 0.625

1: 3/5 0.6

1: 2/3 0.6666666666666666

1: 1/2 0.5

1: 1/1 1

0: 0 0

As you can see, you can pick your poison. Looking down the list, you

can see that perhaps 5/8ths isn't so bad. In this case, you multiply

your 'x' value by 5 (which is a shift left two and add) and then

divide by 8 (which is a shift right three -- and just check the carry

out for rounding, if you want.) That would take the value of 160

(which is 16 km/hr) and convert it to 100 (which is 10 mi/hr.) That

might be close enough and would be easily done in assembly.

If you need greater accuracy, work your way up the chain. But as you

can see, the divisors aren't powers of 2 anymore so a simple shift

won't work and you'll be looking for an integer division routine,

perhaps.

If you really do just want miles/hr and not tenths of miles/hr, then

the continued fraction setup looks like, in descending precision:

Term Fraction Decimal value

6: 3125/50292 0.0621371192237334

4: 503/8095 0.062137121680049416

1: 107/1722 0.062137049941927994

2: 75/1207 0.06213753106876554

2: 32/515 0.062135922330097085

1: 11/177 0.062146892655367235

10: 10/161 0.062111801242236024

16: 1/16 0.0625

0: 0 0

There you can see the (1/16) recommended elsewhere. But you can also

see other options for more precision, assuming you've got a nifty

integer division algorithm floating about and want greater accuracy.

Hope that helps.

Jon