Linux串口編程(中斷方式和select方式)

 

  Linux下的串口編程,在嵌入式開發中佔據着重要的地位,由於不少的嵌入式設備都是經過串口交換數據的。在沒有操做系統的咱們能夠使用UART的中斷來出來數據的接受和發送,而在Linux操做系統下,咱們也能夠使用軟中斷的方式來處理數據的接受和發送,這裏主要使用的是信號SIGIO,也就是異步I/O。這裏也能夠使用select實現異步形式的通知。  這裏能夠參考《UNIX 環境高級編程》中的第14章 高級I/O和第18章的I/O終端,這兩章描述了串口的編程和異步I/O方面的內容。還有一本書《linux serial programming how-to》,《Serial Programming Guide for POSIX Operating Systems》。這都是串口編程的必讀和經典書籍。html

串口參數的設置通常包括波特率、起始位數量、數據位、中止位和流控協議。在接收端和發送端要配置成同樣的參數設置。在Linux中,全部的設備文件通常都位於「/dev」下,其中串口1、串口二對應的設備名依次爲「/dev/ttyS0」、"/dev/ttyS1"。這能夠經過查看"/dev"下的文件加以確認。個人串口通訊是開發板ARM9--mini2440發送數據,PC機經過串口接受數據。個人串口的參數設置爲 1152008,‘N’,1。也就是波特率是1152008位數據位,無奇偶校驗位,1位中止位。由於是用的開發板發送數據,因此要用到在minicom中運行發送的程序,不過在發送程序運行後,要當即關閉minicom,不然,接受程序不能接受到數據。這個是我使用中斷時出現的問題,當我使用select是沒有此問題,如今還不知道具體的緣由是什麼。linux

    串口編程中有一個最重要的結構體:ios

   

struct termios
{
tcflag_t c_iflag; /* 輸入選項標誌 */
tcflag_t c_oflag; /* 輸出選項標誌 */
tcflag_t c_cflag; /* 控制選項標誌 */
tcflag_t c_lflag; /* 本地選項標誌 */
編程

unsigned char c_line /*線控制*/
cc_t c_cc[NCCS]; /* 控制特性 */
};
數組

 

    這個結構中最重要的是c_cflag。經過對它的賦值,用戶能夠設置波特率、字符大小、數據位、中止位、奇偶校驗位和硬件流控等。其中的參數在網上和不少的書籍上都介紹的很詳細了,這裏主要介紹一下我在其中遇到的問題和解決的辦法,以供學習。其中的c_line,在POSIX中的Linux中沒有用到。異步

    在c_lflag中有這麼一個參數ICANON,如若設置,則按規範模式工做,這使下列字符起做用:EOF、EOL EOL二、 ERASE、 KILL、 REPRINT 、STATUS、WERASE。輸入字符被裝配成行。若是不以規範模式工做,則讀請求直接從輸入隊列取字符。在至少接收到MIN個字節或已超過TIME值以前,read將不返回。async

    在規範模式很容易:系統每次返回一行。但在非規範模式下,系統怎樣才能知道在何時將數據返回給咱們呢?若是它一次返回一個字節,那麼系統開銷就很大。解決方法是:當已讀了指定量的數據後,或者已通過了給定的時間後,即通知系統返回。這種技術使用了termios結構中c_cc數組的兩個變量:MINTIMEC_cc數據中的這兩個元素的下標名爲VMINVTIMEMIN說明一個read返回前的最小字節數。TIME說明等待數據到達的分秒數。ide

須要包括的頭文件是:
#include <stdio.h>
#include <string.h>
#include <sys/types.h>
#include <errno.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
#include <termios.h>
#include <stdlib.h>
#include "Set_Uart.c"
函數

其中的「Set_Uart.c」是我設置串口的模式和打開串口的文件。模式的設置就是按上面說的進行設置。oop

下面是發送數據,寫串口的程序,

 

int main(void)
{
  int fd;
  int nwrite,i;
  char buff[] = "Hello world!\n";
  fd = 0;
  /*打開串口*/
  if((fd = open_port(fd,0)) < 0) //在個人Set_Uart.c文件中定義。

  {
    perror("open_port error!\n");
    return (-1);
  }
  /*設置串口*/
  if((i = set_opt(fd,115200,8,'N',1)) < 0) //在Set_Uart.c文件中定義

  {
    perror("set_opt error!\n");
    return (-1);
  }
  printf("fd =%d\n",fd);
  /*向串口寫入字符串*/
sleep(10);  
  nwrite = write(fd,buff,strlen(buff));
sleep(10);
  printf("nwrite = %d\n",nwrite);
  close(fd);
  return (1);
}

   把該文件進行交叉編譯後下載到開發板,進行運行./Write_Uart。

      下面是中斷讀取數據的main函數。Read_Uart_IRQ.c 當使用中斷方式的讀取數據時,要先運行開發板上的Write_Uart.c文件,而後,當即關閉,再在PC上運行讀取數據的 Read_Uart_IRQ.c文件。因此,在Write_Uart.c中,在使用write()函數向UART寫數據之間加入一小段的延時。這樣便於關 閉minicom,若是在兩臺PC上進行測試的話,應該不存在此問題。

 

 

int main(void)
{
  int fd,res,i;
  struct sigaction saio; /*definition of signal axtion */
  char buf[255];
  fd_set rd;
  fd = 0;
  /*打開串口*/
  if((fd = open_port(fd,1))<0)
  {
    perror("open_port error!\n");
    return (-1);
  }
  /* install the signal handle before making the device asynchronous*/
  saio.sa_handler = signal_handler_IO;
  sigemptyset(&saio.sa_mask);
  //saio.sa_mask = 0; 必須用sigemptyset函數初始話act結構的sa_mask成員

  saio.sa_flags = 0;
  saio.sa_restorer = NULL;
  sigaction(SIGIO,&saio,NULL);

  /* allow the process to recevie SIGIO*/
  fcntl(fd,F_SETOWN,getpid());
  /* Make the file descriptor asynchronous*/
  fcntl(fd,F_SETFL,FASYNC);
  
  /*設置串口*/
  if((i= set_opt(fd,115200,8,'N',1))<0)
  {
    perror("set_opt error!\n");
    return (-1);
  }
  /* loop while waiting for input,normally we would do something useful here*/
  while(STOP == FALSE)
  {
    usleep(100000);
    /* after receving SIGIO ,wait_flag = FALSE,input is availabe and can be read*/
    if(wait_flag == FALSE)
    {
          memset(buf,0,255);
      res = read(fd,buf,255);
       printf("nread=%d,%s\n",res,buf);
      if(res == 1)
        STOP = TRUE; /*stop loop if only a CR was input */
       wait_flag = TRUE; /*wait for new input*/
    }
  }
  close(fd);
  return 0; 
}
/******************************************
 信號處理函數,設備wait_flag=FASLE
 ******************************************************/
void signal_handler_IO(int status)
{
   printf("received SIGIO signale.\n");
  wait_flag = FALSE; 
}

下面是select方式的讀取數據的main函數。Read_Uart.c

int main(void)
{
  int fd;
  int nread,nwrite,i;
  char buff[8];
  fd_set rd;
  fd = 0;
  /*打開串口*/
  if((fd = open_port(fd,1)) < 0)
  {
    perror("open_port error!\n");
    return ;
  }
  /*設置串口*/
  if((i= set_opt(fd,115200,8,'N',1)) < 0)
  {
    perror("set_opt error!\n");
    return (-1);
  }
  /*利用select函數來實現多個串口的讀寫*/
while(1)
{
  FD_ZERO(&rd);
  FD_SET(fd,&rd);
  while(FD_ISSET(fd,&rd))
  {
    if(select(fd+1,&rd,NULL,NULL,NULL) < 0)
      perror("select error!\n");
    else
    {
      while((nread = read(fd,buff,8))>0)
      {
        printf("nread = %d,%s\n",nread,buff);
     printf("nread = %d,%s\n",nread,buff);
      }
    }
  }
}
close(fd);
    return ;
}

下面是運行的結果,PC機收到的開發板發送過來的數據。

./Read_Uart_IRQ
fcntl = 0
isatty success !
fd-open=3
set
received SIGIO signale.
nread=13,Hello

其中串口中的一些重要的設備以下;

/*設置等待時間它最小接收字符*/
  newtio.c_cc[VTIME] = 1;
  newtio.c_cc[VMIN] = 0;
  newtio.c_lflag &= ~( ECHO | ECHOE | ISIG);
  newtio.c_lflag |=ICANON; //關閉ICANON標誌就使終端處於非規範模式 如今處於打開 處於規範模式下
  newtio.c_oflag &= ~OPOST; //執行輸出處理 如今就關閉狀態
  newtio.c_iflag |= (IGNPAR | ICRNL); //忽略奇偶校驗錯誤 將CR 映射成NL

 

http://blog.chinaunix.net/uid-20788636-id-1841319.html

相關文章
相關標籤/搜索