1

I've got a variety of different PCBs each with an AtMega2560 microcontroller using a avr-gcc/ C compiler (using Arduino 1.8.12 compiler, which has classes, new, delete but not vector and such). My firmware is the same for all of the PCBs, which is really nice because a single EEPROM byte allows the PCB to select it's type and for all PCBs to run using the same firmware. But I've got memory problems and would like to not allocate the memory for non-instantiated classes. For example, 2 different types of PCBs might have large arrays with different allocations for different information:

voltateOutputPcb.h:
Class voltateOutputPcb
     uint8_t outputVoltages[1000];

temperaturePCB.h:
Class temperaturePCB
     uint32_t temperatureSensorReading[200];

The usage is nice and pretty. To get the 100th (0-indexed) outputVoltages_array element:

uint8_t 100thSensorValue = outputVoltages[100];

Option #1: Allocate memory during construction using pointers

If the code that selects both of these classes uses #include, the both big arrays are allocated on the stack at compile. I'd like to only allocate the memory of the one that is actually going to be instantiated. One way to do this would be to allocate a pointer and the create the array using new in the constructor:

voltageOutputPcb.h:
Class voltateOutputPcb
     uint8_t *outputVoltages_array;

voltageOutputPcb.cpp constructor:
outputVoltages_array = new uint8_t[1000];

But usage is a bit ugly and could lead to future bugs. To get the 100th outputVoltages_array element:

uint8_t 100thSensorValue = *(outputVoltages_array+100);

Option #2: Separate versions of the code

Another option is to make a different compiled version of the firmware so that each PCB just has the .h files #included that it needs. This would be a manual process of doing multiple compiles and saving each different output file. This isn't ideal either, as it's really nice to have 1 piece of code, with details selected at run time.

Option #3: Shared memory

Having one big memory space that is allocated differently between the different instances is an option... but not a very good one here because the memory is being used for very different things. In this case I feel this is far inferior to Option #1 above.

Is there a better way?

Is there a better way to do dynamic allocation of memory on construction? Is there a less manual way to conditionally #include only what I want?

Some other relevant links:

Forward Declaration vs Include

Regarding C++ Include another class

Including .cpp files

Static array vs. dynamic array in C++

Casey
  • 475
  • 6
  • 19
  • 3
    Are you actually asking about [C with classes](https://en.wikipedia.org/wiki/Cfront) or C++? Because C with classes is definitely not the same thing as C++. – François Andrieux Sep 01 '20 at 20:33
  • 2
    `*(outputVoltages_array+100);` is exactly the same as `outputVoltages_array[100];` so it doesn't need to be "ugly" – Support Ukraine Sep 01 '20 at 20:34
  • @FrançoisAndrieux: And I was wondering if he meant Objective C and didn't recognize it. – Joshua Sep 01 '20 at 20:52
  • *"To get the 100th..."* Do the array indices start from 0 or 1 in the language you are using? – Bob__ Sep 01 '20 at 21:43
  • For the compiler, it's Arduino, which uses avr-gcc. It's C, but with new and delete and I'm not sure what else (https://arduino.github.io/arduino-cli/sketch-build-process/). Because it's C, it's zero indexed, so my language on "100th element" is sloppy. – Casey Sep 02 '20 at 20:56
  • Any question about a generic "microcontroller" without a part number is useless to even try to answer. Who even knows if whatever version of gcc you're using even supports this mysterious controller? I don't. – TomServo Sep 02 '20 at 21:01
  • @TomServo It's an AtMega2560 running avr-gcc through Arduino 1.8.12. But why does that matter? If I've got the same microcontroller on a number of different PCBs with the memory limitations described in the question, why does the specific amount of memory in question matter? Don't the same solutions apply to a "small micro" with small memory requirements and a "big micro" with more memory needs? – Casey Sep 02 '20 at 21:07
  • @TomServo Are you the down vote? That seems a bit harsh for neglecting to add part numbers. Also, the compiler and the micro do work together. – Casey Sep 02 '20 at 21:07

1 Answers1

4

I would consider a union. Something like:

struct PcbCommon
{
    char pcbName[32];
    unsigned version;
    char sn[200];
    uint8_t some_big_common_array[100];
    ...
};

struct PcbA
{
    uint8_t some_big_array_specific_to_PCB_A[1000];
    ...
}

struct PcbB
{
    uint8_t some_big_array_specific_to_PCB_B[500];
    ...
}

struct Pcb
{
    struct PcbCommon common;
    union
    {
        struct PcbA pcbA;
        struct PcbB pcbB;
        ...
    } u;
};

In this way you'll never allocate more memory than your most memory-demanding PCB needs. In fact you'll always allocate what the most memory-demanding PCB needs but sinse you have to "handle" that PCB, it shouldn't be a problem.

p.s. I would avoid new/delete as dynamic memory allocation on small memory constrained embedded systems is very hard to manage.

Support Ukraine
  • 42,271
  • 4
  • 38
  • 63