October
2004, Issue 171
Test Your
EQ
|
Answer
4It
is always a good practice to keep your resume updated.
However, Frugalson need not panic. Integer arithmetic
functions are simple to implement in assembly. As long
as Frugalson can download a free assembler, the project
is not lost yet!
With
a known domain (range of input values) and range (range
of output values), floating-point numbers are generally
unnecessary. In this case, each count of the ADC represents
10 V/4096, and the MCU can use the same units for internal
storage and computation. When it is time to convert an
internal value (v) to volts with two digits after the
decimal point, Frugalson can use simple integer arithmetic
operations.
Although
Frugalson could implement a full 16 × 16 bit integer multiplication
function, all he really needs is to multiply by 10, or
1010 binary. Because 10 = 2 + 8, and 2 and 8 are both
powers of two, he can rewrite v × 10 as v × 2 + v
× 8. Simply simply shifting v to the left can generate
the two terms. On an 8-bit processor, this requires a
total of six shift operations and two additions, along
with 2 bytes of temporary storage.
The
other thing Frugalson needs to realize is that he can
think of the 12-bit binary value from the ADC as a fixed-point
fractional value, with the binary point between the fourth
and fifth bits: 0000.vvvvvvvvvvvv. This fraction represents
some value between 0 and 10 V (e.g., 0x0FFF or 0000.11111111111
represents 9.9976 V). Each time you multiply this value
by 10, the integer part of the result is another digit
in the output representation that he needs.
The
pseudo-code is as follows, assuming v
is a 16-bit register:
This
code executes quickly when properly implemented in assembly.
Multiplying by 10 involves six 8-bit shifts and two 8-bit
additions. Shifting by 12 is really just shifting the
high byte by four places. Similarly, masking with 0x0FFF
only involves one 8-bit AND of the most significant byte.
The variables unit,
tenth, and hundredth only need to be 4 bits
wide (unsigned).
There
are 18 8-bit shifts and six 8-bit additions for the multiplication
steps, plus 12 more 8-bit shifts to isolate the digits,
and two 8-bit ANDs to do the masking. A total of 38 8-bit
operations are needed. For an MCU that executes most 8-bit
operations in one cycle, this code should take no more
than 80 cycles. At a modest 5-MHz clock speed, the conversion
takes at most 16 µs, much less than the transmission time
for even 1 byte at 230,400 bps. It is time that Frugalson
ask for a raise!
One
other point that Frugalson may want to consider is the
fact that the decimal conversion has been truncated to
three digits rather than rounded. If he adds 0.005 V (0000.000000000010
in binary) to v before starting the conversion, the result
will be rounded correctly. However, full-scale inputs
will now generate an output value of 10.00, which will
require a fourth digit to be generated. It may be better
to leave it well enough alone.
Contributor:
David Tweed