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:数据发送最大缓冲区
- 支持传输的数据类型
数据类型 | 说明 | 长度 |
uInt16 | 16位无符号短整型变量 | 2字节 |
uInt32 | 32位无符号短整型变量 | 4字节 |
uString | 8位无符号数组 | 自定义长度 |
uVoid | 自定义类型 | 自定义长度 |
uButton | 按键类型,16位无符号短整型变量,收到写请求,会回发写请求,可用于更新上位机操作状态。 | 2字节 |
uForm | 表格类型,支持16位无符号短整型变量和32位无符号短整型变量 | 自定义长度 |
uRow | 数组行类型,支持16位无符号短整型变量和32位无符号短整型变量 | 自定义长度 |
uHeart | 心跳类型,16位无符号短整型变量 | 2字节 |
- 支持的请求
AF_SYNC_WRITE_REQUEST | 写请求 | 0x82 |
AF_SYNC_WRITE_REPORT | 写回复 | 0x28 |
AF_SYNC_READ_REQUEST | 读请求 | 0x83 |
AF_SYNC_READ_REPORT | 读回复 | 0x38 |
- 类型结构体说明
结构体名称 | 描述 | 成员变量 |
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: 指针,指向与该类型相关的配置信息 |
- 以下是对 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 结构体用于管理通信机制,它包含了各种用于数据同步和通信的配置、接口以及处理方法。
- 以下是对 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 是内部计数器,不需要外部配置,可能用于内部计数或其他用途。
- 接口说明
函数名 | 描述 |
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);
}
}
- 注意事项
- 版权说明 我不是阿沸: AF工具库,不断完善 (gitee.com)