ESP32-S3 ULP RISC-V读取HDC1080温度失败问题求助
ESP32-S3 ULP RISC-V 读取HDC1080温度失败问题解决
问题背景
我在ESP32-S3的ULP RISC-V协处理器深度休眠模式下,尝试通过I2C读取HDC1080传感器的温度值。Arduino环境下的测试代码运行正常,但移植到ESP-IDF的ULP RISC-V I2C库后,无法读取HDC1080的温度值,参考ESP-IDF提供的BMP180 I2C示例代码也无法解决问题。
根据ESP-IDF文档,ULP RISC-V I2C库要求指定从设备子寄存器地址,但HDC1080的温度寄存器地址为0x00,有效数据为[15:02]位,怀疑这是读取失败的核心原因。
Arduino下正常运行的代码
double temp() { Wire.beginTransmission((uint8_t)0x40); Wire.write((uint8_t)0x00); Wire.endTransmission(); delay(20); Wire.requestFrom((uint8_t)0x40, (uint8_t)2); byte msb = Wire.read(); byte lsb = Wire.read(); uint16_t t = msb << 8 | lsb; return (t / pow(2, 16)) * 165.0 - 40.0; }
当前ESP-IDF测试代码
uint8_t data_buffer[2] = {0, 0}; int a = 0; while (1) { vTaskDelay(pdMS_TO_TICKS(3000)); printf("Ölçüm alınıyor...%d\n\n", a++); ulp_riscv_i2c_master_set_slave_addr(0x40); // HDC1080的I2C地址 uint8_t cmd = 0x00; // 温度寄存器地址 ulp_riscv_i2c_master_write_to_device(&cmd, 1); vTaskDelay(pdMS_TO_TICKS(10)); ulp_riscv_i2c_master_set_slave_reg_addr(0x00); ulp_riscv_i2c_master_read_from_device(data_buffer, 2); uint16_t xut_data = ((uint16_t)data_buffer[0] << 8) | data_buffer[1]; uint16_t temperatureValue = xut_data >> 2; // 丢弃低2位,取[15:02]有效位 printf("temperatureValue: %" PRIu16 "\n", temperatureValue); printf("data_buffer[0]: %u\n", data_buffer[0]); printf("data_buffer[1]: %u\n", data_buffer[1]); // 计算实际温度 float realTemperature = ((float)temperatureValue / (float)(1 << 14)) * 165.0 - 40.0; printf("Real Temp: %.2f°C\n", realTemperature); }
错误输出
Fixaj: 04-07 15:19:26-414691 E (58576) ulp_riscv_i2c: Read Failed! Fixaj: 04-07 15:19:26-420707 E (58576) ulp_riscv_i2c: RTC I2C Interrupt Raw Reg 0x16c Fixaj: 04-07 15:19:26-426829 E (58576) ulp_riscv_i2c: RTC I2C Status Reg 0x65ff0000
问题分析与解决方案
核心问题
HDC1080的I2C读取流程和BMP180不同:
- 写入温度寄存器地址0x00后,传感器开始转换温度,转换完成后,直接发起读操作即可获取结果,不需要再次发送寄存器地址。
- 当前代码中,调用
ulp_riscv_i2c_master_read_from_device前又设置了子寄存器地址,导致ULP I2C库再次发送0x00地址,违反了HDC1080的操作时序,因此读取失败。
修正步骤
- 移除
ulp_riscv_i2c_master_set_slave_reg_addr调用,避免重复发送寄存器地址。 - 确保转换等待时间足够:HDC1080的温度转换时间最长为14ms,因此等待时间应设置为≥14ms(建议20ms,和Arduino代码保持一致)。
- 调整温度计算逻辑:原始16位数据的低2位为0,直接用16位计算即可,或者右移2位后用14位计算,结果一致。
修正后的代码
#include <stdio.h> #include <stdint.h> #include "freertos/FreeRTOS.h" #include "freertos/task.h" #include "ulp_riscv_i2c.h" #define HDC1080_I2C_ADDR 0x40 #define HDC1080_TEMP_REG 0x00 void app_main(void) { // 初始化ULP RISC-V I2C(需确保已完成硬件引脚配置) ulp_riscv_i2c_master_init(); uint8_t data_buffer[2] = {0, 0}; int a = 0; while (1) { vTaskDelay(pdMS_TO_TICKS(3000)); printf("测量中...%d\n\n", a++); // 设置HDC1080的I2C地址 ulp_riscv_i2c_master_set_slave_addr(HDC1080_I2C_ADDR); // 发送温度寄存器地址,启动转换 uint8_t cmd = HDC1080_TEMP_REG; esp_err_t write_err = ulp_riscv_i2c_master_write_to_device(&cmd, 1); if (write_err != ESP_OK) { printf("写入寄存器地址失败: %d\n", write_err); continue; } // 等待温度转换完成(HDC1080最长需要14ms) vTaskDelay(pdMS_TO_TICKS(20)); // 直接读取2字节转换结果,无需再次设置寄存器地址 esp_err_t read_err = ulp_riscv_i2c_master_read_from_device(data_buffer, 2); if (read_err != ESP_OK) { printf("读取数据失败: %d\n", read_err); continue; } // 处理原始数据 uint16_t raw_data = ((uint16_t)data_buffer[0] << 8) | data_buffer[1]; // 计算实际温度:公式参考HDC1080 datasheet float real_temp = ((float)raw_data / 65536.0) * 165.0 - 40.0; printf("原始数据: 0x%04X\n", raw_data); printf("data_buffer[0]: %u\n", data_buffer[0]); printf("data_buffer[1]: %u\n", data_buffer[1]); printf("实际温度: %.2f°C\n\n", real_temp); } }
额外注意事项
- 确保ULP RISC-V的I2C引脚配置正确,使用的是RTC_GPIO引脚(因为ULP只能访问RTC域的硬件)。
- 如果在深度休眠模式下运行,需确保ULP程序已正确加载并配置唤醒源,上述代码是在主CPU运行的测试代码,若要迁移到ULP协处理器,需将核心逻辑放入ULP汇编/C代码中,并通过主CPU加载。
内容的提问来源于stack exchange,提问作者mehmet
相关产品推荐
相关产品推荐

