五、关于zephyr上使用spi通信时(如使用dma+回调)需要的配置

首先app.overlay的配置

使用dma+回调方式

c 复制代码
&dma1 {

	status = "okay";
};

&dmamux1 {

	status = "okay";

};

&spi2 {
	/* 使用 PLL1_Q 作为 SPI2 时钟源 */

	pinctrl-0 = <&spi2_nss_pb12 &spi2_sck_pb13
	&spi2_miso_pb14 &spi2_mosi_pb15>;
	pinctrl-names = "default";
	status = "okay";
	dmas = <&dmamux1 0 40 (STM32_DMA_PERIPH_TX | STM32_DMA_PRIORITY_HIGH)>,
	       <&dmamux1 1 39 (STM32_DMA_PERIPH_RX | STM32_DMA_PRIORITY_HIGH)>;
	dma-names = "tx", "rx";
};

在pro.conf中添加

c 复制代码
CONFIG_NOCACHE_MEMORY=y
CONFIG_SPI_ASYNC=y
CONFIG_SPI=y
CONFIG_SPI_STM32=y
CONFIG_SPI_STM32_DMA=y
CONFIG_SPI_STM32_INTERRUPT=y

main.c测试

c 复制代码
#define PACKET_SIZE 64
#define TEST_COUNT  10

/* 测试数据 */
static const uint8_t tx_pattern[PACKET_SIZE] = {
	0xAA, 0x55, 0x12, 0x34, 0xDE, 0xAD, 0xBE, 0xEF, 0x00, 0x11, 0x22, 0x33, 0x44,
	0x55, 0x66, 0x77, 0x88, 0x99, 0xAA, 0xBB, 0xCC, 0xDD, 0xEE, 0xFF, 0x01, 0x23,
	0x45, 0x67, 0x89, 0xAB, 0xCD, 0xEF, 0xFE, 0xDC, 0xBA, 0x98, 0x76, 0x54, 0x32,
	0x10, 0xA5, 0x5A, 0xA5, 0x5A, 0xA5, 0x5A, 0xA5, 0x5A, 0xFF, 0xEE, 0xDD, 0xCC,
	0xBB, 0xAA, 0x99, 0x88, 0x77, 0x66, 0x55, 0x44, 0x33, 0x22, 0x11, 0x00};
__nocache static uint8_t tx_buffer[PACKET_SIZE];
__nocache static uint8_t rx_buffer[PACKET_SIZE];

/* 同步信号量(用于等待异步回调) */
static K_SEM_DEFINE(spi_sync_sem, 0, 1);

/* 传输结果(回调中写入) */
static int spi_transfer_result;

/* SPI 配置 */
static struct spi_config spi2_cfg = {
	.frequency = 16000000,
	.operation = SPI_OP_MODE_MASTER | SPI_WORD_SET(8) | SPI_TRANSFER_MSB,
	.slave = 0,
};
/* ================================================================
 * 异步传输完成回调
 *   在中断上下文中调用,仅做标记+释放信号量,不能做耗时操作
 * ================================================================ */
static void spi_transfer_done(const struct device *dev, int result, void *user_data)
{
	spi_transfer_result = result;
	k_sem_give(&spi_sync_sem);
}
/* ================================================================
 * 单次异步收发(封装:提交 + 等待回调)
 *   返回 0 成功,负值为错误码
 * ================================================================ */
static int spi_transceive_async_once(const struct device *dev, const struct spi_config *cfg,
				     const uint8_t *tx_data, size_t tx_len, uint8_t *rx_data,
				     size_t rx_len, int32_t timeout_ms)
{
	/* 构建 TX buf */
	struct spi_buf tx_buf = {
		.buf = (void *)tx_data,
		.len = tx_len,
	};
	struct spi_buf_set tx_set = {
		.buffers = &tx_buf,
		.count = 1,
	};

	/* 构建 RX buf */
	struct spi_buf rx_buf = {
		.buf = rx_data,
		.len = rx_len,
	};
	struct spi_buf_set rx_set = {
		.buffers = &rx_buf,
		.count = 1,
	};

	/* 提交异步传输 */
	int ret = spi_transceive_cb(dev, cfg, &tx_set, &rx_set, spi_transfer_done, NULL);
	if (ret < 0) {
		printk("spi_transceive_cb submit failed: %d\n", ret);
		return ret;
	}

	/* 等待回调唤醒 */
	ret = k_sem_take(&spi_sync_sem, K_MSEC(timeout_ms));
	if (ret != 0) {
		printk("SPI transfer timeout! (%d)\n", ret);
		return -ETIMEDOUT;
	}

	/* 检查传输结果 */
	if (spi_transfer_result < 0) {
		printk("SPI transfer error: %d\n", spi_transfer_result);
		return spi_transfer_result;
	}

	return 0;
}
void my_thread(void *arg1, void *arg2, void *arg3)
{
int pass = 0, fail = 0;

	k_msleep(500);

	printk("\n");
	printk("============================================\n");
	printk("   SPI2 + DMA Async Loopback Test\n");
	printk("   Connect: PB14(MISO) <-> PB15(MOSI)\n");
	printk("   Packet size: %d bytes\n", PACKET_SIZE);
	printk("============================================\n\n");

	const struct device *spi2 = DEVICE_DT_GET(DT_NODELABEL(spi2));
	if (!device_is_ready(spi2)) {
		printk("ERROR: SPI2 device not ready!\n");
		while (1) {
			k_sleep(K_FOREVER);
		}
	}
	printk("SPI2 device ready\n");

	printk("tx_buffer addr: %p\n", tx_buffer);
	printk("rx_buffer addr: %p\n", rx_buffer);

	memcpy(tx_buffer, tx_pattern, PACKET_SIZE);

	/* 循环测试 */
	for (int i = 0; i < TEST_COUNT; i++) {

		memset(rx_buffer, 0, PACKET_SIZE);

		int ret = spi_transceive_async_once(spi2, &spi2_cfg, tx_buffer, PACKET_SIZE,
						    rx_buffer, PACKET_SIZE, 11000);
		if (ret < 0) {
			printk("[%2d] FAIL: error %d\n", i, ret);
			fail++;
			continue;
		}

		if (memcmp(tx_buffer, rx_buffer, PACKET_SIZE) == 0) {
			printk("[%2d] PASS\n", i);
			pass++;
		} else {
			printk("[%2d] FAIL: data mismatch\n", i);
			printk("  byte[0]: tx=0x%02X rx=0x%02X\n", tx_buffer[0], rx_buffer[0]);
			fail++;
		}

		k_msleep(100);
	}
	printk("\n============================================\n");
	printk("   Result: PASS=%d  FAIL=%d  (Total=%d)\n", pass, fail, TEST_COUNT);
	printk("============================================\n");
	while (1) {
		k_msleep(1000);
	}
}

使用正常的收发方式(即收发一体),上面配置中取消dma相关即可。

main.c

c 复制代码
#define PACKET_SIZE   8

static const uint8_t tx_pattern[PACKET_SIZE] = {0xAA, 0x55, 0x12, 0x34, 0xDE, 0xAD, 0xBE, 0xEF};
static uint8_t rx_buffer[PACKET_SIZE];

static struct spi_config spi2_cfg_fast = {
	.frequency = 16000000,
	.operation = SPI_TRANSFER_LSB| SPI_WORD_SET(8) | SPI_TRANSFER_MSB|SPI_MODE_CPOL|SPI_MODE_CPHA,
	.slave = 0,
};

void my_thread(void *arg1, void *arg2, void *arg3)
{

	int pass = 0, fail = 0;

	printk("=== SPI2 Interrupt Loopback Test ===\n");
	printk("Connect PB14(MISO) <-> PB15(MOSI)\n\n");

	const struct device *spi2 = DEVICE_DT_GET(DT_NODELABEL(spi2));
	if (!device_is_ready(spi2)) {
		printk("SPI2 not ready!\n");
	}
	printk("SPI2 ready\n\n");

	for (int i = 0; i < 10; i++) {
		memset(rx_buffer, 0, PACKET_SIZE);
		struct spi_buf tx = {.buf = (void *)tx_pattern, .len = PACKET_SIZE};
		struct spi_buf rx = {.buf = rx_buffer, .len = PACKET_SIZE};
		struct spi_buf_set tx_set = {.buffers = &tx, .count = 1};
		struct spi_buf_set rx_set = {.buffers = &rx, .count = 1};

		int ret = spi_transceive(spi2, &spi2_cfg_fast, &tx_set, &rx_set);
		if (ret < 0) {
			printk("Iter %d: fail\n", i);
			fail++;
			continue;
		}
		if (memcmp(tx_pattern, rx_buffer, PACKET_SIZE) == 0) {
			printk("Iter %d: OK\n", i);
			pass++;
		} else {
			printk("Iter %d: mismatch\n", i);
			fail++;
		}
	}
	printk("\n=== Result: Pass=%d Fail=%d ===\n", pass, fail);
	while (1) {
		k_msleep(1000);
	}
}