ESP32第一步-读懂代码之ADC单次扫描例程详解

一、创建一个新项目

1、打开ESP—IDF插件

2、新建文件

3、选择例程

一直下滑,知道看见下图中的选项

二、例程代码分析

首先,因为是例程代码,代码中有大量的条件编译。大家看的时候可以分块去看

1、ADC初始化

adc_oneshot_unit_handle_t adc1_handle;

定义了一个ADC_handle的变量,这个变量的作用是作为一个句柄

句柄(handle)在软件开发中是一种常见的抽象机制,用于管理和引用底层资源或对象。在处理硬件资源如ADC(模数转换器)时,句柄提供了一种方便且安全的方式来操作这些资源。

再具体的介绍大家自己在网上搜索一下吧,我在这里就不过多陈述了

adc_oneshot_unit_init_cfg_t init_config1 = {

.unit_id = ADC_UNIT_1,

};

adc_oneshot_unit_init_cfg_t 是一个用于配置ADC单次转换模式的结构体类型。

ESP_ERROR_CHECK(adc_oneshot_new_unit(&init_config1, &adc1_handle));

创建一个新的ADC1单元并获取句柄。

ESP_ERROR_CHECK的作用

是检查函数的返回值,在返回值错误时终止程序

adc_oneshot_new_unit

这个函数的作用:

1、创建新的ADC单元

  • 该函数会为一个新的ADC单元分配必要的资源,并将其配置为单次转换模式。
  • 这个过程可能包括初始化底层硬件、设置默认配置以及分配内存等。

2、返回句柄

  • 函数成功后,会返回一个 adc_oneshot_unit_handle_t 类型的句柄。这个句柄是用来管理和操作新创建的ADC单元的唯一标识符。
  • 通过这个句柄,可以调用其他API函数来进行进一步的配置、启动转换、读取结果等操作。

3、错误处理

  • 如果在创建过程中发生错误(例如资源不足、硬件故障等),函数可能会返回一个错误码,而不是有效的句柄。开发者可以通过检查返回值来判断是否成功创建了ADC单元。

ADC Init代码模块

//-------------ADC1 Init---------------//
adc_oneshot_unit_handle_t adc1_handle;
adc_oneshot_unit_init_cfg_t init_config1 = {
    .unit_id = ADC_UNIT_1,
};
ESP_ERROR_CHECK(adc_oneshot_new_unit(&init_config1, &adc1_handle));

2、ADC配置

typedef struct {

adc_atten_t atten; ///< ADC attenuation

adc_bitwidth_t bitwidth; ///< ADC conversion result bits

} adc_oneshot_chan_cfg_t;

在这里主要配置了ADC通道的位宽(分辨率)和衰减(增益)

其中的位宽和衰减的值大家自己跳转看一下吧

位宽(Bitwidth)

  • 成员bitwidth
  • 类型:通常是 adc_bitwidth_t 类型的枚举值。
  • 作用:指定ADC转换结果的位数,即ADC的分辨率。常见的位宽有12位、10位等。
  • 示例ADC_BITWIDTH_DEFAULT 通常是一个预定义的枚举值,表示默认的位宽。在ESP32中,默认的位宽通常是12位。

衰减(Attenuation)

  • 成员atten
  • 类型:通常是 adc_atten_t 类型的枚举值。
  • 作用:设置输入信号的衰减(增益),以适应ADC的输入范围。通过衰减,可以扩展ADC的测量范围,允许测量更高电压的信号。
  • 示例EXAMPLE_ADC_ATTEN 可能是一个预定义的枚举值,用于示例代码中的衰减设置。常见的衰减值包括 ADC_ATTEN_DB_0ADC_ATTEN_DB_2_5ADC_ATTEN_DB_6ADC_ATTEN_DB_11 等。

adc_oneshot_config_channel

以通道初始配置函数为例

给大家解析一下,其他地方的配置函数逻辑都是相同的,不同的就是参数了

handle && config

这是前面传过来的两个参数“句柄”和“通道基本参数配置信息”,这里是通过逻辑与的方式验证这两个指针是否有空,

ESP_ERR_INVALID_ARG

错误码,如果前面的逻辑与结果为0了,那么就返回这个错误码交由传参函数处理

TAG, "invalid argument: null pointer"

TAG,日志标签,一般都在文件的最前面定义了,大家可以自行查看

"invalid argument: null pointer",这个就是要打印出来的字符串,下面圈出来的是打印函数

大家跳转一下自己看看

 ESP_RETURN_ON_FALSE(handle && config, ESP_ERR_INVALID_ARG, TAG, "invalid argument: null pointer");

这里是判断“衰减参数值”是否有效,看他是否小于常量 4,如果不是的话,就不属于有效值

衰减参数的数值

SOC_ADC_ATTEN_NUM代表的数值

ESP_RETURN_ON_FALSE(config->atten < SOC_ADC_ATTEN_NUM, ESP_ERR_INVALID_ARG, TAG, "invalid attenuation");

下面这两个就是大同小异了,大家自己体会吧。加油!!

ESP_RETURN_ON_FALSE(((config->bitwidth >= SOC_ADC_RTC_MIN_BITWIDTH && config->bitwidth <= SOC_ADC_RTC_MAX_BITWIDTH) || config->bitwidth == ADC_BITWIDTH_DEFAULT), ESP_ERR_INVALID_ARG, TAG, "invalid bitwidth");
ESP_RETURN_ON_FALSE(channel < SOC_ADC_CHANNEL_NUM(handle->unit_id), ESP_ERR_INVALID_ARG, TAG, "invalid channel");

adc_oneshot_config_channel函数

esp_err_t adc_oneshot_config_channel(adc_oneshot_unit_handle_t handle, adc_channel_t channel, const adc_oneshot_chan_cfg_t *config)
{
    ESP_RETURN_ON_FALSE(handle && config, ESP_ERR_INVALID_ARG, TAG, "invalid argument: null pointer");
    ESP_RETURN_ON_FALSE(config->atten < SOC_ADC_ATTEN_NUM, ESP_ERR_INVALID_ARG, TAG, "invalid attenuation");
    ESP_RETURN_ON_FALSE(((config->bitwidth >= SOC_ADC_RTC_MIN_BITWIDTH && config->bitwidth <= SOC_ADC_RTC_MAX_BITWIDTH) || config->bitwidth == ADC_BITWIDTH_DEFAULT), ESP_ERR_INVALID_ARG, TAG, "invalid bitwidth");
    ESP_RETURN_ON_FALSE(channel < SOC_ADC_CHANNEL_NUM(handle->unit_id), ESP_ERR_INVALID_ARG, TAG, "invalid channel");

    s_adc_io_init(handle->unit_id, channel);

    adc_oneshot_hal_ctx_t *hal = &(handle->hal);
    adc_oneshot_hal_chan_cfg_t cfg = {
        .atten = config->atten,
        .bitwidth = (config->bitwidth == ADC_BITWIDTH_DEFAULT) ? SOC_ADC_RTC_MAX_BITWIDTH : config->bitwidth,
    };
    portENTER_CRITICAL(&rtc_spinlock);
    adc_oneshot_hal_channel_config(hal, &cfg, channel);
    if (handle->ulp_mode) {
        adc_oneshot_hal_setup(hal, channel);
    }
    portEXIT_CRITICAL(&rtc_spinlock);

    return ESP_OK;
}

ADC Config代码模块

    //-------------ADC1 Config---------------//
    adc_oneshot_chan_cfg_t config = {
        .bitwidth = ADC_BITWIDTH_DEFAULT,
        .atten = EXAMPLE_ADC_ATTEN,
    };
    ESP_ERROR_CHECK(adc_oneshot_config_channel(adc1_handle, EXAMPLE_ADC1_CHAN0, &config));
    ESP_ERROR_CHECK(adc_oneshot_config_channel(adc1_handle, EXAMPLE_ADC1_CHAN1, &config));

3、ADC 校准初始化

 adc_cali_handle_t adc1_cali_chan0_handle = NULL;

定义用来包含是否校准成功的标志的句柄,如果成功就接收“TRUE”,如果不成功继续保持NULL

bool do_calibration1_chan0 = example_adc_calibration_init(ADC_UNIT_1, EXAMPLE_ADC1_CHAN0, EXAMPLE_ADC_ATTEN, &adc1_cali_chan0_handle);

“2、ADC配置”中是配置通道的参数,这里是初始化通道的参数

这个是example_adc_calibration_init函数,这个函数的基本逻辑是,判断曲性校准和线性校准哪一个宏定义为1就执行哪个,如果都为一就优先曲性校准,如果都没有就默认校准失败

最下面有三种根据校准情况所对应的三种打印具体解析可以看后面“7、ADC校准函数”

ADC Calibration Init代码模块

    //-------------ADC1 Calibration Init---------------//
    adc_cali_handle_t adc1_cali_chan0_handle = NULL;
    adc_cali_handle_t adc1_cali_chan1_handle = NULL;
    bool do_calibration1_chan0 = example_adc_calibration_init(ADC_UNIT_1, EXAMPLE_ADC1_CHAN0, EXAMPLE_ADC_ATTEN, &adc1_cali_chan0_handle);
    bool do_calibration1_chan1 = example_adc_calibration_init(ADC_UNIT_1, EXAMPLE_ADC1_CHAN1, EXAMPLE_ADC_ATTEN, &adc1_cali_chan1_handle);

4、ADC2条件编译

条件编译,若果此处为1,则编译ADC2,如果为0 则不进行编译

5、while(1)中的ADC读取

框1和框2都是ADC1进行读取的过程、框3是ADC2进行读取的过程

while (1) {
        ESP_ERROR_CHECK(adc_oneshot_read(adc1_handle, EXAMPLE_ADC1_CHAN0, &adc_raw[0][0]));
        ESP_LOGI(TAG, "ADC%d Channel[%d] Raw Data: %d", ADC_UNIT_1 + 1, EXAMPLE_ADC1_CHAN0, adc_raw[0][0]);
        if (do_calibration1_chan0) {
            ESP_ERROR_CHECK(adc_cali_raw_to_voltage(adc1_cali_chan0_handle, adc_raw[0][0], &voltage[0][0]));
            ESP_LOGI(TAG, "ADC%d Channel[%d] Cali Voltage: %d mV", ADC_UNIT_1 + 1, EXAMPLE_ADC1_CHAN0, voltage[0][0]);
        }
        vTaskDelay(pdMS_TO_TICKS(1000));

        ESP_ERROR_CHECK(adc_oneshot_read(adc1_handle, EXAMPLE_ADC1_CHAN1, &adc_raw[0][1]));
        ESP_LOGI(TAG, "ADC%d Channel[%d] Raw Data: %d", ADC_UNIT_1 + 1, EXAMPLE_ADC1_CHAN1, adc_raw[0][1]);
        if (do_calibration1_chan1) {
            ESP_ERROR_CHECK(adc_cali_raw_to_voltage(adc1_cali_chan1_handle, adc_raw[0][1], &voltage[0][1]));
            ESP_LOGI(TAG, "ADC%d Channel[%d] Cali Voltage: %d mV", ADC_UNIT_1 + 1, EXAMPLE_ADC1_CHAN1, voltage[0][1]);
        }
        vTaskDelay(pdMS_TO_TICKS(1000));

#if EXAMPLE_USE_ADC2
        ESP_ERROR_CHECK(adc_oneshot_read(adc2_handle, EXAMPLE_ADC2_CHAN0, &adc_raw[1][0]));
        ESP_LOGI(TAG, "ADC%d Channel[%d] Raw Data: %d", ADC_UNIT_2 + 1, EXAMPLE_ADC2_CHAN0, adc_raw[1][0]);
        if (do_calibration2) {
            ESP_ERROR_CHECK(adc_cali_raw_to_voltage(adc2_cali_handle, adc_raw[1][0], &voltage[1][0]));
            ESP_LOGI(TAG, "ADC%d Channel[%d] Cali Voltage: %d mV", ADC_UNIT_2 + 1, EXAMPLE_ADC2_CHAN0, voltage[1][0]);
        }
        vTaskDelay(pdMS_TO_TICKS(1000));
#endif  //#if EXAMPLE_USE_ADC2
    }

6、资源释放模块 //Tear Down

这里主要的作用是释放之前的ADC资源和ADC校准资源,这里都是对句柄直接操作的,这也是句柄的一个作用

//Tear Down
    ESP_ERROR_CHECK(adc_oneshot_del_unit(adc1_handle));
    if (do_calibration1_chan0) {
        example_adc_calibration_deinit(adc1_cali_chan0_handle);
    }
    if (do_calibration1_chan1) {
        example_adc_calibration_deinit(adc1_cali_chan1_handle);
    }

如果使用了ADC2,就必须也对此进行资源释放

7、ADC校准函数 对应着“3、ADC校准初始化”

adc_cali_handle_t handle = NULL;

这里定义定义了一个句柄

esp_err_t ret = ESP_FAIL;

bool calibrated = false;

这个变量用来记录 从ret = adc_cali_create_scheme_curve_fitting(&cali_config, &handle);得到的返回值:

然后根据ret的状态值,决定给 calibrated 的赋值,大家自己在代码中跳转一下,看看各种标示被定义的状态值,以此来更加熟悉运行原理

接下来就是上图中的两种校准模式了,一种曲性一种线性,主要流程就是线配置,然后初始化,和stm32中是一样的其实,stm32中的ADC也是先定义一个指针,指向各种基本配置,然后进行Init初始化,之后进行校准。大概的原理都是一样的,只是换了一种编译环境,换了一套代码。

ADC校准函数模块

/*---------------------------------------------------------------
        ADC Calibration
---------------------------------------------------------------*/
static bool example_adc_calibration_init(adc_unit_t unit, adc_channel_t channel, adc_atten_t atten, adc_cali_handle_t *out_handle)
{
    adc_cali_handle_t handle = NULL;
    esp_err_t ret = ESP_FAIL;
    bool calibrated = false;

#if ADC_CALI_SCHEME_CURVE_FITTING_SUPPORTED
    if (!calibrated) {
        ESP_LOGI(TAG, "calibration scheme version is %s", "Curve Fitting");
        adc_cali_curve_fitting_config_t cali_config = {
            .unit_id = unit,
            .chan = channel,
            .atten = atten,
            .bitwidth = ADC_BITWIDTH_DEFAULT,
        };
        ret = adc_cali_create_scheme_curve_fitting(&cali_config, &handle);
        if (ret == ESP_OK) {
            calibrated = true;
        }
    }
#endif

#if ADC_CALI_SCHEME_LINE_FITTING_SUPPORTED
    if (!calibrated) {
        ESP_LOGI(TAG, "calibration scheme version is %s", "Line Fitting");
        adc_cali_line_fitting_config_t cali_config = {
            .unit_id = unit,
            .atten = atten,
            .bitwidth = ADC_BITWIDTH_DEFAULT,
        };
        ret = adc_cali_create_scheme_line_fitting(&cali_config, &handle);
        if (ret == ESP_OK) {
            calibrated = true;
        }
    }
#endif

    *out_handle = handle;
    if (ret == ESP_OK) {
        ESP_LOGI(TAG, "Calibration Success");
    } else if (ret == ESP_ERR_NOT_SUPPORTED || !calibrated) {
        ESP_LOGW(TAG, "eFuse not burnt, skip software calibration");
    } else {
        ESP_LOGE(TAG, "Invalid arg or no memory");
    }

    return calibrated;
}

8、反初始化和释放校准的函数

详细解释

1、条件编译
  • 该函数使用了条件编译来决定使用哪种校准方案进行反初始化。
  • 如果定义了 ADC_CALI_SCHEME_CURVE_FITTING_SUPPORTED,则执行曲线拟合校准方案的反初始化。
  • 如果定义了 ADC_CALI_SCHEME_LINE_FITTING_SUPPORTED,则执行线性拟合校准方案的反初始化。
2、日志记录
  • 使用 ESP_LOGI 宏记录一条信息日志,指示正在注销哪种校准方案。
  • 日志内容为 "deregister %s calibration scheme",其中 %s 会被替换为具体的校准方案名称(如 "Curve Fitting" 或 "Line Fitting")。
3、删除校准方案

曲线拟合校准方案

ESP_ERROR_CHECK(adc_cali_delete_scheme_curve_fitting(handle));
  • 调用 adc_cali_delete_scheme_curve_fitting 函数来删除曲线拟合校准方案,并释放相关资源。
  • handle 是之前创建的校准句柄。
  • ESP_ERROR_CHECK 宏用于检查函数调用是否成功。如果返回值不是 ESP_OK,则会记录错误信息并终止程序。

线性拟合校准方案

ESP_ERROR_CHECK(adc_cali_delete_scheme_line_fitting(handle));

  • 调用 adc_cali_delete_scheme_line_fitting 函数来删除线性拟合校准方案,并释放相关资源。
  • handle 是之前创建的校准句柄。
  • ESP_ERROR_CHECK 宏用于检查函数调用是否成功。如果返回值不是 ESP_OK,则会记录错误信息并终止程序。

具体作用

  • 反初始化校准方案:根据支持的校准方案(曲线拟合或线性拟合),删除相应的校准方案并释放相关资源。
  • 日志记录:记录注销校准方案的信息,便于调试和跟踪。
  • 错误处理:使用 ESP_ERROR_CHECK 宏确保操作成功,否则记录错误信息并终止程序。

反初始化和校准代码模块

static void example_adc_calibration_deinit(adc_cali_handle_t handle)
{
#if ADC_CALI_SCHEME_CURVE_FITTING_SUPPORTED
    ESP_LOGI(TAG, "deregister %s calibration scheme", "Curve Fitting");
    ESP_ERROR_CHECK(adc_cali_delete_scheme_curve_fitting(handle));

#elif ADC_CALI_SCHEME_LINE_FITTING_SUPPORTED
    ESP_LOGI(TAG, "deregister %s calibration scheme", "Line Fitting");
    ESP_ERROR_CHECK(adc_cali_delete_scheme_line_fitting(handle));
#endif
}
要下载ESP32-C3-LCD-EV板的示例程序,你可以按照以下步骤进行操作: 1. 首先,打开ESP32-C3-LCD-EV板的官方网站,通常可以在开发板的制造商网站或者ESP32的官方网站上找到。 2. 在官方网站上,找到关于ESP32-C3-LCD-EV板的页面或者单独的板支持页面。这个页面通常会提供板的相关信息、规格和下载链接。 3. 查询页面上是否有与板子配套使用的IDE或者开发环境。如果有,下载并安装它们。 4. 在官方网站的页面上查找示例程序的下载链接。它们通常会以压缩文件的形式提供,可以点击链接进行下载。 5. 下载示例程序后,解压缩文件到你选择的位置。确保你已经安装了所需的开发环境,以便能够打开和编辑这些示例程序。 6. 打开解压缩后的示例程序文件夹,并查找一个入口文件(通常命名为main.c或者main.ino)。这是一个示例程序的主文件,你可以在此基础上进行修改和调试。 7. 使用你的IDE或者开发工具打开入口文件,然后将板子连接到电脑上。通常需要使用USB线缆将ESP32-C3-LCD-EV板连至电脑。 8. 在IDE或者开发工具中选择合适的开发设备(通常是ESP32系列)和端口(通常是USB端口),然后编译并烧录示例程序到板子上。 9. 编译和烧写完成后,断开ESP32-C3-LCD-EV板与电脑的连接,然后重新连接板子供电。 10. 示范程序应该会开始在板子上运行,你可以观察板子上的LCD显示屏或者其他指示灯来确认是否成功下载和运行了示例程序。 以上就是下载ESP32-C3-LCD-EV板示例程序的一般步骤。具体步骤可能因为开发环境、操作系统和板子的不同而有所差异。如果在下载或者使用示例程序的过程中遇到问题,建议参考官方文档、开发者社区或者相关论坛寻求帮助。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值