linux 串口編程

http://blog.chinaunix.net/uid-27717694-id-3483095.html

 1、串口概述
    
用戶常見的數據通信的基本方式有兩種:並行通信和串行通信。
    
串行通信是計算機常用的接口,如:RS-232-C接口。該標準規定採用一個DB25芯引腳連接器或DB9芯引腳連接器。芯片內部常具有UART控制器,其可工作於Interrupt(中斷模式)DMA(直接內存訪問)模式。
     UART
的操作主要包括以下幾個部分:
數據發送;數據接收;產生中斷;產生波特率;Loopback模式;紅外模式;自動流控模式。
    
串口參數的配置主要包括:波特率、數據位、停止位、流控協議

    linux中的串口設備文件存放於/dev目錄下,其中串口一,串口二對應設備名依次爲“/dev/ttyS0”“/dev/ttyS1”。在linux下操作串口與操作文件相同。


2、串口詳細配置
   
在使用串口之前必須設置相關配置,包括:波特率、數據位、校驗位、停止位等。

串口設置由下面結構體實現:

Struct termios {
tcflag_t c_iflag; /*input flags */
tcflag_t c_oflag; /*output flags */
tcflag_t c_cflag; /*control flags */
tcflag_t c_lflag; /*local flags */
cc_t   c_cc[NCCS]; /*control characters */
};

    該結構中c_cflag最爲重要,可設置波特率、數據位、校驗位、停止位。在設置波特率時需在數字前加上‘B’,如B9600B19200。使用其需通過”“操作方式。

輸入模式c_iflag成員控制端口接收端的字符輸入處理。


串口控制函數:

Tcgetattr                      取屬性(termios結構)
Tcsetattr                    
設置屬性(termios結構)
cfgetispeed                  
得到輸入速度
Cfgetospeed                
得到輸出速度
Cfsetispeed                 
設置輸入速度
Cfsetospeed                
設置輸出速度
Tcdrain                      
等待所有輸出都被傳輸
tcflow                         
掛起傳輸或接收
tcflush                        
刷清未決輸入和/或輸出
Tcsendbreak              
BREAK字符
tcgetpgrp                    
得到前臺進程組ID
tcsetpgrp                    
設置前臺進程組ID

2.1 串口配置流程
1. 
保存原先串口配置使用tcgetattr(fd,&oldtio)函數

            struct termios newtio,oldtio;
            tcgetattr( fd,&oldtio );

2. 激活選項有CLOCALCREAD,用於本地連接和接收使能。

             newtio.c_cflag | = CLOCAL | CREAD;

3. 設置波特率,使用cfsetispeedcfsetospeed函數。

             cfsetispeed(&newtio, B115200);
             cfsetospeed(&newtio, B115200);

4. 設置數據位,需使用掩碼設置。

             newtio.c_cflag &= ~CSIZE;
             newtio.c_cflag |= CS8;

5. 設置奇偶校驗位,使用c_cflagc_iflag
    
設置奇校驗:

             newtio.c_cflag |= PARENB;
             newtio.c_cflag |= PARODD;
             newtio.c_iflag |= (INPCK | ISTRIP);

    設置偶校驗:

              newtio.c_iflag |= (INPCK | ISTRIP);
              newtio.c_cflag |= PARENB;
              newtio.c_cflag &= ~PARODD;

6. 設置停止位,通過激活c_cflag中的CSTOPB實現。若停止位爲1,則清除CSTOPB,若停止位爲2,則激活CSTOPB

             newtio.c_cflag &= ~CSTOPB;

7. 設置最少字符和等待時間,對於接收字符和等待時間沒有特別要求時,可設爲0

              newtio.c_cc[VTIME] = 0;
              newtio.c_cc[VMIN] = 0;


8. 
處理要寫入的引用對象

    tcflush函數刷清輸入緩存(終端驅動程序已接收到,但用戶程序尚未讀)或輸出緩存(用戶程序已經寫,但尚未發送)。

             int tcflush(int filedes, int queue )

queue參數應當是下列三個常數之一:

? TCIFLUSH    刷清輸入隊列。
? TCOFLUSH   
刷清輸出隊列。
? TCIOFLUSH 
刷清輸入和輸出隊列。

9. 激活配置。在完成配置後,需激活配置使其生效。使用tcsettattr()函數。原型:

int tcgetattr(int filedes, struct termios * termptr);
int tcsetattr(int filedes, int opt, const struct termios * termptr);

tcsetattr的參數opt使我們可以指定在什麼時候新的終端屬性才起作用。opt可以指定爲下列常數中的一個:

? TCSANOW    更改立即發生。
? TCSADRAIN 
發送了所有輸出後更改才發生。若更改輸出參數則應使用此選擇項。
? TCSAFLUSH 
發送了所有輸出後更改才發生。更進一步,在更改發生時未讀的所有輸入數據都被刪除。


3. 串口使用詳解

     在配置完串口的相關屬性後,就可對串口進行打開,讀寫操作了。其使用方式與文件操作一樣,區別在於串口是一個終端設備。

3.1 打開串口

      fd = open( "/dev/ttyS0",O_RDWR|O_NOCTTY|O_NDELAY);
      Open
函數中除普通參數外,另有兩個參數O_NOCTTYO_NDELAY
      O_NOCTTY: 
通知linix系統,這個程序不會成爲這個端口的控制終端。
      O_NDELAY: 
通知linux系統不關心DCD信號線所處的狀態(端口的另一端是否激活或者停止)。
      
然後,恢復串口的狀態爲阻塞狀態,用於等待串口數據的讀入。用fcntl函數:

            fcntlfd, F_SETFL, 0;

      接着,測試打開的文件描述府是否引用一個終端設備,以進一步確認串口是否正確打開。

            isatty(STDIN_FILENO);


3.2 讀寫串口
      串口的讀寫與普通文件一樣,使用read,write函數。

            read(fd,buff,8);
            write(fd,buff,8);

Example:seri.c

#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>

 

 // 關於串口的配置情況

int set_opt(int fd,int nSpeed, int nBits, char nEvent, int nStop)

{

//保存原先串口配置使用

     struct termios newtio,oldtio;

     if ( tcgetattr( fd,&oldtio) != 0)

     {

         perror("SetupSerial 1");

         return -1;

     }

// 將一段內存內容全部清零:將 newtio 所指的內存區域前 sizeof( newtio ) 個字節,全部設爲0

//  <==> memset((void *) newtio,0, sizeof( newtio ))

     bzero( &newtio, sizeof( newtio ) );

// 激活選項有CLOCALCREAD,用於本地連接和接收使能

     newtio.c_cflag |= CLOCAL | CREAD;

//設置數據位,需使用掩碼設置

     newtio.c_cflag &= ~CSIZE;

 

     switch( nBits )  // 數據位的判斷

     {

         case 7:

              newtio.c_cflag |= CS7;

         break;

         case 8:

              newtio.c_cflag |= CS8;

         break;

     }

 

     switch( nEvent )  // 奇偶校驗的判斷

     {

         case 'O':     // 奇校驗

              newtio.c_cflag |= PARENB;

              newtio.c_cflag |= PARODD;

              newtio.c_iflag |= (INPCK | ISTRIP);

         break;

        

         case 'E':     // 偶校驗

              newtio.c_iflag |= (INPCK | ISTRIP);

              newtio.c_cflag |= PARENB;

              newtio.c_cflag &= ~PARODD;

         break;

         case 'N':     // 沒有校驗位

              newtio.c_cflag &= ~PARENB;

         break;

     }

 

     switch( nSpeed )   // 波特率的判斷

     {

         case 2400:

              cfsetispeed(&newtio, B2400);

              cfsetospeed(&newtio, B2400);

         break;

         case 4800:

              cfsetispeed(&newtio, B4800);

              cfsetospeed(&newtio, B4800);

         break;

         case 9600:

              cfsetispeed(&newtio, B9600);

              cfsetospeed(&newtio, B9600);

         break;

         case 115200:       //////

              cfsetispeed(&newtio, B115200);

              cfsetospeed(&newtio, B115200);

         break;

         default:

              cfsetispeed(&newtio, B9600);

              cfsetospeed(&newtio, B9600);

         break;

     }

// 停止位的判斷

     if( nStop == 1 )  

         newtio.c_cflag &= ~CSTOPB;

     else if ( nStop == 2 )

         newtio.c_cflag |= CSTOPB;

// 設置最少字符和等待時間,對於接收字符和等待時間沒有特別要求時,可設爲0

     newtio.c_cc[VTIME] = 0;

     newtio.c_cc[VMIN] = 0;

/* 處理要寫入的引用對象

    tcflush函數刷清輸入緩存(終端驅動程序已接收到,但用戶程序尚未讀)或輸出緩存(用戶程序已經寫,但尚未發送)。queue參數應當是下列三個常數之一:

     ? TCIFLUSH    刷清輸入隊列。

     ? TCOFLUSH   刷清輸出隊列。

     ? TCIOFLUSH 刷清輸入和輸出隊列。

*/

     tcflush(fd,TCIFLUSH);

/* 激活配置。在完成配置後,需激活配置使其生效。使用tcsettattr()函數。

tcsetattr的參數opt使我們可以指定在什麼時候新的終端屬性才起作用。opt可以指定爲下列常數中的一個:

     ? TCSANOW    更改立即發生。

     ? TCSADRAIN 發送了所有輸出後更改才發生。若更改輸出參數則應使用此選擇項。

     ? TCSAFLUSH 發送了所有輸出後更改才發生。更進一步,在更改發生時未讀的所有輸入數據都被刪除。

*/

     if((tcsetattr(fd,TCSANOW,&newtio))!=0)

     {

         perror("com set error");

         return -1;

     }

     printf("set done!\n");

     return 0;

}

 // 打開驅動設備文件

int open_port(int fd,int comport)

{

     char *dev[]={"/dev/ttyS0","/dev/ttyS1","/dev/ttyS2"};

     long vdisable;

/*

O_NOCTTY: 通知linix系統,這個程序不會成爲這個端口的控制終端。

O_NDELAY: 通知linux系統不關心DCD信號線所處的狀態(端口的另一端是否激活或者停止)。

*/

     if (comport==1)

     {

         fd = open( "/dev/ttyS0", O_RDWR|O_NOCTTY|O_NDELAY);

         if (-1 == fd)

         {

              perror("Can't Open Serial Port");

              return(-1);

         }

         else

              printf("open ttyS0 .....\n");

     }

/////////////////////////////

     else if(comport==2)

     {

         fd = open( "/dev/ttyS1", O_RDWR|O_NOCTTY|O_NDELAY);

         if (-1 == fd)

         {

              perror("Can't Open Serial Port");

              return(-1);

         }

         else

              printf("open ttyS1 .....\n");

     }

/////////////////////////////

     else if (comport==3)

     {

         fd = open( "/dev/ttyS2", O_RDWR|O_NOCTTY|O_NDELAY);

         if (-1 == fd)

         {

              perror("Can't Open Serial Port");

              return(-1);

         }

         else

              printf("open ttyS2 .....\n");

     }

// 恢復串口的狀態爲阻塞狀態,用於等待串口數據的讀入。用fcntl函數:

     if(fcntl(fd, F_SETFL, 0)<0)

         printf("fcntl failed!\n");

     else

         printf("fcntl=%d\n",fcntl(fd, F_SETFL,0));

// 測試打開的文件描述府是否引用一個終端設備,以進一步確認串口是否正確打開。

     if(isatty(STDIN_FILENO)==0)

         printf("standard input is not a terminal device\n");

     else

         printf("isatty success!\n");

     printf("fd-open=%d\n",fd);

     return fd;

}

 

int main(void)

{

     int fd;

     int nread,i;

     char buff[]="Hello\n";

 

     if((fd=open_port(fd,1))<0)

     {

         perror("open_port error");

         return;

     }

     if((i=set_opt(fd,115200,8,'N',1))<0)

     {

         perror("set_opt error");

         return;

     }

     printf("fd=%d\n",fd);

// fd=3;

     nread=read(fd,buff,8);

     printf("nread=%d,%s\n",nread,buff);

     close(fd);

}



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