侧边栏壁纸
  • 累计撰写 125 篇文章
  • 累计创建 16 个标签
  • 累计收到 4 条评论

目 录CONTENT

文章目录
ESP32   

ESP32-ADC学习

概述

ESP32集成了两个12位SAR(逐次逼近寄存器:Successive Approximation Register)ADC,总共支持18个测量通道(支持模拟的引脚)。
ESP32引脚功能示意图
ADC驱动器API支持ADC1(8个通道,连接到GPIO 32-39)和ADC2(10个通道,连接到GPIO 0、2、4、12-15和25-27)。但是,ADC2的使用对应用程序有一些限制:

  • Wi-Fi驱动程序使用了ADC2。因此,应用程序只能在未启动Wi-Fi驱动程序时使用ADC2。
  • 在一些开发板中ADC2引脚用作捆绑引脚(GPIO 0、2、15),因此不能自由使用。例如GPIO0:用于选择运行方式

ADC配置重点

需要在程序中着重配置内容:精度+衰减倍数+通道引脚
也就是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,  /*!<The input voltage of ADC will be reduced to about 1/1 */
    ADC_ATTEN_DB_2_5 = 1,  /*!<The input voltage of ADC will be reduced to about 1/1.34 */
    ADC_ATTEN_DB_6   = 2,  /*!<The input voltage of ADC will be reduced to about 1/2 */
    ADC_ATTEN_DB_11  = 3,  /*!<The input voltage of ADC will be reduced to about 1/3.6*/
    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层的函数调用,再挑几个重要的底层函数分析。

analogRead(uint8_t pin);

如果我们想用最懒的方法读取到模数转化数据,直接调用该函数即可读取到,无需其他的配置,全部使用系统默认的最佳配置即可,该配置可以满足绝大部分的需求。
仔细看代码,我们会发现在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;
}

analogReadResolution(uint8_t bits); && analogSetWidth(uint8_t bits);

设置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() & analogSetPinAttenuation()

设置引脚的衰减倍数,对应不同的电压检测范围,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();
}

hallRead();

霍尔值读取。请注意,即使霍尔传感器也位于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);
}
0

评论区