SPI driver huge malfunction

KanyeKanye
Posts: 54
Joined: Mon Dec 05, 2016 12:34 am

SPI driver huge malfunction

Postby KanyeKanye » Tue Dec 03, 2019 12:21 am

I struggle with a huge SPI driver malfunction:
Lets take DS1390 RTC (but some problems occurs with all devices I've tested).
Chip supports SPI in mode 1 or 3, up to 4MHz. After receiving address it replies with corresponding bytes of datetime.

Code: Select all

static void ds1390_spi_init() {
	spi_bus_config_t buscfg = {
		.miso_io_num=PIN_NUM_SPI_MISO, .mosi_io_num=PIN_NUM_SPI_MOSI, .sclk_io_num=PIN_NUM_SPI_CLK,
		.quadwp_io_num=-1, .quadhd_io_num=-1,
		.max_transfer_sz=32 // Does it take address and command bits?
	};
	spi_device_interface_config_t devcfg = {
		.spics_io_num=PIN_NUM_SPI_RTC_CS,
		.clock_speed_hz=40000, // 4kHz used, 4MHz max
		.mode=1, // SPI MODE 1
		.command_bits=8, // 8-bit command length
		.queue_size=1
	};

	spi_bus_initialize(VSPI_HOST, &buscfg, 1 /*dma_chan - 0 no DMA*/);
	ESP_ERROR_CHECK(spi_bus_add_device(VSPI_HOST, &devcfg, &ds1390_spi));
}

Code: Select all

void ds1390_get_time() {
	char * buffer = heap_caps_malloc(8, MALLOC_CAP_DMA);
	spi_transaction_t transaction;
	memset(&transaction, 0, sizeof(transaction));
	transaction.cmd = DS1390_REG_SECONDS;
	transaction.rx_buffer = buffer;
	transaction.length = 8 * 8;
	assert(spi_device_polling_transmit(ds1390_spi, &transaction) == ESP_OK);

	ESP_LOGI(TAG, "DS1390 Time: %02x %02x %02x %02x %02x %02x %02x %02x", buffer[0], buffer[1], buffer[2], buffer[3], buffer[4], buffer[5], buffer[6], buffer[7]);
	free(buffer);
}
Below there is a screen from logic analyzer:
Image

As you can see:
- CLK mode is shifted,
- MOSI transmit some random bytes after command phase
- when higher frequencies (4MHz) CLK duty seems inconsistent

When I write similar program for arduino everything works.
Below I post logic analyzer screen for arduino program that shows how it should work (same configuration, same environment, same board):
Image
Last edited by KanyeKanye on Thu Dec 05, 2019 2:06 pm, edited 2 times in total.

ESP_Sprite
Posts: 9764
Joined: Thu Nov 26, 2015 4:08 am

Re: SPI driver huge malfunction [mode "inverted", random bytes transmitted]

Postby ESP_Sprite » Tue Dec 03, 2019 2:45 am

The mode does not quite look inverted to me; cs looks like it is going down in the same cycle as clk, but given the fact that you're only sampling at 8MHz, you may be missing the delay between the two. You can artifically increase this delay by setting cs_ena_pretrans to non-zero (1-16) in devcfg, but you will need to configure your device in halfduplex mode (devcfg.flags=SPI_DEVICE_HALFDUPLEX) for that to work.

For the random MOSI bytes: Not 100% sure, but given that you have not set data to be output during the data phase, it's allowed to just send whatever, and for speed we may just do that. Set specific data you want to output if you need the pin to have a specific value.

For the clk duty cycle thing: 4MHz is an integer multiple of the APB clock (80MHz) so the duty cycle should be exactly 50%. However, you're using a LA to measure this signal: if you have degradation of the signal because of stray capacitance, the 'real' clock signal probably more resembles a sine wave, and if you don't have your LA set to exactly 50% of the *actual* amplitude of this signal, you get a weird duty cycle.

In general: Note that the ESP-IDF SPI driver is entirely different from the Arduino driver, so up to a certain point, differences in (non protocol critical) behaviour are to be expected.

KanyeKanye
Posts: 54
Joined: Mon Dec 05, 2016 12:34 am

Re: SPI driver huge malfunction [mode "inverted", random bytes transmitted]

Postby KanyeKanye » Tue Dec 03, 2019 2:05 pm

Thanks @ESP_Sprite for your reply
I have changed mode to HALF-DUPLEX and I've added CS delay (.cs_ena_pretrans), as you suggested. Basic communication started working but sending anything messes up in communication:

Code: Select all

static void ds1390_spi_init() {
	spi_bus_config_t buscfg = {
		.miso_io_num=PIN_NUM_SPI_MISO, .mosi_io_num=PIN_NUM_SPI_MOSI, .sclk_io_num=PIN_NUM_SPI_CLK,
		.quadwp_io_num=-1, .quadhd_io_num=-1,
		.max_transfer_sz=256 // ************
	};
	spi_device_interface_config_t devcfg = {
		.spics_io_num=PIN_NUM_SPI_RTC_CS,
		.clock_speed_hz=4000000, // 4MHz max
		.mode=1, // SPI MODE 1
		.command_bits=8, // 8-bit command length
		.queue_size=1,
		.input_delay_ns = 80, // SCLK to Data Valid tCDD 80ns max
		.cs_ena_pretrans = 2, // CS to SCLK Setup tCC 400ns min; 2*1/4MHz=500ns
		.flags = SPI_DEVICE_HALFDUPLEX
	};

	spi_bus_initialize(VSPI_HOST, &buscfg, 1 /*dma_chan - 0 no DMA*/);
	ESP_ERROR_CHECK(spi_bus_add_device(VSPI_HOST, &devcfg, &ds1390_spi));
}
static void ds1390_trickle_charger_init() {
	ESP_LOGI(TAG, "ds1390_trickle_charger_init");
	spi_transaction_t transaction;
	memset(&transaction, 0, sizeof(transaction));
	transaction.cmd = DS1390_REG_TRICKLE | DS1390_REG_WRITE_MASK;
	transaction.length = 8;
	transaction.flags = SPI_TRANS_USE_TXDATA;
	transaction.tx_data[0] = DS1390_TRICKLE_CHARGER_ENABLE | DS1390_TRICKLE_CHARGER_NO_DIODE | DS1390_TRICKLE_CHARGER_2K_OHM;
	assert(spi_device_polling_transmit(ds1390_spi, &transaction) == ESP_OK);
}
void ds1390_set_time() {
	ESP_LOGI(TAG, "ds1390_set_time");
	uint8_t data[8] = {
			ds1390_dec2bcd(0) /* Hundredths of Seconds */,
			ds1390_dec2bcd(23) /* Seconds */,
			ds1390_dec2bcd(24) /* Minutes */,
			ds1390_dec2bcd(16) /* Hours */,
			ds1390_dec2bcd(3) /* Day */,
			ds1390_dec2bcd(3) /* Date */,
			ds1390_dec2bcd(12) /* Month / Century */,
			ds1390_dec2bcd(19) /* Year */
	};
	spi_transaction_t transaction;
	memset(&transaction, 0, sizeof(transaction));
	transaction.cmd = DS1390_REG_100THS | DS1390_REG_WRITE_MASK;
	transaction.length = 8 * 8;
	transaction.tx_buffer = data;
	assert(spi_device_polling_transmit(ds1390_spi, &transaction) == ESP_OK);
}
void ds1390_get_time() {
	ESP_LOGI(TAG, "ds1390_get_time");
	char * buffer = heap_caps_malloc(8, MALLOC_CAP_DMA);

	spi_transaction_t transaction;
	memset(&transaction, 0, sizeof(transaction));
	transaction.cmd = DS1390_REG_SECONDS;
	transaction.rx_buffer = buffer;
	transaction.rxlength = 7 * 8;
	transaction.length = transaction.rxlength;
	assert(spi_device_polling_transmit(ds1390_spi, &transaction) == ESP_OK);

	ESP_LOGI(TAG, "DS1390 Time: %02x %02x %02x %02x %02x %02x %02x %02x", buffer[0], buffer[1], buffer[2], buffer[3], buffer[4], buffer[5], buffer[6], buffer[7]);

	free(buffer);
}
When whole executed code looks like:

Code: Select all

ds1390_initialise();
while (1) {
	ds1390_get_time();
	vTaskDelay(200 / portTICK_PERIOD_MS);
}
Log is OK, eg: DS1390 Time: 14 26 16 03 03 12 19 00

when code looks like:

Code: Select all

ds1390_initialise();
ds1390_trickle_charger_init();
while (1) {
	ds1390_get_time();
	vTaskDelay(200 / portTICK_PERIOD_MS);
}
Log is INVALID - last byte is missing: DS1390 Time: 14 26 16 03 03 12 00 00

when code looks like:

Code: Select all

ds1390_initialise();
ds1390_set_time();
while (1) {
	ds1390_get_time();
	vTaskDelay(200 / portTICK_PERIOD_MS);
}
Log is INVALID - first byte is ok, all the rest looks random: DS1390 Time: 25 00 00 00 cc 6c fb 3f
Adding delay (vTaskDelay or ets_delay_us) before while loop changes nothing.

All bytes are valid, doesnt mater if I add ds1390_trickle_charger_init / ds1390_set_time or not. Logic analyzer screen:
Image

Who is online

Users browsing this forum: Bing [Bot] and 99 guests