精华内容
参与话题
问答
  • https://blog.csdn.net/specialshoot/article/details/50707965 linux串口通信主要有下面几个步骤   下面我会一一介绍这几个步骤。 1.打开串口 代码(串口为ttyUSB0) ...

    https://blog.csdn.net/specialshoot/article/details/50707965

    linux下串口通信主要有下面几个步骤

                                                                                 Linux串口通信流程

    下面我会一一介绍这几个步骤。

    1.打开串口

    代码(串口为ttyUSB0)

    //打开串口
    int open_port(void)
    {
    	int fd;
    		
    	fd=open("/dev/ttyUSB0",O_RDWR | O_NOCTTY | O_NONBLOCK);//O_NONBLOCK设置为非阻塞模式,在read时不会阻塞住,在读的时候将read放在while循环中,下一节篇文档将详细讲解阻塞和非阻塞
    //	printf("fd=%d\n",fd);
    	
    	if(fd==-1)
    	{
    		perror("Can't Open SerialPort");
    	}
    	
    	return fd;
    }
    

    打开串口时也可以多加一些内容,比如判断串口为阻塞状态、测试是否为终端设备等,这些是必要的,所以较上面的基本的打开串口的代码,更加完整健壮一些的代码流程如下所示:

    打开串口较完整流程图

    代码:

    /**
     * open port
     * @param  fd
     * @param  comport 想要打开的串口号
     * @return  返回-1为打开失败
     */
    int open_port(int fd,int comport) 
    { 
    	char *dev[]={"/dev/ttyUSB0","/dev/ttyS1","/dev/ttyS2"};
     
    	if (comport==1)//串口1 
    	{
    		fd = open( "/dev/ttyUSB0", O_RDWR|O_NOCTTY|O_NDELAY); 
    		if (-1 == fd)
    		{ 
    			perror("Can't Open Serial Port"); 
    			return(-1); 
    		} 
         } 
         else if(comport==2)//串口2 
         {     
    		fd = open( "/dev/ttyS1", O_RDWR|O_NOCTTY|O_NDELAY); //没有设置<span style="font-family: Arial, Helvetica, sans-serif;">O_NONBLOCK非阻塞模式,也可以设置为非阻塞模式,两个模式在下一篇博客中具体说明</span>
     
    		if (-1 == fd)
    		{ 
    			perror("Can't Open Serial Port"); 
    			return(-1); 
    		} 
         } 
         else if (comport==3)//串口3 
         { 
    		fd = open( "/dev/ttyS2", O_RDWR|O_NOCTTY|O_NDELAY); 
    		if (-1 == fd)
    		{ 
    			perror("Can't Open Serial Port"); 
    			return(-1); 
    		} 
         } 
         /*恢复串口为阻塞状态*/ 
         if(fcntl(fd, F_SETFL, 0)<0) 
         		printf("fcntl failed!\n"); 
         else 
    		printf("fcntl=%d\n",fcntl(fd, F_SETFL,0)); 
         /*测试是否为终端设备*/ 
         if(isatty(STDIN_FILENO)==0) 
    		printf("standard input is not a terminal device\n"); 
         else 
    		printf("isatty success!\n"); 
         printf("fd-open=%d\n",fd); 
         return fd; 
    }
    

    关键函数解释:

    open

    功能描述:用于打开或创建文件,成功则返回文件描述符,否则返回-1,open返回的文件描述符一定是最小的未被使用的描述符

    #include<fcntl.h>
    int open(const char *pathname, int oflag, ... );
    

    参数解释:

     

    pathname:文件路径名,串口在Linux中被看做是一个文件

    oflag:一些文件模式选择,有如下几个参数可以设置

     

    • O_RDONLY只读模式
    • O_WRONLY只写模式
    • O_RDWR读写模式

    上面三个参数在设置的时候必须选择其中一个!!!下面的是可选的

     

     

    • O_APPEND每次写操作都写入文件的末尾
    • O_CREAT如果指定文件不存在,则创建这个文件
    • O_EXCL如果要创建的文件已存在,则返回 -1,并且修改 errno 的值
    • O_TRUNC如果文件存在,并且以只写/读写方式打开,则清空文件全部内容
    • O_NOCTTY如果路径名指向终端设备,不要把这个设备用作控制终端。
    • O_NONBLOCK如果路径名指向 FIFO/块文件/字符文件,则把文件的打开和后继 I/O设置为非阻塞模式(nonblocking mode)

    下面三个常量同样是选用的,他们用于同步输入输出

     

    • O_DSYNC等待物理 I/O 结束后再 write。在不影响读取新写入的数据的前提下,不等待文件属性更新。
    • O_RSYNC读(read)等待所有写入同一区域的写操作完成后再进行
    • O_SYNC等待物理 I/O 结束后再 write,包括更新文件属性的 I/O

    对于串口的打开操作,必须使用O_NOCTTY参数,它表示打开的是一个终端设备,程序不会成为该端口的控制终端。如果不使用此标志,任务的一个输入(比如键盘终止信号等)都会影响进程。

     

    O_NDELAY表示不关心DCD信号所处的状态(端口的另一端是否激活或者停止)。

    fcntl

    功能描述:根据文件描述词来操作文件的特性,返回-1代表出错

    #include<unistd.h>
    #include<fcntl.h>
    int fcntl(int fd,int cmd);
    int fcntl(int fd,int cmd,long arg);
    int fcntl(int fd,int cmd,struct flock *lock);
    

     

    参数说明:

     

     

    • fd:文件描述符
    • cmd:命令参数

    fcntl函数有5种功能: 
    1. 复制一个现有的描述符(cmd=F_DUPFD). 
    2. 获得/设置文件描述符标记(cmd=F_GETFD或F_SETFD). 
    3. 获得/设置文件状态标记(cmd=F_GETFL或F_SETFL). 
    4. 获得/设置异步I/O所有权(cmd=F_GETOWN或F_SETOWN). 
    5. 获得/设置记录锁(cmd=F_GETLK , F_SETLK或F_SETLKW).

    具体使用见http://www.cnblogs.com/lonelycatcher/archive/2011/12/22/2297349.html

    isatty

    函数功能,实现只使用了一个终端专用的函数tcgetattr(如果成功之星,它不改变任何东西),并取其返回值。若为终端设备返回1,否则返回0。详情见http://blog.csdn.net/wangjingyu00711/article/details/41693155

    2.串口的初始化

    串口初始化工作需要做以下工作:

    1. 设置波特率
    2. 设置数据流控制
    3. 设置帧的格式(即数据位个数,停止位,校验位)

    串口初始化

    串口初始化

    代码:

    int set_opt(int fd,int nSpeed, int nBits, char nEvent, int nStop) 
    { 
         struct termios newtio,oldtio; 
    /*保存测试现有串口参数设置,在这里如果串口号等出错,会有相关的出错信息*/ 
         if  ( tcgetattr( fd,&oldtio)  !=  0) {  
          perror("SetupSerial 1");
    	printf("tcgetattr( fd,&oldtio) -> %d\n",tcgetattr( fd,&oldtio)); 
          return -1; 
         } 
         bzero( &newtio, sizeof( newtio ) ); 
    /*步骤一,设置字符大小*/ 
         newtio.c_cflag  |=  CLOCAL | CREAD;  
         newtio.c_cflag &= ~CSIZE;  
    /*设置停止位*/ 
         switch( nBits ) 
         { 
         case 7: 
          newtio.c_cflag |= CS7; 
          break; 
         case 8: 
          newtio.c_cflag |= CS8; 
          break; 
         } 
    /*设置奇偶校验位*/ 
         switch( nEvent ) 
         { 
         case 'o':
         case 'O': //奇数 
          newtio.c_cflag |= PARENB; 
          newtio.c_cflag |= PARODD; 
          newtio.c_iflag |= (INPCK | ISTRIP); 
          break; 
         case 'e':
         case 'E': //偶数 
          newtio.c_iflag |= (INPCK | ISTRIP); 
          newtio.c_cflag |= PARENB; 
          newtio.c_cflag &= ~PARODD; 
          break;
         case 'n':
         case 'N':  //无奇偶校验位 
          newtio.c_cflag &= ~PARENB; 
          break;
         default:
          break;
         } 
         /*设置波特率*/ 
    switch( nSpeed ) 
         { 
         case 2400: 
          cfsetispeed(&newtio, B2400); 
          cfsetospeed(&newtio, B2400); 
          break; 
         case 4800: 
          cfsetispeed(&newtio, B4800); 
          cfsetospeed(&newtio, B4800); 
          break; 
         case 9600: 
          cfsetispeed(&newtio, B9600); 
          cfsetospeed(&newtio, B9600); 
          break; 
         case 115200: 
          cfsetispeed(&newtio, B115200); 
          cfsetospeed(&newtio, B115200); 
          break; 
         case 460800: 
          cfsetispeed(&newtio, B460800); 
          cfsetospeed(&newtio, B460800); 
          break; 
         default: 
          cfsetispeed(&newtio, B9600); 
          cfsetospeed(&newtio, B9600); 
         break; 
         } 
    /*设置停止位*/ 
         if( nStop == 1 ) 
          newtio.c_cflag &=  ~CSTOPB; 
         else if ( nStop == 2 ) 
          newtio.c_cflag |=  CSTOPB; 
    /*设置等待时间和最小接收字符*/ 
         newtio.c_cc[VTIME]  = 0; 
         newtio.c_cc[VMIN] = 0; 
    /*处理未接收字符*/ 
         tcflush(fd,TCIFLUSH); 
    /*激活新配置*/ 
    if((tcsetattr(fd,TCSANOW,&newtio))!=0) 
         { 
          perror("com set error"); 
          return -1; 
         } 
         printf("set done!\n"); 
         return 0; 
    } 
    

    讲解这片代码之前,我们要先研究一下termios的数据结构。最小的termios结构的典型定义如下:

    struct termios
    {
               tcflag_t c_iflag;
               tcflag_t c_oflag;
               tcflag_t c_cflag;
               tcflag_t c_lflag;
               cc_t           c_cc[NCCS];
    };
    

    上面五个结构成员名称分别代表:

    • c_iflag:输入模式
    • c_oflag:输出模式
    • c_cflag:控制模式
    • c_lflag:本地模式
    • c_cc[NCCS]:特殊控制模式

    五种模式的参数说明见博客http://blog.csdn.net/querdaizhi/article/details/7436722
    tcgetattr可以初始化一个终端对应的termios结构,tcgetattr函数原型如下:

    #include<termios.h>  
    int tcgetattr(int fd, struct termios *termios_p); 
    

    这个函数调用把低昂前终端接口变量的值写入termios_p参数指向的结构。如果这些值其后被修改了,可以通过调用函数tcsetattr来重新配置。

    tcsetattr函数原型如下:

    #include<termios.h>  
    int tcsetattr(int fd , int actions , const struct termios *termios_h);  
    

    参数actions控制修改方式,共有三种修改方式,如下所示:

    1. TCSANOW:立刻对值进行修改
    2. TCSADRAIN:等当前的输出完成后再对值进行修改
    3. TCSAFLUSH:等当前的输出完成之后,再对值进行修改,但丢弃还未从read调用返回的当前的可用的任何输入。

    在我们的代码中,我们设置为NOW立即对值进行修改。

    tcflush用于清空中端为完成的输入/输出请求及数据,它的函数原型如下:

    int tcflush(int fd, int queue_selector);
    

    其中queue_selector时控制tcflush的操作,取值可以为如下参数中的一个:TCIFLUSH清楚正收到的数据,且不会读出来;TCOFLUSH清楚正写入的数据,且不会发送至终端;TCIOFLUSH清除所有正在发送的I/O数据。

    再看我们的代码,我们修改字符大小的代码为

    newtio.c_cflag  |=  CLOCAL | CREAD;  
    newtio.c_cflag &= ~CSIZE;  
    

    c_cflag代表控制模式

    • CLOCAL含义为忽略所有调制解调器的状态行,这个目的是为了保证程序不会占用串口。
    • CREAD代表启用字符接收器,目的是是的能够从串口中读取输入的数据。
    • CS5/6/7/8表示发送或接收字符时使用5/6/7/8比特。
    • CSTOPB表示每个字符使用两位停止位。
    • HUPCL表示关闭时挂断调制解调器。
    • PARENB:启用奇偶校验码的生成和检测功能。
    • PARODD:只使用奇校验而不使用偶校验。

    c_iflag代表输入模式

     

    • BRKINT:当在输入行中检测到一个终止状态时,产生一个中断。
    • TGNBRK:忽略输入行中的终止状态。
    • TCRNL:将接受到的回车符转换为新行符。
    • TGNCR:忽略接受到的新行符。
    • INLCR:将接受到的新行符转换为回车符。
    • IGNPAR:忽略奇偶校检错误的字符。
    • INPCK:对接收到的字符执行奇偶校检。
    • PARMRK:对奇偶校检错误作出标记。
    • ISTRIP:将所有接收的字符裁减为7比特。
    • IXOFF:对输入启用软件流控。
    • IXON:对输出启用软件流控。

    c_cc特殊的控制字符

     

     

    标准模式和非标准模式下,c_cc数组的下标有不同的值:

    标准模式:

     

    • VEOF:EOF字符
    • VEOL:EOF字符
    • VERASE:ERASE字符
    • VINTR:INTR字符
    • VKILL:KILL字符
    • VQUIT:QUIT字符
    • VSTART:START字符 
    • VSTOP:STOP字符

     

    非标准模式:

     

    • VINTR:INTR字符
    • VMIN:MIN值
    • VQUIT:QUIT字符
    • VSUSP:SUSP字符
    • VTIME:TIME值
    • VSTART:START字符 
    • VSTOP:STOP字符

     

    cfsetispeed和cfsetospeed用来设置输入输出的波特率,函数模型如下:

    int cfsetispeed(struct termios *termptr, speed_t speed);
    int cfsetospeed(struct termios *termptr, speed_t speed);
    

    参数说明:

     

     

    • struct termios *termptr:指向termios结构的指针
    • speed_t speed:需要设置的波特率
    • 返回值:成功返回0,否则返回-1

     

    这样,所有的初始化操作我们就完成了。

    下一篇文章我会记录串口的读写及关闭操作的详细步骤。并且会把源代码链接给出供大家参考!

    展开全文
  • linux 串口数据接收 发送实例

    热门讨论 2010-02-25 00:13:50
    此例子经过验证 不是转载 Linux 串口 数据接收 发送 <其中列举 串口 字符串和16进制数的发送和接收 以及 串口接收字符串和16进制数,解决16进制数0x0a 0x0d 0x13 0x11接收异常的问题 此例子没有线程>
  • Linux下C语言串口应用编程

    万次阅读 多人点赞 2018-07-03 14:53:34
    在编写Linux串口的C程序之前,需要包含以下头文件:#include &lt;termios.h&gt;在Linux系统中,一切皆文件,所以串口设备也是一类文件,学习过Linux驱动程序的学员都知道,Linux有三类设备:字符设备,块...

    在编写Linux串口的C程序之前,需要包含以下头文件:

    #include <termios.h>

    Linux系统中,一切皆文件,所以串口设备也是一类文件,学习过Linux驱动程序的学员都知道,Linux有三类设备:字符设备,块设备,网络设备。那么串口设备属于字符设备。所以串口设备的命名一般为/dev/ttySn(n = 012......),如果该串口为USB转串口,可能名称为/dev/ttyUSBn(n = 012......),不同的平台下串口的名称是不同的,且串口的名称也是可以更改的。如何更改?在板卡对应的Linux驱动中更改。

    Linux下操作串口,那么也就是跟操作一个文件一样,既然是文件,也就可以使用标准的文件操作API来操作。

    1、打开串口


    2、关闭串口


    fd为文件描述符。

    如果不设置串口的波特率,数据位,停止位,校验位的情况下,Linux下默认设置的属性值为:

    波特率:9600

    数据位:8

    校验位:n(表示无)

    停止位:1

    在不设置串口属性值的情况下,也可以读写串口值。

    3、读写串口

    使用readwrite函数即可,例如:

     

    以上例程展现的是往串口写入一个数据,串口就会回复一个数据,也就是自发自收。

    完整例程:

    #include <stdio.h>
    #include <stdlib.h>
    #include <unistd.h>
    #include <fcntl.h>
    #include <asm/termios.h>
    
    #define DEV_NAME  "/dev/ttyS1"
    
    
    int main (int argc, char *argv[])
    {
    	int fd;
    	int len, i,ret;
            char buf[] = "hello ZLG!";
    
    
    	fd = open(DEV_NAME, O_RDWR | O_NOCTTY);
            if(fd < 0) {
                    perror(DEV_NAME);
                    return -1;
            }
    
    
    	len = write(fd, buf, sizeof(buf));
    	if (len < 0) {
    		printf("write data error \n");
    	}
    	
    	len = read(fd, buf, sizeof(buf));
            if (len < 0) {
                    printf("read error \n");
                    return -1;
            }
    
    	printf("%s", buf);
    
    	return(0);
    }
    
    

      上面给出的例程是串口驱动的默认属性值(9600,8n1,无流控),在实际产品开发过程中,还是会根据不同的应用场景来设置串口的属性。操作串口属性需要设置对应的标志,在POSIX终端已经帮我们实现了若干结构体以及相应的标志位,我们只要熟练使用它即可,非常简单。

      最重要的struct termios结构体:



    1、设置串口波特率


    .

    例程:

    static void set_baudrate (struct termios *opt, unsigned int baudrate)
    {
    	cfsetispeed(opt, baudrate);
    	cfsetospeed(opt, baudrate);
    }

    2、设置数据位

      数据位指的是每字节中实际数据所占的比特数。要修改数据位可以通过修改termios结构体中c_cflag成员来实现。CS5CS6CS7CS8分别表示数据位为5678。值得注意的是,在设置数据位时,必须先使用CSIZE做位屏蔽。

    例程:

    static void set_data_bit (struct termios *opt, unsigned int databit)
    {
        opt->c_cflag &= ~CSIZE;
        switch (databit) {
        case 8:
            opt->c_cflag |= CS8;
            break;
        case 7:
            opt->c_cflag |= CS7;
            break;
        case 6:
            opt->c_cflag |= CS6;
            break;
        case 5:
            opt->c_cflag |= CS5;
            break;
        default:
            opt->c_cflag |= CS8;
            break;
        }
    }

    3、设置校验位

      奇偶校验可以选择偶校验、奇校验、空格等方式,也可以不使用校验。如果要设置为偶校验的话,首先要将termios结构体中c_cflag设置PARENB标志,并清除PARODD标志。如果要设置奇校验,要同时设置termios结构体中c_cflag设置PARENB标志和PARODD标志。如果不想使用任何校验的话,清除termios结构体中c_cflag的PARENB位。表6.12所示为设置奇偶校验的具体方法。


    例程:

    static void set_parity (struct termios *opt, char parity)
    {
        switch (parity) {
        case 'N':                  /* no parity check */
            opt->c_cflag &= ~PARENB;
            break;
        case 'E':                  /* even */
            opt->c_cflag |= PARENB;
            opt->c_cflag &= ~PARODD;
            break;
        case 'O':                  /* odd */
            opt->c_cflag |= PARENB;
            opt->c_cflag |= ~PARODD;
            break;
        default:                   /* no parity check */
            opt->c_cflag &= ~PARENB;
            break;
        }
    }

    4、设置停止位

     设置串口停止位是在 termios 对象的 c_cflag 成员上设置,需要用到的选项标志为

    CSTOPB(2 位停止位,否则为 1 位)。

    例程:

    static void set_stopbit (struct termios *opt, const char *stopbit)
    {
        if (0 == strcmp (stopbit, "1")) {
            opt->c_cflag &= ~CSTOPB; /* 1 stop bit */
        }	else if (0 == strcmp (stopbit, "1")) {
            opt->c_cflag &= ~CSTOPB; /* 1.5 stop bit */
        }   else if (0 == strcmp (stopbit, "2")) {
            opt->c_cflag |= CSTOPB;  /* 2 stop bits */
        } else {
            opt->c_cflag &= ~CSTOPB; /* 1 stop bit */
        }
    }

    于是我们就可以将串口这三样基本的设置写成一个函数,以后用的时候就调用它即可。


    //串口设置
    int  Serial_Handle::set_port_attr (
                  int fd,
                  int  baudrate,          // B1200 B2400 B4800 B9600 .. B115200
                  int  databit,           // 5, 6, 7, 8
                  const char *stopbit,    //  "1", "1.5", "2"
                  char parity,            // N(o), O(dd), E(ven)
                  int vtime,
                  int vmin )
    {
         struct termios opt;
         tcgetattr(fd, &opt);
         //设置波特率
         set_baudrate(&opt, baudrate);
         opt.c_cflag 		 |= CLOCAL | CREAD;      /* | CRTSCTS */
         //设置数据位
         set_data_bit(&opt, databit);
         //设置校验位
         set_parity(&opt, parity);
         //设置停止位
         set_stopbit(&opt, stopbit);
         //其它设置
         opt.c_oflag 		 = 0;
         opt.c_lflag            	|= 0;
         opt.c_oflag          	&= ~OPOST;
         opt.c_cc[VTIME]     	 = vtime;
         opt.c_cc[VMIN]         	 = vmin;
         tcflush (fd, TCIFLUSH);
         return (tcsetattr (fd, TCSANOW, &opt));
    }

    以后在使用串口的时候即是要遵循以下的顺序才能使用。

    1、打开串口设备

    2、设置串口的波特率,数据位,校验位,停止位以及其它设置

    3、写数据

    4、读出数据

    5、关闭串口


    参考文献:

    1、http://book.51cto.com/art/200711/59758.htm

    2、周立功=====>【开发指南】M6G2C&A6G2C系列核心板软件开发指南_V1.05







    展开全文
  • Linux 检测串口并查看串口消息

    万次阅读 2018-09-12 15:19:23
    背景:在win7 系统不能识别usb,尝试使用Ubuntu来识别串口并查看串口消息 当给电脑插上USB串口线后,先在windows下设备管理器查看端口,对应虚拟机下会多一个可移动设备   在ubuntu虚拟机下的可移动设备的串口...

    背景:在win7 系统不能识别usb,尝试使用Ubuntu来识别串口并查看串口消息

    当给电脑插上USB串口线后,先在windows下设备管理器查看端口,对应虚拟机下会多一个可移动设备 
    这里写图片描述

     在ubuntu虚拟机下的可移动设备的串口设备点击连接。
    这里写图片描述
     

    显示所有USB设备(插USB和不插USB对比查看是否识别了串口)

    ticy@ubuntu:~$ lsusb
    Bus 001 Device 001: ID 1d6b:0002 Linux Foundation 2.0 root hub
    Bus 002 Device 003: ID 0e0f:0002 VMware, Inc. Virtual USB Hub
    Bus 002 Device 002: ID 0e0f:0003 VMware, Inc. Virtual Mouse
    Bus 002 Device 001: ID 1d6b:0001 Linux Foundation 1.1 root hub
    ticy@ubuntu:~$ lsusb
    Bus 001 Device 001: ID 1d6b:0002 Linux Foundation 2.0 root hub
    Bus 002 Device 004: ID 0403:6001 Future Technology Devices International, Ltd FT232 USB-Serial (UART) IC
    Bus 002 Device 003: ID 0e0f:0002 VMware, Inc. Virtual USB Hub
    Bus 002 Device 002: ID 0e0f:0003 VMware, Inc. Virtual Mouse
    Bus 002 Device 001: ID 1d6b:0001 Linux Foundation 1.1 root hub

    安装minicom

    sudo apt-get install minicom

    安装烧写协议

     sudo apt-get install lrzsz

    配置minicom

    sudo minicom -s

    打开minicom配置界面

    选择第3个Serial port setup 
    这里写图片描述
    进入后按自己设备的情况配置好,矩形框内的参数。

    • 输入a或者A,选择串口设备,在这里我使用的是USB转串口,并且我的开发板连接到了COM1上,将/dev/tty8修改为/dev/ttyUSB(命令dmesg | grep tty------查看开发板连接的串口编号)
    •  配置完串口设备后,按Enter键,再输入E,配置波特率,按默认配置即可  115200 8N1 (波特率:115200,数据位:8,奇偶校验位:N 无,停止位:1)。
    •    配置完波特率,按Enter键,再输入F,配置硬件流控,选择NO
    •    再继续配置软件流控,也选择NO。
    • 按下Enter键返回上一界面,选择save setup as dfl(即将其保存位默认配置),再选择Exit,关闭minicom。
    • minicom的键盘操作快捷键是配合Ctrl+A+松开再另外一个键的组合键,输入Ctrl+A+Z可以看到全部的快捷键

    这里写图片描述

    进入minicom

    sudo minicom

    (我实验中没有出现OK 及下面内容)
    这里写图片描述

     


     

    展开全文
  • C——Linux下的串口编程

    万次阅读 多人点赞 2017-06-06 19:30:50
    之前在学习安信可A7模块时,是在PC上使用串口调试助手做了GPS的坐标数据信息的采集,同时分析了一些语句的含义。在这过程中,涉及到对嵌入式开发人员一个非常重要的知识:串口通信。在前篇也说到,我们将会自己写...

    之前在学习安信可A7模块时,是在PC上使用串口调试助手做了GPS的坐标数据信息的采集,同时分析了一些语句的含义。在这过程中,涉及到对嵌入式开发人员一个非常重要的知识:串口通信。在前篇也说到,我们将会自己写程序来对GPS数据进行解析,而这些数据正是靠串口来传输的。所以,本篇博文将进行关于串口通信的学习。

    一、串口接头

    首先我们得知道串口长什么样,常用的串口接头有两种,一种是9针串口(简称DB-9),一种是25针串口(简称DB-25)。每种接头都有公头和母头之分,其中带针状的接头是公头,而带孔状的接头是母头。
    以DB9为例,如图:
    这里写图片描述
    各个针脚功能说明:
    这里写图片描述
    在TXD和RXD数据线上:
      (1)逻辑1为-3~-15V的电压
      (2)逻辑0为3~15V的电压
    在RTS、CTS、DSR、DTR和DCD等控制线上:
      (1)信号有效(ON状态)为3~15V的电压
      (2)信号无效(OFF状态)为-3~-15V的电压
    这是由通信协议RS-232C规定的(请看后文)。

    注:一般我们需要的就是2,3,5接口,连接时是TXD接RXD,RXD接TXD,GND接GND。自己的TXD口接RXD口,自发自收,测试串口是否正常。

    二、串口通信基础知识

    OK,知道了串口的样子了,接下来就要更进一步,学习串口通信的基础知识了。

    1、什么是串口通信?

    串口通信(Serial Communication),是指外设和计算机间,通过数据信号线、地线等,按位进行传输数据的一种通讯方式。
    串口是一种接口标准,它规定了接口的电气标准,没有规定接口插件电缆以及使用的协议。

    2、串口通信协议

    在串口通信中,常用的协议包括RS-232、RS-422和RS-485。
    •RS-232:标准串口,最常用的一种串行通讯接口。有三种类型(A,B和C),它们分别采用不同的电压来表示on和off。最被广泛使用的是RS-232C,它将mark(on)比特的电压定义为-3V到-12V之间,而将space(off)的电压定义到+3V到+12V之间。传送距离最大为约15米,最高速率为20kb/s。RS-232是为点对点(即只用一对收、发设备)通讯而设计的,其驱动器负载为3~7kΩ。所以RS-232适合本地设备之间的通信。
    •RS-422:最大传输距离为1219米,最大传输速率为10Mb/s。其平衡双绞线的长度与传输速率成反比,在100kb/s速率以下,才可能达到最大传输距离。只有在很短的距离下才能获得最高速率传输。一般100米长的双绞线上所能获得的最大传输速率仅为1Mb/s。
    •RS-485:从RS-422基础上发展而来的,最大传输距离约为1219米,最大传输速率为10Mb/s。平衡双绞线的长度与传输速率成反比,在100kb/s速率以下,才可能使用规定最长的电缆长度。只有在很短的距离下才能获得最高速率传输。一般100米长双绞线最大传输速率仅为1Mb/s。

    3、同步通信?异步通信?

    同步通信:是一种比特同步通信技术,要求发收双方具有同频同相的同步时钟信号,只需在传送报文的最前面附加特定的同步字符,使发收双方建立同步,此后便在同步时钟的控制下逐位发送/接收。如:SPI总线。
    异步通信:指两个互不同步的设备通过计时机制或其他技术进行数据传输。也就是说,双方不需要共同的时钟。发送方可以随时传输数据,而接收方必须在信息到达时准备好接收。如:串口(UART)。
    接下来在后续的串口编程中讨论的都是异步通信,所以对同步通信不做过多的赘述了。
    这里提一下UART和USART,实际上,从字面意思即可理解:
    UART:universal asynchronous receiver and transmitter(通用异步收/发器)。
    USART:universal synchronous asynchronous receiver and transmitter(通用同步/异步收/发器)。
    USART在UART基础上增加了同步功能,即USART是UART的增强型。
    我常使用的S3C2440上就是支持的UART。

    3、通信方式

    •单工模式(Simplex Communication):单向的数据传输。通信双方中,一方为发送端,一方则为接收端。信息只能沿一个方向传输,使用一根传输线。双方是固定的。
    •半双工模式(Half Duplex):通信使用同一根传输线,既可以发送数据又可以接收数据,但不能同时进行发送和接收。数据传输允许数据在两个方向上传输,但是,在任何时刻只能由其中的一方发送数据,另一方接收数据。
    •全双工模式(Full Duplex)通信允许数据同时在两个方向上传输。因此,全双工通信是两个单工通信方式的结合,它要求发送设备和接收设备都有独立的接收和发送能力。在全双工模式中,每一端都有发送器和接收器,有两条传输线,信息传输效率高。

    4、数据格式

    我们有必要先弄清楚异步通信的数据格式。
    这里写图片描述
    (1)起始位:起始位必须是持续一个比特时间的“0”,标志传输一个字符的开始。
    (2)数据位:数据位紧跟在起始位之后,是通信中的真正有效信息。数据位的位数可以由通信双方共同约定,一般可以是5位、7位或8位。传输数据时先传送字符的低位,后传送字符的高位。
    (3)奇偶校验位:奇偶校验位仅占一位,用于进行奇校验或偶校验,奇偶检验位不是必须有的。如果是奇校验,需要保证传输的数据总共有奇数个“1”;如果是偶校验,需要保证传输的数据总共有偶数个“1”。
      举例来说,假设传输的数据位为01001100,如果是奇校验,则奇校验位为0(要确保总共有奇数个1),如果是偶校验,则偶校验位为1(要确保总共有偶数个1)。
      由此可见,奇偶校验位仅是对数据进行简单的置逻辑高位或逻辑低位,不会对数据进行实质的判断,这样做的好处是接收设备能够知道一个位的状态,有可能判断是否有噪声干扰了通信以及传输的数据是否同步。
    (4)停止位:停止位可以是是1位、1.5位或2位,可以由软件设定。它一定是“1”,标志着传输一个字符的结束。
    (5)空闲位:空闲位是指从一个字符的停止位结束到下一个字符的起始位开始,表示线路处于空闲状态,必须由高电平来填充。

    好了,一些基础知识暂时先到这里,更深入的知识得自己自行了解了,接下来便是重头戏,在Linux下的串口编程了。

    ===========================================
    这里同时可以参考我之前的博文:STM8串口 (有相关经验的话)。对比一下两者的区别来进行学习。

    ===========================================

    1、首先是操作串口需要包含的头文件:

    #include <stdio.h>   /*标准输入输出的定义*/
    #include <errno.h>  /*错误号定义*/
    #include <sys/stat.h>
    #include <fcntl.h>  /*文件控制定义*/
    #include <termios.h>    /*PPSIX 终端控制定义*/
    #include <stdlib.h> /*标准函数库定义*/
    #include <sys/types.h>
    #include <unistd.h> /*UNIX 标准函数定义*/

    2、串口相关操作
    打开串口:
    我们都知道,在Linux下,除了网络设备,其余的都是文件的形式。串口设备也一样在/dev下。
    这里写图片描述
    所以我们可以通过open系统调用/函数来访问它。
    示例:fd = open("/dev/ttyUSB0",O_RDWR|O_NOCTTY|O_NDELAY);
    O_NOCTTY:可以告诉Linux这个程序不会成为这个端口上的“控制终端”.如果不这样做的话,所有的输入,比如键盘上过来的Ctrl+C中止信号等等,会影响到你的进程。
    O_NDELAY:标志则是告诉Linux,这个程序并不关心DCD信号线的状态——也就是不关心端口另一端是否已经连接。

    读写串口:
    与普通文件一样,使用read,write函数。
    示例:read(fd,buff,8);
    write(fd,buff,8);

    串口属性设置:
    最基本的设置串口包括波特率设置,效验位和停止位设置。这由通信双方协定。

    很多系统都支持POSIX终端(串口)接口.程序可以利用这个接口来改变终端的参数,比如,波特率,字符大小等等.要使用这个端口的话,你必须将<termios.h>头文件包含到你的程序中。这个头文件中定义了终端控制结构体和POSIX控制函数。

    最重要的就是这个结构体:

    struct termios
          {
          tcflag_t  c_iflag;  //输入选项
          tcflag_t  c_oflag;  //输出选项
          tcflag_t  c_cflag;  //控制选项
          tcflag_t  c_lflag;  //行选项
          cc_t      c_cc[NCCS]; //控制字符
          }; 

    其中我们更关注的是c_cflag控制选项。其中包含了波特率、数据位、校验位、停止位的设置。
    它可以支持很多常量名称其中设置数据传输率为相应的数据传输率前要加上“B”。
    c_cflag成员不能直接对其初始化,而要将其通过与、或操作使用其中的某些选项。
    设置串口属性主要是配置termios结构体中的各个变量,大致流程如下:

    1.使用函数tcgetattr保存原串口属性
    struct termios newtio,oldtio;
    tcgetattr(fd,&oldtio);

    2.通过位掩码的方式激活本地连接和接受使能选项:CLOCAL和CREAD
    newtio.c_cflag | = CLOCAL | CREAD;

    3.使用函数cfsetispeed和cfsetospeed设置数据传输率
    cfsetispeed(&newtio,B115200);
    cfsetospeed(&newtio,B115200);

    4.通过位掩码设置字符大小。
    newtio.c_cflag &= ~CSIZE;
    newtio.c_cflag |= CS8;

    5.设置奇偶效验位需要用到两个termios中的成员:c_cflag和c_iflag。首先要激活c_cflag中的校验位使能标志PARENB和是否进行奇偶效验,同时还要激活c_iflag中的奇偶效验使能。
    设置奇校验:
    newtio.c_cflag |= PARENB;
    newtio.c_cflag |= PARODD;
    newtio.c_iflag |= (INPCK | ISTRIP);
    设置偶校验:
    newtio.c_iflag |= (INPCK|ISTRIP);
    newtio.c_cflag |= PARENB;
    newtio.c_cflag |= ~PARODD;

    6.激活c_cflag中的CSTOPB设置停止位。若停止位为1,则清除CSTOPB;若停止位为0,则激活CSTOPB。
    newtio.c_cflag &= ~CSTOPB;

    7.设置最少字符和等待时间。在对接收字符和等待时间没有特别要求的情况下,可以将其设置为0。
    newtio.c_cc[VTIME] = 0;
    newtio.c_cc[VMIN] = 0;

    8.调用函数”tcflush(fd,queue_selector)”来处理要写入引用的对象,queue_selector可能的取值有以下几种。
    TCIFLUSH:刷新收到的数据但是不读
    TCOFLUSH:刷新写入的数据但是不传送
    TCIOFLUSH:同时刷新收到的数据但是不读,并且刷新写入的数据但是不传送。

    9.激活配置。在完成配置后,需要激活配置使其生效。使用tcsetattr()函数。
    int tcsetattr(int filedes,int opt,const struct termios *termptr);

    最后贴出串口配置的完整代码:

    /*********************************************************************************
      *      Copyright:  (C) 2017 TangBin<tangbinmvp@gmail.com>
      *                  All rights reserved.
      *
      *       Filename:  s_uart1.c
      *    Description:  This file 
      *                 
      *        Version:  1.0.0(06/04/2017)
      *         Author:  TangBin <tangbinmvp@gmail.com>
      *      ChangeLog:  1, Release initial version on "06/04/2017 07:51:59 PM"
      *                 
      ********************************************************************************/
    #include <stdio.h>
    #include <errno.h>
    #include <sys/stat.h>
    #include <fcntl.h>
    #include <termios.h>
    #include <stdlib.h>
    #include <string.h>
    #include <sys/types.h>
    #include <unistd.h>
    
    
    int set_serial(int fd,int nSpeed,int nBits,char nEvent,int nStop)
    {
        struct termios newttys1,oldttys1;
    
         /*保存原有串口配置*/
         if(tcgetattr(fd,&oldttys1)!=0) 
         {
              perror("Setupserial 1");
              return -1;
         }
         bzero(&newttys1,sizeof(newttys1));
         newttys1.c_cflag|=(CLOCAL|CREAD ); /*CREAD 开启串行数据接收,CLOCAL并打开本地连接模式*/
    
         newttys1.c_cflag &=~CSIZE;/*设置数据位*/
         /*数据位选择*/   
         switch(nBits)
         {
             case 7:
                 newttys1.c_cflag |=CS7;
                 break;
             case 8:
                 newttys1.c_cflag |=CS8;
                 break;
         }
         /*设置奇偶校验位*/
         switch( nEvent )
         {
             case '0':  /*奇校验*/
                 newttys1.c_cflag |= PARENB;/*开启奇偶校验*/
                 newttys1.c_iflag |= (INPCK | ISTRIP);/*INPCK打开输入奇偶校验;ISTRIP去除字符的第八个比特  */
                 newttys1.c_cflag |= PARODD;/*启用奇校验(默认为偶校验)*/
                 break;
             case 'E':/*偶校验*/
                 newttys1.c_cflag |= PARENB; /*开启奇偶校验  */
                 newttys1.c_iflag |= ( INPCK | ISTRIP);/*打开输入奇偶校验并去除字符第八个比特*/
                 newttys1.c_cflag &= ~PARODD;/*启用偶校验*/
                 break;
             case 'N': /*无奇偶校验*/
                 newttys1.c_cflag &= ~PARENB;
                 break;
         }
         /*设置波特率*/
        switch( nSpeed )  
        {
            case 2400:
                cfsetispeed(&newttys1, B2400);
                cfsetospeed(&newttys1, B2400);
                break;
            case 4800:
                cfsetispeed(&newttys1, B4800);
                cfsetospeed(&newttys1, B4800);
                break;
            case 9600:
                cfsetispeed(&newttys1, B9600);
                cfsetospeed(&newttys1, B9600);
                break;
            case 115200:
                cfsetispeed(&newttys1, B115200);
                cfsetospeed(&newttys1, B115200);
                break;
            default:
                cfsetispeed(&newttys1, B9600);
                cfsetospeed(&newttys1, B9600);
                break;
        }
         /*设置停止位*/
        if( nStop == 1)/*设置停止位;若停止位为1,则清除CSTOPB,若停止位为2,则激活CSTOPB*/
        {
            newttys1.c_cflag &= ~CSTOPB;/*默认为一位停止位; */
        }
        else if( nStop == 2)
        {
            newttys1.c_cflag |= CSTOPB;/*CSTOPB表示送两位停止位*/
        }
    
        /*设置最少字符和等待时间,对于接收字符和等待时间没有特别的要求时*/
        newttys1.c_cc[VTIME] = 0;/*非规范模式读取时的超时时间;*/
        newttys1.c_cc[VMIN]  = 0; /*非规范模式读取时的最小字符数*/
        tcflush(fd ,TCIFLUSH);/*tcflush清空终端未完成的输入/输出请求及数据;TCIFLUSH表示清空正收到的数据,且不读取出来 */
    
         /*激活配置使其生效*/
        if((tcsetattr( fd, TCSANOW,&newttys1))!=0)
        {
            perror("com set error");
            return -1;
        }
    
        return 0;
    }
    

    下一篇将具体结合GPS的数据解析,便能更好从整体上理解、学习了。

    展开全文
  • Linux 串口编程简介和实例学习

    千次阅读 2018-04-14 15:35:24
    Linux 串口编程简介和实例学习一、无论是从linux官方直接下载的原生态内核还是任何一家芯片厂家提供的linux内核,都已经把串口驱动写好了,所以在linux串口编程中,是完全不需要动手去写串口驱动的。对于一般的...
  • linux串口使用

    千次阅读 2015-10-16 11:24:22
    前面已经提到过Linux下皆为文件,这当然也包括我们今天的主角àUART0串口。因此对他的一切操作都和文件的操作一样(涉及到了open,read,write,close等文件的基本操作)。 一.Linux下的串口编程又那几部分组成 ...
  • Linux串口收发通信

    万次阅读 多人点赞 2018-09-19 01:47:15
    Linux下编程的过程有些固定,很多都是比如打开、配置、关闭等等 串口通信流程:打开串口ttySn---&gt;初始化串口---&gt;读写(read、write)---&gt;关闭串口 最合适的指导书:...
  • Linux串口编程

    千次阅读 2019-05-18 13:32:23
    一、串口通信介绍 串口是计算机上的串行通信的物理接口。首先先介绍一下串行通信,串行通信的分类: 1、按照数据传送方向,分为: 单工:数据传输只支持数据在一个方向上传输;就像路上的单行线。 半双工:允许...
  • linux串口操作及设置详解

    千次阅读 2014-10-17 10:07:51
    串口操作需要的头文件 #include /*标准输入输出定义*/ #include /*标准函数库定义*/ #include /*Unix 标准函数定义*/ #include #include #include /*文件控制定义*/ #include /*PPSIX 终端控制定义*/...
  • Linux串口通信编程大全,串行通信接口标准经过使用和发展,目前已经有几种。但都是在RS-232标准的基础上 经过改进而形成的。所以,以RS-232C为主来讨论。RS-323C 标准是美国EIA(电子工业联合 会)与BELL等公司一起...
  • ///////////////linux serial sample linux串口例程,linux RS232例程,/////////////linux串口配置,好用的linux串口设置 打开linux串口 设置串口//////////////串口例子,linux ttys编程,linux
  • Linux 串口调试工具汇总

    万次阅读 2017-04-15 15:06:28
    linux 系统下进行串口调试或者开发时配合简单易用的串口调试工具那是必不可少。这篇博客对当前用的较多的,且我自己经常用的一些串口工具做一下汇总,大家可以参考一下。 实验环境: OS: Ubuntu16.04 Kernel: ...
  • Linux串口驱动

    2014-02-18 17:02:02
    linux下USB串口驱动,硬件为CH341,该硬件可被系统自动识别,对串口进行操作时,只需要调用read,write等函数即可。
  • linux串口通信

    2016-02-21 23:16:28
    linux串口的资源 见博客http://blog.csdn.net/specialshoot/article/details/50707965
  • Linux 串口接收不定长数据,网上查找好久都没有找到能用的资源,就自己写了个(ARM能正常使用),最大2K缓冲,接收文件时大于1K写一次缓冲,防止读写次数过多对flash损害。
  • LINUX串口文件传输

    2018-07-25 08:55:12
    串口文件传输,数据帧格式:0xAA+0xAA+序号+数据长度+50字节数据+校验和。支持错误重传。 用法: 发: ./test /devttyUSB0 0 a.txt 收: ./test /devttyUSB1 1 b.txt a.txt为要发送的文件,b.txt为保存的文件 test...
  • Linux 串口源代码(纯C++)

    热门讨论 2015-01-08 10:47:15
    Linux 串口源代码,使用纯C++代码,没有使用其他库
  • Linux串口属性设置

    千次阅读 2019-06-15 13:40:07
    串口属于终端设备,其接口属性用termios结构描述,如程序清单13.9所示。 程序清单13.9 termios结构 structtermios{ tcflag_tc_cflag/*控制标志*/ tcflag_tc_iflag;/*输入标志*/ tcflag_tc_oflag;/*输出标志*/ ...
  • Linux串口读写

    千次阅读 2014-07-10 16:31:16
    串口简介 串行口是计算机一种常用的接口,具有连接线少,通讯简单,得到广泛的使用。常用的串口是 RS-232-C 接口(又称 EIA RS-232-C)它是在 1970 年由美国电子工业协会(EIA)联合贝尔系统、 调制解调器厂家及...
  • tcgetattr  http://baike.baidu.com/view/5644808.htm?fr=aladdin
  • LINUX串口共享

    千次阅读 2013-07-18 19:51:38
    Linux下共享串口通过在screen中执行minicom来实现。 minicom minicom是一个串口通信工具,就像Windows下的超级终端。可用来与串口设备通信,如调试交换机和Modem等。 安装 yum install minicom 配置 minicom ...
  • 一个简单的linux串口通信源代码,含串口的各种设置。
  • linux系统下串口数据接收一帧,采用select接收,超时检测,为保证准确性发送间隔必须大于20ms(数据量越大间隔必须越大),实测没发现问题,如发现问题欢迎交流指正
  • Linux 串口编程记录

    千次阅读 2019-01-18 11:48:34
    ...在 /dev/目录中 tty* ttys* 的文件为串口设备的驱动文件,现在的笔记本和台式机很多已经不在自带串口,我用了USB-串口的数据线,此时驱动为ttyUSB*,我用的Ubuntu 18.04 内核中已经自带USB-...
  • linux 串口通信详解

    千次阅读 2015-01-06 15:47:07
    Linux串口编程详解 目录(?)[+] 串口本身,标准和硬件 † 串口是计算机上的串行通讯的物理接口。计算机历史上,串口曾经被广泛用于连接计算机和终端设备和各种外部设备。虽然以太网接口...
  • linux串口登陆

    2011-04-23 09:31:52
    在这个互连网高速发展的时代, 企业内网络设备的安全和运行稳定也成为人们的焦点话题之一,在网络安全方面有句行话"技术不是万能药!".在整个安全问题的全方面考虑中,人才是最重要的环节....其实Linux...
  • linux 串口调试 图形界面工具 Qt开发
  • Linux 串口接收数据

    千次阅读 2014-05-30 10:34:06
    这两天在研究Linux下的串口通信,在做接收测试的时候遇到点问题,被坑死了。
  • Linux 串口驱动

    千次阅读 2018-07-22 13:18:27
    1 串口本质上也是字符设备 !!!!!!!!!!!!串口是不支持热拔插的。如果强行热拔插,容易损坏串口芯片!!!!!!!!!!!!!!!!!!!!! 名词解释:流控 "软件”流控制 :帧头帧尾  “硬件”流控制:信号线 一。流程  ...

空空如也

1 2 3 4 5 ... 20
收藏数 61,245
精华内容 24,498
关键字:

linux串口

linux 订阅