【问题标题】:STM32L4 SPI Transfer complete interrupt using DMA fires only once使用 DMA 的 STM32L4 SPI 传输完成中断仅触发一次
【发布时间】:2018-11-13 08:57:06
【问题描述】:

我正在尝试使用 SPI 和 DMA 在 2 个核板 (NUCLEO-L432KCU) 之间发送一个 10 字节的数组。我的目标是使用低级 API 为从板开发代码。主板仅用于测试,当一切正常时,将替换为真实系统。

在继续之前,这里有一些关于系统的更多详细信息: 发件人被配置为主机。 master 的代码是使用 HAL API 开发的。主板上的片选是使用 GPIO 实现的。 接收器配置为从设备,启用了仅接收从设备选项和硬件 NSS 输入。初始化代码是使用 CubeMX 工具G自动生成的。

使用我当前的实现,我能够在从板上接收数据,但只能接收一次:实际上,中断似乎只触发一次,我很难弄清楚我错过了什么!

我相信这个错误与清除一些中断标志有关。我浏览了参考手册,但看不到我做错了什么。

以下是我的发送者和接收者代码。


发件人代码

注意:关于发件人,我只报告主要功能,因为所有其他代码都是自动生成的。此外,我用逻辑分析仪检查了代码是否有效。如果您需要更多详细信息,请告诉我。

int main(void)
{
  /* USER CODE BEGIN 1 */

  /* USER CODE END 1 */

  /* MCU Configuration----------------------------------------------------------*/

  /* Reset of all peripherals, Initializes the Flash interface and the Systick. */
  HAL_Init();

  /* USER CODE BEGIN Init */

  /* USER CODE END Init */

  /* Configure the system clock */
  SystemClock_Config();

  /* USER CODE BEGIN SysInit */

  /* USER CODE END SysInit */

  /* Initialize all configured peripherals */
  MX_GPIO_Init();
  MX_DMA_Init();
  MX_SPI1_Init();
  MX_SPI3_Init();
  MX_USART2_UART_Init();
  MX_TIM1_Init();
  /* USER CODE BEGIN 2 */

  uint8_t test[] = { 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0A};
  HAL_GPIO_WritePin(SPI1_CS_GPIO_Port,SPI1_CS_Pin,RESET);
  HAL_SPI_Transmit(&hspi1,test,sizeof(test),1000);
  HAL_GPIO_WritePin(SPI1_CS_GPIO_Port,SPI1_CS_Pin,SET);

  /* USER CODE END 2 */

  /* Infinite loop */
  /* USER CODE BEGIN WHILE */
  while (1)
  {

  /* USER CODE END WHILE */

  /* USER CODE BEGIN 3 */

  }
  /* USER CODE END 3 */

}

接收方代码 注意: DMA 和 SPI 的配置大多由 CubeMX 工具自动完成。我的项目的其他初始化被提供到 main 函数中。

uint8_t aRxBuffer[10];
uint8_t received_buffer[100];
uint16_t cnt = 0;

int main(void)
{
  /* USER CODE BEGIN 1 */

  /* USER CODE END 1 */

  /* MCU Configuration----------------------------------------------------------*/

  /* Reset of all peripherals, Initializes the Flash interface and the Systick. */
  HAL_Init();

  /* USER CODE BEGIN Init */

  /* USER CODE END Init */

  /* Configure the system clock */
  SystemClock_Config();

  /* USER CODE BEGIN SysInit */

  /* USER CODE END SysInit */

  /* Initialize all configured peripherals */
  MX_GPIO_Init();
  MX_DMA_Init();
  MX_SPI1_Init();
  MX_SPI3_Init();
  MX_USART2_UART_Init();
  MX_TIM1_Init();
  /* USER CODE BEGIN 2 */

  // Custom configuration of DMA (after calling function MX_SPI3_INIT()
  // Configure address of the buffer for receiving data
  LL_DMA_ConfigAddresses(DMA2, LL_DMA_CHANNEL_1, LL_SPI_DMA_GetRegAddr(SPI3), (uint32_t)aRxBuffer,LL_DMA_GetDataTransferDirection(DMA2, LL_DMA_CHANNEL_1));
  // Configure data length
  LL_DMA_SetDataLength(DMA2, LL_DMA_CHANNEL_1,10);
  // Enable DMA Transfer complete interrupt
  LL_DMA_EnableIT_TC(DMA2, LL_DMA_CHANNEL_1);
  // LL_DMA_EnableIT_TE(DMA2, LL_DMA_CHANNEL_1);

  // We Want the SPI3 to receive 8-bit data
  // Therefore we trigger the RXNE interrupt when the FIFO level is greater than or equal to 1/4 (8bit)
  // See pag. 1221 of the TRM
  LL_SPI_SetRxFIFOThreshold(SPI3,LL_SPI_RX_FIFO_TH_QUARTER);
  LL_SPI_EnableDMAReq_RX(SPI3);

  // Enable SPI_3
  LL_SPI_Enable(SPI3);
  // Enable DMA_2,CHANNEL_1
  LL_DMA_EnableChannel(DMA2, LL_DMA_CHANNEL_1);

  /* USER CODE END 2 */

  /* Infinite loop */
  /* USER CODE BEGIN WHILE */
  while (1)
  {

  /* USER CODE END WHILE */

  /* USER CODE BEGIN 3 */

  }
  /* USER CODE END 3 */

}

以下是 IRQ 处理程序(注释代码表示使其工作的各种尝试!):

void DMA2_Channel1_IRQHandler(void)
{
  /* USER CODE BEGIN DMA2_Channel1_IRQn 0 */

    // Transfer-complete interrupt management
    if(LL_DMA_IsActiveFlag_TC1(DMA2))
    {
        //LL_DMA_ClearFlag_TC1(DMA2);
        LL_DMA_ClearFlag_GI1(DMA2);
        /* Call function Transmission complete Callback */
        DMA1_TransmitComplete_Callback();
    }
    else if(LL_DMA_IsActiveFlag_TE1(DMA2))
    {
        /* Call Error function */
        int _error = 0;
    }


      // Enable SPI_3
      //LL_SPI_Disable(SPI3);
      // Enable DMA_2,CHANNEL_1
      //LL_DMA_DisableChannel(DMA2, LL_DMA_CHANNEL_1);

      //LL_DMA_EnableIT_TC(DMA2, LL_DMA_CHANNEL_1);
      // LL_DMA_EnableIT_TE(DMA2, LL_DMA_CHANNEL_1);

      // We Want the SPI3 to receive 8-bit data
      // Therefore we trigger the RXNE interrupt when the FIFO level is greater than or equal to 1/4 (8bit)
      // See pag. 1221 of the TRM
      //LL_SPI_SetRxFIFOThreshold(SPI3,LL_SPI_RX_FIFO_TH_QUARTER);
      //LL_SPI_EnableDMAReq_RX(SPI3);

      // Enable SPI_3
      //LL_SPI_Enable(SPI3);
      // Enable DMA_2,CHANNEL_1
      LL_DMA_EnableChannel(DMA2, LL_DMA_CHANNEL_1);



    //  LL_DMA_EnableIT_TE(DMA2, LL_DMA_CHANNEL_1);

  /* USER CODE END DMA2_Channel1_IRQn 0 */

  /* USER CODE BEGIN DMA2_Channel1_IRQn 1 */

  /* USER CODE END DMA2_Channel1_IRQn 1 */
}

以下是 SPI 和 DMA 的初始化(自动生成):

/* SPI1 init function */
void MX_SPI1_Init(void)
{
  LL_SPI_InitTypeDef SPI_InitStruct;

  LL_GPIO_InitTypeDef GPIO_InitStruct;
  /* Peripheral clock enable */
  LL_APB2_GRP1_EnableClock(LL_APB2_GRP1_PERIPH_SPI1);

  /**SPI1 GPIO Configuration  
  PA1   ------> SPI1_SCK
  PA7   ------> SPI1_MOSI 
  */
  GPIO_InitStruct.Pin = SCLK1_to_SpW_Pin|MOSI1_to_SpW_Pin;
  GPIO_InitStruct.Mode = LL_GPIO_MODE_ALTERNATE;
  GPIO_InitStruct.Speed = LL_GPIO_SPEED_FREQ_VERY_HIGH;
  GPIO_InitStruct.OutputType = LL_GPIO_OUTPUT_PUSHPULL;
  GPIO_InitStruct.Pull = LL_GPIO_PULL_NO;
  GPIO_InitStruct.Alternate = LL_GPIO_AF_5;
  LL_GPIO_Init(GPIOA, &GPIO_InitStruct);

  SPI_InitStruct.TransferDirection = LL_SPI_FULL_DUPLEX;
  SPI_InitStruct.Mode = LL_SPI_MODE_MASTER;
  SPI_InitStruct.DataWidth = LL_SPI_DATAWIDTH_4BIT;
  SPI_InitStruct.ClockPolarity = LL_SPI_POLARITY_LOW;
  SPI_InitStruct.ClockPhase = LL_SPI_PHASE_1EDGE;
  SPI_InitStruct.NSS = LL_SPI_NSS_SOFT;
  SPI_InitStruct.BaudRate = LL_SPI_BAUDRATEPRESCALER_DIV8;
  SPI_InitStruct.BitOrder = LL_SPI_LSB_FIRST;
  SPI_InitStruct.CRCCalculation = LL_SPI_CRCCALCULATION_DISABLE;
  SPI_InitStruct.CRCPoly = 7;
  LL_SPI_Init(SPI1, &SPI_InitStruct);

  LL_SPI_SetStandard(SPI1, LL_SPI_PROTOCOL_MOTOROLA);

  LL_SPI_EnableNSSPulseMgt(SPI1);

}
/* SPI3 init function */
void MX_SPI3_Init(void)
{
  LL_SPI_InitTypeDef SPI_InitStruct;

  LL_GPIO_InitTypeDef GPIO_InitStruct;
  /* Peripheral clock enable */
  LL_APB1_GRP1_EnableClock(LL_APB1_GRP1_PERIPH_SPI3);

  /**SPI3 GPIO Configuration  
  PA4   ------> SPI3_NSS
  PB3 (JTDO-TRACESWO)   ------> SPI3_SCK
  PB5   ------> SPI3_MOSI 
  */
  GPIO_InitStruct.Pin = LL_GPIO_PIN_4;
  GPIO_InitStruct.Mode = LL_GPIO_MODE_ALTERNATE;
  GPIO_InitStruct.Speed = LL_GPIO_SPEED_FREQ_VERY_HIGH;
  GPIO_InitStruct.OutputType = LL_GPIO_OUTPUT_PUSHPULL;
  GPIO_InitStruct.Pull = LL_GPIO_PULL_NO;
  GPIO_InitStruct.Alternate = LL_GPIO_AF_6;
  LL_GPIO_Init(GPIOA, &GPIO_InitStruct);

  GPIO_InitStruct.Pin = SCLK_from_SpW_Pin|MOSI_from_SpW_Pin;
  GPIO_InitStruct.Mode = LL_GPIO_MODE_ALTERNATE;
  GPIO_InitStruct.Speed = LL_GPIO_SPEED_FREQ_VERY_HIGH;
  GPIO_InitStruct.OutputType = LL_GPIO_OUTPUT_PUSHPULL;
  GPIO_InitStruct.Pull = LL_GPIO_PULL_NO;
  GPIO_InitStruct.Alternate = LL_GPIO_AF_6;
  LL_GPIO_Init(GPIOB, &GPIO_InitStruct);

  /* SPI3 DMA Init */

  /* SPI3_RX Init */
  LL_DMA_SetPeriphRequest(DMA2, LL_DMA_CHANNEL_1, LL_DMA_REQUEST_3);

  LL_DMA_SetDataTransferDirection(DMA2, LL_DMA_CHANNEL_1, LL_DMA_DIRECTION_PERIPH_TO_MEMORY);

  LL_DMA_SetChannelPriorityLevel(DMA2, LL_DMA_CHANNEL_1, LL_DMA_PRIORITY_LOW);

  LL_DMA_SetMode(DMA2, LL_DMA_CHANNEL_1, LL_DMA_MODE_NORMAL);

  LL_DMA_SetPeriphIncMode(DMA2, LL_DMA_CHANNEL_1, LL_DMA_PERIPH_NOINCREMENT);

  LL_DMA_SetMemoryIncMode(DMA2, LL_DMA_CHANNEL_1, LL_DMA_MEMORY_INCREMENT);

  LL_DMA_SetPeriphSize(DMA2, LL_DMA_CHANNEL_1, LL_DMA_PDATAALIGN_BYTE);

  LL_DMA_SetMemorySize(DMA2, LL_DMA_CHANNEL_1, LL_DMA_MDATAALIGN_BYTE);

  /* SPI3 interrupt Init */
  NVIC_SetPriority(SPI3_IRQn, NVIC_EncodePriority(NVIC_GetPriorityGrouping(),0, 0));
  NVIC_EnableIRQ(SPI3_IRQn);

  SPI_InitStruct.TransferDirection = LL_SPI_SIMPLEX_RX;
  SPI_InitStruct.Mode = LL_SPI_MODE_SLAVE;
  SPI_InitStruct.DataWidth = LL_SPI_DATAWIDTH_4BIT;
  SPI_InitStruct.ClockPolarity = LL_SPI_POLARITY_LOW;
  SPI_InitStruct.ClockPhase = LL_SPI_PHASE_1EDGE;
  SPI_InitStruct.NSS = LL_SPI_NSS_HARD_INPUT;
  SPI_InitStruct.BitOrder = LL_SPI_LSB_FIRST;
  SPI_InitStruct.CRCCalculation = LL_SPI_CRCCALCULATION_DISABLE;
  SPI_InitStruct.CRCPoly = 7;
  LL_SPI_Init(SPI3, &SPI_InitStruct);

  LL_SPI_SetStandard(SPI3, LL_SPI_PROTOCOL_MOTOROLA);

  LL_SPI_DisableNSSPulseMgt(SPI3);

}

谢谢。

【问题讨论】:

    标签: interrupt stm32 spi dma


    【解决方案1】:

    我最近实现了一个类似的系统,希望能帮上忙。我有几个问题,cmets,可能可以解决你的问题,但是如果没有在那里很难做到这一点。

    • 你知道是SPI还是DMA出错了吗?从机上是否发生 SPI 中断?这意味着 DMA 有故障,而不是 SPI。准确了解系统发生故障的位置非常重要。
    • LL_SPI_SetRxFIFOThreshold(SPI3,LL_SPI_RX_FIFO_TH_QUARTER); 是必要的,但应该在init 期间完成
    • TCIF 标志应该在IRQ 期间被清除(如您所做的那样)。
    • 您应该使用SPI_CR2_RXDMAEN 寄存器设置SPI 以触发DMA(我在您的代码中没有看到)。如果您不知道何时会收到数据,您也应该在init 期间执行此操作。
    • 出于同样的原因,我认为您应该在init 期间启用 DMA 通道,并保持启用状态。

    我希望这些 cmets 之一有所帮助。如果没有,我们会再试一次。

    编辑:干得好。我很高兴你通过解决大部分问题让它运行起来。根据您提供的信息,我发现缓冲区的主要问题是什么。

    您将 DMA 设置为接收 10 个字节:

    LL_DMA_SetDataLength(DMA2, LL_DMA_CHANNEL_1,10);
    

    这会将 DMA 内部计数器设置为 10。对于它接收到的每个字节,计数器都会减一,直到达到零。这就是使它能够计算 10 个字节的原因。在普通模式下,如果你想再接收10个字节,那么你需要再次发送该命令。在 循环 模式下,该值将自动重置为 10,这意味着它可以再接收 10 个字节。

    因此,如果您希望总是接收 10 个字节,那么 cicular 模式应该适合您。如果不是,那么您将不得不使用正常模式,并向MCU指定您期望的字节数(稍微复杂一点)。

    【讨论】:

    • 您好,谢谢您的回答。如果我没记错的话,LL_SPI_SetRxFIFOThreshold 应该没问题:根据 STM32L4 LL 示例项目,在调用 LL_SPI_Init 函数后立即调用此函数。调用函数 LL_SPI_EnableDMAReq_RX(SPI_TypeDef* SPIx) 启用 SPI_CR2_RXDMAEN,因此问题不应该存在。我注意到以下内容:如果我在 LL_DMA_ClearFlag_GI1(DMA2) 上设置断点;功能,状态寄存器 (SR) 有 RXNE = 0x01,TXE = 0x01,OVR = 0x01 和 FRLVL = 0c03。这意味着发生了溢出错误,事实上,FIFO 已满。
    • 但是,运行指令 LL_DMA_ClearFlag_GI1(DMA2);清除 OVR、RXNE 和 FRLVL。这很奇怪,因为我知道我正在发送 10 个字节,而在接收方,aRxBuffer 的维度为 10,所以在我看来应该没问题。我也尝试将 aRxBuffer 增加到 20(只是为了看看),但问题仍然存在。
    • 更新:我已经解决了设置 OVR 标志的问题,但主要问题仍然存在。我还注意到以下情况:如果我发送 10 个字节,比如说“ABCDEFGHIJ”,当 IRQ 例程上的 LL_DMA_IsActiveFlag_TC1 为真时,数据寄存器包含“GH”而不是“IJ”,正如我所期望的。
    • 我想我终于想通了。如果我将 DMA 缓冲区从 NORMAL 设置为 CIRCULAR,它就可以工作。但是,我在想为什么如果我将它设置为正常模式它不起作用:如果我希望恰好接收 10 个字节,则在 10 个字节后缓冲区应该重置。对吗?
    • 干得好!我更新了我的答案以解释缓冲区模式,以及为什么您的代码没有收到超过一次的传输。希望它有所帮助:)
    猜你喜欢
    • 2019-05-28
    • 2019-02-21
    • 2021-10-31
    • 1970-01-01
    • 1970-01-01
    • 2019-04-25
    • 2017-04-14
    • 2017-08-26
    • 2017-07-05
    相关资源
    最近更新 更多