Use of the LPM (Load Program Memory)



Use of the LPM (Load Program Memory) Instruction with the AVR Assembler

Load Constants from Program Memory

Use of Lookup Tables



The LPM instruction is included in the AVR Instruction Set to load a data byte from the

FLASH Program memory into the Register File.


The Flash Program memory of the AVR microcontroller is organized as 16 bits words.


The Register File and SRAM Data memory are organized as eight bits bytes.


Special consideration must therefore be taken when loading data from Program memory to

Data meory.


The Z-register in the Register File is used to access the Program memory. This 16 bits

register pair is used as a 16 bits pointer to the Program memory.






Byte-Address = Word-Address*2


The 15 most significant bits selects the word address in Program memory. Because of this, the word

address is multiplied by two before it is put in the Z-register (Z-register needs the Byte-Address of low Byte)


The least significant bit of the Z Address Register selects either Low byte (0) or High

byte(1) of the Program memory word.


To load data from random places in program memory, the Z-register must be set up

with the proper address each time a new address is accessed.


In Program memory the data is organized with one byte in the low part of a program

word and the next byte in the high part. Because of this, the message string will

appear as if every pair of characters has been swapped, when viewed in the memory

view in AVR Studio






;**** Ex_LPM1 ****

;**** A P P L I C A T I O N N O T E A V R 1 0 8 ************************

;* DESCRIPTION

;* This Application note shows how to use the Load Program Memory (LPM)

;* instruction. The App. note loads the string “Hello World” from

;* program memory byte by byte, and puts it onto port B.

;*

;***************************************************************************

.include “8515def.inc” für PORTB, DDRB benötigt

.device AT90S8515 ; Specify device der Assembler kann dann Meldungen liefern, ob ein bestimmter Befehl für dieses Device überhaupt vorhanden ist

.def temp=r16 ; Define temporary variable

start:

ldi temp,low(RAMEND) der Stack wird am Ende des RAMS eingerichtet; er wächst nach unten; SPL = Stack pointer LOW Byte SPH = Stack pointer HIGHT Byte out SPL,temp ; stack pointer to last int. RAM location

ldi temp,high(RAMEND)

out SPH,temp

ldi temp,$ff der Befehl „out“ nimmt nur ein Register als Argument! Daher zuerst den Wert in Register laden

out DDRB,temp ; Set port B as output

out PORTB,temp ; Set all pins at port B high


; Load the address of ‘message’ into the Z register. Multiplies


; word address with 2 to achieve the byte address, and uses the

; functions high() and low() to calculate high and low address byte.

Der Befehl lpm holt von der Adresse z (16bit pointer) ein Byte in das Register r0

message ist ein label (Adresse); der Programmspeicher ist in Worten (2Byte) organisiert d.h. Das Label hat z.B. den Wert 5 (Wortadresse 5), die Adresse im Speicher, also die Adresse wo der Pointer z hinzeigt ist aber 10

ldi ZH,high(2*message) ; Load high part of byte address

ldi ZL,low(2*message) ; Load low part of byte address into ZL

loadbyte:

lpm ; Load byte from program memory into r0

tst r0 ; end of the message reached?

breq quit branch if equal ; If so, quit

out PORTB,r0 ; Put the character onto Port B

rcall one_sec_delay ; A short delay

adiw ZL,1 16 bit Pointer increment (add immediate word); Increase Z

rjmp loadbyte

quit: rjmp quit


one_sec_delay:

ldi r20, 20

ldi r21, 255

ldi r22, 255

delay:

dec r22

brne delay

dec r21

brne delay

dec r20

brne delay

ret

message:

.db “Hello World” db = define byte; legt die Zeichen hintereinander im Programmspeicher ab!

.db 0 abschließende Null (vgl. Strings in c)




;**** Ex_LPM2 ****

;binär --> siebensegment Coder


In diesem Beispiel wird für eine Ziffer r1 der Siebensegmentcode ermittelt. Dazu wird in einer Lookuptable nachgeschlagen, die mit db-Direktiven als Konstante in den Programmspeicher gebracht wurden
Siebensegmentcode: die Elemente a..g werden in aufeineanderfolgender Reihe angegeben:
z.B: '0' 0111 1110 = 0x7E
'1' 0011 0000 = 0x30
das 8. Element ist der Punkt;





.include "8515def.inc"


.CSEG


ldi r31,HIGH(lookuptable*2) ; Z high byte

ldi r30,LOW(lookuptable*2) ; Set Z low byte to

lpm ; load from flash (0x33)


adiw ZH:ZL,1 ; z++ 16bit increment of Pointer z add immediate word

lpm ; load into r0 from z Pointer (0x77)


; lpm r0,z+ ; does not work at at90s8515 microprocessor


lpm ; load into r0 0xff (255)


ldi r30, 1 ; ldi needs register r16-r31

mov r1, r30

rcall bin_to_7seg

ldi r30, 0xFF ; write back the result

out DDRB, r30

out PORTB, r0


end:rjmp end


bin_to_7seg:

; get the 7segment code

; parameter: r1 ... bin value

; return value: r0 .. 7seg code

; changed registers: z


ldi r31,HIGH(code7seg<<1) shift left 1 position = Multipliziere* 2 ; Z high byte

ldi r30,LOW(code7seg<<1) ; Set Z low byte to

z zeigt jetzt auf den Anfang der Lookuptable; er muß jetzt um r1 Stellen weitergestellt werden z.B: r1 =2 ==> z zeigt auf den Siebensegmentcode von „2“

clr r2 ; set the pointer to z+r1

add r30, r1

adc r31, r2

lpm ;7seg Cod

ret


.ORG 0x122 ; organize the following code starting at location 0x122

lookuptable:

.DW 0x7733,255, 0b01010101, -128, 0xaa ,1,2,3,4,0

code7seg:


.DW 0xa0,0xa1,0xa2,0xa3,0xa4,0xa5,0xa6,0xa7,0xa8,0xa9 diese Werte dienen in der Entwicklungsphase als Ersatzwerte für die eigentlichen 7-Segment-Codes


Kner 2004 6/6