一、终端控制台体系
1>数据通信
数据通信的基本方式可分为并行通信与串行通信两种:
并行通信:利用多条数据线将数据的各位同时传送。它的特点是传输速度快,适用于短距离通信。
串行通信:利用一条数据线将数据一位位的顺序传送。特点是通信线路简单,利用简单的线缆就可以实现通信,低成
本,适用于远距离通信。
2>异步通信
异步通信以一个字符为传输单位,通信中两个字符间的时间间隔是不固定的,然而同一个字符中的两个相邻位之间的
时间间隔是固定的。
通信协议:是指通信双方约定的一些规则。在使用异步串口传送一个字符的信息时,对数据格式有如下约定:规定
有空闲位、起始位、资料位、奇偶校验位、停止位。
起始位:先发一个逻辑"0"信号,表示传输字符的开始。
数据位:紧接在起始位之后。数据位的个数可以是4、5、6、7、8等,从最低位开始传送,考时钟定位。
奇偶校验位:数据位加上这一位后,使得"1"的位数为偶数(偶校验)或奇数(奇校验),依次检验数据传送的正确性。
停止位:它是一个字符数据的结束标志。
空闲位:处于逻辑"1"状态,表示当前线路上没有数据传送。
3>波特率
是衡量数据传送速率的指针。表示每秒钟传送的二进制位数。例如数据传送速率为120字符/秒,
而每一个字符为10位,则其传送的波特率为10*120 = 1200位/秒 = 1200波特。
注:异步通信时按字符传输的,接收设备在收到起始信号之后只要在一个字符的传输时间内能和发送
设备保持同步就能正确接收。
4>传送方式
单工方式:数据始终从A设备发向B设备
半双工方式:资料能从A设备传送到B设备,也能从B设备传送到A设备。但在任何时候都不能在两个方向上同时传送,即每次只能有一个设备发送,另一个设备接收。
全双工方式:允许通信双方同时进行发送和接收。这时,A设备在发送的同时也可以接收,B设备也一样。
5>终端概述
在Linux中,TTY(终端)是一类字符设备的统称,包括了3种类型:控制台,串口和伪终端。
1、控制台
供内核使用的终端为控制台。控制台在Linux启动时,通过命令console=...指定,如果没有指定控制台,系统把第一个注册的中断(tty)作为控制台。
1、控制台是一个虚拟的终端,它必须映射到真正的终端上。
2、控制台可以简单的理解为printk输出的地方。
3、控制台是个只输出的设备,功能很简单,只能在内核中访问。
2、伪终端
伪终端设备是一种特殊的终端设备,由主-从两个成对的设备构成,当打开主设备时,对应的从
设备随之打开,形成连接状态。输入到主设备的数据成为从设备的输出,输入到从设备的数据成为主
设备的输出,形成双向管道。伪终端设备常用于远程登录服务器来建立网络和终端的关联。当通过
telent远程登录到另一台主机时,telent进程与远程主机的telent服务器相连。telent服务器使用某
个主设备并通过对应的从设备与telent进程相互通信。
6>终端体系
在Linux中,TTY体系分为:TTY核心,TTY线路规程,TTY驱动3部分。TTY核心从用户获取要发送给TTY
设备的数据,然后把数据传递给TTY线路规程,它对数据进行处理后,负责把数据传递到TTY驱动程
序,TTY驱动程序负责格式化数据,并通过硬件发送出去。从硬件收到的数据向上通过TTY驱动,进入
TTY线路规程,再进入TTY核心,最后被用户获取。TTY驱动可以直接和TTY核心通讯,但是通常TTY线
路规程会修改在两者之间传送的数据。TTY驱动不能直接和线路规程通信,甚至不知道它的存在,线
路规程的工作是格式化用户或者硬件收到数据,这种格式化常常实现为一个协议,如PPP或Bluetooth
6>中断体系--串口
7>数据流
读操作:TTY驱动从硬件收到数据后,负责把数据传递到TTY核心,TTY核心将从TTY驱动收到的数据缓
存到一个tty_flip_buffer类型的结构中。该结构包含两个数据数组。从TTY设备接收到的数据被存储
于第一个数组,当这个数组满,等待数据的用户将被通知。当用户从这个数组读数据时,任何从TTY
驱动新来的数据将被存储在第二个数组,当第二个数组存满后,数据再次提交给用户,并且又开始填
充第一个数组,一次交替。
8>驱动描述
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;
};
9>注册驱动
Linux为串口驱动程序提供了如下接口:
int uart_register_driver(struct uart_driver *drv)
10>端口描述
uart_port用于描述一个UART端口(一个串口)的地址,FIFO大小、端口类型等信息
struct uart_port
{
spinlock_t lock;/*端口锁*/
unsigned int iobase;/*IO端口基地址*/
unsigned char __iomem *membase;/*IO内存基地址*/
unsigned int irq;/*中断号*/
unsigned char fifosize;/*传输fifo大小*/
const struct uart_ops *ops;
...... ...... ...... .....
}
10>操作串口
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*);//停止接收
...... ...... ......
}
11>添加端口
串口核心层提供了如下函数来添加1个端口:
int uart_add_one_port(struct uart_driver *drv,struct uart_port *port)
12>操作流程
1、定义一个uart_driver的变量,并初始化;
2、使用uart_register_driver来注册这个驱动;
3、初始化uart_port和ops函数表;
4、调用uart_add_one_port()添加初始化好的uart_port。
二、串口驱动程序分析
1、发送和接收
发送:循环buffer->发送fifo->发送移位寄存器
接收:接收移位寄存器-->接收fifo->Flip_buf
发送的过程是:把数据写到发送fifo中,fifo把收到的数据传给发送移位寄存器(自动的,非driver控制),然后每个时钟脉冲往串口上写一bit数据。
接收的过程是:接收移位寄存器受到数据,发送给接收fifo,接收fifo事先设置好了触发门限,当里面的数据量超过门限时就会触发一个中断,调用驱动中的中断处理函数,把数据写到flip_buf中
2、寄存器
(1)UART Line Contrl Register
ULCONn(线控制寄存器)
Word Length: 数据位长度
Number of Stop Bit: 停止位数
Parity Mode: 奇偶校验位类型
Infra-Red Mode: UART/红外模式选择(当以UART模式工作时,需设为"0")
(2)UART Contrcl Register(UCON
n)
Recevice Mode:选择接收模式。如果是采用DMA模式的话,还需要指定说使用的DMA信道。
Transmit Mode :同上
Send Break Signal:选择是否在传1帧资料中途发送Break信号
Loopback Mode:选择是否将UART置于Loopback测试模式
Rx Error Status Interrupt Enable: 选择是否使能当发生接收异常时,是否产生接收错误中断。
Rx Time Out Enable:是否使能接收超时中断
Rx Interrupt Type:选择接收中断类型
选择0:Pulse(脉冲式/边沿式中断,非FIFO模式时,一旦接收缓冲区中有数据,即产生一个中断;为FIFO模式时,一旦当FIFO中的资料达到一定的触发水平后,即产生一个中断)
选择1:Level(电平模式中断,非FIFO模式时,只要接收缓冲区中有数据,即产生中断;为FIFO模式时,只有FIFO中的资料达到触发水平后,即产生中断)
Tx Interrupt Type:类同于Rx Interrupt Type
(3)UART FIFO Contrcl Register
FIFO Enable: FIFO使能选择
Rx FIFO Rest:选择当复位接收FIFO时是否自动清除FIFO中的内容。
Tx FIFO Rest:选择当复位发送FIFO时是否自动清除FIFO中的内容。
Rx FIFO Trigger Level:选择接收FIFO的触发水平。
Tx FIFO Trigger Level: 选择发送FIFO的触发水平。
(4)UART TX/RX Status Register(UTRSTATn)
Receive buffer data ready:当接收缓冲寄存器从UART接收端口接收到有效资料时将自动置"1",反之为"0"则表示缓冲器中没有资料。
Transmit buffer empty:当发送缓冲寄存器中为空,自动置"1"反之表明缓冲器中正有资料等待发送。
Transmit empty:当发送缓冲器中已经没有有效资料时,自动置"1",反之表明尚有资料发送。
(5)UART FIFO Status Register
Rx FIFO Count:接收FIFO中当前存放的字节数
Tx FIFO Count:发送FIFO中当前存放的字节数
Rx FIFO Full:为"1"表明接收FIFO已满
Tx FIFO Full:为"1"表明发送FIFO已满。
3、函数介绍
/*模块初始化函数*/
static int __init s3c2440uart_init(void)
{
return uart_register_driver(&s3c2440_reg);//使用uart_register_driver注册串口驱动
}
static struct uart_driver s3c2440_reg = {
owner: THIS_MODULE,
normal_major: SERIAL_S3C2440_MAJOR,
normal_name:"ttyS%d",
callout_name:"cua%d",
normal_driver:&normal,
callout_major:CALLOUT_S3C2440_MAJOR,
callout_driver:&callout,
table: s3c2440_table,
termios: s3c2440_termios,
termios_locked: s3c2440_termios_locked,
minor: MINOR_START,
nr: UART_NR,
port: s3c2440_ports,
cons: S3C2440_CONSOLE,
};
static struct uart_port s3c2410_ports[UART_NR] = {
{
iobase: (unsigned long)(UART0_CTL_BASE),
iotype: SERIAL_IO_PORT,
irq: IRQ_RXD0,
uartclk: 130252800,
fifosize: 16,
ops: &s3c2410_pops,
tyoe: PORT_S3C2410,
flags: ASYNC_BOOT_AUTOCONF,
},
....... ....... .......
}
static struct uart_ops s3c2410_pops = {
tx_empty: s3c2410uart_tx_empty,
set_mctrl: s3c2410uart_set_mctrl,
get_mctrl: s3c2410uart_get_mctrl,
stop_tx: s3c2410uart_stop_tx,
start_tx: s3c2410uart_start_tx,
stop_rx: s3c2410uart_stop_rx,
enable_ms: s3c2410uart_enable_ms,
break_ctl: s3c2410uart_break_ctl,
startup: s3c2410uart_startup,
shuwdown: s3c2410uart_shutdown,
change_speed: s3c2410uart_change_speed,
type: s3c2410uart_type,
config_port: s3c2410uart_config_port,
release_port: s3c2410uart_release_port,
request_port: s3c2410uart_request_port,
};
/*3.1阻止发送函数uart_stop_tx*/
static void s3c2410uart_stop_tx(struct uart_port *port,u_int from_tty)
{
disable_irq(TX_IRQ(port));
}
/*停止发送的功能,器内部的函数disable_irq是停止中断的功能,发送数据时通过中断来完成的,关闭中断也就关闭了发送*/
/*3.2发送使能函数uart_start_tx*/
static void s3c2410uart_start_tx(struct uart_port *port,u_int nonempty,u_int from_tty)
{
enable_irq(TX_IRQ(port));
}//与上面的过程类似,就是一个相反的过程。
/*3.3阻止接收函数uart_stop_rx*/
static void s3c2410uart_stop_rx(struct uart_port *port)
{
disable_irq(RX_IRQ(port));
}
/*发送缓冲空判断函数uart_tx_empty*/
static u_int s3c2410uart_tx_empty(struct uart_port *port)
{
return (UART_UTRSTAT(port) & UTRSTAT_TR_EMP ? 0 : TIOCSER_TEMT);
}
/*如果发送缓冲为空则返回0,否则返回1*/
/*3.5获取控制信息函数uart_get_mctrl*/
static u_int s3c2410uart_get_mctrl(struct uart_port *port)
{
return (TIOCM_CTS | TIOCM_DSR | TIOCM_CAR);
}
/*获取控制信息,TIOCM_CTS,TIOCM_DSR和TIOCM_CAR,这几个宏代表串口的控制信息,分别是clear to send,data set ready 和 data carrier detect(详见/serial Programming Guide for POSIX Operating Systems)*/
/*3.6接收中断函数uart_rx_interrupt*/
static void s3c2410uart_rx_interrupt(int irq,void *dev_id,struct pt_regs *regs)
{
struct uart_info *info = dec_id;
struct tty_struct *tty = info->tty;
unsigned int status,ch,max_count = 256;
struct uart_port *port = info->port;
status = UART_UTRSTAT(port);
while((status & UTRSTAT_RX_RDY) && max_count--)
{
if(tty->flip.count >= TTY_FLIPBUF_SIZE)
{
tty->flip.tqueue.routine((void*)tty);
if(tty->flip.count >= TTY_FLIPBUF_SIZE)
{
printk(KERN_WARING "TTY_DONT_FLIP set\n");
return;
}
}
ch = UART_URXH(port);
*tty->flip.char_buf_ptr = ch;
*tty->flip.flag_buf_ptr = TTY_NORMAL;
port->icount.rx++;
tty->flip.flag_buf_ptr++;
tty->flip.char_buf_ptr++;
tty->flip.count++;
status = UART_UTRSTAT(port);
while((staus & UTRSTAT_RX_RDY) && max_count--)
{
if(tty->flip.count >= TTY_FLIPBUF_SIZE)
{
tty->flip.tqueue.routine((void*)tty);
if(tty->flip.count >= TTY_FLIPBUF_SIZE)
{
printk(KERN_WARNING "TTY_DONT_FLIP set\n");
return;
}
}
ch = UART_URXH(port);
*tty->flip.char_buf_ptr = ch;
*tty->flip.flag_buf_ptr = TTY_NORMAL;
port->icount.rx++;
tty->flip.flag_buf_ptr++;
tty->flip.char_buf_ptr++;
tty->flip.count++;
status = UART_UTRSTAT(port);
}
tty_flip_buffer_push(tty);
return;
}
}
/*发送中断函数uart_tx_interrupt*/
static void s3c2410uart_tx_interrupt(int irq, void* dev_id,struct pt_regs *reg)
{
}