boarchuz / HULP

ESP32 ULP Coprocessor Helper
MIT License
180 stars 18 forks source link

problems reading sht3x i2c sensor #12

Closed will-emmerson closed 3 years ago

will-emmerson commented 3 years ago

Thanks for making this amazing library, I had previously put off using the ULP because I don't really know assembly but using this I think it might be possible. Having said that, I am struggling to read from a sht3x sensor which requires a write of 2 bytes and a read of 6 bytes. I have created the following by piecing together examples there are no interupts firing in the app, unlike the simpler i2c examples:

#define SCL_PIN GPIO_NUM_26
#define SDA_PIN GPIO_NUM_25

#define SLAVE_ADDR 0x0
#define SLAVE_REG 0x44

RTC_SLOW_ATTR ulp_var_t ulp_write_cmd[] = {
        HULP_I2C_CMD_HDR(SLAVE_ADDR, SLAVE_REG, 2),
        HULP_I2C_CMD_2B(0x24, 0x00),
};
RTC_SLOW_ATTR ulp_var_t ulp_read_cmd[HULP_I2C_CMD_BUF_SIZE(6)] = {
        HULP_I2C_CMD_HDR(SLAVE_ADDR, SLAVE_REG, 6),
};

gpio_num_t LED_ERR = GPIO_NUM_13;

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

    const ulp_insn_t program[] = {

            // write 2 bytes
            I_MOVO(R1, ulp_write_cmd),
            M_MOVL(R3, LABEL_I2C_WRITE_RETURN),
            M_BX(LABEL_I2C_WRITE),
            M_LABEL(LABEL_I2C_WRITE_RETURN),
            M_BGE(LABEL_I2C_ERROR, 1),

            // delay 20ms
            M_DELAY_MS_20_1000(20),

            // read 6 bytes
            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),

            // notify change and exit
            I_WAKE(),
            I_HALT(),

            M_LABEL(LABEL_I2C_ERROR),
            I_GPIO_SET(LED_ERR, 1),
            I_END(),

            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), 1ULL * 1000 * 1000, 0));
    ESP_ERROR_CHECK(hulp_ulp_run(0));
}

I've tried to copy the I2CBB_CMD example in the documentation but that only does a single read. The sensor definitely works and the following normal i2c code returns a value:

    i2c_init(GPIO_NUM_25, GPIO_NUM_26);

    uint8_t out[] = {0x24, 0x00};
    i2c_write(SLAVE_REG, out, 2);

    vTaskDelay(pdMS_TO_TICKS(10));

    #define in_len 6
    uint8_t in[in_len] = {};
    i2c_read(SLAVE_REG, in, in_len);

I've also tried RTC_GPIO_MODE_INPUT_OUTPUT but that doesn't work either.

boarchuz commented 3 years ago

Thanks, @will-emmerson ! Can you confirm that you've set the slave address? #define SLAVE_ADDR 0x0

will-emmerson commented 3 years ago

I'm fairly sure the SLAVE_ADDR is 0, that's what it defaults to when using normal esp-idf i2c which works.

boarchuz commented 3 years ago

The datasheet says that the device's address is 0x44 or 0x45 depending on the configuration of its address pin. Maybe you have SLAVE_ADDR and SLAVE_REG the wrong way around?

will-emmerson commented 3 years ago

You're right, I am getting interupts now but for some reason not any data. The problem is I don't really understand what SLAVE_REG is, according to the datasheet it does a 2 byte write with a 'command', then does a 6 byte read but it doesn't seem to mention registers. Unless I'm getting mixed up and {0x24 0x00} is the register. I am reading the data with this:

printf("0: %u\n", ulp_read_cmd[HULP_I2C_CMD_DATA_OFFSET].val);
printf("1: %u\n", ulp_read_cmd[HULP_I2C_CMD_DATA_OFFSET + 1].val);
printf("2: %u\n", ulp_read_cmd[HULP_I2C_CMD_DATA_OFFSET + 2].val);
will-emmerson commented 3 years ago

Sorry, actually I'm not getting interrupts, so clearly there's still an error somewhere. Will have a proper look tomorrow.

boarchuz commented 3 years ago

I can see what's wrong now! You just need to view MSB|LSB in the SHT3X datasheet as SLAVE_REG|FIRST_BYTE in this implementation. In other words, to send your {0x24, 0x00} command, prepare a command to send 1 byte (0x00) to slave's register 0x24:

RTC_SLOW_ATTR ulp_var_t ulp_write_cmd[] = {
        HULP_I2C_CMD_HDR(SLAVE_ADDR, 0x24, 1),
        HULP_I2C_CMD_1B(0x00),
};

Except now I realise that a read might not work, because for a read command, I have it writing the 1 byte slave register before starting the read, but the SHT3X might not like receiving one byte...

I would suggest checking that the above write command works first (without read). Once you're confident that works, try add the read back in either like this:

RTC_SLOW_ATTR ulp_var_t ulp_read_cmd[HULP_I2C_CMD_BUF_SIZE(6)] = {
        HULP_I2C_CMD_HDR(SLAVE_ADDR, 0x00, 6),
};

or like this:

RTC_SLOW_ATTR ulp_var_t ulp_read_cmd[HULP_I2C_CMD_BUF_SIZE(6)] = {
        HULP_I2C_CMD_HDR(SLAVE_ADDR, 0x24, 6),
};
will-emmerson commented 3 years ago

Awesome, it's working now using 0x00 as register and error led now also working. Still a lot of work to do because I want to only wake the ESP up when the temperature has changed by a certain amount e.g. 0.1°C but I will try and piece together some of the examples. If I manage to get it working I'll make a pull request for a sht3x example, it's a fairly popular sensor.

boarchuz commented 3 years ago

I've just pushed a change to allow a 'raw' read without setting the pointer first, as some devices such as SHT3X require. Give it a try, the command looks like this:

RTC_SLOW_ATTR ulp_var_t ulp_read_cmd[HULP_I2C_CMD_BUF_SIZE(6)] = {
        HULP_I2C_CMD_HDR_NO_PTR(SLAVE_ADDR, 6),
};
will-emmerson commented 3 years ago

Thanks, I'll give it a go. Something that might be useful is an abs(x) or absolute_difference(a,b) macro, that part was a bit of a struggle for me because I don't know assembly, but perhaps there's an easy way of doing it. I did it with jumps but it seems you can do it with XOR - but there is no XOR instruction I can find. I will have a go at these when I add the example.

will-emmerson commented 3 years ago

Thanks, got it all working and made a pull request with the example. There's probably a better way to do the abs part but it seems to work.