实验6 I/O程序设计
来源:互联网 发布:武汉知豆电动汽车4s店 编辑:程序博客网 时间:2024/06/04 19:44
实验6 I/O程序设计
输入/输出是对外部设备进行控制和数据交换的过程。CPU与外设之间的信息交换,是通过接口电路中的I/O端口进行的。I/O程序设计必须使用IN、OUT指令,对端口进行读、写操作。
6.1 读取实时时钟
现代微机中都包含一个实时钟,它自动地定时更新时间与日期。实时钟信息(年、月、日、时、分、秒)保存在CMOS RAM中,在系统关机后,时钟电路自带的后备电池继续向RAM供电,并且继续更新时间与日期。
1. 实时钟信息的内容
实时钟信息存储在CMOS RAM最前面的14个字节,而其他的字节用于保存微机的配置信息,例如系统的内存容量、软盘、硬盘类型等。实时钟信息的格式如表6-1所示。
表6-1 实时钟信息的格式
位移
内容
取值范围
格 式
0
秒
00H~59H
BCD码,如10H表示10秒,59H表示59秒。
1
报警秒
00H~59H
BCD码,同上。设为
2
分
00H~59H
BCD码,如06H表示06分,45H表示45分。
3
报警分
00H~59H
BCD码,同上。设为
4
小时
00H~23H
BCD码,如12H表示12点,16H表示16点。
5
报警小时
00H~23H
BCD码,同上。设为
6
星期几
1~7
1=星期日,2=星期一……7=星期六
7
日
01H~31H
BCD码,如31H表示31日。
8
月
01H~12H
BCD码,如04H表示4月,12H表示12月。
9
年
00H~99H
BCD码,年份的后面2位。如03H表示2003年。
10
状态/控制A
Bit 7=1,正在刷新。Bit 6~4=010b,基准频率=32768Hz。
11
状态/控制B
Bit 1=1,使用24小时制。Bit 0=1,使用夏时制。
12
状态C
Bit 7=1,产生中断请求。Bit 6~4,中断原因。
13
状态D
Bit 7=1,CMOS电池正常。
其中,报警信息记录在1、3、5单元中。若这些单元的内容为
2. 实时钟信息的读取方法
对CMOS RAM的访问必须用I/O指令来完成。接口电路设置了两个寄存器,其I/O地址分别是70H和71H。70H是RTC地址寄存器,71H是RTC数据寄存器。要访问某个CMOS RAM单元时,CPU先用OUT指令将该单元的地址(00H~7FH)写入到70H端口中,再使用IN指令读入该CMOS RAM单元的内容。如图6-1所示。
I/O地址
70H
RTC地址寄存器
RTC地址
CPU
数据
CMOS
71H
RAM
读/写
RTC数据寄存器
数据
RTC接口电路
图6-1 RTC输入/输出接口
3. 在用户模式下执行IN、OUT指令
在Windows NT/2000/XP/2003中,应用程序运行最低特权级(CPL=3)。标志寄存器中的IO特权级被设置为3(IOPL=3),而且任务状态段中I/O位图中所有位为1,这样,不允许应用程序使用I/O指令。如果在程序中执行IO指令,会产生异常,程序被中止运行。
为避免该问题,可以将giveio.sys驱动程序装入内核,应用程序打开//./giveio设备文件,驱动程序就将任务状态段中I/O位图的全部位设为0。这样,在应用程序中就可以执行IN、OUT指令了。
将giveio.sys驱动程序装入内核的命令为:
c:/asm/bin/allowio -load c:/asm/bin/giveio.sys
从内核中卸载giveio.sys驱动程序的命令为:
c:/asm/bin/allowio -unload
;程序清单:cmosram.asm(读取实时钟)
.386
.model flat,stdcall
option casemap:none
includelib msvcrt.lib
includelib kernel32.lib
printf PROTO C format:ptr sbyte,:vararg
CreateFileA PROTO stdcall,
lpFileName:NEAR32, dwDesiredAccess:dword, dwShareMode:dword,
lpSecurityAttributes:NEAR32, dwCreationDisposition:dword,
dwFlagsAndAttributes:dword, hTemplateFile:dword
CloseHandle PROTO stdcall, hObject:dword
; 以下是程序中用到的一些常量
GENERIC_READ EQU 80000000h
OPEN_EXISTING EQU 3
FILE_ATTRIBUTE_NORMAL EQU 00000080h
INVALID_HANDLE_VALUE EQU -1
NULL EQU 0
.data
driverStr byte "//./giveio", 0 ; 设备文件名
cmosIndex byte 9, 8, 7, 4, 2, 0 ; 年/月/日/时/分/秒的索引
cmosData dword 6 dup (?) ; 年/月/日/时/分/秒
fmtStr byte '20%02d/%02d/%02d %02d:%02d:%02d', 0ah, 0
.code
AllowIo proc
pushfd ; 标志寄存器压栈
pop eax ; 标志寄存器 -> EAX
and eax, 00003000h ; 取其第12,13位
cmp eax, 00003000h ; IOPL是否等于3 ?
jnz IOPLZero ; IOPL != 3, 需借助于驱动程序
; IOPL = 3, 程序可以执行I/O
mov eax, 1 ; 返回1, 表示TRUE
ret
IOPLZero:
invoke CreateFileA, ; 打开文件
offset driverStr, ; 文件名
GENERIC_READ, ; 只读方式打开
0,
NULL,
OPEN_EXISTING, ; 打开已存在的文件
0,
0
cmp eax, INVALID_HANDLE_VALUE
jz OpenFail ; 不能打开, 退出
invoke CloseHandle, eax ; 关闭文件
mov eax, 1 ; 返回1, 表示TRUE
ret
OpenFail:
mov eax, 0 ; 返回0, 表示FALSE
ret
AllowIo endp
start:
call AllowIo ; 是否可以进行I/O?
cmp eax, 0 ; eax=0,不能进行I/O
jz ExitIo ; 退出
mov ecx, 6 ; 一共要读取6个字节
mov esi, 0 ; 数组下标初始化为0
GetCmos:
mov al, cmosIndex[esi] ; 取得索引
out 70h, al ; 设置索引
in al, 71h ; 读取数据
; 读取到的数据是BCD码格式
; 例如 al=56h 表示 56 秒
mov ah, al ; al->ah, ah=56h
shr ah, 4 ; 取高4位到ah中
and al, 0fh ; al高4位清零,
aad ; ah*10+al->al
mov byte ptr cmosData[esi*4], al ; 保存
inc esi ; 索引加1
loop GetCmos ; 依次取得
; 年/月/日/时/分/秒
invoke printf, ; 显示结果
offset fmtStr,
cmosData[0*4], ; 年
cmosData[1*4], ; 月
cmosData[2*4], ; 日
cmosData[3*4], ; 时
cmosData[4*4], ; 分
cmosData[5*4] ; 秒
ExitIo:
ret
end start
6.2 直接读取硬盘扇区
硬盘一般采取IDE(Integrated Drive Electronics)接口,硬盘控制器与硬盘盘体集成在一起。光驱也大多采取IDE接口。IDE连接器是一种40针连接器,针脚间距
图6-2 IDE电缆和硬盘接口
1. IDE控制器
PC机一般都集成了2个IDE控制器,主板上具有2个IDE插槽(Primary和Secondary,主/次)。
这些ATA控制器所使用的地址一般也是固定的。主控制器使用
在“开始”→“设置” →“控制面板” →“系统” →“硬件” →“设备管理器”中,用菜单“查看”→“按类型排序资源”,展开“输入/输出(I/O)”,可以看到图6-3所示的IDE控制器所占用的I/O地址。
图6-3 IDE控制器所占用的I/O地址
2. 主盘和从盘
一条IDE电缆的一端连接到主板(或接口卡)的IDE控制器上,另一端最多可以连接2个硬盘。这2个硬盘中,其中的一个作为主盘(Master driver, 驱动器0),而另一个作为从盘(Slave drive, 驱动器1)。可以通过设置硬盘上的跳线来确定它作为主盘还是从盘,也可以由电缆的连接位置来决定。
两个硬盘连接到同一条电缆,也就是同一个IDE通道(Channel)上,它们在系统中占用相同的I/O地址。
3. 数据传输模式
ATAPI标准定义了IDE控制器与硬盘(或光驱)设备的连接接口。ATAPI是硬盘、CD/DVD设备采用的工业标准。
硬盘有2种数据传输模式:
(1) PIO模式
PIO模式即可编程I/O模式,由CPU执行IN/OUT指令访问ATA控制器的端口,将数据从硬盘读出或者写入到硬盘。采用PIO模式时,最快只能达到16.6MB/s的传输率。采用PIO模式,每次传输16位,即2个字节。
(2)DMA传输模式
在DMA模式下,数据的传送在ATA控制器的端口和内存之间直接进行,不需要通过CPU,因此能使CPU的负担减轻,数据传输率也能进一步提高。
单字DMA(DMA single word)每次传输8位,多字DMA(DMA single word)每次传输16位。
4. 扇区编址模式
每个扇区的大小为512字节。主机在读写硬盘时,需要指定它要读写的是哪一个扇区。有两种方式来表示扇区的地址,它们是CHS编址模式和LBA编址模式。
(1)CHS编址模式
CHS编址方式中,扇区的地址由三个部分组成:柱面号(Cylinder)、磁头号(Head)和扇区号(Sector)。扇区号从1开始,而柱面号和磁头号从0开始。所以,0柱面0磁头1扇区是整个硬盘的第1个扇区。
(2)LBA编址模式
LBA编址方式中,扇区的地址就是这个扇区的序号,用一个整数来表示。例如,0柱面0磁头1扇区的序号为0。LBA的编号从0开始。
LBA编址模式有2种:28位地址和48位地址,扇区序号分别用28位或48位整数来表示。在28位LBA编址模式下,扇区序号的最大值为228,能够使用的最大容量为:228×512=137438953472字节=128GB。
在对硬盘进行编程时,通过寄存器中的LBA位来区分编址模式。LBA=0时,表示使用CHS编址模式;LBA=1时,表示使用LBA编址模式。
5. ATA设备寄存器
主机对硬盘的读写操作是通过硬盘上的两组寄存器来实现,第一组寄存器中占用8个端口(
表6-2 ATA设备寄存器的I/O地址
主控制器
从控制器
寄存器
作 用
170~171H
数据寄存器
读写,主机与ATA设备交换数据
171H
特征寄存器
写,只对部分命令有效
171H
错误寄存器
读,状态寄存器中ERR=1时有效
172H
扇区数寄存器
读写,要求读/写的扇区数
173H
扇区号寄存器
读写,扇区号或8位LBA
174H
低位柱面寄存器
读写,磁道号或8位LBA
175H
高位柱面寄存器
读写,磁道号或8位LBA
176H
设备/磁头寄存器
读写,指定LBA模式、DEV位、磁头号及4位LBA
177H
状态寄存器
读,读出ATA设备的状态
177H
命令寄存器
写,向ATA设备发出命令
376H
设备控制寄存器
写,设置HOB、复位、nIEN等
每一个控制器上可以连接两个ATA设备,这两个设备使用同样的I/O地址,在设备/磁头寄存器中用DEV位来区分这两个设备。DEV=0时,表示主盘;DEV=1时,表示从盘。
(1) 命令寄存器(Command Register, 8位)
将一个命令写入该寄存器后,设备就立即开始执行这个命令。执行命令所需的其它参数需要预先写入其它的寄存器。例如,要读一个扇区,可以将命令码(20h)写入命令寄存器,但之前必须把扇区号、要读取的扇区数目写入相应的寄存器。
ATA标准中规定了命令码,常用的命令码如表6-3所示。
表 6-3 ATA设备的部分命令码
命令码
名 称
作 用
20h
READ SECTOR(S)
读扇区,支持CHS模式和28位LBA
24h
READ SECTOR(S) EXT
读扇区,支持CHS模式和48位LBA
30h
WRITE SECTOR(S)
写扇区,支持CHS模式和28位LBA
34h
WRITE SECTOR(S) EXT
写扇区,支持CHS模式和48位LBA
C8h
READ DMA
采用DMA方式读扇区
CAh
WRITE DMA
采用DMA方式写扇区
ECh
IDENTIFY DEVICE
读取设备信息,包括型号、容量、序列号等
(2) 状态寄存器(Status Register, 8位)
包含了设备的当前状态信息。该寄存器的格式如图6-4所示。
7
6
5
4
3
2
1
0
BSY
DRDY
DF
DSC
DRQ
CORR
IDX
ERR
图 6-4 ATA状态寄存器
BSY代表是否忙(Busy)。当BSY为1时,主机不能执行对设备寄存器的读写操作(除开状态寄存器外)。
DRDY代表设备是否准备好(Device Ready)。DRDY为1表明设备现在可以执行一个命令。
DF代表Device Fault,该设备不支持状态寄存器的这一位,所以DF位总是为0。
DSC代表Device Seek Complete,如果DSC位为1表明磁头定位已经完成。
DRQ代表Data Request,DRQ为1表明设备已经准备好在主机和设备之间传输数据。当DRQ为1时,主机不能向命令寄存器中写命令码。
CORR代表Corrected Data,该位总是为0。
IDX代表Index。
ERR代表是否有错误(Error),ERR位为1表明在执行前面一条命令的过程中出现了错误。错误的具体类型由错误寄存器(Error Register)的内容决定。
(3) 数据寄存器(Data Register, 16位)
数据寄存器用于在主机和设备之间传输数据块。当状态寄存器中的DRQ为1时,数据寄存器中包含的数据是有效的。
(4) 设备控制寄存器(Device Control Register, 8位)
设备控制寄存器的格式如图6-5所示。
7
6
5
4
3
2
1
0
HOB
保留
保留
保留
保留
SRST
nIEN
0
图6-5 ATA控制寄存器
HOB=1时,表示要将扇区数的15~8位、LBA的31~24位、39~32位、47~40位写入扇区数寄存器、扇区号寄存器、低位柱面寄存器和高位柱面寄存器。写入完毕后,应将HOB设为0。
SRST=1,表示复位该IDE通道。
nIEN=1,表示禁止设备产生中断。=0,允许设备产生中断。
(5) 扇区数寄存器(Sector Count Register, 8位)
包含要读写的扇区的个数。在28位LBA模式或者CHS模式下,一次命令可以传送1~256个扇区。写入0代表传送256个扇区。在48位LBA模式下,一次命令可以传送1~65536个扇区。需要写入2次,HOB置为0时写入低8位,HOB置为1时写入高8位。16位全部为0时代表要传送65536个扇区。
(6) 设备/磁头寄存器(Device/Head Register)
设备/磁头寄存器的格式如图6-6所示。
7
6
5
4
3
2
1
0
保留
LBA
保留
DEV
磁头号
图6-6 ATA设备/磁头寄存器
第6位LBA等于0时,表示使用CHS寻址模式。等于1时,表示使用LBA寻址模式。
第4位DEV等于0时,表示选择drive 0(主盘)。等于1时,选择drive 1(从盘)。
在28位LBA模式时,设备/磁头寄存器的低4位表示LBA的27~24位。
(7) 高位柱面寄存器(Cylinder High Register, 8位)、低位柱面寄存器(Cylinder Low Register, 8位)、扇区号寄存器(Sector Number Register, 8位)
这些寄存器表示扇区的地址。一共有3种情况:CHS模式、28位LBA模式、48位LBA模式。3种模式下,各寄存器的使用如表6-4所示。
表6-4 扇区地址在寄存器中的表示
寄存器
CHS模式
28位LBA模式
48位LBA模式
第1次写入(HOB=1)
第2次写入(HOB=0)
高位柱面寄存器
磁道号高8位
LBA第23~16位
LBA第47~40位
LBA第23~16位
低位柱面寄存器
磁道号低8位
LBA第15~8位
LBA第39~32位
LBA第15~8位
扇区号寄存器
8位扇区号
LBA第7~0位
LBA第31~24位
LBA第7~0位
设备/磁头寄存器
4位磁头号
LBA第27~24位
未使用
未使用
扇区数寄存器
要读写的扇区数(8位)
要读写的扇区数(8位)
扇区数(高8位)
扇区数(低8位)
使用CHS模式时,设备/磁头寄存器中的LBA位设为0,将磁道号、扇区号、磁头号写入相应寄存器;使用28位LBA模式时,LBA位设为1,将LBA的28位写入上述4个寄存器;使用48位LBA模式时,LBA位设为1,先将LBA的高24位(3个字节)分别写入前面3个寄存器,再写入LBA的低24位;
(8) 错误寄存器(Error Register, 8位)
包含设备上一次执行命令后的状态信息。错误寄存器中各位的意义与执行的命令有关。
(9) 特征寄存器(Feature Register, 8位)
只有使用少数特殊命令才使用到这个寄存器。
6. 采用PIO方式读取硬盘扇区
20h命令(READ SECTOR(S))是用PIO方式读取硬盘的命令。其格式如图6-7所示。
图6-7 READ SECTOR(S)格式
按照以下步骤读取硬盘扇区:
(1) 复位硬盘,将SRST=1,再设为0(SRST=1)。
(2) 读取状态寄存器,等待其BSY=0,DRQ=0。
(3) 按照图6-7,设置7个寄存器。
(4) 读取状态寄存器,等待其DRDY=1,DSC=1,DRQ=1。
(5) 从数据寄存器中读取扇区内容。
在第(3)步之后,硬盘控制器将扇区数据读入硬盘内部的缓冲区,再将状态寄存器的DRDY、DSC、DRQ设为1。因此在第(4)步必须查询状态寄存器,等待硬盘控制器完成读扇区的操作。
在第(5)步,每次读写2个字节。每个扇区为512个字节,一个扇区执行256次读取操作。
;程序清单:hddio.asm(PIO方式读取硬盘扇区)
.386
.model flat,stdcall
option casemap:none
includelib msvcrt.lib
includelib kernel32.lib
printf PROTO C format:ptr sbyte,:vararg
CreateFileA PROTO stdcall,
lpFileName:NEAR32,dwDesiredAccess:dword,dwShareMode:dword,
lpSecurityAttributes:NEAR32,dwCreationDisposition:dword,
dwFlagsAndAttributes:dword,hTemplateFile:dword
CloseHandle PROTO stdcall,hObject:dword
; 以下是程序中用到的一些常量
GENERIC_READ EQU 80000000h
OPEN_EXISTING EQU 3
FILE_ATTRIBUTE_NORMAL EQU 00000080h
INvalID_HANDLE_valUE EQU -1
NULL EQU 0
pio_base_addr1 EQU
pio_base_addr2 EQU
numSect EQU 1 ; 读入的扇区个数
lbaSector EQU 0 ; 扇区编号LBA
.data
driverStr byte "//./giveio",0 ; 设备文件名
errStr byte 'Load giveio.sys first!',0ah,0
_Buffer byte 512*numSect dup (2) ; 用于保存读入的扇区数据
_BufferLen equ $-_Buffer
szFmt byte '%02X ',0
szCRLF byte 0dh, 0ah, 0
outx MACRO port,val
mov dx,port
mov al,val
out dx,al
ENDM
inx MACRO port
mov dx,port
in al,dx
ENDM
.code
AllowIo proc
invoke CreateFileA, ; 打开文件
offset driverStr, ; 文件名
GENERIC_READ, ; 只读方式打开
0,
NULL,
OPEN_EXISTING, ; 打开已存在的文件
0,
0
cmp eax,INvalID_HANDLE_valUE
jz OpenFail ; 不能打开,退出
invoke CloseHandle,eax ; 关闭文件
mov eax,1 ; 返回1,表示TRUE
ret
OpenFail:
mov eax,0 ; 返回0,表示FALSE
ret
AllowIo endp
start:
call AllowIo ; 是否可以进行I/O?
cmp eax,0 ; eax=0,不能进行I/O
jnz AllowIoLoadOk ; 退出
invoke printf,offset errStr ; 显示提示信息
ret
AllowIoLoadOk:
; SRST=1, 复位硬盘
outx pio_base_addr2+6,04h ; SRST=1
outx pio_base_addr2+6,00h ; SRST=0
; 等待,直到BSY=0而且DRQ=0.
waitReady:
inx pio_base_addr1+7 ; read primary status
and al,10001000b ; busy,or data request
jnz waitReady
; 设置feature port寄存器
outx pio_base_addr1+1,00h
; 设置sector count寄存器, 要读写的扇区数
outx pio_base_addr1+2,numSect
; 设置sector numbert寄存器, LBA(7:0)
outx pio_base_addr1+3,((lbaSector shr 0) and 0ffh)
; 设置cylinder low寄存器, LBA(15:8)
outx pio_base_addr1+4,((lbaSector shr 8) and 0ffh) ;
; 设置cylinder high寄存器, LBA(23:16)
outx pio_base_addr1+5,((lbaSector shr 16) and 0ffh) ;
; 设置device/head寄存器, LBA=1, DEV=0, LBA(27:24)
outx pio_base_addr1+6,01000000b or ((lbaSector shr 24) and 0fh)
; 设置command寄存器, 20h表示READ SECTOR(S)
outx pio_base_addr1+7,020h
; 等待,直到DRDY=1, DSC=1, DRQ=1.
waitHDD:
inx pio_base_addr1+7
cmp al,01011000b
jnz waitHDD
; 每个扇区读入256次,每次读入2个字节,顺序保存在_Buffer中
lea edi,_Buffer
cld
mov ecx,numSect*256
Read2Bytes:
mov dx,pio_base_addr1
in ax,dx
stosw
loop Read2Bytes
; 显示_Buffer内容
call DisplayBuffer
ret
DisplayBuffer proc
lea esi,_Buffer
mov ecx,_BufferLen
xor eax,eax
xor ebp,ebp
cld
DisplayByte:
push ecx
lodsb
invoke printf,offset szFmt,eax
inc ebp
test ebp, 0fh
jnz DisplayCRLF
invoke printf,offset szCRLF
DisplayCRLF:
pop ecx
loop DisplayByte
ret
DisplayBuffer endp
end start
运行该程序之前,必须装入giveio.sys:
allowio -load c:/asm/bin/giveio.sys
程序中定义了outx、inx两个宏,调用这些宏能够方便地执行OUT、IN操作。
hddio.exe读入硬盘的0扇区,运行程序的结果为:
C:/asm/sample>hddio
BF 1B 06 50 57 B9 E5
38 6E 00
F
4E 10 E8 46 00 73
80 7E 04
46 08 06 83 56
BC 81 3E FE 7D 55 AA 74 0B 80 7E 10 00
B7 07 EB A9 8B FC 1E 57 8B F5 CB BF 05 00
00 B4 08 CD 13 72 23
8B 4E 02 8B 56 00 CD 13 73 51
56 00 CD 13 EB E4
13 72 36 81 FB 55 AA 75
01
32 E4
62
67 20
65 6D 00 4D 69 73 73 69 6E 67 20
74 69 6E 67 20 73 79 73 74 65 6D 00 00 00 00 00
00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
00 00 00 00 00
01 00 06 FE
01 FF 07 FE FF FF
C1 FF 05 FE FF FF 68 D5 BC 05 57 68
00 00 00 00 00 00 00 00 00 00 00 00 00 00 55 AA
可以修改程序中的numSect和lbaSector,读入多个硬盘扇区,以及扇区编号。
7. 验证扇区数据
为验证程序是否读入了正确的扇区,可以执行WinHex等磁盘编辑软件。
运行WinHex时,从菜单中选择Tools→Open Disk…,选择第1个物理硬盘(Physical Media),如图6-8所示。
图6-8 选择物理磁盘
之后,再按Ctrl+G,或者从菜单中选择Position→Go To Sector…,出现图6-9所示的对话框,在Logical中的Sector后面输入扇区序号(即LBA)。
图6-9 输入LBA
WinHex显示出扇区中的数据,如图6-10所示。与hddio输出的结果相对比,确认hddio是否正确。
图6-10 显示扇区的内容
6.3 双机全双工串行通信
串行接口是微机应用系统常用的接口,也被称做COM口。微机中一般都配备了两个COM口,称为COM1、COM2。部分微机还有另外两个串口COM3、COM4。
1. RS 232
计算机的串行接口使用RS 232-C标准的9芯连接器,如图6-11所示。
图6-11 9芯连接器
表6-3列出了计算机中常用的RS-232-C信号的名称、功能。在通信中,Modem起着传输信号的作用,是一种数据通信设备(Data Communication Equipment),简称DCE。接收设备和发送设备称为数据终端设备(Data Terminal Equipment),简称DTE。
表6-3 常用RS-232-C信号
引脚编号
名 称
功 能
方 向
1
DCD
接收线路信号检测
DCE→DTE
2
RxD
接收数据
DCE→DTE
3
TxD
发送数据
DTE→DCE
4
DTR
DTE就绪
DTE→DCE
5
SG
信号地
信号公共地
6
DSR
DCE就绪
DCE→DTE
7
RTS
请求发送
DTE→DCE
8
CTS
允许/清除发送
DCE→DTE
9
RI
振铃指示
DCE→DTE
这些信号的作用为:
·TXD:输出,串行数据发送引脚,将串行数据从数据终端(CPU方面)发出,传送到数据装置(调制解调或外设)。
·RXD:输入,串行数据接收引脚,数据终端接收从数据装置传来的串行数据。
·RTS#:输出,数据终端请求发送。低电平时有效,RTS#为低,表示数据终端准备好发送数据,是计算机一方送往外设或调制解调的信号,表示计算机请求发送数据。
·CTS#:输入,数据设备清除请求发送(允许发送)信号,低有效,该信号来源于数据装置,它和RTS#组成一对联络信号,是对RTS#的响应,表示外设或调制解调准备好,允许计算机发送数据。当CTS有效时,计算机才能向外设发送数据。
·DSR#:输入,数据设备准备好,是外设送往CPU方面的信号,低电平有效,表示当前外部设备已经准备好。它和DTR#组成一对联络信号。
·GND:地信号,是数据终端和数据装置的公共地。
·DCD#:输入,载波检测信号,来源于调制解调器,低电平有效,它指示调制解调器已经建立了有效的连接。只有在DCD#的有效状态下,CPU才能开始传送数据。
·DTR#:输出,数据终端准备好,低有效,是由计算机送往外设的信号,通知外部设备CPU当前已经准备就绪。
·RI:输入,振铃指示信号,高有效,当调制解调器在线路上检测到一个响铃信号,便使RI信号有效。该信号用来通知CPU有一个输入的呼叫。
在传输距离较近时,不必使用调制解调器,可以直接将两台计算机(或数据终端)的RS-232接口相连接。为了变换信息,TxD和RxD应当交叉连接,如图6-12所示。
图6-12 计算机串口之间的连接
1. 串口设备寄存器
在“设备管理器”中,用鼠标双击COM1设备,选择“资源”,显示出为COM1设备分配的端口地址(
图6-13 COM1设备所占用的资源
微机的串口设备通常采用16550芯片或兼容产品。16550内部有12个可访问的寄存器,但只为它分配了8个I/O地址(A
DLAB是线路控制寄存器(LCR)的D7位。要设置波特率除数寄存器时,先设置线路控制寄存器中的DLAB为1,然后再写入波特率除数寄存器的低字节和高字节。低字节写入到A
A
表6-3 16550内部寄存器地址
A
被访问的寄存器
000
DLAB=0:接收缓冲寄存器RBR(读),发送保持寄存器THR(写)
DLAB=1:波特率除数寄存器DLL(低字节)
001
DLAB=0:中断允许寄存器IER
DLAB=1:波特率除数寄存器DLM(高字节)
010
中断识别寄存器IIR(读),FIFO控制寄存器FCR(写)
011
线路控制寄存器LCR
100
MODEM控制寄存器MCR
101
线路状态寄存器LSR
110
MODEM状态寄存器MSR
111
Scratch寄存器SCR
(1)发送保持寄存器(Transmitter Holding Register,THR)
CPU将要发送的数据字节输出到这个寄存器,串行接口电路就将这个数据字节转换成串行信号传送给对方。数据字节的有效位可以是5位、6位、7位或8位。
发送时,CPU将待发送的字符写到THR后,然后进入发送移位寄存器,在发送时钟的作用下,按起始位、数据位、校验位、停止位的顺序,从SOUT引脚逐位输出。一旦THR的内容送到发送移位寄存器TSR后,THR变空,就使LSR的THRE位置1,产生中断请求。THRE位置1时,表示CPU可以发送下一个字符。CPU向THR写入一个字符后,THRE置0。
(2)接收缓冲寄存器(Receiver Buffer Register,RBR)
串行接口电路接收到发送方的一个数据字节后,CPU可以从这个寄存器读取这个数据字节。
接收时,串行数据在接收时钟作用下,从SIN引脚以串行移位的方式输入到接收移位寄存器RSR,然后由RSR并行输入到接收缓冲寄存器RBR,一旦RBR变满,就使LSR的DR位置1。DR位为1时,表示CPU可以读取数据字符。CPU从RBR读取一个字符后,DR置0。
(3)线路状态寄存器(Line Status Register,LSR)
CPU读取LSR来取得串行数据传送的状态。寄存器中各位的定义如图6-14所示。
7
6
5
4
3
2
1
0
RFE
TEMT
THRE
BI
FE
PE
OE
DR
RFE
接收FIFO出错(只对16550有效)。等于1时,表示接收FIFO出错
TEMT
发送移位器空。等于1时,表示发送器空。
THRE
发送保持寄存器空。等于1时,表示THR中的数据字节已被取走。数据字节写入到THR时,此位被清零。
BI
间断识别指示。等于1时,接收线SIN空闲的时间超过了传送一个字符的时间,对方发送过程出现了间断。
FE
帧格式错。等于1时,表示传输的数据格式错误。
PE
奇偶校验错。等于1时,表示奇偶校验错误。
OE
覆盖错。等于1时,表示接收到有效的数据但被丢失。
DR
接收缓冲寄存器有效。等于1时,已接收到一个数据字节放入到RBR中。读取RBR后,此位被清零。
图6-14 线路状态寄存器LSR的格式
LSR的第0位DR和第5位THRE是最基本的指示位。只有DR=1时,CPU从RBR中读取的数据字节才是从发送方发出的有效数据字节。DR=0时,CPU从RBR中读出的是“旧的”数据字节或无效的数据字节。只有THRE=1时,CPU写到THR的数据字节才会被正确地发送出去。THRE=0时,THR中的数据字节还没有被取走,不能向其中写入新的数据字节。
设8250的基地址为
(4)线路控制寄存器(Line Control Register, LCR)
LCR主要用来指定异步通信数据格式,同时它的最高位DLAB用来指定允许访问除数寄存器,如图6-15所示。
7
6
5
4
3
2
1
0
DLAB
SB
SP
EPS
PEN
STB
WLS1
WLS0
WLS1
WLS0
WLS1 WLS0=00b, 字符长度为5位; =01b, 字符长度为6位;
=10b, 字符长度为7位; =11b, 字符长度为8位。
STB
=0,停止位长度为1位;=1,1.5位或2位(字符长度为5位时采用1.5位停止位,字符长度为6、7、8位时采用2位停止位)。
PEN
=0, 不使用奇偶校验。发送接收时没有校验位。
EPS
=0, 奇校验; =1, 偶校验。EP=0时,此位无效。
SP
=1时,奇偶校验位固定为0或1。=0时,根据字符计算校验位。
SB
=1时,发送线SOUT设为0并保持至少一个字符的时间,即产生一个间断,进入发送间断状态。=0时,退出间断状态。
DLAB
=1,访问除数寄存器;DLAB=0,访问其他寄存器。
图6-15 线路控制寄存器LCR的格式
第5位SP(STICK PARITY)是固定校验选择位。当PEN=1(有奇偶校验)时,若SP=1,那么校验位完全根据EPS来设定,而不考虑发送字符的内容。PEN=1、SP=1、EPS=0时,校验位始终为1;PEN=1、SP=1、EPS=1时,校验位始终为0。SP设为1的目的是发送方把采用何种奇校验还是偶校验(即发送方采用的EPS值)告诉接收方。显然,在收发双方已约定奇偶校验方式的情况下,那么就不需要采用这种方式来沟通,应该使SP=0。
(5)除数锁存器(Divisor Latch LSB/MSB, DLL/DLM)
8250芯片传输数据的速率是由除数锁存器控制的。外接的1.8432MHz基准时钟,通过除数寄存器给定的分频值,在8250内部产生不同的波特率,通过BAUDOUT#引脚输出到RCLK,控制接收传输速率。对一个已知的波特率,按照以下公式计算除数锁存器的内容:
f工作时钟 = f基准时钟 ÷ 除数锁存器 = 波特率 × 16
这里f基准时钟= 1.8432MHz = 1843200Hz。
除数锁存器 = f基准时钟 ÷ (波特率 × 16) = 1843200 ÷ (波特率 × 16) = 115200 ÷波特率
(6)中断允许寄存器(Interrupt Enable Register, IER)
在以下4种情况都可以产生中断:接收数据出错中断、接收缓冲器满中断、发送保持寄存器空中断、以及来自MODEM的控制信号状态改变。
通过设置IER,可以允许或禁止上述中断。
7
6
5
4
3
2
1
0
0
0
0
0
EDSSI
ELSI
ETBEI
ERBFI
ERBFI
接收缓冲器满以后,是否产生中断。=0, 禁止;=1, 允许。
ETBEI
发送保持寄存器空以后,是否产生中断。=0, 禁止;=1, 允许。
ELSI
接收数据出错后,是否产生中断。=0, 禁止;=1, 允许。
EDSSI
MODEM的控制信号状态改变,是否产生中断。=0, 禁止;=1, 允许。
图6-16 中断允许寄存器IER的格式
(7)Modem控制寄存器(Modem Control Register, MCR)
MODEM控制寄存器MCR用来设置对MODEM的联络控制信号(DTR、DSR)。OUT1#和OUT2#引脚的电平也由MCR的第2、3位来控制。LOOP=1常用于芯片自检。LOOP=1时,16550工作在自环路状态,从THR发送的字符被复制到RBR中,就像从Rx线路上接收到一个字符一样。
7
6
5
4
3
2
1
0
0
0
0
OUT2
OUT1
RTS
DTR
DTR
=0, DTR#引脚输出高电平;=1, DTR#引脚输出低电平。
RTS
=0, RTS #引脚输出高电平;=1, RTS#引脚输出低电平。
OUT1
=0, OUT1#引脚输出高电平;=1, OUT1#引脚输出低电平。
OUT2
=0, OUT2#引脚输出高电平;=1, OUT2#引脚输出低电平。
=0,正常模式;=1,诊断模式,自发自收。
图6-17 Modem控制寄存器MCR的格式
下面的程序在两台计算机的COM1之间进行全双工串行通信。它从键盘上接收字符,发送到对方。发送的字符显示在屏幕上的第23行,接收的字符显示在屏幕上的第24行,输入Esc键后,程序结束。
;程序清单: comiox.asm (双机串行通信程序)
.model small ; 模式为small, 在虚拟8086模式下运行
.stack 1024 ; 堆栈段, 大小为1024字节
.data ; 数据段
sendChar db 0 ; 待发送字符
recvChar db 0 ; 接收到的字符
nSendPos db 0 ; 已发送字符的个数
nRecvPos db 0 ; 已接收字符的个数
loopBack db 0 ; 是否支持LoopBack
com_port_base equ
.code
; 从键盘读取待发送字符
ReadKb proc
cmp sendChar, 0 ; 若sendChar!=0, 表示该字符还未被发送,
jnz NoKey ; 不检查键盘
mov ah, 1 ; AH=1, INT 16h
int 16h ; 检查键盘是否输入了字符
jz NoKey ; ZF=1, 未输入字符
mov ah, 0 ; AH=0, INT 16h
int 16h ; 从键盘读取字符
cmp al, 0 ; AL=字符的ASCII码
jz NoKey ; AL=0, 扩展键
mov sendChar, al ; sendChar=待发送字符
NoKey:
ret
ReadKb endp
; 通过COM1发送字符sendChar
SndChar proc
mov dx, com_port_base+5 ; DX=线路状态寄存器(LCR)地址
in al, dx ; 读取线路状态寄存器
test al, 20h ; 检测THRE位
jz SndBusy ; THRE=0, ZF=1, THR中的字符还没有被取走
mov al, sendChar ; AL=待发送字符
mov dx, com_port_base+0 ; DX=发送保持寄存器(THR)地址
out dx, al ; 将字符写入THR
mov ah,2 ; AH=09, INT 10h, 置光标
mov dh,23
mov dl,nSendPos
mov bh,0
int 10h ; 将光标置于(23,nSendPos)处
mov ah,09h ; AH=09, INT 10h, 显示字符
mov al,sendChar
mov cx,1 ; 显示一次
mov bl,06h ; 字符属性06h(颜色)
mov bh,0
int 10h ; 在光标位置显示字符sendChar
inc nSendPos ; nSendPos=已经发送字符的个数
SndBusy:
ret
SndChar endp
; 通过COM1接收字符recvChar
RcvChar proc
mov dx, com_port_base+5 ; DX=线路状态寄存器(LCR)地址
in al, dx ; 读取线路状态寄存器
test al, 01h ; 检测DR位
jz NoChar ; DR=0, ZF=1, 没有可接收的字符
test al, 0eh ; 检测FE,PE,OE位是否为1
jnz NoChar ; 任何一位为1, 接收出错
mov dx, com_port_base+0 ; DX=接收缓冲寄存器(RBR)地址
in al, dx ; 从RBR中读入字符
mov recvChar, al ; 保存在recvChar中
mov ah,2
mov dh,24
mov dl,nRecvPos
mov bh,0
int 10h ; 将光标置于(24,nRecvPos)处
mov ah,09h
mov al,recvChar
mov cx,1
mov bl,0Fh ; 字符属性0Fh(颜色)
mov bh,0
int 10h ; 在光标位置显示字符recvChar
inc nRecvPos ; nRecvPos=已经接收字符的个数
ret
NoChar:
ret
RcvChar endp
; 对串口进行初始化
InitCom proc
mov dx, com_port_base+3 ; 线路控制寄存器地址
mov al, 80h
out dx, al ; DLAB=1
mov dx, com_port_base+0 ; 低位除数寄存器
mov al, 120 ; 9600波特率的除数低8位
out dx, al
mov al, 00
inc dx ; 高位除数寄存器
out dx, al
mov al, 00011011b ; 偶校验、1位停止位, 8位数据位
mov dx, com_port_base+3 ; 线路控制寄存器地址
out dx, al
mov al, 0 ; 禁止中断
mov dx,
out dx, al
mov al, 03h ; RTS=1,DTR=1
cmp loopBack, 0
jz NotLoopBack
or al, 10h ; Loopback位设为1
NotLoopBack:
mov dx, com_port_base+4 ; MODEM控制寄存器地址
out dx, al
ret
InitCom endp
; 主程序
main proc
mov ax, seg loopBack
mov ds, ax ; 设置DS指向数据段
mov es, ax ; 设置ES指向数据段
mov loopBack, 0 ; loopBack=1,可以在单机上调试此程序
call InitCom ; 对COM1进行初始化
SendRecvChars:
call ReadKb ; 从键盘读入字符
cmp sendChar, 0
jz ReadCom2 ; 未读入字符,不发送
call SndChar ; 发送字符
cmp sendChar, 1bh
jz ExitLoop ; 输入了Esc字符, 程序结束
mov sendChar,0 ; sendChar清0, 避免重复发送
ReadCom2:
call RcvChar ; 接收字符
cmp recvChar, 1bh
jz ExitLoop ; 接收到Esc字符, 程序结束
jmp SendRecvChars ; 循环执行
ExitLoop:
mov ax,
int 21h ; 程序退出
main endp
end main
该程序运行在虚拟8086模式或纯DOS下,编译过程与其他程序有所不同。执行以下两个命令可完成它的编译、连接:
masm comiox;
link comiox;
从键盘接收字符时,调用了INT 16H中断的1号(AH=1)功能,当键盘没有输入字符时,程序可接收对方发送的字符。
将“mov loopBack,
6.4 实验题:读取硬盘序列号
操作系统是通过IDENTIFY DEVICE命令获得硬盘的型号、容量等信息的。这个命令还能够报告硬盘的序列号、硬盘固件程序版本等。
IDENTIFY DEVICE命令是一个PIO命令,返回512字节内容,其中重要的信息如图6-18所示。
偏移量
长度
内容
20
20
硬盘序列号字符串(前后2个字节需颠倒顺序)
46
8
硬盘固件版本号字符串(前后2个字节需颠倒顺序)
54
40
硬盘型号字符串(前后2个字节需颠倒顺序)
120
4
硬盘容量(扇区数)
图6-18 IDENTIFY DEVICE信息格式
要求:
1. 采用PIO方式,读取并显示硬盘的型号、序列号、固件版本、容量,例如:
Model number: [ST
Serial number: [ 3LF1VQ9E]
Firmware reversion: [3.02 ]
Total size: [60011642880] bytes
2. 改进程序,使它能够读取从盘(DEV=1)的信息。
3. 改进程序,使它能够读取连接在第2个IDE通道上的硬盘。
4. 改进程序,使它搜索计算机上连接的所有IDE硬盘。
5. 本程序是否能够获取SATA硬盘的信息,为什么?
可参考IdeDiskInfo程序示例,显示结果如图6-19所示。
图6-19 IdeDiskInfo运行结果
- 实验6 I/O程序设计
- CC2530基础实验一 I/O实验
- 七、基本I/O接口电路设计实验
- 基本I/O接口电路设计实验
- 关于标准I/O的实验
- 基于Java I/O的应用程序实验
- Standard I/O 后续实验部分
- Proteus 8086 简单I/O读写实验
- 实验五 I/O 模型网络程序实验
- AVRWARE++开发笔记6:8路直接I/O口控制LED灯实验
- Win 32 多线程程序设计学习笔记之六:异步I/O(Overlapped I/O)
- 微软.NET FRAMEWORK 2.0 程序设计 第二章输入输出 I/O
- 课程 Java程序设计14:Advanced I/O (怀旧版)
- 《Win32多线程程序设计》(12)---overlapped I/O
- 《Win32多线程程序设计》(13)---理解I/O Completion Port
- 《Win32多线程程序设计》(14)---操作I/O Completion Port
- Linux程序设计——The Standard I/O Library
- Linux 程序设计学习笔记----ANSI C 文件I/O管理
- button 共享事件
- 动态添加checkbox,与获得checkbox
- C++函数(const与引用一点理解)
- smarty安装及初级使用
- Linux Mysql、Apache、Php卸载
- 实验6 I/O程序设计
- 什么叫虚拟主机?
- TMS IntraWeb Component Pack Pro release v3.0.0.0 for delphi 7组件包安装补充说明
- 建设JXTA技术论坛
- 为什么建立虚拟主机
- 在PHP中加密
- 项目经理的职责总结
- 怎么选择虚拟主机?
- Notice: Undefined index: ... 问题的解决方案