HULP: I2C bitbang CRC third byte does not match

Great job implementing the I2C bitbang mode in ULP! Your repo deserves more stars.

I’m porting the C code of reading the Sensirion SDP8xx digital sensor to HULP I2C bitbang. I’m reading the sensor periodically in continuous mode. C code is proven to work right, and it’s here https://github.com/dizcza/sdpsensor-esp-arduino/blob/master/sdpsensor.cpp

The issue happens with the third byte which is CRC according to the documentation. The read value does not match with the expected (only in ULP mode, works fine in plain C).


#include <esp_log.h>
#include <esp_sleep.h>
#include <freertos/FreeRTOS.h>
#include <freertos/task.h>
#include <hulp.h>
#include <hulp_i2cbb.h>

#include "sdpsensor.h"


static const char *TAG = "MAIN";

static const uint8_t SLAVE_ADDR = 0x25;
static const gpio_num_t SDA_PIN = GPIO_NUM_33;
static const gpio_num_t SCL_PIN = GPIO_NUM_32;

static RTC_SLOW_ATTR ulp_var_t ulp_read_cmd[HULP_I2C_CMD_BUF_SIZE(3)] = {
        HULP_I2C_CMD_HDR_NO_PTR(SLAVE_ADDR, 3),
};


void init_ulp() {
    enum {
        LABEL_I2C_READ,
        LABEL_I2C_READ_CONTINUOUS,
        LABEL_I2C_READ_RETURN,
        LABEL_I2C_WRITE,
        LABEL_I2C_ERROR,
    };

    const ulp_insn_t program[] = {
            M_LABEL(LABEL_I2C_READ_CONTINUOUS),
            M_DELAY_US_100_5000(1000),
            I_MOVO(R1, ulp_read_cmd),
            M_MOVL(R3, LABEL_I2C_READ_RETURN),
            M_BX(LABEL_I2C_READ),
            M_LABEL(LABEL_I2C_READ_RETURN),
            M_BGE(LABEL_I2C_ERROR, 1),
            M_BX(LABEL_I2C_READ_CONTINUOUS),

            M_LABEL(LABEL_I2C_ERROR),
            I_WAKE(),
            I_END(),                // end ulp program so it won't run again
            I_HALT(),

            M_INCLUDE_I2CBB_CMD(LABEL_I2C_READ, LABEL_I2C_WRITE, SCL_PIN, SDA_PIN)
    };

    ESP_ERROR_CHECK(hulp_configure_pin(SCL_PIN, RTC_GPIO_MODE_INPUT_ONLY, GPIO_FLOATING, 0));
    ESP_ERROR_CHECK(hulp_configure_pin(SDA_PIN, RTC_GPIO_MODE_INPUT_ONLY, GPIO_FLOATING, 0));

    hulp_peripherals_on();

    vTaskDelay(1000 / portTICK_PERIOD_MS);

    ESP_ERROR_CHECK(hulp_ulp_load(program, sizeof(program), 10ULL * 1000, 0));
    ESP_ERROR_CHECK(hulp_ulp_run(0));
}


void print_result() {
    const int16_t dp = (int16_t) ulp_read_cmd[HULP_I2C_CMD_DATA_OFFSET].val;
    const uint8_t crc = ulp_read_cmd[HULP_I2C_CMD_DATA_OFFSET + 1].val_bytes[0];
    ESP_LOGI(TAG, "Diff pressure %3d CRC %3d expected %3d", dp, crc,  compute_crc((uint8_t*) &dp));
}

void app_main(void) {

    SDPSensor_I2C_GPIO i2c_gpio = {
            .addr = SLAVE_ADDR,
            .port = 0,
            .sda = SDA_PIN,
            .scl = SCL_PIN
    };
    
    // Init the sensor & start continuous mode
    SDPSensor_Init(&i2c_gpio, NULL);
    
    init_ulp();

    while (1) {
        vTaskDelay(pdMS_TO_TICKS(100));
        print_result();
    }
}

Logs

I (2943) MAIN: Diff pressure  -8 CRC   0 expected   2
I (3043) MAIN: Diff pressure   7 CRC   0 expected  47
I (3143) MAIN: Diff pressure   3 CRC   0 expected 172
I (3243) MAIN: Diff pressure   9 CRC   0 expected  66
I (3343) MAIN: Diff pressure   3 CRC   0 expected 172
I (3443) MAIN: Diff pressure   2 CRC   0 expected  88
I (3543) MAIN: Diff pressure 10751 CRC   0 expected  14   // 10751 looks fishy, need to investigate; probably due to unsynchonous read-write operations of the same memory block
I (3643) MAIN: Diff pressure   4 CRC   0 expected   2
I (3743) MAIN: Diff pressure  -5 CRC   0 expected  47
I (3843) MAIN: Diff pressure   0 CRC   0 expected 129
I (3943) MAIN: Diff pressure  -4 CRC   0 expected 129
I (4043) MAIN: Diff pressure  12 CRC   0 expected  53
I (4143) MAIN: Diff pressure   1 CRC   0 expected 117
I (4243) MAIN: Diff pressure  -3 CRC   0 expected 117
I (4343) MAIN: Diff pressure   1 CRC   0 expected 117
I (4443) MAIN: Diff pressure   4 CRC   0 expected   2

Would much appreciate if you provide any hint why this might happen.

I have several other questions but let’s deal with this one first.

About this issue

  • Original URL
  • State: closed
  • Created 2 years ago
  • Comments: 23 (8 by maintainers)

Most upvoted comments

The ULP can only access RTC Slow Memory. For that reason, I think it’s best to be explicit and use RTC_SLOW_ATTR for anything and everything the ULP will be using.

RTC Fast Memory is very useful if you’re running low on Slow, but can only be accessed by CPU0 so you need to take care using it.

RTC_DATA_ATTR should be avoided here, in my opinion. It will almost always default to Slow but is sometimes Fast, depending on configuration. Only use it if you’re sure you don’t need to care which segment is used, or need to support targets which may not have one or the other segment. Just to be extra clear: There are only two RTC memory segments - Fast and Slow; anything with the RTC_DATA_ATTR attribute will be placed in one of these, depending on config.

So to answer your questions directly:

  1. Yes, because RTC_DATA_ATTR == RTC_SLOW_ATTR for your config. I would suggest using RTC_SLOW_ATTR to be “more correct”.

2 & 3. No, the ULP can only access RTC Slow Mem.

Yes you should make that change, that’s a typo sorry!

There would be no need for the delay usually, so you can safely remove it. It was probably something I stuck there to be conservative with the ULP/bus initialisation one day, and it got copied to all of the others too.