SPI从机将数据读入stm32上的缓冲区?

Posted

技术标签:

【中文标题】SPI从机将数据读入stm32上的缓冲区?【英文标题】:SPI slave read data into buffer on stm32? 【发布时间】:2020-03-01 13:21:09 【问题描述】:

我正在尝试通过 SPI 设置 esp32(主)和 stm32(从)之间的通信。 esp32在micropython下运行,发送四个字节,例如 spi.write_readinto(b'\x31\x32\x33\x34', buf)

stm32' 代码在这里(而不是这个我使用SPI_InitDef.SPI_NSS = SPI_NSS_Soft;

void SPI_Init(void) 
    ...

    //  initialize SPI slave
    // for slave, no need to define SPI_BaudRatePrescaler
    SPI_InitDef.SPI_Direction = SPI_Direction_2Lines_FullDuplex;
    SPI_InitDef.SPI_Mode = SPI_Mode_Slave;
    SPI_InitDef.SPI_DataSize = SPI_DataSize_8b; // 8-bit transactions
    SPI_InitDef.SPI_FirstBit = SPI_FirstBit_MSB; // MSB first
    SPI_InitDef.SPI_CPOL = SPI_CPOL_Low; // CPOL = 0, clock idle low
    SPI_InitDef.SPI_CPHA = SPI_CPHA_2Edge; // CPHA = 1
    SPI_InitDef.SPI_NSS = SPI_NSS_Hard; // use hardware SS
    SPI_InitDef.SPI_BaudRatePrescaler = SPI_BaudRatePrescaler_64; // APB2 72/64 = 1.125 MHz

    SPI_InitDef.SPI_CRCPolynomial = 7;
    SPI_Init(SPI1, &SPI_InitDef);

    SPI_Cmd(SPI1, ENABLE);

    NVIC_EnableIRQ(SPI1_IRQn);
    //Тут мы разрешаем прерывание по приему
    SPI_I2S_ITConfig(SPI1, SPI_I2S_IT_RXNE, ENABLE);


void main() 
    /* Setup SysTick Timer for 10ms interrupts  */
    if (SysTick_Config(SystemCoreClock / 100))
    
    /* Capture error */
    while (1);
    
    /* Configure the SysTick handler priority */
    NVIC_SetPriority(SysTick_IRQn, 0x0);

    SPI_Init();

    while(1) 
        while(!SPI_I2S_GetFlagStatus(SPI1, SPI_I2S_FLAG_RXNE));
        for (u8 i=0; i<4; i++) 
            printf("0x%02x ", SPI_I2S_ReceiveData(SPI1));
        
        printf("\r\n");
    

但是当我发送四个字节 0x31 0x32 0x33 0x34(分析器确认字节已发送)并且我的 stm 只得到 0x31 0x32 0x31 0x32

UPD 我使用 std periph 库,SPI_I2S_ReceiveData 是从 SPI 读取字节的本机方法。

uint16_t SPI_I2S_ReceiveData    (   SPI_TypeDef *   SPIx     )  
Returns the most recent received data by the SPIx/I2Sx peripheral.

Parameters:
SPIx,:  To select the SPIx/I2Sx peripheral, where x can be: 1, 2 or 3 in SPI mode or 2 or 3 in I2S mode or I2Sxext for I2S full duplex mode.
Return values:
The     value of the received data.

uint16_t SPI_I2S_ReceiveData    (   SPI_TypeDef *   SPIx     )  
Returns the most recent received data by the SPIx/I2Sx peripheral.

Parameters:
SPIx,:  To select the SPIx/I2Sx peripheral, where x can be: 1, 2 or 3 in SPI mode or 2 or 3 in I2S mode or I2Sxext for I2S full duplex mode.
Return values:
The     value of the received data.

但也许我在读取所有数据之前退出了 IRQ。我发现运行while循环直到最后一个字节的传输完成

【问题讨论】:

旁注 - 您应该避免从事务中打印调试输出。调试信息通道可能会停止您的 SPI 事务,从而丢失数据。 可能有原因导致它意外工作。谢谢,我试试 请在此站点上包含代码。 关于您的编辑:我不明白您为什么要谈论中断。您的代码正在轮询而不是中断(或者我可能错过了什么)。 "我发现一直运行while循环,直到最后一个字节的传输完成"是什么意思?知道接收是否完成的唯一方法是使用我描述的设置轮询标志 EOT。 RXNE 不会告诉您接收完成,只有一个字节(或字)可用。 【参考方案1】:

我认为下面的代码不正确(但我不知道SPI_I2S_ReceiveData这个函数在做什么):

while(!SPI_I2S_GetFlagStatus(SPI1, SPI_I2S_FLAG_RXNE));
for (u8 i=0; i<4; i++) 
  printf("0x%02x ", SPI_I2S_ReceiveData(SPI1));

一旦准备好读取一个字节,您就退出while。我假设SPI_I2S_ReceiveData 只读取 SPI FIFO。在这种情况下,当可能只收到一两个字节时,您尝试读取 4 个字节。

您没有准确说明您使用的是哪种 STM32,所以我描述的是 STM32H7 的 SPI(据我所知,它在其他 STM32 中应该非常相似)。

要在从模式下设置接收,您应该特别定义以下 3 个参数:

所谓的“帧”的长度(一次读取/写入的字节数)。这是 HAL 数据结构中的 SPI_DataSize 字段,这里是 8 位。 传输次数 (TSIZE),它指定何时生成传输结束事件。它以“帧”的数量表示。此参数必须在每次接收前通过寄存器 SPI.CR2 设置(当然前提是您知道要接收的字节数)。 “FIFO 阈值”。它指定生成事件 RXP 或 TXP 的频率。您可以更改此参数以减少软件的工作量,但只接收 4 个字节并没有影响。

在您的情况下,我认为您应该将传输大小设置为 4(4 个字节)并等待设置 EOT 标志。设置后,您只需从 SPI 接收寄存器读取 4 个字节(顺便说一下,您可以一次读取所有 4 个字节)。

我建议你不要使用 HAL,而是通过读/写寄存器来编写自己的 SPI 接收/发送例程。它不是一个非常复杂的外围设备(因此不会花费您很多时间),并且您会准确地理解它是如何工作的(而不是深入研究 HAL)。

【讨论】:

以上是关于SPI从机将数据读入stm32上的缓冲区?的主要内容,如果未能解决你的问题,请参考以下文章

STM32F337:SPI从机帧同步

STM32F405 裸机 SPI 从机 - MISO 数据有时会混乱

STM32的SPI从机接收数据错误是怎么回事

stm32 spi全双工从机模式

是否可以在 STM32F3 上使用 DMA 通过 SPI 传输无限数据?

FPGA作为从机与STM32进行SPI协议通信---Verilog实现