esp32学习笔记(4)——adc

这篇具有很好参考价值的文章主要介绍了esp32学习笔记(4)——adc。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。


前言

ADC即模拟数字转换器(Analog-to-digital converter)是用于将模拟形式的连续信号转换数字形式的离散信号的一类设备。一个模拟数字转换器可以提供信号用于测量。与之相对的设备成为数字模拟转换器。
例如温度、压力、声音或者图像等,需要转换成更容易储存、处理和发射的数字形式。那就可以用到ADC了


提示:以下是本篇文章正文内容,下面案例可供参考

一、ESP32 ADC相关介绍

一些 ADC2 引脚用作捆绑引脚(GPIO 0、2、15),因此不能自由使用。
ESP32 DevKitC:由于外部自动编程电路,GPIO 0 无法使用。
ESP-WROVER-KIT:GPIO 0、2、4 和 15 不能使用,因为用于不同目的的外部连接。
由于 ADC2 模块也被 Wi-Fi 使用,因此它们一起使用时只有一个可以抢占,这意味着adc2_get_raw()可能会被阻塞直到 Wi-Fi 停止

ESP32 集成了 2 个 SAR(逐次逼近寄存器)ADC,总共支持 18 个测量通道(模拟使能引脚)。
ADC1,8通道:GPIO32 - GPIO39
ADC2,10个通道:GPIO0、GPIO2、GPIO4、GPIO12 - GPIO15、GOIO25 - GPIO27
以下介绍通过ADC1进行介绍
2、ADC衰减
Vref 是 ESP32 ADC 内部用于测量输入电压的参考电压。ESP32 ADC 可以测量从 0 V 到 Vref 的模拟电压。在不同的芯片中,Vref 不同,中位数为 1.1 V。为了转换大于 Vref 的电压,可以在输入 ADC 之前对输入电压进行衰减。有 4 种可用的衰减选项,衰减越高,可测量的输入电压就越高。
esp32学习笔记(4)——adc
3、ADC 转换
ADC 转换是将输入模拟电压转换为数字值。ADC 驱动程序 API (adc1_get_raw())提供的 ADC 转换结果是原始数据。Single Read 模式下 ESP32 ADC 原始结果的分辨率为 12 位。
如果根据ADC采集的原始数据来计算电压那可以用Vout(输出电压)=Dout(输出的数据)*Vmax(最大测量电压)/Dmax(最大输出数据)
ps:如果是带有ADC校准位的板子可以直接调用esp_adc_cal_raw_to_voltage()来直接读取输出电压,单位为mv
4、ADC 单次读取
在配置好位宽adc1_config_width()和衰减adc1_config_channel_atten()之后就可以直接调用adc1_get_raw(),读取结果了;值得说明的是官方也提供给了从ULP直接读取ADC1通道的结果,但是这个要在配置的时候调用adc1_ulp_enable()来使能ULP

二、使用步骤

1.接口函数介绍

(1)设置通道衰减

esp_err_t adc1_config_channel_atten(adc1_channel_t channel,adc_atten_t atten )

第一个参数是设置ADC通道,第二个参数是设置衰减等级;默认衰减是0dB;通过官方给的衰减设置可以看到,衰减设置的越大,那么可采集的电压范围就越大(通过对输入电压进行衰减)
返回值:
ESP_OK 成功
ESP_ERR_INVALID_ARG 参数错误
esp32学习笔记(4)——adc

esp32学习笔记(4)——adc
(2)配置ADC的捕获位宽

esp_err_t adc1_config_width( adc_bits_width_t width_bit )

参数:ADC1 的位捕获宽度
返回值:
ESP_OK 成功
ESP_ERR_INVALID_ARG 参数错误
esp32学习笔记(4)——adc

(3)读取单个通道上的ADC数据

int adc1_get_raw( adc1_channel_t channel)

返回
-1:参数错误
其他:ADC1 通道读数。
参数
channel: ADC1 通道读取

(4)初始化数字ADC

esp_err_t adc_digi_initialize(const adc_digi_init_config_t *init_config )

参数:数字ADC的初始化配置adc_digi_init_config_t 在这个结构体里面;
返回:

    • ESP_ERR_INVALID_ARG 参数错误
    • ESP_ERR_NOT_FOUND 没有找到中断空闲
    • ESP_ERR_NO_MEM 内存不足
    • ESP_OK 成功
      esp32学习笔记(4)——adc
      结构体中参数依次是
      1)转换后的数据可以存储的最大长度。
      2)在一个中断中可以转换的数据字节数
      3)待初始化的ADC1通道列表。
      4)需要初始化的ADC2通道列表。

(5)通过 DMA 从数字 ADC 读取字节。

esp_err_t adc_digi_read_bytes(uint8_t *buf, uint32_t length_max, uint32_t *out_length, uint32_t timeout_ms);

[out] buf:从 ADC 读取的缓冲区。
[in] length_max:从 ADC 读取的预期数据长度。
[out] out_length:从 ADC 读取的数据的实际长度。
[in] timeout_ms:等待数据的时间,以毫秒为单位。
返回
ESP_ERR_INVALID_STATE 驱动状态无效。通常这意味着 ADC 采样率快于任务处理率。
ESP_ERR_TIMEOUT 操作超时
ESP_OK 成功
(6)启动数字ADC和 DMA

esp_err_t adc_digi_start(void);

返回
ESP_ERR_INVALID_STATE 驱动状态无效。
ESP_OK 成功
(6)设置数字控制器

esp_err_t adc_digi_controller_configure(const adc_digi_configuration_t *config);

返回值:
ESP_ERR_INVALID_STATE 驱动状态无效。
ESP_ERR_INVALID_ARG 如果参数组合无效。
ESP_OK 成功
参数为adc_digi_configuration_t 结构体
esp32学习笔记(4)——adc
结构体中参数依次是
1)限制ADC转换的时间,转换完成后是否停止
2)限制ADC转换触发器上限1-255
3)ADC通道数
4)初始化结构体配置参数
5)采样频率611Hz ~ 83333Hz
6)ADC DMA传输模式:选择ADC1、ADC2、ADC1与ADC2、ADC1与ADC2轮流
7)ADC DMA传输转换格式:12BIT转换/11BIT转换
esp32学习笔记(4)——adc
esp32学习笔记(4)——adc

对于设置的输出格式来说如果设置输出12BIT的数据,那么输出的16位中存放格式是[15:12] 通道,[11:0]12 位 ADC 数据 。注意:对于单次转换模式。
如果设置的数是11BIT的数据那么存放内容就是[15]adc 单元,[14:11]通道,[10:0]11 位 ADC 数据 。注意:对于多或交替转换模式。

(7)检查 ADC 校准值是否烧入 eFuse

esp_err_t esp_adc_cal_check_efuse( esp_adc_cal_value_t value_type )

检查 ADC 参考电压或两点值是否已烧到当前 ESP32 的 eFuse
返回:
ESP_OK:eFuse 支持校准模式
ESP_ERR_NOT_SUPPORTED:错误,eFuse 值未烧入
ESP_ERR_INVALID_ARG:错误,无效参数(ESP_ADC_CAL_VAL_DEFAULT_VREF)
参数:
value_type:校准值的类型(ESP_ADC_CAL_VAL_EFUSE_VREF 或 ESP_ADC_CAL_VAL_EFUSE_TP)
(8)以特定衰减表征 ADC,
esp_adc_cal_value_t esp_adc_cal_characterize( adc_unit_t adc_num , adc_atten_t atten , adc_bits_width_t bit_width , uint32_t default_vref , esp_adc_cal_characteristics_t * chars )
返回:
ESP_ADC_CAL_VAL_EFUSE_VREF:用于表征的 eFuse Vref
ESP_ADC_CAL_VAL_EFUSE_TP:用于表征的两点值(仅在线性模式下)
ESP_ADC_CAL_VAL_DEFAULT_VREF:用于表征的默认 Vref
参数:
[in] adc_num:ADC_UNIT_1 或 ADC_UNIT_2
[in] atten: 衰减
[in] bit_width: ADC的位宽配置
[in] default_vref:默认 ADC 参考电压,单位 mV
[out] chars: 指向用于存储 ADC 特征的空结构的指针
(9)将 ADC 读数转换为以 mV 为单位的电压。
uint32_t esp_adc_cal_raw_to_voltage(uint32_t adc_reading, const esp_adc_cal_characteristics_t *chars);
在调用此函数之前必须初始化特征结构(调用 esp_adc_cal_characterize())
返回:
电压 (mV)
参数:
[in] adc_reading: ADC 读数
[in] chars: 指向包含 ADC 特征的初始化结构的指针
(10)直接获取通道以 mV 为单位的电压。
esp_err_t esp_adc_cal_get_voltage(adc_channel_t channel, const esp_adc_cal_characteristics_t *chars, uint32_t *voltage);
这个函数同(9)函数一样都是得到电压,但是(9)传参是ADC的读数,本函数传参是ADC通道
参数
[in] channel:要读取的 ADC 通道
[in] chars: 指向已初始化 ADC 特征结构的指针
[out] voltage:存储转换电压的指针
返回
ESP_OK:ADC 读取并转换为 mV
ESP_ERR_INVALID_ARG:由于参数无效导致的错误
ESP_ERR_INVALID_STATE:读取结果无效。尝试再次阅读。

2.代码示例

ADC+DMA
my_adc.c

#include "my_adc.h"

#if CONFIG_IDF_TARGET_ESP32S2
static uint16_t adc1_chan_mask = BIT(0);
static uint16_t adc2_chan_mask = 0;
static adc_channel_t channel[1] = {ADC1_CHANNEL_0};
#endif
#if CONFIG_IDF_TARGET_ESP32
static uint16_t adc1_chan_mask = BIT(0);//
static uint16_t adc2_chan_mask = 0;
static adc_channel_t channel[1] = {ADC1_CHANNEL_0};
#endif


static void continuous_adc_init(uint16_t adc1_chan_mask, uint16_t adc2_chan_mask, adc_channel_t *channel, uint8_t channel_num)
{
    adc_digi_init_config_t adc_dma_config = {
        .max_store_buf_size = 1024,//可以传输的字节大小
        .conv_num_each_intr = TIMES,//一個中斷可以转换的字節數
        .adc1_chan_mask = adc1_chan_mask,//待初始化的通道列表
        .adc2_chan_mask = adc2_chan_mask,
    };
    ESP_ERROR_CHECK(adc_digi_initialize(&adc_dma_config));

    adc_digi_configuration_t dig_cfg = {
        .conv_limit_en = ADC_CONV_LIMIT_EN,//限制ADC转换的次数,在转换conv_limit_num之后就会停止,选择是否使能
        .conv_limit_num = 250,//设置ADC触发器的上限
        .sample_freq_hz = 10 * 1000,//ADC采样频率
        .conv_mode = ADC_CONV_MODE,//选择DMA转换模式,选择使用哪个ADC或者都使用
        .format = ADC_OUTPUT_TYPE,//设置数据输出格式
    };

    adc_digi_pattern_config_t adc_pattern[SOC_ADC_PATT_LEN_MAX] = {0};//将使用ADC通道的配置列表
    dig_cfg.pattern_num = channel_num;
    for (int i = 0; i < channel_num; i++) {
        uint8_t unit = GET_UNIT(channel[i]);
        uint8_t ch = channel[i] & 0x7;//ADC通道,总共0-7八个通道,只看后三位
        adc_pattern[i].atten = ADC_ATTEN_DB_11;//衰减
        adc_pattern[i].channel = ch;//通道
        adc_pattern[i].unit = unit;//索引
        adc_pattern[i].bit_width = SOC_ADC_DIGI_MAX_BITWIDTH;//设置位宽为最大宽度

        ESP_LOGI(TAG, "adc_pattern[%d].atten is :%x", i, adc_pattern[i].atten);
        ESP_LOGI(TAG, "adc_pattern[%d].channel is :%x", i, adc_pattern[i].channel);
        ESP_LOGI(TAG, "adc_pattern[%d].unit is :%x", i, adc_pattern[i].unit);
    }
    dig_cfg.adc_pattern = adc_pattern;
    ESP_ERROR_CHECK(adc_digi_controller_configure(&dig_cfg));
}


bool check_valid_data(const adc_digi_output_data_t *data)//判断数据是不是有效的
{
    const unsigned int unit = data->type2.unit;
    if (unit > 2) {
        return false;
    }
    if (data->type2.channel >= SOC_ADC_CHANNEL_NUM(unit)) {
        return false;//判断通道数是不是配置多了
    }
    
    return true;
}

void my_adc_dma_init(void)
{
    continuous_adc_init(adc1_chan_mask, adc2_chan_mask, channel, sizeof(channel) / sizeof(adc_channel_t));//初始化配置
}

main.c

#include "my_adc.h"

// #define LEDC_MAX_DUTY   (8191)
// #define LEDC_FADE_TIME  (1000)
void app_main(void)
{
    esp_err_t ret;
    uint32_t ret_num = 0;
    uint8_t result[TIMES] = {0};
    memset(result, 0xcc, TIMES);//把数组result里面的times个值都初始化成0xcc=204;
    // continuous_adc_init(adc1_chan_mask, adc2_chan_mask, channel, sizeof(channel) / sizeof(adc_channel_t));//初始化配置
    my_adc_dma_init();
    adc_digi_start();//启动转换
    while (1)
    {
        ret = adc_digi_read_bytes(result, TIMES, &ret_num, ADC_MAX_DELAY);//读取转换数据,数据存在result里面,预期是256个,实际是ret_num个。转换超时时间限制是最大
        //判断adc_digi_read_bytes返回的参数,成功、驱动设备未安装(残阳速率大于任务处理速率)、超时
        if (ret == ESP_OK || ret == ESP_ERR_INVALID_STATE) 
        {
            if (ret == ESP_ERR_INVALID_STATE) 
            {
                /**
                 * 对于普通的只打印任务处理速率还是很快的
                 * 
                 * @note 1
                 * Issue:
                 * As an example, we simply print the result out, which is super slow. Therefore the conversion is too
                 * fast for the task to handle. In this condition, some conversion results lost.
                 *
                 * Reason:
                 * When this error occurs, you will usually see the task watchdog timeout issue also.
                 * Because the conversion is too fast, whereas the task calling `adc_digi_read_bytes` is slow.
                 * So `adc_digi_read_bytes` will hardly block. Therefore Idle Task hardly has chance to run. In this
                 * example, we add a `vTaskDelay(1)` below, to prevent the task watchdog timeout.
                 *
                 * Solution:
                 * Either decrease the conversion speed, or increase the frequency you call `adc_digi_read_bytes`
                 */
                printf("ERROR:请降低转换速度,或者增加调用adc_digi_read_bytes的频率\n" );
            }
            for (int i = 0; i < ret_num; i += ADC_RESULT_BYTE) 
            {
                adc_digi_output_data_t *p = (void*)&result[i]; //把result中的数据传到adc_digi_output_data_t中,(void*)就是可以把数据转换成任意类型的数据
    #if CONFIG_IDF_TARGET_ESP32
                ESP_LOGI(TAG, "Unit: %d, Channel: %d, Value: %x", 1, p->type1.channel, p->type1.data);//如果选择的是ESP32那么输出使用的哪个ADC的哪个通道以及那种类型的数据
    #else
                if (ADC_CONV_MODE == ADC_CONV_BOTH_UNIT || ADC_CONV_MODE == ADC_CONV_ALTER_UNIT) //判断ADC转换模式时ADC1、2,或者是轮流转换
                {
                    //判断数据是否有效
                    if (check_valid_data(p))
                    {
                        ESP_LOGI(TAG, "Unit: %d,_Channel: %d, Value: %x", p->type2.unit+1, p->type2.channel, p->type2.data);//输出有效数据
                    } else 
                    {
                        // abort();
                        ESP_LOGI(TAG, "Invalid data [%d_%d_%x]", p->type2.unit+1, p->type2.channel, p->type2.data);//输出数据无效
                    }
                }
    #if CONFIG_IDF_TARGET_ESP32S2
                else if (ADC_CONV_MODE == ADC_CONV_SINGLE_UNIT_2) 
                {
                    ESP_LOGI(TAG, "Unit: %d, Channel: %d, Value: %x", 2, p->type1.channel, p->type1.data);
                } else if (ADC_CONV_MODE == ADC_CONV_SINGLE_UNIT_1) 
                {
                    ESP_LOGI(TAG, "Unit: %d, Channel: %d, Value: %d", 1, p->type1.channel, p->type1.data);
                }
    #endif  //#if CONFIG_IDF_TARGET_ESP32S2
    #endif
            }
            //See `note 1`
            vTaskDelay(1);//程序运行太快,防止任务看门狗超时
        } else if (ret == ESP_ERR_TIMEOUT) {
            /**
             * ``ESP_ERR_TIMEOUT``: If ADC conversion is not finished until Timeout, you'll get this return error.
             * Here we set Timeout ``portMAX_DELAY``, so you'll never reach this branch.
             */
            ESP_LOGW(TAG, "No data, increase timeout or reduce conv_num_each_intr");
            vTaskDelay(1000);
        }
    }  
    adc_digi_stop();
    ret = adc_digi_deinitialize();
    assert(ret == ESP_OK);
}


总结

完整代码工程在这里:代码文章来源地址https://www.toymoban.com/news/detail-417841.html

到了这里,关于esp32学习笔记(4)——adc的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • 【ESP32最全学习笔记(协议篇)——2.ESP32 LoRa】

    关于本教程: 1.ESP32简介                                                                 2.ESP32 Arduino 集成开发环境 3.VS 代码和 PlatformIO 4.ESP32 引脚 5.ESP32 输入输出 6.ESP32 脉宽调制 7.ESP32 模拟输入 8.ESP32 中断定时器 9 .ESP32 深度睡眠 1.ESP32 网络服

    2024年02月04日
    浏览(48)
  • 【ESP32最全学习笔记(基础篇)——4.ESP32 引脚介绍】

    关于本教程: 1.ESP32简介                                                                 2.ESP32 Arduino 集成开发环境 3.VS 代码和 PlatformIO 4.ESP32 引脚 ☑  5.ESP32 输入输出 6.ESP32 脉宽调制 7.ESP32 模拟输入 8.ESP32 中断定时器 9.ESP32 深度睡眠 ESP32 网络

    2024年02月03日
    浏览(36)
  • 【ESP32最全学习笔记(基础篇)——9.ESP32 深度睡眠模式】

    关于本教程: 1.ESP32简介                                                                 2.ESP32 Arduino 集成开发环境 3.VS 代码和 PlatformIO 4.ESP32 引脚 5.ESP32 输入输出 6.ESP32 脉宽调制 7.ESP32 模拟输入 8.ESP32 中断定时器 9.ESP32 深度睡眠 ☑

    2024年02月05日
    浏览(49)
  • 【ESP32最全学习笔记(基础篇)——8.ESP32 中断定时器】

    关于本教程: 1.ESP32简介                                                                 2.ESP32 Arduino 集成开发环境 3.VS 代码和 PlatformIO 4.ESP32 引脚 5.ESP32 输入输出 6.ESP32 脉宽调制 7.ESP32 模拟输入 8.ESP32 中断定时器 ☑ 9 .ESP32 深度睡眠 ESP32 网络

    2024年02月06日
    浏览(37)
  • ESP32学习笔记 MQTT协议

    MQTT(Message Queuing Telemetry Transport,消息队列遥测传输协议),信息传输的对象我分为 客户端消息发布者 (pub)、 服务器 (server)、 客户端消息订阅者 (sub), 一个客户端可以同时为发布者和订阅者 。服务器,服务器为信息传输的枢纽有传递数据、管理客户端、数据保真等作用。

    2024年02月08日
    浏览(37)
  • esp32学习笔记(4)——adc

    ADC即模拟数字转换器(Analog-to-digital converter)是用于将 模拟 形式的 连续 信号 转换 为 数字 形式的 离散 信号的一类设备。一个模拟数字转换器可以提供信号用于测量。与之相对的设备成为数字模拟转换器。 例如温度、压力、声音或者图像等,需要转换成更容易储存、处理

    2023年04月19日
    浏览(29)
  • 【ESP32最全学习笔记(基础篇)——2.ESP32 Arduino 集成开发环境】

    关于本教程: 1.ESP32简介                                                                 2.ESP32 Arduino 集成开发环境 ☑ 3.VS 代码和 PlatformIO 4.ESP32 引脚 5.ESP32 输入输出 6.ESP32 脉宽调制 7.ESP32 模拟输入 8.ESP32 中断定时器 9 .ES P32 深度睡眠 ESP32 网络

    2024年02月10日
    浏览(38)
  • ESP32学习笔记(七) 复位和时钟

    目录: ESP32学习笔记(一) 芯片型号介绍 ESP32学习笔记(二) 开发环境搭建 VSCode+platformio ESP32学习笔记(三) 硬件资源介绍 ESP32学习笔记(四) 串口通信 ESP32学习笔记(五) 外部中断 ESP32学习笔记(六) 定时器 ESP32学习笔记(七) 复位和时钟 从时钟树可以看出 时钟源共七种 ESP32 的时钟源分别

    2024年01月16日
    浏览(31)
  • 学习笔记ESP32——lvgl disp移植(1)

    目录 一、简介 二、GitHub上下载lvgl库         1、下载lvgl库 三、准备一个工程  四、修改工程  五、对disp进行修改 ​编辑 六、主函数的修改 七、硬件的修改 八、MUSIC示例演示 一、简介         以esp32 idf导出的hello world例程,移植lvgl disp和indev驱动的学习笔记。 二、Gi

    2024年01月16日
    浏览(30)
  • 【EPS32S3学习笔记】ESP32+OPENCV+人脸识别 本地部署

    提示:这里可以添加系列文章的所有文章的目录,目录需要自己手动添加 例如:第一章 Python 机器学习入门之pandas的使用 提示:写完文章后,目录可以自动生成,如何生成可参考右边的帮助文档 提示:这里可以添加本文要记录的大概内容: 从https://github.com/joachimBurket/esp32-

    2024年02月09日
    浏览(49)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包