Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How can I automatically add padding to bytes in a struct to fix register alignment in C?

This might sound like a weird question at first and I understand why. My problem is a little hard to explain but hopefully I can explain it well.

EDIT: Padding may already be done in the CPU, but this padding is lost when being saved to the HMI local memory registers therefore the alignment is off and the HMI does not display the data correctly.

I have an HMI that I am programming. The software to create the pages on the HMI is mainly drag-and-drop. This is important to note because a user can't access variables in the macros, only once the data is save to local memory can they enter the data into these drag-and-drop objects.

I have a sensor that is connected to the HMI and the sensor has "blocks" of memory that can only be accessed by a multi-register read. This means I need a macro to read this data and temporarily save it in the HMI so that you can edit it before sending it back after clicking "send"

The drag-and-drop elements can only access 16-bit registers. The data that is coming in has single bytes that are thrown in the middle.

I have created a struct and received the data into the struct and written it to the HMI registers. Here is the code in C:

struct configStruct { // 72 Bytes or 36 words/registers long
    char description[32]; // char string
    float c1CE_A; // coefficient 1_A
    float c1CE_B; // coefficient 1_B
    float c1CE_C; // coefficient 1_C
    char c1useLogFit; // Single byte that is 1 if "Log Fit" is enabled, and 0 if disabled.
    float c2CE_A;
    float c2CE_B;
    float c2CE_C;
    char c2useLogFit;
    float c3CE_A;
    float c3CE_B;
    float c3CE_C;
    char c3useLogFit;
    char dummyByte;
};

int MacroEntry()
{

    struct configStruct *dataPtr, data1;

    ReadData(ProductConfig_PLC,1,2305,36,(void*)&data1); // read data from sensor and save in struct

    // Something needs to happen here that converts the bytes into words so the data lines up with the 16-bit registers in the HMI

    WriteData(ProductConfig_HMI,0,2305,36,(void*)&data1); // write temporary data to HMI from struct

    return 0;
}

(ProductConfig_PLC/HMI are defined in a tab in the software. It has options for the read/write address and whatnot)

The ReadData function is working properly, as well as WriteData. The problem is that when the macro saves the struct data to the HMI's 16-bit registers, the bytes throw off the alignment. I end up with my floats split between registers. I know this is the case because the first three floats display fine. The other floats after the single byte dont.

(Also, I've fixed this using arrays and shifting bytes but it's extremely tedious and I have many more to do)

Is there a way to take the single byte variables and convert them to words so that when they are written to the HMI the alignment is proper? I can't access the bytes if there is other data in those same registers.

I also know that I could just create another struct with the proper size variables and copy everything over. However, this becomes a problem because some "blocks" in the sensor contain hundreds of variables and doing it by hand for both send and receive would be tedious, and mind numbing.

Here is the data definition:

The Product Code Calibration Database starts at modbus address 0x900
repeats for each Product Code (currently 0 – 49). Each product code
calibration table is 72 bytes/36 words long. To calculate the start address for each
product code: address = 0x900 + (36 * Product Code number)
Data Definition:
Product Code Description – character string (32 bytes)
Constituent 1 Coefficient A – IEEE float (4 bytes)
Constituent 1 Coefficient B – IEEE float (4 bytes)
Constituent 1 Coefficient C – IEEE float (4 bytes)
Constituent 1 Use Log Fit - 8 bit integer (1 byte) 1= use log fit
Constituent 2 Coefficient A – IEEE float (4 bytes)
Constituent 2 Coefficient B – IEEE float (4 bytes)
Constituent 2 Coefficient C – IEEE float (4 bytes)
Constituent 2 Use Log Fit - 8 bit integer (1 byte) 1= use log fit
Constituent 3 Coefficient A – IEEE float (4 bytes)
Constituent 3 Coefficient B – IEEE float (4 bytes)
Constituent 3 Coefficient C – IEEE float (4 bytes)
Constituent 3 Use Log Fit - 8 bit integer (1 byte) 1= use log fit

Link to HMI datasheet here

Thanks for the help.

like image 403
Andrew Cline Avatar asked Nov 27 '25 00:11

Andrew Cline


1 Answers

C11 does allow alignment control.

Perhaps align many members - any time a more restrictive member may follow a less restrictive one.

#include <stdalign.h>

struct configStruct {
    char description[32];
    alignas (short) float c1CE_A; // coefficient 1_A
    float c1CE_B; // coefficient 1_B
    float c1CE_C; // coefficient 1_C
    char c1useLogFit;
    alignas (short) float c2CE_A;
    float c2CE_B;
    float c2CE_C;
    char cc2useLogFit;
    alignas (short) float c3CE_A;
    float c3CE_B;
    float c3CE_C;
    char c3useLogFit;
    char dummyByte;
};
like image 90
chux - Reinstate Monica Avatar answered Nov 29 '25 12:11

chux - Reinstate Monica



Donate For Us

If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!