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
+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;
}