计数器和时钟中断

master
阳光少年 1 year ago
parent 297d6a0fe1
commit 80320033a3

@ -22,6 +22,9 @@
"task.h": "c", "task.h": "c",
"interrupt.h": "c", "interrupt.h": "c",
"vector": "c", "vector": "c",
"stdlib.h": "c" "stdlib.h": "c",
"cassert": "c",
"*.inc": "c",
"clock.h": "c"
} }
} }

@ -52,6 +52,7 @@ $(BUILD_PATH)/kernel.bin:$(BUILD_PATH)/kernel/start.o \
$(BUILD_PATH)/kernel/handle.o\ $(BUILD_PATH)/kernel/handle.o\
$(BUILD_PATH)/kernel/interrupt.o\ $(BUILD_PATH)/kernel/interrupt.o\
$(BUILD_PATH)/lib/stdlib.o\ $(BUILD_PATH)/lib/stdlib.o\
$(BUILD_PATH)/kernel/clock.o\
$(shell mkdir -p $(dir $@)) $(shell mkdir -p $(dir $@))
ld -m elf_i386 -static $^ -o $@ -Ttext $(KERNEL_ENTRY_POINT) ld -m elf_i386 -static $^ -o $@ -Ttext $(KERNEL_ENTRY_POINT)

@ -0,0 +1,19 @@
#ifndef ONIX_CLOCK_H
#define ONIX_CLOCK_H
#define PIT_CHAN0_REG 0X40 // 设置计数器 0
#define PIT_CHAN2_REG 0X42 // 设置计数器 2 我们用不到
#define PIT_CTRL_REG 0X43 // 控制字, 端口号是0x43, 8位寄存器, 控制字寄存器也是模式控制寄存器, 用于指定计数器的工作方式, 读写格式, 以及数字制式
#define HITS 100 // 我们设置的 每秒钟想要发生中断的次数, 当然也不能太低或者太高, 因为要在计数器寄存器(16)位存放
#define OSCILLATOR 1193182 // 每秒钟时钟震荡次数
#define CLOCK_COUNTER (OSCILLATOR / HITS) // 如果想每秒钟 发生 HITS 次中断, 需要给计数器设置为 总震荡次数/我们想要发生的次数 得到每次发生中断需要计算的计数器
#define HIT_GAP (1000 / HITS) // 1秒有1000ms, 算出来每次 中断的间隔时间, 也就是 每个时间片 大概10ms
void clock_init();
#endif

@ -17,6 +17,24 @@
#define PIC_S_DATA 0xa1 // 从片的数据端口 #define PIC_S_DATA 0xa1 // 从片的数据端口
#define PIC_EOI 0x20 // 通知中断控制器中断结束 #define PIC_EOI 0x20 // 通知中断控制器中断结束
#define IRQ_CLOCK 0 // 时钟
#define IRQ_KEYBOARD 1 // 键盘
#define IRQ_CASCADE 2 // 8259 从片控制器
#define IRQ_SERIAL_2 3 // 串口 2
#define IRQ_SERIAL_1 4 // 串口 1
#define IRQ_PARALLEL_2 5 // 并口 2
#define IRQ_FLOPPY 6 // 软盘控制器
#define IRQ_PARALLEL_1 7 // 并口 1
#define IRQ_RTC 8 // 实时时钟
#define IRQ_REDIRECT 9 // 重定向 IRQ2
#define IRQ_MOUSE 12 // 鼠标
#define IRQ_MATH 13 // 协处理器 x87
#define IRQ_HARDDISK 14 // ATA 硬盘第一通道
#define IRQ_HARDDISK2 15 // ATA 硬盘第二通道
#define IRQ_MASTER_NR 0x20 // 主片起始向量号
#define IRQ_SLAVE_NR 0x28 // 从片起始向量号
typedef struct gate_t{ typedef struct gate_t{
u16 offset0; // 段内偏移 0 ~ 15 位 u16 offset0; // 段内偏移 0 ~ 15 位
@ -33,6 +51,13 @@ typedef struct gate_t{
// 初始化保护模式的中断向量表 // 初始化保护模式的中断向量表
void interrupt_init(); void interrupt_init();
// 通知中断控制器,中断处理结束
void send_eoi(u8 handle_num);
// 设置中断处理函数
void set_interrupt_handler(u8 irq, usize handler); // 修改handler_table设置的默认处理函数为我们自己定义的
void set_interrupt_mask(u8 irq, bool enable); // 设置某个外中断是否打开
static char *messages[] = { static char *messages[] = {
"#DE Divide Error\0", "#DE Divide Error\0",

@ -11,6 +11,7 @@
#include <onix/task.h>> #include <onix/task.h>>
#include <onix/interrupt.h>> #include <onix/interrupt.h>>
#include <onix/stdlib.h>> #include <onix/stdlib.h>>
#include <onix/clock.h>>
void kernel_init(); void kernel_init();

@ -0,0 +1,31 @@
#include <onix/interrupt.h>
#include <onix/clock.h>
#include <onix/assert.h>
#include <onix/io.h>
usize counter = 0;
// 时钟中断的处理函数, 等下就替换掉默认的外中断处理函数
void clock_handler(u8 handle_num){
assert(handle_num == 0x20); // 只允许时钟中断调用这个
send_eoi(handle_num); // 通知中断控制器, cpu正在处理这个中断了
counter += 1;
DEBUGK("clock handler %d", counter);
}
void pit_init(){
// 配置计数器 0 时钟
out_8(PIT_CTRL_REG, 0b00110100); // 第0号计数器, 计数器先读写低字节, 再读写高字节, 模式2, 使用二进制编码的计数器
out_8(PIT_CHAN0_REG, CLOCK_COUNTER & 0xff); // 先设置低字节
out_8(PIT_CHAN0_REG, (CLOCK_COUNTER >> 8) & 0xff); // 再设置高字节
}
void clock_init(){
pit_init();
// 设置中断处理函数
set_interrupt_handler(IRQ_CLOCK, clock_handler);
// 打开中断
set_interrupt_mask(IRQ_CLOCK, true);
}

@ -13,9 +13,6 @@ u8* handler_table[IDT_SIZE];
// 为了让我们从c语言加载 idt表, 中断时 会先进入这个指针所在函数内, 然后继续跳入到 handler_table中 // 为了让我们从c语言加载 idt表, 中断时 会先进入这个指针所在函数内, 然后继续跳入到 handler_table中
extern handler_entry_table[ENTRY_SIZE]; extern handler_entry_table[ENTRY_SIZE];
// 导入调度函数
extern void schdule();
// 异常 中断处理函数默认函数 // 异常 中断处理函数默认函数
void exception_handle(u8 handle_num, void exception_handle(u8 handle_num,
u32 edi, u32 esi, u32 ebp, u32 esp, u32 edi, u32 esi, u32 ebp, u32 esp,
@ -57,11 +54,36 @@ void send_eoi(u8 handle_num)
} }
} }
// 外中断 中断处理函数默认函数 // 外中断 中断处理函数默认函数
void default_handler(u8 handle_num){ void default_handler(u8 handle_num){
send_eoi(handle_num); send_eoi(handle_num);
// 外中断发生就立即调度 DEBUGK("[0x%x] default interrupt called...\n", handle_num);
schdule(); }
// 修改handler_table设置的默认处理函数为我们自己定义的
// 主要针对外中断
void set_interrupt_handler(u8 irq, usize handler) {
assert(irq >= 0 && irq < 16); // 中断处理函数需要大于0, 且小于16, 因为外中断就是只有16个, 0x20到0x2f
handler_table[IRQ_MASTER_NR + irq] = handler;
}
// 设置某个外中断的状态是否打开和关闭
void set_interrupt_mask(u8 irq, bool enable){
assert(irq >= 0 && irq < 16);
u16 port;
if (irq < 8) { // 说明是一个主片
port = PIC_M_DATA;
} else { // 从片
port = PIC_S_DATA;
irq -= 8;
}
if (enable){
out_8(port, in_8(port) & ~(1 << irq));
} else {
out_8(port, in_8(port) & (1 << irq));
}
} }
@ -109,7 +131,7 @@ void pic_init(){
out_8(PIC_S_DATA, 2); // ICW3: 设置从片连接到主片的 IR2 引脚 out_8(PIC_S_DATA, 2); // ICW3: 设置从片连接到主片的 IR2 引脚
out_8(PIC_S_DATA, 0b00000001); // ICW4: 8086模式, 正常EOI out_8(PIC_S_DATA, 0b00000001); // ICW4: 8086模式, 正常EOI
out_8(PIC_M_DATA, 0b11111110); // 只打开主片的第0个中断也就是 的主片的0x20时钟中断 out_8(PIC_M_DATA, 0b11111111); // 关闭所有主片pic中断
out_8(PIC_S_DATA, 0b11111111); // 关闭从片所有pic中断 out_8(PIC_S_DATA, 0b11111111); // 关闭从片所有pic中断
} }

@ -1,4 +1,3 @@
#include <onix/onix.h> #include <onix/onix.h>
@ -14,14 +13,13 @@ void kernel_init(){
// 初始化中断 // 初始化中断
interrupt_init(); interrupt_init();
// int cnt = 0;
// while (true){
// cnt += 1;
// printk("%d", 123);
// }
// 初始化任务 // 初始化任务
task_init(); // task_init();
clock_init();
asm volatile("sti\n");
hang();
} }

@ -15,8 +15,8 @@ u32 printk(const u8 *fmt, ...){
i = vsprintf(buf, fmt, args); i = vsprintf(buf, fmt, args);
va_end(args); va_end(args);
asm volatile("cli\n"); // asm volatile("cli\n");
console_write(buf, i); console_write(buf, i);
asm volatile("sti\n"); // asm volatile("sti\n");
return i; return i;
} }
Loading…
Cancel
Save