串口调试小节之二 串口通讯原理
2015-06-08 10:49
363 查看
我们以接收方为例,详细讲解串口通讯的简单原理,一个串口数据的接收情况基本如下:
主要分了三层:
1、
硬件层:负责将比特位转换成字节型数据,并且将数据传输的通讯状态记录下来,产生中断让驱动读取,并且部分硬件具备硬件FIFO缓冲区的功能,具体见相关硬件手册。
2、
操作系统驱动层:驱动层的任务很简单,只是负责将数据按照控制方式和通讯状态,放入读缓存,并对产生的数据传输状态进行处理,例如硬件缓存溢出。
3、
应用层:只是负责读取数据和设置通讯方式。
所以简单来说,如果控制方式全部不设置,并且不考虑通讯状态的干扰,从应用层读取的数据就是串口链路的通讯的数据。
2、数据位:就是传输数据是按照多少位进行传输的,常用的是5,6,7,8.
3、校验位:只有1位,分奇校验和偶校验,数据位中 1
的个数是奇数,那么偶校验位就置为 1,从而使得总的 1
的个数是偶数;数据位中 1 的个数是偶数,那么奇校验位就置为 1,使得总的 1
的个数保持奇数不变。
4、停止位:分为1和2,用于通知传输结束,高电平,由于串口通讯一定要以一个起始位开始(低电平),那么停止位的高电平到起始位的低电平转换一定可以分辨出传输中的数据位变化,即使两边时钟不一致。
题外话:校验位和停止位设置错误并不一定会导致通讯错误,现代的硬件具备自适应的功能,即使设置错误,也可以被自动纠正,麻烦的是老硬件和新硬件做通讯,就会出现A能接收到B的数据,而B接不到A的数据,常常是这个原因。
5、流控制:
流控制分为两种,一种是硬件流控制,一种是软件流控制,流控制部分本身也比较复杂,下面只是按照大致的功能进行说明。
硬件流控制:硬件流控制常用的有RTS/CTS流控制和DTR/DSR(数据终端就绪/数据设置就绪)流控制。硬件流控制必须将相应的电缆线连上,用RTS/CTS(请求发送/清除发送)流控制时,应将通讯两端的RTS、CTS线对应相连,数据终端设备(如计算机)使用RTS来起始调制解调器或其它数据通讯设备的数据流,而数据通讯设备(如调制解调器)则用CTS来起动和暂停来自计算机的数据流。这种硬件握手方式的过程为:我们在编程时根据接收端缓冲区大小设置一个高位标志(可为缓冲区大小的75%)和一个低位标志(可为缓冲区大小的25%),当缓冲区内数据量达到高位时,我们在接收端将CTS线置低电平(送逻辑0),当发送端的程序检测到CTS为低后,就停止发送数据,直到接收端缓冲区的数据量低于低位而将CTS置高电平。RTS则用来标明接收设备有没有准备好接收数据。
常用的流控制还有还有DTR/DSR(数据终端就绪/数据设置就绪)。我们在此不再详述。由于流控制的多样性,我个人认为,当软件里用了流控制时,应做详细的说明,如何接线,如何应用。
软件流控制:由于电缆线的限制,我们在普通的控制通讯中一般不用硬件流控制,而用软件流控制。一般通过XON/XOFF来实现软件流控制。常用方法是:当接收端的输入缓冲区内数据量超过设定的高位时,就向数据发送端发出XOFF字符(十进制的19或Control-S,设备编程说明书应该有详细阐述),发送端收到XOFF字符后就立即停止发送数据;当接收端的输入缓冲区内数据量低于设定的低位时,就向数据发送端发出XON字符(十进制的17或Control-Q),发送端收到XON字符后就立即开始发送数据。一般可以从设备配套源程序中找到发送的是什么字符。
应该注意,若传输的是二进制数据,标志字符也有可能在数据流中出现而引起误操作,这是软件流控制的缺陷,而硬件流控制不会有这个问题,所以一般的串口通讯需要将软件流控制屏蔽掉。
6、其他
其他部分还有几个,包括CREAD等的设置,由于在基本的通讯里不常用,部分在其他地方讲解,这里就不做详细介绍。
2.2.1
串口一般可以分两种,一种是主板自带的串口,一般是中断3和4,地址分别为2F8和3F8,另一种是由类似super/IO上的共享串口,中断号可配置成一个,这部分一般可在BIOS部分找到。
串口端口映射到PC机的主板上后,IO端口功能也比较固定,一般一个串口占用8个字节的IO端口,负责完成串口收发工作,这里对应的中断和IO端口地址在BIOS都能找到,并且各个端口的在linux驱动中都能找到相关对应。
详细可以见下,这部分内容需要根据需要进行查找:
在PC上,串口的寄存器是要占用相应的IO空间的,也就是说我们可以通过向相应的IO口写值来改变寄存器的配置。如表2-1所示:
表 2-1 IO端口映射
上面这张表是默认的串口默认使用的I/O地址和中断向量号,在一些系统上,这些值有可能会变动,在PC机上可在BIOS里查看。
接下来要讲的是串口所使用的IO端口了,可暂时略过,需要时做为表进行查找。
对于每一个UART来说,有8个字节的I/O用于其寄存器的访问。如表1-2所示:表中的DLAB是LCR寄存器的Bit7,位于base+3处。
下面将详细地介绍每一个寄存器。
(1)RBR :
RBR在FIFO未开启的时候,只包含当前接收到的一个Byte。当FIFO被启用的时候,每一次的读操作都会返回FIFO头部的一个数据,直到FIFO空为止。当FIFO为空时LSR的Bit
0会由1变为0。
(2)THR :
THR主要用于缓冲输出数据,当FIFO未被启用的时候,只能缓冲一个Byte的数据,当FIFO启用时,可以缓冲多个Byte的数据,当LSR的Bit
1为“1”时表示缓冲器为空,此时可以写入数据,但是没有一个寄存器可以表示当前FIFO中的有效数据个数。
(3)IER :
IER是中断使能寄存器,在使用串口通信时使用中断方式无疑是高效的。使用中断可以不必定时地去查询寄存器的状态,UART会自动地向处理器发起中断信号。除非UART被配置成中断模式,否则中断是不会自动产生的。
(4)IIR :
当UART的状态改变的时候会产生中断,但具体是哪种状态的改变产生的中断呢?如果软件在中断产生时想要识别当前的状态改变信息,如何办呢?这个时候,IIR就派上用场了。
(5)FCR : FIFO控制寄存器FIFO
FCR主要用于控制UART的FIFO动作。
THR和IER寄存器一样被映射同样的IO口上。它们只有在串口初始化的时候才会被配置。
常用的串口配置有:
l 8个数据位,1个停止位,无奇偶校验
l 7个数据位,1个停止位,偶校验
其中bit 0用于接收数据状态,bit 5用于发送数据状态。
* 10^6 / 16= 115200bps。但有的modem使用24MHz的晶体作为初始频率,因此最大的传输速率可以达到1.5Mbps。
2.2.2
驱动层在任务上比较简单:只做几件事情。
1、
负责串口硬件层设置。一般驱动都会给一个默认的通讯设置作为基本通讯测试,每个驱动的设置也不完全一样。
2、
负责调用中断将数据送到tty处理层。这里的数据如果设置为所有数据全部到达应用层,则串口从应用层收到数据就是串口链路的数据,不需要抓包之类的工具。
这里会有一个丢数据的问题,那么一定要先查看丢数据的规律是什么,对应的ASCII码是什么,一般情况下都是串口设置的有问题。
3、
负责检测串口控制IO端口状态并做相应的处理。
例如,串口硬件缓冲区满的时候会设置LSR的overrun位,出现inputs overruns的调试信息,Linux驱动在检测到这个标志会自动为该数据添加一个0,如过这个0添加和你预期的不一致,很可能是串口中断注册失败了,导致驱动默认分配了一个中断导致的。
关于驱动层的详细分析,会在后面做详细说明。
主要分了三层:
1、
硬件层:负责将比特位转换成字节型数据,并且将数据传输的通讯状态记录下来,产生中断让驱动读取,并且部分硬件具备硬件FIFO缓冲区的功能,具体见相关硬件手册。
2、
操作系统驱动层:驱动层的任务很简单,只是负责将数据按照控制方式和通讯状态,放入读缓存,并对产生的数据传输状态进行处理,例如硬件缓存溢出。
3、
应用层:只是负责读取数据和设置通讯方式。
所以简单来说,如果控制方式全部不设置,并且不考虑通讯状态的干扰,从应用层读取的数据就是串口链路的通讯的数据。
2.1硬件层设置
1、波特率:对于串口而言波特率就是每秒传输多少个比特位,比如9600,就是每秒传输9600个比特位,常用的是8个比特位1字节。那么每一位是怎么分辨的,答案是完全靠时钟分辨的。2、数据位:就是传输数据是按照多少位进行传输的,常用的是5,6,7,8.
3、校验位:只有1位,分奇校验和偶校验,数据位中 1
的个数是奇数,那么偶校验位就置为 1,从而使得总的 1
的个数是偶数;数据位中 1 的个数是偶数,那么奇校验位就置为 1,使得总的 1
的个数保持奇数不变。
4、停止位:分为1和2,用于通知传输结束,高电平,由于串口通讯一定要以一个起始位开始(低电平),那么停止位的高电平到起始位的低电平转换一定可以分辨出传输中的数据位变化,即使两边时钟不一致。
题外话:校验位和停止位设置错误并不一定会导致通讯错误,现代的硬件具备自适应的功能,即使设置错误,也可以被自动纠正,麻烦的是老硬件和新硬件做通讯,就会出现A能接收到B的数据,而B接不到A的数据,常常是这个原因。
5、流控制:
流控制分为两种,一种是硬件流控制,一种是软件流控制,流控制部分本身也比较复杂,下面只是按照大致的功能进行说明。
硬件流控制:硬件流控制常用的有RTS/CTS流控制和DTR/DSR(数据终端就绪/数据设置就绪)流控制。硬件流控制必须将相应的电缆线连上,用RTS/CTS(请求发送/清除发送)流控制时,应将通讯两端的RTS、CTS线对应相连,数据终端设备(如计算机)使用RTS来起始调制解调器或其它数据通讯设备的数据流,而数据通讯设备(如调制解调器)则用CTS来起动和暂停来自计算机的数据流。这种硬件握手方式的过程为:我们在编程时根据接收端缓冲区大小设置一个高位标志(可为缓冲区大小的75%)和一个低位标志(可为缓冲区大小的25%),当缓冲区内数据量达到高位时,我们在接收端将CTS线置低电平(送逻辑0),当发送端的程序检测到CTS为低后,就停止发送数据,直到接收端缓冲区的数据量低于低位而将CTS置高电平。RTS则用来标明接收设备有没有准备好接收数据。
常用的流控制还有还有DTR/DSR(数据终端就绪/数据设置就绪)。我们在此不再详述。由于流控制的多样性,我个人认为,当软件里用了流控制时,应做详细的说明,如何接线,如何应用。
软件流控制:由于电缆线的限制,我们在普通的控制通讯中一般不用硬件流控制,而用软件流控制。一般通过XON/XOFF来实现软件流控制。常用方法是:当接收端的输入缓冲区内数据量超过设定的高位时,就向数据发送端发出XOFF字符(十进制的19或Control-S,设备编程说明书应该有详细阐述),发送端收到XOFF字符后就立即停止发送数据;当接收端的输入缓冲区内数据量低于设定的低位时,就向数据发送端发出XON字符(十进制的17或Control-Q),发送端收到XON字符后就立即开始发送数据。一般可以从设备配套源程序中找到发送的是什么字符。
应该注意,若传输的是二进制数据,标志字符也有可能在数据流中出现而引起误操作,这是软件流控制的缺陷,而硬件流控制不会有这个问题,所以一般的串口通讯需要将软件流控制屏蔽掉。
6、其他
其他部分还有几个,包括CREAD等的设置,由于在基本的通讯里不常用,部分在其他地方讲解,这里就不做详细介绍。
2.2操作系统驱动层
这部分主要介绍操作系统的相关任务,一部分也是硬件的知识,但是由于在调试驱动过程中需要了解,将这部分也移动到这里。操作系统需要了解串口中断的配置和设置,这里也以PC机为例,研祥的MEC5003的工控机和PC机也是一个模式:2.2.1
串口中断的基本配置:
串口一般可以分两种,一种是主板自带的串口,一般是中断3和4,地址分别为2F8和3F8,另一种是由类似super/IO上的共享串口,中断号可配置成一个,这部分一般可在BIOS部分找到。串口端口映射到PC机的主板上后,IO端口功能也比较固定,一般一个串口占用8个字节的IO端口,负责完成串口收发工作,这里对应的中断和IO端口地址在BIOS都能找到,并且各个端口的在linux驱动中都能找到相关对应。
详细可以见下,这部分内容需要根据需要进行查找:
在PC上,串口的寄存器是要占用相应的IO空间的,也就是说我们可以通过向相应的IO口写值来改变寄存器的配置。如表2-1所示:
Default I/O addresses and IRQ's on a PC system | ||
COM1 | 0x3F8 - 0x3FF | 4 |
COM2 | 0x2F8 - 0x2FF | 3 |
COM3 | 0x3E8 - 0x3EF | 4 |
COM4 | 0x2E8 - 0x2EF | 3 |
上面这张表是默认的串口默认使用的I/O地址和中断向量号,在一些系统上,这些值有可能会变动,在PC机上可在BIOS里查看。
接下来要讲的是串口所使用的IO端口了,可暂时略过,需要时做为表进行查找。
对于每一个UART来说,有8个字节的I/O用于其寄存器的访问。如表1-2所示:表中的DLAB是LCR寄存器的Bit7,位于base+3处。
UART register to port conversion table | ||||
| DLAB = 0 | DLAB = 1 | ||
I/O port | Read | Write | Read | Write |
base | RBR receiver buffer | THR transmitter holding | DLL divisor latch LSB | |
base + 1 | IER interrupt enable | IER interrupt enable | DLM divisor latch MSB | |
base + 2 | IIR interrupt identification | FCR FIFO control | IIR interrupt identification | FCR FIFO control |
base + 3 | LCR line control | |||
base + 4 | MCR modem control | |||
base + 5 | LSR line status | – factory test | LSR line status | – factory test |
base + 6 | MSR modem status | – not used | MSR modem status | – not used |
base + 7 | SCR scratch |
(1)RBR :
接收缓冲寄存器Receiver buffer register (RO)
RBR在FIFO未开启的时候,只包含当前接收到的一个Byte。当FIFO被启用的时候,每一次的读操作都会返回FIFO头部的一个数据,直到FIFO空为止。当FIFO为空时LSR的Bit0会由1变为0。
(2)THR :
发送保持寄存器Transmitter holding register (WO)
THR主要用于缓冲输出数据,当FIFO未被启用的时候,只能缓冲一个Byte的数据,当FIFO启用时,可以缓冲多个Byte的数据,当LSR的Bit1为“1”时表示缓冲器为空,此时可以写入数据,但是没有一个寄存器可以表示当前FIFO中的有效数据个数。
(3)IER :
中断使能寄存器Interrupt enable register (R/W)
IER是中断使能寄存器,在使用串口通信时使用中断方式无疑是高效的。使用中断可以不必定时地去查询寄存器的状态,UART会自动地向处理器发起中断信号。除非UART被配置成中断模式,否则中断是不会自动产生的。IER : Interrupt enable register | |
Bit | Description |
0 | Received data available |
1 | Transmitter holding register empty |
2 | Receiver line status register change |
3 | Modem status register change |
4 | Sleep mode (16750 only) |
5 | Low power mode (16750 only) |
6 | reserved |
7 | |
(4)IIR :
中断识别寄存器Interrupt identification register (RO)
当UART的状态改变的时候会产生中断,但具体是哪种状态的改变产生的中断呢?如果软件在中断产生时想要识别当前的状态改变信息,如何办呢?这个时候,IIR就派上用场了。IIR : Interrupt identification register | |||||
Bit | Value | Description | Reset by | ||
0 | 0 | Interrupt pending | – | ||
1 | No interrupt pending | – | |||
1,2,3 | Bit 3 | Bit 2 | Bit 1 | | |
0 | 0 | 0 | Modem status change | MSR read | |
0 | 0 | 1 | Transmitter holding register empty | IIR read or THR write | |
0 | 1 | 0 | Received data available | RBR read | |
0 | 1 | 1 | Line status change | LSR read | |
1 | 1 | 0 | Character timeout (16550) | RBR read | |
4 | 0 | Reserved | – | ||
5 | 0 | Reserved (8250, 16450, 16550) | – | ||
1 | 64 byte FIFO enabled (16750) | – | |||
6,7 | Bit 7 | Bit 6 | | | |
0 | 0 | No FIFO | – | ||
1 | 0 | Unusable FIFO (16550 only) | – | ||
1 | 1 | FIFO enabled | – |
(5)FCR : FIFO控制寄存器FIFO
control register (WO)
FCR主要用于控制UART的FIFO动作。FCR : FIFO control register | |||
Bit | Value | Description | |
0 | 0 | Disable FIFO's | |
1 | Enable FIFO's | ||
1 | 0 | – | |
1 | Clear receive FIFO | ||
2 | 0 | – | |
1 | Clear transmit FIFO | ||
3 | 0 | Select DMA mode 0 | |
1 | Select DMA mode 1 | ||
4 | 0 | Reserved | |
5 | 0 | Reserved (8250, 16450, 16550) | |
1 | Enable 64 byte FIFO (16750) | ||
6,7 | Bit 7 | Bit 6 | Receive FIFO interrupt trigger level |
0 | 0 | 1 byte | |
0 | 1 | 4 bytes | |
1 | 0 | 8 bytes | |
1 | 1 | 14 bytes |
(6)LCR : Line control register (R/W)
LCR,线控寄存器,主要用于配置stop bit的位数,奇偶校验以及DLL和DLM寄存器的可访问性。DLL和DLM这些寄存器与RBR,THR和IER寄存器一样被映射同样的IO口上。它们只有在串口初始化的时候才会被配置。
常用的串口配置有:
l 8个数据位,1个停止位,无奇偶校验
l 7个数据位,1个停止位,偶校验
LCR : line control register | ||||
Bit | Value | Description | ||
0,1 | Bit 1 | Bit 0 | Data word length | |
0 | 0 | 5 bits | ||
0 | 1 | 6 bits | ||
1 | 0 | 7 bits | ||
1 | 1 | 8 bits | ||
2 | 0 | 1 stop bit | ||
1 | 1.5 stop bits (5 bits word) 2 stop bits (6, 7 or 8 bits word) | |||
3,4,5 | Bit 5 | Bit 4 | Bit 3 | |
x | x | 0 | No parity | |
0 | 0 | 1 | Odd parity | |
0 | 1 | 1 | Even parity | |
1 | 0 | 1 | High parity (stick) | |
1 | 1 | 1 | Low parity (stick) | |
6 | 0 | Break signal disabled | ||
1 | Break signal enabled | |||
7 | 0 | DLAB : RBR, THR and IER accessible | ||
1 | |
(7)MCR : Modem control register(R/W)
MCR主要用于控制Modem。这个不多说。MCR : Modem control register | |
Bit | Description |
0 | Data terminal ready |
1 | Request to send |
2 | Auxiliary output 1 |
3 | Auxiliary output 2 |
4 | Loopback mode |
5 | Autoflow control (16750 only) |
6 | Reserved |
7 | Reserved |
(8)LSR : Line status register (RO)
LSR这个寄存器很有用,它作为状态寄存器反映了当前通信的状态。LSR : Line status register | |
Bit | Description |
0 | Data available |
1 | Overrun error |
2 | Parity error |
3 | Framing error |
4 | Break signal received |
5 | THR is empty |
6 | THR is empty, and line is idle |
7 | Errornous data in FIFO |
(9)MSR : Modem status register (RO)
MSR反映了Modem的状态信息,这个也不多说。MSR : Modem status register | |
Bit | Description |
0 | change in Clear to send |
1 | change in Data set ready |
2 | trailing edge Ring indicator |
3 | change in Carrier detect |
4 | Clear to send |
5 | Data set ready |
6 | Ring indicator |
7 | Carrier detect |
(10)SCR : Scratch register (R/W)
这个寄存器也用得很少,在此不多讲。(11)DLL and DLM : Divisor latchregisters (R/W)
这两个寄存器合起来用于配置分频器,以得到合适的波特率。一般而言,在pc上用于串口通信的晶体的频率为:1.8432MHz。用这个频率除以16后用作串口的最大频率。所以一般串的最大传输速率为:1.8432* 10^6 / 16= 115200bps。但有的modem使用24MHz的晶体作为初始频率,因此最大的传输速率可以达到1.5Mbps。
DLL and DLM : Divisor latch registers | |||
Speed (bps) | Divisor | DLL | DLM |
50 | 2,304 | 0x00 | 0x09 |
300 | 384 | 0x80 | 0x01 |
1,200 | 96 | 0x60 | 0x00 |
2,400 | 48 | 0x30 | 0x00 |
4,800 | 24 | 0x18 | 0x00 |
9,600 | 12 | 0x0C | 0x00 |
19,200 | 6 | 0x06 | 0x00 |
38,400 | 3 | 0x03 | 0x00 |
57,600 | 2 | 0x02 | 0x00 |
115,200 | 1 | 0x01 | 0x00 |
2.2.2
驱动层任务
驱动层在任务上比较简单:只做几件事情。1、
负责串口硬件层设置。一般驱动都会给一个默认的通讯设置作为基本通讯测试,每个驱动的设置也不完全一样。
2、
负责调用中断将数据送到tty处理层。这里的数据如果设置为所有数据全部到达应用层,则串口从应用层收到数据就是串口链路的数据,不需要抓包之类的工具。
这里会有一个丢数据的问题,那么一定要先查看丢数据的规律是什么,对应的ASCII码是什么,一般情况下都是串口设置的有问题。
3、
负责检测串口控制IO端口状态并做相应的处理。
例如,串口硬件缓冲区满的时候会设置LSR的overrun位,出现inputs overruns的调试信息,Linux驱动在检测到这个标志会自动为该数据添加一个0,如过这个0添加和你预期的不一致,很可能是串口中断注册失败了,导致驱动默认分配了一个中断导致的。
关于驱动层的详细分析,会在后面做详细说明。
相关文章推荐
- 基于GO语言最简单的HTTP服务器
- 九度OJ-题目1523:从上往下打印二叉树
- Qt Creator 窗体控件自适应窗口大小布局
- 1001. 杀死吸引力(3n+1)猜想 (15)(ZJUPAT 数学)
- css sprite、background-size、Retina屏幕详解
- 部署用到的常用命令
- java各种协议相关(杂)
- 牛人对STM32的NVIC_PriorityGroupConfig使用及优先级分组方式理解
- lua协程coroutine
- Effective C++ 条款46
- android AsyncTask介绍
- POJ3294:Life Forms(后缀数组)
- 一个奇葩站点上传绕过与ssh免密码登陆
- Java和android及iOS对接RSA加密经验
- centos7 配置虚拟交换机(物理交换机truck端口设置)(使用brctl)
- Android通讯录操作
- 触摸事件对应的消息响应
- ASP.net MVC4 AsyncController异步action无法异步执行?
- C++ tips: C++ Dark Corner 0 标准流格式化输出
- JavaScript中exec函数用法实例分析