Chinaunix首页 | 论坛 | 博客
  • 博客访问: 10135
  • 博文数量: 3
  • 博客积分: 0
  • 博客等级: 民兵
  • 技术积分: 11
  • 用 户 组: 普通用户
  • 注册时间: 2014-02-27 15:15
文章分类
文章存档

2014年(3)

我的朋友
最近访客

分类: 嵌入式

2014-02-27 20:56:08

  

    最近在linux系统中写了个串口通信的程序,主要是PC机和ARM-mini2440开发板的串口进行通信(当然在开发板上也是跑的Linux操作系统),PC和开发板都要进行接收和发送。发送端要发送从0x00~0xFF中的任意字符,(包括不可见字符)但接收端某些字符老接收不到,而接收端是使用的是软中断的方式,也就是使用的信号SIGIO,进行数据的接收,但是在收到SIGIO信号后,总是接收不到数据,分析及其解决方法如下:

 

一、接收数据时

写数据时,直接调用write函数就可以了,但是在用read读数据时,就会有一定的规则了。解决这些问题之前我们先看终端I/O的两种输入处理模式:

 

(1) 规范方式输入处理。

在这种方式中,终端输入以行为单位进行处理。对于每个读要求,终端驱动程序最多返回一行。只有遇到NLEOLEOL2,和EOF。此类表示一行结束的特殊字符时,才会read到真正的数据,否则即使收到SIGIO信号,当调用read函数进行读串口时,一样读不到数据。就是在这里,自己调试了好长时间,只到设置了 newio.c_lflag  & =~ICANON .这上面的5个行定界符中,其中只有一个EOF字符在终端驱动程序对其处理后即被删除。其他4个字符则作为该行的最后一个字符返回给调用者。

0x0a'\n',以NL来表示,也就是说类似按了enter键,本行数据才输出,所以必须修改,将终端设置在非规范方式输入处理。

(2) 非规范方式输入处理。输入字符不以行为单位进行装配。

如果不作特殊处理,则默认方式是规范方式。例如:若shell的标准输入、输出是终端,在用readwrite将标准输入复制到标准输出时,终端以规范方式进行工作,每次read最多返回一行。处理整个屏幕的程序,例如vi编辑程序使用非规范方式,其原因是其命令是由不以新行符终止的一个或几个字符组成的。另外,该编辑程序使用了若干特殊字符作为编辑命令,所以它也不希望系统对特殊字符进行处理。例如,Ctrl+D字符通常是终端的文件结束符,但在vi中它是向下滚动半个屏幕的命令。

POSIX.1定义了11个特殊输入字符,其中9个可以改变。

关闭termios结构中中c_lflag字段的ICANO标志就使终端处于非规范模式。在非规范模式中,输入数据并不组成行,不处理下列特殊字符:ERASE,KILLEOFNLEOLEOL2CRREPRINTSTATUSWERASE。不处理的意思就是,不会对这些特殊的字符,进行特殊的处理。

在规范模式下,系统每次返回一行,但在非规范模式下,系统怎样才能知道在什么时候将数据返回给我们呢?如果它一次返回一个字节,那么系统开销就会很大。在启动读数据时,往往不知道要读多少数据,所以系统不能总是一次返回多个字节。

解决方法: 当已读了指定量的数据后,或者已经过了给定的时间后,即通知系统返回,这种技术使用了termios结构中的c_cc数组的两个变量:MINTIMEc_cc数组中的这两个元素下标名为VMINVTIMEMIN说明一个read返回前的最小字节数据,TIME说明等待数据到达的分秒数(秒的十分之一是分秒)。

有下列四种情形:

 情形AMIN>0TIME>0

    TIME说明字节间的计时器,在接到第一个字节时才启动它,在该计时器超时之前,若已接到MIN个字节,则read返回MIN个字节。如果在接到MIN个字节之前,该计时器已超时,则read返回已经接收到的字节,因为只有在接收到第一个字节时才启动,所以在计时器超时时,至少返回一个字节。这种情形中,在接到第一个字节之前,调用者阻塞。如果在调用read时数据已经可用,则这如同在read后数据立即被接收到一样。

情形B MIN>0,TIME==0

 已经接到了MIN个字节时,read才返回,这可以造成read的无限期阻塞

情形CMIN == 0TIME>0

TIME 指定了一个调用read时启动的读计时器,在接收到一个字节或者该计时器超时时,read即返回,如果是计时器超时,则read返回0.

情形DMIN == 0TIME ==0

如果有数据可用,则read最多返回所要求的字节数。如果无数据可用,则read立即返回0.

中,有如下定义

struct termios {

 tcflag_t c_iflag;  /* input mode flags */

 tcflag_t c_oflag;  /* output mode flags */

 tcflag_t c_cflag;  /* control mode flags */

 tcflag_t c_lflag;  /* local mode flags */

 cc_t c_line;   /* line discipline */

 cc_t c_cc[NCCS];  /* control characters */

};

 

c_iflag由终端设备驱动程序用来控制输入特性(剥除输入字节的第8位,允许输

入奇偶校验等等)

c_oflag则控制输出特性(执行输出处理,将新行映照为C R / L F等)

c_cflag影响到UART串行线(忽略调制解调器的状态线,每个字符的一个或两个停止位等等),

c_lflag影响驱动程序和用户之间的界面(回送的开或关,可视的擦除符,允许终端产生的信

号,对后台作业输出的控制停止信号等)。

修改非规范方式输入处理,如下:struct termios options;

tcgetattr( fd,&options) 

options.c_lflag &= ~(ICANON );

通过修改c_lflag本地模式,将ICANON属性去掉,ICANON就是规范化方式。

二、接收到数据:

 

 

其余数据全正确

 

03以前的数据全部没有,而0x03相当于 ^c, CTRL+C ^c, ^?ISIG来控制,那么去除该控制,应该就

可以显示03了,再次修改为:

 

options.c_lflag &= ~(ICANON |ISIG);

 

 

 

三、接收到数据:

 

00 01 02 03 04 05 06 07 08 09 0a 0b

0c 0a 0e 0f ……

 

其余数据全部正确

 

看,0d变成了0a

 

c_iflag模式中,有ICRNL项,IGNCR项,将两者去掉:

 

options.c_iflag &= ~(ICRNL|IGNCR)

 

 

 

至此:串口能接收到所有的数据。

阅读(1309) | 评论(0) | 转发(0) |
1

上一篇:没有了

下一篇:一个中国黑客致中国黑客和红客的公开信

给主人留下些什么吧!~~