Hallo,
ich brauche mal Eure Glaskugel. Das Programm ist viel zu komplex um es
hier zu Posten.
ich habe ein Programm geschrieben, das auf einem mega32 problemlos
seinen Dienst versieht.
Ich will es nun auf einen mega328 am laufen bringen.
Grundsätzlich läuft es auch.
UART per Interrupt, Timer gehen sicher
ADC scheint auch zu laufen
Ich habe den Eindruck, dass mir der RAM ausgeht.
Das Programm läuft und ich kann bestimmte Funktionen 1 mal an triggern,
dann wird alles langsamer (hat mit dem Timer zu tun, dass weis ich
schon), und beim 2. mal ist er tot. Daher nehme ich an, es geht der RAM
aus.
Wobei RAM und EEPROM doch gleich groß sind ?!
Hab auch schon versucht, dass Programm teilweise auszukommentieren,
woraufhin es auch geht.
Hat jemand von Euch schon mal ein Programm konvertiert und kann mir
sagen was ich noch übersehe.
Im Prinzip sind die beiden Typen doch gleich ?
Der mega328 hat weniger Pins, ein paar Interrupts sind anders benannt.
Danke und Gruss
J. Sachs
Jürgen Sachs schrieb:> Das Programm ist viel zu komplex um es> hier zu Posten.
Als Anhang verdaut die Forensoftware beliebig komplexe Programme.
Was sagt denn avr-size zur Speicherauslastung?
Oliver
Jürgen Sachs schrieb:> Das Programm ist viel zu komplex um es> hier zu Posten.
Unterschätze mal die Leute hier nicht.
Wieviel 100 Files und 10.000 Quelltextzeilen sind es insgesamt?
Einfach mal alles in ein ZIP packen, wieviel MB sind es dann?
Jürgen Sachs schrieb:> Im Prinzip sind die beiden Typen doch gleich ?
Nö.
Der ATmega32 ist ein Nachfolger der Linie AT90S4434/8535, aus denen
dann ATmega163 und ATmega16 hervorgegangen sind. Ab ATmega16 dann
auch mit JTAG zum Debuggen.
Der ATmega328 ist ein Nachfolger der Linie AT90S2232/4433, aus denen
der ATmega8 hervorgegangen ist. Ab ATmega48/88/168/328 dann auch mit
debugWIRE zum Debuggen.
Schon bei Dingen wie den Timer-Bits würde ich beide Datenblätter sehr
genau nebeneinander halten.
Das Programm ist wirklich zu komplex.
Es ist in mehrere Einzelprojekte aufgeteilt, wie Receiver, Transmitter,
Bootloader, globale Dateien, etc.
Aus den Files kommen jetzt schon 6 unterschiedliche Builds für
unterschiedliche Endgeräte raus.
Es ist einfach zu kompliziert für Außenstehende das Projekt zu erklären,
damit er es nachbauen kann.
Natürlich alles schön in SVN gehostet, sonst könnte ich das auch nicht
mehr handeln.
Aber zum Thema zurück....
Ich habe die Register durch, dachte ich. Die Unterschiede habe ich mit
Defines abgefangen.
avr-size liefert:
1
avr-size rx_main.elf
2
text data bss dec hex filename
3
26414 324 669 27407 6b0f rx_main.elf
bzw im Detail:
1
avr-size -A rx_main.elf
2
rx_main.elf :
3
section size addr
4
.data 324 8388864
5
.text 26414 0
6
.bss 669 8389188
7
.stab 3732 0
8
.stabstr 655 0
9
.comment 17 0
10
.debug_aranges 288 0
11
.debug_info 1278 0
12
.debug_abbrev 180 0
13
.debug_line 1002 0
14
Total 34559
hier sehe ich aber nicht mein Stack verbrauch. Und ich vermute hier mein
Problem.
Auch weis ich nicht genau, wie sich die Programmierung in C++ hier noch
auswirkt zur Laufzeit.
Eine vernünftige Lösung für die Ermittlung des verbleibenden Speichers
zur Laufzeit habe ich noch nicht gefunden. Ich meine es gab mal ein
Ansatz der den Speicher mit Werten vollschreibt und diese danach zählt.
Achja, der Bootloader ist derzeit auf Max Size, fängt also bei 0x7000
an.
Ja, die Wortbreite berücksichitge ich im Programm, wobei ich meine
letzten Tests auch ohne Bootloader gemacht habe, also direkt geflashed
per ISP.
Gruss
Juergen
Jürgen Sachs schrieb:> Aber zum Thema zurück....> Ich habe die Register durch, dachte ich. Die Unterschiede habe ich mit> Defines abgefangen.
Tja, mehr kann es eigentlich kaum sein.
Ein diff über die Disassemblies der beiden Versionen sollte aber die
letzten Zeifel ausräumen können.
Oliver
Jürgen Sachs schrieb:> Auch weis ich nicht genau, wie sich die Programmierung in C++ hier noch> auswirkt zur Laufzeit.
C++ oder C ?
Jürgen Sachs schrieb:> hier sehe ich aber nicht mein Stack verbrauch. Und ich vermute hier mein> Problem.
Bei richtiger Programmierung sollte der Stackverbrauch immer gleich
sein, oder zumindest sollte der 2. Durchlauf eine Funktion nicht mehr
Stack verbrauchen als das 1. Mal (wenn überall in die gleichen
Unterprogramme gesprungen wird).
Hast Du Rekursionen im Programm oder Funktionen, in denen große Array
angelegt werden?
Werden die Array dynamisch angelegt und nicht mehr freigeben?
Jürgen Sachs schrieb:> Das Programm läuft und ich kann bestimmte Funktionen 1 mal an triggern,> dann wird alles langsamer (hat mit dem Timer zu tun, dass weis ich> schon), und beim 2. mal ist er tot.
Vielleicht kannst Du zumindest die Timer ISR zeigen?
Also das Programm ist zum Großteil C++. Allerdings benutze ich es
Hauptsächlich um das Programm zu Kapseln, also Memberfunktionen, keine
Vererbung etc. So lassen sich Programmteile leichter mehrfach nutzen.
Ein Teil des Programms ist noch C, der neuere C++.
Die Timer sind noch "C".
2 Timer genutzt:
- 16 Bit Timer macht ein PWM Signal für Servos
- 8 Bit Timer ,macht eine ungefähre Zeitbasis
Kurz vorab. Ich habe zwei Makros definiert
BV() => Setzt Bit
BVC() => Löscht ein Bit
Es geht hierbei um das "Modul" (Ich nenne meine Hardware Teile Module)
"1003", also die defines "_TYPE_1003_"
I2C ist derzeit deaktiviert, und ruft nur leere Funktionen auf.
Das Modul läuft mit dem internen RC auf 8MHz, Fuses
1
L_FUSE=0xe2
2
H_FUSE=0xd0
3
E_FUSE=0xff
Zeitbasis Timer
1
#include <avr/io.h>
2
#include <avr/interrupt.h>
3
#include "timer.h"
4
5
#define REALTIME_TC0 10UL /* time in ms */
6
#define PRESCALE_TC0 1024UL /* seems to be a good value for 8 and 16 MHz, change CS22-CS20 too */
Ganz vergessen,
Keine Rekursion, zumindest nicht bewusst.
Keine großen Arrays, mal ein "char buffer[5]" für die Wandlung int auf
ASCII als temp buffer.
dient nur der vereinfachung, wenn man alle Bits eines Riegisters listed
und Explizit "0" oder "1" setzt, kann man schneller sehen was man
vergessen hat.
Geht nur nicht immer, da es ja Register gibt, wo man einige Werte
Unverändert lässt.
Bei deinem ersten Ausdruck
1
#define BVC(x) ~(1 << (x))
würde ich doch das ganze Byte negieren, bis auf das eine Bit...
Das wäre eher nicht gut :-)
Wie gesagt geht es auf einem mega32.
Habe es eben auch nochmal auf einem anderen Modul (Hardware Variante)
versucht, die auch mit 8MHz, aber mega32 läuft, da geht alles.
Ich muss folglich etwas übersehen haben, oder ich komme bei dem mega328
tatsächlich an sein Speicher Limit. Wobei ich bisher dachte das ich
nicht mal ca 1k belegt habe und somit noch über 1k RAM für Stack übrig
ist, sihe avrsize oben.
Und der mega328 hat ja auch 2k RAM, wie der mega32
PS: wisst ihr wie das tool hieß, was den freien Speicher zur Laufzeit
ermittelt hat ?
Gruss
Juergen
Der erzeugte Assemblercode sollte für die beiden Prozessoren identisch
sein, bis auf die anders gesetzten Bits in den Timerregistern. avr-size
sollte daher identische Werte liefern.
Oliver
Oliver schrieb:> Der erzeugte Assemblercode sollte für die beiden Prozessoren identisch> sein, bis auf die anders gesetzten Bits in den Timerregistern. avr-size> sollte daher identische Werte liefern.>> Oliver
Korrigiere mich, aber ist das nicht nur so, wenn man jegliche
Optimierung deaktiviert ?
Es kann ja sein, das der Compiler denkt er kann hier einen anderen
Befehl verwenden um XX statt YY zu laden ?!
Nachtrag:
Das geht bei mir sowieso nicht, da durch die wenigeren Pinns, andere
Konfiguration vorgenommen wird.
Der meg328 hat ja weniger pins, daher wird der Code, der z.B. diese auf
Ausgang Konfiguriert, per define ausgeklammert. Der Code ist also in
jedem Fall anders.
Gruss
Juergen
Der Befehlssatz ist bei den Prozessoren identisch, daher sollte da auch
mit Optimierung der selbe Assemblercode rauskommen. Wie gesagt, ein paar
Byte Unterschied durch die unterschiedliche Anzahl der Timerregister
kann es geben, mehr sollte es aber nicht sein.
Oliver
(Oben hattest du es noch ohne Unterstrich geschrieben …)
Eine Null nach links zu schieben, ist stets eine unsinnige Aktion. Es
bleibt eine Null. Dass du damit mental das Bit auf 0 belassen willst,
verdeutlichst du ja bereits durch die Benutzung des entsprechenden
Makros, aber in dessen Definition muss man das dann nicht so
umständlich verklausulieren.
Persönliche Anmerkung: ich bin da eher ein Freund, das so hier zu
schreiben:
1
CTRLREG=/* !_BV(BIT1) | */_BV(BIT2);
Jürgen Sachs schrieb:> Der Code ist also in jedem Fall anders.
Dann wird es schwierig mit dem Vergleichen der Disassembler-Listings.
Das ist richtig, aber wenn es einfach wäre, würde ich ja den Fehler
selbst finden....
Ich denke, ich sehe den Wald vor lauter Bäumen nicht mehr.
Ich werde das Gefühl nicht los, ich Jage ein gcc bug...
Warum ?
Ich wollte auf der mega32 Platform den Code reduzieren, damit er
schlanker wird und weniger Speicher Benötigt.
Das ist im Prinzip eine komplett gekapselte Klasse.
Macht auch keinen IO nur mit dem EEPROM und ruft Funktionen auf, die es
so oder so schon gibt.
Mach ich die Klasse raus (Kann ich per define einfach ausklammern),
komme ich nicht mehr weit.
Dachte erst ich habe noch irgend einen Verweis, Zugriff auf eine
uninitialiserte Variable. Nichts, nichts zu finden.
Jetzt habe ich herausgefunden, das ich ohne die Klasse in meiner Port
Initialisierung fest hänge. Genau gesagt, startet der Kontroller ständig
neu.
Die Klasse hat damit nichts zu tun, gar nichts.
Und das Kuriose ist, das ich nur ein paar Ports nicht initialisieren
muss und es geht. Welche ist egal Hauptsache ein paar raus....
Mache ich die Klasse rein, geht es, mache ich ein paar Ports und die
Klasse raus, geht es.
Ich habe versucht an dem LSS File (mit und ohne Klasse) einen
Unterschied zu sehen, aber da reichen meine Assembler Kenntnisse weit
nicht aus.
Gruss
Juergen
So, jetzt habe ich die Reihenfolge der Port Initialisierung geändert,
und nun geht es wieder auch ohne die Klasse....
Ich gehe mal Käfer Jagen....
Gruss
Juergen
> Jürgen Sachs schrieb:>> Der Code ist also in jedem Fall anders.>> Dann wird es schwierig mit dem Vergleichen der Disassembler-Listings.
Nun, ein einigermaßen anständiges diff-Programm lässt sich auch durch
ein paar zusätzliche Zeilen nicht gleich aus dem Konzept bringen.
Versuch macht kluch...
Oliver
Oliver S. schrieb:>> Jürgen Sachs schrieb:>>> Der Code ist also in jedem Fall anders.>>>> Dann wird es schwierig mit dem Vergleichen der Disassembler-Listings.>> Nun, ein einigermaßen anständiges diff-Programm lässt sich auch durch> ein paar zusätzliche Zeilen nicht gleich aus dem Konzept bringen.>> Versuch macht kluch...>> Oliver
Auf die Idee mit dem Diff bin ich noch nicht gekommen.
Das ist eine Idee. Werde ich noch versuchen.
Gruss
Juergen
Ich komme hier leider nicht weiter.
Der Diff unterscheidet sich leider erheblich. Und mein Assembler
Kenntnisse sind hier am Ende.
ich habe mal ein ZIP angehängt mit beiden LSS Files.
Das 1001 (mega32) geht, das 1003 (mega328p) geht nicht richtig.
Vielleicht kann ja jemand mit etwas Wissen kurz einen Blick darauf
werfen.
So wie ich das sehe, hat der mega328p ja wesentlich mehr Vectoren (21 vs
23).
Danke
Juergen
Jürgen Sachs schrieb:> Das 1001 (mega32) geht, das 1003 (mega328p) geht nicht richtig.
Entgegen den landläufigen Hinweisen wenigstens ein Datenplatt zu
bemühen, bist du in der glücklichen Lage, gleichwohl zwei dieser
heiligen Schriften zu konsultieren. Nur wer die Unterschiede kennt, kann
in die ewigen Jagdgründe emigrieren...
Jürgen Sachs schrieb:> ich habe mal ein ZIP angehängt mit beiden LSS Files.
Ooch nöö, das ist ja Linux ohne Zeilenende.
Meinst Du wirklich, da wühlt sich jemand durch, ohne den Source zu
kennen?
Jürgen Sachs schrieb:> Jetzt habe ich herausgefunden, das ich ohne die Klasse in meiner Port> Initialisierung fest hänge. Genau gesagt, startet der Kontroller ständig> neu.
Was denn nun, hängt er oder resettet er und welche Resetquelle ist
gesetzt?
Füge dochmal einige Debugausgaben ein. Einen Pin hast Du bestimmt frei
zur Ausgabe auf ein Terminalprogramm.
Und selbstverständlich gehört beim Debuggen der Watchdog disabled.
Jürgen Sachs schrieb:> Der Diff unterscheidet sich leider erheblich. Und mein Assembler> Kenntnisse sind hier am Ende.
Das sieht nur so aus, weil in den lss-Files Zeilennummern usw. mit drin
sind, die natürlich nicht übereinstimmen. Erzeug dir mal richtige
Dissassembler-Files, dann siehst du, daß das allermeiste in den beiden
Pogrammen identisch ist. Ja, der 328 hat ein paar Vectoren mehr, daher
verschieben sich alle labels, aber das ist nicht weiter tragisch.
Du vergleichst allerdings Äpfel mit Birnen. Das sollte schon für beide
Prozessoren der selbe Sourcecode sein. Ist es aber nicht, die
1003er-Version enthält mehr Code (einiege Routinen mehr). Das hat sicher
Einfluß auf den Sram-Bedarf.
Insofern ist die Ausage "es läuft auf dem 32er, aber nicht auf dem
328er" nicht richtig, es ist halt nicht das selbe Programm.
Oliver
D. V. schrieb:> Jürgen Sachs schrieb:>> Das 1001 (mega32) geht, das 1003 (mega328p) geht nicht richtig.>> Entgegen den landläufigen Hinweisen wenigstens ein Datenplatt zu> bemühen, bist du in der glücklichen Lage, gleichwohl zwei dieser> heiligen Schriften zu konsultieren. Nur wer die Unterschiede kennt, kann> in die ewigen Jagdgründe emigrieren...
Was will mir der Nutzer mit diesen Worten sagen ?
Das Register anders heißen und zum Teil anders belegt sind, ist mir
bekannt und ich denke auch alle Punkte angepasst zu haben.
Einfache Programme, die z.B. nur Serielle Daten ausgeben, laufen ja
auch.
Gruss
Juergen
Jörg Wunsch schrieb:> Poste doch zumindest mal noch die ELF-Files. Die kann sich dann jeder> selbst disassemblieren, sich die Symboltabelle ansehen usw.
Ich habe gestern noch einen Punkt auf dem PC Programm gesehen (Das die
RS232 Daten auswertet), wo mich zu einer falschen Aussage geführt hat.
Scheinbar kamen die Daten manchmal zu schnell und wurden dann nicht
abgearbeitet, bis ein weiteres Ereignis aufgelaufen ist
(Tastatureingaben oder mehr Daten). Seid 2 Jahren in Betrieb und noch
nie aufgefallen....
Ist im Prinzip ein Terminal, wo mir die Rückmeldungen auswertet und im
Klartext anzeigt, Programm updates lädt usw...
Das habe ich nun angepasst. Ein Test steht noch aus.
Das Programm geht immer noch nicht, aber so wie es aussieht bleibt das
Programm doch nicht "hängen" für ein paar Sekunden und macht dann
weiter.
Das der 328er mehr Funktionen hat, wundert mich. Sollte genau anders rum
sein, erklärt aber den größeren Code. Ich versuche mal die Funktionen
raus zu finden und das im Code zu prüfen.
Wegen dem Linux Zeilenende, sorry ich arbeite Praktisch nur unter Linux.
Ich verifiziere das im laufe des Tages und Poste sonst die ELF Files.
Habe von Euch ja eine lange Liste zum Checken bekommen.
Danke, ich weiß das zu schätzen.
Gruss
Juergen
Jürgen Sachs schrieb:> Das der 328er mehr Funktionen hat, wundert mich.
Der ist einfach ein paar Jahre jünger von der Entwicklung her.
> Sollte genau anders rum> sein, erklärt aber den größeren Code.
Größerer Code entsteht vor allem deshalb, weil bei den mega*8
IO-Register
außerhalb des regulären IO-Bereichs liegen und nur noch über reguläre
Speicherzugriffe (LDS/STS) erreichbar sind, während beim ATmega16 noch
alles in die unteren 64 Adressen gepasst hat, auf die man mit IN/OUT
zugreifen kann.
> Wegen dem Linux Zeilenende, sorry ich arbeite Praktisch nur unter Linux.
Sollte auch unter Windows aber kein Thema sein. Notepad kann damit
umgehen, viele andere Editoren auch. Die Leute werden ja kaum auf der
Kommandozeile mit "more" arbeiten. ;-)
Du kannst natürlich auch über einen Zwischeschritt gehen.
Erstmal das Programm vom 32 auf den 324 umschreiben, dann bleiben die
Portpins ja gleich und Du mußt nur die Timerregister usw. ändern.
Und wenn das läuft, kannst Du die Portpins ändern, also von 324 auf 328.
Jürgen Sachs schrieb:> Das der 328er mehr Funktionen hat, wundert mich. Sollte genau anders rum> sein, erklärt aber den größeren Code.
Im 1003er stecken mehr Klassen drin, daher gibt es da zu Begin schonmal
mehr statische Konstruktoren.
Aber der Programmablauf ist auch anders. Anbei ein Screeshot von
WinMerge, der eine Ausschnitt aus main zeigt (ziemlich am Anfang von
main).
Im 1003er File gigt es da einige Funktionsaufrufe, die im 1001er nicht
stattfinden.
Ganz kriegt WinMErge das nicht synchronisiert, der Aufruf von
<_Z7adcInitv>, der links als letztes zu sehen ist, kommt rechts auf der
nächsten Bildschirmseite, <_Z8initGearv> kommt zwei Seiten später.
Die Aufruffolge von <__strlen_P> und <_Z17sendI2CBlocking_phPhh> kommt
rechts in der Folge noch mehrfach vor, links gibt es die nicht.
Das ist nicht das selbe Programm.
Oliver
Outputs.setOutputConfig ( 8, OUT_PORTD, PD5 ); // Fernlicht, LED 1
14
Outputs.setOutputConfig ( 9, OUT_PORTD, PD6 ); // Nebellicht, LED 2
15
16
....................
17
#elif _TYPE_1003_
18
// JPx, Servo 1-8
19
Outputs.setOutputConfig ( 0, OUT_PORTD, PD5 );
20
//Outputs.setOutputConfig ( 1, OUT_PORTC, PC7 );
21
Outputs.setOutputConfig ( 2, OUT_PORTC, PC0 );
22
....................
23
#else
24
#error "Module type not set or unknown"
25
#endif
26
}
Ich speichere hierbei im übrigen nicht den Port, sondern nur einen ENUM
Wert, so kann ich mit einem Bitfeld in einem Byte, Port und Pin
speichern.
Im Programm spreche ich nur Ausgang "1" oder "5" an.
Es gibt dann quasi eine Funktion die über einen Switch case block den
jeweiligen Port anspricht.
So kann ich mit wenigen Zeilen Änderung, den Code an unterschiedliche
Hardware anpassen.
Außerdem kann man zur Laufzeit die Funktion eines Pins anpassen, daher
Arbeite ich lieber mit einer Referenz, als mit Ports und Pins.
Gruss
Juergen
Oliver schrieb:> Jürgen Sachs schrieb:>> Das der 328er mehr Funktionen hat, wundert mich. Sollte genau anders rum>> sein, erklärt aber den größeren Code.>> Im 1003er stecken mehr Klassen drin, daher gibt es da zu Begin schonmal> mehr statische Konstruktoren.>> Aber der Programmablauf ist auch anders. Anbei ein Screeshot von> WinMerge, der eine Ausschnitt aus main zeigt (ziemlich am Anfang von> main).>> Im 1003er File gigt es da einige Funktionsaufrufe, die im 1001er nicht> stattfinden.>> Ganz kriegt WinMErge das nicht synchronisiert, der Aufruf von> <_Z7adcInitv>, der links als letztes zu sehen ist, kommt rechts auf der> nächsten Bildschirmseite, <_Z8initGearv> kommt zwei Seiten später.>> Die Aufruffolge von <__strlen_P> und <_Z17sendI2CBlocking_phPhh> kommt> rechts in der Folge noch mehrfach vor, links gibt es die nicht.>> Das ist nicht das selbe Programm.>> Oliver
Danke,
Ja manche Module haben I2C, dass ich aber nicht nutze. Es gibt Ansätze
darüber ein Debugging zu machen. Im Prinzip eine RS232 über I2C, habe
ich aus Zeitgründen aber nicht mehr verfolgt, da es immer wieder zu
Problemen kam. Daher habe ich das auch deaktiviert, dachte ich.
Was aber bedeutet, solange DEBUG_ENABLED NICHT definiert ist, sollte
der code nicht implementiert werden.
So ist es ja beim mega32.
Aber laut dem Diff, ist es beim mega328 doch eingebunden.
Der Sache gehe ich mal gleich nach.
Juergen
Der Tipp war Gold wert.
Ich habe kurz noch 2 Warnungen in den Defines eingebaut um zu sehen was
greift.
Beim mega32 ist es deaktiviert, beim mega328 aktiv.
Was Fatal ist, da die I2C Pins dort mit anderer Hardware belegt sind.
Ich werde berichten..
Gruss
Juergen
Jürgen Sachs schrieb:> Es gibt dann quasi eine Funktion die über einen Switch case block den> jeweiligen Port anspricht.
Quasi Arduino-Style.
Jürgen Sachs schrieb:> So kann ich mit wenigen Zeilen Änderung, den Code an unterschiedliche> Hardware anpassen.
Kann ich auch super bequem machen, mit meiner sbit.h.
Da diese Macros aber schon zur Compilezeit aufgelöst werden, macht der
Compiler einfach SBI/CBI bzw. SBIS/CBIS daraus.
Peter Dannegger schrieb:> Jürgen Sachs schrieb:>> Es gibt dann quasi eine Funktion die über einen Switch case block den>> jeweiligen Port anspricht.>> Quasi Arduino-Style.
Ich habe mich mit der Programmierung für das Arduino nicht wirklich
beschäftigt.
Für mich war es die einzige Möglichkeit das so umzusetzen, ohne Port und
Pin komplett abzuspeichern.
>> Jürgen Sachs schrieb:>> So kann ich mit wenigen Zeilen Änderung, den Code an unterschiedliche>> Hardware anpassen.>> Kann ich auch super bequem machen, mit meiner sbit.h.> Da diese Macros aber schon zur Compilezeit aufgelöst werden, macht der> Compiler einfach SBI/CBI bzw. SBIS/CBIS daraus.
Hört sich interessant an, würdest du mir/uns das näher erklären ?
Hatte das hier mal angefragt, würde mit so 30 Byte Speicher sparen :-)
Gruss
Juergen
Peter Dannegger schrieb:> Kann ich auch super bequem machen, mit meiner sbit.h.> Da diese Macros aber schon zur Compilezeit aufgelöst werden, macht der> Compiler einfach SBI/CBI bzw. SBIS/CBIS daraus.
Du kannst damit aber keine "Pin-Konfigurationen" abspeichern.
Bei einem objektorientierten Ansatz kann es unter Umständen Sinn machen
z.B. jeder Instanz einer Klasse "Schieberegister" den jeweiligen Data-,
Clock, und Enable-Pin mitzugeben.
Aber ja, leider will man den Overhead den man durch Run-Time
Konfigurationen kriegt leider selten haben...
Jon schrieb:> Bei einem objektorientierten Ansatz kann es unter Umständen Sinn machen> z.B. jeder Instanz einer Klasse "Schieberegister" den jeweiligen Data-,> Clock, und Enable-Pin mitzugeben.
Kann man aber auch im Konstruktor statisch(*) machen, dann braucht man
den Overhead trotzdem nicht.
(*) Soll heißen: zur Compilezeit bekannt und danach nicht mehr
änderbar, nicht "static" als C++-Schlüsselwort.
Jörg Wunsch schrieb:> Kann man aber auch im Konstruktor statisch(*) machen, dann braucht man> den Overhead trotzdem nicht.>> (*) Soll heißen: zur Compilezeit bekannt und danach nicht mehr> änderbar, nicht "static" als C++-Schlüsselwort.
Ich muss sagen, in C++ bin ich nicht so bewandert, bin jetzt von C
ausgegangen wo alle "Attribute" in einer Strukt gebündelt werden?
Kannst du vielleicht ein Beispiel bringen wie sowas aussehen würde?
Mir ist nicht ganz klar wie du den Port/Pin festhalten kannst ohne dem
Compiler sämtliche Optimierungsmöglichkeiten zu nehmen...
Interessanter Ansatz.
Ich wusste gar nicht dass man const class member erst im Construtor
Initialisieren darf.
Muss man hier die Parameter Variablen (Port, Pin, DDR) nicht auch als
"const" declarieren, zumindest der Form halber ?
Jetzt müsste ich von der Klasse LED nur noch ein Array definieren und
alles ist gut. Das könnte mir einiges an overhead sparen, vorallem in
den Interrupts für das PWM, da ja der Switch case wegfällt usw...
Gruss
Juergen
Jürgen Sachs schrieb:> Ich wusste gar nicht dass man const class member erst im Construtor> Initialisieren darf.
Man darf sie nur dort initialisieren, und zwar nur in der
member initialization list (also auch nicht erst innerhalb der
folgenden geschweiften Klammern).
> Muss man hier die Parameter Variablen (Port, Pin, DDR) nicht auch als> "const" declarieren, zumindest der Form halber ?
So genau kenne ich mich da auch nicht aus, aber ich denke nicht.
> Jetzt müsste ich von der Klasse LED nur noch ein Array definieren und> alles ist gut.
Habe mal etwas nachgelesen. Scheint seit C++11 dabei zu sein.
Zumindest
versteht GCC 4.7 sowas:
Jörg Wunsch schrieb:> Habe mal etwas nachgelesen. Scheint seit C++11 dabei zu sein.> Zumindest> versteht GCC 4.7 sowas:
Man kann doch schon immer ein Array aus Objekten einer Klasse anlegen,
oder nicht? Zumindest bin ich bisher davon ausgegangen. Wie gesagt, C++
ist bisher noch eine Baustelle bei mir...
Ahja, oder ging das etwa bisher nur mit STD::Vektor und seit C++11 auch
mit "Plain C Arrays"?
Noch eine Frage @ Jörg Wunsch:
Der Assembler-Output überzeugt mich :-) Mir ist nur nicht klar wie der
Compiler das hinkriegt. Ich vermute das geht nur weil:
- die Klasse in der aktuellen Übersetzungseinheit komplett bekannt ist,
d.h. auch der Code des Konstruktors ist bekannt
- &PORTB und Konsorten zur Compilezeit bekannt sind
Was wäre denn wenn ich meine Klasse "led" in Header und Source-File
auftrennen würde und nur die Schnittstellen im Header bekannt mache? Ich
denke, dann muss der Compiler wieder die umständlicheren Befehle
benutzen...
(Kann das leider nicht nachprüfen, da ich unterwegs bin und auf dem Win
Rechner keinen gcc habe :/)
Jon schrieb:> Man kann doch schon immer ein Array aus Objekten einer Klasse anlegen,> oder nicht?
Vor C++11 aber offenbar nur mit dem Default-Konstruktor der Klasse,
was für diesen Fall (Initialisierung von const members) nicht hilft.
> Noch eine Frage @ Jörg Wunsch:> Der Assembler-Output überzeugt mich :-) Mir ist nur nicht klar wie der> Compiler das hinkriegt. Ich vermute das geht nur weil:> - die Klasse in der aktuellen Übersetzungseinheit komplett bekannt ist,> d.h. auch der Code des Konstruktors ist bekannt> - &PORTB und Konsorten zur Compilezeit bekannt sind
Ja, irgendwie so.
> Was wäre denn wenn ich meine Klasse "led" in Header und Source-File> auftrennen würde und nur die Schnittstellen im Header bekannt mache?
Kann man nur mal ausprobieren, ja. Aber es ist natürlich logisch, der
Compiler kann nur bauen, was er auch kennt.
Habe keine Ahnung, inwiefern dann LTO da noch helfen könnte.
Jon schrieb:> Was wäre denn wenn ich meine Klasse "led" in Header und Source-File> auftrennen würde und nur die Schnittstellen im Header bekannt mache? Ich> denke, dann muss der Compiler wieder die umständlicheren Befehle> benutzen...
Ist das wirklich eine Einschränkung?
Mir scheint, dass ist wie mit den inline-Funktionen in C.
Die müssen doch wohl auch im Header stehen, wenn man sie in mehreren
Modulen
benutzen will.
Mal kurz als Meldung zum eigentlichen Problem. :-)
Der Tipp mit den zusätzlichen Funktionen war die Urspüngliche Ursache.
Ich hatte in der Moduldefinition das I2C Debugging nicht deaktiviert,
was Fatal war.
Für die die es interessiert, ich habe ein Include für ein Makefile, in
dem die wichtigsten Spezifikationen Definiert werden. Hierdurch passt
sich der Code auf den Modultyp und die Eigenschaften an. In der erstem
CCMODULE_DEFS Zeile Fehlte die "#" vor dem "-D_DEBUG_ENABLED_":
Das war allerdings nicht die alleinige Ursache, danach startete der Code
Permanent neu.
Um es einzugrenzen habe ich dann irgendwann einen codeblock eingebaut
wie diesen (Beispielhaft)
1
while(1)
2
{
3
uartPuts("Ich Lebe");
4
delay(lange);
5
}
Und habe mich so langsam vor gearbeitet, bis ich wieder im Hauptloop
war.
Dort bin ich ohne weitere Änderung angekommen, habe dieses Konstrukt
wieder entfernt und es lief.
Anschließen habe ich noch entdeckt, das meine Servoimpulse zu lange
sind, da diese sich nicht an F_CPU anpassen, was schnell korrigiert war.
Ich habe noch ein leichtes Jittern, laut Osci und Servo knurren. Sofern
mein Osci das anzeigen kann ca 1,425ms und 1,43ms. Also ca 0,005ms.
Genauer kann ich es an meinem Scope nicht ablesen.
Ist so ein Jitter von ca. 5us noch in den Griff zu bekommen?
Punkt ist, es geht jetzt. Ich habe noch einige Punkte in Sachen Hardware
entdeckt. Falscher PIN, habe irgendwie überlesen, das ADC6 und ADC7
reine ADC und keine IO sind. Das muss ich noch anpassen, aber mit etwas
Fädeldraht ist das auch schnell gemacht.
Vorhin habe ich nochmal einen SVN Diff gemacht zwischen den Versionen am
Anfang Rev 21.01.2014@703 und Rev 25.01.2014@727 und kann leider, bis
auf oben genannte Punkte keine Relevante Punkte erkennen.
Daher mal Asche auf mein Haupt und riesigen Dank an Eure Hilfe.
Gruss
Juergen
PS: Ich werde das mal mit dem "LED" Objekt testen und hier noch
berichten, auch wenn es jetzt nicht ganz zum Thema passt.
So,
wie versprochen, hab ich mal einen Test gemacht, mit den io_ports class.
Ein paar kleine Fehler hatten sich eingeschlichen bei Jörg, aber er
wollte ja nur, das ich meinen Kopf anstrengen muss :-)
Ich habe die Beispiel Files mal angehängt, die include Pfade müsst Ihr
anpassen....
Und es ist Linux Code, als nur "CR" Zeilenende ;-)
1
#include <avr/io.h>
2
3
#include "../global_files/io_port.h"
4
5
class allIOs
6
{
7
public:
8
io_port ios[5] = {
9
// LED PD5, PD6
10
io_port(PD5, &DDRD, &PORTD, &PIND, true),
11
io_port(PD6, &DDRD, &PORTD, &PIND, true),
12
// Schalter PD2, PD3, PD4
13
io_port(PD2, &DDRD, &PORTD, &PIND, false),
14
io_port(PD3, &DDRD, &PORTD, &PIND, false),
15
io_port(PD4, &DDRD, &PORTD, &PIND, false)
16
};
17
}allios;
18
19
20
21
22
int main(void)
23
{
24
while(1)
25
{
26
io_port *io = &allios.ios[0];
27
io->toggle();
28
29
if(allios.ios[2].readPin()) // nicht entprellt, nur fuer test !
30
allios.ios[1].toggle();
31
}
32
}
ist nur 860 Bytes groß. Und mal für ein Pollin Evaluation Board
angepasst.
avr-size -x main_io_ports.elf
text data bss dec hex filename
0x35c 0x0 0x23 895 37f main_io_ports.elf
Ich muss sagen, der Ansatz hat was !
Ob das jetzt Ressourcen spart oder nicht, geht wieder über meine
Assembler Kenntnisse hinaus.
Auf alle Fälle kann man schön "Abstrakt" Programmieren :-)
Die Klasse allIOs ist in dem Beispiel eventuell nicht notwendig, aber
bei mir sind hier noch mehr variablen verpackt, daher wollte ich das so
mal testen.
Gruss
Juergen
Ich muss mich mal reindrängeln:
Jon schrieb:> Mir ist nicht ganz klar wie du den Port/Pin festhalten kannst ohne dem> Compiler sämtliche Optimierungsmöglichkeiten zu nehmen...
+1
Ich füttere meinen Compiler mit dem Code ...
Jörg Wunsch schrieb:> ...
... und von Optimierung keine Spur! Portadressen aus dem Speicher holen,
Funktionen aufrufen.
Jörg Wunsch schrieb:> Das Compilat:
1
.global main
2
.type main, @function
3
main:
4
/* prologue: function */
5
/* frame size = 0 */
6
/* stack size = 0 */
7
.L__stack_usage = 0
8
sbi 0x17,0
9
sbi 0x18,0
10
ldi r24,lo8(1)
11
out 0x16,r24
12
cbi 0x18,0
13
ldi r24,0
14
ldi r25,0
15
ret
Wo kommt denn da sowas her?
Was hab' ich schon wieder verpasst?
Jörg Wunsch schrieb:> Was bitte? Wie man zu dem Compilat kommt, oder was ist deine Frage?
Ich habe das so interpretiert, dass der Quellcode (obendrüber) zu diesem
Ergebnis führt. Irgendwas muss dort doch fehlen?
Weil:
Ralf G. schrieb:> Jon schrieb:>> Mir ist nicht ganz klar wie du den Port/Pin festhalten kannst ohne dem>> Compiler sämtliche Optimierungsmöglichkeiten zu nehmen...
Als Ergebnis hätte ich vermutet, dass ganz normal auf die Ports
zugegriffen wird, so als wären sie global. Aber das Ergebnis sind bei
mir ganz normale ('umständliche') Zeigeroperationen und sogar 'richtige'
Funktionsaufrufe (Bei dem einen Mal hätte ich inlinen erwartet.)
Edit:
zwei Änderungen habe ich noch am Quelltext vorgenommen:
- Die Initialisierung von 'pinmask' am Anfang erzeugte bei mir eine
Warnung.
-
Ralf G. schrieb:> Ich habe das so interpretiert, dass der Quellcode (obendrüber) zu diesem> Ergebnis führt.
Ja.
> Irgendwas muss dort doch fehlen?
Nein, das Beispiel war vollständig. Habe es per copy&paste eben
nochmal compiliert.
> Als Ergebnis hätte ich vermutet, dass ganz normal auf die Ports> zugegriffen wird, so als wären sie global. Aber das Ergebnis sind bei> mir ganz normale ('umständliche') Zeigeroperationen und sogar 'richtige'> Funktionsaufrufe (Bei dem einen Mal hätte ich inlinen erwartet.)
Dann hast du einen zu alten Compiler. Ab GCC 4.7 jedenfalls ergibt
sich das, was du da sehen kannst.
> Edit:> zwei Änderungen habe ich noch am Quelltext vorgenommen:> - Die Initialisierung von 'pinmask' am Anfang erzeugte bei mir eine> Warnung.
OK, ich hatte keine Warnungen aktiviert. Ist eine Warnung, dass er
die Konstruktoraufrufe umsortiert, muss man entsprechend tauschen:
Nein, das ist falsch. Bei allen hinreichend neuen AVRs ist die
effizientestes Methode, einen Portpin auf PORTx zu toggeln, dass man
dessen Bitwert auf PINx schreibt. Die XOR-Variante müsste auf
PORTx gehen, erzeugt aber aufwändigeren Code.
Jörg Wunsch schrieb:> dass man> dessen Bitwert auf PINx schreibt.
Ah ja. Totaler Käse von mir. Na klar! PORTx ...
Jörg Wunsch schrieb:> Dann hast du einen zu alten Compiler. Ab GCC 4.7 jedenfalls ergibt> sich das, was du da sehen kannst.
Hab' mir extra dazu gestern den 4.7.2 runtergeladen und eingestellt. :-/
Edit:
Allerdings: 'make' war da nicht mit dabei. Da habe ich das vom 4.5er
genommen. Wird da vielleicht noch was 'umsortiert'?
Jörg Wunsch schrieb:> Ich habe das einfach auf der Kommandozeile compiliert mit:
Das habe ich jetzt mal versucht. Da wird allerdings automatisch die alte
Version verwendet. (Die mit der Toolchain 'richtig' installiert wurde?)
Das Ergebnis ist gleich (riesig). Ich habe die *.C-Datei mal in das
Verzeichnis von 'avr-gcc' kopiert und den Aufruf von diesem Verzeichnis
gestartet. Ergebnis:
avr-gcc: error: CreateProcess: No such file or directory
Warum kann man im AVR-Studio unter Custom Options/External Tools/avr-gcc
nicht einfach den Compiler eintragen und das funktioniert?
Ausnahmsweise. (Falls in dem Thread noch Platz ist.)
Wieso vervielfacht sich die Codegröße, wenn L1 außerhalb von 'main'
definiert wird? Ich würde die Variante übersichtlicher für die Kontrolle
des RAM-Bedarfes halten.