提问人:Andrew Cline 提问时间:3/27/2020 最后编辑:Andrew Cline 更新时间:3/28/2020 访问量:624
如何自动为结构中的字节添加填充以修复 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 在软件的选项卡中定义。它有读/写地址等选项)
该功能工作正常,以及 。问题在于,当宏将结构数据保存到 HMI 的 16 位寄存器时,字节会偏离对齐方式。我最终将我的浮子拆分在寄存器之间。我知道是这种情况,因为前三个浮动显示良好。另一个在单个字节 don't 之后浮动。ReadData
WriteData
(另外,我已经使用数组和移动字节解决了这个问题,但这非常乏味,我还有很多事情要做)
有没有办法将单字节变量转换为单词,以便在将它们写入 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;
};
评论
大多数编译器将填充添加到 a 中以达到对齐目的。假设 a 为 4 个字节,则结构的实际布局如下所示:struct
float
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;
}
评论
dummy
评论
#pragma pack