|
Conversion and Optimization Techniques
by Stephen Bowling
Start ı A
Code Example ı Optimizing the Code Conversion
ı Sources and PDF
A CODE EXAMPLE
One of the best ways to discuss code
conversion and optimization is to use some real source code. The example
program that Iıll use was written to operate on a PIC16C74B device
and implements a simple digital thermometer. Itıs not a complex program,
but it demonstrates many of the code conversion issues that Iıll discuss.
Figure 1 shows a block diagram of the application. A resistor/thermistor
combination is connected to channel 0 of the A/D converter. Two seven-segment
LED displays are multiplexed to PORTD of the MCU. Timer0 and Timer1
are used to time the display updates and A/D conversions, respectively,
that are handled in an interrupt service routine. The A/D result is
used as an offset for a temperature lookup table. Each byte in the
lookup table contains a packed BCD temperature result. The resulting
BCD values are then used to obtain the proper LED segment data in
a second lookup table.
|
|
| Figure 1ıA digital thermometer
makes a great code-conversion example. This is the simplified
connection diagram of the thermometer. |
The original code written for the PIC16C74B
is given in Listing 1, which
is available for downloading. Let me show you how to convert the code
so that it will be compatible with a PIC18C452 device. The PIC18C452
is pin-to-pin compatible with the PIC16C74B and has a set of peripherals
that are compatible with the PIC16C74B with enhancements.
Letıs walk through the code and perform
the minimal conversion. The first two lines in the code specify the
processor type and the device definition file. The first thing you
need to do is change these for the appropriate processor:
LIST P = 18C452
include <p18c452.inc>
The device definition file specifies
symbolic names for all of the registers and control bits associated
with the device. Itıs always good practice to use register and bit
definitions supplied by the vendor to avoid any naming convention
issues.
Definitions for numerical constants and
data storage variables are also included at the beginning of the source
code. For example, the start address for the data lookup tables has
been defined to be 800h. Constants like this one should be defined
symbolically at the beginning of the code, because it enhances readability
and allows them to be changed without having to sift through many
lines of source code.
Next, youıll need to take care of any
register name changes. To begin with, the PIC16Cxxx architecture
has one FSR, which is eight bits wide. Because there are three 12-bit
FSRs in the PIC18Cxxx architecture, the register names are
different. For example, assuming youıll use FSR0, references to the
FSR and INDF registers in the PIC16C74B code can be redefined as follows:
#define FSR FSR0L
#define INDF INDF0
The operation of Timer0 is enhanced
in the PIC18Cxxx architecture so that it can be used as a 16-
or 8-bit timer. Consequently, there are two registers (TMR0H and TMR0L)
for Timer0. You can take care of this change with the following #define
statement:
#define TMR0 TMR0L
The PIC18C452 A/D converter provides
a 10-bit result in two 8-bit registers, ADRESH and ADRESL. The PIC16C74B
has an 8-bit A/D converter and a single result register, so youıll
need the following #define:
#define ADRES ADRESH
Finally, it is useful to define constants
for the IRP, RP0, and RP1 bank selection bits. These bits are not
implemented in the ı18Cxxx architecture, nor are they defined
in the device definition file.
#define RP0 5
#define RP1 6
#define IRP 7
For minimal conversion, leave all
data memory banking statements in the converted code. Any instructions
in the code that operate on the IRP, RP0, or RP1 bits have no effect
because the bit locations are not implemented.
The interrupt vector origin needs
to be changed at this point. Because byte addressing is used in the
PIC18Cxxx architecture, the interrupt vector location is changed
from 0004h to 000008h. You should also ensure that there is enough
space for the program instructions that are located between the reset
vector and interrupt vector. These instructions may need to be modified
because CALL and GOTO instructions are one-word instructions
in the PIC16Cxxx architecture but are implemented as two-word
instructions in the PIC18Cxxx architecture.
Next, you need to verify that there
are no bit location changes in the SFRs that control operation of
the peripherals. For example, the values loaded into ADCON0 and ADCON1
should be verified using the device datasheet to ensure that the A/D
converter is configured properly. In most cases, the bit locations
will be the same, but there may be differences between devices. You
have to be careful here because the assembler will not catch these
types of errors in the program. The only indication of a problem you
will receive is when the peripheral controlled by the SFR does not
operate as expected.
One special case is the SFR that controls
Timer0. In the PIC16Cxxx architecture, Timer0 is configured
by the OPTION_REG register. This register is not present in the PIC18Cxxx
architecture, so you need to change the name to T0CON. T0CON now controls
whether the timer is in 16- or 8-bit mode, therefore, you also need
to ensure that the correct set-up value is loaded into the register.
In general, it is best not to use
hard-coded bit and register values. For example, the two instructions
below are equivalent, but the second is preferred:
bsf ADCON0, 0 ; Donıt do
this !
bsf ADCON0, ADON ;
Preferred !
The code should be checked for any
instructions that may be incompatible with the new architecture. When
converting from the PIC16Cxxx to the PIC18Cxxx architecture,
there are three cases that should be checked. The first instruction
is CLRW, which clears the accumulator working register. This
instruction is needed in the PIC16Cxxx device family because
the working register is not physically addressable. However, the following
#define statement makes a simple substitution:
#define CLRW CLRF WREG
The next two instructions that need
modification are RLF and RRF, which rotate a register
left or right, respectively, through the carry flag. The PIC18Cxxx
instructions operate the same but are named differently to indicate
usage of the carry flag. Again, two #define statements will
make the required substitutions:
#define RRF RRCF
#define RLF RLCF
The final modifications to the code
account for differences in the operation of the program counter. Remember,
the program counter increments in steps of two in order to maintain
word alignment with program instructions. Because of this, any references
to the program counter value must be multiplied by two. In the original
PIC16C74B source code, the following instructions poll the GO bit
in ADCON0 to determine when an A/D conversion has completed:
btfsc ADCON0,GO ; Conversion
complete?
goto $ -
1 ; No, keep checking.
The $ symbol represents the
present program counter value. The GOTO instruction decrements
the program counter value so that the previous instruction is executed.
To operate correctly on the PIC18Cxxx
architecture, the code should be modified to the following:
Btfsc ADCON0,GO ; Conversion
complete?
goto $ - 2 ;
No, keep checking.
It is always good practice to use
symbolic address labels. For example, the following code will operate
the same on both device families without modification:
Test
btfsc ADCON0,GO ;
Conversion complete?
goto Test ;
No, keep checking.
The lookup tables will require modification.
To implement a lookup table, an offset value is placed in WREG and
a CALL is made to the table. The table consists of a series
of RETLW instructions, which return from the call with a specific
value in WREG. The offset value in WREG is added to the program counter
and causes a jump to the desired instruction. After returning from
the call to the lookup table, the WREG value is stored in the desired
location. An example lookup table is
given below:
| Movlw |
Offset |
; Table offset value |
| Call |
Table |
; Call the table |
| Movwf |
Result |
; Get table result |
| ·
|
|
|
| ·
|
|
; other code |
| ·
|
|
|
| addwf |
PCL,F |
; Add offset to PC |
| retlw |
Constant2 |
; Table entry #2 |
| retlw |
Constant3 |
; Table entry #3 |
| retlw |
Constant4 |
; Table entry #4 |
| retlw |
Constant5 |
; Table entry #5 |
For the lookup table to function properly
on the PIC18Cxxx architecture, the offset value must be multiplied
by two. This will cause the program counter to be advanced by the
correct number of instructions. The lookup table has been modified
to operate on the PIC18Cxxx architecture:
| Movlw |
Offset
|
; Table offset value
|
| Call |
Table |
; Call the table |
| Movwf |
Result |
; Get table result |
| |
|
|
| |
|
; other code |
| |
|
|
| rlncf |
WREG
|
; MULTIPLY OFFSET BY 2 |
| addwf |
PCL,F |
; Add offset to PC |
| retlw |
Constant1 |
; Table entry #1 |
| retlw |
Constant2 |
; Table entry #2 |
| retlw |
Constant3 |
; Table entry #3 |
| retlw |
Constant4 |
; Table entry #4 |
| retlw |
Constant5 |
; Table entry #5 |
The minimal modifications to your PIC16C74B
source code have now been completed. The code should now be fully
functional on the PIC18C452 device. The complete minimal code conversion
is provided in Listing 2.
PREVIOUS
NEXT
Circuit Cellar provides up-to-date information for engineers. Visit
www.circuitcellar.com for
more information and additional articles.
For subscription information, call (860) 875-2199, subscribe@circuitcellar.com
or subscribe online.
ıCircuit Cellar, the Magazine for Computer Applications. Posted with
permission. |