Multiple SPI Busses in parallel does not work

AG_OHP
Posts: 9
Joined: Mon Oct 10, 2022 7:28 am

Multiple SPI Busses in parallel does not work

Postby AG_OHP » Mon Oct 10, 2022 9:40 am

We are developing a System in which 5 SPI Slaves are connected to the ESP32-S3.

For that we intend to use the SPI2_HOST and SPI3_HOST. SPI2 will have 2 slave devices connected and SPI3 3 slave devices.
For this test only one slave is connected to each bus.
Initializing the busses independently, the communication tests works flawlessly. The Problem only occurs if I try to initialize and use both busses at the same time. I initialize the busses in a seperate c file. I try to add the devices in a .c file specific to that device.

The file structure is as followed

components
>SPI
>>include
>>>spi_bus.h
>>>spi_deviceA_driver.h
>>>spi_deviceB_driver.h
>>spi_bus.c
>>spi_deviceA_driver.c
>>spi_deviceB_driver.c

main
>main.c

in main.c the function spi_initialize_busses is called and throws no error
  1. esp_err_t spi_initialize_busses(){
  2. esp_err_t err = ESP_OK;
  3.  
  4. spi_bus_config_t bus_config_ctrl = {
  5.         .mosi_io_num = CTRL_SPI_MOSI_PIN, //PIN 9
  6.         .miso_io_num = CTRL_SPI_MISO_PIN, //PIN 10
  7.         .sclk_io_num = CTRL_SPI_SCLK_PIN, //PIN 46
  8.         .quadwp_io_num = -1,
  9.         .quadhd_io_num = -1,
  10.         .data4_io_num = -1,
  11.         .data5_io_num = -1,
  12.         .data6_io_num = -1,
  13.         .data7_io_num = -1,
  14.         .max_transfer_sz = 0,
  15.         .flags = 0U,
  16.         .intr_flags = ESP_INTR_FLAG_LEVEL1};
  17.  
  18.  
  19.  
  20. spi_bus_config_t bus_config_pwr = {
  21.         .mosi_io_num = PWR_SPI_MOSI_PIN, //47
  22.         .miso_io_num = PWR_SPI_MISO_PIN, // 45
  23.         .sclk_io_num = PWR_SPI_SCLK_PIN, // 48
  24.         .quadwp_io_num = -1,
  25.         .quadhd_io_num = -1,
  26.         .data4_io_num = -1,
  27.         .data5_io_num = -1,
  28.         .data6_io_num = -1,
  29.         .data7_io_num = -1,
  30.         .max_transfer_sz = 0,
  31.         .flags = 0U,
  32.         .intr_flags = ESP_INTR_FLAG_LEVEL1};
  33.  
  34.     err = spi_bus_initialize(PWR_SPI_PERIPHERAL_HOST, &bus_config_pwr, SPI_DMA_DISABLED);  //SPI2_HOST
  35.     ESP_ERROR_CHECK(err);
  36.     err = spi_bus_initialize(CTRL_SPI_PERIPHERAL_HOST, &bus_config_ctrl, SPI_DMA_DISABLED); //SPI3_HOST
  37.     ESP_ERROR_CHECK(err);
  38.     return err;
  39. }
The devices are then added like followed (code lies in spi_devicea_driver.c and spi_deviceb_driver.c respectively:)
  1. spi_device_handle_t spi_handle;
  2.  
  3. esp_err_t deviceA_initialize(void)
  4. {
  5.  
  6.     esp_err_t spi_error = 0;
  7.     spi_device_interface_config_t slave_device_config = {
  8.         .command_bits = 0,
  9.         .address_bits = 0,
  10.         .dummy_bits = 0,
  11.         .mode = 1,
  12.         .duty_cycle_pos = 0,
  13.         .cs_ena_pretrans = 0,
  14.         .cs_ena_posttrans = 0,
  15.         .clock_speed_hz = 1 * 1000 * 1000,
  16.         .input_delay_ns = 0,
  17.         .queue_size = 1,
  18.         .spics_io_num = -1; //multiplexer is used
  19.         .flags = 0U,
  20.         .pre_cb = NULL,
  21.         .post_cb = NULL};
  22.  
  23.     spi_error = spi_bus_add_device(CTRL_SPI_PERIPHERAL_HOST, &slave_device_config, &spi_handle);
  24.     ESP_ERROR_CHECK_WITHOUT_ABORT(spi_error);
  25.     return spi_error;
  26. }
  1. spi_device_handle_t spi_handle;
  2.  
  3. esp_err_t deviceB_initialize(void)
  4. {
  5.  
  6.     esp_err_t spi_error;
  7.     spi_device_interface_config_t slave_device_config = {
  8.         .command_bits = 0,
  9.         .address_bits = 0,
  10.         .dummy_bits = 0,
  11.         .mode = 1,
  12.         .duty_cycle_pos = 0, // 0 equals 50% duty cycle
  13.         .cs_ena_pretrans = 0,
  14.         .cs_ena_posttrans = 0,
  15.         .clock_speed_hz = 1 * 1000 * 1000,
  16.         .input_delay_ns = 0, // may be further adjusted. taken from ESP32 to ESP32 values
  17.         .queue_size = 1,
  18.         .spics_io_num = deviceB_SPI_CS_1, //14
  19.         .flags = 0U,
  20.         .pre_cb = NULL,
  21.         .post_cb = NULL};
  22.  
  23.    
  24.     spi_error = spi_bus_add_device(PWR_SPI_PERIPHERAL_HOST, &slave_device_config, &spi_handle);
  25.     ESP_ERROR_CHECK_WITHOUT_ABORT(spi_error);
  26.  
  27.     return spi_error;
  28. }
The errors are as followed:
  1. I (364) Setup: Initializing SPI Busses
  2. I (364) Setup: Initialized SPI Busses
  3. I (394) Setup: Testing ESP to PWR Communication
  4. I (414) SPI_PWR_TEST: Start Test, sending 3 packets for testing
  5. I (484) SPI_PWR_TEST: Test Finished with 0 Errors
  6. I (484) Setup: Test succesfull
  7. I (484) Setup: Testing ESP to CTRL Communication
  8. ESP_ERROR_CHECK_WITHOUT_ABORT failed: esp_err_t 0x105 (ESP_ERR_NOT_FOUND) at 0x4037abdb
  9. 0x4037abdb: _esp_error_check_failed_without_abort at C:/Users/UserA/esp/esp-idf/components/esp_system/esp_err.c:37
  10. file: "../components/SPI/spi_deviceA_driver.c" line 136
  11. func: deviceA_initialize
  12. expression: spi_error
This Error occurs either at Communication on SPI2_Host OR SPI3_Host. The bus that has a device added first works flawlessly. As soon as SPI2_Host or SPI3_Host has a device added, the other Bus cannot add a device without throwing esp_err_t 0x105 (ESP_ERR_NOT_FOUND).


If i misread the datasheets and User Guide and commited an stupid error i apologize, but if i am correct, the busses should be able to be initialized independently and work.
The Error ONLY occurs if i try to have both busses initialized at the same time. Initializing and Testing the busses individually generates no errors. The workaround right now is to deinit and reinit the busses every communication, however this is not a solution i would deem fit for production. So help to identify the error would be appreciated. :)

Best Regards

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

Re: Multiple SPI Busses in parallel does not work

Postby ESP_Sprite » Tue Oct 11, 2022 1:27 am

That is odd, your logic itself is supposed to work. Any chance you can whittle your code down to the minimum that exhibits this behaviour and post that here, so we can check?

AG_OHP
Posts: 9
Joined: Mon Oct 10, 2022 7:28 am

Re: Multiple SPI Busses in parallel does not work

Postby AG_OHP » Tue Oct 11, 2022 11:10 am

Thanks for the timely reply.
I tested the 2 SPI Devices on 2 Busses in an isolated environment and it worked just fine.
I then retested it within my setup&test function and it stopped working again.

I then disabled the modules one by one, observing wether the bug stops occuring if i do not initialize another communication method.

This is what my setup code looked before:
(After disabling the modbus slave component the adding of devices worked again. Reintroducing the "start_modbus_slave(MB_SLAVE_ADDR);" Function made it again impossible to add devices to a second bus.)
  1. esp_err_t setup()
  2. {
  3.     static const char* TAG = "Setup";
  4.     esp_err_t err = ESP_OK;
  5.      ESP_LOGI(TAG, "Starting Setup of connected peripherals.");
  6.    
  7.     ESP_LOGI(TAG, "Initializing Multiplexer");
  8.     err = mltplx_initialise();
  9.     ESP_ERROR_CHECK(err);
  10.     ESP_LOGI(TAG, "Multiplexer Initialized");
  11.    
  12.     ESP_LOGI(TAG, "Initializing SPI Busses");
  13.     err = spi_initialize_busses();
  14.     ESP_ERROR_CHECK(err);
  15.     ESP_LOGI(TAG, "Initialized SPI Busses");
  16.  
  17.     ESP_LOGI(TAG, "Initializing Modbus Slave");  
  18.     start_modbus_slave(MB_SLAVE_ADDR); //Addr = 1  if i leave this part out, the spi driver throws no errors anymore. I can add devices
  19.     ESP_LOGI(TAG, "Modbus Initialized.");
  20.  
  21.     ESP_LOGI(TAG, "Initializing I2C Master");
  22.     err = i2c_master_initialization();
  23.     ESP_ERROR_CHECK(err);
  24.     ESP_LOGI(TAG, "I2C Initialized.");
  25.  
  26.     ESP_LOGI(TAG, "Testing ESP to PWR Communication");
  27.     err = spi_pwr_initialize();
  28.     ESP_ERROR_CHECK_WITHOUT_ABORT(err);
  29.     err = test_spi(3);
  30.     if(err == ESP_OK){ESP_LOGI(TAG, "Test succesfull");}else{ESP_ERROR_CHECK_WITHOUT_ABORT(err);}
  31.    
  32.     ESP_LOGI(TAG, "Testing ESP to MKE_CTRL Communication");
  33.     err = spi_ctrl_initialize();
  34.     ESP_ERROR_CHECK_WITHOUT_ABORT(err);
  35.     err = test_ctrl_spi(3);
  36.     if(err == ESP_OK){ESP_LOGI(TAG, "Test succesfull");}else{ESP_ERROR_CHECK_WITHOUT_ABORT(err);}
  37. }
  38.  

The code for the modbus function is pretty much taken from the example programm.

The Code for the Function "start_modbus_slave(MB_SLAVE_ADDR);" is:
  1. #include <stdio.h>
  2. #include <stdint.h>
  3. #include "esp_err.h"
  4. #include "mbcontroller.h"       // for mbcontroller defines and api
  5. #include "modbus_virtual_register.h"
  6. #include "esp_log.h"            // for log_write
  7. #include "sdkconfig.h"
  8. #include "modbus_slave.h"
  9.  
  10.  
  11. static const char *SLAVE_TAG = "MODBUS_SLAVE_FUNCTIONALITY";
  12.  
  13. void start_modbus_slave( uint8_t slave_address)
  14. {
  15.     mb_param_info_t reg_info; // keeps the Modbus registers access information
  16.     mb_communication_info_t comm_info; // Modbus communication parameters
  17.     mb_register_area_descriptor_t reg_area; // Modbus register area descriptor structure
  18.  
  19.     // Set UART log level
  20.     esp_log_level_set(SLAVE_TAG, ESP_LOG_INFO);
  21.     void* mbc_slave_handler = NULL;
  22.  
  23.     ESP_ERROR_CHECK(mbc_slave_init(MB_PORT_SERIAL_SLAVE, &mbc_slave_handler)); // 1
  24.  
  25.     // Setup communication parameters and start stack
  26.  
  27.     comm_info.mode = MB_MODE_RTU,
  28.     comm_info.slave_addr = slave_address;
  29.     comm_info.port = MB_SLV_PORT_NUM; //1
  30.     comm_info.baudrate = MB_SLV_DEV_SPEED; //9600
  31.     comm_info.parity = MB_SLV_PARITY; //EVEN
  32.     ESP_ERROR_CHECK(mbc_slave_setup((void*)&comm_info));
  33.  
  34.     // Initialization of Input Registers area
  35.     reg_area.type =         MB_PARAM_INPUT;
  36.     reg_area.start_offset = offsetof(input_registers_t, register1);
  37.     reg_area.address =      (void*)&input_registers.register1;      
  38.     reg_area.size =         sizeof(input_registers_t);                    
  39.     ESP_ERROR_CHECK(mbc_slave_set_descriptor(reg_area));
  40.  
  41.     // Initialization of Holding Registers area
  42.     reg_area.type =         MB_PARAM_HOLDING;
  43.     reg_area.start_offset = offsetof(holding_registers_t, register1);    
  44.     reg_area.address =      (void*)&holding_registers.register1;      
  45.     reg_area.size =         sizeof(holding_registers_t);                    
  46.     ESP_ERROR_CHECK(mbc_slave_set_descriptor(reg_area));
  47.  
  48.     // Initialization of Coil Registers area
  49.     reg_area.type =         MB_PARAM_COIL;
  50.     reg_area.start_offset = offsetof(coil_registers_t, register1);  
  51.     reg_area.address =      (void*)&coil_registers.register1;      
  52.     reg_area.size =         sizeof(coil_registers_t);                    
  53.     ESP_ERROR_CHECK(mbc_slave_set_descriptor(reg_area));
  54.  
  55.     // Initialization of Discrete Registers area
  56.     reg_area.type =         MB_PARAM_DISCRETE;
  57.     reg_area.start_offset = offsetof(discrete_registers_t, register1);  
  58.     reg_area.address =      (void*)&discrete_registers.register1;          
  59.     reg_area.size =         sizeof(discrete_registers_t);                    
  60.     ESP_ERROR_CHECK(mbc_slave_set_descriptor(reg_area));
  61.  
  62.  
  63.     // Starts of modbus controller and stack
  64.     ESP_ERROR_CHECK(mbc_slave_start());
  65.  
  66.     // Set UART pin numbers
  67.     ESP_ERROR_CHECK(uart_set_pin(MB_SLV_PORT_NUM, MB_SLV_TXD,
  68.                             MB_SLV_RXD, MB_SLV_RTS,
  69.                             UART_PIN_NO_CHANGE));
  70.  
  71.     // Set UART driver mode to Half Duplex
  72.     ESP_ERROR_CHECK(uart_set_mode(MB_SLV_PORT_NUM, UART_MODE_RS485_HALF_DUPLEX));
  73.  
  74.     ESP_LOGI(SLAVE_TAG, "Modbus slave stack initialized.");
  75. }
  76.  
  1. #ifndef _MODBUS_VIRTUAL_REGISTER
  2. #define _MODBUS_VIRTUAL_REGISTER
  3.  
  4.  
  5. #pragma pack(push, 1)
  6. typedef struct  
  7. {
  8.     uint16_t register1;                
  9.     uint16_t register2;                
  10.     uint16_t register3;                
  11.     uint16_t register4;                
  12.     uint16_t register5;                
  13. } input_registers_t;
  14. #pragma pack(pop)
  15.  
  16. #pragma pack(push, 1)
  17. typedef struct
  18. {
  19.     uint16_t register1;                
  20.     uint16_t register2;              
  21.     uint16_t register3;                
  22.     uint16_t register4;                
  23.     uint16_t register5;                
  24. } holding_registers_t;          //The Holding registers struct has a lot more registers, but for simplification I only present placeholders.
  25. #pragma pack(pop)
  26.  
  27. #pragma pack(push, 1)
  28. typedef struct
  29. {
  30.     uint16_t register1;                
  31.     uint16_t register2;                
  32.     uint16_t register3;                
  33.     uint16_t register4;                
  34.     uint16_t register5;                
  35. } coil_registers_t;
  36. #pragma pack(pop)
  37.  
  38. #pragma pack(push, 1)
  39. typedef struct
  40. {
  41.     uint16_t register1;              
  42.     uint16_t register2;                
  43.     uint16_t register3;                
  44.     uint16_t register4;                
  45.     uint16_t register5;                
  46. } discrete_registers_t;
  47. #pragma pack(pop)
  48.  
  49. extern input_registers_t input_registers;
  50. extern holding_registers_t holding_registers;
  51. extern coil_registers_t coil_registers;
  52. extern discrete_registers_t discrete_registers;
  53.  
  54. #endif
  1. #include <stdint.h>
  2. #include "modbus_virtual_register.h"
  3.  
  4. input_registers_t input_registers = {0};
  5. holding_registers_t holding_registers = {0};
  6. coil_registers_t coil_registers = {0};
  7. discrete_registers_t discrete_registers = {0};

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

Re: Multiple SPI Busses in parallel does not work

Postby ESP_Sprite » Wed Oct 12, 2022 4:14 am

I'm wondering if you're running out of available interrupts... when initializing the buses, can you change 'ESP_INTR_FLAG_LEVEL1' to `ESP_INTR_FLAG_LOWMED` and try to see if that fixes things?

AG_OHP
Posts: 9
Joined: Mon Oct 10, 2022 7:28 am

Re: Multiple SPI Busses in parallel does not work

Postby AG_OHP » Wed Oct 12, 2022 11:38 am

Thank you a lot,
replacing the interrupt flag did solve the problem. Maybe a error code could be added to the IDF.


Is there a better documentation regarding the use and limits of these Interrupt flags? I could not really conclude the requirements out of the comments in the code.

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

Re: Multiple SPI Busses in parallel does not work

Postby ESP_Sprite » Thu Oct 13, 2022 6:21 am

Yep, I agree there. The interrupt allocator is somewhat old code and follows a style that is somewhat out of date compared to current esp-idf when it comes to reporting errors, and that makes errors like yours quite opaque and hard to detect. I've asked the team to take a look at it and make it a bit more clear.

The flags are generally on an 'use if you know that you need them' level, as they're somewhat deeply interconnected with the internals of the interrupt logic in the CPU (as in: they expose the capabilities of that hardware, even if those capabilities don't make much sense in the ESP-IDF context). For instance, if you hadn't initialized the flags at all (=set them to 0), the interrupt allocator would automatically have done 'the right thing' and returned you an interrupt that would work under C.

AG_OHP
Posts: 9
Joined: Mon Oct 10, 2022 7:28 am

Re: Multiple SPI Busses in parallel does not work

Postby AG_OHP » Fri Oct 14, 2022 8:36 am

thanks for that honest and comprehensive answer.

Thanks again for the help, and i hope you will have a nice rest of the week.

giomate
Posts: 1
Joined: Fri Oct 28, 2022 2:06 pm

Re: Multiple SPI Busses in parallel does not work

Postby giomate » Fri Oct 28, 2022 3:42 pm

ESP_Sprite wrote:
Tue Oct 11, 2022 1:27 am
That is odd, your logic itself is supposed to work. Any chance you can whittle your code down to the minimum that exhibits this behaviour and post that here, so we can check?
Hi ESP_Sprite,
i'm kind of new with the S3. I'm working with a module ESP32S3-WROOM1-M0N4R2. i want to use the properly the SPI3_HOST, as shown in this post. However i did not find an example to check what are the prefered pins for that. In fact in this post had been used pins which are not availble on my module. Therefore , i wondering about the SPI3_HOST availability and use on the ESP32S3 module

i appreciate your comments

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

Re: Multiple SPI Busses in parallel does not work

Postby ESP_Sprite » Mon Oct 31, 2022 1:11 am

Please do not hijack another topic for your question, start a new one if you still have questions after. The ESP32 series allow you to pick any GPIO for SPI signals; some SPI buses on the ESP32 (non-S3) have some prefered pins that can work at higher speeds, but aside from that, any free GPIO will work.

Who is online

Users browsing this forum: Bing [Bot] and 109 guests