Exemple #1
0
static void rsa_isr_initialise()
{
    if (op_complete_sem == NULL) {
        op_complete_sem = xSemaphoreCreateBinary();
        esp_intr_alloc(ETS_RSA_INTR_SOURCE, 0, rsa_complete_isr, NULL, NULL);
    }
}
void ws2812_init(int gpioNum)
{
  SET_PERI_REG_MASK(DPORT_PERIP_CLK_EN_REG, DPORT_RMT_CLK_EN);
  CLEAR_PERI_REG_MASK(DPORT_PERIP_RST_EN_REG, DPORT_RMT_RST);

  PIN_FUNC_SELECT(GPIO_PIN_MUX_REG[gpioNum], 2);
  gpio_matrix_out(gpioNum, RMT_SIG_OUT0_IDX + RMTCHANNEL, 0, 0);
  gpio_set_direction(gpioNum, GPIO_MODE_OUTPUT);

  ws2812_initRMTChannel(RMTCHANNEL);

  RMT.tx_lim_ch[RMTCHANNEL].limit = MAX_PULSES;
  RMT.int_ena.ch0_tx_thr_event = 1;
  RMT.int_ena.ch0_tx_end = 1;

  ws2812_bits[0].level0 = 1;
  ws2812_bits[0].level1 = 0;
  ws2812_bits[0].duration0 = PULSE_T0H;
  ws2812_bits[0].duration1 = PULSE_T0L;
  ws2812_bits[1].level0 = 1;
  ws2812_bits[1].level1 = 0;
  ws2812_bits[1].duration0 = PULSE_T1H;
  ws2812_bits[1].duration1 = PULSE_T1L;

  esp_intr_alloc(ETS_RMT_INTR_SOURCE, 0, ws2812_handleInterrupt, NULL, &rmt_intr_handle);

  return;
}
Exemple #3
0
esp_err_t rmt_isr_register(void (*fn)(void*), void * arg, int intr_alloc_flags, rmt_isr_handle_t *handle)
{
    RMT_CHECK((fn != NULL), RMT_ADDR_ERROR_STR, ESP_ERR_INVALID_ARG);
    RMT_CHECK(s_rmt_driver_channels == 0, "RMT driver installed, can not install generic ISR handler", ESP_FAIL);

    return esp_intr_alloc(ETS_RMT_INTR_SOURCE, intr_alloc_flags, fn, arg, handle);
}
Exemple #4
0
void local_timer_test()
{
    intr_handle_t ih;
    esp_err_t r;
    r=esp_intr_alloc(ETS_INTERNAL_TIMER1_INTR_SOURCE, 0, int_timer_handler, NULL, &ih);
    TEST_ASSERT(r==ESP_OK);
    printf("Int timer 1 intno %d\n", esp_intr_get_intno(ih));
    xthal_set_ccompare(1, xthal_get_ccount()+8000000);
    int_timer_ctr=0;
    vTaskDelay(1000 / portTICK_PERIOD_MS);
    printf("Timer val after 1 sec: %d\n", int_timer_ctr);
    TEST_ASSERT(int_timer_ctr!=0);
    printf("Disabling int\n");
    esp_intr_disable(ih);
    int_timer_ctr=0;
    vTaskDelay(1000 / portTICK_PERIOD_MS);
    printf("Timer val after 1 sec: %d\n", int_timer_ctr);
    TEST_ASSERT(int_timer_ctr==0);
    printf("Re-enabling\n");
    esp_intr_enable(ih);
    vTaskDelay(1000 / portTICK_PERIOD_MS);
    printf("Timer val after 1 sec: %d\n", int_timer_ctr);
    TEST_ASSERT(int_timer_ctr!=0);

    printf("Free int, re-alloc disabled\n");
    r=esp_intr_free(ih);
    TEST_ASSERT(r==ESP_OK);
    r=esp_intr_alloc(ETS_INTERNAL_TIMER1_INTR_SOURCE, ESP_INTR_FLAG_INTRDISABLED, int_timer_handler, NULL, &ih);
    TEST_ASSERT(r==ESP_OK);
    int_timer_ctr=0;
    vTaskDelay(1000 / portTICK_PERIOD_MS);
    printf("Timer val after 1 sec: %d\n", int_timer_ctr);
    TEST_ASSERT(int_timer_ctr==0);
    printf("Re-enabling\n");
    esp_intr_enable(ih);
    vTaskDelay(1000 / portTICK_PERIOD_MS);
    printf("Timer val after 1 sec: %d\n", int_timer_ctr);
    TEST_ASSERT(int_timer_ctr!=0);
    r=esp_intr_free(ih);
    TEST_ASSERT(r==ESP_OK);
    printf("Done.\n");
}
Exemple #5
0
static void testthread(void *arg) {
    intr_handle_t handle;
    in_int_context=0;
    int_handled=0;
    TEST_ASSERT(!xPortInIsrContext());
    xthal_set_ccompare(1, xthal_get_ccount()+8000000);
    esp_err_t err = esp_intr_alloc(ETS_INTERNAL_TIMER1_INTR_SOURCE, 0, &testint, NULL, &handle);
    TEST_ASSERT_EQUAL_HEX32(ESP_OK, err);
    vTaskDelay(100 / portTICK_PERIOD_MS);
    TEST_ASSERT(int_handled);
    TEST_ASSERT(in_int_context);
    TEST_ASSERT_EQUAL_HEX32( ESP_OK, esp_intr_free(handle) );
    vTaskDelete(NULL);
}
Exemple #6
0
void esp_task_wdt_init() {
    TIMERG0.wdt_wprotect=TIMG_WDT_WKEY_VALUE;
    TIMERG0.wdt_config0.sys_reset_length=7;                 //3.2uS
    TIMERG0.wdt_config0.cpu_reset_length=7;                 //3.2uS
    TIMERG0.wdt_config0.level_int_en=1;
    TIMERG0.wdt_config0.stg0=TIMG_WDT_STG_SEL_INT;          //1st stage timeout: interrupt
    TIMERG0.wdt_config0.stg1=TIMG_WDT_STG_SEL_RESET_SYSTEM; //2nd stage timeout: reset system
    TIMERG0.wdt_config1.clk_prescale=80*500;                //Prescaler: wdt counts in ticks of 0.5mS
    TIMERG0.wdt_config2=CONFIG_TASK_WDT_TIMEOUT_S*2000;     //Set timeout before interrupt
    TIMERG0.wdt_config3=CONFIG_TASK_WDT_TIMEOUT_S*4000;     //Set timeout before reset
    TIMERG0.wdt_config0.en=1;
    TIMERG0.wdt_feed=1;
    TIMERG0.wdt_wprotect=0;
#if CONFIG_TASK_WDT_CHECK_IDLE_TASK
    esp_register_freertos_idle_hook(idle_hook);
#endif
    ESP_ERROR_CHECK( esp_intr_alloc(ETS_TG0_WDT_LEVEL_INTR_SOURCE, 0, task_wdt_isr, NULL, NULL) );
}
Exemple #7
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;
}
Exemple #8
0
/**
 * @brief EXPERIMENTAL
 */
void I2S::cameraMode(dma_config_t config, int desc_count, int sample_count) {
	ESP_LOGD(LOG_TAG, ">> cameraMode");
	ESP32CPP::GPIO::setInput(config.pin_d0);
	ESP32CPP::GPIO::setInput(config.pin_d1);
	ESP32CPP::GPIO::setInput(config.pin_d2);
	ESP32CPP::GPIO::setInput(config.pin_d3);
	ESP32CPP::GPIO::setInput(config.pin_d4);
	ESP32CPP::GPIO::setInput(config.pin_d5);
	ESP32CPP::GPIO::setInput(config.pin_d6);
	ESP32CPP::GPIO::setInput(config.pin_d7);
	//ESP32CPP::GPIO::setInput(config.pin_xclk);
	ESP32CPP::GPIO::setInput(config.pin_vsync);
	ESP32CPP::GPIO::setInput(config.pin_href);
	ESP32CPP::GPIO::setInput(config.pin_pclk);
	//ESP32CPP::GPIO::setOutput(config.pin_reset);

	const uint32_t const_high = 0x38;

	gpio_matrix_in(config.pin_d0,	   I2S0I_DATA_IN0_IDX, false);
	gpio_matrix_in(config.pin_d1,	   I2S0I_DATA_IN1_IDX, false);
	gpio_matrix_in(config.pin_d2,	   I2S0I_DATA_IN2_IDX, false);
	gpio_matrix_in(config.pin_d3,	   I2S0I_DATA_IN3_IDX, false);
	gpio_matrix_in(config.pin_d4,	   I2S0I_DATA_IN4_IDX, false);
	gpio_matrix_in(config.pin_d5,	   I2S0I_DATA_IN5_IDX, false);
	gpio_matrix_in(config.pin_d6,	   I2S0I_DATA_IN6_IDX, false);
	gpio_matrix_in(config.pin_d7,	   I2S0I_DATA_IN7_IDX, false);
	gpio_matrix_in(config.pin_vsync,	I2S0I_V_SYNC_IDX,   true);
	gpio_matrix_in(config.pin_href,	 I2S0I_H_SYNC_IDX,   false);
//	gpio_matrix_in(const_high,		  I2S0I_V_SYNC_IDX,   false);
//	gpio_matrix_in(const_high,		  I2S0I_H_SYNC_IDX,   false);
	gpio_matrix_in(const_high,		  I2S0I_H_ENABLE_IDX, false);
	gpio_matrix_in(config.pin_pclk,	 I2S0I_WS_IN_IDX,	false);

	// Enable and configure I2S peripheral
	periph_module_enable(PERIPH_I2S0_MODULE);
	// Toggle some reset bits in LC_CONF register
	// Toggle some reset bits in CONF register
	// Enable slave mode (sampling clock is external)

	i2s_conf_reset();

	// Switch on Slave mode.
	// I2S_CONF_REG -> I2S_RX_SLAVE_MOD
	// Set to 1 to enable slave mode.
	I2S0.conf.rx_slave_mod = 1;

	// Enable parallel mode
	// I2S_CONF2_REG -> I2S_LCD_END
	// Set to 1 to enable LCD mode.
	I2S0.conf2.lcd_en = 1;

	// Use HSYNC/VSYNC/HREF to control sampling
	// I2S_CONF2_REG -> I2S_CAMERA_EN
	// Set to 1 to enable camera mode.
	I2S0.conf2.camera_en = 1;


	// Configure clock divider
	I2S0.clkm_conf.clkm_div_a   = 1;
	I2S0.clkm_conf.clkm_div_b   = 0;
	I2S0.clkm_conf.clkm_div_num = 2;

	// I2S_FIFO_CONF_REG -> I2S_DSCR_EN
	// FIFO will sink data to DMA
	I2S0.fifo_conf.dscr_en = 1;

	// FIFO configuration
	// I2S_FIFO_CONF_REG -> RX_FIFO_MOD
	I2S0.fifo_conf.rx_fifo_mod          = 1; // 0-3???

	// I2S_FIFO_CONF_REG -> RX_FIFO_MOD_FORCE_EN
	I2S0.fifo_conf.rx_fifo_mod_force_en = 1;

	// I2S_CONF_CHAN_REG -> I2S_RX_CHAN_MOD
	I2S0.conf_chan.rx_chan_mod          = 1;

	// Clear flags which are used in I2S serial mode
	// I2S_SAMPLE_RATE_CONF_REG -> I2S_RX_BITS_MOD
	I2S0.sample_rate_conf.rx_bits_mod = 0;

	// I2S_CONF_REG -> I2S_RX_RIGHT_FIRST
	I2S0.conf.rx_right_first = 0;
	//I2S0.conf.rx_right_first = 0;

	// I2S_CONF_REG -> I2S_RX_MSB_RIGHT
	I2S0.conf.rx_msb_right   = 0;
	//I2S0.conf.rx_msb_right   = 1;

	// I2S_CONF_REG -> I2S_RX_MSB_SHIFT
	I2S0.conf.rx_msb_shift   = 0;
	//I2S0.conf.rx_msb_shift   = 1;

	// I2S_CONF_REG -> I2S_RX_MSB_MONO
	I2S0.conf.rx_mono        = 0;

	// I2S_CONF_REG -> I2S_RX_SHORT_SYNC
	I2S0.conf.rx_short_sync  = 0;

	I2S0.timing.val          = 0;


	ESP_LOGD(LOG_TAG, "Initializing %d descriptors", desc_count);
	DMABuffer* pFirst = new DMABuffer(); // TODO: POTENTIAL LEAK
	DMABuffer* pLast = pFirst;
	for (int i = 1; i < desc_count; i++) {
		DMABuffer* pNewDMABuffer = new DMABuffer();  // TODO: POTENTIAL LEAK
		pLast->setNext(pNewDMABuffer);
		pLast = pNewDMABuffer;
	}
	pLast->setNext(pFirst);
	pCurrentDMABuffer = pFirst;

	// I2S_RX_EOF_NUM_REG
	I2S0.rx_eof_num	  = sample_count;

	// I2S_IN_LINK_REG -> I2S_INLINK_ADDR
	I2S0.in_link.addr	= (uint32_t) pFirst;

	// I2S_IN_LINK_REG -> I2S_INLINK_START
	I2S0.in_link.start   = 1;

	I2S0.int_clr.val	 = I2S0.int_raw.val;
	I2S0.int_ena.val	 = 0;
	I2S0.int_ena.in_done = 1;

	// Register the interrupt handler.
	esp_intr_alloc(
			ETS_I2S0_INTR_SOURCE,
			ESP_INTR_FLAG_INTRDISABLED | ESP_INTR_FLAG_LEVEL1 | ESP_INTR_FLAG_IRAM,
			&i2s_isr,
			this,
			&i2s_intr_handle);

	m_dmaSemaphore.take();
	// Start the interrupt handler
	esp_intr_enable(i2s_intr_handle);

	I2S0.conf.rx_start = 1;

	/*
	while(1) {
		m_dmaSemaphore.wait();
		uint32_t dataLength = pLastDMABuffer->getLength();
		ESP_LOGD(LOG_TAG, "Got a DMA buffer; length=%d", dataLength);
		//pLastDMABuffer->dump();
		uint8_t *pData = new uint8_t[dataLength];
		pLastDMABuffer->getData(pData, dataLength);
		GeneralUtils::hexDump(pData, dataLength);
		delete[] pData;
		m_dmaSemaphore.take();
	}
	*/

	ESP_LOGD(LOG_TAG, "<< cameraMode");
}
Exemple #9
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;
}
int digitalLeds_initStrands(strand_t strands [], int numStrands)
{
  #if DEBUG_ESP32_DIGITAL_LED_LIB
    snprintf(digitalLeds_debugBuffer, digitalLeds_debugBufferSz,
             "%sdigitalLeds_init numStrands = %d\n", digitalLeds_debugBuffer, numStrands);
  #endif

  localStrands = strands;
  localStrandCnt = numStrands;
  if (localStrandCnt < 1 || localStrandCnt > 8) {
    return -1;
  }

  DPORT_SET_PERI_REG_MASK(DPORT_PERIP_CLK_EN_REG, DPORT_RMT_CLK_EN);
  DPORT_CLEAR_PERI_REG_MASK(DPORT_PERIP_RST_EN_REG, DPORT_RMT_RST);

  RMT.apb_conf.fifo_mask = 1;  // Enable memory access, instead of FIFO mode
  RMT.apb_conf.mem_tx_wrap_en = 1;  // Wrap around when hitting end of buffer

  for (int i = 0; i < localStrandCnt; i++) {
    strand_t * pStrand = &localStrands[i];
    ledParams_t ledParams = ledParamsAll[pStrand->ledType];

    pStrand->pixels = static_cast<pixelColor_t*>(malloc(pStrand->numPixels * sizeof(pixelColor_t)));
    if (pStrand->pixels == nullptr) {
      return -1;
    }

    pStrand->_stateVars = static_cast<digitalLeds_stateData*>(malloc(sizeof(digitalLeds_stateData)));
    if (pStrand->_stateVars == nullptr) {
      return -1;
    }
    digitalLeds_stateData * pState = static_cast<digitalLeds_stateData*>(pStrand->_stateVars);

    pState->buf_len = (pStrand->numPixels * ledParams.bytesPerPixel);
    pState->buf_data = static_cast<uint8_t*>(malloc(pState->buf_len));
    if (pState->buf_data == nullptr) {
      return -1;
    }

    rmt_set_pin(
      static_cast<rmt_channel_t>(pStrand->rmtChannel),
      RMT_MODE_TX,
      static_cast<gpio_num_t>(pStrand->gpioNum));
  
    RMT.conf_ch[pStrand->rmtChannel].conf0.div_cnt = DIVIDER;
    RMT.conf_ch[pStrand->rmtChannel].conf0.mem_size = 1;
    RMT.conf_ch[pStrand->rmtChannel].conf0.carrier_en = 0;
    RMT.conf_ch[pStrand->rmtChannel].conf0.carrier_out_lv = 1;
    RMT.conf_ch[pStrand->rmtChannel].conf0.mem_pd = 0;
  
    RMT.conf_ch[pStrand->rmtChannel].conf1.rx_en = 0;
    RMT.conf_ch[pStrand->rmtChannel].conf1.mem_owner = 0;
    RMT.conf_ch[pStrand->rmtChannel].conf1.tx_conti_mode = 0;  //loop back mode
    RMT.conf_ch[pStrand->rmtChannel].conf1.ref_always_on = 1;  // use apb clock: 80M
    RMT.conf_ch[pStrand->rmtChannel].conf1.idle_out_en = 1;
    RMT.conf_ch[pStrand->rmtChannel].conf1.idle_out_lv = 0;
  
    RMT.tx_lim_ch[pStrand->rmtChannel].limit = MAX_PULSES;
  
    // RMT config for transmitting a '0' bit val to this LED strand
    pState->pulsePairMap[0].level0 = 1;
    pState->pulsePairMap[0].level1 = 0;
    pState->pulsePairMap[0].duration0 = ledParams.T0H / (RMT_DURATION_NS * DIVIDER);
    pState->pulsePairMap[0].duration1 = ledParams.T0L / (RMT_DURATION_NS * DIVIDER);
    
    // RMT config for transmitting a '0' bit val to this LED strand
    pState->pulsePairMap[1].level0 = 1;
    pState->pulsePairMap[1].level1 = 0;
    pState->pulsePairMap[1].duration0 = ledParams.T1H / (RMT_DURATION_NS * DIVIDER);
    pState->pulsePairMap[1].duration1 = ledParams.T1L / (RMT_DURATION_NS * DIVIDER);

    RMT.int_ena.val |= tx_thr_event_offsets[pStrand->rmtChannel];  // RMT.int_ena.ch<n>_tx_thr_event = 1;
    RMT.int_ena.val |= tx_end_offsets[pStrand->rmtChannel];  // RMT.int_ena.ch<n>_tx_end = 1;
  }
  
  esp_intr_alloc(ETS_RMT_INTR_SOURCE, 0, handleInterrupt, nullptr, &rmt_intr_handle);

  for (int i = 0; i < localStrandCnt; i++) {
    strand_t * pStrand = &localStrands[i];
    digitalLeds_resetPixels(pStrand);
  }

  return 0;
}