ROMable(但复杂)数组(ROMable)对象-如何解决它
ROMable (but complex) array of (ROMable) objects - how to solve it?
我正在为MCU (ARM Cortex-M3)开发固件。这些设备没有很多内存,所以无论如何你都应该尝试把数据放在固定内存(闪存)中。
问题是这样的:设备必须提供由接口(MODBUS)读取的"寄存器",因此操作员读取"地址"10并获得一些数字,他/她"写"到"地址"101并引起一些动作等。将有数百个这样的"地址",访问它们会导致一些动作-例如,从1-10读取会导致传感器1到10的温度测量,从11-20读取会导致读取一些校准值,写入这些地址会导致这些校准值存储在非易失性存储器中等等-将有很多不同的功能(;
目前我是这样实现的:
-
有一个数组将地址绑定到回调函数用于读写-一个回调函数可以绑定到多个地址(就像在上面的例子中,相同的回调将用于1-10)
-
有另一个数组绑定地址回调参数,有许多数组,因为参数可能是不同的类型/大小-在上面的例子中,有一个数组structs {int address;}表示1-10和structs数组{Int address;int id;int大小;int最小;int最大;
-
每个回调函数都可以获取当前地址,在其数组中查找相关结构体并获取所需参数
这种方法有点重复,因为我必须多次声明地址-主数组中有一个条目{1,readSensor, writessensor},数组或传感器中有另一个地址为1的条目{1,0x5423} -它不适合DRY原则(;
我想到的一个解决方案是一个多态对象数组,但是:
><罢工。虚拟函数导致对象被放置在RAM中(它不是ROMable)> EDIT:这似乎是由GCC错误引起的,在4.6 constexpr构造函数导致对象被放置在RAM中,但对于4.7它工作!
b。这仍然有点麻烦,因为我必须创建对象"某处",并把它的地址放在数组中(数组实际上是放在flash中)
我不能使用任何STL的东西,如向量,因为它完全放在RAM中。
我想过一些模板魔法,但那更像是黑魔法(;
我也考虑过链表,但我只是没有看到任何"好"的方式来声明它在一个可读的和连续的形式(如数组[;),但我可能不熟悉一些好的解决这个问题的方法。
最简单的解决方案是让回调接受另一个"void *"参数,并将其强制转换为内部需要的任何内容,但这"不太好",还需要我在"某处"创建带有参数的结构体,然后将它们绑定到主数组。
有什么好的解决方案吗?这必须在ROM中,将有数百个条目,每个条目可以有多个不同的参数。
我可能会写一些代码来提供这个"源",并有一个"编译器"。
那么你的原始来源应该是这样的:
# INPUT_SENSOR(callback1, callback2, address, sensor_id)
SENSOR(read_sensor, write_sensor, 100, 1)
SENSOR(read_sensor, write_sensor, 104, 2)
...
# CALIBTRATE(callback1, callback2, id, address, size, min, max, default)
CALIBRATE(calib_write, calib_read, 1, 44, 11, 18, 99, 33)
CALIBRATE(calib_write, calib_read, 2, 45, 12, 19, 98, 34)
你可以让它生成一个这样的数据结构:
struct funcptrs {
int (*readfunc)(int count, int arr[]);
void (*writefunc)(int count, int arr[]);
int count;
int *arr;
};
static const int arr1[] = { 100, 1 };
static const int arr2[] = { 104, 2 };
static const int arr3[] = { 1, 44, 11, 18, 99, 33 };
static const int arr4[] = { 2, 45, 12, 19, 98, 34 };
struct funcptrs functable[] =
{
{ read_sensor, write_sensor, 2, arr1 },
{ read_sensor, write_sensor, 2, arr2 },
{ calib_write, calib_read, 6, arr3 },
{ calib_write, calib_read, 6, arr4 },
};
也许可以使用C预处理器并运行两次,也许-我过去做过这种事情,但我有点懒得在这里尝试-我想我宁愿写20-30行C代码来生成代码,因为它既更灵活,通常也更容易理解/遵循。
我肯定会选择switch .. case
语句。你的编译器很可能会将其解析为分支表,这与你实现的回调函数向量一样有效,但在我看来更具可读性。它是这样的:
void parse_modbus (int register, bool write, int more_parameters) {
switch (register) {
case TEMPERATURE_REGISTER_1:
...
case TEMPERATURE_REGISTER_10:
read_temperature(register - TEMPERATURE_REGISTER_1);
break;
case CALIBRATION_REGISTER_1:
...
case CALIBRATION_REGISTER_10:
if (write)
write_calibration(register - CALIBRATION_REGISTER_1);
else
read_calibration((register - CALIBRATION_REGISTER_1);
break;
default:
unimplemented_register(register);
break;
}
}
我们用x -宏解决了这个问题。
这个想法是用寄存器创建一个可读的文件,它将通过多次包含它来创建适当的代码。- register.h
typdef enum {
MR_SET_VOLTAGE = 100, // 8bit-Register for the voltage R/W
MR_SET_CURRENT = 104, // 8bit-Register for the current R/W
MR_ACT_VOLTAGE = 206, // 16bit-Register for the actual Voltage Read-Only
} teRegister;
- allRegister.inc
REGISTER(uint8_t,MR_SET_VOLTAGE,readFnA,writeFnA)
REGISTER(uint8_t,MR_SET_CURRENT,readFnA,writeFnA)
REGISTER(uint16_t,MR_ACT_VOLTAGE,readFnB,NULL)
- register.c
#define PASTE(a,b) a##b
#define REGISTER(_type,_regName,_readCallback,_writeCallback)
static _type PASTE(VAR_,_regName);
#include "allRegister.inc"
#undef REGISTER
typedef uint8_t (*tfnRegistercallback)(tsRegister const *pRegister);
typedef struct {
uint8_t registerIdx;
uint8_t registerSize;
tfnRegistercallback readCallback;
tfnRegistercallback writeCallback;
void *pData; // This is a pointer to the data of the register (in RAM)
} tsRegister;
// Creating of the array of all registers
#define REGISTER(_type,_regName,_readCallback,_writeCallback)
{_regName, sizeof(_type), _readCallback,_ writeCallback, &PASTE(VAR_,_regName)},
// Array of all registers, resides in Flash
static const tsRegister allRegister[] = {
#include "allRegister.inc"
};
#undef REGISTER
为每个寄存器创建一个变量,如static uint8_t VAR_MR_SET_VOLTAGE;
每个寄存器都有一个数组项。
static const tsRegister allRegister[] = {
{ MR_SET_VOLTAGE, sizeof(uint8_t), readFnA, writeFnA, &VAR_MR_SET_VOLTAGE},
...
};
并且回调函数获得一个指向寄存器的const表项的指针,因此函数本身可以用于多个寄存器。
void readCallback(tsRegister const *pRegister)
{
int value;
if (pRegister->registerSize == 1 )
value = *(uint8_t*)pData;
else if (pRegister->registerSize == 2 )
value = *(uint16_t*)pData;
if ( pRegister->register == MR_ACT_VOLTAGE )
doSomething();
}
- 运行同一解决方案的另一个项目的项目
- Project Euler问题4的错误解决方案
- Ardunio UNO解决了多个重叠的定时器循环
- 函数何时会在c++中包含stack_Unwind_Resume调用
- 如何解决gcc编译器优化导致的centos双编译器设置中的分段错误
- 两个文件使用彼此的功能-如何解决
- 计算每个节点的树高,帮助我解释这个代码解决方案
- Python中的for循环与C++有何不同
- 如何解决"invalid conversion from 'char' to 'const char*'"
- 在java中解决这段代码时面临循环中的问题
- C++:Application.cpp中抛出了未解析的外部符号(解决方案在问题的末尾,供未来的读者参考)
- 难以理解某些人解决IOI问题的源代码
- visual c++,如何获取解决方案目录中的代码
- 如何解决错误:SCIP C++中的 SCIP 阶段无效 <10>
- 节俭并发:未解决的外部问题
- IpOpt拒绝解决不受约束的问题
- 如何解决这个超硬恒星的创造问题
- 循环无限运行C++解决骑士之旅问题
- 有没有办法在远程设备上打开和编辑visual Studio 2017解决方案
- ROMable(但复杂)数组(ROMable)对象-如何解决它