zephyr icon indicating copy to clipboard operation
zephyr copied to clipboard

STM32 SPI does not work properly with async + interrupts

Open arturkow2000 opened this issue 2 years ago • 0 comments

Describe the bug Seems like STM32 shifts the data out during master inactivity period instead of waiting for master to enable SPI clock. After starting the transfer with spi_transceive_signal SPI interrupt is fired almost immediatelly, and this results in call to spi_stm32_shift_s. SPI activity can be seen on logic analyzer (no clock signal present, but MISO goes low). The data sent is 0xaa, maybe due lack of clock it is sent as 0x00.

DSView 21 09 2022 20_42_10

When master starts transmission no SPI interrupts occur at all, also, the signal passed to spi_transceive_signal is never raised leaving the program frozen forever.

Target platform is Nucleo-L476RG, we are using latest Zephyr from master (f7251073c4b6b7a6b391bc413aaa8683fee41af9) however on latest stable this problem was also present.

To Reproduce Enable CONFIG_SPI_STM32_INTERRUPT and CONFIG_SPI_ASYNC, try sending anything through spi_transceive_signal

static struct k_poll_signal spi_completion_signal = K_POLL_SIGNAL_INITIALIZER(spi_completion_signal);
static struct k_poll_event spi_completion_event = K_POLL_EVENT_INITIALIZER(K_POLL_TYPE_SIGNAL,
						     K_POLL_MODE_NOTIFY_ONLY,
						     &spi_completion_signal);
const uint8_t one_buf[SPI_MAX_DATA_SIZE] = {
	[0] = 0xaa,
	[1] = 0xaa,
	[2] = 0xaa,
	[3] = 0xaa,
	[4 ... SPI_MAX_DATA_SIZE - 1] = 0x00
};

      struct spi_buf spi_buf = {
		.buf = buf,
		.len = len,
	};

	struct spi_buf_set spi_buf_set = {
		.buffers = &spi_buf,
		.count = 1,
	};

	const struct spi_buf spi_one_buf = {
		.buf = (void *)one_buf,
		.len = len,
	};

	const struct spi_buf_set spi_one_buf_set = {
		.buffers = &spi_one_buf,
		.count = 1,
	};

	assert(len <= SPI_MAX_DATA_SIZE);

	int ret = spi_transceive_signal(spi_dev, &spi_cfg, &spi_one_buf_set, &spi_buf_set, &spi_completion_signal);
	if (ret < 0) {
		LOG_ERR("spi_transceive_signal failed: %d", ret);
		return -1;
	}
	LOG_INF("SPI ret: %d", ret);
	k_poll(&spi_completion_event, 1, K_FOREVER);
	LOG_INF("EVENT");

Seems like the problem occurs with interrupts and sync API too - with interrupts disabled transfer times out and we retry it, with interrupt enabled ISR is called during inactivity period and when host starts transfer nothing happens. Also, in that case spi_transceive hangs forever.

Expected behavior Once the transfer is queued we expect

  • the driver to wait till master initiates communication and due the transfer in the right time
  • signal transfer completion

Impact We need high frequencies (up to 24 MHz) for STM32 to operate as TPM module. SPI sync API is not appriopriate to to the timeout behaviour described above, slave may not be quick enough to queue next transfer after timeout which causes instability and host receives corrupted data. We hoped that async API would provide us the ability to queue the transfer and let the SPI controller handle everything else.

So far we couldn't achieve stability at anything higher that 1 MHz, despite our attempts to improve communication it still doesn't work properly, communication always goes unstable after a few seconds at most.

arturkow2000 avatar Sep 21 '22 19:09 arturkow2000