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 [email protected] 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 <[email protected]> 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 : [email protected] + ***************************************************************************/ + +/*************************************************************************** + * * + * 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 [email protected]
