首页 > 编程语言 >微机原理与接口技术——期末笔记 实验总结 侧重Win32汇编代码实现

微机原理与接口技术——期末笔记 实验总结 侧重Win32汇编代码实现

时间:2025-01-03 18:34:05浏览次数:3  
标签:接口技术 中断 MOV AL Win32 微机 DX AX OUT

微机原理与接口技术

声明:本篇文章是在复习期末考试的过程中根据教材与实验所总结的复习资料,参考书目为*《微机原理与接口技术(第2版)——Win汇编、接口及设备驱动》郭兰英 赵祥模编著*。
文章只重点总结了前两章、第五章、五大芯片、键盘、AD转换的具体代码示例。所有代码均来自课程实验以及书本代码。重点侧重于代码的实现,芯片的具体介绍没有列出。读者可以先查阅学习基本的芯片信息,再来看代码巩固。
我们学校的考试题型:
考试题型:选择和填空各10分,简答40分(包括简答和程序设计),综合题40分(主要考察接口技术方面的内容)

文章目录

第1章 微型计算机系统概述

1.1 微型计算机系统组成

微机组成包括硬件和软件。

紧靠硬件层之上的是硬件驱动层,固化在ROM中,直接控制底层硬件,称为基本输入输出系统(BIOS)

驱动软件层之上是操作系统,是计算机所有软件、硬件资源的控制者,决定计算机各种资源的使用和分配。

操作系统之上的是应用软件

1.计算机设计思想:存储程序(冯诺依曼)

2.硬件组成:微处理器、存储器、总线、接口、外设

1.2 工作方法

1.指令

微处理器主要任务:数据传送操作、算术运算、控制程序的流向。

微处理器的每一条基本操作成为一条指令。一个微处理器所能执行的全部指令,就是这个微处理器的指令系统。指令系统在设计微处理器时就已经决定,是固有的功能。每个指令都有一个唯一的指令码(OP)

2.程序

把指令按要求排列起来就是程序。编程分为汇编语言编程和机器码语言编程,高级语言程序中的一条语句就是由若干条指令的程序段完成。

3.存储程序和程序控制(现代计算机的基本特征)

微处理器顺序的、周而复始的从内存中取出指令,把指令码变换成控制序列信号(指令译码),控制序列信号发向有关部件,控制完成指令规定的操作(指令执行),直至完成全部指令操作(执行程序)。

1.3 微机系统的启动和操作系统的装载

BIOS是直接与主板上的硬件打交道的底层代码,固化在主板的一个ROM芯片上的程序,它保存着基本输入输出程序、系统设置信息、开机上电自检程序和系统启动自举程序。

  1. 按下电源开关,CPU得到启动地址为FFFF0H,CPU从该地址开始取指令、执行指令,放在这里的是一条跳转指令,跳到系统BIOS中真正的启动程序处。
  2. 系统BIOS首先通电后自检,检查设备是否能正常工作,否则蜂鸣器发出报警。
  3. 系统BIOS最后一步,根据系统CMOS设置中保存的启动顺序搜寻软驱、硬盘等设备,读取并执行操作系统引导记录,将操作系统核心文件调入存储器,以启动系统。

BIOS程序控制计算机的启动以及操作系统的装载。

1.4 微处理器体系结构

1.8086的结构

8086处理器具有16位寄存器组和16位的数据总线,20位地址总线,因此可以寻址220=1MB的地址单元。

而8088不同之处在于其对外的数据总线是8位,所以称为准16位处理器。

8088处理器由两个独立的工作单元组成:执行单元EU总线接口单元BIU。EU不与外部总线相连,它只负责执行指令,而BIU负责从存储器或外部设备中读取指令和读写数据,即总线操作

这两个单元处于并行状态,可以同时进行执行指令和读取指令操作。

BIU始终根据IP提供偏移地址、CS提供段地址在内存中进行寻址,并放入指令队列中。

2.8086程序设计模型

8086/8088的寄存器有8个通用寄存器、2个控制寄存器、4个段寄存器。

(1)16位通用寄存器(8个):

AX(AH AL)累加器Accumulator
BX(BH BL)基址寄存器Base Register
CX(CH CL)计数器Count Register
DX(DH DL)数据寄存器Data Register
SP堆栈指针Stack Point
BP基址指针Base Point
SI源变址寄存器Source Index
DI目标变址寄存器Destination Index

AX、BX、CX、DX均可分为高8位和低8位,可作为独立的8位寄存器使用,因此也可以处理8位二进制数,做到向上兼容。

(2)2个控制寄存器:

IP指令指针Instruction Point
FLAGS状态标志寄存器Flags Register

指令指针(IP):

具有自动递增功能,跟踪指令地址,程序执行过程中自动加1。

状态标志寄存器(FLAGS):

共有9个标志位,其中6个为反应ALU结果,3个是控制标志。

**6个状态标志位:**只有CPU进行了算术或逻辑运算的指令,才会影响标志寄存器的6位状态位。

  1. 进位标志位(CF Carry Flag):ALU结果最高位有进位或借位,CF=1。
  2. 奇偶标志位(PF Parity Flag):指令执行后,ALU结果的低8位中1的个数为偶数,PF=1。
  3. 辅助进位标志位(AF Auxiliary Flag):加减算术指令执行后,最低4位有进位或者借位,AF=1。该标志用于系统进行BCD码算术结果的调整。
  4. 零标志位(ZF Zero Flag):指令执行后,ALU的结果为0,ZF=1。
  5. 符号位标志位(SF Sign Flag):该为总是和ALU结果的最高位相同。
  6. 溢出位(OF Overflow Flag):加减算术指令执行后,如果溢出,OF=1。

3个控制标志位:

  1. 陷阱标志(TF Trap Flag):若TF=1,则CPU处于单步执行指令的工作方式,每执行一条指令就自动产生一次内部中断,用于调试程序
  2. 中断允许标志位(IF Interupt Enable Flag):当IF=1时,CPU允许响应可屏蔽中断请求。
  3. 方向标志(DF Direction Flag):该标志用于控制数据块操作指令的步进方向。若DF=0,则从低地址向高地址方向步进。

(3)4个段寄存器:

CS代码段寄存器Code Register
SS堆栈段寄存器Stack Register
DS数据段寄存器Data Register
ES附加段寄存器Extra Register

8086采用段地址和偏移地址进行寻址(CS:IP)

访问内存中的变量时需要通过段寄存器来指定段地址。默认情况下,指令会使用 DS 寄存器作为段地址来访问内存中的变量。

因此,通常情况下,访问数据段中的变量前必须正确设置 DS(数据段寄存器)

变量 VALUE 的地址由 (DS × 16) + 偏移 计算得来。

如果 DS 没有正确指向 DATA 段,则访问的内存位置会出错。

3.计算机的工作过程

计算机的工作过程就是运行程序,用户通过程序控制计算机的操作。

运行程序:逐条地从内存中取出程序中的指令并执行指令规定的操作。

4.80386~Pentium4处理器

32位扩展寄存器:
通用寄存器EAX、EBX、ECX、EDX、ESP、EBP、ESI、EDI

控制寄存器EIP、EFLAGS

段寄存器CS、SS、DS、ES、FS、GS(新增)

第2章 微处理器存储器管理技术

2.1 实模式存储器寻址

实模式只允许微处理器寻址第一个1MB内存空间(实模式内存/常规内存),DOS操作系统要求微处理器工作于实模式,可以向上兼容。

实模式下用段地址和段内地址(偏移地址)的组合访问内存单元。即段地址:偏移地址,这样表示称为逻辑地址。物理地址为:
物理地址 = 段的起始地址 + 偏移地址 = 段地址 ∗ 10 H + 偏移地址 物理地址=段的起始地址+偏移地址=段地址*10H+偏移地址 物理地址=段的起始地址+偏移地址=段地址∗10H+偏移地址

2.2 默认段和偏移寄存器

  1. 代码段:CS:IP或CS:EIP
  2. 堆栈段:通过栈指针或基址指针寻址。SS:SP/BP或SS:ESP/EBP
  3. 数据段:DS:BX/DI/SI/8位数或16位数
  4. 附加段:ES:串指令DI

段加偏移寻址机制允许程序和数据不需要任何修改,而使程序和数据重定位。

第3章 汇编语言

第5章 微处理器的硬件特性

5.1 8088总线周期

CPU的操作都是在系统主时钟CLK的控制下按节拍有序进行的。

**指令周期:**CPU执行一条指令的时间称为一个指令周期。

**总线周期:**通过CPU总线对存储器或者I/O端口进行一次读/写的过程。

一个指令周期由若干总线周期组成,一个总线周期由若干时钟周期T组成。时钟周期是系统主时钟频率的倒数。

在8086/8088CPU中,一个总线周期由4个时钟周期T1,T2,T3,T4组成,时钟周期也称时钟状态。

在T1状态,CPU往数据/地址多路复用总线上发出访问存储器或I/O端口的地址信号

在T2状态,CPU从总线上撤销地址,若为读周期,发出RD信号,使数据/地址多路复用总线的低8位(数据线)处于高阻抗状态,以便CPU有足够的时间从输出地址方式转变为输入数据方式。若为写周期,发出WR信号,由于输出数据和输出地址都是写总线过程,因此不需要缓冲时间,CPU在T2~T4期间把数据放到总线上。

在T3状态,数据/地址分时复用线的低8位上出现由CPU输出的数据或为CPU从存储器或I/O端口读入的数据。

在T4状态,8088完成数据传送,使控制信号变为无效,结束总线周期。

5.2 总线控制逻辑

  1. 总线的分离

8088的数据/地址是分时复用,但是实际使用时必须分离,因为存储器和I/O要求在整个读周期或写周期期间,地址保持有效和稳定。

  1. 总线的缓冲

CPU连接芯片过多时,必须经过缓冲驱动,增加负载能力。

第8章 基本的I/O接口

外设不能与CPU直接相连,必须经过中间电路与系统相连,这部分电路叫做I/O接口电路。有一部分功能模块是所有I/O接口都需要的,PC系统把这部分功能模块做在了主板上,称之为基本的I/O接口,包括中断控制器8259,DMA控制器8237,定时/计数器8254,并行接口8255,串行接口8251,五大芯片。

8.1 接口技术

接口设计两个基本问题:CPU如何寻址I/O设备;CPU如何与I/O设备连接,进行数据、状态和控制信号的交换。

8.1.1 基本概念
  1. I/O接口的主要功能

(1)I/O接口的选择功能:CPU在同一时间只能与一台I/O设备交换信息,只有被选定的I/O设备才能与CPU交换信息,因此要进行地址译码

(2)对输入/输出数据进行缓冲、隔离和锁存

(3)对信号的形式和数据的格式进行变换

(4)与CPU和I/O设备进行联络

  1. I/O接口典型结构

(1)内部结构

I/O接口内部一般由数据、状态、控制三类寄存器组成

(2)外部特性

接口引脚根据其连接特性分为面向CPU一侧的信号(数据线、地址线、控制线,一般比较规整)和面向外设一侧的信号(复杂,种类繁多)

(3)接口的可编程性

可编程是指接口芯片的功能和工作方式可通过程序设定,通过向芯片中的寄存器写入相应的信息来完成,这样的写入程序称为接口芯片的初始化程序

  1. I/O接口的编址方法

(1)I/O接口统一编址

统一编址方式也称存储器映像I/O寻址方式。将每一个I/O端口作为存储器的一个单元看待,即每一个端口占一个存储单元地址。不需要专门的I/O指令,程序中不易区分哪些访问存储器哪些访问外设。

(2)I/O接口独立编址

将存储器和I/O端口建立两个完全独立的地址空间,且二者可以重叠,CPU需要使用专门的信号(IN、OUT指令)来区分是对存储器访问还是对I/O端口进行访问。不占用存储器的存储空间且地址译码电路相对简单,但是访问端口的指令没有访问存储器的指令丰富,CPU需要设置专门的门控信号进行区分。

8.1.2 输入/输出方式

CPU在控制总线时,CPU与外设之间传输必须以CPU的AL/AX/EAX为中介,即输入输出指令(IN、OUT)完成。输入输出过程分为两步:查询状态、完成数据输入输出。基于这两步,输入输出传送方式分为三种:程序方式、中断方式、DMA方式。

  1. 程序方式

(1)无条件输入输出

一些简单外设如放光二极管、数码管、按键、开关等由于其输入输出频次低,可以随时输入输出,因此认为它们总是处于“准备好”状态的,这样就不需要查询外设的工作状态。

假设地址译码器的译码为380H,可用指令:

MOV DX,380H  ;DX存入译码值
IN AL,DX     ;把外设的数据输入AL寄存器
MOV DX,380H  
OUT DX,AL    ;把AL的值通过锁存器输出外设

端口地址如果是8位地址可直接寻址,如果超过8位,则需要通过DX寻址。

(2)条件输入输出(查询方式)

通过程序查询相应设备的工作状态,若状态处于“没准备好”,则CPU不能进行输入输出操作,需要等待。

一般外设可以提供一些反应其状态的信号,如READY=1,BUSY=1等。

假设从某输入设备上输入一组数据送缓冲区,若缓冲区已满,则输出一组信息BUFFER OVERFLOW,然后结束。设该设备的数据端口位382H,状态端口为383H。

DATA SEGMENT                        ;定义数据段
MESS1 DB "BUFFER OVERFLOW","$"      ;定义需要显示的信息,$ 用于字符串结束标记
BUFF DB 60 DUP(?)                   ;定义一个大小为 60 字节的缓冲区,初始化为空
DATA ENDS                           ;数据段结束

CODE SEGMENT                        ;定义代码段
     ASSUME CS:CODE,DS:DATA         ;声明代码段和数据段的关系
START: MOV AX,DATA                  ;将数据段地址加载到 AX 寄存器
       MOV DS,AX                    ;将数据段地址加载到数据段寄存器 DS,此时为需要显示的字符串
       MOV BX,OFFSET BUFF           ;取缓冲区首地址,送缓冲区指针
       MOV CX,60                    ;设置计数器 CX 为 60,表示缓冲区的大小
       
WAIT:  IN    AL,383H                ;查询状态端口,若为0则等待
       TSET  AL,01H                 ;逻辑与,检查最低位是否为 1
       JZ    WAIT                   ;如果ZF=1,即逻辑与结果为0,则跳转WAIT,等待并重新查询 
       IN    AL,382H                ;外设已经准备好,端口号直接寻址,从外设读取数据字节到 AL
       MOV   [BX],AL                ;将读取的数据字节存入当前的缓冲区地址(BX 指向)
       INC   BX                     ;缓冲区指针递增,指向下一个字节
       LOOP  WAIT                   ;循环,CX 减 1,直到 CX 为 0(缓冲区填满)
       
       MOV   DX,OFFSET MESS1        ;将字符串 MESS1 的地址加载到 DX
       MOV   AH,09H                 ;调用 DOS 功能 09H,用于显示字符串
       INT   21H                    ;执行中断,显示“BUFFER OVERFLOW”
       
       MOV   AH,4CH                 ;调用 DOS 功能 4CH,用于结束程序
       INT   21H                    ;执行中断,返回操作系统
CODE ENDS
END START
  1. 中断方式

查询式的传输过程中,CPU要不断地查询外设的状态,当外设没有准备好时,CPU就只能等待循环,不能执行其他程序,浪费了大量时间,降低了CPU的利用率。

中断传送方式:由外设通过接口电路向CPU发出中断请求信号,CPU在满足条件的情况下,暂停当前正在进行的主程序,转入中断服务子程序,待输入输出操作执行完毕后返回被中断的主程序。

中断服务子程序的任务:
(1)保护现场 PUSH;

(2)开中断,由STI指令实现,可实现中断的嵌套;

(3)中断服务;

(4)恢复现场 POP;

(5)返回 IRET,不能使用一般的子程序返回指令RET,IRET除了能恢复断点地址外,还能恢复中断响应时的标志寄存器的值(后一个动作RET无法完成)。

  1. DMA方式(Direct Memory Access)

直接数据传送DMA是内存与外设之间直接进行数据传送,而不经过CPU中转的方式,由硬件直接完成。

传送过程:

(1)若外设有传送要求,将向DMAC发送“DMA请求”DMAREQ,该信号维持到DMAACK响应为止,DMAC收到请求后,向CPU发送”总线请求“信号HOLD,表示希望占用总线,该信号在整个传送过程中维持有效。CPU在当前总线周期结束时相应请求,向DMAC发送”总线响应“信号HLDA,表示已经放弃总线,即CPU总线呈高阻态。此时,DMAC控制器向设备回送”DMA响应“信号DMAACK,该信号将清除DMA请求触发器,传送开始。

(2)传送开始时,DMAC向存储器(或外设)发送读(写)控制信号,同时向输出设备(或存储器)发送写(读)控制信号,完成一个字节的传送。

(3)DMAC内部具有自动增减存储器地址和计数功能,据此判断任务是否完成,如果完成将使CPU的”总线请求“信号HOLD无效,通知CPU传送结束,CPU重新接管对总线的控制。

8.2 中断系统

8.2.1 中断的基本原理
  1. 中断是指暂停当前CPU正在运行的程序,而跳转到执行相应的中断服务子程序,在中断服务子程序执行完毕后,再返回到原程序执行的一种工作方式。

  2. 中断源的分类:

    外部中断内部中断
    可屏蔽中断INTR内部硬件中断:​除法出错(0号中断)、单步跟踪(TF=1,1号中断)
    断点中断、溢出中断(OF=1)
    不可屏蔽中断NMI内部软件中断:INT n
  3. 中断的过程:中断请求、中断判优、中断响应、中断服务、中断返回

    CPU在INTR引脚上接到一个中断请求信号,如果此时IF=1,且具有最高优先级,CPU就会在当前指令执行完以后开始响应外部的中断请求。

    (1)将中断类型码放入暂存器保存。

    (2)将标志寄存器内容压入堆栈,保护中断时状态。

    (3)将IF和TF标志清零,防止在响应中断的同时又来别的中断请求,并且防止CPU以单步方式执行中断处理子程序。当用户要进行中断嵌套时,必须在自己的中断处理子程序中用开中断指令来重新设置IF。

    (4)保护断点,IP和CS入栈。

    (5)根据中断类型码在中断向量表中找到中断向量,将其装入IP和CS,这样就实现了自动转向中断服务子程序执行。

8.2.2 中断控制器 8259A

8259实例应用:
主片的端口地址为20H、21H,从片的端口为A0H、A1H。主片、从片8259都采用普通的EOI方式,对于从片提出的中断,其服务子程序返回前应发送两个EOI命令,一个给从片一个给主片:

MOV AL,20H
OUT 0A0H,AL ;从片   0010 0000 EOI 发送结束命令  OCW2
OUT 20H,AL  ;主片

20H和A0H分别控制主片和从片的ICW1、OCW2、OCW3

21H和A1H分别控制主片和从片的ICW2、ICW3、ICW4、OCW1

用手动产生单脉冲KK1作为中断请求信号。要求每按一次开关产生一次中断,在屏幕上显示一次 “7”。

;=========================================================
; 文件名: A8259.ASM
; 功能描述: 8259中断实验,中断源为主片8259的IRQ7
;           每产生一次中断输出显示一个字符7
;=========================================================

SSTACK	SEGMENT STACK           ;定义堆栈段
		DW 32 DUP(?)            ;分配32个字(DW)的堆栈空间
SSTACK	ENDS                    ;堆栈段结束

CODE   	SEGMENT                 ;定义代码段
	   	ASSUME CS:CODE          ;假设代码段为CODE
START: 	PUSH DS
		MOV AX, 0000H
		MOV DS, AX              ;数据段地址设置为 0000H(BIOS 数据区)
		MOV AX, OFFSET MIR7		;取中断入口地址 中断服务程序 MIR7 的偏移地址放入 AX
		MOV SI, 003CH			;中断矢量地址   中断矢量表中 IRQ7 的偏移地址为 003CH
		MOV [SI], AX			;填IRQ7的偏移矢量 设置中断服务程序的偏移地址到中断向量表
		MOV AX, CS				;取当前代码段的段地址
		MOV SI, 003EH           ;中断矢量表中 IRQ7 的段地址为 003EH,一个中断向量号占4个单元
		MOV [SI], AX			;填IRQ7的段地址矢量
		CLI                     ;关中断,防止初始化过程中发生中断
		POP DS
		;初始化主片8259
		MOV AL, 11H             ;0001 0001 边缘触发、级联、需要ICW4
		OUT 20H, AL				;写入主片8259的控制端口,A0=0,D4=1,ICW1
		MOV AL, 08H             ;0000 1000 中断类型号为1
		OUT 21H, AL				;引脚A0=1,ICW2
		MOV AL, 04H             ;0000 0100 IR2上接有从片
		OUT 21H, AL				;ICW3,仅用于级联方式
		MOV AL, 01H             ;0000 0001 普通全嵌套方式、非缓冲方式、普通EOI、8086
		OUT 21H, AL				;ICW4
		MOV AL, 6FH				;OCW1  0110 1111   开放4号中断串口用,7号中断实验用
		OUT 21H, AL             ;A0=1,OCW1
		STI                     ;开中断,允许中断
AA1:	NOP                     ;空操作,用于延时或等待
		JMP AA1		            ;无限循环等待中断发生
MIR7:	STI                     ;开中断(必要时重新开启)
		CALL DELAY              ;调用延时子程序,模拟处理时间
		MOV AX, 0137H           ;设置显示字符 '7' 的功能(BIOS 中断 10H 功能)
		INT 10H					;显示字符7
		MOV AX, 0120H           ;设置光标移动的功能(BIOS 中断 10H 功能)
		INT 10H
		MOV AL, 20H             ;中断结束命令(EOI)
		OUT 20H, AL				;写入主片 8259 的控制端口,通知中断结束
		IRET		            ;中断返回
DELAY:	PUSH CX                 ;保存 CX 寄存器
		MOV CX, 0F00H           ;初始化延时计数器
AA0:	PUSH AX                 ;伪操作模拟延时
		POP  AX                 ;伪操作模拟延时
		LOOP AA0                ;循环直到 CX 减为 0
		POP CX                  ;恢复CX
		RET		                ;返回到调用点
CODE	ENDS
		END  START

从片中断子程序编程实例:

系统保留的中断源IR10其信号线接到了系统总线的引脚B4,利用该中断源向CPU提出中断请求,CPU响应中断后显示Interrupt was acknowledged!

IR10的中断属于CPU的外部中断,其中断请求信号通过8259转发给CPU,首先根据对应中断类型码将中断服务程序的入口地址填入中断向量表,随后向8259写入操作命令字对中断屏蔽与中断结束进行处理。

Stack SEGEMNT STACK 'STACK'; 定义一个堆栈段,段名为 Stack,类名为 'STACK'
      DW 100DUP(?)         ; 分配 100 个字(每个字为 2 字节)的空间,内容初始化为未知值
Stack ENDS
Data SEGMENT
sDa1 DB 'Wait interrupt',0AH,0DH,'$' ;0AH 和 0DH 分别是换行(\n)和回车(\r)字符。
sDa2 DB 'Interrupt was acknowledged!',0AH,0DH,'$'
sDa3 DB 'Programe terminated normally',0AH,0DH,'$'
Data ENDS
Code SEGMENT
     ASSUME SS:Stack,CS:Code,DS:Data
Start: MOV AX,0
       MOV ES,AX
       MOV AX,Data                   ; 加载数据段地址到 AX
       MOV DS,AX                     ; 初始化数据段寄存器 DS,字符串开始位置
       MOV AX,SEG Ir10               ; 获取 Ir10 段地址
       MOV ES:01CAH,AX               ; 将中断服务程序段地址存入中断矢量表的高字节部分
       MOV AX,OFFSET Ir10            ; 获取 Ir10 偏移地址
       MOV ES:01C8H,AX               ; 将中断服务程序偏移地址存入中断矢量表的低字节部分
       
       MOV DX,OFFSET sDa1            ; 加载字符串 sDa1 的地址到 DX
       MOV AH,9                      ; 准备调用 DOS 中断服务显示字符串,以'$'结尾
       INT 21H                       ; 显示字符串 'Wait interrupt'
       
       IN AL,0A1H                    ; 从 8259 中断控制器的 OCW1 端口读取掩码字,A0=1
       AND AL,0FBH                   ;1111 1011允许 IRQ2 (主片从片连接中断)
       OUT 0A1H,AL                   ;将修改后的掩码字写回
       JMP $                         ; 死循环等待中断
       
       MOV DX,OFFSET sDa3            ;中断执行完毕,程序正常退出
       MOV AH,9
       INT 21H                       ; 显示字符串 'Program terminated normally'

       MOV AH,4CH                    ; 准备调用 DOS 中断服务退出程序
       INT 21H                       ; 程序退出
Ir10:  PROC FAR                      ; 表示这个过程是“远程过程”。可能不在同一个代码段

       MOV DX,OFFSET sDa3
       MOV AH,9
       INT 21H                       ; 显示Interrupt was acknowledged!
       
       MOV AH,4CH                    ; 调用退出服务
       INT 21H
       
       MOV AL,62H                    ; 中断结束命令
       OUT 0A0H,AL                   ; 通知从片中断结束
       OUT 20H,AL                    ; 通知主片中断结束
       IN AL,0A1H                    ; 从 8259 中读取掩码字
       OR AL,4                       ; 0100 恢复 IRQ2 的屏蔽
       OUT 0A1H,AL                   ; 更新掩码寄存器
       POP AX                        ; 修改返回地址
       ADD AX,2                      ; 调整堆栈指针
       PUSH AX
       IRET
Ir9    ENDP
CODE   ENDS
       END START

8.3 DMA 控制器 8237A

在一个芯片中有4个独立的DMA通道,对每个通道的DMA请求都可以分别允许和禁止,每个通道请求具有不同的优先权,每个通道一次传送最大长度为64KB,可以用级联的方式扩展通道数。

编程将内存D4000H的10个数据,使用DMA方式从内存输出到L0-L7,8个LEd灯,假定8237的地址为10H-1FH。

DATA SEGMENT AT 0D400H
OUTDATA DB 01,02,04,08,10H,20H,40H,80H,0FFH,00H  ; 定义 10 个数据,存储在 0D400H 的内存地址
DATA ENDS
CODE SEGMENT
ASSUME CS:CODE,DS:DATA
START:
        MOV AX,DATA             ; 加载数据段基地址
        MOV DS,AX               ; 初始化数据段寄存器
        OUT 1CH,AL              ; 屏蔽所有通道  地址 1CH:DMA 屏蔽寄存器。
        MOV AL,49H              ; 模式寄存器设置 地址 1BH:模式寄存器 写方式控制字
        OUT 1BH,AL              ; 0100 1001 单字节 递增 禁止自动预置 读传送(内存到外设) 通道1
        MOV AL,0DH              ; 传输计数寄存器高字节
        OUT 83H,AL              ; 通道 1 的计数寄存器
        MOV AL,0                ; 传输计数低字节
        OUT 12H,AL
        MOV AL,40H              ; 传输 40H(64 字节)数据
        OUT 12H,AL
        MOV AL,0AH              ; 配置传输数据量为 10 字节(十六进制 0AH)地址寄存器高字节
        OUT 13H,AL
        MOV AL,00H              ; 地址寄存器低字节
        OUT 13H,AL
        MOV AL,01               ; 启动通道 1
        OUT 1AH,AL              ; 地址 1AH:DMA 请求寄存器。
        MOV AH,4CH              ; DOS 中断功能:结束程序
        INT 21H
CODE ENDS
END START

8.4 计数/定时器8254

一般来说,定时信号可由软件和软硬件结合两种方法来获得:

软件:通过编程,统计执行一段程序所花费的时钟数,乘以一个时钟周期的时间,就得到定时的时间。但是要占用CPU的工作时间来等待,且延时子程序需要用指令执行来拼凑时间,比较麻烦。

软硬件结合:用可编程计数/定时器构成一个定时电路,用指令对计数器设置定时常数,并用指令启动计数器,到定时值时,便产生一个定时输出。不占用CPU的工作时间,且可以利用定时结束信号建立多种作业环境。

8254主要功能:

三个独立的16位计数器,每个计数器可按二进制或十进制编程,均可编程6种不同的工作方式。每个计数器计数频率最高位10MHz,可读回状态。

有6种计数方式:

方式概述计数过程门控信号的影响新的初值影响
0 计数结束产生中断一次性,门控信号常有效方式控制字写入后,OUT变为低电平,计数初值写入后的下一个时钟下降沿开始计数,减到0后OUT变为高电平GATE=1时允许计数,GATE=0时暂停计数保持当前值,门控对输出无影响写入新初值后,下一个时钟下降沿开始新的计数
1 可重触发的单稳触发器输出宽度可控的负脉冲方式字写入,OUT变为高电平,计数值写入后不计数,当门控信号正脉冲后,下一个下降沿开始计数门控上升沿后在下一个时钟下降沿终止原先计数,开始新的计数计数过程中写入初值不影响,只有门控信号到来时,下一个时钟下降沿才会按新值开始计数
2 分频器输出连续的负脉冲,宽度为一个时钟周期方式字写入,OUT变为高电平。计数值写入后的下一个时钟下降沿开始计数,减到1时,OUT变为低电平,减到0时又变为高电平,同时从初值开始新的计数过程低电平终止计数,恢复高电平的第一个时钟下降沿重新按照初值开始计数当门控信号一直为高电平时,新值不影响当前计数,计数结束后,下一个技术周期按照新值计数。
当写入新值后遇到门控上升沿结束当前计数,下一个时钟下降沿按照新值开始计数
3 方波发生器当计数值N为偶数,输出对称方波,OUT先高后低。
为奇数时,高电平多占一个时钟周期的锯齿波。
同2同2同2
4 软件触发选通输出负脉冲后停止计数,一次性写入控制字后,OUT变为高电平,计数值写入后的下一个时钟下降沿开始计数,减到0时,输出一个时钟周期的负脉冲。下一轮计数必须重新写入初值低电平终止计数,恢复高电平的第一个时钟下降沿重新按照初值开始计数写入新初值后,下一个时钟下降沿开始新的计数
5 硬件触发选通输出负脉冲后停止计数,一次性,但门控上升沿可重新开始计数。计数过程靠门控信号触发,称为硬触发门控上升沿后在下一个时钟下降沿终止原先计数,开始新的计数计数过程中写入初值不影响,只有门控信号到来时,下一个时钟下降沿才会按新值开始计数

在方式2和3中:
$$
脉冲周期=计数值*时钟周期 \

计数值=脉冲周期*时钟频率
$$
三个计数器构成系统三个通道,PC分配了4个I/O端口地址用于定时系统,0040H~0042H分别对应8254的三个计数器,8254的控制寄存器和状态寄存器公用端口地址0043H。三个通道的时钟输入CLK的频率均为1.19318MHz:

  1. 通道0:系统计时器,输出接到IR0,不受各种中断影响。中断0每隔55ms产生一次中断,是最高级的可屏蔽中断,可以完成日历时钟计数
MOV AL,36H
OUT 43H,AL      ;0011 0110 计数器0 读写16位数,方式3方波发生器,binary
                ;计数值=55ms*1.19318MHz=65624.9>65535,因此选择计数值为0
MOV AL,0         
OUT 40H,AL      ;输出计数值,低八位
OUT 40H,AL      ;输出计数值,高八位
  1. 通道1:为系统动态存储器刷新,设定刷新时间约为15us:
MOV AL,54H      ;0101 0100 计数器1 只读写低字节 方式2 分频器 Binary
                ;计数值=15*1.19318=18=12H
OUT 43H,AL
MOV AL,12H
OUT 41H,AL
  1. 通道3:驱动系统机箱内的声频扬声器,方式3,方波输出约1KHz方波,发声程序还受并口61H的D1D0控制:
Beef PROC  ;定义一个Beef过程
     MOV AL,0B6H       ;1011 0110 计数器2,读写16位数,方式3,b
     OUT 43H,AL
     MOV AX,0533H      ;1.19318MHZ/1331=869Hz
     OUT 42H,AL        ;低8位
     MOV AL,AH         
     OUT 42H,AL        ;高8位
     IN AL,61H
     MOV AH,AL         ;保存当前状态
     OR AL,03H         ;AL后两位置1,激活扬声器
     OUT 61H,AL
     SUB CX,CX         ;CX清零
G0:  LOOP G0           ;延迟片段
	 DEC BL            ;入口参数BL=6发长声,BL=1发短声
	 JNZ GO
	 MOV AL,AH
	 OUT 61H,AL        ;关闭发声
	 RET
Beef ENDP

将 8254 的计数器 0 设置为方式 0,计数值为十进制数 4,用单次脉冲 KK1+作为 CLK0 时钟,OUT0 连接 MIR7,每当 KK1+按动 5 次后产生中断请求,在屏幕上显示字符“count4、count3……”。

IOY0     EQU  0600H       ;IOY0是基础地址,后续各寄存器的地址通过偏移量计算得到 EQU相当于宏定义语句
A8254    EQU  IOY0+00H*2               ; 8254 计数器 0 地址
B8254    EQU  IOY0+01H*2               ; 8254 计数器 1 地址
C8254    EQU  IOY0+02H*2               ; 8254 计数器 2 地址
CON8254  EQU  IOY0+03H*2               ; 8254 控制字寄存器地址

SSTACK	SEGMENT STACK
		DW 32 DUP(?)                   ; 分配32字的栈空间
SSTACK	ENDS

CODE	SEGMENT
		ASSUME CS:CODE, SS:SSTACK
START:	MOV CX,0134H				;设开始显示4 34H为4的ASCII码
		PUSH DS
		MOV AX, 0000H
		MOV DS, AX
		MOV AX, OFFSET IRQ7			;取中断服务程序入口地址
		MOV SI, 003CH				;中断向量表地址:IRQ7 对应 0x3C 和 0x3E
		MOV [SI], AX				;填IRQ7的偏移矢量
		MOV AX, CS					;段地址
		MOV SI, 003EH
		MOV [SI], AX				;填IRQ7的段地址矢量
		CLI                         ;CLI 禁用中断,以防止配置过程中被打断。
		POP DS
		;初始化主片8259
		MOV AL, 11H             ;这里方式字设置和8.2.2相同
		OUT 20H, AL				;ICW1
		MOV AL, 08H
		OUT 21H, AL				;ICW2
		MOV AL, 04H
		OUT 21H, AL				;ICW3
		MOV AL, 01H
		OUT 21H, AL				;ICW4
		MOV AL, 6FH				;OCW1
		OUT 21H, AL
		;8254
		MOV DX, CON8254 
		MOV AL, 10H				;计数器0,方式0  0001 0000
		OUT DX, AL
		MOV DX, A8254
		MOV AL, 04H             ; 初始计数值为4(十六进制)  
		OUT DX, AL
		STI
AA1:	JMP AA1                 ;主程序进入死循环,等待中断发生
IRQ7:	CMP CX,0130H            ; 判断是否显示结束
		JL	OVER                ; 如果 CX < 0x130,则结束
		MOV DX, A8254
		MOV AL, 04H             ;方式0,需要重新加载计数器值为4
		OUT DX, AL
		MOV AX, 0143H
		INT 10H		
		MOV AX, 014FH
		INT 10H	
		MOV AX, 0155H
		INT 10H	
		MOV AX, 014EH
		INT 10H	
		MOV AX, 0154H
		INT 10H				;显示字符COUNT
		MOV AX, CX
		INT 10H	            ; 显示计数值
		MOV AX, 010DH       ;ODH 回车键
		INT 10H	
		SUB CX,1	
OVER:	MOV AL, 20H
		OUT 20H, AL				;中断结束命令
		IRET
CODE	ENDS
		END  START

8.5 并行接口8255

并行指的是接口与I/O设备或控制对象一侧的并行数据线。

8255A接口芯片有三个8位并行输入/输出端口。

方式0——基本的输入/输出方式

任何一个端口都可以作为输入或输出端口,无需设置专用的应答线,此方式下输出端口具有锁存功能,输入端口具有缓冲功能。通常用于无条件传送方式的接口电路。

利用中断IRQ10完成K0K7对LED显示电路L0L7的单次控制。

.MODEL SMALL                ;小端模式
.CODE
.STRATUP
MOV AX,CS                   ; 将代码段地址加载到 AX
MOV DS,AX                   ; 将DS设置为代码段地址,数据段寄存器 DS 指向代码段,允许访问程序中的数据
MOV DX,OFFSET INT10         ; 中断服务程序 `INT10` 的偏移地址加载到 DX
MOV AX,2572H                ; 准备设置 IRQ10 中断向量,AH=25H(DOS中断25H是设置中断向量的服务)
                            ; DL=72H 表示中断号 72H(IRQ10)                            
INT 21H                     ; 调用 DOS 中断,设置 IRQ10 的中断向量为 INT10


;配置 8259 中断控制器,解除 IRQ10 的屏蔽,使其可以响应中断请求:   
IN AL,21H                   ; 读取主片 8259 的 OCW1 寄存器(中断屏蔽寄存器)
AND AL,0FBH                 ;1111 1011 清除 AL 的第2位,开IR2中断(IRQ10 对应),解除屏蔽
OUT 21H,AL                  ; 写回主片 8259 的 OCW1
IN AL,0A1H                  ; 读取从片 8259 的 OCW1
AND AL,0FBH                 ; 清除 AL 的第 2 位(IRQ10 对应),解除屏蔽
OUT 0A1H,AL                   ; 写回从片 8259 的 OCW1,解除 IRQ10 的屏蔽

;配置8255
MOV DX,2F7H                 ;控制端口
MOV AL,90H                  ;控制字1001 0000 A组 方式0 A口输入 C口高4位输出
                            ;               B组 方式0 B口输出 C口低4位输出
OUT DX,AL                   ;将控制字写入 2F7H(控制端口),完成 8255 配置

STI                         ; 设置中断标志位,允许处理硬件中断
JMP $                       ; 无限循环,等待中断触发

;发生中断后的处理:
IN AL,21H                   ; 读取主片 8259 的 OCW1 寄存器
OR AL,04H                   ;0000 0100 设置 AL 的第 2 位,重新屏蔽 IRQ10
OUT 21H,AL                  ; 写回主片 8259 的 OCW1,屏蔽 IRQ10
STI                         ; 重新启用中断

MOV AH,4CH                  ; DOS 中断功能号 4CH,退出程序
INT 21H                     ; 调用 DOS 中断,结束程序

INT10:
MOV DX,2F4H                 ; 设置端口地址为0100 PA口 2F4H(8255 的一个端口)
IN AL,DX                    ; 从端口读取数据(键控 K0~K7 的输入状态)
MOV DX,2F5H                 ; 设置端口地址为PB口 2F5H(对应 LED 的控制端口)
OUT DX,AL                   ; 将键控数据输出到 LED,控制显示

;返回中断,发结束命令
MOV AL,20H                  ; 发送中断结束命令(EOI)
OUT 20H,AL                  ; 通知主片 8259 中断结束
OUT 0A0H,AL                 ; 通知从片 8259 中断结束
IRET                        ; 返回中断前的状态
END

8255A的C口置位/复位控制字与8255的控制字共用一个端口地址,区分标志是D7=0时,是C口控制字。

方式1——选通式输入/输出

此方式下端口A和端口B为数据传送口,端口C的某些位作为控制端口,配合AB进行输入输出。

8255B口接逻辑电平开关K0~K7,A口接LED显示电路L0~L7;基本要求:编程实现通过开关K0~K7控制对应的LED灯亮;附加要求:通过MIR7中断一次,输入一次开关的值到LED;(MIR7直接连KK1)

;1.编程实现通过开关K0~K7控制对应的LED灯亮
IOY0         EQU   0600H          ;片选IOY0对应的端口始地址
MY8255_A     EQU   IOY0+00H*2     ;8255的A口地址
MY8255_B     EQU   IOY0+01H*2     ;8255的B口地址
MY8255_C     EQU   IOY0+02H*2     ;8255的C口地址
MY8255_MODE  EQU   IOY0+03H*2     ;8255的控制寄存器地址
SSTACK	SEGMENT STACK
		DW 32 DUP(?)              ;定义了一个名为SSTACK的栈段,大小为64字节(32个双字)
SSTACK	ENDS
CODE	SEGMENT
		ASSUME CS:CODE           ;定义了一个名为CODE的代码段,并假设代码段寄存器CS指向这个代码段
;初始化8255
START:	MOV DX, MY8255_MODE      ;将控制寄存器的地址加载到DX寄存器中
		MOV AL, 82H ;1000 0010 方式0将控制字82H(表示B口输入,A口输出,基本模式)加载到AL寄存器中
		OUT DX, AL               ;通过OUT指令写入到8255的控制寄存器中
		
AA1:	MOV DX, MY8255_B         ;将B口的地址加载到DX寄存器中
		IN  AL, DX               ;从B口读取数据到AL寄存器中
		CALL DELAY               ;调用延时子程序
		MOV DX, MY8255_A         ;将A口的地址加载到DX寄存器中
		OUT DX, AL               ;将AL寄存器中的数据写到A口
		JMP AA1                  ;跳转到标签AA1处,形成无限循环 持续读开关状态并输出到LED

;延时子程序
DELAY:	PUSH CX   ;保存CX寄存器的值到栈上
		MOV CX, 0F00H   ;设置延时计数器CX的初始值
AA2:	PUSH AX
		POP  AX
		LOOP AA2   ;通过PUSH AX和POP AX指令消耗时间,每次循环CX的值减1
		POP  CX   ;恢复CX寄存器的值
		RET   ;从子程序返回
		
CODE	ENDS
		END  START


;2.通过MIR7中断一次,输入一次开关的值到LED;(MIR7直接连KK1)
IOY0         EQU   0600H          ;片选IOY0对应的端口始地址
MY8255_A     EQU   IOY0+00H*2     ;8255的A口地址,由于端口地址通常是偶数,所以乘以2
MY8255_B     EQU   IOY0+01H*2     ;8255的B口地址
MY8255_C     EQU   IOY0+02H*2     ;8255的C口地址
MY8255_MODE  EQU   IOY0+03H*2     ;8255的控制寄存器地址
STACK1 	SEGMENT STACK
        DW 256 DUP(?)  ;定义了一个名为STACK1的栈段,大小为512字节(256个双字)。
STACK1 	ENDS
CODE 	SEGMENT
        ASSUME CS:CODE             ;定义了代码段,假设代码段寄存器CS指向当前代码段
START: 	MOV DX,MY8255_MODE         ;初始化8255工作方式
       	MOV AL,86H                 ;1000 0110 B口输入,B组方式1 A口输出,A组方式0
       	OUT DX,AL       
       	MOV DX,MY8255_MODE         ;C口PC2置位 将 C 口 PC2 设置为输出。
       	MOV AL,05H                 ;0000 0101 D7=0,C口控制字 D0=1,置位
       	OUT DX,AL
        
        ;配置中断向量表
	    PUSH DS                    ; 保存当前的数据段寄存器值
		MOV AX, 0000H              ; 将0000H加载到AX寄存器,准备设置数据段寄存器
		MOV DS, AX                 ; 将AX的值(0000H)加载到数据段寄存器DS
		                           ;确保能够正确访问 中断向量表 的内容
		MOV AX, OFFSET IRQ7	       ; 取IRQ7中断服务程序的偏移地址
		MOV SI, 003CH	   ; 中断向量地址(IRQ7 对应中断类型号 0FH,向量表地址为 4*0FH=003CH)
		MOV [SI], AX               ; 将中断服务程序的偏移地址存入中断向量表
		MOV AX, CS		           ; 将代码段的段地址加载到AX
		MOV SI, 003EH              ; 设置中断向量地址段地址
		MOV [SI], AX	           ; 将代码段的段地址存入中断向量表
        CLI                        ; 禁用中断
		POP DS                     ; 恢复数据段寄存器DS的原值

		;初始化主片8259
		MOV AL, 11H
		OUT 20H, AL	  ;设置ICW1,边缘触发
		MOV AL, 0FH
		OUT 21H, AL	  ;设置ICW2,定义IRQ7对应的中断类型号为0x0F
		MOV AL, 04H
		OUT 21H, AL	  ;设置ICW3,指定IR2连接从PIC的INT引脚
		MOV AL, 01H
		OUT 21H, AL	  ;设置ICW4,全嵌套方式
		MOV AL, 6FH	  ;设置OCW1,允许IRQ4和IRQ7的中断请求
		OUT 21H, AL
		STI   
        
AA1:	NOP
		JMP AA1        ;等待中断
MIR7:  
PUSH AX                    ; 保存AX寄存器的当前值到堆栈
MOV DX, MY8255_B           ; 将8255 B口的地址加载到DX寄存器
    IN  AL, DX             ; 从DX指定的端口(8255 B口)读取数据到AL寄存器
    MOV DX, MY8255_A       ; 将8255 A口的地址加载到DX寄存器
    OUT DX, AL             ; 将AL寄存器中的数据(即B口读取的数据)输出到DX指定的端口(8255 A口)  
MOV AL, 20H                ; OCW2:结束中断处理命令,允许其他中断  0010 0000 EOI 发送结束命令
OUT 20H, AL                ; 输出到主8259的命令寄存器
POP AX                     ; 从堆栈恢复AX寄存器的原始值
IRET                       ; 中断返回指令

DELAY:
    PUSH CX                ; 保存CX寄存器的当前值到堆栈
    MOV CX, 0F00H          ; 将CX寄存器初始化为0F00H,作为延时循环的计数器初始值
AA0:
    PUSH AX                
    POP  AX                ; 通过循环保存、恢复寄存器AX的值,消耗时钟周期
    LOOP AA0          ; CX寄存器减1,如果CX不为0,则跳转到AA0标签处继续执行;否则,继续执行下一条指令
    POP CX                 ; 从堆栈恢复CX寄存器的原始值
RET                        ; 返回调用者
CODE ENDS
     END START

8.6 串行接口8251

双机通讯实验,使用两台实验装置,一台为发送机,一台为接收机,进行两机间的串行通讯,实验步骤如下:

1.按连接好电路,其中8254计数器用于产生8251的发送和接收时钟,TXD和RXD连在一起。

波特率因子若选16,计数器2初值为12。

则波特率=1.8432MHZ/12/16=9600bps

2.编程:在发送机 3000H~3009H 内存单元写入ASCII 值:30,31,32,33,34,35,36,37,38,39 共 10 个数。将这10个数发送给接收机后显示在屏幕上,收发采用查询方式。

程序流程:

  1. 初始化8254:设置8254计数器产生8251的发送和接收时钟。
  2. 复位8251:通过发送特定的命令初始化8251串口芯片。
  3. 设置8251工作模式:配置8251的工作方式和控制字。
  4. 数据发送:从发送机的内存中读取数据并通过串口发送。
  5. 数据接收与显示:接收机接收数据并在屏幕上显示。
;1.	发送端程序代码:
IOY0           EQU  0600H        ; 定义IOY0的起始地址为0600H
IOY1           EQU  0640H        ; 定义IOY1的起始地址为0640H
M8251_DATA  	EQU  IOY0+00H*2    ; 定义8251数据线端口地址为IOY0的基地址
M8251_CON	    EQU  IOY0+01H*2    ; 定义8251控制端口地址为IOY0基地址+02H
M8254_2		    EQU  IOY1+02H*2    ; 定义8254端口2地址为IOY1基地址+04H
M8254_CON	    EQU  IOY1+03H*2    ; 定义8254控制端口地址为IOY1基地址+06H

SSTACK	SEGMENT STACK
		DW 64 DUP(?)  ;定义了一个名为SSTACK的栈段,并为其分配了64个字
SSTACK	ENDS
CODE	SEGMENT
		ASSUME CS:CODE   ;定义了一个名为CODE的代码段,并假设代码段寄存器(CS)指向这个段
START:	MOV AL, 0B6H     ; 设置8254计数器2为方式3,16位读写,二进制计数  1011 0110 
		MOV DX, M8254_CON
		OUT DX, AL
		MOV AL, 0CH      ; 设置计数器2的初值为12波特率为1.8432MHZ/12/64=2400bps		MOV DX, M8254_2       低8位
		MOV DX, M8254_2  
		OUT DX, AL
		MOV AL, 00H      ;读写16位,每次写8位,分两次写,高低字节触发器  高8位
		OUT DX, AL

		CALL INIT		;复位8251
		MOV AL, 0CFH   		
        MOV DX, M8251_CON
		OUT DX, AL		;8251方式字,1100 1111 异步*64,数据长度8位,无奇偶校验位,2位停止位
		CALL DALLY
		MOV AL, 31H   
		OUT DX, AL		;8251操作命令字0011 0001 请求发送,清除错误标志位,允许发送
		CALL DALLY
        MOV DI, 3000H   ;存入ASCII值的起始单元为3000H
		MOV CX, 000AH         ;设置计数值10
		PUSH  AX
		MOV  AL,30H           ;0的ASCII码 30H
A0:     MOV [DI],AL             ;向3000~3009H单元中写数
        INC AL            ;从0加到9   
        INC DI            ;地址向后移一位
        LOOP A0
        POP AX
        
		MOV DI, 3000H      ;要发送的数据的起始地址
		MOV CX, 000AH      ;要发送10个数据,所以计数初值设为10
A1:		MOV AL, [DI]

		CALL SEND
		
		CALL DALLY
		INC DI
		LOOP A1
A2:		JMP A2

INIT:	MOV AL, 00H		   ;复位8251子程序 8251规定的
		MOV DX, M8251_CON
		OUT DX, AL
		CALL DALLY
		OUT DX, AL
		CALL DALLY
		OUT DX, AL
		CALL DALLY
		MOV AL, 40H        ;8251复位特殊规定给控制口送3个00H和1个40H
		OUT DX, AL
CALL DALLY
		RET
DALLY:	PUSH CX
		MOV CX, 3000H
A4:		PUSH AX
		POP AX
		LOOP A4
		POP CX
		RET
;数据发送子程序
SEND:   PUSH DX              ;程序要修改AX和DX所以压栈进行保护
        PUSH AX	
		MOV DX, M8251_CON
A3:		IN AL, DX       ;读状态字
		AND AL, 01H     ;判断TxRDY位,看发送器是否准备好
		JZ A3           ;如果没准备好就一直监听,如果准备好了就继续运行
		POP AX          ;AL里的值是要发送的值
		MOV DX, M8251_DATA    
		OUT DX, AL            ;out发送数据,将AL里的值
	    MOV AH,01H           ;屏幕上显示
		INT 10H
		POP DX
		RET
CODE	ENDS
		END START

;2.	接收端程序代码:
IOY0        EQU  0600H             ;IOY0起始地址
IOY1        EQU  0640H             ;IOY1起始地址
M8251_DATA    EQU  IOY0+00H*2      ;8251的数据线用来读写数据,C/D非连接A1 
M8251_CON	    EQU  IOY0+01H*2    ;8251的控制端,C/D非取1
M8254_2		    EQU  IOY1+02H*2    ;写计数器2,因为试验箱内部是out2连接时钟的
M8254_CON	    EQU  IOY1+03H*2    ;写控制器

SSTACK	SEGMENT STACK
	DW 64 DUP(?)
SSTACK	ENDS
CODE	SEGMENT
	ASSUME CS:CODE
	;8254初始化
START:	MOV AL, 0B6H    ;10110110,计数器2,读写16位,方式3,2进制计数
		MOV DX, M8254_CON
		OUT DX, AL
		MOV AL, 0CH      ;给计数器2赋初值12
		MOV DX, M8254_2
		OUT DX, AL
		MOV AL, 00H      ;读写16位,每次写8位,分两次写,高低字节触法器
		OUT DX, AL
		
		CALL INIT				;复位8251
	MOV AL, 0CFH        ;1100 1111 异步*64,数据长度8位,无奇偶,2位停止位
	MOV DX, M8251_CON   ;设置8251方式字
	OUT DX, AL 
	CALL DALLY
	MOV AL, 16H
	OUT DX, AL          ;设置8251操作命令字 0001 0110 允许接受
	CALL DALLY
	MOV AX, 0152H	    ;输出显示字符R表示READY
	INT 10H
	MOV DI, 3000H
	MOV CX, 000BH
A1:	    MOV DX,M8251_CON
        IN AL, DX
        AND AL, 02H            ;测试RxRDY,若接收器没准备好则继续监听
        JZ A1 ;没准备好
        MOV DX, M8251_DATA
        IN AL, DX              ;输入接收的数据
        AND AL, 7FH            ;最高位清零,保留低七位(ASCII码7位)
        MOV [DI],AL
        INC DI
        LOOP A1
        MOV AL, 00H
        MOV SI, 300BH
        MOV [SI], AL
        MOV AH, 06H       		;指明需要输出字符串
        MOV BX, 3000H     		;首地址
        INT 10H					;输出显示接收到的数据
A2:		JMP A2
INIT:	MOV AL, 00H				;复位8251子程序
		MOV DX, M8251_CON
		OUT DX, AL
		CALL DALLY
		OUT DX, AL
		CALL DALLY
		OUT DX, AL
		CALL DALLY
		MOV AL, 40H       ;8251复位特殊规定给控制口送3个00H和1个40H
		OUT DX, AL
CALL DALLY
		RET
DALLY:	PUSH CX
	    MOV CX, 3000H
A3:	    PUSH AX
		POP AX
		LOOP A3
		POP CX
		RET
CODE	ENDS
END START

第9章 键盘与显示接口

9.1 键盘接口

  1. 键盘分类:
  • 编码键盘:能自动检测被按下的键,并提供对应的编码。使用方便,接口简单但价格较贵。
  • 非编码键盘:只能提供行列矩阵的键盘,而按键的识别和键值的确定、输入灯工作全靠软件完成。
  1. 键盘接口基本功能:
  • 去抖动

每个键在按下和松开时,都会经历短时间的抖动才能达到稳定接通或者断开,因此在脉冲的开头和尾部总要出现毛齿波,必须避免这段不稳定的抖动状态。有两种方法:

软件延时法:发现有键按下或者释放时,软件延时一段时间再去检测。

**硬件消抖法:**在键开关与微型机接口之间加一个消抖动电路。

在键数较多时,大多采用软件延时法。

  • 防串键

双键锁定:只要检测到有两个或者两个以上的按键被按下,就不考虑从键盘读键码,只把最后释放的键看作正确的被按键。

N键连锁:只考虑按下一个键的情况,当一个键被按下时,在此键未被完全释放前,对其他键不予理会。实现简单,较为常用。

  • 按键识别及键码产生

(1) 行扫描法

向所有行输出低电平,若无键按下闭合,则+5V电压经电阻R使所有列的输出均为高电平。若有一个按键按下闭合,就会将所在列钳位到低电平。读列线,可判断有无按键按下。

接下来识别哪一个按键按下,并查找该键编码。

**逐行扫描法:**首先向第0行输出低电平,其余输出高电平,如果这时从列线读取的值全为1(全部为高电平),说明按下去的键不在第0行,以此类推,当某一行输出低电平而列线读得某一列也为低电平时即可确定按下闭合键的位置,同时确定了键码(由行号和列号组成)。例如三行四列的按键键码可表示为0010 0100。(行列码,位置扫描码)

以8*8矩阵键盘为例,两个端口地址分别为OutPort和InPort。

Lp1: MOV AL,00H
     MOV DX,OutPort
     OUT DX,AL                ;行寄存器输出全0
     MOV DX,Inport            
     IN  AL,DX                ;读列缓冲器
     CMP AL,0FFH              ;1111 1111 判断有无按键按下
     JZ  Lp1                  ;如果相等,列全为高电平,则无按键按下,继续扫描
     CALL Dly                 ;不相等,有按键按下,延时20ms去抖动
     MOV AH,0FEH              ;设置扫描代码 1111 1110   第一行设置低电平,其余均为高电平
     MOV SI,8                  
Lp2: MOV AL,AH                ;取扫描代码
     MOV DX,OutPort
     OUT DX,AL                ;输出扫描代码  第一行设置低电平,其余均为高电平
     RCL AL,1                 ;带进位的循环左移1位 1111 1101 改变扫描代码
     MOV AH,AL                ;保存扫描代码
     MOV DX,InPort
     IN  AL,DX                ;读列缓冲器
     CMP AL,0FFH
     JNZ LP3                  ;当某一列为0时转移(即该行按键按下)
     DEC SI
     JNZ Lp2                  ;循环读取8行
     JMP Lp6
Lp3: LEA BX,Table             ;取键码表的首地址给BX
     MOV CX,n                 ;取键的个数给CX
Lp4: CMP AX,[BX]              ;查表
     JZ  Lp5                  ;找到键码转到Lp5
     INC BX
     INC BX
     LOOP Lp4
     JMP  Lp1
Lp5: MOV DX,Inport
     IN  AL,DX
     CMP AL,0FFH
     JNZ Lp5                  ;判断按键是否释放
     CALL Dly
     CALL Key                 ;调键盘命令处理程序
Lp6: RET
Table DW...,键码表

(2) 行反转法

要求行线和列线连接的接口,CPU即可写又可读,使用8255的PA口和PB口分别作为行线和列线的输入输出口。

设置PA口输出,PB口输入,向PA口写入全0,即输出全低电平,然后读B口,若PB口读入全为1,说明没有按键按下,若PB2读入0,其余全为1,则第二列有键按下。(输入0000 读1101)

设置PA口输入,PB收输出。将第一步由PB口读入的数据再由PB口输出,这时读PA口,若PA1读入为0,其余全为1,则说明按下的键在第一行。(输入1101 读1011)

(3) 行列扫描法

通过计数译码,使各行依次输出低电平,其余全为高电平。在扫描每一行时,读列线,若读得结果全为1,则该行没有按键按下,若某一列为低电平,则该行有按键按下,且行号和列号即可确定。

然后使用同样的方法,依次向列线扫描输出,读行线,如果两次行号和列号相同,则确定无疑。该方法使用最多。

9.2 LED显示接口

内部发光二极管连接方法:共阴极(1亮)、共阳极(0亮)。

  1. 单个LED的显示接口

一个LED显示器接口只需要在8段LED显示器与微处理器之间加一个8位锁存器即可。为了显示某个字符,只需要利用OUT指令将该字符对应的段码送到输出端口(90H)即可。例如共阳极显示3:

MOV AL,0B0H
OUT 90H,AL
  1. 多个LED的显示接口

采用动态扫描、分时循环显示的方法,利用人眼视觉滞留效应。

8位显示器从左往右显示2005.05.

        MOV DI,OFFSET Dismem         ;指向显示缓冲区首地址
        MOV CL,80H                   ;指向左端LED显示器 1000 0000
        MOV AL,00H                   ;将00送位码锁存器,关显示
        OUT ProtB,AL
Disp:   MOV AL,[DI]                  ;取要显示的字符
        MOV BX,OFFSET Sgept
        XLAT        	;用于查表转换的指令,它允许程序员通过一个表来转换一个字节的数据
                    	;这个指令的功能是将DS: [BX+AL]指向的字节单元的内容送给AL寄存器。
        OUT ProtA,AL	;输出段码
        MOV AL,CL
        OUT ProtB,AL	;B口输出控制最左边显示
        PUSH CX
        MOV CX,50H       ;延时一定时间
Delay:  LOOP Delay
        POP CX
        CMP CL,01       ;显示到最右端了吗
        JZ  Desend
        INC DI          ;没有到最右端,取下一个要显示的字符
        SHR CL,1        ;位码右移一位,指向下一个数位
        JMP Disp
Disend: RET
Segpt:  DB 0C0H,0F9H,0A4H,0B0H,99H,92H,82H,0F8H,80H
        DB 90H,88H,83H,0C6H,0A1H,86H,8EH,7FH
Dismem: DB 02H,00H,00H,05H,10H,00H,05H,10H

第10章 D/A、A/D转换接口

10.1 D/A转换

数模转换:数字量输入的位数有8、12、16位,输出的模拟量有电流和电压。

  1. 分辨率

分辨率是指D/A转换器所能分辨的最小电压。分辨率高低用二进制输入量的位数来表示,例如分辨率是8位、10位、12位等。

也可用最小输出电压与最大输出电压之比的百分数表示,例如8位D/A转换器,其分辨率:
1 2 8 − 1 ≈ 0.392 % \frac{1}{2^8-1}\approx0.392\% 28−11​≈0.392%
12位D/A转换器,其分辨率:
1 2 12 − 1 ≈ 0.024 % \frac{1}{2^{12}-1}\approx0.024\% 212−11​≈0.024%
当满量程输出电压为5V时,一个12位D/A转换器能分辨的电压为 5 ∗ 0.024 % ≈ 1.2 m V 5*0.024\%\approx1.2mV 5∗0.024%≈1.2mV

分辨率越高,对应数字输入信号最低位的模拟输入信号电压的数值越小,越灵敏。

  1. 转换精度

是指D/A转换器实际输出电压与理论值之间的误差:
± 1 2 L S B = ± 1 2 × 1 2 n V ± 1 2 n + 1 V   \pm\frac{1}{2}LSB=\pm\frac{1}{2}×\frac{1}{2^{n}}V\pm\frac{1}{2^{n+1}}V\ ±21​LSB=±21​×2n1​V±2n+11​V 

  1. 温度灵敏度
  2. 建立时间
  3. 输出

设DAC0832端口号为2F7H,采用单锁存方式,输出产生三角波,三角波最高电压为5V,最低电压为0V。

对应输出数据为00H~FFH,驱动程序如下:

	MOV AL,00H
	MOV DX,2F7H
L1: OUT DX,AL
	INC AL
	CMP AL,FFH
	JNZ L1
L2: OUT DX,AL
	DEC AL
	CMP AL,00H
	JNZ L2
	JMP L1

编写程序实现数字信号到模拟信号的转换,要求产生三角波。

; 定义堆栈段
SSTACK SEGMENT STACK
    DW 32 DUP(?)  ; 分配32个字的空间初始化堆栈
SSTACK ENDS
 
; 定义代码段
CODE SEGMENT
ASSUME CS: CODE, SS: SSTACK  ; 
START:  ; 
    MOV AL, 00H  ; 将0作为锯齿波的起始值
    MOV DX, 600  ; 将输出端口地址加载到DX
 
L1:  ; 锯齿波上升段
    OUT DX, AL   ; 生成当前电平的锯齿波
    CALL DELAY   ; 延时
    CALL DELAY
    CALL DELAY
    CALL DELAY
    CALL DELAY
    INC AL       ; AL值加1
    CMP AL, 0FFH ; AL是否等于255,即锯齿波的峰值
    JNZ L1       ; 不等于则继续循环,生成上升段的锯齿波

L2:  ; 锯齿波下降段
    OUT DX, AL   ; 生成当前电平的锯齿波
    CALL DELAY   ; 延时
    CALL DELAY
    CALL DELAY
    CALL DELAY
    CALL DELAY
    DEC AL       ; AL值减1
    CMP AL, 00H ; AL是否等于0
    JNZ L2      ; 不等于则继续循环,生成下降段的锯齿波

; 延时程序
DELAY:
    PUSH CX      ; 保存CX值
    MOV CX, 003FH ; 将003FH作为循环计数器的初始值
L3:  ; 
    PUSH AX      ; 保存AX值
    POP AX       ; 恢复AX值
    LOOP L3      ;CX不为0,则继续循环
    POP CX       ; 恢复CX值
    RET          
CODE ENDS  ; 
END START  ; 

编写程序实现数字信号到模拟信号的转换,要求产生锯齿波。

IOY0        EQU   0600H 
DA0832      EQU   IOY0+00H*2	
STACK	SEGMENT STACK
DW 32 DUP(?)
STACK	ENDS
CODE	SEGMENT
ASSUME	CS:CODE, SS:STACK
START:	MOV AX, 0000H
	MOV DX, DA0832
A1:	OUT DX, AL
	CALL DELAY
	INC AL
	JMP A1			   
DELAY:	PUSH CX
	MOV CX, 03FFH
A2:	PUSH AX	
	POP  AX
	LOOP A2
	POP CX
	RET  
CODE	ENDS
END STAR

10.2 A/D转换

  1. 分辨率
  2. 转换时间
  3. 量程
  4. 精度

ADC0809芯片在时钟频率为500kHz的情况下,转换时间为128us。采用软件延时法,分别对8路模拟信号轮流采样一次,并依次将转换结果转存到数据存储区Data开始的内存中,无条件输入输出方式。

	MOV AX,SEG Data  ;SEG获得Data段地址
	MOV DS,AX
	LEA DI,Data      ;将Data的地址值赋给DI
	MOV CX,08H       ;循环8次
	MOV AH,00H       ;初始通道号
	MOV DX,2F7H
AD: MOV AL,AH
	OUT DX,AL        ;启动A/D转换
    CALL Delay       ;至少延时128us
    IN  AL,DX        ;读取转换结果
    MOV [DI],AL      ;存入内存
    INC AH           ;指向下一个通道
    INC DI           ;下一个数据区
    LOOP AD          ;循环八次
    ...

ADC0809单元,编程采集IN0输入的电压。

IOYO   EQU 0600H        ; AD控制端口的基地址
AD0809 EQU IOYO+00H*2   ; AD端口地址0600H
; 定义堆栈段
SSTACK SEGMENT STACK
       DW 64 DUP(?)     ;分配64个字的空间
SSTACK ENDS
PUBLIC VALUE            ; 全局变量VALUE用于存储AD转换的结果
; 定义数据段
DATA SEGMENT
VALUE DB ?
DATA ENDS
; 定义代码段
CODE SEGMENT
        ASSUME CS:CODE,DS:DATA 
START:
       MOV AX,DATA
       MOV DS,AX
       mov AL,00H             ; 采集IN0电压
       MOV DX,AD0809          ; 将AD端口地址加载到DX
       OUT DX,AL              ; 将AL值输出到AD端口
       CALL DALLY       	  ; 延时
       IN AL,DX               ; 从AD端口读取转换结果到AL
       MOV VALUE,AL           ; 将读取结果存储到VALUE
       JMP START              ; 设置断点,观察VALUE值
; 延时程序
DALLY: 
       PUSH CX                ; 保存CX值
       PUSH AX                ; 保存AX值
       MOV CX,100H            ; 循环计数器CX初值100H
A5:    MOV AX,0800H
A6:    DEC AX                   ;不断递减AX值来消耗时间
       JNZ A6                   ; AX不为0,则继续递减
       LOOP A5
       POP AX       ; 恢复AX值
       POP CX       ; 恢复CX值
       RET
CODE ENDS
      END START

标签:接口技术,中断,MOV,AL,Win32,微机,DX,AX,OUT
From: https://blog.csdn.net/Zuming12138/article/details/144915383

相关文章

  • Win32汇编学习笔记02.RadAsm和联合编译
    https://bpsend.net/thread-151-1-1.html汇编使用资源汇编使用资源的方式和C的一样,也是把资源文件rc编译成 res 再链接进去,汇编没有自己的资源编辑器,需要借助vc6.0或者vs主要是把头文件.h转化为对应的.inc使用vc6.0建立资源文件用vs建立资源文件......
  • win32汇编环境,对话框中设置RichEdit内文本的字体
    ;运行效果;win32汇编环境,对话框中设置RichEdit内文本的字体;直接抄进RadAsm可编译运行。;下面为asm文件;>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>......
  • win32汇编环境,窗口程序中设置RichEdit内文本的字体
    ;运行效果;>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>......
  • Win32汇编学习笔记01.环境配置
    Win32汇编学习笔记01.环境配置-C/C++基础-断点社区-专业的老牌游戏安全技术交流社区-BpSend.net环境配置masm32下载官网:http://www.masm32.com/安装成功标志环境配置:将masm32下的bin目录添加到path新建include,将masm32目录下的inclcude目录添加进去新建lib,将mas......
  • 【Python项目】用pywin32在聊天窗口发送QQ好友/群消息
    源码中涉及到的函数用法:win32clipboard.SetClipboardData(format,hMem):它需要一个格式化的数据对象和一个数据块。CF_DIB——DIB图片,它包含一个BITMAPINFO结构,然后是位图位。CF_DIF——软件领域的数据交换格式。CF_PALETTE——调色板。每当应用程序放置数据在剪贴板......
  • 在C#中,使用 Stopwatch 比较简单粗糙的替代 WIN32 下 C++ 中调用的 QueryPerformanceCo
    C#中自带的那个CTimer看上去是通过消息事件方式的,精度上好像小于10ms就不行了。于是找了半天网络,有的方式是引用kernel32.dll的库,然后就可以在C#中调用 QueryPerformanceCounter。感觉是不那么优雅。最后居然发现这个Stopwatch。真的像一个计时器一样,按一下,开始【Sto......
  • Get-WmiObject -Class Win32_SystemEnclosure -Namespace "root\CIMV2" | Select-Obj
    Get-WmiObject-ClassWin32_SystemEnclosure-Namespace"root\CIMV2"|Select-ObjectChassisTypes这条PowerShell命令用于查询计算机的硬件外壳(Chassis)信息,特别是返回系统机箱类型(ChassisTypes)。解释命令的组成部分:Get-WmiObject:这是一个用于查询WindowsManagement......
  • C# 使用CliWrap库 报错 System.ComponentModel.Win32Exception (0x80004005):目录名称
    System.ComponentModel.Win32Exception(0x80004005):目录名称无效。开发环境不报错,正式环境报错可能的原因使用了.WithWorkingDirectory,指定了不存在的工作目录varresult=awaitCli.Wrap(JFlashExeFilePath).WithArguments(args=>{......
  • 数据结构:Win32 API详解
    目录一.Win32API的介绍二.控制台程序(Console)与COORD1..控制台程序(Console):2.控制台窗口坐标COORD:3.GetStdHandle函数:(1)语法:(2)参数:4.GetConsoleCursorInfo函数:(1)语法:(2)参数:(3)CONSOLE_CURSOR_INFO结构体:5.SetConsoleCursorInfo函数:实例:6.SetConsoleCursorPosition......
  • anydesk连接提示错误win32_10061
    前言全局说明一、说明1.1环境:Windows11家庭版23H222631.3737二、错误提示2.1win32_10061解决方法:方法1:尝试开启防火墙后,添加anydesk到允许的防火墙列表中方法2:使用anydesk自带的安装,安装后,即可连接免责声明:本号所涉及内容仅供安全研究与教学使用,如......