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

;

; 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 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