- UT_T1_1
- 8Mpsram
+UT_013_01:
+ <<: *unit_test_template
+ tags:
+ - ESP32_IDF
+ - Example_SPI_Multi_device
+
+UT_013_02:
+ <<: *unit_test_template
+ tags:
+ - ESP32_IDF
+ - Example_SPI_Multi_device
+
+UT_013_03:
+ <<: *unit_test_template
+ tags:
+ - ESP32_IDF
+ - Example_SPI_Multi_device
+
+UT_013_04:
+ <<: *unit_test_template
+ tags:
+ - ESP32_IDF
+ - Example_SPI_Multi_device
+ - psram
+
UT_014_01:
<<: *unit_test_template
tags:
--- /dev/null
+#ifndef _TEST_COMMON_SPI_H_
+#define _TEST_COMMON_SPI_H_
+
+#include <esp_types.h>
+#include "driver/spi_master.h"
+#include "freertos/FreeRTOS.h"
+#include "freertos/ringbuf.h"
+#include "freertos/task.h"
+#include "unity.h"
+#include "test_utils.h"
+#include <string.h>
+#include "param_test.h"
+
+// All the tests using the header should use this definition as much as possible,
+// so that the working host can be changed easily in the future.
+#define TEST_SPI_HOST HSPI_HOST
+#define TEST_SLAVE_HOST VSPI_HOST
+
+#define FUNC_SPI 1
+#define FUNC_GPIO 2
+
+//Delay information
+#define ESP_SPI_SLAVE_TV (12.5*3.5)
+#define GPIO_DELAY (12.5*2)
+#define WIRE_DELAY 12.5
+#define TV_INT_CONNECT_GPIO (ESP_SPI_SLAVE_TV+GPIO_DELAY)
+#define TV_INT_CONNECT (ESP_SPI_SLAVE_TV)
+//when connecting to another board, the delay is usually increased by 12.5ns
+#define TV_WITH_ESP_SLAVE_GPIO (TV_INT_CONNECT_GPIO+WIRE_DELAY)
+#define TV_WITH_ESP_SLAVE (TV_INT_CONNECT+WIRE_DELAY)
+
+//currently ESP32 slave only supports up to 20MHz, but 40MHz on the same board
+#define ESP_SPI_SLAVE_MAX_FREQ SPI_MASTER_FREQ_20M
+#define ESP_SPI_SLAVE_MAX_FREQ_SYNC SPI_MASTER_FREQ_40M
+
+#define MAX_TEST_SIZE 16 ///< in this test we run several transactions, this is the maximum trans that can be run
+#define PSET_NAME_LEN 30 ///< length of each param set name
+
+//test low frequency, high frequency until freq limit for worst case (both GPIO)
+#define TEST_FREQ_DEFAULT(){\
+ 1*1000*1000, \
+ SPI_MASTER_FREQ_8M , \
+ SPI_MASTER_FREQ_9M , \
+ SPI_MASTER_FREQ_10M, \
+ SPI_MASTER_FREQ_11M, \
+ SPI_MASTER_FREQ_13M, \
+ SPI_MASTER_FREQ_16M, \
+ SPI_MASTER_FREQ_20M, \
+ SPI_MASTER_FREQ_26M, \
+ SPI_MASTER_FREQ_40M, \
+ SPI_MASTER_FREQ_80M, \
+ 0,\
+ }
+
+#define PIN_NUM_MISO HSPI_IOMUX_PIN_NUM_MISO
+#define PIN_NUM_MOSI HSPI_IOMUX_PIN_NUM_MOSI
+#define PIN_NUM_CLK HSPI_IOMUX_PIN_NUM_CLK
+#define PIN_NUM_CS HSPI_IOMUX_PIN_NUM_CS
+
+//default bus config for tests
+#define SPI_BUS_TEST_DEFAULT_CONFIG() {\
+ .miso_io_num=PIN_NUM_MISO, \
+ .mosi_io_num=PIN_NUM_MOSI,\
+ .sclk_io_num=PIN_NUM_CLK,\
+ .quadwp_io_num=-1,\
+ .quadhd_io_num=-1\
+ }
+
+//default device config for master devices
+#define SPI_DEVICE_TEST_DEFAULT_CONFIG() {\
+ .clock_speed_hz=10*1000*1000,\
+ .mode=0,\
+ .spics_io_num=PIN_NUM_CS,\
+ .queue_size=16,\
+ .pre_cb=NULL, \
+ .cs_ena_pretrans = 0,\
+ .cs_ena_posttrans = 0,\
+ .input_delay_ns = 62.5,\
+ }
+
+//default device config for slave devices
+#define SPI_SLAVE_TEST_DEFAULT_CONFIG() {\
+ .mode=0,\
+ .spics_io_num=PIN_NUM_CS,\
+ .queue_size=3,\
+ .flags=0,\
+ }
+
+typedef enum {
+ FULL_DUPLEX = 0,
+ HALF_DUPLEX_MISO = 1,
+ HALF_DUPLEX_MOSI = 2,
+} spi_dup_t;
+
+/*-------- slave task related stuff -----------*/
+typedef struct {
+ uint32_t len;
+ uint8_t* tx_start;
+ uint8_t data[1];
+} slave_rxdata_t;
+
+typedef struct {
+ uint32_t len;
+ const uint8_t *start;
+} slave_txdata_t;
+
+typedef struct {
+ spi_host_device_t spi;
+ RingbufHandle_t data_received;
+ QueueHandle_t data_to_send;
+} spi_slave_task_context_t;
+
+// test data for master and slave
+extern uint8_t spitest_master_send[];
+extern uint8_t spitest_slave_send[];
+
+//tags for master and slave app
+extern const char MASTER_TAG[];
+extern const char SLAVE_TAG[];
+
+//parameter set definition
+typedef struct {
+ const char pset_name[PSET_NAME_LEN];
+ /*The test work till the frequency below,
+ *set the frequency to higher and remove checks in the driver to know how fast the system can run.
+ */
+ const int *freq_list; // list of tested frequency, terminated by 0
+ int freq_limit; //freq larger (not equal) than this will be ignored
+ spi_dup_t dup;
+ int mode;
+ bool length_aligned;
+ int test_size;
+
+ int master_limit; // the master disable dummy bits and discard readings over this freq
+ bool master_iomux;
+ int master_dma_chan;
+
+ bool slave_iomux;
+ int slave_dma_chan;
+ int slave_tv_ns;
+ bool slave_unaligned_addr;
+} spitest_param_set_t;
+
+//context definition for the parameterized test
+typedef struct {
+ uint8_t master_rxbuf[480];
+ spi_transaction_t master_trans[MAX_TEST_SIZE];
+ TaskHandle_t handle_slave;
+ spi_slave_task_context_t slave_context;
+ slave_txdata_t slave_trans[MAX_TEST_SIZE];
+} spitest_context_t;
+
+// fill default value of spitest_param_set_t
+void spitest_def_param(void* arg);
+
+// functions for slave task
+esp_err_t init_slave_context(spi_slave_task_context_t *context);
+void deinit_slave_context(spi_slave_task_context_t *context);
+void spitest_slave_task(void* arg);
+
+//called by slave, pull-up all pins used by slave
+void slave_pull_up(const spi_bus_config_t* cfg, int spics_io_num);
+
+// to access data of pre-defined transactions.
+void spitest_init_transactions(const spitest_param_set_t *cfg, spitest_context_t* context);
+
+// print data from a transaction
+void spitest_master_print_data(spi_transaction_t *t, int rxlength);
+void spitest_slave_print_data(slave_rxdata_t *t, bool print_rxdata);
+// Check whether master and slave data match
+esp_err_t spitest_check_data(int len, spi_transaction_t *master_t, slave_rxdata_t *slave_t, bool check_master_data, bool check_slave_len, bool check_slave_data);
+
+static inline int get_trans_len(spi_dup_t dup, spi_transaction_t *master_t)
+{
+ if (dup!=HALF_DUPLEX_MISO) {
+ return master_t->length;
+ } else {
+ return master_t->rxlength;
+ }
+}
+//remove device from bus and free the bus
+void master_free_device_bus(spi_device_handle_t spi);
+
+//use this function to fix the output source when assign multiple funcitons to a same pin
+void spitest_gpio_output_sel(uint32_t gpio_num, int func, uint32_t signal_idx);
+
+#endif //_TEST_COMMON_SPI_H_
\ No newline at end of file
--- /dev/null
+#include "test/test_common_spi.h"
+#include "driver/spi_slave.h"
+#include "esp_log.h"
+
+int test_freq_default[]=TEST_FREQ_DEFAULT();
+
+const char MASTER_TAG[] = "test_master";
+const char SLAVE_TAG[] = "test_slave";
+
+DRAM_ATTR uint8_t spitest_master_send[] = {
+ 0x93, 0x34, 0x56, 0x78, 0x9a, 0xbc, 0xde, 0xf0, 0xaa, 0xcc, 0xff, 0xee, 0x55, 0x77, 0x88, 0x43,
+ 0x74,
+ 0x93, 0x34, 0x56, 0x78, 0x9a, 0xbc, 0xde, 0xf0, 0xaa, 0xcc, 0xff, 0xee, 0x55, 0x77, 0x88, 0x43,
+ 0x74,
+ 0x93, 0x34, 0x56, 0x78, 0x9a, 0xbc, 0xde, 0xf0, 0xaa, 0xcc, 0xff, 0xee, 0x55, 0x77, 0x88, 0x43,
+ 0x74,
+ };
+DRAM_ATTR uint8_t spitest_slave_send[] = {
+ 0xaa, 0xdc, 0xba, 0x98, 0x76, 0x54, 0x32, 0x10, 0x13, 0x57, 0x9b, 0xdf, 0x24, 0x68, 0xac, 0xe0,
+ 0xda,
+ 0xaa, 0xdc, 0xba, 0x98, 0x76, 0x54, 0x32, 0x10, 0x13, 0x57, 0x9b, 0xdf, 0x24, 0x68, 0xac, 0xe0,
+ 0xda,
+ 0xaa, 0xdc, 0xba, 0x98, 0x76, 0x54, 0x32, 0x10, 0x13, 0x57, 0x9b, 0xdf, 0x24, 0x68, 0xac, 0xe0,
+ 0xda,
+ };
+
+void spitest_def_param(void* arg)
+{
+ spitest_param_set_t *param_set=(spitest_param_set_t*)arg;
+ param_set->test_size = 8;
+ if (param_set->freq_list==NULL) param_set->freq_list = test_freq_default;
+}
+
+/**********************************************************************************
+ * functions for slave task
+ *********************************************************************************/
+esp_err_t init_slave_context(spi_slave_task_context_t *context)
+{
+ context->data_to_send = xQueueCreate( 16, sizeof( slave_txdata_t ));
+ if ( context->data_to_send == NULL ) {
+ return ESP_ERR_NO_MEM;
+ }
+ context->data_received = xRingbufferCreate( 1024, RINGBUF_TYPE_NOSPLIT );
+ if ( context->data_received == NULL ) {
+ return ESP_ERR_NO_MEM;
+ }
+ context->spi=VSPI_HOST;
+ return ESP_OK;
+}
+
+void deinit_slave_context(spi_slave_task_context_t *context)
+{
+ TEST_ASSERT( context->data_to_send != NULL );
+ vQueueDelete( context->data_to_send );
+ context->data_to_send = NULL;
+ TEST_ASSERT( context->data_received != NULL );
+ vRingbufferDelete( context->data_received );
+ context->data_received = NULL;
+}
+
+/* The task requires a queue and a ringbuf, which should be initialized before task starts.
+ Send ``slave_txdata_t`` to the queue to make the task send data;
+ the task returns data got to the ringbuf, which should have sufficient size.
+*/
+void spitest_slave_task(void* arg)
+{
+ spi_slave_task_context_t* context = (spi_slave_task_context_t*) arg;
+ QueueHandle_t queue = context->data_to_send;
+ RingbufHandle_t ringbuf = context->data_received;
+ uint8_t recvbuf[320+8];
+ slave_txdata_t txdata;
+
+ ESP_LOGI( SLAVE_TAG, "slave up" );
+ //never quit, but blocked by the queue, waiting to be killed, when no more send from main task.
+ while( 1 ) {
+ BaseType_t ret = xQueueReceive( queue, &txdata, portMAX_DELAY );
+ assert(ret);
+
+ spi_slave_transaction_t t = {};
+ t.length = txdata.len;
+ t.tx_buffer = txdata.start;
+ t.rx_buffer = recvbuf+8;
+ //loop until trans_len != 0 to skip glitches
+ do {
+ TEST_ESP_OK( spi_slave_transmit( context->spi, &t, portMAX_DELAY ) );
+ } while ( t.trans_len <= 2 );
+ memcpy(recvbuf, &t.trans_len, sizeof(uint32_t));
+ *(uint8_t**)(recvbuf+4) = (uint8_t*)txdata.start;
+ ESP_LOGI( SLAVE_TAG, "received: %d", t.trans_len );
+ xRingbufferSend( ringbuf, recvbuf, 8+(t.trans_len+7)/8, portMAX_DELAY );
+ }
+}
+
+void slave_pull_up(const spi_bus_config_t* cfg, int spics_io_num)
+{
+ gpio_set_pull_mode(cfg->mosi_io_num, GPIO_PULLUP_ONLY);
+ gpio_set_pull_mode(cfg->sclk_io_num, GPIO_PULLUP_ONLY);
+ gpio_set_pull_mode(spics_io_num, GPIO_PULLUP_ONLY);
+}
+
+/**********************************************************************************
+ * functions for slave task
+ *********************************************************************************/
+
+static int test_len[] = {1, 3, 5, 7, 9, 11, 33, 64};
+
+void spitest_init_transactions(const spitest_param_set_t *cfg, spitest_context_t* context)
+{
+ spi_transaction_t* trans = context->master_trans;
+ uint8_t *rx_buf_ptr = context->master_rxbuf;
+ const spi_dup_t dup = cfg->dup;
+
+ for (int i = 0; i < cfg->test_size; i++) {
+ const void* tx_buffer = spitest_master_send + i%8;
+ int length = 8*test_len[i];
+ if (cfg->length_aligned) length = (length+31)&(~31);
+
+ if (dup == HALF_DUPLEX_MISO) {
+ trans[i] = (spi_transaction_t) {
+ .rx_buffer = rx_buf_ptr,
+ .rxlength = length,
+ };
+ } else if (dup == HALF_DUPLEX_MOSI) {
+ trans[i] = (spi_transaction_t) {
+ .tx_buffer = tx_buffer,
+ .length = length,
+ };
+ } else {
+ trans[i] = (spi_transaction_t) {
+ .tx_buffer = tx_buffer,
+ .length = length,
+ .rx_buffer = rx_buf_ptr,
+ };
+ }
+ rx_buf_ptr = (uint8_t*)( (uint32_t)(rx_buf_ptr + (length+7)/8 + 3) & (~3));
+
+ const void* slave_tx = spitest_slave_send + (cfg->slave_unaligned_addr? i%3: (i%3)*4);
+ //prepare slave tx data
+ context->slave_trans[i] = (slave_txdata_t) {
+ .start = slave_tx,
+ .len = 512,
+ };
+ if (cfg->slave_dma_chan != 0) context->slave_trans[i].len = 1024;
+ }
+}
+
+void spitest_master_print_data(spi_transaction_t *t, int rxlength)
+{
+ if (t->tx_buffer) ESP_LOG_BUFFER_HEX( "master tx", t->tx_buffer, t->length/8 );
+ if (t->rx_buffer) ESP_LOG_BUFFER_HEX( "master rx", t->rx_buffer, rxlength/8 );
+}
+
+void spitest_slave_print_data(slave_rxdata_t *t, bool print_rxdata)
+{
+ int rcv_len = (t->len+7)/8;
+ ESP_LOGI(SLAVE_TAG, "trans_len: %d", t->len);
+ ESP_LOG_BUFFER_HEX("slave tx", t->tx_start, rcv_len);
+ if (print_rxdata) ESP_LOG_BUFFER_HEX("slave rx", t->data, rcv_len);
+}
+
+esp_err_t spitest_check_data(int len, spi_transaction_t *master_t, slave_rxdata_t *slave_t, bool check_master_data, bool check_slave_len, bool check_slave_data)
+{
+ //currently the rcv_len can be in range of [t->length-1, t->length+3]
+ if (check_slave_len) {
+ uint32_t rcv_len = slave_t->len;
+ TEST_ASSERT(rcv_len >= len - 1 && rcv_len <= len + 4);
+ }
+
+ //if (dup!=HALF_DUPLEX_MOSI) {
+ if (check_master_data) {
+ TEST_ASSERT_EQUAL_HEX8_ARRAY(slave_t->tx_start, master_t->rx_buffer, (len + 7) / 8);
+ }
+
+ //if (dup!=HALF_DUPLEX_MISO) {
+ if (check_slave_data) {
+ TEST_ASSERT_EQUAL_HEX8_ARRAY(master_t->tx_buffer, slave_t->data, (len + 7) / 8);
+ }
+ return ESP_OK;
+}
+
+
+void master_free_device_bus(spi_device_handle_t spi)
+{
+ TEST_ESP_OK( spi_bus_remove_device(spi) );
+ TEST_ESP_OK( spi_bus_free(TEST_SPI_HOST) );
+}
+
+void spitest_gpio_output_sel(uint32_t gpio_num, int func, uint32_t signal_idx)
+{
+ PIN_FUNC_SELECT(GPIO_PIN_MUX_REG[gpio_num], func);
+ GPIO.func_out_sel_cfg[gpio_num].func_sel=signal_idx;
+}
\ No newline at end of file
#include "esp_heap_caps.h"
#include "esp_log.h"
#include "soc/spi_periph.h"
-#include "freertos/ringbuf.h"
+#include "test_utils.h"
+#include "test/test_common_spi.h"
#include "soc/gpio_periph.h"
#include "sdkconfig.h"
-#include "test_utils.h"
const static char TAG[] = "test_spi";
-#define SPI_BUS_TEST_DEFAULT_CONFIG() {\
- .miso_io_num=PIN_NUM_MISO, \
- .mosi_io_num=PIN_NUM_MOSI,\
- .sclk_io_num=PIN_NUM_CLK,\
- .quadwp_io_num=-1,\
- .quadhd_io_num=-1\
-}
-
-#define SPI_DEVICE_TEST_DEFAULT_CONFIG() {\
- .clock_speed_hz=10*1000*1000,\
- .mode=0,\
- .spics_io_num=PIN_NUM_CS,\
- .queue_size=16,\
- .pre_cb=NULL, \
- .cs_ena_pretrans = 0,\
- .cs_ena_posttrans = 0,\
- .input_delay_ns = 62.5,\
-}
-
-#define FUNC_SPI 1
-#define FUNC_GPIO 2
-
-void gpio_output_sel(uint32_t gpio_num, int func, uint32_t signal_idx)
-{
- PIN_FUNC_SELECT(GPIO_PIN_MUX_REG[gpio_num], func);
- GPIO.func_out_sel_cfg[gpio_num].func_sel=signal_idx;
-}
-
static void check_spi_pre_n_for(int clk, int pre, int n)
{
esp_err_t ret;
ret=spi_bus_add_device(HSPI_HOST, &devcfg, &handle);
TEST_ASSERT(ret==ESP_OK);
//connect MOSI to two devices breaks the output, fix it.
- gpio_output_sel(26, FUNC_GPIO, HSPID_OUT_IDX);
+ spitest_gpio_output_sel(26, FUNC_GPIO, HSPID_OUT_IDX);
printf("Bus/dev inited.\n");
return handle;
}
return success;
}
-static void destroy_spi_bus(spi_device_handle_t handle) {
- esp_err_t ret;
- ret=spi_bus_remove_device(handle);
- TEST_ASSERT(ret==ESP_OK);
- ret=spi_bus_free(HSPI_HOST);
- TEST_ASSERT(ret==ESP_OK);
-}
-
-
-#define TEST_LEN 111
-
TEST_CASE("SPI Master test", "[spi]")
{
bool success = true;
success &= spi_test(handle, 4096-1); //multiple descs, edge case 2
success &= spi_test(handle, 4096*3); //multiple descs
- destroy_spi_bus(handle);
+ master_free_device_bus(handle);
printf("Testing bus at 80KHz, non-DMA\n");
handle=setup_spi_bus(80000, false);
success &= spi_test(handle, 63); //small
success &= spi_test(handle, 64); //small, unaligned
- destroy_spi_bus(handle);
-
+ master_free_device_bus(handle);
printf("Testing bus at 26MHz\n");
handle=setup_spi_bus(20000000, true);
success &= spi_test(handle, 128); //DMA, aligned
success &= spi_test(handle, 4096*3); //DMA, multiple descs
- destroy_spi_bus(handle);
+ master_free_device_bus(handle);
printf("Testing bus at 900KHz\n");
handle=setup_spi_bus(9000000, true);
success &= spi_test(handle, 128); //DMA, aligned
success &= spi_test(handle, 4096*3); //DMA, multiple descs
- destroy_spi_bus(handle);
+ master_free_device_bus(handle);
TEST_ASSERT(success);
}
ret=spi_bus_remove_device(handle2);
TEST_ASSERT(ret==ESP_OK);
- destroy_spi_bus(handle1);
+ master_free_device_bus(handle1);
TEST_ASSERT(success);
}
0x70, 0x22, 0x7D, 0x0A, 0x6D, 0xD3, 0x77, 0x73, 0xD0, 0xF4, 0x06, 0xB2, 0x19, 0x8C, 0xFF, 0x58, 0xE4, 0xDB, 0xE9, 0xEC, 0x89, 0x6A, 0xF4, 0x0E, 0x67, 0x12, 0xEC, 0x11, 0xD2, 0x1F, 0x8D, 0xD7,
};
-#if 1 //HSPI
-#define PIN_NUM_MISO HSPI_IOMUX_PIN_NUM_MISO
-#define PIN_NUM_MOSI HSPI_IOMUX_PIN_NUM_MOSI
-#define PIN_NUM_CLK HSPI_IOMUX_PIN_NUM_CLK
-#define PIN_NUM_CS HSPI_IOMUX_PIN_NUM_CS
-#elif 1 //VSPI
-#define PIN_NUM_MISO VSPI_IOMUX_PIN_NUM_MISO
-#define PIN_NUM_MOSI VSPI_IOMUX_PIN_NUM_MOSI
-#define PIN_NUM_CLK VSPI_IOMUX_PIN_NUM_CLK
-#define PIN_NUM_CS VSPI_IOMUX_PIN_NUM_CS
-#endif
-
-#define PIN_NUM_DC 21
-#define PIN_NUM_RST 18
-#define PIN_NUM_BCKL 5
-
TEST_CASE("SPI Master DMA test, TX and RX in different regions", "[spi]")
{
#ifdef CONFIG_SPIRAM_SUPPORT
ret=spi_bus_add_device(HSPI_HOST, &devcfg, &spi);
TEST_ASSERT(ret==ESP_OK);
//connect MOSI to two devices breaks the output, fix it.
- gpio_output_sel(buscfg.mosi_io_num, FUNC_GPIO, HSPID_OUT_IDX);
+ spitest_gpio_output_sel(buscfg.mosi_io_num, FUNC_GPIO, HSPID_OUT_IDX);
#define TEST_REGION_SIZE 5
static spi_transaction_t trans[TEST_REGION_SIZE];
TEST_ASSERT(ret==ESP_OK);
//connect MOSI to two devices breaks the output, fix it.
- gpio_output_sel(buscfg.mosi_io_num, FUNC_GPIO, HSPID_OUT_IDX);
+ spitest_gpio_output_sel(buscfg.mosi_io_num, FUNC_GPIO, HSPID_OUT_IDX);
memset(rx_buf, 0x66, 320);
TEST_ASSERT(spi_bus_free(HSPI_HOST) == ESP_OK);
}
-static const char MASTER_TAG[] = "test_master";
-static const char SLAVE_TAG[] = "test_slave";
-DRAM_ATTR static uint8_t master_send[] = {
- 0x93, 0x34, 0x56, 0x78, 0x9a, 0xbc, 0xde, 0xf0, 0xaa, 0xcc, 0xff, 0xee, 0x55, 0x77, 0x88, 0x43,
- 0x74,
- 0x93, 0x34, 0x56, 0x78, 0x9a, 0xbc, 0xde, 0xf0, 0xaa, 0xcc, 0xff, 0xee, 0x55, 0x77, 0x88, 0x43,
- 0x74,
- 0x93, 0x34, 0x56, 0x78, 0x9a, 0xbc, 0xde, 0xf0, 0xaa, 0xcc, 0xff, 0xee, 0x55, 0x77, 0x88, 0x43,
- 0x74,
- };
-DRAM_ATTR static uint8_t slave_send[] = {
- 0xaa, 0xdc, 0xba, 0x98, 0x76, 0x54, 0x32, 0x10, 0x13, 0x57, 0x9b, 0xdf, 0x24, 0x68, 0xac, 0xe0,
- 0xda,
- 0xaa, 0xdc, 0xba, 0x98, 0x76, 0x54, 0x32, 0x10, 0x13, 0x57, 0x9b, 0xdf, 0x24, 0x68, 0xac, 0xe0,
- 0xda,
- 0xaa, 0xdc, 0xba, 0x98, 0x76, 0x54, 0x32, 0x10, 0x13, 0x57, 0x9b, 0xdf, 0x24, 0x68, 0xac, 0xe0,
- 0xda,
- };
-
-
-static void master_deinit(spi_device_handle_t spi)
-{
- TEST_ESP_OK( spi_bus_remove_device(spi) );
- TEST_ESP_OK( spi_bus_free(HSPI_HOST) );
-}
-
-#define SPI_SLAVE_TEST_DEFAULT_CONFIG() {\
- .mode=0,\
- .spics_io_num=PIN_NUM_CS,\
- .queue_size=3,\
- .flags=0,\
-}
-
-static void slave_pull_up(const spi_bus_config_t* cfg, int spics_io_num)
-{
- gpio_set_pull_mode(cfg->mosi_io_num, GPIO_PULLUP_ENABLE);
- gpio_set_pull_mode(cfg->sclk_io_num, GPIO_PULLUP_ENABLE);
- gpio_set_pull_mode(spics_io_num, GPIO_PULLUP_ENABLE);
-}
-
-typedef struct {
- uint32_t len;
- uint8_t *start;
-} slave_txdata_t;
-
-typedef struct {
- uint32_t len;
- uint8_t* tx_start;
- uint8_t data[1];
-} slave_rxdata_t;
-
-typedef struct {
- spi_host_device_t spi;
- RingbufHandle_t data_received;
- QueueHandle_t data_to_send;
-} spi_slave_task_context_t;
-
-esp_err_t init_slave_context(spi_slave_task_context_t *context)
-{
- context->data_to_send = xQueueCreate( 16, sizeof( slave_txdata_t ));
- if ( context->data_to_send == NULL ) {
- return ESP_ERR_NO_MEM;
- }
- context->data_received = xRingbufferCreate( 1024, RINGBUF_TYPE_NOSPLIT );
- if ( context->data_received == NULL ) {
- return ESP_ERR_NO_MEM;
- }
- context->spi=VSPI_HOST;
- return ESP_OK;
-}
-
-void deinit_slave_context(spi_slave_task_context_t *context)
-{
- TEST_ASSERT( context->data_to_send != NULL );
- vQueueDelete( context->data_to_send );
- context->data_to_send = NULL;
- TEST_ASSERT( context->data_received != NULL );
- vRingbufferDelete( context->data_received );
- context->data_received = NULL;
-}
-
-/* The task requires a queue and a ringbuf, which should be initialized before task starts.
- Send ``slave_txdata_t`` to the queue to make the task send data;
- the task returns data got to the ringbuf, which should have sufficient size.
-*/
-static void task_slave(void* arg)
-{
- spi_slave_task_context_t* context = (spi_slave_task_context_t*) arg;
- QueueHandle_t queue = context->data_to_send;
- RingbufHandle_t ringbuf = context->data_received;
- uint8_t recvbuf[320+8];
- slave_txdata_t txdata;
-
- ESP_LOGI( SLAVE_TAG, "slave up" );
- //never quit, but blocked by the queue, waiting to be killed, when no more send from main task.
- while( 1 ) {
- xQueueReceive( queue, &txdata, portMAX_DELAY );
-
- ESP_LOGI( "test", "to send: %p", txdata.start );
- spi_slave_transaction_t t = {};
- t.length = txdata.len;
- t.tx_buffer = txdata.start;
- t.rx_buffer = recvbuf+8;
- //loop until trans_len != 0 to skip glitches
- memset(recvbuf, 0x66, sizeof(recvbuf));
- do {
- TEST_ESP_OK( spi_slave_transmit( context->spi, &t, portMAX_DELAY ) );
- } while ( t.trans_len == 0 );
- memcpy(recvbuf, &t.trans_len, sizeof(uint32_t));
- *(uint8_t**)(recvbuf+4) = txdata.start;
- ESP_LOGI( SLAVE_TAG, "received: %d", t.trans_len );
- xRingbufferSend( ringbuf, recvbuf, 8+(t.trans_len+7)/8, portMAX_DELAY );
- }
-}
-
-#define TEST_SPI_HOST HSPI_HOST
-#define TEST_SLAVE_HOST VSPI_HOST
-
static uint8_t bitswap(uint8_t in)
{
uint8_t out = 0;
TEST_ESP_OK(spi_bus_add_device(TEST_SPI_HOST, &devcfg, &spi));
//connecting pins to two peripherals breaks the output, fix it.
- gpio_output_sel(buscfg.mosi_io_num, FUNC_GPIO, spi_periph_signal[TEST_SPI_HOST].spid_out);
- gpio_output_sel(buscfg.miso_io_num, FUNC_GPIO, spi_periph_signal[TEST_SLAVE_HOST].spiq_out);
- gpio_output_sel(devcfg.spics_io_num, FUNC_GPIO, spi_periph_signal[TEST_SPI_HOST].spics_out[0]);
- gpio_output_sel(buscfg.sclk_io_num, FUNC_GPIO, spi_periph_signal[TEST_SPI_HOST].spiclk_out);
+ spitest_gpio_output_sel(buscfg.mosi_io_num, FUNC_GPIO, spi_periph_signal[TEST_SPI_HOST].spid_out);
+ spitest_gpio_output_sel(buscfg.miso_io_num, FUNC_GPIO, spi_periph_signal[TEST_SLAVE_HOST].spiq_out);
+ spitest_gpio_output_sel(devcfg.spics_io_num, FUNC_GPIO, spi_periph_signal[TEST_SPI_HOST].spics_out[0]);
+ spitest_gpio_output_sel(buscfg.sclk_io_num, FUNC_GPIO, spi_periph_signal[TEST_SPI_HOST].spiclk_out);
for (int i= 0; i < 8; i++) {
//prepare slave tx data
slave_txdata_t slave_txdata = (slave_txdata_t) {
- .start = slave_send,
+ .start = spitest_slave_send + 4*(i%3),
.len = 256,
};
xQueueSend(slave_context->data_to_send, &slave_txdata, portMAX_DELAY);
esp_err_t err = init_slave_context( &slave_context );
TEST_ASSERT( err == ESP_OK );
TaskHandle_t handle_slave;
- xTaskCreate( task_slave, "spi_slave", 4096, &slave_context, 0, &handle_slave);
+ xTaskCreate( spitest_slave_task, "spi_slave", 4096, &slave_context, 0, &handle_slave);
//initial slave, mode 0, no dma
int dma_chan = 0;
ESP_LOGI(MASTER_TAG, "test passed.");
}
-/********************************************************************************
- * Test Timing By Internal Connections
- ********************************************************************************/
-typedef enum {
- FULL_DUPLEX = 0,
- HALF_DUPLEX_MISO = 1,
- HALF_DUPLEX_MOSI = 2,
-} spi_dup_t;
-
-static int timing_speed_array[]={/**/
- SPI_MASTER_FREQ_8M ,
- SPI_MASTER_FREQ_9M ,
- SPI_MASTER_FREQ_10M,
- SPI_MASTER_FREQ_11M,
- SPI_MASTER_FREQ_13M,
- SPI_MASTER_FREQ_16M,
- SPI_MASTER_FREQ_20M,
- SPI_MASTER_FREQ_26M,
- SPI_MASTER_FREQ_40M,
- SPI_MASTER_FREQ_80M,
-};
-
-typedef struct {
- uint8_t master_rxbuf[320];
- spi_transaction_t master_trans[16];
- TaskHandle_t handle_slave;
- spi_slave_task_context_t slave_context;
- slave_txdata_t slave_trans[16];
-} timing_context_t;
-
-void master_print_data(spi_transaction_t *t, spi_dup_t dup)
-{
- if (t->tx_buffer) {
- ESP_LOG_BUFFER_HEX( "master tx", t->tx_buffer, t->length/8 );
- } else {
- ESP_LOGI( "master tx", "no data" );
- }
-
- int rxlength;
- if (dup!=HALF_DUPLEX_MISO) {
- rxlength = t->length/8;
- } else {
- rxlength = t->rxlength/8;
- }
- if (t->rx_buffer) {
- ESP_LOG_BUFFER_HEX( "master rx", t->rx_buffer, rxlength );
- } else {
- ESP_LOGI( "master rx", "no data" );
- }
-}
-
-void slave_print_data(slave_rxdata_t *t)
-{
- int rcv_len = (t->len+7)/8;
- ESP_LOGI(SLAVE_TAG, "trans_len: %d", t->len);
- ESP_LOG_BUFFER_HEX( "slave tx", t->tx_start, rcv_len);
- ESP_LOG_BUFFER_HEX( "slave rx", t->data, rcv_len);
-}
-
-esp_err_t check_data(spi_transaction_t *t, spi_dup_t dup, slave_rxdata_t *slave_t)
-{
- int length;
- if (dup!=HALF_DUPLEX_MISO) {
- length = t->length;
- } else {
- length = t->rxlength;
- }
- TEST_ASSERT(length!=0);
-
- //currently the rcv_len can be in range of [t->length-1, t->length+3]
- uint32_t rcv_len = slave_t->len;
- TEST_ASSERT(rcv_len >= length-1 && rcv_len <= length+3);
-
- //the timing speed is temporarily only for master
- if (dup!=HALF_DUPLEX_MISO) {
-// TEST_ASSERT_EQUAL_HEX8_ARRAY(t->tx_buffer, slave_t->data, (t->length+7)/8);
- }
- if (dup!=HALF_DUPLEX_MOSI) {
- TEST_ASSERT_EQUAL_HEX8_ARRAY(slave_t->tx_start, t->rx_buffer, (length+7)/8);
- }
- return ESP_OK;
-}
-
-int test_len[] = {1, 3, 5, 7, 9, 11, 33, 64};
-
-static void timing_init_transactions(spi_dup_t dup, timing_context_t* context)
-{
- spi_transaction_t* trans = context->master_trans;
- uint8_t *rx_buf_ptr = context->master_rxbuf;
- if (dup==HALF_DUPLEX_MISO) {
- for (int i = 0; i < 8; i++ ) {
- trans[i] = (spi_transaction_t) {
- .flags = 0,
- .rxlength = 8*test_len[i],
- .rx_buffer = rx_buf_ptr,
- };
- rx_buf_ptr += ((context->master_trans[i].rxlength + 31)/8)&(~3);
- }
- } else if (dup==HALF_DUPLEX_MOSI) {
- for (int i = 0; i < 8; i++ ) {
- trans[i] = (spi_transaction_t) {
- .flags = 0,
- .length = 8*test_len[i],
- .tx_buffer = master_send+i,
- };
- }
- } else {
- for (int i = 0; i < 8; i++ ) {
- trans[i] = (spi_transaction_t) {
- .flags = 0,
- .length = 8*test_len[i],
- .tx_buffer = master_send+i,
- .rx_buffer = rx_buf_ptr,
- };
- rx_buf_ptr += ((context->master_trans[i].length + 31)/8)&(~3);
- }
- }
- //prepare slave tx data
- for (int i = 0; i < 8; i ++) {
- context->slave_trans[i] = (slave_txdata_t) {
- .start = slave_send + 4*(i%3),
- .len = 512,
- };
- }
-}
-
-typedef struct {
- const char cfg_name[30];
- /*The test work till the frequency below,
- *set the frequency to higher and remove checks in the driver to know how fast the system can run.
- */
- int freq_limit;
- spi_dup_t dup;
- bool master_iomux;
- bool slave_iomux;
- int slave_tv_ns;
-} test_timing_config_t;
-
-#define ESP_SPI_SLAVE_TV (12.5*3)
-#define GPIO_DELAY (12.5*2)
-#define SAMPLE_DELAY 12.5
-
-#define TV_INT_CONNECT_GPIO (ESP_SPI_SLAVE_TV+GPIO_DELAY)
-#define TV_INT_CONNECT (ESP_SPI_SLAVE_TV)
-#define TV_WITH_ESP_SLAVE_GPIO (ESP_SPI_SLAVE_TV+SAMPLE_DELAY+GPIO_DELAY)
-#define TV_WITH_ESP_SLAVE (ESP_SPI_SLAVE_TV+SAMPLE_DELAY)
-
-//currently ESP32 slave only supports up to 20MHz, but 40MHz on the same board
-#define ESP_SPI_SLAVE_MAX_FREQ SPI_MASTER_FREQ_20M
-#define ESP_SPI_SLAVE_MAX_FREQ_SYNC SPI_MASTER_FREQ_40M
-
-
-static test_timing_config_t timing_master_conf_t[] = {
- { .cfg_name = "FULL_DUP, MASTER IOMUX",
- .freq_limit = SPI_MASTER_FREQ_13M,
- .dup = FULL_DUPLEX,
- .master_iomux = true,
- .slave_iomux = false,
- .slave_tv_ns = TV_INT_CONNECT_GPIO,
- },
- { .cfg_name = "FULL_DUP, SLAVE IOMUX",
- .freq_limit = SPI_MASTER_FREQ_13M,
- .dup = FULL_DUPLEX,
- .master_iomux = false,
- .slave_iomux = true,
- .slave_tv_ns = TV_INT_CONNECT,
- },
- { .cfg_name = "FULL_DUP, BOTH GPIO",
- .freq_limit = SPI_MASTER_FREQ_10M,
- .dup = FULL_DUPLEX,
- .master_iomux = false,
- .slave_iomux = false,
- .slave_tv_ns = TV_INT_CONNECT_GPIO,
- },
- { .cfg_name = "HALF_DUP, MASTER IOMUX",
- .freq_limit = ESP_SPI_SLAVE_MAX_FREQ_SYNC,
- .dup = HALF_DUPLEX_MISO,
- .master_iomux = true,
- .slave_iomux = false,
- .slave_tv_ns = TV_INT_CONNECT_GPIO,
- },
- { .cfg_name = "HALF_DUP, SLAVE IOMUX",
- .freq_limit = ESP_SPI_SLAVE_MAX_FREQ_SYNC,
- .dup = HALF_DUPLEX_MISO,
- .master_iomux = false,
- .slave_iomux = true,
- .slave_tv_ns = TV_INT_CONNECT,
- },
- { .cfg_name = "HALF_DUP, BOTH GPIO",
- .freq_limit = ESP_SPI_SLAVE_MAX_FREQ_SYNC,
- .dup = HALF_DUPLEX_MISO,
- .master_iomux = false,
- .slave_iomux = false,
- .slave_tv_ns = TV_INT_CONNECT_GPIO,
- },
- { .cfg_name = "MOSI_DUP, MASTER IOMUX",
- .freq_limit = ESP_SPI_SLAVE_MAX_FREQ_SYNC,
- .dup = HALF_DUPLEX_MOSI,
- .master_iomux = true,
- .slave_iomux = false,
- .slave_tv_ns = TV_INT_CONNECT_GPIO,
- },
- { .cfg_name = "MOSI_DUP, SLAVE IOMUX",
- .freq_limit = ESP_SPI_SLAVE_MAX_FREQ_SYNC,
- .dup = HALF_DUPLEX_MOSI,
- .master_iomux = false,
- .slave_iomux = true,
- .slave_tv_ns = TV_INT_CONNECT,
- },
- { .cfg_name = "MOSI_DUP, BOTH GPIO",
- .freq_limit = ESP_SPI_SLAVE_MAX_FREQ_SYNC,
- .dup = HALF_DUPLEX_MOSI,
- .master_iomux = false,
- .slave_iomux = false,
- .slave_tv_ns = TV_INT_CONNECT_GPIO,
- },
-};
-
-//this case currently only checks master read
-TEST_CASE("test timing_master","[spi][timeout=120]")
-{
- timing_context_t context;
-
- //Enable pull-ups on SPI lines so we don't detect rogue pulses when no master is connected.
- //slave_pull_up(&slv_buscfg, slvcfg.spics_io_num);
-
- context.slave_context = (spi_slave_task_context_t){};
- esp_err_t err = init_slave_context( &context.slave_context );
- TEST_ASSERT( err == ESP_OK );
-
- xTaskCreate( task_slave, "spi_slave", 4096, &context.slave_context, 0, &context.handle_slave);
-
- const int test_size = sizeof(timing_master_conf_t)/sizeof(test_timing_config_t);
- for (int i = 0; i < test_size; i++) {
- test_timing_config_t* conf = &timing_master_conf_t[i];
-
- spi_device_handle_t spi;
-
- timing_init_transactions(conf->dup, &context);
-
- ESP_LOGI(MASTER_TAG, "****************** %s ***************", conf->cfg_name);
- for (int j=0; j<sizeof(timing_speed_array)/sizeof(int); j++ ) {
- if (timing_speed_array[j] > conf->freq_limit) break;
- ESP_LOGI(MASTER_TAG, "======> %dk", timing_speed_array[j]/1000);
-
- //master config
- const int master_mode = 0;
- spi_bus_config_t buscfg=SPI_BUS_TEST_DEFAULT_CONFIG();
- spi_device_interface_config_t devcfg=SPI_DEVICE_TEST_DEFAULT_CONFIG();
- devcfg.mode = master_mode;
- if (conf->dup==HALF_DUPLEX_MISO||conf->dup==HALF_DUPLEX_MOSI) {
- devcfg.cs_ena_pretrans = 20;
- devcfg.flags |= SPI_DEVICE_HALFDUPLEX;
- } else {
- devcfg.cs_ena_pretrans = 1;
- }
- devcfg.cs_ena_posttrans = 20;
- devcfg.input_delay_ns = conf->slave_tv_ns;
- devcfg.clock_speed_hz = timing_speed_array[j];
-
- //slave config
- int slave_mode = 0;
- spi_slave_interface_config_t slvcfg=SPI_SLAVE_TEST_DEFAULT_CONFIG();
- slvcfg.mode = slave_mode;
-
- //pin config & initialize
- //we can't have two sets of iomux pins on the same pins
- assert(!conf->master_iomux || !conf->slave_iomux);
- if (conf->slave_iomux) {
- //only in this case, use VSPI iomux pins
- buscfg.miso_io_num = VSPI_IOMUX_PIN_NUM_MISO;
- buscfg.mosi_io_num = VSPI_IOMUX_PIN_NUM_MOSI;
- buscfg.sclk_io_num = VSPI_IOMUX_PIN_NUM_CLK;
- devcfg.spics_io_num = VSPI_IOMUX_PIN_NUM_CS;
- slvcfg.spics_io_num = VSPI_IOMUX_PIN_NUM_CS;
- } else {
- buscfg.miso_io_num = HSPI_IOMUX_PIN_NUM_MISO;
- buscfg.mosi_io_num = HSPI_IOMUX_PIN_NUM_MOSI;
- buscfg.sclk_io_num = HSPI_IOMUX_PIN_NUM_CLK;
- devcfg.spics_io_num = HSPI_IOMUX_PIN_NUM_CS;
- slvcfg.spics_io_num = HSPI_IOMUX_PIN_NUM_CS;
- }
- slave_pull_up(&buscfg, slvcfg.spics_io_num);
-
- //this does nothing, but avoid the driver from using iomux pins if required
- buscfg.quadhd_io_num = (!conf->master_iomux && !conf->slave_iomux? VSPI_IOMUX_PIN_NUM_MISO: -1);
- TEST_ESP_OK(spi_bus_initialize(HSPI_HOST, &buscfg, 0));
- TEST_ESP_OK(spi_bus_add_device(HSPI_HOST, &devcfg, &spi));
- //slave automatically use iomux pins if pins are on VSPI_* pins
- buscfg.quadhd_io_num = -1;
- TEST_ESP_OK( spi_slave_initialize(VSPI_HOST, &buscfg, &slvcfg, 0) );
-
- //initialize master and slave on the same pins break some of the output configs, fix them
- if (conf->master_iomux) {
- gpio_output_sel(buscfg.mosi_io_num, FUNC_SPI, HSPID_OUT_IDX);
- gpio_output_sel(buscfg.miso_io_num, FUNC_GPIO, VSPIQ_OUT_IDX);
- gpio_output_sel(devcfg.spics_io_num, FUNC_SPI, HSPICS0_OUT_IDX);
- gpio_output_sel(buscfg.sclk_io_num, FUNC_SPI, HSPICLK_OUT_IDX);
- } else if (conf->slave_iomux) {
- gpio_output_sel(buscfg.mosi_io_num, FUNC_GPIO, HSPID_OUT_IDX);
- gpio_output_sel(buscfg.miso_io_num, FUNC_SPI, VSPIQ_OUT_IDX);
- gpio_output_sel(devcfg.spics_io_num, FUNC_GPIO, HSPICS0_OUT_IDX);
- gpio_output_sel(buscfg.sclk_io_num, FUNC_GPIO, HSPICLK_OUT_IDX);
- } else {
- gpio_output_sel(buscfg.mosi_io_num, FUNC_GPIO, HSPID_OUT_IDX);
- gpio_output_sel(buscfg.miso_io_num, FUNC_GPIO, VSPIQ_OUT_IDX);
- gpio_output_sel(devcfg.spics_io_num, FUNC_GPIO, HSPICS0_OUT_IDX);
- gpio_output_sel(buscfg.sclk_io_num, FUNC_GPIO, HSPICLK_OUT_IDX);
- }
-
- //clear master receive buffer
- memset(context.master_rxbuf, 0x66, sizeof(context.master_rxbuf));
-
- //prepare slave tx data
- for (int k = 0; k < 8; k ++) xQueueSend( context.slave_context.data_to_send, &context.slave_trans[k], portMAX_DELAY );
-
- for( int k= 0; k < 8; k ++ ) {
- //wait for both master and slave end
- ESP_LOGI( MASTER_TAG, "=> test%d", k );
- //send master tx data
- vTaskDelay(9);
-
- spi_transaction_t *t = &context.master_trans[k];
- TEST_ESP_OK (spi_device_transmit( spi, t) );
- master_print_data(t, conf->dup);
-
- size_t rcv_len;
- slave_rxdata_t *rcv_data = xRingbufferReceive( context.slave_context.data_received, &rcv_len, portMAX_DELAY );
- slave_print_data(rcv_data);
-
- //check result
- TEST_ESP_OK(check_data(t, conf->dup, rcv_data));
- //clean
- vRingbufferReturnItem(context.slave_context.data_received, rcv_data);
- }
- master_deinit(spi);
- TEST_ASSERT(spi_slave_free(VSPI_HOST) == ESP_OK);
- }
- }
-
- vTaskDelete( context.handle_slave );
- context.handle_slave = 0;
-
- deinit_slave_context(&context.slave_context);
-
- ESP_LOGI(MASTER_TAG, "test passed.");
-}
-
/********************************************************************************
* Test SPI transaction interval
********************************************************************************/
TEST_ASSERT(ret==ESP_OK);
}
-static void speed_deinit(spi_device_handle_t spi)
-{
- TEST_ESP_OK( spi_bus_remove_device(spi) );
- TEST_ESP_OK( spi_bus_free(HSPI_HOST) );
-}
-
static void sorted_array_insert(uint32_t* array, int* size, uint32_t item)
{
int pos;
//release the bus
spi_device_release_bus(spi);
- speed_deinit(spi);
+ master_free_device_bus(spi);
+
speed_setup(&spi, !use_dma);
//record flight time by isr, without DMA
//release the bus
spi_device_release_bus(spi);
- speed_deinit(spi);
+ master_free_device_bus(spi);
}
typedef struct {
--- /dev/null
+#include "test/test_common_spi.h"
+#include "driver/spi_master.h"
+#include "driver/spi_slave.h"
+#include "esp_log.h"
+#include "soc/spi_periph.h"
+#include "test/test_common_spi.h"
+
+/********************************************************************************
+ * Test By Internal Connections
+ ********************************************************************************/
+static void local_test_init(void** context);
+static void local_test_deinit(void* context);
+static void local_test_loop(const void *test_param, void* context);
+
+static const ptest_func_t local_test_func = {
+ .pre_test = local_test_init,
+ .post_test = local_test_deinit,
+ .loop = local_test_loop,
+ .def_param = spitest_def_param,
+};
+
+#define TEST_SPI_LOCAL(name, param_set) \
+ PARAM_GROUP_DECLARE(name, param_set) \
+ TEST_LOCAL(name, param_set, "[spi][timeout=120]", &local_test_func)
+
+static void local_test_init(void** arg)
+{
+ TEST_ASSERT(*arg==NULL);
+ *arg = malloc(sizeof(spitest_context_t));
+ spitest_context_t* context = (spitest_context_t*)*arg;
+ TEST_ASSERT(context!=NULL);
+ context->slave_context = (spi_slave_task_context_t){};
+ esp_err_t err = init_slave_context( &context->slave_context);
+ TEST_ASSERT(err == ESP_OK);
+
+ xTaskCreate(spitest_slave_task, "spi_slave", 4096, &context->slave_context, 0, &context->handle_slave);
+}
+
+static void local_test_deinit(void* arg)
+{
+ spitest_context_t* context = arg;
+ vTaskDelete(context->handle_slave);
+ context->handle_slave = 0;
+ deinit_slave_context(&context->slave_context);
+}
+
+static void local_test_start(spi_device_handle_t *spi, int freq, const spitest_param_set_t* pset, spitest_context_t* context)
+{
+ //master config
+ spi_bus_config_t buscfg = SPI_BUS_TEST_DEFAULT_CONFIG();
+ spi_device_interface_config_t devcfg = SPI_DEVICE_TEST_DEFAULT_CONFIG();
+ spi_slave_interface_config_t slvcfg = SPI_SLAVE_TEST_DEFAULT_CONFIG();
+ //pin config & initialize
+ //we can't have two sets of iomux pins on the same pins
+ assert(!pset->master_iomux || !pset->slave_iomux);
+ if (pset->slave_iomux) {
+ //only in this case, use VSPI iomux pins
+ buscfg.miso_io_num = VSPI_IOMUX_PIN_NUM_MISO;
+ buscfg.mosi_io_num = VSPI_IOMUX_PIN_NUM_MOSI;
+ buscfg.sclk_io_num = VSPI_IOMUX_PIN_NUM_CLK;
+ devcfg.spics_io_num = VSPI_IOMUX_PIN_NUM_CS;
+ slvcfg.spics_io_num = VSPI_IOMUX_PIN_NUM_CS;
+ } else {
+ buscfg.miso_io_num = HSPI_IOMUX_PIN_NUM_MISO;
+ buscfg.mosi_io_num = HSPI_IOMUX_PIN_NUM_MOSI;
+ buscfg.sclk_io_num = HSPI_IOMUX_PIN_NUM_CLK;
+ devcfg.spics_io_num = HSPI_IOMUX_PIN_NUM_CS;
+ slvcfg.spics_io_num = HSPI_IOMUX_PIN_NUM_CS;
+ }
+ //this does nothing, but avoid the driver from using iomux pins if required
+ buscfg.quadhd_io_num = (!pset->master_iomux && !pset->slave_iomux ? VSPI_IOMUX_PIN_NUM_MISO : -1);
+ devcfg.mode = pset->mode;
+ const int cs_pretrans_max = 15;
+ if (pset->dup == HALF_DUPLEX_MISO) {
+ devcfg.cs_ena_pretrans = cs_pretrans_max;
+ devcfg.flags |= SPI_DEVICE_HALFDUPLEX;
+ } else if (pset->dup == HALF_DUPLEX_MOSI) {
+ devcfg.cs_ena_pretrans = cs_pretrans_max;
+ devcfg.flags |= SPI_DEVICE_NO_DUMMY;
+ } else {
+ devcfg.cs_ena_pretrans = cs_pretrans_max;
+ }
+ const int cs_posttrans_max = 15;
+ devcfg.cs_ena_posttrans = cs_posttrans_max;
+ devcfg.input_delay_ns = pset->slave_tv_ns;
+ devcfg.clock_speed_hz = freq;
+ if (pset->master_limit != 0 && freq > pset->master_limit) devcfg.flags |= SPI_DEVICE_NO_DUMMY;
+
+ //slave config
+ slvcfg.mode = pset->mode;
+
+ slave_pull_up(&buscfg, slvcfg.spics_io_num);
+
+ TEST_ESP_OK(spi_bus_initialize(HSPI_HOST, &buscfg, pset->master_dma_chan));
+ TEST_ESP_OK(spi_bus_add_device(HSPI_HOST, &devcfg, spi));
+
+ //slave automatically use iomux pins if pins are on VSPI_* pins
+ buscfg.quadhd_io_num = -1;
+ TEST_ESP_OK(spi_slave_initialize(VSPI_HOST, &buscfg, &slvcfg, pset->slave_dma_chan));
+
+ //initialize master and slave on the same pins break some of the output configs, fix them
+ if (pset->master_iomux) {
+ spitest_gpio_output_sel(buscfg.mosi_io_num, FUNC_SPI, HSPID_OUT_IDX);
+ spitest_gpio_output_sel(buscfg.miso_io_num, FUNC_GPIO, VSPIQ_OUT_IDX);
+ spitest_gpio_output_sel(devcfg.spics_io_num, FUNC_SPI, HSPICS0_OUT_IDX);
+ spitest_gpio_output_sel(buscfg.sclk_io_num, FUNC_SPI, HSPICLK_OUT_IDX);
+ } else if (pset->slave_iomux) {
+ spitest_gpio_output_sel(buscfg.mosi_io_num, FUNC_GPIO, HSPID_OUT_IDX);
+ spitest_gpio_output_sel(buscfg.miso_io_num, FUNC_SPI, VSPIQ_OUT_IDX);
+ spitest_gpio_output_sel(devcfg.spics_io_num, FUNC_GPIO, HSPICS0_OUT_IDX);
+ spitest_gpio_output_sel(buscfg.sclk_io_num, FUNC_GPIO, HSPICLK_OUT_IDX);
+ } else {
+ spitest_gpio_output_sel(buscfg.mosi_io_num, FUNC_GPIO, HSPID_OUT_IDX);
+ spitest_gpio_output_sel(buscfg.miso_io_num, FUNC_GPIO, VSPIQ_OUT_IDX);
+ spitest_gpio_output_sel(devcfg.spics_io_num, FUNC_GPIO, HSPICS0_OUT_IDX);
+ spitest_gpio_output_sel(buscfg.sclk_io_num, FUNC_GPIO, HSPICLK_OUT_IDX);
+ }
+
+ //prepare slave tx data
+ for (int k = 0; k < pset->test_size; k++)
+ xQueueSend(context->slave_context.data_to_send, &context->slave_trans[k], portMAX_DELAY);
+
+ //clear master receive buffer
+ memset(context->master_rxbuf, 0x66, sizeof(context->master_rxbuf));
+
+}
+
+static void local_test_loop(const void* arg1, void* arg2)
+{
+ const spitest_param_set_t *pset = arg1;
+ spitest_context_t *context = arg2;
+ spi_device_handle_t spi;
+ spitest_init_transactions(pset, context);
+ const int *timing_speed_array = pset->freq_list;
+
+ ESP_LOGI(MASTER_TAG, "****************** %s ***************", pset->pset_name);
+ for (int i = 0; ; i++) {
+ const int freq = timing_speed_array[i];
+ if (freq==0) break;
+ if (pset->freq_limit && freq > pset->freq_limit) break;
+
+ ESP_LOGI(MASTER_TAG, "======> %dk", freq / 1000);
+ local_test_start(&spi, freq, pset, context);
+
+ for (int k = 0; k < pset->test_size; k++) {
+ //wait for both master and slave end
+ ESP_LOGI(MASTER_TAG, "=> test%d", k);
+ //send master tx data
+ vTaskDelay(9);
+
+ spi_transaction_t *t = &context->master_trans[k];
+ TEST_ESP_OK(spi_device_transmit(spi, t));
+ int len = get_trans_len(pset->dup, t);
+ spitest_master_print_data(t, len);
+
+ size_t rcv_len;
+ slave_rxdata_t *rcv_data = xRingbufferReceive(context->slave_context.data_received, &rcv_len, portMAX_DELAY);
+ spitest_slave_print_data(rcv_data, true);
+
+ //check result
+ bool check_master_data = (pset->dup!=HALF_DUPLEX_MOSI &&
+ (pset->master_limit==0 || freq <= pset->master_limit));
+ bool check_slave_data = (pset->dup!=HALF_DUPLEX_MISO);
+ const bool check_len = true;
+ if (!check_master_data) ESP_LOGI(MASTER_TAG, "skip master data check");
+ if (!check_slave_data) ESP_LOGI(SLAVE_TAG, "skip slave data check");
+
+ TEST_ESP_OK(spitest_check_data(len, t, rcv_data, check_master_data, check_len, check_slave_data));
+ //clean
+ vRingbufferReturnItem(context->slave_context.data_received, rcv_data);
+ }
+ master_free_device_bus(spi);
+ TEST_ASSERT(spi_slave_free(VSPI_HOST) == ESP_OK);
+ }
+}
+
+/************ Timing Test ***********************************************/
+static spitest_param_set_t timing_pgroup[] = {
+ { .pset_name = "FULL_DUP, MASTER IOMUX",
+ .freq_limit = ESP_SPI_SLAVE_MAX_FREQ_SYNC,
+ .master_limit = SPI_MASTER_FREQ_13M,
+ .dup = FULL_DUPLEX,
+ .master_iomux = true,
+ .slave_iomux = false,
+ .slave_tv_ns = TV_INT_CONNECT_GPIO,
+ },
+ { .pset_name = "FULL_DUP, SLAVE IOMUX",
+ .freq_limit = ESP_SPI_SLAVE_MAX_FREQ_SYNC,
+ .master_limit = SPI_MASTER_FREQ_13M,
+ .dup = FULL_DUPLEX,
+ .master_iomux = false,
+ .slave_iomux = true,
+ .slave_tv_ns = TV_INT_CONNECT,
+ },
+ { .pset_name = "FULL_DUP, BOTH GPIO",
+ .freq_limit = ESP_SPI_SLAVE_MAX_FREQ_SYNC,
+ .master_limit = SPI_MASTER_FREQ_10M,
+ .dup = FULL_DUPLEX,
+ .master_iomux = false,
+ .slave_iomux = false,
+ .slave_tv_ns = TV_INT_CONNECT_GPIO,
+ },
+ { .pset_name = "MISO_DUP, MASTER IOMUX",
+ .freq_limit = ESP_SPI_SLAVE_MAX_FREQ_SYNC,
+ .master_limit = ESP_SPI_SLAVE_MAX_FREQ_SYNC,
+ .dup = HALF_DUPLEX_MISO,
+ .master_iomux = true,
+ .slave_iomux = false,
+ .slave_tv_ns = TV_INT_CONNECT_GPIO+12.5,
+ //for freq lower than 20M, the delay is 60(62.5)ns, however, the delay becomes 75ns over 26M
+ },
+ { .pset_name = "MISO_DUP, SLAVE IOMUX",
+ .freq_limit = ESP_SPI_SLAVE_MAX_FREQ_SYNC,
+ //.freq_limit = ESP_SPI_SLAVE_MAX_FREQ_SYNC,
+ .dup = HALF_DUPLEX_MISO,
+ .master_iomux = false,
+ .slave_iomux = true,
+ .slave_tv_ns = TV_INT_CONNECT+12.5,
+ //for freq lower than 20M, the delay is 60(62.5)ns, however, the delay becomes 75ns over 26M
+
+ },
+ { .pset_name = "MISO_DUP, BOTH GPIO",
+ .freq_limit = ESP_SPI_SLAVE_MAX_FREQ_SYNC,
+ //.freq_limit = ESP_SPI_SLAVE_MAX_FREQ_SYNC,
+ .dup = HALF_DUPLEX_MISO,
+ .master_iomux = false,
+ .slave_iomux = false,
+ .slave_tv_ns = TV_INT_CONNECT_GPIO+12.5,
+ //for freq lower than 20M, the delay is 60(62.5)ns, however, the delay becomes 75ns over 26M
+
+ },
+ { .pset_name = "MOSI_DUP, MASTER IOMUX",
+ .freq_limit = ESP_SPI_SLAVE_MAX_FREQ_SYNC,
+ //.freq_limit = ESP_SPI_SLAVE_MAX_READ_FREQ, //ESP_SPI_SLAVE_MAX_FREQ_SYNC,
+ .dup = HALF_DUPLEX_MOSI,
+ .master_iomux = true,
+ .slave_iomux = false,
+ .slave_tv_ns = TV_INT_CONNECT_GPIO,
+ },
+ { .pset_name = "MOSI_DUP, SLAVE IOMUX",
+ .freq_limit = ESP_SPI_SLAVE_MAX_FREQ_SYNC,
+ //.freq_limit = ESP_SPI_SLAVE_MAX_READ_FREQ, //ESP_SPI_SLAVE_MAX_FREQ_SYNC,
+ .dup = HALF_DUPLEX_MOSI,
+ .master_iomux = false,
+ .slave_iomux = true,
+ .slave_tv_ns = TV_INT_CONNECT,
+ },
+ { .pset_name = "MOSI_DUP, BOTH GPIO",
+ .freq_limit = ESP_SPI_SLAVE_MAX_FREQ_SYNC,
+ //.freq_limit = ESP_SPI_SLAVE_MAX_READ_FREQ, //ESP_SPI_SLAVE_MAX_FREQ_SYNC,
+ .dup = HALF_DUPLEX_MOSI,
+ .master_iomux = false,
+ .slave_iomux = false,
+ .slave_tv_ns = TV_INT_CONNECT_GPIO,
+ },
+};
+TEST_SPI_LOCAL(TIMING, timing_pgroup)
+
+/************ Mode Test ***********************************************/
+#define FREQ_LIMIT_MODE SPI_MASTER_FREQ_16M
+static int test_freq_mode_local[]={
+ 1*1000*1000,
+ SPI_MASTER_FREQ_9M, //maximum freq MISO stable before next latch edge
+ SPI_MASTER_FREQ_13M,
+ SPI_MASTER_FREQ_16M,
+ SPI_MASTER_FREQ_20M,
+ SPI_MASTER_FREQ_26M,
+ SPI_MASTER_FREQ_40M,
+ 0,
+};
+
+static spitest_param_set_t mode_pgroup[] = {
+ { .pset_name = "Mode 0",
+ .freq_list = test_freq_mode_local,
+ .master_limit = SPI_MASTER_FREQ_13M,
+ .dup = FULL_DUPLEX,
+ .mode = 0,
+ .master_iomux = false,
+ .slave_iomux = true,
+ .slave_tv_ns = TV_INT_CONNECT,
+ },
+ { .pset_name = "Mode 1",
+ .freq_list = test_freq_mode_local,
+ .freq_limit = SPI_MASTER_FREQ_26M,
+ .master_limit = SPI_MASTER_FREQ_13M,
+ .dup = FULL_DUPLEX,
+ .mode = 1,
+ .master_iomux = false,
+ .slave_iomux = true,
+ .slave_tv_ns = TV_INT_CONNECT,
+ },
+ { .pset_name = "Mode 2",
+ .freq_list = test_freq_mode_local,
+ .master_limit = SPI_MASTER_FREQ_13M,
+ .dup = FULL_DUPLEX,
+ .mode = 2,
+ .master_iomux = false,
+ .slave_iomux = true,
+ .slave_tv_ns = TV_INT_CONNECT,
+ },
+ { .pset_name = "Mode 3",
+ .freq_list = test_freq_mode_local,
+ .freq_limit = SPI_MASTER_FREQ_26M,
+ .master_limit = SPI_MASTER_FREQ_13M,
+ .dup = FULL_DUPLEX,
+ .mode = 3,
+ .master_iomux = false,
+ .slave_iomux = true,
+ .slave_tv_ns = TV_INT_CONNECT,
+ },
+ { .pset_name = "Mode 0, DMA",
+ .freq_list = test_freq_mode_local,
+ .master_limit = SPI_MASTER_FREQ_13M,
+ .dup = FULL_DUPLEX,
+ .mode = 0,
+ .slave_dma_chan = 2,
+ .master_iomux = false,
+ .slave_iomux = true,
+ .slave_tv_ns = TV_INT_CONNECT, //at 16M, the MISO delay (-0.5T+(3+2)apb) equals to non-DMA mode delay (3apb).
+ .length_aligned = true,
+ },
+ { .pset_name = "Mode 1, DMA",
+ .freq_list = test_freq_mode_local,
+ .freq_limit = SPI_MASTER_FREQ_26M,
+ .master_limit = SPI_MASTER_FREQ_13M,
+ .dup = FULL_DUPLEX,
+ .mode = 1,
+ .slave_dma_chan = 2,
+ .master_iomux = false,
+ .slave_iomux = true,
+ .slave_tv_ns = TV_INT_CONNECT,
+ .length_aligned = true,
+ },
+ { .pset_name = "Mode 2, DMA",
+ .freq_list = test_freq_mode_local,
+ .master_limit = SPI_MASTER_FREQ_13M,
+ .dup = FULL_DUPLEX,
+ .mode = 2,
+ .slave_dma_chan = 2,
+ .master_iomux = false,
+ .slave_iomux = true,
+ .slave_tv_ns = TV_INT_CONNECT, //at 16M, the MISO delay (-0.5T+(3+2)apb) equals to non-DMA mode delay (3apb).
+ .length_aligned = true,
+ },
+ { .pset_name = "Mode 3, DMA",
+ .freq_list = test_freq_mode_local,
+ .freq_limit = SPI_MASTER_FREQ_26M,
+ .master_limit = SPI_MASTER_FREQ_13M,
+ .dup = FULL_DUPLEX,
+ .mode = 3,
+ .slave_dma_chan = 2,
+ .master_iomux = false,
+ .slave_iomux = true,
+ .slave_tv_ns = TV_INT_CONNECT,
+ .length_aligned = true,
+ },
+ // MISO ////////////////////////////////////
+ { .pset_name = "MISO, Mode 0",
+ .freq_list = test_freq_mode_local,
+ .dup = HALF_DUPLEX_MISO,
+ .mode = 0,
+ .master_iomux = false,
+ .slave_iomux = true,
+ .slave_tv_ns = TV_INT_CONNECT,
+ },
+ { .pset_name = "MISO, Mode 1",
+ .freq_list = test_freq_mode_local,
+ .dup = HALF_DUPLEX_MISO,
+ .mode = 1,
+ .master_iomux = false,
+ .slave_iomux = true,
+ .slave_tv_ns = TV_INT_CONNECT,
+ },
+ { .pset_name = "MISO, Mode 2",
+ .freq_list = test_freq_mode_local,
+ .dup = HALF_DUPLEX_MISO,
+ .mode = 2,
+ .master_iomux = false,
+ .slave_iomux = true,
+ .slave_tv_ns = TV_INT_CONNECT,
+ },
+ { .pset_name = "MISO, Mode 3",
+ .freq_list = test_freq_mode_local,
+ .dup = HALF_DUPLEX_MISO,
+ .mode = 3,
+ .master_iomux = false,
+ .slave_iomux = true,
+ .slave_tv_ns = TV_INT_CONNECT,
+ },
+ { .pset_name = "MISO, Mode 0, DMA",
+ .freq_list = test_freq_mode_local,
+ .dup = HALF_DUPLEX_MISO,
+ .mode = 0,
+ .slave_dma_chan = 2,
+ .master_iomux = false,
+ .slave_iomux = true,
+ .slave_tv_ns = TV_INT_CONNECT+12.5, //at 16M, the MISO delay (-0.5T+(3+2)apb) equals to non-DMA mode delay (3apb).
+ .length_aligned = true,
+ },
+ { .pset_name = "MISO, Mode 1, DMA",
+ .freq_list = test_freq_mode_local,
+ .dup = HALF_DUPLEX_MISO,
+ .mode = 1,
+ .slave_dma_chan = 2,
+ .master_iomux = false,
+ .slave_iomux = true,
+ .slave_tv_ns = TV_INT_CONNECT,
+ .length_aligned = true,
+ },
+ { .pset_name = "MISO, Mode 2, DMA",
+ .freq_list = test_freq_mode_local,
+ .dup = HALF_DUPLEX_MISO,
+ .mode = 2,
+ .slave_dma_chan = 2,
+ .master_iomux = false,
+ .slave_iomux = true,
+ .slave_tv_ns = TV_INT_CONNECT+12.5, //at 16M, the MISO delay (-0.5T+(3+2)apb) equals to non-DMA mode delay (3apb).
+ .length_aligned = true,
+ },
+ { .pset_name = "MISO, Mode 3, DMA",
+ .freq_list = test_freq_mode_local,
+ .dup = HALF_DUPLEX_MISO,
+ .mode = 3,
+ .slave_dma_chan = 2,
+ .master_iomux = false,
+ .slave_iomux = true,
+ .slave_tv_ns = TV_INT_CONNECT,
+ .length_aligned = true,
+ },
+};
+TEST_SPI_LOCAL(MODE, mode_pgroup)
+
+/********************************************************************************
+ * Test By Master & Slave (2 boards)
+ ********************************************************************************/
+static void test_master_init(void** context);
+static void test_master_deinit(void* context);
+static void test_master_loop(const void *test_cfg, void* context);
+
+static const ptest_func_t master_test_func = {
+ .pre_test = test_master_init,
+ .post_test = test_master_deinit,
+ .loop = test_master_loop,
+ .def_param = spitest_def_param,
+};
+
+static void test_slave_init(void** context);
+static void test_slave_deinit(void* context);
+static void test_slave_loop(const void *test_cfg, void* context);
+
+static const ptest_func_t slave_test_func = {
+ .pre_test = test_slave_init,
+ .post_test = test_slave_deinit,
+ .loop = test_slave_loop,
+ .def_param = spitest_def_param,
+};
+
+#define TEST_SPI_MASTER_SLAVE(name, param_group) \
+ PARAM_GROUP_DECLARE(name, param_group) \
+ TEST_MASTER_SLAVE(name, param_group, "[spi_ms][test_env=Example_SPI_Multi_device][timeout=120]", &master_test_func, &slave_test_func)
+
+/************ Master Code ***********************************************/
+static void test_master_init(void** arg)
+{
+ TEST_ASSERT(*arg==NULL);
+ *arg = malloc(sizeof(spitest_context_t));
+ spitest_context_t* context = *arg;
+ TEST_ASSERT(context!=NULL);
+ context->slave_context = (spi_slave_task_context_t){};
+ esp_err_t err = init_slave_context(&context->slave_context);
+ TEST_ASSERT(err == ESP_OK);
+}
+
+static void test_master_deinit(void* arg)
+{
+ spitest_context_t* context = (spitest_context_t*)arg;
+ deinit_slave_context(&context->slave_context);
+}
+
+static void test_master_start(spi_device_handle_t *spi, int freq, const spitest_param_set_t* pset, spitest_context_t* context)
+{
+ //master config
+ spi_bus_config_t buspset=SPI_BUS_TEST_DEFAULT_CONFIG();
+ buspset.miso_io_num = HSPI_IOMUX_PIN_NUM_MISO;
+ buspset.mosi_io_num = HSPI_IOMUX_PIN_NUM_MOSI;
+ buspset.sclk_io_num = HSPI_IOMUX_PIN_NUM_CLK;
+ //this does nothing, but avoid the driver from using native pins
+ if (!pset->master_iomux) buspset.quadhd_io_num = VSPI_IOMUX_PIN_NUM_MISO;
+ spi_device_interface_config_t devpset=SPI_DEVICE_TEST_DEFAULT_CONFIG();
+ devpset.spics_io_num = HSPI_IOMUX_PIN_NUM_CS;
+ devpset.mode = pset->mode;
+ const int cs_pretrans_max = 15;
+ if (pset->dup==HALF_DUPLEX_MISO) {
+ devpset.cs_ena_pretrans = cs_pretrans_max;
+ devpset.flags |= SPI_DEVICE_HALFDUPLEX;
+ } else if (pset->dup == HALF_DUPLEX_MOSI) {
+ devpset.cs_ena_pretrans = cs_pretrans_max;
+ devpset.flags |= SPI_DEVICE_NO_DUMMY;
+ } else {
+ devpset.cs_ena_pretrans = cs_pretrans_max;//20;
+ }
+ const int cs_posttrans_max = 15;
+ devpset.cs_ena_posttrans = cs_posttrans_max;
+ devpset.input_delay_ns = pset->slave_tv_ns;
+ devpset.clock_speed_hz = freq;
+ if (pset->master_limit != 0 && freq > pset->master_limit) devpset.flags |= SPI_DEVICE_NO_DUMMY;
+ TEST_ESP_OK(spi_bus_initialize(HSPI_HOST, &buspset, pset->master_dma_chan));
+ TEST_ESP_OK(spi_bus_add_device(HSPI_HOST, &devpset, spi));
+
+ //prepare data for the slave
+ for (int i = 0; i < pset->test_size; i ++) {
+ /* in the single board, the data is send to the slave task, then to the driver.
+ * However, in this test we don't know the data received by the slave.
+ * So we send to the return queue of the slave directly.
+ */
+ //xQueueSend( slave_context.data_to_send, &slave_txdata[i], portMAX_DELAY );
+
+ uint8_t slave_buffer[320+8];
+ int length;
+ if (pset->dup!=HALF_DUPLEX_MISO) {
+ length = context->master_trans[i].length;
+ } else {
+ length = context->master_trans[i].rxlength;
+ }
+ uint32_t* ptr = (uint32_t*)slave_buffer;
+ ptr[0] = length;
+ ptr[1] = (uint32_t)context->slave_trans[i].start;
+ if (context->master_trans[i].tx_buffer!=NULL) {
+ memcpy(ptr+2, context->master_trans[i].tx_buffer, (context->master_trans[i].length+7)/8);
+ }
+ //Send to return queue directly
+ xRingbufferSend(context->slave_context.data_received, slave_buffer, 8+(length+7)/8, portMAX_DELAY);
+ }
+ memset(context->master_rxbuf, 0x66, sizeof(context->master_rxbuf));
+}
+
+static void test_master_loop(const void *arg1, void* arg2)
+{
+ const spitest_param_set_t *test_cfg = (spitest_param_set_t*)arg1;
+ spitest_context_t* context = (spitest_context_t*)arg2;
+ spi_device_handle_t spi;
+ spitest_init_transactions(test_cfg, context);
+ const int *timing_speed_array = test_cfg->freq_list;
+
+ ESP_LOGI(MASTER_TAG, "****************** %s ***************", test_cfg->pset_name);
+ for (int i=0; ; i++ ) {
+ const int freq = timing_speed_array[i];
+ if (freq==0) break;
+ if (test_cfg->freq_limit && freq > test_cfg->freq_limit) break;
+
+ ESP_LOGI(MASTER_TAG, "==============> %dk", freq/1000);
+ test_master_start(&spi, freq, test_cfg, context);
+
+ unity_wait_for_signal("slave ready");
+
+ for( int j= 0; j < test_cfg->test_size; j ++ ) {
+ //wait for both master and slave end
+ ESP_LOGI( MASTER_TAG, "=> test%d", j );
+ //send master tx data
+ vTaskDelay(20);
+
+ spi_transaction_t *t = &context->master_trans[j];
+ TEST_ESP_OK (spi_device_transmit(spi, t) );
+ int len = get_trans_len(test_cfg->dup, t);
+ spitest_master_print_data(t, len);
+
+ size_t rcv_len;
+ slave_rxdata_t *rcv_data = xRingbufferReceive( context->slave_context.data_received, &rcv_len, portMAX_DELAY );
+ spitest_slave_print_data(rcv_data, false);
+
+ //check result
+ bool check_master_data = (test_cfg->dup != HALF_DUPLEX_MOSI &&
+ (test_cfg->master_limit == 0 || freq <= test_cfg->master_limit));
+ const bool check_slave_data = false;
+ const bool check_len = false;
+ if (!check_master_data) {
+ ESP_LOGI(MASTER_TAG, "skip data check due to duplex mode or freq.");
+ } else {
+ TEST_ESP_OK(spitest_check_data(len, t, rcv_data, check_master_data,
+ check_len, check_slave_data));
+ }
+ //clean
+ vRingbufferReturnItem( context->slave_context.data_received, rcv_data );
+ }
+ master_free_device_bus(spi);
+ }
+}
+
+/************ Slave Code ***********************************************/
+static void test_slave_init(void** arg)
+{
+ TEST_ASSERT(*arg==NULL);
+ *arg = malloc(sizeof(spitest_context_t));
+ spitest_context_t* context = (spitest_context_t*)*arg;
+ TEST_ASSERT(context!=NULL);
+ context->slave_context = (spi_slave_task_context_t){};
+ esp_err_t err = init_slave_context( &context->slave_context );
+ TEST_ASSERT( err == ESP_OK );
+
+ xTaskCreate( spitest_slave_task, "spi_slave", 4096, &context->slave_context, 0, &context->handle_slave);
+}
+
+static void test_slave_deinit(void* arg)
+{
+ spitest_context_t* context = (spitest_context_t*)arg;
+ vTaskDelete( context->handle_slave );
+ context->handle_slave = 0;
+
+ deinit_slave_context(&context->slave_context);
+}
+
+static void timing_slave_start(int speed, const spitest_param_set_t* pset, spitest_context_t *context)
+{
+ //slave config
+ spi_bus_config_t slv_buscfg=SPI_BUS_TEST_DEFAULT_CONFIG();
+ slv_buscfg.miso_io_num = VSPI_IOMUX_PIN_NUM_MISO;
+ slv_buscfg.mosi_io_num = VSPI_IOMUX_PIN_NUM_MOSI;
+ slv_buscfg.sclk_io_num = VSPI_IOMUX_PIN_NUM_CLK;
+ //this does nothing, but avoid the driver from using native pins
+ if (!pset->slave_iomux) slv_buscfg.quadhd_io_num = HSPI_IOMUX_PIN_NUM_CLK;
+ spi_slave_interface_config_t slvcfg=SPI_SLAVE_TEST_DEFAULT_CONFIG();
+ slvcfg.spics_io_num = VSPI_IOMUX_PIN_NUM_CS;
+ slvcfg.mode = pset->mode;
+ //Enable pull-ups on SPI lines so we don't detect rogue pulses when no master is connected.
+ slave_pull_up(&slv_buscfg, slvcfg.spics_io_num);
+
+ TEST_ESP_OK( spi_slave_initialize(VSPI_HOST, &slv_buscfg, &slvcfg, pset->slave_dma_chan) );
+
+ //prepare data for the master
+ for (int i = 0; i < pset->test_size; i++) {
+ if (pset->dup==FULL_DUPLEX) {
+ memcpy(context->master_trans[i].rx_buffer, context->slave_trans[i].start, (context->master_trans[i].length+7)/8);
+ } else if (pset->dup==HALF_DUPLEX_MISO) {
+ memcpy(context->master_trans[i].rx_buffer, context->slave_trans[i].start, (context->master_trans[i].rxlength+7)/8);
+ }
+ }
+}
+
+static void test_slave_loop(const void *arg1, void* arg2)
+{
+ const spitest_param_set_t *pset = (spitest_param_set_t*)arg1;
+ spitest_context_t* context = (spitest_context_t*)arg2;
+ ESP_LOGI(SLAVE_TAG, "****************** %s ***************", pset->pset_name);
+ spitest_init_transactions(pset, context);
+
+ const int *timing_speed_array = pset->freq_list;
+ for (int i=0; ; i++ ) {
+ const int freq = timing_speed_array[i];
+ if (freq==0) break;
+ if (pset->freq_limit != 0 && freq > pset->freq_limit) break;
+
+ ESP_LOGI(MASTER_TAG, "==============> %dk", timing_speed_array[i]/1000);
+ //Initialize SPI slave interface
+ timing_slave_start(freq, pset, context);
+
+ //prepare slave tx data
+ for (int i = 0; i < pset->test_size; i ++) {
+ xQueueSend( context->slave_context.data_to_send, &context->slave_trans[i], portMAX_DELAY );
+ //memcpy(context->master_trans[i].rx_buffer, context->slave_trans[i].start, (context->master_trans[i].length+7)/8);
+ }
+
+ vTaskDelay(50/portTICK_PERIOD_MS);
+ unity_send_signal("slave ready");
+
+ for( int i= 0; i < pset->test_size; i ++ ) {
+ //wait for both master and slave end
+ ESP_LOGI( MASTER_TAG, "===== test%d =====", i );
+ //send master tx data
+ vTaskDelay(20);
+
+ spi_transaction_t *t = &context->master_trans[i];
+ int len = get_trans_len(pset->dup, t);
+ spitest_master_print_data(t, FULL_DUPLEX);
+
+ size_t rcv_len;
+ slave_rxdata_t *rcv_data = xRingbufferReceive( context->slave_context.data_received, &rcv_len, portMAX_DELAY );
+ spitest_slave_print_data(rcv_data, true);
+
+ //check result
+ const bool check_master_data = false;
+ bool check_slave_data = (pset->dup!=HALF_DUPLEX_MISO);
+ const bool check_len = true;
+ TEST_ESP_OK(spitest_check_data(len, t, rcv_data, check_master_data, check_len, check_slave_data));
+ //clean
+ vRingbufferReturnItem( context->slave_context.data_received, rcv_data );
+ }
+ TEST_ASSERT(spi_slave_free(VSPI_HOST) == ESP_OK);
+ }
+}
+
+/************ Timing Test ***********************************************/
+static spitest_param_set_t timing_conf[] = {
+ { .pset_name = "FULL_DUP, BOTH IOMUX",
+ .freq_limit = ESP_SPI_SLAVE_MAX_FREQ,
+ .master_limit = SPI_MASTER_FREQ_16M,
+ .dup = FULL_DUPLEX,
+ .master_iomux= true,
+ .slave_iomux = true,
+ .slave_tv_ns = TV_WITH_ESP_SLAVE,
+ },
+ { .pset_name = "FULL_DUP, MASTER IOMUX",
+ .freq_limit = ESP_SPI_SLAVE_MAX_FREQ,
+ .master_limit = SPI_MASTER_FREQ_11M,
+ .dup = FULL_DUPLEX,
+ .master_iomux = true,
+ .slave_iomux = false,
+ .slave_tv_ns = TV_WITH_ESP_SLAVE_GPIO,
+ },
+ { .pset_name = "FULL_DUP, SLAVE IOMUX",
+ .freq_limit = ESP_SPI_SLAVE_MAX_FREQ,
+ .master_limit = SPI_MASTER_FREQ_11M,
+ .dup = FULL_DUPLEX,
+ .master_iomux = false,
+ .slave_iomux = true,
+ .slave_tv_ns = TV_WITH_ESP_SLAVE,
+ },
+ { .pset_name = "FULL_DUP, BOTH GPIO",
+ .freq_limit = ESP_SPI_SLAVE_MAX_FREQ,
+ .master_limit = SPI_MASTER_FREQ_9M,
+ .dup = FULL_DUPLEX,
+ .master_iomux = false,
+ .slave_iomux = false,
+ .slave_tv_ns = TV_WITH_ESP_SLAVE_GPIO,
+ },
+ { .pset_name = "MOSI_DUP, BOTH IOMUX",
+ .freq_limit = ESP_SPI_SLAVE_MAX_FREQ,
+ .dup = HALF_DUPLEX_MOSI,
+ .master_iomux= true,
+ .slave_iomux = true,
+ .slave_tv_ns = TV_WITH_ESP_SLAVE,
+ },
+ { .pset_name = "MOSI_DUP, MASTER IOMUX",
+ .freq_limit = ESP_SPI_SLAVE_MAX_FREQ,
+ .dup = HALF_DUPLEX_MOSI,
+ .master_iomux= true,
+ .slave_iomux = false,
+ .slave_tv_ns = TV_WITH_ESP_SLAVE_GPIO,
+ },
+ { .pset_name = "MOSI_DUP, SLAVE IOMUX",
+ .freq_limit = ESP_SPI_SLAVE_MAX_FREQ,
+ .dup = HALF_DUPLEX_MOSI,
+ .master_iomux= false,
+ .slave_iomux = true,
+ .slave_tv_ns = TV_WITH_ESP_SLAVE,
+ },
+ { .pset_name = "MOSI_DUP, BOTH GPIO",
+ .freq_limit = ESP_SPI_SLAVE_MAX_FREQ,
+ .dup = HALF_DUPLEX_MOSI,
+ .master_iomux= false,
+ .slave_iomux = false,
+ .slave_tv_ns = TV_WITH_ESP_SLAVE_GPIO,
+ },
+ { .pset_name = "MISO_DUP, BOTH IOMUX",
+ .freq_limit = ESP_SPI_SLAVE_MAX_FREQ,
+ .dup = HALF_DUPLEX_MISO,
+ .master_iomux = true,
+ .slave_iomux = true,
+ .slave_tv_ns = TV_WITH_ESP_SLAVE,
+ },
+ { .pset_name = "MISO_DUP, MASTER IOMUX",
+ .freq_limit = ESP_SPI_SLAVE_MAX_FREQ,
+ .dup = HALF_DUPLEX_MISO,
+ .master_iomux = true,
+ .slave_iomux = false,
+ .slave_tv_ns = TV_WITH_ESP_SLAVE_GPIO,
+ },
+ { .pset_name = "MISO_DUP, SLAVE IOMUX",
+ .freq_limit = ESP_SPI_SLAVE_MAX_FREQ,
+ .dup = HALF_DUPLEX_MISO,
+ .master_iomux = false,
+ .slave_iomux = true,
+ .slave_tv_ns = TV_WITH_ESP_SLAVE,
+ },
+ { .pset_name = "MISO_DUP, BOTH GPIO",
+ .freq_limit = ESP_SPI_SLAVE_MAX_FREQ,
+ .dup = HALF_DUPLEX_MISO,
+ .master_iomux = false,
+ .slave_iomux = false,
+ .slave_tv_ns = TV_WITH_ESP_SLAVE_GPIO,
+ },
+};
+TEST_SPI_MASTER_SLAVE(TIMING, timing_conf)
+
+/************ Mode Test ***********************************************/
+#define FREQ_LIMIT_MODE SPI_MASTER_FREQ_16M
+//Set to this input delay so that the master will read with delay until 7M
+#define DELAY_HCLK_UNTIL_7M 12.5*3
+
+static int test_freq_mode_ms[]={
+ 100*1000,
+ 6*1000*1000,
+ 7*1000*1000,
+ SPI_MASTER_FREQ_8M, //maximum freq MISO stable before next latch edge
+ SPI_MASTER_FREQ_9M, //maximum freq MISO stable before next latch edge
+ SPI_MASTER_FREQ_10M,
+ SPI_MASTER_FREQ_11M,
+ SPI_MASTER_FREQ_13M,
+ SPI_MASTER_FREQ_16M,
+ SPI_MASTER_FREQ_20M,
+ 0,
+};
+static int test_freq_20M_only[]={
+ SPI_MASTER_FREQ_20M,
+ 0,
+};
+
+spitest_param_set_t mode_conf[] = {
+ //non-DMA tests
+ { .pset_name = "mode 0, no DMA",
+ .freq_list = test_freq_mode_ms,
+ .master_limit = FREQ_LIMIT_MODE,
+ .dup = FULL_DUPLEX,
+ .master_iomux= true,
+ .slave_iomux = true,
+ .slave_tv_ns = TV_WITH_ESP_SLAVE,
+ .mode = 0,
+ },
+ { .pset_name = "mode 1, no DMA",
+ .freq_list = test_freq_mode_ms,
+ .master_limit = FREQ_LIMIT_MODE,
+ .dup = FULL_DUPLEX,
+ .master_iomux= true,
+ .slave_iomux = true,
+ .slave_tv_ns = TV_WITH_ESP_SLAVE,
+ .mode = 1,
+ },
+ { .pset_name = "mode 2, no DMA",
+ .freq_list = test_freq_mode_ms,
+ .master_limit = FREQ_LIMIT_MODE,
+ .dup = FULL_DUPLEX,
+ .master_iomux= true,
+ .slave_iomux = true,
+ .slave_tv_ns = TV_WITH_ESP_SLAVE,
+ .mode = 2,
+ },
+ { .pset_name = "mode 3, no DMA",
+ .freq_list = test_freq_mode_ms,
+ .master_limit = FREQ_LIMIT_MODE,
+ .dup = FULL_DUPLEX,
+ .master_iomux= true,
+ .slave_iomux = true,
+ .slave_tv_ns = TV_WITH_ESP_SLAVE,
+ .mode = 3,
+ },
+ //the master can only read to 16MHz, use half-duplex mode to read at 20.
+ { .pset_name = "mode 0, no DMA, 20M",
+ .freq_list = test_freq_20M_only,
+ .dup = HALF_DUPLEX_MISO,
+ .master_iomux= true,
+ .slave_iomux = true,
+ .slave_tv_ns = TV_WITH_ESP_SLAVE,
+ .mode = 0,
+ },
+ { .pset_name = "mode 1, no DMA, 20M",
+ .freq_list = test_freq_20M_only,
+ .dup = HALF_DUPLEX_MISO,
+ .master_iomux= true,
+ .slave_iomux = true,
+ .slave_tv_ns = TV_WITH_ESP_SLAVE,
+ .mode = 1,
+ },
+ { .pset_name = "mode 2, no DMA, 20M",
+ .freq_list = test_freq_20M_only,
+ .dup = HALF_DUPLEX_MISO,
+ .master_iomux= true,
+ .slave_iomux = true,
+ .slave_tv_ns = TV_WITH_ESP_SLAVE,
+ .mode = 2,
+ },
+ { .pset_name = "mode 3, no DMA, 20M",
+ .freq_list = test_freq_20M_only,
+ .dup = HALF_DUPLEX_MISO,
+ .master_iomux= true,
+ .slave_iomux = true,
+ .slave_tv_ns = TV_WITH_ESP_SLAVE,
+ .mode = 3,
+ },
+ //DMA tests
+ { .pset_name = "mode 0, DMA",
+ .freq_list = test_freq_mode_ms,
+ .master_limit = FREQ_LIMIT_MODE,
+ .dup = FULL_DUPLEX,
+ .master_iomux= true,
+ .slave_iomux = true,
+ .slave_tv_ns = DELAY_HCLK_UNTIL_7M,
+ .mode = 0,
+ .master_dma_chan = 1,
+ .slave_dma_chan = 1,
+ .length_aligned = true,
+ },
+ { .pset_name = "mode 1, DMA",
+ .freq_list = test_freq_mode_ms,
+ .master_limit = FREQ_LIMIT_MODE,
+ .dup = FULL_DUPLEX,
+ .master_iomux= true,
+ .slave_iomux = true,
+ .slave_tv_ns = TV_WITH_ESP_SLAVE,
+ .mode = 1,
+ .master_dma_chan = 1,
+ .slave_dma_chan = 1,
+ .length_aligned = true,
+ },
+ { .pset_name = "mode 2, DMA",
+ .freq_list = test_freq_mode_ms,
+ .master_limit = FREQ_LIMIT_MODE,
+ .dup = FULL_DUPLEX,
+ .master_iomux= true,
+ .slave_iomux = true,
+ .slave_tv_ns = DELAY_HCLK_UNTIL_7M,
+ .mode = 2,
+ .master_dma_chan = 1,
+ .slave_dma_chan = 1,
+ .length_aligned = true,
+ },
+ { .pset_name = "mode 3, DMA",
+ .freq_list = test_freq_mode_ms,
+ .master_limit = FREQ_LIMIT_MODE,
+ .dup = FULL_DUPLEX,
+ .master_iomux= true,
+ .slave_iomux = true,
+ .slave_tv_ns = TV_WITH_ESP_SLAVE,
+ .mode = 3,
+ .master_dma_chan = 1,
+ .slave_dma_chan = 1,
+ .length_aligned = true,
+ },
+ //the master can only read to 16MHz, use half-duplex mode to read at 20.
+ { .pset_name = "mode 0, DMA, 20M",
+ .freq_list = test_freq_20M_only,
+ .dup = HALF_DUPLEX_MISO,
+ .master_iomux= true,
+ .slave_iomux = true,
+ .slave_tv_ns = TV_WITH_ESP_SLAVE,
+ .mode = 0,
+ .master_dma_chan = 1,
+ .slave_dma_chan = 1,
+ },
+ { .pset_name = "mode 1, DMA, 20M",
+ .freq_list = test_freq_20M_only,
+ .dup = HALF_DUPLEX_MISO,
+ .master_iomux= true,
+ .slave_iomux = true,
+ .slave_tv_ns = TV_WITH_ESP_SLAVE,
+ .mode = 1,
+ .master_dma_chan = 1,
+ .slave_dma_chan = 1,
+ },
+ { .pset_name = "mode 2, DMA, 20M",
+ .freq_list = test_freq_20M_only,
+ .dup = HALF_DUPLEX_MISO,
+ .master_iomux= true,
+ .slave_iomux = true,
+ .slave_tv_ns = TV_WITH_ESP_SLAVE,
+ .mode = 2,
+ .master_dma_chan = 1,
+ .slave_dma_chan = 1,
+ },
+ { .pset_name = "mode 3, DMA, 20M",
+ .freq_list = test_freq_20M_only,
+ .dup = HALF_DUPLEX_MISO,
+ .master_iomux= true,
+ .slave_iomux = true,
+ .slave_tv_ns = TV_WITH_ESP_SLAVE,
+ .mode = 3,
+ .master_dma_chan = 1,
+ .slave_dma_chan = 1,
+ },
+};
+TEST_SPI_MASTER_SLAVE(MODE, mode_conf)