第07章下 中断和8253

计算机中的时钟分为两类:

  1. 内部时钟:是有处理器固件结构决定的,在出厂就设定好了, 无法改变,处理器内部元件工作速度最快,所以内部时钟的时间单位粒度比较细,以纳秒为单位。
  2. 外部时钟:是处理器中的内部原件,如运算器,控制器的工作时序,主要用于控制、同步内部工作的不掉,内部时钟有晶体振荡器产生。简称晶振。位于主板上,其频率经过分频之后就是主板的外频。处理器和南北桥之间的通信基于外频。Intel处理器将外频乘某个倍数之后称为主频。处理器取指令执行指令所消耗的时钟周期都是基于主频的。

外部时钟指的是处理器与外部设备或是外部设备之间通信采用的一种时序。例如IO接口和处理器之间在A/D转换时的工作时序,两个串口设备之间进行数据传输是也要实现同步时钟。外部时钟的粒度一般是毫秒。

处理器与外部设备间同步数据是的时序配合问题通过,以处理器内部时钟为依据来设计外部设备的时钟。定时计数器是用来解决时序配合问题的。振晶产生的信号频率过高,因此将其送到定时计数器分频,这样产生各种定是信号。

硬件上实现定时器分为可编程和不可变成定时器,常用的可编程定时器有8253、8254等。

8253在名字上称为定时器,又称为计数器。

1 8253

8253内部有3个独立的计数器,分别是0~2,使用的端口分别是0x40~0x42。每个计数器完全相同,相互独立,都是16位的。每个计数器必须明确自己的控制模式才能知道怎样去工作。

  1. 0:0x40,专门用于产生实时时钟信号,采用工作方式3,次计数器写入0时表示最大值65536,这个时钟是值连接在珠片IRQ0引脚上的那个时钟,也就是说,计数器0决定了始终中断信号的发生频率
  2. 1:0x41,专门用于DRAM定时刷新控制。
  3. 2:0x42,专门用于内部扬声器发出不同音调的声音

每个计数器都有3个引脚:

  1. CLK:适中输入信号,计数器自己的时钟频率,每当CLK引脚收到一个时钟信号,减法计数器就将计数-1,连接次引脚的频率最高为2MHz
  2. GATE:门控输入信号,在某些工作方式下用于控制计数器是否开始计数
  3. OUT:计数器输出信号,当计数器工作结束,也就是计数器为0时,根据计数器的工作方式,在OUT'因脚伤输出相应的信号。

计数器开始之前的计数值,保存在计数初值寄存器中,计数器执行将此初值载入后,CLK引脚每收到依次买中,则将其数值-1、当为0时表示技术工作结束,通过OUT引脚发出信号。

技术执行不见是计数器真正计数的部件,8253是一个倒计时计数器,因为计数执行不见是一个16为的减法计数器,从初值寄存器中拿到初值,载入到自己的寄存器后开始做减法运算。因此初值寄存器的值并不会被改变。

2 代码

目录结构:

└── bochs
├── 02.tar.gz
├── 03.tar.gz
├── 04.tar.gz
├── 05a.tar.gz
├── 05b.tar.gz
├── 05c.tar.gz
├── 06a.tar.gz
├── 07a.tar.gz
├── 07b.tar.gz
├── 07c
│   ├── boot
│   │   ├── include
│   │   │   └── boot.inc
│   │   ├── loader.asm
│   │   └── mbr.asm
│   ├── build
│   ├── device
│   │   ├── timer.c
│   │   └── timer.h
│   ├── kernel
│   │   ├── global.h
│   │   ├── idt.asm
│   │   ├── init.c
│   │   ├── init.h
│   │   ├── interrupt.c
│   │   ├── interrupt.h
│   │   └── main.c
│   ├── lib
│   │   ├── kernel
│   │   │   ├── io.h
│   │   │   ├── print.asm
│   │   │   └── print.h
│   │   └── libint.h
│   └── start.sh

2.1 timer.h/timer.c

#ifndef __DEVICE_TIME_H
#define __DEVICE_TIME_H
#include "libint.h"
void timer_init( void );
#endif
#include "timer.h"
#include "interrupt.h"
#include "io.h"
#include "print.h"

#define IRQ0_FREQUENCY 100
#define INPUT_FREQUENCY 1193180
#define COUNTER0_VALUE INPUT_FREQUENCY / IRQ0_FREQUENCY
#define CONTRER0_PORT 0x40
#define COUNTER0_NO 0
#define COUNTER_MODE 2
#define READ_WRITE_LATCH 3
#define PIT_CONTROL_PORT 0x43

// ticks是内核自中断开启以来总共的嘀嗒数
uint32_t ticks;

static void frequency_set( uint8_t  counter_port,
                           uint8_t  counter_no,
                           uint8_t  rwl,
                           uint8_t  counter_mode,
                           uint16_t counter_value )
{
    outb( PIT_CONTROL_PORT, ( uint8_t )( counter_no << 6 | rwl << 4 | counter_mode << 1 ) );
    outb( counter_port, ( uint8_t )counter_value );
    outb( counter_port, ( uint8_t )counter_value >> 8 );
}
// 时钟中断处理函数
static void intr_timer_handler( void )
{
    put_str("\n timer 8253 !");
}

// 初始化 timer
void timer_init()
{
    put_str( "timer_init start\n" );
    //  初始化8253
    frequency_set( CONTRER0_PORT, COUNTER0_NO, READ_WRITE_LATCH, COUNTER_MODE, COUNTER0_VALUE );
    // 为 0x20 时钟中断,设置新的中断处理函数
    register_handler( 0x20, intr_timer_handler );
    put_str( "timer_init done\n" );
}

2.2 init.c

#include "init.h"
#include "print.h"
#include "interrupt.h"
#include "timer.h"

/*负责初始化所有模块 */
void init_all() {
   put_str("init_all\n");
   idt_init();   //初始化中断
   timer_init();
}

猜你喜欢

转载自www.cnblogs.com/perfy576/p/9139124.html