Bank 0. We do that by clearing the RP0 bit in the STATUS
register. Remember we set it with the BSF command to
move to Bank 1, so now we need to BCF or bit clear the
same bit to put control back at Bank 0.
bcf STATUS,RP0 ; back to Register Page 0
Finally, we turn on the LED by placing a high level on the
PortC pin 0. We use an already familiar command BSF to set
the PortC pin 0 bit in the PORTC data register. By setting
that bit to a 1 or high value, the LED will light. The PORTC
register is in Bank 0 of program memory. You can see where
these different special function registers are by looking at the
memory map in the datasheet for the part you are using.
bsf PORTC,0 ; turn on LED C0 (DS1)
Finally, we issue a GOTO command again; but instead of
going to a label we use the assembler’s special character “$,”
which represents the value of the current program counter
location. The program counter is pointing to the GOTO
command when it gets here, so all it is saying is goto the same
spot where you currently are. I could have put a label above
the GOTO command and then put that label in the GOTO
command line; but instead I chose to show that sometimes
little symbols that make assembly coding confusing are really
just shortcuts, and not actual assembly commands.
goto ; wait here
The last step is to issue the assembly command to end
the program. This is a safe way to mark the end of the
program, and should be part of all assembly language code.
end
As you can see, we only used four assembly language
commands — BSF, BCF, GOTO, and END — to light an LED.
This wasn’t that difficult to understand, and should begin to
make you feel a little more comfortable with understanding
assembly code.
BLINK AN LED
Lighting an LED is a great start, but that can be accomplished with a battery and resistor. Getting an LED to flash
takes a little more effort and is a better demonstration of a
MCU’s capabilities. So, I’ll expand on the previous example
to show how to blink the LED and also add a delay, so the
LED blinks slow enough for the human eye to see it. The
program in Listing 2 shows the “Blink an LED” routine. I’ll
step through that program to show how delays work.
The top of the program is the same as Listing 1, where
we indicate the MCU being used and the configuration fuses.
#include <p16F690.inc>
__config (_INTRC_OSC_NOCLKOUT & _WDT_OFF &
_MCLRE_OFF)
For this program, we need to establish a few variables in
RAM to store the delay time count value. Using the assembler directive “cblock,” we establish a block of RAM starting
at address 20 hex. If you look at the PIC16F690 datasheet’s
16 March 2008
memory map page, you’ll see that the general-purpose
register locations start at memory location 20h. It has a total
of 96 bytes of space in the memory block, so we can fit 96
bytes of variables in this bank of memory. In this example we
only need two, which I call Delay1 and Delay2. Other banks
have more space for variables, if we need more than 96. The
“endc” directive indicates the end of the cblock.
A cblock is defined as a way to designate a block of
constants (constant block) with a specific starting address,
and then each label increments one byte off that starting
location. In the assembler, though, this isn’t really a constant
value, it’s a constant location. The assembler manual even
states, “when creating non-relocatable (absolute) code,
cblock is often used to define variable address location
names.” So, don’t let the C in cblock confuse you into
thinking it only works for constant values. You have more
freedom in assembly programming, since you are controlling memory directly — not through some compiler’s rules.
cblock 0x20
Delay1 ; Create two byte size variables for the
Delay2 ; delay loop
endc
The now familiar org 0 and org 5 statements are
implemented the same way as before, by marking the
program memory locations. Also, the setting of the TRISC
register is identical to the previous example.
org 0
goto Start
org 5
Start
bsf
bcf
bcf
STATUS,RP0
TRISC,0
STATUS,RP0
; select Register Page 1
; make IO Pin C0 an output
; back to Register Page 0
The main loop is where the program begins to change.
The first command issued is the BSF command, to light the
LED on PortC pin 0 with a high value.
MainLoop
bsf
PORTC,0
; turn on LED C0
Now that the LED is on, we need to keep it on for a period long enough for the human eye to see it. If we don’t delay,
the program will flash the LED too fast; as each assembly
instruction only takes one clock cycle. If we are running with
a 4 MHz internal oscillator, which then gets divided by four
internally for the system instruction clock, the instruction
clock is functioning each assembly command at one million
instructions per second, or one microsecond each. Therefore,
we need to add a significant number of loops to kill time, and
this is where those variables Delay1 and Delay2 come in.
As you can see in the block of code that follows, we first
decrement the value of Delay1 with a “DECFSZ” command,
which stands for Decrement File register by one and Skip
the next instruction if zero. Delay1 will decrement from 0 to
FF hex, so it’s not zero when tested, and the “GOTO
OndelayLoop” command line is implemented by putting the
program back to the DECFSZ command. This continues for