歡迎來到Linux教程網
Linux教程網
Linux教程網
Linux教程網
Linux教程網 >> Linux基礎 >> Linux服務器 >> Linux下串口信息的讀取

Linux下串口信息的讀取

日期:2017/3/2 16:56:14   编辑:Linux服務器
在Linux下串口信息的讀取有了一點心得體會。
 
  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中有兩個下標(VTIME和VMIN)對應的元素不是控制符,並且只是在原始模式下有效。只有在原始模式下,他們決定了read()函數在什麼時候返回。在標准模式下,除非設置了O_NONBLOCK選項,否則只有當遇到文件結束符或各行的字符都已經編輯完畢後才返回。
 
  控制符VTIME和VMIN之間有著復雜的關系。VTIME定義要求等待的零到幾百毫秒的時間量(通常是一個8位的unsigned char變量,取值不能大於cc_t)。VMIN定義了要求等待的最小字節數(不是要求讀的字節數——read()的第三個參數才是指定要求讀的最大字節數),這個字節數可能是0.
 
  l 如果VTIME取0,VMIN定義了要求等待讀取的最小字節數。函數read()只有在讀取了VMIN個字節的數據或者收到一個信號的時候才返回。
 
  l 如果VMIN取0,VTIME定義了即使沒有數據可以讀取,read()函數返回前也要等待幾百毫秒的時間量。這時,read()函數不需要像其通常情況那樣要遇到一個文件結束標志才返回0.
 
  l 如果VTIME和VMIN都不取0,VTIME定義的是當接收到第一個字節的數據後開始計算等待的時間量。如果當調用read函數時可以得到數據,計時器馬上開始計時。如果當調用read函數時還沒有任何數據可讀,則等接收到第一個字節的數據後,計時器開始計時。函數read可能會在讀取到VMIN個字節的數據後返回,也可能在計時完畢後返回,這主要取決於哪個條件首先實現。不過函數至少會讀取到一個字節的數據,因為計時器是在讀取到第一個數據時開始計時的。
 
  l 如果VTIME和VMIN都取0,即使讀取不到任何數據,函數read也會立即返回。同時,返回值0表示read函數不需要等待文件結束標志就返回了。
 
  這就是這兩個變量對read函數的影響。我使用的讀卡器每次傳送的數據是13個字節,一開始,我把它們設置成
 
  options.c_cc[VTIME] = 150
 
  options.c_cc[VMIN] = 0;
 
  結果,每次讀取的信息只有8個字節,剩下的5個字節要等到下一次打卡時才能收到。就是由於這個原因造成的。根據上面規則的第一條,我把VTIME取0,VMIN=13,也就是正好等於一次需要接收的字節數。這樣就實現了一次讀取13個字節值。同時,得出這樣的結論,如果讀卡器送出的數據為n個字節,那麼就把VMIN=n,這樣一次讀取的信息正好為讀卡器送出的信息,並且讀取的時候不需要進行循環讀取。
 
  4. 讀取數據
 
  有了上面的函數後,我設置了串口的基本信息,根據我們自己的實際情況,設置了相應的參數,就可以讀取數據了。
 
  void getcardinfo(char *buff){
 
  int fd;
 
  int nread,count=0;
 
  char tempbuff[13];
 
  char *dev = "/dev/ttyS0"; //串口1
 
  fd = OpenDev(dev);
 
  set_speed(fd,9600);
 
  if (set_Parity(fd,8,1,'N') == FALSE) {
 
  printf("Set Parity Error\n");
 
  //return -1;
 
  }
 
  while (1) //循環讀取數據
 
  {
 
  count=0;
 
  //sleep(5000);
 
  while(1)
 
  {
 
  if((nread = read(fd, tempbuff, 13))>0)
 
  {
 
  //printf("\nLen %d\n",nread);
 
  memcpy(&buff[count],tempbuff,nread);
 
  count+=nread;
 
  }
 
  if(count==13)
 
  {
 
  buff[count+1] = '\0';
 
  //printf( "\n%s", buff);
 
  break;
 
  }
 
  }
 
  //break;
 
  }
 
  //return buff;
 
  close(fd);
 
  pthread_exit(NULL);
 
  //close(fd);
 
  // exit (0);
 
  }
 
  這是我原來的程序,其實把VMIN設置以後,可以改成:
 
  void getcardinfo(char *buff){
 
  int fd;
 
  int nread,count=0;
 
  char tempbuff[13];
 
  char *dev = "/dev/ttyS0"; //串口1
 
  fd = OpenDev(dev);
 
  set_speed(fd,9600);
 
  if (set_Parity(fd,8,1,'N') == FALSE) {
 
  printf("Set Parity Error\n");
 
  //return -1;
 
  }
 
  nread = read(fd, buff, 13)
 
  close(fd);
 
  }
 
  5. 程序完整代碼:
 
  #include /*標准輸入輸出定義*/
 
  #include /*標准函數庫定義*/
 
  #include /*Unix 標准函數定義*/
 
  #include
 
  #include
 
  #include /*文件控制定義*/
 
  #include /*PPSIX 終端控制定義*/
 
  #include /*錯誤號定義*/
 
  #define FALSE -1
 
  #define TRUE 0
 
  /**
 
  *@brief 設置串口通信速率
 
  *@param fd 類型 int 打開串口的文件句柄
 
  *@param speed 類型 int 串口速度
 
  *@return void
 
  */
 
  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);
 
  }
 
  }
 
  }
 
  /**
 
  *@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; /* 設置超時15 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);
 
  }
 
  /**********************************************************************
 
  代碼說明:使用串口一測試的,發送的數據是字符,
 
  但是沒有發送字符串結束符號,所以接收到後,後面加上了結束符號
 
  **********************************************************************/
 
  /*********************************************************************/
 
  int OpenDev(char *Dev)
 
  {
 
  int fd = open( Dev, O_RDWR );
 
  //| O_NOCTTY | O_NDELAY
 
  if (-1 == fd)
 
  {
 
  perror("Can't Open Serial Port");
 
  return -1;
 
  }
 
  else
 
  return fd;
 
  }
 
  void getcardinfo(char *buff){
 
  int fd;
 
  int nread,count=0;
 
  char tempbuff[13];
 
  char *dev = "/dev/ttyS0"; //串口1
 
  fd = OpenDev(dev);
 
  set_speed(fd,9600);
 
  if (set_Parity(fd,8,1,'N') == FALSE) {
 
  printf("Set Parity Error\n");
 
  //return -1;
 
  }
 
  while (1) //循環讀取數據
 
  {
 
  count=0;
 
  //sleep(5000);
 
  while(1)
 
  {
 
  if((nread = read(fd, tempbuff, 13))>0)
 
  {
 
  //printf("\nLen %d\n",nread);
 
  memcpy(&buff[count],tempbuff,nread);
 
  count+=nread;
 
  }
 
  if(count==13)
 
  {
 
  buff[count+1] = '\0';
 
  //printf( "\n%s", buff);
 
  break;
 
  }
 
  }
 
  //break;
 
  }
 
  //return buff;
 
  close(fd);
 
  pthread_exit(NULL);
 
  //close(fd);
 
  // exit (0);
 
  }
Copyright © Linux教程網 All Rights Reserved