diff --git a/CMakeLists.txt b/CMakeLists.txt new file mode 100644 index 0000000..b653f67 --- /dev/null +++ b/CMakeLists.txt @@ -0,0 +1,57 @@ +cmake_minimum_required(VERSION 3.5) +project(usb_bridge) + +# Default to C99 +if(NOT CMAKE_C_STANDARD) + set(CMAKE_C_STANDARD 99) +endif() + +# Default to C++14 +if(NOT CMAKE_CXX_STANDARD) + set(CMAKE_CXX_STANDARD 14) +endif() + +if(CMAKE_COMPILER_IS_GNUCXX OR CMAKE_CXX_COMPILER_ID MATCHES "Clang") + add_compile_options(-Wall -Wextra -Wpedantic) +endif() + +# find dependencies +find_package(ament_cmake REQUIRED) +# uncomment the following section in order to fill in +# further dependencies manually. +# find_package( REQUIRED) +find_package(rclcpp REQUIRED) +find_package(std_msgs REQUIRED) +find_package(example_interfaces REQUIRED) + +list(APPEND CMAKE_MODULE_PATH "${CMAKE_CURRENT_LIST_DIR}/cmake") + +find_package(LibUSB REQUIRED) + + +add_executable(libusb_demo src/libusb_demo.cpp) +ament_target_dependencies( + libusb_demo + rclcpp + std_msgs + example_interfaces + LIBUSB +) + + +install(TARGETS + libusb_demo + DESTINATION lib/${PROJECT_NAME}) + +if(BUILD_TESTING) + find_package(ament_lint_auto REQUIRED) + # the following line skips the linter which checks for copyrights + # uncomment the line when a copyright and license is not present in all source files + #set(ament_cmake_copyright_FOUND TRUE) + # the following line skips cpplint (only works in a git repo) + # uncomment the line when this package is not in a git repo + #set(ament_cmake_cpplint_FOUND TRUE) + ament_lint_auto_find_test_dependencies() +endif() + +ament_package() diff --git a/cmake/FindLibUSB.cmake b/cmake/FindLibUSB.cmake new file mode 100644 index 0000000..7b36e54 --- /dev/null +++ b/cmake/FindLibUSB.cmake @@ -0,0 +1,55 @@ +if(NOT LIBUSB_FOUND) + pkg_check_modules (LIBUSB_PKG libusb-1.0 libusb) + find_path(LIBUSB_INCLUDE_DIR NAMES libusb.h + PATHS + ${LIBUSB_PKG_INCLUDE_DIRS} + /usr/include/libusb-1.0 + /usr/include + /usr/local/include + ) + +#standard library name for libusb-1.0 +set(libusb1_library_names usb-1.0) + +#libusb-1.0 compatible library on freebsd +if((CMAKE_SYSTEM_NAME STREQUAL "FreeBSD") OR (CMAKE_SYSTEM_NAME STREQUAL "kFreeBSD") OR (CMAKE_SYSTEM_NAME STREQUAL "GNU")) + list(APPEND libusb1_library_names usb) +endif() + + find_library(LIBUSB_LIBRARIES + NAMES ${libusb1_library_names} + PATHS + ${LIBUSB_PKG_LIBRARY_DIRS} + /usr/lib + /usr/local/lib + ) + +include(CheckFunctionExists) +if(LIBUSB_INCLUDE_DIRS) + set(CMAKE_REQUIRED_INCLUDES ${LIBUSB_INCLUDE_DIRS}) +endif() +if(LIBUSB_LIBRARIES) + set(CMAKE_REQUIRED_LIBRARIES ${LIBUSB_LIBRARIES}) +endif() + +CHECK_FUNCTION_EXISTS("libusb_handle_events_timeout_completed" HAVE_LIBUSB_HANDLE_EVENTS_TIMEOUT_COMPLETED) +if(HAVE_LIBUSB_HANDLE_EVENTS_TIMEOUT_COMPLETED) + add_definitions(-DHAVE_LIBUSB_HANDLE_EVENTS_TIMEOUT_COMPLETED=1) +endif(HAVE_LIBUSB_HANDLE_EVENTS_TIMEOUT_COMPLETED) + +CHECK_FUNCTION_EXISTS("libusb_error_name" HAVE_LIBUSB_ERROR_NAME) +if(HAVE_LIBUSB_ERROR_NAME) + add_definitions(-DHAVE_LIBUSB_ERROR_NAME=1) +endif(HAVE_LIBUSB_ERROR_NAME) + +if(LIBUSB_INCLUDE_DIR AND LIBUSB_LIBRARIES) + set(LIBUSB_FOUND TRUE CACHE INTERNAL "libusb-1.0 found") + message(STATUS "Found libusb-1.0: ${LIBUSB_INCLUDE_DIR}, ${LIBUSB_LIBRARIES}") +else(LIBUSB_INCLUDE_DIR AND LIBUSB_LIBRARIES) + set(LIBUSB_FOUND FALSE CACHE INTERNAL "libusb-1.0 found") + message(STATUS "libusb-1.0 not found.") +endif(LIBUSB_INCLUDE_DIR AND LIBUSB_LIBRARIES) + +mark_as_advanced(LIBUSB_INCLUDE_DIR LIBUSB_LIBRARIES) + +endif(NOT LIBUSB_FOUND) \ No newline at end of file diff --git a/package.xml b/package.xml new file mode 100644 index 0000000..a6e7457 --- /dev/null +++ b/package.xml @@ -0,0 +1,20 @@ + + + + usb_bridge + 0.0.0 + TODO: Package description + brettrd + TODO: License declaration + + ament_cmake + + libusb-1.0 + + ament_lint_auto + ament_lint_common + + + ament_cmake + + diff --git a/src/libusb_demo.cpp b/src/libusb_demo.cpp new file mode 100644 index 0000000..b448a0a --- /dev/null +++ b/src/libusb_demo.cpp @@ -0,0 +1,216 @@ +#include +#include +#include +#include +#include + +#include "rclcpp/rclcpp.hpp" +#include "std_msgs/msg/string.hpp" +#include "example_interfaces/msg/u_int8.hpp" +#include "example_interfaces/msg/u_int32_multi_array.hpp" + + + +using namespace std::chrono_literals; + + +#define CTRL_IN (LIBUSB_REQUEST_TYPE_VENDOR | LIBUSB_RECIPIENT_OTHER | LIBUSB_ENDPOINT_IN) +#define CTRL_OUT (LIBUSB_REQUEST_TYPE_VENDOR | LIBUSB_RECIPIENT_OTHER | LIBUSB_ENDPOINT_OUT) + + + +#define CSR_BASE 0x0L +#define CSR_PPM_INPUT_BASE (CSR_BASE + 0x3000L) +#define CSR_PPM_INPUT_CHANNELS 2 +#define CSR_PPM_OUTPUT_BASE (CSR_BASE + 0x3800L) +#define CSR_PPM_OUTPUT_CHANNELS 2 +#define CSR_PWM_INPUT_BASE (CSR_BASE + 0x7800L) +#define CSR_TRIV_REG_BASE (CSR_BASE + 0x8000L) + + + +class WishbonePublisher : public rclcpp::Node +{ + public: + WishbonePublisher() + : Node("minimal_publisher"), count_(0) + { + + uint16_t idVendor=0x1209; + uint16_t idProduct=0x5bf0; + + + + triv_reg_pub = this->create_publisher("triv_reg", 10); + ppm_in_pub = this->create_publisher("ppm_in", 10); + + + + if (0 <= libusb_init(NULL)) + { + devh = libusb_open_device_with_vid_pid(NULL, idVendor, idProduct); + if(devh != NULL) + { + if(0 <= libusb_claim_interface(devh, 0)) + { + RCLCPP_INFO(this->get_logger(), "claimed interface"); + + + this->triv_reg_timer = this->create_wall_timer(500ms, std::bind(&WishbonePublisher::triv_reg_callback, this)); + this->ppm_in_timer = this->create_wall_timer(200ms, std::bind(&WishbonePublisher::ppm_in_callback, this)); + + } + else + { + RCLCPP_ERROR(this->get_logger(), "could not claim interface"); + libusb_close(devh); + devh = NULL; + } + } + else + { + RCLCPP_ERROR(this->get_logger(), "No device"); + } + } + else + { + RCLCPP_ERROR(this->get_logger(), "libusb did not init"); + } + } + + ~WishbonePublisher() + { + if(devh) + { + libusb_release_interface(devh, 0); + libusb_close(devh); + } + } + + + + private: + + + int wishbone_poke(uint32_t address, uint32_t* data) + { + int r; + uint8_t bRequest = 0x00; + uint16_t addr_h, addr_l; + unsigned char buf[4]; + uint16_t len = 4; + addr_h = address >> 16; + addr_l = address & 0xFFFF; + + for(int i=0; i<4; i++) + { + buf[i] = (*data >> (8*i)) & 0xFF; + } + + r = libusb_control_transfer(devh, CTRL_OUT, bRequest, addr_l, addr_h, buf, len, 0); + if (r < 0) { + RCLCPP_ERROR(this->get_logger(), "poke error: %s", libusb_error_name(r)); + } + else if ((unsigned int) r < len) { + RCLCPP_ERROR(this->get_logger(), "poke error: short write (%d)", r); + } + return r; + } + + int wishbone_peek(uint32_t address, uint32_t* data) + { + int r; + uint8_t bRequest = 0x00; + uint16_t addr_h, addr_l; + unsigned char buf[4]; + uint16_t len = 4; + addr_h = address >> 16; + addr_l = address & 0xFFFF; + + r = libusb_control_transfer(devh, CTRL_IN, bRequest, addr_l, addr_h, buf, len, 0); + if (r < 0) { + RCLCPP_ERROR(this->get_logger(), "peek error: %s", libusb_error_name(r)); + } + else if ((unsigned int) r < len) { + RCLCPP_ERROR(this->get_logger(), "peek error: short read (%d)", r); + } + else + { + *data = 0; + for(int i=0; i<4; i++) + { + *data += ((uint32_t)(buf[i])) << (8*i); + } + } + + return r; + } + + + + + void read_ppm(uint32_t* data, int count) + { + if(count > CSR_PPM_INPUT_CHANNELS) count = CSR_PPM_INPUT_CHANNELS; + for(int i=0; i CSR_PPM_OUTPUT_CHANNELS) count = CSR_PPM_OUTPUT_CHANNELS; + for(int i=0; ipublish(message); + } + + + void ppm_in_callback() + { + auto message = example_interfaces::msg::UInt32MultiArray(); + message.data.resize(CSR_PPM_INPUT_CHANNELS); + read_ppm(message.data.data(), CSR_PPM_INPUT_CHANNELS); + ppm_in_pub->publish(message); + } + + + + struct libusb_device_handle *devh = NULL; + rclcpp::TimerBase::SharedPtr triv_reg_timer; + rclcpp::TimerBase::SharedPtr ppm_in_timer; + rclcpp::Publisher::SharedPtr triv_reg_pub; + rclcpp::Publisher::SharedPtr ppm_in_pub; + + size_t count_; + }; + + int main(int argc, char * argv[]) + { + rclcpp::init(argc, argv); + rclcpp::spin(std::make_shared()); + rclcpp::shutdown(); + return 0; + } \ No newline at end of file