Linux串口编程.docx
《Linux串口编程.docx》由会员分享,可在线阅读,更多相关《Linux串口编程.docx(13页珍藏版)》请在冰豆网上搜索。
![Linux串口编程.docx](https://file1.bdocx.com/fileroot1/2023-2/6/b243c22c-d45d-4f00-8ed4-4cb1a99cc1ee/b243c22c-d45d-4f00-8ed4-4cb1a99cc1ee1.gif)
Linux串口编程
Linux串口编程
1.串口操作需要的头文件
#include //标准输入输出定义
#include //标准函数库定义
#include //Unix标准函数定义
#include
#include
#include //文件控制定义
#include //POSIX中断控制定义
#include //错误号定义
2.打开串口
串口位于/dev中,可作为标准文件的形式打开,其中:
串口1 /dev/ttyS0
串口2 /dev/ttyS1
代码如下:
int fd;
fd = open(“/dev/ttyS0”, O_RDWR);
if(fd == -1)
{
Perror(“串口1打开失败!
”);
}
//else
//fcntl(fd, F_SETFL, FNDELAY);
除了使用O_RDWR标志之外,通常还会使用O_NOCTTY和O_NDELAY这两个标志。
O_NOCTTY:
告诉Unix这个程序不想成为“控制终端”控制的程序,不说明这个标志的话,任何输入都会影响你的程序。
O_NDELAY:
告诉Unix这个程序不关心DCD信号线状态,即其他端口是否运行,不说明这个标志的话,该程序就会在DCD信号线为低电平时停止。
3.设置波特率
最基本的串口设置包括波特率、校验位和停止位设置,且串口设置主要使用termios.h头文件中定义的termios结构,如下:
structtermios
{
tcflag_t c_iflag; //输入模式标志
tcflag_t c_oflag; //输出模式标志
tcflag_t c_cflag; //控制模式标志
tcflag_t c_lflag; //本地模式标志
cc_t c_line; //linediscipline
cc_t c_cc[NCC]; //controlcharacters
}
代码如下:
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 SetSpeed(int fd, int speed)
{
int i;
struct termios Opt; //定义termios结构
if(tcgetattr(fd, &Opt) !
= 0)
{
perror(“tcgetattr fd”);
return;
}
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]);
if(tcsetattr(fd, TCSANOW, &Opt) !
= 0)
{
perror(“tcsetattr fd”);
return;
}
tcflush(fd, TCIOFLUSH);
}
}
}
注意tcsetattr函数中使用的标志:
TCSANOW:
立即执行而不等待数据发送或者接受完成。
TCSADRAIN:
等待所有数据传递完成后执行。
TCSAFLUSH:
Flushinputandoutputbuffersandmakethechange
4.设置数据位、停止位和校验位
以下是几个数据位、停止位和校验位的设置方法:
(以下均为1位停止位)
8位数据位、无校验位:
Opt.c_cflag&=~PARENB;
Opt.c_cflag&=~CSTOPB;
Opt.c_cflag&=~CSIZE;
Opt.c_cflag|=CS8;
7位数据位、奇校验:
Opt.c_cflag|=PARENB;
Opt.c_cflag|=PARODD;
Opt.c_cflag&=~CSTOPB;
Opt.c_cflag&=~CSIZE;
Opt.c_cflag|=CS7;
7位数据位、偶校验:
Opt.c_cflag|=PARENB;
Opt.c_cflag&=~PARODD;
Opt.c_cflag&=~CSTOPB;
Opt.c_cflag&=~CSIZE;
Opt.c_cflag|=CS7;
7位数据位、Space校验:
Opt.c_cflag&=~PARENB;
Opt.c_cflag&=~CSTOPB;
Opt.c_cflag&=~CSIZE;
Opt.c_cflag|=CS7;
代码如下:
int SetParity(int fd, int databits, int stopbits, int parity)
{
struct termios Opt;
if(tcgetattr(fd, &Opt) !
= 0)
{
perror("tcgetattr fd");
return FALSE;
}
Opt.c_cflag |= (CLOCAL | CREAD); //一般必设置的标志
switch(databits) //设置数据位数
{
case 7:
Opt.c_cflag &= ~CSIZE;
Opt.c_cflag |= CS7;
break;
case 8:
Opt.c_cflag &= ~CSIZE;
Opt.c_cflag |= CS8;
berak;
default:
fprintf(stderr, "Unsupported data size.\n");
return FALSE;
}
switch(parity) //设置校验位
{
case 'n':
case 'N':
Opt.c_cflag &= ~PARENB; //清除校验位
Opt.c_iflag &= ~INPCK; //enable parity checking
break;
case 'o':
case 'O':
Opt.c_cflag |= PARENB; //enable parity
Opt.c_cflag |= PARODD; //奇校验
Opt.c_iflag |= INPCK //disable parity checking
break;
case 'e':
case 'E':
Opt.c_cflag |= PARENB; //enable parity
Opt.c_cflag &= ~PARODD; //偶校验
Opt.c_iflag |= INPCK; //disable pairty checking
break;
case 's':
case 'S':
Opt.c_cflag &= ~PARENB; //清除校验位
Opt.c_cflag &= ~CSTOPB; //?
?
?
?
?
?
?
?
?
?
?
?
?
?
Opt.c_iflag |= INPCK; //disable pairty checking
break;
default:
fprintf(stderr, "Unsupported parity.\n");
return FALSE;
}
switch(stopbits) //设置停止位
{
case 1:
Opt.c_cflag &= ~CSTOPB;
break;
case 2:
Opt.c_cflag |= CSTOPB;
break;
default:
fprintf(stderr, "Unsupported stopbits.\n");
return FALSE;
}
opt.c_cflag |= (CLOCAL | CREAD);
opt.c_lflag &= ~(ICANON | ECHO | ECHOE | ISIG);
opt.c_oflag &= ~OPOST;
opt.c_oflag &= ~(ONLCR | OCRNL); //添加的
opt.c_iflag &= ~(ICRNL | INLCR);
opt.c_iflag &= ~(IXON | IXOFF | IXANY); //添加的
tcflush(fd, TCIFLUSH);
Opt.c_cc[VTIME] = 0; //设置超时为15sec
Opt.c_cc[VMIN] = 0; //Update the Opt and do it now
if(tcsetattr(fd, TCSANOW, &Opt) !
= 0)
{
perror("tcsetattr fd");
return FALSE;
}
return TRUE;
}
5.某些设置项
在第四步中我们看到一些比较特殊的设置,下面简述一下他们的作用。
c_cc数组的VSTART和VSTOP元素被设定成DC1和DC3,代表ASCII标准的XON和XOFF字符,如果在传输这两个字符的时候就传不过去,需要把软件流控制屏蔽,即:
Opt.c_iflag&=~(IXON|IXOFF|IXANY);
有时候,在用write发送数据时没有键入回车,信息就发送不出去,这主要是因为我们在输入输出时是按照规范模式接收到回车或换行才发送,而更多情况下我们是不必键入回车或换行的。
此时应转换到行方式输入,不经处理直接发送,设置如下:
Opt.c_lflag&=~(ICANON|ECHO|ECHOE|ISIG);
还存在这样的情况:
发送字符0X0d的时候,往往接收端得到的字符是0X0a,原因是因为在串口设置中c_iflag和c_oflag中存在从NL-CR和CR-NL的映射,即串口能把回车和换行当成同一个字符,可以进行如下设置屏蔽之:
Opt.c_iflag&=~(INLCR|ICRNL|IGNCR);
Opt.c_oflag&=~(ONLCR|OCRNL);
6.读写串口
发送数据方式如下,write函数将返回写的位数或者当错误时为-1。
charbuffer[1024];
intlength;
intnByte;
nByte=write(fd,buffer,length);
读取数据方式如下,原始数据模式下每个read函数将返回实际串口收到的字符数,如果串口中没有字符可用,回叫将会阻塞直到以下几种情况:
有字符进入;一个间隔计时器失效;错误发送。
在打开串口成功后,使用fcntl(fd,F_SETFL,FNDELAY)语句,可以使read函数立即返回而不阻塞。
FNDELAY选项使read函数在串口无字符时立即返回且为0。
charbuffer[1024];
intlength;
intreadByte;
readByte=read(fd,buffer,len);
注意:
设置为原始模式传输数据的话,read函数返回的字符数是实际串口收到的字符数。
Linux下直接用read读串口可能会造成堵塞,或者数据读出错误,此时可使用tcntl或者select等函数实现异步读取。
用select先查询com口,再用read去读就可以避免上述错误。
7.关闭串口
串口作为文件来处理,所以一般的关闭文件函数即可:
close(fd);
8.例子
这个例子中,需要打开串口1,设置9600波特率、8位数据位、1位停止位以及空校验,之后利用while语句循环判断串口中是否可以读出数据,将串口中数据连续读出后重新写回到串口中。
该程序可与minicom联合测试。
#include
#include
#include
#include
#include
#include
#include
#include
main()
{
int fd;
int i;
int len;
int n = 0;
char read_buf[256];
char write_buf[256];
struct termios opt;
fd = open("/dev/ttyS0", O_RDWR | O_NOCTTY); //默认为阻塞读方式
if(fd == -1)
{
perror("open serial 0\n");
exit(0);
}
tcgetattr(fd, &opt);
cfsetispeed(&opt, B9600);
cfsetospeed(&opt, B9600);
if(tcsetattr(fd, TCSANOW, &opt) !
= 0 )
{
perror("tcsetattr error");
return -1;
}
opt.c_cflag &= ~CSIZE;
opt.c_cflag |= CS8;
opt.c_cflag &= ~CSTOPB;
opt.c_cflag &= ~PARENB;
opt.c_cflag &= ~INPCK;
opt.c_cflag |= (CLOCAL | CREAD);
opt.c_lflag &= ~(ICANON | ECHO | ECHOE | ISIG);
opt.c_oflag &= ~OPOST;
opt.c_oflag &= ~(ONLCR | OCRNL); //添加的
opt.c_iflag &= ~(ICRNL | INLCR);
opt.c_iflag &= ~(IXON | IXOFF | IXANY); //添加的
opt.c_cc[VTIME] = 0;
opt.c_cc[VMIN] = 0;
tcflush(fd, TCIOFLUSH);
printf("configure complete\n");
if(tcsetattr(fd, TCSANOW, &opt) !
= 0)
{
perror("serial error");
return -1;
}
printf("start send and receive data\n");
while
(1)
{
n = 0;
len = 0;
bzero(read_buf, sizeof(read_buf)); //类似于memset
bzero(write_buf, sizeof(write_buf));
while( (n = read(fd, read_buf, sizeof(read_buf))) > 0 )
{
for(i = len; i < (len + n); i++)
{
write_buf[i] = read_buf[i - len];
}
len += n;
}
write_buf[len] = '\0';
printf("Len %d \n", len);
printf("%s \n", write_buf);
n = write(fd, write_buf, len);
printf("write %d chars\n",n);
sleep
(2);
}
}
9.附录
c_cflag用于设置控制参数,除了波特率外还包含以下内容:
EXTA Externalrateclock
EXTB Externalrateclock
CSIZE Bitmaskfordatabits
CS5 5个数据位
CS6 6个数据位
CS7 7个数据位
CS8 8个数据位
CSTOPB 2个停止位(清除该标志表示1个停止位
PARENB 允许校验位
PARODD 使用奇校验(清除该标志表示使用偶校验)
CREAD Enablereceiver
HUPCL Hangup(dropDTR)onlastclose
CLOCAL Localline–donotchange“owner”ofport
LOBLK Blockjobcontroloutpu
c_cflag标志可以定义CLOCAL和CREAD,这将确保该程序不被其他端口控制和信号干扰,同时串口驱动将读取进入的数据。
CLOCAL和CREAD通常总是被是能的。
c_lflag用于设置本地模式,决定串口驱动如何处理输入字符,设置内容如下:
ISIG EnableSIGINTR,SIGSUSP,SIGDSUSP,andSIGQUITsignals
ICANON Enablecanonicalinput(elseraw)
XCASE Mapuppercase\lowercase(obsolete)
ECHO Enableechoingofinputcharacters
ECHOE EchoerasecharacterasBS-SP-BS
ECHOK EchoNLafterkillcharacter
ECHONL EchoNL
NOFLSH Disableflushingofinputbuffersafterinterruptorquitcharacters
IEXTEN Enableextendedfunctions
ECHOCTL Echocontrolcharactersas^charanddeleteas~?
ECHOPRT Echoerasedcharacterascharactererased
ECHOKE BS-SP-BSentirelineonlinekill
FLUSHO Outputbeingflushed
PENDIN Retypependinginputatnextreadorinputchar
TOSTOP SendSIGTTOUforbackgroundoutput
c_iflag用于设置如何处理串口上接收到的数据,包含如下内容:
INPCK Enableparitycheck
IGNPAR Ignoreparityerrors
PARMRK Markparityerrors
ISTRIP Stripparitybits
IXON Enablesoftwareflowcontrol(outgoing)
IXOFF Enablesoftwareflowcontrol(incoming)
IXANY Allowanycharacter