API
 
Loading...
Searching...
No Matches
zb_serial.c
Go to the documentation of this file.
1/*
2 * \file zb_serial.c
3 * \author Eric Dand
4 * \version 1.0
5 * \date 28 November 2014
6 * \copyright Apache Software License Version 2.0
7 *
8 * Implementation file for binary portion of the Zaber Serial API in C.
9 * See zb_serial.h for documentation.
10 */
11#include <string.h>
12#include <stdio.h>
13#include <stdint.h>
14#include <limits.h>
15
16#include "zb_serial.h"
17
18static int zb_verbose = 1;
19
20void zb_set_verbose(int value)
21{
22 zb_verbose = value;
23}
24
25int zb_encode(uint8_t *destination, uint8_t device_number,
26 uint8_t command_number, int32_t data)
27{
28 unsigned int i;
29 uint32_t udata = (uint32_t)data;
30
31 if (destination == NULL)
32 {
34 }
35
36 destination[0] = device_number;
37 destination[1] = command_number;
38
39 for (i = 2; i < 6; i++)
40 {
41 destination[i] = (uint8_t)udata;
42 udata >>= 8;
43 }
44
45 return Z_SUCCESS;
46}
47
48int zb_decode(int32_t *destination, const uint8_t *reply)
49{
50 unsigned int i;
51 uint32_t data = 0;
52
53 if (destination == NULL)
54 {
56 }
57
58 for (i = 5; i > 1; i--)
59 {
60 data <<= 8;
61 data |= reply[i];
62 }
63
64 if ((data & 0x80000000UL) == 0)
65 {
66 *destination = (int32_t)data;
67 }
68 else
69 {
70 *destination = -(int32_t)(UINT32_MAX - data) - 1;
71 }
72
73 return Z_SUCCESS;
74}
75
76#if defined(_WIN32)
77
78/* These macros save us a lot of repetition. Call the specified function,
79 * and complain and return early with -1 if things go badly. */
80#if defined(NDEBUG)
81#define PRINT_ERROR(M)
82#define PRINT_SYSCALL_ERROR(M)
83#else
84#define PRINT_ERROR(M) do { if (zb_verbose) { fprintf(stderr, "(%s: %d)" M\
85 "\n", __FILE__, __LINE__); } } while(0)
86#define PRINT_SYSCALL_ERROR(M) do { if (zb_verbose) { fprintf(stderr,\
87 "(%s: %d) [ERROR] " M " failed with error code %d.\n",\
88 __FILE__, __LINE__, GetLastError()); } } while(0)
89#endif
90#define SYSCALL(F) do { if ((F) == 0) {\
91 PRINT_SYSCALL_ERROR(#F); return Z_ERROR_SYSTEM_ERROR; } } while (0)
92
93int zb_connect(z_port *port, const char *port_name)
94{
95 DCB dcb = { 0 };
96 COMMTIMEOUTS timeouts;
97
98 if (port_name == NULL)
99 {
100 PRINT_ERROR("[ERROR] port name cannot be NULL.");
102 }
103
104 *port = CreateFileA(port_name,
105 GENERIC_READ | GENERIC_WRITE,
106 0,
107 NULL,
108 OPEN_EXISTING,
109 0,
110 NULL);
111 if (*port == INVALID_HANDLE_VALUE)
112 {
113 PRINT_SYSCALL_ERROR("CreateFileA");
115 }
116
117 SYSCALL(GetCommState(*port, &dcb));
118 dcb.DCBlength = sizeof(DCB);
119 dcb.BaudRate = 9600;
120 dcb.fBinary = TRUE; /* Binary Mode (skip EOF check) */
121 dcb.fParity = FALSE; /* Disable parity checking */
122 dcb.fOutxCtsFlow = FALSE; /* No CTS handshaking on output */
123 dcb.fOutxDsrFlow = FALSE; /* No DSR handshaking on output */
124 dcb.fDtrControl = DTR_CONTROL_DISABLE; /* Disable DTR Flow control */
125 dcb.fDsrSensitivity = FALSE; /* No DSR Sensitivity */
126 dcb.fTXContinueOnXoff = TRUE; /* Continue TX when Xoff sent */
127 dcb.fOutX = FALSE; /* Disable output X-ON/X-OFF */
128 dcb.fInX = FALSE; /* Disable input X-ON/X-OFF */
129 dcb.fErrorChar = FALSE; /* Disable Err Replacement */
130 dcb.fNull = FALSE; /* Disable Null stripping */
131 dcb.fRtsControl = RTS_CONTROL_DISABLE; /* Disable Rts Flow control */
132 dcb.fAbortOnError = FALSE; /* Do not abort all reads and writes on Error */
133 dcb.wReserved = 0; /* Not currently used, but must be set to 0 */
134 dcb.XonLim = 0; /* Transmit X-ON threshold */
135 dcb.XoffLim = 0; /* Transmit X-OFF threshold */
136 dcb.ByteSize = 8; /* Number of bits/byte, 4-8 */
137 dcb.Parity = NOPARITY; /* 0-4=None,Odd,Even,Mark,Space */
138 dcb.StopBits = ONESTOPBIT; /* 0,1,2 = 1, 1.5, 2 */
139 SYSCALL(SetCommState(*port, &dcb));
140
141 timeouts.ReadIntervalTimeout = MAXDWORD;
142 timeouts.ReadTotalTimeoutMultiplier = MAXDWORD;
143 timeouts.ReadTotalTimeoutConstant = READ_TIMEOUT; /* #defined in header */
144 timeouts.WriteTotalTimeoutMultiplier = 0;
145 timeouts.WriteTotalTimeoutConstant = 100;
146 SYSCALL(SetCommTimeouts(*port, &timeouts));
147
148 return Z_SUCCESS;
149}
150
151int zb_disconnect(z_port port)
152{
153 SYSCALL(CloseHandle(port));
154 return Z_SUCCESS;
155}
156
157int zb_send(z_port port, const uint8_t *command)
158{
159 DWORD nbytes;
160
161 if (command == NULL)
162 {
163 PRINT_ERROR("[ERROR] command cannot be NULL.");
165 }
166
167 SYSCALL(WriteFile(port, command, 6, &nbytes, NULL));
168 if (nbytes == 6)
169 {
170 return (int) nbytes;
171 }
173}
174
175/* We read bytes one-at-a-time as ReadFile(port, destination, 6, &nread, NULL)
176 * often enough will "read" bytes of \0 in the middle of a message when it
177 * should instead be waiting for a real byte of data down the line.
178 * Worse, it reports afterwards that it has read a full 6 bytes, making this
179 * behaviour hard to track and harder to debug and compensate for. */
180int zb_receive(z_port port, uint8_t *destination)
181{
182 DWORD nread;
183 int i;
184 char c;
185
186 for (i = 0; i < 6; i++)
187 {
188 SYSCALL(ReadFile(port, &c, 1, &nread, NULL));
189
190 if (nread == 0) /* timed out */
191 {
192 PRINT_ERROR("[INFO] Read timed out.");
193 break;
194 }
195
196 if (destination != NULL) destination[i] = c;
197 }
198
199 if (i == 6)
200 {
201 return i;
202 }
203 /* if we didn't read a whole 6 bytes, we count that as an error. */
205}
206
207int zb_drain(z_port port)
208{
209 char c;
210 DWORD nread,
211 old_timeout;
212 COMMTIMEOUTS timeouts;
213
214 SYSCALL(PurgeComm(port, PURGE_RXCLEAR));
215 SYSCALL(GetCommTimeouts(port, &timeouts));
216 old_timeout = timeouts.ReadTotalTimeoutConstant;
217 timeouts.ReadTotalTimeoutConstant = 100;
218 SYSCALL(SetCommTimeouts(port, &timeouts));
219
220 do
221 {
222 SYSCALL(ReadFile(port, &c, 1, &nread, NULL));
223 }
224 while (nread == 1);
225
226 timeouts.ReadTotalTimeoutConstant = old_timeout;
227 SYSCALL(SetCommTimeouts(port, &timeouts));
228
229 return Z_SUCCESS;
230}
231
232int zb_set_timeout(z_port port, int milliseconds)
233{
234 COMMTIMEOUTS timeouts;
235
236 SYSCALL(GetCommTimeouts(port, &timeouts));
237 timeouts.ReadTotalTimeoutConstant = milliseconds;
238 SYSCALL(SetCommTimeouts(port, &timeouts));
239
240 return milliseconds;
241}
242
243#elif defined(__unix__) || defined(__APPLE__) /* end of if defined(_WIN32) */
244#include <fcntl.h>
245#include <sys/types.h>
246#include <sys/stat.h>
247#include <termios.h>
248#include <unistd.h>
249
250/* A little sugar for checking return values from system calls.
251 * I would have liked to use GNU/GCC's "statement expressions" so that one
252 * do something like "z_port port = SYSCALL(open([parameters]))", but they're
253 * a GNU extension, and therefore unfriendly to non-GCC compilers.
254 * Workaround to avoid dependence on statement expressions for SYSCALL macro:
255 * use SYSCALL on result of assignment instead. */
256#if defined(NDEBUG)
257#define PRINT_ERROR(M)
258#define PRINT_SYSCALL_ERROR(M)
259#else
260#include <errno.h>
261#define PRINT_ERROR(M) do { if (zb_verbose) { fprintf(stderr, "(%s: %d)" M\
262 "\n", __FILE__, __LINE__); } } while(0)
263#define PRINT_SYSCALL_ERROR(M) do { if (zb_verbose) {\
264 fprintf(stderr, "(%s: %d) [ERROR] " M " failed: %s.\n",\
265 __FILE__, __LINE__, strerror(errno)); } } while(0)
266#endif
267/* A little sugar for checking return values from system calls.
268 * I would have liked to use GNU/GCC's "statement expressions" so that one
269 * do something like "z_port port = SYSCALL(open([parameters]))", but they're
270 * a GNU extension, and therefore unfriendly to non-GCC compilers.
271 * Workaround to avoid dependence on statement expressions for SYSCALL macro:
272 * use SYSCALL on result of assignment instead. */
273#define SYSCALL(F) do { if ((F) < 0) { PRINT_SYSCALL_ERROR(#F);\
274 return Z_ERROR_SYSTEM_ERROR; } } while(0)
275
276int zb_connect(z_port *port, const char *port_name)
277{
278 struct termios tio, orig_tio;
279
280 if (port == NULL || port_name == NULL)
281 {
282 PRINT_ERROR("[ERROR] port and port_name cannot be NULL.");
284 }
285
286 /* blocking read/write */
287 SYSCALL(*port = open(port_name, O_RDWR | O_NOCTTY));
288 SYSCALL(tcgetattr(*port, &orig_tio));
289 memcpy(&tio, &orig_tio, sizeof(struct termios)); /* copy padding too */
290
291 /* cfmakeraw() without cfmakeraw() for cygwin compatibility */
292 tio.c_iflag &= ~(IGNBRK | BRKINT | PARMRK | ISTRIP | INLCR
293 | IGNCR | ICRNL | IXON);
294 tio.c_oflag &= ~OPOST;
295 tio.c_lflag &= ~(ECHO | ECHONL | ICANON | ISIG | IEXTEN);
296 tio.c_cflag &= ~(CSIZE | PARENB);
297 /* end cfmakeraw() */
298 tio.c_cflag = CS8|CREAD|CLOCAL;
299
300 /* READ_TIMEOUT is defined in zb_serial.h */
301 if (READ_TIMEOUT % 100 != 0)
302 {
303 tio.c_cc[VTIME] = READ_TIMEOUT / 100 + 1; /* Round up */
304 }
305 else
306 {
307 tio.c_cc[VTIME] = READ_TIMEOUT / 100;
308 }
309 tio.c_cc[VMIN] = 0;
310
311 SYSCALL(cfsetospeed(&tio, B9600) & cfsetispeed(&tio, B9600));
312
313 while(memcmp(&orig_tio, &tio, sizeof(struct termios)) != 0)
314 { /* memcmp is only OK here because we used memcpy above */
315 SYSCALL(tcsetattr(*port, TCSAFLUSH, &tio));
316 SYSCALL(tcgetattr(*port, &orig_tio));
317 }
318
319 return Z_SUCCESS;
320}
321
322int zb_disconnect(z_port port)
323{
324 SYSCALL(close(port));
325 return Z_SUCCESS;
326}
327
328int zb_send(z_port port, const uint8_t *command)
329{
330 int nbytes;
331
332 SYSCALL(nbytes = write(port, command, 6));
333 if (nbytes == 6)
334 {
335 return nbytes;
336 }
338}
339
340/* More struggles with termios: we're forced to read one byte at a time on
341 * *NIX because of how termios.c_cc[VTIME] and [VMIN] work. From the termios
342 * man page:
343 *
344 * * if MIN == 0; TIME > 0: "read(2) returns either when at least one
345 * byte of data is available, or when the timer expires."
346 * * if MIN > 0; TIME > 0: "Because the timer is started only after the
347 * initial byte becomes available, at least one byte will be read."
348 *
349 * Neither of these cases are what we want, namely to start the timer
350 * immediately, and to only return when all 6 requested/MIN bytes are received
351 * or the timer times out. As a result, we instead set MIN to 0 (the first
352 * case above), then read 1 byte at a time to get the behaviour we want. */
353int zb_receive(z_port port, uint8_t *destination)
354{
355 int nread,
356 i;
357 char c;
358
359 for (i = 0; i < 6; i++)
360 {
361 SYSCALL(nread = (int) read(port, &c, 1));
362
363 if (nread == 0) /* timed out */
364 {
365 PRINT_ERROR("[INFO] Read timed out.");
366 break;
367 }
368
369 if (destination != NULL) destination[i] = c;
370 }
371
372 if (i == 6)
373 {
374 return i;
375 }
376 /* if we didn't read a whole 6 bytes, we count that as an error. */
378}
379
380int zb_drain(z_port port)
381{
382 struct termios tio;
383 int old_timeout;
384 char c;
385
386 /* set timeout to 0.1s */
387 SYSCALL(tcgetattr(port, &tio));
388 old_timeout = tio.c_cc[VTIME];
389 tio.c_cc[VTIME] = 1;
390 SYSCALL(tcsetattr(port, TCSANOW, &tio));
391
392 /* flush and read whatever else comes in */
393 SYSCALL(tcflush(port, TCIFLUSH));
394 while(read(port, &c, 1) > 0);
395
396 /* set timeout back to what it was */
397 tio.c_cc[VTIME] = old_timeout;
398 SYSCALL(tcsetattr(port, TCSANOW, &tio));
399
400 return Z_SUCCESS;
401}
402
403int zb_set_timeout(z_port port, int milliseconds)
404{
405 struct termios tio;
406 int new_time;
407
408 if (milliseconds % 100 != 0)
409 {
410 new_time = milliseconds / 100 + 1;
411 }
412 else
413 {
414 new_time = milliseconds / 100; /* VTIME is in increments of 0.1s */
415 }
416
417 SYSCALL(tcgetattr(port, &tio));
418 tio.c_cc[VTIME] = new_time;
419 SYSCALL(tcsetattr(port, TCSANOW, &tio));
420
421 return new_time * 100;
422}
423
424#endif
425
#define READ_TIMEOUT
Definition z_common.h:53
@ Z_ERROR_SYSTEM_ERROR
Definition z_common.h:67
@ Z_ERROR_NULL_PARAMETER
Definition z_common.h:71
@ Z_SUCCESS
Definition z_common.h:65
#define SYSCALL(F)
Definition za_serial.c:50
#define PRINT_ERROR(M)
Definition za_serial.c:35
#define PRINT_SYSCALL_ERROR(M)
Definition za_serial.c:40
static int zb_verbose
Definition zb_serial.c:18
int zb_decode(int32_t *destination, const uint8_t *reply)
Definition zb_serial.c:48
int zb_encode(uint8_t *destination, uint8_t device_number, uint8_t command_number, int32_t data)
Definition zb_serial.c:25
void zb_set_verbose(int value)
Definition zb_serial.c:20
Provides a set of functions for interacting with Zaber devices in the binary protocol.
int zb_disconnect(z_port port)
int zb_send(z_port port, const uint8_t *command)
int zb_receive(z_port port, uint8_t *destination)
int zb_drain(z_port port)
int zb_set_timeout(z_port port, int milliseconds)
int zb_connect(z_port *port, const char *port_name)