Page 1 of 1

SPI Latency

Posted: Wed May 15, 2019 9:08 am
by Chorus2020
I'm using the SPI to communicate with 5 quad channel DACs connected as shown in the diagram. On a timer interrupt I write to all the DAC channels with successive spi_device_polling_transmits. This is required to latch the data into the DAC registers with the CS line. What I need to to is reduce the latency between the initial interrupt and the latency between each polling transmit. Is this possible?
Alternatively is there a way to toggle the CS single within a polling transmit?
SPI latency.png
Scope traces
SPI latency.png (35.96 KiB) Viewed 853 times
DAC circuit.png
DAC circuit
DAC circuit.png (17.24 KiB) Viewed 853 times
Couple of code snippets:
From interrupt:

Code: Select all

    xTaskNotifyFromISR( xHandlingTask, ulStatusRegister, eSetBits, &xHigherPriorityTaskWoken);
From SpiTask:

Code: Select all

void SpiTask(void *pvParameters)
	uint32_t ulInterruptStatus;

    	xTaskNotifyWait( 0x00, ULONG_MAX, &ulInterruptStatus, portMAX_DELAY );

		if((spiSendNow == 1) && (dataReady == 1)){
			gpio_set_level(GPIO_NUM_15, 1);

			spi_device_polling_transmit(spi, &transA[bufferIndex]);
			spi_device_polling_transmit(spi, &transB[bufferIndex]);
			spi_device_polling_transmit(spi, &transC[bufferIndex]);
			spi_device_polling_transmit(spi, &transD[bufferIndex]);

			spiSendNow = 0;
			if(bufferIndex >= BUFFER_SIZE)	bufferIndex = 0;
			gpio_set_level(GPIO_NUM_15, 0);

Re: SPI Latency

Posted: Wed May 15, 2019 11:28 am
by username
you are sending data 4 times which is causing part of your delay.

Code: Select all

			spi_device_polling_transmit(spi, &transA[bufferIndex]);
			spi_device_polling_transmit(spi, &transB[bufferIndex]);
			spi_device_polling_transmit(spi, &transC[bufferIndex]);
			spi_device_polling_transmit(spi, &transD[bufferIndex]);
You can minimize that by sending your data as a chunk instead.
You need to look into .length. Here is a snippit where I write 32 bits at once.

Code: Select all

void mcp23S17_WriteWord(uint8_t address, uint8_t reg, uint16_t data)
	uint8_t tx_data[8];
	tx_data[0] = MCP23S17_MANUF_CHIP_ADDRESS | (address << 1);
	tx_data[1] = reg;
	tx_data[2] = (uint8_t)(data);
	tx_data[3] = (uint8_t)(data >> 8);
	trans_mcp23S17.tx_buffer = tx_data;	
	trans_mcp23S17.rx_buffer = NULL;
	trans_mcp23S17.length = 32;

	ESP_ERROR_CHECK(spi_device_polling_transmit(handle_spi_mcp23S17, &trans_mcp23S17)); 

NOTE: the only reason why I am setting .rx_buffer, and .length every call to this function is because I have other SPI devices on this channel and that device uses 8 bits to talk to it so i change trans_mcp23S17.rx_buffer = 8; & trans_mcp23S17.length = 8;
In your situation you just need to set those up once, and do not need to keep setting them.
In your spi init you can set them there and not bother with them again like I did.

below is the init for the SPI channel.

Code: Select all

// mcp23S17 Setup
spi_bus_config_t buscfg;
spi_bus_config_t buscfg_mcp23S17;
spi_device_interface_config_t devcfg_mcp23S17;
spi_device_handle_t handle_spi_mcp23S17;
spi_transaction_t trans_mcp23S17;

Code: Select all


void initHSPI(void)
	xMutex_SPIClock = xSemaphoreCreateMutex();
    // spi_bus_config_t
	buscfg.sclk_io_num = SPI_PIN_NUM_CLK;  		// GPIO pin for Spi CLocK signal, or -1 if not used.
	buscfg.mosi_io_num = SPI_PIN_NUM_MOSI;  	// GPIO pin for Master Out Slave In (=spi_d) signal, or -1 if not used.
	buscfg.miso_io_num = SPI_PIN_NUM_MISO; 	    // GPIO pin for Master In Slave Out (=spi_q) signal, or -1 if not used.O
	buscfg.quadwp_io_num = -1; 					// GPIO pin for WP (Write Protect) signal which is used as D2 in 4-bit communication modes, or -1 if not used.
	buscfg.quadhd_io_num = -1; 					// GPIO pin for HD (HolD) signal which is used as D3 in 4-bit communication modes, or -1 if not used.
	buscfg.max_transfer_sz = 0; 				// Maximum transfer size, in bytes. Defaults to 4094 if 0.
    ESP_ERROR_CHECK(spi_bus_initialize(HSPI_HOST, &buscfg, 1));		// Use dma_chan 1
     * Set Up SPI for MCP23S17 (IO Expander)
    devcfg_mcp23S17.address_bits = 0;
	devcfg_mcp23S17.command_bits = 0;
	devcfg_mcp23S17.dummy_bits = 0;
	devcfg_mcp23S17.mode = 0;
	devcfg_mcp23S17.duty_cycle_pos = 0;
	devcfg_mcp23S17.cs_ena_posttrans = 0;
	devcfg_mcp23S17.cs_ena_pretrans = 0;
	devcfg_mcp23S17.clock_speed_hz = 100000;   //10000
	devcfg_mcp23S17.spics_io_num = SPI_PIN_NUM_CS;
	devcfg_mcp23S17.flags = 0;
	devcfg_mcp23S17.queue_size = 7;
	devcfg_mcp23S17.pre_cb = NULL;
	devcfg_mcp23S17.post_cb = NULL;
	ESP_ERROR_CHECK(spi_bus_add_device(HSPI_HOST, &devcfg_mcp23S17, &handle_spi_mcp23S17));

	// spi_transaction_t
	trans_mcp23S17.flags = 0;
	trans_mcp23S17.addr = 0;
	trans_mcp23S17.cmd = 0;
	trans_mcp23S17.length = 32;  		// 4 bytes
 	trans_mcp23S17.rxlength = 24;
	trans_mcp23S17.tx_buffer = NULL;
	trans_mcp23S17.rx_buffer = NULL;

I am assuming gpio_set_level(GPIO_NUM_15, 1); you are using as your Cs pin. You can eliminate this as well as this is taking time too, by setting this in your SPI init as well. Change .spics_io_num = SPI_PIN_NUM_CS; to your CS pin.

Re: SPI Latency

Posted: Wed May 15, 2019 1:27 pm
by Chorus2020
I think I did not make it clear in my first post the reason why I transmit the data with 4 separate polling transmit calls. It's because after writing to channel A of the DACs the CS pin must go high to latch in the data. Then I can write to channel B and so on. GPIO 15 is just a debug pin, the CS is setup as you suggested.