> Warum wird _SFR_IO_ADDR(PORTB) nicht in eine Adresse umgesetzt
Weil der Assembler diese Makros nicht kennt. Schau dir mal die
avr-libc-Doku an. Da wird beschrieben, wie man genau das mit
inline-Assembler macht. Das _SFR_IO_ADDR(PORTB) muß in die
Parameterliste.
Abgeändert auf mein Problem mit "out ...." ergeben sich immernoch
Fehler. Daher habe ich mal nen Bastelprogramm (s.u.) gebaut und folgende
Möglichkeiten ausprobiert.
Fehler:
tt.c: In function 'main':
tt.c:9: error: invalid lvalue in asm statement
tt.c:9: error: output operand constraint lacks '='
tt.c:9: error: output operand constraint lacks '='
tt.c:9: error: invalid lvalue in asm output 0
Die betreffende Zeile geändert und die Var. "value" mit in die
Parameterliste aufgenommen.
tt.c: In function 'main':
tt.c:9: error: invalid lvalue in asm statement
tt.c:9: error: output operand constraint lacks '='
tt.c:9: error: input operand constraint contains '='
tt.c:9: error: output operand constraint lacks '='
tt.c:9: error: invalid lvalue in asm output 0
tt.c:9: error: input operand constraint contains '='
ander typische Fehlermeldung war auch öfters:
error: expected ':' or ')' before ';' token
Vorallem, wenn nach dem "out ...." noch eine Zeile kommt (rjmp 1b) und
dann erst die schließende Klammer ");" von "asm volatile ("
Irgendwo habe ich auch einen solchen Konstrukt mit dem Befehl "in"
gesehen, das ging, geändert auf "out und die Parameter umgedreht, auch
in der Parameterliste, ging nicht mehr. Kann echt nicht sein, seit 3
Tagen schraube ich daran rum....
> das ging, geändert auf "out und die Parameter umgedreht, auch> in der Parameterliste, ging nicht mehr.
Natürlich nicht. Die Adresse ist immer ein Eingangsparameter. Daher
kommen vermutlich auch die Fehler. Außerdem ergibt das "=" für einen
Eingangsparameter keinen Sinn, da es bedeutet, daß er nicht gelesen,
sondern nur geschrieben wird. Also:
asm volatile("out %0,%1 " : : "I" (_SFR_IO_ADDR(PORTD)), "r" (value));
Das ist ein gängiges Misstverständnis:
Die Richtung "in" und "out" bezieht sich nicht etwa darauf, was der
Prozessor mit seiner Umwelt dabei anstellt -- das interessiert das
inline-Assembler-Interface des Compilers nämlich herzlich wenig.
Sie bezieht sich darauf, was in die inline-Assembler-Anweisung
hineingegeben wird und was diese in das umgebende C-Programm wieder
zurückgibt.
Mit dem Vertauschen/umdrehen der Parameter zum 'in' bzw. 'out'-Befehl
meinte ich so, wie es auch in der Assembler-Doku von Atmel steht.
1
inr25,$16;ReadPortB
2
out$18,r16;WritezerostoPortB
Da kommt bei 'in' erst das Register, wo die Daten reinwandern, und dann
die Datenquelle. Umgekehrt ist es beim out-Befehl. Ich merke mir das so:
in Register25 der Wert von $16 und für out, out nach $18 der Wert von
Register16, in Anlehnung an das Bsp. oben.
Zu Rolfs Code:
Ich habe mal die Zeile in mein Bastelcode eingefügt und da eine Schleife
drübergelegt. Dabei kommt es mir jetzt nicht auf den Sinn des Programms
an, sondern darum, wie es nach dieser Zeile weitergeht. Alle Beispiele,
die ich bisher gesehen habe, enden komischerweise immer nach dem 'in'
oder 'out'-Befehl per ");" Daher hier mal mein gesamter Bastelcode:
1
#include<avr/io.h>
2
3
charvalue;
4
5
intmain(void)
6
{
7
value=11;
8
9
asmvolatile
10
(
11
"L0: "
12
"out %0,%1 "::"I"(_SFR_IO_ADDR(PORTD)),"r"(value)
13
"rjmp L0\n\t"
14
);
15
16
return0;
17
}
Simple Fehlermeldung:
tt.c: In function 'main':
tt.c:12: error: expected ':' or ')' before string constant
Also mal geändert:
So geht es! Aber es ist doch nicht Sinn und Zweck, jede einzelne Zeile
mit asm volatile einzuläuten.
[Einiges Zeugs wieder gelöscht, weil --- tataa --- Lösung in Sicht.]
Woran es jetzt geklemmt hat, habe ich raus. Nicht nur die "constraints"
waren nicht immer richtig, auch die Anordnung des Parameterblocks (die
"Dingers" nach dem Doppelpunkt). Dieser Parameterblock darf nämlich
nicht zwischen den Assembler Befehlszeilen stehen, sondern immer am Ende
des Anweisungsblockes, den Assemblerbefehlen, und schließt mit dem ");"
ab. Das Bastelproggi sieht dann so aus:
1
#include<avr/io.h>
2
3
intmain(void)
4
{
5
registerunsignedcharreg0asm("r0");
6
7
asmvolatile
8
(
9
"L0: \n\t"
10
"out %0,%1 \n\t"
11
"rjmp L0\n\t"
12
::"I"(_SFR_IO_ADDR(PORTD)),"r"(reg0)
13
);
14
15
return0;
16
}
Der Compiler beanstandet garnix mehr und auch im Assemblerfile, welches
dabei generiert wird (und wegen des avr-gcc-Parameters "-save-temps"
nicht gelöscht wird), sieht es sehr gut aus!
1
[....]
2
.LM1:
3
/* #APP */
4
L0:
5
out 11,r0
6
rjmp L0
7
8
.stabn 68,0,36,.LM2-main
9
.LM2:
10
/* #NOAPP */
11
[....]
Und auch mit der 11 in "out 11,r0 " ist richtig, weil
#define PORTD _SFR_IO8 (0x0B)
Damit hat das tagelange rumsuchen ein Ende. Dank euch und bis demnäxt.
Hegy
> Da kommt bei 'in' erst das Register, wo die Daten reinwandern, und dann> die Datenquelle. Umgekehrt ist es beim out-Befehl.
Nein, wieso? Auch bei out kommt zuerst das Ziel, dann die Quelle.
> Dieser Parameterblock darf nämlich nicht zwischen den Assembler> Befehlszeilen stehen, sondern immer am Ende des Anweisungsblockes, den> Assemblerbefehlen, und schließt mit dem ");" ab.
Ja. Die einzelnen Assembler-Zeilen sind ja nur "string literals", die
dann in einer frühen Übersetzungsphase zu einem kombiniert werden. Wenn
da mittendrin auf einmal ein Parameterblock kommt, kann der Compiler
damit natürlich nichts anfangen.
> Der Compiler beanstandet garnix mehr und auch im Assemblerfile, welches> dabei generiert wird (und wegen des avr-gcc-Parameters "-save-temps"> nicht gelöscht wird), sieht es sehr gut aus!
Du kannst den Compiler auch mit dem Parameter -S aufrufen. Dann erzeugt
er statt eines Object-Files ein Assembler-File, das du nicht erst in
irgendeinem Temp-Verzeichnis suchen mußt.
> Nein, wieso? Auch bei out kommt zuerst das Ziel, dann die Quelle.
Aus der Sicht schon, wir reden aneinander vorbei, ich meinte die Stelle,
wo die Parameter Rd bzw Rr steht und I/O stehen.
IN
Operation: Rd ← I/O(A)
Syntax: IN Rd,A
OUT
Operation: I/O(A) ← Rr
Syntax: OUT A,Rr
> Du kannst den Compiler auch mit dem Parameter -S aufrufen. Dann erzeugt> er statt eines Object-Files ein Assembler-File, das du nicht erst in> irgendeinem Temp-Verzeichnis suchen mußt.
Das Assemblerfile (*.s (kleines 's')) steht im dem Vz., wo auch das
C-File liegt und das Listing etc.
Hegy wrote:
>> Nein, wieso? Auch bei out kommt zuerst das Ziel, dann die Quelle.>> Aus der Sicht schon, wir reden aneinander vorbei,
Ich denke nicht ;)
> ich meinte die Stelle,> wo die Parameter Rd bzw Rr steht und I/O stehen.
Ja, genau. Und wie üblich bei Assembler Mnemonics kommt zuerst das Ziel
(Rd) und dann die Quelle (Rr). Ob jetzt eines von beiden ein I/O
Register ist, ist ja wumpe.
>> Du kannst den Compiler auch mit dem Parameter -S aufrufen. Dann erzeugt>> er statt eines Object-Files ein Assembler-File, das du nicht erst in>> irgendeinem Temp-Verzeichnis suchen mußt.>> Das Assemblerfile (*.s (kleines 's')) steht im dem Vz., wo auch das> C-File liegt und das Listing etc.
Der Compiler erzeugt ein komplettes Assembler-Listing und das ist meist
in einem der Unterordner zu finden (Relase, Debug, default, was auch
immer).