Chinaunix首页 | 论坛 | 博客
  • 博客访问: 855670
  • 博文数量: 102
  • 博客积分: 7086
  • 博客等级: 少将
  • 技术积分: 2245
  • 用 户 组: 普通用户
  • 注册时间: 2006-04-18 11:01
文章分类

全部博文(102)

文章存档

2012年(2)

2011年(1)

2010年(21)

2009年(31)

2008年(47)

我的朋友

分类: LINUX

2009-02-03 15:07:28

串口编程最经典的资料当然还是Serial Programming Guide for POSIX Operating Systems. ()

这篇文章可以用来做参考用...

Table of Contents

9.1. 常用函数
9.2. 设置串口属性
9.3. c_iflag输入标志说明
9.4. c_oflag输出标志说明
9.5. c_cflag控制模式标志说明
9.6. c_cc[]控制字符说明
9.7. c_lflag本地模式标志说明
9.8. 下面介绍一些常用串口属性的设置方法。

9.1. 常用函数

使用open()函数打开串口,open()函数有两个参数,第一个是要打开的设备名(如:/dev/ttyS0)。第二个是打开的方式。打开方式有以下三种:

  • O_RDWR,表示以读写方式打开串口。

  • O_NOCTTY,表示不成为端口的控制终端,如果没有这个选项,则任何输入(键盘按键)都会中断程序的执行。

  • O_NDELAY,表示程序不会关注DCD信号线所处的状态,即不管对端设备是运行或挂起。如果没有该选项,则程序会被设置成睡眠状态,直到DCD信号为低为止。

成功打开串口则会返回文件描述符,打开失败则返回-1。下面是一个打开串口的示例:

fd = open("/dev/ttyS0",O_RDWR|O_NDELAY|O_NDELAY);

使用close()关闭打开的串口,唯一的参数是打开串口的文件描述符。下面是一个关闭串口的示例:

close(fd);        //fd是打开串口返回的文件描述符

用write()函数向串口写数据。下面是一个向串口写数据的示例:

n = write(fd,buff,len);    
/* n表示成功写到串口的字节数,如果写入失败则返回-1
   fd是打开串口返回的文件描述符
   buff表示写入的内容
   len表示写入信息的长度。
*/

用read()函数从串口读取数据。下面是一个从串口读数据的示例:

n = read(fd,buff,len);
/* n表示从串口读到字节数
   fd是文件描述符
   buff是读入字节存放的缓冲区
   len表示读入的字节数
*/

通过fcntl()函数可以操作文件描述符,用以控制读取数据的状态。fcntl(fd,F_SETFL,0)表示没有数据则阻塞,处于等待状态,直到有数据到来;fcntl(fd,F_SETFL,FNDELAY)表示当端口没有数据时马上返回0。

9.2. 设置串口属性

所有的串口属性都在一个名为termios的结构体中,要使用该结构体要包含termios.h头文件。在该头文件中还定义两个重要的函数tcgetattr()和tcsetattr(),分别用以获取和设置串口的属性。如:tcgetattr(fd,&old_termios),tcsetattr(fd,TCSANOW,&new_termios)。old_termios是旧的串口属性,new_termios是重新设置的新串口属性。tcsetattr()函数中常量的意义是:

  • TCSANOW表示新设置的串口属性马上生效。

  • TCSADRAIN表示等所有数据传送完成后才生效。

  • TCSAFLUSH表示马上清空输入和输出缓存,然后应用新的串口设置。

termios结构体内容:

成员            描述
-------------------------------------------
c_cflag         控制模式标志
c_lflag         本地模式标志
c_iflag         输入模式标志
c_oflag         输出模式标志
c_line          line discipline
c_cc[NCCS]      控制字符
c_ispeed        输入波特率
c_ospeed        输出波特率

在termios结构中的四个标志控制了输入输出的四个不同部份。输入模式标志c_iflag决定如何解释和处理接收的字符。输出模式标志c_oflag决定如何解释和处理发送到tty设备的字符。控制模式标志决定设备的一系列协议特征,这一标志只对物理设备有效。本地模式标志c_lflag决定字符在输出前如何收集和处理。

在串口传输中,用波特率来表示传输的速度,1波特表示在1秒钟内可以传输1个码元。波特率设置可以使用cfsetispeed(&new_termios,B19200)和cfsetospeed(&new_termios,B19200)这两个函数来完成,默认的波特率为9600baud。cfsetispeed()函数用来设置输入的波特率,cfsetospeed()函数用来设置输出的波特率。B19200是termios.h头文件里定义的一个宏,表示19200的波特率。

CLOCAL和CREAD是c_cflag成员中与速率相关的标志,在串口编程中,这两个标志一定要有效,以确保程序在突发的作业控制或挂起时,不会成为端口的占有都,同时串口的接收驱动会自动读入数据。设置方法如下:

termios_new.c_cflag |= CLOCAL;                 //保证程序不会成为端的占有者
termios_new.c_cflag |= CREAD;                  //使端口能读取输入的数据

设置串口属性不能直接赋值,要通过对termios不同成员进行"与"和"或"操作来实现。在termios.h文件,定义了各种常量,如上面介绍的CLOCAL,CREAD。这些常量的值是掩码,通过把这些常量与termios结构成员进行逻辑操作就可实现串口属性的设置。在编程时用"|="来启用属性,用"&=~"来取消属性。

9.3. c_iflag输入标志说明

  • BRKINT和IGNBRK

    如果设置了IGNBRK,中断条件被忽略。如果没有设置IGNBRK而设置了BRKINT,中断条件清空输入输出队列中所有的数据并且向tty的前台进程组中所有进程发送一个SIGINT信号。如果这两个都没有设置,中断条件会被看作一个0字符。这时,如果设置了PARMRK,当检测到一个帧误差时将会向应用程序发送三个字节'\377''\0''\0',而不是只发送一个'\0'。

  • PARMRK和IGNPAR

    如果设定了IGNPAR,则忽略接收到的数据的奇偶检验错误或帧错误(除了前面提到的中断条件)。如果没有设置IGNPAR而设置了PARMRK,当接收到的字节存在奇偶检验错误或帧错误的时候。将向应用程序发送一个三字节的'\377''\0''\n'错误报告。其中n表示所接收到的字节。如果两者都没有设置,除了接收到的字节存在奇偶检验错误或帧误差之外的中止条件都会向应用程序发送一个单字节('\0')的报告。

  • INPCK

    如果设置,则进行奇偶校验。如果不进行奇偶检验,PARMRK和IGNPAR将对存在的奇偶校验错误不产生任何的影响。

  • ISTRIP

    如果设置,所接收到的所有字节的高位将会被去除,保证它们是一个7位的字符。

  • INLCR

    如果设置,所接收到的换行字符('\n')将会被转换成回车符('\r')。

  • IGNCR

    如果设置,则会忽略所有接收的回车符('\r')。

  • ICRNL

    如果设置,但IGNCR没有设置,接收到的回车符向应用程序发送时会变换成换行符。

  • IUCLC

    如果IUCLC和IEXTEN都设置,接收到的所有大写字母发送给应程序时都被转换成小写字母。POSIX中没有定义该标记。

  • IXOFF

    如果设置,为避免tty设备的输入缓冲区溢出,tty设备可以向终端发送停止符^S和开始符^Q,要求终端停止或重新开始向计算机发送数据。通过停止符和开始符来控制数据流的方式叫软件流控制,软件流控制方式较少用,我们主要还是用硬件流控制方式。硬件流控制在c_cflag标志中设置。

  • IXON

    如果设置,接收到^S后会停止向这个tty设备输出,接收到^Q后会恢复输出。

  • IXANY

    如果设置,则接到任何字符都会重新开始输出,而不仅仅是^Q字符。

  • IMAXBEL

    如果设置,当输入缓冲区空间满时,再接收到的任何字符就会发出警报符'\a'。POSIX中没有定义该标记。

9.4. c_oflag输出标志说明

OPOST是POSIX定义的唯一一个标志,只有设置了该标志后,其它非POSIX的输出标记才会生效。

  • OPOST

    开启该标记,后面的输出标记才会生效。否则,不会对输出数据进行处理。

  • OLCUC

    如果设置,大写字母被转换成小写字母输出。

  • ONLCR

    如果设置,在发送换行符('\n')前先发送回车符('\r')。

  • ONOCR

    如果设置,当current column为0时,回车符不会被发送也不会被处理。

  • OCRNL

    如果设置,回车符会被转换成换行符。另外,如果设置了ONLRET,则current column会被设为0.

  • ONLRET

    如果设置,当一个换行符或回车符被发送的时候,current column会被设置为0。

  • OXTABS

    如果设置,制表符会被转换成空格符。

9.5. c_cflag控制模式标志说明

  • CLOCAL

    如果设置,modem的控制线将会被忽略。如果没有设置,则open()函数会阻塞直到载波检测线宣告modem处于摘机状态为止。

  • CREAD

    只有设置了才能接收字符,该标记是一定要设置的。

  • CSIZE

    设置传输字符的位数。CS5表示每个字符5位,CS6表示每个字符6位,CS7表示每个字符7位,CS8表示每个字符8位。

  • CSTOPB

    设置停止位的位数,如果设置,则会在每帧后产生两个停止位,如果没有设置,则产生一个停止位。一般都是使用一位停止位。需要两位停止位的设备已过时了。

  • HUPCL

    如果设置,当设备最后打开的文件描述符关闭时,串口上的DTR和RTS线会减弱信号,通知Modem挂断。也就是说,当一个用户通过Modem拔号登录系统,然后注销,这时Modem会自动挂断。

  • PARENB和PARODD

    如果设置PARENB,会产生一个奇偶检验位。如果没有设置PARODD,则产生偶校验位,如果设置了PARODD,则产生奇校验位。如果没有设置PARENB,则PARODD的设置会被忽略。

  • CRTSCTS

    使用硬件流控制。在高速(19200bps或更高)传输时,使用软件流控制会使效率降低,这个时候必须使用硬件流控制。

9.6. c_cc[]控制字符说明

只有在本地模式标志c_lflag中设置了IEXITEN时,POSIX没有定义的控制字符才能在Linux中使用。每个控制字符都对应一个按键组合(^C、^H等),但VMIN和VTIME这两个控制字符除外,它们不对应控制符。这两个控制字符只在原始模式下才有效。

  • c_cc[VINTR]

    默认对应的控制符是^C,作用是清空输入和输出队列的数据并且向tty设备的前台进程组中的每一个程序发送一个SIGINT信号,对SIGINT信号没有定义处理程序的进程会马上退出。

  • c_cc[VQUIT]

    默认对应的控制符是^\,作用是清空输入和输出队列的数据并向tty设备的前台进程组中的每一个程序发送一个SIGQUIT信号,对SIGQUIT信号没有定义处理程序的进程会马上退出。

  • c_cc[verase]

    默认对应的控制符是^H或^?,作用是在标准模式下,删除本行前一个字符,该字符在原始模式下没有作用。

  • c_cc[VKILL]

    默认对应的控制符是^U,在标准模式下,删除整行字符,该字符在原始模式下没有作用。

  • c_cc[VEOF]

    默认对应的控制符是^D,在标准模式下,使用read()返回0,标志一个文件结束。

  • c_cc[VSTOP]

    默认对应的控制字符是^S,作用是使用tty设备暂停输出直到接收到VSTART控制字符。或者,如果设备了IXANY,则等收到任何字符就开始输出。

  • c_cc[VSTART]

    默认对应的控制字符是^Q,作用是重新开始被暂停的tty设备的输出。

  • c_cc[VSUSP]

    默认对应的控制字符是^Z,使当前的前台进程接收到一个SIGTSTP信号。

  • c_cc[VEOL]和c_cc[VEOL2]

    在标准模式下,这两个下标在行的末尾加上一个换行符('\n'),标志一个行的结束,从而使用缓冲区中的数据被发送,并开始新的一行。POSIX中没有定义VEOL2。

  • c_cc[VREPRINT]

    默认对应的控制符是^R,在标准模式下,如果设置了本地模式标志ECHO,使用VERPRINT对应的控制符和换行符在本地显示,并且重新打印当前缓冲区中的字符。POSIX中没有定义VERPRINT。

  • c_cc[VWERASE]

    默认对应的控制字符是^W,在标准模式下,删除缓冲区末端的所有空格符,然后删除与之相邻的非空格符,从而起到在一行中删除前一个单词的效果。POSIX中没有定义VWERASE。

  • c_cc[VLNEXT]

    默认对应的控制符是^V,作用是让下一个字符原封不动地进入缓冲区。如果要让^V字符进入缓冲区,需要按两下^V。POSIX中没有定义VLNEXT。

要禁用某个控制字符,只需把它设置为_POSIX_VDISABLE即可。但该常量只在Linux中有效,所以如果程序要考虑移植性的问题,请不要使用该常量。

9.7. c_lflag本地模式标志说明

  • ICANON

    如果设置,则启动标准模式,如果没有设置,则启动原始模式。

  • ECHO

    如果设置,则启动本地回显。如果没有设置,则除了ECHONL之外,其他以ECHO开头的标记都会失效。

  • ECHOCTL

    如果设置,则以^C的形式打印控制字符,如:按Ctrl+C显示^C,按Ctrl+?显示^?。

  • ECHOE

    如果在标准模式下设定了ECHOE标志,则当收到一个ERASE控制符时将删除前一个显示字符。

  • ECHOK和ECHOKE

    在标准模式下,当接收到一个KILL控制符,则在缓冲区中删除当前行。如果ECHOK、ECHOKE和ECHOE都没有设置,则用ECHOCTL表示的KILL字符(^U)将会在输出终端上显示,表示当前行已经被删除。

    如果已经设置了ECHOE和ECHOK,但没有设置ECHOKE,将会在输出终端显示ECHOCTL表示的KILL字符,紧接着是换行,如果设置了OPOST,将会通过OPOST处理程序进行适当的处理。

    如果ECHOK、ECHOKE和ECHOE都有设置,则会删除当前行。

    在POSIX中没有定义ECHOKE标记,在没有定义ECHOKE标记的系统中,设置ECHOK则表示同时设置了ECHOKE标志。

  • ECHONL

    如果在标准模式下设置了该标志,即使没有设置ECHO标志,换行符还是会被显示出来。

  • ECHOPRT

    如果设置,则字符会被简单地打印出来,包括各种控制字符。在POSIX中没有定义该标志。

  • ISIG

    如果设置,与INTR、QUIT和SUSP相对应的信号SIGINT、SIGQUIT和SIGTSTP会发送到tty设备的前台进程组中的所有进程。

  • NOFLSH

    一般情况下,当接收到INTR或QUIT控制符的时候会清空输入输出队列,当接收到SUSP控制符时会清空输入队列。但是如果设置了NOFLUSH标志,则所有队列都不会被清空。

  • TOSTOP

    如果设置,则当一个非前台进程组的进程试图向它的控制终端写入数据时,信号SIGTTOU会被被发送到这个进程所在的进程组。默认情况下,这个信号会使进程停止,就像收到SUSP控制符一样。

  • IEXIEN

    默认已设置,我们不应修改它。在Linux中IUCLC和几个与删除字符相关的标记都要求在设置了IEXIEN才能正常工作。

9.8. 下面介绍一些常用串口属性的设置方法。

  • 设置流控制

    termios_new.c_cflag &= ~CRTSCTS;            //不使用流控制
    termios_new.c_cflag |= CRTSCTS;                 //使用硬件流控制
    termios_new.c_iflag |= IXON|IXOFF|IXANY;        //使用软件流控制
  • 屏蔽字符大小位

    termios_new.c_cflag &= ~CSIZE;
  • 设置数据位大小

    termios_new.c_cflag |= CS8;         //使用8位数据位
    termios_new.c_cflag |= CS7;         //使用7位数据位
    termios_new.c_cflag |= CS6;         //使用6位数据位
    termios_new.c_cflag |= CS5;         //使用5位数据位
  • 设置奇偶校验方式

    termios_new.c_cflag &= ~PARENB;       //无奇偶校验
    
    termios_new.c_cflag |= PARENB;            //奇校验
    termios_new.c_cflag &= ~PARODD;       
    
    termios_new.c_cflag |= PARENB;            //偶校验
    termios_new.c_cflag &= ~PARODD;
  • 停止位

    termios_new.c_cflag |= CSTOPB;            //2位停止位
    termios_new.c_cflag &= ~CSTOPB;       //1位停止位
  • 输出模式

    termios_new.c_cflag &= ~OPOST;        //原始数据(RAW)输出
  • 控制字符

    termios_new.c_cc[VMIN] = 1;               //读取字符的最小数量
    termios_new.c_cc[VTIME] = 1;              //读取第一个字符的等待时间
  • 关闭终端回显,键盘输入的字符不会在终端窗口显示。

    #include 
    #include 
    #include 
    #include 
    
    int main(void)
    {
            struct termios ts,ots;
            char passbuf[1024];
    
            tcgetattr(STDIN_FILENO,&ts);  /* STDIN_FILENO的值是1,表示标准输入的文件描述符 */
            ots = ts;
    
            ts.c_lflag &= ~ECHO;         /* 关闭回终端回显功能*/
            ts.c_lflag |= ECHONL;
            tcsetattr(STDIN_FILENO,TCSAFLUSH,&ts);  /* 应用新终端设置 */
    
            fgets(passbuf,1024,stdin);     /* 输入字符不会在终端显示 */
            printf("you input character = %s\n",passbuf);
    
            tcsetattr(STDIN_FILENO,TCSANOW,&ots);  /* 恢复旧的终端设备 */
    }
    用ioctl关闭DTR的方法:
    #include
    #include   
    int fd;
    int status;
    ioctl(fd, TIOCMGET, &status);
    status &= ~TIOCM_DTR;

    ioctl(fd, TIOCMSET, &status);

    Chapter 4. 使用gdb调试程序

    如果想利用gdb工具来调试程序,在编译程序时要使用-g选项。如:

    debian:~/c# gcc -g serial.c -o serial
    

    调试serial程序。

    debian:~/c# gdb serial
    GNU gdb 6.5-debian
    Copyright (C) 2006 Free Software Foundation, Inc.
    GDB is free software, covered by the GNU General Public License, and you are
    welcome to change it and/or distribute copies of it under certain conditions.
    Type "show copying" to see the conditions.
    There is absolutely no warranty for GDB.  Type "show warranty" for details.
    This GDB was configured as "i486-linux-gnu"...Using host libthread_db library "/lib/tls/libthread_db.so.1".
    
    (gdb) list
    8       #include           /*错误号定义*/
    9
    10      int main(void)
    11      {
    12              int fd,n,status,buffsize;
    13              struct termios a;
    14              struct termios *oldtio;
    15              char m[255],*comm;
    16
    17              fd = open("/dev/ttyS0",O_RDWR|O_NOCTTY|O_NDELAY);
    (gdb)
    

    gdb的list命令是列出程序源码。下面介绍gdb下的各种操作。

    • list,列出程序源代码,一次只列出10行的内容。list命令可以指定范围。如:list 5,10可列出第5行到第10行的内容。

    • run,执行程序。按Ctrl+c可中断程序的执行。

    • shell,暂时退出gdb回到shell环境。在shell环境用exit命令可以返回gdb。

    • break,设置断点,后跟行号则把断点设置在指定的行号,后跟函数名则把断点设置在函数。如break 6,break function。还可根据条件设置断点,如:break 9 if result > 50。这条命令的意思是,当运行到第9行时,如果result变量的值大于50,则中断程序。

      (gdb) break 6
      Breakpoint 1 at 0x8048634: file serial.c, line 6.
      
    • watch,指定条件,如果成立则中断。如:watch result >50。当result的变量大于50时,马上中断程序。

    • print,打印变量值,如:print result。

    • whatis,查看变量类型,如:whatis result。

    • continue,从中断点继续运行程序。

    • step,从中断点开始单步运行,如果遇到函数,则进入函数单步运行

    • next,从中断点开始单步运行,如果遇到函数,则运行函数,该命令不会进入函数单步运行,而是运行整个函数。

    • info breakpoints,查看程序中所设置的所有中断点信息。

      (gdb) info breakpoints
      Num Type           Disp Enb Address    What
      1   breakpoint     keep y   0x08048634 in main at serial.c:6
      

      Enb字段是"y",表示断点1现正生效。

    • disable/enable,控制中断点失效和启用。如:disable 1。如果disable/enable命令后没有指定断点号,则该命令作用于所有已设置的断点。

      (gdb) disable 1
      (gdb) info breakpoints
      Num Type           Disp Enb Address    What
      1   breakpoint     keep n   0x08048634 in main at serial.c:6
      

      Enb字段由"y"变成"n",断点1暂时被禁止。

    • enable once,使断点生效一次。

    • delete,删除断点。如:delete 1。delete要指定断点号。

    • clear,删除断点。如:clear 6。clear要指定设置断点的行号或函数名。

    • help all,显示所有gdb环境的命令。

    在gdb环境下,按tab键可自动补全命令。直接按回车键可重复执行上一个操作。按上下光标键可显示历史命令。
阅读(2276) | 评论(0) | 转发(0) |
给主人留下些什么吧!~~