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