How sharing SPI for ethernet w5500, lora sx1276 and sdcard

mauribr123
Posts: 6
Joined: Mon Mar 22, 2021 7:47 pm

How sharing SPI for ethernet w5500, lora sx1276 and sdcard

Postby mauribr123 » Tue Aug 31, 2021 6:57 pm

Good afternoon,
I have a firmware doubt that totally implies my hardware design. I'm designing a PCB and I wanted to share the same SPI for ethernet w5500, lora sx1276 and sdcard, since HSPI or VSPI make it possible to use 3 CS slaves.

However, looking at all the ESP-NETIF API for w5500 and the mqtt and tcp socket layers that I will use, it is not clear how I can implement a mutex in the call of these functions to protect shared access to this SPI by various tasks, since the SDCARD and LORA will make use of the same SPI, with the lib lora I built, I managed to insert the mutexes properly, however, in the case of the LIB of the sdcard and ethernet ESP-NETIF, I have no idea how to build this from a thread-safe way.

Since in the case of Ethernett mainly, I will receive external interrupts and I need to ensure that the lora and not sdcard will be consuming the bus at this time.

I saw in the documentation that there are functions spi_device_acquire_bus() and spi_device_release_bus(), which are a kind of mutex, but again, how will I ensure that the MQTT callback event loop at the time it happens is pulling data via ESP-NETIF , do not collide with my SPI when I am occupying bus with LORA ? It would be awesome to have this clear of how to properly insert the FreeRTOS mutexes into the ethernet API, so that you can share the SPI with as much confidence as possible.

Att. Maurício Alencar

ESP_Sprite
Posts: 9723
Joined: Thu Nov 26, 2015 4:08 am

Re: How sharing SPI for ethernet w5500, lora sx1276 and sdcard

Postby ESP_Sprite » Wed Sep 01, 2021 3:07 am

As long as you use the SPI API to add devices to the bus, you should not have to worry about multithreading race conditions: the SPI driver is designed to solve that for you. The spi acquire/release calls can be useful if you have a device that needs continuous, uninterrupted access to the SPI bus without transactions to other devices going on: unless you have a SPI device that has specific requirements, you generally don't need it.

mauribr123
Posts: 6
Joined: Mon Mar 22, 2021 7:47 pm

Re: How sharing SPI for ethernet w5500, lora sx1276 and sdcard

Postby mauribr123 » Wed Sep 01, 2021 1:45 pm

Thanks for the reply, ESP Sprite.

Just to finish my doubt, when I call the spi_bus_add_device() function, will I be adding the devices to the VSPI bus correct? as in the code used below!

Code: Select all

ESP_ERROR_CHECK(spi_bus_add_device(VSPI_HOST, &cfg_eth, &spi_handle[i]));

Code: Select all

spi_bus_add_device(VSPI_HOST, &cfg_lora, &__spi);

Code: Select all

spi_bus_add_device(VSPI_HOST, &cfg_sdcard, &sd_handle);

And from the moment I added the devices to the bus, can I call, for example, the lora_write_reg function under any task without worrying about additional mutex because the driver itself will take care of it? even knowing that I can pull the cs pin to LOW and capture the SPI bus on different devices (like a task calling lora_write_reg and another task writing on sdcard or ethernet?). So my last question is how the driver ensures that this CS pin is used 1 at a time, since it is the user's responsibility to set it down before calling spi_device_transmit(), as in the lora_write_reg code below?

Code: Select all

void 
lora_write_reg(int reg, int val)
{
   uint8_t out[2] = { 0x80 | reg, val };
   uint8_t in[2];

   spi_transaction_t t = {
      .flags = 0,
      .length = 8 * sizeof(out),
      .tx_buffer = out,
      .rx_buffer = in  
   };

   gpio_set_level(CONFIG_CS_GPIO, 0);
   spi_device_transmit(__spi, &t);
   gpio_set_level(CONFIG_CS_GPIO, 1);
}

ESP_Sprite
Posts: 9723
Joined: Thu Nov 26, 2015 4:08 am

Re: How sharing SPI for ethernet w5500, lora sx1276 and sdcard

Postby ESP_Sprite » Thu Sep 02, 2021 2:34 am

Given that your program makes use of the SPI driver properly, you can do exactly what you sketched out. (However, do note it cannot protect you from issues if multiple threads try to access *the same* SPI device. If you were to have e.g. two threads writing to the LoRa device, you would have to implement your own muxing there.)

That LoRa driver does not seem like it makes proper use of the SPI driver, however. It should be re-written so the CS line is controlled by the SPI driver, and not tweaked manually, or (in case it needs some weird CS behaviour) it should claim the SPI bus for itself when doing a transaction, releasing it afterwards.

mauribr123
Posts: 6
Joined: Mon Mar 22, 2021 7:47 pm

Re: How sharing SPI for ethernet w5500, lora sx1276 and sdcard

Postby mauribr123 » Wed Sep 15, 2021 1:45 am

Good night ESP_Sprite, I was able to successfully share the SPI for the W5500 and LORA, with the drive initialization at 20MHz and using the spi_bus_add_device() function, but I'm trying to integrate the SDCARD example that I'll leave the link below, and there's no example in it where I can configure which SPI pins will be used, what bus speed will be used and how I add this SDCard to the SPI driver with the function spi_bus_add_device(), could you help me please? before asking this question, i've already gone over the example a lot and haven't found a solution... Here's the link to the SDSPI example that I used as a basis to try to modify the use of the SPI according to the other devices, such as modifying the GPIO of the SPI, change to SPI2_HOST and change to 20MHz so that the bus is compatible between SDCARD, LORA and W5500.

https://github.com/espressif/esp-idf/tr ... card/sdspi

ESP_Sprite
Posts: 9723
Joined: Thu Nov 26, 2015 4:08 am

Re: How sharing SPI for ethernet w5500, lora sx1276 and sdcard

Postby ESP_Sprite » Wed Sep 15, 2021 10:10 am

Did you read the docs page on that? It may clarify everything a bit. Effectively, you initialize the SPI bus using spi_bus_initialize (you already do that for the LoRa/W5500 chips, so no need to add that to your code) and then you add a SPI card to it using esp_vfs_fat_sdspi_mount().

mauribr123
Posts: 6
Joined: Mon Mar 22, 2021 7:47 pm

Re: How sharing SPI for ethernet w5500, lora sx1276 and sdcard

Postby mauribr123 » Wed Sep 29, 2021 2:13 pm

Hello ESP_Sprit, I followed your recommendations and everything went well in the initialization of SDSPI_SDMMC, however, both the LORA and the W5500 showed totally strange behavior, and I found a little problem that I believe is related to your driver, in the case of LORA and SDCARD, I used the spi_add_bus_device function so that the SPI driver could manage the CS PIN and all the communication mutex as you recommended, however, when I used the function to mount the SD CARD that you mentioned esp_vfs_fat_sdspi_mount(), I noticed that it changed a lot of points on the SPI bus, and worst of all, that it does not add a device to the SPI with CS pin control by the SPI driver, causing collisions to occur probably on the SPI bus and for this reason W5500 and LORA are totally frozen or if misbehaving! Taking a look at the configure_spi_dev() function that is used to add the device to the bus, it is possible to prove that the SPI driver unfortunately does not take care of CS :cry:

Code: Select all

/**
 * (Re)Configure SPI device. Used to change clock speed.
 * @param slot Pointer to the slot to be configured
 * @param clock_speed_hz  clock speed, Hz
 * @return ESP_OK on success
 */
static esp_err_t configure_spi_dev(slot_info_t *slot, int clock_speed_hz)
{
    if (slot->spi_handle) {
        // Reinitializing
        spi_bus_remove_device(slot->spi_handle);
        slot->spi_handle = NULL;
    }
    spi_device_interface_config_t devcfg = {
        .clock_speed_hz = clock_speed_hz,
        .mode = 0,
        // For SD cards, CS must stay low during the whole read/write operation,
        // rather than a single SPI transaction.
        .spics_io_num = GPIO_NUM_NC,
        .queue_size = SDSPI_TRANSACTION_COUNT,
    };
    return spi_bus_add_device(slot->host_id, &devcfg, &slot->spi_handle);
}
Below I'll leave the code I'm using to start the SDCARD bus with a frequency of 20MHZ (compatible with adding my other devices to the bus)

Code: Select all

esp_err_t ret;

    // Options for mounting the filesystem.
    // If format_if_mount_failed is set to true, SD card will be partitioned and
    // formatted in case when mounting fails.
    esp_vfs_fat_sdmmc_mount_config_t mount_config = {
        .format_if_mount_failed = false,
        .max_files = 5,
        .allocation_unit_size = 16 * 1024
    };

    const char mount_point[] = MOUNT_POINT;
    ESP_LOGI(__func__, "Initializing SD card");

    // Use settings defined above to initialize SD card and mount FAT filesystem.
    // Note: esp_vfs_fat_sdmmc/sdspi_mount is all-in-one convenience functions.
    // Please check its source code and implement error recovery when developing
    // production applications.
    ESP_LOGI(__func__, "Using SPI peripheral");

    sdmmc_host_t host = SDSPI_HOST_DEFAULT();
    // This initializes the slot without card detect (CD) and write protect (WP) signals.
    // Modify slot_config.gpio_cd and slot_config.gpio_wp if your board has these signals.
    sdspi_device_config_t slot_config = SDSPI_DEVICE_CONFIG_DEFAULT();
    slot_config.gpio_cs = 21;
    slot_config.host_id = host.slot;

    ESP_LOGI(TAG, "Mounting filesystem");
    ret = esp_vfs_fat_sdspi_mount(mount_point, &host, &slot_config, &mount_config, &card);

    if (ret != ESP_OK) {
        if (ret == ESP_FAIL) {
            ESP_LOGE(__func__, "Failed to mount filesystem. "
                     "If you want the card to be formatted, set the EXAMPLE_FORMAT_IF_MOUNT_FAILED menuconfig option.");
        } else {
            ESP_LOGE(__func__, "Failed to initialize the card (%s). "
                     "Make sure SD card lines have pull-up resistors in place.", esp_err_to_name(ret));
        }
        return;
    }

    ESP_LOGI(__func__, "Filesystem mounted");

    // Card has been initialized, print its properties
    sdmmc_card_print_info(stdout, card);
  
So, ESP_Sprite, do you have any recommendations to follow given the limitation mentioned above, I'm sure this is a limitation and I have some alternatives within the IDF SDK to get around this and have the SDCARD via SPI working with shared SPI bus? With SDCARD I get the bus at the right limit of devices, sharing between LORA, W5500 and SDCARD, and unfortunately I won't be able to use the SDCARD in another SPI because I don't have any pins available in the project :cry: , if I did have the pins available, I would happily use the SDCARD on another SPI. Try to help me so we can close this topic with excellence!

since then, thanks for all the support given so far!

mauribr123
Posts: 6
Joined: Mon Mar 22, 2021 7:47 pm

Re: How sharing SPI for ethernet w5500, lora sx1276 and sdcard

Postby mauribr123 » Sun Oct 10, 2021 2:43 pm

Hello @ESP_Sprite, any recommendations on how to solve the problem I mentioned above?

dmaxben
Posts: 108
Joined: Thu Nov 16, 2017 6:04 pm

Re: How sharing SPI for ethernet w5500, lora sx1276 and sdcard

Postby dmaxben » Sun Oct 17, 2021 12:04 am

mauribr123 wrote:
Wed Sep 29, 2021 2:13 pm
Hello ESP_Sprit, I followed your recommendations and everything went well in the initialization of SDSPI_SDMMC, however, both the LORA and the W5500 showed totally strange behavior, and I found a little problem that I believe is related to your driver, in the case of LORA and SDCARD, I used the spi_add_bus_device function so that the SPI driver could manage the CS PIN and all the communication mutex as you recommended, however, when I used the function to mount the SD CARD that you mentioned esp_vfs_fat_sdspi_mount(), I noticed that it changed a lot of points on the SPI bus, and worst of all, that it does not add a device to the SPI with CS pin control by the SPI driver, causing collisions to occur probably on the SPI bus and for this reason W5500 and LORA are totally frozen or if misbehaving! Taking a look at the configure_spi_dev() function that is used to add the device to the bus, it is possible to prove that the SPI driver unfortunately does not take care of CS :cry:

Code: Select all

/**
 * (Re)Configure SPI device. Used to change clock speed.
 * @param slot Pointer to the slot to be configured
 * @param clock_speed_hz  clock speed, Hz
 * @return ESP_OK on success
 */
static esp_err_t configure_spi_dev(slot_info_t *slot, int clock_speed_hz)
{
    if (slot->spi_handle) {
        // Reinitializing
        spi_bus_remove_device(slot->spi_handle);
        slot->spi_handle = NULL;
    }
    spi_device_interface_config_t devcfg = {
        .clock_speed_hz = clock_speed_hz,
        .mode = 0,
        // For SD cards, CS must stay low during the whole read/write operation,
        // rather than a single SPI transaction.
        .spics_io_num = GPIO_NUM_NC,
        .queue_size = SDSPI_TRANSACTION_COUNT,
    };
    return spi_bus_add_device(slot->host_id, &devcfg, &slot->spi_handle);
}
Below I'll leave the code I'm using to start the SDCARD bus with a frequency of 20MHZ (compatible with adding my other devices to the bus)

Code: Select all

esp_err_t ret;

    // Options for mounting the filesystem.
    // If format_if_mount_failed is set to true, SD card will be partitioned and
    // formatted in case when mounting fails.
    esp_vfs_fat_sdmmc_mount_config_t mount_config = {
        .format_if_mount_failed = false,
        .max_files = 5,
        .allocation_unit_size = 16 * 1024
    };

    const char mount_point[] = MOUNT_POINT;
    ESP_LOGI(__func__, "Initializing SD card");

    // Use settings defined above to initialize SD card and mount FAT filesystem.
    // Note: esp_vfs_fat_sdmmc/sdspi_mount is all-in-one convenience functions.
    // Please check its source code and implement error recovery when developing
    // production applications.
    ESP_LOGI(__func__, "Using SPI peripheral");

    sdmmc_host_t host = SDSPI_HOST_DEFAULT();
    // This initializes the slot without card detect (CD) and write protect (WP) signals.
    // Modify slot_config.gpio_cd and slot_config.gpio_wp if your board has these signals.
    sdspi_device_config_t slot_config = SDSPI_DEVICE_CONFIG_DEFAULT();
    slot_config.gpio_cs = 21;
    slot_config.host_id = host.slot;

    ESP_LOGI(TAG, "Mounting filesystem");
    ret = esp_vfs_fat_sdspi_mount(mount_point, &host, &slot_config, &mount_config, &card);

    if (ret != ESP_OK) {
        if (ret == ESP_FAIL) {
            ESP_LOGE(__func__, "Failed to mount filesystem. "
                     "If you want the card to be formatted, set the EXAMPLE_FORMAT_IF_MOUNT_FAILED menuconfig option.");
        } else {
            ESP_LOGE(__func__, "Failed to initialize the card (%s). "
                     "Make sure SD card lines have pull-up resistors in place.", esp_err_to_name(ret));
        }
        return;
    }

    ESP_LOGI(__func__, "Filesystem mounted");

    // Card has been initialized, print its properties
    sdmmc_card_print_info(stdout, card);
 
So, ESP_Sprite, do you have any recommendations to follow given the limitation mentioned above, I'm sure this is a limitation and I have some alternatives within the IDF SDK to get around this and have the SDCARD via SPI working with shared SPI bus? With SDCARD I get the bus at the right limit of devices, sharing between LORA, W5500 and SDCARD, and unfortunately I won't be able to use the SDCARD in another SPI because I don't have any pins available in the project :cry: , if I did have the pins available, I would happily use the SDCARD on another SPI. Try to help me so we can close this topic with excellence!

since then, thanks for all the support given so far!
Did you ever get this working?

Im looking to use an SD card and an MCP2515 CAN controller on the same SPI bus...hopefully its possible...?

Who is online

Users browsing this forum: No registered users and 67 guests