| |
 |
The 8051
|
|
|
|
| |
|
| |
|
| |
Conditional Jumps and Time
Delays |
| |
|
| |
We have already looked at the
conditional jump JC rel, the operation for which is detailed
below: |
| |
JC rel
jump if carry set
(PC) <- (PC) + 2
IF (C) = 1 THEN (PC)
<- (PC) + rel
|
| |
|
| |
If the carry is equal to 1, branch to
the indicated address. |
| |
|
| |
|
| |
The JNC rel instruction is
the opposite to this: |
| |
JNC rel
jump if carry not set
(PC) <- (PC) + 2
IF (C) = 0 THEN (PC) <- (PC) + rel
|
| |
|
| |
If the carry is equal to 0, branch to
the indicated address. |
| |
|
| |
|
| |
There are two conditional jump
instructions that are used for testing the contents of the accumulator. |
| |
JZ rel
JNZ rel
|
| |
|
| |
JZ rel |
| |
Jump if the accumulator is equal to 0 |
| |
JZ rel
jump if accumulator zero
(PC) <- (PC) + 2
IF (A) = 0 THEN (PC) <- (PC) + rel
|
| |
|
| |
This instruction tests the contents
of the accumulator. If the contents are equal to 0 it causes a jump to
the indicated address, otherwise execution continues with the next
instruction. |
| |
|
| |
|
| |
JNZ rel |
| |
Jump if the accumulator is not equal
to 0 |
| |
JZ rel
jump if accumulator zero
(PC) <- (PC) + 2
IF (A) <> 0 THEN (PC) <- (PC) + rel
|
| |
|
| |
This instruction also tests the
contents of the accumulator. However, if the contents are NOT equal to
0 it causes a jump to the indicated address, otherwise execution
continues with the next instruction. |
| |
|
| |
|
| |
Time Delays |
| |
There are times when it is necessary
to wait before executing the next part of a program. For this
discussion we will take the example of flashing an LED. |
| |
|
| |
 |
| |
|
| |
The above flowchart details program
for flashing an LED. It works by turning on the LED, turning it off and
then looping back to the start again. This program is in a never-ending
loop. |
| |
However, while the above program may
flash an LED, because of the speed at which microcontrollers operate,
the frequency would be too fast for the human eye to see. |
| |
|
| |
 |
| |
|
| |
Above is a modification of the LED
flashing program. It turns on the LED, waits half a second, turns it
off, waits another half second and then loops back to the start again. |
| |
|
| |
One method of forcing the
microcontroller to wait is the time delay. The flow chart for a
single-loop time delay is shown below. |
| |
|
| |
 |
| |
|
| |
A number is loaded into the
accumulator. It is then decremented. Following this, a check on the
contents of the accumulator is made. If the contents of the accumulator
is not equal to zero a jump back to the decrement instruction is made. |
| |
|
| |
If 0AH is loaded into the
accumulator, then the loop is executed ten times. To increase the
length of the time delay we load a larger number into the accumulator.
Therefore, the longest time delay (using this single loop time delay)
is achieved by loading the accumulator with FFH - the loop would be
executed 255 times. |
| |
|
| |
|
| |
The DJNZ Instruction |
| |
The DJNZ instruction is specifically
designed for this kind of program. It stands for decrement jump not
zero and the operation is detailed below. |
| |
DJNZ byte, rel
decrement and jump if not zero
(PC) <- (PC) +
instructionSize
(byte) <- (byte) - 1
IF (byte) <> 0 THEN (PC) <- (PC) + rel
|
| |
|
| |
There are two versions - a 2-byte
version and a 3-byte version. |
| |
|
| |
The 2-byte version works on the
register set R0 to R7. |
| |
DJNZ Rn, rel
decrement and jump if not zero
(PC) <- (PC) + 2
(Rn) <- (Rn) - 1
IF (Rn) <> 0 THEN (PC) <- (PC) + rel
|
| |
|
| |
Rn can be any of the eight
registers R0 to R7. The encoding for the instruction is |
| |
11011rrr rel
|
| |
where rrr is replaced by the
register number. |
| |
For example, if the instruction DJNZ
R5, rel was used, the opcode would be 11011101. |
| |
The second byte of the instruction,
the operand, is the relative offset. Like all jumps, you the programmer
don't need to work out the offset; you use a label and the assembler
works out the offset for you. |
| |
|
| |
The 3-byte version allows you to work
with any RAM location. |
| |
DJNZ direct, rel
decrement and jump if not zero
(PC) <- (PC) + 3
(direct) <- (direct) - 1
IF (direct) <> 0 THEN (PC) <- (PC) + rel
|
| |
|
| |
direct can be any 8-bit
address. The encoding for the instruction is |
| |
11010101 add rel
|
| |
This code states - decrement a
location specified by the second byte (add), test to see if the
result is non-zero, if so jump to location specified by the third byte (rel). |
| |
An example of this instruction: DJNZ
30H, rel - decrement the contents of RAM location 30H and, if the
result is non-zero, jump to location specified by rel. |
| |
|
| |
|
| |
This instruction is prefect for the
time delay program, as detailed below. |
| |
|
| |
MOV R0, 0FFH
DJNZ R0, $
|
| |
|
| |
The $ Operator |
| |
Most assemblers use the $ character
as a pointer to the current program location. Therefore, the
instruction DJNZ R0, $ means - decrement the contents of R0 and if the
result is non-zero jump back to this line. In this way, the contents of
R0 are continuously decremented until they reach zero. |
| |
|
| |
|
| |
Longer Delays |
| |
As stated above, the longest
single-loop delay is achieved by initialising the decrement register
(ie; the register that will be decremented) to FFH. To achieve longer
delays we can use multiple-loop time delays, as detailed in the flow
chart below. |
| |
|
| |
 |
| |
|
| |
The inner loop (nested loop) takes
255 iterations before R1 reaches 0. When this happens, the loop is
exited and R0 is decremented. If R0 is not equal to zero the program
jumps back to the line that loads R1 with FFH again. Another 255
iterations of the nested loop must occur before R0 is decremented
again. Therefore, for every one iteration of the outer loop there are
255 iterations of the inner loop. |
| |
We control the length of the time
delay by changing the value loaded into R0. If R0 is initialised to
03H, then the overall number of iterations is 3 * 255 = 765. The
maximum number of iteration for a two-loop time delay is 255 * 255 =
65025. |
| |
|
| |
|
| |
Calling a Subroutine from within a
Subroutine |
| |
To write a three-loop delay we could
create two nested loops inside a third outer loop as detailed in the
flowchart below. |
| |
|
| |
 |
| |
|
| |
Take note that the inner two loops
are identical to the maximum two-loop delay shown above. Therefore, if
we write the three-loop delay in this manner we must rewrite the
two-loop delay and test it. This is not an efficient programming
practice. It would be better if we reused our two-loop delay as
detailed in the flowchart below. |
| |
|
| |
 |
| |
|
| |
The inner two-loop delay is replaced
by a call to our original maximum two-loop delay. In this manner we are
reusing the code that was written and tested earlier. |
| |
|
| |
The code for the above three-loop
delay is: |
| |
|
| |
| threeLoopDelay:
|
MOV R2, #0A0H
|
|
loop: CALL
twoLoopDelay
|
DJNZ R2, loop
RET
|
|
| |
|
| |
and the code for the two-loop delay
is: |
| |
|
| |
| twoLoopDelay:
|
MOV R0,
#0FFH
|
| loadR1: |
MOV R1,
#0FFH
DJNZ R1, $
DJNZ R0, loadR1
RET
|
|
| |
|
| |
Calling the three-loop delay will
therefore result in 10 * 255 * 255 iterations. |
| |
|
| |
We can now use our two delays for
flashing the LED at something other than a 50% duty cycle. |
| |
|
| |
| start: |
SETB P1.0
CALL twoLoopDelay
CLR P1.0
CALL threeLoopDelay
JMP start
|
|
| |
|
| |
The above program sets the least
significant bit of port 1 and then calls the two-loop delay, after
which it clears the least significant bit of port 1 and calls the
three-loop delay. The three-loop delay itself calls the two-loop delay
ten times. Therefore, the LED (on P1.0) will be off for approximately
ten times longer than it is on. |
| |
|
| |
|
| |
|
| |
|
| |
|
| |
Copyright (c) 2005-2006 NyCelt LLC
|