簡體   English   中英

如何自動向結構中的字節添加填充以修復 C 中的寄存器對齊?

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

乍一看,這可能聽起來像一個奇怪的問題,我明白為什么。 我的問題有點難以解釋,但希望我能解釋得很好。

編輯:填充可能已經在 CPU 中完成,但是當保存到 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都正常工作。 問題在於,當宏將結構體數據保存到 HMI 的 16 位寄存器時,字節會脫離對齊。 我最終在寄存器之間分配了我的浮點數。 我知道是這種情況,因為前三個浮點數顯示正常。 另一個在單字節后浮動。

(另外,我已經使用數組和移位字節修復了這個問題,但它非常乏味,而且我還有很多事情要做)

有沒有辦法獲取單字節變量並將它們轉換為字,以便在將它們寫入 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 數據表

謝謝您的幫助。

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

大多數編譯器出於對齊目的向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 字節。

如果您使用 gcc,您可以聲明要打包的結構:

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

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM