PWM mit Timer erzeugen ?

Antwort erstellen


Diese Frage dient dazu, das automatisierte Versenden von Formularen durch Spam-Bots zu verhindern.
Smilies
:-) ;-) 8-) :-( :arrow: :idea: :like: :keen:
Mehr Smilies anzeigen

BBCode ist ausgeschaltet
Smilies sind eingeschaltet

Die letzten Beiträge des Themas
   

Ansicht erweitern Die letzten Beiträge des Themas: PWM mit Timer erzeugen ?

Re: PWM mit Timer erzeugen ?

von Nightflyer » Do 27. Mär 2014, 18:08

Hallo Zusammen

Ich greife das Thema nochmals auf.

Der Code läuft so wie oben beschrieben einwandfrei. Nun wollte ich den Code in meiner Anwendung integrieren, und jetzt fangen die Probleme an. Ab und zu stürzt die Software ab, und es erscheint DOS ERROR 5 (Zugriff verweigert). Der Fehler tritt nicht in der ISR auf, sondern immer an einer unterschiedlichen Stelle im Hauptprogramm. Mir ist aufgefallen, dass einige Variablen im Hauptprogramm durcheinander gebracht werden, und dadurch entsteht natürlich der Fehler. Ich muss jedoch noch anfügen, dass ich nicht jede Variable mit DIM definiert habe.

Könnte dies das Problem sein, oder ist ein STACK zu klein ? Was hat es mit dem ISR STACK auf sich ?

Re: PWM mit Timer erzeugen ?

von freecrac » Di 10. Dez 2013, 07:58

Es ist auf jeden Fall schon ungewöhnlich den Timer innerhalb der ISR zu verändern.
Sonst bin ich schon beeindruckt davon wie man mit PowerBASIC und Assembler zusammen arbeiten kann.

...

Bei diesen beiden Befehlen können wir das Segment override Prefix herausnehmen, weil defaultmäßig schon DS verwendet wird.
! mov ds:[si+2], es ; copy ES (segment address) into VSeg??
! mov ds:[si], bx ; copy BX (offset address) into VOff??

So ist es je ein Byte weniger
! mov [si+2], es ; copy ES (segment address) into VSeg??
! mov [si], bx ; copy BX (offset address) into VOff??

Nur wenn wir z.B. BP als Adressregister verwenden würden müssten wir DS: angeben.

Defaultmäßige Zuordnung
Adressregister Segmentregister
BX, DI, SI = DS
BP, SP = SS

Dirk

Re: PWM mit Timer erzeugen ?

von wobo » Mo 9. Dez 2013, 21:34

Nightflyer hat geschrieben:Aha alles klar. Gibt es irgendwo eine Tabelle/Beschreibung von den ganzen Interrupts ? Ich hab nähmlich nur so eine Portlist: http://bochs.sourceforge.net/techspec/PORTS.LST
Wenn es nur um einen schnellen Überblick geht, hier:
http://www.tu-chemnitz.de/informatik/RA ... belle.html

Ansonsten sehr umfangreich und detaillierter: google nach Ralph Brown's Interrupt list
Nightflyer hat geschrieben: Den Timer muss ich in der ISR neu setzten. Sonst passiert folgendes: Timer an Port H40 mit Wert 1193 laden, der Wert wird nun herunter gezählt 1192, 1191, 1190... 3, 2, 1, 0 -> ISR wird aufgerufen, danach wird der Wert weiter runtergezählt, ist er jedoch bereits bei 0, überläuft der Timer und beginnt wieder bei 65535.... Deshalb muss ich den Timer immer wieder neu setzten.
Kann ich nicht nachvollziehen, da bei mir der Zähler, wie es die Systemliteratur auch sagt, immer beim eingestellten Zähler wieder anfängt. Aber, wenn es jetzt bei Dir funktioniert, ist gut.


Nightflyer hat geschrieben: Durch leichte Anpassung deines Code, funktioniert dieser einwandfrei. Frequenz wird am LPT ausgegeben, und die Uhr stimmt ebenfalls.
Danke für die Rückmeldung. Freut mich, dass Du es noch hinbekommen hast. Weil, jetzt kann ich es ja zugeben: ich hatte noch nie was mit PowerBasic gemacht und meine letzte Berührung mit Basic war vor 1990 mit GW-Basic. Ich hatte aber anlässlich dieses Threads nach PowerBasic gegoogelt und war und bin schon mächtig beeindruckt, was man damit machen kann und wie strukturiert man damit programmieren kann. Deswegen meine Meinung: PowerBasic ist das beste Basic :-)

Was ich noch nicht ganz verstanden habe, ist das Variablenanlegen: Wenn Du eine Variable mit DIM anlegst, ist die dann lokal, d.h. nur in der jeweiligen SUB nutzbar, oder im gesamten Programm? Ich dachte bisher immer DIM MyVar AS SHARED ist quasi eine globale Variable und DIM MyVar AS STATIC eine lokale - das scheint aber nicht zu stimmen?

Aber egal, Hauptsache es läuft solide :-)

Edit: globale Variablen kann man mit GLOBAL anlegen..
Hier doch noch ein Versuch, bei dem das Setzen des Timers aus der ISR herausgenommen wird. (Aber Hauptsache es läuft bei Dir!)

Code: Alles auswählen


DIM Frequenz AS Shared word
DIM OldAddr AS SHARED DWORD

GLOBAL ISRCounter AS WORD

Frequenz = 2000
ClockOn

sleep

ClockOff


SUB ClockOn()

DIM zaehler AS STATIC word
DIM L AS STATIC byte
DIM H AS STATIC byte

  ! mov Word Ptr CS: SaveDS, DS ;store PowerBASIC Data segment
  GetInterruptVector &H08, OldAddr
  SetInterruptVector &H08, CODEPTR32(ClockISR)
  SetInterruptVector &H68, OldAddr

  zaehler = 1193180 / Frequenz
  L = zaehler
  shift right zaehler, 8  
  H = zaehler

  OUT (&H43), &b00110110
  OUT (&H40), L
  OUT (&H40), H
  ISRCounter = Frequenz / 18.2

END SUB


SUB ClockOff()
  SetInterruptVector &H08, OldAddr
  OUT (&H43), &b00110110
  OUT (&H40), 0
  OUT (&H40), 0
END SUB

SUB SetInterruptVector(BYVAL Intr?, BYVAL NewAddr???) LOCAL
  ! push ds ; save DS
  ! mov ah, &H25 ; set AH to 25h (function number)
  ! mov al, Intr? ; set AL to interrupt vector being redirected
  ! lds DX, NewAddr???
  ! int &H21 ; call DOS services
  ! pop ds ; restore DS
END SUB

SUB GetInterruptVector(BYVAL Intr?, VAddr???) LOCAL PUBLIC
  ! push ds ; save DS
  ! push si ; save SI
  ! mov ah, &H35 ; set AH to 35h (function number)
  ! mov al, Intr? ; set AL to interrupt vector
  ! int &H21 ; call DOS services (ES:BX now holds address)
  ! lds si, VAddr??? ; get a pointer to VSeg??
  ! mov ds:[si+2], es ; copy ES (segment address) into VSeg??
  ! mov ds:[si], bx ; copy BX (offset address) into VOff??
  ! pop si ; restore SI
  ! pop ds ; restore DS
END SUB


SUB ISR () PRIVATE

DIM IsrStack AS STATIC STRING * 1024 '1k stack (probably too much)
DIM Counter AS STATIC INTEGER

DIM l AS STATIC word


SaveDS:
! dw 0
SaveSS:
! dw 0
SaveSP:
! dw 0

ClockISR:
! push ax ; save the general registers
! push bx
! push cx
! push dx
! push si
! push di
! push bp
! push ds
! push es
! push bp
! pushf ; save the flags register
! cli ; suspend all maskable interrupts

! mov Word Ptr CS:SaveSS, SS ; save current stack segment
! mov Word Ptr CS:SaveSP, SP ; save current stack pointer
! mov DS, Word Ptr CS:SaveDS ; restore PowerBASIC data segment

' Create a local stack frame
! mov SS, Word Ptr CS:SaveDS ; SS must equal DS for PowerBASIC runtime
! lea BX, IsrStack ; get address of the local stack frame
! add BX, 1024 ; point to the end of the fixed-length string
! mov SP, BX ; set the stack pointer

' ** Your Basic code goes here **
l = inp(888)
bit toggle l, 1
OUT (888), l

IF Counter = 0 THEN
LOCATE 1, 72, 0
COLOR 14,0
PRINT TIME$;
Counter = 18 '18 ticks per second
END IF
DECR Counter

DECR ISRCounter
IF ISRCounter = 0 THEN
  ! sti
  ! int &H68
  ISRCounter = Frequenz / 18.2
ELSE
  OUT (&H20), &H20
END IF


' ** End of Basic code

! mov SP, Word Ptr CS:SaveSP ; restore the original stack segment
! mov SS, Word Ptr CS:SaveSS ; and stack pointer before restoring the registers

! popf ; restore the flags register
! pop bp ; restore the general registers
! pop es
! pop ds
! pop bp
! pop di
! pop si
! pop dx
! pop cx
! pop bx
! pop ax
! sti ; re-enable maskable interrupt processing
! iret ; return from our interrupt handler

END SUB

Re: PWM mit Timer erzeugen ?

von Nightflyer » Mo 9. Dez 2013, 18:51

Aha alles klar. Gibt es irgendwo eine Tabelle/Beschreibung von den ganzen Interrupts ? Ich hab nähmlich nur so eine Portlist: http://bochs.sourceforge.net/techspec/PORTS.LST

Den Timer muss ich in der ISR neu setzten. Sonst passiert folgendes: Timer an Port H40 mit Wert 1193 laden, der Wert wird nun herunter gezählt 1192, 1191, 1190... 3, 2, 1, 0 -> ISR wird aufgerufen, danach wird der Wert weiter runtergezählt, ist er jedoch bereits bei 0, überläuft der Timer und beginnt wieder bei 65535.... Deshalb muss ich den Timer immer wieder neu setzten.

Durch leichte Anpassung deines Code, funktioniert dieser einwandfrei. Frequenz wird am LPT ausgegeben, und die Uhr stimmt ebenfalls.

Code:

DIM Frequenz AS Shared word
DIM OldAddr AS SHARED DWORD

Frequenz = 2000
ClockOn

sleep

ClockOff


SUB ClockOn()
! mov Word Ptr CS: SaveDS, DS ;store PowerBASIC Data segment
GetInterruptVector &H08, OldAddr
SetInterruptVector &H08, CODEPTR32(ClockISR)
SetInterruptVector &H68, OldAddr
END SUB


SUB ClockOff()
SetInterruptVector &H08, OldAddr
END SUB

SUB SetInterruptVector(BYVAL Intr?, BYVAL NewAddr???) LOCAL
! push ds ; save DS
! mov ah, &H25 ; set AH to 25h (function number)
! mov al, Intr? ; set AL to interrupt vector being redirected
! lds DX, NewAddr???
! int &H21 ; call DOS services
! pop ds ; restore DS
END SUB

SUB GetInterruptVector(BYVAL Intr?, VAddr???) LOCAL PUBLIC
! push ds ; save DS
! push si ; save SI
! mov ah, &H35 ; set AH to 35h (function number)
! mov al, Intr? ; set AL to interrupt vector
! int &H21 ; call DOS services (ES:BX now holds address)
! lds si, VAddr??? ; get a pointer to VSeg??
! mov ds:[si+2], es ; copy ES (segment address) into VSeg??
! mov ds:[si], bx ; copy BX (offset address) into VOff??
! pop si ; restore SI
! pop ds ; restore DS
END SUB


SUB ISR () PRIVATE

DIM IsrStack AS STATIC STRING * 1024 '1k stack (probably too much)
DIM Counter AS STATIC INTEGER

DIM zaehler AS STATIC word
DIM L AS STATIC byte
DIM H AS STATIC byte

DIM ISRCounter AS STATIC word

SaveDS:
! dw 0
SaveSS:
! dw 0
SaveSP:
! dw 0

ClockISR:
! push ax ; save the general registers
! push bx
! push cx
! push dx
! push si
! push di
! push bp
! push ds
! push es
! push bp
! pushf ; save the flags register
! cli ; suspend all maskable interrupts

! mov Word Ptr CS:SaveSS, SS ; save current stack segment
! mov Word Ptr CS:SaveSP, SP ; save current stack pointer
! mov DS, Word Ptr CS:SaveDS ; restore PowerBASIC data segment

' Create a local stack frame
! mov SS, Word Ptr CS:SaveDS ; SS must equal DS for PowerBASIC runtime
! lea BX, IsrStack ; get address of the local stack frame
! add BX, 1024 ; point to the end of the fixed-length string
! mov SP, BX ; set the stack pointer

' ** Your Basic code goes here **

zaehler = 1193180 / Frequenz

L = zaehler
shift right zaehler, 8
H = zaehler
OUT (&H43), &b00110110
OUT (&H40), L
OUT (&H40), H

l = inp(888)
bit toggle l, 1
OUT (888), l

IF Counter = 0 THEN
LOCATE 1, 72, 0
COLOR 14,0
PRINT TIME$;
Counter = 18 '18 ticks per second
END IF
DECR Counter

IF ISRCounter = 0 THEN
! int &H68
ISRCounter = Frequenz / 18.2
ELSE
OUT (&H20), &H20
END IF
DECR ISRCounter


' ** End of Basic code

! mov SP, Word Ptr CS:SaveSP ; restore the original stack segment
! mov SS, Word Ptr CS:SaveSS ; and stack pointer before restoring the registers

! popf ; restore the flags register
! pop bp ; restore the general registers
! pop es
! pop ds
! pop bp
! pop di
! pop si
! pop dx
! pop cx
! pop bx
! pop ax
! sti ; re-enable maskable interrupt processing
! iret ; return from our interrupt handler

END SUB

Re: PWM mit Timer erzeugen ?

von wobo » So 8. Dez 2013, 20:12

Nightflyer hat geschrieben:Ok, hab ich mir auch so ähnlich überlegt...

Nur frag ich mich, wie genau die Uhr überhaupt hochgezählt wird ? Geschieht dies Hardwaremässig bei jedem Interrupt automatisch, oder eben per Software in der ISR ? Würde dies per Software geschehen, so müsste mit der neuen ISR die Uhr gar nicht mehr hochgezählt werden, weil in dieser werden nur Register gesichert, einen Stack angelegt, LPT Output gesetzt, usw... Da wird also nirgends etwas in ein Register oder so geschrieben.
Sollte dies nun per Hardware geschehen, muss ich denn irgend wo einen Arbeitsmode umstellen, damit in meiner ISR die Uhr nicht hochgezählt wird ?
Das hat mich auch schon gewundert. Wenn Du nämlich die alte ISR nicht aufrufst kann auch nichts hochgezählt werden.
Aber im Listing verwendest du Int $1C und nicht Int$08. Dann is' klar, dass die Uhr rennt.
Und wieso setzt Du _innerhalb_ Deiner ISR den Timer neu? Du solltest Du eigentlich nicht, weil das nicht so gesund für den Timer-Chip sein dürfte, wenn er ca. 1000/sec neu gesetzt wird (IMHO).

Und warum hast die Belegung des Auswahlregisters auskommentiert?

So hätte ich es jetzt versucht (ungetestet, da ich kein PowerBasic habe!):

Code: Alles auswählen


DIM Frequenz AS Shared word
DIM OldAddr AS SHARED DWORD
DIM ISRCounterBase AS SHARED word
DIM ISRCounter AS SHARED word

Frequenz = 1000
ClockOn

sleep

ClockOff


SUB ClockOn()

DIM zaehler AS STATIC word
DIM L AS STATIC byte
DIM H AS STATIC byte

! mov Word Ptr CS: SaveDS, DS ;store PowerBASIC Data segment

zaehler = 1193180 / Frequenz

ISRCounterBase = zaehler
ISRCounter = ISRCounterBase

L = zaehler
shift right zaehler, 8
H = zaehler
OUT (&H43), &b00110110
OUT (&H40), L
OUT (&H40), H

GetInterruptVector &H08, OldAddr
SetInterruptVector &H08, CODEPTR32(ClockISR)
SetInterruptVector &H68, OldAddr

END SUB


SUB ClockOff()

OUT (&H43), &b00110110
OUT (&H40), 0
OUT (&H40), 0
SetInterruptVector &H08, OldAddr

END SUB

SUB SetInterruptVector(BYVAL Intr?, BYVAL NewAddr???) LOCAL
! push ds ; save DS
! mov ah, &H25 ; set AH to 25h (function number)
! mov al, Intr? ; set AL to interrupt vector being redirected
! lds DX, NewAddr???
! int &H21 ; call DOS services
! pop ds ; restore DS
END SUB

SUB GetInterruptVector(BYVAL Intr?, VAddr???) LOCAL PUBLIC
! push ds ; save DS
! push si ; save SI
! mov ah, &H35 ; set AH to 35h (function number)
! mov al, Intr? ; set AL to interrupt vector
! int &H21 ; call DOS services (ES:BX now holds address)
! lds si, VAddr??? ; get a pointer to VSeg??
! mov ds:[si+2], es ; copy ES (segment address) into VSeg??
! mov ds:[si], bx ; copy BX (offset address) into VOff??
! pop si ; restore SI
! pop ds ; restore DS
END SUB


SUB ISR () PRIVATE

DIM IsrStack AS STATIC STRING * 1024 '1k stack (probably too much)
DIM Counter AS STATIC INTEGER

SaveDS:
! dw 0
SaveSS:
! dw 0
SaveSP:
! dw 0

ClockISR:
! push ax ; save the general registers
! push bx
! push cx
! push dx
! push si
! push di
! push bp
! push ds
! push es
! push bp
! pushf ; save the flags register
! cli ; suspend all maskable interrupts

! mov Word Ptr CS:SaveSS, SS ; save current stack segment
! mov Word Ptr CS:SaveSP, SP ; save current stack pointer
! mov DS, Word Ptr CS:SaveDS ; restore PowerBASIC data segment

' Create a local stack frame
! mov SS, Word Ptr CS:SaveDS ; SS must equal DS for PowerBASIC runtime
! lea BX, IsrStack ; get address of the local stack frame
! add BX, 1024 ; point to the end of the fixed-length string
! mov SP, BX ; set the stack pointer

' ** Your Basic code goes here **

l = inp(888)
bit toggle l, 1
OUT (888), l

IF Counter = 0 THEN
LOCATE 1, 72, 0
COLOR 14,0
PRINT TIME$;
Counter = 18 '18 ticks per second
END IF
DECR Counter

  IF ISRCounter = 0 THEN
   ISRCounter = ISRCounterBase
    ! int &H68
  ELSE
    OUT (&H20), &H20
  END IF
  DECR ISRCounter


' ** End of Basic code

! mov SP, Word Ptr CS:SaveSP ; restore the original stack segment
! mov SS, Word Ptr CS:SaveSS ; and stack pointer before restoring the registers

! popf ; restore the flags register
! pop bp ; restore the general registers
! pop es
! pop ds
! pop bp
! pop di
! pop si
! pop dx
! pop cx
! pop bx
! pop ax
! sti ; re-enable maskable interrupt processing
! iret ; return from our interrupt handler

END SUB

Re: PWM mit Timer erzeugen ?

von Nightflyer » So 8. Dez 2013, 18:55

Ok, hab ich mir auch so ähnlich überlegt...

Nur frag ich mich, wie genau die Uhr überhaupt hochgezählt wird ? Geschieht dies Hardwaremässig bei jedem Interrupt automatisch, oder eben per Software in der ISR ? Würde dies per Software geschehen, so müsste mit der neuen ISR die Uhr gar nicht mehr hochgezählt werden, weil in dieser werden nur Register gesichert, einen Stack angelegt, LPT Output gesetzt, usw... Da wird also nirgends etwas in ein Register oder so geschrieben.
Sollte dies nun per Hardware geschehen, muss ich denn irgend wo einen Arbeitsmode umstellen, damit in meiner ISR die Uhr nicht hochgezählt wird ?

Hier mein Code:
Ist eigentlich das gleiche wie in dem Beispiel: https://www.powerbasic.com/support/pbfo ... hp?t=22856

[code]

ClockOn
DIM Frequenz AS Shared word
Frequenz = 1000
'OUT (&H43), &b11110110

sleep

ClockOff

SUB ClockOn()

DIM OldAddr AS SHARED DWORD

! mov Word Ptr CS: SaveDS, DS ;store PowerBASIC Data segment

GetInterruptVector &H1C, OldAddr
SetInterruptVector &H1C, CODEPTR32(ClockISR)

END SUB

SUB ClockOff()

DIM OldAddr AS SHARED DWORD

SetInterruptVector &H1C, OldAddr

END SUB

SUB SetInterruptVector(BYVAL Intr?, BYVAL NewAddr???) LOCAL
! push ds ; save DS
! mov ah, &H25 ; set AH to 25h (function number)
! mov al, Intr? ; set AL to interrupt vector being redirected
! lds DX, NewAddr???
! int &H21 ; call DOS services
! pop ds ; restore DS
END SUB

SUB GetInterruptVector(BYVAL Intr?, VAddr???) LOCAL PUBLIC
! push ds ; save DS
! push si ; save SI
! mov ah, &H35 ; set AH to 35h (function number)
! mov al, Intr? ; set AL to interrupt vector
! int &H21 ; call DOS services (ES:BX now holds address)
! lds si, VAddr??? ; get a pointer to VSeg??
! mov ds:[si+2], es ; copy ES (segment address) into VSeg??
! mov ds:[si], bx ; copy BX (offset address) into VOff??
! pop si ; restore SI
! pop ds ; restore DS
END SUB

SUB ISR () PRIVATE

DIM IsrStack AS STATIC STRING * 1024 '1k stack (probably too much)
DIM Counter AS STATIC INTEGER

DIM zaehler AS STATIC word
DIM L AS STATIC byte
DIM H AS STATIC byte

SaveDS:
! dw 0
SaveSS:
! dw 0
SaveSP:
! dw 0

ClockISR:
! push ax ; save the general registers
! push bx
! push cx
! push dx
! push si
! push di
! push bp
! push ds
! push es
! push bp
! pushf ; save the flags register
! cli ; suspend all maskable interrupts

! mov Word Ptr CS:SaveSS, SS ; save current stack segment
! mov Word Ptr CS:SaveSP, SP ; save current stack pointer
! mov DS, Word Ptr CS:SaveDS ; restore PowerBASIC data segment

' Create a local stack frame
! mov SS, Word Ptr CS:SaveDS ; SS must equal DS for PowerBASIC runtime
! lea BX, IsrStack ; get address of the local stack frame
! add BX, 1024 ; point to the end of the fixed-length string
! mov SP, BX ; set the stack pointer

' ** Your Basic code goes here **

zaehler = 1193180 / Frequenz
L = zaehler
shift right zaehler, 8
H = zaehler
'OUT (&H43), &b00110110
OUT (&H40), L
OUT (&H40), H

l = inp(888)
bit toggle l, 1
OUT (888), l

IF Counter = 0 THEN
LOCATE 1, 72, 0
COLOR 14,0
PRINT TIME$;
Counter = 18 '18 ticks per second
END IF
DECR Counter

' ** End of Basic code

! mov SP, Word Ptr CS:SaveSP ; restore the original stack segment
! mov SS, Word Ptr CS:SaveSS ; and stack pointer before restoring the registers

! popf ; restore the flags register
! pop bp ; restore the general registers
! pop es
! pop ds
! pop bp
! pop di
! pop si
! pop dx
! pop cx
! pop bx
! pop ax
! sti ; re-enable maskable interrupt processing
! iret ; return from our interrupt handler

END SUB

[/code]

Re: PWM mit Timer erzeugen ?

von wobo » So 8. Dez 2013, 04:54

Nightflyer hat geschrieben:Super vielen Dank für das Beispiel, genau sowas habe ich gesucht.

Habe auch gleich einige Versuche damit gemacht. In der neuen ISR habe ich die Berechnung der Frequenz und das setzten des Output Bits am LPT eingefügt, und funktioniert auch einwandfrei. Ausserhalb der ISR kann ich nun eine Frequenz festlegen, und diese wird dann am LPT ausgegeben. Genau so soll es sein ! :-) :-)

Nun habe ich trotzdem noch ein paar Fragen:
Ausserhalb der ISR hab ich einer variable FREQUENZ als SHARED deklariert, damit ich in der ISR darauf zugreifen kann. In der ISR selber wurde jedoch per ASM einen lokalen STACK angelegt. Können irgendwelche Probleme entstehen, mit dem lokalen Stack und der SHARED Variable von Powerbasic ? Bis jetzt funktionierts, frage aber trotzdem...
Halte ich für ausgeschlossen, dass das Probleme gibt, jedenfalls nicht bei einer Handvoll Variablen - ich habe aber nur sehr, sehr wenig Erfahrung mit PowerBasic! Vielleicht kann da einer der Basic-Profis was sagen...
Nightflyer hat geschrieben: Durch die neue Frequenz, läuft natürlich auch die Systemuhr schneller.. ;-) Wie genau muss ich dies zurecht biegen, damit die Uhr trotzdem stimmt ?
Du mußt innerhalb Deiner ISR die alte ISR aufrufen, und zwar so, dass sie wieder mit ca. 18.2 Mal die Sekunde aufgerufen wird.

Wahrscheinlich sicherst Du die alte ISR so:

Code: Alles auswählen

  GetInterruptVector &H08, OldAddr
  SetInterruptVector &H08, CODEPTR32(ClockISR)
Wenn Du jetzt die alte ISR auch aufrufen willst, dann solltest Du das am Besten als Int tun, da die alte ISR ja als Interrupthandler konzipiert ist. Die Systemliteratur sagt, dass z.B. die Int ab $68 bis $6F zur Benutzung in Anwendungsprogrammen frei sind. Du kannst also die alte ISR z.B. noch auf den Int $68 legen und dann als Int aufrufen. Außerdem weisst Du ja die Frequenz, mit der Deine neue ISR aufgerufen wird. Wenn diese z.B. 1000Hz, dann musst Du hier immer 1000/18.2 Aufrufe verstreichen lassen, bis es soweit ist, dass die ursprüngliche ISR wieder aufgerufen werden soll.
z.B. so:

Code: Alles auswählen

DIM OldIntCounterBase AS SHARED DWORD
DIM OldIntCounter AS SHARED DWORD
DIM ISRFrequenz AS SHARED WORD

  GetInterruptVector &H08, OldAddr
  SetInterruptVector &H08, CODEPTR32(ClockISR)
  SetInterruptVector &H68, OldAddr
  
  OldIntCounterBase = 1193180 / ISRFrequenz
  OldIntCounter = OldIntCounterBase

Im Basic-Teil Deiner ISR musst Du jetzt halt dafür sorgen, dass die alte ISR ca 18.2 / Sekunde aufgerufen wird, also in etwa so:

Code: Alles auswählen

** Your Basic code goes here **
  
  IF OldIntCounter = 0 THEN
    OldIntCounter = OldIntCounterBase
    ! int &H68
  END IF
  DECR OldIntCounter

Die Systemuhr wird aber dennoch leicht ungenau gehen, weil Du sie in der Regel nicht exakt mit 18.2 Hz aufrufen kannst. Die hier vorgestellte Vorgehensweise ist nur eine ungefähre Annäherung, die mir aber bislang immer genügt hat.

Aber vielleicht postest Du einfach Deine ISR mal hier im Forum. Dann können sich das auch mal echte Basic-Profis anguggen. Ich kann nämlich gar kein Basic mehr :-)

Edit: Viele Nächtliche Fehler ausgebessert... ..und nochmal Fehler....

Re: PWM mit Timer erzeugen ?

von Nightflyer » Sa 7. Dez 2013, 13:25

Super vielen Dank für das Beispiel, genau sowas habe ich gesucht.

Habe auch gleich einige Versuche damit gemacht. In der neuen ISR habe ich die Berechnung der Frequenz und das setzten des Output Bits am LPT eingefügt, und funktioniert auch einwandfrei. Ausserhalb der ISR kann ich nun eine Frequenz festlegen, und diese wird dann am LPT ausgegeben. Genau so soll es sein ! :-) :-)

Nun habe ich trotzdem noch ein paar Fragen:
Ausserhalb der ISR hab ich einer variable FREQUENZ als SHARED deklariert, damit ich in der ISR darauf zugreifen kann. In der ISR selber wurde jedoch per ASM einen lokalen STACK angelegt. Können irgendwelche Probleme entstehen, mit dem lokalen Stack und der SHARED Variable von Powerbasic ? Bis jetzt funktionierts, frage aber trotzdem...

Durch die neue Frequenz, läuft natürlich auch die Systemuhr schneller.. ;-) Wie genau muss ich dies zurecht biegen, damit die Uhr trotzdem stimmt ?

Re: PWM mit Timer erzeugen ?

von wobo » Sa 7. Dez 2013, 09:05

Du willst Systemprogrammierung machen mit Powerbasic, hast aber noch keine Ahnung von den Interrupts? Dann hast Du viel vor ... :-)

Ganz am Anfang des Speichers findet sich die sog. Interrupt-Tabelle. Diese enthält für jeden Interrupt einen Zeiger - dass der PC-Speicher unter DOS unter Bezug auf Segment und Offset adressiert wird, weißt Du? - auf die Segment- und Offset-Adresse der zum dem jeweiligen Interrupt gehörenden ISR.

Man kann zwar auf diese Interrupt-Tabelle direkt zugreifen, sollte dies aber wie immer über die jeweiligen MS-DOS-Interrupts Nr. $25, $35 der DOS Api (Int $21) machen.

Außerdem kannst Du nicht so ohne weiteres Deine jeweilige SUB zur ISR machen, da diese einen anderen Aufbau haben müssen als die Standard Unterprogramme (SUB) in BASIC.

Auch wenn ich glaube, dass Powerbasic das beste BASIC ist, wird dies schwierig, weil PowerBasic, z.B. im Gegensatz zu Turbo Pascal, Dich in der Systemprogrammierung nicht unterstützt, wie z.B. TP es mit den Befehlen SetIntVec, GetIntVec und die Anweisung Interrupt tut.

PS: Hier findest Du ein meiner Meinung nach extrem gut programmiertes Beispiel für SetIntVec, GetIntVec und eine PowerBasic-SUB als ISR:
https://www.powerbasic.com/support/pbfo ... hp?t=22856

Re: PWM mit Timer erzeugen ?

von Nightflyer » Fr 6. Dez 2013, 17:57

Also der erste Test mit dem Timer 0 hat funktioniert !
Ich habe den Timer mit h36 konfiguriert und mit dem wert 1193 geladen, und schon läuft die Systemuhr viel schneller... :-)

Nun nochmal die geschichte mit der ISR, ich begreif irgendwie das prinzip nicht ganz...
Im interrupthandler ist unter INT8 die Adresse der ISR abgelegt. Wo genau ist der Interrupthandler ? Ist INT8 immer am gleichen Ort abgelegt ?

Ich programmiere mit Powerbasic, dort kann ich die effektive Adresse meiner Prozedur abfragen (mit CODEPTR, CODESEG, CODEPTR32). Ich hab dann einen OFFSET und ein SEGMENT, was bedeutet das genau ? Ist das das richtige ? Mit CODEPTR32 habe ich eine 32bit Adresse.

Re: PWM mit Timer erzeugen ?

von S+M » Do 5. Dez 2013, 17:02

wobo hat geschrieben:Soo, gegoogelt habe ich!

Die 4pin-Anschlüsse kenne ich: das sind die, die von den Festplatten immer so schwer abgehen und von denen ich immer die Vermutung hatte, dass die den Saft und nicht die Daten liefern.
Der Pulsweitenmodulationseintrag in Wikipedia übersteigt jetzt doch mein Grundlagenwissen enorm. Bei Gelegenheit werde ich mich mal informieren müssen, was Strom eigentlich ist... Und zu den Lüftern: PCs hören für mich eh beim am486dx40 auf, dem schnellsten Prozessor ohne Lüfter. Alles, was drüber ist, ist was für Fachleute!
Du liegst richtig, diese 4-Pin Stecker liefern den "Saft"
Es ist von einem kleineren 4-Pin Anschluss auf dem Mainboard die Rede ;-)

Re: PWM mit Timer erzeugen ?

von Nightflyer » Do 5. Dez 2013, 11:32

Super !! Vielen dank für eure Antworten !
Ich werde das gleich mal ausprobieren...

Re: PWM mit Timer erzeugen ?

von freecrac » Mi 4. Dez 2013, 23:33

wobo hat geschrieben:@FreeCrack:Wie überrpüfst Du eigentlich, ob ein Int mit einer Service-Routine belegt ist? Zeigt die dann auf Null? Oder ist das nicht standardisiert?
Man könnte schauen ob es zu einer ROM-Adresse führt.
Ein TSR wird sich entweder im unteren Speicher oder den UMBS befinden.
Ich glaube beim int 1Ch wird defaultmässig auch nur ein Rücksprung gemacht.

Zum Setzen und ermitteln kenne ich sonst noch diese Interrupts:
RBIL->inter61b.zip->INTERRUP.F

Code: Alles auswählen

--------D-2125-------------------------------
INT 21 - DOS 1+ - SET INTERRUPT VECTOR
	AH = 25h
	AL = interrupt number
	DS:DX -> new interrupt handler
Notes:	this function is preferred over direct modification of the interrupt
	  vector table
	some DOS extenders place an API on this function, as it is not
	  directly meaningful in protected mode
	under DR DOS 5.0-6.0, this function does not use any of the
	  DOS-internal stacks and may thus be called at any time; however,
	  under Novell DOS 7.0 - DR-DOS 7.02, this function was not reentrant.
	  Since 1998/05/29, DR-DOS 7.03 no longer uses any internal stacks and
	  tests for this function much earlier, to allow a minimal stack usage
	  of just two words in addition to the IRET frame, allowing it to be
	  called from INT 21h functions, specificially device drivers.  This
	  fixes the MCS SMB client
	Novell NetWare (except the new DOS Requester) monitors the offset of
	  any INT 24 set, and if equal to the value at startup, substitutes
	  its own handler to allow handling of network errors; this introduces
	  the potential bug that any program whose INT 24 handler offset
	  happens to be the same as COMMAND.COM's will not have its INT 24
	  handler installed
SeeAlso: AX=2501h,AH=35h

--------D-2135-------------------------------
INT 21 - DOS 2+ - GET INTERRUPT VECTOR
	AH = 35h
	AL = interrupt number
Return: ES:BX -> current interrupt handler
Note:	under DR DOS 5.0+, this function does not use any of the DOS-internal
	  stacks and may thus be called at any time
SeeAlso: AH=25h,AX=2503h
Auf null wird wohl kein Pointer stehen dürfen.

Dirk

Re: PWM mit Timer erzeugen ?

von wobo » Mi 4. Dez 2013, 23:09

@FreeCrack:Wie überrpüfst Du eigentlich, ob ein Int mit einer Service-Routine belegt ist? Zeigt die dann auf Null? Oder ist das nicht standardisiert?

Re: PWM mit Timer erzeugen ?

von wobo » Mi 4. Dez 2013, 23:03

Nightflyer hat geschrieben:Super !! Vielen dank, das wahr schon sehr hilfreich !

Wenn ich das richtig verstehe ist IRQ0 und INT8 das gleiche. Es kann also nur einer der drei Timer ein Interrupt auslösen ? Standardmässig ist dies Timer0 für die Uhr. Wie muss ich nun das Interrupt auf den Timer 2 umschalten ? Oder gleich den Timer0 verwenden ? Und wie setzte ich meine eigene ISR auf INT8 ?

Noch was:
Laufen alle drei Timer gleich schnell und haben die alle 16bit ?
Richtig, der Timer-Chip (intel 8253) hat drei Kanäle. Kanal 0 steuert die Systemuhr, Kanal 1 macht einen DMA-Refresh, Kanal 2 wird in der Regel für den Speaker verwendet und ist laut Systemliteratur zur allgemeinen Benutzung frei. Allerdings erzeugt nur Kanal 0 einen IRQ, nämlich den IRQ 0 = Int 8.

Der Timer hat immer 1.19318 Mhz. Jedem Kanal kann aber ein eigener Zähler zugewiesen werden, und zwar über Port 0x40 für Kanal 0, über Port 0x41 für Kanal 1 und über Port 0x42 für kanal 2. Der Zähler wird nach jedem Tick um eins für jeden Kanal heruntergezählt. Standardmäßig ist als Zähler Null eingestellt, was wegen des Herunterzählens als 65536 (Überlauf!) vom Intel 8253 aufgefasst wird. Daher kommt auch die Standardfrequenz von 18,2 Hz, nämlich 1193180/65536=18.2 (ca.).

Willst Du jetzt statt 18.2 Hz, dass der Timer über z.B. Kanal 0 mit 1000 Hz arbeitet, dann musst du den Zähler 1193180/1000=1193 an Port 0x40 senden. Kanal 0 löst dann mit ca. 1000 Hz den IRQ0 aus.

Zuvor musst du aber noch über das Steuerregister Port 0x43 Arbeitsmodus und Kanal für den Arbeitsmodus einstellen. Ich verwende hier immer 0x36, nämlich Kanal 0 (bit 7+6=0) und als Zähler zuerst lowest significant byte, dann most significant byte (bit 5+4 gesetzt), da wir als Zähler einen 16bit-Wert übergeben. Zuletzt noch Modus 3 (bits 1-3 auf 3, weil Kanal 0 nur im Modus 3 arbeitet, weiß ich aber nicht mehr genau...) und bit 0 auf 0, weil ich keine BCD-Zahlen übergebe = 0x36 bzw. 54 decimal.

Die eigene ISR setzt du am besten mit der DOS-APi (Int. 0x21), Funktion 0x25 (ISR-Setzen) bzw. 0x35 (ISR-Lesen). IRQ-Acknowledge nicht vergessen, wenn Du nicht die alte ISR aufrufen musst (weil noch keine 1/18.2 Sek um sind).


Oder In Pascal :-)

Code: Alles auswählen

USES CRT;
VAR OldInt8 : pointer;
    TimerCounter,
    CallOldTimer : word;

PROCEDURE SetTimer( NewInt8:pointer; MyFreq : word );
VAR MyZaehler:word;
BEGIN
  MyZaehler := 1193180 div MyFreq;
  CallOldTimer := Round( MyFreq / 18.2 );
  TimerCounter := CallOldTimer;
  asm cli end;
  Port[$43] := $36;
  Port[$40] := lo(MyZaehler);
  Port[$40] := hi(MyZaehler);
  GetIntVec(8, OldInt8 );
  SetIntVec($1A, OldInt8 );
  SetintVec(8, NewInt8 );
  asm sti end;
END;
  
PROCEDURE RestoreTimer;
BEGIN
  asm cli end;
  Port[$43] := $36;
  Port[$40] := 0;
  Port[$40] := 0;
  SetIntVec(8, OldInt8 );
  asm sti end;
END;  

PROCEDURE MyTimer; interrupt;
BEGIN
  dec( TimerCounter );
  if TimerCounter = 0 then begin
    TimerCounter := CallOldTimer;
    asm int $1A end;
  end 
  else Port[$20] := $20;
END;

BEGIN
  SetTimer( @MyTimer, 1000 );
  Repeat Until KeyPressed;
  RestoreTimer;
END.

Nach oben