這個話題,你們可能再熟悉不過了,網上資料不少,由於這是
linux
下編程比較重要的一個方面,懂這方面的人不少;這裏我只是想給初學者簡單的介紹下這方面的知識:
串口編程其實說白了,
是拿根串口線把電腦和所要控制的機器鏈接起來,而後在經過編程的方式對下位機進行發送指定的數據或進行控制,或進行傳輸,而後在接受下位機反饋回來的信息提示是否已經正確。是否是好俗!
串口是計算機上一種很是通用設備通訊的協議,經常使用
PC
機上包含的是
RS232
規格的串口,固然,除了
RS232
,還有
RS485
和
RS422
兩種規格,用於不一樣的設備通訊;這裏主要是介紹
RS232
串口編程。
在串口編程中,比較重要的是串口的設置,咱們要設置的部分包括波特率,數據位,中止位,奇偶校驗位;要注意的是,每臺機器的串口默認設置多是不一樣的,若是你沒設置這些,僅僅按照默認設置進行發送數據,極可能出現
n
多異想不到而又查不出來的狀況;因此,在真正通信前,咱們必須設置這些:
下面就開始介紹這些基本設置的函數,(其實都是些固定框架,程序中稍微改改就行)
~o~
1.
設置波特率
注意每臺機器都有輸出和輸入接受信息的速度
,因此用
cfsetispeed
和
cfsetospeed
來分別設置;注意到
struct termios
這樣一個結構,它包括了串口端全部的設置,下面還要用到。它在
termios.h
中被定義。。還有一個地方比較難以理解,爲何設置了
speed_arr
和
name_arr
兩個數組,這是由於在
linuxe
下,系統爲波特率專門準備了一張表用
B38400,B19200......
代替,而咱們實際上傳進去的只能是
38400,19200
這些值,因此咱們拿咱們傳進去的和
name_arr
進行比較,若是相等則從系統對照表中取出相應值進行設置,若是不等證實傳的值在系統對照表中沒有,則不進行設置。
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) //
判斷傳進來是否相等
{
tcflush(fd, TCIOFLUSH); //
刷新輸入輸出緩衝
cfsetispeed(&Opt, speed_arr); //
這裏分別設置
cfsetospeed(&Opt, speed_arr);
status = tcsetattr(fd, TCSANOW, &Opt); //
這是馬上把
bote rates
設置真正寫到串口中去
if (status != 0)
perror("tcsetattr fd1"); //
設置錯誤
return;
}
tcflush(fd,TCIOFLUSH); //
同上
}
}
2
。設置奇偶校驗,數據,中止位
這三個參數一般放在一塊兒設置,
databits
是數據位,
stopbits
是中止位,
parity
是校驗位。
串口的這些設置是很複雜很複雜的,
Termios
成員中共定義
c_cflag
控制項
c_lflag
線路項
c_iflag
輸入項
c_oflag
輸出項
c_cc
控制字符
c_ispeed
輸入波特
c_ospeed
輸出波特
那麼多項,對於每一項都有不少的設置,這裏咱們不講的那麼複雜,就一個通用的串口框架進行解釋,主要進行奇偶校驗,數據,中止位的設置。而其餘的一些控制項,在程序中用到時穿插講解:
int set_Parity(int fd,int databits,int stopbits,int parity)
{
struct termios options; //
定義一個結構
if ( tcgetattr( fd,&options) != 0) //
首先讀取系統默認設置
options
中
,
必須
{
perror("SetupSerial 1");
return(FALSE);
}
options.c_cflag &= ~CSIZE; //
這是設置
c_cflag
選項不按位數據位掩碼
switch (databits) /*
設置數據位數
*/
{
case 7:
options.c_cflag |= CS7; //
設置
c_cflag
選項數據位爲
7
位
break;
case 8:
options.c_cflag |= CS8; //
設置
c_cflag
選項數據位爲
8
位
break;
default:
fprintf(stderr,"Unsupported data size\n"); //
其餘的都不支持
return (FALSE);
}
switch (parity) //
設置奇偶校驗,
c_cflag
和
c_iflag
有效
{
case 'n':
case 'N': //
無校驗
固然都不選
options.c_cflag &= ~PARENB; /* Clear parity enable */
options.c_iflag &= ~INPCK; /* Enable parity checking */
break;
case 'o': //
奇校驗
其中
PARENB
校驗位有效;
PARODD
奇校驗
INPCK
檢查校驗
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;
default:
fprintf(stderr,"Unsupported parity\n");
return (FALSE);
}
/*
設置中止位
*/
switch (stopbits) //
這是設置中止位數,影響的標誌是
c_cflag
{
case 1:
options.c_cflag &= ~CSTOPB; //
不指明表示一位中止位
break;
case 2:
options.c_cflag |= CSTOPB; //
指明
CSTOPB
表示兩位,只有兩種可能
break;
default:
fprintf(stderr,"Unsupported stop bits\n");
return (FALSE);
}
/* Set input parity option */
if (parity != 'n') //
這是設置輸入是否進行校驗
options.c_iflag |= INPCK;
//
這個地方是用來設置控制字符和超時參數的,通常默認便可。稍微要注意的是
c_cc
數組的
VSTART
和
VSTOP
元素被設定成
DC1
和
DC3
,表明
ASCII
標準的
XON
和
XOFF
字符。因此若是在傳輸這兩個字符的時候就傳不過去,這時須要把軟件流控制屏蔽
options.c_iflag &= ~(IXON | IXOFF | IXANY);
options.c_cc[VTIME] = 150; // 15 seconds
options.c_cc[VMIN] = 0;
tcflush(fd,TCIFLUSH); /* Update the options and do it NOW */ //
刷新和馬上寫進去
if (tcsetattr(fd,TCSANOW,&options) != 0)
{
perror("SetupSerial 3");
return (FALSE);
}
return (TRUE);
}
//
串口設置框架到這裏就大概結束了,對於設置了數據位校驗位中止位和波特率的端口已經能夠傳輸大多數信息。在實際中的狀況每每是不少特例,好比,
在用
write
發送數據時沒有鍵入回車,信息就將發送不出去的狀況,這主要是由於咱們在輸出輸入時是按照
規範模式接受到回車或者換行才發送,而不少狀況咱們是不須要回車和換行的,這時,應當切換到行方式輸入,設置
options.c_lflag &= ~(ICANON | ECHO | ECHOE | ISIG);
不經處理直接發送。
又好比
在咱們發送字符
0x0d
的時候,每每接受端獲得的字符是
0x0a
這是怎麼回事,緣由是在串口設置中
c_iflag
和
c_oflag
中存在從
NL-CR
和
CR-NL
的映射,也就是說,串口能夠把回車和換行當作一個字符,因此,此時咱們應該屏蔽掉這些,用
options.c_oflag &=~(INLCR|IGNCR|ICRNL|);
和
options.c_oflag &=~(ONLCR|OCRNL);
進行設置。
總之,串口的設置是很複雜也很麻煩的東西,具體狀況要具體分析,找到相應的辦法,若是發現數據不能傳送,不妨耐點心在串口設置上找答案吧
言歸正傳,後面的東西就很簡單了,接下來是打開串口:
int OpenDev(char *Dev)
{
int fd = open( Dev, O_RDWR ); //| O_NOCTTY | O_NDELAY
這種方式看
open
函數
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/ttyS0"; //linux
下的端口就是經過打開設備文件操做的
fd = OpenDev(dev); //
打開
if (fd>0)
set_speed(fd,19200); //
打開後設置波特率
19200
else
{
printf("Can't Open Serial Port!\n");
exit(0);
}
if (set_Parity(fd,8,1,'N')== FALSE) //
設置
8
,
1
,
n
注意,這裏和上面要和下位機相符纔可能通訊
{
printf("Set Parity Error\n");
exit(1);
}
//
通常讀的時候通常都用
read
,寫的時候通常都用
write,read
要注意阻塞後程序中止不動,因此要用
select
進行控制,注意
tv
每次循環都要設置;
write
不用考慮阻塞,但要用循環寫方式保證必定寫完,其實讀最好也用循環讀方式保證必定能讀到全部東西而且能拼接在一塊兒,而後在進行其餘操做。最後
while (1)
是串口通信中經常使用的循環
就是一直執行,直到碰到
break;
這些東西挺煩瑣,不過其實也沒什麼。這裏就不詳細說了,下面是個最最簡單的。。
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);
}
完了,是否是不難,其實除了串口設置是新知識,,事實上
linux
都是文件,串口是設備文件,設置好後,其餘的東西就當成文件進行操做吧。