串口操作
串口操作需要的頭文件
#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)
級(jí)別: 初級(jí)
左錦(zuo170@163.com),副總裁,南沙資訊科技園
2003年7月03日
Linux操作系統(tǒng)從一開(kāi)始就對(duì)串行口提供了很好的支持,本文就Linux下的串行口通訊編程進(jì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)振鈴
串口操作需要的頭文件
#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)定義*/
在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ò)誤冠王!");
}
最基本的設(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*/
設(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);
關(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)鞭衩。我測(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è)置葵陵。