Unverified Commit 352af864 authored by Jan Procházka's avatar Jan Procházka Committed by GitHub

Analog Continuous mode API (#8715)

* Adds Analog Continuous mode API

* fix when stopping/starting ADC data are not complete

* Added example

* fix size check

* update frequency in example

* set buffer to NULL if error occurs

* add docs

* set buffer to null on error

* fix example

* update docs

* fix example

* change return value to bool type

* updated adc modes description in docs

* Add empty line at the end of sketch
parent b98b52a1
// Copyright 2015-2016 Espressif Systems (Shanghai) PTE LTD
// Copyright 2015-2023 Espressif Systems (Shanghai) PTE LTD
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
......@@ -18,14 +18,28 @@
#include "esp32-hal.h"
#include "esp32-hal-periman.h"
#include "esp_adc/adc_oneshot.h"
#include "esp_adc/adc_continuous.h"
#include "esp_adc/adc_cali_scheme.h"
static uint8_t __analogAttenuation = ADC_11db;
static uint8_t __analogWidth = SOC_ADC_RTC_MAX_BITWIDTH;
static uint8_t __analogReturnedWidth = SOC_ADC_RTC_MAX_BITWIDTH;
adc_oneshot_unit_handle_t adc_handle[SOC_ADC_PERIPH_NUM];
adc_cali_handle_t adc_cali_handle[SOC_ADC_PERIPH_NUM];
typedef struct {
voidFuncPtr fn;
void* arg;
} interrupt_config_t;
typedef struct {
adc_oneshot_unit_handle_t adc_oneshot_handle;
adc_continuous_handle_t adc_continuous_handle;
interrupt_config_t adc_interrupt_handle;
adc_cali_handle_t adc_cali_handle;
uint32_t buffer_size;
uint32_t conversion_frame_size;
} adc_handle_t;
adc_handle_t adc_handle[SOC_ADC_PERIPH_NUM];
static bool adcDetachBus(void * pin){
adc_channel_t adc_channel;
......@@ -42,11 +56,23 @@ static bool adcDetachBus(void * pin){
}
if(used_channels == 1){ //only 1 channel is used
esp_err_t err = adc_oneshot_del_unit(adc_handle[adc_unit]);
esp_err_t err = adc_oneshot_del_unit(adc_handle[adc_unit].adc_oneshot_handle);
if(err != ESP_OK){
return false;
}
adc_handle[adc_unit].adc_oneshot_handle = NULL;
#if ADC_CALI_SCHEME_CURVE_FITTING_SUPPORTED
err = adc_cali_delete_scheme_curve_fitting(adc_handle[adc_unit].adc_cali_handle);
if(err != ESP_OK){
return false;
}
adc_handle[adc_unit] = NULL;
#elif !defined(CONFIG_IDF_TARGET_ESP32H2)
err = adc_cali_delete_scheme_line_fitting(adc_handle[adc_unit].adc_cali_handle);
if(err != ESP_OK){
return false;
}
#endif
adc_handle[adc_unit].adc_cali_handle = NULL;
}
return true;
}
......@@ -59,12 +85,12 @@ esp_err_t __analogChannelConfig(adc_bitwidth_t width, adc_attenuation_t atten, i
};
if(pin == -1){ //Reconfigure all used analog pins/channels
for(int adc_unit = 0 ; adc_unit < SOC_ADC_PERIPH_NUM; adc_unit++){
if(adc_handle[adc_unit] != NULL){
if(adc_handle[adc_unit].adc_oneshot_handle != NULL){
for (uint8_t channel = 0; channel < SOC_ADC_CHANNEL_NUM(adc_unit); channel++){
int io_pin;
adc_oneshot_channel_to_io( adc_unit, channel, &io_pin);
if(perimanGetPinBusType(io_pin) == ESP32_BUS_TYPE_ADC_ONESHOT){
err = adc_oneshot_config_channel(adc_handle[adc_unit], channel, &config);
err = adc_oneshot_config_channel(adc_handle[adc_unit].adc_oneshot_handle, channel, &config);
if(err != ESP_OK){
log_e("adc_oneshot_config_channel failed with error: %d", err);
return err;
......@@ -72,10 +98,10 @@ esp_err_t __analogChannelConfig(adc_bitwidth_t width, adc_attenuation_t atten, i
}
}
//ADC calibration reconfig only if all channels are updated
if(adc_cali_handle[adc_unit] != NULL){
if(adc_handle[adc_unit].adc_cali_handle != NULL){
#if ADC_CALI_SCHEME_CURVE_FITTING_SUPPORTED
log_d("Deleting ADC_UNIT_%d cali handle",adc_unit);
err = adc_cali_delete_scheme_curve_fitting(adc_cali_handle[adc_unit]);
err = adc_cali_delete_scheme_curve_fitting(adc_handle[adc_unit].adc_cali_handle);
if(err != ESP_OK){
log_e("adc_cali_delete_scheme_curve_fitting failed with error: %d", err);
return err;
......@@ -86,14 +112,14 @@ esp_err_t __analogChannelConfig(adc_bitwidth_t width, adc_attenuation_t atten, i
.bitwidth = width,
};
log_d("Creating ADC_UNIT_%d curve cali handle",adc_unit);
err = adc_cali_create_scheme_curve_fitting(&cali_config, &adc_cali_handle[adc_unit]);
err = adc_cali_create_scheme_curve_fitting(&cali_config, &adc_handle[adc_unit].adc_cali_handle);
if(err != ESP_OK){
log_e("adc_cali_create_scheme_curve_fitting failed with error: %d", err);
return err;
}
#elif !defined(CONFIG_IDF_TARGET_ESP32C6) && !defined(CONFIG_IDF_TARGET_ESP32H2) //ADC_CALI_SCHEME_LINE_FITTING_SUPPORTED
#elif !defined(CONFIG_IDF_TARGET_ESP32H2) //ADC_CALI_SCHEME_LINE_FITTING_SUPPORTED
log_d("Deleting ADC_UNIT_%d line cali handle",adc_unit);
err = adc_cali_delete_scheme_line_fitting(adc_cali_handle[adc_unit]);
err = adc_cali_delete_scheme_line_fitting(adc_handle[adc_unit].adc_cali_handle);
if(err != ESP_OK){
log_e("adc_cali_delete_scheme_line_fitting failed with error: %d", err);
return err;
......@@ -104,7 +130,7 @@ esp_err_t __analogChannelConfig(adc_bitwidth_t width, adc_attenuation_t atten, i
.bitwidth = width,
};
log_d("Creating ADC_UNIT_%d line cali handle",adc_unit);
err = adc_cali_create_scheme_line_fitting(&cali_config, &adc_cali_handle[adc_unit]);
err = adc_cali_create_scheme_line_fitting(&cali_config, &adc_handle[adc_unit].adc_cali_handle);
if(err != ESP_OK){
log_e("adc_cali_create_scheme_line_fitting failed with error: %d", err);
return err;
......@@ -128,7 +154,7 @@ esp_err_t __analogChannelConfig(adc_bitwidth_t width, adc_attenuation_t atten, i
log_e("Pin %u is not ADC pin!", pin);
return err;
}
err = adc_oneshot_config_channel(adc_handle[adc_unit], channel, &config);
err = adc_oneshot_config_channel(adc_handle[adc_unit].adc_oneshot_handle, channel, &config);
if(err != ESP_OK){
log_e("adc_oneshot_config_channel failed with error: %d", err);
return err;
......@@ -174,12 +200,12 @@ void __analogSetWidth(uint8_t bits){
esp_err_t __analogInit(uint8_t pin, adc_channel_t channel, adc_unit_t adc_unit){
esp_err_t err = ESP_OK;
if(adc_handle[adc_unit] == NULL) {
if(adc_handle[adc_unit].adc_oneshot_handle == NULL) {
adc_oneshot_unit_init_cfg_t init_config1 = {
.unit_id = adc_unit,
.ulp_mode = ADC_ULP_MODE_DISABLE,
};
err = adc_oneshot_new_unit(&init_config1, &adc_handle[adc_unit]);
err = adc_oneshot_new_unit(&init_config1, &adc_handle[adc_unit].adc_oneshot_handle);
if(err != ESP_OK){
log_e("adc_oneshot_new_unit failed with error: %d", err);
......@@ -197,7 +223,7 @@ esp_err_t __analogInit(uint8_t pin, adc_channel_t channel, adc_unit_t adc_unit){
.atten = __analogAttenuation,
};
err = adc_oneshot_config_channel(adc_handle[adc_unit], channel, &config);
err = adc_oneshot_config_channel(adc_handle[adc_unit].adc_oneshot_handle, channel, &config);
if(err != ESP_OK){
log_e("adc_oneshot_config_channel failed with error: %d", err);
return err;
......@@ -245,7 +271,7 @@ uint16_t __analogRead(uint8_t pin){
}
}
adc_oneshot_read(adc_handle[adc_unit], channel, &value);
adc_oneshot_read(adc_handle[adc_unit].adc_oneshot_handle, channel, &value);
return mapResolution(value);
}
......@@ -269,7 +295,7 @@ uint32_t __analogReadMilliVolts(uint8_t pin){
}
}
if(adc_cali_handle[adc_unit] == NULL){
if(adc_handle[adc_unit].adc_cali_handle == NULL){
log_d("Creating cali handle for ADC_%d", adc_unit);
#if ADC_CALI_SCHEME_CURVE_FITTING_SUPPORTED
adc_cali_curve_fitting_config_t cali_config = {
......@@ -277,14 +303,14 @@ uint32_t __analogReadMilliVolts(uint8_t pin){
.atten = __analogAttenuation,
.bitwidth = __analogWidth,
};
err = adc_cali_create_scheme_curve_fitting(&cali_config, &adc_cali_handle[adc_unit]);
#elif !defined(CONFIG_IDF_TARGET_ESP32C6) && !defined(CONFIG_IDF_TARGET_ESP32H2) //ADC_CALI_SCHEME_LINE_FITTING_SUPPORTED
err = adc_cali_create_scheme_curve_fitting(&cali_config, &adc_handle[adc_unit].adc_cali_handle);
#elif !defined(CONFIG_IDF_TARGET_ESP32H2) //ADC_CALI_SCHEME_LINE_FITTING_SUPPORTED
adc_cali_line_fitting_config_t cali_config = {
.unit_id = adc_unit,
.bitwidth = __analogWidth,
.atten = __analogAttenuation,
};
err = adc_cali_create_scheme_line_fitting(&cali_config, &adc_cali_handle[adc_unit]);
err = adc_cali_create_scheme_line_fitting(&cali_config, &adc_handle[adc_unit].adc_cali_handle);
#endif
if(err != ESP_OK){
log_e("adc_cali_create_scheme_x failed!");
......@@ -292,7 +318,7 @@ uint32_t __analogReadMilliVolts(uint8_t pin){
}
}
err = adc_oneshot_get_calibrated_result(adc_handle[adc_unit], adc_cali_handle[adc_unit], channel, &value);
err = adc_oneshot_get_calibrated_result(adc_handle[adc_unit].adc_oneshot_handle, adc_handle[adc_unit].adc_cali_handle, channel, &value);
if(err != ESP_OK){
log_e("adc_oneshot_get_calibrated_result failed!");
return 0;
......@@ -310,4 +336,357 @@ extern void analogSetPinAttenuation(uint8_t pin, adc_attenuation_t attenuation)
extern void analogSetWidth(uint8_t bits) __attribute__ ((weak, alias("__analogSetWidth")));
#endif
/*
* ADC Continuous mode
*/
#if CONFIG_IDF_TARGET_ESP32 || CONFIG_IDF_TARGET_ESP32S2
#define ADC_OUTPUT_TYPE ADC_DIGI_OUTPUT_FORMAT_TYPE1
#define ADC_GET_CHANNEL(p_data) ((p_data)->type1.channel)
#define ADC_GET_DATA(p_data) ((p_data)->type1.data)
#else
#define ADC_OUTPUT_TYPE ADC_DIGI_OUTPUT_FORMAT_TYPE2
#define ADC_GET_CHANNEL(p_data) ((p_data)->type2.channel)
#define ADC_GET_DATA(p_data) ((p_data)->type2.data)
#endif
static uint8_t __adcContinuousAtten = ADC_11db;
static uint8_t __adcContinuousWidth = SOC_ADC_DIGI_MAX_BITWIDTH;
static uint8_t used_adc_channels = 0;
adc_continuos_data_t * adc_result = NULL;
static bool adcContinuousDetachBus(void * adc_unit_number){
adc_unit_t adc_unit = (adc_unit_t)adc_unit_number - 1;
if(adc_handle[adc_unit].adc_continuous_handle == NULL){
return true;
}
else
{
esp_err_t err = adc_continuous_deinit(adc_handle[adc_unit].adc_continuous_handle);
if(err != ESP_OK){
return false;
}
adc_handle[adc_unit].adc_continuous_handle = NULL;
#if ADC_CALI_SCHEME_CURVE_FITTING_SUPPORTED
err = adc_cali_delete_scheme_curve_fitting(adc_handle[adc_unit].adc_cali_handle);
if(err != ESP_OK){
return false;
}
#elif !defined(CONFIG_IDF_TARGET_ESP32H2)
err = adc_cali_delete_scheme_line_fitting(adc_handle[adc_unit].adc_cali_handle);
if(err != ESP_OK){
return false;
}
#endif
adc_handle[adc_unit].adc_cali_handle = NULL;
//set all used pins to INIT state
for (uint8_t channel = 0; channel < SOC_ADC_CHANNEL_NUM(adc_unit); channel++){
int io_pin;
adc_oneshot_channel_to_io(adc_unit, channel, &io_pin);
if(perimanGetPinBusType(io_pin) == ESP32_BUS_TYPE_ADC_CONT){
if(!perimanSetPinBus(io_pin, ESP32_BUS_TYPE_INIT, NULL)){
return false;
}
}
}
}
return true;
}
bool IRAM_ATTR adcFnWrapper(adc_continuous_handle_t handle, const adc_continuous_evt_data_t *edata, void *args){
interrupt_config_t * isr = (interrupt_config_t*)args;
//Check if edata->size matches conversion_frame_size, else just return from ISR
if(edata->size == adc_handle[0].conversion_frame_size){
if(isr->fn) {
if(isr->arg){
((voidFuncPtrArg)isr->fn)(isr->arg);
} else {
isr->fn();
}
}
}
return false;
}
esp_err_t __analogContinuousInit(adc_channel_t *channel, uint8_t channel_num, adc_unit_t adc_unit, uint32_t sampling_freq_hz){
//Create new ADC continuous handle
adc_continuous_handle_cfg_t adc_config = {
.max_store_buf_size = adc_handle[adc_unit].buffer_size,
.conv_frame_size = adc_handle[adc_unit].conversion_frame_size,
};
esp_err_t err = adc_continuous_new_handle(&adc_config, &adc_handle[adc_unit].adc_continuous_handle);
if(err != ESP_OK){
log_e("adc_continuous_new_handle failed with error: %d", err);
return ESP_FAIL;
}
//Configure adc pins
adc_continuous_config_t dig_cfg = {
.sample_freq_hz = sampling_freq_hz,
.conv_mode = ADC_CONV_SINGLE_UNIT_1,
.format = ADC_OUTPUT_TYPE,
};
adc_digi_pattern_config_t adc_pattern[SOC_ADC_PATT_LEN_MAX] = {0};
dig_cfg.pattern_num = channel_num;
for (int i = 0; i < channel_num; i++) {
adc_pattern[i].atten = __adcContinuousAtten;
adc_pattern[i].channel = channel[i] & 0x7;
adc_pattern[i].unit = ADC_UNIT_1;
adc_pattern[i].bit_width = __adcContinuousWidth;
}
dig_cfg.adc_pattern = adc_pattern;
err = adc_continuous_config(adc_handle[adc_unit].adc_continuous_handle, &dig_cfg);
if(err != ESP_OK){
log_e("adc_continuous_config failed with error: %d", err);
return ESP_FAIL;
}
used_adc_channels = channel_num;
return ESP_OK;
}
bool analogContinuous(uint8_t pins[], size_t pins_count, uint32_t conversions_per_pin, uint32_t sampling_freq_hz, void (*userFunc)(void)){
adc_channel_t channel[pins_count];
adc_unit_t adc_unit;
esp_err_t err = ESP_OK;
//Convert pins to channels and check if all are ADC1s unit
for(int i = 0; i < pins_count; i++){
err = adc_continuous_io_to_channel(pins[i], &adc_unit, &channel[i]);
if(err != ESP_OK){
log_e("Pin %u is not ADC pin!", pins[i]);
return false;
}
if(adc_unit != 0){
log_e("Only ADC1 pins are supported in continuous mode!");
return false;
}
}
//Check if Oneshot and Continous handle exists
if(adc_handle[adc_unit].adc_oneshot_handle != NULL){
log_e("ADC%d is running in oneshot mode. Aborting.", adc_unit+1);
return false;
}
if(adc_handle[adc_unit].adc_continuous_handle != NULL){
log_e("ADC%d continuous is already initialized. To reconfigure call analogContinuousDeinit() first.", adc_unit+1);
return false;
}
//Check sampling frequency
if((sampling_freq_hz < SOC_ADC_SAMPLE_FREQ_THRES_LOW) || (sampling_freq_hz > SOC_ADC_SAMPLE_FREQ_THRES_HIGH)){
log_e("Sampling frequency is out of range. Supported sampling frequencies are %d - %d", SOC_ADC_SAMPLE_FREQ_THRES_LOW, SOC_ADC_SAMPLE_FREQ_THRES_HIGH);
return false;
}
//Set periman deinit function and reset all pins to init state.
perimanSetBusDeinit(ESP32_BUS_TYPE_ADC_CONT, adcContinuousDetachBus);
for(int j = 0; j < pins_count; j++){
if(!perimanSetPinBus(pins[j], ESP32_BUS_TYPE_INIT, NULL)){
return false;
}
}
//Set conversion frame and buffer size (conversion frame must be in multiples of SOC_ADC_DIGI_DATA_BYTES_PER_CONV)
adc_handle[adc_unit].conversion_frame_size = conversions_per_pin * pins_count * SOC_ADC_DIGI_RESULT_BYTES;
#if CONFIG_IDF_TARGET_ESP32 || CONFIG_IDF_TARGET_ESP32S2
uint8_t calc_multiple = adc_handle[adc_unit].conversion_frame_size % SOC_ADC_DIGI_DATA_BYTES_PER_CONV;
if(calc_multiple != 0){
adc_handle[adc_unit].conversion_frame_size = (adc_handle[adc_unit].conversion_frame_size + calc_multiple);
}
#endif
adc_handle[adc_unit].buffer_size = adc_handle[adc_unit].conversion_frame_size * 2;
//Conversion frame size buffer cant be bigger than 4092 bytes
if(adc_handle[adc_unit].conversion_frame_size > 4092){
log_e("Buffers are too big. Please set lower conversions per pin.");
return false;
}
//Initialize continuous handle and pins
err = __analogContinuousInit(channel, sizeof(channel) / sizeof(adc_channel_t), adc_unit, sampling_freq_hz);
if(err != ESP_OK){
log_e("Analog initialization failed!");
return false;
}
//Setup callbacks for complete event
adc_continuous_evt_cbs_t cbs = {
.on_conv_done = adcFnWrapper,
//.on_pool_ovf can be used in future
};
adc_handle[adc_unit].adc_interrupt_handle.fn = (voidFuncPtr)userFunc;
err = adc_continuous_register_event_callbacks(adc_handle[adc_unit].adc_continuous_handle, &cbs, &adc_handle[adc_unit].adc_interrupt_handle);
if(err != ESP_OK){
log_e("adc_continuous_register_event_callbacks failed!");
return false;
}
//Allocate and prepare result structure for adc readings
adc_result = malloc(pins_count * sizeof(adc_continuos_data_t));
for(int k = 0; k < pins_count; k++){
adc_result[k].pin = pins[k];
adc_result[k].channel = channel[k];
}
//Initialize ADC calibration handle
if(adc_handle[adc_unit].adc_cali_handle == NULL){
log_d("Creating cali handle for ADC_%d", adc_unit);
#if ADC_CALI_SCHEME_CURVE_FITTING_SUPPORTED
adc_cali_curve_fitting_config_t cali_config = {
.unit_id = adc_unit,
.atten = __adcContinuousAtten,
.bitwidth = __adcContinuousWidth,
};
err = adc_cali_create_scheme_curve_fitting(&cali_config, &adc_handle[adc_unit].adc_cali_handle);
#elif !defined(CONFIG_IDF_TARGET_ESP32H2) //ADC_CALI_SCHEME_LINE_FITTING_SUPPORTED
adc_cali_line_fitting_config_t cali_config = {
.unit_id = adc_unit,
.bitwidth = __adcContinuousWidth,
.atten = __adcContinuousAtten,
};
err = adc_cali_create_scheme_line_fitting(&cali_config, &adc_handle[adc_unit].adc_cali_handle);
#endif
if(err != ESP_OK){
log_e("adc_cali_create_scheme_x failed!");
return false;
}
}
for(int k = 0; k < pins_count; k++){
if(!perimanSetPinBus(pins[k], ESP32_BUS_TYPE_ADC_CONT, (void *)(adc_unit+1))){
log_e("perimanSetPinBus to ADC Continuous failed!");
adcContinuousDetachBus((void *)(adc_unit+1));
return false;
}
}
return true;
}
bool analogContinuousRead(adc_continuos_data_t ** buffer, uint32_t timeout_ms){
if(adc_handle[ADC_UNIT_1].adc_continuous_handle != NULL){
uint32_t bytes_read = 0;
uint32_t read_raw[used_adc_channels];
uint32_t read_count[used_adc_channels];
uint8_t adc_read[adc_handle[ADC_UNIT_1].conversion_frame_size];
memset(adc_read, 0xcc, sizeof(adc_read));
memset(read_raw, 0, sizeof(read_raw));
memset(read_count, 0, sizeof(read_count));
esp_err_t err = adc_continuous_read(adc_handle[ADC_UNIT_1].adc_continuous_handle, adc_read, adc_handle[0].conversion_frame_size, &bytes_read, timeout_ms);
if(err != ESP_OK){
if(err == ESP_ERR_TIMEOUT){
log_e("Reading data failed: No data, increase timeout");
}
else {
log_e("Reading data failed with error: %X", err);
}
*buffer = NULL;
return false;
}
for (int i = 0; i < bytes_read; i += SOC_ADC_DIGI_RESULT_BYTES) {
adc_digi_output_data_t *p = (adc_digi_output_data_t*)&adc_read[i];
uint32_t chan_num = ADC_GET_CHANNEL(p);
uint32_t data = ADC_GET_DATA(p);
/* Check the channel number validation, the data is invalid if the channel num exceed the maximum channel */
if(chan_num >= SOC_ADC_CHANNEL_NUM(0)){
log_e("Invalid data [%d_%d]", chan_num, data);
*buffer = NULL;
return false;
}
if(data >= (1 << SOC_ADC_DIGI_MAX_BITWIDTH))
{
data = 0;
log_e("Invalid data");
}
for(int j = 0; j < used_adc_channels; j++){
if(adc_result[j].channel == chan_num){
read_raw[j] += data;
read_count[j] += 1;
break;
}
}
}
for (int j = 0; j < used_adc_channels; j++){
if (read_count[j] != 0){
adc_result[j].avg_read_raw = read_raw[j] / read_count[j];
adc_cali_raw_to_voltage(adc_handle[ADC_UNIT_1].adc_cali_handle, adc_result[j].avg_read_raw, &adc_result[j].avg_read_mvolts);
}
else {
log_w("No data read for pin %d", adc_result[j].pin);
}
}
*buffer = adc_result;
return true;
}
else {
log_e("ADC Continuous is not initialized!");
return false;
}
}
bool analogContinuousStart(){
if(adc_handle[ADC_UNIT_1].adc_continuous_handle != NULL){
if(adc_continuous_start(adc_handle[ADC_UNIT_1].adc_continuous_handle) == ESP_OK){
return true;
}
} else {
log_e("ADC Continuous is not initialized!");
}
return false;
}
bool analogContinuousStop(){
if(adc_handle[ADC_UNIT_1].adc_continuous_handle != NULL){
if(adc_continuous_stop(adc_handle[ADC_UNIT_1].adc_continuous_handle) == ESP_OK){
return true;
}
} else {
log_e("ADC Continuous is not initialized!");
}
return false;
}
bool analogContinuousDeinit(){
if(adc_handle[ADC_UNIT_1].adc_continuous_handle != NULL){
esp_err_t err = adc_continuous_deinit(adc_handle[ADC_UNIT_1].adc_continuous_handle);
if (err != ESP_OK){
return false;
}
free(adc_result);
adc_handle[ADC_UNIT_1].adc_continuous_handle = NULL;
} else {
log_i("ADC Continuous was not initialized");
}
return true;
}
void analogContinuousSetAtten(adc_attenuation_t attenuation){
__adcContinuousAtten = attenuation;
}
void analogContinuousSetWidth(uint8_t bits){
if ((bits < SOC_ADC_DIGI_MIN_BITWIDTH) && (bits > SOC_ADC_DIGI_MAX_BITWIDTH)){
log_e("Selected width cannot be set. Range is from %d to %d", SOC_ADC_DIGI_MIN_BITWIDTH, SOC_ADC_DIGI_MAX_BITWIDTH);
return;
}
__adcContinuousWidth = bits;
}
#endif
......@@ -77,6 +77,56 @@ void analogSetWidth(uint8_t bits);
#endif
/*
* Analog Continuous mode
* */
typedef struct {
uint8_t pin; /*!<ADC pin */
uint8_t channel; /*!<ADC channel */
int avg_read_raw; /*!<ADC average raw data */
int avg_read_mvolts; /*!<ADC average voltage in mV */
} adc_continuos_data_t;
/*
* Setup ADC continuous peripheral
* */
bool analogContinuous(uint8_t pins[], size_t pins_count, uint32_t conversions_per_pin, uint32_t sampling_freq_hz, void (*userFunc)(void));
/*
* Read ADC continuous conversion data
* */
bool analogContinuousRead(adc_continuos_data_t ** buffer, uint32_t timeout_ms);
/*
* Start ADC continuous conversions
* */
bool analogContinuousStart();
/*
* Stop ADC continuous conversions
* */
bool analogContinuousStop();
/*
* Deinitialize ADC continuous peripheral
* */
bool analogContinuousDeinit();
/*
* Sets the attenuation for continuous mode reading
* Default is 11db
* */
void analogContinuousSetAtten(adc_attenuation_t attenuation);
/*
* Sets the read resolution for continuous mode
* Default is 12bit (0 - 4095)
* Range is 9 - 12
* */
void analogContinuousSetWidth(uint8_t bits);
#ifdef __cplusplus
}
#endif
......
......@@ -11,13 +11,17 @@ to a digital form so that it can be read and processed by a microcontroller.
ADCs are very useful in control and monitoring applications since most sensors
(e.g., temperature, pressure, force) produce analogue output voltages.
.. note:: Each SoC or module has a different number of ADC's with a different number of channels and pins availible. Refer to datasheet of each board for more info.
.. note:: Each SoC or module has a different number of ADC's with a different number of channels and pins available. Refer to datasheet of each board for more info.
Arduino-ESP32 ADC API
---------------------
ADC common API
**************
ADC OneShot mode
****************
The ADC OneShot mode API is fully compatible with Arduino's ``analogRead`` function.
When you call the ``analogRead`` or ``analogReadMillivolts`` function, it returns the result of a single conversion on the requested pin.
analogRead
^^^^^^^^^^
......@@ -82,7 +86,7 @@ The measurable input voltage differs for each chip, see table below for detailed
``ADC_ATTEN_DB_0`` 100 mV ~ 950 mV
``ADC_ATTEN_DB_2_5`` 100 mV ~ 1250 mV
``ADC_ATTEN_DB_6`` 150 mV ~ 1750 mV
``ADC_ATTEN_DB_11`` 150 mV ~ 2450 mV
``ADC_ATTEN_DB_11`` 150 mV ~ 3100 mV
===================== ===========================================
.. tab:: ESP32-S2
......@@ -136,12 +140,11 @@ This function is used to set the attenuation for a specific pin/ADC channel. For
* ``pin`` selects specific pin for attenuation settings.
* ``attenuation`` sets the attenuation.
ADC API specific for ESP32 chip
*******************************
analogSetWidth
^^^^^^^^^^^^^^
.. note:: This function is only available for ESP32 chip.
This function is used to set the hardware sample bits and read resolution.
Default is 12bit (0 - 4095).
Range is 9 - 12.
......@@ -150,12 +153,129 @@ Range is 9 - 12.
void analogSetWidth(uint8_t bits);
ADC Continuous mode
*******************
ADC Continuous mode is an API designed for performing analog conversions on multiple pins in the background,
with the feature of receiving a callback upon completion of these conversions to access the results.
This API allows you to specify the desired number of conversions per pin within a single cycle, along with its corresponding sampling rate.
The outcome of the ``analogContinuousRead`` function is an array of ``adc_continuous_data_t`` structures.
These structures hold both the raw average value and the average value in millivolts for each pin.
analogContinuous
^^^^^^^^^^^^^^^^
This function is used to configure ADC continuous peripheral on selected pins.
.. code-block:: arduino
bool analogContinuous(uint8_t pins[], size_t pins_count, uint32_t conversions_per_pin, uint32_t sampling_freq_hz, void (*userFunc)(void));
* ``pins[]`` array of pins to be set up
* ``pins_count`` count of pins in array
* ``conversions_per_pin`` sets how many conversions per pin will run each ADC cycle
* ``sampling_freq_hz`` sets sampling frequency of ADC in Hz
* ``userFunc`` sets callback function to be called after adc conversion is done (can be set to ``NULL``)
This function will return ``true`` if configuration is successful.
If ``false`` is returned, error occurs and ADC continuous was not configured.
analogContinuousRead
^^^^^^^^^^^^^^^^^^^^
This function is used to read ADC continuous data to the result buffer. The result buffer is an array of ``adc_continuos_data_t``.
.. code-block:: arduino
typedef struct {
uint8_t pin; /*!<ADC pin */
uint8_t channel; /*!<ADC channel */
int avg_read_raw; /*!<ADC average raw data */
int avg_read_mvolts; /*!<ADC average voltage in mV */
} adc_continuos_data_t;
.. code-block:: arduino
bool analogContinuousRead(adc_continuos_data_t ** buffer, uint32_t timeout_ms);
* ``buffer`` conversion result buffer to read from ADC in adc_continuos_data_t format.
* ``timeout_ms`` time to wait for data in milliseconds.
This function will return ``true`` if reading is successful and ``buffer`` is filled with data.
If ``false`` is returned, reading has failed and ``buffer`` is set to NULL.
analogContinuousStart
^^^^^^^^^^^^^^^^^^^^^
This function is used to start ADC continuous conversions.
.. code-block:: arduino
bool analogContinuousStart();
This function will return ``true`` if ADC continuous is succesfully started.
If ``false`` is returned, starting ADC continuous has failed.
analogContinuousStop
^^^^^^^^^^^^^^^^^^^^
This function is used to stop ADC continuous conversions.
.. code-block:: arduino
bool analogContinuousStop();
This function will return ``true`` if ADC continuous is succesfully stopped.
If ``false`` is returned, stopping ADC continuous has failed.
analogContinuousDeinit
^^^^^^^^^^^^^^^^^^^^^^
This function is used to deinitialize ADC continuous peripheral.
.. code-block:: arduino
bool analogContinuousDeinit();
This function will return ``true`` if ADC continuous is succesfully deinitialized.
If ``false`` is returned, deinitilization of ADC continuous has failed.
analogContinuousSetAtten
^^^^^^^^^^^^^^^^^^^^^^^^
This function is used to set the attenuation for ADC continuous peripheral. For more informations refer to `analogSetAttenuation`_.
.. code-block:: arduino
void analogContinuousSetAtten(adc_attenuation_t attenuation);
* ``attenuation`` sets the attenuation (default is 11db).
analogContinuousSetWidth
^^^^^^^^^^^^^^^^^^^^^^^^
This function is used to set the hardware resolution bits.
Default value for all chips is 12bit (0 - 4095).
.. note:: This function will take effect only for ESP32 chip, as it allows to set resolution in range 9-12 bits.
.. code-block:: arduino
void analogContinuousSetWidth(uint8_t bits);
* ``bits`` sets resolution bits.
Example Applications
********************
Here is an example of how to use the ADC.
Here is an example of how to use the ADC in OneShot mode or you can run Arduino example 01.Basics -> AnalogReadSerial.
.. literalinclude:: ../../../libraries/ESP32/examples/AnalogRead/AnalogRead.ino
:language: arduino
Or you can run Arduino example 01.Basics -> AnalogReadSerial.
Here is an example of how to use the ADC in Continuous mode.
.. literalinclude:: ../../../libraries/ESP32/examples/AnalogReadContinuous/AnalogReadContinuous.ino
:language: arduino
// Define how many conversion per pin will happen and reading the data will be and average of all conversions
#define CONVERSIONS_PER_PIN 5
// Declare array of ADC pins that will be used for ADC Continuous mode - ONLY ADC1 pins are supported
// Number of selected pins can be from 1 to ALL ADC1 pins.
#ifdef CONFIG_IDF_TARGET_ESP32
uint8_t adc_pins[] = {36, 39, 34, 35}; //some of ADC1 pins for ESP32
#else
uint8_t adc_pins[] = {1, 2, 3, 4}; //ADC1 common pins for ESP32S2/S3 + ESP32C3/C6 + ESP32H2
#endif
// Calculate how many pins are declared in the array - needed as input for the setup function of ADC Continuous
uint8_t adc_pins_count = sizeof(adc_pins) / sizeof(uint8_t);
// Flag which will be set in ISR when conversion is done
volatile bool adc_coversion_done = false;
// Result structure for ADC Continuous reading
adc_continuos_data_t * result = NULL;
// ISR Function that will be triggered when ADC conversion is done
void ARDUINO_ISR_ATTR adcComplete() {
adc_coversion_done = true;
}
void setup() {
// Initialize serial communication at 115200 bits per second:
Serial.begin(115200);
// Optional for ESP32: Set the resolution to 9-12 bits (default is 12 bits)
analogContinuousSetWidth(12);
// Optional: Set different attenaution (default is ADC_11db)
analogContinuousSetAtten(ADC_11db);
// Setup ADC Continuous with following input:
// array of pins, count of the pins, how many conversions per pin in one cycle will happen, sampling frequency, callback function
analogContinuous(adc_pins, adc_pins_count, CONVERSIONS_PER_PIN, 20000, &adcComplete);
// Start ADC Continuous conversions
analogContinuousStart();
}
void loop() {
// Check if conversion is done and try to read data
if (adc_coversion_done == true) {
// Set ISR flag back to false
adc_coversion_done = false;
// Read data from ADC
if (analogContinuousRead(&result, 0)) {
// Optional: Stop ADC Continuous conversions to have more time to process (print) the data
analogContinuousStop();
for (int i = 0; i < adc_pins_count; i++) {
Serial.printf("\nADC PIN %d data:", result[i].pin);
Serial.printf("\n Avg raw value = %d", result[i].avg_read_raw);
Serial.printf("\n Avg milivolts value = %d", result[i].avg_read_mvolts);
}
// Delay for better readability of ADC data
delay(1000);
// Optional: If ADC was stopped, start ADC conversions and wait for callback function to set adc_coversion_done flag to true
analogContinuousStart();
}
else {
Serial.println("Error occured during reading data. Set Core Debug Level to error or lower for more informations.");
}
}
}
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment