ESP32-C3入门教程 基础篇(一、ADC采样)

举报
矜辰所致 发表于 2022/09/25 06:01:59 2022/09/25
【摘要】 经过前面的折腾,设计好了自己的测试开发板 搭建好了开发环境, 然后正式开始进行功能测试了,测试顺序先从简单的开始吧,一步一步来 12 目录 前言1、ADC采样示例测试1.1 DMA连续采样1.2...
经过前面的折腾,设计好了自己的测试开发板 搭建好了开发环境,
然后正式开始进行功能测试了,测试顺序先从简单的开始吧,一步一步来

  
 
  • 1
  • 2

前言

接下来的ESP32-C3 功能测试都是基于自己设计的开发板:

自己画一块ESP32-C3 的开发板(第一次使用立创EDA)(PCB到手)

开发环境是乐鑫官方的 ESP-IDF, 基于VScode插件搭建好的:

ESP32-C3 VScode开发环境搭建(基于乐鑫官方ESP-IDF——Windows和Ubuntu双环境)

1、ADC采样示例测试

新建一个ADC采样的工程,当然是基于官方的ADC示例代码建立的,建立工程的方式在上面开发环境搭建的示例测试章节有图文说明:
在这里插入图片描述

1.1 DMA连续采样

示例代码有2个函数,单次检测 和 DMA连续检测,分别接在如下通道上面:

在这里插入图片描述开发板上面,我们只预留了一个ADC接口,就是ADC1_CHANNEL_0,连接的是一个光敏电阻:
在这里插入图片描述
所以需要对示例进行稍微修改,主要是对读取函数,只设置 ADC1_CHANNEL_0 ,如下图:

在这里插入图片描述
在主函数中只调用continuous_read(NULL);函数,测试结果如下:

在这里插入图片描述

1.2 单次采样

单次采样比较简单,也是直接在上面的样例中修改,下面直接上修改后的测试代码:

static void single_read(void *arg)
{
    // esp_err_t ret;
    // int adc1_reading[3] = {0xcc};
    int adc1_reading[1] = {0xcc};
    // int adc2_reading[1] = {0xcc};
    float vout;
    // const char TAG_CH[][10] = {"ADC1_CH2", "ADC1_CH3","ADC1_CH4", "ADC2_CH0"};
    const char TAG_CH[1][10] = {"ADC1_CH0"};

    adc1_config_width(ADC_WIDTH_BIT_DEFAULT);
    adc1_config_channel_atten(ADC1_CHANNEL_0, ADC_ATTEN_DB_11);
    // adc1_config_channel_atten(ADC1_CHANNEL_3, ADC_ATTEN_DB_6);
    // adc1_config_channel_atten(ADC1_CHANNEL_4, ADC_ATTEN_DB_0);
    // adc2_config_channel_atten(ADC2_CHANNEL_0, ADC_ATTEN_DB_0);
    
    // int n = 20;
    // while (n--) {
    while (1) {

        adc1_reading[0] = adc1_get_raw(ADC1_CHANNEL_0);
        // adc1_reading[1] = adc1_get_raw(ADC1_CHANNEL_3);
        // adc1_reading[2] = adc1_get_raw(ADC1_CHANNEL_4);
        vout = (adc1_reading[0] * 2500.00)/4095.00;
        ESP_LOGI(TAG_CH[0], "%x vout mv is %f", adc1_reading[0],vout);

        
        // for (int i = 0; i < 3; i++) {
        //     ESP_LOGI(TAG_CH[i], "%x", adc1_reading[i]);
        // }
        // ret = adc2_get_raw(ADC2_CHANNEL_0, ADC_WIDTH_BIT_12, &adc2_reading[0]);
        // assert(ret == ESP_OK);
        // ESP_LOGI(TAG_CH[3], "%x", adc2_reading[0]);
        vTaskDelay(500 / portTICK_PERIOD_MS);
    }
}

void app_main(void)
{
    single_read(NULL);
    //continuous_read(NULL);
}

  
 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42

测试效果如下:
在这里插入图片描述

1.3 测试源码

上一份自己稍微修改的最后测试的adc_dma_example_main.c源码

  • 针对自己的板子只有一个 ADC 接口进行代码精简
  • 增加实际电压值的计算输出
  • LED切换表示采样一次
  • 注释部分为了避免警告需要自行去掉,使用单次模式注释连续采样代码,反之一样
#include <string.h>
#include <stdio.h>
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#include "freertos/semphr.h"
#include "esp_log.h"
#include "driver/adc.h"
#include "driver/gpio.h"

#define TIMES 256

// static void continuous_adc_init(uint16_t adc1_chan_mask, uint16_t adc2_chan_mask, adc_channel_t *channel, uint8_t channel_num)
// {
//     esp_err_t ret = ESP_OK;
//     assert(ret == ESP_OK);

//     adc_digi_init_config_t adc_dma_config = {
//         .max_store_buf_size = 1024,
//         .conv_num_each_intr = 256,
//         .adc1_chan_mask = adc1_chan_mask,
//         .adc2_chan_mask = adc2_chan_mask,
//     };
//     ret = adc_digi_initialize(&adc_dma_config);
//     assert(ret == ESP_OK);

//     adc_digi_pattern_table_t adc_pattern[10] = {0};

//     //Do not set the sampling frequency out of the range between `SOC_ADC_SAMPLE_FREQ_THRES_LOW` and `SOC_ADC_SAMPLE_FREQ_THRES_HIGH`
//     adc_digi_config_t dig_cfg = {
//         .conv_limit_en = 0,
//         .conv_limit_num = 250,
//         .sample_freq_hz = 620,
//     };

//     dig_cfg.adc_pattern_len = channel_num;
//     for (int i = 0; i < channel_num; i++) {
//         uint8_t unit = ((channel[i] >> 3) & 0x1);
//         uint8_t ch = channel[i] & 0x7;
//         adc_pattern[i].atten = ADC_ATTEN_DB_11;
//         adc_pattern[i].channel = ch;
//         adc_pattern[i].unit = unit;
//     }
//     dig_cfg.adc_pattern = adc_pattern;
//     ret = adc_digi_controller_config(&dig_cfg);
//     assert(ret == ESP_OK);
// }

// static 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;
// }

// static void continuous_read(void *arg)
// {
//     esp_err_t ret;
//     uint32_t ret_num = 0;
//     uint8_t result[TIMES] = {0};
//     memset(result, 0xcc, TIMES);
//     float vout;

//     // uint16_t adc1_chan_mask = BIT(0) | BIT(1);
//     uint16_t adc1_chan_mask = BIT(0);
//     uint16_t adc2_chan_mask = BIT(0);
//     // adc_channel_t channel[3] = {ADC1_CHANNEL_0, ADC1_CHANNEL_1, (ADC2_CHANNEL_0 | 1 << 3)};
//     adc_channel_t channel[1] = {ADC1_CHANNEL_0};

//     continuous_adc_init(adc1_chan_mask, adc2_chan_mask, channel, sizeof(channel) / sizeof(adc_channel_t));
//     adc_digi_start();
//     // int n = 20;
//     while(1) {
//         ret = adc_digi_read_bytes(result, TIMES, &ret_num, ADC_MAX_DELAY);
//         for (int i = 0; i < ret_num; i+=4) {
//             adc_digi_output_data_t *p = (void*)&result[i];
//             if (check_valid_data(p)) {
//                 vout = (p->type2.data * 2500.00)/4095.00;
//                 printf("ADC%d_CH%d: %x  voltage is %fmv\n", p->type2.unit+1, p->type2.channel, p->type2.data,vout);
//             } else {
//                 printf("Invalid data [%d_%d_%x]\n", p->type2.unit+1, p->type2.channel, p->type2.data);
//             }
//         }
//         vTaskDelay(1000 / portTICK_PERIOD_MS);
//         // If you see task WDT in this task, it means the conversion is too fast for the task to handle

//     }  
//     adc_digi_stop();
//     ret = adc_digi_deinitialize();
//     assert(ret == ESP_OK);
// }

static void single_read(void *arg)
{
    // esp_err_t ret;
    // int adc1_reading[3] = {0xcc};
    int adc1_reading[1] = {0xcc};    
    // int adc2_reading[1] = {0xcc};
    uint32_t etc = 2;
    float vout;
    // const char TAG_CH[][10] = {"ADC1_CH2", "ADC1_CH3","ADC1_CH4", "ADC2_CH0"};
    const char TAG_CH[1][10] = {"ADC1_CH0"};
	gpio_set_direction(1, GPIO_MODE_OUTPUT);
		
    adc1_config_width(ADC_WIDTH_BIT_DEFAULT);
    adc1_config_channel_atten(ADC1_CHANNEL_0, ADC_ATTEN_DB_11);
    // adc1_config_channel_atten(ADC1_CHANNEL_3, ADC_ATTEN_DB_6);
    // adc1_config_channel_atten(ADC1_CHANNEL_4, ADC_ATTEN_DB_0);
    // adc2_config_channel_atten(ADC2_CHANNEL_0, ADC_ATTEN_DB_0);

    // int n = 20;
    // while (n--) {
    while (1) {
        adc1_reading[0] = adc1_get_raw(ADC1_CHANNEL_0);
        // adc1_reading[1] = adc1_get_raw(ADC1_CHANNEL_3);
        // adc1_reading[2] = adc1_get_raw(ADC1_CHANNEL_4);
        vout = (adc1_reading[0] * 2500.00)/4095.00;
        ESP_LOGI(TAG_CH[0], "%x vout mv is %f", adc1_reading[0],vout);

        
        // for (int i = 0; i < 3; i++) {
        //     ESP_LOGI(TAG_CH[i], "%x", adc1_reading[i]);
        // }
        ret = adc2_get_raw(ADC2_CHANNEL_0, ADC_WIDTH_BIT_12, &adc2_reading[0]);
        // assert(ret == ESP_OK);
        // ESP_LOGI(TAG_CH[3], "%x", adc2_reading[0]);
        vTaskDelay(500 / portTICK_PERIOD_MS);
        etc++;
        if(etc%2){
            gpio_set_level(1,1);
        }
        else
            gpio_set_level(1,0); 
        if(etc > 60000) etc = 2; 
    }
}

void app_main(void)
{
    single_read(NULL);
    //continuous_read(NULL);
}


  
 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
  • 59
  • 60
  • 61
  • 62
  • 63
  • 64
  • 65
  • 66
  • 67
  • 68
  • 69
  • 70
  • 71
  • 72
  • 73
  • 74
  • 75
  • 76
  • 77
  • 78
  • 79
  • 80
  • 81
  • 82
  • 83
  • 84
  • 85
  • 86
  • 87
  • 88
  • 89
  • 90
  • 91
  • 92
  • 93
  • 94
  • 95
  • 96
  • 97
  • 98
  • 99
  • 100
  • 101
  • 102
  • 103
  • 104
  • 105
  • 106
  • 107
  • 108
  • 109
  • 110
  • 111
  • 112
  • 113
  • 114
  • 115
  • 116
  • 117
  • 118
  • 119
  • 120
  • 121
  • 122
  • 123
  • 124
  • 125
  • 126
  • 127
  • 128
  • 129
  • 130
  • 131
  • 132
  • 133
  • 134
  • 135
  • 136
  • 137
  • 138
  • 139
  • 140
  • 141
  • 142
  • 143
  • 144

2、 ESP32-C3 ADC相关介绍

对于ESP32-C3 ADC的介绍,在乐鑫的官网有很详细的说明,官方链接如下:

乐鑫官方ESP32-C3 ADC部分说明

2.1 实际电压的计算

对于实际电压的计算,有如下计算公式:
在这里插入图片描述其中 Vmax 中的 ADC Attenuation 在官方文档中如下介绍:
在这里插入图片描述
在 SDK 的库函数中 使用枚举类型定义的,如下(数值上有一点区别):
在这里插入图片描述
在示例中根据公式测试电压值的计算:
在这里插入图片描述
在库函数中也有关于电压转换的函数esp_adc_cal_get_voltage,其中调用了esp_adc_cal_raw_to_voltage进行计算:
在这里插入图片描述源码如下:

/*
esp_adc_cal_characteristics_t 结构体如下
typedef struct {
    adc_unit_t adc_num;                     /**< ADC number
    adc_atten_t atten;                      /**< ADC attenuation
    adc_bits_width_t bit_width;             /**< ADC bit width 
    uint32_t coeff_a;                       /**< Gradient of ADC-Voltage curve
    uint32_t coeff_b;                       /**< Offset of ADC-Voltage curve
    uint32_t vref;                          /**< Vref used by lookup table
    const uint32_t *low_curve;              /**< Pointer to low Vref curve of lookup table (NULL if unused)
    const uint32_t *high_curve;             /**< Pointer to high Vref curve of lookup table (NULL if unused)
} esp_adc_cal_characteristics_t;

计算函数如下:
uint32_t esp_adc_cal_raw_to_voltage(uint32_t adc_reading, const esp_adc_cal_characteristics_t *chars)
{
    ADC_CALIB_CHECK(chars != NULL, "No characteristic input.", ESP_ERR_INVALID_ARG);

    return adc_reading * chars->coeff_a / coeff_a_scaling + chars->coeff_b / coeff_b_scaling;
}

*/
esp_err_t esp_adc_cal_get_voltage(adc_channel_t channel,
                                  const esp_adc_cal_characteristics_t *chars,
                                  uint32_t *voltage)
{
    // Check parameters
    ADC_CALIB_CHECK(chars != NULL, "No characteristic input.", ESP_ERR_INVALID_ARG);
    ADC_CALIB_CHECK(voltage != NULL, "No output buffer.", ESP_ERR_INVALID_ARG);

    int adc_reading;
    if (chars->adc_num == ADC_UNIT_1) {
        //Check if channel is valid on ADC1
        ADC_CALIB_CHECK((adc1_channel_t)channel < ADC1_CHANNEL_MAX, "Invalid channel", ESP_ERR_INVALID_ARG);
        adc_reading = adc1_get_raw(channel);
    } else {
        //Check if channel is valid on ADC2
        ADC_CALIB_CHECK((adc2_channel_t)channel < ADC2_CHANNEL_MAX, "Invalid channel", ESP_ERR_INVALID_ARG);
        if (adc2_get_raw(channel, chars->bit_width, &adc_reading) != ESP_OK) {
            return ESP_ERR_TIMEOUT;     //Timed out waiting for ADC2
        }
    }
    *voltage = esp_adc_cal_raw_to_voltage((uint32_t)adc_reading, chars);
    return ESP_OK;
}

  
 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45

2.2 连续采样步骤

官方的连续采样步骤说明:
在这里插入图片描述根据说明我们对比一下示例代码:
在这里插入图片描述

2.3 单步采样步骤

在这里插入图片描述根据说明我们对比一下示例代码:

在这里插入图片描述

2.4 ADC使用注意事项

还是官方手册,简单说明一下:

  • ADC2模块也被 WI-FI 组件使用了,所以在 esp_err_t esp_wifi_start(void)esp_err_t esp_wifi_stop(void)函数之间进行 ADC2 读取,不一定能够获得正确的值,其实这点我们在使用中尽量避免就可以
  • 一个特定的ADC模块在同一时间只能工作在一种工作模式下(单次和连续模式)
  • ADC1和ADC2不能同时在单读模式下工作。其中一个会被阻塞,直到另一个完成。
  • 对于连续(DMA)模式,ADC采样频率应该在SOC_ADC_SAMPLE_FREQ_THRES_LOWSOC_ADC_SAMPLE_FREQ_THRES_HIGH范围内。
    在这里插入图片描述

文章来源: blog.csdn.net,作者:矜辰所致,版权归原作者所有,如需转载,请联系作者。

原文链接:blog.csdn.net/weixin_42328389/article/details/122266604

【版权声明】本文为华为云社区用户转载文章,如果您发现本社区中有涉嫌抄袭的内容,欢迎发送邮件进行举报,并提供相关证据,一经查实,本社区将立刻删除涉嫌侵权内容,举报邮箱: cloudbbs@huaweicloud.com
  • 点赞
  • 收藏
  • 关注作者

评论(0

0/1000
抱歉,系统识别当前为高风险访问,暂不支持该操作

全部回复

上滑加载中

设置昵称

在此一键设置昵称,即可参与社区互动!

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。