Hallo,
ich "möchte gern" mit Assembler ein Array im Flash-Speicher eines
XMega256 auslesen, bislang jedoch erfolglos. Bevor ich jetzt aber mit
detaillierteren Code-Beispielen komme, möchte ich erst einmal wissen, ob
mein Ansatz prinzipiell richtig ist:
Ich habe ein Array mit x Werten im Flash-Speicher abgelegt. Diese Werte
sollen an das DAC-Modul des XMegas gegeben werden. Das funktioniert
unter C auch tadellos mit Hilfe von PROGMEM und pgm_read_*(). Nun ist
aber die Ausgabe an den DAC zeitkritisch geworden und muss mit Assembler
gemacht werden.
Also caste ich zunächst die (Start-)Adresse, auf die der Array-Zeiger
zeigt, zu einem Integer und übergebe diesen an ein
Assembler-Unterprogramm. Dort gebe ich diesen Integer an den Z-Pointer,
der ja eigentlich zum Flash zeigen soll, und lasse mir mit "lpm" den
Wert an dieser Adresse ausgeben. Zum Schluss geht dieser Wert an das
DAC-Datenregister. Der Z-Pointer wird nun inkrementiert und der nächste
Wert des Arrays ausgelesen und an den DAC gegeben, bis das Array
abgearbeitet ist.
Da ich hier nach Hilfe suche, ist klar, dass es nicht funktioniert. Aber
wie gesagt, bevor ich das verkompliziere: ist der Ansatz denn
prinzipiell so machbar?
Danke schon mal und Grüße
Peter
Hi
>Dort gebe ich diesen Integer an den Z-Pointer,>der ja eigentlich zum Flash zeigen soll, und lasse mir mit "lpm" den>Wert an dieser Adresse ausgeben.
Deine Adresse dürfte ein Pointer auf ein Word im im Flash sein. LPM
braucht aber eine Byte-Adresse. Also einmal nach links shiften.
MfG Spess
spess53 schrieb:> Deine Adresse dürfte ein Pointer auf ein Word im im Flash sein.
Ein Pointer ist ein Pointer ist ein Pointer...
Da gcc das Konzept "Flash" überhaupt nicht kennt, sind C-Pointer immer
Byte-Pointer. Das lässt ich aber einfach prüfen, in dem man sich den
Wert mal ansieht.
Der Simulator eines der Studios ist bei solchen Fargestellungen auch
immer sehr hilfreich.
Prinzipiell ist das Vorgehen aber richtig - einfach den Wert ins
Z-Register laden, und die Daten lpm rx, Z+ auslesen. Den cast nach int
braucht es dafür nicht mal.
Oliver
Ich bin nicht sicher, ob ich dich richtig verstehe.
Im Array sind 16-Bit-Werte gespeichert. Laut dem "Assembler Instruction
Set"-Manual müsste "lpm" den Inhalt eines Bytes in ein gegebenes
Register laden. Da ich 16-Bit habe, muss ich das also zweimal machen
(und dazwischen einmal den Zeiger inkrementieren).
Der Adresswert, den ich von C zu Assembler gebe, sollte im Registerpaar
r25:24 liegen. r27:26 sind hier als 16-Bit Zählregister gedacht.
1
ldi r27, 0x00 ; High-Byte der Loop-Zählers (0x002D=45) in r27
2
ldi r26, 0x2D ; Low-Byte des Loop-Zählers (0x002D=45) in r26
3
4
movw r30, r24 ; Kopiere Addresse aus der 'main.c' ins Z-register
5
6
loop:
7
lpm r16, Z ; Lade Inhalt an der Adresse nach r16
8
sts DACB_CH0DATA, r16 ; Schreibe Low-Byte ins DAC-Register
9
adiw ZL,1 ; Erhöhe Z-pointer um 1
10
sts DACB_CH0DATA+1, r16 ; Schreibe High-Byte ins DAC-Register
11
adiw ZL,1 ; Erhöhe Z-pointer um 1
12
13
14
sbiw r26, 1 ; Dekrementiere Loop-Zähler (r27:26) um 1
15
mov r16, r26 ; Diese und nächste Zeile ermöglich einen...
16
or r16, r27 ; ...effizienten "Test-for-zero" für 16-Bit
17
18
brne loop ; Springe zu loop:, wenn Zähler nicht null war
19
20
ret
In meiner Theorie sollte das wunderbar funktionieren, aber irgendwas --
oder eben sehr viel -- ist daran nicht in Ordnung...
@ Peder (Gast)
>ich "möchte gern" mit Assembler ein Array im Flash-Speicher eines>XMega256 auslesen, bislang jedoch erfolglos. Bevor ich jetzt aber mit>Ich habe ein Array mit x Werten im Flash-Speicher abgelegt. Diese Werte>sollen an das DAC-Modul des XMegas gegeben werden. Das funktioniert>unter C auch tadellos mit Hilfe von PROGMEM und pgm_read_*(). Nun ist>aber die Ausgabe an den DAC zeitkritisch geworden und muss mit Assembler>gemacht werden.
Dann nimmt man so oder so DMA, das geht auch unter C problemlos.
Peder schrieb:> Im Array sind 16-Bit-Werte gespeichert. Laut dem "Assembler Instruction> Set"-Manual müsste "lpm" den Inhalt eines Bytes in ein gegebenes> Register laden. Da ich 16-Bit habe, muss ich das also zweimal machen> (und dazwischen einmal den Zeiger inkrementieren).
Oh mein Gott, ich dachte, es soll schneller werden, als der C-Compiler
es kann. Das wird dir mit diesem Code aber nicht gelingen, so Scheiße
sind die C-Compiler dann auch nicht mehr. Außerdem enthält der Code auch
noch einen fetten Fehler.
> ldi r27, 0x00 ; 1> ldi r26, 0x2D ; 1> movw r30, r24 ; 1> loop:> lpm r16, Z ; 3> sts DACB_CH0DATA, r16 ; 2> adiw ZL,1 ; 2
Hier ist der Fehler, es fehlt ein:
lpm r16,Z ; 3
> sts DACB_CH0DATA+1, r16 ; 2> adiw ZL,1 ; 2>> sbiw r26, 1 ; 2> mov r16, r26 ; 1> or r16, r27 ; 1>> brne loop ; 2/ 1
;-----
;20/19
Also für jeden Schleifendurchlauf außer dem letzten 20 Takte, im letzten
19 und einmalig nochmal drei Takte für das Vorspiel.
44*20+19+3=902
Und so macht man das richtig:
Erstmal überlegt man sich, daß das Runterzählen von 45 überhaupt kein
Zählregister in Wort-Breite benötigt, sondern ein Byte völlig
ausreichend ist. Das spart einen Takt im Vorspann und drei Takte in der
Schleife. Will man den Code so schreiben, daß er auch mit größeren
Bytezahlen funktioniert und man deshalb zur Sicherheit einen
16Bit-Zahler vorsieht, macht man das trotzdem anders. Man berechnet im
Vorspann die Endadresse und vergleicht die laufende Adressen mit dieser.
Das kostet zwar im Vorspann einmalig zwei Takte mehr, spart aber dafür
dann in jedem Schleifendurchlauf zwei Takte.
Tja und dann noch zum Schleifenkern: Da benutzt man natürlich eine
effizientere Adressierungsart, nämlich indirekt, entweder mit
postincrement oder mit predecrement. Das spart gegenüber deiner Routine
in jedem Schleifendurchlauf vier Takte.
Also, entweder so:
ldi R26,$2d ; 1
movw R30,R24 ; 1
loop:
lpm R16,Z+ ; 3
sts DACB_CH0DATA,R16 ; 2
lpm R16,Z+ ; 3
sts DACB_CH0DATA+1,R16 ; 2
dec R26 ; 1
brne loop ; 2/ 1
;-----
;13/12
44*13+12+2=586 -> (902-586)*100/902=35% gespart
oder so:
ldi R26,Low($2d*2) ; 1
ldi R27,High($2d*2) ; 1
add R26,R24 ; 1
adc R27,R25 ; 1
movw R30,R24 ; 1
loop:
lpm R16,Z+ ; 3
sts DACB_CH0DATA,R16 ; 2
lpm R16,Z+ ; 3
sts DACB_CH0DATA+1,R16 ; 2
cp ZL,R26 ; 1
cpc ZL,R27 ; 1
brcs loop ; 2/ 1
;-----
;14/13
44*14+13+5=634 -> (902-634)*100/902=30% gespart
So geht Assembler richtig.
der alte Hanns schrieb:> Und ein 'lpm' reicht unter allen Umständen auf einem XMega256?
Sicher nicht. Genausowenig wie ein 16Bit-Zeiger übrigens...
Wenn aber der 16Bit Zeiger zur Adressübergabe reicht, dann reicht auch
das lpm zum Auslesen der Daten.
der alte Hanns schrieb:> Verstehe ich Ihre Antwort richtig dahingehend, dass ein C-Compiler> solche Tabellen immer in die untersten 64 kiB legt?
nein, es gibt ja auch noch elpm und die höheren Adressbits kommen in das
Register RAMPZ.
Sascha
Das heißt, Herr Weber, dass ein C-Compiler solche Tabellen wahlweise
über den gesamten verfügbaren Adressraum verteilt? Dann allerdings
verstehe ich weder den Programmeinstieg von Peder noch die Antwort von
c-hater.
der alte Hanns schrieb:> Verstehe ich Ihre Antwort richtig dahingehend, dass ein C-Compiler> solche Tabellen immer in die untersten 64 kiB legt?
Nein, nicht wenn es ein brauchbarer Compiler ist.
@ der alte Hanns (Gast)
>Verstehe ich Ihre Antwort richtig dahingehend, dass ein C-Compiler>solche Tabellen immer in die untersten 64 kiB legt?
Nein, EIN C-Compiler nicht, aber DER avr gcc. Will man mehr, muss man
workarounden ;-)
AVR-GCC-Tutorial
-> Variablenzugriff >64kB
> Nein, EIN C-Compiler nicht, aber DER avr gcc. Will man mehr, muss man> workarounden ;-)>> AVR-GCC-Tutorial>> -> Variablenzugriff >64kB
Hier wendet sich der Gast mit Grausen...
Entschuldigen Sie die Störung, ich bleibe bei Assembler.
der alte Hanns schrieb:> Das heißt, Herr Weber, dass ein C-Compiler solche Tabellen> wahlweise> über den gesamten verfügbaren Adressraum verteilt?
Wenn er was taugt: ja.
> Dann allerdings> verstehe ich weder den Programmeinstieg von Peder noch die Antwort von> c-hater.
Man kann auch steuern, was der Compiler oder vielmehr der Linker tut.
In reinem unverfälschten Assembler, unbehelligt von jeglichen Compiler-
und Linker-Befindlichkeiten stellt sich die Frage allerdings erst
garnicht, da hat man sowieso jederzeit die volle Kontrolle über das
Speicher-Layout und bemüht sich natürlich, das so zu organisieren, daß
alle Routinen mit Vmax laufen können.
Im Kernel von RSX-11 hatte sich digital noch etwas anderes einfallen
lassen um möglichst schnell Daten herum zuschaufeln. Das könnte man hier
natürlich auch noch machen. Anstelle in einem Schlaufendurchgang nur ein
16-Bit Wort auszulesen/verschieben, kann man den Teil
1
lpm R16,Z+ ; 3
2
sts DACB_CH0DATA,R16 ; 2
3
lpm R16,Z+ ; 3
4
sts DACB_CH0DATA+1,R16 ; 2
ein paar mal (16,32,64) wiederholen, man muss natürlich den
Schlaufenzähler vorgängig anpassen. Und bei der Division der Anzahl
bytes die man übertragen muss durch die Anzahl der Bytes die in der
Schleife mit einem Rutsch übertragen werden muss man natürlich den Rest
noch berücksichtigen. Ein ijmp fällt hier leider weg. Der braucht auch
das Z register.
Peter Schranz schrieb:> Im Kernel von RSX-11 hatte sich digital noch etwas anderes> einfallen> lassen um möglichst schnell Daten herum zuschaufeln. Das
kann man auch in C, siehe Duff's Device.
Oliver
Peter Schranz schrieb:> Im Kernel von RSX-11 hatte sich digital noch etwas anderes> einfallen> lassen um möglichst schnell Daten herum zuschaufeln. Das könnte man hier> natürlich auch noch machen. Anstelle in einem Schlaufendurchgang nur ein> 16-Bit Wort auszulesen/verschieben, kann man den Teil lpm R16,Z+> ; 3> sts DACB_CH0DATA,R16 ; 2> lpm R16,Z+ ; 3> sts DACB_CH0DATA+1,R16 ; 2>> ein paar mal (16,32,64) wiederholen, man muss natürlich den> Schlaufenzähler vorgängig anpassen.
Richtig, das klassische loop-unrolling sollte man auch immer in Erwägung
ziehen. Die Indikatoren für diese Optimierung sind: wenige Takte im
Nutzcode der Schleife und eine geringe Zahl von Schleifendurchläufen.
Dann lohnt das richtig und bläht den Code auch nicht allzusehr auf.
Im Speziellen kann es aber durchaus auch mal sein, daß man sogar massive
Codeblähungen hinnimmt, wenn der Code dadurch timingmäßig die Grenze
zwischen "geht" und "geht nicht" durchbrechen kann...
Okay... Das ist jetzt doch deutlich mehr geworden, als ich erhofft
hatte. Ich werde mir das alles mal in den nächsten Tagen zu Gemüte
führen. Über die Feiertage komme ich leider nicht mehr dazu, das Ganze
umzusetzen, aber ich werde auf jeden Fall berichten, ob und was
funktioniert hat oder auch, wenn es nicht läuft.
Danke erstmal an alle und @c-hater: Ich arbeite mit Ausnahme einer
Vorlesung zum ersten Mal produktiv mit Assembler. Es lässt sich einfach
nicht vermeiden, dass mein Code nicht den Ansprüchen eines Profis
entspricht. Deshalb frage ich hier nach Rat, um dem zumindest etwas
näher kommen zu können.
Also die Zieladresse vorher zu bestimmen, ist keine schlechte Idee und
funktioniert auch. Die 45 Schleifendurchläufe habe ich deshalb in 16 Bit
gesteckt, weil es später deutlich mehr sein werden.
Das fehlende "lpm" war in meinem Code auch enthalten, ich habe es hier
wahrscheinlich aus Versehen gelöscht. Ich verstehe allerdings das (*2)
nicht, ist das ein Hinweis auf zwei Bytes?
Ich habe den Vormittag lang ein paar Tests gemacht und es scheint, dass
ich R17:16 nicht benutzen sollte, ohne sie zu sichern, deshalb nutze ich
erstmal R27:26. Selbst wenn ich R17:16 mit Konstanten beladen habe,
zeigte das Oszi nicht die erwarteten Spannungen an, also denke ich, dass
R17:16 schon benutzt wurden.
Folgendes (interessantes) habe ich aber in der lss-Datei gefunden:
1
1fc: 00 00 06 00 0c 00 12 00 18 00 1e 00 24 00 2a 00 ; Das sind die ersten 8 Werte des Arrays
2
[...]
3
28e: fc 01 movw r30, r24 ; Das ist die Übergabe des Parameters aus C in das Z-Register
Daraus kann ich doch schließen, dass die Adressübergabe funktioniert,
oder?
Mit meinem jetzigen Code (vorsichtshalber diesmal die komplette Datei)
sehe ich die assemblerproduzierten Werte des DACs aber immer auf 100%,
das hat mit den Werten des Arrays nichts zu tun. Richtige Werte
produziert er aber, wenn ich nicht aus dem Array abzufragen versuche,
sondern Konstanten einsetze.
1
#define __SFR_OFFSET 0
2
#include <avr/io.h>
3
4
.global asm_set_DAC
5
6
.section .text
7
8
asm_set_DAC:
9
ldi r26, 0x2D ; load low byte of loop-counter (45 = 0x002D) to r26
10
ldi r27, 0x00 ; load high byte of loop-counter (45 = 0x002D) to r27
11
add r26, r24 ; add address to loop-counter for...
12
adc r27, r25 ; ...pre-calculating the last address of flash-table
13
movw r30, r24 ; load address to z-register
14
15
loop:
16
lpm r24, Z+ ; load content of the address to which the 2byte-Z-pointer is pointing into r24
17
sts DACB_CH0DATA, r24 ; write to DAC - Low Byte
18
lpm r24, Z+ ; load content of the address to which the 2byte-Z-pointer is pointing into r24
19
sts DACB_CH0DATA+1, r24 ; write to DAC - High Byte
20
cp ZL, r26 ; compare low byte of Z with low byte of final address
21
cpc ZH, r27 ; compare high byte of Z (with previous carry) with high byte of final address
22
brcs loop ; check is carry flag is set (by the two last operations) - if so, branch
23
24
ret
Gibt es vielleicht noch andere Dinge, die ich bisher nicht
berücksichtigt habe, wie etwaige Eigenarten der XMEGAs oder ähnliches?
Ich habe mir gerade beim Debuggen den Speicher angesehen und
festgestellt, dass der EEPROM mit 0xFF gefüllt ist. Quasi genau das, was
der DAC ausgibt und nicht soll. Kann es sein, dass der Z-Pointer nicht
auf den Flash zeigt, sondern auf den EEPROM? Nach dem, was ich so auf
die Schnelle im Netz finde, kann man den Z-Pointer zumindest für den
EEPROM nutzen.
Die Konstante kann man besser leserlich so laden, egal ob hex oder
dezimal:
1
asm_set_DAC:
2
ldi r26, lo8(45) ;; X = 45
3
ldi r27, hi8(45)
4
...
Dann addierst du W zu X und subtrahierst es später wieder (beim
Vergleich von X gegen Z). In der Schleife ist folgendes zwar nicht
schneller aber besser verständlich:
1
...
2
movw r30, r24
3
4
loop:
5
lpm r24, Z+
6
sts DACB_CH0DATA, r24
7
lpm r24, Z+
8
sts DACB_CH0DATA+1, r24
9
sbiw r26, 1 ;; while (--X)
10
brne loop
11
...
Was die Verwendung von R16/R17 angeht musst du das ABI beachten:
http://gcc.gnu.org/wiki/avr-gcc#Register_Layout
Für die beiden Register bedeutet das, daß sie in einer Funktion nicht
einfach überschrieben werden dürfen — egal ob die Funktion in Assembler
steht oder in C.
Die Schleife sagt mir von der Lesbarkeit auch nicht besonders zu, aber
hier geht es mir vorrangig um Geschwindigkeit.
Dein Link ist i.Ü. sehr informativ. Auch wenn es mein momentanes Problem
noch nicht löst, ist diese Art Information ist genau das, was ich
normalerweise suche, aber was meistens nirgendwo steht.
Peder schrieb:> Die Schleife sagt mir von der Lesbarkeit auch nicht besonders zu, aber> hier geht es mir vorrangig um Geschwindigkeit.
SBIW ist genauso schnell wie CP + CPC, nämlich 2 Ticks. Das einzige was
geht, ist bei Arraygröße von bis zu 256 Werten (wobei 256 in der
Schleifenvariable als 0 dargestellt wird) das SBIW + BRNE durch DEC +
BRNE zu ersetzen. Das spart dann 1 Tick und ist das Ende der
Fahnenstange...
Peder schrieb:> Ich verstehe allerdings das (*2)> nicht, ist das ein Hinweis auf zwei Bytes?
Ja, klar. Bei jedem Schleifendurchlauf werden zwei Bytes gelesen, der
Zeiger also auch um zwei inkrementiert.
> Daraus kann ich doch schließen, dass die Adressübergabe funktioniert,> oder?
Nicht unbedingt. Wie schon im Thread Thema war, reicht für Flash>64k ein
16Bit-Pointer nicht unbedingt aus. Wenn du erzwingen willst, daß er
ausreicht, mußt du die Tabelle vollständig in den Bereich unter 64k
zwingen. Du kannst dich nicht darauf verlassen, was der Compiler tut,
der macht, was er will, nicht das, was sinnvoll ist oder gar das, was du
willst.
Alternativ kannst du auch den Rest des Zeigers benutzen, den der
Compiler höchstwahrscheinlich in RAMPZ übergibt. Aber dann wird's wieder
langsamer, jedenfalls, wenn du auf die Nasen hörst, die dich von der
Idee mit dem Vergleich mit der Endadresse abbringen wollen.
Wenn du nämlich einen Zähler verwendest, brauchst du dann einen
24Bit-Zähler und mußt obendrein in jedem Schleifendurchlauf einmal RAMPZ
in ein Rechenregister holen.
Bei dem Ansatz mit der Endadresse hingegen brauchst du bloß das "brcs"
durch ein "brne" ersetzen und es funktioniert. Jedenfalls solange die
Größe deiner Tabelle unter 64k ist.
> Gibt es vielleicht noch andere Dinge, die ich bisher nicht> berücksichtigt habe, wie etwaige Eigenarten der XMEGAs oder ähnliches?
Du hättest den ganzen Thread lesen sollen...
c-hater schrieb:>> Daraus kann ich doch schließen, dass die Adressübergabe funktioniert,>> oder?>> Nicht unbedingt. Wie schon im Thread Thema war, reicht für Flash>64k ein> 16Bit-Pointer nicht unbedingt aus.
Flash > 64k will er vermutlich eh nicht verwenden, da es um
Geschwindigkeit geht und ELPM langsamer ist als LPM.
> Du kannst dich nicht darauf verlassen, was der Compiler tut,> der macht, was er will,
Wasn das für'n Schmarrn?
Ein Compiler ist kein Zufallsgenerator, und in diesem Fall der Linker
auch nicht. Die Ablage der Daten im Flash (Section .progmem, die man
z.B. mittels PROGMEM oder __flash erreicht) folgt direkt auf .vectors.
Siehe die entsprechenden Linker-Skripte, welche die Code- und
Datenablage beschreiben:
1
.text :
2
{
3
*(.vectors)
4
KEEP(*(.vectors))
5
/* For data that needs to reside in the lower 64k of progmem. */
6
*(.progmem.gcc*)
7
*(.progmem*)
Daten in .progmem gelangen also nur dann in Bereiche > 64k , wenn
.vectors + .progmem > 64k ist.
Da das alles in C bereits funktioniert hat, es dort lediglich zu langsam
war, ist davon auszugehen, daß .progmem Problemlos in die unteren 64k
passt.
Johann L. schrieb:> Geschwindigkeit geht und ELPM langsamer ist als LPM.
Seit wann? In Atmels instruction set reference brauchen beide
gleichermaßen 3 Takte. Und ich kann aus umfassender Erfahrung mit
zeitkritischen Asm-Programmen bestätigen, daß Atmel hier völlig richtig
liegt.
Aber du bist ja der Überflieger, du weißt wahrscheinlich was, was
nichtmal Atmel von den eigenen Cores weiß...
c-hater schrieb:> Johann L. schrieb:>>> Geschwindigkeit geht und ELPM langsamer ist als LPM.>> Seit wann? In Atmels instruction set reference brauchen beide> gleichermaßen 3 Takte.
Ja, ist auch das, was ich in Erinnerung hatte. Hab aber beim
Nachschlagen wohl nen blöden Fehler gemacht. Mea Culpa.
Ich schließe meine Themen immer gern ab, wenn ich eine vollständige
Lösung habe, falls jemand irgendwann über etwas ähnliches stolpern
sollte:
Was ist die Fragestellung? Es sollen 16-Bit-Werte an das
DA-Wandler-Modul eines XMEGA256 ausgeben. Diese Werte stehen als Array
im Flash-Speicher - und zwar auch im Bereich > 64k.
Zunächst der C-Code, mit dem ich die Assembler-Routine aufrufe:
1
externvoidasm_set_DAC(uint16_taddress);// include "asm_set_DAC" from assembler file
2
constuint16_t*flash_pointer=flash_table;// pointer to flash_table
3
uint16_taddress=(uint16_t)&flash_pointer[0];// address of first array element
4
asm_set_DAC(address);// pass address into r25:24-register
Einer meiner Fehler bestand in der Annahme, dass "&flash_pointer" ohne
"[0]" mir die Startadresse des Arrays geben würde. Was genau
1
uint16_taddress=(uint16_t)&flash_pointer;
macht, verstehe ich immer noch nicht.
Der Assembler-Code:
1
#define __SFR_OFFSET 0
2
#include <avr/io.h>
3
4
.global asm_set_DAC ; makes asm_set_DAC visible in other source files
5
.section .text ; defines a code section
6
7
asm_set_DAC: ; start of asm_set_DAC routine
8
ldi r26, lo8(15840*2) ; load low byte of loop-counter into r26
9
ldi r27, hi8(15840*2) ; load high byte of loop-counter into r27
10
add r26, r24 ; add address to loop-counter for...
11
adc r27, r25 ; ...precalculating the last address of flash-table
12
movw r30, r24 ; load address to z-register
13
14
loop:
15
lpm r24, Z+ ; load content of the current flash-address into r24 and increment Z
16
lpm r25, Z+ ; load content of the current flash-address into r24 and increment Z
17
sts DACB_CH0DATA, r24 ; write to DAC - low byte
18
sts DACB_CH0DATA+1, r25 ; write to DAC - high byte
19
cp ZL, r26 ; compare low byte of Z with low byte of final address
20
cpc ZH, r27 ; compare high byte of Z (with previous carry) with high byte of last address
21
brcs loop ; check is carry flag is set (by the two last operations) - if so, branch
22
23
ret
Zwei Dinge könnten hier den einen oder überraschen:
1. "elpm" ist nicht nötig, auch wenn das Array den kompletten Rest des
256k-Flashs ausnutzt, also mehr als nur 64k.
2. Ich muss mich nicht um RAMPZ kümmern. Anscheinend - und das
bestätigen mir andere Funde im Internet - wird beim Überlauf des
Z-Registers das RAMPZ hardwaremäßig inkrementiert.
Nun handelt es sich bei dem DAC aber um einen 12Bit-DAC, damit sind
16Bit-Werte reine Platzverschwendung. Die Idee, die in C bereits
funktioniert, ist folgende: Die 8 kleineren Bits kommen in ein
"Low-Byte"-Array und die 4 größeren Bits kommen in ein
"High-Byte"-Array. Das ermöglicht eine um ein Drittel größere
Wertemenge, wenn man sich per Bitshifting die 12-Bit-Werte
zusammensetzt.
Vielleicht melde ich mich in einem neuen Thema nochmal, wenn ich das in
Assembler nicht umzusetzen können sollte. :)
Danke für all die Hilfe!
Peder schrieb:> die 4 größeren Bits kommen in ein "High-Byte"-Array.
Das wäre natürlich Blödsinn. Ich meinte natürlich, dass zweimal 4-Bit in
ein 8-Bit-Platz kommen.
Hi
>1. "elpm" ist nicht nötig, auch wenn das Array den kompletten Rest des>256k-Flashs ausnutzt, also mehr als nur 64k.
Nur elpm nutzt die RAMZ-Bits zu Adresserweiterung. lpm ist auf den
Adressbereich <64k beschränkt. Instruction Set zu lpm:
This
instruction can address the first 64K bytes (32K words) of Program
memory.
>2. Ich muss mich nicht um RAMPZ kümmern. Anscheinend - und das>bestätigen mir andere Funde im Internet - wird beim Überlauf des>Z-Registers das RAMPZ hardwaremäßig inkrementiert.
Richtig. Aber nur bei elpm. Allerdings funktioniert
> cp ZL, r26> cpc ZH, r27
nur bis 64k. Darüber musst du ein drittes Register benutzen.
MfG Spess
spess53 schrieb:> Allerdings funktioniert>>> cp ZL, r26>> cpc ZH, r27>> nur bis 64k. Darüber musst du ein drittes Register benutzen.
Nein. Solange die Größe der Tabelle<=64k ist, geht es weiterhin mit dem
16Bit-Vergleich. Genau das ist ja der Vorteil des Ansatzes mit dem
Adreßvergleich.
Der Unterschied besteht darin, daß man dann statt des "brcs" aus dem
Beispielcode "brne" zum Verzweigen auf den Anfang der Schleife benutzen
muß.
Das schrieb ich übrigens bereits einmal in diesem Thread...
Peder schrieb:> Zwei Dinge könnten hier den einen oder überraschen:> 1. "elpm" ist nicht nötig, auch wenn das Array den kompletten Rest des> 256k-Flashs ausnutzt, also mehr als nur 64k.
das halte ich für ein Gerücht, lpm nutzt nur eine 64k-Adresse und nicht
mehr!
Nach einem Überlauf wird wieder ab Adresse 0 gelesen.
> 2. Ich muss mich nicht um RAMPZ kümmern. Anscheinend - und das> bestätigen mir andere Funde im Internet - wird beim Überlauf des> Z-Registers das RAMPZ hardwaremäßig inkrementiert.
das stimmt, bei jedem Überlauf von Z wird RAMPZ incrementiert, aber nur
bei Befehlen, die auch RAMPZ verwenden - also nicht bei lpm.
Sascha
spess53 schrieb:> Hi>>>Nein. Solange die Größe der Tabelle<=64k ist,...>> Das war eigentlich mit>>> nur bis 64k.>> gemeint.>> MfG Spess
Peder schreibt in seinem Post aber
>und zwar auch im Bereich > 64k.
Sascha