Path: Home =>
AVR-Overview =>
Tutorial => Part 2
; Test 2: Learn to more about the board: Input from a port
; What to learn here:
; - to read input from a port
; - call subroutines and
setup the stack
; - Binary math operations like AND,
OR, ROL,
etc.
; - Conditional branches (commands SBIx,
BRxx)
.NOLIST
.INCLUDE "8515def.inc"
.LIST
; Define a universal register:
.DEF mp = R16
; The jump-command on adress 0 again:
RJMP main
; The main program starts here:
main: LDI mp,LOW(RAMEND)
;Initiate Stackpointer
OUT SPL,mp
LDI mp,HIGH(RAMEND)
OUT SPH,mp
These commands initiate the stack
in the build in
SRAM. Stack operations
; are always necessary when subroutines or interrupts
are called.
; By calling the subroutine or
interrupt handling routine the actual adress
; is written to the stack in
order to later jump back to the code where the
; interrupt or
call occurred. The stack
is located at the upper end of the
; build in SRAM. The upper end of the SRAM
is called RAMEND and is defined
; in the file "xxxxdef.inc" for the respective
processor type, so we do not
; have to care about its real value.
; If a byte is disposed on the stack
it is written to the SRAM location and
the
; stack pointer
at adress SPH:SPL
(a 16 bit value) is decremented to the next
; lower stack location. Further disposing
bytes brings this pointer
nearer
; to the beginning of the SRAM. If a byte
is taken from the stack then the
; stackpointer is incremented first and then
the value is read.
; The last value put on the stack is read
first when the stack is read, called
; a Last-In-First-Out
structure.
; As the program counter and the adress structure
requires 16 bits and
; all registers and the SRAM
are 8 bits wide, every adress on stack
requires
; two write/read operations to process the 16 bits. The SRAM
adress is 16
; bits wide, so the port
SPL holds the lower 8 bits
and the port
SPH holds
; the upper eight bits of the stack
adress. Togeter we get the pointer
SPH:SPL
; as a 16 bit pointer
to the stack adress.
; The operations LOW and HIGH provide the opportunity
to commincate to
; the assembler that the lower or
upper byte of RAMEND is meant when
; we set up the stack
pointer ports
with the RAMEND value.
; Port D is connected to the eight switches
on the board. In order to
; read these switches these pins have to have a zero in
their data direction
; register
LDI mp,0x00
; 8 zeros in universal register
OUT DDRD,mp
; to data direction register
; The switches connect the inputs of port
D with GND. In order to provide
; a clear logical 1 when the key is open pull-up resistors have to be added.
; On board the STK200 are external pull-ups, so we don't need the internal
; resistors. Those resistors are build in the
chip, so we can switch them
; on by software action. This is done by writing ones to the port
register:
LDI mp,0xFF
; 8 Ones into the universal register
OUT PORTD,mp
; and to port
D (these are the pull-ups now!)
; Port B connected to the LEDs is again
output, so we need to set its direction
; register.lOn startup we want the LEDs to be all off, so we need to write ones
; to the port output registers, too.
LDI mp,0xFF
; 8 Ones to the universal register
OUT DDRB,mp
; and to the data direction register
OUT PORTB,mp
; and to the output registers.
; Clicking the keys 0 and 1 should switch on
the corresponding LEDs,
; the keys 2 to 6 all the other LEDs. Clicking key 7 swiches all LEDs off.
; Within the main loop the switches are read and,
if the different conditions
; are met, branched to the different subroutines.
loop:
; Reading switch 0 (very easy first)
; The first command (SBIS) reads port
D (PIND) and tests if the bit
0 is
; one. If so, the next command is skipped. This is the case, if the switch
; is open and the input pin is pulled to one
by the pull-up. If the switch
; is on, the pin reports zero and the condition
for branching is not fulfilled.
; So the next command after SBIS must be
a single byte command that
; branches to the routine that sets LED 0 on. This must be a subroutine,
; as it has to come back after execution, because we have to process the
; other switches as well.
; This subroutine is further down in
the source code, the assembler cares
; about the displacement for the RCALL command.
The RCALL pushes the
; current adress on stack, so the subroutine
can come back to the next
; byte to be processed. The RCALL
is used here, because it is a single
; byte command, while a normal CALL
command, also implemented in the
; AVRs, is a 2-byte-command and
would not fit.
SBIS PIND,0
; Jump if bit 0 in
port D input is one
RCALL Lampe0
; Relative call to the subroutine
named Lampe0
; After processing the subroutine and
by jumping over that call command
; the next command is processed.
; Reading switch 1 (a little bit exotic)
; The ports are mirrored into the adress
space of the SRAM. The SRAM
; adress is 32 bytes higher than the respective
port adress (add
hex 20).
; So we can use SRAM read commands
to access the port. For our
; convenience we give that adress a new name:
.EQU d_in_spiegel=PIND
+ $20
; With the register pair R27:R26 we define a pointer
that points to that input
; port. With the LoaD-command we read the port
input to a register as if it
; were a SRAM byte.
LDI R26,LOW(d_in_spiegel)
; define lower pointer in
R26
LDI R27,HIGH(d_in_spiegel)
; define upper pointer in
R27
LD mp,X
; Laad register mp from pointer
adress (PIND)
; Isolate Pin1 (the switch 1) using mit AND-command
and test for all zeros
ANDI mp,0b00000010
; AND Bit
1
; Branch over all following commands
if the result of the AND command is
; not zero (switch was off, input was one). The jump command BRNE
(branch
; if not equal) branches to a lable up- or downwards
and is not limited to
; a single byte command to follow. Use it
for bigger jumps (here we don't).
BRNE weiter
; branch to lablel weiter, if not zero
RCALL Lampe1
; Relative call to subroutine
Lampe1
; Switches 2 to 6
; Read the ports D into a register, mask
the switches 0, 1 and 7 with the
; OR command and
isolate the switches 2 to 6, if all are ones skip the next
; commands with the BREQ
command to the label sw7, otherwise read the
; current status of the LEDs on port
B (PINB), set all pins from 2 to 7 to
zeros
; and send this to the port
B output.
weiter: IN mp,PIND
; Read port D
ORI mp,0b10000011
; mask switches 0, 1 und 7
CPI mp,0b11111111
; any one switch on?
BREQ sw7
; branch to label sw7, if not (= $FF)
IN mp,PINB
; read current LED-status
ANDI mp,0b00000011
; switch on lamps 2 bis 7
OUT PORTB,mp
; to LED-port
sw7: IN mp,PIND
; read port with the switches
ROL mp
; shift the seventh bit
into the carry-flag
BRCS endloop
; 7th bit is 1 (BRanch
if carry is set)
LDI mp,0xFF
; All LEDs off
OUT PORTB,mp
endloop:
RJMP loop
;
; switches LED 0 on.
Lampe0: IN mp,PINB
; Read current status on port B
ANDI mp,0b11111110
; Whatever the other LEDs might be, this is 0
OUT PORTB,mp
; write back the result to the LED port
RET ; get the return
adress from the stack and
return where you came from.
; switches LED1 on (a little bit different
than Lampe0)
Lampe1: IN mp,PINB
; read status of port B
CBR mp,0b00000010
; set bit
1 to zero with the CBR command
OUT PORTB,mp
; write back the result to the LEDs
RET ; return adress
from stack and
return
©2002 by http://www.avr-asm-tutorial.net