IT数码 购物 网址 头条 软件 日历 阅读 图书馆
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
图片批量下载器
↓批量下载图片,美女图库↓
图片自动播放器
↓图片自动播放器↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁
 
   -> 嵌入式 -> DellaOS内核程序篇——初级中断处理 -> 正文阅读

[嵌入式]DellaOS内核程序篇——初级中断处理

DellaOS内核程序篇——初级中断处理

本文参照《Orange’S:一个操作系统的实现》与《一个64位操作系统的设计与实现》实现(部分内容有删改),如有描述不清或错误处,请阅读原著或联系本人

在学习过程中,由于操作系统知识过于庞大,我们奉行”懒加载“的原则,用到什么学习什么

2021-12.15

1、中断处理

中断大多是由外部硬件设备(如鼠标、键盘、硬盘、光驱等)产生,并向处理器发送事件请求信号。中断请求信号可能是关于数据读写操作的,也可能是关于对外部设备控制的。由于Intel处理器只有一个外部中断引脚INTR,为了使处理器能够同时接收多个硬件设备发送来的中断请求信号,特将所有外部设备的中断请求信号汇总到中断控制器,再经由中断控制器的仲裁后,有选择性地将中断请求信号依次发往处理器的外部中断引脚INTR。

在多核处理器出现之前,8259A PIC(Programmable Interrupt Controller,可编程中断控制器)是PC机使用最为普遍的中断控制器,在很多教科书和资料中经常会提及它。自从多核处理器面世后,8259A PIC对多核处理器的支持已逐渐变得力不从心,从而出现了后来的APIC(Advanced Programmable Interrupt Controller,高级可编程中断控制器)

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-2cAjhWGO-1639554370292)(C:\Users\xumeng03\AppData\Roaming\Typora\typora-user-images\image-20211215111150127.png)]

在两个8259A芯片级联的过程中,一个8259A中断控制器作为主芯片,与CPU的INTR引脚相连;另一个8259A中断控制器作为从芯片,与主8259A芯片的IR2引脚相连。其他中断请求引脚IR将与外部硬件设备的中断请求引脚相连。

8259APIN典型中断请求源
主芯片IR0timer时钟
IR1键盘
IR2级联从8259A芯片
IR3串口2
IR4串口1
IR5并口2
IR6软盘驱动器
IR7并口1
从芯片IR0CMOS RTC实时时钟
IR1重定向到主8259A芯片的IR2引脚
IR2保留
IR3保留
IR4PS/2鼠标
IR5协处理器
IR6SATA主硬盘
IR7SATA从硬盘

8259A被叫作可编程中断控制器,那么它必定拥有一系列可配置的寄存器。一个8259A PIC包含两组寄存器,分别是ICW(Initialization Command Word,初始化命令字)寄存器组和OCW(Operational Control Word,操作控制字)寄存器组。其中,ICW寄存器组用于初始化中断控制器,在8259A芯片正常工作前必须先对ICW寄存器组进行设置;OCW寄存器组用于操作中断控制器,可随时通过OCW寄存器组设置和管理中断控制器的工作方式

PC机采用I/O地址映射方式,将8259A PIC的寄存器映射到I/O端口地址空间,因此必须借助IN和OUT汇编指令才能访问8259A PIC。主8259A芯片的I/O端口地址是20h和21h,从8259A芯片的I/O端口地址是A0h和A1h

2、配置ICW寄存器组和OCW寄存器组

ICW寄存器组共包含ICW1、ICW2、ICW3、ICW4四个寄存器,它们必须按照从ICW1到ICW4的顺序进行初始化。主8259A芯片的ICW1寄存器映射到I/O端口20h地址处,ICW2、ICW3、ICW4寄存器映射到I/O端口21h地址处;从8259A芯片的ICW1寄存器映射到I/O端口A0h地址处,ICW2、ICW3、ICW4寄存器映射到I/O端口A1h地址处。对主/从8259A芯片的初始化顺序可以是先后式的(先配置主芯片的ICW寄存器组,再配置从芯片的ICW寄存器组)或交替式的(先配置主/从芯片的ICW1寄存器,再设置主/从芯片的ICW2寄存器,依次类推直至ICW4寄存器)。

2.1、初始化命令字ICW

ICW1寄存器

描述
5~7对于PC机,该位必须为0
4对于ICW,该位必须为1
3触发模式,现已忽略,必须为0
2忽略,必须为0
11=单片8259A,0=级联8259A
01=使用ICW4,0=不使用ICW4

主/从8259A芯片的ICW1寄存器都固定初始化为00010001B(11h)

ICW2寄存器

描述
3~7中断向量号
0~20

对于中断向量号并没有特殊要求,通常情况下,主8259A芯片的中断向量号设置为20h(占用中断向量号20h27h),从8259A芯片的中断向量号设置为28h(占用中断向量号28h2fh)

ICW3寄存器

描述
71=IR7级联从芯片,0=无从芯片
61=IR6级联从芯片,0=无从芯片
51=IR5级联从芯片,0=无从芯片
41=IR4级联从芯片,0=无从芯片
31=IR3级联从芯片,0=无从芯片
21=IR2级联从芯片,0=无从芯片
11=IR1级联从芯片,0=无从芯片
01=IR0级联从芯片,0=无从芯片

描述
3~7必须为0
0~2从芯片连接到主芯片的IR引脚号

ICW4寄存器

描述
5~7恒为0
41=SFNM模式,0=FNM模式
3、2缓冲模式:00=无缓冲模式,10=从芯片缓冲模式,11=主芯片缓冲模式
11=AEOI模式,0=EOI模式
01=8086/88模式,0=MCS 80/85模式
  • AEOI模式:此模式可使中断控制器收到CPU发送来的第2个INTA中断响应脉冲后,自动复位ISR寄存器的对应位。
  • EOI模式:在EOI模式下,处理器执行完中断处理程序后,必须手动向中断控制器发送中断结束EOI指令,来复位ISR寄存器的对应位。
  • FNM(Fully Nested Mode,全嵌套模式):在此模式下,中断请求的优先级按引脚名从高到低依次为IR0~IR7。如果从8259A芯片的中断请求正在被处理,那么该从芯片将被主芯片屏蔽直至处理结束,即使从芯片产生更高优先级的中断请求也不会得到执行。
  • SFNM(Special Fully Nested Mode,特殊全嵌套模式):该模式与FNM基本相同,不同点是当从芯片的中断请求正在被处理时,主芯片不会屏蔽该从芯片,这使得主芯片可以接收来自从芯片的更高优先级中断请求。在中断处理程序返回时,需要先向从芯片发送EOI命令,并检测从芯片的ISR寄存器值,如果ISR寄存器仍有其他中断请求,则无需向主芯片发送EOI命令。

2.2、操作控制字OCW

OCW1寄存器

描述
71=屏蔽IRQ7中断请求,0=允许IRQ7中断请求
61=屏蔽IRQ6中断请求,0=允许IRQ6中断请求
51=屏蔽IRQ5中断请求,0=允许IRQ5中断请求
41=屏蔽IRQ4中断请求,0=允许IRQ4中断请求
31=屏蔽IRQ3中断请求,0=允许IRQ3中断请求
21=屏蔽IRQ2中断请求,0=允许IRQ2中断请求
11=屏蔽IRQ1中断请求,0=允许IRQ1中断请求
01=屏蔽IRQ0中断请求,0=允许IRQ0中断请求

OCW2寄存器

描述
7优先级循环状态
6特殊设定标志
5非自动结束标志
4恒为0
3恒为0
0~2优先级设定

OCW2寄存器的D5~D7位组合值含义

D7D6D5含义
000循环AEOI模式(清除)
001非特殊EOI命令(全嵌套方式)
010无操作
011特殊EOI命令(非全嵌套方式)
100循环AEOI模式(设置)
101循环非特殊EOI命令
110设置优先级命令
111循环特殊EOI命令

OCW3寄存器

描述
7恒为0
6、5特殊屏蔽模式:11=开启特殊屏蔽,10=关闭特殊屏蔽
4恒为0
3恒为1
21=轮询,0=无查询
1、010=读IRR寄存器,11=读ISR寄存器

2.3、初始化后数据

主/从8259AICWxI/O端口数值
主8259AICW120h11h
ICW221h20h
ICW321h04h
ICW421h01h
从8259AICW1A0h11h
ICW2A1h28h
ICW3A1h02h
ICW4A1h01h

3、键盘驱动

键盘设备也是拥有控制芯片的,如果希望键盘可以持续接收到按键中断请求,则必须对键盘控制器芯片有所了解才能实现

目前,市面上的键盘控制器芯片大多采用Intel 8042以及兼容芯片,键盘控制器芯片通过PS/2接口(或一些USB接口)与外部键盘设备相连。键盘设备通常会包含一个Intel 8048或兼容芯片,这个芯片会时刻扫描键盘设备的每个按键,并将扫描到的按键进行编码,每个按键的编码是唯一的,不会重复,所以8048芯片也被称为键盘编码芯片

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-Bp22vpZJ-1639554370294)(C:\Users\xumeng03\AppData\Roaming\Typora\typora-user-images\image-20211215150441829.png)]

当8048键盘编码器扫描到按键被按下时,它会将按键对应的编码值通过PS/2接口发送到8042键盘控制器芯片中。8042键盘控制器在接收到编码值后,会将其解析并转换成统一的键盘扫描码(第1套XT扫描码集),并存放到输出缓冲区中待处理器读取。如果此时键盘又有新的键被按下,8042芯片将不再接收新的数据,直至输出缓冲区被清空后,8042芯片才会继续接收按键编码数据。

键盘扫描码一共有3套:第1套为原始的XT扫描码集;第2套为AT扫描码集;第3套为PS/2扫描码集(很少使用)。现在,键盘皆默认使用第2套AT键盘扫描码,但是出于兼容性考虑,第2套扫描码最终都转换成第1套XT扫描码集供处理器使用(也可以设置成不转换成第1套XT扫描码集,但这样需要另作特殊配置)

第1套扫描码的特点是,每个按键扫描码由1 B数据组成,这1 B数据的低7位(位6到位0)代表按键的扫描码,最高位(位7)代表按键的状态(0:按下,1:松开)。当某个键被按下时,键盘控制器输出的扫描码叫作Make Code码,而松开按键时的扫描码则叫作Break Code码。例如,按键B的Make Code码是0x30,则它的Break Code码便是0xb0

4、实现

4.1、目录结构

├── a.img			# 镜像文件
├── boot
│	├── boot.asm	# boot
│	├── fat12.inc	# FAT12文件系统头
│   └── loader.asm	# loader
├── kernel		
│   ├── head.S		# 内核头程序
│   ├── entry.S		# 中断/异常现场保存
│   ├── entry.S		# 初始化IDT、TSS
│   ├── font.h		# ASCII
│   ├── printf.h	# 打印函数头文件
│   ├── printf.c	# 打印函数
│   ├── lib.h		# 依赖库
│   ├── string.h	# string相关函数
│   ├── trap.h		# 中断/异常头文件
│   ├── trap.c		# 中断/异常处理
│   ├── memory.h	# 初级内存管理头文件
│   ├── memory.c	# 初级内存管理
│   ├── interrupt.h	# 初级中断处理头文件
│   ├── interrupt.c	# 初级中断处理
│   ├── Kernel.lds	# 内核头程序链接脚本
│   └── main.c		# 内核主程序
├── clear.sh
└── run.sh

interrupt.h

#ifndef __INTERRUPT_H__
#define __INTERRUPT_H__

void init_interrupt();
void do_IRQ(unsigned long regs,unsigned long nr);

#endif

interrupt.c

#include "interrupt.h"
#include "lib.h"
#include "printf.h"
#include "memory.h"
#include "gate.h"

#define SYMBOL_NAME_STR(X)	#X
#define IRQ_NAME(nr) nr##_interrupt(void)
#define IRQ_NA(nr) IRQ_NAME(IRQ##nr)

#define SAVE_ALL				\
	"cld;			\n\t"		\
	"pushq	%rax;		\n\t"		\
	"pushq	%rax;		\n\t"		\
	"movq	%es,	%rax;	\n\t"		\
	"pushq	%rax;		\n\t"		\
	"movq	%ds,	%rax;	\n\t"		\
	"pushq	%rax;		\n\t"		\
	"xorq	%rax,	%rax;	\n\t"		\
	"pushq	%rbp;		\n\t"		\
	"pushq	%rdi;		\n\t"		\
	"pushq	%rsi;		\n\t"		\
	"pushq	%rdx;		\n\t"		\
	"pushq	%rcx;		\n\t"		\
	"pushq	%rbx;		\n\t"		\
	"pushq	%r8;		\n\t"		\
	"pushq	%r9;		\n\t"		\
	"pushq	%r10;		\n\t"		\
	"pushq	%r11;		\n\t"		\
	"pushq	%r12;		\n\t"		\
	"pushq	%r13;		\n\t"		\
	"pushq	%r14;		\n\t"		\
	"pushq	%r15;		\n\t"		\
	"movq	$0x10,	%rdx;	\n\t"		\
	"movq	%rdx,	%ds;	\n\t"		\
	"movq	%rdx,	%es;	\n\t"

#define Build_IRQ(nr)							\
void IRQ_NA(nr);						\
__asm__ (	SYMBOL_NAME_STR(IRQ)#nr"_interrupt:		\n\t"	\
			"pushq	$0x00				\n\t"	\
			SAVE_ALL					\
			"movq	%rsp,	%rdi			\n\t"	\
			"leaq	ret_from_intr(%rip),	%rax	\n\t"	\
			"pushq	%rax				\n\t"	\
			"movq	$"#nr",	%rsi			\n\t"	\
			"jmp	do_IRQ	\n\t");

//自定义中断
Build_IRQ(0x20)
Build_IRQ(0x21)
Build_IRQ(0x22)
Build_IRQ(0x23)
Build_IRQ(0x24)
Build_IRQ(0x25)
Build_IRQ(0x26)
Build_IRQ(0x27)
Build_IRQ(0x28)
Build_IRQ(0x29)
Build_IRQ(0x2a)
Build_IRQ(0x2b)
Build_IRQ(0x2c)
Build_IRQ(0x2d)
Build_IRQ(0x2e)
Build_IRQ(0x2f)
Build_IRQ(0x30)
Build_IRQ(0x31)
Build_IRQ(0x32)
Build_IRQ(0x33)
Build_IRQ(0x34)
Build_IRQ(0x35)
Build_IRQ(0x36)
Build_IRQ(0x37)

void (* interrupt[24])(void)=
{
	IRQ0x20_interrupt,
	IRQ0x21_interrupt,
	IRQ0x22_interrupt,
	IRQ0x23_interrupt,
	IRQ0x24_interrupt,
	IRQ0x25_interrupt,
	IRQ0x26_interrupt,
	IRQ0x27_interrupt,
	IRQ0x28_interrupt,
	IRQ0x29_interrupt,
	IRQ0x2a_interrupt,
	IRQ0x2b_interrupt,
	IRQ0x2c_interrupt,
	IRQ0x2d_interrupt,
	IRQ0x2e_interrupt,
	IRQ0x2f_interrupt,
	IRQ0x30_interrupt,
	IRQ0x31_interrupt,
	IRQ0x32_interrupt,
	IRQ0x33_interrupt,
	IRQ0x34_interrupt,
	IRQ0x35_interrupt,
	IRQ0x36_interrupt,
	IRQ0x37_interrupt,
};

void init_interrupt()
{
	int i;
	for(i = 32;i < 56;i++)
	{
		set_intr_gate(i , 2 , interrupt[i - 32]);
	}

	Printf("8259A init \n");

	//8259A-master	ICW1-4
	io_out8(0x20,0x11);
	io_out8(0x21,0x20);
	io_out8(0x21,0x04);
	io_out8(0x21,0x01);

	//8259A-slave	ICW1-4
	io_out8(0xa0,0x11);
	io_out8(0xa1,0x28);
	io_out8(0xa1,0x02);
	io_out8(0xa1,0x01);

	//8259A-M/S	OCW1
	io_out8(0x21,0xfd);
	io_out8(0xa1,0xff);

	sti();
}

void do_IRQ(unsigned long regs,unsigned long nr)	//regs:rsp,nr
{
	unsigned char x;
	Printf("do_IRQ:%#08x ",nr);
	x = io_in8(0x60);
	Printf("key code:%#08x\n",x);
	io_out8(0x20,0x20);
}
  嵌入式 最新文章
基于高精度单片机开发红外测温仪方案
89C51单片机与DAC0832
基于51单片机宠物自动投料喂食器控制系统仿
《痞子衡嵌入式半月刊》 第 68 期
多思计组实验实验七 简单模型机实验
CSC7720
启明智显分享| ESP32学习笔记参考--PWM(脉冲
STM32初探
STM32 总结
【STM32】CubeMX例程四---定时器中断(附工
上一篇文章      下一篇文章      查看所有文章
加:2021-12-16 17:50:31  更:2021-12-16 17:52:35 
 
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁

360图书馆 购物 三丰科技 阅读网 日历 万年历 2025年1日历 -2025/1/9 15:30:22-

图片自动播放器
↓图片自动播放器↓
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
图片批量下载器
↓批量下载图片,美女图库↓
  网站联系: qq:121756557 email:121756557@qq.com  IT数码