OpenEdv-开源电子网

 找回密码
 立即注册
正点原子全套STM32/Linux/FPGA开发资料,上千讲STM32视频教程免费下载...
查看: 5805|回复: 10

SPI(硬件控制NSS)+DMA 基于HAL库的传输问题

[复制链接]

41

主题

278

帖子

0

精华

金牌会员

Rank: 6Rank: 6

积分
2357
金钱
2357
注册时间
2019-10-29
在线时间
380 小时
发表于 2021-11-23 18:27:16 | 显示全部楼层 |阅读模式
20金钱
本帖最后由 chenyuan 于 2021-11-29 14:11 编辑

之前是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的现象就是更改此宏出来的


3.png
2.png
1.png

SPI_DMA_Test.rar

9.4 MB, 下载次数: 54

最佳答案

查看完整内容[请看2#楼]

通过修改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行); 测试效果如下,发送5个字节大概需要5.16us,NSS上 ...
正点原子逻辑分析仪DL16劲爆上市
回复

使用道具 举报

41

主题

278

帖子

0

精华

金牌会员

Rank: 6Rank: 6

积分
2357
金钱
2357
注册时间
2019-10-29
在线时间
380 小时
 楼主| 发表于 2021-11-23 18:27:17 | 显示全部楼层
本帖最后由 chenyuan 于 2021-12-31 09:12 编辑

通过修改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. * 在it.c中,要先调用
  3.   HAL_DMA_IRQHandler(&hdma_spi1_rx);
  4.   HAL_SPI1_TX_DMA_IRQHandler(&hdma_spi1_tx);//在此函数中会对NSS引脚拉高
  5. */
  6. void HAL_SPI1_TX_DMA_IRQHandler(DMA_HandleTypeDef *hdma)
  7. {
  8.   ................................省略.....
  9.   /* Transfer Complete Interrupt management ***********************************/
  10.   else if ((RESET != (flag_it & (DMA_FLAG_TC1 << hdma->ChannelIndex))) && (RESET != (source_it & DMA_IT_TC)))
  11.   {
  12.           if((hdma->Instance->CCR & DMA_CCR_CIRC) == 0U)
  13.           {
  14.                   /* Disable the transfer complete  & transfer error interrupts */
  15.                   /* if the DMA mode is not CIRCULAR */
  16.                   hdma->Instance->CCR &= ~(DMA_IT_TC | DMA_IT_TE);
  17.       
  18.                   /* Change the DMA state */
  19.                   hdma->State = HAL_DMA_STATE_READY;
  20.           }
  21.         
  22.           /* Clear the transfer complete flag */
  23.           hdma->DmaBaseAddress->IFCR = DMA_FLAG_TC1 << hdma->ChannelIndex;

  24.     /*nss up*/
  25.     HAL_GPIO_WritePin(GPIOA, GPIO_PIN_15, GPIO_PIN_SET);

  26.           /* Process Unlocked */
  27.           __HAL_UNLOCK(hdma);

  28.           if(hdma->XferCpltCallback != NULL)
  29.           {
  30.                   /* Transfer complete callback */
  31.                   hdma->XferCpltCallback(hdma);
  32.           }
  33.   }
  34. .............................省略

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

测试记录步骤:https://bbs.21ic.com/forum.php?m ... ;page=1#pid12510066



测试结果.png
回复

使用道具 举报

13

主题

643

帖子

0

精华

金牌会员

Rank: 6Rank: 6

积分
2432
金钱
2432
注册时间
2019-12-28
在线时间
527 小时
发表于 2021-11-23 18:31:02 | 显示全部楼层
帮顶  
回复

使用道具 举报

1

主题

385

帖子

0

精华

金牌会员

Rank: 6Rank: 6

积分
1023
金钱
1023
注册时间
2019-9-21
在线时间
269 小时
发表于 2021-11-23 18:31:10 | 显示全部楼层
帮顶一下。
回复

使用道具 举报

41

主题

278

帖子

0

精华

金牌会员

Rank: 6Rank: 6

积分
2357
金钱
2357
注册时间
2019-10-29
在线时间
380 小时
 楼主| 发表于 2021-11-24 09:15:37 | 显示全部楼层
顶.....
回复

使用道具 举报

2

主题

592

帖子

0

精华

金牌会员

Rank: 6Rank: 6

积分
1458
金钱
1458
注册时间
2019-7-28
在线时间
137 小时
发表于 2021-11-24 18:44:02 | 显示全部楼层
帮顶   
回复

使用道具 举报

530

主题

11万

帖子

34

精华

管理员

Rank: 12Rank: 12Rank: 12

积分
165516
金钱
165516
注册时间
2010-12-1
在线时间
2116 小时
发表于 2021-11-25 00:51:20 | 显示全部楼层
1,不要用硬件SS. 2,用寄存器写DMA启动发送函数。
回复

使用道具 举报

41

主题

278

帖子

0

精华

金牌会员

Rank: 6Rank: 6

积分
2357
金钱
2357
注册时间
2019-10-29
在线时间
380 小时
 楼主| 发表于 2021-11-25 09:31:03 | 显示全部楼层
正点原子 发表于 2021-11-25 00:51
1,不要用硬件SS. 2,用寄存器写DMA启动发送函数。

1.用软件控制NSS,然后直接调用HAL库的 SPI_DMA读写函数,就会出现上图1的现象,然后加上一些等待,就会变成下图的现象,NSS拉低后,好长时间才会进行传输

2.使用寄存器进行DMA的传输,是这样的吗,以发送为例(百度找到的相关SPI_DMA操作):
  1. /*******************************************************************************
  2. * Function Name  : SPI1_Send
  3. * Description    : SPI1的DMA方式发送
  4. * Input          : SPI1_TX_Buff[SPI1_SendBufferSize]
  5. * Output         : None
  6. * Return         : None
  7. * Attention             : 关闭DMA通道3之前必须等待TXE为1,等待忙标志为0
  8. *******************************************************************************/
  9. void SPI1_Send( u8 *buff, u32 len )
  10. {
  11.     DMA1_Channel3->CPAR = SPI1_DR_Addr; //外设地址
  12.     DMA1_Channel3->CMAR = (u32) buff; //mem地址
  13.     DMA1_Channel3->CNDTR = len ; //传输长度
  14.     DMA1_Channel3->CCR = (0 << 14) | // 非存储器到存储器模式
  15.             (2 << 12) | // 通道优先级高
  16.             (0 << 11) | // 存储器数据宽度8bit
  17.             (0 << 10) | // 存储器数据宽度8bit
  18.             (0 <<  9) | // 外设数据宽度8bit
  19.             (0 <<  8) | // 外设数据宽度8bit
  20.             (1 <<  7) | // 存储器地址增量模式
  21.             (0 <<  6) | // 外设地址增量模式(不增)
  22.             (0 <<  5) | // 非循环模式
  23.             (1 <<  4) | // 从存储器读
  24.             (1 <<  3) | // 允许传输错误中断
  25.             (0 <<  2) | // 允许半传输中断
  26.             (1 <<  1) | // 允许传输完成中断
  27.             (1);        // 通道开启
  28. }

  29. /*
  30. * 实际调用
  31. */
  32. {
  33. NSS_L; //拉低NSS
  34. SPI1_Send( buff, 8 )
  35. NSS_H; //拉高NSS
  36. }


复制代码




snipaste_20211125_091802.png
回复

使用道具 举报

41

主题

278

帖子

0

精华

金牌会员

Rank: 6Rank: 6

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

有类似SPI操作的 相关的资料 和 例程 可以@我

谢谢啦!


snipaste_20211125_110120.png
回复

使用道具 举报

41

主题

278

帖子

0

精华

金牌会员

Rank: 6Rank: 6

积分
2357
金钱
2357
注册时间
2019-10-29
在线时间
380 小时
 楼主| 发表于 2021-11-29 14:13:54 | 显示全部楼层
本帖最后由 chenyuan 于 2021-11-29 14:15 编辑

今天进行了进一步测试,采用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
回复

使用道具 举报

0

主题

2

帖子

0

精华

新手上路

积分
22
金钱
22
注册时间
2019-8-11
在线时间
3 小时
发表于 2021-12-30 09:35:06 | 显示全部楼层
关注ing
回复

使用道具 举报

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

本版积分规则



关闭

原子哥极力推荐上一条 /2 下一条

正点原子公众号

QQ|手机版|OpenEdv-开源电子网 ( 粤ICP备12000418号-1 )

GMT+8, 2025-5-20 23:01

Powered by OpenEdv-开源电子网

© 2001-2030 OpenEdv-开源电子网

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