手写操作系统(二十三)-提高时钟中断的频率

代码、内容参考来自于包括《操作系统真象还原》、《一个64位操作系统的设计与实现》以及《ORANGE’S:一个操作系统的实现》。

我们要完成的步骤简单:

  • IRQ0引脚上的时钟中断信号频率是由8253的计数器0设置的,我们要使用计数器0
  • 时钟发出的中断信号不能只发一次,必须是周期性发出的,也就是我们要采取循环计数的工作方式,可选的工作方式为方式2和方式3,这里咱们就选择方式2,这是标准的分频方式,这正是咱们所需要的。
  • 计数器发出输出信号的频率是由计数初值决定的,所以我们要为计数器0赋予合适的计数初值。

这个初值的计算方式为1193180/中断信号的频率=计数器0的初始计数值

我们要设置的中断信号的频率为100Hz,直接代入公式即可,计数器0的初始计数值=1193180/100约等于 11932。11932 就是计数器 0 的计数初值。

device目录用来存储设备代码。

代码/device/timer.c

#include "timer.h"
#include "io.h"
#include "print.h"

#define IRQ0_FREQUENCY   100
#define INPUT_FREQUENCY  1193180
#define COUNTER0_VALUE INPUT_FREQUENCY/IRQ0_FREQUENCY
#define COUNTER0_PORT    0x40
#define COUNTER0_NO      0
#define COUNTER_MODE     2
#define READ_WRITE_LATCH 3
#define PIT_CONTROL_PORT 0x43

/*把操作的计数器counter_no、读写锁属性rw1、计数器模式counter_mode写入模式控制寄存器中并赋予初始值counter_value*/
static void frequency_set(uint8_t coutner_port,uint8_t counter_no,uint8_t rw1,uint8_t counter_mode,uint16_t counter_value){
    /*向控制字寄存器端口0x43写入控制字*/
    outb(PIT_CONTROL_PORT,(uint8_t)(counter_no << 6 | rw1 << 4 | counter_mode << 1));
    /*先写入counter_value的低8位*/
    outb(coutner_port,(uint8_t)counter_value);
    /*再写入counter_value的高8位*/
    outb(coutner_port,(uint8_t)counter_value >> 8);
}

/*初始化PIT8253*/
void timer_init(){
    put_str("timer_init start\n");
    /*设置8253的定时周期,也就是发中断的周期*/
    frequency_set(COUNTER0_PORT,COUNTER0_NO,READ_WRITE_LATCH,COUNTER_MODE,COUNTER0_VALUE);
    put_str("timer_init done\n");
}

8253的设置在timer_init函数中完成,不过,最终做初始化工作的是frequency_set函数。

frequency_set函数定义了五个参数:

  • counter_port是计数器的端口号,用来指定初值counter_value的目的端口号。
  • counter_no 用来在控制字中指定所使用的计数器号码,对应于控制字中的 SC1 和 SC2 位。
  • rwl用来设置计数器的读/写/锁存方式,对应于控制字中的RW1和RW0位。
  •  counter_mode用来设置计数器的工作方式,对应于控制字中的M2~M0位。
  • counter_value用来设置计数器的计数初值,由于此值是16位,所以我们用了uint16_t来定义它。

此函数的功能是把操作的计数器counter_no、读写锁属性rwl、计数器工作模式counter_mode写入模式控制寄存器并赋予计数器的计数初值为 counter_value.

frequency_set使用的实参都是一些预先定义好的宏:

IRQ0_FREQUENCY是我们要设置的时钟中断的频率,我们要将它设为100Hz

INPUT_FREQUENCY是计数器0的工作脉冲信号频率。

COUNTERO_VALUE 是计数器 0 的计数初值,由之前的公式算出来的,也就是COUNTERO_VALUE 的值为INPUT_FREQUENCY/IRQO_FREQUENCY。

CONTRERO_PORT 是计数器 0 的端口号 0x40。

COUNTERO_NO 是用在控制字中选择计数器的号码,其值为 0,代表计数器 0,它将被赋值给函数的形参 counter_no。

COUNTER_MODE 是工作模式的代码,其值为 2,即方式 2,这是我们选择的工作方式:比率发生器。

READ_WRITE_LATCH是读写方式,其值为3,这表示先读写低8位,再读写高8位。我们要写入的初值是16位,按照8253的初始化步骤,必须先写低8位,后写高8位。

PIT_CONTROL_PORT 是控制字寄存器的端口。

frequency_set函数中初始化8253的步骤,先写入控制字,再将16位的计数初值的低8位和高8位分别写入计数器0的端口。

提供对外暴露timer.h

#ifndef __DEVICE_TIMER_H
#define __DEVICE_TIMER_H
#include "stdint.h"
void timer_init();
#endif

 

然后修改/kernel/init.c

#include "init.h"
#include "print.h"
#include "interrupt.h"
#include "../device/timer.h"

/*负责初始化所有模块*/
void init_all(){
    put_str("init_all\n");
    idt_init(); //初始化中断
    timer_init(); //初始化PIT
}

 

然后编译

nasm -f elf -o build/print.o lib/kernel/print.S
nasm -f elf -o build/kernel.o kernel/kernel.S

gcc -m32 -I lib/kernel -c -o build/timer.o device/timer.c
gcc -m32 -I lib/kernel -m32 -I kernel/ -c -fno-builtin -o build/main.o kernel/main.c
gcc -m32 -I lib/kernel -m32 -I kernel/ -c -fno-stack-protector -fno-builtin -o build/interrupt.o kernel/interrupt.c
gcc -m32 -I lib/kernel -m32 -I kernel/ -c -fno-builtin -o build/init.o kernel/init.c
ld -m elf_i386 -Ttext 0xc0001500 -e main -o build/kernel.bin build/main.o build/init.o build/interrupt.o build/print.o build/kernel.o build/timer.o

写入硬盘

dd if=build/kernel.bin of=/bochs/bin/dreams.img bs=512 count=200 seek=9 conv=notrunc

运行

sudo /bochs/bin/bochs -f /bochs/bin/bochsrc.disk

这里与之前的变化仅仅变快了而已。

 

参考

郑钢著操作系统真象还原

田宇著一个64位操作系统的设计与实现

丁渊著ORANGE’S:一个操作系统的实现

暂无评论

发送评论 编辑评论

|´・ω・)ノ
ヾ(≧∇≦*)ゝ
(☆ω☆)
(╯‵□′)╯︵┴─┴
 ̄﹃ ̄
(/ω\)
∠( ᐛ 」∠)_
(๑•̀ㅁ•́ฅ)
→_→
୧(๑•̀⌄•́๑)૭
٩(ˊᗜˋ*)و
(ノ°ο°)ノ
(´இ皿இ`)
⌇●﹏●⌇
(ฅ´ω`ฅ)
(╯°A°)╯︵○○○
φ( ̄∇ ̄o)
ヾ(´・ ・`。)ノ"
( ง ᵒ̌皿ᵒ̌)ง⁼³₌₃
(ó﹏ò。)
Σ(っ °Д °;)っ
( ,,´・ω・)ノ"(´っω・`。)
╮(╯▽╰)╭
o(*////▽////*)q
>﹏<
( ๑´•ω•) "(ㆆᴗㆆ)
😂
😀
😅
😊
🙂
🙃
😌
😍
😘
😜
😝
😏
😒
🙄
😳
😡
😔
😫
😱
😭
💩
👻
🙌
🖕
👍
👫
👬
👭
🌚
🌝
🙈
💊
😶
🙏
🍦
🍉
😣
Source: github.com/k4yt3x/flowerhd
颜文字
Emoji
小恐龙
花!
上一篇
下一篇