FreeTRX/MDUV380_firmware/application/source/interfaces/adc.c
2026-07-06 08:45:33 +02:00

201 lines
7.9 KiB
C

/*
* Copyright (C) 2019 Kai Ludwig, DG4KLU
* Copyright (C) 2019-2025 Roger Clark, VK3KYY / G4KYF
*
*
* Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions
* are met:
*
* 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer.
*
* 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer
* in the documentation and/or other materials provided with the distribution.
*
* 3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote products derived
* from this software without specific prior written permission.
*
* 4. Use of this source code or binary releases for commercial purposes is strictly forbidden. This includes, without limitation,
* incorporation in a commercial product or incorporation into a product or project which allows commercial use.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
* HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
* ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE
* USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
*/
#include "interfaces/adc.h"
#include "functions/settings.h"
#include "interfaces/batteryAndPowerManagement.h"
#include "functions/ticks.h"
const int TEMPERATURE_DECIMAL_RESOLUTION = 1000000;
const int CUTOFF_VOLTAGE_UPPER_HYST = 64;
const int CUTOFF_VOLTAGE_LOWER_HYST = 62;
const int BATTERY_MAX_VOLTAGE = 82;
const int POWEROFF_VOLTAGE_THRESHOLD = 55;
#if defined(PLATFORM_MDUV380)
#define BATTERY_ADC_COEFF 40.3f
#else
#define BATTERY_ADC_COEFF 37.5f
#endif
//lookup table to convert linear Voltage values between 1 and 255 to dBs relative to 1 with a resolution of 0.2dB. Values in tabble are dbs * 5 to use all of the available byte range.
//calculated using dBs= (20 * Log10(index)) * 5
const uint8_t PointTwodBs[] =
{
0, 0, 30, 48, 60, 70, 78, 85, 90, 95, 100, 104, 108, 111, 115, 118,
120, 123, 126, 128, 130, 132, 134, 136, 138, 140, 141, 143, 145, 146, 148, 149,
151, 152, 153, 154, 156, 157, 158, 159, 160, 161, 162, 163, 164, 165, 166, 167,
168, 169, 170, 171, 172, 172, 173, 174, 175, 176, 176, 177, 178, 179, 179, 180,
181, 181, 182, 183, 183, 184, 185, 185, 186, 186, 187, 188, 188, 189, 189, 190,
190, 191, 191, 192, 192, 193, 193, 194, 194, 195, 195, 196, 196, 197, 197, 198,
198, 199, 199, 200, 200, 200, 201, 201, 202, 202, 203, 203, 203, 204, 204, 205,
205, 205, 206, 206, 206, 207, 207, 208, 208, 208, 209, 209, 209, 210, 210, 210,
211, 211, 211, 212, 212, 212, 213, 213, 213, 214, 214, 214, 215, 215, 215, 216,
216, 216, 216, 217, 217, 217, 218, 218, 218, 218, 219, 219, 219, 220, 220, 220,
220, 221, 221, 221, 221, 222, 222, 222, 223, 223, 223, 223, 224, 224, 224, 224,
225, 225, 225, 225, 226, 226, 226, 226, 226, 227, 227, 227, 227, 228, 228, 228,
228, 229, 229, 229, 229, 229, 230, 230, 230, 230, 231, 231, 231, 231, 231, 232,
232, 232, 232, 232, 233, 233, 233, 233, 233, 234, 234, 234, 234, 234, 235, 235,
235, 235, 235, 236, 236, 236, 236, 236, 237, 237, 237, 237, 237, 237, 238, 238,
238, 238, 238, 239, 239, 239, 239, 239, 239, 240, 240, 240, 240, 240, 240, 241
};
volatile uint16_t adcVal[NUM_ADC_CHANNELS];
static const int AVERAGE_BATTERY_VOLTAGE_SAMPLE_WINDOW = 1000.0f;
static int16_t averagedVolume = 0; // value is (* 16) the real value.
void adcStartDMA(void)
{
HAL_ADC_Start_DMA(&hadc1, (uint32_t*)&adcVal, NUM_ADC_CHANNELS);
HAL_TIM_Base_Start_IT(&htim3);
}
//void HAL_ADC_ConvHalfCpltCallback(ADC_HandleTypeDef* hadc){} // Don't need to use the half full callback at the moment
void HAL_ADC_ConvCpltCallback(ADC_HandleTypeDef* hadc)
{
potLevel = adcVal[0];
batteryVoltage = ((int)adcVal[1] / BATTERY_ADC_COEFF) + ((nonVolatileSettings.batteryCalibration & 0x0F) - 5);
micLevel = adcVal[2];
temperatureLevel = adcVal[3];
// Handle switching the power off, using the rotary control.
if (batteryVoltage > POWEROFF_VOLTAGE_THRESHOLD)
{
lastValidBatteryVoltage = batteryVoltage;
}
if (ticksGetMillis() < (BATTERY_VOLTAGE_STABILISATION_TIME + resumeTicks))
{
averageBatteryVoltage = batteryVoltage;
}
else
{
averageBatteryVoltage = (averageBatteryVoltage * (AVERAGE_BATTERY_VOLTAGE_SAMPLE_WINDOW - 1) + batteryVoltage) / AVERAGE_BATTERY_VOLTAGE_SAMPLE_WINDOW;
}
}
int adcGetBatteryVoltage(void)
{
return batteryVoltage; //doesn't need averaging so just use the most recent conversion
}
int adcGetVOX(void)
{
return ((micLevel > 255) ? 255 : PointTwodBs[micLevel]);
}
int getTemperature(void)
{
const int tV25 = 943; // ADC Value at 25 degrees from data sheet (0.76V with 12 Bit ADC ref=3V3)
const int tslope = 31; //Slope from datasheet ADC Counts *10
return (((temperatureLevel - tV25) * 100) / tslope) + 250 + (nonVolatileSettings.temperatureCalibration * 5);
}
#if defined(PLATFORM_RT84_DM1701)
#define MIN_VOL_ADC_LOW 34 // 29 + 5
#define MAX_VOL_ADC_VAL 2070 // 2075 - 5
#define VOL_POT_ADC_MID_POINT 318
#define MIN_VOL_LOW_TO_HIGH_OFFSET 5
#elif defined(PLATFORM_VARIANT_UV380_PLUS_10W)
#define MIN_VOL_ADC_LOW 24 // 19 + 5
#define MAX_VOL_ADC_VAL 2049 // 2054 - 5
#define VOL_POT_ADC_MID_POINT 308
#define MIN_VOL_LOW_TO_HIGH_OFFSET 5
#elif defined(PLATFORM_MD2017)
#define MIN_VOL_ADC_LOW 37 // 32 + 5
#define MAX_VOL_ADC_VAL 2043 // 2048 - 5
#define VOL_POT_ADC_MID_POINT 321
#define MIN_VOL_LOW_TO_HIGH_OFFSET 5
#else
// COMMENTED: MD-UV380 values
//#define MIN_VOL_ADC_LOW 25 // 22 + 3
//#define MAX_VOL_ADC_VAL 2065 // 2070 - 5
//#define VOL_POT_ADC_MID_POINT 309
//#define MIN_VOL_LOW_TO_HIGH_OFFSET 5
// RT3S
#define MIN_VOL_ADC_LOW 30 // 23 + 7 // RT3S ADC reading is not really stable at lowest level
#define MAX_VOL_ADC_VAL 2015 // 2020 - 5
#define VOL_POT_ADC_MID_POINT 317
#define MIN_VOL_LOW_TO_HIGH_OFFSET 8
#endif
#define MIN_VOL_ADC_HIGH (MIN_VOL_ADC_LOW + MIN_VOL_LOW_TO_HIGH_OFFSET)
#define VOL_LOW_SIDE_DIV ((VOL_POT_ADC_MID_POINT - MIN_VOL_ADC_HIGH) / 31)
#define VOL_HIGH_SIDE_DIV ((MAX_VOL_ADC_VAL - VOL_POT_ADC_MID_POINT) / 31)
static int8_t getVolumeControlRaw(void)
{
// volume control is adc[0]. Max value seems to be:
// - RT3S: 2020, min value seems to be 23
// - MD-UV380: 2070, min value seems to be 22
// - MD-UV380Plus 10W: 2054, min value seems to be 19
// - DM-1701: 2075, min value seems to be 29
// - MD-2017: 2048, min value seems to be 32
// Pot is Log law which needs converting back to linear law
// Log Law is approximated by two straight lines with breakpoint at 300
int vol = potLevel;
int pos;
static bool minvol;
if (vol < VOL_POT_ADC_MID_POINT)
{
pos = (vol - MIN_VOL_ADC_HIGH) / VOL_LOW_SIDE_DIV; // convert to 0-31
}
else
{
pos = 31 + ((vol - VOL_POT_ADC_MID_POINT) / VOL_HIGH_SIDE_DIV); // convert to 31-62
}
if (minvol ? (vol < MIN_VOL_ADC_HIGH) : (vol < MIN_VOL_ADC_LOW))
{
minvol = true;
return -99; // -99 = volume min (muted)
}
minvol = false;
return (int8_t)CLAMP(pos - 31, -31, 31);
}
int8_t getVolumeControl(void)
{
int8_t v = getVolumeControlRaw();
if ((v == -99) || ((v != -99) && (averagedVolume == (-99 << 4))))
{
averagedVolume = (v << 4);
return v;
}
averagedVolume = (v + averagedVolume - ((averagedVolume - 8) >> 4));
return (int8_t)CLAMP((averagedVolume >> 4), -31, 31);
}