异步通信--以一个字符为单位,通信中两个字符间的时间间隔不固定的,然而同一个字符中的两个相邻位之间的时间间隔是固定 的。
通信协议----是指通信双方约定的一些规则。在使用异步串口传送一个字符的信息时,对数据格式有如下约定:规定有空闲位,起始位,资料位,奇偶校验位,停止位。
波特率---表示每秒传送的二进制位数。
注:异步通信是按字符传输的,接收设备在收到起始信号之后只要在一个字符的传输时间内能和发送设备保持同步就能正确接收。
终端概述
在Linux中,TTY(终端)是一类字符设备的统称,包括了3种类型:控制台,串口和伪终端。
控制台
供内核使用的终端为控制台。控制台在Linux启动时,通过命令console=…指定,如果没有指定控制台,系统把第一个注册的终端(tty)作为控制台。
1. 控制台是一个虚拟的终端,它必须映射到真正的终端上。
2. 控制台可以简单的理解为printk输出的地方。
3. 控制台是个只输出的设备,功能很简单,只能在内核中访问。
伪终端
伪终端设备是一种特殊的终端设备, 由主-从两个成对的设备构成, 当打开主设备时, 对应的从设备随之打开, 形成连接状态。输入到主设备的数据成为从设备的输出,输入到从设备的数据成为主设备的输出, 形成双向管道。伪终端设备常用于远程登录服务器来建立网络和终端的关联。当通过telnet远程登录到另一台主机时,telnet进程与远程主机的telnet服务器相连接. telnet服务器使用某个主设备并通过对应的从设备与telnet进程相互通信。
终端体系
在Linux中,TTY体系分为:TTY核心,TTY线路规程,TTY驱动3部分。TTY核心从用户获取要发送给TTY设备的数据,然后把数据传递给TTY线路规程, 它对数据进行处理后,负责把数据传递到TTY驱动程序,TTY驱动程序负责格式化数据,并通过硬件发送出去。
从硬件收到的数据向上通过TTY驱动, 进入TTY线路规程, 再进入TTY核心, 最后被用户获取。TTY驱动可以直接和TTY核心通讯, 但是通常TTY线路规程会修改在两者之间传送的数据。TTY驱动不能直接和线路规程通信,甚至不知道它的存在,线路规程的工作是格式化从用户或者硬件收到的数据. 这种格式化常常实现为一个协议, 如PPP或Bluetooth。
读操作
TTY驱动从硬件收到数据后,负责把数据传递到TTY 核心,TTY核心将从TTY驱动收到的数据缓存到一个tty_flip_buffer 类型的结构中。该结构包含两个数据数组。从TTY设备接收到的数据被存储于第一个数组,当这个数组满, 等待数据的用户将被通知。当用户从这个数组读数据时, 任何从TTY驱动新来的数据将被存储在第2个数组。当第二个数组存满后,数据再次提交给用户, 并且驱动又开始填充第1个数组,以此交替。
驱动描述
Linux内核使用uart_driver描述串口驱动,它包含串口设备的驱动名、设备名、设备号等信息。
struct uart_driver {
struct module *owner;
const char *driver_name; //驱动名
const char *dev_name; //设备名
int major; //主设备号
int minor; //起始次设备号
int nr; //设备数
struct console *cons;
struct uart_state *state;
struct tty_driver *tty_driver;
};
注册驱动
Linux为串口驱动注册提供了如下接口:
int uart_register_driver(struct uart_driver *drv)
端口描述
Uart_port用于描述一个UART端口(直接对应于一个串口)的I/O端口或I/O内存地址、FIFO大小、端口类型等信息
struct uart_port
{
spinlock_t lock; /* 端口锁*/
unsigned int iobase; /* IO端口基地址*/
unsigned char __iomem *membase; /* IO内存基地址*/
unsigned int irq; /* 中断号*/
unsigned int uartclk; /* UART时钟*/
unsigned char fifosize; /* 传输fifo大小*/
…………………………………………………………
}
添加端口
串口核心层提供如下函数来添加1个端口:
int uart_add_one_port(struct uart_driver *drv, struct uart_port *port)
操作串口
uart_ops 定义了针对串口的一系列操作,包括发送、接收及线路设置等。
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); //发送xchar
void(*stop_rx)(struct uart_port*); //停止接收
。。。。。。。。。。。。。。。。。。
}
操作流程
1. 定义一个uart_driver的变量,并初始化;
2. 使用uart_register_driver来注册这个驱动;
3. 初始化uart_port和ops函数表;
4. 调用uart_add_one_port()添加初始化好的uart_port。