PWM mit Timer erzeugen ?

Hier dürfen auch unregistrierte Besucher posten.
Nightflyer

PWM mit Timer erzeugen ?

Beitrag von Nightflyer »

Hallo Zusammen

Ich habe eine Software programmiert, die ein PWM am LPT erzeugt. Das ganze läuft auf FreeDOS. Soweit so gut. Bis jetzt habe ich das PWM ohne Timer oder so ähnlich erzeugt, was halt nicht ganz optimal ist. Besser währe, wenn ich einen Timer verwenden könnte, der nach Ablauf eine ISR aufruft. In der ISR wird dann der Output Pin auf H oder L gesetzt. Soweit sollte alles klar sein....

Nun habe ich mit dem Timer 2 (h43) der irgendwie noch für den Speaker zuständig ist einige Versuche gemacht. Timer mit einem Wert laden funktioniert, und Zählt dann auch runter bis 0, so weit so gut.

Nun zum Problem:
Wie sag ich dem Prozessor nun, das er nach Ablauf des Timers ein interrupt auslösen soll und in eine ISR springen ?

Ich hoffe ich bin mit meinem Problem am richtigen Ort, und hoffe natürlich, dass mir jemand helfen kann.
Besten Dank
wobo
DOS-Guru
Beiträge: 613
Registriert: So 17. Okt 2010, 14:40

Re: PWM mit Timer erzeugen ?

Beitrag von wobo »

Was ist ein PWM?

Der Prozessor kann keinen IRQ auslösen. Der Timer selbst löst einen IRQ aus, und zwar den IRQ0, welcher Interrupt No.8 ist.
Also eigene ISR auf Int 8 setzen und dabei darauf achten, den alten Interrupt-Handler weiterhin 18.2 / Sekunde aufzurufen.
Benutzeravatar
Dosenware
DOS-Gott
Beiträge: 3745
Registriert: Mi 24. Mai 2006, 20:29

Re: PWM mit Timer erzeugen ?

Beitrag von Dosenware »

Pulsweitenmodulation wird z.b. bei 4pin Lüftern zur Drehzahlsteuerung eingesetzt
wobo
DOS-Guru
Beiträge: 613
Registriert: So 17. Okt 2010, 14:40

Re: PWM mit Timer erzeugen ?

Beitrag von wobo »

Dosenware hat geschrieben:Pulsweitenmodulation wird z.b. bei 4pin Lüftern zur Drehzahlsteuerung eingesetzt
Ich müßte jetzt fragen, was 4pin Lüfter sind. Aber ich google erstmal ... :-) [PS: Danke für die Info!]
wobo
DOS-Guru
Beiträge: 613
Registriert: So 17. Okt 2010, 14:40

Re: PWM mit Timer erzeugen ?

Beitrag von wobo »

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!
Nightflyer

Re: PWM mit Timer erzeugen ?

Beitrag von Nightflyer »

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 ?
freecrac
DOS-Guru
Beiträge: 861
Registriert: Mi 21. Apr 2010, 11:44
Wohnort: Hamburg Horn

Re: PWM mit Timer erzeugen ?

Beitrag von freecrac »

Moin.
Zum besseren Verständniss was der Timerinterrupt genau macht:

Original-ROM-BIOS Vers.3.1 Prozedur:

Code: Alles auswählen

TIMER_INT:         sti              ; Interrupts erlauben
                   push    ax
                   push    bx
                   push    cx
                   push    dx
                   push    ds
                   push    es
                   mov     ax,40h
                   mov     ds,ax
                   inc   WORD PTR ds:[6Ch]          ; Timer_low
                   jnz   T1
                   inc   WORD PTR ds:[6Eh]          ; Timer_high
T1:                cmp   WORD PTR ds:[6Eh],018h     ; Timer_high
                   jnz   T2
                   cmp   WORD PTR ds:[6Ch],0B0h     ; Timer_low
                   jnz   T2

                   mov   WORD PTR ds:[6Eh],0        ; Timer_high
                   mov   WORD PTR ds:[6Ch],0        ; Timer_low
                   mov   BYTE PTR ds:[70h],1        ; Timer_OFL

T2:                dec   BYTE PTR ds:[40h]          ; Motor_count
                   jnz   T3
                   and   BYTE PTR ds:[3Fh],0F0h     ; Motor_status
                   mov     al,0Ch
                   mov     dx,03F2h
                   out     dx,al

T3:                int     1Ch            ; Anschluss fuer Benutzerprozedur
                   mov     al,020h        ; =EOI
                   out     020h,al
                   pop     es
                   pop     ds
                   pop     dx
                   pop     cx
                   pop     bx
                   pop     ax
                   iret
Und so könnte man den Timerinterupt auf obige ISR verlegen.

Code: Alles auswählen

                   mov     ax,0
                   mov     ds,ax
                   cli
                   mov  WORD PTR ds:[32],OFFSET TIMER_INT
                   mov  WORD PTR ds:[34],cs
                   sti
Zum Zurückschalten auf die alte Routine wäre es noch zweckmässig den vorherigen Pointer auszulesen und zu speichern.
Auf diese Weise könnte die eigene Routine(mit anderen Inhalt) auch am Ende zum originalen Timerinterrupt mit einem Far Jump weiterspringen anstelle selber ein EOI zu senden und mit iret zurückzukehren.

Unsere Routine darf selber aber keine anderen Software-Interuppts verwenden, der Bildschirmspeicher kann aber beschrieben werden, um z.B. eine Uhrzeit anzeigen zu lassen.

Default-Einstellung des Timers:

Code: Alles auswählen

          cli
          mov      al, 36h            ; default 18,2 Hertz
          out      43h, al
          xor      al, al
          out      40h, al            ; low
          out      40h, al            ; high
          sti
Dirk
Zuletzt geändert von freecrac am Mi 4. Dez 2013, 23:12, insgesamt 1-mal geändert.
wobo
DOS-Guru
Beiträge: 613
Registriert: So 17. Okt 2010, 14:40

Re: PWM mit Timer erzeugen ?

Beitrag von wobo »

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.
Zuletzt geändert von wobo am Sa 7. Dez 2013, 09:04, insgesamt 1-mal geändert.
wobo
DOS-Guru
Beiträge: 613
Registriert: So 17. Okt 2010, 14:40

Re: PWM mit Timer erzeugen ?

Beitrag von wobo »

@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?
freecrac
DOS-Guru
Beiträge: 861
Registriert: Mi 21. Apr 2010, 11:44
Wohnort: Hamburg Horn

Re: PWM mit Timer erzeugen ?

Beitrag von freecrac »

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
Nightflyer

Re: PWM mit Timer erzeugen ?

Beitrag von Nightflyer »

Super !! Vielen dank für eure Antworten !
Ich werde das gleich mal ausprobieren...
S+M
DOS-Übermensch
Beiträge: 1059
Registriert: Mo 10. Jun 2013, 17:04
Wohnort: BW

Re: PWM mit Timer erzeugen ?

Beitrag von S+M »

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 ;-)
Nightflyer

Re: PWM mit Timer erzeugen ?

Beitrag von Nightflyer »

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.
wobo
DOS-Guru
Beiträge: 613
Registriert: So 17. Okt 2010, 14:40

Re: PWM mit Timer erzeugen ?

Beitrag von wobo »

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
Nightflyer

Re: PWM mit Timer erzeugen ?

Beitrag von Nightflyer »

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 ?
Antworten