Hallo, ich möchte ein Assemblermodul als Funktion aus meinem C-Hauptprogramm aufrufen können (kein InlineASM). Leider klappt das nicht. bisher habe ich folgendes gemacht: 1. ASRC = port.S im makefile eingefügt. 2. ein kurzes Assembler Test-Prog soweit verändert: ;*********************************************************** ;void port(unsigned char); #include <avr/io.h> #define temp1 = r16 #define temp2 = r17 .global port .func port port: ldi temp1, 0xFF ;Port B = Ausgang out DDRC, temp1 loop: inc temp2 out PORTC,temp2 DLY: dec r4 brne DLY dec r5 brne DLY rjmp loop ret // end of function, return to main program .endfunc ;******************************************************************** Jetzt das Problem: die Register defines (temp1) funktionieren nicht (Fehlermeldungen- bad expression), wenn ich temp1, temp2 direkt mit den Registernamen (r16,r17) ersetze, funktioniert das übersetzen, allerdings funktioniert die port.S nicht.(Normalerweise müsste das Programm in der loop (endlosschleife hängen bleiben)und den PORTC hochzählen). Ich bin für jede Hilfe dankbar. Gruß Patrick
Nun, grundsätzlich erstmal ist Deine Vorgehensweise korrekt.
Aber:
> #define temp1 = r16
Das ist eine Präprozessoranweisung, in der gesagt wird, daß für jedes
Auftreten von temp1 "= r16" zu schreiben ist. Ich glaube, das ist
nicht das, was Du wolltest. :-)
#define temp1 r16
ist es wohl eher.
Zweitens, bitte lies Dir zuerst die Registerzuordnungen für den
Compiler in der avr-libc FAQ durch. r16 und r17 gehören nicht zu
den Registern, die Du blind modifizieren darfst. Ich vermute, daß Du
hier irgendwo bei für den IAR geschriebenem Assemblercode gespickt
hast, die haben aber ein völlig anderes ABI. Vergleichbar zu r16/r17
bei IAR wären r24/r25 beim GCC (erstes Argument bzw. Rückgabewert
einer Funktion).
Ferner, sieh Dir das Kapitel über die special function registers an.
Ganz so, wie Du das da schreibst, geht das nicht. Als Hack könntest
Du
#define __SFR_OFFSET 0
benutzen, aber besser ist es, die umständliche Schreibweise (s.u.) zu
nehmen.
Außerdem solltest Du natürlich sicherstellen, daß der GCC keineswegs
r4 und r5 selbst irgendwo belegt, wenn Du die wie oben gezeigt
zweckentfremden willst. Generell ist es aber oft keine gute Idee, dem
Compiler Register zu entziehen.
Abschließend, ich persönlich ein Fan der Unix-style local labels:
port:
ldi temp, 0xff
out _SFR_IO_ADDR(DDRC), temp1
1: inc temp2
out _SFR_IO_ADDR(PORTC), temp2
2: dec r4
brne 2b
dec r5
brne 2b
rjmp 1b
ret
Falls Du mit dem GDB debuggen willst, sind local labels praktisch
Pflicht (nach meiner Erfahrung), weil der GDB sonst beim ersten Label
in einer Funktion durcheinanderkommt. Alternativ zu den rein
numerischen Labels kann man auch Labels benutzen, die mit ".L"
anfangen. Die numerischen Labels haben den Vorteil, daß man sie
innerhalb einer Quelle wiederverwenden kann, es wird der jeweils
nächstgelegene Label ausgewählt beim Springen. Habe ich auch irgendwo
in der Doku alles beschrieben...
@ Jörg Wunsch Erst mal vielen Dank für die schnelle Antwort. Mit deiner Version funkioniert das Test-Prog. Allerdings kann man anscheinend mit den local labels nur nach weiter oben im Program verzweigen bzw. springen. Habe das Test-Prog auch mit normalen Labels ausprobiert und es funktionierte. Anscheinend lag das Problem an out PORTC,temp erzetzt durch out _SFR_IO_ADDR(PORTC),temp. Das kann ich allerdings nicht ganz nachvollziehen da in der io.h bzw. in der iom16.h PORTC schon mit der adresse ersetzt wird: #define PORTC _SFR_IO8(0x15) Meine Assember-Funktionen sind sehr umfangreich, so dass ich fast alle verfügbaren Register verwenden musste. Jetzte meine Frage: Ist es möglich das ich die benutzten Register zu Begin der ASM-Funktion auf den Stack schreibe (PUSH) und am ende wieder rausschreibe (POP). Könnte ich so alle Register benutzen ohne den Compiler wichtige Register zu überschreiben? Gruß Patrick
Ja, du musst nur aufpassen dass keine Interruptroutine dazwischenfunkt und dir die Register kaputt macht.
> Allerdings kann man anscheinend mit den local labels nur nach weiter > oben im Program verzweigen bzw. springen. Nein, der angehängte Suffix entscheidet: `b' sucht sich den nächsten Label mit dieser Nummer rückwärts, `f' sucht vorwärts. Sowas geht also (nur als sinnloses Beispiel -- ich habe nicht viel Ahnung von AVR-Assembler, so daß ich mir das ein bißchen abquäle jetzt): ldi r16, 5 clr r17 ldi r26, lo8(XXX) ldi r27, hi8(XXX) 1: ld r18, X+ eor r17, r18 tst r17 breq 1f dec r16 brne 1b 1: ret Das "breq 1f" springt an den Ausgang, das "brne 1b" springt zurück an den Schleifenanfang. > Das kann ich allerdings nicht ganz nachvollziehen da in der io.h > bzw. in der iom16.h PORTC schon mit der adresse ersetzt wird: > #define PORTC _SFR_IO8(0x15) Wenn Du Dir jetzt noch die Definition von _SFR_IO8() ansiehst, kannst Du es vielleicht eher nachvollziehen. :-) Diese Definitionen sind so gezimmert, daß Du halt aus C die direkte Zuweisungsnotation benutzen kannst, die Atmel auch in den Datenblättern propagiert: PORTC = 42; tmp = PINC; Dabei wird erstmal generisch über MMIO-Adressen gearbeitet (da einzig diese auf allen IO-Registern verfügbar sind). Der Optimierer konvertiert dann die MMIO-Zugriffe in direkte Port-IO, wenn das möglich ist (sprich, wenn die Registeradresse klein genug ist). > Könnte ich so alle Register benutzen ohne den Compiler wichtige > Register zu überschreiben? Andreas hat Dir ja schon geantwortet. Schau Dir inbesondere in der (avr-libc-) FAQ die Registernutzungsrichtlinien genau an, damit Du weißt, was Du retten mußt und was nicht.
Die Assembler Funktionen enthalten Interruptroutinen. Welche Register müssen dann noch gesichert werden? Das Flag Register SREG ?
Ja, natürlich. Praktisch alles. Ist ja auch logisch, ein Interrupt kann schließlich an beliebiger Stelle ausgeführt werden und darf keine Spuren hinterlassen. Laß einfach den Compiler mal eine ISR übersetzen und guck Dir an, was er daraus macht. Allerdings sind viele PUSH/POPs in einer ISR doch auch recht zeitaufwendig, da fragt man sich nach dem Sinn, warum Du eine ISR in Assembler schreiben willst...
Vielen Dank für eure Antworten. @Jörg Wunsch: Die Assembler-Funktionen existieren bereits und funktionieren, deshalb will ich Sie weiterverwenden. Jetzt habe ich aber noch ein weiteres Problem: 1. Die Adressangabe für die Interrupthandler funktioniert nicht mehr z.B: .org 0x26 rjmp timer0_compare In C geht es ja mit der Funktion: INTERRUPT(SIG_OUTPUT_COMPARE0 ){...} 2. Das GICR Register lässt sich leider nicht so beschreiben wie die anderen Register (fehlermeldung: constant value required ) z.B: out _SFR_IO_ADDR(GICR), temp1 komischerweise kommt keine Fehlermeldung wenn ich nur das INT0 nehme. (Ich schreibe dann ein Byte in ein Bit ) out _SFR_IO_ADDR(INT0), temp1 Gruß Patrick
> z.B: .org 0x26 Vergiß .org. Das hat man schon zu Z80-Zeiten unter CP/M nicht mehr benutzt. ;-) Im Ernst, für einen Prozeß, bei dem Du für verschiebliche (`relocatable') Objektdateien assemblierst, die dann erst vom Linker an den endgültigen Platz sortiert werden, hat die Angabe einer bestimmten Speicherstelle in der Quelldatei keinen Sinn. Diese Angabe müßtest Du dem Linker ja übergeben. > In C geht es ja mit der Funktion: > INTERRUPT(SIG_OUTPUT_COMPARE0 ){...} Im Assembler genauso. #include <avr/io.h> .global SIG_OUTPUT_COMPARE0 SIG_OUTPUT_COMPARE0: ... reti > Das GICR Register lässt sich leider nicht so beschreiben wie die > anderen Register (fehlermeldung: constant value required ) Hmm, welcher Prozessor? Das tut für mich... > komischerweise kommt keine Fehlermeldung wenn ich nur das INT0 > nehme. (Ich schreibe dann ein Byte in ein Bit ) Nein, machst Du nicht, sondern Du schreibst einfach nur Unsinn. :-) Im Assembler bist Du für Deinen Unfug halt selst zuständig... Du benutzt dann eine Bitnummer als Registeradresse und schreibst Dein temp1 auf dieses Register. Allerdings läßt mir der Assembler diesen Versuch mit foo.S:3: Error: number must be less than 64 nicht durchgehen.
Vielen Dank für deine Hilfe. Die Assemblerfunktion läuft einwandfrei. Jetzt hab ich aber noch eine Frage: Ist es möglich die Einsprunglabel für die Interupts nur lokal anzulegen (nicht .global SIG_OUTPUT_COMPARE0), da diese sowieso nur in der Funktion selbst benutzt werden. Somit könnte man doch wieder neue IR-Routinen für die gleichen Interrupt in anderen Funktionen schreiben (natürlich ebenfalls lokal). Mein Versuch .global SIG_OUTPUT_COMPARE0 mit .local SIG_OUTPUT_COMPARE0 zu ändern schlug leider fehl, da er dann irgendwo ins Nirvana springt und ein Neustart des Programmes erfolgt. MfG Patrick
Nein, das geht nicht, da die Interruptvektortabelle vom Linker zusammengebaut wird. Wenn Du die Labels nicht global hast, setzt der Linker stattdessen die als `weak' markierten gleichnamigen Funktionen aus dem C-Startup-Code dafür ein. Was für einen Sinn soll das haben, sie lokal zu benutzen? Das verstehe ich einfach nicht. Interrupteinsprungpunkte sind doch per definitionem global, da sie ausschließlich von der Hardware angesprungen werden, nicht von anderen Funktionen aus.
----------------------------- Diplomand SOROKA JP HTW des Saarlandes 06898-692-5831 jp.soroka@web.de -------------------- Nun möchte ich eine Ladeelektronik für ein Li-Ion 3S3P Batterypack entwickeln. Bei meiner Suche habe ich bereits eine interessante Application Note gefunden : www.atmel.com/dyn/resources/prod_documents/doc1659.pdf Nun bin ich momentan mit der ganze Problematik um zu überlegen. Es gibt ein Controller, der mit hochsprachige Quellcode programmiert ist. Ich habe bereits das eine oder das andere Programm runtergeladen, aber ich glaube das nicht alle Dateien datei ware (.c / .h Dateien in C). Die Frage die ich mir stellt : 1. Gibt es Leute die mir emailmässig helfen können ? 2. Wo kann ich Evaluation Karte kaufen ? jp.soroka@web.de ------------------------------------- AOL : Xcider21 MSN : seuldepuispeu@hotmail.com -------------------------------------
Bitte nicht also Followup auf jeden Thread hier! Mach dafür einen eigenen Thread auf, genau einen.
Bitte melde dich an um einen Beitrag zu schreiben. Anmeldung ist kostenlos und dauert nur eine Minute.
Bestehender Account
Schon ein Account bei Google/GoogleMail? Keine Anmeldung erforderlich!
Mit Google-Account einloggen
Mit Google-Account einloggen
Noch kein Account? Hier anmelden.