一个用于小型嵌入式系统各单元之间(STM32,迪文屏,其他)的数据同步框架(基于rt_thread 开发,带数据安全,适用于嵌入式操作系统以及状态机裸机开发)

本文主要是介绍一个用于小型嵌入式系统各单元之间(STM32,迪文屏,其他)的数据同步框架(基于rt_thread 开发,带数据安全,适用于嵌入式操作系统以及状态机裸机开发),希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

前言(作者与读者的唠嗑):

        

        在项目开发时,我们或许有一个这样的苦恼,项目由多个单元组成,一起协同完成一件事情,这时候就需要同步各个单元之间的数据了,这就头疼了,这个工作量不是一般的巨大,相信有过类似经验的应该都懂☺!而且每个单元之间耦合性非常高,稍微改一点那就是伤筋动骨;或者还有人开发过迪文屏,如果用这个做一个比较复杂的项目,相信开发过的就知道重复代码有多少,工作量有多大,没错这个框架最初是为迪文屏而生的,不过后来某个项目里面又有迪文屏又有安卓屏,各种屏,各种之间的数据要相互同步,这就头疼了,于是这个框架就开始进化了。

        你可能一看这个图你会说,难道你不会用MODBUS吗?读者朋友仔细看,博主不是傻子,这么说坑定有原因的;看这个双箭头你就知道,数据是双向同步的,modbus?主从?我一个个去读?去写?脑壳有泡?真的适用吗?而且存在多个控制器执行器,也就是说最原始的主从几乎不适用,当然你要愿意可以去魔改modbus,不过你会后悔的,作为过来人告诉你?这个类似于一个星型网络,虚线代表间接控制,对于这个通信,硬件给的解决方案是CAN,不一定要用can,用多几个串口再加上我这个框架一样能解决。

        废话不多说,再来一句:在我长达两年半的嵌入式开发历程中,这个框架的诞生让我工作效率更高,摸鱼的时间也变多了,提前干完不就可以摸鱼了吗?摸鱼才是自己挣得,拿摸鱼的时间去打篮球不好吗?框架的优点:体量小,能减少大部分工作量,便于维护升级,移植性强,健壮(其实还不够健壮,还在进一步探索新的功能)。

正文

1.框架优点

        1).通信接口独立,可自由定义通信接口(发送、接收)。

        2).可自定义通信协议,兼容串口屏通信协议,需要做一些处理,内嵌默认的通信协议(兼容迪文串口屏),可满足绝大部分需求,如需定制可联系作者。

        3).每一个同步块由管理者管理,互不干扰,一个模块允许存在多个同步块,每个同步块之间提供接口交换数据。

        4).使用方法简单,学习成本低,上手就会,最大程度缩减数据同步的代码量。

        5).耦合性低,维护方便,只需更改同步数据列表就可以自动同步相关数据,这个主要是考虑到如果一个模块修改了一部分,另一个也要跟着大动干戈,这个框架很好的避免了这一点。

        6).唯一性,目前这个框架网上你找不到对应的解决办法,嘿嘿,绝无仅有。

        7).优点太多,就不一一介绍了......

2.主体说明

         af_sync_admin:这就是数据同步的主体,管理所有的数据同步,以及同步回调、重发机制、数据发送,接收。    

1). af_unit_body * body_list       

数据同步列表,你需要同步的数据需提前注册到这个列表,列表的单体组成:

typedef struct
{afu8 name[16];     		//地址名 afu16 addr; 	    	//地址 af_type_cfg * type_cfg; //数据类型 AF_DATA_TYPE data;    	//默认值,数据缓冲区,如果是数组类型,存储的就是buffer首地址,其他就是对应的具体值afu8 rush_flag;    		//刷新标志,上电是否自动同步数据application handle;     //外部数据同步回调,可用于处理发来的数据以及其他操作,这里特别注意,u32、u16、ubutton 与 其他类型使用方法不一样,详见附件说明}af_unit_body;

af_type_cfg 数据类型在下面会有介绍,这里暂时跳过;application 回调接口用于处理接收到的数据看是否合法,或者执行一些其他操作;这里每个不同的数据类型,传入的参数不一样,在线免会有详细介绍。

       2).  af_write_data ,af_error

        数据发送接口:

typedef void (* af_write_data)(afu16 addr, afu8 cmd, afu8 * buffer, int len); //外部通信接口

        错误处理接口,这里目前只开发了一个同步超时错误,后续在进一步添加

typedef void (*af_error)(af_error_type error);   //错误处理接口

         3).  af_sync_cfg * cfg_list

        重发机制列表,目前所有数据只支持一种重发配置,超时时间与重试次数,数据列表中的数据共用一种配置。


typedef struct
{afu8 rush_state; //刷新状态 0:刷新成功 1:刷新失败afu16 out_time;  //超时时间afu16 retry_time;//重试次数afu16 cur_time;  //内部计数用,无需配置afu16 cur_rett;  //内部计数用,无需配置
}af_sync_cfg; 		 //超时判断机制

        4).rt_mutex_t

                互斥量,用于数据安全,如换成其他操作系统可更换

        数据类型,目前提供了六种数据类型:

typedef enum{uInt16,uInt32, uString,//字符串类型,必须有结束符uButton,//uButton是uInt16 阉割了上传功能,也就是说只接受外部写,不支持主动写外部uForm,  //暂不支持表格,可以用uRow代替uRow
}af_type;

    uForm 实现比较复杂,暂时为开发,如有需要可以联系我单独制定。    

     uRow 相当于excle表的一行,也可以理解为数组,这也是为了简化开发。有很多需要注意的地方,这里为了适配迪文屏,这个类型做了一点点的定制,就是说这个类型占用了一片连续的地址,可以一次性接收这一行的所有数据,也可以接收处于这个地址范围内的单个数据或者多个数据。数据类型组成如下:(注:uint_byte:指的是单个数据占用的地址个数,type:只能为u16和u32,支不支持其他数据类型)

   其他数据类型不做过多的介绍。     除了u16, u32, button这三种类型外,其他类型都需要用户自己开辟缓存区。

typedef enum{uInt16,uInt32, uString,//字符串类型,必须有结束符uButton,//uButton是uInt16 阉割了上传功能,也就是说只接受外部写,不支持主动写外部uForm,  //暂不支持表格,可以用uRow代替uRow
}af_type;typedef struct
{int horizontal;	//横int vertical;	//竖af_type type;   //单元格数据类型char uint_byte; //单个元素所占的地址个数
}af_type_form; 		//暂时未完善相关代码,待后续更新typedef struct
{int horizontal; //横af_type type;   //单元格数据类型char uint_byte; //单个元素所占的地址个数
}af_type_row;typedef struct
{int len;    	//字符串长度
}af_type_string;typedef struct{af_type type;   //类型void * cfg;     //类型相关配置
}af_type_cfg;

3.。。。

4.源码介绍

af_sync.h

#ifndef __AF_SYNC_H
#define __AF_SYNC_H
#include "main.h"#define AF_DATA_TYPE afu32typedef unsigned int afu32;
typedef unsigned short afu16;
typedef unsigned char afu8;typedef AF_DATA_TYPE (*application)(AF_DATA_TYPE data, AF_DATA_TYPE data2);#define afu32_buffer_min(x) (afu32)(*((x+3))<<24 | *((x)+2)<<16 | *((x)+1)<<8 | *(x))
#define afu32_buffer_max(x) (afu32)(*(x)<<24 | *((x)+1)<<16 | *((x)+2)<<8 | *((x)+3))#define afu16_buffer_min(x) (afu16)(*((x)+1)<<8 | *(x))
#define afu16_buffer_max(x) (afu16)(*(x)<<8 | *(x+1))#define buffer_afu32_min(x, buf) {(buf)[0] = (x); (buf)[1] = (x)>>8; (buf)[2] = (x)>>16; (buf)[3] = (x)>>24;}
#define buffer_afu32_max(x, buf) {(buf)[3] = (x); (buf)[2] = (x)>>8; (buf)[1] = (x)>>16; (buf)[0] = (x)>>24;}#define buffer_afu16_min(x, buf) {(buf)[0] = (x); (buf)[1] = (x)>>8;}
#define buffer_afu16_max(x, buf) {(buf)[1] = (x); (buf)[0] = (x)>>8;}typedef enum{uInt16,uInt32, uString,//字符串类型,必须有结束符uButton,//uButton是uInt16 阉割了上传功能,也就是说只接受外部写,不支持主动写外部uForm,  //暂不支持表格,可以用uRow代替uRow,uHeart
}af_type;typedef struct
{int horizontal;	//横int vertical;	//竖af_type type;   //单元格数据类型char uint_byte; //单个元素所占的地址个数
}af_type_form; 		//暂时未完善相关代码,待后续更新typedef struct
{int horizontal; //横af_type type;   //单元格数据类型char uint_byte; //单个元素所占的地址个数
}af_type_row;typedef struct
{int len;    	//字符串长度
}af_type_string;//只允许存在一个心跳类型!!!!
typedef struct
{int time;		//心跳时间int count;
}af_type_heart;typedef struct{af_type type;   //类型void * cfg;     //类型相关配置
}af_type_cfg;typedef struct
{afu8 rush_state; //刷新状态 0:刷新成功 1:刷新失败afu16 out_time;  //超时时间afu16 retry_time;//重试次数afu16 cur_time;  //内部计数用,无需配置afu16 cur_rett;  //内部计数用,无需配置
}af_sync_cfg; 		 //超时判断机制typedef struct
{afu8 name[16];     		//地址名 afu16 addr; 	    	//地址 af_type_cfg * type_cfg; //数据类型 AF_DATA_TYPE data;    	//数据缓冲区,如果是数组类型,存储的就是buffer首地址,其他就是对应的具体值afu8 rush_flag;    		//刷新标志 application handle;     //外部数据同步回调,可用于处理发来的数据以及其他操作,这里特别注意,u32、u16、ubutton 与 其他类型使用方法不一样,详见附件说明}af_unit_body;typedef enum
{AF_SYNC_NONE = 0,AF_SYNC_WRITE_REQUST = 0x82,  //写请求AF_SYNC_WRITE_REPORT = 0x28,  //写回复AF_SYNC_READ_REQUST  = 0x83,  //读请求AF_SYNC_READ_REPORT  = 0x38    //读回复
}af_message_mode;typedef enum
{AF_ERROR_OUTTIME,
}af_error_type; //错误码enum
{AF_NONE_FLAG,AF_WRITE_FLAG,AF_READ_FLAG,
}; //错误码enum
{AF_OFFLINE,AF_ONLINE,
};typedef void (* af_write_data)(afu16 addr, afu8 cmd, afu8 * buffer, int len); //外部通信接口
typedef void (* af_read_data)(afu16 addr, int len); //外部通信接口
typedef void (*af_error)(af_error_type error);   //错误处理接口
typedef void (*af_register)(afu16);   //设备注册回调
typedef void (*af_offline)(afu16);    //设备离线回调typedef struct{af_unit_body * body_list;af_sync_cfg * cfg_list;afu16 out_time;afu16 retry_time;int number;//body_list 数量struct rt_mutex rt_mutex_t;//数据安全接口af_write_data write;//外部通信接口af_error error;//错误处理接口afu8 reg_flag;af_register online;af_offline offline;
}af_sync_admin;//管理机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_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, int delay);
AF_DATA_TYPE af_uint_name_get(af_sync_admin * af, afu8 * name);#endif

af_sync.c

/**********************************************************************************Copyright(C) -*FileName: af_sync.c*Author: 我不是阿沸*Version: 6.1.0*Date: 2023.08.20*Description:  数据同步框架,目前接口功能比较完善,如遇问题可联系作者*Others:  使用接口前一定调用初始化接口,仅适配rt_thread,如需其他操作系统需修改部分代码  *History:  1.Date:2023/08/03Author:我不是阿沸 适配迪文屏2.Date:2023/08/16Author:我不是阿沸 适配can3.Date:2023/09/18Author:我不是阿沸 增加相关接口以及超时机制4.Date:2023/10/01Author:我不是阿沸 修复无限制发送bug5.Date:2023/10/31Author:我不是阿沸 增加在线离线事件回调接口以及心跳处理
**********************************************************************************/#include "af_sync.h"/*** @brief  通过变量名获取数据* @param  变量名* @param  变量值* @retval None*/
AF_DATA_TYPE af_uint_name_get(af_sync_admin * af, afu8 * name)
{AF_DATA_TYPE val = 0;for(int i = 0; i < af->number; i++){if(strcmp((const char *)name, (const char *)(af->body_list[i].name)) == 0){rt_mutex_take(&(af->rt_mutex_t), RT_WAITING_FOREVER);val = af->body_list[i].data;rt_mutex_release(&(af->rt_mutex_t));break;}}return val;
}/*** @brief  通过变量名置位向外读取数据标志* @param  变量名* @param  变量值* @retval None*/
void af_uint_name_update(af_sync_admin * af, afu8 * name)
{for(int i = 0; i < af->number; i++){if(strcmp((const char *)name, (const char *)(af->body_list[i].name)) == 0){rt_mutex_take(&(af->rt_mutex_t), RT_WAITING_FOREVER);af->body_list[i].rush_flag = AF_READ_FLAG;rt_mutex_release(&(af->rt_mutex_t));break;}}return;
}/*** @brief  通过名字同步两个同步管理机的数据* @param  变量名* @param  变量值* @retval None*/
void af_struct_name_sync(af_sync_admin * dec, af_sync_admin * src, afu8 * name)
{AF_DATA_TYPE val = 0;afu8 flag = 0;for(int i = 0; i < src->number; i++){if(strcmp((const char *)name, (const char *)(src->body_list[i].name)) == 0){rt_mutex_take(&(src->rt_mutex_t), RT_WAITING_FOREVER);val = src->body_list[i].data;flag = 1;rt_mutex_release(&(src->rt_mutex_t));break;}}if(flag == 0) return;for(int i = 0; i < dec->number; i++){if(strcmp((const char *)name, (const char *)(dec->body_list[i].name)) == 0){rt_mutex_take(&(dec->rt_mutex_t), RT_WAITING_FOREVER);af_unit_body * body = dec->body_list+i;af_type_cfg * type_cfg = (dec->body_list+i)->type_cfg;switch(type_cfg->type){case uButton:{body->data = val;}break;case uInt16:case uInt32:{if(body->data != val){body->rush_flag = AF_WRITE_FLAG;body->data = val;}}break;case uString:{af_type_string * _ustr = (af_type_string*)type_cfg->cfg;if(strcmp((const char *)body->data, (const char *)val) != 0){strncpy((char*)(body->data), (const char *)val, _ustr->len * sizeof(afu8));body->rush_flag = AF_WRITE_FLAG;}}break;case uRow:{af_type_row * _urow = (af_type_row*)type_cfg->cfg;switch(_urow->type){case uInt16:if(memcmp((const char *)body->data, (const char *)val, _urow->horizontal * sizeof(afu16)) != 0){memcpy((char*)(body->data), (const char *)val, _urow->horizontal * sizeof(afu16));body->rush_flag = AF_WRITE_FLAG;}break;case uInt32:if(memcmp((const char *)body->data, (const char *)val, _urow->horizontal * sizeof(afu32)) != 0){memcpy((char*)(body->data), (const char *)val, _urow->horizontal * sizeof(afu32));body->rush_flag = AF_WRITE_FLAG;}break;default:break;}}break;default:break;}rt_mutex_release(&(dec->rt_mutex_t));break;}}return;
}/*** @brief  通过地址同步两个同步管理机的数据* @param  变量名* @param  变量值* @retval None*/
void af_struct_addr_sync(af_sync_admin * dec, af_sync_admin * src, afu16 addr)
{AF_DATA_TYPE val = 0;afu8 flag = 0;for(int i = 0; i < src->number; i++){if(addr == src->body_list[i].addr){rt_mutex_take(&(src->rt_mutex_t), RT_WAITING_FOREVER);val = src->body_list[i].data;flag = 1;rt_mutex_release(&(src->rt_mutex_t));break;}}if(flag == 0) return;for(int i = 0; i < dec->number; i++){if(addr == dec->body_list[i].addr){rt_mutex_take(&(dec->rt_mutex_t), RT_WAITING_FOREVER);af_unit_body * body = dec->body_list+i;af_type_cfg * type_cfg = (dec->body_list+i)->type_cfg;switch(type_cfg->type){case uButton:{body->data = val;}break;case uInt16:case uInt32:{if(body->data != val){body->rush_flag = AF_WRITE_FLAG;body->data = val;}}break;case uString:{af_type_string * _ustr = (af_type_string*)type_cfg->cfg;if(strcmp((const char *)body->data, (const char *)val) != 0){strncpy((char*)(body->data), (const char *)val, _ustr->len * sizeof(afu8));body->rush_flag = AF_WRITE_FLAG;}}break;case uRow:{af_type_row * _urow = (af_type_row*)type_cfg->cfg;switch(_urow->type){case uInt16:if(memcmp((const char *)body->data, (const char *)val, _urow->horizontal * sizeof(afu16)) != 0){memcpy((char*)(body->data), (const char *)val, _urow->horizontal * sizeof(afu16));body->rush_flag = AF_WRITE_FLAG;}break;case uInt32:if(memcmp((const char *)body->data, (const char *)val, _urow->horizontal * sizeof(afu32)) != 0){memcpy((char*)(body->data), (const char *)val, _urow->horizontal * sizeof(afu32));body->rush_flag = AF_WRITE_FLAG;}break;default:break;}}break;default:break;}rt_mutex_release(&(dec->rt_mutex_t));break;}}return;
}/*** @brief  通过变量地址置位向外写数据标志* @param  变量地址* @param  变量值* @retval None*/
void af_uint_addr_set(af_sync_admin * af, afu16 addr, AF_DATA_TYPE val)
{for(int i = 0; i < af->number; i++){if(addr == af->body_list[i].addr){rt_mutex_take(&(af->rt_mutex_t), RT_WAITING_FOREVER);af_unit_body * body = af->body_list+i;af_type_cfg * type_cfg = (af->body_list+i)->type_cfg;switch(type_cfg->type){case uButton:{body->data = val;}break;case uInt16:case uInt32:{if(body->data != val){body->data = val;body->rush_flag = AF_WRITE_FLAG; }}break;case uString:{af_type_string * _ustr = (af_type_string*)type_cfg->cfg;if(strcmp((const char *)body->data, (const char *)val) != 0){strncpy((char*)(body->data), (const char *)val, _ustr->len * sizeof(afu8));body->rush_flag = AF_WRITE_FLAG;}}break;case uRow:{af_type_row * _urow = (af_type_row*)type_cfg->cfg;switch(_urow->type){case uInt16:if(memcmp((const char *)body->data, (const char *)val, _urow->horizontal * sizeof(afu16)) != 0){memcpy((char*)(body->data), (const char *)val, _urow->horizontal * sizeof(afu16));body->rush_flag = AF_WRITE_FLAG;}break;case uInt32:if(memcmp((const char *)body->data, (const char *)val, _urow->horizontal * sizeof(afu32)) != 0){memcpy((char*)(body->data), (const char *)val, _urow->horizontal * sizeof(afu32));body->rush_flag = AF_WRITE_FLAG;}break;default:break;}}break;default:break;}rt_mutex_release(&(af->rt_mutex_t));break;}}
}/*** @brief  通过变量名置位向外写数据标志* @param  变量地址* @param  变量值* @retval None*/
void af_uint_name_set(af_sync_admin * af, afu8* name, AF_DATA_TYPE val)
{for(int i = 0; i < af->number; i++){if(strcmp((const char *)name, (const char *)(af->body_list[i].name)) == 0){rt_mutex_take(&(af->rt_mutex_t), RT_WAITING_FOREVER);af_unit_body * body = af->body_list+i;af_type_cfg * type_cfg = (af->body_list+i)->type_cfg;switch(type_cfg->type){case uButton:{body->data = val;}break;case uInt16:case uInt32:{if(body->data != val){body->rush_flag = AF_WRITE_FLAG;body->data = val;}}break;case uString:{af_type_string * _ustr = (af_type_string*)type_cfg->cfg;if(strcmp((const char *)body->data, (const char *)val) != 0){strncpy((char*)(body->data), (const char *)val, _ustr->len * sizeof(afu8));body->rush_flag = AF_WRITE_FLAG;}}break;case uRow:{af_type_row * _urow = (af_type_row*)type_cfg->cfg;switch(_urow->type){case uInt16:if(memcmp((const char *)body->data, (const char *)val, _urow->horizontal * sizeof(afu16)) != 0){memcpy((char*)(body->data), (const char *)val, _urow->horizontal * sizeof(afu16));body->rush_flag = AF_WRITE_FLAG;}break;case uInt32:if(memcmp((const char *)body->data, (const char *)val, _urow->horizontal * sizeof(afu32)) != 0){memcpy((char*)(body->data), (const char *)val, _urow->horizontal * sizeof(afu32));body->rush_flag = AF_WRITE_FLAG;}break;default:break;}}break;default:break;}rt_mutex_release(&(af->rt_mutex_t));break;}}
}/*** @brief  同步管理机的消息发送* @param  同步管理机* @param  同步的地址* @param  同步的数据* @param  同步的类型* @param  消息的类型、模式 读回复、写回复* @retval None*/
void af_sync_message_transmit(af_sync_admin * af, afu16 addr, afu32 data, af_type_cfg *type, af_message_mode mode)
{afu8 buffer[40] = {0};int len = 0;switch(mode){case AF_SYNC_WRITE_REQUST:case AF_SYNC_READ_REPORT:{switch(type->type){case uInt16:{buffer_afu16_max(data, buffer);len = 2;}break;case uInt32:{buffer_afu32_max(data, buffer);len = 4;}break;case uString:{len = strlen((char *)(data));memcpy(buffer, (unsigned char *)data, len);}break;case uForm:;break;case uRow:{af_type_row * row = (af_type_row*)type->cfg;for(int i = 0; i < row->horizontal; i++){//这里注意迪文屏一个地址占用两个字节if(row->type == uInt32){if(i*sizeof(afu32) >= 40) break;afu32 _data = *((afu32*)data+i);buffer_afu32_max(_data, buffer+i*sizeof(afu32));}else{if(i*sizeof(afu16) >= 40) break;afu16 _data = *((afu16*)data+i);//这个地方注意修buffer_afu16_max(_data, buffer+i*sizeof(afu16));}}if(row->type == uInt32) len = row->horizontal*sizeof(afu32);else len = row->horizontal*sizeof(afu16);}break;default:break;}}break;default:break;}if(AF_SYNC_WRITE_REQUST == mode) af->write(addr, AF_SYNC_WRITE_REQUST, buffer, len);if(AF_SYNC_READ_REPORT == mode) af->write(addr, AF_SYNC_READ_REPORT, buffer, len);
}/*** @brief  视图更新线程* @param  同步管理机* @retval None*/
void af_bodys_updata_handle(af_sync_admin * af)
{for(int i = 0; i < af->number; i++){af_unit_body * body =  af->body_list+i;af_sync_cfg * sync_cfg = NULL;rt_mutex_take(&(af->rt_mutex_t), RT_WAITING_FOREVER);if(af->cfg_list != NULL){sync_cfg = af->cfg_list+i;}afu32 addr = body->addr;afu32 data = body->data;if(body->rush_flag == AF_WRITE_FLAG){af_sync_message_transmit(af, addr, data, body->type_cfg, AF_SYNC_WRITE_REQUST);if(sync_cfg != NULL){sync_cfg->rush_state = AF_WRITE_FLAG;}body->rush_flag = AF_NONE_FLAG;}else if(body->rush_flag == AF_READ_FLAG){af_sync_message_transmit(af, addr, data, body->type_cfg, AF_SYNC_READ_REQUST);if(sync_cfg != NULL){sync_cfg->rush_state = AF_READ_FLAG;}body->rush_flag = AF_NONE_FLAG;}if(sync_cfg == NULL){rt_mutex_release(&(af->rt_mutex_t));continue;}if(sync_cfg->rush_state == AF_WRITE_FLAG || sync_cfg->rush_state == AF_READ_FLAG){sync_cfg->cur_time++;if(sync_cfg->cur_time > sync_cfg->out_time){sync_cfg->cur_rett++;sync_cfg->cur_time = 0;if(i == 0 && (af->offline != NULL || af->online != NULL)){if(af->reg_flag == AF_ONLINE){if(af->offline != NULL){af->offline(data);}af->reg_flag = AF_OFFLINE;}sync_cfg->rush_state = AF_NONE_FLAG;sync_cfg->cur_rett = 0;sync_cfg->cur_time = 0;//第一个uint body 默认为心跳体,超时会调用}else if(sync_cfg->rush_state == AF_WRITE_FLAG){af_sync_message_transmit(af, addr, data, body->type_cfg, AF_SYNC_WRITE_REQUST);}else if(sync_cfg->rush_state == AF_READ_FLAG){af_sync_message_transmit(af, addr, data, body->type_cfg, AF_SYNC_READ_REQUST);}}if(sync_cfg->cur_rett > sync_cfg->retry_time){if(af->error != NULL){af->error(AF_ERROR_OUTTIME);}/sync_cfg->cur_rett = 0;sync_cfg->cur_time = 0;sync_cfg->rush_state = AF_NONE_FLAG;}}rt_mutex_release(&(af->rt_mutex_t));}
}/*** @brief  心跳处理* @param  同步管理机* @param  进入心跳处理的间隔* @retval None*/
void af_heart_handle(af_sync_admin * af, int delay)
{af_unit_body * heart =  af->body_list;af_sync_cfg * cfg = af->cfg_list;af_type_heart * type = (af_type_heart*)heart->type_cfg->cfg;if(heart->rush_flag == AF_NONE_FLAG){if(type->count < type->time / delay){type->count++;}else{heart->rush_flag = AF_WRITE_FLAG;type->count = 0;}}
}/*** @brief  数据同步管理者初始化,初始化不包含重发机制的初始化,记得调用 af_sync_repeat_init* @param  视图列表* @param  视图列表条目* @retval None*/
void af_sync_admin_init(af_sync_admin * af,af_unit_body * list,int number,af_write_data write,af_error error)
{af->body_list = list;af->cfg_list = NULL;af->out_time = 0;af->retry_time = 0;af->number = number;af->write = write;af->error = error;af->reg_flag = AF_OFFLINE;af->offline = NULL;af->online = NULL;if(RT_EOK != rt_mutex_init( &(af->rt_mutex_t), "af syne mutex", RT_IPC_FLAG_FIFO)){return;}rt_mutex_release(&(af->rt_mutex_t));
}/*** @brief  视图初始化* @param  视图列表* @param  视图列表条目* @retval None*/
void af_sync_repeat_init(af_sync_admin * af, af_sync_cfg * cfg, afu16 out_time, afu16 retry_time)
{if(cfg == NULL) return;for(int i = 0; i < af->number; i++){(cfg+i)->out_time = out_time;(cfg+i)->retry_time = retry_time;(cfg+i)->cur_rett = 0;(cfg+i)->cur_time = 0;(cfg+i)->rush_state = 0;}af->cfg_list = cfg;return;
}/*** @brief  注册初始化,用于设备的在线和离线* @param  注册事件回调* @param  离线事件回调* @retval None*/
void af_sync_register_init(af_sync_admin * af, af_register reg, af_offline off)
{if(af == NULL) return;af->online = reg;af->offline = off;return;
}/*** @brief  用于接受外部发来的数据同步消息处理* @param  同步管理机* @param  事件* @param  数据,大端模式* @param  数据长度(字节),uString 数据类型的子类型为u8,所以u8为一个字, uRow子类型如果是u16 那u16就是一个字,以此类推* @retval None*/
void af_sync_recv_handle(af_sync_admin * af, afu8 cmd, afu16 addr, afu8 * buffer, afu16 len)
{afu32 data;af_unit_body * vi = NULL;af_type_cfg * type_cfg = NULL;af_sync_cfg * sync_cfg = NULL;for(int i = 0; i < af->number; i++){if(addr == af->body_list[i].addr && af->body_list[i].type_cfg->type != uRow && af->body_list[i].type_cfg->type != uForm){rt_mutex_take(&(af->rt_mutex_t), RT_WAITING_FOREVER);vi = af->body_list+i;type_cfg = vi->type_cfg;if(af->cfg_list != NULL){sync_cfg = af->cfg_list+i;}switch(cmd){//写请求和读回复,一个需要回复,一个不需要回复case AF_SYNC_WRITE_REQUST:case AF_SYNC_READ_REPORT:{switch(type_cfg->type){case uString:if(vi->handle != NULL){data = vi->handle((afu32)buffer, vi->data);}af_type_string * _ustr = (af_type_string*)type_cfg->cfg;if(strcmp((const char *)vi->data, (const char *)data) != 0){strncpy((char*)(vi->data), (const char *)data, _ustr->len * sizeof(afu8));}//break;case uInt32:data = afu32_buffer_max(buffer);if(vi->handle != NULL){data = vi->handle(data, vi->data);}vi->data = data;//break;case uInt16:data = afu16_buffer_max(buffer);if(vi->handle != NULL){data = vi->handle(data, vi->data);}vi->data = data;//break;case uButton:data = afu16_buffer_max(buffer);if(vi->handle != NULL){data = vi->handle(data, vi->data);}vi->data = data;//break;default:break;}if(cmd == AF_SYNC_WRITE_REQUST) //写请求{af->write(addr, AF_SYNC_WRITE_REPORT, NULL, 0);}else //读回复,需要清空标志位{if(sync_cfg != NULL){//rt_kprintf("AF_SYNC_WRITE_REPORT\n");sync_cfg->rush_state = AF_NONE_FLAG;sync_cfg->cur_rett = 0;sync_cfg->cur_time = 0;}}}break;//写回复,不许要回复,清空标志位就可case AF_SYNC_WRITE_REPORT:{if(sync_cfg != NULL){rt_kprintf("can 1 write report\n");sync_cfg->rush_state = AF_NONE_FLAG;sync_cfg->cur_rett = 0;sync_cfg->cur_time = 0;}}break;//读请求,需要回复,与写请求的发送类似case AF_SYNC_READ_REQUST:{af_sync_message_transmit(af, addr, vi->data, type_cfg, AF_SYNC_READ_REPORT);}break;}rt_mutex_release(&(af->rt_mutex_t));}//需要完善else if(af->body_list[i].type_cfg->type == uRow){vi = af->body_list+i;type_cfg = vi->type_cfg;if(af->cfg_list != NULL){sync_cfg = af->cfg_list+i;}af_type_row * row = (af_type_row *)type_cfg->cfg;afu32 sta = vi->addr;afu32 end = sta + row->horizontal * row->uint_byte;if(addr >= sta && addr < end) //地址处于row的地址范围{afu16 index = (addr - sta) / row->uint_byte;rt_mutex_take(&(af->rt_mutex_t), RT_WAITING_FOREVER);switch(cmd){case AF_SYNC_WRITE_REQUST:case AF_SYNC_READ_REPORT:{switch(row->type){case uInt16:{afu16 *ptr = (afu16*)(vi->data);//row类型接收到的值可能为多个,循环判断接收到的值//rt_kprintf("recive val sum: %d\n", len / sizeof(afu16));for(int m = 0; m < len / sizeof(afu16); m++){data = afu16_buffer_max((buffer+m*sizeof(afu16)));if(vi->handle != NULL){//这里有点不一样,普通的u16和u32异步回调处理传送的是更新值和历史值,//当类型为row时传送更新值和索引;data = vi->handle(data, index+m);}ptr[index+m] = data;}}break;case uInt32:{afu32 *ptr = (afu32*)(vi->data);//row类型接收到的值可能为多个,循环判断接收到的值//rt_kprintf("recive val sum: %d\n", len / sizeof(afu32));for(int m = 0; m < len / sizeof(afu32); m++){data = afu32_buffer_max((buffer+m*sizeof(afu32)));//rt_kprintf("recive val: %02X %02X %02X %02X, data:%d\n", buffer[0], buffer[1], buffer[2], buffer[3], data);if(vi->handle != NULL){//这里有点不一样,普通的u16和u32异步回调处理传送的是更新值和历史值,//当类型为row时传送更新值和索引;data = vi->handle(data, index+m);}//rt_kprintf("index: %d\n", index);ptr[index+m] = data;}}break;default:break;}if(cmd == AF_SYNC_WRITE_REQUST){if(sync_cfg != NULL){af_sync_message_transmit(af, addr, data, type_cfg, AF_SYNC_WRITE_REPORT);}}}break;case AF_SYNC_WRITE_REPORT:{if(sync_cfg != NULL){//rt_kprintf("AF_SYNC_WRITE_REPORT\n");sync_cfg->rush_state = 0;}}break;case AF_SYNC_READ_REQUST:{af_sync_message_transmit(af, addr, vi->data, type_cfg, AF_SYNC_READ_REPORT);}break;}rt_mutex_release(&(af->rt_mutex_t));}else{vi = NULL;type_cfg = NULL;}}else if(af->body_list[i].type_cfg->type == uForm){vi = af->body_list+i;type_cfg = vi->type_cfg;//待完善!!!!!!!!!!!!!!!!!!!!!!暂时没有这个功能,太过于复杂}else if(af->body_list[i].type_cfg->type == uHeart){data = afu32_buffer_max(buffer);if(af->reg_flag != AF_ONLINE){if(af->online != NULL){af->online(data);}}vi =  af->body_list;af_type_heart * type = (af_type_heart*)vi->type_cfg->cfg;type->count = 0;af->reg_flag = AF_ONLINE;}}
}

5.实际应用

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

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);}
}

结尾

因工作原因,文章只能一天更新一点,见谅!!!995的苦不知道你们懂不懂

这篇关于一个用于小型嵌入式系统各单元之间(STM32,迪文屏,其他)的数据同步框架(基于rt_thread 开发,带数据安全,适用于嵌入式操作系统以及状态机裸机开发)的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/339356

相关文章

详谈redis跟数据库的数据同步问题

《详谈redis跟数据库的数据同步问题》文章讨论了在Redis和数据库数据一致性问题上的解决方案,主要比较了先更新Redis缓存再更新数据库和先更新数据库再更新Redis缓存两种方案,文章指出,删除R... 目录一、Redis 数据库数据一致性的解决方案1.1、更新Redis缓存、删除Redis缓存的区别二

Redis事务与数据持久化方式

《Redis事务与数据持久化方式》该文档主要介绍了Redis事务和持久化机制,事务通过将多个命令打包执行,而持久化则通过快照(RDB)和追加式文件(AOF)两种方式将内存数据保存到磁盘,以防止数据丢失... 目录一、Redis 事务1.1 事务本质1.2 数据库事务与redis事务1.2.1 数据库事务1.

基于Qt开发一个简单的OFD阅读器

《基于Qt开发一个简单的OFD阅读器》这篇文章主要为大家详细介绍了如何使用Qt框架开发一个功能强大且性能优异的OFD阅读器,文中的示例代码讲解详细,有需要的小伙伴可以参考一下... 目录摘要引言一、OFD文件格式解析二、文档结构解析三、页面渲染四、用户交互五、性能优化六、示例代码七、未来发展方向八、结论摘要

Oracle Expdp按条件导出指定表数据的方法实例

《OracleExpdp按条件导出指定表数据的方法实例》:本文主要介绍Oracle的expdp数据泵方式导出特定机构和时间范围的数据,并通过parfile文件进行条件限制和配置,文中通过代码介绍... 目录1.场景描述 2.方案分析3.实验验证 3.1 parfile文件3.2 expdp命令导出4.总结

更改docker默认数据目录的方法步骤

《更改docker默认数据目录的方法步骤》本文主要介绍了更改docker默认数据目录的方法步骤,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一... 目录1.查看docker是否存在并停止该服务2.挂载镜像并安装rsync便于备份3.取消挂载备份和迁

什么是cron? Linux系统下Cron定时任务使用指南

《什么是cron?Linux系统下Cron定时任务使用指南》在日常的Linux系统管理和维护中,定时执行任务是非常常见的需求,你可能需要每天执行备份任务、清理系统日志或运行特定的脚本,而不想每天... 在管理 linux 服务器的过程中,总有一些任务需要我们定期或重复执行。就比如备份任务,通常会选在服务器资

不删数据还能合并磁盘? 让电脑C盘D盘合并并保留数据的技巧

《不删数据还能合并磁盘?让电脑C盘D盘合并并保留数据的技巧》在Windows操作系统中,合并C盘和D盘是一个相对复杂的任务,尤其是当你不希望删除其中的数据时,幸运的是,有几种方法可以实现这一目标且在... 在电脑生产时,制造商常为C盘分配较小的磁盘空间,以确保软件在运行过程中不会出现磁盘空间不足的问题。但在

mysql重置root密码的完整步骤(适用于5.7和8.0)

《mysql重置root密码的完整步骤(适用于5.7和8.0)》:本文主要介绍mysql重置root密码的完整步骤,文中描述了如何停止MySQL服务、以管理员身份打开命令行、替换配置文件路径、修改... 目录第一步:先停止mysql服务,一定要停止!方式一:通过命令行关闭mysql服务方式二:通过服务项关闭

TP-LINK/水星和hasivo交换机怎么选? 三款网管交换机系统功能对比

《TP-LINK/水星和hasivo交换机怎么选?三款网管交换机系统功能对比》今天选了三款都是”8+1″的2.5G网管交换机,分别是TP-LINK水星和hasivo交换机,该怎么选呢?这些交换机功... TP-LINK、水星和hasivo这三台交换机都是”8+1″的2.5G网管交换机,我手里的China编程has

在 VSCode 中配置 C++ 开发环境的详细教程

《在VSCode中配置C++开发环境的详细教程》本文详细介绍了如何在VisualStudioCode(VSCode)中配置C++开发环境,包括安装必要的工具、配置编译器、设置调试环境等步骤,通... 目录如何在 VSCode 中配置 C++ 开发环境:详细教程1. 什么是 VSCode?2. 安装 VSCo