Making an LED blink is kind of the hardware version of "Hello
World." It's about as simple as you can get - it requires no more
than 2 components besides the MCU - an LED, and if you want your LED
to last very long, a resistor. And of course, batteries... and some
wire... and a little breadboard to connect it all up.
This is a fairly long and involved tutorial - not for the faint of
heart! SO, turn on some appropriate music,
grab a can of Pepsi, and 'ere we go!
1. I'm using the 16F630 for this tutorial. If you're
using a different PIC, and you're completely new to PICs, you might
have trouble! Conceptually, this will still be good information for
you, and most of the actual assembly won't change for different PIC's,
but certain configuration values may be different, and you may have
to use a different port (i.e. many PIC's don't have a PORTC).
Instead of building this program up chunk by chunk, I'm just going
to throw the whole thing at you, and explain it line by line. Here it
is:
;Anthony Rogers
;Oct 5th, 05
;BlnkDem - flash an LED
list p=16F630 ; list directive to define processor
#include <p16F630.inc> ; processor specific variable definitions
__CONFIG _CP_OFF & _CPD_OFF & _BODEN_OFF & _MCLRE_OFF & _WDT_OF
F & _PWRTE_ON & _INTRC_OSC_NOCLKOUT
VARIABLES UDATA_SHR
delayA RES 1
delayB RES 1
;**********************************************************************
RESET_VECTOR CODE 0x000 ; processor reset vector
goto main ; go to beginning of program
PROGRAM CODE
main
call 0x3FF ; retrieve factory calibration value
bsf STATUS,RP0 ; set file register bank to 1
movwf OSCCAL ; update register with factory cal value
movlw 0x00
movwf TRISC
bcf STATUS,RP0 ; set file register bank to 0
loop
clrf PORTC
call delay
movlw 0xFF
movwf PORTC
call delay
goto loop
;-------------------------------------------------------------------
;Delay Routine
delay
movlw 0xFF
movwf delayA
loopA
movlw 0xFF
movwf delayB
loopB
decfsz delayB, f
goto loopB
decfsz delayA, f
goto loopA
return
;-------------------------------------------------------------------
END ; directive 'end of program'
|
;Anthony Rogers
;Oct 5th, 05
;BlnkDem - flash an LED
Semicolons (obviously) denote comments....
list p=16F630
This line tells the assembler what PIC you are compiling for. Honestly,
I don't know why this is necessary, as you ALSO have to tell which PIC
you're using under menu item Configure | Select Device... .
But, it's in the Microchip provided templates, so I guess I'd better
use it. : )
#include <p16F630.inc>
This file essentially contains a bunch of EQU directives, associating
special register locations with names, as they appear in the PIC data
sheets. For example, without this file, if you wanted to access PORTC
on this PIC, you'd have to use it's actual address: 0x0007. There are
at least 3 reasons you don't want to have to do this. 1) There are MANY
special registers, and
obviously no one is going to want to be memorizing
or constantly checking register location charts! 2) Using the names,
your code is more portable. A different PIC might have PORTC mapped
to a different address. If you ever use your code on a different processor,
you wouldn't have to go through and update all the lines that use the
PORTC address if you utilized the include file - it would automatically
update the address for you. 3) It makes your code more readable.
It's very beneficial to actually open the include file and take a look.
It will be under C:\Program Files\Microchip\MPASM Suite\P16F630.INC
(or similiar, per your installation particulars). There are a few other
goodies in there besides EQU's (i.e. Configuration Bits... read on!)
__CONFIG _CP_OFF
& _CPD_OFF & _BODEN_OFF & _MCLRE_OFF & _WDT_OFF & _PWRTE_ON & _INTRC_OSC_NOCLKOUT
This scary looking line sets certain "fuses"
in the PIC pertaining to configurations that do not change during program
execution. These "fuses" are just bits in a special register
- CONFIG - that cannot be accessed accept during programming. In other
words, normal program execution cannot access this register. See the
data sheet for what each bit means.
VARIABLES UDATA_SHR
This defines the start of a section in the program
used soley for defining variables. There is nothing particular about
the "VARIABLES"
term - it could be any valid label. In fact, if don't even need
a label, and the assembler will automatically label the section
".UDATA_SHR". But, a good label improves program readability.
UDATA_SHR stands for "Shared Uninitialized Data." You can
specifiy a memory start location for the section, but might as well
let the assembler decide where to put it! One less detail to worry about.
delayA RES 1
delayB RES 1
Here I've defined the only two variables we'll need,
to be used in the delay loop. "delayA" is the variable name,
RES stands for "reserve", and "1" is how many bytes
to reserve.
RESET_VECTOR CODE 0x000
This, like the UDATA_SHR section, defines the start of a section in
the program. The CODE directive tells the assembler that this section
will contain actual assembly, as opposed to say - variable
definitions. Also different from the UDATA section, for this particular
code section we must define the start address. The reason is - the start
of this section must be at the memory location the processor defaults
to on startup.
goto main
Ahhh! Finally, some actual
assembly! This line simply causes program execution to jump straight
into the main program routine. Because the previous line (RESET_VECTOR
CODE 0x00) forces this section of code to be placed at memory location
0x00, and because the program counter starts at 0x00, this
will be the first line executed when you power up your PIC.
PROGRAM CODE
Yet another section of code - this time specifying the main body of
the program. You'll notice there's no address specified for this section.
Not explicity giving it an address is basically giving the assembler
permission to put it wherever it fits.
main
This is simply a label. It's purpose is soley to provide
a location to jump to. During the assembly process, it will be converted
to a physical address, and any instructions that "goto" or
"call" it will simply set the program counter to that
physical address. When you power up the PIC, the program counter
will jump from "goto main" to this location.
call 0x3FF ; retrieve
factory calibration value
bsf STATUS,RP0 ; set file register bank to 1
movwf OSCCAL ; update register with factory cal value
These lines are particular to this PIC, and any others
that have a factory oscillator calibration value. If you look back at
our configuration bits, you'll see that the _INTRC_OSC_NOCLKOUT
is set. This means we're going to be utilizing the built-in 4MHz clock
- thus we need to use the calibration, as instructed in the original
16F630 template. Register Banks are covered
in another tutorial.
movlw 0x00
movwf TRISC
The first line here moves the literal value 0x00 ("0xXX"
specifies a hex number) into the working register, and the second line
moves that value into the speical register TRISC. The TRIS registers
control the input/output status of the corresponding ports
(i.e. TRISA controls PORTA). We'll be using PORTC, thus "TRISC."
Putting "0" into the TRIS registers specifies that bit will
be used for output. "1" specifies input. An easy way to remember
which specifies which? 1nput and 0utput. : )
Why does it take two commands to simply put a literal value into a
register? There is no operation in the PIC instruction set to put a
literal directly into an arbitrary register. You must use the working
register as the "middle-man."
bcf STATUS,RP0
Another line for the Banking
Tutorial.
loop
Another label, like main.
These labels, by the way, can be named just about anything. You could
use "asdf" if you wanted. Of course, if you want to be able
to read your code after you haven't looked at it for a week, I'd recommend
using well thought out labels. : ) We will use THIS label to continuously
loop through our program - to turn the LED on and off.
clrf PORTC
Now for some action! clrf is the mnemonic (instruction) for "clear
file" - a "file" being a program accessable "register."
We're simply turning all the bits of PORTC to 0. This forces the PORTC
pins, which we've configured as outputs, to go low - turning the LED's
off.
call delay
This will cause the program
counter to jump down to a delay routine. At 4MHz clock speed, if we
didn't have the delay, the LED would be blinking pretty darn fast! Far
too fast to see even a flicker, of course. For now, think of the delay
routine as a black-box that simply pauses the execution for half a second
or so. I'll cover the actual routine in another tutorial.
movlw 0xFF
movwf PORTC
Here we are again moving
a literal into a file register, using the working register as the middle
man. Again - we set all the bits of PORTC to output earlier in the program,
so when we put binary 11111111 (hex FF) into it, every output goes high
(there are actually only 6 I/O's on PORTC on this PIC - the 2 most significant
(left most) bits are ignored). At this point, our LED will light up.
call delay
We want our LED to stay on
long enough to see! Thus another pause.
goto loop
Jump back up to label "loop"...
and clear PORTC again, turning the output's off.
That's it for the software! The delay routine is another substantial
chunck to go over, and I'm sure you need a break at this point (I sure
do!), so I'll cover in the next tutorial.
For now, just copy it, assume it works, and let's get to the hardware!
How's this for simple? Heck - I won't even post a schematic! After
you've uploaded your compiled program to your PIC, simply connect +3
volts to Vdd (pin 1), the LED (with a current limiting resistor - I'm
using 680 ohms) to any of the PORTC pins (I used RC0 - pin 10), and
0 volts to Vss (pin 14) - and enjoy your blinker!

Note: I didn't have a dual AA battery holder, so I used a quad with
a quick tin-foil modification - but just to
clarify, there are actually 2 AA batteries on my circuit (3 volts).
That's it for this one - please email me
if anything is not clear, if you find any mistakes, or it simply won't
work.
Good luck!
top