本文主要是介绍一个用于小型嵌入式系统各单元之间(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 开发,带数据安全,适用于嵌入式操作系统以及状态机裸机开发)的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!