1、DOS 串口编程第一章串行通信接口串行通信使用单条数据线代替了并行通信的 8 位数据线,传输的距离更远。通信接口每次从 CPU 得到 8 位数据,然后通过一个并行入串行出的移位寄存器,转换成串行位,每次发送一位,将数据发送出去。同样,在接收端也必须有一个串行入并行出的移位寄存器来接收串行数据。并组合打包成一个字节。以串行方式进入数据线的是由 0 和 1 组成的数据,一组这样的数叫做一个字符,一个字符可能有 8 位,或者 7 位,6 位,5 位。在传输中,每个字符都要加上起始位和终止位,起始位总是 1 位,终止位可以是 1 位或 2 位。为了保证传输数据的正确性,有时还包括一位效验位。一般芯片都
2、允许编程时设定效验方式为奇效验,偶效验或者无效验。串行通信的数据传输速率用 bps(bits per second)来表示。另外,还有一种表示信号传输速率的单位是波特率(band rate)。波特率是一种信号调制单位,和 bps 不一定相等,它定义每秒钟传输的离散信号的数目。所谓的离散信号,就是指不均匀的,不连续的也不相关的信号。更详细的讲解请查阅相关文档。通信端口的传输速率从 110bps 到 115200bps,经验表明,波特率相当于 9600bps 的时候,传输相当稳定。1960 年电子工业协会(Electronics Industries Association,EIA) 制定了 RS
3、-232 接口标准,以后又陆续发布了修订版本,这是目前广泛应用于个人计算机上的串行接口,用于近程数据通信,连接一些外部设备。下图就是我们经常用到的 9 针 RS-232 插头,每个引脚的定义为: 引脚 方向 名称 描述 含义1 输入 CD Carrier Detect 数据载波检测2 输入 RXD Receive Data 数据接收端3 输出 TXD Transmit Data 数据发送端4 输出 DTR Data Terminal Ready 数据终端准备就绪(计算机)5 - SG System Ground 信号地6 输入 DSR Data Set Ready 数据设备准备就绪7 输出 R
4、TS Request to Send 请求发送(计算机要求发送数据)8 输入 CTS Clear to Send 清除发送(MODEM 准备接收数据)9 输入 RI Ring Indicator 响铃指示以上信号在通信过程中可能会被全部或者部分使用,把两台计算机通过串口连接起来,最简单的通讯仅需 TXD 及 RXD 及 SG 即可完成。TXD 3RXD 2GND 5DTR 4DSR 6CD 1RTS 7CTS 82 RXD3 TXD5 GND4 DTR6 DSR1 CD7 RTS8 CTS第二章端口设置IBM PC 和 80x86 兼容机可以连接 4 个串行端口,即 COM1COM4,相应的
5、BIOS 中的编号为 COM0COM3,但程序每次只能对其中一个端口进行存取。计算机启动时,自检程序就会测试 4 个 COM 端口是否存在,并把每个 COM 端口的 I/O 地址写到 BIOS 的数据区0040:00000040:0007 共 8 个字节,每个 COM 地址占用 2 个字节。如果系统没有连接串行端口,BIOS 数据区的这几个单元内容就成为 0。用 debug 可以查看 COM 端口地址。Cdebug-d 0040:0000 L080040:0000 F8 03 F8 02 E8 03 E8 02上例查看结果表明系统中有 4 个 COM 端口,对应 I/O 地址分别为 3F8,2
6、F8,3E8,2E8。每个 COM 端口都包括一组 8 位的寄存器,这四个地址都叫做基地址,也就是第一个寄存器的 I/O 地址,其他寄存器的地址按照递增的顺序排列 。COM1 的基地址是 3F8,COM2的基地址是 2F8,COM3 的基地址是 3E8,COM4 的基地址是 2E8。我们通过这些寄存器编程控制数据接收或者发送。COM1 及 COM3 使用 PC 机中断 4,COM2 及 COM4 使用中断 3。第三章寄存器1 寄存器组COM 端口的寄存器组如下表所示,共有 12 个寄存器,使用了 8 个地址,其中部分寄存器共用一个地址,由 DLAB=0/1 来区分。DLAB 是线路控制寄存器的
7、第 7 位。基地址 读/写 寄存器缩写 描述0 Write - 发送保持寄存器(DLAB=0)0 Read - 接收数据寄存器(DLAB=0)0 Read/Write - 波特率低八位(DLAB=1)1 Read/Write IER 中断允许寄存器1 Read/Write - 波特率高八位(DLAB=1)2 Read IIR 中断标识寄存器2 Write FCR FIFO 控制寄存器3 Read/Write LCR 线路控制寄存器4 Read/Write MCR MODEM 控制寄存器5 Read LSR 线路状态寄存器6 Read MSR MODEM 状态寄存器7 Read/Write -
8、Scratch Register2 中断允许寄存器(IER)位 描述 7 未使用6 未使用5 进入低功耗模式(16750)4 进入睡眠模式(16750)3 允许 MODEM 状态中断2 允许接收线路状态中断1 允许发送保持器空中断0 允许接收数据就绪中断Bit0 置 1 将允许接收到数据时产生中断,Bit1 置 1 时允许发送保持寄存器空时产生中断,Bit2 置 1 将在 LSR 变化时产生中断,相应的 Bit3 置位将在 MSR 变化时产生中断。3 中断识别寄存器(IIR)位 描述Bit6:7=00 无 FIFOBit6:7=01 允许 FIFO,但不可用Bit6:7=11 允许 FIFOB
9、it5 允许 64 字节 FIFO(16750)Bit4 未使用Bit3 16550 超时中断Bit2:1=00 MODEM 状态中断(CTS/RI/DTR/DCD)Bit2:1=01 发送保持寄存器空中断Bit2:1=10 接收数据就绪中断Bit2:1=11 接收线路状态中断Bit0=0 有中断产生Bit0=1 无中断产生IIR 为只读寄存器,Bit6:7 用来指示 FIFO 的状态,均为 0 时则无 FIFO,此时为 8250 或16450 芯片,为 01 时有 FIFO 但不可以使用,为 11 时 FIFO 有效并可以正常工作。Bit3 用来指示超时中断(16550/16750 ) 。B
10、it0 用来指示是否有中断发生,Bit1:2 标识具体的中断类型,这些中断具有不同的优先级别,其中 LSR 中断级别最高,其次是数据就绪中断,然后是发送寄存器空中断,而 MSR 中断级别最低。4 FIFO 控制寄存器(FCR)位 描述Bit7:6=00 1Byte 产生中断Bit7:6=01 4Byte 产生中断Bit7:6=10 8Byte 产生中断Bit7:6=11 14Byte 产生中断Bit5 允许 64 字节 FIFOBit4 未使用Bit3 DMA 模式选择Bit2 清除发送 FIFOBit1 清除接收 FIFOBit0 允许 FIFOFCR 可写但不可以读,该寄存器用来控制 16
11、550 或 16750 的 FIFO 寄存器。Bit0 置 1 将允许发送/接收的 FIFO 工作,Bit1 和 Bit2 置 1 分别用来清除接收及发送 FIFO。清除接收及发送 FIFO 并不影响移位寄存器。Bit1:2 可自行复位,因此无需使用软件对其清零。Bit6:7用来设定产生中断的级别,发送/接收中断将在发送/ 接收到对应字节数时产生。5 线路控制寄存器(LCR)位 描述Bit7=1 允许访问波特率因子寄存器 Bit7=0 允许访问接收/发送及中断允许寄存器Bit6 设置间断,0-禁止,1-设置Bit5:3=XX0 无校验 Bit5:3=001 奇校验Bit5:3=011 偶校验
12、Bit5:3=101 奇偶保持为 1Bit5:3=111 奇偶保持为 0 Bit2=0 1 位停止位 Bit2=1 2 位停止位(6-8 位数据位) , 1.5 位停止位(5 位数据位)Bit1:0=00 5 位数据位Bit1:0=01 6 位数据位Bit1:0=10 7 位数据位Bit1:0=11 8 位数据位 LCR 用来设定通讯所需的一些基本参数。Bit7 为 1 指定波特率因子寄存器有效,为 0 则指定发送/接收及 IER 有效。Bit6 置 1 会将发送端置为 0,这将会使接收端产生一个“间断”。Bit3-5 用来设定是否使用奇偶校验以及奇偶校验的类型, Bit3=1 时使用校验,B
13、it4 为 0 则为奇校验,1 为偶校验,而 Bit5 则强制校验为 1 或 0,并由 Bit4 决定具体为 0 或 1。Bit2用来设定停止位的长度,0 表示 1 位停止位,为 1 则根据数据长度的不同使用 1.5-2 位停止位。Bit0:1 用来设定数据长度。6 MODEM 控制寄存器(MCR)位 描述 Bit7 未使用Bit6 未使用 Bit5 自动流量控制(仅 16750) Bit4 环路测试 Bit3 辅助输出 2 Bit2 辅助输出 1 Bit1 设置 RTS Bit0 设置 DSR MCR 寄存器可读可写,Bit4=1 进入环路测试模式。Bit3-0 用来控制对应的管脚。7 线路
14、状态寄存器(LSR)位 描述 Bit7 FIFO 中接收数据错误 Bit6 发送移位寄存器空Bit5 发送保持寄存器空Bit4 间断Bit3 帧格式错 Bit2 奇偶错 Bit1 超越错Bit0 接收数据就绪LSR 为只读寄存器,当发生错误时 Bit7 为 1,Bit6 为 1 时标示发送保持及发送移位寄存器均空,Bit5 为 1 时标示仅发送保持寄存器空,此时,可以由软件发送下一数据。当线路状态为 0 时 Bit4 置位为 1,帧格式错时 Bit3 置位为 1,奇偶错和超越错分别将 Bit2 及 Bit1 置位为 1。Bit0 置位为 1 表示接收数据就绪。在接收和发送过程中,错误状态位(1
15、,2,3,4 位) 一旦被置为 1,则读入的接收数据已不是有效数据,所以在串行应用程序中,应检测数据传输是否出错。 奇偶错:通信线上(尤其是用电话线传输时 )的噪声引起某些数据位的改变,产生奇偶错,通检测出奇偶错时,要求正在接受的数据至少应重新发送一段。超越错:在上一个字符还未被 CPU 取走,又有字符要传送到数据寄存器里,则会引起超越错。帧格式错:当接收/发送器未收到一个字符数据的终止位,会引起帧格式错。这种错误可能是由于通信线上的噪声引起终止位的丢失,或者是由于接收方和发送方初始化不匹配。间断:间断有有时候并不能算是一个错误,而是为某些特殊的通信环境设置的“空格”状态。当间断为 1 时,这
16、说明接收的“空格”状态超过了一个完整的数据字传输时间。8 MODEM 状态寄存器(MSR)位 描述Bit7 载波检测Bit6 响铃指示 Bit5 DSR 准备就绪Bit4 CTS 有效Bit3 DCD 已改变Bit2 RI 已改变Bit1 DSR 已改变Bit0 CTS 已改变MSR 寄存器的高 4 位分别对应 MODEM 的状态线,低 4 位表示 MODEM 的状态线是否发生了变化。到此,串口的寄存器就介绍完了,下面就要讲如何编程。第四章 BIOS 串行通信功能BIOS int 14h 提供了串行数据通信功能,包括将串行口初始化为指定的字节结构和传输速率,检查控制器的状态,读写字符等功能。串
17、行通信口 BIOS 功能(int 14h)AH 功能 调用参数 返回参数0 初始化串行端口 AL=初始化参数DX=通信口号COM1=0,COM2=1COM3=2,COM4=3AH=线路状态AL=MODEL 状态1 向串行通信口写字符 AL=要写的字符DX=通信口号写字符成功:AH=0,AL=字符。写字符失败:AH 中,Bit7=1,Bit6:0=线路状态。2 从串行通信口读字符 DX=通信口号 读成功:AH 中,Bit7=0;AL=字符读失败:AH 中,Bit7=1,Bit6:0=线路状态3 取线路状态 DX=通信口号 AH=线路状态AL=MODEL 状态1 初始化串口int 14h 的 AH
18、=0 功能把指定的串行通信口初始化为希望的波特率,奇偶性,字长和终止位的位数,这些初始化参数设置在 AL 寄存器中,其各位的含义见下表: AL 意义 值Bit7:5 波特率 000=110 波特001=150 波特010=300 波特011=600 波特100=1200 波特101=2400 波特110=4800 波特111=9600 波特Bit4:3 效验 1=2 位Bit2 终止位 0=1 位Bit1:0 字长 10=7 位11=8 位例如要求 COM1 口的传输率为 2400 波特,字长为 8 位,1 位终止位,无奇偶效验:_asm mov AH, 0mov AL, 0A03h / 0A
19、03h =10100011mov DX, 0 / COM1int 14h / 调用 BIOS2 发送数据char my_data = 65h; /假设要发送 65h_asmmov AL, my_datamov AH, 1mov DX, 1 /通过 COM2 口发送出去int 14h 3 接受数据为了接收字符,首先要用 int 14h,AH=3 来获取 COM 端口的状态,其返回值在 AH 寄存器中。检查 AH 的第 0 位,它是数据准备好位,如果该位是 1,说明 COM 端口已经接收到字符。然后就可以用 int 14h,AH=2 功能,把字符读到 AL 寄存器中来。char LSR = 0;
20、/线路状态char my_data; /接收到的数据_asmmov AH, 3mov DX, 0 /COM1 的线路状态int 14hmov LSR, AHif(! (LSR goto somewhere;else /有数据到达 ,接受数据_asmmov AH, 2 /读字符mov DX, 0int 14hmov my_data, AL /接受到的字符保存在 my_data 中第五章 I/O 寄存器编程直接读写 I/O 寄存器可以更灵活更可靠地操作串口通信,很多程序都是用这种方式编写的。#define BASEADDR1 0x3F8 /COM1 的寄存器基地址1 初始化串口void InitC
21、om1()/不允许中断outportb(BASEADDR1 + 1, 0);/设置 DLAB=1, 允许访问波特率寄存器outportb(BASEADDR1 + 3, 0x80);/设置波特率 9600, 设置的参数可以在下面表格中查到outportb(BASEADDR1 + 0, 0x0C); /波特率低 8 位outportb(BASEADDR1 + 1, 0x00); /波特率高 8 位/设置 8 位数据位, 1 位停止位, 无校验, DLAB=0outportb(BASEADDR1 + 3, 0x03);在 DLAB=1 时去设定通讯所需的波特率。常用的波特率参数见下表: 速率(BPS
22、) 波特率高八位 波特率低八位50 09h 00h300 01h 80h600 00h C0h1200 00h 60h2400 00h 30h4800 00h 18h 9600 00h 0Ch19200 00h 06h38400 00h 03h57600 00h 02h115200 00h 01h 2 发送数据再多的语言都不如代码表达得清楚,所以我还是把代码摆在下面,适当地做点注释。下面这个函数是把一个字符通过 COM1 口发送出去,my_data 是要发送的字符。如果 COM1 不能够发送,就返回失败。bool send_com1(char my_data) char status = in
23、portb(BASEADDR1 + 5); /检查线路状态int count = 10000;/一直等到发送寄存器空,或者超时while(!(status status = inportb(BASEADDR1 + 5);if(count 0) /发送寄存器空 outportb(BASEADDR1 + 0, my_data); /发送字符return true;elsereturn false; /超时未发送3 接收数据下面这个函数是从 COM1 端口读一个字符,保存到 pData 指向的变量中,如果 COM1 口没有数据,就返回失败。bool recv_com1(char* pData);ch
24、ar status = inportb(BASEADDR1 + 5); /检查线路状态if(status /读一个字符return true;elsereturn false; /没有数据到达4 打开 FIFO 功能串口中所谓的 FIFO,也就是先入先出队列功能,说的就是一组接收缓冲寄存器,和一组发送保持寄存器。接收缓冲寄存器和发送保持寄存器的大小是 16 个字节,当 FIFO 功能关闭的时候,接收缓冲寄存器和发送保持寄存器都只能放一个数据,即相当于一个字节大小。我们最好开始从数据的接收和发送说起。数据接收:来自于线路上的数据首先进入接收移位寄存器(RSR),一个字符接收完成之后,数据移入接收
25、缓冲寄存器(RBR),RBR 实际就是一个 16 字节的 FIFO 队列。当中断设置时,串口控制器会根据 FIFO 的设置和 RBR 中数据的数目产生中断。 FIFO 功能关闭时,RBR只能放一个数据,如果主机设备来不及从 RBR 中读字符,那么串口接收到的下一个数据会把 RBR 覆盖,这样,就会有数据丢失。FIFO 功能打开时, RBR 中能够放 16 个数据,串口把完成的字符推入到 RBR 中,RBR 原有的数就会前移,这样主机读字符的时间变得宽裕了。数据发送:发送操作和接收操作相反,主机数据写入发送保持寄存器(THR),THR 也是一个 16 字节的 FIFO,然后数据移入发送移位寄存器
26、(TSR),之后送到线路上。当中断设置时,串口控制器也会根据 THR 中数据的数目产生中断。FIFO 功能打开时,主机可以把 16 字节的数据一次写入发送保持寄存器中。我们接着就可以应用 FIFO 功能来完善先前的程序。发送数据:/pData 指向要发送的数据,Len 是数据长度,返回是否发送成功bool fifo_send(char* pData, int Len)int count;/禁用 FIFO,目的是让程序第一时间感知字符是否发送成功outportb(BASEADDR1 + 2, 0x00);while(Len 0)/一直等到发送寄存器空,或者超时count = 10000;while(!(inportb(BASEADDR1 + 5) if(count 0)count-;if(count = 0) return false; /超时未发送elsereturn true; /确认,已经发送出去