Linux_终端控制台体系及串口驱动分析

Linux_终端控制台体系及串口驱动分析
Linux_终端控制台体系及串口驱动分析

Linux终端控制台体系及串口驱动分析

数据通信的基本方式可分为并行通信与串行通信两种:

并行通信:利用多条数据线路将数据的各位同时传送。它的特点是传输速度快,适用于短距离通信。

串行通信:利用一条数据线将数据一位位顺序传送。特点是通信线路简单,利用简单的线缆就可实现通信,低成本,适用于远距离通信。

异步通信以一个字符为传输单位,通信中的两个字符间的时间间隔是不固定的,然而同一个字符中的两个相邻位之间的时间间隔是固定的。

通信协议:是指双方约定的一些规则。在使用异步串口传送一个字符信息时,对数据格式有如下规定:规定有空闲位、起始位、资料位、奇偶校验位、停止位。

起始位:先发一个逻辑“0”信号,表示传输字符的开始

数据位:紧接在起始位之后。数据位的个数可以是4、5、6、7、8,从最低位开始传送,靠时钟定位。

奇偶校验位:数据位加上这一位后,使得“1”的位数应为偶数(偶校验)或奇数(奇校验),以此校验数据传送的正确性。

停止位:它是一个字符数据的结束标志。

空闲位:处于逻辑“1”状态,表示当前线路上没有数据传送。

波特率:是衡量数据传送速率的指针。表述每秒钟传送的二进制位数。

注:异步通信是按字符传输的,接收设备在收到起始信号之后在一个字符的传输时间内能和发送设备保持同步就能正确接收。

传送方式:单工方式、半双工方式、全双工方式

终端概述:

在Linux中,TTY(终端)是一类字符设备的统称,它包括了3种类型:控制台、串口和伪终端。

控制台:供内核使用的终端为控制台。控制台在Linux启动时,通过命令console=…指定,如果没有指定控制台,系统把第一个注册的终端(tty)作为控制台。

1、控制台是一个虚拟的终端,它必须映射到真正的终端上。

2、控制台可以简单的理解为printk输出的地方

3、控制台是个只输出的设备,功能很简单,只能在内核中访问。

伪终端:

伪终端设备是一种特殊的终端设备,由主-从两个成对的设备构成,当打开主设备时,对应的从设备随之打开,形成连接状态。输入到主设备的数据成为从设备的输出,输入到从设备的数据成为主设备的输出,形成双向管道。伪终端设备通常用于远程登录服务器来建立网络和终端的关联。当通过telnet远程登录到另一台主机时,telnet进程与远程主机的telnet服务器相连接。telnet服务器使用某个主设备并通过对应的从设备与telnet进程相互通信。

终端体系:

从上往下:TTY core—>TTY line discipline(可以没有)—>tty driver 在Linux中,TTY体系分为:TTY核心、TTY线路规程、TTY驱动3部分。TTY核心从用户获取要发送给TTY设备的数据,然后把数据传送给TTY线路规程(协议),它对数据进行处理后,负责把数据传递到TTY驱动程序,TTY驱动程序负责格式化数据,并通过硬件发送出去。

从硬件收到的数据向上通过TTY驱动,进入TTY线路规程,再进入TTY核心,最后被用户获取。TTY驱动可以直接和TTY核心通讯,但通常TTY线路规程会修改在两者之间的传送的数据。TTY驱动不能直接和线路规程通信,甚至不知道它的存在,线路规程的工作是格式化从用户或者硬件收到的数据。这种格式常常实现为一个协议,如PPP或Bluetooth

终端体系—串口(★)

往串口发送数据时的数据流向:

/dev/ttys0(串口的设备文件)—>tty_io.c(TTY core)—>n_tty.c(tty线路规程,必须向core注册)—>处理完返回tty core—>再由tty core交给驱动程序serial.c—>驱动程序控制硬件

数据流

读操作:

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端口(一个串口)的地址、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;

……………………..

}

操作串口:

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_poart*); //停止发送

void (*start_tx)(struct uart_poart*); //开始发送

void(*send_xchar)(struct uart_poart*, char ch); //发送xchar

void(*stop_rx)(struct uart_port*); //停止接收

………………………………..

}

添加串口:

串口核心层提供如下函数添加1个端口:

int uart_add_one_port(struct uart_driver*drv, struct uart_port* port)

★串口驱动设计流程:

1、定义一个uart_driver的变量,并初始化

2、使用uart_regisetr_driver来注册这个驱动

3、初始化uart_port和ops函数表

4、调用uart_add_one_poart()添加初始化好的uart_port

实例分析—mini2440串口驱动程序分析

1.发送和接收

发送:循环buffer →发送fifo→发送移位寄存器

//循环buffer(驱动实现)→发送fifo(串口芯片)过程由驱动程序完成;发送fifo→发送移位寄存器过程由硬件完成

接收:接收移位寄存器→接收fifo →Flip_buf

发送的过程是:把数据写到发送fifo中,fifo把收到的数据传给发送移位寄存器(自动的,非driver控制),然后每个时钟脉冲往串口线上写一bit数据。

接收的过程是:接收移位寄存器收到数据,发送给接收fifo,接收fifo 事先设置好了触发门限,当里面的数据量超过门限时就会触发一个中断,调用驱动中的中断处理函数,把数据写到flip_buf中。

2.寄存器

UART Line Control Register:

Word Length :数据位长度

Number of Stop Bit :停止位数

Parity Mode :奇偶校验位类型

Infra-Red Mode :UART/红外模式选择(当以UART模式工作时,需设为“0”)

UART Control Register

Receive 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

UART FIFO Conrtol Register

FIFO Enable :FIFO使能选择。

Rx FIFO Reset :选择当复位接收FIFO时是否自动清除FIFO中的内容。Tx FIFO Reset :选择当复位发送FIFO时是否自动清除FIFO中的内容。Rx FIFO Trigger Level :选择接收FIFO的触发水平。

Tx FIFO Trigger Level :选择发送FIFO的触发水平。

UART TX/RX Status Register

Receive buffer data ready :当接收缓冲寄存器从UART接收端口接收到有效资料时将自动置“1”。反之为“0则表示缓冲器中没有资料。Transmit buffer empty :当发送缓冲寄存器中为空,自动置“1”;反之表明缓冲器中正有资料等待发送。

Transmitter empty :当发送缓冲器中已经没有有效资料时,自动置“1”;反之表明尚有资料未发送。

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 s3c2410uart_init(void)

{

return uart_register_driver(&s3c2410_reg); }

使用uart_register_driver注册串口驱动。

static struct uart_driver s3c2410_reg = {

owner: THIS_MODULE,

normal_major: SERIAL_S3C2410_MAJOR,

normal_name: "ttyS%d",

callout_name: "cua%d",

normal_driver: &normal,

callout_major: CALLOUT_S3C2410_MAJOR,

callout_driver: &callout,

table: s3c2410_table,

termios: s3c2410_termios,

termios_locked: s3c2410_termios_locked,

minor: MINOR_START,

nr: UART_NR,

port: s3c2410_ports,

cons: S3C2410_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,

type: 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,

shutdown: 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));

}

3.4 发送缓冲空判断函数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 = dev_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--)

{ // status & UTRSTAT_RX_RDY判断有数据

if (tty->flip.count >= TTY_FLIPBUF_SIZE)

{ //flipbuf已满

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;

}

功能:主要是是while大循环,首先看循环判断条件status & UTRSTAT_RX_RDY,前面有status = UART_UTRSTAT(port),查2410的datasheet, status & UTRSTAT_RX_RDY这个位是判断接收buffer内是否还有有效数据?按道理一次中断只是把接收的fifobuffer中的数据放到flipbuffer中去,接收的fifo的中断门限是4-12字节,进行一次接收往往要中断好多次,这样中断开销比较大,所以在while的循环条件中判断一下是否还有接收的有效数据,如果有,就继续在中断程序中继续接收,当然,永远都在接收中断中(如果一直有数据要接收)也不合适,所以while循环还有计数,最多循环256次。

在循环中,首先是要判断一下接收数据用的flip-buffer是不是已经满了, if (tty->flip.count >= TTY_FLIPBUF_SIZE)如果满了,就要跳到另一个buffer上去, tty->flip.tqueue.routine((void *) tty)是用来实现跳到另一个buffer上的功能,然后把收到的数据写到flip-buffer中,相应的状态,统计数据都要改,接着再来while 循环,循环结束后就要调用tty_flip_buffer_push(tty)来让用户把存在缓冲里的数据取走,接收一次都要把缓存清空。

3.7 发送中断函数uart_tx_interrupt

static void s3c2410uart_tx_interrupt(int irq, void *dev_id,

struct pt_regs *reg) {

struct uart_info *info = dev_id;

struct uart_port *port = info->port;

int count;

if (port->x_char) { //x_char为停止位

UART_UTXH(port) = port->x_char;

port->icount.tx++;

port->x_char = 0;

return;

}

if (info->xmit.head == info->xmit.tail //无数据发送

|| info->tty->stopped || info->tty->hw_stopped) {

s3c2410uart_stop_tx(info->port, 0); //关闭发送中断

return;

}

count = port->fifosize >> 1;

do { //有数据发送

UART_UTXH(port) = info->xmit.buf[info->xmit.tail];

info->xmit.tail = (info->xmit.tail + 1) & (UART_XMIT_SIZE - 1);

port->icount.tx++;

if (info->xmit.head == info->xmit.tail)

break;

} while (--count > 0); //count=fifpsize(16字节)>>1即8字节

if (CIRC_CNT(info->xmit.head, info->xmit.tail,

UART_XMIT_SIZE) < WAKEUP_CHARS)

uart_event(info, EVT_WRITE_WAKEUP); //通知上层

if (info->xmit.head == info->xmit.tail)

s3c2410uart_stop_tx(info->port, 0);

}

(1) 首先查看port中的x_char是不是为0,不为0则把x_char发送出去。

x_char是xon/xoff的意思,每发一个字节时在开始前先发xon信号,在结束时发xoff。

(2) 如果x_char没有被设置,再看环形缓冲区是否为空,或者

info->tty->stopped 和info->tty->hw_stopped 两个位是不是为1,如果这些条件成立的话,就停止发送。Tty->stop指示tty设备是否停止,tty->hw_stop指示tty设备的硬件是否停止了,以上两个位都可以通过ttydriver来设定,否则的话说明有数据要发送。

(3) 如果以上条件都通过了,就利用一个while循环正式发送数据了,

从环形缓冲尾巴上取一个数赋给UART_UTXH(port)(发送FIFO), UART_UTXH(port) = info->xmit.buf[info->xmit.tail],这条语句就是把数据送到发送FIFO中,然后计数++,循环一共进行fifosize/2次,也就是一次只能发送8 byte。

(4)循环传送完一次后,再查看缓冲器里还剩余多少数据,如果少于

WAKEUP_CHARS(256)的话,就执行uart_event(info, 0),告诉TTY 核心,可以接受更多数据了。这里可以看出,tty_driver和tty_core之间的层次,tty_driver可以知道缓冲空还是满,但是它没有权力让发送数据过来,它只能是通知tty_core,让它来处理。

(5) 最后再察看一下环形寄存器,如果serial core 没有发送来更多的

数据,就关闭发送。

3.8 出错中断函数uart_err_interrupt

static void s3c2410uart_err_interrupt(int irq, void *dev_id,

struct pt_regs *reg) {

struct uart_info *info = dev_id;

struct uart_port *port = info->port;

struct tty_struct *tty = info->tty;

unsigned char err = UART_UERSTAT(port) & UART_ERR_MASK;

unsigned int ch, flg;

ch = UART_URXH(port);

if (!(err & (UERSTAT_BRK | UERSTAT_FRAME |

UERSTAT_PARITY | UERSTAT_OVERRUN)))

return;

if (err & UERSTAT_BRK)

port->icount.brk++;

if (err & UERSTAT_FRAME)

port->icount.frame++;

if (err & UERSTAT_PARITY)

port->icount.parity++;

if (err & UERSTAT_OVERRUN)

port->icount.overrun++;

err &= port->read_status_mask;

if (err & UERSTAT_PARITY)

flg = TTY_PARITY;

else if (err & UERSTAT_FRAME)

flg = TTY_FRAME;

else

flg = TTY_NORMAL;

if (err & UERSTAT_OVERRUN) {

*tty->flip.char_buf_ptr = ch;

*tty->flip.flag_buf_ptr = flg;

tty->flip.flag_buf_ptr++;

tty->flip.char_buf_ptr++;

tty->flip.count++;

if (tty->flip.count < TTY_FLIPBUF_SIZE) {

ch = 0;

flg = TTY_OVERRUN;

}

}

*tty->flip.flag_buf_ptr++ = flg;

*tty->flip.char_buf_ptr++ = ch;

tty->flip.count++;

}

#endif

首先err = UART_UERSTAT(port) & UART_ERR_MASK确定了err的值,err的值是从是从UART Error Status Register读到的,该erstate只用了四位,所以用UART_ERR_MASK把高四位掩掉,然后测试低四位中哪个位被置1了,从而判断错误种类

UERSTAT_BRK/FRAME/PARITY/OVERRUN 分别代表

1000/0100/0010/0001 ,判断出错误种类再进行相应的中断计数,然后再根据不同的err给flg设上不同的值,有

#define TTY_NORMAL 0

#define TTY_BREAK 1

#define TTY_FRAME 2

#define TTY_PARITY 3

#define TTY_OVERRUN 4

3.9 初始化函数uart_startup

static int s3c2410uart_startup(struct uart_port *port, struct uart_info *info)

{

int ret, flags;

u_int ucon;

ret = request_irq(RX_IRQ(port), s3c2410uart_rx_interrupt,

SA_INTERRUPT,

"serial_s3c2410_rx", info);

if (ret) goto rx_failed;

ret = request_irq(TX_IRQ(port), s3c2410uart_tx_interrupt,

SA_INTERRUPT,

"serial_s3c2410_tx", info);

if (ret) goto tx_failed;

#ifdef CONFIG_USE_ERR_IRQ

ret = request_irq(ERR_IRQ(port), s3c2410uart_err_interrupt,

SA_INTERRUPT,

"serial_s3c2410_err", info);

if (ret) goto err_failed;

#endif

ucon = (UCON_TX_INT_LVL | UCON_RX_INT_LVL |

UCON_TX_INT | UCON_RX_INT | UCON_RX_TIMEOUT);

#if defined(CONFIG_IRDA) || defined(CONFIG_IRDA_MODULE) ULCON2 |= ULCON_IR | ULCON_PAR_NONE | ULCON_WL8 | ULCON_ONE_STOP;

#endif

save_flags(flags);

cli();

UART_UCON(port) = ucon;

sti();

restore_flags(flags);

return 0;

#ifdef CONFIG_USE_ERR_IRQ

err_failed:

free_irq(TX_IRQ(port), info);

#endif

tx_failed:

free_irq(RX_IRQ(port), info);

rx_failed:

return ret;

}

如果使用了函数open(ttyS0),那么最后调用的实现open功能的就是这个函数,它打开ttyS0。

1:利用request_irq()申请发送,接收,错误三个中断,如果失败,就要释放调已经申请的全部资源

2:设置UART Control Register

3.10 函数uart_change_speed

static void s3c2410uart_change_speed(struct uart_port *port, u_int cflag, u_int iflag, u_int quot)

{

u_int ulcon, ufcon;

int flags;

ufcon = UART_UFCON(port);

switch (cflag & CSIZE) {

case CS5: ulcon = ULCON_WL5; break;

case CS6: ulcon = ULCON_WL6; break;

case CS7: ulcon = ULCON_WL7; break;

default: ulcon = ULCON_WL8; break;

}

if (cflag & CSTOPB)

ulcon |= ULCON_STOP;

if (cflag & PARENB) {

if (!(cflag & PARODD))

ulcon |= ULCON_PAR_EVEN;

}

if (port->fifosize > 1)

ufcon |= UFCON_FIFO_EN;

port->read_status_mask = UERSTAT_OVERRUN;

if (iflag & INPCK)

port->read_status_mask |= UERSTAT_PARITY | UERSTAT_FRAME;

Linux驱动程序工作原理简介

Linux驱动程序工作原理简介 一、linux驱动程序的数据结构 (1) 二、设备节点如何产生? (2) 三、应用程序是如何访问设备驱动程序的? (2) 四、为什么要有设备文件系统? (3) 五、设备文件系统如何实现? (4) 六、如何使用设备文件系统? (4) 七、具体设备驱动程序分析 (5) 1、驱动程序初始化时,要注册设备节点,创建子设备文件 (5) 2、驱动程序卸载时要注销设备节点,删除设备文件 (7) 参考书目 (8) 一、linux驱动程序的数据结构 设备驱动程序实质上是提供一组供应用程序操作设备的接口函数。 各种设备由于功能不同,驱动程序提供的函数接口也不相同,但linux为了能够统一管理,规定了linux下设备驱动程序必须使用统一的接口函数file_operations 。 所以,一种设备的驱动程序主要内容就是提供这样的一组file_operations 接口函数。 那么,linux是如何管理种类繁多的设备驱动程序呢? linux下设备大体分为块设备和字符设备两类。 内核中用2个全局数组存放这2类驱动程序。 #define MAX_CHRDEV 255 #define MAX_BLKDEV 255 struct device_struct { const char * name; struct file_operations * fops; }; static struct device_struct chrdevs[MAX_CHRDEV]; static struct { const char *name; struct block_device_operations *bdops; } blkdevs[MAX_BLKDEV]; //此处说明一下,struct block_device_operations是块设备驱动程序内部的接口函数,上层文件系统还是通过struct file_operations访问的。

Linux串口(serial、uart)驱动程序设计

Linux串口(serial、uart)驱动程序设计 https://www.360docs.net/doc/818324768.html,/space.php?uid=23089249&do=blog&id=34481 一、核心数据结构 串口驱动有3个核心数据结构,它们都定义在<#include linux/serial_core.h> 1、uart_driver uart_driver包含了串口设备名、串口驱动名、主次设备号、串口控制台(可选)等信息,还封装了tty_driver(底层串口驱动无需关心tty_driver)。 struct uart_driver { struct module *owner;/* 拥有该uart_driver的模块,一般为THIS_MODULE */ const char*driver_name;/* 串口驱动名,串口设备文件名以驱动名为基础 */ const char*dev_name;/* 串口设备名*/ int major;/* 主设备号*/ int minor;/* 次设备号*/ int nr;/* 该uart_driver支持的串口个数(最大) */ struct console *cons;/* 其对应的console.若该uart_driver支持serial console, 否则为NULL */ /* * 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; }; 2、uart_port uart_port用于描述串口端口的I/O端口或I/O内存地址、FIFO大小、端口类型、串口时钟等信息。实际上,一个uart_port实例对应一个串口设备

Linux设备驱动程序举例

Linux设备驱动程序设计实例2007-03-03 23:09 Linux系统中,设备驱动程序是操作系统内核的重要组成部分,在与硬件设备之间 建立了标准的抽象接口。通过这个接口,用户可以像处理普通文件一样,对硬件设 备进行打开(open)、关闭(close)、读写(read/write)等操作。通过分析和设计设 备驱动程序,可以深入理解Linux系统和进行系统开发。本文通过一个简单的例子 来说明设备驱动程序的设计。 1、程序清单 //MyDev.c 2000年2月7日编写 #ifndef __KERNEL__ #define __KERNEL__//按内核模块编译 #endif #ifndef MODULE #define MODULE//设备驱动程序模块编译 #endif #define DEVICE_NAME "MyDev" #define OPENSPK 1 #define CLOSESPK 2 //必要的头文件 #include //同kernel.h,最基本的内核模块头文件 #include //同module.h,最基本的内核模块头文件 #include //这里包含了进行正确性检查的宏 #include //文件系统所必需的头文件 #include //这里包含了内核空间与用户空间进行数据交换时的函数宏 #include //I/O访问 int my_major=0; //主设备号 static int Device_Open=0; static char Message[]="This is from device driver"; char *Message_Ptr; int my_open(struct inode *inode, struct file *file) {//每当应用程序用open打开设备时,此函数被调用 printk ("\ndevice_open(%p,%p)\n", inode, file); if (Device_Open) return -EBUSY;//同时只能由一个应用程序打开 Device_Open++; MOD_INC_USE_COUNT;//设备打开期间禁止卸载 return 0; } static void my_release(struct inode *inode, struct file *file)

linux UART串口驱动开发文档

linux UART串口驱动开发文档 w83697/w83977 super I/O串口驱动开发 内容简介: 介绍了Linux下的串口驱动的设计层次及接口, 并指出串口与TTY终端之间的关联层次(串口可作TTY终端使用), 以及Linux下的中断处理机制/中断共享机制, 还有串口缓冲机制当中涉及的软中断机制; 其中有关w83697/w83977 IC方面的知识, 具体参考相关手册, 对串口的配置寄存器有详细介绍, 本文不再进行说明. 目录索引: 一. Linux的串口接口及层次. 二. Linux的中断机制及中断共享机制. 三. Linux的软中断机制. 四. TTY与串口的具体关联. 一. Linux的串口接口及层次. 串口是使用已经非常广的设备了, 因此在linux下面的支持已经很完善了, 具有统一的编程接口, 驱动开发者所要完整的工作就是针对不同的串口IC来做完成相应的配置宏, 这此配置宏包括读与写, 中断打开与关闭(如传送与接收中断), 接收状态处理, 有FIFO时还要处理FIFO的状态. 如下我们就首先切入这一部分, 具体了解一下与硬件串口IC相关的部分在驱动中的处理, 这一部分可以说是串口驱动中的最基础部分, 直接与硬件打交道, 完成最底层具体的串口数据传输. 1. 串口硬件资源的处理. W83697及W83977在ep93xx板子上的映射的硬件物理空间如下: W83697: 0x20000000起1K空间. W83977: 0x30000000起1K空间. 因为串口设备的特殊性, 可以当作终端使用, 但是终端的使用在内核还未完全初始化之前(关于串口与终端的关联及层次在第四节中详细), 此时还没有通过mem_init()建立内核的虚存管理机制, 所以不能通过ioreamp来进行物理内存到虚存的映射(物理内存必须由内核映射成系统管理的虚拟内存后才能进行读写访问), 这与先前所讲的framebuffer的物理内存映射是不同的, 具体原因如下: √终端在注册并使用的调用路径如下: start_kernel→console_init→uart_console_init→ep93xxuart_console_init→register_conso

linux串口测试程序

linux串口测试程序 由于已经完成了第一个HELLO程序,标志着整个编译环境已经没有问题了,下来准备做一下串口测试程序。由于串口驱动开发板已经作好了,所以就作一个Linux串口测试工具简单的数据收发看看。 Linux串口测试工具网上常见的版本都看起来比较烦琐,下面是一个简单一点的,这个程序功能是收到10个字节后会发前7个字节,如果所发的数据的第一个字节是9则退出。 #include #include #include #include #include #include #include #include #define BAUDRATE B9600 #define MODEMDEVICE "/dev/ttyUSB1" int main() { int fd,c=0,res;struct termios oldtio,newtio;//intch;static char s1[10],buf[10];printf("start ……\n");/*打开PC的COM1口*/ fd = open(MODEMDEVICE,O_RDWR|O_NOCTTY);if (fd < 0) { perror(MODEMDEVICE);exit(1);} printf("open……\n");/*将旧的通讯参数存入oldtio结构*/ tcgetattr(fd,&oldtio);/*初始化新的newtio */ bzero(&newtio,sizeof(newtio));/*8N1*/ newtio.c_cflag = BAUDRATE|CS8|CLOCAL|CREAD;newtio.c_iflag = IGNPAR;newtio.c_oflag = 0;/*正常模式*/ /*newtio.c_lflag = ICANON;*/ /*非正常模式*/ newtio.c_lflag = 0;newtio.c_cc[VTIME] = 0;newtio.c_cc[VMIN] = 10; tcflush(fd,TCIFLUSH);/*新的temios作为通讯端口参数*/ tcsetattr(fd,TCSANOW,&newtio);printf("writing……\n"); while(1) { //printf("read……\n");res = read(fd,buf,10);//res = read(fd,s1,10);//strcat(buf,s1);// res = write(fd,buf,7);printf("buf = %s\n",buf);if(buf[0]==9) break;} printf("close……\n");close(fd);/*还原旧参数*/ tcsetattr(fd,TCSANOW,&oldtio);return 0;} 还有一点要注意,就是Linux串口测试工具串口有两种工作模式,即正规模式和非正规模式,如果习惯在串口调试器中用16进制发送,此时串口应该为非正规模式才行。 下面是这两种模式的说明Linux串口测试工具正规模式(CANONICAL或者COOKED) 此模式下,终端设备会处理特殊字符,并且数据传输是一次一行的方式,既按回车后才开始发送和接收数据。例如LINUX的SHELL. Linux串口测试工具非正规模式(NON-CANONICAL

linux串口编程参数配置详解

linux串口编程参数配置详解 1.linux串口编程需要的头文件 #include //标准输入输出定义 #include //标准函数库定义 #include //Unix标准函数定义 #include #include #include //文件控制定义 #include //POSIX中断控制定义 #include //错误号定义 2.打开串口 串口位于/dev中,可作为标准文件的形式打开,其中: 串口1 /dev/ttyS0 串口2 /dev/ttyS1 代码如下: int fd; fd = open(“/dev/ttyS0”, O_RDWR); if(fd == -1) { Perror(“串口1打开失败!”); } //else //fcntl(fd, F_SETFL, FNDELAY); 除了使用O_RDWR标志之外,通常还会使用O_NOCTTY和O_NDELAY这两个标志。 O_NOCTTY:告诉Unix这个程序不想成为“控制终端”控制的程序,不说明这

个标志的话,任何输入都会影响你的程序。 O_NDELAY:告诉Unix这个程序不关心DCD信号线状态,即其他端口是否运行,不说明这个标志的话,该程序就会在DCD信号线为低电平时停止。 3.设置波特率 最基本的串口设置包括波特率、校验位和停止位设置,且串口设置主要使用termios.h头文件中定义的termios结构,如下: struct termios { tcflag_t c_iflag; //输入模式标志 tcflag_t c_oflag; //输出模式标志 tcflag_t c_cflag; //控制模式标志 tcflag_t c_lflag; //本地模式标志 cc_t c_line; //line discipline cc_t c_cc[NCC]; //control characters } 代码如下: int speed_arr[] = { B38400, B19200, B9600, B4800, B2400, B1200, B300, B384 00, B19200, B9600, B4800, B2400, B1200, B300, }; int name_arr[] = {38400, 19200, 9600, 4800, 2400, 1200, 300, 38400, 19200, 9 600, 4800, 2400, 1200, 300, }; void SetSpeed(int fd, int speed) { int i; struct termios Opt; //定义termios结构 if(tcgetattr(fd, &Opt) != 0) { perror(“tcgetattr fd”); return; }

Linux设备驱动程序学习(18)-USB 驱动程序(三)

Linux设备驱动程序学习(18)-USB 驱动程序(三) (2009-07-14 11:45) 分类:Linux设备驱动程序 USB urb (USB request block) 内核使用2.6.29.4 USB 设备驱动代码通过urb和所有的 USB 设备通讯。urb用 struct urb 结构描述(include/linux/usb.h )。 urb以一种异步的方式同一个特定USB设备的特定端点发送或接受数据。一个USB 设备驱动可根据驱动的需要,分配多个 urb 给一个端点或重用单个 urb 给多个不同的端点。设备中的每个端点都处理一个 urb 队列, 所以多个 urb 可在队列清空之前被发送到相同的端点。 一个 urb 的典型生命循环如下: (1)被创建; (2)被分配给一个特定 USB 设备的特定端点; (3)被提交给 USB 核心; (4)被 USB 核心提交给特定设备的特定 USB 主机控制器驱动; (5)被 USB 主机控制器驱动处理, 并传送到设备; (6)以上操作完成后,USB主机控制器驱动通知 USB 设备驱动。 urb 也可被提交它的驱动在任何时间取消;如果设备被移除,urb 可以被USB 核心取消。urb 被动态创建并包含一个内部引用计数,使它们可以在最后一个用户释放它们时被自动释放。 struct urb

struct list_head urb_list;/* list head for use by the urb's * current owner */ struct list_head anchor_list;/* the URB may be anchored */ struct usb_anchor *anchor; struct usb_device *dev;/* 指向这个 urb 要发送的目标 struct usb_device 的指针,这个变量必须在这个 urb 被发送到 USB 核心之前被USB 驱动初始化.*/ struct usb_host_endpoint *ep;/* (internal) pointer to endpoint */ unsigned int pipe;/* 这个 urb 所要发送到的特定struct usb_device 的端点消息,这个变量必须在这个 urb 被发送到 USB 核心之前被 USB 驱动初始化.必须由下面的函数生成*/ int status;/*当 urb开始由 USB 核心处理或处理结束, 这个变量被设置为 urb 的当前状态. USB 驱动可安全访问这个变量的唯一时间是在 urb 结束处理例程函数中. 这个限制是为防止竞态. 对于等时 urb, 在这个变量中成功值(0)只表示这个 urb 是否已被去链. 为获得等时 urb 的详细状态, 应当检查 iso_frame_desc 变量. */ unsigned int transfer_flags;/* 传输设置*/ void*transfer_buffer;/* 指向用于发送数据到设备(OUT urb)或者从设备接收数据(IN urb)的缓冲区指针。为了主机控制器驱动正确访问这个缓冲, 它必须使用 kmalloc 调用来创建, 不是在堆栈或者静态内存中。对控制端点, 这个缓冲区用于数据中转*/ dma_addr_t transfer_dma;/* 用于以 DMA 方式传送数据到 USB 设备的缓冲区*/ int transfer_buffer_length;/* transfer_buffer 或者 transfer_dma 变量指向的缓冲区大小。如果这是 0, 传送缓冲没有被 USB 核心所使用。对于一个 OUT 端点, 如果这个端点大小比这个变量指定的值小, 对这个USB 设备的传输将被分成更小的块,以正确地传送数据。这种大的传送以连续的 USB 帧进行。在一个 urb 中提交一个大块数据, 并且使 USB 主机控制器去划分为更小的块, 比以连续地顺序发送小缓冲的速度快得多*/

linux驱动程序的编写

linux驱动程序的编写 一、实验目的 1.掌握linux驱动程序的编写方法 2.掌握驱动程序动态模块的调试方法 3.掌握驱动程序填加到内核的方法 二、实验内容 1. 学习linux驱动程序的编写流程 2. 学习驱动程序动态模块的调试方法 3. 学习驱动程序填加到内核的流程 三、实验设备 PentiumII以上的PC机,LINUX操作系统,EL-ARM860实验箱 四、linux的驱动程序的编写 嵌入式应用对成本和实时性比较敏感,而对linux的应用主要体现在对硬件的驱动程序的编写和上层应用程序的开发上。 嵌入式linux驱动程序的基本结构和标准Linux的结构基本一致,也支持模块化模式,所以,大部分驱动程序编成模块化形式,而且,要求可以在不同的体系结构上安装。linux是可以支持模块化模式的,但由于嵌入式应用是针对具体的应用,所以,一般不采用该模式,而是把驱动程序直接编译进内核之中。但是这种模式是调试驱动模块的极佳方法。 系统调用是操作系统内核和应用程序之间的接口,设备驱动程序是操作系统内核和机器硬件之间的接口。设备驱动程序为应用程序屏蔽了硬件的细节,这样在应用程序看来,硬件设备只是一个设备文件,应用程序可以像操作普通文件一样对硬件设备进行操作。同时,设备驱动程序是内核的一部分,它完成以下的功能:对设备初始化和释放;把数据从内核传送到硬件和从硬件读取数据;读取应用程序传送给设备文件的数据和回送应用程序请求的数据;检测和处理设备出现的错误。在linux操作系统下有字符设备和块设备,网络设备三类主要的设备文件类型。 字符设备和块设备的主要区别是:在对字符设备发出读写请求时,实际的硬件I/O一般就紧接着发生了;块设备利用一块系统内存作为缓冲区,当用户进程对设备请求满足用户要求时,就返回请求的数据。块设备是主要针对磁盘等慢速设备设计的,以免耗费过多的CPU时间来等待。 1 字符设备驱动结构 Linux字符设备驱动的关键数据结构是cdev和file_operations结构体。

从零开始搭建Linux驱动开发环境

参考: 韦东山视频第10课第一节内核启动流程分析之编译体验 第11课第三节构建根文件系统之busybox 第11课第四节构建根文件系统之构建根文件系统韦东山书籍《嵌入式linux应用开发完全手册》 其他《linux设备驱动程序》第三版 平台: JZ2440、mini2440或TQ2440 交叉网线和miniUSB PC机(windows系统和Vmware下的ubuntu12.04) 一、交叉编译环境的选型 具体的安装交叉编译工具,网上很多资料都有,我的那篇《arm-linux- gcc交叉环境相关知识》也有介绍,这里我只是想提示大家:构建跟文件系统中所用到的lib库一定要是本系统Ubuntu中的交叉编译环境arm-linux- gcc中的。即如果电脑ubuntu中的交叉编译环境为arm-linux-

二、主机、开发板和虚拟机要三者互通 w IP v2.0》一文中有详细的操作步骤,不再赘述。 linux 2.6.22.6_jz2440.patch组合而来,具体操作: 1. 解压缩内核和其补丁包 tar xjvf linux-2.6.22.6.tar.bz2 # 解压内核 tar xjvf linux-2.6.22.6_jz2440.tar.bz2 # 解压补丁

cd linux_2.6.22.6 patch –p1 < ../linux-2.6.22.6_jz2440.patch 3. 配置 在内核目录下执行make 2410_defconfig生成配置菜单,至于怎么配置,《嵌入式linux应用开发完全手册》有详细介绍。 4. 生成uImage make uImage 四、移植busybox 在我们的根文件系统中的/bin和/sbin目录下有各种命令的应用程序,而这些程序在嵌入式系统中都是通过busybox来构建的,每一个命令实际上都是一个指向bu sybox的链接,busybox通过传入的参数来决定进行何种命令操作。 1)配置busybox 解压busybox-1.7.0,然后进入该目录,使用make menuconfig进行配置。这里我们这配置两项 一是在编译选项选择动态库编译,当然你也可以选择静态,不过那样构建的根文件系统会比动态编译的的大。 ->Busybox Settings ->Build Options

linux设备驱动,tty串口编程

linux设备驱动,tty串口编程2011-12-04 08:56:33 分类:LINUX XC2440开发板上已经含有S3C2440的3个串口驱动,我们只要知道各个串口的设备名称就可以了, 204 s3c2410_serial ,204是串口的主设备号。s3c2410_serial是设备名称,在 dev目录下 ls 一下就可以发现 ptyd0 s3c2410_serial0 ttysa ptyd1 s3c2410_serial1 ttysb ptyd2 s3c2410_serial2 ttysc s3c2410_serial0,s3c2410_serial1,s3c2410_serial2 分别是串口1、2、3的设备名称 下面是测试源码,打开串口1、2,程序执行后,串口1的波特率变为9600,这时候你的串口 终端就没有反应了(串口1波特率默认115200),把终端软件串口1 波特率改为9600后, 连接终端,回车一下,然后输入几个‘1’后,画面如上图。 这时用telnet工具登陆开发板,执行ps 查看现有运行的程序,找到tty [root@XC2440 /root]# ps PID USER TIME COMMAND 1 root 0:04 init 2 root 0:00 [kthreadd] 3 root 0:00 [ksoftirqd/0] 5 root 0:00 [kworker/u:0] 6 root 0:00 [khelper] 7 root 0:00 [kworker/u:1] 10 root 0:00 [netns] 236 root 0:00 [sync_supers] 238 root 0:00 [bdi-default] 240 root 0:00 [kblockd] 249 root 0:00 [khubd] 252 root 0:00 [kseriod]

Linux下串口编程所要知道的那些事

Linux下串口编程所要知道的那些事 [日期:2011- 来源:csdn 作者:tiger-john 1. 波特率 1> 表示每秒传输的比特数。 2> 说明:若波特率为115200,它表示什么呢? ? 对于发送断,即每秒钟发送115200bit。 ? 对于接收端,115200波特率意味着串口通信在数据线上的采样率为115200HZ. 注:波特率和距离之间成反比,距离相隔很近的设备之间可以实现高波特率通信。 2. 数据位 1> 表示通信中实际数据位的参数。在计算机发送的数据包中,实际的数据往往不会是8位。 2> 说明:在串口通信中,可以选择5,6,7,8位。设定数据位时,主要考虑所要传输的数据内容。 3> 事例:如果要传输的是标准的ASCII码。那么又该如何设定数据位呢? ? 由于ASCII码的范围是0~127,因此设定数据位为7就OK了。 ? 若为扩展ASCII码,其范围是0~255,必须使用8位。 注:7位或8位数据中不仅仅是数据,还包括开始/停止位,数据位以及奇偶校验位等。 3. 奇偶校验位 1> 作用:该位用于串口通信中的简单检验错。 2> 类型:主要有偶校验,奇校验,标记,空格的方式 在ARM7(LPC2200)中,只有偶校验,奇校验两种方式。 3> 方法:如何进行校验? ? 奇偶校验是通过统计数据中高位或低位的个数来实现校验的。 ? 标记,空格并不是真正校验错误的,只是通过简单的置位来实现对

数据的检测。https://www.360docs.net/doc/818324768.html,通过置位方式,可以判断出是否存在噪声干扰数据通信或数据传输,以及是否存在不同步的现象 4. 停止位 1> 作用:停止位用于标志该数据包数据结束,可以取1位,1.5位或2位。 在ARM7(lpc2200中)停止位可以取1位,2位或不取 2> 说明: ? 停止位不仅仅用于数据包的传输结束标志,还提供了计算机之间校正同步时钟的机会。 ? 用于停止位的位数越多,不同时钟同步的容忍程序越大。 ? 但是由于停止位占用了数据空间,过多的停止位将导致数据传输速度的下降。 5. 数据流控制 1> 通过串口传输数据时,由于计算机之间处理速度或其他因素的影响,会造成丢失数据的现象。 2> 作用:数据流控制用于解决上面的问题,通过控制发送数据的速度,确保数据不会出现丢失。 3> 类型:数据流控制可以分为软件流控制(Xon/Xoff)和硬件流控制,当然你可以选择不使用数据流控制。 ? 软件流控制使用特殊的字符作为启动或停止的标志 ? 硬件流控制通过使用硬件信号(CTR/RTS)来实现。 注:使用硬件流控制时,在接收端准备好接收数据后,设为CTS为1,否则CTS为0。同样,如果发送端准备好要发送数据时,则设定RTS为1;如果还未准备好,设置CTS为0. 二. Linux串口下编程所要考虑的问题 1. Linux下编写串口程序的思想 看图:

linux设备驱动之8250串口驱动

linux设备驱动之8250串口驱动 一:前言 前一段时间自己实践了一下8250芯片串口驱动的编写。今天就在此基础上分析一下linux kernel自带的串口驱动。毕竟只有对比专业的驱动代码才能更好的进步,同以往一样,基于linix kernel2.6.25.相应驱动代码位于:linux-2.6.25/drivers/serial/8250.c。 二:8250串口驱动初始化 相应的初始化函数为serial8250_init().代码如下: static int __init serial8250_init(void) { int ret, i; if (nr_uarts > UART_NR) nr_uarts = UART_NR; printk(KERN_INFO "Serial: 8250/16550 driver $Revision: 1.90 $ " "%d ports, IRQ sharing %sabled\n", nr_uarts, share_irqs ? "en" : "dis"); for (i = 0; i < NR_IRQS; i++) spin_lock_init(&irq_lists[i].lock); ret = uart_register_driver(&serial8250_reg); if (ret) goto out; serial8250_isa_devs = platform_device_alloc("serial8250", PLAT8250_DEV_LEGACY); if (!serial8250_isa_devs) { ret = -ENOMEM; goto unreg_uart_drv; } ret = platform_device_add(serial8250_isa_devs); if (ret) goto put_dev; serial8250_register_ports(&serial8250_reg, &serial8250_isa_devs->dev); ret = platform_driver_register(&serial8250_isa_driver); if (ret == 0) goto out; platform_device_del(serial8250_isa_devs);

Linux设备驱动程序简介

第一章Linux设备驱动程序简介 Linux Kernel 系统架构图 一、驱动程序的特点 ?是应用和硬件设备之间的一个软件层。 ?这个软件层一般在内核中实现 ?设备驱动程序的作用在于提供机制,而不是提供策略,编写访问硬件的内核代码时不要给用户强加任何策略 o机制:驱动程序能实现什么功能。 o策略:用户如何使用这些功能。 二、设备驱动分类和内核模块 ?设备驱动类型。Linux 系统将设备驱动分成三种类型 o字符设备 o块设备 o网络设备 ?内核模块:内核模块是内核提供的一种可以动态加载功能单元来扩展内核功能的机制,类似于软件中的插件机制。这种功能单元叫内核模块。 ?通常为每个驱动创建一个不同的模块,而不在一个模块中实现多个设备驱动,从而实现良好的伸缩性和扩展性。 三、字符设备 ?字符设备是个能够象字节流<比如文件)一样访问的设备,由字符设备驱动程序来实现这种特性。通过/dev下的字符设备文件来访问。字符设备驱动程序通常至少需要实现 open、close、read 和 write 等系统调用 所对应的对该硬件进行操作的功能函数。 ?应用程序调用system call<系统调用),例如:read、write,将会导致操作系统执行上层功能组件的代码,这些代码会处理内核的一些内部 事务,为操作硬件做好准备,然后就会调用驱动程序中实现的对硬件进 行物理操作的函数,从而完成对硬件的驱动,然后返回操作系统上层功 能组件的代码,做好内核内部的善后事务,最后返回应用程序。 ?由于应用程序必须使用/dev目录下的设备文件<参见open调用的第1个参数),所以该设备文件必须事先创建。谁创建设备文件呢? ?大多数字符设备是个只能顺序访问的数据通道,不能前后移动访问指针,这点和文件不同。比如串口驱动,只能顺序的读写设备。然而,也 存在和数据区或者文件特性类似的字符设备,访问它们时可前后移动访

linux驱动程序进入内核

ARM-uClinux下编写加载驱动程序详细过程 本文主要介绍在uClinux下,通过加载模块的方式调试IO控制蜂鸣器的驱动程序。实验过程与上篇文章所讲的过程基本相似,更多注重细节及注意事项。 本文适合学习ARM—Linux的初学者。 //================================================================== 硬件平台:MagicARM2200教学试验开发平台(LPC2290) Linux version 2.4.24,gcc version 2.95.3 电路连接:P0.7——蜂鸣器,低电平发声。 实验条件:uClinux内核已经下载到开发板上,能够正常运行;与宿主机相连的网络、串口连接正常。 //================================================================== 编写蜂鸣器的驱动程序相对来说容易实现,不需要处理中断等繁琐的过程,本文以蜂鸣器的驱动程序为例,详细说明模块化驱动程序设计的主要过程和注意事项。 一、编写驱动程序 驱动程序的编写与上文所说的编写过程基本相同,这里再详细说明一下。 //========================================== //蜂鸣器驱动程序:beep.c文件 //------------------------------------------------------------------- #include /*模块相关*/ #include /*内核相关*/ #include /*linux定义类型*/ #include /*文件系统 file_opertions 结构体定义*/ #include /*出错信息*/ /*PINSEL0 注意:低2位是UART0复用口,不要改动*/ #define PINSEL0 (*((volatile unsigned*) 0xE002C000)) /*P0口控制寄存器*/ #define IO0PIN (*((volatile unsigned*) 0xE0028000))

Linux_终端控制台体系及串口驱动分析

Linux终端控制台体系及串口驱动分析 数据通信的基本方式可分为并行通信与串行通信两种: 并行通信:利用多条数据线路将数据的各位同时传送。它的特点是传输速度快,适用于短距离通信。 串行通信:利用一条数据线将数据一位位顺序传送。特点是通信线路简单,利用简单的线缆就可实现通信,低成本,适用于远距离通信。 异步通信以一个字符为传输单位,通信中的两个字符间的时间间隔是不固定的,然而同一个字符中的两个相邻位之间的时间间隔是固定的。 通信协议:是指双方约定的一些规则。在使用异步串口传送一个字符信息时,对数据格式有如下规定:规定有空闲位、起始位、资料位、奇偶校验位、停止位。 起始位:先发一个逻辑“0”信号,表示传输字符的开始 数据位:紧接在起始位之后。数据位的个数可以是4、5、6、7、8,从最低位开始传送,靠时钟定位。 奇偶校验位:数据位加上这一位后,使得“1”的位数应为偶数(偶校验)或奇数(奇校验),以此校验数据传送的正确性。 停止位:它是一个字符数据的结束标志。 空闲位:处于逻辑“1”状态,表示当前线路上没有数据传送。 波特率:是衡量数据传送速率的指针。表述每秒钟传送的二进制位数。 注:异步通信是按字符传输的,接收设备在收到起始信号之后在一个字符的传输时间内能和发送设备保持同步就能正确接收。 传送方式:单工方式、半双工方式、全双工方式 终端概述: 在Linux中,TTY(终端)是一类字符设备的统称,它包括了3种类型:控制台、串口和伪终端。 控制台:供内核使用的终端为控制台。控制台在Linux启动时,通过命令console=…指定,如果没有指定控制台,系统把第一个注册的终端(tty)作为控制台。 1、控制台是一个虚拟的终端,它必须映射到真正的终端上。 2、控制台可以简单的理解为printk输出的地方 3、控制台是个只输出的设备,功能很简单,只能在内核中访问。 伪终端: 伪终端设备是一种特殊的终端设备,由主-从两个成对的设备构成,当打开主设备时,对应的从设备随之打开,形成连接状态。输入到主设备的数据成为从设备的输出,输入到从设备的数据成为主设备的输出,形成双向管道。伪终端设备通常用于远程登录服务器来建立网络和终端的关联。当通过telnet远程登录到另一台主机时,telnet进程与远程主机的telnet服务器相连接。telnet服务器使用某个主设备并通过对应的从设备与telnet进程相互通信。

linux串口触摸屏设计总结

Linux serial touch 设计总结 概述: 最近在做嵌入式linux下串口触摸屏设计,遇到一些问题,经过查找资料和请教同事,总算把问题解决了,事后有把linux相关的内核代码仔细看了一遍,为了有点成果,特别写了个总结。如有任何问题请联系yxj_5421@https://www.360docs.net/doc/818324768.html,,转载请标明出处。 系统资源: Linux:2.6.36 UI:QT+TSLIB 硬件资源不关心 设计方法: 有两种实现途径。 1、是将要使用的串口单独拿出来,作为一个platform总线设备实现,在嵌入式平 台mach文件里面,加上串口中断号和寄存器首地址,然后将这个串口注册成 一个platform总线设备。在驱动probe函数里面需要得到这个串口中断号以及 寄存器映射地址,通过寄存器映射地址设置串口波特率,数据位,停止位等, 通过中断号注册中断等,然后调用input_register_device注册一个input设备。 在中断里面得到外面触摸屏的数据,然后根据input touch协议上报触摸数据。 这种方法实现简单明了,不需要和linux的tty,serio等打交道。但是要求知道 串口硬件spec,比如寄存器等,而且这个串口就只能给触摸屏使用了,不能作 为tty使用。因为是嵌入式开发,因此很容易知道硬件spec,而且嵌入式平台 一旦确定,那么这个串口肯定就是给触摸屏使用了。因此在嵌入式平台上,推 荐使用这个方法。 2、是将串口作为一个serio总线设备,利用linux内核提供serio总线驱动,通过设 置对应的串口,调用serport提供的函数将串口当做serio总线设备,在驱动里 面需要按照serio总线设备驱动的框架来实现,这方面的例子linux里面有很多, 比如touchright.c,在模块init函数里面调用serio_register_driver注册serio总线 设备驱动,如果serio总线上对应的serio设备存在,就调用connect函数,在 这个函数里面调用input_register_device注册一个input设备。具体驱动不再分 析了,很简单,相信各位都能看的懂。 至此,两种方法都实现了串口触摸屏的驱动,讲到这里是不是就完了,非也,本文的重点还在后面,请看下面分析: 第一种方法只要驱动模块被加载,就会在/dev/input下面创建一个eventx节点,tslib就能访问这个节点,获得触摸坐标,然后送给qt。 第二种方法驱动模块加载后,并没有创建eventx节点,也就是说connect函数没有被调用,按照linux驱动模型来看,就是serio总线上还没有对应的serio设备,因此驱动加载时没有对应的设备,就不会调用connect函数,这时的串口还是作为一个linux tty设备存在。 我遇到的问题就是serio驱动加载了,但是没有创建eventx节点,查找资料也只有一个说是要把tty设置成N_MOUSE,然后读,说的不清楚,也不知道怎么实现,经过自己摸索,终于把问题解决了。 Linux 启动后串口形式:

Linux设备驱动程序说明介绍

Linux设备驱动程序简介 Linux是Unix操作系统的一种变种,在Linux下编写驱动程序的原理和思想完全类似于其他的Unix系统,但它dos或window环境下的驱动程序有很大的区别。在Linux环境下设计驱动程序,思想简洁,操作方便,功能也很强大,但是支持函数少,只能依赖kernel 中的函数,有些常用的操作要自己来编写,而且调试也不方便。本人这几周来为实验室自行研制的一块多媒体卡编制了驱动程序,获得了一些经验,愿与Linux fans共享,有不当之处,请予指正。 以下的一些文字主要来源于khg,johnsonm的Write linux device driver,Brennan's Guide to Inline Assembly,The Linux A-Z,还有清华BBS上的有关device driver的一些资料. 这些资料有的已经过时,有的还有一些错误,我依据自己的试验结果进行了修正. 一、Linux device driver 的概念 系统调用是操作系统内核和应用程序之间的接口,设备驱动程序是操作系统内核和机器硬件之间的接口.设备驱动程序为应用程序屏蔽了硬件的细节,这样在应用程序看来,硬件设备只是一个设备文件,应用程序可以象操作普通文件一样对硬件设备进行操作.设备驱动程序是内核的一部分,它完成以下的功能: 1.对设备初始化和释放. 2.把数据从内核传送到硬件和从硬件读取数据. 3.读取应用程序传送给设备文件的数据和回送应用程序请求的数据. 4.检测和处理设备出现的错误. 在Linux操作系统下有两类主要的设备文件类型,一种是字符设备,另一种是块设备.字符设备和块设备的主要区别是:在对字符设备发出读/写请求时,实际的硬件I/O一般就紧接着发生了,块设备则不然,它利用一块系统内存作缓冲区,当用户进程对设备请求能满足用户的要求,就返回请求的数据,如果不能,就调用请求函数来进行实际的I/O操作.块设备是主要针对磁盘等慢速设备设计的,以免耗费过多的CPU时间来等待. 已经提到,用户进程是通过设备文件来与实际的硬件打交道.每个设备文件都都有其文件属性(c/b),表示是字符设备还蔤强樯璞?另外每个文件都有两个设备号,第一个是主设备号,标识驱动程序,第二个是从设备号,标识使用同一个设备驱动程序的不同的硬件设备,比如有两个软盘,就可以用从设备号来区分他们.设备文件的的主设备号必须与设备驱动程序在登记时申请的主设备号一致,否则用户进程将无法访问到驱动程序. 最后必须提到的是,在用户进程调用驱动程序时,系统进入核心态,这时不再是抢先式调度.也就是说,系统必须在你的驱动程序的子函数返回后才能进行其他的工作.如果你的驱动程序陷入死循环,不幸的是你只有重新启动机器了,然后就是漫长的fsck. 读/写时,它首先察看缓冲区的内容,如果缓冲区的数据 如何编写Linux操作系统下的设备驱动程序 二、实例剖析 我们来写一个最简单的字符设备驱动程序。虽然它什么也不做,但是通过它可以了解Linux的设备驱动程序的工作原理.把下面的C代码输入机器,你就会获得一个真正的设备驱动程序.不过我的kernel是2.0.34,在低版本的kernel上可能会出现问题,我还没测试过. [code]#define __NO_VERSION__

相关文档
最新文档