Linux下串口設備驅動

串口操作

串口操作需要的頭文件

#include /*標準輸入輸出定義*/

#include /*標準函數庫定義*/

#

#include /*Unix標準函數定義*/

#include

#include

#include /*文件控制定義*/

#include /*PPSIX終端控制定義*/

#include /*錯誤號定義*/

打開串口

Linux下串口文件是位于/dev下的。串口一/dev/ttyS0,串口二/dev/ttyS1。打開串口是通過使用標準的文件打開函數操作:

int fd;

/*以讀寫方式打開串口*/

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

if (fd==-1)

{

/*不能打開串口一*/

perror("提示錯誤!");

}

設置串口最基本的設置串口包括波特率設置,效驗位和停止位設置。串口的設置主要是設置struct termios結構體的各成員值。

struct termio

{ unsigned short c_iflag; /*輸入模式標志*/

unsigned short c_oflag; /*輸出模式標志*/

unsigned short c_cflag; /*控制模式標志*/

unsigned short c_lflag; /* local mode flags */

unsigned char c_line; /* line discipline */

unsigned char c_cc[NCC]; /* control characters */

};

設置這個結構體很復雜,我這里就只說說常見的一些設置:波特率設置下面是修改波特率的代碼:

struct termios Opt;

tcgetattr(fd, &Opt);

cfsetispeed(&Opt,B19200); /*設置為19200Bps*/

cfsetospeed(&Opt,B19200);

tcsetattr(fd,TCANOW,&Opt);

設置波特率的例子函數:

/**

*@brief設置串口通信速率

*@param fd類型int打開串口的文件句柄

*@param speed類型int串口速度

*@return void

*/int speed_arr[] = { B38400, B19200, B9600, B4800, B2400, B1200, B300,

B38400, B19200, B9600, B4800, B2400, B1200, B300, };

int name_arr[] = {38400, 19200, 9600, 4800, 2400, 1200, 300, 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(fd1, 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;

switch (databits) /*設置數據位數*/

{

case 7:

options.c_cflag |= CS7;

break;

case 8:

options.c_cflag |= CS8;

break;

default:

fprintf(stderr,"Unsupported data sizen"); 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 parityn");

return (FALSE);

}

/*設置停止位*/

switch (stopbits)

{

case 1:

options.c_cflag &= ~CSTOPB;

break;

case 2:

options.c_cflag |= CSTOPB;

break;

default:

fprintf(stderr,"Unsupported stop bitsn");

return (FALSE);

}

/* Set input parity option */

if (parity != 'n')

options.c_iflag |= INPCK;

tcflush(fd,TCIFLUSH);

options.c_cc[VTIME] = 150; /*設置超時15 seconds*/

options.c_cc[VMIN] = 0; /* Update the options and do it NOW */

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

{

perror("SetupSerial 3");

return (FALSE);

}

return (TRUE);

}

需要注意的是:如果不是開發終端之類的,只是串口傳輸數據,而不需要串口來處理,那么使用原始模式(Raw Mode)方式來通訊,設置方式如下:

options.c_lflag &= ~(ICANON | ECHO | ECHOE | ISIG); /*Input*/

options.c_oflag &= ~OPOST; /*Output*/

讀寫串口設置好串口之后,讀寫串口就很容易了,把串口當作文件讀寫就是。

·發送數據char buffer[1024];int Length;int nByte;nByte = write(fd, buffer ,Length)

·讀取串口數據使用文件操作read函數讀取,如果設置為原始模式(Raw Mode)傳輸數據,那么read函數返回的字符數是實際串口收到的字符數??梢允褂貌僮魑募暮瘮祦韺崿F異步讀取,如fcntl,或者select等來操作。

char buff[1024];int Len;int readByte = read(fd,buff,Len);

關閉串口關閉串口就是關閉文件。

close(fd);

例子下面是一個簡單的讀取串口數據的例子,使用了上面定義的一些函數和頭文件

/**********************************************************************代碼說明:使用串口二測試的,發送的數據是字符,但是沒有發送字符串結束符號,所以接收到后,后面加上了結束符號。in我測試使用的是單片機發送數據到第二個串口,測試通過。**********************************************************************/

#define FALSE -1

#define TRUE 0

/*********************************************************************/

int OpenDev(char *Dev)

{

int fd = open( Dev, O_RDWR );

//| O_NOCTTY | O_NDELAY

if (-1 == fd)

{

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

return -1;

}

else

return fd;

}

int main(int argc, char **argv){

int fd;

int nread;

char buff[512];

char *dev = "/dev/ttyS1"; //串口二

fd = OpenDev(dev);

set_speed(fd,19200);

if (set_Parity(fd,8,1,'N') == FALSE) {

printf("Set Parity Errorn");

exit (0);

}

while (1) //循環讀取數據

{

while((nread = read(fd, buff, 512))>0)

{

printf("nLen %dn",nread);

buff[nread+1] = '';

printf( "n%s", buff);

}

}

//close(fd);

// exit (0);

}

Linux下串口編程入門

文檔選項

打印本頁

將此頁作為電子郵件發送

級別: 初級

左錦(zuo170@163.com),副總裁,南沙資訊科技園

2003年7月03日

Linux操作系統從一開始就對串行口提供了很好的支持,本文就Linux下的串行口通訊編程進行簡單的介紹。

串口簡介

串行口是計算機一種常用的接口,具有連接線少,通訊簡單,得到廣泛的使用。常用的串口是RS-232-C接口(又稱EIA RS-232-C)它是在1970年由美國電子工業協會(EIA)聯合貝爾系統、 調制解調器廠家及計算機終端生產廠家共同制定的用于串行通訊的標準。它的全名是"數據終端設備(DTE)和數據通訊設備(DCE)之間串行二進制數據交換接口技術標準"該標準規定采用一個25個腳的DB25連接器,對連接器的每個引腳的信號內容加以規定,還對各種信號的電平加以規定。傳輸距離在碼元畸變小于4%的情況下,傳輸電纜長度應為50英尺。

Linux操作系統從一開始就對串行口提供了很好的支持,本文就Linux下的串行口通訊編程進行簡單的介紹,如果要非常深入了解,建議看看本文所參考的《Serial Programming Guide for POSIX Operating Systems》

計算機串口的引腳說明

序號

信號名稱

符號

流向

功能

2

發送數據

TXD

DTE→DCE

DTE發送串行數據

3

接收數據

RXD

DTE←DCE

DTE接收串行數據

4

請求發送

RTS

DTE→DCE

DTE請求DCE將線路切換到發送方式

5

允許發送

CTS

DTE←DCE

DCE告訴DTE線路已接通可以發送數據

6

數據設備準備好

DSR

DTE←DCE

DCE準備好

7

信號地

信號公共地

8

載波檢測

DCD

DTE←DCE

表示DCE接收到遠程載波

20

數據終端準備好

DTR

DTE→DCE

DTE準備好

22

振鈴指示

RI

DTE←DCE

表示DCE與線路接通,出現振鈴

回頁首

串口操作

串口操作需要的頭文件

#include/*標準輸入輸出定義*/

#include/*標準函數庫定義*/

#include/*Unix標準函數定義*/

#include

#include

#include/*文件控制定義*/

#include/*PPSIX終端控制定義*/

#include/*錯誤號定義*/

回頁首

打開串口

在Linux下串口文件是位于/dev下的

串口一 為/dev/ttyS0

串口二 為/dev/ttyS1

打開串口是通過使用標準的文件打開函數操作:

int fd;

/*以讀寫方式打開串口*/

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

if (-1 == fd){

/*不能打開串口一*/

perror("提示錯誤!");

}

回頁首

設置串口

最基本的設置串口包括波特率設置,效驗位和停止位設置。

串口的設置主要是設置struct termios結構體的各成員值。

struct termio

{unsigned shortc_iflag;/*輸入模式標志*/

unsigned shortc_oflag;/*輸出模式標志*/

unsigned shortc_cflag;/*控制模式標志*/

unsigned shortc_lflag;/* local mode flags */

unsigned charc_line;/* line discipline */

unsigned charc_cc[NCC];/* control characters */

};

設置這個結構體很復雜,我這里就只說說常見的一些設置:

波特率設置

下面是修改波特率的代碼:

structtermios Opt;

tcgetattr(fd, &Opt);

cfsetispeed(&Opt,B19200);/*設置為19200Bps*/

cfsetospeed(&Opt,B19200);

tcsetattr(fd,TCANOW,&Opt);

設置波特率的例子函數:

/**

*@brief設置串口通信速率

*@paramfd類型int打開串口的文件句柄

*@paramspeed類型int串口速度

*@returnvoid

*/

int speed_arr[] = { B38400, B19200, B9600, B4800, B2400, B1200, B300,

B38400, B19200, B9600, B4800, B2400, B1200, B300, };

int name_arr[] = {38400,19200,9600,4800,2400,1200,300, 38400,

19200,9600, 4800, 2400, 1200,300, };

void set_speed(int fd, int speed){

inti;

intstatus;

struct termiosOpt;

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(fd1, TCSANOW, &Opt);

if(status != 0) {

perror("tcsetattr fd1");

return;

}

tcflush(fd,TCIOFLUSH);

}

}

}

效驗位和停止位的設置:

無效驗

8位

Option.c_cflag &= ~PARENB;

Option.c_cflag &= ~CSTOPB;

Option.c_cflag &= ~CSIZE;

Option.c_cflag |= ~CS8;

奇效驗(Odd)

7位

Option.c_cflag |= ~PARENB;

Option.c_cflag &= ~PARODD;

Option.c_cflag &= ~CSTOPB;

Option.c_cflag &= ~CSIZE;

Option.c_cflag |= ~CS7;

偶效驗(Even)

7位

Option.c_cflag &= ~PARENB;

Option.c_cflag |= ~PARODD;

Option.c_cflag &= ~CSTOPB;

Option.c_cflag &= ~CSIZE;

Option.c_cflag |= ~CS7;

Space效驗

7位

Option.c_cflag &= ~PARENB;

Option.c_cflag &= ~CSTOPB;

Option.c_cflag &= &~CSIZE;

Option.c_cflag |= CS8;

設置效驗的函數:

/**

*@brief設置串口數據位,停止位和效驗位

*@paramfd類型int打開的串口文件句柄

*@paramdatabits類型int數據位取值為7或者8

*@paramstopbits類型int停止位取值為1或者2

*@paramparity類型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;

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] = 150; /*設置超時15 seconds*/

options.c_cc[VMIN] = 0; /* Update the options and do it NOW */

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

{

perror("SetupSerial 3");

return (FALSE);

}

return (TRUE);

}

需要注意的是:

如果不是開發終端之類的,只是串口傳輸數據,而不需要串口來處理,那么使用原始模式(Raw Mode)方式來通訊,設置方式如下:

options.c_lflag&= ~(ICANON | ECHO | ECHOE | ISIG);/*Input*/

options.c_oflag&= ~OPOST;/*Output*/

回頁首

讀寫串口

設置好串口之后,讀寫串口就很容易了,把串口當作文件讀寫就是。

發送數據

charbuffer[1024];intLength;intnByte;nByte = write(fd, buffer ,Length)

讀取串口數據

使用文件操作read函數讀取,如果設置為原始模式(Raw Mode)傳輸數據,那么read函數返回的字符數是實際串口收到的字符數。

可以使用操作文件的函數來實現異步讀取,如fcntl,或者select等來操作。

charbuff[1024];intLen;intreadByte = read(fd,buff,Len);

回頁首

關閉串口

關閉串口就是關閉文件。

close(fd);

回頁首

例子

下面是一個簡單的讀取串口數據的例子,使用了上面定義的一些函數和頭文件

/**********************************************************************代碼說明:使用串口二測試的,發送的數據是字符,

但是沒有發送字符串結束符號,所以接收到后,后面加上了結束符號。我測試使用的是單片機發送數據到第二個串口,測試通過。

**********************************************************************/

#define FALSE-1

#define TRUE0

/*********************************************************************/

int OpenDev(char *Dev)

{

intfd = open( Dev, O_RDWR );//| O_NOCTTY | O_NDELAY

if (-1 == fd)

{

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

return -1;

}

else

return fd;

}

int main(int argc, char **argv){

int fd;

int nread;

char buff[512];

char *dev= "/dev/ttyS1"; //串口二

fd = OpenDev(dev);

set_speed(fd,19200);

if (set_Parity(fd,8,1,'N') == FALSE){

printf("Set Parity Error\n");

exit (0);

}

while (1) //循環讀取數據

{

while((nread = read(fd, buff, 512))>0)

{

printf("\nLen %d\n",nread);

buff[nread+1] = '\0';

printf( "\n%s", buff);

}

}

//close(fd);

// exit (0);

}

參考資料

Serial Programming Guide for POSIX Operating Systems

Linux的源代碼

代碼下載:代碼

Linux串口編程-中英文簡體對照版()

時間:2004-08-01

3.Program Examples示例程序

All examples have been derived fromminiterm.c. The type ahead buffer is limited to 255 characters, just like the maximum string length for canonical input processing (or).

See the comments in the code for explanation of the use of the different input modes. I hope that the code is understandable. The example for canonical input is commented best, the other examples are commented only where they differ from the example for canonical input to emphasize the differences.

The descriptions are not complete, but you are encouraged to experiment with the examples to derive the best solution for your application.

Don't forget to give the appropriate serial ports the right permissions (e. g.:chmod a+rw /dev/ttyS1)!

所有的示例來自于miniterm.c. The type ahead緩存器限制在255字節的大小,這與標準輸入(canonical input)進程的字符串最大長度相同(或).

代碼中的注釋解釋了不同輸入模式的使用以希望這些代碼能夠易于理解。標準輸入程序的示例做了最詳細的注解,其它的示例則只是在不同于標準輸入示例的地方做了強調。

敘述不是很完整,但可以激勵你對這范例做實驗,以延生出合于你所需應用程序的最佳解.

不要忘記賦予串口正確的權限(也就是:chmod a+rw /dev/ttyS1)!

3.1.Canonical Input Processing標準輸入模式

#include

#include

#include

#include

#include

/* baudrate settings are defined in , which is included by */

//波特率的設置定義在.包含在里

#define BAUDRATE B38400

/* change this definition for the correct port */

//定義您所需要的串口號

#define MODEMDEVICE "/dev/ttyS1"

#define _POSIX_SOURCE 1 /*POSIX compliant source POSIX系統兼容*/

#define FALSE 0

#define TRUE 1

volatile int STOP=FALSE;

main() {

int fd,c, res;

struct termios oldtio,newtio;

char buf[255];

/* Open modem device for reading and writing and not as controlling

tty because we don't want to get killed if linenoise sends CTRL-C.

開啟設備用于讀寫,但是不要以控制tty的模式,因為我們并不希望在發送Ctrl-C

后結束此進程

*/

fd = open(MODEMDEVICE, O_RDWR | O_NOCTTY );

if (fd <0) {perror(MODEMDEVICE); exit(-1); }

tcgetattr(fd,&oldtio); /* save current serial port settings */

//儲存當前的串口設置

bzero(&newtio, sizeof(newtio)); /* clear struct for new port settings */

//清空新的串口設置結構體

/*

BAUDRATE: Set bps rate. You could also use cfsetispeed and cfsetospeed.

CRTSCTS : output hardware flow control (only used if the cable has all

ecessary lines. See sect. 7 of Serial-HOWTO)

CS8???? : 8n1 (8bit,no parity,1 stopbit)

CLOCAL? : local connection, no modem contol

CREAD?? : enable receiving characters

BAUDRATE:設置串口的傳輸速率bps,也可以使用cfsetispeed和cfsetospeed來設置

CRTSCTS :輸出硬件流控(只能在具完整線路的纜線下工作,參考Serial-HOWTO第七節)

CS8???? : 8n1 (每一幀8比特數據,無奇偶校驗位,1比特停止位)

CLOCAL? :本地連接,無調制解調器控制

CREAD?? :允許接收數據

*/

newtio.c_cflag = BAUDRATE | CRTSCTS | CS8 | CLOCAL | CREAD;

/*

IGNPAR? : ignore bytes with parity errors

ICRNL?? : map CR to NL (otherwise a CR input on the other computer will not

terminate input) otherwise make device raw (no other input processing)

IGNPAR? :忽略奇偶校驗出錯的字節

ICRNL?? :把CR映像成NL (否則從其它機器傳來的CR無法終止輸入)或者就把設備設

為raw狀態(沒有額外的輸入處理)

*/

newtio.c_iflag = IGNPAR | ICRNL;

/*

Raw output.? Raw模式輸出

*/

newtio.c_oflag = 0;

/*

ICANON? : enable canonical input

disable all echo functionality, and don't send signals to calling program

ICANON :啟動標準輸出,關閉所有回顯echo功能,不向程序發送信號

*/

newtio.c_lflag = ICANON;

/*

initialize all control characters

default values can be found in /usr/include/termios.h, and

are given in the comments, but we don't need them here

初始化所有的控制字符,默認值可以在/usr/include/termios.h找到,

并且做了注解,不過這里我們并不需要考慮這些

*/

newtio.c_cc[VINTR]??? = 0;???? /* Ctrl-c */

newtio.c_cc[VQUIT]??? = 0;???? /* Ctrl-\ */

newtio.c_cc[VERASE]?? = 0;???? /* del */

newtio.c_cc[VKILL]??? = 0;???? /* @ */

newtio.c_cc[VEOF]???? = 4;???? /* Ctrl-d */

newtio.c_cc[VTIME]??? = 0;???? /* inter-character timer unused */

/*不使用字符間的計時器*/

newtio.c_cc[VMIN]???? = 1;???? /* blocking read until 1 character arrives */

/*阻塞,直到讀取到一個字符*/

newtio.c_cc[VSWTC]??? = 0;???? /* '\0' */

newtio.c_cc[VSTART]?? = 0;???? /* Ctrl-q */

newtio.c_cc[VSTOP]??? = 0;???? /* Ctrl-s */

newtio.c_cc[VSUSP]??? = 0;???? /* Ctrl-z */

newtio.c_cc[VEOL]???? = 0;???? /* '\0' */

newtio.c_cc[VREPRINT] = 0;???? /* Ctrl-r */

newtio.c_cc[VDISCARD] = 0;???? /* Ctrl-u */

newtio.c_cc[VWERASE]? = 0;???? /* Ctrl-w */

newtio.c_cc[VLNEXT]?? = 0;???? /* Ctrl-v */

newtio.c_cc[VEOL2]??? = 0;???? /* '\0' */

/*

now clean the modem line and activate the settings for the port

清空數據線,啟動新的串口設置

*/

tcflush(fd, TCIFLUSH);

tcsetattr(fd,TCSANOW,&newtio);

/*

terminal settings done, now handle input

In this example, inputting a 'z' at the beginning of a line will

exit the program.

終端設置完成,現在就可以處理數據了

在本程序中,在一行的開始輸入一個'z'會終止該程序

*/

while (STOP==FALSE) {???? /* loop until we have a terminating condition */

//循環直到滿足終止條件

/* read blocks program execution until a line terminating character is

input, even if more than 255 chars are input. If the number

of characters read is smaller than the number of chars available,

subsequent reads will return the remaining chars. res will be set

to the actual number of characters actually read

即使輸入超過255個字節,讀取的程序段還是會一直等到行結束符出現才會停止。

如果讀到的字符少于應剛獲得的字符數,則剩下的字符串會在下一次讀取時讀到。

res用來獲得每次真正讀到的字節數

*/

res = read(fd,buf,255);

buf[res]=0;???????????? /* set end of string, so we can printf */

//設置字符串結束符,從而可以順利使用printf

printf(":%s:%d\n", buf, res);

if (buf[0]=='z') STOP=TRUE;

}

/* restore the old port settings恢復舊的串口設置*/

tcsetattr(fd,TCSANOW,&oldtio);

}

3.2.Non-Canonical Input Processing非標準輸入模式

In non-canonical input processing mode, input is not assembled into lines and input processing (erase, kill, delete, etc.) does not occur. Two parameters control the behavior of this mode:c_cc[VTIME]sets the character timer, andc_cc[VMIN]sets the minimum number of characters to receive before satisfying the read.

If MIN > 0 and TIME = 0, MIN sets the number of characters to receive before the read is satisfied. As TIME is zero, the timer is not used.

If MIN = 0 and TIME > 0, TIME serves as a timeout value. The read will be satisfied if a single character is read, or TIME is exceeded (t = TIME *0.1 s). If TIME is exceeded, no character will be returned.

If MIN > 0 and TIME > 0, TIME serves as an inter-character timer. The read will be satisfied if MIN characters are received, or the time between two characters exceeds TIME. The timer is restarted every time a character is received and only becomes active after the first character has been received.

If MIN = 0 and TIME = 0, read will be satisfied immediately. The number of characters currently available, or the number of characters requested will be returned. According to Antonino (see contributions), you could issue afcntl(fd, F_SETFL, FNDELAY);before reading to get the same result.

By modifyingnewtio.c_cc[VTIME]andnewtio.c_cc[VMIN]all modes described above can be tested.

在非標準輸入模式中,輸入的數據并不組合成行,也不會進行erase, kill, delete等輸入處理。我們只是用兩個參數來控制這種模式的輸入行為:c_cc[VTIME]設定字符輸入間隔時間的計時器,而c_cc[VMIN]設置滿足讀取函數的最少字節數。

MIN > 0, TIME = 0:讀取函數在讀到了MIN值的字符數后返回。

MIN = 0, TIME > 0:TIME決定了超時值,讀取函數在讀到一個字節的字符,或者等待讀取時間超過TIME(t = TIME * 0.1s)以后返回,也就是說,即使沒有從串口中讀到數據,讀取函數也會在TIME時間后返回。

MIN > 0, TIME > 0:讀取函數會在收到了MIN字節的數據后,或者超過TIME時間沒收到數據后返回。此計時器會在每次收到字符的時候重新計時,也只會在收到第一個字節后才啟動。

MIN = 0, TIME = 0:讀取函數會立即返回。實際讀取到的字符數,或者要讀到的字符數,會作為返回值返回。根據Antonino(參考conditions),可以使用fcntl(fd, F_SETFL, FNDELAY),在讀取前獲得同樣的結果。

改變了nettio.c_cc[VTIME]和newtio.c_cc[VMIN],就可以測試以上的設置了。

#include

#include

#include

#include

#include

#define BAUDRATE B38400

#define MODEMDEVICE "/dev/ttyS1"

#define _POSIX_SOURCE 1 /* POSIX compliant source */

#define FALSE 0

#define TRUE 1

volatile int STOP=FALSE;

main() {

int fd,c, res;

struct termios oldtio,newtio;

char buf[255];

fd = open(MODEMDEVICE, O_RDWR | O_NOCTTY );

if (fd <0) {perror(MODEMDEVICE); exit(-1); }

tcgetattr(fd,&oldtio); /* save current port settings */

bzero(&newtio, sizeof(newtio));

newtio.c_cflag = BAUDRATE | CRTSCTS | CS8 | CLOCAL | CREAD;

newtio.c_iflag = IGNPAR;

newtio.c_oflag = 0;

/* set input mode (non-canonical, no echo,...) */

//設置輸入模式為非標準輸入

newtio.c_lflag = 0;

newtio.c_cc[VTIME] = 0;?? /* inter-character timer unused */

//不是用字符間隔計時器

newtio.c_cc[VMIN] = 5;??? /* blocking read until 5 chars received */

//收到5個字符數以后,read函數才返回

tcflush(fd, TCIFLUSH);

tcsetattr(fd,TCSANOW,&newtio);

while (STOP==FALSE) {?????? /* loop for input */

res = read(fd,buf,255);?? /* returns after 5 chars have been input */

buf[res]=0;?????????????? /* so we can printf... */

printf(":%s:%d\n", buf, res);

if (buf[0]=='z') STOP=TRUE;

}

tcsetattr(fd,TCSANOW,&oldtio);

}

3.3.Asynchronous Input異步輸入模式

#include

#include

#include

#include

#include

#include

#define BAUDRATE B38400

#define MODEMDEVICE "/dev/ttyS1"

#define _POSIX_SOURCE 1 /* POSIX compliant source */

#define FALSE 0

#define TRUE 1

volatile int STOP=FALSE;

void signal_handler_IO (int status);?? /* definition of signal handler */

//定義信號處理程序

int wait_flag=TRUE;?????????????????? /* TRUE while no signal received */

// TRUE代表沒有受到信號,正在等待中

main()?? {

int fd,c, res;

struct termios oldtio,newtio;

struct sigaction saio;

/* definition of signal action */

//定義信號處理的結構

char buf[255];

/* open the device to be non-blocking (read will return immediatly) */

//是用非阻塞模式打開設備read函數立刻返回,不會阻塞

fd = open(MODEMDEVICE, O_RDWR | O_NOCTTY | O_NONBLOCK);

if (fd <0) {perror(MODEMDEVICE); exit(-1); }

/* install the signal handler before making the device asynchronous */

//在進行設備異步傳輸前,安裝信號處理程序

saio.sa_handler = signal_handler_IO;

saio.sa_mask = 0;

saio.sa_flags = 0;

saio.sa_restorer = NULL;

sigaction(SIGIO,&saio,NULL);

/* allow the process to receive SIGIO */

//允許進程接收SIGIO信號

fcntl(fd, F_SETOWN, getpid());

/* Make the file descriptor asynchronous (the manual page says only

O_APPEND and O_NONBLOCK, will work with F_SETFL...) */

//設置串口的文件描述符為異步,man上說,只有O_APPEND和O_NONBLOCK才能使用F_SETFL

fcntl(fd, F_SETFL, FASYNC);

tcgetattr(fd,&oldtio); /* save current port settings */

/* set new port settings for canonical input processing */

//設置新的串口為標準輸入模式

newtio.c_cflag = BAUDRATE | CRTSCTS | CS8 | CLOCAL | CREAD;

newtio.c_iflag = IGNPAR | ICRNL;

newtio.c_oflag = 0;

newtio.c_lflag = ICANON;

newtio.c_cc[VMIN]=1;

newtio.c_cc[VTIME]=0;

tcflush(fd, TCIFLUSH);

tcsetattr(fd,TCSANOW,&newtio);

/* loop while waiting for input. normally we would do something

useful here循環等待輸入,通常我們會在這里做些其它的事情*/

while (STOP==FALSE) {

printf(".\n");usleep(100000);

/* after receiving SIGIO, wait_flag = FALSE, input is availableand can be read */

//在收到SIGIO信號后,wait_flag = FALSE,表示有輸入進來,可以讀取了

if (wait_flag==FALSE) {

res = read(fd,buf,255);

buf[res]=0;

printf(":%s:%d\n", buf, res);

if (res==1) STOP=TRUE; /* stop loop if only a CR was input */

wait_flag = TRUE;????? /* wait for new input等待新的輸入*/

}

}

/* restore old port settings */

tcsetattr(fd,TCSANOW,&oldtio);

}

/***************************************************************************

* signal handler. sets wait_flag to FALSE, to indicate above loop that??? *

* characters have been received.????????????????????????????????????????? *

***************************************************************************/

//信號處理函數,設置wait_flag為FALSE,以告知上面的循環函數串口收到字符了

void signal_handler_IO (int status)?? {

printf("received SIGIO signal.\n");

wait_flag = FALSE;

}

3.4.Waiting for Input from Multiple Sources等待來自多個源的輸入

This section is kept to a minimum. It is just intended to be a hint, and therefore the example code is kept short. This will not only work with serial ports, but with any set of file descriptors.

The select call and accompanying macros use afd_set. This is a bit array, which has a bit entry for every valid file descriptor number.selectwill accept afd_setwith the bits set for the relevant file descriptors and returns afd_set, in which the bits for the file descriptors are set where input, output, or an exception occurred. All handling offd_setis done with the provided macros. See also the manual pageselect(2).

這一部分的內容很少,只是作為一個提示,因此這段代碼也很簡短。而且這部分內容不僅適用于串口編程,而且適用于任意的一組文件描述符。

select調用及其相應的宏,使用fd_set.這是一個比特數組,其中每一個比特代表了一個有效的文件描述符號。select調用接收一個有效的文件描述符結構,并返回fd_set比特數組,如果此比特數組中有某一個位設為1,就表示對應的文件描述符發生了輸入,輸出或者有例外事件。所有fg_set的處理都由宏提供了,具體參考man select 2。

#include

#include

#include

main()

{

intfd1, fd2;/* input sources 1 and 2輸入源1和2 */

fd_set readfs;/* file descriptor set */

intmaxfd;/* maximum file desciptor used用到的文件描述符的最大值*/

intloop=1;/* loop while TRUE循環標志*/

/* open_input_source opens a device, sets the port correctly, and

returns a file descriptor */

// open_input_source函數打開一個設備,正確設置端口,并返回文件描述符

fd1 = open_input_source("/dev/ttyS1");/* COM2 */

if (fd1<0) exit(0);

fd2 = open_input_source("/dev/ttyS2");/* COM3 */

if (fd2<0) exit(0);

maxfd = MAX (fd1, fd2)+1;/* maximum bit entry (fd) to test */

/* loop for input */

while (loop) {

FD_SET(fd1, &readfs);/* set testing for source 1 */

FD_SET(fd2, &readfs);/* set testing for source 2 */

/* block until input becomes available阻塞直到有輸入進來*/

select(maxfd, &readfs, NULL, NULL, NULL);

if (FD_ISSET(fd1))/* input from source 1 available源1有輸入*/

handle_input_from_source1();

if (FD_ISSET(fd2))/* input from source 2 available源2有輸入*/

handle_input_from_source2();

}

}

The given example blocks indefinitely, until input from one of the sources becomes available. If you need to timeout on input, just replace the select call by:

這個例子會導致未知的阻塞,知道其中一個源有數據輸入。如果你需要為輸入設置一個超時值,就用下面的select替代:

int res;

struct timeval Timeout;

/* set timeout value within input loop在輸入循環中設置超時值*/

Timeout.tv_usec = 0;/* milliseconds設置毫秒數*/

Timeout.tv_sec= 1;/* seconds設置秒數*/

res = select(maxfd, &readfs, NULL, NULL, &Timeout);

if (res==0)

/* number of file descriptors with input = 0, timeout occurred.所有的文件描述符都沒有得到輸入,超時退出返回0 */

This example will timeout after 1 second. If a timeout occurs, select will return 0, but beware that Timeout is decremented by the time actually waited for input by select. If the timeout value is zero, select will return immediatly.

這個例子會在1秒以后超時退出,如果發生超時,select返回0,請注意Timeout是根據select實際等待輸入的時間遞減的,如果把timeout設為0,select函數會立刻退出。

Other Sources of Information其它資源信息

·The Linux Serial-HOWTO describes how to set up serial ports and contains hardware information.

Linux Serial HOWTO介紹了如何安裝串口,并包括了硬件信息。

·Serial Programming Guide for POSIX Compliant Operating Systems, by Michael Sweet.

POSIX兼容的操作系統上的串口編程

·The manual pagetermios(3)describes all flags for thetermiosstructure.

man termios 3介紹了所有termios結構里的設置。

最后編輯于
?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市,隨后出現的幾起案子,更是在濱河造成了極大的恐慌,老刑警劉巖,帶你破解...
    沈念sama閱讀 229,001評論 6 537
  • 序言:濱河連續發生了三起死亡事件,死亡現場離奇詭異,居然都是意外死亡,警方通過查閱死者的電腦和手機,發現死者居然都...
    沈念sama閱讀 98,786評論 3 423
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人,你說我怎么就攤上這事?!?“怎么了?”我有些...
    開封第一講書人閱讀 176,986評論 0 381
  • 文/不壞的土叔 我叫張陵,是天一觀的道長。 經常有香客問我,道長,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 63,204評論 1 315
  • 正文 為了忘掉前任,我火速辦了婚禮,結果婚禮上,老公的妹妹穿的比我還像新娘。我一直安慰自己,他們只是感情好,可當我...
    茶點故事閱讀 71,964評論 6 410
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著,像睡著了一般。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發上,一...
    開封第一講書人閱讀 55,354評論 1 324
  • 那天,我揣著相機與錄音,去河邊找鬼。 笑死,一個胖子當著我的面吹牛,可吹牛的內容都是我干的。 我是一名探鬼主播,決...
    沈念sama閱讀 43,410評論 3 444
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了?” 一聲冷哼從身側響起,我...
    開封第一講書人閱讀 42,554評論 0 289
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后,有當地人在樹林里發現了一具尸體,經...
    沈念sama閱讀 49,106評論 1 335
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內容為張勛視角 年9月15日...
    茶點故事閱讀 40,918評論 3 356
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發現自己被綠了。 大學時的朋友給我發了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 43,093評論 1 371
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖,靈堂內的尸體忽然破棺而出,到底是詐尸還是另有隱情,我是刑警寧澤,帶...
    沈念sama閱讀 38,648評論 5 362
  • 正文 年R本政府宣布,位于F島的核電站,受9級特大地震影響,放射性物質發生泄漏。R本人自食惡果不足惜,卻給世界環境...
    茶點故事閱讀 44,342評論 3 347
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧,春花似錦、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 34,755評論 0 28
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至,卻和暖如春,著一層夾襖步出監牢的瞬間,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 36,009評論 1 289
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人。 一個月前我還...
    沈念sama閱讀 51,839評論 3 395
  • 正文 我出身青樓,卻偏偏與公主長得像,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 48,107評論 2 375

推薦閱讀更多精彩內容