数据在单条传输线上,一位接一位地按顺序传送的方式称为串行通信。串行通信有两种方式:异步方式和同步方式。实现串行通信的接口叫串行接口,它主要的功能就是实现CPU与输入输出设备之间的串并行转换。把从CPU传送过来的并行数据转换成串行从输出设备输出,以及把从输入设备输入的串行数据转换成并行数据送给CPU。串行通信中,因为数据是一位一位地传输,所以为了是接收端能够识别接收到的每位数据的具体意思,收发双方就需要遵守相同的约定,即串口通信协议,它用来保证接收方知晓数据传输的开始及结束,以及判断接收数据的正确性。在异步串行通信中,在发送数据之前会先发送一个开始位,表示数据传输的开始,在发送完所有的数据后,会再多发送一个结束位,表示整个传输过程结束。为了保证数据的正确性,通常会采用奇偶校验法来验证接收到的数据,所以在传输的原始数据后面会再加上奇偶校验位。在同步串行通信中,收发双方采用同一个时钟源来双方的同步,所以传输数据时无需起始位和停止位。同步的方法有外同步和内同步两种,外同步法是在发送数据之前向接收端发送一串时钟脉冲,接收端按这个时钟频率调整自己的时序,是接收时钟频率锁定在接收到的时钟频率上,内同步法是接收端从接收到的数据信息波形本身提取同步的方法。
RS-232C定义了串行通信的物理接口,主要包括机械指标和电气指标。RS-232C标准的连接器有DB25和DB9两种,目前PC上常见的COM口就是DB9类型的连接器。DB9包含9个信号Pin,分别是TxD/RxD,RTS/CTS,DTR/DSR,SG/DCD/RI。TxD/RxD是用来发送和接收数据的,RTS/CTS是用来实现硬件流量控制的,当数据终端设备准备好数据时,会发出RTS信号通知数据通信设备,如果数据通信设备已经准备好接收数据,就会发送一个CTS信号来响应RTS信号。DTR/DSR是数据终端设备和数据通信设备之间的联络信号,DTR表明数据终端设备准备就绪,而DSR用来表明数据通信设备准备就绪。SG为信号地,用来提供参考电平。当通信双方之间有接调制解调器的时候,DCD/RI用来反映调制解调器所侦测到的状态。
异步串行通信的核心是通用异步收发传输器,通常称为UART。常见的UART芯片有INS8250,PC16550,PC16650,PC16750。以PC16650为例,与CPU连接一侧,它有8条并行数据线实现与CPU的并行数据传输,另外还有一个选择和控制逻辑单元,负责实现芯片片选以及IO寄存器选址功能。与输入输出设备连接一侧,由接收移位寄存器和发送移位寄存器实现串行数据的接收和发送。同时还有一个Modem控制逻辑单元,负责数据终端设备和数据通信设备之间的联络信号以及流量控制。在芯片内部,有12个8位寄存器,但是PC16550与CPU之间只有三个寻址Pin脚用来寻址,所以最多只能访问到8个IO端口,也就是说只能存取8个寄存器,所以需要结合读写信号和线路控制寄存器LCR的最高位DLAB位来协同寻址。
DLAB | A2A1A0 | 被访问的寄存器 |
0 | 000 | 接收数据寄存器RBR(读) 发送保持寄存器THR(写) |
0 | 001 | 中断允许寄存器IER |
1 | 000 | 波特率除数寄存器(低字节) |
1 | 001 | 波特率除数寄存器(高字节) |
X | 010 | 中断识别寄存器IIR(只读) FIFO控制器FCR(只写) |
X | 011 | 线路控制寄存器LCR |
X | 100 | MODEM控制寄存器MCR |
X | 101 | 线路状态寄存器LSR |
X | 110 | MODEM状态寄存器MSR |
X | 111 | 暂存 |
struct uart_driver {
struct module *owner;
const char *driver_name;
const char *dev_name;
int major;
int minor;
int nr;
struct console *cons;
/*
* these are private; the low level driver should not
* touch these; they should be initialised to NULL
*/
struct uart_state *state;
struct tty_driver *tty_driver;
};
struct uart_state {
struct tty_port port;
int pm_state;
struct circ_buf xmit;
struct tasklet_struct tlet;
struct uart_port *uart_port;
};
uart_icount为串口信息计数器,包含了发送字符计数、接受字符计数等,在串口的发送中断处理函数和接收中断处理函数中,需要管理这些计数信息。uart_ops 跟tty_operations类似,是实现对UART操作的函数接口集合。
struct uart_icount {
__u32 cts;
__u32 dsr;
__u32 rng;
__u32 dcd;
__u32 rx;
__u32 tx;
__u32 frame;
__u32 overrun;
__u32 parity;
__u32 brk;
__u32 buf_overrun;
};
struct uart_ops {
unsigned int (*tx_empty)(struct uart_port *);
void (*set_mctrl)(struct uart_port *, unsigned int mctrl);
unsigned int (*get_mctrl)(struct uart_port *);
void (*stop_tx)(struct uart_port *);
void (*start_tx)(struct uart_port *);
void (*send_xchar)(struct uart_port *, char ch);
void (*stop_rx)(struct uart_port *);
void (*enable_ms)(struct uart_port *);
void (*break_ctl)(struct uart_port *, int ctl);
int (*startup)(struct uart_port *);
void (*shutdown)(struct uart_port *);
void (*flush_buffer)(struct uart_port *);
void (*set_termios)(struct uart_port *, struct ktermios *new,
struct ktermios *old);
void (*set_ldisc)(struct uart_port *);
void (*pm)(struct uart_port *, unsigned int state,
unsigned int oldstate);
int (*set_wake)(struct uart_port *, unsigned int state);
/*
* Return a string describing the type of the port
*/
const char *(*type)(struct uart_port *);
/*
* Release IO and memory resources used by the port.
* This includes iounmap if necessary.
*/
void (*release_port)(struct uart_port *);
/*
* Request IO and memory resources used by the port.
* This includes iomapping the port if necessary.
*/
int (*request_port)(struct uart_port *);
void (*config_port)(struct uart_port *, int);
int (*verify_port)(struct uart_port *, struct serial_struct *);
int (*ioctl)(struct uart_port *, unsigned int, unsigned long);
#ifdef CONFIG_CONSOLE_POLL
void (*poll_put_char)(struct uart_port *, unsigned char);
int (*poll_get_char)(struct uart_port *);
#endif
};