; 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)
; This is the replacement for the malfunctioning .DEVICE directive again:
.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.
; This is already done on the STK200 by external resistors, so we wouldn't
; need to use the internal resistors.Those internal resistors are build on
; 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. On 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 outputregisters.
; 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
; Subroutine Lampe0
; 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.
; Subroutine Lampe1
; switches LED1 on (a little bit different than Lampe0)
Lampe1:
in mp,PINB ; read status of port B
cbr mp,0b00000010 ; set bit 2 to zero with the CBR command
out PORTB,mp ; write back the result to the LEDs
ret ; return adress from stack and return
No comments:
Post a Comment