|簡體中文

比思論壇

 找回密碼
 按這成為會員
搜索



查看: 1652|回復: 9
打印 上一主題 下一主題

Linux下串口通信

[複製鏈接]

24

主題

0

好友

356

積分

中學生

Rank: 3Rank: 3

  • TA的每日心情
    慵懶
    2024-6-12 08:54
  • 簽到天數: 137 天

    [LV.7]常住居民III

    推廣值
    0
    貢獻值
    0
    金錢
    48
    威望
    356
    主題
    24
    樓主
    發表於 2015-12-23 15:03:48

    1.         打开串口

           与其他的关于设备编程的方法一样,在Linux下,操作、控制串口也是通过操作起设备文件进行的。在Linux下,串口的设备文件是/dev/ttyS0/dev/ttyS1等。因此要读写串口,我们首先要打开串口:

           char *dev  = "/dev/ttyS0"; //串口1

           int    fd = open( dev, O_RDWR );

            //| O_NOCTTY | O_NDELAY      

           if (-1 == fd)   

           {                  

                  perror("Can't Open Serial Port");

                  return -1;      

           }   

           else

                  return fd;

          

    2.         设置串口速度

           打开串口成功后,我们就可以对其进行读写了。首先要设置串口的波特率:

           int speed_arr[] = { B38400, B19200, B9600, B4800, B2400, B1200, B300,

                          B38400, B19200, B9600, B4800, B2400, B1200, B300, };

    int name_arr[] = {38400,  19200,  9600,  4800,  2400,  1200,  300, 38400,

                                       19200,  9600, 4800, 2400, 1200,  300, };

    void set_speed(int fd, int speed){

           int   i;

           int   status;

           struct termios   Opt;

           tcgetattr(fd, &Opt);

           for ( i= 0;  i < sizeof(speed_arr) / sizeof(int);  i++) {

                  if  (speed == name_arr) {   

                         tcflush(fd, TCIOFLUSH);   

                         cfsetispeed(&Opt, speed_arr);

                         cfsetospeed(&Opt, speed_arr);  

                         status = tcsetattr(fd, TCSANOW, &Opt);

                         if  (status != 0) {      

                                perror("tcsetattr fd");

                                return;   

                         }   

                         tcflush(fd,TCIOFLUSH);  

                  }

           }

    }

    3.         设置串口信息

    这主要包括:数据位、停止位、奇偶校验位这些主要的信息。

          /**

    *@brief   设置串口数据位,停止位和效验位

    *@param  fd     类型  int  打开的串口文件句柄

    *@param  databits 类型  int 数据位   取值 7 或者8

    *@param  stopbits 类型  int 停止位   取值为 1 或者2

    *@param  parity  类型  int  效验类型 取值为N,E,O,,S

    */

    int set_Parity(int fd,int databits,int stopbits,int parity)

    {

           struct termios options;

           if  ( tcgetattr( fd,&options)  !=  0) {

                  perror("SetupSerial 1");   

                  return(FALSE);

           }

           options.c_cflag &= ~CSIZE;

           options.c_lflag  &= ~(ICANON | ECHO | ECHOE | ISIG);  /*Input*/

           options.c_oflag  &= ~OPOST;   /*Output*/

           switch (databits) /*设置数据位数*/

           {  

           case 7:         

                  options.c_cflag |= CS7;

                  break;

           case 8:   

                  options.c_cflag |= CS8;

                  break;  

           default:   

                  fprintf(stderr,"Unsupported data size/n"); return (FALSE);

           }

    switch (parity)

    {  

           case 'n':

           case 'N':   

                  options.c_cflag &= ~PARENB;   /* Clear parity enable */

                  options.c_iflag &= ~INPCK;     /* Enable parity checking */

                  break;

           case 'o':  

           case 'O':   

                  options.c_cflag |= (PARODD | PARENB); /* 设置为奇效验*/

                  options.c_iflag |= INPCK;             /* Disnable parity checking */

                  break;

           case 'e':

           case 'E':  

                  options.c_cflag |= PARENB;     /* Enable parity */   

                  options.c_cflag &= ~PARODD;   /* 转换为偶效验*/   

                  options.c_iflag |= INPCK;       /* Disnable parity checking */

                  break;

           case 'S':

           case 's':  /*as no parity*/  

               options.c_cflag &= ~PARENB;

                  options.c_cflag &= ~CSTOPB;break;

           default:  

                  fprintf(stderr,"Unsupported parity/n");   

                  return (FALSE);

           }

    /* 设置停止位*/

    switch (stopbits)

    {  

           case 1:   

                  options.c_cflag &= ~CSTOPB;

                  break;

           case 2:   

                  options.c_cflag |= CSTOPB;

              break;

           default:   

                   fprintf(stderr,"Unsupported stop bits/n");

                   return (FALSE);

    }

    /* Set input parity option */

    if (parity != 'n')  

           options.c_iflag |= INPCK;

    tcflush(fd,TCIFLUSH);

    options.c_cc[VTIME] = 0; /* 设置超时0 seconds*/  

    options.c_cc[VMIN] = 13; /* define the minimum bytes data to be readed*/

    if (tcsetattr(fd,TCSANOW,&options) != 0)  

    {

           perror("SetupSerial 3");  

           return (FALSE);

    }

    return (TRUE);

    }

    在上述代码中,有两句话特别重要:

    options.c_cc[VTIME] = 0; /* 设置超时0 seconds*/  

    options.c_cc[VMIN] = 13; /* define the minimum bytes data to be readed*/

    这两句话决定了对串口读取的函数read()的一些功能。我将着重介绍一下他们对read()函数的影响。

           对串口操作的结构体是

    Struct{

           tcflag_t   c_iflag;    /*输入模式标记*/

           tcflag_t   c_oflag;   /*输出模式标记*/

           tcflag_t   c_cflag;   /*控制模式标记*/

           tcflag_t   c_lflag;    /*本地模式标记*/

           cc_t        c_line;     /*线路规程*/

           cc_t        c_cc[NCCS];  /*控制符号*/

    }

    其中cc_t       c_line只有在一些特殊的系统程序(比如,设置通过tty设备来通信的网络协议)中才会用。在数组c_cc中有两个下标(VTIMEVMIN)对应的元素不是控制符,并且只是在原始模式下有效。只有在原始模式下,他们决定了read()函数在什么时候返回。在标准模式下,除非设置了O_NONBLOCK选项,否则只有当遇到文件结束符或各行的字符都已经编辑完毕后才返回。

    控制符VTIMEVMIN之间有着复杂的关系。VTIME定义要求等待的零到几百毫秒的时间量(通常是一个8位的unsigned char变量,取值不能大于cc_t)VMIN定义了要求等待的最小字节数(不是要求读的字节数——read()的第三个参数才是指定要求读的最大字节数),这个字节数可能是0

    l         如果VTIME0VMIN定义了要求等待读取的最小字节数。函数read()只有在读取了VMIN个字节的数据或者收到一个信号的时候才返回。

    l         如果VMIN0VTIME定义了即使没有数据可以读取,read()函数返回前也要等待几百毫秒的时间量。这时,read()函数不需要像其通常情况那样要遇到一个文件结束标志才返回0

    l         如果VTIMEVMIN都不取0VTIME定义的是当接收到第一个字节的数据后开始计算等待的时间量。如果当调用read函数时可以得到数据,计时器马上开始计时。如果当调用read函数时还没有任何数据可读,则等接收到第一个字节的数据后,计时器开始计时。函数read可能会在读取到VMIN个字节的数据后返回,也可能在计时完毕后返回,这主要取决于哪个条件首先实现。不过函数至少会读取到一个字节的数据,因为计时器是在读取到第一个数据时开始计时的。

    l         如果VTIMEVMIN都取0,即使读取不到任何数据,函数read也会立即返回。同时,返回值0表示read函数不需要等待文件结束标志就返回了。

    这就是这两个变量对read函数的影响。我使用的读卡器每次传送的数据是13个字节,一开始,我把它们设置成

    options.c_cc[VTIME] = 150

    options.c_cc[VMIN] = 0;

    结果,每次读取的信息只有8个字节,剩下的5个字节要等到下一次打卡时才能收到。就是由于这个原因造成的。根据上面规则的第一条,我把VTIME0VMIN=13,也就是正好等于一次需要接收的字节数。这样就实现了一次读取13个字节值。同时,得出这样的结论,如果读卡器送出的数据为n个字节,那么就把VMIN=n,这样一次读取的信息正好为读卡器送出的信息,并且读取的时候不需要进行循环读取。


    2

    主題

    0

    好友

    434

    積分

    中學生

    Rank: 3Rank: 3

  • TA的每日心情
    開心
    2021-9-23 03:27
  • 簽到天數: 291 天

    [LV.8]以壇為家I

    推廣值
    0
    貢獻值
    0
    金錢
    5
    威望
    434
    主題
    2
    沙發
    發表於 2015-12-24 00:08:25
    感谢大神提供!

    5

    主題

    0

    好友

    3793

    積分

    大學生

    Rank: 6Rank: 6

  • TA的每日心情
    開心
    6 天前
  • 簽到天數: 1164 天

    [LV.10]以壇為家III

    推廣值
    0
    貢獻值
    2
    金錢
    361
    威望
    3793
    主題
    5
    板凳
    發表於 2015-12-24 01:18:26
    厉害 厉害 厉害

    0

    主題

    0

    好友

    114

    積分

    小學生

    Rank: 2

  • TA的每日心情

    2022-3-16 10:00
  • 簽到天數: 122 天

    [LV.7]常住居民III

    推廣值
    0
    貢獻值
    0
    金錢
    396
    威望
    114
    主題
    0
    地板
    發表於 2016-3-4 16:08:15
    請善用帖子右下角舉報鍵,來檢舉有害網站/垃圾/宣傳帖,每個舉報會有金錢增加。

    学会了这些快捷键以后方便多了

    24

    主題

    7

    好友

    546

    積分

    中學生

    苏维埃√殿下

    Rank: 3Rank: 3

  • TA的每日心情
    慵懶
    2024-1-13 18:18
  • 簽到天數: 403 天

    [LV.9]以壇為家II

    推廣值
    0
    貢獻值
    71
    金錢
    309
    威望
    546
    主題
    24

    回文勇士 文明人 男生勳章 中學生 簽到勳章

    5#
    發表於 2016-3-4 16:42:52
    看不懂···厉害厉害·

    0

    主題

    0

    好友

    113

    積分

    小學生

    Rank: 2

  • TA的每日心情
    開心
    2024-11-7 18:04
  • 簽到天數: 82 天

    [LV.6]常住居民II

    推廣值
    0
    貢獻值
    0
    金錢
    653
    威望
    113
    主題
    0
    6#
    發表於 2016-3-25 14:46:03
    先赞一个

    11

    主題

    5

    好友

    5756

    積分

    教授

    Rank: 8Rank: 8

  • TA的每日心情
    奮斗
    2021-3-30 11:52
  • 簽到天數: 694 天

    [LV.9]以壇為家II

    推廣值
    0
    貢獻值
    0
    金錢
    708
    威望
    5756
    主題
    11
    7#
    發表於 2016-3-31 18:15:10
    路过帮顶```
    重要聲明:本論壇是以即時上載留言的方式運作,比思論壇對所有留言的真實性、完整性及立場等,不負任何法律責任。而一切留言之言論只代表留言者個人意見,並非本網站之立場,讀者及用戶不應信賴內容,並應自行判斷內容之真實性。於有關情形下,讀者及用戶應尋求專業意見(如涉及醫療、法律或投資等問題)。 由於本論壇受到「即時上載留言」運作方式所規限,故不能完全監察所有留言,若讀者及用戶發現有留言出現問題,請聯絡我們比思論壇有權刪除任何留言及拒絕任何人士上載留言 (刪除前或不會作事先警告及通知 ),同時亦有不刪除留言的權利,如有任何爭議,管理員擁有最終的詮釋權。用戶切勿撰寫粗言穢語、誹謗、渲染色情暴力或人身攻擊的言論,敬請自律。本網站保留一切法律權利。

    手機版| 廣告聯繫

    GMT+8, 2024-11-27 10:42 , Processed in 0.023291 second(s), 16 queries , Gzip On, Memcache On.

    Powered by Discuz! X2.5

    © 2001-2012 Comsenz Inc.

    回頂部