ROMable(但复杂)数组(ROMable)对象-如何解决它

ROMable (but complex) array of (ROMable) objects - how to solve it?

本文关键字:ROMable 何解决 解决 对象 复杂 数组      更新时间:2023-10-16

我正在为MCU (ARM Cortex-M3)开发固件。这些设备没有很多内存,所以无论如何你都应该尝试把数据放在固定内存(闪存)中。

问题是这样的:设备必须提供由接口(MODBUS)读取的"寄存器",因此操作员读取"地址"10并获得一些数字,他/她"写"到"地址"101并引起一些动作等。将有数百个这样的"地址",访问它们会导致一些动作-例如,从1-10读取会导致传感器1到10的温度测量,从11-20读取会导致读取一些校准值,写入这些地址会导致这些校准值存储在非易失性存储器中等等-将有很多不同的功能(;

目前我是这样实现的:

  1. 有一个数组将地址绑定到回调函数用于读写-一个回调函数可以绑定到多个地址(就像在上面的例子中,相同的回调将用于1-10)

  2. 有另一个数组绑定地址回调参数,有许多数组,因为参数可能是不同的类型/大小-在上面的例子中,有一个数组structs {int address;}表示1-10和structs数组{Int address;int id;int大小;int最小;int最大;

  3. 每个回调函数都可以获取当前地址,在其数组中查找相关结构体并获取所需参数

这种方法有点重复,因为我必须多次声明地址-主数组中有一个条目{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();
}