diff options
Diffstat (limited to 'libk8055/libk8055.c')
-rw-r--r-- | libk8055/libk8055.c | 600 |
1 files changed, 600 insertions, 0 deletions
diff --git a/libk8055/libk8055.c b/libk8055/libk8055.c new file mode 100644 index 0000000..9556294 --- /dev/null +++ b/libk8055/libk8055.c @@ -0,0 +1,600 @@ +/* $Id: libk8055.c,v 1.7 2008/08/20 17:00:55 mr_brain Exp $ + + This file is part of the libk8055 Library. + + The libk8055 Library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + The libk8055 Library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the + Free Software Foundation, Inc., + 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + + http://opensource.org/licenses/ + + Copyleft (C) 2005 by Sven Lindberg + k8055@k8055.mine.nu + + Copyright (C) 2007 by Pjetur G. Hjaltason + pjetur@pjetur.net + Commenting, general rearrangement of code, bugfixes, + python interface with swig and simple k8055 python class + + + Input packet format + + +---+---+---+---+---+---+---+---+ + |DIn|Sta|A1 |A2 | C1 | C2 | + +---+---+---+---+---+---+---+---+ + DIn = Digital input in high nibble, except for input 3 in 0x01 + Sta = Status, Board number + 1 + A1 = Analog input 1, 0-255 + A2 = Analog input 2, 0-255 + C1 = Counter 1, 16 bits (lsb) + C2 = Counter 2, 16 bits (lsb) + + Output packet format + + +---+---+---+---+---+---+---+---+ + |CMD|DIG|An1|An2|Rs1|Rs2|Dbv|Dbv| + +---+---+---+---+---+---+---+---+ + CMD = Command + DIG = Digital output bitmask + An1 = Analog output 1 value, 0-255 + An2 = Analog output 2 value, 0-255 + Rs1 = Reset counter 1, command 3 + Rs2 = Reset counter 3, command 4 + Dbv = Debounce value for counter 1 and 2, command 1 and 2 + + Or split by commands + + Cmd 0, Reset ?? + Cmd 1, Set debounce Counter 1 + +---+---+---+---+---+---+---+---+ + |CMD| | | | | |Dbv| | + +---+---+---+---+---+---+---+---+ + Cmd 2, Set debounce Counter 2 + +---+---+---+---+---+---+---+---+ + |CMD| | | | | | |Dbv| + +---+---+---+---+---+---+---+---+ + Cmd 3, Reset counter 1 + +---+---+---+---+---+---+---+---+ + | 3 | | | | 00| | | | + +---+---+---+---+---+---+---+---+ + Cmd 4, Reset counter 2 + +---+---+---+---+---+---+---+---+ + | 4 | | | | | 00| | | + +---+---+---+---+---+---+---+---+ + cmd 5, Set analog/digital + +---+---+---+---+---+---+---+---+ + | 5 |DIG|An1|An2| | | | | + +---+---+---+---+---+---+---+---+ + +**/ + +#include <string.h> +#include <stdio.h> +#include <usb.h> +#include <assert.h> +#include <math.h> +#include "k8055.h" + +#define STR_BUFF 256 +#define PACKET_LEN 8 + +#define K8055_IPID 0x5500 +#define VELLEMAN_VENDOR_ID 0x10cf +#define K8055_MAX_DEV 4 + +#define USB_OUT_EP 0x01 /* USB output endpoint */ +#define USB_INP_EP 0x81 /* USB Input endpoint */ + +#define USB_TIMEOUT 20 +#define K8055_ERROR -1 + +#define DIGITAL_INP_OFFSET 0 +#define DIGITAL_OUT_OFFSET 1 +#define ANALOG_1_OFFSET 2 +#define ANALOG_2_OFFSET 3 +#define COUNTER_1_OFFSET 4 +#define COUNTER_2_OFFSET 6 + +#define CMD_RESET 0x00 +#define CMD_SET_DEBOUNCE_1 0x01 +#define CMD_SET_DEBOUNCE_2 0x01 +#define CMD_RESET_COUNTER_1 0x03 +#define CMD_RESET_COUNTER_2 0x04 +#define CMD_SET_ANALOG_DIGITAL 0x05 + +/* set debug to 0 to not print excess info */ +int DEBUG = 0; + +/* variables for usb */ +static struct usb_bus *bus, *busses; +static struct usb_device *dev; + +/* globals for datatransfer */ +struct k8055_dev { + unsigned char data_in[PACKET_LEN+1]; + unsigned char data_out[PACKET_LEN+1]; + struct usb_dev_handle *device_handle; + int DevNo; +}; + +static struct k8055_dev k8055d[K8055_MAX_DEV]; +static struct k8055_dev *CurrDev; + +/* Keep these globals for now */ +unsigned char *data_in, *data_out; + +/* char* device_id[]; */ + +/* Initialize the usb library - only once */ +static void init_usb(void) +{ + static int Done = 0; /* Only need to do this once */ + if (!Done) + { + usb_init(); + usb_find_busses(); + usb_find_devices(); + busses = usb_get_busses(); + Done = 1; + } +} +/* Actual read of data from the device endpoint, retry 3 times if not responding ok */ +static int ReadK8055Data(void) +{ + int read_status = 0, i = 0; + + if (CurrDev->DevNo == 0) return K8055_ERROR; + + for(i=0; i < 3; i++) + { + read_status = usb_interrupt_read(CurrDev->device_handle, USB_INP_EP, (char *)CurrDev->data_in, PACKET_LEN, USB_TIMEOUT); + if ((read_status == PACKET_LEN) && (CurrDev->data_in[1] == CurrDev->DevNo )) return 0; + if (DEBUG) + fprintf(stderr, "Read retry\n"); + } + return K8055_ERROR; +} + +/* Actual write of data to the device endpont, retry 3 times if not reponding correctly */ +static int WriteK8055Data(unsigned char cmd) +{ + int write_status = 0, i = 0; + + if (CurrDev->DevNo == 0) return K8055_ERROR; + + CurrDev->data_out[0] = cmd; + for(i=0; i < 3; i++) + { + write_status = usb_interrupt_write(CurrDev->device_handle, USB_OUT_EP, (char *)CurrDev->data_out, PACKET_LEN, USB_TIMEOUT); + if (write_status == PACKET_LEN) return 0; + if (DEBUG) + fprintf(stderr, "Write retry\n"); + } + return K8055_ERROR; +} + +/* If device is owned by some kernel driver, try to disconnect it and claim the device*/ +static int takeover_device(usb_dev_handle * udev, int interface) +{ + char driver_name[STR_BUFF]; + + memset(driver_name, 0, STR_BUFF); + int ret = K8055_ERROR; + + assert(udev != NULL); + ret = usb_get_driver_np(udev, interface, driver_name, sizeof(driver_name)); + if (ret == 0) + { + if (DEBUG) + fprintf(stderr, "Got driver name: %s\n", driver_name); + if (0 > usb_detach_kernel_driver_np(udev, interface)) + { + if (DEBUG) + fprintf(stderr, "Disconnect OS driver: %s\n", usb_strerror()); + } + else if (DEBUG) + fprintf(stderr, "Disconnected OS driver: %s\n", usb_strerror()); + } + else if (DEBUG) + fprintf(stderr, "Get driver name: - %s\n", usb_strerror()); + + /* claim interface */ + if (usb_claim_interface(udev, interface) < 0) + { + if (DEBUG) + fprintf(stderr, "Claim interface error: %s\n", usb_strerror()); + return K8055_ERROR; + } + else + usb_set_altinterface(udev, interface); + usb_set_configuration(udev, 1); + + if (DEBUG) + { + fprintf(stderr, "Found interface %d\n", interface); + fprintf(stderr, "Took over the device\n"); + } + + return 0; +} + +/* Open device - scan through usb busses looking for the right device, + claim it and then open the device +*/ +int OpenDevice(long BoardAddress) +{ + + int ipid; + + /* init USB and find all of the devices on all busses */ + init_usb(); + + /* ID of the welleman board is 5500h + address config */ + if (BoardAddress >= 0 && BoardAddress < K8055_MAX_DEV) + ipid = K8055_IPID + (int)BoardAddress; + else + return K8055_ERROR; /* throw error instead of being nice */ + + /* start looping through the devices to find the correct one */ + for (bus = busses; bus; bus = bus->next) + { + for (dev = bus->devices; dev; dev = dev->next) + { + if ((dev->descriptor.idVendor == VELLEMAN_VENDOR_ID) && + (dev->descriptor.idProduct == ipid)) + { + CurrDev = &k8055d[BoardAddress]; + CurrDev->device_handle = usb_open(dev); + if (DEBUG) + fprintf(stderr, + "Velleman Device Found @ Address %s Vendor 0x0%x Product ID 0x0%x\n", + dev->filename, dev->descriptor.idVendor, + dev->descriptor.idProduct); + if (takeover_device(CurrDev->device_handle, 0) < 0) + { + if (DEBUG) + fprintf(stderr, + "Can not take over the device from the OS driver\n"); + usb_close(CurrDev->device_handle); /* close usb if we fail */ + return K8055_ERROR; /* throw K8055_ERROR to show that OpenDevice failed */ + } + else + { + CurrDev->DevNo = BoardAddress + 1; /* Mark as open and valid */ + SetCurrentDevice(BoardAddress); + memset(CurrDev->data_out,0,PACKET_LEN); /* Write cmd 0, read data */ + WriteK8055Data(CMD_RESET); + if (ReadK8055Data() == 0) + return BoardAddress; /* This function should return board address */ + else + return K8055_ERROR; + } + } + } + } + if (DEBUG) + fprintf(stderr, "Could not find Velleman k8055 with address %d\n", + (int)BoardAddress); + return K8055_ERROR; +} + +/* Close the Current device */ +int CloseDevice() +{ + int rc; + + if (CurrDev->DevNo == 0) + { + if (DEBUG) + fprintf(stderr, "Current device is not open\n" ); + return 0; + } + rc = usb_close(CurrDev->device_handle); + if (rc >= 0) + { + CurrDev->DevNo = 0; /* Not active nay more */ + CurrDev->device_handle = NULL; + } + return rc; +} + +/* New function in version 2 of Velleman DLL, should return deviceno if OK */ +long SetCurrentDevice(long deviceno) +{ + if (deviceno >= 0 && deviceno < K8055_MAX_DEV) + { + if (k8055d[deviceno].DevNo != 0) + { + CurrDev = &k8055d[deviceno]; + data_in = CurrDev->data_in; + data_out = CurrDev->data_out; + return deviceno; + } + } + return K8055_ERROR; + +} + +/* New function in version 2 of Velleman DLL, should return devices-found bitmask or 0*/ +long SearchDevices(void) +{ + int retval = 0; + init_usb(); + /* start looping through the devices to find the correct one */ + for (bus = busses; bus; bus = bus->next) + { + for (dev = bus->devices; dev; dev = dev->next) + { + if (dev->descriptor.idVendor == VELLEMAN_VENDOR_ID) { + if(dev->descriptor.idProduct == K8055_IPID + 0) retval |= 0x01; + if(dev->descriptor.idProduct == K8055_IPID + 1) retval |= 0x02; + if(dev->descriptor.idProduct == K8055_IPID + 2) retval |= 0x04; + if(dev->descriptor.idProduct == K8055_IPID + 3) retval |= 0x08; + /* else some other kind of Velleman board */ + } + } + } + return retval; +} + +long ReadAnalogChannel(long Channel) +{ + if (Channel == 1 || Channel == 2) + { + if ( ReadK8055Data() == 0) + { + if (Channel == 2) + return CurrDev->data_in[ANALOG_2_OFFSET]; + else + return CurrDev->data_in[ANALOG_1_OFFSET]; + } + else + return K8055_ERROR; + } + else + return K8055_ERROR; +} + +int ReadAllAnalog(long *data1, long *data2) +{ + if (ReadK8055Data() == 0) + { + *data1 = CurrDev->data_in[ANALOG_1_OFFSET]; + *data2 = CurrDev->data_in[ANALOG_2_OFFSET]; + return 0; + } + else + return K8055_ERROR; +} + +int OutputAnalogChannel(long Channel, long data) +{ + if (Channel == 1 || Channel == 2) + { + if (Channel == 2) + CurrDev->data_out[ANALOG_2_OFFSET] = (unsigned char)data; + else + CurrDev->data_out[ANALOG_1_OFFSET] = (unsigned char)data; + + return WriteK8055Data(CMD_SET_ANALOG_DIGITAL); + } + else + return K8055_ERROR; +} + +int OutputAllAnalog(long data1, long data2) +{ + CurrDev->data_out[2] = (unsigned char)data1; + CurrDev->data_out[3] = (unsigned char)data2; + + return WriteK8055Data(CMD_SET_ANALOG_DIGITAL); +} + +int ClearAllAnalog() +{ + return OutputAllAnalog(0, 0); +} + +int ClearAnalogChannel(long Channel) +{ + if (Channel == 1 || Channel == 2) + { + if (Channel == 2) + return OutputAnalogChannel(2, 0); + else + return OutputAnalogChannel(1, 0); + } + else + return K8055_ERROR; +} + +int SetAnalogChannel(long Channel) +{ + if (Channel == 1 || Channel == 2) + { + if (Channel == 2) + return OutputAnalogChannel(2, 0xff); + else + return OutputAnalogChannel(1, 0xff); + } + else + return K8055_ERROR; + +} + +int SetAllAnalog() +{ + return OutputAllAnalog(0xff, 0xff); +} + +int WriteAllDigital(long data) +{ + CurrDev->data_out[1] = (unsigned char)data; + return WriteK8055Data(CMD_SET_ANALOG_DIGITAL); +} + +int ClearDigitalChannel(long Channel) +{ + unsigned char data; + + if (Channel > 0 && Channel < 9) + { + data = CurrDev->data_out[1] & ~(1 << (Channel-1)); + return WriteAllDigital(data); + } + else + return K8055_ERROR; +} + +int ClearAllDigital() +{ + return WriteAllDigital(0x00); +} + +int SetDigitalChannel(long Channel) +{ + unsigned char data; + + if (Channel > 0 && Channel < 9) + { + data = CurrDev->data_out[1] | (1 << (Channel-1)); + return WriteAllDigital(data); + } + else + return K8055_ERROR; +} + +int SetAllDigital() +{ + return WriteAllDigital(0xff); +} + +int ReadDigitalChannel(long Channel) +{ + int rval; + if (Channel > 0 && Channel < 9) + { + if ((rval = ReadAllDigital()) == K8055_ERROR) return K8055_ERROR; + return ((rval & (1 << (Channel-1))) > 0); + } + else + return K8055_ERROR; +} + +long ReadAllDigital() +{ + int return_data = 0; + + if (ReadK8055Data() == 0) + { + return_data = ( + ((CurrDev->data_in[0] >> 4) & 0x03) | /* Input 1 and 2 */ + ((CurrDev->data_in[0] << 2) & 0x04) | /* Input 3 */ + ((CurrDev->data_in[0] >> 3) & 0x18) ); /* Input 4 and 5 */ + return return_data; + } + else + return K8055_ERROR; +} + +int ReadAllValues(long int *data1, long int * data2, long int * data3, long int * data4, long int * data5) +{ + if (ReadK8055Data() == 0) + { + *data1 = ( + ((CurrDev->data_in[0] >> 4) & 0x03) | /* Input 1 and 2 */ + ((CurrDev->data_in[0] << 2) & 0x04) | /* Input 3 */ + ((CurrDev->data_in[0] >> 3) & 0x18) ); /* Input 4 and 5 */ + *data2 = CurrDev->data_in[ANALOG_1_OFFSET]; + *data3 = CurrDev->data_in[ANALOG_2_OFFSET]; + *data4 = *((short int *)(&CurrDev->data_in[COUNTER_1_OFFSET])); + *data5 = *((short int *)(&CurrDev->data_in[COUNTER_2_OFFSET])); + return 0; + } + else + return K8055_ERROR; +} + +int SetAllValues(int DigitalData, int AdData1, int AdData2) +{ + CurrDev->data_out[1] = (unsigned char)DigitalData; + CurrDev->data_out[2] = (unsigned char)AdData1; + CurrDev->data_out[3] = (unsigned char)AdData2; + + return WriteK8055Data(CMD_SET_ANALOG_DIGITAL); +} + +int ResetCounter(long CounterNo) +{ + if (CounterNo == 1 || CounterNo == 2) + { + CurrDev->data_out[0] = 0x02 + (unsigned char)CounterNo; /* counter selection */ + CurrDev->data_out[3 + CounterNo] = 0x00; + return WriteK8055Data(CurrDev->data_out[0]); + } + else + return K8055_ERROR; +} + +long ReadCounter(long CounterNo) +{ + if (CounterNo == 1 || CounterNo == 2) + { + if (ReadK8055Data() == 0) + { + if (CounterNo == 2) + return *((short int *)(&CurrDev->data_in[COUNTER_2_OFFSET])); + else + return *((short int *)(&CurrDev->data_in[COUNTER_1_OFFSET])); + } + else + return K8055_ERROR; + } + else + return K8055_ERROR; +} + +int SetCounterDebounceTime(long CounterNo, long DebounceTime) +{ + float value; + + if (CounterNo == 1 || CounterNo == 2) + { + CurrDev->data_out[0] = (unsigned char)CounterNo; + /* the velleman k8055 use a exponetial formula to split up the + DebounceTime 0-7450 over value 1-255. I've tested every value and + found that the formula dbt=0,338*value^1,8017 is closest to + vellemans dll. By testing and measuring times on the other hand I + found the formula dbt=0,115*x^2 quite near the actual values, a + little below at really low values and a little above at really + high values. But the time set with this formula is within +-4% */ + if (DebounceTime > 7450) + DebounceTime = 7450; + value = sqrtf(DebounceTime / 0.115); + if (value > ((int)value + 0.49999999)) /* simple round() function) */ + value += 1; + CurrDev->data_out[5 + CounterNo] = (unsigned char)value; + if (DEBUG) + fprintf(stderr, "Debouncetime%d value for k8055:%d\n", + (int)CounterNo, CurrDev->data_out[5 + CounterNo]); + return WriteK8055Data(CurrDev->data_out[0]); + } + else + return K8055_ERROR; +} + +char * Version(void) +{ + return(VERSION); +} |