天天看點

Linux序列槽程式設計(中斷方式和select方式)

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》 。這都是序列槽程式設計的必讀和經典書籍。

序列槽參數的設定一般包括波特率、起始位數量、資料位、停止位和流控協定。在接收端和發送端要配置成一樣的參數設定。在Linux中,所有的裝置檔案一般都位于“/dev”下,其中序列槽一、序列槽二對應的裝置名依次為“/dev/ttyS0”、"/dev/ttyS1"。這可以通過檢視"/dev"下的檔案加以确認。我的序列槽通信是開發闆ARM9--mini2440發送資料,PC機通過序列槽接受資料。我的序列槽的參數設定為 115200,8,‘N’,1。也就是波特率是115200,8位資料位,無奇偶校驗位,1位停止位。因為是用的開發闆發送資料,是以要用到在minicom中運作發送的程式,不過在發送程式運作後,要立即關閉minicom,否則,接受程式不能接受到資料。這個是我使用中斷時出現的問題,當我使用select是沒有此問題,現在還不知道具體的原因是什麼。

    序列槽程式設計中有一個最重要的結構體:

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 EOL2、 ERASE、 KILL、 REPRINT 、STATUS、WERASE。輸入字元被裝配成行。如果不以規範模式工作,則讀請求直接從輸入隊列取字元。在至少接收到MIN個位元組或已超過TIME值之前,read将不傳回。

    在規範模式很容易:系統每次傳回一行。但在非規範模式下,系統怎樣才能知道在什麼時候将資料傳回給我們呢?如果它一次傳回一個位元組,那麼系統開銷就很大。解決方法是:當已讀了指定量的資料後,或者已經過了給定的時間後,即通知系統傳回。這種技術使用了termios結構中c_cc數組的兩個變量:MIN和TIME。C_cc資料中的這兩個元素的下标名為VMIN和VTIME。MIN說明一個read傳回前的最小位元組數。TIME說明等待資料到達的分秒數。

需要包括的頭檔案是: #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”是我設定序列槽的模式和打開序列槽的檔案。模式的設定就是按上面說的進行設定。

下面是發送資料,寫序列槽的程式,

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;   char buf[255];   fd_set rd;   fd = 0;      if((fd = open_port(fd,1))<0)   {     perror("open_port error!\n");     return (-1);   }      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);      fcntl(fd,F_SETOWN,getpid());      fcntl(fd,F_SETFL,FASYNC);         if((i= set_opt(fd,115200,8,'N',1))<0)   {     perror("set_opt error!\n");     return (-1);   }      while(STOP == FALSE)   {     usleep(100000);          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;        wait_flag = TRUE;     }   }   close(fd);   return 0;  } 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);   }    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

繼續閱讀