首先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);
}
}