Hallo Zusammen,
bisher war Assembler meine bevorzugte Variante meine Controller zu
programmieren. Seit einer Woche "quäle" (aller Anfang ist schwer) ich
mich mit C (AVR Studio mit WINAVR) herum. Bisher klappte bis auf
kleinere Anfangschwierigkeiten alles bestens.
Nun wollte ich eine Routine einfügen, doch komischer Weise werden in
dieser Befehle übersprungen? Das verstehe ich nun überhaupt nicht. Eine
Fehlermeldung kann ich nicht erkennen.
Hier mal das Fragment:
1
{
2
unsignedinti;
3
4
i=((ucMaxUserPreset*4)+((event_chart[3]-1)*65));
5
6
/*Wait for completion of previous write */
7
while(EECR&(1<<EEPE));
8
9
/* Set up address register */
10
EEAR=i;
11
12
/* Start eeprom read by writing EERE */
13
EECR|=(1<<EERE);
14
15
/*unsigned char j;
16
j = EEDR;
17
j = (~j);
18
j++;
19
j = (j / 10);
20
21
SPDR = DISPL_NR_CODE[j]; */
22
23
24
SPDR=DISPL_NR_CODE[(((~EEDR)+1)/10)];
25
}
In der letzten Zeile kommt nur Käse heraus, dort soll ein Byte aus dem
EEProm geholtenwerden. Der Wert soll für die 10-er Stelle für eine 7
Segmentanzeige berechnet und ausgegeben werden. In DISPL_NR_CODE steht
lediglich die Codierung dafür, diese funktioniert auch in anderen
Routinen bestens.
Da dies nun nicht funktioniert, habe ich versucht alles in
Einzelschritten zu machen ( jetzt auskommentierter Teil über der SPDR =
- Anweisung) aber dort liesst er nur den Wert aus dem EEPROM, die
Invertierung, das Increment und die Teilung werden übersprungen (sind im
Assembler-File oder List-File auch nicht zu finden.
Kann mir hierzu mal jemand einen Tip geben? Ich weiss nicht mehr
weiter!!!
Vorab schon einmal vielen Dank!!!
Ist nicht nachvollziehbar. Ich habe aus deinem copy&paste-Teil mal
ein Stück in sich compilierfähigen Code gebaut:
1
#include<avr/io.h>
2
3
voidreadeeprom(uint8_ti,uint8_tj)
4
{
5
/*Wait for completion of previous write */
6
while(EECR&(1<<EEPE));
7
8
/* Set up address register */
9
EEAR=i;
10
11
/* Start eeprom read by writing EERE */
12
EECR|=(1<<EERE);
13
14
SPDR=j;*/
15
16
17
SPDR=(((~EEDR)+1)/10);
18
}
danach einen ATmega88 als Prozessor erraten und es durch den
Compiler geschickt. Hier der generierte Assemblercode:
1
.global readeeprom
2
.type readeeprom, @function
3
readeeprom:
4
/* prologue: frame size=0 */
5
/* prologue end (size=0) */
6
.L3:
7
sbic 63-0x20,1
8
rjmp .L3
9
clr r25
10
out (65)+1-0x20,r25
11
out 65-0x20,r24
12
sbi 63-0x20,0
13
in r24,64-0x20
14
clr r25
15
ldi r22,lo8(-10)
16
ldi r23,hi8(-10)
17
rcall __divmodhi4
18
out 78-0x20,r22
19
/* epilogue: frame size=0 */
20
ret
21
/* epilogue end (size=1) */
Die bitweise Negation und anschließende Addition entspricht einer
numerischen Negation. Die hat der Compiler dadurch ersetzt, dass
er den Divisor der Division von 10 auf -10 geändert hat. Aber die
Division selbst ist gut zu erkennen.
p.s.: <avr/eeprom.h> kennst du aber, ja?
http://www.nongnu.org/avr-libc/user-manual/group__avr__eeprom.html
Tu dir selbst einen Gefallen und verwende die fertigen EEPROM Lese
Funktionen. Jede Wette, dass der Optimizer einen Weg gefunden hat, dass
sich EEDR seiner Meinung nach nicht verändert, von dort also j immer den
gleichen Wert erhält und daher der Rest wegfallen kann. Irgendsowas in
der Richtung
1
#include<avr/eeprom.h>
2
3
...
4
5
voidfoo()
6
{
7
unsignedinti;
8
unsignedcharj;
9
10
i=((ucMaxUserPreset*4)+((event_chart[3]-1)*65));
11
12
j=eeprom_read_byte(i);
13
14
SPDR=DISPL_NR_CODE[j];
15
}
Zumindest für gewisse Basisfunktionalität kriegst du vom WinAvr
Unterstützung. Überflieg das Avr-Gcc-Tutorial um zumindest zu wissen, in
welchen Bereichen du das Rad nicht neu erfinden musst.
Hallo und DANKE für die schnelle Antwort, aber so richig verstanden habe
ich es trotzdem noch nicht, denn im restlichen Code greife ich wie
beschrieben x mal mit lesen und schreiben so auf den EEprom zu und es
klappt.
hmmm....
@Jörg Wunsch:
stimmt, es ist ein Mega88 "grübel" und <avr/eeprom.h> schau ich mir
gleich an :-( obwohl der zugriff auf das eeprom super funktioniert. also
die daten liesst er bestens aus, nur die wertewandlung dahinter macht
mir sorgen.
@Karl heinz Buchegger
so einfach geht es leider bei mir nicht. bei mir ist der EEPROM-wert das
invertierte von dem was ich benötige, einfach weil der eeprom 0xff ab
werk ist, aber für mich als 0x00 zählt. demnach kann dort später einmal
ein wert stehen, der invertiert dezimal z.B. 24 ergibt, dieser muss um 1
erhöht werden und an dieser stelle die 2 für die 10-er stelle ergeben,
in einem anderen fragment habeich das gleiche problem für die einer
stelle also nicht i / 10 sondern i % 10 - bisher wie gesagt ging alles
super - nur hier nicht mehr.
------
ich versteh auchnicht was der gute dort wegoptimieren will, und warum
erst recht nicht, aber die optimierung ist tatsächlich an. ich glaube
bis ich das verstehe, werdn noch ein paar wochen ins land gehen.
Hallo Zusammen,
cih habe es jetzt so gelöst, er kann da scheinbar nichts weg optimieren,
dafür brauch ich 2 register.
[c]
{
unsigned char j;
unsigned char k;
j = EEDR;
k = (~j);
j = k +1;
k = (j % 10);
SPDR = DISPL_NR_CODE[k];
}
und schon bekomme ich das richtige angezeigt!
ich würde aber schon gern wissen, woran das liegt! vielleicht kann sich
ja jemand erbarmen, denn mit meinen 2 C-Büchern und dem GCC Tutorial
komm ich da nicht auf einen grünen Zweig.
VG
Ich habe mich erbarmt, und kann dein Problem nicht nachvollziehen,
wie ich oben schon schrieb.
Du musst also wohl oder übel mehr an Details rüberwachsen lassen,
insbesondere:
. eine compilierbare(!) minimale Version des Codes, der das Problem
verursacht, und
. Informationen über deinen Compiler/deine Umgebung.
Nicky wrote:
> so einfach geht es leider bei mir nicht.
Doch, ist es.
Benutz einfach die eeprom_read_byte anstelle das du da selbst was
erfindest. Zum Beispiel stört mich an deinem Beispiel, dass du zwar das
Bit setzt um mit dem Lesen anzufangen, aber nicht abwartest, bis das
EEPROM die Werte rausgesucht hat :-)
> bei mir ist der EEPROM-wert das> stelle also nicht i / 10 sondern i % 10 - bisher wie gesagt ging alles> super - nur hier nicht mehr.
Dann rechne eben den Wert aus dem EEPROM entsprechend um.
@ Karl heinz Buchegger
genau das mache ich doch, die eeprom routine ist exact die aus dem
datenblatt des mega88. das j = EEDR gibt mir auch den richtigen wert.
aber die 3 anweisungen zwischen j = EEDR und der ausgabe SPDR = .... die
machen nichts, oder besser gesagt, die gibt es im assebler dann nicht
mehr und beim single step im c-code sind die auch nur schmuck ohne
wirkung. :-) also die eepromzurgriffe funktionieren. aber trotzdem lass
ich mich gern belehren und mich auf optimaler lösungen führen. deshalb
danke.
€ Jörg Wunsch
ich bastle gerade an einer version, die sich auf das nötigste beschränkt
und hoffe das dort dann der fehler auch auftritt - kann aber noch ein
wenig dauern, denn der µC ist breits mit 50% voll und das muss ich ja
nicht alles mitschicken.
Zu compiler und umgebung:
WinXP, AVR Studio 4.16 build 628 mit WinAVR vom 05.12.2008! controller
ist ein mega88, clock 20Mhz, Optimierungseinstellung -0s
der rest folgt später!
So, weiter kürzen geht kaum noch, sonst wird es zu undurchsichtig denke
ich, wird es so schon sein, denn ich lerne noch, also immer ran mit den
verbesserungsvorschlägen. der fehler tritt in "displ_mux.c" auf, der
bereicht mit den 3 varianten ist markiert und auch das verhalten steht
dabei.
ausserdem so ist es in C:
1
unsignedcharj;
2
3
j=EEDR;// <--- funktioniert
4
5
j=(~j);// berechnung erfolgt nicht
6
j=j+1;// berechnung erfolgt nicht
7
j=(j%10);// berechnung erfolgt nicht
8
9
SPDR=DISPL_NR_CODE[j];// <--- funktioniert
und dies wird daraus im Disassembler:
1
165: EEAR = i;
2
+000000F6: BD92 OUT 0x22,R25 Out to I/O location
3
+000000F7: BD81 OUT 0x21,R24 Out to I/O location
4
168: EECR |= (1<<EERE);
5
+000000F8: 9AF8 SBI 0x1F,0 Set bit in I/O register
und s wie ich das sehe, sind die 3 Anweisungen zwischen
j = EEDR; und SPDR = ..... nicht wirklich vorhanden.
die kurzform als einzeilige anweisung verhält sich gleich!
die lange forn mit j und k variablen funktioniert!
Ich bin ein wenig gespannt, was ich dort versaut habe!?
VG
Nicky wrote:
> und s wie ich das sehe, sind die 3 Anweisungen zwischen> j = EEDR; und SPDR = ..... nicht wirklich vorhanden.
Sie sind nur als Kommentar nicht vorhanden (weil der Disassembler die
Zuordnung wohl nicht hin bekommt). Als Assembler-Code sind sie da.
Noch was:
Wenn ich mir deine Postings so anschaue, dann ist es mal "/10" und mal
"%10". Könnte das vielleicht der Grund sein, warum es mal geht und mal
nicht? ;-)
Hallo Stefan Ernst,
nein, das ich mal / und mal % liegt daran, das ich die gleiche abfolge
in 2 routinen haben, einmal die 10-stelle und einmal die 1-er stelle
eines 7 segment display, demnach einmal / 10 und einmal rest (modulo).
aber trotzdem danke für deine mühe!
hmm, du meinst also im disassembler werden die zuordnungen durcheinander
gehauen? aber nichts desto trotz kam trotzdem müll bei der berechnung
heraus - ich hab es ja auch auf dem display sehen können und im single
step in C werden die zeilen auch gnadenlos übersprungen.
Nicky wrote:
> genau das mache ich doch, die eeprom routine ist exact die aus dem> datenblatt des mega88.
Karl Heinz hatte dir allerdings (genau wie ich schon weiter oben)
nahe gelegt, dass du stattdessen die Routinen aus der avr-libc
(aus <avr/eeprom.h>) benutzt.
Ansonsten hast du 1.) nach wie vor noch kein compilierbares
Beispiel geliefert (ich möchte mir gern den Assemblercode angucken,
den mein Compiler draus macht, nicht den blöden Disassembler vom
AVR Studio mit seinen unsinnigen Kommentaren und fehlenden symbolischen
Adressen), zweitens hat dein Disassembler-Listing aber trotzdem
gezeigt, dass beide Rechenoperationen da sind -- auch wenn der
Compiler die Rechnung anders gelöst hat als du.
Nicky wrote:
> aber nichts desto trotz kam trotzdem müll bei der berechnung> heraus
Dann solltest du wohl als nächstes erst einmal überdenken, ob denn
dein Algorithmus überhaupt hinhaut...
Nicky wrote:
> und im single> step in C werden die zeilen auch gnadenlos übersprungen.
Ja, logisch, weil, wie gesagt, der Disassembler den C-Code nicht dem
Assembler-Code zuordnen kann.
> aber nichts desto trotz kam trotzdem müll bei der berechnung> heraus - ich hab es ja auch auf dem display sehen können
Der Assembler-Code entspricht jedenfalls deinen C-Vorgaben.
@Jörg Wunsch
ich hab ja eine losung, mit 2 variablen geht es ja, nur würde mich
interessieen, warum es nicht mit einer klappt - ist halt reines
interesse, und vielleicht gibt es einen guten grund. mit j und k ist ja
alles prima, ich hatte mich nur dran gewöhnt, alles in eine anweisung zu
packen. im endeffekt ändert sich ja an der abarbeitung nicht wirklich
viel - denn diese routine wird nur bei einem tastendruck durchlaufen und
nicht ständig.
aber aus fehlern lernt man - und wenn es einer ist, wüsste ich schon
gern wo er steckt.
trotzdem allen vielen dank!
Nicky wrote:
> ich hab ja eine losung, mit 2 variablen geht es ja, nur würde mich> interessieen, warum es nicht mit einer klappt - ist halt reines> interesse, und vielleicht gibt es einen guten grund. mit j und k ist ja> alles prima, ich hatte mich nur dran gewöhnt, alles in eine anweisung zu> packen. im endeffekt ändert sich ja an der abarbeitung nicht wirklich> viel - denn diese routine wird nur bei einem tastendruck durchlaufen und> nicht ständig.
Alles in eine Anweiung zu packen ist kein Problem, und oben seh ich
nirgends einen falschen Code.
> aber aus fehlern lernt man - und wenn es einer ist, wüsste ich schon> gern wo er steckt.
Hier würde ein kleiner Testfall helfen, d.h. ein Stück Code, der sich
ohne Compiler-Fehler compilieren lässt.
Bei deinem obigen Code kommt zB ein
1
foo.c: In function 'foo':
2
foo.c:12: error: 'DISPL_NR_CODE' undeclared (first use in this function)
nachdem man das ganze in ne Funktion gepackt hat.
Geht man von folgenden Testfällen aus
1
#include<avr/io.h>
2
3
externunsignedcharDISPL_NR_CODE[];
4
5
voidfoo1()
6
{
7
unsignedcharj;
8
unsignedchark;
9
10
j=EEDR;
11
k=(~j);
12
j=k+1;
13
k=(j%10);
14
SPDR=DISPL_NR_CODE[k];
15
}
16
17
voidfoo2()
18
{
19
SPDR=DISPL_NR_CODE[((~EEDR)+1)%10];
20
}
dann ergibt sich i.W. folgendes Kompilat (mit -S)
1
foo1:
2
in r24,64-32
3
neg r24
4
ldi r22,lo8(10)
5
rcall __udivmodqi4
6
ldi r30,lo8(DISPL_NR_CODE)
7
ldi r31,hi8(DISPL_NR_CODE)
8
add r30,r25
9
adc r31,__zero_reg__
10
ld r24,Z
11
out 78-32,r24
12
ret
13
14
foo2:
15
in r24,64-32
16
ldi r25,lo8(0)
17
com r25
18
neg r24
19
sbci r25,lo8(-1)
20
ldi r22,lo8(10)
21
ldi r23,hi8(10)
22
rcall __divmodhi4
23
movw r30,r24
24
subi r30,lo8(-(DISPL_NR_CODE))
25
sbci r31,hi8(-(DISPL_NR_CODE))
26
ld r24,Z
27
out 78-32,r24
28
ret
Die zweite Version unterscheidet sich von der ersten durch einen
(impliziten) Cast aufgrund des C Sprachstandards. Vielleicht liegt darin
ein Problem, daß einmal mit 8 Bit gerechnet wird und einmal mit 16 Bit?
Optionen waren:
1
; GNU C (WinAVR 20090313) version 4.3.2 (avr)
2
; compiled by GNU C version 3.4.5 (mingw-vista special r3), GMP version 4.2.3, MPFR version 2.4.0.
Nicky wrote:
> @ Karl heinz Buchegger>> genau das mache ich doch,
Wo machst du das?
Dein Code in Pseudocode
Warte bis das EEPROM eine mglw. vorhergehende Operation
abgeschlossen hat
Stell die Adresse ein
Gib den Lesebefehl
Hol den Wert aus dem EEDR Register
Wo wartest du nach dem geben des Lesebefehls, das der angeforderte Wert
tatsächlich im EEDR Register angekommen ist?
Karl heinz Buchegger wrote:
> Wo wartest du nach dem geben des Lesebefehls, das der angeforderte Wert> tatsächlich im EEDR Register angekommen ist?
Man muss nicht warten.
Datenblatt:
1
The EEPROM Read Enable Signal EERE is the read strobe to the EEPROM.
2
When the correct address is set up in the EEAR Register, the EERE bit
3
must be written to a logic one to trigger the EEPROM read.
4
The EEPROM read access takes one instruction, and the requested data is
5
available immediately. When the EEPROM is read, the CPU is halted for four
Stefan Ernst wrote:
> Karl heinz Buchegger wrote:>>> Wo wartest du nach dem geben des Lesebefehls, das der angeforderte Wert>> tatsächlich im EEDR Register angekommen ist?>> Man muss nicht warten.
Ehrlich?
[Datenblatt rauskram, Tutorial rauskram]
tatsächlich!
Bin schon still. War ein Fehler meinerseits.
Johann L. wrote:
> Die zweite Version unterscheidet sich von der ersten durch einen> (impliziten) Cast aufgrund des C Sprachstandards. Vielleicht liegt darin> ein Problem, daß einmal mit 8 Bit gerechnet wird und einmal mit 16 Bit?
Ja, den Eindruck habe ich mittlerweile auch.
Wenn ich den Ausdruck
1
(((~EEDR)+1)/10)
umschreibe in
1
((uint8_t)((~EEDR)+1)/10)
dann bekomme ich den exakt gleichen Assemblercode wie für die Variante
mit den zwei zusätzlichen Variablen. Die Benutzung der Zwischen-
variablen wirkt wie ein typecast auf (uint8_t) für die einzelnen
Teilschritte.
Jörg Wunsch wrote:
> Johann L. wrote:>>> Die zweite Version unterscheidet sich von der ersten durch einen>> (impliziten) Cast aufgrund des C Sprachstandards. Vielleicht liegt darin>> ein Problem, daß einmal mit 8 Bit gerechnet wird und einmal mit 16 Bit?>> Ja, den Eindruck habe ich mittlerweile auch.
Möglicherweise wegen des sign-extend? Damit landen Zwischenwerte > 127
im Nirvana...
Johann
> ... (impliziten) Cast ... sign-extend ...
Ich möchte bei diesen Überlegungen nur zu bedenken geben, dass der
Mehrzeiler mit einer Variablen ja angeblich auch nicht funktionieren
soll.
@Karl heinz Buchegger Datum: 16.03.2009 21:27
------------------------------------------------------------------------
--------
Nicky wrote:
> @ Karl heinz Buchegger>> genau das mache ich doch,>@ Karl heinz Buchegger>Wo machst du das?>Dein Code in Pseudocode> Warte bis das EEPROM eine mglw. vorhergehende Operation> abgeschlossen hat
1
/* Wait for completion of previous write */
2
while(EECR&(1<<EEPE));
> Stell die Adresse ein
1
/* Set up address register */
2
EEAR=i;
> Gib den Lesebefehl
1
/* Start eeprom read by writing EERE */
2
EECR|=(1<<EERE);
> Hol den Wert aus dem EEDR Register
1
j=EEDR;
>Wo wartest du nach dem geben des Lesebefehls, das der angeforderte Wert>tatsächlich im EEDR Register angekommen ist?
Der Wert steht nach
1
EECR|=(1<<EERE);
nach 4 Takten im EEDR, im Disassembler dauert es auch tasächlich so
lange, bis er die folgenden Schritte durchführt.
> Auszug aus dem Datenblatt des Mega88 Seite 18:
When the EEPROM is read, the CPU is halted for four clock cycles before
the next instruction is
executed.
Zumal ich noch einmal betonen möchte, es wird der richtige Wert
ausgelesen!!! und dies x-fach auch in anderen Abschnitten nach gleichem
Schema. Es mag vielleicht nicht sonderlich Schick sein, o.k. und ich
gelobe mich zu bessern, wenn ich verstanden habe warum. Aber das EEprom
lesen und schreiben macht mir keinerlei sorgen.
@ Johann L. , @ Jörg Wunsch & @Stefan Ernst
>impliziter Cast ???
Cast-Operator habe ich schonmal gehört. hmmm
ok, ich werd mal schauen was ich dazu finde --> momentan versteh ich nur
bahnhof.
Danke euch allen!
impliziter Cast?
typumwandlung während der berechnung - hab ich es richtig?
wenn ja nur wo und warum?
ich lese aus dem eeprom
>0xFF
da dies bei mir 0 entspricht --> complement bilden, aber dabei sollte
unsingned char doch auch dies bleiben?
jetzt sollte also aus 0xFF
>0x00 -> geworden sein
dann 0x00 + 1 weil ich den folgenden Step benötige dort sehe ich
eigentlich auch keinen Grund für eine typumwandlung.
>0x01
und davon bilde ich division durch 10 und im andere Teil rest von der
Division
-> also
1
/10
= 0
-->
1
%10
= 1
da ich j als unsigned char und nicht als signed verwende - versteh ich
also nicht, wo da eine typumwandlung stattfinden soll.
Nicky wrote:
> ich lese aus dem eeprom>>>0xFF>> da dies bei mir 0 entspricht
Nun, es ist aber hier 255, d. h. durch die Umwandlung nach Typ
int (die implizit durch C vorgegeben ist), wird daraus ein 0x00FF.
Das negierst du bitweise, also 0xFF00. Danach addierst du 1, also
0xFF01. Das ist das Bitmuster der Zahl -255. Die teilst du durch
10, das ergibt eine -25, oder 0xFFE7. Davon nimmst du das untere
Byte, also 0xE7, oder 231, wenn man es als uint8_t betrachtet.
Was du dagegen willst ist, dass aus der 0xFF eine 0x00 wird, zu
dieser 1 addiert (=> 0x01), und durch 10 dividiert, ergibt 0.
> da ich j als unsigned char und nicht als signed verwende - versteh ich> also nicht, wo da eine typumwandlung stattfinden soll.
Weil C immer wenigstens im Bereich `int' rechnet.
Den Typecast, den du brauchst, habe ich dir oben schon gezeigt:
Beitrag "Re: fehlende Rechenschritte in listfile aus C"
Jörg Wunsch wrote:
> Weil C immer wenigstens im Bereich `int' rechnet.
Na das ist doch eine Aussage - die verstehe sogar ich und verstehe dann
auch, warum da Käse herauskommen muss! Ich wusste dies nicht und werd es
gleich mal ausprobieren. Ich wollte zwar
1
#include<stdint.h>
nicht verwenden, aber dann werde ich mal einiges ändern.
Ich hoffe es funktioniert dann und bedanke mich für die Erklärung und
eure Mühen!
Ich hoffe ich kann mich mal wieder revanchieren, zwar sicher nicht in
naher Zukunft bei GCC aber viellecht im Elektronikteil.
VG