异步串口通信框架

前言:

  前段时间,服务器增加串口通信需求,早期串口框架仅仅只能支持单串口通信,还不支持热插拔。

  由于在网上找了下没有自己想要的框架,因此,自己搭建了一个异步串口通信框架, 目前该框架已投入使用,没有发现内存泄露或者其他问题。该框架运用了【libserialport】【libuv】开源库,二者都支持跨平台,因此在可以放心在其他平台下使用该框架。

  如果你确定要使用我的框架,请阅读最后的框架分点讲解部分,里面涉及到部分细节设置的地方。

功能:异步串口服务器框架(支持热插拔、多COM口通信框架)

源代码:链接:https://pan.baidu.com/s/1XmwsXP8UbG1l-AFESogFiQ   提取码:qdz7 

平台:Linux_X86(该平台已验证)(支持跨平台)

语言:C

工程:

  下载源代码后,直接在QCom目录下运行【make】命令即可生成测试程序【com】。框架代码在【QCom/src/ComFarme】目录下

框架主体:

我先把整体代码放出来,在分点讲解。

框架头文件:【comfarme.h】

#ifndef COMFARME_H
#define COMFARME_H

#ifdef __cplusplus
extern "C" {
#endif

int ComFarme_NewHandle(void **handle);

void ComFarme_FreeHandle(void *handle);

int ComFarme_WaitHandle(void *handle);

#ifdef  __cplusplus
	}
#endif  /* #ifdef  __cplusplus */

#endif

框架测试DEMO: 【main.c】

#include <stdio.h>
#include <unistd.h>
#include "libserialport.h"
#include "uv.h"
#include "../ComFarme/comfarme.h"

volatile sig_atomic_t _running = 1;

static void catch_signal(int nsig)
{
	_running = 0;
}

int main()
{
	void *handle = NULL;
	int ret = 0;

	signal(SIGINT, catch_signal);
	signal(SIGTERM, catch_signal);

	ret = ComFarme_NewHandle(&handle);
	if (ret)
	{
		printf("ComFarme_NewHandle error\n");
		return -1;
	}
	else
	{
		printf("ComFarme_NewHandle success\n");
	}

	while (_running)
	{
		sleep(2);
	}

	printf("begin free\n");
	ComFarme_FreeHandle(handle);
	return 0;
}

框架代码:【comfarme.c】

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <pthread.h>
#include "libserialport.h"
#include "uv.h"
#include "comfarme.h"

#define COMFARME_RUN 1
#define COMFARME_STOP 2
#define COMFARME_LOOPTIME 3
#define COMFARME_WAIETIME 2000
#define CONFARME_MAXCOM 1024
#define CONFARME_BUFLEN 1024
#define CONFARME_ADAPTER "/dev/ttyUSB"

typedef struct
{
	void *hand;
	void *next;
	void *handle;

	struct sp_port *list_ptr;
	uv_thread_t thread_communicate;

	unsigned char buf[CONFARME_BUFLEN];

}COMFARME_LIST;

typedef struct
{
	uv_thread_t thread_findcom;

	struct sp_port_config *config_ptr;

	COMFARME_LIST *list;
	pthread_mutex_t list_lock;

	int comfarme_switch;
}COMFARME_HANDLE;

static int ComFarme_ListDelete(COMFARME_HANDLE *handle, struct sp_port *list_ptr);

/* 打印二进制数据 */
static void ComFarme_PrintfData(unsigned char *data, int len)
{
	int i = 0;
	for (i = 0; i < len; i++)
	{
		printf("%02x ", data[i]);
	}
	printf("\n");
}

/* 数据通信模块 */
static void ComFarme_Communicate(void* arg)
{
	COMFARME_LIST *data = (COMFARME_LIST *)arg;
	COMFARME_HANDLE *handle = (COMFARME_HANDLE *)data->handle;
	struct sp_event_set *result_ptr = NULL;
	enum sp_return  sp_ret = SP_OK;

	/* 打开串口 */
	sp_ret = sp_open(data->list_ptr, SP_MODE_READ_WRITE);
	if (sp_ret != SP_OK)
	{
		printf("sp_open error @ret %d @errmsg %s\n", sp_ret, sp_last_error_message());
		goto ERR_OPEN;
	}
	else
	{
		printf("sp_open success\n");
	}
	/* 配置串口 */
	sp_ret = sp_set_config(data->list_ptr, handle->config_ptr);
	if (sp_ret != SP_OK)
	{
		printf("sp_set_config error @ret %d @errmsg %s\n", sp_ret, sp_last_error_message());
		goto ERR_CONFIG;
	}
	else
	{
		printf("sp_set_config success\n");
	}
	
	/* 添加触发器 */
	sp_ret = sp_new_event_set(&result_ptr);
	if (sp_ret != SP_OK)
	{
		printf("sp_new_event_set error @ret %d @errmsg %s\n", sp_ret, sp_last_error_message());
		goto ERR_CONFIG;
	}
	else
	{
		printf("sp_new_event_set success\n");
	}
	sp_ret = sp_add_port_events(result_ptr, data->list_ptr, SP_EVENT_ERROR);
	if (sp_ret != SP_OK)
	{
		printf("sp_add_port_events error1 @ret %d @errmsg %s\n", sp_ret, sp_last_error_message());
		goto ERR_EVENT;
	}
	sp_ret = sp_add_port_events(result_ptr, data->list_ptr, SP_EVENT_RX_READY);
	if (sp_ret != SP_OK)
	{
		printf("sp_add_port_events error2 @ret %d @errmsg %s\n", sp_ret, sp_last_error_message());
		goto ERR_EVENT;
	}
	/* 等待数据传输 */
	while (handle->comfarme_switch == COMFARME_RUN)
	{
		sp_ret = sp_wait(result_ptr, 0);
		if (sp_ret != SP_OK)
		{
			printf("sp_wait error @ret %d @errmsg %s\n", sp_ret, sp_last_error_message());
		}
		memset(data->buf, 0, CONFARME_BUFLEN);
		sp_ret = sp_nonblocking_read(data->list_ptr, data->buf, CONFARME_BUFLEN);
		if (sp_ret <= 0)
		{
			printf("sp_nonblocking_read error @ret %d @errmsg %s\n", sp_ret, sp_last_error_message());
			goto ERR_EVENT;
		}
		else
		{
			printf("sp_nonblocking_read success @ret %d\n", sp_ret);
			ComFarme_PrintfData(data->buf, (int)sp_ret);
			sp_ret = sp_blocking_write(data->list_ptr, data->buf, (size_t )sp_ret, COMFARME_WAIETIME);
			if (sp_ret <= 0)
			{
				printf("sp_nonblocking_write error @ret %d @errmsg %s\n", sp_ret, sp_last_error_message());
			}
			else
			{
				printf("send:\n");
				ComFarme_PrintfData(data->buf, sp_ret);
			}
		}
	}

ERR_EVENT:
	sp_free_event_set(result_ptr);
ERR_CONFIG:
	sp_close(data->list_ptr);
ERR_OPEN:
	ComFarme_ListDelete(handle, data->list_ptr);
}

/* 注册串口事件 */
static int ComFarm_EventAdd(COMFARME_LIST *data)
{
	return uv_thread_create(&(data->thread_communicate), ComFarme_Communicate, data);
}

/* 删除串口 */
static int ComFarme_ListDelete(COMFARME_HANDLE *handle, struct sp_port *list_ptr)
{
	COMFARME_LIST *list = handle->list;
	COMFARME_LIST *hand = NULL, *next = NULL;
	pthread_mutex_lock(&handle->list_lock);

	while ( list != NULL )
	{
		if (strcmp(sp_get_port_name(list->list_ptr), sp_get_port_name(list_ptr)) == 0 )
		{
			hand = (COMFARME_LIST *)list->hand;
			next = (COMFARME_LIST *)list->next;
			if (hand != NULL)
			{
				hand->next = next;
			}
			if (next != NULL)
			{
				next->hand = hand;
			}
			if (hand == NULL && next == NULL)
			{
				handle->list = NULL;
			}
			else if(hand == NULL)
			{
				handle->list = (COMFARME_LIST *)(handle->list)->next;
			}
			sp_free_port(list->list_ptr);
			free(list);
			pthread_mutex_unlock(&handle->list_lock);
			return 0;
		}
		list = (COMFARME_LIST *)list->next;
	}
	pthread_mutex_unlock(&handle->list_lock);
	return 0;
}

/* 增加串口 */
static int ComFarme_ListAdd(COMFARME_HANDLE *handle, struct sp_port *list_ptr)
{
	struct sp_port *ptr = NULL;
	enum sp_return  sp_ret = SP_OK;
	COMFARME_LIST *node = NULL;
	int ret = 0;

	pthread_mutex_lock(&handle->list_lock);

	sp_ret = sp_copy_port(list_ptr, &ptr);
	if (sp_ret != SP_OK)
	{
		pthread_mutex_unlock(&handle->list_lock);
		return -1;
	}

	node = (COMFARME_LIST *)malloc(sizeof(COMFARME_LIST));
	memset(node, 0, sizeof(COMFARME_LIST));

	node->list_ptr = ptr;
	node->handle = handle;
	if (handle->list == NULL)
	{
		node->hand = NULL;
		node->next = NULL;
		handle->list = node;
	}
	else
	{
		node->hand = NULL;
		node->next = handle->list;
		handle->list = node;
	}

	ret = ComFarm_EventAdd(node);
	if (ret)
	{
		pthread_mutex_unlock(&handle->list_lock);
		return -1;
	}

	pthread_mutex_unlock(&handle->list_lock);
	return 0;
}

static int ComFarme_ListCompare(COMFARME_HANDLE *handle, struct sp_port *list_ptr)
{
	COMFARME_LIST *list = handle->list;

	//printf("@ComFarme_ListCompare %s\n", sp_get_port_name(list_ptr));

	/* 过滤COM口 */
	if (memcmp(sp_get_port_name(list_ptr), CONFARME_ADAPTER, strlen(CONFARME_ADAPTER)) != 0)
	{
		//printf("@ComFarme_ListCompare -1 @len %d\n", strlen(CONFARME_ADAPTER));
		return -1;
	}

	pthread_mutex_lock(&handle->list_lock);
	while (list != NULL)
	{
		if ( strcmp(sp_get_port_name(list->list_ptr), sp_get_port_name(list_ptr) ) == 0 )
		{
			pthread_mutex_unlock(&handle->list_lock);
			//printf("@ComFarme_ListCompare -2");
			return -2;
		}
		list = (COMFARME_LIST *)list->next;
	}

	pthread_mutex_unlock(&handle->list_lock);
	printf("@new com %s\n", sp_get_port_name(list_ptr));
	return 0;
}

static int ComFarme_GetCom(COMFARME_HANDLE *handle)
{
	struct sp_port **list_ptr = NULL;
	enum sp_return  sp_ret = SP_OK;
	int i = 0, ret = 0;

	/* COM口配置 */
	if (!handle->config_ptr)
	{
		sp_ret = sp_new_config(&(handle->config_ptr));
		if (sp_ret != SP_OK)
		{
			printf("sp_new_config error\n");
			return -1;
		}
		sp_set_config_baudrate(handle->config_ptr, 115200);
		sp_set_config_bits(handle->config_ptr, 8);
		sp_set_config_parity(handle->config_ptr, SP_PARITY_NONE);
		sp_set_config_stopbits(handle->config_ptr, 1);
	}

	/* 获取列表 */
	sp_ret = sp_list_ports(&list_ptr);
	if (sp_ret != SP_OK)
	{
		printf("sp_list_ports error @ret %d\n", sp_ret);
		return -1;
	}

	for (i = 0; i < CONFARME_MAXCOM; i++)
	{
		if (list_ptr[i] == NULL)
		{
			break;
		}
		if (ComFarme_ListCompare(handle, list_ptr[i]) == 0 )
		{
			/* 增加串口 */
			ret = ComFarme_ListAdd(handle, list_ptr[i]);
			if (ret)
			{
				printf("ComFarme_ListAdd error @ret %d\n", ret);
				sp_free_port_list(list_ptr);
				return -1;
			}
			else
			{
				printf("ComFarme_ListAdd success @ret %d\n", ret);
			}
		}
	}

	/* 释放列表 */
	sp_free_port_list(list_ptr);
	return 0;
}

static void ComFarme_WaitList(COMFARME_LIST *data)
{
	if (data == NULL)
	{
		return;
	}
	ComFarme_WaitList((COMFARME_LIST *)data->next);
	uv_thread_join(&(data->thread_communicate));
}

static void ComFarme_CheckCom(void* arg)
{
	COMFARME_HANDLE *handle = (COMFARME_HANDLE *)arg;
	while (handle->comfarme_switch == COMFARME_RUN)
	{
		ComFarme_GetCom(handle);
		sleep(COMFARME_LOOPTIME);
	}

	/* 等待结束 */
	ComFarme_WaitList(handle->list);
}

int ComFarme_NewHandle(void **handle)
{
	int ret = 0;
	if (*handle != NULL)
	{
		return -1;
	}

	COMFARME_HANDLE *data = (COMFARME_HANDLE *)malloc(sizeof(COMFARME_HANDLE));
	memset(data, 0, sizeof(COMFARME_HANDLE));
	data->comfarme_switch = COMFARME_RUN;
	pthread_mutex_init(&data->list_lock, NULL);

	ret = uv_thread_create(&(data->thread_findcom), ComFarme_CheckCom, data);
	if (ret)
	{
		free(data);
		return -1;
	}

	*handle = data;
	return 0;
}

void ComFarme_FreeHandle(void *handle)
{
	COMFARME_HANDLE *data = (COMFARME_HANDLE *)handle;

	data->comfarme_switch = COMFARME_STOP;
	ComFarme_WaitHandle(handle);
	pthread_mutex_destroy(&data->list_lock);
	free(data);
}

int ComFarme_WaitHandle(void *handle)
{
	COMFARME_HANDLE *data = (COMFARME_HANDLE *)handle;
	return uv_thread_join(&(data->thread_findcom));
}

框架分点讲解:

框架入口:

【ComFarme_NewHandle】函数为框架入口,主要功能是启动监听服务器串口的线程【uv_thread_create(&(data->thread_findcom), ComFarme_CheckCom, data);】。

监听服务器串口

【ComFarme_CheckCom】函数的功能定时监听服务器串口,每隔【COMFARME_LOOPTIME】秒监听一次;

【ComFarme_GetCom】函数的功能为检查是否有新增串口,【handle->config_ptr】为串口配置,可以在该函数中设置串口波特率、奇偶校验等参数,通过【sp_list_ports】函数获取当前服务器串口情况,并通过【ComFarme_ListCompare】函数判断当前串口是否已添加进链表,你可以在【ComFarme_ListCompare】函数过滤串口,比如本工程中在【ComFarme_ListCompare】函数中添加了下列代码,表示只对【ttyUSB*】提供串口读写服务,这块根据个人需求进行修改,如果你需要对所有串口提供服务,则可以删除该段:

	/* 过滤COM口 */
	if (memcmp(sp_get_port_name(list_ptr), CONFARME_ADAPTER, strlen(CONFARME_ADAPTER)) != 0)
	{
		//printf("@ComFarme_ListCompare -1 @len %d\n", strlen(CONFARME_ADAPTER));
		return -1;
	}

 

如果是新串口,则通过【ComFarme_ListAdd】函数添加进链表;

添加链表

【ComFarme_ListAdd】函数功能是将新串口添加进链表,并通过【ComFarm_EventAdd】函数,开启新的线程,该线程为新串口提供读写服务。

读写服务

【ComFarm_EventAdd】函数功能是为一个串口开启读写服务线程,【ComFarme_Communicate】函数主要功能是为串口提供读写服务;

【ComFarme_Communicate】函数,先通过【sp_open】函数将【ComFarme_ListAdd】函数传入的新串口打开,而后通过【sp_set_config】函数将串口波特率等参数进行设置,通过【sp_new_event_set】创建触发器,并通过【sp_add_port_events】函数将该串口的读写及插拔设置为触发事件,而后通过【sp_wait】函数进行阻塞,等待事件的发生。

当该串口数据传入服务器后,通过【sp_nonblocking_read】函数进行数据读取,通过【sp_blocking_write】函数进行数据回复。

当该串口被拔走后,则调用【ComFarme_ListDelete】函数,将该串口从链表中删除。

框架出口:

调用【ComFarme_FreeHandle】函数,可以结束该框架。

首先通过【data->comfarme_switch = COMFARME_STOP】语句将框架标志位设置为停止,然后等待【ComFarme_CheckCom】线程函数正常退出。

在【ComFarme_CheckCom】线程函数中【ComFarme_WaitList】函数的功能是等待链表中的串口服务线程【ComFarme_Communicate】正常退出。由于【ComFarme_Communicate】函数中【sp_wait】阻塞,因此只有串口发生通信或者串口被拔出后,【ComFarme_Communicate】函数才能正常退出。我在使用中,是先拔出串口再结束程序,如果该框架在你程序中只开启关闭一次,则可以在【ComFarme_CheckCom】线程函数删除下面这条语句:

	/* 等待结束 */
	ComFarme_WaitList(handle->list);

 

 

 

 

  • 2
    点赞
  • 10
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值