Bascom Quelltext aufsplitten und includieren?

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

    • Bascom Quelltext aufsplitten und includieren?

      Hallo!

      So langsam werden manche Programme größer und unübersichtlicher, weswegen ich mir überlege wie ich größere Projekte in einzelne Module aufsplitten könnte.

      Mein profaner Versuch eben hat mir gezeigt das diese Idee doch nicht so einfach ist:

      In einem bestehenden Programm die Zeile

      Source Code

      1. Gosub ExternSub0815



      Sowie am Anfang folgenes eingefügt:

      Source Code

      1. $Include ExternSub0815.bas

      Im Projektordner dann die ExternSub0815.bas mit folgendem Inhalt:

      Source Code

      1. ExternSub0815:
      2. For i = 1 to 25
      3. next
      4. Return

      Beim Syntax-Test wurde dann angemeckert das die Variable i nicht dimensioniert wäre, obwohl sie Im Hauptprogramm dimensioniert und etlichen Sub's benutzt wird.
      Wenn ich die For-Next-Schleife (Zeile 3+4) in der ExternSub0815.bas lösche, wird nix mehr angemeckert.
      Die in der Gosub ExternSub0815 angesprungende Sub wird also durch das $Include gefunden und auch eingebunden.
      Nur die Variablen des Hauptprogrammes sind offensichtlich in solch einer externen Sub nicht nutzbar.

      Mache ich da einen Denkfehler?

      Jürgen
    • Hallo tschoeatsch!

      tschoeatsch wrote:

      Probier' mal deine externen subs auch da zu includieren, wo deine internen subs stehen, also nach dem main-Programm und auch nach den dims.

      *Stirnklatsch* Funktioniert!
      Danke für den Hinweis!

      Also gehe ich recht in der Annahme das der Syntax-Check da streng nach Reihenfolge der Zeilen geht und dieses anmeckert, obwohl es praktisch kein Fehler wäre?
      Naja, egal...$Include externer Subs also am besten dort, wo man sonst die Sub hingepackt hätte....am Ende.

      Jürgen
    • Ich denke, die sub kannst du auch am Anfang includieren, aber nach den dims. Es wird dann aber der code dieser sub bei Programmstart auch durchlaufen, was man eigentlich nicht will. Und dann ist es auch blöd, dass jetzt ein return kommt, wo kein Ziel hinterlegt ist, also wird's komische Sachen geben.
      Raum für Notizen

      -----------------------------------------------------------------------------------------------------

      -----------------------------------------------------------------------------------------------------
    • Nach meinem Wissensstand betrifft der submode nur declared subs und functions. Wenn was mit gosub angesprungen wird, dann muss das nach der main stehen. Ich kann aber auch falsch liegen, dann bitte korrigieren.
      Raum für Notizen

      -----------------------------------------------------------------------------------------------------

      -----------------------------------------------------------------------------------------------------
    • Das ist ein interessantes Thema. Darüber könnte man ein Tutorial ins Lexikon machen.

      Aber grundsätzlich gibt es mehrere Möglichkeiten, ein Programm aufzusplitten.

      Man kann z.B. in einer Includedatei die Routinen zusammen fassen (keine Gosub, Label, sondern Sub's und Funktionen), die man vielleicht auch in anderen Projekten verwenden kann. Das können z.B. Routinen für ein Sensor sein.

      Oder man kann die Routinen gruppieren. Fasst also Routinen in einer Include zusammen, die sich um einen bestimmten Bereich erstrecken. So könnte man z.B. Routinen für Ausgabe auf Display und Tastenabfragen in einer Include zusammen fassen.

      Man kann auch den Anspruch haben, in einem Modul alle IO-Sachen zu behandeln. Damit man im Programm selbst nicht mehr auf Pins und Ports zugreifen muss. Dafür kann man eine Include machen (HAL = Hardware abstraktions Layer), in die Routinen bereit stehen, mit denen man die Pins-Ports steuert.

      Das kann sehr universell sein und ist auch immer vom Projekt abhängig. Eine Include lohnt sich nicht, wenn da nur eine einzige Routine drin steht.

      Man kann auch ISR-Routinen in Include-Dateien implementieren. Dazu muss man eine Sub machen, die keine Parameter haben darf.

      Damit die Routinen nicht schon beim Befehl Include ausgeführt werden, sondern erst, wenn sie aufgerufen werden, bietet sich an, die Routinen mit Goto zu überspringen.

      Übrigens sollte man sollte Include-Files nicht Suffix bas, sondern mit inc versehen!

      Um bei deinem Beispiel zu bleiben, könnte die INC-Datei dann so aussehen:

      BASCOM Source Code: ExternSub0815.inc

      1. ' Includefile: ExternSub0815.inc
      2. ' Hier können Variablen dimensioniert und
      3. ' Konstanten definiert werden.
      4. ' Alle Routinen in der Inclide-Datei müssen Declariert werden,
      5. ' wenn man nicht "Submode=New" verwendet.
      6. Declare Sub ExternSub0815()
      7. Goto Includename_Exit ' Routinen überspringen
      8. Sub ExternSub0815()
      9. Local i as Byte ' Lokale Variable i
      10. For i = 1 to 25
      11. next
      12. End Sub
      13. Includename_Exit:
      Display All

      Und dein Hauptprogramm dann so:

      BASCOM Source Code: Demo.bas

      1. ' Hauptprogramm
      2. $Regfile = "m8def.dat"
      3. $HWStack = 20
      4. $SWStack = 20
      5. $Framesize = 40
      6. $Crystal = 8000000
      7. $Include "ExternSub0815.inc"
      8. ' Hauptschleife
      9. Do
      10. ' Aufruf der Routine in der include-Datei
      11. Call ExternSub0815()
      12. Loop
      Display All

      Ob man nun Submode verwendet oder nicht muss jeder für sich entscheiden.
      Beides hat vor und Nachteile.

      Vorteil, wenn man SubMode=New verwendet:
      • Die Deklaration entfällt
      • Nicht verwendete Routinen (Dead Code) werden nicht eingebunden
      Nachteil:
      • Bei größeren INC-Files muss mal das ganze File durchscrollen und zu sehen, welche Routinen unterstützt werden.
      • Compiler prüft die nicht verwendeten Routinen nicht. So schleichen sich gerne Fehler ein, die man unter Umständen erst später bemerkt.
      • Es darf kein Ausführbarer Code vorhanden sein, bevor man mit Goto includename_exit (siehe Beispiel) die inc verlässt (z.B. Initialisierungen).


      Übrigens:
      Auch wenn Submode eingeschaltet ist, kann man die Routinen deklarieren. Das ergibt keinen Compiler-Fehler.
      Kaum macht man es richtig - und schon geht's!
    • "Man kann auch ISR-Routinen in Include-Dateien implementieren. Dazu muss man eine Sub machen, die keine Parameter haben darf."

      Wie macht man das? Declare sub isr()?
      on timer1 call isr()?

      Wird dann das end sub als reti (return from interrupt) interpretiert?
      Raum für Notizen

      -----------------------------------------------------------------------------------------------------

      -----------------------------------------------------------------------------------------------------
    • Ok, hätte ich mal in die Hilfe gespitzt, hätte ich nicht überflüssigerweise (ein 'blöd' verkneif ich mir) fragen brauchen. Das steht ja auch:

      The label to jump to if the interrupt occurs.
      When using a label, you need to use a RETURN to resume the main program.
      The label may also be a sub routine. When using a sub routine, the sub routine needs to end with END SUB like any normal sub routine.
      This sub routine may not have parameters.
      So either a label must be uses like :
      ISR_INT0:
      Or you define a sub routine :
      Declare Sub MyISR_Int0()
      ON INT0 MyISR_Int0 SAVEALL
      Raum für Notizen

      -----------------------------------------------------------------------------------------------------

      -----------------------------------------------------------------------------------------------------
    • Mir ist jetzt nur noch nicht klar, ob man in so einer sub isr() lokale Variablen verwenden kann. Ich bilde mir es ein, man kann es nicht, finde aber keine Bestätigung in der Hilfe.

      Das mit den lokalen Variablen ist vielleicht auch ein Thema, wenn man mit include arbeitet. Dann ist dieser code ja erstmal nicht auf dem Schirm, sodass man leichter Namen für globalen Variablen verwendet, die in den ausgelagerten subs schon für lokale verwendet werden. Ich bilde mir ein (wiedermal), es gibt keine Fehlermeldung, nur welche Variable wird dann in der sub verwendet, die lokale oder globale?
      Raum für Notizen

      -----------------------------------------------------------------------------------------------------

      -----------------------------------------------------------------------------------------------------
    • Ich vermute mal, dass in der ISR, sofern die als Sub deklariert ist, auch lokale Variablen verwendet werden können. Denn die liegen ja auf dem Framesize. Und die Adressen dazu auf dem Soft Stack.

      Das muss man berücksichtigen wenn man die Stacks definiert.

      Zur zweiten Frage.
      Wenn du im Hauptprogramm eine globale variable, sagen wir mal
      Dim temp as Byte
      definiert hast.
      Und du rufst eine Sub auf, die ebenfalls eine Variable temp lokal definiert hat,
      dann wird innerhalb der Routine das lokale temp verwendet.
      Kaum macht man es richtig - und schon geht's!
    • Wenn mehrere Include-Dateien eingebunden werden sollen, macht es auch Sinn, eine Projektdatei anzulegen. Das vereinfacht das Laden eines Projekts (alle Dateien werden geladen) und auch das Editieren und kompilieren wird einfacher, da man nicht versehentlich inc-Dateien versucht zu compilieren.

      Projekte legt man an, indem man Menü: Datei --> Projekt --> New auswählt und dann einen Pfad und Dateinamen für das Projektfile angibt. Die zugehörigen Dateien kann man dann per Drag und Drop in die IDE ziehen. Man muss dann noch das Main-File bestimmen im Projekt-Explorer.

      Nicht vergessen nach Änderungen die Projekt-Datei zu speichern: Menü: Datei --> Project --> Save

      Ich nutze das viel und ist eine tolle Einrichtung von Bascom.
      Kaum macht man es richtig - und schon geht's!
    • Hallo!

      Oha, da schein ich ja was losgetreten zu haben.
      Die Frage ob Variablen aus includierten Subs wieder mit in die Hauptschleife übertragen werden hat mich heute auch schon beschäftigt, weil mein Programm nicht so wollte wie ich.
      Daraufhin habe ich alles ausgelagerte schrittweise wieder in die Hauptdatei kopiert, aber ohne Erfolg.
      Drehe mich seit guten zwei Stunden wieder im Kreis...muss irgendwas komisches sein.

      Wer Lust hat darf da gerne mal über den Anhang schauen.
      Beim ersten Durchlauf scheint alles so zu laufen wie ich es mir wünsche:

      DCF-Routine mit Sectick.
      In dieser werden je nach Senkundenwert eine Aufgabe gesetzt.
      Diese Aufgabe (0-6) wird in der Hauptschleife abgefragt.

      Das funktioniert auch so weit: Der RS232-Output welcher mir neben dem Timestamp den Aufgabenwert und den Modus ausgibt, schaltet den Aufgabenwert korrekt um.

      Jedoch: Nach der ersten Minute nach einem Reset läuft auch der Modus korrekt durch.
      Ab der zweiten Minute wird zwar in Sekunde 1 Aufgabe 2 Aufgerufen, aber Modus bleibt auf 0 (Standby) statt 1 (Empfangsmodus RFM69) hängen.

      Modus ist dabei lediglich eine Indikatorvariable welche der Hauptschleife zurückmelden soll in welchen Betriebsmodus (Standby, RX, TX) das Funkmodem RFM69 ist.

      Vom Sinn her:
      @ Sekunde 00 soll ein Paket gesendet werden - funktioniert
      @ Sekunde 01 soll der RFM69 auf RX geschaltet werden - funktioniert nur in erster Minute!
      @ Sekunde 10 werden Meßbefehle an Sensoren gesendet (I²C)
      @ Sekunde 15 werden Sensoren ausgelesen
      @ Sekunde 48 wird RX-Mode beendet und der RFM69 soll empfangende Pakete aussenden
      @ Sekunde 58 soll das Minutenpaket "Timestamp" vorgelegt werden zur Aussndung in Sekunde 00.

      Merkwürdig...
      Files
      • Repeater_15.bas

        (62.73 kB, downloaded 13 times, last: )
    • Hi Amateurfunker @DG7GJ

      Ich habe mir deinen Code mal durchgesehen. Und auch verschiedene Dinge abgecheckt.
      Aber keins von den üblichen Fehlern Dingen scheint hier vorzuliegen.

      Da ich auch nicht deine Hardware habe, ist eine Fehlersuche fast aussichtslos.

      Ich habe aber einen Tipp:
      Ich würde mal einzelne Zeilen in der Hauptschleife remmen, wo die Aufgaben ausgeführt werden.
      Also hier: "If Aufgabe = x then Gosub ..."
      Und dann laufen lassen.
      Vielleicht kannst du so eingrenzen, durch welche "Aufgabe" der Fehler ausgelöst wird.

      Was ich auf alle Fälle Remmen würde ist in der ISR "Sectic" die Ausgabe mit Print remmen.
      Denn die Ausgabe benötigt einen Interrupt und du befindest dich in einem. So was sollte man unterlassen.

      Wenn du eine Ausgabe brauchst, dann setze in der ISR ein Flag und frage das dann in der Hauptschleife ab, ob eine Ausgabe erfolgen soll, und dann das Flag wieder löschen nach der Ausgabe.

      Vielleicht fängst du mit der sectic an.
      Dann die Aufgaben deaktivieren.

      Das wäre jetzt mal meine Vorgehensweise.
      Kaum macht man es richtig - und schon geht's!