当前位置: 首页 > 知识库问答 >
问题:

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

井镜
2023-03-14

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

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

但我不想在每次ADC读取时(SPI准备就绪)循环中等待。因为它会影响我的实时计算。这就是为什么我把我的功能切换到DMA。

我的新算法如下:

  1. 使用下降沿触发器生成外部GPIO中断,以检测外部ADC的数据就绪输出。
  2. 将芯片选择引脚设为低电平以开始与外部ADC通信
  3. 使用HAL_SPI_Transmit_DMA()函数向外部ADC发送read命令。
  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*/

}

共有1个答案

邰博远
2023-03-14

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

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

 类似资料:
  • 垃圾回收 我们对生产中花了很多时间来调整垃圾回收。垃圾回收的关注点与Java大致相似,尽管一些惯用的Scala代码比起惯用的Java代码会容易产生更多(短暂的)垃圾——函数式风格的副产品。Hotspot的分代垃圾收集通常使这不成问题,因为短暂的(short-lived)垃圾在大多情形下会被有效的释放掉。 在谈GC调优话题前,先看看这个Attila的报告,它阐述了我们在GC方面的一些经验。 Scal

  • 对于开发者来说,JavaScript 的内存管理是自动的、无形的。我们创建的原始值、对象、函数……这一切都会占用内存。 当我们不再需要某个东西时会发生什么?JavaScript 引擎如何发现它并清理它? 可达性(Reachability) JavaScript 中主要的内存管理概念是 可达性。 简而言之,“可达”值是那些以某种方式可访问或可用的值。它们一定是存储在内存中的。 这里列出固有的可达值的

  • Kubernetes 垃圾收集器的角色是删除指定的对象,这些对象曾经有但以后不再拥有 Owner 了。 注意:垃圾收集是 beta 特性,在 Kubernetes 1.4 及以上版本默认启用。 Owner 和 Dependent 一些 Kubernetes 对象是其它一些的 Owner。例如,一个 ReplicaSet 是一组 Pod 的 Owner。具有 Owner 的对象被称为是 Owner

  • 垃圾收集,引用计数,显式分配 和所有的现代语言一样,OCaml提供垃圾收集器,所以你不用像C/C++一样显式地分配和释放内存。 JWZ在他的文章 "Java sucks" rant(Java蛋疼(怒)!): 第一个好家伙是Java没有 free()。其他的都没有所谓了。这几乎掩盖了所有的缺点,不管有多糟糕, 这个有点让后续文档基本都没有意义了,但是...(译注:但是啥大家自己看吧) OCaml的垃

  • 我在STM32F4发现板上安装了STM32F4407VGT6控制器。我尝试使用SPI DMA从AD7683 ADC读取数据,但DMA接收缓冲区始终为空(全部为零)。在轮询模式下,一切正常,但我必须读取一个16位样本值作为3x 8位SPI值,并使用位移位。这也许就是问题所在。我的采样频率为48 kHz,在每个周期内必须读取三个spi值以获得一个ADC样本。 AD7683时序图见第5页的数据表。 引脚

  • 主要内容:垃圾回收器函数,实例Lua 采用了自动内存管理。 这意味着你不用操心新创建的对象需要的内存如何分配出来, 也不用考虑在对象不再被使用后怎样释放它们所占用的内存。 Lua 运行了一个垃圾收集器来收集所有死对象 (即在 Lua 中不可能再访问到的对象)来完成自动内存管理的工作。 Lua 中所有用到的内存,如:字符串、表、用户数据、函数、线程、 内部结构等,都服从自动管理。 Lua 实现了一个增量标记-扫描收集器。 它使用