Skip to content

Commit 13bd584

Browse files
authored
Fixes float encoding/decoding for both big and little endian (fixes #665, #694)
* Removes useless memcpy calls and no longer used swap32 and swap16 macros. * Updated float encoding functions to consider endianess of 16-bit registers (fixes #665) * Deprecated modbus_get_float() and modbus_set_float() are implemented using cdab variants.
1 parent 98f5764 commit 13bd584

File tree

1 file changed

+54
-112
lines changed

1 file changed

+54
-112
lines changed

src/modbus-data.c

Lines changed: 54 additions & 112 deletions
Original file line numberDiff line numberDiff line change
@@ -26,50 +26,6 @@
2626

2727
#include "modbus.h"
2828

29-
#if defined(HAVE_BYTESWAP_H)
30-
# include <byteswap.h>
31-
#endif
32-
33-
#if defined(__APPLE__)
34-
# include <libkern/OSByteOrder.h>
35-
# define bswap_16 OSSwapInt16
36-
# define bswap_32 OSSwapInt32
37-
# define bswap_64 OSSwapInt64
38-
#endif
39-
40-
#if defined(__GNUC__)
41-
# define GCC_VERSION (__GNUC__ * 100 + __GNUC_MINOR__ * 10)
42-
# if GCC_VERSION >= 430
43-
// Since GCC >= 4.30, GCC provides __builtin_bswapXX() alternatives so we switch to them
44-
# undef bswap_32
45-
# define bswap_32 __builtin_bswap32
46-
# endif
47-
# if GCC_VERSION >= 480
48-
# undef bswap_16
49-
# define bswap_16 __builtin_bswap16
50-
# endif
51-
#endif
52-
53-
#if defined(_MSC_VER) && (_MSC_VER >= 1400)
54-
# define bswap_32 _byteswap_ulong
55-
# define bswap_16 _byteswap_ushort
56-
#endif
57-
58-
#if !defined(bswap_16)
59-
# warning "Fallback on C functions for bswap_16"
60-
static inline uint16_t bswap_16(uint16_t x)
61-
{
62-
return (x >> 8) | (x << 8);
63-
}
64-
#endif
65-
66-
#if !defined(bswap_32)
67-
# warning "Fallback on C functions for bswap_32"
68-
static inline uint32_t bswap_32(uint32_t x)
69-
{
70-
return (bswap_16(x & 0xffff) << 16) | (bswap_16(x >> 16));
71-
}
72-
#endif
7329
// clang-format on
7430

7531
/* Sets many bits from a single byte value (all 8 bits of the byte value are
@@ -124,17 +80,29 @@ uint8_t modbus_get_byte_from_bits(const uint8_t *src, int idx, unsigned int nb_b
12480
/* Get a float from 4 bytes (Modbus) without any conversion (ABCD) */
12581
float modbus_get_float_abcd(const uint16_t *src)
12682
{
83+
/* Suppose the modbus byte stream contained the 32-bit float 0x10203040 - abcd.
84+
On big endian systems, the memory starting at src contains two 16-bit registers 0x1020 and 0x3040
85+
On little endian system, the 16-bit registers memory holds 0x2010 and 0x4030
86+
87+
To convert the data to float32 on big-endian we only need to cast the pointer and we are done.
88+
On little endian systems, we need to swap the bytes in each word again and then assemble
89+
an integer via shift operations and finally cast to float32.
90+
91+
A portable way is to retrieve low and high bytes of both words using shift operations, then assemble
92+
the 32-bit integer.
93+
*/
94+
12795
float f;
128-
uint32_t i;
12996
uint8_t a, b, c, d;
13097

131-
a = (src[0] >> 8) & 0xFF;
132-
b = (src[0] >> 0) & 0xFF;
133-
c = (src[1] >> 8) & 0xFF;
134-
d = (src[1] >> 0) & 0xFF;
98+
a = (src[0] >> 8) & 0xFF; // high byte if first word
99+
b = (src[0] >> 0) & 0xFF; // low byte if first word
100+
c = (src[1] >> 8) & 0xFF; // high byte if second word
101+
d = (src[1] >> 0) & 0xFF; // low byte if second word
135102

136-
i = (a << 24) | (b << 16) | (c << 8) | (d << 0);
137-
memcpy(&f, &i, 4);
103+
// assemble in memory location of float
104+
// from right to left: get address of float, interpret as address to uint32, dereference and write uint32
105+
*(uint32_t *)&f = (a << 24) | (b << 16) | (c << 8) | (d << 0);
138106

139107
return f;
140108
}
@@ -143,16 +111,16 @@ float modbus_get_float_abcd(const uint16_t *src)
143111
float modbus_get_float_dcba(const uint16_t *src)
144112
{
145113
float f;
146-
uint32_t i;
147114
uint8_t a, b, c, d;
148115

149-
a = (src[0] >> 8) & 0xFF;
150-
b = (src[0] >> 0) & 0xFF;
151-
c = (src[1] >> 8) & 0xFF;
152-
d = (src[1] >> 0) & 0xFF;
116+
d = (src[0] >> 8) & 0xFF;
117+
c = (src[0] >> 0) & 0xFF;
118+
b = (src[1] >> 8) & 0xFF;
119+
a = (src[1] >> 0) & 0xFF;
153120

154-
i = (d << 24) | (c << 16) | (b << 8) | (a << 0);
155-
memcpy(&f, &i, 4);
121+
// assemble in memory location of float
122+
// from right to left: get address of float, interpret as address to uint32, dereference and write uint32
123+
*(uint32_t *)&f = (a << 24) | (b << 16) | (c << 8) | (d << 0);
156124

157125
return f;
158126
}
@@ -161,16 +129,16 @@ float modbus_get_float_dcba(const uint16_t *src)
161129
float modbus_get_float_badc(const uint16_t *src)
162130
{
163131
float f;
164-
uint32_t i;
165132
uint8_t a, b, c, d;
166133

167-
a = (src[0] >> 8) & 0xFF;
168-
b = (src[0] >> 0) & 0xFF;
169-
c = (src[1] >> 8) & 0xFF;
170-
d = (src[1] >> 0) & 0xFF;
134+
b = (src[0] >> 8) & 0xFF;
135+
a = (src[0] >> 0) & 0xFF;
136+
d = (src[1] >> 8) & 0xFF;
137+
c = (src[1] >> 0) & 0xFF;
171138

172-
i = (b << 24) | (a << 16) | (d << 8) | (c << 0);
173-
memcpy(&f, &i, 4);
139+
// assemble in memory location of float
140+
// from right to left: get address of float, interpret as address to uint32, dereference and write uint32
141+
*(uint32_t *)&f = (a << 24) | (b << 16) | (c << 8) | (d << 0);
174142

175143
return f;
176144
}
@@ -179,114 +147,88 @@ float modbus_get_float_badc(const uint16_t *src)
179147
float modbus_get_float_cdab(const uint16_t *src)
180148
{
181149
float f;
182-
uint32_t i;
183150
uint8_t a, b, c, d;
184151

185-
a = (src[0] >> 8) & 0xFF;
186-
b = (src[0] >> 0) & 0xFF;
187-
c = (src[1] >> 8) & 0xFF;
188-
d = (src[1] >> 0) & 0xFF;
152+
c = (src[0] >> 8) & 0xFF;
153+
d = (src[0] >> 0) & 0xFF;
154+
a = (src[1] >> 8) & 0xFF;
155+
b = (src[1] >> 0) & 0xFF;
189156

190-
i = (c << 24) | (d << 16) | (a << 8) | (b << 0);
191-
memcpy(&f, &i, 4);
157+
// assemble in memory location of float
158+
// from right to left: get address of float, interpret as address to uint32, dereference and write uint32
159+
*(uint32_t *)&f = (a << 24) | (b << 16) | (c << 8) | (d << 0);
192160

193161
return f;
194162
}
195163

196164
/* DEPRECATED - Get a float from 4 bytes in sort of Modbus format */
197165
float modbus_get_float(const uint16_t *src)
198166
{
199-
float f;
200-
uint32_t i;
201-
202-
i = (((uint32_t) src[1]) << 16) + src[0];
203-
memcpy(&f, &i, sizeof(float));
204-
205-
return f;
167+
return modbus_get_float_cdab(src);
206168
}
207169

208170
/* Set a float to 4 bytes for Modbus w/o any conversion (ABCD) */
209171
void modbus_set_float_abcd(float f, uint16_t *dest)
210172
{
211-
uint32_t i;
212-
uint8_t *out = (uint8_t *) dest;
173+
uint32_t i = *(uint32_t*)(&f);
213174
uint8_t a, b, c, d;
214175

215-
memcpy(&i, &f, sizeof(uint32_t));
216176
a = (i >> 24) & 0xFF;
217177
b = (i >> 16) & 0xFF;
218178
c = (i >> 8) & 0xFF;
219179
d = (i >> 0) & 0xFF;
220180

221-
out[0] = a;
222-
out[1] = b;
223-
out[2] = c;
224-
out[3] = d;
181+
dest[0] = (a << 8) | b;
182+
dest[1] = (c << 8) | d;
225183
}
226184

227185
/* Set a float to 4 bytes for Modbus with byte and word swap conversion (DCBA) */
228186
void modbus_set_float_dcba(float f, uint16_t *dest)
229187
{
230-
uint32_t i;
231-
uint8_t *out = (uint8_t *) dest;
188+
uint32_t i = *(uint32_t*)(&f);
232189
uint8_t a, b, c, d;
233190

234-
memcpy(&i, &f, sizeof(uint32_t));
235191
a = (i >> 24) & 0xFF;
236192
b = (i >> 16) & 0xFF;
237193
c = (i >> 8) & 0xFF;
238194
d = (i >> 0) & 0xFF;
239195

240-
out[0] = d;
241-
out[1] = c;
242-
out[2] = b;
243-
out[3] = a;
196+
dest[0] = (d << 8) | c;
197+
dest[1] = (b << 8) | a;
244198
}
245199

246200
/* Set a float to 4 bytes for Modbus with byte swap conversion (BADC) */
247201
void modbus_set_float_badc(float f, uint16_t *dest)
248202
{
249-
uint32_t i;
250-
uint8_t *out = (uint8_t *) dest;
203+
uint32_t i = *(uint32_t*)(&f);
251204
uint8_t a, b, c, d;
252205

253-
memcpy(&i, &f, sizeof(uint32_t));
254206
a = (i >> 24) & 0xFF;
255207
b = (i >> 16) & 0xFF;
256208
c = (i >> 8) & 0xFF;
257209
d = (i >> 0) & 0xFF;
258210

259-
out[0] = b;
260-
out[1] = a;
261-
out[2] = d;
262-
out[3] = c;
211+
dest[0] = (b << 8) | a;
212+
dest[1] = (d << 8) | c;
263213
}
264214

265215
/* Set a float to 4 bytes for Modbus with word swap conversion (CDAB) */
266216
void modbus_set_float_cdab(float f, uint16_t *dest)
267217
{
268-
uint32_t i;
269-
uint8_t *out = (uint8_t *) dest;
218+
uint32_t i = *(uint32_t*)(&f);
270219
uint8_t a, b, c, d;
271220

272-
memcpy(&i, &f, sizeof(uint32_t));
273221
a = (i >> 24) & 0xFF;
274222
b = (i >> 16) & 0xFF;
275223
c = (i >> 8) & 0xFF;
276224
d = (i >> 0) & 0xFF;
277225

278-
out[0] = c;
279-
out[1] = d;
280-
out[2] = a;
281-
out[3] = b;
226+
dest[0] = (c << 8) | d;
227+
dest[1] = (a << 8) | b;
282228
}
283229

284230
/* DEPRECATED - Set a float to 4 bytes in a sort of Modbus format! */
285231
void modbus_set_float(float f, uint16_t *dest)
286232
{
287-
uint32_t i;
288-
289-
memcpy(&i, &f, sizeof(uint32_t));
290-
dest[0] = (uint16_t) i;
291-
dest[1] = (uint16_t) (i >> 16);
233+
return modbus_set_float_cdab(f, dest);
292234
}

0 commit comments

Comments
 (0)