Как я могу автоматически добавить заполнение байтов в структуре, чтобы исправить выравнивание регистров в C? - PullRequest
3 голосов
/ 27 марта 2020

Поначалу это может звучать странно, и я понимаю, почему. Моя проблема немного сложна для объяснения, но, надеюсь, я могу объяснить ее хорошо.

РЕДАКТИРОВАТЬ: заполнение может быть уже выполнено в ЦП, но это заполнение теряется при сохранении в регистрах локальной памяти HMI, поэтому выравнивание выключен, и HMI не отображает данные правильно.

У меня есть HMI, который я программирую. Программное обеспечение для создания страниц в HMI в основном перетаскивается. Это важно отметить, поскольку пользователь не может получить доступ к переменным в макросах, только после сохранения данных в локальной памяти они могут вводить данные в эти объекты перетаскивания.

У меня есть датчик, который подключен к HMI, и у датчика есть «блоки» памяти, доступ к которым возможен только при многоканальном считывании. Это означает, что мне нужен макрос, чтобы прочитать эти данные и временно сохранить их в HMI, чтобы вы могли отредактировать их перед отправкой обратно после нажатия кнопки «отправить»

Элементы перетаскивания могут получить доступ только к 16- битовые регистры. Входящие данные имеют отдельные байты, которые выбрасываются в середине.

Я создал структуру, получил данные в структуре и записал их в регистры HMI. Вот код в 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 определены на вкладке в программном обеспечении. У него есть опции для адреса чтения / записи и еще много чего)

Функция ReadData работает нормально, как и WriteData. Проблема в том, что когда макрос сохраняет структурные данные в 16-битные регистры HMI, байты сбрасывают выравнивание. В итоге мои поплавки разделены между регистрами. Я знаю, что это так, потому что первые три поплавка отображаются нормально. Другие плавающие после одного байта не делаются.

(Кроме того, я исправил это, используя массивы и сдвигая байты, но это чрезвычайно утомительно, и у меня есть еще много дел)

Есть ли способ взять однобайтовые переменные и преобразовать их в слова, чтобы при их записи в HMI выравнивание было правильным? Я не могу получить доступ к байтам, если в этих же регистрах есть другие данные.

Я также знаю, что мог бы просто создать другую структуру с переменными правильного размера и скопировать все заново. Однако это становится проблемой, потому что некоторые «блоки» в датчике содержат сотни переменных, и делать это вручную для отправки и получения было бы утомительно, и просто онемел.

* Вот определение данных:
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

Ссылка на таблицу HMI здесь

Спасибо за помощь.

Ответы [ 2 ]

1 голос
/ 27 марта 2020

Большинство компиляторов добавляют отступы к struct для выравнивания. Предполагая, что float составляет 4 байта, фактический макет структуры будет выглядеть следующим образом:

struct configStruct { 
    char description[32];
    float c1CE_A; 
    float c1CE_B; 
    float c1CE_C; 
    char c1useLogFit; 
    char dummy[3];    // padding bytes
    float c2CE_A;
    float c2CE_B;
    float c2CE_C;
    char c2useLogFit;
    char dummy[3];    // padding bytes
    float c3CE_A;
    float c3CE_B;
    float c3CE_C;
    char c3useLogFit;
    char dummyByte;
    char dummy[2];    // padding bytes
};

Таким образом, ваша 72-байтовая структура на самом деле 80 байтов.

Если вы используете g cc, вы можете объявить структуру упакованной:

struct configStruct __attribute__((packed)) { 
    char description[32];
    float c1CE_A; 
    float c1CE_B; 
    float c1CE_C; 
    char c1useLogFit; 
    float c2CE_A;
    float c2CE_B;
    float c2CE_C;
    char c2useLogFit;
    float c3CE_A;
    float c3CE_B;
    float c3CE_C;
    char c3useLogFit;
    char dummyByte;
};

, что устранит заполнение.

Если у вас его нет, вам придется десериализовать вручную данные для работы с ним сериализуются обратно в байтовый буфер для записи на другой конец.

Пример того, как вы это сделаете, выглядит следующим образом:

void deserialize(char *indata, struct configStruct *data)
{
    char *p = indata;
    int offset = 0;
    memcpy(data->description, p + offset, sizeof(data->description));
    offset += sizeof(data->description);
    memcpy(&data->c1CE_A, p + offset, sizeof(data->c1CE_A));
    offset += sizeof(data->c1CE_A);
    memcpy(&data->c1CE_B, p + offset, sizeof(data->c1CE_B));
    offset += sizeof(data->c1CE_B);
    memcpy(&data->c1CE_C, p + offset, sizeof(data->c1CE_C));
    offset += sizeof(data->c1CE_C);
    memcpy(&data->c1useLogFit, p + offset, sizeof(data->c1useLogFit));
    offset += sizeof(data->c1useLogFit);
    // etc.
}

void serialize(struct configStruct *data, char *outdata)
{
    char *p = outdata;
    int offset = 0;
    memcpy(p + offset, data->description, sizeof(data->description));
    offset += sizeof(data->description);
    memcpy(p + offset, &data->c1CE_A, sizeof(data->c1CE_A));
    offset += sizeof(data->c1CE_A);
    memcpy(p + offset, &data->c1CE_B, sizeof(data->c1CE_B));
    offset += sizeof(data->c1CE_B);
    memcpy(p + offset, &data->c1CE_C, sizeof(data->c1CE_C));
    offset += sizeof(data->c1CE_C);
    memcpy(p + offset, &data->c1useLogFit, sizeof(data->c1useLogFit));
    offset += sizeof(data->c1useLogFit);
    // etc.
}

int MacroEntry()
{

    char indata[72], outdata[72];
    struct configStruct data;

    ReadData(ProductConfig_PLC,1,2305,36,(void*)indata); 
    deserialize(indata, &data);

    // work with "data"

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

    return 0;
}
1 голос
/ 27 марта 2020

C11 разрешает управление выравниванием.

Возможно, выровняйте много членов - в любое время более ограничительный член может следовать за менее ограничительным.

#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;
};
...