STM32 SPI DMA在外部中断时进行传输

问题描述 投票:1回答:1

我有一个通过SPI连接到STM32H7x MCU的ADC芯片。 MCU是主spi,但只要新值准备好传输,ADC就会发送“数据就绪”信号。我的旧实现(可以工作)监听外部中断(数据就绪)并调用SPI传输(KEIL驱动程序)来读回值。它传输固定的32个字节并读取32个字节的同步。到目前为止一切正常。

问题是当我将采样率提高到每秒20,000个样本以上时,它会变得太慢并且cpu被中断太多次而且我无法做任何其他事情。我的目标是每秒达到32K样本。 Keil spi驱动程序使用DMA下层,但仍然每秒32K次中断CPU太多。

我认为应该有一种方法可以配置低级DMA,以便在发生外部事件(数据就绪信号)时开始传输。我的目标是设置DMA,以便在每个DRDY信号上读取值多次(例如128次并将其写入缓冲区),然后给我一个中断。这样我只需要处理32000/128 =每秒250次,这是合理的。

我使用CubeMX创建低级SPI DMA驱动程序,到目前为止,我有这样的事情:

void HAL_SPI_MspInit(SPI_HandleTypeDef* hspi)


{

  GPIO_InitTypeDef GPIO_InitStruct;
  HAL_DMA_MuxSyncConfigTypeDef pSyncConfig;
  if(hspi->Instance==SPI2)
  {
  /* USER CODE BEGIN SPI2_MspInit 0 */

  /* USER CODE END SPI2_MspInit 0 */
    /* Peripheral clock enable */
    __HAL_RCC_SPI2_CLK_ENABLE();


/**SPI2 GPIO Configuration    
PB4     ------> SPI2_NSS
PA12    ------> SPI2_SCK
PC2     ------> SPI2_MISO
PC3     ------> SPI2_MOSI 
*/
GPIO_InitStruct.Pin = GPIO_PIN_4;
GPIO_InitStruct.Mode = GPIO_MODE_AF_PP;
GPIO_InitStruct.Pull = GPIO_NOPULL;
GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_LOW;
GPIO_InitStruct.Alternate = GPIO_AF7_SPI2;
HAL_GPIO_Init(GPIOB, &GPIO_InitStruct);

GPIO_InitStruct.Pin = GPIO_PIN_12;
GPIO_InitStruct.Mode = GPIO_MODE_AF_PP;
GPIO_InitStruct.Pull = GPIO_NOPULL;
GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_LOW;
GPIO_InitStruct.Alternate = GPIO_AF5_SPI2;
HAL_GPIO_Init(GPIOA, &GPIO_InitStruct);

GPIO_InitStruct.Pin = GPIO_PIN_2|GPIO_PIN_3;
GPIO_InitStruct.Mode = GPIO_MODE_AF_PP;
GPIO_InitStruct.Pull = GPIO_NOPULL;
GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_LOW;
GPIO_InitStruct.Alternate = GPIO_AF5_SPI2;
HAL_GPIO_Init(GPIOC, &GPIO_InitStruct);

/* SPI2 DMA Init */
/* SPI2_TX Init */
hdma_spi2_tx.Instance = DMA1_Stream0;
hdma_spi2_tx.Init.Request = DMA_REQUEST_SPI2_TX;
hdma_spi2_tx.Init.Direction = DMA_MEMORY_TO_PERIPH;
hdma_spi2_tx.Init.PeriphInc = DMA_PINC_DISABLE;
hdma_spi2_tx.Init.MemInc = DMA_MINC_DISABLE;
hdma_spi2_tx.Init.PeriphDataAlignment = DMA_PDATAALIGN_WORD;
hdma_spi2_tx.Init.MemDataAlignment = DMA_MDATAALIGN_WORD;
hdma_spi2_tx.Init.Mode = DMA_NORMAL;
hdma_spi2_tx.Init.Priority = DMA_PRIORITY_VERY_HIGH;
hdma_spi2_tx.Init.FIFOMode = DMA_FIFOMODE_DISABLE;
if (HAL_DMA_Init(&hdma_spi2_tx) != HAL_OK)
{
  _Error_Handler(__FILE__, __LINE__);
}

pSyncConfig.SyncSignalID = HAL_DMAMUX1_SYNC_EXTI0;
pSyncConfig.SyncPolarity = HAL_DMAMUX_SYNC_RISING_FALLING;
pSyncConfig.SyncEnable = ENABLE;
pSyncConfig.EventEnable = DISABLE;
pSyncConfig.RequestNumber = 1;
if (HAL_DMAEx_ConfigMuxSync(&hdma_spi2_tx, &pSyncConfig) != HAL_OK)
{
  _Error_Handler(__FILE__, __LINE__);
}

__HAL_LINKDMA(hspi,hdmatx,hdma_spi2_tx);

/* SPI2_RX Init */
hdma_spi2_rx.Instance = DMA1_Stream1;
hdma_spi2_rx.Init.Request = DMA_REQUEST_SPI2_RX;
hdma_spi2_rx.Init.Direction = DMA_PERIPH_TO_MEMORY;
hdma_spi2_rx.Init.PeriphInc = DMA_PINC_DISABLE;
hdma_spi2_rx.Init.MemInc = DMA_MINC_ENABLE;
hdma_spi2_rx.Init.PeriphDataAlignment = DMA_PDATAALIGN_WORD;
hdma_spi2_rx.Init.MemDataAlignment = DMA_MDATAALIGN_WORD;
hdma_spi2_rx.Init.Mode = DMA_CIRCULAR;
hdma_spi2_rx.Init.Priority = DMA_PRIORITY_VERY_HIGH;
hdma_spi2_rx.Init.FIFOMode = DMA_FIFOMODE_DISABLE;
if (HAL_DMA_Init(&hdma_spi2_rx) != HAL_OK)
{
  _Error_Handler(__FILE__, __LINE__);
}

pSyncConfig.SyncSignalID = HAL_DMAMUX1_SYNC_DMAMUX1_CH0_EVT;
pSyncConfig.SyncPolarity = HAL_DMAMUX_SYNC_NO_EVENT;
pSyncConfig.SyncEnable = DISABLE;
pSyncConfig.EventEnable = ENABLE;
pSyncConfig.RequestNumber = 1;
if (HAL_DMAEx_ConfigMuxSync(&hdma_spi2_rx, &pSyncConfig) != HAL_OK)
{
  _Error_Handler(__FILE__, __LINE__);
}

__HAL_LINKDMA(hspi,hdmarx,hdma_spi2_rx);

/* SPI2 interrupt Init */
HAL_NVIC_SetPriority(SPI2_IRQn, 0, 0);
HAL_NVIC_EnableIRQ(SPI2_IRQn);


/* USER CODE BEGIN SPI2_MspInit 1 */

  /* USER CODE END SPI2_MspInit 1 */
  }

}

SPI和DMA初始化如下:

static void MX_DMA_Init(void) 
{
  /* DMA controller clock enable */
  __HAL_RCC_DMA1_CLK_ENABLE();

  /* DMA interrupt init */
  /* DMA1_Stream0_IRQn interrupt configuration */
  HAL_NVIC_SetPriority(DMA1_Stream0_IRQn, 0, 0);
  HAL_NVIC_EnableIRQ(DMA1_Stream0_IRQn);
  /* DMA1_Stream1_IRQn interrupt configuration */
  HAL_NVIC_SetPriority(DMA1_Stream1_IRQn, 0, 0);
  HAL_NVIC_EnableIRQ(DMA1_Stream1_IRQn);
  /* DMAMUX1_OVR_IRQn interrupt configuration */
  HAL_NVIC_SetPriority(DMAMUX1_OVR_IRQn, 0, 0);
  HAL_NVIC_EnableIRQ(DMAMUX1_OVR_IRQn);

}

/* SPI2 init function */
static void MX_SPI2_Init(void)
{

  /* SPI2 parameter configuration*/
  hspi2.Instance = SPI2;
  hspi2.Init.Mode = SPI_MODE_MASTER;
  hspi2.Init.Direction = SPI_DIRECTION_2LINES;
  hspi2.Init.DataSize = SPI_DATASIZE_32BIT;
  hspi2.Init.CLKPolarity = SPI_POLARITY_LOW;
  hspi2.Init.CLKPhase = SPI_PHASE_1EDGE;
  hspi2.Init.NSS = SPI_NSS_SOFT;
  hspi2.Init.BaudRatePrescaler = SPI_BAUDRATEPRESCALER_4;
  hspi2.Init.FirstBit = SPI_FIRSTBIT_MSB;
  hspi2.Init.TIMode = SPI_TIMODE_DISABLE;
  hspi2.Init.CRCCalculation = SPI_CRCCALCULATION_DISABLE;
  hspi2.Init.CRCPolynomial = 7;
  hspi2.Init.NSSPMode = SPI_NSS_PULSE_ENABLE;
  hspi2.Init.NSSPolarity = SPI_NSS_POLARITY_LOW;
  hspi2.Init.FifoThreshold = SPI_FIFO_THRESHOLD_01DATA;
  hspi2.Init.TxCRCInitializationPattern = SPI_CRC_INITIALIZATION_ALL_ZERO_PATTERN;
  hspi2.Init.RxCRCInitializationPattern = SPI_CRC_INITIALIZATION_ALL_ZERO_PATTERN;
  hspi2.Init.MasterSSIdleness = SPI_MASTER_SS_IDLENESS_00CYCLE;
  hspi2.Init.MasterInterDataIdleness = SPI_MASTER_INTERDATA_IDLENESS_00CYCLE;
  hspi2.Init.MasterReceiverAutoSusp = SPI_MASTER_RX_AUTOSUSP_DISABLE;
  hspi2.Init.MasterKeepIOState = SPI_MASTER_KEEP_IO_STATE_DISABLE;
  hspi2.Init.IOSwap = SPI_IO_SWAP_DISABLE;

  if (HAL_SPI_Init(&hspi2) != HAL_OK)
  {
    _Error_Handler(__FILE__, __LINE__);
  }

}

外部数据就绪连接到PB0 GPIO。从这一点开始,我不知道如何告诉DMA监听外部事件以及如何设置TX / RX缓冲区地址,以及如何告诉DMA我有一个能够读取128个块的RX缓冲区以及如何配置DMA来引发中断当RX缓冲区已满时。

整点是不使用HAL_SPI_Transmit_DMA函数。因为这需要我手动处理外部DRDY中断,并且我将得到与我已有的相同的解决方案。

stm32 spi dma
1个回答
0
投票

我完全不知道STM32H7 DMA的实现。

在其他STM32控制器上,您可以选择DMA触发中断,例如,计时器溢出。问题是,在触发器开始第一次DMA传输后,必须将其更改为SPI RX缓冲区空触发,这不能自动完成。

您还必须检查EXT引脚中断是否可用作DMA触发器。

H7 DMA模块比我通常使用的DMA控制器更先进,因此可以解决上述任务。

© www.soinside.com 2019 - 2024. All rights reserved.