chore: New module/serial API (#3055)
* WiP: new module interface * WiP: softserial timer DMA * WiP: setup() and send() in one step and remove methods specific to internal or external module * fixes and extra port config * horus targets * module ports have moved * T8 support (only internal module) * fix nv14 * stupid GCC.... * disable BT * remove nv14 telem driver * remove old aux serial driver * Move module buffers out of pulses.h * Soft serial fixes and PXX1 support * Fixed PPM module output * Fixed CLI * compilation fixes * Ported bluetooth and flysky gimbal to new serial driver * Re-enable soft-serial RX * fixes and cleanup * nv14 fixes * Fix flysky gimbals * Ghost module * 16 bytes of RAM * Remove getByte from module API * Ghost cleanup * fix: _half_duplex_input() must be called after usart init * WiP * more cleanup * Remove board specific settings from PXX1 / PXX2 drivers * A bit of PXX2 header cleanup * Add support for OVER8 and ONEBIT * Add some support for inverters (NV14 / EL18) * Support NV14 power fixes in generic board * Fix polarity handling * Fix RX DMA * Fix mixer not running * Add soft-serial polarity control * Quick fix for telemetry polling while module port is changed * Fix bootloader DEBUG * Fix MPM telemetry * Fix pulse start * DSM2 soft-serial fallback * DSMP async module restart * Fix CRSF LUA upstream * Remove global variable telemetryProtocol * Use HAL/LL constants * Fix various issue with AFHDS3 and NV14/EL18 internal module * Fixed S.PORT unit tests * fix: update widx after writing at current index * fix: init error condition * Fix: possible infinite loop in _find_port() * Add support for bidir S.PORT init * Add support for "getLastByte" in HW serial driver * PXX1: Add support for S.PORT based on IDLE callback * Fix rebase error * Fix softserial _rx_fifo_push() * Add softserial support for waitForTxCompleted() * Fix MPM flashing (internal / external) * Fixed external MPM * Activate RX DMA on internal module Seems to work with serial passthrough without issues, at least on the L3R PRO. * Fix softserial DMA on stdlcd F4 targets * Add softserial support for 32bit timers (mostly TIM2) * More precise soft-serial * Fix MPM update using inverters & HW uart * Raise S.PORT DMA/USART IRQ prio to achieve faster switching to input * Remove useless delays * Fix flashing external ELRS module * Added a retry mechanism for FrSky S.PORT update * Remove useless enableRx() * External FrSky modules are all flashed via S.PORT * Remove power management unit update This was only used on the first batch of X-lite.
This commit is contained in:
@@ -472,6 +472,8 @@ set(SRC
|
||||
model_init.cpp
|
||||
serial.cpp
|
||||
sbus.cpp
|
||||
hal/module_port.cpp
|
||||
tasks/mixer_task.cpp
|
||||
)
|
||||
|
||||
if(GUI)
|
||||
|
||||
+19
-29
@@ -22,6 +22,7 @@
|
||||
#include <stdio.h>
|
||||
#include "opentx.h"
|
||||
#include "io/frsky_firmware_update.h"
|
||||
#include "bluetooth_driver.h"
|
||||
|
||||
#if defined(LIBOPENUI)
|
||||
#include "libopenui.h"
|
||||
@@ -49,37 +50,25 @@ extern FIL g_bluetoothFile;
|
||||
#define SWAP32(val) (__builtin_bswap32(val))
|
||||
#endif
|
||||
|
||||
extern Fifo<uint8_t, BT_TX_FIFO_SIZE> btTxFifo;
|
||||
extern Fifo<uint8_t, BT_RX_FIFO_SIZE> btRxFifo;
|
||||
|
||||
Bluetooth bluetooth;
|
||||
|
||||
void Bluetooth::write(const uint8_t * data, uint8_t length)
|
||||
{
|
||||
if (btTxFifo.hasSpace(length)) {
|
||||
BLUETOOTH_TRACE_VERBOSE("BT>");
|
||||
for (int i = 0; i < length; i++) {
|
||||
BLUETOOTH_TRACE_VERBOSE(" %02X", data[i]);
|
||||
btTxFifo.push(data[i]);
|
||||
}
|
||||
BLUETOOTH_TRACE_VERBOSE(CRLF);
|
||||
BLUETOOTH_TRACE_VERBOSE("BT>");
|
||||
for (int i = 0; i < length; i++) {
|
||||
BLUETOOTH_TRACE_VERBOSE(" %02X", data[i]);
|
||||
}
|
||||
else {
|
||||
BLUETOOTH_TRACE("[BT] TX fifo full!" CRLF);
|
||||
}
|
||||
|
||||
bluetoothWriteWakeup();
|
||||
BLUETOOTH_TRACE_VERBOSE(CRLF);
|
||||
bluetoothWrite(data, length);
|
||||
}
|
||||
|
||||
static const char _bt_crlf[] = "\r\n";
|
||||
|
||||
void Bluetooth::writeString(const char * str)
|
||||
{
|
||||
BLUETOOTH_TRACE("BT> %s" CRLF, str);
|
||||
while (*str != 0) {
|
||||
btTxFifo.push(*str++);
|
||||
}
|
||||
btTxFifo.push('\r');
|
||||
btTxFifo.push('\n');
|
||||
bluetoothWriteWakeup();
|
||||
bluetoothWrite(str, strlen(str));
|
||||
bluetoothWrite(_bt_crlf, sizeof(_bt_crlf) - 1);
|
||||
}
|
||||
|
||||
char * Bluetooth::readline(bool error_reset)
|
||||
@@ -87,7 +76,7 @@ char * Bluetooth::readline(bool error_reset)
|
||||
uint8_t byte;
|
||||
|
||||
while (true) {
|
||||
if (!btRxFifo.pop(byte)) {
|
||||
if (!bluetoothRead(&byte)) {
|
||||
#if defined(PCBX9E)
|
||||
// X9E BT module can get unresponsive
|
||||
BLUETOOTH_TRACE("NO RESPONSE FROM BT MODULE" CRLF);
|
||||
@@ -327,7 +316,7 @@ void Bluetooth::receiveTrainer()
|
||||
uint8_t byte;
|
||||
|
||||
while (true) {
|
||||
if (!btRxFifo.pop(byte)) {
|
||||
if (!bluetoothRead(&byte)) {
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -398,7 +387,6 @@ void Bluetooth::wakeup(void)
|
||||
}
|
||||
else if (IS_BLUETOOTH_TRAINER()){
|
||||
state = BLUETOOTH_STATE_CONNECTED;
|
||||
bluetoothWriteWakeup();
|
||||
sendTrainer();
|
||||
}
|
||||
}
|
||||
@@ -408,7 +396,6 @@ void Bluetooth::wakeup(void)
|
||||
void Bluetooth::wakeup()
|
||||
{
|
||||
if (state != BLUETOOTH_STATE_OFF) {
|
||||
bluetoothWriteWakeup();
|
||||
if (bluetoothIsWriting()) {
|
||||
return;
|
||||
}
|
||||
@@ -425,7 +412,10 @@ void Bluetooth::wakeup()
|
||||
return;
|
||||
}
|
||||
|
||||
if (g_eeGeneral.bluetoothMode == BLUETOOTH_OFF || (g_eeGeneral.bluetoothMode == BLUETOOTH_TRAINER && !IS_BLUETOOTH_TRAINER())) {
|
||||
if (g_eeGeneral.bluetoothMode == BLUETOOTH_OFF ||
|
||||
(g_eeGeneral.bluetoothMode == BLUETOOTH_TRAINER &&
|
||||
!IS_BLUETOOTH_TRAINER())) {
|
||||
|
||||
if (state != BLUETOOTH_STATE_OFF) {
|
||||
bluetoothDisable();
|
||||
state = BLUETOOTH_STATE_OFF;
|
||||
@@ -567,7 +557,7 @@ uint8_t Bluetooth::read(uint8_t * data, uint8_t size, uint32_t timeout)
|
||||
while (len < size) {
|
||||
uint32_t elapsed = 0;
|
||||
uint8_t byte;
|
||||
while (!btRxFifo.pop(byte)) {
|
||||
while (!bluetoothRead(&byte)) {
|
||||
if (elapsed++ >= timeout) {
|
||||
return len;
|
||||
}
|
||||
@@ -825,7 +815,7 @@ const char * Bluetooth::flashFirmware(const char * filename, ProgressHandler pro
|
||||
|
||||
state = BLUETOOTH_STATE_FLASH_FIRMWARE;
|
||||
|
||||
pausePulses();
|
||||
pulsesStop();
|
||||
|
||||
bluetoothInit(BLUETOOTH_BOOTLOADER_BAUDRATE, true); // normal mode
|
||||
watchdogSuspend(500 /*5s*/);
|
||||
@@ -854,7 +844,7 @@ const char * Bluetooth::flashFirmware(const char * filename, ProgressHandler pro
|
||||
RTOS_WAIT_MS(1000);
|
||||
|
||||
state = BLUETOOTH_STATE_OFF;
|
||||
resumePulses();
|
||||
pulsesStart();
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
@@ -0,0 +1,148 @@
|
||||
/*
|
||||
* Copyright (C) EdgeTX
|
||||
*
|
||||
* Based on code named
|
||||
* opentx - https://github.com/opentx/opentx
|
||||
* 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 "hal/serial_port.h"
|
||||
#include "stm32_serial_driver.h"
|
||||
|
||||
#include "board.h"
|
||||
#include "dataconstants.h"
|
||||
|
||||
#define AUX_SERIAL_TX_BUFFER 512
|
||||
#if defined(SDRAM)
|
||||
#define AUX_SERIAL_RX_BUFFER 512
|
||||
#else
|
||||
#define AUX_SERIAL_RX_BUFFER 32
|
||||
#endif
|
||||
|
||||
#if defined(AUX_SERIAL_PWR_GPIO) || defined(AUX2_SERIAL_PWR_GPIO)
|
||||
static void _aux_pwr(GPIO_TypeDef *GPIOx, uint32_t pin, uint8_t on)
|
||||
{
|
||||
LL_GPIO_InitTypeDef pinInit;
|
||||
LL_GPIO_StructInit(&pinInit);
|
||||
pinInit.Pin = pin;
|
||||
pinInit.Mode = LL_GPIO_MODE_OUTPUT;
|
||||
pinInit.Pull = LL_GPIO_PULL_UP;
|
||||
LL_GPIO_Init(GPIOx, &pinInit);
|
||||
|
||||
if (on) {
|
||||
LL_GPIO_SetOutputPin(GPIOx, pin);
|
||||
} else {
|
||||
LL_GPIO_ResetOutputPin(GPIOx, pin);
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
#if defined(AUX_SERIAL)
|
||||
|
||||
#if !defined(AUX_SERIAL_DMA_TX)
|
||||
#define AUX_SERIAL_DMA_TX nullptr
|
||||
#define AUX_SERIAL_DMA_Stream_TX nullptr
|
||||
#define AUX_SERIAL_DMA_Stream_TX_LL 0
|
||||
#define AUX_SERIAL_DMA_Channel_TX 0
|
||||
#endif
|
||||
|
||||
static const stm32_usart_t auxUSART = {
|
||||
.USARTx = AUX_SERIAL_USART,
|
||||
.GPIOx = AUX_SERIAL_GPIO,
|
||||
.GPIO_Pin = AUX_SERIAL_GPIO_PIN_TX | AUX_SERIAL_GPIO_PIN_RX,
|
||||
.IRQn = AUX_SERIAL_USART_IRQn,
|
||||
.IRQ_Prio = 7, // TODO: define constant
|
||||
.txDMA = AUX_SERIAL_DMA_TX,
|
||||
.txDMA_Stream = AUX_SERIAL_DMA_Stream_TX_LL,
|
||||
.txDMA_Channel = AUX_SERIAL_DMA_Channel_TX,
|
||||
.rxDMA = AUX_SERIAL_DMA_RX,
|
||||
.rxDMA_Stream = AUX_SERIAL_DMA_Stream_RX_LL,
|
||||
.rxDMA_Channel = AUX_SERIAL_DMA_Channel_RX,
|
||||
};
|
||||
|
||||
DEFINE_STM32_SERIAL_PORT(Aux, auxUSART, AUX_SERIAL_RX_BUFFER, AUX_SERIAL_TX_BUFFER);
|
||||
|
||||
#if defined(AUX_SERIAL_PWR_GPIO)
|
||||
void set_aux_pwr(uint8_t on)
|
||||
{
|
||||
_aux_pwr(AUX_SERIAL_PWR_GPIO, AUX_SERIAL_PWR_GPIO_PIN, on);
|
||||
}
|
||||
#define AUX_PWR_FCT set_aux_pwr
|
||||
#else
|
||||
#define AUX_PWR_FCT nullptr
|
||||
#endif
|
||||
|
||||
const etx_serial_port_t auxSerialPort = {
|
||||
.name = "AUX1",
|
||||
.uart = &STM32SerialDriver,
|
||||
.hw_def = REF_STM32_SERIAL_PORT(Aux),
|
||||
AUX_PWR_FCT
|
||||
};
|
||||
#define AUX_SERIAL_PORT &auxSerialPort
|
||||
#else
|
||||
#define AUX_SERIAL_PORT nullptr
|
||||
#endif
|
||||
|
||||
#if defined(AUX2_SERIAL)
|
||||
|
||||
static const stm32_usart_t aux2USART = {
|
||||
.USARTx = AUX2_SERIAL_USART,
|
||||
.GPIOx = AUX2_SERIAL_GPIO,
|
||||
.GPIO_Pin = AUX2_SERIAL_GPIO_PIN_TX | AUX2_SERIAL_GPIO_PIN_RX,
|
||||
.IRQn = AUX2_SERIAL_USART_IRQn,
|
||||
.IRQ_Prio = 7, // TODO: define constant
|
||||
.txDMA = nullptr,
|
||||
.txDMA_Stream = 0,
|
||||
.txDMA_Channel = 0,
|
||||
.rxDMA = AUX2_SERIAL_DMA_RX,
|
||||
.rxDMA_Stream = AUX2_SERIAL_DMA_Stream_RX_LL,
|
||||
.rxDMA_Channel = AUX2_SERIAL_DMA_Channel_RX,
|
||||
};
|
||||
|
||||
DEFINE_STM32_SERIAL_PORT(Aux2, aux2USART, AUX_SERIAL_RX_BUFFER, AUX_SERIAL_TX_BUFFER);
|
||||
|
||||
#if defined(AUX2_SERIAL_PWR_GPIO)
|
||||
void set_aux2_pwr(uint8_t on)
|
||||
{
|
||||
_aux_pwr(AUX2_SERIAL_PWR_GPIO, AUX2_SERIAL_PWR_GPIO_PIN, on);
|
||||
}
|
||||
#define AUX2_PWR_FCT set_aux2_pwr
|
||||
#else
|
||||
#define AUX2_PWR_FCT nullptr
|
||||
#endif
|
||||
|
||||
const etx_serial_port_t aux2SerialPort = {
|
||||
.name = "AUX2",
|
||||
.uart = &STM32SerialDriver,
|
||||
.hw_def = REF_STM32_SERIAL_PORT(Aux2),
|
||||
AUX2_PWR_FCT
|
||||
};
|
||||
#define AUX2_SERIAL_PORT &aux2SerialPort
|
||||
#else
|
||||
#define AUX2_SERIAL_PORT nullptr
|
||||
#endif
|
||||
|
||||
static const etx_serial_port_t* serialPorts[MAX_AUX_SERIAL] = {
|
||||
AUX_SERIAL_PORT,
|
||||
AUX2_SERIAL_PORT,
|
||||
};
|
||||
|
||||
const etx_serial_port_t* auxSerialGetPort(int port_nr)
|
||||
{
|
||||
if (port_nr >= MAX_AUX_SERIAL) return nullptr;
|
||||
return serialPorts[port_nr];
|
||||
}
|
||||
|
||||
@@ -0,0 +1,63 @@
|
||||
/*
|
||||
* Copyright (C) EdgeTX
|
||||
*
|
||||
* Based on code named
|
||||
* opentx - https://github.com/opentx/opentx
|
||||
* 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 "intmodule_heartbeat.h"
|
||||
|
||||
#include "heartbeat_driver.h"
|
||||
#include "mixer_scheduler.h"
|
||||
#include "dataconstants.h"
|
||||
#include "pulses/pxx.h"
|
||||
|
||||
#if defined(PXX1)
|
||||
#include "pulses/pxx1.h"
|
||||
#define isPxx1Driver(drv) (drv == &Pxx1Driver)
|
||||
#else
|
||||
#define isPxx1Driver(drv) (false)
|
||||
#endif
|
||||
|
||||
#if defined(PXX2)
|
||||
#include "pulses/pxx2.h"
|
||||
#define isPxx2Driver(drv) (drv == &Pxx2Driver)
|
||||
#else
|
||||
#define isPxx2Driver(drv) (false)
|
||||
#endif
|
||||
|
||||
void _intmodule_heartbeat_init(uint8_t module, const etx_proto_driver_t* drv)
|
||||
{
|
||||
if (module == INTERNAL_MODULE && (isPxx1Driver(drv) || isPxx2Driver(drv))) {
|
||||
init_intmodule_heartbeat();
|
||||
if (isPxx1Driver(drv)) {
|
||||
// XJT / iXJT
|
||||
auto period = mixerSchedulerGetPeriod(module);
|
||||
mixerSchedulerSetPeriod(module, period + 1000/*us*/);
|
||||
} else if (isPxx2Driver(drv)) {
|
||||
// ISRM
|
||||
mixerSchedulerSetPeriod(module, PXX2_MAX_HEARTBEAT_PERIOD);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void _intmodule_heartbeat_deinit(uint8_t module, const etx_proto_driver_t* drv)
|
||||
{
|
||||
if (module == INTERNAL_MODULE) {
|
||||
stop_intmodule_heartbeat();
|
||||
}
|
||||
}
|
||||
+4
-9
@@ -19,15 +19,10 @@
|
||||
* GNU General Public License for more details.
|
||||
*/
|
||||
|
||||
#include <stdint.h>
|
||||
#pragma once
|
||||
|
||||
#include "hal/serial_driver.h"
|
||||
#include "hal/module_driver.h"
|
||||
|
||||
#if defined(AUX_SERIAL)
|
||||
extern const etx_serial_driver_t AuxSerialDriver;
|
||||
#endif
|
||||
|
||||
#if defined(AUX2_SERIAL)
|
||||
extern const etx_serial_driver_t Aux2SerialDriver;
|
||||
#endif
|
||||
void _intmodule_heartbeat_init(uint8_t module, const etx_proto_driver_t* drv);
|
||||
void _intmodule_heartbeat_deinit(uint8_t module, const etx_proto_driver_t* drv);
|
||||
|
||||
@@ -0,0 +1,484 @@
|
||||
/*
|
||||
* Copyright (C) EdgeTX
|
||||
*
|
||||
* Based on code named
|
||||
* opentx - https://github.com/opentx/opentx
|
||||
* 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 "hal/module_port.h"
|
||||
#include "stm32_serial_driver.h"
|
||||
#include "stm32_softserial_driver.h"
|
||||
|
||||
#include "module_ports.h"
|
||||
#include "board.h"
|
||||
#include "dataconstants.h"
|
||||
|
||||
#if defined(INTMODULE_USART)
|
||||
|
||||
#define INTMODULE_USART_IRQ_PRIORITY 6
|
||||
|
||||
static const stm32_usart_t intmoduleUSART = {
|
||||
.USARTx = INTMODULE_USART,
|
||||
.GPIOx = INTMODULE_GPIO,
|
||||
.GPIO_Pin = INTMODULE_TX_GPIO_PIN | INTMODULE_RX_GPIO_PIN,
|
||||
.IRQn = INTMODULE_USART_IRQn,
|
||||
.IRQ_Prio = INTMODULE_USART_IRQ_PRIORITY,
|
||||
#if defined(INTMODULE_DMA)
|
||||
.txDMA = INTMODULE_DMA,
|
||||
.txDMA_Stream = INTMODULE_DMA_STREAM,
|
||||
.txDMA_Channel = INTMODULE_DMA_CHANNEL,
|
||||
#else
|
||||
.txDMA = nullptr,
|
||||
.txDMA_Stream = 0,
|
||||
.txDMA_Channel = 0,
|
||||
#endif
|
||||
#if defined(INTMODULE_RX_DMA)
|
||||
.rxDMA = INTMODULE_RX_DMA,
|
||||
.rxDMA_Stream = INTMODULE_RX_DMA_STREAM,
|
||||
.rxDMA_Channel = INTMODULE_RX_DMA_CHANNEL,
|
||||
#else
|
||||
.rxDMA = nullptr,
|
||||
.rxDMA_Stream = 0,
|
||||
.rxDMA_Channel = 0,
|
||||
#endif
|
||||
};
|
||||
|
||||
DEFINE_STM32_SERIAL_PORT(InternalModule, intmoduleUSART, INTMODULE_FIFO_SIZE, 0);
|
||||
|
||||
#else // INTMODULE_USART
|
||||
|
||||
#include "stm32_pulse_driver.h"
|
||||
#include "timers_driver.h"
|
||||
|
||||
extern const stm32_pulse_timer_t intmoduleTimer;
|
||||
DEFINE_STM32_SOFTSERIAL_PORT(InternalModule, intmoduleTimer);
|
||||
|
||||
#endif
|
||||
|
||||
#include "module_timer_driver.h"
|
||||
#include "extmodule_driver.h"
|
||||
|
||||
#if defined(HARDWARE_EXTERNAL_MODULE)
|
||||
#if defined(EXTMODULE_USART)
|
||||
|
||||
#define EXTMODULE_USART_IRQ_PRIORITY 6
|
||||
|
||||
#if defined(EXTMODULE_TX_INVERT_GPIO) && defined(EXTMODULE_RX_INVERT_GPIO)
|
||||
static void _extmod_set_inverted(uint8_t enable)
|
||||
{
|
||||
// In EdgeTX the external module is normally always
|
||||
// inverted, so invert the logic here
|
||||
if (!enable) {
|
||||
LL_GPIO_SetOutputPin(EXTMODULE_TX_INVERT_GPIO,
|
||||
EXTMODULE_TX_INVERT_GPIO_PIN);
|
||||
LL_GPIO_SetOutputPin(EXTMODULE_RX_INVERT_GPIO,
|
||||
EXTMODULE_RX_INVERT_GPIO_PIN);
|
||||
} else {
|
||||
LL_GPIO_ResetOutputPin(EXTMODULE_TX_INVERT_GPIO,
|
||||
EXTMODULE_TX_INVERT_GPIO_PIN);
|
||||
LL_GPIO_ResetOutputPin(EXTMODULE_RX_INVERT_GPIO,
|
||||
EXTMODULE_RX_INVERT_GPIO_PIN);
|
||||
}
|
||||
}
|
||||
|
||||
static void _extmod_init_inverter()
|
||||
{
|
||||
LL_GPIO_InitTypeDef pinInit;
|
||||
LL_GPIO_StructInit(&pinInit);
|
||||
|
||||
pinInit.Mode = LL_GPIO_MODE_OUTPUT;
|
||||
pinInit.Pin = EXTMODULE_TX_INVERT_GPIO_PIN;
|
||||
LL_GPIO_Init(EXTMODULE_TX_INVERT_GPIO, &pinInit);
|
||||
|
||||
pinInit.Pin = EXTMODULE_RX_INVERT_GPIO_PIN;
|
||||
LL_GPIO_Init(EXTMODULE_RX_INVERT_GPIO, &pinInit);
|
||||
|
||||
// this sets the output to idle-low as is
|
||||
// the default in EdgeTX on external module
|
||||
// (historically, most radios work this way)
|
||||
_extmod_set_inverted(false);
|
||||
}
|
||||
#endif
|
||||
|
||||
static const stm32_usart_t extmoduleUSART = {
|
||||
.USARTx = EXTMODULE_USART,
|
||||
.GPIOx = EXTMODULE_TX_GPIO,
|
||||
.GPIO_Pin = EXTMODULE_TX_GPIO_PIN | EXTMODULE_RX_GPIO_PIN,
|
||||
.IRQn = EXTMODULE_USART_IRQn,
|
||||
.IRQ_Prio = EXTMODULE_USART_IRQ_PRIORITY,
|
||||
.txDMA = EXTMODULE_USART_TX_DMA,
|
||||
.txDMA_Stream = EXTMODULE_USART_TX_DMA_STREAM_LL,
|
||||
.txDMA_Channel = EXTMODULE_USART_TX_DMA_CHANNEL,
|
||||
.rxDMA = EXTMODULE_USART_TX_DMA,
|
||||
.rxDMA_Stream = EXTMODULE_USART_RX_DMA_STREAM_LL,
|
||||
.rxDMA_Channel = EXTMODULE_USART_RX_DMA_CHANNEL,
|
||||
.set_input = nullptr,
|
||||
.txDMA_IRQn = (IRQn_Type)0,
|
||||
.txDMA_IRQ_Prio = 0,
|
||||
};
|
||||
|
||||
DEFINE_STM32_SERIAL_PORT(ExternalModule, extmoduleUSART, INTMODULE_FIFO_SIZE, 0);
|
||||
|
||||
#endif
|
||||
|
||||
DEFINE_STM32_SOFTSERIAL_PORT(ExternalModule, extmoduleTimer);
|
||||
#endif
|
||||
|
||||
#define TELEMETRY_USART_IRQ_PRIORITY 5
|
||||
#define TELEMETRY_DMA_IRQ_PRIORITY 0
|
||||
|
||||
static void _set_sport_input(uint8_t enable)
|
||||
{
|
||||
if (TELEMETRY_SET_INPUT) {
|
||||
if (enable) {
|
||||
LL_GPIO_SetOutputPin(TELEMETRY_DIR_GPIO, TELEMETRY_DIR_GPIO_PIN);
|
||||
} else {
|
||||
LL_GPIO_ResetOutputPin(TELEMETRY_DIR_GPIO, TELEMETRY_DIR_GPIO_PIN);
|
||||
}
|
||||
} else {
|
||||
if (enable) {
|
||||
LL_GPIO_ResetOutputPin(TELEMETRY_DIR_GPIO, TELEMETRY_DIR_GPIO_PIN);
|
||||
} else {
|
||||
LL_GPIO_SetOutputPin(TELEMETRY_DIR_GPIO, TELEMETRY_DIR_GPIO_PIN);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#if defined(TELEMETRY_REV_GPIO)
|
||||
static void _sport_set_inverted(uint8_t enable)
|
||||
{
|
||||
uint32_t pins = TELEMETRY_TX_REV_GPIO_PIN | TELEMETRY_RX_REV_GPIO_PIN;
|
||||
if (!enable) {
|
||||
LL_GPIO_SetOutputPin(TELEMETRY_REV_GPIO, pins);
|
||||
} else {
|
||||
LL_GPIO_ResetOutputPin(TELEMETRY_REV_GPIO, pins);
|
||||
}
|
||||
}
|
||||
|
||||
static void _sport_init_inverter()
|
||||
{
|
||||
LL_GPIO_InitTypeDef pinInit;
|
||||
LL_GPIO_StructInit(&pinInit);
|
||||
|
||||
pinInit.Pin = TELEMETRY_TX_REV_GPIO_PIN | TELEMETRY_RX_REV_GPIO_PIN;
|
||||
pinInit.Mode = LL_GPIO_MODE_OUTPUT;
|
||||
LL_GPIO_Init(TELEMETRY_REV_GPIO, &pinInit);
|
||||
_sport_set_inverted(false);
|
||||
}
|
||||
#endif
|
||||
|
||||
static const stm32_usart_t sportUSART = {
|
||||
.USARTx = TELEMETRY_USART,
|
||||
.GPIOx = TELEMETRY_GPIO,
|
||||
.GPIO_Pin = TELEMETRY_TX_GPIO_PIN | TELEMETRY_RX_GPIO_PIN,
|
||||
.IRQn = TELEMETRY_USART_IRQn,
|
||||
.IRQ_Prio = TELEMETRY_USART_IRQ_PRIORITY,
|
||||
.txDMA = TELEMETRY_DMA,
|
||||
.txDMA_Stream = TELEMETRY_DMA_Stream_TX,
|
||||
.txDMA_Channel = TELEMETRY_DMA_Channel_TX,
|
||||
.rxDMA = nullptr,
|
||||
.rxDMA_Stream = 0,
|
||||
.rxDMA_Channel = 0,
|
||||
.set_input = _set_sport_input,
|
||||
.txDMA_IRQn = TELEMETRY_DMA_TX_Stream_IRQ,
|
||||
.txDMA_IRQ_Prio = TELEMETRY_DMA_IRQ_PRIORITY,
|
||||
};
|
||||
|
||||
extern "C" void TELEMETRY_DMA_TX_IRQHandler(void)
|
||||
{
|
||||
stm32_usart_tx_dma_isr(&sportUSART);
|
||||
}
|
||||
|
||||
DEFINE_STM32_SERIAL_PORT(SportModule, sportUSART, TELEMETRY_FIFO_SIZE, 0);
|
||||
|
||||
static void _sport_direction_init()
|
||||
{
|
||||
LL_GPIO_InitTypeDef dirPinInit;
|
||||
LL_GPIO_StructInit(&dirPinInit);
|
||||
|
||||
dirPinInit.Pin = TELEMETRY_DIR_GPIO_PIN;
|
||||
dirPinInit.Mode = LL_GPIO_MODE_OUTPUT;
|
||||
LL_GPIO_Init(TELEMETRY_DIR_GPIO, &dirPinInit);
|
||||
}
|
||||
|
||||
#if defined(TELEMETRY_TIMER)
|
||||
static const stm32_softserial_rx_port sportSoftRX = {
|
||||
.GPIOx = TELEMETRY_GPIO,
|
||||
.GPIO_Pin = TELEMETRY_RX_GPIO_PIN,
|
||||
.TIMx = TELEMETRY_TIMER,
|
||||
.TIM_Freq = PERI2_FREQUENCY * TIMER_MULT_APB2,
|
||||
.TIM_IRQn = TELEMETRY_TIMER_IRQn,
|
||||
.EXTI_Port = TELEMETRY_EXTI_PORT,
|
||||
.EXTI_SysLine = TELEMETRY_EXTI_SYS_LINE,
|
||||
.EXTI_Line = TELEMETRY_EXTI_LINE,
|
||||
.dir_GPIOx = TELEMETRY_DIR_GPIO,
|
||||
.dir_Pin = TELEMETRY_DIR_GPIO_PIN,
|
||||
.dir_Input = TELEMETRY_SET_INPUT,
|
||||
// re-use S.PORT serial RX buffer
|
||||
.buffer = { SportModule_RXBuffer, TELEMETRY_FIFO_SIZE },
|
||||
};
|
||||
|
||||
extern "C" void TELEMETRY_TIMER_IRQHandler()
|
||||
{
|
||||
stm32_softserial_rx_timer_isr(&sportSoftRX);
|
||||
}
|
||||
#endif
|
||||
|
||||
#if defined(HARDWARE_INTERNAL_MODULE)
|
||||
|
||||
static void _internal_module_set_pwr(uint8_t enable)
|
||||
{
|
||||
if (enable) {
|
||||
INTERNAL_MODULE_ON();
|
||||
} else {
|
||||
INTERNAL_MODULE_OFF();
|
||||
}
|
||||
}
|
||||
|
||||
#if defined(INTMODULE_BOOTCMD_GPIO)
|
||||
static void _internal_module_set_bootcmd(uint8_t enable)
|
||||
{
|
||||
// If default state is SET, invert the logic
|
||||
if (INTMODULE_BOOTCMD_DEFAULT) {
|
||||
enable = !enable;
|
||||
}
|
||||
|
||||
if (enable) {
|
||||
LL_GPIO_SetOutputPin(INTMODULE_BOOTCMD_GPIO, INTMODULE_BOOTCMD_GPIO_PIN);
|
||||
} else {
|
||||
LL_GPIO_ResetOutputPin(INTMODULE_BOOTCMD_GPIO, INTMODULE_BOOTCMD_GPIO_PIN);
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
static const etx_module_port_t _internal_ports[] = {
|
||||
#if defined(INTMODULE_USART)
|
||||
{
|
||||
.port = ETX_MOD_PORT_UART,
|
||||
.type = ETX_MOD_TYPE_SERIAL,
|
||||
.dir_flags = ETX_MOD_DIR_TX_RX | ETX_MOD_FULL_DUPLEX,
|
||||
.drv = { .serial = &STM32SerialDriver },
|
||||
.hw_def = REF_STM32_SERIAL_PORT(InternalModule),
|
||||
},
|
||||
#else // INTMODULE_USART
|
||||
{
|
||||
.port = ETX_MOD_PORT_SOFT_INV,
|
||||
.type = ETX_MOD_TYPE_SERIAL,
|
||||
.dir_flags = ETX_MOD_DIR_TX,
|
||||
.drv = { .serial = &STM32SoftSerialTxDriver },
|
||||
.hw_def = REF_STM32_SOFTSERIAL_PORT(InternalModule),
|
||||
},
|
||||
#endif
|
||||
#if defined(INTERNAL_MODULE_PXX1)
|
||||
{
|
||||
.port = ETX_MOD_PORT_SPORT,
|
||||
.type = ETX_MOD_TYPE_SERIAL,
|
||||
.dir_flags = ETX_MOD_DIR_TX | ETX_MOD_DIR_RX,
|
||||
.drv = { .serial = &STM32SerialDriver },
|
||||
.hw_def = REF_STM32_SERIAL_PORT(SportModule),
|
||||
},
|
||||
#endif
|
||||
};
|
||||
|
||||
static const etx_module_t _internal_module = {
|
||||
.ports = _internal_ports,
|
||||
.set_pwr = _internal_module_set_pwr,
|
||||
#if defined(INTMODULE_BOOTCMD_GPIO)
|
||||
.set_bootcmd = _internal_module_set_bootcmd,
|
||||
#else
|
||||
.set_bootcmd = nullptr,
|
||||
#endif
|
||||
.n_ports = DIM(_internal_ports),
|
||||
};
|
||||
#endif
|
||||
|
||||
#if defined(HARDWARE_EXTERNAL_MODULE)
|
||||
|
||||
static void _external_module_set_pwr(uint8_t enable)
|
||||
{
|
||||
if (enable) {
|
||||
EXTERNAL_MODULE_ON();
|
||||
#if defined(PCBNV14)
|
||||
if (hardwareOptions.pcbrev == PCBREV_NV14) {
|
||||
LL_GPIO_ResetOutputPin(EXTMODULE_PWR_FIX_GPIO, EXTMODULE_PWR_FIX_GPIO_PIN);
|
||||
}
|
||||
#endif
|
||||
} else {
|
||||
EXTERNAL_MODULE_OFF();
|
||||
#if defined(PCBNV14)
|
||||
if (hardwareOptions.pcbrev == PCBREV_NV14) {
|
||||
LL_GPIO_SetOutputPin(EXTMODULE_PWR_FIX_GPIO, EXTMODULE_PWR_FIX_GPIO_PIN);
|
||||
}
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
||||
static const etx_module_port_t _external_ports[] = {
|
||||
#if defined(EXTMODULE_USART)
|
||||
// TX on PPM, RX on HEARTBEAT
|
||||
{
|
||||
.port = ETX_MOD_PORT_UART,
|
||||
.type = ETX_MOD_TYPE_SERIAL,
|
||||
.dir_flags = ETX_MOD_DIR_TX_RX | ETX_MOD_FULL_DUPLEX,
|
||||
.drv = { .serial = &STM32SerialDriver },
|
||||
.hw_def = REF_STM32_SERIAL_PORT(ExternalModule),
|
||||
#if defined(EXTMODULE_TX_INVERT_GPIO) && defined(EXTMODULE_RX_INVERT_GPIO)
|
||||
.set_inverted = _extmod_set_inverted,
|
||||
#else
|
||||
.set_inverted = nullptr,
|
||||
#endif
|
||||
},
|
||||
#endif
|
||||
// Timer output on PPM
|
||||
{
|
||||
.port = ETX_MOD_PORT_TIMER,
|
||||
.type = ETX_MOD_TYPE_TIMER,
|
||||
.dir_flags = ETX_MOD_DIR_TX,
|
||||
.drv = { .timer = &STM32ModuleTimerDriver },
|
||||
.hw_def = (void*)&extmoduleTimer,
|
||||
#if defined(EXTMODULE_TX_INVERT_GPIO) && defined(EXTMODULE_RX_INVERT_GPIO)
|
||||
.set_inverted = _extmod_set_inverted,
|
||||
#else
|
||||
.set_inverted = nullptr,
|
||||
#endif
|
||||
},
|
||||
// TX inverted DMA pulse train on PPM
|
||||
{
|
||||
.port = ETX_MOD_PORT_SOFT_INV,
|
||||
.type = ETX_MOD_TYPE_SERIAL,
|
||||
.dir_flags = ETX_MOD_DIR_TX,
|
||||
.drv = { .serial = &STM32SoftSerialTxDriver },
|
||||
.hw_def = REF_STM32_SOFTSERIAL_PORT(ExternalModule),
|
||||
#if defined(EXTMODULE_TX_INVERT_GPIO) && defined(EXTMODULE_RX_INVERT_GPIO)
|
||||
.set_inverted = _extmod_set_inverted,
|
||||
#else
|
||||
.set_inverted = nullptr,
|
||||
#endif
|
||||
},
|
||||
// TX/RX half-duplex on S.PORT
|
||||
{
|
||||
.port = ETX_MOD_PORT_SPORT,
|
||||
.type = ETX_MOD_TYPE_SERIAL,
|
||||
.dir_flags = ETX_MOD_DIR_TX | ETX_MOD_DIR_RX,
|
||||
.drv = { .serial = &STM32SerialDriver },
|
||||
.hw_def = REF_STM32_SERIAL_PORT(SportModule),
|
||||
#if defined(TELEMETRY_REV_GPIO)
|
||||
.set_inverted = _sport_set_inverted,
|
||||
#else
|
||||
.set_inverted = nullptr,
|
||||
#endif
|
||||
},
|
||||
#if defined(TELEMETRY_TIMER)
|
||||
// RX soft-serial sampled bit-by-bit via timer IRQ on S.PORT
|
||||
{
|
||||
.port = ETX_MOD_PORT_SPORT_INV,
|
||||
.type = ETX_MOD_TYPE_SERIAL,
|
||||
.dir_flags = ETX_MOD_DIR_RX,
|
||||
.drv = { .serial = &STM32SoftSerialRxDriver },
|
||||
.hw_def = (void*)&sportSoftRX,
|
||||
},
|
||||
#endif
|
||||
};
|
||||
|
||||
static const etx_module_t _external_module = {
|
||||
.ports = _external_ports,
|
||||
.set_pwr = _external_module_set_pwr,
|
||||
.set_bootcmd = nullptr,
|
||||
.n_ports = DIM(_external_ports),
|
||||
};
|
||||
#endif
|
||||
|
||||
#if defined(SPORT_UPDATE_PWR_GPIO)
|
||||
#include "sport_update.h"
|
||||
|
||||
// Not declared 'static' to be accessible
|
||||
// from board.cpp
|
||||
void _sport_set_pwr(uint8_t enabled)
|
||||
{
|
||||
if (enabled) {
|
||||
LL_GPIO_SetOutputPin(SPORT_UPDATE_PWR_GPIO, SPORT_UPDATE_PWR_GPIO_PIN);
|
||||
} else {
|
||||
LL_GPIO_ResetOutputPin(SPORT_UPDATE_PWR_GPIO, SPORT_UPDATE_PWR_GPIO_PIN);
|
||||
}
|
||||
}
|
||||
|
||||
#if defined(RADIO_X7)
|
||||
// set_pwr is set at runtime once
|
||||
// the PCB has been identified
|
||||
etx_module_t _sport_module = {
|
||||
.ports = nullptr,
|
||||
.set_pwr = nullptr,
|
||||
.set_bootcmd = nullptr,
|
||||
.n_ports = 0,
|
||||
};
|
||||
#else
|
||||
const etx_module_t _sport_module = {
|
||||
.ports = nullptr,
|
||||
.set_pwr = _sport_set_pwr,
|
||||
.set_bootcmd = nullptr,
|
||||
.n_ports = 0,
|
||||
};
|
||||
#endif // RADIO_X7
|
||||
|
||||
#endif // SPORT_UPDATE_PWR_GPIO
|
||||
|
||||
void boardInitModulePorts()
|
||||
{
|
||||
_sport_direction_init();
|
||||
#if defined(SPORT_UPDATE_PWR_GPIO)
|
||||
sportUpdateInit();
|
||||
#endif
|
||||
|
||||
#if defined(TELEMETRY_REV_GPIO)
|
||||
_sport_init_inverter();
|
||||
#endif
|
||||
#if defined(EXTMODULE_TX_INVERT_GPIO) && defined(EXTMODULE_RX_INVERT_GPIO)
|
||||
_extmod_init_inverter();
|
||||
#endif
|
||||
|
||||
#if defined(PCBNV14)
|
||||
if (hardwareOptions.pcbrev == PCBREV_NV14) {
|
||||
LL_GPIO_InitTypeDef pinInit;
|
||||
LL_GPIO_StructInit(&pinInit);
|
||||
pinInit.Pin = EXTMODULE_PWR_FIX_GPIO_PIN;
|
||||
pinInit.Mode = LL_GPIO_MODE_OUTPUT;
|
||||
|
||||
// pin must be pulled to V+ (voltage of board - VCC is not enough to fully close transistor)
|
||||
// for additional transistor to ensuring module is completely disabled
|
||||
pinInit.OutputType = LL_GPIO_OUTPUT_OPENDRAIN;
|
||||
|
||||
LL_GPIO_SetOutputPin(EXTMODULE_PWR_FIX_GPIO, EXTMODULE_PWR_FIX_GPIO_PIN);
|
||||
LL_GPIO_Init(EXTMODULE_PWR_FIX_GPIO, &pinInit);
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
BEGIN_MODULES()
|
||||
#if defined(HARDWARE_INTERNAL_MODULE)
|
||||
&_internal_module,
|
||||
#else
|
||||
nullptr,
|
||||
#endif
|
||||
#if defined(HARDWARE_EXTERNAL_MODULE)
|
||||
&_external_module,
|
||||
#else
|
||||
nullptr,
|
||||
#endif
|
||||
#if defined(SPORT_UPDATE_PWR_GPIO)
|
||||
&_sport_module,
|
||||
#endif
|
||||
END_MODULES()
|
||||
@@ -0,0 +1,24 @@
|
||||
/*
|
||||
* Copyright (C) EdgeTX
|
||||
*
|
||||
* Based on code named
|
||||
* opentx - https://github.com/opentx/opentx
|
||||
* 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.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
void boardInitModulePorts();
|
||||
@@ -0,0 +1,57 @@
|
||||
/*
|
||||
* Copyright (C) EdgeTX
|
||||
*
|
||||
* Based on code named
|
||||
* opentx - https://github.com/opentx/opentx
|
||||
* 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 "hal.h"
|
||||
#include "stm32_hal_ll.h"
|
||||
|
||||
#include "hal/module_port.h"
|
||||
#include "opentx.h" // g_eeGeneral
|
||||
|
||||
void sportUpdateInit()
|
||||
{
|
||||
#if defined(SPORT_UPDATE_PWR_GPIO)
|
||||
|
||||
#if defined(RADIO_X7)
|
||||
// QX7 has a external S.PORT connector
|
||||
// with switchable power from revision 40 on
|
||||
if (hardwareOptions.pcbrev == PCBREV_X7_40) {
|
||||
extern etx_module_t _sport_module;
|
||||
extern void _sport_set_pwr(uint8_t);
|
||||
_sport_module.set_pwr = _sport_set_pwr;
|
||||
} else {
|
||||
return;
|
||||
}
|
||||
#endif
|
||||
|
||||
GPIO_InitTypeDef GPIO_InitStructure;
|
||||
GPIO_InitStructure.GPIO_Pin = SPORT_UPDATE_PWR_GPIO_PIN;
|
||||
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_OUT;
|
||||
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_2MHz;
|
||||
GPIO_InitStructure.GPIO_OType = GPIO_OType_PP;
|
||||
GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_UP;
|
||||
GPIO_Init(SPORT_UPDATE_PWR_GPIO, &GPIO_InitStructure);
|
||||
#endif
|
||||
}
|
||||
|
||||
void sportUpdatePowerInit()
|
||||
{
|
||||
modulePortSetPower(SPORT_MODULE, g_eeGeneral.sportUpdatePower);
|
||||
}
|
||||
@@ -0,0 +1,26 @@
|
||||
/*
|
||||
* Copyright (C) EdgeTX
|
||||
*
|
||||
* Based on code named
|
||||
* opentx - https://github.com/opentx/opentx
|
||||
* 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.
|
||||
*/
|
||||
|
||||
// init S.PORT power pin
|
||||
void sportUpdateInit();
|
||||
|
||||
// Set S.PORT power ON/OFF on start as configured
|
||||
void sportUpdatePowerInit();
|
||||
+39
-47
@@ -25,6 +25,10 @@
|
||||
#include "opentx.h"
|
||||
#include "diskio.h"
|
||||
#include "timers_driver.h"
|
||||
#include "hal/module_port.h"
|
||||
|
||||
#include "tasks.h"
|
||||
#include "tasks/mixer_task.h"
|
||||
|
||||
#include "cli.h"
|
||||
|
||||
@@ -34,10 +38,6 @@
|
||||
#include <stdarg.h>
|
||||
|
||||
|
||||
#if defined(INTMODULE_USART)
|
||||
#include "intmodule_serial_driver.h"
|
||||
#endif
|
||||
|
||||
#define CLI_COMMAND_MAX_ARGS 8
|
||||
#define CLI_COMMAND_MAX_LEN 256
|
||||
|
||||
@@ -581,7 +581,7 @@ int cliTestGraphics()
|
||||
if (pulsesStarted()) {
|
||||
pausePulses();
|
||||
}
|
||||
pauseMixerCalculations();
|
||||
mixerTaskStop();
|
||||
perMainEnabled = false;
|
||||
|
||||
float result = 0;
|
||||
@@ -607,7 +607,7 @@ int cliTestGraphics()
|
||||
if (pulsesStarted()) {
|
||||
resumePulses();
|
||||
}
|
||||
resumeMixerCalculations();
|
||||
mixerTaskStart();
|
||||
watchdogSuspend(0);
|
||||
|
||||
return 0;
|
||||
@@ -714,7 +714,7 @@ int cliTestMemorySpeed()
|
||||
if (pulsesStarted()) {
|
||||
pausePulses();
|
||||
}
|
||||
pauseMixerCalculations();
|
||||
mixerTaskStop();
|
||||
perMainEnabled = false;
|
||||
|
||||
float result = 0;
|
||||
@@ -749,7 +749,7 @@ int cliTestMemorySpeed()
|
||||
if (pulsesStarted()) {
|
||||
resumePulses();
|
||||
}
|
||||
resumeMixerCalculations();
|
||||
mixerTaskStart();
|
||||
watchdogSuspend(0);
|
||||
|
||||
return 0;
|
||||
@@ -906,7 +906,7 @@ int cliReboot(const char ** argv)
|
||||
#if !defined(SIMU)
|
||||
if (!strcmp(argv[1], "wdt")) {
|
||||
// do a user requested watchdog test by pausing mixer thread
|
||||
pausePulses();
|
||||
pulsesStop();
|
||||
}
|
||||
else {
|
||||
NVIC_SystemReset();
|
||||
@@ -973,17 +973,9 @@ int cliSet(const char **argv)
|
||||
}
|
||||
if (!strcmp(argv[3], "power")) {
|
||||
if (!strcmp("on", argv[4])) {
|
||||
if (module == 0) {
|
||||
INTERNAL_MODULE_ON();
|
||||
} else {
|
||||
EXTERNAL_MODULE_ON();
|
||||
}
|
||||
modulePortSetPower(module, true);
|
||||
} else if (!strcmp("off", argv[4])) {
|
||||
if (module == 0) {
|
||||
INTERNAL_MODULE_OFF();
|
||||
} else {
|
||||
EXTERNAL_MODULE_OFF();
|
||||
}
|
||||
modulePortSetPower(module, false);
|
||||
} else {
|
||||
cliSerialPrint("%s: invalid power argument '%s'", argv[0], argv[4]);
|
||||
return -1;
|
||||
@@ -1026,10 +1018,10 @@ int cliSet(const char **argv)
|
||||
|
||||
if (level) {
|
||||
cliSerialPrint("%s: pulses start", argv[0]);
|
||||
startPulses();
|
||||
pulsesStart();
|
||||
} else {
|
||||
cliSerialPrint("%s: pulses stop", argv[0]);
|
||||
stopPulses();
|
||||
pulsesStop();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1037,21 +1029,24 @@ int cliSet(const char **argv)
|
||||
}
|
||||
|
||||
#if defined(ENABLE_SERIAL_PASSTHROUGH)
|
||||
static void *spInternalModuleCTX = nullptr;
|
||||
static etx_module_state_t *spInternalModuleState = nullptr;
|
||||
|
||||
static void spInternalModuleTx(uint8_t* buf, uint32_t len)
|
||||
{
|
||||
auto drv = modulePortGetSerialDrv(spInternalModuleState->tx);
|
||||
auto ctx = modulePortGetCtx(spInternalModuleState->tx);
|
||||
|
||||
while (len > 0) {
|
||||
IntmoduleSerialDriver.sendByte(spInternalModuleCTX, *(buf++));
|
||||
drv->sendByte(ctx, *(buf++));
|
||||
len--;
|
||||
}
|
||||
}
|
||||
|
||||
static const etx_serial_init spIntmoduleSerialInitParams = {
|
||||
.baudrate = 0,
|
||||
.parity = ETX_Parity_None,
|
||||
.stop_bits = ETX_StopBits_One,
|
||||
.word_length = ETX_WordLength_8,
|
||||
.rx_enable = true,
|
||||
.encoding = ETX_Encoding_8N1,
|
||||
.direction = ETX_Dir_TX_RX,
|
||||
.polarity = ETX_Pol_Normal,
|
||||
};
|
||||
|
||||
// TODO: use proper method instead
|
||||
@@ -1100,7 +1095,7 @@ int cliSerialPassthrough(const char **argv)
|
||||
|
||||
// stop pulses
|
||||
watchdogSuspend(200/*2s*/);
|
||||
stopPulses();
|
||||
pulsesStop();
|
||||
|
||||
// suspend RTOS scheduler
|
||||
vTaskSuspendAll();
|
||||
@@ -1114,30 +1109,27 @@ int cliSerialPassthrough(const char **argv)
|
||||
etx_serial_init params(spIntmoduleSerialInitParams);
|
||||
params.baudrate = baudrate;
|
||||
|
||||
void* uart_ctx = IntmoduleSerialDriver.init(¶ms);
|
||||
spInternalModuleCTX = uart_ctx;
|
||||
spInternalModuleState = modulePortInitSerial(port_n, ETX_MOD_PORT_UART,
|
||||
¶ms);
|
||||
|
||||
auto drv = modulePortGetSerialDrv(spInternalModuleState->rx);
|
||||
auto ctx = modulePortGetCtx(spInternalModuleState->rx);
|
||||
|
||||
// backup and swap CLI input
|
||||
auto backupCB = cliReceiveCallBack;
|
||||
cliReceiveCallBack = spInternalModuleTx;
|
||||
|
||||
|
||||
// loop until cable disconnected
|
||||
while (cdcConnected) {
|
||||
|
||||
uint32_t cli_br = cliGetBaudRate();
|
||||
if (cli_br && (cli_br != (uint32_t)baudrate)) {
|
||||
baudrate = cli_br;
|
||||
|
||||
etx_serial_init params(spIntmoduleSerialInitParams);
|
||||
params.baudrate = baudrate;
|
||||
|
||||
// re-configure serial port
|
||||
spInternalModuleCTX = IntmoduleSerialDriver.init(¶ms);
|
||||
drv->setBaudrate(ctx, baudrate);
|
||||
}
|
||||
|
||||
uint8_t data;
|
||||
if (IntmoduleSerialDriver.getByte(uart_ctx, &data) > 0) {
|
||||
if (drv->getByte(ctx, &data) > 0) {
|
||||
|
||||
uint8_t timeout = 10; // 10 ms
|
||||
while(!usbSerialFreeSpace() && (timeout > 0)) {
|
||||
@@ -1154,13 +1146,13 @@ int cliSerialPassthrough(const char **argv)
|
||||
|
||||
// restore callsbacks
|
||||
cliReceiveCallBack = backupCB;
|
||||
spInternalModuleCTX = nullptr;
|
||||
|
||||
// and stop module
|
||||
IntmoduleSerialDriver.deinit(uart_ctx);
|
||||
modulePortDeInit(spInternalModuleState);
|
||||
spInternalModuleState = nullptr;
|
||||
|
||||
// power off the module and wait for a bit
|
||||
INTERNAL_MODULE_OFF();
|
||||
modulePortSetPower(port_n, false);
|
||||
delay_ms(200);
|
||||
}
|
||||
#endif
|
||||
@@ -1169,7 +1161,7 @@ int cliSerialPassthrough(const char **argv)
|
||||
// - external module (S.PORT?)
|
||||
|
||||
watchdogSuspend(200/*2s*/);
|
||||
startPulses();
|
||||
pulsesStart();
|
||||
|
||||
// suspend RTOS scheduler
|
||||
xTaskResumeAll();
|
||||
@@ -1549,7 +1541,7 @@ int cliCrypt(const char ** argv)
|
||||
if (pulsesStarted()) {
|
||||
pausePulses();
|
||||
}
|
||||
pauseMixerCalculations();
|
||||
mixerTaskStop();
|
||||
perMainEnabled = false;
|
||||
|
||||
uint32_t startMs = RTOS_GET_MS();
|
||||
@@ -1564,7 +1556,7 @@ int cliCrypt(const char ** argv)
|
||||
if (pulsesStarted()) {
|
||||
resumePulses();
|
||||
}
|
||||
resumeMixerCalculations();
|
||||
mixerTaskStart();
|
||||
watchdogSuspend(0);
|
||||
|
||||
return 0;
|
||||
@@ -1587,7 +1579,7 @@ int cliResetGT911(const char** argv)
|
||||
|
||||
// stop pulses & suspend RTOS scheduler
|
||||
watchdogSuspend(200/*2s*/);
|
||||
stopPulses();
|
||||
pulsesStop();
|
||||
vTaskSuspendAll();
|
||||
|
||||
// reset touch controller
|
||||
@@ -1598,7 +1590,7 @@ int cliResetGT911(const char** argv)
|
||||
cliSerialPrintf("GT911: new config version is %u\n", tp_gt911_cfgVer);
|
||||
|
||||
// restart pulses & RTOS scheduler
|
||||
startPulses();
|
||||
pulsesStart();
|
||||
xTaskResumeAll();
|
||||
|
||||
return 0;
|
||||
@@ -1719,7 +1711,7 @@ void cliTask(void * pdata)
|
||||
const TickType_t xTimeout = 100 / RTOS_MS_PER_TICK;
|
||||
size_t xReceivedBytes = xStreamBufferReceive(cliRxBuffer, &c, 1, xTimeout);
|
||||
|
||||
if (s_pulses_paused) {
|
||||
if (!mixerTaskRunning()) {
|
||||
WDG_RESET();
|
||||
}
|
||||
|
||||
|
||||
@@ -136,6 +136,7 @@ enum CurveType {
|
||||
#define MAX_CURVE_POINTS 512
|
||||
#endif
|
||||
|
||||
// TODO: remove this
|
||||
#if defined(PCBFRSKY) || defined(PCBNV14)
|
||||
#define NUM_MODULES 2
|
||||
#else
|
||||
@@ -183,7 +184,13 @@ enum BeeperMode {
|
||||
enum ModuleIndex {
|
||||
INTERNAL_MODULE,
|
||||
EXTERNAL_MODULE,
|
||||
SPORT_MODULE
|
||||
// end of "normal" modules
|
||||
|
||||
MAX_MODULES,
|
||||
|
||||
// only used for power control
|
||||
// and firmware updates
|
||||
SPORT_MODULE = MAX_MODULES
|
||||
};
|
||||
|
||||
enum TrainerMode {
|
||||
@@ -215,19 +222,22 @@ enum SerialPort {
|
||||
#define PILOTPOS_MIN_HDOP 500
|
||||
|
||||
#if defined(HARDWARE_INTERNAL_MODULE)
|
||||
#define IS_INTERNAL_MODULE_ENABLED() (g_model.moduleData[INTERNAL_MODULE].type != MODULE_TYPE_NONE)
|
||||
#define IS_INTERNAL_MODULE_ENABLED() \
|
||||
(g_model.moduleData[INTERNAL_MODULE].type != MODULE_TYPE_NONE)
|
||||
#else
|
||||
#define IS_INTERNAL_MODULE_ENABLED() (false)
|
||||
#define IS_INTERNAL_MODULE_ENABLED() (false)
|
||||
#endif
|
||||
|
||||
#define IS_EXTERNAL_MODULE_ENABLED() (g_model.moduleData[EXTERNAL_MODULE].type != MODULE_TYPE_NONE)
|
||||
|
||||
#if defined(HARDWARE_INTERNAL_MODULE)
|
||||
#define IS_MODULE_ENABLED(moduleIdx) (moduleIdx==INTERNAL_MODULE ? IS_INTERNAL_MODULE_ENABLED() : moduleIdx==EXTERNAL_MODULE ? IS_EXTERNAL_MODULE_ENABLED() : false)
|
||||
#if defined(HARDWARE_EXTERNAL_MODULE)
|
||||
#define IS_EXTERNAL_MODULE_ENABLED() \
|
||||
(g_model.moduleData[EXTERNAL_MODULE].type != MODULE_TYPE_NONE)
|
||||
#else
|
||||
#define IS_MODULE_ENABLED(moduleIdx) (moduleIdx==EXTERNAL_MODULE ? IS_EXTERNAL_MODULE_ENABLED() : false)
|
||||
#define IS_EXTERNAL_MODULE_ENABLED() false
|
||||
#endif
|
||||
|
||||
#define IS_MODULE_ENABLED(moduleIdx) \
|
||||
(g_model.moduleData[moduleIdx].type != MODULE_TYPE_NONE)
|
||||
|
||||
enum UartModes {
|
||||
UART_MODE_NONE,
|
||||
UART_MODE_TELEMETRY_MIRROR,
|
||||
|
||||
@@ -45,65 +45,6 @@ void debugPrintf(const char * format, ...)
|
||||
}
|
||||
#endif
|
||||
|
||||
#if defined(DEBUG_TRACE_BUFFER)
|
||||
static struct TraceElement traceBuffer[TRACE_BUFFER_LEN];
|
||||
static uint8_t traceBufferPos;
|
||||
extern Fifo<uint8_t, 512> auxSerialTxFifo;
|
||||
gtime_t filltm(const gtime_t *t, struct gtm *tp);
|
||||
|
||||
void trace_event(enum TraceEvent event, uint32_t data)
|
||||
{
|
||||
if (traceBufferPos >= TRACE_BUFFER_LEN) return;
|
||||
__disable_irq();
|
||||
struct TraceElement * p = &traceBuffer[traceBufferPos++];
|
||||
__enable_irq();
|
||||
p->time = g_rtcTime;
|
||||
p->time_ms = g_ms100;
|
||||
p->event = event;
|
||||
p->data = data;
|
||||
}
|
||||
|
||||
void trace_event_i(enum TraceEvent event, uint32_t data)
|
||||
{
|
||||
if (traceBufferPos >= TRACE_BUFFER_LEN) return;
|
||||
struct TraceElement * p = &traceBuffer[traceBufferPos++];
|
||||
p->time = g_rtcTime;
|
||||
p->time_ms = g_ms100;
|
||||
p->event = event;
|
||||
p->data = data;
|
||||
}
|
||||
|
||||
|
||||
const struct TraceElement * getTraceElement(uint16_t idx)
|
||||
{
|
||||
if (idx < TRACE_BUFFER_LEN) return &traceBuffer[idx];
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
void dumpTraceBuffer()
|
||||
{
|
||||
TRACE("Dump of Trace Buffer (%s " DATE " " TIME "):", vers_stamp);
|
||||
TRACE("# Time Event Data");
|
||||
for(int n = 0; n < TRACE_BUFFER_LEN; ++n) {
|
||||
struct gtm tp;
|
||||
filltm(&traceBuffer[n].time, &tp);
|
||||
TRACE_NOCRLF("%02d ", n);
|
||||
TRACE_NOCRLF("%4d-%02d-%02d,%02d:%02d:%02d.%02d0", tp.tm_year+TM_YEAR_BASE, tp.tm_mon+1, tp.tm_mday, tp.tm_hour, tp.tm_min, tp.tm_sec, traceBuffer[n].time_ms);
|
||||
TRACE(" %03d 0x%08x", traceBuffer[n].event, traceBuffer[n].data);
|
||||
if (traceBuffer[n].time == 0 && traceBuffer[n].time_ms == 0) break;
|
||||
#if !defined(SIMU)
|
||||
if ((n % 5) == 0) {
|
||||
while (!auxSerialTxFifo.isEmpty()) {
|
||||
RTOS_WAIT_TICKS(1);
|
||||
}
|
||||
}
|
||||
#endif
|
||||
}
|
||||
TRACE("End of Trace Buffer dump");
|
||||
}
|
||||
#endif
|
||||
|
||||
#if defined(DEBUG_INTERRUPTS)
|
||||
|
||||
#if defined(PCBHORUS)
|
||||
|
||||
@@ -1,83 +0,0 @@
|
||||
/*
|
||||
* Copyright (C) EdgeTX
|
||||
*
|
||||
* Based on code named
|
||||
* opentx - https://github.com/opentx/opentx
|
||||
* 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 _DMA_FIFO_H_
|
||||
#define _DMA_FIFO_H_
|
||||
|
||||
#include "definitions.h"
|
||||
|
||||
template <int N>
|
||||
class DMAFifo
|
||||
{
|
||||
public:
|
||||
explicit DMAFifo(DMA_Stream_TypeDef * stream):
|
||||
stream(stream),
|
||||
ridx(0)
|
||||
{
|
||||
}
|
||||
|
||||
void clear()
|
||||
{
|
||||
ridx = N - stream->NDTR;
|
||||
}
|
||||
|
||||
uint32_t size()
|
||||
{
|
||||
return N;
|
||||
}
|
||||
|
||||
uint8_t last(int index)
|
||||
{
|
||||
return fifo[(2*N - stream->NDTR - index) & (N-1)];
|
||||
}
|
||||
|
||||
bool isEmpty()
|
||||
{
|
||||
#if defined(SIMU)
|
||||
return true;
|
||||
#endif
|
||||
return !(stream->CR & DMA_SxCR_EN) || (ridx == N - stream->NDTR);
|
||||
}
|
||||
|
||||
bool pop(uint8_t & element)
|
||||
{
|
||||
if (isEmpty()) {
|
||||
return false;
|
||||
}
|
||||
else {
|
||||
element = fifo[ridx];
|
||||
ridx = (ridx+1) & (N-1);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
uint8_t * buffer()
|
||||
{
|
||||
return fifo;
|
||||
}
|
||||
|
||||
protected:
|
||||
uint8_t fifo[N];
|
||||
DMA_Stream_TypeDef * stream;
|
||||
volatile uint32_t ridx;
|
||||
};
|
||||
|
||||
#endif // _DMA_FIFO_H_
|
||||
@@ -500,9 +500,13 @@ void menuModelSetup(event_t event)
|
||||
|
||||
if (event == EVT_ENTRY || event == EVT_ENTRY_UP) {
|
||||
memclear(&reusableBuffer.moduleSetup, sizeof(reusableBuffer.moduleSetup));
|
||||
|
||||
#if defined(HARDWARE_EXTERNAL_MODULE)
|
||||
reusableBuffer.moduleSetup.r9mPower = g_model.moduleData[EXTERNAL_MODULE].pxx.power;
|
||||
reusableBuffer.moduleSetup.previousType = g_model.moduleData[EXTERNAL_MODULE].type;
|
||||
reusableBuffer.moduleSetup.newType = g_model.moduleData[EXTERNAL_MODULE].type;
|
||||
#endif
|
||||
|
||||
#if defined(INTERNAL_MODULE_PXX1) && defined(EXTERNAL_ANTENNA)
|
||||
reusableBuffer.moduleSetup.antennaMode = g_model.moduleData[INTERNAL_MODULE].pxx.antennaMode;
|
||||
#endif
|
||||
@@ -1004,10 +1008,11 @@ void menuModelSetup(event_t event)
|
||||
lcdDrawTextAlignedLeft(y, INDENT TR_MODE);
|
||||
lcdDrawTextAtIndex(
|
||||
MODEL_SETUP_2ND_COLUMN, y,
|
||||
moduleIdx == EXTERNAL_MODULE ? STR_EXTERNAL_MODULE_PROTOCOLS
|
||||
: STR_INTERNAL_MODULE_PROTOCOLS,
|
||||
moduleIdx == EXTERNAL_MODULE ? reusableBuffer.moduleSetup.newType
|
||||
: g_model.moduleData[moduleIdx].type,
|
||||
STR_MODULE_PROTOCOLS,
|
||||
#if defined(HARDWARE_EXTERNAL_MODULE)
|
||||
moduleIdx == EXTERNAL_MODULE ? reusableBuffer.moduleSetup.newType :
|
||||
#endif
|
||||
g_model.moduleData[moduleIdx].type,
|
||||
menuHorizontalPosition == 0 ? attr : 0);
|
||||
if (isModuleXJT(moduleIdx))
|
||||
lcdDrawTextAtIndex(lcdNextPos + 3, y, STR_XJT_ACCST_RF_PROTOCOLS,
|
||||
@@ -1031,6 +1036,7 @@ void menuModelSetup(event_t event)
|
||||
lcdNextPos + 3, y, STR_CRSF_BAUDRATE,
|
||||
CROSSFIRE_STORE_TO_INDEX(g_eeGeneral.internalModuleBaudrate), 0);
|
||||
|
||||
#if defined(HARDWARE_EXTERNAL_MODULE)
|
||||
if (attr && menuHorizontalPosition == 0 &&
|
||||
moduleIdx == EXTERNAL_MODULE) {
|
||||
if (s_editMode > 0) {
|
||||
@@ -1048,6 +1054,7 @@ void menuModelSetup(event_t event)
|
||||
reusableBuffer.moduleSetup.newType;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
if (attr) {
|
||||
if (s_editMode > 0) {
|
||||
switch (menuHorizontalPosition) {
|
||||
@@ -1177,17 +1184,17 @@ void menuModelSetup(event_t event)
|
||||
lcdDrawText(INDENT_WIDTH, y, STR_STATUS);
|
||||
lcdDrawNumber(MODEL_SETUP_2ND_COLUMN, y, 1000000 / getMixerSchedulerPeriod(), LEFT | attr);
|
||||
lcdDrawText(lcdNextPos, y, "Hz ", attr);
|
||||
lcdDrawNumber(lcdNextPos, y, telemetryErrors, attr);
|
||||
lcdDrawText(lcdNextPos + 1, y, "Err", attr);
|
||||
if (attr) {
|
||||
s_editMode = 0;
|
||||
if (event == EVT_KEY_LONG(KEY_ENTER)) {
|
||||
START_NO_HIGHLIGHT();
|
||||
telemetryErrors = 0;
|
||||
AUDIO_WARNING1();
|
||||
killEvents(event);
|
||||
}
|
||||
}
|
||||
// lcdDrawNumber(lcdNextPos, y, telemetryErrors, attr);
|
||||
// lcdDrawText(lcdNextPos + 1, y, "Err", attr);
|
||||
// if (attr) {
|
||||
// s_editMode = 0;
|
||||
// if (event == EVT_KEY_LONG(KEY_ENTER)) {
|
||||
// START_NO_HIGHLIGHT();
|
||||
// telemetryErrors = 0;
|
||||
// AUDIO_WARNING1();
|
||||
// killEvents(event);
|
||||
// }
|
||||
// }
|
||||
break;
|
||||
#endif
|
||||
|
||||
|
||||
@@ -61,10 +61,10 @@ enum MenuModelTelemetryFrskyItems {
|
||||
#define VARIO_ROWS
|
||||
#endif
|
||||
|
||||
#if defined(PCBTARANIS)
|
||||
#if defined(HARDWARE_EXTERNAL_MODULE)
|
||||
#define TELEMETRY_TYPE_ROW (!IS_INTERNAL_MODULE_ENABLED() && g_model.moduleData[EXTERNAL_MODULE].type == MODULE_TYPE_PPM) ? (uint8_t)0 : HIDDEN_ROW
|
||||
#else
|
||||
#define TELEMETRY_TYPE_ROW (g_model.moduleData[EXTERNAL_MODULE].type == MODULE_TYPE_PPM) ? (uint8_t)0 : HIDDEN_ROW
|
||||
#define TELEMETRY_TYPE_ROW HIDDEN_ROW
|
||||
#endif
|
||||
|
||||
#define TELEMETRY_TYPE_ROWS TELEMETRY_TYPE_ROW,
|
||||
@@ -174,9 +174,12 @@ void menuModelTelemetry(event_t event)
|
||||
switch (k) {
|
||||
case ITEM_TELEMETRY_PROTOCOL_TYPE:
|
||||
lcdDrawTextAlignedLeft(y, STR_TELEMETRY_TYPE);
|
||||
lcdDrawTextAtIndex(TELEM_COL2, y, STR_TELEMETRY_PROTOCOLS, g_model.telemetryProtocol, attr);
|
||||
lcdDrawTextAtIndex(TELEM_COL2, y, STR_TELEMETRY_PROTOCOLS,
|
||||
g_model.telemetryProtocol, attr);
|
||||
if (attr) {
|
||||
g_model.telemetryProtocol = checkIncDec(event, g_model.telemetryProtocol, PROTOCOL_TELEMETRY_FIRST, PROTOCOL_TELEMETRY_LAST, EE_MODEL, isTelemetryProtocolAvailable);
|
||||
g_model.telemetryProtocol = checkIncDec(
|
||||
event, g_model.telemetryProtocol, PROTOCOL_TELEMETRY_FIRST,
|
||||
PROTOCOL_TELEMETRY_LAST, EE_MODEL, isTelemetryProtocolAvailable);
|
||||
}
|
||||
break;
|
||||
|
||||
|
||||
@@ -26,7 +26,7 @@ enum SensorFields {
|
||||
SENSOR_FIELD_TYPE,
|
||||
SENSOR_FIELD_ID,
|
||||
SENSOR_FIELD_FORMULA = SENSOR_FIELD_ID,
|
||||
SENSOR_FILED_RECEIVER_NAME,
|
||||
// SENSOR_FIELD_RECEIVER_NAME,
|
||||
SENSOR_FIELD_UNIT,
|
||||
SENSOR_FIELD_PRECISION,
|
||||
SENSOR_FIELD_PARAM1,
|
||||
@@ -54,7 +54,7 @@ void menuModelSensor(event_t event)
|
||||
0, // Name
|
||||
0, // Type
|
||||
sensor->type == TELEM_TYPE_CALCULATED ? (uint8_t)0 : (uint8_t)1, // ID / Formula
|
||||
sensor->type == TELEM_TYPE_CALCULATED ? HIDDEN_ROW : READONLY_ROW, // Receiver name
|
||||
// sensor->type == TELEM_TYPE_CALCULATED ? HIDDEN_ROW : READONLY_ROW, // Receiver name
|
||||
((sensor->type == TELEM_TYPE_CALCULATED && (sensor->formula == TELEM_FORMULA_DIST)) || sensor->isConfigurable() ? (uint8_t)0 : HIDDEN_ROW), // Unit
|
||||
(sensor->isPrecConfigurable() && sensor->unit != UNIT_FAHRENHEIT ? (uint8_t)0 : HIDDEN_ROW), // Precision
|
||||
(sensor->unit >= UNIT_FIRST_VIRTUAL ? HIDDEN_ROW : (uint8_t)0), // Param1
|
||||
@@ -145,21 +145,24 @@ void menuModelSensor(event_t event)
|
||||
}
|
||||
break;
|
||||
|
||||
case SENSOR_FILED_RECEIVER_NAME:
|
||||
lcdDrawTextAlignedLeft(y, STR_SOURCE);
|
||||
if (telemetryProtocol == PROTOCOL_TELEMETRY_FRSKY_SPORT && sensor->frskyInstance.rxIndex != TELEMETRY_ENDPOINT_SPORT) {
|
||||
drawReceiverName(SENSOR_2ND_COLUMN, y, sensor->frskyInstance.rxIndex >> 2, sensor->frskyInstance.rxIndex & 0x03, 0);
|
||||
}
|
||||
#if defined(HARDWARE_INTERNAL_MODULE)
|
||||
else if (isModuleUsingSport(INTERNAL_MODULE, g_model.moduleData[INTERNAL_MODULE].type)) {
|
||||
// far from perfect
|
||||
lcdDrawText(SENSOR_2ND_COLUMN, y, STR_INTERNAL_MODULE);
|
||||
}
|
||||
#endif
|
||||
else {
|
||||
lcdDrawText(SENSOR_2ND_COLUMN, y, STR_EXTERNAL_MODULE);
|
||||
}
|
||||
break;
|
||||
// TODO: this needs to be known from the sensor data alone!
|
||||
// case SENSOR_FIELD_RECEIVER_NAME:
|
||||
// lcdDrawTextAlignedLeft(y, STR_SOURCE);
|
||||
// if (telemetryProtocol == PROTOCOL_TELEMETRY_FRSKY_SPORT &&
|
||||
// sensor->frskyInstance.rxIndex != TELEMETRY_ENDPOINT_SPORT) {
|
||||
// drawReceiverName(SENSOR_2ND_COLUMN, y, sensor->frskyInstance.rxIndex >> 2,
|
||||
// sensor->frskyInstance.rxIndex & 0x03, 0);
|
||||
// }
|
||||
// #if defined(HARDWARE_INTERNAL_MODULE)
|
||||
// else if (isModuleUsingSport(INTERNAL_MODULE, g_model.moduleData[INTERNAL_MODULE].type)) {
|
||||
// // far from perfect
|
||||
// lcdDrawText(SENSOR_2ND_COLUMN, y, STR_INTERNAL_MODULE);
|
||||
// }
|
||||
// #endif
|
||||
// else {
|
||||
// lcdDrawText(SENSOR_2ND_COLUMN, y, STR_EXTERNAL_MODULE);
|
||||
// }
|
||||
// break;
|
||||
|
||||
case SENSOR_FIELD_UNIT:
|
||||
lcdDrawTextAlignedLeft(y, STR_UNIT);
|
||||
|
||||
@@ -22,6 +22,7 @@
|
||||
#define LANGUAGE_PACKS_DEFINITION
|
||||
|
||||
#include "opentx.h"
|
||||
#include "tasks/mixer_task.h"
|
||||
|
||||
const unsigned char sticks[] = {
|
||||
#include "sticks.lbm"
|
||||
@@ -693,10 +694,10 @@ void menuRadioSetup(event_t event)
|
||||
CHECK_INCDEC_GENVAR(event, reusableBuffer.generalSettings.stickMode, 0, 3);
|
||||
}
|
||||
else if (reusableBuffer.generalSettings.stickMode != g_eeGeneral.stickMode) {
|
||||
pausePulses();
|
||||
mixerTaskStop();
|
||||
g_eeGeneral.stickMode = reusableBuffer.generalSettings.stickMode;
|
||||
checkThrottleStick();
|
||||
resumePulses();
|
||||
mixerTaskStart();
|
||||
waitKeysReleased();
|
||||
}
|
||||
break;
|
||||
|
||||
@@ -20,6 +20,11 @@
|
||||
*/
|
||||
|
||||
#include "opentx.h"
|
||||
#include "tasks.h"
|
||||
|
||||
#if defined(BLUETOOTH)
|
||||
#include "bluetooth_driver.h"
|
||||
#endif
|
||||
|
||||
#define STATS_1ST_COLUMN 1
|
||||
#define STATS_2ND_COLUMN 7*FW+FW/2
|
||||
@@ -228,9 +233,9 @@ void menuStatisticsDebug2(event_t event)
|
||||
title(STR_MENUDEBUG);
|
||||
|
||||
switch(event) {
|
||||
case EVT_KEY_FIRST(KEY_ENTER):
|
||||
telemetryErrors = 0;
|
||||
break;
|
||||
// case EVT_KEY_FIRST(KEY_ENTER):
|
||||
// telemetryErrors = 0;
|
||||
// break;
|
||||
|
||||
case EVT_KEY_FIRST(KEY_UP):
|
||||
#if defined(KEYS_GPIO_REG_PAGEDN)
|
||||
@@ -258,8 +263,8 @@ void menuStatisticsDebug2(event_t event)
|
||||
|
||||
uint8_t y = FH + 1;
|
||||
|
||||
lcdDrawTextAlignedLeft(y, "Tlm RX Err");
|
||||
lcdDrawNumber(MENU_DEBUG_COL1_OFS, y, telemetryErrors, RIGHT);
|
||||
// lcdDrawTextAlignedLeft(y, "Tlm RX Err");
|
||||
// lcdDrawNumber(MENU_DEBUG_COL1_OFS, y, telemetryErrors, RIGHT);
|
||||
y += FH;
|
||||
|
||||
#if defined(BLUETOOTH)
|
||||
|
||||
@@ -923,7 +923,7 @@ void menuModelSetup(event_t event)
|
||||
{
|
||||
lcdDrawTextAlignedLeft(y, INDENT TR_MODE);
|
||||
lcdDrawTextAtIndex(MODEL_SETUP_2ND_COLUMN, y,
|
||||
STR_INTERNAL_MODULE_PROTOCOLS,
|
||||
STR_MODULE_PROTOCOLS,
|
||||
g_model.moduleData[INTERNAL_MODULE].type,
|
||||
menuHorizontalPosition == 0 ? attr : 0);
|
||||
if (isModuleXJT(INTERNAL_MODULE))
|
||||
@@ -977,7 +977,7 @@ void menuModelSetup(event_t event)
|
||||
|
||||
case ITEM_MODEL_SETUP_EXTERNAL_MODULE_TYPE:
|
||||
lcdDrawTextAlignedLeft(y, INDENT TR_MODE);
|
||||
lcdDrawTextAtIndex(MODEL_SETUP_2ND_COLUMN, y, STR_EXTERNAL_MODULE_PROTOCOLS, reusableBuffer.moduleSetup.newType, menuHorizontalPosition==0 ? attr : 0);
|
||||
lcdDrawTextAtIndex(MODEL_SETUP_2ND_COLUMN, y, STR_MODULE_PROTOCOLS, reusableBuffer.moduleSetup.newType, menuHorizontalPosition==0 ? attr : 0);
|
||||
if (isModuleXJT(EXTERNAL_MODULE))
|
||||
lcdDrawTextAtIndex(lcdNextPos + 3, y, STR_XJT_ACCST_RF_PROTOCOLS, g_model.moduleData[EXTERNAL_MODULE].subType, menuHorizontalPosition==1 ? attr : 0);
|
||||
else if (isModuleDSM2(EXTERNAL_MODULE))
|
||||
@@ -1110,17 +1110,17 @@ void menuModelSetup(event_t event)
|
||||
lcdDrawText(INDENT_WIDTH, y, STR_STATUS);
|
||||
lcdDrawNumber(MODEL_SETUP_2ND_COLUMN, y, 1000000 / getMixerSchedulerPeriod(), LEFT | attr);
|
||||
lcdDrawText(lcdNextPos, y, "Hz ", attr);
|
||||
lcdDrawNumber(lcdNextPos, y, telemetryErrors, attr);
|
||||
lcdDrawText(lcdNextPos + 1, y, "Err", attr);
|
||||
if (attr) {
|
||||
s_editMode = 0;
|
||||
if (event == EVT_KEY_LONG(KEY_ENTER)) {
|
||||
START_NO_HIGHLIGHT();
|
||||
telemetryErrors = 0;
|
||||
AUDIO_WARNING1();
|
||||
killEvents(event);
|
||||
}
|
||||
}
|
||||
// lcdDrawNumber(lcdNextPos, y, telemetryErrors, attr);
|
||||
// lcdDrawText(lcdNextPos + 1, y, "Err", attr);
|
||||
// if (attr) {
|
||||
// s_editMode = 0;
|
||||
// if (event == EVT_KEY_LONG(KEY_ENTER)) {
|
||||
// START_NO_HIGHLIGHT();
|
||||
// telemetryErrors = 0;
|
||||
// AUDIO_WARNING1();
|
||||
// killEvents(event);
|
||||
// }
|
||||
// }
|
||||
break;
|
||||
#endif
|
||||
|
||||
|
||||
@@ -143,14 +143,6 @@ void menuModelTelemetry(event_t event)
|
||||
lcdDrawText(TELEM_COL2, y, "---", 0); // TODO shortcut
|
||||
}
|
||||
TelemetrySensor * sensor = & g_model.telemetrySensors[index];
|
||||
#if defined(MULTIMODULE)
|
||||
if (IS_SPEKTRUM_PROTOCOL()) {
|
||||
// Spektrum does not (yet?) really support multiple sensor of the same type. But a lot of
|
||||
// different sensor display the same information (e.g. voltage, capacity). Show the id
|
||||
// of the sensor in the overview to ease figuring out what sensors belong together
|
||||
lcdDrawHexNumber(TELEM_COL3, y, sensor->id, LEFT);
|
||||
} else
|
||||
#endif
|
||||
if (sensor->type == TELEM_TYPE_CUSTOM && !g_model.ignoreSensorIds) {
|
||||
lcdDrawNumber(TELEM_COL3, y, sensor->instance, LEFT);
|
||||
}
|
||||
@@ -183,7 +175,7 @@ void menuModelTelemetry(event_t event)
|
||||
case ITEM_TELEMETRY_SENSORS_LABEL:
|
||||
lcdDrawTextAlignedLeft(y, STR_TELEMETRY_SENSORS);
|
||||
lcdDrawText(TELEM_COL2, y, STR_VALUE, 0);
|
||||
if (!g_model.ignoreSensorIds && !IS_SPEKTRUM_PROTOCOL()) {
|
||||
if (!g_model.ignoreSensorIds /*&& !IS_SPEKTRUM_PROTOCOL()*/) {
|
||||
lcdDrawText(TELEM_COL3, y, STR_ID, 0);
|
||||
}
|
||||
break;
|
||||
|
||||
@@ -26,7 +26,7 @@ enum SensorFields {
|
||||
SENSOR_FIELD_TYPE,
|
||||
SENSOR_FIELD_ID,
|
||||
SENSOR_FIELD_FORMULA=SENSOR_FIELD_ID,
|
||||
SENSOR_FILED_RECEIVER_NAME,
|
||||
// SENSOR_FIELD_RECEIVER_NAME,
|
||||
SENSOR_FIELD_UNIT,
|
||||
SENSOR_FIELD_PRECISION,
|
||||
SENSOR_FIELD_PARAM1,
|
||||
@@ -69,7 +69,7 @@ void menuModelSensor(event_t event)
|
||||
0, //Name
|
||||
0, // Type
|
||||
sensor->type == TELEM_TYPE_CALCULATED ? (uint8_t)0 : (uint8_t)1, // ID / Formula
|
||||
sensor->type == TELEM_TYPE_CALCULATED ? HIDDEN_ROW : READONLY_ROW, // Receiver name
|
||||
// sensor->type == TELEM_TYPE_CALCULATED ? HIDDEN_ROW : READONLY_ROW, // Receiver name
|
||||
SENSOR_UNIT_ROWS,
|
||||
SENSOR_PREC_ROWS,
|
||||
SENSOR_PARAM1_ROWS,
|
||||
@@ -151,19 +151,22 @@ void menuModelSensor(event_t event)
|
||||
}
|
||||
break;
|
||||
|
||||
case SENSOR_FILED_RECEIVER_NAME:
|
||||
lcdDrawTextAlignedLeft(y, STR_SOURCE);
|
||||
if (telemetryProtocol == PROTOCOL_TELEMETRY_FRSKY_SPORT && sensor->frskyInstance.rxIndex != TELEMETRY_ENDPOINT_SPORT) {
|
||||
drawReceiverName(SENSOR_2ND_COLUMN, y, sensor->frskyInstance.rxIndex >> 2, sensor->frskyInstance.rxIndex & 0x03, 0);
|
||||
}
|
||||
else if (isModuleUsingSport(INTERNAL_MODULE, g_model.moduleData[INTERNAL_MODULE].type)) {
|
||||
// far from perfect
|
||||
lcdDrawText(SENSOR_2ND_COLUMN, y, STR_INTERNAL_MODULE);
|
||||
}
|
||||
else {
|
||||
lcdDrawText(SENSOR_2ND_COLUMN, y, STR_EXTERNAL_MODULE);
|
||||
}
|
||||
break;
|
||||
// case SENSOR_FIELD_RECEIVER_NAME:
|
||||
// lcdDrawTextAlignedLeft(y, STR_SOURCE);
|
||||
// // TODO: this needs to be known from the sensor data alone!
|
||||
// if (telemetryProtocol == PROTOCOL_TELEMETRY_FRSKY_SPORT &&
|
||||
// sensor->frskyInstance.rxIndex != TELEMETRY_ENDPOINT_SPORT) {
|
||||
// drawReceiverName(SENSOR_2ND_COLUMN, y, sensor->frskyInstance.rxIndex >> 2,
|
||||
// sensor->frskyInstance.rxIndex & 0x03, 0);
|
||||
// }
|
||||
// else if (isModuleUsingSport(INTERNAL_MODULE, g_model.moduleData[INTERNAL_MODULE].type)) {
|
||||
// // far from perfect
|
||||
// lcdDrawText(SENSOR_2ND_COLUMN, y, STR_INTERNAL_MODULE);
|
||||
// }
|
||||
// else {
|
||||
// lcdDrawText(SENSOR_2ND_COLUMN, y, STR_EXTERNAL_MODULE);
|
||||
// }
|
||||
// break;
|
||||
|
||||
case SENSOR_FIELD_UNIT:
|
||||
lcdDrawTextAlignedLeft(y, STR_UNIT);
|
||||
|
||||
@@ -22,6 +22,7 @@
|
||||
#define LANGUAGE_PACKS_DEFINITION
|
||||
|
||||
#include "opentx.h"
|
||||
#include "tasks/mixer_task.h"
|
||||
|
||||
const unsigned char sticks[] = {
|
||||
#include "sticks.lbm"
|
||||
@@ -623,10 +624,10 @@ void menuRadioSetup(event_t event)
|
||||
CHECK_INCDEC_GENVAR(event, reusableBuffer.generalSettings.stickMode, 0, 3);
|
||||
}
|
||||
else if (reusableBuffer.generalSettings.stickMode != g_eeGeneral.stickMode) {
|
||||
pausePulses();
|
||||
mixerTaskStop();
|
||||
g_eeGeneral.stickMode = reusableBuffer.generalSettings.stickMode;
|
||||
checkThrottleStick();
|
||||
resumePulses();
|
||||
mixerTaskStart();
|
||||
waitKeysReleased();
|
||||
}
|
||||
break;
|
||||
|
||||
@@ -20,6 +20,7 @@
|
||||
*/
|
||||
|
||||
#include "opentx.h"
|
||||
#include "tasks.h"
|
||||
|
||||
#define STATS_1ST_COLUMN FW/2
|
||||
#define STATS_2ND_COLUMN 12*FW+FW/2
|
||||
@@ -112,7 +113,7 @@ void menuStatisticsDebug(event_t event)
|
||||
warningResult = 0;
|
||||
// do a user requested watchdog test
|
||||
TRACE("Performing watchdog test");
|
||||
pausePulses();
|
||||
pulsesStop();
|
||||
}
|
||||
#endif
|
||||
|
||||
@@ -232,14 +233,14 @@ void menuStatisticsDebug2(event_t event)
|
||||
chainMenu(menuMainView);
|
||||
break;
|
||||
|
||||
case EVT_KEY_LONG(KEY_ENTER):
|
||||
telemetryErrors = 0;
|
||||
break;
|
||||
// case EVT_KEY_LONG(KEY_ENTER):
|
||||
// telemetryErrors = 0;
|
||||
// break;
|
||||
}
|
||||
|
||||
// UART statistics
|
||||
lcdDrawTextAlignedLeft(MENU_DEBUG_ROW1, "Tlm RX Err");
|
||||
lcdDrawNumber(MENU_DEBUG_COL1_OFS, MENU_DEBUG_ROW1, telemetryErrors, RIGHT);
|
||||
// lcdDrawTextAlignedLeft(MENU_DEBUG_ROW1, "Tlm RX Err");
|
||||
// lcdDrawNumber(MENU_DEBUG_COL1_OFS, MENU_DEBUG_ROW1, telemetryErrors, RIGHT);
|
||||
|
||||
|
||||
lcdDrawText(LCD_W/2, 7*FH+1, STR_MENUTORESET, CENTERED);
|
||||
|
||||
@@ -61,8 +61,9 @@ CrossfireSettings::CrossfireSettings(Window* parent, const FlexGridLayout& g,
|
||||
new StaticText(line, rect_t{}, STR_STATUS, 0, COLOR_THEME_PRIMARY1);
|
||||
new DynamicText(line, rect_t{}, [=] {
|
||||
char msg[64] = "";
|
||||
sprintf(msg, "%d Hz %" PRIu32 " Err", 1000000 / getMixerSchedulerPeriod(),
|
||||
telemetryErrors);
|
||||
// sprintf(msg, "%d Hz %" PRIu32 " Err", 1000000 / getMixerSchedulerPeriod(),
|
||||
// telemetryErrors);
|
||||
sprintf(msg, "%d Hz", 1000000 / getMixerSchedulerPeriod());
|
||||
return std::string(msg);
|
||||
});
|
||||
}
|
||||
|
||||
@@ -376,7 +376,8 @@ void drawGPSSensorValue(BitmapBuffer * dc, coord_t x, coord_t y, TelemetryItem &
|
||||
drawGPSPosition(dc, x, y, telemetryItem.gps.longitude, telemetryItem.gps.latitude, flags);
|
||||
}
|
||||
|
||||
void drawSensorCustomValue(BitmapBuffer * dc, coord_t x, coord_t y, uint8_t sensor, int32_t value, LcdFlags flags)
|
||||
void drawSensorCustomValue(BitmapBuffer* dc, coord_t x, coord_t y,
|
||||
uint8_t sensor, int32_t value, LcdFlags flags)
|
||||
{
|
||||
if (sensor >= MAX_TELEMETRY_SENSORS) {
|
||||
// Lua luaLcdDrawChannel() can call us with a bad value
|
||||
@@ -392,65 +393,19 @@ void drawSensorCustomValue(BitmapBuffer * dc, coord_t x, coord_t y, uint8_t sens
|
||||
else if (telemetrySensor.unit == UNIT_GPS) {
|
||||
drawGPSSensorValue(dc, x, y, telemetryItem, flags);
|
||||
}
|
||||
else if (telemetrySensor.unit == UNIT_BITFIELD) {
|
||||
if (IS_FRSKY_SPORT_PROTOCOL()) {
|
||||
if (telemetrySensor.id >= RBOX_STATE_FIRST_ID && telemetrySensor.id <= RBOX_STATE_LAST_ID) {
|
||||
if (telemetrySensor.subId == 0) {
|
||||
if (value == 0) {
|
||||
dc->drawText(x, y, "OK", flags);
|
||||
}
|
||||
else {
|
||||
for (uint8_t i=0; i<16; i++) {
|
||||
if (value & (1 << i)) {
|
||||
char s[] = "CH__ KO";
|
||||
strAppendUnsigned(&s[2], i+1, 2);
|
||||
dc->drawText(x, flags & FONT(XL) ? y+1 : y, s, flags & ~FONT(XL));
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
else {
|
||||
if (value == 0) {
|
||||
dc->drawText(x, flags & FONT(XL) ? y+1 : y, "Rx OK", flags & ~FONT(XL));
|
||||
}
|
||||
else {
|
||||
static const char * const RXS_STATUS[] = {
|
||||
"Rx1 Ovl",
|
||||
"Rx2 Ovl",
|
||||
"SBUS Ovl",
|
||||
"Rx1 FS",
|
||||
"Rx1 LF",
|
||||
"Rx2 FS",
|
||||
"Rx2 LF",
|
||||
"Rx1 Lost",
|
||||
"Rx2 Lost",
|
||||
"Rx1 NS",
|
||||
"Rx2 NS",
|
||||
};
|
||||
for (uint8_t i=0; i<DIM(RXS_STATUS); i++) {
|
||||
if (value & (1<<i)) {
|
||||
dc->drawText(x, flags & FONT(XL) ? y+1 : y, RXS_STATUS[i], flags & ~FONT(XL));
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
else if (telemetrySensor.unit == UNIT_TEXT) {
|
||||
dc->drawSizedText(x, flags & FONT(XL) ? y+1 : y, telemetryItem.text, sizeof(telemetryItem.text), flags & ~FONT(XL));
|
||||
}
|
||||
else {
|
||||
dc->drawSizedText(x, flags & FONT(XL) ? y + 1 : y, telemetryItem.text,
|
||||
sizeof(telemetryItem.text), flags & ~FONT(XL));
|
||||
} else {
|
||||
if (telemetrySensor.prec > 0) {
|
||||
flags |= (telemetrySensor.prec==1 ? PREC1 : PREC2);
|
||||
}
|
||||
drawValueWithUnit(dc, x, y, value, telemetrySensor.unit == UNIT_CELLS ? UNIT_VOLTS : telemetrySensor.unit, flags);
|
||||
drawValueWithUnit(dc, x, y, value,
|
||||
telemetrySensor.unit == UNIT_CELLS ? UNIT_VOLTS : telemetrySensor.unit,
|
||||
flags);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void drawTimer(BitmapBuffer * dc, coord_t x, coord_t y, int32_t tme, LcdFlags flags)
|
||||
{
|
||||
char str[LEN_TIMER_STRING];
|
||||
|
||||
@@ -47,7 +47,7 @@ InternalModuleWindow::InternalModuleWindow(Window *parent) :
|
||||
lv_obj_set_style_grid_cell_x_align(box->getLvObj(), LV_GRID_ALIGN_STRETCH, 0);
|
||||
|
||||
auto internalModule = new Choice(
|
||||
box, rect_t{}, STR_INTERNAL_MODULE_PROTOCOLS, MODULE_TYPE_NONE,
|
||||
box, rect_t{}, STR_MODULE_PROTOCOLS, MODULE_TYPE_NONE,
|
||||
MODULE_TYPE_COUNT - 1, GET_DEFAULT(g_eeGeneral.internalModule),
|
||||
[=](int type) { return setModuleType(type); });
|
||||
|
||||
|
||||
@@ -29,6 +29,7 @@
|
||||
#include "input_mix_group.h"
|
||||
#include "input_mix_button.h"
|
||||
|
||||
#include "tasks/mixer_task.h"
|
||||
#include <algorithm>
|
||||
|
||||
#define SET_DIRTY() storageDirty(EE_MODEL)
|
||||
@@ -52,7 +53,7 @@ uint8_t getExposCount()
|
||||
//
|
||||
void copyExpo(uint8_t source, uint8_t dest, uint8_t input)
|
||||
{
|
||||
pauseMixerCalculations();
|
||||
mixerTaskStop();
|
||||
ExpoData sourceExpo;
|
||||
memcpy(&sourceExpo, expoAddress(source), sizeof(ExpoData));
|
||||
ExpoData *expo = expoAddress(dest);
|
||||
@@ -60,13 +61,13 @@ void copyExpo(uint8_t source, uint8_t dest, uint8_t input)
|
||||
memmove(expo + 1, expo, trailingExpos * sizeof(ExpoData));
|
||||
memcpy(expo, &sourceExpo, sizeof(ExpoData));
|
||||
expo->chn = input;
|
||||
resumeMixerCalculations();
|
||||
mixerTaskStart();
|
||||
storageDirty(EE_MODEL);
|
||||
}
|
||||
|
||||
void deleteExpo(uint8_t idx)
|
||||
{
|
||||
pauseMixerCalculations();
|
||||
mixerTaskStop();
|
||||
ExpoData * expo = expoAddress(idx);
|
||||
int input = expo->chn;
|
||||
memmove(expo, expo+1, (MAX_EXPOS-(idx+1))*sizeof(ExpoData));
|
||||
@@ -74,7 +75,7 @@ void deleteExpo(uint8_t idx)
|
||||
if (!isInputAvailable(input)) {
|
||||
memclear(&g_model.inputNames[input], LEN_INPUT_NAME);
|
||||
}
|
||||
resumeMixerCalculations();
|
||||
mixerTaskStart();
|
||||
storageDirty(EE_MODEL);
|
||||
}
|
||||
|
||||
@@ -85,7 +86,7 @@ int8_t s_copySrcRow;
|
||||
|
||||
void insertExpo(uint8_t idx, uint8_t input)
|
||||
{
|
||||
pauseMixerCalculations();
|
||||
mixerTaskStop();
|
||||
ExpoData * expo = expoAddress(idx);
|
||||
memmove(expo+1, expo, (MAX_EXPOS-(idx+1))*sizeof(ExpoData));
|
||||
memclear(expo, sizeof(ExpoData));
|
||||
@@ -94,7 +95,7 @@ void insertExpo(uint8_t idx, uint8_t input)
|
||||
expo->mode = 3; // pos+neg
|
||||
expo->chn = input;
|
||||
expo->weight = 100;
|
||||
resumeMixerCalculations();
|
||||
mixerTaskStart();
|
||||
storageDirty(EE_MODEL);
|
||||
}
|
||||
|
||||
|
||||
@@ -31,6 +31,8 @@
|
||||
#include "input_mix_button.h"
|
||||
#include "mixer_edit.h"
|
||||
|
||||
#include "tasks/mixer_task.h"
|
||||
|
||||
#define SET_DIRTY() storageDirty(EE_MODEL)
|
||||
#define PASTE_BEFORE -2
|
||||
#define PASTE_AFTER -1
|
||||
@@ -66,7 +68,7 @@ uint8_t getMixesCount()
|
||||
|
||||
void insertMix(uint8_t idx, uint8_t channel)
|
||||
{
|
||||
pauseMixerCalculations();
|
||||
mixerTaskStop();
|
||||
MixData *mix = mixAddress(idx);
|
||||
memmove(mix + 1, mix, (MAX_MIXERS - (idx + 1)) * sizeof(MixData));
|
||||
memclear(mix, sizeof(MixData));
|
||||
@@ -80,17 +82,17 @@ void insertMix(uint8_t idx, uint8_t channel)
|
||||
}
|
||||
}
|
||||
mix->weight = 100;
|
||||
resumeMixerCalculations();
|
||||
mixerTaskStart();
|
||||
storageDirty(EE_MODEL);
|
||||
}
|
||||
|
||||
void deleteMix(uint8_t idx)
|
||||
{
|
||||
pauseMixerCalculations();
|
||||
mixerTaskStop();
|
||||
MixData * mix = mixAddress(idx);
|
||||
memmove(mix, mix + 1, (MAX_MIXERS - (idx + 1)) * sizeof(MixData));
|
||||
memclear(&g_model.mixData[MAX_MIXERS - 1], sizeof(MixData));
|
||||
resumeMixerCalculations();
|
||||
mixerTaskStart();
|
||||
storageDirty(EE_MODEL);
|
||||
}
|
||||
|
||||
@@ -106,7 +108,7 @@ void insertMix(uint8_t idx)
|
||||
|
||||
void copyMix(uint8_t source, uint8_t dest, int8_t ch)
|
||||
{
|
||||
pauseMixerCalculations();
|
||||
mixerTaskStop();
|
||||
MixData sourceMix;
|
||||
memcpy(&sourceMix, mixAddress(source), sizeof(MixData));
|
||||
MixData *mix = mixAddress(dest);
|
||||
@@ -114,7 +116,7 @@ void copyMix(uint8_t source, uint8_t dest, int8_t ch)
|
||||
memmove(mix + 1, mix, trailingMixes * sizeof(MixData));
|
||||
memcpy(mix, &sourceMix, sizeof(MixData));
|
||||
mix->destCh = ch;
|
||||
resumeMixerCalculations();
|
||||
mixerTaskStart();
|
||||
storageDirty(EE_MODEL);
|
||||
}
|
||||
|
||||
@@ -153,9 +155,9 @@ bool swapMixes(uint8_t &idx, uint8_t up)
|
||||
return true;
|
||||
}
|
||||
|
||||
pauseMixerCalculations();
|
||||
mixerTaskStop();
|
||||
memswap(x, y, sizeof(MixData));
|
||||
resumeMixerCalculations();
|
||||
mixerTaskStart();
|
||||
|
||||
idx = tgt_idx;
|
||||
return true;
|
||||
|
||||
@@ -187,9 +187,7 @@ class SensorButton : public Button {
|
||||
lv_obj_set_grid_cell(v->getLvObj(), LV_GRID_ALIGN_STRETCH, 2, 2, LV_GRID_ALIGN_CENTER, 0, 1);
|
||||
|
||||
TelemetrySensor * sensor = & g_model.telemetrySensors[index];
|
||||
if (IS_SPEKTRUM_PROTOCOL()) {
|
||||
sprintf(s, "%x", sensor->id);
|
||||
} else if (sensor->type == TELEM_TYPE_CUSTOM && !g_model.ignoreSensorIds) {
|
||||
if (sensor->type == TELEM_TYPE_CUSTOM && !g_model.ignoreSensorIds) {
|
||||
sprintf(s, "%d", sensor->instance);
|
||||
}
|
||||
|
||||
@@ -589,7 +587,7 @@ void ModelTelemetryPage::build(FormWindow * window, int8_t focusSensorIndex)
|
||||
new StaticText(line, rect_t{}, STR_NAME, 0, FONT(XS) | COLOR_THEME_PRIMARY1);
|
||||
grid2.nextCell();
|
||||
new StaticText(line, rect_t{}, STR_VALUE, 0, FONT(XS) | COLOR_THEME_PRIMARY1);
|
||||
if (!g_model.ignoreSensorIds && !IS_SPEKTRUM_PROTOCOL()) {
|
||||
if (!g_model.ignoreSensorIds) {
|
||||
new StaticText(line, rect_t{}, STR_ID, 0, FONT(XS) | COLOR_THEME_PRIMARY1);
|
||||
}
|
||||
}
|
||||
@@ -821,51 +819,6 @@ std::string getSensorCustomValue(uint8_t sensor, int32_t value, LcdFlags flags)
|
||||
else if (telemetrySensor.unit == UNIT_GPS) {
|
||||
return getGPSSensorValue(telemetryItem, flags);
|
||||
}
|
||||
else if (telemetrySensor.unit == UNIT_BITFIELD) {
|
||||
if (IS_FRSKY_SPORT_PROTOCOL()) {
|
||||
if (telemetrySensor.id >= RBOX_STATE_FIRST_ID && telemetrySensor.id <= RBOX_STATE_LAST_ID) {
|
||||
if (telemetrySensor.subId == 0) {
|
||||
if (value == 0) {
|
||||
return std::string("OK");
|
||||
}
|
||||
else {
|
||||
for (uint8_t i=0; i<16; i++) {
|
||||
if (value & (1 << i)) {
|
||||
char s[] = "CH__ KO";
|
||||
strAppendUnsigned(&s[2], i+1, 2);
|
||||
return std::string(s);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
else {
|
||||
if (value == 0) {
|
||||
return std::string("Rx OK");
|
||||
}
|
||||
else {
|
||||
static const char * const RXS_STATUS[] = {
|
||||
"Rx1 Ovl",
|
||||
"Rx2 Ovl",
|
||||
"SBUS Ovl",
|
||||
"Rx1 FS",
|
||||
"Rx1 LF",
|
||||
"Rx2 FS",
|
||||
"Rx2 LF",
|
||||
"Rx1 Lost",
|
||||
"Rx2 Lost",
|
||||
"Rx1 NS",
|
||||
"Rx2 NS",
|
||||
};
|
||||
for (uint8_t i=0; i<DIM(RXS_STATUS); i++) {
|
||||
if (value & (1<<i)) {
|
||||
return std::string(RXS_STATUS[i]);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
else if (telemetrySensor.unit == UNIT_TEXT) {
|
||||
return std::string(telemetryItem.text);
|
||||
}
|
||||
|
||||
@@ -751,7 +751,7 @@ ModulePage::ModulePage(uint8_t moduleIdx) : Page(ICON_MODEL_SETUP)
|
||||
lv_obj_set_width(box->getLvObj(), LV_SIZE_CONTENT);
|
||||
|
||||
ModuleData* md = &g_model.moduleData[moduleIdx];
|
||||
auto moduleChoice = new Choice(box, rect_t{}, STR_INTERNAL_MODULE_PROTOCOLS,
|
||||
auto moduleChoice = new Choice(box, rect_t{}, STR_MODULE_PROTOCOLS,
|
||||
MODULE_TYPE_NONE, MODULE_TYPE_COUNT - 1,
|
||||
GET_DEFAULT(md->type));
|
||||
|
||||
|
||||
@@ -29,7 +29,7 @@
|
||||
// #endif
|
||||
|
||||
#if defined(RADIO_FAMILY_T16) || defined(PCBNV14)
|
||||
#include "../../targets/horus/flyskyHallStick_driver.h"
|
||||
#include "flysky_gimbal_driver.h"
|
||||
#endif
|
||||
|
||||
#define STATSDEPTH 8 // ideally a value of power of 2
|
||||
|
||||
@@ -26,7 +26,7 @@
|
||||
#include "opentx.h"
|
||||
#include "libopenui.h"
|
||||
#include "hal/adc_driver.h"
|
||||
#include "aux_serial_driver.h"
|
||||
|
||||
#include "hw_intmodule.h"
|
||||
#include "hw_extmodule.h"
|
||||
#include "hw_serial.h"
|
||||
|
||||
@@ -255,10 +255,11 @@ void RadioSdManagerPage::fileAction(const char* path, const char* name,
|
||||
[=]() { BootloaderUpdate(fullpath); });
|
||||
}
|
||||
} else if (!READ_ONLY() && !strcasecmp(ext, SPORT_FIRMWARE_EXT)) {
|
||||
if (HAS_SPORT_UPDATE_CONNECTOR()) {
|
||||
menu->addLine(STR_FLASH_EXTERNAL_DEVICE,
|
||||
[=]() { FrSkyFirmwareUpdate(fullpath, SPORT_MODULE); });
|
||||
}
|
||||
// TODO
|
||||
// if (HAS_SPORT_UPDATE_CONNECTOR()) {
|
||||
// menu->addLine(STR_FLASH_EXTERNAL_DEVICE,
|
||||
// [=]() { FrSkyFirmwareUpdate(fullpath, SPORT_MODULE); });
|
||||
// }
|
||||
menu->addLine(STR_FLASH_INTERNAL_MODULE,
|
||||
[=]() { FrSkyFirmwareUpdate(fullpath, INTERNAL_MODULE); });
|
||||
menu->addLine(STR_FLASH_EXTERNAL_MODULE,
|
||||
@@ -279,15 +280,15 @@ void RadioSdManagerPage::fileAction(const char* path, const char* name,
|
||||
}
|
||||
if (information.productFamily == FIRMWARE_FAMILY_RECEIVER ||
|
||||
information.productFamily == FIRMWARE_FAMILY_SENSOR) {
|
||||
if (HAS_SPORT_UPDATE_CONNECTOR()) {
|
||||
menu->addLine(STR_FLASH_EXTERNAL_DEVICE, [=]() {
|
||||
FrSkyFirmwareUpdate(fullpath, SPORT_MODULE);
|
||||
});
|
||||
} else {
|
||||
menu->addLine(STR_FLASH_EXTERNAL_MODULE, [=]() {
|
||||
FrSkyFirmwareUpdate(fullpath, EXTERNAL_MODULE);
|
||||
});
|
||||
}
|
||||
// if (HAS_SPORT_UPDATE_CONNECTOR()) {
|
||||
// menu->addLine(STR_FLASH_EXTERNAL_DEVICE, [=]() {
|
||||
// FrSkyFirmwareUpdate(fullpath, SPORT_MODULE);
|
||||
// });
|
||||
// } else {
|
||||
menu->addLine(STR_FLASH_EXTERNAL_MODULE, [=]() {
|
||||
FrSkyFirmwareUpdate(fullpath, EXTERNAL_MODULE);
|
||||
});
|
||||
// }
|
||||
}
|
||||
#if _NYI_ // Not yet implemented
|
||||
#if defined(PXX2)
|
||||
@@ -325,14 +326,6 @@ void RadioSdManagerPage::fileAction(const char* path, const char* name,
|
||||
});
|
||||
}
|
||||
#endif
|
||||
#if defined(HARDWARE_POWER_MANAGEMENT_UNIT)
|
||||
if (information.productFamily ==
|
||||
FIRMWARE_FAMILY_POWER_MANAGEMENT_UNIT) {
|
||||
menu->addLine(STR_FLASH_POWER_MANAGEMENT_UNIT, [=]() {
|
||||
FrSkyFirmwareUpdate(fullpath, STR_FLASH_POWER_MANAGEMENT_UNIT);
|
||||
});
|
||||
}
|
||||
#endif
|
||||
#endif // _NYI_
|
||||
}
|
||||
}
|
||||
|
||||
@@ -25,6 +25,8 @@
|
||||
#include "opentx.h"
|
||||
#include "libopenui.h"
|
||||
|
||||
#include "tasks/mixer_task.h"
|
||||
|
||||
#define SET_DIRTY() storageDirty(EE_GENERAL)
|
||||
|
||||
static const lv_coord_t col_two_dsc[] = {LV_GRID_FR(1), LV_GRID_FR(1),
|
||||
@@ -777,11 +779,11 @@ void RadioSetupPage::build(FormWindow * window)
|
||||
grid.setColSpan(2);
|
||||
choice = new Choice(line, rect_t{}, 0, 3, GET_DEFAULT(g_eeGeneral.stickMode),
|
||||
[=](uint8_t newValue) {
|
||||
pausePulses();
|
||||
mixerTaskStop();
|
||||
g_eeGeneral.stickMode = newValue;
|
||||
SET_DIRTY();
|
||||
checkThrottleStick();
|
||||
resumePulses();
|
||||
mixerTaskStart();
|
||||
});
|
||||
choice->setTextHandler([](uint8_t value) {
|
||||
return std::to_string(1 + value) + ": " + STR_LEFT_STICK + " = " +
|
||||
|
||||
@@ -281,15 +281,21 @@ void RadioSpectrumAnalyser::start()
|
||||
|
||||
void RadioSpectrumAnalyser::stop()
|
||||
{
|
||||
#if defined(PXX2)
|
||||
if (isModulePXX2(moduleIdx)) {
|
||||
moduleState[moduleIdx].readModuleInformation(
|
||||
&reusableBuffer.moduleSetup.pxx2.moduleInformation, PXX2_HW_INFO_TX_ID,
|
||||
PXX2_HW_INFO_TX_ID);
|
||||
} else if (isModuleMultimodule(moduleIdx)) {
|
||||
}
|
||||
#endif
|
||||
|
||||
#if defined(MULTIMODULE)
|
||||
if (isModuleMultimodule(moduleIdx)) {
|
||||
moduleState[moduleIdx].mode = MODULE_MODE_NORMAL;
|
||||
if (reusableBuffer.spectrumAnalyser.moduleOFF)
|
||||
setModuleType(INTERNAL_MODULE, MODULE_TYPE_NONE);
|
||||
}
|
||||
#endif
|
||||
/* wait 1s to resume normal operation before leaving */
|
||||
// watchdogSuspend(1000);
|
||||
// RTOS_WAIT_MS(1000);
|
||||
|
||||
@@ -27,6 +27,7 @@
|
||||
#include "libopenui.h"
|
||||
#include "lua/lua_api.h"
|
||||
#include "standalone_lua.h"
|
||||
#include "hal/module_port.h"
|
||||
|
||||
extern uint8_t g_moduleIdx;
|
||||
|
||||
@@ -44,7 +45,7 @@ void RadioToolsPage::build(FormWindow * window)
|
||||
|
||||
#if defined(PXX2)
|
||||
for (uint8_t module = 0; module < NUM_MODULES; module++) {
|
||||
if (isModulePXX2(module) && (module == INTERNAL_MODULE ? IS_INTERNAL_MODULE_ON() : IS_EXTERNAL_MODULE_ON())) {
|
||||
if (isModulePXX2(module) && (module == INTERNAL_MODULE ? modulePortPowered(INTERNAL_MODULE) : modulePortPowered(EXTERNAL_MODULE))) {
|
||||
waiting |= (1 << module);
|
||||
moduleState[module].readModuleInformation(&reusableBuffer.radioTools.modules[module], PXX2_HW_INFO_TX_ID, PXX2_HW_INFO_TX_ID);
|
||||
}
|
||||
@@ -56,6 +57,7 @@ void RadioToolsPage::build(FormWindow * window)
|
||||
|
||||
void RadioToolsPage::checkEvents()
|
||||
{
|
||||
#if defined(PXX2)
|
||||
bool refresh = false;
|
||||
|
||||
for (uint8_t module = 0; module < NUM_MODULES; module++) {
|
||||
@@ -68,6 +70,7 @@ void RadioToolsPage::checkEvents()
|
||||
if (refresh) {
|
||||
rebuild(window);
|
||||
}
|
||||
#endif
|
||||
|
||||
PageTab::checkEvents();
|
||||
}
|
||||
|
||||
@@ -24,11 +24,13 @@
|
||||
#include "options.h"
|
||||
#include "libopenui.h"
|
||||
#include "fw_version.h"
|
||||
#include "hal/module_port.h"
|
||||
|
||||
#if defined(CROSSFIRE)
|
||||
#include "mixer_scheduler.h"
|
||||
#endif
|
||||
|
||||
#if defined(PXX2)
|
||||
char *getVersion(char *str, PXX2Version version)
|
||||
{
|
||||
if (version.major == 0xFF && version.minor == 0x0F &&
|
||||
@@ -40,6 +42,7 @@ char *getVersion(char *str, PXX2Version version)
|
||||
return str;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
static const lv_coord_t col_dsc[] = {LV_GRID_FR(1), LV_GRID_FR(2), LV_GRID_TEMPLATE_LAST};
|
||||
static const lv_coord_t row_dsc[] = {LV_GRID_CONTENT, LV_GRID_TEMPLATE_LAST};
|
||||
@@ -70,22 +73,24 @@ class VersionDialog : public Dialog
|
||||
VersionDialog(Window *parent) :
|
||||
Dialog(parent, STR_MODULES_RX_VERSION, rect_t{ 0, 0, 200, 100 })
|
||||
{
|
||||
#if defined(PXX2)
|
||||
memclear(&reusableBuffer.hardwareAndSettings.modules,
|
||||
sizeof(reusableBuffer.hardwareAndSettings.modules));
|
||||
reusableBuffer.hardwareAndSettings.updateTime = get_tmr10ms();
|
||||
|
||||
// Query modules
|
||||
if (isModulePXX2(INTERNAL_MODULE) && IS_INTERNAL_MODULE_ON()) {
|
||||
if (isModulePXX2(INTERNAL_MODULE) && modulePortPowered(INTERNAL_MODULE)) {
|
||||
moduleState[INTERNAL_MODULE].readModuleInformation(
|
||||
&reusableBuffer.hardwareAndSettings.modules[INTERNAL_MODULE],
|
||||
PXX2_HW_INFO_TX_ID, PXX2_MAX_RECEIVERS_PER_MODULE - 1);
|
||||
}
|
||||
|
||||
if (isModulePXX2(EXTERNAL_MODULE) && IS_EXTERNAL_MODULE_ON()) {
|
||||
if (isModulePXX2(EXTERNAL_MODULE) && modulePortPowered(EXTERNAL_MODULE)) {
|
||||
moduleState[EXTERNAL_MODULE].readModuleInformation(
|
||||
&reusableBuffer.hardwareAndSettings.modules[EXTERNAL_MODULE],
|
||||
PXX2_HW_INFO_TX_ID, PXX2_MAX_RECEIVERS_PER_MODULE - 1);
|
||||
}
|
||||
#endif
|
||||
|
||||
setCloseWhenClickOutside(true);
|
||||
|
||||
@@ -176,7 +181,7 @@ class VersionDialog : public Dialog
|
||||
// initialize to module does not provide status
|
||||
// PXX2 will overwrite name
|
||||
// CRSF, MPM, NV14 and PXX2 will overwrite status
|
||||
name->setText(STR_INTERNAL_MODULE_PROTOCOLS[g_model.moduleData[module].type]);
|
||||
name->setText(STR_MODULE_PROTOCOLS[g_model.moduleData[module].type]);
|
||||
lv_obj_add_flag(module_status_w->getLvObj(), LV_OBJ_FLAG_HIDDEN);
|
||||
|
||||
#if defined(CROSSFIRE)
|
||||
@@ -185,7 +190,8 @@ class VersionDialog : public Dialog
|
||||
char statusText[64];
|
||||
|
||||
auto hz = 1000000 / getMixerSchedulerPeriod();
|
||||
snprintf(statusText, 64, "%d Hz %" PRIu32 " Err", hz, telemetryErrors);
|
||||
// snprintf(statusText, 64, "%d Hz %" PRIu32 " Err", hz, telemetryErrors);
|
||||
snprintf(statusText, 64, "%d Hz", hz);
|
||||
status->setText(statusText);
|
||||
lv_obj_clear_flag(module_status_w->getLvObj(), LV_OBJ_FLAG_HIDDEN);
|
||||
}
|
||||
@@ -292,16 +298,17 @@ class VersionDialog : public Dialog
|
||||
#endif
|
||||
}
|
||||
|
||||
#if defined(PXX2)
|
||||
void checkEvents() override
|
||||
{
|
||||
if (get_tmr10ms() >= reusableBuffer.hardwareAndSettings.updateTime) {
|
||||
// Query modules
|
||||
if (isModulePXX2(INTERNAL_MODULE) && IS_INTERNAL_MODULE_ON()) {
|
||||
if (isModulePXX2(INTERNAL_MODULE) && modulePortPowered(INTERNAL_MODULE)) {
|
||||
moduleState[INTERNAL_MODULE].readModuleInformation(
|
||||
&reusableBuffer.hardwareAndSettings.modules[INTERNAL_MODULE],
|
||||
PXX2_HW_INFO_TX_ID, PXX2_MAX_RECEIVERS_PER_MODULE - 1);
|
||||
}
|
||||
if (isModulePXX2(EXTERNAL_MODULE) && IS_EXTERNAL_MODULE_ON()) {
|
||||
if (isModulePXX2(EXTERNAL_MODULE) && modulePortPowered(EXTERNAL_MODULE)) {
|
||||
moduleState[EXTERNAL_MODULE].readModuleInformation(
|
||||
&reusableBuffer.hardwareAndSettings.modules[EXTERNAL_MODULE],
|
||||
PXX2_HW_INFO_TX_ID, PXX2_MAX_RECEIVERS_PER_MODULE - 1);
|
||||
@@ -311,6 +318,7 @@ class VersionDialog : public Dialog
|
||||
update();
|
||||
Dialog::checkEvents();
|
||||
}
|
||||
#endif
|
||||
};
|
||||
|
||||
RadioVersionPage::RadioVersionPage():
|
||||
|
||||
@@ -24,6 +24,9 @@
|
||||
#include "opentx.h"
|
||||
#include "draw_functions.h"
|
||||
|
||||
#include "tasks.h"
|
||||
#include "tasks/mixer_task.h"
|
||||
|
||||
static const lv_coord_t col_dsc[] = {LV_GRID_FR(1), LV_GRID_FR(1),
|
||||
LV_GRID_FR(1), LV_GRID_FR(1),
|
||||
LV_GRID_TEMPLATE_LAST};
|
||||
|
||||
@@ -440,89 +440,16 @@ void drawSensorCustomValue(coord_t x, coord_t y, uint8_t sensor, int32_t value,
|
||||
else if (telemetrySensor.unit == UNIT_GPS) {
|
||||
drawGPSSensorValue(x, y, telemetryItem, flags);
|
||||
}
|
||||
else if (telemetrySensor.unit == UNIT_BITFIELD) {
|
||||
if (IS_FRSKY_SPORT_PROTOCOL()) {
|
||||
if (telemetrySensor.id >= RBOX_STATE_FIRST_ID && telemetrySensor.id <= RBOX_STATE_LAST_ID) {
|
||||
if (telemetrySensor.subId == 0) {
|
||||
if (value == 0) {
|
||||
lcdDrawText(x, y, "OK", flags);
|
||||
}
|
||||
else {
|
||||
for (uint8_t i = 0; i < 16; i++) {
|
||||
if (value & (1u << i)) {
|
||||
char s[] = "CH__ KO";
|
||||
strAppendUnsigned(&s[2], i + 1, 2);
|
||||
lcdDrawText(x, flags & DBLSIZE ? y + 1 : y, s, flags & ~DBLSIZE);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
else {
|
||||
if (value == 0) {
|
||||
lcdDrawText(x, flags & DBLSIZE ? y + 1 : y, "Rx OK", flags & ~DBLSIZE);
|
||||
}
|
||||
else {
|
||||
static const char * const RXS_STATUS[] = {
|
||||
"Rx1 Ovl",
|
||||
"Rx2 Ovl",
|
||||
"SBUS Ovl",
|
||||
"Rx1 FS",
|
||||
"Rx1 LF",
|
||||
"Rx2 FS",
|
||||
"Rx2 LF",
|
||||
"Rx1 Lost",
|
||||
"Rx2 Lost",
|
||||
"Rx1 NS",
|
||||
"Rx2 NS",
|
||||
"Rx3 FS",
|
||||
"Rx3 LF",
|
||||
"Rx3 Lost",
|
||||
"Rx3 NS"
|
||||
};
|
||||
for (uint8_t i = 0; i < DIM(RXS_STATUS); i++) {
|
||||
if (value & (1u << i)) {
|
||||
lcdDrawText(x, flags & DBLSIZE ? y + 1 : y, RXS_STATUS[i], flags & ~DBLSIZE);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
else if (telemetrySensor.id >= RB3040_OUTPUT_FIRST_ID && telemetrySensor.id <= RB3040_OUTPUT_LAST_ID) {
|
||||
if (telemetrySensor.subId == 0) {
|
||||
if (value == 0) {
|
||||
lcdDrawText(x, y, "OK", flags);
|
||||
}
|
||||
else {
|
||||
for (uint8_t i = 0; i < 9; i++) {
|
||||
if (value & (1u << i)) {
|
||||
if (i < 8) {
|
||||
char s[] = "CH__ KO";
|
||||
strAppendUnsigned(&s[2], i + 17, 2);
|
||||
lcdDrawText(x, flags & DBLSIZE ? y + 1 : y, s, flags & ~DBLSIZE);
|
||||
break;
|
||||
}
|
||||
else {
|
||||
char s[] = "S.P Ovl";
|
||||
lcdDrawText(x, flags & DBLSIZE ? y + 1 : y, s, flags & ~DBLSIZE);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
else if (telemetrySensor.unit == UNIT_TEXT) {
|
||||
lcdDrawSizedText(x, flags & DBLSIZE ? y + 1 : y, telemetryItem.text, sizeof(telemetryItem.text), flags & ~DBLSIZE);
|
||||
}
|
||||
else {
|
||||
lcdDrawSizedText(x, flags & DBLSIZE ? y + 1 : y, telemetryItem.text,
|
||||
sizeof(telemetryItem.text), flags & ~DBLSIZE);
|
||||
} else {
|
||||
if (telemetrySensor.prec > 0) {
|
||||
flags |= (telemetrySensor.prec == 1 ? PREC1 : PREC2);
|
||||
}
|
||||
drawValueWithUnit(x, y, value, telemetrySensor.unit == UNIT_CELLS ? UNIT_VOLTS : telemetrySensor.unit, flags);
|
||||
drawValueWithUnit(x, y, value,
|
||||
telemetrySensor.unit == UNIT_CELLS ? UNIT_VOLTS : telemetrySensor.unit,
|
||||
flags);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -20,6 +20,7 @@
|
||||
*/
|
||||
|
||||
#include "opentx.h"
|
||||
#include "tasks/mixer_task.h"
|
||||
|
||||
#define _STR_MAX(x) "/" #x
|
||||
#define STR_MAX(x) _STR_MAX(x)
|
||||
@@ -51,7 +52,7 @@ bool reachExposLimit()
|
||||
int8_t s_currCh;
|
||||
void insertExpo(uint8_t idx)
|
||||
{
|
||||
pauseMixerCalculations();
|
||||
mixerTaskStop();
|
||||
ExpoData * expo = expoAddress(idx);
|
||||
memmove(expo+1, expo, (MAX_EXPOS-(idx+1))*sizeof(ExpoData));
|
||||
memclear(expo, sizeof(ExpoData));
|
||||
@@ -65,16 +66,16 @@ void insertExpo(uint8_t idx)
|
||||
expo->mode = 3; // pos+neg
|
||||
expo->chn = s_currCh - 1;
|
||||
expo->weight = 100;
|
||||
resumeMixerCalculations();
|
||||
mixerTaskStart();
|
||||
storageDirty(EE_MODEL);
|
||||
}
|
||||
|
||||
void copyExpo(uint8_t idx)
|
||||
{
|
||||
pauseMixerCalculations();
|
||||
mixerTaskStop();
|
||||
ExpoData * expo = expoAddress(idx);
|
||||
memmove(expo+1, expo, (MAX_EXPOS-(idx+1))*sizeof(ExpoData));
|
||||
resumeMixerCalculations();
|
||||
mixerTaskStart();
|
||||
storageDirty(EE_MODEL);
|
||||
}
|
||||
|
||||
@@ -112,9 +113,9 @@ bool swapExpos(uint8_t & idx, uint8_t up)
|
||||
return true;
|
||||
}
|
||||
|
||||
pauseMixerCalculations();
|
||||
mixerTaskStop();
|
||||
memswap(x, y, sizeof(ExpoData));
|
||||
resumeMixerCalculations();
|
||||
mixerTaskStart();
|
||||
|
||||
idx = tgt_idx;
|
||||
return true;
|
||||
@@ -122,7 +123,7 @@ bool swapExpos(uint8_t & idx, uint8_t up)
|
||||
|
||||
void deleteExpo(uint8_t idx)
|
||||
{
|
||||
pauseMixerCalculations();
|
||||
mixerTaskStop();
|
||||
ExpoData * expo = expoAddress(idx);
|
||||
int input = expo->chn;
|
||||
memmove(expo, expo+1, (MAX_EXPOS-(idx+1))*sizeof(ExpoData));
|
||||
@@ -130,7 +131,7 @@ void deleteExpo(uint8_t idx)
|
||||
if (!isInputAvailable(input)) {
|
||||
memclear(&g_model.inputNames[input], LEN_INPUT_NAME);
|
||||
}
|
||||
resumeMixerCalculations();
|
||||
mixerTaskStart();
|
||||
storageDirty(EE_MODEL);
|
||||
}
|
||||
|
||||
|
||||
@@ -20,6 +20,7 @@
|
||||
*/
|
||||
|
||||
#include "opentx.h"
|
||||
#include "tasks/mixer_task.h"
|
||||
|
||||
#define _STR_MAX(x) "/" #x
|
||||
#define STR_MAX(x) _STR_MAX(x)
|
||||
@@ -49,17 +50,17 @@ bool reachMixesLimit()
|
||||
|
||||
void deleteMix(uint8_t idx)
|
||||
{
|
||||
pauseMixerCalculations();
|
||||
mixerTaskStop();
|
||||
MixData * mix = mixAddress(idx);
|
||||
memmove(mix, mix+1, (MAX_MIXERS-(idx+1))*sizeof(MixData));
|
||||
memclear(&g_model.mixData[MAX_MIXERS-1], sizeof(MixData));
|
||||
resumeMixerCalculations();
|
||||
mixerTaskStart();
|
||||
storageDirty(EE_MODEL);
|
||||
}
|
||||
|
||||
void insertMix(uint8_t idx)
|
||||
{
|
||||
pauseMixerCalculations();
|
||||
mixerTaskStop();
|
||||
MixData * mix = mixAddress(idx);
|
||||
memmove(mix+1, mix, (MAX_MIXERS-(idx+1))*sizeof(MixData));
|
||||
memclear(mix, sizeof(MixData));
|
||||
@@ -72,16 +73,16 @@ void insertMix(uint8_t idx)
|
||||
}
|
||||
}
|
||||
mix->weight = 100;
|
||||
resumeMixerCalculations();
|
||||
mixerTaskStart();
|
||||
storageDirty(EE_MODEL);
|
||||
}
|
||||
|
||||
void copyMix(uint8_t idx)
|
||||
{
|
||||
pauseMixerCalculations();
|
||||
mixerTaskStop();
|
||||
MixData * mix = mixAddress(idx);
|
||||
memmove(mix+1, mix, (MAX_MIXERS-(idx+1))*sizeof(MixData));
|
||||
resumeMixerCalculations();
|
||||
mixerTaskStart();
|
||||
storageDirty(EE_MODEL);
|
||||
}
|
||||
|
||||
@@ -120,9 +121,9 @@ bool swapMixes(uint8_t & idx, uint8_t up)
|
||||
return true;
|
||||
}
|
||||
|
||||
pauseMixerCalculations();
|
||||
mixerTaskStop();
|
||||
memswap(x, y, sizeof(MixData));
|
||||
resumeMixerCalculations();
|
||||
mixerTaskStart();
|
||||
|
||||
idx = tgt_idx;
|
||||
return true;
|
||||
|
||||
@@ -21,9 +21,9 @@
|
||||
|
||||
#include "opentx.h"
|
||||
|
||||
#if defined(GHOST)
|
||||
#include "telemetry/ghost.h"
|
||||
#endif
|
||||
#if defined(GHOST) && defined(HARDWARE_EXTERNAL_MODULE)
|
||||
|
||||
#include "telemetry/ghost.h"
|
||||
|
||||
void menuGhostModuleConfig(event_t event)
|
||||
{
|
||||
@@ -126,3 +126,5 @@ void menuGhostModuleConfig(event_t event)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
@@ -21,12 +21,15 @@
|
||||
|
||||
#include <opentx.h>
|
||||
#include "hal/adc_driver.h"
|
||||
#include "aux_serial_driver.h"
|
||||
|
||||
#if defined(CROSSFIRE)
|
||||
#include "telemetry/crossfire.h"
|
||||
#endif
|
||||
|
||||
#if defined(BLUETOOTH)
|
||||
#include "bluetooth_driver.h"
|
||||
#endif
|
||||
|
||||
#if defined(PCBTARANIS)
|
||||
enum {
|
||||
ITEM_RADIO_HARDWARE_LABEL_STICKS,
|
||||
@@ -532,7 +535,7 @@ void menuRadioHardware(event_t event)
|
||||
case ITEM_RADIO_HARDWARE_INTERNAL_MODULE_TYPE: {
|
||||
g_eeGeneral.internalModule =
|
||||
editChoice(HW_SETTINGS_COLUMN2, y, STR_TYPE,
|
||||
STR_INTERNAL_MODULE_PROTOCOLS,
|
||||
STR_MODULE_PROTOCOLS,
|
||||
g_eeGeneral.internalModule, MODULE_TYPE_NONE,
|
||||
MODULE_TYPE_MAX, attr, event, isInternalModuleSupported);
|
||||
if (g_model.moduleData[INTERNAL_MODULE].type !=
|
||||
@@ -663,14 +666,15 @@ void menuRadioHardware(event_t event)
|
||||
lcdDrawText(lcdNextPos, y, "---");
|
||||
break;
|
||||
|
||||
#if defined(SPORT_UPDATE_PWR_GPIO)
|
||||
case ITEM_RADIO_HARDWARE_SPORT_UPDATE_POWER:
|
||||
g_eeGeneral.sportUpdatePower = editChoice(HW_SETTINGS_COLUMN2, y, STR_SPORT_UPDATE_POWER_MODE, STR_SPORT_UPDATE_POWER_MODES, g_eeGeneral.sportUpdatePower, 0, 1, attr, event);
|
||||
if (attr && checkIncDec_Ret) {
|
||||
SPORT_UPDATE_POWER_INIT();
|
||||
}
|
||||
break;
|
||||
#endif
|
||||
// TODO: this is board specific, do something about it!
|
||||
// #if defined(SPORT_UPDATE_PWR_GPIO)
|
||||
// case ITEM_RADIO_HARDWARE_SPORT_UPDATE_POWER:
|
||||
// g_eeGeneral.sportUpdatePower = editChoice(HW_SETTINGS_COLUMN2, y, STR_SPORT_UPDATE_POWER_MODE, STR_SPORT_UPDATE_POWER_MODES, g_eeGeneral.sportUpdatePower, 0, 1, attr, event);
|
||||
// if (attr && checkIncDec_Ret) {
|
||||
// SPORT_UPDATE_POWER_INIT();
|
||||
// }
|
||||
// break;
|
||||
// #endif
|
||||
|
||||
case ITEM_RADIO_HARDWARE_DEBUG:
|
||||
lcdDrawTextAlignedLeft(y, STR_DEBUG);
|
||||
|
||||
@@ -92,6 +92,9 @@ void onSdFormatConfirm(const char * result)
|
||||
}
|
||||
|
||||
#if defined(PXX2)
|
||||
|
||||
#include "pulses/pxx2_ota.h"
|
||||
|
||||
void onUpdateConfirmation(const char * result)
|
||||
{
|
||||
if (result == STR_OK) {
|
||||
@@ -108,26 +111,19 @@ void onUpdateStateChanged()
|
||||
{
|
||||
if (reusableBuffer.sdManager.otaUpdateInformation.step == BIND_INFO_REQUEST) {
|
||||
uint8_t modelId = reusableBuffer.sdManager.otaUpdateInformation.receiverInformation.modelID;
|
||||
if (modelId > 0 && modelId < DIM(PXX2ReceiversNames)) {
|
||||
if (isPXX2ReceiverOptionAvailable(modelId, RECEIVER_OPTION_OTA)) {
|
||||
POPUP_CONFIRMATION(getPXX2ReceiverName(modelId), onUpdateConfirmation);
|
||||
char *tmp = strAppend(reusableBuffer.sdManager.otaReceiverVersion, TR_CURRENT_VERSION);
|
||||
tmp = strAppendUnsigned(tmp, 1 + reusableBuffer.sdManager.otaUpdateInformation.receiverInformation.swVersion.major);
|
||||
*tmp++ = '.';
|
||||
tmp = strAppendUnsigned(tmp, reusableBuffer.sdManager.otaUpdateInformation.receiverInformation.swVersion.minor);
|
||||
*tmp++ = '.';
|
||||
tmp = strAppendUnsigned(tmp, reusableBuffer.sdManager.otaUpdateInformation.receiverInformation.swVersion.revision);
|
||||
SET_WARNING_INFO(reusableBuffer.sdManager.otaReceiverVersion, tmp - reusableBuffer.sdManager.otaReceiverVersion, 0);
|
||||
}
|
||||
else {
|
||||
POPUP_WARNING(STR_OTA_UPDATE_ERROR);
|
||||
SET_WARNING_INFO(STR_UNSUPPORTED_RX, sizeof(TR_UNSUPPORTED_RX) - 1, 0);
|
||||
moduleState[reusableBuffer.sdManager.otaUpdateInformation.module].mode = MODULE_MODE_NORMAL;
|
||||
}
|
||||
if (isPXX2ReceiverOptionAvailable(modelId, RECEIVER_OPTION_OTA)) {
|
||||
POPUP_CONFIRMATION(getPXX2ReceiverName(modelId), onUpdateConfirmation);
|
||||
char *tmp = strAppend(reusableBuffer.sdManager.otaReceiverVersion, TR_CURRENT_VERSION);
|
||||
tmp = strAppendUnsigned(tmp, 1 + reusableBuffer.sdManager.otaUpdateInformation.receiverInformation.swVersion.major);
|
||||
*tmp++ = '.';
|
||||
tmp = strAppendUnsigned(tmp, reusableBuffer.sdManager.otaUpdateInformation.receiverInformation.swVersion.minor);
|
||||
*tmp++ = '.';
|
||||
tmp = strAppendUnsigned(tmp, reusableBuffer.sdManager.otaUpdateInformation.receiverInformation.swVersion.revision);
|
||||
SET_WARNING_INFO(reusableBuffer.sdManager.otaReceiverVersion, tmp - reusableBuffer.sdManager.otaReceiverVersion, 0);
|
||||
}
|
||||
else {
|
||||
POPUP_WARNING(STR_OTA_UPDATE_ERROR);
|
||||
SET_WARNING_INFO(STR_UNKNOWN_RX, sizeof(TR_UNKNOWN_RX) - 1, 0);
|
||||
SET_WARNING_INFO(STR_UNSUPPORTED_RX, sizeof(TR_UNSUPPORTED_RX) - 1, 0);
|
||||
moduleState[reusableBuffer.sdManager.otaUpdateInformation.module].mode = MODULE_MODE_NORMAL;
|
||||
}
|
||||
}
|
||||
@@ -220,12 +216,14 @@ void onSdManagerMenu(const char * result)
|
||||
FrskyDeviceFirmwareUpdate device(INTERNAL_MODULE);
|
||||
device.flashFirmware(lfn, drawProgressScreen);
|
||||
}
|
||||
#if defined(HARDWARE_EXTERNAL_MODULE)
|
||||
else if (result == STR_FLASH_EXTERNAL_MODULE) {
|
||||
// needed on X-Lite (as the R9M needs 2S while the external device flashing port only provides 5V)
|
||||
getSelectionFullPath(lfn);
|
||||
FrskyDeviceFirmwareUpdate device(EXTERNAL_MODULE);
|
||||
device.flashFirmware(lfn, drawProgressScreen);
|
||||
}
|
||||
#endif
|
||||
else if (result == STR_FLASH_EXTERNAL_DEVICE) {
|
||||
getSelectionFullPath(lfn);
|
||||
FrskyDeviceFirmwareUpdate device(SPORT_MODULE);
|
||||
@@ -239,6 +237,7 @@ void onSdManagerMenu(const char * result)
|
||||
device.flashFirmware(lfn, drawProgressScreen);
|
||||
}
|
||||
#endif
|
||||
#if defined(HARDWARE_EXTERNAL_MODULE)
|
||||
else if (result == STR_FLASH_EXTERNAL_MULTI) {
|
||||
getSelectionFullPath(lfn);
|
||||
MultiDeviceFirmwareUpdate device(EXTERNAL_MODULE, MULTI_TYPE_MULTIMODULE);
|
||||
@@ -250,20 +249,14 @@ void onSdManagerMenu(const char * result)
|
||||
device.flashFirmware(lfn, drawProgressScreen);
|
||||
}
|
||||
#endif
|
||||
#endif
|
||||
#if defined(BLUETOOTH)
|
||||
else if (result == STR_FLASH_BLUETOOTH_MODULE) {
|
||||
getSelectionFullPath(lfn);
|
||||
bluetooth.flashFirmware(lfn, drawProgressScreen);
|
||||
}
|
||||
#endif
|
||||
#if defined(HARDWARE_POWER_MANAGEMENT_UNIT)
|
||||
else if (result == STR_FLASH_POWER_MANAGEMENT_UNIT) {
|
||||
getSelectionFullPath(lfn);
|
||||
FrskyChipFirmwareUpdate device;
|
||||
device.flashFirmware(lfn, drawProgressScreen);
|
||||
}
|
||||
#endif
|
||||
#if defined(PXX2)
|
||||
#if defined(PXX2) && defined(HARDWARE_EXTERNAL_MODULE)
|
||||
else if (result == STR_FLASH_RECEIVER_BY_INTERNAL_MODULE_OTA || result == STR_FLASH_RECEIVER_BY_EXTERNAL_MODULE_OTA) {
|
||||
memclear(&reusableBuffer.sdManager.otaUpdateInformation, sizeof(OtaUpdateInformation));
|
||||
getSelectionFullPath(reusableBuffer.sdManager.otaUpdateInformation.filename);
|
||||
@@ -432,29 +425,38 @@ void menuRadioSdManager(event_t _event)
|
||||
}
|
||||
}
|
||||
else if (!READ_ONLY() && !strcasecmp(ext, SPORT_FIRMWARE_EXT)) {
|
||||
if (HAS_SPORT_UPDATE_CONNECTOR())
|
||||
auto mod_desc = modulePortGetModuleDescription(SPORT_MODULE);
|
||||
if (mod_desc && mod_desc->set_pwr)
|
||||
POPUP_MENU_ADD_ITEM(STR_FLASH_EXTERNAL_DEVICE);
|
||||
#if defined(INTERNAL_MODULE_PXX1) || defined(INTERNAL_MODULE_PXX2)
|
||||
POPUP_MENU_ADD_ITEM(STR_FLASH_INTERNAL_MODULE);
|
||||
#endif
|
||||
|
||||
if (g_eeGeneral.internalModule == MODULE_TYPE_XJT_PXX1 ||
|
||||
g_eeGeneral.internalModule == MODULE_TYPE_ISRM_PXX2)
|
||||
POPUP_MENU_ADD_ITEM(STR_FLASH_INTERNAL_MODULE);
|
||||
|
||||
POPUP_MENU_ADD_ITEM(STR_FLASH_EXTERNAL_MODULE);
|
||||
}
|
||||
else if (!READ_ONLY() && !strcasecmp(ext, FRSKY_FIRMWARE_EXT)) {
|
||||
FrSkyFirmwareInformation information;
|
||||
if (readFrSkyFirmwareInformation(line, information) == nullptr) {
|
||||
#if defined(INTERNAL_MODULE_PXX1) || defined(INTERNAL_MODULE_PXX2)
|
||||
if (information.productFamily == FIRMWARE_FAMILY_INTERNAL_MODULE)
|
||||
|
||||
if (information.productFamily == FIRMWARE_FAMILY_INTERNAL_MODULE &&
|
||||
(g_eeGeneral.internalModule == MODULE_TYPE_XJT_PXX1 ||
|
||||
g_eeGeneral.internalModule == MODULE_TYPE_ISRM_PXX2))
|
||||
POPUP_MENU_ADD_ITEM(STR_FLASH_INTERNAL_MODULE);
|
||||
#endif
|
||||
|
||||
if (information.productFamily == FIRMWARE_FAMILY_EXTERNAL_MODULE)
|
||||
POPUP_MENU_ADD_ITEM(STR_FLASH_EXTERNAL_MODULE);
|
||||
if (information.productFamily == FIRMWARE_FAMILY_RECEIVER || information.productFamily == FIRMWARE_FAMILY_SENSOR) {
|
||||
if (HAS_SPORT_UPDATE_CONNECTOR())
|
||||
|
||||
if (information.productFamily == FIRMWARE_FAMILY_RECEIVER ||
|
||||
information.productFamily == FIRMWARE_FAMILY_SENSOR) {
|
||||
|
||||
auto mod_desc = modulePortGetModuleDescription(SPORT_MODULE);
|
||||
if (mod_desc && mod_desc->set_pwr)
|
||||
POPUP_MENU_ADD_ITEM(STR_FLASH_EXTERNAL_DEVICE);
|
||||
else
|
||||
POPUP_MENU_ADD_ITEM(STR_FLASH_EXTERNAL_MODULE);
|
||||
}
|
||||
#if defined(PXX2)
|
||||
#if defined(PXX2) && defined(HARDWARE_EXTERNAL_MODULE)
|
||||
if (information.productFamily == FIRMWARE_FAMILY_RECEIVER) {
|
||||
if (isReceiverOTAEnabledFromModule(INTERNAL_MODULE, information.productId))
|
||||
POPUP_MENU_ADD_ITEM(STR_FLASH_RECEIVER_BY_INTERNAL_MODULE_OTA);
|
||||
@@ -469,10 +471,6 @@ void menuRadioSdManager(event_t _event)
|
||||
#if defined(BLUETOOTH)
|
||||
if (information.productFamily == FIRMWARE_FAMILY_BLUETOOTH_CHIP)
|
||||
POPUP_MENU_ADD_ITEM(STR_FLASH_BLUETOOTH_MODULE);
|
||||
#endif
|
||||
#if defined(HARDWARE_POWER_MANAGEMENT_UNIT)
|
||||
if (information.productFamily == FIRMWARE_FAMILY_POWER_MANAGEMENT_UNIT)
|
||||
POPUP_MENU_ADD_ITEM(STR_FLASH_POWER_MANAGEMENT_UNIT);
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
||||
@@ -44,7 +44,9 @@ void menuRadioSpectrumAnalyser(event_t event)
|
||||
if (menuEvent) {
|
||||
lcdDrawCenteredText(LCD_H/2, STR_STOPPING);
|
||||
lcdRefresh();
|
||||
#if defined(PXX2)
|
||||
moduleState[g_moduleIdx].readModuleInformation(&reusableBuffer.moduleSetup.pxx2.moduleInformation, PXX2_HW_INFO_TX_ID, PXX2_HW_INFO_TX_ID);
|
||||
#endif
|
||||
/* wait 1s to resume normal operation before leaving */
|
||||
watchdogSuspend(500 /*5s*/);
|
||||
RTOS_WAIT_MS(1000);
|
||||
|
||||
@@ -22,6 +22,7 @@
|
||||
#include <algorithm>
|
||||
#include <vector>
|
||||
#include "opentx.h"
|
||||
#include "hal/module_port.h"
|
||||
|
||||
extern uint8_t g_moduleIdx;
|
||||
|
||||
@@ -104,7 +105,7 @@ void menuRadioTools(event_t event)
|
||||
memclear(&reusableBuffer.radioTools, sizeof(reusableBuffer.radioTools));
|
||||
#if defined(PXX2)
|
||||
for (uint8_t module = 0; module < NUM_MODULES; module++) {
|
||||
if (isModulePXX2(module) && (module == INTERNAL_MODULE ? IS_INTERNAL_MODULE_ON() : IS_EXTERNAL_MODULE_ON())) {
|
||||
if (isModulePXX2(module) && (module == INTERNAL_MODULE ? modulePortPowered(INTERNAL_MODULE) : modulePortPowered(EXTERNAL_MODULE))) {
|
||||
moduleState[module].readModuleInformation(&reusableBuffer.radioTools.modules[module], PXX2_HW_INFO_TX_ID, PXX2_HW_INFO_TX_ID);
|
||||
}
|
||||
}
|
||||
@@ -161,12 +162,25 @@ void menuRadioTools(event_t event)
|
||||
if (isPXX2ModuleOptionAvailable(reusableBuffer.radioTools.modules[INTERNAL_MODULE].information.modelID, MODULE_OPTION_POWER_METER))
|
||||
addRadioModuleTool(index++, STR_POWER_METER_INT, menuRadioPowerMeter, INTERNAL_MODULE);
|
||||
#endif
|
||||
|
||||
#if defined(HARDWARE_INTERNAL_MODULE) && defined(MULTIMODULE)
|
||||
if (g_eeGeneral.internalModule == MODULE_TYPE_MULTIMODULE)
|
||||
addRadioModuleTool(index++, STR_SPECTRUM_ANALYSER_INT, menuRadioSpectrumAnalyser, INTERNAL_MODULE);
|
||||
#endif
|
||||
#if defined(PXX2) || defined(MULTIMODULE)
|
||||
if (isPXX2ModuleOptionAvailable(reusableBuffer.radioTools.modules[EXTERNAL_MODULE].information.modelID, MODULE_OPTION_SPECTRUM_ANALYSER) || isModuleMultimodule(EXTERNAL_MODULE))
|
||||
|
||||
#if defined(HARDWARE_EXTERNAL_MODULE)
|
||||
|
||||
#if (defined(PXX2) || defined(MULTIMODULE))
|
||||
bool has_spectrum_analyser = false;
|
||||
#if defined(PXX2)
|
||||
if (isPXX2ModuleOptionAvailable(reusableBuffer.radioTools.modules[EXTERNAL_MODULE].information.modelID, MODULE_OPTION_SPECTRUM_ANALYSER))
|
||||
has_spectrum_analyser = true;
|
||||
#endif
|
||||
#if defined(MULTIMODULE)
|
||||
if (isModuleMultimodule(EXTERNAL_MODULE))
|
||||
has_spectrum_analyser = true;
|
||||
#endif
|
||||
if (has_spectrum_analyser)
|
||||
addRadioModuleTool(index++, STR_SPECTRUM_ANALYSER_EXT, menuRadioSpectrumAnalyser, EXTERNAL_MODULE);
|
||||
#endif
|
||||
#if defined(PXX2)
|
||||
@@ -177,6 +191,8 @@ void menuRadioTools(event_t event)
|
||||
#if defined(GHOST)
|
||||
if (isModuleGhost(EXTERNAL_MODULE))
|
||||
addRadioModuleTool(index++, "Ghost Menu", menuGhostModuleConfig, EXTERNAL_MODULE);
|
||||
#endif
|
||||
|
||||
#endif
|
||||
|
||||
if (index == 0) {
|
||||
|
||||
@@ -21,6 +21,8 @@
|
||||
|
||||
#include "opentx.h"
|
||||
#include "options.h"
|
||||
#include "hal/module_port.h"
|
||||
|
||||
#if defined(CROSSFIRE)
|
||||
#include "mixer_scheduler.h"
|
||||
#endif
|
||||
@@ -95,8 +97,9 @@ void drawPXX2FullVersion(coord_t x, coord_t y, PXX2Version hwVersion, PXX2Versio
|
||||
void menuRadioModulesVersion(event_t event)
|
||||
{
|
||||
if (menuEvent) {
|
||||
moduleState[INTERNAL_MODULE].mode = MODULE_MODE_NORMAL;
|
||||
moduleState[EXTERNAL_MODULE].mode = MODULE_MODE_NORMAL;
|
||||
for (uint8_t i = 0; i < MAX_MODULES; i++) {
|
||||
moduleState[i].mode = MODULE_MODE_NORMAL;
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -107,13 +110,18 @@ void menuRadioModulesVersion(event_t event)
|
||||
}
|
||||
|
||||
if (event == EVT_ENTRY || get_tmr10ms() >= reusableBuffer.hardwareAndSettings.updateTime) {
|
||||
if (isModulePXX2(INTERNAL_MODULE) && IS_INTERNAL_MODULE_ON()) {
|
||||
|
||||
#if defined(HARDWARE_INTERNAL_MODULE)
|
||||
if (isModulePXX2(INTERNAL_MODULE) && modulePortPowered(INTERNAL_MODULE)) {
|
||||
moduleState[INTERNAL_MODULE].readModuleInformation(&reusableBuffer.hardwareAndSettings.modules[INTERNAL_MODULE], PXX2_HW_INFO_TX_ID, PXX2_MAX_RECEIVERS_PER_MODULE - 1);
|
||||
}
|
||||
#endif
|
||||
|
||||
if (isModulePXX2(EXTERNAL_MODULE) && IS_EXTERNAL_MODULE_ON()) {
|
||||
#if defined(HARDWARE_EXTERNAL_MODULE)
|
||||
if (isModulePXX2(EXTERNAL_MODULE) && modulePortPowered(EXTERNAL_MODULE)) {
|
||||
moduleState[EXTERNAL_MODULE].readModuleInformation(&reusableBuffer.hardwareAndSettings.modules[EXTERNAL_MODULE], PXX2_HW_INFO_TX_ID, PXX2_MAX_RECEIVERS_PER_MODULE - 1);
|
||||
}
|
||||
#endif
|
||||
|
||||
reusableBuffer.hardwareAndSettings.updateTime = get_tmr10ms() + 1000 /* 10s*/;
|
||||
}
|
||||
@@ -123,18 +131,30 @@ void menuRadioModulesVersion(event_t event)
|
||||
for (uint8_t module=0; module<NUM_MODULES; module++) {
|
||||
// Label
|
||||
if (y >= MENU_BODY_TOP && y < MENU_BODY_BOTTOM) {
|
||||
#if defined(HARDWARE_INTERNAL_MODULE)
|
||||
if (module == INTERNAL_MODULE)
|
||||
lcdDrawTextAlignedLeft(y, STR_INTERNAL_MODULE);
|
||||
else if (module == EXTERNAL_MODULE)
|
||||
#endif
|
||||
#if defined(HARDWARE_EXTERNAL_MODULE)
|
||||
if (module == EXTERNAL_MODULE)
|
||||
lcdDrawTextAlignedLeft(y, STR_EXTERNAL_MODULE);
|
||||
#endif
|
||||
}
|
||||
y += FH;
|
||||
|
||||
// Module model
|
||||
if (y >= MENU_BODY_TOP && y < MENU_BODY_BOTTOM) {
|
||||
lcdDrawText(INDENT_WIDTH, y, STR_MODULE);
|
||||
if ((module == INTERNAL_MODULE && !IS_INTERNAL_MODULE_ON()) ||
|
||||
(module == EXTERNAL_MODULE && !IS_EXTERNAL_MODULE_ON())) {
|
||||
bool module_off = true;
|
||||
#if defined(HARDWARE_INTERNAL_MODULE)
|
||||
if (module == INTERNAL_MODULE && modulePortPowered(INTERNAL_MODULE))
|
||||
module_off = false;
|
||||
#endif
|
||||
#if defined(HARDWARE_EXTERNAL_MODULE)
|
||||
if (module == EXTERNAL_MODULE && modulePortPowered(EXTERNAL_MODULE))
|
||||
module_off = false;
|
||||
#endif
|
||||
if (module_off) {
|
||||
lcdDrawText(COLUMN2_X, y, STR_OFF);
|
||||
y += FH;
|
||||
continue;
|
||||
@@ -152,7 +172,7 @@ void menuRadioModulesVersion(event_t event)
|
||||
if (isModuleCrossfire(module)) {
|
||||
char statusText[64] = "";
|
||||
sprintf(statusText, "%d Hz %zu Err",
|
||||
1000000 / getMixerSchedulerPeriod(), (size_t)telemetryErrors);
|
||||
1000000 / getMixerSchedulerPeriod(), (size_t)0/*telemetryErrors*/);
|
||||
lcdDrawText(COLUMN2_X, y, statusText);
|
||||
y += FH;
|
||||
continue;
|
||||
|
||||
@@ -20,10 +20,7 @@
|
||||
*/
|
||||
|
||||
#include "opentx.h"
|
||||
|
||||
#if defined(PXX2)
|
||||
#include "extmodule_serial_driver.h"
|
||||
#endif
|
||||
#include "hal/module_port.h"
|
||||
|
||||
#if defined(PCBFRSKY) || defined(PCBFLYSKY)
|
||||
uint8_t switchToMix(uint8_t source)
|
||||
@@ -726,12 +723,16 @@ bool isModuleUsingSport(uint8_t moduleBay, uint8_t moduleType)
|
||||
// External XJT has a physical switch to disable S.PORT
|
||||
case MODULE_TYPE_R9M_PXX1:
|
||||
// R9M telemetry is disabled by pulses (pxx1.cpp)
|
||||
#if defined(HARDWARE_EXTERNAL_MODULE)
|
||||
if (moduleBay == EXTERNAL_MODULE)
|
||||
return false;
|
||||
#endif
|
||||
|
||||
case MODULE_TYPE_CROSSFIRE:
|
||||
#if defined(HARDWARE_INTERNAL_MODULE)
|
||||
if (moduleBay == INTERNAL_MODULE)
|
||||
return false;
|
||||
#endif
|
||||
|
||||
default:
|
||||
return true;
|
||||
@@ -785,7 +786,7 @@ bool isInternalModuleAvailable(int moduleType)
|
||||
if (g_eeGeneral.internalModule != moduleType)
|
||||
return false;
|
||||
|
||||
#if defined(INTERNAL_MODULE_PXX1)
|
||||
#if defined(INTERNAL_MODULE_PXX1) && defined(HARDWARE_EXTERNAL_MODULE)
|
||||
if ((moduleType == MODULE_TYPE_XJT_PXX1) &&
|
||||
isModuleUsingSport(EXTERNAL_MODULE,
|
||||
g_model.moduleData[EXTERNAL_MODULE].type)) {
|
||||
@@ -793,7 +794,7 @@ bool isInternalModuleAvailable(int moduleType)
|
||||
}
|
||||
#endif
|
||||
|
||||
#if defined(INTERNAL_MODULE_PXX2)
|
||||
#if defined(INTERNAL_MODULE_PXX2) && defined(HARDWARE_EXTERNAL_MODULE)
|
||||
if ((moduleType == MODULE_TYPE_ISRM_PXX2) &&
|
||||
areModulesConflicting(moduleType,
|
||||
g_model.moduleData[EXTERNAL_MODULE].type)) {
|
||||
@@ -854,7 +855,8 @@ bool isExternalModuleAvailable(int moduleType)
|
||||
moduleType == MODULE_TYPE_R9M_LITE_PRO_PXX2) {
|
||||
|
||||
#if defined(PXX2)
|
||||
return extmoduleGetSerialPort() != nullptr;
|
||||
return modulePortFind(EXTERNAL_MODULE, ETX_MOD_TYPE_SERIAL,
|
||||
ETX_MOD_PORT_UART, ETX_Pol_Normal);
|
||||
#else
|
||||
return false;
|
||||
#endif
|
||||
@@ -916,22 +918,25 @@ bool isExternalModuleAvailable(int moduleType)
|
||||
|
||||
bool isRfProtocolAvailable(int protocol)
|
||||
{
|
||||
#if defined(CROSSFIRE)
|
||||
#if defined(CROSSFIRE) && defined(HARDWARE_EXTERNAL_MODULE)
|
||||
if (protocol != MODULE_SUBTYPE_PXX1_OFF && g_model.moduleData[EXTERNAL_MODULE].type == MODULE_TYPE_CROSSFIRE) {
|
||||
return false;
|
||||
}
|
||||
#endif
|
||||
#if defined(GHOST)
|
||||
|
||||
#if defined(GHOST) && defined(HARDWARE_EXTERNAL_MODULE)
|
||||
if (protocol != MODULE_SUBTYPE_PXX1_OFF && g_model.moduleData[EXTERNAL_MODULE].type == MODULE_TYPE_GHOST) {
|
||||
return false;
|
||||
}
|
||||
#endif
|
||||
|
||||
#if !defined(MODULE_PROTOCOL_D8)
|
||||
if (protocol == MODULE_SUBTYPE_PXX1_ACCST_D8) {
|
||||
return false;
|
||||
}
|
||||
#endif
|
||||
#if defined(PCBTARANIS) || defined(PCBHORUS)
|
||||
|
||||
#if (defined(PCBTARANIS) || defined(PCBHORUS)) && defined(HARDWARE_EXTERNAL_MODULE)
|
||||
if (protocol != MODULE_SUBTYPE_PXX1_OFF && g_model.moduleData[EXTERNAL_MODULE].type == MODULE_TYPE_R9M_PXX1) {
|
||||
return false;
|
||||
}
|
||||
@@ -1029,7 +1034,7 @@ bool isTrainerModeAvailable(int mode)
|
||||
return false;
|
||||
#endif
|
||||
|
||||
#if !defined(MULTIMODULE)
|
||||
#if !defined(MULTIMODULE) || !defined(HARDWARE_INTERNAL_MODULE) || !defined(HARDWARE_EXTERNAL_MODULE)
|
||||
if (mode == TRAINER_MODE_MULTI)
|
||||
return false;
|
||||
#else
|
||||
|
||||
@@ -27,23 +27,21 @@ enum ChannelsProtocols {
|
||||
PROTOCOL_CHANNELS_UNINITIALIZED,
|
||||
PROTOCOL_CHANNELS_NONE,
|
||||
PROTOCOL_CHANNELS_PPM,
|
||||
PROTOCOL_CHANNELS_PXX1_PULSES,
|
||||
PROTOCOL_CHANNELS_PXX1_SERIAL,
|
||||
PROTOCOL_CHANNELS_PXX1,
|
||||
PROTOCOL_CHANNELS_DSM2_LP45,
|
||||
PROTOCOL_CHANNELS_DSM2_DSM2,
|
||||
PROTOCOL_CHANNELS_DSM2_DSMX,
|
||||
PROTOCOL_CHANNELS_CROSSFIRE,
|
||||
PROTOCOL_CHANNELS_MULTIMODULE,
|
||||
PROTOCOL_CHANNELS_SBUS,
|
||||
PROTOCOL_CHANNELS_PXX2_LOWSPEED,
|
||||
PROTOCOL_CHANNELS_PXX2_HIGHSPEED,
|
||||
PROTOCOL_CHANNELS_PXX2,
|
||||
PROTOCOL_CHANNELS_AFHDS2A,
|
||||
PROTOCOL_CHANNELS_AFHDS3,
|
||||
PROTOCOL_CHANNELS_GHOST,
|
||||
PROTOCOL_CHANNELS_DSMP
|
||||
};
|
||||
|
||||
struct etx_module_driver_t {
|
||||
struct etx_proto_driver_t {
|
||||
|
||||
// Protocol implemented by this module driver
|
||||
ChannelsProtocols protocol;
|
||||
@@ -51,20 +49,11 @@ struct etx_module_driver_t {
|
||||
// Init module communication
|
||||
void* (*init)(uint8_t module);
|
||||
|
||||
// // Default pulse period in microseconds
|
||||
// uint16_t (*get_default_period)(uint8_t module);
|
||||
|
||||
// De-Init module communication
|
||||
void (*deinit)(void* context);
|
||||
void (*deinit)(void* ctx);
|
||||
|
||||
// Prepare the pulses to be sent
|
||||
void (*setupPulses)(void* context, int16_t* channels, uint8_t nChannels);
|
||||
|
||||
// Send the pulses
|
||||
void (*sendPulses)(void* context);
|
||||
|
||||
// Fetch telemetry byte
|
||||
int (*getByte)(void* context, uint8_t* data);
|
||||
// Send the next pulse frame
|
||||
void (*sendPulses)(void* ctx, uint8_t* buffer, int16_t* channels, uint8_t nChannels);
|
||||
|
||||
// Process input data byte (telemetry)
|
||||
void (*processData)(void* context, uint8_t data, uint8_t* buffer, uint8_t* len);
|
||||
|
||||
@@ -0,0 +1,254 @@
|
||||
/*
|
||||
* Copyright (C) EdgeTX
|
||||
*
|
||||
* Based on code named
|
||||
* opentx - https://github.com/opentx/opentx
|
||||
* 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 "module_port.h"
|
||||
#include "dataconstants.h" // MAX_MODULES
|
||||
#include "myeeprom.h" // g_eeGeneral
|
||||
|
||||
#include <string.h>
|
||||
|
||||
extern const etx_module_t* const _modules[];
|
||||
extern const uint8_t _n_modules;
|
||||
|
||||
static etx_module_state_t _module_states[MAX_MODULES];
|
||||
static uint8_t _module_power;
|
||||
|
||||
void modulePortInit()
|
||||
{
|
||||
memset(_module_states, 0, sizeof(_module_states));
|
||||
_module_power = 0;
|
||||
|
||||
#if defined(CONFIGURABLE_MODULE_PORT)
|
||||
memset(&_extra_module_port, 0, sizeof(_extra_module_port));
|
||||
_extra_module_port.type = 0xFF; // some invalid type
|
||||
#endif
|
||||
}
|
||||
|
||||
#if defined(CONFIGURABLE_MODULE_PORT)
|
||||
// supplemental configurable port
|
||||
static etx_module_port_t _extra_module_port;
|
||||
|
||||
void modulePortConfigExtra(const etx_module_port_t* port)
|
||||
{
|
||||
memcpy(&_extra_module_port, port, sizeof(_extra_module_port));
|
||||
}
|
||||
#endif
|
||||
|
||||
static void modulePortClear(etx_module_state_t* st)
|
||||
{
|
||||
memset(st, 0, sizeof(etx_module_state_t));
|
||||
}
|
||||
|
||||
static void _init_serial_driver(etx_module_driver_t* d, const etx_module_port_t* port,
|
||||
const etx_serial_init* params)
|
||||
{
|
||||
auto drv = port->drv.serial;
|
||||
d->ctx = drv->init(port->hw_def, params);
|
||||
d->port = port;
|
||||
|
||||
// S.PORT specific HW settings
|
||||
if (port->port == ETX_MOD_PORT_SPORT && params->baudrate >= 400000) {
|
||||
|
||||
// enable over-sampling by 8
|
||||
if (drv->setHWOption) drv->setHWOption(d->ctx, ETX_HWOption_OVER8);
|
||||
|
||||
// one-bit
|
||||
if (g_eeGeneral.uartSampleMode == UART_SAMPLE_MODE_ONEBIT) {
|
||||
if (drv->setHWOption) drv->setHWOption(d->ctx, ETX_HWOption_ONEBIT);
|
||||
}
|
||||
}
|
||||
|
||||
// setup polarity
|
||||
if (port->set_inverted) {
|
||||
port->set_inverted(params->polarity == ETX_Pol_Inverted);
|
||||
}
|
||||
}
|
||||
|
||||
static void _init_timer_driver(etx_module_driver_t* d, const etx_module_port_t* port,
|
||||
const etx_timer_config_t* cfg)
|
||||
{
|
||||
auto drv = port->drv.timer;
|
||||
d->ctx = drv->init(port->hw_def, cfg);
|
||||
d->port = port;
|
||||
|
||||
// setup polarity
|
||||
if (port->set_inverted) {
|
||||
port->set_inverted(false);
|
||||
}
|
||||
}
|
||||
|
||||
static const etx_module_port_t* _find_port(uint8_t module, uint8_t type,
|
||||
uint8_t port, uint8_t polarity)
|
||||
{
|
||||
if (module >= MAX_MODULES || module >= _n_modules || !_modules[module])
|
||||
return nullptr;
|
||||
|
||||
uint8_t n_ports = _modules[module]->n_ports;
|
||||
const etx_module_port_t* p = _modules[module]->ports;
|
||||
const etx_module_port_t* found_port = nullptr;
|
||||
|
||||
while(n_ports > 0) {
|
||||
|
||||
if (p->type == type && p->port == port) {
|
||||
|
||||
// skip the port if polarity does not match
|
||||
// and cannot be set
|
||||
if (polarity != ETX_Pol_Inverted ||
|
||||
p->set_inverted ||
|
||||
port == ETX_MOD_PORT_SOFT_INV ||
|
||||
port == ETX_MOD_PORT_SPORT_INV) {
|
||||
|
||||
found_port = p;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
++p; --n_ports;
|
||||
}
|
||||
|
||||
#if defined(CONFIGURABLE_MODULE_PORT)
|
||||
if (!found_port &&
|
||||
polarity == ETX_Pol_Normal &&
|
||||
module == EXTERNAL_MODULE &&
|
||||
_extra_module_port.type == type &&
|
||||
_extra_module_port.port == port) {
|
||||
found_port = &_extra_module_port;
|
||||
}
|
||||
#endif
|
||||
|
||||
return found_port;
|
||||
}
|
||||
|
||||
const etx_module_t* modulePortGetModuleDescription(uint8_t module)
|
||||
{
|
||||
if (module >= _n_modules) return nullptr;
|
||||
return _modules[module];
|
||||
}
|
||||
|
||||
const etx_module_port_t* modulePortFind(uint8_t module, uint8_t type,
|
||||
uint8_t port, uint8_t polarity)
|
||||
{
|
||||
return _find_port(module, type, port, polarity);
|
||||
}
|
||||
|
||||
void modulePortSetPower(uint8_t module, uint8_t enable)
|
||||
{
|
||||
if (module >= _n_modules) return;
|
||||
auto mod = _modules[module];
|
||||
if (mod && mod->set_pwr) {
|
||||
mod->set_pwr(enable);
|
||||
if (enable) {
|
||||
_module_power |= (1 << module);
|
||||
} else {
|
||||
_module_power &= ~(1 << module);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
bool modulePortPowered(uint8_t module)
|
||||
{
|
||||
if (module >= _n_modules) return false;
|
||||
return _module_power & (1 << module);
|
||||
}
|
||||
|
||||
etx_module_state_t* modulePortInitSerial(uint8_t module, uint8_t port,
|
||||
const etx_serial_init* params)
|
||||
{
|
||||
// TODO: match capabilities (1. USART -> 2. SOFT-SERIAL)
|
||||
const etx_module_port_t* found_port = _find_port(module, ETX_MOD_TYPE_SERIAL,
|
||||
port, params->polarity);
|
||||
if (!found_port) return nullptr;
|
||||
|
||||
auto state = &(_module_states[module]);
|
||||
|
||||
const uint8_t duplex = ETX_Dir_TX_RX;
|
||||
uint8_t dir = params->direction & duplex;
|
||||
|
||||
if (dir == duplex) {
|
||||
|
||||
// init RX first, in case TX was already done previously
|
||||
_init_serial_driver(&state->rx, found_port, params);
|
||||
|
||||
// do not overwrite TX state if it has already been set:
|
||||
// -> support using S.PORT in bidir mode
|
||||
if (!state->tx.port) {
|
||||
state->tx.port = state->rx.port;
|
||||
state->tx.ctx = state->rx.ctx;
|
||||
}
|
||||
} else if (dir == ETX_Dir_TX) {
|
||||
_init_serial_driver(&state->tx, found_port, params);
|
||||
} else if (dir == ETX_Dir_RX) {
|
||||
_init_serial_driver(&state->rx, found_port, params);
|
||||
}
|
||||
|
||||
return state;
|
||||
}
|
||||
|
||||
etx_module_state_t* modulePortInitTimer(uint8_t module, uint8_t port,
|
||||
const etx_timer_config_t* cfg)
|
||||
{
|
||||
const etx_module_port_t* found_port = _find_port(module, ETX_MOD_TYPE_TIMER,
|
||||
port, ETX_Pol_Normal);
|
||||
if (!found_port) return nullptr;
|
||||
|
||||
auto state = &(_module_states[module]);
|
||||
_init_timer_driver(&state->tx, found_port, cfg);
|
||||
|
||||
return state;
|
||||
}
|
||||
|
||||
static void _deinit_driver(etx_module_driver_t* d)
|
||||
{
|
||||
auto p = d->port;
|
||||
auto ctx = d->ctx;
|
||||
|
||||
if (p->type == ETX_MOD_TYPE_SERIAL) {
|
||||
auto drv = p->drv.serial;
|
||||
drv->deinit(ctx);
|
||||
} else if (p->type == ETX_MOD_TYPE_TIMER) {
|
||||
auto drv = p->drv.timer;
|
||||
drv->deinit(ctx);
|
||||
}
|
||||
}
|
||||
|
||||
void modulePortDeInit(etx_module_state_t* st)
|
||||
{
|
||||
if (st->tx.port != nullptr) {
|
||||
_deinit_driver(&st->tx);
|
||||
}
|
||||
|
||||
if (st->rx.port != nullptr && st->rx.port != st->tx.port) {
|
||||
_deinit_driver(&st->rx);
|
||||
}
|
||||
|
||||
modulePortClear(st);
|
||||
}
|
||||
|
||||
etx_module_state_t* modulePortGetState(uint8_t module)
|
||||
{
|
||||
if (module >= MAX_MODULES) return nullptr;
|
||||
return &(_module_states[module]);
|
||||
}
|
||||
|
||||
uint8_t modulePortGetModule(etx_module_state_t* st)
|
||||
{
|
||||
return st - _module_states;
|
||||
}
|
||||
@@ -0,0 +1,158 @@
|
||||
/*
|
||||
* Copyright (C) EdgeTx
|
||||
*
|
||||
* Based on code named
|
||||
* opentx - https://github.com/opentx/opentx
|
||||
* 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.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <stdint.h>
|
||||
#include "serial_driver.h"
|
||||
#include "timer_driver.h"
|
||||
#include "definitions.h"
|
||||
|
||||
//
|
||||
// Module ports
|
||||
//
|
||||
|
||||
// flags
|
||||
#define ETX_MOD_DIR_TX (1 << 0)
|
||||
#define ETX_MOD_DIR_RX (1 << 1)
|
||||
#define ETX_MOD_DIR_TX_RX (ETX_MOD_DIR_TX | ETX_MOD_DIR_RX)
|
||||
#define ETX_MOD_FULL_DUPLEX (1 << 2)
|
||||
|
||||
#define ETX_MOD_TYPE_NONE 0
|
||||
#define ETX_MOD_TYPE_TIMER (1 << 0)
|
||||
#define ETX_MOD_TYPE_SERIAL (1 << 1)
|
||||
|
||||
enum ModulePort : uint8_t {
|
||||
ETX_MOD_PORT_UART,
|
||||
ETX_MOD_PORT_TIMER,
|
||||
ETX_MOD_PORT_SOFT_INV,
|
||||
ETX_MOD_PORT_SPORT,
|
||||
ETX_MOD_PORT_SPORT_INV,
|
||||
ETX_MOD_PORT_MAX
|
||||
};
|
||||
|
||||
//
|
||||
// A module port is a pin (single UART RX or TX; PPM pin)
|
||||
// or pin pair (UART RX/TX)
|
||||
//
|
||||
typedef struct {
|
||||
|
||||
// enum ModulePort
|
||||
uint8_t port;
|
||||
|
||||
// ETX_MOD_TYPE_xx
|
||||
uint8_t type;
|
||||
|
||||
// flags bitfield ETX_MOD_DIR_xx
|
||||
uint8_t dir_flags;
|
||||
|
||||
union {
|
||||
const etx_timer_driver_t *timer;
|
||||
const etx_serial_driver_t *serial;
|
||||
} drv;
|
||||
|
||||
// this is specific to the driver above
|
||||
void* hw_def;
|
||||
|
||||
// inverter support: only works on the following ports
|
||||
// - ETX_MOD_PORT_UART / ETX_MOD_PORT_SPORT
|
||||
void (*set_inverted)(uint8_t enable);
|
||||
|
||||
} etx_module_port_t;
|
||||
|
||||
typedef struct {
|
||||
const etx_module_port_t* const ports;
|
||||
void (*set_pwr)(uint8_t on);
|
||||
void (*set_bootcmd)(uint8_t on);
|
||||
const uint8_t n_ports;
|
||||
} etx_module_t;
|
||||
|
||||
#define BEGIN_MODULES() \
|
||||
extern const etx_module_t* const _modules[]; \
|
||||
const etx_module_t* const _modules[] = {
|
||||
|
||||
#define END_MODULES() }; \
|
||||
extern const uint8_t _n_modules; \
|
||||
const uint8_t _n_modules = DIM(_modules);
|
||||
|
||||
typedef struct {
|
||||
const etx_module_port_t* port;
|
||||
void* ctx;
|
||||
} etx_module_driver_t;
|
||||
|
||||
typedef struct {
|
||||
|
||||
etx_module_driver_t tx;
|
||||
etx_module_driver_t rx;
|
||||
|
||||
void* user_data;
|
||||
|
||||
} etx_module_state_t;
|
||||
|
||||
// Init the internal module port data
|
||||
void modulePortInit();
|
||||
|
||||
#if defined(CONFIGURABLE_MODULE_PORT)
|
||||
// Configure an extra port
|
||||
void modulePortConfigExtra(const etx_module_port_t* port);
|
||||
#endif
|
||||
|
||||
const etx_module_t* modulePortGetModuleDescription(uint8_t module);
|
||||
|
||||
const etx_module_port_t* modulePortFind(uint8_t module, uint8_t type,
|
||||
uint8_t port, uint8_t polarity);
|
||||
|
||||
void modulePortSetPower(uint8_t module, uint8_t enabled);
|
||||
|
||||
bool modulePortPowered(uint8_t module);
|
||||
|
||||
// Init module port with params (driver & context stored locally)
|
||||
etx_module_state_t* modulePortInitSerial(uint8_t module, uint8_t port,
|
||||
const etx_serial_init* params);
|
||||
|
||||
// Init module port with params (driver & context stored locally)
|
||||
etx_module_state_t* modulePortInitTimer(uint8_t module, uint8_t port,
|
||||
const etx_timer_config_t* cfg);
|
||||
|
||||
// De-init port and clear data
|
||||
void modulePortDeInit(etx_module_state_t* st);
|
||||
|
||||
// Once initialized, retrieve module serial driver and context
|
||||
etx_module_state_t* modulePortGetState(uint8_t module);
|
||||
|
||||
uint8_t modulePortGetModule(etx_module_state_t* st);
|
||||
|
||||
inline uint8_t modulePortGetType(const etx_module_driver_t& d) {
|
||||
return d.port->type;
|
||||
}
|
||||
|
||||
inline const etx_serial_driver_t* modulePortGetSerialDrv(const etx_module_driver_t& d) {
|
||||
return d.port->drv.serial;
|
||||
}
|
||||
|
||||
inline const etx_timer_driver_t* modulePortGetTimerDrv(const etx_module_driver_t& d) {
|
||||
return d.port->drv.timer;
|
||||
}
|
||||
|
||||
inline void* modulePortGetCtx(const etx_module_driver_t& d) {
|
||||
return d.ctx;
|
||||
}
|
||||
|
||||
@@ -23,42 +23,49 @@
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
enum SerialParity {
|
||||
ETX_Parity_None,
|
||||
ETX_Parity_Even,
|
||||
ETX_Parity_Odd,
|
||||
|
||||
enum SerialEncoding {
|
||||
ETX_Encoding_8N1,
|
||||
ETX_Encoding_8E2,
|
||||
ETX_Encoding_PXX1_PWM,
|
||||
};
|
||||
|
||||
enum SerialStopBits {
|
||||
ETX_StopBits_One,
|
||||
ETX_StopBits_OneAndHalf,
|
||||
ETX_StopBits_Two,
|
||||
enum SerialDirection {
|
||||
ETX_Dir_None = 0,
|
||||
ETX_Dir_RX = 1,
|
||||
ETX_Dir_TX = 2,
|
||||
ETX_Dir_TX_RX = 3,
|
||||
};
|
||||
|
||||
enum SerialWordLength {
|
||||
ETX_WordLength_8,
|
||||
ETX_WordLength_9,
|
||||
enum SerialPolarity {
|
||||
ETX_Pol_Normal,
|
||||
ETX_Pol_Inverted,
|
||||
};
|
||||
|
||||
typedef struct {
|
||||
uint32_t baudrate; // = 0;
|
||||
uint8_t parity; // = ETX_Parity_None;
|
||||
uint8_t stop_bits; // = ETX_StopBits_One;
|
||||
uint8_t word_length; // = ETX_WordLength_8;
|
||||
uint8_t rx_enable; // = false;
|
||||
|
||||
uint8_t encoding; // = ETX_Encoding_8N1;
|
||||
uint8_t direction; // = ETX_Dir_None;
|
||||
uint8_t polarity; // = ETX_Pol_Normal;
|
||||
} etx_serial_init;
|
||||
|
||||
// TODO: move somewhere else
|
||||
struct etx_serial_callbacks_t {
|
||||
uint8_t (*on_send)(uint8_t* data);
|
||||
void (*on_receive)(uint8_t data);
|
||||
void (*on_idle)();
|
||||
void (*on_error)();
|
||||
};
|
||||
|
||||
enum SerialHWOption {
|
||||
ETX_HWOption_OVER8, // oversampling by 8
|
||||
ETX_HWOption_ONEBIT, // one-bit sampling
|
||||
};
|
||||
|
||||
typedef struct {
|
||||
|
||||
// Init serial communication
|
||||
void* (*init)(const etx_serial_init* params);
|
||||
void* (*init)(void* hw_def, const etx_serial_init* params);
|
||||
|
||||
// De-Init serial communication
|
||||
void (*deinit)(void* ctx);
|
||||
@@ -67,22 +74,41 @@ typedef struct {
|
||||
void (*sendByte)(void* ctx, uint8_t byte);
|
||||
|
||||
// Send a buffer
|
||||
void (*sendBuffer)(void* ctx, const uint8_t* data, uint8_t size);
|
||||
void (*sendBuffer)(void* ctx, const uint8_t* data, uint32_t size);
|
||||
|
||||
// Is TX phase completed
|
||||
uint8_t (*txCompleted)(void* ctx);
|
||||
|
||||
// Wait for last send operation to complete
|
||||
void (*waitForTxCompleted)(void* ctx);
|
||||
|
||||
// Fetch byte from internal buffer
|
||||
|
||||
// 2-wire half-duplex
|
||||
void (*enableRx)(void* ctx);
|
||||
|
||||
// Fetch next available byte from internal buffer
|
||||
int (*getByte)(void* ctx, uint8_t* data);
|
||||
|
||||
// Fetch a byte by its index from the end of the buffer
|
||||
int (*getLastByte)(void* ctx, uint32_t idx, uint8_t* data);
|
||||
|
||||
// Clear internal buffer
|
||||
void (*clearRxBuffer)(void* ctx);
|
||||
|
||||
// Get current baudrate
|
||||
uint32_t (*getBaudrate)(void*);
|
||||
|
||||
// Set baudrate
|
||||
void (*setBaudrate)(void*, uint32_t baudrate);
|
||||
|
||||
// Configure inverter
|
||||
void (*setPolarity)(void*, uint8_t polarity);
|
||||
|
||||
// Set a HW specific option (SerialHWOption; possibly unsupported by the driver)
|
||||
void (*setHWOption)(void*, uint32_t option);
|
||||
|
||||
// Callbacks
|
||||
void (*setReceiveCb)(void* ctx, void (*on_receive)(uint8_t*, uint32_t));
|
||||
void (*setIdleCb)(void* ctx, void (*on_idle)());
|
||||
void (*setBaudrateCb)(void* ctx, void (*on_set_baudrate)(uint32_t));
|
||||
|
||||
} etx_serial_driver_t;
|
||||
|
||||
@@ -26,5 +26,6 @@
|
||||
typedef struct {
|
||||
const char* name;
|
||||
const etx_serial_driver_t* uart;
|
||||
void* hw_def;
|
||||
void (*set_pwr)(uint8_t on);
|
||||
} etx_serial_port_t;
|
||||
|
||||
@@ -0,0 +1,41 @@
|
||||
/*
|
||||
* Copyright (C) EdgeTx
|
||||
*
|
||||
* Based on code named
|
||||
* opentx - https://github.com/opentx/opentx
|
||||
* 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.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
enum PulseGenerationType {
|
||||
ETX_PWM=0,
|
||||
ETX_TOGGLE
|
||||
};
|
||||
|
||||
typedef struct {
|
||||
uint8_t type;
|
||||
uint8_t polarity;
|
||||
uint16_t cmp_val;
|
||||
} etx_timer_config_t;
|
||||
|
||||
typedef struct {
|
||||
void* (*init)(void* hw_def, const etx_timer_config_t* cfg);
|
||||
void (*deinit)(void* ctx);
|
||||
void (*send)(void* ctx, const etx_timer_config_t* cfg, const void* pulses, uint16_t length);
|
||||
} etx_timer_driver_t;
|
||||
@@ -50,7 +50,7 @@ void BootloaderFirmwareUpdate::flashFirmware(const char * filename, ProgressHand
|
||||
uint8_t buffer[1024];
|
||||
UINT count;
|
||||
|
||||
pausePulses();
|
||||
pulsesStop();
|
||||
|
||||
f_open(&file, filename, FA_READ);
|
||||
|
||||
@@ -110,5 +110,5 @@ void BootloaderFirmwareUpdate::flashFirmware(const char * filename, ProgressHand
|
||||
|
||||
f_close(&file);
|
||||
|
||||
resumePulses();
|
||||
pulsesStart();
|
||||
}
|
||||
|
||||
@@ -24,6 +24,7 @@
|
||||
#include "frsky_firmware_update.h"
|
||||
#include "debug.h"
|
||||
#include "timers_driver.h"
|
||||
#include "tasks/mixer_task.h"
|
||||
|
||||
#if defined(LIBOPENUI)
|
||||
#include "libopenui.h"
|
||||
@@ -31,9 +32,7 @@
|
||||
#include "libopenui/src/libopenui_file.h"
|
||||
#endif
|
||||
|
||||
#if defined(INTMODULE_USART)
|
||||
#include "intmodule_serial_driver.h"
|
||||
#endif
|
||||
#include "hal/module_port.h"
|
||||
|
||||
#define PRIM_REQ_POWERUP 0
|
||||
#define PRIM_REQ_VERSION 1
|
||||
@@ -110,97 +109,65 @@ void FrskyDeviceFirmwareUpdate::processFrame(const uint8_t * frame)
|
||||
}
|
||||
}
|
||||
|
||||
#if defined(PCBHORUS)
|
||||
bool FrskyDeviceFirmwareUpdate::readBuffer(uint8_t * buffer, uint8_t count, uint32_t timeout)
|
||||
{
|
||||
watchdogSuspend(timeout);
|
||||
|
||||
switch (module) {
|
||||
case INTERNAL_MODULE:
|
||||
{
|
||||
uint32_t elapsed = 0;
|
||||
uint8_t index = 0;
|
||||
while (index < count && elapsed < timeout) {
|
||||
if (IntmoduleSerialDriver.getByte(uart_ctx, &(buffer[index]))) {
|
||||
++index;
|
||||
}
|
||||
else {
|
||||
RTOS_WAIT_MS(1);
|
||||
if (++elapsed == timeout)
|
||||
return false;
|
||||
}
|
||||
}
|
||||
break;
|
||||
uint32_t elapsed = 0;
|
||||
uint8_t index = 0;
|
||||
while (index < count && elapsed < timeout) {
|
||||
if (uart_drv->getByte(uart_ctx, &(buffer[index]))) {
|
||||
++index;
|
||||
}
|
||||
else {
|
||||
RTOS_WAIT_MS(1);
|
||||
if (++elapsed == timeout)
|
||||
return false;
|
||||
}
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
#endif
|
||||
|
||||
const uint8_t * FrskyDeviceFirmwareUpdate::readFullDuplexFrame(uint32_t timeout)
|
||||
{
|
||||
#if defined(INTMODULE_USART)
|
||||
uint8_t len = 0;
|
||||
bool bytestuff = false;
|
||||
while (len < 10) {
|
||||
uint32_t elapsed = 0;
|
||||
uint8_t byte = 0;
|
||||
while (IntmoduleSerialDriver.getByte(uart_ctx, &byte) == 0) {
|
||||
RTOS_WAIT_MS(1);
|
||||
if (elapsed++ >= timeout) {
|
||||
return nullptr;
|
||||
}
|
||||
}
|
||||
if (byte == 0x7D) {
|
||||
bytestuff = true;
|
||||
continue;
|
||||
}
|
||||
if (bytestuff) {
|
||||
frame[len] = 0x20 ^ byte;
|
||||
bytestuff = false;
|
||||
}
|
||||
else {
|
||||
frame[len] = byte;
|
||||
}
|
||||
if (len > 0 || byte == 0x7E) {
|
||||
++len;
|
||||
}
|
||||
}
|
||||
return &frame[1];
|
||||
#else
|
||||
return nullptr;
|
||||
#endif
|
||||
}
|
||||
|
||||
const uint8_t * FrskyDeviceFirmwareUpdate::readHalfDuplexFrame(uint32_t timeout)
|
||||
{
|
||||
for (int i = timeout; i >= 0; i--) {
|
||||
uint8_t byte ;
|
||||
while (sportGetByte(&byte)) {
|
||||
if (pushFrskyTelemetryData(byte)) {
|
||||
return telemetryRxBuffer;
|
||||
}
|
||||
}
|
||||
RTOS_WAIT_MS(1);
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
const uint8_t * FrskyDeviceFirmwareUpdate::readFrame(uint32_t timeout)
|
||||
{
|
||||
RTOS_WAIT_MS(1);
|
||||
|
||||
#if defined(INTERNAL_MODULE_PXX2)
|
||||
if ((module == INTERNAL_MODULE) &&
|
||||
(g_eeGeneral.internalModule = MODULE_TYPE_ISRM_PXX2)) {
|
||||
return readFullDuplexFrame(timeout);
|
||||
uint8_t len = 0;
|
||||
bool bytestuff = false;
|
||||
|
||||
while (len <= FRSKY_SPORT_PACKET_SIZE) {
|
||||
|
||||
uint32_t elapsed = 0;
|
||||
uint8_t byte = 0;
|
||||
|
||||
while (uart_drv->getByte(uart_ctx, &byte) == 0) {
|
||||
RTOS_WAIT_MS(1);
|
||||
if (elapsed++ >= timeout) {
|
||||
TRACE("timeout in frame (len=%d)",len);
|
||||
return nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
if (byte == BYTE_STUFF) {
|
||||
bytestuff = true;
|
||||
continue;
|
||||
}
|
||||
|
||||
if (bytestuff) {
|
||||
frame[len] = STUFF_MASK ^ byte;
|
||||
bytestuff = false;
|
||||
}
|
||||
else {
|
||||
frame[len] = byte;
|
||||
}
|
||||
|
||||
if (len > 0 || byte == START_STOP) {
|
||||
++len;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
return readHalfDuplexFrame(timeout);
|
||||
|
||||
return &frame[1];
|
||||
}
|
||||
|
||||
bool FrskyDeviceFirmwareUpdate::waitState(State newState, uint32_t timeout)
|
||||
@@ -238,29 +205,20 @@ void FrskyDeviceFirmwareUpdate::startFrame(uint8_t command)
|
||||
void FrskyDeviceFirmwareUpdate::sendFrame()
|
||||
{
|
||||
uint8_t * ptr = outputTelemetryBuffer.data;
|
||||
*ptr++ = 0x7E;
|
||||
*ptr++ = START_STOP;
|
||||
*ptr++ = 0xFF;
|
||||
frame[7] = crc16(CRC_1021, frame, 7);
|
||||
for (int i=0; i<8; i++) {
|
||||
if (frame[i] == 0x7E || frame[i] == 0x7D) {
|
||||
*ptr++ = 0x7D;
|
||||
*ptr++ = 0x20 ^ frame[i];
|
||||
}
|
||||
else {
|
||||
for (int i = 0; i < 8; i++) {
|
||||
if (frame[i] == START_STOP || frame[i] == BYTE_STUFF) {
|
||||
*ptr++ = BYTE_STUFF;
|
||||
*ptr++ = STUFF_MASK ^ frame[i];
|
||||
} else {
|
||||
*ptr++ = frame[i];
|
||||
}
|
||||
}
|
||||
|
||||
#if defined(INTERNAL_MODULE_PXX2)
|
||||
if ((module == INTERNAL_MODULE) &&
|
||||
(g_eeGeneral.internalModule = MODULE_TYPE_ISRM_PXX2)) {
|
||||
IntmoduleSerialDriver.sendBuffer(uart_ctx, outputTelemetryBuffer.data,
|
||||
ptr - outputTelemetryBuffer.data);
|
||||
return;
|
||||
}
|
||||
#endif
|
||||
|
||||
sportSendBuffer(outputTelemetryBuffer.data, ptr - outputTelemetryBuffer.data);
|
||||
uart_drv->sendBuffer(uart_ctx, outputTelemetryBuffer.data,
|
||||
ptr - outputTelemetryBuffer.data);
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -269,7 +227,7 @@ const char * FrskyDeviceFirmwareUpdate::sendPowerOn()
|
||||
state = SPORT_POWERUP_REQ;
|
||||
|
||||
RTOS_WAIT_MS(50);
|
||||
telemetryClearFifo();
|
||||
uart_drv->clearRxBuffer(uart_ctx);
|
||||
|
||||
for (int i=0; i<10; i++) {
|
||||
// max 10 attempts
|
||||
@@ -279,21 +237,13 @@ const char * FrskyDeviceFirmwareUpdate::sendPowerOn()
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
if (telemetryProtocol != PROTOCOL_TELEMETRY_FRSKY_SPORT) {
|
||||
return STR_DEVICE_NO_RESPONSE;
|
||||
}
|
||||
|
||||
if (!IS_FRSKY_SPORT_PROTOCOL()) {
|
||||
return STR_DEVICE_NO_RESPONSE;
|
||||
}
|
||||
|
||||
return STR_DEVICE_NO_RESPONSE;
|
||||
}
|
||||
|
||||
const char * FrskyDeviceFirmwareUpdate::sendReqVersion()
|
||||
{
|
||||
RTOS_WAIT_MS(20);
|
||||
telemetryClearFifo();
|
||||
uart_drv->clearRxBuffer(uart_ctx);
|
||||
|
||||
state = SPORT_VERSION_REQ;
|
||||
for (int i=0; i<10; i++) {
|
||||
@@ -311,20 +261,18 @@ const char * FrskyDeviceFirmwareUpdate::sendReqVersion()
|
||||
// X9D / X9D+ / X9E / XLite IXJT = use S.PORT @ 57600 bauds
|
||||
// XLite PRO / X9Lite / X9D+ 2019 ISRM = use TX + RX @ 57600 bauds
|
||||
|
||||
#if defined(INTMODULE_USART)
|
||||
static const etx_serial_init serialInitParams = {
|
||||
.baudrate = 0,
|
||||
.parity = ETX_Parity_None,
|
||||
.stop_bits = ETX_StopBits_One,
|
||||
.word_length = ETX_WordLength_8,
|
||||
.rx_enable = true,
|
||||
.encoding = ETX_Encoding_8N1,
|
||||
.direction = ETX_Dir_TX_RX,
|
||||
.polarity = ETX_Pol_Normal,
|
||||
};
|
||||
#endif
|
||||
|
||||
const char * FrskyDeviceFirmwareUpdate::doFlashFirmware(const char * filename, ProgressHandler progressHandler)
|
||||
const char *FrskyDeviceFirmwareUpdate::doFlashFirmware(
|
||||
const char *filename, ProgressHandler progressHandler)
|
||||
{
|
||||
FIL file;
|
||||
const char * result;
|
||||
const char *result;
|
||||
FrSkyFirmwareInformation information;
|
||||
UINT count;
|
||||
|
||||
@@ -332,64 +280,99 @@ const char * FrskyDeviceFirmwareUpdate::doFlashFirmware(const char * filename, P
|
||||
return STR_NEEDS_FILE;
|
||||
}
|
||||
|
||||
const char * ext = getFileExtension(filename);
|
||||
// Start with checking if we have a firmware description:
|
||||
// this mainly covers "newer" modules (ISRM, ACCESS based)
|
||||
|
||||
// default to S.PORT update...
|
||||
uint8_t port = ETX_MOD_PORT_SPORT;
|
||||
void (*set_pwr)(uint8_t) = nullptr;
|
||||
void (*set_bootcmd)(uint8_t) = nullptr;
|
||||
|
||||
// TODO: what about S.PORT update power control? (RX?)
|
||||
auto mod_desc = modulePortGetModuleDescription(module);
|
||||
set_pwr = mod_desc->set_pwr;
|
||||
|
||||
// ... and 57600 bps
|
||||
etx_serial_init cfg(serialInitParams);
|
||||
cfg.baudrate = 57600;
|
||||
|
||||
const char *ext = getFileExtension(filename);
|
||||
if (ext && !strcasecmp(ext, FRSKY_FIRMWARE_EXT)) {
|
||||
auto ret = f_read(&file, &information, sizeof(FrSkyFirmwareInformation), &count);
|
||||
auto ret =
|
||||
f_read(&file, &information, sizeof(FrSkyFirmwareInformation), &count);
|
||||
if (ret != FR_OK || count != sizeof(FrSkyFirmwareInformation)) {
|
||||
f_close(&file);
|
||||
return STR_DEVICE_FILE_ERROR;
|
||||
}
|
||||
|
||||
// probably some ISRM or ACCESS module...
|
||||
// TODO: what are we supposed to start with the firmware header file?
|
||||
if ((information.productFamily == FIRMWARE_FAMILY_INTERNAL_MODULE &&
|
||||
module != INTERNAL_MODULE) ||
|
||||
(information.productFamily == FIRMWARE_FAMILY_EXTERNAL_MODULE &&
|
||||
module != EXTERNAL_MODULE)) {
|
||||
|
||||
return STR_DEVICE_FILE_WRONG_SIG;
|
||||
}
|
||||
|
||||
if (information.productFamily == FIRMWARE_FAMILY_INTERNAL_MODULE &&
|
||||
information.productId == FIRMWARE_ID_MODULE_ISRM) {
|
||||
port = ETX_MOD_PORT_UART;
|
||||
}
|
||||
|
||||
} else if (module == INTERNAL_MODULE) {
|
||||
// For the older modules we need some guess work:
|
||||
//
|
||||
// - X12S / X10 IXJT = use TX + RX @ 38400 bauds with BOOTCMD pin
|
||||
// -> internal USART + boot pin
|
||||
//
|
||||
// - X9D / X9D+ / X9E / XLite IXJT = use S.PORT @ 57600 bauds
|
||||
// -> no internal USART OR no boot pin (XLite has an internal USART)
|
||||
|
||||
// X12S / X10 iXJT: TX + RX @ 38400 bauds with BOOTCMD pin inverted
|
||||
if (mod_desc->set_bootcmd) {
|
||||
cfg.baudrate = 38400;
|
||||
port = ETX_MOD_PORT_UART;
|
||||
set_bootcmd = mod_desc->set_bootcmd;
|
||||
} else {
|
||||
// keep the above defaults (S.PORT @ 57600 bps)
|
||||
}
|
||||
} else {
|
||||
#if defined(PCBHORUS)
|
||||
information.productId = FIRMWARE_ID_MODULE_XJT;
|
||||
#endif
|
||||
}
|
||||
|
||||
#if defined(PCBHORUS)
|
||||
if (module == INTERNAL_MODULE && information.productId == FIRMWARE_ID_MODULE_XJT) {
|
||||
INTERNAL_MODULE_ON();
|
||||
RTOS_WAIT_MS(1);
|
||||
// SPORT_MODULE is only defined for power control
|
||||
// but has not ports declared on it.
|
||||
//
|
||||
uint8_t serial_module = module != SPORT_MODULE ? module : EXTERNAL_MODULE;
|
||||
|
||||
etx_serial_init params(serialInitParams);
|
||||
params.baudrate = 38400;
|
||||
uart_ctx = IntmoduleSerialDriver.init(¶ms);
|
||||
mod_st = modulePortInitSerial(serial_module, port, &cfg);
|
||||
if (!mod_st) return "Communication port error";
|
||||
|
||||
GPIO_SetBits(INTMODULE_BOOTCMD_GPIO, INTMODULE_BOOTCMD_GPIO_PIN);
|
||||
// assume RX port is the same as TX
|
||||
uart_drv = modulePortGetSerialDrv(mod_st->tx);
|
||||
uart_ctx = modulePortGetCtx(mod_st->tx);
|
||||
|
||||
if (set_bootcmd) set_bootcmd(true);
|
||||
if (set_pwr) set_pwr(true);
|
||||
|
||||
// wait a bit for PWR to settle
|
||||
RTOS_WAIT_MS(1);
|
||||
|
||||
// Special update method for X12S / X10 iXJT
|
||||
if (module == INTERNAL_MODULE && port == ETX_MOD_PORT_UART && set_bootcmd != nullptr) {
|
||||
result = uploadFileToHorusXJT(filename, &file, progressHandler);
|
||||
GPIO_ResetBits(INTMODULE_BOOTCMD_GPIO, INTMODULE_BOOTCMD_GPIO_PIN);
|
||||
|
||||
f_close(&file);
|
||||
return result;
|
||||
}
|
||||
#endif
|
||||
|
||||
#if defined(INTERNAL_MODULE_PXX2)
|
||||
if ((module == INTERNAL_MODULE) &&
|
||||
(g_eeGeneral.internalModule = MODULE_TYPE_ISRM_PXX2)) {
|
||||
etx_serial_init params(serialInitParams);
|
||||
params.baudrate = 57600;
|
||||
uart_ctx = IntmoduleSerialDriver.init(¶ms);
|
||||
}
|
||||
else
|
||||
#endif
|
||||
{
|
||||
telemetryInit(PROTOCOL_TELEMETRY_FRSKY_SPORT);
|
||||
} else {
|
||||
result = uploadFileNormal(filename, &file, progressHandler);
|
||||
}
|
||||
|
||||
if (module == INTERNAL_MODULE)
|
||||
INTERNAL_MODULE_ON();
|
||||
else if (module == EXTERNAL_MODULE)
|
||||
EXTERNAL_MODULE_ON();
|
||||
else
|
||||
SPORT_UPDATE_POWER_ON();
|
||||
if (set_pwr) set_pwr(false);
|
||||
if (set_bootcmd) set_bootcmd(false);
|
||||
|
||||
result = uploadFileNormal(filename, &file, progressHandler);
|
||||
f_close(&file);
|
||||
modulePortDeInit(mod_st);
|
||||
return result;
|
||||
}
|
||||
|
||||
#if defined(PCBHORUS)
|
||||
const char * FrskyDeviceFirmwareUpdate::uploadFileToHorusXJT(const char * filename, FIL * file, ProgressHandler progressHandler)
|
||||
const char *FrskyDeviceFirmwareUpdate::uploadFileToHorusXJT(
|
||||
const char *filename, FIL *file, ProgressHandler progressHandler)
|
||||
{
|
||||
uint32_t buffer[1024 / sizeof(uint32_t)];
|
||||
UINT count;
|
||||
@@ -399,14 +382,14 @@ const char * FrskyDeviceFirmwareUpdate::uploadFileToHorusXJT(const char * filena
|
||||
return STR_DEVICE_NO_RESPONSE;
|
||||
}
|
||||
|
||||
IntmoduleSerialDriver.sendByte(uart_ctx, 0x81);
|
||||
uart_drv->sendByte(uart_ctx, 0x81);
|
||||
readBuffer(frame, 1, 100);
|
||||
|
||||
if (!readBuffer(frame, 8, 100) || frame[0] != 0x02) {
|
||||
return STR_DEVICE_NO_RESPONSE;
|
||||
}
|
||||
|
||||
IntmoduleSerialDriver.sendByte(uart_ctx, 0x82);
|
||||
uart_drv->sendByte(uart_ctx, 0x82);
|
||||
readBuffer(frame, 1, 100);
|
||||
|
||||
uint8_t index = 0;
|
||||
@@ -424,7 +407,7 @@ const char * FrskyDeviceFirmwareUpdate::uploadFileToHorusXJT(const char * filena
|
||||
return STR_DEVICE_WRONG_REQUEST;
|
||||
|
||||
if (count == 0) {
|
||||
IntmoduleSerialDriver.sendByte(uart_ctx, 0xA1);
|
||||
uart_drv->sendByte(uart_ctx, 0xA1);
|
||||
RTOS_WAIT_MS(50);
|
||||
return nullptr;
|
||||
}
|
||||
@@ -432,22 +415,32 @@ const char * FrskyDeviceFirmwareUpdate::uploadFileToHorusXJT(const char * filena
|
||||
if (count < 1024)
|
||||
memset(((uint8_t *)buffer) + count, 0, 1024 - count);
|
||||
|
||||
IntmoduleSerialDriver.sendByte(uart_ctx, frame[0] + 0x80);
|
||||
IntmoduleSerialDriver.sendByte(uart_ctx, frame[1]);
|
||||
uart_drv->sendByte(uart_ctx, frame[0] + 0x80);
|
||||
uart_drv->sendByte(uart_ctx, frame[1]);
|
||||
|
||||
uint16_t crc_16 = crc16(CRC_1189, (uint8_t *)buffer, 1024, crc16(CRC_1189, &frame[1], 1));
|
||||
for (size_t i = 0; i < sizeof(buffer); i++) {
|
||||
IntmoduleSerialDriver.sendByte(uart_ctx, ((uint8_t *)buffer)[i]);
|
||||
uart_drv->sendByte(uart_ctx, ((uint8_t *)buffer)[i]);
|
||||
}
|
||||
IntmoduleSerialDriver.sendByte(uart_ctx, crc_16 >> 8);
|
||||
IntmoduleSerialDriver.sendByte(uart_ctx, crc_16);
|
||||
uart_drv->sendByte(uart_ctx, crc_16 >> 8);
|
||||
uart_drv->sendByte(uart_ctx, crc_16);
|
||||
|
||||
index++;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
const char * FrskyDeviceFirmwareUpdate::uploadFileNormal(const char * filename, FIL * file, ProgressHandler progressHandler)
|
||||
void FrskyDeviceFirmwareUpdate::sendDataTransfer(uint32_t* buffer)
|
||||
{
|
||||
startFrame(PRIM_DATA_WORD);
|
||||
uint32_t offset = (address & 1023) >> 2; // 32 bit word offset into buffer
|
||||
*((uint32_t *)(frame + 2)) = buffer[offset];
|
||||
frame[6] = address & 0x000000FF;
|
||||
state = SPORT_DATA_TRANSFER;
|
||||
sendFrame();
|
||||
}
|
||||
|
||||
const char *FrskyDeviceFirmwareUpdate::uploadFileNormal(
|
||||
const char *filename, FIL *file, ProgressHandler progressHandler)
|
||||
{
|
||||
uint32_t buffer[1024 / sizeof(uint32_t)];
|
||||
UINT count;
|
||||
@@ -461,12 +454,14 @@ const char * FrskyDeviceFirmwareUpdate::uploadFileNormal(const char * filename,
|
||||
return result;
|
||||
|
||||
RTOS_WAIT_MS(200);
|
||||
telemetryClearFifo();
|
||||
uart_drv->clearRxBuffer(uart_ctx);
|
||||
|
||||
state = SPORT_DATA_TRANSFER;
|
||||
startFrame(PRIM_CMD_DOWNLOAD);
|
||||
sendFrame();
|
||||
|
||||
uint8_t retries = 0;
|
||||
|
||||
while (true) {
|
||||
if (f_read(file, buffer, 1024, &count) != FR_OK) {
|
||||
return STR_DEVICE_FILE_ERROR;
|
||||
@@ -474,16 +469,19 @@ const char * FrskyDeviceFirmwareUpdate::uploadFileNormal(const char * filename,
|
||||
|
||||
count >>= 2;
|
||||
|
||||
for (uint32_t i=0; i<count; i++) {
|
||||
if (!waitState(SPORT_DATA_REQ, 2000)) {
|
||||
for (uint32_t i = 0; i < count; i++) {
|
||||
|
||||
if (waitState(SPORT_DATA_REQ, 80)) {
|
||||
retries = 4; // reset retries
|
||||
} else if (retries > 0){
|
||||
--retries;
|
||||
TRACE("retrying last transfer (address=0x%04X)", address);
|
||||
} else {
|
||||
return STR_DEVICE_DATA_REFUSED;
|
||||
}
|
||||
startFrame(PRIM_DATA_WORD);
|
||||
uint32_t offset = (address & 1023) >> 2; // 32 bit word offset into buffer
|
||||
*((uint32_t *)(frame + 2)) = buffer[offset];
|
||||
frame[6] = address & 0x000000FF;
|
||||
state = SPORT_DATA_TRANSFER,
|
||||
sendFrame();
|
||||
|
||||
sendDataTransfer(buffer);
|
||||
|
||||
if (i == 0) {
|
||||
progressHandler(getBasename(filename), STR_WRITING, file->fptr, file->obj.objsize);
|
||||
}
|
||||
@@ -509,23 +507,13 @@ const char * FrskyDeviceFirmwareUpdate::endTransfer()
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
const char * FrskyDeviceFirmwareUpdate::flashFirmware(const char * filename, ProgressHandler progressHandler)
|
||||
const char *FrskyDeviceFirmwareUpdate::flashFirmware(
|
||||
const char *filename, ProgressHandler progressHandler)
|
||||
{
|
||||
pauseMixerCalculations();
|
||||
pausePulses();
|
||||
pulsesStop();
|
||||
|
||||
#if defined(HARDWARE_INTERNAL_MODULE)
|
||||
stopPulsesInternalModule();
|
||||
#endif
|
||||
|
||||
#if defined(HARDWARE_EXTERNAL_MODULE)
|
||||
stopPulsesExternalModule();
|
||||
#endif
|
||||
|
||||
#if defined(SPORT_UPDATE_PWR_GPIO)
|
||||
uint8_t spuPwr = IS_SPORT_UPDATE_POWER_ON();
|
||||
SPORT_UPDATE_POWER_OFF();
|
||||
#endif
|
||||
// switch S.PORT power OFF if supported
|
||||
modulePortSetPower(SPORT_MODULE, false);
|
||||
|
||||
progressHandler(getBasename(filename), STR_DEVICE_RESET, 0, 0);
|
||||
|
||||
@@ -544,305 +532,18 @@ const char * FrskyDeviceFirmwareUpdate::flashFirmware(const char * filename, Pro
|
||||
POPUP_INFORMATION(STR_FIRMWARE_UPDATE_SUCCESS);
|
||||
}
|
||||
|
||||
#if defined(INTMODULE_USART)
|
||||
if (uart_ctx) {
|
||||
// If the UART is not deactivated, the module
|
||||
// will not properly reset
|
||||
IntmoduleSerialDriver.deinit(uart_ctx);
|
||||
}
|
||||
#endif
|
||||
|
||||
#if defined(HARDWARE_INTERNAL_MODULE)
|
||||
INTERNAL_MODULE_OFF();
|
||||
#endif
|
||||
EXTERNAL_MODULE_OFF();
|
||||
SPORT_UPDATE_POWER_OFF();
|
||||
watchdogSuspend(50 /*500ms*/);
|
||||
pulsesStart();
|
||||
|
||||
/* wait 2s off */
|
||||
watchdogSuspend(500 /*5s*/);
|
||||
RTOS_WAIT_MS(2000);
|
||||
telemetryClearFifo();
|
||||
|
||||
resumePulses();
|
||||
resumeMixerCalculations();
|
||||
|
||||
#if defined(SPORT_UPDATE_PWR_GPIO)
|
||||
if (spuPwr) {
|
||||
SPORT_UPDATE_POWER_ON();
|
||||
}
|
||||
#endif
|
||||
// TODO: S.PORT power control
|
||||
// -> where is it actually turned ON normally?
|
||||
//
|
||||
// #if defined(SPORT_UPDATE_PWR_GPIO)
|
||||
// if (spuPwr) {
|
||||
// SPORT_UPDATE_POWER_ON();
|
||||
// }
|
||||
// #endif
|
||||
|
||||
state = SPORT_IDLE;
|
||||
return result;
|
||||
}
|
||||
|
||||
#define CHIP_FIRMWARE_UPDATE_TIMEOUT 20000 /* 20s */
|
||||
|
||||
const char * FrskyChipFirmwareUpdate::waitAnswer(uint8_t & status)
|
||||
{
|
||||
watchdogSuspend(CHIP_FIRMWARE_UPDATE_TIMEOUT);
|
||||
|
||||
telemetryPortSetDirectionInput();
|
||||
|
||||
uint8_t buffer[12];
|
||||
for (uint8_t i = 0; i < sizeof(buffer); i++) {
|
||||
uint32_t retry = 0;
|
||||
while (true) {
|
||||
if (sportGetByte(&buffer[i])) {
|
||||
if ((i == 0 && buffer[0] != 0x7F) ||
|
||||
(i == 1 && buffer[1] != 0xFE) ||
|
||||
(i == 10 && buffer[10] != 0x0D) ||
|
||||
(i == 11 && buffer[11] != 0x0A)) {
|
||||
i = 0;
|
||||
continue;
|
||||
}
|
||||
break;
|
||||
}
|
||||
if (++retry == CHIP_FIRMWARE_UPDATE_TIMEOUT) {
|
||||
return STR_DEVICE_NO_RESPONSE;
|
||||
}
|
||||
RTOS_WAIT_MS(1);
|
||||
}
|
||||
}
|
||||
status = buffer[8];
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
const char * FrskyChipFirmwareUpdate::startBootloader()
|
||||
{
|
||||
sportSendByte(0x03);
|
||||
RTOS_WAIT_MS(20);
|
||||
sportSendByte(0x02);
|
||||
RTOS_WAIT_MS(20);
|
||||
sportSendByte(0x01);
|
||||
|
||||
for (uint8_t i = 0; i < 30; i++)
|
||||
sportSendByte(0x7E);
|
||||
|
||||
for (uint32_t i = 0; i < 100; i++) {
|
||||
RTOS_WAIT_MS(20);
|
||||
sportSendByte(0x7F);
|
||||
}
|
||||
|
||||
RTOS_WAIT_MS(20);
|
||||
sportSendByte(0xFA);
|
||||
|
||||
/*for (uint8_t i=0; i < 30; i++)
|
||||
sportSendByte(0x7E);
|
||||
for (uint8_t i=0; i < 50; i++)
|
||||
sportSendByte(0x7F);*/
|
||||
|
||||
uint8_t status;
|
||||
const char * result = waitAnswer(status);
|
||||
if (result)
|
||||
return result;
|
||||
|
||||
return status == 0x08 ? nullptr : "Bootloader failed";
|
||||
}
|
||||
|
||||
void FrskyChipFirmwareUpdate::sendByte(uint8_t byte, bool crcFlag)
|
||||
{
|
||||
sportSendByte(byte);
|
||||
if (crcFlag) {
|
||||
crc ^= byte;
|
||||
}
|
||||
}
|
||||
|
||||
const char * FrskyChipFirmwareUpdate::sendUpgradeCommand(char command, uint32_t packetsCount)
|
||||
{
|
||||
crc = 0;
|
||||
|
||||
// Head
|
||||
sendByte(0x7F, false);
|
||||
sendByte(0xFE, false);
|
||||
|
||||
// Addr
|
||||
sendByte(0xFA);
|
||||
|
||||
// Cmd
|
||||
sendByte(command);
|
||||
|
||||
// Packets count
|
||||
sendByte(packetsCount >> 8);
|
||||
sendByte(packetsCount);
|
||||
|
||||
// Len
|
||||
sendByte('E' == command ? 0x00 : 0x0C);
|
||||
sendByte(0x40);
|
||||
|
||||
// Data
|
||||
for (uint8_t i=0; i < 0x40; i++)
|
||||
sendByte('E' == command ? 0xF7 : 0x7F);
|
||||
|
||||
// Checksum
|
||||
sendByte(crc, false);
|
||||
|
||||
// Tail
|
||||
sendByte(0x0D, false);
|
||||
sendByte(0x0A, false);
|
||||
|
||||
uint8_t status;
|
||||
const char * result = waitAnswer(status);
|
||||
if (result)
|
||||
return result;
|
||||
|
||||
return status == 0x00 ? nullptr : STR_FIRMWARE_UPDATE_ERROR;
|
||||
}
|
||||
|
||||
const char * FrskyChipFirmwareUpdate::sendUpgradeData(uint32_t index, uint8_t * data)
|
||||
{
|
||||
crc = 0;
|
||||
|
||||
// Head
|
||||
sendByte(0x7F, false);
|
||||
sendByte(0xFE, false);
|
||||
|
||||
// Addr
|
||||
sendByte(0xFA);
|
||||
|
||||
// Cmd
|
||||
sendByte('W');
|
||||
|
||||
// Packets count
|
||||
sendByte(index >> 8);
|
||||
sendByte(index);
|
||||
|
||||
// Len
|
||||
sendByte(0x00);
|
||||
sendByte(0x40);
|
||||
|
||||
// Data
|
||||
for (uint8_t i = 0; i < 0x40; i++)
|
||||
sendByte(*data++);
|
||||
|
||||
// Checksum
|
||||
sendByte(crc, false);
|
||||
|
||||
// Tail
|
||||
sendByte(0x0D, false);
|
||||
sendByte(0x0A, false);
|
||||
|
||||
uint8_t status;
|
||||
const char * result = waitAnswer(status);
|
||||
if (result)
|
||||
return result;
|
||||
|
||||
return status == 0x00 ? nullptr : STR_FIRMWARE_UPDATE_ERROR;
|
||||
}
|
||||
|
||||
const char * FrskyChipFirmwareUpdate::doFlashFirmware(const char * filename, ProgressHandler progressHandler)
|
||||
{
|
||||
const char * result;
|
||||
FIL file;
|
||||
uint8_t buffer[64];
|
||||
UINT count;
|
||||
|
||||
result = startBootloader();
|
||||
if (result)
|
||||
return result;
|
||||
|
||||
if (f_open(&file, filename, FA_READ) != FR_OK) {
|
||||
return STR_NEEDS_FILE;
|
||||
}
|
||||
|
||||
FrSkyFirmwareInformation * information = (FrSkyFirmwareInformation *)buffer;
|
||||
if (f_read(&file, buffer, sizeof(FrSkyFirmwareInformation), &count) != FR_OK || count != sizeof(FrSkyFirmwareInformation)) {
|
||||
f_close(&file);
|
||||
return STR_DEVICE_FILE_ERROR;
|
||||
}
|
||||
|
||||
uint32_t packetsCount = (information->size + sizeof(buffer) - 1) / sizeof(buffer);
|
||||
progressHandler(getBasename(filename), STR_FLASH_WRITE, 0, packetsCount);
|
||||
|
||||
result = sendUpgradeCommand('A', packetsCount);
|
||||
if (result)
|
||||
return result;
|
||||
|
||||
uint32_t index = 0;
|
||||
while (1) {
|
||||
progressHandler(getBasename(filename), STR_FLASH_WRITE, index, packetsCount);
|
||||
if (f_read(&file, buffer, sizeof(buffer), &count) != FR_OK) {
|
||||
f_close(&file);
|
||||
return STR_DEVICE_FILE_ERROR;
|
||||
}
|
||||
result = sendUpgradeData(index + 1, buffer);
|
||||
if (result)
|
||||
return result;
|
||||
if (++index == packetsCount)
|
||||
break;
|
||||
}
|
||||
|
||||
f_close(&file);
|
||||
|
||||
return sendUpgradeCommand('E', packetsCount);
|
||||
}
|
||||
|
||||
const char * FrskyChipFirmwareUpdate::flashFirmware(const char * filename, ProgressHandler progressHandler, bool wait)
|
||||
{
|
||||
progressHandler(getBasename(filename), STR_DEVICE_RESET, 0, 0);
|
||||
|
||||
pausePulses();
|
||||
|
||||
#if defined(HARDWARE_INTERNAL_MODULE)
|
||||
uint8_t intPwr = IS_INTERNAL_MODULE_ON();
|
||||
INTERNAL_MODULE_OFF();
|
||||
#endif
|
||||
|
||||
#if defined(HARDWARE_EXTERNAL_MODULE)
|
||||
uint8_t extPwr = IS_EXTERNAL_MODULE_ON();
|
||||
EXTERNAL_MODULE_OFF();
|
||||
#endif
|
||||
|
||||
#if defined(SPORT_UPDATE_PWR_GPIO)
|
||||
uint8_t spuPwr = IS_SPORT_UPDATE_POWER_ON();
|
||||
SPORT_UPDATE_POWER_OFF();
|
||||
#endif
|
||||
|
||||
if (wait) {
|
||||
/* wait 2s off */
|
||||
watchdogSuspend(1000 /*10s*/);
|
||||
RTOS_WAIT_MS(2000);
|
||||
}
|
||||
|
||||
telemetryInit(PROTOCOL_TELEMETRY_FRSKY_SPORT);
|
||||
|
||||
const char * result = doFlashFirmware(filename, progressHandler);
|
||||
|
||||
AUDIO_PLAY(AU_SPECIAL_SOUND_BEEP1);
|
||||
BACKLIGHT_ENABLE();
|
||||
|
||||
if (result) {
|
||||
POPUP_WARNING(STR_FIRMWARE_UPDATE_ERROR, result);
|
||||
}
|
||||
else {
|
||||
POPUP_INFORMATION(STR_FIRMWARE_UPDATE_SUCCESS);
|
||||
}
|
||||
|
||||
/* wait 2s off */
|
||||
watchdogSuspend(1000 /*10s*/);
|
||||
RTOS_WAIT_MS(2000);
|
||||
|
||||
#if defined(HARDWARE_INTERNAL_MODULE)
|
||||
if (intPwr) {
|
||||
INTERNAL_MODULE_ON();
|
||||
setupPulsesInternalModule();
|
||||
}
|
||||
#endif
|
||||
|
||||
#if defined(HARDWARE_EXTERNAL_MODULE)
|
||||
if (extPwr) {
|
||||
EXTERNAL_MODULE_ON();
|
||||
setupPulsesExternalModule();
|
||||
}
|
||||
#endif
|
||||
|
||||
#if defined(SPORT_UPDATE_PWR_GPIO)
|
||||
if (spuPwr) {
|
||||
SPORT_UPDATE_POWER_ON();
|
||||
}
|
||||
#endif
|
||||
|
||||
resumePulses();
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
@@ -25,6 +25,8 @@
|
||||
#include "dataconstants.h"
|
||||
#include "definitions.h"
|
||||
|
||||
#include "hal/module_port.h"
|
||||
|
||||
enum FrskyFirmwareProductFamily {
|
||||
FIRMWARE_FAMILY_INTERNAL_MODULE,
|
||||
FIRMWARE_FAMILY_EXTERNAL_MODULE,
|
||||
@@ -133,6 +135,9 @@ class FrskyDeviceFirmwareUpdate {
|
||||
uint32_t address = 0;
|
||||
ModuleIndex module;
|
||||
uint8_t frame[12];
|
||||
|
||||
etx_module_state_t* mod_st = nullptr;
|
||||
const etx_serial_driver_t* uart_drv = nullptr;
|
||||
void* uart_ctx = nullptr;
|
||||
|
||||
void startFrame(uint8_t command);
|
||||
@@ -144,6 +149,7 @@ class FrskyDeviceFirmwareUpdate {
|
||||
const uint8_t * readFrame(uint32_t timeout);
|
||||
bool waitState(State state, uint32_t timeout);
|
||||
void processFrame(const uint8_t * frame);
|
||||
void sendDataTransfer(uint32_t* buffer);
|
||||
|
||||
const char * doFlashFirmware(const char * filename, ProgressHandler progressHandler);
|
||||
const char * sendPowerOn();
|
||||
@@ -153,24 +159,4 @@ class FrskyDeviceFirmwareUpdate {
|
||||
const char * endTransfer();
|
||||
};
|
||||
|
||||
class FrskyChipFirmwareUpdate {
|
||||
public:
|
||||
FrskyChipFirmwareUpdate()
|
||||
{
|
||||
}
|
||||
|
||||
const char * flashFirmware(const char * filename, ProgressHandler progressHandler, bool wait = true);
|
||||
|
||||
protected:
|
||||
uint8_t crc;
|
||||
|
||||
void sendByte(uint8_t byte, bool crc = true);
|
||||
const char * waitAnswer(uint8_t & status);
|
||||
const char * startBootloader();
|
||||
const char * sendUpgradeCommand(char command, uint32_t packetsCount);
|
||||
const char * sendUpgradeData(uint32_t index, uint8_t * data);
|
||||
|
||||
const char * doFlashFirmware(const char * filename, ProgressHandler progressHandler);
|
||||
};
|
||||
|
||||
#endif // _FRSKY_FIRMWARE_UPDATE_H_
|
||||
|
||||
@@ -21,6 +21,9 @@
|
||||
|
||||
#if !defined(DISABLE_MULTI_UPDATE)
|
||||
|
||||
#include "hal.h"
|
||||
#include "hal/module_port.h"
|
||||
|
||||
#include <stdio.h>
|
||||
#include "opentx.h"
|
||||
#include "multi_firmware_update.h"
|
||||
@@ -40,181 +43,121 @@
|
||||
#include "io/multi_protolist.h"
|
||||
#endif
|
||||
|
||||
#if defined(INTMODULE_USART)
|
||||
#include "intmodule_serial_driver.h"
|
||||
#endif
|
||||
|
||||
#include "extmodule_driver.h"
|
||||
|
||||
#define UPDATE_MULTI_EXT_BIN ".bin"
|
||||
|
||||
class MultiFirmwareUpdateDriver
|
||||
{
|
||||
public:
|
||||
MultiFirmwareUpdateDriver() {}
|
||||
virtual ~MultiFirmwareUpdateDriver() {}
|
||||
const char* flashFirmware(FIL* file, const char* label,
|
||||
ProgressHandler progressHandler);
|
||||
ModuleIndex module;
|
||||
MultiModuleType type;
|
||||
|
||||
protected:
|
||||
virtual void moduleOn() const = 0;
|
||||
virtual void init(bool inverted) = 0;
|
||||
virtual bool getByte(uint8_t & byte) const = 0;
|
||||
virtual void sendByte(uint8_t byte) const = 0;
|
||||
virtual void clear() const = 0;
|
||||
virtual void deinit(bool inverted) {}
|
||||
etx_module_state_t* mod_st = nullptr;
|
||||
|
||||
private:
|
||||
bool getRxByte(uint8_t & byte) const;
|
||||
bool checkRxByte(uint8_t byte) const;
|
||||
const char * waitForInitialSync(bool& inverted);
|
||||
const char * getDeviceSignature(uint8_t * signature) const;
|
||||
const char * loadAddress(uint32_t offset) const;
|
||||
const char * progPage(uint8_t * buffer, uint16_t size) const;
|
||||
void leaveProgMode(bool inverted);
|
||||
bool init();
|
||||
bool getByte(uint8_t& byte) const;
|
||||
void sendByte(uint8_t byte) const;
|
||||
void sendBuffer(uint8_t* buffer, uint16_t size) const;
|
||||
void clear() const;
|
||||
void deinit();
|
||||
|
||||
bool getRxByte(uint8_t& byte) const;
|
||||
bool checkRxByte(uint8_t byte) const;
|
||||
const char* waitForInitialSync();
|
||||
const char* getDeviceSignature(uint8_t* signature) const;
|
||||
const char* loadAddress(uint32_t offset) const;
|
||||
const char* progPage(uint8_t* buffer, uint16_t size) const;
|
||||
void leaveProgMode();
|
||||
|
||||
public:
|
||||
MultiFirmwareUpdateDriver(ModuleIndex module, MultiModuleType type) :
|
||||
module(module), type(type)
|
||||
{
|
||||
}
|
||||
|
||||
const char* flashFirmware(FIL* file, const char* label,
|
||||
ProgressHandler progressHandler);
|
||||
};
|
||||
|
||||
#if defined(INTERNAL_MODULE_MULTI)
|
||||
|
||||
static const etx_serial_init serialInitParams = {
|
||||
.baudrate = 0,
|
||||
.parity = ETX_Parity_None,
|
||||
.stop_bits = ETX_StopBits_One,
|
||||
.word_length = ETX_WordLength_8,
|
||||
.rx_enable = true,
|
||||
.baudrate = 57600,
|
||||
.encoding = ETX_Encoding_8N1,
|
||||
.direction = ETX_Dir_TX_RX,
|
||||
.polarity = ETX_Pol_Normal,
|
||||
};
|
||||
|
||||
class MultiInternalUpdateDriver: public MultiFirmwareUpdateDriver
|
||||
bool MultiFirmwareUpdateDriver::init()
|
||||
{
|
||||
public:
|
||||
MultiInternalUpdateDriver() {}
|
||||
|
||||
protected:
|
||||
void* uart_ctx = nullptr;
|
||||
|
||||
void moduleOn() const override
|
||||
{
|
||||
INTERNAL_MODULE_ON();
|
||||
if (type == MULTI_TYPE_MULTIMODULE && module == INTERNAL_MODULE) {
|
||||
// duplex internal USART
|
||||
mod_st = modulePortInitSerial(INTERNAL_MODULE, ETX_MOD_PORT_UART, &serialInitParams);
|
||||
} else if (type == MULTI_TYPE_MULTIMODULE && module == EXTERNAL_MODULE) {
|
||||
// TX on PPM (inverted softserial)
|
||||
etx_serial_init params(serialInitParams);
|
||||
params.direction = ETX_Dir_TX;
|
||||
params.polarity = ETX_Pol_Inverted;
|
||||
mod_st = modulePortInitSerial(EXTERNAL_MODULE, ETX_MOD_PORT_UART, ¶ms);
|
||||
if (!mod_st) {
|
||||
params.polarity = ETX_Pol_Normal;
|
||||
mod_st = modulePortInitSerial(EXTERNAL_MODULE, ETX_MOD_PORT_SOFT_INV, ¶ms);
|
||||
if (!mod_st) return false;
|
||||
}
|
||||
|
||||
void init(bool inverted) override
|
||||
{
|
||||
etx_serial_init params(serialInitParams);
|
||||
params.baudrate = 57600;
|
||||
uart_ctx = IntmoduleSerialDriver.init(¶ms);
|
||||
// RX on S.PORT (inverted softserial)
|
||||
params.direction = ETX_Dir_RX;
|
||||
params.polarity = ETX_Pol_Inverted;
|
||||
if (!modulePortInitSerial(EXTERNAL_MODULE, ETX_MOD_PORT_SPORT, ¶ms)) {
|
||||
params.polarity = ETX_Pol_Normal;
|
||||
if (!modulePortInitSerial(EXTERNAL_MODULE, ETX_MOD_PORT_SPORT_INV, ¶ms)) {
|
||||
modulePortDeInit(mod_st);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
} else if (type == MULTI_TYPE_ELRS && module == EXTERNAL_MODULE) {
|
||||
// half-duplex on S.PORT USART
|
||||
mod_st = modulePortInitSerial(EXTERNAL_MODULE, ETX_MOD_PORT_SPORT, &serialInitParams);
|
||||
}
|
||||
|
||||
bool getByte(uint8_t & byte) const override
|
||||
{
|
||||
return IntmoduleSerialDriver.getByte(uart_ctx, &byte) > 0;
|
||||
}
|
||||
if (!mod_st) return false;
|
||||
|
||||
void sendByte(uint8_t byte) const override
|
||||
{
|
||||
IntmoduleSerialDriver.sendByte(uart_ctx, byte);
|
||||
}
|
||||
modulePortSetPower(module, true);
|
||||
return true;
|
||||
}
|
||||
|
||||
void clear() const override
|
||||
{
|
||||
IntmoduleSerialDriver.clearRxBuffer(uart_ctx);
|
||||
}
|
||||
|
||||
void deinit(bool inverted) override
|
||||
{
|
||||
IntmoduleSerialDriver.deinit(uart_ctx);
|
||||
clear();
|
||||
uart_ctx = nullptr;
|
||||
}
|
||||
};
|
||||
#endif
|
||||
|
||||
#if defined(HARDWARE_EXTERNAL_MODULE)
|
||||
class MultiExternalUpdateDriver: public MultiFirmwareUpdateDriver
|
||||
bool MultiFirmwareUpdateDriver::getByte(uint8_t & byte) const
|
||||
{
|
||||
public:
|
||||
MultiExternalUpdateDriver() {}
|
||||
auto drv = modulePortGetSerialDrv(mod_st->rx);
|
||||
auto ctx = modulePortGetCtx(mod_st->rx);
|
||||
return drv->getByte(ctx, &byte) > 0;
|
||||
}
|
||||
|
||||
protected:
|
||||
void moduleOn() const override
|
||||
{
|
||||
EXTERNAL_MODULE_ON();
|
||||
}
|
||||
|
||||
void init(bool inverted) override
|
||||
{
|
||||
extmoduleInitTxPin();
|
||||
|
||||
if (inverted)
|
||||
telemetryPortInvertedInit(57600);
|
||||
else
|
||||
telemetryPortInit(57600, TELEMETRY_SERIAL_WITHOUT_DMA);
|
||||
}
|
||||
|
||||
bool getByte(uint8_t & byte) const override
|
||||
{
|
||||
return sportGetByte(&byte);
|
||||
}
|
||||
|
||||
void sendByte(uint8_t byte) const override
|
||||
{
|
||||
extmoduleSendInvertedByte(byte);
|
||||
}
|
||||
|
||||
void clear() const override
|
||||
{
|
||||
telemetryClearFifo();
|
||||
}
|
||||
|
||||
void deinit(bool inverted) override
|
||||
{
|
||||
if (inverted)
|
||||
telemetryPortInvertedInit(0);
|
||||
else
|
||||
telemetryPortInit(0, 0);
|
||||
|
||||
clear();
|
||||
}
|
||||
};
|
||||
#endif
|
||||
|
||||
class MultiExtSportUpdateDriver: public MultiFirmwareUpdateDriver
|
||||
void MultiFirmwareUpdateDriver::sendByte(uint8_t byte) const
|
||||
{
|
||||
public:
|
||||
MultiExtSportUpdateDriver(): MultiFirmwareUpdateDriver() {}
|
||||
auto drv = modulePortGetSerialDrv(mod_st->tx);
|
||||
auto ctx = modulePortGetCtx(mod_st->tx);
|
||||
drv->sendByte(ctx, byte);
|
||||
}
|
||||
|
||||
protected:
|
||||
void moduleOn() const override
|
||||
{
|
||||
EXTERNAL_MODULE_ON();
|
||||
}
|
||||
void MultiFirmwareUpdateDriver::sendBuffer(uint8_t* buffer, uint16_t size) const
|
||||
{
|
||||
auto drv = modulePortGetSerialDrv(mod_st->tx);
|
||||
auto ctx = modulePortGetCtx(mod_st->tx);
|
||||
|
||||
void init(bool inverted) override
|
||||
{
|
||||
telemetryPortInit(57600, TELEMETRY_SERIAL_WITHOUT_DMA);
|
||||
}
|
||||
drv->waitForTxCompleted(ctx);
|
||||
drv->sendBuffer(ctx, buffer, size);
|
||||
drv->waitForTxCompleted(ctx);
|
||||
}
|
||||
|
||||
bool getByte(uint8_t & byte) const override
|
||||
{
|
||||
return sportGetByte(&byte);
|
||||
}
|
||||
void MultiFirmwareUpdateDriver::clear() const
|
||||
{
|
||||
auto drv = modulePortGetSerialDrv(mod_st->rx);
|
||||
auto ctx = modulePortGetCtx(mod_st->rx);
|
||||
drv->clearRxBuffer(ctx);
|
||||
}
|
||||
|
||||
void sendByte(uint8_t byte) const override
|
||||
{
|
||||
sportSendByte(byte);
|
||||
telemetryPortSetDirectionInput();
|
||||
}
|
||||
|
||||
void clear() const override
|
||||
{
|
||||
telemetryClearFifo();
|
||||
}
|
||||
|
||||
void deinit(bool inverted) override
|
||||
{
|
||||
telemetryPortInit(0, 0);
|
||||
clear();
|
||||
}
|
||||
};
|
||||
void MultiFirmwareUpdateDriver::deinit()
|
||||
{
|
||||
clear();
|
||||
modulePortSetPower(module, false);
|
||||
modulePortDeInit(mod_st);
|
||||
}
|
||||
|
||||
bool MultiFirmwareUpdateDriver::getRxByte(uint8_t & byte) const
|
||||
{
|
||||
@@ -241,7 +184,7 @@ bool MultiFirmwareUpdateDriver::checkRxByte(uint8_t byte) const
|
||||
return getRxByte(rxchar) ? rxchar == byte : false;
|
||||
}
|
||||
|
||||
const char * MultiFirmwareUpdateDriver::waitForInitialSync(bool & inverted)
|
||||
const char * MultiFirmwareUpdateDriver::waitForInitialSync()
|
||||
{
|
||||
uint8_t byte;
|
||||
int retries = 200;
|
||||
@@ -253,13 +196,6 @@ const char * MultiFirmwareUpdateDriver::waitForInitialSync(bool & inverted)
|
||||
clear();
|
||||
do {
|
||||
|
||||
// Invert at half-time
|
||||
if (retries == 100) {
|
||||
deinit(inverted);
|
||||
inverted = !inverted;
|
||||
init(inverted);
|
||||
}
|
||||
|
||||
// Send sync request
|
||||
sendByte(STK_GET_SYNC);
|
||||
sendByte(CRC_EOP);
|
||||
@@ -300,7 +236,6 @@ const char * MultiFirmwareUpdateDriver::getDeviceSignature(uint8_t * signature)
|
||||
// Read signature
|
||||
sendByte(STK_READ_SIGN);
|
||||
sendByte(CRC_EOP);
|
||||
clear();
|
||||
|
||||
if (!checkRxByte(STK_INSYNC))
|
||||
return STR_DEVICE_NO_RESPONSE;
|
||||
@@ -343,9 +278,7 @@ const char * MultiFirmwareUpdateDriver::progPage(uint8_t * buffer, uint16_t size
|
||||
// flash/eeprom flag
|
||||
sendByte(0);
|
||||
|
||||
for (uint16_t i = 0; i < size; i++) {
|
||||
sendByte(buffer[i]);
|
||||
}
|
||||
sendBuffer(buffer, size);
|
||||
|
||||
sendByte(CRC_EOP);
|
||||
|
||||
@@ -365,14 +298,14 @@ const char * MultiFirmwareUpdateDriver::progPage(uint8_t * buffer, uint16_t size
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
void MultiFirmwareUpdateDriver::leaveProgMode(bool inverted)
|
||||
void MultiFirmwareUpdateDriver::leaveProgMode()
|
||||
{
|
||||
sendByte(STK_LEAVE_PROGMODE);
|
||||
sendByte(CRC_EOP);
|
||||
|
||||
// eat last sync byte
|
||||
checkRxByte(STK_INSYNC);
|
||||
deinit(inverted);
|
||||
deinit();
|
||||
}
|
||||
|
||||
const char* MultiFirmwareUpdateDriver::flashFirmware(
|
||||
@@ -388,25 +321,22 @@ const char* MultiFirmwareUpdateDriver::flashFirmware(
|
||||
#endif
|
||||
|
||||
const char * result = nullptr;
|
||||
moduleOn();
|
||||
|
||||
bool inverted = true; //false; // true
|
||||
init(inverted);
|
||||
if (!init()) return "Initialisation error";
|
||||
|
||||
/* wait 500ms for power on */
|
||||
watchdogSuspend(500 /*5s*/);
|
||||
RTOS_WAIT_MS(500);
|
||||
|
||||
result = waitForInitialSync(inverted);
|
||||
result = waitForInitialSync();
|
||||
if (result) {
|
||||
leaveProgMode(inverted);
|
||||
leaveProgMode();
|
||||
return result;
|
||||
}
|
||||
|
||||
unsigned char signature[4]; // 3 bytes signature + STK_OK
|
||||
result = getDeviceSignature(signature);
|
||||
if (result) {
|
||||
leaveProgMode(inverted);
|
||||
leaveProgMode();
|
||||
return result;
|
||||
}
|
||||
|
||||
@@ -415,7 +345,7 @@ const char* MultiFirmwareUpdateDriver::flashFirmware(
|
||||
uint32_t writeOffset = 0;
|
||||
|
||||
if (signature[0] != 0x1E) {
|
||||
leaveProgMode(inverted);
|
||||
leaveProgMode();
|
||||
return STR_DEVICE_FILE_WRONG_SIG;
|
||||
}
|
||||
|
||||
@@ -456,7 +386,7 @@ const char* MultiFirmwareUpdateDriver::flashFirmware(
|
||||
progressHandler(label, STR_WRITING, file->fptr, file->obj.objsize);
|
||||
}
|
||||
|
||||
leaveProgMode(inverted);
|
||||
leaveProgMode();
|
||||
return result;
|
||||
}
|
||||
|
||||
@@ -591,6 +521,7 @@ bool MultiDeviceFirmwareUpdate::flashFirmware(const char * filename, ProgressHan
|
||||
}
|
||||
f_lseek(&file, 0);
|
||||
|
||||
#if defined(HARDWARE_EXTERNAL_MODULE)
|
||||
if (module == EXTERNAL_MODULE) {
|
||||
if (!firmwareFile.isMultiExternalFirmware()) {
|
||||
f_close(&file);
|
||||
@@ -598,43 +529,29 @@ bool MultiDeviceFirmwareUpdate::flashFirmware(const char * filename, ProgressHan
|
||||
return false;
|
||||
}
|
||||
}
|
||||
else {
|
||||
#endif
|
||||
|
||||
#if defined(HARDWARE_INTERNAL_MODULE)
|
||||
if (module == INTERNAL_MODULE) {
|
||||
if (!firmwareFile.isMultiInternalFirmware()) {
|
||||
f_close(&file);
|
||||
POPUP_WARNING(STR_NEEDS_FILE, STR_INT_MULTI_SPEC);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
std::unique_ptr<MultiFirmwareUpdateDriver> driver;
|
||||
#if defined(INTERNAL_MODULE_MULTI)
|
||||
if (type == MULTI_TYPE_MULTIMODULE && module == INTERNAL_MODULE)
|
||||
driver.reset(new MultiInternalUpdateDriver());
|
||||
#endif
|
||||
#if defined(HARDWARE_EXTERNAL_MODULE)
|
||||
if (type == MULTI_TYPE_MULTIMODULE && module == EXTERNAL_MODULE)
|
||||
driver.reset(new MultiExternalUpdateDriver());
|
||||
if (type == MULTI_TYPE_ELRS && module == EXTERNAL_MODULE)
|
||||
driver.reset(new MultiExtSportUpdateDriver());
|
||||
pulsesStop();
|
||||
|
||||
#if defined(MULTI_PROTOLIST)
|
||||
for (uint8_t i = 0; i < MAX_MODULES; i++) {
|
||||
MultiRfProtocols::removeInstance(i);
|
||||
}
|
||||
#endif
|
||||
|
||||
pausePulses();
|
||||
|
||||
#if defined(HARDWARE_INTERNAL_MODULE)
|
||||
uint8_t intPwr = IS_INTERNAL_MODULE_ON();
|
||||
INTERNAL_MODULE_OFF();
|
||||
#endif
|
||||
|
||||
#if defined(HARDWARE_EXTERNAL_MODULE)
|
||||
uint8_t extPwr = IS_EXTERNAL_MODULE_ON();
|
||||
EXTERNAL_MODULE_OFF();
|
||||
#endif
|
||||
|
||||
#if defined(SPORT_UPDATE_PWR_GPIO)
|
||||
uint8_t spuPwr = IS_SPORT_UPDATE_POWER_ON();
|
||||
SPORT_UPDATE_POWER_OFF();
|
||||
#endif
|
||||
// switch S.PORT power OFF if supported
|
||||
modulePortSetPower(SPORT_MODULE, false);
|
||||
|
||||
progressHandler(getBasename(filename), STR_DEVICE_RESET, 0, 0);
|
||||
|
||||
@@ -642,22 +559,13 @@ bool MultiDeviceFirmwareUpdate::flashFirmware(const char * filename, ProgressHan
|
||||
watchdogSuspend(500 /*5s*/);
|
||||
RTOS_WAIT_MS(3000);
|
||||
|
||||
const char * result = driver->flashFirmware(&file, getBasename(filename), progressHandler);
|
||||
MultiFirmwareUpdateDriver driver(module, type);
|
||||
const char * result = driver.flashFirmware(&file, getBasename(filename), progressHandler);
|
||||
f_close(&file);
|
||||
|
||||
AUDIO_PLAY(AU_SPECIAL_SOUND_BEEP1);
|
||||
BACKLIGHT_ENABLE();
|
||||
|
||||
#if defined(HARDWARE_INTERNAL_MODULE)
|
||||
INTERNAL_MODULE_OFF();
|
||||
#endif
|
||||
EXTERNAL_MODULE_OFF();
|
||||
SPORT_UPDATE_POWER_OFF();
|
||||
|
||||
/* wait 2s off */
|
||||
watchdogSuspend(500 /*5s*/);
|
||||
RTOS_WAIT_MS(2000);
|
||||
|
||||
if (result) {
|
||||
POPUP_WARNING(STR_FIRMWARE_UPDATE_ERROR, result);
|
||||
}
|
||||
@@ -665,36 +573,17 @@ bool MultiDeviceFirmwareUpdate::flashFirmware(const char * filename, ProgressHan
|
||||
POPUP_INFORMATION(STR_FIRMWARE_UPDATE_SUCCESS);
|
||||
}
|
||||
|
||||
// reset telemetry protocol
|
||||
telemetryInit(255);
|
||||
watchdogSuspend(50 /*500ms*/);
|
||||
pulsesStart();
|
||||
|
||||
#if defined(HARDWARE_INTERNAL_MODULE)
|
||||
if (intPwr) {
|
||||
#if defined(MULTI_PROTOLIST)
|
||||
MultiRfProtocols::removeInstance(INTERNAL_MODULE);
|
||||
#endif
|
||||
INTERNAL_MODULE_ON();
|
||||
setupPulsesInternalModule();
|
||||
}
|
||||
#endif
|
||||
|
||||
#if defined(HARDWARE_EXTERNAL_MODULE)
|
||||
if (extPwr) {
|
||||
#if defined(MULTI_PROTOLIST)
|
||||
MultiRfProtocols::removeInstance(EXTERNAL_MODULE);
|
||||
#endif
|
||||
EXTERNAL_MODULE_ON();
|
||||
setupPulsesExternalModule();
|
||||
}
|
||||
#endif
|
||||
|
||||
#if defined(SPORT_UPDATE_PWR_GPIO)
|
||||
if (spuPwr) {
|
||||
SPORT_UPDATE_POWER_ON();
|
||||
}
|
||||
#endif
|
||||
|
||||
resumePulses();
|
||||
// TODO: S.PORT power control
|
||||
// -> where is it actually turned ON normally?
|
||||
//
|
||||
// #if defined(SPORT_UPDATE_PWR_GPIO)
|
||||
// if (spuPwr) {
|
||||
// SPORT_UPDATE_POWER_ON();
|
||||
// }
|
||||
// #endif
|
||||
|
||||
return result == nullptr;
|
||||
}
|
||||
|
||||
@@ -74,12 +74,16 @@ class MultiFirmwareInformation
|
||||
|
||||
bool isMultiInternalFirmware() const
|
||||
{
|
||||
return (boardType == FIRMWARE_MULTI_STM && optibootSupport && bootloaderCheck && telemetryType == FIRMWARE_MULTI_TELEM_MULTI_TELEMETRY);
|
||||
return (boardType == FIRMWARE_MULTI_STM && optibootSupport &&
|
||||
bootloaderCheck &&
|
||||
telemetryType == FIRMWARE_MULTI_TELEM_MULTI_TELEMETRY);
|
||||
}
|
||||
|
||||
bool isMultiExternalFirmware() const
|
||||
{
|
||||
return ((telemetryInversion || boardType == FIRMWARE_MULTI_STM ) && optibootSupport && bootloaderCheck && telemetryType == FIRMWARE_MULTI_TELEM_MULTI_TELEMETRY);
|
||||
return ((telemetryInversion || boardType == FIRMWARE_MULTI_STM) &&
|
||||
optibootSupport && bootloaderCheck &&
|
||||
telemetryType == FIRMWARE_MULTI_TELEM_MULTI_TELEMETRY);
|
||||
}
|
||||
|
||||
const char * readMultiFirmwareInformation(const char * filename);
|
||||
|
||||
@@ -54,7 +54,7 @@ void MultiRfProtocols::timerCb(TimerHandle_t xTimer)
|
||||
}
|
||||
#endif
|
||||
|
||||
MultiRfProtocols* MultiRfProtocols::_instance[NUM_MODULES] = {};
|
||||
MultiRfProtocols* MultiRfProtocols::_instance[MAX_MODULES] = {};
|
||||
|
||||
// MPM telemetry packet format type = MultiProtoDef (0x11)
|
||||
//
|
||||
|
||||
+3
-1
@@ -34,13 +34,15 @@ static tmr10ms_t lastLogTime = 0;
|
||||
#include <FreeRTOS/include/FreeRTOS.h>
|
||||
#include <FreeRTOS/include/timers.h>
|
||||
|
||||
#include "tasks/mixer_task.h"
|
||||
|
||||
static TimerHandle_t loggingTimer = nullptr;
|
||||
static StaticTimer_t loggingTimerBuffer;
|
||||
|
||||
static void loggingTimerCb(TimerHandle_t xTimer)
|
||||
{
|
||||
(void)xTimer;
|
||||
if (!s_pulses_paused) {
|
||||
if (!mixerTaskRunning()) {
|
||||
DEBUG_TIMER_START(debugTimerLoggingWakeup);
|
||||
logsWrite();
|
||||
DEBUG_TIMER_STOP(debugTimerLoggingWakeup);
|
||||
|
||||
@@ -25,7 +25,7 @@
|
||||
#include "stamp.h"
|
||||
#include "lua_api.h"
|
||||
#include "api_filesystem.h"
|
||||
#include "aux_serial_driver.h"
|
||||
#include "hal/module_port.h"
|
||||
|
||||
#if defined(LIBOPENUI)
|
||||
#include "libopenui.h"
|
||||
@@ -915,10 +915,20 @@ When called without parameters, it will only return the status of the output buf
|
||||
@status current Introduced in 2.2.0, retval nil added in 2.3.4
|
||||
*/
|
||||
|
||||
static bool _supports_sport(uint8_t module)
|
||||
{
|
||||
return IS_NATIVE_FRSKY_PROTOCOL(module) ||
|
||||
(isModuleMultimodule(module) &&
|
||||
(IS_D16_MULTI(module) || IS_R9_MULTI(module)));
|
||||
}
|
||||
|
||||
static int luaSportTelemetryPush(lua_State * L)
|
||||
{
|
||||
bool extmod = _supports_sport(EXTERNAL_MODULE);
|
||||
bool intmod = _supports_sport(INTERNAL_MODULE);
|
||||
|
||||
// dirty hack until 2 simultanous protocols are supported
|
||||
if (isModuleCrossfire(INTERNAL_MODULE) || !IS_FRSKY_SPORT_PROTOCOL()) {
|
||||
if (!extmod && !intmod) {
|
||||
lua_pushnil(L);
|
||||
return 1;
|
||||
}
|
||||
@@ -966,9 +976,11 @@ static int luaSportTelemetryPush(lua_State * L)
|
||||
packet.dataId = dataId;
|
||||
packet.value = luaL_checkunsigned(L, 4);
|
||||
outputTelemetryBuffer.pushSportPacketWithBytestuffing(packet);
|
||||
#if defined(PXX2)
|
||||
uint8_t destination = (IS_INTERNAL_MODULE_ON() ? INTERNAL_MODULE : EXTERNAL_MODULE);
|
||||
outputTelemetryBuffer.setDestination(isModulePXX2(destination) ? (destination << 2) : TELEMETRY_ENDPOINT_SPORT);
|
||||
#if defined(PXX2) && defined(HARDWARE_EXTERNAL_MODULE)
|
||||
uint8_t destination = (intmod ? INTERNAL_MODULE : EXTERNAL_MODULE);
|
||||
outputTelemetryBuffer.setDestination(isModulePXX2(destination)
|
||||
? (destination << 2)
|
||||
: TELEMETRY_ENDPOINT_SPORT);
|
||||
#else
|
||||
outputTelemetryBuffer.setDestination(TELEMETRY_ENDPOINT_SPORT);
|
||||
#endif
|
||||
@@ -1120,10 +1132,10 @@ When called without parameters, it will only return the status of the output buf
|
||||
*/
|
||||
static int luaCrossfireTelemetryPush(lua_State * L)
|
||||
{
|
||||
bool sport = (telemetryProtocol == PROTOCOL_TELEMETRY_CROSSFIRE);
|
||||
bool external = (moduleState[EXTERNAL_MODULE].protocol == PROTOCOL_CHANNELS_CROSSFIRE);
|
||||
bool internal = (moduleState[INTERNAL_MODULE].protocol == PROTOCOL_CHANNELS_CROSSFIRE);
|
||||
|
||||
if (!internal && !sport) {
|
||||
if (!internal && !external) {
|
||||
lua_pushnil(L);
|
||||
return 1;
|
||||
}
|
||||
@@ -1218,9 +1230,8 @@ When called without parameters, it will only return the status of the output buf
|
||||
*/
|
||||
static int luaGhostTelemetryPush(lua_State * L)
|
||||
{
|
||||
bool sport = (telemetryProtocol == PROTOCOL_TELEMETRY_GHOST);
|
||||
|
||||
if (!sport) {
|
||||
bool extmod = (moduleState[EXTERNAL_MODULE].protocol == PROTOCOL_CHANNELS_GHOST);
|
||||
if (!extmod) {
|
||||
lua_pushnil(L);
|
||||
return 1;
|
||||
}
|
||||
@@ -1242,18 +1253,20 @@ static int luaGhostTelemetryPush(lua_State * L)
|
||||
return 1;
|
||||
}
|
||||
|
||||
// Ghost frames are fixed 14B
|
||||
outputTelemetryBuffer.pushByte(getGhostModuleAddr()); // addr (1B)
|
||||
outputTelemetryBuffer.pushByte(12); // len = payload length(10B) + type(1B) + crc(1B)
|
||||
// Ghost frames are fixed 14B:
|
||||
// address(1B) + len (1B) + type(1B) + payload(10B) + crc(1B)
|
||||
// -> address + len up-front are inserted later
|
||||
outputTelemetryBuffer.pushByte(type); // type (1B)
|
||||
for (int i=0; i<length; i++) { // data, max 10B
|
||||
lua_rawgeti(L, 2, i+1);
|
||||
int i = 0;
|
||||
for (; i < length; i++) { // data, max 10B
|
||||
lua_rawgeti(L, 2, i + 1);
|
||||
outputTelemetryBuffer.pushByte(luaL_checkunsigned(L, -1));
|
||||
}
|
||||
for (int i=0; i<10-length; i++) { // fill zeroes to frame size
|
||||
for (; i < 10; i++) { // fill zeroes to frame size
|
||||
outputTelemetryBuffer.pushByte(0);
|
||||
}
|
||||
outputTelemetryBuffer.pushByte(crc8(outputTelemetryBuffer.data + 2, 11 )); // Start at type, CRC over type (1B) + payload (10B)
|
||||
// CRC over type (1B) + payload (10B)
|
||||
outputTelemetryBuffer.pushByte(crc8(outputTelemetryBuffer.data, 11 ));
|
||||
outputTelemetryBuffer.setDestination(TELEMETRY_ENDPOINT_SPORT);
|
||||
lua_pushboolean(L, true);
|
||||
}
|
||||
|
||||
@@ -31,6 +31,10 @@
|
||||
#include <storage/sdcard_yaml.h>
|
||||
#endif
|
||||
|
||||
#if defined(MULTIMODULE)
|
||||
#include "pulses/multi.h"
|
||||
#endif
|
||||
|
||||
/*luadoc
|
||||
@function model.getInfo()
|
||||
|
||||
|
||||
+156
-2
@@ -23,6 +23,8 @@
|
||||
#include "timers.h"
|
||||
#include "switches.h"
|
||||
|
||||
uint8_t s_mixer_first_run_done = false;
|
||||
|
||||
int8_t virtualInputsTrims[MAX_INPUTS];
|
||||
int16_t anas [MAX_INPUTS] = {0};
|
||||
int16_t trims[NUM_TRIMS] = {0};
|
||||
@@ -34,7 +36,6 @@ SwOn swOn [MAX_MIXERS]; // TODO better name later...
|
||||
|
||||
uint8_t mixWarning;
|
||||
|
||||
|
||||
int16_t calibratedAnalogs[NUM_CALIBRATED_ANALOGS];
|
||||
int16_t channelOutputs[MAX_OUTPUT_CHANNELS] = {0};
|
||||
int16_t ex_chans[MAX_OUTPUT_CHANNELS] = {0}; // Outputs (before LIMITS) of the last perMain;
|
||||
@@ -455,6 +456,20 @@ getvalue_t getValue(mixsrc_t i, bool* valid)
|
||||
}
|
||||
}
|
||||
|
||||
void evalTrims()
|
||||
{
|
||||
uint8_t phase = mixerCurrentFlightMode;
|
||||
for (uint8_t i=0; i<NUM_TRIMS; i++) {
|
||||
// do trim -> throttle trim if applicable
|
||||
int16_t trim = getTrimValue(phase, i);
|
||||
if (trimsCheckTimer > 0) {
|
||||
trim = 0;
|
||||
}
|
||||
|
||||
trims[i] = trim*2;
|
||||
}
|
||||
}
|
||||
|
||||
void evalInputs(uint8_t mode)
|
||||
{
|
||||
BeepANACenter anaCenter = 0;
|
||||
@@ -524,7 +539,8 @@ void evalInputs(uint8_t mode)
|
||||
if (mode == e_perout_mode_normal) {
|
||||
if (tmp==0 || (tmp==1 && (bpanaCenter & mask))) {
|
||||
anaCenter |= mask;
|
||||
if ((g_model.beepANACenter & mask) && !(bpanaCenter & mask) && s_mixer_first_run_done && !menuCalibrationState) {
|
||||
if ((g_model.beepANACenter & mask) && !(bpanaCenter & mask) &&
|
||||
s_mixer_first_run_done && !menuCalibrationState) {
|
||||
if (!IS_POT(i) || IS_POT_SLIDER_AVAILABLE(i)) {
|
||||
AUDIO_POT_MIDDLE(i);
|
||||
}
|
||||
@@ -625,6 +641,7 @@ int getSourceTrimValue(int source, int stickValue=0)
|
||||
}
|
||||
|
||||
uint8_t mixerCurrentFlightMode;
|
||||
|
||||
void evalFlightModeMixes(uint8_t mode, uint8_t tick10ms)
|
||||
{
|
||||
evalInputs(mode);
|
||||
@@ -1092,3 +1109,140 @@ void evalMixes(uint8_t tick10ms)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#if defined(THRTRACE)
|
||||
uint8_t s_traceBuf[MAXTRACE];
|
||||
uint16_t s_traceWr;
|
||||
uint8_t s_cnt_10s;
|
||||
uint16_t s_cnt_samples_thr_10s;
|
||||
uint16_t s_sum_samples_thr_10s;
|
||||
#endif
|
||||
|
||||
void doMixerPeriodicUpdates()
|
||||
{
|
||||
static tmr10ms_t lastTMR = 0;
|
||||
|
||||
tmr10ms_t tmr10ms = get_tmr10ms();
|
||||
|
||||
uint8_t tick10ms = (tmr10ms >= lastTMR ? tmr10ms - lastTMR : 1);
|
||||
// handle tick10ms overrun
|
||||
// correct overflow handling costs a lot of code; happens only each 11 min;
|
||||
// therefore forget the exact calculation and use only 1 instead; good compromise
|
||||
lastTMR = tmr10ms;
|
||||
|
||||
DEBUG_TIMER_START(debugTimerMixes10ms);
|
||||
if (tick10ms) {
|
||||
/* Throttle trace */
|
||||
int16_t val;
|
||||
|
||||
if (g_model.thrTraceSrc > NUM_POTS+NUM_SLIDERS) {
|
||||
uint8_t ch = g_model.thrTraceSrc-NUM_POTS-NUM_SLIDERS-1;
|
||||
val = channelOutputs[ch];
|
||||
|
||||
LimitData * lim = limitAddress(ch);
|
||||
int16_t gModelMax = LIMIT_MAX_RESX(lim);
|
||||
int16_t gModelMin = LIMIT_MIN_RESX(lim);
|
||||
|
||||
if (lim->revert)
|
||||
val = -val + gModelMax;
|
||||
else
|
||||
val = val - gModelMin;
|
||||
|
||||
#if defined(PPM_LIMITS_SYMETRICAL)
|
||||
if (lim->symetrical) {
|
||||
val -= calc1000toRESX(lim->offset);
|
||||
}
|
||||
#endif
|
||||
|
||||
gModelMax -= gModelMin; // we compare difference between Max and Mix for recaling needed; Max and Min are shifted to 0 by default
|
||||
// usually max is 1024 min is -1024 --> max-min = 2048 full range
|
||||
|
||||
if (gModelMax != 0 && gModelMax != 2048)
|
||||
val = (int32_t) (val << 11) / (gModelMax); // rescaling only needed if Min, Max differs
|
||||
|
||||
if (val < 0)
|
||||
val=0; // prevent val be negative, which would corrupt throttle trace and timers; could occur if safetyswitch is smaller than limits
|
||||
}
|
||||
else {
|
||||
val = RESX + calibratedAnalogs[g_model.thrTraceSrc == 0 ? THR_STICK : g_model.thrTraceSrc+NUM_STICKS-1];
|
||||
}
|
||||
|
||||
val >>= (RESX_SHIFT-6); // calibrate it (resolution increased by factor 4)
|
||||
|
||||
evalTimers(val, tick10ms);
|
||||
|
||||
static uint8_t s_cnt_100ms;
|
||||
static uint8_t s_cnt_1s;
|
||||
static uint8_t s_cnt_samples_thr_1s;
|
||||
static uint16_t s_sum_samples_thr_1s;
|
||||
|
||||
s_cnt_samples_thr_1s++;
|
||||
s_sum_samples_thr_1s+=val;
|
||||
|
||||
if ((s_cnt_100ms += tick10ms) >= 10) { // 0.1sec
|
||||
s_cnt_100ms -= 10;
|
||||
s_cnt_1s += 1;
|
||||
|
||||
logicalSwitchesTimerTick();
|
||||
checkTrainerSignalWarning();
|
||||
|
||||
if (s_cnt_1s >= 10) { // 1sec
|
||||
s_cnt_1s -= 10;
|
||||
sessionTimer += 1;
|
||||
inactivity.counter++;
|
||||
if ((((uint8_t)inactivity.counter) & 0x07) == 0x01 && g_eeGeneral.inactivityTimer && inactivity.counter > ((uint16_t)g_eeGeneral.inactivityTimer * 60))
|
||||
AUDIO_INACTIVITY();
|
||||
|
||||
#if defined(AUDIO)
|
||||
if (mixWarning & 1) if ((sessionTimer&0x03)==0) AUDIO_MIX_WARNING(1);
|
||||
if (mixWarning & 2) if ((sessionTimer&0x03)==1) AUDIO_MIX_WARNING(2);
|
||||
if (mixWarning & 4) if ((sessionTimer&0x03)==2) AUDIO_MIX_WARNING(3);
|
||||
#endif
|
||||
|
||||
val = s_sum_samples_thr_1s / s_cnt_samples_thr_1s;
|
||||
s_timeCum16ThrP += (val>>3); // s_timeCum16ThrP would overrun if we would store throttle value with higher accuracy; therefore stay with 16 steps
|
||||
if (val)
|
||||
s_timeCumThr += 1;
|
||||
s_sum_samples_thr_1s >>= 2; // correct better accuracy now, because trace graph can show this information; in case thrtrace is not active, the compile should remove this
|
||||
|
||||
#if defined(THRTRACE)
|
||||
// throttle trace is done every 10 seconds; Tracebuffer is adjusted to screen size.
|
||||
// in case buffer runs out, it wraps around
|
||||
// resolution for y axis is only 32, therefore no higher value makes sense
|
||||
s_cnt_samples_thr_10s += s_cnt_samples_thr_1s;
|
||||
s_sum_samples_thr_10s += s_sum_samples_thr_1s;
|
||||
|
||||
if (++s_cnt_10s >= 10) { // 10s
|
||||
s_cnt_10s -= 10;
|
||||
val = s_sum_samples_thr_10s / s_cnt_samples_thr_10s;
|
||||
s_sum_samples_thr_10s = 0;
|
||||
s_cnt_samples_thr_10s = 0;
|
||||
s_traceBuf[s_traceWr % MAXTRACE] = val;
|
||||
s_traceWr++;
|
||||
}
|
||||
#endif
|
||||
|
||||
s_cnt_samples_thr_1s = 0;
|
||||
s_sum_samples_thr_1s = 0;
|
||||
}
|
||||
}
|
||||
|
||||
#if defined(PXX) || defined(DSM2)
|
||||
static uint8_t countRangecheck = 0;
|
||||
for (uint8_t i = 0; i < NUM_MODULES; ++i) {
|
||||
if (isModuleBeeping(i)) {
|
||||
if (++countRangecheck >= 250) {
|
||||
countRangecheck = 0;
|
||||
AUDIO_PLAY(AU_SPECIAL_SOUND_CHEEP);
|
||||
}
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
checkTrims();
|
||||
}
|
||||
|
||||
DEBUG_TIMER_STOP(debugTimerMixes10ms);
|
||||
|
||||
s_mixer_first_run_done = true;
|
||||
}
|
||||
|
||||
@@ -21,6 +21,7 @@
|
||||
|
||||
#include "opentx.h"
|
||||
#include "mixer_scheduler.h"
|
||||
#include "tasks/mixer_task.h"
|
||||
|
||||
bool mixerSchedulerWaitForTrigger(uint8_t timeoutMs)
|
||||
{
|
||||
@@ -98,6 +99,11 @@ void mixerSchedulerSetPeriod(uint8_t moduleIdx, uint16_t periodUs)
|
||||
mixerSchedules[moduleIdx].period = periodUs;
|
||||
}
|
||||
|
||||
uint16_t mixerSchedulerGetPeriod(uint8_t moduleIdx)
|
||||
{
|
||||
return mixerSchedules[moduleIdx].period;
|
||||
}
|
||||
|
||||
void mixerSchedulerISRTrigger()
|
||||
{
|
||||
BaseType_t xHigherPriorityTaskWoken = pdFALSE;
|
||||
|
||||
@@ -46,6 +46,9 @@ void mixerSchedulerResetTimer();
|
||||
// Set the scheduling period for a given module
|
||||
void mixerSchedulerSetPeriod(uint8_t moduleIdx, uint16_t periodUs);
|
||||
|
||||
// Get the scheduling period for a given module
|
||||
uint16_t mixerSchedulerGetPeriod(uint8_t moduleIdx);
|
||||
|
||||
// Enable the timer trigger
|
||||
void mixerSchedulerEnableTrigger();
|
||||
|
||||
@@ -65,6 +68,7 @@ void mixerSchedulerISRTrigger();
|
||||
#define mixerSchedulerStop()
|
||||
#define mixerSchedulerResetTimer()
|
||||
#define mixerSchedulerSetPeriod(m,p) ((void)(p))
|
||||
#define mixerSchedulerGetPeriod(m) ((uint16_t)MIXER_SCHEDULER_DEFAULT_PERIOD_US)
|
||||
#define mixerSchedulerClearTrigger()
|
||||
|
||||
#define mixerSchedulerEnableTrigger()
|
||||
|
||||
+33
-233
@@ -22,9 +22,11 @@
|
||||
#include "opentx.h"
|
||||
#include "io/frsky_firmware_update.h"
|
||||
#include "hal/adc_driver.h"
|
||||
#include "aux_serial_driver.h"
|
||||
#include "timers_driver.h"
|
||||
|
||||
#include "tasks.h"
|
||||
#include "tasks/mixer_task.h"
|
||||
|
||||
#if defined(LIBOPENUI)
|
||||
#include "libopenui.h"
|
||||
// #include "shutdown_animation.h"
|
||||
@@ -675,21 +677,23 @@ void doSplash()
|
||||
#if defined(MULTIMODULE)
|
||||
void checkMultiLowPower()
|
||||
{
|
||||
if (isModuleMultimodule(EXTERNAL_MODULE) && g_model.moduleData[EXTERNAL_MODULE].multi.lowPowerMode) {
|
||||
ALERT("MULTI", STR_WARN_MULTI_LOWPOWER, AU_ERROR);
|
||||
return;
|
||||
bool low_power_warning = false;
|
||||
for (uint8_t i = 0; i < MAX_MODULES; i++) {
|
||||
if (isModuleMultimodule(i) &&
|
||||
g_model.moduleData[i].multi.lowPowerMode) {
|
||||
low_power_warning = true;
|
||||
}
|
||||
}
|
||||
#if defined(INTERNAL_MODULE_MULTI)
|
||||
if (isModuleMultimodule(INTERNAL_MODULE) && g_model.moduleData[INTERNAL_MODULE].multi.lowPowerMode) {
|
||||
|
||||
if (low_power_warning) {
|
||||
ALERT("MULTI", STR_WARN_MULTI_LOWPOWER, AU_ERROR);
|
||||
}
|
||||
#endif
|
||||
}
|
||||
#endif
|
||||
|
||||
static void checkRTCBattery()
|
||||
{
|
||||
GET_ADC_IF_MIXER_NOT_RUNNING();
|
||||
if (!mixerTaskRunning()) getADC();
|
||||
if (getRTCBatteryVoltage() < 200) {
|
||||
ALERT(STR_BATTERY, STR_WARN_RTC_BATTERY_LOW, AU_ERROR);
|
||||
}
|
||||
@@ -815,7 +819,7 @@ bool isThrottleWarningAlertNeeded()
|
||||
// no other information available at the moment, and good enough to my option (otherwise too much exceptions...)
|
||||
uint8_t thrchn = ((g_model.thrTraceSrc==0) || (g_model.thrTraceSrc>NUM_POTS+NUM_SLIDERS)) ? THR_STICK : g_model.thrTraceSrc+NUM_STICKS-1;
|
||||
|
||||
GET_ADC_IF_MIXER_NOT_RUNNING();
|
||||
if (!mixerTaskRunning()) getADC();
|
||||
evalInputs(e_perout_mode_notrainer); // let do evalInputs do the job
|
||||
|
||||
int16_t v = calibratedAnalogs[thrchn];
|
||||
@@ -1088,8 +1092,7 @@ uint16_t sessionTimer;
|
||||
uint16_t s_timeCumThr; // THR in 1/16 sec
|
||||
uint16_t s_timeCum16ThrP; // THR% in 1/16 sec
|
||||
|
||||
uint8_t trimsCheckTimer = 0;
|
||||
|
||||
uint8_t trimsCheckTimer = 0;
|
||||
uint8_t trimsDisplayTimer = 0;
|
||||
uint8_t trimsDisplayMask = 0;
|
||||
|
||||
@@ -1129,194 +1132,6 @@ void flightReset(uint8_t check)
|
||||
}
|
||||
}
|
||||
|
||||
#if defined(THRTRACE)
|
||||
uint8_t s_traceBuf[MAXTRACE];
|
||||
uint16_t s_traceWr;
|
||||
uint8_t s_cnt_10s;
|
||||
uint16_t s_cnt_samples_thr_10s;
|
||||
uint16_t s_sum_samples_thr_10s;
|
||||
#endif
|
||||
|
||||
void evalTrims()
|
||||
{
|
||||
uint8_t phase = mixerCurrentFlightMode;
|
||||
for (uint8_t i=0; i<NUM_TRIMS; i++) {
|
||||
// do trim -> throttle trim if applicable
|
||||
int16_t trim = getTrimValue(phase, i);
|
||||
if (trimsCheckTimer > 0) {
|
||||
trim = 0;
|
||||
}
|
||||
|
||||
trims[i] = trim*2;
|
||||
}
|
||||
}
|
||||
|
||||
uint8_t s_mixer_first_run_done = false;
|
||||
|
||||
void doMixerCalculations()
|
||||
{
|
||||
static tmr10ms_t lastTMR = 0;
|
||||
|
||||
tmr10ms_t tmr10ms = get_tmr10ms();
|
||||
|
||||
#if defined(DEBUG_LATENCY_MIXER_RF) || defined(DEBUG_LATENCY_RF_ONLY)
|
||||
static tmr10ms_t lastLatencyToggle = 0;
|
||||
if (tmr10ms - lastLatencyToggle >= 10) {
|
||||
lastLatencyToggle = tmr10ms;
|
||||
toggleLatencySwitch();
|
||||
}
|
||||
#endif
|
||||
|
||||
uint8_t tick10ms = (tmr10ms >= lastTMR ? tmr10ms - lastTMR : 1);
|
||||
// handle tick10ms overrun
|
||||
// correct overflow handling costs a lot of code; happens only each 11 min;
|
||||
// therefore forget the exact calculation and use only 1 instead; good compromise
|
||||
lastTMR = tmr10ms;
|
||||
|
||||
DEBUG_TIMER_START(debugTimerGetAdc);
|
||||
getADC();
|
||||
DEBUG_TIMER_STOP(debugTimerGetAdc);
|
||||
|
||||
DEBUG_TIMER_START(debugTimerGetSwitches);
|
||||
getSwitchesPosition(!s_mixer_first_run_done);
|
||||
DEBUG_TIMER_STOP(debugTimerGetSwitches);
|
||||
|
||||
|
||||
|
||||
DEBUG_TIMER_START(debugTimerEvalMixes);
|
||||
evalMixes(tick10ms);
|
||||
DEBUG_TIMER_STOP(debugTimerEvalMixes);
|
||||
}
|
||||
|
||||
void doMixerPeriodicUpdates()
|
||||
{
|
||||
static tmr10ms_t lastTMR = 0;
|
||||
|
||||
tmr10ms_t tmr10ms = get_tmr10ms();
|
||||
|
||||
uint8_t tick10ms = (tmr10ms >= lastTMR ? tmr10ms - lastTMR : 1);
|
||||
// handle tick10ms overrun
|
||||
// correct overflow handling costs a lot of code; happens only each 11 min;
|
||||
// therefore forget the exact calculation and use only 1 instead; good compromise
|
||||
lastTMR = tmr10ms;
|
||||
|
||||
DEBUG_TIMER_START(debugTimerMixes10ms);
|
||||
if (tick10ms) {
|
||||
/* Throttle trace */
|
||||
int16_t val;
|
||||
|
||||
if (g_model.thrTraceSrc > NUM_POTS+NUM_SLIDERS) {
|
||||
uint8_t ch = g_model.thrTraceSrc-NUM_POTS-NUM_SLIDERS-1;
|
||||
val = channelOutputs[ch];
|
||||
|
||||
LimitData * lim = limitAddress(ch);
|
||||
int16_t gModelMax = LIMIT_MAX_RESX(lim);
|
||||
int16_t gModelMin = LIMIT_MIN_RESX(lim);
|
||||
|
||||
if (lim->revert)
|
||||
val = -val + gModelMax;
|
||||
else
|
||||
val = val - gModelMin;
|
||||
|
||||
#if defined(PPM_LIMITS_SYMETRICAL)
|
||||
if (lim->symetrical) {
|
||||
val -= calc1000toRESX(lim->offset);
|
||||
}
|
||||
#endif
|
||||
|
||||
gModelMax -= gModelMin; // we compare difference between Max and Mix for recaling needed; Max and Min are shifted to 0 by default
|
||||
// usually max is 1024 min is -1024 --> max-min = 2048 full range
|
||||
|
||||
if (gModelMax != 0 && gModelMax != 2048)
|
||||
val = (int32_t) (val << 11) / (gModelMax); // rescaling only needed if Min, Max differs
|
||||
|
||||
if (val < 0)
|
||||
val=0; // prevent val be negative, which would corrupt throttle trace and timers; could occur if safetyswitch is smaller than limits
|
||||
}
|
||||
else {
|
||||
val = RESX + calibratedAnalogs[g_model.thrTraceSrc == 0 ? THR_STICK : g_model.thrTraceSrc+NUM_STICKS-1];
|
||||
}
|
||||
|
||||
val >>= (RESX_SHIFT-6); // calibrate it (resolution increased by factor 4)
|
||||
|
||||
evalTimers(val, tick10ms);
|
||||
|
||||
static uint8_t s_cnt_100ms;
|
||||
static uint8_t s_cnt_1s;
|
||||
static uint8_t s_cnt_samples_thr_1s;
|
||||
static uint16_t s_sum_samples_thr_1s;
|
||||
|
||||
s_cnt_samples_thr_1s++;
|
||||
s_sum_samples_thr_1s+=val;
|
||||
|
||||
if ((s_cnt_100ms += tick10ms) >= 10) { // 0.1sec
|
||||
s_cnt_100ms -= 10;
|
||||
s_cnt_1s += 1;
|
||||
|
||||
logicalSwitchesTimerTick();
|
||||
checkTrainerSignalWarning();
|
||||
|
||||
if (s_cnt_1s >= 10) { // 1sec
|
||||
s_cnt_1s -= 10;
|
||||
sessionTimer += 1;
|
||||
inactivity.counter++;
|
||||
if ((((uint8_t)inactivity.counter) & 0x07) == 0x01 && g_eeGeneral.inactivityTimer && inactivity.counter > ((uint16_t)g_eeGeneral.inactivityTimer * 60))
|
||||
AUDIO_INACTIVITY();
|
||||
|
||||
#if defined(AUDIO)
|
||||
if (mixWarning & 1) if ((sessionTimer&0x03)==0) AUDIO_MIX_WARNING(1);
|
||||
if (mixWarning & 2) if ((sessionTimer&0x03)==1) AUDIO_MIX_WARNING(2);
|
||||
if (mixWarning & 4) if ((sessionTimer&0x03)==2) AUDIO_MIX_WARNING(3);
|
||||
#endif
|
||||
|
||||
val = s_sum_samples_thr_1s / s_cnt_samples_thr_1s;
|
||||
s_timeCum16ThrP += (val>>3); // s_timeCum16ThrP would overrun if we would store throttle value with higher accuracy; therefore stay with 16 steps
|
||||
if (val)
|
||||
s_timeCumThr += 1;
|
||||
s_sum_samples_thr_1s >>= 2; // correct better accuracy now, because trace graph can show this information; in case thrtrace is not active, the compile should remove this
|
||||
|
||||
#if defined(THRTRACE)
|
||||
// throttle trace is done every 10 seconds; Tracebuffer is adjusted to screen size.
|
||||
// in case buffer runs out, it wraps around
|
||||
// resolution for y axis is only 32, therefore no higher value makes sense
|
||||
s_cnt_samples_thr_10s += s_cnt_samples_thr_1s;
|
||||
s_sum_samples_thr_10s += s_sum_samples_thr_1s;
|
||||
|
||||
if (++s_cnt_10s >= 10) { // 10s
|
||||
s_cnt_10s -= 10;
|
||||
val = s_sum_samples_thr_10s / s_cnt_samples_thr_10s;
|
||||
s_sum_samples_thr_10s = 0;
|
||||
s_cnt_samples_thr_10s = 0;
|
||||
s_traceBuf[s_traceWr % MAXTRACE] = val;
|
||||
s_traceWr++;
|
||||
}
|
||||
#endif
|
||||
|
||||
s_cnt_samples_thr_1s = 0;
|
||||
s_sum_samples_thr_1s = 0;
|
||||
}
|
||||
}
|
||||
|
||||
#if defined(PXX) || defined(DSM2)
|
||||
static uint8_t countRangecheck = 0;
|
||||
for (uint8_t i = 0; i < NUM_MODULES; ++i) {
|
||||
if (isModuleBeeping(i)) {
|
||||
if (++countRangecheck >= 250) {
|
||||
countRangecheck = 0;
|
||||
AUDIO_PLAY(AU_SPECIAL_SOUND_CHEEP);
|
||||
}
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
checkTrims();
|
||||
}
|
||||
|
||||
DEBUG_TIMER_STOP(debugTimerMixes10ms);
|
||||
|
||||
s_mixer_first_run_done = true;
|
||||
}
|
||||
|
||||
#if !defined(OPENTX_START_DEFAULT_ARGS)
|
||||
#define OPENTX_START_DEFAULT_ARGS 0
|
||||
#endif
|
||||
@@ -1372,12 +1187,13 @@ void opentxClose(uint8_t shutdown)
|
||||
watchdogSuspend(2000/*20s*/);
|
||||
|
||||
if (shutdown) {
|
||||
pausePulses(); // stop mixer task to disable trims processing while in shutdown
|
||||
pulsesStop();
|
||||
AUDIO_BYE();
|
||||
// TODO needed? telemetryEnd();
|
||||
|
||||
#if defined(LUA)
|
||||
luaClose(&lsScripts);
|
||||
#endif
|
||||
|
||||
#if defined(HAPTIC)
|
||||
hapticOff();
|
||||
#endif
|
||||
@@ -1509,7 +1325,7 @@ void instantTrim()
|
||||
|
||||
void copySticksToOffset(uint8_t ch)
|
||||
{
|
||||
pauseMixerCalculations();
|
||||
mixerTaskStop();
|
||||
int32_t zero = (int32_t)channelOutputs[ch];
|
||||
|
||||
evalFlightModeMixes(e_perout_mode_nosticks+e_perout_mode_notrainer, 0);
|
||||
@@ -1522,7 +1338,8 @@ void copySticksToOffset(uint8_t ch)
|
||||
}
|
||||
zero = (zero*256000 - val*lim) / (1024*256-val);
|
||||
ld->offset = (ld->revert ? -zero : zero);
|
||||
resumeMixerCalculations();
|
||||
|
||||
mixerTaskStart();
|
||||
storageDirty(EE_MODEL);
|
||||
}
|
||||
|
||||
@@ -1530,7 +1347,7 @@ void copyTrimsToOffset(uint8_t ch)
|
||||
{
|
||||
int16_t zero;
|
||||
|
||||
pauseMixerCalculations();
|
||||
mixerTaskStop();
|
||||
|
||||
evalFlightModeMixes(e_perout_mode_noinput, 0); // do output loop - zero input sticks and trims
|
||||
zero = applyLimits(ch, chans[ch]);
|
||||
@@ -1544,7 +1361,7 @@ void copyTrimsToOffset(uint8_t ch)
|
||||
v += (output * 125) / 128;
|
||||
g_model.limitData[ch].offset = limit((int16_t)-1000, (int16_t)v, (int16_t)1000); // make sure the offset doesn't go haywire
|
||||
|
||||
resumeMixerCalculations();
|
||||
mixerTaskStart();
|
||||
storageDirty(EE_MODEL);
|
||||
}
|
||||
|
||||
@@ -1555,7 +1372,7 @@ void copyMinMaxToOutputs(uint8_t ch)
|
||||
int16_t max = ld->max;
|
||||
int16_t center = ld->ppmCenter;
|
||||
|
||||
pauseMixerCalculations();
|
||||
mixerTaskStop();
|
||||
|
||||
for (uint8_t chan = 0; chan < MAX_OUTPUT_CHANNELS; chan++) {
|
||||
ld = limitAddress(chan);
|
||||
@@ -1564,7 +1381,7 @@ void copyMinMaxToOutputs(uint8_t ch)
|
||||
ld->ppmCenter = center;
|
||||
}
|
||||
|
||||
resumeMixerCalculations();
|
||||
mixerTaskStart();
|
||||
storageDirty(EE_MODEL);
|
||||
}
|
||||
|
||||
@@ -1609,7 +1426,7 @@ void moveTrimsToOffsets() // copy state of 3 primary to subtrim
|
||||
{
|
||||
int16_t zeros[MAX_OUTPUT_CHANNELS];
|
||||
|
||||
pauseMixerCalculations();
|
||||
mixerTaskStop();
|
||||
|
||||
evalFlightModeMixes(e_perout_mode_noinput, 0); // do output loop - zero input sticks and trims
|
||||
|
||||
@@ -1642,7 +1459,7 @@ void moveTrimsToOffsets() // copy state of 3 primary to subtrim
|
||||
}
|
||||
}
|
||||
|
||||
resumeMixerCalculations();
|
||||
mixerTaskStart();
|
||||
|
||||
storageDirty(EE_MODEL);
|
||||
AUDIO_WARNING2();
|
||||
@@ -1726,13 +1543,6 @@ void opentxInit()
|
||||
if (bluetooth.flashFirmware(AUTOUPDATE_FILENAME) == nullptr)
|
||||
f_unlink(AUTOUPDATE_FILENAME);
|
||||
}
|
||||
#endif
|
||||
#if defined(HARDWARE_POWER_MANAGEMENT_UNIT)
|
||||
if (information.productFamily == FIRMWARE_FAMILY_POWER_MANAGEMENT_UNIT) {
|
||||
FrskyChipFirmwareUpdate device;
|
||||
if (device.flashFirmware(AUTOUPDATE_FILENAME, false) == nullptr)
|
||||
f_unlink(AUTOUPDATE_FILENAME);
|
||||
}
|
||||
#endif
|
||||
}
|
||||
}
|
||||
@@ -1771,6 +1581,11 @@ void opentxInit()
|
||||
#endif
|
||||
#endif // #if !defined(EEPROM)
|
||||
|
||||
// TODO: move to board on hook (onStorageReady()?)
|
||||
// #if defined(SPORT_UPDATE_PWR_GPIO)
|
||||
// SPORT_UPDATE_POWER_INIT();
|
||||
// #endif
|
||||
|
||||
initSerialPorts();
|
||||
|
||||
currentSpeakerVolume = requiredSpeakerVolume = g_eeGeneral.speakerVolume + VOLUME_LEVEL_DEF;
|
||||
@@ -1783,12 +1598,6 @@ void opentxInit()
|
||||
audioQueue.start();
|
||||
BACKLIGHT_ENABLE();
|
||||
|
||||
|
||||
|
||||
#if defined(SPORT_UPDATE_PWR_GPIO)
|
||||
SPORT_UPDATE_POWER_INIT();
|
||||
#endif
|
||||
|
||||
#if defined(COLORLCD)
|
||||
loadTheme();
|
||||
if (g_eeGeneral.backlightMode == e_backlight_mode_off) {
|
||||
@@ -1823,8 +1632,7 @@ void opentxInit()
|
||||
|
||||
resetBacklightTimeout();
|
||||
|
||||
startPulses();
|
||||
|
||||
pulsesStart();
|
||||
WDG_ENABLE(WDG_DURATION);
|
||||
}
|
||||
|
||||
@@ -1848,15 +1656,6 @@ int main()
|
||||
NVIC_PriorityGroupConfig( NVIC_PriorityGroup_4 );
|
||||
#endif
|
||||
|
||||
TRACE("reusableBuffer: modelSel=%d, moduleSetup=%d, calib=%d, sdManager=%d, hardwareAndSettings=%d, spectrumAnalyser=%d, usb=%d",
|
||||
sizeof(reusableBuffer.modelsel),
|
||||
sizeof(reusableBuffer.moduleSetup),
|
||||
sizeof(reusableBuffer.calib),
|
||||
sizeof(reusableBuffer.sdManager),
|
||||
sizeof(reusableBuffer.hardwareAndSettings),
|
||||
sizeof(reusableBuffer.spectrumAnalyser),
|
||||
sizeof(reusableBuffer.MSC_BOT_Data));
|
||||
|
||||
// G: The WDT remains active after a WDT reset -- at maximum clock speed. So it's
|
||||
// important to disable it before commencing with system initialisation (or
|
||||
// we could put a bunch more WDG_RESET()s in. But I don't like that approach
|
||||
@@ -1866,6 +1665,7 @@ int main()
|
||||
#endif
|
||||
|
||||
boardInit();
|
||||
pulsesInit();
|
||||
|
||||
#if defined(PCBHORUS)
|
||||
if (!IS_FIRMWARE_COMPATIBLE_WITH_BOARD()) {
|
||||
|
||||
+10
-23
@@ -569,12 +569,6 @@ void checkAlarm();
|
||||
void checkAll();
|
||||
|
||||
void getADC();
|
||||
static inline void GET_ADC_IF_MIXER_NOT_RUNNING()
|
||||
{
|
||||
if (s_pulses_paused) {
|
||||
getADC();
|
||||
}
|
||||
}
|
||||
|
||||
#include "sbus.h"
|
||||
|
||||
@@ -583,23 +577,6 @@ void checkBacklight();
|
||||
|
||||
uint16_t isqrt32(uint32_t n);
|
||||
|
||||
#if defined(BOOT)
|
||||
#define pauseMixerCalculations()
|
||||
#define resumeMixerCalculations()
|
||||
#else
|
||||
#include "tasks.h"
|
||||
extern RTOS_MUTEX_HANDLE mixerMutex;
|
||||
inline void pauseMixerCalculations()
|
||||
{
|
||||
RTOS_LOCK_MUTEX(mixerMutex);
|
||||
}
|
||||
|
||||
inline void resumeMixerCalculations()
|
||||
{
|
||||
RTOS_UNLOCK_MUTEX(mixerMutex);
|
||||
}
|
||||
#endif
|
||||
|
||||
void setDefaultOwnerId();
|
||||
void generalDefault();
|
||||
|
||||
@@ -941,8 +918,10 @@ union ReusableBuffer
|
||||
int8_t antennaMode;
|
||||
uint8_t previousType;
|
||||
uint8_t newType;
|
||||
#if defined(PXX2)
|
||||
BindInformation bindInformation;
|
||||
PXX2ModuleSetup pxx2;
|
||||
#endif
|
||||
#if defined(BLUETOOTH)
|
||||
struct {
|
||||
char devices[MAX_BLUETOOTH_DISTANT_ADDR][LEN_BLUETOOTH_ADDR+1];
|
||||
@@ -973,8 +952,10 @@ union ReusableBuffer
|
||||
uint16_t offset;
|
||||
uint16_t count;
|
||||
char originalName[SD_SCREEN_FILE_LENGTH+1];
|
||||
#if defined(PXX2)
|
||||
OtaUpdateInformation otaUpdateInformation;
|
||||
char otaReceiverVersion[sizeof(TR_CURRENT_VERSION) + 12];
|
||||
#endif
|
||||
} sdManager;
|
||||
#endif
|
||||
|
||||
@@ -983,10 +964,14 @@ union ReusableBuffer
|
||||
char id[27];
|
||||
} version;
|
||||
|
||||
#if defined(PXX2)
|
||||
PXX2HardwareAndSettings hardwareAndSettings; // radio_version
|
||||
#endif
|
||||
|
||||
struct {
|
||||
#if defined(PXX2)
|
||||
ModuleInformation modules[NUM_MODULES];
|
||||
#endif
|
||||
char msg[64];
|
||||
#if !defined(COLORLCD)
|
||||
uint8_t linesCount;
|
||||
@@ -1060,7 +1045,9 @@ union ReusableBuffer
|
||||
} modelFailsafe;
|
||||
|
||||
struct {
|
||||
#if defined(PXX2)
|
||||
ModuleInformation internalModule;
|
||||
#endif
|
||||
} viewMain;
|
||||
|
||||
// Data for the USB mass storage driver. If USB mass storage runs no menu is not allowed to be displayed
|
||||
|
||||
+26
-38
@@ -23,21 +23,18 @@
|
||||
#include "afhds2.h"
|
||||
#include "flysky.h"
|
||||
|
||||
#include "intmodule_serial_driver.h"
|
||||
#include "mixer_scheduler.h"
|
||||
#include "hal/module_port.h"
|
||||
|
||||
#include "pulses/pulses.h"
|
||||
#include "telemetry/telemetry.h"
|
||||
|
||||
|
||||
|
||||
|
||||
const etx_serial_init afhds2SerialInitParams = {
|
||||
.baudrate = INTMODULE_USART_AFHDS2_BAUDRATE,
|
||||
.parity = ETX_Parity_None,
|
||||
.stop_bits = ETX_StopBits_One,
|
||||
.word_length = ETX_WordLength_8,
|
||||
.rx_enable = true,
|
||||
.encoding = ETX_Encoding_8N1,
|
||||
.direction = ETX_Dir_TX_RX,
|
||||
.polarity = ETX_Pol_Normal,
|
||||
};
|
||||
|
||||
static void* afhds2Init(uint8_t module)
|
||||
@@ -46,50 +43,43 @@ static void* afhds2Init(uint8_t module)
|
||||
|
||||
// serial port setup
|
||||
resetPulsesAFHDS2();
|
||||
void* ctx = IntmoduleSerialDriver.init(&afhds2SerialInitParams);
|
||||
auto mod_st = modulePortInitSerial(module, ETX_MOD_PORT_UART,
|
||||
&afhds2SerialInitParams);
|
||||
|
||||
// mixer setup
|
||||
mixerSchedulerSetPeriod(INTERNAL_MODULE, AFHDS2_PERIOD);
|
||||
INTERNAL_MODULE_ON();
|
||||
mixerSchedulerSetPeriod(module, AFHDS2_PERIOD);
|
||||
|
||||
return ctx;
|
||||
return (void*)mod_st;
|
||||
}
|
||||
|
||||
static void afhds2DeInit(void* context)
|
||||
static void afhds2DeInit(void* ctx)
|
||||
{
|
||||
INTERNAL_MODULE_OFF();
|
||||
IntmoduleSerialDriver.deinit(context);
|
||||
|
||||
// mixer setup
|
||||
mixerSchedulerSetPeriod(INTERNAL_MODULE, 0);
|
||||
auto mod_st = (etx_module_state_t*)ctx;
|
||||
modulePortDeInit(mod_st);
|
||||
}
|
||||
|
||||
static void afhds2SetupPulses(void* context, int16_t* channels, uint8_t nChannels)
|
||||
static void afhds2SendPulses(void* ctx, uint8_t* buffer, int16_t* channels, uint8_t nChannels)
|
||||
{
|
||||
(void)context;
|
||||
|
||||
// TODO:
|
||||
(void)channels;
|
||||
(void)nChannels;
|
||||
|
||||
ModuleSyncStatus& status = getModuleSyncStatus(INTERNAL_MODULE);
|
||||
mixerSchedulerSetPeriod(INTERNAL_MODULE, status.isValid()
|
||||
? status.getAdjustedRefreshRate()
|
||||
: AFHDS2_PERIOD);
|
||||
auto mod_st = (etx_module_state_t*)ctx;
|
||||
auto module = modulePortGetModule(mod_st);
|
||||
|
||||
ModuleSyncStatus& status = getModuleSyncStatus(module);
|
||||
mixerSchedulerSetPeriod(module, status.isValid()
|
||||
? status.getAdjustedRefreshRate()
|
||||
: AFHDS2_PERIOD);
|
||||
status.invalidate();
|
||||
setupPulsesAFHDS2();
|
||||
}
|
||||
|
||||
static void afhds2SendPulses(void* context)
|
||||
{
|
||||
uint8_t* data = (uint8_t*)intmodulePulsesData.flysky.pulses;
|
||||
uint16_t size = intmodulePulsesData.flysky.ptr - data;
|
||||
IntmoduleSerialDriver.sendBuffer(context, data, size);
|
||||
}
|
||||
auto p_data = buffer;
|
||||
setupPulsesAFHDS2(p_data);
|
||||
|
||||
static int afhds2GetByte(void* context, uint8_t* data)
|
||||
{
|
||||
return IntmoduleSerialDriver.getByte(context, data);
|
||||
auto drv = modulePortGetSerialDrv(mod_st->tx);
|
||||
auto drv_ctx = modulePortGetCtx(mod_st->tx);
|
||||
|
||||
drv->sendBuffer(drv_ctx, buffer, p_data - buffer);
|
||||
}
|
||||
|
||||
static void afhds2ProcessData(void*, uint8_t data, uint8_t* buffer, uint8_t* len)
|
||||
@@ -97,12 +87,10 @@ static void afhds2ProcessData(void*, uint8_t data, uint8_t* buffer, uint8_t* len
|
||||
processInternalFlySkyTelemetryData(data, buffer, len);
|
||||
}
|
||||
|
||||
const etx_module_driver_t Afhds2InternalDriver = {
|
||||
const etx_proto_driver_t Afhds2InternalDriver = {
|
||||
.protocol = PROTOCOL_CHANNELS_AFHDS2A,
|
||||
.init = afhds2Init,
|
||||
.deinit = afhds2DeInit,
|
||||
.setupPulses = afhds2SetupPulses,
|
||||
.sendPulses = afhds2SendPulses,
|
||||
.getByte = afhds2GetByte,
|
||||
.processData = afhds2ProcessData,
|
||||
};
|
||||
|
||||
@@ -32,18 +32,7 @@
|
||||
|
||||
#define AFHDS2_PERIOD (2 * 1000) /* us */
|
||||
|
||||
struct FlySkySerialPulsesData {
|
||||
uint8_t pulses[64];
|
||||
uint8_t * ptr;
|
||||
uint8_t frame_index;
|
||||
uint8_t crc;
|
||||
uint8_t state;
|
||||
uint8_t timeout;
|
||||
uint8_t esc_state;
|
||||
} __attribute__((__packed__));
|
||||
|
||||
|
||||
extern const etx_serial_init afhds2SerialInitParams;
|
||||
extern const etx_module_driver_t Afhds2InternalDriver;
|
||||
extern const etx_proto_driver_t Afhds2InternalDriver;
|
||||
|
||||
#endif /* PULSES_AFHDS2_H_ */
|
||||
|
||||
+46
-102
@@ -31,16 +31,7 @@
|
||||
#include "telemetry/telemetry.h"
|
||||
#include "mixer_scheduler.h"
|
||||
#include "hal/module_driver.h"
|
||||
|
||||
#if defined(INTMODULE_USART)
|
||||
#include "intmodule_serial_driver.h"
|
||||
#endif
|
||||
|
||||
#if defined(AFHDS3_EXT_UART)
|
||||
#include "extmodule_serial_driver.h"
|
||||
#else
|
||||
#include "extmodule_driver.h"
|
||||
#endif
|
||||
#include "hal/module_port.h"
|
||||
|
||||
#define FAILSAFE_HOLD 1
|
||||
#define FAILSAFE_CUSTOM 2
|
||||
@@ -192,8 +183,7 @@ class ProtoState
|
||||
* @param moduleIndex index of module one of INTERNAL_MODULE, EXTERNAL_MODULE
|
||||
* @param resetFrameCount flag if current frame count should be reseted
|
||||
*/
|
||||
void init(uint8_t moduleIndex, void* buffer, const etx_serial_driver_t* drv);
|
||||
void deinit() { trsp.deinit(); }
|
||||
void init(uint8_t moduleIndex, void* buffer, etx_module_state_t* mod_st);
|
||||
|
||||
/**
|
||||
* Fills DMA buffers with frame to be send depending on actual state
|
||||
@@ -205,11 +195,6 @@ class ProtoState
|
||||
*/
|
||||
void sendFrame() { trsp.sendBuffer(); }
|
||||
|
||||
/**
|
||||
* Fetch telemetry byte
|
||||
*/
|
||||
int getTelemetryByte(uint8_t* data) { return trsp.getTelemetryByte(data); }
|
||||
|
||||
/**
|
||||
* Gets actual module status into provided buffer
|
||||
* @param statusText target buffer for status
|
||||
@@ -320,7 +305,7 @@ static const COMMAND periodicRequestCommands[] =
|
||||
};
|
||||
|
||||
//Static collection of afhds3 object instances by module
|
||||
static ProtoState protoState[NUM_MODULES];
|
||||
static ProtoState protoState[MAX_MODULES];
|
||||
|
||||
void getStatusString(uint8_t module, char* buffer)
|
||||
{
|
||||
@@ -330,7 +315,8 @@ void getStatusString(uint8_t module, char* buffer)
|
||||
//friends function that can access telemetry parsing method
|
||||
void processTelemetryData(void* ctx, uint8_t data, uint8_t* buffer, uint8_t* len)
|
||||
{
|
||||
auto p_state = (ProtoState*)ctx;
|
||||
auto mod_st = (etx_module_state_t*)ctx;
|
||||
auto p_state = (ProtoState*)mod_st->user_data;
|
||||
p_state->processTelemetryData(data, buffer, len);
|
||||
}
|
||||
|
||||
@@ -482,16 +468,10 @@ void ProtoState::setupFrame()
|
||||
}
|
||||
}
|
||||
|
||||
void ProtoState::init(uint8_t moduleIndex, void* buffer, const etx_serial_driver_t* drv)
|
||||
void ProtoState::init(uint8_t moduleIndex, void* buffer, etx_module_state_t* mod_st)
|
||||
{
|
||||
module_index = moduleIndex;
|
||||
|
||||
// TODO: move bit/byte transport to serial driver
|
||||
if (drv) {
|
||||
trsp.init(ByteTransport::Serial, buffer, drv);
|
||||
} else {
|
||||
trsp.init(ByteTransport::Pulses, buffer, nullptr);
|
||||
}
|
||||
trsp.init(buffer, mod_st);
|
||||
|
||||
//clear local vars because it is member of union
|
||||
moduleData = &g_model.moduleData[module_index];
|
||||
@@ -861,106 +841,70 @@ void applyModelConfig(uint8_t module)
|
||||
p_state->applyConfigFromModel();
|
||||
}
|
||||
|
||||
static void* initExternal(uint8_t module)
|
||||
static const etx_serial_init _uartParams = {
|
||||
.baudrate = 0, //AFHDS3_UART_BAUDRATE,
|
||||
.encoding = ETX_Encoding_8N1,
|
||||
.direction = ETX_Dir_TX_RX,
|
||||
.polarity = ETX_Pol_Normal,
|
||||
};
|
||||
|
||||
static void* initModule(uint8_t module)
|
||||
{
|
||||
#if defined(AFHDS3_EXT_UART)
|
||||
auto drv = extmoduleGetSerialPort();
|
||||
if (!drv) return nullptr;
|
||||
etx_module_state_t* mod_st = nullptr;
|
||||
etx_serial_init params(_uartParams);
|
||||
uint16_t period = AFHDS3_UART_COMMAND_TIMEOUT * 1000;
|
||||
|
||||
uint16_t period = AFHDS3_UART_COMMAND_TIMEOUT * 1000 /* us */;
|
||||
#else
|
||||
const etx_serial_driver_t* drv = nullptr;
|
||||
uint16_t period = AFHDS3_SOFTSERIAL_COMMAND_TIMEOUT * 1000 /* us */;
|
||||
params.baudrate = AFHDS3_UART_BAUDRATE;
|
||||
params.polarity =
|
||||
module == INTERNAL_MODULE ? ETX_Pol_Normal : ETX_Pol_Inverted;
|
||||
mod_st = modulePortInitSerial(module, ETX_MOD_PORT_UART, ¶ms);
|
||||
|
||||
telemetryPortInvertedInit(AFHDS3_SOFTSERIAL_BAUDRATE);
|
||||
telemetryPortSetDirectionInput();
|
||||
#endif
|
||||
if (module == EXTERNAL_MODULE && !mod_st) {
|
||||
// soft-serial fallback
|
||||
params.baudrate = AFHDS3_SOFTSERIAL_BAUDRATE;
|
||||
params.direction = ETX_Dir_RX;
|
||||
period = AFHDS3_SOFTSERIAL_COMMAND_TIMEOUT * 1000 /* us */;
|
||||
mod_st = modulePortInitSerial(module, ETX_MOD_PORT_SOFT_INV, ¶ms);
|
||||
// TODO: telemetry RX ???
|
||||
}
|
||||
|
||||
if (!mod_st) return nullptr;
|
||||
|
||||
auto p_state = &protoState[module];
|
||||
p_state->init(module, &extmodulePulsesData.afhds3, drv);
|
||||
p_state->init(module, pulsesGetModuleBuffer(module), mod_st);
|
||||
mod_st->user_data = (void*)p_state;
|
||||
|
||||
mixerSchedulerSetPeriod(module, period);
|
||||
EXTERNAL_MODULE_ON();
|
||||
|
||||
return p_state;
|
||||
return mod_st;
|
||||
}
|
||||
|
||||
static void deinitExternal(void* context)
|
||||
static void deinitModule(void* ctx)
|
||||
{
|
||||
EXTERNAL_MODULE_OFF();
|
||||
mixerSchedulerSetPeriod(EXTERNAL_MODULE, 0);
|
||||
|
||||
auto p_state = (ProtoState*)context;
|
||||
p_state->deinit();
|
||||
auto mod_st = (etx_module_state_t*)ctx;
|
||||
modulePortDeInit(mod_st);
|
||||
}
|
||||
|
||||
static void sendPulses(void* context)
|
||||
{
|
||||
auto p_state = (ProtoState*)context;
|
||||
p_state->sendFrame();
|
||||
}
|
||||
|
||||
static void setupPulses(void* context, int16_t* channels, uint8_t nChannels)
|
||||
static void sendPulses(void* ctx, uint8_t* buffer, int16_t* channels, uint8_t nChannels)
|
||||
{
|
||||
(void)buffer;
|
||||
(void)channels;
|
||||
(void)nChannels;
|
||||
|
||||
auto p_state = (ProtoState*)context;
|
||||
auto mod_st = (etx_module_state_t*)ctx;
|
||||
auto p_state = (ProtoState*)mod_st->user_data;
|
||||
p_state->setupFrame();
|
||||
p_state->sendFrame();
|
||||
}
|
||||
|
||||
etx_module_driver_t externalDriver = {
|
||||
etx_proto_driver_t ProtoDriver = {
|
||||
.protocol = PROTOCOL_CHANNELS_AFHDS3,
|
||||
.init = initExternal,
|
||||
.deinit = deinitExternal,
|
||||
.setupPulses = setupPulses,
|
||||
.init = initModule,
|
||||
.deinit = deinitModule,
|
||||
.sendPulses = sendPulses,
|
||||
.getByte = nullptr, // TODO
|
||||
.processData = processTelemetryData,
|
||||
};
|
||||
|
||||
#if defined(INTERNAL_MODULE_AFHDS3)
|
||||
|
||||
static void* initInternal(uint8_t module)
|
||||
{
|
||||
auto p_state = &protoState[module];
|
||||
p_state->init(module, &intmodulePulsesData, &IntmoduleSerialDriver);
|
||||
|
||||
telemetryProtocol = PROTOCOL_TELEMETRY_AFHDS3;
|
||||
mixerSchedulerSetPeriod(module, AFHDS3_UART_COMMAND_TIMEOUT * 1000 /* us */);
|
||||
INTERNAL_MODULE_ON();
|
||||
|
||||
return p_state;
|
||||
}
|
||||
|
||||
static void deinitInternal(void* context)
|
||||
{
|
||||
INTERNAL_MODULE_OFF();
|
||||
telemetryProtocol = 0xFF;
|
||||
mixerSchedulerSetPeriod(INTERNAL_MODULE, 0);
|
||||
|
||||
auto p_state = (ProtoState*)context;
|
||||
p_state->deinit();
|
||||
}
|
||||
|
||||
static int getByteInternal(void* context, uint8_t* data)
|
||||
{
|
||||
auto p_state = (ProtoState*)context;
|
||||
return p_state->getTelemetryByte(data);
|
||||
}
|
||||
|
||||
etx_module_driver_t internalDriver = {
|
||||
.protocol = PROTOCOL_CHANNELS_AFHDS3,
|
||||
.init = initInternal,
|
||||
.deinit = deinitInternal,
|
||||
.setupPulses = setupPulses,
|
||||
.sendPulses = sendPulses,
|
||||
.getByte = getByteInternal,
|
||||
.processData = processTelemetryData,
|
||||
};
|
||||
|
||||
#endif
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -70,11 +70,7 @@ typedef PulsesData ExtmoduleData;
|
||||
typedef SerialData IntmoduleData;
|
||||
#endif
|
||||
|
||||
extern etx_module_driver_t externalDriver;
|
||||
|
||||
#if defined(INTERNAL_MODULE_AFHDS3)
|
||||
extern etx_module_driver_t internalDriver;
|
||||
#endif
|
||||
extern etx_proto_driver_t ProtoDriver;
|
||||
|
||||
void getStatusString(uint8_t module, char* buffer);
|
||||
void processTelemetryData(uint8_t data, uint8_t module);
|
||||
|
||||
@@ -23,8 +23,9 @@
|
||||
#include "opentx_helpers.h"
|
||||
#include "debug.h"
|
||||
|
||||
#include "extmodule_driver.h"
|
||||
#include "board.h"
|
||||
#include "dataconstants.h"
|
||||
#include "mixer_scheduler.h"
|
||||
|
||||
// timer is 2 MHz
|
||||
#if defined(AFHDS3_SLOW)
|
||||
@@ -60,110 +61,16 @@ enum DeviceAddress
|
||||
//Address used in transmitted frames - it constrains of target address and source address
|
||||
const uint8_t FrameAddress = DeviceAddress::TRANSMITTER | (DeviceAddress::MODULE << 4);
|
||||
|
||||
static void _serial_reset(void* buffer)
|
||||
void FrameTransport::init(void* buffer)
|
||||
{
|
||||
auto data = (SerialData*)buffer;
|
||||
data->ptr = data->pulses;
|
||||
}
|
||||
|
||||
static void _serial_sendByte(void* buffer, uint8_t b)
|
||||
{
|
||||
auto data = (SerialData*)buffer;
|
||||
*(data->ptr++) = b;
|
||||
}
|
||||
|
||||
static void _serial_flush(void*) {}
|
||||
|
||||
uint32_t _serial_getSize(void* buffer)
|
||||
{
|
||||
auto data = (SerialData*)buffer;
|
||||
return data->ptr - data->pulses;
|
||||
}
|
||||
|
||||
static void _pulses_reset(void* buffer)
|
||||
{
|
||||
auto data = (PulsesData*)buffer;
|
||||
data->ptr = data->pulses;
|
||||
}
|
||||
|
||||
static inline void _pulses_send_level(PulsesData* data, uint16_t v)
|
||||
{
|
||||
uint32_t size = data->ptr - data->pulses;
|
||||
if (size >= AFHDS_MAX_PULSES_TRANSITIONS) return;
|
||||
*(data->ptr)++ = v;
|
||||
}
|
||||
|
||||
static void _pulses_sendByte(void* buffer, uint8_t b)
|
||||
{
|
||||
auto data = (PulsesData*)buffer;
|
||||
uint32_t size = data->ptr - data->pulses;
|
||||
if (size >= AFHDS_MAX_PULSES_TRANSITIONS) return;
|
||||
|
||||
// use 8n1
|
||||
// parity: If the parity is enabled, then the MSB bit of the data to be
|
||||
// transmitted is changed by the parity bit start is always 0
|
||||
bool level = 0;
|
||||
uint16_t length = BITLEN_AFHDS; // start bit
|
||||
for (uint8_t i = 0; i <= 8; i++) { // 8 data bits + Stop=1
|
||||
bool next_level = b & 1;
|
||||
if (level == next_level) {
|
||||
length += BITLEN_AFHDS;
|
||||
} else {
|
||||
_pulses_send_level(data, length);
|
||||
length = BITLEN_AFHDS;
|
||||
level = next_level;
|
||||
}
|
||||
b = (b >> 1) | 0x80; // shift left to get next bit, fill msb with stop
|
||||
// bit - needed just once
|
||||
}
|
||||
_pulses_send_level(data, length); // last bit (stop)
|
||||
}
|
||||
|
||||
static void _pulses_flush(void* buffer)
|
||||
{
|
||||
// TODO: use less, as the new driver can handle it
|
||||
auto data = (PulsesData*)buffer;
|
||||
*data->ptr = 60000;
|
||||
}
|
||||
|
||||
uint32_t _pulses_getSize(void* buffer)
|
||||
{
|
||||
auto data = (PulsesData*)buffer;
|
||||
return data->ptr - data->pulses;
|
||||
}
|
||||
|
||||
void ByteTransport::init(Type t)
|
||||
{
|
||||
if (t == Serial) {
|
||||
reset = _serial_reset;
|
||||
sendByte = _serial_sendByte;
|
||||
flush = _serial_flush;
|
||||
getSize = _serial_getSize;
|
||||
} else if (t == Pulses) {
|
||||
reset = _pulses_reset;
|
||||
sendByte = _pulses_sendByte;
|
||||
flush = _pulses_flush;
|
||||
getSize = _pulses_getSize;
|
||||
} else {
|
||||
reset = nullptr;
|
||||
sendByte = nullptr;
|
||||
flush = nullptr;
|
||||
getSize = nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
void FrameTransport::init(ByteTransport::Type t, void* buffer)
|
||||
{
|
||||
trsp.init(t);
|
||||
trsp_buffer = buffer;
|
||||
|
||||
trsp_buffer = (uint8_t*)buffer;
|
||||
clear();
|
||||
}
|
||||
|
||||
void FrameTransport::clear()
|
||||
{
|
||||
// reset send buffer
|
||||
trsp.reset(trsp_buffer);
|
||||
data_ptr = trsp_buffer;
|
||||
|
||||
// reset parser
|
||||
esc_state = 0;
|
||||
@@ -171,7 +78,7 @@ void FrameTransport::clear()
|
||||
|
||||
void FrameTransport::sendByte(uint8_t b)
|
||||
{
|
||||
trsp.sendByte(trsp_buffer, b);
|
||||
*(data_ptr++) = b;
|
||||
}
|
||||
|
||||
void FrameTransport::putBytes(uint8_t* data, int length)
|
||||
@@ -197,7 +104,8 @@ void FrameTransport::putFrame(COMMAND command, FRAME_TYPE frameType, uint8_t* da
|
||||
uint8_t dataLength, uint8_t frameIndex)
|
||||
{
|
||||
//header
|
||||
trsp.reset(trsp_buffer);
|
||||
data_ptr = trsp_buffer;
|
||||
|
||||
crc = 0;
|
||||
sendByte(START);
|
||||
|
||||
@@ -213,7 +121,11 @@ void FrameTransport::putFrame(COMMAND command, FRAME_TYPE frameType, uint8_t* da
|
||||
uint8_t crcValue = crc ^ 0xff;
|
||||
putBytes(&crcValue, 1);
|
||||
sendByte(END);
|
||||
trsp.flush(trsp_buffer);
|
||||
}
|
||||
|
||||
uint32_t FrameTransport::getFrameSize()
|
||||
{
|
||||
return data_ptr - trsp_buffer;
|
||||
}
|
||||
|
||||
static bool _checkCRC(const uint8_t* data, uint8_t size)
|
||||
@@ -311,35 +223,10 @@ void CommandFifo::enqueue(COMMAND command, FRAME_TYPE frameType, bool useData,
|
||||
}
|
||||
}
|
||||
|
||||
static const etx_serial_init _uartParams = {
|
||||
.baudrate = AFHDS3_UART_BAUDRATE,
|
||||
.parity = ETX_Parity_None,
|
||||
.stop_bits = ETX_StopBits_One,
|
||||
.word_length = ETX_WordLength_8,
|
||||
.rx_enable = true,
|
||||
};
|
||||
|
||||
void Transport::init(ByteTransport::Type t, void* buffer, const etx_serial_driver_t* drv)
|
||||
void Transport::init(void* buffer, etx_module_state_t* mod_st)
|
||||
{
|
||||
trsp.init(t, buffer);
|
||||
|
||||
if (drv) {
|
||||
uart_drv = drv;
|
||||
uart_ctx = drv->init(&_uartParams);
|
||||
} else {
|
||||
uart_drv = nullptr;
|
||||
uart_ctx = nullptr;
|
||||
extmoduleSerialStart();
|
||||
}
|
||||
}
|
||||
|
||||
void Transport::deinit()
|
||||
{
|
||||
if (uart_drv) {
|
||||
uart_drv->deinit(uart_ctx);
|
||||
} else {
|
||||
extmoduleStop();
|
||||
}
|
||||
trsp.init(buffer);
|
||||
this->mod_st = mod_st;
|
||||
}
|
||||
|
||||
void Transport::clear()
|
||||
@@ -385,11 +272,9 @@ void Transport::enqueue(COMMAND command, FRAME_TYPE frameType, bool useData,
|
||||
|
||||
void Transport::sendBuffer()
|
||||
{
|
||||
if (uart_drv) {
|
||||
uart_drv->sendBuffer(uart_ctx, (uint8_t*)trsp.trsp_buffer, trsp.getFrameSize());
|
||||
} else {
|
||||
extmoduleSendNextFrameSoftSerial((uint8_t*)trsp.trsp_buffer, trsp.getFrameSize());
|
||||
}
|
||||
auto drv = modulePortGetSerialDrv(mod_st->tx);
|
||||
auto ctx = modulePortGetCtx(mod_st->tx);
|
||||
drv->sendBuffer(ctx, (uint8_t*)trsp.trsp_buffer, trsp.getFrameSize());
|
||||
}
|
||||
|
||||
bool Transport::processQueue()
|
||||
@@ -474,15 +359,6 @@ bool Transport::handleReply(uint8_t* buffer, uint8_t len)
|
||||
return false;
|
||||
}
|
||||
|
||||
int Transport::getTelemetryByte(uint8_t* data)
|
||||
{
|
||||
if (uart_drv) {
|
||||
return uart_drv->getByte(uart_ctx, data);
|
||||
} else {
|
||||
return sportGetByte(data);
|
||||
}
|
||||
}
|
||||
|
||||
bool Transport::processTelemetryData(uint8_t byte, uint8_t* rxBuffer,
|
||||
uint8_t& rxBufferCount, uint8_t maxSize)
|
||||
{
|
||||
|
||||
@@ -24,22 +24,11 @@
|
||||
#include "afhds3_module.h"
|
||||
#include "definitions.h"
|
||||
|
||||
#include "hal/serial_driver.h"
|
||||
#include "hal/module_port.h"
|
||||
|
||||
namespace afhds3
|
||||
{
|
||||
|
||||
struct ByteTransport {
|
||||
enum Type { Serial, Pulses };
|
||||
|
||||
void (*reset)(void* buffer);
|
||||
void (*sendByte)(void* buffer, uint8_t b);
|
||||
void (*flush)(void* buffer);
|
||||
uint32_t (*getSize)(void* buffer);
|
||||
|
||||
void init(Type t);
|
||||
};
|
||||
|
||||
enum FRAME_TYPE: uint8_t
|
||||
{
|
||||
REQUEST_GET_DATA = 0x01, //Get data response: ACK + DATA
|
||||
@@ -130,14 +119,14 @@ struct CommandFifo {
|
||||
|
||||
struct FrameTransport
|
||||
{
|
||||
ByteTransport trsp;
|
||||
void* trsp_buffer;
|
||||
uint8_t* trsp_buffer;
|
||||
uint8_t* data_ptr;
|
||||
|
||||
uint8_t crc;
|
||||
// uint8_t timeout;
|
||||
uint8_t esc_state;
|
||||
|
||||
void init(ByteTransport::Type t, void* buffer);
|
||||
void init(void* buffer);
|
||||
void clear();
|
||||
|
||||
void sendByte(uint8_t b);
|
||||
@@ -146,7 +135,7 @@ struct FrameTransport
|
||||
void putFrame(COMMAND command, FRAME_TYPE frameType, uint8_t* data,
|
||||
uint8_t dataLength, uint8_t frameIndex);
|
||||
|
||||
uint32_t getFrameSize() { return trsp.getSize(trsp_buffer); }
|
||||
uint32_t getFrameSize();
|
||||
|
||||
bool processTelemetryData(uint8_t byte, uint8_t* rxBuffer,
|
||||
uint8_t& rxBufferCount, uint8_t maxSize);
|
||||
@@ -161,8 +150,7 @@ class Transport
|
||||
IDLE
|
||||
};
|
||||
|
||||
const etx_serial_driver_t* uart_drv;
|
||||
void* uart_ctx;
|
||||
etx_module_state_t* mod_st;
|
||||
|
||||
FrameTransport trsp;
|
||||
CommandFifo fifo;
|
||||
@@ -187,8 +175,7 @@ class Transport
|
||||
bool handleReply(uint8_t* buffer, uint8_t len);
|
||||
|
||||
public:
|
||||
void init(ByteTransport::Type t, void* buffer, const etx_serial_driver_t* drv);
|
||||
void deinit();
|
||||
void init(void* buffer, etx_module_state_t* mod_st);
|
||||
|
||||
void clear();
|
||||
|
||||
@@ -213,8 +200,6 @@ class Transport
|
||||
*/
|
||||
bool processQueue();
|
||||
|
||||
int getTelemetryByte(uint8_t* data);
|
||||
|
||||
bool processTelemetryData(uint8_t byte, uint8_t* rxBuffer,
|
||||
uint8_t& rxBufferCount, uint8_t maxSize);
|
||||
};
|
||||
|
||||
+57
-132
@@ -22,10 +22,7 @@
|
||||
#include "opentx.h"
|
||||
#include "mixer_scheduler.h"
|
||||
#include "hal/module_driver.h"
|
||||
|
||||
#if defined(INTMODULE_USART)
|
||||
#include "intmodule_serial_driver.h"
|
||||
#endif
|
||||
#include "hal/module_port.h"
|
||||
|
||||
#include "crossfire.h"
|
||||
#include "telemetry/crossfire.h"
|
||||
@@ -79,26 +76,25 @@ uint8_t createCrossfireChannelsFrame(uint8_t * frame, int16_t * pulses)
|
||||
return buf - frame;
|
||||
}
|
||||
|
||||
static void setupPulsesCrossfire(uint8_t idx, CrossfirePulsesData* p_data,
|
||||
static void setupPulsesCrossfire(uint8_t module, uint8_t*& p_buf,
|
||||
uint8_t endpoint, int16_t* channels,
|
||||
uint8_t nChannels)
|
||||
{
|
||||
#if defined(LUA)
|
||||
if (outputTelemetryBuffer.destination == endpoint) {
|
||||
memcpy(p_data->pulses, outputTelemetryBuffer.data,
|
||||
outputTelemetryBuffer.size);
|
||||
p_data->length = outputTelemetryBuffer.size;
|
||||
auto len = outputTelemetryBuffer.size;
|
||||
memcpy(p_buf, outputTelemetryBuffer.data, len);
|
||||
outputTelemetryBuffer.reset();
|
||||
p_buf += len;
|
||||
} else
|
||||
#endif
|
||||
{
|
||||
if (moduleState[idx].counter == CRSF_FRAME_MODELID) {
|
||||
p_data->length = createCrossfireModelIDFrame(idx, p_data->pulses);
|
||||
moduleState[idx].counter = CRSF_FRAME_MODELID_SENT;
|
||||
if (moduleState[module].counter == CRSF_FRAME_MODELID) {
|
||||
p_buf += createCrossfireModelIDFrame(module, p_buf);
|
||||
moduleState[module].counter = CRSF_FRAME_MODELID_SENT;
|
||||
} else {
|
||||
p_data->length = createCrossfireChannelsFrame(
|
||||
p_data->pulses,
|
||||
channels /*TODO: nChannels*/);
|
||||
/* TODO: nChannels */
|
||||
p_buf += createCrossfireChannelsFrame(p_buf, channels);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -109,7 +105,7 @@ static void crossfireSetupMixerScheduler(uint8_t module)
|
||||
if (status.isValid()) {
|
||||
mixerSchedulerSetPeriod(module, status.getAdjustedRefreshRate());
|
||||
} else {
|
||||
mixerSchedulerSetPeriod(module, CROSSFIRE_PERIOD);
|
||||
mixerSchedulerSetPeriod(module, CROSSFIRE_PERIOD(module));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -120,59 +116,22 @@ static bool _checkFrameCRC(uint8_t* rxBuffer)
|
||||
return (crc == rxBuffer[len+1]);
|
||||
}
|
||||
|
||||
struct CrossfireState {
|
||||
uint8_t module;
|
||||
CrossfirePulsesData* data;
|
||||
const etx_serial_driver_t* uart_drv;
|
||||
void* uart_ctx;
|
||||
uint8_t endpoint;
|
||||
|
||||
void init(uint8_t _module, CrossfirePulsesData* _data,
|
||||
const etx_serial_driver_t* _drv, void* _ctx, uint8_t _endpoint)
|
||||
{
|
||||
module = _module;
|
||||
data = _data;
|
||||
uart_drv = _drv;
|
||||
uart_ctx = _ctx;
|
||||
endpoint = _endpoint;
|
||||
}
|
||||
|
||||
void deinit() { uart_drv->deinit(uart_ctx); }
|
||||
};
|
||||
|
||||
static CrossfireState crossfireState[NUM_MODULES];
|
||||
|
||||
static void crossfireSetupPulses(void* context, int16_t* channels, uint8_t nChannels)
|
||||
static void crossfireSendPulses(void* ctx, uint8_t* buffer, int16_t* channels, uint8_t nChannels)
|
||||
{
|
||||
auto state = (CrossfireState*)context;
|
||||
crossfireSetupMixerScheduler(state->module);
|
||||
setupPulsesCrossfire(state->module, state->data, state->endpoint, channels, nChannels);
|
||||
}
|
||||
auto mod_st = (etx_module_state_t*)ctx;
|
||||
auto module = modulePortGetModule(mod_st);
|
||||
crossfireSetupMixerScheduler(module);
|
||||
|
||||
static void crossfireSendPulses(void* context)
|
||||
{
|
||||
auto state = (CrossfireState*)context;
|
||||
auto data = state->data;
|
||||
uint8_t endpoint = 0;
|
||||
#if defined(HARDWARE_EXTERNAL_MODULE)
|
||||
if (module == EXTERNAL_MODULE) endpoint = TELEMETRY_ENDPOINT_SPORT;
|
||||
#endif
|
||||
auto p_buf = buffer;
|
||||
setupPulsesCrossfire(module, p_buf, endpoint, channels, nChannels);
|
||||
|
||||
if (state->uart_drv) {
|
||||
auto drv = state->uart_drv;
|
||||
auto ctx = state->uart_ctx;
|
||||
drv->sendBuffer(ctx, data->pulses, data->length);
|
||||
} else {
|
||||
sportSendBuffer(data->pulses, data->length);
|
||||
}
|
||||
}
|
||||
|
||||
static int crossfireGetByte(void* context, uint8_t* data)
|
||||
{
|
||||
auto state = (CrossfireState*)context;
|
||||
if (state->uart_drv) {
|
||||
auto drv = state->uart_drv;
|
||||
auto ctx = state->uart_ctx;
|
||||
return drv->getByte(ctx, data);
|
||||
} else {
|
||||
return sportGetByte(data);
|
||||
}
|
||||
auto drv = modulePortGetSerialDrv(mod_st->tx);
|
||||
auto drv_ctx = modulePortGetCtx(mod_st->tx);
|
||||
drv->sendBuffer(drv_ctx, buffer, p_buf - buffer);
|
||||
}
|
||||
|
||||
static bool _lenIsSane(uint8_t len)
|
||||
@@ -210,7 +169,7 @@ static void _seekStart(uint8_t* buffer, uint8_t* len)
|
||||
*len = 0;
|
||||
}
|
||||
|
||||
static void crossfireProcessData(void* context, uint8_t data, uint8_t* buffer, uint8_t* len)
|
||||
static void crossfireProcessData(void* ctx, uint8_t data, uint8_t* buffer, uint8_t* len)
|
||||
{
|
||||
if (*len == 0 && data != RADIO_ADDRESS && data != UART_SYNC) {
|
||||
TRACE("[XF] address 0x%02X error", data);
|
||||
@@ -233,16 +192,15 @@ static void crossfireProcessData(void* context, uint8_t data, uint8_t* buffer, u
|
||||
|
||||
// rxBuffer[1] holds the packet length-2, check if the whole packet was received
|
||||
while (*len > 4 && (buffer[1]+2) == *len) {
|
||||
// TODO: module in context?
|
||||
if (_checkFrameCRC(buffer)) {
|
||||
#if defined(BLUETOOTH)
|
||||
#if defined(BLUETOOTH) // TODO: generic telemetry mirror to BT
|
||||
if (g_eeGeneral.bluetoothMode == BLUETOOTH_TELEMETRY &&
|
||||
bluetooth.state == BLUETOOTH_STATE_CONNECTED) {
|
||||
bluetooth.write(buffer, *len);
|
||||
}
|
||||
#endif
|
||||
auto state = (CrossfireState*)context;
|
||||
processCrossfireTelemetryFrame(state->module);
|
||||
auto mod_st = (etx_module_state_t*)ctx;
|
||||
processCrossfireTelemetryFrame(modulePortGetModule(mod_st));
|
||||
*len = 0;
|
||||
}
|
||||
else {
|
||||
@@ -252,84 +210,51 @@ static void crossfireProcessData(void* context, uint8_t data, uint8_t* buffer, u
|
||||
}
|
||||
}
|
||||
|
||||
#if defined(INTERNAL_MODULE_CRSF)
|
||||
static const etx_serial_init intmoduleCrossfireInitParams = {
|
||||
static const etx_serial_init crsfSerialParams = {
|
||||
.baudrate = 0,
|
||||
.parity = ETX_Parity_None,
|
||||
.stop_bits = ETX_StopBits_One,
|
||||
.word_length = ETX_WordLength_8,
|
||||
.rx_enable = true,
|
||||
.encoding = ETX_Encoding_8N1,
|
||||
.direction = ETX_Dir_TX_RX,
|
||||
.polarity = ETX_Pol_Normal,
|
||||
};
|
||||
|
||||
static void* crossfireInitInternal(uint8_t module)
|
||||
static void* crossfireInit(uint8_t module)
|
||||
{
|
||||
// serial port setup
|
||||
etx_serial_init params(intmoduleCrossfireInitParams);
|
||||
params.baudrate = INT_CROSSFIRE_BAUDRATE;
|
||||
INTERNAL_MODULE_ON();
|
||||
etx_module_state_t* mod_st = nullptr;
|
||||
etx_serial_init params(crsfSerialParams);
|
||||
|
||||
auto state = &crossfireState[module];
|
||||
state->init(module, &intmodulePulsesData.crossfire,
|
||||
&IntmoduleSerialDriver, IntmoduleSerialDriver.init(¶ms), 0);
|
||||
#if defined(INTERNAL_MODULE_CRSF)
|
||||
if (module == INTERNAL_MODULE) {
|
||||
|
||||
return state;
|
||||
}
|
||||
|
||||
static void crossfireDeInitInternal(void* context)
|
||||
{
|
||||
INTERNAL_MODULE_OFF();
|
||||
mixerSchedulerSetPeriod(INTERNAL_MODULE, 0);
|
||||
|
||||
auto state = (CrossfireState*)context;
|
||||
state->deinit();
|
||||
}
|
||||
|
||||
const etx_module_driver_t CrossfireInternalDriver = {
|
||||
.protocol = PROTOCOL_CHANNELS_CROSSFIRE,
|
||||
.init = crossfireInitInternal,
|
||||
.deinit = crossfireDeInitInternal,
|
||||
.setupPulses = crossfireSetupPulses,
|
||||
.sendPulses = crossfireSendPulses,
|
||||
.getByte = crossfireGetByte,
|
||||
.processData = crossfireProcessData,
|
||||
};
|
||||
params.baudrate = INT_CROSSFIRE_BAUDRATE;
|
||||
mod_st = modulePortInitSerial(module, ETX_MOD_PORT_UART, ¶ms);
|
||||
}
|
||||
#endif
|
||||
|
||||
static void* crossfireInitExternal(uint8_t module)
|
||||
{
|
||||
// telemetryInit(PROTOCOL_TELEMETRY_CROSSFIRE);
|
||||
telemetryProtocol = PROTOCOL_TELEMETRY_CROSSFIRE;
|
||||
telemetryPortInit(EXT_CROSSFIRE_BAUDRATE, TELEMETRY_SERIAL_DEFAULT);
|
||||
#if defined(LUA)
|
||||
outputTelemetryBuffer.reset();
|
||||
#if defined(HARDWARE_EXTERNAL_MODULE)
|
||||
if (module == EXTERNAL_MODULE) {
|
||||
|
||||
params.baudrate = EXT_CROSSFIRE_BAUDRATE;
|
||||
mod_st = modulePortInitSerial(module, ETX_MOD_PORT_SPORT, ¶ms);
|
||||
}
|
||||
#endif
|
||||
telemetryPortSetDirectionOutput();
|
||||
|
||||
mixerSchedulerSetPeriod(module, CROSSFIRE_PERIOD);
|
||||
EXTERNAL_MODULE_ON();
|
||||
|
||||
auto state = &crossfireState[module];
|
||||
state->init(module, &extmodulePulsesData.crossfire, nullptr, nullptr,
|
||||
TELEMETRY_ENDPOINT_SPORT);
|
||||
|
||||
return state;
|
||||
if (mod_st) {
|
||||
mixerSchedulerSetPeriod(module, CROSSFIRE_PERIOD(module));
|
||||
}
|
||||
|
||||
return (void*)mod_st;
|
||||
}
|
||||
|
||||
static void crossfireDeInitExternal(void* context)
|
||||
static void crossfireDeInit(void* ctx)
|
||||
{
|
||||
(void)context;
|
||||
telemetryProtocol = 0xFF;
|
||||
|
||||
EXTERNAL_MODULE_OFF();
|
||||
mixerSchedulerSetPeriod(EXTERNAL_MODULE, 0);
|
||||
auto mod_st = (etx_module_state_t*)ctx;
|
||||
modulePortDeInit(mod_st);
|
||||
}
|
||||
|
||||
const etx_module_driver_t CrossfireExternalDriver = {
|
||||
const etx_proto_driver_t CrossfireDriver = {
|
||||
.protocol = PROTOCOL_CHANNELS_CROSSFIRE,
|
||||
.init = crossfireInitExternal,
|
||||
.deinit = crossfireDeInitExternal,
|
||||
.setupPulses = crossfireSetupPulses,
|
||||
.init = crossfireInit,
|
||||
.deinit = crossfireDeInit,
|
||||
.sendPulses = crossfireSendPulses,
|
||||
.getByte = crossfireGetByte,
|
||||
.processData = crossfireProcessData,
|
||||
};
|
||||
|
||||
@@ -23,5 +23,4 @@
|
||||
|
||||
#include "hal/module_driver.h"
|
||||
|
||||
extern const etx_module_driver_t CrossfireInternalDriver;
|
||||
extern const etx_module_driver_t CrossfireExternalDriver;
|
||||
extern const etx_proto_driver_t CrossfireDriver;
|
||||
|
||||
+141
-80
@@ -19,7 +19,12 @@
|
||||
* GNU General Public License for more details.
|
||||
*/
|
||||
|
||||
#include "dsm2.h"
|
||||
#include "hal/module_port.h"
|
||||
#include "mixer_scheduler.h"
|
||||
|
||||
#include "opentx.h"
|
||||
#include "telemetry/spektrum.h"
|
||||
|
||||
#define DSM2_SEND_BIND (1 << 7)
|
||||
#define DSM2_SEND_RANGECHECK (1 << 5)
|
||||
@@ -31,62 +36,18 @@
|
||||
#define DSMX_BIT 0x08
|
||||
#define BAD_DATA 0x47
|
||||
|
||||
#define BITLEN_DSM2 (8*2) //125000 Baud => 8uS per bit
|
||||
#define DSM2_BITRATE 125000
|
||||
|
||||
static void reset_dsm2_buffer()
|
||||
static inline void sendByte(uint8_t*& p_buf, uint8_t b)
|
||||
{
|
||||
extmodulePulsesData.dsm2.index = 0;
|
||||
extmodulePulsesData.dsm2.ptr = extmodulePulsesData.dsm2.pulses;
|
||||
*p_buf++ = b;
|
||||
}
|
||||
|
||||
static void _send_1(uint8_t v)
|
||||
{
|
||||
*extmodulePulsesData.dsm2.ptr++ = v - 1;
|
||||
extmodulePulsesData.dsm2.index += 1;
|
||||
}
|
||||
|
||||
static void _sendByte(uint8_t b, uint8_t bit_len) // max 10 changes 0 10 10 10 10 1
|
||||
{
|
||||
bool lev = 0;
|
||||
uint8_t len = bit_len; // max val: 9*16 < 256
|
||||
for (uint8_t i=0; i<=8; i++) { // 8Bits + Stop=1
|
||||
bool nlev = b & 1; // lsb first
|
||||
if (lev == nlev) {
|
||||
len += bit_len;
|
||||
}
|
||||
else {
|
||||
_send_1(len);
|
||||
len = bit_len;
|
||||
lev = nlev;
|
||||
}
|
||||
b = (b>>1) | 0x80; // shift in stop bit
|
||||
}
|
||||
_send_1(len); // stop bit (len is already bit_len)
|
||||
}
|
||||
|
||||
static void sendByteDsm2(uint8_t b)
|
||||
{
|
||||
_sendByte(b, BITLEN_DSM2);
|
||||
}
|
||||
|
||||
void putDsm2Flush()
|
||||
{
|
||||
if (extmodulePulsesData.dsm2.index & 1)
|
||||
*extmodulePulsesData.dsm2.ptr++ = 255;
|
||||
else
|
||||
*(extmodulePulsesData.dsm2.ptr - 1) = 255;
|
||||
}
|
||||
|
||||
// This is the data stream to send, prepare after 19.5 mS
|
||||
// Send after 22.5 mS
|
||||
|
||||
void setupPulsesDSM2()
|
||||
static void setupPulsesDSM2(uint8_t module, uint8_t*& p_buf)
|
||||
{
|
||||
uint8_t dsmDat[14];
|
||||
|
||||
reset_dsm2_buffer();
|
||||
|
||||
switch (moduleState[EXTERNAL_MODULE].protocol) {
|
||||
switch (moduleState[module].protocol) {
|
||||
case PROTOCOL_CHANNELS_DSM2_LP45:
|
||||
dsmDat[0] = 0x00;
|
||||
break;
|
||||
@@ -98,17 +59,17 @@ void setupPulsesDSM2()
|
||||
break;
|
||||
}
|
||||
|
||||
if (moduleState[EXTERNAL_MODULE].mode == MODULE_MODE_BIND) {
|
||||
if (moduleState[module].mode == MODULE_MODE_BIND) {
|
||||
dsmDat[0] |= DSM2_SEND_BIND;
|
||||
}
|
||||
else if (moduleState[EXTERNAL_MODULE].mode == MODULE_MODE_RANGECHECK) {
|
||||
else if (moduleState[module].mode == MODULE_MODE_RANGECHECK) {
|
||||
dsmDat[0] |= DSM2_SEND_RANGECHECK;
|
||||
}
|
||||
|
||||
dsmDat[1] = g_model.header.modelId[EXTERNAL_MODULE]; // DSM2 Header second byte for model match
|
||||
dsmDat[1] = g_model.header.modelId[module]; // DSM2 Header second byte for model match
|
||||
|
||||
for (int i=0; i<DSM2_CHANS; i++) {
|
||||
int channel = g_model.moduleData[EXTERNAL_MODULE].channelsStart+i;
|
||||
int channel = g_model.moduleData[module].channelsStart+i;
|
||||
int value = channelOutputs[channel] + 2*PPM_CH_CENTER(channel) - 2*PPM_CENTER;
|
||||
uint16_t pulse = limit(0, ((value*13)>>5)+512, 1023);
|
||||
dsmDat[2+2*i] = (i<<2) | ((pulse>>8)&0x03);
|
||||
@@ -116,36 +77,28 @@ void setupPulsesDSM2()
|
||||
}
|
||||
|
||||
for (int i=0; i<14; i++) {
|
||||
sendByteDsm2(dsmDat[i]);
|
||||
sendByte(p_buf, dsmDat[i]);
|
||||
}
|
||||
|
||||
putDsm2Flush();
|
||||
}
|
||||
|
||||
#define BITLEN_DSMP (17)
|
||||
|
||||
static void sendByteDSMP(uint8_t b)
|
||||
{
|
||||
_sendByte(b, BITLEN_DSMP);
|
||||
}
|
||||
#define DSMP_BITRATE 115200
|
||||
|
||||
void setupPulsesLemonDSMP()
|
||||
static void setupPulsesLemonDSMP(uint8_t module, uint8_t*& p_buf)
|
||||
{
|
||||
static uint8_t pass = 0;
|
||||
|
||||
reset_dsm2_buffer();
|
||||
|
||||
const auto& md = g_model.moduleData[EXTERNAL_MODULE];
|
||||
const auto& md = g_model.moduleData[module];
|
||||
|
||||
uint8_t start_channel = md.channelsStart;
|
||||
auto channels = md.getChannelsCount();
|
||||
auto flags = md.dsmp.flags & 0x3F;
|
||||
|
||||
// Force setup packet in Bind mode.
|
||||
auto module_mode = getModuleMode(EXTERNAL_MODULE);
|
||||
auto module_mode = getModuleMode(module);
|
||||
|
||||
sendByteDSMP( 0xAA );
|
||||
sendByteDSMP( pass );
|
||||
sendByte(p_buf, 0xAA);
|
||||
sendByte(p_buf, pass);
|
||||
|
||||
// Setup packet
|
||||
if (pass == 0) {
|
||||
@@ -154,17 +107,17 @@ void setupPulsesLemonDSMP()
|
||||
flags = DSM2_SEND_BIND | (1 << 6 /* AUTO */);
|
||||
channels = 12;
|
||||
}
|
||||
sendByteDSMP( flags );
|
||||
sendByte(p_buf, flags);
|
||||
|
||||
uint8_t pwr = 7;
|
||||
if (module_mode == MODULE_MODE_RANGECHECK) {
|
||||
pwr = 4;
|
||||
}
|
||||
sendByteDSMP( pwr );
|
||||
sendByteDSMP( channels );
|
||||
sendByte(p_buf, pwr);
|
||||
sendByte(p_buf, channels );
|
||||
|
||||
// Model number
|
||||
sendByteDSMP( 1 );
|
||||
sendByte(p_buf, 1);
|
||||
|
||||
// Send only 1 single Setup packet
|
||||
pass = 1;
|
||||
@@ -192,20 +145,18 @@ void setupPulsesLemonDSMP()
|
||||
pulse = limit(0, ((value*13)>>5)+512, 1023) | (current_channel << 10);
|
||||
}
|
||||
|
||||
sendByteDSMP( pulse >> 8 );
|
||||
sendByteDSMP( pulse & 0xFF );
|
||||
sendByte(p_buf, pulse >> 8);
|
||||
sendByte(p_buf, pulse & 0xFF);
|
||||
} else {
|
||||
// Outside of announced number of channels:
|
||||
// -> send invalid value
|
||||
sendByteDSMP( 0xFF );
|
||||
sendByteDSMP( 0xFF );
|
||||
sendByte(p_buf, 0xFF);
|
||||
sendByte(p_buf, 0xFF);
|
||||
}
|
||||
current_channel++;
|
||||
}
|
||||
}
|
||||
|
||||
putDsm2Flush();
|
||||
|
||||
if (++pass > 2) pass = 1;
|
||||
if (channels < 8) pass = 1;
|
||||
|
||||
@@ -213,9 +164,119 @@ void setupPulsesLemonDSMP()
|
||||
// bind packet is setup
|
||||
pass = 0;
|
||||
}
|
||||
else if (--moduleState[EXTERNAL_MODULE].counter == 0) {
|
||||
else if (--moduleState[module].counter == 0) {
|
||||
// every 100th packet is setup
|
||||
pass = 0;
|
||||
moduleState[EXTERNAL_MODULE].counter = 100;
|
||||
moduleState[module].counter = 100;
|
||||
}
|
||||
}
|
||||
|
||||
const etx_serial_init dsmUartParams = {
|
||||
.baudrate = 0,
|
||||
.encoding = ETX_Encoding_8N1,
|
||||
.direction = ETX_Dir_TX,
|
||||
.polarity = ETX_Pol_Inverted,
|
||||
};
|
||||
|
||||
static void* dsmInit(uint8_t module, uint32_t baudrate, uint16_t period, bool telemetry)
|
||||
{
|
||||
// only external module supported
|
||||
if (module == INTERNAL_MODULE) return nullptr;
|
||||
|
||||
etx_serial_init params(dsmUartParams);
|
||||
params.baudrate = baudrate;
|
||||
auto mod_st = modulePortInitSerial(module, ETX_MOD_PORT_UART, ¶ms);
|
||||
if (!mod_st) {
|
||||
// inverted soft-serial fallback
|
||||
mod_st = modulePortInitSerial(module, ETX_MOD_PORT_SOFT_INV, ¶ms);
|
||||
if (!mod_st) return nullptr;
|
||||
}
|
||||
|
||||
if (telemetry) {
|
||||
params.direction = ETX_Dir_RX;
|
||||
if (!modulePortInitSerial(module, ETX_MOD_PORT_SPORT, ¶ms)) {
|
||||
// inverted soft-serial fallback
|
||||
modulePortInitSerial(module, ETX_MOD_PORT_SPORT_INV, ¶ms);
|
||||
}
|
||||
}
|
||||
|
||||
mixerSchedulerSetPeriod(module, period);
|
||||
return (void*)mod_st;
|
||||
}
|
||||
|
||||
static void* dsm2Init(uint8_t module)
|
||||
{
|
||||
return dsmInit(module, DSM2_BITRATE, DSM2_PERIOD, false);
|
||||
}
|
||||
|
||||
static void* dsmpInit(uint8_t module)
|
||||
{
|
||||
return dsmInit(module, DSMP_BITRATE, 11 * 1000 /* 11ms in us */, true);
|
||||
}
|
||||
|
||||
|
||||
static void dsmDeInit(void* ctx)
|
||||
{
|
||||
auto mod_st = (etx_module_state_t*)ctx;
|
||||
modulePortDeInit(mod_st);
|
||||
}
|
||||
|
||||
static void _dsm_send(etx_module_state_t* st, uint8_t* buffer, uint32_t len)
|
||||
{
|
||||
auto drv = modulePortGetSerialDrv(st->tx);
|
||||
auto ctx = modulePortGetCtx(st->tx);
|
||||
drv->sendBuffer(ctx, buffer, len);
|
||||
}
|
||||
|
||||
static void dsm2SendPulses(void* ctx, uint8_t* buffer, int16_t* channels, uint8_t nChannels)
|
||||
{
|
||||
// TODO:
|
||||
(void)channels;
|
||||
(void)nChannels;
|
||||
|
||||
auto mod_st = (etx_module_state_t*)ctx;
|
||||
auto module = modulePortGetModule(mod_st);
|
||||
|
||||
auto p_data = buffer;
|
||||
setupPulsesDSM2(module, p_data);
|
||||
_dsm_send(mod_st, buffer, p_data - buffer);
|
||||
}
|
||||
|
||||
static void dsmpSendPulses(void* ctx, uint8_t* buffer, int16_t* channels, uint8_t nChannels)
|
||||
{
|
||||
// TODO:
|
||||
(void)channels;
|
||||
(void)nChannels;
|
||||
|
||||
auto mod_st = (etx_module_state_t*)ctx;
|
||||
auto module = modulePortGetModule(mod_st);
|
||||
|
||||
auto p_data = buffer;
|
||||
setupPulsesLemonDSMP(module, p_data);
|
||||
_dsm_send(mod_st, buffer, p_data - buffer);
|
||||
}
|
||||
|
||||
static void dsmpProcessData(void* ctx, uint8_t data, uint8_t* buffer, uint8_t* len)
|
||||
{
|
||||
auto mod_st = (etx_module_state_t*)ctx;
|
||||
auto module = modulePortGetModule(mod_st);
|
||||
|
||||
processSpektrumTelemetryData(module, data, buffer, *len);
|
||||
}
|
||||
|
||||
// No telemetry
|
||||
const etx_proto_driver_t DSM2Driver = {
|
||||
.protocol = PROTOCOL_CHANNELS_DSM2_LP45,
|
||||
.init = dsm2Init,
|
||||
.deinit = dsmDeInit,
|
||||
.sendPulses = dsm2SendPulses,
|
||||
.processData = nullptr,
|
||||
};
|
||||
|
||||
const etx_proto_driver_t DSMPDriver = {
|
||||
.protocol = PROTOCOL_CHANNELS_DSMP,
|
||||
.init = dsmpInit,
|
||||
.deinit = dsmDeInit,
|
||||
.sendPulses = dsmpSendPulses,
|
||||
.processData = dsmpProcessData,
|
||||
};
|
||||
|
||||
@@ -0,0 +1,30 @@
|
||||
/*
|
||||
* Copyright (C) EdgeTx
|
||||
*
|
||||
* Based on code named
|
||||
* opentx - https://github.com/opentx/opentx
|
||||
* 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.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "hal/module_driver.h"
|
||||
|
||||
// DSM2/DSMX
|
||||
extern const etx_proto_driver_t DSM2Driver;
|
||||
|
||||
// Lemon RX DSMP
|
||||
extern const etx_proto_driver_t DSMPDriver;
|
||||
+115
-206
@@ -116,11 +116,15 @@ struct rf_info_t {
|
||||
uint8_t fw_state;
|
||||
};
|
||||
|
||||
// static STRUCT_HALL rxBuffer = {0};
|
||||
static uint32_t rfRxCount = 0;
|
||||
static uint8_t lastState = STATE_IDLE;
|
||||
static uint32_t set_loop_cnt = 0;
|
||||
|
||||
static uint8_t _flysky_state;
|
||||
static uint8_t _flysky_frame_index;
|
||||
static uint8_t _flysky_crc;
|
||||
static uint8_t _flysky_timeout;
|
||||
static uint8_t _esc_state;
|
||||
|
||||
uint32_t NV14internalModuleFwVersion = 0;
|
||||
|
||||
static rf_info_t rf_info = {
|
||||
@@ -153,11 +157,7 @@ uint8_t emptyRxID[] = {0, 0, 0, 0};
|
||||
void setFlyskyState(uint8_t state);
|
||||
void onFlySkyBindReceiver();
|
||||
void onFlySkyModuleSetPower(bool isPowerOn);
|
||||
void afhds2Command(uint8_t type, uint8_t cmd);
|
||||
void onFlySkyGetVersionInfoStart(uint8_t isRfTransfer);
|
||||
// void usbDownloadTransmit(uint8_t *buffer, uint32_t size);
|
||||
|
||||
// extern uint32_t NV14internalModuleFwVersion;
|
||||
void afhds2Command(uint8_t*& p_buf, uint8_t type, uint8_t cmd);
|
||||
|
||||
#define END 0xC0
|
||||
#define ESC 0xDB
|
||||
@@ -190,146 +190,85 @@ enum FlySkyModuleCommandID {
|
||||
CMD_LAST
|
||||
};
|
||||
|
||||
bool isFlySkyUsbDownload(void) { return rf_info.fw_state != 0; }
|
||||
|
||||
// void usbSetFrameTransmit(uint8_t packetID, uint8_t *dataBuf, uint32_t nBytes)
|
||||
// {
|
||||
// // send to host via usb
|
||||
// uint8_t *pt = (uint8_t*)&rxBuffer;
|
||||
// // rxBuffer.head = HALL_PROTOLO_HEAD;
|
||||
// rxBuffer.hallID.hall_Id.packetID = packetID;//0x08;
|
||||
// rxBuffer.hallID.hall_Id.senderID = 0x03;
|
||||
// rxBuffer.hallID.hall_Id.receiverID = 0x02;
|
||||
|
||||
// if ( packetID == 0x08 ) {
|
||||
// uint8_t fwVerision[40];
|
||||
// for(uint32_t idx = 40; idx > 0; idx--)
|
||||
// {
|
||||
// if ( idx <= nBytes ) {
|
||||
// fwVerision[idx-1] = dataBuf[idx-1];
|
||||
// }
|
||||
// else fwVerision[idx-1] = 0;
|
||||
// }
|
||||
// dataBuf = fwVerision;
|
||||
// nBytes = 40;
|
||||
// }
|
||||
|
||||
// rxBuffer.length = nBytes;
|
||||
|
||||
// TRACE_NOCRLF("\r\nToUSB: 55 %02X %02X ", rxBuffer.hallID.ID, nBytes);
|
||||
// for ( uint32_t idx = 0; idx < nBytes; idx++ )
|
||||
// {
|
||||
// rxBuffer.data[idx] = dataBuf[idx];
|
||||
// TRACE_NOCRLF("%02X ", rxBuffer.data[idx]);
|
||||
// }
|
||||
// #if !defined(SIMU)
|
||||
// uint16_t checkSum = calc_crc16(pt, rxBuffer.length+3);
|
||||
// TRACE(" CRC:%04X;", checkSum);
|
||||
|
||||
// pt[rxBuffer.length + 3] = checkSum & 0xFF;
|
||||
// pt[rxBuffer.length + 4] = checkSum >> 8;
|
||||
|
||||
// usbDownloadTransmit(pt, rxBuffer.length + 5);
|
||||
// #endif
|
||||
// }
|
||||
|
||||
void onFlySkyModuleSetPower(bool isPowerOn)
|
||||
void setFlyskyState(uint8_t state)
|
||||
{
|
||||
if (isPowerOn) {
|
||||
INTERNAL_MODULE_ON();
|
||||
resetPulsesAFHDS2();
|
||||
} else {
|
||||
moduleState[INTERNAL_MODULE].mode = MODULE_MODE_NORMAL;
|
||||
INTERNAL_MODULE_OFF();
|
||||
}
|
||||
_flysky_state = state;
|
||||
}
|
||||
|
||||
void setFlyskyState(uint8_t state) { intmodulePulsesData.flysky.state = state; }
|
||||
|
||||
void onFlySkyUsbDownloadStart(uint8_t fw_state) { rf_info.fw_state = fw_state; }
|
||||
|
||||
void onFlySkyGetVersionInfoStart(uint8_t isRfTransfer)
|
||||
inline void initFlySkyCRC()
|
||||
{
|
||||
lastState = intmodulePulsesData.flysky.state;
|
||||
setFlyskyState(isRfTransfer ? STATE_GET_RF_VERSION_INFO
|
||||
: STATE_GET_RX_VERSION_INFO);
|
||||
_flysky_crc = 0;
|
||||
}
|
||||
|
||||
inline void initFlySkyArray()
|
||||
{
|
||||
intmodulePulsesData.flysky.ptr = intmodulePulsesData.flysky.pulses;
|
||||
intmodulePulsesData.flysky.crc = 0;
|
||||
}
|
||||
|
||||
inline void putFlySkyByte(uint8_t byte)
|
||||
inline void putFlySkyByte(uint8_t*& p_buf, uint8_t byte)
|
||||
{
|
||||
if (END == byte) {
|
||||
*intmodulePulsesData.flysky.ptr++ = ESC;
|
||||
*intmodulePulsesData.flysky.ptr++ = ESC_END;
|
||||
*p_buf++ = ESC;
|
||||
*p_buf++ = ESC_END;
|
||||
} else if (ESC == byte) {
|
||||
*intmodulePulsesData.flysky.ptr++ = ESC;
|
||||
*intmodulePulsesData.flysky.ptr++ = ESC_ESC;
|
||||
*p_buf++ = ESC;
|
||||
*p_buf++ = ESC_ESC;
|
||||
} else {
|
||||
*intmodulePulsesData.flysky.ptr++ = byte;
|
||||
*p_buf++ = byte;
|
||||
}
|
||||
}
|
||||
|
||||
inline void putFlySkyFrameByte(uint8_t byte)
|
||||
inline void putFlySkyFrameByte(uint8_t*& p_buf, uint8_t byte)
|
||||
{
|
||||
intmodulePulsesData.flysky.crc += byte;
|
||||
putFlySkyByte(byte);
|
||||
_flysky_crc += byte;
|
||||
putFlySkyByte(p_buf, byte);
|
||||
}
|
||||
|
||||
inline void putFlySkyFrameCmd(uint8_t type, uint8_t cmd)
|
||||
inline void putFlySkyFrameCmd(uint8_t*& p_buf, uint8_t type, uint8_t cmd)
|
||||
{
|
||||
intmodulePulsesData.flysky.crc += type + cmd;
|
||||
putFlySkyByte(type);
|
||||
putFlySkyByte(cmd);
|
||||
_flysky_crc += type + cmd;
|
||||
putFlySkyByte(p_buf, type);
|
||||
putFlySkyByte(p_buf, cmd);
|
||||
}
|
||||
|
||||
inline void putFlySkyFrameBytes(uint8_t* data, int length)
|
||||
inline void putFlySkyFrameBytes(uint8_t*& p_buf, uint8_t* data, int length)
|
||||
{
|
||||
for (int i = 0; i < length; i++) {
|
||||
intmodulePulsesData.flysky.crc += data[i];
|
||||
putFlySkyByte(data[i]);
|
||||
_flysky_crc += data[i];
|
||||
putFlySkyByte(p_buf, data[i]);
|
||||
}
|
||||
}
|
||||
|
||||
inline void putFlySkyFrameHeader()
|
||||
inline void putFlySkyFrameHeader(uint8_t*& p_buf)
|
||||
{
|
||||
initFlySkyArray();
|
||||
*intmodulePulsesData.flysky.ptr++ = END;
|
||||
putFlySkyFrameByte(intmodulePulsesData.flysky.frame_index);
|
||||
initFlySkyCRC();
|
||||
*p_buf++ = END;
|
||||
putFlySkyFrameByte(p_buf, _flysky_frame_index);
|
||||
}
|
||||
|
||||
inline void putFlySkyFrameFooter()
|
||||
inline void putFlySkyFrameFooter(uint8_t*& p_buf)
|
||||
{
|
||||
if (++intmodulePulsesData.flysky.frame_index == 0) {
|
||||
intmodulePulsesData.flysky.frame_index = 1;
|
||||
if (++_flysky_frame_index == 0) {
|
||||
_flysky_frame_index = 1;
|
||||
}
|
||||
putFlySkyByte(intmodulePulsesData.flysky.crc ^ 0xff);
|
||||
*intmodulePulsesData.flysky.ptr++ = END;
|
||||
putFlySkyByte(p_buf, _flysky_crc ^ 0xff);
|
||||
*p_buf++ = END;
|
||||
}
|
||||
|
||||
void afhds2Command(uint8_t type, uint8_t cmd)
|
||||
void afhds2Command(uint8_t*& p_buf, uint8_t type, uint8_t cmd)
|
||||
{
|
||||
putFlySkyFrameHeader();
|
||||
putFlySkyFrameCmd(type, cmd);
|
||||
putFlySkyFrameFooter();
|
||||
putFlySkyFrameHeader(p_buf);
|
||||
putFlySkyFrameCmd(p_buf, type, cmd);
|
||||
putFlySkyFrameFooter(p_buf);
|
||||
}
|
||||
|
||||
inline void putFlySkySendChannelData()
|
||||
inline void putFlySkySendChannelData(uint8_t*& p_buf)
|
||||
{
|
||||
uint16_t pulseValue = 0;
|
||||
uint8_t channels_start = g_model.moduleData[INTERNAL_MODULE].channelsStart;
|
||||
uint8_t channels_last =
|
||||
channels_start + 8 + g_model.moduleData[INTERNAL_MODULE].channelsCount;
|
||||
putFlySkyFrameCmd(FRAME_TYPE_REQUEST_NACK, CMD_SEND_CHANNEL_DATA);
|
||||
putFlySkyFrameCmd(p_buf, FRAME_TYPE_REQUEST_NACK, CMD_SEND_CHANNEL_DATA);
|
||||
|
||||
if (moduleState[INTERNAL_MODULE].counter-- == 0) {
|
||||
moduleState[INTERNAL_MODULE].counter = FAILSAVE_SEND_COUNTER_MAX;
|
||||
putFlySkyFrameByte(FLYSKY_CHANNEL_DATA_FAILSAFE);
|
||||
putFlySkyFrameByte(channels_last - channels_start);
|
||||
putFlySkyFrameByte(p_buf, FLYSKY_CHANNEL_DATA_FAILSAFE);
|
||||
putFlySkyFrameByte(p_buf, channels_last - channels_start);
|
||||
for (uint8_t channel = channels_start; channel < channels_last; channel++) {
|
||||
if (g_model.moduleData[INTERNAL_MODULE].failsafeMode == FAILSAFE_CUSTOM) {
|
||||
int16_t failsafeValue = g_model.failsafeChannels[channel];
|
||||
@@ -345,28 +284,28 @@ inline void putFlySkySendChannelData()
|
||||
pulseValue =
|
||||
limit<uint16_t>(0, 988 + ((failsafeValue + 1024) / 2), 0xfff);
|
||||
}
|
||||
putFlySkyFrameByte(pulseValue & 0xff);
|
||||
putFlySkyFrameByte(pulseValue >> 8);
|
||||
putFlySkyFrameByte(p_buf, pulseValue & 0xff);
|
||||
putFlySkyFrameByte(p_buf, pulseValue >> 8);
|
||||
}
|
||||
if (DEBUG_RF_FRAME_PRINT & RF_FRAME_ONLY) {
|
||||
TRACE("------FAILSAFE------");
|
||||
}
|
||||
} else {
|
||||
putFlySkyFrameByte(FLYSKY_CHANNEL_DATA_NORMAL);
|
||||
putFlySkyFrameByte(channels_last - channels_start);
|
||||
putFlySkyFrameByte(p_buf, FLYSKY_CHANNEL_DATA_NORMAL);
|
||||
putFlySkyFrameByte(p_buf, channels_last - channels_start);
|
||||
for (uint8_t channel = channels_start; channel < channels_last; channel++) {
|
||||
int channelValue =
|
||||
channelOutputs[channel] + 2 * PPM_CH_CENTER(channel) - 2 * PPM_CENTER;
|
||||
pulseValue = limit<uint16_t>(0, 988 + ((channelValue + 1024) / 2), 0xfff);
|
||||
putFlySkyFrameByte(pulseValue & 0xff);
|
||||
putFlySkyFrameByte(pulseValue >> 8);
|
||||
putFlySkyFrameByte(p_buf, pulseValue & 0xff);
|
||||
putFlySkyFrameByte(p_buf, pulseValue >> 8);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void putFlySkyUpdateFirmwareStart(uint8_t cmd)
|
||||
void putFlySkyUpdateFirmwareStart(uint8_t*& p_buf, uint8_t cmd)
|
||||
{
|
||||
putFlySkyFrameCmd(FRAME_TYPE_REQUEST_ACK, cmd);
|
||||
putFlySkyFrameCmd(p_buf, FRAME_TYPE_REQUEST_ACK, cmd);
|
||||
}
|
||||
|
||||
bool checkFlySkyFrameCrc(const uint8_t* ptr, uint8_t size)
|
||||
@@ -419,17 +358,17 @@ inline void parseResponse(uint8_t* buffer, uint8_t dataLen)
|
||||
|
||||
if ((moduleState[INTERNAL_MODULE].mode != MODULE_MODE_BIND) &&
|
||||
(resp->frame_type == FRAME_TYPE_ANSWER) &&
|
||||
((intmodulePulsesData.flysky.frame_index - 1) != resp->frame_number)) {
|
||||
((_flysky_frame_index - 1) != resp->frame_number)) {
|
||||
return;
|
||||
} else if (resp->frame_type == FRAME_TYPE_REQUEST_ACK) {
|
||||
intmodulePulsesData.flysky.frame_index = resp->frame_number;
|
||||
_flysky_frame_index = resp->frame_number;
|
||||
}
|
||||
|
||||
debugFrame(&resp->command_id, dataLen - 2);
|
||||
switch (resp->command_id) {
|
||||
default:
|
||||
if (moduleState[INTERNAL_MODULE].mode == MODULE_MODE_NORMAL &&
|
||||
intmodulePulsesData.flysky.state >= STATE_IDLE) {
|
||||
_flysky_state >= STATE_IDLE) {
|
||||
setFlyskyState(STATE_SEND_CHANNELS);
|
||||
if (DEBUG_RF_FRAME_PRINT & RF_FRAME_ONLY)
|
||||
TRACE("State back to channel data");
|
||||
@@ -467,12 +406,12 @@ inline void parseResponse(uint8_t* buffer, uint8_t dataLen)
|
||||
}
|
||||
case CMD_RF_GET_CONFIG:
|
||||
setFlyskyState(STATE_GET_RECEIVER_CONFIG);
|
||||
intmodulePulsesData.flysky.timeout = FLYSKY_MODULE_TIMEOUT;
|
||||
_flysky_timeout = FLYSKY_MODULE_TIMEOUT;
|
||||
break;
|
||||
case CMD_RX_SENSOR_DATA:
|
||||
flySkyNv14ProcessTelemetryPacket(&resp->value, dataLen - 3);
|
||||
if (moduleState[INTERNAL_MODULE].mode == MODULE_MODE_NORMAL &&
|
||||
intmodulePulsesData.flysky.state >= STATE_IDLE) {
|
||||
_flysky_state >= STATE_IDLE) {
|
||||
setFlyskyState(STATE_SEND_CHANNELS);
|
||||
}
|
||||
break;
|
||||
@@ -510,7 +449,7 @@ inline void parseResponse(uint8_t* buffer, uint8_t dataLen)
|
||||
break;
|
||||
}
|
||||
case CMD_GET_VERSION_INFO: {
|
||||
if (intmodulePulsesData.flysky.state == STATE_GET_FW_VERSION_INIT) {
|
||||
if (_flysky_state == STATE_GET_FW_VERSION_INIT) {
|
||||
memcpy(&NV14internalModuleFwVersion, &resp->value + 1,
|
||||
sizeof(NV14internalModuleFwVersion));
|
||||
setFlyskyState(STATE_SET_RECEIVER_ID);
|
||||
@@ -531,48 +470,17 @@ inline void parseResponse(uint8_t* buffer, uint8_t dataLen)
|
||||
}
|
||||
}
|
||||
|
||||
bool isrxBufferMsgOK(void)
|
||||
{
|
||||
bool isMsgOK = (0 != rfRxCount);
|
||||
rfRxCount = 0;
|
||||
return isMsgOK && isFlySkyUsbDownload();
|
||||
}
|
||||
|
||||
void processInternalFlySkyTelemetryData(uint8_t byte, uint8_t* buffer, uint8_t* len)
|
||||
{
|
||||
// #if !defined(SIMU)
|
||||
// parseFlyskyData(&rxBuffer, byte);
|
||||
// #endif
|
||||
// if (rxBuffer.valid) {
|
||||
// rfRxCount++;
|
||||
// rxBuffer.valid = 0;
|
||||
// uint8_t *pt = (uint8_t*)&rxBuffer;
|
||||
// //rxBuffer.head = HALL_PROTOLO_HEAD;
|
||||
// pt[rxBuffer.length + 3] = rxBuffer.checkSum & 0xFF;
|
||||
// pt[rxBuffer.length + 4] = rxBuffer.checkSum >> 8;
|
||||
|
||||
// if((DEBUG_RF_FRAME_PRINT & RF_FRAME_ONLY)) {
|
||||
// TRACE("RF: %02X %02X %02X ...%04X; CRC:%04X", pt[0], pt[1], pt[2],
|
||||
// rxBuffer.checkSum, calc_crc16(pt, rxBuffer.length+3));
|
||||
// }
|
||||
|
||||
// //FW UPDATE done
|
||||
// if (0x01 == rxBuffer.length && (0x05 == rxBuffer.data[0] || 0x06 ==
|
||||
// rxBuffer.data[0])) {
|
||||
// setFlyskyState(STATE_INIT);
|
||||
// rf_info.fw_state = 0;
|
||||
// }
|
||||
// usbDownloadTransmit(pt, rxBuffer.length + 5);
|
||||
// }
|
||||
if (byte == END && *len > 0) {
|
||||
parseResponse(buffer, *len);
|
||||
*len = 0;
|
||||
} else {
|
||||
if (byte == ESC) {
|
||||
intmodulePulsesData.flysky.esc_state = 1;
|
||||
_esc_state = 1;
|
||||
} else {
|
||||
if (intmodulePulsesData.flysky.esc_state) {
|
||||
intmodulePulsesData.flysky.esc_state = 0;
|
||||
if (_esc_state) {
|
||||
_esc_state = 0;
|
||||
if (byte == ESC_END)
|
||||
byte = END;
|
||||
else if (byte == ESC_ESC)
|
||||
@@ -590,10 +498,10 @@ void processInternalFlySkyTelemetryData(uint8_t byte, uint8_t* buffer, uint8_t*
|
||||
void resetPulsesAFHDS2()
|
||||
{
|
||||
NV14internalModuleFwVersion = 0;
|
||||
intmodulePulsesData.flysky.frame_index = 1;
|
||||
_flysky_frame_index = 1;
|
||||
setFlyskyState(STATE_SET_TX_POWER);
|
||||
intmodulePulsesData.flysky.timeout = 0;
|
||||
intmodulePulsesData.flysky.esc_state = 0;
|
||||
_flysky_timeout = 0;
|
||||
_esc_state = 0;
|
||||
uint16_t rx_freq = gRomData.rx_freq[0];
|
||||
rx_freq += (gRomData.rx_freq[1] * 256);
|
||||
if (50 > rx_freq || 400 < rx_freq) {
|
||||
@@ -601,41 +509,43 @@ void resetPulsesAFHDS2()
|
||||
}
|
||||
}
|
||||
|
||||
void setupPulsesAFHDS2()
|
||||
void setupPulsesAFHDS2(uint8_t*& p_buf)
|
||||
{
|
||||
putFlySkyFrameHeader();
|
||||
if (intmodulePulsesData.flysky.state == STATE_DISCONNECT) {
|
||||
auto buffer_start = p_buf;
|
||||
|
||||
putFlySkyFrameHeader(p_buf);
|
||||
if (_flysky_state == STATE_DISCONNECT) {
|
||||
TRACE("STATE_DISCONNECT");
|
||||
putFlySkyFrameCmd(FRAME_TYPE_REQUEST_ACK, CMD_SET_RECEIVER_ID);
|
||||
putFlySkyFrameBytes(emptyRxID, 4);
|
||||
} else if (intmodulePulsesData.flysky.state < STATE_SEND_CHANNELS) {
|
||||
if (++intmodulePulsesData.flysky.timeout >=
|
||||
putFlySkyFrameCmd(p_buf, FRAME_TYPE_REQUEST_ACK, CMD_SET_RECEIVER_ID);
|
||||
putFlySkyFrameBytes(p_buf, emptyRxID, 4);
|
||||
} else if (_flysky_state < STATE_SEND_CHANNELS) {
|
||||
if (++_flysky_timeout >=
|
||||
FLYSKY_MODULE_TIMEOUT / FLYSKY_PERIOD) {
|
||||
intmodulePulsesData.flysky.timeout = 0;
|
||||
switch (intmodulePulsesData.flysky.state) {
|
||||
_flysky_timeout = 0;
|
||||
switch (_flysky_state) {
|
||||
case STATE_INIT: {
|
||||
putFlySkyFrameCmd(FRAME_TYPE_REQUEST_ACK, CMD_RF_INIT);
|
||||
putFlySkyFrameCmd(p_buf, FRAME_TYPE_REQUEST_ACK, CMD_RF_INIT);
|
||||
} break;
|
||||
case STATE_BIND: {
|
||||
putFlySkyFrameCmd(FRAME_TYPE_REQUEST_ACK, CMD_BIND);
|
||||
putFlySkyFrameByte(rf_info.bind_power);
|
||||
putFlySkyFrameCmd(p_buf, FRAME_TYPE_REQUEST_ACK, CMD_BIND);
|
||||
putFlySkyFrameByte(p_buf, rf_info.bind_power);
|
||||
uint32_t txID =
|
||||
0; // internal module firmware does not allow changing TX id
|
||||
putFlySkyFrameBytes((uint8_t*)(&txID), 4);
|
||||
putFlySkyFrameBytes(p_buf, (uint8_t*)(&txID), 4);
|
||||
} break;
|
||||
case STATE_SET_RECEIVER_ID: {
|
||||
putFlySkyFrameCmd(FRAME_TYPE_REQUEST_ACK, CMD_SET_RECEIVER_ID);
|
||||
putFlySkyFrameBytes(gRomData.rx_id, 4);
|
||||
putFlySkyFrameCmd(p_buf, FRAME_TYPE_REQUEST_ACK, CMD_SET_RECEIVER_ID);
|
||||
putFlySkyFrameBytes(p_buf, gRomData.rx_id, 4);
|
||||
} break;
|
||||
case STATE_GET_RECEIVER_CONFIG: {
|
||||
putFlySkyFrameCmd(FRAME_TYPE_ANSWER, CMD_RF_GET_CONFIG);
|
||||
putFlySkyFrameByte(AfhdsPwmMode); // 00:PWM, 01:PPM
|
||||
putFlySkyFrameByte(AfhdsIbusMode); // 00:I-BUS, 01:S-BUS
|
||||
putFlySkyFrameByte(
|
||||
putFlySkyFrameCmd(p_buf, FRAME_TYPE_ANSWER, CMD_RF_GET_CONFIG);
|
||||
putFlySkyFrameByte(p_buf, AfhdsPwmMode); // 00:PWM, 01:PPM
|
||||
putFlySkyFrameByte(p_buf, AfhdsIbusMode); // 00:I-BUS, 01:S-BUS
|
||||
putFlySkyFrameByte(p_buf,
|
||||
gRomData.rx_freq[0] < 50
|
||||
? 50
|
||||
: gRomData.rx_freq[0]); // receiver servo freq bit[7:0]
|
||||
putFlySkyFrameByte(
|
||||
putFlySkyFrameByte(p_buf,
|
||||
gRomData.rx_freq[1]); // receiver servo freq bit[15:8]
|
||||
setFlyskyState(STATE_INIT);
|
||||
} break;
|
||||
@@ -644,12 +554,12 @@ void setupPulsesAFHDS2()
|
||||
moduleState[INTERNAL_MODULE].mode == MODULE_MODE_RANGECHECK ? 0
|
||||
: gRomData.rfPower ? 170
|
||||
: 90;
|
||||
putFlySkyFrameCmd(FRAME_TYPE_REQUEST_ACK, CMD_SET_TX_POWER);
|
||||
putFlySkyFrameByte(power);
|
||||
putFlySkyFrameCmd(p_buf, FRAME_TYPE_REQUEST_ACK, CMD_SET_TX_POWER);
|
||||
putFlySkyFrameByte(p_buf, power);
|
||||
} break;
|
||||
case STATE_SET_RANGE_TEST: {
|
||||
putFlySkyFrameCmd(FRAME_TYPE_REQUEST_ACK, CMD_TEST_RANGE);
|
||||
putFlySkyFrameByte(moduleState[INTERNAL_MODULE].mode ==
|
||||
putFlySkyFrameCmd(p_buf, FRAME_TYPE_REQUEST_ACK, CMD_TEST_RANGE);
|
||||
putFlySkyFrameByte(p_buf, moduleState[INTERNAL_MODULE].mode ==
|
||||
MODULE_MODE_RANGECHECK);
|
||||
} break;
|
||||
case STATE_RANGE_TEST_RUNNING: {
|
||||
@@ -659,71 +569,70 @@ void setupPulsesAFHDS2()
|
||||
}
|
||||
} break;
|
||||
case STATE_SET_RX_PWM_PPM: {
|
||||
putFlySkyFrameCmd(FRAME_TYPE_REQUEST_ACK, CMD_SET_RX_PWM_PPM);
|
||||
putFlySkyFrameByte(AfhdsPwmMode); // 00:PWM, 01:PPM
|
||||
putFlySkyFrameCmd(p_buf, FRAME_TYPE_REQUEST_ACK, CMD_SET_RX_PWM_PPM);
|
||||
putFlySkyFrameByte(p_buf, AfhdsPwmMode); // 00:PWM, 01:PPM
|
||||
} break;
|
||||
case STATE_SET_RX_IBUS_SBUS: {
|
||||
putFlySkyFrameCmd(FRAME_TYPE_REQUEST_ACK, CMD_SET_RX_IBUS_SBUS);
|
||||
putFlySkyFrameByte(AfhdsIbusMode); // 0x00:I-BUS, 0x01:S-BUS
|
||||
putFlySkyFrameCmd(p_buf, FRAME_TYPE_REQUEST_ACK, CMD_SET_RX_IBUS_SBUS);
|
||||
putFlySkyFrameByte(p_buf, AfhdsIbusMode); // 0x00:I-BUS, 0x01:S-BUS
|
||||
} break;
|
||||
case STATE_SET_RX_FREQUENCY: {
|
||||
putFlySkyFrameCmd(FRAME_TYPE_REQUEST_ACK, CMD_SET_RX_SERVO_FREQ);
|
||||
putFlySkyFrameBytes(gRomData.rx_freq, 2);
|
||||
putFlySkyFrameCmd(p_buf, FRAME_TYPE_REQUEST_ACK, CMD_SET_RX_SERVO_FREQ);
|
||||
putFlySkyFrameBytes(p_buf, gRomData.rx_freq, 2);
|
||||
} break;
|
||||
case STATE_UPDATE_RF_PROTOCOL: {
|
||||
putFlySkyFrameCmd(FRAME_TYPE_REQUEST_ACK, CMD_SET_RF_PROTOCOL);
|
||||
putFlySkyFrameByte(
|
||||
putFlySkyFrameCmd(p_buf, FRAME_TYPE_REQUEST_ACK, CMD_SET_RF_PROTOCOL);
|
||||
putFlySkyFrameByte(p_buf,
|
||||
rf_info.protocol); // 0x00:AFHDS1 0x01:AFHDS2 0x02:AFHDS2A
|
||||
} break;
|
||||
case STATE_UPDATE_RX_FIRMWARE: {
|
||||
putFlySkyFrameCmd(FRAME_TYPE_REQUEST_ACK, CMD_UPDATE_RX_FIRMWARE);
|
||||
putFlySkyFrameCmd(p_buf, FRAME_TYPE_REQUEST_ACK, CMD_UPDATE_RX_FIRMWARE);
|
||||
} break;
|
||||
case STATE_UPDATE_RF_FIRMWARE: {
|
||||
putFlySkyFrameCmd(FRAME_TYPE_REQUEST_ACK, CMD_UPDATE_RF_FIRMWARE);
|
||||
putFlySkyFrameCmd(p_buf, FRAME_TYPE_REQUEST_ACK, CMD_UPDATE_RF_FIRMWARE);
|
||||
} break;
|
||||
case STATE_GET_RX_VERSION_INFO: {
|
||||
putFlySkyFrameCmd(FRAME_TYPE_REQUEST_ACK, CMD_GET_VERSION_INFO);
|
||||
putFlySkyFrameByte(FLYSKY_RX_FIRMWARE);
|
||||
putFlySkyFrameCmd(p_buf, FRAME_TYPE_REQUEST_ACK, CMD_GET_VERSION_INFO);
|
||||
putFlySkyFrameByte(p_buf, FLYSKY_RX_FIRMWARE);
|
||||
} break;
|
||||
case STATE_GET_FW_VERSION_INIT:
|
||||
case STATE_GET_RF_VERSION_INFO: {
|
||||
putFlySkyFrameCmd(FRAME_TYPE_REQUEST_ACK, CMD_GET_VERSION_INFO);
|
||||
putFlySkyFrameByte(FLYSKY_RF_FIRMWARE);
|
||||
putFlySkyFrameCmd(p_buf, FRAME_TYPE_REQUEST_ACK, CMD_GET_VERSION_INFO);
|
||||
putFlySkyFrameByte(p_buf, FLYSKY_RF_FIRMWARE);
|
||||
} break;
|
||||
case STATE_IDLE:
|
||||
initFlySkyArray();
|
||||
initFlySkyCRC();
|
||||
break;
|
||||
default:
|
||||
setFlyskyState(STATE_INIT);
|
||||
initFlySkyArray();
|
||||
initFlySkyCRC();
|
||||
if ((DEBUG_RF_FRAME_PRINT & TX_FRAME_ONLY)) {
|
||||
TRACE("State back to INIT\r\n");
|
||||
}
|
||||
return;
|
||||
}
|
||||
} else {
|
||||
initFlySkyArray();
|
||||
initFlySkyCRC();
|
||||
return;
|
||||
}
|
||||
} else {
|
||||
if (moduleState[INTERNAL_MODULE].mode == MODULE_MODE_BIND)
|
||||
moduleState[INTERNAL_MODULE].mode = MODULE_MODE_NORMAL;
|
||||
putFlySkySendChannelData();
|
||||
putFlySkySendChannelData(p_buf);
|
||||
}
|
||||
|
||||
putFlySkyFrameFooter();
|
||||
putFlySkyFrameFooter(p_buf);
|
||||
|
||||
if (intmodulePulsesData.flysky.state < STATE_SEND_CHANNELS) {
|
||||
// uint8_t size = intmodulePulsesData.flysky.ptr -
|
||||
// intmodulePulsesData.flysky.pulses;
|
||||
// debugFrame(intmodulePulsesData.flysky.pulses, size);
|
||||
if (_flysky_state < STATE_SEND_CHANNELS) {
|
||||
// uint8_t size = p_buf - buffer_start;
|
||||
// debugFrame(buffer_start, size);
|
||||
}
|
||||
if ((DEBUG_RF_FRAME_PRINT & TX_FRAME_ONLY)) {
|
||||
/* print each command, except channel data by interval */
|
||||
uint8_t* data = intmodulePulsesData.flysky.pulses;
|
||||
uint8_t* data = buffer_start;
|
||||
if (data[3] != CMD_SEND_CHANNEL_DATA || (set_loop_cnt++ % 100 == 0)) {
|
||||
uint8_t size = intmodulePulsesData.flysky.ptr - data;
|
||||
TRACE_NOCRLF("TX(State%0d)%0dB:", intmodulePulsesData.flysky.state, size);
|
||||
uint8_t size = p_buf - data;
|
||||
TRACE_NOCRLF("TX(State%0d)%0dB:", _flysky_state, size);
|
||||
for (int idx = 0; idx < size; idx++) {
|
||||
TRACE_NOCRLF(" %02X", data[idx]);
|
||||
}
|
||||
|
||||
@@ -23,7 +23,7 @@
|
||||
|
||||
#define INTMODULE_USART_AFHDS2_BAUDRATE 921600
|
||||
|
||||
void setupPulsesAFHDS2();
|
||||
void setupPulsesAFHDS2(uint8_t*& p_buf);
|
||||
void resetPulsesAFHDS2();
|
||||
|
||||
void processInternalFlySkyTelemetryData(uint8_t byte, uint8_t* buffer, uint8_t* len);
|
||||
|
||||
+135
-24
@@ -20,10 +20,28 @@
|
||||
*/
|
||||
|
||||
#include "opentx.h"
|
||||
#include "ghost.h"
|
||||
#include "telemetry/ghost.h"
|
||||
#include "telemetry/ghost_menu.h"
|
||||
#include "hal/module_port.h"
|
||||
#include "mixer_scheduler.h"
|
||||
|
||||
uint8_t createGhostMenuControlFrame(uint8_t * frame, int16_t * pulses)
|
||||
static uint8_t getGhostModuleAddr()
|
||||
{
|
||||
// TODO: check if asymmetric baudrate is really needed
|
||||
// as it introduces a lot of clutter
|
||||
//
|
||||
// #if SPORT_MAX_BAUDRATE < 400000
|
||||
// return g_model.moduleData[EXTERNAL_MODULE].ghost.telemetryBaudrate ==
|
||||
// GHST_TELEMETRY_RATE_400K
|
||||
// ? GHST_ADDR_MODULE_SYM
|
||||
// : GHST_ADDR_MODULE_ASYM;
|
||||
// #else
|
||||
return GHST_ADDR_MODULE_SYM;
|
||||
// #endif
|
||||
}
|
||||
|
||||
static uint8_t createGhostMenuControlFrame(uint8_t * frame, int16_t * pulses)
|
||||
{
|
||||
uint8_t * buf = frame;
|
||||
|
||||
@@ -46,7 +64,7 @@ uint8_t createGhostMenuControlFrame(uint8_t * frame, int16_t * pulses)
|
||||
}
|
||||
|
||||
// Range for pulses (channels output) is [-1024:+1024]
|
||||
uint8_t createGhostChannelsFrame(uint8_t * frame, int16_t * pulses, bool raw12bits)
|
||||
static uint8_t createGhostChannelsFrame(uint8_t * frame, int16_t * pulses, bool raw12bits)
|
||||
{
|
||||
static uint8_t lastGhostFrameId = 0;
|
||||
uint8_t ghostUpper4Offset = 0;
|
||||
@@ -88,10 +106,16 @@ uint8_t createGhostChannelsFrame(uint8_t * frame, int16_t * pulses, bool raw12bi
|
||||
uint8_t bitsavailable = 0;
|
||||
for (int i = 0; i < 4; i++) {
|
||||
uint32_t value;
|
||||
if(raw12bits) {
|
||||
value = limit(0, (1024 + (pulses[i] + 2 * PPM_CH_CENTER(i) - 2 * PPM_CENTER)) << 1, 0xFFF);
|
||||
if (raw12bits) {
|
||||
value = limit(
|
||||
0, (1024 + (pulses[i] + 2 * PPM_CH_CENTER(i) - 2 * PPM_CENTER)) << 1,
|
||||
0xFFF);
|
||||
} else {
|
||||
value = limit(0, GHST_RC_CTR_VAL_12BIT + (((pulses[i] + 2 * PPM_CH_CENTER(i) - 2 * PPM_CENTER) << 3) / 5), 2 * GHST_RC_CTR_VAL_12BIT);
|
||||
value = limit(
|
||||
0,
|
||||
GHST_RC_CTR_VAL_12BIT +
|
||||
(((pulses[i] + 2 * PPM_CH_CENTER(i) - 2 * PPM_CENTER) << 3) / 5),
|
||||
2 * GHST_RC_CTR_VAL_12BIT);
|
||||
}
|
||||
bits |= value << bitsavailable;
|
||||
bitsavailable += GHST_CH_BITS_12;
|
||||
@@ -106,10 +130,20 @@ uint8_t createGhostChannelsFrame(uint8_t * frame, int16_t * pulses, bool raw12bi
|
||||
for (int i = 4; i < 8; ++i) {
|
||||
uint8_t channelIndex = i + ghostUpper4Offset;
|
||||
uint8_t value;
|
||||
if(raw12bits) {
|
||||
value = limit(0, 128 + ((pulses[channelIndex] + 2 * PPM_CH_CENTER(channelIndex) - 2 * PPM_CENTER) >> 3), 0xFF);
|
||||
if (raw12bits) {
|
||||
value = limit(0,
|
||||
128 + ((pulses[channelIndex] +
|
||||
2 * PPM_CH_CENTER(channelIndex) - 2 * PPM_CENTER) >>
|
||||
3),
|
||||
0xFF);
|
||||
} else {
|
||||
value = limit(0, GHST_RC_CTR_VAL_8BIT + (((pulses[channelIndex] + 2 * PPM_CH_CENTER(channelIndex) - 2 * PPM_CENTER) >> 1) / 5), 2 * GHST_RC_CTR_VAL_8BIT);
|
||||
value = limit(0,
|
||||
GHST_RC_CTR_VAL_8BIT +
|
||||
(((pulses[channelIndex] +
|
||||
2 * PPM_CH_CENTER(channelIndex) - 2 * PPM_CENTER) >>
|
||||
1) /
|
||||
5),
|
||||
2 * GHST_RC_CTR_VAL_8BIT);
|
||||
}
|
||||
*buf++ = value;
|
||||
}
|
||||
@@ -120,25 +154,102 @@ uint8_t createGhostChannelsFrame(uint8_t * frame, int16_t * pulses, bool raw12bi
|
||||
return buf - frame;
|
||||
}
|
||||
|
||||
void setupPulsesGhost()
|
||||
static const etx_serial_init ghostSerialParams = {
|
||||
.baudrate = GHOST_BAUDRATE,
|
||||
.encoding = ETX_Encoding_8N1,
|
||||
.direction = ETX_Dir_TX_RX,
|
||||
.polarity = ETX_Pol_Normal,
|
||||
};
|
||||
|
||||
static void* ghostInit(uint8_t module)
|
||||
{
|
||||
if (telemetryProtocol == PROTOCOL_TELEMETRY_GHOST) {
|
||||
auto mod_st = modulePortInitSerial(module, ETX_MOD_PORT_SPORT, &ghostSerialParams);
|
||||
if (!mod_st) return nullptr;
|
||||
|
||||
auto &module = g_model.moduleData[EXTERNAL_MODULE];
|
||||
auto *p_data = &extmodulePulsesData.ghost;
|
||||
mixerSchedulerSetPeriod(module, GHOST_PERIOD);
|
||||
return mod_st;
|
||||
}
|
||||
|
||||
#if defined(LUA)
|
||||
if (outputTelemetryBuffer.destination == TELEMETRY_ENDPOINT_SPORT) {
|
||||
memcpy(p_data->pulses, outputTelemetryBuffer.data, outputTelemetryBuffer.size);
|
||||
p_data->length = outputTelemetryBuffer.size;
|
||||
outputTelemetryBuffer.reset();
|
||||
} else
|
||||
#endif
|
||||
if (moduleState[EXTERNAL_MODULE].counter == GHST_MENU_CONTROL) {
|
||||
p_data->length = createGhostMenuControlFrame(p_data->pulses, &channelOutputs[module.channelsStart]);
|
||||
} else {
|
||||
p_data->length = createGhostChannelsFrame(p_data->pulses, &channelOutputs[module.channelsStart], module.ghost.raw12bits);
|
||||
static void ghostDeInit(void* ctx)
|
||||
{
|
||||
auto mod_st = (etx_module_state_t*)ctx;
|
||||
modulePortDeInit(mod_st);
|
||||
}
|
||||
|
||||
static void ghostSendPulses(void* ctx, uint8_t* buffer, int16_t* channels, uint8_t nChannels)
|
||||
{
|
||||
auto mod_st = (etx_module_state_t*)ctx;
|
||||
auto module = modulePortGetModule(mod_st);
|
||||
|
||||
ModuleSyncStatus& status = getModuleSyncStatus(module);
|
||||
if (status.isValid()) {
|
||||
mixerSchedulerSetPeriod(module, status.getAdjustedRefreshRate());
|
||||
} else {
|
||||
mixerSchedulerSetPeriod(module, GHOST_PERIOD);
|
||||
}
|
||||
|
||||
const auto& mod_cfg = g_model.moduleData[module].ghost;
|
||||
auto p_data = buffer;
|
||||
|
||||
#if defined(LUA)
|
||||
if (outputTelemetryBuffer.destination == TELEMETRY_ENDPOINT_SPORT) {
|
||||
auto len = outputTelemetryBuffer.size;
|
||||
auto f_data = outputTelemetryBuffer.data;
|
||||
while (len >= 12) { // type(1B) + payload(10B) + crc(1B)
|
||||
// insert address byte
|
||||
*p_data++ = getGhostModuleAddr();
|
||||
// and length
|
||||
*p_data++ = 12;
|
||||
memcpy(p_data, f_data, 12);
|
||||
p_data += 12; f_data += 12;
|
||||
len -= 12;
|
||||
}
|
||||
outputTelemetryBuffer.reset();
|
||||
} else
|
||||
#endif
|
||||
if (moduleState[module].counter == GHST_MENU_CONTROL) {
|
||||
p_data += createGhostMenuControlFrame(p_data, channels);
|
||||
} else {
|
||||
p_data += createGhostChannelsFrame(p_data, channels, mod_cfg.raw12bits);
|
||||
}
|
||||
|
||||
moduleState[module].counter = GHST_FRAME_CHANNEL;
|
||||
|
||||
auto drv = modulePortGetSerialDrv(mod_st->tx);
|
||||
auto drv_ctx = modulePortGetCtx(mod_st->tx);
|
||||
drv->sendBuffer(drv_ctx, buffer, p_data - buffer);
|
||||
}
|
||||
|
||||
static void ghostProcessData(void* ctx, uint8_t data, uint8_t* buffer, uint8_t* len)
|
||||
{
|
||||
if (*len == 0 && data != GHST_ADDR_RADIO) {
|
||||
TRACE("[GH] address 0x%02X error", data);
|
||||
return;
|
||||
}
|
||||
|
||||
if (*len < TELEMETRY_RX_PACKET_SIZE) {
|
||||
buffer[(*len)++] = data;
|
||||
}
|
||||
else {
|
||||
TRACE("[GH] array size %d error", *len);
|
||||
*len = 0;
|
||||
}
|
||||
|
||||
if (*len > 4) {
|
||||
uint8_t length = buffer[1];
|
||||
if (length + 2 == *len) {
|
||||
auto mod_st = (etx_module_state_t*)ctx;
|
||||
auto module = modulePortGetModule(mod_st);
|
||||
processGhostTelemetryFrame(module, buffer, *len);
|
||||
*len = 0;
|
||||
}
|
||||
moduleState[EXTERNAL_MODULE].counter = GHST_FRAME_CHANNEL;
|
||||
}
|
||||
}
|
||||
|
||||
const etx_proto_driver_t GhostDriver = {
|
||||
.protocol = PROTOCOL_CHANNELS_GHOST,
|
||||
.init = ghostInit,
|
||||
.deinit = ghostDeInit,
|
||||
.sendPulses = ghostSendPulses,
|
||||
.processData = ghostProcessData,
|
||||
};
|
||||
|
||||
+2
-5
@@ -21,9 +21,6 @@
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <stdint.h>
|
||||
#include "hal/serial_driver.h"
|
||||
#include "hal/module_driver.h"
|
||||
|
||||
#define INTMODULE_USART_IRQ_PRIORITY 6
|
||||
|
||||
extern const etx_serial_driver_t IntmoduleSerialDriver;
|
||||
extern const etx_proto_driver_t GhostDriver;
|
||||
@@ -43,6 +43,10 @@ extern uint32_t NV14internalModuleFwVersion;
|
||||
#define CROSSFIRE_CHANNELS_COUNT 16
|
||||
#define GHOST_CHANNELS_COUNT 16
|
||||
|
||||
#define IS_NATIVE_FRSKY_PROTOCOL(module) \
|
||||
((moduleState[module].protocol == PROTOCOL_CHANNELS_PXX1) || \
|
||||
(moduleState[module].protocol == PROTOCOL_CHANNELS_PXX2))
|
||||
|
||||
#if defined (MULTIMODULE)
|
||||
#define IS_D16_MULTI(module) \
|
||||
((g_model.moduleData[module].multi.rfProtocol == MODULE_SUBTYPE_MULTI_FRSKYX) || \
|
||||
@@ -71,32 +75,15 @@ extern uint32_t NV14internalModuleFwVersion;
|
||||
(g_model.moduleData[module].multi.rfProtocol == \
|
||||
MODULE_SUBTYPE_MULTI_DSM_RX))
|
||||
|
||||
#if defined(HARDWARE_INTERNAL_MODULE)
|
||||
#define IS_FRSKY_SPORT_PROTOCOL() \
|
||||
(telemetryProtocol == PROTOCOL_TELEMETRY_FRSKY_SPORT || \
|
||||
(telemetryProtocol == PROTOCOL_TELEMETRY_MULTIMODULE && \
|
||||
(IS_D16_MULTI(INTERNAL_MODULE) || IS_D16_MULTI(EXTERNAL_MODULE) || \
|
||||
IS_R9_MULTI(INTERNAL_MODULE) || IS_R9_MULTI(EXTERNAL_MODULE))))
|
||||
#else
|
||||
#define IS_FRSKY_SPORT_PROTOCOL() \
|
||||
(telemetryProtocol == PROTOCOL_TELEMETRY_FRSKY_SPORT || \
|
||||
(telemetryProtocol == PROTOCOL_TELEMETRY_MULTIMODULE && \
|
||||
(IS_D16_MULTI(EXTERNAL_MODULE) || IS_R9_MULTI(EXTERNAL_MODULE))))
|
||||
#endif
|
||||
|
||||
#else
|
||||
#define IS_D16_MULTI(module) false
|
||||
#define IS_R9_MULTI(module) false
|
||||
#define IS_HOTT_MULTI(module) false
|
||||
#define IS_CONFIG_MULTI(module) false
|
||||
#define IS_DSM_MULTI(module) false
|
||||
#define IS_FRSKY_SPORT_PROTOCOL() (telemetryProtocol == PROTOCOL_TELEMETRY_FRSKY_SPORT)
|
||||
#define IS_RX_MULTI(module) false
|
||||
#endif
|
||||
|
||||
#define IS_SPEKTRUM_PROTOCOL() (telemetryProtocol == PROTOCOL_TELEMETRY_SPEKTRUM)
|
||||
|
||||
|
||||
#if defined(MULTIMODULE)
|
||||
// When using packed, the pointer in here end up not being aligned, which clang and gcc complain about
|
||||
// Keep the order of the fields that the so that the size stays small
|
||||
@@ -726,6 +713,7 @@ inline bool isTelemAllowedOnBind(uint8_t moduleIndex)
|
||||
return false;
|
||||
#endif
|
||||
|
||||
#if defined(HARDWARE_EXTERNAL_MODULE)
|
||||
if (g_model.moduleData[EXTERNAL_MODULE].type == MODULE_TYPE_R9M_LITE_PXX1) {
|
||||
if (isModuleR9M_LBT(EXTERNAL_MODULE))
|
||||
return g_model.moduleData[EXTERNAL_MODULE].pxx.power < R9M_LITE_LBT_POWER_100_16CH_NOTELEM;
|
||||
@@ -739,6 +727,7 @@ inline bool isTelemAllowedOnBind(uint8_t moduleIndex)
|
||||
else
|
||||
return true;
|
||||
}
|
||||
#endif
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
+156
-153
@@ -41,37 +41,28 @@
|
||||
#define MULTI_FAILSAFE 0x01
|
||||
#define MULTI_DATA 0x02
|
||||
|
||||
static void sendFrameProtocolHeader(uint8_t moduleIdx, bool failsafe);
|
||||
void sendChannels(uint8_t moduleIdx);
|
||||
static void sendD16BindOption(uint8_t moduleIdx);
|
||||
static void sendFrameProtocolHeader(uint8_t*& p_buf, uint8_t module, bool failsafe);
|
||||
static void sendChannels(uint8_t*& p_buf, uint8_t module);
|
||||
static void sendD16BindOption(uint8_t*& p_buf, uint8_t module);
|
||||
#if defined(LUA)
|
||||
static void sendSport(uint8_t moduleIdx);
|
||||
static void sendHott(uint8_t moduleIdx);
|
||||
static void sendConfig(uint8_t moduleIdx);
|
||||
static void sendDSM(uint8_t moduleIdx);
|
||||
static void sendSport(uint8_t*& p_buf, uint8_t module);
|
||||
static void sendHott(uint8_t*& p_buf, uint8_t module);
|
||||
static void sendConfig(uint8_t*& p_buf, uint8_t module);
|
||||
static void sendDSM(uint8_t*& p_buf, uint8_t module);
|
||||
#endif
|
||||
|
||||
#if defined(INTMODULE_USART) && defined(INTERNAL_MODULE_MULTI)
|
||||
#include "intmodule_serial_driver.h"
|
||||
#include "hal/module_port.h"
|
||||
|
||||
etx_serial_init multiSerialInitParams = {
|
||||
.baudrate = MULTIMODULE_BAUDRATE,
|
||||
.parity = ETX_Parity_Even,
|
||||
.stop_bits = ETX_StopBits_Two,
|
||||
.word_length = ETX_WordLength_9,
|
||||
.rx_enable = true,
|
||||
.encoding = ETX_Encoding_8E2,
|
||||
.direction = ETX_Dir_TX,
|
||||
.polarity = ETX_Pol_Normal,
|
||||
};
|
||||
#endif
|
||||
|
||||
static void sendMulti(uint8_t moduleIdx, uint8_t b)
|
||||
static inline void sendMulti(uint8_t*& p_buf, uint8_t b)
|
||||
{
|
||||
#if defined(INTERNAL_MODULE_MULTI)
|
||||
if (moduleIdx == INTERNAL_MODULE) {
|
||||
intmodulePulsesData.multi.sendByte(b);
|
||||
}
|
||||
else
|
||||
#endif
|
||||
sendByteSbus(b);
|
||||
*p_buf++ = b;
|
||||
}
|
||||
|
||||
static void updateMultiSync(uint8_t module)
|
||||
@@ -88,7 +79,7 @@ static void updateMultiSync(uint8_t module)
|
||||
}
|
||||
}
|
||||
|
||||
static void sendFailsafeChannels(uint8_t moduleIdx)
|
||||
static void sendFailsafeChannels(uint8_t*& p_buf, uint8_t module)
|
||||
{
|
||||
uint32_t bits = 0;
|
||||
uint8_t bitsavailable = 0;
|
||||
@@ -97,16 +88,16 @@ static void sendFailsafeChannels(uint8_t moduleIdx)
|
||||
int16_t failsafeValue = g_model.failsafeChannels[i];
|
||||
int pulseValue;
|
||||
|
||||
if (g_model.moduleData[moduleIdx].failsafeMode == FAILSAFE_HOLD ||
|
||||
if (g_model.moduleData[module].failsafeMode == FAILSAFE_HOLD ||
|
||||
failsafeValue == FAILSAFE_CHANNEL_HOLD) {
|
||||
pulseValue = 2047;
|
||||
} else if (g_model.moduleData[moduleIdx].failsafeMode ==
|
||||
} else if (g_model.moduleData[module].failsafeMode ==
|
||||
FAILSAFE_NOPULSES ||
|
||||
failsafeValue == FAILSAFE_CHANNEL_NOPULSE) {
|
||||
pulseValue = 0;
|
||||
} else {
|
||||
failsafeValue +=
|
||||
2 * PPM_CH_CENTER(g_model.moduleData[moduleIdx].channelsStart + i) -
|
||||
2 * PPM_CH_CENTER(g_model.moduleData[module].channelsStart + i) -
|
||||
2 * PPM_CENTER;
|
||||
pulseValue = limit(1, (failsafeValue * 800 / 1000) + 1024, 2046);
|
||||
}
|
||||
@@ -114,14 +105,14 @@ static void sendFailsafeChannels(uint8_t moduleIdx)
|
||||
bits |= pulseValue << bitsavailable;
|
||||
bitsavailable += MULTI_CHAN_BITS;
|
||||
while (bitsavailable >= 8) {
|
||||
sendMulti(moduleIdx, (uint8_t) (bits & 0xff));
|
||||
sendMulti(p_buf, (uint8_t) (bits & 0xff));
|
||||
bits >>= 8;
|
||||
bitsavailable -= 8;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void setupPulsesMulti(uint8_t moduleIdx)
|
||||
static void setupPulsesMulti(uint8_t*& p_buf, uint8_t module)
|
||||
{
|
||||
static int counter[2] = {0,0}; //TODO
|
||||
static uint8_t invert[2] = {0x00, //internal
|
||||
@@ -133,165 +124,177 @@ void setupPulsesMulti(uint8_t moduleIdx)
|
||||
};
|
||||
uint8_t type=MULTI_NORMAL;
|
||||
|
||||
updateMultiSync(moduleIdx);
|
||||
updateMultiSync(module);
|
||||
|
||||
// not scanning protos && not spectrum analyser
|
||||
if (getModuleMode(moduleIdx) == MODULE_MODE_NORMAL) {
|
||||
if (getModuleMode(module) == MODULE_MODE_NORMAL) {
|
||||
// Failsafe packets
|
||||
if (counter[moduleIdx] % 1000 == 0 &&
|
||||
g_model.moduleData[moduleIdx].failsafeMode != FAILSAFE_NOT_SET &&
|
||||
g_model.moduleData[moduleIdx].failsafeMode != FAILSAFE_RECEIVER) {
|
||||
if (counter[module] % 1000 == 0 &&
|
||||
g_model.moduleData[module].failsafeMode != FAILSAFE_NOT_SET &&
|
||||
g_model.moduleData[module].failsafeMode != FAILSAFE_RECEIVER) {
|
||||
type |= MULTI_FAILSAFE;
|
||||
}
|
||||
|
||||
counter[moduleIdx]++;
|
||||
counter[module]++;
|
||||
}
|
||||
|
||||
// Invert telemetry if needed
|
||||
if (invert[moduleIdx] & 0x80 &&
|
||||
!g_model.moduleData[moduleIdx].multi.disableTelemetry) {
|
||||
if (getMultiModuleStatus(moduleIdx).isValid()) {
|
||||
invert[moduleIdx] &= 0x08; // Telemetry received, stop searching
|
||||
} else if (counter[moduleIdx] % 100 == 0) {
|
||||
invert[moduleIdx] ^= 0x08; // Try inverting telemetry
|
||||
if (invert[module] & 0x80 &&
|
||||
!g_model.moduleData[module].multi.disableTelemetry) {
|
||||
if (getMultiModuleStatus(module).isValid()) {
|
||||
invert[module] &= 0x08; // Telemetry received, stop searching
|
||||
} else if (counter[module] % 100 == 0) {
|
||||
invert[module] ^= 0x08; // Try inverting telemetry
|
||||
}
|
||||
}
|
||||
|
||||
// Send header
|
||||
sendFrameProtocolHeader(moduleIdx, type & MULTI_FAILSAFE);
|
||||
sendFrameProtocolHeader(p_buf, module, type & MULTI_FAILSAFE);
|
||||
|
||||
// Send channels
|
||||
if (type & MULTI_FAILSAFE)
|
||||
sendFailsafeChannels(moduleIdx);
|
||||
sendFailsafeChannels(p_buf, module);
|
||||
else
|
||||
sendChannels(moduleIdx);
|
||||
sendChannels(p_buf, module);
|
||||
|
||||
// Multi V1.3.X.X -> Send byte 26, Protocol (bits 7 & 6), RX_Num (bits 5 & 4), invert, not used, disable telemetry, disable mapping
|
||||
if ((moduleState[moduleIdx].mode == MODULE_MODE_SPECTRUM_ANALYSER)
|
||||
if (moduleState[module].mode == MODULE_MODE_SPECTRUM_ANALYSER
|
||||
#if defined(MULTI_PROTOLIST)
|
||||
|| (moduleState[moduleIdx].mode == MODULE_MODE_GET_HARDWARE_INFO)
|
||||
|| moduleState[module].mode == MODULE_MODE_GET_HARDWARE_INFO
|
||||
#endif
|
||||
) {
|
||||
sendMulti(moduleIdx, invert[moduleIdx] & 0x08);
|
||||
sendMulti(p_buf, invert[module] & 0x08);
|
||||
}
|
||||
else {
|
||||
sendMulti(moduleIdx, (uint8_t) (((g_model.moduleData[moduleIdx].multi.rfProtocol + 3) & 0xC0)
|
||||
| (g_model.header.modelId[moduleIdx] & 0x30)
|
||||
| (invert[moduleIdx] & 0x08)
|
||||
//| 0x04 // Future use
|
||||
| (g_model.moduleData[moduleIdx].multi.disableTelemetry << 1)
|
||||
| g_model.moduleData[moduleIdx].multi.disableMapping));
|
||||
sendMulti(p_buf, (uint8_t) (((g_model.moduleData[module].multi.rfProtocol + 3) & 0xC0)
|
||||
| (g_model.header.modelId[module] & 0x30)
|
||||
| (invert[module] & 0x08)
|
||||
//| 0x04 // Future use
|
||||
| (g_model.moduleData[module].multi.disableTelemetry << 1)
|
||||
| g_model.moduleData[module].multi.disableMapping));
|
||||
}
|
||||
|
||||
// Multi V1.3.X.X -> Send protocol additional data: max 9 bytes
|
||||
if (getMultiModuleStatus(moduleIdx).isValid()) {
|
||||
MultiModuleStatus &status = getMultiModuleStatus(moduleIdx);
|
||||
if (getMultiModuleStatus(module).isValid()) {
|
||||
MultiModuleStatus &status = getMultiModuleStatus(module);
|
||||
if (status.minor >= 3 && !(status.flags & 0x80)) { //Version 1.3.x.x or more and Buffer not full
|
||||
if ((IS_D16_MULTI(moduleIdx) || IS_R9_MULTI(moduleIdx)) && moduleState[moduleIdx].mode == MODULE_MODE_BIND) {
|
||||
sendD16BindOption(moduleIdx);//1 byte of additional data
|
||||
if ((IS_D16_MULTI(module) || IS_R9_MULTI(module)) && moduleState[module].mode == MODULE_MODE_BIND) {
|
||||
sendD16BindOption(p_buf, module);//1 byte of additional data
|
||||
}
|
||||
#if defined(LUA)
|
||||
// SPort send
|
||||
if (IS_D16_MULTI(moduleIdx) && outputTelemetryBuffer.destination == TELEMETRY_ENDPOINT_SPORT && outputTelemetryBuffer.size) {
|
||||
sendSport(moduleIdx); //8 bytes of additional data
|
||||
if (IS_D16_MULTI(module) && outputTelemetryBuffer.destination == TELEMETRY_ENDPOINT_SPORT && outputTelemetryBuffer.size) {
|
||||
sendSport(p_buf, module); //8 bytes of additional data
|
||||
}
|
||||
else if (IS_HOTT_MULTI(moduleIdx)) {
|
||||
sendHott(moduleIdx); //1 byte of additional data
|
||||
else if (IS_HOTT_MULTI(module)) {
|
||||
sendHott(p_buf, module); //1 byte of additional data
|
||||
}
|
||||
else if (IS_CONFIG_MULTI(moduleIdx)) {
|
||||
sendConfig(moduleIdx); //7 bytes of additional data
|
||||
else if (IS_CONFIG_MULTI(module)) {
|
||||
sendConfig(p_buf, module); //7 bytes of additional data
|
||||
}
|
||||
else if (IS_DSM_MULTI(moduleIdx)) {
|
||||
sendDSM(moduleIdx); //7 bytes of additional data
|
||||
else if (IS_DSM_MULTI(module)) {
|
||||
sendDSM(p_buf, module); //7 bytes of additional data
|
||||
}
|
||||
#endif
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void setupPulsesMultiExternalModule()
|
||||
{
|
||||
extmodulePulsesData.dsm2.index = 0;
|
||||
extmodulePulsesData.dsm2.ptr = extmodulePulsesData.dsm2.pulses;
|
||||
|
||||
setupPulsesMulti(EXTERNAL_MODULE);
|
||||
putDsm2Flush();
|
||||
}
|
||||
|
||||
#if defined(INTERNAL_MODULE_MULTI)
|
||||
static void* multiInit(uint8_t module)
|
||||
{
|
||||
(void)module;
|
||||
|
||||
// serial port setup
|
||||
intmodulePulsesData.multi.initFrame();
|
||||
void* uart_ctx = IntmoduleSerialDriver.init(&multiSerialInitParams);
|
||||
etx_module_state_t* mod_st = nullptr;
|
||||
etx_serial_init cfg(multiSerialInitParams);
|
||||
|
||||
#if defined(INTERNAL_MODULE_MULTI)
|
||||
if (module == INTERNAL_MODULE) {
|
||||
// serial port setup
|
||||
// TODO: error handling
|
||||
cfg.direction = ETX_Dir_TX_RX;
|
||||
mod_st = modulePortInitSerial(module, ETX_MOD_PORT_UART, &cfg);
|
||||
}
|
||||
#endif
|
||||
|
||||
#if defined(HARDWARE_EXTERNAL_MODULE)
|
||||
if (module == EXTERNAL_MODULE) {
|
||||
// serial port setup
|
||||
cfg.direction = ETX_Dir_TX;
|
||||
cfg.polarity = ETX_Pol_Inverted;
|
||||
mod_st = modulePortInitSerial(module, ETX_MOD_PORT_UART, &cfg);
|
||||
|
||||
if (!mod_st) {
|
||||
cfg.polarity = ETX_Pol_Normal;
|
||||
mod_st = modulePortInitSerial(module, ETX_MOD_PORT_SOFT_INV, &cfg);
|
||||
}
|
||||
|
||||
// no TX port: bail out
|
||||
if (!mod_st) return nullptr;
|
||||
|
||||
// Init S.PORT RX channel
|
||||
cfg.direction = ETX_Dir_RX;
|
||||
cfg.polarity = ETX_Pol_Normal;
|
||||
modulePortInitSerial(module, ETX_MOD_PORT_SPORT, &cfg);
|
||||
}
|
||||
#endif
|
||||
|
||||
if (!mod_st) return nullptr;
|
||||
|
||||
// mixer setup
|
||||
mixerSchedulerSetPeriod(INTERNAL_MODULE, MULTIMODULE_PERIOD);
|
||||
INTERNAL_MODULE_ON();
|
||||
mixerSchedulerSetPeriod(module, MULTIMODULE_PERIOD);
|
||||
|
||||
// reset status
|
||||
getMultiModuleStatus(INTERNAL_MODULE).failsafeChecked = false;
|
||||
getMultiModuleStatus(INTERNAL_MODULE).flags = 0;
|
||||
getMultiModuleStatus(module).failsafeChecked = false;
|
||||
getMultiModuleStatus(module).flags = 0;
|
||||
|
||||
#if defined(MULTI_PROTOLIST)
|
||||
TRACE("enablePulsesInternalModule(): trigger scan");
|
||||
MultiRfProtocols::instance(INTERNAL_MODULE)->triggerScan();
|
||||
TRACE("counter = %d", moduleState[INTERNAL_MODULE].counter);
|
||||
MultiRfProtocols::instance(module)->triggerScan();
|
||||
#endif
|
||||
|
||||
return uart_ctx;
|
||||
return mod_st;
|
||||
}
|
||||
|
||||
static void multiDeInit(void* context)
|
||||
static void multiDeInit(void* ctx)
|
||||
{
|
||||
INTERNAL_MODULE_OFF();
|
||||
mixerSchedulerSetPeriod(INTERNAL_MODULE, 0);
|
||||
IntmoduleSerialDriver.deinit(context);
|
||||
auto mod_st = (etx_module_state_t*)ctx;
|
||||
modulePortDeInit(mod_st);
|
||||
}
|
||||
|
||||
static void multiSetupPulses(void* context, int16_t* channels, uint8_t nChannels)
|
||||
static void multiSendPulses(void* ctx, uint8_t* buffer, int16_t* channels, uint8_t nChannels)
|
||||
{
|
||||
(void)context;
|
||||
// TODO:
|
||||
(void)channels;
|
||||
(void)nChannels;
|
||||
|
||||
intmodulePulsesData.multi.initFrame();
|
||||
setupPulsesMulti(INTERNAL_MODULE);
|
||||
|
||||
auto mod_st = (etx_module_state_t*)ctx;
|
||||
auto module = modulePortGetModule(mod_st);
|
||||
|
||||
auto data = buffer;
|
||||
setupPulsesMulti(data, module);
|
||||
|
||||
auto drv = modulePortGetSerialDrv(mod_st->tx);
|
||||
auto drv_ctx = modulePortGetCtx(mod_st->tx);
|
||||
drv->sendBuffer(drv_ctx, buffer, data - buffer);
|
||||
}
|
||||
|
||||
static void multiSendPulses(void* context)
|
||||
static void multiProcessData(void* ctx, uint8_t data, uint8_t* buffer, uint8_t* len)
|
||||
{
|
||||
IntmoduleSerialDriver.sendBuffer(context, intmodulePulsesData.multi.getData(),
|
||||
intmodulePulsesData.multi.getSize());
|
||||
}
|
||||
auto mod_st = (etx_module_state_t*)ctx;
|
||||
auto module = modulePortGetModule(mod_st);
|
||||
|
||||
static int multiGetByte(void* context, uint8_t* data)
|
||||
{
|
||||
return IntmoduleSerialDriver.getByte(context, data);
|
||||
}
|
||||
|
||||
static void multiProcessData(void* context, uint8_t data, uint8_t* buffer, uint8_t* len)
|
||||
{
|
||||
processMultiTelemetryData(data, INTERNAL_MODULE);
|
||||
processMultiTelemetryData(data, module);
|
||||
}
|
||||
|
||||
#include "hal/module_driver.h"
|
||||
|
||||
const etx_module_driver_t MultiInternalDriver = {
|
||||
const etx_proto_driver_t MultiDriver = {
|
||||
.protocol = PROTOCOL_CHANNELS_MULTIMODULE,
|
||||
.init = multiInit,
|
||||
.deinit = multiDeInit,
|
||||
.setupPulses = multiSetupPulses,
|
||||
.sendPulses = multiSendPulses,
|
||||
.getByte = multiGetByte,
|
||||
.processData = multiProcessData,
|
||||
};
|
||||
#endif
|
||||
|
||||
void sendChannels(uint8_t moduleIdx)
|
||||
static void sendChannels(uint8_t*& p_buf, uint8_t module)
|
||||
{
|
||||
uint32_t bits = 0;
|
||||
uint8_t bitsavailable = 0;
|
||||
@@ -300,7 +303,7 @@ void sendChannels(uint8_t moduleIdx)
|
||||
// Range for pulses (channelsOutputs) is [-1024:+1024] for [-100%;100%]
|
||||
// Multi uses [204;1843] as [-100%;100%]
|
||||
for (int i = 0; i < MULTI_CHANS; i++) {
|
||||
int channel = g_model.moduleData[moduleIdx].channelsStart + i;
|
||||
int channel = g_model.moduleData[module].channelsStart + i;
|
||||
int value = channelOutputs[channel] + 2 * PPM_CH_CENTER(channel) - 2 * PPM_CENTER;
|
||||
|
||||
// Scale to 80%
|
||||
@@ -310,42 +313,42 @@ void sendChannels(uint8_t moduleIdx)
|
||||
bits |= value << bitsavailable;
|
||||
bitsavailable += MULTI_CHAN_BITS;
|
||||
while (bitsavailable >= 8) {
|
||||
sendMulti(moduleIdx, (uint8_t) (bits & 0xff));
|
||||
sendMulti(p_buf, (uint8_t) (bits & 0xff));
|
||||
bits >>= 8;
|
||||
bitsavailable -= 8;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void sendFrameProtocolHeader(uint8_t moduleIdx, bool failsafe)
|
||||
void sendFrameProtocolHeader(uint8_t*& p_buf, uint8_t module, bool failsafe)
|
||||
{// byte 1+2, protocol information
|
||||
|
||||
// Our enumeration starts at 0
|
||||
int type = g_model.moduleData[moduleIdx].multi.rfProtocol + 1;
|
||||
int subtype = g_model.moduleData[moduleIdx].subType;
|
||||
int8_t optionValue = g_model.moduleData[moduleIdx].multi.optionValue;
|
||||
int type = g_model.moduleData[module].multi.rfProtocol + 1;
|
||||
int subtype = g_model.moduleData[module].subType;
|
||||
int8_t optionValue = g_model.moduleData[module].multi.optionValue;
|
||||
|
||||
uint8_t protoByte = 0;
|
||||
|
||||
uint8_t moduleMode = getModuleMode(moduleIdx);
|
||||
uint8_t moduleMode = getModuleMode(module);
|
||||
if (moduleMode == MODULE_MODE_SPECTRUM_ANALYSER) {
|
||||
sendMulti(moduleIdx, (uint8_t) 0x54); // Header byte
|
||||
sendMulti(moduleIdx, (uint8_t) 54); // Spectrum custom protocol
|
||||
sendMulti(moduleIdx, (uint8_t) 0);
|
||||
sendMulti(moduleIdx, (uint8_t) 0);
|
||||
sendMulti(p_buf, (uint8_t) 0x54); // Header byte
|
||||
sendMulti(p_buf, (uint8_t) 54); // Spectrum custom protocol
|
||||
sendMulti(p_buf, (uint8_t) 0);
|
||||
sendMulti(p_buf, (uint8_t) 0);
|
||||
return;
|
||||
}
|
||||
|
||||
#if defined(MULTI_PROTOLIST)
|
||||
if (moduleMode == MODULE_MODE_GET_HARDWARE_INFO) {
|
||||
sendMulti(moduleIdx, (uint8_t) 0x55); // Header byte
|
||||
sendMulti(moduleIdx, (uint8_t) 0); // PROTOLIST custom protocol
|
||||
sendMulti(moduleIdx, (uint8_t) 0);
|
||||
sendMulti(p_buf, (uint8_t) 0x55); // Header byte
|
||||
sendMulti(p_buf, (uint8_t) 0); // PROTOLIST custom protocol
|
||||
sendMulti(p_buf, (uint8_t) 0);
|
||||
|
||||
// proto array item
|
||||
uint8_t protoIdx = MultiRfProtocols::instance(moduleIdx)->getScanProto();
|
||||
uint8_t protoIdx = MultiRfProtocols::instance(module)->getScanProto();
|
||||
TRACE("scan [%d]", protoIdx);
|
||||
sendMulti(moduleIdx, protoIdx);
|
||||
sendMulti(p_buf, protoIdx);
|
||||
return;
|
||||
}
|
||||
#endif
|
||||
@@ -362,19 +365,19 @@ void sendFrameProtocolHeader(uint8_t moduleIdx, bool failsafe)
|
||||
optionValue = 0x80; // Max throw
|
||||
else
|
||||
optionValue = 0x00;
|
||||
if (g_model.moduleData[moduleIdx].multi.optionValue & 0x02)
|
||||
if (g_model.moduleData[module].multi.optionValue & 0x02)
|
||||
optionValue |= 0x40; // 11ms servo refresh
|
||||
optionValue |= sentModuleChannels(moduleIdx); //add number of channels
|
||||
optionValue |= sentModuleChannels(module); //add number of channels
|
||||
}
|
||||
|
||||
// Set the highest bit of option byte in AFHDS2A protocol to instruct MULTI to passthrough telemetry bytes instead
|
||||
// of sending Frsky D telemetry
|
||||
if (g_model.moduleData[moduleIdx].multi.rfProtocol == MODULE_SUBTYPE_MULTI_FS_AFHDS2A)
|
||||
if (g_model.moduleData[module].multi.rfProtocol == MODULE_SUBTYPE_MULTI_FS_AFHDS2A)
|
||||
optionValue = optionValue | 0x80;
|
||||
|
||||
// For custom protocol send unmodified type byte
|
||||
if (g_model.moduleData[moduleIdx].multi.rfProtocol == MM_RF_CUSTOM_SELECTED)
|
||||
type = g_model.moduleData[moduleIdx].multi.rfProtocol;
|
||||
if (g_model.moduleData[module].multi.rfProtocol == MM_RF_CUSTOM_SELECTED)
|
||||
type = g_model.moduleData[module].multi.rfProtocol;
|
||||
|
||||
uint8_t headerByte = 0x55;
|
||||
// header, byte 0, 0x55 for proto 0-31, 0x54 for proto 32-63
|
||||
@@ -384,34 +387,34 @@ void sendFrameProtocolHeader(uint8_t moduleIdx, bool failsafe)
|
||||
if (failsafe)
|
||||
headerByte |= 0x02;
|
||||
|
||||
sendMulti(moduleIdx, headerByte);
|
||||
sendMulti(p_buf, headerByte);
|
||||
|
||||
// protocol byte
|
||||
protoByte |= (type & 0x1f);
|
||||
if (g_model.moduleData[moduleIdx].multi.rfProtocol != MODULE_SUBTYPE_MULTI_DSM2)
|
||||
protoByte |= (g_model.moduleData[moduleIdx].multi.autoBindMode << 6);
|
||||
if (g_model.moduleData[module].multi.rfProtocol != MODULE_SUBTYPE_MULTI_DSM2)
|
||||
protoByte |= (g_model.moduleData[module].multi.autoBindMode << 6);
|
||||
|
||||
sendMulti(moduleIdx, protoByte);
|
||||
sendMulti(p_buf, protoByte);
|
||||
|
||||
// byte 2, subtype, powermode, model id
|
||||
sendMulti(moduleIdx, (uint8_t) ((g_model.header.modelId[moduleIdx] & 0x0f)
|
||||
sendMulti(p_buf, (uint8_t) ((g_model.header.modelId[module] & 0x0f)
|
||||
| ((subtype & 0x7) << 4)
|
||||
| (g_model.moduleData[moduleIdx].multi.lowPowerMode << 7))
|
||||
| (g_model.moduleData[module].multi.lowPowerMode << 7))
|
||||
);
|
||||
|
||||
// byte 3
|
||||
sendMulti(moduleIdx, (uint8_t) optionValue);
|
||||
sendMulti(p_buf, (uint8_t) optionValue);
|
||||
}
|
||||
|
||||
void sendD16BindOption(uint8_t moduleIdx)
|
||||
void sendD16BindOption(uint8_t*& p_buf, uint8_t module)
|
||||
{
|
||||
uint8_t bind_opt = g_model.moduleData[moduleIdx].multi.receiverTelemetryOff ? 1 : 0;
|
||||
bind_opt |= g_model.moduleData[moduleIdx].multi.receiverHigherChannels ? 2 : 0;
|
||||
sendMulti(moduleIdx, bind_opt);
|
||||
uint8_t bind_opt = g_model.moduleData[module].multi.receiverTelemetryOff ? 1 : 0;
|
||||
bind_opt |= g_model.moduleData[module].multi.receiverHigherChannels ? 2 : 0;
|
||||
sendMulti(p_buf, bind_opt);
|
||||
}
|
||||
|
||||
#if defined(LUA)
|
||||
void sendSport(uint8_t moduleIdx)
|
||||
void sendSport(uint8_t*& p_buf, uint8_t module)
|
||||
{
|
||||
// example: B7 30 30 0C 80 00 00 00 13
|
||||
uint8_t j=0;
|
||||
@@ -420,25 +423,25 @@ void sendSport(uint8_t moduleIdx)
|
||||
for (uint8_t i = 0; i < outputTelemetryBuffer.size - 1 && j < 8; i++, j++) {
|
||||
if (outputTelemetryBuffer.data[i] == BYTE_STUFF) {
|
||||
i++;
|
||||
sendMulti(moduleIdx, outputTelemetryBuffer.data[i] ^ STUFF_MASK);
|
||||
sendMulti(p_buf, outputTelemetryBuffer.data[i] ^ STUFF_MASK);
|
||||
}
|
||||
else {
|
||||
sendMulti(moduleIdx, outputTelemetryBuffer.data[i]);
|
||||
sendMulti(p_buf, outputTelemetryBuffer.data[i]);
|
||||
}
|
||||
}
|
||||
|
||||
outputTelemetryBuffer.reset(); // empty buffer
|
||||
}
|
||||
|
||||
void sendHott(uint8_t moduleIdx)
|
||||
void sendHott(uint8_t*& p_buf, uint8_t module)
|
||||
{
|
||||
if (Multi_Buffer && memcmp(Multi_Buffer, "HoTT", 4) == 0 && (Multi_Buffer[5] & 0x80) && (Multi_Buffer[5] & 0x0F) >= 0x07) {
|
||||
// HoTT Lua script is running
|
||||
sendMulti(moduleIdx, Multi_Buffer[5]);
|
||||
sendMulti(p_buf, Multi_Buffer[5]);
|
||||
}
|
||||
}
|
||||
|
||||
void sendConfig(uint8_t moduleIdx)
|
||||
void sendConfig(uint8_t*& p_buf, uint8_t module)
|
||||
{
|
||||
// Multi_Buffer[0..3]=="Conf" -> Lua script is running
|
||||
// Multi_Buffer[4]==0x01 -> TX to Module data ready to be sent
|
||||
@@ -449,13 +452,13 @@ void sendConfig(uint8_t moduleIdx)
|
||||
if (Multi_Buffer && memcmp(Multi_Buffer, "Conf", 4) == 0 && Multi_Buffer[4] == 0x01) {
|
||||
// Config Lua script is running and sending
|
||||
for (uint8_t i = 0; i < 7; i++) {
|
||||
sendMulti(moduleIdx, Multi_Buffer[5 + i]);
|
||||
sendMulti(p_buf, Multi_Buffer[5 + i]);
|
||||
}
|
||||
Multi_Buffer[4] = 0x00; // Send data only once
|
||||
}
|
||||
}
|
||||
|
||||
void sendDSM(uint8_t moduleIdx)
|
||||
void sendDSM(uint8_t*& p_buf, uint8_t module)
|
||||
{
|
||||
// Multi_Buffer[0..2]=="DSM" -> Lua script is running
|
||||
// Multi_Buffer[3]==0x70 + len -> TX to RX data ready to be sent
|
||||
@@ -463,7 +466,7 @@ void sendDSM(uint8_t moduleIdx)
|
||||
// Multi_Buffer[10..25]=16 bytes of RX to TX data
|
||||
if (Multi_Buffer && memcmp(Multi_Buffer, "DSM", 3) == 0 && (Multi_Buffer[3] & 0xF8) == 0x70) {
|
||||
for(uint8_t i = 0; i < 7; i++) {
|
||||
sendMulti(moduleIdx, Multi_Buffer[3+i]);
|
||||
sendMulti(p_buf, Multi_Buffer[3+i]);
|
||||
}
|
||||
Multi_Buffer[3] = 0x00; // Data sent
|
||||
}
|
||||
|
||||
@@ -19,8 +19,7 @@
|
||||
* GNU General Public License for more details.
|
||||
*/
|
||||
|
||||
#ifndef _MULTI_PULSES_H_
|
||||
#define _MULTI_PULSES_H_
|
||||
#pragma once
|
||||
|
||||
#include "pulses_common.h"
|
||||
#include "hal/serial_driver.h"
|
||||
@@ -41,7 +40,4 @@ class UartMultiPulses: public DataBuffer<uint8_t, 64>
|
||||
}
|
||||
};
|
||||
|
||||
extern etx_serial_init multiSerialInitParams;
|
||||
extern const etx_module_driver_t MultiInternalDriver;
|
||||
|
||||
#endif
|
||||
extern const etx_proto_driver_t MultiDriver;
|
||||
|
||||
+77
-30
@@ -21,14 +21,17 @@
|
||||
|
||||
#include <climits>
|
||||
|
||||
#include "ppm.h"
|
||||
#include "hal/module_port.h"
|
||||
#include "mixer_scheduler.h"
|
||||
|
||||
#include "opentx.h"
|
||||
|
||||
// Minimum space after the last PPM pulse in us
|
||||
#define PPM_SAFE_MARGIN 3000 // 3ms
|
||||
|
||||
template <class T>
|
||||
uint16_t setupPulsesPPM(PpmPulsesData<T>* ppmPulsesData, uint8_t channelsStart,
|
||||
int8_t channelsCount)
|
||||
uint16_t setupPulsesPPM(T*& data, uint8_t channelsStart, int8_t channelsCount)
|
||||
{
|
||||
uint16_t total = 0;
|
||||
int16_t PPM_range = g_model.extendedLimits ?
|
||||
@@ -40,16 +43,13 @@ uint16_t setupPulsesPPM(PpmPulsesData<T>* ppmPulsesData, uint8_t channelsStart,
|
||||
// The pulse ISR is 2mhz that's why everything is multiplied by 2
|
||||
|
||||
uint8_t firstCh = channelsStart;
|
||||
uint8_t lastCh =
|
||||
min<uint8_t>(MAX_OUTPUT_CHANNELS, firstCh + 8 + channelsCount);
|
||||
|
||||
ppmPulsesData->ptr = ppmPulsesData->pulses;
|
||||
uint8_t lastCh = min<uint8_t>(MAX_OUTPUT_CHANNELS, firstCh + 8 + channelsCount);
|
||||
|
||||
for (uint32_t i = firstCh; i < lastCh; i++) {
|
||||
int16_t v =
|
||||
limit((int16_t)-PPM_range, channelOutputs[i], (int16_t)PPM_range) +
|
||||
2 * PPM_CH_CENTER(i);
|
||||
*ppmPulsesData->ptr++ = v;
|
||||
*data++ = v;
|
||||
total += v;
|
||||
}
|
||||
|
||||
@@ -58,8 +58,9 @@ uint16_t setupPulsesPPM(PpmPulsesData<T>* ppmPulsesData, uint8_t channelsStart,
|
||||
|
||||
void setupPulsesPPMTrainer()
|
||||
{
|
||||
auto p_data = trainerPulsesData.ppm.pulses;
|
||||
uint16_t total = setupPulsesPPM<trainer_pulse_duration_t>(
|
||||
&trainerPulsesData.ppm, g_model.trainerData.channelsStart,
|
||||
p_data, g_model.trainerData.channelsStart,
|
||||
g_model.trainerData.channelsCount);
|
||||
|
||||
uint32_t rest = PPM_TRAINER_PERIOD_HALF_US();
|
||||
@@ -72,42 +73,88 @@ void setupPulsesPPMTrainer()
|
||||
if (rest >= USHRT_MAX - 1)
|
||||
rest = USHRT_MAX - 1;
|
||||
|
||||
*trainerPulsesData.ppm.ptr++ = (uint16_t)rest;
|
||||
*p_data++ = (uint16_t)rest;
|
||||
|
||||
// stop mark so that IRQ-based sending
|
||||
// knows when to stop
|
||||
*trainerPulsesData.ppm.ptr = 0;
|
||||
*p_data = 0;
|
||||
|
||||
trainerPulsesData.ppm.ptr = p_data;
|
||||
}
|
||||
|
||||
static void setupPulsesPPMModule(uint8_t module)
|
||||
static uint32_t setupPulsesPPMModule(uint8_t module, pulse_duration_t*& data)
|
||||
{
|
||||
PpmPulsesData<pulse_duration_t>* data = nullptr;
|
||||
#if defined(PCBTARANIS) && defined(INTERNAL_MODULE_PPM)
|
||||
if (module == INTERNAL_MODULE) {
|
||||
data = &intmodulePulsesData.ppm;
|
||||
} else
|
||||
#endif
|
||||
{
|
||||
data = &extmodulePulsesData.ppm;
|
||||
}
|
||||
|
||||
setupPulsesPPM(&extmodulePulsesData.ppm,
|
||||
auto start = data;
|
||||
setupPulsesPPM(data,
|
||||
g_model.moduleData[module].channelsStart,
|
||||
g_model.moduleData[module].channelsCount);
|
||||
|
||||
// Set the final period to 1ms after which the
|
||||
// PPM will be switched OFF
|
||||
*data->ptr++ = PPM_SAFE_MARGIN * 2;
|
||||
*data++ = PPM_SAFE_MARGIN * 2;
|
||||
|
||||
return data - start;
|
||||
}
|
||||
|
||||
#if defined(PCBTARANIS) && defined(INTERNAL_MODULE_PPM)
|
||||
void setupPulsesPPMInternalModule()
|
||||
static void* ppmInit(uint8_t module)
|
||||
{
|
||||
setupPulsesPPMModule(INTERNAL_MODULE);
|
||||
}
|
||||
#if defined(HARDWARE_INTERNAL_MODULE)
|
||||
// only external module supported
|
||||
if (module == INTERNAL_MODULE) return nullptr;
|
||||
#endif
|
||||
|
||||
void setupPulsesPPMExternalModule()
|
||||
{
|
||||
setupPulsesPPMModule(EXTERNAL_MODULE);
|
||||
auto delay = GET_MODULE_PPM_DELAY(module) * 2;
|
||||
etx_timer_config_t cfg = {
|
||||
.type = ETX_PWM,
|
||||
.polarity = GET_MODULE_PPM_POLARITY(module),
|
||||
.cmp_val = (uint16_t)delay,
|
||||
};
|
||||
|
||||
auto mod_st = modulePortInitTimer(module, ETX_MOD_PORT_TIMER, &cfg);
|
||||
if (!mod_st) return nullptr;
|
||||
|
||||
mixerSchedulerSetPeriod(module, PPM_PERIOD(module));
|
||||
return (void*)mod_st;
|
||||
}
|
||||
|
||||
static void ppmDeInit(void* ctx)
|
||||
{
|
||||
auto mod_st = (etx_module_state_t*)ctx;
|
||||
modulePortDeInit(mod_st);
|
||||
}
|
||||
|
||||
static void ppmSendPulses(void* ctx, uint8_t* buffer, int16_t* channels, uint8_t nChannels)
|
||||
{
|
||||
// TODO:
|
||||
(void)channels;
|
||||
(void)nChannels;
|
||||
|
||||
auto mod_st = (etx_module_state_t*)ctx;
|
||||
auto module = modulePortGetModule(mod_st);
|
||||
|
||||
pulse_duration_t* pulses = (pulse_duration_t*)buffer;
|
||||
auto length = setupPulsesPPMModule(module, pulses);
|
||||
|
||||
auto drv = modulePortGetTimerDrv(mod_st->tx);
|
||||
auto drv_ctx = modulePortGetCtx(mod_st->tx);
|
||||
|
||||
auto delay = GET_MODULE_PPM_DELAY(module) * 2;
|
||||
etx_timer_config_t cfg = {
|
||||
.type = ETX_PWM,
|
||||
.polarity = GET_MODULE_PPM_POLARITY(module),
|
||||
.cmp_val = (uint16_t)delay,
|
||||
};
|
||||
|
||||
drv->send(drv_ctx, &cfg, buffer, length);
|
||||
|
||||
// PPM_PERIOD is not a constant! It can be set from UI
|
||||
mixerSchedulerSetPeriod(module, PPM_PERIOD(module));
|
||||
}
|
||||
|
||||
const etx_proto_driver_t PpmDriver = {
|
||||
.protocol = PROTOCOL_CHANNELS_PPM,
|
||||
.init = ppmInit,
|
||||
.deinit = ppmDeInit,
|
||||
.sendPulses = ppmSendPulses,
|
||||
.processData = nullptr,
|
||||
};
|
||||
|
||||
@@ -0,0 +1,26 @@
|
||||
/*
|
||||
* Copyright (C) EdgeTx
|
||||
*
|
||||
* Based on code named
|
||||
* opentx - https://github.com/opentx/opentx
|
||||
* 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.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "hal/module_driver.h"
|
||||
|
||||
extern const etx_proto_driver_t PpmDriver;
|
||||
+237
-474
@@ -19,28 +19,36 @@
|
||||
* GNU General Public License for more details.
|
||||
*/
|
||||
|
||||
// #include "hal.h"
|
||||
#include "opentx.h"
|
||||
|
||||
#include "mixer_scheduler.h"
|
||||
#include "heartbeat_driver.h"
|
||||
#include "hal/module_port.h"
|
||||
#include "tasks/mixer_task.h"
|
||||
|
||||
#include "pulses/pxx2.h"
|
||||
#include "pulses/flysky.h"
|
||||
#include "pulses/dsm2.h"
|
||||
|
||||
#if defined(INTMODULE_USART)
|
||||
#include "intmodule_serial_driver.h"
|
||||
#else
|
||||
#include "intmodule_pulses_driver.h"
|
||||
#if defined(PPM)
|
||||
#include "pulses/ppm.h"
|
||||
#endif
|
||||
|
||||
#include "extmodule_serial_driver.h"
|
||||
#include "extmodule_driver.h"
|
||||
#if defined(PXX1)
|
||||
#include "pulses/pxx1.h"
|
||||
#endif
|
||||
|
||||
#if defined(SBUS)
|
||||
#include "pulses/sbus.h"
|
||||
#endif
|
||||
|
||||
#if defined(CROSSFIRE)
|
||||
#include "pulses/crossfire.h"
|
||||
#endif
|
||||
|
||||
#if defined(GHOST)
|
||||
#include "telemetry/ghost.h"
|
||||
#include "pulses/ghost.h"
|
||||
#endif
|
||||
|
||||
#if defined(MULTIMODULE)
|
||||
@@ -56,63 +64,91 @@
|
||||
#include "pulses/afhds3.h"
|
||||
#endif
|
||||
|
||||
uint8_t s_pulses_paused = 0;
|
||||
static module_pulse_driver _module_drivers[MAX_MODULES];
|
||||
static module_pulse_buffer _module_buffers[MAX_MODULES] __DMA;
|
||||
|
||||
void pulsesInit()
|
||||
{
|
||||
memset(_module_drivers, 0, sizeof(_module_drivers));
|
||||
}
|
||||
|
||||
module_pulse_driver* pulsesGetModuleDriver(uint8_t module)
|
||||
{
|
||||
return &(_module_drivers[module]);
|
||||
}
|
||||
|
||||
uint8_t* pulsesGetModuleBuffer(uint8_t module)
|
||||
{
|
||||
return _module_buffers[module]._buffer;
|
||||
}
|
||||
|
||||
ModuleState moduleState[NUM_MODULES];
|
||||
InternalModulePulsesData intmodulePulsesData __DMA;
|
||||
ExternalModulePulsesData extmodulePulsesData __DMA;
|
||||
TrainerPulsesData trainerPulsesData __DMA;
|
||||
|
||||
void startPulses()
|
||||
void pulsesStart()
|
||||
{
|
||||
telemetryStart();
|
||||
s_pulses_paused = false;
|
||||
|
||||
#if defined(HARDWARE_INTERNAL_MODULE)
|
||||
setupPulsesInternalModule();
|
||||
#endif
|
||||
|
||||
#if defined(HARDWARE_EXTERNAL_MODULE)
|
||||
setupPulsesExternalModule();
|
||||
#endif
|
||||
|
||||
#if defined(HARDWARE_EXTRA_MODULE)
|
||||
extramodulePpmStart();
|
||||
#endif
|
||||
mixerTaskStart();
|
||||
}
|
||||
|
||||
void stopPulses()
|
||||
void pulsesStop()
|
||||
{
|
||||
telemetryStop();
|
||||
s_pulses_paused = true;
|
||||
mixerTaskStop();
|
||||
|
||||
for (uint8_t i = 0; i < NUM_MODULES; i++)
|
||||
moduleState[i].protocol = PROTOCOL_CHANNELS_UNINITIALIZED;
|
||||
for (uint8_t i = 0; i < MAX_MODULES; i++)
|
||||
pulsesStopModule(i);
|
||||
}
|
||||
|
||||
void restartModule(uint8_t idx)
|
||||
void restartModule(uint8_t module)
|
||||
{
|
||||
if (idx == INTERNAL_MODULE) {
|
||||
if (!IS_INTERNAL_MODULE_ON()) return;
|
||||
} else if (idx == EXTERNAL_MODULE){
|
||||
if (!IS_EXTERNAL_MODULE_ON()) return;
|
||||
} else {
|
||||
mixerTaskStop();
|
||||
|
||||
// wait for the power output to be drained
|
||||
pulsesStopModule(module);
|
||||
RTOS_WAIT_MS(200);
|
||||
|
||||
mixerTaskStart();
|
||||
}
|
||||
|
||||
#if !defined(SIMU)
|
||||
#include <FreeRTOS/include/FreeRTOS.h>
|
||||
#include <FreeRTOS/include/timers.h>
|
||||
|
||||
static void _setup_async_module_restart(void* p1, uint32_t p2)
|
||||
{
|
||||
if (!mixerTaskTryLock()) {
|
||||
// In case the mixer cannot be locked, try again later
|
||||
// and make the same function pending again.
|
||||
PendedFunction_t cb = _setup_async_module_restart;
|
||||
xTimerPendFunctionCall(cb, p1, p2, 0/* do not wait */);
|
||||
return;
|
||||
}
|
||||
|
||||
pauseMixerCalculations();
|
||||
pausePulses();
|
||||
uint8_t module = (uint8_t)(uintptr_t)p1;
|
||||
moduleState[module].forced_off = 1;
|
||||
|
||||
if (idx == INTERNAL_MODULE) stopPulsesInternalModule();
|
||||
#if defined(HARDWARE_EXTERNAL_MODULE)
|
||||
else stopPulsesExternalModule();
|
||||
uint32_t timeout = p2;
|
||||
moduleState[module].counter = timeout;
|
||||
|
||||
mixerTaskUnlock();
|
||||
}
|
||||
#endif
|
||||
|
||||
RTOS_WAIT_MS(200); // 20ms so that the pulses interrupt will reinit the frame rate
|
||||
|
||||
resumePulses();
|
||||
resumeMixerCalculations();
|
||||
// return true if the request could be posted to the timer queue
|
||||
bool restartModuleAsync(uint8_t module, uint8_t cnt_delay)
|
||||
{
|
||||
#if !defined(SIMU)
|
||||
PendedFunction_t cb = _setup_async_module_restart;
|
||||
return xTimerPendFunctionCall(cb, (void*)(uintptr_t)module, cnt_delay,
|
||||
0/* do not wait */) == pdPASS;
|
||||
#else
|
||||
return true;
|
||||
#endif
|
||||
}
|
||||
|
||||
// TODO: this should be moved to PXX2 territory!
|
||||
#if defined(PXX2)
|
||||
// use only for PXX
|
||||
void ModuleState::startBind(BindInformation* destination,
|
||||
ModuleCallback bindCallback)
|
||||
@@ -160,6 +196,7 @@ void ModuleState::writeReceiverSettings(ReceiverSettings* source)
|
||||
receiverSettings->timeout = 0;
|
||||
mode = MODULE_MODE_RECEIVER_SETTINGS;
|
||||
}
|
||||
#endif
|
||||
|
||||
void getModuleStatusString(uint8_t moduleIdx, char * statusText)
|
||||
{
|
||||
@@ -170,6 +207,7 @@ void getModuleStatusString(uint8_t moduleIdx, char * statusText)
|
||||
getMultiModuleStatus(moduleIdx).getStatusString(statusText);
|
||||
}
|
||||
#endif
|
||||
|
||||
#if defined(AFHDS3)
|
||||
if (isModuleAFHDS3(moduleIdx)) {
|
||||
afhds3::getStatusString(moduleIdx, statusText);
|
||||
@@ -216,6 +254,9 @@ uint8_t getModuleType(uint8_t module)
|
||||
return MODULE_TYPE_NONE;
|
||||
}
|
||||
|
||||
// TODO: replace with some mapping between
|
||||
// module type and protocol driver.
|
||||
//
|
||||
uint8_t getRequiredProtocol(uint8_t module)
|
||||
{
|
||||
uint8_t protocol = PROTOCOL_CHANNELS_UNINITIALIZED;
|
||||
@@ -226,36 +267,17 @@ uint8_t getRequiredProtocol(uint8_t module)
|
||||
break;
|
||||
|
||||
case MODULE_TYPE_XJT_PXX1:
|
||||
#if defined(INTMODULE_USART)
|
||||
if (module == INTERNAL_MODULE) {
|
||||
protocol = PROTOCOL_CHANNELS_PXX1_SERIAL;
|
||||
break;
|
||||
}
|
||||
#endif
|
||||
protocol = PROTOCOL_CHANNELS_PXX1_PULSES;
|
||||
break;
|
||||
|
||||
case MODULE_TYPE_R9M_PXX1:
|
||||
protocol = PROTOCOL_CHANNELS_PXX1_PULSES;
|
||||
break;
|
||||
|
||||
#if defined(HARDWARE_EXTERNAL_MODULE_SIZE_SML)
|
||||
case MODULE_TYPE_R9M_LITE_PXX1:
|
||||
protocol = PROTOCOL_CHANNELS_PXX1_SERIAL;
|
||||
protocol = PROTOCOL_CHANNELS_PXX1;
|
||||
break;
|
||||
|
||||
case MODULE_TYPE_R9M_LITE_PXX2:
|
||||
protocol = PROTOCOL_CHANNELS_PXX2_LOWSPEED;
|
||||
break;
|
||||
#endif
|
||||
|
||||
case MODULE_TYPE_ISRM_PXX2:
|
||||
case MODULE_TYPE_R9M_PXX2:
|
||||
#if defined(HARDWARE_EXTERNAL_MODULE_SIZE_SML)
|
||||
case MODULE_TYPE_R9M_LITE_PXX2:
|
||||
case MODULE_TYPE_XJT_LITE_PXX2:
|
||||
case MODULE_TYPE_R9M_LITE_PRO_PXX2:
|
||||
#endif
|
||||
protocol = PROTOCOL_CHANNELS_PXX2_HIGHSPEED;
|
||||
protocol = PROTOCOL_CHANNELS_PXX2;
|
||||
break;
|
||||
|
||||
case MODULE_TYPE_SBUS:
|
||||
@@ -275,6 +297,7 @@ uint8_t getRequiredProtocol(uint8_t module)
|
||||
PROTOCOL_CHANNELS_DSM2_LP45 + g_model.moduleData[module].subType,
|
||||
PROTOCOL_CHANNELS_DSM2_DSMX);
|
||||
// The module is set to OFF during one second before BIND start
|
||||
// TODO: move this to DSM2 driver...
|
||||
{
|
||||
static tmr10ms_t bindStartTime = 0;
|
||||
if (moduleState[module].mode == MODULE_MODE_BIND) {
|
||||
@@ -322,239 +345,78 @@ uint8_t getRequiredProtocol(uint8_t module)
|
||||
break;
|
||||
}
|
||||
|
||||
if (s_pulses_paused) {
|
||||
protocol = PROTOCOL_CHANNELS_NONE;
|
||||
}
|
||||
|
||||
#if 0
|
||||
// will need an EEPROM conversion
|
||||
if (moduleState[module].mode == MODULE_OFF) {
|
||||
protocol = PROTOCOL_CHANNELS_NONE;
|
||||
}
|
||||
#endif
|
||||
|
||||
return protocol;
|
||||
}
|
||||
|
||||
#if defined(HARDWARE_INTERNAL_MODULE)
|
||||
static void* internalModuleContext = nullptr;
|
||||
static const etx_module_driver_t* internalModuleDriver = nullptr;
|
||||
static module_init_cb_t _on_module_init = nullptr;
|
||||
static module_deinit_cb_t _on_module_deinit = nullptr;
|
||||
|
||||
const etx_module_driver_t* getIntModuleDriver()
|
||||
void pulsesSetModuleInitCb(module_init_cb_t cb)
|
||||
{
|
||||
return internalModuleDriver;
|
||||
_on_module_init = cb;
|
||||
}
|
||||
|
||||
void* getIntModuleCtx()
|
||||
void pulsesSetModuleDeInitCb(module_deinit_cb_t cb)
|
||||
{
|
||||
return internalModuleContext;
|
||||
_on_module_deinit = cb;
|
||||
}
|
||||
|
||||
static void enablePulsesInternalModule(uint8_t protocol)
|
||||
static void _init_module(uint8_t module, const etx_proto_driver_t* drv)
|
||||
{
|
||||
// start new protocol hardware here
|
||||
if (internalModuleDriver) {
|
||||
internalModuleDriver->deinit(internalModuleContext);
|
||||
internalModuleContext = nullptr;
|
||||
internalModuleDriver = nullptr;
|
||||
} else {
|
||||
intmoduleStop();
|
||||
}
|
||||
auto mod = &(_module_drivers[module]);
|
||||
void* ctx = drv->init(module);
|
||||
|
||||
switch (protocol) {
|
||||
#if defined(INTERNAL_MODULE_PXX1) && !defined(INTMODULE_USART)
|
||||
case PROTOCOL_CHANNELS_PXX1_PULSES:
|
||||
intmodulePxx1PulsesStart();
|
||||
#if defined(INTMODULE_HEARTBEAT)
|
||||
init_intmodule_heartbeat();
|
||||
#endif
|
||||
mixerSchedulerSetPeriod(INTERNAL_MODULE, INTMODULE_PXX1_SERIAL_PERIOD);
|
||||
break;
|
||||
#endif
|
||||
|
||||
#if defined(INTERNAL_MODULE_PXX1) && defined(INTMODULE_USART)
|
||||
case PROTOCOL_CHANNELS_PXX1_SERIAL:
|
||||
internalModuleContext = Pxx1InternalSerialDriver.init(INTERNAL_MODULE);
|
||||
internalModuleDriver = &Pxx1InternalSerialDriver;
|
||||
break;
|
||||
#endif
|
||||
|
||||
#if defined(INTERNAL_MODULE_PXX2)
|
||||
case PROTOCOL_CHANNELS_PXX2_HIGHSPEED:
|
||||
internalModuleContext = Pxx2InternalDriver.init(INTERNAL_MODULE);
|
||||
internalModuleDriver = &Pxx2InternalDriver;
|
||||
break;
|
||||
#endif
|
||||
|
||||
#if defined(INTERNAL_MODULE_MULTI) && defined(MULTIMODULE)
|
||||
case PROTOCOL_CHANNELS_MULTIMODULE:
|
||||
internalModuleContext = MultiInternalDriver.init(INTERNAL_MODULE);
|
||||
internalModuleDriver = &MultiInternalDriver;
|
||||
break;
|
||||
#endif
|
||||
|
||||
#if defined(INTERNAL_MODULE_CRSF) && defined(CROSSFIRE)
|
||||
case PROTOCOL_CHANNELS_CROSSFIRE:
|
||||
internalModuleContext = CrossfireInternalDriver.init(INTERNAL_MODULE);
|
||||
internalModuleDriver = &CrossfireInternalDriver;
|
||||
break;
|
||||
#endif
|
||||
|
||||
#if defined(INTERNAL_MODULE_PPM)
|
||||
case PROTOCOL_CHANNELS_PPM:
|
||||
intmodulePpmStart();
|
||||
mixerSchedulerSetPeriod(INTERNAL_MODULE, PPM_PERIOD(INTERNAL_MODULE));
|
||||
break;
|
||||
#endif
|
||||
|
||||
#if defined(INTERNAL_MODULE_AFHDS2A) && defined(AFHDS2)
|
||||
case PROTOCOL_CHANNELS_AFHDS2A:
|
||||
internalModuleContext = Afhds2InternalDriver.init(INTERNAL_MODULE);
|
||||
internalModuleDriver = &Afhds2InternalDriver;
|
||||
break;
|
||||
#endif
|
||||
|
||||
#if defined(INTERNAL_MODULE_AFHDS3)
|
||||
case PROTOCOL_CHANNELS_AFHDS3:
|
||||
internalModuleContext = afhds3::internalDriver.init(INTERNAL_MODULE);
|
||||
internalModuleDriver = &afhds3::internalDriver;
|
||||
break;
|
||||
#endif
|
||||
|
||||
default:
|
||||
// internal module stopped, use default mixer period
|
||||
mixerSchedulerSetPeriod(INTERNAL_MODULE, 0);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
bool setupPulsesInternalModule(uint8_t protocol)
|
||||
{
|
||||
uint8_t channelStart = g_model.moduleData[INTERNAL_MODULE].channelsStart;
|
||||
int16_t* channels = &channelOutputs[channelStart];
|
||||
uint8_t nChannels = 16; // TODO: MAX_CHANNELS - channelsStart
|
||||
|
||||
if (internalModuleDriver) {
|
||||
internalModuleDriver->setupPulses(internalModuleContext,
|
||||
channels, nChannels);
|
||||
return true;
|
||||
}
|
||||
|
||||
switch (protocol) {
|
||||
#if defined(HARDWARE_INTERNAL_MODULE) && defined(PXX1) && !defined(INTMODULE_USART)
|
||||
case PROTOCOL_CHANNELS_PXX1_PULSES:
|
||||
intmodulePulsesData.pxx.setupFrame(INTERNAL_MODULE);
|
||||
return true;
|
||||
#endif
|
||||
|
||||
#if defined(PCBTARANIS) && defined(INTERNAL_MODULE_PPM)
|
||||
case PROTOCOL_CHANNELS_PPM:
|
||||
setupPulsesPPMInternalModule();
|
||||
return true;
|
||||
#endif
|
||||
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
void stopPulsesInternalModule()
|
||||
{
|
||||
auto& proto = moduleState[INTERNAL_MODULE].protocol;
|
||||
if (proto != PROTOCOL_CHANNELS_UNINITIALIZED &&
|
||||
proto != PROTOCOL_CHANNELS_NONE) {
|
||||
if (internalModuleDriver) {
|
||||
internalModuleDriver->deinit(internalModuleContext);
|
||||
internalModuleDriver = nullptr;
|
||||
internalModuleContext = nullptr;
|
||||
} else {
|
||||
mixerSchedulerSetPeriod(INTERNAL_MODULE, 0);
|
||||
intmoduleStop();
|
||||
}
|
||||
proto = PROTOCOL_CHANNELS_NONE;
|
||||
}
|
||||
}
|
||||
|
||||
void intmoduleSendNextFrame()
|
||||
{
|
||||
if (internalModuleDriver) {
|
||||
internalModuleDriver->sendPulses(internalModuleContext);
|
||||
// TODO: module init failed somehow, we should handle this better...
|
||||
if (!ctx) {
|
||||
TRACE("Module #%d init failed", module);
|
||||
return;
|
||||
}
|
||||
|
||||
switch (moduleState[INTERNAL_MODULE].protocol) {
|
||||
mod->drv = drv;
|
||||
mod->ctx = ctx;
|
||||
|
||||
#if defined(INTERNAL_MODULE_PPM)
|
||||
case PROTOCOL_CHANNELS_PPM:
|
||||
intmoduleSendNextFramePPM(
|
||||
intmodulePulsesData.ppm.pulses,
|
||||
intmodulePulsesData.ppm.ptr - intmodulePulsesData.ppm.pulses);
|
||||
break;
|
||||
#endif
|
||||
|
||||
#if defined(PXX1) && !defined(INTMODULE_USART)
|
||||
case PROTOCOL_CHANNELS_PXX1_PULSES:
|
||||
intmoduleSendNextFramePxx1(intmodulePulsesData.pxx.getData(),
|
||||
intmodulePulsesData.pxx.getSize());
|
||||
break;
|
||||
#endif
|
||||
}
|
||||
// board specific hook
|
||||
if (_on_module_init)
|
||||
_on_module_init(module, drv);
|
||||
|
||||
// power ON
|
||||
modulePortSetPower(module, true);
|
||||
TRACE("Module #%d init succeeded", module);
|
||||
}
|
||||
|
||||
bool setupPulsesInternalModule()
|
||||
static void _deinit_module(uint8_t module)
|
||||
{
|
||||
uint8_t protocol = getRequiredProtocol(INTERNAL_MODULE);
|
||||
auto mod = &(_module_drivers[module]);
|
||||
if (!mod->drv) return;
|
||||
|
||||
heartbeat |= (HEART_TIMER_PULSES << INTERNAL_MODULE);
|
||||
// scheduling OFF
|
||||
mixerSchedulerSetPeriod(module, 0);
|
||||
|
||||
if (moduleState[INTERNAL_MODULE].protocol != protocol) {
|
||||
enablePulsesInternalModule(protocol);
|
||||
moduleState[INTERNAL_MODULE].protocol = protocol;
|
||||
return false;
|
||||
}
|
||||
else {
|
||||
return setupPulsesInternalModule(protocol);
|
||||
}
|
||||
}
|
||||
#endif
|
||||
// board specific hook
|
||||
auto drv = mod->drv;
|
||||
if (_on_module_deinit)
|
||||
_on_module_deinit(module, drv);
|
||||
|
||||
// de-init
|
||||
auto ctx = mod->ctx;
|
||||
drv->deinit(ctx);
|
||||
|
||||
#if defined(HARDWARE_EXTERNAL_MODULE)
|
||||
static void* externalModuleContext = nullptr;
|
||||
static const etx_module_driver_t* externalModuleDriver = nullptr;
|
||||
// power OFF
|
||||
modulePortSetPower(module, false);
|
||||
|
||||
const etx_module_driver_t* getExtModuleDriver()
|
||||
{
|
||||
return externalModuleDriver;
|
||||
// clear
|
||||
memset(mod, 0, sizeof(module_pulse_driver));
|
||||
TRACE("Module #%d de-init succeeded", module);
|
||||
}
|
||||
|
||||
void* getExtModuleCtx()
|
||||
static void pulsesEnableModule(uint8_t module, uint8_t protocol)
|
||||
{
|
||||
return externalModuleContext;
|
||||
}
|
||||
|
||||
void enablePulsesExternalModule(uint8_t protocol)
|
||||
{
|
||||
// start new protocol hardware here
|
||||
if (externalModuleDriver) {
|
||||
externalModuleDriver->deinit(externalModuleContext);
|
||||
externalModuleDriver = nullptr;
|
||||
externalModuleContext = nullptr;
|
||||
} else {
|
||||
extmoduleStop();
|
||||
}
|
||||
_deinit_module(module);
|
||||
|
||||
switch (protocol) {
|
||||
#if defined(PXX1)
|
||||
case PROTOCOL_CHANNELS_PXX1_PULSES:
|
||||
extmodulePxx1PulsesStart();
|
||||
mixerSchedulerSetPeriod(EXTERNAL_MODULE, PXX_PULSES_PERIOD);
|
||||
break;
|
||||
#endif
|
||||
|
||||
#if defined(PXX1) && defined(HARDWARE_EXTERNAL_MODULE_SIZE_SML)
|
||||
case PROTOCOL_CHANNELS_PXX1_SERIAL:
|
||||
externalModuleContext = Pxx1ExternalSerialDriver.init(EXTERNAL_MODULE);
|
||||
externalModuleDriver = &Pxx1ExternalSerialDriver;
|
||||
case PROTOCOL_CHANNELS_PXX1:
|
||||
_init_module(module, &Pxx1Driver);
|
||||
break;
|
||||
#endif
|
||||
|
||||
@@ -562,254 +424,155 @@ void enablePulsesExternalModule(uint8_t protocol)
|
||||
case PROTOCOL_CHANNELS_DSM2_LP45:
|
||||
case PROTOCOL_CHANNELS_DSM2_DSM2:
|
||||
case PROTOCOL_CHANNELS_DSM2_DSMX:
|
||||
extmoduleSerialStart();
|
||||
mixerSchedulerSetPeriod(EXTERNAL_MODULE, DSM2_PERIOD);
|
||||
_init_module(module, &DSM2Driver);
|
||||
break;
|
||||
#endif
|
||||
|
||||
#if defined(SBUS)
|
||||
case PROTOCOL_CHANNELS_SBUS:
|
||||
_init_module(module, &SBusDriver);
|
||||
break;
|
||||
#endif
|
||||
|
||||
#if defined(PXX2)
|
||||
case PROTOCOL_CHANNELS_PXX2:
|
||||
_init_module(module, &Pxx2Driver);
|
||||
break;
|
||||
#endif
|
||||
|
||||
#if defined(MULTIMODULE)
|
||||
case PROTOCOL_CHANNELS_MULTIMODULE:
|
||||
_init_module(module, &MultiDriver);
|
||||
break;
|
||||
#endif
|
||||
|
||||
#if defined(CROSSFIRE)
|
||||
case PROTOCOL_CHANNELS_CROSSFIRE:
|
||||
externalModuleContext = CrossfireExternalDriver.init(EXTERNAL_MODULE);
|
||||
externalModuleDriver = &CrossfireExternalDriver;
|
||||
_init_module(module, &CrossfireDriver);
|
||||
break;
|
||||
#endif
|
||||
|
||||
#if defined(GHOST)
|
||||
case PROTOCOL_CHANNELS_GHOST:
|
||||
EXTERNAL_MODULE_ON();
|
||||
mixerSchedulerSetPeriod(EXTERNAL_MODULE, GHOST_PERIOD);
|
||||
break;
|
||||
#endif
|
||||
|
||||
#if defined(PXX2)
|
||||
case PROTOCOL_CHANNELS_PXX2_HIGHSPEED:
|
||||
externalModuleContext = Pxx2ExternalDriver.init(EXTERNAL_MODULE);
|
||||
externalModuleDriver = &Pxx2ExternalDriver;
|
||||
break;
|
||||
|
||||
case PROTOCOL_CHANNELS_PXX2_LOWSPEED:
|
||||
externalModuleContext = Pxx2LowSpeedExternalDriver.init(EXTERNAL_MODULE);
|
||||
externalModuleDriver = &Pxx2LowSpeedExternalDriver;
|
||||
break;
|
||||
#endif
|
||||
|
||||
#if defined(MULTIMODULE)
|
||||
case PROTOCOL_CHANNELS_MULTIMODULE:
|
||||
extmoduleSerialStart();
|
||||
mixerSchedulerSetPeriod(EXTERNAL_MODULE, MULTIMODULE_PERIOD);
|
||||
getMultiModuleStatus(EXTERNAL_MODULE).failsafeChecked = false;
|
||||
getMultiModuleStatus(EXTERNAL_MODULE).flags = 0;
|
||||
#if defined(MULTI_PROTOLIST)
|
||||
MultiRfProtocols::instance(EXTERNAL_MODULE)->triggerScan();
|
||||
#endif
|
||||
break;
|
||||
#endif
|
||||
|
||||
#if defined(SBUS)
|
||||
case PROTOCOL_CHANNELS_SBUS:
|
||||
extmoduleSerialStart();
|
||||
mixerSchedulerSetPeriod(EXTERNAL_MODULE, SBUS_PERIOD);
|
||||
_init_module(module, &GhostDriver);
|
||||
break;
|
||||
#endif
|
||||
|
||||
#if defined(PPM)
|
||||
case PROTOCOL_CHANNELS_PPM:
|
||||
extmodulePpmStart(
|
||||
GET_MODULE_PPM_DELAY(EXTERNAL_MODULE),
|
||||
GET_MODULE_PPM_POLARITY(EXTERNAL_MODULE));
|
||||
mixerSchedulerSetPeriod(EXTERNAL_MODULE, PPM_PERIOD(EXTERNAL_MODULE));
|
||||
case PROTOCOL_CHANNELS_PPM:
|
||||
_init_module(module, &PpmDriver);
|
||||
break;
|
||||
#endif
|
||||
|
||||
#if defined(AFHDS3)
|
||||
#if defined(INTERNAL_MODULE_AFHDS2A) && defined(AFHDS2)
|
||||
case PROTOCOL_CHANNELS_AFHDS2A:
|
||||
_init_module(module, &Afhds2InternalDriver);
|
||||
break;
|
||||
#endif
|
||||
|
||||
#if defined(INTERNAL_MODULE_AFHDS3)
|
||||
case PROTOCOL_CHANNELS_AFHDS3:
|
||||
externalModuleContext = afhds3::externalDriver.init(EXTERNAL_MODULE);
|
||||
externalModuleDriver = &afhds3::externalDriver;
|
||||
_init_module(module, &afhds3::ProtoDriver);
|
||||
break;
|
||||
#endif
|
||||
|
||||
case PROTOCOL_CHANNELS_DSMP:
|
||||
extmoduleSerialStart();
|
||||
mixerSchedulerSetPeriod(EXTERNAL_MODULE, 11 * 1000 /* 11ms in us */);
|
||||
break;
|
||||
|
||||
default:
|
||||
// external module stopped, use default mixer period
|
||||
mixerSchedulerSetPeriod(EXTERNAL_MODULE, 0);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
bool setupPulsesExternalModule(uint8_t protocol)
|
||||
{
|
||||
uint8_t channelStart = g_model.moduleData[EXTERNAL_MODULE].channelsStart;
|
||||
int16_t* channels = &channelOutputs[channelStart];
|
||||
uint8_t nChannels = 16; // TODO: MAX_CHANNELS - channelsStart
|
||||
|
||||
if (externalModuleDriver) {
|
||||
externalModuleDriver->setupPulses(externalModuleContext,
|
||||
channels, nChannels);
|
||||
return true;
|
||||
}
|
||||
|
||||
switch (protocol) {
|
||||
|
||||
#if defined(PXX1)
|
||||
case PROTOCOL_CHANNELS_PXX1_PULSES:
|
||||
extmodulePulsesData.pxx.setupFrame(EXTERNAL_MODULE);
|
||||
return true;
|
||||
#endif
|
||||
|
||||
#if defined(SBUS)
|
||||
case PROTOCOL_CHANNELS_SBUS:
|
||||
setupPulsesSbus();
|
||||
// SBUS_PERIOD is not a constant! It can be set from UI
|
||||
mixerSchedulerSetPeriod(EXTERNAL_MODULE, SBUS_PERIOD);
|
||||
return true;
|
||||
#endif
|
||||
|
||||
#if defined(DSM2)
|
||||
case PROTOCOL_CHANNELS_DSM2_LP45:
|
||||
case PROTOCOL_CHANNELS_DSM2_DSM2:
|
||||
case PROTOCOL_CHANNELS_DSM2_DSMX:
|
||||
setupPulsesDSM2();
|
||||
return true;
|
||||
#endif
|
||||
|
||||
#if defined(GHOST)
|
||||
case PROTOCOL_CHANNELS_GHOST:
|
||||
{
|
||||
ModuleSyncStatus& status = getModuleSyncStatus(EXTERNAL_MODULE);
|
||||
if (status.isValid())
|
||||
mixerSchedulerSetPeriod(EXTERNAL_MODULE, status.getAdjustedRefreshRate());
|
||||
else
|
||||
mixerSchedulerSetPeriod(EXTERNAL_MODULE, GHOST_PERIOD);
|
||||
setupPulsesGhost();
|
||||
return true;
|
||||
}
|
||||
#endif
|
||||
|
||||
#if defined(MULTIMODULE)
|
||||
case PROTOCOL_CHANNELS_MULTIMODULE:
|
||||
setupPulsesMultiExternalModule();
|
||||
return true;
|
||||
#endif
|
||||
|
||||
#if defined(PPM)
|
||||
case PROTOCOL_CHANNELS_PPM:
|
||||
setupPulsesPPMExternalModule();
|
||||
return true;
|
||||
#endif
|
||||
|
||||
case PROTOCOL_CHANNELS_DSMP:
|
||||
setupPulsesLemonDSMP();
|
||||
return true;
|
||||
_init_module(module, &DSMPDriver);
|
||||
break;
|
||||
#endif
|
||||
|
||||
default:
|
||||
return false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
void extmoduleSendNextFrame()
|
||||
// TODO: declare a function in telemetry
|
||||
extern volatile uint8_t _telemetryIsPolling;
|
||||
|
||||
void pulsesStopModule(uint8_t module)
|
||||
{
|
||||
if (externalModuleDriver) {
|
||||
externalModuleDriver->sendPulses(externalModuleContext);
|
||||
if (module >= MAX_MODULES) return;
|
||||
|
||||
while(_telemetryIsPolling) {
|
||||
// In case the telemetry timer is currently polling the port,
|
||||
// we give the timer task a chance to run and finish the polling.
|
||||
RTOS_WAIT_MS(1);
|
||||
}
|
||||
_deinit_module(module);
|
||||
|
||||
auto& proto = moduleState[module].protocol;
|
||||
proto = PROTOCOL_CHANNELS_NONE;
|
||||
}
|
||||
|
||||
static bool _handle_async_restart(uint8_t module)
|
||||
{
|
||||
if (moduleState[module].forced_off) {
|
||||
if (--moduleState[module].counter > 0) {
|
||||
_deinit_module(module);
|
||||
moduleState[module].protocol = PROTOCOL_CHANNELS_NONE;
|
||||
return true;
|
||||
} else {
|
||||
moduleState[module].forced_off = 0;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
void pulsesSendNextFrame(uint8_t module)
|
||||
{
|
||||
if (module >= MAX_MODULES) return;
|
||||
|
||||
uint8_t protocol = getRequiredProtocol(module);
|
||||
|
||||
if (moduleState[module].protocol != protocol ||
|
||||
moduleState[module].forced_off) {
|
||||
// TODO: error checking!
|
||||
|
||||
if (_telemetryIsPolling) {
|
||||
// In case the telemetry timer is currently polling the port,
|
||||
// we just yield in the hope it will be different next time.
|
||||
return;
|
||||
}
|
||||
|
||||
if (_handle_async_restart(module))
|
||||
return;
|
||||
|
||||
pulsesEnableModule(module, protocol);
|
||||
moduleState[module].protocol = protocol;
|
||||
return;
|
||||
}
|
||||
|
||||
switch (moduleState[EXTERNAL_MODULE].protocol) {
|
||||
auto mod = &(_module_drivers[module]);
|
||||
if (mod->drv) {
|
||||
uint8_t channelStart = g_model.moduleData[module].channelsStart;
|
||||
int16_t* channels = &channelOutputs[channelStart];
|
||||
uint8_t nChannels = 16; // TODO: MAX_CHANNELS - channelsStart
|
||||
|
||||
case PROTOCOL_CHANNELS_PPM:
|
||||
extmoduleSendNextFramePpm(
|
||||
extmodulePulsesData.ppm.pulses,
|
||||
extmodulePulsesData.ppm.ptr - extmodulePulsesData.ppm.pulses,
|
||||
GET_MODULE_PPM_DELAY(EXTERNAL_MODULE),
|
||||
GET_MODULE_PPM_POLARITY(EXTERNAL_MODULE));
|
||||
mixerSchedulerSetPeriod(EXTERNAL_MODULE, PPM_PERIOD(EXTERNAL_MODULE));
|
||||
break;
|
||||
|
||||
#if defined(PXX1)
|
||||
case PROTOCOL_CHANNELS_PXX1_PULSES:
|
||||
extmoduleSendNextFramePxx1(extmodulePulsesData.pxx.getData(),
|
||||
extmodulePulsesData.pxx.getSize());
|
||||
break;
|
||||
#endif
|
||||
|
||||
#if defined(SBUS) || defined(DSM2) || defined(MULTIMODULE)
|
||||
case PROTOCOL_CHANNELS_SBUS:
|
||||
extmoduleSendNextFrameSoftSerial(
|
||||
extmodulePulsesData.dsm2.pulses,
|
||||
extmodulePulsesData.dsm2.ptr - extmodulePulsesData.dsm2.pulses,
|
||||
GET_SBUS_POLARITY(EXTERNAL_MODULE));
|
||||
break;
|
||||
|
||||
case PROTOCOL_CHANNELS_DSM2_LP45:
|
||||
case PROTOCOL_CHANNELS_DSM2_DSM2:
|
||||
case PROTOCOL_CHANNELS_DSM2_DSMX:
|
||||
case PROTOCOL_CHANNELS_MULTIMODULE:
|
||||
extmoduleSendNextFrameSoftSerial(
|
||||
extmodulePulsesData.dsm2.pulses,
|
||||
extmodulePulsesData.dsm2.ptr - extmodulePulsesData.dsm2.pulses);
|
||||
break;
|
||||
#endif
|
||||
case PROTOCOL_CHANNELS_DSMP:
|
||||
extmoduleSendNextFrameSoftSerial(
|
||||
extmodulePulsesData.dsm2.pulses,
|
||||
extmodulePulsesData.dsm2.ptr - extmodulePulsesData.dsm2.pulses,
|
||||
true);
|
||||
break;
|
||||
|
||||
#if defined(GHOST)
|
||||
case PROTOCOL_CHANNELS_GHOST:
|
||||
sportSendBuffer(extmodulePulsesData.ghost.pulses,
|
||||
extmodulePulsesData.ghost.length);
|
||||
break;
|
||||
#endif
|
||||
auto drv = mod->drv;
|
||||
auto ctx = mod->ctx;
|
||||
auto buffer = _module_buffers[module]._buffer;
|
||||
drv->sendPulses(ctx, buffer, channels, nChannels);
|
||||
}
|
||||
}
|
||||
|
||||
void stopPulsesExternalModule()
|
||||
void pulsesSendChannels()
|
||||
{
|
||||
auto& proto = moduleState[EXTERNAL_MODULE].protocol;
|
||||
if (proto != PROTOCOL_CHANNELS_UNINITIALIZED &&
|
||||
proto != PROTOCOL_CHANNELS_NONE) {
|
||||
if (externalModuleDriver) {
|
||||
externalModuleDriver->deinit(externalModuleContext);
|
||||
externalModuleDriver = nullptr;
|
||||
externalModuleContext = nullptr;
|
||||
} else {
|
||||
mixerSchedulerSetPeriod(EXTERNAL_MODULE, 0);
|
||||
extmoduleStop();
|
||||
}
|
||||
proto = PROTOCOL_CHANNELS_NONE;
|
||||
for (uint8_t i = 0; i < MAX_MODULES; i++) {
|
||||
pulsesSendNextFrame(i);
|
||||
}
|
||||
}
|
||||
|
||||
bool setupPulsesExternalModule()
|
||||
{
|
||||
uint8_t protocol = getRequiredProtocol(EXTERNAL_MODULE);
|
||||
|
||||
heartbeat |= (HEART_TIMER_PULSES << EXTERNAL_MODULE);
|
||||
|
||||
if (moduleState[EXTERNAL_MODULE].protocol != protocol) {
|
||||
enablePulsesExternalModule(protocol);
|
||||
moduleState[EXTERNAL_MODULE].protocol = protocol;
|
||||
return false;
|
||||
}
|
||||
else {
|
||||
return setupPulsesExternalModule(protocol);
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
// set the failsafe channel values to the current output values
|
||||
void setCustomFailsafe(uint8_t moduleIndex)
|
||||
{
|
||||
if (moduleIndex < NUM_MODULES) {
|
||||
for (int ch=0; ch<MAX_OUTPUT_CHANNELS; ch++) {
|
||||
if (ch < g_model.moduleData[moduleIndex].channelsStart || ch >= sentModuleChannels(moduleIndex) + g_model.moduleData[moduleIndex].channelsStart) {
|
||||
for (int ch = 0; ch < MAX_OUTPUT_CHANNELS; ch++) {
|
||||
if (ch < g_model.moduleData[moduleIndex].channelsStart ||
|
||||
ch >= sentModuleChannels(moduleIndex) +
|
||||
g_model.moduleData[moduleIndex].channelsStart) {
|
||||
g_model.failsafeChannels[ch] = 0;
|
||||
}
|
||||
else if (g_model.failsafeChannels[ch] < FAILSAFE_CHANNEL_HOLD) {
|
||||
} else if (g_model.failsafeChannels[ch] < FAILSAFE_CHANNEL_HOLD) {
|
||||
g_model.failsafeChannels[ch] = channelOutputs[ch];
|
||||
}
|
||||
}
|
||||
@@ -819,5 +582,5 @@ void setCustomFailsafe(uint8_t moduleIndex)
|
||||
|
||||
int32_t getChannelValue(uint8_t channel)
|
||||
{
|
||||
return channelOutputs[channel] + 2*PPM_CH_CENTER(channel) - 2*PPM_CENTER;
|
||||
return channelOutputs[channel] + 2 * PPM_CH_CENTER(channel) - 2 * PPM_CENTER;
|
||||
}
|
||||
|
||||
+45
-145
@@ -25,15 +25,13 @@
|
||||
#include "definitions.h"
|
||||
#include "dataconstants.h"
|
||||
#include "pulses_common.h"
|
||||
#include "pxx1.h"
|
||||
#include "pxx2.h"
|
||||
#include "multi.h"
|
||||
#include "afhds2.h"
|
||||
#include "afhds3_module.h"
|
||||
#include "modules_helpers.h"
|
||||
#include "ff.h"
|
||||
#include "hal/module_driver.h"
|
||||
|
||||
#if defined(PXX2)
|
||||
#include "pxx2.h"
|
||||
#include "pxx2_ota.h"
|
||||
#endif
|
||||
|
||||
#if defined(DSM2)
|
||||
#define IS_DSM2_PROTOCOL(protocol) (protocol>=PROTOCOL_CHANNELS_DSM2_LP45 && protocol<=PROTOCOL_CHANNELS_DSM2_DSMX)
|
||||
@@ -62,17 +60,16 @@
|
||||
#define IS_AFHDS3_PROTOCOL(protocol) (0)
|
||||
#endif
|
||||
|
||||
extern uint8_t s_pulses_paused;
|
||||
|
||||
typedef void (* ModuleCallback)();
|
||||
|
||||
PACK(struct ModuleState {
|
||||
uint8_t protocol;
|
||||
uint8_t mode:4;
|
||||
uint8_t paused:1;
|
||||
uint8_t forced_off:1;
|
||||
uint8_t spare:3;
|
||||
uint16_t counter;
|
||||
|
||||
#if defined(PXX2)
|
||||
// PXX specific items
|
||||
union
|
||||
{
|
||||
@@ -90,6 +87,7 @@ PACK(struct ModuleState {
|
||||
void writeModuleSettings(ModuleSettings * source);
|
||||
void readReceiverSettings(ReceiverSettings * destination);
|
||||
void writeReceiverSettings(ReceiverSettings * source);
|
||||
#endif
|
||||
});
|
||||
|
||||
extern ModuleState moduleState[NUM_MODULES];
|
||||
@@ -130,98 +128,13 @@ typedef Dsm2TimerPulsesData Dsm2PulsesData;
|
||||
#define SBUS_MAX_PERIOD 325 /*Overflows uint16_t if set higher*/
|
||||
#define SBUS_DEF_PERIOD 225
|
||||
#define SBUS_STEPSIZE 5 /* SBUS Step Size 0.5ms */
|
||||
#define SBUS_PERIOD_HALF_US ((g_model.moduleData[EXTERNAL_MODULE].sbus.refreshRate * SBUS_STEPSIZE + SBUS_DEF_PERIOD) * 200) /*half us*/
|
||||
#define SBUS_PERIOD (SBUS_PERIOD_HALF_US / 2) /*us*/
|
||||
#define SBUS_PERIOD_HALF_US(m) ((g_model.moduleData[m].sbus.refreshRate * SBUS_STEPSIZE + SBUS_DEF_PERIOD) * 200) /*half us*/
|
||||
#define SBUS_PERIOD(m) (SBUS_PERIOD_HALF_US(m) / 2) /*us*/
|
||||
#define MULTIMODULE_BAUDRATE 100000
|
||||
#define MULTIMODULE_PERIOD 7000 /*us*/
|
||||
|
||||
#define CROSSFIRE_FRAME_MAXLEN 64
|
||||
PACK(struct CrossfirePulsesData {
|
||||
uint8_t pulses[CROSSFIRE_FRAME_MAXLEN];
|
||||
uint8_t length;
|
||||
});
|
||||
|
||||
#define GHOST_FRAME_MAXLEN 16
|
||||
PACK(struct GhostPulsesData {
|
||||
uint8_t pulses[GHOST_FRAME_MAXLEN];
|
||||
uint8_t length;
|
||||
});
|
||||
|
||||
union InternalModulePulsesData {
|
||||
#if defined(INTERNAL_MODULE_PXX1)
|
||||
#if defined(INTMODULE_USART)
|
||||
UartPxx1Pulses pxx_uart;
|
||||
#else
|
||||
PwmPxx1Pulses pxx;
|
||||
#endif
|
||||
#endif
|
||||
|
||||
#if defined(INTERNAL_MODULE_PXX2)
|
||||
Pxx2Pulses pxx2;
|
||||
#endif
|
||||
|
||||
#if defined(INTERNAL_MODULE_AFHDS2A)
|
||||
FlySkySerialPulsesData flysky;
|
||||
#endif
|
||||
|
||||
#if defined(INTERNAL_MODULE_MULTI)
|
||||
UartMultiPulses multi;
|
||||
#endif
|
||||
|
||||
#if defined(INTERNAL_MODULE_CRSF)
|
||||
CrossfirePulsesData crossfire;
|
||||
#endif
|
||||
|
||||
#if defined(INTERNAL_MODULE_AFHDS3)
|
||||
afhds3::IntmoduleData afhds3;
|
||||
#endif
|
||||
|
||||
#if defined(INTERNAL_MODULE_PPM)
|
||||
PpmPulsesData<pulse_duration_t> ppm;
|
||||
#endif
|
||||
|
||||
} __ALIGNED(4);
|
||||
|
||||
union ExternalModulePulsesData {
|
||||
#if defined(PXX1)
|
||||
#if defined(HARDWARE_EXTERNAL_MODULE_SIZE_SML)
|
||||
UartPxx1Pulses pxx_uart;
|
||||
#endif
|
||||
PwmPxx1Pulses pxx;
|
||||
#endif
|
||||
|
||||
#if defined(PXX2)
|
||||
Pxx2Pulses pxx2;
|
||||
#endif
|
||||
|
||||
#if defined(DSM2) || defined(MULTIMODULE) || defined(SBUS)
|
||||
Dsm2PulsesData dsm2;
|
||||
#endif
|
||||
|
||||
#if defined(AFHDS3)
|
||||
afhds3::ExtmoduleData afhds3;
|
||||
#endif
|
||||
|
||||
PpmPulsesData<pulse_duration_t> ppm;
|
||||
|
||||
#if defined(CROSSFIRE)
|
||||
CrossfirePulsesData crossfire;
|
||||
#endif
|
||||
|
||||
#if defined(GHOST)
|
||||
GhostPulsesData ghost;
|
||||
#endif
|
||||
} __ALIGNED(4);
|
||||
|
||||
/* The __ALIGNED keyword is required to align the struct inside the modulePulsesData below,
|
||||
* which is also defined to be __DMA (which includes __ALIGNED) aligned.
|
||||
* Arrays in C/C++ are always defined to be *contiguously*. The first byte of the second element is therefore always
|
||||
* sizeof(ModulePulsesData). __ALIGNED is required for sizeof(ModulePulsesData) to be a multiple of the alignment.
|
||||
*/
|
||||
|
||||
|
||||
extern InternalModulePulsesData intmodulePulsesData;
|
||||
extern ExternalModulePulsesData extmodulePulsesData;
|
||||
|
||||
union TrainerPulsesData {
|
||||
PpmPulsesData<trainer_pulse_duration_t> ppm;
|
||||
@@ -229,63 +142,50 @@ union TrainerPulsesData {
|
||||
|
||||
extern TrainerPulsesData trainerPulsesData;
|
||||
|
||||
#if defined(HARDWARE_INTERNAL_MODULE)
|
||||
bool setupPulsesInternalModule();
|
||||
void stopPulsesInternalModule();
|
||||
void intmoduleSendNextFrame();
|
||||
const etx_module_driver_t* getIntModuleDriver();
|
||||
void* getIntModuleCtx();
|
||||
#if !defined(AFHDS3)
|
||||
#define MODULE_BUFFER_SIZE 64
|
||||
#else
|
||||
#define MODULE_BUFFER_SIZE 128
|
||||
#endif
|
||||
#if defined(HARDWARE_EXTERNAL_MODULE)
|
||||
bool setupPulsesExternalModule();
|
||||
void stopPulsesExternalModule();
|
||||
void extmoduleSendNextFrame();
|
||||
const etx_module_driver_t* getExtModuleDriver();
|
||||
void* getExtModuleCtx();
|
||||
#endif
|
||||
void restartModule(uint8_t idx);
|
||||
void setupPulsesDSM2();
|
||||
void setupPulsesLemonDSMP();
|
||||
void setupPulsesCrossfire(uint8_t idx);
|
||||
void setupPulsesGhost();
|
||||
void setupPulsesMultiExternalModule();
|
||||
void setupPulsesSbus();
|
||||
void setupPulsesPPMInternalModule();
|
||||
void setupPulsesPPMExternalModule();
|
||||
|
||||
struct module_pulse_buffer {
|
||||
uint8_t _buffer[MODULE_BUFFER_SIZE];
|
||||
};
|
||||
|
||||
struct module_pulse_driver {
|
||||
module_pulse_buffer buffer;
|
||||
const etx_proto_driver_t* drv;
|
||||
void* ctx;
|
||||
};
|
||||
|
||||
module_pulse_driver* pulsesGetModuleDriver(uint8_t module);
|
||||
uint8_t* pulsesGetModuleBuffer(uint8_t module);
|
||||
|
||||
void pulsesStopModule(uint8_t module);
|
||||
void pulsesSendNextFrame(uint8_t module);
|
||||
void pulsesSendChannels();
|
||||
|
||||
typedef void (*module_init_cb_t)(uint8_t, const etx_proto_driver_t*);
|
||||
typedef void (*module_deinit_cb_t)(uint8_t, const etx_proto_driver_t*);
|
||||
|
||||
void pulsesSetModuleInitCb(module_init_cb_t cb);
|
||||
void pulsesSetModuleDeInitCb(module_deinit_cb_t cb);
|
||||
|
||||
void restartModule(uint8_t module);
|
||||
bool restartModuleAsync(uint8_t module, uint8_t cnt_delay);
|
||||
|
||||
void setupPulsesPPMTrainer();
|
||||
void putDsm2Flush();
|
||||
void sendByteSbus(uint8_t b);
|
||||
void intmodulePpmStart();
|
||||
void intmodulePxx1PulsesStart();
|
||||
void intmodulePxx1SerialStart();
|
||||
void extmodulePxx1PulsesStart();
|
||||
void extmodulePpmStart();
|
||||
void intmoduleStop();
|
||||
void extmoduleStop();
|
||||
|
||||
void getModuleStatusString(uint8_t moduleIdx, char * statusText);
|
||||
void getModuleSyncStatusString(uint8_t moduleIdx, char * statusText);
|
||||
|
||||
#if defined(AFHDS3)
|
||||
uint8_t actualAfhdsRunPower(int moduleIndex);
|
||||
#endif
|
||||
void extramodulePpmStart();
|
||||
|
||||
void startPulses();
|
||||
void stopPulses();
|
||||
|
||||
inline bool pulsesStarted()
|
||||
{
|
||||
return moduleState[0].protocol != PROTOCOL_CHANNELS_UNINITIALIZED;
|
||||
}
|
||||
|
||||
inline void pausePulses()
|
||||
{
|
||||
s_pulses_paused = true;
|
||||
}
|
||||
|
||||
inline void resumePulses()
|
||||
{
|
||||
s_pulses_paused = false;
|
||||
}
|
||||
void pulsesInit();
|
||||
void pulsesStart();
|
||||
void pulsesStop();
|
||||
|
||||
inline void SEND_FAILSAFE_NOW(uint8_t idx)
|
||||
{
|
||||
|
||||
+14
-96
@@ -19,10 +19,7 @@
|
||||
* GNU General Public License for more details.
|
||||
*/
|
||||
|
||||
#ifndef _PULSES_PXX_H_
|
||||
#define _PULSES_PXX_H_
|
||||
|
||||
#include "pulses_common.h"
|
||||
#pragma once
|
||||
|
||||
#define PXX_SEND_BIND 0x01
|
||||
#define PXX_SEND_FAILSAFE (1 << 4)
|
||||
@@ -30,100 +27,21 @@
|
||||
|
||||
#define PXX2_LOWSPEED_BAUDRATE 230400
|
||||
#define PXX2_HIGHSPEED_BAUDRATE 450000
|
||||
#define PXX2_NO_HEARTBEAT_PERIOD 4000/*us*/
|
||||
#define PXX2_MAX_HEARTBEAT_PERIOD (9000 + 1000)/*us longest period (isrm/accst) + 1ms heartbeat backup*/
|
||||
#if defined(INTMODULE_HEARTBEAT)
|
||||
#define PXX2_PERIOD PXX2_MAX_HEARTBEAT_PERIOD
|
||||
#else
|
||||
#define PXX2_PERIOD PXX2_NO_HEARTBEAT_PERIOD
|
||||
#endif
|
||||
|
||||
/* microseconds */
|
||||
#define PXX2_DEFAULT_PERIOD 4000
|
||||
|
||||
/* microseconds longest period (isrm/accst) + 1ms heartbeat backup*/
|
||||
#define PXX2_MAX_HEARTBEAT_PERIOD (9000 + 1000)
|
||||
|
||||
#define PXX2_TOOLS_PERIOD 1000/*us*/
|
||||
#define PXX2_FRAME_MAXLENGTH 64
|
||||
|
||||
#define PXX_PULSES_PERIOD 9000/*us*/
|
||||
#define PXX1_DEFAULT_PERIOD 9000/*us*/
|
||||
#define PXX1_DEFAULT_SERIAL_BAUDRATE 115200
|
||||
|
||||
#define PXX1_FAST_PERIOD 4000/*us*/
|
||||
#define PXX1_FAST_SERIAL_BAUDRATE 450000
|
||||
|
||||
#define EXTMODULE_PXX1_SERIAL_PERIOD 4000/*us*/
|
||||
#define EXTMODULE_PXX1_SERIAL_BAUDRATE 420000
|
||||
|
||||
#if defined(PXX_FREQUENCY_HIGH)
|
||||
#define INTMODULE_PXX1_SERIAL_BAUDRATE 450000
|
||||
#if defined(INTMODULE_HEARTBEAT)
|
||||
// use backup trigger (1 ms later)
|
||||
#define INTMODULE_PXX1_SERIAL_PERIOD (4000 + 1000)/*us*/
|
||||
#else
|
||||
#define INTMODULE_PXX1_SERIAL_PERIOD 4000/*us*/
|
||||
#endif
|
||||
#else
|
||||
#define INTMODULE_PXX1_SERIAL_BAUDRATE 115200
|
||||
#if defined(INTMODULE_HEARTBEAT)
|
||||
// use backup trigger (1 ms later)
|
||||
#define INTMODULE_PXX1_SERIAL_PERIOD (9000 + 1000)/*us*/
|
||||
#else
|
||||
#define INTMODULE_PXX1_SERIAL_PERIOD 9000/*us*/
|
||||
#endif
|
||||
#endif
|
||||
|
||||
// Used by the Sky9x family boards
|
||||
class SerialPxxBitTransport: public DataBuffer<uint8_t, 64> {
|
||||
protected:
|
||||
uint8_t byte;
|
||||
uint8_t bits_count;
|
||||
|
||||
void initFrame(uint32_t period)
|
||||
{
|
||||
initBuffer();
|
||||
byte = 0;
|
||||
bits_count = 0;
|
||||
}
|
||||
|
||||
void addSerialBit(uint8_t bit)
|
||||
{
|
||||
byte >>= 1;
|
||||
if (bit & 1) {
|
||||
byte |= 0x80;
|
||||
}
|
||||
if (++bits_count >= 8) {
|
||||
*ptr++ = byte;
|
||||
bits_count = 0;
|
||||
}
|
||||
}
|
||||
|
||||
// 8uS/bit 01 = 0, 001 = 1
|
||||
void addPart(uint8_t value)
|
||||
{
|
||||
addSerialBit(0);
|
||||
if (value) {
|
||||
addSerialBit(0);
|
||||
}
|
||||
addSerialBit(1);
|
||||
}
|
||||
|
||||
void addTail()
|
||||
{
|
||||
while (bits_count != 0) {
|
||||
addSerialBit(1);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
class PwmPxxBitTransport: public PulsesBuffer<pulse_duration_t, 200> {
|
||||
protected:
|
||||
|
||||
void initFrame(uint32_t period)
|
||||
{
|
||||
initBuffer();
|
||||
}
|
||||
|
||||
void addPart(uint8_t value)
|
||||
{
|
||||
pulse_duration_t duration = value ? 47 : 31;
|
||||
*ptr++ = duration;
|
||||
}
|
||||
|
||||
void addTail()
|
||||
{
|
||||
// rest min value is 18000 - 200 * 48 = 8400 (4.2ms)
|
||||
*(ptr - 1) = 255;
|
||||
}
|
||||
};
|
||||
|
||||
#endif
|
||||
|
||||
+172
-109
@@ -19,10 +19,21 @@
|
||||
* GNU General Public License for more details.
|
||||
*/
|
||||
|
||||
#include "opentx.h"
|
||||
#include "pulses/pxx1.h"
|
||||
#include "mixer_scheduler.h"
|
||||
#include "hal/module_port.h"
|
||||
#include "heartbeat_driver.h"
|
||||
#include "mixer_scheduler.h"
|
||||
|
||||
#include "pxx1_transport.h"
|
||||
#include "pxx1.h"
|
||||
|
||||
#include "opentx.h"
|
||||
|
||||
static uint32_t _pxx1_internal_baudrate = PXX1_DEFAULT_SERIAL_BAUDRATE;
|
||||
|
||||
void pxx1SetInternalBaudrate(uint32_t baudrate)
|
||||
{
|
||||
_pxx1_internal_baudrate = baudrate;
|
||||
}
|
||||
|
||||
template <class PxxTransport>
|
||||
void Pxx1Pulses<PxxTransport>::addFlag1(uint8_t module, uint8_t sendFailsafe)
|
||||
@@ -63,10 +74,12 @@ void Pxx1Pulses<PxxTransport>::addExtraFlags(uint8_t module)
|
||||
extraFlags |= (1 << 6);
|
||||
}
|
||||
|
||||
#if defined(HARDWARE_EXTERNAL_MODULE) && defined(HARDWARE_INTERNAL_MODULE)
|
||||
// Disable S.PORT if internal module is active
|
||||
if (module == EXTERNAL_MODULE && isSportLineUsedByInternalModule()) {
|
||||
extraFlags |= (1 << 5);
|
||||
}
|
||||
#endif
|
||||
|
||||
PxxTransport::addByte(extraFlags);
|
||||
}
|
||||
@@ -175,17 +188,25 @@ void Pxx1Pulses<PxxTransport>::add8ChannelsFrame(uint8_t module, uint8_t sendUpp
|
||||
}
|
||||
|
||||
template <class PxxTransport>
|
||||
void Pxx1Pulses<PxxTransport>::setupFrame(uint8_t module)
|
||||
Pxx1Pulses<PxxTransport>::Pxx1Pulses(uint8_t* buffer)
|
||||
: PxxTransport(buffer)
|
||||
{
|
||||
}
|
||||
|
||||
template <class PxxTransport>
|
||||
void Pxx1Pulses<PxxTransport>::setupFrame(uint8_t module, Pxx1Type type)
|
||||
{
|
||||
uint8_t sendUpperChannels = 0;
|
||||
uint8_t sendFailsafe = 0;
|
||||
|
||||
PxxTransport::initFrame(PXX_PULSES_PERIOD);
|
||||
|
||||
#if defined(PXX_FREQUENCY_HIGH)
|
||||
if (moduleState[module].protocol == PROTOCOL_CHANNELS_PXX1_SERIAL) {
|
||||
// Fast serial PXX1 (either X-lite internal or R9M-lite):
|
||||
// - up to 16 channels sent in the same frame
|
||||
//
|
||||
if (type == Pxx1Type::FAST_SERIAL) {
|
||||
if (moduleState[module].counter-- == 0) {
|
||||
sendFailsafe = (g_model.moduleData[module].failsafeMode != FAILSAFE_NOT_SET && g_model.moduleData[module].failsafeMode != FAILSAFE_RECEIVER);
|
||||
sendFailsafe = (g_model.moduleData[module].failsafeMode != FAILSAFE_NOT_SET &&
|
||||
g_model.moduleData[module].failsafeMode != FAILSAFE_RECEIVER);
|
||||
moduleState[module].counter = 1000;
|
||||
}
|
||||
add8ChannelsFrame(module, 0, sendFailsafe);
|
||||
@@ -194,8 +215,11 @@ void Pxx1Pulses<PxxTransport>::setupFrame(uint8_t module)
|
||||
}
|
||||
return;
|
||||
}
|
||||
#endif
|
||||
|
||||
// Slow PXX1:
|
||||
// - if more than 8 channels shall be sent, it happens
|
||||
// in alternating frames
|
||||
//
|
||||
if (moduleState[module].counter & 0x01) {
|
||||
// channelsCount is shifted by 8
|
||||
sendUpperChannels = g_model.moduleData[module].channelsCount;
|
||||
@@ -207,7 +231,8 @@ void Pxx1Pulses<PxxTransport>::setupFrame(uint8_t module)
|
||||
}
|
||||
} else {
|
||||
if (moduleState[module].counter == 0) {
|
||||
sendFailsafe = (g_model.moduleData[module].failsafeMode != FAILSAFE_NOT_SET && g_model.moduleData[module].failsafeMode != FAILSAFE_RECEIVER);
|
||||
sendFailsafe = (g_model.moduleData[module].failsafeMode != FAILSAFE_NOT_SET &&
|
||||
g_model.moduleData[module].failsafeMode != FAILSAFE_RECEIVER);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -218,129 +243,167 @@ void Pxx1Pulses<PxxTransport>::setupFrame(uint8_t module)
|
||||
}
|
||||
}
|
||||
|
||||
template class Pxx1Pulses<StandardPxx1Transport<PwmPxxBitTransport> >;
|
||||
template class Pxx1Pulses<StandardPxx1Transport<SerialPxxBitTransport> >;
|
||||
template class Pxx1Pulses<StandardPxx1Transport>;
|
||||
template class Pxx1Pulses<UartPxx1Transport>;
|
||||
|
||||
#if defined(INTMODULE_USART) && defined(INTERNAL_MODULE_PXX1)
|
||||
#include "intmodule_serial_driver.h"
|
||||
|
||||
static const etx_serial_init pxx1SerialInit = {
|
||||
.baudrate = INTMODULE_PXX1_SERIAL_BAUDRATE,
|
||||
.parity = ETX_Parity_None,
|
||||
.stop_bits = ETX_StopBits_One,
|
||||
.word_length = ETX_WordLength_8,
|
||||
.rx_enable = false,
|
||||
static const etx_serial_init pxx1SerialCfg = {
|
||||
.baudrate = 0,
|
||||
.encoding = ETX_Encoding_8N1,
|
||||
.direction = ETX_Dir_TX,
|
||||
.polarity = ETX_Pol_Normal,
|
||||
};
|
||||
|
||||
static void* pxx1InitInternal(uint8_t module)
|
||||
static const etx_serial_driver_t* _sport_drv;
|
||||
static void* _sport_ctx;
|
||||
|
||||
static void pxx1SportSensorPolling()
|
||||
{
|
||||
void* uart_ctx = IntmoduleSerialDriver.init(&pxx1SerialInit);
|
||||
if (outputTelemetryBuffer.destination != TELEMETRY_ENDPOINT_SPORT)
|
||||
return;
|
||||
|
||||
#if defined(INTMODULE_HEARTBEAT)
|
||||
init_intmodule_heartbeat();
|
||||
#endif
|
||||
mixerSchedulerSetPeriod(INTERNAL_MODULE, INTMODULE_PXX1_SERIAL_PERIOD);
|
||||
INTERNAL_MODULE_ON();
|
||||
// Match sensor polling from the module
|
||||
// -> detect <0x7E [Physical ID]> as the last sequence
|
||||
uint8_t b;
|
||||
if (_sport_drv->getLastByte(_sport_ctx, 2, &b) <= 0 || b != START_STOP)
|
||||
return;
|
||||
if (_sport_drv->getLastByte(_sport_ctx, 1, &b) <= 0 ||
|
||||
b != outputTelemetryBuffer.sport.physicalId)
|
||||
return;
|
||||
|
||||
return uart_ctx;
|
||||
_sport_drv->sendBuffer(_sport_ctx,
|
||||
outputTelemetryBuffer.data + 1,
|
||||
outputTelemetryBuffer.size - 1);
|
||||
|
||||
outputTelemetryBuffer.reset();
|
||||
}
|
||||
|
||||
static void pxx1DeInitInternal(void* context)
|
||||
static void* pxx1Init(uint8_t module)
|
||||
{
|
||||
INTERNAL_MODULE_OFF();
|
||||
mixerSchedulerSetPeriod(INTERNAL_MODULE, 0);
|
||||
#if defined(INTMODULE_HEARTBEAT)
|
||||
stop_intmodule_heartbeat();
|
||||
#endif
|
||||
IntmoduleSerialDriver.deinit(context);
|
||||
etx_module_state_t* mod_st = nullptr;
|
||||
etx_serial_init txCfg(pxx1SerialCfg);
|
||||
|
||||
if (module == INTERNAL_MODULE) {
|
||||
|
||||
txCfg.baudrate = _pxx1_internal_baudrate;
|
||||
mod_st = modulePortInitSerial(module, ETX_MOD_PORT_UART, &txCfg);
|
||||
|
||||
if (!mod_st) {
|
||||
// assume that radios that don't have an internal UART
|
||||
// will have a module that uses legacy PXX1 (PWM)
|
||||
txCfg.encoding = ETX_Encoding_PXX1_PWM;
|
||||
mod_st = modulePortInitSerial(module, ETX_MOD_PORT_SOFT_INV, &txCfg);
|
||||
}
|
||||
|
||||
if (!mod_st) return nullptr;
|
||||
}
|
||||
|
||||
if (module == EXTERNAL_MODULE) {
|
||||
|
||||
// Init driver (timer / serial) based on module type
|
||||
uint8_t type = g_model.moduleData[module].type;
|
||||
switch(type) {
|
||||
|
||||
case MODULE_TYPE_R9M_LITE_PXX1: {
|
||||
txCfg.baudrate = EXTMODULE_PXX1_SERIAL_BAUDRATE;
|
||||
mod_st = modulePortInitSerial(module, ETX_MOD_PORT_UART, &txCfg);
|
||||
if (!mod_st) return nullptr;
|
||||
} break;
|
||||
|
||||
case MODULE_TYPE_XJT_PXX1:
|
||||
case MODULE_TYPE_R9M_PXX1: {
|
||||
txCfg.encoding = ETX_Encoding_PXX1_PWM;
|
||||
mod_st = modulePortInitSerial(module, ETX_MOD_PORT_SOFT_INV, &txCfg);
|
||||
if (!mod_st) return nullptr;
|
||||
} break;
|
||||
|
||||
default:
|
||||
// unknown module: bail out!
|
||||
return nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
// Init telemetry RX
|
||||
etx_serial_init rxCfg(pxx1SerialCfg);
|
||||
rxCfg.baudrate = FRSKY_SPORT_BAUDRATE;
|
||||
rxCfg.direction = ETX_Dir_TX_RX;
|
||||
|
||||
// TODO: handle init errors properly
|
||||
if (modulePortInitSerial(module, ETX_MOD_PORT_SPORT, &rxCfg) != nullptr) {
|
||||
auto drv = modulePortGetSerialDrv(mod_st->rx);
|
||||
auto ctx = modulePortGetCtx(mod_st->rx);
|
||||
if (drv && ctx && drv->setIdleCb) {
|
||||
_sport_drv = drv;
|
||||
_sport_ctx = ctx;
|
||||
drv->setIdleCb(ctx, pxx1SportSensorPolling);
|
||||
}
|
||||
}
|
||||
|
||||
// Store PXX1 type in 'user_data'
|
||||
if (txCfg.encoding == ETX_Encoding_PXX1_PWM ||
|
||||
txCfg.baudrate == PXX1_DEFAULT_SERIAL_BAUDRATE) {
|
||||
|
||||
// Legacy / slow PXX1
|
||||
mixerSchedulerSetPeriod(module, PXX1_DEFAULT_PERIOD);
|
||||
if (txCfg.encoding == ETX_Encoding_PXX1_PWM) {
|
||||
mod_st->user_data = (void*)Pxx1Type::PWM;
|
||||
} else {
|
||||
mod_st->user_data = (void*)Pxx1Type::SLOW_SERIAL;
|
||||
}
|
||||
} else {
|
||||
// Fast PXX1
|
||||
mixerSchedulerSetPeriod(module, PXX1_FAST_PERIOD);
|
||||
mod_st->user_data = (void*)Pxx1Type::FAST_SERIAL;
|
||||
}
|
||||
|
||||
return mod_st;
|
||||
}
|
||||
|
||||
static void pxx1SetupPulsesInternal(void* context, int16_t* channels, uint8_t nChannels)
|
||||
static void pxx1DeInit(void* ctx)
|
||||
{
|
||||
(void)context;
|
||||
|
||||
// TODO:
|
||||
(void)channels;
|
||||
(void)nChannels;
|
||||
intmodulePulsesData.pxx_uart.setupFrame(INTERNAL_MODULE);
|
||||
auto mod_st = (etx_module_state_t*)ctx;
|
||||
modulePortDeInit(mod_st);
|
||||
}
|
||||
|
||||
static void pxx1SendPulsesInternal(void* context)
|
||||
static void pxx1SendPulses(void* ctx, uint8_t* buffer, int16_t* channels, uint8_t nChannels)
|
||||
{
|
||||
IntmoduleSerialDriver.sendBuffer(context, intmodulePulsesData.pxx_uart.getData(),
|
||||
intmodulePulsesData.pxx_uart.getSize());
|
||||
}
|
||||
|
||||
const etx_module_driver_t Pxx1InternalSerialDriver = {
|
||||
.protocol = PROTOCOL_CHANNELS_PXX1_SERIAL,
|
||||
.init = pxx1InitInternal,
|
||||
.deinit = pxx1DeInitInternal,
|
||||
.setupPulses = pxx1SetupPulsesInternal,
|
||||
.sendPulses = pxx1SendPulsesInternal,
|
||||
};
|
||||
|
||||
#endif
|
||||
|
||||
#if defined(EXTMODULE_USART) && defined(HARDWARE_EXTERNAL_MODULE_SIZE_SML)
|
||||
#include "extmodule_serial_driver.h"
|
||||
|
||||
static const etx_serial_init pxx1ExtSerialInit = {
|
||||
.baudrate = EXTMODULE_PXX1_SERIAL_BAUDRATE,
|
||||
.parity = ETX_Parity_None,
|
||||
.stop_bits = ETX_StopBits_One,
|
||||
.word_length = ETX_WordLength_8,
|
||||
.rx_enable = false,
|
||||
};
|
||||
|
||||
static void* pxx1InitExternal(uint8_t module)
|
||||
{
|
||||
auto drv = extmoduleGetSerialPort();
|
||||
if (!drv) return nullptr;
|
||||
|
||||
void* uart_ctx = drv->init(&pxx1ExtSerialInit);
|
||||
|
||||
mixerSchedulerSetPeriod(EXTERNAL_MODULE, EXTMODULE_PXX1_SERIAL_PERIOD);
|
||||
EXTERNAL_MODULE_ON();
|
||||
|
||||
return uart_ctx;
|
||||
}
|
||||
|
||||
static void pxx1DeInitExternal(void* context)
|
||||
{
|
||||
EXTERNAL_MODULE_OFF();
|
||||
mixerSchedulerSetPeriod(EXTERNAL_MODULE, 0);
|
||||
|
||||
auto drv = extmoduleGetSerialPort();
|
||||
if (drv) drv->deinit(context);
|
||||
}
|
||||
|
||||
static void pxx1SetupPulsesExternal(void* context, int16_t* channels, uint8_t nChannels)
|
||||
{
|
||||
(void)context;
|
||||
|
||||
// TODO:
|
||||
(void)channels;
|
||||
(void)nChannels;
|
||||
|
||||
extmodulePulsesData.pxx_uart.setupFrame(EXTERNAL_MODULE);
|
||||
auto mod_st = (etx_module_state_t*)ctx;
|
||||
auto module = modulePortGetModule(mod_st);
|
||||
auto pxx1_type = (Pxx1Type)(uintptr_t)mod_st->user_data;
|
||||
|
||||
uint32_t frame_len = 0;
|
||||
if (pxx1_type == Pxx1Type::PWM) {
|
||||
PwmPxx1Pulses frame(buffer);
|
||||
frame.setupFrame(module, pxx1_type);
|
||||
frame_len = frame.getSize();
|
||||
} else {
|
||||
UartPxx1Pulses frame(buffer);
|
||||
frame.setupFrame(module, pxx1_type);
|
||||
frame_len = frame.getSize();
|
||||
}
|
||||
|
||||
if (frame_len == 0) return;
|
||||
|
||||
auto drv = modulePortGetSerialDrv(mod_st->tx);
|
||||
auto drv_ctx = modulePortGetCtx(mod_st->tx);
|
||||
drv->sendBuffer(drv_ctx, buffer, frame_len);
|
||||
}
|
||||
|
||||
static void pxx1SendPulsesExternal(void* context)
|
||||
static void pxx1ProcessData(void* ctx, uint8_t data, uint8_t* buffer, uint8_t* len)
|
||||
{
|
||||
auto drv = extmoduleGetSerialPort();
|
||||
if (!drv) return;
|
||||
auto mod_st = (etx_module_state_t*)ctx;
|
||||
auto module = modulePortGetModule(mod_st);
|
||||
|
||||
drv->sendBuffer(context, extmodulePulsesData.pxx_uart.getData(),
|
||||
extmodulePulsesData.pxx_uart.getSize());
|
||||
processFrskySportTelemetryData(module, data, buffer, *len);
|
||||
}
|
||||
|
||||
const etx_module_driver_t Pxx1ExternalSerialDriver = {
|
||||
.protocol = PROTOCOL_CHANNELS_PXX1_SERIAL,
|
||||
.init = pxx1InitExternal,
|
||||
.deinit = pxx1DeInitExternal,
|
||||
.setupPulses = pxx1SetupPulsesExternal,
|
||||
.sendPulses = pxx1SendPulsesExternal,
|
||||
const etx_proto_driver_t Pxx1Driver = {
|
||||
.protocol = PROTOCOL_CHANNELS_PXX1,
|
||||
.init = pxx1Init,
|
||||
.deinit = pxx1DeInit,
|
||||
.sendPulses = pxx1SendPulses,
|
||||
.processData = pxx1ProcessData,
|
||||
};
|
||||
|
||||
#endif
|
||||
|
||||
+4
-153
@@ -19,160 +19,11 @@
|
||||
* GNU General Public License for more details.
|
||||
*/
|
||||
|
||||
#ifndef _PULSES_PXX1_H_
|
||||
#define _PULSES_PXX1_H_
|
||||
|
||||
#include "pxx.h"
|
||||
#include "crc.h"
|
||||
#pragma once
|
||||
|
||||
#include "hal/module_driver.h"
|
||||
|
||||
class Pxx1CrcMixin {
|
||||
protected:
|
||||
void initCrc()
|
||||
{
|
||||
crc = 0;
|
||||
}
|
||||
// Set the board specific PXX1 baudrate (X-lite only?)
|
||||
void pxx1SetInternalBaudrate(uint32_t baudrate);
|
||||
|
||||
void addToCrc(uint8_t byte)
|
||||
{
|
||||
crc = (crc << 8) ^ (crc16tab[CRC_1189][((crc >> 8) ^ byte) & 0xFF]);
|
||||
}
|
||||
|
||||
uint16_t crc;
|
||||
};
|
||||
|
||||
template <class BitTransport>
|
||||
class StandardPxx1Transport: public BitTransport, public Pxx1CrcMixin {
|
||||
protected:
|
||||
uint8_t ones_count;
|
||||
|
||||
void initFrame(uint32_t period)
|
||||
{
|
||||
BitTransport::initFrame(period);
|
||||
ones_count = 0;
|
||||
}
|
||||
|
||||
void addByte(uint8_t byte)
|
||||
{
|
||||
Pxx1CrcMixin::addToCrc(byte);
|
||||
addByteWithoutCrc(byte);
|
||||
};
|
||||
|
||||
void addRawByte(uint8_t byte)
|
||||
{
|
||||
for (uint8_t i = 0; i < 8; i++) {
|
||||
if (byte & 0x80)
|
||||
BitTransport::addPart(1);
|
||||
else
|
||||
BitTransport::addPart(0);
|
||||
byte <<= 1;
|
||||
}
|
||||
}
|
||||
|
||||
void addByteWithoutCrc(uint8_t byte)
|
||||
{
|
||||
for (uint8_t i = 0; i < 8; i++) {
|
||||
addBit(byte & 0x80);
|
||||
byte <<= 1;
|
||||
}
|
||||
}
|
||||
|
||||
void addBit(uint8_t bit)
|
||||
{
|
||||
if (bit) {
|
||||
BitTransport::addPart(1);
|
||||
if (++ones_count == 5) {
|
||||
ones_count = 0;
|
||||
BitTransport::addPart(0); // Stuff a 0 bit in
|
||||
}
|
||||
}
|
||||
else {
|
||||
BitTransport::addPart(0);
|
||||
ones_count = 0;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
class UartPxx1Transport: public DataBuffer<uint8_t, 64>, public Pxx1CrcMixin {
|
||||
protected:
|
||||
void initFrame(uint32_t period)
|
||||
{
|
||||
initBuffer();
|
||||
}
|
||||
|
||||
void addByte(uint8_t byte)
|
||||
{
|
||||
Pxx1CrcMixin::addToCrc(byte);
|
||||
addWithByteStuffing(byte);
|
||||
}
|
||||
|
||||
void addRawByte(uint8_t byte)
|
||||
{
|
||||
*ptr++ = byte;
|
||||
}
|
||||
|
||||
void addByteWithoutCrc(uint8_t byte)
|
||||
{
|
||||
addWithByteStuffing(byte);
|
||||
}
|
||||
|
||||
void addWithByteStuffing(uint8_t byte)
|
||||
{
|
||||
if (0x7E == byte) {
|
||||
addRawByte(0x7D);
|
||||
addRawByte(0x5E);
|
||||
}
|
||||
else if (0x7D == byte) {
|
||||
addRawByte(0x7D);
|
||||
addRawByte(0x5D);
|
||||
}
|
||||
else {
|
||||
addRawByte(byte);
|
||||
}
|
||||
}
|
||||
|
||||
void addTail()
|
||||
{
|
||||
// nothing
|
||||
}
|
||||
};
|
||||
|
||||
template <class PxxTransport>
|
||||
class Pxx1Pulses: public PxxTransport
|
||||
{
|
||||
public:
|
||||
void setupFrame(uint8_t port);
|
||||
|
||||
protected:
|
||||
void addHead()
|
||||
{
|
||||
// send 7E, do not CRC
|
||||
PxxTransport::addRawByte(0x7E);
|
||||
}
|
||||
|
||||
void addCrc()
|
||||
{
|
||||
PxxTransport::addByteWithoutCrc(Pxx1CrcMixin::crc >> 8);
|
||||
PxxTransport::addByteWithoutCrc(Pxx1CrcMixin::crc);
|
||||
}
|
||||
|
||||
void addFlag1(uint8_t port, uint8_t sendFailsafe);
|
||||
void addExtraFlags(uint8_t port);
|
||||
void addChannels(uint8_t port, uint8_t sendFailsafe, uint8_t sendUpperChannels);
|
||||
void add8ChannelsFrame(uint8_t port, uint8_t sendUpperChannels, uint8_t sendFailsafe);
|
||||
};
|
||||
|
||||
typedef Pxx1Pulses<UartPxx1Transport> UartPxx1Pulses;
|
||||
typedef Pxx1Pulses<StandardPxx1Transport<PwmPxxBitTransport>> PwmPxx1Pulses;
|
||||
typedef Pxx1Pulses<StandardPxx1Transport<SerialPxxBitTransport>> SerialPxx1Pulses;
|
||||
|
||||
#if defined(INTMODULE_USART)
|
||||
extern const etx_module_driver_t Pxx1InternalSerialDriver;
|
||||
#endif
|
||||
|
||||
#if defined(EXTMODULE_USART)
|
||||
extern const etx_module_driver_t Pxx1ExternalSerialDriver;
|
||||
#endif
|
||||
|
||||
#endif
|
||||
extern const etx_proto_driver_t Pxx1Driver;
|
||||
|
||||
@@ -0,0 +1,212 @@
|
||||
/*
|
||||
* Copyright (C) EdgeTX
|
||||
*
|
||||
* Based on code named
|
||||
* opentx - https://github.com/opentx/opentx
|
||||
* 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.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "pxx.h"
|
||||
#include "crc.h"
|
||||
|
||||
class Pxx1CrcMixin {
|
||||
protected:
|
||||
void initCrc()
|
||||
{
|
||||
crc = 0;
|
||||
}
|
||||
|
||||
void addToCrc(uint8_t byte)
|
||||
{
|
||||
crc = (crc << 8) ^ (crc16tab[CRC_1189][((crc >> 8) ^ byte) & 0xFF]);
|
||||
}
|
||||
|
||||
uint16_t crc;
|
||||
};
|
||||
|
||||
class StandardPxx1Transport: public Pxx1CrcMixin {
|
||||
protected:
|
||||
const uint8_t* buffer;
|
||||
uint8_t* ptr;
|
||||
uint8_t byte = 0;
|
||||
uint8_t bits_count = 0;
|
||||
uint8_t ones_count = 0;
|
||||
|
||||
StandardPxx1Transport(uint8_t* buffer)
|
||||
: buffer(buffer), ptr(buffer)
|
||||
{
|
||||
}
|
||||
|
||||
void addByte(uint8_t b)
|
||||
{
|
||||
Pxx1CrcMixin::addToCrc(b);
|
||||
addByteWithoutCrc(b);
|
||||
};
|
||||
|
||||
void addRawByte(uint8_t b)
|
||||
{
|
||||
for (uint8_t i = 0; i < 8; i++) {
|
||||
// MSB first
|
||||
if (b & 0x80) addPart(1);
|
||||
else addPart(0);
|
||||
b <<= 1;
|
||||
}
|
||||
}
|
||||
|
||||
void addByteWithoutCrc(uint8_t b)
|
||||
{
|
||||
for (uint8_t i = 0; i < 8; i++) {
|
||||
// MSB first
|
||||
addBit(b & 0x80);
|
||||
b <<= 1;
|
||||
}
|
||||
}
|
||||
|
||||
void addBit(uint8_t bit)
|
||||
{
|
||||
if (bit) {
|
||||
addPart(1);
|
||||
if (++ones_count == 5) {
|
||||
ones_count = 0;
|
||||
addPart(0); // Stuff a 0 bit in
|
||||
}
|
||||
}
|
||||
else {
|
||||
addPart(0);
|
||||
ones_count = 0;
|
||||
}
|
||||
}
|
||||
|
||||
void addPart(uint8_t bit)
|
||||
{
|
||||
// MSB first
|
||||
byte <<= 1;
|
||||
if (bit & 1) {
|
||||
byte |= 1;
|
||||
}
|
||||
if (++bits_count >= 8) {
|
||||
*ptr++ = byte;
|
||||
bits_count = 0;
|
||||
}
|
||||
}
|
||||
|
||||
void addTail()
|
||||
{
|
||||
if (bits_count > 0)
|
||||
*ptr++ = byte << (8 - bits_count);
|
||||
}
|
||||
|
||||
public:
|
||||
uint32_t getSize()
|
||||
{
|
||||
uint32_t size = (ptr - buffer) * 8;
|
||||
if (bits_count > 0)
|
||||
size -= (8 - bits_count);
|
||||
|
||||
return size;
|
||||
}
|
||||
};
|
||||
|
||||
class UartPxx1Transport: public Pxx1CrcMixin {
|
||||
protected:
|
||||
const uint8_t* buffer;
|
||||
uint8_t* ptr;
|
||||
|
||||
UartPxx1Transport(uint8_t* buffer)
|
||||
: buffer(buffer), ptr(buffer)
|
||||
{
|
||||
}
|
||||
|
||||
void addByte(uint8_t byte)
|
||||
{
|
||||
Pxx1CrcMixin::addToCrc(byte);
|
||||
addWithByteStuffing(byte);
|
||||
}
|
||||
|
||||
void addRawByte(uint8_t byte)
|
||||
{
|
||||
*ptr++ = byte;
|
||||
}
|
||||
|
||||
void addByteWithoutCrc(uint8_t byte)
|
||||
{
|
||||
addWithByteStuffing(byte);
|
||||
}
|
||||
|
||||
void addWithByteStuffing(uint8_t byte)
|
||||
{
|
||||
if (0x7E == byte) {
|
||||
addRawByte(0x7D);
|
||||
addRawByte(0x5E);
|
||||
}
|
||||
else if (0x7D == byte) {
|
||||
addRawByte(0x7D);
|
||||
addRawByte(0x5D);
|
||||
}
|
||||
else {
|
||||
addRawByte(byte);
|
||||
}
|
||||
}
|
||||
|
||||
void addTail()
|
||||
{
|
||||
// nothing
|
||||
}
|
||||
|
||||
public:
|
||||
uint32_t getSize()
|
||||
{
|
||||
return ptr - buffer;
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
enum class Pxx1Type : uintptr_t {
|
||||
PWM,
|
||||
SLOW_SERIAL,
|
||||
FAST_SERIAL,
|
||||
};
|
||||
|
||||
template <class PxxTransport>
|
||||
class Pxx1Pulses: public PxxTransport
|
||||
{
|
||||
public:
|
||||
Pxx1Pulses(uint8_t* buffer);
|
||||
void setupFrame(uint8_t module, Pxx1Type type);
|
||||
|
||||
protected:
|
||||
void addHead()
|
||||
{
|
||||
// send 7E, do not CRC
|
||||
PxxTransport::addRawByte(0x7E);
|
||||
}
|
||||
|
||||
void addCrc()
|
||||
{
|
||||
PxxTransport::addByteWithoutCrc(Pxx1CrcMixin::crc >> 8);
|
||||
PxxTransport::addByteWithoutCrc(Pxx1CrcMixin::crc);
|
||||
}
|
||||
|
||||
void addFlag1(uint8_t port, uint8_t sendFailsafe);
|
||||
void addExtraFlags(uint8_t port);
|
||||
void addChannels(uint8_t port, uint8_t sendFailsafe, uint8_t sendUpperChannels);
|
||||
void add8ChannelsFrame(uint8_t port, uint8_t sendUpperChannels, uint8_t sendFailsafe);
|
||||
};
|
||||
|
||||
typedef Pxx1Pulses<UartPxx1Transport> UartPxx1Pulses;
|
||||
typedef Pxx1Pulses<StandardPxx1Transport> PwmPxx1Pulses;
|
||||
+184
-298
@@ -21,24 +21,117 @@
|
||||
|
||||
#include <stdio.h>
|
||||
#include "opentx.h"
|
||||
#include "pulses/pxx2.h"
|
||||
#include "io/frsky_firmware_update.h"
|
||||
#include "libopenui/src/libopenui_file.h"
|
||||
#include "mixer_scheduler.h"
|
||||
#include "heartbeat_driver.h"
|
||||
#include "timers_driver.h"
|
||||
|
||||
#if defined(INTMODULE_USART)
|
||||
#include "intmodule_serial_driver.h"
|
||||
#include "pxx2.h"
|
||||
#include "pxx2_transport.h"
|
||||
|
||||
const etx_serial_init pxx2SerialInitParams = {
|
||||
static const etx_serial_init pxx2SerialInitParams = {
|
||||
.baudrate = PXX2_HIGHSPEED_BAUDRATE,
|
||||
.parity = ETX_Parity_None,
|
||||
.stop_bits = ETX_StopBits_One,
|
||||
.word_length = ETX_WordLength_8,
|
||||
.rx_enable = true,
|
||||
.encoding = ETX_Encoding_8N1,
|
||||
.direction = ETX_Dir_TX_RX,
|
||||
.polarity = ETX_Pol_Normal,
|
||||
};
|
||||
|
||||
#include "hal/module_port.h"
|
||||
|
||||
/* Module options order:
|
||||
* - External antenna (0x01)
|
||||
* - Power (0x02)
|
||||
* - Spektrum analyser (0x04)
|
||||
* - Power meter (0x08)
|
||||
*/
|
||||
static const uint8_t PXX2ModuleOptions[] = {
|
||||
#if defined(SIMU)
|
||||
0b11111111, // None = display all options on SIMU
|
||||
#else
|
||||
0b00000000, // None = no option available on unknown modules
|
||||
#endif
|
||||
0b00000001, // XJT
|
||||
0b00000001, // ISRM
|
||||
0b00001101, // ISRM-PRO
|
||||
0b00000101, // ISRM-S
|
||||
0b00000010, // R9M
|
||||
0b00000010, // R9MLite
|
||||
0b00000110, // R9MLite-PRO
|
||||
0b00000100, // ISRM-N
|
||||
0b00000100, // ISRM-S-X9
|
||||
0b00000101, // ISRM-S-X10E
|
||||
0b00000001, // XJT_LITE
|
||||
0b00000101, // ISRM-S-X10S
|
||||
0b00000100, // ISRM-X9LITES
|
||||
};
|
||||
|
||||
uint8_t getPXX2ModuleOptions(uint8_t modelId)
|
||||
{
|
||||
if (modelId < DIM(PXX2ModuleOptions))
|
||||
return PXX2ModuleOptions[modelId];
|
||||
else
|
||||
return PXX2ModuleOptions[0];
|
||||
}
|
||||
|
||||
bool isPXX2ModuleOptionAvailable(uint8_t modelId, uint8_t option)
|
||||
{
|
||||
return getPXX2ModuleOptions(modelId) & (1 << option);
|
||||
}
|
||||
|
||||
/* Receiver options order:
|
||||
* - OTA (0x01)
|
||||
*/
|
||||
static const uint8_t PXX2ReceiverOptions[] = {
|
||||
#if defined(SIMU)
|
||||
0b11111111, // None = display all options on SIMU
|
||||
#else
|
||||
0b00000000, // None = display all options on SIMU
|
||||
#endif
|
||||
0b11111110, // X8R
|
||||
0b11111110, // RX8R
|
||||
0b11111110, // RX8R-PRO
|
||||
0b11111111, // RX6R
|
||||
0b11111111, // RX4R
|
||||
0b11111111, // G-RX8
|
||||
0b11111111, // G-RX6
|
||||
0b11111110, // X6R
|
||||
0b11111110, // X4R
|
||||
0b11111110, // X4R-SB
|
||||
0b11111110, // XSR
|
||||
0b11111110, // XSR-M
|
||||
0b11111111, // RXSR
|
||||
0b11111110, // S6R
|
||||
0b11111110, // S8R
|
||||
0b11111110, // XM
|
||||
0b11111110, // XM+
|
||||
0b11111110, // XMR
|
||||
0b11111110, // R9
|
||||
0b11111110, // R9-SLIM
|
||||
0b11111110, // R9-SLIM+
|
||||
0b11111110, // R9-MINI
|
||||
0b11111110, // R9-MM
|
||||
0b11111111, // R9-STAB+OTA
|
||||
0b11111111, // R9-MINI+OTA
|
||||
0b11111111, // R9-MM+OTA
|
||||
0b11111111, // R9-SLIM+OTA
|
||||
0b11111111, // ARCHER-X
|
||||
0b11111111, // R9MX
|
||||
0b11111111, // R9SX
|
||||
};
|
||||
|
||||
uint8_t getPXX2ReceiverOptions(uint8_t modelId)
|
||||
{
|
||||
if (modelId < DIM(PXX2ReceiverOptions))
|
||||
return PXX2ReceiverOptions[modelId];
|
||||
else
|
||||
return PXX2ReceiverOptions[0];
|
||||
}
|
||||
|
||||
bool isPXX2ReceiverOptionAvailable(uint8_t modelId, uint8_t option)
|
||||
{
|
||||
return getPXX2ReceiverOptions(modelId) & (1 << option);
|
||||
}
|
||||
|
||||
bool isPXX2PowerAvailable(const PXX2HardwareInformation& info, int value)
|
||||
{
|
||||
@@ -435,10 +528,12 @@ void Pxx2Pulses::setupShareMode(uint8_t module)
|
||||
Pxx2Transport::addByte(reusableBuffer.moduleSetup.pxx2.shareReceiverIndex);
|
||||
}
|
||||
|
||||
void Pxx2Pulses::sendOtaUpdate(uint8_t module, const char * rxName, uint32_t address, const char * data)
|
||||
{
|
||||
initFrame();
|
||||
// TODO: move up
|
||||
static void _send_frame(etx_module_state_t* mod_st, uint8_t* buffer, uint32_t len);
|
||||
|
||||
void Pxx2Pulses::sendOtaUpdate(uint8_t module, const char* rxName,
|
||||
uint32_t address, const char* data)
|
||||
{
|
||||
addFrameType(PXX2_TYPE_C_OTA, PXX2_TYPE_ID_OTA);
|
||||
|
||||
if (rxName) {
|
||||
@@ -459,22 +554,11 @@ void Pxx2Pulses::sendOtaUpdate(uint8_t module, const char * rxName, uint32_t add
|
||||
}
|
||||
|
||||
endFrame();
|
||||
|
||||
#if defined(HARDWARE_INTERNAL_MODULE)
|
||||
if (module == INTERNAL_MODULE) {
|
||||
intmoduleSendNextFrame();
|
||||
}
|
||||
#endif
|
||||
#if defined(HARDWARE_EXTERNAL_MODULE)
|
||||
if (module == EXTERNAL_MODULE) {
|
||||
extmoduleSendNextFrame();
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
void Pxx2Pulses::setupAuthenticationFrame(uint8_t module, uint8_t mode, const uint8_t * outputMessage)
|
||||
{
|
||||
initFrame();
|
||||
// initFrame();
|
||||
|
||||
addFrameType(PXX2_TYPE_C_MODULE, PXX2_TYPE_ID_AUTHENTICATION);
|
||||
|
||||
@@ -498,7 +582,7 @@ bool Pxx2Pulses::setupFrame(uint8_t module, int16_t* channels, uint8_t nChannels
|
||||
return false;
|
||||
}
|
||||
|
||||
initFrame();
|
||||
// initFrame();
|
||||
|
||||
switch (moduleState[module].mode) {
|
||||
case MODULE_MODE_GET_HARDWARE_INFO:
|
||||
@@ -548,216 +632,96 @@ bool Pxx2Pulses::setupFrame(uint8_t module, int16_t* channels, uint8_t nChannels
|
||||
return true;
|
||||
}
|
||||
|
||||
bool Pxx2OtaUpdate::waitStep(uint8_t step, uint8_t timeout)
|
||||
static void* pxx2Init(uint8_t module)
|
||||
{
|
||||
OtaUpdateInformation * destination = moduleState[module].otaUpdateInformation;
|
||||
uint8_t elapsed = 0;
|
||||
etx_module_state_t* mod_st = nullptr;
|
||||
etx_serial_init params(pxx2SerialInitParams);
|
||||
|
||||
watchdogSuspend(100 /*1s*/);
|
||||
if (module == INTERNAL_MODULE) {
|
||||
|
||||
while (step != destination->step) {
|
||||
if (elapsed++ > timeout) {
|
||||
return false;
|
||||
}
|
||||
RTOS_WAIT_MS(1);
|
||||
telemetryWakeup();
|
||||
params.baudrate = PXX2_HIGHSPEED_BAUDRATE;
|
||||
mod_st = modulePortInitSerial(module, ETX_MOD_PORT_UART, ¶ms);
|
||||
if (!mod_st) return nullptr;
|
||||
resetAccessAuthenticationCount();
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
if (module == EXTERNAL_MODULE) {
|
||||
|
||||
const char * Pxx2OtaUpdate::nextStep(uint8_t step, const char * rxName, uint32_t address, const uint8_t * buffer)
|
||||
{
|
||||
OtaUpdateInformation * destination = moduleState[module].otaUpdateInformation;
|
||||
uint8_t type = g_model.moduleData[module].type;
|
||||
switch(type) {
|
||||
|
||||
destination->step = step;
|
||||
destination->address = address;
|
||||
case MODULE_TYPE_R9M_LITE_PXX2:
|
||||
params.baudrate = PXX2_LOWSPEED_BAUDRATE;
|
||||
break;
|
||||
|
||||
for (uint8_t retry = 0;; retry++) {
|
||||
if (module == EXTERNAL_MODULE) {
|
||||
extmodulePulsesData.pxx2.sendOtaUpdate(module, rxName, address, (const char *) buffer);
|
||||
}
|
||||
else if (module == INTERNAL_MODULE) {
|
||||
intmodulePulsesData.pxx2.sendOtaUpdate(module, rxName, address, (const char *) buffer);
|
||||
}
|
||||
if (waitStep(step + 1, 20)) {
|
||||
case MODULE_TYPE_ISRM_PXX2:
|
||||
case MODULE_TYPE_R9M_PXX2:
|
||||
case MODULE_TYPE_XJT_LITE_PXX2:
|
||||
case MODULE_TYPE_R9M_LITE_PRO_PXX2:
|
||||
params.baudrate = PXX2_HIGHSPEED_BAUDRATE;
|
||||
break;
|
||||
|
||||
default:
|
||||
return nullptr;
|
||||
}
|
||||
else if (retry == 100) {
|
||||
return "Transfer failed";
|
||||
}
|
||||
|
||||
mod_st = modulePortInitSerial(module, ETX_MOD_PORT_UART, ¶ms);
|
||||
if (!mod_st) return nullptr;
|
||||
}
|
||||
|
||||
mixerSchedulerSetPeriod(module, PXX2_DEFAULT_PERIOD);
|
||||
return mod_st;
|
||||
}
|
||||
|
||||
const char * Pxx2OtaUpdate::doFlashFirmware(const char * filename, ProgressHandler progressHandler)
|
||||
static void pxx2DeInit(void* ctx)
|
||||
{
|
||||
FIL file;
|
||||
uint8_t buffer[32];
|
||||
UINT count;
|
||||
const char * result;
|
||||
auto mod_st = (etx_module_state_t*)ctx;
|
||||
modulePortDeInit(mod_st);
|
||||
}
|
||||
|
||||
result = nextStep(OTA_UPDATE_START, rxName, 0, nullptr);
|
||||
if (result) {
|
||||
return result;
|
||||
}
|
||||
static void _send_frame(etx_module_state_t* mod_st, uint8_t* buffer, uint32_t len)
|
||||
{
|
||||
auto drv = modulePortGetSerialDrv(mod_st->tx);
|
||||
auto ctx = modulePortGetCtx(mod_st->tx);
|
||||
drv->sendBuffer(ctx, buffer, len);
|
||||
}
|
||||
|
||||
if (f_open(&file, filename, FA_READ) != FR_OK) {
|
||||
return "Open file failed";
|
||||
}
|
||||
static void pxx2SendPulses(void* ctx, uint8_t* buffer, int16_t* channels, uint8_t nChannels)
|
||||
{
|
||||
auto mod_st = (etx_module_state_t*)ctx;
|
||||
auto module = modulePortGetModule(mod_st);
|
||||
|
||||
uint32_t size;
|
||||
const char * ext = getFileExtension(filename);
|
||||
if (ext && !strcasecmp(ext, FRSKY_FIRMWARE_EXT)) {
|
||||
FrSkyFirmwareInformation * information = (FrSkyFirmwareInformation *) buffer;
|
||||
if (f_read(&file, buffer, sizeof(FrSkyFirmwareInformation), &count) != FR_OK || count != sizeof(FrSkyFirmwareInformation)) {
|
||||
f_close(&file);
|
||||
return "Format error";
|
||||
}
|
||||
size = information->size;
|
||||
}
|
||||
else {
|
||||
size = f_size(&file);
|
||||
}
|
||||
if (module == INTERNAL_MODULE) {
|
||||
Pxx2Pulses pxx2(buffer);
|
||||
bool should_send = pxx2.setupFrame(module, channels, nChannels);
|
||||
|
||||
uint32_t done = 0;
|
||||
while (1) {
|
||||
progressHandler(getBasename(filename), STR_OTA_UPDATE, done, size);
|
||||
if (f_read(&file, buffer, sizeof(buffer), &count) != FR_OK) {
|
||||
f_close(&file);
|
||||
return "Read file failed";
|
||||
if (moduleState[module].mode == MODULE_MODE_SPECTRUM_ANALYSER ||
|
||||
moduleState[module].mode == MODULE_MODE_POWER_METER) {
|
||||
|
||||
// backup normal period into 'user_data'
|
||||
if (!mod_st->user_data) {
|
||||
mod_st->user_data = (void*)(uintptr_t)mixerSchedulerGetPeriod(module);
|
||||
}
|
||||
mixerSchedulerSetPeriod(module, PXX2_TOOLS_PERIOD);
|
||||
} else if (mod_st->user_data) {
|
||||
// restore old scheduling period
|
||||
uintptr_t period = (uintptr_t)mod_st->user_data;
|
||||
mixerSchedulerSetPeriod(module, period);
|
||||
mod_st->user_data = nullptr;
|
||||
}
|
||||
|
||||
result = nextStep(OTA_UPDATE_TRANSFER, nullptr, done, buffer);
|
||||
if (result) {
|
||||
return result;
|
||||
}
|
||||
|
||||
if (count < sizeof(buffer)) {
|
||||
f_close(&file);
|
||||
break;
|
||||
}
|
||||
|
||||
done += count;
|
||||
if (!should_send) return;
|
||||
_send_frame(mod_st, buffer, pxx2.getSize());
|
||||
}
|
||||
|
||||
return nextStep(OTA_UPDATE_EOF, nullptr, done, nullptr);
|
||||
}
|
||||
|
||||
void Pxx2OtaUpdate::flashFirmware(const char * filename, ProgressHandler progressHandler)
|
||||
{
|
||||
pausePulses();
|
||||
|
||||
watchdogSuspend(100 /*1s*/);
|
||||
RTOS_WAIT_MS(100);
|
||||
|
||||
moduleState[module].mode = MODULE_MODE_OTA_UPDATE;
|
||||
const char * result = doFlashFirmware(filename, progressHandler);
|
||||
moduleState[module].mode = MODULE_MODE_NORMAL;
|
||||
|
||||
AUDIO_PLAY(AU_SPECIAL_SOUND_BEEP1 );
|
||||
BACKLIGHT_ENABLE();
|
||||
|
||||
if (result) {
|
||||
POPUP_WARNING(STR_FIRMWARE_UPDATE_ERROR, result);
|
||||
}
|
||||
else {
|
||||
POPUP_INFORMATION(STR_FIRMWARE_UPDATE_SUCCESS);
|
||||
}
|
||||
|
||||
watchdogSuspend(100);
|
||||
RTOS_WAIT_MS(100);
|
||||
|
||||
resumePulses();
|
||||
}
|
||||
|
||||
struct PXX2State {
|
||||
uint8_t module;
|
||||
Pxx2Pulses* pulses;
|
||||
const etx_serial_driver_t* uart_drv;
|
||||
void* uart_ctx;
|
||||
|
||||
void init(uint8_t _module, Pxx2Pulses* _pulses, const etx_serial_driver_t* _drv, void* _ctx)
|
||||
{
|
||||
module = _module;
|
||||
pulses = _pulses;
|
||||
uart_drv = _drv;
|
||||
uart_ctx = _ctx;
|
||||
}
|
||||
|
||||
void deinit() { uart_drv->deinit(uart_ctx); }
|
||||
};
|
||||
|
||||
static PXX2State pxx2State[NUM_MODULES];
|
||||
|
||||
static void* pxx2InitInternal(uint8_t module)
|
||||
{
|
||||
#if defined(INTMODULE_HEARTBEAT)
|
||||
// use backup trigger (1 ms later)
|
||||
init_intmodule_heartbeat();
|
||||
#endif
|
||||
mixerSchedulerSetPeriod(module, PXX2_PERIOD);
|
||||
INTERNAL_MODULE_ON();
|
||||
|
||||
resetAccessAuthenticationCount();
|
||||
|
||||
auto state = &pxx2State[module];
|
||||
state->init(module, &intmodulePulsesData.pxx2, &IntmoduleSerialDriver,
|
||||
IntmoduleSerialDriver.init(&pxx2SerialInitParams));
|
||||
|
||||
return state;
|
||||
}
|
||||
|
||||
static void pxx2DeInitInternal(void* context)
|
||||
{
|
||||
auto state = (PXX2State*)context;
|
||||
state->deinit();
|
||||
|
||||
INTERNAL_MODULE_OFF();
|
||||
#if defined(INTMODULE_HEARTBEAT)
|
||||
stop_intmodule_heartbeat();
|
||||
#endif
|
||||
mixerSchedulerSetPeriod(state->module, 0);
|
||||
}
|
||||
|
||||
// TODO: move this to Pxx2Pulses
|
||||
static bool pxx2InternalSendNextFrame = true;
|
||||
|
||||
static void pxx2SetupPulsesInternal(void* context, int16_t* channels, uint8_t nChannels)
|
||||
{
|
||||
auto state = (PXX2State*)context;
|
||||
|
||||
auto pulses = state->pulses;
|
||||
auto module = state->module;
|
||||
pxx2InternalSendNextFrame = pulses->setupFrame(module, channels, nChannels);
|
||||
|
||||
if (moduleState[module].mode == MODULE_MODE_SPECTRUM_ANALYSER ||
|
||||
moduleState[module].mode == MODULE_MODE_POWER_METER) {
|
||||
mixerSchedulerSetPeriod(module, PXX2_TOOLS_PERIOD);
|
||||
} else {
|
||||
mixerSchedulerSetPeriod(module, PXX2_PERIOD);
|
||||
if (module == EXTERNAL_MODULE) {
|
||||
Pxx2Pulses pxx2(buffer);
|
||||
pxx2.setupFrame(module, channels, nChannels);
|
||||
_send_frame(mod_st, buffer, pxx2.getSize());
|
||||
}
|
||||
}
|
||||
|
||||
static void pxx2SendPulsesInternal(void* context)
|
||||
{
|
||||
if (pxx2InternalSendNextFrame) {
|
||||
auto state = (PXX2State*)context;
|
||||
auto drv = state->uart_drv;
|
||||
auto ctx = state->uart_ctx;
|
||||
auto pulses = state->pulses;
|
||||
drv->sendBuffer(ctx, pulses->getData(), pulses->getSize());
|
||||
}
|
||||
}
|
||||
|
||||
static int pxx2GetByte(void* context, uint8_t* data)
|
||||
{
|
||||
auto state = (PXX2State*)context;
|
||||
auto drv = state->uart_drv;
|
||||
auto ctx = state->uart_ctx;
|
||||
return drv->getByte(ctx, data);
|
||||
}
|
||||
|
||||
static void pxx2ProcessData(void* context, uint8_t data, uint8_t* buffer, uint8_t* len)
|
||||
static void pxx2ProcessData(void* ctx, uint8_t data, uint8_t* buffer, uint8_t* len)
|
||||
{
|
||||
if (*len == 0 && data != START_STOP) {
|
||||
return;
|
||||
@@ -802,99 +766,21 @@ static void pxx2ProcessData(void* context, uint8_t data, uint8_t* buffer, uint8_
|
||||
return;
|
||||
}
|
||||
|
||||
auto state = (PXX2State*)context;
|
||||
auto module = state->module;
|
||||
auto drv = state->uart_drv;
|
||||
auto ctx = state->uart_ctx;
|
||||
processPXX2Frame(module, frame, drv, ctx);
|
||||
auto mod_st = (etx_module_state_t*)ctx;
|
||||
auto module = modulePortGetModule(mod_st);
|
||||
auto drv = modulePortGetSerialDrv(mod_st->rx);
|
||||
auto drv_ctx = modulePortGetCtx(mod_st->rx);
|
||||
processPXX2Frame(module, frame, drv, drv_ctx);
|
||||
*len = 0;
|
||||
}
|
||||
|
||||
|
||||
#include "hal/module_driver.h"
|
||||
#include "extmodule_serial_driver.h"
|
||||
// #include "extmodule_serial_driver.h"
|
||||
|
||||
const etx_module_driver_t Pxx2InternalDriver = {
|
||||
.protocol = PROTOCOL_CHANNELS_PXX2_HIGHSPEED,
|
||||
.init = pxx2InitInternal,
|
||||
.deinit = pxx2DeInitInternal,
|
||||
.setupPulses = pxx2SetupPulsesInternal,
|
||||
.sendPulses = pxx2SendPulsesInternal,
|
||||
.getByte = pxx2GetByte,
|
||||
.processData = pxx2ProcessData,
|
||||
};
|
||||
|
||||
static void* pxx2InitExternal(uint8_t module, uint32_t baudrate)
|
||||
{
|
||||
etx_serial_init params(pxx2SerialInitParams);
|
||||
params.baudrate = baudrate;
|
||||
|
||||
mixerSchedulerSetPeriod(module, PXX2_NO_HEARTBEAT_PERIOD);
|
||||
EXTERNAL_MODULE_ON();
|
||||
|
||||
telemetryProtocol = PROTOCOL_TELEMETRY_FRSKY_SPORT;
|
||||
|
||||
auto state = &pxx2State[module];
|
||||
auto drv = extmoduleGetSerialPort();
|
||||
if (!drv) return nullptr;
|
||||
|
||||
state->init(module, &extmodulePulsesData.pxx2, drv, drv->init(¶ms));
|
||||
return state;
|
||||
}
|
||||
|
||||
static void* pxx2InitExtLowSpeed(uint8_t module)
|
||||
{
|
||||
return pxx2InitExternal(module, PXX2_LOWSPEED_BAUDRATE);
|
||||
}
|
||||
|
||||
static void* pxx2InitExtHighSpeed(uint8_t module)
|
||||
{
|
||||
return pxx2InitExternal(module, PXX2_HIGHSPEED_BAUDRATE);
|
||||
}
|
||||
|
||||
static void pxx2DeInitExternal(void* context)
|
||||
{
|
||||
auto state = (PXX2State*)context;
|
||||
if (state) state->deinit();
|
||||
|
||||
EXTERNAL_MODULE_OFF();
|
||||
mixerSchedulerSetPeriod(EXTERNAL_MODULE, 0);
|
||||
telemetryProtocol = 0xFF;
|
||||
}
|
||||
|
||||
static void pxx2SetupPulsesExternal(void* context, int16_t* channels, uint8_t nChannels)
|
||||
{
|
||||
auto state = (PXX2State*)context;
|
||||
auto pulses = state->pulses;
|
||||
auto module = state->module;
|
||||
pulses->setupFrame(module, channels, nChannels);
|
||||
}
|
||||
|
||||
static void pxx2SendPulsesExternal(void* context)
|
||||
{
|
||||
auto state = (PXX2State*)context;
|
||||
auto drv = state->uart_drv;
|
||||
auto ctx = state->uart_ctx;
|
||||
auto pulses = state->pulses;
|
||||
drv->sendBuffer(ctx, pulses->getData(), pulses->getSize());
|
||||
}
|
||||
|
||||
const etx_module_driver_t Pxx2ExternalDriver = {
|
||||
.protocol = PROTOCOL_CHANNELS_PXX2_HIGHSPEED,
|
||||
.init = pxx2InitExtHighSpeed,
|
||||
.deinit = pxx2DeInitExternal,
|
||||
.setupPulses = pxx2SetupPulsesExternal,
|
||||
.sendPulses = pxx2SendPulsesExternal,
|
||||
.getByte = pxx2GetByte,
|
||||
.processData = pxx2ProcessData,
|
||||
};
|
||||
|
||||
const etx_module_driver_t Pxx2LowSpeedExternalDriver = {
|
||||
.protocol = PROTOCOL_CHANNELS_PXX2_LOWSPEED,
|
||||
.init = pxx2InitExtLowSpeed,
|
||||
.deinit = pxx2DeInitExternal,
|
||||
.setupPulses = pxx2SetupPulsesExternal,
|
||||
.sendPulses = pxx2SendPulsesExternal,
|
||||
.getByte = pxx2GetByte,
|
||||
const etx_proto_driver_t Pxx2Driver = {
|
||||
.protocol = PROTOCOL_CHANNELS_PXX2,
|
||||
.init = pxx2Init,
|
||||
.deinit = pxx2DeInit,
|
||||
.sendPulses = pxx2SendPulses,
|
||||
.processData = pxx2ProcessData,
|
||||
};
|
||||
|
||||
+12
-332
@@ -22,13 +22,12 @@
|
||||
#ifndef _PULSES_PXX2_H_
|
||||
#define _PULSES_PXX2_H_
|
||||
|
||||
#include "fifo.h"
|
||||
#include "popups.h"
|
||||
#include "pxx.h"
|
||||
|
||||
#include "hal/serial_driver.h"
|
||||
#include "hal/module_driver.h"
|
||||
|
||||
#include "dataconstants.h"
|
||||
#include "opentx_types.h"
|
||||
|
||||
#define PXX2_TYPE_C_MODULE 0x01
|
||||
#define PXX2_TYPE_ID_REGISTER 0x01
|
||||
#define PXX2_TYPE_ID_BIND 0x02
|
||||
@@ -67,11 +66,7 @@
|
||||
|
||||
#define PXX2_HW_INFO_TX_ID 0xFF
|
||||
|
||||
#if defined(COLORLCD)
|
||||
#define SPECTRUM_ANALYSER_POWER_FLOOR -120 /*dBm*/
|
||||
#else
|
||||
#define SPECTRUM_ANALYSER_POWER_FLOOR -120 /*dBm*/
|
||||
#endif
|
||||
#define SPECTRUM_ANALYSER_POWER_FLOOR -120 /*dBm*/
|
||||
|
||||
#define PXX2_AUTH_REFUSED_FLAG 0xA5
|
||||
|
||||
@@ -92,30 +87,7 @@ enum PXX2ModuleModelID {
|
||||
PXX2_MODULE_ISRM_X9LITES,
|
||||
};
|
||||
|
||||
static const char * const PXX2ModulesNames[] = {
|
||||
"---",
|
||||
"XJT",
|
||||
"ISRM",
|
||||
"ISRM-PRO",
|
||||
"ISRM-S",
|
||||
"R9M",
|
||||
"R9MLite",
|
||||
"R9MLite-PRO",
|
||||
"ISRM-N",
|
||||
"ISRM-S-X9",
|
||||
"ISRM-S-X10E",
|
||||
"XJT Lite",
|
||||
"ISRM-S-X10S",
|
||||
"ISRM-X9LiteS"
|
||||
};
|
||||
|
||||
inline const char * getPXX2ModuleName(uint8_t modelId)
|
||||
{
|
||||
if (modelId < DIM(PXX2ModulesNames))
|
||||
return PXX2ModulesNames[modelId];
|
||||
else
|
||||
return PXX2ModulesNames[0];
|
||||
}
|
||||
const char * getPXX2ModuleName(uint8_t modelId);
|
||||
|
||||
enum {
|
||||
MODULE_OPTION_EXTERNAL_ANTENNA,
|
||||
@@ -124,149 +96,21 @@ enum {
|
||||
MODULE_OPTION_POWER_METER,
|
||||
};
|
||||
|
||||
/* Module options order:
|
||||
* - External antenna (0x01)
|
||||
* - Power (0x02)
|
||||
* - Spektrum analyser (0x04)
|
||||
* - Power meter (0x08)
|
||||
*/
|
||||
static const uint8_t PXX2ModuleOptions[] = {
|
||||
#if defined(SIMU)
|
||||
0b11111111, // None = display all options on SIMU
|
||||
#else
|
||||
0b00000000, // None = no option available on unknown modules
|
||||
#endif
|
||||
0b00000001, // XJT
|
||||
0b00000001, // ISRM
|
||||
0b00001101, // ISRM-PRO
|
||||
0b00000101, // ISRM-S
|
||||
0b00000010, // R9M
|
||||
0b00000010, // R9MLite
|
||||
0b00000110, // R9MLite-PRO
|
||||
0b00000100, // ISRM-N
|
||||
0b00000100, // ISRM-S-X9
|
||||
0b00000101, // ISRM-S-X10E
|
||||
0b00000001, // XJT_LITE
|
||||
0b00000101, // ISRM-S-X10S
|
||||
0b00000100, // ISRM-X9LITES
|
||||
};
|
||||
|
||||
inline uint8_t getPXX2ModuleOptions(uint8_t modelId)
|
||||
{
|
||||
if (modelId < DIM(PXX2ModuleOptions))
|
||||
return PXX2ModuleOptions[modelId];
|
||||
else
|
||||
return PXX2ModuleOptions[0];
|
||||
}
|
||||
|
||||
inline bool isPXX2ModuleOptionAvailable(uint8_t modelId, uint8_t option)
|
||||
{
|
||||
return getPXX2ModuleOptions(modelId) & (1 << option);
|
||||
}
|
||||
uint8_t getPXX2ModuleOptions(uint8_t modelId);
|
||||
bool isPXX2ModuleOptionAvailable(uint8_t modelId, uint8_t option);
|
||||
|
||||
enum ModuleCapabilities {
|
||||
MODULE_CAPABILITY_COUNT
|
||||
};
|
||||
|
||||
static const char * const PXX2ReceiversNames[] = {
|
||||
"---",
|
||||
"X8R",
|
||||
"RX8R",
|
||||
"RX8R-PRO",
|
||||
"RX6R",
|
||||
"RX4R",
|
||||
"G-RX8",
|
||||
"G-RX6",
|
||||
"X6R",
|
||||
"X4R",
|
||||
"X4R-SB",
|
||||
"XSR",
|
||||
"XSR-M",
|
||||
"RXSR",
|
||||
"S6R",
|
||||
"S8R",
|
||||
"XM",
|
||||
"XM+",
|
||||
"XMR",
|
||||
"R9",
|
||||
"R9-SLIM",
|
||||
"R9-SLIM+",
|
||||
"R9-MINI",
|
||||
"R9-MM",
|
||||
"R9-STAB", // R9-STAB has OTA
|
||||
"R9-MINI-OTA", // this one has OTA (different bootloader)
|
||||
"R9-MM-OTA", // this one has OTA (different bootloader)
|
||||
"R9-SLIM+-OTA", // this one has OTA (different bootloader)
|
||||
"Archer-X", // this one has OTA (internal module)
|
||||
"R9MX", // this one has OTA
|
||||
"R9SX", // this one has OTA
|
||||
};
|
||||
|
||||
inline const char * getPXX2ReceiverName(uint8_t modelId)
|
||||
{
|
||||
if (modelId < DIM(PXX2ReceiversNames))
|
||||
return PXX2ReceiversNames[modelId];
|
||||
else
|
||||
return PXX2ReceiversNames[0];
|
||||
}
|
||||
const char * getPXX2ReceiverName(uint8_t modelId);
|
||||
|
||||
enum {
|
||||
RECEIVER_OPTION_OTA,
|
||||
};
|
||||
|
||||
/* Receiver options order:
|
||||
* - OTA (0x01)
|
||||
*/
|
||||
static const uint8_t PXX2ReceiverOptions[] = {
|
||||
#if defined(SIMU)
|
||||
0b11111111, // None = display all options on SIMU
|
||||
#else
|
||||
0b00000000, // None = display all options on SIMU
|
||||
#endif
|
||||
0b11111110, // X8R
|
||||
0b11111110, // RX8R
|
||||
0b11111110, // RX8R-PRO
|
||||
0b11111111, // RX6R
|
||||
0b11111111, // RX4R
|
||||
0b11111111, // G-RX8
|
||||
0b11111111, // G-RX6
|
||||
0b11111110, // X6R
|
||||
0b11111110, // X4R
|
||||
0b11111110, // X4R-SB
|
||||
0b11111110, // XSR
|
||||
0b11111110, // XSR-M
|
||||
0b11111111, // RXSR
|
||||
0b11111110, // S6R
|
||||
0b11111110, // S8R
|
||||
0b11111110, // XM
|
||||
0b11111110, // XM+
|
||||
0b11111110, // XMR
|
||||
0b11111110, // R9
|
||||
0b11111110, // R9-SLIM
|
||||
0b11111110, // R9-SLIM+
|
||||
0b11111110, // R9-MINI
|
||||
0b11111110, // R9-MM
|
||||
0b11111111, // R9-STAB+OTA
|
||||
0b11111111, // R9-MINI+OTA
|
||||
0b11111111, // R9-MM+OTA
|
||||
0b11111111, // R9-SLIM+OTA
|
||||
0b11111111, // ARCHER-X
|
||||
0b11111111, // R9MX
|
||||
0b11111111, // R9SX
|
||||
};
|
||||
|
||||
inline uint8_t getPXX2ReceiverOptions(uint8_t modelId)
|
||||
{
|
||||
if (modelId < DIM(PXX2ReceiverOptions))
|
||||
return PXX2ReceiverOptions[modelId];
|
||||
else
|
||||
return PXX2ReceiverOptions[0];
|
||||
}
|
||||
|
||||
inline bool isPXX2ReceiverOptionAvailable(uint8_t modelId, uint8_t option)
|
||||
{
|
||||
return getPXX2ReceiverOptions(modelId) & (1 << option);
|
||||
}
|
||||
uint8_t getPXX2ReceiverOptions(uint8_t modelId);
|
||||
bool isPXX2ReceiverOptionAvailable(uint8_t modelId, uint8_t option);
|
||||
|
||||
enum ReceiverCapabilities {
|
||||
RECEIVER_CAPABILITY_FPORT,
|
||||
@@ -403,15 +247,8 @@ class BindInformation {
|
||||
PXX2HardwareInformation receiverInformation;
|
||||
};
|
||||
|
||||
class OtaUpdateInformation: public BindInformation {
|
||||
public:
|
||||
char filename[FF_MAX_LFN + 1];
|
||||
uint32_t address;
|
||||
uint32_t module;
|
||||
};
|
||||
|
||||
struct PXX2HardwareAndSettings {
|
||||
ModuleInformation modules[NUM_MODULES];
|
||||
ModuleInformation modules[MAX_MODULES];
|
||||
uint32_t updateTime;
|
||||
ModuleSettings moduleSettings;
|
||||
ReceiverSettings receiverSettings; // when dealing with receiver settings, we
|
||||
@@ -423,163 +260,6 @@ PXX2ModuleSetup& getPXX2ModuleSetupBuffer();
|
||||
BindInformation& getPXX2BindInformationBuffer();
|
||||
PXX2HardwareAndSettings& getPXX2HardwareAndSettingsBuffer();
|
||||
|
||||
class Pxx2CrcMixin {
|
||||
protected:
|
||||
void initCrc()
|
||||
{
|
||||
crc = 0xFFFF;
|
||||
}
|
||||
|
||||
void addToCrc(uint8_t byte)
|
||||
{
|
||||
crc -= byte;
|
||||
}
|
||||
|
||||
uint16_t crc;
|
||||
};
|
||||
|
||||
class Pxx2Transport: public DataBuffer<uint8_t, 64>, public Pxx2CrcMixin {
|
||||
protected:
|
||||
void addWord(uint32_t word)
|
||||
{
|
||||
addByte(word);
|
||||
addByte(word >> 8);
|
||||
addByte(word >> 16);
|
||||
addByte(word >> 24);
|
||||
}
|
||||
|
||||
void addByte(uint8_t byte)
|
||||
{
|
||||
Pxx2CrcMixin::addToCrc(byte);
|
||||
addByteWithoutCrc(byte);
|
||||
};
|
||||
|
||||
void addByteWithoutCrc(uint8_t byte)
|
||||
{
|
||||
*ptr++ = byte;
|
||||
}
|
||||
};
|
||||
|
||||
class Pxx2Pulses: public Pxx2Transport {
|
||||
friend class Pxx2OtaUpdate;
|
||||
|
||||
public:
|
||||
bool setupFrame(uint8_t module, int16_t* channels, uint8_t nChannels);
|
||||
void setupAuthenticationFrame(uint8_t module, uint8_t mode, const uint8_t * outputMessage);
|
||||
|
||||
protected:
|
||||
void setupHardwareInfoFrame(uint8_t module, int16_t* channels, uint8_t nChannels);
|
||||
|
||||
void setupRegisterFrame(uint8_t module);
|
||||
|
||||
void setupBindFrame(uint8_t module);
|
||||
|
||||
void setupAccstBindFrame(uint8_t module);
|
||||
|
||||
void setupAccessBindFrame(uint8_t module);
|
||||
|
||||
void setupResetFrame(uint8_t module);
|
||||
|
||||
void setupShareMode(uint8_t module);
|
||||
|
||||
void setupModuleSettingsFrame(uint8_t module, int16_t* channels, uint8_t nChannels);
|
||||
|
||||
void setupReceiverSettingsFrame(uint8_t module, int16_t* channels, uint8_t nChannels);
|
||||
|
||||
void setupChannelsFrame(uint8_t module, int16_t* channels, uint8_t nChannels);
|
||||
|
||||
void setupTelemetryFrame(uint8_t module);
|
||||
|
||||
void setupSpectrumAnalyser(uint8_t module);
|
||||
|
||||
void setupPowerMeter(uint8_t module);
|
||||
|
||||
void sendOtaUpdate(uint8_t module, const char * rxName, uint32_t address, const char * data);
|
||||
|
||||
void addHead()
|
||||
{
|
||||
// send 7E, do not CRC
|
||||
Pxx2Transport::addByteWithoutCrc(0x7E);
|
||||
|
||||
// reserve 1 byte for LEN
|
||||
Pxx2Transport::addByteWithoutCrc(0x00);
|
||||
}
|
||||
|
||||
void addFrameType(uint8_t type_c, uint8_t type_id)
|
||||
{
|
||||
// TYPE_C + TYPE_ID
|
||||
Pxx2Transport::addByte(type_c);
|
||||
Pxx2Transport::addByte(type_id);
|
||||
}
|
||||
|
||||
uint8_t addFlag0(uint8_t module);
|
||||
|
||||
void addFlag1(uint8_t module);
|
||||
|
||||
void addPulsesValues(uint16_t low, uint16_t high);
|
||||
|
||||
void addChannels(uint8_t module, int16_t* channels, uint8_t nChannels);
|
||||
|
||||
void addFailsafe(uint8_t module);
|
||||
|
||||
void addCrc()
|
||||
{
|
||||
Pxx2Transport::addByteWithoutCrc(Pxx2CrcMixin::crc >> 8);
|
||||
Pxx2Transport::addByteWithoutCrc(Pxx2CrcMixin::crc);
|
||||
}
|
||||
|
||||
void initFrame()
|
||||
{
|
||||
// init the CRC counter
|
||||
initCrc();
|
||||
|
||||
// reset the frame pointer
|
||||
Pxx2Transport::initBuffer();
|
||||
|
||||
// add the frame head
|
||||
addHead();
|
||||
}
|
||||
|
||||
void endFrame()
|
||||
{
|
||||
uint8_t size = getSize() - 2;
|
||||
|
||||
if (size > 0) {
|
||||
// update the frame LEN = frame length minus the 2 first bytes
|
||||
data[1] = getSize() - 2;
|
||||
|
||||
// now add the CRC
|
||||
addCrc();
|
||||
}
|
||||
else {
|
||||
Pxx2Transport::initBuffer();
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
class Pxx2OtaUpdate {
|
||||
public:
|
||||
Pxx2OtaUpdate(uint8_t module, const char * rxName):
|
||||
module(module),
|
||||
rxName(rxName)
|
||||
{
|
||||
}
|
||||
|
||||
void flashFirmware(const char * filename, ProgressHandler progressHandler);
|
||||
|
||||
protected:
|
||||
uint8_t module;
|
||||
const char * rxName;
|
||||
|
||||
const char * doFlashFirmware(const char * filename, ProgressHandler progressHandler);
|
||||
bool waitStep(uint8_t step, uint8_t timeout);
|
||||
const char * nextStep(uint8_t step, const char * rxName, uint32_t address, const uint8_t * buffer);
|
||||
};
|
||||
|
||||
extern const etx_serial_init pxx2SerialInitParams;
|
||||
|
||||
extern const etx_module_driver_t Pxx2InternalDriver;
|
||||
extern const etx_module_driver_t Pxx2ExternalDriver;
|
||||
extern const etx_module_driver_t Pxx2LowSpeedExternalDriver;
|
||||
extern const etx_proto_driver_t Pxx2Driver;
|
||||
|
||||
#endif
|
||||
|
||||
@@ -0,0 +1,160 @@
|
||||
/*
|
||||
* Copyright (C) EdgeTX
|
||||
*
|
||||
* Based on code named
|
||||
* opentx - https://github.com/opentx/opentx
|
||||
* 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 "io/frsky_firmware_update.h"
|
||||
#include "tasks/mixer_task.h"
|
||||
|
||||
#include "pxx2_ota.h"
|
||||
#include "pxx2_transport.h"
|
||||
|
||||
bool Pxx2OtaUpdate::waitStep(uint8_t step, uint8_t timeout)
|
||||
{
|
||||
OtaUpdateInformation * destination = moduleState[module].otaUpdateInformation;
|
||||
uint8_t elapsed = 0;
|
||||
|
||||
watchdogSuspend(100 /*1s*/);
|
||||
|
||||
while (step != destination->step) {
|
||||
if (elapsed++ > timeout) {
|
||||
return false;
|
||||
}
|
||||
RTOS_WAIT_MS(1);
|
||||
telemetryWakeup();
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
const char* Pxx2OtaUpdate::nextStep(uint8_t step, const char* rxName,
|
||||
uint32_t address, const uint8_t* buffer)
|
||||
{
|
||||
OtaUpdateInformation * destination = moduleState[module].otaUpdateInformation;
|
||||
|
||||
destination->step = step;
|
||||
destination->address = address;
|
||||
|
||||
for (uint8_t retry = 0;; retry++) {
|
||||
uint8_t* module_buffer = pulsesGetModuleBuffer(module);
|
||||
Pxx2Pulses pxx2(module_buffer);
|
||||
pxx2.sendOtaUpdate(module, rxName, address, (const char *) buffer);
|
||||
|
||||
// send the frame immediately
|
||||
auto mod = pulsesGetModuleDriver(module);
|
||||
auto mod_st = (etx_module_state_t*)mod->ctx;
|
||||
|
||||
auto drv = modulePortGetSerialDrv(mod_st->tx);
|
||||
auto ctx = modulePortGetCtx(mod_st->tx);
|
||||
drv->sendBuffer(ctx, module_buffer, pxx2.getSize());
|
||||
|
||||
if (waitStep(step + 1, 20)) {
|
||||
return nullptr;
|
||||
}
|
||||
else if (retry == 100) {
|
||||
return "Transfer failed";
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const char* Pxx2OtaUpdate::doFlashFirmware(const char* filename,
|
||||
ProgressHandler progressHandler)
|
||||
{
|
||||
FIL file;
|
||||
uint8_t buffer[32];
|
||||
UINT count;
|
||||
const char * result;
|
||||
|
||||
result = nextStep(OTA_UPDATE_START, rxName, 0, nullptr);
|
||||
if (result) {
|
||||
return result;
|
||||
}
|
||||
|
||||
if (f_open(&file, filename, FA_READ) != FR_OK) {
|
||||
return "Open file failed";
|
||||
}
|
||||
|
||||
uint32_t size;
|
||||
const char * ext = getFileExtension(filename);
|
||||
if (ext && !strcasecmp(ext, FRSKY_FIRMWARE_EXT)) {
|
||||
FrSkyFirmwareInformation * information = (FrSkyFirmwareInformation *) buffer;
|
||||
if (f_read(&file, buffer, sizeof(FrSkyFirmwareInformation), &count) != FR_OK ||
|
||||
count != sizeof(FrSkyFirmwareInformation)) {
|
||||
f_close(&file);
|
||||
return "Format error";
|
||||
}
|
||||
size = information->size;
|
||||
}
|
||||
else {
|
||||
size = f_size(&file);
|
||||
}
|
||||
|
||||
uint32_t done = 0;
|
||||
while (1) {
|
||||
progressHandler(getBasename(filename), STR_OTA_UPDATE, done, size);
|
||||
if (f_read(&file, buffer, sizeof(buffer), &count) != FR_OK) {
|
||||
f_close(&file);
|
||||
return "Read file failed";
|
||||
}
|
||||
|
||||
result = nextStep(OTA_UPDATE_TRANSFER, nullptr, done, buffer);
|
||||
if (result) {
|
||||
return result;
|
||||
}
|
||||
|
||||
if (count < sizeof(buffer)) {
|
||||
f_close(&file);
|
||||
break;
|
||||
}
|
||||
|
||||
done += count;
|
||||
}
|
||||
|
||||
return nextStep(OTA_UPDATE_EOF, nullptr, done, nullptr);
|
||||
}
|
||||
|
||||
void Pxx2OtaUpdate::flashFirmware(const char * filename, ProgressHandler progressHandler)
|
||||
{
|
||||
mixerTaskStop();
|
||||
|
||||
watchdogSuspend(100 /*1s*/);
|
||||
RTOS_WAIT_MS(100);
|
||||
|
||||
moduleState[module].mode = MODULE_MODE_OTA_UPDATE;
|
||||
const char * result = doFlashFirmware(filename, progressHandler);
|
||||
moduleState[module].mode = MODULE_MODE_NORMAL;
|
||||
|
||||
AUDIO_PLAY(AU_SPECIAL_SOUND_BEEP1 );
|
||||
BACKLIGHT_ENABLE();
|
||||
|
||||
if (result) {
|
||||
POPUP_WARNING(STR_FIRMWARE_UPDATE_ERROR, result);
|
||||
}
|
||||
else {
|
||||
POPUP_INFORMATION(STR_FIRMWARE_UPDATE_SUCCESS);
|
||||
}
|
||||
|
||||
watchdogSuspend(100);
|
||||
RTOS_WAIT_MS(100);
|
||||
|
||||
mixerTaskStart();
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,53 @@
|
||||
/*
|
||||
* Copyright (C) EdgeTX
|
||||
*
|
||||
* Based on code named
|
||||
* opentx - https://github.com/opentx/opentx
|
||||
* 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.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "pxx2.h"
|
||||
#include "popups.h"
|
||||
|
||||
class OtaUpdateInformation: public BindInformation {
|
||||
public:
|
||||
char filename[FF_MAX_LFN + 1];
|
||||
uint32_t address;
|
||||
uint32_t module;
|
||||
};
|
||||
|
||||
class Pxx2OtaUpdate {
|
||||
public:
|
||||
Pxx2OtaUpdate(uint8_t module, const char * rxName):
|
||||
module(module),
|
||||
rxName(rxName)
|
||||
{
|
||||
}
|
||||
|
||||
void flashFirmware(const char * filename, ProgressHandler progressHandler);
|
||||
|
||||
protected:
|
||||
uint8_t module;
|
||||
const char * rxName;
|
||||
|
||||
const char * doFlashFirmware(const char * filename, ProgressHandler progressHandler);
|
||||
bool waitStep(uint8_t step, uint8_t timeout);
|
||||
const char* nextStep(uint8_t step, const char* rxName, uint32_t address,
|
||||
const uint8_t* buffer);
|
||||
};
|
||||
|
||||
@@ -0,0 +1,156 @@
|
||||
/*
|
||||
* Copyright (C) EdgeTX
|
||||
*
|
||||
* Based on code named
|
||||
* opentx - https://github.com/opentx/opentx
|
||||
* 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.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
class Pxx2CrcMixin {
|
||||
protected:
|
||||
uint16_t crc = 0xFFFF;
|
||||
|
||||
void addToCrc(uint8_t byte)
|
||||
{
|
||||
crc -= byte;
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
class Pxx2Transport: public Pxx2CrcMixin {
|
||||
protected:
|
||||
uint8_t* buffer;
|
||||
uint8_t* ptr;
|
||||
|
||||
Pxx2Transport(uint8_t* buffer)
|
||||
: buffer(buffer), ptr(buffer)
|
||||
{}
|
||||
|
||||
void addWord(uint32_t word)
|
||||
{
|
||||
addByte(word);
|
||||
addByte(word >> 8);
|
||||
addByte(word >> 16);
|
||||
addByte(word >> 24);
|
||||
}
|
||||
|
||||
void addByte(uint8_t byte)
|
||||
{
|
||||
Pxx2CrcMixin::addToCrc(byte);
|
||||
addByteWithoutCrc(byte);
|
||||
};
|
||||
|
||||
void addByteWithoutCrc(uint8_t byte)
|
||||
{
|
||||
*ptr++ = byte;
|
||||
}
|
||||
|
||||
public:
|
||||
uint32_t getSize()
|
||||
{
|
||||
return ptr - buffer;
|
||||
}
|
||||
};
|
||||
|
||||
class Pxx2Pulses: public Pxx2Transport {
|
||||
friend class Pxx2OtaUpdate;
|
||||
|
||||
public:
|
||||
Pxx2Pulses(uint8_t* buffer)
|
||||
: Pxx2Transport(buffer)
|
||||
{
|
||||
addHead();
|
||||
}
|
||||
|
||||
bool setupFrame(uint8_t module, int16_t* channels, uint8_t nChannels);
|
||||
void setupAuthenticationFrame(uint8_t module, uint8_t mode, const uint8_t * outputMessage);
|
||||
|
||||
protected:
|
||||
void setupHardwareInfoFrame(uint8_t module, int16_t* channels, uint8_t nChannels);
|
||||
|
||||
void setupRegisterFrame(uint8_t module);
|
||||
|
||||
void setupBindFrame(uint8_t module);
|
||||
|
||||
void setupAccstBindFrame(uint8_t module);
|
||||
|
||||
void setupAccessBindFrame(uint8_t module);
|
||||
|
||||
void setupResetFrame(uint8_t module);
|
||||
|
||||
void setupShareMode(uint8_t module);
|
||||
|
||||
void setupModuleSettingsFrame(uint8_t module, int16_t* channels, uint8_t nChannels);
|
||||
|
||||
void setupReceiverSettingsFrame(uint8_t module, int16_t* channels, uint8_t nChannels);
|
||||
|
||||
void setupChannelsFrame(uint8_t module, int16_t* channels, uint8_t nChannels);
|
||||
|
||||
void setupTelemetryFrame(uint8_t module);
|
||||
|
||||
void setupSpectrumAnalyser(uint8_t module);
|
||||
|
||||
void setupPowerMeter(uint8_t module);
|
||||
|
||||
void sendOtaUpdate(uint8_t module, const char * rxName, uint32_t address, const char * data);
|
||||
|
||||
void addHead()
|
||||
{
|
||||
// send 7E, do not CRC
|
||||
Pxx2Transport::addByteWithoutCrc(0x7E);
|
||||
|
||||
// reserve 1 byte for LEN
|
||||
Pxx2Transport::addByteWithoutCrc(0x00);
|
||||
}
|
||||
|
||||
void addFrameType(uint8_t type_c, uint8_t type_id)
|
||||
{
|
||||
// TYPE_C + TYPE_ID
|
||||
Pxx2Transport::addByte(type_c);
|
||||
Pxx2Transport::addByte(type_id);
|
||||
}
|
||||
|
||||
uint8_t addFlag0(uint8_t module);
|
||||
|
||||
void addFlag1(uint8_t module);
|
||||
|
||||
void addPulsesValues(uint16_t low, uint16_t high);
|
||||
|
||||
void addChannels(uint8_t module, int16_t* channels, uint8_t nChannels);
|
||||
|
||||
void addFailsafe(uint8_t module);
|
||||
|
||||
void addCrc()
|
||||
{
|
||||
Pxx2Transport::addByteWithoutCrc(Pxx2CrcMixin::crc >> 8);
|
||||
Pxx2Transport::addByteWithoutCrc(Pxx2CrcMixin::crc);
|
||||
}
|
||||
|
||||
void endFrame()
|
||||
{
|
||||
if (getSize() <= 2) return;
|
||||
|
||||
// update the frame LEN = frame length minus the 2 first bytes
|
||||
buffer[1] = getSize() - 2;
|
||||
|
||||
// now add the CRC
|
||||
addCrc();
|
||||
}
|
||||
};
|
||||
+91
-74
@@ -19,56 +19,14 @@
|
||||
* GNU General Public License for more details.
|
||||
*/
|
||||
|
||||
#include "sbus.h"
|
||||
#include "hal/module_port.h"
|
||||
#include "mixer_scheduler.h"
|
||||
|
||||
#include "opentx.h"
|
||||
|
||||
|
||||
#define BITLEN_SBUS (10*2) // 100000 Baud => 10uS per bit
|
||||
|
||||
|
||||
/* The protocol reuse some the DSM2 definitions where they are identical */
|
||||
|
||||
|
||||
static void _send_level(uint8_t v)
|
||||
{
|
||||
/* Copied over from DSM, this looks doubious and in my logic analyzer
|
||||
output the low->high is about 2 ns late */
|
||||
if (extmodulePulsesData.dsm2.index & 1)
|
||||
v += 2;
|
||||
else
|
||||
v -= 2;
|
||||
|
||||
*extmodulePulsesData.dsm2.ptr++ = v - 1;
|
||||
extmodulePulsesData.dsm2.index+=1;
|
||||
}
|
||||
|
||||
void sendByteSbus(uint8_t b) // max 11 changes 0 10 10 10 10 P 1
|
||||
{
|
||||
bool lev = 0;
|
||||
uint8_t parity = 1;
|
||||
|
||||
uint8_t len = BITLEN_SBUS; //max val: 10*20 < 256
|
||||
for (uint8_t i=0; i<=9; i++) { //8Bits + 1Parity + Stop=1
|
||||
bool nlev = b & 1; //lsb first
|
||||
parity = parity ^ (uint8_t)nlev;
|
||||
if (lev == nlev) {
|
||||
len += BITLEN_SBUS;
|
||||
}
|
||||
else {
|
||||
_send_level(len);
|
||||
len = BITLEN_SBUS;
|
||||
lev = nlev;
|
||||
}
|
||||
b = (b>>1) | 0x80; //shift in ones for stop bit and parity
|
||||
if (i==7)
|
||||
b = b ^ parity; // lowest bit is one from previous line
|
||||
}
|
||||
_send_level(len+ BITLEN_SBUS); // enlarge the last bit to be two stop bits long
|
||||
}
|
||||
|
||||
|
||||
#define SBUS_NORMAL_CHANS 16
|
||||
#define SBUS_CHAN_BITS 11
|
||||
|
||||
#define SBUS_NORMAL_CHANS 16
|
||||
#define SBUS_CHAN_BITS 11
|
||||
|
||||
/* Definitions from CleanFlight/BetaFlight */
|
||||
|
||||
@@ -80,44 +38,39 @@ void sendByteSbus(uint8_t b) // max 11 changes 0 10 10 10 10 P 1
|
||||
|
||||
#define SBUS_CHAN_CENTER 992
|
||||
|
||||
inline int getChannelValue(uint8_t port, int channel)
|
||||
static inline void sendByte(uint8_t*& p_buf, uint8_t b)
|
||||
{
|
||||
int ch = g_model.moduleData[port].channelsStart+channel;
|
||||
*p_buf++ = b;
|
||||
}
|
||||
|
||||
static inline int getChannelValue(uint8_t port, int channel)
|
||||
{
|
||||
int ch = g_model.moduleData[port].channelsStart + channel;
|
||||
// We will ignore 17 and 18th if that brings us over the limit
|
||||
if (ch > 31)
|
||||
return 0;
|
||||
return channelOutputs[ch] + 2 * PPM_CH_CENTER(ch) - 2*PPM_CENTER;
|
||||
if (ch > 31) return 0;
|
||||
return channelOutputs[ch] + 2 * PPM_CH_CENTER(ch) - 2 * PPM_CENTER;
|
||||
}
|
||||
|
||||
static void sbusFlush()
|
||||
static void setupPulsesSbus(uint8_t module, uint8_t*& p_buf)
|
||||
{
|
||||
if (extmodulePulsesData.dsm2.index & 1)
|
||||
*extmodulePulsesData.dsm2.ptr++ = 255;
|
||||
else
|
||||
*(extmodulePulsesData.dsm2.ptr - 1) = 255;
|
||||
}
|
||||
|
||||
void setupPulsesSbus()
|
||||
{
|
||||
extmodulePulsesData.dsm2.index = 0;
|
||||
|
||||
extmodulePulsesData.dsm2.ptr = extmodulePulsesData.dsm2.pulses;
|
||||
// extmodulePulsesData.dsm2.index = 0;
|
||||
// extmodulePulsesData.dsm2.ptr = extmodulePulsesData.dsm2.pulses;
|
||||
|
||||
// Sync Byte
|
||||
sendByteSbus(SBUS_FRAME_BEGIN_BYTE);
|
||||
sendByte(p_buf, SBUS_FRAME_BEGIN_BYTE);
|
||||
|
||||
uint32_t bits = 0;
|
||||
uint8_t bitsavailable = 0;
|
||||
|
||||
// byte 1-22, channels 0..2047, limits not really clear (B
|
||||
for (int i=0; i<SBUS_NORMAL_CHANS; i++) {
|
||||
int value = getChannelValue(EXTERNAL_MODULE, i);
|
||||
int value = getChannelValue(module, i);
|
||||
|
||||
value = value*8/10 + SBUS_CHAN_CENTER;
|
||||
bits |= limit(0, value, 2047) << bitsavailable;
|
||||
bitsavailable += SBUS_CHAN_BITS;
|
||||
while (bitsavailable >= 8) {
|
||||
sendByteSbus((uint8_t) (bits & 0xff));
|
||||
sendByte(p_buf, (uint8_t) (bits & 0xff));
|
||||
bits >>= 8;
|
||||
bitsavailable -= 8;
|
||||
}
|
||||
@@ -125,15 +78,79 @@ void setupPulsesSbus()
|
||||
|
||||
// flags
|
||||
uint8_t flags=0;
|
||||
if (getChannelValue(EXTERNAL_MODULE, 16) > 0)
|
||||
if (getChannelValue(module, 16) > 0)
|
||||
flags |= SBUS_FLAG_CHANNEL_17;
|
||||
if (getChannelValue(EXTERNAL_MODULE, 17) > 0)
|
||||
if (getChannelValue(module, 17) > 0)
|
||||
flags |= SBUS_FLAG_CHANNEL_18;
|
||||
|
||||
sendByteSbus(flags);
|
||||
sendByte(p_buf, flags);
|
||||
|
||||
// last byte, always 0x0
|
||||
sendByteSbus(0x00);
|
||||
|
||||
sbusFlush();
|
||||
sendByte(p_buf, 0x00);
|
||||
}
|
||||
|
||||
|
||||
#define SBUS_BAUDRATE 100000
|
||||
|
||||
const etx_serial_init sbusUartParams = {
|
||||
.baudrate = SBUS_BAUDRATE,
|
||||
.encoding = ETX_Encoding_8E2,
|
||||
.direction = ETX_Dir_TX,
|
||||
.polarity = ETX_Pol_Normal,
|
||||
};
|
||||
|
||||
static void* sbusInit(uint8_t module)
|
||||
{
|
||||
#if defined(HARDWARE_INTERNAL_MODULE)
|
||||
// only external module supported
|
||||
if (module == INTERNAL_MODULE) return nullptr;
|
||||
#endif
|
||||
|
||||
// auto mod_st = modulePortInitSerial(module, ETX_MOD_PORT_UART, &sbusUartParams);
|
||||
// TODO: check if inverter is there, or mandate it somehow...
|
||||
// if (!mod_st) {
|
||||
// -> SOFT_INV has 'setPolarity'
|
||||
auto mod_st = modulePortInitSerial(module, ETX_MOD_PORT_SOFT_INV, &sbusUartParams);
|
||||
if (!mod_st) return nullptr;
|
||||
// }
|
||||
|
||||
mixerSchedulerSetPeriod(module, SBUS_PERIOD(module));
|
||||
return (void*)mod_st;
|
||||
}
|
||||
|
||||
static void sbusDeInit(void* ctx)
|
||||
{
|
||||
auto mod_st = (etx_module_state_t*)ctx;
|
||||
modulePortDeInit(mod_st);
|
||||
}
|
||||
|
||||
static void sbusSendPulses(void* ctx, uint8_t* buffer, int16_t* channels, uint8_t nChannels)
|
||||
{
|
||||
// TODO:
|
||||
(void)channels;
|
||||
(void)nChannels;
|
||||
|
||||
auto mod_st = (etx_module_state_t*)ctx;
|
||||
auto module = modulePortGetModule(mod_st);
|
||||
|
||||
auto p_data = buffer;
|
||||
setupPulsesSbus(module, p_data);
|
||||
|
||||
auto drv = modulePortGetSerialDrv(mod_st->tx);
|
||||
auto drv_ctx = modulePortGetCtx(mod_st->tx);
|
||||
|
||||
// TODO: check if the port has an inverter and use it instead
|
||||
drv->setPolarity(drv_ctx, GET_SBUS_POLARITY(module));
|
||||
drv->sendBuffer(drv_ctx, buffer, p_data - buffer);
|
||||
|
||||
// SBUS_PERIOD is not a constant! It can be set from UI
|
||||
mixerSchedulerSetPeriod(module, SBUS_PERIOD(module));
|
||||
}
|
||||
|
||||
const etx_proto_driver_t SBusDriver = {
|
||||
.protocol = PROTOCOL_CHANNELS_SBUS,
|
||||
.init = sbusInit,
|
||||
.deinit = sbusDeInit,
|
||||
.sendPulses = sbusSendPulses,
|
||||
.processData = nullptr,
|
||||
};
|
||||
|
||||
@@ -0,0 +1,26 @@
|
||||
/*
|
||||
* Copyright (C) EdgeTx
|
||||
*
|
||||
* Based on code named
|
||||
* opentx - https://github.com/opentx/opentx
|
||||
* 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.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "hal/module_driver.h"
|
||||
|
||||
extern const etx_proto_driver_t SBusDriver;
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user