【STM32与ZYNQ7020】SPI从机模式下MISO数据回传机制详解:首个字节揭秘历史数据与当前帧交互
背景条件
分别定义有函数:ZYNQ7020_ReadWrite32B(uint32_t data)
和ZYNQ7020_ReadWrite64B(uint64_t data)
/**
* @brief Using SPI to transmit four bytes and return the received bytes.
* @param data: The 32-bit data SPI will transmit.
* @return received data.
**/
uint32_t ZYNQ7020_ReadWrite32B(uint32_t data)
{
uint8_t SendBuffer[4];
uint8_t RecvBuffer[4];
SendBuffer[0] = (data>>16) & 0xff;
SendBuffer[1] = (data>>24) & 0xff;
SendBuffer[2] = (data ) & 0xff;
SendBuffer[3] = (data>> 8) & 0xff;
HAL_SPI_TransmitReceive(&hspi2, SendBuffer, RecvBuffer, 2, (uint32_t)-1);
return (((uint32_t)RecvBuffer[0])<<16) | (((uint32_t)RecvBuffer[1])<<24) | ((uint32_t)RecvBuffer[2]) | (RecvBuffer[3]<<8);
}
/**
* @brief Using SPI to transmit four bytes and return the received bytes.
* @param data: The 64-bit data SPI will transmit.
* @return received data.
**/
uint64_t ZYNQ7020_ReadWrite64B(uint64_t data)
{
uint8_t SendBuffer[8];
uint8_t RecvBuffer[8];
// @TODO: 更改SendBuffer顺序
SendBuffer[0] = (data>>48) & 0xff;
SendBuffer[1] = (data>>56) & 0xff;
SendBuffer[2] = (data>>32) & 0xff;
SendBuffer[3] = (data>>40) & 0xff;
SendBuffer[4] = (data>>16) & 0xff;
SendBuffer[5] = (data>>24) & 0xff;
SendBuffer[6] = (data ) & 0xff;
SendBuffer[7] = (data>> 8) & 0xff;
HAL_SPI_TransmitReceive(&hspi2, SendBuffer, RecvBuffer, 4, (uint32_t)-1);
return (((uint64_t)RecvBuffer[0])<<48) | (((uint64_t)RecvBuffer[1])<<56) | ((uint64_t)RecvBuffer[2]<<32) | (((uint64_t)(RecvBuffer[3])<<40)) | \
(((uint64_t)RecvBuffer[4])<<16) | (((uint64_t)RecvBuffer[5])<<24) | ((uint64_t)RecvBuffer[6]) | ((uint64_t)(RecvBuffer[7]<<8));
}
首先通过ZYNQ7020发送0x00_00_09_03,下位机返回0x45_12_56_78,程序代码中,从机利用函数ZYNQ7020_ReadWrite32B(uint32_t data)
阻塞等待SPI_CS被拉低;
通过下面的逻辑分析仪抓取的结果可以看到,这时候读取和发送是正常的;
下一次读取时,通过STM32G0的PA15上升沿标志从机数据有效回传给ZYNQ7020,回传完成后会拉低PA15;从机回传会发送0x23_01_67_45_ab_89_ef_cd给主机,而主机则发送0x34_12_56_78_12_34_56_78,程序代码中,从机拉高PA15后,通过ZYNQ7020_ReadWrite64B(uint64_t data)
阻塞等待SPI_CS被拉低开始回传数据,阻塞结束,拉高PA15;
通过下面逻辑分析仪的数据可看见,传输不对,主机ZYNQ7020端由于是PL端输出逻辑,发送的数据是正确的;从机回传第一个byte错误,然后才开始传输,同时由于发送的数组是8个byte,此处是在接收到ZYNQ7020第3个byte后才开始正常传输,故此处理论上在ZYNQ7020发送完成8个byte后,从机还有两个byte未发送,所以会卡在ZYNQ7020_ReadWrite64B(uint64_t data)
内阻塞,也未能正确拉高PA15。
需要注意此处为16bit的SPI模式,故SPI寄存器是16bit
猜测问题
➡️由于当从机未能正确回传前两个byte的时候,他回传的是上一次发送的前两个byte,此处猜测可能是当回传前两个byte的时候,可能未能正确建立SPI连接,仍发送旧数据
-
首先采取的手段是将CS拉低后到SPI_CLK时钟建立的setup时间从200ns延长至300ns再到400ns,没有变化;
-
更改仅接收3个byte,可以看到此时正确地退出了阻塞,证明前面猜测的阻塞原因可能正确,是因为第一次从机回传错误的指针地址导致最后两个byte未被回传,count未更新,一直卡在
ZYNQ7020_ReadWrite64B(uint64_t data)
内。
-
重写函数
尝试,但放弃,采用直接操作寄存器,情况类似。
解决办法
- 将CS拉低后到SPI_CLK时钟建立的setup时间1.4μs
- 此处我的主机读使能信号PA15拉低后增加延时2.4us,再拉低spi_CS
- 如果还不行,可以继续增加延时尝试一下
下图为仅将CS拉低后到SPI_CLK时钟建立的setup时间增加到1μs,可见不起作用
补充
当使用16bit模式,数据打包模式
参考
STM32L462RE – SPI slave duplicated first word
作者:康