linux串口編程設置(USB轉串口)

       在嵌入式Linux中,串口是一個字設備,訪問具體的串行端口的編程與讀/寫文件 的操作類似,只需打開相應的設備文件即可操作。串口編程特殊在於串 口通信時相關參數與屬性的設置,還有必須有串口驅動程序(提供設備節點),

在linux系統中一般都自帶有的串口驅動程序,我們只需要配置就可以使用串口的設置節點

配置串口驅動:

串口配置完成後,插上usb轉串口設備,使用ls  -l   /dev/tty*  查看設置節點, 出現一個ttyUBS0的設備節點

串口編程特殊在於串口通信時相關參數與屬性的設置

打開串口
    打開串口設備文件的操作與普通文件的操作類似,都採用標準的I/O操作函數open()。
    fd = open("/dev/ttyS0",O_RDWR|O_NDELAY|O_NOCTTY);
    open()函數有兩個參數,第一個參數是要打開的文件名(此處爲串口設備文件/dev/ttyS0);第二個參數設置打開的方式,O_RDWR表示打開 的文件可讀/寫,O_NDELAY表示以非阻塞方式打開,O_NOCTTY表示若打開的文件爲終端設備,則不會將終端作爲進程控制終端。

2、設置串口屬性
     串口通信時的屬性設置是串口編程的關鍵問題,許多串口通信時的錯誤都與串口的設置相關,所以編程時應特別注意這些設置,最常見的設置包括波特率、奇偶校驗和停止位以及流控制等。
    在Linux中,串口被作爲終端I/O,它的參數設置需要使用struct termios結構體,這個結構體在termio.h文件中定義,且應在程序中包含這個頭文件。
          typedef unsigned char         cc_t ;
          typedef unsigned int            speed_t ;
          typedef unsigned int            tcflag_t ;
          struct termios
          {
             tcflag_t      c_iflag ;          /*輸入模式標誌*/ 
             tcflag_t      c_oflag ;         /*輸出模式標誌*/
             tcflag_t      c_cflag ;            /*控制模式標誌*/
             tcflag_t      c_lflag ;            /*本地模式標誌*/
             tcflag_t      c_line ;            /*行規程類型,一般應用程序不使用*/
             cc_t         c_cc[NCC];      /*控制字符*/
             speed_t      c_ispeed ;      /*輸入數據波特率*/
             speed_t      c_ospeed ;      /*輸出數據波特率*/
       };
       串口的設置主要是設置這個結構體的各成員值,然後利用該結構體將參數傳給硬件驅動程序。在Linux中,串口以串行終端的方式進行處理,因而,可以使用tcgetattr()/tcsetattr()函數獲取/設置串口的參數。
       int tcgetattr( int fd, struct termios *termios_p );
       int tcsetattr( int fd, int optional_actions , struct termios *termios_p );
       這兩個參數都有一個批向termios結構體的指針作爲參數,用於返回當前終端的屬性或設置該終端的屬性。參數fd就是用open()函數打開的終端文件 句柄,而串口就是用open()打開的串口設備文件句柄。tcsetattr()函數的optional_action參數用於指定新設定的參數起作用的 時間,其設定值可以爲:
       TCSANOW         改變立即生效
       TCSADRAIN      在所有的輸出都被傳輸後改變生效,適用於更改影響輸出參數的情況。
       TCSAFLUSH    在所有輸出都被傳輸後改變生效,丟棄所有末讀入的輸入(清空輸入緩存)

(1設置波特率
       使用cfsetospeed()/cfsetispeed()函數設置波特率,它們分別用於在termios結構體中設置輸出和輸入的波特率。設置波特率 可以使用波特率常數,其定義爲字母“B+速率”,如B19200就是波特率爲19200bps,B115200就是波特率爲115200bps。
int cfsetispeed( struct termios *termios_p, speed_t speed );     //speed爲波特率常數
int cfsetospeed( struct termios *termios_p, speed_t speed );

例 :
cfsetispeed( ttys0_opt, B115200 );
cfsetospeed( ttys0_opt, B115200 );

(2)設置控制模式標誌
       控制模式標誌c_cflag主要用於設置串口對DCD信號狀態檢測、硬件流控制、字符位寬、停止位和奇偶校驗等,常用標誌位如
下:
CLOCAL   忽略DCD信號,若不使用MODEM,或沒有串口沒有CD腳就設置此標誌
CREAD    啓用接收裝置,可以接收字符
CRTSCTS啓用硬件流控制,對於許多三線制的串不應使用,需設置~CRTCTS
CSIZE      字符位數掩碼,常用CS8
CSTOPB   使用兩個停止位,若用一位應設置~CSTOPB
PARENB   啓用奇偶校驗

       例如,下面的代碼將串口設置爲忽略DCD信號,啓用接收裝置,關閉硬件流控制,傳輸數據時使用8位數據位和一位停止位(8N1),不使用奇偶校驗。
       struct temios ttys0
       ttyso_opt.c_cflag |= CLOCAL | CREAD ;      //將CLOCAL與CREAD位設置爲1
       ttys0_opt.c_cflag &= ~CRTSCTS ;               //將硬件流控制位CRTSCTS清0,其他位不變
       ttys0_opt.c_cflag &= ~CSIZE ;               //清除數據位掩碼
       ttys0_opt.c_cflag |= CS8 ;                           //設置8位數據位標誌CS8
       ttys0_opt.c_cflag &= ~(PARENB|CSTOPB);//使用1位停止位,停用奇偶校驗

(3)設置本地模式標誌
       本地模式標誌c_lflag主要用於設置終端與用戶的交互方式,常見的設置標誌位有ICAN-ON,ECHO和ECHOE等。其中,ICANON標誌位用 於實現規範輸入,即read()讀到行結束符後返回,常用於終端的處理;若串口用於發送/接收數據,則應清除此標誌,使用非規範模式(raw mode)。非規範模式中,輸入數據不組成行,不處規範模式中的特殊字符。在規範模式中,當設置ECHO標誌位時,用戶向終端輸入的字符將被回傳給用戶; 當設置ECHOE標誌位時,用戶輸入退格鍵時,則回傳“退格-空格-退格”序列給用戶,使得退格鍵覆蓋的字符從顯示中消失,這樣更符合用戶的習慣(若未設 置此標誌,輸入退格鍵時,則光標回退一個字符,但原有的字符未從顯示中消失)。
(4)設置輸入模式標誌
       輸入模式標誌c_iflag主要用於控制串口的輸入特性,常用的設置有IXOFF和IXON,分別用於軟件流控制。其中,IXOFF用於防止輸入緩衝區溢 出;IXON則是在輸入數據中識別軟件流控制標誌。由於許多嵌入式系統無法使用硬件流控制,因此,只能使用軟件流控制數據傳輸的速度,但是,它可能降低串 口數據傳輸效率。啓用軟件流控制的代碼如下:

       ttys0_opt.c_iflag |= IXOFF|IXON ;
(5)設置輸出模式標誌
       輸出模式標誌c_oflag主要用於對串口在規範模式時輸出的特殊字符處理,而對非規範模式無效。
(6)設置控制字符
       在非規範模式中,控制字符數組c_cc[]中的變量c_cc[VMIN]和c_cc[VTIME]用於設置read()返回前讀到的最少字節數和讀超時時間,其值分爲四種情況:
    (a)c_cc[VMIN]>0,c_cc[VTIME]>0
          讀到一個字節後,啓動定時器,其超時時間爲c_cc[VTIME],read()返回的條件爲至少讀到c_cc[VMIN]個字符或定時器超期。
    (b)c_cc[VMIN]>0, c_cc[VTIME] ==0
          只要讀到數據的字節數大於等於c_cc[VMIN],則read()返回;否則,將無限期阻塞等待。
      (c)c_cc[VMIN] == 0, c_cc[VTIME]>0
          只要讀到數據,則read()返回;若定時器超期(定時時間c_cc[VTIME])卻未讀到數據,則read()返回0;
      (d)c_cc[VMIN] == 0, c_cc[VTIME] == 0
          若有數據,則read()讀取指定數量的數據後返回;若沒有數據,則read()返回0;

在termios結構體中填寫完這些參數後,接下來就可以使用tcsetattr()函數設置串口的屬性。
       tcsetattr( fd, &old_opt );         //將原有的設置保存到old_opt,以便程序結束後恢復
       tcsetattr( fd, TCSANOW, &ttsy0_opt );

3、清空發送/接收緩衝區
       爲保證讀/寫操作不被串口緩衝區中原有的數據幹攏,可以在讀/寫數據前用tcflush()函數清空串口發送/接收緩衝區。tcflush()函數的參數可爲:
        TCIFLUSH      清空輸入隊列
        TCOFLUSH   清空輸出隊列
        TCIOFLUSH   同時清空輸入和輸出隊列

4、從串口讀寫數據
        串口的數據讀/寫與普通文件的讀/寫一樣,都是使用read()/write()函數實現。
        n = write( fd, buf, len );         //將buf中len個字節的數據從串口輸出,返回輸出的字節數
        n = read( fd, buf, len );         //從串口讀入len個字節的數據並放入buf, 返回讀取的字節數

5、關閉串口
    關閉串口的操作很簡單,將打開的串口設備文件句柄關閉即可

    close(fd);

打開文件代碼例子:

int  open_port(char  *com_port)
{
       int  fd;
       /*打開串口*/
        fd=open(com_port,O_RDWR|O_NOCTTY|O_NDELAY);
        if(fd<0)
        {
             perror("open\n");
             return -1;  
        }
        /*恢復串口的阻塞狀態*/
        if(fcntl(fd,F_SETFL,0)<0){
             perror("fcntl\n");
        }
        /*判斷是否爲終端設備*/
        if(isatty(fd)==0)
        {
            perror("this is not  a terminal device\n");
        }
        return fd;
}		

串口設置的方式的方式有2種

第一種方式:需要自己設置

//用於設置參數(可以理解爲的串口的初始化)
void set_com_config(int fd,int baud_rate,int date_bits,char  parity,int stop_bits)
{
   
	struct termios  new_cfg,old_cfg;
	int speed;
       //linux還提供了tcgetattr函數和tcsetattr函數。tcgetattr用於獲取終端的相關參數,而tcsetattr函數用於設置終端參數。
	/*保存原有的串口的數據*/
	if(tcgetattr(fd,&old_cfg)!=0)
	{
	         perror("tcgetattr");
		 return ;
	}
	new_cfg=old_cfg;
	
       /*配置爲原始的模式*/
      //此函數設置串口終端的屬性,
	cfmakeraw(&new_cfg);
	
	new_cfg.c_cflag&=~CSIZE;//清楚數據掩碼位
	
	/*設置波特率*/
	switch(baud_rate)
	{
	    case 2400:{
	              speed=B2400;
		          break;
	        }
		case 4800:{
	              speed=B4800;
		          break;
	        }
	    case 9600:{
	              speed=B9600;
		          break;
	        }
		case 19200:{
	              speed=B19200;
		          break;
	        }
		case 38400:{
	              speed=B38400;
		          break;
	        }
		default:
		case 115200:{
	              speed=B115200;
		          break;
	        }
	}
	cfsetispeed(&new_cfg,speed);//用於設置輸入的波特率
	cfsetospeed(&new_cfg,speed);//用於設置輸出的波特率
	/*設置數據位*/
    switch(date_bits)
	{
	    case 7:{
		        new_cfg.c_cflag|=CS7;
		        break;
			}
		case 8:{
		        new_cfg.c_cflag|=CS8;
		    }
	}
	/*設置奇偶校驗位*/
	switch(parity)
	{
	    default:
		case 'n':
		case 'N':{ 
                 new_cfg.c_cflag&=~PARENB;//  不使用奇偶校驗位
                 new_cfg.c_iflag&=~INPCK;
                 break;
            }
		case 'o':
		case 'O':{ 
                 new_cfg.c_cflag|=(PARENB|PARODD);//偶數校驗
                 new_cfg.c_iflag|=INPCK;   /*輸入模式標誌*/ 
                 break;
            }
		case 'e':
		case 'E':{ 
                 new_cfg.c_cflag|=PARENB;
	        new_cfg.c_cflag&=~PARODD;//奇數校驗
                 new_cfg.c_iflag|=INPCK;
                 break;
            }
	}
	/*設置停止位*/
	switch(stop_bits)
    {
	     default:
		 case 1:{
		        new_cfg.c_cflag&=~CSTOPB;
				break;
		 }
		 case 2:{
		        new_cfg.c_cflag|=CSTOPB;
			    break;
		 }
	}

  /*設置控制字符*/	
  //用於設置read()返回前讀到的最少字節數和讀超時時間,
  /*設置等待時間和最小接收字符*/
    new_cfg.c_cc[VTIME]=0;
    new_cfg.c_cc[VMIN]=1;
  // 只要讀到數據,則read()返回;若定時器超期(定時時間c_cc[VTIME])卻未讀到數據,則read()返回0;
  //設置串口的屬性

  tcflush(fd,TCIFLUSH);//TCIFLUSH   清空緩衝區
   //用於設置串口的參數
    if((tcsetattr(fd,TCSANOW,&new_cfg))!=0)
    {
       perror("tcsetattr");
       return  ;
    }
	

}

第二種方式:串口的配置已經寫死

void serial_init(int fd)
{
	struct termios options;
	tcgetattr(fd, &options);
	options.c_cflag |= ( CLOCAL | CREAD );
	options.c_cflag &= ~CSIZE;
	options.c_cflag &= ~CRTSCTS;
	options.c_cflag |= CS8;
	options.c_cflag &= ~CSTOPB; 
	options.c_iflag |= IGNPAR;
	options.c_iflag &= ~(ICRNL | IXON);
	options.c_oflag = 0;
	options.c_lflag = 0;

	cfsetispeed(&options, B115200);
	cfsetospeed(&options, B115200);
	tcsetattr(fd,TCSANOW,&options);
}

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章