Autor Thema: Sleep() Funktion - while() Problem  (Gelesen 6356 mal)

üäpöol

  • Beiträge: 110
    • Profil anzeigen
Gespeichert
« am: 04. May 2012, 13:18 »
Hallo,

ich schreibe seit einigen Wochen an meinem kleines OS. Ich bin im PMode und habe GDT, IDT, IRQ, ISRS etc. gesetzt. Jetzt wollte ich eine kleine Sleep() Funktion programmieren, bei der ich allerdings ein kleines Problem mit einer while Schleife habe:
int sleep(int freq) {
if (freq!= 0 && slept != 1) {
                IRQ_install_handler(0, ret);
freq = 1193182 / freq;
                outb(0x43, 0x34);
                outb(0x40,freq & 0xFF);
                outb(0x40,freq >> 8);
}
while (slept != 1);
return 0;
}

void ret(struct regs* null) {
print("ret", 5*160+12, 0x07);
slept=1;
}
Das "ret" wird ausgegeben, allerdings bleibt hängt der PC trotzdem in der while Schleife. Es ist egal, ob die while Schleife in der sleep() oder hinter dem sleep() Aufruf ist. Ich habe leider keine Ahnung, woran das liegt.
Ich benutze GCC.
Danke vorträglich schonmal für jede Hilfe! :)

Jidder

  • Administrator
  • Beiträge: 1 625
    • Profil anzeigen
Gespeichert
« Antwort #1 am: 04. May 2012, 14:46 »
Hallo und willkommen an Board!

Ich schätze, dass slept nicht als volatile deklariert ist. Der Compiler denkt vermutlich, dass er die Schleife optimieren kann, weil er nicht weiß, dass Interrupts existieren. Das heißt, er denkt sich, dass er die Variable slept nur einmal überprüfen muss, und wenn sie einmal ungleich 1 war, dann wird sie das für immer bleiben, weil in der Schleife selbst ja kein Code steht, der die Variable ändern kann.

Du solltest also slept so definieren:
volatile int slept;
Das volatile sagt dem Compiler, dass sich die Variable auch hinter seinem Rücken ändern kann.
Dieser Text wird unter jedem Beitrag angezeigt.

üäpöol

  • Beiträge: 110
    • Profil anzeigen
Gespeichert
« Antwort #2 am: 04. May 2012, 15:03 »
Vielen Dank erstmal! Mit der while Schleife gibt es jetzt keine Probleme mehr. Die Funktion funktioniert aber leider trotzdem nicht. Ich wollte ursprünglich eigentlich mit Modus 0 arbeiten, weil das aber nicht funktioniert hat und ich nirgens Beispielcode gefunden habe, wollte ich jetzt erstmal Modus 2 verwenden.
Leider funktioniert der aktuelle Code trotzdem nicht:
int sleep(int millisec) {
if (millisec != 0 && slept != 1) {
int freq = 1193182 / millisec;
IRQ_install_handler(0, ret);
outb(0x43, 0x34);
outb(0x40, freq & 0xFF);
outb(0x40, freq >> 8);
}
while (slept != 1);
return 0;
}
Bitte nicht von 'millisec' durcheinander bringen lassen, in diesem Fall ist das die Frequenz. Um ehrlich zu sein, verstehe ich auch diese
outb(0x40, freq & 0xFF);
Zeile nicht ganz. Was ist der Sinn dieser Zeile, denn was soll ein logisches Und mit lauter Einsen...
Ich hoffe, es hat jemand einen Tipp für mich. :)

Jidder

  • Administrator
  • Beiträge: 1 625
    • Profil anzeigen
Gespeichert
« Antwort #3 am: 04. May 2012, 15:54 »
Die Variable freq sollte besser counter oder so heißen, weil du mit outb in den PIT ja den Wert setzt, der dann runtergezählt wird. Der Counter berechnet sich aus der Frequenz durch

int counter = 1193182 / freq;
Um an den Wert von freq (diesmal tatsächlich eine Frequenz) zu kommen, musst du das hier berechnen:

freq = 1000 / millisec
Das & ist kein logisches sondern ein binäres Und. Es setzt alle Bits auf 0, die nicht auf beiden Seiten des Operators 1 sind, was man auch als maskieren bezeichnet. Bei counter & 0xFF werden alle Bits, bis auf die unteren 8 maskiert (0xff = 1111 1111 in binär). Wenn in counter also 0x1234 steht, dann ist counter & 0xff = 0x34. Das counter >> 8 schiebt den Wert um 8 Bits nach rechts, also counter >> 8 ist in dem Beispiel 0x12. Das ist notwendig, weil die Ports vom PIT nur 8-Bit groß sind, aber wir einen 16-Bit-Wert übergeben wollen, und wir diesen deswegen in zwei 8-Bit-Stücke zerlegen müssen.

Zu dem eigentlich Problem: Warum hat es vorhin funktioniert (Ausgabe von "ret"), und was genau funktioniert jetzt nicht mehr?
Dieser Text wird unter jedem Beitrag angezeigt.

üäpöol

  • Beiträge: 110
    • Profil anzeigen
Gespeichert
« Antwort #4 am: 04. May 2012, 16:03 »
Sorry, ich habe mich schlecht ausgedrückt. IRQ 0 wird ausgelöst, aber sofort. Es gibt keine sichbare Verzögerung.

Jidder

  • Administrator
  • Beiträge: 1 625
    • Profil anzeigen
Gespeichert
« Antwort #5 am: 04. May 2012, 16:13 »
Ja, das war zu erwarten. Die größte Verzögerung die der PIT machen kann sind 55 ms oder so. Also 18,2 Hz.

Grund: Der Counter ist 16-Bit groß, also ist das Maximum 65535. Das heißt das was in meinem letzten Post millisec heißt, darf maximal 65535/(1193182 Hz) = ~55ms sein. Und was bei dir fälschlicherweise millisec heißt, und eigentlich die Frequenz ist, muss mindestens 19 (Hz) sein, und wenn du es erhöhst, ist das Sleep noch schneller fertig.

Du solltest dir also eine Zählvariable machen, die IRQs zählt, und ausrechnen, wieviele IRQs auftreten müssen bis die Sleep-Dauer abgelaufen ist. Deswegen ist auch Modus 2 das richtige, weil du in der Regel mehrere IRQs abwarten musst.
Dieser Text wird unter jedem Beitrag angezeigt.

üäpöol

  • Beiträge: 110
    • Profil anzeigen
Gespeichert
« Antwort #6 am: 04. May 2012, 16:26 »
Vielen herzlichen Dank! Jetzt funktioniert alles. :)

üäpöol

  • Beiträge: 110
    • Profil anzeigen
Gespeichert
« Antwort #7 am: 12. May 2012, 22:21 »
Ich habe nochmal eine Frage zur Frequenz. Eigentlich bräuchte ich wirkliche Millisekunden, also, dass jede Millisekunde IRQ 0 ausgelöst wird. Die Begründung, warum das nicht ging, war, dass der counter 16-bit groß ist. Aber warum kann der counter nicht größer sein?
Zum Beispiel
int _sleep(int millisec) {
if (_slept != 1) {
int counter=1193182;
IRQ_install_handler(0, sleep_ret);
port_out(0x43, 0x36);
port_out(0x40, counter & 0xFF);
port_out(0x40, (counter >> 8));
port_out(0x40, (counter >> 16));
port_out(0x40, (counter >> 24));
}
while (_slept < millisec);
slept=0;
_slept=0;
return 0;
}

void sleep_ret(struct regs* null) {
slept=1;
_slept++;
}
führt bei mir dazu, dass IRQ 0 häufiger, aber irgendwie unregelmäßiger ausgelöst wird. Hat jemand einen Vorschlag?[/code]

Jidder

  • Administrator
  • Beiträge: 1 625
    • Profil anzeigen
Gespeichert
« Antwort #8 am: 12. May 2012, 22:31 »
Der Counter kann nicht größer sein, weil die Hardware dafür nur ein 16-Bit Register vorsieht. Dass du da 4 Werte reinlädst, könnte die Erklärung für das Verhalten sein.

Es ist trotzdem kein Problem den Timer jede Millisekunde auszulösen. 55 ms ist das Maximum, nicht das Minimum. Wenn du willst, dass der Timer IRQ jede Millisekunde ausgelöst wird, musst du den counter mit dem Wert 1193 (=1193182 / 1000) laden.
Dieser Text wird unter jedem Beitrag angezeigt.

üäpöol

  • Beiträge: 110
    • Profil anzeigen
Gespeichert
« Antwort #9 am: 13. May 2012, 14:47 »
Ah, OK. Vielen Dank!

 

Einloggen