2 * Generic uart / rs232/ serial port library
4 * Copyright (c) 2013, Roel Verdult
7 * Redistribution and use in source and binary forms, with or without
8 * modification, are permitted provided that the following conditions are met:
9 * 1. Redistributions of source code must retain the above copyright
10 * notice, this list of conditions and the following disclaimer.
11 * 2. Redistributions in binary form must reproduce the above copyright
12 * notice, this list of conditions and the following disclaimer in the
13 * documentation and/or other materials provided with the distribution.
14 * 3. Neither the name of the copyright holders nor the
15 * names of its contributors may be used to endorse or promote products
16 * derived from this software without specific prior written permission.
18 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ''AS IS'' AND ANY
19 * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
20 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
21 * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS BE LIABLE FOR ANY
22 * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
23 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
24 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
25 * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
26 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
27 * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
36 // Test if we are dealing with unix operating systems
40 typedef struct termios term_info
;
42 int fd
; // Serial port file descriptor
43 term_info tiOld
; // Terminal info before using the port
44 term_info tiNew
; // Terminal info during the transaction
47 // Set time-out on 30 miliseconds
48 const struct timeval timeout
= {
49 .tv_sec
= 0, // 0 second
50 .tv_usec
= 30000 // 30000 micro seconds
53 serial_port
uart_open(const char* pcPortName
)
55 serial_port_unix
* sp
= malloc(sizeof(serial_port_unix
));
56 if (sp
== 0) return INVALID_SERIAL_PORT
;
58 sp
->fd
= open(pcPortName
, O_RDWR
| O_NOCTTY
| O_NDELAY
| O_NONBLOCK
);
61 return INVALID_SERIAL_PORT
;
64 // Finally figured out a way to claim a serial port interface under unix
65 // We just try to set a (advisory) lock on the file descriptor
68 fl
.l_whence
= SEEK_SET
;
73 // Does the system allows us to place a lock on this file descriptor
74 if (fcntl(sp
->fd
, F_SETLK
, &fl
) == -1) {
75 // A conflicting lock is held by another process
77 return CLAIMED_SERIAL_PORT
;
80 // Try to retrieve the old (current) terminal info struct
81 if(tcgetattr(sp
->fd
,&sp
->tiOld
) == -1) {
83 return INVALID_SERIAL_PORT
;
86 // Duplicate the (old) terminal info struct
87 sp
->tiNew
= sp
->tiOld
;
89 // Configure the serial port
90 sp
->tiNew
.c_cflag
= CS8
| CLOCAL
| CREAD
;
91 sp
->tiNew
.c_iflag
= IGNPAR
;
92 sp
->tiNew
.c_oflag
= 0;
93 sp
->tiNew
.c_lflag
= 0;
95 // Block until n bytes are received
96 sp
->tiNew
.c_cc
[VMIN
] = 0;
97 // Block until a timer expires (n * 100 mSec.)
98 sp
->tiNew
.c_cc
[VTIME
] = 0;
100 // Try to set the new terminal info struct
101 if(tcsetattr(sp
->fd
,TCSANOW
,&sp
->tiNew
) == -1) {
103 return INVALID_SERIAL_PORT
;
106 // Flush all lingering data that may exist
107 tcflush(sp
->fd
, TCIOFLUSH
);
109 // set speed, works for UBUNTU 14.04
110 bool err
= uart_set_speed(sp
, 460800);
112 uart_set_speed(sp
, 115200);
117 void uart_close(const serial_port sp
) {
118 serial_port_unix
* spu
= (serial_port_unix
*)sp
;
119 tcflush(spu
->fd
,TCIOFLUSH
);
120 tcsetattr(spu
->fd
,TCSANOW
,&(spu
->tiOld
));
123 fl
.l_whence
= SEEK_SET
;
128 // Does the system allows us to place a lock on this file descriptor
129 int err
= fcntl(spu
->fd
, F_SETLK
, &fl
);
137 bool uart_set_speed(serial_port sp
, const uint32_t uiPortSpeed
) {
138 const serial_port_unix
* spu
= (serial_port_unix
*)sp
;
140 switch (uiPortSpeed
) {
141 case 0: stPortSpeed
= B0
; break;
142 case 50: stPortSpeed
= B50
; break;
143 case 75: stPortSpeed
= B75
; break;
144 case 110: stPortSpeed
= B110
; break;
145 case 134: stPortSpeed
= B134
; break;
146 case 150: stPortSpeed
= B150
; break;
147 case 300: stPortSpeed
= B300
; break;
148 case 600: stPortSpeed
= B600
; break;
149 case 1200: stPortSpeed
= B1200
; break;
150 case 1800: stPortSpeed
= B1800
; break;
151 case 2400: stPortSpeed
= B2400
; break;
152 case 4800: stPortSpeed
= B4800
; break;
153 case 9600: stPortSpeed
= B9600
; break;
154 case 19200: stPortSpeed
= B19200
; break;
155 case 38400: stPortSpeed
= B38400
; break;
157 case 57600: stPortSpeed
= B57600
; break;
160 case 115200: stPortSpeed
= B115200
; break;
163 case 230400: stPortSpeed
= B230400
; break;
166 case 460800: stPortSpeed
= B460800
; break;
169 case 921600: stPortSpeed
= B921600
; break;
171 default: return false;
174 if (tcgetattr(spu
->fd
,&ti
) == -1) return false;
175 // Set port speed (Input and Output)
176 cfsetispeed(&ti
,stPortSpeed
);
177 cfsetospeed(&ti
,stPortSpeed
);
178 return (tcsetattr(spu
->fd
,TCSANOW
,&ti
) != -1);
181 uint32_t uart_get_speed(const serial_port sp
) {
183 uint32_t uiPortSpeed
;
184 const serial_port_unix
* spu
= (serial_port_unix
*)sp
;
185 if (tcgetattr(spu
->fd
,&ti
) == -1) return 0;
186 // Set port speed (Input)
187 speed_t stPortSpeed
= cfgetispeed(&ti
);
188 switch (stPortSpeed
) {
189 case B0
: uiPortSpeed
= 0; break;
190 case B50
: uiPortSpeed
= 50; break;
191 case B75
: uiPortSpeed
= 75; break;
192 case B110
: uiPortSpeed
= 110; break;
193 case B134
: uiPortSpeed
= 134; break;
194 case B150
: uiPortSpeed
= 150; break;
195 case B300
: uiPortSpeed
= 300; break;
196 case B600
: uiPortSpeed
= 600; break;
197 case B1200
: uiPortSpeed
= 1200; break;
198 case B1800
: uiPortSpeed
= 1800; break;
199 case B2400
: uiPortSpeed
= 2400; break;
200 case B4800
: uiPortSpeed
= 4800; break;
201 case B9600
: uiPortSpeed
= 9600; break;
202 case B19200
: uiPortSpeed
= 19200; break;
203 case B38400
: uiPortSpeed
= 38400; break;
205 case B57600
: uiPortSpeed
= 57600; break;
208 case B115200
: uiPortSpeed
= 115200; break;
211 case B230400
: uiPortSpeed
= 230400; break;
214 case B460800
: uiPortSpeed
= 460800; break;
217 case B921600
: uiPortSpeed
= 921600; break;
224 bool uart_set_parity(serial_port sp
, serial_port_parity spp
) {
226 const serial_port_unix
* spu
= (serial_port_unix
*)sp
;
227 if (tcgetattr(spu
->fd
,&ti
) == -1) return false;
229 case SP_INVALID
: return false;
230 case SP_NONE
: ti
.c_cflag
&= ~(PARENB
| PARODD
); break;
231 case SP_EVEN
: ti
.c_cflag
|= PARENB
; ti
.c_cflag
&= ~(PARODD
); break;
232 case SP_ODD
: ti
.c_cflag
|= PARENB
| PARODD
; break;
234 return (tcsetattr(spu
->fd
,TCSANOW
,&ti
) != -1);
237 serial_port_parity
uart_get_parity(const serial_port sp
) {
239 const serial_port_unix
* spu
= (serial_port_unix
*)sp
;
240 if (tcgetattr(spu
->fd
,&ti
) == -1) return SP_INVALID
;
242 if (ti
.c_cflag
& PARENB
) {
243 if (ti
.c_cflag
& PARODD
) {
253 bool uart_cts(const serial_port sp
) {
255 if (ioctl(((serial_port_unix
*)sp
)->fd
,TIOCMGET
,&status
) < 0) return false;
256 return (status
& TIOCM_CTS
);
259 bool uart_receive(const serial_port sp
, byte_t
* pbtRx
, size_t* pszRxLen
) {
266 // Reset the output count
270 // Reset file descriptor
272 FD_SET(((serial_port_unix
*)sp
)->fd
,&rfds
);
274 res
= select(((serial_port_unix
*)sp
)->fd
+1, &rfds
, NULL
, NULL
, &tv
);
283 if (*pszRxLen
== 0) {
284 // Error, we received no data
287 // We received some data, but nothing more is available
292 // Retrieve the count of the incoming bytes
293 res
= ioctl(((serial_port_unix
*)sp
)->fd
, FIONREAD
, &byteCount
);
294 if (res
< 0) return false;
296 // There is something available, read the data
297 res
= read(((serial_port_unix
*)sp
)->fd
,pbtRx
+(*pszRxLen
),byteCount
);
299 // Stop if the OS has some troubles reading the data
300 if (res
<= 0) return false;
312 bool uart_send(const serial_port sp
, const byte_t
* pbtTx
, const size_t szTxLen
) {
318 while (szPos
< szTxLen
) {
319 // Reset file descriptor
321 FD_SET(((serial_port_unix
*)sp
)->fd
,&rfds
);
323 res
= select(((serial_port_unix
*)sp
)->fd
+1, NULL
, &rfds
, NULL
, &tv
);
327 printf("write error\n");
333 printf("write time-out\n");
337 // Send away the bytes
338 res
= write(((serial_port_unix
*)sp
)->fd
,pbtTx
+szPos
,szTxLen
-szPos
);
340 // Stop if the OS has some troubles sending the data
342 printf("os troubles\n");
352 // The windows serial port implementation
355 HANDLE hPort
; // Serial port handle
356 DCB dcb
; // Device control settings
357 COMMTIMEOUTS ct
; // Serial port time-out configuration
358 } serial_port_windows
;
360 void upcase(char *p
) {
362 if(*p
>= 97 && *p
<= 122) {
369 serial_port
uart_open(const char* pcPortName
) {
370 char acPortName
[255];
371 serial_port_windows
* sp
= malloc(sizeof(serial_port_windows
));
373 // Copy the input "com?" to "\\.\COM?" format
374 sprintf(acPortName
,"\\\\.\\%s",pcPortName
);
377 // Try to open the serial port
378 sp
->hPort
= CreateFileA(acPortName
,GENERIC_READ
|GENERIC_WRITE
,0,NULL
,OPEN_EXISTING
,0,NULL
);
379 if (sp
->hPort
== INVALID_HANDLE_VALUE
) {
381 return INVALID_SERIAL_PORT
;
384 // Prepare the device control
385 memset(&sp
->dcb
, 0, sizeof(DCB
));
386 sp
->dcb
.DCBlength
= sizeof(DCB
);
387 if(!BuildCommDCBA("baud=115200 parity=N data=8 stop=1",&sp
->dcb
)) {
389 return INVALID_SERIAL_PORT
;
392 // Update the active serial port
393 if(!SetCommState(sp
->hPort
,&sp
->dcb
)) {
395 return INVALID_SERIAL_PORT
;
397 // all zero's configure: no timeout for read/write used.
398 sp
->ct
.ReadIntervalTimeout
= 0;//1;
399 sp
->ct
.ReadTotalTimeoutMultiplier
= 0;//1;
400 sp
->ct
.ReadTotalTimeoutConstant
= 30;
401 sp
->ct
.WriteTotalTimeoutMultiplier
= 0;//1;
402 sp
->ct
.WriteTotalTimeoutConstant
= 30;
404 if(!SetCommTimeouts(sp
->hPort
,&sp
->ct
)) {
406 return INVALID_SERIAL_PORT
;
409 PurgeComm(sp
->hPort
, PURGE_RXABORT
| PURGE_RXCLEAR
);
411 bool err
= uart_set_speed(sp
, 460800);
413 uart_set_speed(sp
, 115200);
418 void uart_close(const serial_port sp
) {
419 CloseHandle(((serial_port_windows
*)sp
)->hPort
);
423 bool uart_set_speed(serial_port sp
, const uint32_t uiPortSpeed
) {
424 serial_port_windows
* spw
;
425 spw
= (serial_port_windows
*)sp
;
426 spw
->dcb
.BaudRate
= uiPortSpeed
;
427 return SetCommState(spw
->hPort
, &spw
->dcb
);
430 uint32_t uart_get_speed(const serial_port sp
) {
431 const serial_port_windows
* spw
= (serial_port_windows
*)sp
;
432 if (!GetCommState(spw
->hPort
, (serial_port
)&spw
->dcb
)) {
433 return spw
->dcb
.BaudRate
;
438 bool uart_receive(const serial_port sp
, byte_t
* pbtRx
, size_t* pszRxLen
) {
439 ReadFile(((serial_port_windows
*)sp
)->hPort
,pbtRx
,*pszRxLen
,(LPDWORD
)pszRxLen
,NULL
);
440 return (*pszRxLen
!= 0);
443 bool uart_send(const serial_port sp
, const byte_t
* pbtTx
, const size_t szTxLen
) {
445 return WriteFile(((serial_port_windows
*)sp
)->hPort
, pbtTx
, szTxLen
, &dwTxLen
, NULL
);
446 return (dwTxLen
!= 0);