Linux的串口读写详解

文章目录

  • 一、前言
  • 二、运行环境
  • 三、串口相关的数据结构和函数
    • 1、需要配置的结构体
    • 2、系统相关函数
      • (1)tcgetattr
      • (2)tcsetattr
      • (3)tcflush
  • 三、串口属性的配置
    • 1、波特率配置
    • 2、奇偶校验配置
    • 3、数据位配置
    • 4、停止位配置
    • 5、硬件流控配置
    • 6、软件流控配置
    • 7、等待时间配置
    • 8、使能配置

一、前言

【第一周】:现在是2019年12月,现大三。希望借这个平台和正在阅读文章的你一起学习和提升。本着分享和记录的目的,我将不定期更新文章,如果文章中有不足之处,还请各位多多指教,如果需要源代码,欢迎在评论区留言邮箱。

本文为《搭建物联网基础通信框架系列教程》的分支部分,读写Linux的串口数据。

二、运行环境

Ubuntu16.04

三、串口相关的数据结构和函数

1、需要配置的结构体

struct termios
  {
    tcflag_t c_iflag;  /* input mode flags */
    tcflag_t c_oflag;  /* output mode flags */
    tcflag_t c_cflag;  /* control mode flags */
    tcflag_t c_lflag;  /* local mode flags */
    cc_t c_line;   /* line discipline */
    cc_t c_cc[NCCS];  /* control characters */
    speed_t c_ispeed;  /* input speed */
    speed_t c_ospeed;  /* output speed */
  };

变量的含义前辈们已经注释得很清楚了,在这我就不做解释了。

2、系统相关函数

(1)tcgetattr

tcgetattr 函数用于获取串口的配置信息。

/* Put the state of FD into *TERMIOS_P.  */
extern int tcgetattr (int __fd, struct termios *__termios_p) __THROW;

参数1:串口设备文件的文件描述符fd。
参数2:上述结构体指针
返回值:
成功返回0.
失败返回-1,并设置错误码。

(2)tcsetattr

tcsetattr 函数用于设置串口的属性。

/* Set the state of FD to *TERMIOS_P.
   Values for OPTIONAL_ACTIONS (TCSA*) are in .  */
extern int tcsetattr (int __fd, int __optional_actions,
        const struct termios *__termios_p) __THROW;

参数1:串口设备文件的文件描述符fd。
参数2:用于控制函数的执行属性,可取如下值

/* tcsetattr uses these */
#define TCSANOW  0
#define TCSADRAIN 1
#define TCSAFLUSH 2

TCSANOW:不等数据传输完毕就立即改变属性。
TCSADRAIN:等待所有数据传输结束才改变属性。
TCSAFLUSH:清空输入输出缓冲区才改变属性。
返回值:
成功返回0.
失败返回-1,并设置错误码。

(3)tcflush

tcflush 用于清空串口缓冲区数据

/* Flush pending data on FD.
   Values for QUEUE_SELECTOR (TC{I,O,IO}FLUSH) are in .  */
extern int tcflush (int __fd, int __queue_selector) __THROW;

参数1:串口设备文件的文件描述符fd。
参数1:用于控制函数的执行属性,可取如下值

/* tcflush() and TCFLSH use these */
#define TCIFLUSH 0
#define TCOFLUSH 1
#define TCIOFLUSH 2

TCIFLUSH:清除正收到的数据,且不会读取出来。
TCOFLUSH :清除正写入的数据,且不会发送至终端。
TCIOFLUSH:清除所有正在发生的I/O数据。
返回值:
成功返回0.
失败返回-1,并设置错误码。

三、串口属性的配置

1、波特率配置

因为波特率可取的值很多,如果逐个判断代码冗余太大,这里我采用两个数组存放可取的值。

//波特率和宏定义对应表 
static const speed_t baudRateTable_Def[] = { B0,B50,B75,B110,B134,B150,B200,B600,B1200,B1800,B2400,B4800,B9600,B19200,B38400,B57600,B115200 };
static const int baudRateTable[] = { 0,50,75,110,134,150,200,600,1200,1800,2400,4800,9600,19200,38400,57600,115200 };

系统宏定义

/* c_cflag bit meaning */
#ifdef __USE_MISC
# define CBAUD 0010017
#endif
#define  B0 0000000  /* hang up */
#define  B50 0000001
#define  B75 0000002
#define  B110 0000003
#define  B134 0000004
#define  B150 0000005
#define  B200 0000006
#define  B300 0000007
#define  B600 0000010
#define  B1200 0000011
#define  B1800 0000012
#define  B2400 0000013
#define  B4800 0000014
#define  B9600 0000015
#define  B19200 0000016
#define  B38400 0000017
bool CSerial::setBaudeRate(int baudeRate)
{
 int i = 0;
 m_iBaudRate = baudeRate;
 for (i = 0; i < 17; i++){
  if (baudeRate == baudRateTable[i]){
   cfsetispeed(&m_Termios, baudRateTable_Def[i]); //接收数据波特率
   cfsetospeed(&m_Termios, baudRateTable_Def[i]); //发送数据波特率
   m_uiError &= ~ERROR_CFG_BaudeRate;
   return true;
  }
 }
 cout << "Invalid BaudeRate!" << endl;
 m_uiError |= ERROR_CFG_BaudeRate;  //波特率配置出错置位
 return false;  //设置的波特率不是常用的标准值
}

2、奇偶校验配置

系统宏定义

#ifdef __USE_MISC
# define CIBAUD   002003600000  /* input baud rate (not used) */
# define CMSPAR   010000000000  /* mark or space (stick) parity */
# define CRTSCTS  020000000000  /* flow control */
#endif
bool CSerial::setFlowCtrl_hw(bool bFlowCtrl_hw)
{
 m_bFlowCtrl_hw = bFlowCtrl_hw;
 if (bFlowCtrl_hw){
  m_Termios.c_cflag |= CRTSCTS; //硬件流控
 }
 else{
  m_Termios.c_cflag &= ~CRTSCTS;
 }
 return true;
}

3、数据位配置

系统宏定义

#define CSIZE 0000060
#define   CS5 0000000
#define   CS6 0000020
#define   CS7 0000040
#define   CS8 0000060
#define CSTOPB 0000100
#define CREAD 0000200
#define PARENB 0000400
#define PARODD 0001000
#define HUPCL 0002000
#define CLOCAL 0004000
bool CSerial::setDataBits(int dataBits)
{
 m_iDataBits = dataBits;
 m_Termios.c_cflag &= ~CSIZE; //在设置数据位时,必须先使用CSIZE做位屏蔽
 switch (dataBits)
 {
 case 5:
  m_Termios.c_cflag |= CS5;
  break;
 case 6:
  m_Termios.c_cflag |= CS6;
  break;
 case 7:
  m_Termios.c_cflag |= CS7;
  break;
 case 8:
  m_Termios.c_cflag |= CS8;
  break;
 default:
  cout << "Invalid DataBits!" << endl;
  m_uiError |= ERROR_CFG_DataBits;  //数据位配置出错置位
  return false;
  break;
 } 
 m_uiError &= ~ERROR_CFG_DataBits;
 return true;
}

4、停止位配置

系统宏定义

#define CSIZE 0000060
#define   CS5 0000000
#define   CS6 0000020
#define   CS7 0000040
#define   CS8 0000060
#define CSTOPB 0000100
#define CREAD 0000200
#define PARENB 0000400
#define PARODD 0001000
#define HUPCL 0002000
#define CLOCAL 0004000
bool CSerial::setStopBits(int stopBits)
{
 m_iStopBits = stopBits;
 switch (stopBits)
 {
 case 1:
  m_Termios.c_cflag &= ~CSTOPB;
  break; 
 case 2:
  m_Termios.c_cflag |= CSTOPB;
  break;
 default:
  cout << "Invalid StopBits!" << endl;
  m_uiError |= ERROR_CFG_StopBits;  //停止位配置出错置位
  return false;
  break;
 }
 m_uiError &= ~ERROR_CFG_StopBits;
 return true;
}

5、硬件流控配置

bool CSerial::setFlowCtrl_hw(bool bFlowCtrl_hw)
{
 m_bFlowCtrl_hw = bFlowCtrl_hw;
 if (bFlowCtrl_hw){
  m_Termios.c_cflag |= CRTSCTS; //硬件流控
 }
 else{
  m_Termios.c_cflag &= ~CRTSCTS;
 }
 return true;
}

6、软件流控配置

bool CSerial::setFlowCtrl_sw(bool bFlowCtrl_sw)
{
 if (bFlowCtrl_sw) {
  m_Termios.c_iflag |= IXON | IXOFF; //使用软件流控制  
 }
 else {
  m_Termios.c_iflag &= ~(IXON | IXOFF);
 }
 return true;
}

7、等待时间配置

系统宏定义

#define VINTR 0
#define VQUIT 1
#define VERASE 2
#define VKILL 3
#define VEOF 4
#define VTIME 5
#define VMIN 6
#define VSWTC 7
#define VSTART 8
#define VSTOP 9
#define VSUSP 10
#define VEOL 11
#define VREPRINT 12
#define VDISCARD 13
#define VWERASE 14
#define VLNEXT 15
#define VEOL2 16
bool CSerial::setVMIN(int vmin)
{
 m_iVMIN = vmin;
 m_Termios.c_cc[VMIN] = (cc_t)vmin;
 return true;
}
bool CSerial::setVTIME(int vtime)
{
 m_iVTIME = vtime;
 m_Termios.c_cc[VTIME] = (cc_t)vtime;
 return true;
}

VMIN 位置用于设置读取字符的最小数量,例如:如果设置为1,串口中只要收到数据read函数就会返回
VTIME 位置用于设置等待时间,单位为n分之1秒

8、使能配置

bool CSerial::enableConfig()
{
 if (m_uiError){ //配置中存在错误
  cout << "Configuration  had errors!" << endl;
  return false;
 }
 m_Termios.c_cflag |= CLOCAL | CREAD; //使能本地发送和接收
 tcflush(m_fd_Dev, TCIFLUSH); //清空串口中的缓存数据
 if (tcsetattr(m_fd_Dev, TCSANOW, &m_Termios) != 0) //进行设置生效
 {
  perror("tcsetattr error:");
  return false;
 }
 return true;
}

你可能感兴趣的:(Linux的串口读写详解)