esp_err_t rmt_set_pin(rmt_channel_t channel, rmt_mode_t mode, gpio_num_t gpio_num) { RMT_CHECK(channel < RMT_CHANNEL_MAX, RMT_CHANNEL_ERROR_STR, ESP_ERR_INVALID_ARG); RMT_CHECK(mode < RMT_MODE_MAX, RMT_MODE_ERROR_STR, ESP_ERR_INVALID_ARG); RMT_CHECK(((GPIO_IS_VALID_GPIO(gpio_num) && (mode == RMT_MODE_RX)) || (GPIO_IS_VALID_OUTPUT_GPIO(gpio_num) && (mode == RMT_MODE_TX))), RMT_GPIO_ERROR_STR, ESP_ERR_INVALID_ARG); PIN_FUNC_SELECT(GPIO_PIN_MUX_REG[gpio_num], 2); if(mode == RMT_MODE_TX) { gpio_set_direction(gpio_num, GPIO_MODE_OUTPUT); gpio_matrix_out(gpio_num, RMT_SIG_OUT0_IDX + channel, 0, 0); } else { gpio_set_direction(gpio_num, GPIO_MODE_INPUT); gpio_matrix_in(gpio_num, RMT_SIG_IN0_IDX + channel, 0); } return ESP_OK; }
/* 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; }
/* 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; }