linux下串口通信程序,关于Linux下串口通信的一点心得
1.
打開串口
與其他的關于設備編程的方法一樣,在
Linux
下,操作、控制串口也是通過操作起設備文件進行的。在
Linux
下,串口的設備文件是
/dev/ttyS0
或
/dev/ttyS1
等。因此要讀些串口,我們首先要打開串口:
char *dev= "/dev/ttyS0"; //
串口
1
intfd = open( dev, O_RDWR );
//| O_NOCTTY | O_NDELAY
if (-1 == fd)
{
perror("Can't Open Serial Port");
return -1;
}
else
return fd;
2.
設置串口速度
打開串口成功后,我們就可以對其進行讀寫了。首先要設置串口的波特率:
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(fd, TCSANOW, &Opt);
if(status != 0) {
perror("tcsetattr fd");
return;
}
tcflush(fd,TCIOFLUSH);
}
}
}
3.
設置串口信息
這主要包括:數據位、停止位、奇偶校驗位這些主要的信息。
/**
*@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;
options.c_lflag&= ~(ICANON | ECHO | ECHOE | ISIG);/*Input*/
options.c_oflag&= ~OPOST;/*Output*/
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] = 0; /*
設置超時
0 seconds*/
options.c_cc[VMIN] = 13; /* define the minimum bytes data to be readed*/
if (tcsetattr(fd,TCSANOW,&options) != 0)
{
perror("SetupSerial 3");
return (FALSE);
}
return (TRUE);
}
在上述代碼中,有兩句話特別重要:
options.c_cc[VTIME] = 0; /*
設置超時
0 seconds*/
options.c_cc[VMIN] = 13; /* define the minimum bytes data to be readed*/
這兩句話決定了對串口讀取的函數
read()
的一些功能。我將著重介紹一下他們對
read()
函數的影響。
對串口操作的結構體是
Struct{
tcflag_t c_iflag;/*
輸入模式標記
*/
tcflag_t c_oflag;/*
輸出模式標記
*/
tcflag_t c_cflag;/*
控制模式標記
*/
tcflag_t c_lflag;/*
本地模式標記
*/
cc_tc_line;/*
線路規程
*/
cc_tc_cc[NCCS];/*
控制符號
*/
}
;
其中
cc_tc_line
只有在一些特殊的系統程序
(
比如,設置通過
tty
設備來通信的網絡協議
)
中才會用。在數組
c_cc
中有兩個下標
(VTIME
和
VMIN)
對應的元素不是控制符,并且只是在原始模式下有效。只有在原始模式下,他們決定了
read()
函數在什么時候返回。在標準模式下,除非設置了
O_NONBLOCK
選項,否則只有當遇到文件結束符或各含的字符都已經編輯完畢后才返回。
控制符
VTIME
和
VMIN
之間有著復雜的關系。
VTIME
定義要求等待的零到幾百號妙的是間量
(
通常是一個
8
位的
unsigned char
變量,取值不能大于
cc_t)
。
VMIN
定義了要求等待的最小字節數
(
不是要求讀的字節數——
read()
的第三個參數才是指定要求讀的最大字節數
)
,這個字節數可能是
0
。
l
如果
VTIME
取
0
,
VMIN
定義了要求等待讀取的最小字節數。函數
read()
只有在讀取了
VMIN
個字節的數據或者收到一個信號的時候才返回。
l
如果
VMIN
取
0
,
VTIME
定義了即使沒有數據可以讀取,
read()
函數返回前也要等待幾百毫秒的時間量。這時,
read()
函數不需要像其通常情況那樣要遇到一個文件結束標志才返回
0
。
l
如果
VTIME
和
VMIN
等不取
0
,
VTIME
定義的時當接收到底一個自己的數據后開始計算等待的時間量。如果當調用
read
函數時可以得到數據,計時器馬上開始計時。如果但調用
read
函數時還沒有任何數據可讀,則等接收到底一個字節的數據后,計時器開始計時。函數
read
可能會在讀取到
VMIN
個字節的數據后返回,也可能在計時完畢后返回,這主要取決于哪個條件首先實現。不過函數至少會讀取到一個字節的數據,因為計時器是在讀取到第一個數據時開始計時的。
l
如果
VTIME
和
VMIN
都取
0
,即使讀取不到任何數據,函數
read
也會立即返回。同時,返回值
0
表示
read
函數不需要等待文件結束標志就返回了。
這就是這兩個變量對
read
函數的影響。我使用的讀卡器每次傳送的數據是
13
個字節,一開始,我把它們設置成
options.c_cc[VTIME] = 150
options.c_cc[VMIN] = 0;
結果,每次讀取的信息只有
8
個字節,剩下的
5
個字節要等到下一次打卡時才能收到。就是由于這個原因造成的。根據上面規則的第一條,我把
VTIME
取
0
,
VMIN=13
,也就是正好等于一次需要接收的字節數。這樣就實現了一次讀取
13
個字節值。同時,得出這樣的結論,如果讀卡器送出的數據為
n
個字節,那么就把
VMIN=n
,這樣一次讀取的信息正好為讀卡器送出的信息,并且讀取的時候不需要進行循環讀取。
4.
讀取數據
有了上面的函數后,我們設置了串口的基本信息,根據我們自己的實際情況,設置了相應的參數,就可以讀取數據了。
void getcardinfo(char *buff){
int fd;
int nread,count=0;
char tempbuff[13];
char *dev= "/dev/ttyS0"; //
串口
1
fd = OpenDev(dev);
set_speed(fd,9600);
if (set_Parity(fd,8,1,'N') == FALSE){
printf("Set Parity Error\n");
//return -1;
}
while (1) //
循環讀取數據
{
count=0;
//sleep(5000);
while(1)
{
if((nread = read(fd, tempbuff, 13))>0)
{
//printf("\nLen %d\n",nread);
memcpy(&buff[count],tempbuff,nread);
count+=nread;
}
if(count==13)
{
buff[count+1] = '\0';
//printf( "\n%s", buff);
break;
}
}
//break;
}
//return buff;
close(fd);
pthread_exit(NULL);
//close(fd);
// exit (0);
}
這是我原來的程序,其實把
VMIN
設置以后,可以改成:
void getcardinfo(char *buff){
int fd;
int nread,count=0;
char tempbuff[13];
char *dev= "/dev/ttyS0"; //
串口
1
fd = OpenDev(dev);
set_speed(fd,9600);
if (set_Parity(fd,8,1,'N') == FALSE){
printf("Set Parity Error\n");
//return -1;
}
nread = read(fd, buff, 13)
close(fd);
}
5.
程序完整代碼:
#include/*
標準輸入輸出定義
*/
#include/*
標準函數庫定義
*/
#include/*Unix
標準函數定義
*/
#include
#include
#include/*
文件控制定義
*/
#include/*PPSIX
終端控制定義
*/
#include/*
錯誤號定義
*/
#define FALSE-1
#define TRUE0
/**
*@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(fd, TCSANOW, &Opt);
if(status != 0) {
perror("tcsetattr fd");
return;
}
tcflush(fd,TCIOFLUSH);
}
}
}
/**
*@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;
options.c_lflag&= ~(ICANON | ECHO | ECHOE | ISIG);/*Input*/
options.c_oflag&= ~OPOST;/*Output*/
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] = 0; /*
設置超時
15 seconds*/
options.c_cc[VMIN] = 13; /* define the minimum bytes data to be readed*/
if (tcsetattr(fd,TCSANOW,&options) != 0)
{
perror("SetupSerial 3");
return (FALSE);
}
return (TRUE);
}
/**********************************************************************
代碼說明:使用串口一測試的,發送的數據是字符,
但是沒有發送字符串結束符號,所以接收到后,后面加上了結束符號
**********************************************************************/
/*********************************************************************/
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;
}
void getcardinfo(char *buff){
int fd;
int nread,count=0;
char tempbuff[13];
char *dev= "/dev/ttyS0"; //
串口
1
fd = OpenDev(dev);
set_speed(fd,9600);
if (set_Parity(fd,8,1,'N') == FALSE){
printf("Set Parity Error\n");
//return -1;
}
while (1) //
循環讀取數據
{
count=0;
//sleep(5000);
while(1)
{
if((nread = read(fd, tempbuff, 13))>0)
{
//printf("\nLen %d\n",nread);
memcpy(&buff[count],tempbuff,nread);
count+=nread;
}
if(count==13)
{
buff[count+1] = '\0';
//printf( "\n%s", buff);
break;
}
}
//break;
}
//return buff;
close(fd);
pthread_exit(NULL);
//close(fd);
// exit (0);
}
總結
以上是生活随笔為你收集整理的linux下串口通信程序,关于Linux下串口通信的一点心得的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: linux怎么编译并安装busybox,
- 下一篇: linux分区知识,Linux硬盘分区知