STM32主/从SPI通信使用HAL_SPI_TransmitReceive()

Posted

技术标签:

【中文标题】STM32主/从SPI通信使用HAL_SPI_TransmitReceive()【英文标题】:STM32 Master/Slave SPI communication using HAL_SPI_TransmitReceive() 【发布时间】:2021-11-22 01:48:37 【问题描述】:

我正在尝试使用 HAL_SPI_TransmitReceive() 测试 SPI 主机,因为我将与 SPI 从机进行通信。 SPI 从设备尚不可用。我有两个正在使用的 STM32F4 NUCLEO。一个将是主设备,一个假装是从设备。我已将它们连接在一起并使用以下代码:

Master_main.c

SPI_HandleTypeDef hspi2;

static void MX_SPI2_Init(void);

uint8_t spi_tx_data[9];
uint8_t spi_rx_data[9];

int main(void)

  MX_SPI2_Init();

  spi_tx_data[0] = 0;
  spi_tx_data[1] = 0;
  spi_tx_data[2] = 0;
  spi_tx_data[3] = 0;
  spi_tx_data[4] = 0;
  spi_tx_data[5] = 0;
  spi_tx_data[6] = 0;
  spi_tx_data[7] = 0;
  spi_tx_data[8] = 1;

  while (1)
  
      static HAL_StatusTypeDef spi_error = HAL_OK;

      static uint32_t PreviousTicks_GreenLight = 0U;
      uint32_t CurrentTicks_GreenLight = HAL_GetTick();
      if((CurrentTicks_GreenLight - PreviousTicks_GreenLight) >= 500u)
      
          HAL_GPIO_TogglePin(LD1_GPIO_Port, LD1_Pin);
          PreviousTicks_GreenLight = CurrentTicks_GreenLight;
      

      static uint8_t runonce = 0;
      if (HAL_GPIO_ReadPin(USER_Btn_GPIO_Port, USER_Btn_Pin) != 0) 
          if (runonce != 1)

              HAL_GPIO_WritePin(GPIO_ADR1_GPIO_Port, GPIO_ADR1_Pin, 0);
              HAL_GPIO_TogglePin(LD2_GPIO_Port, LD2_Pin);
              spi_error = HAL_SPI_TransmitReceive(&hspi2,spi_tx_data, spi_rx_data,18,0x01);
              HAL_Delay(1);
              HAL_GPIO_TogglePin(LD2_GPIO_Port, LD2_Pin);
              HAL_GPIO_WritePin(GPIO_ADR1_GPIO_Port, GPIO_ADR1_Pin, 1);

              runonce = 1;
          
       else 
          runonce = 0;
      

      static uint32_t PreviousTicks_RedLight = 0U;
      uint32_t CurrentTicks_RedLight = HAL_GetTick();
      if((CurrentTicks_RedLight - PreviousTicks_RedLight) >= 500u)
      
          if ( spi_error == HAL_TIMEOUT || spi_error == HAL_ERROR)
              HAL_GPIO_TogglePin(LD3_GPIO_Port, LD3_Pin);
           else 
              if ( spi_rx_data[8] == 2)
                  HAL_GPIO_TogglePin(LD2_GPIO_Port, LD2_Pin);
              
          
          PreviousTicks_RedLight = CurrentTicks_RedLight;
      
  


static void MX_SPI2_Init(void)

  hspi2.Instance = SPI2;
  hspi2.Init.Mode = SPI_MODE_MASTER;
  hspi2.Init.Direction = SPI_DIRECTION_2LINES;
  hspi2.Init.DataSize = SPI_DATASIZE_8BIT;
  hspi2.Init.CLKPolarity = SPI_POLARITY_LOW;
  hspi2.Init.CLKPhase = SPI_PHASE_1EDGE;
  hspi2.Init.NSS = SPI_NSS_SOFT;
  hspi2.Init.BaudRatePrescaler = SPI_BAUDRATEPRESCALER_2;
  hspi2.Init.FirstBit = SPI_FIRSTBIT_MSB;
  hspi2.Init.TIMode = SPI_TIMODE_DISABLE;
  hspi2.Init.CRCCalculation = SPI_CRCCALCULATION_DISABLE;
  hspi2.Init.CRCPolynomial = 10;
  if (HAL_SPI_Init(&hspi2) != HAL_OK)
  
    Error_Handler();
  

Slave_main.c

uint8_t spi_tx_data[9];
uint8_t spi_rx_data[9];

uint8_t rx_cmplt = 0;

void HAL_SPI_RxCpltCallback (SPI_HandleTypeDef *hspi) 
    static uint8_t rx_counter = 0;

    if ( rx_counter == 8 )
        rx_cmplt = 1;
     else 
        rx_counter++;
    
    HAL_GPIO_TogglePin(LD2_GPIO_Port, LD2_Pin);


int main(void)

  MX_SPI2_Init();

  spi_tx_data[0] = 0;
  spi_tx_data[1] = 0;
  spi_tx_data[2] = 0;
  spi_tx_data[3] = 0;
  spi_tx_data[4] = 0;
  spi_tx_data[5] = 0;
  spi_tx_data[6] = 0;
  spi_tx_data[7] = 0;
  spi_tx_data[8] = 2;

  while (1)
  
      static HAL_StatusTypeDef spi_error = HAL_OK;

      static uint32_t PreviousTicks_GreenLight = 0U;
      uint32_t CurrentTicks_GreenLight = HAL_GetTick();
      if((CurrentTicks_GreenLight - PreviousTicks_GreenLight) >= 1000u)
      
          HAL_GPIO_TogglePin(LD1_GPIO_Port, LD1_Pin);
          PreviousTicks_GreenLight = CurrentTicks_GreenLight;
      

      static uint8_t runonce = 0;
      if (HAL_GPIO_ReadPin(GPIO_ADR1_GPIO_Port, GPIO_ADR1_Pin) != 1) 
          if (runonce != 1)

              HAL_GPIO_TogglePin(LD2_GPIO_Port, LD2_Pin);
              //spi_error = HAL_SPI_TransmitReceive_IT(&hspi2, spi_tx_data, spi_rx_data, 18);
              spi_error = HAL_SPI_TransmitReceive(&hspi2,spi_tx_data, spi_rx_data,18,0x01);
              HAL_GPIO_TogglePin(LD2_GPIO_Port, LD2_Pin);

              runonce = 1;
          
       else 
          runonce = 0;
      

      static uint32_t PreviousTicks_RedLight = 0U;
      uint32_t CurrentTicks_RedLight = HAL_GetTick();
      if((CurrentTicks_RedLight - PreviousTicks_RedLight) >= 500u)
      
          if ( rx_cmplt == 1 )
              if (spi_rx_data[8] == 1)
                  HAL_GPIO_WritePin(LD2_GPIO_Port, LD2_Pin, 1);
               else 
                  HAL_GPIO_WritePin(LD3_GPIO_Port, LD3_Pin, 1);
              
           else 
              if ( spi_error == HAL_TIMEOUT || spi_error == HAL_ERROR)
                  HAL_GPIO_TogglePin(LD3_GPIO_Port, LD3_Pin);
                  if (spi_error == HAL_TIMEOUT)
                      HAL_GPIO_TogglePin(LD2_GPIO_Port, LD2_Pin);
                  
              
          

          PreviousTicks_RedLight = CurrentTicks_RedLight;
      
  


static void MX_SPI2_Init(void)

  hspi2.Instance = SPI2;
  hspi2.Init.Mode = SPI_MODE_SLAVE;
  hspi2.Init.Direction = SPI_DIRECTION_2LINES;
  hspi2.Init.DataSize = SPI_DATASIZE_8BIT;
  hspi2.Init.CLKPolarity = SPI_POLARITY_LOW;
  hspi2.Init.CLKPhase = SPI_PHASE_1EDGE;
  hspi2.Init.NSS = SPI_NSS_SOFT;
  hspi2.Init.FirstBit = SPI_FIRSTBIT_MSB;
  hspi2.Init.TIMode = SPI_TIMODE_DISABLE;
  hspi2.Init.CRCCalculation = SPI_CRCCALCULATION_DISABLE;
  hspi2.Init.CRCPolynomial = 10;
  if (HAL_SPI_Init(&hspi2) != HAL_OK)
  
    Error_Handler();
  

为了简单起见,我去掉了各种 MCU 初始化。我之前曾尝试使用 IT 版本的 TransmitRecieve,但没有成功传输。如果我多次使用主设备上的 USER 按钮,它确实会激活从设备上的相应灯,但不会在主设备上激活。

【问题讨论】:

如果我正确理解您的查询,数据没有被传输,有时 LED 没有触发,对吧?可以在从机端接收到中断的时候打印数据吗?但是,如果您在任何时候都没有获取数据,那么您还应该检查一次有线连接和 pinmux。 鉴于我调用 TransmitReceive() 的方式,我认为数据不同步。我调试了 master,整个 rx 数组在所有 9 个位置都填充了 255。我无法调试从机,因为调试器不支持连接两个设备。从机在我当前的配置中从主板上拉电。 使用断点和数据检查,我发现我连一瞬间的好数据都没有。检查我的电线,因为我之前确实遇到过其中一个问题。 有人推荐了一个逻辑分析仪,这是我一直需要的,而不是示波器,明天应该有。应该有助于更多地查看问题。 正确,逻辑分析仪应该在这里提供帮助。我怀疑SPI线的长度也是如此。 SPI 线的长度不能很长。尽量减少长度,检查时钟信号是否正确。 【参考方案1】:

我怀疑我遇到了某种接线问题。我在混音中添加了一个面包板并插入了一些 LED,不知何故,在将所有东西连接起来之后,主从都正常工作。我确实更改了代码以使用 SPI 的一些 STM 示例,但那是在面包板之前。

Slave.c

  static uint8_t runonce = 0;
  if (HAL_GPIO_ReadPin(GPIO_ADR1_GPIO_Port, GPIO_ADR1_Pin) != 1) 
      if (runonce != 1)


      switch(HAL_SPI_TransmitReceive(&hspi2, spi_tx_data, spi_rx_data, 9, 5000))
        
        case HAL_OK:
          /* Communication is completed_____________________________________________*/
          /* Compare the sent and received buffers */
          if(spi_rx_data[8] == 1)
          
              HAL_GPIO_TogglePin(LD1_GPIO_Port, LD1_Pin);
          

          break;

        case HAL_TIMEOUT:
          /* A Timeout occurred_____________________________________________________*/
          /* Call Timeout Handler */
            HAL_GPIO_TogglePin(LD2_GPIO_Port, LD2_Pin);
          break;

          /* An Error occurred______________________________________________________*/
        case HAL_ERROR:
          /* Call Timeout Handler */
            HAL_GPIO_TogglePin(LD3_GPIO_Port, LD3_Pin);
          break;

        default:
          break;
        

      runonce = 1;
  
   else 
      runonce = 0;
  

Master.c

  static uint8_t runonce = 0;
  if (HAL_GPIO_ReadPin(USER_Btn_GPIO_Port, USER_Btn_Pin) != 0) 
      if (runonce != 1)


          HAL_GPIO_WritePin(GPIO_ADR1_GPIO_Port, GPIO_ADR1_Pin, 0);
          HAL_Delay(0x01);

          switch(HAL_SPI_TransmitReceive(&hspi2, spi_tx_data, spi_rx_data, 9, 5000))
            
            case HAL_OK:
              /* Communication is completed_____________________________________________*/
              /* Compare the sent and received buffers */
              if(spi_rx_data[8] == 2)
              
                  HAL_GPIO_WritePin(LD1_GPIO_Port, LD1_Pin, 1);
              

              break;

            case HAL_TIMEOUT:
              /* A Timeout occurred_____________________________________________________*/
              /* Call Timeout Handler */
                HAL_GPIO_WritePin(LD2_GPIO_Port, LD2_Pin, 1);
              break;

              /* An Error occurred______________________________________________________*/
            case HAL_ERROR:
              /* Call Timeout Handler */
                HAL_GPIO_WritePin(LD3_GPIO_Port, LD3_Pin, 1);
              break;

            default:
              break;
            

          //HAL_GPIO_TogglePin(LD2_GPIO_Port, LD2_Pin);

          HAL_GPIO_WritePin(GPIO_ADR1_GPIO_Port, GPIO_ADR1_Pin, 1);

          runonce = 1;
      
   else 
      runonce = 0;
  

【讨论】:

以上是关于STM32主/从SPI通信使用HAL_SPI_TransmitReceive()的主要内容,如果未能解决你的问题,请参考以下文章

stm32的两个spi互相通信,从spi正确接收主spi数据,但主spi收不到从spi数据(MISO一直为低),是何原因?

请教STM32的SPI通信中的CRC校验问题 SPI

STM32和ADXL345之间的SPI通信

带有 STM32F7 的 SPI 从模式和循环 DMA

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

STM32与FPGA进行SPI通信