Hallo zusammen,
gestern habe ich beginnem, zum Elch zu werden, heute bin ich fertig...
Ich arbeite normalerweise mit ATMEL Studio 6.0. Dort kompilierte SW
(ATmega256) läuft auch einwandfrei. Nun habe ich auf dem Netbook STudio
6.2 installiert, da ich außer Haus ggf. die SW ändern muß.
Die unter 6.0. funktionierenden Quelltexte habe ich zum 6.2. kopiert und
dort kompileiert. Nach dem Flashen funktioniert die SW bei den
LCD-Routinen nicht richtig. Fehlersuche und Tests in meinen Routinen
haben nichts erbracht.
Ich will nun auf Studio 6.0. downgraden, nur wo finde ich den Download?
Bei Atmel gibt es nur 6.2....
Danke
Yogy
@ Hanns-Jürgen M. (yogy)
>Die unter 6.0. funktionierenden Quelltexte habe ich zum 6.2. kopiert und>dort kompileiert. Nach dem Flashen funktioniert die SW bei den>LCD-Routinen nicht richtig. Fehlersuche und Tests in meinen Routinen>haben nichts erbracht.
Also ist ein kein NACHGEWIESENER GCC Fehler!
Was funktioniert denn nicht? Mit hoher Wahrscheinlichkeit liegt der
Fehler bei deiner Software. Wo eine ältere Version vielleicht an einer
Stelle nicht so massiv optimiert hat, fällt bei einer neueren Version
dann halt mal ein fehlendes volatile oder ähnliches auf.
"Natürlich" liegt das Problem in meiner SW. Ich finde es aber nicht. Es
wäre auch viel zu aufwendig, hier die einzelnen SW-Teile hochzuladen.
Nur soviel: Mein SW-Lösungen arbeiten alle mit einem zweidimensionalen
Sprungarray in der Hauptprogrammschleife, von der aus Event/Systemstate
gesteuert die einzelnen Routinen angesprungen werden. Hier könnte das
Problem liegen, aber andere Routinen werden fehlerfrei angesprungen und
ausgeführt.
Auch die betreffende Routine (LCD-Ausgabe über TWI/I2C-Bus) wird korekt
angesprungen, nur due Darstellung auf dem LCD ist völlig verquer. z.B.
wird anstelle von $31...§39 $12...§92 ausgegeben. Dabei ist die
Ansteuerung mkit den selbem Display-Routinen ín der Init-Phase korrekt.
Ich habe werder Zeit noch Lust, mit meinem lahmen Netbook tagelang damit
zu amüsieren. Daher will ich auf 6.0 downgraden.
Ach ja, alle globalen variabklen sind volatile....
Hanns-Jürgen M. schrieb:> "Natürlich" liegt das Problem in meiner SW. Ich finde es aber nicht.> Es wäre auch viel zu aufwendig, hier die einzelnen SW-Teile> hochzuladen.>> [...] Ich habe werder Zeit noch Lust, mit meinem lahmen Netbook> tagelang damit zu amüsieren. Daher will ich auf 6.0 downgraden.
Wenn sich ein Fehler manifestiert sollte man auf jeden Fall die Chance
nutzen um ihn zu beheben! Bewusst fehlerhafte Software verwenden /
verteiben ist m.E. nicht weniger als ein Kunstfehler und spricht Bände
über die Qualitätsstandards...
Einfach den Compiler wechsen oder die Optionen oder die Quelle solange
umzustallen bis sich der Fehler nicht mehr manifestiert ist in dem Fall
eine der schlechtesten Optionen.
Dadurch ist der Fehler ja nicht "weg", und keiner garantiert, daß er
nicht auch irgendwann mit anderer Compilerversion x.y.z wieder auftritt.
@ Hanns-Jürgen M. (yogy)
>Auch die betreffende Routine (LCD-Ausgabe über TWI/I2C-Bus) wird korekt>angesprungen, nur due Darstellung auf dem LCD ist völlig verquer. z.B.>wird anstelle von $31...§39 $12...§92 ausgegeben. Dabei ist die>Ansteuerung mkit den selbem Display-Routinen ín der Init-Phase korrekt.
Komisch. Versuch mal, ohne Optimierung zu compilieren und das Programm
zu starten (wenn es in den Flash passt).
>Ach ja, alle globalen variabklen sind volatile....
Was eher nach Holzhammer als nach Verständnis klingt.
@Johann
natürlich hast Du damit Recht. Nur bislang mit 6.0. gab es keine
Fehlermeldungen. Allerdings gab es damals Probleme beim der Portierung
des Grundsystems von einem IAR-Compieler (für 68HC1) auf AVR-Studio
(4-xx). Das kostete mich damals einige Tage Arbeit.
Wenn ich in den nächsten Wochen/Monaten Zeit finde, gebe ich mich auch
án das 6.2. Problem. Nur aktuell habe ich genug vordringlichere Leichen
auf dem Tisch... Die SW wird ja nicht verkauft und steuert auch keine
Rakete.
@Falk
Ja, die Optimierung wollte ich schon abschalten bzw. den Level ändern.
Ich wußte nur noch nicht wie (jetzt weiß ich es)
Holzhammer volatile: Ja, richtig, das hatte habe ich erst gestern
nachgetragen, quasi als erster versuchter Workaround... hat nichts
gebracht. Ich werde zum Abschluß das Vorgängerfile prüfen.
So, Version 6.0. wird aktuell installiert, morgen geht es dann weiter
Noch am Rande bemerkt: Jede SW enthält Fehler, mal mehr, mal weniger.
GCC ist nicht davon auszunehmen. Idealerweise programmiert man halt in
Assembler, Da hat der "Compiler" (der Assembler) keine Möglichkeit, den
Binaercode nach Gutdünken zu manipulieren. C++ und "Optimierungen"
nehmen mir die Macht aus den Händen...
@ Hanns-Jürgen M. (yogy)
>Noch am Rande bemerkt: Jede SW enthält Fehler, mal mehr, mal weniger.>GCC ist nicht davon auszunehmen.
Qualitativ sitmmt das, quantitativ eher nicht. 99% der Fehler liegen am
Programmierer und nicht am Compiler.
> Idealerweise programmiert man halt in>Assembler, Da hat der "Compiler" (der Assembler) keine Möglichkeit, den>Binaercode nach Gutdünken zu manipulieren. C++ und "Optimierungen">nehmen mir die Macht aus den Händen...
Quark. Man sollte schon seine Werkzeuge kennen und beherrschen und nicht
in Assembler-Hobbyfricklermanier vorgehen. Embedded-Programmierung in C
ist seit Jahrzehnten Usus.
@Falk
bez. des "Usus". Das ist wie der Spruch: Leute, eßt Scheiße, Millionen
Fliegen können sich nicht irren.
Natürlich ist "C" schneller und einfacher zu programmieren, ich nutze ja
auch fast ausschließlich "C", aber das Kompilat und insb. das
Linkergebnis ist bei verschiednen Compilern/Linkern nie identisch und
damit nicht verhersagbar. Und C++ ist so gesehen der absolute Horror.
Assembler mit "Hobbyfrickelei" gleichzusetzen, zeigt aber von der nicht
vorhanden Kenntnis von Assembler. Aber wer kann das heute noch von sich
behaupten im Handyoten-Blähstation Zeitalter.
Zu der Bemerkung: "99% der Fehler liegen am Programmierer" gebe ich
meine volle Zustimmung.
So, ich habe mal die Optimierungsstufen durchgespielt. Eungestellt war
-Os, das eragb ein HEX-File vom 70 kB und die beschriebnen Probleme.
Bei Verwendung der anderen Optimierungsstufen zeigt das System das an,
was es soll.
Ich habe nun -O2 eingestellt, HEX Filegröße ist dann nur 9k größer.
-O1 bringt keine Vorteile.
Nur was ich in meiner SW ändern müßte, um das Problem auch bei Os zu
vermeiden, läßt sich so wohl kaum feststellen.
Also, Thema geschlossen und vielen Dank an euch alle.
@ Hanns-Jürgen M. (yogy)
>Natürlich ist "C" schneller und einfacher zu programmieren, ich nutze ja>auch fast ausschließlich "C", aber das Kompilat und insb. das>Linkergebnis ist bei verschiednen Compilern/Linkern nie identisch und>damit nicht verhersagbar.
Sie sind logisch identisch, wenn der Compiler nicht defekt ist und der
Programmierer weiß was er tut. Und taktgenaue Vorhersagen über den
erzeugten Maschinencode hat C NIE versprochen, im Gegenteil. Dass das
ein paar alte Assemblerdionsaurier mokieren ist normal.
>Assembler mit "Hobbyfrickelei" gleichzusetzen, zeigt aber von der nicht>vorhanden Kenntnis von Assembler.
Ich hab genug Assembler programmiert (68000, AVR, 8051, Picoblaze), um
zu wissen wie es geht, was Assembler kann und was man besser nicht in
ASM machen sollte. Ausserdem bezog sich mein Kommentar indirekt auf die
ewig gleichen, albernen Argumente, die einige Assemblerfanatiker hier
immer wieder anbringen. Deine Argumente klingen genau so.
@ Hanns-Jürgen M. (yogy)
>Nur was ich in meiner SW ändern müßte, um das Problem auch bei Os zu>vermeiden, läßt sich so wohl kaum feststellen.
Mann, Mann, Mann!
Du wirfst die Flinte schon ins Korn, wo du noch nicht mal ANSATZWEISE
das Thema Fehlersuche begonnen hast.
Wollen wir wetten, dass die Leute hier in weniger als 24h den Fehler
finden, wenn du den Mumm hast, deine Quellen hier zu veröffentlichen?
> Wollen wir wetten, dass die Leute hier in weniger als 24h den Fehler> finden, wenn du den Mumm hast, deine Quellen hier zu veröffentlichen?
Was ist dein Wetteinsatz?
Hanns-Jürgen M. schrieb:> Nur was ich in meiner SW ändern müßte, um das Problem auch bei Os zu> vermeiden, läßt sich so wohl kaum feststellen.
Du kannst das Artefakt ja gut beschreiben und hast bestimmt noch mehr
Information darüber, z.B. ob es immer auftritt oder sporadisch oder
abhängig ist von der Vorgeschichte oder nur wenn bestimmte
Hardware-Komponenten aktiv sind etc.
Wenn ich mal davon ausgehe, daß du deine Software einigermaßen kennst,
dann hast du bereits jetzt eine ungefähre Vorstellung davon, wo im
Code das Problem steckt:
Ist die Anzeigeroutine falsch oder bekommt sie falsche Daten? Wer
stellt im letzteren Fall die Daten nicht korrekt bereit oder zerschießt
sie? Kommt es zu Glitches, weil ISRs in Daten rumfummeln, die
atomar sein müssten es aber nicht sind? Läuft der Stack über und
wieviel Luft ist da noch? Wurden alle Warnungen aktiviert und
begutachtet? Gibt es besonders "trickreiche" Hacks? Welche
Echtzeit-Anforderungen stellen die einzelnen Komponenten und kannst du
(für dich) nachweisen, das diese erfüllt werden? Gibt es Copy-Paste
Sequenzen von XYZ etc. etc. etc.
Fehler beheben ist lästig, klar. Keiner ist scharf drauf sich dadurch
beim Arbeiten aufhalten zu lassen. Aber es gehört eben auch zum Metier
dazu...
Im Endeffekt bleibt dir nur die Entscheidung, ob du den Fehler gleich
behebst oder erst wenn er dich zum X-te mal nervt...
Hanns-Jürgen M. schrieb:> Nur was ich in meiner SW ändern müßte, um das Problem auch bei Os zu> vermeiden, läßt sich so wohl kaum feststellen.
Das ließe sich schon feststellen mit systematischer Fehlersuche.
Mir würde das keine Ruhe lassen, egal ob es für ein kommerzielles
Produkt ist oder nur eine Hobbybastelei. Ich könnte erst wieder ruhig
schlafen wenn ich in allen Einzelheiten verstanden hätte was, wie und
warum das da schief läuft oder an welcher Stelle mein Verständnis oder
meine Annahmen über den C-Standard oder den Compiler unvollständig oder
fehlerhaft waren und was ich zukünftig beachten muss um das zu
vermeiden.
Hanns-Jürgen M. schrieb:> So, ich habe mal die Optimierungsstufen durchgespielt. Eungestellt war> -Os, das eragb ein HEX-File vom 70 kB und die beschriebnen Probleme.> Bei Verwendung der anderen Optimierungsstufen zeigt das System das an,> was es soll.>> Ich habe nun -O2 eingestellt, HEX Filegröße ist dann nur 9k größer.> -O1 bringt keine Vorteile.>> Nur was ich in meiner SW ändern müßte, um das Problem auch bei Os zu> vermeiden, läßt sich so wohl kaum feststellen.>> Also, Thema geschlossen und vielen Dank an euch alle.
#pragma GCC push_options
#pragma GCC optimize ("O2")
// your code
#pragma GCC pop_options
Damit kannst Du die Optimierung nur für ein einzelnes Codestück ändern
und so die Ursache einkreisen.
Frank K. schrieb:>> #pragma GCC push_options> #pragma GCC optimize ("O2")>> // your code>> #pragma GCC pop_options>> Damit kannst Du die Optimierung nur für ein einzelnes Codestück ändern> und so die Ursache einkreisen.
Oh, danke, dann werde ich mich wohl noch einmal auf die Suche begeben.
Hanns-Jürgen M. schrieb:> Frank K. schrieb:>>>>> #pragma GCC push_options>> #pragma GCC optimize ("O2")>>>> // your code>>>> #pragma GCC pop_options>>>> Damit kannst Du die Optimierung nur für ein einzelnes Codestück ändern>> und so die Ursache einkreisen.>> Oh, danke, dann werde ich mich wohl noch einmal auf die Suche begeben.
Wobei dieses Pragma hier seine Fallstricke hat:
Falls "your code" geinlinet wurde wirkt das Pragma natürlich nicht wenn
die Funktion, in die geinlinet wurde, außerhalb des Pragmas steht. Auch
Funktionen, die nicht explizit "inline" sind, können geinlinet werden,
und umgekehrt kann eine "inline" Funktion nicht geinlinet sein. Dies
ist abhängig von Optionen, dem Kontext und auch dem GCC-Pass, der
Inlining ausführt — da gibt es nämlich mehr als einen.
Analog gilt das für Partial Inlining und Function Cloning: Auch Code
außerhalb des Pragmas kann sich ändern, oder Code, der scheinbar im
Pragma steht, kann sich nicht ändern.
Ist zwar eher unwahrscheinlich das, aber ausgeschlossen ist es nicht und
man sollte es im Hinterstübchen behalten.
Um zu sehen, welche Teile sich wirklich geändert haben, ist wohl ein
Blick in die mit -save-temps erzeugt .s Assembler-Datei am
praktikabelsten, denn bei Diasassemblies ändern sich ab der ersten
Änderung alle Adressen, was das Vergleichen erschwert. Es gibt
diff-Programme, die es erlauben, die zu vergleichenden Daten
vorzuverarbeiten, da kann man dann die Adressen entfernen.
Nun, ich habe mich auf die Suche gamacht, und folgende Routine als
Ursache ??? asusgemacht. Die gesamte Compilation läuft nun mit Opt=s,
nur bei dieser Routine wird Opt=2 verwendet:
Diese Routine ist Bestandteil der Display-Routinen und schreibt 1 Byte,
sprich 2 Nibble, in den I2C-Bus Adapter für das im 4-Bitmode verwendete
LCD ein.
Seltsam ist, daß dieselbe Routinen natürlich auch in der
Initialiserungsphase des Systems verwendet wird, bei dem ebenfalls
Displayausgaben korrekt erfolgten.
Verwendete Größen:
"j" ist eine Wartezeit, die je hier 100 ist. Der Counter TCNT1 taktet
mit 2 MHz (Clock / 8).
Der Verdacht richtet sich nat. auf die lokale Variable iLoop, die ggf.
"wegoptimiert" wird. Aber da diese Routine in der Initphase einwandfrei
arbeitet, könnte das problem auch ganz woanders liegen...
Ach ja: #pragma GCC pop_options generiert eine Fehlermeldung...
Gibe es das auch übersetzbar? Also mit -save-temps und
1) Compilerausgabe mit -v sowie
2) Präprozessierte Quelle, also .i Datei
Dann kann man es nachvollziehen was passiert. Es gibt nämlich auch
PR60486 und PR61055 die drauf passen könnten :-)
Ruf' den Compiler doch mal, wie von Johann beschrieben, mit -save-temps
auf und sieh dir den generierten Assemblercode an.
Ich sehe auf Anhieb nichts Auffälliges, außer dass es mir eine
ziemlich umständliche Formulierung für etwas zu sein scheint, was
ich als:
1
writeI2CByte(RS,1,printchar,0x27);
2
3
wdt_reset();
4
_delay_us(100);
5
wdt_reset();
geschrieben hätte. (Da die kürsteste Periode für den Watchdog um die
15 ms lang ist, braucht man weißgott nicht Mikrosekundenweise den WDR
auslösen.)
Hanns-Jürgen M. schrieb:> while (bTimeOld == TCNT1L) {> wdt_reset();> }
ist das überhaupt sinnvoll?
Wenn der Timer nicht mehr läuft (warum auch immer) wird der WD immer
noch resettet.
1
for(iLoop=j*2;iLoop!=0;iLoop--){
2
bTimeOld=TCNT1L;
3
while(bTimeOld==TCNT1L){};
4
wdt_reset();
5
}
aber einen fehler, der mit Optimierung zu tun hat, kann ich nicht sehen.
@jörg
bez. des Watchdogs nat. richtig, aber es können beim Aufruf der Routine
auch mögliche Zeiten bis zu 2^16 -1
Ich habe hier mal die Assember-Sources anghehängt: (save_temps ist
aktiv)
1. Opt = 2
1
.global writeI2C_LCDByte
2
.type writeI2C_LCDByte, @function
3
writeI2C_LCDByte:
4
.LFB6:
5
.loc 1 522 0
6
.cfi_startproc
7
.LVL28:
8
push r28
9
.LCFI2:
10
.cfi_def_cfa_offset 4
11
.cfi_offset 28, -3
12
push r29
13
.LCFI3:
14
.cfi_def_cfa_offset 5
15
.cfi_offset 29, -4
16
/* prologue: function */
17
/* frame size = 0 */
18
/* stack size = 2 */
19
.L__stack_usage = 2
20
movw r28,r20
21
.LVL29:
22
.loc 1 526 0
23
ldi r18,lo8(39)
24
mov r20,r22
25
.LVL30:
26
ldi r22,lo8(1)
27
.LVL31:
28
call writeI2CByte
29
.LVL32:
30
.loc 1 528 0
31
movw r24,r28
32
lsl r24
33
rol r25
34
.LVL33:
35
sbiw r24,0
36
breq .L44
37
.LVL34:
38
.L56:
39
.loc 1 529 0
40
lds r18,132
41
.LVL35:
42
.loc 1 530 0
43
lds r19,132
44
cpse r19,r18
45
rjmp .L46
46
.LVL36:
47
.L47:
48
.loc 1 531 0
49
/* #APP */
50
; 531 "../c_display.c" 1
51
wdr
52
; 0 "" 2
53
.loc 1 530 0
54
/* #NOAPP */
55
lds r18,132
56
cp r18,r19
57
breq .L47
58
.L46:
59
.loc 1 528 0
60
sbiw r24,1
61
.LVL37:
62
sbiw r24,0
63
brne .L56
64
.L44:
65
/* epilogue start */
66
.loc 1 534 0
67
pop r29
68
pop r28
69
.LVL38:
70
ret
Und das gleiche mit opt=s
1
.global writeI2C_LCDByte
2
.type writeI2C_LCDByte, @function
3
writeI2C_LCDByte:
4
.LFB6:
5
.loc 1 522 0
6
.cfi_startproc
7
.LVL28:
8
push r28
9
.LCFI2:
10
.cfi_def_cfa_offset 4
11
.cfi_offset 28, -3
12
push r29
13
.LCFI3:
14
.cfi_def_cfa_offset 5
15
.cfi_offset 29, -4
16
/* prologue: function */
17
/* frame size = 0 */
18
/* stack size = 2 */
19
.L__stack_usage = 2
20
movw r28,r20
21
.LVL29:
22
.loc 1 526 0
23
ldi r18,lo8(39)
24
mov r20,r22
25
.LVL30:
26
ldi r22,lo8(1)
27
.LVL31:
28
call writeI2CByte
29
.LVL32:
30
.loc 1 528 0
31
movw r20,r28
32
lsl r20
33
rol r21
34
.LVL33:
35
.L45:
36
.loc 1 528 0 is_stmt 0 discriminator 1
37
cp r20,__zero_reg__
38
cpc r21,__zero_reg__
39
breq .L49
40
.loc 1 529 0 is_stmt 1
41
lds r25,132
42
.LVL34:
43
.L46:
44
.loc 1 530 0 discriminator 1
45
lds r24,132
46
cpse r25,r24
47
rjmp .L50
48
.loc 1 531 0
49
/* #APP */
50
; 531 "../c_display.c" 1
51
wdr
52
; 0 "" 2
53
/* #NOAPP */
54
rjmp .L46
55
.L50:
56
.loc 1 528 0
57
subi r20,1
58
sbc r21,__zero_reg__
59
.LVL35:
60
rjmp .L45
61
.LVL36:
62
.L49:
63
/* epilogue start */
64
.loc 1 534 0
65
pop r29
66
pop r28
67
.LVL37:
68
ret
Ich habe nun mal testweise iLoop "volatile" deklariert, obwohl das doch
eigentlich bei einer lokalen Variable wenig Sinn macht. Damit funzt es
aber bei Opt=s
Das sagt garnix, das ergibt nämlich komplett anderen Code. Ohne .i File
kann man nur raten; den Assembler Code könnte man sich daraus nämlich
erzeugen und mit der Quelle gegenlesen.
Auf die Schnelle seh ich keinen großen Unterschied, außer dass mit -Os
zero_reg verwendet wird. Vielleicht ist das nicht 0?
Und der Code bis zum writeI2CByte ist doch der gleiche, oder seh ich das
falsch?
Hanns-Jürgen M. schrieb:> Seltsam ist, daß dieselbe Routinen natürlich auch in der> Initialiserungsphase des Systems verwendet wird, bei dem ebenfalls> Displayausgaben korrekt erfolgten.> while (bTimeOld == TCNT1L) {
Solch ein Vergleich sollte absolut tabu sein. Da muß nur ein passender
Interrupt kommen, und der Vergleich mit TCNT1L scheitert zuhauf. Wenn
man Timer nimmt, dann ist es besser ein OCx-Register zu verwenden und
das betreffende Flag abzufragen, ob der 'compare' stattgefunden hat.
Sind während der Initialisierung schon alle Interrupts aktiv, oder
werden diese erst anschließend zugeschaltet?
Fehler beim Schreiben auf LCDs treten in der Regel auf, wenn das Timing
zu schnell eingestellt ist. Mein Vorschlag ist, den Schreibimpuls
großzügig zu verzögern.
m.n. schrieb:> Solch ein Vergleich sollte absolut tabu sein. Da muß nur ein passender> Interrupt kommen, und der Vergleich mit TCNT1L scheitert zuhauf
genau das soll doch aber passieren. Es will damit nur eine delay
nachbauen.
Johann L. schrieb:> Das sagt garnix, das ergibt nämlich komplett anderen Code. Ohne .i File> kann man nur raten; den Assembler Code könnte man sich daraus nämlich> erzeugen und mit der Quelle gegenlesen.>
Die i-Files sind ziemlich groß, was ist darin von Belang?
> Auf die Schnelle seh ich keinen großen Unterschied, außer dass mit -Os> zero_reg verwendet wird. Vielleicht ist das nicht 0?>> Und der Code bis zum writeI2CByte ist doch der gleiche, oder seh ich das> falsch?
Yepp.
Hanns-Jürgen M. schrieb:> Die i-Files sind ziemlich groß, was ist darin von Belang?
Die aktuelle Funktion. Zusätzlich natürlich noch die Frage, ob das
eventuell irgendwo inline expandiert worden ist.
@m.n.
nach dem Systemreset werden bei mir zunächst die Ports initialisiert und
danach das Timing.System, da das für Zeitschleifen etc benötigt wird.
Geschwindigkeit der LCD-Beschreibung: Ja, ganeu, das wird das Problem
sein. Aber dieses Problem tritt in der Initialisierungsphase der
Peripherie (GSM-Modem, PIR-Sensoren, Temp Sensor), bei der die
Grund-Initialisierung länghst abgeschlossen ist, nicht auf, dort
erfolgen die Systemmeldungen einwandfrei auf dem LCD. Und alles
reproduzierbar,
Hanns-Jürgen M. schrieb:> Johann L. schrieb:>> Das sagt garnix, das ergibt nämlich komplett anderen Code. Ohne .i File>> kann man nur raten; den Assembler Code könnte man sich daraus nämlich>> erzeugen und mit der Quelle gegenlesen.>>> Die i-Files sind ziemlich groß, was ist darin von Belang?
Alles. Zusammen mit der Compilerausgabe mit -v. Zumindest wenn man
sicherstellenn will, dass der Compiler korrekten Code erzeugt hat -- wie
soll man das ohne Quelle und Optionen und Compilerversion sonst
anstellen? Dass es sich bei writeI2CByte nicht um ein riesiges Makro
handelt oder um eine Funktion, die geinlinet wird, erkennt man hier nur
am erzeugten Code. Und des Typ von iLoop kann auch nur geraten werden.
Nicht dass ich dir jetzt alle Infos häppchenweise aus der Nase ziehen
will, sonder dies nur als Beispiel für zwei Dinge, die für dich
sonnenklar sind, anderen aber völlig unbekannt sind und von denen, die
sich mit so einem Puzzle beschäftigen und dir helfen wollen, geraten
werden müssen.
Aus der Ferne betrachtet scheint es sich jedenfalls nicht um einen
Compilerfehler wie die oben erwähnten PRs zu handeln, zumindest nicht
für das von dir gepostete Assembler-Schnippel.
Damit geht es dir zwar wie 99.9 % der Anwender, die nicht für sich
verbuchen können einen Compilerfehler gefunden zu haben, aber immerhin
hast du es damit selbst in der Hand den Code zu korrigieren.
Es gibt ja schon genug Anmerkungen zum Code selbst; z.B. ist auch unklar
ob er in einer ISR läuft oder nicht. Falls er nämlich in einer ISR
läuft und auf main-Ebene TCNT1 gelesen wird, liest man aus TCNT1H bzw.
dessen Latch gerne mal Schrott aus.
Und übrigens kann es immer noch sein dass der Fehler woanders ist und
eben nicht im gezeigten Quellschnipsel, etwa weil das Timing komplett
anders ist. Oder die Funktion wird mit -O2 geinlint und der erzeugte
Code für -O2 so überhaupt nicht verwendet, sondern nur mit -Os. Keiner
weiß es...
@Jörg
Ich habe keine Funktionen als "Inline" deklariert. Innerhalb der
betreffenden Funktion ist der Watchdog-Reset ein asm Macro, aber das
dürfte hier irrelevant sein.
Nun die i-File teile,
zunächst Opt=2
Ich sehe da keine bedeutenden Unterschiede.
Wäre das makefile interessant? Ich muß gestehen, daß ich dieses bei
meinen ersten "Spielereien" mit dem AVR-Studio 4. irgendwas habe
exportieren lassen, und bei dem ich danach nur die Filenamen ergänzt
bzw. entfernt habe...
Was auch immer die exakte Ursache ist, allgemein kann man wiohl sagen,
dass ohne Not ziemlich umständlich und fehlerträchtig eine einfache
Funktion umgesetzt wurde. Naja. 8-0
Beitrag "Re: I2CLCD Library für HD44780 LCDs"
Dort sind keinerlei Tricks drin, im Gegenteil, sämtliche Unsauberkeiten
des Originals wurden wohlweislich entfernt. Und damit kommt man auch auf
mehrere hundert Zeichen/s Schreibgeschwindigkeit. Reicht für einfache
LCDs locker.
Ich setze andere Ansprüche an Routinen als dieses bekannte LCD Library.
Als erstes muss sie kompatibel zu allen meinen früheren Projekten sein.
Dazu gehört die per bedingte Kompilation uneingeschränkte Verwendbarkeit
mit 8-bit, 4-bit, SPI (MEINE Norm), Soft-I2C und Hard-I2C/TWI
Schnittstellen.
Weiterhin sind zusätzlich eigene Funktionen vorhanden.
Vorgefertigten Algorithmen gegenüber stehe ich ohnehin kritisch
gegenüber, da sie i.d.R. kaum meinen Vorgaben entsprechen sonder auch,
wenn auch äußerst selten, Fehler beinhalten. Das trifft z.B. auf CRC16.h
zu, das ich modifiziert und zusätzlich ein wenig beschleunigt habe. Ich
habe das damals hier hochgeladen. Ist schon eine Weile her.
Ergänzung: Meine "LCD-Library"-Funktionen habe ich vor vielen vielen
Jahren, zunächst noch in Assembler für einen 6809 und für ein anderes
Display entworfen. Später erfolgte die Überarbeitung für diese
HD-Controller in Assembler für einen HC11 und HC16, später portierte ich
nach C.
Hanns-Jürgen M. schrieb:> Ich habe keine Funktionen als "Inline" deklariert.
Hat nichts zu sagen, der Compiler kann inlinen, was er gern möchte.
Hanns-Jürgen M. schrieb:> Vorgefertigten Algorithmen gegenüber stehe ich ohnehin kritisch> gegenüber, da sie i.d.R. kaum meinen Vorgaben entsprechen sonder auch,> wenn auch äußerst selten, Fehler beinhalten.
Ach, und dein Code ist fehlerfrei? ;-)
> Das trifft z.B. auf CRC16.h> zu, das ich modifiziert und zusätzlich ein wenig beschleunigt habe.
Wenn es in der Tat Fehler hatte, dann fehlt da ein Bugreport dafür.
(Heißt übrigens crc16.h, die avr-libc-Dateinamen werden alle klein
geschrieben. Spielt bei portablem Code durchaus eine Rolle.)
Auch, wenn es eine sichere Optimierung bei Beibehaltung der exakten
Funktion gibt, wäre ein Bugreport (als Enhancement request) bei avr-libc
durchaus sinnvoll.
Wenn es natürlich nur deine private Erweiterung ist, dann isses egal.
Hanns-Jürgen M. schrieb:> Aber dieses Problem tritt in der Initialisierungsphase der> Peripherie (GSM-Modem, PIR-Sensoren, Temp Sensor), bei der die> Grund-Initialisierung länghst abgeschlossen ist, nicht auf,
Dann würde ich vermuten, dass die IIC-Übertragung nicht sauber läuft und
daher falche Daten am LCD landen.
Miss doch einfach mal das Timing mit dem Oskar.
Hanns-Jürgen M. schrieb:> Und alles> reproduzierbar,
Das sind doch paradiesische Zustände ;-)
>> Dann würde ich vermuten, dass die IIC-Übertragung nicht sauber läuft und> daher falche Daten am LCD landen.> Miss doch einfach mal das Timing mit dem Oskar.>
Leider ist mein Oskar immer noch kaputt, sonst hätte ich das ale erstes
getan...
@Jörg
Natürlich ist mein Code auch nicht zwangsweise fehlerfrei. :-)
Ergänzung zur crc16.h (Kleingeschrieben :-)
Dazu hatte ich hier Anfang 2012 folgendes gepostet, mit "meiner"
Änderung:
Beitrag "CRC16 nach CCITT auf AVR mit crc16.h probs"
Hanns-Jürgen M. schrieb:> Dazu hatte ich hier Anfang 2012 folgendes gepostet, mit "meiner"> Änderung:
Naja, einen Bugreport bräuchten wir schon hier:
https://savannah.nongnu.org/bugs/?group=avr-libc
damit auch die avr-libc-Entwickler das sehen können.
Allerdings fürchte ich, dass das einer der üblichen Non-Bugs ist,
die aus den diversen Verwirrungen um die CRCs entstehen (Reihenfolge
der Bits in den Daten, Reihenfolge der Bits im Polynom, welcher
Startwert wird benutzt).
Für den CCITT-Algorithmus jedenfalls habe ich mir anlässlich eines
früheren Bugreports mal die Mühe gemacht, die Originalbeschreibung
in den entsprechenden CCITT-Dokumenten zu suchen und gegen den dort
vorhandenen Testvektor zu testen.
@Jörg
Ja, ich sehe gerade, daß Du in der Datei crc16.h als Mitautor gelistet
bist.
Es ist schon lange her, daß ioch mich damit beschäftigt habe. 2012
wollte ich mein Busprotokoll von einer 16bit Checksum auf auf CRC16 gem.
CCITT (RFC1171) umstellen. Startwert: 0xFFFF. Meine Erinnerung ist etwas
verblaßt, auch habe ich es nicht richtig dokumentiert. Das wird wohl
kaum für einen "anständigen" Bugreport reichen. Es war, wie ich jetzt im
Nachhinein sagen muß, auch kein "Bug" sondern eher ein "Vertauschen" der
Bezeichnungen.
Nur folgendes: nach meinen rudimentären Aufzeichnungen und ebensolchen
Erinnerungen: Die CRC16-Implementierung lieferte das falsche, jedoch die
CRC-XMODEM Implementierung das richtige Ergebnis. Danach habe ich mich
aus Spaß an der Freude daran gemacht, die ASM Implementierung zu
modifizieren. Das Ergebnis hatte ich hier (siehe obigen Link) gepostet.
Wörtlich hatte ich in meiner SW dokumentiert (Mit Tippfehler...):
ZITAT
// Uebrelegung, ob die Chgecksum des MHA Protolls nicht durch CRC.gem.
CCITT
// RFC1171 implementiert werden soll..
// CRC funzt, aber mit der XMODEM-Routine... Mal die Ursache sucghen
(10.2.2012)..
// so, habe auf Basis der C / VB Routine eine neu ASM Routine fertig.
funzt. 12.2.2012 stolz bin...
/ZITAT
Das jüngste Copyright in der Ori-crc16 war Deines von 2007. Ich sehe
gerade, die neueren crc16 haben Copyrighteinträge von 2013.
VG yogy
Hanns-Jürgen M. schrieb:> Die CRC16-Implementierung lieferte das falsche, jedoch die CRC-XMODEM> Implementierung das richtige Ergebnis.
Typischer Fall für vertauschte Bits.
Dazu muss man wissen, dass so gut wie alle Kommunikationsprotokolle
(mit Ausnahme von USB, wenn ich mich recht entsinne) mit dem
niederwertigen Bit in der Ausgabe anfangen. Daher findet man in
den entsprechenden Niederschriften dann die Bitreihenfolge auch
so herum dargestellt, was unserer „natürlichen“ Leseweise jedoch
zuwider ist, da wir das höchstwertige Bit ganz links schreiben
würden.
Ich kann dir versichern, dass die CCITT-CRC-Implementierung der
avr-libc sowohl direkt als auch als C-Code (den angegebenen
Pseudo-C-Code in richtiges C überführt, für den IAR-Compiler) bereits
erfolgreich in Atmels IEEE-802.15.4-Implemetierung benutzt worden ist.
Hier der Testvektor aus IEEE 802.15.4:
1
As an example, consider an acknowledgment frame with no payload and
2
the following 3 byte MHR:
3
4
0100 0000 0000 0000 0101 0110 [leftmost bit (b0) transmitted first in time]
5
b0..........................b23
6
7
The FCS for this case would be the following:
8
9
0010 0111 1001 1110 [leftmost bit (r0) transmitted first in time]
10
r0................r15
In hexadezimalen Zahlen ist das: 0x02 0x00 0x6A, CRC 0xe4 0x79.
Hier nochmal als C-Code:
@Jörg
Danke für Deine ausführliche Antwort. Na klar, bei mir ist das LSB
rechts- und das MSB linksaussen. (Und alle Bytes sind bei mir in
Motorola-Anordnung.) Leider hat mir 2012 bei meiner Frage hier im Forum
niemand die Schuppen von den Augen entfernt.. Auch die Suche damals im
Internet brachte nicht den richtigen Hinweis.
Das gibt dann wieder ein TODO für mich. Auch wenn es so aktuell bei mir
funzt, so will ich mich doch lieber an die "Norm" halten. Der Winter
wird zunehmend arbeitsreich...
Hanns-Jürgen M. schrieb:> Leider hat mir 2012 bei meiner Frage hier im Forum niemand die Schuppen> von den Augen entfernt.
Vermutlich ist mir sie damals nicht untergekommen, sonst hätte ich dir
das schreiben können. Ich habe mir da schon früher das Gehirn mal
dran verrenkt. ;-)
Jörg Wunsch schrieb:> Auch, wenn es eine sichere Optimierung bei Beibehaltung der exakten> Funktion gibt,
Da hat jemand mit der Geißkanne volatile spendiert, d.h. keins der asm
brauch volatile zu sein; die Funktionen sind sogar const, d.h.
1
__attribute__((__const__))
Was ist eigentlich der Grund für das inline? Die Annahme, dass
Geschwindigkeit klar vordringlich ist und der Code nicht öfter als 1x
pro Applikation verwendet wird? Wäre zumindest plausibel. Zu erwägen
ist dann auch
Hanns-Jürgen M. schrieb:> Wäre das makefile interessant?
Hast du Probleme mit dem Makefile?
> Nun die i-File teile,
Teile? TEILE? Was soll man mit bitte mit TEILEN anfangen???
> Ich sehe da keine bedeutenden Unterschiede.
Warum sollten da Unterschiede sein? Die gäb es bestenfalls mit
1
#ifdef __OPTIMIZE_SIZE__
Der Sinn davon wäre gewesen, weil in 95 % der Fälle der Fehler in
Sequenzen steckt wie der da:
1
>
2
>......
3
>
Hat aber wohl keinen Zweck sich hier weiter die Finger fusselisch zu
tippen...
Johann L. schrieb:> Da hat jemand mit der Geißkanne volatile spendiert, ...
Das Wissen, wann ein asm statement ein "volatile" braucht, ist noch
nicht so sehr verbreitet.
Würdest du als Erinnerung einen Bugreport schreiben für diese Dinge?
Im Moment komme ich gerade nicht dazu.
> Was ist eigentlich der Grund für das inline?
War halt schon immer so. :-/
Wenn man es nicht inline machen würde, müsste man es als reguläre
Funktionen in separate Sourcecodedateien verfrachten, statt die
Implementierung im Headerfile zu haben.
In der Tat ist die Implementierung ja nicht ganz klein.
Was sagt dein „Bauchgefühl“, erscheint es sinnvoller, das als
reguläre Funktion zu realisieren? Das bisschen call/ret macht ja dann
das Kraut auch nicht fett.
Hanns-Jürgen M. schrieb:> Das Thema ist längst durch.
Was denn, hast du die Ursache deines Problems finden können? Klang
mir bislang ja noch nicht so, du hast doch nur einen Hack gefunden,
der sich beim nächsten Compilerwechsel genausogut wieder in Luft
auflösen kann.
@Jörg....
Zunächst reicht mir der Hack. Sobald ich meinen Oszi endlich reparaiert
habe, werde ich das Timing des LCDs checken.
Aktuell habe ich ein wenig Zeit und ich teste die CRC-Korrektur.
Dann plane ich, das Testsystem soweit softwaremäßig abzuspecken, um den
vermeintlichen Fehler eingrenzen zu können.
In diesem Zusammenhang fällt mir ein Problem ein, daß ich zu Beginn
meiner AVR/GCC "Bastelei" hatte. Ich mußte in der Hauptprogrammschleife
einen völlig sinnfreien Befehl / Functioncall einbauen, damit die SW
lief. Mal sehen, ob ich noch eine alte Datensicherung mit diesem Problem
finde. Ich habe das damals einfach ignoriert ("ist halt so").
Zu gegebener zeit melde ich mich wieder in diesem Thread.
VG Yogy
Hanns-Jürgen M. schrieb:> Ich mußte in der Hauptprogrammschleife> einen völlig sinnfreien Befehl / Functioncall einbauen, damit die SW> lief.
Dann war der Befehl wohl doch nicht so sinnfrei ;)
Wenn deine AVR-Basteleien schon vor mehr als 10 Jahren begannen, dann
mag das so gewesen sein. Auch heute noch findet man z.B Code im Netz,
der in einer ansonsten leeren while-Hauptprogrammschleife ein "nop"
ausführt. Ob das jemals erforderlich war, oder schon damals nur aus
falschen Symptom-Fixing entstanden ist und dann als urban legend
weiterlebte, keine Ahnung.
Selbst hier im Tutorial hält sich hartnäckig ein überflüssiger
dummy-Read beim ADC-Auslesen, und wird fleißig in vielen Anwendungen
eingesetzt.
Oliver
@Oliver S. (oliverso)
>Selbst hier im Tutorial hält sich hartnäckig ein überflüssiger>dummy-Read beim ADC-Auslesen,
Wo denn genau?
Eine EINMALIGER Dummy-Read ist nach der Initialisierung bzw. Umschalten
der Referenzspannung nötig.
Datenblatt vom ATmega 48/88/168
"22.5.2 ADC Voltage Reference
If no external voltage is applied to the AREF pin, the user may switch
between AVCC and 1.1V as reference selection. The first ADC conversion
result after switching reference voltage source may be inaccurate, and
the user is advised to discard this result."
> und wird fleißig in vielen Anwendungen eingesetzt.
Dass einige Leute nicht mal gescheites Copy & Paste hinkriegen, ist ein
anderes Problem.
Falk Brunner schrieb:> Eine EINMALIGER Dummy-Read ist nach der Initialisierung bzw. Umschalten> der Referenzspannung nötig.
Er ist (meistens) nur nach einem Umschalten der Referenzspannung
erforderlich. Der erste Wandlungszyklus nach einer Initialisierung
dauert ja extra deshalb länger, um dem Analogteil Zeit zum
"Einschwingen" zu geben.
Aber:
>When the bandgap reference voltage is used as input to the ADC, it will take a >certain time for the voltage to stabilize.>If not stabilized, the first value read after the first conversion may >be wrong.
Da ist dann doch wieder ein Dummy-Read erforderlich.
Oliver
Hanns-Jürgen M. schrieb:> Ich habe erst 2010 mit dem AVR-Studio und den ATmegas angefangen.
Die Versionen funktionierten auch ohne "sinnfreie" Befehle.
Oliver
Oliver S. schrieb:> Hanns-Jürgen M. schrieb:>> Ich habe erst 2010 mit dem AVR-Studio und den ATmegas angefangen.>> Die Versionen funktionierten auch ohne "sinnfreie" Befehle.>> Oliver
Mag sein, ich habe mich damals nicht weiter darum gekümmert.
So, das ursprüngliche Problem ist gelöst bzw. ich habe die Ursache
gefunden, indem ich die SW Stück für Stück auskomentiert habe.
Nach der Initialisierung der Peripherie und den zugehörigen
funktionierenden Bildschirmausgaben erfolgte eine clearscreen, der
bekanntlich bei dem HD-Controller etwas dauert als Cursorsetzen oder
Zeichen ausgeben. Kurze Zeit später (im unteren ms Bereich) erfolgte
dann die erste Ausgabe.
Durch die Optimierung war offenbar die eingestellte Wartzeit zur
Laufzeit dann zu kurz für die Ausführung des Clearscreen. Diese Wartzeit
war gerade im hier vorliegenden Hard- TWI-Betriebsmode fest vorgegeben,
das war dann zu knapp. Ich habe sie nun wie bei den anderen Anschlußmodi
variabel gemacht.
Also mein Fehler.
Das Problem ist damit gefunden und behoben.
Hanns-Jürgen M. schrieb:> Nach der Initialisierung der Peripherie [...]
Sag ich doch ;-)
Johann L. schrieb:> [...] weil in 95 % der Fälle der Fehler in Sequenzen> steckt wie der da:
1
>>
2
>>......
3
>>
Jörg Wunsch schrieb:> Johann L. schrieb:>> Was ist eigentlich der Grund für das inline?>> War halt schon immer so. :-/>> Wenn man es nicht inline machen würde, müsste man es als reguläre> Funktionen in separate Sourcecodedateien verfrachten, statt die> Implementierung im Headerfile zu haben.
Oder als transparente Funktion; wär zwar effizienter aber hier der
Overkill.
> In der Tat ist die Implementierung ja nicht ganz klein.>> Was sagt dein „Bauchgefühl“, erscheint es sinnvoller, das als> reguläre Funktion zu realisieren? Das bisschen call/ret macht ja dann> das Kraut auch nicht fett.
CRC hab ich noch nie gebraucht; ich würd aber schätzen dass es nur in
wenigen Stellen im Code gebraucht wird; mit "wenig = 1" in 95% der
Fälle.
Falls dir die Leute deswegen nicht die Tür einrennen mit Bugreports,
würd ich da keine Zeit reinstecken. Außerdem kann jeder, der liber ne
Funktion hätte, das Inline-Zeugs in eine Wrapper-Funktion packen und hat
damit eine normale Funktion. Mit einer Implementierung in der Lib ist
das umgekehrte aber nicht möglich.
>> Da hat jemand mit der Geißkanne volatile spendiert, ...>> Das Wissen, wann ein asm statement ein "volatile" braucht, ist noch> nicht so sehr verbreitet.>> Würdest du als Erinnerung einen Bugreport schreiben für diese Dinge?
Auch da würd ich keine Zeit reinstecken.
Auch wenn es korrekt wäre, so ein asm über einen davon unabhängigen
volatile Zugriff oder ein anderes asm zu ziehen, kann es wünschenswert
sein, das asm auf einer Bestimmten Seite eines SFR-Zugriffs stehen zu
haben. Macht zwar keinen Unterschied von der Semantik, kann aber wohl
einen Unterschied für Timing bedeuten.
Mit volatile hat man also bessere Kontrolle über die Position des asm im
Code, und das weiß der Programmierer i.d.R. besser als der Compiler. Es
gibt also auch Argumente für volatile.
Und wenn jemand so eine Funktion einsetzt und die Ergebnisse nicht
verwendet dann ist er selber Schuld. Soll er seinen Code aufräumen.
Johann L. schrieb:> Und wenn jemand so eine Funktion einsetzt und die Ergebnisse nicht> verwendet dann ist er selber Schuld. Soll er seinen Code aufräumen.
:-)
OK, überzeugt, ich lass es, wie es ist.