Skip to content

Commit b52e771

Browse files
refactor(radio): unified battery and RTC voltage (#6065)
1 parent f237e9d commit b52e771

File tree

13 files changed

+185
-154
lines changed

13 files changed

+185
-154
lines changed

radio/src/boards/generic_stm32/CMakeLists.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,7 @@ set(BOARD_LIB_SRC
3232
boards/generic_stm32/switches.cpp
3333
boards/generic_stm32/bor_level.cpp
3434
boards/generic_stm32/rgb_leds.cpp
35+
boards/generic_stm32/battery_voltage.cpp
3536
)
3637

3738
add_library(minimal_board_lib OBJECT EXCLUDE_FROM_ALL ${MINIMAL_BOARD_LIB_SRC})
Lines changed: 113 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,113 @@
1+
/*
2+
* Copyright (C) EdgeTx
3+
*
4+
* Based on code named
5+
* opentx - https://github.com/opentx/opentx
6+
* th9x - http://code.google.com/p/th9x
7+
* er9x - http://code.google.com/p/er9x
8+
* gruvin9x - http://code.google.com/p/gruvin9x
9+
*
10+
* License GPLv2: http://www.gnu.org/licenses/gpl-2.0.html
11+
*
12+
* This program is free software; you can redistribute it and/or modify
13+
* it under the terms of the GNU General Public License version 2 as
14+
* published by the Free Software Foundation.
15+
*
16+
* This program is distributed in the hope that it will be useful,
17+
* but WITHOUT ANY WARRANTY; without even the implied warranty of
18+
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
19+
* GNU General Public License for more details.
20+
*/
21+
22+
#include "hal/adc_driver.h"
23+
#include "board.h"
24+
25+
#include "edgetx.h"
26+
27+
#if defined(STM32F407xx) || defined(STM32F205xx)
28+
// internal bridge measure Vbat/2
29+
#define VBAT_RTC_DIV 2
30+
#else
31+
// internal bridge measure Vbat/4
32+
#define VBAT_RTC_DIV 4
33+
#endif
34+
35+
#define __weak __attribute__((weak))
36+
37+
static uint16_t scale_rtc_voltage(uint16_t v)
38+
{
39+
return (v * ADC_VREF_PREC2 * VBAT_RTC_DIV) / ADC_MAX_FILTERED;
40+
}
41+
42+
uint16_t getRTCBatteryVoltage()
43+
{
44+
if (adcGetMaxInputs(ADC_INPUT_RTC_BAT) < 1) return 0;
45+
46+
uint16_t filtered = anaIn(adcGetInputOffset(ADC_INPUT_RTC_BAT));
47+
return scale_rtc_voltage(filtered);
48+
}
49+
50+
#if (defined(VBAT_DIV_R1) && defined(VBAT_DIV_R2)) || \
51+
defined(BATTERY_DIVIDER) || defined(BATT_SCALE)
52+
53+
#define VREF_FLOAT ((float)ADC_VREF_PREC2 / 100.0)
54+
55+
#if defined(VBAT_DIV_R1) && defined(VBAT_DIV_R2)
56+
// defined as a voltage divider
57+
// Vbat --- R1 --+-- ADC
58+
// |
59+
// +-- R2 --- GND
60+
//
61+
#define VOLTAGE_DIVIDER(r1, r2) (((float)r1 + (float)r2) / (float)r2)
62+
63+
#define VBAT_DIVIDER VOLTAGE_DIVIDER(VBAT_DIV_R1, VBAT_DIV_R2)
64+
65+
// Scale of Vbat calibration setting
66+
#define VBAT_CAL 1000.0
67+
68+
#elif defined(BATT_SCALE)
69+
// defined as a simple ratio (old style: mostly taranis targets)
70+
#define VBAT_DIVIDER \
71+
(((float)BATT_SCALE * (float)ADC_MAX_FILTERED) / \
72+
((float)BATTERY_DIVIDER * VREF_FLOAT))
73+
74+
// Scale of Vbat calibration setting
75+
#define VBAT_CAL 128.0
76+
77+
#elif defined(BATTERY_DIVIDER)
78+
// defined a a simple factor (old style: mostly color targets)
79+
#define VBAT_DIVIDER \
80+
(((float)ADC_MAX_FILTERED * 10.0) / ((float)BATTERY_DIVIDER * VREF_FLOAT))
81+
82+
// Scale of Vbat calibration setting
83+
#define VBAT_CAL 1000.0
84+
85+
#endif
86+
87+
// Fixed offset in hundredth of volt (*10 mV)
88+
#if defined(VBAT_MOSFET_DROP)
89+
#define VBAT_OFFSET VBAT_MOSFET_DROP
90+
#elif defined(VOLTAGE_DROP)
91+
#define VBAT_OFFSET VOLTAGE_DROP
92+
#else
93+
#define VBAT_OFFSET 0
94+
#endif
95+
96+
__weak uint16_t getBatteryVoltage()
97+
{
98+
// using filtered ADC value on purpose
99+
if (adcGetMaxInputs(ADC_INPUT_VBAT) < 1) return 0;
100+
101+
float vbat = anaIn(adcGetInputOffset(ADC_INPUT_VBAT));
102+
103+
// apply calibration
104+
vbat = vbat * (VBAT_CAL + g_eeGeneral.txVoltageCalibration) / VBAT_CAL;
105+
106+
// apply voltage divider and ADC scale
107+
vbat *= VBAT_DIVIDER * (VREF_FLOAT / (float)ADC_MAX_FILTERED);
108+
109+
// return hundredth of volt (*10 mV)
110+
return (uint16_t)(100.0 * vbat) + VBAT_OFFSET;
111+
}
112+
113+
#endif

radio/src/hal/adc_driver.cpp

Lines changed: 2 additions & 54 deletions
Original file line numberDiff line numberDiff line change
@@ -29,10 +29,6 @@ const etx_hal_adc_inputs_t* _hal_adc_inputs = nullptr;
2929

3030
static uint16_t adcValues[MAX_ANALOG_INPUTS] __DMA_NO_CACHE;
3131

32-
#if defined(CSD203_SENSOR)
33-
extern uint16_t getCSD203BatteryVoltage(void);
34-
#endif
35-
3632
bool adcInit(const etx_hal_adc_driver_t* driver)
3733
{
3834
// Init buffer, provides non random values before mixer task starts
@@ -197,7 +193,7 @@ void adcCalibSetMinMax()
197193
}
198194

199195
xpot.stepsCount = ++count;
200-
writeXPotCalib(i, xpot.steps, count);
196+
writeXPotCalib(i, xpot.steps, count);
201197
}
202198
}
203199
}
@@ -234,19 +230,6 @@ void adcCalibStore()
234230
storageDirty(EE_GENERAL);
235231
}
236232

237-
uint16_t getRTCBatteryVoltage()
238-
{
239-
// anaIn() outputs value divided by (1 << ANALOG_SCALE)
240-
if (adcGetMaxInputs(ADC_INPUT_RTC_BAT) < 1) return 0;
241-
#if defined(STM32F413xx)
242-
return (anaIn(adcGetInputOffset(ADC_INPUT_RTC_BAT)) * ADC_VREF_PREC2) /
243-
(1024 >> ANALOG_SCALE);
244-
#else
245-
return (anaIn(adcGetInputOffset(ADC_INPUT_RTC_BAT)) * ADC_VREF_PREC2) /
246-
(2048 >> ANALOG_SCALE);
247-
#endif
248-
}
249-
250233
uint16_t getAnalogValue(uint8_t index)
251234
{
252235
if (index >= MAX_ANALOG_INPUTS) return 0;
@@ -307,41 +290,6 @@ JitterMeter<uint16_t> avgJitter[MAX_ANALOG_INPUTS];
307290
tmr10ms_t jitterResetTime = 0;
308291
#endif
309292

310-
uint16_t getBatteryVoltage()
311-
{
312-
#if defined(CSD203_SENSOR) && !defined(SIMU)
313-
return getCSD203BatteryVoltage() / 10;
314-
#else
315-
// using filtered ADC value on purpose
316-
if (adcGetMaxInputs(ADC_INPUT_VBAT) < 1) return 0;
317-
int32_t instant_vbat = anaIn(adcGetInputOffset(ADC_INPUT_VBAT));
318-
319-
// TODO: remove BATT_SCALE / BATTERY_DIVIDER defines
320-
#if defined(VBAT_MOSFET_DROP)
321-
// 1000 is used as multiplier for both numerator and denominator to allow to stay in integer domain
322-
return (uint16_t)((instant_vbat * ADC_VREF_PREC2 * ((((1000 + g_eeGeneral.txVoltageCalibration)) * (VBAT_DIV_R2 + VBAT_DIV_R1)) / VBAT_DIV_R1)) / (2*RESX*1000)) + VBAT_MOSFET_DROP;
323-
#elif defined(BATT_SCALE)
324-
instant_vbat =
325-
(instant_vbat * BATT_SCALE * (128 + g_eeGeneral.txVoltageCalibration)) /
326-
BATTERY_DIVIDER;
327-
// add voltage drop because of the diode TODO check if this is needed, but
328-
// removal will break existing calibrations!
329-
instant_vbat += VOLTAGE_DROP;
330-
return (uint16_t)instant_vbat;
331-
#elif defined(VOLTAGE_DROP)
332-
instant_vbat = ((instant_vbat * (1000 + g_eeGeneral.txVoltageCalibration)) /
333-
BATTERY_DIVIDER);
334-
// add voltage drop because of the diode
335-
// removal will break existing calibrations!
336-
instant_vbat += VOLTAGE_DROP;
337-
return (uint16_t)instant_vbat;
338-
#else
339-
return (uint16_t)((instant_vbat * (1000 + g_eeGeneral.txVoltageCalibration)) /
340-
BATTERY_DIVIDER);
341-
#endif
342-
#endif
343-
}
344-
345293
static uint32_t apply_low_pass_filter(uint32_t v, uint32_t v_prev,
346294
bool is_main_input)
347295
{
@@ -470,7 +418,7 @@ void getADC()
470418
#endif
471419

472420
DEBUG_TIMER_START(debugTimerAdcRead);
473-
if (!adcRead()) TRACE("adcRead failed");
421+
if (!adcRead()) { TRACE("adcRead failed"); }
474422
DEBUG_TIMER_STOP(debugTimerAdcRead);
475423

476424
for (uint8_t x = 0; x < max_analogs; x++) {

radio/src/hal/adc_driver.h

Lines changed: 9 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,8 @@
3535
#define ANALOG_SCALE 1
3636
#define JITTER_ALPHA (1<<JITTER_FILTER_STRENGTH)
3737

38+
#define ADC_MAX_FILTERED (ADC_MAX_VALUE >> ANALOG_SCALE)
39+
3840
enum {
3941
ADC_INPUT_MAIN=0, // gimbals / wheel + throttle
4042
ADC_INPUT_FLEX,
@@ -85,11 +87,9 @@ struct etx_hal_adc_driver_t {
8587
bool adcInit(const etx_hal_adc_driver_t* driver);
8688
// void adcDeInit();
8789

88-
bool adcRead();
89-
uint16_t getBatteryVoltage();
90-
uint16_t getRTCBatteryVoltage();
91-
uint16_t getAnalogValue(uint8_t index);
92-
void setAnalogValue(uint8_t index, uint16_t value);
90+
bool adcRead();
91+
uint16_t getAnalogValue(uint8_t index);
92+
void setAnalogValue(uint8_t index, uint16_t value);
9393
uint16_t* getAnalogValues();
9494

9595
// Run calibration steps:
@@ -114,10 +114,9 @@ extern JitterMeter<uint16_t> avgJitter[MAX_ANALOG_INPUTS];
114114
void getADC();
115115
uint16_t anaIn(uint8_t chan);
116116
uint32_t anaIn_diag(uint8_t chan);
117-
uint16_t getBatteryVoltage();
118117

119118
// Warning:
120-
// STM32 uses a 25K+25K voltage divider bridge to measure the battery voltage
119+
// STM32 uses a voltage divider bridge to measure the battery voltage
121120
// Measuring VBAT puts considerable drain (22 µA) on the battery instead of
122121
// normal drain (~10 nA)
123122
void enableVBatBridge();
@@ -143,10 +142,6 @@ const char* adcGetInputShortLabel(uint8_t type, uint8_t idx);
143142
void adcSetInputMask(uint32_t mask);
144143
uint32_t adcGetInputMask();
145144

146-
// To be implemented by the target driver
147-
// int8_t adcGetVRTC();
148-
// int8_t adcGetVBAT();
149-
// const char* adcGetStickName(uint8_t idx);
150-
// const char* adcGetPotName(uint8_t idx);
151-
// uint8_t adcGetMaxSticks();
152-
// uint8_t adcGetMaxPots();
145+
// these 2 must be implemented as part of board drivers
146+
uint16_t getBatteryVoltage();
147+
uint16_t getRTCBatteryVoltage();

radio/src/targets/common/arm/stm32/stm32_adc.cpp

Lines changed: 13 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -88,6 +88,16 @@ uint32_t stm32_hal_get_inputs_mask()
8888
return _adc_input_inhibt_mask;
8989
}
9090

91+
#if defined(STM32H7)
92+
#if defined(ADC3)
93+
#define VBAT_ADC ADC3
94+
#else
95+
#define VBAT_ADC ADC2
96+
#endif
97+
#else
98+
#define VBAT_ADC ADC1
99+
#endif
100+
91101
// STM32 uses a 25K+25K voltage divider bridge to measure the battery voltage
92102
// Measuring VBAT puts considerable drain (22 µA) on the battery instead of
93103
// normal drain (~10 nA)
@@ -96,7 +106,7 @@ void enableVBatBridge()
96106
if (adcGetMaxInputs(ADC_INPUT_RTC_BAT) < 1) return;
97107

98108
// Set internal measurement path for vbat sensor
99-
LL_ADC_SetCommonPathInternalCh(__LL_ADC_COMMON_INSTANCE(ADC1), LL_ADC_PATH_INTERNAL_VBAT);
109+
LL_ADC_SetCommonPathInternalCh(__LL_ADC_COMMON_INSTANCE(VBAT_ADC), LL_ADC_PATH_INTERNAL_VBAT);
100110

101111
auto channel = adcGetInputOffset(ADC_INPUT_RTC_BAT);
102112
_adc_inhibit_mask &= ~(1 << channel);
@@ -110,13 +120,13 @@ void disableVBatBridge()
110120
_adc_inhibit_mask |= (1 << channel);
111121

112122
// Set internal measurement path to none
113-
LL_ADC_SetCommonPathInternalCh(__LL_ADC_COMMON_INSTANCE(ADC1), LL_ADC_PATH_INTERNAL_NONE);
123+
LL_ADC_SetCommonPathInternalCh(__LL_ADC_COMMON_INSTANCE(VBAT_ADC), LL_ADC_PATH_INTERNAL_NONE);
114124
}
115125

116126
bool isVBatBridgeEnabled()
117127
{
118128
// && !(_adc_inhibit_mask & (1 << channel));
119-
return LL_ADC_GetCommonPathInternalCh(__LL_ADC_COMMON_INSTANCE(ADC1)) == LL_ADC_PATH_INTERNAL_VBAT;
129+
return LL_ADC_GetCommonPathInternalCh(__LL_ADC_COMMON_INSTANCE(VBAT_ADC)) == LL_ADC_PATH_INTERNAL_VBAT;
120130
}
121131

122132
static void adc_enable_clock(ADC_TypeDef* ADCx)

radio/src/targets/horus/csd203_sensor.cpp

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -382,11 +382,16 @@ void initCSD203(void)
382382
}
383383
}
384384

385-
uint16_t getCSD203BatteryVoltage(void)
385+
static uint16_t getCSD203BatteryVoltage(void)
386386
{ // 1000=1000mV
387387
return csd203extvbus;
388388
}
389389

390+
uint16_t getBatteryVoltage()
391+
{
392+
return getCSD203BatteryVoltage() / 10;
393+
}
394+
390395
void readCSD203(void)
391396
{ // 5ms
392397
static uint16_t GetSenSorStep = 0;

radio/src/targets/horus/hal.h

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -397,7 +397,7 @@
397397
#define ADC_DMA_STREAM_IRQ DMA2_Stream0_IRQn
398398
#define ADC_DMA_STREAM_IRQHandler DMA2_Stream0_IRQHandler
399399
#define ADC_SAMPTIME LL_ADC_SAMPLINGTIME_56CYCLES
400-
#define ADC_VREF_PREC2 600
400+
#define ADC_VREF_PREC2 300
401401
#elif defined(RADIO_V16)
402402
#define ADC_GPIO_PIN_STICK_LH LL_GPIO_PIN_0 // PA.00
403403
#define ADC_GPIO_PIN_STICK_LV LL_GPIO_PIN_1 // PA.01
@@ -446,7 +446,7 @@
446446
#define ADC_DMA_STREAM LL_DMA_STREAM_0
447447
#define ADC_DMA_STREAM_IRQ DMA2_Stream0_IRQn
448448
#define ADC_DMA_STREAM_IRQHandler DMA2_Stream0_IRQHandler
449-
#define ADC_VREF_PREC2 660
449+
#define ADC_VREF_PREC2 330
450450
#elif defined(PCBX10)
451451
#if defined(RADIO_T15)
452452
#define ADC_GPIO_PIN_STICK_LH LL_GPIO_PIN_1 // PA.01
@@ -546,11 +546,11 @@
546546

547547
// VBat divider is /4 on F42x and F43x devices
548548
#if defined(RADIO_TX16S) || defined(RADIO_T15) || defined(RADIO_F16) || defined(RADIO_V16)
549-
#define ADC_VREF_PREC2 660
549+
#define ADC_VREF_PREC2 330
550550
#elif defined(RADIO_T16) || defined(RADIO_T18)
551-
#define ADC_VREF_PREC2 600
551+
#define ADC_VREF_PREC2 300
552552
#else
553-
#define ADC_VREF_PREC2 500
553+
#define ADC_VREF_PREC2 250
554554
#endif
555555
#endif
556556

radio/src/targets/nv14/hal.h

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -161,7 +161,7 @@
161161
#define ADC_EXT_DMA_STREAM_IRQ DMA2_Stream0_IRQn
162162
#define ADC_EXT_DMA_STREAM_IRQHandler DMA2_Stream0_IRQHandler
163163
#define ADC_EXT_SAMPTIME LL_ADC_SAMPLINGTIME_28CYCLES
164-
#define ADC_VREF_PREC2 660
164+
#define ADC_VREF_PREC2 330
165165

166166
#define ADC_DIRECTION \
167167
{ 0 /*STICK1*/, 0 /*STICK2*/, 0 /*STICK3*/, 0 /*STICK4*/, \

radio/src/targets/pl18/hal.h

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -162,7 +162,7 @@
162162
#define ADC_EXT_DMA_STREAM_IRQ DMA2_Stream0_IRQn
163163
#define ADC_EXT_DMA_STREAM_IRQHandler DMA2_Stream0_IRQHandler
164164
#define ADC_EXT_SAMPTIME LL_ADC_SAMPLINGTIME_28CYCLES
165-
#define ADC_VREF_PREC2 660
165+
#define ADC_VREF_PREC2 330
166166

167167
#define ADC_DIRECTION \
168168
{ 0 /*STICK1*/, 0 /*STICK2*/, 0 /*STICK3*/, 0 /*STICK4*/, \
@@ -223,7 +223,7 @@
223223
#define ADC_DMA_STREAM_IRQ DMA2_Stream4_IRQn
224224
#define ADC_DMA_STREAM_IRQHandler DMA2_Stream4_IRQHandler
225225

226-
#define ADC_VREF_PREC2 660
226+
#define ADC_VREF_PREC2 330
227227

228228
#define ADC_DIRECTION { \
229229
0,0, /* gimbals */ \
@@ -454,7 +454,7 @@
454454
#define ADC_EXT_DMA_STREAM_IRQHandler DMA2_Stream0_IRQHandler
455455
#define ADC_EXT_SAMPTIME LL_ADC_SAMPLINGTIME_28CYCLES
456456

457-
#define ADC_VREF_PREC2 660
457+
#define ADC_VREF_PREC2 330
458458

459459
#if defined(RADIO_PL18EV)
460460
#define ADC_DIRECTION { \

0 commit comments

Comments
 (0)