DAY12:定时器(1)
使用定时器
CPU通过参考机器周期(牢记每一条指令的执行时间。)来计量时间。
在电脑钟管理定时器,只需对PIT(Programmable Interval Timer)进行设定.(PIT与IRQ的0号相连)
IRQ0中断周期变更原理
需要了解的是IRQ0的中断周期变更原理:
①AL =0x34:OUT(0x43,AL);
②AL =中断周期的低8位:OUT(0x40,AL);
③AL =中断周期的高8位:OUT(0x40,AL);
总结:只要执行3次OUT指令就完成了中断周期的设置。
具体操作源码如下:(将中断周期设定为11932(0x2e9c),即中断频率100Hz)
#define PIT_CTRL 0x0043
#define PIT_CNT0 0x0040
void init_pit(void)
{
io_out8(PIT_CTRL, 0x34);
io_out8(PIT_CNT0, 0x9c);
io_out8(PIT_CNT0, 0x2e);
return;
}
接下来可以编写中断处理程序,同时将中断处理程序注册到IDT
基于上文,我们可以编写基准测试程序(benchmark program)测试电脑性能。
使用多个定时器
为了让实现的功能多样化,我们可以准备多个定时器来控制多个部件。
#define MAX_TIMER 500
struct TIMER{
unsigned int timeout,flags;
struct FIFO8 *fifo;
unsigned char data;
};
struct TIMERCTL{
unsigned int count;
struct TIMER timer[MAX_TIMER];
};
//这样我们可以通过MAX_TIMER来设定定时器个数,flags用于记录各个定时器状态
加快中断处理
先看原中断处理程序:
void inthandler20(int *esp)
{
int i;
io_out8(PIC0_OCW2, 0x60); /* 把IRQ-00信号接收结束的信息通知给PIC*/
timerctl.count++;
for (i = 0; i < MAX_TIMER; i++) {
if (timerctl.timer[i].flags == TIMER_FLAGS_USING) {
timerctl.timer[i].timeout--;
if (timerctl.timer[i].timeout == 0) {
timerctl.timer[i].flags = TIMER_FLAGS_ALLOC;
fifo8_put(timerctl.timer[i].fifo, timerctl.timer[i].data);
}
}
}
return;
}
运行小慢的原因:
//在中断处理程序中采用:
timerctl.timer[i].timeout--
/*CPU要完成从内存中读取变量减去1,然后又往内存中写入操作,增加运行时间
*/
接下来想办法将中断处理的时间压缩:
①改变timer[i].timeout的含义,将其作为”计数器“,不是作为”倒计时器“。
void inthandler20(int *esp)
{
int i;
io_out8(PIC0_OCW2, 0x60); /* 把IRQ-00信号接收结束的信息通知给PIC */
timerctl.count++;
for (i = 0; i < MAX_TIMER; i++) {
if (timerctl.timer[i].flags == TIMER_FLAGS_USING) {
if (timerctl.timer[i].timeout <= timerctl.count) { /* 这里! */
timerctl.timer[i].flags = TIMER_FLAGS_ALLOC;
fifo8_put(timerctl.timer[i].fifo, timerctl.timer[i].data);
}
}
}
return;
}
②之前每次中断都要执行500次(MAX_TIMER的次数)if语句,而其中定时器有的3s就超时,有的50s就超时,还有的一天后超时,没必要将每个都看完,3s定时器超时后记住该时刻,然后看”下一刻“的定时器(50s)。
/*定义一个结构体*/
struct TIMERCTL {
unsigned int count, next; /* 这里! */
struct TIMER timer[MAX_TIMER];
};
//修改后的中断处理程序:
void inthandler20(int *esp)
{
int i;
io_out8(PIC0_OCW2, 0x60); /* 把IRQ-00信号接收结束的信息通知给PIC */
timerctl.count++;
if (timerctl.next > timerctl.count) {
return; /* 还不到下一个时刻,所以结束*/
}
timerctl.next = 0xffffffff;
for (i = 0; i < MAX_TIMER; i++) {
if (timerctl.timer[i].flags == TIMER_FLAGS_USING) {
if (timerctl.timer[i].timeout <= timerctl.count) {
/* 超时 */
timerctl.timer[i].flags = TIMER_FLAGS_ALLOC;
fifo8_put(timerctl.timer[i].fifo, timerctl.timer[i].data);
} else {
/* 还没有超时 */
if (timerctl.next > timerctl.timer[i].timeout) {
timerctl.next = timerctl.timer[i].timeout;
}
}
}
}
return;
}
③有一个问题,那就是到达next时刻和没到next时刻的定时器中断,它们的处理时间差别很大。模仿sheet.c的做法。除了定义sheet0[],还定义了*sheets[]。其里面存放的是按某种顺序排好的图层地址,按顺序描绘图层。
struct TIMERCTL {
unsigned int count, next, using;//using相当于struct SHTCTL中的top
struct TIMER *timers[MAX_TIMER];
struct TIMER timers0[MAX_TIMER];
};
//改进后的inthandler20
void inthandler20(int *esp)
{
int i,j;
io_out8(PIC0_0CW2,0x60);
timerctl.count++;
if(timerctl.next >timerctl.count){
return;
}
for(i=0;i<timerctl.using;i++){
/* timers的定时器都处于动作中,所以不确认flags */
if(timerctl.timers[i]->timeout > timerctl.count){
break;
}
/* 超时*/
timerctl.timers[i]->flags = TIMER_FLAGS_ALLOC;
fifo8_put(timerctl.timers[i]->fifo, timerctl.timers[i]->data);
}
/* 正好有i个定时器超时了。其余的进行移位。 */
timerctl.using -= i;
for (j = 0; j < timerctl.using; j++) {
timerctl.timers[j] = timerctl.timers[i + j];
}
if (timerctl.using > 0) {
timerctl.next = timerctl.timers[0]->timeout;
} else {
timerctl.next = 0xffffffff;
}
return;
}