嵌入式系统各单元数据同步框架适用于网状,线性通信网络,适用于嵌入式操作系统以及状态机裸机开发

AF_SYNC通信架构

  • 前言

af_sync数据同步框架,旨在解决复杂通信问题;在强度稍微高一点的项目,可能有五个模块甚至更多,你可能使用modbus协议来解决各模块的通信问题,但是这只适用于主从场景,在需要双向通信的场景就没那么适用了,这是这个框架诞生的意义。

  • 应对场景

场景一:A->B;  A->C;  B->A->C; C->A->B

场景二:A->B->C->A

场景三:A->B->C; C->B->A; B->A;

主要就是这三种场景,其他的场景都是基于这三个场景组合扩展出来更复杂的通信架构,这个架构的优点在于:能规范通信代码架构,方便后期维护;减少程序员开发时间,迅速构建通信方案;只需进行简单配置即可实现复杂的通信;通过地址和字符串绑定数据,方便快速添加数据源,非常灵活。缺点:列表形式进行轮询比对,会损失一部分效率;地址只是一个标识符,不是物理地址,与modbus有本质区别,不支持一条指令写多个地址,但是一个地址可自由定义长度;容易出现通信环路;这些都是待解决优化的地方,下面也会对应进行说明。

  • 源码介绍

代码由三部分构成,发送,赋值,接收;发送部分主要是轮询地址列表发送数据请求至外部设备;赋值部分提供了变量名赋值和地址赋值接口;接收部分主要是负责接收外部设备的数据请求。支持线程安全,也可以裸机运行。

1.几个重要的宏定义

#define AF_NAME_LEN 20

#define AF_MAX_TRBUFF   100

AF_NAME_LEN:数据名最大长度。

AF_MAX_TRBUFF:数据发送最大缓冲区

  1. 支持传输的数据类型

数据类型

说明

长度

uInt16

16位无符号短整型变量

2字节

uInt32

32位无符号短整型变量

4字节

uString

8位无符号数组

自定义长度

uVoid

自定义类型

自定义长度

uButton

按键类型,16位无符号短整型变量,收到写请求,会回发写请求,可用于更新上位机操作状态。

2字节

uForm

表格类型,支持16位无符号短整型变量和32位无符号短整型变量

自定义长度

uRow

数组行类型,支持16位无符号短整型变量和32位无符号短整型变量

自定义长度

uHeart

心跳类型,16位无符号短整型变量

2字节

  1. 支持的请求

AF_SYNC_WRITE_REQUEST

写请求

0x82

AF_SYNC_WRITE_REPORT

写回复

0x28

AF_SYNC_READ_REQUEST

读请求

0x83

AF_SYNC_READ_REPORT

读回复

0x38

  1. 类型结构体说明

结构体名称

描述

成员变量

af_type_form

用于描述单元格的位置和数据类型。

- horizontal: 单元格的横坐标<br>- vertical: 单元格的纵坐标<br>- type: 自定义类型 af_type,表示单元格的数据类型

af_type_void

用于描述自定义类型的长度。

- len: 自定义类型的长度

af_type_row

用于描述行的位置和数据类型。

- horizontal: 行的横坐标<br>- type: 自定义类型 af_type,表示行的数据类型

af_type_string

用于描述字符串的长度。

- len: 字符串的长度

af_type_heart

用于描述心跳数据。

- time: 心跳时间<br>- count: 心跳次数

af_type_cfg

用于描述数据类型和相关配置。

- type: 自定义类型 af_type,表示数据的类型<br>- cfg: 指针,指向与该类型相关的配置信息

  1. 以下是对 af_sync_admin管理机 结构体的说明

成员变量

描述

成员变量

body_list

指向 af_unit_body 结构体数组的指针。

body_list

cfg_list

指向 af_sync_cfg 结构体数组的指针。

cfg_list

out_time

用于指定超时时间的 16 位无符号整数。

out_time

retry_time

用于指定重试时间的 16 位无符号整数。

retry_time

number

body_list 数组的元素数量。

number

rt_mutex_t

用于数据安全的实时线程互斥锁。

rt_mutex_t

write

外部通信接口。

write

error

错误处理接口。

error

reg_flag

注册标志,可能是 8 位无符号整数。

reg_flag

online

在线注册接口。

online

offline

离线注册接口。

offline

nonind

指向 af_nonind 结构体的指针。

nonind

alignment

对齐标志,可能是 8 位无符号整数。

alignment

af_sync_admin 结构体用于管理通信机制,它包含了各种用于数据同步和通信的配置、接口以及处理方法。

  1. 以下是对 af_sync_cfg 结构体的说明

成员变量

描述

成员变量

rush_state

刷新状态。0 表示刷新成功,1 表示刷新失败。

rush_state

out_time

超时时间,用于指定超时时长的 16 位无符号整数。

out_time

retry_time

重试次数,用于指定重试次数的 16 位无符号整数。

retry_time

cur_time

内部计数器,用于内部计数,无需配置。

cur_time

af_sync_cfg 结构体用于定义超时判断机制的配置参数,包括刷新状态、超时时间和重试次数。cur_time 和 cur_rett 是内部计数器,不需要外部配置,可能用于内部计数或其他用途

  1. 接口说明

函数名

描述

void af_sync_aligment_init(af_sync_admin * af, af_alignment_mode mode);

初始化同步管理器的对齐模式。

void af_sync_register_init(af_sync_admin * af, af_register reg, af_offline off);

初始化同步管理器的注册和离线状态。

void af_sync_repeat_init(af_sync_admin * af, af_sync_cfg * cfg, afu16 out_time, afu16 retry_time);

初始化同步管理器的重复操作配置,包括超时时间和重试次数。

void af_sync_recv_handle(af_sync_admin * af, afu8 cmd, afu16 addr, afu8 * buffer, afu16 len);

处理接收到的同步数据。

void af_sync_admin_init(af_sync_admin * af, af_unit_body * list, int number, af_write_data write, af_error error);

初始化同步管理器,包括单元体列表、数量、写入数据函数和错误处理函数。

void af_sync_nonind_init(af_sync_admin * af, af_nonind * nonind);

初始化非指示符。

void af_bodys_updata_handle(af_sync_admin * af);

处理单元体更新。

void af_uint_addr_receive(af_sync_admin * af, afu16 addr, AF_DATA_TYPE val);

接收地址类型的数据。

void af_uint_name_receive(af_sync_admin * af, afu8 * name, AF_DATA_TYPE val);

接收名称类型的数据。

void af_struct_name_sync(af_sync_admin * dec, af_sync_admin * src, afu8 * name);

同步结构体中指定名称的成员。

void af_struct_addr_sync(af_sync_admin * dec, af_sync_admin * src, afu16 addr);

同步结构体中指定地址的成员。

void af_uint_addr_set(af_sync_admin * af, afu16 addr, AF_DATA_TYPE val);

设置地址类型的数据。

void af_uint_name_set(af_sync_admin * af, afu8* name, AF_DATA_TYPE val);

设置名称类型的数据。

void af_uint_name_update(af_sync_admin * af, afu8 * name);

更新名称类型的数据。

void af_heart_handle(af_sync_admin * af);

处理心跳数据。

AF_DATA_TYPE af_uint_name_get(af_sync_admin * af, afu8 * name);

获取名称类型的数据。

void af_uint_name_force_set(af_sync_admin * af, afu8* name, AF_DATA_TYPE val);

强制设置名称类型的数据。

void af_name_list_set(af_sync_admin * af, af_clist list, afu32 len, af_sync_flag flag);

设置名称列表。

  • 使用范例

 首先注册一个需要同步的数据表:数据名,数据地址, 数据类型, 初始值,是否上电同步, 数据同步事件回调,这张表双端都是一样的,执行端如果有需要就配置相应的回调。

af_unit_body can1_list[] = 
{
	"type",		0xFFFF,  &u32_can, Rf, 		0, NULL,
	"r_im2",	0x2006,  &u32_can, 0, 		0, NULL,
	"r_im3",	0x2008,  &u32_can, 0, 		0, NULL,
	"ctr_cqm1", 0x2002,  &u32_can, 100000,  0, NULL,
	"ctr_cqm2", 0x2004,  &u32_can, 100000,  0, NULL,
	"ad_cqm1",	0x4012,  &u32_can, 0, 		0, NULL,
	"ad_cqm2", 	0x4014,  &u32_can, 0, 		0, NULL,
	"cqm_en", 	0x4018,  &u32_can, 0, 		0, NULL,
	"gnd_en", 	0x4016,  &u32_can, 0, 		0, NULL,
	"temp", 	0x1010,  &u32_can, 0, 		0, NULL,
	"outv",		0x4000,  &u32_can, 0, 		0, NULL,
	"outi",		0x4002,  &u32_can, 0, 		0, NULL,
	"m_outr",	0x4004,  &u32_can, 0, 		0, NULL,
	"outw",		0x4006,  &u32_can, 0, 		0, NULL,
	"ini", 		0x4008,  &u32_can, 0, 		0, NULL,
	"inv", 		0x400a,  &u32_can, 0, 		0, NULL,
	"inw", 		0x400e,  &u32_can, 0, 		0, NULL,
	"vm3", 		0x400c,  &u32_can, 0, 		0, NULL,
	"vm4", 		0x401e,  &u32_can, 0, 		0, NULL,
};
#include "sync.h"
#include "after.h"
#include "serial.h"
#include "gather.h"
#include "serial.h"
#include "handle.h"

typedef enum
{
	Measure,
	Rf,
	PowerSupply,
	HandShank
}Module_Type;


typedef struct
{
	unsigned short id;
	Module_Type type;
}af_module;

#define MASTER_ID 0x56
af_module af_module_pool[20];

void af_data_send(uint8_t * data, int len)
{
	can1_transmit(data, len);
}


/**
  * @brief  封装32位变量数据
  * @param  需要发送的ID,如果需要发送至多个ID,这个ID为板载类型ID,每种类型的板子都会有一个独特的ID,设置为0xff
  * @param	变量值
  * @retval None
  */
void af_write_reg_requst(unsigned short addr, unsigned int data)
{
	uint8_t buffer[20]; 
	buffer[0] = AF_SYNC_WRITE_REQUST;
	buffer[1] = MASTER_ID;
	buffer[2] = addr / 256;
	buffer[3] = addr % 256;
	
	buffer[4] = data >> 24;
	buffer[5] = data >> 16;
	buffer[6] = data >> 8;
	buffer[7] = data & 0xff;
	
	af_data_send(buffer, 8);
}

/**
  * @brief  封装32位变量数据
  * @param  变量地址
  * @param	变量值
  * @retval None
  */
void af_write_reg_reply(unsigned short addr)
{
	uint8_t buffer[20]; 
	buffer[0] = AF_SYNC_WRITE_REPORT;
	buffer[1] = MASTER_ID;
	buffer[2] = addr / 256;
	buffer[3] = addr % 256;
	
	buffer[4] = 0;
	buffer[5] = 0;
	buffer[6] = 0;
	buffer[7] = 0;
	
	af_data_send(buffer, 8);
}

/**
  * @brief  封装32位变量数据
  * @param  需要发送的ID,如果需要发送至多个ID,这个ID为板载类型ID,每种类型的板子都会有一个独特的ID,设置为0xff
  * @param	变量值
  * @retval None
  */
void af_read_reg_requst(unsigned short addr)
{
	uint8_t buffer[20]; 
	buffer[0] = AF_SYNC_READ_REQUST;
	buffer[1] = MASTER_ID;
	buffer[2] = addr / 256;
	buffer[3] = addr % 256;
	
	buffer[4] = 0xff;
	buffer[5] = 0xff;
	buffer[6] = 0xff;
	buffer[7] = 0xff;
	
	af_data_send(buffer, 8);
}

/**
  * @brief  封装32位变量数据
  * @param  变量地址
  * @param	变量值
  * @retval None
  */
void af_read_reg_reply(unsigned char id, unsigned short addr, unsigned int data)
{
	uint8_t buffer[20]; 
	buffer[0] = AF_SYNC_READ_REPORT;
	buffer[1] = id;
	buffer[2] = addr / 256;
	buffer[3] = addr % 256;
	
	buffer[4] = data >> 24;
	buffer[5] = data >> 16;
	buffer[6] = data >> 8;
	buffer[7] = data & 0xff;
	af_data_send(buffer, 8);
}

void can1_tran_buffer(unsigned short addr, unsigned char cmd, unsigned char * data, int len)
{
	switch(cmd)
	{
		case AF_SYNC_WRITE_REQUST: 
		{
			afu32 val = afu32_buffer_max(data);
			af_write_reg_requst(addr, val);
		}
			break;
		case AF_SYNC_WRITE_REPORT:
			af_write_reg_reply(addr);
			break;
	}
}



af_type_cfg u16_can = {uInt16, NULL};
AfScny afCan = {0, 0, 100, 3, 0, 0};
af_type_cfg u32_can = {uInt32, NULL};



af_unit_body can1_list[] = 
{
	"type",		0xFFFF,  &u32_can, Rf, 		0, NULL,
	"r_im2",	0x2006,  &u32_can, 0, 		0, NULL,
	"r_im3",	0x2008,  &u32_can, 0, 		0, NULL,
	"ctr_cqm1", 0x2002,  &u32_can, 100000,  0, NULL,
	"ctr_cqm2", 0x2004,  &u32_can, 100000,  0, NULL,
	"ad_cqm1",	0x4012,  &u32_can, 0, 		0, NULL,
	"ad_cqm2", 	0x4014,  &u32_can, 0, 		0, NULL,
	"cqm_en", 	0x4018,  &u32_can, 0, 		0, NULL,
	"gnd_en", 	0x4016,  &u32_can, 0, 		0, NULL,
	"temp", 	0x1010,  &u32_can, 0, 		0, NULL,
	"outv",		0x4000,  &u32_can, 0, 		0, NULL,
	"outi",		0x4002,  &u32_can, 0, 		0, NULL,
	"m_outr",	0x4004,  &u32_can, 0, 		0, NULL,
	"outw",		0x4006,  &u32_can, 0, 		0, NULL,
	"ini", 		0x4008,  &u32_can, 0, 		0, NULL,
	"inv", 		0x400a,  &u32_can, 0, 		0, NULL,
	"inw", 		0x400e,  &u32_can, 0, 		0, NULL,
	"vm3", 		0x400c,  &u32_can, 0, 		0, NULL,
	"vm4", 		0x401e,  &u32_can, 0, 		0, NULL,
};




af_sync_admin can1_admin;
af_sync_cfg can1_sync_cfgs[sizeof(can1_list) / sizeof(af_unit_body)];



void can1_error(af_error_type error)
{
	rt_kprintf("can out time\n");
}


void can1_sync_init(void)
{
	///初始化数据同步管理者
	af_sync_admin_init(&can1_admin, can1_list, sizeof(can1_list) / sizeof(af_unit_body), can1_tran_buffer, can1_error);
	///初始化重发机制
	af_sync_repeat_init(&can1_admin, can1_sync_cfgs, 600, 3);
}


void af_updata_task(void * p)
{
	SYSTEMDELAY(3000);
	while(1)
	{
		af_bodys_updata_handle(&can1_admin);
		SYSTEMDELAY(1);
	}
}


extern af_sync_admin dw_admin;



void af_logic_task(void * p)
{
	
	while(1)
	{
		Electrical ele;
		get_electrical(&ele);
		ele.outi = af_uint_name_get(&can1_admin, (afu8*)"outi");
		ele.outv = af_uint_name_get(&can1_admin, (afu8*)"outv");
		
		ele.ini = af_uint_name_get(&can1_admin, (afu8*)"ini");
		ele.inv = af_uint_name_get(&can1_admin, (afu8*)"inv");			   
		ele.vm3 = af_uint_name_get(&can1_admin, (afu8*)"vm3");
		ele.vm4  = af_uint_name_get(&can1_admin, (afu8*)"vm4");
		
		af_struct_name_sync(&can1_admin, &dw_admin, (afu8*)"ctr_cqm1");
		af_struct_name_sync(&can1_admin, &dw_admin, (afu8*)"ctr_cqm2");
		af_struct_name_sync(&can1_admin, &dw_admin, (afu8*)"cqm_en");
		af_struct_name_sync(&can1_admin, &dw_admin, (afu8*)"gnd_en");

		
		af_uint_name_set(&dw_admin, (afu8*)"m_outi", (int)(ele.outi));
		af_uint_name_set(&dw_admin, (afu8*)"m_outv",(int)(ele.outv));
		af_uint_name_set(&dw_admin, (afu8*)"m_outw", (int)(ele.outi/1000*ele.outv));
		af_struct_name_sync(&dw_admin, &can1_admin, (afu8*)"m_outr");
		af_struct_name_sync(&dw_admin, &can1_admin, (afu8*)"ad_cqm1");
		af_struct_name_sync(&dw_admin, &can1_admin, (afu8*)"ad_cqm2");
		
		SYSTEMDELAY(50);
	};
}


/**
  * @brief  封装32位变量数据
  * @param  变量地址
  * @param	变量值
  * @retval None
  */
void after_sync_rx(void  * p)
{
	uint16_t addr = 0;	//地址缓存
	uint32_t data = 0;	//数据缓存
	uint16_t CRC_U16=0;
	uint8_t R_Crc_h,R_Crc_l;
	
	uint8_t buffer[50];
	
	int len = 0;
	for(;;)
	{
		if(can1_receive(buffer, &len))
		{
			uint8_t cmd = buffer[0];
			uint8_t id = buffer[1];
			uint32_t addr = afu16_buffer_max((buffer+2));
			uint32_t data = afu32_buffer_max((buffer+4));
			
//			for(int i = 0; i < len; i++)
//			{
//				rt_kprintf("%02X ", buffer[i]);
//			};
//			rt_kprintf("\n");
			switch(cmd)
			{
				case AF_SYNC_WRITE_REQUST:
					//rt_kprintf("addr:%04X, data:%d\n", addr, data);
					af_sync_recv_handle(&can1_admin, AF_SYNC_WRITE_REQUST, addr, buffer+4, 4);
					break;
				case AF_SYNC_WRITE_REPORT:
					
					//rt_kprintf("AF_SYNC_WRITE_REPORT\n");
					af_sync_recv_handle(&can1_admin, AF_SYNC_WRITE_REPORT, addr,NULL, 0);
					break;
				case AF_SYNC_READ_REQUST:
					af_sync_recv_handle(&can1_admin, AF_SYNC_READ_REQUST, addr, NULL, 0);
					break;
				case AF_SYNC_READ_REPORT:
					af_sync_recv_handle(&can1_admin, AF_SYNC_READ_REPORT, addr, buffer+4, 4);
					break;
			}
		}
		else
		{
			//rt_kprintf("error\n");
		}
		SYSTEMDELAY(1);
	}
}

  • 2
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 3
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值