Linux串列埠程式設計

crazy_baoli發表於2019-01-07

開發環境

開發板:A33-Vstar

開發板系統:Linux

Ubuntu版本:Ubuntu14.04

-------------------------------------------

串列埠2(Ubuntu)

串列埠1(A33)

Ubuntu和A33都執行uart_test程式(見附件)。

 

一、串列埠設定

最基本的串列埠設定包括波特率、校驗位和停止位設定,且串列埠設定主要使用termios.h標頭檔案中定義的termios結構,如下:

struct termios

{

tcflag_t c_iflag; //輸入模式標誌

tcflag_t c_oflag; //輸出模式標誌

tcflag_t c_cflag; //控制模式標誌

tcflag_t c_lflag; //本地模式標誌

cc_t c_line; //line discipline

cc_t c_cc[NCC]; //control characters

}

 

1. 重要配置函式

1)獲取終端對應的termios結構:

int tcgetattr(int fd, struct termios *termios_p); 

2)重新配置串列埠

int tcsetattr(int fd , int actions , const struct termios *termios_h); 

引數actions控制修改方式,共有三種修改方式,如下所示。

TCSANOW:立刻對值進行修改

TCSADRAIN:等當前的輸出完成後再對值進行修改。

TCSAFLUSH:等當前的輸出完成之後,再對值進行修改,但丟棄還未從read呼叫返回的當前的可用的任何輸入。

 

2. 控制符VTIME和VMIN

VTIME定義要求等待的時間(百毫秒,通常是unsigned char變數),而VMIN定義了要求等待的最小位元組數(相比之下,read函式的第三個引數指定了要求讀的最大位元組數)。

1)如果VTIME=0,VMIN=要求等待讀取的最小位元組數,read必須在讀取了VMIN個位元組的資料或者收到一個訊號才會返回。

2)如果VTIME=時間量,VMIN=0,不管能否讀取到資料,read也要等待VTIME的時間量。

3)如果VTIME=時間量,VMIN=要求等待讀取的最小位元組數,那麼將從read讀取第一個位元組的資料時開始計時,並會在讀取到VMIN個位元組或者VTIME時間後返回。

4)如果VTIME=0,VMIN=0,不管能否讀取到資料,read都會立即返回

 

二、使用過程中遇到的一些問題

1. 串列埠2到串列埠1:在傳送資料時沒有鍵入回車,資訊就傳送不出去,鍵入回車傳送後,前面沒有傳送出去的資料會和本次一起全部傳送出去。這主要是因為我們在輸入輸出時是按照規範模式接收到回車或換行才傳送,而更多情況下我們是不必鍵入回車或換行的。此時將對方串列埠(串列埠1)設定如下:

Opt.c_lflag &= ~ (ICANON | ECHO | ECHOE | ISIG);

主要是:ICANON

注:當A33使用cat(開機後串列埠1未經過程式設定),Ubuntu使用uart_test會出現此問題即A33收不到,Ubuntu使用echo傳送資料時可以收到(echo 應該是傳送了回車),win7使用串列埠助手不加回車傳送資料時A33收不到,加了回車可以收到。

 

2. 回顯問題:在串列埠2往串列埠1傳送資料時,串列埠2能接收到傳送出去的資料。此時,關閉串列埠1的回顯即可。設定如下:

Opt.c_lflag &= ~ (ICANON | ECHO | ECHOE | ISIG);

主要是:ECHO | ECHOE

 

3. 未經測試(未遇到的問題,網上摘錄)

1)還存在這樣的情況:傳送字元0X0d的時候,往往接收端得到的字元是0X0a,原因是因為在串列埠設定中c_iflag和c_oflag中存在從NL-CR和CR-NL的對映,即串列埠能把回車和換行當成同一個字元,可以進行如下設定遮蔽之:

Opt.c_iflag &= ~ (INLCR | ICRNL | IGNCR);

Opt.c_oflag &= ~(ONLCR | OCRNL);

 

2)c_cc陣列的VSTART和VSTOP元素被設定成DC1和DC3,代表ASCII標準的XON和XOFF字元,如果在傳輸這兩個字元的時候就傳不過去,需要把軟體流控制遮蔽,即:

Opt.c_iflag &= ~ (IXON | IXOFF | IXANY);

 

三、附錄

1. c_cflag用於設定控制引數,除了波特率外還包含以下內容:

EXTA External rate clock

EXTB External rate clock

CSIZE Bit mask for data bits

CS5 5個資料位

CS6 6個資料位

CS7 7個資料位

CS8 8個資料位

CSTOPB 2個停止位(清除該標誌表示1個停止位

PARENB 允許校驗位

PARODD 使用奇校驗(清除該標誌表示使用偶校驗)

CREAD Enable receiver

HUPCL Hangup (drop DTR) on last close

CLOCAL Local line – do not change “owner” of port

LOBLK Block job control outpu

c_cflag標誌可以定義CLOCAL和CREAD,這將確保該程式不被其他埠控制和訊號干擾,同時串列埠驅動將讀取進入的資料。CLOCAL和CREAD通常總是被是能的。

 

2. c_lflag用於設定本地模式,決定串列埠驅動如何處理輸入字元,設定內容如下:

ISIG Enable SIGINTR, SIGSUSP, SIGDSUSP, and SIGQUIT signals

ICANON Enable canonical input (else raw)

XCASE Map uppercase \lowercase (obsolete)

ECHO Enable echoing of input characters

ECHOE Echo erase character as BS-SP-BS

ECHOK Echo NL after kill character

ECHONL Echo NL

NOFLSH Disable flushing of input buffers after interrupt or quit characters

IEXTEN Enable extended functions

ECHOCTL Echo control characters as ^char and delete as ~?

ECHOPRT Echo erased character as character erased

ECHOKE BS-SP-BS entire line on line kill

FLUSHO Output being flushed

PENDIN Retype pending input at next read or input char

TOSTOP Send SIGTTOU for background output

 

3. c_iflag用於設定如何處理串列埠上接收到的資料,包含如下內容:

INPCK Enable parity check

IGNPAR Ignore parity errors

PARMRK Mark parity errors

ISTRIP Strip parity bits

IXON Enable software flow control (outgoing)

IXOFF Enable software flow control (incoming)

IXANY Allow any character to start flow again

IGNBRK Ignore break condition

BRKINT Send a SIGINT when a break condition is detected

INLCR Map NL to CR

IGNCR Ignore CR

ICRNL Map CR to NL

IUCLC Map uppercase to lowercase

IMAXBEL Echo BEL on input line too long

 

4. c_oflag用於設定如何處理輸出資料,包含如下內容:

OPOST Postprocess output (not set = raw output)

OLCUC Map lowercase to uppercase

ONLCR Map NL to CR-NL

OCRNL Map CR to NL

NOCR No CR output at column 0

ONLRET NL performs CR function

OFILL Use fill characters for delay

OFDEL Fill character is DEL

NLDLY Mask for delay time needed between lines

NL0 No delay for NLs

NL1 Delay further output after newline for 100 milliseconds

CRDLY Mask for delay time needed to return carriage to left column

CR0 No delay for CRs

CR1 Delay after CRs depending on current column position

CR2 Delay 100 milliseconds after sending CRs

CR3 Delay 150 milliseconds after sending CRs

TABDLY Mask for delay time needed after TABs

TAB0 No delay for TABs

TAB1 Delay after TABs depending on current column position

TAB2 Delay 100 milliseconds after sending TABs

TAB3 Expand TAB characters to spaces

BSDLY Mask for delay time needed after BSs

BS0 No delay for BSs

BS1 Delay 50 milliseconds after sending BSs

VTDLY Mask for delay time needed after VTs

VT0 No delay for VTs

VT1 Delay 2 seconds after sending VTs

FFDLY Mask for delay time needed after FFs

FF0 No delay for FFs

FF1 Delay 2 seconds after sending FFs

 

5. c_cc定義了控制字元,包含以下內容:

VINTR Interrupt CTRL-C

VQUIT Quit CTRL-Z

VERASE Erase Backspace (BS)

VKILL Kill-line CTRL-U

VEOF End-of-file CTRL-D

VEOL End-of-line Carriage return (CR)

VEOL2 Second end-of-line Line feed (LF)

VMIN Minimum number of characters to read

VSTART Start flow CTRL-Q (XON)

VSTOP Stop flow CTRL-S (XOFF)

VTIME Time to wait for data (tenths of seconds)

 

測試原始碼:

#include <sys/types.h>   
#include <sys/stat.h>  
#include <fcntl.h>  
#include <termios.h>  
#include <stdio.h>  


#define BAUDRATE        9600  
char *uart_device = "/dev/ttyUSB1";
  
#define FALSE  -1  
#define TRUE   0  


char rec_buf[100];
int  fd;


////////////////////////////////////////////////////////////////////////////////  
/** 
*@brief  設定串列埠通訊速率 
*@param  fd     型別 int  開啟串列埠的檔案控制程式碼 
*@param  speed  型別 int  串列埠速度 
*@return  void 
*/  
int speed_arr[] = {B115200, B38400, B19200, B9600, B4800, B2400, B1200, B300,  
                   B115200, B38400, B19200, B9600, B4800, B2400, B1200, B300, };  
int name_arr[] = {115200, 38400, 19200, 9600, 4800, 2400, 1200,  300,   
                  115200, 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[i]) {       
      tcflush(fd, TCIOFLUSH);       
      cfsetispeed(&Opt, speed_arr[i]);    
      cfsetospeed(&Opt, speed_arr[i]);     
      status = tcsetattr(fd, TCSANOW, &Opt);    
      if  (status != 0) {          
        perror("tcsetattr fd1");    
        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; /* 設定超時0 seconds*/     
    options.c_cc[VMIN] = 1; //至少接收了1個位元組資料才可以返回
	
    if (tcsetattr(fd,TCSANOW,&options) != 0)     
    {   
        perror("SetupSerial 3");     
        return (FALSE);    
    }   

    return (TRUE);    
} 


void *read_thread(void *argv) 
{
	int res = 0;
	
	while(1){
        res = read(fd, rec_buf, 50);  
  
        if(res <= 0){
			printf("\nrev faild, try rev again.\n");
            continue; 
		}			
		
        rec_buf[res]=0;   
        printf("rev data:%s\n", rec_buf);           
	
	}
	
}



int main(int argc, char *argv[])  
{  
  
    int  res; 
	pthread_t a_thread;	  
    char  buf[256];  
	
	if(argc == 2){
		uart_device = argv[1];
	}
  
    printf("open %s\n", uart_device);  
    fd = open(uart_device, O_RDWR);  
  
    if (fd < 0) {  
        perror(uart_device);  
        exit(1);  
    }  
  
    printf("setting...\n");  
    set_speed(fd, BAUDRATE);  
    if (set_Parity(fd,8,1,'N') == FALSE)  {  
        printf("Set Parity Error\n");  
        exit (0);  
    }	

    res = pthread_create(&a_thread, NULL, read_thread, NULL); /*啟動執行緒函式*/
    if (res != 0){
        perror("Thread creation failed");
        exit(0);
    }
	printf("read thread is created.\n");
	
    while(1) { 
		printf("input the data: ");
		gets(buf);
		res = write(fd, buf, strlen(buf));
		if(res > 0) printf("write sucess. length:%d.\n", res); 
    }  
  
    printf("Close...\n");  
    close(fd);  
  
    return 0;  
} 

相關文章