你遇到的两个SPI通信问题非常典型,与SPI协议的全双工本质以及Cypress HAL库对Slave角色操作方式的特定要求密切相关。以下是对每个问题的详细分析和解决方案:
核心问题根源:
cyhal_spi_transfer_async(及其调用的底层API)需要Slave提前准备好即将要发送的数据(或数据指针)。只有当Slave准备好一个有效的“Tx Buffer”时,它的内部状态才会准备好接收Master发起的传输,包括接收数据的处理(触发RX中断)。问题1:Slave接收中断不触发
原因分析: 你之前的尝试(例如在main中只做接收相关操作)使得Slave没有准备好要发送的数据。因此:
为什么cyhal_spi_slave_write()能触发中断? 这个函数同时初始化了发送!它内部调用cyhal_spi_transfer_async时,会传入有效的发送数据(或缓冲指针)。这使得Slave准备好处理双向传输:当Master发SCLK时,Slave既能接收(读MOSI)也能发送(写MISO),自然能触发RX中断(告诉你收到了Master的数据)和TX中断(告诉你发送给Master的数据已完成)。即使你的目的是接收,API要求Slave必须提供一个有效的发送缓冲(即使里面是虚拟数据)。
解决方案: Slave必须启动一个双向异步传输(即使只想接收)
#define BUFFER_SIZE 256 // 或者你需要的最大传输大小
cy_rslt_t rslt;
uint8_t rx_buf[BUFFER_SIZE]; // 接收缓冲区
uint8_t tx_buf_dummy[BUFFER_SIZE]; // 虚拟发送缓冲区 - 核心!
// 在SPI Slave初始化之后,启动第一次异步传输
// 这里我们只想接收,所以我们提供一个 "dummy" 发送缓冲区
memset(tx_buf_dummy, 0x00, sizeof(tx_buf_dummy)); // 可选,但清晰,通常设为0x00
rslt = cyhal_spi_transfer_async(&spi_slave, tx_buf_dummy, BUFFER_SIZE, rx_buf, BUFFER_SIZE, spi_event_handler);
if (rslt != CY_RSLT_SUCCESS) {
printf("启动异步传输失败: 0x%08Xn", rslt);
CY_ASSERT(0);
}关键点:
tx_buf_dummy: 即使你没有实际数据要发送给Master,也必须提供一个有效的缓冲区给cyhal_spi_transfer_async的tx参数。这就是你的“虚拟”发送缓冲区。rx_buf: 这是你用来存放从Master接收到的数据的缓冲区。spi_event_handler: 这是传输事件的回调函数(中断上下文),接收数据完成的中断会在这里触发。中断处理函数 (spi_event_handler) 示例:
void spi_event_handler(void *handler_arg, cyhal_spi_event_t event) {
(void) handler_arg; // 防止未使用参数警告
static bool transfer_complete = false;
if (event & CYHAL_SPI_IRQ_DATA) { // 数据收发传输完成
transfer_complete = true;
}
if (event & CYHAL_SPI_IRQ_ERROR) { // 传输错误
printf("SPI传输错误!n");
// 处理错误...
}
// 当传输完成事件发生
if (transfer_complete) {
transfer_complete = false; // 重置标志
// 1. 处理接收到的数据!
// 此时 rx_buf 中的前 '传输长度' 个字节是Master发来的数据
printf("收到%d字节数据。内容:n", BUFFER_SIZE); // 通常长度由协议决定
for (int i = 0; i < BUFFER_SIZE; i++) {
printf("%02X ", rx_buf[i]);
}
printf("n");
// 2. 准备下一次传输 - 至关重要!
// 再次启动异步传输 (包括虚拟发送缓冲区)
memset(tx_buf_dummy, 0x00, BUFFER_SIZE); // 可选,清理虚拟发送缓冲
rslt = cyhal_spi_transfer_async(&spi_slave, tx_buf_dummy, BUFFER_SIZE, rx_buf, BUFFER_SIZE, spi_event_handler);
if (rslt != CY_RSLT_SUCCESS) {
printf("重启异步传输失败: 0x%08Xn", rslt);
// 处理错误,可能需要复位通讯
}
}
}rx_buf) 是在传输完成事件 (CYHAL_SPI_IRQ_DATA) 被处理时进行的。cyhal_spi_transfer_async) 在同一个spi_event_handler内部处理完数据后!这样才能让Slave准备好接收Master的下一次传输请求。否则Slave会再次陷入“无发送准备”状态,导致下一次Master的传输无法被正确处理,RX中断也不会触发。问题2:关于数据未发送却持续收到0xFF
原因分析:
解决方案:
tx_buf_dummy),如问题1的解决方案所示。0x00)。这样:0x00 (0x00, 0x00, ...)。0x00) 而不是之前的全1 (0xFF),这是虚拟缓冲机制开始正常工作的明确标志! 这意味着Slave现在成功提供了数据(虽然是虚拟的)。next_tx_buf)。spi_event_handler处理完当前接收的数据后,将下一次传输的发送缓冲区指针指向 next_tx_buf 而不是 tx_buf_dummy。cyhal_spi_transfer_async时,将真实的next_tx_buf及其长度作为发送参数传入。总结与关键步骤
main初始化SPI Slave之后,立即调用 cyhal_spi_transfer_async 启动第一个异步传输。0x00)的缓冲区作为 cyhal_spi_transfer_async 的 tx 参数。spi_event_handler 里检测 CYHAL_SPI_IRQ_DATA 事件,处理 rx_buf 中的数据(Master发来的)。cyhal_spi_transfer_async。cyhal_spi_transfer_async 的 tx 参数指向真实数据缓冲区即可(同时仍需提供有效的 rx_buf)。ssel_pol (CPOL/Clock Polarity)cpha (CPHA/Clock Phase)data_bits (通常8位)oversample (如使用应匹配)bit_order (MSB先还是LSB先)特别注意: SPI模式通常定义为 (CPOL, CPHA):
请务必查阅 TC387 SPI Master配置 和 CYW20829 SPI Slave HAL 库文档 (cyhal_spi_cfg_t) 确认双方设置的CPOL/CPHA具体值相同。
通过实施这个 “使用虚拟发送缓冲的持续双向异步传输” 策略,应该能够成功触发Slave的RX中断,并正确处理Master发来的数据,同时解决Master接收端收到0xFF的问题。这种方法符合SPI协议标准和Cypress HAL对Slave操作的要求。
举报
更多回帖