Issue
158 September 2003
The
XY-Plotter
Drive
High-Resolution LCDs For Less
Mad
Dash for Flash Cash Contest Winner
OPTIMIZATION
TIPS
Optimizing
the firmware’s cycle count requires a huge effort. For
instance, one of its basic tasks is to send N pulses
to the LCD’s nibble clock input. A loop already needs
five cycles to do this, but remember that you have time
for less than seven instructions per nibble, and the
firmware has more to do than simply send clock pulses!
So, you’ll need additional optimization techniques like
code expansion and the calculated goto procedure (see
Listing 1).
| Listing
1—I used this coding technique to meet the strict
timing requirements of the project. The routine
sends a configurable number of pulses to the LCD
clock input with less than three PIC instructions
per pulse on average! Try to do it with a classic
loop. |
I
used the calculated goto technique extensively. Basically,
I was manually unrolling the code like an optimized
compiler does (or tries to do). For instance, I used
a long calculated goto table to select the specific
line-generation algorithm for each column in the display
(e.g., graduations, plain line, ordinary curve column,
etc.). The result is a strange assembly listing to read
but an interesting one to write!
Another
tip is to copy, at startup, the combined pixel table
from flash memory and paste it in RAM. An indirect access
to RAM is quicker than a table read from flash memory.
MEMORY
REQUIREMENTS
The
aforementioned firmware optimizations are memory hungry.
Fortunately, with 32 KB of flash memory it’s not an
issue. My firmware currently uses only 10 KB.
I
used the PIC18F252’s entire RAM. Three pages at 256
bytes each were used to store the respective minimum,
maximum, and sampled y value for each x value. One page
was devoted to the storage of the ASCII text, although
only 90 bytes were actually needed. One last 256-byte
page was used to store the bitmap patterns. That left
256 bytes for general-purpose variables. All in all,
that’s 1536 bytes.
DEVELOPMENT
PROCESS
The
project was developed with the MPLAB environment and
simulator. I also used Microchip’s boot loader firmware
(AN851) to burn flash memory, which is an interesting
feature even if firmware improvements are welcome. In
particular, no on-chip debug facility is currently provided
(e.g., breakpoints), but I’m sure they’ll be in the
next version.
Also
note that the AN851 boot loader doesn’t provide an automatic
reentry facility. As soon as an application firmware
is downloaded and activated, there’s no way to reactivate
the bootloader without specific user-supplied application
code (like simultaneously pressing the three keys at
power-up). This is well documented in the literature
but more secure solutions exist (e.g., timeout).
I
wasn’t lucky enough to have a full-featured ICE for
the processor, so I wanted to avoid hundreds of burn
and test cycles. I started by developing the critical
code (e.g., the pixel generation algorithm) on a PC
in C—just to validate the algorithm itself. Then, I
developed the full firmware with MPLAB, keeping a structured
approach to facilitate the validation.
Later,
I implemented a bottom-up approach. I simulated 100%
of the software with small stub routines in an effort
to execute each routine individually. Note that I was
still using MPLAB and didn’t have a target system at
that point. I even kept a source listing and ticked
all of the assembly lines to be sure to go across each
of them.
I
used the MPLAB stopwatch to verify the timings. When
everything seemed fine under the simulation framework,
I went to the target processor. That approach proved
successful. My first burned firmware was not free from
bugs, but I got a working display with the first burned
file!
The
RS-232 helped a great deal during the final debugging
steps. In fact, rather than having to develop a specific
protocol for each project, I used an easy and powerful
method.
First,
the UART firmware dumps the RAM’s content on the RS-232
port per the host’s request. Following this, software
on the PC side is able to grab interesting information
based on the RAM content (e.g., rebuilding something
like a screen hard copy). But the most interesting point
is that the same feature is invaluable during the debugging
steps!