自学内容网 自学内容网

《ESP32调试异常集锦》之移植I2C程序时i2c_master_cmd_begin返回-1

项目场景:

《ESP32从0到1》之I2C通讯(AHT20温湿度传感器)_esp32 i2c-CSDN博客

        之前的程序,测试正常。现将其移植到其他的项目中,运行发现导致重启。


问题描述

        串口调试信息如下:跟踪程序发现在第二次发送0x71获取状态字的时候导致的重启。

[0;32mI (11320) i2c-simple-example: I2C initialized successfully[0m
[0;32mI (11320) I2C_DEBUG: err: 0x0[0m
[0;32mI (11330) i2c-simple-example: status = 0x18[0m
[0;32mI (11330) i2c-simple-example: read status[0m
[0;32mI (11340) I2C_DEBUG: err: 0xFFFFFFFF[0m
ESP_ERROR_CHECK failed: esp_err_t 0xffffffff (ESP_FAIL) at 0x400d7ff7
file: "./components/aht20/aht20.c" line 77
func: ReadAHT20Data
expression: AHT20_register_read(statuscmd, 1,data, 1)

abort() was called at PC 0x40090c53 on core 0


Backtrace: 0x40081932:0x3ffcc8f0 0x40090c5d:0x3ffcc910 0x40097042:0x3ffcc930 0x40090c53:0x3ffcc9a0 0x400d7ff7:0x3ffcc9d0 0x400d7b73:0x3ffcca10 0x401bc25e:0x3ffccb00 0x401bc2d9:0x3ffccb50




ELF file SHA256: 8cf2ff4af

Rebooting...
ets Jul 29 2019 12:21:46

        因为程序中用了函数ESP_ERROR_CHECK进行了判断,AHT20_register_read返回的并不是0x00所以导致重启。究其原因需要找到为什么AHT20_register_read没有返回0x00.

         AHT20_register_read函数如下所示:

static esp_err_t AHT20_register_read(uint8_t* reg_addr,size_t cmdlen, uint8_t *data, size_t len)
{
    return i2c_master_write_read_device(I2C_MASTER_NUM, AHT20_SENSOR_ADDR, reg_addr, cmdlen, data, len, I2C_MASTER_TIMEOUT_MS / portTICK_PERIOD_MS);
}

          i2c_master_write_read_device函数如下所示(i2c.c中的函数,未作修改):

esp_err_t i2c_master_write_read_device(i2c_port_t i2c_num, uint8_t device_address,
                                       const uint8_t* write_buffer, size_t write_size,
                                       uint8_t* read_buffer, size_t read_size,
                                       TickType_t ticks_to_wait)
{
    esp_err_t err = ESP_OK;
    uint8_t buffer[I2C_TRANS_BUF_MINIMUM_SIZE] = { 0 };
    i2c_cmd_handle_t handle = i2c_cmd_link_create_static(buffer, sizeof(buffer));
    assert (handle != NULL);   
    err = i2c_master_start(handle);
    if (err != ESP_OK) {
        goto end;
    }
    err = i2c_master_write_byte(handle, device_address << 1 | I2C_MASTER_WRITE, true);
    if (err != ESP_OK) {
        goto end;
    }
    err = i2c_master_write(handle, write_buffer, write_size, true);
    if (err != ESP_OK) {
        goto end;
    }
    err = i2c_master_start(handle);
    if (err != ESP_OK) {
        goto end;
    }
    err = i2c_master_write_byte(handle, device_address << 1 | I2C_MASTER_READ, true);
    if (err != ESP_OK) {
        goto end;
    }
    err = i2c_master_read(handle, read_buffer, read_size, I2C_MASTER_LAST_NACK);
    if (err != ESP_OK) {
        goto end;
    }
    i2c_master_stop(handle);
    err = i2c_master_cmd_begin(i2c_num, handle, ticks_to_wait);
end:
    i2c_cmd_link_delete_static(handle);
    return err;
}

        追踪到是i2c_master_cmd_begin出现问题,返回-1,对应到程序中是没有收到ACK.

        之前调试成功的程序烧录到当前项目PCBA上,同样的地方出现重启。

        之前调试用的ESP32-LyraT-Mini V1.2开发板,烧录之前调试通过的程序,依旧同样地方出现重启。

分析:

        1)怀疑是模块问题?更换了新的AHT20模块,问题依旧,可以排除模块问题;

        2)之前的开发板和现在自制的PCBA使用相同的模块出现相同的问题,问题应该不是出现在硬件上;

        3)程序隐形的问题?

                降低频率到200KHz 无作用

CSDN AI针对问题的回复:

问:ESP32调试I2C时i2c_master_write_to_device之后再调用i2c_master_write_read_device会导致i2c_master_cmd_begin返回-1

答:当你在调试ESP32的I2C时,在i2c_master_write_to_device()之后接着调用i2c_master_write_read_device(),如果出现了i2c_master_cmd_begin()返回-1的问题,可能是以下几个原因:

  1. 数据流冲突:这两个函数实际上都在进行I2C传输,如果前一个操作还没有完成,比如缓冲区未清空或者命令还在传输过程中,新的命令会因资源占用而失败。

  2. 顺序错误:I2C协议通常是半双工的,这意味着它不会同时进行读取和写入。如果你在设备正忙于接收数据时试图写入,可能会发生冲突。

  3. 设备响应超时:如果设备在前一次读写操作中设置了较长的延迟响应时间,或者数据包过大导致处理较慢,可能会影响到后续命令的开始。

  4. 错误检测未清除:如果前一次写操作检测到了错误(例如CRC校验错误),ESP32可能会拒绝继续执行直到错误被纠正。

解决策略:

  • 确保每个I2C操作之间有适当的延时,让前一个操作有足够的时间完成。
  • 检查设备的I2C地址和命令模式,确保它们在写入和读取时是一致的。
  • 如果设备支持,可以先发送一个停止条件,等待设备准备好接受下一个命令。
  • 使用ESP32提供的异常处理机制,捕获并处理可能出现的错误。

        从解决策略上,第一个策略最有可能。检查AHT20资料对照实际程序,发现程序并没有按照资料的要求在启动测量之后等待80ms(之前写程序时预留备注但是因为测试通过,侥幸心理并没有做实际的delay操作)。

        修改程序,增加了等待80ms的程序,则AHT20通讯正常,不再返回-1导致重启。

void ReadAHT20Data(float* temp,float* humi)
{
    uint8_t data[6];
    uint8_t statuscmd[]={0x71};
     uint8_t mearcmd[]={0xac,0x33,0x00};   
     uint16_t timecount=0;
     uint32_t tmp32;    
     ESP_ERROR_CHECK(i2c_master_init());
    ESP_LOGI(I2C_TAG, "I2C initialized successfully");  
    //发送0x71命令,读取状态字
    ESP_ERROR_CHECK(AHT20_register_read(statuscmd, 1,data, 1));  
    ESP_LOGI(I2C_TAG, "status = 0x%X", data[0]);

    //启动测量
    ESP_ERROR_CHECK(AHT20_register_write(mearcmd, 3));   
    //delay 80ms
     vTaskDelay(pdMS_TO_TICKS(80));//实际测试至少30ms
    ESP_ERROR_CHECK(AHT20_register_read(statuscmd, 1,data, 1));  
     ESP_LOGI(I2C_TAG, "status = 0x%X", data[0]);
    while (((data[0]&0x80)==0x80)&&(timecount < I2C_MASTER_TIMEOUT_MS * 1000) ) // 等待BUSY位清空   
    {
         usleep(1); 
         data[0]=0;
        ESP_ERROR_CHECK(AHT20_register_read(statuscmd, 1,data, 1));  
        ESP_LOGI(I2C_TAG, "status = 0x%X", data[0]);       
        timecount++;
    }   
    //读取测量数据
   ESP_ERROR_CHECK(i2c_master_read_from_device(I2C_MASTER_NUM, AHT20_SENSOR_ADDR,data,6,I2C_MASTER_TIMEOUT_MS / portTICK_PERIOD_MS));
    for(int i=0;i<6;i++)
    {
        ESP_LOGI(I2C_TAG, "0x%X", data[i]); 
    } 

    ESP_ERROR_CHECK(i2c_driver_delete(I2C_MASTER_NUM));
      //计算温湿度并打印
    //接收到的6个字节数据,第一个为状态字节,第二第三及第四字节的高四位为湿度,第四字节的低四位和第五第六字节为温度
    tmp32=0;
    tmp32 = ((uint32_t)data[1])<<12 |  ((uint32_t)data[2])<<4 |(data[3]>>4);
    *humi=(float)tmp32/ 1048576 *100;
    //ESP_LOGI(I2C_TAG, "tmp32: 0x%lx,humi: %.2f",tmp32,humi); //tmp32为长整型,打印时需要注意
    tmp32=0;
    tmp32 = ((uint32_t)(data[3]&0x0f))<<16 |  ((uint32_t)data[4])<<8 |(data[5]);
    *temp=(float)tmp32/ 1048576 * 200-50;
    //ESP_LOGI(I2C_TAG, "tmp32: 0x%lx,temp: %.2f",tmp32,temp); 
    ESP_LOGI(I2C_TAG, "I2C de-initialized successfully");
}

解决方案:

        按照AHT20资料,在启动测量后增加了等待80ms的程序。

        I2C的通信需要按照I2C从机模块的时序流程进行编程。否则程序上会存在隐患,可能会因为从机模块的个体差异导致一些莫名的问题。


原文地址:https://blog.csdn.net/u013534357/article/details/142987603

免责声明:本站文章内容转载自网络资源,如本站内容侵犯了原著者的合法权益,可联系本站删除。更多内容请关注自学内容网(zxcms.com)!