STM32 SPI接收DMA正在获取垃圾数据

时间:2018-03-22 06:55:19

标签: embedded stm32 stm32f4discovery adc stm32f7

在我的项目中,我使用主SPI通信从外部ADC获取模拟数据。我的MCU是STM32F746ZGTX。我的系统需要实时工作,所以我使用了SPI DMA接收和发送功能。

我正在使用SPI轮询正确读取所有外部ADC数据,而不使用DMA。在SPI轮询中,我首先将控制字节发送到外部ADC,然后程序在while(SPI_Ready)循环中等待,然后开始接收所有ADC数据。这种情况非常有效。

但我不想在每次读取ADC的while(SPI_Ready)循环中等待。因为它会影响我的实时计算。这就是我将我的功能切换到DMA的原因。

我的新算法如下所示:

  1. 使用下降沿触发生成外部GPIO中断,以检测外部ADC的数据就绪输出。
  2. 使芯片选择引脚为低电平以启动与外部ADC的通信
  3. 使用HAL_SPI_Transmit_DMA()功能将读取命令发送到外部ADC。
  4. HAL_SPI_TxCpltCallback功能中,触发HAL_SPI_Receive_DMA()
  5. HAL_SPI_RxCpltCallback功能中,缓冲器接收ADC数据并使芯片选择引脚为高电平以终止通信。
  6. 当我使用这个算法时,我的ADC缓冲区总是得到0xFF值。似乎即使ADC没有发送原始数据,由于触发接收DMA,我的MCU发送时钟并将所有逻辑高信号检测为接收数据。

    我在下面分享我的代码。如果您对我错了有任何建议,请分享您的意见。

     /* SPI1 init function */
     static void MX_SPI1_Init(void)
     {
    
       hspi1.Instance = SPI1;
       hspi1.Init.Mode = SPI_MODE_MASTER;
       hspi1.Init.Direction = SPI_DIRECTION_2LINES;
       hspi1.Init.DataSize = SPI_DATASIZE_8BIT;
       hspi1.Init.CLKPolarity = SPI_POLARITY_HIGH;
       hspi1.Init.CLKPhase = SPI_PHASE_2EDGE;
       hspi1.Init.NSS = SPI_NSS_SOFT;
       hspi1.Init.BaudRatePrescaler = SPI_BAUDRATEPRESCALER_8;
       hspi1.Init.FirstBit = SPI_FIRSTBIT_MSB;
       hspi1.Init.TIMode = SPI_TIMODE_DISABLE;
       hspi1.Init.CRCCalculation = SPI_CRCCALCULATION_DISABLE;
       hspi1.Init.CRCPolynomial = 7;
       hspi1.Init.CRCLength = SPI_CRC_LENGTH_DATASIZE;
       hspi1.Init.NSSPMode = SPI_NSS_PULSE_DISABLE;
       if (HAL_SPI_Init(&hspi1) != HAL_OK)
       {
    
       }
      }
    
      void HAL_SPI_MspInit(SPI_HandleTypeDef* hspi)
      {
    
         GPIO_InitTypeDef GPIO_InitStruct;
         if(hspi->Instance==SPI1)
         {
            /* USER CODE BEGIN SPI1_MspInit 0 */
    
            /* USER CODE END SPI1_MspInit 0 */
            /* Peripheral clock enable */
            __HAL_RCC_SPI1_CLK_ENABLE();
    
            /**SPI1 GPIO Configuration    
            PA5     ------> SPI1_SCK
            PA6     ------> SPI1_MISO
            PB5     ------> SPI1_MOSI 
            */
            GPIO_InitStruct.Pin = GPIO_PIN_5|GPIO_PIN_6;
            GPIO_InitStruct.Mode = GPIO_MODE_AF_PP;
            GPIO_InitStruct.Pull = GPIO_NOPULL;
            GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_VERY_HIGH;
            GPIO_InitStruct.Alternate = GPIO_AF5_SPI1;
            HAL_GPIO_Init(GPIOA, &GPIO_InitStruct);
    
            GPIO_InitStruct.Pin = GPIO_PIN_5;
            GPIO_InitStruct.Mode = GPIO_MODE_AF_PP;
            GPIO_InitStruct.Pull = GPIO_NOPULL;
            GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_VERY_HIGH;
            GPIO_InitStruct.Alternate = GPIO_AF5_SPI1;
            HAL_GPIO_Init(GPIOB, &GPIO_InitStruct);
    
            /* SPI1 DMA Init */
            /* SPI1_RX Init */
            hdma_spi1_rx.Instance = DMA2_Stream0;
            hdma_spi1_rx.Init.Channel = DMA_CHANNEL_3;
            hdma_spi1_rx.Init.Direction = DMA_PERIPH_TO_MEMORY;
            hdma_spi1_rx.Init.PeriphInc = DMA_PINC_DISABLE;
            hdma_spi1_rx.Init.MemInc = DMA_MINC_ENABLE;
            hdma_spi1_rx.Init.PeriphDataAlignment = DMA_PDATAALIGN_BYTE;
            hdma_spi1_rx.Init.MemDataAlignment = DMA_MDATAALIGN_BYTE;
            hdma_spi1_rx.Init.Mode = DMA_NORMAL;
            hdma_spi1_rx.Init.Priority = DMA_PRIORITY_LOW;
            hdma_spi1_rx.Init.FIFOMode = DMA_FIFOMODE_DISABLE;
            if (HAL_DMA_Init(&hdma_spi1_rx) != HAL_OK)
            {
    
            }
    
            __HAL_LINKDMA(hspi,hdmarx,hdma_spi1_rx);
    
            /* SPI1_TX Init */
            hdma_spi1_tx.Instance = DMA2_Stream3;
            hdma_spi1_tx.Init.Channel = DMA_CHANNEL_3;
            hdma_spi1_tx.Init.Direction = DMA_MEMORY_TO_PERIPH;
            hdma_spi1_tx.Init.PeriphInc = DMA_PINC_DISABLE;
            hdma_spi1_tx.Init.MemInc = DMA_MINC_ENABLE;
            hdma_spi1_tx.Init.PeriphDataAlignment = DMA_PDATAALIGN_BYTE;
            hdma_spi1_tx.Init.MemDataAlignment = DMA_MDATAALIGN_BYTE;
            hdma_spi1_tx.Init.Mode = DMA_NORMAL;
            hdma_spi1_tx.Init.Priority = DMA_PRIORITY_LOW;
            hdma_spi1_tx.Init.FIFOMode = DMA_FIFOMODE_DISABLE;
            if (HAL_DMA_Init(&hdma_spi1_tx) != HAL_OK)
            {
    
            }
    
             __HAL_LINKDMA(hspi,hdmatx,hdma_spi1_tx);
    
            /* SPI1 interrupt Init */
            HAL_NVIC_SetPriority(SPI1_IRQn, 0, 0);
            HAL_NVIC_EnableIRQ(SPI1_IRQn);
            /* USER CODE BEGIN SPI1_MspInit 1 */
    
            /* USER CODE END SPI1_MspInit 1 */
    
            /* USER CODE BEGIN SPI1_MspInit 1 */
    
            /* USER CODE END SPI1_MspInit 1 */
            }
    
            }
    
    void HAL_SPI_MspDeInit(SPI_HandleTypeDef* hspi)
    {
    
      if(hspi->Instance==SPI1)
      {
      /* USER CODE BEGIN SPI1_MspDeInit 0 */
    
      /* USER CODE END SPI1_MspDeInit 0 */
      /* Peripheral clock disable */
      __HAL_RCC_SPI1_CLK_DISABLE();
    
      /**SPI1 GPIO Configuration    
       PA5     ------> SPI1_SCK
       PA6     ------> SPI1_MISO
       PB5     ------> SPI1_MOSI 
      */
      HAL_GPIO_DeInit(GPIOA, GPIO_PIN_5|GPIO_PIN_6);
    
      HAL_GPIO_DeInit(GPIOB, GPIO_PIN_5);
    
      /* USER CODE BEGIN SPI1_MspDeInit 1 */
      /* Peripheral DMA DeInit*/
      HAL_DMA_DeInit(hspi->hdmarx);
      HAL_DMA_DeInit(hspi->hdmatx);
    
      /* Peripheral interrupt Deinit*/
      HAL_NVIC_DisableIRQ(SPI2_IRQn);
     /* USER CODE END SPI1_MspDeInit 1 */
     }
    }
    
    /* External Interrupt for data ready output of ADC */
    void EXTI15_10_IRQHandler(void)
    {
      /* USER CODE BEGIN EXTI15_10_IRQn 0 */
    
      /* USER CODE END EXTI15_10_IRQn 0 */
      HAL_GPIO_EXTI_IRQHandler(GPIO_PIN_15);
      /* USER CODE BEGIN EXTI15_10_IRQn 1 */
    
      adc_selectADC(); /* Make Chip Select pin low */
      HAL_SPI_Transmit_DMA (&hspi1, &controlByte, 1);
    }
    
    void HAL_SPI_TxCpltCallback(SPI_HandleTypeDef *hspi)
    {
       if (hspi->Instance == hspi1.Instance)
       {
           /* Transmit is completed */
           /* Trigger receive DMA to get raw data from external ADC */
           HAL_SPI_Receive_DMA (&hspi1, (uint8_t*)adcRecBuffer, 24);
       }
    }
    
    void HAL_SPI_RxCpltCallback(SPI_HandleTypeDef *hspi)
    {
       if (hspi->Instance == hspi1.Instance)
       {
           /* Receive is completed */
           adc_deselectADC(); /* Make Chip Select pin high */
       }         
    }
    
    ***Working Algorithm without using DMA or Interrupt:***
    
    /* External Interrupt for data ready output of ADC */
    void EXTI15_10_IRQHandler(void)
    {
      /* USER CODE BEGIN EXTI15_10_IRQn 0 */
    
      /* USER CODE END EXTI15_10_IRQn 0 */
      HAL_GPIO_EXTI_IRQHandler(GPIO_PIN_15);
      /* USER CODE BEGIN EXTI15_10_IRQn 1 */
    
      adc_selectADC(); /* Make Chip Select pin low */
    
      while(HAL_SPI_GetState(&hspi1) != HAL_SPI_STATE_READY);
      HAL_SPI_Transmit(&hspi1, &controlByte, 1,1);
      while(HAL_SPI_GetState(&hspi1) != HAL_SPI_STATE_READY);
    
      while(HAL_SPI_GetState(&hspi1) != HAL_SPI_STATE_READY);
      HAL_SPI_Receive(&hspi1, (uint8_t*)adcRecBuffer, 24, 1);
      while(HAL_SPI_GetState(&hspi1) != HAL_SPI_STATE_READY);
    
      adc_deselectADC(); /* Make Chip Select pin high*/
    
    }
    

1 个答案:

答案 0 :(得分:0)

SPI通常是全双工的,这意味着“读取”实际上是主机产生时钟并发送零的信号。在STM HAL实现中,“接收”功能仅发送您在读取缓冲区中拥有的数据。可能为零,可能是垃圾,ADC会将其解释为某些命令并进入某种错误状态。

尝试首先使用命令ID(“控制字节”)对2个25字节缓冲区进行TransmitReceive,然后在TX缓冲区中进行24个零字节的传输。作为响应,您应该获得大小为25的RX缓冲区,可以在其中丢弃第一个字节。这样,您只需要处理RXCplt中断,即可在其中释放ADC CS引脚。