PWM mit Timer erzeugen ?

Hier dürfen auch unregistrierte Gäste posten.

PWM mit Timer erzeugen ?

Beitragvon Nightflyer » Mi 4. Dez 2013, 17:17

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
Nightflyer
 

Re: PWM mit Timer erzeugen ?

Beitragvon wobo » Mi 4. Dez 2013, 20:00

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

Re: PWM mit Timer erzeugen ?

Beitragvon Dosenware » Mi 4. Dez 2013, 20:20

Pulsweitenmodulation wird z.b. bei 4pin Lüftern zur Drehzahlsteuerung eingesetzt
Benutzeravatar
Dosenware
DOS-Gott
 
Beiträge: 3529
Registriert: Mi 24. Mai 2006, 19:29

Re: PWM mit Timer erzeugen ?

Beitragvon wobo » Mi 4. Dez 2013, 20:29

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: 555
Registriert: So 17. Okt 2010, 13:40

Re: PWM mit Timer erzeugen ?

Beitragvon wobo » Mi 4. Dez 2013, 20:58

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

Re: PWM mit Timer erzeugen ?

Beitragvon Nightflyer » Mi 4. Dez 2013, 21:35

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

Re: PWM mit Timer erzeugen ?

Beitragvon freecrac » Mi 4. Dez 2013, 22:33

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

Re: PWM mit Timer erzeugen ?

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

Re: PWM mit Timer erzeugen ?

Beitragvon 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?
wobo
DOS-Guru
 
Beiträge: 555
Registriert: So 17. Okt 2010, 13:40

Re: PWM mit Timer erzeugen ?

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

Re: PWM mit Timer erzeugen ?

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

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

Re: PWM mit Timer erzeugen ?

Beitragvon 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 ;-)
S+M
DOS-Guru
 
Beiträge: 703
Registriert: Mo 10. Jun 2013, 16:04
Wohnort: BW

Re: PWM mit Timer erzeugen ?

Beitragvon 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.
Nightflyer
 

Re: PWM mit Timer erzeugen ?

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

Re: PWM mit Timer erzeugen ?

Beitragvon 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 ?
Nightflyer
 

Nächste

Zurück zu Gästeforum

Wer ist online?

Mitglieder in diesem Forum: 0 Mitglieder und 1 Gast