ESP32_IDF学习(8)--ADC

本文详细介绍了ESP32微控制器中的模拟数字转换器(ADC)特性,包括两个SAR ADC单元的通道分布、衰减选项、转换过程以及ADC的局限性。重点讲述了ADC的配置,如衰减设置和读取模式,并强调了ADC的噪声管理,建议在输入焊盘上连接旁路电容以降低噪声。此外,还讨论了ADC的校准方法,包括使用esp_adc_cal库进行电压校准,以及如何利用eFuse中的校准值来提高测量准确性。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

ADC

ADC通道

ESP32集成了2个SAR(逐次逼近寄存器)ADC,总共支持18个测量通道(模拟启用引脚)。

这些通道支持:

ADC1:

  • 8个通道:gpio32 - gpio39

ADC2:

  • 10个通道:gpio0, gpio2, gpio4, gpio12 - gpio15, goio25 - gpio27

ADC衰减

Vref是ESP32 ADC内部用于测量输入电压的参考电压。ESP32 ADC可以测量从0 V到Vref的模拟电压。在不同的芯片中,Vref是不同的,中位数是1.1V。为了转换大于Vref的电压,输入电压在输入到ADC之前可以被衰减。有4种可用的衰减选项,衰减越高,可测量的输入电压就越高

衰减可测量的输入电压范围
ADC_ATTEN_DB_0100 mV ~ 950 mV
ADC_ATTEN_DB_2_5100 mV ~ 1250 mV
ADC_ATTEN_DB_6150 mV ~ 1750 mV
ADC_ATTEN_DB_11150 mV ~ 2450 mV

ADC转换

ADC转换是将输入的模拟电压转换为数字值。由ADC驱动器APls提供的ADC转换结果是原始数据。在单读模式下,ESP32 ADC原始结果的分辨率为12位。

  • adc1_get_raw()
  • adc2_get_raw()

要根据ADC的原始结果计算电压,可以使用这个公式:
V o u t = D o u t ∗ V m a x / D m a x Vout=Dout*Vmax/Dmax Vout=DoutVmax/Dmax
其中

变量作用
Vout数字输出结果,代表的是电压。
DoutADC原始数字读取结果
Vmax最大可测量输入模拟电压
Dmax输出ADC原始数字读取结果的最大值,在单次读取模式下为4095,在连续读取模式下为4095。

对于带有eFuse ADC校准位的板子,可以使用esp_adc_cal_raw_to_voltage()来获得校准的转换结果。这些结果代表了实际电压(单位:mV)。不需要通过公式来转换这些数据。如果在没有eFuse ADC校准位的板上使用ADC校准API,将产生警告。

ADC的局限性

  • ADC2的一些引脚被用作捆绑引脚(GPIO 0、2、15),因此不能随意使用。在以下官方开发套件中就是这种情况。
  • ESP32 DevKitC:由于外部自动编程电路,不能使用GPIO0。
  • ESP-WROVER-KIT:由于外部连接的目的不同,不能使用GPIO 0、2、4和15。
  • 由于ADC2模块也被Wi-Fi使用,当它们一起使用时,只有其中一个可以获得优先权,这意味着adc2_get_raw()可能被阻止,直到Wi-Fi停止,反之亦然。

驱动程序使用

这两个ADC单元都支持单次读取模式,适用于低频采样操作。

来自未连接到任何信号的引脚的ADC读数是随机的。

ADC单次读取模式

在读数之前,应该对ADC进行配置。

  • 对于ADC1,通过调用函数adc1_config_width()adc1_config_channel_atten()配置所需精度和衰减。
  • 对于ADC2,通过adc2_config_channel_atten()配置衰减。ADC2的读数宽度是在每次进行读数时配置的。

衰减配置是按通道进行的,见adc1_channel_tadc2_channel_t,作为上述函数的一个参数设置。

然后就可以用adc1_get_raw()adc2_get_raw()来读取ADC转换结果。ADC2的读取宽度应该作为adc2_get_raw()的一个参数来设置,而不是在配置函数中。

也可以通过调用专用函数hall_sensor_read(),通过ADC1读取内部霍尔效应传感器。请注意,即使霍尔传感器是ESP32内部的,从它那里读取也是使用ADC1的通道0和3(GPIO 36和39)。不要在这些引脚上连接其他东西,也不要改变它们的配置。否则可能会影响来自传感器的低值信号的测量。

这个API提供了方便的方法来配置ADC1,以便从ULP读取数据。要做到这一点,需要调用函数adc1_ulp_enable(),然后如上所述设置精度和衰减。

还有一个特定的函数adc_vref_to_gpio()用于将内部参考电压传送到一个GPIO引脚。它在校准ADC读数时非常方便,这将在ADC校准部分讨论。

减少噪音

ESP32 ADC对噪声很敏感,导致ADC读数的巨大差异。根据使用情况,用户可以在使用中的ADC输入焊盘上连接一个旁路电容(例如,一个100nF的陶瓷电容),以尽量减少噪声。此外,也可以使用多采样来进一步减轻噪声的影响。

图中说明了使用电容和64个样本的多重取样的噪音缓解。

ADC校准

esp_adc_cal/include/esp_adc_cal.h API提供了一些函数来修正由于芯片之间ADC参考电压(Vref)的变化而导致的测量电压的差异。根据设计,ADC参考电压是1100mV,但是在不同的ESP32中,真正的参考电压可能在1000mV到1200mV之间。

图示不同的参考电压对ADC电压曲线的影响

使用该API纠正ADC读数涉及在给定的衰减下对其中一个ADC进行特征分析,以获得考虑到ADC参考电压差异的特性曲线(ADC-电压曲线)。特性曲线的形式是
y = c o e f f a ∗ x + c o e f f b y = coeff_a * x + coeff_b y=coeffax+coeffb
,用于将ADC读数转换为mV的电压。特性曲线的计算基于校准值,这些校准值可以存储在eFuse中或由用户提供。

校准值

校准值用于生成特性曲线,以说明特定ESP32芯片的ADC参考电压的变化。目前,ESP32上有3个校准值的来源。这些校准值的可用性将取决于ESP32芯片/模块的类型和生产日期。

  • 两点值代表每个ADC在150mV和850mV的读数。BLOCK3在工厂校准过程中测量这些值并将其烧入eFuse 。
  • eFuse 参考电压表示真实的ADC参考电压。BLOCK0在工厂校准期间测量该值并将其烧入eFuse 。
  • 默认参考电压是在表征过程中用户提供的ADC参考电压估计值作为参数。如果两点或eFuse Vref值不可用,则将使用默认参考电压

eFuse Vref的单独测量和烧写已经适用于2018年第一周/之后生产的ESP32-D0WD和ESP32-D0WDQ6芯片。这类芯片可以通过日期代码在012018年/以后来识别(见下图的第4行)。

如果你想购买带校准的芯片或模块,请与分销商或Espressif直接确认。

如果你无法检查日期代码(即芯片可能被封装在罐装模块内,等等),你仍然可以通过运行带有adc_info参数的espefuse.py工具来验证eFuse Vref是否存在

$IDF_PATH/components/esptool_py/esptool/espefuse.py --port /COM8 adc_info  (window)
$IDF_PATH/components/esptool_py/esptool/espefuse.py --port /dev/ttyUSB0 adc_info  (Linux)

在window下用ESP32板的端口名称替换/dev/ttyUSB0。

一个有特定eFuse Vref值编程的芯片(在本例中为1093 mV)将被报告如下:

ADC VRef calibration: 1093 mV

在下面的另一个例子中,eFuse的Vref没有被编程:

ADC VRef calibration: None (1100 mV nominal)

对于一个有两点校准的芯片,信息看起来类似于:

ADC VRef calibration: 1149 mV
ADC readings stored in efuse BLK3:
    ADC1 Low reading  (150 mV): 306
    ADC1 High reading (850 mV): 3153
    ADC2 Low reading  (150 mV): 389
    ADC2 High reading (850 mV): 3206

执行.py脚本一般执行会直接退出无法看到输出值,故可以这样使用即可打印出输出信息:

python -i  espefuse.py --port COM8 adc_info

详细代码

// ADC所接的通道  GPIO34 if ADC1  = ADC1_CHANNEL_6
#define ADC1_TEST_CHANNEL ADC1_CHANNEL_6 
// ADC斜率曲线
static esp_adc_cal_characteristics_t *adc_chars;
// 参考电压
#define DEFAULT_VREF				3300			//使用adc2_vref_to_gpio()获得更好的估计值


void check_efuse(void)
{
    //检查TP是否烧入eFuse
    if (esp_adc_cal_check_efuse(ESP_ADC_CAL_VAL_EFUSE_TP) == ESP_OK) {
        printf("eFuse Two Point: Supported\n");
    } else {
        printf("eFuse Two Point: NOT supported\n");
    }
    //检查Vref是否烧入eFuse
    if (esp_adc_cal_check_efuse(ESP_ADC_CAL_VAL_EFUSE_VREF) == ESP_OK) {
        printf("eFuse Vref: Supported\n");
    } else {
        printf("eFuse Vref: NOT supported\n");
    }
}
void adc_init(void)
{
  adc1_config_width(ADC_WIDTH_BIT_12);// 12位分辨率
	adc1_config_channel_atten(ADC1_TEST_CHANNEL, ADC_ATTEN_DB_11);// 电压输入衰减
  adc_chars = calloc(1, sizeof(esp_adc_cal_characteristics_t));	// 为斜率曲线分配内存
  esp_adc_cal_value_t val_type = esp_adc_cal_characterize(ADC_UNIT_1, ADC_ATTEN_DB_11, ADC_WIDTH_BIT_12, DEFAULT_VREF, adc_chars);
 // print_char_val_type(val_type);
}
void app_main(void)
{
  uint32_t read_raw;
  check_efuse();
	adc_init();
	while(1){
		read_raw = adc1_get_raw(ADC1_TEST_CHANNEL);// 采集ADC原始值//这里可以多次采样取平均值
		uint32_t voltage = esp_adc_cal_raw_to_voltage(read_raw, adc_chars);//通过一条斜率曲线把读取adc1_get_raw()的原始数值转变成了mV
		printf("ADC原始值: %d   转换电压值: %dmV\n", read_raw, voltage);
		vTaskDelay(1000 / portTICK_RATE_MS);
	}
}
xecuting action: flash Serial port /dev/ttyACM0 Connecting.... Detecting chip type... ESP32-S3 Running ninja in directory /home/jichu/esp32/lvgl_display/build Executing "ninja flash"... [1/1] cd /home/jichu/esp32/lvgl_displa...isplay/build/bootloader/bootloader.bin Bootloader binary size 0x5240 bytes. 0x2dc0 bytes (36%) free. [2/5] Linking CXX executable lvgl_display.elf FAILED: lvgl_display.elf : && /home/jichu/.espressif/tools/xtensa-esp-elf/esp-13.2.0_20230928/xtensa-esp-elf/bin/xtensa-esp32s3-elf-g++ -mlongcalls -Wl,--cref -Wl,--defsym=IDF_TARGET_ESP32S3=0 -Wl,--Map=/home/jichu/esp32/lvgl_display/build/lvgl_display.map -Wl,--no-warn-rwx-segments -fno-rtti -fno-lto -Wl,--gc-sections -Wl,--warn-common -T esp32s3.peripherals.ld -T esp32s3.rom.ld -T esp32s3.rom.api.ld -T esp32s3.rom.libgcc.ld -T esp32s3.rom.newlib.ld -T esp32s3.rom.version.ld -T memory.ld -T sections.ld CMakeFiles/lvgl_display.elf.dir/project_elf_src_esp32s3.c.obj -o lvgl_display.elf -L/home/jichu/esp32/esp-idf/components/soc/esp32s3/ld -L/home/jichu/esp32/esp-idf/components/esp_rom/esp32s3/ld -L/home/jichu/esp32/lvgl_display/build/esp-idf/esp_system/ld -L/home/jichu/esp32/esp-idf/components/esp_phy/lib/esp32s3 -L/home/jichu/esp32/esp-idf/components/esp_coex/lib/esp32s3 -L/home/jichu/esp32/esp-idf/components/esp_wifi/lib/esp32s3 -L/home/jichu/esp32/esp-idf/components/bt/controller/lib_esp32c3_family/esp32s3 esp-idf/xtensa/libxtensa.a esp-idf/esp_ringbuf/libesp_ringbuf.a esp-idf/efuse/libefuse.a esp-idf/esp_mm/libesp_mm.a esp-idf/driver/libdriver.a esp-idf/esp_pm/libesp_pm.a esp-idf/mbedtls/libmbedtls.a esp-idf/esp_bootloader_format/libesp_bootloader_format.a esp-idf/esp_app_format/libesp_app_format.a esp-idf/bootloader_support/libbootloader_support.a esp-idf/esp_partition/libesp_partition.a esp-idf/app_update/libapp_update.a esp-idf/spi_flash/libspi_flash.a esp-idf/pthread/libpthread.a esp-idf/esp_system/libesp_system.a esp-idf/esp_rom/libesp_rom.a esp-idf/hal/libhal.a esp-idf/log/liblog.a esp-idf/heap/libheap.a esp-idf/soc/libsoc.a esp-idf/esp_hw_support/libesp_hw_support.a esp-idf/freertos/libfreertos.a esp-idf/newlib/libnewlib.a esp-idf/cxx/libcxx.a esp-idf/esp_common/libesp_common.a esp-idf/esp_timer/libesp_timer.a esp-idf/app_trace/libapp_trace.a esp-idf/esp_event/libesp_event.a esp-idf/nvs_flash/libnvs_flash.a esp-idf/esp_phy/libesp_phy.a esp-idf/vfs/libvfs.a esp-idf/lwip/liblwip.a esp-idf/esp_netif/libesp_netif.a esp-idf/wpa_supplicant/libwpa_supplicant.a esp-idf/esp_coex/libesp_coex.a esp-idf/esp_wifi/libesp_wifi.a esp-idf/bt/libbt.a esp-idf/unity/libunity.a esp-idf/cmock/libcmock.a esp-idf/console/libconsole.a esp-idf/http_parser/libhttp_parser.a esp-idf/esp-tls/libesp-tls.a esp-idf/esp_adc/libesp_adc.a esp-idf/esp_eth/libesp_eth.a esp-idf/esp_gdbstub/libesp_gdbstub.a esp-idf/esp_hid/libesp_hid.a esp-idf/tcp_transport/libtcp_transport.a esp-idf/esp_http_client/libesp_http_client.a esp-idf/esp_http_server/libesp_http_server.a esp-idf/esp_https_ota/libesp_https_ota.a esp-idf/esp_lcd/libesp_lcd.a esp-idf/protobuf-c/libprotobuf-c.a esp-idf/protocomm/libprotocomm.a esp-idf/esp_local_ctrl/libesp_local_ctrl.a esp-idf/espcoredump/libespcoredump.a esp-idf/wear_levelling/libwear_levelling.a esp-idf/sdmmc/libsdmmc.a esp-idf/fatfs/libfatfs.a esp-idf/json/libjson.a esp-idf/mqtt/libmqtt.a esp-idf/nvs_sec_provider/libnvs_sec_provider.a esp-idf/perfmon/libperfmon.a esp-idf/spiffs/libspiffs.a esp-idf/touch_element/libtouch_element.a esp-idf/usb/libusb.a esp-idf/wifi_p
最新发布
03-14
<think>好的,我现在要处理用户提供的关于ESP32-S3项目编译失败的日志。首先,我需要仔细阅读日志内容,找出关键错误信息。用户执行了`ninja flash`命令,但在链接阶段出现了问题,具体是`FAILED: lvgl_display.elf`,说明生成可执行文件时失败。 首先,我注意到链接器命令中使用了`xtensa-esp32s3-elf-g++`,这可能涉及到C++的链接问题。常见的失败原因可能包括内存不足、库缺失或版本不兼容、符号未定义等。用户的环境是ESP-IDF,可能使用了LVGL库,所以需要检查相关依赖是否正确配置。 日志中的错误没有直接显示具体的错误信息,这可能是因为输出被截断或者需要更详细的编译日志。因此,我应该建议用户增加编译的详细程度,例如运行`ninja -v flash`来获取更多信息。此外,检查是否有未定义的引用(undefined reference)或库路径问题。 另一个可能性是项目的sdkconfig配置不正确,特别是对于ESP32-S3的配置,例如PSRAM设置、分区表大小等。用户需要确认`sdkconfig`中是否选择了正确的芯片型号,以及LVGL相关配置是否适配ESP32-S3。 内存不足也是一个常见问题,尤其是在链接阶段。Bootloader的大小显示为0x5240字节,剩余36%的空间,这可能没问题,但主应用程序可能超出了可用内存。需要检查分区表是否合理,以及应用程序的代码量和内存需求。 还有可能是工具链的问题,比如xtensa-esp32s3-elf-g++的版本是否与ESP-IDF版本兼容。用户使用的ESP-IDF版本是否支持所选的LVGL版本?需要确认版本兼容性,并尝试重新安装或更新工具链。 此外,检查CMakeLists.txt和组件依赖是否正确,特别是LVGL组件是否被正确包含,路径是否正确。如果用户自定义了组件,可能存在路径错误或编译选项冲突。 最后,建议用户清理构建目录(删除build文件夹)并重新编译,有时候旧的构建文件会导致链接错误。如果问题依旧,查看更详细的日志或尝试简化项目以隔离问题。</think>您在尝试通过 `ninja flash` 命令烧录 ESP32-S3 固件时遇到了链接阶段的编译错误。以下是问题分析和解决步骤: --- ### **错误定位** 日志显示关键错误: ```bash FAILED: lvgl_display.elf : && /home/jichu/.espressif/tools/.../xtensa-esp32s3-elf-g++ ... (链接命令) ``` 此错误表明在生成最终可执行文件 `lvgl_display.elf` 时,链接器 (`xtensa-esp32s3-elf-g++`) 失败。 --- ### **可能原因** 1. **内存不足** - Bootloader 剩余空间:$$ 0x5240 \text{ bytes ()} - 0x2dc0 \text{ bytes (剩余)} = 0x2480 \text{ bytes (已用)} $$ - 主程序 (`lvgl_display.elf`) 可能超出芯片 Flash 或 RAM 限制。 2. **依赖库缺失或冲突** - 检查是否缺少 LVGL 相关库(如 `lvgl/liblvgl.a`)。 - 确保所有组件(如 Wi-Fi、SPIFFS)在 `sdkconfig` 中正确配置。 3. **符号未定义 (Undefined Reference)** - 常见于未实现函数、头文件未包含或库未链接。 4. **工具链版本不兼容** - ESP-IDF 版本与 LVGL 组件或第三方库存在兼容性问题。 --- ### **解决步骤** #### 1. **启用详细日志** 重新编译并输出详细错误信息: ```bash ninja -v flash | tee build.log ``` 检查 `build.log` 中具体的未定义符号或路径错误。 --- #### 2. **检查内存配置** - **分区表**:确认 `partitions.csv` 中 `app` 分区大小是否足够。 - **sdkconfig**:通过 `idf.py menuconfig` 检查: - **Component config → LVGL → Memory settings**(调整 LVGL 缓冲区大小)。 - **Serial flasher config → Flash Size** 是否匹配硬件。 --- #### 3. **验证依赖关系** - **组件路径**:确保 LVGL 组件路径正确(`CMakeLists.txt` 中 `EXTRA_COMPONENT_DIRS`)。 - **库链接**:在 `CMakeLists.txt` 中添加缺失库: ```cmake target_link_libraries(${PROJECT_NAME} PRIVATE lvgl) ``` --- #### 4. **清理并重新编译** 清除旧构建文件: ```bash rm -rf build idf.py build ``` --- #### 5. **缩小问题范围** - **最小化代码**:注释掉 LVGL 初始化代码,逐步排查功能模块。 - **更新工具链**:重新安装 ESP-IDF 或更新至稳定版本(如 v5.1)。 --- ### **扩展建议** - **使用官方例程**:从 `esp-idf/examples/peripherals/lcd/lvgl` 测试基础功能。 - **内存分析工具**:通过 `idf.py size-components` 查看各组件内存占用。 如果仍有问题,请提供 `build.log` 中的具体错误信息,以便进一步诊断!
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值