October
2005, Issue 183
The
Silicon Wallet
CY8C27443-Based
Data Manager
STYLE
VALUE
The
application code, which is written in C, takes 32% of
the CY8C27443’s flash memory, leaving more than two-thirds
of the nonvolatile memory for storing user data. As
I developed the code, I placed each logic block in a
separate module, banning the use of global variables
whenever possible. This is a discipline I learned the
hard way, working on large projects with other programmers,
but it’s also useful in small designs because it enables
you to make changes easily. The price to pay for flexibility
is that the code is dispersed in many files. But this
practice pays for itself when it’s applied correctly.
Touching one module doesn’t affect the others. Therefore,
you can invent a different keyboard layout, try an error
correction code other than the Hamming, or experiment
with a new offset-level control strategy, concentrating
the changes on one module at a time.
The
receive()
function demonstrates how modularization is implemented.
The function in Listing 1
attempts to receive a code word from the light detector;
it keeps trying until a new code word is detected or
the quadrature encoder button is released. It does its
job one bit at a time, calling the receive_bit()
function, a lower-level routine that can return 0, 1,
or BIT_INVALID.
The last value indicates an error or a timeout condition,
in which case the receiver discards all the previously
received bits and restarts.
The
receive()
function reduces the problem of receiving a code word
to the problem of receiving a single bit. This new problem
is handled by the receive_bit()
function (see Listing 2). The protocol
encodes bits as the sequence starting with a logic 1
(for 45 to 90 ms) followed by a logic 0 (45 ms). Based
on the duration of the previous sequence (with due tolerances),
the function determines if the previously received bit
is a 1 (shorter) or a 0 (longer). Timeouts are provided
during reception to abort the operation if no valid
data arrives after a fixed amount of time.
At
this stage, the problem is reduced to detecting input
logic levels with a couple of functions: is_one()
and is_zero().
This is done in another module that reads the A/D converter,
adds hysteresis, fills inter-frame gaps in CRT monitors,
and so on. Refer to the light_sensor.c file posted on
the Circuit Cellar FTP site.
It’s
important to note that as long as you provide the same
couple of functions, the receiver code will continue
to work untouched even if the logic levels are determined
in a different way. The mechanism is flexible. For example,
you may want to experiment with a two-tone sound as
a data source in place of light, or you can get the
data as a logic level from an external circuit connected
directly to an I/O pin. In both cases, all you have
to do is replace is_one()
and is_zero()
with an alterative implementation.