mirror of
https://github.com/DarkFlippers/unleashed-firmware.git
synced 2024-12-02 12:13:16 +03:00
282 lines
10 KiB
C
282 lines
10 KiB
C
#include <furi_hal_adc.h>
|
|
#include <furi_hal_bus.h>
|
|
#include <furi_hal_cortex.h>
|
|
#include <furi_hal_power.h>
|
|
|
|
#include <furi.h>
|
|
|
|
#include <stm32wbxx_ll_adc.h>
|
|
#include <stm32wbxx_ll_system.h>
|
|
|
|
struct FuriHalAdcHandle {
|
|
ADC_TypeDef* adc;
|
|
FuriMutex* mutex;
|
|
uint32_t full_scale;
|
|
};
|
|
|
|
static const uint32_t furi_hal_adc_clock[] = {
|
|
[FuriHalAdcClockSync16] = LL_ADC_CLOCK_SYNC_PCLK_DIV4,
|
|
[FuriHalAdcClockSync32] = LL_ADC_CLOCK_SYNC_PCLK_DIV2,
|
|
[FuriHalAdcClockSync64] = LL_ADC_CLOCK_SYNC_PCLK_DIV1,
|
|
};
|
|
|
|
static const uint8_t furi_hal_adc_clock_div[] = {
|
|
[FuriHalAdcClockSync16] = 4,
|
|
[FuriHalAdcClockSync32] = 2,
|
|
[FuriHalAdcClockSync64] = 1,
|
|
};
|
|
|
|
static const uint32_t furi_hal_adc_oversample_ratio[] = {
|
|
[FuriHalAdcOversample2] = LL_ADC_OVS_RATIO_2,
|
|
[FuriHalAdcOversample4] = LL_ADC_OVS_RATIO_4,
|
|
[FuriHalAdcOversample8] = LL_ADC_OVS_RATIO_8,
|
|
[FuriHalAdcOversample16] = LL_ADC_OVS_RATIO_16,
|
|
[FuriHalAdcOversample32] = LL_ADC_OVS_RATIO_32,
|
|
[FuriHalAdcOversample64] = LL_ADC_OVS_RATIO_64,
|
|
[FuriHalAdcOversample128] = LL_ADC_OVS_RATIO_128,
|
|
[FuriHalAdcOversample256] = LL_ADC_OVS_RATIO_256,
|
|
};
|
|
|
|
static const uint32_t furi_hal_adc_oversample_shift[] = {
|
|
[FuriHalAdcOversample2] = LL_ADC_OVS_SHIFT_RIGHT_1,
|
|
[FuriHalAdcOversample4] = LL_ADC_OVS_SHIFT_RIGHT_2,
|
|
[FuriHalAdcOversample8] = LL_ADC_OVS_SHIFT_RIGHT_3,
|
|
[FuriHalAdcOversample16] = LL_ADC_OVS_SHIFT_RIGHT_4,
|
|
[FuriHalAdcOversample32] = LL_ADC_OVS_SHIFT_RIGHT_5,
|
|
[FuriHalAdcOversample64] = LL_ADC_OVS_SHIFT_RIGHT_6,
|
|
[FuriHalAdcOversample128] = LL_ADC_OVS_SHIFT_RIGHT_7,
|
|
[FuriHalAdcOversample256] = LL_ADC_OVS_SHIFT_RIGHT_8,
|
|
};
|
|
|
|
static const uint32_t furi_hal_adc_sampling_time[] = {
|
|
[FuriHalAdcSamplingtime2_5] = LL_ADC_SAMPLINGTIME_2CYCLES_5,
|
|
[FuriHalAdcSamplingtime6_5] = LL_ADC_SAMPLINGTIME_6CYCLES_5,
|
|
[FuriHalAdcSamplingtime12_5] = LL_ADC_SAMPLINGTIME_12CYCLES_5,
|
|
[FuriHalAdcSamplingtime24_5] = LL_ADC_SAMPLINGTIME_24CYCLES_5,
|
|
[FuriHalAdcSamplingtime47_5] = LL_ADC_SAMPLINGTIME_47CYCLES_5,
|
|
[FuriHalAdcSamplingtime92_5] = LL_ADC_SAMPLINGTIME_92CYCLES_5,
|
|
[FuriHalAdcSamplingtime247_5] = LL_ADC_SAMPLINGTIME_247CYCLES_5,
|
|
[FuriHalAdcSamplingtime640_5] = LL_ADC_SAMPLINGTIME_640CYCLES_5,
|
|
};
|
|
|
|
static const uint32_t furi_hal_adc_channel_map[] = {
|
|
[FuriHalAdcChannel0] = LL_ADC_CHANNEL_0,
|
|
[FuriHalAdcChannel1] = LL_ADC_CHANNEL_1,
|
|
[FuriHalAdcChannel2] = LL_ADC_CHANNEL_2,
|
|
[FuriHalAdcChannel3] = LL_ADC_CHANNEL_3,
|
|
[FuriHalAdcChannel4] = LL_ADC_CHANNEL_4,
|
|
[FuriHalAdcChannel5] = LL_ADC_CHANNEL_5,
|
|
[FuriHalAdcChannel6] = LL_ADC_CHANNEL_6,
|
|
[FuriHalAdcChannel7] = LL_ADC_CHANNEL_7,
|
|
[FuriHalAdcChannel8] = LL_ADC_CHANNEL_8,
|
|
[FuriHalAdcChannel9] = LL_ADC_CHANNEL_9,
|
|
[FuriHalAdcChannel10] = LL_ADC_CHANNEL_10,
|
|
[FuriHalAdcChannel11] = LL_ADC_CHANNEL_11,
|
|
[FuriHalAdcChannel12] = LL_ADC_CHANNEL_12,
|
|
[FuriHalAdcChannel13] = LL_ADC_CHANNEL_13,
|
|
[FuriHalAdcChannel14] = LL_ADC_CHANNEL_14,
|
|
[FuriHalAdcChannel15] = LL_ADC_CHANNEL_15,
|
|
[FuriHalAdcChannel16] = LL_ADC_CHANNEL_16,
|
|
[FuriHalAdcChannel17] = LL_ADC_CHANNEL_17,
|
|
[FuriHalAdcChannel18] = LL_ADC_CHANNEL_18,
|
|
[FuriHalAdcChannelVREFINT] = LL_ADC_CHANNEL_VREFINT,
|
|
[FuriHalAdcChannelTEMPSENSOR] = LL_ADC_CHANNEL_TEMPSENSOR,
|
|
[FuriHalAdcChannelVBAT] = LL_ADC_CHANNEL_VBAT,
|
|
};
|
|
|
|
static FuriHalAdcHandle* furi_hal_adc_handle = NULL;
|
|
|
|
void furi_hal_adc_init(void) {
|
|
furi_hal_adc_handle = malloc(sizeof(FuriHalAdcHandle));
|
|
furi_hal_adc_handle->adc = ADC1;
|
|
furi_hal_adc_handle->mutex = furi_mutex_alloc(FuriMutexTypeNormal);
|
|
}
|
|
|
|
FuriHalAdcHandle* furi_hal_adc_acquire(void) {
|
|
furi_check(furi_mutex_acquire(furi_hal_adc_handle->mutex, FuriWaitForever) == FuriStatusOk);
|
|
|
|
furi_hal_power_insomnia_enter();
|
|
|
|
furi_hal_bus_enable(FuriHalBusADC);
|
|
|
|
return furi_hal_adc_handle;
|
|
}
|
|
|
|
void furi_hal_adc_release(FuriHalAdcHandle* handle) {
|
|
furi_check(handle);
|
|
|
|
if(furi_hal_bus_is_enabled(FuriHalBusADC)) furi_hal_bus_disable(FuriHalBusADC);
|
|
|
|
LL_VREFBUF_Disable();
|
|
LL_VREFBUF_EnableHIZ();
|
|
|
|
furi_hal_power_insomnia_exit();
|
|
|
|
furi_check(furi_mutex_release(furi_hal_adc_handle->mutex) == FuriStatusOk);
|
|
}
|
|
|
|
void furi_hal_adc_configure(FuriHalAdcHandle* handle) {
|
|
furi_hal_adc_configure_ex(
|
|
handle,
|
|
FuriHalAdcScale2048,
|
|
FuriHalAdcClockSync64,
|
|
FuriHalAdcOversample64,
|
|
FuriHalAdcSamplingtime247_5);
|
|
}
|
|
|
|
void furi_hal_adc_configure_ex(
|
|
FuriHalAdcHandle* handle,
|
|
FuriHalAdcScale scale,
|
|
FuriHalAdcClock clock,
|
|
FuriHalAdcOversample oversample,
|
|
FuriHalAdcSamplingTime sampling_time) {
|
|
furi_check(handle);
|
|
furi_check(scale == FuriHalAdcScale2048 || scale == FuriHalAdcScale2500);
|
|
furi_check(clock <= FuriHalAdcClockSync64);
|
|
furi_check(oversample <= FuriHalAdcOversampleNone);
|
|
furi_check(sampling_time <= FuriHalAdcSamplingtime640_5);
|
|
|
|
FuriHalCortexTimer timer;
|
|
|
|
if(furi_hal_bus_is_enabled(FuriHalBusADC)) furi_hal_bus_disable(FuriHalBusADC);
|
|
|
|
uint32_t trim_value = 0;
|
|
switch(scale) {
|
|
case FuriHalAdcScale2048:
|
|
LL_VREFBUF_SetVoltageScaling(LL_VREFBUF_VOLTAGE_SCALE0);
|
|
trim_value = LL_VREFBUF_SC0_GetCalibration() & 0x3FU;
|
|
handle->full_scale = 2048;
|
|
break;
|
|
case FuriHalAdcScale2500:
|
|
LL_VREFBUF_SetVoltageScaling(LL_VREFBUF_VOLTAGE_SCALE1);
|
|
trim_value = LL_VREFBUF_SC1_GetCalibration() & 0x3FU;
|
|
handle->full_scale = 2500;
|
|
break;
|
|
default:
|
|
furi_crash();
|
|
}
|
|
LL_VREFBUF_SetTrimming(trim_value);
|
|
LL_VREFBUF_Enable();
|
|
LL_VREFBUF_DisableHIZ();
|
|
|
|
timer = furi_hal_cortex_timer_get(500000); // 500ms to stabilize VREF
|
|
while(!LL_VREFBUF_IsVREFReady()) {
|
|
furi_check(!furi_hal_cortex_timer_is_expired(timer), "VREF fail");
|
|
};
|
|
|
|
furi_hal_bus_enable(FuriHalBusADC);
|
|
|
|
// ADC Common config
|
|
LL_ADC_CommonInitTypeDef ADC_CommonInitStruct = {0};
|
|
ADC_CommonInitStruct.CommonClock = furi_hal_adc_clock[clock];
|
|
furi_check(
|
|
LL_ADC_CommonInit(__LL_ADC_COMMON_INSTANCE(handle->adc), &ADC_CommonInitStruct) ==
|
|
SUCCESS);
|
|
LL_ADC_SetCommonPathInternalCh(
|
|
__LL_ADC_COMMON_INSTANCE(handle->adc),
|
|
LL_ADC_PATH_INTERNAL_VREFINT | LL_ADC_PATH_INTERNAL_TEMPSENSOR |
|
|
LL_ADC_PATH_INTERNAL_VBAT);
|
|
|
|
// ADC config part 1
|
|
LL_ADC_InitTypeDef ADC_InitStruct = {0};
|
|
ADC_InitStruct.Resolution = LL_ADC_RESOLUTION_12B; //-V1048
|
|
ADC_InitStruct.DataAlignment = LL_ADC_DATA_ALIGN_RIGHT;
|
|
ADC_InitStruct.LowPowerMode = LL_ADC_LP_MODE_NONE;
|
|
furi_check(LL_ADC_Init(handle->adc, &ADC_InitStruct) == SUCCESS);
|
|
|
|
// ADC config part 2: groups parameters
|
|
LL_ADC_REG_InitTypeDef ADC_REG_InitStruct = {0};
|
|
ADC_REG_InitStruct.TriggerSource = LL_ADC_REG_TRIG_SOFTWARE; //-V1048
|
|
ADC_REG_InitStruct.SequencerLength = LL_ADC_REG_SEQ_SCAN_DISABLE;
|
|
ADC_REG_InitStruct.SequencerDiscont = LL_ADC_REG_SEQ_DISCONT_DISABLE;
|
|
ADC_REG_InitStruct.ContinuousMode = LL_ADC_REG_CONV_SINGLE;
|
|
ADC_REG_InitStruct.DMATransfer = LL_ADC_REG_DMA_TRANSFER_NONE;
|
|
ADC_REG_InitStruct.Overrun = LL_ADC_REG_OVR_DATA_OVERWRITTEN;
|
|
furi_check(LL_ADC_REG_Init(handle->adc, &ADC_REG_InitStruct) == SUCCESS);
|
|
|
|
// ADC config part 3: sequencer and channels
|
|
if(oversample == FuriHalAdcOversampleNone) {
|
|
LL_ADC_SetOverSamplingScope(handle->adc, LL_ADC_OVS_DISABLE);
|
|
} else {
|
|
LL_ADC_SetOverSamplingScope(handle->adc, LL_ADC_OVS_GRP_REGULAR_CONTINUED);
|
|
LL_ADC_ConfigOverSamplingRatioShift(
|
|
handle->adc,
|
|
furi_hal_adc_oversample_ratio[oversample],
|
|
furi_hal_adc_oversample_shift[oversample]);
|
|
}
|
|
|
|
for(FuriHalAdcChannel channel = FuriHalAdcChannel0; channel < FuriHalAdcChannelNone;
|
|
channel++) {
|
|
// 47.5 cycles on 64MHz is first meaningful value for internal sources sampling
|
|
LL_ADC_SetChannelSamplingTime(
|
|
handle->adc,
|
|
furi_hal_adc_channel_map[channel],
|
|
furi_hal_adc_sampling_time[sampling_time]);
|
|
LL_ADC_SetChannelSingleDiff(
|
|
handle->adc, furi_hal_adc_channel_map[channel], LL_ADC_SINGLE_ENDED);
|
|
}
|
|
|
|
// Disable ADC deep power down (enabled by default after reset state)
|
|
LL_ADC_DisableDeepPowerDown(handle->adc);
|
|
|
|
// Enable ADC internal voltage regulator
|
|
LL_ADC_EnableInternalRegulator(handle->adc);
|
|
// Delay for ADC internal voltage regulator stabilization.
|
|
timer = furi_hal_cortex_timer_get(LL_ADC_DELAY_INTERNAL_REGUL_STAB_US);
|
|
while(!furi_hal_cortex_timer_is_expired(timer))
|
|
;
|
|
|
|
// Run ADC self calibration
|
|
LL_ADC_StartCalibration(handle->adc, LL_ADC_SINGLE_ENDED);
|
|
// Poll for ADC effectively calibrated
|
|
while(LL_ADC_IsCalibrationOnGoing(handle->adc) != 0)
|
|
;
|
|
// Delay between ADC end of calibration and ADC enable
|
|
size_t end =
|
|
DWT->CYCCNT + (LL_ADC_DELAY_CALIB_ENABLE_ADC_CYCLES * furi_hal_adc_clock_div[clock]);
|
|
while(DWT->CYCCNT < end)
|
|
;
|
|
|
|
// Enable ADC
|
|
LL_ADC_ClearFlag_ADRDY(handle->adc);
|
|
LL_ADC_Enable(handle->adc);
|
|
while(LL_ADC_IsActiveFlag_ADRDY(handle->adc) == 0)
|
|
;
|
|
}
|
|
|
|
uint16_t furi_hal_adc_read(FuriHalAdcHandle* handle, FuriHalAdcChannel channel) {
|
|
furi_check(handle);
|
|
furi_check(channel <= FuriHalAdcChannelVBAT);
|
|
furi_check(LL_ADC_IsEnabled(handle->adc) == 1);
|
|
furi_check(LL_ADC_IsDisableOngoing(handle->adc) == 0);
|
|
furi_check(LL_ADC_REG_IsConversionOngoing(handle->adc) == 0);
|
|
|
|
LL_ADC_REG_SetSequencerRanks(
|
|
handle->adc, LL_ADC_REG_RANK_1, furi_hal_adc_channel_map[channel]);
|
|
|
|
LL_ADC_REG_StartConversion(handle->adc);
|
|
|
|
while(LL_ADC_IsActiveFlag_EOC(handle->adc) == 0)
|
|
;
|
|
uint16_t value = LL_ADC_REG_ReadConversionData12(handle->adc);
|
|
|
|
return value;
|
|
}
|
|
|
|
float furi_hal_adc_convert_to_voltage(FuriHalAdcHandle* handle, uint16_t value) {
|
|
return (float)__LL_ADC_CALC_DATA_TO_VOLTAGE(handle->full_scale, value, LL_ADC_RESOLUTION_12B);
|
|
}
|
|
|
|
float furi_hal_adc_convert_vref(FuriHalAdcHandle* handle, uint16_t value) {
|
|
UNUSED(handle);
|
|
return (float)__LL_ADC_CALC_VREFANALOG_VOLTAGE(value, LL_ADC_RESOLUTION_12B);
|
|
}
|
|
|
|
float furi_hal_adc_convert_temp(FuriHalAdcHandle* handle, uint16_t value) {
|
|
return (float)__LL_ADC_CALC_TEMPERATURE(handle->full_scale, value, LL_ADC_RESOLUTION_12B);
|
|
}
|
|
|
|
float furi_hal_adc_convert_vbat(FuriHalAdcHandle* handle, uint16_t value) {
|
|
return furi_hal_adc_convert_to_voltage(handle, value) * 3;
|
|
}
|