1

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

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

但我不想while(SPI_Ready)在每次 ADC 读数中循环等待。因为它影响我的实时计算。这就是我将功能切换到 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 数据并使片选引脚为高电平以终止通信。

当我使用这个算法时,我的 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*/

}
4

1 回答 1

0

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

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

于 2019-08-26T06:38:43.507 回答