Hallo ich schreibe eine Klausur über Mikrocontroller,
ich habe viel gelernt allerdings bin ich mir mit der Aufgaben lsg. von
mir so unsicher bzw. denke das es Falsch ist wollte mal lieb bitte
drüber zu schauen.
Vielen dank euch allen.
Aufgabe 2: Speichertest
Es soll ein Programm zum Testen des eingebauten Speichers eines
Mikrocontrollers
geschrieben werden.
Wenn ein Test versagt, soll der Returnwert die Adresse der fehlerhaften
Zelle sein,
sonst soll 0x00 zurückgegeben werden.
„Single-Cell- Test“:
Das Programm hat die Aufgabe, folgende Tests mit jeder 32-bit
Speicherzelle eines
vorgegebenen Bereichs durchzuführen:
1) 0xa5a5a5a5 schreiben und verifizieren, dass die Zelle diesen Wert
enthält.
2) 0x00 schreiben und verifizieren, dass die Zelle diesen Wert nun
enthält.
Codieren Sie die Funktion uint32_t * single_c_t( uint32_t * begin,
uint32_t * end)
1
C-Code
2
//Meine Lösung (kurzer Code hoffe ist noch ok)
3
uint32_t*single_c_t(uint32_t*begin,uint32_t*end){
4
delta=*end-*begin;//Länge des Speichers
5
for(inti=0;i<=delta;i++){
6
*begin=0x5a5a5a5a;//Speichern des Wertes 0x5a5a5a5a
Bimbo. schrieb:> Mal wieder extrem weltfremd. Wer testet schon EEPROM, Flash oder RAM auf> Fehler?
Ka ist ne Klausurfrage! Was hälst du von meiner LSG?
Henrik G. schrieb:> Ka ist ne Klausurfrage! Was hälst du von meiner LSG?
was berechnet
1
delta=*end-*begin;
Oder anders ausgedrückt: welchen wert hat "*begin"?
Henrik G. schrieb:> Hallo vielen Dank für die hilfe.*begin++; //erhöht doch auf die nächste> Zelle oder nicht?
ja, hab ich erst später gesehen, dein Code ist so ganz ohne Einrückungen
schwer lesbar.
Henrik G. schrieb:> "*begin" enthält die Adresse der Speicherzelle die Beschrieben werden> soll.
Nö. "begin" ist die Adresse. "*begin" ist der Speicherinhalt an der
Stelle, und den willst du nicht wirklich zum Berechnen hernehmen.
Wenn z.B. der ganze Speicher vorher voll mit 0x00 ist, dann ist *begin
== 0x00 und *ende == 0x00, und damit delta=0, egal welcher Test-Bereich
angegeben wurde.
Εrnst B. schrieb:> Henrik G. schrieb:>> "*begin" enthält die Adresse der Speicherzelle die Beschrieben werden>> soll.>> Nö. "begin" ist die Adresse. "*begin" ist der Speicherinhalt an der> Stelle, und den willst du nicht wirklich zum Berechnen hernehmen.>> Wenn z.B. der ganze Speicher vorher voll mit 0x00 ist, dann ist *begin> == 0x00 und *ende == 0x00, und damit delta=0, egal welcher Test-Bereich> angegeben wurde.
Kapiert klar es muss delta=&end-&begin;
Wenn dann so:
//Meine Lösung (kurzer Code hoffe ist noch ok)
uint32_t * single_c_t( uint32_t * begin, uint32_t * end){
delta=(end-begin)/sizeof(uint32_t); //Länge des
Speichers
for(int i=0;i<=delta;i++){
*begin=0x5a5a5a5a; //Speichern des Wertes 0x5a5a5a5a
if(*begin!=0xa5a5a5a5a) //Prüfen Wert gespeichert?
return begin; //Wenn nicht Adresse zurückgeben
else
*begin=0x00; //0x00 Speichern
if(*begin!=0x00) //Prüfen Wert gespeichert?
return begin; //Wenn nicht Adresse zurückgeben
else
begin++; //nächste Adresse also um
sizeof(uint32_t)erhöhen
}
return 0;
}
Aber du hast Pointer überhaupt nicht verstanden. Dein Code war voller
Fehler.
Die vorgeschlagene Lösung ist aber wesentlich besser.
Henrik G. schrieb:>> Kapiert klar es muss delta=&end-&begin;
auch nicht, &begin ist die Stelle, an der die "begin" Variable selber
gespeichert ist.
Ganz einfach
begin (ohne irgendwas davor) ist bereits die Adresse. Es wurde ja in
der Funktionsdeklaration bereits als Zeiger festgelegt.
Somit gibt "begin" immer die Adresse zurück, auf die begin zeigt.
*begin dagegen gibt den Inhalt der Speicherzelle zurück.
1
*begin++;
Der Stern ist hier Überflüssig. Du willst ja mit dem Inhalt des
Speichers an der Adresse von begin nichts tun. Ein
1
begin++;
reicht aus. Der Stern wäre nur bei einem solchen Konstrukt sinnvoll:
1
value=*begin++;
Das würde den Inhalt vom Speicher an der Adresse auf die begin zeigt in
value schreiben und danach begin um eins (bzw. 4 bei uint32) erhöhen.
Die Lösung mit for() ist eleganter und schneller zugleich.
Das stimmt übrigens nicht:
deristuntendurch schrieb:> delta=(end-begin)/sizeof(uint32_t);
da end und begin schon uint32_t Pointer sind, ist die Differenz davon
die Anzahl der "uint32_t" zwischen ihnen.
Um genau das zu Umgehen (Pointer-Arithmetik ist schwer für Anfänger) hab
ich oben vorgeschlagen:
Εrnst B. schrieb:> for (uint32_t * lauf = begin; lauf != ende ; lauf++) {
Markus M. schrieb:> Das würde den Inhalt vom Speicher an der Adresse auf die begin zeigt in> value schreiben und danach begin um eins (bzw. 4 bei uint32) erhöhen.
Wie erhöhe ich den Speicher um 1 nicht um 4?
Εrnst B. schrieb:> ich oben vorgeschlagen:>> Εrnst B. schrieb:>> for (uint32_t * lauf = begin; lauf != ende ; lauf++) {
Würde die schleife nicht bei "ende-1" abbrechen da lauf nicht "ende"
sein darf?
> Es soll ein Programm zum Testen des eingebauten Speichers eines
Mikrocontrollers
Harvard said: 'Forget ist'; v. Neumann replied: ' don't crash on me!'
Henrik G. schrieb:> Würde die schleife nicht bei "ende-1" abbrechen da lauf nicht "ende"> sein darf?
Ja. Ist übliche Konvention, dass der "ende" Pointer nach den
Speicherbereich zeigt, deshalb habe ich das ohne groß Nachzudenken auch
so getippt.
Ob das mit deiner Aufgabenstellung matched, habe ich nicht geprüft.
Ich würde zunächst den freien Speicher mit einem Pseudozufallsmuster
füllen, dann dann verifieren. Das gleiche nochmals mit invertiertem
Muster. Damit können defekte Speicherzellen (was eher unwahrscheinlich
ist) und Kurzschlüsse sowie Unterbrechungen von Speicheranschlüsse
erkannt werden.
Gerald K. schrieb:> Ich würde zunächst den freien Speicher mit einem Pseudozufallsmuster> füllen, dann dann verifieren. Das gleiche nochmals mit invertiertem> Muster. Damit können defekte Speicherzellen (was eher unwahrscheinlich> ist) und Kurzschlüsse sowie Unterbrechungen von Speicheranschlüsse> erkannt werden.
Hallo Gerald,
ich danke dir für die Antwort allerdings ist das so nicht in der Aufgabe
verlangt.
Kannst du mir vl anderst helfen wie bekomme ich den uint32_t Pointer um
eine Speicherzelle erhöht?
Kann ich diesen einfach zu einen uint8_t Pointer umwandeln?
Henrik G. schrieb:> kann nicht auch vl das so lösen
Was willst du da lösen?
Wenn du Pointer auf einen 32-Bit Wert erhöhst (++), wandert der Pointer
um 32 Bit weiter. Bei einem Pointer auf uint8_t, uint16_t, uint64_t etc
genauso analog. Vollautomatisch.
Also:
Du brauchst kein Sizeof-Gewurschtel, das kann der Compiler alles selber.
Pfusch ihm da nicht rein, er kann das nämlich nicht nur selbst, sondern
auch besser als du mit deinem aktuellen Kenntnisstand.
Henrik G. schrieb:> Kannst du mir vl anderst helfen wie bekomme ich den uint32_t Pointer um> eine Speicherzelle erhöht?
1
uint32_tpx;// Zeiger deklarieren
2
3
// SPEICHER FÜLLEN
4
5
px=ANFANGSADRESSE;// Zeiger intalisieren
6
7
while(px<ENDADRESSE){
8
*px++=0xa5a5a5a5;// Speicher initalisieren (1. 32 Bit bzw. 1. 4 Bytes)
9
*px++=0x5a5a5a5a;// Speicher initalisieren (2. 32 Bit bzw. 2. 4 Bytes)
10
};
11
12
// SPEICHER TESTEN
13
px=ANFANGSADRESSE;// Zeiger intalisieren
14
15
while(px<ENDADRESSE){
16
if(*px++!=0xa5a5a5a5){// Speicher testen (1. 32 Bit bzw. 1. 4 Bytes)
17
printf("error address: 0x%08×/r/n",--px);
18
break;
19
}
20
if(*px++!=0x5a5a5a5a){// Speicher testen (2. 32 Bit bzw. 2. 4 Bytes)
21
printf("error address: 0x%08x/r/n",--px);
22
break;
23
}
24
};
Der Algorithmus erkennt viele Fehler nicht!
Anmerkung :
Zeigerarithmetik mit Zeiger vom Typ uint32_t verändern den Zeiger um 4
px++ 0x0 => 0x4
px-- 0x8 => 0x4
Zeigerarithmetik mit Zeiger vom Typ uint8_t verändern den Zeiger um 1
(uint8_t)px++ 0x0 => 0x1
(uint8_t)px-- 0x5 => 0x4
> Kann ich diesen einfach zu einen uint8_t> Pointer umwandeln?Man kann einen CAST durchführen :
(uint8_t)px
Das hast du so durch den Compiler bekommen? Wow ...
Und funktioniert hat es auch noch? Doppelt Wow ...
Reden wir Tacheles. Was da steht ist schlampig und du hast nicht
wirklich verstanden was du da machst. Von den Verstößen gegen seit
Jahrzehnte beim C-Programmieren üblichen Konventionen, zur Verringerung
von Flüchtigkeitsfehlern, gar nicht zu reden.
Was ich da verzeihen würde wäre die fehlende Verwendung von UINT32_C.
Das wissen die meisten Helden hier auch nicht.
Gerald K. schrieb:> Anmerkung :>> Zeigerarithmetik mit Zeiger vom Typ uint32_t verändern den Zeiger um 4>> px++ 0x0 => 0x4> px-- 0x8 => 0x4>> Zeigerarithmetik mit Zeiger vom Typ uint8_t verändern den Zeiger um 1>> (uint8_t)px++ 0x0 => 0x1> (uint8_t)px-- 0x5 => 0x4>>> Kann ich diesen einfach zu einen uint8_t>> Pointer umwandeln?>> Man kann einen CAST durchführen :>> (uint8_t)px
Hallo Vielen dank für deine Ausführliche Antwort.
Ich habe, glaube ich jetzt alles verstanden bitte korrigiere mich.
Also ich habe 32 bit Register wenn ich einen Pointer habe mit uint32_t
dann beschreibe ich alle 32 bit im Register mit *ptr. Erhöhe ich diesen
Pointer um 1 (ptr ohne stern) dann wandert dieser logischer Weiße 32 bit
höher (in nächste Register)damit ich die letzten 32 bit nicht
überschreibe. Wenn ich Uint16_t habe beschreibe ich nur die ersten 16
bits und bei uint8_t die ersten 8 bit. Wenn ich im Register den Wert der
im Register steht um ein erhöhen mag kann ich den Pointer casten also
(uint8_t)ptr um dann den wert im Register mit *ptr++ um einzuerhöhen.
Welche Fehler meinst du erkennt der Algo nicht, also wenn es solche
Fehler sind wie begin>end oder sowas glaube ich nicht das er das will
aber fragen werde ich in der Klausur sicherlich.
Hannes J. schrieb:> Reden wir Tacheles. Was da steht ist schlampig und du hast nicht> wirklich verstanden was du da machst. Von den Verstößen gegen seit> Jahrzehnte beim C-Programmieren üblichen Konventionen, zur Verringerung> von Flüchtigkeitsfehlern, gar nicht zu reden.>> Was ich da verzeihen würde wäre die fehlende Verwendung von UINT32_C.> Das wissen die meisten Helden hier auch nicht.
Hallo ich danke dir für die Kritik. Wie du sicherlich bemerkt hast bin
ich in einem lernprozess und versuche mich zuverbessern und dazu
zulernen. Das Bibilotheken fehlen ist ganz klar vl ein fehler aber
sicherlich nicht für eine Funktion bzw Methode relevant diese sollten im
Hauptprogramm stehen. Mich interresieren die Konventionen bitte
erläutere mir dies damit ich das in zukumft beachten kann.
Vielen dank im voraus!
Henrik G. schrieb:> Welche Fehler meinst du erkennt der Algo nicht, also wenn es solche> Fehler sind wie begin>end oder sowas glaube ich nicht das er das will> aber fragen werde ich in der Klausur sicherlich.Einfaches Beispiel :
1
AdresseDaten
2
Bit:3210
3
0x000b00000xa5a5a5a
4
0x040b01000x5a5a5a5
5
0x080b10000xa5a5a5a
6
0x0c0b11000x5a5a5a5
Wenn das Adressbit 3 einen Kurzschluss gegen GND hat, dann wird statt
Adresse 0x08 bzw. 0x0c auf die Adresse 0x00 bzw 0x04 geschrieben und
beim "Verify" gelesen. Nachdem 0x08 und 0x00 bzw. 0x0c und 0x04 den
gleichen Inhalt haben bleibt dieser Kurzschluss unentdeckt
Es gibt noch viele andere solche Konstellationen von Fehlern, die
unentdeckt bleiben.
Fazit : ein Prüfmuster darf keine Regelmässigkeit aufweisen.
Ein gutes Prüfmuster wäre die Zahl Pi oder auch Pseudozufallszahlen.
Auf jeden Fall würde ich noch vermerken das man jede Zelle sowohl auf 1
als auch auf 0 testen sollte. Mit dem geforderte Test überprüft man
nicht alle Zellen auf 1
Also neben deinen a5a5a5a5
10100101101001011010010110100101
und 0 Muster
00000000000000000000000000000000
auch auf ff ff ff ff
11111111111111111111111111111111
testen sonst bleibt 50% der Zellen ungetestet.
Wobei man a5a5a5a5 dann auch komplett weglassen könnte.
Thomas O. schrieb:> Auf jeden Fall würde ich noch vermerken das man jede Zelle sowohl auf 1> als auch auf 0 testen sollte. Mit dem geforderte Test überprüft man> nicht alle Zellen auf 1
mit abwechselnd 0x5a5a5a5a und 0xa5a5a5a5 werden zumindest Kurzschlüsse
und Unterbrechungen benachbarter Bits (Leitungen?) erkannt.
Bei 0x00000000 bzw. 0x11111111 werden diese nicht erkannt
Aufwendige Speichertest schieben einzelne Bits durch den Speicher und
testen immer alle anderen Speicherzellen. Das ist sehr sehr
zeitaufwendig.
Thomas O. schrieb:> Auf jeden Fall würde ich noch vermerken das man jede Zelle sowohl auf 1> als auch auf 0 testen sollte. Mit dem geforderte Test überprüft man> nicht alle Zellen auf 1
Immer noch nicht gemerkt das es für diese Klausuraufgabe VOLLKOMMEN
UNINTERESSANT ist ob dieser Speicher jetzt vollständig geprüft wird.
Der Sinn von solchen Aufgaben ist es das der Schüler eine Lösung
präsentiert die der Problemstellung entspricht. Ansonsten würde da
stehen "Diskutieren Sie Speichertest...Vorteile...Nachteile...bla
bla...".
Darum geht es aber nicht Ihr "Experten".
Henrik G. schrieb:> for(int i=0;i<=delta;i++){> *begin=0x5a5a5a5a; //Speichern des Wertes 0x5a5a5a5a> if(*begin!=0xa5a5a5a5a) //Prüfen Wert gespeichert?> return(&begin); //Wenn nicht Adresse zurückgeben
Dieser Test bringt gar nichts! Funtioniert auch mit einem 32 Bit Latch.
Vielleicht sigar nur mit 32 Kondensatoren an den Ports. Dazu braucht man
kein "Experte" zu sein!
Gerald K. schrieb:> Dieser Test bringt gar nichts
Darum geht es doch auch garnicht. Lernziele, die in der Klausur
überprüft werden sollten waren vermutlich:
- Schleifen
- Bitbreiten verschiedener Integer-Datentypen
- Speicherzugriff über Pointer
- Pointerarithmetik
- Evtl: "volatile".
Das ganze halt in eine fiktive Aufgabe verpackt, um einen Anschein von
Realitätsbezug herzustellen.
Insofern: Schöne Aufgabe, begrenzter Umfang, lässt sich auch mit
Papier-und-Bleistift in der Klausur erledigen.
Das niemand so einen Test real und produktiv einsetzen würde, ist dabei
doch Nebensache, und war dem Aufgabensteller auch ganz bestimmt klar.
Moin,
Εrnst B. schrieb:> Insofern: Schöne Aufgabe,
Nee, schoen find' ich die ganz und gar nicht.
Nicht wegen den Testmethoden, sondern wegen des Rueckgabewerts. Ich kann
nicht in einer Funktion entweder Pointer zurueckgeben oder numerische
Werte (hier 0x00). Sowas ist in einer Aufgabenstellung aeusserst
unschoen.
Gruss
WK
Dergute W. schrieb:> sondern wegen des Rueckgabewerts.
0 ist NULL und ein üblicher Rückgabewert bei Funktionen, die einen
Pointer zurückgeben sollen. Und ja, 0 oder 0x00 auf uint32_t* zu casten
ist vom Standard abgedeckt.
Ok, die Rückgabewert-Logik ist jetzt andersherum als bei "malloc", mit
NULL=>OK, !NULL=>Fehler, aber passt z.B. auf einige der
string.h-Funktionen.
Statt
Dergute W. schrieb:> aeusserst unschoen
Würde ich also eher was wie "Hier könnte man, wenn man unbedingt was zum
Bemängeln braucht, ein paar Stil-Punkte abziehen" anmerken.
Denn wirklich "unschön" ist das nicht, erst recht nicht "äußerst
unschön".
Moin,
Und genau damit:
Εrnst B. schrieb:> 0 ist NULL
hab' ich ganz erhebliche Probleme bei einer Aufgabenstellung. Klar kann
ich allen moeglichen und unmoeglichen Krempel casten. Und wenns nicht
ganz obskur ist, macht's vielleicht der Compiler auch einfach so
automatisch.
Bleibt aber trotzdem schlechter Stil, wenn man nicht unterscheidet
zwischen Pointer und numerischer Konstante. Und grad' wenn man eine
Sprache beigebracht bekommen hat und soll dann so eine unsauber
gestellte Aufgabe loesen, wie will man's dann richtig lernen?
Ein guter Fahrlehrer wird auch kaum in's Auto einsteigen, Fluppe
anzuenden und andere Verkehrsteilnehmer mit Schimpfworten bedenken.
Obwohl man auch so Auto fahren kann.
Und dann bin ich mir garnicht mal ganz sicher, ob's nicht in irgendeiner
obskuren CPU sein koennte, dass (int)NULL eben nicht 0 ist...
Gruss
WK
Εrnst B. schrieb:> 0 ist NULL und ein üblicher Rückgabewert bei Funktionen, die einen> Pointer zurückgeben sollen. Und ja, 0 oder 0x00 auf uint32_t* zu casten> ist vom Standard abgedeckt.
Eine integer constant expression mit Wert 0 cast zu einem Pointer
ist garantiert NULL, ein beliebiger Integer mit Wert 0 nicht. Ein
cast kommt da aber nicht mal vor, da ist nur eine implizite
Umwandlung. Der Code ist strenggenommen fehlerhaft. Glücklicherweise ist
NULL aber meistens tatsächlich 0, damit seit ihr Pfuscher weniger
gefährlich.
Dergute W. schrieb:> Bleibt aber trotzdem schlechter Stil
Sahen K&R anders.
Jemand schrieb:> da ist nur eine implizite Umwandlung.
Für die gilt das auch.
>> In appropriate contexts in source code, [...] a null pointer constant can>> be written as 0, with or without explicit casting to a pointer type, or>> as the NULL macro defined by several standard headers. In conditional>> contexts, null pointer values evaluate to false, while all other pointer>> values evaluate to true.
Insofern: "return 0;" ist voll OK.
Wenn ihr wirklich was zum rummäkeln an der Aufgabe sucht, fangt bei der
Paramterübergabe an. Da ist nämlich nicht definiert ob "end" in den
Testbereich includiert oder exkludiert ist, und das ist ein echtes
Problem und nicht nur ein an den Haaren herbeigezogenes
Wischiwaschi-"Schlechter Stil"-Argument.
Moin,
Εrnst B. schrieb:> Sahen K&R anders.
Nee, seh' ich nicht so:
>>> In appropriate contexts in source code,
Es gibt sicher z.b. Berufskraftfahrer, die zur Stressbewaeltigung beim
Fahren rauchen und andere Verkehrsteilnehmer beschimpfen. Trotzdem
erwarte ich von einem guten Fahrlehrer kein solches Verhalten.
Und hier finde ich den context (Klausuraufgabenstellung) eben nicht
appropriate, um NULL und 0 zu verwursten.
Wenns alles wurscht und eh immer das selbe ist, warum gibt es denn die
Bezeichnung NULL fuer einen Nullpointer. Die waere doch dann voellig
ueberfluessig...
Gruss
WK
Εrnst B. schrieb:>> Bleibt aber trotzdem schlechter Stil> Sahen K&R anders.
Deren Maßstäbe entsprechen nicht mehr dem aktuellen Stand. Die
Grundlagen bleiben, Details ändern sich.
Selbst der Herr Stroustrup (C++) hat in seinen neueren Büchern darauf
hingewiesen, dass er einigen seiner alten Empfehlungen heute nicht mehr
zustimmen würde. Er schreibt sogar, dass er "seine" Programmiersprache
heute anders gestalten würde, wenn er sie heute neu erfinde würde.
Ist das nicht vollkommen normal? Die Firma Sun hatte sich mit Java große
Mühe gegeben, eine Programmiersprache zu schaffen, die mit alten
Fallstricken und Problem aufräumt. Sie haben ihr eigenes Ziel weit
verfehlt. Nur wenige Fallstricke sind wirklich weg, dafür haben wir neue
bekommen.
Bimbo. schrieb:> Mal wieder extrem weltfremd. Wer testet schon EEPROM, Flash oder RAM auf> Fehler?
Jeder, der mit sicherheitskritischen programmierbaren Systemen der
Klasse C zu tun hat. Bei jedem Start und auch zur Laufzeit. Nicht ganz
so plump wie der Test oben, aber es geht hier ja ums Prinzip und den
Lösungsansatz. Flash und (E)Eprom löscht man zB nicht beim Testen. Da
gibt es andere Verfahren.
Zur Abwechslung ist das tatsächlich mal eine Aufgabe mit Praxisbezug.