4 设定多个定时器(harib09d)
在上一节做的超时功能,超时结束后如果再设定1000的话,那我们就可以让它每10秒显示一次,或是让它一闪一灭地显示。另外,间隔不仅限于10秒,我们还可以设定得更长一些或更短一些。比如设定为0.5秒的间隔可以用于文字输入时的光标闪烁。
开发操作系统时,超时功能非常方便,所以在很多地方都可以使用它。比如可以让电子时钟每隔1秒重新显示一次;演奏音乐时,可以用它计量音符的长短;也可以让它以0.1秒1次的频率来监视没有中断功能的装置1 ;另外,还可以用它实现光标的闪烁功能。
1 像键盘和鼠标等是通过中断告知我们有数据过来的,但是还有些装置,即使状态有变化也不会发生中断。大家可能会想:“设计这种装置的人想什么呢!”但生气也于事无补,我们只能定期去查询装置的状态。就算是状态没有变化也得查询,实在是浪费电能。
为了简单地实现这些功能,我们要准备很多能够设定超时的定时器。
比起文字说明来,还是直接看程序更易于理解。
■■■■■
首先把struct TIMERCTL修改成下面这样。
本次的bootback.h节选
#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];
};
这样超时定时器最多就可以设定为500个了,flags则用于记录各个定时器的状态。
■■■■■
下面,我们把函数也相应地修改一下吧。
本次的timer.c节选
#define TIMER_FLAGS_ALLOC 1 /* 已配置状态 */
#define TIMER_FLAGS_USING 2 /* 定时器运行中 */
void init_pit(void)
{
int i;
io_out8(PIT_CTRL, 0x34);
io_out8(PIT_CNT0, 0x9c);
io_out8(PIT_CNT0, 0x2e);
timerctl.count = 0;
for (i = 0; i < MAX_TIMER; i++) {
timerctl.timer[i].flags = 0; /* 未使用 */
}
return;
}
struct TIMER *timer_alloc(void)
{
int i;
for (i = 0; i < MAX_TIMER; i++) {
if (timerctl.timer[i].flags == 0) {
timerctl.timer[i].flags = TIMER_FLAGS_ALLOC;
return &timerctl.timer[i];
}
}
return 0; /* 没找到 */
}
void timer_free(struct TIMER *timer)
{
timer->flags = 0; /* 未使用 */
return;
}
void timer_init(struct TIMER *timer, struct FIFO8 *fifo, unsigned char data)
{
timer->fifo = fifo;
timer->data = data;
return;
}
void timer_settime(struct TIMER *timer, unsigned int timeout)
{
timer->timeout = timeout;
timer->flags = TIMER_FLAGS_USING;
return;
}
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;
}
程序稍微有些长,但只要前面的程序大家都明白了,这里应该也没什么困难。
■■■■■
最后来看HariMain函数。我们不一定都设定为10秒,也尝试一下设为3秒吧。另外,我们还要编写类似光标闪烁那样的程序。
本次的bootback.c节选
void HariMain(void)
{
(中略)
struct FIFO8 timerfifo, timerfifo2, timerfifo3;
char s[40], keybuf[32], mousebuf[128], timerbuf[8], timerbuf2[8], timerbuf3[8];
struct TIMER *timer, *timer2, *timer3;
(中略)
fifo8_init(&timerfifo, 8, timerbuf);
timer = timer_alloc();
timer_init(timer, &timerfifo, 1);
timer_settime(timer, 1000);
fifo8_init(&timerfifo2, 8, timerbuf2);
timer2 = timer_alloc();
timer_init(timer2, &timerfifo2, 1);
timer_settime(timer2, 300);
fifo8_init(&timerfifo3, 8, timerbuf3);
timer3 = timer_alloc();
timer_init(timer3, &timerfifo3, 1);
timer_settime(timer3, 50);
(中略)
for (;;) {
(中略)
io_cli();
if (fifo8_status(&keyfifo) + fifo8_status(&mousefifo) + fifo8_status(&timerfifo)
+ fifo8_status(&timerfifo2) + fifo8_status(&timerfifo3) == 0) {
io_sti();
} else {
if (fifo8_status(&keyfifo) != 0) {
(中略)
} else if (fifo8_status(&mousefifo) != 0) {
(中略)
} else if (fifo8_status(&timerfifo) != 0) {
i = fifo8_get(&timerfifo); /* 首先读入(为了设定起始点) */
io_sti();
putfonts8_asc(buf_back, binfo->scrnx, 0, 64, COL8_FFFFFF, "10[sec]");
sheet_refresh(sht_back, 0, 64, 56, 80);
} else if (fifo8_status(&timerfifo2) != 0) {
i = fifo8_get(&timerfifo2); /* 首先读入(为了设定起始点) */
io_sti();
putfonts8_asc(buf_back, binfo->scrnx, 0, 80, COL8_FFFFFF, "3[sec]");
sheet_refresh(sht_back, 0, 80, 48, 96);
} else if (fifo8_status(&timerfifo3) != 0) { /* 模拟光标 */
i = fifo8_get(&timerfifo3);
io_sti();
if (i != 0) {
timer_init(timer3, &timerfifo3, 0); /* 然后设置0 */
boxfill8(buf_back, binfo->scrnx, COL8_FFFFFF, 8, 96, 15, 111);
} else {
timer_init(timer3, &timerfifo3, 1); /* 然后设置1 */
boxfill8(buf_back, binfo->scrnx, COL8_008484, 8, 96, 15, 111);
}
timer_settime(timer3, 50);
sheet_refresh(sht_back, 8, 96, 16, 112);
}
}
}
}
■■■■■
下面就是期盼已久的“make run”了。我们执行一下,当然会顺利运行了,结果如下图。
都显示出来啦!