1
0
Fork 0
mirror of https://github.com/opentx/opentx.git synced 2025-07-25 09:15:38 +03:00

mixer scheduler

This commit is contained in:
Raphael Coeffic 2019-10-19 23:08:51 +02:00
parent 17feb48d9d
commit 02660c763a
32 changed files with 644 additions and 314 deletions

View file

@ -378,6 +378,7 @@ set(SRC
strhelpers.cpp strhelpers.cpp
switches.cpp switches.cpp
mixer.cpp mixer.cpp
mixer_scheduler.cpp
stamp.cpp stamp.cpp
timers.cpp timers.cpp
trainer.cpp trainer.cpp

View file

@ -185,6 +185,7 @@ inline uint8_t MODULE_CHANNELS_ROWS(int moduleIdx)
} }
#if defined(MULTIMODULE) #if defined(MULTIMODULE)
inline uint8_t MULTI_DISABLE_CHAN_MAP_ROW(uint8_t moduleIdx) inline uint8_t MULTI_DISABLE_CHAN_MAP_ROW(uint8_t moduleIdx)
{ {
if (!isModuleMultimodule(moduleIdx)) if (!isModuleMultimodule(moduleIdx))
@ -276,7 +277,7 @@ inline uint8_t MULTIMODULE_HASOPTIONS(uint8_t moduleIdx)
#define MULTIMODULE_MODULE_ROWS(moduleIdx) (MULTIMODULE_PROTOCOL_KNOWN(moduleIdx) && !IS_RX_MULTI(moduleIdx)) ? (uint8_t) 0 : HIDDEN_ROW, (MULTIMODULE_PROTOCOL_KNOWN(moduleIdx) && !IS_RX_MULTI(moduleIdx)) ? (uint8_t) 0 : HIDDEN_ROW, MULTI_DISABLE_CHAN_MAP_ROW(moduleIdx), // AUTOBIND, DISABLE TELEM, DISABLE CN.MAP #define MULTIMODULE_MODULE_ROWS(moduleIdx) (MULTIMODULE_PROTOCOL_KNOWN(moduleIdx) && !IS_RX_MULTI(moduleIdx)) ? (uint8_t) 0 : HIDDEN_ROW, (MULTIMODULE_PROTOCOL_KNOWN(moduleIdx) && !IS_RX_MULTI(moduleIdx)) ? (uint8_t) 0 : HIDDEN_ROW, MULTI_DISABLE_CHAN_MAP_ROW(moduleIdx), // AUTOBIND, DISABLE TELEM, DISABLE CN.MAP
#define MULTIMODULE_TYPE_ROW(moduleIdx) isModuleMultimodule(moduleIdx) ? MULTIMODULE_RFPROTO_COLUMNS(moduleIdx) : HIDDEN_ROW, #define MULTIMODULE_TYPE_ROW(moduleIdx) isModuleMultimodule(moduleIdx) ? MULTIMODULE_RFPROTO_COLUMNS(moduleIdx) : HIDDEN_ROW,
#define MULTIMODULE_STATUS_ROWS(moduleIdx) isModuleMultimodule(moduleIdx) ? TITLE_ROW : HIDDEN_ROW, (isModuleMultimodule(moduleIdx) && getMultiSyncStatus(moduleIdx).isValid()) ? TITLE_ROW : HIDDEN_ROW, #define MULTIMODULE_STATUS_ROWS(moduleIdx) isModuleMultimodule(moduleIdx) ? TITLE_ROW : HIDDEN_ROW, (isModuleMultimodule(moduleIdx) && getModuleSyncStatus(moduleIdx).isValid()) ? TITLE_ROW : HIDDEN_ROW,
#define MULTIMODULE_MODE_ROWS(moduleIdx) (g_model.moduleData[moduleIdx].multi.customProto) ? (uint8_t) 3 : MULTIMODULE_HAS_SUBTYPE(moduleIdx) ? (uint8_t)2 : (uint8_t)1 #define MULTIMODULE_MODE_ROWS(moduleIdx) (g_model.moduleData[moduleIdx].multi.customProto) ? (uint8_t) 3 : MULTIMODULE_HAS_SUBTYPE(moduleIdx) ? (uint8_t)2 : (uint8_t)1
#define MULTIMODULE_TYPE_ROWS(moduleIdx) isModuleMultimodule(moduleIdx) ? (uint8_t) 0 : HIDDEN_ROW, #define MULTIMODULE_TYPE_ROWS(moduleIdx) isModuleMultimodule(moduleIdx) ? (uint8_t) 0 : HIDDEN_ROW,
#define MULTIMODULE_SUBTYPE_ROWS(moduleIdx) isModuleMultimodule(moduleIdx) ? MULTIMODULE_RFPROTO_COLUMNS(moduleIdx) : HIDDEN_ROW, #define MULTIMODULE_SUBTYPE_ROWS(moduleIdx) isModuleMultimodule(moduleIdx) ? MULTIMODULE_RFPROTO_COLUMNS(moduleIdx) : HIDDEN_ROW,

View file

@ -0,0 +1,79 @@
/*
* Copyright (C) OpenTX
*
* Based on code named
* th9x - http://code.google.com/p/th9x
* er9x - http://code.google.com/p/er9x
* gruvin9x - http://code.google.com/p/gruvin9x
*
* License GPLv2: http://www.gnu.org/licenses/gpl-2.0.html
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*/
#include "opentx.h"
#include "mixer_scheduler.h"
#if !defined(SIMU)
// Global trigger flag
RTOS_FLAG_HANDLE mixerFlag;
// Mixer schedule
struct MixerSchedule {
// period in us
volatile uint16_t period;
};
static MixerSchedule mixerSchedules[NUM_MODULES];
uint16_t getMixerSchedulerPeriod()
{
if (mixerSchedules[INTERNAL_MODULE].period) {
return mixerSchedules[INTERNAL_MODULE].period;
}
else if (mixerSchedules[EXTERNAL_MODULE].period) {
return mixerSchedules[EXTERNAL_MODULE].period;
}
return MIXER_SCHEDULER_DEFAULT_PERIOD_US;
}
void mixerSchedulerInit()
{
RTOS_CREATE_FLAG(mixerFlag);
memset(mixerSchedules, 0, sizeof(mixerSchedules));
}
void mixerSchedulerSetPeriod(uint8_t moduleIdx, uint16_t periodUs)
{
if ((periodUs > 0) && (periodUs < MIN_REFRESH_RATE)) {
periodUs = MIN_REFRESH_RATE;
}
else if ((periodUs > 0) && (periodUs > MAX_REFRESH_RATE)) {
periodUs = MAX_REFRESH_RATE;
}
mixerSchedules[moduleIdx].period = periodUs;
}
bool mixerSchedulerWaitForTrigger(uint8_t timeoutMs)
{
RTOS_CLEAR_FLAG(mixerFlag);
return RTOS_WAIT_FLAG(mixerFlag, timeoutMs);
}
void mixerSchedulerISRTrigger()
{
RTOS_ISR_SET_FLAG(mixerFlag);
}
#endif

View file

@ -0,0 +1,84 @@
/*
* Copyright (C) OpenTX
*
* Based on code named
* th9x - http://code.google.com/p/th9x
* er9x - http://code.google.com/p/er9x
* gruvin9x - http://code.google.com/p/gruvin9x
*
* License GPLv2: http://www.gnu.org/licenses/gpl-2.0.html
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*/
#ifndef _MIXER_SCHEDULER_H_
#define _MIXER_SCHEDULER_H_
#define MIXER_SCHEDULER_DEFAULT_PERIOD_US 4000u // 4ms
#define MIN_REFRESH_RATE 4000 /* us */
#define MAX_REFRESH_RATE 25000 /* us */
#if !defined(SIMU)
// Call once to initialize the mixer scheduler
void mixerSchedulerInit();
// Configure and start the scheduler timer
void mixerSchedulerStart();
// Stop the scheduler timer
void mixerSchedulerStop();
// Set the timer counter to 0
void mixerSchedulerResetTimer();
// Set the scheduling period for a given module
void mixerSchedulerSetPeriod(uint8_t moduleIdx, uint16_t periodUs);
// Wait for the scheduler timer to trigger
// returns true if timeout, false otherwise
bool mixerSchedulerWaitForTrigger(uint8_t timeoutMs);
// Enable the timer trigger
void mixerSchedulerEnableTrigger();
// Disable the timer trigger
void mixerSchedulerDisableTrigger();
// Fetch the current scheduling period
uint16_t getMixerSchedulerPeriod();
// Trigger mixer from an ISR
void mixerSchedulerISRTrigger();
#else
#define mixerSchedulerInit()
#define mixerSchedulerStart()
#define mixerSchedulerStop()
#define mixerSchedulerResetTimer()
#define mixerSchedulerSetPeriod(m,p)
static inline bool mixerSchedulerWaitForTrigger(uint8_t timeout)
{
simuSleep(timeout);
return false;
}
#define mixerSchedulerEnableTrigger()
#define mixerSchedulerDisableTrigger()
#define getMixerSchedulerPeriod() (MIXER_SCHEDULER_DEFAULT_PERIOD_US)
#define mixerSchedulerISRTrigger()
#endif
#endif

View file

@ -483,7 +483,6 @@ extern uint32_t nextMixerTime[NUM_MODULES];
void evalFlightModeMixes(uint8_t mode, uint8_t tick10ms); void evalFlightModeMixes(uint8_t mode, uint8_t tick10ms);
void evalMixes(uint8_t tick10ms); void evalMixes(uint8_t tick10ms);
void doMixerCalculations(); void doMixerCalculations();
void scheduleNextMixerCalculation(uint8_t module, uint32_t period_ms);
void checkTrims(); void checkTrims();
extern uint8_t currentBacklightBright; extern uint8_t currentBacklightBright;

View file

@ -76,7 +76,6 @@ void _send_1(uint8_t v)
*extmodulePulsesData.dsm2.ptr++ = v - 1; *extmodulePulsesData.dsm2.ptr++ = v - 1;
extmodulePulsesData.dsm2.index += 1; extmodulePulsesData.dsm2.index += 1;
extmodulePulsesData.dsm2.rest -= v;
} }
void sendByteDsm2(uint8_t b) // max 10 changes 0 10 10 10 10 1 void sendByteDsm2(uint8_t b) // max 10 changes 0 10 10 10 10 1
@ -101,9 +100,9 @@ void sendByteDsm2(uint8_t b) // max 10 changes 0 10 10 10 10 1
void putDsm2Flush() void putDsm2Flush()
{ {
if (extmodulePulsesData.dsm2.index & 1) if (extmodulePulsesData.dsm2.index & 1)
*extmodulePulsesData.dsm2.ptr++ = extmodulePulsesData.dsm2.rest; *extmodulePulsesData.dsm2.ptr++ = 60000;
else else
*(extmodulePulsesData.dsm2.ptr - 1) = extmodulePulsesData.dsm2.rest; *(extmodulePulsesData.dsm2.ptr - 1) = 60000;
} }
#endif #endif
@ -119,7 +118,6 @@ void setupPulsesDSM2()
extmodulePulsesData.dsm2.serialBitCount = 0 ; extmodulePulsesData.dsm2.serialBitCount = 0 ;
#else #else
extmodulePulsesData.dsm2.index = 0; extmodulePulsesData.dsm2.index = 0;
extmodulePulsesData.dsm2.rest = DSM2_PERIOD * 2000;
#endif #endif
extmodulePulsesData.dsm2.ptr = extmodulePulsesData.dsm2.pulses; extmodulePulsesData.dsm2.ptr = extmodulePulsesData.dsm2.pulses;

View file

@ -209,10 +209,8 @@ void setupPulsesMultiExternalModule()
extmodulePulsesData.dsm2.serialByte = 0 ; extmodulePulsesData.dsm2.serialByte = 0 ;
extmodulePulsesData.dsm2.serialBitCount = 0 ; extmodulePulsesData.dsm2.serialBitCount = 0 ;
#else #else
extmodulePulsesData.dsm2.rest = getMultiSyncStatus(EXTERNAL_MODULE).getAdjustedRefreshRate();
extmodulePulsesData.dsm2.index = 0; extmodulePulsesData.dsm2.index = 0;
#endif #endif
extmodulePulsesData.dsm2.ptr = extmodulePulsesData.dsm2.pulses; extmodulePulsesData.dsm2.ptr = extmodulePulsesData.dsm2.pulses;
setupPulsesMulti(EXTERNAL_MODULE); setupPulsesMulti(EXTERNAL_MODULE);

View file

@ -21,6 +21,7 @@
#include "opentx.h" #include "opentx.h"
#include "io/frsky_pxx2.h" #include "io/frsky_pxx2.h"
#include "pulses/pxx2.h" #include "pulses/pxx2.h"
#include "mixer_scheduler.h"
uint8_t s_pulses_paused = 0; uint8_t s_pulses_paused = 0;
ModuleState moduleState[NUM_MODULES]; ModuleState moduleState[NUM_MODULES];
@ -62,7 +63,7 @@ void getModuleSyncStatusString(uint8_t moduleIdx, char * statusText)
*statusText = 0; *statusText = 0;
#if defined(MULTIMODULE) #if defined(MULTIMODULE)
if (isModuleMultimodule(moduleIdx)) { if (isModuleMultimodule(moduleIdx)) {
getMultiSyncStatus(moduleIdx).getRefreshString(statusText); getModuleSyncStatus(moduleIdx).getRefreshString(statusText);
} }
#endif #endif
#if defined(AFHDS3) #if defined(AFHDS3)
@ -225,12 +226,14 @@ void enablePulsesExternalModule(uint8_t protocol)
#if defined(PXX1) #if defined(PXX1)
case PROTOCOL_CHANNELS_PXX1_PULSES: case PROTOCOL_CHANNELS_PXX1_PULSES:
extmodulePxx1PulsesStart(); extmodulePxx1PulsesStart();
mixerSchedulerSetPeriod(EXTERNAL_MODULE, PXX_PULSES_PERIOD);
break; break;
#endif #endif
#if defined(PXX1) && defined(HARDWARE_EXTERNAL_MODULE_SIZE_SML) #if defined(PXX1) && defined(HARDWARE_EXTERNAL_MODULE_SIZE_SML)
case PROTOCOL_CHANNELS_PXX1_SERIAL: case PROTOCOL_CHANNELS_PXX1_SERIAL:
extmodulePxx1SerialStart(); extmodulePxx1SerialStart();
mixerSchedulerSetPeriod(EXTERNAL_MODULE, EXTMODULE_PXX1_SERIAL_PERIOD);
break; break;
#endif #endif
@ -238,59 +241,69 @@ void enablePulsesExternalModule(uint8_t protocol)
case PROTOCOL_CHANNELS_DSM2_LP45: case PROTOCOL_CHANNELS_DSM2_LP45:
case PROTOCOL_CHANNELS_DSM2_DSM2: case PROTOCOL_CHANNELS_DSM2_DSM2:
case PROTOCOL_CHANNELS_DSM2_DSMX: case PROTOCOL_CHANNELS_DSM2_DSMX:
extmoduleSerialStart(DSM2_BAUDRATE, DSM2_PERIOD * 2000, false); extmoduleSerialStart();
mixerSchedulerSetPeriod(EXTERNAL_MODULE, DSM2_PERIOD);
break; break;
#endif #endif
#if defined(CROSSFIRE) #if defined(CROSSFIRE)
case PROTOCOL_CHANNELS_CROSSFIRE: case PROTOCOL_CHANNELS_CROSSFIRE:
EXTERNAL_MODULE_ON(); EXTERNAL_MODULE_ON();
mixerSchedulerSetPeriod(EXTERNAL_MODULE, CROSSFIRE_PERIOD);
break; break;
#endif #endif
#if defined(GHOST) #if defined(GHOST)
case PROTOCOL_CHANNELS_GHOST: case PROTOCOL_CHANNELS_GHOST:
EXTERNAL_MODULE_ON(); EXTERNAL_MODULE_ON();
mixerSchedulerSetPeriod(EXTERNAL_MODULE, GHOST_PERIOD);
break; break;
#endif #endif
#if defined(PXX2) && defined(EXTMODULE_USART) #if defined(PXX2) && defined(EXTMODULE_USART)
case PROTOCOL_CHANNELS_PXX2_HIGHSPEED: case PROTOCOL_CHANNELS_PXX2_HIGHSPEED:
extmoduleInvertedSerialStart(PXX2_HIGHSPEED_BAUDRATE); extmoduleInvertedSerialStart(PXX2_HIGHSPEED_BAUDRATE);
mixerSchedulerSetPeriod(EXTERNAL_MODULE, PXX2_PERIOD);
break; break;
case PROTOCOL_CHANNELS_PXX2_LOWSPEED: case PROTOCOL_CHANNELS_PXX2_LOWSPEED:
extmoduleInvertedSerialStart(PXX2_LOWSPEED_BAUDRATE); extmoduleInvertedSerialStart(PXX2_LOWSPEED_BAUDRATE);
mixerSchedulerSetPeriod(EXTERNAL_MODULE, PXX2_PERIOD);
break; break;
#endif #endif
#if defined(MULTIMODULE) #if defined(MULTIMODULE)
case PROTOCOL_CHANNELS_MULTIMODULE: case PROTOCOL_CHANNELS_MULTIMODULE:
extmoduleSerialStart(MULTIMODULE_BAUDRATE, MULTIMODULE_PERIOD * 2000, true); extmoduleSerialStart();
mixerSchedulerSetPeriod(EXTERNAL_MODULE, MULTIMODULE_PERIOD);
break; break;
#endif #endif
#if defined(SBUS) #if defined(SBUS)
case PROTOCOL_CHANNELS_SBUS: case PROTOCOL_CHANNELS_SBUS:
extmoduleSerialStart(SBUS_BAUDRATE, SBUS_PERIOD_HALF_US, false); extmoduleSerialStart();
mixerSchedulerSetPeriod(EXTERNAL_MODULE, SBUS_PERIOD);
break; break;
#endif #endif
#if defined(PPM) #if defined(PPM)
case PROTOCOL_CHANNELS_PPM: case PROTOCOL_CHANNELS_PPM:
extmodulePpmStart(); extmodulePpmStart();
mixerSchedulerSetPeriod(EXTERNAL_MODULE, PPM_PERIOD(EXTERNAL_MODULE));
break; break;
#endif #endif
#if defined(AFHDS3) #if defined(AFHDS3)
case PROTOCOL_CHANNELS_AFHDS3: case PROTOCOL_CHANNELS_AFHDS3:
extmodulePulsesData.afhds3.init(EXTERNAL_MODULE); extmodulePulsesData.afhds3.init(EXTERNAL_MODULE);
extmoduleSerialStart(AFHDS3_BAUDRATE, AFHDS3_COMMAND_TIMEOUT * 2000, false); extmoduleSerialStart(/*AFHDS3_BAUDRATE, AFHDS3_COMMAND_TIMEOUT * 2000, false*/);
mixerSchedulerSetPeriod(EXTERNAL_MODULE, AFHDS3_COMMAND_TIMEOUT * 1000 /* us */);
break; break;
#endif #endif
default: default:
// external module stopped, set period to 50ms (necessary for USB Joystick, for instance)
mixerSchedulerSetPeriod(EXTERNAL_MODULE, 50000/*us*/);
break; break;
} }
} }
@ -301,14 +314,12 @@ bool setupPulsesExternalModule(uint8_t protocol)
#if defined(PXX1) #if defined(PXX1)
case PROTOCOL_CHANNELS_PXX1_PULSES: case PROTOCOL_CHANNELS_PXX1_PULSES:
extmodulePulsesData.pxx.setupFrame(EXTERNAL_MODULE); extmodulePulsesData.pxx.setupFrame(EXTERNAL_MODULE);
scheduleNextMixerCalculation(EXTERNAL_MODULE, PXX_PULSES_PERIOD);
return true; return true;
#endif #endif
#if defined(PXX1) && defined(HARDWARE_EXTERNAL_MODULE_SIZE_SML) #if defined(PXX1) && defined(HARDWARE_EXTERNAL_MODULE_SIZE_SML)
case PROTOCOL_CHANNELS_PXX1_SERIAL: case PROTOCOL_CHANNELS_PXX1_SERIAL:
extmodulePulsesData.pxx_uart.setupFrame(EXTERNAL_MODULE); extmodulePulsesData.pxx_uart.setupFrame(EXTERNAL_MODULE);
scheduleNextMixerCalculation(EXTERNAL_MODULE, EXTMODULE_PXX1_SERIAL_PERIOD);
return true; return true;
#endif #endif
@ -316,14 +327,12 @@ bool setupPulsesExternalModule(uint8_t protocol)
case PROTOCOL_CHANNELS_PXX2_HIGHSPEED: case PROTOCOL_CHANNELS_PXX2_HIGHSPEED:
case PROTOCOL_CHANNELS_PXX2_LOWSPEED: case PROTOCOL_CHANNELS_PXX2_LOWSPEED:
extmodulePulsesData.pxx2.setupFrame(EXTERNAL_MODULE); extmodulePulsesData.pxx2.setupFrame(EXTERNAL_MODULE);
scheduleNextMixerCalculation(EXTERNAL_MODULE, PXX2_PERIOD);
return true; return true;
#endif #endif
#if defined(SBUS) #if defined(SBUS)
case PROTOCOL_CHANNELS_SBUS: case PROTOCOL_CHANNELS_SBUS:
setupPulsesSbus(); setupPulsesSbus();
scheduleNextMixerCalculation(EXTERNAL_MODULE, SBUS_PERIOD);
return true; return true;
#endif #endif
@ -332,47 +341,61 @@ bool setupPulsesExternalModule(uint8_t protocol)
case PROTOCOL_CHANNELS_DSM2_DSM2: case PROTOCOL_CHANNELS_DSM2_DSM2:
case PROTOCOL_CHANNELS_DSM2_DSMX: case PROTOCOL_CHANNELS_DSM2_DSMX:
setupPulsesDSM2(); setupPulsesDSM2();
scheduleNextMixerCalculation(EXTERNAL_MODULE, DSM2_PERIOD);
return true; return true;
#endif #endif
#if defined(CROSSFIRE) #if defined(CROSSFIRE)
case PROTOCOL_CHANNELS_CROSSFIRE: case PROTOCOL_CHANNELS_CROSSFIRE:
{
ModuleSyncStatus& status = getModuleSyncStatus(EXTERNAL_MODULE);
if (status.isValid())
mixerSchedulerSetPeriod(EXTERNAL_MODULE, status.getAdjustedRefreshRate());
else
mixerSchedulerSetPeriod(EXTERNAL_MODULE, CROSSFIRE_PERIOD);
setupPulsesCrossfire(); setupPulsesCrossfire();
scheduleNextMixerCalculation(EXTERNAL_MODULE, CROSSFIRE_PERIOD);
return true; return true;
}
#endif #endif
#if defined(GHOST) #if defined(GHOST)
case PROTOCOL_CHANNELS_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(); setupPulsesGhost();
scheduleNextMixerCalculation(EXTERNAL_MODULE, GHOST_PERIOD);
return true; return true;
}
#endif #endif
#if defined(MULTIMODULE) #if defined(MULTIMODULE)
case PROTOCOL_CHANNELS_MULTIMODULE: case PROTOCOL_CHANNELS_MULTIMODULE:
{
ModuleSyncStatus& status = getModuleSyncStatus(EXTERNAL_MODULE);
if (status.isValid())
mixerSchedulerSetPeriod(EXTERNAL_MODULE, status.getAdjustedRefreshRate());
else
mixerSchedulerSetPeriod(EXTERNAL_MODULE, MULTIMODULE_PERIOD);
setupPulsesMultiExternalModule(); setupPulsesMultiExternalModule();
scheduleNextMixerCalculation(EXTERNAL_MODULE, MULTIMODULE_PERIOD);
return true; return true;
}
#endif #endif
#if defined(PPM) #if defined(PPM)
case PROTOCOL_CHANNELS_PPM: case PROTOCOL_CHANNELS_PPM:
setupPulsesPPMExternalModule(); setupPulsesPPMExternalModule();
scheduleNextMixerCalculation(EXTERNAL_MODULE, PPM_PERIOD(EXTERNAL_MODULE));
return true; return true;
#endif #endif
#if defined(AFHDS3) #if defined(AFHDS3)
case PROTOCOL_CHANNELS_AFHDS3: case PROTOCOL_CHANNELS_AFHDS3:
extmodulePulsesData.afhds3.setupFrame(); extmodulePulsesData.afhds3.setupFrame();
scheduleNextMixerCalculation(EXTERNAL_MODULE, AFHDS3_COMMAND_TIMEOUT);
return true; return true;
#endif #endif
default: default:
scheduleNextMixerCalculation(EXTERNAL_MODULE, 50/*ms*/);
return false; return false;
} }
} }
@ -386,12 +409,24 @@ static void enablePulsesInternalModule(uint8_t protocol)
#if defined(PXX1) && !defined(INTMODULE_USART) #if defined(PXX1) && !defined(INTMODULE_USART)
case PROTOCOL_CHANNELS_PXX1_PULSES: case PROTOCOL_CHANNELS_PXX1_PULSES:
intmodulePxx1PulsesStart(); intmodulePxx1PulsesStart();
#if defined(INTMODULE_HEARTBEAT)
// use backup trigger (1 ms later)
mixerSchedulerSetPeriod(INTERNAL_MODULE, INTMODULE_PXX1_SERIAL_PERIOD + 1000/*us*/);
#else
mixerSchedulerSetPeriod(INTERNAL_MODULE, INTMODULE_PXX1_SERIAL_PERIOD);
#endif
break; break;
#endif #endif
#if defined(PXX1) && defined(INTMODULE_USART) #if defined(PXX1) && defined(INTMODULE_USART)
case PROTOCOL_CHANNELS_PXX1_SERIAL: case PROTOCOL_CHANNELS_PXX1_SERIAL:
intmodulePxx1SerialStart(); intmodulePxx1SerialStart();
#if defined(INTMODULE_HEARTBEAT)
// use backup trigger (1 ms later)
mixerSchedulerSetPeriod(INTERNAL_MODULE, INTMODULE_PXX1_SERIAL_PERIOD + 1000/*us*/);
#else
mixerSchedulerSetPeriod(INTERNAL_MODULE, INTMODULE_PXX1_SERIAL_PERIOD);
#endif
break; break;
#endif #endif
@ -399,6 +434,13 @@ static void enablePulsesInternalModule(uint8_t protocol)
case PROTOCOL_CHANNELS_PXX2_HIGHSPEED: case PROTOCOL_CHANNELS_PXX2_HIGHSPEED:
intmoduleSerialStart(PXX2_HIGHSPEED_BAUDRATE, true, USART_Parity_No, USART_StopBits_1, USART_WordLength_8b); intmoduleSerialStart(PXX2_HIGHSPEED_BAUDRATE, true, USART_Parity_No, USART_StopBits_1, USART_WordLength_8b);
resetAccessAuthenticationCount(); resetAccessAuthenticationCount();
#if defined(INTMODULE_HEARTBEAT)
// use backup trigger (1 ms later)
mixerSchedulerSetPeriod(INTERNAL_MODULE, PXX2_PERIOD + 1000/*us*/);
#else
mixerSchedulerSetPeriod(INTERNAL_MODULE, PXX2_PERIOD);
#endif
break; break;
#endif #endif
@ -406,17 +448,20 @@ static void enablePulsesInternalModule(uint8_t protocol)
case PROTOCOL_CHANNELS_MULTIMODULE: case PROTOCOL_CHANNELS_MULTIMODULE:
intmodulePulsesData.multi.initFrame(); intmodulePulsesData.multi.initFrame();
intmoduleSerialStart(MULTIMODULE_BAUDRATE, true, USART_Parity_Even, USART_StopBits_2, USART_WordLength_9b); intmoduleSerialStart(MULTIMODULE_BAUDRATE, true, USART_Parity_Even, USART_StopBits_2, USART_WordLength_9b);
intmoduleTimerStart(MULTIMODULE_PERIOD); mixerSchedulerSetPeriod(INTERNAL_MODULE, MULTIMODULE_PERIOD);
break; break;
#endif #endif
#if defined(INTERNAL_MODULE_PPM) #if defined(INTERNAL_MODULE_PPM)
case PROTOCOL_CHANNELS_PPM: case PROTOCOL_CHANNELS_PPM:
intmodulePpmStart(); intmodulePpmStart();
mixerSchedulerSetPeriod(INTERNAL_MODULE, PPM_PERIOD(INTERNAL_MODULE));
break; break;
#endif #endif
default: default:
// internal module stopped, set internal period to 0 and start the scheduler
mixerSchedulerSetPeriod(INTERNAL_MODULE, 0);
break; break;
} }
} }
@ -427,18 +472,18 @@ bool setupPulsesInternalModule(uint8_t protocol)
#if defined(HARDWARE_INTERNAL_MODULE) && defined(PXX1) && !defined(INTMODULE_USART) #if defined(HARDWARE_INTERNAL_MODULE) && defined(PXX1) && !defined(INTMODULE_USART)
case PROTOCOL_CHANNELS_PXX1_PULSES: case PROTOCOL_CHANNELS_PXX1_PULSES:
intmodulePulsesData.pxx.setupFrame(INTERNAL_MODULE); intmodulePulsesData.pxx.setupFrame(INTERNAL_MODULE);
scheduleNextMixerCalculation(INTERNAL_MODULE, INTMODULE_PXX1_SERIAL_PERIOD); #if defined(INTMODULE_HEARTBEAT)
mixerSchedulerResetTimer();
mixerSchedulerSetPeriod(INTERNAL_MODULE, INTMODULE_PXX1_SERIAL_PERIOD + 1000 /* backup */);
#else
mixerSchedulerSetPeriod(INTERNAL_MODULE, INTMODULE_PXX1_SERIAL_PERIOD);
#endif
return true; return true;
#endif #endif
#if defined(PXX1) && defined(INTMODULE_USART) #if defined(PXX1) && defined(INTMODULE_USART)
case PROTOCOL_CHANNELS_PXX1_SERIAL: case PROTOCOL_CHANNELS_PXX1_SERIAL:
intmodulePulsesData.pxx_uart.setupFrame(INTERNAL_MODULE); intmodulePulsesData.pxx_uart.setupFrame(INTERNAL_MODULE);
#if defined(INTMODULE_HEARTBEAT)
scheduleNextMixerCalculation(INTERNAL_MODULE, INTMODULE_PXX1_SERIAL_PERIOD + 1 /* backup */);
#else
scheduleNextMixerCalculation(INTERNAL_MODULE, INTMODULE_PXX1_SERIAL_PERIOD);
#endif
return true; return true;
#endif #endif
@ -447,15 +492,16 @@ bool setupPulsesInternalModule(uint8_t protocol)
{ {
bool result = intmodulePulsesData.pxx2.setupFrame(INTERNAL_MODULE); bool result = intmodulePulsesData.pxx2.setupFrame(INTERNAL_MODULE);
if (moduleState[INTERNAL_MODULE].mode == MODULE_MODE_SPECTRUM_ANALYSER || moduleState[INTERNAL_MODULE].mode == MODULE_MODE_POWER_METER) { if (moduleState[INTERNAL_MODULE].mode == MODULE_MODE_SPECTRUM_ANALYSER || moduleState[INTERNAL_MODULE].mode == MODULE_MODE_POWER_METER) {
scheduleNextMixerCalculation(INTERNAL_MODULE, PXX2_TOOLS_PERIOD); mixerSchedulerSetPeriod(INTERNAL_MODULE, PXX2_TOOLS_PERIOD);
} }
else { else {
#if defined(INTMODULE_HEARTBEAT) #if defined(INTMODULE_HEARTBEAT)
scheduleNextMixerCalculation(INTERNAL_MODULE, PXX2_PERIOD + 1 /* backup */); mixerSchedulerResetTimer();
mixerSchedulerSetPeriod(INTERNAL_MODULE, PXX2_PERIOD + 1000 /* backup */);
#else #else
scheduleNextMixerCalculation(INTERNAL_MODULE, PXX2_PERIOD); mixerSchedulerSetPeriod(INTERNAL_MODULE, PXX2_PERIOD);
#endif #endif
} }
return result; return result;
} }
#endif #endif
@ -463,19 +509,20 @@ bool setupPulsesInternalModule(uint8_t protocol)
#if defined(PCBTARANIS) && defined(INTERNAL_MODULE_PPM) #if defined(PCBTARANIS) && defined(INTERNAL_MODULE_PPM)
case PROTOCOL_CHANNELS_PPM: case PROTOCOL_CHANNELS_PPM:
setupPulsesPPMInternalModule(); setupPulsesPPMInternalModule();
scheduleNextMixerCalculation(INTERNAL_MODULE, PPM_PERIOD(INTERNAL_MODULE)); // probably useless, as the interval did not change since "enable" function
mixerSchedulerSetPeriod(INTERNAL_MODULE, PPM_PERIOD(INTERNAL_MODULE));
return true; return true;
#endif #endif
#if defined(INTERNAL_MODULE_MULTI) #if defined(INTERNAL_MODULE_MULTI)
case PROTOCOL_CHANNELS_MULTIMODULE: case PROTOCOL_CHANNELS_MULTIMODULE:
setupPulsesMultiInternalModule(); setupPulsesMultiInternalModule();
scheduleNextMixerCalculation(INTERNAL_MODULE, MULTIMODULE_PERIOD); mixerSchedulerSetPeriod(INTERNAL_MODULE, MULTIMODULE_PERIOD);
return true; return true;
#endif #endif
default: default:
scheduleNextMixerCalculation(INTERNAL_MODULE, 10 /*ms*/); // used for USB sim for example mixerSchedulerSetPeriod(INTERNAL_MODULE, 10000 /*us*/); // used for USB sim for example
return false; return false;
} }
} }

View file

@ -216,21 +216,20 @@ typedef Dsm2SerialPulsesData Dsm2PulsesData;
PACK(struct Dsm2TimerPulsesData { PACK(struct Dsm2TimerPulsesData {
pulse_duration_t pulses[MAX_PULSES_TRANSITIONS]; pulse_duration_t pulses[MAX_PULSES_TRANSITIONS];
pulse_duration_t * ptr; pulse_duration_t * ptr;
uint16_t rest;
uint8_t index; uint8_t index;
}); });
typedef Dsm2TimerPulsesData Dsm2PulsesData; typedef Dsm2TimerPulsesData Dsm2PulsesData;
#endif #endif
#define PPM_PERIOD_HALF_US(module) ((g_model.moduleData[module].ppm.frameLength * 5 + 225) * 200) /*half us*/ #define PPM_PERIOD_HALF_US(module) ((g_model.moduleData[module].ppm.frameLength * 5 + 225) * 200) /*half us*/
#define PPM_PERIOD(module) (PPM_PERIOD_HALF_US(module) / 2000) /*ms*/ #define PPM_PERIOD(module) (PPM_PERIOD_HALF_US(module) / 2) /*us*/
#define DSM2_BAUDRATE 125000 #define DSM2_BAUDRATE 125000
#define DSM2_PERIOD 22 /*ms*/ #define DSM2_PERIOD 22000 /*us*/
#define SBUS_BAUDRATE 100000 #define SBUS_BAUDRATE 100000
#define SBUS_PERIOD_HALF_US ((g_model.moduleData[EXTERNAL_MODULE].sbus.refreshRate * 5 + 225) * 200) /*half us*/ #define SBUS_PERIOD_HALF_US ((g_model.moduleData[EXTERNAL_MODULE].sbus.refreshRate * 5 + 225) * 200) /*half us*/
#define SBUS_PERIOD (SBUS_PERIOD_HALF_US / 2000) /*ms*/ #define SBUS_PERIOD (SBUS_PERIOD_HALF_US / 2) /*us*/
#define MULTIMODULE_BAUDRATE 100000 #define MULTIMODULE_BAUDRATE 100000
#define MULTIMODULE_PERIOD 7 /*ms*/ #define MULTIMODULE_PERIOD 7000 /*us*/
#define CROSSFIRE_FRAME_MAXLEN 64 #define CROSSFIRE_FRAME_MAXLEN 64
PACK(struct CrossfirePulsesData { PACK(struct CrossfirePulsesData {

View file

@ -29,20 +29,20 @@
#define PXX2_LOWSPEED_BAUDRATE 230400 #define PXX2_LOWSPEED_BAUDRATE 230400
#define PXX2_HIGHSPEED_BAUDRATE 450000 #define PXX2_HIGHSPEED_BAUDRATE 450000
#define PXX2_PERIOD 4 // 4ms #define PXX2_PERIOD 4000/*us*/
#define PXX2_TOOLS_PERIOD 1 // 1ms #define PXX2_TOOLS_PERIOD 1000/*us*/
#define PXX2_FRAME_MAXLENGTH 64 #define PXX2_FRAME_MAXLENGTH 64
#define PXX_PULSES_PERIOD 9/*ms*/ #define PXX_PULSES_PERIOD 9000/*us*/
#define EXTMODULE_PXX1_SERIAL_PERIOD 4/*ms*/ #define EXTMODULE_PXX1_SERIAL_PERIOD 4000/*us*/
#define EXTMODULE_PXX1_SERIAL_BAUDRATE 420000 #define EXTMODULE_PXX1_SERIAL_BAUDRATE 420000
#if defined(PXX_FREQUENCY_HIGH) #if defined(PXX_FREQUENCY_HIGH)
#define INTMODULE_PXX1_SERIAL_BAUDRATE 450000 #define INTMODULE_PXX1_SERIAL_BAUDRATE 450000
#define INTMODULE_PXX1_SERIAL_PERIOD 4/*ms*/ #define INTMODULE_PXX1_SERIAL_PERIOD 4000/*us*/
#else #else
#define INTMODULE_PXX1_SERIAL_BAUDRATE 115200 #define INTMODULE_PXX1_SERIAL_BAUDRATE 115200
#define INTMODULE_PXX1_SERIAL_PERIOD 9/*ms*/ #define INTMODULE_PXX1_SERIAL_PERIOD 9000/*us*/
#endif #endif
// Used by the Sky9x family boards // Used by the Sky9x family boards

View file

@ -55,7 +55,6 @@ static void _send_level(uint8_t v)
*extmodulePulsesData.dsm2.ptr++ = v - 1; *extmodulePulsesData.dsm2.ptr++ = v - 1;
extmodulePulsesData.dsm2.index+=1; extmodulePulsesData.dsm2.index+=1;
extmodulePulsesData.dsm2.rest -=v;
} }
void sendByteSbus(uint8_t b) // max 11 changes 0 10 10 10 10 P 1 void sendByteSbus(uint8_t b) // max 11 changes 0 10 10 10 10 P 1
@ -113,7 +112,6 @@ void setupPulsesSbus()
extmodulePulsesData.dsm2.serialByte = 0; extmodulePulsesData.dsm2.serialByte = 0;
extmodulePulsesData.dsm2.serialBitCount = 0; extmodulePulsesData.dsm2.serialBitCount = 0;
#else #else
extmodulePulsesData.dsm2.rest = SBUS_PERIOD_HALF_US;
extmodulePulsesData.dsm2.index = 0; extmodulePulsesData.dsm2.index = 0;
#endif #endif

View file

@ -36,7 +36,9 @@ extern "C++" {
typedef pthread_t RTOS_TASK_HANDLE; typedef pthread_t RTOS_TASK_HANDLE;
typedef pthread_mutex_t RTOS_MUTEX_HANDLE; typedef pthread_mutex_t RTOS_MUTEX_HANDLE;
typedef uint32_t RTOS_FLAG_HANDLE; typedef uint32_t RTOS_FLAG_HANDLE;
typedef sem_t * RTOS_EVENT_HANDLE; typedef sem_t * RTOS_EVENT_HANDLE;
extern uint64_t simuTimerMicros(void); extern uint64_t simuTimerMicros(void);
@ -76,16 +78,26 @@ extern "C++" {
pthread_mutex_unlock(&mutex); pthread_mutex_unlock(&mutex);
} }
static inline void RTOS_CREATE_FLAG(uint32_t &flag) static inline void RTOS_CREATE_FLAG(RTOS_FLAG_HANDLE flag)
{ {
flag = 0; // TODO: real flags (use semaphores?)
} }
static inline void RTOS_SET_FLAG(uint32_t &flag) static inline void RTOS_SET_FLAG(RTOS_FLAG_HANDLE flag)
{ {
flag = 1;
} }
static inline void RTOS_CLEAR_FLAG(RTOS_FLAG_HANDLE flag)
{
}
static inline bool RTOS_WAIT_FLAG(RTOS_FLAG_HANDLE flag, uint32_t timeout)
{
simuSleep(timeout);
return false;
}
#define RTOS_ISR_SET_FLAG RTOS_SET_FLAG
template<int SIZE> template<int SIZE>
class FakeTaskStack class FakeTaskStack
{ {
@ -146,6 +158,7 @@ template<int SIZE>
{ {
return (uint32_t)(simuTimerMicros() / 1000); return (uint32_t)(simuTimerMicros() / 1000);
} }
#elif defined(RTOS_COOS) #elif defined(RTOS_COOS)
#ifdef __cplusplus #ifdef __cplusplus
extern "C" { extern "C" {
@ -230,7 +243,18 @@ template<int SIZE>
#define RTOS_CREATE_FLAG(flag) flag = CoCreateFlag(false, false) #define RTOS_CREATE_FLAG(flag) flag = CoCreateFlag(false, false)
#define RTOS_SET_FLAG(flag) (void)CoSetFlag(flag) #define RTOS_SET_FLAG(flag) (void)CoSetFlag(flag)
#define RTOS_CLEAR_FLAG(flag) (void)CoClearFlag(flag)
#define RTOS_WAIT_FLAG(flag,timeout) (CoWaitForSingleFlag(flag,timeout) == E_TIMEOUT)
static inline void RTOS_ISR_SET_FLAG(RTOS_FLAG_HANDLE flag)
{
CoEnterISR();
CoSchedLock();
isr_SetFlag(flag);
CoSchedUnlock();
CoExitISR();
}
#ifdef __cplusplus #ifdef __cplusplus
template<int SIZE> template<int SIZE>
class TaskStack class TaskStack

View file

@ -19,6 +19,7 @@
*/ */
#include "opentx.h" #include "opentx.h"
#include "mixer_scheduler.h"
#if defined(INTMODULE_HEARTBEAT_GPIO) #if defined(INTMODULE_HEARTBEAT_GPIO)
volatile HeartbeatCapture heartbeatCapture; volatile HeartbeatCapture heartbeatCapture;
@ -77,6 +78,8 @@ void check_intmodule_heartbeat()
heartbeatCapture.count++; heartbeatCapture.count++;
#endif #endif
EXTI_ClearITPendingBit(INTMODULE_HEARTBEAT_EXTI_LINE); EXTI_ClearITPendingBit(INTMODULE_HEARTBEAT_EXTI_LINE);
mixerSchedulerISRTrigger();
} }
} }
#endif #endif

View file

@ -45,12 +45,6 @@ void intmoduleStop()
USART_DeInit(INTMODULE_USART); USART_DeInit(INTMODULE_USART);
GPIO_ResetBits(INTMODULE_GPIO, INTMODULE_TX_GPIO_PIN | INTMODULE_RX_GPIO_PIN); GPIO_ResetBits(INTMODULE_GPIO, INTMODULE_TX_GPIO_PIN | INTMODULE_RX_GPIO_PIN);
#if defined(INTERNAL_MODULE_MULTI)
// stop pulses timer
INTMODULE_TIMER->DIER &= ~TIM_DIER_CC2IE;
INTMODULE_TIMER->CR1 &= ~TIM_CR1_CEN;
#endif
} }
void intmodulePxx1SerialStart() void intmodulePxx1SerialStart()
@ -103,28 +97,6 @@ void intmoduleSerialStart(uint32_t baudrate, uint8_t rxEnable, uint16_t parity,
} }
} }
#if defined(INTERNAL_MODULE_MULTI)
void intmoduleTimerStart(uint32_t periodMs)
{
// Timer
INTMODULE_TIMER->CR1 &= ~TIM_CR1_CEN;
INTMODULE_TIMER->PSC = INTMODULE_TIMER_FREQ / 2000000 - 1; // 0.5uS (2Mhz)
INTMODULE_TIMER->ARR = periodMs * 2000;
INTMODULE_TIMER->CCR2 = (periodMs - 1) * 2000;
INTMODULE_TIMER->CCER = TIM_CCER_CC3E;
INTMODULE_TIMER->CCMR2 = 0;
INTMODULE_TIMER->EGR = 1; // Restart
INTMODULE_TIMER->CCMR2 = TIM_CCMR2_OC3M_1 | TIM_CCMR2_OC3M_0; // Toggle CC1 o/p
INTMODULE_TIMER->SR &= ~TIM_SR_CC2IF; // Clear flag
INTMODULE_TIMER->DIER |= TIM_DIER_CC2IE; // Enable this interrupt
INTMODULE_TIMER->CR1 |= TIM_CR1_CEN;
NVIC_EnableIRQ(INTMODULE_TIMER_IRQn);
NVIC_SetPriority(INTMODULE_TIMER_IRQn, 7);
}
#endif
#define USART_FLAG_ERRORS (USART_FLAG_ORE | USART_FLAG_NE | USART_FLAG_FE | USART_FLAG_PE) #define USART_FLAG_ERRORS (USART_FLAG_ORE | USART_FLAG_NE | USART_FLAG_FE | USART_FLAG_PE)
extern "C" void INTMODULE_USART_IRQHandler(void) extern "C" void INTMODULE_USART_IRQHandler(void)
{ {
@ -217,12 +189,3 @@ void intmoduleSendNextFrame()
#endif #endif
} }
} }
#if defined(INTERNAL_MODULE_MULTI)
extern "C" void INTMODULE_TIMER_IRQHandler()
{
INTMODULE_TIMER->SR &= ~TIM_SR_CC2IF; // clear flag
setupPulsesInternalModule();
intmoduleSendNextFrame();
}
#endif

View file

@ -0,0 +1,81 @@
/*
* Copyright (C) OpenTX
*
* Based on code named
* th9x - http://code.google.com/p/th9x
* er9x - http://code.google.com/p/er9x
* gruvin9x - http://code.google.com/p/gruvin9x
*
* License GPLv2: http://www.gnu.org/licenses/gpl-2.0.html
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*/
#include "opentx.h"
#include "mixer_scheduler.h"
#if !defined(SIMU)
// Start scheduler with default period
void mixerSchedulerStart()
{
MIXER_SCHEDULER_TIMER->CR1 &= ~TIM_CR1_CEN;
MIXER_SCHEDULER_TIMER->CR1 = TIM_CR1_URS; // do not generate interrupt on soft update
MIXER_SCHEDULER_TIMER->PSC = MIXER_SCHEDULER_TIMER_FREQ / 2000000 - 1; // 0.5uS (2Mhz)
MIXER_SCHEDULER_TIMER->CCER = 0;
MIXER_SCHEDULER_TIMER->CCMR1 = 0;
MIXER_SCHEDULER_TIMER->ARR = 2 * getMixerSchedulerPeriod() - 1;
MIXER_SCHEDULER_TIMER->EGR = TIM_EGR_UG; // reset timer
NVIC_EnableIRQ(MIXER_SCHEDULER_TIMER_IRQn);
NVIC_SetPriority(MIXER_SCHEDULER_TIMER_IRQn, 8);
MIXER_SCHEDULER_TIMER->SR &= TIM_SR_UIF; // clear interrupt flag
MIXER_SCHEDULER_TIMER->DIER |= TIM_DIER_UIE; // enable interrupt
MIXER_SCHEDULER_TIMER->CR1 |= TIM_CR1_CEN;
}
void mixerSchedulerStop()
{
MIXER_SCHEDULER_TIMER->CR1 &= ~TIM_CR1_CEN;
NVIC_DisableIRQ(MIXER_SCHEDULER_TIMER_IRQn);
}
void mixerSchedulerResetTimer()
{
mixerSchedulerDisableTrigger();
MIXER_SCHEDULER_TIMER->CNT = 0;
mixerSchedulerEnableTrigger();
}
void mixerSchedulerEnableTrigger()
{
MIXER_SCHEDULER_TIMER->DIER |= TIM_DIER_UIE; // enable interrupt
}
void mixerSchedulerDisableTrigger()
{
MIXER_SCHEDULER_TIMER->DIER &= ~TIM_DIER_UIE; // disable interrupt
}
extern "C" void MIXER_SCHEDULER_TIMER_IRQHandler(void)
{
MIXER_SCHEDULER_TIMER->SR &= ~TIM_SR_UIF; // clear flag
mixerSchedulerDisableTrigger();
// set next period
MIXER_SCHEDULER_TIMER->ARR = 2 * getMixerSchedulerPeriod() - 1;
// trigger mixer start
mixerSchedulerISRTrigger();
}
#endif

View file

@ -240,6 +240,7 @@ set(TARGET_SRC
led_driver.cpp led_driver.cpp
extmodule_driver.cpp extmodule_driver.cpp
trainer_driver.cpp trainer_driver.cpp
../common/arm/stm32/mixer_scheduler_driver.cpp
../common/arm/stm32/heartbeat_driver.cpp ../common/arm/stm32/heartbeat_driver.cpp
../common/arm/stm32/timers_driver.cpp ../common/arm/stm32/timers_driver.cpp
../common/arm/stm32/intmodule_serial_driver.cpp ../common/arm/stm32/intmodule_serial_driver.cpp

View file

@ -137,6 +137,7 @@ void boardInit()
INTMODULE_RCC_APB1Periph | INTMODULE_RCC_APB1Periph |
EXTMODULE_RCC_APB1Periph | EXTMODULE_RCC_APB1Periph |
I2C_RCC_APB1Periph | I2C_RCC_APB1Periph |
MIXER_SCHEDULER_TIMER_RCC_APB1Periph |
GPS_RCC_APB1Periph | GPS_RCC_APB1Periph |
BACKLIGHT_RCC_APB1Periph, BACKLIGHT_RCC_APB1Periph,
ENABLE); ENABLE);

View file

@ -180,14 +180,11 @@ void init_intmodule_heartbeat();
void check_intmodule_heartbeat(); void check_intmodule_heartbeat();
void intmoduleSerialStart(uint32_t baudrate, uint8_t rxEnable, uint16_t parity, uint16_t stopBits, uint16_t wordLength); void intmoduleSerialStart(uint32_t baudrate, uint8_t rxEnable, uint16_t parity, uint16_t stopBits, uint16_t wordLength);
#if defined(INTERNAL_MODULE_MULTI)
void intmoduleTimerStart(uint32_t periodMs);
#endif
void intmoduleSendByte(uint8_t byte); void intmoduleSendByte(uint8_t byte);
void intmoduleSendBuffer(const uint8_t * data, uint8_t size); void intmoduleSendBuffer(const uint8_t * data, uint8_t size);
void intmoduleSendNextFrame(); void intmoduleSendNextFrame();
void extmoduleSerialStart(uint32_t baudrate, uint32_t period_half_us, bool inverted); void extmoduleSerialStart();
void extmoduleInvertedSerialStart(uint32_t baudrate); void extmoduleInvertedSerialStart(uint32_t baudrate);
void extmoduleSendBuffer(const uint8_t * data, uint8_t size); void extmoduleSendBuffer(const uint8_t * data, uint8_t size);
void extmoduleSendNextFrame(); void extmoduleSendNextFrame();

View file

@ -143,7 +143,7 @@ void extmodulePxx1PulsesStart()
} }
#endif #endif
void extmoduleSerialStart(uint32_t /*baudrate*/, uint32_t period_half_us, bool inverted) void extmoduleSerialStart()
{ {
EXTERNAL_MODULE_ON(); EXTERNAL_MODULE_ON();
@ -176,10 +176,9 @@ void extmoduleSerialStart(uint32_t /*baudrate*/, uint32_t period_half_us, bool i
EXTMODULE_TIMER->CCMR1 = TIM_CCMR1_OC1M_1 | TIM_CCMR1_OC1M_0; EXTMODULE_TIMER->CCMR1 = TIM_CCMR1_OC1M_1 | TIM_CCMR1_OC1M_0;
#endif #endif
EXTMODULE_TIMER->ARR = period_half_us; EXTMODULE_TIMER->ARR = 40000; // dummy value until the DMA request kicks in
EXTMODULE_TIMER->CCR2 = period_half_us - 4000;
EXTMODULE_TIMER->SR &= ~TIM_SR_CC2IF; // Clear flag EXTMODULE_TIMER->SR &= ~TIM_SR_CC2IF; // Clear flag
EXTMODULE_TIMER->DIER |= TIM_DIER_UDE | TIM_DIER_CC2IE; EXTMODULE_TIMER->DIER |= TIM_DIER_UDE;
EXTMODULE_TIMER->CR1 |= TIM_CR1_CEN; EXTMODULE_TIMER->CR1 |= TIM_CR1_CEN;
NVIC_EnableIRQ(EXTMODULE_TIMER_DMA_STREAM_IRQn); NVIC_EnableIRQ(EXTMODULE_TIMER_DMA_STREAM_IRQn);
@ -342,23 +341,36 @@ void extmoduleSendNextFrame()
#if defined(DSM2) #if defined(DSM2)
case PROTOCOL_CHANNELS_SBUS: case PROTOCOL_CHANNELS_SBUS:
#if defined(PCBX10) || PCBREV >= 13
EXTMODULE_TIMER->CCER = TIM_CCER_CC3E | (GET_SBUS_POLARITY(EXTERNAL_MODULE) ? TIM_CCER_CC3P : 0); // reverse polarity for Sbus if needed
#else
EXTMODULE_TIMER->CCER = TIM_CCER_CC1E | (GET_SBUS_POLARITY(EXTERNAL_MODULE) ? TIM_CCER_CC1P : 0); // reverse polarity for Sbus if needed
#endif
// no break
case PROTOCOL_CHANNELS_DSM2_LP45: case PROTOCOL_CHANNELS_DSM2_LP45:
case PROTOCOL_CHANNELS_DSM2_DSM2: case PROTOCOL_CHANNELS_DSM2_DSM2:
case PROTOCOL_CHANNELS_DSM2_DSMX: case PROTOCOL_CHANNELS_DSM2_DSMX:
case PROTOCOL_CHANNELS_MULTIMODULE: case PROTOCOL_CHANNELS_MULTIMODULE:
EXTMODULE_TIMER->CCR2 = *(extmodulePulsesData.dsm2.ptr - 1) - 4000; // 2mS in advance
if (EXTMODULE_TIMER_DMA_STREAM->CR & DMA_SxCR_EN)
return;
if (PROTOCOL_CHANNELS_SBUS == moduleState[EXTERNAL_MODULE].protocol) {
#if defined(PCBX10) || PCBREV >= 13
EXTMODULE_TIMER->CCER = TIM_CCER_CC3E | (GET_SBUS_POLARITY(EXTERNAL_MODULE) ? TIM_CCER_CC3P : 0); // reverse polarity for Sbus if needed
#else
EXTMODULE_TIMER->CCER = TIM_CCER_CC1E | (GET_SBUS_POLARITY(EXTERNAL_MODULE) ? TIM_CCER_CC1P : 0); // reverse polarity for Sbus if needed
#endif
}
// disable timer
EXTMODULE_TIMER->CR1 &= ~TIM_CR1_CEN;
// send DMA request
EXTMODULE_TIMER_DMA_STREAM->CR &= ~DMA_SxCR_EN; // Disable DMA EXTMODULE_TIMER_DMA_STREAM->CR &= ~DMA_SxCR_EN; // Disable DMA
EXTMODULE_TIMER_DMA_STREAM->CR |= EXTMODULE_TIMER_DMA_CHANNEL | DMA_SxCR_DIR_0 | DMA_SxCR_MINC | EXTMODULE_TIMER_DMA_SIZE | DMA_SxCR_PL_0 | DMA_SxCR_PL_1; EXTMODULE_TIMER_DMA_STREAM->CR |= EXTMODULE_TIMER_DMA_CHANNEL | DMA_SxCR_DIR_0 | DMA_SxCR_MINC | EXTMODULE_TIMER_DMA_SIZE | DMA_SxCR_PL_0 | DMA_SxCR_PL_1;
EXTMODULE_TIMER_DMA_STREAM->PAR = CONVERT_PTR_UINT(&EXTMODULE_TIMER->ARR); EXTMODULE_TIMER_DMA_STREAM->PAR = CONVERT_PTR_UINT(&EXTMODULE_TIMER->ARR);
EXTMODULE_TIMER_DMA_STREAM->M0AR = CONVERT_PTR_UINT(extmodulePulsesData.dsm2.pulses); EXTMODULE_TIMER_DMA_STREAM->M0AR = CONVERT_PTR_UINT(extmodulePulsesData.dsm2.pulses);
EXTMODULE_TIMER_DMA_STREAM->NDTR = extmodulePulsesData.dsm2.ptr - extmodulePulsesData.dsm2.pulses; EXTMODULE_TIMER_DMA_STREAM->NDTR = extmodulePulsesData.dsm2.ptr - extmodulePulsesData.dsm2.pulses;
EXTMODULE_TIMER_DMA_STREAM->CR |= DMA_SxCR_EN | DMA_SxCR_TCIE; // Enable DMA EXTMODULE_TIMER_DMA_STREAM->CR |= DMA_SxCR_EN | DMA_SxCR_TCIE; // Enable DMA
// re-init timer
EXTMODULE_TIMER->EGR = 1;
EXTMODULE_TIMER->CR1 |= TIM_CR1_CEN;
break; break;
#endif #endif
@ -419,14 +431,20 @@ extern "C" void EXTMODULE_TIMER_DMA_IRQHandler()
DMA_ClearITPendingBit(EXTMODULE_TIMER_DMA_STREAM, EXTMODULE_TIMER_DMA_FLAG_TC); DMA_ClearITPendingBit(EXTMODULE_TIMER_DMA_STREAM, EXTMODULE_TIMER_DMA_FLAG_TC);
EXTMODULE_TIMER->SR &= ~TIM_SR_CC2IF; // Clear flag switch (moduleState[EXTERNAL_MODULE].protocol) {
EXTMODULE_TIMER->DIER |= TIM_DIER_CC2IE; // Enable this interrupt case PROTOCOL_CHANNELS_PXX1_PULSES:
case PROTOCOL_CHANNELS_PPM:
EXTMODULE_TIMER->SR &= ~TIM_SR_CC2IF; // Clear flag
EXTMODULE_TIMER->DIER |= TIM_DIER_CC2IE; // Enable this interrupt
break;
}
} }
extern "C" void EXTMODULE_TIMER_IRQHandler() extern "C" void EXTMODULE_TIMER_IRQHandler()
{ {
EXTMODULE_TIMER->DIER &= ~TIM_DIER_CC2IE; // Stop this interrupt EXTMODULE_TIMER->DIER &= ~TIM_DIER_CC2IE; // Stop this interrupt
EXTMODULE_TIMER->SR &= ~TIM_SR_CC2IF; EXTMODULE_TIMER->SR &= ~TIM_SR_CC2IF;
if (setupPulsesExternalModule()) if (setupPulsesExternalModule())
extmoduleSendNextFrame(); extmoduleSendNextFrame();
} }

View file

@ -902,6 +902,13 @@
#define TIMER_2MHz_RCC_APB1Periph RCC_APB1Periph_TIM7 #define TIMER_2MHz_RCC_APB1Periph RCC_APB1Periph_TIM7
#define TIMER_2MHz_TIMER TIM7 #define TIMER_2MHz_TIMER TIM7
// Mixer scheduler timer
#define MIXER_SCHEDULER_TIMER_RCC_APB1Periph RCC_APB1Periph_TIM13
#define MIXER_SCHEDULER_TIMER TIM13
#define MIXER_SCHEDULER_TIMER_FREQ (PERI1_FREQUENCY * TIMER_MULT_APB1)
#define MIXER_SCHEDULER_TIMER_IRQn TIM8_UP_TIM13_IRQn
#define MIXER_SCHEDULER_TIMER_IRQHandler TIM8_UP_TIM13_IRQHandler
// Bluetooth // Bluetooth
#define STORAGE_BLUETOOTH #define STORAGE_BLUETOOTH
#if defined(BLUETOOTH) #if defined(BLUETOOTH)

View file

@ -401,6 +401,7 @@ set(TARGET_SRC
../common/arm/stm32/audio_dac_driver.cpp ../common/arm/stm32/audio_dac_driver.cpp
../common/arm/stm32/adc_driver.cpp ../common/arm/stm32/adc_driver.cpp
../common/arm/stm32/heartbeat_driver.cpp ../common/arm/stm32/heartbeat_driver.cpp
../common/arm/stm32/mixer_scheduler_driver.cpp
) )
if(PCB STREQUAL XLITE OR PCB STREQUAL XLITES) if(PCB STREQUAL XLITE OR PCB STREQUAL XLITES)

View file

@ -110,6 +110,7 @@ void boardInit()
AUX_SERIAL_RCC_APB1Periph | AUX_SERIAL_RCC_APB1Periph |
INTMODULE_RCC_APB1Periph | INTMODULE_RCC_APB1Periph |
TRAINER_MODULE_RCC_APB1Periph | TRAINER_MODULE_RCC_APB1Periph |
MIXER_SCHEDULER_TIMER_RCC_APB1Periph |
BT_RCC_APB1Periph | BT_RCC_APB1Periph |
GYRO_RCC_APB1Periph, GYRO_RCC_APB1Periph,
ENABLE); ENABLE);
@ -339,4 +340,4 @@ void initJackDetect(void)
GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_UP; GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_UP;
GPIO_Init(JACK_DETECT_GPIO, &GPIO_InitStructure); GPIO_Init(JACK_DETECT_GPIO, &GPIO_InitStructure);
} }
#endif #endif

View file

@ -143,7 +143,7 @@ void intmoduleSendByte(uint8_t byte);
void intmoduleSendBuffer(const uint8_t * data, uint8_t size); void intmoduleSendBuffer(const uint8_t * data, uint8_t size);
void intmoduleSendNextFrame(); void intmoduleSendNextFrame();
void extmoduleSerialStart(uint32_t baudrate, uint32_t period_half_us, bool inverted); void extmoduleSerialStart();
void extmoduleInvertedSerialStart(uint32_t baudrate); void extmoduleInvertedSerialStart(uint32_t baudrate);
void extmoduleSendBuffer(const uint8_t * data, uint8_t size); void extmoduleSendBuffer(const uint8_t * data, uint8_t size);
void extmoduleSendNextFrame(); void extmoduleSendNextFrame();

View file

@ -83,7 +83,7 @@ void extmodulePpmStart()
NVIC_SetPriority(EXTMODULE_TIMER_CC_IRQn, 7); NVIC_SetPriority(EXTMODULE_TIMER_CC_IRQn, 7);
} }
void extmoduleSerialStart(uint32_t /*baudrate*/, uint32_t period_half_us, bool inverted) void extmoduleSerialStart()
{ {
EXTERNAL_MODULE_ON(); EXTERNAL_MODULE_ON();
@ -99,16 +99,17 @@ void extmoduleSerialStart(uint32_t /*baudrate*/, uint32_t period_half_us, bool i
EXTMODULE_TIMER->CR1 &= ~TIM_CR1_CEN; EXTMODULE_TIMER->CR1 &= ~TIM_CR1_CEN;
EXTMODULE_TIMER->PSC = EXTMODULE_TIMER_FREQ / 2000000 - 1; // 0.5uS from 30MHz EXTMODULE_TIMER->PSC = EXTMODULE_TIMER_FREQ / 2000000 - 1; // 0.5uS from 30MHz
EXTMODULE_TIMER->CCER = EXTMODULE_TIMER_OUTPUT_ENABLE | (inverted ? 0 : EXTMODULE_TIMER_OUTPUT_POLARITY);
EXTMODULE_TIMER->CCR3 = 0;
EXTMODULE_TIMER->CCER = EXTMODULE_TIMER_OUTPUT_ENABLE | EXTMODULE_TIMER_OUTPUT_POLARITY;
EXTMODULE_TIMER->BDTR = TIM_BDTR_MOE; // Enable outputs EXTMODULE_TIMER->BDTR = TIM_BDTR_MOE; // Enable outputs
EXTMODULE_TIMER->CCR1 = 0; EXTMODULE_TIMER->CCR1 = 0;
EXTMODULE_TIMER->CCMR1 = TIM_CCMR1_OC1M_2 | TIM_CCMR1_OC1M_0; // Force O/P high EXTMODULE_TIMER->CCMR1 = TIM_CCMR1_OC1M_2 | TIM_CCMR1_OC1M_0; // Force O/P high
EXTMODULE_TIMER->EGR = 1; // Restart EXTMODULE_TIMER->EGR = 1; // Restart
EXTMODULE_TIMER->CCMR1 = TIM_CCMR1_OC1M_1 | TIM_CCMR1_OC1M_0; EXTMODULE_TIMER->CCMR1 = TIM_CCMR1_OC1M_1 | TIM_CCMR1_OC1M_0;
EXTMODULE_TIMER->ARR = period_half_us; EXTMODULE_TIMER->ARR = 40000; // dummy value until the DMA request kicks in
EXTMODULE_TIMER->CCR2 = 40000; // The first frame will be sent in 20ms
EXTMODULE_TIMER->SR &= ~TIM_SR_CC2IF; // Clear flag EXTMODULE_TIMER->SR &= ~TIM_SR_CC2IF; // Clear flag
EXTMODULE_TIMER->DIER |= TIM_DIER_UDE | TIM_DIER_CC2IE; EXTMODULE_TIMER->DIER |= TIM_DIER_UDE;
EXTMODULE_TIMER->CR1 |= TIM_CR1_CEN; EXTMODULE_TIMER->CR1 |= TIM_CR1_CEN;
NVIC_EnableIRQ(EXTMODULE_TIMER_DMA_STREAM_IRQn); NVIC_EnableIRQ(EXTMODULE_TIMER_DMA_STREAM_IRQn);
@ -300,19 +301,33 @@ void extmoduleSendNextFrame()
#endif #endif
#if defined(SBUS) || defined(DSM2) || defined(MULTIMODULE) #if defined(SBUS) || defined(DSM2) || defined(MULTIMODULE)
case PROTOCOL_CHANNELS_SBUS: case PROTOCOL_CHANNELS_SBUS:
EXTMODULE_TIMER->CCER = EXTMODULE_TIMER_OUTPUT_ENABLE | (GET_SBUS_POLARITY(EXTERNAL_MODULE) ? 0 : EXTMODULE_TIMER_OUTPUT_POLARITY); // reverse polarity for Sbus if needed
// no break // no break
case PROTOCOL_CHANNELS_DSM2_LP45: case PROTOCOL_CHANNELS_DSM2_LP45:
case PROTOCOL_CHANNELS_DSM2_DSM2: case PROTOCOL_CHANNELS_DSM2_DSM2:
case PROTOCOL_CHANNELS_DSM2_DSMX: case PROTOCOL_CHANNELS_DSM2_DSMX:
case PROTOCOL_CHANNELS_MULTIMODULE: case PROTOCOL_CHANNELS_MULTIMODULE:
EXTMODULE_TIMER->CCR2 = *(extmodulePulsesData.dsm2.ptr - 1) - 4000; // 2mS in advance
if (EXTMODULE_TIMER_DMA_STREAM->CR & DMA_SxCR_EN)
return;
if (PROTOCOL_CHANNELS_SBUS == moduleState[EXTERNAL_MODULE].protocol) {
EXTMODULE_TIMER->CCER = EXTMODULE_TIMER_OUTPUT_ENABLE | (GET_SBUS_POLARITY(EXTERNAL_MODULE) ? EXTMODULE_TIMER_OUTPUT_POLARITY : 0); // reverse polarity for Sbus if needed
}
// disable timer
EXTMODULE_TIMER->CR1 &= ~TIM_CR1_CEN;
// send DMA request
EXTMODULE_TIMER_DMA_STREAM->CR &= ~DMA_SxCR_EN; // Disable DMA EXTMODULE_TIMER_DMA_STREAM->CR &= ~DMA_SxCR_EN; // Disable DMA
EXTMODULE_TIMER_DMA_STREAM->CR |= EXTMODULE_TIMER_DMA_CHANNEL | DMA_SxCR_DIR_0 | DMA_SxCR_MINC | DMA_SxCR_PSIZE_0 | DMA_SxCR_MSIZE_0 | DMA_SxCR_PL_0 | DMA_SxCR_PL_1; EXTMODULE_TIMER_DMA_STREAM->CR |= EXTMODULE_TIMER_DMA_CHANNEL | DMA_SxCR_DIR_0 | DMA_SxCR_MINC | DMA_SxCR_PSIZE_0 | DMA_SxCR_MSIZE_0 | DMA_SxCR_PL_0 | DMA_SxCR_PL_1;
EXTMODULE_TIMER_DMA_STREAM->PAR = CONVERT_PTR_UINT(&EXTMODULE_TIMER->ARR); EXTMODULE_TIMER_DMA_STREAM->PAR = CONVERT_PTR_UINT(&EXTMODULE_TIMER->ARR);
EXTMODULE_TIMER_DMA_STREAM->M0AR = CONVERT_PTR_UINT(extmodulePulsesData.dsm2.pulses); EXTMODULE_TIMER_DMA_STREAM->M0AR = CONVERT_PTR_UINT(extmodulePulsesData.dsm2.pulses);
EXTMODULE_TIMER_DMA_STREAM->NDTR = extmodulePulsesData.dsm2.ptr - extmodulePulsesData.dsm2.pulses; EXTMODULE_TIMER_DMA_STREAM->NDTR = extmodulePulsesData.dsm2.ptr - extmodulePulsesData.dsm2.pulses;
EXTMODULE_TIMER_DMA_STREAM->CR |= DMA_SxCR_EN | DMA_SxCR_TCIE; // Enable DMA EXTMODULE_TIMER_DMA_STREAM->CR |= DMA_SxCR_EN | DMA_SxCR_TCIE; // Enable DMA
// re-init timer
EXTMODULE_TIMER->EGR = 1;
EXTMODULE_TIMER->CR1 |= TIM_CR1_CEN;
break; break;
#endif #endif
@ -373,14 +388,20 @@ extern "C" void EXTMODULE_TIMER_DMA_STREAM_IRQHandler()
DMA_ClearITPendingBit(EXTMODULE_TIMER_DMA_STREAM, EXTMODULE_TIMER_DMA_FLAG_TC); DMA_ClearITPendingBit(EXTMODULE_TIMER_DMA_STREAM, EXTMODULE_TIMER_DMA_FLAG_TC);
EXTMODULE_TIMER->SR &= ~TIM_SR_CC2IF; // Clear flag switch (moduleState[EXTERNAL_MODULE].protocol) {
EXTMODULE_TIMER->DIER |= TIM_DIER_CC2IE; // Enable this interrupt case PROTOCOL_CHANNELS_PXX1_PULSES:
case PROTOCOL_CHANNELS_PPM:
EXTMODULE_TIMER->SR &= ~TIM_SR_CC2IF; // Clear flag
EXTMODULE_TIMER->DIER |= TIM_DIER_CC2IE; // Enable this interrupt
break;
}
} }
extern "C" void EXTMODULE_TIMER_CC_IRQHandler() extern "C" void EXTMODULE_TIMER_CC_IRQHandler()
{ {
EXTMODULE_TIMER->DIER &= ~TIM_DIER_CC2IE; // Stop this interrupt EXTMODULE_TIMER->DIER &= ~TIM_DIER_CC2IE; // Stop this interrupt
EXTMODULE_TIMER->SR &= ~TIM_SR_CC2IF; EXTMODULE_TIMER->SR &= ~TIM_SR_CC2IF;
if (setupPulsesExternalModule()) { if (setupPulsesExternalModule()) {
extmoduleSendNextFrame(); extmoduleSendNextFrame();
} }

View file

@ -2024,4 +2024,11 @@
#define TIMER_2MHz_RCC_APB1Periph RCC_APB1Periph_TIM7 #define TIMER_2MHz_RCC_APB1Periph RCC_APB1Periph_TIM7
#define TIMER_2MHz_TIMER TIM7 #define TIMER_2MHz_TIMER TIM7
// Mixer scheduler timer
#define MIXER_SCHEDULER_TIMER_RCC_APB1Periph RCC_APB1Periph_TIM13
#define MIXER_SCHEDULER_TIMER TIM13
#define MIXER_SCHEDULER_TIMER_FREQ (PERI1_FREQUENCY * TIMER_MULT_APB1)
#define MIXER_SCHEDULER_TIMER_IRQn TIM8_UP_TIM13_IRQn
#define MIXER_SCHEDULER_TIMER_IRQHandler TIM8_UP_TIM13_IRQHandler
#endif // _HAL_H_ #endif // _HAL_H_

View file

@ -19,6 +19,7 @@
*/ */
#include "opentx.h" #include "opentx.h"
#include "mixer_scheduler.h"
RTOS_TASK_HANDLE menusTaskId; RTOS_TASK_HANDLE menusTaskId;
RTOS_DEFINE_STACK(menusStack, MENUS_STACK_SIZE); RTOS_DEFINE_STACK(menusStack, MENUS_STACK_SIZE);
@ -75,13 +76,29 @@ bool isForcePowerOffRequested()
bool isModuleSynchronous(uint8_t moduleIdx) bool isModuleSynchronous(uint8_t moduleIdx)
{ {
uint8_t protocol = moduleState[moduleIdx].protocol; switch(moduleState[moduleIdx].protocol) {
if (protocol == PROTOCOL_CHANNELS_PXX2_HIGHSPEED || protocol == PROTOCOL_CHANNELS_PXX2_LOWSPEED || protocol == PROTOCOL_CHANNELS_CROSSFIRE || protocol == PROTOCOL_CHANNELS_GHOST || protocol == PROTOCOL_CHANNELS_NONE)
return true; case PROTOCOL_CHANNELS_PXX2_HIGHSPEED:
#if defined(INTMODULE_USART) || defined(EXTMODULE_USART) case PROTOCOL_CHANNELS_PXX2_LOWSPEED:
if (protocol == PROTOCOL_CHANNELS_PXX1_SERIAL) case PROTOCOL_CHANNELS_CROSSFIRE:
return true; case PROTOCOL_CHANNELS_GHOST:
case PROTOCOL_CHANNELS_AFHDS3:
case PROTOCOL_CHANNELS_NONE:
#if defined(MULTIMODULE)
case PROTOCOL_CHANNELS_MULTIMODULE:
#endif #endif
#if defined(INTMODULE_USART) || defined(EXTMODULE_USART)
case PROTOCOL_CHANNELS_PXX1_SERIAL:
#endif
#if defined(DSM2)
case PROTOCOL_CHANNELS_SBUS:
case PROTOCOL_CHANNELS_DSM2_LP45:
case PROTOCOL_CHANNELS_DSM2_DSM2:
case PROTOCOL_CHANNELS_DSM2_DSMX:
#endif
return true;
}
return false; return false;
} }
@ -100,12 +117,17 @@ void sendSynchronousPulses(uint8_t runMask)
} }
} }
//#define DEBUG_MIXER_SCHEDULER
uint32_t nextMixerTime[NUM_MODULES]; uint32_t nextMixerTime[NUM_MODULES];
TASK_FUNCTION(mixerTask) TASK_FUNCTION(mixerTask)
{ {
s_pulses_paused = true; s_pulses_paused = true;
mixerSchedulerInit();
mixerSchedulerStart();
while (true) { while (true) {
#if defined(SBUS_TRAINER) #if defined(SBUS_TRAINER)
// SBUS trainer // SBUS trainer
@ -120,7 +142,16 @@ TASK_FUNCTION(mixerTask)
bluetooth.wakeup(); bluetooth.wakeup();
#endif #endif
RTOS_WAIT_TICKS(1); // run mixer at least every 30ms
bool timeout = mixerSchedulerWaitForTrigger(30);
#if defined(DEBUG_MIXER_SCHEDULER)
GPIO_SetBits(EXTMODULE_TX_GPIO, EXTMODULE_TX_GPIO_PIN);
GPIO_ResetBits(EXTMODULE_TX_GPIO, EXTMODULE_TX_GPIO_PIN);
#endif
// re-enable trigger
mixerSchedulerEnableTrigger();
#if defined(SIMU) #if defined(SIMU)
if (pwrCheck() == e_power_off) { if (pwrCheck() == e_power_off) {
@ -132,23 +163,6 @@ TASK_FUNCTION(mixerTask)
} }
#endif #endif
uint32_t now = RTOS_GET_MS();
uint8_t runMask = 0;
if (now >= nextMixerTime[0]) {
runMask |= (1 << 0);
}
#if NUM_MODULES >= 2
if (now >= nextMixerTime[1]) {
runMask |= (1 << 1);
}
#endif
if (!runMask) {
continue; // go back to sleep
}
if (!s_pulses_paused) { if (!s_pulses_paused) {
uint16_t t0 = getTmr2MHz(); uint16_t t0 = getTmr2MHz();
@ -183,29 +197,18 @@ TASK_FUNCTION(mixerTask)
if (t0 > maxMixerDuration) if (t0 > maxMixerDuration)
maxMixerDuration = t0; maxMixerDuration = t0;
sendSynchronousPulses(runMask); // TODO:
// - check the cause of timeouts when switching
// between protocols with multi-proto RF
if (timeout)
serialPrint("mix sched timeout!");
// TODO: fix runMask
sendSynchronousPulses((1 << INTERNAL_MODULE) | (1 << EXTERNAL_MODULE));
} }
} }
} }
void scheduleNextMixerCalculation(uint8_t module, uint32_t period_ms)
{
// Schedule next mixer calculation time,
if (isModuleSynchronous(module)) {
nextMixerTime[module] += period_ms / RTOS_MS_PER_TICK;
if (nextMixerTime[module] < RTOS_GET_TIME()) {
// we are late ... let's add some small delay
nextMixerTime[module] = (uint32_t) RTOS_GET_TIME() + (period_ms / RTOS_MS_PER_TICK);
}
}
else {
// for now assume mixer calculation takes 2 ms.
nextMixerTime[module] = (uint32_t) RTOS_GET_TIME() + (period_ms / RTOS_MS_PER_TICK);
}
DEBUG_TIMER_STOP(debugTimerMixerCalcToUsage);
}
#define MENU_TASK_PERIOD_TICKS (50 / RTOS_MS_PER_TICK) // 50ms #define MENU_TASK_PERIOD_TICKS (50 / RTOS_MS_PER_TICK) // 50ms

View file

@ -184,6 +184,26 @@ void processCrossfireTelemetryFrame()
break; break;
} }
case RADIO_ID:
if (telemetryRxBuffer[3] == 0xEA // radio address
&& telemetryRxBuffer[5] == 0x10 // timing correction frame
) {
uint32_t update_interval;
int32_t offset;
if (getCrossfireTelemetryValue<4>(6, (int32_t&)update_interval) && getCrossfireTelemetryValue<4>(10, offset)) {
// values are in 10th of micro-seconds
update_interval /= 10;
offset /= 10;
TRACE("[XF] Rate: %d, Lag: %d", update_interval, offset);
getModuleSyncStatus(EXTERNAL_MODULE).update(update_interval, offset + SAFE_SYNC_LAG);
}
}
break;
#if defined(LUA) #if defined(LUA)
default: default:
if (luaInputTelemetryFifo && luaInputTelemetryFifo->hasSpace(telemetryRxBufferCount-2) ) { if (luaInputTelemetryFifo && luaInputTelemetryFifo->hasSpace(telemetryRxBufferCount-2) ) {

View file

@ -99,15 +99,15 @@ const uint32_t CROSSFIRE_BAUDRATES[] = {
115200, 115200,
}; };
const uint8_t CROSSFIRE_PERIODS[] = { const uint8_t CROSSFIRE_PERIODS[] = {
4, 4,
16, 16,
}; };
#if SPORT_MAX_BAUDRATE < 400000 #if SPORT_MAX_BAUDRATE < 400000
#define CROSSFIRE_BAUDRATE CROSSFIRE_BAUDRATES[g_eeGeneral.telemetryBaudrate] #define CROSSFIRE_BAUDRATE CROSSFIRE_BAUDRATES[g_eeGeneral.telemetryBaudrate]
#define CROSSFIRE_PERIOD CROSSFIRE_PERIODS[g_eeGeneral.telemetryBaudrate] #define CROSSFIRE_PERIOD (CROSSFIRE_PERIODS[g_eeGeneral.telemetryBaudrate]*1000)
#else #else
#define CROSSFIRE_BAUDRATE 400000 #define CROSSFIRE_BAUDRATE 400000
#define CROSSFIRE_PERIOD 4 // 4ms #define CROSSFIRE_PERIOD 6666 /* us; 150 Hz */
#endif #endif
#define CROSSFIRE_TELEM_MIRROR_BAUDRATE 115200 #define CROSSFIRE_TELEM_MIRROR_BAUDRATE 115200

View file

@ -63,7 +63,6 @@ enum MultiBufferState : uint8_t
#if defined(INTERNAL_MODULE_MULTI) #if defined(INTERNAL_MODULE_MULTI)
static MultiModuleStatus multiModuleStatus[NUM_MODULES] = {MultiModuleStatus(), MultiModuleStatus()}; static MultiModuleStatus multiModuleStatus[NUM_MODULES] = {MultiModuleStatus(), MultiModuleStatus()};
static MultiModuleSyncStatus multiSyncStatus[NUM_MODULES] = {MultiModuleSyncStatus(), MultiModuleSyncStatus()};
static uint8_t multiBindStatus[NUM_MODULES] = {MULTI_NORMAL_OPERATION, MULTI_NORMAL_OPERATION}; static uint8_t multiBindStatus[NUM_MODULES] = {MULTI_NORMAL_OPERATION, MULTI_NORMAL_OPERATION};
static MultiBufferState multiTelemetryBufferState[NUM_MODULES]; static MultiBufferState multiTelemetryBufferState[NUM_MODULES];
@ -74,11 +73,6 @@ MultiModuleStatus &getMultiModuleStatus(uint8_t module)
return multiModuleStatus[module]; return multiModuleStatus[module];
} }
MultiModuleSyncStatus &getMultiSyncStatus(uint8_t module)
{
return multiSyncStatus[module];
}
uint8_t getMultiBindStatus(uint8_t module) uint8_t getMultiBindStatus(uint8_t module)
{ {
return multiBindStatus[module]; return multiBindStatus[module];
@ -111,7 +105,6 @@ uint8_t intTelemetryRxBufferCount;
#else // !INTERNAL_MODULE_MULTI #else // !INTERNAL_MODULE_MULTI
static MultiModuleStatus multiModuleStatus; static MultiModuleStatus multiModuleStatus;
static MultiModuleSyncStatus multiSyncStatus;
static uint8_t multiBindStatus = MULTI_NORMAL_OPERATION; static uint8_t multiBindStatus = MULTI_NORMAL_OPERATION;
static MultiBufferState multiTelemetryBufferState; static MultiBufferState multiTelemetryBufferState;
@ -122,11 +115,6 @@ MultiModuleStatus& getMultiModuleStatus(uint8_t)
return multiModuleStatus; return multiModuleStatus;
} }
MultiModuleSyncStatus& getMultiSyncStatus(uint8_t)
{
return multiSyncStatus;
}
uint8_t getMultiBindStatus(uint8_t) uint8_t getMultiBindStatus(uint8_t)
{ {
return multiBindStatus; return multiBindStatus;
@ -254,24 +242,17 @@ static void processMultiStatusPacket(const uint8_t * data, uint8_t module, uint8
static void processMultiSyncPacket(const uint8_t * data, uint8_t module) static void processMultiSyncPacket(const uint8_t * data, uint8_t module)
{ {
MultiModuleSyncStatus &status = getMultiSyncStatus(module); ModuleSyncStatus &status = getModuleSyncStatus(module);
status.lastUpdate = get_tmr10ms(); uint16_t refreshRate = data[0] << 8 | data[1];
status.interval = data[4]; int16_t inputLag = data[2] << 8 | data[3];
status.target = data[5];
#if !defined(PPM_PIN_SERIAL)
auto oldlag = status.inputLag;
(void) oldlag;
#endif
status.calcAdjustedRefreshRate(data[0] << 8 | data[1], data[2] << 8 | data[3]); // if (inputLag > refreshRate/2)
// inputLag -= refreshRate;
#if !defined(PPM_PIN_SERIAL) status.update(refreshRate, inputLag);
TRACE("MP ADJ: rest: %d, lag %04d, diff: %04d target: %d, interval: %d, Refresh: %d, intAdjRefresh: %d, adjRefresh %d\r\n",
module == EXTERNAL_MODULE ? extmodulePulsesData.dsm2.rest : 0, serialPrint("MP ADJ: R %d, L %04d", refreshRate, inputLag);
status.inputLag, oldlag - status.inputLag, status.target, status.interval, status.refreshRate, status.adjustedRefreshRate / 50,
status.getAdjustedRefreshRate());
#endif
} }
#if defined(PCBTARANIS) || defined(PCBHORUS) #if defined(PCBTARANIS) || defined(PCBHORUS)
@ -434,104 +415,6 @@ static void processMultiTelemetryPaket(const uint8_t * packet, uint8_t module)
} }
} }
#define MIN_REFRESH_RATE 5500
void MultiModuleSyncStatus::calcAdjustedRefreshRate(uint16_t newRefreshRate, uint16_t newInputLag)
{
// Check how far off we are from our target, positive means we are too slow, negative we are too fast
int lagDifference = newInputLag - inputLag;
// The refresh rate that we target
// Below is least common multiple of MIN_REFRESH_RATE and requested rate
uint16_t targetRefreshRate = (uint16_t) (newRefreshRate * ((MIN_REFRESH_RATE / (newRefreshRate - 1)) + 1));
// Overflow, reverse sample
if (lagDifference < -targetRefreshRate / 2)
lagDifference = -lagDifference;
// Reset adjusted refresh if rate has changed
if (newRefreshRate != refreshRate) {
refreshRate = newRefreshRate;
adjustedRefreshRate = targetRefreshRate;
if (adjustedRefreshRate >= 30000)
adjustedRefreshRate /= 2;
// Our refresh rate in ps
adjustedRefreshRate *= 1000;
return;
}
// Caluclate how many samples went into the reported input Lag (*10)
int numsamples = interval * 10000 / targetRefreshRate;
// Convert lagDifference to ps
lagDifference = lagDifference * 1000;
// Calculate the time we intentionally were late/early
if (inputLag > target * 10 + 30)
lagDifference += numsamples * 500;
else if (inputLag < target * 10 - 30)
lagDifference -= numsamples * 500;
// Caculate the time in ps each frame is to slow (positive), fast(negative)
int perframeps = lagDifference * 10 / numsamples;
if (perframeps > 20000)
perframeps = 20000;
if (perframeps < -20000)
perframeps = -20000;
adjustedRefreshRate = (adjustedRefreshRate + perframeps);
// Safeguards
if (adjustedRefreshRate < MIN_REFRESH_RATE * 1000)
adjustedRefreshRate = MIN_REFRESH_RATE * 1000;
if (adjustedRefreshRate > 30 * 1000 * 1000)
adjustedRefreshRate = 30 * 1000 * 1000;
inputLag = newInputLag;
}
static uint8_t counter;
const uint16_t MultiModuleSyncStatus::getAdjustedRefreshRate()
{
if (!isValid() || refreshRate == 0)
return 18000;
counter = (uint8_t) (counter + 1 % 10);
uint16_t rate = (uint16_t) ((adjustedRefreshRate + counter * 50) / 500);
// Check how far off we are from our target, positive means we are too slow, negative we are too fast
if (inputLag > target * 10 + 30)
return (uint16_t) (rate - 1);
else if (inputLag < target * 10 - 30)
return (uint16_t) (rate + 1);
else
return rate;
}
void MultiModuleSyncStatus::getRefreshString(char * statusText)
{
if (!isValid()) {
return;
}
char * tmp = statusText;
#if defined(DEBUG)
*tmp++ = 'L';
tmp = strAppendUnsigned(tmp, inputLag, 5);
tmp = strAppend(tmp, "us R ");
tmp = strAppendUnsigned(tmp, (uint32_t) (adjustedRefreshRate / 1000), 5);
tmp = strAppend(tmp, "us");
#else
tmp = strAppend(tmp, "Sync at ");
tmp = strAppendUnsigned(tmp, (uint32_t) (adjustedRefreshRate / 1000000));
tmp = strAppend(tmp, " ms");
#endif
}
void MultiModuleStatus::getStatusString(char * statusText) const void MultiModuleStatus::getStatusString(char * statusText) const
{ {
if (!isValid()) { if (!isValid()) {

View file

@ -94,28 +94,6 @@ void processMultiTelemetryData(uint8_t data, uint8_t module);
#define MULTI_SCANNER_MAX_CHANNEL 249 #define MULTI_SCANNER_MAX_CHANNEL 249
// This should be put into the Module definition if other modules gain this functionality
struct MultiModuleSyncStatus {
uint32_t adjustedRefreshRate = 9000 * 1000; // in ps
tmr10ms_t lastUpdate;
uint16_t refreshRate;
uint16_t inputLag;
uint8_t interval;
uint8_t target;
inline bool isValid() const
{
return (get_tmr10ms() - lastUpdate < 100);
}
void getRefreshString(char * refreshText);
const uint16_t getAdjustedRefreshRate();
void calcAdjustedRefreshRate(uint16_t newRefreshRate, uint16_t newInputLag);
};
MultiModuleSyncStatus& getMultiSyncStatus(uint8_t module);
struct MultiModuleStatus { struct MultiModuleStatus {
uint8_t major; uint8_t major;

View file

@ -21,6 +21,7 @@
#include "opentx.h" #include "opentx.h"
#include "multi.h" #include "multi.h"
#include "pulses/afhds3.h" #include "pulses/afhds3.h"
#include "mixer_scheduler.h"
uint8_t telemetryStreaming = 0; uint8_t telemetryStreaming = 0;
uint8_t telemetryRxBuffer[TELEMETRY_RX_PACKET_SIZE]; // Receive buffer. 9 bytes (full packet), worst case 18 bytes with byte-stuffing (+1) uint8_t telemetryRxBuffer[TELEMETRY_RX_PACKET_SIZE]; // Receive buffer. 9 bytes (full packet), worst case 18 bytes with byte-stuffing (+1)
@ -374,3 +375,88 @@ OutputTelemetryBuffer outputTelemetryBuffer __DMA;
#if defined(LUA) #if defined(LUA)
Fifo<uint8_t, LUA_TELEMETRY_INPUT_FIFO_SIZE> * luaInputTelemetryFifo = NULL; Fifo<uint8_t, LUA_TELEMETRY_INPUT_FIFO_SIZE> * luaInputTelemetryFifo = NULL;
#endif #endif
#if defined(HARDWARE_INTERNAL_MODULE)
static ModuleSyncStatus moduleSyncStatus[NUM_MODULES];
ModuleSyncStatus &getModuleSyncStatus(uint8_t moduleIdx)
{
return moduleSyncStatus[moduleIdx];
}
#else
static ModuleSyncStatus moduleSyncStatus;
ModuleSyncStatus &getModuleSyncStatus(uint8_t moduleIdx)
{
return moduleSyncStatus;
}
#endif
ModuleSyncStatus::ModuleSyncStatus()
{
memset(this, 0, sizeof(ModuleSyncStatus));
}
void ModuleSyncStatus::update(uint16_t newRefreshRate, uint16_t newInputLag)
{
if (!newRefreshRate)
return;
if (newRefreshRate < MIN_REFRESH_RATE)
newRefreshRate = newRefreshRate * (MIN_REFRESH_RATE / (newRefreshRate + 1));
else if (newRefreshRate > MAX_REFRESH_RATE)
newRefreshRate = MAX_REFRESH_RATE;
refreshRate = newRefreshRate;
inputLag = newInputLag;
currentLag = newInputLag;
lastUpdate = get_tmr10ms();
}
uint16_t ModuleSyncStatus::getAdjustedRefreshRate()
{
int16_t lag = currentLag - SAFE_SYNC_LAG;
int32_t newRefreshRate = refreshRate;
if (lag == 0) {
return refreshRate;
}
newRefreshRate += lag;
if (newRefreshRate < MIN_REFRESH_RATE) {
newRefreshRate = MIN_REFRESH_RATE;
}
else if (newRefreshRate > MAX_REFRESH_RATE) {
newRefreshRate = MAX_REFRESH_RATE;
}
TRACE("[SYNC] rate = %dus",newRefreshRate);
currentLag -= newRefreshRate - refreshRate;
return (uint16_t)newRefreshRate;
}
void ModuleSyncStatus::getRefreshString(char * statusText)
{
if (!isValid()) {
return;
}
char * tmp = statusText;
#if defined(DEBUG)
*tmp++ = 'L';
tmp = strAppendUnsigned(tmp, inputLag, 5);
tmp = strAppend(tmp, "us R ");
tmp = strAppendUnsigned(tmp, (uint32_t) (refreshRate / 1000), 5);
tmp = strAppend(tmp, "us");
#else
tmp = strAppend(tmp, "Sync at ");
tmp = strAppendUnsigned(tmp, (uint32_t) (refreshRate / 1000000));
tmp = strAppend(tmp, " ms");
#endif
}

View file

@ -289,4 +289,35 @@ extern Fifo<uint8_t, LUA_TELEMETRY_INPUT_FIFO_SIZE> * luaInputTelemetryFifo;
void processPXX2Frame(uint8_t module, const uint8_t *frame); void processPXX2Frame(uint8_t module, const uint8_t *frame);
// Module pulse synchronization
#define SAFE_SYNC_LAG 800 /* us */
struct ModuleSyncStatus
{
// feedback input: last received values
uint16_t refreshRate; // in us
int16_t inputLag; // in us
tmr10ms_t lastUpdate; // in 10ms
int16_t currentLag; // in us
inline bool isValid() {
// 2 seconds
return (get_tmr10ms() - lastUpdate < 200);
}
// Set feedback from RF module
void update(uint16_t newRefreshRate, uint16_t newInputLag);
// Get computed settings for scheduler
uint16_t getAdjustedRefreshRate();
// Status string for the UI
void getRefreshString(char* refreshText);
ModuleSyncStatus();
};
ModuleSyncStatus& getModuleSyncStatus(uint8_t moduleIdx);
#endif // _TELEMETRY_H_ #endif // _TELEMETRY_H_