嵌入式C数据存储模块设计
我正在设计一个嵌入式C数据存储模块。它将由希望访问此“共享”系统范围数据的文件/模块包含。多个任务聚合数十个输入(GPIO、CAN、I2C/SPI/SSP数据等),并使用API存储这些值。然后,其他任务可以通过API安全地访问数据。该系统是一个带有RTOS的嵌入式应用程序,因此使用互斥锁来保护数据。无论实施情况如何,都将使用这些想法 我以前设计过类似的东西,我正在努力改进它。我目前正在进行一个新的实现,我遇到了一些小问题,从一个全新的角度来看,我会受益匪浅 快速概述本模块的要求:嵌入式C数据存储模块设计,c,database,embedded,C,Database,Embedded,我正在设计一个嵌入式C数据存储模块。它将由希望访问此“共享”系统范围数据的文件/模块包含。多个任务聚合数十个输入(GPIO、CAN、I2C/SPI/SSP数据等),并使用API存储这些值。然后,其他任务可以通过API安全地访问数据。该系统是一个带有RTOS的嵌入式应用程序,因此使用互斥锁来保护数据。无论实施情况如何,都将使用这些想法 我以前设计过类似的东西,我正在努力改进它。我目前正在进行一个新的实现,我遇到了一些小问题,从一个全新的角度来看,我会受益匪浅 快速概述本模块的要求: 理想情况下,将
问题是你将如何着手设计这样的东西?枚举、结构、访问器、宏等?我不是在这里寻找代码,只是讨论一般的总体设计思想。如果互联网上有解决这类问题的解决方案,也许仅仅一个链接就足够了。我自己也经历过几次这种情况。每次我结束“滚我自己的”的时候,我肯定不会患上非发明于此(NIH)综合症。有时,空间、处理周转时间或可靠性/可恢复性要求只是使这条路最不痛苦 因此,我不想就此主题写一部伟大的美国小说,我只想在这里抛开一些想法,因为你的问题相当广泛(但至少感谢你提出一个问题并提供背景)
是C++的表吗?内联函数、模板和一些Boost库在这里可能很有用。但我猜这是C
如果您使用的是C99,那么至少可以使用内联函数,在类型安全方面,内联函数比宏高出一步 您可能需要考虑使用多个互斥体来保护数据的不同部分;即使更新很快,您也可能希望将数据分成多个部分(例如,配置数据、初始化数据、错误记录数据、跟踪数据等),并为每个部分提供自己的互斥,从而减少漏斗/瓶颈 <>你也可以考虑让所有的数据访问都通过服务器任务。所有读写操作都通过与服务器任务通信的API进行。服务器任务从队列中按顺序提取读写请求,通过写入RAM镜像快速处理它们,如果需要发送响应(至少对于读请求),然后在后台将数据缓冲到NVM(如果需要)。与简单的互斥体相比,它听起来很重,但在某些用例中有其优势。对你的申请了解不够,不知道这是否有可能 我要说的一件事是,通过标记(例如,可能是一个枚举列表,如配置数据、地址数据等)获取/设置的想法比直接寻址数据(例如,“给我地址ox42000处的256字节)前进了一大步.我看到许多商店在整个物理寻址方案最终崩溃时承受着巨大的痛苦&他们需要重新考虑/重新设计。试着将“什么”与“如何”分离开来-客户不必知道或关心东西存储在哪里,有多大,等等。(你可能已经知道了这一切,如果是的话,很抱歉,我只是一直在看……)最后一件事。你提到了互斥体。当心优先级反转…这会使那些“快速访问”"在某些情况下需要很长的时间。大多数内核互斥实现允许您考虑这一点,但通常默认情况下它不会启用。再次,如果这是一条老消息,我很抱歉…我通常使用一个简单的字典,如API,使用int作为键和一个固定大小的值。这执行速度很快,使用非常少的程序RAM和has可预测的数据RAM使用情况。换句话说,最低级别的API如下所示:
void data_set(uint16 key, uint32 value);
uint32 data_get(uint16 key);
键成为常量列表:
#define KEY_BOGOMIPS 1
#define KEY_NERDS_PER_HOUR 2
您可以通过强制转换来处理不同的数据类型。这很糟糕,但您可以编写宏以使代码更清晰:
#define data_get_float(key) (float)data_get(key)
如果不为每个项目编写单独的宏或访问函数,就很难实现类型安全。在一个项目中,我需要验证输入数据,这就成为了类型安全机制
数据的物理存储结构取决于您拥有的数据内存、程序内存、周期和独立密钥的数量。如果您拥有大量的程序空间,请对密钥进行散列,以获得一个较小的密钥,您可以直接在数组中查找。通常,我将底层存储设置为:
struct data_item_t {
uint16 key;
uint32 value;
}
struct data_item_t items[NUM_ITEMS];
对我来说,即使在非常小的(8位)微控制器上,这也足够快,尽管如果你有很多东西,它可能不适合你
请记住,您的编译器可能会很好地内联或优化写操作,因此每次访问的周期可能比您预期的要低。我有过一些经验,并且发现每种方法都适合自己的需要。请写下我对这个问题的想法,希望这能给您一些想法
- 对于具有大量依赖关系和约束的更复杂的数据,我发现通常最好使用更高级别的模块,即使是在牺牲速度的情况下。它为您省去了很多麻烦,而且根据我的经验,这通常是一种方法,除非您有非常严格的约束。问题是存储大部分“静态”数据,在这种类型的模块中变化不大
#ifndef __LIB_DATA_H #define __LIB_DATA_H #include <type.h> /**************************************************************************************** * Constant Definitions ***************************************************************************************/ /* Varname, default value (uint32_t) */ #define DATA_LIST \ DM(D_VAR1, 0) \ DM(D_VAR2, 1) \ DM(D_VAR3, 43) #define DM(y, z) y, /* create data structure from the macro */ typedef enum { DATA_LIST NUM_DATA_VARIABLES } dataNames_t; typedef struct { dataNames_t name; uint32_t value; } dataPair_t; /* the macro has to be undefined to allow the fault list to be reused without being * defined multiple times * * this requires: * a file specific lint option to suppress the rule for use of #undef */ #undef DM /**************************************************************************************** * Data Prototypes ***************************************************************************************/ /**************************************************************************************** * External Function Prototypes ***************************************************************************************/ /** * Fetch a machine parameter * * \param dataName The variable from DATA_LIST that you want to fetch * * \return The value of the requested parameter * */ uint32_t lib_data_Get(dataNames_t dataName); /** * Set a machine parameter * * \param dataName The variable from DATA_LIST that you want to set * \param dataVal The value you want to set the variable to * * \return void * */ void lib_data_Set(dataNames_t dataName, uint32_t dataVal); #endif /* __LIB_DATA_H */
#include <type.h> #include "lib_data.h" /**************************************************************************************** * Variable Declarations ***************************************************************************************/ /* Used to initialize the data array with defaults ##U appends a 'U' to the bare * integer specified in the DM macro */ #define DM(y, z) \ dataArray[y].name = y; \ dataArray[y].value = z##U; static bool_t dataInitialized = FALSE; static dataPair_t dataArray[NUM_DATA_VARIABLES]; /**************************************************************************************** * Private Function Prototypes ***************************************************************************************/ static void lib_data_Init(void); /**************************************************************************************** * Public Functions ***************************************************************************************/ uint32_t lib_data_Get(dataNames_t dataName) { if(!dataInitialized) { lib_data_Init(); } /* Should only be used on systems that do word-sized asm reads/writes. * If the lib gets extended to multi-word storage capabilities, a mutex * is necessary to protect against multi-threaded access */ return dataArray[dataName].value; } void lib_data_Set(dataNames_t dataName, uint32_t dataVal) { if(!dataInitialized) { lib_data_Init(); } /* Should only be used on systems that do word-sized asm reads/writes. * If the lib gets extended to multi-word storage capabilities, a mutex * is necessary to protect against multi-threaded access */ dataArray[dataName].value = dataVal; } /**************************************************************************************** * Private Functions ***************************************************************************************/ /** * initialize the machine data tables * * \param none * * \return none * */ static void lib_data_Init(void) { /* Invoke the macro to initialize dataArray */ DATA_LIST dataInitialized = TRUE; }
Std_ReturnType NvM_ReadBlock(NvM_BlockIdType BlockId, uint8* data); Std_ReturnType NvM_WriteBlock(NvM_BlockIdType BlockId, uint8* data); Std_ReturnType NvM_ReadAll(void); Std_ReturnType NvM_WriteAll(void); Std_ReturnType NvM_GetErrorStatus(NvM_BlockIdType BlockId, NvM_RequestResultType* res);