diff --git a/CMakeLists.txt b/CMakeLists.txt index f4b8796b5cb460e854ff7ca4fbde2df9f270d523..137ab1e41c917a97e0265f5fdccb20427237ed2a 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -1,29 +1,33 @@ cmake_minimum_required(VERSION 3.0) -project(uvosled) +project(eismultiplexer) -set(SRC_FILES uvosled.c usbshm.c) -set(LIBS -pthread -lusb-1.0 ) +find_package(PkgConfig REQUIRED) +pkg_check_modules(LIBUSB REQUIRED libusb-1.0) +if(CMAKE_INSTALL_PREFIX_INITIALIZED_TO_DEFAULT) + set(CMAKE_INSTALL_PREFIX "/usr" CACHE PATH "..." FORCE) +endif(CMAKE_INSTALL_PREFIX_INITIALIZED_TO_DEFAULT) + +set(SRC_FILES eismultiplexer.c usbshm.c) add_library(${PROJECT_NAME} SHARED ${SRC_FILES}) -target_link_libraries( ${PROJECT_NAME} ${LIBS}) +target_link_libraries(${PROJECT_NAME} ${LIBUSB_LIBRARIES} -pthread) +target_include_directories(${PROJECT_NAME} PUBLIC ${LIBUSB_INCLUDE_DIRS}) add_definitions("-std=c17 -Wall -O2 -fno-strict-aliasing") - -set(CMAKE_INSTALL_PREFIX "/usr") install(TARGETS ${PROJECT_NAME} DESTINATION lib) -install(FILES ./uvosled.h DESTINATION include) +install(FILES ./eismultiplexer.h DESTINATION include) link_directories(${CMAKE_CURRENT_BINARY_DIR}) -set(SRC_FILES_TEST_APP main.c) -set(LIBS_TEST -l${PROJECT_NAME}) -add_executable(${PROJECT_NAME}_test ${SRC_FILES_TEST_APP}) +add_executable(${PROJECT_NAME}_test main.c) add_dependencies(${PROJECT_NAME}_test ${PROJECT_NAME}) -target_link_libraries( ${PROJECT_NAME}_test ${LIBS_TEST}) +target_link_libraries( ${PROJECT_NAME}_test -l${PROJECT_NAME}) add_definitions("-std=c17 -Wall -O2 -fno-strict-aliasing") - install(TARGETS ${PROJECT_NAME}_test DESTINATION bin) +configure_file(pkgconfig/eismuliplexer.pc.in pkgconfig/eismuliplexer.pc @ONLY) +install(FILES ${CMAKE_CURRENT_BINARY_DIR}/pkgconfig/eismuliplexer.pc DESTINATION lib/pkgconfig) + if(CMAKE_SYSTEM_NAME MATCHES "^Linux") set(UDEV_RULES_INSTALL_DIR /lib/udev/rules.d CACHE PATH "install directory for linux udev config") - install(FILES 90-usbled.rules DESTINATION ${UDEV_RULES_INSTALL_DIR}) + install(FILES 90-eismultiplexer.rules DESTINATION ${UDEV_RULES_INSTALL_DIR}) endif() diff --git a/eismultiplexer.c b/eismultiplexer.c new file mode 100644 index 0000000000000000000000000000000000000000..67a653c4c9de9c96a2d984b7ded3d6ad846be6a9 --- /dev/null +++ b/eismultiplexer.c @@ -0,0 +1,113 @@ +/** +* Copyright (C) 2023 Carl Klemm +* +* This program is free software; you can redistribute it and/or +* modify it under the terms of the GNU General Public License +* version 3 as published by the Free Software Foundation. +* +* This program 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 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., 51 Franklin Street, Fifth Floor, +* Boston, MA 02110-1301, USA. +*/ + +#define _POSIX_C_SOURCE 199309L +#include "eismultiplexer.h" +#include "usbshm.h" +#include <stdlib.h> +#include <time.h> + +static void usleep(uint64_t microseconds) +{ + struct timespec ts; + ts.tv_sec = microseconds / 1000000; + ts.tv_nsec = (microseconds % 1000000) * 1000; + nanosleep(&ts, NULL); +} + +static void dataCallback(uint8_t request, unsigned char* data, size_t length, void* user_data) +{ + struct eismulitplexer* muliplexer = user_data; + + if(length >= 1) + muliplexer->activeChannels = data[0]; + + sem_post(&muliplexer->readSem); +} + +int eismulitplexer_connect(struct eismulitplexer* muliplexer, int serial) +{ + int ret; + muliplexer->priv = malloc(sizeof(*muliplexer->priv)); + if(!muliplexer->priv) + return -1; + + ret = usbshm_init(muliplexer->priv, &dataCallback, muliplexer); + if(ret) + return -2; + + ret = usbshm_open(muliplexer->priv, 0xfe17, 0x07dc , NULL); + if(ret) + return -3; + + ret = sem_init(&muliplexer->readSem, 0, 0); + if(ret) + return -4; + return 0; +} + +int eismulitplexer_connect_channel_exclusive(struct eismulitplexer* muliplexer, channel_t channel) +{ + uint16_t wValue; + // we compile with fno-strict-aliasing + uint8_t* wValChar = (uint8_t*)&wValue; + wValChar[0] = channel; + wValChar[1] = 0; + int ret; + while((ret = usbshm_writeControlTransfer(muliplexer->priv, 2, NULL, 0, wValue, 0)) == USBSHM_ERROR_AGAIN) + usleep(1000000); + return ret; +} + +int eismulitplexer_connect_channel(struct eismulitplexer* muliplexer, channel_t channel) +{ + channel_t channels = eismulitplexer_get_connected(muliplexer); + channels |= channel; + return eismulitplexer_connect_channel_exclusive(muliplexer, channels); +} + +int eismulitplexer_disconnect_channel(struct eismulitplexer* muliplexer, channel_t channel) +{ + channel_t channels = eismulitplexer_get_connected(muliplexer); + channels &= ~channel; + return eismulitplexer_connect_channel_exclusive(muliplexer, channels); +} + +channel_t eismulitplexer_get_connected(struct eismulitplexer* muliplexer) +{ + usbshm_readControlTransfer(muliplexer->priv, 3, 1); + sem_wait(&muliplexer->readSem); + return muliplexer->activeChannels; +} + + +int eismulitplexer_set_led(struct eismulitplexer* muliplexer, bool on) +{ + int ret; + while((ret = usbshm_writeControlTransfer(muliplexer->priv, on, NULL, 0, 0, 0)) == USBSHM_ERROR_AGAIN) + usleep(1000000); + return ret; +} + +void eismulitplexer_disconnect(struct eismulitplexer* muliplexer) +{ + usbshm_distroy(muliplexer->priv); + free(muliplexer->priv); + muliplexer->priv = NULL; + sem_destroy(&muliplexer->readSem); +} diff --git a/eismultiplexer.h b/eismultiplexer.h new file mode 100644 index 0000000000000000000000000000000000000000..0a78563de7b9147294151b750086a24fd4c02632 --- /dev/null +++ b/eismultiplexer.h @@ -0,0 +1,113 @@ +/** +* Copyright (C) 2023 Carl Klemm +* +* This program is free software; you can redistribute it and/or +* modify it under the terms of the GNU General Public License +* version 3 as published by the Free Software Foundation. +* +* This program 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 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., 51 Franklin Street, Fifth Floor, +* Boston, MA 02110-1301, USA. +*/ + +#pragma once + +#include <stdint.h> +#include <semaphore.h> +#include <stdbool.h> + +/** +Api for use by libscipaper eismulitplexer. +* @defgroup API User API +* This api allows you to controll the EISmulitplexer device. +* @{ +*/ + +#ifdef __cplusplus +extern "C" { +#endif + +typedef enum { + CHANNEL_A = (1 << 1), + CHANNEL_B = (1 << 2), + CHANNEL_C = (1 << 3), + CHANNEL_D = (1 << 4), + CHANNEL_E = (1 << 5), + CHANNEL_F = (1 << 6), + CHANNEL_G = (1 << 7), + CHANNEL_NONE = 0, +} channel_t; + +struct eismulitplexer { + struct usbshm* priv; + sem_t readSem; + channel_t activeChannels; +}; + +/** + * @brief Attempts to connect to a EISmulitplexer device and initalizes a eismulitplexer struct + * @param muliplexer pointer to a eismulitplexer struct to initalize + * @param serial The serial number of the device to connect to, or 0 for any + * @return 0 on sucess and < 0 on failure + */ +int eismulitplexer_connect(struct eismulitplexer* muliplexer, int serial); + +/** + * @brief Conects the given channel(s) to the common inputs + * @param muliplexer pointer to a eismulitplexer struct + * @param channel A channel to connect, multiple channels can be specified by or'ing together the chanel flags e.g. (CHANNEL_A | CHANNEL_B) + * @return 0 on sucess and < 0 on failure + */ +int eismulitplexer_connect_channel(struct eismulitplexer* muliplexer, channel_t channel); + +/** + * @brief Conects the given channel(s) to the common inputs disconnecting all others + * @param muliplexer pointer to a eismulitplexer struct + * @param channel A channel to connect, multiple channels can be specified by or'ing together the chanel flags e.g. (CHANNEL_A | CHANNEL_B) + * @return 0 on sucess and < 0 on failure + */ +int eismulitplexer_connect_channel_exclusive(struct eismulitplexer* muliplexer, channel_t channel); + +/** + * @brief Disconnect the given channel(s) to the common inputs disconnecting all others + * @param muliplexer pointer to a eismulitplexer struct + * @param channel A channel to connect, multiple channels can be specified by or'ing together the chanel flags e.g. (CHANNEL_A | CHANNEL_B) + * All channels can be dissconnected by passing CHANNEL_NONE + * @return 0 on sucess and < 0 on failure + */ +int eismulitplexer_disconnect_channel(struct eismulitplexer* muliplexer, channel_t channel); + +/** + * @brief Returns the channels currently connected + * @param muliplexer pointer to a eismulitplexer struct + * @return channels connected as a bitfield + */ +channel_t eismulitplexer_get_connected(struct eismulitplexer* muliplexer); + +/** + * @brief Turns the led on the pcb on or off + * @param muliplexer pointer to a eismulitplexer struct + * @param on true to turn the led on, false to turn it off + * @return 0 on sucess and < 0 on failure + */ +int eismulitplexer_set_led(struct eismulitplexer* muliplexer, bool on); + +/** + * @brief Disconnects from the eismulitplexer + */ +void eismulitplexer_disconnect(struct eismulitplexer* muliplexer); + +#ifdef __cplusplus +} +#endif + +/** +.... +* @} +*/ diff --git a/main.c b/main.c index ccba5671f5c4ae5783781840f1933538ac06c2ac..106352677a7dee7930e82b574ff78e54566b3e5e 100644 --- a/main.c +++ b/main.c @@ -18,92 +18,104 @@ */ #include <stdio.h> -#include <unistd.h> -#include <math.h> #include <stdlib.h> #include <string.h> -#include "uvosled.h" +#include "eismultiplexer.h" void print_help(const char* progname) { printf("usage: %s [OPERATION] [CHANNEL] [VALUE]\n", progname); - printf("available operations: set on off help\n"); + printf("available operations: connect disconnect help\n"); } +channel_t char_to_channel(char ch) +{ + switch(ch) + { + case 'a': + case 'A': + case '1': + return CHANNEL_A; + case 'b': + case 'B': + case '2': + return CHANNEL_B; + case 'c': + case 'C': + case '3': + return CHANNEL_C; + case 'd': + case 'D': + case '4': + return CHANNEL_D; + case 'e': + case 'E': + case '5': + return CHANNEL_E; + case 'f': + case 'F': + case '6': + return CHANNEL_F; + case 'g': + case 'G': + case '7': + return CHANNEL_NONE; + default: + return CHANNEL_NONE; + } +} + + int main(int argc, char* argv[]) { - if(argc < 2 || strcmp (argv[1], "help") == 0) + if(argc < 2 || strcmp(argv[1], "help") == 0) { print_help(argv[0]); return 0; } - struct uvosled led; + struct eismulitplexer multiplexer; - if(uvosled_connect(&led)) + if(eismulitplexer_connect(&multiplexer, 0)) { - printf("Can not connect to UVOS usbled device\n"); - return -1; + printf("Can not connect to EISmultiplexer device\n"); + return 1; } - - if(strcmp(argv[1], "on") == 0) + + if(argc != 4) { - if(uvosled_poweron(&led)) - printf("cant power on \n"); + print_help(argv[0]); + eismulitplexer_disconnect(&multiplexer); + return 2; } - else if(strcmp(argv[1], "off") == 0) + + channel_t channel = char_to_channel(argv[2][0]); + if(channel == CHANNEL_NONE) { - uvosled_set_current(&led, 0xff , 0); - uvosled_poweroff(&led); + printf("%c is not a valid channel", argv[2][0]); + eismulitplexer_disconnect(&multiplexer); + return 2; } - else if(strcmp(argv[1], "set") == 0) + + if(strcmp(argv[1], "connect") == 0) { - if(argc != 4) + if(eismulitplexer_connect_channel(&multiplexer, channel)) { - printf("usage: %s set [CHANNEL] [VALUE]\n", argv[0]); - uvosled_poweroff(&led); - return 0; + printf("could not connect channel %c", argv[2][0]); + eismulitplexer_disconnect(&multiplexer); + return 3; } - - uint8_t mask = 0; - - switch(argv[2][0]) - { - case 'a': - case 'A': - case '1': - mask = CHANNEL_A; - break; - case 'b': - case 'B': - case '2': - mask = CHANNEL_B; - break; - case 'c': - case 'C': - case '3': - mask = CHANNEL_C; - break; - case 'd': - case 'D': - case '4': - mask = CHANNEL_D; - break; - default: - printf("A channel must be selected out of A, B, C or D\n"); - } - - if(mask) + } + else if(strcmp(argv[1], "disconnect") == 0) + { + if(eismulitplexer_disconnect_channel(&multiplexer, channel)) { - char* end; - float value = strtof(argv[3], &end); - if(isnan(value) || value < 0 || value > 1.0f) - printf("A channel value between 0 and 1.0 must be supplied\n"); - else if(uvosled_set_current(&led, mask, value)) - printf("Failed to set current\n"); + printf("could not connect channel %c", argv[2][0]); + eismulitplexer_disconnect(&multiplexer); + return 3; } } - uvosled_disconnect(&led); + eismulitplexer_disconnect(&multiplexer); return 0; } diff --git a/pkgconfig/eismuliplexer.pc.in b/pkgconfig/eismuliplexer.pc.in new file mode 100644 index 0000000000000000000000000000000000000000..c181acff60ba40257e1f6a590f94e6f1e2e68248 --- /dev/null +++ b/pkgconfig/eismuliplexer.pc.in @@ -0,0 +1,7 @@ +includedir=@CMAKE_INSTALL_PREFIX@/include + +Name: libeismuliplexer +Description: C lib to control EISmultiplexer devices +Version: 1.0 +Libs: -L${libdir} -leismultiplexer +Cflags: -I${includedir} diff --git a/usbshm.c b/usbshm.c index 35b9da3fa687044346566dc43cdda1118f59cf76..0de07f3a20d27b3c5432c6e8e47ea48dde5fe509 100644 --- a/usbshm.c +++ b/usbshm.c @@ -75,7 +75,7 @@ bool usbshm_isOpen(struct usbshm* instance) return instance->priv->handle != NULL; } -int usbshm_init(struct usbshm* instance, void (*dataCallback)(uint8_t request, unsigned char* data, size_t length)) +int usbshm_init(struct usbshm* instance, void (*dataCallback)(uint8_t request, unsigned char* data, size_t length, void* user_data), void* user_data) { int ret=0; instance->priv = malloc(sizeof(*instance->priv)); @@ -86,6 +86,7 @@ int usbshm_init(struct usbshm* instance, void (*dataCallback)(uint8_t request, u instance->productID = 0; instance->productName = NULL; instance->dataCallback = dataCallback; + instance->user_data = user_data; if(objectCounter == 0) { printf("Usb Init\n"); @@ -249,7 +250,7 @@ static void usbshm_transferCallBack(struct libusb_transfer *transfer) else if(transfer->type == LIBUSB_TRANSFER_TYPE_CONTROL) { if(context->dataCallback && transfer->length-8 >= transfer->actual_length) - context->dataCallback(transfer->buffer[1], transfer->buffer+8, transfer->actual_length); + context->dataCallback(transfer->buffer[1], transfer->buffer+8, transfer->actual_length, context->user_data); } free(context->priv->buffer); libusb_free_transfer(context->priv->transfer); diff --git a/usbshm.h b/usbshm.h index 29066ed709cb353403f29c44488aa4829439f040..21eefddd0d541a0db9ca4e600c5e78e87f214332 100644 --- a/usbshm.h +++ b/usbshm.h @@ -36,12 +36,13 @@ struct usbshm { int vendorID; int productID; char* productName; - void (*dataCallback)(uint8_t request, unsigned char* data, size_t length); + void* user_data; + void (*dataCallback)(uint8_t request, unsigned char* data, size_t length, void* user_data); }; void usbshm_distroy(struct usbshm* instance); -int usbshm_init(struct usbshm* instance, void (*dataCallback)(uint8_t request, unsigned char* data, size_t length)); +int usbshm_init(struct usbshm* instance, void (*dataCallback)(uint8_t request, unsigned char* data, size_t length, void* user_data), void* user_data); bool usbshm_ready(struct usbshm* instance); diff --git a/uvosled.c b/uvosled.c deleted file mode 100644 index efc059fb0b9c3f2e6bceb82d75f18fe4820eb69b..0000000000000000000000000000000000000000 --- a/uvosled.c +++ /dev/null @@ -1,124 +0,0 @@ -/** -* Copyright (C) 2023 Carl Klemm -* -* This program is free software; you can redistribute it and/or -* modify it under the terms of the GNU General Public License -* version 3 as published by the Free Software Foundation. -* -* This program 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 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., 51 Franklin Street, Fifth Floor, -* Boston, MA 02110-1301, USA. -*/ - -#define _POSIX_C_SOURCE 199309L -#include "uvosled.h" -#include "usbshm.h" -#include <stdlib.h> -#include <time.h> - -void usleep(uint64_t microseconds) -{ - struct timespec ts; - ts.tv_sec = microseconds / 1000000; - ts.tv_nsec = (microseconds % 1000000) * 1000; - nanosleep(&ts, NULL); -} - -int uvosled_connect(struct uvosled* led) -{ - int ret; - led->priv = malloc(sizeof(*led->priv)); - if(!led->priv) - return -1; - - ret = usbshm_init(led->priv, NULL); - if(ret) - return -2; - - ret = usbshm_open(led->priv, 0xfe17, 0x06dc , NULL); - if(ret) - return -3; - return 0; -} - -int uvosled_poweron(struct uvosled* led) -{ - int ret; - while((ret = usbshm_writeControlTransfer(led->priv, 0, NULL, 0, 0, 0)) == USBSHM_ERROR_AGAIN) - usleep(1000000); - return ret; -} - -int uvosled_poweroff(struct uvosled* led) -{ - int ret; - while((ret = usbshm_writeControlTransfer(led->priv, 1, NULL, 0, 0, 0)) == USBSHM_ERROR_AGAIN) - usleep(1000000); - return ret; -} - -int uvosled_set_current(struct uvosled* led, uint8_t channels, float current) -{ - if(current < 0) - return -1; - else if(current > 1) - return -2; - uint8_t currentU = current * 255; - // TODO: implment endianess - uint16_t wValue; - // we compile with fno-strict-aliasing - uint8_t* wValChar = (uint8_t*)&wValue; - wValChar[0] = channels; - wValChar[1] = currentU; - int ret; - while((ret = usbshm_writeControlTransfer(led->priv, 2, NULL, 0, wValue, 0)) == USBSHM_ERROR_AGAIN) - usleep(1000000); - return ret; -} - -int uvosled_trigger(struct uvosled* led) -{ - int ret; - while((ret = usbshm_writeControlTransfer(led->priv, 1, NULL, 0, 0, 0)) == USBSHM_ERROR_AGAIN) - usleep(1000000); - return ret; -} - -int uvosled_capture(struct uvosled* led, int channels, float current, double time, double cameraOffset) -{ - if(current < 0 || current > 1 || time > 1 || cameraOffset < -1 || cameraOffset > 1) - return USBSHM_ERROR_PARAM; - - uint8_t currentU = current * 255; - uint16_t timeU = time * 1000; - int16_t cameraOffsetU = cameraOffset * 1000; - int ret; - - while((ret = usbshm_writeControlTransfer(led->priv, 5, NULL, 0, *((uint16_t*)&cameraOffsetU), 0)) == USBSHM_ERROR_AGAIN) - usleep(1000000); - if(ret < 0) - return ret; - - // TODO: implment endianess - uint16_t wValue; - // we compile with fno-strict-aliasing - uint8_t* wValChar = (uint8_t*)&wValue; - wValChar[0] = channels; - wValChar[1] = currentU; - while((ret = usbshm_writeControlTransfer(led->priv, 1, NULL, 0, wValue, timeU)) == USBSHM_ERROR_AGAIN) - usleep(1000000); - return ret; -} - -void uvosled_disconnect(struct uvosled* led) -{ - usbshm_distroy(led->priv); - free(led->priv); - led->priv = NULL; -} diff --git a/uvosled.h b/uvosled.h deleted file mode 100644 index 94784b382f9c3788541037e0bc9a9c5494f82c6a..0000000000000000000000000000000000000000 --- a/uvosled.h +++ /dev/null @@ -1,65 +0,0 @@ -/** -* Copyright (C) 2023 Carl Klemm -* -* This program is free software; you can redistribute it and/or -* modify it under the terms of the GNU General Public License -* version 3 as published by the Free Software Foundation. -* -* This program 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 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., 51 Franklin Street, Fifth Floor, -* Boston, MA 02110-1301, USA. -*/ - -#pragma once - -#include <stdint.h> - -#ifdef __cplusplus -extern "C" { -#endif - -#define CHANNEL_A 1 -#define CHANNEL_B (1 << 1) -#define CHANNEL_C (1 << 2) -#define CHANNEL_D (1 << 3) - -struct uvosled { - struct usbshm* priv; -}; - -// returns 0 on sucess and < 0 on failure -int uvosled_connect(struct uvosled* led); - -// power on cameras -// returns 0 on sucess and < 0 on failure -int uvosled_poweron(struct uvosled* led); - -// power off cameras -// returns 0 on sucess and < 0 on failure -int uvosled_poweroff(struct uvosled* led); - -// channels is a mask of bits, you can set it like this: CHANNEL_A | CHANNEL_C to activate channels A and C -// current is in percent of the values selected by the linear regulator resistors -// returns 0 on sucess and < 0 on failure -int uvosled_set_current(struct uvosled* led, uint8_t channels, float current); - -// causes the cameras to take an image -// returns 0 on sucess and < 0 on failure -int uvosled_trigger(struct uvosled* led); - -// leds are lit for time seconds and the camera is activated cameraOffset seconds after they are lit -// real time guarenteed by microcontroller -// returns 0 on sucess and < 0 on failure -int uvosled_capture(struct uvosled* led, int channels, float current, double time, double cameraOffset); - -void uvosled_disconnect(struct uvosled* led); - -#ifdef __cplusplus -} -#endif