diff --git a/components/esp8266/CMakeLists.txt b/components/esp8266/CMakeLists.txt index 31783bda..4eaf46bd 100644 --- a/components/esp8266/CMakeLists.txt +++ b/components/esp8266/CMakeLists.txt @@ -46,6 +46,7 @@ else() "driver/i2s.c" "driver/pwm.c" "driver/spi.c" + "driver/hspi_logic_layer.c" "driver/uart.c" "driver/ir_tx.c" "driver/ir_rx.c" diff --git a/components/esp8266/Kconfig b/components/esp8266/Kconfig index 8fdd15d9..5ae4a6b6 100644 --- a/components/esp8266/Kconfig +++ b/components/esp8266/Kconfig @@ -627,3 +627,12 @@ config ESP8266_PHY_MAX_WIFI_TX_POWER endmenu # PHY +menu HSPI +config ESP8266_HSPI_HIGH_THROUGHPUT + bool "Do some optimization to improve throughput" + default n + help + If enable this configuration, some spi api will be placed into iram. + And it will reduce iram memory. +endmenu # Driver + diff --git a/components/esp8266/driver/hspi_logic_layer.c b/components/esp8266/driver/hspi_logic_layer.c new file mode 100644 index 00000000..fbe7169e --- /dev/null +++ b/components/esp8266/driver/hspi_logic_layer.c @@ -0,0 +1,278 @@ + +#include +#include +#include +#include "freertos/FreeRTOS.h" +#include "freertos/task.h" +#include "freertos/semphr.h" +#include "freertos/event_groups.h" +#include "freertos/stream_buffer.h" +#include "ringbuf.h" + +#include "esp8266/spi_struct.h" +#include "esp8266/gpio_struct.h" +#include "esp_system.h" +#include "esp_log.h" + +#include "driver/gpio.h" +#include "driver/spi.h" +#include "driver/hspi_logic_layer.h" + +static const char *TAG = "hspi_logic"; +#define SPI_CHECK(a, str, ret_val) \ + do { \ + if (!(a)) { \ + ESP_LOGE(TAG,"%s(%d): %s", __FUNCTION__, __LINE__, str); \ + return (ret_val); \ + } \ + } while(0) + +typedef struct { + gpio_num_t trigger_pin; + uint8_t trigger_level; + bool is_sending; + bool is_blocking_recv; + uint32_t sending_len; + uint32_t recving_len; + StreamBufferHandle_t* tx_buffer; + StreamBufferHandle_t* rx_buffer; + + SemaphoreHandle_t semphor; + spi_event_callback_t event_cb; +} spi_logic_device_t; + +static spi_logic_device_t * spi_logic_device; + +static void IRAM_ATTR hspi_slave_event_callback(int event, void *arg) +{ + int x; + BaseType_t xHigherPriorityTaskWoken; + uint32_t status; + uint32_t trans_done; + uint32_t data[16]; + spi_trans_t trans; + uint16_t cmd = 0; + bool trigger_flag = false; + + switch (event) { + case SPI_TRANS_DONE_EVENT: { + gpio_set_level(spi_logic_device->trigger_pin, !spi_logic_device->trigger_level); + trans_done = *(uint32_t *)arg; + if (trans_done & SPI_SLV_RD_BUF_DONE) { + if (spi_logic_device->sending_len == 0) { + spi_logic_device->is_sending = false; + spi_logic_device->sending_len = xStreamBufferBytesAvailable(spi_logic_device->tx_buffer); + if (spi_logic_device->sending_len > 0) { + spi_slave_set_status(HSPI_HOST, (uint32_t*)&spi_logic_device->sending_len); + spi_logic_device->is_sending = true; + trigger_flag = true; + } + } else { + memset(&trans, 0x0, sizeof(trans)); + trans.cmd = &cmd; + trans.addr = NULL; + trans.bits.val = 0; + // In Slave mode, spi cmd must be longer than 3 bits and shorter than 16 bits + trans.bits.cmd = 8 * 1; + // In Slave mode, spi addr must be longer than 1 bits and shorter than 32 bits + trans.bits.addr = 8 * 1; + trans.bits.mosi = 0; + trans.miso = data; + trans.bits.miso = xStreamBufferReceiveFromISR(spi_logic_device->tx_buffer, data, 64, &xHigherPriorityTaskWoken); + if (trans.bits.miso != 0) { + spi_logic_device->sending_len -= trans.bits.miso; + trans.bits.miso <<= 3; + spi_trans(HSPI_HOST, &trans); + trigger_flag = true;; + } + } + } + + if (trans_done & SPI_SLV_WR_BUF_DONE) { + uint32_t len = spi_logic_device->recving_len; + if (len > 64) { + len = 64; + } + + if (len > 0) { + for (x = 0; x < 16; x++) { + data[x] = SPI1.data_buf[x]; + } + xStreamBufferSendFromISR(spi_logic_device->rx_buffer, (void *) data, len, &xHigherPriorityTaskWoken); + spi_logic_device->recving_len -= len; + } else { + ets_printf("remained %d\r\n", len); + } + + if (xStreamBufferSpacesAvailable(spi_logic_device->rx_buffer) >= 64) { + trigger_flag = true; + } else { + spi_logic_device->is_blocking_recv = true; + } + } + + if (trans_done & SPI_SLV_WR_STA_DONE) { + spi_slave_get_status(HSPI_HOST, &status); + spi_logic_device->recving_len = status; + uint32_t tx_size = xStreamBufferBytesAvailable(spi_logic_device->tx_buffer); + + if (spi_logic_device->recving_len > 0) { + trigger_flag = true; + } else if (tx_size > 0) { + if (spi_logic_device->is_sending == false) { + spi_slave_set_status(HSPI_HOST, &tx_size); + } + trigger_flag = true; + } + } + + if (trans_done & SPI_SLV_RD_STA_DONE) { + memset(&trans, 0x0, sizeof(trans)); + trans.cmd = &cmd; + trans.addr = NULL; + trans.bits.val = 0; + // In Slave mode, spi cmd must be longer than 3 bits and shorter than 16 bits + trans.bits.cmd = 8 * 1; + // In Slave mode, spi addr must be longer than 1 bits and shorter than 32 bits + trans.bits.addr = 8 * 1; + trans.bits.mosi = 0; + trans.miso = data; + trans.bits.miso = xStreamBufferReceiveFromISR(spi_logic_device->tx_buffer, data, 64, &xHigherPriorityTaskWoken); + if (trans.bits.miso != 0) { + spi_logic_device->sending_len -= trans.bits.miso; + trans.bits.miso <<= 3; + spi_trans(HSPI_HOST, &trans); + trigger_flag = true; + } + } + + if (trigger_flag) { + gpio_set_level(spi_logic_device->trigger_pin, spi_logic_device->trigger_level); + } + + if (spi_logic_device->event_cb) { + spi_logic_device->event_cb(event, arg); + } + + if (xHigherPriorityTaskWoken == pdTRUE) { + taskYIELD(); + } + } + break; + case SPI_DEINIT_EVENT: { + } + break; + } + +} + +uint32_t hspi_slave_logic_read_data(uint8_t*data, uint32_t len, TickType_t xTicksToWait) +{ + uint32_t ret = 0; + + ret = xStreamBufferReceive(spi_logic_device->rx_buffer, data, len, xTicksToWait); + if (spi_logic_device->is_blocking_recv) { + if (xStreamBufferBytesAvailable(spi_logic_device->rx_buffer) > 64) { + gpio_set_level(spi_logic_device->trigger_pin, spi_logic_device->trigger_level); + spi_logic_device->is_blocking_recv = false; + } + } + + return ret; +} + +uint32_t hspi_slave_logic_write_data(uint8_t*data, uint32_t len, TickType_t xTicksToWait) +{ + uint32_t ret = 0; + uint32_t avail_spaces = 0; + + if (!spi_logic_device->is_sending) { + portENTER_CRITICAL(); + avail_spaces = xStreamBufferSpacesAvailable(spi_logic_device->tx_buffer); + if (avail_spaces > len) { + avail_spaces = len; + } + ret = xStreamBufferSend(spi_logic_device->tx_buffer, data, avail_spaces, xTicksToWait); // + spi_logic_device->sending_len = xStreamBufferBytesAvailable(spi_logic_device->tx_buffer); + spi_slave_set_status(HSPI_HOST, (uint32_t*)&spi_logic_device->sending_len); + spi_logic_device->is_sending = true; + gpio_set_level(spi_logic_device->trigger_pin, spi_logic_device->trigger_level); + portEXIT_CRITICAL(); + } + + if (ret < len) { + ret += xStreamBufferSend(spi_logic_device->tx_buffer, data + ret, len - ret, xTicksToWait); + } + + return ret; +} + +esp_err_t hspi_slave_logic_device_create(gpio_num_t trigger_pin, uint32_t trigger_level,uint32_t tx_buffer_size, uint32_t rx_buffer_size) +{ + SPI_CHECK(GPIO_IS_VALID_GPIO(trigger_pin), "gpio num error", ESP_ERR_INVALID_ARG); + SPI_CHECK(tx_buffer_size != 0, "tx buffer error", ESP_ERR_INVALID_ARG); + SPI_CHECK(rx_buffer_size != 0, "rx buffer error", ESP_ERR_INVALID_ARG); + + gpio_config_t io_conf; + + if (spi_logic_device) { + hspi_slave_logic_device_delete(); + } + spi_logic_device = (spi_logic_device_t*)malloc(sizeof(spi_logic_device_t)); + assert(spi_logic_device); + + memset(spi_logic_device, 0x0, sizeof(spi_logic_device_t)); + spi_logic_device->tx_buffer = xStreamBufferCreate(tx_buffer_size,1); + if (!spi_logic_device->tx_buffer) { + free(spi_logic_device); + spi_logic_device = NULL; + return ESP_ERR_NO_MEM; + } + + spi_logic_device->rx_buffer = xStreamBufferCreate(rx_buffer_size,1); + if (!spi_logic_device->rx_buffer) { + vStreamBufferDelete(spi_logic_device->tx_buffer); + spi_logic_device->tx_buffer = NULL; + + free(spi_logic_device); + spi_logic_device = NULL; + return ESP_ERR_NO_MEM; + } + + spi_logic_device->trigger_pin = trigger_pin; + spi_logic_device->trigger_level = (trigger_level==1)?1:0; + + memset(&io_conf, 0x0, sizeof(io_conf)); + io_conf.intr_type = GPIO_INTR_DISABLE; + io_conf.mode = GPIO_MODE_OUTPUT; + io_conf.pin_bit_mask = (1ULL << trigger_pin); + io_conf.pull_down_en = 0; + io_conf.pull_up_en = 0; + gpio_config(&io_conf); + gpio_set_level(trigger_pin, !spi_logic_device->trigger_level); + + spi_get_event_callback(HSPI_HOST, &spi_logic_device->event_cb); + + spi_event_callback_t event_cb = hspi_slave_event_callback; + spi_set_event_callback(HSPI_HOST, &event_cb); + + return ESP_OK; +} + +esp_err_t hspi_slave_logic_device_delete(void) +{ + if (spi_logic_device == NULL) { + return ESP_ERR_INVALID_STATE; + } + + vStreamBufferDelete(spi_logic_device->tx_buffer); + spi_logic_device->tx_buffer = NULL; + + vStreamBufferDelete(spi_logic_device->rx_buffer); + spi_logic_device->rx_buffer = NULL; + + free(spi_logic_device); + spi_logic_device = NULL; + + return ESP_OK; +} diff --git a/components/esp8266/driver/spi.c b/components/esp8266/driver/spi.c index a7ce207c..a197c120 100644 --- a/components/esp8266/driver/spi.c +++ b/components/esp8266/driver/spi.c @@ -15,10 +15,9 @@ #include #include #include - #include "FreeRTOS.h" +#include "freertos/task.h" #include "freertos/semphr.h" - #include "esp8266/eagle_soc.h" #include "esp8266/spi_struct.h" #include "esp8266/pin_mux_register.h" @@ -27,21 +26,45 @@ #include "esp_attr.h" #include "esp_err.h" #include "esp_log.h" +#include "esp_heap_caps.h" #include "rom/ets_sys.h" - #include "spi.h" + #define ENTER_CRITICAL() portENTER_CRITICAL() #define EXIT_CRITICAL() portEXIT_CRITICAL() +#define SPI_CHECK(a, str, ret_val) \ + do { \ + if (!(a)) { \ + ESP_LOGE(TAG,"%s(%d): %s", __FUNCTION__, __LINE__, str); \ + return (ret_val); \ + } \ + } while(0) + +#define portYIELD_FROM_ISR() taskYIELD() + +#ifndef CONFIG_ESP8266_HSPI_HIGH_THROUGHPUT +#define ENTER_CRITICAL_HIGH_THROUGHPUT() ENTER_CRITICAL() +#define EXIT_CRITICAL_HIGH_THROUGHPUT() EXIT_CRITICAL() +#define SPI_HIGH_THROUGHPUT_ATTR +#define SPI_CHECK_HIGH_THROUGHPUT(a, str, ret_val) SPI_CHECK(a, str, ret_val) + +#else +#define SPI_HIGH_THROUGHPUT_ATTR IRAM_ATTR +#define ENTER_CRITICAL_HIGH_THROUGHPUT() do{} while(0) +#define EXIT_CRITICAL_HIGH_THROUGHPUT() do{} while(0) + +#define SPI_CHECK_HIGH_THROUGHPUT(a, str, ret_val) \ + do { \ + if (!(a)) { \ + ets_printf("%s(%d): %s", __FUNCTION__, __LINE__, str); \ + return (ret_val); \ + } \ + } while(0) +#endif static const char *TAG = "spi"; -#define SPI_CHECK(a, str, ret_val) \ - if (!(a)) { \ - ESP_LOGE(TAG,"%s(%d): %s", __FUNCTION__, __LINE__, str); \ - return (ret_val); \ - } - #define spi_intr_enable() _xt_isr_unmask(1 << ETS_SPI_INUM) #define spi_intr_disable() _xt_isr_mask(1 << ETS_SPI_INUM) #define spi_intr_register(a, b) _xt_isr_attach(ETS_SPI_INUM, (a), (b)) @@ -56,6 +79,8 @@ typedef struct { spi_interface_t interface; SemaphoreHandle_t trans_mux; spi_event_callback_t event_cb; + spi_intr_enable_t intr_enable; + uint32_t *buf; } spi_object_t; static spi_object_t *spi_object[SPI_NUM_MAX] = {NULL, NULL}; @@ -164,6 +189,8 @@ esp_err_t spi_set_intr_enable(spi_host_t host, spi_intr_enable_t *intr_enable) SPI[host]->slave.trans_done = false; EXIT_CRITICAL(); + spi_object[host]->intr_enable.val = intr_enable->val; + return ESP_OK; } @@ -209,16 +236,25 @@ esp_err_t spi_set_mode(spi_host_t host, spi_mode_t *mode) // Set to Slave mode SPI[host]->pin.slave_mode = true; SPI[host]->slave.slave_mode = true; - SPI[host]->user.usr_miso_highpart = true; + SPI[host]->user.usr_mosi_highpart = false; + SPI[host]->user.usr_miso_highpart = false; + SPI[host]->user.usr_addr = 1; // MOSI signals are delayed by APB_CLK(80MHz) mosi_delay_num cycles SPI[host]->ctrl2.mosi_delay_num = 2; SPI[host]->ctrl2.miso_delay_num = 0; + SPI[host]->slave.wr_rd_buf_en = 1; SPI[host]->slave.wr_rd_sta_en = 1; SPI[host]->slave1.status_bitlen = 31; SPI[host]->slave1.status_readback = 0; - // Put the slave's miso on the highpart, so you can only send 256bits + // Put the slave's miso on the highpart, so you can only send 512bits // In Slave mode miso, mosi length is the same - SPI[host]->slave1.buf_bitlen = 255; + SPI[host]->slave1.buf_bitlen = 511; + SPI[host]->slave1.wr_addr_bitlen = 7; + SPI[host]->slave1.rd_addr_bitlen = 7; + SPI[host]->user1.usr_addr_bitlen = 7; + SPI[host]->user1.usr_miso_bitlen = 31; + SPI[host]->user1.usr_mosi_bitlen = 31; + SPI[host]->user2.usr_command_bitlen = 7; SPI[host]->cmd.usr = 1; } @@ -231,6 +267,7 @@ esp_err_t spi_set_mode(spi_host_t host, spi_mode_t *mode) SPI[host]->ctrl.fread_dio = false; SPI[host]->ctrl.fread_qio = false; SPI[host]->ctrl.fastrd_mode = true; + SPI[host]->slave.sync_reset = 1; EXIT_CRITICAL(); return ESP_OK; @@ -396,71 +433,81 @@ esp_err_t spi_slave_get_status(spi_host_t host, uint32_t *status) return ESP_OK; } -esp_err_t spi_slave_set_status(spi_host_t host, uint32_t *status) +esp_err_t SPI_HIGH_THROUGHPUT_ATTR spi_slave_set_status(spi_host_t host, uint32_t *status) { - SPI_CHECK(host < SPI_NUM_MAX, "host num error", ESP_ERR_INVALID_ARG); - SPI_CHECK(spi_object[host], "spi has not been initialized yet", ESP_FAIL); - SPI_CHECK(SPI_SLAVE_MODE == spi_object[host]->mode, "this function must used by spi slave mode", ESP_FAIL); - SPI_CHECK(status, "parameter pointer is empty", ESP_ERR_INVALID_ARG); + SPI_CHECK_HIGH_THROUGHPUT(host < SPI_NUM_MAX, "host num error", ESP_ERR_INVALID_ARG); + SPI_CHECK_HIGH_THROUGHPUT(spi_object[host], "spi has not been initialized yet", ESP_FAIL); + SPI_CHECK_HIGH_THROUGHPUT(SPI_SLAVE_MODE == spi_object[host]->mode, "this function must used by spi slave mode", ESP_FAIL); + SPI_CHECK_HIGH_THROUGHPUT(status, "parameter pointer is empty", ESP_ERR_INVALID_ARG); - ENTER_CRITICAL(); + ENTER_CRITICAL_HIGH_THROUGHPUT(); SPI[host]->rd_status.val = *status; - EXIT_CRITICAL(); + EXIT_CRITICAL_HIGH_THROUGHPUT(); return ESP_OK; } -static esp_err_t spi_master_trans(spi_host_t host, spi_trans_t trans) +static esp_err_t SPI_HIGH_THROUGHPUT_ATTR spi_master_trans(spi_host_t host, spi_trans_t *trans) { - SPI_CHECK(trans.bits.cmd <= 16, "spi cmd must be shorter than 16 bits", ESP_ERR_INVALID_ARG); - SPI_CHECK(trans.bits.addr <= 32, "spi addr must be shorter than 32 bits", ESP_ERR_INVALID_ARG); - SPI_CHECK(trans.bits.mosi <= 512, "spi mosi must be shorter than 512 bits", ESP_ERR_INVALID_ARG); - SPI_CHECK(trans.bits.miso <= 512, "spi miso must be shorter than 512 bits", ESP_ERR_INVALID_ARG); + SPI_CHECK_HIGH_THROUGHPUT(trans->bits.cmd <= 16, "spi cmd must be shorter than 16 bits", ESP_ERR_INVALID_ARG); + SPI_CHECK_HIGH_THROUGHPUT(trans->bits.addr <= 32, "spi addr must be shorter than 32 bits", ESP_ERR_INVALID_ARG); + SPI_CHECK_HIGH_THROUGHPUT(trans->bits.mosi <= 512, "spi mosi must be shorter than 512 bits", ESP_ERR_INVALID_ARG); + SPI_CHECK_HIGH_THROUGHPUT(trans->bits.miso <= 512, "spi miso must be shorter than 512 bits", ESP_ERR_INVALID_ARG); int x, y; // Waiting for an incomplete transfer while (SPI[host]->cmd.usr); - ENTER_CRITICAL(); + ENTER_CRITICAL_HIGH_THROUGHPUT(); // Set the cmd length and transfer cmd - if (trans.bits.cmd && trans.cmd) { + if (trans->bits.cmd && trans->cmd) { SPI[host]->user.usr_command = 1; - SPI[host]->user2.usr_command_bitlen = trans.bits.cmd - 1; - SPI[host]->user2.usr_command_value = *trans.cmd; + SPI[host]->user2.usr_command_bitlen = trans->bits.cmd - 1; + SPI[host]->user2.usr_command_value = *trans->cmd; } else { SPI[host]->user.usr_command = 0; } // Set addr length and transfer addr - if (trans.bits.addr && trans.addr) { + if (trans->bits.addr && trans->addr) { SPI[host]->user.usr_addr = 1; - SPI[host]->user1.usr_addr_bitlen = trans.bits.addr - 1; - SPI[host]->addr = *trans.addr; + SPI[host]->user1.usr_addr_bitlen = trans->bits.addr - 1; + SPI[host]->addr = *trans->addr; } else { SPI[host]->user.usr_addr = 0; } // Set mosi length and transmit mosi - if (trans.bits.mosi && trans.mosi) { + if (trans->bits.mosi && trans->mosi) { SPI[host]->user.usr_mosi = 1; - SPI[host]->user1.usr_mosi_bitlen = trans.bits.mosi - 1; - - for (x = 0; x < trans.bits.mosi; x += 32) { - y = x / 32; - SPI[host]->data_buf[y] = trans.mosi[y]; + SPI[host]->user1.usr_mosi_bitlen = trans->bits.mosi - 1; + if ((uint32_t)(trans->mosi) % 4 == 0) { + for (x = 0; x < trans->bits.mosi; x += 32) { + y = x / 32; + SPI[host]->data_buf[y] = trans->mosi[y]; + } + } else { + ESP_LOGW(TAG,"Using unaligned data may reduce transmission efficiency"); + memset(spi_object[host]->buf, 0, sizeof(uint32_t) * 16); + memcpy(spi_object[host]->buf, trans->mosi, trans->bits.mosi / 8 + (trans->bits.mosi % 8) ? 1 : 0); + for (x = 0; x < trans->bits.mosi; x += 32) { + y = x / 32; + SPI[host]->data_buf[y] = spi_object[host]->buf[y]; + } } + } else { SPI[host]->user.usr_mosi = 0; } // Set the length of the miso - if (trans.bits.miso && trans.miso) { + if (trans->bits.miso && trans->miso) { SPI[host]->user.usr_miso = 1; - SPI[host]->user1.usr_miso_bitlen = trans.bits.miso - 1; + SPI[host]->user1.usr_miso_bitlen = trans->bits.miso - 1; } else { SPI[host]->user.usr_miso = 0; } @@ -474,74 +521,98 @@ static esp_err_t spi_master_trans(spi_host_t host, spi_trans_t trans) SPI[host]->cmd.usr = 1; // Receive miso data - if (trans.bits.miso && trans.miso) { + if (trans->bits.miso && trans->miso) { while (SPI[host]->cmd.usr); - for (x = 0; x < trans.bits.miso; x += 32) { - y = x / 32; - trans.miso[y] = SPI[host]->data_buf[y]; + if ((uint32_t)(trans->miso) % 4 == 0) { + for (x = 0; x < trans->bits.miso; x += 32) { + y = x / 32; + trans->miso[y] = SPI[host]->data_buf[y]; + } + } else { + ESP_LOGW(TAG,"Using unaligned data may reduce transmission efficiency"); + memset(spi_object[host]->buf, 0, sizeof(uint32_t) * 16); + for (x = 0; x < trans->bits.miso; x += 32) { + y = x / 32; + spi_object[host]->buf[y] = SPI[host]->data_buf[y]; + } + memcpy(trans->miso, spi_object[host]->buf, trans->bits.miso / 8 + (trans->bits.miso % 8) ? 1 : 0); } } - EXIT_CRITICAL(); + EXIT_CRITICAL_HIGH_THROUGHPUT(); return ESP_OK; } -static esp_err_t spi_slave_trans(spi_host_t host, spi_trans_t trans) +static esp_err_t SPI_HIGH_THROUGHPUT_ATTR spi_slave_trans(spi_host_t host, spi_trans_t *trans) { - SPI_CHECK(trans.bits.cmd >= 3 && trans.bits.cmd <= 16, "spi cmd must be longer than 3 bits and shorter than 16 bits", ESP_ERR_INVALID_ARG); - SPI_CHECK(trans.bits.addr >= 1 && trans.bits.addr <= 32, "spi addr must be longer than 1 bits and shorter than 32 bits", ESP_ERR_INVALID_ARG); - SPI_CHECK(trans.bits.miso <= 256, "spi miso must be shorter than 256 bits", ESP_ERR_INVALID_ARG); - SPI_CHECK(trans.bits.mosi <= 256, "spi mosi must be shorter than 256 bits", ESP_ERR_INVALID_ARG); + SPI_CHECK_HIGH_THROUGHPUT(trans->bits.cmd >= 3 && trans->bits.cmd <= 16, "spi cmd must be longer than 3 bits and shorter than 16 bits", ESP_ERR_INVALID_ARG); + SPI_CHECK_HIGH_THROUGHPUT(trans->bits.addr >= 1 && trans->bits.addr <= 32, "spi addr must be longer than 1 bits and shorter than 32 bits", ESP_ERR_INVALID_ARG); + SPI_CHECK_HIGH_THROUGHPUT(trans->bits.miso <= 512, "spi miso must be shorter than 512 bits", ESP_ERR_INVALID_ARG); + SPI_CHECK_HIGH_THROUGHPUT(trans->bits.mosi <= 512, "spi mosi must be shorter than 512 bits", ESP_ERR_INVALID_ARG); int x, y; - ENTER_CRITICAL(); + ENTER_CRITICAL_HIGH_THROUGHPUT(); // Set cmd length and receive cmd - SPI[host]->user2.usr_command_bitlen = trans.bits.cmd - 1; + SPI[host]->user2.usr_command_bitlen = trans->bits.cmd - 1; - if (trans.cmd) { - *trans.cmd = SPI[host]->user2.usr_command_value; + if (trans->cmd) { + *trans->cmd = SPI[host]->user2.usr_command_value; } // Set addr length and transfer addr - SPI[host]->slave1.wr_addr_bitlen = trans.bits.addr - 1; - SPI[host]->slave1.rd_addr_bitlen = trans.bits.addr - 1; + SPI[host]->slave1.wr_addr_bitlen = trans->bits.addr - 1; + SPI[host]->slave1.rd_addr_bitlen = trans->bits.addr - 1; - if (trans.addr) { - *trans.addr = SPI[host]->addr; + if (trans->addr) { + *trans->addr = SPI[host]->addr; } // Set the length of the miso and transfer the miso - if (trans.bits.miso && trans.miso) { - for (x = 0; x < trans.bits.miso; x += 32) { - y = x / 32; - SPI[host]->data_buf[y + 8] = trans.miso[y]; + if (trans->bits.miso && trans->miso) { + if ((uint32_t)(trans->miso) % 4 == 0) { + for (x = 0; x < trans->bits.miso; x += 32) { + y = x / 32; + SPI[host]->data_buf[y] = trans->miso[y]; + } + } else { + ESP_LOGW(TAG,"Using unaligned data may reduce transmission efficiency"); + memset(spi_object[host]->buf, 0, sizeof(uint32_t) * 16); + memcpy(spi_object[host]->buf, trans->miso, trans->bits.miso / 8 + (trans->bits.miso % 8) ? 1 : 0); + for (x = 0; x < trans->bits.miso; x += 32) { + y = x / 32; + SPI[host]->data_buf[y] = spi_object[host]->buf[y]; + } } } - // Call the event callback function to send a transfer start event - if (spi_object[host]->event_cb) { - spi_object[host]->event_cb(SPI_TRANS_START_EVENT, NULL); - } - // Receive mosi data - if (trans.bits.mosi && trans.mosi) { - for (x = 0; x < trans.bits.mosi; x += 32) { - y = x / 32; - trans.mosi[y] = SPI[host]->data_buf[y]; + if (trans->bits.mosi && trans->mosi) { + if ((uint32_t)(trans->mosi) % 4 == 0) { + for (x = 0; x < trans->bits.mosi; x += 32) { + y = x / 32; + trans->mosi[y] = SPI[host]->data_buf[y]; + } + } else { + ESP_LOGW(TAG,"Using unaligned data may reduce transmission efficiency"); + memset(spi_object[host]->buf, 0, sizeof(uint32_t) * 16); + for (x = 0; x < trans->bits.mosi; x += 32) { + y = x / 32; + spi_object[host]->buf[y] = SPI[host]->data_buf[y]; + } + memcpy(trans->mosi, spi_object[host]->buf, trans->bits.mosi / 8 + (trans->bits.mosi % 8) ? 1 : 0); } } - EXIT_CRITICAL(); + EXIT_CRITICAL_HIGH_THROUGHPUT(); return ESP_OK; } -static esp_err_t spi_trans_static(spi_host_t host, spi_trans_t trans) +static esp_err_t SPI_HIGH_THROUGHPUT_ATTR spi_trans_static(spi_host_t host, spi_trans_t *trans) { int ret; - if (SPI_MASTER_MODE == spi_object[host]->mode) { ret = spi_master_trans(host, trans); } else { @@ -551,17 +622,30 @@ static esp_err_t spi_trans_static(spi_host_t host, spi_trans_t trans) return ret; } -esp_err_t spi_trans(spi_host_t host, spi_trans_t trans) +esp_err_t SPI_HIGH_THROUGHPUT_ATTR spi_trans(spi_host_t host, spi_trans_t *trans) { - SPI_CHECK(host < SPI_NUM_MAX, "host num error", ESP_ERR_INVALID_ARG); - SPI_CHECK(spi_object[host], "spi has not been initialized yet", ESP_FAIL); - SPI_CHECK(trans.bits.val, "trans bits is empty", ESP_ERR_INVALID_ARG); + SPI_CHECK_HIGH_THROUGHPUT(host < SPI_NUM_MAX, "host num error", ESP_ERR_INVALID_ARG); + SPI_CHECK_HIGH_THROUGHPUT(spi_object[host], "spi has not been initialized yet", ESP_FAIL); + SPI_CHECK_HIGH_THROUGHPUT(trans->bits.val, "trans bits is empty", ESP_ERR_INVALID_ARG); int ret; - - xSemaphoreTake(spi_object[host]->trans_mux, portMAX_DELAY); - ret = spi_trans_static(host, trans); - xSemaphoreGive(spi_object[host]->trans_mux); + if (xPortInIsrContext()) { + /* In ISR Context */ + BaseType_t higher_task_woken = false; + if (xSemaphoreTakeFromISR(spi_object[host]->trans_mux, NULL) != pdTRUE) { + return ESP_FAIL; + } + ret = spi_trans_static(host, trans); + xSemaphoreGiveFromISR(spi_object[host]->trans_mux, &higher_task_woken); + if (higher_task_woken) { + portYIELD_FROM_ISR(); + } + } + else { + xSemaphoreTake(spi_object[host]->trans_mux, portMAX_DELAY); + ret = spi_trans_static(host, trans); + xSemaphoreGive(spi_object[host]->trans_mux); + } return ret; } @@ -569,7 +653,7 @@ static IRAM_ATTR void spi_intr(void *arg) { spi_host_t host; uint32_t trans_done; - + uint32_t cnt = 0; if (READ_PERI_REG(DPORT_SPI_INT_STATUS_REG) & DPORT_SPI_INT_STATUS_SPI0) { // DPORT_SPI_INT_STATUS_SPI0 trans_done = SPI0.slave.val & 0x1F; SPI0.slave.val &= ~0x3FF; @@ -577,13 +661,25 @@ static IRAM_ATTR void spi_intr(void *arg) } else if (READ_PERI_REG(DPORT_SPI_INT_STATUS_REG) & DPORT_SPI_INT_STATUS_SPI1) { // DPORT_SPI_INT_STATUS_SPI1 trans_done = SPI1.slave.val & 0x1F; SPI1.slave.val &= ~0x1F; + // Hardware issues: We need to wait for the hardware to clear the registers successfully. + while ((SPI1.slave.val & 0x1F) != 0) { + if (cnt >= 50) { + ets_printf("WARNING: waiting too much time, maybe error\r\n"); + cnt = 0; + } + SPI1.slave.val &= ~0x1F; + cnt++; + } + host = HSPI_HOST; } else { return; } if (spi_object[host]) { - if (spi_object[host]->event_cb) { + // Hardware has no interrupt flag, which can be generated by software. + trans_done &= spi_object[host]->intr_enable.val; + if (spi_object[host]->event_cb && trans_done != 0) { spi_object[host]->event_cb(SPI_TRANS_DONE_EVENT, &trans_done); } } @@ -621,7 +717,10 @@ esp_err_t spi_deinit(spi_host_t host) if (spi_object[host]->trans_mux) { vSemaphoreDelete(spi_object[host]->trans_mux); } - free(spi_object[host]); + heap_caps_free(spi_object[host]->buf); + spi_object[host]->buf = NULL; + + heap_caps_free(spi_object[host]); spi_object[host] = NULL; return ESP_OK; @@ -633,20 +732,25 @@ esp_err_t spi_init(spi_host_t host, spi_config_t *config) SPI_CHECK(host > CSPI_HOST, "CSPI_HOST can't support now", ESP_FAIL); SPI_CHECK(NULL == spi_object[host], "spi has been initialized", ESP_FAIL); - spi_object[host] = (spi_object_t *)malloc(sizeof(spi_object_t)); + spi_object[host] = (spi_object_t *)heap_caps_malloc(sizeof(spi_object_t), MALLOC_CAP_8BIT); SPI_CHECK(spi_object[host], "malloc fail", ESP_ERR_NO_MEM); spi_object[host]->trans_mux = xSemaphoreCreateMutex(); - if (NULL == spi_object[host]->trans_mux) { +#ifdef CONFIG_ESP8266_HSPI_HIGH_THROUGHPUT + spi_object[host]->buf = (uint32_t *)heap_caps_malloc(sizeof(uint32_t) * 16, MALLOC_CAP_8BIT); +#else + spi_object[host]->buf = (uint32_t *)heap_caps_malloc(sizeof(uint32_t) * 16, MALLOC_CAP_32BIT); +#endif + if (NULL == spi_object[host]->trans_mux || NULL == spi_object[host]->buf) { spi_deinit(host); - SPI_CHECK(false, "Semaphore create fail", ESP_ERR_NO_MEM); + SPI_CHECK(false, "no memory", ESP_ERR_NO_MEM); } uint16_t dummy_bitlen = 0; - spi_set_event_callback(host, &config->event_cb); spi_set_mode(host, &config->mode); spi_set_interface(host, &config->interface); spi_set_clk_div(host, &config->clk_div); spi_set_dummy(host, &dummy_bitlen); + spi_set_intr_enable(host, &config->intr_enable); spi_intr_register(spi_intr, NULL); spi_intr_enable(); @@ -654,6 +758,5 @@ esp_err_t spi_init(spi_host_t host, spi_config_t *config) if (spi_object[host]->event_cb) { spi_object[host]->event_cb(SPI_INIT_EVENT, NULL); } - return ESP_OK; -} \ No newline at end of file +} diff --git a/components/esp8266/include/driver/hspi_logic_layer.h b/components/esp8266/include/driver/hspi_logic_layer.h new file mode 100644 index 00000000..99fd1d99 --- /dev/null +++ b/components/esp8266/include/driver/hspi_logic_layer.h @@ -0,0 +1,88 @@ +// Copyright 2018-2025 Espressif Systems (Shanghai) PTE LTD +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#pragma once + +#include +#include "esp_err.h" +#include "driver/gpio.h" + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * @brief Receive SPI data + * + * @param data Data buffer to receive + * @param len Data length + * @param xTicksToWait Ticks to wait until receive data; use portMAX_DELAY to + * never time out. + * @return + * - Actual length received + */ +uint32_t hspi_slave_logic_read_data(uint8_t*data, uint32_t len, TickType_t xTicksToWait); + +/** + * @brief Send SPI data + * + * @param data Data buffer to send + * @param len Data length + * @param xTicksToWait Ticks to wait until send data; use portMAX_DELAY to + * never time out. + * @return + * - Actual length received + */ +uint32_t hspi_slave_logic_write_data(uint8_t*data, uint32_t len, TickType_t xTicksToWait); + +/** + * @brief Create a SPI device to transmit SPI data + * + * @param trigger_pin The pin used for handshake + * @param trigger_level The number of bytes that must be in the stream + * buffer before a task that is blocked on the stream buffer to wait for data is + * moved out of the blocked state. For example, if a task is blocked on a read + * of an empty stream buffer that has a trigger level of 1 then the task will be + * unblocked when a single byte is written to the buffer or the task's block + * time expires. As another example, if a task is blocked on a read of an empty + * stream buffer that has a trigger level of 10 then the task will not be + * unblocked until the stream buffer contains at least 10 bytes or the task's + * block time expires. If a reading task's block time expires before the + * trigger level is reached then the task will still receive however many bytes + * are actually available. Setting a trigger level of 0 will result in a + * trigger level of 1 being used. It is not valid to specify a trigger level + * that is greater than the buffer size. + * @param tx_buffer_size The total number of bytes the send stream buffer will be + * able to hold at any one time. + * @param rx_buffer_size The total number of bytes the receive stream buffer will be + * able to hold at any one time. + * + * @return + * - ESP_OK Success + * - ESP_ERR_NO_MEM No memory + */ +esp_err_t hspi_slave_logic_device_create(gpio_num_t trigger_pin, uint32_t trigger_level,uint32_t tx_buffer_size, uint32_t rx_buffer_size); + +/** + * @brief Delete the SPI slave bus + * + * @return + * - ESP_OK Success + * - ESP_ERR_INVALID_STATE SPI slave already deleted + */ +esp_err_t hspi_slave_logic_device_delete(void); + +#ifdef __cplusplus +} +#endif \ No newline at end of file diff --git a/components/esp8266/include/driver/spi.h b/components/esp8266/include/driver/spi.h index 932dffd9..b902caa7 100644 --- a/components/esp8266/include/driver/spi.h +++ b/components/esp8266/include/driver/spi.h @@ -36,7 +36,7 @@ extern "C" { #define SPI_BYTE_ORDER_LSB_FIRST 0 /* SPI default bus interface parameter definition */ -#define SPI_DEFAULT_INTERFACE 0x1F0 /* CS_EN:1, MISO_EN:1, MOSI_EN:1, BYTE_TX_ORDER:1, BYTE_TX_ORDER:1, BIT_RX_ORDER:0, BIT_TX_ORDER:0, CPHA:0, CPOL:0 */ +#define SPI_DEFAULT_INTERFACE 0x1C0 /* CS_EN:1, MISO_EN:1, MOSI_EN:1, BYTE_TX_ORDER:0, BYTE_TX_ORDER:0, BIT_RX_ORDER:0, BIT_TX_ORDER:0, CPHA:0, CPOL:0 */ /* SPI master default interrupt enable definition */ #define SPI_MASTER_DEFAULT_INTR_ENABLE 0x10 /* TRANS_DONE: true, WRITE_STATUS: false, READ_STATUS: false, WRITE_BUFFER: false, READ_BUFFER: false */ @@ -140,8 +140,8 @@ typedef union { typedef struct { uint16_t *cmd; /*!< SPI transmission command */ uint32_t *addr; /*!< SPI transmission address */ - uint32_t *mosi; /*!< SPI transmission MOSI buffer */ - uint32_t *miso; /*!< SPI transmission MISO buffer */ + uint32_t *mosi; /*!< SPI transmission MOSI buffer, in order to improve the transmission efficiency, it is recommended that the external incoming data is (uint32_t *) type data, do not use other type data. */ + uint32_t *miso; /*!< SPI transmission MISO buffer, in order to improve the transmission efficiency, it is recommended that the external incoming data is (uint32_t *) type data, do not use other type data. */ union { struct { uint32_t cmd: 5; /*!< SPI transmission command bits */ @@ -403,14 +403,14 @@ esp_err_t spi_slave_set_status(spi_host_t host, uint32_t *status); * - CSPI_HOST SPI0 * - HSPI_HOST SPI1 * - * @param trans Transmission parameter structure + * @param trans Pointer to transmission parameter structure * * @return * - ESP_OK Success * - ESP_ERR_INVALID_ARG Parameter error * - ESP_FAIL spi has not been initialized yet */ -esp_err_t spi_trans(spi_host_t host, spi_trans_t trans); +esp_err_t spi_trans(spi_host_t host, spi_trans_t *trans); /** * @brief Deinit the spi diff --git a/examples/peripherals/spi/README.md b/examples/peripherals/spi/README.md new file mode 100644 index 00000000..1debbf63 --- /dev/null +++ b/examples/peripherals/spi/README.md @@ -0,0 +1,272 @@ +## SPI Demo User Guide + +### Overview + +Since the ESP8266 does not have DMA, it can only transmit 64 bytes per time at most. There are two demos of ESP8266 SPI driver, normal_performance and high_performance. + +* normal_performance demo is for the normal SPI speed use case. It provides SPI slave APIs, so users can implement the ESP8266 running as SPI slave easily. But in this demo, the software schedules between task and interrupt when SPI sending/receiving data, so it is a normal speed demo. +* high_performance demo is for the high SPI speed use case. In order to rise SPI speed, ESP8266 SPI slave sends/receives data in the interrupt. It is suggested that host MCU (SPI master) also handles SPI transmission in the interrupt, otherwise the SPI transmission speed will be decreased due to software scheduling. + +Both normal_performance and high_performance demo are for the SPI slave, user can implement one program of host MCU to communicate with those two demos. + +### Hardware Connection + +| Signal | Slave | Master | +| --------- | ------ | ------ | +| SCLK | GPIO14 | GPIO14 | +| MISO | GPIO12 | GPIO12 | +| MOSI | GPIO13 | GPIO13 | +| CS | GPIO15 | GPIO15 | +| HANDSHARK | GPIO4 | GPIO4 | +| GND | GND | GND | + +**Note:** + +* To run the firmware stored in flash, the GPIO15 needs to be low level when the ESP8266 starts up. So the power-on sequence can be `Master OFF -> Slave ON -> Master ON`. + + +# Software Introduction +### SPI Transmission Command + +Master communicates with the ESP8266 SPI AT in half-duplex mode. The command that master uses to communicates with the ESP8266 SPI AT is in the following format. + +| | Command(1byte)| Address(1byte) | Data(64byte) | +| :----: | :-----------: | :---------------: | :-----------: | +| Read Data | 0x3 | 0x0 | Actual length | +| Write Data | 0x2 | 0x0 | Actual length | + +Since the ESP8266 does not have SPI DMA, it can only transmit up to 64 bytes per time. + +Except the above read/write data commands, ESP8266 also has two 32bit status registers that the SPI master can access: one can be wrote by master and read by ESP8266, the other can be read by master and wrote by ESP8266. In this case, the master can transmit the data length information with these two 32bit status registers. + +| | Command(1byte) | Data(4byte) | +| :------: | :-----------: | --------------------------- | +| Read Status | 0x4 | Length of data that master read from ESP8266| +| Write Status | 0x1 | Length of data that master transmit to ESP8266 | + +**Note: When SPI master reads/writes status, it needs not to use the address bit.** So, the SPI master needs to implement read/write status and read/write data seperately. + + +### Master Sends Data to ESP8266 + +The work flow of master transmiting data to the ESP8266 (SPI slave) is as the following figure. + +![](res/master_send.png) + +1. Since there will be a conflict if both master and slave transmit data to each other at the same time. So, before the master transmiting data, it will check if the ESP8266 is transmitting data now. If it is, then the master will wait until the ESP8266's transmission completed. +2. The master writes the data length to the status register, indicates the length of data that it is going to transmit. +3. The ESP8266 will generate a WR\_STA_DONE interrupt, and pull up the GPIO\_HANDSHAKE pin to inform the master that it has got the data length information. +4. The master can send up to 64 bytes per time. If data is longer than 64 bytes, then it needs to be divided to several subpackets to send. +5. ESP8266 will store the received data into the SPI register, and generate a WR\_BUF\_DONE interrupt, and then pull up the GPIO\_HANDSHAKE pin to inform the master that it has received the previous data and the master can start the next transmission now. +6. The master has to wait for the interrupt of GPIO\_HANDSHAKE pin, which means ESP8266 received the data successfully, to start the next transmission. +7. After sent data, the master will check the software buffer to see if there is more data wait for sending. + * If there is, then the master will repeat the procedure starts from step 2. + * If there is not, then the master needs to write 0 to the status register, indicates that data sending completed. When ESP8266 reads the status 0, the ESP8266 stops pulling up the GPIO\_HANDSHAKE pin. + +### ESP8266 Sends Data to Master + +![](res/slave_send.png) + +1. Before ESP8266 sends data, it will check if the master is sending data now. If it is, then the ESP8266 will wait until the master's transmission completed. +2. The ESP8266 copies data into a software buffer, writes the data length to the rd_status, and then pulls up the GPIO\_HANDSHAKE pin to inform the master to read data. +3. Master reads the data length from the status register, and generates a RD\_STA_DONE interrupt. +4. When ESP8266 gets the RD\_STA_DONE interrupt, it will read 64 bytes from the software buffer and write those 64 bytes to the SPI register, and then pull up the GPIO\_HANDSHAKE pin to inform the master to read data. +5. When the master gets the interrupt, it will read data through SPI. +6. ESP8266 generates RD\_BUF_DONE interrupt, and reads data (64 bytes at most) from the software buffer and write those data to the SPI register again, and then pull up the GPIO\_HANDSHAKE pin to inform the master to read data. +7. Repeat above steps until the software buffer is empty. When all data in software buffer sent, the ESP8266 will not pull up the master, but set the rd_status to be 0 instead. + +**Note:** + + * The streambuffer of ESP8266 and the ringbuffer of ESP32 are the software buffer to keep SPI data. If the SPI master is based on FreeRTOS, then it is suggested to use the FreeRTOS-provided streambuffer(which only available since FreeRTOS v10.01 and later versions). + +### HANDSHAKE Pin Introduction + +HANDSHAKE pin is to indicate the transmission completes, and it can be used to the transmission that starts by ESP8266(slave). + +1. When ESP8266 is ready to receive/send data, it will pull up the HANDSHAKE pin. Then the SPI master will read data from ESP8266 when it gets the interrupt. +2. After SPI master sent data, the master will block procedure to wait the HANDSHAKE pin interrupt. When ESP8266 read the data from SPI register, the ESP8266 will pull up the HANDSHAKE pin to inform the master to transmit next packet. + +### Test Result of SPI Communication Speed +One ESP8266 runs as the SPI master, another ESP8266 runs as the SPI slave, both of them are running in 160MHz and send 64 bytes per time. +The throughput test result of high_performance demo is as the following figure. + +| SPI Clock | master -> slave | slave -> master | +| --------- | --------------- | --------------- | +| 20M | 1.33MB/s | 1.31MB/s | +| 16M | 1.17MB/s | 1.13MB/s | +| 10M | 861KB/s | 845KB/s | + +The throughput test result of normal_performance demo is as the following figure. + +| SPI Clock | master -> slave | slave -> master | +| --------- | --------------- | --------------- | +| 20M | 645KB/s | 450KB/s | +| 16M | 606KB/s | 430KB/s | +| 10M | 510KB/s | 378KB/s | + +### Capture Packets +This is an example of SPI master sending “AT\r\n” and the ESP8266(slave) responding “AT\r\n”. The channel0 is SCLK, channel1 is MOSI, channel2 is MISO, channel3 is CS, channel4 is handshake pin. + +- SPI\_MASTER\_WRITE\_STATUS\_TO\_SLAVE + +![](res/master_send_length.png) + +Master sends command of data length, 0x01. And then the master sends the data length, 4. Handshake pin is low level in the initialization. + +- SPI\_MASTER\_WRITE\_DATA\_TO_SLAVE + +![](res/master_send_data.png) + +Master sends command of writing data, 0x02, following with 8bit address, 0x0. And then the master sends the 4bytes data. Handshake pin is high level during the communication. + +- SPI\_MASTER\_READ\_STATUS\_FROM_SLAVE + +![](res/master_recv_length.png) + +Master sends command of reading data length, 0x04. The slave will respond the data length, 4. Handshake pin is high level during the communication. + +- SPI\_MASTER\_READ\_DATA\_FROM\_SLAVE + +![](res/master_recv_data.png) + +Master sends command of reading data, 0x02, following with 8bit address, 0x0. And then the slave will respond the data that needs to be transmitted. + +## SPI demo 使用说明 + +### 简介 + +受限于 ESP8266 没有 DMA,并且每次只能传输最大 64bytes,我们使用了两个项目(normal_performance 和 high_performance)来表述 ESP8266 SPI 驱动的用法, + +其中 normal_performance 针对 SPI 速率要求不高的项目,我们封装了 SPI slave 的接口,在开发 ESP8266 作为slave 的项目时,只需要简单的接口调用即可使用,因为每次 SPI 收发均需要中断与 task 之间来回调度,因此速率较低。 + +high_performance 针对 SPI 速率要求高的场景,ESP8266 SPI slave 的收发均在中断中操作,为了尽可能提高传输速率, MCU 最好同样在中断中处理 SPI 传输,否则速率会因为频繁切换 task 而骤降。 + +两者的区别主要在 SPI slave 侧,对于 MCU 可以使用同一套程序。 + +### 硬件连接 + +- 接线: + +| Signal | Slave | Master | +| --------- | ------ | ------ | +| SCLK | GPIO14 | GPIO14 | +| MISO | GPIO12 | GPIO12 | +| MOSI | GPIO13 | GPIO13 | +| CS | GPIO15 | GPIO15 | +| HANDSHARK | GPIO4 | GPIO4 | +| GND | GND | GND | + +- 注意: + +在 ESP8266 上电后,需要保证一开机 GPIO15 处于低电平才能进入 flash 模式,所以开机顺序需要如下: + +``` +Master OFF -> Slave ON -> Master ON +``` + +# 软件介绍 + +### SPI 通信命令 + +MCU 在与 ESP8266 通信时采用半双工模式, MCU 通过使用不同的命令表示读数据或者写数据。数据格式如下所示: + +| | 命令(1byte) | 地址(1byte) | 数据长度(64byte) | +| :----: | :-----------: | ------------- | ------------------ | +| 读数据 | 0x3 | 0x0 | 实际长度 | +| 写数据 | 0x2 | 0x0 | 实际长度 | + +ESP8266 没有 SPI DMA, 所以一次最多只能传输 64 个字节。 + +除了收发数据命令之外,ESP8266 有两个 MCU 可访问的 32bit 寄存器(status), 其中一个 MCU 可写,而 ESP8266只可读,另外一个则与之相反。MCU 通过读写这两个 status 寄存器,以此实现传递数据长度信息。通信格式如下所示: + +| | 命令(1byte) | 数据长度(4byte) | +| :------: | :-----------: | --------------------------- | +| 读status | 0x4 | MCU 读取 ESP8266 传输的长度 | +| 写status | 0x1 | MCU 可写需要传输的长度 | + +需要注意的是,**MCU 读写 status 不需要使用地址位**,因此在 MCU 开发中需要将读写 status 与读写数据区分开。 + +### MCU 发送数据给 ESP8266 + +MCU 主动发送的流程如下: + +![](res/master_send.png) + +1. MCU 如果有数据需要发送,首先需要检测 ESP8266 是否正在发送数据(避免同时发送),MCU 需要等待 ESP8266 发送完毕才能传输 +2. MCU 首先需要向 status 里面写入本次需要发送的数据长度 +3. ESP8266 会产生 WR_STA_DONE 中断, ESP8266 会拉高管脚通知 MCU 已经获取到需要接收的数据长度 +4. MCU 可以一次发送最多 64bytes 的数据,如果数据超过 64bytes,需分多次发送 +5. ESP8266 接收导数据后, 会存储到 SPI 寄存器,并产生 WR_BUF_DONE 中断,ESP8266 会拉管脚通知 MCU 数据已经取出,可以继续发送 +6. MCU 必须等待 ESP8266 接收到数据产生的 GPIO 中断 (一次有效数据传输),之后才能继续传输 +7. 发送完数据之后 MCU 需要再查询发送的软件 buffer 是否还存在数据,如果存在数据,从第 2 步继续开始,否则需要额外再向 status 写入 0 , 以此来标明发送完毕, ESP8266 收到写入 status 0 后不再拉管脚 + +### ESP8266 发送数据给 MCU + +ESP8266 发送的流程与 MCU 发送的流程基本类似: + +![](res/slave_send.png) + +1. ESP8266 如果有数据需要发送,首先需要检测 MCU 是否正在发送数据,ESP8266 需要等待 MCU 发送完毕 +2. ESP8266 会将数据全部拷贝到软件 buffer 中,并向 rd_status 中写入本次需要发送的数据长度,然后拉管脚通知 MCU 取数据 +3. MCU 读取 status 中的数据长度信息,读取 status 会产生 RD_STA_DONE 中断 +4. ESP8266 在 RD_STA_DONE 中断中会读取软件 buffer 中的 64个字节到 SPI 寄存器,拉管脚通知 MCU 读取数据 +5. MCU 在接收到 GPIO 中断后会发起一次 SPI 读数据传输 +6. ESP8266 产生 RD_BUF_DONE 中断,并从软件 buffer 中再读取最大 64 个字节的数据填充到 SPI 寄存器,并拉管脚通知 MCU 读取 +7. 如此循环,直到软件 buffer 中不再有数据,此时 ESP8266 不会再拉管脚通知 MCU,并且 ESP8266 会把 rd_status 置为0,读取完成 + +*备注:* ESP8266 所使用的 streambuffer 以及 ESP32 所使用的 ringbuffer 功能一致,用于数据的缓冲,对于 FreeRTOS 的 MCU,建议使用 FreeRTOS 自带的 streambuffer (需要 FreeRTOS 系统版本在 10.01及以上) + +### Handshake 线在 SPI-AT 中的作用 + +Handshake 线在 SPI 相互通信中需要保证传输数据完成,并担任 ESP8266 主动发起传输的任务,其主要作用体现在如下两点: + +1. ESP8266 在准备好接收/发送数据时将此针脚拉高,此时 MCU 会产生一个 GPIO 中断信号,MCU 在接收到中断信号后会读取 ESP8266 中的数据。 +2. MCU 在发送完数据之后需要堵塞等待 GPIO 中断信号,ESP8266 在将 SPI 寄存器中的数据取出后会拉高管脚,从而 MCU 会产生 GPIO 中断,之后 MCU 可以继续传输 + +### 测试速率 + +一个 ESP8266 作为 MCU 充当 SPI master, 另一个 ESP8266 作为 SPI slave,两者 CPU 同时跑在 160M , 每次发送 64bytes。 + +使用 high performance demo, 测试吞吐率结果如下: + +| SPI 时钟 | master -> slave | slave -> master | +| --------- | --------------- | --------------- | +| 20M | 1.33MB/s | 1.31MB/s | +| 16M | 1.17MB/s | 1.13MB/s | +| 10M | 861KB/s | 845KB/s | + +使用 normal performance demo, 测试吞吐率结果如下: + +| SPI 时钟 | master -> slave | slave -> master | +| --------- | --------------- | --------------- | +| 20M | 645KB/s | 450KB/s | +| 16M | 606KB/s | 430KB/s | +| 10M | 510KB/s | 378KB/s | + +### 抓包 + +抓包以 MCU 发送 AT\r\n , ESP8266 返回 AT\r\n 为例, 其中 channel0 为 SCLK , channel1 为 MOSI,channel2 为 MISO,channel3 为 CS,channel4 为 handshake 线 + +- SPI_MASTER_WRITE_STATUS_TO_SLAVE + +![](res/master_send_length.png) + +MCU 发送长度命令 0x01,后面跟着需要发送的数据长度 4,第一次发送时 handshake 为初始低电平。 + +- SPI_MASTER_WRITE_DATA_TO_SLAVE + +![](res/master_send_data.png) + +MCU 发送数据命令 0x02, 后跟 8bit 长的地址 0x0,后面跟上实际数据长度 4 字节的数据,注意在发送过程中 handshake 是一直拉高的。 + +- SPI_MASTER_READ_STATUS_FROM_SLAVE + +![](res/master_recv_length.png) + +发送读取长度命令 0x04, slave 会返回长度信息 4, 在读取过程中 handshake 也是一直拉高的。 + +- SPI_MASTER_READ_DATA_FROM_SLAVE + +![](res/master_recv_data.png) + +发送读取数据命令 0x03,后跟 8bit 长的地址 0x0, 之后就是 slave 返回的实际数据 diff --git a/examples/peripherals/spi_master/CMakeLists.txt b/examples/peripherals/spi/high_performance/spi_master/CMakeLists.txt similarity index 100% rename from examples/peripherals/spi_master/CMakeLists.txt rename to examples/peripherals/spi/high_performance/spi_master/CMakeLists.txt diff --git a/examples/peripherals/spi_master/Makefile b/examples/peripherals/spi/high_performance/spi_master/Makefile similarity index 100% rename from examples/peripherals/spi_master/Makefile rename to examples/peripherals/spi/high_performance/spi_master/Makefile diff --git a/examples/peripherals/spi_master/main/CMakeLists.txt b/examples/peripherals/spi/high_performance/spi_master/main/CMakeLists.txt similarity index 100% rename from examples/peripherals/spi_master/main/CMakeLists.txt rename to examples/peripherals/spi/high_performance/spi_master/main/CMakeLists.txt diff --git a/examples/peripherals/spi_master/main/component.mk b/examples/peripherals/spi/high_performance/spi_master/main/component.mk similarity index 100% rename from examples/peripherals/spi_master/main/component.mk rename to examples/peripherals/spi/high_performance/spi_master/main/component.mk diff --git a/examples/peripherals/spi/high_performance/spi_master/main/spi_master_example_main.c b/examples/peripherals/spi/high_performance/spi_master/main/spi_master_example_main.c new file mode 100644 index 00000000..7b2d5329 --- /dev/null +++ b/examples/peripherals/spi/high_performance/spi_master/main/spi_master_example_main.c @@ -0,0 +1,331 @@ +/* spi_master example + + This example code is in the Public Domain (or CC0 licensed, at your option.) + + Unless required by applicable law or agreed to in writing, this + software is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR + CONDITIONS OF ANY KIND, either express or implied. +*/ + +#include +#include +#include +#include "freertos/FreeRTOS.h" +#include "freertos/task.h" +#include "freertos/semphr.h" +#include "freertos/stream_buffer.h" + +#include "esp8266/spi_struct.h" +#include "esp8266/gpio_struct.h" +#include "esp_system.h" +#include "esp_log.h" + +#include "driver/gpio.h" +#include "driver/spi.h" + +#include "time.h" + +static const char* TAG = "spi_master_example"; + +#define SPI_MASTER_HANDSHARK_GPIO 4 +#define SPI_MASTER_HANDSHARK_SEL (1ULL< 0) { + ESP_EARLY_LOGD(TAG, "Receive data len: %d\n", transmit_len); + intr_trans_mode = SPI_READ; + return; + } else { + ESP_EARLY_LOGE(TAG, "Nothing to do"); + return; + } + } + + read_len = transmit_len > 64 ? 64 : transmit_len; + + // SPI slave have some data want to transmit, read it + if (intr_trans_mode == SPI_READ) { + if (xStreamBufferSpacesAvailable(spi_master_recv_ring_buf) >= 64) { // Stream buffer not full, can be read agian + spi_master_transmit(SPI_READ, transmit_data); + recv_actual_len = xStreamBufferSendFromISR(spi_master_recv_ring_buf, (void*) transmit_data, read_len, &xHigherPriorityTaskWoken); + assert(recv_actual_len == read_len); + transmit_len -= read_len; + + if (transmit_len == 0) { + intr_trans_mode = SPI_NULL; + + /* When SPI slave sending data , maybe MCU also have some date wait for send */ + if (xStreamBufferIsEmpty(spi_master_send_ring_buf) == pdFALSE) { + GPIO.status_w1ts |= BIT(SPI_MASTER_HANDSHARK_GPIO); // Manual generate GPIO interrupts + } + } + } else { // stream buffer full, wait to be tacken out + wait_recv_data = true; + } + + + // MCU want to send data to ESP8266 + } else if (intr_trans_mode == SPI_WRITE) { + if (read_len > 0) { + recv_actual_len = xStreamBufferReceiveFromISR(spi_master_send_ring_buf, + (void*)transmit_data, + read_len, + &xHigherPriorityTaskWoken); + if (recv_actual_len != read_len) { + ESP_EARLY_LOGE(TAG, "Expect to send %d bytes, but only %d bytes", read_len, recv_actual_len); + return; + } + + spi_master_transmit(SPI_WRITE, transmit_data); + transmit_len -= read_len; + } else { + intr_trans_mode = SPI_NULL; + + if (xStreamBufferIsEmpty(spi_master_send_ring_buf) == pdFALSE) { + GPIO.status_w1ts |= BIT(SPI_MASTER_HANDSHARK_GPIO); // Manual generate GPIO interrupts + } else { + // if ring buffer is empty, send status=0 tell slave send done + spi_master_send_length(0); + } + } + + } + + if (xHigherPriorityTaskWoken == pdTRUE) { + taskYIELD(); + } +} + +#ifdef ESP_HSPI_MASTER_SEND +static void IRAM_ATTR spi_master_write_slave_task(void* arg) +{ +#define TEST_SEND_BUFFER_LEN 2048 + time_t start; + time_t end; + uint32_t total_len = 0; + uint8_t* buf = malloc(TEST_SEND_BUFFER_LEN); + memset(buf, 0x33, TEST_SEND_BUFFER_LEN); + vTaskDelay(5000 / portTICK_RATE_MS); + printf(" Test send\r\n"); + start = time(NULL); + + while (1) { + size_t xBytesSent = xStreamBufferSend(spi_master_send_ring_buf, (void*) buf, TEST_SEND_BUFFER_LEN, portMAX_DELAY); + + if (xBytesSent != TEST_SEND_BUFFER_LEN) { + ESP_LOGE(TAG, "Send error, len:%d", xBytesSent); + break; + } + + portENTER_CRITICAL(); + + if (intr_trans_mode == SPI_NULL) { + ESP_LOGI(TAG, "Manual generate GPIO interrupts"); + GPIO.status_w1ts |= BIT(SPI_MASTER_HANDSHARK_GPIO); + } + + portEXIT_CRITICAL(); + total_len += TEST_SEND_BUFFER_LEN; + + if (total_len > 10 * 1024 * 1024) { + end = time(NULL); + printf("send done, total len: %d, time: %lds\n", total_len, (end - start)); + break; + } + } + + vTaskDelete(NULL); +} +#else +uint32_t read_count = 0; + +static void spi_master_count_task(void* arg) +{ + uint32_t tmp_count = 0; + + while (1) { + printf("recv_count: %d , speed: %dB/s\n", read_count, ((read_count - tmp_count) / 2)); + tmp_count = read_count; + vTaskDelay(2000 / portTICK_RATE_MS); + } +} + +static void IRAM_ATTR spi_master_read_slave_task(void* arg) +{ + size_t xReceivedBytes; + uint8_t read_data[1024 + 1]; + + while (1) { + xReceivedBytes = xStreamBufferReceive(spi_master_recv_ring_buf, read_data, 1024, 2000 / portTICK_RATE_MS); + + if (xReceivedBytes != 0) { + for (int i = 0; i < xReceivedBytes; i++) { + if (read_data[i] != 0x44) { + printf("receive error data: %x\n", read_data[i]); + } + } + +#if 0 + read_data[xReceivedBytes] = '\0'; + printf("%s", read_data); + fflush(stdout); //Force to print even if have not '\n' +#else + read_count += xReceivedBytes; +#endif + } + // steam buffer full + if (wait_recv_data) { + if (xStreamBufferBytesAvailable(spi_master_recv_ring_buf) > 64) { + wait_recv_data = false; + GPIO.status_w1ts |= BIT(SPI_MASTER_HANDSHARK_GPIO); // Manual generate GPIO interrupts + } + } + } +} +#endif + +void app_main(void) +{ + spi_master_send_ring_buf = xStreamBufferCreate(SPI_BUFFER_MAX_SIZE, 1024); + spi_master_recv_ring_buf = xStreamBufferCreate(SPI_BUFFER_MAX_SIZE, 1); + + ESP_LOGI(TAG, "init gpio"); + gpio_config_t io_conf; + io_conf.intr_type = GPIO_INTR_POSEDGE; + io_conf.mode = GPIO_MODE_INPUT; + io_conf.pin_bit_mask = SPI_MASTER_HANDSHARK_SEL; + io_conf.pull_down_en = 0; + io_conf.pull_up_en = 0; + gpio_config(&io_conf); + + gpio_install_isr_service(0); + gpio_isr_handler_add(SPI_MASTER_HANDSHARK_GPIO, gpio_isr_handler, (void*) SPI_MASTER_HANDSHARK_GPIO); + + ESP_LOGI(TAG, "init spi"); + spi_config_t spi_config; + // Load default interface parameters + // CS_EN:1, MISO_EN:1, MOSI_EN:1, BYTE_TX_ORDER:1, BYTE_TX_ORDER:1, BIT_RX_ORDER:0, BIT_TX_ORDER:0, CPHA:0, CPOL:0 + spi_config.interface.val = SPI_DEFAULT_INTERFACE; + + // Load default interrupt enable + // TRANS_DONE: true, WRITE_STATUS: false, READ_STATUS: false, WRITE_BUFFER: false, READ_BUFFER: false + spi_config.intr_enable.val = SPI_MASTER_DEFAULT_INTR_ENABLE; + // Set SPI to master mode + // ESP8266 Only support half-duplex + spi_config.mode = SPI_MASTER_MODE; + // Set the SPI clock frequency division factor + spi_config.clk_div = SPI_20MHz_DIV; + // Register SPI event callback function + spi_config.event_cb = NULL; + spi_init(HSPI_HOST, &spi_config); + +#ifdef ESP_HSPI_MASTER_SEND + // create spi_master_write_slave_task + xTaskCreate(spi_master_write_slave_task, "spi_master_write_slave_task", 2048, NULL, 6, NULL); +#else + // create spi_master_read_slave_task + xTaskCreate(spi_master_read_slave_task, "spi_master_read_slave_task", 2048, NULL, 5, NULL); + xTaskCreate(spi_master_count_task, "spi_master_count_task", 2048, NULL, 4, NULL); +#endif +} + + diff --git a/examples/peripherals/spi/high_performance/spi_master/sdkconfig.defaults b/examples/peripherals/spi/high_performance/spi_master/sdkconfig.defaults new file mode 100644 index 00000000..1e339565 --- /dev/null +++ b/examples/peripherals/spi/high_performance/spi_master/sdkconfig.defaults @@ -0,0 +1,7 @@ + +CONFIG_ESP8266_HSPI_HIGH_THROUGHPUT=y + +CONFIG_ESP8266_DEFAULT_CPU_FREQ_160=y +CONFIG_ESP8266_DEFAULT_CPU_FREQ_MHZ=160 + +CONFIG_FREERTOS_CODE_LINK_TO_IRAM=y diff --git a/examples/peripherals/spi_slave/CMakeLists.txt b/examples/peripherals/spi/high_performance/spi_slave/CMakeLists.txt similarity index 100% rename from examples/peripherals/spi_slave/CMakeLists.txt rename to examples/peripherals/spi/high_performance/spi_slave/CMakeLists.txt diff --git a/examples/peripherals/spi_slave/Makefile b/examples/peripherals/spi/high_performance/spi_slave/Makefile similarity index 100% rename from examples/peripherals/spi_slave/Makefile rename to examples/peripherals/spi/high_performance/spi_slave/Makefile diff --git a/examples/peripherals/spi_slave/main/CMakeLists.txt b/examples/peripherals/spi/high_performance/spi_slave/main/CMakeLists.txt similarity index 100% rename from examples/peripherals/spi_slave/main/CMakeLists.txt rename to examples/peripherals/spi/high_performance/spi_slave/main/CMakeLists.txt diff --git a/examples/peripherals/spi/high_performance/spi_slave/main/component.mk b/examples/peripherals/spi/high_performance/spi_slave/main/component.mk new file mode 100644 index 00000000..2fb1bbe5 --- /dev/null +++ b/examples/peripherals/spi/high_performance/spi_slave/main/component.mk @@ -0,0 +1,4 @@ +# +# Main Makefile. This is basically the same as a component makefile. +# + diff --git a/examples/peripherals/spi/high_performance/spi_slave/main/spi_slave_example_main.c b/examples/peripherals/spi/high_performance/spi_slave/main/spi_slave_example_main.c new file mode 100644 index 00000000..f5e21fb2 --- /dev/null +++ b/examples/peripherals/spi/high_performance/spi_slave/main/spi_slave_example_main.c @@ -0,0 +1,283 @@ +/* spi_slave example + + This example code is in the Public Domain (or CC0 licensed, at your option.) + + Unless required by applicable law or agreed to in writing, this + software is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR + CONDITIONS OF ANY KIND, either express or implied. +*/ +#include +#include +#include +#include "freertos/FreeRTOS.h" +#include "freertos/task.h" +#include "freertos/semphr.h" +#include "freertos/event_groups.h" +#include "freertos/stream_buffer.h" +#include "ringbuf.h" + +#include "esp8266/spi_struct.h" +#include "esp8266/gpio_struct.h" +#include "esp_system.h" +#include "esp_log.h" + +#include "driver/gpio.h" +#include "driver/spi.h" + +static const char* TAG = "spi_slave_example"; + +#define SPI_SLAVE_HANDSHARK_GPIO 4 +#define SPI_SLAVE_HANDSHARK_SEL (1ULL< master data + if (total_send_len == 0) { + sending_flag = false; + total_send_len = xStreamBufferBytesAvailable(spi_slave_tx_ring_buf); + + if (total_send_len > 0) { // Have some data send to MCU + spi_slave_set_status(HSPI_HOST, (uint32_t*)&total_send_len); + sending_flag = true; + trigger_flag = true; + } + } else { // Have some data send to MCU + memset(&trans, 0x0, sizeof(trans)); + trans.cmd = &cmd; + trans.addr = NULL; + trans.bits.val = 0; + trans.bits.cmd = 8 * 1; + trans.bits.addr = 8 * 1; + trans.bits.mosi = 0; + trans.miso = data; + trans.bits.miso = xStreamBufferReceiveFromISR(spi_slave_tx_ring_buf, data, 64, &xHigherPriorityTaskWoken); // send max 32bytes + + if (trans.bits.miso != 0) { + total_send_len -= trans.bits.miso; + trans.bits.miso <<= 3; + spi_trans(HSPI_HOST, &trans); + trigger_flag = true;; + } + } + } + + if (trans_done & SPI_SLV_WR_BUF_DONE) { // master -> slave data + uint32_t len = total_recv_len; + + if (len > 64) { // only send max 32bytes one time + len = 64; + } + + if (len > 0) { + for (x = 0; x < 16; x++) { + data[x] = SPI1.data_buf[x]; + } + + xStreamBufferSendFromISR(spi_slave_rx_ring_buf, (void*) data, len, &xHigherPriorityTaskWoken); + total_recv_len -= len; + } + + if (xStreamBufferSpacesAvailable(spi_slave_rx_ring_buf) >= 64) { // Stream buffer not full, can be read agian + trigger_flag = true; + } else { + wait_recv_data = true; + } + } + + if (trans_done & SPI_SLV_WR_STA_DONE) { // master -> slave status len + spi_slave_get_status(HSPI_HOST, &status); + total_recv_len = status; + uint32_t tx_size = xStreamBufferBytesAvailable(spi_slave_tx_ring_buf); + + if (total_recv_len > 0) { + trigger_flag = true; + } else if (tx_size > 0) { // SPI send done and ESP8266 send buffer have data + if (sending_flag == false) { + spi_slave_set_status(HSPI_HOST, &tx_size); + } + + trigger_flag = true; + } + } + + if (trans_done & SPI_SLV_RD_STA_DONE) { // Slave -> Master status len + memset(&trans, 0x0, sizeof(trans)); + trans.cmd = &cmd; + trans.addr = NULL; + trans.bits.val = 0; + trans.bits.cmd = 8 * 1; + trans.bits.addr = 8 * 1; + trans.bits.mosi = 0; + trans.miso = data; + trans.bits.miso = xStreamBufferReceiveFromISR(spi_slave_tx_ring_buf, data, 64, &xHigherPriorityTaskWoken); + + if (trans.bits.miso != 0) { + total_send_len -= trans.bits.miso; + trans.bits.miso <<= 3; + spi_trans(HSPI_HOST, &trans); + trigger_flag = true; + } + } + + if (trigger_flag) { + gpio_set_level(SPI_SLAVE_HANDSHARK_GPIO, 1); + } + + if (xHigherPriorityTaskWoken == pdTRUE) { + taskYIELD(); + } + } + break; + + case SPI_DEINIT_EVENT: { + + } + break; + } + +} + +#ifdef ESP_SPI_SLAVE_RECV +uint32_t read_count = 0; + +static void spi_slave_count_task(void* arg) +{ + uint32_t tmp_count = 0; + + while (1) { + printf("recv_count: %d , speed: %dB/s\n", read_count, ((read_count - tmp_count) / 2)); + tmp_count = read_count; + vTaskDelay(2000 / portTICK_RATE_MS); + } +} + +static void IRAM_ATTR spi_slave_read_master_task(void* arg) +{ + uint8_t read_data[SPI_READ_BUFFER_MAX_SIZE + 1]; + size_t xReceivedBytes; + + while (1) { + xReceivedBytes = xStreamBufferReceive(spi_slave_rx_ring_buf, read_data, SPI_READ_BUFFER_MAX_SIZE, 2000 / portTICK_RATE_MS); + + if (xReceivedBytes != 0) { + for (int i = 0; i < xReceivedBytes; i++) { + if (read_data[i] != 0x33) { + printf("receive error data: %x\n", read_data[i]); + } + } + + read_count += xReceivedBytes; + memset(read_data, 0x0, xReceivedBytes); + + // steam buffer full + if (wait_recv_data) { + if (xStreamBufferBytesAvailable(spi_slave_rx_ring_buf) > 64) { + gpio_set_level(SPI_SLAVE_HANDSHARK_GPIO, 1); + wait_recv_data = false; + } + } + } + } +} +#else +#include "time.h" +static void IRAM_ATTR spi_slave_write_master_task(void* arg) +{ + static uint8_t write_data[SPI_WRITE_BUFFER_MAX_SIZE]; + memset(write_data, 0x44, SPI_WRITE_BUFFER_MAX_SIZE); + vTaskDelay(5000 / portTICK_RATE_MS); + printf("Test send\r\n"); + time_t start = time(NULL); + + while (1) { + total_tx_count += xStreamBufferSend(spi_slave_tx_ring_buf, write_data, SPI_WRITE_BUFFER_MAX_SIZE, portMAX_DELAY); + portENTER_CRITICAL(); + + if (sending_flag == false) { + total_send_len = xStreamBufferBytesAvailable(spi_slave_tx_ring_buf); + spi_slave_set_status(HSPI_HOST, (uint32_t*)&total_send_len); + sending_flag = true; + gpio_set_level(SPI_SLAVE_HANDSHARK_GPIO, 1); + } + + portEXIT_CRITICAL(); + + if (total_tx_count >= 20 * 1024 * 1024) { + printf("tx done; %d bytes, time : %ld\r\n", total_tx_count, time(NULL) - start); + + for (;;) { + vTaskDelay(100); + } + } + + } +} +#endif + +void app_main(void) +{ + spi_slave_tx_ring_buf = xStreamBufferCreate(4096, 1); + spi_slave_rx_ring_buf = xStreamBufferCreate(4096, 1024); + + ESP_LOGI(TAG, "init gpio"); + gpio_config_t io_conf; + io_conf.intr_type = GPIO_INTR_DISABLE; + io_conf.mode = GPIO_MODE_OUTPUT; + io_conf.pin_bit_mask = SPI_SLAVE_HANDSHARK_SEL; + io_conf.pull_down_en = 0; + io_conf.pull_up_en = 0; + gpio_config(&io_conf); + gpio_set_level(SPI_SLAVE_HANDSHARK_GPIO, 0); + + ESP_LOGI(TAG, "init spi"); + + spi_config_t spi_config; + // Load default interface parameters + // CS_EN:1, MISO_EN:1, MOSI_EN:1, BYTE_TX_ORDER:1, BYTE_TX_ORDER:1, BIT_RX_ORDER:0, BIT_TX_ORDER:0, CPHA:0, CPOL:0 + spi_config.interface.val = SPI_DEFAULT_INTERFACE; + + // Load default interrupt enable + // TRANS_DONE: false, WRITE_STATUS: true, READ_STATUS: true, WRITE_BUFFER: true, READ_BUFFER: ture + spi_config.intr_enable.val = SPI_SLAVE_DEFAULT_INTR_ENABLE; + // Set SPI to slave mode + spi_config.mode = SPI_SLAVE_MODE; + // Register SPI event callback function + spi_config.event_cb = spi_event_callback; + spi_init(HSPI_HOST, &spi_config); + +#ifdef ESP_SPI_SLAVE_RECV + // create spi_slave_read_master_task + xTaskCreate(spi_slave_read_master_task, "spi_slave_read_master_task", 2048, NULL, 6, NULL); + xTaskCreate(spi_slave_count_task, "spi_slave_count_task", 2048, NULL, 3, NULL); +#else + // create spi_slave_write_master_task + xTaskCreate(spi_slave_write_master_task, "spi_slave_write_master_task", 2048, NULL, 2, NULL); +#endif +} diff --git a/examples/peripherals/spi/high_performance/spi_slave/sdkconfig.defaults b/examples/peripherals/spi/high_performance/spi_slave/sdkconfig.defaults new file mode 100644 index 00000000..248e2b18 --- /dev/null +++ b/examples/peripherals/spi/high_performance/spi_slave/sdkconfig.defaults @@ -0,0 +1,5 @@ + +CONFIG_ESP8266_HSPI_HIGH_THROUGHPUT=y + +CONFIG_ESP8266_DEFAULT_CPU_FREQ_160=y +CONFIG_ESP8266_DEFAULT_CPU_FREQ_MHZ=160 diff --git a/examples/peripherals/spi/normal_performance/spi_master/CMakeLists.txt b/examples/peripherals/spi/normal_performance/spi_master/CMakeLists.txt new file mode 100644 index 00000000..254fb8b0 --- /dev/null +++ b/examples/peripherals/spi/normal_performance/spi_master/CMakeLists.txt @@ -0,0 +1,6 @@ +# The following four lines of boilerplate have to be in your project's CMakeLists +# in this exact order for cmake to work correctly +cmake_minimum_required(VERSION 3.5) + +include($ENV{IDF_PATH}/tools/cmake/project.cmake) +project(spi_master) diff --git a/examples/peripherals/spi/normal_performance/spi_master/Makefile b/examples/peripherals/spi/normal_performance/spi_master/Makefile new file mode 100644 index 00000000..7ca171bb --- /dev/null +++ b/examples/peripherals/spi/normal_performance/spi_master/Makefile @@ -0,0 +1,9 @@ +# +# This is a project Makefile. It is assumed the directory this Makefile resides in is a +# project subdirectory. +# + +PROJECT_NAME := spi_master + +include $(IDF_PATH)/make/project.mk + diff --git a/examples/peripherals/spi/normal_performance/spi_master/main/CMakeLists.txt b/examples/peripherals/spi/normal_performance/spi_master/main/CMakeLists.txt new file mode 100644 index 00000000..d55df5d0 --- /dev/null +++ b/examples/peripherals/spi/normal_performance/spi_master/main/CMakeLists.txt @@ -0,0 +1,3 @@ +set(COMPONENT_SRCS "spi_master_example_main.c") + +register_component() diff --git a/examples/peripherals/spi_slave/main/component.mk b/examples/peripherals/spi/normal_performance/spi_master/main/component.mk similarity index 100% rename from examples/peripherals/spi_slave/main/component.mk rename to examples/peripherals/spi/normal_performance/spi_master/main/component.mk diff --git a/examples/peripherals/spi/normal_performance/spi_master/main/spi_master_example_main.c b/examples/peripherals/spi/normal_performance/spi_master/main/spi_master_example_main.c new file mode 100644 index 00000000..42a6ff12 --- /dev/null +++ b/examples/peripherals/spi/normal_performance/spi_master/main/spi_master_example_main.c @@ -0,0 +1,251 @@ +/* spi_master example + + This example code is in the Public Domain (or CC0 licensed, at your option.) + + Unless required by applicable law or agreed to in writing, this + software is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR + CONDITIONS OF ANY KIND, either express or implied. +*/ + +#include +#include +#include +#include "freertos/FreeRTOS.h" +#include "freertos/task.h" +#include "freertos/semphr.h" +#include "ringbuf.h" + +#include "esp8266/spi_struct.h" +#include "esp8266/gpio_struct.h" +#include "esp_system.h" +#include "esp_log.h" + +#include "driver/gpio.h" +#include "driver/spi.h" + +static const char *TAG = "spi_master_example"; + +#define SPI_MASTER_HANDSHARK_GPIO 4 +#define SPI_MASTER_HANDSHARK_SEL (1ULL< 16) { + ESP_LOGE(TAG, "ESP8266 only support transmit 64bytes(16 * sizeof(uint32_t)) one time"); + return; + } + + memset(&trans, 0x0, sizeof(trans)); + trans.bits.val = 0; // clear all bit + + if (trans_mode == SPI_SEND) { + cmd = SPI_MASTER_WRITE_DATA_TO_SLAVE_CMD; + trans.bits.mosi = len * 32; // One time transmit only support 64bytes + trans.mosi = data; + } else { + cmd = SPI_MASTER_READ_DATA_FROM_SLAVE_CMD; + trans.bits.miso = len * 32; + trans.miso = data; + } + + trans.bits.cmd = 8 * 1; + trans.bits.addr = 8 * 1; // transmit data will use 8bit address + trans.cmd = &cmd; + trans.addr = &addr; + + spi_trans(HSPI_HOST, &trans); +} + +#ifdef ESP_SPI_MASTER_TEST_SEND + +/* SPI master send length, format: 8bit command(value:1) + 32bit status length */ +static void IRAM_ATTR spi_master_send_length(uint32_t len) +{ + spi_trans_t trans; + uint16_t cmd = SPI_MASTER_WRITE_STATUS_TO_SLAVE_CMD; + memset(&trans, 0x0, sizeof(trans)); + trans.bits.val = 0; + trans.bits.cmd = 8 * 1; + trans.bits.addr = 0; // transmit status do not use address bit + trans.bits.mosi = 8 * 4; // status length is 32bit + trans.cmd = &cmd; + trans.addr = NULL; + trans.mosi = &len; + spi_trans(HSPI_HOST, &trans); +} + +static void IRAM_ATTR spi_master_write_slave_task(void *arg) +{ + /* In order to improve the transmission efficiency, it is recommended that the external + incoming data is (uint32_t *) type data, do not use other type data. */ + static uint32_t write_data[SPI_WRITE_BUFFER_MAX_SIZE/4]; + uint32_t total_len = 0; + uint32_t send_len = 0; + + for (uint32_t loop = 0; loop < sizeof(write_data)/sizeof(write_data[0]);loop++) { + write_data[loop] = 0x34343434; + } + + xSemaphoreTake(semphor, 0); + vTaskDelay(5000 / portTICK_RATE_MS); + ESP_LOGI(TAG, "Start test send data"); + while (1) { + + send_len = sizeof(write_data); + spi_master_send_length(send_len); + + // wait ESP8266 received the length + xSemaphoreTake(semphor, portMAX_DELAY); + + for (uint32_t loop = 0; loop < (send_len + 63)/64; loop++) { + // transmit data, ESP8266 only transmit 64bytes one time + spi_master_transmit(SPI_SEND, write_data + (loop * 16), 64 /sizeof(uint32_t)); + xSemaphoreTake(semphor, portMAX_DELAY); + } + // send 0 to clear send length, and tell Slave send done + spi_master_send_length(0); + + total_len += send_len; + + if (total_len >= (10*1024*1024)) { + ESP_LOGI(TAG, "total_len=%d\r\n", total_len); + for (;;) { + vTaskDelay(1000); + } + } + } +} +#else + +/* SPI master revecive length, format: 8bit command(value:4) + 32bit status length */ +static uint32_t IRAM_ATTR spi_master_get_length(void) +{ + spi_trans_t trans; + uint32_t len = 0; + uint16_t cmd = SPI_MASTER_READ_STATUS_FROM_SLAVE_CMD; + memset(&trans, 0x0, sizeof(trans)); + trans.bits.val = 0; + trans.cmd = &cmd; + trans.miso = &len; + trans.addr = NULL; + trans.bits.cmd = 8 * 1; + trans.bits.miso = 8 * 4; + spi_trans(HSPI_HOST, &trans); + return len; +} + +uint32_t read_count = 0; +static void spi_master_count_task(void* arg) +{ + uint32_t tmp_count = 0; + while(1){ + printf("recv_count: %d , speed: %dB/s\n", read_count, ((read_count - tmp_count)/2)); + tmp_count = read_count; + vTaskDelay(2000 / portTICK_RATE_MS); + } +} + +static void IRAM_ATTR spi_master_read_slave_task(void *arg) +{ + uint32_t read_data[16]; + uint32_t read_len = 0; + uint32_t read_time = 0; + + while (1) { + xSemaphoreTake(semphor, portMAX_DELAY); + + read_len = spi_master_get_length(); + ESP_LOGD(TAG, "read len: %d\n", read_len); + xSemaphoreTake(semphor, portMAX_DELAY); + read_count += read_len; + + if (read_len > 0) { + read_time = (read_len + 63) / 64; // read_len is the total bytes, every time send 32bytes, so send time will be divided by 32 + while(read_time > 0) { + spi_master_transmit(SPI_RECV, read_data, 64 /sizeof(uint32_t)); + for (int x = 0; x < 16; x++) { + if (read_data[x] != 0xa3a3a3a3) { + ESP_LOGE(TAG, "error 0x%02x,%d\r\n", read_data[x], x); + } + } + read_time--; + if(read_time != 0) { + xSemaphoreTake(semphor, portMAX_DELAY); + } + } + + } + } +} +#endif + +void app_main(void) +{ + semphor = xSemaphoreCreateBinary(); + + ESP_LOGI(TAG, "init gpio"); + gpio_config_t io_conf; + io_conf.intr_type = GPIO_INTR_POSEDGE; + io_conf.mode = GPIO_MODE_INPUT; + io_conf.pin_bit_mask = SPI_MASTER_HANDSHARK_SEL; + io_conf.pull_down_en = 0; + io_conf.pull_up_en = 0; + gpio_config(&io_conf); + + gpio_install_isr_service(0); + gpio_isr_handler_add(SPI_MASTER_HANDSHARK_GPIO, gpio_isr_handler, (void *) SPI_MASTER_HANDSHARK_GPIO); + + ESP_LOGI(TAG, "init spi"); + spi_config_t spi_config; + // Load default interface parameters + // CS_EN:1, MISO_EN:1, MOSI_EN:1, BYTE_TX_ORDER:1, BYTE_TX_ORDER:1, BIT_RX_ORDER:0, BIT_TX_ORDER:0, CPHA:0, CPOL:0 + spi_config.interface.val = SPI_DEFAULT_INTERFACE; + + // Load default interrupt enable + // TRANS_DONE: true, WRITE_STATUS: false, READ_STATUS: false, WRITE_BUFFER: false, READ_BUFFER: false + spi_config.intr_enable.val = SPI_MASTER_DEFAULT_INTR_ENABLE; + // Set SPI to master mode + // ESP8266 Only support half-duplex + spi_config.mode = SPI_MASTER_MODE; + // Set the SPI clock frequency division factor + spi_config.clk_div = SPI_10MHz_DIV; + spi_config.event_cb = NULL; + spi_init(HSPI_HOST, &spi_config); + +#ifdef ESP_SPI_MASTER_TEST_SEND + xTaskCreate(spi_master_write_slave_task, "spi_master_write_slave_task", 2048, NULL, 3, NULL); +#else + // create spi_master_read_slave_task + xTaskCreate(spi_master_read_slave_task, "spi_master_read_slave_task", 2048, NULL, 2, NULL); + xTaskCreate(spi_master_count_task, "spi_master_count_task", 2048, NULL, 4, NULL); +#endif +} \ No newline at end of file diff --git a/examples/peripherals/spi/normal_performance/spi_slave/CMakeLists.txt b/examples/peripherals/spi/normal_performance/spi_slave/CMakeLists.txt new file mode 100644 index 00000000..d7efcea4 --- /dev/null +++ b/examples/peripherals/spi/normal_performance/spi_slave/CMakeLists.txt @@ -0,0 +1,6 @@ +# The following four lines of boilerplate have to be in your project's CMakeLists +# in this exact order for cmake to work correctly +cmake_minimum_required(VERSION 3.5) + +include($ENV{IDF_PATH}/tools/cmake/project.cmake) +project(spi_slave) diff --git a/examples/peripherals/spi/normal_performance/spi_slave/Makefile b/examples/peripherals/spi/normal_performance/spi_slave/Makefile new file mode 100644 index 00000000..88f0f9db --- /dev/null +++ b/examples/peripherals/spi/normal_performance/spi_slave/Makefile @@ -0,0 +1,9 @@ +# +# This is a project Makefile. It is assumed the directory this Makefile resides in is a +# project subdirectory. +# + +PROJECT_NAME := spi_slave + +include $(IDF_PATH)/make/project.mk + diff --git a/examples/peripherals/spi/normal_performance/spi_slave/main/CMakeLists.txt b/examples/peripherals/spi/normal_performance/spi_slave/main/CMakeLists.txt new file mode 100644 index 00000000..6ccbe341 --- /dev/null +++ b/examples/peripherals/spi/normal_performance/spi_slave/main/CMakeLists.txt @@ -0,0 +1,3 @@ +set(COMPONENT_SRCS "spi_slave_example_main.c") + +register_component() diff --git a/examples/peripherals/spi/normal_performance/spi_slave/main/component.mk b/examples/peripherals/spi/normal_performance/spi_slave/main/component.mk new file mode 100644 index 00000000..44bd2b52 --- /dev/null +++ b/examples/peripherals/spi/normal_performance/spi_slave/main/component.mk @@ -0,0 +1,3 @@ +# +# Main Makefile. This is basically the same as a component makefile. +# diff --git a/examples/peripherals/spi/normal_performance/spi_slave/main/spi_slave_example_main.c b/examples/peripherals/spi/normal_performance/spi_slave/main/spi_slave_example_main.c new file mode 100644 index 00000000..4658462c --- /dev/null +++ b/examples/peripherals/spi/normal_performance/spi_slave/main/spi_slave_example_main.c @@ -0,0 +1,109 @@ +/* spi_slave example + + This example code is in the Public Domain (or CC0 licensed, at your option.) + + Unless required by applicable law or agreed to in writing, this + software is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR + CONDITIONS OF ANY KIND, either express or implied. +*/ + +#include +#include +#include "freertos/FreeRTOS.h" +#include "freertos/task.h" +#include "esp_system.h" +#include "esp_log.h" + +#include "driver/spi.h" +#include "driver/hspi_logic_layer.h" + +static const char *TAG = "spi_slave_example"; + +#define SPI_SLAVE_HANDSHARK_GPIO 4 + +#define SPI_WRITE_BUFFER_MAX_SIZE 2048 +#define SPI_READ_BUFFER_MAX_SIZE 1024 + +//#define SPI_SLAVE_TEST_SEND // Define the macro is slave send mode, delete will be master send mode + +#ifdef SPI_SLAVE_TEST_SEND +static void IRAM_ATTR spi_slave_write_master_task(void *arg) +{ + static uint8_t write_data[SPI_WRITE_BUFFER_MAX_SIZE]; + uint32_t total_tx_count = 0; + + for (int32_t loop = 0; loop < SPI_WRITE_BUFFER_MAX_SIZE; loop++) { + write_data[loop] = 0xa3; + } + + vTaskDelay(5000 / portTICK_RATE_MS); + ESP_LOGI(TAG, "SPI slave start send data"); + for (;;) { + total_tx_count += hspi_slave_logic_write_data(write_data, SPI_READ_BUFFER_MAX_SIZE, portMAX_DELAY); + + if (total_tx_count >= 10*1024*1024) { + ESP_LOGI(TAG, "send done; %d bytes\r\n", total_tx_count); + break; + } + } + vTaskDelete(NULL); +} +#else +uint32_t read_count = 0; + +static void spi_slave_receive_count_task(void* arg) +{ + uint32_t tmp_count = 0; + while(1){ + ESP_LOGI(TAG,"recv_count: %d , speed: %dB/s\n", read_count, ((read_count - tmp_count)/2)); + tmp_count = read_count; + vTaskDelay(2000 / portTICK_RATE_MS); + } +} + +static void IRAM_ATTR spi_slave_read_master_task(void *arg) +{ + static uint8_t read_data[SPI_READ_BUFFER_MAX_SIZE]; + uint32_t read_len = 0; + for (;;) { + read_len = hspi_slave_logic_read_data(read_data, SPI_READ_BUFFER_MAX_SIZE, 1000); + for(int i=0;i< read_len;i++){ + + if(read_data[i] != 0x34){ + ESP_LOGE(TAG,"Receive error data: %x\n", read_data[i]); + } + + } + read_count += read_len; + memset(read_data, 0x0, SPI_READ_BUFFER_MAX_SIZE); + } +} +#endif + +void app_main(void) +{ + spi_config_t spi_config; + // Load default interface parameters + // CS_EN:1, MISO_EN:1, MOSI_EN:1, BYTE_TX_ORDER:1, BYTE_TX_ORDER:1, BIT_RX_ORDER:0, BIT_TX_ORDER:0, CPHA:0, CPOL:0 + spi_config.interface.val = SPI_DEFAULT_INTERFACE; + + // Load default interrupt enable + // TRANS_DONE: false, WRITE_STATUS: true, READ_STATUS: true, WRITE_BUFFER: true, READ_BUFFER: ture + spi_config.intr_enable.val = SPI_SLAVE_DEFAULT_INTR_ENABLE; + // Set SPI to slave mode + spi_config.mode = SPI_SLAVE_MODE; + // Register SPI event callback function + spi_config.event_cb = NULL; + spi_init(HSPI_HOST, &spi_config); + + hspi_slave_logic_device_create(SPI_SLAVE_HANDSHARK_GPIO, 1, SPI_WRITE_BUFFER_MAX_SIZE, SPI_READ_BUFFER_MAX_SIZE); +#ifdef SPI_SLAVE_TEST_SEND + xTaskCreate(spi_slave_write_master_task, "spi_slave_write_master_task", 2048, NULL, 2, NULL); +#else + // create spi_slave_read_master_task + xTaskCreate(spi_slave_read_master_task, "spi_slave_read_master_task", 2048, NULL, 5, NULL); + xTaskCreate(spi_slave_receive_count_task, "spi_slave_receive_count_task", 2048, NULL, 4, NULL); +#endif +} + + diff --git a/examples/peripherals/spi/res/master_recv_data.png b/examples/peripherals/spi/res/master_recv_data.png new file mode 100644 index 00000000..87f074c7 Binary files /dev/null and b/examples/peripherals/spi/res/master_recv_data.png differ diff --git a/examples/peripherals/spi/res/master_recv_length.png b/examples/peripherals/spi/res/master_recv_length.png new file mode 100644 index 00000000..dda2010d Binary files /dev/null and b/examples/peripherals/spi/res/master_recv_length.png differ diff --git a/examples/peripherals/spi/res/master_send.png b/examples/peripherals/spi/res/master_send.png new file mode 100644 index 00000000..08212e8f Binary files /dev/null and b/examples/peripherals/spi/res/master_send.png differ diff --git a/examples/peripherals/spi/res/master_send_data.png b/examples/peripherals/spi/res/master_send_data.png new file mode 100644 index 00000000..3da7e991 Binary files /dev/null and b/examples/peripherals/spi/res/master_send_data.png differ diff --git a/examples/peripherals/spi/res/master_send_length.png b/examples/peripherals/spi/res/master_send_length.png new file mode 100644 index 00000000..53643c9a Binary files /dev/null and b/examples/peripherals/spi/res/master_send_length.png differ diff --git a/examples/peripherals/spi/res/slave_send.png b/examples/peripherals/spi/res/slave_send.png new file mode 100644 index 00000000..74168ea2 Binary files /dev/null and b/examples/peripherals/spi/res/slave_send.png differ diff --git a/examples/peripherals/spi_master/README.md b/examples/peripherals/spi_master/README.md deleted file mode 100644 index 1cc8640d..00000000 --- a/examples/peripherals/spi_master/README.md +++ /dev/null @@ -1,82 +0,0 @@ -# _SPI Master Example_ - -_This example uses the ESP8266 hspi Master to send and receive data to another ESP8266 hspi Slave_ - -## How to use example - -### Hardware Required - -* Connection: - -| Signal | Slave | Master | -|-----------|--------|--------| -| SCLK | GPIO14 | GPIO14 | -| MISO | GPIO12 | GPIO12 | -| MOSI | GPIO13 | GPIO13 | -| CS | GPIO15 | GPIO15 | -| HANDSHARK | GPIO4 | GPIO4 | -| GND | GND | GND | - -* Note: - -When the ESP8266 is powered on, it is necessary to keep the GPIO15 low to enter the Flash mode, so the Master and the Slave have different power-on sequences. - -``` -Master OFF -> Slave ON -> Master ON -``` - -### Configure the project - -``` -make menuconfig -``` - -* Set serial port under Serial Flasher Options. - - -### Build and Flash - -Build the project and flash it to the board, then run monitor tool to view serial output: - -``` -make -j4 flash monitor -``` - -(To exit the serial monitor, type ``Ctrl-]``.) - -See the Getting Started Guide for full steps to configure and use ESP-IDF to build projects. - -## Example Output - -* LOG: - -``` -I (516) spi_master_example: init gpio -I (526) gpio: GPIO[4]| InputEn: 1| OutputEn: 0| OpenDrain: 0| Pullup: 0| Pulldown: 0| Intr:1 -I (536) spi_master_example: init spi -I (556) spi_master_example: Master wrote 3200 bytes in 4302 us -I (656) spi_master_example: Master wrote 3200 bytes in 4519 us -I (766) spi_master_example: Master wrote 3200 bytes in 4522 us -I (866) spi_master_example: Master wrote 3200 bytes in 4520 us -I (966) spi_master_example: Master wrote 3200 bytes in 4521 us -I (1066) spi_master_example: Master wrote 3200 bytes in 4520 us -I (1166) spi_master_example: Master wrote 3200 bytes in 4522 us -I (1266) spi_master_example: Master wrote 3200 bytes in 4521 us -I (1366) spi_master_example: Master wrote 3200 bytes in 4520 us -I (1466) spi_master_example: Master wrote 3200 bytes in 4520 us -I (1566) spi_master_example: Master wrote 3200 bytes in 4520 us -I (1666) spi_master_example: Master wrote 3200 bytes in 4519 us -I (1766) spi_master_example: Master wrote 3200 bytes in 4521 us -I (1866) spi_master_example: Master wrote 3200 bytes in 4519 us -I (1966) spi_master_example: Master wrote 3200 bytes in 4520 us -``` - -* WAVE FORM: - - - SPI_MASTER_WRITE_DATA_TO_SLAVE - - ![wave](wave_write_to_slave.png) - - - SPI_MASTER_READ_DATA_FROM_SLAVE - - ![wave](wave_read_from_slave.png) \ No newline at end of file diff --git a/examples/peripherals/spi_master/main/spi_master_example_main.c b/examples/peripherals/spi_master/main/spi_master_example_main.c deleted file mode 100644 index 22abc310..00000000 --- a/examples/peripherals/spi_master/main/spi_master_example_main.c +++ /dev/null @@ -1,175 +0,0 @@ -/* spi_master example - - This example code is in the Public Domain (or CC0 licensed, at your option.) - - Unless required by applicable law or agreed to in writing, this - software is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR - CONDITIONS OF ANY KIND, either express or implied. -*/ - -#include -#include -#include "freertos/FreeRTOS.h" -#include "freertos/task.h" -#include "freertos/semphr.h" -#include "ringbuf.h" - -#include "esp8266/spi_struct.h" -#include "esp8266/gpio_struct.h" -#include "esp_system.h" -#include "esp_log.h" - -#include "driver/gpio.h" -#include "driver/spi.h" - -static const char *TAG = "spi_master_example"; - -#define SPI_MASTER_HANDSHARK_GPIO 4 -#define SPI_MASTER_HANDSHARK_SEL (1ULL< Slave ON -> Master ON -``` - -### Configure the project - -``` -make menuconfig -``` - -* Set serial port under Serial Flasher Options. - - -### Build and Flash - -Build the project and flash it to the board, then run monitor tool to view serial output: - -``` -make -j4 flash monitor -``` - -(To exit the serial monitor, type ``Ctrl-]``.) - -See the Getting Started Guide for full steps to configure and use ESP-IDF to build projects. - -## Example Output - -* LOG: - -``` -I (500) spi_slave_example: init gpio -I (500) gpio: GPIO[4]| InputEn: 0| OutputEn: 1| OpenDrain: 0| Pullup: 0| Pulldown: 0| Intr:0 -I (520) spi_slave_example: init spi -I (3390) spi_slave_example: Slave wrote 3200 bytes in 4632 us -I (3490) spi_slave_example: Slave wrote 3200 bytes in 4616 us -I (3590) spi_slave_example: Slave wrote 3200 bytes in 4622 us -I (3690) spi_slave_example: Slave wrote 3200 bytes in 4611 us -I (3790) spi_slave_example: Slave wrote 3200 bytes in 4612 us -I (3890) spi_slave_example: Slave wrote 3200 bytes in 4612 us -I (3990) spi_slave_example: Slave wrote 3200 bytes in 4619 us -I (4090) spi_slave_example: Slave wrote 3200 bytes in 4607 us -I (4190) spi_slave_example: Slave wrote 3200 bytes in 4613 us -I (4290) spi_slave_example: Slave wrote 3200 bytes in 4609 us -I (4390) spi_slave_example: Slave wrote 3200 bytes in 4618 us -I (4490) spi_slave_example: Slave wrote 3200 bytes in 4619 us -I (4590) spi_slave_example: Slave wrote 3200 bytes in 4614 us -I (4690) spi_slave_example: Slave wrote 3200 bytes in 4613 us - -``` - -* WAVE FORM: - - - SPI_MASTER_WRITE_DATA_TO_SLAVE - - ![wave](wave_write_to_slave.png) - - - SPI_MASTER_READ_DATA_FROM_SLAVE - - ![wave](wave_read_from_slave.png) \ No newline at end of file diff --git a/examples/peripherals/spi_slave/main/spi_slave_example_main.c b/examples/peripherals/spi_slave/main/spi_slave_example_main.c deleted file mode 100644 index 6042a219..00000000 --- a/examples/peripherals/spi_slave/main/spi_slave_example_main.c +++ /dev/null @@ -1,222 +0,0 @@ -/* spi_slave example - - This example code is in the Public Domain (or CC0 licensed, at your option.) - - Unless required by applicable law or agreed to in writing, this - software is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR - CONDITIONS OF ANY KIND, either express or implied. -*/ - -#include -#include -#include "freertos/FreeRTOS.h" -#include "freertos/task.h" -#include "freertos/semphr.h" -#include "ringbuf.h" - -#include "esp8266/spi_struct.h" -#include "esp8266/gpio_struct.h" -#include "esp_system.h" -#include "esp_log.h" - -#include "driver/gpio.h" -#include "driver/spi.h" - -static const char *TAG = "spi_slave_example"; - -#define SPI_SLAVE_HANDSHARK_GPIO 4 -#define SPI_SLAVE_HANDSHARK_SEL (1ULL<