[STM32F0] 关于SPI+DMA的使用问题

[复制链接]
6107|30
 楼主| chenyuanjiyi 发表于 2021-11-25 15:59 | 显示全部楼层 |阅读模式
主芯片:STM32F072CBT6
RF芯片:SX1280
开发环境:cubemx +keil

之前是MCU和SX1280(RF模块)是通过普通的SPI配置进行通讯的,这是没问题的,然后想优化下传输的速度,于是想到了SPI+DMA进行传输,当我通过SPI+DMA同时通过软件控制NSS片选时,
会出现图1的现象,NSS拉低后要过段时间才会开始进行传输,图1的测试代码main函数中
  1.   while (1)
  2.   {
  3.     /* USER CODE END WHILE */
  4. HAL_GPIO_WritePin(GPIOA,GPIO_PIN_15,GPIO_PIN_RESET);
  5.     HAL_SPI_TransmitReceive(&hspi1,txbuff,rxbuff,8);
  6.      HAL_GPIO_WritePin(GPIOA,GPIO_PIN_15,GPIO_PIN_SET);
  7.    
  8.     HAL_GPIO_TogglePin(LED_Red_GPIO_Port,LED_Red_Pin);
  9.       HAL_Delay(50);
  10.     /* USER CODE BEGIN 3 */
  11.   }
然后我就想着将软件控制NSS改成硬件控制,让其自动切换,SPI初始化配置代码
  1. void MX_SPI1_Init(void)
  2. {

  3.   hspi1.Instance = SPI1;
  4.   hspi1.Init.Mode = SPI_MODE_MASTER;
  5.   hspi1.Init.Direction = SPI_DIRECTION_2LINES;
  6.   hspi1.Init.DataSize = SPI_DATASIZE_8BIT;
  7.   hspi1.Init.CLKPolarity = SPI_POLARITY_LOW;
  8.   hspi1.Init.CLKPhase = SPI_PHASE_1EDGE;

  9.   hspi1.Init.NSS = SPI_NSS_HARD_INPUT;
  10.   hspi1.Init.BaudRatePrescaler = SPI_BAUDRATEPRESCALER_4;
  11.   hspi1.Init.FirstBit = SPI_FIRSTBIT_MSB;
  12.   hspi1.Init.TIMode = SPI_TIMODE_DISABLE;
  13.   hspi1.Init.CRCCalculation = SPI_CRCCALCULATION_DISABLE;
  14.   hspi1.Init.CRCPolynomial = 7;
  15.   hspi1.Init.CRCLength = SPI_CRC_LENGTH_DATASIZE;
  16.   hspi1.Init.NSSPMode = SPI_NSS_PULSE_ENABLE;
  17.   if (HAL_SPI_Init(&hspi1) != HAL_OK)
  18.   {
  19.     Error_Handler();
  20.   }

  21. }
然后测试发现,每发一个字节NSS引脚就自动拉高了,这就导致了读取的数据是有问题的,具体现象如图2

关于正常配置的SPI(软件控制NSS)的现象如图3

问题:如何改正SPI(硬件控制)+DMA每发一个字节NSS引脚就自动拉高的现象?

向大家请教啦  谢谢!


关于测试工程如下:
(附件中在spi.h中有关于 正常SPI(软件控制NSS)配置 和 SPI(硬件控制NSS)+DMA的宏)
#define SPI_DMA    0 //1:开启DMA
图2 和 图3的现象就是更改此宏出来的



snipaste_20211125_155610.png
snipaste_20211125_155649.png

SPI_DMA_Test.rar

9.4 MB, 下载次数: 44

 楼主| chenyuanjiyi 发表于 2021-11-30 17:23 | 显示全部楼层
本帖最后由 chenyuanjiyi 于 2021-12-9 17:52 编辑

修改HAl库可实现,具体见:SPI+DMA

通过修改HAL库,现已可以实现NSS引脚快速上下拉,主要修改了两个地方,一是HAL_SPI_TransmitReceive_DMA()函数里,在开启DMA时拉高NSS;二是HAL_DMA_IRQHandler()里面拉低NSS引脚。将上述两个函数复制,增加修改后重命名:HAL_SPI_MY_TransmitReceive_DMA()(函数原型在工程中stm32f0xx_hal_spi.c的1977行) 和 HAL_SPI1_TX_DMA_IRQHandler()(函数原型在工程中spi.c的322行);
  1. /*
  2.     * 复制HAL_SPI_TransmitReceive_DMA()函数,在开启DMA前,拉低NSS引脚
  3.     */
  4.     HAL_StatusTypeDef HAL_SPI_MY_TransmitReceive_DMA(SPI_HandleTypeDef *hspi, uint8_t *pTxData, uint8_t *pRxData, uint16_t Size)
  5.     {
  6.       ..................................
  7.     ................................省略.....
  8.       /* Set the SPI Tx DMA transfer complete callback as NULL because the communication closing is performed in DMA reception complete callback  */

  9.       hspi->hdmatx->XferHalfCpltCallback = NULL;
  10.       hspi->hdmatx->XferCpltCallback     = NULL;
  11.       hspi->hdmatx->XferErrorCallback    = NULL;
  12.       hspi->hdmatx->XferAbortCallback    = NULL;


  13.       /* Enable the Tx DMA Stream/Channel  */
  14.       HAL_DMA_Start_IT(hspi->hdmatx, (uint32_t)hspi->pTxBuffPtr, (uint32_t)&hspi->Instance->DR, hspi->TxXferCount);
  15.       
  16.     /* Check if the SPI is already enabled */
  17.       if ((hspi->Instance->CR1 & SPI_CR1_SPE) != SPI_CR1_SPE)
  18.       {
  19.         /* Enable SPI peripheral */
  20.         __HAL_SPI_ENABLE(hspi);
  21.       }
  22.       /* Enable the SPI Error Interrupt Bit */
  23.       __HAL_SPI_ENABLE_IT(hspi, (SPI_IT_ERR));

  24.     HAL_GPIO_WritePin(GPIOA, GPIO_PIN_15, GPIO_PIN_RESET);

  25.       /* Enable Tx DMA Request */
  26.       SET_BIT(hspi->Instance->CR2, SPI_CR2_TXDMAEN);

  27.     error :
  28.       /* Process Unlocked */
  29.       __HAL_UNLOCK(hspi);
  30.       return errorcode;
  31.     }
  1. /*
  2.     * 复制HAL_SPI_TransmitReceive_DMA()函数,在开启DMA前,拉低NSS引脚
  3.     */
  4.     HAL_StatusTypeDef HAL_SPI_MY_TransmitReceive_DMA(SPI_HandleTypeDef *hspi, uint8_t *pTxData, uint8_t *pRxData, uint16_t Size)
  5.     {
  6.       ..................................
  7.     ................................省略.....
  8.       /* Set the SPI Tx DMA transfer complete callback as NULL because the communication closing is performed in DMA reception complete callback  */

  9.       hspi->hdmatx->XferHalfCpltCallback = NULL;
  10.       hspi->hdmatx->XferCpltCallback     = NULL;
  11.       hspi->hdmatx->XferErrorCallback    = NULL;
  12.       hspi->hdmatx->XferAbortCallback    = NULL;


  13.       /* Enable the Tx DMA Stream/Channel  */
  14.       HAL_DMA_Start_IT(hspi->hdmatx, (uint32_t)hspi->pTxBuffPtr, (uint32_t)&hspi->Instance->DR, hspi->TxXferCount);
  15.       
  16.     /* Check if the SPI is already enabled */
  17.       if ((hspi->Instance->CR1 & SPI_CR1_SPE) != SPI_CR1_SPE)
  18.       {
  19.         /* Enable SPI peripheral */
  20.         __HAL_SPI_ENABLE(hspi);
  21.       }
  22.       /* Enable the SPI Error Interrupt Bit */
  23.       __HAL_SPI_ENABLE_IT(hspi, (SPI_IT_ERR));

  24.     HAL_GPIO_WritePin(GPIOA, GPIO_PIN_15, GPIO_PIN_RESET);

  25.       /* Enable Tx DMA Request */
  26.       SET_BIT(hspi->Instance->CR2, SPI_CR2_TXDMAEN);

  27.     error :
  28.       /* Process Unlocked */
  29.       __HAL_UNLOCK(hspi);
  30.       return errorcode;
  31.     }   

测试效果如下,发送5个字节大概需要5.16us,NSS上下拉时间在600ns左右:
附件放不下,github链接:https://github.com/Gu-Yue-Hu/SPI_DMA_HAL.git




测试结果.png
 楼主| chenyuanjiyi 发表于 2021-11-25 16:01 | 显示全部楼层
最终想实现如图的效果,这是采集样机的SPI的效果时序;
对图进行分析,可以判断是有两个NSS的,那么可以判断采用的是软件NSS,看了下时钟,频率大概是17.5M,最主要的还是两个字节的间距特别近,才680ns,而我正常进行SPI通讯的频率是12.5M,间距有2us左右,看到样机的效果,想试着调下,结果卡胡同了,目前未想到应该怎么实现,望大佬们指点 谢谢啦!





snipaste_20211125_110120.png
yljon 发表于 2021-11-26 07:27 | 显示全部楼层
学习了
香水城 发表于 2021-11-26 16:25 | 显示全部楼层
正常来讲,那个NSS每个字节抬高一个时钟时间是正常的操作,不应该导致数据接收错误。

你如果想取消这个也可以,你使用Motorala模式,然后将NSSP的极性模式选择DISABLE.

  hspi1.Init.NSSPMode = SPI_NSS_PULSE_DISABLE;

3089161a099f5f1e2e.png
 楼主| chenyuanjiyi 发表于 2021-11-29 10:26 | 显示全部楼层
本帖最后由 chenyuanjiyi 于 2021-11-29 10:29 编辑
香水城 发表于 2021-11-26 16:25
正常来讲,那个NSS每个字节抬高一个时钟时间是正常的操作,不应该导致数据接收错误。

你如果想取消这个也 ...

上面数据错误的话,我理解的是,每个字节抬高,导致了读的是一个字节的数据,但实际上我们要读的是一个32位的地址的数据,这样就导致了每个字节都抬高,然后读出的数据错误

大佬试了下你上图的配置,直接死机了
  1. /************SPI配置*************/
  2. void MX_SPI1_Init(void)
  3. {

  4.   hspi1.Instance = SPI1;
  5.   hspi1.Init.Mode = SPI_MODE_MASTER;
  6.   hspi1.Init.Direction = SPI_DIRECTION_2LINES;
  7.   hspi1.Init.DataSize = SPI_DATASIZE_8BIT;
  8.   hspi1.Init.CLKPolarity = SPI_POLARITY_LOW;
  9.   hspi1.Init.CLKPhase = SPI_PHASE_1EDGE;
  10.   #if (SPI_DMA==0)
  11.   hspi1.Init.NSS = SPI_NSS_SOFT;//SPI_NSS_HARD_INPUT;
  12.   #else
  13.   hspi1.Init.NSS = SPI_NSS_HARD_OUTPUT;
  14.   #endif
  15.   hspi1.Init.BaudRatePrescaler = SPI_BAUDRATEPRESCALER_4;
  16.   hspi1.Init.FirstBit = SPI_FIRSTBIT_MSB;
  17.   hspi1.Init.TIMode = SPI_TIMODE_DISABLE;
  18.   hspi1.Init.CRCCalculation = SPI_CRCCALCULATION_DISABLE;
  19.   hspi1.Init.CRCPolynomial = 7;
  20.   hspi1.Init.CRCLength = SPI_CRC_LENGTH_DATASIZE;
  21.   hspi1.Init.NSSPMode = SPI_NSS_PULSE_DISABLE;
  22.   if (HAL_SPI_Init(&hspi1) != HAL_OK)
  23.   {
  24.     Error_Handler();
  25.   }

  26. }
  27. /*--------------SPI读取指令操作---------*/
  28. /*!
  29. * \brief 用于阻止执行等待无线电繁忙引脚上的低电平状态。基本上用于SPI通信
  30. */
  31. void SX1280HalWaitOnBusy( void )
  32. {         
  33.    while( HAL_GPIO_ReadPin(SX1280_BUSY_GPIO_Port,SX1280_BUSY_Pin ) == 1 );         
  34. }


  35. /*
  36. 函数功能:读指令
  37. 参数:指令 数据  大小
  38. */
  39. void SX1280HalReadCommand( RadioCommands_t command, uint8_t *buffer, uint16_t size )
  40. {
  41.     uint16_t halSize = 2 + size;
  42.     halTxBuffer[0] = command;
  43.     halTxBuffer[1] = 0x00;
  44.     for( uint16_t index = 0; index < size; index++ )
  45.     {
  46.         halTxBuffer[2+index] = 0x00;
  47.     }

  48.     SX1280HalWaitOnBusy( );

  49.     SpiInOut( halTxBuffer, halRxBuffer, halSize );

  50.     memcpy( buffer, halRxBuffer + 2, size );

  51.     SX1280HalWaitOnBusy( );
  52. }


死机原因是因为每次对RF芯片进行读写时,要进行检测RF芯片的BUSY引脚的状态,然后我调试发现一直卡在SX1280HalWaitOnBusy()函数里。

目前是想实现2楼的一个现象,每个SPI字节的间距给缩短,按照 那个NSS每个字节抬高一个时钟时间是正常的操作的话,那么硬件控制NSS的话,可以认为是不能实现的,但是软件控制NSS的话,又会出现我上述图1的现象,请大佬指点,请问有这方面的例程或资料吗??

谢谢啦!
 楼主| chenyuanjiyi 发表于 2021-11-29 14:09 | 显示全部楼层
今天进行了进一步测试,采用SPI+DMA配置(cubemx生成),使用软件控制NSS引脚
修改拉低拉高的位置,在
HAL_SPI_TransmitReceive_DMA(&hspi1, pTxData,pRxData,Size)
  1. /* Check if the SPI is already enabled */
  2.   if ((hspi->Instance->CR1 & SPI_CR1_SPE) != SPI_CR1_SPE)
  3.   {
  4.     /* Enable SPI peripheral */
  5.     __HAL_SPI_ENABLE(hspi);
  6.   }
  7.   /* Enable the SPI Error Interrupt Bit */
  8.   __HAL_SPI_ENABLE_IT(hspi, (SPI_IT_ERR));
  9. HAL_GPIO_WritePin(GPIOA, GPIO_PIN_15, GPIO_PIN_RESET);
  10.   /* Enable Tx DMA Request */
  11.   SET_BIT(hspi->Instance->CR2, SPI_CR2_TXDMAEN);

  12. error :
  13.   /* Process Unlocked */
  14.   __HAL_UNLOCK(hspi);
  15.   return errorcode;
在SPI_DMA的读写函数里,将GPIOA_15(NSS控制引脚)拉低,然后在DMA的中断回调函数
  1. void DMA1_Channel2_3_IRQHandler(void)
  2. {
  3.   /* USER CODE BEGIN DMA1_Channel2_3_IRQn 0 */

  4.   /* USER CODE END DMA1_Channel2_3_IRQn 0 */
  5.   HAL_DMA_IRQHandler(&hdma_spi1_rx);
  6.   HAL_DMA_IRQHandler(&hdma_spi1_tx);
  7.   /* USER CODE BEGIN DMA1_Channel2_3_IRQn 1 */
  8. if(__HAL_DMA_GET_IT_SOURCE(&hdma_spi1_rx, DMA_IT_TC)){
  9.   HAL_GPIO_WritePin(GPIOA, GPIO_PIN_15, GPIO_PIN_SET);
  10.   }
  11.   /* USER CODE END DMA1_Channel2_3_IRQn 1 */
  12. }
当判断DMA_RX通道是DMA完成中断,然后将NSS引脚拉高,这样可以达到下图的效果:
可以看到两个字节间的距离是变短了,而且读取出的数据和我不采用DMA的时候是一致的,同时也保证了数据传输在NSS拉低时快速传输;
但是拉低的操作是修改的库(尽量不修改库),有什么方法,可以直接判断DMA准备传输前拉低NSS,传输完成后拉高NSS(DMA的中断就:传输一半,传输完成,传输错误);
请问大家在使用HAL_SPI_TransmitReceive_DMA(&hspi1, pTxData,pRxData,Size)函数时,NSS是如何控制的呢??
谢谢啦


snipaste_20211129_135846.png
呐咯密密 发表于 2021-11-29 15:08 | 显示全部楼层
chenyuanjiyi 发表于 2021-11-29 14:09
今天进行了进一步测试,采用SPI+DMA配置(cubemx生成),使用软件控制NSS引脚
修改拉低拉高的位置,在
HAL_ ...

为啥要纠结不修改库啊,我看你这个实现效果挺好的,你这个改的真的已经很不错了,库就是给人用的,修改一下也不影响。目的是实现我们想要的效果。不要太钻牛角尖嘛
 楼主| chenyuanjiyi 发表于 2021-11-29 16:13 | 显示全部楼层
呐咯密密 发表于 2021-11-29 15:08
为啥要纠结不修改库啊,我看你这个实现效果挺好的,你这个改的真的已经很不错了,库就是给人用的,修改一 ...

修改库的话,下次换个SPI的操作,比较容易忘记再次修改库,还有在这库里面要进行判断,另外实验了一下;
拉低NSS放的位置不变,拉高放在DMA1_Channel2_3_IRQHandler()这里的话,当工程中代码较多时,拉高的时间也更长了,所以最好是放在 HAL_DMA_IRQHandler(&hdma_spi1_rx);里面的,但是放在这个里面,又需要判断是否是这个&hdma_spi1_rx参数进来的

所以想知道大家使用HAL_SPI_TransmitReceive_DMA(&hspi1, pTxData,pRxData,Size)函数,NSS是如何使用的,因为我实验出来的结果,最好是放在库里面进行拉高拉低,.....
呐咯密密 发表于 2021-11-29 16:49 | 显示全部楼层
chenyuanjiyi 发表于 2021-11-29 16:13
修改库的话,下次换个SPI的操作,比较容易忘记再次修改库,还有在这库里面要进行判断,另外实验 ...

你可以重写一个函数啊,把库函数的内容复制出来,再加上你的判断,就不会出现忘记有没有修改库的事了。我都是在DMA启动前拉低,发送完成后拉高,只不过我没你这么苛刻,而且我用的固件库和寄存器的方式搭配,好用得很,就不会出现时间过长的情况,因为代码执行效率高。还有你的CLK是用逻辑分析仪捕捉的,你可以用示波器捕捉一下看看,会有新发现
 楼主| chenyuanjiyi 发表于 2021-11-29 17:48 | 显示全部楼层
呐咯密密 发表于 2021-11-29 16:49
你可以重写一个函数啊,把库函数的内容复制出来,再加上你的判断,就不会出现忘记有没有修改库的事了。我 ...

确实,我现在就是将HAL_DMA_IRQHandler();直接复制一份,然后改成HAL_SPI1_TX_DMA_IRQHandler();这个,这个函数里就增加了一个拉高的操作,因为我发现,如果我在HAL_DMA_IRQHandler这里面通过判断拉高引脚的话,会造成拉高的操作会在数据传输完后13us才会拉高,所以索性直接复制出来
另外通过在实际项目中测试,发现DMA_SPI通道的优先级也会影响拉高拉低的时间,目前修改后测试的最终结果如下图,上下拉NSS的时间大概在600ns左右,效果还是可以的
微信图片_20211129174714.png
呐咯密密 发表于 2021-11-30 09:27 | 显示全部楼层
chenyuanjiyi 发表于 2021-11-29 17:48
确实,我现在就是将HAL_DMA_IRQHandler();直接复制一份,然后改成HAL_SPI1_TX_DMA_IRQHandler();这个,这 ...

高优先级会抢占优先级,可能之前有其他的干扰了,
 楼主| chenyuanjiyi 发表于 2021-12-1 17:59 | 显示全部楼层
呐咯密密 发表于 2021-11-30 09:27
高优先级会抢占优先级,可能之前有其他的干扰了,

大佬 你用固件库和寄存器的方式搭配,使用SPI+DMA会出现数据错误吗?
今天看了下测试工程,发现返回的sx1280的ID=0xB7A9
但是正常应该是 0xA9B7,而且我看采集到的波形也确实是A9先执行的,然后在执行B7
  1. uint16_t SX1280GetFirmwareVersion( void )
  2. {
  3.     return( ( ( SX1280HalReadRegister( REG_LR_FIRMWARE_VERSION_MSB ) ) << 8 ) | ( SX1280HalReadRegister( REG_LR_FIRMWARE_VERSION_MSB + 1 ) ) );
  4. //返回 0xA9<<8|0xB7
  5. }

我用的STM32F072的芯片,主频为48M,配置的SPI速率为12M,当我使用SPI(没加DMA的时候),读取的ID是正常的0xA9B7
呐咯密密 发表于 2021-12-2 09:20 | 显示全部楼层
chenyuanjiyi 发表于 2021-12-1 17:59
大佬 你用固件库和寄存器的方式搭配,使用SPI+DMA会出现数据错误吗?
今天看了下测试工程,发现返回的sx1 ...

出现过,我当时的处理是多发一个字节,这样接收到的数据就方便处理了。当时我产生这样的原因是提高了代码优化等级导致的,还有就是看一下你的DMA速度配置为最高。
 楼主| chenyuanjiyi 发表于 2021-12-2 12:05 | 显示全部楼层
呐咯密密 发表于 2021-12-2 09:20
出现过,我当时的处理是多发一个字节,这样接收到的数据就方便处理了。当时我产生这样的原因是提高了代码 ...

尝试了大哥你说的三个方法,多发两字节、DMA速度配置为最高、代码优化等级为0,但是还是相同的效果,还是会读取数据错误,如图1;

然后我在读取数据时加了个等待
  1. #define SX1280_SPI_WR(pTxData, pRxData, Size)        HAL_SPI_MY_TransmitReceive_DMA(&hspi1, pTxData,pRxData,Size)

  2. /*
  3. 函数功能:读字节
  4. 参数:写入的数据  读取的数据 大小
  5. */
  6. void SpiInOut( uint8_t *txBuffer, uint8_t *rxBuffer, uint16_t size )
  7. {
  8.     SX1280_SPI_WR(txBuffer,rxBuffer,size);
  9.   while(__HAL_DMA_GET_COUNTER(&hdma_spi1_rx)!=0);
  10. }



现在读出的数据时正常的,但是我发现两包数据的间距挺大的,见图2(在加不加这个等待时间都挺多的,怀疑是因为调用HAL的原因)

下午看写一个DMA_SPI读写函数了,可以参考下大哥你用固件库和寄存器写的关于SPI_DMA的读写库吗?  谢谢啦


图1.png
图2.png
呐咯密密 发表于 2021-12-2 13:04 | 显示全部楼层
仅供参考,我的主控是STM32F031
  1. void SPI_DMA_WriteRead(void)
  2. {
  3.         GPIO_ResetBits(GPIOA, GPIO_Pin_15);//拉低片选       
  4.         SPI1->CR2 |= SPI_I2S_DMAReq_Tx;//SPI 发送DMA使能       
  5.         SPI1->CR2 |= SPI_I2S_DMAReq_Rx;//SPI 接收DMA使能
  6.         DMA1_Channel3->CCR &= (uint16_t)(~DMA_CCR_EN);  //失能DMA通道3
  7.         DMA1_Channel3->CNDTR = DMA1_MEM_LEN;                        //传输长度
  8.         DMA1_Channel3->CCR |= DMA_CCR_EN;               //使能DMA通道3
  9.         DMA1_Channel2->CCR &= (uint16_t)(~DMA_CCR_EN);  //失能DMA通道2
  10.         DMA1_Channel2->CNDTR = DMA1_MEM_LEN;                        //传输长度
  11.         DMA1_Channel2->CCR |= DMA_CCR_EN;               //使能DMA通道2
  12.         if(DMA_GetFlagStatus(DMA1_FLAG_TC3) == RESET)
  13.         {
  14.                 DMA_ClearFlag(DMA1_FLAG_TC3);                       
  15.         }                                       
  16.         while(SPI_I2S_GetFlagStatus(SPI1,SPI_I2S_FLAG_TXE) == RESET);       
  17.         while(SPI_I2S_GetFlagStatus(SPI1,SPI_I2S_FLAG_BSY) == RESET);        //保证发送接收数据完整
  18.         GPIO_SetBits(GPIOA, GPIO_Pin_15);//拉高片选

  19. }


呐咯密密 发表于 2021-12-2 13:05 | 显示全部楼层
库函数版本:
  1. void SPI_DMA_WriteReadByte(void)
  2. {
  3.         GPIO_ResetBits(GPIOA, GPIO_Pin_15);//拉低片选                (放在此处为了节省0.5us的时间)
  4.         SPI_I2S_DMACmd(SPI1,SPI_I2S_DMAReq_Tx, ENABLE);//SPI 发送DMA使能                       
  5.         SPI_I2S_DMACmd(SPI1,SPI_I2S_DMAReq_Rx, ENABLE);//SPI 接收DMA使能
  6.         MYDMA_TX_Enable(DMA1_Channel3);                //发送
  7.         MYDMA_RX_Enable(DMA1_Channel2);                //接收
  8.         if(DMA_GetFlagStatus(DMA1_FLAG_TC3) == RESET)
  9.         {
  10.                 DMA_ClearFlag(DMA1_FLAG_TC3);                       
  11.         }                                       
  12.         while(SPI_I2S_GetFlagStatus(SPI1,SPI_I2S_FLAG_TXE) == RESET);       
  13.         while(SPI_I2S_GetFlagStatus(SPI1,SPI_I2S_FLAG_BSY) == RESET);        //保证发送接收数据完整
  14.         GPIO_SetBits(GPIOA, GPIO_Pin_15);//拉高片选

  15. }
  1. /************************************************
  2. 函数名称 : MYDMA_TX_Enable
  3. 功    能 : DMA发送
  4. 参    数 : 无
  5. 返 回 值 : 无
  6. 作    者 : Mico
  7. *************************************************/
  8. void MYDMA_TX_Enable(DMA_Channel_TypeDef*DMA_CHx)
  9. {
  10.         DMA_Cmd(DMA_CHx, DISABLE );  //关闭SPI TX DMA1 所指示的通道      
  11.         DMA_SetCurrDataCounter(DMA_CHx,DMA1_MEM_LEN);//DMA通道的DMA缓存的大小
  12.         DMA_Cmd(DMA_CHx, ENABLE);  //使能SPI TX DMA1 所指示的通道
  13. }          
  14. /************************************************
  15. 函数名称 : MYDMA_RX_Enable
  16. 功    能 : DMA接收
  17. 参    数 : 无
  18. 返 回 值 : 无
  19. 作    者 : Mico
  20. *************************************************/
  21. void MYDMA_RX_Enable(DMA_Channel_TypeDef*DMA_CHx)
  22. {
  23.         DMA_Cmd(DMA_CHx, DISABLE );  //关闭SPI RX DMA1 所指示的通道      
  24.         DMA_SetCurrDataCounter(DMA_CHx,DMA1_MEM_LEN);//DMA通道的DMA缓存的大小
  25.         DMA_Cmd(DMA_CHx, ENABLE);  //使能SPI RX DMA1 所指示的通道
  26. }       


 楼主| chenyuanjiyi 发表于 2021-12-2 13:41 | 显示全部楼层

好的  谢谢啦
 楼主| chenyuanjiyi 发表于 2021-12-2 16:26 | 显示全部楼层
  1. HAL_StatusTypeDef HAL_SPI_MY_TransmitReceive_DMA0(SPI_HandleTypeDef *hspi, uint8_t *pTxData, uint8_t *pRxData,uint16_t Size)
  2. {
  3.   HAL_StatusTypeDef errorcode = HAL_OK;
  4.   static uint8_t Flag=0;
  5.   /* Process locked */
  6. //  __HAL_LOCK(hspi);

  7.   /* Reset the threshold bit */
  8.   CLEAR_BIT(hspi->Instance->CR2, SPI_CR2_LDMATX | SPI_CR2_LDMARX);

  9.   /* Set fiforxthresold according the reception data length: 8bit */
  10.   SET_BIT(hspi->Instance->CR2, SPI_RXFIFO_THRESHOLD);

  11.   /* Enable the Rx DMA Stream/Channel  */
  12. //  HAL_DMA_Start_IT(hspi->hdmarx, (uint32_t)&hspi->Instance->DR, (uint32_t)pRxData, Size);
  13. {
  14. /* Disable the peripheral */
  15.           hdma_spi1_rx.Instance->CCR &= ~DMA_CCR_EN;
  16.          
  17.           /* Configure the source, destination address and the data length */  
  18. //          DMA_SetConfig(hdma_spi1_rx, (uint32_t)&hspi->Instance->DR, (uint32_t)pRxData, Size);
  19. {
  20. /* Clear all flags */
  21.   hdma_spi1_rx.DmaBaseAddress->IFCR  = (DMA_FLAG_GL1 << hdma_spi1_rx.ChannelIndex);
  22.   
  23.   /* Configure DMA Channel data length */
  24.   hdma_spi1_rx.Instance->CNDTR = Size;
  25.   
  26.   /* Peripheral to Memory */
  27.   {
  28.     /* Configure DMA Channel source address */
  29.     hdma_spi1_rx.Instance->CPAR = (uint32_t)&hspi->Instance->DR;
  30.    
  31.     /* Configure DMA Channel destination address */
  32.     hdma_spi1_rx.Instance->CMAR = (uint32_t)pRxData;
  33.   }
  34. }
  35.           /* Enable the transfer complete, & transfer error interrupts */
  36.           /* Half transfer interrupt is optional: enable it only if associated callback is available */
  37.       hdma_spi1_rx.Instance->CCR |= (DMA_IT_TC | DMA_IT_HT | DMA_IT_TE);
  38.          
  39.           /* Enable the Peripheral */
  40.           hdma_spi1_rx.Instance->CCR |= DMA_CCR_EN;

  41. }
  42.   
  43.   /* Enable Rx DMA Request */
  44.   SET_BIT(hspi->Instance->CR2, SPI_CR2_RXDMAEN);
  45.   
  46.   /* Enable the Tx DMA Stream/Channel  */
  47. //  HAL_DMA_Start_IT(hspi->hdmatx, (uint32_t)pTxData, (uint32_t)&hspi->Instance->DR,Size);
  48. {
  49. /* Disable the peripheral */
  50.           hdma_spi1_tx.Instance->CCR &= ~DMA_CCR_EN;
  51.          
  52.           /* Configure the source, destination address and the data length */  
  53. //          DMA_SetConfig(hdma_spi1_rx, (uint32_t)&hspi->Instance->DR, (uint32_t)pRxData, Size);
  54. {
  55. /* Clear all flags */
  56.   hdma_spi1_tx.DmaBaseAddress->IFCR  = (DMA_FLAG_GL1 << hdma_spi1_rx.ChannelIndex);
  57.   
  58.   /* Configure DMA Channel data length */
  59.   hdma_spi1_tx.Instance->CNDTR = Size;
  60.   
  61. /* Memory to Peripheral */
  62.   {   
  63.     /* Configure DMA Channel destination address */
  64.     hdma_spi1_tx.Instance->CPAR = (uint32_t)&hspi->Instance->DR;
  65.    
  66.     /* Configure DMA Channel source address */
  67.     hdma_spi1_tx.Instance->CMAR = (uint32_t)pTxData;
  68.   }
  69.   
  70. }
  71.           /* Enable the transfer complete, & transfer error interrupts */
  72.           /* Half transfer interrupt is optional: enable it only if associated callback is available */
  73.       hdma_spi1_tx.Instance->CCR |= (DMA_IT_TC | DMA_IT_HT | DMA_IT_TE);

  74.           HAL_GPIO_WritePin(GPIOA, GPIO_PIN_15, GPIO_PIN_RESET);
  75.           /* Enable the Peripheral */
  76.           hdma_spi1_tx.Instance->CCR |= DMA_CCR_EN;

  77. }
  78.   /* Check if the SPI is already enabled */
  79.   if ((hspi->Instance->CR1 & SPI_CR1_SPE) != SPI_CR1_SPE)
  80.   {
  81.     /* Enable SPI peripheral */
  82.     __HAL_SPI_ENABLE(hspi);
  83.   }
  84.   /* Enable the SPI Error Interrupt Bit */
  85.   __HAL_SPI_ENABLE_IT(hspi, (SPI_IT_ERR));

  86.   /* Enable Tx DMA Request */
  87.   SET_BIT(hspi->Instance->CR2, SPI_CR2_TXDMAEN);

  88. error :
  89.   /* Process Unlocked */
  90. //  __HAL_UNLOCK(hspi);
  91.   return errorcode;
  92. }
这是我改完后的SPI_DMA的读写函数,主要针对 HAL_SPI_TransmitReceive_DMA()函数,对其进行删减,根据我SPI_DMA的配置,将此函数里的一些判断,设置等删除,然后留下了这些操作,测试发现两包数据间的间隔又27.8us左右,降到了16.5us左右,这个16.5us目前没想到方法降了(DMA的配置已拉满,NSS引脚速度也达到最大了)
目前只能怀疑是F072的芯片的48M速度导致的这个原因了(因为这个读写的基本都是对寄存器操作了),后面测试下F103的72M速度,相同配置看看能加快多少了


呐咯密密 发表于 2021-12-2 17:10 | 显示全部楼层
chenyuanjiyi 发表于 2021-12-2 16:26
这是我改完后的SPI_DMA的读写函数,主要针对 HAL_SPI_TransmitReceive_DMA()函数,对其进行删减,根据 ...

关于两个数据之间的时间间隔不要用逻辑分析仪去抓,用示波器去抓,抓完拍照给我看一下。
您需要登录后才可以回帖 登录 | 注册

本版积分规则

16

主题

134

帖子

3

粉丝
快速回复 在线客服 返回列表 返回顶部