feat: More flexible ADC inputs (#3870)
Co-authored-by: 3djc <lesitewebdejc@hotmail.com> Co-authored-by: Peter Feerick <peter.feerick@gmail.com> Co-authored-by: philmoz <phil.a.mitchell@gmail.com>
This commit is contained in:
co-authored by
3djc
Peter Feerick
philmoz
parent
f5593c44e8
commit
b48e07026d
@@ -7,8 +7,14 @@ set(SRC ${SRC}
|
||||
hal/module_port.cpp
|
||||
hal/adc_driver.cpp
|
||||
hal/key_driver.cpp
|
||||
hal/switch_driver.cpp
|
||||
)
|
||||
|
||||
if (FLEXSW)
|
||||
# define the number of allowed flexible adc entries used as switch
|
||||
add_definitions(-DMAX_FLEX_SWITCHES=${FLEXSW})
|
||||
endif()
|
||||
|
||||
include_directories(${FATFS_DIR} ${FATFS_DIR}/option)
|
||||
|
||||
set(BOOTLOADER_SRC ${BOOTLOADER_SRC}
|
||||
|
||||
+158
-92
@@ -22,10 +22,8 @@
|
||||
#include "adc_driver.h"
|
||||
#include "board.h"
|
||||
|
||||
// IS_POT_SLIDER_AVAILABLE()
|
||||
#include "opentx.h"
|
||||
|
||||
|
||||
const etx_hal_adc_driver_t* _hal_adc_driver = nullptr;
|
||||
const etx_hal_adc_inputs_t* _hal_adc_inputs = nullptr;
|
||||
|
||||
@@ -71,10 +69,20 @@ bool adcRead()
|
||||
return true;
|
||||
}
|
||||
|
||||
void adcCalibDefaults()
|
||||
{
|
||||
for (int i = 0; i < adcGetMaxCalibratedInputs(); i++) {
|
||||
CalibData* calib = &g_eeGeneral.calib[i];
|
||||
calib->mid = 1023;
|
||||
calib->spanNeg = 1024 - (1024 / STICK_TOLERANCE);
|
||||
calib->spanPos = 1024 - (1024 / STICK_TOLERANCE);
|
||||
}
|
||||
}
|
||||
|
||||
void adcCalibSetMidPoint()
|
||||
{
|
||||
uint8_t max_inputs = adcGetMaxCalibratedInputs();
|
||||
uint8_t pot_offset = adcGetInputOffset(ADC_INPUT_POT);
|
||||
uint8_t pot_offset = adcGetInputOffset(ADC_INPUT_FLEX);
|
||||
|
||||
for (uint8_t i = 0; i < max_inputs; i++) {
|
||||
|
||||
@@ -124,32 +132,32 @@ void adcCalibSetMinMax()
|
||||
{
|
||||
// get low and high vals for sticks and pots
|
||||
uint8_t max_input = adcGetMaxCalibratedInputs();
|
||||
uint8_t pot_offset = adcGetInputOffset(ADC_INPUT_POT);
|
||||
uint8_t pot_offset = adcGetInputOffset(ADC_INPUT_FLEX);
|
||||
|
||||
for (uint8_t i = 0; i < max_input; i++) {
|
||||
|
||||
auto& calib = reusableBuffer.calib.inputs[i];
|
||||
int16_t vt = getAnalogValue(i) >> 1;
|
||||
|
||||
if (i < pot_offset || !IS_POT_MULTIPOS(i - pot_offset)) {
|
||||
int16_t vt = anaIn(i);
|
||||
calib.input.loVal = min(vt, calib.input.loVal);
|
||||
calib.input.hiVal = max(vt, calib.input.hiVal);
|
||||
|
||||
if (i >= pot_offset && IS_POT_WITHOUT_DETENT(i - pot_offset)) {
|
||||
calib.input.midVal = (calib.input.hiVal + calib.input.loVal) / 2;
|
||||
if (i >= pot_offset) {
|
||||
auto pot_cfg = POT_CONFIG(i - pot_offset);
|
||||
if (pot_cfg == FLEX_POT || pot_cfg == FLEX_SWITCH || pot_cfg == FLEX_NONE) {
|
||||
calib.input.midVal = (calib.input.hiVal + calib.input.loVal) / 2;
|
||||
}
|
||||
}
|
||||
|
||||
// in case we enough input movement, store the result
|
||||
if (abs(calib.input.loVal - calib.input.hiVal) > 50) {
|
||||
writeAnalogCalib(i, calib.input.loVal, calib.input.midVal, calib.input.hiVal);
|
||||
writeAnalogCalib(i, calib.input.loVal, calib.input.midVal, calib.input.hiVal);
|
||||
}
|
||||
} else {
|
||||
auto& xpot = calib.xpot;
|
||||
int count = xpot.stepsCount;
|
||||
if (count <= XPOTS_MULTIPOS_COUNT) {
|
||||
// use raw analog value for multipos calibraton,
|
||||
// anaIn() already has multipos decoded value
|
||||
int16_t vt = getAnalogValue(i) >> 1;
|
||||
if (xpot.lastCount == 0 || vt < xpot.lastPosition - XPOT_DELTA ||
|
||||
vt > xpot.lastPosition + XPOT_DELTA) {
|
||||
xpot.lastPosition = vt;
|
||||
@@ -192,8 +200,8 @@ void adcCalibSetMinMax()
|
||||
|
||||
static void disableUncalibratedXPots()
|
||||
{
|
||||
uint8_t pot_offset = adcGetInputOffset(ADC_INPUT_POT);
|
||||
uint8_t max_pots = adcGetMaxInputs(ADC_INPUT_POT);
|
||||
uint8_t pot_offset = adcGetInputOffset(ADC_INPUT_FLEX);
|
||||
uint8_t max_pots = adcGetMaxInputs(ADC_INPUT_FLEX);
|
||||
|
||||
for (uint8_t i = 0; i < max_pots; i++) {
|
||||
if (IS_POT_MULTIPOS(i)) {
|
||||
@@ -252,8 +260,8 @@ uint16_t* getAnalogValues()
|
||||
// used by diaganas
|
||||
uint32_t s_anaFilt[MAX_ANALOG_INPUTS];
|
||||
|
||||
#define ANALOG_MULTIPLIER (1<<ANALOG_SCALE)
|
||||
#define ANA_FILT(chan) (s_anaFilt[chan] / (JITTER_ALPHA * ANALOG_MULTIPLIER))
|
||||
#define ANALOG_MULTIPLIER (1 << ANALOG_SCALE)
|
||||
#define ANA_FILT(chan) (s_anaFilt[chan] / (JITTER_ALPHA * ANALOG_MULTIPLIER))
|
||||
#if (JITTER_ALPHA * ANALOG_MULTIPLIER > 32)
|
||||
#error "JITTER_FILTER_STRENGTH and ANALOG_SCALE are too big, their summ should be <= 5 !!!"
|
||||
#endif
|
||||
@@ -308,10 +316,121 @@ uint16_t getBatteryVoltage()
|
||||
#endif
|
||||
}
|
||||
|
||||
static uint32_t apply_low_pass_filter(uint32_t v, uint32_t v_prev,
|
||||
bool is_main_input)
|
||||
{
|
||||
// Jitter filter:
|
||||
// * pass trough any big change directly
|
||||
// * for small change use Modified moving average (MMA) filter
|
||||
//
|
||||
// Explanation:
|
||||
//
|
||||
// Normal MMA filter has this formula:
|
||||
// <out> = ((ALPHA-1)*<out> + <in>)/ALPHA
|
||||
//
|
||||
// If calculation is done this way with integer arithmetics, then any small
|
||||
// change in input signal is lost. One way to combat that, is to rearrange the
|
||||
// formula somewhat, to store a more precise (larger) number between
|
||||
// iterations. The basic idea is to store undivided value between iterations.
|
||||
// Therefore an new variable <filtered> is used. The new formula becomes:
|
||||
// <filtered> = <filtered> - <filtered>/ALPHA + <in>
|
||||
// <out> = <filtered>/ALPHA (use only when out is needed)
|
||||
//
|
||||
// The above formula with a maximum allowed ALPHA value (we are limited by
|
||||
// the 16 bit s_anaFilt[]) was tested on the radio. The resulting signal still
|
||||
// had some jitter (a value of 1 was observed). The jitter might be bigger on
|
||||
// other radios.
|
||||
//
|
||||
// So another idea is to use larger input values for filtering. So instead of
|
||||
// using input in a range from 0 to 2047, we use twice larger number (temp[x]
|
||||
// is divided less)
|
||||
//
|
||||
// This also means that ALPHA must be lowered (remember 16 bit limit), but
|
||||
// test results have proved that this kind of filtering gives better results.
|
||||
// So the recommended values for filter are:
|
||||
// JITTER_FILTER_STRENGTH 4
|
||||
// ANALOG_SCALE 1
|
||||
//
|
||||
uint32_t previous = v_prev / JITTER_ALPHA;
|
||||
uint32_t diff = (v > previous) ? (v - previous) : (previous - v);
|
||||
|
||||
// Combine ADC jitter filter setting form radio and model.
|
||||
// Model can override (on or off) or use setting from radio setup.
|
||||
// Model setting is active when 1, radio setting is active when 0
|
||||
// Please note: these settings only apply to main controls.
|
||||
bool useJitterFilter = true;
|
||||
if (is_main_input) {
|
||||
if (g_model.jitterFilter == OVERRIDE_GLOBAL) {
|
||||
// Use radio setting - which is inverted
|
||||
useJitterFilter = !g_eeGeneral.noJitterFilter;
|
||||
} else {
|
||||
// Enable if value is "On", disable if "Off"
|
||||
useJitterFilter = (g_model.jitterFilter == OVERRIDE_ON);
|
||||
}
|
||||
}
|
||||
|
||||
uint32_t out;
|
||||
if (useJitterFilter && diff < (10 * ANALOG_MULTIPLIER)) {
|
||||
// apply jitter filter
|
||||
out = (v_prev - previous) + v;
|
||||
} else {
|
||||
// use unfiltered value
|
||||
out = v * JITTER_ALPHA;
|
||||
}
|
||||
|
||||
return out;
|
||||
}
|
||||
|
||||
static uint32_t apply_calibration(const CalibData* calib, uint32_t v)
|
||||
{
|
||||
// Simu uses normed inputs
|
||||
#if !defined(SIMU)
|
||||
// Apply calibration relative to mid-point
|
||||
int32_t s = v - 2 * calib->mid;
|
||||
s = s * (int32_t)RESX /
|
||||
(max((int16_t)100, (s > 0 ? calib->spanPos : calib->spanNeg)));
|
||||
|
||||
// Translate back in range
|
||||
s += 2 * RESX;
|
||||
|
||||
// Limit values to supported range
|
||||
if (s < 0) {
|
||||
s = 0;
|
||||
} else if (s > 4 * RESX) {
|
||||
s = 4 * RESX;
|
||||
}
|
||||
|
||||
v = s;
|
||||
#endif
|
||||
|
||||
return v;
|
||||
}
|
||||
|
||||
static uint32_t apply_multipos(const StepsCalibData* calib, uint32_t v)
|
||||
{
|
||||
constexpr uint32_t ALPHA_MULT = JITTER_ALPHA * ANALOG_MULTIPLIER;
|
||||
constexpr uint32_t ANAFILT_MAX = 2 * RESX * ALPHA_MULT;
|
||||
|
||||
// TODO: consider adding another low pass filter to eliminate multipos
|
||||
// switching glitches
|
||||
uint8_t vShifted = (v / ALPHA_MULT) >> 4;
|
||||
|
||||
for (uint32_t i = 0; i < calib->count; i++) {
|
||||
if (vShifted < calib->steps[i]) {
|
||||
return (i * (ANAFILT_MAX + ALPHA_MULT)) / calib->count;
|
||||
}
|
||||
}
|
||||
|
||||
return ANAFILT_MAX;
|
||||
}
|
||||
|
||||
void getADC()
|
||||
{
|
||||
uint8_t max_analogs = adcGetMaxInputs(ADC_INPUT_ALL);
|
||||
uint8_t pot_offset = adcGetInputOffset(ADC_INPUT_POT);
|
||||
auto max_analogs = adcGetMaxInputs(ADC_INPUT_ALL);
|
||||
auto max_mains = adcGetMaxInputs(ADC_INPUT_MAIN);
|
||||
auto max_pots = adcGetMaxInputs(ADC_INPUT_FLEX);
|
||||
auto pot_offset = adcGetInputOffset(ADC_INPUT_FLEX);
|
||||
auto max_calib_analogs = adcGetMaxCalibratedInputs();
|
||||
|
||||
#if defined(JITTER_MEASURE)
|
||||
if (JITTER_MEASURE_ACTIVE() && jitterResetTime < get_tmr10ms()) {
|
||||
@@ -320,78 +439,39 @@ void getADC()
|
||||
rawJitter[x].reset();
|
||||
avgJitter[x].reset();
|
||||
}
|
||||
jitterResetTime = get_tmr10ms() + 100; //every second
|
||||
jitterResetTime = get_tmr10ms() + 100; // every second
|
||||
}
|
||||
#endif
|
||||
|
||||
DEBUG_TIMER_START(debugTimerAdcRead);
|
||||
if (!adcRead())
|
||||
TRACE("adcRead failed");
|
||||
if (!adcRead()) TRACE("adcRead failed");
|
||||
DEBUG_TIMER_STOP(debugTimerAdcRead);
|
||||
|
||||
// TODO: jitter filter should probably still be applied
|
||||
// to VBAT and RTC_BAT, no matter what is configured
|
||||
//
|
||||
for (uint8_t x = 0; x < max_analogs; x++) {
|
||||
|
||||
uint32_t v = getAnalogValue(x) >> (1 - ANALOG_SCALE);
|
||||
bool is_flex_input = (x >= pot_offset) && (x < pot_offset + max_pots);
|
||||
bool is_multipos = is_flex_input && IS_POT_MULTIPOS(x - pot_offset);
|
||||
|
||||
// Jitter filter:
|
||||
// * pass trough any big change directly
|
||||
// * for small change use Modified moving average (MMA) filter
|
||||
//
|
||||
// Explanation:
|
||||
//
|
||||
// Normal MMA filter has this formula:
|
||||
// <out> = ((ALPHA-1)*<out> + <in>)/ALPHA
|
||||
//
|
||||
// If calculation is done this way with integer arithmetics, then any small change in
|
||||
// input signal is lost. One way to combat that, is to rearrange the formula somewhat,
|
||||
// to store a more precise (larger) number between iterations. The basic idea is to
|
||||
// store undivided value between iterations. Therefore an new variable <filtered> is
|
||||
// used. The new formula becomes:
|
||||
// <filtered> = <filtered> - <filtered>/ALPHA + <in>
|
||||
// <out> = <filtered>/ALPHA (use only when out is needed)
|
||||
//
|
||||
// The above formula with a maximum allowed ALPHA value (we are limited by
|
||||
// the 16 bit s_anaFilt[]) was tested on the radio. The resulting signal still had
|
||||
// some jitter (a value of 1 was observed). The jitter might be bigger on other
|
||||
// radios.
|
||||
//
|
||||
// So another idea is to use larger input values for filtering. So instead of using
|
||||
// input in a range from 0 to 2047, we use twice larger number (temp[x] is divided less)
|
||||
//
|
||||
// This also means that ALPHA must be lowered (remember 16 bit limit), but test results
|
||||
// have proved that this kind of filtering gives better results. So the recommended values
|
||||
// for filter are:
|
||||
// JITTER_FILTER_STRENGTH 4
|
||||
// ANALOG_SCALE 1
|
||||
//
|
||||
// Variables mapping:
|
||||
// * <in> = v
|
||||
// * <out> = s_anaFilt[x]
|
||||
uint32_t previous = s_anaFilt[x] / JITTER_ALPHA;
|
||||
uint32_t diff = (v > previous) ? (v - previous) : (previous - v);
|
||||
// 1st: apply calibration
|
||||
uint32_t v = getAnalogValue(x);
|
||||
|
||||
// Combine ADC jitter filter setting form radio and model.
|
||||
// Model can override (on or off) or use setting from radio setup.
|
||||
// Model setting is active when 1, radio setting is active when 0
|
||||
uint8_t useJitterFilter = 0;
|
||||
if (g_model.jitterFilter == OVERRIDE_GLOBAL) {
|
||||
// Use radio setting - which is inverted
|
||||
useJitterFilter = !g_eeGeneral.noJitterFilter;
|
||||
} else {
|
||||
// Enable if value is "On", disable if "Off"
|
||||
useJitterFilter = (g_model.jitterFilter == OVERRIDE_ON)?1:0;
|
||||
if (x < max_calib_analogs && !is_multipos) {
|
||||
v = apply_calibration(&g_eeGeneral.calib[x], v);
|
||||
}
|
||||
|
||||
if (useJitterFilter && diff < (10*ANALOG_MULTIPLIER)) {
|
||||
// apply jitter filter
|
||||
s_anaFilt[x] = (s_anaFilt[x] - previous) + v;
|
||||
// 2nd: apply inversion
|
||||
if (is_flex_input && getPotInversion(x - pot_offset)) {
|
||||
v = 4 * RESX - v;
|
||||
}
|
||||
else {
|
||||
// use unfiltered value
|
||||
s_anaFilt[x] = v * JITTER_ALPHA;
|
||||
|
||||
// 3rd: apply filtering
|
||||
s_anaFilt[x] = apply_low_pass_filter(v, s_anaFilt[x], x < max_mains);
|
||||
|
||||
if (is_multipos) {
|
||||
const auto* calib = (const StepsCalibData*)&g_eeGeneral.calib[x];
|
||||
if (IS_MULTIPOS_CALIBRATED(calib)) {
|
||||
s_anaFilt[x] = apply_multipos(calib, s_anaFilt[x]);
|
||||
}
|
||||
}
|
||||
|
||||
#if defined(JITTER_MEASURE)
|
||||
@@ -399,20 +479,6 @@ void getADC()
|
||||
avgJitter[x].measure(ANA_FILT(x));
|
||||
}
|
||||
#endif
|
||||
|
||||
#define ANAFILT_MAX (2 * RESX * JITTER_ALPHA * ANALOG_MULTIPLIER)
|
||||
StepsCalibData * calib = (StepsCalibData *) &g_eeGeneral.calib[x];
|
||||
if (IS_POT_MULTIPOS(x - pot_offset) && IS_MULTIPOS_CALIBRATED(calib)) {
|
||||
// TODO: consider adding another low pass filter to eliminate multipos switching glitches
|
||||
uint8_t vShifted = ANA_FILT(x) >> 4;
|
||||
s_anaFilt[x] = ANAFILT_MAX;
|
||||
for (uint32_t i = 0; i < calib->count; i++) {
|
||||
if (vShifted < calib->steps[i]) {
|
||||
s_anaFilt[x] = (i * (ANAFILT_MAX + JITTER_ALPHA * ANALOG_MULTIPLIER)) / calib->count;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -436,7 +502,7 @@ uint8_t adcGetInputOffset(uint8_t type)
|
||||
|
||||
uint8_t adcGetMaxCalibratedInputs()
|
||||
{
|
||||
// ADC_INPUT_MAIN + ADC_INPUT_POT + ADC_INPUT_AXIS
|
||||
// ADC_INPUT_MAIN + ADC_INPUT_FLEX + ADC_INPUT_AXIS
|
||||
return adcGetInputOffset(ADC_INPUT_VBAT);
|
||||
}
|
||||
|
||||
@@ -467,7 +533,7 @@ const char* adcGetInputName(uint8_t idx)
|
||||
|
||||
// find the proper input type
|
||||
while (_hal_adc_inputs[type].offset + _hal_adc_inputs[type].n_inputs <= idx) {
|
||||
if (++type > ADC_INPUT_AXIS) return nullptr;
|
||||
if (++type > ADC_INPUT_FLEX) return nullptr;
|
||||
}
|
||||
|
||||
idx -= _hal_adc_inputs[type].offset;
|
||||
|
||||
@@ -37,8 +37,7 @@
|
||||
|
||||
enum {
|
||||
ADC_INPUT_MAIN=0, // gimbals / wheel + throttle
|
||||
ADC_INPUT_POT,
|
||||
ADC_INPUT_AXIS,
|
||||
ADC_INPUT_FLEX,
|
||||
ADC_INPUT_VBAT,
|
||||
ADC_INPUT_RTC_BAT,
|
||||
ADC_INPUT_ALL,
|
||||
@@ -92,6 +91,9 @@ uint16_t* getAnalogValues();
|
||||
|
||||
// Run calibration steps:
|
||||
|
||||
// Set default values before loading radio settings
|
||||
void adcCalibDefaults();
|
||||
|
||||
// Set mid-points and reset min/max in temporary buffer
|
||||
void adcCalibSetMidPoint();
|
||||
|
||||
|
||||
@@ -0,0 +1,255 @@
|
||||
/*
|
||||
* Copyright (C) EdgeTX
|
||||
*
|
||||
* Based on code named
|
||||
* opentx - https://github.com/opentx/opentx
|
||||
* th9x - http://code.google.com/p/th9x
|
||||
* er9x - http://code.google.com/p/er9x
|
||||
* gruvin9x - http://code.google.com/p/gruvin9x
|
||||
*
|
||||
* License GPLv2: http://www.gnu.org/licenses/gpl-2.0.html
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License version 2 as
|
||||
* published by the Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*/
|
||||
|
||||
#include "switch_driver.h"
|
||||
|
||||
#include "definitions.h"
|
||||
#include "hal/adc_driver.h"
|
||||
|
||||
#include "dataconstants.h"
|
||||
#include "opentx_helpers.h"
|
||||
#include "opentx.h"
|
||||
|
||||
// Board API
|
||||
void boardInitSwitches();
|
||||
|
||||
SwitchHwPos boardSwitchGetPosition(uint8_t cat, uint8_t idx);
|
||||
|
||||
const char* boardSwitchGetName(uint8_t cat, uint8_t idx);
|
||||
SwitchHwType boardSwitchGetType(uint8_t cat, uint8_t idx);
|
||||
|
||||
uint8_t boardGetMaxSwitches();
|
||||
uint8_t boardGetMaxFctSwitches();
|
||||
|
||||
swconfig_t boardSwitchGetDefaultConfig();
|
||||
|
||||
constexpr uint8_t _INVALID_ADC_CH = 0xFF;
|
||||
|
||||
static uint8_t _flex_switches[MAX_FLEX_SWITCHES];
|
||||
|
||||
void switchInit()
|
||||
{
|
||||
memset(_flex_switches, _INVALID_ADC_CH, sizeof(_flex_switches));
|
||||
boardInitSwitches();
|
||||
}
|
||||
|
||||
swconfig_t switchGetDefaultConfig() { return boardSwitchGetDefaultConfig(); }
|
||||
|
||||
uint8_t switchGetMaxSwitches()
|
||||
{
|
||||
return boardGetMaxSwitches() + MAX_FLEX_SWITCHES;
|
||||
}
|
||||
|
||||
uint8_t switchGetMaxFctSwitches() { return boardGetMaxFctSwitches(); }
|
||||
|
||||
uint32_t switchState(uint8_t pos_idx)
|
||||
{
|
||||
auto d = div(pos_idx, 3);
|
||||
auto idx = (uint8_t)d.quot;
|
||||
auto pos = (SwitchHwPos)d.rem;
|
||||
|
||||
return switchGetPosition(idx) == pos;
|
||||
}
|
||||
|
||||
SwitchHwPos switchGetPosition(uint8_t sw_idx)
|
||||
{
|
||||
auto idx = (int)sw_idx;
|
||||
auto max_switches = boardGetMaxSwitches();
|
||||
if (idx < max_switches) {
|
||||
return boardSwitchGetPosition(SWITCH_PHYSICAL, idx);
|
||||
}
|
||||
|
||||
idx -= max_switches;
|
||||
if (MAX_FLEX_SWITCHES > 0) {
|
||||
if (idx < MAX_FLEX_SWITCHES) {
|
||||
auto channel = _flex_switches[idx];
|
||||
SwitchHwPos ret = SWITCH_HW_UP;
|
||||
|
||||
if (channel != _INVALID_ADC_CH) {
|
||||
auto offset = adcGetInputOffset(ADC_INPUT_FLEX);
|
||||
uint16_t value = anaIn(channel + offset);
|
||||
if (value > 3 * 512) {
|
||||
ret = SWITCH_HW_DOWN;
|
||||
} else if (value >= 512) {
|
||||
ret = SWITCH_HW_MID;
|
||||
}
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
idx -= MAX_FLEX_SWITCHES;
|
||||
}
|
||||
|
||||
if (idx < boardGetMaxFctSwitches()) {
|
||||
return boardSwitchGetPosition(SWITCH_FUNCTION, idx);
|
||||
}
|
||||
|
||||
return SWITCH_HW_UP;
|
||||
}
|
||||
|
||||
static const char * const _flex_sw_canon_names[] = {
|
||||
"FL1", "FL2", "FL3", "FL4"
|
||||
};
|
||||
|
||||
static_assert(DIM(_flex_sw_canon_names) >= MAX_FLEX_SWITCHES,
|
||||
"Missing canonical name for flex switches");
|
||||
|
||||
const char* switchGetName(uint8_t sw_idx)
|
||||
{
|
||||
auto idx = (int)sw_idx;
|
||||
auto max_switches = boardGetMaxSwitches();
|
||||
if (idx < max_switches) {
|
||||
return boardSwitchGetName(SWITCH_PHYSICAL, idx);
|
||||
}
|
||||
|
||||
idx -= max_switches;
|
||||
if (MAX_FLEX_SWITCHES > 0) {
|
||||
if (idx < MAX_FLEX_SWITCHES) {
|
||||
return _flex_sw_canon_names[idx];
|
||||
}
|
||||
|
||||
idx -= MAX_FLEX_SWITCHES;
|
||||
}
|
||||
|
||||
if (idx < boardGetMaxFctSwitches()) {
|
||||
return boardSwitchGetName(SWITCH_FUNCTION, idx);
|
||||
}
|
||||
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
SwitchHwType switchGetHwType(uint8_t sw_idx)
|
||||
{
|
||||
auto idx = (int)sw_idx;
|
||||
auto max_switches = boardGetMaxSwitches();
|
||||
if (idx < max_switches) {
|
||||
return boardSwitchGetType(SWITCH_PHYSICAL, idx);
|
||||
}
|
||||
|
||||
idx -= max_switches;
|
||||
if (idx < MAX_FLEX_SWITCHES) {
|
||||
return SWITCH_HW_3POS;
|
||||
}
|
||||
|
||||
return SWITCH_HW_2POS;
|
||||
}
|
||||
|
||||
bool switchIsFlex(uint8_t idx)
|
||||
{
|
||||
auto max_switches = boardGetMaxSwitches();
|
||||
return idx >= max_switches && idx < max_switches + MAX_FLEX_SWITCHES;
|
||||
}
|
||||
|
||||
void switchConfigFlex_raw(uint8_t idx, int8_t channel)
|
||||
{
|
||||
if (!MAX_FLEX_SWITCHES || idx > MAX_FLEX_SWITCHES) return;
|
||||
if ((int)channel >= (int)adcGetMaxInputs(ADC_INPUT_FLEX)) return;
|
||||
|
||||
auto& sw_channel = _flex_switches[idx];
|
||||
if (channel < 0) {
|
||||
sw_channel = _INVALID_ADC_CH;
|
||||
} else if (POT_CONFIG((uint8_t)channel) == FLEX_SWITCH) {
|
||||
sw_channel = (uint8_t)channel;
|
||||
}
|
||||
}
|
||||
|
||||
uint8_t switchGetFlexChannel(uint8_t idx)
|
||||
{
|
||||
return _flex_switches[idx];
|
||||
}
|
||||
|
||||
void switchConfigFlex(uint8_t idx, int8_t channel)
|
||||
{
|
||||
auto max_switches = boardGetMaxSwitches();
|
||||
if (idx < max_switches) return;
|
||||
idx -= max_switches;
|
||||
switchConfigFlex_raw(idx, channel);
|
||||
}
|
||||
|
||||
int8_t switchGetFlexConfig_raw(uint8_t idx)
|
||||
{
|
||||
if (!MAX_FLEX_SWITCHES || idx > MAX_FLEX_SWITCHES) return _INVALID_ADC_CH;
|
||||
return (int8_t)_flex_switches[idx];
|
||||
}
|
||||
|
||||
int8_t switchGetFlexConfig(uint8_t idx)
|
||||
{
|
||||
auto max_switches = boardGetMaxSwitches();
|
||||
if (idx < max_switches) return _INVALID_ADC_CH;
|
||||
idx -= max_switches;
|
||||
return switchGetFlexConfig_raw(idx);
|
||||
}
|
||||
|
||||
bool switchIsFlexValid_raw(uint8_t sw_idx)
|
||||
{
|
||||
auto idx = (int)sw_idx;
|
||||
return MAX_FLEX_SWITCHES > 0 && idx < MAX_FLEX_SWITCHES &&
|
||||
_flex_switches[idx] != _INVALID_ADC_CH;
|
||||
}
|
||||
|
||||
bool switchIsFlexValid(uint8_t idx)
|
||||
{
|
||||
auto max_switches = boardGetMaxSwitches();
|
||||
if (idx < max_switches) return false;
|
||||
idx -= max_switches;
|
||||
return switchIsFlexValid_raw(idx);
|
||||
}
|
||||
|
||||
static bool is_flex_input_available(uint8_t flex_idx, uint8_t channel)
|
||||
{
|
||||
for (int i = 0; i < MAX_FLEX_SWITCHES; i++) {
|
||||
if (_flex_switches[i] == channel && (int)flex_idx != i)
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool switchIsFlexInputAvailable(uint8_t idx, uint8_t channel)
|
||||
{
|
||||
if (MAX_FLEX_SWITCHES == 0) return false;
|
||||
if (POT_CONFIG(channel) != FLEX_SWITCH) return false;
|
||||
|
||||
auto phy_switches = boardGetMaxSwitches();
|
||||
if (idx < phy_switches) return false;
|
||||
|
||||
return is_flex_input_available(idx - phy_switches, channel);
|
||||
}
|
||||
|
||||
static void invalidate_flex_config(uint8_t flex_idx)
|
||||
{
|
||||
auto sw_idx = flex_idx + boardGetMaxSwitches();
|
||||
swconfig_t mask = (swconfig_t)SWITCH_CONFIG_MASK(sw_idx);
|
||||
g_eeGeneral.switchConfig &= ~mask;
|
||||
|
||||
_flex_switches[flex_idx] = _INVALID_ADC_CH;
|
||||
}
|
||||
|
||||
void switchFixFlexConfig()
|
||||
{
|
||||
for (int i = 0; i < MAX_FLEX_SWITCHES; i++) {
|
||||
auto channel = _flex_switches[i];
|
||||
if (channel != _INVALID_ADC_CH && POT_CONFIG(channel) != FLEX_SWITCH) {
|
||||
invalidate_flex_config(i);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -36,6 +36,11 @@ enum SwitchHwPos {
|
||||
SWITCH_HW_DOWN,
|
||||
};
|
||||
|
||||
enum SwitchCategory {
|
||||
SWITCH_PHYSICAL=0,
|
||||
SWITCH_FUNCTION,
|
||||
};
|
||||
|
||||
#define SWITCH_HW_INVERTED 1
|
||||
|
||||
// init hardware for switches
|
||||
@@ -44,7 +49,7 @@ void switchInit();
|
||||
// returns the factory configuration for all switches
|
||||
swconfig_t switchGetDefaultConfig();
|
||||
|
||||
// returns the maximum number of regular switches supported in hardware
|
||||
// returns the maximum number of regular switches supported in hardware (customizable switches NOT included)
|
||||
uint8_t switchGetMaxSwitches();
|
||||
|
||||
// returns the number of configured switches
|
||||
@@ -63,6 +68,21 @@ struct switch_display_pos_t {
|
||||
|
||||
switch_display_pos_t switchGetDisplayPosition(uint8_t idx);
|
||||
|
||||
// Configure Flex switch (using unified switch index)
|
||||
void switchConfigFlex(uint8_t idx, int8_t channel);
|
||||
int8_t switchGetFlexConfig(uint8_t idx);
|
||||
|
||||
bool switchIsFlex(uint8_t idx);
|
||||
bool switchIsFlexValid(uint8_t idx);
|
||||
bool switchIsFlexInputAvailable(uint8_t idx, uint8_t channel);
|
||||
|
||||
void switchFixFlexConfig();
|
||||
|
||||
// "Raw" variants indexing only flex switches (used for storage)
|
||||
void switchConfigFlex_raw(uint8_t idx, int8_t channel);
|
||||
int8_t switchGetFlexConfig_raw(uint8_t idx);
|
||||
bool switchIsFlexValid_raw(uint8_t idx);
|
||||
|
||||
// The functions bellow support regular as well as customizable switches.
|
||||
//
|
||||
// Regular switches are indexed from `0` to `switchGetMaxSwitches() - 1`
|
||||
|
||||
Reference in New Issue
Block a user