C语⾔实现串⼝通信
⽬录
前⾔
在学习4G拨号前,⾸先要了解清楚串⼝通信的⼯作原理。搞明⽩了之后,在现有⼀个Rx链接了Tx的⼀个串⼝上调试实现⾃收⾃发的串⼝通信程序。
⼀、要掌握的知识
1.串⼝通信
在上⼀篇博客中写了关于串⼝通信⽅⾯的的知识。总的来说,我们在Linux下编写串⼝通信程序时,必须对起始位、数据位、奇偶校验位、停⽌位、波特率进⾏初始化。特别是波特率,输⼊和输出要保持⼀致。
2.struct termios 结构体及相关函数
termios 函数族提供了⼀个常规的终端接⼝,⽤于控制⾮同步通信端⼝。简单来说就是通过这个结构体来对串⼝进⾏新的配置。
中级口译报名时间先来看⼀下struct termios结构体成员:
struct termios{
unsigned short c_iflag;/* 输⼊模式标志*/
unsigned short c_oflag;/* 输出模式标志*/
unsigned short c_cflag;/* 控制模式标志*/
unsigned short c_lflag;/*区域模式标志或本地模式标志或局部模式*/
unsigned char c_line;/*⾏控制line discipline */
unsigned char c_cc[NCC];/* 控制字符特性*/
};
2.1 c_iflag 输⼊模式标志
c_iflag:输⼊模式标志,控制终端输⼊⽅式,具体参数如表1所⽰。
fidler2.2 c_oflag 输出模式标志
c_oflag:输出模式标志,控制终端输出⽅式,具体参数如表2所⽰。
2.3 c_cflag 控制模式标志
c_cflag:控制模式标志,指定终端硬件控制信息,具体参数如表3所⽰
2.4 c_lflag 区域模式标志或本地模式标志或局部模式
c_lflag:本地模式标志,控制终端编辑功能,具体参数如表4所⽰。
2.4 c_cc[NCC] 控制字符特性
c_cc[NCCS]:控制字符,⽤于保存终端驱动程序中的特殊字符,如输⼊结束符等。c_cc中定义了如表5所⽰的控制字符。
2.5 tctattr() 与 tcgetattr()函数
2.5.1 tctattr()函数
是⽤于设置终端参数的函数。成功的时候返回0,失败的时候返回-1,并设置errno的值。
函数原型 :int tctattr(int fd, int optional_actions, conststruct termios *termios_p);the holiday
fd:打开的终端⽂件描述符;
optional_actions: 控制修改起作⽤的时间;
optional_actions可以取如下的值:
TCSANOW: 不等数据传输完毕就⽴即改变属性。
TCSADRAIN: 等待所有数据传输结束才改变属性。
TCSAFLUSH: 等待所有数据传输结束,清空输⼊输出缓冲区才改变属性。
termios_p: 保存了要修改的参数;
错误信息:
EBADF: ⾮法的⽂件描述符。
EINTR: tctattr函数调⽤被信号中断。
EINVAL: 参数optional_actions使⽤了⾮法值,或参数termios中使⽤了⾮法值。
始螈
ENOTTY: ⾮终端的⽂件描述符。
2.5.2 tcgetattr()函数
tcgetattr函数⽤于获取与终端相关的参数。返回的结果保存在termios 结构体中。气门座圈
函数原型:int tcgetattr(int fd, struct termios *termios_p);
fd:打开的终端⽂件描述符;
termios *termios_p: ⽤来保存从终端获取到的相关参数。
2.6 cftispeed() 与 cftospeed()
cftispeed() 与 cftospeed()函数分别⽤于设置输⼊和输出的波特率
函数原型
int cftispeed(struct termios *termios_p, speed_t speed); int
cftospeed(struct termios *termios_p, speed_t speed);
参数说明:
struct termios*termptr:指向termios结构的指针;
speed_t speed: 需要设置的输出波特率;
⽰例:pandas 是基于NumPy 的⼀种⼯具,该⼯具是为了解决数据分析任务⽽创建的。
⼆、绘制流程图和设计代码
对于串⼝模块来说,可以把程序分别封装为下⾯⼏个函数:
1、Linux下⼀切皆⽂件,所以⼜开就有关。设计两个函数⽤于串⼝的开关,在打开串⼝时候我们就因该对串⼝进⾏初始化所以设计如下两个函数⽤于串⼝的开关
1、int rial_open(rial_t *rial, char *devname, long baudrate, char *conf );
2、int rial_clo(rial_t *rial);
2、串⼝间要实现通信,就得有发和接收两个函数。
3、int rial_nd(rial_t *rial, char *sbuf, int sbuf_len);
胞嘧啶
4、int rial_recv(int fd, char *rbuf, int rbuf_len, int timeout);
由于串⼝是⼀个模块所以所有的函数应该封装为⼀个.c和⼀个.h⽂件⾥
rial.h
#ifndef _SERIAL_H
#define _SERIAL_H
#include<stdio.h>
#include<stdlib.h>
#include<fcntl.h>
#include<sys/types.h>
#include<sys/stat.h>
#include<string.h>
#include<errno.h>
#include<termios.h>
#include<unistd.h>
#define SERIALNAME_LEN 128
typedef struct rial_s{
int fd;//串⼝⽂件描述符
long Baudrate;//波特率
int Databits;//数据位
char Parity;//奇偶校验位
int Stopbits;//停⽌位
int mSend;//⼀次发送的最⼤数据
char SerialName[SERIALNAME_LEN];//端⼝名字
struct termios OldTermios;//保存原来的端⼝配置
becks
}rial_t;
int rial_open(rial_t *rial,char*devname,long baudrate,char*conf /*"8N1N"*/);
int rial_clo(rial_t *rial);
int rial_nd(rial_t *rial,char*sbuf,int sbuf_len);
int rial_recv(int fd,char*rbuf,int rbuf_len,int timeout);
#endif
1.串⼝的打开和关闭
1.1打开串⼝
rial_open()函数
int rial_open(rial_t *rial,char*devname,long baudrate,char*conf) {
int retval;
char Baudrate[32];
struct termios Newtermios;
memt(Baudrate,0,sizeof(baudrate));
memt(&Newtermios,0,sizeof(struct termios));
memt(&(rial->OldTermios),0,sizeof(struct termios));
if(strlen(rial->SerialName)==0|| devname ==NULL)
{
printf("open_rial Invalid parameter\n");英语英标
return-1;
}
if(devname !=NULL)
{
strncpy(rial->SerialName, devname, SERIALNAME_LEN);
}
el
{
return-2;
}
if(baudrate)
{
rial->Baudrate = baudrate;
}
el
{
rial->Baudrate =9600;
}
if(conf ==NULL)
{
rial->Databits =8;
rial->Parity ='N';
rial->Stopbits =1;
}
el
{
rial->Databits =atoi(&conf[0]);
rial->Parity = conf[1];
rial->Stopbits =atoi(&conf[2]);
上海自力
}
/*
*
* O_RDWR 读写
* O_NOCTTY 如果打开的⽂件为终端机设备时, 则不会将该终端机当成进程控制终端机.
* O_NONBLOCK 以不可阻断的⽅式打开⽂件, 也就是⽆论有⽆数据读取或等待, 都会⽴即返回进程之中. *
*/
rial->fd =open(rial->SerialName, O_RDWR | O_NOCTTY | O_NONBLOCK);
if(rial->fd <0)
{
printf("%s rail open failed: %s\n", rial->SerialName,strerror(errno));
return-3;
}
if((retval =fcntl(rial->fd,F_SETFL,0))<0)
{
gosprintf("%s,Fcntl check faile. \n", rial->SerialName);
return-4;
}
/* isattr(int fd) 检测设备fd是否是终端,返回1是,0就不是 */
if(isatty(rial->fd)==0)
{
printf("%s isn`t a device type\n", rial->SerialName);
return-5;
}
printf("%s is a device type \n", rial->SerialName);
if(rial ==NULL)
{
printf("Invalid parameter.\n");
return-6;
}
rial->mSend =128;
/*Serial port configuration backup*/
if(tcgetattr(rial->fd,&(rial->OldTermios))!=0)
{
printf("Serial port configuration backup errno\n");
return-7;
}
if(tcgetattr(rial->fd,&Newtermios))
{
printf("Get termios to Newtermios failure:%s\n",strerror(errno));
return-8;
}
/* 修改控制模式,保证程序不会占⽤串⼝ */
Newtermios.c_cflag |= CLOCAL;