最近做的项目需要使用到串口通信方面的知识,就这方面的内容加以总结和分享:
(1)首先是串口的读写操作,都是在Linux下进行的操作
1.1 串口的打开
//打开串口

/******************************************************************************* 函数名称:		OpenSerialPort()* 功能描述:		打开串口* 输入参数: 	int	iComNum				// 串口号(COM0,COM1,COM2等)* 输出参数:* 返回值:		打开的串口的文件描述符* 其它说明:* 修改历史:******************************************************************************/
static int	OpenSerialPort(int	iComNum)// 串口号(COM1,COM2等){/* 各变量的定义 */int		fd = -1;			// 串口的文件描述符if ( 1 == iComNum )	// 串口1{fd = open("/dev/ttyS1", O_RDWR | O_NOCTTY | O_NDELAY);if ( -1 == fd ){perror("Can't Open Serial Port 1");return	(-1);}}else if ( 2 == iComNum )	// 串口2{fd = open("/dev/ttyS2", O_RDWR | O_NOCTTY | O_NDELAY);if ( -1 == fd ){perror("Can't Open Serial Port 2");return	(-1);}}else if ( 3 == iComNum )	// 串口3{fd = open("/dev/ttyS3", O_RDWR | O_NOCTTY | O_NDELAY);if ( -1 == fd ){perror("Can't Open Serial Port 3");return	(-1);}}//change by hss delete 0 add 4 serial portelse if ( 4 == iComNum )			// 串口4{fd = open("/dev/ttyS4", O_RDWR | O_NOCTTY | O_NDELAY);if ( -1 == fd ){perror("Can't Open Serial Port 4");return	(-1);}}else{printf("system don't have Serial Port %d\n",iComNum);return -1;} 
 
fcntl(fd, F_SETFL, O_NONBLOCK);			//非阻塞/* 测试是否为一个终端设备,以进一步确认串口是否正确打开 */if( 0 == isatty(STDIN_FILENO) ){printf("standard input is not a terminal device.\n");}else{;//		printf("is a tty success!\n");} 
 
//	printf("fd-open = %d\n", fd); 
 
return	fd;} 
 
 
 
 
使用到的函数fcntl(fd, F_SETFL, 0);			//此为阻塞方式 

 
/******************************************************************************* 函数名称:		SetSerialPort()* 功能描述:		配置串口参数* 输入参数: 	int		fd,					// 串口的文件描述符*				int		iSerialPortSpeed,	// 串口速率(读写速率一致)*				int		iBits,				// 数据位 或 字符大小 ???*				char	cParityCheck,		// 奇偶校验位*				int		iStop				// 停止位* 输出参数:		无* 返回值:		int* 其它说明:******************************************************************************/int	SetSerialPort(int		fd,					// 串口的文件描述符int		iSerialPortSpeed,	// 串口速率(读写速率一致)int		iBits,				// 数据位 或 字符大小 ???int		iParityCheck,		// 奇偶校验位int		iStop,				// 停止位int		iFlowControl		// 流控标志位){/* 各变量的定义 */struct termios	TNewSerialPortParam;	// 新串口配置参数struct termios	TOldSerialPortParam;	// 老串口配置参数 
 
/* 各变量值的赋值和初始化 */ 
 
/* 函数任务开始 */// 保存测试现有串口的参数设置,如果串口号等出错,则有相关出错信息if ( 0 != tcgetattr( fd, &TOldSerialPortParam ) ){perror("SetupSerial 1");return	(-1);} 
 
// 新串口配置参数清零bzero( &TNewSerialPortParam, sizeof(TNewSerialPortParam) ); 
 
// 激活本地连接和接收使能TNewSerialPortParam.c_cflag |= CLOCAL | CREAD; 
 
// 设置数据位TNewSerialPortParam.c_cflag &= ~CSIZE;switch ( iBits ){case 7:TNewSerialPortParam.c_cflag |= CS7;break;case 8:TNewSerialPortParam.c_cflag |= CS8;break;default:				// 默认数据位为8TNewSerialPortParam.c_cflag |= CS8;break;}// end of switch ( iBits ) 
 
// 设置波特率switch ( iSerialPortSpeed ){case 2400:同下case 4800:同下case 9600:cfsetispeed( &TNewSerialPortParam, B9600);cfsetospeed( &TNewSerialPortParam, B9600);break;case 19200:同上case 38400:同上case 57600:同上case 115200:cfsetispeed( &TNewSerialPortParam, B115200 );cfsetospeed( &TNewSerialPortParam, B115200);break;case 460800:同上default:cfsetispeed( &TNewSerialPortParam, B9600 );cfsetospeed( &TNewSerialPortParam, B9600);break;}// end of switch ( iSerialPortSpeed ) 
 
// 设置奇偶校验位switch ( iParityCheck ){case ODD_CHECK:			// 奇校验TNewSerialPortParam.c_cflag |= PARENB;TNewSerialPortParam.c_cflag |= PARODD;TNewSerialPortParam.c_iflag |= (INPCK | ISTRIP);break;case EVEN_CHECK:			// 偶校验TNewSerialPortParam.c_iflag |= (INPCK | ISTRIP);TNewSerialPortParam.c_cflag |= PARENB;TNewSerialPortParam.c_cflag &= ~PARODD;break;case NO_CHECK:			// 无奇偶校验位TNewSerialPortParam.c_cflag &= ~PARENB;break;default:				// 默认无奇偶校验位TNewSerialPortParam.c_cflag &= ~PARENB;break;}// end of switch ( cParityCheck ) 
 
// 设置停止位switch ( iStop ){case 2:				// 停止位为2,激活CSTOPBTNewSerialPortParam.c_cflag |= CSTOPB;break;case 1:				// 停止位为1,清除CSTOPBTNewSerialPortParam.c_cflag &= ~CSTOPB;break;default:				// 默认停止位为1TNewSerialPortParam.c_cflag &= ~CSTOPB;break;}// end of switch ( iStop ) 
 
// 设置流控switch ( iFlowControl ){case NO_FLOW_CTRL:	    // 无流控TNewSerialPortParam.c_cflag &= ~CRTSCTS;	// 关闭硬件流控TNewSerialPortParam.c_iflag &= ~( IXON | IXOFF | IXANY );// 关闭软件流控break; 
 
case HARD_FLOW_CTRL:	// 硬流控TNewSerialPortParam.c_cflag |= CRTSCTS;		// 开启硬件流控TNewSerialPortParam.c_iflag &= ~( IXON | IXOFF | IXANY );// 关闭软件流控break; 
 
case SOFT_FLOW_CTRL:	// 软流控TNewSerialPortParam.c_cflag &= ~CRTSCTS;	// 关闭硬件流控TNewSerialPortParam.c_iflag |= ( IXON | IXOFF | IXANY );// 开启软件流控break;default:				// 默认无流控TNewSerialPortParam.c_cflag &= ~CRTSCTS;	// 关闭硬件流控TNewSerialPortParam.c_iflag &= ~( IXON | IXOFF | IXANY );// 关闭软件流控break;}// end of switch ( iFlowControl ) 
 
// 设置等待时间和最小接收字符TNewSerialPortParam.c_cc[VTIME] = 0;TNewSerialPortParam.c_cc[VMIN] = 0;// 处理未接受字符tcflush( fd, TCIFLUSH ); 
 
// 激活新配置if ( 0 != tcsetattr(fd, TCSANOW, &TNewSerialPortParam) ){perror("com set error");return	(-1);} 
 
 
//	printf("set done!\n");/* 函数任务结束 */return	0;} 

 
用以上两个函数,打开串口,并设置串口的各项属性,主要的有波特率等信息,设置错误即使打开串口也无法通信。
调用例子:
int di_s4b_comfd = OpenSerialPort();SetSerialPort(di_s4b_comfd, //文件描述符115200,//波特率8,//数据位0,//奇偶校验位1,//停止位0);//流控标志位 
1.2 数据的写入 
写操作较为简单,因为不需要一直监视串口,直接写到串口就可以,不完备的写操作大致如下:
/*
将ACK通过三号端口写出
*/
void writeMessage(){int comfd = sthread->retComfd();//由外部获得端口的文件描述符const char array[]={'A','C','K'};write(comfd,array,3);qDebug("writeMessage done!");} 
主要使用的函数为write函数 
ssize_t write(int fd, const void *buf, size_t count);  
返回值为写入串口的数据长度,buf为写入的数据,count为数据长度
 以上例子是一个不完备的写入情况,没有对写入不完全的情况进行处理,也没有考虑资源的保护和互斥的使用。1.3  数据的读出
    数据的读出较写入麻烦,原因在于对读取数据时机的判断,为在数据到达时立即得到通知,并把数据读出,需要时时对端口进行监控,同时主程序保持运行,此时就需要用到多线程,一个主线程,一个守护线程。此处先给出读数据的示例代码,多线程串口通信在下一节给出。
 /*
 子线程一直监视串口,在有数据时,将其读出
 */void SThread::run(){while (1){res = read(di_s4b_comfd, tosendstr, 4);if (res >=4){qDebug("abcd\n");emit get(1);}}qDebug("Serial done\n");}read函数原型
#include<unistd.h>
ssize_t read(intfd, void *buf, size_t count);
read返回为读取字符串的长度,fd为端口的文件描述符,buf为读取的数据,count为长度。