Hallo zusammen,
kurz zur Vorgeschichte: zur Zeit sitze ich am Redesign einer 20 Jahre
alten Baugruppe, die dabei von einem Dallas 80C320 auf einen ATMega2560
umgestellt wird. Hauptsächliche Gründe sind eine dringend benötigte 3.
serielle Schnittstelle, der Wunsch nach Flash anstelle von EPROM und
mehr als die derzeit vorhandenen 32k RAM. Programmiert ist das Ganze in
Assembler, da bin ich hinreichend fit auf der 51er Strecke.
Jetzt ist die Hardware fertig und ich sinne darüber nach, ob ich die
Portierung von 51er Assembler nach AVR-Assembler ausführe oder mal
wieder versuche, mit C zu beginnen. Bis jetzt ist das immer erfolgreich
gescheitert und spätestens an dem Punkt, wo der Hinweis kam, bei
Problemen doch gefälligst im Assemblerlisting nachzuschauen, habe ich
mir dann gesagt, dann kann ich das gleich in Assembler programmieren
(und habe das dann auch getan).
Jetzt habe ich mit dem AVR Studio 4.18 mal ein C-Projekt angelegt und
mich mit ersten Schritten versucht. Problem ist schon die Darstellung
der Anbindung des externen RAMs und Unterscheidung in den internen
RAM-Bereich 200h – 21FFh und den externen Bereich 2200h – FFFFh bei der
Deklaration von Variablen. Der Quelltext sieht jetzt so aus (die
Linkeroption steht mit dabei):
//memory is used for variables (.data/.bss) and heap (malloc()).
11
*/
12
13
staticconstcharT000[]PROGMEM="Hello World 1 ";
14
staticconstcharT001[]PROGMEM="Hello World 2 ";
15
unsignedchareeso00EEMEM;
16
unsignedchareeso01EEMEM;
17
unsignedchareeso02EEMEM;
18
unsignedchareeso03EEMEM;
19
unsignedcharso00EXTMEM;
20
unsignedcharso01;
21
unsignedcharso02;
22
unsignedcharso03;
23
24
uint8_tmy_array[100]EXTMEM;
25
26
intmain(void){
27
28
init_io();//Ports initialisieren
29
30
lcd_init(LCD_DISP_ON);
31
lcd_puts_p(&(*T000));
32
lcd_gotoxy(0,1);
33
lcd_puts_p(&(*T001));
34
35
xbus_adr=eeprom_read_byte((uint8_t*)&eexbus_adr);
36
my_array[0]=xbus_adr;
37
so03=xbus_adr;
38
39
40
while(1)
41
{
42
}
43
44
}
Die Linkeroption ist unter project - configuration options - custom
options eingetragen.
Ich habe jetzt erwartet, dass die Deklarationen von so00 mit EXTMEM im
Bereich ab 2200h landen, die anderen im Bereich 200h-21FFh. Der
Stackpointer wird auf 21FFh gesetzt.
Frage: Was habe ich falsch gemacht?
Thomas
PS: Mir ist bewusst, dass ich hier eine prima Zielscheibe aufgestellt
habe. Wenn die zu erwartenden Prügel in die richtige Richtung führen,
nehme ich das in Kauf. Da ich im direkten Umfeld aber niemanden habe,
der meinen Start mit C unterstützen kann, bleibt nur diese Möglichkeit,
irgendwie die Spur mal aufzunehmen - oder es mal wieder bleiben zu
lassen.
Thomas P. schrieb:> Frage: Was habe ich falsch gemacht?
Du hast .data nach 2200h gelegt, nicht .EXTMEM.
Darüber hinaus wäre es für zukünftige Fragen sinnvoll, wenn du das "Ich
habe jetzt erwartet" auch um ein "Ich habe stattdessen bekommen"
ergänzen würdest.
Hallo Stefan,
danke für den Hinweis, aber auch der Eintrag
-Wl,--section-start,.extmem=0x802200,--defsym=__heap_end=0x80ffff
führt nicht zum Ziel.
Alle Variablen landen im Bereich ab 200h, zuerst das array mit EXTMEM,
dann die einzelne Variable mit EXTMEM und dann der Rest ohne explizite
Anweisung. Da kommt gleich die nächste Frage hoch: Warum erfolgt die
Anlage der Variablen (array, so00) nicht in der Reihenfolge der
Deklaration?
Thomas
Thomas P. schrieb:> Hallo Stefan,>> danke für den Hinweis, aber auch der Eintrag> -Wl,--section-start,.extmem=0x802200,--defsym=__heap_end=0x80ffff> führt nicht zum Ziel.
.extmem ist nicht gleich .EXTMEM
Thomas P. schrieb:> Der Weg Richtung C ist steinig. Mal schauen, an welcher Klippe ich als> nächstes scheitere.
Nicht so sehr, wenn du schon mal Assember gut kannst. Die grundsätzliche
Herangehensweise ist bei beiden gleich. C ist nich sooo weit weg von
Assembler, wie das manch einer denkt :-)
Recht hilfreich fand ich dieses Buch zum Thema:
http://www.rrzn.uni-hannover.de/buch.html?&titel=ansi-c
Thomas P. schrieb:> Hallo Johann,>> dickes Dankeschön, genau das war es.> Der Weg Richtung C ist steinig. Mal schauen, an welcher Klippe ich als> nächstes scheitere.
Das hat mit C an sich wenig zu tun, sondern mehr damit wie deine
Werkzeuge funktionieren. Die Sprache C hat zu Thema 'externer Speicher'
überhaupt nichts zu sagen. Solche Dinge sind immer plattformabhängig
(wie vieles andere auch in C) und du musst eben lernen, wie deine
Entwicklungsumgebung das handhabt.
> ... Problem ist schon ...
'schon' ist gut gesagt :-) Denn eigentlich bist du da schon in der hohen
Schule der Sonderfälle und nicht beim Einstieg in C.
Karl Heinz schrieb:> 'schon' ist gut gesagt :-) Denn eigentlich bist du da schon in der hohen> Schule der Sonderfälle und nicht beim Einstieg in C.
Hmm, das war für mich im ASM immer selbstverständlich, dass ich den
Speicher selber aufteile und die sinnvolle Anordnung festlege. Dieses
Vorgehen hat es im vorliegenden Projekt ermöglicht, durch
Pointermanipulation (Offset im High- und Low-Teil) sehr schnell auf die
Inhalte mehrerer Arrays zugreifen zu können, was bei den 4MHz des
DS80C320 den Programmlauf deutlich beschleunigt hat.
Thomas
So, ich muss nochmal nerven.
Folgende Fragen habe ich derzeit:
1. In der main.c habe ich wie oben angeraten folgendes Konstrukt
angelegt:
folge_t folge[255] EXTMEM; // funktioniert auch
Damit ich den Inhalt bequem byteweise transferieren kann, möchte ich
quasi ein zweites Array in gleicher Größe genau darüber legen.
Wie stelle ich das an? Oder macht man das ganz anders?
2. Auf das in der main.c definierte Arrays möchte ich aus einer anderen
.c-Datei zugreifen. Dazu habe ich dort ein
extern folge[];
hinterlegt. Beim compilieren bekomme ich die Warnung
:6: warning: type defaults to 'int' in declaration of 'folge'
Was muss ich anders machen? Wie ist es richtig?
Danke für alle Hinweise.
Thomas
Thomas P. schrieb:> So, ich muss nochmal nerven.> Folgende Fragen habe ich derzeit:>> 1. In der main.c habe ich wie oben angeraten folgendes Konstrukt> angelegt:> folge_t folge[255] EXTMEM; // funktioniert auch> Damit ich den Inhalt bequem byteweise transferieren kann, möchte ich> quasi ein zweites Array in gleicher Größe genau darüber legen.> Wie stelle ich das an? Oder macht man das ganz anders?
Man könnte natürlich mit unions arbeiten
1
union {
2
folge_t folge[255];
3
unsigned char folgeBytes[ sizeof folge ];
4
} data EXTMEM;
Ich denke, so müsste das klappen mit dem EXTMEM.
Was man natürlich immer machen kann, das ist einen Pointer auf diesen
Speicher einrichten und dann bei den Transferfunktionen anstelle des
Arrays übrall mit dem Pointer arbeiten
1
folge_t folge[255] EXTMEM;
2
folge_t * EXTMEM dataPtr = &folge;
oder aber du kannst ganz einfach auch in den Bearbeitungsfunktionen in
denen du byteweise hantiiren möchtest, den Datentyp des Arrays auf einen
unsigend char Pointer niedercasten.
> 2. Auf das in der main.c definierte Arrays möchte ich aus einer anderen> .c-Datei zugreifen. Dazu habe ich dort ein> extern folge[];> hinterlegt. Beim compilieren bekomme ich die Warnung> :6: warning: type defaults to 'int' in declaration of 'folge'> Was muss ich anders machen? Wie ist es richtig?
logisch.
Schau
Der COmpiler muss ja bei der Verwendung immer noch wissen, was das für
einen Datentyp hat. Mit
1
extern folge[];
hast du ja nur gesagt, dass es ein Array ist. Schon. Aber von welchem
Typ denn? Sind das int, sind das double. Oder vielleicht Character. Oder
ganz was anderes?
Du musst ihm schon sagen: folge ist ein Array. UNd zwar ein Array aus
folge_t. Das kann ja der Compiler nicht riechen
Vielen Dank, auf die Lösung bei 2. hätte ich auch selbst kommen können
schäm.
Das:
1
union{
2
folge_tfolge[255];
3
unsignedcharfolgeBytes[sizeoffolge];<<Zeile291
4
}dataEXTMEM;
mag er nicht, da wirft der Compiler immer
../main.c:291: error: 'folge' undeclared here (not in a function)
Da war ich schon dicht dran, kann den Fehler aber nicht einordnen.
Thomas
Thomas P. schrieb:> Vielen Dank, auf die Lösung bei 2. hätte ich auch selbst kommen können> schäm.>> Das:>
1
>union{
2
>folge_tfolge[255];
3
>unsignedcharfolgeBytes[sizeoffolge];<<Zeile291
4
>}dataEXTMEM;
5
>
> mag er nicht, da wirft der Compiler immer> ../main.c:291: error: 'folge' undeclared here (not in a function)
klar.
weil die Variable ja auch jetzt 'data' heisst. folge ist nur noch einer
der beiden Member der union.
Vielleicht doch mal ein C-Buch durcharbeiten? Es programmiert sich
einfach besser, wenn man die Möglichkeiten seiner Programmiersprache und
wie man sie benutzt nicht nur zu 5% kennt.
Karl Heinz schrieb:> Vielleicht doch mal ein C-Buch durcharbeiten? Es programmiert sich> einfach besser, wenn man die Möglichkeiten seiner Programmiersprache und> wie man sie benutzt nicht nur zu 5% kennt.
Bin ich sofort dafür. An Literatur habe ich schon einiges angehäuft,
aber immer dann, wenn es an die etwas spezielleren Fälle geht, ist Ende
im Gelände oder das schon etwas ältere Köpfchen rafft es nicht. Ist auch
der Grund, dass ich immer wieder reumütig beim Assembler gelandet bin.
Da habe ich im Kopf, was ich jetzt mühsam versuche, dem Compiler
beizubiegen.
Welche Literatur ist denn zu empfehlen, die auch auf solche Sachverhalte
allgemeinverständlich eingeht?
>> Das:>>> union {>> folge_t folge[255];>> unsigned char folgeBytes[ sizeof folge ]; << Zeile 291>> } data EXTMEM;>> > mag er nicht, da wirft der Compiler immer>> ../main.c:291: error: 'folge' undeclared here (not in a function)>> klar.> weil die Variable ja auch jetzt 'data' heisst. folge ist nur noch einer> der beiden Member der union.
Bei mir ist nix klar, ich raffe es immer noch nicht.
Ich definiere eine union mit Namen "data", bestehend aus einem Array des
Typs folge_t mit Namen "folge" und 255 Elementen. Darüber wird ein
zweites Array aus unsigned char mit Namen "folgebytes" gelegt. Die Größe
(sizeof) des gerade definierten Arrays "folge" kennt der Compiler aber
nicht. Und ich begreife nicht, warum?
Ich glaube, der Zeitpunkt für einen Rückfall zum Assembler ist nicht
mehr weit. Dann kann ich in eineem Speicher von 32768 Bytes auch wieder
ein Array mit 256 Sätzen zu 128 Bytes anlegen....
Thomas
Thomas P. schrieb:> Bei mir ist nix klar, ich raffe es immer noch nicht.
Mein Fehler. Ist halt immer besser, wenn man selber 'an den Kontrollen
sitzt' :-)
An dieser Stelle kann ich mich noch nicht auf den Member folge beziehen
und kann daher auch noch nicht sizeof benutzen.
1
union{
2
folge_tfolge[255];
3
unsignedcharfolgeBytes[255*sizeof(folge_t)];
4
}dataEXTMEM;
ich nehm bei einem sizeof immer lieber den Variablennamen als den
Dtaentypnamen. Denn wenn eine Variable mal ihren Typ wechselt, hab ich
so weniger Arbeit in der Anpassung. Aber hier gehts nicht anders. Einzig
die 2-fache Erwähnung von 255 ist mir noch ein Dorn im Auge
Ist nicht relevant, das habe ich auch gerade durch probieren
herausgefunden. So langsam kommt Licht in das Dunkel an dieser Stelle;
wird aber nicht lange dauern, dann wird es an anderer Stelle wieder
finster bei mir.
Vielen Dank für die Hinweise.
Thomas
Der Compiler, das unbekannte Wesen:
Ich dachte, dass ich mit der Datendefinition nun durch bin. Theoretisch
ja, aber...
In den Datendefinitionen steckt unter anderem der Teil:
1
{
2
unsignedintrest_oben:6;
3
unsignedintrm_nr:10;
4
}lm_dat_t;
Dabei hatte ich mir gedacht, dass die Anordnung der Daten dazu führt,
dass ein Datentransfer aus dem EXTRAM mit wenigen Befehlen möglich wird.
Pustekuchen, es ergibt sich:
so03 = folgedata.folge[250].lm_dat[3].rm_nr;
1ee: 90 91 56 9f lds r25, 0x9F56
1f2: 92 95 swap r25
1f4: 96 95 lsr r25
1f6: 96 95 lsr r25
1f8: 93 70 andi r25, 0x03 ; 3
1fa: 20 91 57 9f lds r18, 0x9F57
1fe: 82 2f mov r24, r18
200: 88 0f add r24, r24
202: 88 0f add r24, r24
204: 89 2b or r24, r25
206: 22 95 swap r18
208: 26 95 lsr r18
20a: 26 95 lsr r18
20c: 23 70 andi r18, 0x03 ; 3
20e: 80 93 04 02 sts 0x0204, r24
212: 20 93 05 02 sts 0x0205, r18
Ändert man die Definition in:
1
{
2
unsignedintrm_nr:10;
3
unsignedintrest_oben:6;
4
}lm_dat_t;
dann wird der Code deutlich kompakter:
so03 = folgedata.folge[250].lm_dat[3].rm_nr;
1f0: 90 91 56 9f lds r25, 0x9F56
1f4: 80 91 57 9f lds r24, 0x9F57
1f8: 83 70 andi r24, 0x03 ; 3
1fa: 90 93 04 02 sts 0x0204, r25
1fe: 80 93 05 02 sts 0x0205, r24
Wenn solche Probleme öfters vorkommen, sehe ich selbst mit einem
16MHz-ATMega2560 unter C gegen den alten 4MHz-DS80C320 mit Assembler
keinen Stich.
Die Frage: Wie kann man dem Compiler helfen, besseren Code zu erzeugen?
Wie macht ihr das? Gibt es dazu Literatur/Tutorials?
Kann ja nicht im Snne des Erfinders sein, das .lss-File auf solche
Probleme zu flöhen.
Thomas
Thomas P. schrieb:> Wie macht ihr das?
Erstmal programmiert man nur die Funktion.
Und wenn das läuft, guckt man, wo es klemmt. Also was muß besonders
schnell sein oder was wird besonders oft aufgerufen.
Versuche in Funktion zu denken.
D.h. versuche nicht das Asssemblerprogramm Zeile für Zeile in C
nachzuschreiben.
Am besten schaltet man den PC ab und erstellt erstmal einen
Programmablaufplan mit Papier und Bleistift.
Einen Programmablaufplan habe ich schon, wenn auch noch aus der Zeit der
Entstehung des Assembler-Programms. Auch in Funktionen habe ich damals
gedacht, bin aber beim Programmieren immer so herangegangen, dass jede
Funktion programmiert und getestet und dann nicht mehr angefasst wurde.
Die Funktionen waren dann schon nahe beim Optimum, da meinereiner immer
zu faul war, viele Zeilen Coding zu schreiben, wenn es auch einfacher
ging. Und in Assembler korreliert die Anzahl der Befehle im Allgemeinen
mit der Laufzeit.
Und so wollte ich auch hier wieder vorgehen: Funktion schreiben, testen
und weglegen/benutzen. Die Neugier hat mich zum Betrachten des Listings
getrieben und das Ergebnis war erschütternd. Eine Zeile Coding, 40 Bytes
Assembler für eine Sache, die problemlos mit 18 Bytes zu erschlagen wäre
- und das nur wegen der Reihenfolge der Datendefinition. Ich würde gerne
mal eine Baustelle (hier Datendefinition) endgültig abschließen um mich
dann in Ruhe der nächsten zu widmen, ohne am Schluss des Projektes
wieder von vorne anzufangen. Aber so langsam glaube ich zu verstehen,
warum hier viele unter einem ARM nicht mehr anfangen.
Thomas
Ich muß vorausschicken, daß ich von AVR-Assembler keine Ahnung habe,
aber wenn Du das mit Assembler programmiert hättest, hättest Du deine
Datenstruktur doch wahrscheinlich auch gleich so hingeschrieben, daß der
10-Bit Teil "rechtsbündig" gestanden wäre, damit Du an die oberen 2 Bit
durch einfaches Ausmaskieren kommst?
Natürlich, aber eigentlich definiert man doch von oben nach unten,
respektive von links nach rechts. Wenn also in einem Bereich von 16 Bits
zuerst 6 ungenutzte Bits stehen und dann eine Adresse mit 10 Bit, dann
bin ich bisher davon ausgegangen, dass die 10 Bits schon rechtsbündig
stehen > error. Man muss erst einmal herausfinden, dass der Compiler
sehr eigene Ansichten dazu hat.
Nach längerer Suche zu einer Beschreibung habe ich nach "das Verhalten
ist compilerspezifisch" den Versuch aufgegeben. Bei drei oder mehr
Member der struct habe ich keine Logik in der Anordnung entdeckt, bin
aber durch Probieren und anschließendes Betrachten des
Assembler-Listings zu einer zufriedenstellenden Anordnung gekommen.
Hoffentlich ändert der Compiler in einer neuen Version nicht seine
Ansichten... Das wäre u.U. tödlich für abgelegte Daten bzw. die
Folgeprozesse.
Thomas
Thomas P. schrieb:> Eine Zeile Coding, 40 Bytes> Assembler für eine Sache, die problemlos mit 18 Bytes zu erschlagen wäre
Da geht jedem Umsteiger so, aber das muß man schnell ablegen. C braucht
ab und zu etwas mehr Zyklen, das ist normal.
Die Frage ist doch nur, spielt es überhaupt eine Rolle?
22 Byte = 11 Befehle = 0,55µs[20MHz], reißt es das wirklich raus?
Vielleicht kannst Du ja mal diese super kritische Funktion beschreiben,
die so absolut sauschnell sein muß.
Oftmals findet sich ja eine andere Herangehensweise, die alles
entschärft.
Ich hatte das auch öfter, daß durch nochmal nachdenken plötzlich das C
Programm Däumchen drehte, wo in Assembler nichtmal mehr ein NOP
reinpaßte.
Thomas P. schrieb:> Wenn also in einem Bereich von 16 Bits> zuerst 6 ungenutzte Bits stehen und dann eine Adresse mit 10 Bit
CPUs mögen Vielfache von einem Byte.
Wenn der RAM nicht super knapp ist, kann es was bringen, wenn man
aufrundet, also 10 auf 16 und 6 auf 8 Bit.
Du wirst oft in anderen AVR-Programmen sehen, daß Flags ein ganzes Byte
groß sind. Das ist effektiver, da der AVR keine Bitbefehle kennt, wie
sie der 8051 hat.
@Thomas
Wenn Du erlaubst, möchte ich Dir raten, die aus Deiner Sicht "seltsamen"
Dinge in C nur zur Kenntnis zu nehmen (evtl. 'ne kleine Liste machen)
und erstmal keine Wertungen zu treffen.
So schaffst Du die Möglichkeit C einmal insgesamt und in einem
praxisrelevanten Bezug zu sehen. Dazu musst Du natürlich erstmal C
lernen.
Wenn ich bei jedem kleinen Schritt auf die Hindernisse achte, dann ist
die Welt (egal ob ASM oder C) voller Probleme. Verdamme C meinetwegen am
Ende, aber dann bitte fundiert. - Würde mich nicht wundern, wenn Du aber
dann doch mal ab und zu C verwendest.
Ich benutzte übrigens beides. Ich glaube so wird es im Endeffekt einer
Menge Leute gehen.
Thomas P. schrieb:> Man muss erst einmal herausfinden, dass der Compiler> sehr eigene Ansichten dazu hat.
Nö, man muß nur wissen, daß es nicht definiert ist. Daher wirst Du kaum
Programme finden mit Datenbreiten != n*8Bit.
Wenn es darauf ankommt, maskiert man die Werte selber mit & bzw. |
heraus.
Erstmal danke für alle Hinweise.
Ich habe schon begriffen, dass die Herangehensweise unter C eine andere
sein sollte und man auch eine gewisse Gelassenheit entwickeln muss.
Das sind für mich als Hobbyprogrammierer aber schon gravierende
Umstellungen, da ich gewohnt bin, ein ganzes Stück voraus zu denken.
@Bitflüsterer:
Eine Liste habe ich schon ;-)) und mit deren Hilfe versuche ich, die mir
ungewohnten Verhaltensweisen im Blick zu behalten. Eine Wertung im Sinn
"ist so, gefällt mir aber ganz und gar nicht" gibt es aber auch schon.
Es geht mir auch nicht darum, C zu verdammen. Die Vorteile sind mir
schon klar, sonst hätte ich damit garnicht erst beschäftigt, aber leider
wächst im Moment die Seite mit den (aus meiner begrenzten Sicht)
Absonderlichkeiten an.
@ Peter Dannegger:
Ich hatte unter der alten Hardware 32kByte RAM zur Verfügung und die
waren gut ausgenutzt. Ohne Bitschubserei im RAM hätte die Software dort
garnicht funktionieren können. Am liebsten wäre mir auch die neue
Hardware unter einem 8051-Derivat gewesen - eben wegen den
leistungsfähigen Bitbefehlen. Da ich da aber nichts gefunden habe, was
meine Anforderungen erfüllt und ich zwischendurch im
AVR-Assemblerbereich Erfahrungen gesammelt habe, fiel die Wahl auf einen
ATMega und auf Grund der benötigten 3 seriellen Schnittstellen auf einen
ATMega2560. Hardware ist fertig und Assembler war gesetzt, aber dann kam
die Idee, es doch mal mit C zu versuchen.
Ich habe derzeit ein Array[64] (in Zukunft [255]) in Verwendung, welches
für jeden Satz 32 Aktionen enthält. Die Entscheidung über Aktiv oder
Inaktiv wird über Bedingungen getroffen, die zu jedem Satz mit zweimal
uint16_t [4] hinterlegt sind. Bei einem Durchlauf muss ich also im worst
case 255*4*2 Mal diese Bitmanipulation vornehmen und das Ergebnis
untersuchen und weiter verarbeiten.
Daneben gilt es noch 2 serielle Schnittstellen permanent zu betreiben,
zeitnah ein LCD zu bedienen und eine Tastatur zu überwachen sowie
diverse Kleinigkeiten mehr. Eine weitere Ausbaustufe wäre der Betrieb
der 3. seriellen Schnittstelle. Unter asm wäre das unter Zeitaspekten
auch noch mit dem DS80C320 lösbar gewesen, allerdings war der RAM alle
und die 3. serielle Schnittstelle nicht da.
Und aus diesen Gründen habe ich schon das Gefühl, nicht gleich am Anfang
mit "Was-kostet-die-Welt" heranzugehen zu können und den
Ressourcenverbrauch möglichst gering zu halten. Bisher habe ich mich mit
dem anderen Prozessor auf der sicheren Seite gewähnt, werde aber langsam
unsicher, ob das auch wirklich so ist.
Thomas
Thomas P. schrieb:> Wenn also in einem Bereich von 16 Bits> zuerst 6 ungenutzte Bits stehen und dann eine Adresse mit 10 Bit, dann> bin ich bisher davon ausgegangen, dass die 10 Bits schon rechtsbündig> stehen > error
Naheliegend wäre für mich gewesen, wenn mich 6 Bits nicht interessieren,
die auch nicht hinzuschreiben - ich würde dann davon ausgehen, daß der
Compiler mit dem Rest so umgeht, wie es für ihn am günstigsten ist.
Meine Compiler machen das auch so.
Wenn man allerdings wirklich sicher gehen will, sollte man - wie schon
richtig geschrieben - die Sache selber in die Hand nehmen.
Thomas P. schrieb:> @Bitflüsterer:> Eine Liste habe ich schon ;-)) und mit deren Hilfe versuche ich, die mir> ungewohnten Verhaltensweisen im Blick zu behalten. Eine Wertung im Sinn> "ist so, gefällt mir aber ganz und gar nicht" gibt es aber auch schon.> Es geht mir auch nicht darum, C zu verdammen. Die Vorteile sind mir> schon klar, sonst hätte ich damit garnicht erst beschäftigt, aber leider> wächst im Moment die Seite mit den (aus meiner begrenzten Sicht)> Absonderlichkeiten an.
Das Wort "verdammen" war zunächst nur als Synonym zu "abschliessend als
unbrauchbar beurteilen" gemeint. Ich bedauere diesen Scherz, der
anscheinend so nicht angekommen ist.
Es geht mir weniger um die Liste an sich und auch nicht um Deine
Entscheidung, als darum, Dich dahin zu bringen, ein Urteil nicht anhand
einzelner Punkte und aufgrund begrenzter Sicht zu fällen. Ob Du C nun
ablehnst oder nicht, ist mir gleichgültig.
Aber ich wollte Dich anregen dies aufgrund fundierter Kenntnis und
Erfahrung zu machen.
Thomas P. schrieb:> zeitnah ein LCD zu bedienen und eine Tastatur zu überwachen sowie> diverse Kleinigkeiten mehr.
Solange das kein GLCD ist, sind das nur Peanuts.
Du hast zwar viel erzählt, das entscheidende fehlt aber, nämlich die
Zeit.
Ohne Zeitvorgaben klingt das alles nicht nach hoher CPU-Last.
Thomas P. schrieb:> Unter asm wäre das unter Zeitaspekten> auch noch mit dem DS80C320 lösbar gewesen, allerdings war der RAM alle
Das verstehe ich nicht.
Der DS80C320 kann 64kB XDATA adressieren + 256B IDATA.
Beim ATmega2560 ist es sogar weniger (64kB - 512B für IO).
Ja, ein DS80C320 'kann' mehr Speicher adressieren, in der alten Hardware
sind aber nur 32kByte verbaut. Auf Grund der Pinknappheit dieser Dinger
hatte man damals vor mehr als 20 Jahren Register 74HC573 Memory-mapped
mit unvollständiger Decodierung verwendet. Der Ram war batteriegestützt
für den Erhalt der Stammdaten.
Diese Gegebenheiten sollten modernisiert werden und dabei auch der
externe EPROM verschwinden (Kopierschutz light). Heute gibt es eben z.B.
als RAM einen HM628512 und die drei Bits für A16-A18 waren am Prozessor
auch noch frei - ohne zusätzliches Latch.
So ganz allein bin ich bei den Entscheidungen zur Hardware nicht auf der
Welt und mir wäre aus Sicht der Programmierung auch ein DS89C450 lieber
gewesen. Allerdings traue ich Maxim bei der Lieferfähigkeit nicht so
recht über den Weg und mindestens 13€ teurer sind die Dinger auch noch.
Und eine dritte (und ev. vierte) serielle Schnittstelle direkt im Chip
ist weder für Geld noch für gute Worte zu bekommen - das hätte wieder
neue externe Hardware bedeutet.
Zu den Zeitvorgaben:
Kann ich nicht genau sagen (ich weiß: "ganz schlecht"), da abhängig von
den Ereignissen in den externen Geräten über beide (über drei in
Zukunft) serielle Schnittstellen Daten einlaufen, die analysiert,
abgespeichert und ggf. an eine andere Schnittstelle weitergegeben und
angezeigt werden müssen. Da kann minutenlang mal garnichts passieren und
dann kommt es fortlaufend hintereinander.
In der alten Hardware wurde die Tastenbedienung gefühlt ab und an recht
"träge" und da hatte ich mal versucht, eine Led so einzubinden, dass
damit bestimmte kritische Abschnitte der Software signalisiert werden.
Die Led war nicht oft aus... (Und ja, ich habe mir das mit einem Oszi
angeschaut, nicht dass mir jetzt noch einer erklärt, dass ich das
optisch garnicht richtig wahrnehmen kann).
Dazu kommt auch noch, dass ich kein Informatiker sondern Maschinenbauer
bin, mir also bestimmte theoretische Voraussetzungen für das
Ausquetschen eines Prozessors bis auf das letzte Prozent Leistung fehlen
werden.
Thomas
PS: Fällt mir gerade wieder ein: Ein weiteres Problem der Dallas-MC war
die Taktbereitstellung der seriellen Schnittstellen, mit 62,5k und 9,6k
gab es nicht viel Spielraum bei der Wahl des Systemtakts.
Und bevor die Notwendigkeit dazu wieder in Frage gestellt wird: Das sind
Vorgaben durch externe Geräte, deren Designänderung absolut keine Option
ist.
Irgendwann hatte keiner mehr Lust auf die Dinger und ein 8051 mit der
Ausstattung eines ATMega2560 ist uns nicht bekannt.
@ Thomas P. (topla)
>Kann ich nicht genau sagen (ich weiß: "ganz schlecht"),
EBEN!
> da abhängig von>den Ereignissen in den externen Geräten über beide (über drei in>Zukunft) serielle Schnittstellen Daten einlaufen, die analysiert,>abgespeichert und ggf. an eine andere Schnittstelle weitergegeben und>angezeigt werden müssen.
Klingt nicht so dramatisch.
Welche Baudrate?
Welche Datenpaketgröße?
Was muss ungefähr analysiert werden?
Was muss ungefähr angezeigt werden? Wie oft?
> Da kann minutenlang mal garnichts passieren und>dann kommt es fortlaufend hintereinander.
Wie oft? 1 Datenpaket/s?
So what. Packt man halt einen Software-FIFO an jeder Schnittstelle.
Peter Fleury & Co lassen grüßen. Das man hier auch das Thema
Interrupt beherrschen muss, ist selbstredend.
>In der alten Hardware wurde die Tastenbedienung gefühlt ab und an recht>"träge"
Schlechte Programmierung.
>Dazu kommt auch noch, dass ich kein Informatiker sondern Maschinenbauer>bin, mir also bestimmte theoretische Voraussetzungen für das>Ausquetschen eines Prozessors bis auf das letzte Prozent Leistung fehlen>werden.
Das kommt dem Problem schon näher. Aber niemand muss hier die CPU bis
auf das letzte Prozent ausquetschen. Ein AVR @ 20 MHz hat soviel Power,
das können sich die meisten Bastler/Quereinsteiger gar nicht vorstellen!
Das Hauptproblem ist das richtige Konzept bzw. Grundlagenwissen. Das da
lautet Multitasking und Statemachine. Damit kriegt man
eigentlich alles sauber und flüssig hin.
Mal als Beispiel. Ich hab hier vor einiger Zeit einen DMX-Rekorder
gebaut, dort wurden DMX-Daten mit 22kB/s auf eine SD-Karte geschrieben,
nebenbei lief noch ein Menu mit Drehgeberbedienung. Nix hat gehakt, lief
immer flüssig, auch bei stellenweise 90% CPU-Last. Man muss nur wissen
wie.
>Irgendwann hatte keiner mehr Lust auf die Dinger und ein 8051 mit der>Ausstattung eines ATMega2560 ist uns nicht bekannt.
Nimm den AVR und mach es in C. Auch wenn der Umstieg mit Anstrengung
verbunden ist, wenn es fertig ist wirst du die Eleganz und
Leistungsfähigkeit sehen und du hast dann viel gelernt!
Viel Spaß und Erfolg
Falk
P S Ich wette mal, dass sich der AVR zu 99% langweilt.
Thomas P. schrieb:> nd eine dritte (und ev. vierte) serielle Schnittstelle direkt im Chip> ist weder für Geld noch für gute Worte zu bekommen
Ja, da sind die 8051-er recht geizig mit.
Und mal eben ne Software-UART mit Input-Capture und Output-Compare wie
beim AVR geht auch nicht so leicht.
Wir haben auch noch ein Altgerät mit DS80C320-QCL (33MHz), bei den
Mondpreisen wird mir ganz schlecht.
Thomas P. schrieb:> serielle Schnittstellen Daten einlaufen, die analysiert,> abgespeichert und ggf. an eine andere Schnittstelle weitergegeben und> angezeigt werden müssen.
Da kennst Du ja die Baudrate und die Anzahl Bytes für einen Befehl.
Schneller geht es nicht.
Thomas P. schrieb:> In der alten Hardware wurde die Tastenbedienung gefühlt ab und an recht> "träge"
Fürs Tastenentprellen empfehle ich einen Timerinterrupt, der
Ereignis-Flags setzt, die dann die Mainloop auswertet.
Damit gehen Drücke nicht verloren und die Mainloop wird nicht
ausgebremst.
Thomas P. schrieb:> Ausquetschen eines Prozessors bis auf das letzte Prozent Leistung
Laß das.
premature optimization is the root of all evil
Falk Brunner schrieb:> Nimm den AVR und mach es in C. Auch wenn der Umstieg mit Anstrengung> verbunden ist, wenn es fertig ist wirst du die Eleganz und> Leistungsfähigkeit sehen und du hast dann viel gelernt!
Und wenn Du nach einem Jahr an dem Projekt noch etwas hinzufügen sollst,
wirst Du dich freuen, wie Bolle, daß es in C ist.
Die ganzen internen Assemblertricks hättest Du dann längst vergessen.
@ Thomas P. (topla)
>Und so wollte ich auch hier wieder vorgehen: Funktion schreiben, testen>und weglegen/benutzen. Die Neugier hat mich zum Betrachten des Listings>getrieben und das Ergebnis war erschütternd. Eine Zeile Coding, 40 Bytes>Assembler für eine Sache, die problemlos mit 18 Bytes zu erschlagen wäre>- und das nur wegen der Reihenfolge der Datendefinition.
Vergiss diese Erbsenzählerrei!
https://www.mikrocontroller.net/articles/AVR-GCC-Codeoptimierung#Prinzipien_der_Optimierung> Ich würde gerne>mal eine Baustelle (hier Datendefinition) endgültig abschließen um mich>dann in Ruhe der nächsten zu widmen, ohne am Schluss des Projektes>wieder von vorne anzufangen. Aber so langsam glaube ich zu verstehen,>warum hier viele unter einem ARM nicht mehr anfangen.
Ach was, du malst nur den Teufel an die Wand. Du kümmerst dich viel
zuviel um Kleingkeiten und klebst viel zuviel an deinem Assembler.
Falk Brunner schrieb:> @ Thomas P. (topla)> Klingt nicht so dramatisch.>> Welche Baudrate?> Welche Datenpaketgröße?> Was muss ungefähr analysiert werden?> Was muss ungefähr angezeigt werden? Wie oft?
Dramatisch ist was anderes, dank effizienter Programmierung reißt es ja
der DS80C320@4MHz im Allgemeinen auch.
- 62,5kB 9Bit
- Datenpaketgröße variabel bis 15 Byte
- Art des Paketes analysieren,
- bei Relevanz Inhalt analysieren und in das Array schreiben
- Relevanz für zweite Schnittstelle ermitteln, Daten zusammenbauen
und in den Senderingpuffer stellen
- Relevanz für Anzeige (momentan dargestellter Bereich) ermitteln
und ggf. ausgeben
> Wie oft? 1 Datenpaket/s?
Nachrichten auf dem Bus laufen ca. aller 2ms, das sind sog. Token mit
der Adresse (9.Bit). Über Int-Einstellungen werden alle nicht
zutreffenden Token ausgefiltert. Abhängig von der Anzahl der
Busteilnehmer wird das Gerät also zwischen 4 und max. 45ms angesprochen,
Mittelwert 10ms.
> So what. Packt man halt einen Software-FIFO an jeder Schnittstelle.> Peter Fleury & Co lassen grüßen. Das man hier auch das Thema> Interrupt beherrschen muss, ist selbstredend.
Interruptgesteuert läuft das ohnehin, ich wüsste nicht, wie man das
sonst beherrschen könnte.
Problem ist, dass die eingehenden Daten verschiedene Bedeutung haben
können. Grob sind das das Token (als Sendeaufforderung), eine
Information für das Gerät oder eine Information an alle Geräte. Bekommt
das Gerät das Token, kann es eine Anfrage (gibt eine Antwort) oder eine
Information senden (gibt keine Antwort). Bei einer Anfrage kann aber
zwischen dieser und der zugehörigen Antwort auch eine allgemeine
Information stecken.
> Schlechte Programmierung.
Das war ich nicht. Puh, mal Glück gehabt.
> Viel Spaß und Erfolg> Falk
Danke, auf Grund der fertigen Hardware wird das wohl auch so werden.
> P S Ich wette mal, dass sich der AVR zu 99% langweilt.
Würde ich mal dagegen halten. ;-))
Falk Brunner schrieb:> @ Thomas P. (topla)> Vergiss diese Erbsenzählerrei!>> https://www.mikrocontroller.net/articles/AVR-GCC-Codeoptimierung#Prinzipien_der_Optimierung
Ja, ist vielleicht ein Geburtsfehler bei mir. Ich mag auch vorzeigbares
Innenleben, bei Geräten wie bei Software.
> Ach was, du malst nur den Teufel an die Wand. Du kümmerst dich viel> zuviel um Kleingkeiten und klebst viel zuviel an deinem Assembler.
Hmm, mag sein, aber in diesem Zusammenhang geht mir folgendes Szenario
durch den Kopf: Im externen EEPROM sind Stammdaten abgelegt, die beim
Einschalten in den RAM geladen werden und ein Array füllen. Dabei bin
ich zwingend darauf angewiesen, dass auch bei neuen Programmversionen
die Datenlage im externen EEPROM exakt mit der vom Programm erwarteten
im RAM übereinstimmt. Ist jetzt der Compiler mal anderer Meinung zur
Datenanordnung, geht das voll in die Hose.
Peter Dannegger schrieb:> Und wenn Du nach einem Jahr an dem Projekt noch etwas hinzufügen sollst,> wirst Du dich freuen, wie Bolle, daß es in C ist.> Die ganzen internen Assemblertricks hättest Du dann längst vergessen.
Diese Erkenntnis führte ja zur Entscheidung, es hier mal mit C zu
versuchen.
Bis jetzt ging es mit Assembler ganz gut, dank übersichtlicher und
modularer Programmierung (und bis zum Alzheimer ist es hoffentlich noch
eine Weile hin). Nervig ist das Verwalten von Flagbits und Registern.
Thomas
@Thomas P. (topla)
>- 62,5kB 9Bit>- Datenpaketgröße variabel bis 15 Byte
Wenig.
>- Art des Paketes analysieren,> - bei Relevanz Inhalt analysieren und in das Array schreiben> - Relevanz für zweite Schnittstelle ermitteln, Daten zusammenbauen> und in den Senderingpuffer stellen> - Relevanz für Anzeige (momentan dargestellter Bereich) ermitteln> und ggf. ausgeben
Das muss aber sicher nicht im 2ms Raster passieren, dann das kann keiner
lesen. 10 Hz Updaterate erscheinen da als Obergrenze sinnvoll, ggf.
sogar weniger.
> Wie oft? 1 Datenpaket/s?>Nachrichten auf dem Bus laufen ca. aller 2ms, das sind sog. Token mit>der Adresse (9.Bit). Über Int-Einstellungen werden alle nicht>zutreffenden Token ausgefiltert. Abhängig von der Anzahl der>Busteilnehmer wird das Gerät also zwischen 4 und max. 45ms angesprochen,>Mittelwert 10ms.
Bei 2ms Periodendauer, 15 Bytes/Paket und 62,5 kBaud stimmt was nicht.
Denn es gehen maximal 5682 Bytes/s über den Bus, macht in 2ms
bestenfalls 11 Bytes. Also werden viele Pakete deutlich kleiner als
15Bytes sein.
>Interruptgesteuert läuft das ohnehin, ich wüsste nicht, wie man das>sonst beherrschen könnte.
Es geht auch über Polling und eine Statemachine.
>Problem ist, dass die eingehenden Daten verschiedene Bedeutung haben>können.
Ist das nicht immer so?
> Grob sind das das Token (als Sendeaufforderung), eine>Information für das Gerät oder eine Information an alle Geräte. Bekommt>das Gerät das Token, kann es eine Anfrage (gibt eine Antwort) oder eine>Information senden (gibt keine Antwort). Bei einer Anfrage kann aber>zwischen dieser und der zugehörigen Antwort auch eine allgemeine>Information stecken.
Ja und? Klingt dennoch nicht wirklich wild. Im Extremfall packt man das
alles in große Tabellen, du hast ja mit dem neuen Prozessor mehr als
genug Flash.
@Thomas P. (topla)
>ich zwingend darauf angewiesen, dass auch bei neuen Programmversionen>die Datenlage im externen EEPROM exakt mit der vom Programm erwarteten>im RAM übereinstimmt. Ist jetzt der Compiler mal anderer Meinung zur>Datenanordnung, geht das voll in die Hose.
Darum legt man diese Datenstruktur in einem struc an, dort DARF der
Compiler nichts verschieben, er MUSS es EXAKT so abbilden, wie es
vorgegeben ist.
>eine Weile hin). Nervig ist das Verwalten von Flagbits und Registern.
Und vieles mehr. Assembler ist heutzutage wirklich nur noch in GANZ
kleinen Nischen sinnvoll, wo es WIRKLICH auf 100% volle Leistung bzw.
absolut exaktes Timing ankommt. Wenn man ein paar grundlegende Konzepte
zur sinnvollen Variablenauswahl und Verarbeitung kennt (Siehe mein Link
weiter oben), ist C in 99% der Fälle vollkommen ausreichend.
Falk Brunner schrieb:> @Thomas P. (topla)> Das muss aber sicher nicht im 2ms Raster passieren, dann das kann keiner> lesen. 10 Hz Updaterate erscheinen da als Obergrenze sinnvoll, ggf.> sogar weniger.
Ja natürlich. Ich habe das jetzt nicht in epischer Breite ausgewalzt.
Wird die eingetroffene bzw. ermittelte Information als relevant
betrachtet, wird das Ergebnis in einen Ausgabepuffer geschrieben und im
Zeitraster von ich-weiß-jetzt-nicht-genau-irgendwas um 15Hz ausgegeben.
>>Nachrichten auf dem Bus laufen ca. aller 2ms, das sind sog. Token mit>>der Adresse (9.Bit). Über Int-Einstellungen werden alle nicht>>zutreffenden Token ausgefiltert. Abhängig von der Anzahl der>>Busteilnehmer wird das Gerät also zwischen 4 und max. 45ms angesprochen,>>Mittelwert 10ms.>> Bei 2ms Periodendauer, 15 Bytes/Paket und 62,5 kBaud stimmt was nicht.> Denn es gehen maximal 5682 Bytes/s über den Bus, macht in 2ms> bestenfalls 11 Bytes. Also werden viele Pakete deutlich kleiner als> 15Bytes sein.
Deswegen habe ich oben ja auch "bis" geschrieben. Je länger das
Telegramm, umso größer auch der Abstand zum nächsten Telegramm, geht ja
nicht anders. Verlängernd wirken natürlich auch allgemeine
Informationen.
>>Problem ist, dass die eingehenden Daten verschiedene Bedeutung haben>>können.>> Ist das nicht immer so?
Ich meine, dass die Art der einlaufenden Daten Einfluss auf die
Sendedaten nimmt. Ich kann also nicht einfach alles, was ich loswerden
will, hintereinander in einen Ringpuffer schreiben und abarbeiten,
sondern muss da flexibler reagieren. Im Moment ist das so gelöst, dass
das Programm halt auf die vollständige Abarbeitung einer Anfrage wartet.
Haben wir schon öfters diskutiert, hat aber auch keiner eine brauchbare
Lösung in den Ring geworfen. Wir sind aber alle keine Profis.
> Ja und? Klingt dennoch nicht wirklich wild. Im Extremfall packt man das> alles in große Tabellen, du hast ja mit dem neuen Prozessor mehr als> genug Flash.
Tabellen, variable Daten und Flash?
Jetzt hast Du mich total abgehängt.
Thomas
Thomas P. schrieb:> Dabei bin> ich zwingend darauf angewiesen, dass auch bei neuen Programmversionen> die Datenlage im externen EEPROM exakt mit der vom Programm erwarteten> im RAM übereinstimmt.
Geht nur, wenn Du zufällig in Assembler die gleiche Byteorder benutzt
hast, wie der AVR-GCC.
Wieviel Byte sind es denn?
Ich hatte bisher nie den Fall, daß ein anderes Tool den EEPROM
beschreibt.
D.h. immer nur die Anwendung hat den EEPROM geschrieben und dann ist die
Order egal.
Mir reichte bisher auch der internen EEPROM aus.
@ Thomas P. (topla)
>Ich meine, dass die Art der einlaufenden Daten Einfluss auf die>Sendedaten nimmt.
Sicher, sonst wären sie ja sinnlos ;-)
>Ich kann also nicht einfach alles, was ich loswerden>will, hintereinander in einen Ringpuffer schreiben und abarbeiten,
Warum nicht?
>sondern muss da flexibler reagieren.
???
> Im Moment ist das so gelöst, dass>das Programm halt auf die vollständige Abarbeitung einer Anfrage wartet.
Das klingt nicht gut, siehe Multitasking.
>Haben wir schon öfters diskutiert, hat aber auch keiner eine brauchbare>Lösung in den Ring geworfen. Wir sind aber alle keine Profis.
Dann beschreibe doch dein Aufgabe hier mal detailierter.
>Tabellen, variable Daten und Flash?>Jetzt hast Du mich total abgehängt.
Man kann im EXTREMFALL für jedes mögliche Eingangspaket exakt ein
zugehöriges Ausgangspaket in eine feste Tabelle packen. JA, die wird
dann riesig! Geht aber manchmal, ist halt ein ultimativer "Space for
Time" Ansatz. Damit muss man nicht die Ausgangsdaten kompliziert und
langwierig aus den Eingangsdaten berechnen/bitmanipulieren, sondern
wählt einfach die fertige Antwort aus der riesigen Tabelle aus.
Ob und wie man das hier machen könnte, weiß ich nicht. Aber wenn im
alten System ein 8051 Derivat mit 4 MHz die Aufgabe geschafft hat
(Maschinentakt ist 1/4 Oszillatortakt, siehe
http://www.8052.com/320intro.php
), kann das ein AVR mit 20 MHz locker.
Ich meine, dein Problem ist nach wie vor der ungünstige Lösungsansatz,
bedingt durch deine Quereinsteigerposition.
Peter Dannegger schrieb:> Geht nur, wenn Du zufällig in Assembler die gleiche Byteorder benutzt> hast, wie der AVR-GCC.> Wieviel Byte sind es denn?
Unter anderem deshalb habe ich mit der Anordnung der Daten in den strutc
so herumgerudert. Sind knapp 32k an Daten.
> Mir reichte bisher auch der internen EEPROM aus.
Ich weiß, ich grabe immer irgendwelchen Scheixx aus, aber ich finde ums
Verrecken den toten Vogel in meinen Taschen nicht.
Thomas
Thomas P. schrieb:> Unter anderem deshalb habe ich mit der Anordnung der Daten in den strutc> so herumgerudert. Sind knapp 32k an Daten.
Wenn es um Speicherplatz sparen geht, werden die structs gepackt, oder
sind unbenutzte/redundante Informationen enthalten welche mit Unions
überlagert werden könnten? Notfalls können die Werte im struct auch
manuell ins EEPROM geschrieben werden.
Falk Brunner schrieb:> @ Thomas P. (topla)>> Im Moment ist das so gelöst, dass>>das Programm halt auf die vollständige Abarbeitung einer Anfrage wartet.>> Das klingt nicht gut, siehe Multitasking.> Dann beschreibe doch dein Aufgabe hier mal detailierter.
Ist DV-technisch nicht der Brüller (das weiß ich schon), macht aber
technisch Sinn. Wenn ich eine Befehlsfolge abarbeite, dann bekomme ich
keine Quittung über den erfolgreichen Empfang. Ich bekomme lediglich
eine Rückmeldung zum Ergebnis der Ausführung meines Kommandos (oder auch
nicht, da begrenzt dann eine timeout-Funktion die Wartezeit). Bekomme
ich keine Antwort oder nicht die erwartete, wird der Vorgang für diese
Folge abgebrochen (und ein Fehler gesetzt) und die nächste
abzuarbeitende Folge gesucht.
In der Wartezeit kann ich jetzt weitere Token (als Sendemöglichkeit)
bekommen, es können die erwarteten Informationen eintreffen, es kann
eine Abfrage von Daten eintreffen (die sofort beantwortet werden muss)
oder es können allgemeine Informationen eintreffen (also quasi
unverlangte (aber relevante) Daten), die erstmal nur abgelegt werden
müssen.
Würde ich jetzt ein weiteres Sendefenster nutzen, wäre ein Mechanismus
zu implementieren, der die Zuordnung von gesendeten Daten zu empfangenen
Daten und einem eigenen Timeoutzähler ermöglicht. Einen eindeutigen
Identifikator zwischen Sende- und Empfangsdaten müsste man hinbekommen.
Fehlt die Antwort, wartet die weitere Ausgabe von Befehlen eben auf den
Timeout oder die passende Antwort. Aber wie man das brauchbar
gestaltet...?
Interruptgesteuert bediene ich die Ausgabe auf die zweite serielle
Schnittstelle aus einem Ringpuffer, muss aber auch dort zyklisch im
Polling Eingaben abholen und bearbeiten. Das Ergebnis könnte dann in den
Ringpuffer der ersten Schnittstelle.
> Man kann im EXTREMFALL für jedes mögliche Eingangspaket exakt ein> zugehöriges Ausgangspaket in eine feste Tabelle packen. ....
Ist nicht notwendig und scheidet aus.
> Ich meine, dein Problem ist nach wie vor der ungünstige Lösungsansatz,> bedingt durch deine Quereinsteigerposition.
Da wirst Du recht haben und deshalb versuche ich hier, so viele
Informationen wie möglich aufzusammeln.
Thomas
Daniel A. schrieb:> Wenn es um Speicherplatz sparen geht, werden die structs gepackt, oder> sind unbenutzte/redundante Informationen enthalten welche mit Unions> überlagert werden könnten? Notfalls können die Werte im struct auch> manuell ins EEPROM geschrieben werden.
Redundante Informationen nicht, unbenutzte schon.
Allerdings ist dieses Thema ja vom Tisch, ein 32kEEPROM und ausreichend
RAM machen es möglich.
Die Anordnung der Informationen im Speicher habe ich durch Probieren im
Griff - hoffe ich mal.
Thomas
Damit alles hier etwas zusammen bleibt, habe ich den Thread wieder
ausgebuddelt.
Nach Hinweisen hier habe ich eine eigene section für den externen RAM am
ATMEGA2560 angelegt und die Linker-Option eingestellt.
#define EXTMEM __attribute__((section(".EXTMEM")))
/* Linker option:
-Wl,--section-start,.EXTMEM=0x802200,--defsym=__heap_end=0x80ffff
Mit kleinen Datenmengen hat das auch gut funktioniert, allerdings kam
bei deutlicher Vergrößerung eines Arrays im .EXTMEM die Fehlermeldung,
dass das Programm zu groß sei und nicht geflasht werden kann.
Die Betrachtung des hex-Files ergab, dass der Bereich des Arrays dort
ebenfalls erscheint, aufgefüllt mit 0x00 und offensichtlich der
Initialisierung dient. Lösche ich diesen Bereich aus dem hex-File,
funktioniert alles wie gewünscht - ist nur etwas lästig nach jedem
build.
Da ich den EXTMEM-Bereich nicht zu initialisieren brauche, wäre
offensichtlich eine section .noinit die richtige Version gewesen.
Frage:
Muss ich zwingend die section EXTMEM in noinit ändern oder ist es
möglich, der section EXTMEM zusätzlich einen Parameter "noint" zu
verpassen? Und wenn ja, wie macht man das?
Thomas
Thomas P. schrieb:> Im externen EEPROM sind Stammdaten abgelegt, die beim Einschalten> in den RAM geladen werden und ein Array füllen. Dabei bin ich> zwingend darauf angewiesen, dass auch bei neuen Programmversionen> die Datenlage im externen EEPROM exakt mit der vom Programm erwarteten> im RAM übereinstimmt. Ist jetzt der Compiler mal anderer Meinung zur> Datenanordnung, geht das voll in die Hose.
Der Compiler ist nicht irgendeiner "Meinung", sondern folgt einem
Sprachstandard.
Konkret: Die Reihenfolge, in der Variablen ablegegt werden, ist nicht
spezifiziert und interessiert i.d.R auch nicht.
Wenn es interessiert, dann geht oft um einen Sack voll globaler
Daten-Flöhe, auf die auf eine bestimmte Weise zuzugreigen ist, und das
geht z.B. so:
Für die EEprom-Daten wird eine Struktur angelegt, welche die Daten
origanisiert, ich nenn den mal eeprom_t. Von diesem Ding gibt es dann 2
Instanzen: Eine im EEMEM:
1
#include<avr/eeprom.h>
2
3
constvolatileeeprom_teeprom_contentEEMEM={...};
sowie eine im RAM:
1
eeprom_teeprom_werte;
Nun können wie Werte Problemlos zwischen EEPROM und RAM konsistent
gehalten werden (modulo Problemen aufgrund der Spannungsversorgung),
z.B. eeprom_read_block und eeprom_update_block.
Danke für Deinen Beitrag. Löst jetzt zwar nicht das akute Problem mit
dem überlaufenden hex-File, erinnert mich aber an eine andere offene
Baustelle, die ich erstmal weiter nach hinten geschoben habe.
Für die Anordnung im EEMEM ist mir die Vorgehensweise auch klar, da der
Compiler den Ablauf für den Zugriff auf diese Daten kennt, aber wie
verhält sich das, wenn diese Daten in einem SPI-EEPROM liegen? Was muss
ich denn dann wie bereitstellen, damit der Compiler damit umgehen kann?
Das würde ja dann eine neue section bedeuten, die aber weder im sram, im
flash oder im eeprom liegt.
Thomas
Thomas P. schrieb:> [...] aber wie verhält sich das, wenn diese Daten in einem> SPI-EEPROM liegen? Was muss ich denn dann wie bereitstellen,> damit der Compiler damit umgehen kann? Das würde ja dann eine> neue section bedeuten, die aber weder im sram, im flash oder> im eeprom liegt.
Ich nehme mal an, dass dein µC den SPI nicht per DMA abbildet, sondern
dass das SPI "händisch" abläuft, d.h. es werden old-school bytes per SPI
desendet / empfangen. Wo erlche Daten hinkommen, implementiert dann die
[De]Serialisierung C-Daten <-> SPI.
Eine neue Section könnte sinnvoll sein wenn, wenn das SPI-EEprom z.B.
durch irgendwelche (Hardware-Magie) in den Adressraum gemappt würde.
Abel selbst dann wäre es wahrscheinlich ausreichend, die einzelnen Bytes
dieses RAM-gemappte SPI-EEproms wie SFRs zu behandeln.
Stephan schrieb:> Hi,> bin mir nicht sicher, aber sollte es nicht reichen:> -set-section-flags=.EXTMEM="NOLOAD">> zu setzen.
Das heißt also, dass ich ein externes Makefile benutzen muss, richtig?
Ich habe jetzt das automatisch erzeugte Makefile um die angegebene Zeile
im Bereich HEX_FLASH_FLAGS ergänzt, unter Optionen den Pfad zum Makefile
eingestellt und die Verwendung eines externen Makefiles angehakt:
Ergebnis ist, dass keinerlei Outputfiles erzeugt werden. Was habe ich
denn nun wieder verbrochen? Fehlermeldungen gibt es keine.
Thomas
PS: Auch ohne die eingefügte Zeile passiert bei der Wahl "externes
Makefile" garnichts.
Orischwernochbleedehier....
..ich verstehe diese EXTMEM nicht; wozu ist das gut?
Verstehe ich das richtig: Du willst keine Daten dort lokatieren, sondern
nur ein definiertes Abbild zugreifen?
Johann L. schrieb:> Ich nehme mal an, dass dein µC den SPI nicht per DMA abbildet, sondern> dass das SPI "händisch" abläuft, d.h. es werden old-school bytes per SPI> desendet / empfangen. Wo erlche Daten hinkommen, implementiert dann die> [De]Serialisierung C-Daten <-> SPI.
Richtig, kein DMA sondern SPI Byte für Byte. Dafür existiert eine union
mit den Daten der 255 Sätze zu je 128 Byte / unsigned char im Ram über
die immer auf einen kompletten Satz zugegriffen wird. Das Problem sehe
ich eben an der Stelle, wenn die abgelegten Daten pro Satz bei einer
neuen Programmversion z.B. die Reihenfolge ändern. Die Daten im Ram
werden dann aus dem SPI-EEPROM falsch versorgt.
Thomas
Thomas P. schrieb:> Jetzt ist die Hardware fertig und ich sinne darüber nach,> ob ich die Portierung [...] nach AVR-Assembler ausführe> oder mal wieder versuche, mit C zu beginnen.
Das schliesst sich nicht aus. Und lass dir folgenden Ratschlag ans Herz
legen:
Falls du dich für Assembler entscheidest, dann
1) Nimm auf jeden Fall den GNU-Assembler.
2) Implementiere dein Funktionen nach avr-gcc ABI.
Dann kannst du in der Zufunft ganz easy von Assembler nach C migrieren
oder umgekehrt -- und zwar für jede einzelne Funktion wie es dir
gefällt.
https://gcc.gnu.org/wiki/avr-gcc
3) Lass dir nicht einreden, diese ABI verwenden sei zu ineffizient.
Ja, es ist nicht so gut wie kaputt-optimierter Assembler-Code.
Aber das wird keine Rolle spielen — und wenn, hast du dich
vermutlich nicht für die richtige Hardware entschieden.
Ein "leeres" Programm kann nich dir bei Bedarf geben.
EXTMEM ist ein externer SRAM und eine eigene section existiert aus dem
Grund, dass nur so zu steuern ist, wo welche Daten landen. Ich möchte
ganz gezielt Variablen im internen oder externen Ram ablegen können.
Thomas
Im Moment reite ich die C-Welle und es geht auch ganz brauchbar voran,
wenn die kleinen, aber saugemeinen, Stolpersteine nicht dauernd vor die
Füße geraten würden. Isr halt das erste C-Projekt.
Thomas
Thomas P. schrieb:> Johann L. schrieb:>> [...]> Richtig, kein DMA sondern SPI Byte für Byte. Dafür existiert eine union
Warum eine Union?
> Das Problem sehe ich eben an der Stelle, wenn die abgelegten Daten> pro Satz bei einer neuen Programmversion z.B. die Reihenfolge ändern.> Die Daten im Ram werden dann aus dem SPI-EEPROM falsch versorgt.
Selbt bei der gleichen Tool-Version ist die Reihenfolge immer noch
nicht spezifiziert und hängt ab u.a. von der Verschalterung oder dem
eigentlichen Progamm.
Nochmals: Vermeide globalen Datenmatsch, lies das ABI, modelliere dein
Layout als Struct, und gut ist. Falls du unbedingt Matsch magst, wirst
du eine Lösung wählen müssen, die du später mal hassen wirst:
o Verwende ein eigenes Linker Description File und mach das
Layout händisch. Oder:
o Lege ein eigenes Assembler-Modul an, in dem die Daten definiert
werden. Anbei ein Header mit den C-Deklarationen. Oder:
o Alle Variablen in ein C-Modul wie du sie haben willst. Als
Schalter mindestens -fno-toplevel-reorder, mehr sagt dir die
gcc-Hilfe:
http://gcc.gnu.org/onlinedocs/gcc/Option-Summary.html
Johann L. schrieb:> Warum eine Union?> ...> Nochmals: Vermeide globalen Datenmatsch, lies das ABI, modelliere dein> Layout als Struct, und gut ist. Falls du unbedingt Matsch magst, wirst> du eine Lösung wählen müssen, die du später mal hassen wirst:
Ich glaube, soweit auseinander sind wir garnicht.
Die Daten sind schon nach Anleitung von hier als struct angelegt, die
union dazu für den byteweisen Umgang mit dem SPI-EEPROM und dieser
Schritt könnte zum Problem werden, wenn ich das richtig interpretiere:
> Selbt bei der gleichen Tool-Version ist die Reihenfolge immer noch> nicht spezifiziert und hängt ab u.a. von der Verschalterung oder dem> eigentlichen Progamm.
Das Ganze ist in einer definitions.h ausgelagert und funktioniert prima.
Thomas
Thomas P. schrieb:> Damit alles hier etwas zusammen bleibt, habe ich den Thread wieder> ausgebuddelt.>> Nach Hinweisen hier habe ich eine eigene section für den externen RAM am> ATMEGA2560 angelegt und die Linker-Option eingestellt.> #define EXTMEM __attribute__((section(".EXTMEM")))> /* Linker option:> -Wl,--section-start,.EXTMEM=0x802200,--defsym=__heap_end=0x80ffff>> Mit kleinen Datenmengen hat das auch gut funktioniert, allerdings kam> bei deutlicher Vergrößerung eines Arrays im .EXTMEM die Fehlermeldung,> dass das Programm zu groß sei und nicht geflasht werden kann.
Das wundert nicht, denn .data wird im Startup initialisiert aus dem
Flash. Wo steht das .EXTMEM im Linker-Script? Im Default-Skript find
ich da nix, d.h. dein .EXTMEM ist eine Input-Section, der keine
Output-Section zugeordnen ist (Orphan).
Weiteres Problem: Der Location-Pointer (.) des Linkers kann nicht
rückwärts wandern. Wenn er also ein Objekt nach .EXTMEM legt kann es
sein dass andere Objekte danach lokatert werden und ebenfalls in
.EXTMEM landen, was du dann vermutlich nicht willst.
Ergo: Du willst
o eine eigene Linker-Description, sehr wahrscheinlich mit eigener
Output-Section für externen Speicher.
o falls es nur ein Objekt im externen Speicher ist und das auch so
bleiben wird, dann ist folgendes abzuwägen:
1
externfolge_tfolgen[255];
und Linken mit --Wl,-defsym=folgen=0x2200. Für folgen[] gibt es
keine Definition auf C-Ebene, oder
o es gibt ein lineares Speichermodell, in dem sich der externe
Speicher nahtlos an den internen RAM anschließt. Es ist egal,
was in welchem Speicher landet, und explizites Attributieren
einzelner Objekte ist nötig.
> Die Betrachtung des hex-Files ergab, dass der Bereich des Arrays dort> ebenfalls erscheint, aufgefüllt mit 0x00 und offensichtlich der> Initialisierung dient. Lösche ich diesen Bereich aus dem hex-File,> funktioniert alles wie gewünscht - ist nur etwas lästig nach jedem> build.
Klaro, s.o. Händisch an Section-Flags rummachen würd ich abraten,
insbesondere
o Wenn es keine Output-Section ist. In es eine Output Section ist,
steht eh im ld-Skript, wie diese zu behandeln ist)
o Wenn es eine Orphan ist. In dem Falls bewirken andere Flags nur,
dass der Linker anders rät. Die Regeln für Orphans sind, naja
loes eben selbst nach :-)
> Muss ich zwingend die section EXTMEM in noinit ändern oder ist es> möglich, der section EXTMEM zusätzlich einen Parameter "noint" zu> verpassen?
Mach dich mal vertraut damit, was Input- Output- und Orphan-Sections
sind.
Zur Union: Das verunstaltet die Quellen, denn jedes Objekt, dessen
Adresse du einer Zugriffsfunktion übergibst, würde in eine Union gepackt
werden müssen. Stell dir vor, wie verunstaltet die Quellen wären, wenn
das für jedes Objekt genacht werden würde, das mit pgm_read_*
zugegriffen wird !
Ergo:
Hallo Johann,
vielen Dank für Deine ausführlichen Erklärungen.
Da ich jetzt nicht in die Niederungen des Linker-Scripts absteigen
wollte, habe ich mit meinem angelesenen Halbwissen die Definition von
EXTMEM wie folgt geändert:
#define EXTMEM __attribute__((section(".noinit")))
Dazu noch:
/* Linker option:
-Wl,--section-start,.noinit=0x802200,--defsym=__heap_end=0x80ffff
So funktioniert das einwandfrei, EXTMEM im batteriegepufferten
SRAM-Bereich wird nicht initialisiert und damit sind auch die Tonnen von
Nullbytes aus dem Flash (hex-File) verschwunden.
Johann L. schrieb:> Zur Union: Das verunstaltet die Quellen, denn jedes Objekt, dessen> Adresse du einer Zugriffsfunktion übergibst, würde in eine Union gepackt> werden müssen. Stell dir vor, wie verunstaltet die Quellen wären, wenn> das für jedes Objekt genacht werden würde, das mit pgm_read_*> zugegriffen wird !
Ja, stimmt und Deinen Hinweis werde ich an der Stelle umsetzen.
Nochmals vielen Dank für Deine Hinweise.
Thomas