linux 下串口编程,uclinux下串口编程

转载来源:http://blog.myspace.cn/e/401588684.htm

在Linux中,设备分为3类:字符设备、块设备和网络设备。uClinux用设备文件表示大部分I/O设备。文件系统提供了统一的接口来访问一般意义上的文件和设备文件。

系统串口COMl与COM2,分别对应uClinux系统的/dev/ttyS0 /dev/ttySl两个串口设备文件。串口属于字符型设备,对串口的编

程也就是对相应文件进行读/写、控制等操作。串口编程的基本步骤是:先打开串口,设置串口属性,然后进行收发数据,最后关闭串口。 (1)打开串口 通过使用标准的文件打开函数open,达到访问串口设备驱动的目的。例如,以读写的方式打开串口1,可用下面的方法实现:

fd=open(“/dev/ttyS0”,O_RDWR); (2)设置串口属性 主要是设定结构体termios各成员的值。基本设置包括:波特率、数据位、校验位、停止位、输入和输出模式等。一般在设置时,先获取系统已有的串口属性,并在它的基础上进行修改。另外,设置时要用到系统预定义的宏。

(结合实例的说明略。--编者注) (3)收发数据 uClinux下串几发送和接收数据,通过使用文件操作中的read和write的方法来实现。例如:

write(fd,buffer,Length);

read(fd,buffer,Lerxgth); (4)关闭串口 关闭串口只须关闭已打开的串口文件描述符,如close(fd);

2 常用的几种I/O模型 通常在操作I/O时,会用到下而几种模型之一:阻塞型I/O、非阻塞型I/O和复用型I/O。下面以读取串口数据为例,简要说明它们的基本工作原理和特点。2.1 阻塞型I/O顾名思义,它以阻塞方式操作I/O,如图1所示。若一个进程以阻塞方式调用read函数读取串口数据,则该进程会一直睡眠在read系统调用上。此时系统

内核会一直等待数据,直到串口有数据到达为止。当串口数据准备好后,内核就把数据从内核拷贝至用户空间;而当数据拷贝完成后,才唤醒串口读取进程,通知它

读取数据报。

l_ce5948a7ac5bd303d52e3236425a627d.jpg

2.2 非阻塞I/O 图2中,在非阻塞I/O模型下,I/O

操作是即时完成的。当进程调用read函数时,设置了0_NONBLOCK标志,那么即使串口没有数据可读,read函数也会立即返回。此时其返同值为

EAGAIN,表明串口数据未就绪。如果串口有数据可读,则read函数会读取该数据,并返回所读数据的长度。通常轮询I/O的方法就足采用这种模型来读

取串口数据的,此时进程必须通过反复调用来检测是否有数据可读。如果轮询频率过低,则容易丢失数据;轮询频率过高,则占用太多处理器的处理周期。 2.3 I/O复用 上述两种I/O模型,是最常用的两种操

作I/O的方式;但在面向较复杂、需要处理多个I/O的系统时,这两种模型存在着不足之处。例如:在应用进程中需要对多个I/O设备进行监听,当某个设备可读或可写时,进程能马上得知,并进行相关处理。这时若采用阻塞方式操作I/O,则进程会阻塞在某个设备的 I/O读写操作上而不能适用于这种情况;若采用

非阻塞方式,则往往需要定时或循环地探测所有设备,才作相应处理,这种作法相当耗费系统中央处理器的执行周期。可见,上述的两个I/O模型都不能满足这类

应用,故此需要引入一种特别的I/O处理机制,即I/O复用。

所谓I/O复用,是指当一个或多个I/O条件(可读、能写或出现异常)满足时,进程能立即知道,从而正确并高效地对它们进行处理。

在uClinux下,系统提供select函数和poll函数,用来支持I/O复用的实现。如图3所示.若使用select的系统调用来查询是否有数据可

读时,进程是在等待多个I/O描述接口的任一个变为可读,但此期间并不阻塞进程。当有数据报已准备好时,返回可读条件,并通知进程再次进行系统调用准备读

取相应的I/O数据。此时内核就开始拷贝准备好的数据至用户空间,并返回指示进程处理数据报。

l_688bcf1fa1876872254657864efe2ab5.jpg

与上面提及的两种I/O模型不同的是:在这个处理过程中,使用了两次系统调用来达到读取数据的目的。虽然两次系统调用的开销似乎更大,但它的最大好处在于能同时等待多个描述符准备好。因此select调用功能更多地是借助了内核来监听I/O设备描述符的。

下面具体介绍select函数的功能及应用。

3 uClinux中基于select的I/O复用机制和工作原理在系统存在多个输入或输出流但不希望其中任一个流被阻塞的场合,经常使用复用I/O的方法解决。uClinux中,用户程序多使用select机制实现I/O复用控制,select函数允许进程对一个或多个设备文件进行非阻塞的读或写操作。

select的函数定义于<

unistd.h>

中,原型如下:

int select(int n,fd_set*reaclfds,fd_set*writefds,fd_set*exceptfds,struct timeval*timeout);

该函数允许进程指示内核等待多个事件中的任一个发生,并仅在一个或多个事件发生或经过某指定的时间后才唤醒进程。该函数的第1个参数n表示文件描述符集合

中最大值再加1;第2个参数readfds,表示可读的文件描述符集合,用于查看是否有可读取数据;第3个参数writefds表示可写的文件描述符集

合,用于查看是否能写入数据;第4个参数exceptfds用于异常控制;最后一个参数timeout决定了select将会阻塞多久才把控制权移交给调

用它的进程。调用select之前,必须对此参数进行初始化。若timeout值为O,则select直接返回O。此时I/O操作没有等待就立即返回,相

当于一种非阻塞I/O的调用。select的参数:

int n是一个整数值,是指集合中所有文件描述符的范围,即所有文件描述符的最大值加1,不能错!在Windows中这个参数的值无所谓,可以设置不正确。

fd_set *readfds是指向fd_set结构的指针,这个集合中应该包括文件描述符,我们是要监视这些文件描述符的读变化的,即我们关心是否可以从这些文件中读取数据了,如果这个集合中有一个文件可读,select就会返回一个大于0的值,表示有文件可读,如果没有可读的文件,则根据timeout参数再判断是否超时,若超出timeout的时间,select返回0,若发生错误返回负值。可以传入NULL值,表示不关心任何文件的读变化。

fd_set *writefds是指向fd_set结构的指针,这个集合中应该包括文件描述符,我们是要监视这些文件描述符的写变化的,即我们关心是否可以向这些文件中写入数据了,如果这个集合中有一个文件可写,select就会返回一个大于0的值,表示有文件可写,如果没有可写的文件,则根据timeout参数再判断是否超时,若超出timeout的时间,select返回0,若发生错误返回负值。可以传入NULL值,表示不关心任何文件的写变化。

fd_set *errorfds同上面两个参数的意图,用来监视文件错误异常。

struct timeval* timeout是select的超时时间,这个参数至关重要,它可以使select处于三种状态,第一,若将NULL以形参传入,即不传入时间结构,就是将select置于阻塞状态,一定等到监视文件描述符集合中某个文件描述符发生变化为止;第二,若将时间值设为0秒0毫秒,就变成一个纯粹的非阻塞函数,不管文件描述符是否有变化,都立刻返回继续执行,文件无变化返回0,有变化返回一个正值;第三,timeout的值大于0,这就是等待的超时时间,即select在timeout时间内阻塞,超时时间之内有事件到来就返回了,否则在超时后不管怎样一定返回,返回值同上述。

返回值:

负值:select错误

正值:某些文件可读写或出错

0:等待超时,没有可读写或错误的文件

在应用中,通常先调用select查看哪个I/O设备可读/写。如果没有可读/写的设备,并且没有设置超时返回功能,那么进程将阻塞在sekct调用上;

如果有,则select函数返回,紧接着可通过测试参数readfds和writef如来确定哪个I/O设备可读或能写,而后以非阻塞方式操作该I/O设

备,从而实现期望功能。

在实现selcct应用的过程中,还会使用到这些select相关接口:

l_bbf42d0d801d86a08d0a14e36f91ea15.jpg

其中,fd_set表示设备文件描述符集合,fd表示设备文件

描述符。FD_ZERC)函数用于清除设备文件描述符集合所有元素;FD_SET函数用于把某个文件描述符添加至文件描述符集合;FD_cLR函数用于从

文件描述符集合中删除某个文件描述符;而FD_ISSET用于检测设备文件描述符集合的某个文件描述符是否有效,有效则表示该位对应的设备有数据可读或可

写。

自注:fd_set

select()机制中提供一fd_set的,实际上是一long类型的,

每一个数组元素都能与一打开的文件句柄(不管是socket句柄,还是其他文件或命名管道或设备句柄)建立联系,建立联系的工作由程序员完成,当调用

select()时,由内核根据IO状态修改fd_set的内容,由此来通知执行了select()的进程哪一socket或文件可读。

fd_set set;

FD_ZERO(&set); /*将set清零使集合中不含任何fd*/

FD_SET(fd, &set); /*将fd加入set集合*/

FD_CLR(fd, &set); /*将fd从set集合中清除*/

(fd, &set); /*测试fd是否在set集合中*/

4 轮询检测方法与select方法的比较

4.1 轮询检测方法 轮询检测方法是指对串口进行非阻塞的读

写操作。当操作末成功时,让进程或线程挂起一段时间,然后再使用非阻塞调用来重新查询串口是否有可读/写数据。用此方法,相当于系统不断地对接收或者发送

操作的执行结果进行探测,直到把数据发出去或者接收完成定量的数据,才退出此轮询循环。而对于接收与发送不确定哪个时刻会到达的情况,即随机性比较高的读

/写操作,采用轮询方法会造成CPU资源浪费。如果轮询频率过低,则会使系统少接收一部分数据或接收过慢;反之,则接收方会因为等待太久而不能接收更多新

的数据。轮询频率过高的情况,会让CPU过度频繁地查询串口状态,造成过多的耗用CPU执行周期,降低其利用率。 4.2 select机制能充分利用系统时间的原因与频繁调用菲阻塞读写函数来轮询监听I/O的方法相比,select调用允许用户把进程本身挂起来,同时使系统内核监听所要求的一组文件描述符的任何活

动。只要确认在任何被监控的文件描述符上出现活动,select调用将返回指示该设备文件已经准备好的信息。这样就使进程能相对实时地监测到I/O设备上

随机的变化,而不必由进程本身去探测输入数据是否准备好。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值