跨平台串口编程

希望这套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

跨平台串口编程

时间: 2024-10-11 05:49:48

跨平台串口编程的相关文章

Linux 程序设计学习笔记----终端及串口编程基础之概念详解

转载请注明出处,谢谢! linux下的终端及串口的相关概念有: tty,控制台,虚拟终端,串口,console(控制台终端)详解 部分内容整理于网络. 终端/控制台 终端和控制台都不是个人电脑的概念,而是多人共用的小型中型大型计算机上的概念. 1.终端 一台主机,连很多终端,终端为主机提供了人机接口,每个人都通过终端使用主机的资源. 终端有字符哑终端和图形终端两种. 控制台是另一种人机接口, 不通过终端与主机相连, 而是通过显示卡-显示器和键盘接口分别与主机相连, 这是人控制主机的第一人机接口.

Linux下串口编程【转】

本文转载自:http://blog.csdn.net/w282529350/article/details/7378388 /************声明:本人只是见到这篇文章对我帮助很大才转载的,但是这个完整的程序里面本来有语法错误的,现在让我改过来了************/ --------------------------------------------------------- Author             :tiger-johnWebSite            :b

串口编程补充

首先声明此文只适合我这等串口编程新手,也欢迎大神吐槽 参数转换问题: /// <summary> /// 数据转换 /// </summary> /// <param name="values">要转换的byte数组</param> /// <param name="factor">计算因子</param> /// <param name="reh">高位字符&l

linux串口编程参数配置详解

1.linux串口编程需要的头文件 #include <stdio.h>         //标准输入输出定义 #include <stdlib.h>        //标准函数库定义 #include <unistd.h>       //Unix标准函数定义 #include <sys/types.h> #include <sys/stat.h> #include <fcntl.h>          //文件控制定义 #incl

基于树莓派的Linux串口编程_实现自发自收

串口是计算机上一种非常通用设备通信的协议,常用PC机上包含的是RS232规格的串口,具有连接线少,通讯简单,得到广泛的使用. Linux对所有设备的访问是通过设备文件来进行的,串口也是这样,为了访问串口,只需打开其设备文件即可操作串口设备.在linux系统下面,每一个串口设备都有设备文件与其关联,设备文件位于系统的/dev目录下面.如linux下的/ttyS0,/ttyS1分别表示的是串口1和串口2. 树莓派UART端口的位置:见下图的GPIO14(TXD).GPIO 15(RXD) 本文是基于

Linux串口编程详解

串口本身,标准和硬件 ? 串口是计算机上的串行通讯的物理接口.计算机历史上,串口曾经被广泛用于连接计算机和终端设备和各种外部设备.虽然以太网接口和USB接口也是以一个串行流进行数据传送的,但是串口连接通常特指那些与RS-232标准兼容的硬件或者调制解调器的接口.虽然现在在很多个人计算机上,原来用以连接外部设备的串口已经广泛的被USB和Firewire替代:而原来用以连接网络的串口则被以太网替代,还有用以连接终端的串口设备则已经被MDA或者VGA取而代之.但是,一方面因为串口本身造价便宜技术成熟,

QT开发(五十)——QT串口编程基础

QT开发(五十)--QT串口编程基础 一.QtSerialPort简介 1.串口通信基础 目前使用最广泛的串口为DB9接口,适用于较近距离的通信.一般小于10米.DB9接口有9个针脚. 串口通信的主要参数如下: A.波特率:衡量通信速度的参数,表示每秒钟传送的bit的个数.例如9600波特表示每秒钟发送9600个bit. B.数据位:衡量通信中实际数据位的参数,当计算机发送一个信息包,实际包含的有效数据位个数. C.停止位:用于表示单个包的最后一位.典型的值为1和2位. D.奇偶校验位:串口通信

linux串口编程参数配置详解(转)

1.linux串口编程需要的头文件 #include <stdio.h>         //标准输入输出定义#include <stdlib.h>        //标准函数库定义#include <unistd.h>       //Unix标准函数定义#include <sys/types.h>#include <sys/stat.h>#include <fcntl.h>          //文件控制定义#include &l

使用C#数组与串口编程的应用实例

我们从两个方面向你介绍C#串口编程具体的步骤,那么我们第一要注意使用串口编程 的软件是什么?并且要注意到它的安装及环境的配置,只有这些做得到位了,我们才可以做好之后的事情,那么接下来就是我们的编程实际动手,那么如何做好每一 步呢?下面就和大家一起分享下具体内容具体可以观看.NET/C#课程[1]:初学者应掌握的C#基础教程了解: C#串口编程1.虚拟串口软件 由于手上没有串口硬件,所以在网上找了一个 VSPM 虚拟串口软件 ,据说是免费的. 刚开始也不知道怎么用,摸索了一下,终于成功. 安装以后