diff --git a/HISTORY.txt b/HISTORY.txt index 838f25d05..1e9bcb162 100644 --- a/HISTORY.txt +++ b/HISTORY.txt @@ -1,5 +1,9 @@ Short summary of changes. For a complete list see the git log. +2012-08-11 +CopterControl can now emulate an 8-channel USB HID joystick. Primarily, +this lets you use any RC transmitter with flight simulators on your PC. + 2012-07-20 AeroSimRC simulator plugin is now included into the Windows distribution (will be installed into .../OpenPilot/misc/AeroSIM-RC directory). Still diff --git a/flight/CopterControl/Makefile b/flight/CopterControl/Makefile index 7c5498806..9acfdff41 100644 --- a/flight/CopterControl/Makefile +++ b/flight/CopterControl/Makefile @@ -239,6 +239,7 @@ SRC += $(PIOSSTM32F10X)/pios_bl_helper.c SRC += $(PIOSSTM32F10X)/pios_usb.c SRC += $(PIOSSTM32F10X)/pios_usbhook.c SRC += $(PIOSSTM32F10X)/pios_usb_hid.c +SRC += $(PIOSSTM32F10X)/pios_usb_rctx.c SRC += $(PIOSSTM32F10X)/pios_usb_cdc.c SRC += $(PIOSSTM32F10X)/pios_usb_hid_istr.c SRC += $(PIOSSTM32F10X)/pios_usb_hid_pwr.c diff --git a/flight/CopterControl/System/inc/pios_config.h b/flight/CopterControl/System/inc/pios_config.h index 7c5857ab8..0edaecbf7 100644 --- a/flight/CopterControl/System/inc/pios_config.h +++ b/flight/CopterControl/System/inc/pios_config.h @@ -66,6 +66,7 @@ #define PIOS_INCLUDE_USART #define PIOS_INCLUDE_USB #define PIOS_INCLUDE_USB_HID +#define PIOS_INCLUDE_USB_RCTX #define PIOS_INCLUDE_USB_CDC #define PIOS_INCLUDE_COM #define PIOS_INCLUDE_SETTINGS diff --git a/flight/CopterControl/System/pios_board.c b/flight/CopterControl/System/pios_board.c index 42d5a4bfe..ccbae3058 100644 --- a/flight/CopterControl/System/pios_board.c +++ b/flight/CopterControl/System/pios_board.c @@ -67,6 +67,8 @@ uint32_t pios_com_vcp_id; uint32_t pios_com_gps_id; uint32_t pios_com_bridge_id; +uint32_t pios_usb_rctx_id; + /** * Configuration for MPU6000 chip */ @@ -358,6 +360,15 @@ void PIOS_Board_Init(void) { } #endif /* PIOS_INCLUDE_COM */ break; + case HWSETTINGS_USB_HIDPORT_RCTRANSMITTER: +#if defined(PIOS_INCLUDE_USB_RCTX) + { + if (PIOS_USB_RCTX_Init(&pios_usb_rctx_id, &pios_usb_rctx_cfg, pios_usb_id)) { + PIOS_Assert(0); + } + } +#endif /* PIOS_INCLUDE_USB_RCTX */ + break; } #endif /* PIOS_INCLUDE_USB_HID */ diff --git a/flight/Modules/ManualControl/manualcontrol.c b/flight/Modules/ManualControl/manualcontrol.c index cad5d065d..7d0e78881 100644 --- a/flight/Modules/ManualControl/manualcontrol.c +++ b/flight/Modules/ManualControl/manualcontrol.c @@ -48,6 +48,10 @@ #include "positionactual.h" #include "baroaltitude.h" +#if defined(PIOS_INCLUDE_USB_RCTX) +#include "pios_usb_rctx.h" +#endif /* PIOS_INCLUDE_USB_RCTX */ + // Private constants #if defined(PIOS_MANUAL_STACK_SIZE) #define STACK_SIZE_BYTES PIOS_MANUAL_STACK_SIZE @@ -369,6 +373,15 @@ static void manualControlTask(void *parameters) // Update cmd object ManualControlCommandSet(&cmd); +#if defined(PIOS_INCLUDE_USB_RCTX) + if (pios_usb_rctx_id) { + PIOS_USB_RCTX_Update(pios_usb_rctx_id, + cmd.Channel, + settings.ChannelMin, + settings.ChannelMax, + NELEMENTS(cmd.Channel)); + } +#endif /* PIOS_INCLUDE_USB_RCTX */ } else { ManualControlCommandGet(&cmd); /* Under GCS control */ diff --git a/flight/PiOS/Common/pios_usb_desc_hid_cdc.c b/flight/PiOS/Common/pios_usb_desc_hid_cdc.c index 1d4f0a9b2..747eda3d4 100644 --- a/flight/PiOS/Common/pios_usb_desc_hid_cdc.c +++ b/flight/PiOS/Common/pios_usb_desc_hid_cdc.c @@ -51,7 +51,7 @@ static const struct usb_device_desc device_desc = { .bNumConfigurations = 1, }; -static const uint8_t hid_report_desc[36] = { +static const uint8_t hid_report_desc[89] = { HID_GLOBAL_ITEM_2 (HID_TAG_GLOBAL_USAGE_PAGE), 0x9C, 0xFF, /* Usage Page 0xFF9C (Vendor Defined) */ HID_LOCAL_ITEM_1 (HID_TAG_LOCAL_USAGE), @@ -93,6 +93,75 @@ static const uint8_t hid_report_desc[36] = { 0x82, /* Volatile, Variable */ HID_MAIN_ITEM_0 (HID_TAG_MAIN_ENDCOLLECTION), + +/* 36 bytes to here */ + + /* Emulate a Joystick */ + HID_GLOBAL_ITEM_1 (HID_TAG_GLOBAL_USAGE_PAGE), + 0x01, /* Usage Page 0x01 (Generic Desktop Controls) */ + HID_LOCAL_ITEM_1 (HID_TAG_LOCAL_USAGE), + 0x04, /* Usage ID 0x0004 (Joystick) */ + + HID_GLOBAL_ITEM_1 (HID_TAG_GLOBAL_LOGICAL_MIN), + 0x00, /* Values range from min = 0x00 */ + HID_GLOBAL_ITEM_4 (HID_TAG_GLOBAL_LOGICAL_MAX), + 0xFF, 0xFF, 0x00, 0x00, /* Values range to max = 0x0000FFFF */ + + HID_MAIN_ITEM_1 (HID_TAG_MAIN_COLLECTION), + 0x01, /* Application */ + HID_MAIN_ITEM_1 (HID_TAG_MAIN_COLLECTION), + 0x00, /* Physical */ + HID_GLOBAL_ITEM_1 (HID_TAG_GLOBAL_REPORT_ID), + 0x03, /* OpenPilot Emulated joystick */ + + /* X + Y controls */ + + HID_LOCAL_ITEM_1 (HID_TAG_LOCAL_USAGE), + 0x30, /* Usage ID 0x00010030 (Generic Desktop: X) */ + HID_LOCAL_ITEM_1 (HID_TAG_LOCAL_USAGE), + 0x31, /* Usage ID 0x00010031 (Generic Desktop: Y) */ + HID_GLOBAL_ITEM_1 (HID_TAG_GLOBAL_REPORT_SIZE), + 0x10, /* 16 bits wide */ + HID_GLOBAL_ITEM_1 (HID_TAG_GLOBAL_REPORT_CNT), + 2, + HID_MAIN_ITEM_1 (HID_TAG_MAIN_INPUT), + 0x82, /* Data, Var, Abs, Vol */ + + /* Y + Rx controls */ + + HID_LOCAL_ITEM_1 (HID_TAG_LOCAL_USAGE), + 0x32, /* Usage ID 0x00010032 (Generic Desktop: Z) */ + HID_LOCAL_ITEM_1 (HID_TAG_LOCAL_USAGE), + 0x33, /* Usage ID 0x00010031 (Generic Desktop: Rx) */ + HID_GLOBAL_ITEM_1 (HID_TAG_GLOBAL_REPORT_SIZE), + 0x10, /* 16 bits wide */ + HID_GLOBAL_ITEM_1 (HID_TAG_GLOBAL_REPORT_CNT), + 2, + HID_MAIN_ITEM_1 (HID_TAG_MAIN_INPUT), + 0x82, /* Data, Var, Abs, Vol */ + + /* Ry, Rz, Slider + Dial controls */ + + HID_LOCAL_ITEM_1 (HID_TAG_LOCAL_USAGE), + 0x34, /* Usage ID 0x00010034 (Generic Desktop: Ry) */ + HID_LOCAL_ITEM_1 (HID_TAG_LOCAL_USAGE), + 0x35, /* Usage ID 0x00010035 (Generic Desktop: Rz) */ + HID_LOCAL_ITEM_1 (HID_TAG_LOCAL_USAGE), + 0x36, /* Usage ID 0x00010036 (Generic Desktop: Slider) */ + HID_LOCAL_ITEM_1 (HID_TAG_LOCAL_USAGE), + 0x37, /* Usage ID 0x00010037 (Generic Desktop: Dial) */ + HID_GLOBAL_ITEM_1 (HID_TAG_GLOBAL_REPORT_SIZE), + 0x10, /* 16 bits wide */ + HID_GLOBAL_ITEM_1 (HID_TAG_GLOBAL_REPORT_CNT), + 4, + HID_MAIN_ITEM_1 (HID_TAG_MAIN_INPUT), + 0x82, /* Data, Var, Abs, Vol */ + + HID_MAIN_ITEM_0 (HID_TAG_MAIN_ENDCOLLECTION), + + HID_MAIN_ITEM_0 (HID_TAG_MAIN_ENDCOLLECTION), + +/* 89 bytes to here */ }; struct usb_config_hid_cdc { diff --git a/flight/PiOS/STM32F10x/pios_usb_rctx.c b/flight/PiOS/STM32F10x/pios_usb_rctx.c new file mode 100644 index 000000000..3976eafc3 --- /dev/null +++ b/flight/PiOS/STM32F10x/pios_usb_rctx.c @@ -0,0 +1,211 @@ +/** + ****************************************************************************** + * @addtogroup PIOS PIOS Core hardware abstraction layer + * @{ + * @addtogroup PIOS_USB_RCTX USB RC Transmitter/Joystick Functions + * @brief PIOS USB implementation for a HID Joystick + * @notes This implements transmitter/joystick emulation over HID reports + * @{ + * + * @file pios_usb_rctx.c + * @author The OpenPilot Team, http://www.openpilot.org Copyright (C) 2012. + * @see The GNU Public License (GPL) Version 3 + * + *****************************************************************************/ +/* + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * 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., + * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +/* Project Includes */ +#include "pios.h" + +#if defined(PIOS_INCLUDE_USB_RCTX) + +#include "pios_usb.h" +#include "pios_usb_rctx_priv.h" + +/* STM32 USB Library Definitions */ +#include "usb_lib.h" + +#define PIOS_USB_RCTX_NUM_CHANNELS 8 + +enum pios_usb_rctx_dev_magic { + PIOS_USB_RCTX_DEV_MAGIC = 0xAB98B745, +}; + +struct pios_usb_rctx_dev { + enum pios_usb_rctx_dev_magic magic; + const struct pios_usb_rctx_cfg * cfg; + + uint32_t lower_id; + + struct { + uint8_t id; + uint16_t vals[PIOS_USB_RCTX_NUM_CHANNELS]; + } __attribute__((packed)) report; +}; + +static bool PIOS_USB_RCTX_validate(struct pios_usb_rctx_dev * usb_rctx_dev) +{ + return (usb_rctx_dev->magic == PIOS_USB_RCTX_DEV_MAGIC); +} + +#if defined(PIOS_INCLUDE_FREERTOS) +static struct pios_usb_rctx_dev * PIOS_USB_RCTX_alloc(void) +{ + struct pios_usb_rctx_dev * usb_rctx_dev; + + usb_rctx_dev = (struct pios_usb_rctx_dev *)pvPortMalloc(sizeof(*usb_rctx_dev)); + if (!usb_rctx_dev) return(NULL); + + usb_rctx_dev->magic = PIOS_USB_RCTX_DEV_MAGIC; + return(usb_rctx_dev); +} +#else +static struct pios_usb_rctx_dev pios_usb_rctx_devs[PIOS_USB_RCTX_MAX_DEVS]; +static uint8_t pios_usb_rctx_num_devs; +static struct pios_usb_rctx_dev * PIOS_USB_RCTX_alloc(void) +{ + struct pios_usb_rctx_dev * usb_rctx_dev; + + if (pios_usb_rctx_num_devs >= PIOS_USB_RCTX_MAX_DEVS) { + return (NULL); + } + + usb_rctx_dev = &pios_usb_rctx_devs[pios_usb_rctx_num_devs++]; + usb_rctx_dev->magic = PIOS_USB_RCTX_DEV_MAGIC; + + return (usb_rctx_dev); +} +#endif + +static void PIOS_USB_RCTX_EP_IN_Callback(void); +static void PIOS_USB_RCTX_SendReport(struct pios_usb_rctx_dev * usb_rctx_dev); + +/* Need a better way to pull these in */ +extern void (*pEpInt_IN[7])(void); + +int32_t PIOS_USB_RCTX_Init(uint32_t * usbrctx_id, const struct pios_usb_rctx_cfg * cfg, uint32_t lower_id) +{ + PIOS_Assert(usbrctx_id); + PIOS_Assert(cfg); + + struct pios_usb_rctx_dev * usb_rctx_dev; + + usb_rctx_dev = (struct pios_usb_rctx_dev *) PIOS_USB_RCTX_alloc(); + if (!usb_rctx_dev) goto out_fail; + + /* Bind the configuration to the device instance */ + usb_rctx_dev->cfg = cfg; + usb_rctx_dev->lower_id = lower_id; + + /* Set the initial report buffer */ + memset(&usb_rctx_dev->report, 0, sizeof(usb_rctx_dev->report)); + + pEpInt_IN[cfg->data_tx_ep - 1] = PIOS_USB_RCTX_EP_IN_Callback; + + *usbrctx_id = (uint32_t) usb_rctx_dev; + + return 0; + +out_fail: + return -1; +} + +static void PIOS_USB_RCTX_SendReport(struct pios_usb_rctx_dev * usb_rctx_dev) +{ +#if defined(PIOS_INCLUDE_FREERTOS) + bool need_yield = false; +#endif /* PIOS_INCLUDE_FREERTOS */ + + usb_rctx_dev->report.id = 3; /* FIXME: shouldn't hard-code this report ID */ + + UserToPMABufferCopy((uint8_t *) &usb_rctx_dev->report, + GetEPTxAddr(usb_rctx_dev->cfg->data_tx_ep), + sizeof(usb_rctx_dev->report)); + + SetEPTxCount(usb_rctx_dev->cfg->data_tx_ep, sizeof(usb_rctx_dev->report)); + SetEPTxValid(usb_rctx_dev->cfg->data_tx_ep); + +#if defined(PIOS_INCLUDE_FREERTOS) + if (need_yield) { + vPortYieldFromISR(); + } +#endif /* PIOS_INCLUDE_FREERTOS */ +} + +static void PIOS_USB_RCTX_EP_IN_Callback(void) +{ + struct pios_usb_rctx_dev * usb_rctx_dev = (struct pios_usb_rctx_dev *)pios_usb_rctx_id; + + bool valid = PIOS_USB_RCTX_validate(usb_rctx_dev); + PIOS_Assert(valid); + + if (!PIOS_USB_CheckAvailable(usb_rctx_dev->lower_id)) { + return; + } + + PIOS_USB_RCTX_SendReport(usb_rctx_dev); +} + +void PIOS_USB_RCTX_Update(uint32_t usbrctx_id, const uint16_t channel[], const int16_t channel_min[], const int16_t channel_max[], uint8_t num_channels) +{ + struct pios_usb_rctx_dev * usb_rctx_dev = (struct pios_usb_rctx_dev *)usbrctx_id; + + bool valid = PIOS_USB_RCTX_validate(usb_rctx_dev); + PIOS_Assert(valid); + + if (!PIOS_USB_CheckAvailable(usb_rctx_dev->lower_id)) { + return; + } + + for (uint8_t i = 0; + i < PIOS_USB_RCTX_NUM_CHANNELS && i < num_channels; + i++) { + int16_t min = channel_min[i]; + int16_t max = channel_max[i]; + uint16_t val = channel[i]; + + if (channel_min[i] > channel_max[i]) { + /* This channel is reversed, flip min and max */ + min = channel_max[i]; + max = channel_min[i]; + + /* and flip val to be an offset from the lower end of the range */ + val = channel_min[i] - channel[i] + channel_max[i]; + } + + /* Scale channel linearly between min and max */ + if (min == max) { + val = 0; + } else { + if (val < min) val = min; + if (val > max) val = max; + + val = (val - min) * (65535 / (max - min)); + } + + usb_rctx_dev->report.vals[i] = val; + } + + if (GetEPTxStatus(usb_rctx_dev->cfg->data_tx_ep) == EP_TX_VALID) { + /* Endpoint is already transmitting */ + return; + } + + PIOS_USB_RCTX_SendReport(usb_rctx_dev); +} + +#endif /* PIOS_INCLUDE_USB_RCTX */ diff --git a/flight/PiOS/inc/pios_usb_defs.h b/flight/PiOS/inc/pios_usb_defs.h index be827a494..ea54306a7 100644 --- a/flight/PiOS/inc/pios_usb_defs.h +++ b/flight/PiOS/inc/pios_usb_defs.h @@ -147,7 +147,7 @@ enum usb_ep_attr { #define HID_LOCAL_ITEM_0(tag) HID_SHORT_ITEM((tag), HID_ITEM_TYPE_LOCAL, HID_ITEM_SIZE_0) #define HID_LOCAL_ITEM_1(tag) HID_SHORT_ITEM((tag), HID_ITEM_TYPE_LOCAL, HID_ITEM_SIZE_1) #define HID_LOCAL_ITEM_2(tag) HID_SHORT_ITEM((tag), HID_ITEM_TYPE_LOCAL, HID_ITEM_SIZE_2) -#define HID_LOCAL_ITEM_3(tag) HID_SHORT_ITEM((tag), HID_ITEM_TYPE_LOCAL, HID_ITEM_SIZE_3) +#define HID_LOCAL_ITEM_4(tag) HID_SHORT_ITEM((tag), HID_ITEM_TYPE_LOCAL, HID_ITEM_SIZE_4) struct usb_device_desc { uint8_t bLength; diff --git a/flight/PiOS/inc/pios_usb_rctx.h b/flight/PiOS/inc/pios_usb_rctx.h new file mode 100644 index 000000000..ea07a6d7f --- /dev/null +++ b/flight/PiOS/inc/pios_usb_rctx.h @@ -0,0 +1,3 @@ +extern uint32_t pios_usb_rctx_id; + +extern void PIOS_USB_RCTX_Update(uint32_t usbrctx_id, const uint16_t channel[], const int16_t channel_min[], const int16_t channel_max[], uint8_t num_channels); diff --git a/flight/PiOS/inc/pios_usb_rctx_priv.h b/flight/PiOS/inc/pios_usb_rctx_priv.h new file mode 100644 index 000000000..cf9aab2b6 --- /dev/null +++ b/flight/PiOS/inc/pios_usb_rctx_priv.h @@ -0,0 +1,48 @@ +/** + ****************************************************************************** + * @addtogroup PIOS PIOS Core hardware abstraction layer + * @{ + * @addtogroup PIOS_USB USB HID RC Transmitter/Joystick + * @brief Hardware communication layer + * @{ + * + * @file pios_usb_rctx_priv.h + * @author The OpenPilot Team, http://www.openpilot.org Copyright (C) 2012. + * @brief USB COM HID private definitions. + * @see The GNU Public License (GPL) Version 3 + * + *****************************************************************************/ +/* + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * 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., + * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#ifndef PIOS_USB_RCTX_PRIV_H +#define PIOS_USB_RCTX_PRIV_H + +#include "pios_usb_rctx.h" + +struct pios_usb_rctx_cfg { + uint8_t data_if; + uint8_t data_tx_ep; +}; + +extern int32_t PIOS_USB_RCTX_Init(uint32_t * usbrctx_id, const struct pios_usb_rctx_cfg * cfg, uint32_t lower_id); + +#endif /* PIOS_USB_RCTX_PRIV_H */ + +/** + * @} + * @} + */ diff --git a/flight/board_hw_defs/coptercontrol/board_hw_defs.c b/flight/board_hw_defs/coptercontrol/board_hw_defs.c index 328fd44ce..cc6ec4552 100644 --- a/flight/board_hw_defs/coptercontrol/board_hw_defs.c +++ b/flight/board_hw_defs/coptercontrol/board_hw_defs.c @@ -1284,6 +1284,16 @@ const struct pios_usb_hid_cfg pios_usb_hid_cfg = { #endif /* PIOS_INCLUDE_USB_HID */ +#if defined(PIOS_INCLUDE_USB_RCTX) +#include + +const struct pios_usb_rctx_cfg pios_usb_rctx_cfg = { + .data_if = 2, + .data_tx_ep = 1, +}; + +#endif /* PIOS_INCLUDE_USB_RCTX */ + #if defined(PIOS_INCLUDE_USB_CDC) #include diff --git a/ground/openpilotgcs/src/plugins/coreplugin/OpenPilotGCS.xml b/ground/openpilotgcs/src/plugins/coreplugin/OpenPilotGCS.xml index d61dba982..af2072fe9 100644 --- a/ground/openpilotgcs/src/plugins/coreplugin/OpenPilotGCS.xml +++ b/ground/openpilotgcs/src/plugins/coreplugin/OpenPilotGCS.xml @@ -2080,7 +2080,7 @@ 4294901760 None - X + x Magnetometer 0 1 @@ -2090,7 +2090,7 @@ 4283782655 None - Y + y Magnetometer 0 1 @@ -2100,7 +2100,7 @@ 4283804160 None - Z + z Magnetometer 0 1 diff --git a/make/firmware-defs.mk b/make/firmware-defs.mk index 3ead53444..522d2e320 100644 --- a/make/firmware-defs.mk +++ b/make/firmware-defs.mk @@ -210,7 +210,8 @@ endef # $(1) = Name of binary image to write # $(2) = Base of flash region to write/wipe # $(3) = Size of flash region to write/wipe -# $(4) = OpenOCD configuration file to use +# $(4) = OpenOCD JTAG interface configuration file to use +# $(5) = OpenOCD configuration file to use define JTAG_TEMPLATE # --------------------------------------------------------------------------- # Options for OpenOCD flash-programming @@ -223,7 +224,7 @@ OOCD_EXE ?= openocd OOCD_JTAG_SETUP = -d0 # interface and board/target settings (using the OOCD target-library here) OOCD_JTAG_SETUP += -s $(TOP)/flight/Project/OpenOCD -OOCD_JTAG_SETUP += -f foss-jtag.revb.cfg -f $(4) +OOCD_JTAG_SETUP += -f $(4) -f $(5) # initialize OOCD_BOARD_RESET = -c init diff --git a/shared/uavobjectdefinition/hwsettings.xml b/shared/uavobjectdefinition/hwsettings.xml index 0cee2a6d9..00c904dd4 100644 --- a/shared/uavobjectdefinition/hwsettings.xml +++ b/shared/uavobjectdefinition/hwsettings.xml @@ -15,7 +15,7 @@ - +