找回密码
 立即注册

QQ登录

只需一步,快速开始

搜索
查看: 809|回复: 13
打印 上一主题 下一主题
收起左侧

C语言算法求助,关于保留最新的N次数据

[复制链接]
回帖奖励 30 黑币 回复本帖可获得 3 黑币奖励! 每人限 1 次
跳转到指定楼层
楼主
ID:1104941 发表于 2024-6-17 16:40 | 只看该作者 回帖奖励 |倒序浏览 |阅读模式
使用场景:需要在外接FLASH存储最新的10次数据,每次存储的数据有点大(4Kbyte),按照更新的时间排序,次数越小,时间越新。
比如,昨天存完10次后,今天又需要存一次数据了,昨天的次数10舍弃,全部次数往后挪一个单位,今天的次数存在次数1位置。

我能想到的是使用链表,但是搞单片机好久没碰过链表方面的知识了,还有其它方法吗?
分享到:  QQ好友和群QQ好友和群 QQ空间QQ空间 腾讯微博腾讯微博 腾讯朋友腾讯朋友
收藏收藏 分享淘帖 顶 踩
回复

使用道具 举报

沙发
ID:1104941 发表于 2024-6-17 16:45 | 只看该作者
之前简单的排序可以在数组实现,但是这个逻辑感觉不适合在FLASH中操作,因为又要读,擦写,既花时间,又要一直擦除会影响寿命。
回复

使用道具 举报

板凳
ID:1080935 发表于 2024-6-18 08:25 | 只看该作者
链表和索引表都可以,队列也可以,控制头尾,不在头尾的都是空闲空间。
回复

使用道具 举报

地板
ID:401564 发表于 2024-6-18 09:40 | 只看该作者
增加一个断电检测或者写入动作,如果没有断电,最新的数据是保留在单片机上的,不用写入FLASH
把写入FLASH地址的记录单独放到一个页面,每次写入的时候,把已经写入的地址写到这个页面中
数据存放在其它的页面,页面是循环使用的,比如先写1页面,然后是2页面
这样的话,每次写入就只要擦写地址页面就可以了
FLASH不是普通存储器,一个地址只能写入一次,你想再写入第二次就擦写整个页面了
回复

使用道具 举报

5#
ID:277550 发表于 2024-6-18 10:08 | 只看该作者
使用这样的方法
固定的第1个位置是保存索引,每次保存完索引+1到第10、复位。表示一下个该写入的索引。
固定的第2个位置是保存是开始索引。表示从该索引开始读、到第10回0、读够10个。
后面是数据。
这样读写最少。
回复

使用道具 举报

6#
ID:1104941 发表于 2024-6-18 18:14 | 只看该作者
devcang 发表于 2024-6-18 10:08
使用这样的方法
固定的第1个位置是保存索引,每次保存完索引+1到第10、复位。表示一下个该写入的索引。
...

好的,我也有这个思路,在索引里面更改FLASH的地址偏移,指向不同地址,好像指针啊
回复

使用道具 举报

7#
ID:1104941 发表于 2024-6-18 18:21 | 只看该作者
Y_G_G 发表于 2024-6-18 09:40
增加一个断电检测或者写入动作,如果没有断电,最新的数据是保留在单片机上的,不用写入FLASH
把写入FLASH地 ...

谢谢,我打算做个外部超级电容了,掉电检测再保存FLASH,平时保存RAM。目前是旧数据的删除和新数据的插入有点想不通,还有就是加了电容,上电缓慢又怕出问题,感觉要加复位芯片啊
回复

使用道具 举报

8#
ID:1104941 发表于 2024-6-18 18:23 | 只看该作者
LaoYuTou 发表于 2024-6-18 08:25
链表和索引表都可以,队列也可以,控制头尾,不在头尾的都是空闲空间。

目前计划是打算使用索引表,索引保存FLASH的偏移地址,每次删除或者增加数据,旧偏移索引里面的地址,这样可以实现吧
回复

使用道具 举报

9#
ID:1080935 发表于 2024-6-19 08:41 | 只看该作者
是的,但是flash是按扇区操作的,一次写一个扇区,而且读写不快,影响寿命,不建议频繁读写。
回复

使用道具 举报

10#
ID:1125920 发表于 2024-6-19 12:07 | 只看该作者
对于在外接FLASH上存储最新的10次数据,可以采用循环缓冲区(环形队列)的方式,这种方法比链表更加简单高效,非常适合在单片机上使用。下面是实现这一方案的步骤:  ### 1. 设计存储结构 假设每次数据的大小为4Kbyte,需要存储10次数据,因此需要的存储空间为40Kbyte。  ### 2. 初始化环形缓冲区 环形缓冲区的概念是使用一个固定大小的数组,通过一个指针来跟踪最新的数据位置,并循环使用数组空间。  ### 3. 写入数据 每次写入新的数据时,将指针移动到下一个位置。如果指针超出数组范围,则回到数组的起点。  ### 4. 读取数据 读取数据时,根据指针和偏移量来获取最新的数据。  ### 具体实现 下面是一个示例代码,假设使用C语言来实现这一功能:  ```c #define DATA_SIZE 4096       // 每次数据大小为4Kbyte #define BUFFER_SIZE 10       // 环形缓冲区大小为10 #define FLASH_START_ADDRESS 0x00000000  // 外接FLASH起始地址,根据具体情况修改  // 环形缓冲区的当前索引 unsigned int currentIndex = 0;  // 写入数据到FLASH的函数 void writeDataToFlash(unsigned int index, const char* data) {     unsigned int address = FLASH_START_ADDRESS + (index * DATA_SIZE);     // 写入数据到指定地址,具体的FLASH写入函数根据芯片手册实现     Flash_Write(address, data, DATA_SIZE); }  // 读取FLASH中的数据到内存 void readDataFromFlash(unsigned int index, char* data) {     unsigned int address = FLASH_START_ADDRESS + (index * DATA_SIZE);     // 从指定地址读取数据,具体的FLASH读取函数根据芯片手册实现     Flash_Read(address, data, DATA_SIZE); }  // 存储新数据 void storeNewData(const char* newData) {     // 写入新数据到当前索引位置     writeDataToFlash(currentIndex, newData);          // 更新索引,循环使用缓冲区     currentIndex = (currentIndex + 1) % BUFFER_SIZE; }  // 获取最新的n次数据,n从1到10 void getLatestData(unsigned int n, char* data) {     if (n == 0 || n > BUFFER_SIZE) {         return; // 无效参数     }          int index = (currentIndex + BUFFER_SIZE - n) % BUFFER_SIZE;     readDataFromFlash(index, data); }  void Flash_Write(unsigned int address, const char* data, unsigned int size) {     // 具体的FLASH写入实现 }  void Flash_Read(unsigned int address, char* data, unsigned int size) {     // 具体的FLASH读取实现 } ```  ### 代码解释 - `currentIndex`:记录当前最新数据在缓冲区中的位置。 - `writeDataToFlash`:将数据写入FLASH的指定位置。 - `readDataFromFlash`:从FLASH的指定位置读取数据。 - `storeNewData`:存储新数据到环形缓冲区,并更新索引。 - `getLatestData`:获取最新的n次数据。  这种方法避免了复杂的链表操作,使用一个固定大小的数组来存储数据,通过简单的索引操作实现数据的循环存储和读取,适合单片机环境下的使用。
回复

使用道具 举报

11#
ID:401564 发表于 2024-6-19 12:41 | 只看该作者
bstljq 发表于 2024-6-18 18:21
谢谢,我打算做个外部超级电容了,掉电检测再保存FLASH,平时保存RAM。目前是旧数据的删除和新数据的插入 ...

单片机本身工作电流并不大,超级电容可以串联一个100欧的电阻或者是另外加一个LDO给超级电容充电
充电的电压不要超过超级电容的耐压
回复

使用道具 举报

12#
ID:688692 发表于 2024-6-19 19:31 | 只看该作者
每次存储扇区往后移动,用到最后一个扇区再擦除前面的扇区,可以节约FLASH寿命。循环滚动,只要记得上次是从哪里存起的就行了。
回复

使用道具 举报

13#
ID:384109 发表于 2024-6-19 21:05 | 只看该作者
应该不是数据存储区的操作问题吧,单独加个EEPROM用来存储指针或链表什么的都可以
回复

使用道具 举报

14#
ID:1126074 发表于 2024-6-23 22:51 | 只看该作者
在单片机环境下,由于资源有限,使用链表可能不是最理想的解决方案,因为它需要额外的内存空间来存储节点。考虑到数据量较大(4Kbyte)且需要频繁操作,可以考虑以下几种方法:  1.  **循环数组(Circular Buffer)**:          *   使用一个固定大小的数组(例如,10个元素,每个元素4Kbyte),数组的最后一个元素和第一个元素相连,形成循环。     *   当新的数据到来时,替换数组的第一个元素(次数为1),然后更新次数(例如,使用一个寄存器记录当前次数)。     *   当需要访问数据时,根据次数索引到数组的相应位置。 2.  **堆栈(Stack)**:          *   如果单片机支持堆栈操作,可以使用堆栈来存储数据和次数。每次新数据到来,将数据压入堆栈,同时更新堆栈顶部的次数(如果堆栈已满,可能需要先弹出最旧的数据)。     *   访问数据时,从堆栈顶部获取。 3.  **带计数的顺序存储**:          *   如果内存空间允许,可以将数据和次数连续存储,每个数据块后面紧跟着一个表示次数的字节。当新数据到来时,直接插入到数据末尾,次数加1。需要访问时,根据次数找到对应的数据。 4.  **文件系统(如SD卡)**:          *   如果你的应用支持外部存储,可以使用文件系统(如FatFS或YottaDB)将数据存储为文件,每个文件代表一次数据,文件名或文件属性可以包含次数信息。每次新数据写入一个新文件,旧文件保持,次数递增。  在选择方法时,要考虑单片机的内存限制、操作速度和数据持久性等因素。循环数组和顺序存储通常更适用于资源有限的情况,而堆栈和文件系统则需要更高级的硬件支持。
回复

使用道具 举报

您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

手机版|小黑屋|51黑电子论坛 |51黑电子论坛6群 QQ 管理员QQ:125739409;技术交流QQ群281945664

Powered by 单片机教程网

快速回复 返回顶部 返回列表