Merkfähigkeit eines ATmega1284P

    This site uses cookies. By continuing to browse this site, you are agreeing to our Cookie Policy.

    • Merkfähigkeit eines ATmega1284P

      Hallo
      Ich habe da ein eigenartiges Problem in einer meiner Hardware.

      Gegeben ist eine elektronische Steuerung basierend auf dem ATmega1284P (ein Spielautomat) aus 2014.
      Diese Steuerung funktionierte in den den letzten acht Jahren soweit klaglos nur fällt mir jetzt auf, dass der Chip in exakt einer Stelle des EEPROMS vergesslich wird.
      Das zeigt sich dahingehend, dass in das EEPROM einmalig bei der Grundinitialisierung in eben dieses EEPROM geschrieben wird (also in den vergangenen Jahren keine zehn mal) und dann beim Einschalten des Gerätes nur daraus gelesen wird. Seitens der SW wird da nur geschrieben wenn man da mittels serieller Schnittstelle zugreift was aber durch besondere Maßnahmen händisch aktiviert werden muss.

      Nun fällt auf, dass in dieser einen Stelle immer wieder nach kurzer Zeit anstatt dem Wert "5" der Wert "100" steht (dies entsprach zufällig dem Wert der Nachbarzelle).
      Daraufhin habe ich nach mehrmaligen Auftreten des Fehlers (nach händischer Korrektur über die Konfigurationsfunktion der SW) den Chip neu geflasht und auch den Eintrittspunkt im EEPROM progammseitig um 64 Byte verschoben (die Konfiguration befindet sich nunmehr 64Byte weiter hinten im EEPROM).
      Nun nach nicht mal vier Wochen verändert sich wiederum nur diese eine Speicherzelle des EEPROMs (nur diesmal steht da plötzlich statt "5" nun "3" drinnen.

      Jetzt frage ich mich, was könnte die Ursache für dieses Verhalten sein?
      Hält das EEPROM nur acht Jahre obwohl darin praktisch nicht geschrieben wird oder kennt sonst wer noch andere Ursachen für dieses Verhalten?
      Warum ist nun plötzlich nach so langer Zeit nur diese eine Speicherzelle betroffen (egal wo im EEPROM die ist, weil jetzt liegt die da ja 64 Byte weiter)?
    • Ich würde mal mit aktuellem Bascom den Code neu compilieren lassen und das dann Flashen.
      Damit kann man schon mal einen Lib-Fehler, wenn denn das die Ursache sein sollte, ausschließen.

      Als 2. könnte man im Datenblatt mal schauen, ob es da eine Alterung des EEProms gibt. Die max. Schreibzyklen sind das eine, aber die Datenverfügbarkeit eine andere.
      Ich meine die müsste hinreichend groß sein (100 Jahre oder so), aber besser mal kontrollieren.

      Als 3. Möglichkeit könnte vielleicht doch ein Software-Fehler vorliegen, der unter bestimmten Umständen die Adresse beschreibt.

      Vielleicht möchtest du den Code mal zeigen?
    • Mitch64 wrote:

      Ich würde mal mit aktuellem Bascom den Code neu compilieren lassen und das dann Flashen.
      Das war meine erste Idee wobei ich auch gleich den Speicherbereich im EEPROM um 64 Byte verschoben hatte.
      Aber nach wenigen Wochen zeigte sich das Problem wieder.
      Ich werde das jetzt zurücksetzen und weiter beobachten. Vor der Änderung war nämlich in diese Speicherzelle immer der Wert 100 (der Wert der Nachbarzelle) drinnen.
      Jetzt ist es der Wert 3 der im EEPROM gar nicht vorkommt.
      Wie gesagt in das EEPROM wird nur bei der Initialisierung oder über das Setup geschrieben.
      Die restlichen Sachen (wie z.B. Kredite) werden in einem externen I2C-RAM geschrieben weil ich es eben verhindern wollte dafür das EEPROM zu verwenden weil sich dieser Zustand ja bei jedem Münzeinwurf und Spielstart verändert und damit das EEPROM ja schnell den Hut nehmen würde.

      Mitch64 wrote:

      Vielleicht möchtest du den Code mal zeigen?
      Hier mal der Code als ZIP, ist ja kein Geheimnis aber halt nicht klein (und auch schon acht Jahre alt) LittleRoll2014_V1.1.bas.zip
    • " Appconfig(appconfig_modus) = 3" wäre die 3 falls appconfig_modus=0
      Kann das ausgeschlossen werden? Oder das bei den weiteren 10 Zuweisungen eine Variable nicht besetzt (0) ist?

      Zitronenfalter wrote:

      in exakt einer Stelle
      welche? Die 8.? Ein Überlauf?
      Die Interrupts schaltet Bascom selber ab?
      Und wartet auch bei jedem Schreiben das das Eerom bereit ist?

      The post was edited 1 time, last by Pluto25 ().

    • Das trifft nur bei der Erstinitialisierung nach dem ersten Einschalten zu und würde dann die entsprechenden hier abgefragten Bytes richtig setzen (übrigens durch Konstanten).
      Könnte auch sein, dass da mehrere Bytes im EEPROM kippen und dann diese Erstinstallation schlagend wird. Nur würden dann das EEPROM mit den korrekten Werten beaufschlagt.
      Ich habe jetzt auch beim Konfigurieren des Gerätes entdeckt, dass das benachbarte Byte statt dem Wert 100 den Wert 0 hatte. Aber auch dieses Byte kann nur durch diese Konfiguration beliebig verändert oder durch die Erstinitialisierung auf den Wert 100 gesetzt werden.
      Ich würde jetzt auch nicht unbedingt an einen Programmfehler denken wollen nachdem das Gerät ja die letzten acht Jahre klaglos funktionierte da denke ich eher an die Hardware.
    • Ich habe deinen Code mal geladen und Compiliert.
      Es werden im Code-Explorer 2 Fehler angezeigt.

      "Parameter not the save as Declared"

      Dies wird bei den folgenden Deklrarationen angezeigt.
      Zeile 31 und Zeile 20:

      Declare Sub Set_LED_Win(pWin As Byte , ByVal pSound As Byte)
      Declare Function Menu_Input(ByVal pFrom As Byte , ByVal pTo As Word , ByVal pValue1 As Word , ByVal pValue2 As Byte) As Word


      Das solltest du erstmal beheben.

      Dann hätte ich noch einen Tip:
      Berechne doch eine Checksumme der EEProm-Werte und lege die auch im EEProm ab.

      Bei jedem Start kannst du so eine kurze Meldung ausgeben, ob die EEPromwerte OK sind.

      Vielleicht finde ich noch einen anderen Hinweis auf den Fehler.
    • Mitch64 wrote:

      Menü Anzeigen -> Code Explorer
      Witzigerweise habe ich den Code-Explorer standardmäßig sichtbar seit es den gibt, nur war die Fehleranzeige deaktiviert (in der Vergangenheit, frühere BasCom-Version) hatte der mehrmals Fehler angezeigt die aber keine waren da habe ich das wohl deaktiviert.

      Mitch64 wrote:

      Prüf das mal.
      Jetzt meckert auch der Codeexplorer nicht mehr :D
      Das Programm entstand zu einer Zeit wo man wohl noch deklarieren musste nun verwende ich den neuen Modus da gibt es glücklicherweise nur mehr eine Deklaration direkt bei der Sub oder Func.

      Pluto25 wrote:

      Gibts was neues bei dem Gerät das massive Störungen verursacht?
      Auf meiner Seite nicht, ob der Nachbar auf der anderen Seite der Kaminmauer da was hingestellt hat kann ich aber nicht sagen.

      Pluto25 wrote:

      Bestimmt ein Chip aus der "Customer" Sparte :D
      Ganz sicher sogar oder verkauft Pollin was anderes :whistling:
    • Diese Routine kommt mir auch nicht ganz "koscher" vor.

      BASCOM Source Code

      1. Function Calc_Motor(pShould As Byte , pIs As Byte) As Byte
      2. Local fX As Byte
      3. fX = 6 - pIs
      4. Calc_Motor = fX + pShould
      5. If Calc_Motor >= 6 Then
      6. Calc_Motor = Calc_Motor - 6
      7. End If
      8. End Function
      Zum einen sind die Parameter hier ByRef, was wohl eher ein Versehen ist?
      Und zum Anderen ruft sich die Funktion rekursiv selbst auf?

      Per Byval wäre das sicherer und der "Selbstaufruf" lässt sich durch eine lokale Variable auch abstellen.
    • Mitch64 wrote:

      Und zum Anderen ruft sich die Funktion rekursiv selbst auf?
      Ich würde das so verstehen, dass das kein rekursiver Aufruf der Funktion ist sondern eine Zuweisung der "Return-Variable" die ja den Funktionsnamen hat.
      Weil X= ist doch eine Zuweisung und X=Y() wäre dann ein Funktionsaufruf von der Funktion Y.
      Zumal ja auch die Zuweisung "Funktion = " nur innerhalb der Funktion ohne Fehler "anerkannt" wird während die gleiche Schreibweise außerhalb der Funktion wohl zu einem Fehler führen wird.

      Mitch64 wrote:

      was wohl eher ein Versehen ist
      Ja das ist wohl so, da schaue ich noch mal drüber, eventuell nehme ich auch die Deklarationen heraus und werde das Programm nach der neuen Methode ohne die extra Deklarationen aufbauen.
      Wenn ich dann schon dabei bin wird auch das Setup über die RS232 verbessert das werde ich dann auf AT-Befehlsbasis umbauen.
    • Zitronenfalter wrote:

      Weil X= ist doch eine Zuweisung und X=Y() wäre dann ein Funktionsaufruf von der Funktion Y.
      Auch X=Y() ist eine Zuweisung.
      Nämlich die Zuweisung des Rückgabewerts von der Function Y.

      Was du da machst ist sehr gefährlich.

      Jeder Compiler würde erkennen, dass "Calc_Motor" die ID von einer Function ist und als nächstes Zeichen eine Klammer auf "(" erwarten mit Parametern und Klammer Zu ")". Also Fehler ausgeben.

      Dass das so funktioniert, ist wohl der Tatsache geschuldet, dass der Bascom-Compiler nicht so genau hinschaut.
      Ich würde mich nicht wundern, wenn der Compiler mal eine Besserung erfährt, dass das plötzlich nicht mehr funktioniert.

      Ich würde das sauber schreiben, dann hast du auch an der Stelle künftig keine Probleme (Stichwort "sauberer Code").

      Um die Fehler Byval und Byref bei Deklarationen zu vermeiden, verwende ich schon lange die neue Methode "Submode=New".

      Das hat viele Vorteile, wenn man z.B. mal die Routinen ändert, muss man weniger tippen. Muss nur den Sub/Functions-Kopf ändern und fertig.
      Nachteil ist, dass die Routinen dann vor dem Aufruf eingebunden sein müssen.

      Aber dafür gibts ja die Include. So bleibts übersichtlicher und Wartungsfreundlicher.

      Übrigens sind die Stackwerte ja extrem groß gewählt. Werte mit 250 für die Stacks wären durchaus auch ausreichend (Siehe Codeexplorer "Info").
    • Mitch64 wrote:

      Was du da machst ist sehr gefährlich.
      Ich verstehe nur nicht warum.
      Im angehangenen Beispiel (übrigens aus der Hilfe wird ja auch dem Funktionsnamen ein Wert zugewiesen- Wie sonst soll denn sonst die Funktion was zurückgeben.
      Ich bin bisher eigentlich davon ausgegangen, dass das im Prinzip eine lokale Variable ist die auf Funktionsdauer innerhalb der Funktion wie eine normale Variable verwendet werden kann und der Compiler übergibt dann den Wert oder einen Handler auf die Variable zur aufrufenden Variable.wie in jeder anderen Programmiersprache auch.
      In jeder anderen Hochsprache kann ich diese Rückgabevariable ja auch gleich am Anfang vorbelegen und innerhalb der Funktion beliebig oft zuweisen. Geht das in BasCom nicht?
      Ist mir aber bisher nicht aufgefallen, weil alles was ich bisher gemacht hatte in dieser Richtung erwartungsgemäß funktionierte.

      Wäre interessant was zurückgegeben wird wenn diese "Variable" gar nicht vorbelegt wurde.
      Bei normalen lokalen Variablen wird ja auch irgend ein Wert hergenommen (was im Stack halt gerade frei war)

      Mitch64 wrote:

      Übrigens sind die Stackwerte ja extrem groß gewählt.
      Ich weiß, aber ist ja auch ein großer "Stein" mit viel RAM :D .
      In Wahrheit habe ich das dann am Ende nicht mehr angepasst.

      BASCOM Source Code

      1. Function Myfunction(byval I As Integer , S As String) As Integer
      2. 'you can use local variables in subs and functions
      3. Local P As Integer
      4. P = I
      5. 'because I is passed by value, altering will not change the original
      6. 'variable named k
      7. I = 10
      8. P = Val(s) + I
      9. 'finally assign result
      10. 'Note that the same data type must be used !
      11. 'So when declared as an Integer function, the result can only be
      12. 'assigned with an Integer in this case.
      13. Myfunction = P
      14. End Function
      Display All
    • Also kurz zur Erklärung

      BASCOM Source Code

      1. Function Calc_Motor(pShould As Byte , pIs As Byte) As Byte
      2. Local fX As Byte
      3. fX = 6 - pIs
      4. Calc_Motor = fX + pShould
      5. If Calc_Motor >= 6 Then
      6. Calc_Motor = Calc_Motor - 6
      7. End If
      8. End Function


      Zeile 4 ist in Ordnung, hier wird ja der Rückgabewert gesetzt. Leider gehts nicht mit Return Wert.
      Aber dann Zeile 5 ist nicht OK.
      Hier rufst da ja eigentlich die eigene Function auf (allerdings ohne Parameter) und prüfst, ob der Rückgabewert >=6 ist.
      Das ist Syntax-technisch nicht ok - auch wenn es mit aktuellem Compiler funktioniert.

      Im übrigen sollte man den Rückgabewert nicht gleich am Anfang festlegen und später ändern.
      Das hat bei mir in der Vergangenheit zu merkwürdigem Verhalten geführt.

      Ich hatte ein Flag namens Flag_Changed verwendet, welches ich mit einer Routine StateChnaged() abfragen und auf 0 setzten wollte.

      Also etwa so:

      BASCOM Source Code

      1. dim Flag_Changed as Bit ' Flag
      2. ' Funktioniert unter bestimmten Umständen nicht zuverlässig und liefert dann falsche Werte
      3. Function StateChanged() as Byte
      4. StateChanged = Flag_Changed
      5. Reset Flag_Changed
      6. End Function
      7. ' so funktioniert es perfekt
      8. Function StateChanged() as Byte
      9. If Flag_Changed = 1 then
      10. Reset Flag_Changed ' Flag löschen
      11. StateChanged = 1 ' letzte Anweisung = Rückgabewert setzen
      12. Else
      13. StateChanged = 0 ' letzte Anweisung = Rückgabewert setzen
      14. End If
      15. End Function
      Display All

      Die 1. Variante funktioniert nicht immer. Vor allem, wenn man mit IF StateChanged()=1 abfrägt, weil dann das Register von dem Rückgabewert verwendet wird.

      Im übrigen steht auch irgendwo in der Bascom-Hilfe, dass man den Rückgabewert möglichst erst unmittelbar vor dem Verlassen der Function setzen soll.

      Eine Fehlersuche ergab, dass bei Verwendung der Abfrage mit einer Variablen das wohl funktioniert, weil der Rückgabewert auf die Zuweisungsvariable zeigt. (keine lokale Variable).

      Wenn aber keine Variable verwendet wird (Abfrage mit If, daher keine Zuweisung an Variable), wird der Rückgabewert im Register 16 abgelegt. Folgen dann Operationen, die das R16 ändern, oder Interrupts, die das tun, war es das mit dem korrekten Rückgabewert.

      Solche Fehler zu finden ist mühsehlig und langwierig. Daher ist meine Devise möglichst sicheren Code zu schreiben.

      Deine Routine könntest du auch so schreiben:

      BASCOM Source Code

      1. Function Calc_Motor(pShould As Byte , pIs As Byte) As Byte
      2. Local fX As Byte
      3. fX = 6 - pIs
      4. fx = fX + pShould
      5. If fx >= 6 Then
      6. Calc_Motor = Calc_Motor - 6
      7. Else
      8. Calc_Motor = fx
      9. End If
      10. End Function
      Display All
    • Weil das vielleicht noch nicht für jeden nachvollziehbar ist, möchte ich hier eine kleine Demo zeigen, die genau dieses Phänomen aufzeigt.

      Zuerst noch die Stelle in der Hilfe mit dem Hinweis, dass nach der Zuweisung des Rückgabe-Werts in einer Function kein Code mehr folgen sollte.

      Hier in der Hilfe steht es: Link
      Etwas runter scrollen, beim 2. gelben Warnzeichen stehts:

      Zitat: "When you set the function result, you need to take care that no other code is executed after this."

      Und hier die Demo, sie enthält weitere Erklärungen.

      BASCOM Source Code

      1. ' Demo
      2. ' Das Beispiel zeigt, dass der Rückgabewert einer Function keine lokale Variable ist.
      3. ' Wird eine Function per Zuweisung an eine Variable aufgerufen (x = function()),
      4. ' so wird ein Zeiger auf die Ergebnisvariable übergeben.
      5. ' Wird eine Function per If-Abfrage aufgerufen (If function() = Y then ...) wird auch ein
      6. ' Zeiger auf eine Speicherstelle übergeben, allerdings zeigt diese auf r16.
      7. ' Wird nun der Rückgabewert in der Function gesetzt und danach steht in der Function
      8. ' noch Code, der das Register r16 ändert und das wird nicht per Zuweisung aufgerufen,
      9. ' dann gehts schief.
      10. ' Daher steht auch in der Hilfe (Index: Function) folgender Hinweis:
      11. ' Zitat: "When you set the function result, you need to take care
      12. ' that no other code is executed after this. "
      13. ' Dieses Demo zeigt genau dieses Verhalten anhand der Test-Function 'getValue()'.
      14. ' Der Aufruf per IF-Abfrage liefert ein falsches Ergebnis.
      15. $regfile = "m8def.dat"
      16. $crystal = 8000000
      17. $hwstack = 50
      18. $swstack = 50
      19. $framesize = 60
      20. $Baud = 9600
      21. Config SubMode = New
      22. $SIM
      23. ' Die Test-Function liefert als Rückgabewert das übergebene Argument 'value'.
      24. ' Function ändert r16, nachdem der Rückgabewert gesetzt wurde.
      25. ' Dies führt je nach Art des Funktions-Aufrufes zu falschen Ergebnissen.
      26. Function getValue(Byval value as Byte) as Byte
      27. getValue = Value ' Rückgabewert setzen
      28. r16 = 123 ' Operation ändert r16
      29. End Function
      30. Dim tmpValue as Byte : tmpValue = 5 ' Parameter-Wert für Functions-Aufruf
      31. Dim tmpResult as Byte
      32. Do
      33. ' Function 'getValue()' wird hier per IF-Abfrage aufgerufen, daher wird
      34. ' der Rückgabe-Wert in r16 gespeichert. Wird dieses Register durch eine
      35. ' nachfolgende Operation geändert, bevor die Function verlassen wird,
      36. ' so wird ein falscher Rückgabewert geliefert. In dem Fall Wert 123!
      37. ' Der Rückgabewert ist keine lokale Variable.
      38. If getValue(tmpValue) = tmpValue then
      39. Print "Korrektr Rückgabe-Wert"
      40. Else
      41. Print "Falscher Rückgabewert: Wert ist 123!"
      42. End If
      43. ' Hier wird der Rückgabe-Wert der Function der Variablen 'tmpResult' zugewiesen.
      44. ' Der Rückgabewert wird also nicht in r16 abgelegt, sondern direkt in
      45. ' die Zielvariable 'tmpResult' geschrieben. Der Function wird beim Aufruf
      46. ' ein Zeiger auf diese Zuweisungs-Variable mitgegeben. Wird nun nach dem Setzen
      47. ' des Rückgabe-Wertes das Register r16 geändert, hat das keine Relevanz
      48. ' mehr auf das Ergebnis in 'tmpResult'.
      49. ' Auch hier ist der Rückgabewert keine lokale Variable.
      50. tmpResult = getValue(tmpValue)
      51. If tmpResult = tmpValue then
      52. Print "Rückgabe-Wert Korrekt"
      53. Else
      54. Print "Falscher Rückgabewert: Wert ist 123!"
      55. End If
      56. Loop
      Display All
      Das Demo kann man im Simulator im Einzelschritt-Modus schön nachvollziehen.
    • Mitch64 wrote:

      ' Auch hier ist der Rückgabewert keine lokale Variable.
      tmpResult = getValue(tmpValue)
      Wie würdest Du die denn sonst bezeichnen?

      Mitch64 wrote:

      Dim tmpResult as Byte
      In den zweiten Fall ist der Wert korrekt da die Variable schon innerhalb der Funktion gesetzt wird.
      Das das im ersten Fall schief geht ist verständlich. Wenn ich was in einem Register zwischenspeichere darf ich das nicht schreddern bevor es abgespeichert/bearbeitet ist.

      Irgenwie sollte man den Zitronenfalter doch schreddern können

      Source Code

      1. $regfile = "m8def.dat"
      2. $crystal = 8000000
      3. $hwstack = 50
      4. $swstack = 50
      5. $framesize = 60
      6. $baud = 9600
      7. Config Submode = New
      8. $sim
      9. Dim A , B , C , D , E , F As Byte
      10. Function Calc_Motor(pShould As Byte , pIs As Byte) As Byte
      11. Local fX As Byte
      12. fX = 6 - pIs
      13. Calc_motor = Fx + Pshould
      14. R16 = 123
      15. R20 = 202
      16. R24 = 24
      17. If Calc_motor >= 6 Then
      18. R16 = 234
      19. Calc_Motor = Calc_Motor - 6
      20. End If
      21. End Function
      22. Do
      23. F = 6 - C
      24. D = F + B
      25. If D >= 6 Then D = D -6
      26. A = Calc_motor(b , C)
      27. If A <> D Then End
      28. Incr B
      29. C = B * 2
      30. Loop
      Display All
      Oder auch nicht . Läuft und läuft...

      Mitch64 wrote:

      oder Interrupts, die das tun,
      dazu benützen manche Leute Push/Pop :D

      Auch wenn das hier funktioniert sollte man natürlich immer schauen ob man ein Register ändern darf ohne "Spätfolgen" R28-31 sind da sehr crashfreudig ;)

      The post was edited 1 time, last by Pluto25 ().

    • Mitch64 wrote:

      Deine Routine könntest du auch so schreiben:
      Danke für deine Ausführungen, waren sehr lehrreich in Bezug auf BasCom, da vergesse ich immer wieder, dass das eben nicht ganz so komfortabel ist wie ich es von anderen Hochsprachen gewohnt bin (In "echter" Software hat der Compiler halt mehr Platz und wohl auch Zeit und braucht sich da wesentlich weniger um Speicher für Zwischenwertzuweisungen kümmern.

      Aber eines noch, in deinem obigen Beispiel

      BASCOM Source Code

      1. Function Calc_Motor(pShould As Byte , pIs As Byte) As Byte
      2. Local fX As Byte
      3. fX = 6 - pIs
      4. fx = fX + pShould
      5. If fx >= 6 Then
      6. Calc_Motor = Calc_Motor - 6
      7. Else
      8. Calc_Motor = fx
      9. End If
      10. End Function
      hat sich dennoch ein kleiner Fehler eingeschlichen.
      Die Zeile 7 wird wieder ein falsches Ergebnis liefern weil die "Variable" Calc_Motor zu diesem Zeitpunkt im günstigsten Fall Null, im ungünstigsten Fall den vorherigen Registerinhalt führen wird.
      Die Zeile wird wohl richtig heißen : Calc_Motor = fX - 6.

      Wie ist das dann übrigens mit Funktionsparameter? Darf man die dann auch nicht direkt ändern sondern muss die auch in eine weitere (lokale) Variable überführen.
      Wäre mir in der Vergangenheit aber nicht aufgefallen, weil Abläufe in diese Richtung bisher erwartungsgemäß gearbeitet hatten.