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:
Raphael Coeffic
2023-02-09 19:44:32 +10:00
committed by GitHub
parent d80adc6c4d
commit 72193eda82
189 changed files with 7960 additions and 8364 deletions
+2
View File
@@ -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
View File
@@ -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();
}
}
@@ -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
View File
@@ -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(&params);
spInternalModuleCTX = uart_ctx;
spInternalModuleState = modulePortInitSerial(port_n, ETX_MOD_PORT_UART,
&params);
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(&params);
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();
}
+18 -8
View File
@@ -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,
-59
View File
@@ -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)
-83
View File
@@ -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_
+22 -15
View File
@@ -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
+7 -4
View File
@@ -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;
+20 -17
View File
@@ -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);
+3 -2
View File
@@ -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;
+10 -5
View File
@@ -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)
+13 -13
View File
@@ -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
+1 -9
View File
@@ -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;
+18 -15
View File
@@ -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);
+3 -2
View File
@@ -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;
+7 -6
View File
@@ -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);
});
}
+8 -53
View File
@@ -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];
+1 -1
View File
@@ -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); });
+7 -6
View File
@@ -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);
}
+10 -8
View File
@@ -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;
+2 -49
View File
@@ -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);
}
+1 -1
View File
@@ -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));
+1 -1
View File
@@ -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
+1 -1
View File
@@ -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"
+14 -21
View File
@@ -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_
}
}
+4 -2
View File
@@ -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);
+4 -1
View File
@@ -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();
}
+14 -6
View File
@@ -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};
+6 -79
View File
@@ -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);
}
}
+9 -8
View File
@@ -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);
}
+9 -8
View File
@@ -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
+14 -10
View File
@@ -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);
+37 -39
View File
@@ -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);
+19 -3
View File
@@ -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) {
+28 -8
View File
@@ -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;
+16 -11
View File
@@ -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
+6 -17
View File
@@ -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);
+254
View File
@@ -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;
}
+158
View File
@@ -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;
}
+46 -20
View File
@@ -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;
+1
View File
@@ -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;
+41
View File
@@ -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;
+2 -2
View File
@@ -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();
}
+188 -487
View File
@@ -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(&params);
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(&params);
}
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;
}
+6 -20
View File
@@ -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_
+130 -241
View File
@@ -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, &params);
if (!mod_st) {
params.polarity = ETX_Pol_Normal;
mod_st = modulePortInitSerial(EXTERNAL_MODULE, ETX_MOD_PORT_SOFT_INV, &params);
if (!mod_st) return false;
}
void init(bool inverted) override
{
etx_serial_init params(serialInitParams);
params.baudrate = 57600;
uart_ctx = IntmoduleSerialDriver.init(&params);
// RX on S.PORT (inverted softserial)
params.direction = ETX_Dir_RX;
params.polarity = ETX_Pol_Inverted;
if (!modulePortInitSerial(EXTERNAL_MODULE, ETX_MOD_PORT_SPORT, &params)) {
params.polarity = ETX_Pol_Normal;
if (!modulePortInitSerial(EXTERNAL_MODULE, ETX_MOD_PORT_SPORT_INV, &params)) {
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;
}
+6 -2
View File
@@ -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);
+1 -1
View File
@@ -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
View File
@@ -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);
+30 -17
View File
@@ -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);
}
+4
View File
@@ -31,6 +31,10 @@
#include <storage/sdcard_yaml.h>
#endif
#if defined(MULTIMODULE)
#include "pulses/multi.h"
#endif
/*luadoc
@function model.getInfo()
+156 -2
View File
@@ -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;
}
+6
View File
@@ -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;
+4
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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,
};
+1 -12
View File
@@ -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
View File
@@ -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, &params);
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, &params);
// 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
}
+1 -5
View File
@@ -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);
+19 -143
View File
@@ -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)
{
+7 -22
View File
@@ -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
View File
@@ -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(&params), 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, &params);
}
#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, &params);
}
#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,
};
+1 -2
View File
@@ -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
View File
@@ -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, &params);
if (!mod_st) {
// inverted soft-serial fallback
mod_st = modulePortInitSerial(module, ETX_MOD_PORT_SOFT_INV, &params);
if (!mod_st) return nullptr;
}
if (telemetry) {
params.direction = ETX_Dir_RX;
if (!modulePortInitSerial(module, ETX_MOD_PORT_SPORT, &params)) {
// inverted soft-serial fallback
modulePortInitSerial(module, ETX_MOD_PORT_SPORT_INV, &params);
}
}
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,
};
+30
View File
@@ -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
View File
@@ -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]);
}
+1 -1
View File
@@ -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
View File
@@ -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,
};
@@ -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;
+6 -17
View File
@@ -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
View File
@@ -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
}
+2 -6
View File
@@ -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
View File
@@ -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,
};
+26
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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;
+212
View File
@@ -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
View File
@@ -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, &params);
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, &params);
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(&params));
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
View File
@@ -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
+160
View File
@@ -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();
}
+53
View File
@@ -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);
};
+156
View File
@@ -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
View File
@@ -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,
};
+26
View File
@@ -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