简介
以前做延时操作时都是使用循环,让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,效果如下