Example #1
0
esp_err_t spi_bus_free(spi_host_device_t host)
{
    int x;
    SPI_CHECK(host>=SPI_HOST && host<=VSPI_HOST, "invalid host", ESP_ERR_INVALID_ARG);
    SPI_CHECK(spihost[host]!=NULL, "host not in use", ESP_ERR_INVALID_STATE);
    for (x=0; x<NO_CS; x++) {
        SPI_CHECK(spihost[host]->device[x]==NULL, "not all CSses freed", ESP_ERR_INVALID_STATE);
    }

    if ( spihost[host]->dma_chan > 0 ) {
        spicommon_dma_chan_free ( spihost[host]->dma_chan );
    }
#ifdef CONFIG_PM_ENABLE
    esp_pm_lock_delete(spihost[host]->pm_lock);
#endif
    spihost[host]->hw->slave.trans_inten=0;
    spihost[host]->hw->slave.trans_done=0;
    esp_intr_free(spihost[host]->intr);
    spicommon_periph_free(host);
    free(spihost[host]->dmadesc_tx);
    free(spihost[host]->dmadesc_rx);
    free(spihost[host]);
    spihost[host]=NULL;
    return ESP_OK;
}
Example #2
0
esp_err_t spi_init(spi_host_t host, spi_config_t *config)
{
    SPI_CHECK(host < SPI_NUM_MAX, "host num error", ESP_ERR_INVALID_ARG);
    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_CHECK(spi_object[host], "malloc fail", ESP_ERR_NO_MEM);
    spi_object[host]->trans_mux = xSemaphoreCreateMutex();
    if (NULL == spi_object[host]->trans_mux) {
        spi_deinit(host);
        SPI_CHECK(false, "Semaphore create fail", 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();

    if (spi_object[host]->event_cb) {
        spi_object[host]->event_cb(SPI_INIT_EVENT, NULL);
    }

    return ESP_OK;
}
Example #3
0
esp_err_t spi_slave_get_trans_result(spi_host_device_t host, spi_slave_transaction_t **trans_desc, TickType_t ticks_to_wait)
{
    BaseType_t r;
    SPI_CHECK(VALID_HOST(host), "invalid host", ESP_ERR_INVALID_ARG);
    SPI_CHECK(spihost[host], "host not slave", ESP_ERR_INVALID_ARG);
    r = xQueueReceive(spihost[host]->ret_queue, (void *)trans_desc, ticks_to_wait);
    if (!r) return ESP_ERR_TIMEOUT;
    return ESP_OK;
}
Example #4
0
esp_err_t spi_set_mode(spi_host_t host, spi_mode_t *mode)
{
    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(mode, "parameter pointer is empty", ESP_ERR_INVALID_ARG);

    spi_object[host]->mode = *mode;
    ENTER_CRITICAL();
    // Disable flash operation mode
    SPI[host]->user.flash_mode = false;

    if (SPI_MASTER_MODE == *mode) {
        // Set to Master mode
        SPI[host]->pin.slave_mode = false;
        SPI[host]->slave.slave_mode = false;
        // Master uses the entire hardware buffer to improve transmission speed
        SPI[host]->user.usr_mosi_highpart = false;
        SPI[host]->user.usr_miso_highpart = false;
        SPI[host]->user.usr_mosi = true;
        // Create hardware cs in advance
        SPI[host]->user.cs_setup = true;
        // Hysteresis to keep hardware cs
        SPI[host]->user.cs_hold = true;
        SPI[host]->user.duplex = true;
        SPI[host]->user.ck_i_edge = true;
        SPI[host]->ctrl2.mosi_delay_num = 0;
        SPI[host]->ctrl2.miso_delay_num = 1;
    } else {
        // Set to Slave mode
        SPI[host]->pin.slave_mode = true;
        SPI[host]->slave.slave_mode = true;
        SPI[host]->user.usr_miso_highpart = true;
        // 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_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
        // In Slave mode miso, mosi length is the same
        SPI[host]->slave1.buf_bitlen = 255;
        SPI[host]->cmd.usr = 1;
    }

    SPI[host]->user.fwrite_dual = false;
    SPI[host]->user.fwrite_quad = false;
    SPI[host]->user.fwrite_dio  = false;
    SPI[host]->user.fwrite_qio  = false;
    SPI[host]->ctrl.fread_dual  = false;
    SPI[host]->ctrl.fread_quad  = false;
    SPI[host]->ctrl.fread_dio   = false;
    SPI[host]->ctrl.fread_qio   = false;
    SPI[host]->ctrl.fastrd_mode = true;
    EXIT_CRITICAL();

    return ESP_OK;
}
Example #5
0
esp_err_t spi_get_intr_enable(spi_host_t host, spi_intr_enable_t *intr_enable)
{
    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(intr_enable, "parameter pointer is empty", ESP_ERR_INVALID_ARG);

    intr_enable->val = (SPI[host]->slave.val >> 5) & 0x1F;

    return ESP_OK;
}
Example #6
0
esp_err_t spi_get_interface(spi_host_t host, spi_interface_t *interface)
{
    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(interface, "parameter pointer is empty", ESP_ERR_INVALID_ARG);

    *interface = spi_object[host]->interface;

    return ESP_OK;
}
Example #7
0
esp_err_t spi_set_event_callback(spi_host_t host, spi_event_callback_t *event_cb)
{
    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(event_cb, "parameter pointer is empty", ESP_ERR_INVALID_ARG);

    spi_object[host]->event_cb = *event_cb;

    return ESP_OK;
}
Example #8
0
esp_err_t spi_set_clk_div(spi_host_t host, spi_clk_div_t *clk_div)
{
    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(clk_div, "parameter pointer is empty", ESP_ERR_INVALID_ARG);

    ENTER_CRITICAL();

    if (SPI_MASTER_MODE == spi_object[host]->mode) {
        if (SPI_80MHz_DIV == *clk_div) {
            switch (host) {
                case CSPI_HOST: {
                    SET_PERI_REG_MASK(PERIPHS_IO_MUX_CONF_U, SPI0_CLK_EQU_SYS_CLK);
                }
                break;

                case HSPI_HOST: {
                    SET_PERI_REG_MASK(PERIPHS_IO_MUX_CONF_U, SPI1_CLK_EQU_SYS_CLK);
                }
                break;
            }

            SPI[host]->clock.clk_equ_sysclk = true;
        } else {
            // Configure the IO_MUX clock (required, otherwise the clock output will be confusing)
            switch (host) {
                case CSPI_HOST: {
                    CLEAR_PERI_REG_MASK(PERIPHS_IO_MUX_CONF_U, SPI0_CLK_EQU_SYS_CLK);
                }
                break;

                case HSPI_HOST: {
                    CLEAR_PERI_REG_MASK(PERIPHS_IO_MUX_CONF_U, SPI1_CLK_EQU_SYS_CLK);
                }
                break;
            }

            // FRE(SCLK) = clk_equ_sysclk ? 80MHz : APB_CLK(80MHz) / clkdiv_pre / clkcnt
            SPI[host]->clock.clk_equ_sysclk = false;
            SPI[host]->clock.clkdiv_pre = 0;
            SPI[host]->clock.clkcnt_n = *clk_div - 1;
            // In the master mode clkcnt_h = floor((clkcnt_n+1)/2-1). In the slave mode it must be 0
            SPI[host]->clock.clkcnt_h = *clk_div / 2 - 1;
            // In the master mode clkcnt_l = clkcnt_n. In the slave mode it must be 0
            SPI[host]->clock.clkcnt_l = *clk_div - 1;
        }
    } else {
        // Slave mode must be set to 0
        SPI[host]->clock.val = 0;
    }

    EXIT_CRITICAL();

    return ESP_OK;
}
Example #9
0
esp_err_t spi_slave_queue_trans(spi_host_device_t host, const spi_slave_transaction_t *trans_desc, TickType_t ticks_to_wait)
{
    BaseType_t r;
    SPI_CHECK(VALID_HOST(host), "invalid host", ESP_ERR_INVALID_ARG);
    SPI_CHECK(spihost[host], "host not slave", ESP_ERR_INVALID_ARG);

    SPI_CHECK(trans_desc->length <= spihost[host]->max_transfer_sz * 8, "data transfer > host maximum", ESP_ERR_INVALID_ARG);
    r = xQueueSend(spihost[host]->trans_queue, (void *)&trans_desc, ticks_to_wait);
    if (!r) return ESP_ERR_TIMEOUT;
    esp_intr_enable(spihost[host]->intr);
    return ESP_OK;
}
Example #10
0
esp_err_t 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);

    int ret;

    xSemaphoreTake(spi_object[host]->trans_mux, portMAX_DELAY);
    ret = spi_trans_static(host, trans);
    xSemaphoreGive(spi_object[host]->trans_mux);
    return ret;
}
Example #11
0
esp_err_t spi_get_clk_div(spi_host_t host, spi_clk_div_t *clk_div)
{
    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(clk_div, "parameter pointer is empty", ESP_ERR_INVALID_ARG);

    if (SPI[host]->clock.clk_equ_sysclk) {
        *clk_div = SPI_80MHz_DIV;
    }

    *clk_div = SPI[host]->clock.clkcnt_n + 1;
    return ESP_OK;
}
Example #12
0
esp_err_t spi_slave_free(spi_host_device_t host)
{
    SPI_CHECK(VALID_HOST(host), "invalid host", ESP_ERR_INVALID_ARG);
    SPI_CHECK(spihost[host], "host not slave", ESP_ERR_INVALID_ARG);
    if (spihost[host]->trans_queue) vQueueDelete(spihost[host]->trans_queue);
    if (spihost[host]->ret_queue) vQueueDelete(spihost[host]->ret_queue);
    free(spihost[host]->dmadesc_tx);
    free(spihost[host]->dmadesc_rx);
    free(spihost[host]);
    spihost[host] = NULL;
    spicommon_periph_free(host);
    spihost[host] = NULL;
    return ESP_OK;
}
Example #13
0
esp_err_t spi_get_dummy(spi_host_t host, uint16_t *bitlen)
{
    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(bitlen, "parameter pointer is empty", ESP_ERR_INVALID_ARG);

    if (SPI[host]->user.usr_dummy) {
        *bitlen = SPI[host]->user1.usr_dummy_cyclelen + 1;
    } else {
        *bitlen = 0;
    }

    return ESP_OK;
}
Example #14
0
static esp_err_t 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);

    int x, y;
    ENTER_CRITICAL();
    // Set cmd length and receive cmd
    SPI[host]->user2.usr_command_bitlen = trans.bits.cmd - 1;

    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;

    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];
        }
    }

    // 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];
        }
    }

    EXIT_CRITICAL();

    return ESP_OK;
}
Example #15
0
esp_err_t 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);

    ENTER_CRITICAL();

    SPI[host]->rd_status.val = *status;

    EXIT_CRITICAL();

    return ESP_OK;
}
Example #16
0
esp_err_t spi_set_dummy(spi_host_t host, uint16_t *bitlen)
{
    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(bitlen, "parameter pointer is empty", ESP_ERR_INVALID_ARG);
    SPI_CHECK(*bitlen <= 256, "spi dummy must be shorter than 256 bits", ESP_ERR_INVALID_ARG);

    ENTER_CRITICAL();

    if (*bitlen) {
        SPI[host]->user.usr_dummy = 1;
        SPI[host]->user1.usr_dummy_cyclelen = *bitlen - 1;
    } else {
        SPI[host]->user.usr_dummy = 0;
    }

    EXIT_CRITICAL();

    return ESP_OK;
}
Example #17
0
esp_err_t spi_set_intr_enable(spi_host_t host, spi_intr_enable_t *intr_enable)
{
    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(intr_enable, "parameter pointer is empty", ESP_ERR_INVALID_ARG);

    ENTER_CRITICAL();
    SPI[host]->slave.rd_buf_inten = intr_enable->read_buffer;
    SPI[host]->slave.wr_buf_inten = intr_enable->write_buffer;
    SPI[host]->slave.rd_sta_inten = intr_enable->read_status;
    SPI[host]->slave.wr_sta_inten = intr_enable->write_status;
    SPI[host]->slave.trans_inten  = intr_enable->trans_done;
    // Clear interrupt status register
    SPI[host]->slave.rd_buf_done  = false;
    SPI[host]->slave.wr_buf_done  = false;
    SPI[host]->slave.rd_sta_done  = false;
    SPI[host]->slave.wr_sta_done  = false;
    SPI[host]->slave.trans_done   = false;
    EXIT_CRITICAL();

    return ESP_OK;
}
Example #18
0
esp_err_t spi_bus_remove_device(spi_device_handle_t handle)
{
    int x;
    SPI_CHECK(handle!=NULL, "invalid handle", ESP_ERR_INVALID_ARG);
    //These checks aren't exhaustive; another thread could sneak in a transaction inbetween. These are only here to
    //catch design errors and aren't meant to be triggered during normal operation.
    SPI_CHECK(uxQueueMessagesWaiting(handle->trans_queue)==0, "Have unfinished transactions", ESP_ERR_INVALID_STATE);
    SPI_CHECK(handle->host->cur_cs == NO_CS || handle->host->device[handle->host->cur_cs]!=handle, "Have unfinished transactions", ESP_ERR_INVALID_STATE);
    SPI_CHECK(uxQueueMessagesWaiting(handle->ret_queue)==0, "Have unfinished transactions", ESP_ERR_INVALID_STATE);

    //Kill queues
    vQueueDelete(handle->trans_queue);
    vQueueDelete(handle->ret_queue);
    //Remove device from list of csses and free memory
    for (x=0; x<NO_CS; x++) {
        if (handle->host->device[x] == handle){
            handle->host->device[x]=NULL;
            if ( x == handle->host->prev_cs ) handle->host->prev_cs = NO_CS;
        }
    }
    free(handle);
    return ESP_OK;
}
Example #19
0
esp_err_t spi_deinit(spi_host_t host)
{
    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_intr_enable_t intr_enable;
    // Turn off the current host interrupt enable
    intr_enable.val = 0;
    spi_set_intr_enable(host, &intr_enable);

    // Turn off the SPI interrupt if all other hosts are not initialized
    if (host == CSPI_HOST) {
        if (spi_object[HSPI_HOST] == NULL) {
            spi_intr_disable();
        }
    } else {
        if (spi_object[CSPI_HOST] == NULL) {
            spi_intr_disable();
        }
    }

    // Waiting for all transfers to complete
    while (SPI[host]->cmd.usr);

    // Call the event callback function to send the SPI_DEINIT event
    if (spi_object[host]->event_cb) {
        spi_object[host]->event_cb(SPI_DEINIT_EVENT, NULL);
    }

    if (spi_object[host]->trans_mux) {
        vSemaphoreDelete(spi_object[host]->trans_mux);
    }
    free(spi_object[host]);
    spi_object[host] = NULL;

    return ESP_OK;
}
Example #20
0
/*
Do the common stuff to hook up a SPI host to a bus defined by a bunch of GPIO pins. Feed it a host number and a
bus config struct and it'll set up the GPIO matrix and enable the device. It will set is_native to 1 if the bus
config can be done using the IOMUX instead of using the GPIO matrix.
*/
esp_err_t spicommon_bus_initialize_io(spi_host_device_t host, const spi_bus_config_t *bus_config, int dma_chan, int flags, bool *is_native)
{
    bool native = true;
    bool use_quad = (flags & SPICOMMON_BUSFLAG_QUAD) != 0;

    SPI_CHECK(bus_config->mosi_io_num < 0 || GPIO_IS_VALID_OUTPUT_GPIO(bus_config->mosi_io_num), "spid pin invalid", ESP_ERR_INVALID_ARG);
    SPI_CHECK(bus_config->sclk_io_num < 0 || GPIO_IS_VALID_OUTPUT_GPIO(bus_config->sclk_io_num), "spiclk pin invalid", ESP_ERR_INVALID_ARG);
    SPI_CHECK(bus_config->miso_io_num < 0 || GPIO_IS_VALID_GPIO(bus_config->miso_io_num), "spiq pin invalid", ESP_ERR_INVALID_ARG);
    if (use_quad) {
        SPI_CHECK(bus_config->quadwp_io_num < 0 || GPIO_IS_VALID_OUTPUT_GPIO(bus_config->quadwp_io_num), "spiwp pin invalid", ESP_ERR_INVALID_ARG);
        SPI_CHECK(bus_config->quadhd_io_num < 0 || GPIO_IS_VALID_OUTPUT_GPIO(bus_config->quadhd_io_num), "spihd pin invalid", ESP_ERR_INVALID_ARG);
    }

    //Check if the selected pins correspond to the native pins of the peripheral
    if (bus_config->mosi_io_num >= 0 && bus_config->mosi_io_num != io_signal[host].spid_native) native = false;
    if (bus_config->miso_io_num >= 0 && bus_config->miso_io_num != io_signal[host].spiq_native) native = false;
    if (bus_config->sclk_io_num >= 0 && bus_config->sclk_io_num != io_signal[host].spiclk_native) native = false;
    if (use_quad) {
        if (bus_config->quadwp_io_num >= 0 && bus_config->quadwp_io_num != io_signal[host].spiwp_native) native = false;
        if (bus_config->quadhd_io_num >= 0 && bus_config->quadhd_io_num != io_signal[host].spihd_native) native = false;
    }

    *is_native = native;

    if (native) {
        //All SPI native pin selections resolve to 1, so we put that here instead of trying to figure
        //out which FUNC_GPIOx_xSPIxx to grab; they all are defined to 1 anyway.
        if (bus_config->mosi_io_num > 0) PIN_FUNC_SELECT(GPIO_PIN_MUX_REG[bus_config->mosi_io_num], 1);
        if (bus_config->miso_io_num > 0) PIN_FUNC_SELECT(GPIO_PIN_MUX_REG[bus_config->miso_io_num], 1);
        if (use_quad && bus_config->quadwp_io_num > 0) PIN_FUNC_SELECT(GPIO_PIN_MUX_REG[bus_config->quadwp_io_num], 1);
        if (use_quad && bus_config->quadhd_io_num > 0) PIN_FUNC_SELECT(GPIO_PIN_MUX_REG[bus_config->quadhd_io_num], 1);
        if (bus_config->sclk_io_num > 0) PIN_FUNC_SELECT(GPIO_PIN_MUX_REG[bus_config->sclk_io_num], 1);
    } else {
        //Use GPIO
        if (bus_config->mosi_io_num > 0) {
            PIN_FUNC_SELECT(GPIO_PIN_MUX_REG[bus_config->mosi_io_num], PIN_FUNC_GPIO);
            gpio_set_direction(bus_config->mosi_io_num, GPIO_MODE_INPUT_OUTPUT);
            gpio_matrix_out(bus_config->mosi_io_num, io_signal[host].spid_out, false, false);
            gpio_matrix_in(bus_config->mosi_io_num, io_signal[host].spid_in, false);
        }
        if (bus_config->miso_io_num > 0) {
            PIN_FUNC_SELECT(GPIO_PIN_MUX_REG[bus_config->miso_io_num], PIN_FUNC_GPIO);
            gpio_set_direction(bus_config->miso_io_num, GPIO_MODE_INPUT_OUTPUT);
            gpio_matrix_out(bus_config->miso_io_num, io_signal[host].spiq_out, false, false);
            gpio_matrix_in(bus_config->miso_io_num, io_signal[host].spiq_in, false);
        }
        if (use_quad && bus_config->quadwp_io_num > 0) {
            PIN_FUNC_SELECT(GPIO_PIN_MUX_REG[bus_config->quadwp_io_num], PIN_FUNC_GPIO);
            gpio_set_direction(bus_config->quadwp_io_num, GPIO_MODE_INPUT_OUTPUT);
            gpio_matrix_out(bus_config->quadwp_io_num, io_signal[host].spiwp_out, false, false);
            gpio_matrix_in(bus_config->quadwp_io_num, io_signal[host].spiwp_in, false);
        }
        if (use_quad && bus_config->quadhd_io_num > 0) {
            PIN_FUNC_SELECT(GPIO_PIN_MUX_REG[bus_config->quadhd_io_num], PIN_FUNC_GPIO);
            gpio_set_direction(bus_config->quadhd_io_num, GPIO_MODE_INPUT_OUTPUT);
            gpio_matrix_out(bus_config->quadhd_io_num, io_signal[host].spihd_out, false, false);
            gpio_matrix_in(bus_config->quadhd_io_num, io_signal[host].spihd_in, false);
        }
        if (bus_config->sclk_io_num > 0) {
            PIN_FUNC_SELECT(GPIO_PIN_MUX_REG[bus_config->sclk_io_num], PIN_FUNC_GPIO);
            gpio_set_direction(bus_config->sclk_io_num, GPIO_MODE_INPUT_OUTPUT);
            gpio_matrix_out(bus_config->sclk_io_num, io_signal[host].spiclk_out, false, false);
            gpio_matrix_in(bus_config->sclk_io_num, io_signal[host].spiclk_in, false);
        }
    }

    //Select DMA channel.
    DPORT_SET_PERI_REG_BITS(DPORT_SPI_DMA_CHAN_SEL_REG, 3, dma_chan, (host * 2));

    return ESP_OK;
}
Example #21
0
/*
 Add a device. This allocates a CS line for the device, allocates memory for the device structure and hooks
 up the CS pin to whatever is specified.
*/
esp_err_t spi_bus_add_device(spi_host_device_t host, spi_device_interface_config_t *dev_config, spi_device_handle_t *handle)
{
    int freecs;
    int apbclk=APB_CLK_FREQ;
    SPI_CHECK(host>=SPI_HOST && host<=VSPI_HOST, "invalid host", ESP_ERR_INVALID_ARG);
    SPI_CHECK(spihost[host]!=NULL, "host not initialized", ESP_ERR_INVALID_STATE);
    SPI_CHECK(dev_config->spics_io_num < 0 || GPIO_IS_VALID_OUTPUT_GPIO(dev_config->spics_io_num), "spics pin invalid", ESP_ERR_INVALID_ARG);
    SPI_CHECK(dev_config->clock_speed_hz > 0, "invalid sclk speed", ESP_ERR_INVALID_ARG);
    for (freecs=0; freecs<NO_CS; freecs++) {
        //See if this slot is free; reserve if it is by putting a dummy pointer in the slot. We use an atomic compare&swap to make this thread-safe.
        if (__sync_bool_compare_and_swap(&spihost[host]->device[freecs], NULL, (spi_device_t *)1)) break;
    }
    SPI_CHECK(freecs!=NO_CS, "no free cs pins for host", ESP_ERR_NOT_FOUND);
    //The hardware looks like it would support this, but actually setting cs_ena_pretrans when transferring in full
    //duplex mode does absolutely nothing on the ESP32.
    SPI_CHECK(dev_config->cs_ena_pretrans==0 || (dev_config->flags & SPI_DEVICE_HALFDUPLEX), "cs pretrans delay incompatible with full-duplex", ESP_ERR_INVALID_ARG);
    //Speeds >=40MHz over GPIO matrix needs a dummy cycle, but these don't work for full-duplex connections.
    SPI_CHECK(!( ((dev_config->flags & SPI_DEVICE_HALFDUPLEX)==0) && (dev_config->clock_speed_hz > ((apbclk*2)/5)) && (!spihost[host]->no_gpio_matrix)),
            "No speeds >26MHz supported for full-duplex, GPIO-matrix SPI transfers", ESP_ERR_INVALID_ARG);

    //Allocate memory for device
    spi_device_t *dev=malloc(sizeof(spi_device_t));
    if (dev==NULL) goto nomem;
    memset(dev, 0, sizeof(spi_device_t));
    spihost[host]->device[freecs]=dev;

    //Allocate queues, set defaults
    dev->trans_queue=xQueueCreate(dev_config->queue_size, sizeof(spi_trans_priv));
    dev->ret_queue=xQueueCreate(dev_config->queue_size, sizeof(spi_trans_priv));
    if (!dev->trans_queue || !dev->ret_queue) goto nomem;
    if (dev_config->duty_cycle_pos==0) dev_config->duty_cycle_pos=128;
    dev->host=spihost[host];

    //We want to save a copy of the dev config in the dev struct.
    memcpy(&dev->cfg, dev_config, sizeof(spi_device_interface_config_t));
    // TODO: if we have to change the apb clock among transactions, re-calculate this each time the apb clock lock is acquired.
    dev->clk_cfg.eff_clk = spi_cal_clock(apbclk, dev_config->clock_speed_hz, dev_config->duty_cycle_pos, (uint32_t*)&dev->clk_cfg.reg);

    //Set CS pin, CS options
    if (dev_config->spics_io_num >= 0) {
        gpio_set_direction(dev_config->spics_io_num, GPIO_MODE_OUTPUT);
        spicommon_cs_initialize(host, dev_config->spics_io_num, freecs, spihost[host]->no_gpio_matrix == false);
    }
    if (dev_config->flags&SPI_DEVICE_CLK_AS_CS) {
        spihost[host]->hw->pin.master_ck_sel |= (1<<freecs);
    } else {
        spihost[host]->hw->pin.master_ck_sel &= (1<<freecs);
    }
    if (dev_config->flags&SPI_DEVICE_POSITIVE_CS) {
        spihost[host]->hw->pin.master_cs_pol |= (1<<freecs);
    } else {
        spihost[host]->hw->pin.master_cs_pol &= (1<<freecs);
    }
    *handle=dev;
    ESP_LOGD(SPI_TAG, "SPI%d: New device added to CS%d, effective clock: %dkHz", host, freecs, dev->clk_cfg.eff_clk/1000);
    return ESP_OK;

nomem:
    if (dev) {
        if (dev->trans_queue) vQueueDelete(dev->trans_queue);
        if (dev->ret_queue) vQueueDelete(dev->ret_queue);
    }
    free(dev);
    return ESP_ERR_NO_MEM;
}
Example #22
0
esp_err_t spi_slave_initialize(spi_host_device_t host, const spi_bus_config_t *bus_config, const spi_slave_interface_config_t *slave_config, int dma_chan)
{
    bool native, claimed;
    //We only support HSPI/VSPI, period.
    SPI_CHECK(VALID_HOST(host), "invalid host", ESP_ERR_INVALID_ARG);

    claimed = spicommon_periph_claim(host);
    SPI_CHECK(claimed, "host already in use", ESP_ERR_INVALID_STATE);

    spihost[host] = malloc(sizeof(spi_slave_t));
    if (spihost[host] == NULL) goto nomem;
    memset(spihost[host], 0, sizeof(spi_slave_t));
    memcpy(&spihost[host]->cfg, slave_config, sizeof(spi_slave_interface_config_t));

    spicommon_bus_initialize_io(host, bus_config, dma_chan, SPICOMMON_BUSFLAG_SLAVE, &native);
    gpio_set_direction(slave_config->spics_io_num, GPIO_MODE_INPUT);
    spicommon_cs_initialize(host, slave_config->spics_io_num, 0, native == false);
    spihost[host]->no_gpio_matrix = native;
    spihost[host]->dma_chan = dma_chan;
    if (dma_chan != 0) {
        //See how many dma descriptors we need and allocate them
        int dma_desc_ct = (bus_config->max_transfer_sz + SPI_MAX_DMA_LEN - 1) / SPI_MAX_DMA_LEN;
        if (dma_desc_ct == 0) dma_desc_ct = 1; //default to 4k when max is not given
        spihost[host]->max_transfer_sz = dma_desc_ct * SPI_MAX_DMA_LEN;
        spihost[host]->dmadesc_tx = pvPortMallocCaps(sizeof(lldesc_t) * dma_desc_ct, MALLOC_CAP_DMA);
        spihost[host]->dmadesc_rx = pvPortMallocCaps(sizeof(lldesc_t) * dma_desc_ct, MALLOC_CAP_DMA);
        if (!spihost[host]->dmadesc_tx || !spihost[host]->dmadesc_rx) goto nomem;
    } else {
        //We're limited to non-DMA transfers: the SPI work registers can hold 64 bytes at most.
        spihost[host]->max_transfer_sz = 16 * 4;
    }

    //Create queues
    spihost[host]->trans_queue = xQueueCreate(slave_config->queue_size, sizeof(spi_slave_transaction_t *));
    spihost[host]->ret_queue = xQueueCreate(slave_config->queue_size, sizeof(spi_slave_transaction_t *));
    if (!spihost[host]->trans_queue || !spihost[host]->ret_queue) goto nomem;

    esp_intr_alloc(spicommon_irqsource_for_host(host), ESP_INTR_FLAG_INTRDISABLED, spi_intr, (void *)spihost[host], &spihost[host]->intr);
    spihost[host]->hw = spicommon_hw_for_host(host);

    //Configure slave
    spihost[host]->hw->clock.val = 0;
    spihost[host]->hw->user.val = 0;
    spihost[host]->hw->ctrl.val = 0;
    spihost[host]->hw->slave.wr_rd_buf_en = 1; //no sure if needed
    spihost[host]->hw->user.doutdin = 1; //we only support full duplex
    spihost[host]->hw->user.sio = 0;
    spihost[host]->hw->slave.slave_mode = 1;
    spihost[host]->hw->dma_conf.val |= SPI_OUT_RST | SPI_IN_RST | SPI_AHBM_RST | SPI_AHBM_FIFO_RST;
    spihost[host]->hw->dma_out_link.start = 0;
    spihost[host]->hw->dma_in_link.start = 0;
    spihost[host]->hw->dma_conf.val &= ~(SPI_OUT_RST | SPI_IN_RST | SPI_AHBM_RST | SPI_AHBM_FIFO_RST);
    spihost[host]->hw->dma_conf.out_data_burst_en = 1;
    spihost[host]->hw->slave.sync_reset = 1;
    spihost[host]->hw->slave.sync_reset = 0;


    bool nodelay = true;
    spihost[host]->hw->ctrl.rd_bit_order = (slave_config->flags & SPI_SLAVE_RXBIT_LSBFIRST) ? 1 : 0;
    spihost[host]->hw->ctrl.wr_bit_order = (slave_config->flags & SPI_SLAVE_TXBIT_LSBFIRST) ? 1 : 0;
    if (slave_config->mode == 0) {
        spihost[host]->hw->pin.ck_idle_edge = 0;
        spihost[host]->hw->user.ck_i_edge = 1;
        spihost[host]->hw->ctrl2.miso_delay_mode = nodelay ? 0 : 2;
    } else if (slave_config->mode == 1) {
        spihost[host]->hw->pin.ck_idle_edge = 0;
        spihost[host]->hw->user.ck_i_edge = 0;
        spihost[host]->hw->ctrl2.miso_delay_mode = nodelay ? 0 : 1;
    } else if (slave_config->mode == 2) {
        spihost[host]->hw->pin.ck_idle_edge = 1;
        spihost[host]->hw->user.ck_i_edge = 0;
        spihost[host]->hw->ctrl2.miso_delay_mode = nodelay ? 0 : 1;
    } else if (slave_config->mode == 3) {
        spihost[host]->hw->pin.ck_idle_edge = 1;
        spihost[host]->hw->user.ck_i_edge = 1;
        spihost[host]->hw->ctrl2.miso_delay_mode = nodelay ? 0 : 2;
    }


    //Reset DMA
    spihost[host]->hw->dma_conf.val |= SPI_OUT_RST | SPI_IN_RST | SPI_AHBM_RST | SPI_AHBM_FIFO_RST;
    spihost[host]->hw->dma_out_link.start = 0;
    spihost[host]->hw->dma_in_link.start = 0;
    spihost[host]->hw->dma_conf.val &= ~(SPI_OUT_RST | SPI_IN_RST | SPI_AHBM_RST | SPI_AHBM_FIFO_RST);

    //Disable unneeded ints
    spihost[host]->hw->slave.rd_buf_done = 0;
    spihost[host]->hw->slave.wr_buf_done = 0;
    spihost[host]->hw->slave.rd_sta_done = 0;
    spihost[host]->hw->slave.wr_sta_done = 0;
    spihost[host]->hw->slave.rd_buf_inten = 0;
    spihost[host]->hw->slave.wr_buf_inten = 0;
    spihost[host]->hw->slave.rd_sta_inten = 0;
    spihost[host]->hw->slave.wr_sta_inten = 0;

    //Force a transaction done interrupt. This interrupt won't fire yet because we initialized the SPI interrupt as
    //disabled.  This way, we can just enable the SPI interrupt and the interrupt handler will kick in, handling
    //any transactions that are queued.
    spihost[host]->hw->slave.trans_inten = 1;
    spihost[host]->hw->slave.trans_done = 1;

    return ESP_OK;

nomem:
    if (spihost[host]) {
        if (spihost[host]->trans_queue) vQueueDelete(spihost[host]->trans_queue);
        if (spihost[host]->ret_queue) vQueueDelete(spihost[host]->ret_queue);
        free(spihost[host]->dmadesc_tx);
        free(spihost[host]->dmadesc_rx);
    }
    free(spihost[host]);
    spihost[host] = NULL;
    spicommon_periph_free(host);
    return ESP_ERR_NO_MEM;
}
Example #23
0
esp_err_t spi_bus_initialize(spi_host_device_t host, const spi_bus_config_t *bus_config, int dma_chan)
{
    bool native, spi_chan_claimed, dma_chan_claimed;
    /* ToDo: remove this when we have flash operations cooperating with this */
    SPI_CHECK(host!=SPI_HOST, "SPI1 is not supported", ESP_ERR_NOT_SUPPORTED);

    SPI_CHECK(host>=SPI_HOST && host<=VSPI_HOST, "invalid host", ESP_ERR_INVALID_ARG);
    SPI_CHECK( dma_chan >= 0 && dma_chan <= 2, "invalid dma channel", ESP_ERR_INVALID_ARG );

    spi_chan_claimed=spicommon_periph_claim(host);
    SPI_CHECK(spi_chan_claimed, "host already in use", ESP_ERR_INVALID_STATE);

    if ( dma_chan != 0 ) {
        dma_chan_claimed=spicommon_dma_chan_claim(dma_chan);
        if ( !dma_chan_claimed ) {
            spicommon_periph_free( host );
            SPI_CHECK(dma_chan_claimed, "dma channel already in use", ESP_ERR_INVALID_STATE);
        }
    }

    spihost[host]=malloc(sizeof(spi_host_t));
    if (spihost[host]==NULL) goto nomem;
    memset(spihost[host], 0, sizeof(spi_host_t));
#ifdef CONFIG_PM_ENABLE
    esp_err_t err = esp_pm_lock_create(ESP_PM_APB_FREQ_MAX, 0, "spi_master",
            &spihost[host]->pm_lock);
    if (err != ESP_OK) {
        goto nomem;
    }
#endif //CONFIG_PM_ENABLE
    
    spicommon_bus_initialize_io(host, bus_config, dma_chan, SPICOMMON_BUSFLAG_MASTER|SPICOMMON_BUSFLAG_QUAD, &native);
    spihost[host]->no_gpio_matrix=native;
    
    spihost[host]->dma_chan=dma_chan;
    if (dma_chan == 0) {
        spihost[host]->max_transfer_sz = 32;
    } else {
        //See how many dma descriptors we need and allocate them
        int dma_desc_ct=(bus_config->max_transfer_sz+SPI_MAX_DMA_LEN-1)/SPI_MAX_DMA_LEN;
        if (dma_desc_ct==0) dma_desc_ct=1; //default to 4k when max is not given
        spihost[host]->max_transfer_sz = dma_desc_ct*SPI_MAX_DMA_LEN;
        spihost[host]->dmadesc_tx=heap_caps_malloc(sizeof(lldesc_t)*dma_desc_ct, MALLOC_CAP_DMA);
        spihost[host]->dmadesc_rx=heap_caps_malloc(sizeof(lldesc_t)*dma_desc_ct, MALLOC_CAP_DMA);
        if (!spihost[host]->dmadesc_tx || !spihost[host]->dmadesc_rx) goto nomem;
    }
    esp_intr_alloc(spicommon_irqsource_for_host(host), ESP_INTR_FLAG_INTRDISABLED, spi_intr, (void*)spihost[host], &spihost[host]->intr);
    spihost[host]->hw=spicommon_hw_for_host(host);

    spihost[host]->cur_cs = NO_CS;
    spihost[host]->prev_cs = NO_CS;

    //Reset DMA
    spihost[host]->hw->dma_conf.val|=SPI_OUT_RST|SPI_IN_RST|SPI_AHBM_RST|SPI_AHBM_FIFO_RST;
    spihost[host]->hw->dma_out_link.start=0;
    spihost[host]->hw->dma_in_link.start=0;
    spihost[host]->hw->dma_conf.val&=~(SPI_OUT_RST|SPI_IN_RST|SPI_AHBM_RST|SPI_AHBM_FIFO_RST);
    //Reset timing
    spihost[host]->hw->ctrl2.val=0;

    //Disable unneeded ints
    spihost[host]->hw->slave.rd_buf_done=0;
    spihost[host]->hw->slave.wr_buf_done=0;
    spihost[host]->hw->slave.rd_sta_done=0;
    spihost[host]->hw->slave.wr_sta_done=0;
    spihost[host]->hw->slave.rd_buf_inten=0;
    spihost[host]->hw->slave.wr_buf_inten=0;
    spihost[host]->hw->slave.rd_sta_inten=0;
    spihost[host]->hw->slave.wr_sta_inten=0;

    //Force a transaction done interrupt. This interrupt won't fire yet because we initialized the SPI interrupt as
    //disabled.  This way, we can just enable the SPI interrupt and the interrupt handler will kick in, handling 
    //any transactions that are queued.
    spihost[host]->hw->slave.trans_inten=1;
    spihost[host]->hw->slave.trans_done=1;

    return ESP_OK;

nomem:
    if (spihost[host]) {
        free(spihost[host]->dmadesc_tx);
        free(spihost[host]->dmadesc_rx);
#ifdef CONFIG_PM_ENABLE
        if (spihost[host]->pm_lock) {
            esp_pm_lock_delete(spihost[host]->pm_lock);
        }
#endif
    }
    free(spihost[host]);
    spicommon_periph_free(host);
    spicommon_dma_chan_free(dma_chan);
    return ESP_ERR_NO_MEM;
}
Example #24
0
esp_err_t spi_set_interface(spi_host_t host, spi_interface_t *interface)
{
    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(interface, "parameter pointer is empty", ESP_ERR_INVALID_ARG);

    spi_object[host]->interface = *interface;
    ENTER_CRITICAL();

    switch (host) {
        case CSPI_HOST: {
            // Initialize SPI IO
            PIN_PULLUP_EN(PERIPHS_IO_MUX_SD_CLK_U);
            PIN_FUNC_SELECT(PERIPHS_IO_MUX_SD_CLK_U, FUNC_SPICLK);

            if (interface->mosi_en) {
                PIN_PULLUP_EN(PERIPHS_IO_MUX_SD_DATA1_U);
                PIN_FUNC_SELECT(PERIPHS_IO_MUX_SD_DATA1_U, FUNC_SPID_MOSI);
            }

            if (interface->miso_en) {
                PIN_PULLUP_EN(PERIPHS_IO_MUX_SD_DATA0_U);
                PIN_FUNC_SELECT(PERIPHS_IO_MUX_SD_DATA0_U, FUNC_SPIQ_MISO);
            }

            if (interface->cs_en) {
                PIN_PULLUP_EN(PERIPHS_IO_MUX_SD_CMD_U);
                PIN_FUNC_SELECT(PERIPHS_IO_MUX_SD_CMD_U, FUNC_SPICS0);
            }
        }
        break;

        case HSPI_HOST: {
            // Initialize HSPI IO
            PIN_PULLUP_EN(PERIPHS_IO_MUX_MTMS_U);
            PIN_FUNC_SELECT(PERIPHS_IO_MUX_MTMS_U, FUNC_HSPI_CLK); //GPIO14 is SPI CLK pin (Clock)

            if (interface->mosi_en) {
                PIN_PULLUP_EN(PERIPHS_IO_MUX_MTCK_U);
                PIN_FUNC_SELECT(PERIPHS_IO_MUX_MTCK_U, FUNC_HSPID_MOSI); //GPIO13 is SPI MOSI pin (Master Data Out)
            }

            if (interface->miso_en) {
                PIN_PULLUP_EN(PERIPHS_IO_MUX_MTDI_U);
                PIN_FUNC_SELECT(PERIPHS_IO_MUX_MTDI_U, FUNC_HSPIQ_MISO); //GPIO12 is SPI MISO pin (Master Data In)
            }

            if (interface->cs_en) {
                PIN_PULLUP_EN(PERIPHS_IO_MUX_MTDO_U);
                PIN_FUNC_SELECT(PERIPHS_IO_MUX_MTDO_U, FUNC_HSPI_CS0);
            }
        }
        break;
    }

    // Set the clock polarity and phase
    SPI[host]->pin.ck_idle_edge = interface->cpol;

    if (interface->cpol == interface->cpha) {
        SPI[host]->user.ck_out_edge = false;
    } else {
        SPI[host]->user.ck_out_edge = true;
    }

    // Set data bit order
    SPI[host]->ctrl.wr_bit_order = interface->bit_tx_order;
    SPI[host]->ctrl.rd_bit_order = interface->bit_rx_order;
    // Set data byte order
    SPI[host]->user.wr_byte_order = interface->byte_tx_order;
    SPI[host]->user.rd_byte_order = interface->byte_rx_order;
    EXIT_CRITICAL();

    return ESP_OK;
}
Example #25
0
static esp_err_t 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);

    int x, y;

    // Waiting for an incomplete transfer
    while (SPI[host]->cmd.usr);

    ENTER_CRITICAL();

    // Set the cmd length and transfer 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;
    } else {
        SPI[host]->user.usr_command = 0;
    }

    // Set addr length and transfer 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;
    } else {
        SPI[host]->user.usr_addr = 0;
    }

    // Set mosi length and transmit 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];
        }
    } else {
        SPI[host]->user.usr_mosi = 0;
    }

    // Set the length of the miso
    if (trans.bits.miso && trans.miso) {
        SPI[host]->user.usr_miso = 1;
        SPI[host]->user1.usr_miso_bitlen = trans.bits.miso - 1;
    } else {
        SPI[host]->user.usr_miso = 0;
    }

    // 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);
    }

    // Start transmission
    SPI[host]->cmd.usr = 1;

    // Receive miso data
    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];
        }
    }

    EXIT_CRITICAL();

    return ESP_OK;
}