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?
If you don't want perfect accuracy - you need to a) divide by 10 b) multiply by 5/8 Equivalently, you need to divide by 16 - which you can do by shifting right 4 places. You can use a look up table to deal with the 4 bit remainder
There should be some routines to do this conversion (integer->float). In general you'd normalize the number by left shifting until the ms bit is 1, and then one more shift (no sense storing the leftmost bit if it's always one). The exponent gets decremented from a starting value (typically biased from zero) with each shift. When you see the floating point format described, it should be pretty obvious. Usually you're going to also want the reverse function to display the number.
For only a single operation you might find it easier to work entirely in fixed point and avoid the double conversions. For example you could use some routines that would handle 16 or 32 bit math (or write them). Multiply by 10 and then divide by 16 (shift), more-or-less as Brian suggested would give you 0.1 MPH resolution and ~0.6% error.
Personally, for an accurate instrument, in assembly, I'd use a 32-bit x 32-bit -> 64-bit multiply by a fixed 32-bit constant of 0x4F9175A (keep the most significant 32 bits) and get essentially perfect accuracy, probably faster than the floating point calculations and conversions, but hey, that's just me.
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:
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:
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:
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.
I usually start by seeing what arithmetic routines are already linked into the program in question and try to avoid adding more if I can.
Suppose that what I already have is a 16x16 multiply. I don't want to introduce a divide as well, because I'm short on code space, so I want to divide by a power of two, or even better a power of 256. 4072/65536 is 0.0621338, not too bad at all. This can be implemented as multiplying the value by 4072 and then taking the upper two bytes of the result, possibly rounding if desired.
Alternatively, suppose I have a 24/24 divide, but no multiply (this has happened). I'd put the number in the highest two bytes of my dividend and make the lowest byte zero. Then I want to divide by N such that 256/N = 0.621371, giving N=412. This gives 256/412=0.621359.
I like it. It can be observed easily by computing (5/8 - 15625/25146) in floating point notation (32-bit is 3B6DD100) and noting the mantissa, which is 1.11011011101.... * 2^-9. All those bits near the leading left side spells an easy round up to 2^-8, which is your
1/256. Good call.
It's better than you give here, though. The expression is also just KM*( (5*32 - 1)/256 ). This codes up as:
Isnt your GPS module capable of NMEA output ?? NMEA is a full text format, fixed field width, newline delimited.... Just find the command to tell the GPS to output NMEA and then decode the string and get preformatted text :)
Not this one. Its a Furuno GH81. Tiny little sucker at .8" x .8" but it has its own binary protocol. Talk about a PITA. It outputs words in 7 bit bytes with the msb set to 1. I have to concatinate the 7 bits in each byte (eliminating the 8th bit in each byte) to get the a 14 bit word of actual data.
I'd never seen that before and thought my unit was broken when I tried to do a straight conversion.
ElectronDepot website is not affiliated with any of the manufacturers or service providers discussed here.
All logos and trade names are the property of their respective owners.