Commit 49af73debd756be68497a61bd53a07c02673da96
Committed by
Stéphane Raimbault
1 parent
1a503b7f
Fix float endianness issue on big endian architecture.
It converts float values depending on what order they come in. This patch was modified from rm5248 [1] [1] https://github.com/synexxus/libmodbus/commit/a511768e7fe7ec52d7bae1d9ae04e33f87a59627
Showing
3 changed files
with
141 additions
and
32 deletions
src/modbus-data.c
| @@ -123,9 +123,18 @@ float modbus_get_float_abcd(const uint16_t *src) | @@ -123,9 +123,18 @@ float modbus_get_float_abcd(const uint16_t *src) | ||
| 123 | { | 123 | { |
| 124 | float f; | 124 | float f; |
| 125 | uint32_t i; | 125 | uint32_t i; |
| 126 | + uint8_t a, b, c, d; | ||
| 126 | 127 | ||
| 127 | - i = ntohl(((uint32_t)src[0] << 16) + src[1]); | ||
| 128 | - memcpy(&f, &i, sizeof(float)); | 128 | + a = (src[0] >> 8) & 0xFF; |
| 129 | + b = (src[0] >> 0) & 0xFF; | ||
| 130 | + c = (src[1] >> 8) & 0xFF; | ||
| 131 | + d = (src[1] >> 0) & 0xFF; | ||
| 132 | + | ||
| 133 | + i = (a << 24) | | ||
| 134 | + (b << 16) | | ||
| 135 | + (c << 8) | | ||
| 136 | + (d << 0); | ||
| 137 | + memcpy(&f, &i, 4); | ||
| 129 | 138 | ||
| 130 | return f; | 139 | return f; |
| 131 | } | 140 | } |
| @@ -135,9 +144,18 @@ float modbus_get_float_dcba(const uint16_t *src) | @@ -135,9 +144,18 @@ float modbus_get_float_dcba(const uint16_t *src) | ||
| 135 | { | 144 | { |
| 136 | float f; | 145 | float f; |
| 137 | uint32_t i; | 146 | uint32_t i; |
| 147 | + uint8_t a, b, c, d; | ||
| 138 | 148 | ||
| 139 | - i = ntohl(bswap_32((((uint32_t)src[0]) << 16) + src[1])); | ||
| 140 | - memcpy(&f, &i, sizeof(float)); | 149 | + a = (src[0] >> 8) & 0xFF; |
| 150 | + b = (src[0] >> 0) & 0xFF; | ||
| 151 | + c = (src[1] >> 8) & 0xFF; | ||
| 152 | + d = (src[1] >> 0) & 0xFF; | ||
| 153 | + | ||
| 154 | + i = (d << 24) | | ||
| 155 | + (c << 16) | | ||
| 156 | + (b << 8) | | ||
| 157 | + (a << 0); | ||
| 158 | + memcpy(&f, &i, 4); | ||
| 141 | 159 | ||
| 142 | return f; | 160 | return f; |
| 143 | } | 161 | } |
| @@ -147,9 +165,18 @@ float modbus_get_float_badc(const uint16_t *src) | @@ -147,9 +165,18 @@ float modbus_get_float_badc(const uint16_t *src) | ||
| 147 | { | 165 | { |
| 148 | float f; | 166 | float f; |
| 149 | uint32_t i; | 167 | uint32_t i; |
| 168 | + uint8_t a, b, c, d; | ||
| 150 | 169 | ||
| 151 | - i = ntohl((uint32_t)(bswap_16(src[0]) << 16) + bswap_16(src[1])); | ||
| 152 | - memcpy(&f, &i, sizeof(float)); | 170 | + a = (src[0] >> 8) & 0xFF; |
| 171 | + b = (src[0] >> 0) & 0xFF; | ||
| 172 | + c = (src[1] >> 8) & 0xFF; | ||
| 173 | + d = (src[1] >> 0) & 0xFF; | ||
| 174 | + | ||
| 175 | + i = (b << 24) | | ||
| 176 | + (a << 16) | | ||
| 177 | + (d << 8) | | ||
| 178 | + (c << 0); | ||
| 179 | + memcpy(&f, &i, 4); | ||
| 153 | 180 | ||
| 154 | return f; | 181 | return f; |
| 155 | } | 182 | } |
| @@ -159,9 +186,18 @@ float modbus_get_float_cdab(const uint16_t *src) | @@ -159,9 +186,18 @@ float modbus_get_float_cdab(const uint16_t *src) | ||
| 159 | { | 186 | { |
| 160 | float f; | 187 | float f; |
| 161 | uint32_t i; | 188 | uint32_t i; |
| 189 | + uint8_t a, b, c, d; | ||
| 162 | 190 | ||
| 163 | - i = ntohl((((uint32_t)src[1]) << 16) + src[0]); | ||
| 164 | - memcpy(&f, &i, sizeof(float)); | 191 | + a = (src[0] >> 8) & 0xFF; |
| 192 | + b = (src[0] >> 0) & 0xFF; | ||
| 193 | + c = (src[1] >> 8) & 0xFF; | ||
| 194 | + d = (src[1] >> 0) & 0xFF; | ||
| 195 | + | ||
| 196 | + i = (c << 24) | | ||
| 197 | + (d << 16) | | ||
| 198 | + (a << 8) | | ||
| 199 | + (b << 0); | ||
| 200 | + memcpy(&f, &i, 4); | ||
| 165 | 201 | ||
| 166 | return f; | 202 | return f; |
| 167 | } | 203 | } |
| @@ -176,50 +212,84 @@ float modbus_get_float(const uint16_t *src) | @@ -176,50 +212,84 @@ float modbus_get_float(const uint16_t *src) | ||
| 176 | memcpy(&f, &i, sizeof(float)); | 212 | memcpy(&f, &i, sizeof(float)); |
| 177 | 213 | ||
| 178 | return f; | 214 | return f; |
| 215 | + | ||
| 179 | } | 216 | } |
| 180 | 217 | ||
| 181 | /* Set a float to 4 bytes for Modbus w/o any conversion (ABCD) */ | 218 | /* Set a float to 4 bytes for Modbus w/o any conversion (ABCD) */ |
| 182 | void modbus_set_float_abcd(float f, uint16_t *dest) | 219 | void modbus_set_float_abcd(float f, uint16_t *dest) |
| 183 | { | 220 | { |
| 184 | uint32_t i; | 221 | uint32_t i; |
| 222 | + uint8_t *out = (uint8_t*) dest; | ||
| 223 | + uint8_t a, b, c, d; | ||
| 185 | 224 | ||
| 186 | memcpy(&i, &f, sizeof(uint32_t)); | 225 | memcpy(&i, &f, sizeof(uint32_t)); |
| 187 | - i = htonl(i); | ||
| 188 | - dest[0] = (uint16_t)(i >> 16); | ||
| 189 | - dest[1] = (uint16_t)i; | 226 | + a = (i >> 24) & 0xFF; |
| 227 | + b = (i >> 16) & 0xFF; | ||
| 228 | + c = (i >> 8) & 0xFF; | ||
| 229 | + d = (i >> 0) & 0xFF; | ||
| 230 | + | ||
| 231 | + out[0] = a; | ||
| 232 | + out[1] = b; | ||
| 233 | + out[2] = c; | ||
| 234 | + out[3] = d; | ||
| 190 | } | 235 | } |
| 191 | 236 | ||
| 192 | /* Set a float to 4 bytes for Modbus with byte and word swap conversion (DCBA) */ | 237 | /* Set a float to 4 bytes for Modbus with byte and word swap conversion (DCBA) */ |
| 193 | void modbus_set_float_dcba(float f, uint16_t *dest) | 238 | void modbus_set_float_dcba(float f, uint16_t *dest) |
| 194 | { | 239 | { |
| 195 | uint32_t i; | 240 | uint32_t i; |
| 241 | + uint8_t *out = (uint8_t*) dest; | ||
| 242 | + uint8_t a, b, c, d; | ||
| 196 | 243 | ||
| 197 | memcpy(&i, &f, sizeof(uint32_t)); | 244 | memcpy(&i, &f, sizeof(uint32_t)); |
| 198 | - i = bswap_32(htonl(i)); | ||
| 199 | - dest[0] = (uint16_t)(i >> 16); | ||
| 200 | - dest[1] = (uint16_t)i; | 245 | + a = (i >> 24) & 0xFF; |
| 246 | + b = (i >> 16) & 0xFF; | ||
| 247 | + c = (i >> 8) & 0xFF; | ||
| 248 | + d = (i >> 0) & 0xFF; | ||
| 249 | + | ||
| 250 | + out[0] = d; | ||
| 251 | + out[1] = c; | ||
| 252 | + out[2] = b; | ||
| 253 | + out[3] = a; | ||
| 254 | + | ||
| 201 | } | 255 | } |
| 202 | 256 | ||
| 203 | /* Set a float to 4 bytes for Modbus with byte swap conversion (BADC) */ | 257 | /* Set a float to 4 bytes for Modbus with byte swap conversion (BADC) */ |
| 204 | void modbus_set_float_badc(float f, uint16_t *dest) | 258 | void modbus_set_float_badc(float f, uint16_t *dest) |
| 205 | { | 259 | { |
| 206 | uint32_t i; | 260 | uint32_t i; |
| 261 | + uint8_t *out = (uint8_t*) dest; | ||
| 262 | + uint8_t a, b, c, d; | ||
| 207 | 263 | ||
| 208 | memcpy(&i, &f, sizeof(uint32_t)); | 264 | memcpy(&i, &f, sizeof(uint32_t)); |
| 209 | - i = htonl(i); | ||
| 210 | - dest[0] = (uint16_t)bswap_16(i >> 16); | ||
| 211 | - dest[1] = (uint16_t)bswap_16(i & 0xFFFF); | 265 | + a = (i >> 24) & 0xFF; |
| 266 | + b = (i >> 16) & 0xFF; | ||
| 267 | + c = (i >> 8) & 0xFF; | ||
| 268 | + d = (i >> 0) & 0xFF; | ||
| 269 | + | ||
| 270 | + out[0] = b; | ||
| 271 | + out[1] = a; | ||
| 272 | + out[2] = d; | ||
| 273 | + out[3] = c; | ||
| 212 | } | 274 | } |
| 213 | 275 | ||
| 214 | /* Set a float to 4 bytes for Modbus with word swap conversion (CDAB) */ | 276 | /* Set a float to 4 bytes for Modbus with word swap conversion (CDAB) */ |
| 215 | void modbus_set_float_cdab(float f, uint16_t *dest) | 277 | void modbus_set_float_cdab(float f, uint16_t *dest) |
| 216 | { | 278 | { |
| 217 | uint32_t i; | 279 | uint32_t i; |
| 280 | + uint8_t *out = (uint8_t*) dest; | ||
| 281 | + uint8_t a, b, c, d; | ||
| 218 | 282 | ||
| 219 | memcpy(&i, &f, sizeof(uint32_t)); | 283 | memcpy(&i, &f, sizeof(uint32_t)); |
| 220 | - i = htonl(i); | ||
| 221 | - dest[0] = (uint16_t)i; | ||
| 222 | - dest[1] = (uint16_t)(i >> 16); | 284 | + a = (i >> 24) & 0xFF; |
| 285 | + b = (i >> 16) & 0xFF; | ||
| 286 | + c = (i >> 8) & 0xFF; | ||
| 287 | + d = (i >> 0) & 0xFF; | ||
| 288 | + | ||
| 289 | + out[0] = c; | ||
| 290 | + out[1] = d; | ||
| 291 | + out[2] = a; | ||
| 292 | + out[3] = b; | ||
| 223 | } | 293 | } |
| 224 | 294 | ||
| 225 | /* DEPRECATED - Set a float to 4 bytes in a sort of Modbus format! */ | 295 | /* DEPRECATED - Set a float to 4 bytes in a sort of Modbus format! */ |
tests/unit-test-client.c
| @@ -27,6 +27,7 @@ int send_crafted_request(modbus_t *ctx, int function, | @@ -27,6 +27,7 @@ int send_crafted_request(modbus_t *ctx, int function, | ||
| 27 | uint16_t max_value, uint16_t bytes, | 27 | uint16_t max_value, uint16_t bytes, |
| 28 | int backend_length, int backend_offset); | 28 | int backend_length, int backend_offset); |
| 29 | int equal_dword(uint16_t *tab_reg, const uint32_t value); | 29 | int equal_dword(uint16_t *tab_reg, const uint32_t value); |
| 30 | +int is_memory_equal(const void *s1, const void *s2, size_t size); | ||
| 30 | 31 | ||
| 31 | #define BUG_REPORT(_cond, _format, _args ...) \ | 32 | #define BUG_REPORT(_cond, _format, _args ...) \ |
| 32 | printf("\nLine %d: assertion error for '%s': " _format "\n", __LINE__, # _cond, ## _args) | 33 | printf("\nLine %d: assertion error for '%s': " _format "\n", __LINE__, # _cond, ## _args) |
| @@ -40,6 +41,11 @@ int equal_dword(uint16_t *tab_reg, const uint32_t value); | @@ -40,6 +41,11 @@ int equal_dword(uint16_t *tab_reg, const uint32_t value); | ||
| 40 | } \ | 41 | } \ |
| 41 | }; | 42 | }; |
| 42 | 43 | ||
| 44 | +int is_memory_equal(const void *s1, const void *s2, size_t size) | ||
| 45 | +{ | ||
| 46 | + return (memcmp(s1, s2, size) == 0); | ||
| 47 | +} | ||
| 48 | + | ||
| 43 | int equal_dword(uint16_t *tab_reg, const uint32_t value) { | 49 | int equal_dword(uint16_t *tab_reg, const uint32_t value) { |
| 44 | return ((tab_reg[0] == (value >> 16)) && (tab_reg[1] == (value & 0xFFFF))); | 50 | return ((tab_reg[0] == (value >> 16)) && (tab_reg[1] == (value & 0xFFFF))); |
| 45 | } | 51 | } |
| @@ -287,26 +293,26 @@ int main(int argc, char *argv[]) | @@ -287,26 +293,26 @@ int main(int argc, char *argv[]) | ||
| 287 | /** FLOAT **/ | 293 | /** FLOAT **/ |
| 288 | printf("1/4 Set/get float ABCD: "); | 294 | printf("1/4 Set/get float ABCD: "); |
| 289 | modbus_set_float_abcd(UT_REAL, tab_rp_registers); | 295 | modbus_set_float_abcd(UT_REAL, tab_rp_registers); |
| 290 | - ASSERT_TRUE(equal_dword(tab_rp_registers, UT_IREAL_ABCD), "FAILED Set float ABCD"); | ||
| 291 | - real = modbus_get_float_abcd(tab_rp_registers); | 296 | + ASSERT_TRUE(is_memory_equal(tab_rp_registers, UT_IREAL_ABCD_SET, 4), "FAILED Set float ABCD"); |
| 297 | + real = modbus_get_float_abcd(UT_IREAL_ABCD_GET); | ||
| 292 | ASSERT_TRUE(real == UT_REAL, "FAILED (%f != %f)\n", real, UT_REAL); | 298 | ASSERT_TRUE(real == UT_REAL, "FAILED (%f != %f)\n", real, UT_REAL); |
| 293 | 299 | ||
| 294 | printf("2/4 Set/get float DCBA: "); | 300 | printf("2/4 Set/get float DCBA: "); |
| 295 | modbus_set_float_dcba(UT_REAL, tab_rp_registers); | 301 | modbus_set_float_dcba(UT_REAL, tab_rp_registers); |
| 296 | - ASSERT_TRUE(equal_dword(tab_rp_registers, UT_IREAL_DCBA), "FAILED Set float DCBA"); | ||
| 297 | - real = modbus_get_float_dcba(tab_rp_registers); | 302 | + ASSERT_TRUE(is_memory_equal(tab_rp_registers, UT_IREAL_DCBA_SET, 4), "FAILED Set float DCBA"); |
| 303 | + real = modbus_get_float_dcba(UT_IREAL_DCBA_GET); | ||
| 298 | ASSERT_TRUE(real == UT_REAL, "FAILED (%f != %f)\n", real, UT_REAL); | 304 | ASSERT_TRUE(real == UT_REAL, "FAILED (%f != %f)\n", real, UT_REAL); |
| 299 | 305 | ||
| 300 | printf("3/4 Set/get float BADC: "); | 306 | printf("3/4 Set/get float BADC: "); |
| 301 | modbus_set_float_badc(UT_REAL, tab_rp_registers); | 307 | modbus_set_float_badc(UT_REAL, tab_rp_registers); |
| 302 | - ASSERT_TRUE(equal_dword(tab_rp_registers, UT_IREAL_BADC), "FAILED Set float BADC"); | ||
| 303 | - real = modbus_get_float_badc(tab_rp_registers); | 308 | + ASSERT_TRUE(is_memory_equal(tab_rp_registers, UT_IREAL_BADC_SET, 4), "FAILED Set float BADC"); |
| 309 | + real = modbus_get_float_badc(UT_IREAL_BADC_GET); | ||
| 304 | ASSERT_TRUE(real == UT_REAL, "FAILED (%f != %f)\n", real, UT_REAL); | 310 | ASSERT_TRUE(real == UT_REAL, "FAILED (%f != %f)\n", real, UT_REAL); |
| 305 | 311 | ||
| 306 | printf("4/4 Set/get float CDAB: "); | 312 | printf("4/4 Set/get float CDAB: "); |
| 307 | modbus_set_float_cdab(UT_REAL, tab_rp_registers); | 313 | modbus_set_float_cdab(UT_REAL, tab_rp_registers); |
| 308 | - ASSERT_TRUE(equal_dword(tab_rp_registers, UT_IREAL_CDAB), "FAILED Set float CDAB"); | ||
| 309 | - real = modbus_get_float_cdab(tab_rp_registers); | 314 | + ASSERT_TRUE(is_memory_equal(tab_rp_registers, UT_IREAL_CDAB_SET, 4), "FAILED Set float CDAB"); |
| 315 | + real = modbus_get_float_cdab(UT_IREAL_CDAB_GET); | ||
| 310 | ASSERT_TRUE(real == UT_REAL, "FAILED (%f != %f)\n", real, UT_REAL); | 316 | ASSERT_TRUE(real == UT_REAL, "FAILED (%f != %f)\n", real, UT_REAL); |
| 311 | 317 | ||
| 312 | printf("\nAt this point, error messages doesn't mean the test has failed\n"); | 318 | printf("\nAt this point, error messages doesn't mean the test has failed\n"); |
tests/unit-test.h.in
| @@ -56,12 +56,45 @@ const uint16_t UT_INPUT_REGISTERS_ADDRESS = 0x108; | @@ -56,12 +56,45 @@ const uint16_t UT_INPUT_REGISTERS_ADDRESS = 0x108; | ||
| 56 | const uint16_t UT_INPUT_REGISTERS_NB = 0x1; | 56 | const uint16_t UT_INPUT_REGISTERS_NB = 0x1; |
| 57 | const uint16_t UT_INPUT_REGISTERS_TAB[] = { 0x000A }; | 57 | const uint16_t UT_INPUT_REGISTERS_TAB[] = { 0x000A }; |
| 58 | 58 | ||
| 59 | +/* | ||
| 60 | + * This float value is 0x47F12000 (in big-endian format). | ||
| 61 | + * In Little-endian(intel) format, it will be stored in memory as follows: | ||
| 62 | + * 0x00 0x20 0xF1 0x47 | ||
| 63 | + * | ||
| 64 | + * You can check this with the following code: | ||
| 65 | + | ||
| 66 | + float fl = UT_REAL; | ||
| 67 | + uint8_t *inmem = (uint8_t*)&fl; | ||
| 68 | + int x; | ||
| 69 | + for(x = 0; x < 4; x++){ | ||
| 70 | + printf("0x%02X ", inmem[ x ]); | ||
| 71 | + } | ||
| 72 | + printf("\n"); | ||
| 73 | + */ | ||
| 59 | const float UT_REAL = 123456.00; | 74 | const float UT_REAL = 123456.00; |
| 60 | 75 | ||
| 61 | -const uint32_t UT_IREAL_ABCD = 0x0020F147; | ||
| 62 | -const uint32_t UT_IREAL_DCBA = 0x47F12000; | ||
| 63 | -const uint32_t UT_IREAL_BADC = 0x200047F1; | ||
| 64 | -const uint32_t UT_IREAL_CDAB = 0xF1470020; | 76 | +/* |
| 77 | + * The following arrays assume that 'A' is the MSB, | ||
| 78 | + * and 'D' is the LSB. | ||
| 79 | + * Thus, the following is the case: | ||
| 80 | + * A = 0x47 | ||
| 81 | + * B = 0xF1 | ||
| 82 | + * C = 0x20 | ||
| 83 | + * D = 0x00 | ||
| 84 | + * | ||
| 85 | + * There are two sets of arrays: one to test that the setting is correct, | ||
| 86 | + * the other to test that the getting is correct. | ||
| 87 | + * Note that the 'get' values must be constants in processor-endianness, | ||
| 88 | + * as libmodbus will convert all words to processor-endianness as they come in. | ||
| 89 | + */ | ||
| 90 | +const uint8_t UT_IREAL_ABCD_SET[] = {0x47, 0xF1, 0x20, 0x00}; | ||
| 91 | +const uint16_t UT_IREAL_ABCD_GET[] = {0x47F1, 0x2000}; | ||
| 92 | +const uint8_t UT_IREAL_DCBA_SET[] = {0x00, 0x20, 0xF1, 0x47}; | ||
| 93 | +const uint16_t UT_IREAL_DCBA_GET[] = {0x0020, 0xF147}; | ||
| 94 | +const uint8_t UT_IREAL_BADC_SET[] = {0xF1, 0x47, 0x00, 0x20}; | ||
| 95 | +const uint16_t UT_IREAL_BADC_GET[] = {0xF147, 0x0020}; | ||
| 96 | +const uint8_t UT_IREAL_CDAB_SET[] = {0x20, 0x00, 0x47, 0xF1}; | ||
| 97 | +const uint16_t UT_IREAL_CDAB_GET[] = {0x2000, 0x47F1}; | ||
| 65 | 98 | ||
| 66 | /* const uint32_t UT_IREAL_ABCD = 0x47F12000); | 99 | /* const uint32_t UT_IREAL_ABCD = 0x47F12000); |
| 67 | const uint32_t UT_IREAL_DCBA = 0x0020F147; | 100 | const uint32_t UT_IREAL_DCBA = 0x0020F147; |