Lesson 4 Lab Activities

advertisement
ENGR 172 Intermediate Microcontrollers
Lesson 4
Name: _______________________
8 extra credit points of 110 total points
Lesson 4 is a brief introduction to assembly language programming. Assembly language takes us several
steps closer to understanding and controlling exactly how the microcontroller operates (it’s a great language
if you’re a control freak). Be warned: Assembly language is often described as arcane and cryptic, and I
suspect you’ll agree with that assessment.
Activity 1 (3 pt. I. I. ________)
In the first activity we will learn how to turn on an LED using the PIC assembly language, then how to
flash the LED.
1. Start by connecting an LED and a 470W resistor to PortB, pin 0 (pin 6 of the 16F84A), as shown
in Figure 4.1.
Figure 4.1. LED connection for assembly language exercises
2.
Create a folder for your assembly code programming project, giving it a distinctive name like
TurnOnLED.
3.
Open the MPLab IDE (IDE stands for Integrated Development Environment).
a. An IDE is a suite of programs that usually include an editor, a linker, an assembler.
These programs take your program from what you originally type in, called the source
code, to the final file you put into the microcontroller, called the hex file.
4.
Start a new project by selecting Project/Project Wizard …
a. When it asks you to select a device, choose PIC16F84A.
b. The active toolsuite should be left as MicroChip MPASM toolsuite
c. When it asks for a project name, use the project name and directory you set up in step 2
above.
d. Click through until you’re done. If you look in your directory now, you’ll see you have
several files listed.
5.
Create the assembly language source code.
a. Select File/New
b. This will bring up an untitled window. Type in your source code, as follows. You may
skip typing in the comments. Those are for your understanding, and to help the
discussion that follows.
;***********EQUATES SECTION***********
status equ
3
;Line 2. Status register is data memory location 3
portb equ
6
;Ln 3. Port B (I/O) is data memory location 6
trisb
equ
h'86'
;Ln 4. Port B's tri-state enable register is
;data memory location 86 in hex (=134 in base 10)
;***********CONFIGURATION************
list
p=16f84
;Ln 8. Tells the compiler you have a 16F84 micro
__config h'3ff1' ;Ln 9. Too complicated to explain for now. Use this line in all
;assembly language programs you write today. Note that
;the underscore in front of config is actually a double underscore
org
0
;Ln 12. Program will start at program memory location 0
start
bsf
status, 5
;Ln. 14. Set bit 5 (make it equal to 1) in the status register.
movlw b'00000000'
movwf trisb
bcf
status, 5 ;
;This permits the microcontroller to access Bank 1 of the data registers
;(locations 80 to FF (hex) , see page 78 of your text)
;Ln 17. Move binary 00000000 into the working register
;Ln 18. Move binary 00000000 from working reg to tristate B
;register. This sets all bits of B to output
Ln 20. Clear bit 5 (making it equal to 0) of status register.
;This permits the microcontroller to access Bank 0 of the register
;files (locations 0 to 7F). These include most of the usual registers
;we will work with, in particular port B and the status register
;***********Main Routine*************
begin movlw b'00000001'
;Ln 26. Move binary 00000001 into the working register
movwf portb
;Ln 27. Move binary 00000001 from working reg to port B, making pin
; 0 go high (and therefore turning on the LED connected to pin 0)
wait
goto
wait
;Ln 29. Stay here forever - LED stays on
end
;Ln 30. ALWAYS use the end statement at the end of all your code. It
;tells the compiler to stop compiling
;************************************
c.
Save this file within your project folder using a distinctive name. You have now written
your first assembly language program.
6.
Add the file to your project.
a. Select Project/Add File to Project
b. Select your source file.
7.
Compile the program.
a. Select Project/Build All.
b. Watch the output window. It lists what happens in your attempt to compile your file. If
all went well, the final line will be BUILD SUCCEEDED.
i. Look in your directory now. There are several files, all with the same name, but
various suffixes. You have succeeded when there is one with a .hex suffix. This
is the file you will load onto the microcontroller.
c. If the output window shows errors, it will tell you on which line number they occur. Fix
these errors and try again.
i. One common error is to have too long a path name. If it has more than 62
characters, the compiler will choke. Here is the message you will receive:
Source file path exceeds 62 characters
(F:\CUYAMACAFALL08\ENGR172FALL2008\LESSON4\FIRSTPROGTUR
NONLED\TURNONLED.ASM)
If you get this error, simply move your entire folder up (for example to your C:
directory).
8.
Load the hex file onto the microcontroller. Do this using the meLabs programmer, exactly as you
would a hex file produced by PICBASIC. Put the chip into the circuit. Did the LED turn on?
9.
Let’s look at how assembly language works. If you haven’t yet read the handout from Chapter 19
of PIC In Practice, by D.W. Smith, this would be a good time to do it. Much of what a
microcontroller (indeed any computer) does at the lowest level is to manipulate data in memory
locations. These memory locations are called more-or-less equivalently files, registers, or file
registers. Certain file registers are reserved for special functions. For example, memory location
6 is I/O Port B. Other memory locations (0C to 4F) are used more generally to store variables.
See the register file map on p 78 of your text. In addition to these locations, there is something
called the working register, or W register. The W register plays the role of a central clearing
2
house. Data are first loaded into the W register, then moved to other registers. Lines 17 and 18 in
the source code are examples of this. Line 17: movlw b’00000000’ tells the processor to “move
the binary number 00000000 into the W register” (the l in movlw stands for “literal” A plain ol’
number, as opposed to a memory location, is considered a literal). Line 18: movwf trisb means
“move what is in the W register to file trisb”, which was defined previously in line 4 as being
memory location 86. So putting a 00000000 into the tristate enable B file takes 2 operations, each
of which requires exactly 1 s (more on this later). Note that the W register does not have a
numbered memory location in the same way that other registers do.
10. movwf trisb is an example of an assembly language instruction. It shows how fundamental
assembly language really is. You’re down in the guts of your computer telling it exactly how to
move a piece of data. This is why I say assembly language programming is great for control
freaks. There are 35 assembly language instructions for all PIC microcontrollers, and together
these form the PIC’s instruction set. movwf trisb is also an example of a byte instruction, because
it works on a byte of data at a time. bsf status, 5 (line 14 of the source code) is an example of a bit
instruction, because it operates on a single bit of data at a time. bsf status, 5 “sets” bit 5 in the
status register.
11. The status register is another important register, almost as important as the W register. It records
the outcomes of math operations and also tells what memory bank is in use. Each bit of the status
register contains valuable information (see the Smith handout, pp 290 – 291), but we will focus on
bits 2, 5, and 6.
a. Bit 2 is the zero bit, which is set when an operation results in a zero. We’ll see its use in
the next program, when we build timers using counters that count down to zero.
b. Bits 5 and 6 together identify which register file bank the processor is accessing. Some
explanation is needed here. PIC processors are 8-bit processors, which means they can
access up to 256 memory locations. If there are more than 256 locations, memory is
organized into file banks, sometimes called pages of memory. Bits 5 and 6 help select
which of 4 file banks to use: If bits 5 and 6 are 00, then bank 0 is selected. 01 gives bank
1, 10 gives bank 2, and 11 gives bank 3. Actually the 16F84A only has 2 memory banks
(text page 78), and you will do most of your work in bank 0. However, the tristate
registers (85h and 86h for ports A and B) are located in bank 1. To address them, you
must first set bit 5 in the status register (line 14). Then later, to go back to addressing the
lower memory locations, you must clear bit 5 (line 20).
12. Look at line 12 which reads org 0. This instruction is interesting in that it points out how PIC
microcontrollers handle programs and data. In general for computers, programs and data are
stored in RAM (random access memory). There are 2 main competing approaches to this, called
Von Neumann architecture and Harvard architecture. With Von Neumann architecture, programs
and data share the same memory area, so that if you’re not careful where you POKE data, you can
corrupt your program. With Harvard architecture data and program are stored separately, so there
is no such danger. Von Neumann architecture is by far the more commonly used, because it is
generally more efficient in terms of RAM. Small programs working with large amounts of data
can run on the same computer as large programs working with small amounts of data. PIC micros
in contrast use Harvard architecture. org 0 tells the computer to start reading the program at
program memory location 0. File register 0 does not contain the program (actually, it is used in
something called indirect addressing, which we will not do today).
13. Look finally at the end instruction, line 30. In contrast to PICBASIC programs, the end
instruction is NOT OPTIONAL in assembly language. It tells the assembler to stop compiling.
Also unlike with PICBASIC, you do not put it at the end of your main routine if you have
subroutines that follow. Instead, you put it after all your routines, both main and sub-. If you put
the end instruction before the subroutines, you will never compile them. I spent hours tearing my
hair out (and I don’t have much to tear) until I figured this out.
3
14. For comparison purposes, the following code in PICBASIC does the same thing as the assembly
language program you’ve just written. Note the similarity, but also note how PICBASIC takes
care of many of the overhead items, like deciding where in program memory to start the program,
switching file banks, and so forth.
SYMBOL
portb = 6
SYMBOL
trisb = $86
POKE trisb, %00000000
POKE portb, %00000001
wait:
GOTO wait
END
15. In this next program we will flash an LED once per second. In doing so, we will learn about
timing, conditionals, and subroutines. Type in the following program, without the comments if
you wish.
;***********EQUATES SECTION***********
status equ
3
portb equ
6
trisb
equ
h'86'
count1ms
equ
h'0c'
;Ln 5. counter for 1 ms pause
pausecount
equ
h'0d'
;Ln 6. counter for pause
;***********CONFIGURATION************
list
p=16f84 ;all this is the same as previous
__config h'3ff1'
org
0
start
bsf
status, 5
movlw b'00000000'
movwf trisb
bcf
status, 5
clrf
portb ;Ln 17. clears port B (sets all bits to zero)
;***********Main Routine*************
;Turns on LED, pauses twice for 250 ms each, then turns off
;the LED, pauses twice for 250 ms each
;Repeat forever
begin bsf
portb, 0 ;
Ln 23. sets bit 0 of port b to 1 (LED on)
movlw d'250'
;Ln 24. moves decimal 250 into working reg
movwf pausecount
;Ln 25. moves 250 from W to counter for pause
call
pause
;Ln 26. pause for 250 ms
movlw d'250'
;repeat pause sequence
movwf pausecount
call
pause
bcf
movlw
movwf
call
movlw
movwf
call
goto
portb, 0 ;
d'250'
pausecount
pause
d'250'
pausecount
pause
begin
Ln 31. sets bit Port B, bit 0 to 1 (LED off)
;do the whole pause thing again
;
;***********Subroutines**************
;subroutine pause pauses the number of ms given by number in register pausecount
;(in this case, 250)
4
pause
call
decfsz
pause1ms
pausecount
goto
return
pause
;Ln 43. pause for 1 ms
;Ln 44. decrement the number in register pausecount
;by 1. When number reaches 0, skip past the next instruction
;to the return
;Ln 48.
;subroutine pause1ms pauses for 1 ms
pause1ms
movlw d'200'
movwf count1ms
;counter to loop 200 times
dec1
nop
;Ln 53. means no operation. Does nothing except
nop
;take 1 microsecond to execute. Use for timing
decfsz count1ms
;decrement count1ms. Skip the goto instruction
;when count1ms reaches 0
goto
dec1
return
end
16. Note the new way of declaring variables. Line 5 defines a counter, count1ms, by defining its
location as register file 0C. Anytime you refer to the variable count1ms, you are in fact referring
to the number stored in that register file.
17. Notice how subroutine pause is invoked using the call instruction (e.g. Line 26). When pause
finishes executing, control will return to the next line of the main routine (e.g. Line 27). Contrast
this with GOSUB and RETURN in BASIC.
18. Let’s examine how the pause subroutine works. When it is called, it immediately calls another
subroutine called pause1ms which, not surprisingly, causes a 1 ms pause. The next instruction is
decfsz pausecount. This is a very interesting instruction because it does several things. First, it
decrements pausecount by 1. If the number in pausecount is 250 before decfsz, it is 249
afterwards. The next thing decfsz does is to determine whether the new number is zero. It does
this by looking at the Z bit in the status register (bit 2, according to Smith), which gets set to 1
when the result of decfsz is zero. Under this condition only, the program skips the next instruction
(goto pause) and executes the one that follows (return). The overall operation of the pause
subroutine is to pause 1 ms, decrement the counter, look to see if the counter is 0. If not, the
subroutine jumps back to pause another millisecond, decrement the counter, and so forth. When
the counter finally reaches 0, the subroutine returns to the main routine.
19. PIC assembly language has no IF-THEN structure, as such. Instead the language handles
conditional situations using the approach we just saw with decfsz. That is, it tests a bit (in the case
of decfsz, the Z bit in the status register) and if the bit is set, the program skips the following
instruction and executes the next one after that. If the bit is not set, the program executes the
instruction immediately after the test. Almost always, this instruction is a goto or call. There are
4 instructions which operate much like decfsz. The 4 are decfsz, incfsz, btfss (which tests a bit in a
file and skips the next instruction if the bit is set), and btfsc (which skips if the bit is clear). We
will use btfss in the next exercise.
20. Timing in the PIC microcontroller. Consider now the pause1ms subroutine. Here timing is
critical, and here is where assembly language really shines. Watch how it works: First the number
200 is moved into the counter count1ms. Then the subroutine goes 200 times through a loop.
Look at the instructions inside the loop. Two of them are nop. nop means “no operation”. What
kind of useless instruction is that? The answer is nop is used to soak up time.
a. PIC processors execute instructions at ¼ the oscillator speed. Since the oscillator runs at
4 MHz, this means the PIC executes 1 million instructions per second. Said differently,
5
b.
each instruction requires 1 s to execute. Actually most instructions require 1 s.
However, goto and call each require  s.
The total time for each loop in the pause1ms subroutine is therefore5 s, as computed
below. Since there are 200 loops, the total time is 200 x 5 s = 1000 s = 1 ms. If you
include the extra time for the movlw, movwf, and return instructions, the actual total is
1004 s, but it’s pretty close to 1 ms. You could get closer by looping only 199 times
instead of 200. Then your total would be 999 s.
pause1ms
movwf
dec1
nop
nop
decfsz
goto
return
movlw d'200'
count1ms
count1ms
dec1
;1 s
;1 s
;1 s
;2 s
5 s
=
total time in loop
21. In PICBASIC, the instruction PAUSE 250 will result in a 250 ms pause. In the assembly program
we’ve been using, the equivalent requires 3 lines of code:
movlw d'250'
movwf pausecount
call
pause
This approach may seem unwieldy, but it does allow us to pass the value of 250 to the subroutine.
This makes the pause command flexible. If we want to pause a different time, say for example
100 ms, we could simply replace 250 with 100.
To get a 500 ms pause, we invoke 2 250 ms pauses. Why not simply movlw d’500’? The answer
is we can’t. Remember that all memory locations are 1 byte in size. The biggest number you can
move into a register is 255.
22.
Activity 2 (5 pt. I. I. ________)
Write an assembly language program that flashes an LED once a second when a pushbutton is pressed.
Hint: you can use most of the previous program as subroutines, and you may want to check the status of the
pushbutton using the btfss instruction. Demonstrate operation, print your subroutine (well commented of
course, though you do not need to include as much detail as I gave in the examples – just explain the
functions of the main parts of the program, what each variable means, etc.), and include a schematic.
Final Comments
Even among programmers there aren’t many who can write assembly language code. You now belong to
that select group, although I’m sure you’d agree that you have a ways to go before you’d want to put it on
your resume. Keep assembly programming in mind when you need to control the timing exactly in a
project.
References
www.microchip.com. Microchip makes the PIC microcontrollers, and also provides the MPLab IDE for
free.
Benson, D., Easy Microcontrol’n – A Beginner’s Guide to Using PIC Microcontrollers, Square 1
Electronics, 2002.
Smith, D. W., PIC In Practice, A Project-Based Approach, 2nd Edition. ISBN 0-75-066826-1. Elsevier,
2006.
6
Download