libftdi Archives

Subject: RFC: Add One-Wire primitives via MPSSE

From: Uwe Bonnes <bon@xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx>
To: libftdi@xxxxxxxxxxxxxxxxxxxxxxx
Date: Fri, 25 Jun 2010 15:09:22 +0200
Hello,

appended patch adds the One-Wire primitives via MPSSE and provides a simple
test program to read out Hardware ID and temperature of 1 or 2 connected
DS18B20 one-wire devices. It uses the ASYNC api and is against current
libftdi-1.0 git.

Please comment if this is usefull and in an acceptable form.

-- 
Uwe Bonnes                bon@xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx

Institut fuer Kernphysik  Schlossgartenstrasse 9  64289 Darmstadt
--------- Tel. 06151 162516 -------- Fax. 06151 164321 ----------

>From dd3a969eba5d5688e521bfe5a9816c6ebc455b2b Mon Sep 17 00:00:00 2001
From: Uwe Bonnes <bon@xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx>
Date: Fri, 25 Jun 2010 15:00:28 +0200
Subject: Implement the One-Wire primitives with the MPSSE machine, add test 
program to read out DS18B20

---
 examples/CMakeLists.txt |    2 +
 examples/Makefile.am    |    1 +
 examples/ow_test.c      |  125 +++++++++++++++++++
 src/CMakeLists.txt      |    2 +-
 src/Makefile.am         |    2 +-
 src/ftdi.h              |   24 ++++
 src/ftdi_ow.c           |  305 +++++++++++++++++++++++++++++++++++++++++++++++
 7 files changed, 459 insertions(+), 2 deletions(-)
 create mode 100644 examples/ow_test.c
 create mode 100644 src/ftdi_ow.c

diff --git a/examples/CMakeLists.txt b/examples/CMakeLists.txt
index af7f57a..6f27665 100644
--- a/examples/CMakeLists.txt
+++ b/examples/CMakeLists.txt
@@ -21,6 +21,7 @@ if (EXAMPLES)
     add_executable(serial_read serial_read.c)
     add_executable(baud_test baud_test.c)
     add_executable(stream_test stream_test.c)
+    add_executable(ow_test ow_test.c)
 
     # Linkage
     target_link_libraries(simple ftdi)
@@ -32,6 +33,7 @@ if (EXAMPLES)
     target_link_libraries(serial_read ftdi)
     target_link_libraries(baud_test ftdi)
     target_link_libraries(stream_test ftdi)
+    target_link_libraries(ow_test ftdi)
 
     # libftdi++ examples
     if(FTDI_BUILD_CPP)
diff --git a/examples/Makefile.am b/examples/Makefile.am
index 820599a..45d9265 100644
--- a/examples/Makefile.am
+++ b/examples/Makefile.am
@@ -18,6 +18,7 @@ bin_PROGRAMS = simple \
        find_all \
        serial_read \
        baud_test \
+       ow_test \
        $(examples_libftdipp)
 
 # Don't install the example files
diff --git a/examples/ow_test.c b/examples/ow_test.c
new file mode 100644
index 0000000..e0e35e0
--- /dev/null
+++ b/examples/ow_test.c
@@ -0,0 +1,125 @@
+#include <stdio.h>
+#include <stdlib.h>
+#include <signal.h>
+
+#include <ftdi.h>
+
+#define do_and_check(x,y,z) if (x <0) { fprintf(stderr,y); return z;}
+#define do_and_checkr(a,x,y,z) if (x <0) {                      \
+        fprintf(stderr,y,ftdi_get_error_string(a)); return z;}
+
+/* On the used test board, SEL_N is connected to ADBUS4, 
+   and there is the possibility to connect up to two DS18B20 
+   One-Wire devices*/
+
+#define MOSI      0x02
+#define OWI_EN_N  0x10
+#define TEMP_GENERAL_ERROR  -1
+
+static int exitRequested = 0;
+/*
+ * sigintHandler --
+ *
+ *    SIGINT handler, so we can gracefully exit when the user hits ctrl-C.
+ */
+
+static void
+sigintHandler(int signum)
+{
+    exitRequested = 1;
+    fprintf(stderr,"\n");
+}
+
+
+double get_temp(struct ftdi_context *ftdi, unsigned char *id)
+{
+    int16_t temp = 0;
+    if ((OWCommand(ftdi, OW_READ, id) ==  0) &&
+        (OWReadBlock(ftdi, (unsigned char*)&temp,2) == 2))
+    {
+        if (temp == TEMP_GENERAL_ERROR)
+        {
+            fprintf(stderr, "Device 0x%08Lx disconneced\n", 
+                    *(unsigned long long*)id);
+            id[0] = 0;
+        }
+    }
+    return (double)temp/16.0;
+}
+
+void try_reconnect (struct ftdi_context *ftdi, unsigned char *id)
+{
+    int16_t temp;
+    if (*(unsigned long long*)id)
+    {
+        /* Try if reconnected */
+        id[0] = 0x28;
+        if ((OWCommand(ftdi, OW_READ, id)==  0) &&
+            (OWReadBlock(ftdi, (unsigned char*)&temp,2) == 2))
+        {
+            if (temp == TEMP_GENERAL_ERROR)
+                id[0] = 0;
+            else
+                fprintf(stderr, "Device 0x%08Lx reconnected\n", 
+                        *(unsigned long long*)id);
+            
+        }
+    }
+}
+int main(int argc, char **argv)
+{
+    struct ftdi_context ftdi_a;
+    unsigned char buf[3];
+    int diff;
+    unsigned char hid_nco0[8] = {0};
+    unsigned char hid_nco1[8] = {0};
+
+    do_and_check(ftdi_init(&ftdi_a), "ftdi_init A failed\n",EXIT_FAILURE);
+    do_and_check(ftdi_set_interface(&ftdi_a, INTERFACE_A),
+                 "ftdi_set_interface A failed\n", EXIT_FAILURE);
+    do_and_checkr(&ftdi_a, 
+                  ftdi_usb_open_desc(&ftdi_a, 0x0403, 0x6010, NULL, NULL),
+                  "Can't open A on ftdi device: %s\n", EXIT_FAILURE);
+    do_and_checkr(&ftdi_a,ftdi_set_latency_timer(&ftdi_a, 2),
+                  "Can't set latency A, Error %s\n",EXIT_FAILURE);
+    do_and_checkr(&ftdi_a, 
+                  ftdi_set_bitmode(&ftdi_a, 
+                                   MOSI | OWI_EN_N,
+                                   BITMODE_MPSSE),
+                  "Can't set Bitmode for Interface A: %s\n", EXIT_FAILURE); 
+    do_and_checkr(&ftdi_a,ftdi_usb_purge_buffers(&ftdi_a),
+                  "Cant purge buffer A: %s\n",EXIT_FAILURE); 
+
+    buf[0]= SET_BITS_LOW;
+    buf[1]= MOSI;
+    buf[2]= MOSI|OWI_EN_N;
+    ftdi_write_data(&ftdi_a, buf, 3);
+  
+    diff = OWRomSearch(&ftdi_a, OW_SEARCH_FIRST,  hid_nco0);
+    if(diff != OW_LAST_DEVICE)
+        diff = OWRomSearch(&ftdi_a, diff,  hid_nco1);
+    fprintf(stderr, "OWI0: Got  ID 0x%08Lx\n", *(unsigned long long*) 
hid_nco0);
+    fprintf(stderr, "OWI1: Got  ID 0x%08Lx\n", *(unsigned long long*) 
hid_nco1);
+
+    signal(SIGINT, sigintHandler);
+    while (!exitRequested && (OWCommand( &ftdi_a, OW_CONVERT_T, NULL) != 
EXIT_FAILURE))
+    {
+        usleep(750000);
+        if(hid_nco0[0])
+            fprintf(stderr, "OWI0: %8.4f ", get_temp(&ftdi_a, hid_nco0));
+        else
+            try_reconnect(&ftdi_a, hid_nco0);
+        if(hid_nco1[0])
+            fprintf(stderr, "OWI1: %8.4f", get_temp(&ftdi_a, hid_nco1));
+        else
+            try_reconnect(&ftdi_a, hid_nco1);
+        if(hid_nco0[0] || hid_nco1[0])
+            fprintf(stderr, "\n");
+    }
+    signal(SIGINT, SIG_DFL);
+    do_and_checkr(&ftdi_a, 
+                  ftdi_set_bitmode(&ftdi_a, 0, BITMODE_RESET),
+                  "Can't reset Bitmode for Interface A: %s\n", EXIT_FAILURE); 
+   ftdi_deinit(&ftdi_a);
+     return 0;
+}
diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt
index a4da870..b7ba79d 100644
--- a/src/CMakeLists.txt
+++ b/src/CMakeLists.txt
@@ -4,7 +4,7 @@ include_directories( ${CMAKE_CURRENT_BINARY_DIR}
                      )
 
 # Targets
-set(c_sources     ftdi.c ftdi_stream.c)
+set(c_sources     ftdi.c ftdi_stream.c ftdi_ow.c)
 set(c_headers     ftdi.h)
 
 add_library(ftdi SHARED ${c_sources})
diff --git a/src/Makefile.am b/src/Makefile.am
index 9141fb9..b666382 100644
--- a/src/Makefile.am
+++ b/src/Makefile.am
@@ -1,6 +1,6 @@
 # the library search path.
 lib_LTLIBRARIES =  libftdi.la
-libftdi_la_SOURCES =  ftdi.c ftdi_stream.c
+libftdi_la_SOURCES =  ftdi.c ftdi_stream.c ftdi_ow.c
 include_HEADERS =  ftdi.h
 
 # Note:  If you specify a:b:c as the version in the next line,
diff --git a/src/ftdi.h b/src/ftdi.h
index 324d07b..4510c9d 100644
--- a/src/ftdi.h
+++ b/src/ftdi.h
@@ -157,6 +157,23 @@ enum ftdi_interface
     #define DEPRECATED(func) func
 #endif
 
+/* One Wire related definitions */
+#define OW_RESET_RATE     16666
+/* Minimal low time is 6 us, so use a clock of 1/6us*/
+#define OW_TRANSFER_RATE 166666 
+#define OW_LAST_DEVICE   0x00
+#define OW_DATA_ERR      0xFE
+#define OW_PRESENCE_ERR  0xFF
+#define OW_SEARCH_FIRST  0xFF
+
+#define OW_READ          0xBE
+#define OW_MATCH_ROM     0x55
+#define OW_SKIP_ROM      0xCC
+#define OW_CONVERT_T     0x44            // DS1820 commands
+#define OW_READ_ROM      0x33
+#define OW_SEARCH_ROM    0xF0
+
+
 struct ftdi_transfer_control
 {
     int completed;
@@ -403,6 +420,13 @@ extern "C"
 
     char *ftdi_get_error_string(struct ftdi_context *ftdi);
 
+    unsigned char OWTouchReset(struct ftdi_context *ftdi);
+    unsigned char OWReadBit(struct ftdi_context *ftdi);
+    int OWCommand(struct ftdi_context *ftdi, unsigned char command, unsigned 
char *id );
+    void OWWriteByte(struct ftdi_context *ftdi, unsigned char data);
+    int OWReadBlock(struct ftdi_context *ftdi, unsigned char *data, int len);
+    unsigned char OWRomSearch(struct ftdi_context *ftdi, unsigned char diff, 
unsigned char *id);
+
 #ifdef __cplusplus
 }
 #endif
diff --git a/src/ftdi_ow.c b/src/ftdi_ow.c
new file mode 100644
index 0000000..b566454
--- /dev/null
+++ b/src/ftdi_ow.c
@@ -0,0 +1,305 @@
+/***************************************************************************
+                          ftdi_ow.c  -  description
+                             -------------------
+    begin                : June 24 2010
+    copyright            : (C) 2010 Uwe Bonnes, IKDA, TU Darmstadt
+    email                : bon@xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
+ ***************************************************************************/
+ 
+/***************************************************************************
+ *                                                                         *
+ *   This program is free software; you can redistribute it and/or modify  *
+ *   it under the terms of the GNU Lesser General Public License           *
+ *   version 2.1 as published by the Free Software Foundation;             *
+ *                                                                         *
+ ***************************************************************************/
+
+/*  Basic Connection
+ *                 VCC
+ DO       ------    |
+               |    R
+             |\o    |
+ GND/SEL_N---| --------- DI/Devices
+             |/
+
+ Use e.g. 74XX125 as driver
+
+ With SEL_N driven by a FT Pin, DO/MOSI can be shared with other devices
+ using  the MPSSE serial engine. Drive SEL_N low to activate the OWI Devices.
+ Sharing DI/MISO needs more considerations, like another  74XX125 )
+
+ For One Wire Timing basics, see Maxim AN126
+ http://pdfserv.maxim-ic.com/en/an/AN126.pdf
+
+ The code is derived form code using an UART as described in
+ http://www.maxim-ic.com/app-notes/index.mvp/id/214
+ and Peter Danneger's code at
+ http://www.mikrocontroller.net/attachment/4550/1wire.zip
+*/
+#include <stdlib.h>
+
+#include "ftdi.h"
+
+/**
+   Send the reset Pulse
+
+   \param ftdi pointer to ftdi_context
+
+   \retval  0: all fine
+   \retval OW_PRESENCE_ERR: No reaction from device
+*/
+unsigned char OWTouchReset(struct ftdi_context *ftdi)
+{
+    unsigned char ibuf[2];
+    unsigned char buf[9]= {TCK_DIVISOR, 
+                            DIV_VALUE(OW_RESET_RATE) & 0xff,
+                           (DIV_VALUE(OW_RESET_RATE) >> 8) & 0xff,
+                            MPSSE_DO_READ|MPSSE_DO_WRITE |MPSSE_LSB,
+                            1,0,
+                            0,0xff, SEND_IMMEDIATE};
+                               
+    struct ftdi_transfer_control *tc = ftdi_read_data_submit(ftdi, ibuf, 2);
+
+    ftdi_write_data(ftdi, buf, 8);
+    if(ftdi_transfer_data_done (tc) < 0)
+    {
+        ftdi->error_str = "Unexpected error";
+        return OW_PRESENCE_ERR;
+    }
+    if (ibuf[1] != 0xff)
+        return 0;
+    else
+        return OW_PRESENCE_ERR;
+}
+
+static void OWWriteBit(struct ftdi_context *ftdi, unsigned char bit)
+{
+    unsigned char buf[8]= {TCK_DIVISOR, 
+                           DIV_VALUE(OW_TRANSFER_RATE) & 0xff,
+                           (DIV_VALUE(OW_TRANSFER_RATE) >> 8) & 0xff,
+                           MPSSE_DO_WRITE |MPSSE_LSB,1,0,0xfe,0xff};
+    if (!bit)
+    {
+        buf[6] = 0;
+        buf[7] = 0xf8;
+    }
+    ftdi_write_data(ftdi, buf, 8);
+}
+
+/**
+   Read one bit
+
+   \param ftdi pointer to ftdi_context
+   
+   \retval    0: A '0' was read
+   \retval OW_DATA_ERR: Unexpected error
+   \retval 0xff: A '1' was read
+*/
+
+unsigned char OWReadBit(struct ftdi_context *ftdi)
+{
+    unsigned char ibuf[2];
+    unsigned char buf[9]= {TCK_DIVISOR, 
+                           DIV_VALUE(OW_TRANSFER_RATE) & 0xff,
+                           (DIV_VALUE(OW_TRANSFER_RATE) >> 8) & 0xff,
+                           MPSSE_DO_READ|MPSSE_DO_WRITE |MPSSE_LSB,
+                           1,0, 0xfe, 0xff, SEND_IMMEDIATE};
+    struct ftdi_transfer_control *tc = ftdi_read_data_submit(ftdi, ibuf, 2);
+    ftdi_write_data(ftdi, buf, 9);
+    if(ftdi_transfer_data_done (tc) < 0)
+    {
+        ftdi->error_str = "Unexpected error";
+        return OW_DATA_ERR;
+    }
+    if (ibuf[0] == 0xfe)
+        return 0xff;
+    else
+        return 0;    
+}
+
+/**
+   Read a block of data
+
+   \param ftdi pointer to ftdi_context
+   \param buf Buffer to store data in
+   \param size Size of buffer (and data to read)
+   
+   \retval    -2: Couldn't alloc buffer
+   \retval    -1: Unexpected error
+   \retval    >0: Number of bytes read
+*/
+int OWReadBlock(struct ftdi_context *ftdi, unsigned char *buf, int size)
+{
+    unsigned char *ibuf;
+    int i, j, ret =0;
+    unsigned char result = 0;
+    struct ftdi_transfer_control *tc;
+
+    ibuf = malloc(7+4*8*size);
+    if (!ibuf)
+        return -2;
+    tc = ftdi_read_data_submit(ftdi, ibuf+7+2*8*size, 2*8*size);
+    ibuf[0] = TCK_DIVISOR;
+    ibuf[1] = DIV_VALUE(OW_TRANSFER_RATE) & 0xff;
+    ibuf[2] = (DIV_VALUE(OW_TRANSFER_RATE) >> 8) & 0xff;
+    ibuf[3] = MPSSE_DO_READ|MPSSE_DO_WRITE |MPSSE_LSB;
+    ibuf[4] = (2*8*size -1) & 0xff;
+    ibuf[5] = ((2*8*size -1)>>8) & 0xff;
+
+    for (i=0; i<size*8; i++)
+    {
+        ibuf[2*i+6] = 0xfe;
+        ibuf[2*i+7] = 0xff;
+    }
+    ibuf[6+ 2*8*size] = SEND_IMMEDIATE;
+    ftdi_write_data(ftdi, ibuf, 7+ 2*8*size );
+    if(ftdi_transfer_data_done (tc) < 0)
+    {
+        ftdi->error_str = "Unexpected error";
+        ret = -1;
+    }
+    else
+    {
+        for(j=0, i=0; i<size*8; i++)
+        {
+            result >>= 1;
+            if(ibuf[7+2*8*size +i*2] == 0xfe)
+                result |=0x80;
+            if (i%8 == 7)
+            {
+                buf[j] = result;
+                result = 0;
+                j++;
+            }
+        }
+        ret = size;
+    }
+    free(ibuf);
+    return ret;
+}
+
+/**
+   Write one byte of data
+
+   \param ftdi pointer to ftdi_context
+   \param data Data Byte to write
+   
+   \retval    -2: Couldn't alloc buffer
+   \retval    -1: Unexpected error
+   \retval    >0: Number of bytes read
+*/
+void OWWriteByte(struct ftdi_context *ftdi, unsigned char data)
+{
+    unsigned char obuf[22]= {TCK_DIVISOR, 
+                           DIV_VALUE(OW_TRANSFER_RATE) & 0xff,
+                           (DIV_VALUE(OW_TRANSFER_RATE) >> 8) & 0xff,
+                           MPSSE_DO_WRITE |MPSSE_LSB,15,0};
+    int i;
+    
+    for (i=0; i<8; i++)
+    {
+        if(data & 0x01)
+        {
+            obuf[i*2+6] = 0xfe;
+            obuf[i*2+7] = 0xff;
+        }
+        else
+        {
+            obuf[i*2+6] = 0x00;
+            obuf[i*2+7] = 0xf8;
+        }
+        data >>= 1;
+    }
+    ftdi_write_data(ftdi, obuf, 22);
+}   
+
+/**
+   Search for ROM IDs
+
+   \param ftdi pointer to ftdi_context
+   \param diff Result for last call to this function, start with 
OW_SEARCH_FIRST
+   \param id Buffer to hold found ID
+
+   \retval OW_PRESENCE_ERR : No device found
+   \retval OW_DATA_ERR: Unknown error
+   \retval OW_LAST_DEVICE : Last device found
+   \retval else: Difference to use for next search
+   
+*/
+unsigned char OWRomSearch(struct ftdi_context *ftdi, 
+                          unsigned char diff, unsigned char *id)
+{
+    unsigned char i,j, next_diff;
+    unsigned char b;
+
+    if (OWTouchReset(ftdi))
+    {
+        ftdi->error_str = "Unexpected error";
+        return OW_PRESENCE_ERR;
+    }
+    OWWriteByte(ftdi, OW_SEARCH_ROM);
+  
+    next_diff = OW_LAST_DEVICE;                      // unchanged on last 
device
+    i = 8 * 8;                                    // 8 bytes
+    do{
+        j = 8;                                      // 8 bits
+        do{
+            b = OWReadBit(ftdi);                       // read bit
+            if( OWReadBit(ftdi) ){                     // read complement bit
+                if( b )                                 // 11
+                    return OW_DATA_ERR;                      // data error
+            }else{
+                if( !b ){                               // 00 = 2 devices
+                    if( diff > i ||
+                        ((*id & 1) && diff != i) ){
+                        b = 1;                              // now 1
+                        next_diff = i;                      // next pass 0
+                    }
+                }
+            }
+            OWWriteBit(ftdi, b );                           // write bit
+            *id >>= 1;
+            if( b )                                   // store bit
+                *id |= 0x80;
+            i--;
+        }while( --j );
+        id++;                                       // next byte
+    }while( i );
+    return next_diff;                             // to continue search
+}
+
+/**
+   Send a One-Wire command to all or a specific device
+
+   \param ftdi pointer to ftdi_context
+   \param command Commandbyte to send
+   \param id Id of specific device or NULL for all devices
+
+   \retval OW_PRESENCE_ERR : No device found
+   \retval               0 : All fine
+   
+*/
+int OWCommand(struct ftdi_context *ftdi,
+              unsigned char command, unsigned char *id )
+{
+    unsigned char i;
+    if (OWTouchReset(ftdi)) 
+    {
+        if (id)
+        {
+            ftdi->error_str = "Device disconnected";
+            id[0] = 0;
+        }
+        return OW_PRESENCE_ERR;
+    }
+    if( id ){
+        OWWriteByte(ftdi, OW_MATCH_ROM );           // to a single device
+        for (i=0; i<8; i++)
+            OWWriteByte(ftdi,  id[i] );
+    }
+    else {
+        OWWriteByte(ftdi, OW_SKIP_ROM );            // to all devices
+    }
+    OWWriteByte(ftdi, command );
+    return 0;
+}
-- 
1.6.4.2


--
libftdi - see http://www.intra2net.com/en/developer/libftdi for details.
To unsubscribe send a mail to libftdi+unsubscribe@xxxxxxxxxxxxxxxxxxxxxxx   

Current Thread
  • RFC: Add One-Wire primitives via MPSSE, Uwe Bonnes <=