6b55e94038e816469b5aedc6ed74f84d84f3ffbe
[owTools.git] / src / owCOMInterface.cpp
1 // Copyright (c) 2016, Tobias Mueller tm(at)tm3d.de
2 // All rights reserved.
3 //
4 // Redistribution and use in source and binary forms, with or without
5 // modification, are permitted provided that the following conditions are
6 // met:
7 //
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
13 //    distribution.
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.
20 //
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.
32
33
34 #include <unistd.h>
35 #include <sys/types.h>
36 #include <sys/select.h>
37 #include <sys/stat.h>
38 #include <fcntl.h>
39 #include <sys/ioctl.h>
40 #include <time.h>
41 #include <termios.h>
42 #include <errno.h>
43 #include <sys/time.h>
44 #include "owCOMInterface.h"
45
46 // Mode Commands
47 #define MODE_DATA                      0xE1
48 #define MODE_COMMAND                   0xE3
49 #define MODE_STOP_PULSE                0xF1
50
51 // Return byte value
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
58
59 #define RB_BIT_MASK                    0x03
60 #define RB_BIT_ONE                     0x03
61 #define RB_BIT_ZERO                    0x00
62
63 // Masks for all bit ranges
64 #define CMD_MASK                       0x80
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
72
73 // Command or config bit
74 #define CMD_COMM                       0x81
75 #define CMD_CONFIG                     0x01
76
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
83
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
89
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
95
96 // Data/Command mode select bits
97 #define MODSEL_DATA                    0x00
98 #define MODSEL_COMMAND                 0x02
99
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
104
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
114
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
124
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
134
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
144
145 // Write 1 low time
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
154
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
164
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
174
175 // Baud rate bits
176 #define PARMSET_9600                   0x00
177 #define PARMSET_19200                  0x02
178 #define PARMSET_57600                  0x04
179 #define PARMSET_115200                 0x06
180
181 // DS2480B program voltage available
182 #define DS2480PROG_MASK                0x20
183
184 // mode bit flags
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
190
191 // Versions of DS2480
192 #define VER_LINK                       0x1C
193 #define VER_DS2480                     0x08
194 #define VER_DS2480B                    0x0C
195
196
197
198
199 //---------------------------------------------------------------------------
200 //  Description:
201 //     flush the rx and tx buffers
202 //
203 // 'portnum'  - number 0 to MAX_PORTNUM-1.  This number was provided to
204 //              OpenCOM to indicate the port number.
205 //
206 void owCOMInterface::FlushCOM() {
207    tcflush(fd, TCIOFLUSH);
208 }
209
210
211 int owCOMInterface::OpenCOM(uint8_t comnr) {
212         struct termios t;               // see man termios - declared as above
213         int rc;
214         //int fd;
215         char port_zstr[100];
216         if (com_init) return fd;
217
218         sprintf(port_zstr,"/dev/ttyUSB%i",comnr);
219
220    fd = open(port_zstr, O_RDWR|O_NONBLOCK);
221    if (fd<0)
222    {
223            log->set(OWLOG_ERROR,"ERROR open Com %s return %i",port_zstr,fd);
224       return fd;
225    }
226    rc = tcgetattr (fd, &t);
227    if (rc < 0)
228    {
229       int tmp;
230       tmp = errno;
231       close(fd);
232       errno = tmp;
233       log->set(OWLOG_ERROR,"OWERROR_SYSTEM_RESOURCE_INIT_FAILED %s",port_zstr);
234       return rc; // changed (2.00), used to return rc;
235    }
236
237    cfsetospeed(&t, B9600);
238    cfsetispeed (&t, B9600);
239
240    // Get terminal parameters. (2.00) removed raw
241    tcgetattr(fd,&t);
242    // Save original settings.
243    origterm = t;
244
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);
252    t.c_cc[VMIN] = 0;
253    t.c_cc[VTIME] = 3;
254
255    rc = tcsetattr(fd, TCSAFLUSH, &t);
256    tcflush(fd,TCIOFLUSH);
257
258    if (rc < 0)
259    {
260       int tmp;
261       tmp = errno;
262       close(fd);
263       errno = tmp;
264       log->set(OWLOG_ERROR,"OWERROR_SYSTEM_RESOURCE_INIT_FAILED %s",port_zstr);
265       return rc; // changed (2.00), used to return rc;
266    }
267         com_init=1;
268    return fd; // changed (2.00), used to return fd;
269 }
270
271
272 //---------------------------------------------------------------------------
273 // Closes the connection to the port.
274 //
275 // 'portnum'  - number 0 to MAX_PORTNUM-1.  This number was provided to
276 //              OpenCOM to indicate the port number.
277 //
278 void owCOMInterface::CloseCOM()
279 {
280    // restore tty settings
281    tcsetattr(fd, TCSAFLUSH, &origterm);
282    FlushCOM();
283    close(fd);
284    com_init=0;
285    
286 }
287
288
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.
292 //
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
297 //
298 int owCOMInterface::WriteCOM( int outlen, uint8_t *outbuf)
299 {
300    long count = outlen;
301    int i = write(fd, outbuf, outlen);
302
303    tcdrain(fd);
304    return (i == count);
305 }
306
307
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.
311 //
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
316 //
317 // Returns:  TRUE(1)  - success
318 //           FALSE(0) - failure
319 //
320 int owCOMInterface::ReadCOM( int inlen, uint8_t *inbuf)
321 {
322    fd_set         filedescr;
323    struct timeval tval;
324    int            cnt;
325
326    // loop to wait until each byte is available and read it
327    for (cnt = 0; cnt < inlen; cnt++)
328    {
329       // set a descriptor to wait for a character available
330       FD_ZERO(&filedescr);
331       FD_SET(fd,&filedescr);
332       // set timeout to 10ms
333       tval.tv_sec = 0;
334       tval.tv_usec = 20000;
335
336       // if byte available read or return bytes read
337       if (select(fd+1,&filedescr,NULL,NULL,&tval) != 0)
338       {
339          if (read(fd,&inbuf[cnt],1) != 1) {
340                         log->set(OWLOG_ERROR,"Read Error on Serial");
341
342             return cnt;
343          }
344       }
345       else {
346                 log->set(OWLOG_ERROR,"Read Error on Serial (select) (new time)");
347         return cnt;
348         }
349    }
350
351    // success, so return desired length
352    return inlen;
353 }
354
355
356
357
358 //--------------------------------------------------------------------------
359 //  Description:
360 //     Send a break on the com port for at least 2 ms
361 //
362 // 'portnum'  - number 0 to MAX_PORTNUM-1.  This number was provided to
363 //              OpenCOM to indicate the port number.
364 //
365 void owCOMInterface::BreakCOM()
366 {
367    int duration = 0;              // see man termios break may be
368    tcsendbreak(fd, duration);     // too long
369 }
370
371
372 //--------------------------------------------------------------------------
373 // Set the baud rate on the com port.
374 //
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
378 // PARMSET_9600     0x00
379 // PARMSET_19200    0x02
380 // PARMSET_57600    0x04
381 // PARMSET_115200   0x06
382 //
383 void owCOMInterface::SetBaudCOM( uint8_t new_baud)
384 {
385    struct termios t;
386    int rc;
387    speed_t baud;
388
389    // read the attribute structure
390    rc = tcgetattr(fd, &t);
391    if (rc < 0)
392    {
393       close(fd); 
394       log->set(OWLOG_ERROR,"Error on Serial (set Boudrate)");
395
396       return;
397    }
398
399    // convert parameter to linux baud rate
400    switch(new_baud)
401    {
402       case PARMSET_9600:
403          baud = B9600;
404          break;
405       case PARMSET_19200:
406          baud = B19200;
407          break;
408       case PARMSET_57600:
409          baud = B57600;
410          break;
411       case PARMSET_115200:
412          baud = B115200;
413          break;
414       default:
415          baud = B9600;
416          break;
417    }
418
419    // set baud in structure
420    cfsetospeed(&t, baud);
421    cfsetispeed(&t, baud);
422
423    // change baud on port
424    rc = tcsetattr(fd, TCSAFLUSH, &t);
425    if (rc < 0) {
426           log->set(OWLOG_ERROR,"Error on Serial (set Boudrate)");
427
428       close(fd);
429   }
430 }
431  
432
433 //---------------------------------------------------------------------------
434 // Attempt to resyc and detect a DS2480B
435 //
436 // 'portnum'    - number 0 to MAX_PORTNUM-1.  This number was provided to
437 //                OpenCOM to indicate the port number.
438 //
439 // Returns:  TRUE  - DS2480B detected successfully
440 //           FALSE - Could not detect DS2480B
441 //
442
443 int8_t owCOMInterface::DS2480Detect(){
444    uint8_t sendpacket[10],readbuffer[10];
445    uint8_t sendlen=0;
446
447    // reset modes
448    UMode = MODSEL_COMMAND;
449    UBaud = PARMSET_9600;
450    USpeed = SPEEDSEL_FLEX;
451
452    // set the baud rate to 9600
453    SetBaudCOM((uint8_t)UBaud);
454
455    // send a break to reset the DS2480
456    BreakCOM();
457
458    // delay to let line settle
459         usleep(2000);
460
461    // flush the buffers
462    FlushCOM();
463
464    // send the timing byte
465    sendpacket[0] = 0xC1;
466    if (WriteCOM(1,sendpacket) != 1)
467    {
468                 log->set(OWLOG_ERROR,"Error on Serial (Write in DS2480Detect)");
469       return FALSE;
470    }
471
472    // delay to let line settle
473    usleep(4000);
474
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;
482
483    // construct the command to read the baud rate (to test command block)
484    sendpacket[sendlen++] = CMD_CONFIG | PARMSEL_PARMREAD | (PARMSEL_BAUDRATE >> 3);
485
486    // also do 1 bit operation (to test 1-Wire block)
487    sendpacket[sendlen++] = CMD_COMM | FUNCTSEL_BIT | UBaud | BITPOL_ONE;
488
489    // flush the buffers
490    FlushCOM();
491
492    // send the packet
493    if (WriteCOM(sendlen,sendpacket))
494    {
495       // read back the response
496       if (ReadCOM(5,readbuffer) == 5)
497       {
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))
504             return TRUE;
505          else
506                         log->set(OWLOG_ERROR,"OWERROR_DS2480_BAD_RESPONSE\n");
507       }
508       else
509                 //      printf("OWERROR_READCOM_FAILED\n");
510          log->set(OWLOG_ERROR,"OWERROR_READCOM_FAILED");
511    }
512    else
513         //printf("OWERROR_READCOM_FAILED\n");
514       log->set(OWLOG_ERROR,"OWERROR_WRITECOM_FAILED");
515
516    return FALSE;
517 }
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536 int owCOMInterface::InitAdapter(uint8_t nr) {
537         // attempt to open the communications port
538         if ((fd = OpenCOM(nr)) < 0)
539         {
540                 log->set(OWLOG_ERROR,"OWERROR_OPENCOM_FAILED");
541                 return -1;
542         }
543
544         // detect DS2480
545         if (!DS2480Detect())
546         {
547                 CloseCOM();
548                 log->set(OWLOG_ERROR,"OWERROR_DS2480_NOT_DETECTED");
549                 return -1;
550         }
551         com_init=1;
552         return 1;       
553 }
554
555 void owCOMInterface::ReleaseAdapter() {
556         CloseCOM();
557 }
558
559
560 int owCOMInterface::Reset() {
561         uint8_t readbuffer[10],sendpacket[10];
562         uint8_t sendlen=0;
563
564         // check if correct mode
565         if (UMode != MODSEL_COMMAND) {
566                 UMode = MODSEL_COMMAND;
567                 sendpacket[sendlen++] = MODE_COMMAND;
568         }
569
570         // construct the command
571         sendpacket[sendlen++] = (uint8_t)(CMD_COMM | FUNCTSEL_RESET | USpeed);
572         // flush the buffers
573         FlushCOM();
574         // send the packet
575         if (WriteCOM(sendlen,sendpacket))
576         {
577                 // read back the 1 byte response
578                 if (ReadCOM(1,readbuffer) == 1)
579                 {
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))
583                         {
584             // check if programming voltage available
585             UVersion = (readbuffer[0] & VERSION_MASK);
586             return 1;
587          }
588          else
589            log->set(OWLOG_ERROR,"OWERROR_RESET_FAILED");
590
591       }
592      else
593        log->set(OWLOG_ERROR,"OWERROR_READCOM_FAILED");
594    }
595    else
596      log->set(OWLOG_ERROR,"OWERROR_WRITECOM_FAILED");
597
598    // an error occured so re-sync with DS2480
599    DS2480Detect();
600
601    return FALSE;        
602         
603 }
604
605 uint8_t owCOMInterface::sendrecivBit(uint8_t bit) { 
606         uint8_t readbuffer[10],sendpacket[10];
607         uint8_t sendlen=0;
608
609         // check if correct mode
610         if (UMode != MODSEL_COMMAND) {
611                 UMode = MODSEL_COMMAND;
612                 sendpacket[sendlen++] = MODE_COMMAND;
613         }
614
615         // construct the command
616         sendpacket[sendlen] = (bit != 0) ? BITPOL_ONE : BITPOL_ZERO;
617         sendpacket[sendlen++] |= CMD_COMM | FUNCTSEL_BIT | USpeed;
618         // flush the buffers
619         FlushCOM();
620
621         // send the packet
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))
628                                 return 1;
629                         else
630                                 return 0;
631                 }
632       else
633          log->set(OWLOG_ERROR,"OWERROR_READCOM_FAILED");
634         } else
635       log->set(OWLOG_ERROR,"OWERROR_WRITECOM_FAILED");
636
637    // an error occured so re-sync with DS2480
638         DS2480Detect();
639         return 0;
640 }
641
642
643 uint8_t owCOMInterface::sendrecivByte(uint8_t byte) {
644    uint8_t readbuffer[10],sendpacket[10];
645    uint8_t sendlen=0;
646         
647         
648    // check if correct mode
649    if (UMode != MODSEL_DATA)
650    {
651       UMode = MODSEL_DATA;
652       sendpacket[sendlen++] = MODE_DATA;
653    }
654
655    // add the byte to send
656    sendpacket[sendlen++] = byte;
657
658    // check for duplication of data that looks like COMMAND mode
659    if (byte ==(uint8_t)MODE_COMMAND)
660       sendpacket[sendlen++] = (uint8_t)byte;
661
662    // flush the buffers
663    FlushCOM();
664
665    // send the packet
666    if (WriteCOM(sendlen,sendpacket))
667    {
668       // read back the 1 byte response
669       if (ReadCOM(1,readbuffer) == 1)
670       {
671
672           // return the response
673           return readbuffer[0];
674       }
675       else
676          log->set(OWLOG_ERROR,"OWERROR_READCOM_FAILED");
677    }
678    else
679       log->set(OWLOG_ERROR,"OWERROR_WRITECOM_FAILED");
680
681    // an error occured so re-sync with DS2480
682    DS2480Detect();
683    return 0;
684 }
685
686