Discussion in 'Opencharge' started by lsimma, Jan 23, 2008.

  1. lsimma

    lsimma New Member

    Hallo,

    ich würde gerne das recht lange Softwarefile nach und nach in include Files aufteilen und dabei grundlegende bzw. generelle Programmfunktionen auslagern.

    Habe als Beispiel die Anzeige und Eingabefunktion eines Word überarbeitet.

    Das sieht dann so im Hauptfile (oc_main.bas) aus um einen Wert mit dem Drehgeber eingeben zu lassen (er wird am Display angezeigt).

    ' Anzeige und Eingaberoutinen
    $include "oc_dialog.bas"

    ' So wird die Eingabefunktion dann genutzt
    Displaypos = 0
    Displaymask = Mask_4_0
    Maxwert = 0
    Minwert = 1023
    Call Eingabe_word(wertexternadc)

    Ist das ok für euch ?

    PS: Eine Funktion die eine Variable ändert wie hier brauch am wenigsten Platz wenn wie ein einzelner Übergabeparameter ByRef definiert (gilt nur bei einem einzigen Parameter).

    Lukas

    Attached Files:

  2. Space

    Space New Member

    Hallo Lukas,

    das Vorrantreiben der Modularisierung macht sicherlich Sinn.
    Auf der Suche Potenzial nach Flasheinsparung ist der Code hiernach aber schon einige Male durchforstet worden. Ich bin mir aber sicher das andere Augen, neue Ansätze entdecken.

    Das zerlegen des Codes, in mehrer INCLUDE Files ist geschmackssache. Ich habe lieber alles in einem File zusammen.
    Finde ich deutlich übersichtlicher. Das INCLUDE hat mich bei C schon immer genervt, weil häufig das passende File zu suchen, mit relativem/absoluten Pfad einbinden und ggf. auch noch die falsche Version von diesem hat.

    Wenn es nicht zu sehr ausufert, kannst du es aber so machen.

    Thomas
  3. lsimma

    lsimma New Member

    Hallo Thomas,

    die Modularisierung hat sich - leider - erledigt da Bascom bei Verwendung des $include ein nicht funktionsfähiges Programm zusammenkompiliert :-(

    Lukas
  4. lsimma

    lsimma New Member

    Bei Ladeende ist der HWStack noch belegt - Versteckter Bug

    Hallo,

    ich hab da vermutlich einen versteckten Bug gesichtet der eher selten Auftritt, dann aber ist er lästig da das OC irgendwo mittendrin versumpft weil es einen Stackoverflow produziert.

    Ich versuche es zu beschreiben:
    Die Menüs und die Ladefunktion werden flach durchlaufen, dh. es wird mit Goto gearbeitet um den Stack in vernünftigen Grenzen zu halten.

    Tritt während der Abarbeitung irgendwo ein Ladeendebedinung auf dann wird auf den Label Ladeende: gesprungen und der springt dann wieder an den Anfang zum Init: oder wenn mehrer Loops (Laden/Entlanden) definiert sind dann wird zum Charge oder Decharge Label geprungen.

    Jedoch wird innerhalb der Ladefuktionalität mehrmals ein Unterprogramm verwendet zb. Sonstigeabschaltkriterien, Autobatterie usw. diese werden normalerweise mit return beendet. Tritt nun aber innerhalb dieser Unterprogramme eine Ladeendebedinung auf so wird auch direkt auf das Ladeende Label gesprungen welches wiederum auf den Anfang springt bzw. eine neues Decharche bzw. Charge anspringt..... da die Unterprogramme nicht mehr mit return beendet wurden bleibt auf dem HWStack jeweils ein Unterprogrammaufruf stehen.

    Bug: Wenn genügend Akkus in Folge geladen/entladen/formatiert werden dann tritt irgendwann ein Stackoverflow auf und das OC wird irgendwas unbestimmbares machen.

    Hier der Pfad den das Programm zb. bei LiPo Laden zurücklegt:

    ------------
    Hauptmenue
    Goto Lixxmenue
    Goto Ladestart
    Goto Sicherheitsmenue
    Goto Sicherheitsmenuestart
    Goto Liposicherheitsmenue
    Goto Nixxcharge
    Goto Lixxcharge_phase1

    ---------
    Lixxcharge_phase1:
    IF Strom 10% Goto Ladungsende (hier Sprung ohne dass wir in einer Unterroutine sind, HWStack ist nicht verändert)
    -> Gosub Wandlerhochlauf
    -> Gosub Autobatterie
    -> Gosub Stromholen
    -> Gosub Temperaturakkuholen
    -> Gosub Sonstigeabschaltkriterien
    Goto Lixxcharge_phase1

    ---------
    Ladungsende:
    Goto Init
    or
    Goto Decharge
    or
    Goto Nixxcharge

    ##### In allen nachfolgenden Programmteilen sind wir in einer Unterroutinen drin, dh. der HWStack ist verändert !!!

    ======= Subroutine =======
    Sonstigeabschaltkriterien:
    IF a Goto Ladungsende
    IF b Goto Ladungsende
    IF c Goto Ladungsende
    return

    ======= Subroutine =======
    Wandlerhochlauf:
    IF AKKULOST Goto Ladungsende
    return

    ======= Subroutine =======
    Autobatterie:
    IF BATTERIE Goto Ladungsende
    return

    ======= Subroutine =======
    Stromholen:
    IF Strom hoch Goto Ladungsende
    return

    ======= Subroutine =======
    Temperaturakkuholen:
    If AKKUTEMP Goto Ladungsende
    return


    Fragen:
    * Kann der Stack zwangsweise mit einem Befehl zurückgesetzt (neu initialisiert) werden ?
    * Gibt es bei Atmel bzw. Bascom irgend einen Trick ?

    Lukas
  5. Space

    Space New Member

    Moin Lukas,

    ich glaube du hast recht, der Stack müllt mit der Zeit voll.
    Man muss wohl wenn man in der Subroutine aussteigt, 2 Byte vom Stack wegschmeissen, da die Rücksprungadresse ja nicht mehr gebraucht wird.
    Also etwa so:

    ======= Subroutine =======
    Stromholen:
    IF Strom hoch then
    pop r28
    pop r28
    Goto Ladungsende
    end if
    return

    (r28 ist in diesem Fall > /dev/null und sollte in Bascom frei sein, solange keine Subs verwendet werden)

    Oder aber einen SW Reste wie z.B. durch den Überlauf des Watchdogtimers auslösen. Mit der Option NORAMCLEAR sollten sogar alle Werte erhalten bleiben. Müsste man mal im Debugger ausprobieren, was da wirklich passiert.
  6. lsimma

    lsimma New Member

    Hallo,

    hab beim suchen im netz gefunden dass der Stackpointer am Anfang initialisiert werden muss. Sowas könnte dann den Stack zurücksetzen egal wo er steht.
    Leider kann ich noch kein AVR Assembler, werde das beizeiten mal probieren.

    How do I set up the stack on SRAM devices?
    Answer:
    For AVR devices with SRAM, the stack pointer must be set to point at the last address in SRAM. On new devices it's automaticly
    initialized to RAMEND during reset, while on other devices this must be handled by the user code. See the initial stack pointer value in datasheet of your device.

    In assembly, initializing the stack pointer is done like this:

    ldi R16,low(RAMEND) ; Load the low byte of the stack pointer in R16
    out SPL,R16 ; Output the value to the stack pointer
    ldi R16,high(RAMEND) ; Load the high byte of the stack pointer in R16
    out SPH,R16 ; Comment out if the AVR have <256 bytes SRAM

    In "C" the stack pointer is automatically set up in the startup code. Note that the linker configuration file defines where the stack pointer is located.

    Lukas
  7. lsimma

    lsimma New Member

    Hallo Thomas,

    an das pop hatte ich auch schon gedacht. Da jedoch das Ladeende auch direkt angesprungen wird ohne dass der Code in einer Unterroutine steckt (zb.
    IF Strom 10% Goto Ladungsende ) und bei anderen Stellen steckt er in (mindestens) einer Unterroutine würde das pop alleine nicht reichen. Entweder wird eine leere Hilfsunterroutine angesprungen sodass alle in derselben Tiefe stecken oder es wird noch ein Flag mitgegeben wie tief man in den Unterroutinen steckt - oder so irgendwie...

    PS: Was passiert wenn ein pop ausgeführt wird jedoch der Stackpointer ist auf der "grundstellung" dh. nix ist auf dem Stack drauf ?

    Lieber wäre mir ein "knallhartes" neu-initialisieren der Pointer (HWStack Pointer+SWStack Pointer+Frame Pointer) - muss da wie gesagt noch das Asembler ansehen. Sollte das klappen dann ist es meines erachtens "fast egal" wie man vorher programmiert hat.

    Das mit den SW Reset und dem NORAMCLEAR guck ich mir auch an. Es wird dann ein Hilfskonstkrukt brauchen damit der Lader bei definierten Lade/Entlade Cyklen selbständig weitermacht.

    Jetzt haben wir ja mehrere Ideen, werde mal prüfen was sich da am besten machen lässt.

    Beste Grüße,
    Lukas
  8. Space

    Space New Member

    Ich habs eben mal ausprobiert, die 2 POP zählen den Stackpointer wie erwaret 2 Adressen zurück und folgender Code setzt den Pointer auf die Ursprungsadresse (@ M32):
    Code:
    $noramclear
    $regfile "m32def.dat"
    $crystal = 8000000
    Starte:
    Do
    Gosub Test1
    Gosub Test2
    Loop
    Test1:
    nop
    Return
    Test2:
    Goto Test3
    Return
     
    Test3:
    ldi r28,&H08
    !Out Sph ,r28
    ldi r28,&H5F
    !out spl,r28
    Goto Starte
  9. lsimma

    lsimma New Member

    Hallo Thomas,

    jetzt wollte ich schon fragen was r28 ist. Habs dann in der Bascom Help gefunden, es ist der Bascom Softwarestack.

    So wie ich das sehe sind drei Pointer neu zu initialisieren.

    AVR Hardware Stackpointer (2x8bit = ein 16bit register als pointer):
    SPL (low)
    SPH (high)

    Bascom hat einen Software Stackpointer
    (Aus Bascom Help: Y can may not be used because it is used as the soft stack pointer)
    AVR Y Register == Bascom Software Stackpointer
    Y Register
    R28 ($1C) (low)
    R29 ($1D) (high)

    Bascom belegt ein Register um in den Frame (temp storage) zu zeigen.
    (Aus Bascom Help: Some registers are used by BASCOM, R4 and R5 are used to point to the stack frame or the temp data storage)
    AVR generelle Register R4+R5 == Bascom Frame/Temp Storage Pointer
    R4 ($04)
    R5 ($05)


    Die Größenverhältnisse der drei Bereiche werden im Code vorgegeben:
    $framesize = 46
    $hwstack = 46
    $swstack = 38
  10. lsimma

    lsimma New Member

    Habe ein mini Bascom Programm mit den Stack und Framewerten von Opencharge disassembliert:

    ' Das Programm
    $regfile = "m32def.dat"

    $baud = 38400
    $crystal = 8000000
    $framesize = 46 [=2Eh]
    $hwstack = 46 [=2Eh]
    $swstack = 38 [=26h]

    Progstart:
    nop
    nop
    nop
    nop
    nop
    nop
    Goto Progstart

    ===========================
    ; Disassembly of TESTMINI.BIN (avr-gcc style)

    .equ SPL, 0x3d
    .equ SPH, 0x3e
    .text
    main:
    jmp Label1
    reti
    nop
    reti
    nop
    reti
    nop
    reti
    nop
    reti
    nop
    reti
    nop
    reti
    nop
    reti
    nop
    reti
    nop
    reti
    nop
    reti
    nop
    reti
    nop
    reti
    nop
    reti
    nop
    reti
    nop
    reti
    nop
    reti
    nop
    reti
    nop
    reti
    nop
    reti
    nop

    ; Referenced from offset 0x00 by jmp
    Label1:
    ldi r24, 0x5f ; 95
    out SPL, r24
    ldi r28, 0x32 ; 50
    ldi r30, 0xf6 ; 246
    mov r4, r30
    ldi r24, 0x08 ; 8
    out SPH, r24
    ldi r29, 0x08 ; 8
    ldi r31, 0x07 ; 7
    mov r5, r31
    ldi r30, 0xfe ; 254
    ldi r31, 0x07 ; 7
    ldi r26, 0x60 ; 96
    ldi r27, 0x00 ; 0
    clr r24

    ; Referenced from offset 0x76 by brne
    Label2:
    st X+, r24
    sbiw r30, 0x01 ; 1
    brne Label2
    ldi r24, 0x0c ; 12
    out 0x09, r24 ; 9
    ldi r24, 0x00 ; 0
    out 0x20, r24 ; 32
    ldi r24, 0x18 ; 24
    out 0x0a, r24 ; 10
    clr r6

    ; Referenced from offset 0x92 by jmp
    Label3:
    nop
    nop
    nop
    nop
    nop
    nop
    jmp Label3

    ; Referenced from offset 0x98 by brne
    Label4:
    sbiw r30, 0x01 ; 1
    brne Label4
    ret

    set
    bld r6, 2 ; 0x04 = 4
    ret

    clt
    bld r6, 2 ; 0x04 = 4
    ret
  11. lsimma

    lsimma New Member

    Hier die wesentlichen stellen bei denen Bascom die Pointer initialisiert.

    a) AVR HWStack wird initialisiert (SPL=low, SPH=high)

    ldi r24, 0x5f ; 95
    out SPL, r24
    ldi r24, 0x08 ; 8
    out SPH, r24

    Somit HWStack=85Fh
    M32DEF.DAT besagt: RAMEND =$85F ;Last On-Chip SRAM location
    Perfekt.. somit wird der Hardwarestack gleich mal am Anfang auf den Wert gesetzt der im M32DEF.DAT drinsteht. Leider find ich noch nicht raus wie ich in Bascom (mit oder ohne ASM auf das RAMEND zugreiffen kann)

    b) Bascom SWstack wird initialisiert
    ldi r28, 0x32 ; 50 (low)
    ldi r29, 0x08 ; 8 (high)

    Somit SWStack=832h
    85Fh - 2Eh = 831h ?? wo ist mein Denkfehler, es sollte doch 832 rauskommen.

    c) Bascom Framepointer wird initialisiert

    ldi r30, 0xf6 ; 246 (low)
    mov r4, r30
    ldi r31, 0x07 ; 7 (high)
    mov r5, r31

    r30 und r31 wird als Hilfsregister verwendet da Konstenten nur in ein Register >=r16 übertragbar sind (Steht in der Bascom Hilfe :).
    Somit Framepointer=7F6h (hmm.. wie wird von Bascom dieser Wert errechnet ??)


    *********** Auszug aus M32DEF.DAT ****************
    FILE=M32DEF.DAT ; file name
    pdf=atmega32.pdf
    pdfurl=http://www.atmel.com/dyn/resources/prod_documents/doc2503.pdf
    device = ATMEGA32
    UP = M32 ; shortname for micro
    RAMSTART = $60 ; start of SRAM memory
    _CHIP= 23 ; FOr backwards compatibility
    RAMEND =$85F ;Last On-Chip SRAM location
    XRAMEND =$85F
    E2END =$3FF
    ......
    ***************************


    Soweit so gut. Jetzt ergeben sich neue Fragen:
    * Wie kann in Bascom auf die Definition RAMEND zugegriffen werden ?
    * Wie errechnet Bascom aus den Direktiven $hwstack.... die Werte ?


    Lukas
  12. lsimma

    lsimma New Member

    Hallo Thomas,

    hab dein Posting erst jetzt gesehen.

    Hmm.. bei Programmteil Test3: verwendest Du das r28. Dieses ist so wie ich das rauslese jedoch das low byte des Software-Stackpointers von Bascom welcher somit zerstört wird. Wenn danach dann eine Funktion aus einer Bascom Library verwendet wird welche Parameter erwartet (zp. print <parameter> oder LCD <parameter>) dann wird die Parameteradresse über den Software-Stackpointer ins Nirvana gesendet oder eine Variable wird überschrieben.

    Aus der Bascom Help:
    Mixing ASM and BASIC
    STCHECK
    Assembler mnemonics

    Some registers are used by BASCOM:
    R4 and R5 are used to point to the stack frame or the temp data storage
    R6 is used to store some bit variables:
    R6 bit 0 = flag for integer/word conversion
    R6 bit 1 = temp bit space used for swapping bits
    R6 bit 2 = error bit (ERR variable)
    R6 bit 3 = show/noshow flag when using INPUT statement
    R8 and R9 are used as a data pointer for the READ statement.

    The soft stack begins directly after the hardware stack and is also growing down. The Y pointer(r28+r29) is used to point to this data.


    Lukas
  13. lsimma

    lsimma New Member

    Hallo Thomas,

    Du hast was von einem Debugger geschrieben. Gibt es ausser dem Bascom Simulator noch eine andere Debugmöglichkeit ?

    Wenn ich den Opencharge Code im Bascom Simulator starte dann wird bei mir weder auf der Simulator LCD was angezeigt noch wird eine Ausgabe auf der Simulator seriellen ausgegeben. Geht das gar nicht oder muss dazu noch was besonderes eingestellt werden ?

    Danke,
    Lukas
  14. Space

    Space New Member

    Fast richtig, r28 ist ein ganz normales Register im Atmel Prozessor welches von Bascom (zusammen mit r29) für die Funktion Softwarestack Verwendung findet. Wenn innerhalb des Bascom Programmes keine SUB's verwendet werden, kann man per Assemblerbefehle frei über diese beiden Register verfügen.
    Nachzuprüfen, ob das Register wirklich frei ist --> Im Dissamsembling nach Y oder nach R28 suchen. (Einmal am Programmanfang wird es von Bascom kurz zur Initialiserung genutzt, hiernach aber nie wieder)

    Yep, das SP Register ist auf den SRAM Ende Wert (-2)zu setzen, um den Stack zurückzusetzen. Siehe mein Listing.
    Only used for SUB's. Siehe oben....

    Wie du schon schreibst, sind das Register welche von Bascom als $tmp und für interen Flags genutzt werden. Deren Inhalt muss/sollte nicht zurück gesetzt werden.
  15. Space

    Space New Member

    Hy Lukas, ich hatte deine 3 letzen Statements (Proxycache) leider auch übersehen.

    Ich benutze nur den Bascom Simulator. Der hat zwar im Bereich INT Simulation einen Bug (..oder bin ich nur zu blöd das Handling zu verstehen :p), aber er zeigt einem alle notwendigen Register und Taktcyles an. Finde ich gar nicht schlecht...
  16. lsimma

    lsimma New Member

    Hallo Thomas,

    ok Du hast recht da kommt in deiner originalversion kein r28 vor.

    Jetzt hab ich dafür wieder eine Verständnisslücke:
    Wie läuft denn dann die Parameterübergabe bei den Bascom eigenen Libraries ab ? zb. lcd, print, format, usw.

    Muss mich wohl noch etwas tiefer in die hinter die Kulissen von Bascom bewegen.

    OK: Da ich für meine Testprogramme eine Sub mit einem Referenzparameter verwendet habe benötige ich in meiner Version auch das r28, das original von dir kommt ohne r28 und r4 aus.
    Bei mir hat das Sub mit einem einzelnen Parameter weniger code erzeugt als den Wert als übergabe in eine Variable zu schreiben... muss ich jetzt aber mit dem "avrdisas" (tooles Tooll übrigens, erstellt auch Pseudo C-Code auf Anforderung) genauer betrachen was da tatsächlich passiert.

    Lukas

Share This Page