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
+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()) {