Using a dual-function pin

sb_espressif
Posts: 28
Joined: Fri Dec 08, 2023 3:04 am

Using a dual-function pin

Postby sb_espressif » Mon Dec 11, 2023 11:37 pm

Hi there;

I'm relatively new to embedded systems, interrupts, and especially to esp-idf, an am trying to wrap my head around something. I'm trying to write a driver for the TLV493D magnetic encoder. This encoder is - apparently - somewhat peculiar in that it communicates exclusively over I2C, but shares its SCL pin with /INT (which is to say: the sensor can send an interrupt to alert the mcu that it has data available to read).

Setting aside how difficult I think it might be to manage detecting an interrupt on a clock line with multiple peripherals, I'd like to figure out how to get it working with a single-mcu-single-sensor setup. But even then, I'm unsure how to configure this pin to be both an output for SCL and an input for INT.

I *think*, once configured, I can do something like disable the interrupt (somehow) inside the ISR, do the data read, and re-enable the interrupt. But I haven't yet made it far enough to try that algorithm because I can't quite figure out how to configure things to begin with. Might anyone have some advice? (also, I'm sure there are lots of ways to sidestep this difficulty; I'd like to learn how it can be done because, I guess, I'm stubborn and eager to learn).

Here's a snippet from the code I'm trying (guided largely by chatGPT/BARD, who really seem to struggle keeping up with the changes in various esp-idf versions. I'm using 5.1.2 currently)

Code: Select all

static void IRAM_ATTR tlv_interrupt(void *arg) {

    // if we made it here we're on the right track...
}

void app_main() {

  
    i2c_config_t conf = {
        .mode = I2C_MODE_MASTER,                // Configure Master mode
        .sda_io_num = I2C_MASTER_SDA_IO,        // SDA pin
        .scl_io_num = I2C_MASTER_SCL_INT_IO,    // SCL pin
        .master.clk_speed = I2C_MASTER_FREQ_HZ, // Clock speed
        // .sda_pullup_en = GPIO_PULLUP_ENABLE, // I bypass these because I already include
        // .scl_pullup_en = GPIO_PULLUP_ENABLE, // 2.2k resistors on my breakout boards.
    };
    
    // Initialize the above configuration
    i2c_param_config(I2C_MASTER_NUM, 
                        &conf);

    i2c_driver_install( I2C_MASTER_NUM,             
                        conf.mode,                  
                        0,  
                        0,  
                        0                           
                       );

    // doing the below seems to break communicaiton with the sensor

    gpio_config_t gpio_interrupt_conf = {
        .pin_bit_mask = (1ULL << I2C_MASTER_SCL_INT_IO), 
        .mode = GPIO_MODE_INPUT_OUTPUT_OD, // not sure what this should be
        //.pull_up_en = GPIO_PULLUP_ENABLE,  
        .intr_type = GPIO_INTR_LOW_LEVEL,
    };

    gpio_config(&gpio_interrupt_conf);
    gpio_isr_handler_add(I2C_MASTER_SCL_INT_IO, tlv_interrupt, NULL);
    
    ...
    
    

MicroController
Posts: 1708
Joined: Mon Oct 17, 2022 7:38 pm
Location: Europe, Germany

Re: Using a dual-function pin

Postby MicroController » Tue Dec 12, 2023 1:34 pm

do something like disable the interrupt (somehow) inside the ISR, do the data read, and re-enable the interrupt.
Potential timing constraints aside, this will work. The only issue is that using the IDF I2C driver you cannot start/perform an I2C transaction from inside an ISR. From the GPIO ISR, you'll have to send a signal to a task which then does the I2C transaction. The overhead of switching contexts (twice) before the I2C driver even starts preparing the transaction may or may not cause timing issues.
You could also create your own version of i2c_master_cmd_begin(...) which would still prepare the transaction and wait for it to finish, but would not actually start it. Then you can start the transaction via a single register write from the GPIO ISR, and when it's done your custom cmd_begin would return just like the normal I2C driver.

sb_espressif
Posts: 28
Joined: Fri Dec 08, 2023 3:04 am

Re: Using a dual-function pin

Postby sb_espressif » Tue Dec 12, 2023 2:24 pm

Thank you! I think what I might try doing is disabling the interrupt and setting a simple "data_available" flag inside the ISR itself, then returning to main to do the data read and re-enabling the interrupt - I suspected dealing with i2c communication inside the interrupt handler itself might cause even more complications. Or is what I'm describing basically the same thing as what you're saying?

But before I can even get to that part, I'm stuck on the configuration bit (I commented this in the code snippet above). I can get the i2c stuff up and running, but when I try to configure the SCL pin using gpio_config(), I get timeout errors and the i2c communication stops working entirely. Any advice what I might be doing wrong?

MicroController
Posts: 1708
Joined: Mon Oct 17, 2022 7:38 pm
Location: Europe, Germany

Re: Using a dual-function pin

Postby MicroController » Tue Dec 12, 2023 4:07 pm

sb_espressif wrote:
Tue Dec 12, 2023 2:24 pm
Thank you! I think what I might try doing is disabling the interrupt and setting a simple "data_available" flag inside the ISR itself, then returning to main to do the data read and re-enabling the interrupt - I suspected dealing with i2c communication inside the interrupt handler itself might cause even more complications. Or is what I'm describing basically the same thing as what you're saying?
Yes, that's my suggestion :) Instead of setting a flag you can use a semaphore or "direct-to-task notification" on which the task can wait/block.
I can get the i2c stuff up and running, but when I try to configure the SCL pin using gpio_config(), I get timeout errors and the i2c communication stops working entirely. Any advice what I might be doing wrong?
Yes, gpio_config(...) disconnects a pin from any peripheral it may already be connected to. You may have to use esp_rom_gpio_connect_in_signal(...) and/or esp_rom_gpio_connect_out_signal(...) to connect both I2C and GPIO input to the same pin.

sb_espressif
Posts: 28
Joined: Fri Dec 08, 2023 3:04 am

Re: Using a dual-function pin

Postby sb_espressif » Tue Dec 12, 2023 7:41 pm

Yes, gpio_config(...) disconnects a pin from any peripheral it may already be connected to. You may have to use esp_rom_gpio_connect_in_signal(...) and/or esp_rom_gpio_connect_out_signal(...) to connect both I2C and GPIO input to the same pin.
Oh this is so helpful to learn, thanks!

That function you mentioned (esp_rom_gpio_connect_in/out_signal) seems quite buried in the esp-idf code - I can't find documentation for it and I'm not sure how I might have found it on my own. How does it relate to gpio_iomux_in/out, which I find on the GPIO documentation page? To my very untrained eyes they seem related?

MicroController
Posts: 1708
Joined: Mon Oct 17, 2022 7:38 pm
Location: Europe, Germany

Re: Using a dual-function pin

Postby MicroController » Tue Dec 12, 2023 10:53 pm

How does it relate to gpio_iomux_in/out, which I find on the GPIO documentation page? To my very untrained eyes they seem related?
I can't tell for sure, but I think these are indeed functionally equivalent to the ROM functions. I 'found' the ROM functions being used in the UART driver when I was looking to mess with the UART's signals, so ... might as well. But the 'official' GPIO functions are probably a better choice :)
I also haven't looked into the ESP32's I2C signals specifically, but this may be relevant there too:
A 'signal' can be routed via the GPIO matrix to the IO MUX to a pad ("pin"). This provides the flexibility of the GPIO matrix for routing.
Some signals can alternatively be connected to the IO MUX directly, then to specific pins. The driver, when setting up the pins, may try to use the 'direct to IO MUX' route, and fall back to GPIO matrix + IO MUX (only) if necessary, i.e. when the selected pins are not usable for the required signal via IO MUX directly.
If you want to connect multiple (input) signals, or GPIO and a peripheral signal, to the same pin, the peripheral's signal must be routed via the GPIO matrix too though the driver may originally select direct IO MUX. You may have to 'manually' reroute both the peripheral's signal and the GPIO via the GPIO matrix even though the driver itself already connects the signal to the correct pin via the IO MUX.

I'm sure this sounds confusing. Maybe take a look into the TRM where GPIO matrix and IO MUX are described; then the above may start to make sense at some point :)

MicroController
Posts: 1708
Joined: Mon Oct 17, 2022 7:38 pm
Location: Europe, Germany

Re: Using a dual-function pin

Postby MicroController » Tue Dec 12, 2023 11:13 pm

- Or, instead of connecting I2C and GPIO at the same time, you just switch the pin(s) to I2C, do the transaction, and then switch back to GPIO...

sb_espressif
Posts: 28
Joined: Fri Dec 08, 2023 3:04 am

Re: Using a dual-function pin

Postby sb_espressif » Tue Dec 12, 2023 11:58 pm

THAT is a very appealing idea! How do I do something like that? I admit I'm in over my head here trying to understand how to configure this pin for I2C vs. interrupts...

MicroController
Posts: 1708
Joined: Mon Oct 17, 2022 7:38 pm
Location: Europe, Germany

Re: Using a dual-function pin

Postby MicroController » Wed Dec 13, 2023 12:12 am

You should be able to alternately connect I2C signals via i2c_set_pin(...) and GPIO via gpio_config(...) 'on-the-fly', implicitly disconnecting the respective other signal at the same time.
(You may want to disconnect both I2C signals, and reconnect them only after the rising edge of /INT because the ESP32's I2C controller seems to easily get upset&confused if it sees unexpected signals on the bus.
Edit: Nah, the SDA line doesn't move anyway, so no need to GPIO it. But reconnect SCL only after /INT is back up.)

User avatar
ok-home
Posts: 78
Joined: Sun May 02, 2021 7:23 pm
Location: Russia Novosibirsk
Contact:

Re: Using a dual-function pin

Postby ok-home » Wed Dec 13, 2023 3:52 am

hi

With what period do you want to take measurements?

If the period is comparable to the freertos tick ( 10 mS ), refuse to use the interrupt at all. The sensor is guaranteed to have time to take measurements.

Who is online

Users browsing this forum: Romaszewski and 72 guests