unleashed-firmware/targets/furi_hal_include/furi_hal_adc.h

230 lines
8.7 KiB
C

/**
* @file furi_hal_adc.h
* @brief ADC HAL API
*
* For the sake of simplicity this API implements only small subset
* of what ADC is actually capable of. Feel free to visit Reference
* Manual for STM32WB series and implement any other modes by your
* self.
*
* Couple things to keep in mind:
*
* - ADC resolution is 12 bits, but effective number of bits is ~10 at the best
* and further depends on how you use and configure it.
* - Analog domain is fed from SMPS which is quite noisy.
* - Because of that we use internal on-chip voltage reference for ADC.
* - It's capable of producing 2 voltages: 2.5V and 2.048V. This is the scale
* for your signal.
* - Only single ended mode is available. But you can implement differential one
* by using low level controls directly.
* - No DMA or interrupt API available at this point. But can be implemented
* with low level controls.
*
*
* How to use:
*
* - furi_hal_gpio_init - Configure your pins in `GpioModeAnalog`
* - furi_hal_adc_acquire - acquire ADC handle to work with
* - furi_hal_adc_configure - configure ADC block
* - furi_hal_adc_read - read value
* - furi_hal_adc_release - release ADC handle
*/
#pragma once
#include <furi.h>
#include <stdbool.h>
#include <stdint.h>
#ifdef __cplusplus
extern "C" {
#endif
typedef struct FuriHalAdcHandle FuriHalAdcHandle;
typedef enum {
FuriHalAdcScale2048, /**< 2.048V scale */
FuriHalAdcScale2500, /**< 2.5V scale */
} FuriHalAdcScale;
typedef enum {
FuriHalAdcClockSync16, /**< 16MHZ, synchronous */
FuriHalAdcClockSync32, /**< 32MHZ, synchronous */
FuriHalAdcClockSync64, /**< 64MHz, synchronous */
} FuriHalAdcClock;
typedef enum {
FuriHalAdcOversample2, /**< ADC will take 2 samples per each value */
FuriHalAdcOversample4, /**< ADC will take 4 samples per each value */
FuriHalAdcOversample8, /**< ADC will take 8 samples per each value */
FuriHalAdcOversample16, /**< ADC will take 16 samples per each value */
FuriHalAdcOversample32, /**< ADC will take 32 samples per each value */
FuriHalAdcOversample64, /**< ADC will take 64 samples per each value */
FuriHalAdcOversample128, /**< ADC will take 128 samples per each value */
FuriHalAdcOversample256, /**< ADC will take 256 samples per each value */
FuriHalAdcOversampleNone, /**< disable oversampling */
} FuriHalAdcOversample;
typedef enum {
FuriHalAdcSamplingtime2_5, /**< Sampling time 2.5 ADC clock */
FuriHalAdcSamplingtime6_5, /**< Sampling time 6.5 ADC clock */
FuriHalAdcSamplingtime12_5, /**< Sampling time 12.5 ADC clock */
FuriHalAdcSamplingtime24_5, /**< Sampling time 24.5 ADC clock */
FuriHalAdcSamplingtime47_5, /**< Sampling time 47.5 ADC clock */
FuriHalAdcSamplingtime92_5, /**< Sampling time 92.5 ADC clock */
FuriHalAdcSamplingtime247_5, /**< Sampling time 247.5 ADC clock */
FuriHalAdcSamplingtime640_5, /**< Sampling time 640.5 ADC clock */
} FuriHalAdcSamplingTime;
typedef enum {
/* Channels 0 - 5 are fast channels */
FuriHalAdcChannel0, /**< Internal channel, see `FuriHalAdcChannelVREFINT`. */
FuriHalAdcChannel1, /**< Channel 1p */
FuriHalAdcChannel2, /**< Channel 2p or 1n */
FuriHalAdcChannel3, /**< Channel 3p or 2n */
FuriHalAdcChannel4, /**< Channel 4p or 3n */
FuriHalAdcChannel5, /**< Channel 5p or 4n */
/* Channels 6 - 18 are slow channels */
FuriHalAdcChannel6, /**< Channel 6p or 5n */
FuriHalAdcChannel7, /**< Channel 7p or 6n */
FuriHalAdcChannel8, /**< Channel 8p or 7n */
FuriHalAdcChannel9, /**< Channel 9p or 8n */
FuriHalAdcChannel10, /**< Channel 10p or 9n */
FuriHalAdcChannel11, /**< Channel 11p or 10n */
FuriHalAdcChannel12, /**< Channel 12p or 11n */
FuriHalAdcChannel13, /**< Channel 13p or 12n */
FuriHalAdcChannel14, /**< Channel 14p or 13n */
FuriHalAdcChannel15, /**< Channel 15p or 14n */
FuriHalAdcChannel16, /**< Channel 16p or 15n */
FuriHalAdcChannel17, /**< Internal channel, see `FuriHalAdcChannelTEMPSENSOR`. */
FuriHalAdcChannel18, /**< Internal channel, see `FuriHalAdcChannelVBAT`. */
/* Special Channels: combines one of the 0-18 channel and additional internal peripherals */
FuriHalAdcChannelVREFINT, /**< Special channel for VREFINT, used for calibration and self test */
FuriHalAdcChannelTEMPSENSOR, /**< Special channel for on-die temperature sensor, requires at least 5us of sampling time */
FuriHalAdcChannelVBAT, /**< Special channel for VBAT/3 voltage, requires at least 12us of sampling time */
/* Special value to indicate that pin is not connected to ADC */
FuriHalAdcChannelNone, /**< No channel */
} FuriHalAdcChannel;
/** Initialize ADC subsystem */
void furi_hal_adc_init(void);
/** Acquire ADC handle
*
* Enables appropriate power and clocking domains
*
* @return FuriHalAdcHandle pointer
*/
FuriHalAdcHandle* furi_hal_adc_acquire(void);
/** Release ADC handle
*
* @param handle The ADC handle
*/
void furi_hal_adc_release(FuriHalAdcHandle* handle);
/** Configure with default parameters and enable ADC
*
* Parameters used:
* - FuriHalAdcScale2048 - 2.048V VREF Scale. Your signal should be in 0 -
* 2.048V range.
* - FuriHalAdcClockSync64 - Clocked from sysclk bus at 64MHz in synchronous
* mode. Fast, no delay on data bus access.
* - FuriHalAdcOversample64 - Going to acquire and average 64 samples. For
* circuits with slowly or not changing signal. Total time per one read:
* (1/64)*(12.5+247.5)*64 = 260us. The best results you'll get if your signal
* will stay on the same level all this time.
* - FuriHalAdcSamplingtime247_5 - Sampling(transfer from source to internal
* sampling capacitor) time is 247.5 ADC clocks: (1/64)*247.5 = 3.8671875us.
* For relatively high impedance circuits.
*
* Also keep your measurement circuit impedance under 10KOhm or oversampling
* results will be compromised. Verify your signal with oscilloscope(you may
* need fast oscilloscope: 200MHz bandwidth, 125MS/s), ensure that signal is not
* distorted by sampling.
*
* Those parameters were optimized for 0 - 2.048 voltage measurement with ~0.1%
* precision. You can get more, but it will require some magic.
*
* @param handle The ADC handle
*/
void furi_hal_adc_configure(FuriHalAdcHandle* handle);
/** Configure with extended parameters and enable ADC
*
* General advice is to start with default parameters, figure out what exactly
* is not working for you and then tune it. Also in some cases changing
* circuit(adding caps, lowering impedance, adding opamp) may be better than changing
* parameters.
*
* @warning In general ADC is a world of magic: make sure that you understand
* how your circuit and ADC works. Then carefully read STM32WB
* series reference manual. Setting incorrect parameters leads to
* very poor results. Also internal channels require special
* settings.
*
* @param handle The ADC handle
* @param[in] scale The ADC voltage scale
* @param[in] clock The ADC clock
* @param[in] oversample The ADC oversample mode
* @param[in] sampling_time The ADC sampling time
*/
void furi_hal_adc_configure_ex(
FuriHalAdcHandle* handle,
FuriHalAdcScale scale,
FuriHalAdcClock clock,
FuriHalAdcOversample oversample,
FuriHalAdcSamplingTime sampling_time);
/** Read single ADC value
*
* @param handle The ADC handle
* @param[in] channel The channel to sample
*
* @return value, 12 bits
*/
uint16_t furi_hal_adc_read(FuriHalAdcHandle* handle, FuriHalAdcChannel channel);
/** Convert sampled value to voltage
*
* @param handle The ADC handle
* @param[in] value The value acquired with `furi_hal_adc_read`
*
* @return Voltage in mV
*/
float furi_hal_adc_convert_to_voltage(FuriHalAdcHandle* handle, uint16_t value);
/** Convert sampled VREFINT value to voltage
*
* @param handle The ADC handle
* @param[in] value The value acquired with `furi_hal_adc_read` for
* `FuriHalAdcChannelVREFINT` channel
*
* @return Voltage in mV
*/
float furi_hal_adc_convert_vref(FuriHalAdcHandle* handle, uint16_t value);
/** Convert sampled TEMPSENSOR value to temperature
*
* @param handle The ADC handle
* @param[in] value The value acquired with `furi_hal_adc_read` for
* `FuriHalAdcChannelTEMPSENSOR` channel
*
* @return temperature in degree C
*/
float furi_hal_adc_convert_temp(FuriHalAdcHandle* handle, uint16_t value);
/** Convert sampled VBAT value to voltage
*
* @param handle The ADC handle
* @param[in] value The value acquired with `furi_hal_adc_read` for
* `FuriHalAdcChannelVBAT` channel
*
* @return Voltage in mV
*/
float furi_hal_adc_convert_vbat(FuriHalAdcHandle* handle, uint16_t value);
#ifdef __cplusplus
}
#endif