乡下人产国偷v产偷v自拍,国产午夜片在线观看,婷婷成人亚洲综合国产麻豆,久久综合给合久久狠狠狠9

  • <output id="e9wm2"></output>
    <s id="e9wm2"><nobr id="e9wm2"><ins id="e9wm2"></ins></nobr></s>

    • 分享

      Linux串口編程分析

       skywood 2006-11-23


       

      Linux串口編程分析

      這個(gè)話題,大家可能再熟悉不過了,網(wǎng)上資料很多,因?yàn)檫@是linux下編程比較重要的一個(gè)方面,懂這方面的人很多;這里我只是想給初學(xué)者簡單的介紹下這方面的知識(shí):
      串口編程其實(shí)說白了, 是拿根串口線把電腦和所要控制的機(jī)器連接起來,然后在通過編程的方式對(duì)下位機(jī)進(jìn)行發(fā)送指定的數(shù)據(jù)或進(jìn)行控制,或進(jìn)行傳輸,然后在接受下位機(jī)反饋回來的信息提示是否已經(jīng)正確。是不是好俗!
      串口是計(jì)算機(jī)上一種非常通用設(shè)備通信的協(xié)議,常用PC機(jī)上包含的是RS232規(guī)格的串口,當(dāng)然,除了RS232 ,還有RS485和RS422兩種規(guī)格,用于不同的設(shè)備通信;這里主要是介紹RS232串口編程
      串口編程中,比較重要的是串口的設(shè)置,我們要設(shè)置的部分包括 波特率,數(shù)據(jù)位,停止位,奇偶校驗(yàn)位;要注意的是,每臺(tái)機(jī)器的串口默認(rèn)設(shè)置可能是不同的,如果你沒設(shè)置這些,僅僅按照默認(rèn)設(shè)置進(jìn)行發(fā)送數(shù)據(jù),很可能出現(xiàn)n多異想不到而又查不出來的情況;所以,在真正通訊前,我們必須設(shè)置這些:

      下面就開始介紹這些基本設(shè)置的函數(shù),(其實(shí)都是些固定框架,程序中稍微改改就行)~o~

      1.設(shè)置波特率

      注意每臺(tái)機(jī)器都有輸出和輸入接受信息的速度 ,所以用cfsetispeed 和cfsetospeed來分別設(shè)置;注意到struct termios 這樣一個(gè)結(jié)構(gòu),它包括了串口端所有的設(shè)置,下面還要用到。它在termios.h中被定義。。還有一個(gè)地方比較難以理解,為什么設(shè)置了speed_arr 和name_arr兩個(gè)數(shù)組,這是因?yàn)樵趌inuxe下,系統(tǒng)為波特率專門準(zhǔn)備了一張表用B38400,B19200......代替,而我們實(shí)際上傳進(jìn)去的只能是38400,19200這些值,所以我們拿我們傳進(jìn)去的和name_arr進(jìn)行比較,如果相等則從系統(tǒng)對(duì)照表中取出相應(yīng)值進(jìn)行設(shè)置,如果不等證明傳的值在系統(tǒng)對(duì)照表中沒有,則不進(jìn)行設(shè)置。

      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; //定義了這樣一個(gè)結(jié)構(gòu)
      tcgetattr(fd, &Opt); //用來得到機(jī)器原端口的默認(rèn)設(shè)置
      for ( i= 0;i < sizeof(speed_arr) / sizeof(int);i++)
      {
      if(speed == name_arr[i]) //判斷傳進(jìn)來是否相等
      {
      tcflush(fd, TCIOFLUSH); //刷新輸入輸出緩沖
      cfsetispeed(&Opt, speed_arr[i]); //這里分別設(shè)置
      cfsetospeed(&Opt, speed_arr[i]);
      status = tcsetattr(fd, TCSANOW, &Opt); //這是立刻把bote rates設(shè)置真正寫到串口中去
      if(status != 0)
      perror("tcsetattr fd1"); //設(shè)置錯(cuò)誤
      return;
      }
      tcflush(fd,TCIOFLUSH); //同上
      }
      }

      2。設(shè)置奇偶校驗(yàn),數(shù)據(jù),停止位

      這三個(gè)參數(shù)通常放在一起設(shè)置,databits是數(shù)據(jù)位,stopbits是停止位,parity是校驗(yàn)位。
      串口的這些設(shè)置是很復(fù)雜很復(fù)雜的,Termios成員中共定義c_cflag 控制項(xiàng) c_lflag 線路項(xiàng) c_iflag 輸入項(xiàng) c_oflag 輸出項(xiàng) c_cc 控制字符c_ispeed 輸入波特 c_ospeed 輸出波特 那么多項(xiàng) ,對(duì)于每一項(xiàng)都有很多的設(shè)置,這里我們不講的那么復(fù)雜,就一個(gè)通用的串口框架進(jìn)行解釋,主要進(jìn)行奇偶校驗(yàn),數(shù)據(jù),停止位的設(shè)置。而其他的一些控制項(xiàng),在程序中用到時(shí)穿插講解:


      int set_Parity(int fd,int databits,int stopbits,int parity)
      {
      struct termios options; //定義一個(gè)結(jié)構(gòu)
      if( tcgetattr( fd,&options)!=0) //首先讀取系統(tǒng)默認(rèn)設(shè)置options中,必須
      {
      perror("SetupSerial 1");
      return(FALSE);
      }
      options.c_cflag &= ~CSIZE; //這是設(shè)置c_cflag選項(xiàng)不按位數(shù)據(jù)位掩碼
      switch (databits) /*設(shè)置數(shù)據(jù)位數(shù)*/
      {
      case 7:
      options.c_cflag |= CS7; //設(shè)置c_cflag選項(xiàng)數(shù)據(jù)位為7位
      break;
      case 8:
      options.c_cflag |= CS8; //設(shè)置c_cflag選項(xiàng)數(shù)據(jù)位為8位
      break;
      default:
      fprintf(stderr,"Unsupported data size\n"); //其他的都不支持
      return (FALSE);
      }
      switch (parity) //設(shè)置奇偶校驗(yàn),c_cflag和c_iflag有效
      {
      case ‘n‘:
      case ‘N‘: //無校驗(yàn)當(dāng)然都不選
      options.c_cflag &= ~PARENB; /* Clear parity enable */
      options.c_iflag &= ~INPCK; /* Enable parity checking */
      break;
      case ‘o‘: //奇校驗(yàn) 其中PARENB校驗(yàn)位有效;PARODD奇校驗(yàn)
      INPCK檢查校驗(yàn)
      case ‘O‘: options.c_cflag |= (PARODD | PARENB);/* 設(shè)置為奇效驗(yàn)*/
      options.c_iflag |= INPCK; /* Disnable parity checking */
      break;
      case ‘e‘:
      case ‘E‘: //偶校驗(yàn),奇校驗(yàn)不選就是偶校驗(yàn)了
      options.c_cflag |= PARENB; /* Enable parity */
      options.c_cflag &= ~PARODD; /* 轉(zhuǎn)換為偶效驗(yàn)*/
      options.c_iflag |= INPCK; /* Disnable parity checking */
      break;
      default:
      fprintf(stderr,"Unsupported parity\n");
      return (FALSE);
      }
      /* 設(shè)置停止位*/
      switch (stopbits) //這是設(shè)置停止位數(shù),影響的標(biāo)志是c_cflag
      {
      case 1:
      options.c_cflag &= ~CSTOPB; //不指明表示一位停止位
      break;
      case 2:
      options.c_cflag |= CSTOPB; //指明CSTOPB表示兩位,只有兩種可能
      break;
      default:
      fprintf(stderr,"Unsupported stop bits\n");
      return (FALSE);
      }
      /* Set input parity option */
      if (parity != ‘n‘) //這是設(shè)置輸入是否進(jìn)行校驗(yàn)
      options.c_iflag |= INPCK;

      //這個(gè)地方是用來設(shè)置控制字符和超時(shí)參數(shù)的,一般默認(rèn)即可。稍微要注意的是c_cc數(shù)組的VSTART 和 VSTOP 元素被設(shè)定成DC1 和 DC3,代表ASCII 標(biāo)準(zhǔn)的XON和XOFF字符。所以如果在傳輸這兩個(gè)字符的時(shí)候就傳不過去,這時(shí)需要把軟件流控制屏蔽 options.c_iflag &= ~(IXON | IXOFF | IXANY);


      options.c_cc[VTIME] = 150; // 15 seconds
      options.c_cc[VMIN] = 0;

      tcflush(fd,TCIFLUSH); /* Update the options and do it NOW */ //刷新和立刻寫進(jìn)去
      if (tcsetattr(fd,TCSANOW,&options) != 0)
      {
      perror("SetupSerial 3");
      return (FALSE);
      }
      return (TRUE);
      }
      //串口設(shè)置框架到這里就大概結(jié)束了,對(duì)于設(shè)置了數(shù)據(jù)位校驗(yàn)位停止位和波特率的端口已經(jīng)可以傳輸大多數(shù)信息。在實(shí)際中的情況往往是很多特例,比如,
      在用write發(fā)送數(shù)據(jù)時(shí)沒有鍵入回車,信息就將發(fā)送不出去的情況,這主要是因?yàn)槲覀冊(cè)谳敵鲚斎霑r(shí)是按照 規(guī)范模式 接受到回車或者換行才發(fā)送,而很多情況我們是不需要回車和換行的,這時(shí),應(yīng)當(dāng)切換到行方式輸入,設(shè)置options.c_lflag &= ~(ICANON | ECHO | ECHOE | ISIG);不經(jīng)處理直接發(fā)送。
      又比如
      在我們發(fā)送字符0x0d的時(shí)候,往往接受端得到的字符是0x0a 這是怎么回事,原因是在串口設(shè)置中c_iflag和c_oflag中存在 從NL-CR 和CR-NL的映射,也就是說,串口可以把回車和換行看成一個(gè)字符,所以,此時(shí)我們應(yīng)該屏蔽掉這些,用options.c_oflag &=~(INLCR|IGNCR|ICRNL|);和options.c_oflag &=~(ONLCR|OCRNL);進(jìn)行設(shè)置。

      總之,串口的設(shè)置是很復(fù)雜也很麻煩的東西,具體情況要具體分析,找到相應(yīng)的辦法,如果發(fā)現(xiàn)數(shù)據(jù)不能傳送,不妨耐點(diǎn)心在串口設(shè)置上找答案吧

      言歸正傳,后面的東西就很簡單了,接下來是打開串口:

      int OpenDev(char *Dev)
      {
      int fd = open( Dev, O_RDWR ); //| O_NOCTTY | O_NDELAY這種方式看open函數(shù)
      if (-1 == fd)
      { /*設(shè)置數(shù)據(jù)位數(shù)*/
      perror("Can‘t Open Serial Port");
      return -1;
      }
      else
      return fd;

      }

      然后是數(shù)據(jù)的接受和發(fā)送,把通用的主函數(shù)貼下來,很容易的。

      int main(int argc, char **argv)
      {
      int fd;
      int nread;
      char buff[512];
      char *dev ="/dev/ttyS0"; //linux下的端口就是通過打開設(shè)備文件操作的
      fd = OpenDev(dev); //打開
      if (fd>0)
      set_speed(fd,19200); //打開后設(shè)置波特率19200
      else
      {
      printf("Can‘t Open Serial Port!\n");
      exit(0);
      }
      if (set_Parity(fd,8,1,‘N‘)== FALSE) //設(shè)置8,1,n 注意,這里和上面要和下位機(jī)相符才可能通信
      {
      printf("Set Parity Error\n");
      exit(1);
      }

      //一般讀的時(shí)候一般都用read ,寫的時(shí)候一般都用write,read要注意阻塞后程序停止不動(dòng),所以要用select 進(jìn)行控制,注意tv每次循環(huán)都要設(shè)置;write 不用考慮阻塞,但要用循環(huán)寫方式保證一定寫完,其實(shí)讀最好也用循環(huán)讀方式保證一定能讀到所有東西并且能拼接在一起,然后在進(jìn)行其他操作。最后while(1) 是串口通訊中常用的循環(huán)就是一直執(zhí)行,直到碰到break;這些東西挺煩瑣,不過其實(shí)也沒什么。這里就不詳細(xì)說了,下面是個(gè)最最簡單的。。
      while(1)
      {
      while((nread = read(fd,buff,512))>0)
      {
      printf("\nLen %d\n",nread);
      buff[nread+1]=‘\0‘;
      printf("\n%s",buff);
      }
      }
      //close(fd);
      //exit(0);
      }

      完了,是不是不難,其實(shí)除了串口設(shè)置是新知識(shí),,事實(shí)上linux都是文件,串口是設(shè)備文件,設(shè)置好后,其他的東西就當(dāng)成文件進(jìn)行操作吧。

       

        本站是提供個(gè)人知識(shí)管理的網(wǎng)絡(luò)存儲(chǔ)空間,所有內(nèi)容均由用戶發(fā)布,不代表本站觀點(diǎn)。請(qǐng)注意甄別內(nèi)容中的聯(lián)系方式、誘導(dǎo)購買等信息,謹(jǐn)防詐騙。如發(fā)現(xiàn)有害或侵權(quán)內(nèi)容,請(qǐng)點(diǎn)擊一鍵舉報(bào)。
        轉(zhuǎn)藏 分享 獻(xiàn)花(0

        0條評(píng)論

        發(fā)表

        請(qǐng)遵守用戶 評(píng)論公約

        類似文章 更多