ESP32集成了两个12位SAR(逐次逼近寄存器:Successive Approximation Register)ADC,总共支持18个测量通道(支持模拟的引脚)。
ADC驱动器API支持ADC1(8个通道,连接到GPIO 32-39)和ADC2(10个通道,连接到GPIO 0、2、4、12-15和25-27)。但是,ADC2的使用对应用程序有一些限制:
需要在程序中着重配置内容:精度+衰减倍数+通道引脚。
也就是ADC的位数配置、检测范围、接在哪个引脚的问题。
typedef enum {
ADC_WIDTH_BIT_9 = 0, /*!< ADC capture width is 9Bit*/
ADC_WIDTH_BIT_10 = 1, /*!< ADC capture width is 10Bit*/
ADC_WIDTH_BIT_11 = 2, /*!< ADC capture width is 11Bit*/
ADC_WIDTH_BIT_12 = 3, /*!< ADC capture width is 12Bit*/
ADC_WIDTH_MAX,
} adc_bits_width_t;
从上述的枚举中可知,ESP32的内置12位ADC可以在9位到12位的精度之间调整。
typedef enum {
ADC_ATTEN_DB_0 = 0, /*!
ADC_ATTEN_DB_2_5 = 1, /*!
ADC_ATTEN_DB_6 = 2, /*!
ADC_ATTEN_DB_11 = 3, /*!
ADC_ATTEN_MAX,
} adc_atten_t;
不同的衰减倍数对应不同的检测电压范围。
typedef enum {
ADC1_CHANNEL_0 = 0, /*!< ADC1 channel 0 is GPIO36 */
ADC1_CHANNEL_1, /*!< ADC1 channel 1 is GPIO37 */
ADC1_CHANNEL_2, /*!< ADC1 channel 2 is GPIO38 */
ADC1_CHANNEL_3, /*!< ADC1 channel 3 is GPIO39 */
ADC1_CHANNEL_4, /*!< ADC1 channel 4 is GPIO32 */
ADC1_CHANNEL_5, /*!< ADC1 channel 5 is GPIO33 */
ADC1_CHANNEL_6, /*!< ADC1 channel 6 is GPIO34 */
ADC1_CHANNEL_7, /*!< ADC1 channel 7 is GPIO35 */
ADC1_CHANNEL_MAX,
} adc1_channel_t;
typedef enum {
ADC2_CHANNEL_0 = 0, /*!< ADC2 channel 0 is GPIO4 */
ADC2_CHANNEL_1, /*!< ADC2 channel 1 is GPIO0 */
ADC2_CHANNEL_2, /*!< ADC2 channel 2 is GPIO2 */
ADC2_CHANNEL_3, /*!< ADC2 channel 3 is GPIO15 */
ADC2_CHANNEL_4, /*!< ADC2 channel 4 is GPIO13 */
ADC2_CHANNEL_5, /*!< ADC2 channel 5 is GPIO12 */
ADC2_CHANNEL_6, /*!< ADC2 channel 6 is GPIO14 */
ADC2_CHANNEL_7, /*!< ADC2 channel 7 is GPIO27 */
ADC2_CHANNEL_8, /*!< ADC2 channel 8 is GPIO25 */
ADC2_CHANNEL_9, /*!< ADC2 channel 9 is GPIO26 */
ADC2_CHANNEL_MAX,
} adc2_channel_t;
两个12位的ADC,其中ADC1(8个通道,连接到GPIO 32-39)和ADC2(10个通道,连接到GPIO 0、2、4、12-15和25-27)。
在Arduino层的编程由于经过了好几层的函数封装,很大程度的降低了我们编程配置难度。许多参数都已经默认设置好了。接下来我将先介绍Arduino层的函数调用,再挑几个重要的底层函数分析。
如果我们想用最懒的方法读取到模数转化数据,直接调用该函数即可读取到,无需其他的配置,全部使用系统默认的最佳配置即可,该配置可以满足绝大部分的需求。
仔细看代码,我们会发现在Arduino层在函数名并没有区分ADC1与ADC2,但是其函数内进行了区分,所以在调用时,我们一定要注意WIFI功能与ADC2的冲突。
/*
* Get ADC value for pin
* */
uint16_t __analogRead(uint8_t pin)
{
int8_t channel = digitalPinToAnalogChannel(pin);
int value = 0;
esp_err_t r = ESP_OK;
if(channel < 0){
log_e("Pin %u is not ADC pin!", pin);
return value;
}
__adcAttachPin(pin);
if(channel > 9){
channel -= 10;
r = adc2_get_raw( channel, __analogWidth, &value);
if ( r == ESP_OK ) {
return value;
} else if ( r == ESP_ERR_INVALID_STATE ) {
log_e("GPIO%u: %s: ADC2 not initialized yet.", pin, esp_err_to_name(r));
} else if ( r == ESP_ERR_TIMEOUT ) {
log_e("GPIO%u: %s: ADC2 is in use by Wi-Fi.", pin, esp_err_to_name(r));
} else {
log_e("GPIO%u: %s", pin, esp_err_to_name(r));
}
} else {
return adc1_get_raw(channel);
}
return value;
}
设置ADC1的读取精度,注意这是设置的ADC1的并不包括ADC2。analogReadResolution()可以从源代码中得知是为了兼容调用的analogSetWidth(),虽然设置范围还在1-16,但实际就只有在9-12(0 - 4095)之间。
/*
* Set the resolution of analogRead return values. Default is 12 bits (range from 0 to 4096).
* If between 9 and 12, it will equal the set hardware resolution, else value will be shifted.
* Range is 1 - 16
*
* Note: compatibility with Arduino SAM
*/
void __analogReadResolution(uint8_t bits)
{
if(!bits || bits > 16){
return;
}
__analogSetWidth(bits); // hadware from 9 to 12
}
/*
* Sets the sample bits and read resolution
* Default is 12bit (0 - 4095)
* Range is 9 - 12
* */
void __analogSetWidth(uint8_t bits){
if(bits < 9){
bits = 9;
} else if(bits > 12){
bits = 12;
}
__analogWidth = bits - 9;
adc1_config_width(__analogWidth);
}
设置引脚的衰减倍数,对应不同的电压检测范围,analogSetAttenuation()用于改变所有引脚衰减倍数,它通过直接改变__analogAttenuation全局变量的值,实现引脚初始化时的改变。
而analogSetPinAttenuation()则是对指定的引脚进行设置。要注意每个衰减倍数所对应的范围,在默认情况下是11db,其检测范围是0-3.6V。
static uint8_t __analogAttenuation = 3;//11db
typedef enum {
ADC_0db,
ADC_2_5db,
ADC_6db,
ADC_11db
} adc_attenuation_t;
/*
* Set the attenuation for all channels
* Default is 11db
* */
void analogSetAttenuation(adc_attenuation_t attenuation);
{
__analogAttenuation = attenuation & 3;
}
/*
* Set the attenuation for particular pin
* Default is 11db
* */
void analogSetPinAttenuation(uint8_t pin, adc_attenuation_t attenuation);
{
int8_t channel = digitalPinToAnalogChannel(pin);
if(channel < 0 || attenuation > 3){
return ;
}
if(channel > 9){
adc2_config_channel_atten(channel - 10, attenuation);
} else {
adc1_config_channel_atten(channel, attenuation);
}
__analogInit();
}
霍尔值读取。请注意,即使霍尔传感器也位于ESP32的内部,从ESP32读取也是使用ADC1的通道0和3(GPIO 36和39)。请勿将其他任何东西连接到这些引脚,也不要更改其配置。否则可能会影响传感器的低值信号的测量。
/*
* Get value for HALL sensor (without LNA)
* connected to pins 36(SVP) and 39(SVN)
* */
int hallRead();
{
int Sens_Vp0;
int Sens_Vn0;
int Sens_Vp1;
int Sens_Vn1;
pinMode(36, ANALOG);
pinMode(39, ANALOG);
SET_PERI_REG_MASK(SENS_SAR_TOUCH_CTRL1_REG, SENS_XPD_HALL_FORCE_M); // hall sens force enable
SET_PERI_REG_MASK(RTC_IO_HALL_SENS_REG, RTC_IO_XPD_HALL); // xpd hall
SET_PERI_REG_MASK(SENS_SAR_TOUCH_CTRL1_REG, SENS_HALL_PHASE_FORCE_M); // phase force
CLEAR_PERI_REG_MASK(RTC_IO_HALL_SENS_REG, RTC_IO_HALL_PHASE); // hall phase
Sens_Vp0 = __analogRead(36);
Sens_Vn0 = __analogRead(39);
SET_PERI_REG_MASK(RTC_IO_HALL_SENS_REG, RTC_IO_HALL_PHASE);
Sens_Vp1 = __analogRead(36);
Sens_Vn1 = __analogRead(39);
SET_PERI_REG_BITS(SENS_SAR_MEAS_WAIT2_REG, SENS_FORCE_XPD_SAR, 0, SENS_FORCE_XPD_SAR_S);
CLEAR_PERI_REG_MASK(SENS_SAR_TOUCH_CTRL1_REG, SENS_XPD_HALL_FORCE);
CLEAR_PERI_REG_MASK(SENS_SAR_TOUCH_CTRL1_REG, SENS_HALL_PHASE_FORCE);
return (Sens_Vp1 - Sens_Vp0) - (Sens_Vn1 - Sens_Vn0);
}