关于HAL库SPI作为从机,接收主机发来的数据的超时溢出硬件错误中断等问题的硬解法

 一、问题产生基本情况

一、1 主机

        主机F407,时钟频率较高。

        SPI具体配置如下:

  hspi1.Instance = SPI1;
  hspi1.Init.Mode = SPI_MODE_MASTER;
  hspi1.Init.Direction = SPI_DIRECTION_2LINES;
  hspi1.Init.DataSize = SPI_DATASIZE_16BIT;
  hspi1.Init.CLKPolarity = SPI_POLARITY_HIGH;
  hspi1.Init.CLKPhase = SPI_PHASE_2EDGE;
  hspi1.Init.NSS = SPI_NSS_SOFT;
//  hspi1.Init.BaudRatePrescaler = SPI_BAUDRATEPRESCALER_8;	
  hspi1.Init.BaudRatePrescaler = SPI_BAUDRATEPRESCALER_16;	
//  hspi1.Init.BaudRatePrescaler = SPI_BAUDRATEPRESCALER_32;	
  hspi1.Init.FirstBit = SPI_FIRSTBIT_MSB;
  hspi1.Init.TIMode = SPI_TIMODE_DISABLE;
//  hspi1.Init.CRCCalculation = SPI_CRCCALCULATION_ENABLE;
  hspi1.Init.CRCCalculation = SPI_CRCCALCULATION_DISABLE;
//  hspi1.Init.CRCPolynomial = 7;	
  hspi1.Init.CRCPolynomial = 10;

CUBEMX配置:

PS:由于后期更改都是直接改的代码,并不完全相同(仅供参考)

首先是时钟树如下图。

关于HAL库SPI作为从机,接收主机发来的数据的超时溢出硬件错误中断等问题的硬解法_第1张图片

然后是SPI配置如下图。

关于HAL库SPI作为从机,接收主机发来的数据的超时溢出硬件错误中断等问题的硬解法_第2张图片

一、2 从机

主机F302,时钟频率次之。

SPI具体配置如下:

  hspi2.Instance = SPI2;
  hspi2.Init.Mode = SPI_MODE_SLAVE;
  hspi2.Init.Direction = SPI_DIRECTION_2LINES;
  hspi2.Init.DataSize = SPI_DATASIZE_16BIT;
  hspi2.Init.CLKPolarity = SPI_POLARITY_HIGH;
  hspi2.Init.CLKPhase = SPI_PHASE_2EDGE;
  hspi2.Init.NSS = SPI_NSS_HARD_INPUT;
  hspi2.Init.FirstBit = SPI_FIRSTBIT_MSB;
  hspi2.Init.TIMode = SPI_TIMODE_DISABLE;
  hspi2.Init.CRCCalculation = SPI_CRCCALCULATION_DISABLE;
  hspi2.Init.CRCPolynomial = 10;
  hspi2.Init.CRCLength = SPI_CRC_LENGTH_DATASIZE;
  hspi2.Init.NSSPMode = SPI_NSS_PULSE_DISABLE;

CUBEMX配置:

PS:由于后期更改都是直接改的代码,并不完全相同(仅供参考)

首先是时钟树如下图。

关于HAL库SPI作为从机,接收主机发来的数据的超时溢出硬件错误中断等问题的硬解法_第3张图片

然后是SPI配置如下图。

关于HAL库SPI作为从机,接收主机发来的数据的超时溢出硬件错误中断等问题的硬解法_第4张图片

二、解决经过

如果您熟悉或了解标题一中的东西,通过上面的一些配置我们对大致情况有了一定的了解,正如我刚接手项目的时候,也是先去了解的参数配置等等。

项目主要是从机的移植,标准库移到HAL库,F030移植到F302,在此之前上一个经手人是移植到了G0上的,但是HAL库中的SPI却没有办法触发中断。

由于项目需求,接收到的数据是要在中断中处理并返回,数据处理函数相当繁琐,这是无可改变的,至少我想不到更好的办法,咱对HAL库也不熟,拿着HAL库的书硬着头皮试呗。

第一版直接卡死不运行,什么问题呢,直接进入HardFault中断里了,里面是while(1)死循环,此时用的是spi中断使能函数和spi使能函数,启动中断,中断回调函数里使用阻塞式接收。

再之后就是中断方式和堵塞式的各种搭配或者单独使用,经常直接卡死收到乱码,于是在Keil中进入的debug模式查句柄中的状态位等等,其中的一些宏定义如下:

errorcode
#define HAL_SPI_ERROR_NONE              (0x00000000U)   /*!< No error                               */
#define HAL_SPI_ERROR_MODF              (0x00000001U)   /*!< MODF error                             */
#define HAL_SPI_ERROR_CRC               (0x00000002U)   /*!< CRC error                              */
#define HAL_SPI_ERROR_OVR               (0x00000004U)   /*!< OVR error                              */
#define HAL_SPI_ERROR_FRE               (0x00000008U)   /*!< FRE error                              */
#define HAL_SPI_ERROR_DMA               (0x00000010U)   /*!< DMA transfer error                     */
#define HAL_SPI_ERROR_FLAG              (0x00000020U)   /*!< Error on RXNE/TXE/BSY/FTLVL/FRLVL Flag */
#define HAL_SPI_ERROR_ABORT             (0x00000040U)   /*!< Error during SPI Abort procedure       */
#if (USE_HAL_SPI_REGISTER_CALLBACKS == 1U)
#define HAL_SPI_ERROR_INVALID_CALLBACK  (0x00000080U)   /*!< Invalid Callback error                 */


state
  HAL_SPI_STATE_RESET      = 0x00U,    /*!< Peripheral not Initialized                         */
  HAL_SPI_STATE_READY      = 0x01U,    /*!< Peripheral Initialized and ready for use           */
  HAL_SPI_STATE_BUSY       = 0x02U,    /*!< an internal process is ongoing                     */
  HAL_SPI_STATE_BUSY_TX    = 0x03U,    /*!< Data Transmission process is ongoing               */
  HAL_SPI_STATE_BUSY_RX    = 0x04U,    /*!< Data Reception process is ongoing                  */
  HAL_SPI_STATE_BUSY_TX_RX = 0x05U,    /*!< Data Transmission and Reception process is ongoing */
  HAL_SPI_STATE_ERROR      = 0x06U,    /*!< SPI error state                                    */
  HAL_SPI_STATE_ABORT      = 0x07U     /*!< SPI abort is ongoing             


@defgroup SPI_Flags_definition SPI Flags Definition
  * @{     SR
  */
#define SPI_FLAG_RXNE                   SPI_SR_RXNE   /* SPI status flag: Rx buffer not empty flag       */1
#define SPI_FLAG_TXE                    SPI_SR_TXE    /* SPI status flag: Tx buffer empty flag           */2
#define SPI_FLAG_BSY                    SPI_SR_BSY    /* SPI status flag: Busy flag                      */80
#define SPI_FLAG_CRCERR                 SPI_SR_CRCERR /* SPI Error flag: CRC error flag                  */
#define SPI_FLAG_MODF                   SPI_SR_MODF   /* SPI Error flag: Mode fault flag                 */模式故障标志
#define SPI_FLAG_OVR                    SPI_SR_OVR    /* SPI Error flag: Overrun flag                    */40
#define SPI_FLAG_FRE                    SPI_SR_FRE    /* SPI Error flag: TI mode frame format error flag */
#define SPI_FLAG_FTLVL                  SPI_SR_FTLVL  /* SPI fifo transmission level                     */1800
#define SPI_FLAG_FRLVL                  SPI_SR_FRLVL  /* SPI fifo reception level                        */600
#define SPI_FLAG_MASK                   (SPI_SR_RXNE | SPI_SR_TXE | SPI_SR_BSY | SPI_SR_CRCERR\
                                         | SPI_SR_MODF | SPI_SR_OVR | SPI_SR_FRE | SPI_SR_FTLVL | SPI_SR_FRLVL)

经常出现的就是溢出,电平,模式故障标志,到最后写了错误中断回调处理函数,倒是可以一直运行了,只是接收到的数据都不对,最后经查证是每次都溢出了,可为啥之前F302标准库就可以呢,同样都是复杂的中断处理函数,甚至现在的时钟还比原来高,随后开始研究HAL库一次完整的中断都要干什么,最后发现那干的可太多了比我处理函数都复杂,甚至于回调函数啥也不干就光储存数据都经常溢出。

其实早就想用DMA了,主要是对DMA不太熟悉,对HAL库也不太熟,并且时间比较紧,谁知道能整出啥幺蛾子。

于是不得已尝试了,心中很早就想试的方法了,直接删掉自带中断服务函数,直接调用我的回调函数,直接读寄存器,经过对HAL库中断函数的研究,加上点需要的东西就可以了。于是结果就确实可以了。

三、代码分享

首先、主函数里先开启中断:

    __HAL_SPI_ENABLE_IT(&hspi2, SPI_IT_RXNE);
	__HAL_SPI_ENABLE(&hspi2);

然后我们看中断函数:

void SPI2_IRQHandler(void)
{
  /* USER CODE BEGIN SPI2_IRQn 0 */

  /* USER CODE END SPI2_IRQn 0 */
	HAL_SPI_TxRxCpltCallback(&hspi2);
	
  

  /* USER CODE BEGIN SPI2_IRQn 1 */

  /* USER CODE END SPI2_IRQn 1 */
}

就一个回调没别的。

void HAL_SPI_TxRxCpltCallback(SPI_HandleTypeDef *hspi)
{
    if(hspi ==(&hspi2))
    {

		if(SPI_RxCnt==0) memset(SPI_RxBuff,0,sizeof(SPI_RxBuff));
		SPI_RxBuff[SPI_RxCnt]=SPI2_ReadByte16(&hspi2);
		SPI_RxCnt++;
		data_processing();//数据处理

        
    }
}

数据处理函数中主要用到了以下两个函数:

void SPI2_WriteByte16(uint16_t TxData)
{
	 while(__HAL_SPI_GET_FLAG(&hspi2,SPI_FLAG_TXE)==false);
//	SPI_TxBuff[0]=TxData;
    hspi2.Instance->DR = TxData;
	
}
uint16_t SPI2_ReadByte16(SPI_HandleTypeDef *hspi)
{
    uint16_t res = 0;
	while(__HAL_SPI_GET_FLAG(&hspi2,SPI_FLAG_RXNE)==false);
     *((uint16_t *)&res) = (uint16_t)hspi->Instance->DR;
    return res;
}

如果使用HAL库函数发送,发送完一次会自动关闭中断,需要再次使用函数。但效率堪忧,直接从中提取用于开启中断的函数使用,在使用HAL库函数发送时又会引起错误,第一版就是这种情况。

所以最后这个硬解法,其实什么也不用额外加。

PS:本人水平有限,此方法针对不同项目需求有效性可能并不一致,不过确实是能收到正确的数据了,正确率吗还是有的,毕竟应用层协议和如何主机如何发送,压根就不是我写的(是之前就做好的),还用的是HAL库收发,他是主机他肯定是想咋样咋样,我这是从机。能力属实有限,还请各位看官多多指教。

你可能感兴趣的:(SPI,stm32,c语言)