1 // Copyright (c) 2016, Tobias Mueller tm(at)tm3d.de
2 // All rights reserved.
4 // Redistribution and use in source and binary forms, with or without
5 // modification, are permitted provided that the following conditions are
8 // * Redistributions of source code must retain the above copyright
9 // notice, this list of conditions and the following disclaimer.
10 // * Redistributions in binary form must reproduce the above copyright
11 // notice, this list of conditions and the following disclaimer in the
12 // documentation and/or other materials provided with the
14 // * All advertising materials mentioning features or use of this
15 // software must display the following acknowledgement: This product
16 // includes software developed by tm3d.de and its contributors.
17 // * Neither the name of tm3d.de nor the names of its contributors may
18 // be used to endorse or promote products derived from this software
19 // without specific prior written permission.
21 // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
22 // "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
23 // LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
24 // A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
25 // OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
26 // SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
27 // LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
28 // DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
29 // THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
30 // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
31 // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
35 #include <sys/types.h>
36 #include <sys/select.h>
39 #include <sys/ioctl.h>
44 #include "owCOMInterface.h"
47 #define MODE_DATA 0xE1
48 #define MODE_COMMAND 0xE3
49 #define MODE_STOP_PULSE 0xF1
52 #define RB_CHIPID_MASK 0x1C
53 #define RB_RESET_MASK 0x03
54 #define RB_1WIRESHORT 0x00
55 #define RB_PRESENCE 0x01
56 #define RB_ALARMPRESENCE 0x02
57 #define RB_NOPRESENCE 0x03
59 #define RB_BIT_MASK 0x03
60 #define RB_BIT_ONE 0x03
61 #define RB_BIT_ZERO 0x00
63 // Masks for all bit ranges
65 #define FUNCTSEL_MASK 0x60
66 #define BITPOL_MASK 0x10
67 #define SPEEDSEL_MASK 0x0C
68 #define MODSEL_MASK 0x02
69 #define PARMSEL_MASK 0x70
70 #define PARMSET_MASK 0x0E
71 #define VERSION_MASK 0x1C
73 // Command or config bit
75 #define CMD_CONFIG 0x01
77 // Function select bits
78 #define FUNCTSEL_BIT 0x00
79 #define FUNCTSEL_SEARCHON 0x30
80 #define FUNCTSEL_SEARCHOFF 0x20
81 #define FUNCTSEL_RESET 0x40
82 #define FUNCTSEL_CHMOD 0x60
84 // Bit polarity/Pulse voltage bits
85 #define BITPOL_ONE 0x10
86 #define BITPOL_ZERO 0x00
87 #define BITPOL_5V 0x00
88 #define BITPOL_12V 0x10
90 // One Wire speed bits
91 #define SPEEDSEL_STD 0x00
92 #define SPEEDSEL_FLEX 0x04
93 #define SPEEDSEL_OD 0x08
94 #define SPEEDSEL_PULSE 0x0C
96 // Data/Command mode select bits
97 #define MODSEL_DATA 0x00
98 #define MODSEL_COMMAND 0x02
100 // 5V Follow Pulse select bits (If 5V pulse
101 // will be following the next byte or bit.)
102 #define PRIME5V_TRUE 0x02
103 #define PRIME5V_FALSE 0x00
105 // Parameter select bits
106 #define PARMSEL_PARMREAD 0x00
107 #define PARMSEL_SLEW 0x10
108 #define PARMSEL_12VPULSE 0x20
109 #define PARMSEL_5VPULSE 0x30
110 #define PARMSEL_WRITE1LOW 0x40
111 #define PARMSEL_SAMPLEOFFSET 0x50
112 #define PARMSEL_ACTIVEPULLUPTIME 0x60
113 #define PARMSEL_BAUDRATE 0x70
115 // Pull down slew rate.
116 #define PARMSET_Slew15Vus 0x00
117 #define PARMSET_Slew2p2Vus 0x02
118 #define PARMSET_Slew1p65Vus 0x04
119 #define PARMSET_Slew1p37Vus 0x06
120 #define PARMSET_Slew1p1Vus 0x08
121 #define PARMSET_Slew0p83Vus 0x0A
122 #define PARMSET_Slew0p7Vus 0x0C
123 #define PARMSET_Slew0p55Vus 0x0E
125 // 12V programming pulse time table
126 #define PARMSET_32us 0x00
127 #define PARMSET_64us 0x02
128 #define PARMSET_128us 0x04
129 #define PARMSET_256us 0x06
130 #define PARMSET_512us 0x08
131 #define PARMSET_1024us 0x0A
132 #define PARMSET_2048us 0x0C
133 #define PARMSET_infinite 0x0E
135 // 5V strong pull up pulse time table
136 #define PARMSET_16p4ms 0x00
137 #define PARMSET_65p5ms 0x02
138 #define PARMSET_131ms 0x04
139 #define PARMSET_262ms 0x06
140 #define PARMSET_524ms 0x08
141 #define PARMSET_1p05s 0x0A
142 #define PARMSET_2p10s 0x0C
143 #define PARMSET_infinite 0x0E
146 #define PARMSET_Write8us 0x00
147 #define PARMSET_Write9us 0x02
148 #define PARMSET_Write10us 0x04
149 #define PARMSET_Write11us 0x06
150 #define PARMSET_Write12us 0x08
151 #define PARMSET_Write13us 0x0A
152 #define PARMSET_Write14us 0x0C
153 #define PARMSET_Write15us 0x0E
155 // Data sample offset and Write 0 recovery time
156 #define PARMSET_SampOff3us 0x00
157 #define PARMSET_SampOff4us 0x02
158 #define PARMSET_SampOff5us 0x04
159 #define PARMSET_SampOff6us 0x06
160 #define PARMSET_SampOff7us 0x08
161 #define PARMSET_SampOff8us 0x0A
162 #define PARMSET_SampOff9us 0x0C
163 #define PARMSET_SampOff10us 0x0E
165 // Active pull up on time
166 #define PARMSET_PullUp0p0us 0x00
167 #define PARMSET_PullUp0p5us 0x02
168 #define PARMSET_PullUp1p0us 0x04
169 #define PARMSET_PullUp1p5us 0x06
170 #define PARMSET_PullUp2p0us 0x08
171 #define PARMSET_PullUp2p5us 0x0A
172 #define PARMSET_PullUp3p0us 0x0C
173 #define PARMSET_PullUp3p5us 0x0E
176 #define PARMSET_9600 0x00
177 #define PARMSET_19200 0x02
178 #define PARMSET_57600 0x04
179 #define PARMSET_115200 0x06
181 // DS2480B program voltage available
182 #define DS2480PROG_MASK 0x20
185 #define MODE_NORMAL 0x00
186 #define MODE_OVERDRIVE 0x01
187 #define MODE_STRONG5 0x02
188 #define MODE_PROGRAM 0x04
189 #define MODE_BREAK 0x08
191 // Versions of DS2480
192 #define VER_LINK 0x1C
193 #define VER_DS2480 0x08
194 #define VER_DS2480B 0x0C
199 //---------------------------------------------------------------------------
201 // flush the rx and tx buffers
203 // 'portnum' - number 0 to MAX_PORTNUM-1. This number was provided to
204 // OpenCOM to indicate the port number.
206 void owCOMInterface::FlushCOM() {
207 tcflush(fd, TCIOFLUSH);
211 int owCOMInterface::OpenCOM(uint8_t comnr) {
212 struct termios t; // see man termios - declared as above
216 if (com_init) return fd;
218 sprintf(port_zstr,"/dev/ttyUSB%i",comnr);
220 fd = open(port_zstr, O_RDWR|O_NONBLOCK);
223 log->set(OWLOG_ERROR,"ERROR open Com %s return %i",port_zstr,fd);
226 rc = tcgetattr (fd, &t);
233 log->set(OWLOG_ERROR,"OWERROR_SYSTEM_RESOURCE_INIT_FAILED %s",port_zstr);
234 return rc; // changed (2.00), used to return rc;
237 cfsetospeed(&t, B9600);
238 cfsetispeed (&t, B9600);
240 // Get terminal parameters. (2.00) removed raw
242 // Save original settings.
245 // Set to non-canonical mode, and no RTS/CTS handshaking
246 t.c_iflag &= ~(BRKINT|ICRNL|IGNCR|INLCR|INPCK|ISTRIP|IXON|IXOFF|PARMRK);
247 t.c_iflag |= IGNBRK|IGNPAR;
248 t.c_oflag &= ~(OPOST);
249 t.c_cflag &= ~(CRTSCTS|CSIZE|HUPCL|PARENB);
250 t.c_cflag |= (CLOCAL|CS8|CREAD);
251 t.c_lflag &= ~(ECHO|ECHOE|ECHOK|ECHONL|ICANON|IEXTEN|ISIG);
255 rc = tcsetattr(fd, TCSAFLUSH, &t);
256 tcflush(fd,TCIOFLUSH);
264 log->set(OWLOG_ERROR,"OWERROR_SYSTEM_RESOURCE_INIT_FAILED %s",port_zstr);
265 return rc; // changed (2.00), used to return rc;
268 return fd; // changed (2.00), used to return fd;
272 //---------------------------------------------------------------------------
273 // Closes the connection to the port.
275 // 'portnum' - number 0 to MAX_PORTNUM-1. This number was provided to
276 // OpenCOM to indicate the port number.
278 void owCOMInterface::CloseCOM()
280 // restore tty settings
281 tcsetattr(fd, TCSAFLUSH, &origterm);
289 //--------------------------------------------------------------------------
290 // Write an array of bytes to the COM port, verify that it was
291 // sent out. Assume that baud rate has been set.
293 // 'portnum' - number 0 to MAX_PORTNUM-1. This number provided will
294 // be used to indicate the port number desired when calling
295 // all other functions in this library.
296 // Returns 1 for success and 0 for failure
298 int owCOMInterface::WriteCOM( int outlen, uint8_t *outbuf)
301 int i = write(fd, outbuf, outlen);
308 //--------------------------------------------------------------------------
309 // Read an array of bytes to the COM port, verify that it was
310 // sent out. Assume that baud rate has been set.
312 // 'portnum' - number 0 to MAX_PORTNUM-1. This number was provided to
313 // OpenCOM to indicate the port number.
314 // 'outlen' - number of bytes to write to COM port
315 // 'outbuf' - pointer ot an array of bytes to write
317 // Returns: TRUE(1) - success
318 // FALSE(0) - failure
320 int owCOMInterface::ReadCOM( int inlen, uint8_t *inbuf)
326 // loop to wait until each byte is available and read it
327 for (cnt = 0; cnt < inlen; cnt++)
329 // set a descriptor to wait for a character available
331 FD_SET(fd,&filedescr);
332 // set timeout to 10ms
334 tval.tv_usec = 20000; //TOOO CHECK!!!!!!!!!!!!!!!!!!
336 // if byte available read or return bytes read
337 if (select(fd+1,&filedescr,NULL,NULL,&tval) != 0)
339 if (read(fd,&inbuf[cnt],1) != 1) {
340 log->set(OWLOG_ERROR,"Read Error on Serial");
346 log->set(OWLOG_ERROR,"Read Error on Serial (select) (new time)");
351 // success, so return desired length
358 //--------------------------------------------------------------------------
360 // Send a break on the com port for at least 2 ms
362 // 'portnum' - number 0 to MAX_PORTNUM-1. This number was provided to
363 // OpenCOM to indicate the port number.
365 void owCOMInterface::BreakCOM()
367 int duration = 0; // see man termios break may be
368 tcsendbreak(fd, duration); // too long
372 //--------------------------------------------------------------------------
373 // Set the baud rate on the com port.
375 // 'portnum' - number 0 to MAX_PORTNUM-1. This number was provided to
376 // OpenCOM to indicate the port number.
377 // 'new_baud' - new baud rate defined as
379 // PARMSET_19200 0x02
380 // PARMSET_57600 0x04
381 // PARMSET_115200 0x06
383 void owCOMInterface::SetBaudCOM( uint8_t new_baud)
389 // read the attribute structure
390 rc = tcgetattr(fd, &t);
394 log->set(OWLOG_ERROR,"Error on Serial (set Boudrate)");
399 // convert parameter to linux baud rate
419 // set baud in structure
420 cfsetospeed(&t, baud);
421 cfsetispeed(&t, baud);
423 // change baud on port
424 rc = tcsetattr(fd, TCSAFLUSH, &t);
426 log->set(OWLOG_ERROR,"Error on Serial (set Boudrate)");
433 //---------------------------------------------------------------------------
434 // Attempt to resyc and detect a DS2480B
436 // 'portnum' - number 0 to MAX_PORTNUM-1. This number was provided to
437 // OpenCOM to indicate the port number.
439 // Returns: TRUE - DS2480B detected successfully
440 // FALSE - Could not detect DS2480B
443 int8_t owCOMInterface::DS2480Detect(){
444 uint8_t sendpacket[10],readbuffer[10];
448 UMode = MODSEL_COMMAND;
449 UBaud = PARMSET_9600;
450 USpeed = SPEEDSEL_FLEX;
452 // set the baud rate to 9600
453 SetBaudCOM((uint8_t)UBaud);
455 // send a break to reset the DS2480
458 // delay to let line settle
464 // send the timing byte
465 sendpacket[0] = 0xC1;
466 if (WriteCOM(1,sendpacket) != 1)
468 log->set(OWLOG_ERROR,"Error on Serial (Write in DS2480Detect)");
472 // delay to let line settle
475 // set the FLEX configuration parameters
476 // default PDSRC = 1.37Vus
477 sendpacket[sendlen++] = CMD_CONFIG | PARMSEL_SLEW | PARMSET_Slew1p37Vus;
478 // default W1LT = 10us
479 sendpacket[sendlen++] = CMD_CONFIG | PARMSEL_WRITE1LOW | PARMSET_Write10us;
480 // default DSO/WORT = 8us
481 sendpacket[sendlen++] = CMD_CONFIG | PARMSEL_SAMPLEOFFSET | PARMSET_SampOff8us;
483 // construct the command to read the baud rate (to test command block)
484 sendpacket[sendlen++] = CMD_CONFIG | PARMSEL_PARMREAD | (PARMSEL_BAUDRATE >> 3);
486 // also do 1 bit operation (to test 1-Wire block)
487 sendpacket[sendlen++] = CMD_COMM | FUNCTSEL_BIT | UBaud | BITPOL_ONE;
493 if (WriteCOM(sendlen,sendpacket))
495 // read back the response
496 if (ReadCOM(5,readbuffer) == 5)
498 // look at the baud rate and bit operation
499 // to see if the response makes sense
500 if (((readbuffer[3] & 0xF1) == 0x00) &&
501 ((readbuffer[3] & 0x0E) == UBaud) &&
502 ((readbuffer[4] & 0xF0) == 0x90) &&
503 ((readbuffer[4] & 0x0C) == UBaud))
506 log->set(OWLOG_ERROR,"OWERROR_DS2480_BAD_RESPONSE\n");
509 // printf("OWERROR_READCOM_FAILED\n");
510 log->set(OWLOG_ERROR,"OWERROR_READCOM_FAILED");
513 //printf("OWERROR_READCOM_FAILED\n");
514 log->set(OWLOG_ERROR,"OWERROR_WRITECOM_FAILED");
536 int owCOMInterface::InitAdapter(uint8_t nr) {
537 // attempt to open the communications port
538 if ((fd = OpenCOM(nr)) < 0)
540 log->set(OWLOG_ERROR,"OWERROR_OPENCOM_FAILED");
548 log->set(OWLOG_ERROR,"OWERROR_DS2480_NOT_DETECTED");
555 void owCOMInterface::ReleaseAdapter() {
560 int owCOMInterface::Reset() {
561 uint8_t readbuffer[10],sendpacket[10];
564 // check if correct mode
565 if (UMode != MODSEL_COMMAND) {
566 UMode = MODSEL_COMMAND;
567 sendpacket[sendlen++] = MODE_COMMAND;
570 // construct the command
571 sendpacket[sendlen++] = (uint8_t)(CMD_COMM | FUNCTSEL_RESET | USpeed);
575 if (WriteCOM(sendlen,sendpacket))
577 // read back the 1 byte response
578 if (ReadCOM(1,readbuffer) == 1)
580 // make sure this byte looks like a reset byte
581 if (((readbuffer[0] & RB_RESET_MASK) == RB_PRESENCE) ||
582 ((readbuffer[0] & RB_RESET_MASK) == RB_ALARMPRESENCE))
584 // check if programming voltage available
585 UVersion = (readbuffer[0] & VERSION_MASK);
589 log->set(OWLOG_ERROR,"OWERROR_RESET_FAILED");
593 log->set(OWLOG_ERROR,"OWERROR_READCOM_FAILED");
596 log->set(OWLOG_ERROR,"OWERROR_WRITECOM_FAILED");
598 // an error occured so re-sync with DS2480
605 uint8_t owCOMInterface::sendrecivBit(uint8_t bit) {
606 uint8_t readbuffer[10],sendpacket[10];
609 // check if correct mode
610 if (UMode != MODSEL_COMMAND) {
611 UMode = MODSEL_COMMAND;
612 sendpacket[sendlen++] = MODE_COMMAND;
615 // construct the command
616 sendpacket[sendlen] = (bit != 0) ? BITPOL_ONE : BITPOL_ZERO;
617 sendpacket[sendlen++] |= CMD_COMM | FUNCTSEL_BIT | USpeed;
622 if (WriteCOM(sendlen,sendpacket)) {
623 // read back the response
624 if (ReadCOM(1,readbuffer) == 1) {
625 // interpret the response
626 if (((readbuffer[0] & 0xE0) == 0x80) &&
627 ((readbuffer[0] & RB_BIT_MASK) == RB_BIT_ONE))
633 log->set(OWLOG_ERROR,"OWERROR_READCOM_FAILED");
635 log->set(OWLOG_ERROR,"OWERROR_WRITECOM_FAILED");
637 // an error occured so re-sync with DS2480
643 uint8_t owCOMInterface::sendrecivByte(uint8_t byte) {
644 uint8_t readbuffer[10],sendpacket[10];
648 // check if correct mode
649 if (UMode != MODSEL_DATA)
652 sendpacket[sendlen++] = MODE_DATA;
655 // add the byte to send
656 sendpacket[sendlen++] = byte;
658 // check for duplication of data that looks like COMMAND mode
659 if (byte ==(uint8_t)MODE_COMMAND)
660 sendpacket[sendlen++] = (uint8_t)byte;
666 if (WriteCOM(sendlen,sendpacket))
668 // read back the 1 byte response
669 if (ReadCOM(1,readbuffer) == 1)
672 // return the response
673 return readbuffer[0];
676 log->set(OWLOG_ERROR,"OWERROR_READCOM_FAILED");
679 log->set(OWLOG_ERROR,"OWERROR_WRITECOM_FAILED");
681 // an error occured so re-sync with DS2480