前言
在学习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所示。
2.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 tcsetattr() 与 tcgetattr()函数
2.5.1 tcsetattr()函数
是用于设置终端参数的函数。成功的时候返回0,失败的时候返回-1,并设置errno的值。
函数原型 :int tcsetattr(int fd, int optional_actions, conststruct termios *termios_p);
fd:打开的终端文件描述符;
optional_actions: 控制修改起作用的时间;
optional_actions可以取如下的值:
TCSANOW: 不等数据传输完毕就立即改变属性。
TCSADRAIN: 等待所有数据传输结束才改变属性。
TCSAFLUSH: 等待所有数据传输结束,清空输入输出缓冲区才改变属性。
termios_p: 保存了要修改的参数;
错误信息:
EBADF: 非法的文件描述符。
EINTR: tcsetattr函数调用被信号中断。
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 cfsetispeed() 与 cfsetospeed()
cfsetispeed() 与 cfsetospeed()函数分别用于设置输入和输出的波特率
函数原型
int cfsetispeed(struct termios *termios_p, speed_t speed); int
cfsetospeed(struct termios *termios_p, speed_t speed);
参数说明:
struct termios*termptr:指向termios结构的指针;
speed_t speed: 需要设置的输出波特率;
示例:pandas 是基于NumPy 的一种工具,该工具是为了解决数据分析任务而创建的。
二、绘制流程图和设计代码
对于串口模块来说,可以把程序分别封装为下面几个函数:
1、Linux下一切皆文件,所以又开就有关。设计两个函数用于串口的开关,在打开串口时候我们就因该对串口进行初始化所以设计如下两个函数用于串口的开关
1、int serial_open(serial_t *serial, char *devname, long baudrate, char *conf );
2、int serial_close(serial_t *serial);
2、串口间要实现通信,就得有发和接收两个函数。
3、int serial_send(serial_t *serial, char *sbuf, int sbuf_len);
4、int serial_recv(int fd, char *rbuf, int rbuf_len, int timeout);
由于串口是一个模块所以所有的函数应该封装为一个.c和一个.h文件里
serial.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 serial_s{
int fd; //串口文件描述符
long Baudrate; //波特率
int Databits; //数据位
char Parity; //奇偶校验位
int Stopbits; //停止位
int mSend; //一次发送的最大数据
char SerialName[SERIALNAME_LEN]; //端口名字
struct termios OldTermios; //保存原来的端口配置
}serial_t;
int serial_open(serial_t *serial, char *devname, long baudrate, char *conf /*"8N1N"*/);
int serial_close(serial_t *serial);
int serial_send(serial_t *serial, char *sbuf, int sbuf_len);
int serial_recv(int fd, char *rbuf, int rbuf_len, int timeout);
#endif
1.串口的打开和关闭
1.1打开串口
serial_open()函数
int serial_open(serial_t *serial, char *devname, long baudrate, char *conf)
{
int retval;
char Baudrate[32];
struct termios Newtermios;
memset