PC机键盘的处理过程(外中断-键盘输入)

我们知道cpu在计算机系统中除了能够执行指令,进行运算以外,还应该能对外部设备进行控制,接收它们的输入,向它们进行输出,也就是说cpu除了具有运算能力外,还要有I/O(Input/Output,输入输出)能力。通过以前的学习我们知道,PC系统的接口卡和主板上装有各种接口芯片。这些外设接口芯片的内部有若干寄存器,cpu将这些寄存器当作端口来访问。

外设的输入不直接送入内存和cpu,而是送入相关的接口芯片的端口中,cpu向外设的输出也不是直接送入外设,而是先送入端口中,再由相关的芯片送到外设。cpu还可以向外设输出控制命令,而这些控制命令也是先送到相关芯片的端口中,然后再由相关的芯片根据命令对外设实施控制。可见cpu通过端口和外部设备进行联系。

至此,我们知道了外设的输入被存放在端口中,可是外设的输入随时都有可能到达,cpu如何及时的知道并进行处理呢?这就用到了我们今天的主角-中断机制。cpu提供中断机制来满足这种需要,当cpu的内部有需要处理的事情发生时,将产生中断信息,引发中断过程,这种中断信息来自cpu内部,还有一种中断信息,来自于cpu外部,当cpu外部有需要处理的事情发生的时候,比如说,外设的输入到达,相关芯片将向cpu发出相应的中断信息。cpu在执行完当前指令后,可以检测到发送过来的中断信息,引发中断过程,处理外设的输入。

PC机处理键盘输入的过程如下:

1、键盘输入

键盘上的每一个按键相当于一个开关,键盘中有一个芯片对键盘上的每一个键的开关状态进行扫描,按下一个键时,开关接通,
该芯片就产生一个扫描码,扫描码说明了按下的键在键盘上的位置。扫描码被送入主板上的相关芯片的寄存器中,该寄存器的
端口地址为60h。松开按下的键时,也产生一个扫描码, 扫描码说明了松开的键在键盘上的位置,松开按键时产生的扫描码也
被送入60端口中。一般我们将按下一个键时产生的扫描码称为通码,松开一个按键产生的扫描码称为断码。扫描码长度为一个
字节,通码的第七位为0,断码的第七位为1,即断码 = 通码 + 80h。

2、引发9号中断

键盘的输入到达60端口时,相关的芯片就会向cpu发出中断类型码为9的可屏蔽中断信息。cpu检测到改中断信息后,如果IF = 1
,则响应中断,引发中断过程,转去执行int 9中断例程。

3、执行int9中断例程

BIOS提供了int 9中断例程,用来进行基本的键盘输入处理,主要的动作如下:
(1)读出60端口中的扫描码;
(2)如果是字符键的扫描码,将该扫描码和它所对应的字符码即ASCII码送入内存中的BIOS键盘缓冲区;如果是控制键(如ctrl)
和切换键(如CapsLock)的扫描码,则将其转变为状态字节(用二进制位记录控制键和切换键状态的字节)写入内存中存储 状态字节的单元;
(3)对键盘系统进行相关的控制,比如说,向相关芯片发出应答信息。
BIOS键盘缓冲区是系统启动后,BIOS用于存放int 9号中断例程所接受的键盘输入的内存区。该内存区可以存储15个键盘输入,因为int 9中断例程除了接受扫描码外,还要产生和扫描码对应的字符码,所以在BIOS键盘缓冲区中,一个键盘输入用一个字单元存放,高位字节存放扫描码,低位字节存放字符码。
0040:17单元存储键盘状态字节,该字节记录了控制键和切换键的状态。键盘状态字节各位记录的信息如下:

由以上内容我们知道,键盘输入的处理过程:
1、键盘上的芯片产生扫描码,2、扫描码被送入60端口,3、引导9号中断;4、cpu执行int9中断例程处理键盘输入
在上面的过程中,前3步都是由硬件系统完成的。我们能改变的只有int 9中断处理程序。我们可以重新编写int9中断例程。按照
自己的意图来处理键盘的输入,因为一个完整的键盘输入的处理要涉及一些硬件细节,针对这种情况,我们可以在自己编写的中
断例程中调用BIOS的int9中断例程帮我们处理。

编程:在屏幕中间依次显示"a"~“z”,并可以让人看清。在显示的过程中,按下Esc键后,改变显示的颜色。
依次显示"a"~"z"的代码如下:

assume cs:code
code segment
	start:mov ax, 0b800h
		  mov es, ax
		  mov ah, 'a'
	  s1: mov es:[160*12+40*2], ah
		  inc ah
		  cmp ah, 'z'
		  jna s1
		  mov ax, 4c00h
		  int 21h
code ends
end start 

在上面代码的执行过程中,我们无法看清屏幕上字符的变化,因为cpu执行太快导致字符切换过快,因此我们想办法在每显示一个
字符后延时一段时间,让人看清后再显示下一个字符,这里我们实现一个延时的子程序供主程序调用,代码如下:

   delay: push ax
		  push dx
		  
		  mov ax, 0
		  mov dx, 1000h	;//cpu循环执行10000 000h次
	   s2:sub ax, 1
		  sbb dx, 0
		  cmp ax, 0
		  jne s2
		  cmp dx, 0
		  jne s2
		  pop dx
		  pop ax
		  ret

那如何改变颜色呢?
键盘输入到达端口后,就会引发9号中断,cpu转去执行int9中断例程。我们可以编写int9中断例程,功能如下:
(1)从60端口中读出键盘的输入;
(2)调用BIOS的int9中断例程,处理其他硬件细节;
(3)判断按键是否为Esc的扫描码,如果是,则改变显示的颜色后返回;如果不是则直接返回。
下面我们分步实现以上步骤

1、从60端口中读出键盘的输入
in al, 60h
2、调用BIOS的int9中断例程

有一点我们要注意,在我们编写好int9中断例程后,主程序必须将中断向量表中的int9中断例程的入口地址改为我们新写的中断
处理程序的入口地址,但是在新的中断处理程序中调用原来的int9中断例程时,中断向量表中的int9号中断例程的入口地址已不
是原来的int9中断例程的地址,所以我们不能使用int指令直接调用。
要能在我们新写的中断例程中调用原来的中断例程,就必须在将中断向量表中的中断例程的入口地址改为新地址之前,将原来的
入口地址保存起来,这样在需要调用的时候才能找到。

保存了入口地址后,该如何调用呢?
当然不能使用指令int9来调用,我们可以使用别的指令来对int指令进行模拟,从而实现对中断例程的调用。
我们知道,int指令在执行的时候,cpu进行下面的工作。
(1)取中断类型码n;
(2)标志寄存器入栈
(3)IF=0,TF=0;
(4)CS、IP入栈
(5)(IP)=(n4), (CS=n4+2)
取中断类型码是为了定位中断例程的入口地址,在我们自己写的中断例程中已知道地址,因此不需要做第一步,假设要调用的中断
例程的入口地址被保存在ds:0和ds:2单元,我们将用以下步骤来模拟int过程
(1)标志寄存器入栈
(2)IF=0,TF=0;
(3)CS、IP入栈
(4)(IP)=(ds16+0), (CS=ds16+2)
注意到第三步和第四步的功能和call dword ptr ds:[0]的功能一样,call dword ptr ds:[0]的功能也是
push cs, push ip, (IP)=(ds16+0), (CS=ds16+2),所以int的模拟过程变为:
(1)标志寄存器入栈
(2)IF=0,TF=0
(3)call dword ptr ds:[0]
对于上面步骤一可用pushf实现。对于步骤二可用下面的指令来实现

pushf
pop bx
and bh, 11111100B
push bx
popf	;IF=0, TF=0
;CS, IP入栈,IP = n*4, CS = n*4+2
call dword ptr ds:[0]	;对int指令进行模拟,调用原来的int 9中断例程
3、如果是Esc按键的扫描码,改变显示的颜色后返回

如何改变现实的颜色呢?
显示的位置是在屏幕的中间,即第12行40列,显存中的偏移地址为:16012+402。所以字符的ASCII码要送入段地址b800h,偏移
地址16012+402处。而段地址b800h偏移地址16012+402+1处时字符的属性,我们只要改变此处的数据就可以改变显示字符的颜色
该程序的最后一个问题是,要在程序返回前,将中断向量表中的int9号中断例程的入口地址恢复为原来的地址。否则程序返回后,
别的程序将无法使用键盘。
经以上分析,完整源码如下:

assume cs:code

stack segment
	db 128 dup (0)
stack ends

data segment
	dw 0, 0
data ends

code segment
	start:mov ax, stack
		  mov ss, ax
		  mov sp, 128
		  
		  mov ax, data
		  mov ds, ax
		  
		  ;保存原有int 9号中断处理程序的入口地址
		  mov ax, 0
		  mov es, ax
		  push es:[9*4]
		  pop ds:[0]
		  push es:[9*4+2]
		  pop ds:[2]
		  
		  mov word ptr es:[9*4], offset int9
		  mov es:[9*4+2], cs
		  
		  mov ax, 0b800h
		  mov es, ax
		  mov ah, 'a'
	  s1: mov es:[160*12+40*2], ah
		  call delay
		  inc ah
		  cmp ah, 'z'
		  jna s1
		  
		  ;为了不影响后续的键盘输入处理,需要将int 9号中断处理程序的入口地址恢复为原来的地址
		  push ds:[0]
		  pop es:[9*4]
		  push ds:[2]
		  pop es:[9*4+2]
		  mov ax, 4c00h
		  int 21h
		  
		  
	delay:push ax
		  push dx
		  
		  mov ax, 0
		  mov dx, 1000h	;//cpu循环执行10000 000h次
	   s2:sub ax, 1
		  sbb dx, 0
		  cmp ax, 0
		  jne s2
		  cmp dx, 0
		  jne s2
		  pop dx
		  pop ax
		  ret
		  
	;-----------新的int 9中断例程-------------	  
	int9: push ax
		  push bx
		  push es
		  
		  in al, 60h	;从端口60h读出键盘的输入
		  pushf	;标志寄存器入栈
		  
		  ;设置IF = 0, TF = 0
		  pushf
		  pop bx
		  and bh, 11111100B
		  push bx
		  popf
		  ;CS, IP入栈,IP = n*4, CS = n*4+2
		  call dword ptr ds:[0]	;对int指令进行模拟,调用原来的int 9中断例程
		  
		  cmp al, 1	;esc键的扫描码为1
		  jne int9ret
		  
		  mov ax, 0b800h
		  mov es, ax
		  inc byte ptr es:[160*12+40*2 + 1]	;属性值加1改变颜色
		  
		  
  int9ret:pop es
		  pop bx
		  pop ax
		  iret
code ends
end start 

程序运行图示如下:
在这里插入图片描述
注意,本文中所有关于键盘的程序,因为要直接访问真实的硬件,则必须在DOS实模式下运行。在Windows2000的DOS方式下运行,
会出现一些和硬件工作原理不符合的现象。

  • 9
    点赞
  • 24
    收藏
    觉得还不错? 一键收藏
  • 3
    评论
看了这本书你就不会觉得汇编很难了.难得一见的好书,可以做收藏用,看完本书,汇编也就学会了 本书 第1章 汇编语言基础知识      1  1.1 汇编语言的特点       1  1.2 汇编语言中的数       2  1.3 数学运算和逻辑操作     3  1.4 汇编语言中的文字和符号   6  1.5 数据的存储         8  1.6 寄存器           9   5.1.3 通过地址表传递参数    88  5.2 特殊的过程调用        91  5.3 数据串处理          97 第6章 文字输出与键盘输入      116  6.1 基础知识           116   6.1.1 认识显示系统       116   6.1.2 显示卡的类别       117 第2章 开始设计程序        11  2.1 如何发出声音        11   2.1.1 喇叭的构造       11   2.1.2 汇编伴侣--DEBUG.EXE   11   2.1.3 细看PC机        12  2.2 编制第一个程序       14   2.2.1 程序的输入和保存    14   2.2.2 程序透析        16   2.2.3 程序的缺点       20  2.3 回到DOS的怀抱        20  2.4 其它的改进方法       20   6.1.3 文字与图形的构成     117   6.1.4 显示模式         119  6.2 Video BIOS的应用       120  6.3 直接操作Video RAM       141  6.4 端口编程           152  6.5 键盘输入           159 第7章 文件控制块          168  7.1 文件操作基础         168   7.1.1 文件的概念        168   7.1.2 操作系统         168   7.1.3 处理的对象        168 第3章 中断调用与子程序      27  3.1 中断的概念和处理过程    27   3.1.1 中断的基本概念     27   3.1.2 中断处理过程     28  3.2 几个常用的软件中断     30  3.3 子程序           36  3.4 细致地咀嚼         41   7.1.4 两种方法         168   7.1.5 文本与二进制       169   7.1.6 文件的处理步骤      169  7.2 文件控制块          170   7.2.1 文件控制块的结构     170   7.2.2 文件的属性        170   7.2.3 驱动器号         171 第4章 奇妙的声音         45  4.1 控制定器         45   4.1.1 定电路        45   4.1.2 编制源程序       47  4.2 精确定          57  4.3 数字的表示         66  4.4 更多的技术         68   7.2.4 记录块与记录       171  7.3 FCB记录的随机存取       192   7.3.1 随机读写         192   7.3.2 随机块读写功能      195  7.4 FCB完结篇           198 第8章 文件句柄功能与磁盘      207  8.1 文件句柄功能调用       207 第5章 子过程和串处理       80  5.1 子过程参数的传递      80   5.1.1 通过寄存器传送数据   80   5.1.2 通过堆栈传递参数    83   8.1.1 文件的存取        211   8.1.2 一些辅助功能       218  8.2 细看磁盘           227 第9章 图形显示           238
xxxxxx大学 课 程 设 计 报 告 课程设计名称:单片机系统综合课程设计 课程设计题目:单片机与PC机串行通讯设计 院(系): 专 业: 班 级: 学 号: 姓 名: 指导教师: 完成日期: 目 录 第1章 总体设计方案………………………………………………………………1 1.1 课程设计的内容和要求………………………………………………………..1 1.2 课程设计原理…………………………………………………………………..1 1.3 课程设计思路…………………………………………………………………..2 1.4 实验环境………………………………………………………………………..2 第2章 详细设计方案………………………………………………………………3 2.1 实现方法………………………………………………………………………..3 2.2 模块设计………………………………………………………………………..3 2.2.1主函数流程图……………………………………………………………...3 2.2.2功能1模块………………………………………………………………...4 2.3 程序连线图……………………………………………………………………..7 第3章 调试及结果分析……………………………………………………………8 3.1 调试步骤及方法………………………………………………………………..8 3.2 实验结果及分析………………………………………………………………..8 参考文献………………………………………………………………………………9 附 录A(源程序)………………………………………………………………10 附 录B(原理图)………………………………………………………………13 附 录C(元器件清单)…………………………………………………………14 第1章 总体设计方案 1.1 课程设计的内容和要求 (1)、课程设计内容: 利用南京伟福公司的LAB6000通用微控制器仿真实验系统开发单片机与PC机串行通讯 系统,实现单片机与PC机的通讯。要求实现数据收发功能。具要求容如下: .通过LAB6000的小键盘选择功能; .功能1:接收PC机发送的数据,并将其回发给PC机; .功能2:从LAB6000的小键盘输入数据,发送给PC机。 (2)、课程设计要求: .认真完成课程设计任务; .通过老师现场验收; .交出完整的课程设计报告。 1.2 课程设计原理 根据题目要求,要完成LAB6000的小键盘选择,接收并且回发数据,小键盘可以输入 并发送数据等功能,在本次设计中主要使用了MCS- 51芯片以及部分连线来完成串行通讯的模拟。 采用MCS- 51芯片实现不同模式串行通讯,通过延程序控制串行通讯的频率。将键盘选择功能、 PC机发送及接收数据功能、小键盘发送功能等通过不同的子程序实现,然后在程序中调 用不同的子程序来实现不同的功能。 因为需要实现小键盘输入数据,所以必须实现键盘扫描功能及按键检测功能。按键的 扫描方法有扫描法和线反转法,因为小键盘按键不是非常多,所以本设计采用行列扫描 法来进行小键盘扫描。 3 课程设计思路 (1).提出方案 设计键盘扫描函数函数和按键检测函数,通过MCS- 51芯片,实现键盘扫描。通过小键盘设置17个键位选择键,输入"1"在LED灯显示并选 择功能1(接收和发送PC机发送的数据);输入"2"在LED灯显示并选择功能2(从小键 盘输入数据并发送给PC机)。 小键盘选择功能:首先设置定器及波特率,然后再设计键盘扫描函数和按键检测函 数,使之能实现小键盘选择功能。输入不同的数实现调用不同的函数,从而实现不同的 功能。 功能1:设计PC发送函数,此功能还需要有串行口中断处理函数。目的是实现单片机 可以接受PC机发送的数据,并将其回发给PC机。设置一个返回键,如按键"1"返回小键 盘选择功能,可以重新选择功能1还是功能2。 功能2:设计小键盘发送程序,需调用键盘扫描函数及按键检测函数,若有键按下, 将键值发送到LED灯上,并将其转为ASCII码发送回PC机。同样设置一返回键,如按键"N EXT"(右数第一列,下数第一行)返回小键盘选择功能。使函数能循环使用。 (2).方案论证 从实际应用的角度,用户可以任意选择事先设计好的2种状态,当选择完一种状态 ,它可以无限的按照这种方案循环的运行下去,在此过程中,用户可以选择退出这种工 作状态,从而实现对当前进行状态的控制,即可以令程序重新开始。在整个串行口输入 过程中可随做出不同的输入方式,对输入数据的工作状态进行控制。 1.4 实验环境 ·硬件环境:MCS-51实验箱,PC机。 ·软件环境:WAVE2000应用软件 第2章 详细设计方案 2.1 实现方法 开始运行程
这是一个比较复杂的问题,需要涉及到单片机的编程和串口通信的知识。我简单介绍一下实现的思路和步骤。 1. 硬件连接 首先需要将单片机的串口与 PC 机的串口连接起来,可以使用串口转USB转接器,连接单片机的串口与电脑的 USB 接口。 2. 单片机程序设计 单片机的程序需要使用串口通信协议与 PC 机进行通信,实现数据的发送和接收。以下是部分代码示例: ```c #include <reg52.h> #define BAUDRATE 9600 #define TH1_INIT 0xFA // 波特率计数器初值 #define TL1_INIT 0xFA // 波特率计数器初值 sbit SDA = P3^4; // I2C总线数据线 sbit SCL = P3^5; // I2C总线钟线 void InitSerialPort() { TMOD |= 0x20; // 定器1工作在模式2,8位自动重装载 SCON = 0x50; // 串口工作在模式1,8位数据位,无校验位,1位停止位 TH1 = TH1_INIT; // 设置波特率计数器初值 TL1 = TL1_INIT; // 设置波特率计数器初值 TR1 = 1; // 启动定器1 ES = 1; // 开启串口中断 EA = 1; // 开启总中断 } void SerialPortInterrupt() interrupt 4 { if (TI) { // 发送数据中断 TI = 0; } if (RI) { // 接收数据中断 RI = 0; } } void SendData(char data) { SBUF = data; // 将数据写入发送缓冲区 while (!TI); // 等待发送完成 TI = 0; // 清除发送完成标志位 } char ReceiveData() { while (!RI); // 等待接收完成 char data = SBUF; // 从接收缓冲区读取数据 RI = 0; // 清除接收完成标志位 return data; } void main() { InitSerialPort(); // 初始化串口 while (1) { // 从串口接收数据,并将数据显示在数码管上 char data = ReceiveData(); // ... // 从键盘输入数据,并通过串口发送到 PC 机 // ... SendData(data); } } ``` 在程序中,首先需要初始化串口,包括设置波特率、数据位、停止位和校验位等参数。然后使用中断服务程序来处理串口中断,实现数据的发送和接收。在主程序中,使用 `ReceiveData()` 函数从串口接收数据,并将数据显示在数码管上;同使用 `SendData()` 函数从键盘输入数据,并通过串口发送到 PC 机。 3. PC 程序设计 PC 程序需要通过串口通信协议与单片机进行通信,实现数据的发送和接收。以下是部分代码示例: ```c++ #include <iostream> #include <Windows.h> using namespace std; int main() { HANDLE hComm; char portName[] = "COM1"; DCB dcb; DWORD bytesRead, bytesWritten; char buf[100]; // 打开串口 hComm = CreateFile(portName, GENERIC_READ | GENERIC_WRITE, 0, NULL, OPEN_EXISTING, 0, NULL); if (hComm == INVALID_HANDLE_VALUE) { cout << "Failed to open serial port!" << endl; return -1; } // 配置串口参数 dcb.DCBlength = sizeof(DCB); if (!GetCommState(hComm, &dcb)) { cout << "Failed to get serial port state!" << endl; CloseHandle(hComm); return -1; } dcb.BaudRate = CBR_9600; dcb.ByteSize = 8; dcb.Parity = NOPARITY; dcb.StopBits = ONESTOPBIT; if (!SetCommState(hComm, &dcb)) { cout << "Failed to set serial port state!" << endl; CloseHandle(hComm); return -1; } // 从键盘输入数据,并通过串口发送到单片机 cout << "Please enter data to send: "; cin >> buf; WriteFile(hComm, buf, strlen(buf), &bytesWritten, NULL); // 从串口接收数据,并在控制台上显示 ReadFile(hComm, buf, sizeof(buf), &bytesRead, NULL); buf[bytesRead] = '\0'; cout << "Received data: " << buf << endl; // 关闭串口 CloseHandle(hComm); return 0; } ``` 在程序中,首先需要使用 `CreateFile()` 函数打开串口,包括设置串口的名称和访问权限等参数。然后使用 `GetCommState()` 函数获取串口状态,包括波特率、数据位、停止位和校验位等参数。接着使用 `SetCommState()` 函数设置串口状态。在主程序中,使用 `WriteFile()` 函数从键盘输入数据,并通过串口发送到单片机;同使用 `ReadFile()` 函数从串口接收数据,并在控制台上显示。最后使用 `CloseHandle()` 函数关闭串口。 以上是实现利用 8031 单片机串行口与 PC 机通讯的大致思路和步骤,具体实现还需要根据具体的硬件和软件环境进行调整和完善。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值