电子工程师技术服务社区
公告
登录
|
注册
首页
技术问答
厂商活动
正点原子
板卡试用
资源库
下载
文章
社区首页
文章
嵌入式网络设备一次盲点补报的开发经验(一)
分 享
扫描二维码分享
嵌入式网络设备一次盲点补报的开发经验(一)
物联网
450c4aed63d8393c
关注
发布时间: 2020-08-24
丨
阅读: 605
作者:良知犹存 转载授权以及围观:欢迎添加微信号:Conscience_Remains 总述 曾经开发的时候遇到这样一个情况,我们的设备是车载设备,在车辆行驶过程中需要实时上报车辆数据,但是由于用的网络端是2G网,在行驶过程中,会遇到信号不好导致网络中断的情况,最早时候因为配置联网状态机很快,重联网时间很小,我直接就选择重联网之后再进行上报。可是在实际使用过程中发现,基站的切换、网络信号不好都会导致网络中断,重联网机制也不是每次都很迅速。所以开始考虑将数据本地保存,等待联网成功之后再次进行补报。下面我就介绍一下具体的思路。 一、需求的落地 确认思路实现方式: 思路就代表未来代码执行的逻辑,所以第一步选择梳理逻辑。首先脑海中大致会出现两种数据结构,一种是栈排序;一种是队列。 顺道也简单介绍一下这两种数据结构: 下图是栈排序的示意图,看图说话,栈是CPU分配的一块内存,分成很多个小单元用来储存数据,栈有栈顶和栈底。 入栈的动作是,当数据来的时候从栈底开始进行储存,当储存的数据top指向的地址从栈底到了栈顶地址,那么意味着本次分配的栈空间都满了。 出栈的时候,数据从存放的最高处top开始pop,直至top指向的地址等于栈底的地址,那么说明栈空了。 ![](https://imgconvert.csdnimg.cn/aHR0cHM6Ly9tbWJpei5xcGljLmNuL3N6X21tYml6X3BuZy91Y2RZbUdYTGlhOThZN3QwQmx2Q1U1MDZmc3M2YzhHc0NlOG9wdWVBazlnRDNpYjFOVXNKU0h4UktsTThHczNEaHA0ekFZWXBqRXJBbzFNWjJEUEVPdTVRLzY0MA?x-oss-process=image/format,png) 队列有链表式有顺序表式,两者一个是动态分配内存一个是静态分配内存,但是对于数据的进出来说都是一样的,队列是一边设置为数据入队口,一边为数据出队口。 ![](https://imgconvert.csdnimg.cn/aHR0cHM6Ly9tbWJpei5xcGljLmNuL3N6X21tYml6X2pwZy91Y2RZbUdYTGlhOThZN3QwQmx2Q1U1MDZmc3M2YzhHc0N5bDRjQ0RGZTdOOFZpYmljVW8yenFGTlZldkVrOURJdUw4MWpBUU85NVZnZzJFcmJHMTNGZDJBZy82NDA?x-oss-process=image/format,png) 这两者最大区别就是栈是先入后出 (First input Last output),而队列是先入先出(FIrst input First output) 。 针对设备实际情况来看,盲点的时间长度是不可知的,也相当于说数据的长度是未知的。我们需要获知到最新的数据,而内存是有限的,所以我们需要在大量数据保存的时候把旧的数据不停的覆盖掉,保留最新的数据,并且第一时间上报最新的数据,综合考虑栈结构最适合我们。 确认平台控制分配内存大小: 操作平台:STM32 RAM大小:32K 因为不同的芯片不同内存不一样,可用于分配的内存不一样,例如这次我们要进行一个小时的盲点补报的数据保存,按照实时上报数据大小乘以频次,再乘以总数量计算得到我们需要至少25k的内存,又因为程序运行需要十多K的内存,芯片RAM总大小只有32K,最后经过测试22K的大小刚合适。 const int MaxCachesLen = 22 * 1024; const int MaxCachesCount = MaxCachesLen / sizeof(Message); g_Caches = (Message**)malloc(sizeof(Message*) * MaxCachesCount); 二、程序的介紹 昨天介绍从接到项目到最终选定实现的数据结构,下面我就来介绍一下实际的代码。 首先盲点补报的数据我们要清楚,是车辆行驶过程中,由于各种原因网络中断,导致数据无法实时上传,这个时候需要本地将车辆的一些信息保存,等待重联网成功再进行上报到服务器。 先建立一个可以支持数据保存的结构体:包括UTC时间和经纬度,以及车辆的一些基本信息 __packed typedef struct { u32 year :12; //年 u32 month :4;//月 u32 day :5;//日 u32 hour :5;//时 u32 min :6;//分 u8 sec :6;//秒 u8 res :2; }utcTypeDef; //UTC时间结构体定义 /@@*nmea_utc_time 此结构体达不到精度,会导致 UTC2Sec 函数计算错误*/ __packed typedef struct { utcTypeDef utc; u32 Longitude; u32 Latitude; u16 Speed; u16 RPM; }Message; 开始定义我们需要保存的并且分配的内存大小,这里有个提示分配的时候,因为是malloc,此时芯片中数据堆对应的内存被我们分配,所以分配的第一件事就是先设置好堆的大小。 首先我们使用的是双级指针(一级指针地址指向储存的数据,二级指针地址指向一级指针的地址),一次性分配的方法进行栈排序存储,利用所以第一步初始化栈的空间和头地址。 /@@*全局变量*/ const int MaxCachesLen = 22 * 1024; const int MaxCachesCount = MaxCachesLen / sizeof(Message); u32 g_CachesCount = 0; //缓存计数 Message** g_Caches =NULL;// malloc(sizeof(char*) * MaxCachesCount); void GpsCacheInit(void) { g_Caches = (Message**)malloc(sizeof(Message*) * MaxCachesCount);//MaxCachesCount);//最大是1408*16=22*1204数据量 //建立MaxCachesCount个缓存空间 首地址 if(NULL == g_Caches) { LOG("malloc g_cahce error\r\n"); //DEBUG }else{ LOG("malloc g_cahce suc\r\n"); //DEBUG } } int GetCachesTotalSize(void){ return sizeof(Message) * g_CachesCount; } 写入栈和出栈的函数: // 压入 u32 Push(Message item) { /@@*如果满了 就把栈底最后一条数据地址的地址清空,丢弃掉旧的数据 把栈底以上的地址重新排列*/ if (GetCachesTotalSize() >= MaxCachesLen || g_CachesCount >= MaxCachesCount) { free((g_Caches[g_CachesCount - 1]));//先free栈底 memmove(g_Caches + 1, g_Caches, sizeof(Message*) * (g_CachesCount-1)); } else { memmove(g_Caches + 1, g_Caches, sizeof(Message*) * g_CachesCount); g_CachesCount++; } /@@*分配新的地址的指针空间 存放到栈中*/ Message* newItem = (Message*)malloc(sizeof(Message)); if(NULL == newItem) { LOG("newItem NULL\r\n"); return 0; } else{ *newItem = item; g_Caches[0] = newItem; LOG("newItem success\r\n"); return 1; } } // 弹出 u32 Pop(Message* out) { if (!out || g_CachesCount == 0) return 0; *out = *(g_Caches[0]); free(g_Caches[0]);/@@*把栈顶的地址的指针数据释放掉*/ memmove(g_Caches, g_Caches + 1, sizeof(Message*) * (g_CachesCount-1)); g_CachesCount--; return 1; } 把行驶态的车辆信息打包压入栈中保存(有两种方式保存一种是把UTC转化成秒计数存放到内存,一种是利用数据结构保存到内存中,最后我选择了第一种保存方式) u8 InCache(void)/@@*缓存包*/ { u8 ret; /@@*得到当前GPS的UTC*/ Message pIn; pIn.utc.year = gpsx.utc.year; pIn.utc.month = gpsx.utc.month; pIn.utc.day = gpsx.utc.date; pIn.utc.hour = gpsx.utc.hour; pIn.utc.min = gpsx.utc.min; pIn.utc.sec = gpsx.utc.sec;; // pIn.utc = UTC2Sec(gpsUtc);//转换成秒存储 pIn.Latitude = gpsx.latitude; pIn.Longitude = gpsx.longitude; pIn.RPM = carPulse.EngRpm ; pIn.Speed = carPulse.carSpeed; ret = Push(pIn); LOG("Incache Message Count:%d.TotalSize:%d\r\n",g_CachesCount,GetCachesTotalSize()); return ret; } 把数据从栈中取出打包发到服务器 u8 OutCache(void) { u8 ret=0; Message pOut; ret = Pop(&pOut); if(ret) { LOG("outcache Message Count:%d.TotalSize:%d\r\n",g_CachesCount,GetCachesTotalSize()); char *p,*p1,*p2,length; p2= malloc(30); p=malloc(300); p1=p; memset(p,0x00,300); memset(p2,0x00,30); if(p1 == NULL ) { LOG("%s:malloc p1 fault!\n", __FILE__); return 0; } sprintf(p2,"%04d-%02d-%02d %02d:%02d:%02d,",pOut.utc.year,pOut.utc.month,pOut.utc.day,pOut.utc.hour,pOut.utc.min,pOut.utc.sec); strcat(p1,p2); //1,2 3/@@**/ memset(p2,0x00,20); /@@*经纬度*/ sprintf(p2,"%03d.%05d,%02d.%05d, ,",(pOut.Longitude/100000),(pOut.Longitude%100000),(pOut.Latitude/100000),(pOut.Latitude%100000)); strcat(p1,p2); //4 memset(p2,0x00,20); sprintf(p2,"%d,",pOut.Speed);//车速 strcat(p1,p2); p1=p; length = strlen(p); p[length] = MsgOrCRC((u8*)&p[2],length-2); SendBuf(USART1, p, length+1); free(p); free(p2); } return ret; }
原创作品,未经权利人授权禁止转载。详情见
转载须知
。
举报文章
点赞
(
0
)
450c4aed63d8393c
关注
评论
(0)
登录后可评论,请
登录
或
注册
相关文章推荐
MK-米客方德推出工业级存储卡
Beetle ESP32 C3 蓝牙数据收发
Beetle ESP32 C3 wifi联网获取实时天气信息
开箱测评Beetle ESP32-C3 (RISC-V芯片)模块
正点原子数控电源DP100测评
DP100试用评测-----开箱+初体验
Beetle ESP32 C3环境搭建
【花雕体验】16 使用Beetle ESP32 C3控制8X32位WS2812硬屏之二
X
你的打赏是对原创作者最大的认可
请选择打赏IC币的数量,一经提交无法退回 !
100IC币
500IC币
1000IC币
自定义
IC币
确定
X
提交成功 ! 谢谢您的支持
返回
我要举报该内容理由
×
广告及垃圾信息
抄袭或未经授权
其它举报理由
请输入您举报的理由(50字以内)
取消
提交