Hallo Forum,
ich überlege gerade mir eine Library für SPI zu bauen. Library im echten
Sinne, also libspi-avr.a + spi.h . So weit die "Aufgabenstellung"
Nur wenn ich bei eclipse AVR cross target static Library auswähle muss
ich immer einen spezifischen AVR angeben und nur für diesen passt die
lib dann. Bei allen anderen überspringt der Linker die unpassende lib
und findet dann natürlich keine mehr. Möglicherweise liegt es am
"static" im Namen. Was hat es damit auf sich? Ich meine mich zu
erinnern, dass es auch noch "shared Libs" gibt, bin mir aber nicht mehr
sicher.
Dass der Linker meckert ist ja auch klar, so wie ich das sehe wir ganz
normal der asm-Code generiert und gelinkt und dann in eine Library
gepackt, damit man diese verwenden kann ohne dass der Compiler im
Ziel-Projekt noch einmal drüber muss. So kann es aber ja sein, dass
unpassender Code generiert wurde, etwa mit Op-Codes die der Ziel-AVR gar
nicht hat.
Aber beim PC ist es doch auch so, dass es nur eine Lib gibt, für
verschiedene Architekturen, oder irre ich mich da?
Also meine frage ob ich mit dem AVR-GCC solch eine "multi-avr-lib"
zusammenbasteln kann. Von Hand zig mal die selbe lib zu erstellen ist
mir zu aufwändig...
Ein paar Schlüsselwörter zum googlen wären auch nicht schlecht, weil ich
weiß ja nicht mal wie ich mein Problem genau beschreiben soll...
mfG,
N.G.
N. G. schrieb:> Aber beim PC ist es doch auch so, dass es nur eine Lib gibt, für> verschiedene Architekturen, oder irre ich mich da?
Pssst.
Das was landläufig unter PC verstanden wird, ist immer ein und dieselbe
Prozessor Architektur - Intels
So Dinge wie unterschiedliche Hardware als CPU-Erweiterungen sind
entweder kompatibel oder werden über herstellerspezifische Treiber
eingebunden.
Tatsächlich unterschiedliche Prozessor Architekturen findet man
Meinstream heute gar nicht mehr so oft. Und wenn, dann läuft da meistens
ein Linux drauf und Programme werden als Source Code zum selber
compilieren ausgegeben. Aber wenn wo Windows drauf steht, dann ist da
ein Intel-Prozessor (oder kompatibler) drinnen. Und wenn nicht, wie zb
beim RasPi auf dem Windows 10 läuft, dann brauchst du eigens dafür
compilierte Programme (wenn es native laufen soll, was immer mehr aus
der Mode kommt)
D.h ich kann das wirklich nur so lösen, dass ich entweder den Sourcecode
jedes mal kompiliere oder einmal für jedes meiner Devices eine lib
erstelle?
Gibt es da nicht evtl ein Format zum austausch oder eine ganz andere
Lösung?
(Sorry für die vllt dummen Fragen, aber irgendwie finde ich es komisch,
dass es so was nicht gibt.)
PS: Das mit den PC-CPU wusste ich zwar, aber es will immer noch nicht in
meinen Schädel...
N. G. schrieb:> D.h ich kann das wirklich nur so lösen, dass ich entweder den Sourcecode> jedes mal kompiliere
Wo liegt das Problem?
Typische AVR Programme kompilieren sich doch in diesen Bereichen in ein
paar Millisekunden. Zude braucht man das pro Projekt nur ein einziges
mal. Dann ist der Code ja compiliert und wird in weiteren
Entwicklungszyklen nur mehr gelinkt.
> Gibt es da nicht evtl ein Format zum austausch oder eine ganz andere> Lösung?
Mir kommt vor, du siehst ein Problem wo es in der Praxis gar keines
gibt.
> PS: Das mit den PC-CPU wusste ich zwar, aber es will immer noch nicht in> meinen Schädel...
Sei froh, dass es heutzutage so ist. Das war nicht immer so und oft war
es schwierig, genau das Programm von dem man so viel gehört hatte und
das man gerne haben wollte, genau für seine Maschine zu bekommen. Wie
glücklich war ich, als ich endlich mal meine Finger an einen TCP/IP
Stack für eine VAX (unter VMS) kriegte und damit aus Decnet ausbrechen
konnte. Einen make Ersatz hab ich mir selber programmiert, weil ich in
den diversen Gopher oder FTP Tummelplätzen einfach keinen finden konnte.
War allerdings kein Vergleich mit dem make, das auf den Apollo
Workstations nebenan lief.
hääää schrieb:> N. G. schrieb:>> Gibt es da nicht evtl ein Format zum austausch oder eine ganz andere>> Lösung?>>> z.B. Bytecode und die entsprechende VM dazu
Naja, wäre dann doch ein bisschen Overkill, meinst du nicht auch ;)
Karl H. schrieb:> N. G. schrieb:>> D.h ich kann das wirklich nur so lösen, dass ich entweder den Sourcecode>> jedes mal kompiliere>> Wo liegt das Problem?> Typische AVR Programme kompilieren sich doch in diesen Bereichen in ein> paar Millisekunden. Zude braucht man das pro Projekt nur ein einziges> mal. Dann ist der Code ja compiliert und wird in weiteren> Entwicklungszyklen nur mehr gelinkt.
Das stimmt natürlich, es geht mir auch nicht wirklich um Zeitersparnis,
vielmehr wollte ich (meinem Spieltrieb gefolgt) einfach mehrere
"Treiber" für die AVRs schreiben.
Karl H. schrieb:>> Gibt es da nicht evtl ein Format zum austausch oder eine ganz andere>> Lösung?>> Mir kommt vor, du siehst ein Problem wo es in der Praxis gar keines> gibt.
Mag schon sein.
Wie schon gesagt, Spieltrieb...
Karl H. schrieb:>> PS: Das mit den PC-CPU wusste ich zwar, aber es will immer noch nicht in>> meinen Schädel...>> Sei froh, dass es heutzutage so ist. Das war nicht immer so und oft war> es schwierig, genau das Programm von dem man so viel gehört hatte und> das man gerne haben wollte, genau für seine Maschine zu bekommen.
Man merkt halt dass ich noch die Schulbank drücke ;)
Wobei mich eigentlich vor allem die alte Technik interessiert, aber ich
hatte noch nicht so viel Zugang zu alten Maschinen, dass mir das
nennenswert aufgefallen wäre.
Karl H. schrieb:> Wie> glücklich war ich, als ich endlich mal meine Finger an einen TCP/IP> Stack für eine VAX kriegte und damit aus Decnet ausbrechen konnte. Einen> make Ersatz hab ich mir selber programmiert, weil ich in den diversen> Gopher oder FTP Tummelplätzen einfach keinen finden konnte.
Tja, bei mir war nur das Problem, dass ich nicht ins Internet konnte,
wenn jemand telefonierte. Das konnt ich damals nicht durch programmieren
lösen, und könnte es heute immernoch nicht ;)
Wer wirklich wissen will, welcher Aufwand dahinter steckt, solche
"Binär-Biblioteken" zu bauen -> die AVRlibc gibt's komplett mit alle
Build-Scripts als Source.
Kein Vergleich zu einer SPI-Source, die per #ifdef mal schnell die ein
oder andere Eigenheit bereinigt. Manches hängt ja garnicht vom Chip ab,
sondern davon, daß ein USART-Register manchmal durchnummeriert ist, oder
auch nicht. Die 100ms Compiletime wurden schon erwähnt.
Hallo NG
das was mit Hardware zu tun hat muss 'immer' MC bindend programmiert
werden!
Da es MCs gibt die 1 oder mehrere SPIs haben. Auch können einige mit 5
Bit bis 16 Bit im Datenregister umgehen.
Das was du suchst, die Gemeinsamkeit, folgt danach! API.
Alles was mit SPI zu tun hat, wie Speicher, Ethernet, LCD usw. sollte
eine 'gemeinsame' API nutzen können. Selbst das ist nicht so einfach.
Sieh dir mal den Code zu Arduino, die haben es versucht, viele MCs mit
Hilfe einer API zu verbinden, aber es bleibt da viel auf der Strecke.
Man kann doch einen Präprozessor verwenden!
Also in der Lib erstmal eine z.b. Funktion die speziell auf die Hardware
abgestimmt ist (z.b. AES beim XMega). wenn jetzt ein AVR ohne internen
AES verwendet werden möchte (z.b. ein normaler AVR) so wird jetzt die
AES-Routine aktiv. Ist zwar langsamer als die XMega-AES-Hardwareintern,
aber der Programmierer muss sioch nicht groß umstellen. Er wird sich nur
über die arschlahmen Verarbeitungsgeschwindigkeit wundern.
N. G. schrieb:> hääää schrieb:>> N. G. schrieb:>>> Gibt es da nicht evtl ein Format zum austausch oder eine ganz andere>>> Lösung?>>>>> z.B. Bytecode und die entsprechende VM dazu>> Naja, wäre dann doch ein bisschen Overkill, meinst du nicht auch ;)>
du scheinst nicht zu verstehen, um was es hier geht!
Du möchtest einen (übersetzten) Code für alle AVRs schreiben, oder?
Jeder dieser MCU hat aber unterschiedliche Hardware, Maschinenbefehle
und Register.
Wenn du einen plattformunabhängigen und vorübersetzten Code schreiben
möchtest, kein Problem, aber du benötigst etwas, was diesen universellen
Code mit den Spezifika der jeweiligen Plattform abarbeitet. Das ist wäre
dann halt eine Art Interpreter oder VM, welche für jede MCU angepasst
ist. Z.B. das allseits bekannte Java arbeitet genau so...
Damit hast du aber plötzlich mehrere Baustellen:
* den plattformspezifischen Interpreter/VM, der erst mal geschrieben
werden muss
* den Bytecode-Übersetzer, der den universellen Code für den
plattformunabhängigen Interpreter/VM bereitstellt, auch der muss
geschrieben werden
* plus noch ein paar Kleinigkeiten, wie z.B. man den Bytecode durch die
VM auf der MCU durchjagt etc.
Fazit: es ist also wahrscheinlich doch einfacher mit diversen Makros,
Defines etc. die plattformspezifischen Dinge im Quellcode abzuhandeln
und dann jedes mal zu übesetzen.
*
Hier mal eine Idee, die ich ein einem Soft-I2C umgesetzt hab: Das
C-Modul ist allgemein gehalten und unabhängig von der Hardware. Read
from und Write to SPI haben dann folgende Abhängigkeiten von der
Hardware:
1) Ports, die für SDA und SCL zu verwenden sind
2) Geschwindigkeit, mit der der Soft-SPI arbeiten soll
3) Befehle zum Erreichen der atomarität und Nebenläufigkeit einzelner
Aktionen.
Zunächst zu 2): Die Geschwindigkeit ergibt sich durch die Aufrufrate,
mit der der SPI-Treiber verwendet wird. Der I2C-Treiber ist eine
State-Machine und führt alle N Aufrufe maximal 1 Port-Operation aus.
Aufruf erfolgt aus einer "allgemeinen" Timer-ISR, die nicht zum I2C
gehört.
3) Geschieht wie gesagt dadurch, dass der Treiber auf Interruptebene
operiert. Er verwendet 2 FIFOs, die von einem externen FIFO-Modul
gehandhabt werden.
Am interessantesten ist 1) gelöst: Im Header gibt es die Deklaration
einer externen Routine, welche die I2C-Portoperationen auszuführen hat:
== i2c-soft.h ==
Im Modul i2c-soft.c wird i2cs_port dann verwendet, um mit SDA und SCL zu
spielen. Zur Bequemlichkeit gibt's da zunächst ein paar Abkürzungen:
== i2c-soft.c ==
Wie sieht nun der erzeugte Code aus?
o job_i2c_soft aus i2s-soft.c ist geinlinet in die main.c ISR.
o gleiches gilt für die Implementierung von i2cs_port von main,
die also durch die Hintertür wieder in main landet. Die cases
für JOB_start_sda0 und JOB_start_scl0, jetze in der ISR,
sehen so aus:
1
.L120:
2
sbi 0x5,1 ; 238 *sbi [length = 1]
3
ldi r24,lo8(2) ; 13 movqi_insn/2 [length = 1]
4
rjmp .L117 ; 704 jump [length = 1]
5
.L121:
6
sbi 0xb,7 ; 247 *sbi [length = 1]
7
ldi r24,0 ; 658 movqi_insn/1 [length = 1]
8
rjmp .L117 ; 702 jump [length = 1]
Der case für JOB_idle:
1
.L118:
2
;; Z = i2c_soft->fifo
3
ld r30,Y ; 213 *movhi/3 [length = 2]
4
ldd r31,Y+1
5
;; Z = fifo->count
6
;; if (0 == fifo->count)
7
;; return -1; --> führt zu "break" in JOB_idle.
8
ld r24,Z ; 214 movqi_insn/4 [length = 1]
9
tst r24 ; 215 *cmpqi/1 [length = 1]
10
brne .+2 ; 216 branch [length = 2]
11
rjmp .L100
12
;; Die FIFO enthält mindestens ein Byte, gib dieses zurück
;; Dieser Vergleich ist überflüssig, und zudem ginge er
17
;; in 1 Instruktion weniger.
18
cpi r24,-1 ; 226 *cmphi/7 [length = 3]
19
ldi r18,-1
20
cpc r25,r18
21
breq .+2 ; 227 branch [length = 2]
22
rjmp .L117
23
rjmp .L100 ; 707 jump [length = 1]
24
25
...
26
27
.L117:
28
std Y+6,r24 ; 412 movqi_insn/3 [length = 1]
29
.L100:
30
/* epilogue start */
31
pop r31 ; 551 popqi [length = 1]
32
pop r30 ; 552 popqi [length = 1]
33
...
Von fifo_get() wurde also nur ein Teil geinlint, nämlich der, der
überprüft, ob die FIFO ein neues Kommando enthält (in dem Falle gibt sie
0...255 zurück, ansonsten -1).
Obwohl der Code nicht optimal ist, ist der Overhead sehr klein, z.B.
sind die Aufrufe i2cs_port() von und Verleiche auf Port-IDs in main.c
komplett weggefallen.
Im Projekt ist das i2c-soft.c nur ein C-Modul, aber das ginge genauso in
einer Bibliothek, indem diese mit -flto -Os erzeugt wird und mit -flto
-Os -fuse-linker-plugin gegen diese gelinkt wird. Verwendet hab ich
avr-gcc-5.2.
Wichtig ist bei diesem Ansatz der Verzicht auf Callbacks.
Ich glaube anfangs denkt jeder darüber nach, aber dann stellt man fest,
das man seinen oder seine (paar wenige) µC's gefunden hat und da ist es
einfacher den Code möglichst unabhängig zu schreiben, um nur noch die
Deklaration ändern zu müssen (vornehmlich die Pin Zuordnung). Ein paar
Sachen wiederverwendbar auszulagern macht da mehr Sinn, wie im diesem
Beispiel:
Beitrag "Re: Library für alle AVRs erstellen"
Karl H. schrieb:> Das was landläufig unter PC verstanden wird, ist immer ein und dieselbe> Prozessor Architektur - Intels
Naja, mittlerweile sind es zwei, Intels und AMDs (die offiziell
leise weinend in "x86_64" umbenannt worden ist, nachdem Intel sie
schnell noch adoptiert hat), und entsprechend gibt es nunmehr selbst
in der heilen Windowswelt oft zwei Binärversionen von einem Programm,
die dann "32 bit" und "64 bit" tituliert werden.
Beim AVR kommt's drauf an. Wenn man sich die Architektur-Klassen in
der avr-libc bzw. die Multilib-Optionen im AVR-GCC ansieht, bekommt
man ein Gefühl dafür, was man da binär in einen Topf werfen kann und
tatsächlich in einer Bibliothek implementieren könnte.
Was dann allerdings noch bliebe ist, dass nicht in jedem AVR der
SPI-Modul auf der gleichen Adresse liegt. Man könnte die Adressen
über das API übergeben, dann verzichtet man jedoch auf die Möglichkeit,
dass der Compiler IN/OUT-Befehle für den Zugriff benutzt, und muss
immer über MMIO gehen. (Bei IN/OUT ist die Registeradresse Bestandteil
des Opcodes und muss daher zur Compilezeit bekannt sein. Bei MMIO
kann man über Zeigerregister die Bindung auf die Laufzeit verschieben.)
Alternativ kann man natürlich abseits der Multilib-Optionen noch eine
Unterteilung vornehmen in die AVRs, die SPI auf 0x2C … 0x2E haben
(alle neueren) und die, die es auf 0x0D … 0x0F haben:
ATmega128A
ATmega128
ATmega162
ATmega16A
ATmega16
ATmega32A
ATmega32
ATmega64A
ATmega64
ATmega8515
ATmega8535
ATmega8A
ATmega8
Das ist jetzt nur auf ATmegas bezogen, Tinys, Xmegas (sind aber
einfacher) oder ganz alte AVRs habe ich nicht nachgesehen.
Carl D. schrieb:> Wer wirklich wissen will, welcher Aufwand dahinter steckt, solche> "Binär-Biblioteken" zu bauen -> die AVRlibc gibt's komplett mit alle> Build-Scripts als Source.
Und ich hatte schon Probleme einfach nur einen Compiler mit dieser libc
zu bauen (main/trunk, etc)...
Stephan schrieb:> das was mit Hardware zu tun hat muss 'immer' MC bindend programmiert> werden!> Da es MCs gibt die 1 oder mehrere SPIs haben. Auch können einige mit 5> Bit bis 16 Bit im Datenregister umgehen.
Ich würde mich ja in diesem Fall auf die Schnittmenge beschränken. (Und
naiv wie ich bin dachte ich die SPI-Hardware ist bei jedem AVR gleich,
einschließlich Registeradresse)
Stephan schrieb:> Sieh dir mal den Code zu Arduino, die haben es versucht, viele MCs mit> Hilfe einer API zu verbinden, aber es bleibt da viel auf der Strecke.
Also DAS (Arduino) kommt bei meinem "Problem" raus? Ein bisschen mehr
Benutzerfreundlichkeit und dafür den µC um einen hohen Faktor
verlangsamen oder beschneiden? Dann is mir schnelle HW lieber ;)
Alex W. schrieb:> Man kann doch einen Präprozessor verwenden!>> ...
Wird sowieso viel Präprozessor-magie drin sein. Alleine schon um an die
Register vom ATmega324PA immer ne 0 anzuhängen (warum auch immer?).
häää schrieb:> ...> Fazit: es ist also wahrscheinlich doch einfacher mit diversen Makros,> Defines etc. die plattformspezifischen Dinge im Quellcode abzuhandeln> und dann jedes mal zu übesetzen.
Ja da hast du recht. Hätte ich auch selbst drauf kommen können...
@Johann L.
Interessanter Beitrag, geht schon mal in die Richtung. Wieder was
gelernt.
Johann L. schrieb:> Wichtig ist bei diesem Ansatz der Verzicht auf Callbacks.
Was meinst du mit Callbacks (Sagt mir in C jetzt nichts)?
Möglicherweise nicht zur Compilezeit feststehende Funktionsaufrufe?
F. F. schrieb:> Ich glaube anfangs denkt jeder darüber nach, aber dann stellt man fest,> das man seinen oder seine (paar wenige) µC's gefunden hat und da ist es> einfacher den Code möglichst unabhängig zu schreiben, um nur noch die> Deklaration ändern zu müssen (vornehmlich die Pin Zuordnung).
Wenn ich wenigstens nicht der einige bin ... ;)
Die paar wenigen µCs beschränken sich (bei den megaAVRs) auf 30 Stück,
wobei viele kompatibel zueinander sind (nur mehr Speicher).
Bisher löse ich das immer so: (für den ADC, siehe Anhang, wobei mir noch
nicht gefällt dass im Header-File schon etwas AVR-abhäniges stehen muss,
mir wären da nur die Deklarationen lieber, aber wie schon mehrfach
erwähnt hat nicht jeder AVR die selbe HW).
Jörg W. schrieb:> Beim AVR kommt's drauf an. Wenn man sich die Architektur-Klassen in> der avr-libc bzw. die Multilib-Optionen im AVR-GCC ansieht, bekommt> man ein Gefühl dafür, was man da binär in einen Topf werfen kann und> tatsächlich in einer Bibliothek implementieren könnte.
So wie ich das beim Kompilieren der avr-libc gesehen habe sind das aber
auch mdst 10 Architekturen (ohne jetzt nachgeschaut zu haben), also
immearnoch eine Menge. Die Autoren der libc haben definitiv meinen
Respekt!
Jörg W. schrieb:> Was dann allerdings noch bliebe ist, dass nicht in jedem AVR der> SPI-Modul auf der gleichen Adresse liegt. Man könnte die Adressen> über das API übergeben, dann verzichtet man jedoch auf die Möglichkeit,> dass der Compiler IN/OUT-Befehle für den Zugriff benutzt, und muss> immer über MMIO gehen. (Bei IN/OUT ist die Registeradresse Bestandteil> des Opcodes und muss daher zur Compilezeit bekannt sein. Bei MMIO> kann man über Zeigerregister die Bindung auf die Laufzeit verschieben.)> ...
Naja, eigentlich bin ich ein Verfechter davon, alles was möglich ist zur
Compilezeit zu erledigen.
Aber dein Beitrag ist auch hilfreich, gerade das mit der multilib wusste
ich noch nicht.
F. F. schrieb:> Und wenn der TO dann so weit ist, dann ... hat er Arduino. :-)
Ich fürchte auch...
Ich spiel aber noch ein bisschen damit rum, habe ja außer Zeit nichts zu
verlieren (und grad Ferien :P also genug Zeit)