Das Open-Control-Projekt - Die Alternative zur C-Control-I


Das Forum zur C-Control-1
Welche C-Control-Varianten existieren?
Übersicht - Suchen - Neueste 50 Beiträge - Neuer Beitrag - Login - Registrieren
INFO - FAQ - CC2-Forum - CCPro-Forum 

 Interrupt-Routine/Beep (Kollision?) Kategorie: Programmierung ASM (von Kay - 3.02.2005 22:30)
 Als Antwort auf Interrupt-Routine/Beep (Kollision?) von Christian - 8.12.2004 11:54
Auch wenn der Thread schon etwas älter ist, möchte ich hier noch einmal darauf eingehen.
Mit dem Dimmer hat Conrad ein fragliches Teil auf den Markt geworfen, dessen Ansteuerung
sich zum Bleistift mit einem DA-Port weit einfacher hätte machen lassen.
Der Dimmer könnte das auch, der Nutzer einer CControl-Station sucht jedoch vergebens
nach einem D/A-Port an seiner Kiste :-(
Das ist mal wieder Conrad live: Der geneigte Kunde hat das Nachsehen und diejenigen,
die mit der Programmierung wenig am Hut haben, verzweifeln an Fragen wie:

1. Warum, muss ich krampfhaft ein und die selbe feste Variable für den
    auszugebenden Wert verwenden?
2. Wieso, geht das Ding nur am Digitalport 3?
3. Warum stottert die BEEP-Ausgabe?
4. Geht das denn überhaupt anders zu machen?

Antworten wir mal :-)  ......  zuerst mit dem Original-Dimmer-Treiber in BASIC-Worten

0101 lda #6     ' Sorge dafür, dass alles
0103 sta $52    ' ab Adresse 0106 automatisch alle 20 ms runtergerasselt wird
0105 rts        ' und kehre zurück zu BASIC

0106 lda $a1    ' akku = "BASIC"-Variable 1 (byte[1])
0108 bset 2,1   ' Port3 (des Byteport 1) = on
010a deca       ' akku = akku - 1
010b bne $010a  ' if akku > 0 then goto 010a
010d bclr  2,1  ' Port3 (des Byteport 1) = off
010f lda #1     ' akku = 1
0111 rts        ' return

binär (hexdump) sieht das so aus:
a606 b752 81 b6a1 1401 4a 26fd 1501 a601 81
bzw kennt der Dimmer-Neueinsteiger das so:
a6 06 b7 52 81 b6 a1 14 01 4a 26 fd 15 01 a6 01 81

Jetzt hat sicher jeder verstanden, was die merkwürdigen Zahlen hinter dem
syscode-Befehl im Beispielprogramm bedeuten.

zu 1. ...Variablenfrage...
Wer aufmerksam mitzählt, weiß nun sicher schon, was er ändern müsste, um
die Nummer der Basic-Variable zu ändern. Diese liegt im System an Adresse
a1 und wird bei jedem Aufruf der Routine ab Adresse 0106 geladen.
Das 7. Byte also im syscode nennt die Adresse der Varable. Hexadezimal
weitergerechnet könnte man so die nächsten 23 möglichen Adressen verwenden.
Der Ladebefehl lda a3 an Adresse 6 (0106 b6a3) würde also zum Bleistift
byte[3] in Basic meinen.
Die "sys-Bytes" entsprechend neu in den Kontroller übertragen - und schon könnte
man sein Basic-Programm diesbezüglich anpassen.
(nicht machen! der Treiber ist so falsch und hier nur als Beispiel gezeigt)

zu 2. ...Digitalportfrage...
Genau in den Quelltext gesehen? die Maschinenbefehle bset und bclr
bedienen den (Bit-)Port. die Parameter hinter den Befehlen geben an, welches Bit an
welchem Byteport gemeint ist. Bits werden im System von 0...7 gezählt, Basic macht
daraus 1...8. Da der Kontroller als 8-Bit-Rechner nicht wirklich mit Words im Sinne
von Basic rechnet, gilt in Maschinenprogrammen für den Byteport 2, zu dem
in Basic die Ports 9...16 gehören: ebenfalls Bits 0...7, aber port 2.
Um zum Bleistift Port 15 einzuschalten, muss bset 6,2 benutzt werden.
Auf die Richtungsregister möchte ich nicht eingehen, die haben keine Bedeutung für
den Dimmer, wenn man den Dimmerport bei Start des Basic-Programms off setzt.
Das sollte man auch tunlichst, sonst ist der Port ein "Eingang" und nichts geht.

zu 3. ...BEEP-Stotter-Frage...
Basic-Benutzern ist der freilaufende 20-ms-Timer sicher schon genug geläufig.
Er wird in diesem Takt (also alle 20 ms, 50 mal pro Sekunde) um 1 erhöht -
völlig egal, was man tut. Somit ist er gut für Zeitmessungen einsetzbar.
Seine Existenz ist aber nur die halbe Wahrheit, in Wirklichkeit ist er von einem system-
internen Timer, der 10.000 mal schneller läuft und andere Timingfunktionen sowie
die Echtzeituhr bedient, mathematisch abgeleitet. Die Mathematik bewirkt einen
Interrupt, der alle 20 ms ausgelöst wird und eben genannte Systemfunktionen bedient.
Der Dimmertreiber läßt sich von diesem Interrupt anschubsen, macht sein kurzes Ding
und gibt ans System zurück. Dies dauert selbst im Höchstfall nicht einmal 1 ms!
Trotzdem kommt ihm da Wer in die Quere: der BEEPer.
Ich will es nicht genauer erklären - es reicht zu wissen, daß der Beep
von dem selben Interrupt abgekoppelt wird.
Da die Herunterzählschleife des Dimmertreibers eine Echtzeitanwendung ist und,
wenn auch nur sehr kurz, so aber voll den Prozessor in Anspruch nimmt, überlagert
sie den BEEP stroboskop-artig. Das heißt, nicht immer und nicht permanent, aber spürbar.
Hinzu kommt, dass ein Design-Fehler im Betriebssystem (es ist nicht Microsoft! *g*)
die Interrupt-Behandlung in falscher Weise erledigt und den Effekt so verdoppelt.
Dies hat der Programierer des Dimmertreibers nicht beachtet.
Deshalb wird der Dimmer durch den mitgelieferten Treiber auch nicht 50 mal pro
Sekunde "bepulst", sondern 57,7 mal. Warum? Der BEEP-Timer löst alle 131,07 s
(7,7 mal pro s) einen Roundtrip-Interrupt aus, den der Dimmertreiber eigentlich
ignorieren müsste. Deshalb wird der BEEP ärger als ohnehin unterbrochen.

zu 4. ...geht's besser...
Klare Antwort: JA! Und zwar auf verschiednen Wegen.
Einen möchte ich hier vorstellen. Er entstand als Resultat meiner "Forschungen"
zur Funktionsweise der ganzen Sache und der Mithilfe bei der Programmierung
einer Steuerung von Terrarien. Das Ziel des Dimmers: Zeitgesteuert Sonnenauf-
und Untergang simulieren und bei Überhitzung der Becken die Lichtflut reduzieren.
Da die Kennlinie des Dimmers mit der Digiport-Pulserei alles andere als auch nur
ansatzweise eine Gerade ist, war diese zunächst "geradezubiegen".
Ein weiterer Dorn in meinen Augen war, dass das Byte[1] in Basic als Variable
für den Dimmer "festgenagelt" war. Die falsche Interruptbehandlung war auch zu
lösen und der störende Einfluss des Dimmertreibers auf des BEEP zu minimieren.
herausgekommen ist dabei dies:

portnr         equ 5
byteport       equ $01

 org $101
               lda #$08
               sta $52
               clr $dc
               rts

               brclr 6,$13,_handleST
               jsr $ee9
               clra
               rts

_handleST      jsr $e70
               lda $dc
               cmp #20
               bhs _endTOCIRQon
               tax
               lda _dimtab,x
               beq _endTOCIRQoff
               bset portnr,byteport
_deca          deca
               bne _deca
_endTOCIRQoff  bclr portnr,byteport
               clra
               rts
_endTOCIRQon   bset portnr,byteport
               clra
               rts              
               
               lda $92
               sta $dc
               rts
                       
_dimtab    fcb  0,  4,  5,  6,  7,  8,  8, 10, 11, 12
           fcb 13, 14, 15, 17, 19, 21, 23, 27, 40, 50

Das funktioniert folgendermaßen:

Mit sys &h101 startet man in BASIC das erste Stück, womit das Ding
loszulaufen beginnt. Setzt man davor oder dahinter den (im Moment) Port 6 off,
so fällt der Dimmer ab.
Um dem Dimmer einen neuen Ausgabewert zuzuweisen, wird mit sys &h12c,wert
dieser ans Maschinenprogramm gegeben, welches den Wert in den Weiten des Systems
verbuddelt. Damit ist man von der zwanghaften Verwendung einer festen Variable befreit.
Der Hauptteil des Treibers holt sich den zwischengespeicherten Wert, der jetzt auch nur
noch 0...20 (dezimal) betragen darf, und prüft diesen:
wenn 0, dann schalte ganz aus, wenn 20, dann schalte ganz an.
Damit ist man von unnützen Schleifen befreit, die den Prozessor bremsen und nur mühseelig
eine Vollaussteuerung des Dimmers erreichen.
Liegt der Wert im bereich 1<= x <= 19, so wird aus der fcb-Tabelle ein Wert für den
Schleifenzähler geholt, und die bekannte Prozedur abgewickelt.
Zu den fcb's sei gesagt, dass die Werte experimentell an der CC1-Station eines Bekannten
ermittelt wurden. Die Digitalports an dieser erzeugen ein recht rundes Signal. Main-Unit'ler
müssen die vereinfachte, berichtigte Routine weiter unten verwenden (nicht den original-Treiber!!!),
um sich die für sie passenden Werte zu ermitteln. Diese liegen deutlich unterhalb der obigen.
Somit wäre auch die Frage der Linearität des Dimmers geklärt. Je nach Situation kann der
Beep zwar auch künftig stottern, jedoch kann man mit der Tonhöhe experiemntieren, um
einen Ton zu finden, der störungsfrei wiedergegeben wird, wenn der Dimmer nicht gerade
auf 0 oder 20 läuft - dann stört eh nichts mehr.
Ich hoffe, künftigen Dimmer-Benutzern damit einen Einblick und Hilfe gegeben zu haben.

Zum Schluß die einfache, aber berichtigte Standard-Routine, welche byte[1]
und Port 3 benutzt. Zum Testen, Spielen und Ausgabewerte ermitteln ist sie OK:

 org $101
           lda #6
           sta $52
           rts

           brclr 6,$13,_handleST
           jsr $ee9
           bra _ready

_handleST  jsr $e70

           lda  $a1
           beq  _ready
           bset 2,1
_deca      deca
           bne  _deca
           bclr 2,1
_ready     clra
           rts

Wer das nicht erst übersetzen möchte, nimmt einen ASCII-Editor, übernimmt den
folgenden Inhalt und speichert die Daten als wasweißichwas.s19

S1200101A606B752810D1305CD0EE9200ECD0E70B6A1270714014A26FD15014F8158
S5030001FB

viel spaß beim dimmen :-)

 Antwort schreiben

Bisherige Antworten:

Re: Interrupt-Routine/Beep (Kollision?) (von Axel F - 11.12.2006 23:55)