串口通信总体步骤:
- 打开串口
int m_nPortFD = open(m_sPortName, O_RDWR | O_NOCTTY);
参数1:串口名称
参数2:打开方式(可选)
返回值:成功返回文件描述符,失败返回-1
参数2必选其一
-
O_RDONLY只读模式
-
O_WRONLY只写模式
-
O_RDWR读写模式
参数2可选配置 -
O_APPEND 每次写操作都写入文件的末尾
-
O_CREAT 如果指定文件不存在,则创建这个文件
-
O_EXCL如 果要创建的文件已存在,则返回 -1,并且修改 errno 的值
-
O_TRUNC 如果文件存在,并且以只写/读写方式打开,则清空文件全部内容
-
O_NOCTTY 如果路径名指向终端设备,不要把这个设备用作控制终端。对于串口的打开操作,必须使用O_NOCTTY参数,它表示打开的是一个终端设备,程序不会成为该端口的控制终端。如果不使用此标志,任务的一个输入(比如键盘终止信号等)都会影响进程。
-
O_NONBLOCK 如果路径名指向 FIFO/块文件/字符文件,则把文件的打开和后继 I/O设置为非阻塞模式(nonblocking mode)
- 初始化串口
2.1 设置波特率
cfsetispeed(&m_PortOptions, nUtiBaudRate); // set up input baudrate
cfsetospeed(&m_PortOptions, nUtiBaudRate); // set up output baudrate
参数1:struct termios m_PortOptions; 参数2: 波特率
struct termios
{
tcflag_t c_iflag; 1.输入模式
tcflag_t c_oflag; 2.输出模式
tcflag_t c_cflag; 3.控制模式
tcflag_t c_lflag; 4.本地模式
cc_t c_cc[NCCS]; 5.特殊控制模式
};
你可以调用函数tcgetattr来初始化一个终端对应的termios结构,该函数的原型如下:
#include<termios.h>
int tcgetattr(int fd, struct termios *m_OldPortOptions);
这个函数调用把当前终端接口变量的值写入termios_p参数指向的结构。如果这些值其后被修改了,你可以通过调用函数tcsetattr来重新配置
终端接口。
#include<termios.h>
int tcsetattr(int fd , int actions , const struct termios *m_PortOptions);
参数actions控制修改方式,共有三种修改方式,如下所示。
1.TCSANOW: 立刻对值进行修改
2.TCSADRAIN:等当前的输出完成后再对值进行修改。
3.TCSAFLUSH:等当前的输出完成之后,再对值进行修改,但丢弃还未从read调用返回的当前的可用的任何输入。
bool CreatePort(const char *sPortName, int nBaudRate)
{
int nUtiBaudRate = 0;
switch(m_nBaudRate)
{
case 115200:
nUtiBaudRate = B115200;
break;
case 38400:
nUtiBaudRate = B38400;
break;
case 19200:
nUtiBaudRate = B19200;
break;
case 9600:
nUtiBaudRate = B9600;
break;
case 4800:
nUtiBaudRate = B4800;
break;
case 2400:
nUtiBaudRate = B2400;
break;
case 1200:
nUtiBaudRate = B1200;
break;
case 600:
nUtiBaudRate = B600;
break;
case 300:
nUtiBaudRate = B300;
break;
default:
printf("Unsupported baud rate\n");
return false;
}
m_nPortFD = open(m_sPortName, O_RDWR | O_NOCTTY);
if (-1 == m_nPortFD)
{
printf("Open failed\n");
perror(m_sPortName);
exit(-1);
}
// 保存以前的串口配置
if(tcgetattr(m_nPortFD, &m_OldPortOptions) != 0)
{
printf("Failed to get old default configuration");
perror("tcgetattr");
};
// 清空m_PortOptions
memset(&m_PortOptions, 0, sizeof(m_PortOptions));
// 配置前,清空输入输出缓冲区
tcflush(m_nPortFD, TCIOFLUSH);
// m_PortOptions = m_OldPortOptions;
m_PortOptions.c_cflag = nUtiBaudRate | CLOCAL | CREAD;
m_PortOptions.c_iflag = IGNPAR;
m_PortOptions.c_oflag = 0;
// 配置数据位
switch(m_nDataBits)
{
case 7:
m_PortOptions.c_cflag |= CS7;
break;
case 8:
m_PortOptions.c_cflag |= CS8;
break;
default:
m_PortOptions.c_cflag |= CS8;
}
//设置奇偶校验
switch(m_cParity)
{
case 'O'://odd
m_PortOptions.c_cflag |= PARENB;
m_PortOptions.c_cflag |= PARODD;
m_PortOptions.c_iflag |= (INPCK | ISTRIP);
break;
case 'E': //even
m_PortOptions.c_cflag |= PARENB;
m_PortOptions.c_cflag &= ~PARODD;
m_PortOptions.c_iflag |= (INPCK | ISTRIP);
break;
case 'N': //no parity
m_PortOptions.c_cflag &= ~PARENB;
break;
default:
m_PortOptions.c_cflag &= ~PARENB;
}
// 设置输入速率
cfsetispeed(&m_PortOptions, nUtiBaudRate);
// 设置输出速率
cfsetospeed(&m_PortOptions, nUtiBaudRate);
// 设置停止位
switch(m_nStopBits)
{
case 1:
m_PortOptions.c_cflag &= ~CSTOPB;
break;
case 2:
m_PortOptions.c_cflag |= CSTOPB;
break;
default:
m_PortOptions.c_cflag &= ~CSTOPB;
}
// additional configuration
// set input mode (non-canonical, no echo,...)
m_PortOptions.c_lflag = 0;
// configure VTIME VMIN
m_PortOptions.c_cc[VTIME] = 0;
m_PortOptions.c_cc[VMIN] = 1;
//read一直等待,直到有MIN个字符可以读取,返回值是字符的数量.到达文件尾时返回0
// /*处理未接收字符*/
tcflush(m_nPortFD, TCIFLUSH);
/*激活新配置*/
if(tcsetattr(m_nPortFD, TCSANOW, &m_PortOptions) != 0) // 立即生效
{
printf("Failed to enable the configuration for serial port\n");
perror("tcsetattr");
return false;
}
// 配置使能后 也清空输入和输出缓冲区
tcflush(m_nPortFD, TCIOFLUSH);
return m_nPortFD>-1;
}