Linux下串口設(shè)備驅(qū)動(dòng)

串口操作

串口操作需要的頭文件

#include /*標(biāo)準(zhǔn)輸入輸出定義*/

#include /*標(biāo)準(zhǔn)函數(shù)庫(kù)定義*/

#

#include /*Unix標(biāo)準(zhǔn)函數(shù)定義*/

#include

#include

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

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

#include /*錯(cuò)誤號(hào)定義*/

打開(kāi)串口

Linux下串口文件是位于/dev下的。串口一/dev/ttyS0鼻百,串口二/dev/ttyS1肴捉。打開(kāi)串口是通過(guò)使用標(biāo)準(zhǔn)的文件打開(kāi)函數(shù)操作:

int fd;

/*以讀寫(xiě)方式打開(kāi)串口*/

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

if (fd==-1)

{

/*不能打開(kāi)串口一*/

perror("提示錯(cuò)誤!");

}

設(shè)置串口最基本的設(shè)置串口包括波特率設(shè)置,效驗(yàn)位和停止位設(shè)置赦政。串口的設(shè)置主要是設(shè)置struct termios結(jié)構(gòu)體的各成員值拙已。

struct termio

{ unsigned short c_iflag; /*輸入模式標(biāo)志*/

unsigned short c_oflag; /*輸出模式標(biāo)志*/

unsigned short c_cflag; /*控制模式標(biāo)志*/

unsigned short c_lflag; /* local mode flags */

unsigned char c_line; /* line discipline */

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

};

設(shè)置這個(gè)結(jié)構(gòu)體很復(fù)雜冲粤,我這里就只說(shuō)說(shuō)常見(jiàn)的一些設(shè)置:波特率設(shè)置下面是修改波特率的代碼:

struct termios Opt;

tcgetattr(fd, &Opt);

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

cfsetospeed(&Opt,B19200);

tcsetattr(fd,TCANOW,&Opt);

設(shè)置波特率的例子函數(shù):

/**

*@brief設(shè)置串口通信速率

*@param fd類型int打開(kāi)串口的文件句柄

*@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);

}

}

}

設(shè)置效驗(yàn)的函數(shù):

/**

*@brief設(shè)置串口數(shù)據(jù)位,停止位和效驗(yàn)位

*@param fd類型int打開(kāi)的串口文件句柄

*@param databits類型int數(shù)據(jù)位取值7或者8

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

*@param parity類型int效驗(yàn)類型取值為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) /*設(shè)置數(shù)據(jù)位數(shù)*/

{

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); /*設(shè)置為奇效驗(yàn)*/

options.c_iflag |= INPCK; /* Disnable parity checking */

break;

case 'e':

case 'E':

options.c_cflag |= PARENB; /* Enable parity */

options.c_cflag &= ~PARODD; /*轉(zhuǎn)換為偶效驗(yàn)*/

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);

}

/*設(shè)置停止位*/

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; /*設(shè)置超時(shí)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);

}

需要注意的是:如果不是開(kāi)發(fā)終端之類的爪瓜,只是串口傳輸數(shù)據(jù)蹬跃,而不需要串口來(lái)處理,那么使用原始模式(Raw Mode)方式來(lái)通訊铆铆,設(shè)置方式如下:

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

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

讀寫(xiě)串口設(shè)置好串口之后蝶缀,讀寫(xiě)串口就很容易了,把串口當(dāng)作文件讀寫(xiě)就是薄货。

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

·讀取串口數(shù)據(jù)使用文件操作read函數(shù)讀取扼劈,如果設(shè)置為原始模式(Raw Mode)傳輸數(shù)據(jù),那么read函數(shù)返回的字符數(shù)是實(shí)際串口收到的字符數(shù)菲驴〖龀常可以使用操作文件的函數(shù)來(lái)實(shí)現(xiàn)異步讀取,如fcntl赊瞬,或者select等來(lái)操作先煎。

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

關(guān)閉串口關(guān)閉串口就是關(guān)閉文件。

close(fd);

例子下面是一個(gè)簡(jiǎn)單的讀取串口數(shù)據(jù)的例子巧涧,使用了上面定義的一些函數(shù)和頭文件

/**********************************************************************代碼說(shuō)明:使用串口二測(cè)試的薯蝎,發(fā)送的數(shù)據(jù)是字符,但是沒(méi)有發(fā)送字符串結(jié)束符號(hào)谤绳,所以接收到后占锯,后面加上了結(jié)束符號(hào)。in我測(cè)試使用的是單片機(jī)發(fā)送數(shù)據(jù)到第二個(gè)串口缩筛,測(cè)試通過(guò)消略。**********************************************************************/

#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) //循環(huán)讀取數(shù)據(jù)

{

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

{

printf("nLen %dn",nread);

buff[nread+1] = '';

printf( "n%s", buff);

}

}

//close(fd);

// exit (0);

}

Linux下串口編程入門

文檔選項(xiàng)

打印本頁(yè)

將此頁(yè)作為電子郵件發(fā)送

級(jí)別: 初級(jí)

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

2003年7月03日

Linux操作系統(tǒng)從一開(kāi)始就對(duì)串行口提供了很好的支持,本文就Linux下的串行口通訊編程進(jìn)行簡(jiǎn)單的介紹瞎抛。

串口簡(jiǎn)介

串行口是計(jì)算機(jī)一種常用的接口艺演,具有連接線少,通訊簡(jiǎn)單,得到廣泛的使用胎撤。常用的串口是RS-232-C接口(又稱EIA RS-232-C)它是在1970年由美國(guó)電子工業(yè)協(xié)會(huì)(EIA)聯(lián)合貝爾系統(tǒng)晓殊、 調(diào)制解調(diào)器廠家及計(jì)算機(jī)終端生產(chǎn)廠家共同制定的用于串行通訊的標(biāo)準(zhǔn)。它的全名是"數(shù)據(jù)終端設(shè)備(DTE)和數(shù)據(jù)通訊設(shè)備(DCE)之間串行二進(jìn)制數(shù)據(jù)交換接口技術(shù)標(biāo)準(zhǔn)"該標(biāo)準(zhǔn)規(guī)定采用一個(gè)25個(gè)腳的DB25連接器伤提,對(duì)連接器的每個(gè)引腳的信號(hào)內(nèi)容加以規(guī)定巫俺,還對(duì)各種信號(hào)的電平加以規(guī)定。傳輸距離在碼元畸變小于4%的情況下肿男,傳輸電纜長(zhǎng)度應(yīng)為50英尺识藤。

Linux操作系統(tǒng)從一開(kāi)始就對(duì)串行口提供了很好的支持,本文就Linux下的串行口通訊編程進(jìn)行簡(jiǎn)單的介紹次伶,如果要非常深入了解痴昧,建議看看本文所參考的《Serial Programming Guide for POSIX Operating Systems》

計(jì)算機(jī)串口的引腳說(shuō)明

序號(hào)

信號(hào)名稱

符號(hào)

流向

功能

2

發(fā)送數(shù)據(jù)

TXD

DTE→DCE

DTE發(fā)送串行數(shù)據(jù)

3

接收數(shù)據(jù)

RXD

DTE←DCE

DTE接收串行數(shù)據(jù)

4

請(qǐng)求發(fā)送

RTS

DTE→DCE

DTE請(qǐng)求DCE將線路切換到發(fā)送方式

5

允許發(fā)送

CTS

DTE←DCE

DCE告訴DTE線路已接通可以發(fā)送數(shù)據(jù)

6

數(shù)據(jù)設(shè)備準(zhǔn)備好

DSR

DTE←DCE

DCE準(zhǔn)備好

7

信號(hào)地

信號(hào)公共地

8

載波檢測(cè)

DCD

DTE←DCE

表示DCE接收到遠(yuǎn)程載波

20

數(shù)據(jù)終端準(zhǔn)備好

DTR

DTE→DCE

DTE準(zhǔn)備好

22

振鈴指示

RI

DTE←DCE

表示DCE與線路接通,出現(xiàn)振鈴

回頁(yè)首

串口操作

串口操作需要的頭文件

#include/*標(biāo)準(zhǔn)輸入輸出定義*/

#include/*標(biāo)準(zhǔn)函數(shù)庫(kù)定義*/

#include/*Unix標(biāo)準(zhǔn)函數(shù)定義*/

#include

#include

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

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

#include/*錯(cuò)誤號(hào)定義*/

回頁(yè)首

打開(kāi)串口

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

串口一 為/dev/ttyS0

串口二 為/dev/ttyS1

打開(kāi)串口是通過(guò)使用標(biāo)準(zhǔn)的文件打開(kāi)函數(shù)操作:

int fd;

/*以讀寫(xiě)方式打開(kāi)串口*/

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

if (-1 == fd){

/*不能打開(kāi)串口一*/

perror("提示錯(cuò)誤冠王!");

}

回頁(yè)首

設(shè)置串口

最基本的設(shè)置串口包括波特率設(shè)置赶撰,效驗(yàn)位和停止位設(shè)置。

串口的設(shè)置主要是設(shè)置struct termios結(jié)構(gòu)體的各成員值柱彻。

struct termio

{unsigned shortc_iflag;/*輸入模式標(biāo)志*/

unsigned shortc_oflag;/*輸出模式標(biāo)志*/

unsigned shortc_cflag;/*控制模式標(biāo)志*/

unsigned shortc_lflag;/* local mode flags */

unsigned charc_line;/* line discipline */

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

};

設(shè)置這個(gè)結(jié)構(gòu)體很復(fù)雜豪娜,我這里就只說(shuō)說(shuō)常見(jiàn)的一些設(shè)置:

波特率設(shè)置

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

structtermios Opt;

tcgetattr(fd, &Opt);

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

cfsetospeed(&Opt,B19200);

tcsetattr(fd,TCANOW,&Opt);

設(shè)置波特率的例子函數(shù):

/**

*@brief設(shè)置串口通信速率

*@paramfd類型int打開(kāi)串口的文件句柄

*@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);

}

}

}

效驗(yàn)位和停止位的設(shè)置:

無(wú)效驗(yàn)

8位

Option.c_cflag &= ~PARENB;

Option.c_cflag &= ~CSTOPB;

Option.c_cflag &= ~CSIZE;

Option.c_cflag |= ~CS8;

奇效驗(yàn)(Odd)

7位

Option.c_cflag |= ~PARENB;

Option.c_cflag &= ~PARODD;

Option.c_cflag &= ~CSTOPB;

Option.c_cflag &= ~CSIZE;

Option.c_cflag |= ~CS7;

偶效驗(yàn)(Even)

7位

Option.c_cflag &= ~PARENB;

Option.c_cflag |= ~PARODD;

Option.c_cflag &= ~CSTOPB;

Option.c_cflag &= ~CSIZE;

Option.c_cflag |= ~CS7;

Space效驗(yàn)

7位

Option.c_cflag &= ~PARENB;

Option.c_cflag &= ~CSTOPB;

Option.c_cflag &= &~CSIZE;

Option.c_cflag |= CS8;

設(shè)置效驗(yàn)的函數(shù):

/**

*@brief設(shè)置串口數(shù)據(jù)位,停止位和效驗(yàn)位

*@paramfd類型int打開(kāi)的串口文件句柄

*@paramdatabits類型int數(shù)據(jù)位取值為7或者8

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

*@paramparity類型int效驗(yàn)類型取值為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) /*設(shè)置數(shù)據(jù)位數(shù)*/

{

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); /*設(shè)置為奇效驗(yàn)*/

options.c_iflag |= INPCK;/* Disnable parity checking */

break;

case 'e':

case 'E':

options.c_cflag |= PARENB;/* Enable parity */

options.c_cflag &= ~PARODD;/*轉(zhuǎn)換為偶效驗(yàn)*/

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);

}

/*設(shè)置停止位*/

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; /*設(shè)置超時(shí)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);

}

需要注意的是:

如果不是開(kāi)發(fā)終端之類的哟楷,只是串口傳輸數(shù)據(jù)瘤载,而不需要串口來(lái)處理,那么使用原始模式(Raw Mode)方式來(lái)通訊卖擅,設(shè)置方式如下:

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

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

回頁(yè)首

讀寫(xiě)串口

設(shè)置好串口之后鸣奔,讀寫(xiě)串口就很容易了,把串口當(dāng)作文件讀寫(xiě)就是惩阶。

發(fā)送數(shù)據(jù)

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

讀取串口數(shù)據(jù)

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

可以使用操作文件的函數(shù)來(lái)實(shí)現(xiàn)異步讀取锨匆,如fcntl,或者select等來(lái)操作冬筒。

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

回頁(yè)首

關(guān)閉串口

關(guān)閉串口就是關(guān)閉文件恐锣。

close(fd);

回頁(yè)首

例子

下面是一個(gè)簡(jiǎn)單的讀取串口數(shù)據(jù)的例子,使用了上面定義的一些函數(shù)和頭文件

/**********************************************************************代碼說(shuō)明:使用串口二測(cè)試的舞痰,發(fā)送的數(shù)據(jù)是字符土榴,

但是沒(méi)有發(fā)送字符串結(jié)束符號(hào),所以接收到后匀奏,后面加上了結(jié)束符號(hào)鞭衩。我測(cè)試使用的是單片機(jī)發(fā)送數(shù)據(jù)到第二個(gè)串口,測(cè)試通過(guò)娃善。

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

#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) //循環(huán)讀取數(shù)據(jù)

{

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串口編程-中英文簡(jiǎn)體對(duì)照版(續(xù))

時(shí)間: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)!

所有的示例來(lái)自于miniterm.c. The type ahead緩存器限制在255字節(jié)的大小,這與標(biāo)準(zhǔn)輸入(canonical input)進(jìn)程的字符串最大長(zhǎng)度相同(或).

代碼中的注釋解釋了不同輸入模式的使用以希望這些代碼能夠易于理解论衍。標(biāo)準(zhǔn)輸入程序的示例做了最詳細(xì)的注解,其它的示例則只是在不同于標(biāo)準(zhǔn)輸入示例的地方做了強(qiáng)調(diào)。

敘述不是很完整,但可以激勵(lì)你對(duì)這范例做實(shí)驗(yàn),以延生出合于你所需應(yīng)用程序的最佳解.

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

3.1.Canonical Input Processing標(biāo)準(zhǔn)輸入模式

#include

#include

#include

#include

#include

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

//波特率的設(shè)置定義在.包含在里

#define BAUDRATE B38400

/* change this definition for the correct port */

//定義您所需要的串口號(hào)

#define MODEMDEVICE "/dev/ttyS1"

#define _POSIX_SOURCE 1 /*POSIX compliant source POSIX系統(tǒng)兼容*/

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

開(kāi)啟設(shè)備用于讀寫(xiě)聚磺,但是不要以控制tty的模式坯台,因?yàn)槲覀儾⒉幌M诎l(fā)送Ctrl-C

后結(jié)束此進(jìn)程

*/

fd = open(MODEMDEVICE, O_RDWR | O_NOCTTY );

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

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

//儲(chǔ)存當(dāng)前的串口設(shè)置

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

//清空新的串口設(shè)置結(jié)構(gòu)體

/*

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:設(shè)置串口的傳輸速率bps,也可以使用cfsetispeed和cfsetospeed來(lái)設(shè)置

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

CS8???? : 8n1 (每一幀8比特?cái)?shù)據(jù),無(wú)奇偶校驗(yàn)位,1比特停止位)

CLOCAL? :本地連接瘫寝,無(wú)調(diào)制解調(diào)器控制

CREAD?? :允許接收數(shù)據(jù)

*/

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? :忽略奇偶校驗(yàn)出錯(cuò)的字節(jié)

ICRNL?? :把CR映像成NL (否則從其它機(jī)器傳來(lái)的CR無(wú)法終止輸入)或者就把設(shè)備設(shè)

為raw狀態(tài)(沒(méi)有額外的輸入處理)

*/

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 :啟動(dòng)標(biāo)準(zhǔn)輸出蜒蕾,關(guān)閉所有回顯echo功能,不向程序發(fā)送信號(hào)

*/

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

初始化所有的控制字符焕阿,默認(rèn)值可以在/usr/include/termios.h找到咪啡,

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

*/

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 */

/*不使用字符間的計(jì)時(shí)器*/

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

/*阻塞暮屡,直到讀取到一個(gè)字符*/

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

清空數(shù)據(jù)線撤摸,啟動(dòng)新的串口設(shè)置

*/

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.

終端設(shè)置完成,現(xiàn)在就可以處理數(shù)據(jù)了

在本程序中褒纲,在一行的開(kāi)始輸入一個(gè)'z'會(huì)終止該程序

*/

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

//循環(huán)直到滿足終止條件

/* 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

即使輸入超過(guò)255個(gè)字節(jié)准夷,讀取的程序段還是會(huì)一直等到行結(jié)束符出現(xiàn)才會(huì)停止。

如果讀到的字符少于應(yīng)剛獲得的字符數(shù)莺掠,則剩下的字符串會(huì)在下一次讀取時(shí)讀到衫嵌。

res用來(lái)獲得每次真正讀到的字節(jié)數(shù)

*/

res = read(fd,buf,255);

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

//設(shè)置字符串結(jié)束符,從而可以順利使用printf

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

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

}

/* restore the old port settings恢復(fù)舊的串口設(shè)置*/

tcsetattr(fd,TCSANOW,&oldtio);

}

3.2.Non-Canonical Input Processing非標(biāo)準(zhǔn)輸入模式

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.

在非標(biāo)準(zhǔn)輸入模式中彻秆,輸入的數(shù)據(jù)并不組合成行楔绞,也不會(huì)進(jìn)行erase, kill, delete等輸入處理。我們只是用兩個(gè)參數(shù)來(lái)控制這種模式的輸入行為:c_cc[VTIME]設(shè)定字符輸入間隔時(shí)間的計(jì)時(shí)器唇兑,而c_cc[VMIN]設(shè)置滿足讀取函數(shù)的最少字節(jié)數(shù)墓律。

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

MIN = 0, TIME > 0:TIME決定了超時(shí)值幔亥,讀取函數(shù)在讀到一個(gè)字節(jié)的字符耻讽,或者等待讀取時(shí)間超過(guò)TIME(t = TIME * 0.1s)以后返回,也就是說(shuō)帕棉,即使沒(méi)有從串口中讀到數(shù)據(jù)针肥,讀取函數(shù)也會(huì)在TIME時(shí)間后返回。

MIN > 0, TIME > 0:讀取函數(shù)會(huì)在收到了MIN字節(jié)的數(shù)據(jù)后香伴,或者超過(guò)TIME時(shí)間沒(méi)收到數(shù)據(jù)后返回慰枕。此計(jì)時(shí)器會(huì)在每次收到字符的時(shí)候重新計(jì)時(shí),也只會(huì)在收到第一個(gè)字節(jié)后才啟動(dòng)即纲。

MIN = 0, TIME = 0:讀取函數(shù)會(huì)立即返回具帮。實(shí)際讀取到的字符數(shù),或者要讀到的字符數(shù),會(huì)作為返回值返回蜂厅。根據(jù)Antonino(參考conditions),可以使用fcntl(fd, F_SETFL, FNDELAY),在讀取前獲得同樣的結(jié)果匪凡。

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

#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,...) */

//設(shè)置輸入模式為非標(biāo)準(zhǔn)輸入

newtio.c_lflag = 0;

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

//不是用字符間隔計(jì)時(shí)器

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

//收到5個(gè)字符數(shù)以后掘猿,read函數(shù)才返回

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 */

//定義信號(hào)處理程序

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

// TRUE代表沒(méi)有受到信號(hào)病游,正在等待中

main()?? {

int fd,c, res;

struct termios oldtio,newtio;

struct sigaction saio;

/* definition of signal action */

//定義信號(hào)處理的結(jié)構(gòu)

char buf[255];

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

//是用非阻塞模式打開(kāi)設(shè)備read函數(shù)立刻返回,不會(huì)阻塞

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 */

//在進(jìn)行設(shè)備異步傳輸前稠通,安裝信號(hào)處理程序

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 */

//允許進(jìn)程接收SIGIO信號(hào)

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...) */

//設(shè)置串口的文件描述符為異步衬衬,man上說(shuō),只有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 */

//設(shè)置新的串口為標(biāo)準(zhǔn)輸入模式

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循環(huán)等待輸入改橘,通常我們會(huì)在這里做些其它的事情*/

while (STOP==FALSE) {

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

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

//在收到SIGIO信號(hào)后滋尉,wait_flag = FALSE,表示有輸入進(jìn)來(lái),可以讀取了

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.????????????????????????????????????????? *

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

//信號(hào)處理函數(shù)飞主,設(shè)置wait_flag為FALSE,以告知上面的循環(huán)函數(shù)串口收到字符了

void signal_handler_IO (int status)?? {

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

wait_flag = FALSE;

}

3.4.Waiting for Input from Multiple Sources等待來(lái)自多個(gè)源的輸入

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).

這一部分的內(nèi)容很少兼砖,只是作為一個(gè)提示,因此這段代碼也很簡(jiǎn)短既棺。而且這部分內(nèi)容不僅適用于串口編程讽挟,而且適用于任意的一組文件描述符。

select調(diào)用及其相應(yīng)的宏丸冕,使用fd_set.這是一個(gè)比特?cái)?shù)組耽梅,其中每一個(gè)比特代表了一個(gè)有效的文件描述符號(hào)。select調(diào)用接收一個(gè)有效的文件描述符結(jié)構(gòu)胖烛,并返回fd_set比特?cái)?shù)組眼姐,如果此比特?cái)?shù)組中有某一個(gè)位設(shè)為1,就表示對(duì)應(yīng)的文件描述符發(fā)生了輸入佩番,輸出或者有例外事件众旗。所有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循環(huán)標(biāo)志*/

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

returns a file descriptor */

// open_input_source函數(shù)打開(kāi)一個(gè)設(shè)備贡歧,正確設(shè)置端口,并返回文件描述符

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阻塞直到有輸入進(jìn)來(lái)*/

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:

這個(gè)例子會(huì)導(dǎo)致未知的阻塞赋秀,知道其中一個(gè)源有數(shù)據(jù)輸入利朵。如果你需要為輸入設(shè)置一個(gè)超時(shí)值,就用下面的select替代:

int res;

struct timeval Timeout;

/* set timeout value within input loop在輸入循環(huán)中設(shè)置超時(shí)值*/

Timeout.tv_usec = 0;/* milliseconds設(shè)置毫秒數(shù)*/

Timeout.tv_sec= 1;/* seconds設(shè)置秒數(shù)*/

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

if (res==0)

/* number of file descriptors with input = 0, timeout occurred.所有的文件描述符都沒(méi)有得到輸入猎莲,超時(shí)退出返回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.

這個(gè)例子會(huì)在1秒以后超時(shí)退出绍弟,如果發(fā)生超時(shí),select返回0,請(qǐng)注意Timeout是根據(jù)select實(shí)際等待輸入的時(shí)間遞減的著洼,如果把timeout設(shè)為0樟遣,select函數(shù)會(huì)立刻退出而叼。

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兼容的操作系統(tǒng)上的串口編程

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

man termios 3介紹了所有termios結(jié)構(gòu)里的設(shè)置葵陵。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市屿衅,隨后出現(xiàn)的幾起案子埃难,更是在濱河造成了極大的恐慌莹弊,老刑警劉巖涤久,帶你破解...
    沈念sama閱讀 207,248評(píng)論 6 481
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場(chǎng)離奇詭異忍弛,居然都是意外死亡响迂,警方通過(guò)查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 88,681評(píng)論 2 381
  • 文/潘曉璐 我一進(jìn)店門细疚,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái)蔗彤,“玉大人,你說(shuō)我怎么就攤上這事疯兼∪欢簦” “怎么了?”我有些...
    開(kāi)封第一講書(shū)人閱讀 153,443評(píng)論 0 344
  • 文/不壞的土叔 我叫張陵吧彪,是天一觀的道長(zhǎng)待侵。 經(jīng)常有香客問(wèn)我,道長(zhǎng)姨裸,這世上最難降的妖魔是什么秧倾? 我笑而不...
    開(kāi)封第一講書(shū)人閱讀 55,475評(píng)論 1 279
  • 正文 為了忘掉前任,我火速辦了婚禮傀缩,結(jié)果婚禮上那先,老公的妹妹穿的比我還像新娘。我一直安慰自己赡艰,他們只是感情好售淡,可當(dāng)我...
    茶點(diǎn)故事閱讀 64,458評(píng)論 5 374
  • 文/花漫 我一把揭開(kāi)白布。 她就那樣靜靜地躺著慷垮,像睡著了一般勋又。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上换帜,一...
    開(kāi)封第一講書(shū)人閱讀 49,185評(píng)論 1 284
  • 那天楔壤,我揣著相機(jī)與錄音,去河邊找鬼惯驼。 笑死蹲嚣,一個(gè)胖子當(dāng)著我的面吹牛递瑰,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播隙畜,決...
    沈念sama閱讀 38,451評(píng)論 3 401
  • 文/蒼蘭香墨 我猛地睜開(kāi)眼抖部,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼!你這毒婦竟也來(lái)了议惰?” 一聲冷哼從身側(cè)響起慎颗,我...
    開(kāi)封第一講書(shū)人閱讀 37,112評(píng)論 0 261
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤,失蹤者是張志新(化名)和其女友劉穎言询,沒(méi)想到半個(gè)月后俯萎,有當(dāng)?shù)厝嗽跇?shù)林里發(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 43,609評(píng)論 1 300
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡运杭,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 36,083評(píng)論 2 325
  • 正文 我和宋清朗相戀三年夫啊,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片辆憔。...
    茶點(diǎn)故事閱讀 38,163評(píng)論 1 334
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡撇眯,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出虱咧,到底是詐尸還是另有隱情熊榛,我是刑警寧澤,帶...
    沈念sama閱讀 33,803評(píng)論 4 323
  • 正文 年R本政府宣布腕巡,位于F島的核電站玄坦,受9級(jí)特大地震影響,放射性物質(zhì)發(fā)生泄漏逸雹。R本人自食惡果不足惜营搅,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 39,357評(píng)論 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望梆砸。 院中可真熱鬧转质,春花似錦、人聲如沸帖世。這莊子的主人今日做“春日...
    開(kāi)封第一講書(shū)人閱讀 30,357評(píng)論 0 19
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)日矫。三九已至赂弓,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間哪轿,已是汗流浹背盈魁。 一陣腳步聲響...
    開(kāi)封第一講書(shū)人閱讀 31,590評(píng)論 1 261
  • 我被黑心中介騙來(lái)泰國(guó)打工, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留窃诉,地道東北人杨耙。 一個(gè)月前我還...
    沈念sama閱讀 45,636評(píng)論 2 355
  • 正文 我出身青樓赤套,卻偏偏與公主長(zhǎng)得像,于是被迫代替她去往敵國(guó)和親珊膜。 傳聞我的和親對(duì)象是個(gè)殘疾皇子容握,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 42,925評(píng)論 2 344

推薦閱讀更多精彩內(nèi)容