- 程序查询IO方式
- 一、无条件传送方式
- 输入接口
- 输出接口
- 二、查询方式
- 一、无条件传送方式
- 中断IO方式
- 中断类型
- 中断执行流程
- 可编程中断控制器8259
- 硬件与基本寄存器
- 工作流程
- 工作方式及其选择
- 级联
- 编程使用
- 中断实现方式
- 基本流程
- 1. 硬件连接
- 2. 编写初始化程序
- 初始化8259
- 设置中断向量表
- 3. 编写中断处理程序
- 中断方式实现举例
- 保护模式下的中断方式
程序查询IO方式
一、无条件传送方式
输入:开关、……
输出:发光二极管、继电器、步进电机、……
输入接口
- 开关的接法:接一个上拉电阻。如果开关闭合,输出为0;开关断开,输出为1。为了消抖,并联一个电容(抖动的波形频率较高,可以通过电容接地直接导走)
- 接入总线:三态门。由于总线是复用的,所以外设的信号不能直接接入到总线上,需要经过三态门的控制。
- 三态门的使能端定义了接口地址,避免了总线冲突。只有地址线输入固定的值的时候,三态门才会打开,数据线才能拿到接口的输出,这就是接口的地址的本质含义,即三态门的使能信号。在本例中,接口地址为FFF7H。
注意,三态门使能除了与接口地址有关外,还需要检测内存读写信号(IOR\IOW)和AEN信号(AEN=0表示由cpu控制总线,否则由DAM控制)
驱动程序
MOV DX,0FFF7H
IN AL,DX
AND AL,01H
JZ ON
JMP OFF
In
指令做了什么事情?
- 把DX(接口地址)的内容放到地址线上(16位)
- IOR给一个负脉冲, AEN=0
- 经过1、2后,三态门打开,接口的信息放入地址线D[7..0],放入寄存器 AL
AND AL,01H
得到最低一位,看看是否为1, 如果为1, 说明开关断开;如果为0,说明开关闭合。
输出接口
- 二极管的接法:我们的逻辑器件的高电平带来的电流不足以驱动三极管,但是能吸收较大的电流。所以我们接入一个上拉电阻,采用负逻辑控制二极管。
- 接入总线:锁存器。数据总线上无时无刻都有数据传输,所以如果不加锁存器而直接接入总线,一方面会有总线冲突,另一方面三极管会一直出于半亮的状态(01不断变化,相当于脉宽调制)。
- 锁存器的使能端定义了接口地址,避免了总线冲突。只有数据线的信号等于接口地址的时候,译码电路的才会触发锁存器的使能信号,把数据线中的内容传送给二极管寄存器。【并且会锁存住,所以二极管的亮暗不会变化】。在本例中,接口地址为0000H。
由于锁存器是上升沿有效,我们一般都是后沿写入,所以译码电路的输出应为低电平有效;如果锁存器是下降沿有效,那么要保证后沿写入,译码电路输出应为高电平有效
驱动程序
MOV DX,0000H
MOV AL,81H
OUT DX,ALMOV AL,00H
OUT DX,AL
OUT
指令做了什么事情?
- 把AL的内容放入数据线上,把DX的内容放到地址线上。
- 给IOW一个负脉冲,AEN=0
- 经过1、2后,锁存器打开,把数据线上DX的内容锁存住,传给输出接口(二极管)
二极管收到的是81H(1000 0001),所以上下两头的二极管会亮,其余不亮
如果采用8086内存管理方式进行16位的数据传送8
双体结构A0和BHE作为奇偶存储体的片选信号,分别连接地址线的低、高八位。
这也是的连接两个不同存储体的二极管有不同的接口地址。 3804H和3805H
但由于二者可以同时有效,所以译码电路的使能端只能有一个输出。
在编写程序的时候,由于接口地址3804H是偶地址,而8086可以在一个总线周期内完成对偶地址的16位数据读写,所以可以直接向3804H,传入16位数据来控制这16个二极管(当然也可以分开控制)
二、查询方式
基本思想是在准备读写的时候先检查,如果外设忙,就不读写;不忙则读写。
- \(D_0 - D_7\) : 数据线
- \(STB\) : 相当于使能信号,上升沿有效,写入数据,同时 \(BUSY\) 置1。由于上升沿有效所以要保证后沿写入就需要给负脉冲,所以初始值要置为1
- \(BUSY\) :忙信号,busy=1为忙,busy=0为不忙
方案1:如上图所示, 把 \(D_0 - D_7\) 、 \(STB\) 看做是输入接口, \(BUSY\) 看做是输出接口。
74LS244输入输出逻辑不变,可用作驱动,也可看做8个三态门,我们只用到其中一个三态门
- 两个输入接口接锁存器,一个输出接口接三态门
- 锁存器和三态门的使能分别接入译码电路定义的接口地址和接口读写信号。(为了严谨,最好也接入AEN)
- 该输出设备占用了三个连续的接口地址: 02F8H、02F9H、02FAH
驱动程序:
初始化选通信号:把STB置为高,后面好传入负脉冲
循环检测与传送:
- S检测外设忙:从接口地址为02FAH处读取BUSY信号到D7。不忙则开始传送
- 数据输出:把D0-D7数据传送到接口地址02F8H处
- 产生负脉冲来写入数据
- 将D7置为低电平,传送到接口地址02F9H处,使STB为低
- 按照题目要求,延时100us,给外设响应的时间
- 将D7置为高电平,传送到接口地址02F9H处,使STB再次为高,实现负脉冲
- 一个字节传送完成,循环1000次
上面的连接方式似曾相识:一个使能端,一个数据端,使能端收到负脉冲后读取数据段的数据。
这有点类似于前面所学的SRAM,ROM等内存的读写。
方案2:把 \(D_0 - D_7\) 、 \(STB\) 看做是输入接口,\(STB\) 看做使能,用内存写入的方式来控制外设。
数据段直接连到系统总线,因为有使能端,所以只要使能端不接通,不会写入数据。节省了一个IO口。
由于BUSY的检测和数据的写入不会同时发生,相当于一个半双工通信,所以BUSY的接口可以和STB的接口进行复用,再节省一个IO口。
参考内存的读写,我们能不能用一个 out
指令直接实现对外设的写入呢,前面我们已经知道,out
指令执行时会把数据放到数据线上然后发出负脉冲,这点和外设STB的写时序是一致的。所以可以直接用 out 02F9H,xxx
来讲XXX内容写入外设。
驱动程序:
MOV DX,02F9H
MOV AL,[BX]
OUT DX,AL ;将[al]写入外设
...
IN AL,DX ;将BUSY信号读入AL(复用)
但是此种接法也存在问题:外设可能来不及反映。
IN
和 OUT
执行需要一个总线周期 = 四个时钟周期,如果cpu主频为 5MHz,那么总线周期=0.2us*4=0.8us。对于内存来说是来得及反映的,但是对于外设来说很难反映(0.8us << 100us)。所以如果题目对外设的脉冲宽度有明确要求的话,就不能使用 in
和 out
指令来产生脉冲和传递地址与数据了。只能采用方案1,手动调节电平。
由于要手动调节电平并延迟,我们无法保证在此期间要传输的数据还保持在数据线上,所以要加锁存器,那么就又要为D0-D7添加接口地址。BUSY似乎还可以复用。
多外设查询方式示意图:
单点故障,一个花了就会一直阻塞在那里
无优先级的轮询
有优先级的轮询
中断IO方式
中断类型
中断向量表256行,每行四个字节(前两个字节存储偏移,后两个字节存储段基址),总共占1K
IF=0:关中断
注意:开中断指的是此时可以响应外部中断,至于内部中断和非屏蔽中断请求,任何时候都会响应的,与是否开关中断无关。
INTA响应:两个负脉冲,一个提醒中断控制器准备好,另一个使中断控制器发送中断向量码
中断执行流程
可编程中断控制器8259
硬件与基本寄存器
IRR:中断请求寄存器(正在请求的中断)
ISR:中断服务寄存器(正在执行的中断)
IMR:中断屏蔽寄存器(屏蔽的中断)
工作流程
ISR不止一个元素为1,说明允许中断嵌套。ISR的作用就是用来实现默认优先级,数字越小优先级越高。
工作流程:
- 发出外部中断IR3,置IRR[3]=1
- 收到来自cpu的第一个负脉冲后,8259可以认为中断已经处理完成了,IRR[3]=0,ISR[3]=1。表示中断正在处理。
- 收到来自cpu的第二个负脉冲后,8259发出中断向量码。
- CPU处理中断, 在中断服务程序的末尾要添加EOI命令
- 一般EOI:允许中断嵌套,默认的优先级(数字小的优先级高)。把中断优先级最高的中断的对应位清零。
- 特殊EOI:允许中断嵌套,自定义的优先级。在EOI后要添加数字,表示要清零的ISR的位。
- 自动EOI:不允许中断嵌套。第一个cpu脉冲时置为1,第二个cpu脉冲时置为0,相当于一直为0。【不允许中断嵌套时,其实ISR没有存在的必要】
工作方式及其选择
- 特殊屏蔽方式:允许中断嵌套,自定义的优先级。使用特殊EOI,后面要跟ISR的清零位。
- 一般屏蔽方式:允许中断嵌套。使用一般EOI。
- 固定优先级:默认的,数字小的优先级高
- 循环优先级:自动循环优先级和指定循环优先级
自动循环优先级:一个中断执行后,优先级变为最低
级联
指定循环优先级:指定中断的优先级
- 【主片】特殊全嵌套:同级或更高优先级的中断均可以打断当前中断。
- 【从片】一般嵌套方式(默认方式):只有公钥优先级的中断才可以打断当前中断。
我们预期中的中断源编号,越小优先级越高
为了实现这种理想的优先级。
- 如果主片采用一般嵌套方式的话,那么在IR7【级联后的编号】发出中断请求时,如果IR6也发出一个中断请求,那么主片由于是同级中断,就不会打断当前正在执行的IR7,导致IR6无法先执行。
- 如果主片采用特殊全嵌套方式的话,那么统计同级中断能打断当前的中断,然后在判断IR6的优先级是否比IR7高,高的话就打断IR7,执行IR6,这就达到了我们的预期
- SP/EN = 1:说明为主片。ICW3表示哪些引脚连接了从片
- SP/EN = 0:说明为从片。ICW3表示哪个引脚连接了主片
举个例子:
- 从片的IR2和IR1同时发送中断请求给主片,主片经过中断判优后,决定先响应从片3的IR2
- 主片向CPU发送中断请求
- CPU应答
- 发送第一个负脉冲,给主片和所有从片
- 主片收到第一个负脉冲后,向从片发送一个级联地址3
- 从片收到第一个负脉冲后,检查主片发来的级联地址,从片3发现收到的级联地址与自己ICW3中的地址是一致的,由此知道cpu在响应自己的请求
- cpu发送第二个负脉冲,从片3向数据总线发送中断向量码
编程使用
ICW1~4 加上 OCW1~3 加上 ISR,IRR,一共9个寄存器【IMR和OCW1是同一个寄存器】
如何通过一个地址线来访问9个寄存器?部分数据线也参与寻址。
初始化流程介绍
- 写偶地址【A0=0】,数据的第四位为1:此时会写ICW1
- 根据ICW1的写入内容,决定是否要写ICW3和ICW4。
- ICW1的D1写入0,说明为单片连接,不写ICW3
- ICW1的D0写入0,不写ICW4
- 写完ICW1后,要马上跟奇地址【A0=1】写ICW2、(ICW3、ICW4)
至此初始化结束,之后对奇地址的写入都是写入OCW1
注:OCW1是唯一一个既可以读也可以写的寄存器
各个命令字的介绍:【具体讲解:11.02网课1:25:00】
ICW:
刚才的级联例子已经表明了用法
OCW:
中断实现方式
基本流程
- 8259连接(硬件)
- 编写初始化程序
- 8259初始化
- 设置中断向量表
- 编写中断处理程序
1. 硬件连接
8259接入的是总线的A1,从中可知cpu是8086,因为8086的A0用作偶地址片选。
8259接入的是总线的D0~7,从中可知接入的是8086的偶存储体,总线的A0必然为0
如果不加上A0,会浪费掉两个奇地址:
(比如用FF01读取的时候,会选中8259,但是8259发的数据在低八位上。CPU却采样到高八位上。)
加上A0后,读取奇地址的时候,8259不会被选中,地址就没有被浪费。
2. 编写初始化程序
初始化8259
初始化:
偶地址写入 :\(ICW1 = 0001 0011\) :单片【不写ICW3】,写ICW4
奇地址写入: \(ICW2 = 0100 1000\) :设置中断向量码,注意只有高八位有效,所以填入49H的效果和48一样。中断向量码都是由48H开始。
奇地址写入: \(ICW3 = 0000 0011\) :一般嵌套方式,非缓冲,自动EOI
到此为止初始化完成。
奇地址写入:由于初始化已经完成,所以写的是OCW1/IMR: \(IMR = 1110 0000\) :屏蔽IR7、IR6、IR5
检查OCW1,查看其读写是否正常。
设置中断向量表
3. 编写中断处理程序
如果允许中断嵌套就需要开关中断
如果不是自动EOI就要在程序中发送EOI命令【让ISR的指定位清零】
如何发送EOI
中断方式实现举例
- 硬件连接
- 初始化程序:初始化8259,初始化中断向量表
- 编写中断处理程序:CLOCK
串讲:
第一步完成了硬件的连接。
第二步写中断处理程序
第三步写初始化程序,初始化程序一般做了三件事:
- 初始化8259,配置它的工作模式
- 调整中断向量表
- 把中断处理程序驻留在内存中
- 找一块空闲内存,用串操作把中断处理程序传送过去
- 用DOS的功能调用
这样当一个中断源如IR0来中断时,8259向CPU发送中断请求。
CPU收到后进行中断应答,两个负脉冲。
8259收到第一个负脉冲后,调整IRR和ISR。收到第二个负脉冲后,把中断向量码48H(在初始化时定义的)发给CPU。
CPU收到48H后,查找中断向量表。 IP = (48H4); CS = (48H4+2)。然后跳转中断处理程序来处理中断。
保护模式下的中断方式
增加的内存的特权级,使得中断向量表不能被随意访问。
中断向量表→中断描述符表
从内存地址0开始0→其实地址记录在IDTR寄存器中【记录表的起始地址和表的长度,防止越界访问】
中断描述符(表的每一项)为 8B:存储中断处理程序在段表的基址和偏移,以及访问级别等信息。