diff --git a/radio/src/CMakeLists.txt b/radio/src/CMakeLists.txt index b620c7f86..713655f44 100644 --- a/radio/src/CMakeLists.txt +++ b/radio/src/CMakeLists.txt @@ -47,6 +47,7 @@ option(FRSKY_STICKS "Reverse sticks for FrSky sticks" OFF) option(NANO "Use nano newlib and binalloc") option(NIGHTLY_BUILD_WARNING "Warn this is a nightly build" OFF) option(MODULE_R9M_FLEX_FW "Add R9M options for non certified firmwwares" OFF) +option(PXX2 "Enable PXX v2 support" OFF) # since we reset all default CMAKE compiler flags for firmware builds, provide an alternate way for user to specify additional flags. set(FIRMWARE_C_FLAGS "" CACHE STRING "Additional flags for firmware target c compiler (note: all CMAKE_C_FLAGS[_*] are ignored for firmware/bootloader).") @@ -313,7 +314,11 @@ if(NIGHTLY_BUILD_WARNING) endif(NIGHTLY_BUILD_WARNING) if(MODULE_R9M_FLEX_FW) - add_definitions(-DMODULE_R9M_FLEX_FW) + add_definitions(-DMODULE_R9M_FLEX_FW) +endif() + +if(PXX2) + add_definitions(-DPXX2) endif() set(SRC diff --git a/radio/src/io/pxx2.h b/radio/src/io/pxx2.h new file mode 100644 index 000000000..3a1506a67 --- /dev/null +++ b/radio/src/io/pxx2.h @@ -0,0 +1,27 @@ +/* + * Copyright (C) OpenTX + * + * Based on code named + * th9x - http://code.google.com/p/th9x + * er9x - http://code.google.com/p/er9x + * gruvin9x - http://code.google.com/p/gruvin9x + * + * License GPLv2: http://www.gnu.org/licenses/gpl-2.0.html + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 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. + */ + +#ifndef _IO_PXX2_H_ +#define _IO_PXX2_H_ + +#define PXX2_BAUDRATE 230400 +#define PXX2_PERIOD 4 // 4ms + +#endif diff --git a/radio/src/myeeprom.h b/radio/src/myeeprom.h index c8a2d9af0..30767b1cb 100644 --- a/radio/src/myeeprom.h +++ b/radio/src/myeeprom.h @@ -342,10 +342,16 @@ enum Protocols { #endif PROTO_MULTIMODULE, PROTO_SBUS, - PROTO_MAX, + PROTO_PXX2, PROTO_NONE }; +#if defined(PXX2) +#define PROTO_PXX_EXTERNAL_MODULE PROTO_PXX2 +#else +#define PROTO_PXX_EXTERNAL_MODULE PROTO_PXX +#endif + enum XJTRFProtocols { RF_PROTO_OFF = -1, RF_PROTO_X16, diff --git a/radio/src/pulses/pulses_arm.cpp b/radio/src/pulses/pulses_arm.cpp index f9e613682..14543ebd7 100755 --- a/radio/src/pulses/pulses_arm.cpp +++ b/radio/src/pulses/pulses_arm.cpp @@ -20,6 +20,11 @@ #include "opentx.h" +#if defined(PXX2) +#include "io/pxx2.h" +#include "pulses/pxx2.h" +#endif + uint8_t s_pulses_paused = 0; uint8_t s_current_protocol[NUM_MODULES] = { MODULES_INIT(255) }; uint16_t failsafeCounter[NUM_MODULES] = { MODULES_INIT(100) }; @@ -56,14 +61,13 @@ uint8_t getRequiredProtocol(uint8_t port) #endif default: - port = EXTERNAL_MODULE; // ensure it's external module only switch (g_model.moduleData[EXTERNAL_MODULE].type) { case MODULE_TYPE_PPM: required_protocol = PROTO_PPM; break; case MODULE_TYPE_XJT: case MODULE_TYPE_R9M: - required_protocol = PROTO_PXX; + required_protocol = PROTO_PXX_EXTERNAL_MODULE; // either PXX or PXX2 depending on compilation options break; case MODULE_TYPE_SBUS: required_protocol = PROTO_SBUS; @@ -146,6 +150,12 @@ void setupPulses(uint8_t port) break; #endif +#if defined(PXX2) + case PROTO_PXX2: + disable_pxx2(port); + break; +#endif + #if defined(MULTIMODULE) case PROTO_MULTIMODULE: #endif @@ -208,6 +218,16 @@ void setupPulses(uint8_t port) break; #endif +#if defined(PXX2) + case PROTO_PXX2: + if (telemetryProtocol == PROTOCOL_FRSKY_SPORT && !init_needed) { + createPXX2ChannelsFrame(port); + sportSendBuffer(modulePulsesData[port].pxx_uart.pulses, modulePulsesData[port].pxx_uart.ptr - modulePulsesData[port].pxx_uart.pulses); + } + scheduleNextMixerCalculation(port, PXX2_PERIOD); + break; +#endif + #if defined(MULTIMODULE) case PROTO_MULTIMODULE: setupPulsesMultimodule(port); diff --git a/radio/src/pulses/pulses_arm.h b/radio/src/pulses/pulses_arm.h index e726f8e63..92841f311 100644 --- a/radio/src/pulses/pulses_arm.h +++ b/radio/src/pulses/pulses_arm.h @@ -86,7 +86,7 @@ PACK(struct Dsm2TimerPulsesData { }); #endif -#if defined(INTMODULE_USART) || defined(EXTMODULE_USART) +#if defined(INTMODULE_USART) || defined(EXTMODULE_USART) || defined(PXX2) PACK(struct PxxUartPulsesData { uint8_t pulses[64]; uint8_t * ptr; @@ -131,7 +131,7 @@ PACK(struct CrossfirePulsesData { }); union ModulePulsesData { -#if defined(INTMODULE_USART) || defined(EXTMODULE_USART) +#if defined(INTMODULE_USART) || defined(EXTMODULE_USART) || defined(PXX2) PxxUartPulsesData pxx_uart; #endif #if defined(PPM_PIN_SERIAL) diff --git a/radio/src/pulses/pxx.h b/radio/src/pulses/pxx.h new file mode 100644 index 000000000..15ac96784 --- /dev/null +++ b/radio/src/pulses/pxx.h @@ -0,0 +1,23 @@ +/* + * Copyright (C) OpenTX + * + * Based on code named + * th9x - http://code.google.com/p/th9x + * er9x - http://code.google.com/p/er9x + * gruvin9x - http://code.google.com/p/gruvin9x + * + * License GPLv2: http://www.gnu.org/licenses/gpl-2.0.html + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 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. + */ + +#define PXX_SEND_BIND 0x01 +#define PXX_SEND_FAILSAFE (1 << 4) +#define PXX_SEND_RANGECHECK (1 << 5) diff --git a/radio/src/pulses/pxx2.cpp b/radio/src/pulses/pxx2.cpp new file mode 100644 index 000000000..93df83ec1 --- /dev/null +++ b/radio/src/pulses/pxx2.cpp @@ -0,0 +1,184 @@ +/* + * Copyright (C) OpenTX + * + * Based on code named + * th9x - http://code.google.com/p/th9x + * er9x - http://code.google.com/p/er9x + * gruvin9x - http://code.google.com/p/gruvin9x + * + * License GPLv2: http://www.gnu.org/licenses/gpl-2.0.html + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 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. + */ + +#include "opentx.h" +#include "pulses/pxx.h" + +void uartInitPcmArray(uint8_t port) +{ + modulePulsesData[port].pxx_uart.pcmCrc = 0; + modulePulsesData[port].pxx_uart.ptr = modulePulsesData[port].pxx_uart.pulses; +} + +void putPXX2Byte(uint8_t port, uint8_t byte) +{ + modulePulsesData[port].pxx_uart.pcmCrc = (modulePulsesData[port].pxx_uart.pcmCrc<<8) ^ (CRCTable[((modulePulsesData[port].pxx_uart.pcmCrc>>8) ^ byte) & 0xFF]); + *modulePulsesData[port].pxx_uart.ptr++ = byte; +} + +void createPXX2ChannelsFrame(uint8_t port) +{ + uint16_t pulseValue=0, pulseValueLow=0; + + static uint8_t pass[NUM_MODULES] = { MODULES_INIT(0) }; + uint8_t sendUpperChannels = 0; + if (pass[port]++ & 0x01) { + sendUpperChannels = g_model.moduleData[port].channelsCount; + } + + uartInitPcmArray(port); + + // Model ID + putPXX2Byte(port, g_model.header.modelId[port]); + + // Flag1 + uint8_t flag1 = (g_model.moduleData[port].rfProtocol << 6); + if (moduleFlag[port] == MODULE_BIND) { + flag1 |= (g_eeGeneral.countryCode << 1) | PXX_SEND_BIND; + } + else if (moduleFlag[port] == MODULE_RANGECHECK) { + flag1 |= PXX_SEND_RANGECHECK; + } + else if (g_model.moduleData[port].failsafeMode != FAILSAFE_NOT_SET && g_model.moduleData[port].failsafeMode != FAILSAFE_RECEIVER) { + if (failsafeCounter[port]-- == 0) { + failsafeCounter[port] = 1000; + flag1 |= PXX_SEND_FAILSAFE; + } + if (failsafeCounter[port] == 0 && g_model.moduleData[port].channelsCount > 0) { + flag1 |= PXX_SEND_FAILSAFE; + } + } + putPXX2Byte(port, flag1); + + // Flag2 + putPXX2Byte(port, 0); + + // Channels + for (int i=0; i<8; i++) { + if (flag1 & PXX_SEND_FAILSAFE) { + if (g_model.moduleData[port].failsafeMode == FAILSAFE_HOLD) { + pulseValue = (i < sendUpperChannels ? 4095 : 2047); + } + else if (g_model.moduleData[port].failsafeMode == FAILSAFE_NOPULSES) { + pulseValue = (i < sendUpperChannels ? 2048 : 0); + } + else { + if (i < sendUpperChannels) { + int16_t failsafeValue = g_model.moduleData[port].failsafeChannels[8+i]; + if (failsafeValue == FAILSAFE_CHANNEL_HOLD) { + pulseValue = 4095; + } + else if (failsafeValue == FAILSAFE_CHANNEL_NOPULSE) { + pulseValue = 2048; + } + else { + failsafeValue += 2*PPM_CH_CENTER(8+g_model.moduleData[port].channelsStart+i) - 2*PPM_CENTER; + pulseValue = limit(2049, (failsafeValue * 512 / 682) + 3072, 4094); + } + } + else { + int16_t failsafeValue = g_model.moduleData[port].failsafeChannels[i]; + if (failsafeValue == FAILSAFE_CHANNEL_HOLD) { + pulseValue = 2047; + } + else if (failsafeValue == FAILSAFE_CHANNEL_NOPULSE) { + pulseValue = 0; + } + else { + failsafeValue += 2*PPM_CH_CENTER(g_model.moduleData[port].channelsStart+i) - 2*PPM_CENTER; + pulseValue = limit(1, (failsafeValue * 512 / 682) + 1024, 2046); + } + } + } + } + else { + if (i < sendUpperChannels) { + int channel = 8 + g_model.moduleData[port].channelsStart + i; + int value = channelOutputs[channel] + 2*PPM_CH_CENTER(channel) - 2*PPM_CENTER; + pulseValue = limit(2049, (value * 512 / 682) + 3072, 4094); + } + else if (i < sentModuleChannels(port)) { + int channel = g_model.moduleData[port].channelsStart + i; + int value = channelOutputs[channel] + 2*PPM_CH_CENTER(channel) - 2*PPM_CENTER; + pulseValue = limit(1, (value * 512 / 682) + 1024, 2046); + } + else { + pulseValue = 1024; + } + } + + if (i & 1) { + putPXX2Byte(port, pulseValueLow); // Low byte of channel + putPXX2Byte(port, ((pulseValueLow >> 8) & 0x0F) | (pulseValue << 4)); // 4 bits each from 2 channels + putPXX2Byte(port, pulseValue >> 4); // High byte of channel + } + else { + pulseValueLow = pulseValue; + } + } + + // Ext. flag (holds antenna selection on Horus internal module, 0x00 otherwise) + uint8_t extra_flags = 0; +#if defined(PCBHORUS) || defined(PCBXLITE) + if (port == INTERNAL_MODULE) { + extra_flags |= (g_model.moduleData[port].pxx.external_antenna << 0); + } +#endif + extra_flags |= (g_model.moduleData[port].pxx.receiver_telem_off << 1); + extra_flags |= (g_model.moduleData[port].pxx.receiver_channel_9_16 << 2); + if (isModuleR9M(port)) { + extra_flags |= (min(g_model.moduleData[port].pxx.power, isModuleR9M_FCC_VARIANT(port) ? (uint8_t)R9M_FCC_POWER_MAX : (uint8_t)R9M_LBT_POWER_MAX) << 3); + if (isModuleR9M_EUPLUS(port)) { + extra_flags |= (1 << 6); + } + } + // Disable S.PORT if internal module is active + if (IS_TELEMETRY_INTERNAL_MODULE()) { + extra_flags |= (1 << 5); + } + putPXX2Byte(port, extra_flags); + + // Flag3, reserved + putPXX2Byte(port, 0); + +#if defined(LUA) + if (outputTelemetryBufferTrigger != 0x00 && outputTelemetryBufferSize > 0) { + // CMD/Resp + putPXX2Byte(port, 1); + // primID (1 byte) + dataID (2 bytes) + value (4 bytes) + for (uint8_t i=0; i<7; i++) { + putPXX2Byte(port, outputTelemetryBuffer[i]); + } + outputTelemetryBufferTrigger = 0x00; + outputTelemetryBufferSize = 0; + } + else { + // CMD/Resp + putPXX2Byte(port, 0); + } +#else + // CMD/Resp + putPXX2Byte(port, 0); +#endif + + // CRC + putPXX2Byte(port, modulePulsesData[port].pxx_uart.pcmCrc); // TODO need to check the CRC chosen algorithm +} + diff --git a/radio/src/pulses/pxx2.h b/radio/src/pulses/pxx2.h new file mode 100644 index 000000000..fc5f8116a --- /dev/null +++ b/radio/src/pulses/pxx2.h @@ -0,0 +1,21 @@ +/* + * Copyright (C) OpenTX + * + * Based on code named + * th9x - http://code.google.com/p/th9x + * er9x - http://code.google.com/p/er9x + * gruvin9x - http://code.google.com/p/gruvin9x + * + * License GPLv2: http://www.gnu.org/licenses/gpl-2.0.html + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 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. + */ + +void createPXX2ChannelsFrame(uint8_t port); diff --git a/radio/src/pulses/pxx_arm.cpp b/radio/src/pulses/pxx_arm.cpp index 1418df9a4..8d19a1642 100644 --- a/radio/src/pulses/pxx_arm.cpp +++ b/radio/src/pulses/pxx_arm.cpp @@ -19,10 +19,7 @@ */ #include "opentx.h" - -#define PXX_SEND_BIND 0x01 -#define PXX_SEND_FAILSAFE (1 << 4) -#define PXX_SEND_RANGECHECK (1 << 5) +#include "pulses/pxx.h" const uint16_t CRCTable[]= { diff --git a/radio/src/targets/common/arm/stm32/CMakeLists.txt b/radio/src/targets/common/arm/stm32/CMakeLists.txt index ad2dca3d2..feedb073f 100644 --- a/radio/src/targets/common/arm/stm32/CMakeLists.txt +++ b/radio/src/targets/common/arm/stm32/CMakeLists.txt @@ -9,6 +9,7 @@ option(USB_SERIAL "Enable USB serial (CDC)" OFF) set(ARCH ARM) set(STM32USB_DIR ${THIRDPARTY_DIR}/STM32_USB-Host-Device_Lib_V2.2.0/Libraries) add_definitions(-DSTM32 -DLUA_INPUTS -DVARIO -DCROSSFIRE) + include_directories(${RADIO_SRC_DIRECTORY}/targets/common/arm/stm32) include_directories(${STM32USB_DIR}/STM32_USB_OTG_Driver/inc) include_directories(${STM32USB_DIR}/STM32_USB_Device_Library/Core/inc) @@ -90,12 +91,21 @@ foreach(FILE ${STM32USB_SRC}) ${STM32USB_DIR}/${FILE} ) endforeach() + set(SRC ${SRC} telemetry/crossfire.cpp io/bootloader_flash.cpp ) + set(PULSES_SRC ${PULSES_SRC} crossfire.cpp ) + +if(PXX2) + set(PULSES_SRC + ${PULSES_SRC} + pxx2.cpp + ) +endif() diff --git a/radio/src/targets/taranis/board.h b/radio/src/targets/taranis/board.h index 87fabf59b..235afc1d4 100644 --- a/radio/src/targets/taranis/board.h +++ b/radio/src/targets/taranis/board.h @@ -182,14 +182,16 @@ uint32_t isBootloaderStart(const uint8_t * buffer); #endif void init_no_pulses(uint32_t port); void disable_no_pulses(uint32_t port); -void init_ppm( uint32_t module_index ); -void disable_ppm( uint32_t module_index ); -void init_pxx( uint32_t module_index ); -void disable_pxx( uint32_t module_index ); -void init_serial( uint32_t module_index, uint32_t baudrate, uint32_t period ); -void disable_serial( uint32_t module_index ); -void init_crossfire( uint32_t module_index ); -void disable_crossfire( uint32_t module_index ); +void init_ppm( uint32_t module_index); +void disable_ppm( uint32_t module_index); +void init_pxx( uint32_t module_index); +void disable_pxx( uint32_t module_index); +void init_serial( uint32_t module_index, uint32_t baudrate, uint32_t period); +void disable_serial( uint32_t module_index); +void init_crossfire( uint32_t module_index); +void disable_crossfire( uint32_t module_index); +void init_pxx2( uint32_t module_index); +void disable_pxx2( uint32_t module_index); // Trainer driver #define SLAVE_MODE() (g_model.trainerMode == TRAINER_MODE_SLAVE) diff --git a/radio/src/targets/taranis/pulses_driver.cpp b/radio/src/targets/taranis/pulses_driver.cpp index f70a241ed..ad7931bc1 100644 --- a/radio/src/targets/taranis/pulses_driver.cpp +++ b/radio/src/targets/taranis/pulses_driver.cpp @@ -120,3 +120,12 @@ void disable_crossfire(uint32_t port) extmoduleStop(); } } + +void disable_pxx2(uint32_t port) +{ + if (port == EXTERNAL_MODULE) { + extmoduleStop(); + } +} + +