打印

转:Linux内核中的可编程间隔定时器PIT

[复制链接]
2101|0
手机看帖
扫描二维码
随时随地手机跟帖
跳转到指定楼层
楼主
6019赵文|  楼主 | 2011-3-31 12:49 | 只看该作者 回帖奖励 |倒序浏览 |阅读模式
Linux内核中的可编程间隔定时器PIT(Programmable Interval Timer)
    每个PC机中都有一个PIT,通过IRQ产生周期性的时钟中断信号来充当系统定时器。i386中使用的通常是Intel 8254 PIT芯片,它的I/O端口地址范围是40h~43h。
    8254 PIT有3个计时通道,每个通道都有其不同的用途:
        通道0用来负责更新系统时钟。它在每一个时钟滴答会通过IRQ0向系统发出一次时钟中断信号。
        通道1通常用于控制DMAC对RAM的刷新。
        通道2被连接到PC机的扬声器,以产生方波信号。
    下面我们重点关心通道0。
    每个通道都有一个递减的计数器,8254 PIT的输入时钟信号的频率是1.193181MHZ,也即一秒钟输入1193181个时钟周期。该数字在Linux内核中被定义为:
include/asm-i386/timex.h

#define CLOCK_TICK_RATE 1193180



    每输入一个时钟周期其时间通道的计数器就自减1,一直减到0。因此对于通道0而言,当他的计数器减到0时,PIT就向系统产生一次时钟中断,表示一个时钟滴答已经过去了。该计数器为16bit,因此所能表示的最大值是65536,可以算出该定时器最慢一秒内能发生的滴答数是:1193181 / 65536 = 18.206482。
    PIT的I/O端口:
        40h    通道0计数器    可读写
        41h    通道1计数器    可读写
        42h    通道2计数器    可读写
        43h    控制字         只写

    注意,因为PIT I/O端口是8位的,而PIT相应计数器是16位的,因此必须对PIT计数器进行两次读写才能得到完整的计数值。
    8254 PIT的控制寄存器43h的格式如下:
        bit[7:6]为通道选择位:
            00    通道0
            01    通道1
            10    通道2
            11    read-back command(仅8254)。

        bit[5:4]为Read/Write/Latch锁定位:
            00    锁定当前计数器以便读取计数值
            01    只读高字节
            10    只读低字节
            11    先高后低

        bit[3:1]为设定各通道的工作模式:
            000   mode0    当通道处于count out时产生中断信号,可用于系统定时
            001   mode1    Hardware retriggerable one-shot
            010   mode2    Rate Generator。产生实时时钟中断,通道0通常工作在这个模式下
            011   mode3    方波信号发生器
            100   mode4    Software triggered strobe
            101   mode5    Hardware triggered strobe

        bit[0]总为0
    我们来看看Linux2.4中是如何对PIT做初始化的:
arch/i386/kernel/i8259.c
[size=+0]
void __init init_IRQ(void)[size=+0]

{
      ...
     
[size=+0]
outb_p(0x34, 0x43);
[size=+0]
         
/* binary, mode 2, LSB/MSB, ch 0 */
     
outb_p(LATCH &
0xff, 0x40);
[size=+0]

/* LSB */
     
outb(LATCH >>
8, 0x40);
[size=+0]
     
/* MSB */
      ...
}



    其中的LATCH定义为: include/asm-i386/param.h
[size=+0][size=+0]
#define HZ     100


include/asm-i386/timex.h[size=+0]#define LATCH  ((CLOCK_TICK_RATE + HZ / 2) / HZ)

    也即是等于常数(1193180 + 50) / 100 = 11932,加50是为了尽可能的减小误差(结果四舍五入)。这个结果即是Linux的系统嘀嗒长度(约为0.01秒)经过所需要的PIT时钟周期数。
    上面的那段C基本等价于汇编(省去了延时操作):

mov   
ax,
LATCH

out   
43h,
34h

out   
40h,
al

out   
40h,
ah




    首先写控制字43h,通道选择位为00(通道0),RWL锁定位为11(先高后低),通道工作模式010(Rate Generator),最后位0保留,合起来就是34h。
    然后把LATCH的值分成高低两个字节依次写入通道0计数器40h,完成PIT编程。
    当Linux下次打开中断的时候,系统时钟就将会按照设定的频率(约为100HZ)发出时钟中断了。

相关帖子

发新帖 我要提问
您需要登录后才可以回帖 登录 | 注册

本版积分规则

350

主题

1515

帖子

1

粉丝