手写简易操作系统(十二)--实现时钟中断

前情提要

前面我们开启了中断,但是这些中断都对应着一个通用的中断处理函数,而且几乎都是处理器触发的中断,没有我们的外设中断,虽然我们提前预留了这些接口。

现在我们实现一个时钟中断

一、可编程计数器8253

计算机中的时钟分为两种:内部时钟,外部时钟。

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

外部时钟是指处理器与外部设备或外部设备之间通信时采用的一种时序,比如两个串口在通信时要首先指定波特率,只有波特率相同的两个串口之间才能通信成功。外部设备的速度对于处理器来讲就很慢了,所以其时钟的时间单位粒度比较大,一般是ms毫秒级别。

如何保证在不同的时钟下的设备能够同步通信呢?解决这个问题的大体思路就是:以处理器的内部时钟为依据来设计外部设备时钟,即要符合处理器内部运行时序,又要满足外部设备工作时序要求。定时计数器就是用来解决时序配合问题的。大家知道,处理器内部时钟信号由晶振产生,所以计时较为精确,但是晶振产生的频率较高,因此必须将其送到定时计数器分频,这样才能产生需要的各种定时信号。

对于外部定时,有两种实现方式,一种是软件实现

int count = 90000;
while(count -- > 0);

假设我们的晶振的频率是12MHZ,12个时钟周期是一个机器周期,也就是一个机器周期的时间是1us,即1微秒,上述的程序执行时间是90000us,即90ms。所以这种方式也是可以的。但是这是在白白消耗计算机的计算时间,是一种浪费的行为。

另一种就是硬件实现,硬件实现需要专门的硬件,这个硬件就是定时器。

计时器的功能就是定时发信号。当到达了所计数的时间,计数器可以自动发一个输出信号,可以用该信号向处理器发出中断,这样处理器可以去执行相应的中断处理程序。和软件定时相比从,硬件定时器不占用CPU资源。定时器分为可编程定时器和不可编程定时器,8253属于可编程定时器PIC,Programmable Interval Timer。

定时器有两种计时方式,一种是正计时,每一个脉冲都将当前的计数值加一,直到与设定的目标终止值相等。一种是倒计时,先设定好计数值,每一次脉冲都将当前值减一,直到为0时提示时间已到。

二、8253入门

8253内部有3个独立的计数器,如下图所示,分别是计数器0,计数器1,计数器2。他们的端口分别是0x40~0x42。计数器又称为通道,每个计数器都完全相同,都是16位大小。既然他们是独立的,也就是这三个计数器的工作是不依赖的,可以各干各的。可以这样做的基础就是他们都有自己的一套寄存器资源,互不干扰,寄存器资源包括一个16位的计数初值寄存器,一个计数器执行部件,和一个输出锁存器,其中计数器执行部件是计数器中真正进行计数工作的元器件,其本质是一个减法计数器。

image-20230517191431761

计数器内部简图

image-20230517192350958

每个计数器都有三个引脚,CLK,GATE,OUT。

CLK 很好理解,一看就是接时钟的输入信号,每收到一个时钟输入信号,计数器就减一,连接到这个引脚的最高频率为10MHZ,8253为2MHZ。

GATE 表示门控输入信号,在某些工作模式下用于控制计数器是否可以开始计数,在不同的工作模式下GATE作用不同。

OUT 表示计数器输出信号,当定时工作结束时,也就是计数值为0时,根据计数器的工作方式,会在OUT上输出相应信号。

计数初值寄存器用来保存计数器的初始值,是一个16位宽的寄存器,它的作用是为计数器执行部件准备初始计数值,之后的计数过程与它无关。

加法计数器是整个定时器的核心,它从初值寄存器中拿到起始值,载入到自己的寄存器后便开始递减计数。注意,计数过程中不断变化的值称为当前计数值,它保存在执行部件自己的寄存器中,初值寄存器中的值不受影响。

输出锁存器也称为当前计数值锁存器,用于把当前减法计数器中的计数值保存下来,其目的就是为了让外界可以随时获取当前计数值。计数器中的计数值是不断变化的,处理器无法直接从计数器中获取当前计数值。

计数初值寄存器、计数器执行部件和输出锁存器都是16位宽度的寄存器,所以高8位和低8位都可以单独访问。

三个计数器都有自己的作用

计数器0(端口0x40)在个人计算机中,计数器0专用于产生实时时钟信号。它采用工作方式3,往此计数器写入0时则为最大计数值65536

计数器1(端口0x41)在个人计算机中,计数器1专用于DRAM的定时刷新控制。这个刷新是因为DRAM的结构是电容存储,存储的时间短,需要定时刷新,即给电容充电。

计数器2(端口0x42)在个人计算机中,计数器2原用于通过输出方波PWM输出声音,这个我们用不到。

计数器0是我们研究的重点,他就是时钟中断的信号源

三、8253控制字

8253控制字格式,其端口为0x43

请添加图片描述

SC1和SC0位是选择计数器位,即Select Counter,或者叫选择通道位,即Select Channel。

RW1和RW0位是读/写/锁存操作位,即Read/Write/Latch,用来设置待操作计数器(通道)的读写及锁存方式。计数器是16位宽度,当我们往计数器中写入计数初值时,或者读取计数器中的数值时,可以指定读写低8位,还是高8位。RW1和RW0这两位组合成4种读写方式,具体选择值如图7-47所示。

M2~M0这三位是工作方式(模式)选择位,即Method或Mode。每个计数器有6种不同的工作方式,即方式0~方式5。

BCD码(Binary-Coded Decimal‎),用4位二进制数来表示1位十进制中的0~9这10个数码,是一种二进制的数字编码形式,用二进制编码的十进制代码。

四、8253工作模式

工作模式描述
模式0计数结束中断方式(Interrupt on Terminal Count)
模式1硬件可重触发单稳方式(Hardware Retriggerable One-Shot)
模式2比率发生器(Rate Generator)
模式3方波发生器(Square Wave Generator)
模式4软件触发选通(Software Triggered Strobe)
模式5硬件触发选通(Hardware Triggered Strobe)

计数器的计数过程在何时发生呢?将计数初值写入计数器后就开始计数了吗?不完全是。开始计数的时机与工作方式相关,计数器开始计数需要两个条件。

  • GATE为高电平,即GATE为1,这是硬件控制的
  • 计数初值已经写入利润计数器中的减法计数器,这是由软件out指令控制的。

当这两个条件具备后,计数器将在下一个时钟信号CLK的下降沿开始计数。

软件启动

软件启动是指上面硬件负责的条件1已经完成,也就是GATE已经为1,目前只差软件来完成条件2,即尚未写入计数初值,只要软件负责的条件准备好,计数器就开始启动。当处理器用out指令往计数器写入计数初值,减法器将此初值加载后,计数器便开始计数。工作方式0、2、3、4都是用软件启动计数过程。

硬件启动

硬件启动是指上面软件负责的条件2已经完成,即计数初值已写入计数器。目前只差硬件来完成条件1了,也就是门控信号GATE目前还是低电平,即目前GATE=0,只要硬件负责的条件准备好,计数器就开始启动。GATE引脚是由外部信号来控制的,只有当GATE由0变1的上升沿出现时,计数器才开始启动计数。工作方式1、5都是用硬件启动计数过程。

强制终止

有些工作方式中,计数器是重复计数的,当计时到期(计数值为0)后,减法计数器又会重新把计数初值寄存器中的值重新载入,继续下一轮计数,比如工作方式2和工作方式3都是采用此方式计数,此方式常见于需要周期性发信号的场合。对于采用此类循环计数工作方式的计数器,只能通过外加控制信号来将其计数过程终止,办法是破坏启动计数的条件:将GATE置为0即可。

自动终止

有些工作方式中,计数器是单次计数,只要定时(计数)一到期就停止,不再进行下一轮计数,所以计数过程自然就自动终止了。比如工作方式0、1、4、5都是单次计数,完成后自动终止。如果想在计数过程中将其终止怎么做呢?还是用那个简单粗暴可依赖的方法,将GATE置0

方式0:计数结束中断方式(Interrupt on Terminal Count)

方式0也称为“计数结束输出正跳变信号”方式,其典型应用是作为事件计数器。

在方式0时,对8253任意计数器通道写入控制字,都会使该计数器通道的OUT变为低电平,直到计数值为0。计数值为0时,输出会从低电平跳变到高电平,因此这个信号可以接在8259A的中断引脚IR0上。计数工作会在下一个时钟信号的下降沿开始。

方式0进行计数时,计数器只是单次计数,计数为0时,并不会再将计数初值寄存器中的值重新载入。此方式中,门控信号GATE用于允许或禁止计数,当GATE=1时允许计数,GATE=0时则禁止计数。

方式1: 硬件可重触发单稳方式(Hardware Retriggerable One-Shot)

方式1的典型应用是作为可编程单稳态触发器,其触发信号是GATE,这是由硬件来控制的,故此方式称为硬件可重触发单稳方式。

在方式1下,由处理器将计数初值写入计数器后,OUT引脚变为高电平。不过,无论此时GATE是高电平,还是低电平,计数器都不会启动计数,而是等待外部门控脉冲信号GATE由低到高的上升沿出现,这是由硬件启动的,之后才会在下一个时钟信号CLK的下降沿开始启动计数,同时会将OUT引脚变为低电平。此后,每当CLK引脚收到一个时钟脉冲信号时,在其下降沿,减法计数器便开始对计数值减1。

OUT引脚的低电平状态一直保持到计数为0,当计数为0时,OUT引脚产生由低到高的正跳变信号。

方式2:比率发生器(Rate Generator)

按照比率来分频,其典型应用就是分频器,故也称为分频器方式。

此方式的特点是计数器计数到达后,自动重新载入计数初值,不需要重新写入控制字或计数初值便能连续工作。当计数初值为N时,每N个CLK时钟脉冲,就会在OUT端产生一个输出信号,这样一来,输入信号CLK和输出信号OUT的关系是N:1。

方式2主要用在循环分频的场合。

方式3:方波发生器(Square Wave Generator)

在方式3下工作,就相当于一个方波发生器。当处理器把控制字写入到计数器后,OUT端输出高电平。在GATE为高电平的前提下,在处理器把计数初值写入计数器后的下一个CLK时钟脉冲的下降沿,计数器开始计数。

方式3的特别之处在于每次计时结束都翻转out的电平

方式4:软件触发选通(Software Triggered Strobe)

当处理器把控制字写入到计数器后,OUT端变成高电平。在GATE为高电平的前提下,在处理器把计数初值写入计数器后的下一个CLK时钟脉冲的下降沿,计数器开始计数,所以是软件启动。

当计数值为1时,OUT端由高电平变为低电平,当计数值为0,即持续一个CLK时钟周期后,OUT端又回到高电平,此时计数器停止计数。此方式和方式0类似,都是单次计数,只有在重新写入控制字或重新写入计数初值时才会重新开启计数。

方式5:硬件触发选通(Hardware Triggered Strobe)

此方式与方式4类似,都是一次计数,区别是计数启动的方式不同,方式5是硬件启动。

方式5中,当处理器把控制字写入到计数器后,OUT端变成高电平。处理器把计数初值写入计数器后,计数工作要等到外部门控脉冲信号GATE由低到高的上升沿出现时才开启,这是由硬件启动的。

当计数值为1时,OUT端由高电平变为低电平,保持一个CLK周期,即计数值变为0时,OUT端又变为高电平,同时停止计数。

五、代码

代码就很简单了,就是向相应的端口写控制字就可以了

#define IRQ0_FREQUENCY     100               // IR0需要的频率
#define INPUT_FREQUENCY    1193180           // 8253的输入频率
#define COUNTER0_VALUE     INPUT_FREQUENCY / IRQ0_FREQUENCY 
#define CONTRER0_PORT      0x40              // 计数器0的端口
#define CONTRER0_NO        0                 // 选择计数器0
#define CONTRER0_MODE      2                 // 选择工作模式2,比率发生器,即分频
#define CONTRER0_RWL       3                 // 读写方式位先读写低字节再读写高字节
#define COUNTER0_BCD       0                 // 采用二进制方式
#define PIT_CONTROL_PORT   0x43              // 控制端口

static void frequency_set(uint8_t counter_port, uint8_t counter_no, uint8_t counter_rwl, \
                          uint8_t counter_mode,uint8_t counter_bcd, uint16_t counter_value) {
    outb(counter_port,(uint8_t)(counter_no << 6 | counter_rwl << 4 | counter_mode << 1 | counter_bcd));
    outb(counter_port, (uint8_t)counter_value); 
    outb(counter_port, (uint8_t)(counter_value >> 8)); 
}

void timer_init() {
    put_str("timer_init start!\n");
    frequency_set(CONTRER0_PORT,CONTRER0_NO,CONTRER0_RWL,CONTRER0_MODE,\
                  COUNTER0_BCD,COUNTER0_VALUE);
    put_str("timer_init end!\n");
}

为了验证我们的代码是成功的,我们注册了一个时钟中断的中断处理函数

static void timer_interrupt(void) {
    put_str("this is timer interrupt\n");
    return;
}

并将其注册

idt_table[0x20] = timer_interrupt;

可以看到下面的仿真结果

image-20240318220556472

结束语

这三节我们讲解了中断以及如何跳转到中断处理函数执行,并且实现了一个时钟中断,下一节我们将实现一个简单的C语言库

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/469422.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

基于SpringBoot+MyBatis-Plus的图书管理系统

基于SpringBoot的图书管理系统 图书管理系统开发技术功能模块代码结构数据库设计运行截图源码获取 图书管理系统 开发技术 技术&#xff1a;SpringBoot、MyBatis-Plus、MySQL、Beetl、Layui。 框架&#xff1a;基于开源框架Snowy-Layui开发。 工具&#xff1a;IDEA、Navicat等…

2078: [蓝桥杯2023初赛] 01 串的熵

对于一个长度为 n 的 01 串 S x1x2x3...xn. 香农信息熵的定义为&#xff1a; 。 其中 p(0), p(1) 表示在这个 01 串中 0 和 1 出现的占比。 比如&#xff0c;对于S 100 来说&#xff0c;信息熵 H(S ) - 1/3 log2(1/3) - 2/3 log2(2/3) - 2/3 log2(2/3) 1.3083。 对于一个…

QT--信号和槽机制

信号槽 信号槽是 Qt 框架引以为豪的机制之一。所谓信号槽&#xff0c;实际就是观察者模式。当某个事件发生之后&#xff0c;比如&#xff0c;按钮检测到自己被点击了一下&#xff0c;它就会发出一个信号(signal)。这种发出是没有目的的&#xff0c;类似广播。如果有对象对这个…

选择排序算法(Selection Sort)原理及实现

选择排序算法&#xff0c;运行效率不高&#xff0c;但是非常容易理解&#xff0c;算法复杂度为 。 原理&#xff1a; 假设要排序的数组的长度为n&#xff0c;将数组先分为两个部分&#xff0c;一个是有序区域部分&#xff0c;另一个为无序区域部分。初始时有序部分中没有元素…

Python学习:注释和运算符

python 注释 在Python中&#xff0c;注释用于在代码中添加解释、说明或者提醒&#xff0c;但并不会被解释器执行。Python中的注释以#开头&#xff0c;直到行末为止。下面是关于Python注释的详细解释和举例&#xff1a; 单行注释&#xff1a;使用#符号在行的开头添加注释&…

十四届蓝桥杯 冶炼金属(二分 / 公式)

二分代码1&#xff1a; #include<iostream> #include<cstdio> #include<cmath> using namespace std;int get(int a, int b){int l1;r1e91;while(l<r){int mid lr >>1;if(a / mid < b){r mid;}else l mid 1;}return l; } int main() {int n…

为什么技术人员副业赚钱那么难?

公众号&#xff1a;小北技术圈。 34岁老程序员&#xff0c;长期探索副业项目&#xff0c;写过IDEA插件&#xff0c;搞过工具导航&#xff0c;做过出海网站&#xff0c;运营过自媒体。欢迎提前探索35岁程序员的第二赛道。 每周分享干货内容。寻找100个技术人员&#xff0c;聚在…

2000-2021年各省研发强度数据(原始数据+计算结果)(无缺失)

2000-2021年各省研发强度数据&#xff08;原始数据计算结果&#xff09;&#xff08;无缺失&#xff09; 1、时间&#xff1a;2000-2021年 2、指标&#xff1a;RD经费内部支出&#xff08;万元&#xff09;、国内生产总值、研发强度 3、范围&#xff1a;31省 4、来源&#…

人工智能技术应用笔记(九):大道至简!提示词学习入门,看这一篇就够了!

本篇为《人工智能技术应用》专栏的第九篇。希望以学习笔记的形式和大家一起了解和探索人工智能技术的实际应用。 现在关于提示词的武功秘笈已经多如牛毛&#xff0c;但是我相信这个就像练功一样&#xff0c;练到最后总是化繁为简&#xff0c;一招制胜&#xff01; 今天想说的这…

03python注释与输入函数

Python 注释的作用: 注释可用于解释 Python 代码。 注释可用于提高代码的可读性。 在测试代码时,可以使用注释来阻止执行。 注释可以放在一行的末尾,Python 将忽略该行的其余部分: 实例1 print("Hello, World!") #打印输出Hello,World print(9-3) #输出9…

C++_day6:2024/3/18

作业1&#xff1a;编程题&#xff1a; 以下是一个简单的比喻&#xff0c;将多态概念与生活中的实际情况相联系&#xff1a; 比喻&#xff1a;动物园的讲解员和动物表演 想象一下你去了一家动物园&#xff0c;看到了许多不同种类的动物&#xff0c;如狮子、大象、猴子等。现在…

发挥实力,引领游戏行业的未来——武汉灰京文化的成功之路

作为一家游戏发行商&#xff0c;武汉灰京文化的公司实力源于其强大的战略规划和专业团队。自成立以来&#xff0c;公司坚持以用户为中心&#xff0c;不断提升用户体验。通过深入了解玩家需求&#xff0c;武汉灰京文化在游戏运营和推广过程中&#xff0c;精益求精&#xff0c;力…

使用C#的winform控制数据库实例服务的运行状态

一、得到sqlserver的实例名 二、引用对应的程序集和命名空间 using System.ServiceProcess; C#操作服务要用的类 ServiceController 声明类 private ServiceController serviceController new ServiceController("MSSQLSERVER"); 三、判断服务状态 serviceCon…

Vue.js+SpringBoot开发企业项目合同信息系统

目录 一、摘要1.1 项目介绍1.2 项目录屏 二、功能模块2.1 数据中心模块2.2 合同审批模块2.3 合同签订模块2.4 合同预警模块2.5 数据可视化模块 三、系统设计3.1 用例设计3.2 数据库设计3.2.1 合同审批表3.2.2 合同签订表3.2.3 合同预警表 四、系统展示五、核心代码5.1 查询合同…

仿懂车帝的二手车交易平台功能介绍

二手车交易平台app是一款功能丰富的二手车交易平台&#xff0c;以下是其主要功能介绍&#xff1a; 二手车信息展示&#xff1a;APP首页展示各类二手车信息&#xff0c;包括车型、品牌、价格等&#xff0c;用户可以轻松浏览并选择自己感兴趣的车辆。搜索与筛选功能&#xff1a;…

AI智能客服系统的费用

实现智能客服所需的费用取决于多个因素&#xff0c;包括项目的规模、所选择的技术和服务提供商、数据的获取和处理方式等。以下是一些可能影响费用的因素&#xff0c;希望对大家有所帮助。北京木奇移动技术有限公司&#xff0c;专业的软件外包开发公司&#xff0c;欢迎交流合作…

微服务day03 -- Docker

1.初识Docker 1.1.什么是Docker 微服务虽然具备各种各样的优势&#xff0c;但服务的拆分通用给部署带来了很大的麻烦。 分布式系统中&#xff0c;依赖的组件非常多&#xff0c;不同组件之间部署时往往会产生一些冲突。 在数百上千台服务中重复部署&#xff0c;环境不一定一致…

Android中Gradle的生命周期详解

前些天发现了一个蛮有意思的人工智能学习网站,8个字形容一下"通俗易懂&#xff0c;风趣幽默"&#xff0c;感觉非常有意思,忍不住分享一下给大家。 &#x1f449;点击跳转到教程 Gradle的生命周期分为三个阶段&#xff1a; 初始化阶段定义阶段(配置阶段)执行阶段 第…

【网络原理】HTTP协议和使用Fiddler抓包

文章目录 &#x1f343;HTTP协议是什么&#xff1f;&#x1f340;理解 "应用层协议"&#x1f38d;HTTP 协议的工作过程&#x1f334;HTTP 协议格式&#x1f333;Fiddler抓包工具的使用&#x1f338;如何抓HTTPS的包&#xff1f; &#x1f38b;抓包工具的原理&#x1…

基于php高校选课系统设计与实现flask-django-python-nodejs

接着&#xff0c;本论文将设计一个基于Web的高校选课系统&#xff0c;并通过详细的需求分析和系统架构设计来解决现有系统中存在的问题。系统的开发将采用目前流行的Web技术和数据库技术&#xff0c;并考虑系统的灵活性、安全性和易用性。最后&#xff0c;本论文将对开发出的系…