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

[复制链接]
557|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函数中
  while (1)
  {
    /* USER CODE END WHILE */
HAL_GPIO_WritePin(GPIOA,GPIO_PIN_15,GPIO_PIN_RESET);
    HAL_SPI_TransmitReceive(&hspi1,txbuff,rxbuff,8);
     HAL_GPIO_WritePin(GPIOA,GPIO_PIN_15,GPIO_PIN_SET);
   
    HAL_GPIO_TogglePin(LED_Red_GPIO_Port,LED_Red_Pin);
      HAL_Delay(50);
    /* USER CODE BEGIN 3 */
  }
然后我就想着将软件控制NSS改成硬件控制,让其自动切换,SPI初始化配置代码
void MX_SPI1_Init(void)
{

  hspi1.Instance = SPI1;
  hspi1.Init.Mode = SPI_MODE_MASTER;
  hspi1.Init.Direction = SPI_DIRECTION_2LINES;
  hspi1.Init.DataSize = SPI_DATASIZE_8BIT;
  hspi1.Init.CLKPolarity = SPI_POLARITY_LOW;
  hspi1.Init.CLKPhase = SPI_PHASE_1EDGE;

  hspi1.Init.NSS = SPI_NSS_HARD_INPUT;
  hspi1.Init.BaudRatePrescaler = SPI_BAUDRATEPRESCALER_4;
  hspi1.Init.FirstBit = SPI_FIRSTBIT_MSB;
  hspi1.Init.TIMode = SPI_TIMODE_DISABLE;
  hspi1.Init.CRCCalculation = SPI_CRCCALCULATION_DISABLE;
  hspi1.Init.CRCPolynomial = 7;
  hspi1.Init.CRCLength = SPI_CRC_LENGTH_DATASIZE;
  hspi1.Init.NSSPMode = SPI_NSS_PULSE_ENABLE;
  if (HAL_SPI_Init(&hspi1) != HAL_OK)
  {
    Error_Handler();
  }

}
然后测试发现,每发一个字节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_155649.png
snipaste_20211125_155610.png

SPI_DMA_Test.rar

9.4 MB, 下载次数: 1

使用特权

评论回复
chenyuanjiyi|  楼主 | 2021-11-30 17:23 | 显示全部楼层
本帖最后由 chenyuanjiyi 于 2021-11-30 17:35 编辑

通过修改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行);
/*
    * 复制HAL_SPI_TransmitReceive_DMA()函数,在开启DMA前,拉低NSS引脚
    */
    HAL_StatusTypeDef HAL_SPI_MY_TransmitReceive_DMA(SPI_HandleTypeDef *hspi, uint8_t *pTxData, uint8_t *pRxData, uint16_t Size)
    {
      ..................................
    ................................省略.....
      /* Set the SPI Tx DMA transfer complete callback as NULL because the communication closing is performed in DMA reception complete callback  */

      hspi->hdmatx->XferHalfCpltCallback = NULL;
      hspi->hdmatx->XferCpltCallback     = NULL;
      hspi->hdmatx->XferErrorCallback    = NULL;
      hspi->hdmatx->XferAbortCallback    = NULL;


      /* Enable the Tx DMA Stream/Channel  */
      HAL_DMA_Start_IT(hspi->hdmatx, (uint32_t)hspi->pTxBuffPtr, (uint32_t)&hspi->Instance->DR, hspi->TxXferCount);
      
    /* Check if the SPI is already enabled */
      if ((hspi->Instance->CR1 & SPI_CR1_SPE) != SPI_CR1_SPE)
      {
        /* Enable SPI peripheral */
        __HAL_SPI_ENABLE(hspi);
      }
      /* Enable the SPI Error Interrupt Bit */
      __HAL_SPI_ENABLE_IT(hspi, (SPI_IT_ERR));

    HAL_GPIO_WritePin(GPIOA, GPIO_PIN_15, GPIO_PIN_RESET);

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

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

      hspi->hdmatx->XferHalfCpltCallback = NULL;
      hspi->hdmatx->XferCpltCallback     = NULL;
      hspi->hdmatx->XferErrorCallback    = NULL;
      hspi->hdmatx->XferAbortCallback    = NULL;


      /* Enable the Tx DMA Stream/Channel  */
      HAL_DMA_Start_IT(hspi->hdmatx, (uint32_t)hspi->pTxBuffPtr, (uint32_t)&hspi->Instance->DR, hspi->TxXferCount);
      
    /* Check if the SPI is already enabled */
      if ((hspi->Instance->CR1 & SPI_CR1_SPE) != SPI_CR1_SPE)
      {
        /* Enable SPI peripheral */
        __HAL_SPI_ENABLE(hspi);
      }
      /* Enable the SPI Error Interrupt Bit */
      __HAL_SPI_ENABLE_IT(hspi, (SPI_IT_ERR));

    HAL_GPIO_WritePin(GPIOA, GPIO_PIN_15, GPIO_PIN_RESET);

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

    error :
      /* Process Unlocked */
      __HAL_UNLOCK(hspi);
      return errorcode;
    }   

测试效果如下,发送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位的地址的数据,这样就导致了每个字节都抬高,然后读出的数据错误

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

  hspi1.Instance = SPI1;
  hspi1.Init.Mode = SPI_MODE_MASTER;
  hspi1.Init.Direction = SPI_DIRECTION_2LINES;
  hspi1.Init.DataSize = SPI_DATASIZE_8BIT;
  hspi1.Init.CLKPolarity = SPI_POLARITY_LOW;
  hspi1.Init.CLKPhase = SPI_PHASE_1EDGE;
  #if (SPI_DMA==0)
  hspi1.Init.NSS = SPI_NSS_SOFT;//SPI_NSS_HARD_INPUT;
  #else
  hspi1.Init.NSS = SPI_NSS_HARD_OUTPUT;
  #endif
  hspi1.Init.BaudRatePrescaler = SPI_BAUDRATEPRESCALER_4;
  hspi1.Init.FirstBit = SPI_FIRSTBIT_MSB;
  hspi1.Init.TIMode = SPI_TIMODE_DISABLE;
  hspi1.Init.CRCCalculation = SPI_CRCCALCULATION_DISABLE;
  hspi1.Init.CRCPolynomial = 7;
  hspi1.Init.CRCLength = SPI_CRC_LENGTH_DATASIZE;
  hspi1.Init.NSSPMode = SPI_NSS_PULSE_DISABLE;
  if (HAL_SPI_Init(&hspi1) != HAL_OK)
  {
    Error_Handler();
  }

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


/*
函数功能:读指令
参数:指令 数据  大小
*/
void SX1280HalReadCommand( RadioCommands_t command, uint8_t *buffer, uint16_t size )
{
    uint16_t halSize = 2 + size;
    halTxBuffer[0] = command;
    halTxBuffer[1] = 0x00;
    for( uint16_t index = 0; index < size; index++ )
    {
        halTxBuffer[2+index] = 0x00;
    }

    SX1280HalWaitOnBusy( );

    SpiInOut( halTxBuffer, halRxBuffer, halSize );

    memcpy( buffer, halRxBuffer + 2, size );

    SX1280HalWaitOnBusy( );
}


死机原因是因为每次对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)
 /* Check if the SPI is already enabled */
  if ((hspi->Instance->CR1 & SPI_CR1_SPE) != SPI_CR1_SPE)
  {
    /* Enable SPI peripheral */
    __HAL_SPI_ENABLE(hspi);
  }
  /* Enable the SPI Error Interrupt Bit */
  __HAL_SPI_ENABLE_IT(hspi, (SPI_IT_ERR));
HAL_GPIO_WritePin(GPIOA, GPIO_PIN_15, GPIO_PIN_RESET);
  /* Enable Tx DMA Request */
  SET_BIT(hspi->Instance->CR2, SPI_CR2_TXDMAEN);

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

  /* USER CODE END DMA1_Channel2_3_IRQn 0 */
  HAL_DMA_IRQHandler(&hdma_spi1_rx);
  HAL_DMA_IRQHandler(&hdma_spi1_tx);
  /* USER CODE BEGIN DMA1_Channel2_3_IRQn 1 */
if(__HAL_DMA_GET_IT_SOURCE(&hdma_spi1_rx, DMA_IT_TC)){
  HAL_GPIO_WritePin(GPIOA, GPIO_PIN_15, GPIO_PIN_SET);
  }
  /* USER CODE END DMA1_Channel2_3_IRQn 1 */
}
当判断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
uint16_t SX1280GetFirmwareVersion( void )
{
    return( ( ( SX1280HalReadRegister( REG_LR_FIRMWARE_VERSION_MSB ) ) << 8 ) | ( SX1280HalReadRegister( REG_LR_FIRMWARE_VERSION_MSB + 1 ) ) );
//返回 0xA9<<8|0xB7
}

我用的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;

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

/*
函数功能:读字节
参数:写入的数据  读取的数据 大小
*/
void SpiInOut( uint8_t *txBuffer, uint8_t *rxBuffer, uint16_t size )
{
    SX1280_SPI_WR(txBuffer,rxBuffer,size);
  while(__HAL_DMA_GET_COUNTER(&hdma_spi1_rx)!=0);
}



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

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


图2.png
图1.png

使用特权

评论回复
呐咯密密| | 2021-12-2 13:04 | 显示全部楼层
仅供参考,我的主控是STM32F031
void SPI_DMA_WriteRead(void)
{
        GPIO_ResetBits(GPIOA, GPIO_Pin_15);//拉低片选       
        SPI1->CR2 |= SPI_I2S_DMAReq_Tx;//SPI 发送DMA使能       
        SPI1->CR2 |= SPI_I2S_DMAReq_Rx;//SPI 接收DMA使能
        DMA1_Channel3->CCR &= (uint16_t)(~DMA_CCR_EN);  //失能DMA通道3
        DMA1_Channel3->CNDTR = DMA1_MEM_LEN;                        //传输长度
        DMA1_Channel3->CCR |= DMA_CCR_EN;               //使能DMA通道3
        DMA1_Channel2->CCR &= (uint16_t)(~DMA_CCR_EN);  //失能DMA通道2
        DMA1_Channel2->CNDTR = DMA1_MEM_LEN;                        //传输长度
        DMA1_Channel2->CCR |= DMA_CCR_EN;               //使能DMA通道2
        if(DMA_GetFlagStatus(DMA1_FLAG_TC3) == RESET)
        {
                DMA_ClearFlag(DMA1_FLAG_TC3);                       
        }                                       
        while(SPI_I2S_GetFlagStatus(SPI1,SPI_I2S_FLAG_TXE) == RESET);       
        while(SPI_I2S_GetFlagStatus(SPI1,SPI_I2S_FLAG_BSY) == RESET);        //保证发送接收数据完整
        GPIO_SetBits(GPIOA, GPIO_Pin_15);//拉高片选

}


使用特权

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

}
/************************************************
函数名称 : MYDMA_TX_Enable
功    能 : DMA发送
参    数 : 无
返 回 值 : 无
作    者 : Mico
*************************************************/
void MYDMA_TX_Enable(DMA_Channel_TypeDef*DMA_CHx)
{
        DMA_Cmd(DMA_CHx, DISABLE );  //关闭SPI TX DMA1 所指示的通道      
        DMA_SetCurrDataCounter(DMA_CHx,DMA1_MEM_LEN);//DMA通道的DMA缓存的大小
        DMA_Cmd(DMA_CHx, ENABLE);  //使能SPI TX DMA1 所指示的通道
}          
/************************************************
函数名称 : MYDMA_RX_Enable
功    能 : DMA接收
参    数 : 无
返 回 值 : 无
作    者 : Mico
*************************************************/
void MYDMA_RX_Enable(DMA_Channel_TypeDef*DMA_CHx)
{
        DMA_Cmd(DMA_CHx, DISABLE );  //关闭SPI RX DMA1 所指示的通道      
        DMA_SetCurrDataCounter(DMA_CHx,DMA1_MEM_LEN);//DMA通道的DMA缓存的大小
        DMA_Cmd(DMA_CHx, ENABLE);  //使能SPI RX DMA1 所指示的通道
}       


使用特权

评论回复
chenyuanjiyi|  楼主 | 2021-12-2 13:41 | 显示全部楼层

好的  谢谢啦

使用特权

评论回复
chenyuanjiyi|  楼主 | 2021-12-2 16:26 | 显示全部楼层
HAL_StatusTypeDef HAL_SPI_MY_TransmitReceive_DMA0(SPI_HandleTypeDef *hspi, uint8_t *pTxData, uint8_t *pRxData,uint16_t Size)
{
  HAL_StatusTypeDef errorcode = HAL_OK;
  static uint8_t Flag=0;
  /* Process locked */
//  __HAL_LOCK(hspi);

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

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

  /* Enable the Rx DMA Stream/Channel  */
//  HAL_DMA_Start_IT(hspi->hdmarx, (uint32_t)&hspi->Instance->DR, (uint32_t)pRxData, Size);
{
/* Disable the peripheral */
          hdma_spi1_rx.Instance->CCR &= ~DMA_CCR_EN;
         
          /* Configure the source, destination address and the data length */  
//          DMA_SetConfig(hdma_spi1_rx, (uint32_t)&hspi->Instance->DR, (uint32_t)pRxData, Size);
{
/* Clear all flags */
  hdma_spi1_rx.DmaBaseAddress->IFCR  = (DMA_FLAG_GL1 << hdma_spi1_rx.ChannelIndex);
  
  /* Configure DMA Channel data length */
  hdma_spi1_rx.Instance->CNDTR = Size;
  
  /* Peripheral to Memory */
  {
    /* Configure DMA Channel source address */
    hdma_spi1_rx.Instance->CPAR = (uint32_t)&hspi->Instance->DR;
   
    /* Configure DMA Channel destination address */
    hdma_spi1_rx.Instance->CMAR = (uint32_t)pRxData;
  }
}
          /* Enable the transfer complete, & transfer error interrupts */
          /* Half transfer interrupt is optional: enable it only if associated callback is available */
      hdma_spi1_rx.Instance->CCR |= (DMA_IT_TC | DMA_IT_HT | DMA_IT_TE);
         
          /* Enable the Peripheral */
          hdma_spi1_rx.Instance->CCR |= DMA_CCR_EN;

}
  
  /* Enable Rx DMA Request */
  SET_BIT(hspi->Instance->CR2, SPI_CR2_RXDMAEN);
  
  /* Enable the Tx DMA Stream/Channel  */
//  HAL_DMA_Start_IT(hspi->hdmatx, (uint32_t)pTxData, (uint32_t)&hspi->Instance->DR,Size);
{
/* Disable the peripheral */
          hdma_spi1_tx.Instance->CCR &= ~DMA_CCR_EN;
         
          /* Configure the source, destination address and the data length */  
//          DMA_SetConfig(hdma_spi1_rx, (uint32_t)&hspi->Instance->DR, (uint32_t)pRxData, Size);
{
/* Clear all flags */
  hdma_spi1_tx.DmaBaseAddress->IFCR  = (DMA_FLAG_GL1 << hdma_spi1_rx.ChannelIndex);
  
  /* Configure DMA Channel data length */
  hdma_spi1_tx.Instance->CNDTR = Size;
  
/* Memory to Peripheral */
  {   
    /* Configure DMA Channel destination address */
    hdma_spi1_tx.Instance->CPAR = (uint32_t)&hspi->Instance->DR;
   
    /* Configure DMA Channel source address */
    hdma_spi1_tx.Instance->CMAR = (uint32_t)pTxData;
  }
  
}
          /* Enable the transfer complete, & transfer error interrupts */
          /* Half transfer interrupt is optional: enable it only if associated callback is available */
      hdma_spi1_tx.Instance->CCR |= (DMA_IT_TC | DMA_IT_HT | DMA_IT_TE);

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

}
  /* Check if the SPI is already enabled */
  if ((hspi->Instance->CR1 & SPI_CR1_SPE) != SPI_CR1_SPE)
  {
    /* Enable SPI peripheral */
    __HAL_SPI_ENABLE(hspi);
  }
  /* Enable the SPI Error Interrupt Bit */
  __HAL_SPI_ENABLE_IT(hspi, (SPI_IT_ERR));

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

error :
  /* Process Unlocked */
//  __HAL_UNLOCK(hspi);
  return errorcode;
}
这是我改完后的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()函数,对其进行删减,根据 ...

关于两个数据之间的时间间隔不要用逻辑分析仪去抓,用示波器去抓,抓完拍照给我看一下。

使用特权

评论回复
您需要登录后才可以回帖 登录 | 注册

本版积分规则