最近做的项目需要使用到串口通信方面的知识,就这方面的内容加以总结和分享:

(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 port
	else 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:				// 默认数据位为8
			TNewSerialPortParam.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,激活CSTOPB
			TNewSerialPortParam.c_cflag |= CSTOPB;
			break;
		case 1:				// 停止位为1,清除CSTOPB
			TNewSerialPortParam.c_cflag &= ~CSTOPB;
			break;
		default:				// 默认停止位为1
			TNewSerialPortParam.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为长度。




Logo

更多推荐