23.时钟中断

简介

以前做延时操作时都是使用循环,让cpu不断的循环。但是一个潜在的问题是浪费cpu资源,在循环中cpu将不能执行其他任务。

目标

使用8259A 的IRQ0 实现时钟中断。

1.kernel.s
打开IRQ0中断控制

mov  al, 11111000b	;CPU只接收主8259A,IRQ0,IRQ1,IRQ2管线发送的信号,其他管线发送信号一概忽略

设置中断向量,新增IRQ0中断向量设置 .0x20: 标签

;中断描述符表
	LABEL_IDT:
	%rep  0x20
		Gate  SelectorCode32, SpuriousHandler,0, DA_386IGate
	%endrep

	;8259A IRQ0 定时器中断
	.0x20:
		Gate  SelectorCode32, TimerHandler,0, DA_386IGate
	
	;键盘中断向量(8259A 键盘中断向量0x20,IRQ1 是键盘中断请求,0x20 + IRQ[n] = 0x21
	.0x21:
		Gate  SelectorCode32, KeyboardHandler,0, DA_386IGate

汇编调用C 语言实现中断操作

;定时器中断
LabelTimerHandler:
	TimerHandler equ LabelTimerHandler - $$
	push es
    push ds
    pushad
    mov  eax, esp
    push eax

	call int_timer

	pop  eax
    mov  esp, eax
    popad
    pop  ds
    pop  es
	iretd

2.抽取os中可以公共使用部分的代码
global_def.h

#ifndef GLOBAL_DEF_H
#define GLOBAL_DEF_H



#define true 1
#define false 0


//定义缓冲区
typedef struct _FIFO8{
	//指向缓冲区
    char* buf;
	//r:读索引,w:写索引
	//size:缓存容量
	//len:存储有效数据长度
    int r, w, size, len, flag;
}FIFO8;


//缓存初始化
void fifo8_init(FIFO8 *fifo,int size,char *buf);
//缓冲区存放数据
int fifo8_put(FIFO8 *fifo,char data);
//缓冲区读取数据
int fifo8_get(FIFO8 *fifo);



#endif

global_def.c

#include"global_def.h"


void fifo8_init(FIFO8 *fifo, int size,char *buf){
	fifo->buf = buf;
	fifo->r = 0;
	fifo->w = 0;
	fifo->size = size;
	fifo->len = 0;
	fifo->flag = 0;
}


int fifo8_put(FIFO8 *fifo,char data){
    if (fifo->len == fifo->size) {
        return -1;
    }

    fifo->buf[fifo->w] = data;
    fifo->w++;
    if (fifo->w == fifo->size) {
        fifo->w = 0;
    }

    fifo->len++;
    return 0;
}


int fifo8_get(FIFO8 *fifo) {
    
    if (fifo->len == 0) {
        return -1;
    }

   	int data = fifo->buf[fifo->r];
    fifo->r++;
    if (fifo->r == fifo->size) {
        fifo->r = 0;
    }

    fifo->len--;
    return data;
}

3.新建timer.h

#ifndef	TIMER_H
#define TIMER_H

#include "global_def.h"

//定义时钟管理器
typedef struct _TIMERCTL {
    unsigned int count;
    unsigned int timeout;
    FIFO8 *fifo;
    unsigned char data;
}TIMERCTL;

//初始化定时器
void init_timer(TIMERCTL *timctl);

//设置定时器相关参数
void setTimer(TIMERCTL *timctl,unsigned int timeout,FIFO8 *fifo,unsigned char data);


#endif

4.新建timer.c

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

#define PIT_CTRL   0x43
#define PIT_CNT0   0x40

//向8259A芯片的对应端口发送指定数据,首先需要向端口0x43发送一个数值0x34, 
//紧接着向端口0x40发送两个数据0x9c,0x2d, 这样时钟中断就能在1秒内发生100次了
void init_timer(TIMERCTL *timctl){
	io_out8(PIT_CTRL, 0x34);
    io_out8(PIT_CNT0, 0x9c);
    io_out8(PIT_CNT0, 0x2e);

    timctl->count = 0;
    timctl->timeout = 0;
}

void setTimer(TIMERCTL *timctl,unsigned int timeout,FIFO8 *fifo,unsigned char data){
	int flag = io_readFlag();
	io_cli();
	timctl->timeout = timeout;
	timctl->fifo = fifo;
	timctl->data = data;
	io_writeFlag(flag);
}

5.os.c
申明时钟中断控制器和中断缓存队列

//时钟中断管理器
static TIMERCTL _timctl;
static FIFO8 _timFifo;
static char _timbuf[16];

init_main中初始化时钟中断硬件

//初始化时钟中断,初始化后每隔10毫秒发送一次中断,中断超时后将把一个数据送入时钟FIFO8队列中
	fifo8_init(&_timFifo,16,_timbuf);
	init_timer(&_timctl);
	setTimer(&_timctl,1000,&_timFifo,1);

实现时钟中断数据写入时钟队列

//定时器中断
void int_timer(){
	io_out8(0x20, 0x60);
	_timctl.count++;
	_timctl.timeout -= 10;
	if(_timctl.timeout<=0){
		fifo8_put(&_timFifo,_timctl.data);
	}
}

循环中实现定时任务

else if(_timFifo.len>0){
			int data = fifo8_get(&_timFifo);
			int len = 0;
			_tempArr[len++] = '0';
			_tempArr[len++] = 'x';

			for(int j=0;j<4;j++){
				int temp = char2HexStr(((char *)&data)[3-j]);
				_tempArr[len++] = ((char *)&temp)[0];
				_tempArr[len++] = ((char *)&temp)[1];
				
			}
			_tempArr[len] = 0;
			showString(_shtctl,_shtMsg,10,54,COL8_FFFFFF,COL8_C6C6C6,_tempArr);
		}

6.编译加载floppy.img,1秒后消息框上输出0x00000001,效果如下
23.时钟中断_第1张图片

你可能感兴趣的:(自制操作系统,时钟中断)