首页 > 代码库 > 跨平台串口编程

跨平台串口编程

希望这套API可以帮助有需要的人!

如果您有异议,请给出理由,让我可以改善存在的问题,谢谢。

如果您在使用中发现问题,请及时联系我。 


我承认在用C++写 C,纯粹为了封装方便,如果是这个原因要喷我,那我替您先喷了..


虽说是跨平台但我测试的平台有限。

windows7 x32使用vs2008编译运行成功

Ubuntu12.04 x32 使用gcc version 4.6.3 (Ubuntu/Linaro 4.6.3-1ubuntu5) 编译运行成功

arm-linux-gcc [ gcc version 4.3.3 (Sourcery G++ Lite 2009q1-203) ] 交叉编译成功


关于Linux下使用欢迎参考我的一篇短文

http://my.oschina.net/mlgb/blog/300925


#ifndef SERIALPORT_H_
#define SERIALPORT_H_

#include <stddef.h>

#ifdef _WIN32
	#include <Windows.h>
	typedef HANDLE 	serial_t;
#elif defined __linux__
	typedef int 	serial_t;
#endif

enum COMPARAM
{
	CHECK_NONE,	//无校验
	CHECK_ODD,	//奇校验
	CHECK_EVEN,	//偶校验
	STOP_1,		//停止位1
	STOP_1_5,	//停止位1.5
	STOP_2,		//停止位2
	DATA_7,		//数据位7
	DATA_8		//数据位8
};

enum COMIO
{
	COM_IN,		//输入选项
	COM_OUT		//输出选项
};

class SerialPort
{
public:
	SerialPort(void);
	~SerialPort(void);
	
	/*
	 *打开串口
	 *Param:
	 *	[IN]port	COM端口
	 *	[IN]opt		Windows平台	是否启用重叠IO
	 *				Linux平台 	是否启用非阻塞方式
	 * */
	bool	
	open(int port,bool opt = false);
	
	/*
	 *关闭串口
	 * */
	void
	close();

	/*
	 *设置串口属性
	 *Param:
	 *	[IN]rate		波特率
	 *	[IN]check_bit	校验位
	 *	[IN]stop_bit	停止位(linux无1.5)
	 *	[IN]data_bit	数据位
	 * */
	bool	
	set_attribute(	size_t rate, 
					COMPARAM check_bit 	= CHECK_NONE,
					COMPARAM stop_bit 	= STOP_1,
					COMPARAM data_bit 	= DATA_8);

	/*
	 *设置超时
	 *Param:
	 *	[IN]write_ms	写超时(单位 毫秒)
	 *	[IN]read_ms		读超时(单位 毫秒)
	 *	[IN]flag		Windows下指定间隔延迟
						Linux下指定非规范模式的最小字符数
	 *Other
	 *	linux在非阻塞模式下设置超时无效
	 *	超时设置为0时“效果”等同于非阻塞模式
	 *	read超时返回0
	 * */
	bool
	set_timeout(size_t write_ms,size_t read_ms,int flag = 0);

	/*
	 *发送数据
	 *Param:
	 *	[IN]sendBuf		发送数据缓存
	 *	[IN]len			发送数据长度
	 */
	int
	send(const char* sendBuf,size_t len);

	/*
	 *接收数据
	 *Param:
	 *	[OUT]recvBuf	接收数据缓存
	 *	[IN] len		接收数据长度
	 */
	int
	recv(char* recvBuf,size_t len);

	/*
	 *终止IO操作
	 *Param:
	 *	[IN]mode		指定需要操作的IO
	 */
	void	
	abort(COMIO mode);

	/*
	 *清空缓冲区数据
	 *Param:
	 *	[IN]mode		指定需要操作的IO
	 */
	void	
	clear_buff(COMIO mode);

	/*
	 *开放使用和平台相关的高级IO操作
	 *如:
	 *LINUX下的select和poll模型
	 *WIN32下的Overlapped模型
	 * */
	serial_t
	get_serial_handle();

#ifdef _WIN32
	/*
	 *获取接收缓冲区数据长度
	 */
	size_t
	peek();
#endif

#ifdef __linux__
	/*
	 *设置LINUX下串口阻塞方式
	 *Param:
	 *	[IN]isBlock		是否阻塞
	 */
	bool
	set_block(bool isBlock);
#endif

private:
	serial_t m_hSerial;//串口句柄/描述符
};

#endif //SERIALPORT_H_

#include "SerialPort.h"
#include <stdio.h>

#ifdef _WIN32
	#define INVALID_SERRAL INVALID_HANDLE_VALUE
#elif defined __linux__
	#include <unistd.h>
	#include <fcntl.h> //open close
	#include <termios.h>//struct termios
	#define INVALID_SERRAL -1

	size_t t_raw_rate[] =
	{
		50,			75,			110,		134,		150,
		200,		300,		600,		1200,		1800,
		2400,		4800, 		9600,		19200,		38400,
		57600,		115200,		230400,		460800,		500000,
		576000,		921600,		1000000,	1152000,	1500000,
		2000000,	2500000,	3000000,	3500000,	4000000
	};

	size_t t_unix_rate[] =
	{
		B50,		B75,		B110,		B134,		B150,
		B200,		B300,		B600,		B1200,		B1800,
		B2400,		B4800, 		B9600,		B19200,		B38400,
		B57600,		B115200,	B230400,	B460800,	B500000,
		B576000,	B921600,	B1000000,	B1152000,	B1500000,
		B2000000,	B2500000,	B3000000,	B3500000,	B4000000
	};
#endif







SerialPort::SerialPort(void)
{
	m_hSerial = INVALID_SERRAL;
}

SerialPort::~SerialPort(void)
{
	this->close();
}

bool
SerialPort::open(int port,bool opt)
{
	char com[16]={0};

#ifdef _WIN32

	#ifdef WINCE
		sprintf(com,"COM%d:",port);
	#else
		sprintf(com,"COM%d",port);
	#endif
	
	DWORD dwFlagsAndAttributes = FILE_ATTRIBUTE_NORMAL;
	if(opt)
	{
		dwFlagsAndAttributes |= FILE_FLAG_OVERLAPPED;
	}

	m_hSerial = CreateFile(com, GENERIC_READ | GENERIC_WRITE,
							0,
							NULL, 
							OPEN_EXISTING,
							dwFlagsAndAttributes,
							NULL);
#elif defined __linux__

	#ifdef __arm__
		sprintf(com,"/dev/ttySAC%d",port-1);
	#else
		sprintf(com,"/dev/ttyS%d",port-1);
	#endif

	int flags = O_RDWR | O_NOCTTY;
	if(opt)
	{
		flags |= O_NDELAY;
	}
	m_hSerial = ::open(com,flags);

#endif

	return (m_hSerial != INVALID_SERRAL)?true:false;
}

void
SerialPort::close()
{
	if(m_hSerial != INVALID_SERRAL)
	{
#ifdef _WIN32
		CloseHandle(m_hSerial);
#elif defined __linux__
		::close(m_hSerial);
#endif
		m_hSerial = INVALID_SERRAL;
	}
}

bool
SerialPort::set_attribute(	size_t rate, 
							COMPARAM check_bit,
							COMPARAM stop_bit,
							COMPARAM data_bit)
{
	if(m_hSerial == INVALID_SERRAL) return false;

#ifdef _WIN32
	DCB dcb = {0};
	dcb.BaudRate = rate;

	dcb.fParity = TRUE;
	dcb.fBinary = FALSE;

	if(check_bit == CHECK_NONE)
	{
		dcb.fParity = FALSE;
		dcb.Parity = NOPARITY;
	}
	else if(check_bit == CHECK_ODD)
	{
		dcb.Parity = ODDPARITY;
	}
	else if(check_bit == CHECK_EVEN)
	{
		dcb.Parity = EVENPARITY;
	}

	if(stop_bit == STOP_1)
	{
		dcb.StopBits = ONESTOPBIT;
	}
	else if(stop_bit == STOP_1_5)
	{
		dcb.StopBits = ONE5STOPBITS;;
	}
	else if(stop_bit == STOP_2)
	{
		dcb.StopBits = TWOSTOPBITS;
	}

	if(data_bit == DATA_8)
	{
		dcb.StopBits = 8;
	}
	else if(data_bit == DATA_7)
	{
		dcb.StopBits = 7;
	}
	
	if(  ! SetCommState(m_hSerial,&dcb)  )
	{
		return false;
	}
	SetupComm(m_hSerial,50/*IN BUF*/,50/*OUT BUFF*/);

	PurgeComm(m_hSerial,PURGE_TXABORT | PURGE_RXABORT | PURGE_TXCLEAR | PURGE_RXCLEAR);

#elif defined __linux__
	struct termios com_cfg = {0};
	size_t raw_rate = 0;
	size_t i=0;


	for(i=0; i< sizeof(t_raw_rate)/sizeof(size_t) ; i++)
	{
		if(t_raw_rate[i] == rate)
		{
			raw_rate = t_unix_rate[i];
			break;
		}
	}

	if(!raw_rate)
	{
		return false;
	}
	else
	{
		cfsetispeed(&com_cfg,raw_rate);//IN  speend;
		cfsetospeed(&com_cfg,raw_rate);//OUT speend;
	}

	switch(check_bit)
	{
	case CHECK_NONE:
		com_cfg.c_cflag &= ~PARENB;
		com_cfg.c_iflag &= ~INPCK;
		break;
	case CHECK_ODD:
		com_cfg.c_cflag |= (PARODD|PARENB);
		com_cfg.c_iflag |= INPCK;
		break;
	case CHECK_EVEN:
		com_cfg.c_cflag |= PARENB;
		com_cfg.c_iflag &= ~PARODD;
		com_cfg.c_iflag |= INPCK;
		break;
	default:
		return false;
	}

	switch(stop_bit)
	{
	case STOP_1:
		com_cfg.c_cflag &= ~CSTOPB;
		break;
	case STOP_1_5:
		com_cfg.c_cflag &= ~CSTOPB;//Linux没有1.5停止位
		break;
	case STOP_2:
		com_cfg.c_cflag |= CSTOPB;
		break;
	default:
		return false;
	}

	switch(data_bit)
	{
	case DATA_8:
		com_cfg.c_cflag &= ~CSIZE;
		com_cfg.c_cflag |= CS8;
		break;
	case DATA_7:
		com_cfg.c_cflag &= ~CSIZE;
		com_cfg.c_cflag |= CS7;
		break;
	default:
		return false;
	}

	tcflush(m_hSerial,TCIOFLUSH);

	com_cfg.c_cc[VTIME] = 0;
	com_cfg.c_cc[VMIN] =  1;

	if( tcsetattr(m_hSerial,TCSANOW,&com_cfg) )
	{
		return false;
	}
#endif
	return true;
}

bool 
SerialPort::set_timeout(size_t write_ms,size_t read_ms,int flag)
{
	if(m_hSerial == INVALID_SERRAL) return false;

#ifdef _WIN32

	COMMTIMEOUTS to;
	memset(&to,0,sizeof(to));

	/* 读间隔超时。 接收时,两字符间最大的时延。
	 * 将ReadIntervalTimeout设置为0, 则不使用间隔超时, 只考虑总超时设置
	 * */
	to.ReadIntervalTimeout = flag;

	to.WriteTotalTimeoutMultiplier = 1;//写每字节的超时
	to.WriteTotalTimeoutConstant = write_ms;//写串口数据的固定超时
	// TIMEOUT = WriteTotalTimeoutMultiplier  Bytecount  +   WriteTotalTimeoutConstant

	to.ReadTotalTimeoutMultiplier =  1; //读取每字节的超时
	to.ReadTotalTimeoutConstant = read_ms; //读串口数据的固定超时
	// TIMEOUT = ReadTotalTimeoutMultiplier * Bytecount   +   ReadTotalTimeoutConstant
	return (SetCommTimeouts(m_hSerial,&to) == TRUE)?true:false;

#elif defined __linux__
	struct termios com_cfg = {0};

	tcgetattr(m_hSerial,&com_cfg);//get old setting

	com_cfg.c_cc[VTIME] = read_ms*0.01;//超时单位 百毫秒
	com_cfg.c_cc[VMIN] =  flag;//最少可读数据 超时有效必须是0
	printf("[SET] VTIME(%d),VMIN(%d)\n",com_cfg.c_cc[VTIME],com_cfg.c_cc[VMIN]);

	return (!tcsetattr(m_hSerial,TCSANOW,&com_cfg) )?true:false;
#endif
}

#ifdef __linux__
bool
SerialPort::set_block(bool isBlock)
{
	if(m_hSerial == INVALID_SERRAL) return false;

	if(isBlock)
	{
		printf("[SET] block mode\n");
		return ( -1 == fcntl(m_hSerial,F_SETFL,0) )?false:true;
	}
	else
	{
		printf("[SET] no block mode\n");
		return ( -1 == fcntl(m_hSerial,F_SETFL,FNDELAY) )?false:true;
	}
	return true;
}
#endif
int
SerialPort::send(const char* sendBuf,size_t len)
{
	if(m_hSerial == INVALID_SERRAL) return 0;

#ifdef _WIN32
	DWORD write_len = 0;
	WriteFile(m_hSerial,sendBuf,len,&write_len,NULL);
	return write_len;
#elif defined __linux__
	return write(m_hSerial,sendBuf,len);
#endif
}

int
SerialPort::recv(char* recvBuf,size_t len)
{
	if(m_hSerial == INVALID_SERRAL) return 0;

#ifdef _WIN32
	DWORD read_len = 0;
	ReadFile(m_hSerial,recvBuf,len,&read_len,NULL);
	return read_len;
#elif defined __linux__
	return read(m_hSerial,recvBuf,len);
#endif
}

void 
SerialPort::abort(COMIO mode)
{
	if(m_hSerial != INVALID_SERRAL)
	{
		if(mode == COM_IN)
		{
#ifdef _WIN32
			//中断所有读操作并立即返回,即使读操作还没有完成
			PurgeComm(m_hSerial,PURGE_RXABORT);
#elif defined __linux__
			tcflush(m_hSerial,TCIOFF);//挂起输入
#endif
		}
		else if(mode == COM_OUT)
		{
#ifdef _WIN32
			//中断所有写操作并立即返回,即使写操作还没有完成
			PurgeComm(m_hSerial,PURGE_TXABORT);
#elif defined __linux__
			tcflush(m_hSerial,TCOOFF);//挂起输出
#endif
		}
	}
}

void
SerialPort::clear_buff(COMIO mode)
{
	if(m_hSerial != INVALID_SERRAL)
	{
		if(mode == COM_IN)
		{
#ifdef _WIN32
			PurgeComm(m_hSerial,PURGE_RXCLEAR);
#elif defined __linux__
			tcflush(m_hSerial,TCIFLUSH);
#endif
		}
		else if(mode == COM_OUT)
		{
#ifdef _WIN32
			PurgeComm(m_hSerial,PURGE_TXCLEAR);
#elif defined __linux__
			tcflush(m_hSerial,TCOFLUSH);
#endif
		}
	}
}

serial_t
SerialPort::get_serial_handle()
{
	return m_hSerial;
}

#ifdef _WIN32
size_t
SerialPort::peek()
{
	if(m_hSerial == INVALID_SERRAL)
	{
		return  0;
	}

	DWORD dwErrorFlags;
	COMSTAT ComStat;

	ClearCommError( m_hSerial, &dwErrorFlags, &ComStat );

	return ComStat.cbInQue;
}
#endif