AVR: C-Bibliothek in Assembler verwenden Im Unterforum Microcontroller - Beschreibung: Hardware - Software - Ideen - Projekte
Autor |
AVR: C-Bibliothek in Assembler verwenden |
|
|
|
|
BID = 608276
Lupin III. Schriftsteller
Beiträge: 616 Wohnort: Salzburg
|
|
Ich programmiere meine AVRs in Assembler. Ich habe es vor einiger Zeit mal mit C probiert, aber der Code der da rausgekommen ist war so überladen mit sinnlosen Anweisungen, dass ich das als "Effizienzialist" (mein Wort für "perfektionistischer Minimalist" oder "minimalistischer Perfektionist" ) nicht ansehen konnte (außerdem gingen sich dadurch einige zeitkritische Dinge einfach nicht aus).
Jetzt möchte ich aber einen Dallas DS18B20 ansteuern (Temperatur-Sensor mit 1-Wire-Interface). Es gibt davon einige Implementierungen im Internet, nur habe ich bis jetzt in Assembler nur eine sehr verbugte und eine zweite, nur für den DS18S20 (der hat niedrigere Auflösung) funktionierende, gefunden. In C gibt es einiges mehr (obwohl ich davon nichts getestet habe). Da mein restliches Programm in Assembler geschrieben ist, frag ich mich jetzt, ob es irgendwie möglich ist, eine C-Bibliothek einzubinden. Die müsste man wahrscheinlich natürlich zuerst mal in ein Assembler Listing compilieren lassen.
Kann mir jemand sagen, ob und wie das möglich ist, sowas zu verknüpfen? Ich verwende avrasm2 im AVR-Studio (das ich auch gerne wegen der Debugging-Möglichkeiten weiter verwenden würde, so sehr mich auch der Editor selbst nervt). |
|
BID = 608288
DonComi Inventar
Beiträge: 8605 Wohnort: Amerika
|
|
Moin,
Das ist möglich, zumindestens mit der Toolchain, die ich verwende.
Wichtig ist eigentlich nur, dass der Objektcode, der vom Compiler erstellt wurde, mit dem Ausgabeformat des Assemblers kompatibel ist.
Du assemblierst dein Assemblerlisting, in dem die zu verwendenden Symbole (z.B. Funktionsnamen aus der C-Bibliothek) extern deklariert werden. Damit erfährt der Assembler, dass diese Referenzen erst vom Linker aufgelöst werden.
Dann übersetzt du die C-Bibliothek, und linkst die beiden gegeneinander. Fertig.
P.S.:
Zitat :
| war so überladen mit sinnlosen Anweisungen |
Tja, man sollte eben nicht nur C beherrschen, sondern auch die verarbeitende Toolchain. Man kann mit vielen Einstellungen schon recht effizienten Code erzeugen lassen.
Damit C-Programme korrekt laufen, sind z.B. anfangs viele _init-Stufen vorhanden, die je nach Priorität bestimmte Sachen initialisieren. Das kann man alles abschalten, wenn man sich sicher ist, es nicht zu benötigen.
Achso, sollte es nicht klappen, die beiden Objektcodes zu linken, compiliere die C-Bibliothek, deassembliere sie und passe den Kode an - ist zwar arbeitsaufwändig, klappt dafür aber meistens (wenns nicht zu lang ist).
Oder mach es wie ich: ich schreibe fast alles in C und linke später zeitkritische Dinge, die ich in Assembler kodiert habe, zu dem C-Programm hinzu. Dazu erstellt man einen Header, in dem exportierte Symbole (wie Funktionsnamen, globale Flaggen, etc.) deklariert werden. Der Linker löst das dann auf.
_________________
|
|
BID = 608345
DonComi Inventar
Beiträge: 8605 Wohnort: Amerika
|
Moin, was mir noch eingefallen ist:
Je nach dem, mit welchem C-Compiler du kompilierst, musst du natürlich ein paar Teile in deinem Hauptprogrann anpassen:
Wenn z.B. einer Funktion Parameter übergeben werden sollen, musst du schauen, wie das dein Compiler haben möchte. Es ist abhängig vom Datentyp, von der Anzahl und von der Größe der Parameter/Argumente.
Sind es zuviele oder zu große, werden sie über den Stack übergeben. Ansonsten wird das bei AVRs sinnigerweise über Register gemacht.
Das Gleiche gilt für entsprechende Ergebnisse, die die Funktion zurückliefert. Auch diese landen i.d.R. bei AVRs in definierten Registern.
An diese Regeln, die auf den ersten Blick nicht immer Sinn ergeben (der aber bei genauerem Überlegen doch da ist ) musst du dich zwingend halten, sonst funktionierts nicht.
Zudem musst du die C-Bibliothek mit bestimmten Compiler-Optionen übersetzten: es muss festgelegt werden, dass die Quelldatei(en) nur compiliert, nicht aber gelinkt (z.B. gegen die Initialisierungs-Prozeduren und kleinere AVR-Bibliotheken) werden sollen. Mit dem gcc, den du u.U. in Verbindung mit WinAVR einsetzt, geschieht das mit dem Argument -c.
Außerdem muss dein Zielchip (-mmcu=atmega8 z.B.) sowie die Taktfrequenz angegeben werden, das geschieht mit einem Makro, welches man dem Compiler in der Form -D übergeben kann, in diesem Fall -DF_CPU=12000000UL oder ähnlich. Weiterhin kannst du ja mal herumprobieren, auf welchem Optimierungslevel der Kode am kleinsten bzw. am schnellsten ist: -O(n) bzw. -Os.
Wenn es mit deinem Assembler dann nicht klappt, versuche es mit dem gcc als Linker und Assembler (ld und as).
Viel Erfolg
(ist immer schwierig zu helfen, wenn man U.u. unterschiedliche Programme benutzt. Wenn du aber WinAVR hast, sieht alles gut aus )
_________________
|
BID = 608348
DonComi Inventar
Beiträge: 8605 Wohnort: Amerika
|
So, habs mal scherzhaft ausprobiert, meine "Bibliothek" zählt alle gesetzten Bits in einem Byte. Dieses Byte wird vom Compiler in r24 erwartet:
bibliothek.c:
#include <avr/io.h>
#include <inttypes.h>
/* berechne die Anzahl aller gesetzten Bits von WERT */
uint8_t count_bits (uint8_t wert_)
{
register uint8_t counter_ = 0;
for(; wert_ ; wert_ >>= 1) if(wert_ & 1) counter_++;
return counter_;
};
Das Hauptprogramm main.S:
#include <avr/io.h>
.global main
;dieses Symbol ist die Funktion aus der C-Bibliothek
.extern count_bits
.func
main:
ldi r24, 0xff
rcall count_bits
;Ergebnis nun in r24
rjmp -0x00
.endfunc
.end
Das Makefile erstellt mit > make main das Hauptprogramm und spuckt auch das Disassembling aus:
CC=avr-gcc
OC=avr-objcopy
OD=avr-objdump
AVR_OPT=-mmcu=atmega8 -DF_CPU
# C-Bibliothek übersetzen (nur kompilieren, optimieren)
bibliothek.o: bibliothek.c
$(CC) $(AVR_OPT) -o bibliothek.o -c -Os bibliothek.c
# Assemblerlisting ausspucken
$(OD) -d bibliothek.o
# Hauptprogramm übersetzen und mit bibliothek linken
main: main.S bibliothek.o
$(CC) $(AVR_OPT) -o main main.S bibliothek.o
# auch mal deassemblieren
$(OD) -d main
# Hexdatei erstellen
$(OC) -O ihex main main.hex
clean:
rm -f *.o main main.hex *~
Hier das Disassembling von bibliothek.o:
00000000 <count_bits>:
0: 90 e0 ldi r25, 0x00 ; 0
2: 00 c0 rjmp .+0 ; 0x4 <count_bits+0x4>
4: 80 fd sbrc r24, 0
6: 9f 5f subi r25, 0xFF ; 255
8: 86 95 lsr r24
a: 88 23 and r24, r24
c: 01 f4 brne .+0 ; 0xe <count_bits+0xe>
e: 89 2f mov r24, r25
10: 99 27 eor r25, r25
12: 08 95 ret
Man sieht deutlich, dass die relativen Sprünge noch keinen Sinn ergeben. Das liegt daran, das beim Linken die Adressen bzw. die Offsets zum Springen neu berechnet werden (müssen).
Hier das entsprechend fertige Programm:
0000005e <main>:
5e: 8f ef ldi r24, 0xFF ; 255
60: 01 d0 rcall .+2 ; 0x64 <count_bits>
62: fd cf rjmp .-6 ; 0x5e <main>
00000064 <count_bits>:
64: 90 e0 ldi r25, 0x00 ; 0
66: 03 c0 rjmp .+6 ; 0x6e <count_bits+0xa>
68: 80 fd sbrc r24, 0
6a: 9f 5f subi r25, 0xFF ; 255
6c: 86 95 lsr r24
6e: 88 23 and r24, r24
70: d9 f7 brne .-10 ; 0x68 <count_bits+0x4>
72: 89 2f mov r24, r25
74: 99 27 eor r25, r25
76: 08 95 ret
Die relativen Sprünge stimmen nun.
Den Teil, der die Initialisierungen übernimmt, fehlt. Du kannst ja mal die drei Dateien erzeugen (Makefile, bibliothek.c und main.S) und damit herumspielen. Mit > make clean werden alle Dateien außer Quellen gelöscht.
Am Ende wird auch ein brennbares Hex-File erzeugt.
Viel Spaß
Edit: och nö, meine Formatierung ist völlig im Eimer. Daher lade ich mal alles als Archiv hoch.
_________________
[ Diese Nachricht wurde geändert von: DonComi am 16 Mai 2009 23:50 ]
|
BID = 608383
Lupin III. Schriftsteller
Beiträge: 616 Wohnort: Salzburg
|
Vielen Dank für die große Hilfe! Ich habe jetzt grade noch keine Zeit das zu testen (Sonntag, zu schönes Wetter und noch kein avr-gcc auf diesem Rechner installiert). Aber später dann. Ich muss noch schauen, ob ich das ganze mit avrasm2 verknüpfen kann. Der hat nämlich leichte andere Funktionen eingebaut, was Preprocessor und ähnliches betrifft, und von denen habe ich schon einiges verwendet.
|
BID = 608444
DonComi Inventar
Beiträge: 8605 Wohnort: Amerika
|
Moin,
Ja, das ist das Problem. Ich kann immer nur aus der Sicht vom GCC und den GNU Binutils berichten.
Aber es wäre schon viel geholfen, wenn dein Assembler das ELF-Format könnte. Das können die Binutils und der GCC verarbeiten.
Du könntest in diesem Fall also mit deinem Assembler weiterarbeiten.
Es ist auch nicht unbedingt notwendig, die Funktion extern zu deklarieren, u.U. funktioniert es auch so. Es kann dann eben nur passieren, dass eine Warnung angezeigt wird, dass es sich bei der externen Funktion um eine undefinierte Referenz handelt. Beim Linken jedoch wird diese Referenz aufgelöst und gut ist.
Achso, der GCC selbst hat keinen Linker und keinen Assembler. Er ruft die Programme aber auf. Daher habe ich oben als Linker auch den avr-gcc genannt, auch wenn dieser den externen Linker avr-ld aufruft.
_________________
|
|
Zum Ersatzteileshop
Bezeichnungen von Produkten, Abbildungen und Logos , die in diesem Forum oder im Shop verwendet werden, sind Eigentum des entsprechenden Herstellers oder Besitzers. Diese dienen lediglich zur Identifikation!
Impressum
Datenschutz
Copyright © Baldur Brock Fernsehtechnik und Versand Ersatzteile in Heilbronn Deutschland
gerechnet auf die letzten 30 Tage haben wir 20 Beiträge im Durchschnitt pro Tag heute wurden bisher 0 Beiträge verfasst © x sparkkelsputz Besucher : 182408340 Heute : 322 Gestern : 7797 Online : 282 27.11.2024 3:44 0 Besucher in den letzten 60 Sekunden ---- logout ----viewtopic ---- logout ----
|
xcvb
ycvb
0.0767440795898
|