硬汉嵌入式论坛

 找回密码
 立即注册
查看: 4133|回复: 14
收起左侧

[SPI/QSPI] STM32H7 SPI使用DMA通信,单次模式只能通信一次,循环模式可连续发但不受控制

[复制链接]

1

主题

8

回帖

11

积分

新手上路

积分
11
发表于 2021-6-24 22:17:17 | 显示全部楼层 |阅读模式
求助各位硬汉:
       本人最近想使用SPI+DMA完成对AD7606电压信号的采集,设计方案如下:
       1.使用定时器控制AD7606采样率,采样率50K;
       2.外部中断感知AD7606下降沿信号,外部中断处理函数中设置标志位或者直接读取数据;
       现在的问题是,使用SPI可以采集到准确的AD数据,但是加入DMA后,使用HAL_SPI_TransmitReceive_DMA(&hspi2,Tx_Buffer,Rx_Buffer,16)读取数据,问题就来了,若是DMA配置为DMA_NORMAL模式,则DMA只能使用一次,不论是DMA中断还是查询处理程序中都无法重启使用DMA,但是使用串口的话没问题,可以重启;我之前也浏览过很多相关资料,说单次模式下需要在中断处理函数中清标志位,重置传输数量,但是H7的HAL中应该是完成了相应操作的,看串口+DMA的例子中也只是进行了清传输完成标志位,我也试过在DMA中断中进行过类似操作,死马当活马医嘛,但是没有起到任何作用,且仿真界面注意到DMA的发送数据流置位了FIFO错误标志位,这就很奇怪了,我明明失能了FIFO模式,SPI中也没有配置,找了很久也不明白问题出在哪里;这是单次模式的情况
       若是使用DMA_CIRCULAR模式,霍那就不得了,根本停不下来,同样的也在发送数据流中发现了FIFO错误,但是循环模式可以一直不停的跑,暂且没有理会,看能不能采集到正确的数据嘛,如果可以采到那就不管了。但是问题也来了,循环模式只要调用了HAL_SPI_TransmitReceive_DMA(&hspi2,Tx_Buffer,Rx_Buffer,16)这个HAL库函数后,用逻辑分析仪抓SPI的SCK引脚,根本停不下来,没法控制脉冲时序的话采集的数据肯定是错的,后来想了想,主函数中调用一次HAL_SPI_TransmitReceive_DMA函数,在DMA接收完成中断中我停止DMA传输,外部中断中恢复DMA传输,这样大致就可以控制时钟脉冲时序,理论上可行,说干就干,尴尬的是并没有用,就感觉我对整个DMA失去了控制权。
        啰嗦了这么多,请教各位:
        1.DMA_NORMAL模式应该怎么再次重新启用DMA;
        2.DMA_CIRCULAR模式下是如何控制通信时序的;
        3.HAL_SPI_TransmitReceive_DMA这个函数是否就是一个坑、自己重写发送接收函数比较好;
        补充:DCache我是失能了的;
源码如下,希望各位指正:
SPI和DMA配置如下
void MX_SPI2_Init(void)
{

  hspi2.Instance = SPI2;
  hspi2.Init.Mode = SPI_MODE_MASTER;
  hspi2.Init.Direction = SPI_DIRECTION_2LINES;
  hspi2.Init.DataSize = SPI_DATASIZE_8BIT;
  hspi2.Init.CLKPolarity = SPI_POLARITY_LOW;
  hspi2.Init.CLKPhase = SPI_PHASE_1EDGE;
  hspi2.Init.NSS = SPI_NSS_SOFT;
  hspi2.Init.BaudRatePrescaler = SPI_BAUDRATEPRESCALER_16;
  hspi2.Init.FirstBit = SPI_FIRSTBIT_MSB;
  hspi2.Init.TIMode = SPI_TIMODE_DISABLE;
  hspi2.Init.CRCCalculation = SPI_CRCCALCULATION_DISABLE;
  hspi2.Init.CRCPolynomial = 0x0;
  hspi2.Init.NSSPMode = SPI_NSS_PULSE_DISABLE;
  hspi2.Init.NSSPolarity = SPI_NSS_POLARITY_LOW;
//  hspi2.Init.FifoThreshold = SPI_FIFO_THRESHOLD_16DATA;
  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_ENABLE;
  hspi2.Init.IOSwap = SPI_IO_SWAP_DISABLE;
  if (HAL_SPI_Init(&hspi2) != HAL_OK)
  {
    Error_Handler();
  }

}


void HAL_SPI_MspInit(SPI_HandleTypeDef* spiHandle)
{

  GPIO_InitTypeDef GPIO_InitStruct = {0};
  if(spiHandle->Instance==SPI2)
  {
  /* USER CODE BEGIN SPI2_MspInit 0 */
       
  /* USER CODE END SPI2_MspInit 0 */
    /* SPI2 clock enable */
    __HAL_RCC_SPI2_CLK_ENABLE();

    __HAL_RCC_GPIOB_CLK_ENABLE();
    /**SPI2 GPIO Configuration   
    PB13     ------> SPI2_SCK
    PB14     ------> SPI2_MISO
    PB15     ------> SPI2_MOSI
    */
    GPIO_InitStruct.Pin = SPI2_SCK_Pin|SPI2_MISO_Pin|SPI2_MOSI_Pin;
    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(GPIOB, &GPIO_InitStruct);

    /* SPI2 DMA Init */
    /* SPI2_RX Init */
    hdma_spi2_rx.Instance = DMA1_Stream0;
    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_BYTE;
    hdma_spi2_rx.Init.MemDataAlignment = DMA_MDATAALIGN_BYTE;
    hdma_spi2_rx.Init.Mode = DMA_NORMAL;
    hdma_spi2_rx.Init.Priority = DMA_PRIORITY_HIGH;
    hdma_spi2_rx.Init.FIFOMode = DMA_FIFOMODE_DISABLE;
    if (HAL_DMA_Init(&hdma_spi2_rx) != HAL_OK)
    {
      Error_Handler();
    }

    __HAL_LINKDMA(spiHandle,hdmarx,hdma_spi2_rx);

    /* SPI2_TX Init */
    hdma_spi2_tx.Instance = DMA1_Stream1;
    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_ENABLE;
    hdma_spi2_tx.Init.PeriphDataAlignment = DMA_PDATAALIGN_BYTE;
    hdma_spi2_tx.Init.MemDataAlignment = DMA_MDATAALIGN_BYTE;
    hdma_spi2_tx.Init.Mode = DMA_NORMAL;
    hdma_spi2_tx.Init.Priority = DMA_PRIORITY_LOW;
    hdma_spi2_tx.Init.FIFOMode = DMA_FIFOMODE_DISABLE;
    if (HAL_DMA_Init(&hdma_spi2_tx) != HAL_OK)
    {
      Error_Handler();
    }

    __HAL_LINKDMA(spiHandle,hdmatx,hdma_spi2_tx);

    /* SPI2 interrupt Init */
    HAL_NVIC_SetPriority(SPI2_IRQn, 4, 0);
    HAL_NVIC_EnableIRQ(SPI2_IRQn);
  /* USER CODE BEGIN SPI2_MspInit 1 */

  /* USER CODE END SPI2_MspInit 1 */
  }
}


void DMA1_Stream0_IRQHandler(void)
{
  /* USER CODE BEGIN DMA1_Stream0_IRQn 0 */
                static uint8_t K=0;
          uint8_t i=0;
  if(__HAL_DMA_GET_FLAG(&hdma_spi2_rx,DMA_FLAG_TCIF0_4)!=RESET)    //êy¾Y½óêÕíê3é
                {   
                        __HAL_DMA_CLEAR_FLAG(&hdma_spi2_rx,DMA_FLAG_TCIF0_4);
                      DATA_OK=1;       
                }
  /* USER CODE END DMA1_Stream0_IRQn 0 */
  HAL_DMA_IRQHandler(&hdma_spi2_rx);
  /* USER CODE BEGIN DMA1_Stream0_IRQn 1 */

  /* USER CODE END DMA1_Stream0_IRQn 1 */
}

void DMA1_Stream1_IRQHandler(void)
{
  /* USER CODE BEGIN DMA1_Stream1_IRQn 0 */
if(__HAL_DMA_GET_FLAG(&hdma_spi2_rx,DMA_FLAG_TCIF1_5)!=RESET)    //êy¾Y½óêÕíê3é
                {   
                        __HAL_DMA_CLEAR_FLAG(&hdma_spi2_tx,DMA_FLAG_TCIF1_5);
                }
  /* USER CODE END DMA1_Stream1_IRQn 0 */
  HAL_DMA_IRQHandler(&hdma_spi2_tx);
  /* USER CODE BEGIN DMA1_Stream1_IRQn 1 */

  /* USER CODE END DMA1_Stream1_IRQn 1 */
}

void EXTI15_10_IRQHandler(void)
{
  /* USER CODE BEGIN EXTI15_10_IRQn 0 */
      static uint8_t K=0;
            uint8_t i,data;
            uint8_t Free_data=0xff;
                         if(ADC_Complete==0)  //′«êäíê3éÖ®oóÔù×öà-¸ß¸üDÂ′|àí
                        {
                                 AD_CS_0();
                                 HAL_SPI_TransmitReceive_DMA(&hspi2,Tx_Buffer,Rx_Buffer,16);         //还是希望外部中断中控制DMA通信
                        }
  /* USER CODE END EXTI15_10_IRQn 0 */
  HAL_GPIO_EXTI_IRQHandler(GPIO_PIN_10);
  /* USER CODE BEGIN EXTI15_10_IRQn 1 */

  /* USER CODE END EXTI15_10_IRQn 1 */
}


回复

使用道具 举报

1万

主题

6万

回帖

10万

积分

管理员

Rank: 9Rank: 9Rank: 9

积分
106882
QQ
发表于 2021-6-25 08:47:55 | 显示全部楼层
楼主这种方式的DMA,其实意义不是很大,因为这种方式是每次BUSY外部中断触发然后启动DMA传输读取。采样率高,比如100Ksps,那么中断也需要高达200KHz。然后每次都要在里面启动DMA传输。

好的做法是配合DMAMUX做自动触发。之前研究过一段时间,忙别的事情搁置了。

现在发布的FMC DMA方式案例就是这个思路。
回复

使用道具 举报

1

主题

8

回帖

11

积分

新手上路

积分
11
 楼主| 发表于 2021-6-25 09:46:33 | 显示全部楼层
eric2013 发表于 2021-6-25 08:47
楼主这种方式的DMA,其实意义不是很大,因为这种方式是每次BUSY外部中断触发然后启动DMA传输读取。采样率高 ...

非常感谢大佬提供的思路,我去看看DMAMUX相关的文档,最近刚接触DMA,不太熟
回复

使用道具 举报

1

主题

8

回帖

11

积分

新手上路

积分
11
 楼主| 发表于 2021-6-25 17:33:17 | 显示全部楼层
单步调试过程中发现点现象,第一次调用HAL_SPI_TransmitReceive_DMA这个函数的时候,里面的语句都执行了,发送完成后第二次调用中间跳过了好多语句直接退出了,不知道为什么
回复

使用道具 举报

1

主题

8

回帖

11

积分

新手上路

积分
11
 楼主| 发表于 2021-6-25 17:33:56 | 显示全部楼层
单步调试过程中发现点现象,第一次调用HAL_SPI_TransmitReceive_DMA这个函数的时候,里面的语句都执行了,发送完成后第二次调用中间跳过了好多语句直接退出了,不知道为什么
回复

使用道具 举报

1万

主题

6万

回帖

10万

积分

管理员

Rank: 9Rank: 9Rank: 9

积分
106882
QQ
发表于 2021-6-26 08:47:33 | 显示全部楼层
crx112321 发表于 2021-6-25 17:33
单步调试过程中发现点现象,第一次调用HAL_SPI_TransmitReceive_DMA这个函数的时候,里面的语句都执行了, ...

调试状态不能说明问题,调试会根据优化不同,很多语句都跳过去了。
回复

使用道具 举报

1

主题

8

回帖

11

积分

新手上路

积分
11
 楼主| 发表于 2021-6-28 16:41:25 | 显示全部楼层
eric2013 发表于 2021-6-26 08:47
调试状态不能说明问题,调试会根据优化不同,很多语句都跳过去了。

哦哦,跳过之前我跟踪到的是SP的状态有问题,然后看返回的状态确实是busy,在清标志位中重新初始化了一下SPI和DMA,现在可以控制了,我把SPI的发送和接收短接了,测试的时候第一次接收的数据是对的,第二次接收是0,第三次只有前4个字节是对的,然后发送就卡死了,接收倒是一直在进行,不知道这是啥原因
回复

使用道具 举报

1万

主题

6万

回帖

10万

积分

管理员

Rank: 9Rank: 9Rank: 9

积分
106882
QQ
发表于 2021-6-28 17:45:30 | 显示全部楼层
crx112321 发表于 2021-6-28 16:41
哦哦,跳过之前我跟踪到的是SP的状态有问题,然后看返回的状态确实是busy,在清标志位中重新初始化了一下 ...

现在是测试的是什么,还是你楼主位的方式吗
回复

使用道具 举报

1

主题

8

回帖

11

积分

新手上路

积分
11
 楼主| 发表于 2021-6-28 19:52:58 | 显示全部楼层
eric2013 发表于 2021-6-28 17:45
现在是测试的是什么,还是你楼主位的方式吗

不是,我之前不是DMA没法用嘛,现在做的是SPI+DMA测试,就把SPI的发送和接收短接在一起,自定义一个8位,16字节的发送数组,看接收到的对不对
回复

使用道具 举报

1万

主题

6万

回帖

10万

积分

管理员

Rank: 9Rank: 9Rank: 9

积分
106882
QQ
发表于 2021-6-29 08:39:59 | 显示全部楼层
crx112321 发表于 2021-6-28 19:52
不是,我之前不是DMA没法用嘛,现在做的是SPI+DMA测试,就把SPI的发送和接收短接在一起,自定义一个8位, ...

这个SPI DMA回环测试不太好,最好还是接上外设,看波形。
回复

使用道具 举报

1

主题

8

回帖

11

积分

新手上路

积分
11
 楼主| 发表于 2021-6-30 16:34:15 | 显示全部楼层
eric2013 发表于 2021-6-29 08:39
这个SPI DMA回环测试不太好,最好还是接上外设,看波形。

接上外设打开中断后就不行了0.0,第三次传输SPI的状态会变成HAL_SPI_STATE_BUSY_TX_RX然后就没法重启了,另外我有个疑问,DMA的中断回调函数可以自己定义吗,我看了看好像库中定义的不是弱函数
回复

使用道具 举报

1万

主题

6万

回帖

10万

积分

管理员

Rank: 9Rank: 9Rank: 9

积分
106882
QQ
发表于 2021-7-1 10:23:21 | 显示全部楼层
crx112321 发表于 2021-6-30 16:34
接上外设打开中断后就不行了0.0,第三次传输SPI的状态会变成HAL_SPI_STATE_BUSY_TX_RX然后就没法重启了, ...

那个回调函数
回复

使用道具 举报

1

主题

8

回帖

11

积分

新手上路

积分
11
 楼主| 发表于 2021-7-1 15:23:35 | 显示全部楼层

就是DMA中断完成的回调函数,类似像定时器,外部中断这些都有CALLBACK回调函数,然后我兜兜转转找了一圈终于找到了,SPI+DMA,DMA的中断回调函数原来就是SPI通信完成的回调函数,汗..HAL库好绕啊,其实就是HAL_SPI_RxCpltCallback(),现在呢通信的话过一段时间会卡死,SPI的状态会变成HAL_SPI_STATE_BUSY_TX_RX,观察了一下,大概在运行18次的时候卡死。我看了之前您发的帖子中有关于SPI+DMA 的,其中提到内存地址分配的,看了V7的例子里,定义DMA发送接收数组的语句是这样的
/* DMAÄ£ê½ê1óÃμÄSRAM4 */
#elif defined (USE_SPI_DMA)
    #if defined ( __CC_ARM )    /* IAR *******/
        __attribute__((section (".RAM_D3"))) uint8_t g_spiTxBuf[SPI_BUFFER_SIZE];   
        __attribute__((section (".RAM_D3"))) uint8_t g_spiRxBuf[SPI_BUFFER_SIZE];
    #elif defined (__ICCARM__)   /* MDK ********/
        #pragma location = ".RAM_D3"
        uint8_t g_spiTxBuf[SPI_BUFFER_SIZE];   
        #pragma location = ".RAM_D3"
        uint8_t g_spiRxBuf[SPI_BUFFER_SIZE];
    #endif
#endif
而我直接是全局变量定义,这样会有影响吗
回复

使用道具 举报

1万

主题

6万

回帖

10万

积分

管理员

Rank: 9Rank: 9Rank: 9

积分
106882
QQ
发表于 2021-7-2 09:59:29 | 显示全部楼层
crx112321 发表于 2021-7-1 15:23
就是DMA中断完成的回调函数,类似像定时器,外部中断这些都有CALLBACK回调函数,然后我兜兜转转找了一圈 ...

可以的,注意不要定义到TCM上了,TCM空间上不支持通用的DMA1和DMA2。
使用AXI SRAM这些是没问题的。


回复

使用道具 举报

1

主题

8

回帖

11

积分

新手上路

积分
11
 楼主| 发表于 2021-7-2 16:09:52 | 显示全部楼层
eric2013 发表于 2021-7-2 09:59
可以的,注意不要定义到TCM上了,TCM空间上不支持通用的DMA1和DMA2。
使用AXI SRAM这些是没问题的。

好的,十分感谢!
回复

使用道具 举报

您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

QQ|小黑屋|Archiver|手机版|硬汉嵌入式论坛

GMT+8, 2024-5-8 17:11 , Processed in 0.344625 second(s), 25 queries .

Powered by Discuz! X3.4 Licensed

Copyright © 2001-2023, Tencent Cloud.

快速回复 返回顶部 返回列表