Hallo,
es geht um das Hauptprogramm von hier:
http://www.myplace.nu/avr/minidds/index.htm
Ich denke mal, dass ich die Tabellen und so, in eine Assembler-Datei
verpackt, auch weiterverwenden kann.
Sinn der Aktion ist der, dass ich gerne das Benutzer-Interface ändern
möchte - hin zu ein paar Taster plus Drehencoder und Display.
Auch möchte ich nicht, dass der Ausgang sofort zu arbeiten anfängt, wenn
Spannung vorhanden ist.
Dafür würde ich gerne eine Art Zustandsautomat coden,der bei
entsprechendem Zustand die Schleife des jetzigen Hauptprogramms
abarbeitet. Dafür müsste ich zumindest eine Abfrage auf Abbruchbedingung
zufügen.
Das Original ist geil - ohne Frage.
Kann mir vielleicht jemand die impliziten Seiteneffekte (die
zweifelsohne ausgereizt wurden) erklären?
Das Hauptprogramm sieht so aus:
; r24,r25,r26 is the adder value determining frequency
7
;
8
; add value to accumulator
9
; load byte from current table in ROM
10
; output byte to port
11
; repeat
12
;
13
LOOP1:
14
add r28,r24 ; 1
15
adc r29,r25 ; 1
16
adc r30,r26 ; 1
17
lpm ; 3
18
out PORTB,r0 ; 1
19
rjmp LOOP1 ; 2 => 9 cycles
Was ich schon rausgefunden habe:
- Der Zähler ist eine 24bit Variable über die Register R28-R30, wobei
R30 bereits Teil des Z-Pointers ist.
- R24-R26 enthält wohl die Schrittweite (auch als 24bit Variable)
- lpm - lädt den Wert aus einer Tabelle in das Register R0
Also in C könnte ich den 24bit Typ ja über eine Struktur/Bitfeld/Union
definieren und Zähler und Schrittweite getrennt verwalten.
Aber wie wird der Index in eine Tabelle mit 256 Einträgen aus dem
24bittigen Zähler erledigt?
Welche Rolle spielt das doppelt verwendete Register R30?
Oder ...
... könnte mir jemand die Schleife in eine Assembler-Funktion verpacken
(und mir dann noch den Aufruf erklären - bitte), die ein globales
Bitfeld-Element als Endekriterium abfragt?
z.B. aus folgender Struktur:
Kurz gesagt: Vergiss es.
Das ganze ist extremst auf Timing optimiert, in C wird es sehr viel
schlechter sein. Auf jedenfall wirst du nicht um eine Analyse des
erzeugten Assembler Codes herum kommen, denn die benötigte Zeit pro
Durchlauf ist ein wichtiger Faktor um die Ausgangsfrequenz zu berechnen.
> Kurz gesagt: Vergiss es.
Das finde ich jetzt weder nett, noch konstruktiv.
Wenn es für mich OK wäre, dass die Schleife um einen Faktor 10 langsamer
würde - warum nicht? Exakte Zeiten kann man ja auch in C erreichen.
Ich schrieb doch schon, dass ich erkannt habe, dass der Code extrem
optimiert wurde. Vielleicht brauche ich die "Hochfrequenz" ja garnicht.
Keine Ahnung, was Dich zu dieser pauschalen Ohrfeige veranlasst hat -
ich hoffe mal, dass es noch andere Leser gibt, die Assembler können und
mir weiterhelfen mögen.
Santiago wrote:
>> Kurz gesagt: Vergiss es.>> Das finde ich jetzt weder nett, noch konstruktiv.
Aber realistisch.
Ich bin sicher ein großer Freund davon, statt Assembler eine
höhere Sprache zu benutzen, aber derart handgefeilten Code wie
Jaspers Mini-DDS würde auch ich so lassen, wie er ist.
> Wenn es für mich OK wäre, dass die Schleife um einen Faktor 10 langsamer> würde - warum nicht? Exakte Zeiten kann man ja auch in C erreichen.
Wie denn?
> Aber wie wird der Index in eine Tabelle mit 256 Einträgen aus dem> 24bittigen Zähler erledigt?
Indem du den entsprechenden Teil deiner union als Index benutzt?
Knackpunkt ist, wieder aus der Schleife rauszukommen. Momentan sehe ich
aber nicht, wie man es einer nicht-naked ISR beibringt, das T-Flag zu
setzen. Naked-ISR wiederum bedeutet Assembler, so daß Du nicht wirklich
weiter bist...
Alle anderen Möglichkeiten würden das Zeitverhalten ändern. Wenn Du mit
10 Ticks leben kannst würde es die Sache deutlich vereinfachen. Du
könntest einen ATtiny2313 statt des AT90S2313 verwenden (fast
pinkompatibel, aber regulär bis 20MHz taktbar anstatt 16MHz).
Wie auch immer, es wird n ziemlicher Hack das auf "C"-Ebene zu machen.
Ach ja, Du musst bei den Tabellen Alignments zu 2**8 beachten. Die
Tabellen selbst sind einfach zu übernehmen, da das Original in GNU-C
dasteht.
Santiago wrote:
>> Kurz gesagt: Vergiss es.>> Das finde ich jetzt weder nett, noch konstruktiv.
Die Wahrheit ist manchmal hart, ich weiß.
> Ich schrieb doch schon, dass ich erkannt habe, dass der Code extrem> optimiert wurde. Vielleicht brauche ich die "Hochfrequenz" ja garnicht.
Das ist alles anderere als Hochfreuquenz: Wenn ein Sinus einigermaßen
sauber aussehen soll, dann solltest du mindestens 10 Samples pro Periode
ausgeben. Das sind beim Audiofrequenzbereich von 0-20kHz schon 200kHz
Samplerate die notwendig sind. Und das ist in C alles andere als
einfach.
> Keine Ahnung, was Dich zu dieser pauschalen Ohrfeige veranlasst hat -
Wie ich oben schon schrieb: Die Wahrheit.
Wenn ich du wäre, würde ich eine von den folgenden Lösungen wählen:
Die Software so lassen, und das ganze einfach in einen µC brennen und
diesen dann per UART über einen zweiten µC der in C, BASCOM oder sonst
was programmiert ist ansteuern (genau so habe ich das vor vielen Jahren
auch gemacht.)
Oder versuchen das ganze um einiges langsamer in C zu programmieren, und
anstelle des DACs einen Audio Codec wie z.B. den PCM1753 zu verwenden.
Dieser hat ein Rekonstruktions und Oversampling Filter eingebaut, so
dass für 20kHz nur etwas mehr als 40kHz Samplerate notwendig sind.
Dafür sehen andere Kurvenformen außer Sinus dann halt bescheiden aus, da
die Bandbreite hart auf halbe Samplerate begrenzt ist.
Hier das Setzen des T-Flags. In einer ISR, welche was von LOOP1 updatet,
triggerst Du einen Timer0 Overflow-IRQ.
Die ISR dazu deaktiviert die IRQ und setzt T.
Hallo,
> @Benedikt K. und Jörg Wunsch:
Mir kommt es so vor, als hättet Ihr nur den Titel und den Link aber
meinen Post nicht zu Ende gelesen.
Mir geht es nicht darum, was Geniales kaputt zu machen, sondern um was
drum herum zu stricken. Wenn man die Schleife in C nicht machen kann -
OK, dann eben in Assembler lassen - aber ob die Schleife jetzt 9 Takte
oder 15 braucht, ist mir völlig Banane (ich denke, dass durch die
Taktzahl der Schleife die minimale Schrittweite und die maximale
Frequenz beeinflusst werden. Die Kwalität des Signals sollte davon
unbenommen sein).
> @Georg-johann L.:
Du scheinst verstanden zu haben, worauf ich hinaus will.
Danke Dir für Deine Mühen!
Ich will in der Tat einen ATtiny2313 einsetzen (da bereits vorhanden).
Einen schnelleren Kwartz einsetzen ist etwas, was später problemlos
gehen sollte.
Im Augenblick ist mir das Programm wichtiger.
Wie gesagt - wenn die Schleife statt 9 Takten eben 10 oder 15 braucht
ist das für mich ok.
Ich denke, eine Abfrage einer Abbruchbedingung ist auch in konstanten
Taktzahlen machbar. Sobald die Bedingung nicht mehr erfüllt ist, soll ja
keine Ausgabe mehr erfolgen. Also spielt es keine Geige, ob die
Bedingungsabfrage im 'Ja'-Zweig andere Taktzahlen braucht, als im
'Nein'-Zweig.
Und ob die Schleife jetzt in einer Unterfunktion oder direkt im
Hauptprogramm liegt spielt ja auch keine Rolle. Vor dem Funktionsaufruf
wird ja kein Signal ausgegeben, also wird auch keines verfälscht.
> Ach ja, Du musst bei den Tabellen Alignments zu 2**8 beachten.
Kann ich die Tabellen nicht in eine asm-Datei kopieren und dann zu
meinem C-Code dazu linken?
> Hier das Setzen des T-Flags. In einer ISR, welche was von LOOP1 updatet,> triggerst Du einen Timer0 Overflow-IRQ.
Verzeih mir, aber ich habe nicht wirklich verstanden, was Du mir
geschrieben hast.
- Wieso muss das T-Flag gesetzt werden?
- Wer braucht das?
- Timer werden im Original ja nicht verwendet - die wollte ich für das
Benutzerinterface verwenden.
Kann man nicht in der Assemblerschleife eine Bedingung abfragen, die ich
aus meinem C-Code heraus setzten könnte?
Wenn eine globale Variable zu viel Registerwechsel bedeuten würde,
könnte man dann nicht auch ein (unbenutztes) Pin abfragen?!?
Hallo,
mein Ansatz wäre: die main-Schleife unverändert irgendwie in C
reinbekommen (programmiere nicht in C, nur ASM.
Ansonsten sein Konzept beibehalten, also Tasten/Encoder an irgendwelche
Portpins, ein AVR mit Pin-Change-IRQ dazu nehmen.
Die komplette Behandlung in der PinChangeIRQ erledigen, so wie er es in
der RX-Komplett macht.
Wenn nicht sofort gestartet werden soll, gibt es ein Problem. Eine
zusätzliche Abfrage würde ich nicht in die main_loop einbauen, wäre mir
zuviel Umbau.
2 Ansätze: wenn nicht ausgegeben werden soll den R2R-Port einfach auf
Eingang setzen oder einen TriState-Treiber zwischen AVR und R2R und den
über ein Portpin schalten.
Ach so: TimerIRQ würde ich nicht benutzen, das Ding funktioniert nur,
weil die main_loop durch nichts gestört wird.
Entweder sie läuft völlig ungestört (Signal wird erzeugt) oder ganicht
(Bedienung).
Gruß aus Berlin
Michael
Michael U. wrote:
> mein Ansatz wäre: die main-Schleife unverändert irgendwie in C> reinbekommen (programmiere nicht in C, nur ASM.
Ja.
Das mit dem T-Bit ist völlig unnötig.
Die Variablen für die Addition als Registervariablen definieren, dann
kann sie ein Interrupt verändern.
> Wenn nicht sofort gestartet werden soll, gibt es ein Problem.
Warum?
Wenn 0 addiert wird, passiert am Ausgang garnichts.
> Ach so: TimerIRQ würde ich nicht benutzen, das Ding funktioniert nur,> weil die main_loop durch nichts gestört wird.
Timer ist aber wichtig fürs Tasten/Drehgeber entprellen.
Aber nicht durchlaufend, sondern durch Pin-Change für 4 Durchläufe
gestartet.
Peter
> Wenn 0 addiert wird, passiert am Ausgang garnichts.mirvordiestirnschlag Natürlich! Danke Peter, für die Glühbirne :)
> Die Variablen für die Addition als Registervariablen definieren, dann> kann sie ein Interrupt verändern.
Ok, das klingt zumindest plausibel. Muss ich mich allerdings erst
einlesen, in Assembler werden ja feste Register erwartet.
> Aber nicht durchlaufend, sondern durch Pin-Change für 4 Durchläufe> gestartet.
Das ist auch eine neue Idee. Vielleicht kann so die Bedienung völlig
ohne Einfluss von main auskommen. Mal grübeln gehen :)
Danke für die Denkanstöße!
Hallo,
Peter Dannegger wrote:
> Michael U. wrote:>> Wenn nicht sofort gestartet werden soll, gibt es ein Problem.>> Warum?> Wenn 0 addiert wird, passiert am Ausgang garnichts.
Stimmt. Soweit hatte ich beim Schreiben noch nicht gedacht...
>> Ach so: TimerIRQ würde ich nicht benutzen, das Ding funktioniert nur,>> weil die main_loop durch nichts gestört wird.>> Timer ist aber wichtig fürs Tasten/Drehgeber entprellen.> Aber nicht durchlaufend, sondern durch Pin-Change für 4 Durchläufe> gestartet.
Auch hier hast Du recht, hätte ich eben erst fertig denken und dann
schreiben sollen. ;) Meinte damit auch nur, keinen durchlaufenden
Timer-IRQ, der die main_loop stören könnte.
Ich sehe hier aber auch durchaus einen der Fälle, wo der Timer-IRQ
gegenüber einer BusyLoop vermutlich keinen Vorteil hat, weil der µC in
der Zwischenzeit ohnehin nichts sinnvolles machen kann.
PinChange -> entprellen, neue Werte setzen, Anzeige aktualisieren, raus.
Während dieses Ablaufs gibt es eigentlich nichts anderes, was er
erledigen könnte, egal, wie lange das dauert.
Gruß aus Berlin
Michael
Santiago wrote:
>> @Georg-johann L.:> Ich will in der Tat einen ATtiny2313 einsetzen (da bereits vorhanden).
Ich denke, der 2313 wird zu klein sein. Für C musst Du Overhead
einrechnen (zB Startup-Code, wegen C, größere Vectab, ...)
Schau mal was das Original an Programmcode brauch (ohne Tabellen). Nimm
das x2 und schau ob es immer noch passt. Wenn nicht, hast Du mit einem
2313 keinen Spaß.
Ein ATmega48 tut es, falls es nicht schon ne fertige Schaltung gibt,
denn der hat nen anderen Fußabdruck. Dann geht auch gleich ein ATmega168
und Du hasst mehr Platz für Tabellen. Zudem hat der 1k RAM, Du könntest
also sogar Tabellen dynamisch nachladen und ausm RAM lesen.
> Einen schnelleren Kwartz einsetzen ist etwas, was später problemlos> gehen sollte.> Im Augenblick ist mir das Programm wichtiger.>> Wie gesagt - wenn die Schleife statt 9 Takten eben 10 oder 15 braucht> ist das für mich ok.> Ich denke, eine Abfrage einer Abbruchbedingung ist auch in konstanten> Taktzahlen machbar. Sobald die Bedingung nicht mehr erfüllt ist, soll ja> keine Ausgabe mehr erfolgen. Also spielt es keine Geige, ob die> Bedingungsabfrage im 'Ja'-Zweig andere Taktzahlen braucht, als im> 'Nein'-Zweig.> Und ob die Schleife jetzt in einer Unterfunktion oder direkt im> Hauptprogramm liegt spielt ja auch keine Rolle. Vor dem Funktionsaufruf> wird ja kein Signal ausgegeben, also wird auch keines verfälscht.>>> Ach ja, Du musst bei den Tabellen Alignments zu 2**8 beachten.> Kann ich die Tabellen nicht in eine asm-Datei kopieren und dann zu> meinem C-Code dazu linken?
Alignments müsstest Du dennoch beachten. Besonders ärgerlich, weil das
unnötig Flash verschwendet -- es sei denn, die Tabellen liegen am
Flashende.
>> Hier das Setzen des T-Flags. In einer ISR, welche was von LOOP1 updatet,>> triggerst Du einen Timer0 Overflow-IRQ.>> Verzeih mir, aber ich habe nicht wirklich verstanden, was Du mir> geschrieben hast.> - Wieso muss das T-Flag gesetzt werden?> - Wer braucht das?> - Timer werden im Original ja nicht verwendet - die wollte ich für das> Benutzerinterface verwenden.
Das T-Flag wurde mit BRTC als Schleifenbedingung verwendet.
Über Timer0-IRQ wurde als Soft-IRQ missbraucht, um T zu setzen.
> Kann man nicht in der Assemblerschleife eine Bedingung abfragen, die ich> aus meinem C-Code heraus setzten könnte?> Wenn eine globale Variable zu viel Registerwechsel bedeuten würde,> könnte man dann nicht auch ein (unbenutztes) Pin abfragen?!?
Das ginge auch, globale Variable zum Austausch ist je net so dramatisch.
Falls ein Admin mitliest : Da fehlen Underscores im als C-Code
gekennzeichneten Teil, die irgendein Skript wegwischt.
Mit dem Code sind keine Alignments mehr erforderlich, er braucht
allerdings 17 Ticks. Mit einem NOP und einem 22.118400 MHz hättest Du
das gleiche Verhalten wie ursprünglich (inclusive Übertaktung ;-))
Mit einem missbrauchten I/O-Bit und alignten Tabellen sind 11 Ticks
machbar
> @Georg-johann L.:>> Santiago wrote:>> Ich will in der Tat einen ATtiny2313 einsetzen (da bereits vorhanden).> Ich denke, der 2313 wird zu klein sein. Für C musst Du Overhead> einrechnen (zB Startup-Code, wegen C, größere Vectab, ...)>> Schau mal was das Original an Programmcode brauch (ohne Tabellen). Nimm> das x2 und schau ob es immer noch passt. Wenn nicht, hast Du mit einem> 2313 keinen Spaß.>> Ein ATmega48 tut es, falls es nicht schon ne fertige Schaltung gibt,
Also wenn die Tina zu klein ist, hätte ich noch einen mega88.
Den Speicherbedarf zeigt ja das AVR-Studio an - sollte also
rauszubekommen sein.
Denke mal, dass ich es hinbekomme, die Schaltung anzupassen.
Ich bin ja noch blutiger Anfänger mit mcs und da verstehe ich getrixtend
Code noch nicht (wohl weil ich die Zusammenhänge mit der HW nicht
kenne).
> Das T-Flag wurde mit BRTC als Schleifenbedingung verwendet.> Über Timer0-IRQ wurde als Soft-IRQ missbraucht, um T zu setzen.
Ah jetzt ja - also auch getrixt?!?
> Das ginge auch, globale Variable zum Austausch ist je net so dramatisch.
Danke für das Beispiel. Übrigens:
Mir gefällt Deine Art, inline zu coden. Die abgesetzte Formatierung
bringt den Code noch besser zu Geltung. Sehr schön.
> Mit dem Code sind keine Alignments mehr erforderlich, er braucht> allerdings 17 Ticks. Mit einem NOP und einem 22.118400 MHz hättest Du> das gleiche Verhalten wie ursprünglich (inclusive Übertaktung ;-))
Habe ich das richtig verstanden, dass die Alignments nötig sind, damit
der (eine Teil des) Z-Pointer nicht geändert werden muss?
Ansonsten klingt das ja genial.
Bin mir jetzt nicht ganz sicher, ob ich es verstanden habe, aber in
Deinem Beispiel ist ja jetzt weniger Getrixe - wäre das dann nicht auch
mit C so machbar? Du verwendest ja schon 32bit Werte.
Mal hören was der Compiler dazu meint :)
Nochmals ein Dankeschön für Deine Unterstützung.
Georg-johann L. wrote:
> Falls ein Admin mitliest : Da fehlen Underscores im als C-Code> gekennzeichneten Teil, die irgendein Skript wegwischt.
Es gibt nur einen Admin hier: Andreas. Die andere Leute sind
Moderatoren und haben mit der Forumssoftware selbst nichts zu tun
(und zumindest ich habe Andreas auch noch nicht persönlich getroffen).
Mach am besten einen Thread in
http://www.mikrocontroller.net/forum/website
dafür auf.
Hallo,
muss nochmal nachfragen. Irgendwie bekomme ich es nicht auf die Reihe.
Ich wollte den Tip von Peter mit den benannten Registern umsetzen.
Dazu habe ich die Register wie folgt definiert:
1
registerunsignedcharacc1asm("r28");
2
registerunsignedcharacc2asm("r29");
3
registerunsignedcharacc3asm("r30");
4
registerunsignedcharinc1asm("r24");
5
registerunsignedcharinc2asm("r25");
6
registerunsignedcharinc3asm("r26");
Jetzt meint der Compiler (zu den Registern r28 und r29):
>register specified for 'acc1' isn't suitable for data type
Bedeutet der Fehler, dass die beiden Register tabu für Variablen sind,
oder was habe ich übergesehen?
zu den restlichen Registern meint er:
>call-clobbered register used for global register variable
Da letzteres eine Warnung ist, entnehme ich, dass die Register bei einem
Funktionsaufruf überschrieben würden?!?
Da ich wohl kaum ohne Funktionsaufrufe auskomme, müsste ich die Register
direkt vor der Aktivierung des Loops neu setzen?
Andere Frage :
- Könnte ich den Spieß auch umdrehen - und aus der Assemblerdatei das
ganze serielle Zeugs löschen und statt der Zeilen zwischen RESET: und
LOOP1: eine C-Funktion namens init() aufrufen?
- Zu der Assemblerdatei müsste ich dann eine C-Datei linken, in der
einige Interrupts stehen
- Geht das so zu kombinieren? - wenn ja, wie rufe ich eine C-Funktion
aus Assembler auf?
- Kann ich die Register der Hauptschleife via Inline-Assembler aus einer
ISR direkt setzen? Oder werden die Register für den Interruptaufruf
gesichert und nach Rückkehr aus der ISR wieder zurückgeschrieben?
Wenn's Dir leiber ist übersetze den ASM-Schnippsel, schau was avr-gcc
draus macht (.s-File) und bastel ne .S-Datei draus.
1
#include <avr/io.h>
2
3
.arch attiny2313
4
5
.text
6
.global dds
7
8
dds:
9
clr 18 ; acc
10
clr 19 ; acc
11
0:
12
; Index berechnen
13
add 18, 20 ; 1 ; acc, step
14
adc 19, 21 ; 1 ; acc, step
15
adc 26, 22 ; 1 ; idx, step
16
; Adresse berechnen
17
mov 30, 24 ; 1 ; pwert, base
18
mov 31, 25 ; 1 ; pwert, base
19
add 30, 26 ; 1 ; pwert, idx
20
adc 31, 1 ; 1 ; pwert
21
; Wert lesen & ausgeben
22
lpm 0, Z ; 3
23
out _SFR_IO_ADDR(PORTB), 0 ; 1
24
; update?
25
lds 0, update ; 2
26
sbrs 0, 0 ; 2
27
rjmp 0b ; 2 => 17 cycles
28
ret
Globale Register kannst Du verwenden wennn Du genau weisst, was Du
tust und genau, was gcc tut. Insbesondere, welches ABI er
implementiert. Weiterlesen zB hier:
Beitrag "Re: Ausführungszeit von C code, ist in funktionen langsamer?"
Und für das .S musst Du ebenfalls genau wissen was abgeht (Inline
Assembler ebenso). Die Parameterübergabe im Inline Asm sieht zwar übel
aus, aber so weiß gcc immerhin was abgeht.
Ein Hauptgrund, warum man einen Compiler verwendet, ist daß man sich
nicht mehr um die nervige Registerverwendung kümmern muss. Wenn man doch
was dran schraubt, sollte man nicht Features nutzen, die globales
Verhalten ändern, sondern den Hack auf wenige chirurgische Eingriffe
beschränken.
Hallo,
also das mit Assembler und C kombinieren habbich ned hinbekommen :(
Irgendwie kennt C die Bezeichner von Assembler nich un wennich se in C
als extern deklariere, bekomme ich Linker-Fehler.
Dann bleibe ich lieber bei Inline-Assembler - das Zusammenspiel habe ich
(halbwegs) verstanden.
Jetzt hab ich das Ganze mal nur in C gemacht und die
Benutzerschnittstelle ist auch soweit, dass ich mich den Bits wieder
nächern kann (Prost). Habe jetzt für jede Wellenform eine eigene
Funktion, sodass ich mit der Schleifenzeit in die Nähe des Originals
kommen könnte.
Deshalb würde ich gerne nochmal auf die Sache mit dem T-Flag zurück
kommen. Das habe ich noch nicht wirklich verstanden.
In den obigen Beispielen finde ich zwar wie man es benutzt, aber wofür
ist das T-Flag zuständig? Wie kann ich damit Konflikte
verursachen/vermeiden?
Im Handbuch zum mega88 habe ich nix darüber gefunden und die Erklärung
im "Instruction manual" ist so dürftig, dass sich mir der Sinn, bzw. die
normale Verwendung nicht erschließt.
Falls mir jemand hier etwas Licht ins Dunkel bringen könnte, würde mir
das sehr freuen tun :)
Ich habe auch mal ausprobiert, die Abbruchbedingung als Registervariable
anzulegen - das hat mal so richtig überhaupt nix gebracht. Der Compiler
hat die Variable in ein anderes Register umkopiert und dann zusätzliche
Befehle eingefügt, sodass sich kein Zeitgewinn ergab.
Insgesamt sind es inzwischen 3k geworden - passt also nimmer zur Tina.
Aber mit einem mega48 könnte es locker reichen.
Santiago wrote:
> Hallo,>> also das mit Assembler und C kombinieren habbich ned hinbekommen :(> Irgendwie kennt C die Bezeichner von Assembler nich un wennich se in C> als extern deklariere, bekomme ich Linker-Fehler.
1) Normale H-header schreiben
2) C-Quelle compilieren uns assemblieren
3) Im .S die globalen Objekte (zB Funktionen) mit .global kennzeichnen
4) Im .S die C-ABI beachten (Parameterübergabe, Registerverwendung)
5) .S assemblieren mit -x assembler-with-cpp
6) Objekte linken
> Habe jetzt für jede Wellenform eine eigene Funktion
hmmm grübel
> Deshalb würde ich gerne nochmal auf die Sache mit dem T-Flag zurück> kommen. Das habe ich noch nicht wirklich verstanden.> In den obigen Beispielen finde ich zwar wie man es benutzt, aber wofür> ist das T-Flag zuständig? Wie kann ich damit Konflikte> verursachen/vermeiden?
Das T-Flag ist ein User-Flag. Es hat keine Einfluss auf die Maschine und
wir auch nicht durch Arithmetik gesetz/gelöscht. Man kann es von Hand
setzen, löschen, abhängig davon springen und es in ein Registerbit hin-
nud herschieben.
> Ich habe auch mal ausprobiert, die Abbruchbedingung als Registervariable> anzulegen - das hat mal so richtig überhaupt nix gebracht. Der Compiler> hat die Variable in ein anderes Register umkopiert und dann zusätzliche> Befehle eingefügt, sodass sich kein Zeitgewinn ergab.
Jau. GCC ist eigensinnig.
Das T-Flag könnte man auch anders setzen:
Nehmen wir mal an, es wird der USART_RX_vect benutzt (USART receive des
ATtiny2313)
Dazu schreiben wir eine Funktion usart_rx, die als ISR fungiert:
Damit bekommt usart_rx den üblichen ISR-Prolog und Epilog und taugt als
ISR. Allerdings gibt es keinen Eintrag in die VecTab, weil die ISR
keinen Standard-ISR-Name hat (man bekommt dafür ne Warnung von avr-gcc).
Zum Einsprung implementieren wir die Standard-ISR als naked, also ohne
Prolog und Epilog:
1
void__attribute__((naked))
2
USART_RX_vect(void)
3
{
4
__asmvolatile("set""\n\t"
5
"rjmp usart_rx");
6
}
Damit erhalten wir den Eintrag in die VecTab und den Einsprungpunkt für
unserer ISR. Nach Setzen des T-Flags geht's weiter mit usart_rx und
gewohnter Abhandlung der IRQ.
Damit ist die BRTC-Version der DDS-Schleife mit 9 Ticks verwendbar.
Hack ist auch diese Lösung...aber die Hacks werden langsam besser :-)
>> also das mit Assembler und C kombinieren habbich ned hinbekommen :(>> Irgendwie kennt C die Bezeichner von Assembler nich un wennich se in C>> als extern deklariere, bekomme ich Linker-Fehler.>> 1) Normale H-header schreiben> 2) C-Quelle compilieren uns assemblieren> 3) Im .S die globalen Objekte (zB Funktionen) mit .global kennzeichnen> 4) Im .S die C-ABI beachten (Parameterübergabe, Registerverwendung)> 5) .S assemblieren mit -x assembler-with-cpp> 6) Objekte linken
Danke für das Rezept. Schätze ich hatte das .global
vergessen/übergesehen.
Werde es also nomml probieren.
Die "C-ABI" ist noch ein Fremdwort. Hätte mir jemand vielleicht einen
Link, sodass ich mich zu dem Thema etwas schlauer machen könnte?
>> Habe jetzt für jede Wellenform eine eigene Funktion>> hmmm grübel
Na ich wollte die Varianten aus der Schleife rausbekommen - wenn man so
will, also die Tabelle hart codieren. Habbich dann zuerst mit Gotos und
verschiedenen Schleifen probiert - und das hat mir garnicht gefallen.
Dann bin ich draufgekommen, dass ein Funktionsaufruf ja genau das Goto
auch macht, nur eben C-like. Sieht jetzt viel besser aus und ich nähere
mich so dem Original (aba nur da wo es nötig is).
> Das T-Flag könnte man auch anders setzen:
Hm, das scheint mir ein sehr interessantes Verfahren zu sein.
Muss ich aber erst etwas setzen lassen (geistiger Wiederkäuer :D ).
Wird das T-Flag beim ISR-pro- und -Epilog berücksichtigt?
Im Moment ist es so, dass ich das Benutzerinterface abschalte, bevor der
Funktionsgenerator loslegt. Per Pin-Change-Interupt wird die
Benutzerschnittstelle wieder eingeschaltet.
Habe die Schaltung nocht nicht konzipiert - deshalb könnte es sein, dass
ich mehrerere Pin-Change-Interrupts verwenden müsste.
Das Prinzip mit dem nackichen Interrupt kann ich auch mehrfach
einsetzen?
Ach ja, ich hatte es bislang so verstanden, dass SIGNAL kwasi als
deprecated eingestuft ist und deshalb verwende ich bei den ISRs eben das
ISR-Makro.
Schätze bei dem Häck wäre jetzt doch SIGNAL einzusetzen?
Und nur damit ich es richtig verstanden habe:
Der Name "usart_rx" ist ein frei erfundener Name und könnte auch
"hebbelbebbel" sein?
Wenn ich dann per Inline-Assembler die Funktion anspringen will, woher
kennt der Assembler den Namen "usart_rx"?
> Hack ist auch diese Lösung...aber die Hacks werden langsam besser :-)
Ja, ich lebe langsam wieder auf und freue mich, dass der Dickkopf sich
manchmal doch auszahlt (un nich immer Leute wie Benedikt Recht behalten)
;)
Santiago wrote:
> Die "C-ABI" ist noch ein Fremdwort. Hätte mir jemand vielleicht einen> Link, sodass ich mich zu dem Thema etwas schlauer machen könnte?
"Application Binary Interface"
Erste und einzig Sichere Referen sind die Compiler-Quellen
$GCC_SOURCE/gcc/config/avr/avr.*
Ein bissl steht bestimmt auch im avr-gcc Tutorial hier und in
http://www.roboternetz.de/wissen/index.php/Avr-gcc/Interna#Registerverwendung>> Das T-Flag könnte man auch anders setzen:>> Hm, das scheint mir ein sehr interessantes Verfahren zu sein.> Muss ich aber erst etwas setzen lassen (geistiger Wiederkäuer :D ).> Wird das T-Flag beim ISR-pro- und -Epilog berücksichtigt?
Das T-Flag gehört zum SREG (PSW) und wird dementsprechend
gesichert/restauriert.
> Das Prinzip mit dem nackichen Interrupt kann ich auch mehrfach> einsetzen?
Ja.
> Ach ja, ich hatte es bislang so verstanden, dass SIGNAL kwasi als> deprecated eingestuft ist und deshalb verwende ich bei den ISRs eben das> ISR-Makro.> Schätze bei dem Häck wäre jetzt doch SIGNAL einzusetzen?
Ich nehm das gute, alte SIGNAL oder INTERRUPT mit dem guten, alten
avr-gcc 3.4.6. Zu GCC 4 kann ich keine Tipps geben. Schau eben man in
die Compiler-Header und was cpp und gcc draus basteln mit (-save-temps).
> Und nur damit ich es richtig verstanden habe:> Der Name "usart_rx" ist ein frei erfundener Name und könnte auch> "hebbelbebbel" sein?
Ja.
> Wenn ich dann per Inline-Assembler die Funktion anspringen will, woher> kennt der Assembler den Namen "usart_rx"?
Der Assembler braucht keine Namen zu kennen, er fügt für Symbole FixUps
ein, die der Linker/Locator auflöst und ausfüllt.
>> Hack ist auch diese Lösung...aber die Hacks werden langsam besser :-)>> Ja, ich lebe langsam wieder auf und freue mich, dass der Dickkopf sich> manchmal doch auszahlt (un nich immer Leute wie Benedikt Recht behalten)> ;)
Benedikt hat Recht behalten. Du schriebst was von "Umschreiben nach C"
und er sagte "Vergiss es". Keine der bisher erwägten Varianten des
asm-Schnippsels mit ähnlichem Zeitverhalten ist in C.
Georg-johann Lay wrote:
> "Application Binary Interface">> Erste und einzig Sichere Referen sind die Compiler-Quellen> $GCC_SOURCE/gcc/config/avr/avr.*
Dieser FAQ-Eintrag:
http://www.nongnu.org/avr-libc/user-manual/FAQ.html#faq_reg_usage
erhebt für sich den Anspruch, das ABI zu dokumentieren.
> Ich nehm das gute, alte SIGNAL oder INTERRUPT mit dem guten, alten> avr-gcc 3.4.6. Zu GCC 4 kann ich keine Tipps geben.
Das hat nichts mit der GCC-Version zu tun, wohl aber mit der
avr-libc-Version. Ist generell keine gute Idee, neu hinzu kommenden
Nutzern die Verwendung von als `deprecated' markierten Interfaces
kommentarlos nahe zu legen. Ja, ISR ist einfach nur ein neuer
(und hoffentlich/offensichtlich zu weniger Missverständnissen
führender) Name für das alte SIGNAL bzw. INTERRUPT (letzteres mit
zusätzlichen Argumenten für ISR).
Ob GCC 3.4.6 nun wirklich ,,gut und alt'' ist, da stimme ich dir für
den zweiten Teil uneingeschränkt zu, für den ersten würde ich durchaus
Widerspruch anmelden. ;-)
Hallo Georg-johann,
herzlichen Dank für Deine Unterstützung und Geduld!
> Ein bissl steht bestimmt auch im avr-gcc Tutorial hier und in>> http://www.roboternetz.de/wissen/index.php/Avr-gcc...
Danke für den Link. Die Seite kannte ich noch nicht. Scheint sogar
Dau-proof zu sein (schließlich verstehe ich etwas mehr als vorher) ;)
Werde mich heute daran machen, die Tips mal umzusetzen.
>>> Hack ist auch diese Lösung...aber die Hacks werden langsam besser :-)>>>> Ja, ich lebe langsam wieder auf und freue mich, dass der Dickkopf sich>> manchmal doch auszahlt (un nich immer Leute wie Benedikt Recht behalten)>> ;)>> Benedikt hat Recht behalten. Du schriebst was von "Umschreiben nach C"> und er sagte "Vergiss es". Keine der bisher erwägten Varianten des> asm-Schnippsels mit ähnlichem Zeitverhalten ist in C.
Dank meiner grenzenlosen Unkenntnis der Materie habe ich es doch
geschafft, mein Anliegen falsch zu formulieren. Auch wenn ich schon im
ersten Post schrieb, dass es mir darum geht, dass Verhalten und die
Benutzerschnittstelle zu ändern.
Da ich nur C konnte, erschien mir das der einzig gangbare Weg. Dank
meines Dickkopfes und Eurer Unterstützung habe ich jetzt soviel dazu
gelernt, dass es auch einen anderen Weg zum gleichen Ziel gibt.
Auch wenn Benedikt faktisch recht behalten hat, dass man das
Hauptprogramm nicht umschreiben kann - ich halte nix davon, Anfänger
erstmal abzuwatschen, ohne sich wirklich mit dessen Problem zu
beschäftigen. Schätze mal, ich werde nicht der Einzige sein, der aus
Unwissenheit die Frage nach Unterstützung falsch stellt?!?
Wie heißt es doch so schön: Wahrheiten sind die Illusionen, von denen
wir vergessen haben, dass sie welche sind.
Hallo Jörg,
>> Erste und einzig Sichere Referen sind die Compiler-Quellen>> $GCC_SOURCE/gcc/config/avr/avr.*>> Dieser FAQ-Eintrag:>> http://www.nongnu.org/avr-libc/user-manual/FAQ.htm...>> erhebt für sich den Anspruch, das ABI zu dokumentieren.
Schon komisch - das Manual der avr-libc liegt bei mir auf Platte und ist
immer offen, wenn ich an der Firmware schraube.
Die FAX habe ich auch schon öfters gelesen, nur wenn ich hier von C-ABI
lese, konnte ich das nicht mit der entsprechenden Frage aus dem FAQ
zusammen bringen.
Jetzt, da ich weiß, dass die Frage 13 sich mit dem C-ABI beschäftigt,
bekomme ich die Sachen auch wieder zusammen.
Oftmals sind es Kleinigkeiten, die ein Scheitern verursachen :)
Danke für die Aufklärung.
Jörg Wunsch wrote:
>> Ich nehm das gute, alte SIGNAL oder INTERRUPT mit dem guten, alten>> avr-gcc 3.4.6. Zu GCC 4 kann ich keine Tipps geben.>> Das hat nichts mit der GCC-Version zu tun, wohl aber mit der> avr-libc-Version. Ist generell keine gute Idee, neu hinzu kommenden> Nutzern die Verwendung von als `deprecated' markierten Interfaces> kommentarlos nahe zu legen. Ja, ISR ist einfach nur ein neuer> (und hoffentlich/offensichtlich zu weniger Missverständnissen> führender) Name für das alte SIGNAL bzw. INTERRUPT (letzteres mit> zusätzlichen Argumenten für ISR).>> Ob GCC 3.4.6 nun wirklich ,,gut und alt'' ist, da stimme ich dir für> den zweiten Teil uneingeschränkt zu, für den ersten würde ich durchaus> Widerspruch anmelden. ;-)
Ji Jörg,
hmmm, ich hatte den avr-gcc 4.x mal angetestet, fand es aber nicht sehr
verlockend darauf umzusteigen, weil er wohl noch was wacklig ist auf den
Beinen. Vielleicht hast Du ja n paar Tipps was ich falsch mache? Näheres
dazu und weil die Frage hier OT ist
F'UP
Beitrag "avr-gcc: 3.4.6 contra 4.3.0"
Georg-johann Lay wrote:
> hmmm, ich hatte den avr-gcc 4.x mal angetestet, fand es aber nicht sehr> verlockend darauf umzusteigen, weil er wohl noch was wacklig ist auf den> Beinen. [...]
4.3.0 ist ja nun wirklich kein Vergleich. ;-)
Aber 4.2.x ist mindestens genauso stabil wie die alte 3.xer Linie.
4.3.0 hat Eric nur deshalb so schnell unter die Massen bringen müssen,
weil sein Brötchengeber natürlich Interesse daran hatte, dass WinAVR
die neuen Xmega-Prozessoren möglichst schnell nach deren offizieller
Ankündigung unterstützen können sollte. Da Xmega teilweise gravierende
Änderungen in der CPU-Architektur hat, die sich im Compiler nieder-
schlagen, war dies nicht mehr mit der vorhandenen man power auf 4.2.x
zurück portierbar. Andererseits kann Erics Installer leider keine
optionalen Komponenten, d. h. er konnte damit den Nutzern nicht
anbieten, in einem WinAVR-Release alternativ GCC 4.2.x (stabil, aber
kein Xmega) oder 4.3.0 (taufrisch + Xmega) zu installieren.
Wie schon öfter geschrieben, wenn jemand mit Windows-Installern und
deren Konfiguration bewandert ist, dann wäre das eine Stelle, an der
er Eric gut unterstützen kann.
Die aufgekommenen Probleme mit 4.3.0, auch im Bereich der Optimierung,
sind übrigens der Grund, warum ich die FreeBSD-Ports noch nicht auf
diese Version angepasst habe. Wenn, dann werde ich aber dort auf
jeden Fall erstmal zweigleisig fahren, d. h. der Nutzer kann sich
wählen, ob er 4.2.x oder 4.3.x nimmt.
Jörg Wunsch wrote:
>> Wie schon öfter geschrieben, wenn jemand mit Windows-Installern und> deren Konfiguration bewandert ist, dann wäre das eine Stelle, an der> er Eric gut unterstützen kann.>
Ähhh, das verstehe ich nicht. Er hat doch in dem verwendeten Installer
die Möglichkeit, Optionen anzubieten ([ ] Notepad z.B.). Warum kann er
da nicht genauso die Option einbauen, welchen AVR-GCC man verwenden
möchte? Wenn das AVR-GCC-Paket so getrennt werden kann, dann ist es doch
ein leichtes diese 2 Optionen dort zu integrieren. Oder hat ihm jemand
anderes dieses jetzigen Installer so hingebogen, dass er ihn "stumpf"
nur verwendet, ihn aber nicht ändern kann? -kopfkratz-
Hallo,
habe jetzt die Tabellen und Funktionen nochmal etwas umstrukturiert,
sodass ich jetzt "nur" in einer Funktion inline assembler haben muss.
Leider bekomme ich eine Fehlermeldung.
Ich vermute mal, dass es mit dem Zeiger (pWaveTable) zusammenhängt,
jedoch komme ich auch mit den Inline-Assembler-Tutorials nicht weiter.
Dabei dachte ich schon, ich hätte die Parametergeschichte verstanden :(
1
voidgenSine(){genWave(sine);}
2
voidgenSawtooth(){genWave(sawtooth);}
3
voidgenTriangle(){genWave(triangle);}
4
voidgenSquare(){genWave(square);}
5
6
voidgenWave(uint8_t*pWaveTable){
7
__asmvolatile(
8
// setup wavetable address
9
"ldi r31, hi8([pWaveTable]) ; setup Z pointer hi""\n\t"
10
"ldi r30, lo8([pWaveTable]) ; setup Z pointer lo""\n\t"
11
12
// clear accumulator
13
"ldi r29, 0x00 ; clear accumulator""\n\t"
14
"ldi r28, 0x00 ; clear accumulator""\n\t"
15
16
"set ; work until T-flag is cleared""\n\t"
17
18
"0:""\n\t"
19
"add r28, %A[step] ; 1""\n\t"
20
"adc r29, %B[step] ; 1""\n\t"
21
"adc r30, %C[step] ; 1""\n\t"
22
"lpm ; 3""\n\t"
23
"out %[port], r0 ; 1""\n\t"
24
"brts 0b ; 2 => 9 cycles"
25
26
/* out */:
27
/* in */:[step]"r"(work.step),[pWaveTable]"z"(pWaveTable),[port]"n"(_SFR_IO_ADDR(PORT_DAC))
28
/* clobber */:"r28","r29","r30","r31"
29
);
30
}
Da der Zähler im Rest des Proggies ohne Bedeutung ist, habe ich keine
Ladebefehle oder ähnliches.
So gesehen habe ich keinerlei Ausgabe, deshalb ist der entsprechende
Bereich leer.
Die Fehlermeldung lautet:
r28/r29 würde ich auch nicht hart codieren, sondern dem Compiler
die Wahl überlassen. Es kann gut sein, dass er für dieses
Zeigerregisterpaar eine bessere Verwendung hätte, die durch die
Clobber-Deklaration unnütz behindert wird. Für die eigentliche
Berechnung darf es ja jedes beliebige Register >= 16 sein.
Hallo,
dank Euch, für Eure Unterstützung.
> Wenn du ihm r30/r31 als clobbers deklarierst, wie soll er dann in> der Lage sein, für sich noch einen Z-Pointer zu benutzen?
Ok, da haben wohl meine Englisch-Kenntnisse nicht ausgereicht.
Ich hatte es so verstanden, dass man die Register bei clobber angibt,
die durch den Assemblercode verändert werden.
> Zudem: Z wird bereits mit pWaveTable vorgeladen. Die ersten beiden> Instruktionen sind überflüssig (und zudem nicht korrekt).
Ok, habe sie auskommentiert und die clobbers gelöscht (Fehlermeldungen
sind wech).
Nur der Vollständigkeit halber: wie müsste ich denn einen C-pointer
richtig übergeben/übernehmen?
> r28/r29 würde ich auch nicht hart codieren ...> Für die eigentliche> Berechnung darf es ja jedes beliebige Register >= 16 sein.
In Bezug auf die Berechnung stimmt das sicher. Nur hatte ich es so
verstanden, das lpm die Register r28, r29 und r30 brauchen würde.
Santiago wrote:
> Hallo,>> Ich hatte es so verstanden, dass man die Register bei clobber angibt,> die durch den Assemblercode verändert werden.
Nein. Man muss dem Compiler mitteien, welche Register verändert werden.
Das kann über clobbers oder über Constraints wie "=r", "+r" oder "=&r"
geschehen. R1 muss man von Hand wieder herstellen falls verändert, da es
nicht vom Compiler verwaltet wird.
> Nur der Vollständigkeit halber: wie müsste ich denn einen C-pointer> richtig übergeben/übernehmen?
Oben gibt's genügend Beispiel-Schnippsel wie's geht.
> In Bezug auf die Berechnung stimmt das sicher. Nur hatte ich es so> verstanden, das lpm die Register r28, r29 und r30 brauchen würde.
Das Handbuch verrät Dir zu LPM, daß es r30 und r31 (z-Reg) als Adresse
verwendet.
>> In Bezug auf die Berechnung stimmt das sicher. Nur hatte ich es so>> verstanden, das lpm die Register r28, r29 und r30 brauchen würde.>> Das Handbuch verrät Dir zu LPM, daß es r30 und r31 (z-Reg) als Adresse> verwendet.
Stimmt. Ich bin verwirrt :{
... dachte ich hätte das Programm verstanden.
Das Assembler-Listing (*.lss) verwirrt mich noch mehr:
tab ist eine Tabelle aus C heraus angelegt, sine die Tabelle über
inline-Assembler angelegt. Die Hex-Werte stimmen wohl, aber irgendwie
scheint die Tabelle für den Compiler Programm statt Daten zu sein?!?
Was habbich da wieder verbockt?
Nicht für den Compiler, aber für den Disassembler: sie ist als zu
.text gehörig deklariert, damit wird sie disassembliert. Richtig
wäre .progmem.data (oder so ähnlich).
Ist aber ein reiner Schönheitsfehler.
> Nicht für den Compiler, aber für den Disassembler: sie ist als zu> .text gehörig deklariert...
Ich habe es wie hier Beitrag "Re: Würde gerne Assembler in C umschreiben, aber ich blick's net"
gemacht. Aber ok - wenn nur Schönheitsfehler, solls mir recht sein :)
> r28/r29 würde ich auch nicht hart codieren, sondern dem Compiler> die Wahl überlassen.
Das habe ich jetzt so umgesetzt, dass ich eine 2Byte-Variable definiert
und die als Parameter übergeben habe.
Kann man das Gleiche auch ohne lokale Variable erreichen?
Santiago wrote:
> Das habe ich jetzt so umgesetzt, dass ich eine 2Byte-Variable definiert> und die als Parameter übergeben habe.
Ja, so hätte ich das auch gemacht, wobei du natürlich auch zwei
einzelne Variablen hättest nehmen können. Es gibt ja keinen
richtigen Grund, dass beide Register hinteinander liegen müssen.
> Kann man das Gleiche auch ohne lokale Variable erreichen?
Nein, da du dem Compiler ja die Auswahl überlassen willst (nur dann
kann er das alles in sein Optimierungskonzept einbeziehen), braucht
er ein eigenes Objekt, an dem er das festmachen kann. Das ist also
völlig OK so. Diese lokalen Variablen sind ja nur Platzhalter für
die Register, die der Compiler dafür einsetzen muss und benötigen
sonst keine anderweitigen Ressourcen.
> Diese lokalen Variablen sind ja nur Platzhalter für> die Register, die der Compiler dafür einsetzen muss und benötigen> sonst keine anderweitigen Ressourcen.
Wunderbar. Gut zu wissen. Danke für die Aufklärung.
Bei dem nackichen ISR bekomme ich einen internen Compiler-Fehler (gleich
nach der prophezeiten Warnung).
Habe sowohl ISR, als auch SIGNAL probiert - kein Unterschied.
Ich hänge mal das ganze Proggy an.
Vielleicht mag ja jemand mal drüber schaun.
Für Kritik und/oder Anregung bin ich immer zu haben :)
Ich habe mir mal ein Makefile dazu erfinden lassen von Mfile, da
bekomme ich:
1
In file included from main.c:6:
2
DDS.h:27: warning: function declaration isn't a prototype
3
DDS.h:28: warning: function declaration isn't a prototype
4
DDS.h:29: warning: function declaration isn't a prototype
5
DDS.h:30: warning: function declaration isn't a prototype
6
In file included from main.c:7:
7
Data.h:32: warning: function declaration isn't a prototype
8
Data.h:33: warning: function declaration isn't a prototype
9
In file included from main.c:8:
10
Counter.h:8: warning: function declaration isn't a prototype
11
In file included from main.c:9:
12
UI.h:42: warning: function declaration isn't a prototype
13
In file included from main.c:10:
14
SPI.h:15: warning: function declaration isn't a prototype
15
main.c:13: warning: function declaration isn't a prototype
16
main.c:19: warning: function declaration isn't a prototype
17
main.c:19: warning: return type of 'main' is not 'int'
18
main.c: In function 'main':
19
main.c:26: error: 'run' undeclared (first use in this function)
20
main.c:26: error: (Each undeclared identifier is reported only once
21
main.c:26: error: for each function it appears in.)
22
main.c: At top level:
23
main.c:81: warning: function declaration isn't a prototype
24
make: *** [main.o] Error 1
Ziemlich viele Warnungen, und insbesondere scheint ja noch was zu
fehlen.
Bezeichner, die mit einem Unterstrich anfangen, sind übrigens tabu
für dich als Anwender, vermeide also besser sowas:
1
#ifndef __DDS_DATA_H__
2
#define __DDS_DATA_H__
3
...
4
#endif
Schreibe stattdessen:
1
#ifndef DDS_H
2
#define DDS_H
3
...
4
#endif
(Es ist mehr als sinnvoll, dass der Name dieses Makros mit dem Namen
der Datei übereinstimmt.)
Sowas solltest du auch vermeiden:
1
#ifndef __PGMSPACE_H_
2
#include<avr/pgmspace.h>
3
#endif
Erstens hast du dich sowieso vertan (PGMSPACE_H hat vorn und hinten
je zwei Unterstriche), zweitens sind ja eben diese Makros dafür da,
dass nichts passiert, wenn man eine Headerdatei mehr als einmal
includet.
> Ich habe mir mal ein Makefile dazu erfinden lassen von Mfile, da> bekomme ich:
Hm - hast Du da einen besonderen "strict" mode eingeschaltet?
Ich wüßte nicht, was da falsch sein soll.
Dass noch eine Zuweisung auf "run" in main vorhanden ist - gut, soweit
kam der Compiler bei mir nicht. Kann ersatzlos gestrichen werden (ist ja
jetzt das T-Flag)
> Sowas solltest du auch vermeiden:>> #ifndef __PGMSPACE_H_> #include <avr/pgmspace.h>> #endif>> Erstens hast du dich sowieso vertan (PGMSPACE_H hat vorn und hinten> je zwei Unterstriche), zweitens sind ja eben diese Makros dafür da,
Sorry, aber da bin ich anderer Meinung.
Mein Gelerntes mag ja bei kleinen Projekten überflüssig sein, dennoch
denke ich, dass es heute noch genauso gültig ist, wie vor 10 Jahren. Ich
habe es jedenfalls von Lakos: "large scale C++ software design" gelernt
und seither ist es in den Fingern drin. Tut ja auch niemand weh.
> Bezeichner, die mit einem Unterstrich anfangen, sind übrigens tabu> für dich als Anwender, vermeide also besser sowas:
Hm, dass ich Anfänger bei Mikrokontrollern und Hardware bin, bedeutet
nicht, dass ich auch Anfänger in C wäre. Auch wenn ich sicher mehr C++
als C codiert habe.
... und Nein! ich habe mich nicht verschrieben.
Ich habe extra in den Dateien nachgeschaut. Dort sind 2 Unterstriche am
Anfang und einer am Ende.
Zumindest in der Version die ich habe (WinAVR vom 12.01.07)
Ok, unsere Posts haben sich wohl überschnitten.
Ich verwende WinAVR im AVR-Studio (weil ich dort gleich debuggen kann).
Unter linux habe ich das Ganze noch nicht probiert.
Santiago wrote:
> Hm - hast Du da einen besonderen "strict" mode eingeschaltet?
-Wall -Wextra
> Ich wüßte nicht, was da falsch sein soll.
Eine Deklaration im Stil der 1980er Jahre.
> Sorry, aber da bin ich anderer Meinung.
Warum?
Du testet ein nicht dokumentiertes Feature. Der Name des
Makros _PGMSPACE_H_ ist ,,Eigentum'' der avr-libc und kann
in dieser kommentarlos im nächsten Release ganz anders heißen,
da er 1.) zum implementation name space gehört und 2.) von der
Dokumentation nicht beschrieben ist, also keinerlei offizielles
Interface darstellt.
> Mein Gelerntes mag ja bei kleinen Projekten überflüssig sein, dennoch> denke ich, dass es heute noch genauso gültig ist, wie vor 10 Jahren.
Es war vor 10 Jahren schon genauso falsch, aus gleichen Gründen
wie heute. ;-)
Wenn du partout denkst, dass die Tests in den Bibliotheksheadern
unzureichend für deine Belange sind, dann müsstest du dir deinen
eigenen Test drumrum schreiben:
1
#ifndef PGMSPACE_H
2
# include <avr/pgmspace.h>
3
#define PGMSPACE_H
4
#endif
Ich sehe aber keinen Gewinn. In einem Zeitalter, in dem selbst
Windows einen brauchbaren filesystem cache hat (anders als MS-DOS
seinerzeit) kostet das zusätzliche Öffnen der Datei praktisch
nichts, und hernach würde sie der Präprozessor ohnehin komplett
überspringen.
> Ich habe extra in den Dateien nachgeschaut. Dort sind 2 Unterstriche am> Anfang und einer am Ende.
Ja, da gehören sie auch hin. avr-libc versteht sich selbst als
Teil des application name space, schließlich ist sie die Standard-
bibliothek zum AVR-GCC. Sie muss sich natürlich noch mit dem GCC
,,absprechen'' (in irgendeiner Form), damit die reservierten
Bezeichner zwischen Bibliothek und Compiler keine Konflikte
verursachen, aber das ist für den Endanwender nicht von Belang.
Du bist Endanwender, damit solltest du bitte keine reservierten
Bezeichner in deinem Code benutzen, es sei denn, die Dokumentation
beschreibt dies in irgendeiner Form (wie z. B. _BV() oder
__STDIO_FDEVOPEN_COMPAT_12). In letztgenanntem Falle fühlt sich
dann aber avr-libc auch verpflichtet, dass diese Bezeichner nicht
im nächsten Release gleich mal wieder ihren Namen wechseln oder
eine ganz andere Bedeutung bekommen.
Jörg Wunsch wrote:
>> Ich habe extra in den Dateien nachgeschaut. Dort sind 2 Unterstriche am>> Anfang und einer am Ende.>> Ja, da gehören sie auch hin.
**plong** Jetzt sehe ich, was du meinst. Das ist ein Bug, und damit
hast du übrigens schon einen Grund, warum das möglicherweise in
einer späteren avr-libc-Version geändert werden könnte: wir könnten
uns entschließen, die Namen der idempotency-Makros innerhalb der
gesamten Bibliothek zu vereinheitlichen. Im Moment ist da leider
recht viel Wildwuchs, wie ich gerade sehe. :-(
> Du testet ein nicht dokumentiertes Feature. Der Name des> Makros PGMSPACE_H ist ,,Eigentum'' der avr-libc und kann> in dieser kommentarlos im nächsten Release ganz anders heißen,> da er 1.) zum implementation name space gehört und 2.) von der> Dokumentation nicht beschrieben ist, also keinerlei offizielles> Interface darstellt.>>> Mein Gelerntes mag ja bei kleinen Projekten überflüssig sein, dennoch>> denke ich, dass es heute noch genauso gültig ist, wie vor 10 Jahren.>> Es war vor 10 Jahren schon genauso falsch, aus gleichen Gründen> wie heute. ;-)
Mit dem nicht dokumentierten Viehtscher hast Du natürlich recht.
Deshalb habe ich mir auch angewöhnt, in den Dateien nach zu schauen.
Zum Punkt Richtig/Falsch nur soviel:
Wenn Du mal wieder in einem gut sortierten Buchladen bist, nimm Dir doch
einfach oben genanntes Buch zur Hand und schau mal "Figure 2-9" und
"Figure 2-10". Sind imho beeindruckende Fakten.
Das Buch wurde meines Wissens erst Mitte der 90er geschrieben - also nix
mit DOS und Co :)
Buchladen habe ich gerade nicht am Weg liegen. Wenn ich nicht
gerade zu faul wäre, würde ich in unserem aktuellen Mammutprojekt
mal die entsprechenden ifdefs reinbauen. Obwohl da ziemlich viel
Spaghetti mit den includes drin ist (dessen Aufräumen tatsächlich
sehr viel wichtiger wäre als das Anbringen weiterer ifdefs, aber
selbst dafür haben wir keine rechte Zeit), sollte es mich arg
wundern, wenn mehr als 5 % Geschwindigkeitszuwachs beim Compilieren
davon zu erhalten wären. Das liegt unter der Wahrnehmbarkeitsgrenze.
Zurück zum Subject: vielleicht legst du ja dein Makefile nochmal
mit hin. Wo genau bekommst du den internal compiler error (ICE)?
Welche Compilerversion (avr-gcc -v)?
Warum willst Du denn unbedingt diesen Test implementieren, wenn er in
der Header-Datei auch drin ist? Das verstehe ich nicht. Hast Du Angst,
dass der schon implementierte Test schlechter funktioniert oder manchmal
nicht?
Und das zitierte Buch werde ich mir nicht extra kaufen :-)
Leutz - ich will jetzt hier nicht als Bekehrer auftreten. Ich habe meine
Überzeugung und andere haben andere Überzeugungen :)
Das halte ich auch für völlig in Ordnung.
Wie ich oben schon schrieb, denke ich nicht, dass die Auswirkungen bei
einem µC-Projekt merkbar wären, schaden tut es aber auch niemand.
Nur soviel: ich bin als Teamplayer aufgezogen worden und habe viel in
Teams > 15 Personen gearbeitet. Dort werden so scheinbare
Lächerlichkeiten plötzlich lebensnotwendig, bzw. lassen sich sogar in
unproduktiver Arbeitszeit ausrechnen.
> Wo genau bekommst du den internal compiler error (ICE)?
1
../init.c:68: internal compiler error: in start_function, at c-decl.c:6014
OT:
Ich habe die entsprechende Seite mal abfotografiert und angehängt.
Auch wenn die Zeiten aus den 90er Jahren stammen, die prozentualen
Unterschiede dürften auch heute noch gelten.
Santiago wrote:
> Wie ich oben schon schrieb, denke ich nicht, dass die Auswirkungen bei> einem µC-Projekt merkbar wären, schaden tut es aber auch niemand.
Naja, das von dem ich schrieb ist auch etwas größer als das Mini-DDS,
und dürfte irgendwas zwischen 50 und 100 Quelldateien haben. Hab
sie nicht gezählt. Ist übrigens auch in einem Team von bis zu 10
Leuten entstanden. Wie gesagt, es krankt an anderen Dingen als an
der Zeit, doppelte Headerdateien zu öffnen um festzustellen, dass
man die gar nicht braucht. ;-)
>> Wo genau bekommst du den internal compiler error (ICE)?>
1
> ../init.c:68: internal compiler error: in start_function, at
2
> c-decl.c:6014
3
>
Achso. Das trat aus irgendwelchen Gründen immer nur bei Erics
Windows-Version auf, auf Unixen konnte ich das nie nachvollziehen.
Das ist hier:
1
void__attribute__((naked))PCINT0_vect(void){
2
__asmvolatile("clt""\n\t"
3
"rjmp wakeup");
4
}
Davon abgesehen, dass du meines Erachtens viel besser bedient wärst,
all diese in C mächtig verrenkt aussehenden Assembler-Teile in
eine eigene Assembler-Quelldatei zu schreiben, für diesen ICE gibt's
einen einfachen Workaround: man trenne die Deklaration von der
Definition der Funktion.
1
void__attribute__((naked))PCINT0_vect(void);
2
voidPCINT0_vect(void){
3
__asmvolatile("clt""\n\t"
4
"rjmp wakeup");
5
}
Aber: mit dem aktuellen ISR-Makro geht das viel eleganter:
1
ISR(PCINT0_vect,ISR_NAKED){
2
__asmvolatile("clt""\n\t"
3
"rjmp wakeup");
4
}
(Das volatile ist hier übrigens unnötig: der Compiler könnte die
Anweisung sowieso nirgendwo anders hin schieben.)
Santiago wrote:
> Ich habe die entsprechende Seite mal abfotografiert und angehängt.> Auch wenn die Zeiten aus den 90er Jahren stammen, die prozentualen> Unterschiede dürften auch heute noch gelten.
Danke erstmal. Schade, dass es nirgends einen runterladbaren Satz
an Quelldateien gibt, mit denen man das ausprobieren könnte. ;-)
Ich müsste mir mal ein aktuelles C++-Projekt ansehen und versuchen,
ob man die Guards da automatisch reinbauen kann. Was meiner
Vermutung ist: das war noch der alte CFront, und der hat ja die
gesamte C++-zu-C-Umsetzung in einem Präprozessor erledigt. In
heutiger Zeit verbraucht zwar der Compiler bei C++ immer noch massig
viel Zeit, aber der Präprozessoranteil wird vergleichsweise gerade
bei C++ eher verschwindend gering sein (bei C ist er relativ noch
höher).
Moin Jörg,
> für diesen ICE gibt's> einen einfachen Workaround: man trenne die Deklaration von der> Definition der Funktion.
Yepp, das war's. Musste hier (leider?) die erste Variante nehmen, das
erweiterte ISR-Makro scheint in meiner Version von WinAVR noch ned drin
zu sein.
> Davon abgesehen, dass du meines Erachtens viel besser bedient wärst,> all diese in C mächtig verrenkt aussehenden Assembler-Teile in> eine eigene Assembler-Quelldatei zu schreiben
Ich habe jetzt alle möglichen Varianten durchprobiert und bei meinem
Kenntnisstand der Materie ist es die einzige Variante die funktioniert.
Deshalb halte ich mich an die alte Weisheit:
> First make it work, then optimize.
Ich würde es auch lieber anders machen, aber mir fehlt noch zuviel -
unter anderem die Sicherheit, es richtig zu machen.
Immerhin habe ich jetzt - dank Eurer Hilfe - einen Stand erreicht, bei
dem ich mit der HW-Entwicklung loslegen könnte. Mal sehen, ob es dann so
tuht wie erwartet :)
> In heutiger Zeit verbraucht zwar der Compiler bei C++ immer noch massig> viel Zeit, aber der Präprozessoranteil wird vergleichsweise gerade> bei C++ eher verschwindend gering sein (bei C ist er relativ noch> höher).
Der Zeitgewinn beschäftigt sich nicht mit dem Fall, dass der
Präprozessor was zu tun hätte, sondern genau mit dem Gegenteil. Und da
ist die Sprache rel. unerheblich. In C++ gibt es idR viel mehr Header
als in einen C-Projekt.
Selbst wenn ein Header schon verarbeitet wurde, wenn die
include-Anweisung erneut auftaucht, kennt der Präprozessor zwar den
Blocker, er muss aber dennoch jede Zeile überprüfen. Schließlich kann
der Präprozessor nicht davon ausgehen, dass die ganze Datei geblockt
wird. Es könnte zwischendrin auch ein aktiver Part existieren.
Er muss also mindestens einen Logikbaum erstellen und dann verwerfen.
> Ich müsste mir mal ein aktuelles C++-Projekt ansehen und versuchen,> ob man die Guards da automatisch reinbauen kann.
Perl kann bei solchen Aufgaben wahre Wunder bewirken :)
Santiago wrote:
> Yepp, das war's. Musste hier (leider?) die erste Variante nehmen, das> erweiterte ISR-Makro scheint in meiner Version von WinAVR noch ned drin> zu sein.
Dann nimm eine neuere. Die Weihnachtsversion 2007 ist OK, es gibt
keinen
echten Grund, stattdessen eine nochmal ein Jahr ältere zu nehmen.
>> Davon abgesehen, dass du meines Erachtens viel besser bedient wärst,>> all diese in C mächtig verrenkt aussehenden Assembler-Teile in>> eine eigene Assembler-Quelldatei zu schreiben> Ich habe jetzt alle möglichen Varianten durchprobiert und bei meinem> Kenntnisstand der Materie ist es die einzige Variante die funktioniert.
Ich habe gerade erst richtig geschnallt, was du damit überhaupt
machst. Das ist eigentlich das Musterbeispiel für ISR_ALIASOF().
Damit kann man das kurz und prägnant so ausdrücken:
1
ISR(PCINT0_vect){
2
asmvolatile("clt");
3
listen2User(1);
4
}
5
6
ISR(PCINT1_vect,ISR_ALIASOF(PCINT0_vect));
7
ISR(PCINT2_vect,ISR_ALIASOF(PCINT0_vect));
Das spart dann sogar noch deine extra RJMPs:
1
Disassembly of section .text:
2
3
00000000 <__vectors>:
4
0: 3e c3 rjmp .+1660 ; 0x67e <__ctors_end>
5
2: 58 c3 rjmp .+1712 ; 0x6b4 <__bad_interrupt>
6
4: 57 c3 rjmp .+1710 ; 0x6b4 <__bad_interrupt>
7
6: cf c3 rjmp .+1950 ; 0x7a6 <__vector_3>
8
8: ce c3 rjmp .+1948 ; 0x7a6 <__vector_3>
9
a: cd c3 rjmp .+1946 ; 0x7a6 <__vector_3>
10
...
11
20: d4 c3 rjmp .+1960 ; 0x7ca <__vector_16>
12
...
Alle drei Vektoren zeigen auf die gleiche Routine.
> Deshalb halte ich mich an die alte Weisheit:> First make it work, then optimize.
Apropos optimize: wenn du listen2User() static inline machst, spart
das die elendige push/pop-Orgie im PCINT-Handler und insgesamt um
die 50 Bytes an Code, trotz der inline-Erweiterung an mehreren
Stellen. Leider ignoriert der Compiler das inline bei -Os, man
muss entweder eine höhere Optimierungsstufe wählen oder die Funktion
noch mit __attribute__((always_inline)) zu ihrem Glück zwingen.
Santiago wrote:
> Selbst wenn ein Header schon verarbeitet wurde, wenn die> include-Anweisung erneut auftaucht, kennt der Präprozessor zwar den> Blocker, er muss aber dennoch jede Zeile überprüfen. Schließlich kann> der Präprozessor nicht davon ausgehen, dass die ganze Datei geblockt> wird. Es könnte zwischendrin auch ein aktiver Part existieren.> Er muss also mindestens einen Logikbaum erstellen und dann verwerfen.
Er muss nur das Auftreten weiterer Präprozessor-Direktiven scannen.
Die ganze u. U. recht komplexe Syntax der ausgeblendeten Teile darf
er sich klemmen.
Hab's gerade mal probiert: #if 0 ... #endif, dazwischen per copy&paste
,,Datenmüll'' aus einem Terminal reinkopiert. Kein Problem.
Möglicherweise hat der alte CFront das aus irgendwelchen Gründen nicht
gemacht (nicht machen können?), keine Ahnung. Das Alter des Buches
lässt in Zusammenhang mit der Analyse des Autors jedenfalls darauf
schließen, dass das Wort `preprocessor' dort weniger den klassischen
C-Präprozessor als mehr das CFront meint, das damals noch gängige
Technologie für die C++-Implementierung war.
>> Ich müsste mir mal ein aktuelles C++-Projekt ansehen und versuchen,>> ob man die Guards da automatisch reinbauen kann.>> Perl kann bei solchen Aufgaben wahre Wunder bewirken :)
Jaja, schon klar, braucht trotzdem Gehirnschmalz.
Hallo,
habe inzwischen ein update auf winAVR-071221 gemacht, die Anregungen
umgesetzt, die Steine des Anstoßes beseitigt und die fehlende
Funktionalität eingebaut ...
Jetzt bekomme ich Warnungen, mit denen ich nix anfangen kann und die ich
auch keinem Fehler meinerseits zuordnen kann.
Util.s:203: Warning: expression dangerous with linker stubs
Bitte drücke gedanklich die Taste ,,Ingnorieren''. Ich bin in der
Pflicht, den Bugfix dafür endlich mal zu testen... (der weiter nichts
macht, als die Meldung rauszuwerfen).