日常入坑之STM32串口空闲中断问题

前言:
在使用小熊派开发板STM32L4单片机开发蓝牙驱动时遇到的了一个非常非常奇怪的问题,当然也怪学术不精,具体问题描述如下:使用STM32L4单片机,蓝牙为串口驱动,选择单片机的串口3,为啥选择这个呢,这得问问我那脾气很臭的硬件工程师,串口配置为DMA空闲中断,使用STMcube生成的代码,代码生成后基本初始化已经自动完成,在外设比较少的情况下运行正常,但是当把外设加多的时候,比如初始化了ADC、TIM、SPI等时候,程序在刚开始运行就进入硬件故障中断,HardFault_Handler();纠结了好几天,各种仿真单步调试都不行,折磨的我是要死要活的,仿真的时候也是很乱,什么时候跳进去的也不稳定,具体代码结构如下:

/* Initialize all configured peripherals */
  MX_GPIO_Init();
  MX_DMA_Init();
  MX_USART1_UART_Init();
  MX_USART3_UART_Init();
  MX_ADC1_Init();
  MX_SPI2_Init();
  MX_SPI1_Init();
  MX_TIM1_Init();
  /* USER CODE BEGIN 2 */
	Uart_Drv_init();
	BLE_Drv_odev_init();
//	MPU6050_init();
//	mpu_dmp_init();
	cdsSensor_odev_init();
	HAL_TIM_Base_Start_IT(&htim1);
	/*Configure RS485 GPIO pin Output High Level,Because RS485 Just Send*/
//  HAL_GPIO_WritePin(GPIOC, RS485EN_Pin, GPIO_PIN_SET);
  /* USER CODE END 2 */

  /* Infinite loop */
  /* USER CODE BEGIN WHILE */
	my_log("Hello world!\r\n");

后面发现规律,只要串口初始化 MX_USART1_UART_Init();
MX_USART3_UART_Init();与 Uart_Drv_init();初始化相隔比较远的话就会出现这种问题,那么问题就在于初始化Uart_Drv_init()时具体操作了什么,再进这个函数看看:

  /*Ä£¿é³õʼ»¯*/
void Uart_Drv_init(void)
{
	Uart_Drv_struct_init();
	USRT_DMA_IDLE_Start();
	my_rb_init(&Uart_Drv_data.Ring3, Uart_Drv_data.RingBuf3, sizeof(Uart_Drv_data.RingBuf3));	//³õʼ»¯»·Ðλº³åÇø

	Uart_Drv_data.Uart1_sendFun = bUart1_sendFun;
	Uart_Drv_data.Uart3_reciveFun = bUart_reciveFun;
	Uart_Drv_data.Uart3_sendFun = bUart3_sendFun;
	Uart_Drv_data.Uart3_RecvCB = UART3_Rx_IDLE_Callback;
	Uart_Drv_data.Uart1_RecvCB = UART1_Rx_IDLE_Callback;
	Uart_Drv_data.Uart1_SendCB = UART1_Tx_Callback;
	Uart_Drv_data.Uart3_SendCB = UART3_Tx_Callback; 
}

此函数就是初始化串口收发使用的内存、回调函数等,其中有一个函数比较特殊,那就是:USRT_DMA_IDLE_Start();

void USRT_DMA_IDLE_Start()
{
	__HAL_UART_ENABLE_IT(&huart1,UART_IT_IDLE);        //ʹÄÜIDLEÖжÏ
	HAL_UART_Receive_DMA(&huart1,Uart_Drv_data.Uart1RxBuff,UART_DMA_BUFF_LEN_MAX);    //¿ªÆôDMA½ÓÊÕ
	
		__HAL_UART_ENABLE_IT(&huart3,UART_IT_IDLE);	
	HAL_UART_Receive_DMA(&huart3,Uart_Drv_data.Uart3RxBuff,UART_DMA_BUFF_LEN_MAX);

}

其功能就是开启空闲中断,开启DMA接收并配置接收buff;
功能介绍完了,再插一句过程的艰辛,期间实在没辙了,就请教了一位网络上认识的嵌入式大神,一位老东家的大神,也是我的师父吧,两位没有实际看我的代码,只是教我处理办法,当然是在微信上问了一下,给了大概思路,也就是内存越界等等,后面发现初始化的时候把蓝牙串口不要接,就会成功,此时知道是蓝牙串口有影响,为什么有影响,分析原因是蓝牙在上电的时候有数据从串口发出去,那么我在仿真的时候把断点打到回调这里;

/* USER CODE BEGIN 4 */
void UART_Rx_Callback(UART_HandleTypeDef *huart)
{
	if (!__HAL_UART_GET_FLAG(huart,UART_FLAG_IDLE))
	{
		return;
	}
	//Çå³ýIDLEÖжÏ
	__HAL_UART_CLEAR_IDLEFLAG(huart);
	
	//ÅжÏÖжÏÔ´
	if (huart == &huart1)
	{
//		UART1_Rx_IDLE_Callback();
		Uart_Drv_data.Uart1_RecvCB();
	}
	else if(huart == &huart3)
	{
//		UART2_Rx_IDLE_Callback();
		Uart_Drv_data.Uart3_RecvCB();
	}


}

这时候有经验的大神估计已经知道问题所在了,可是我还是没经验啊,不明白啊,发现还没有初始化完Uart_Drv_init()这个函数,就已经有中断进来,此时必死;恍然大悟,原来回调函数中没有分配接收函数指针,串口也没有分配buff,这可不是内存越界嘛;再进入系统自动生成的初始化串口函数: MX_USART1_UART_Init();
MX_USART3_UART_Init();中,进去如下:

/* USART3 init function */

void MX_USART3_UART_Init(void)
{

  huart3.Instance = USART3;
  huart3.Init.BaudRate = 115200;
  huart3.Init.WordLength = UART_WORDLENGTH_8B;
  huart3.Init.StopBits = UART_STOPBITS_1;
  huart3.Init.Parity = UART_PARITY_NONE;
  huart3.Init.Mode = UART_MODE_TX_RX;
  huart3.Init.HwFlowCtl = UART_HWCONTROL_NONE;
  huart3.Init.OverSampling = UART_OVERSAMPLING_16;
  huart3.Init.OneBitSampling = UART_ONE_BIT_SAMPLE_DISABLE;
  huart3.AdvancedInit.AdvFeatureInit = UART_ADVFEATURE_NO_INIT;
  if (HAL_UART_Init(&huart3) != HAL_OK)
  {
    _Error_Handler(__FILE__, __LINE__);
  }

}

void HAL_UART_MspInit(UART_HandleTypeDef* uartHandle)
{

  GPIO_InitTypeDef GPIO_InitStruct;
  if(uartHandle->Instance==USART1)
  {
  /* USER CODE BEGIN USART1_MspInit 0 */

  /* USER CODE END USART1_MspInit 0 */
    /* USART1 clock enable */
    __HAL_RCC_USART1_CLK_ENABLE();
  
    /**USART1 GPIO Configuration    
    PA9     ------> USART1_TX
    PA10     ------> USART1_RX 
    */
    GPIO_InitStruct.Pin = GPIO_PIN_9|GPIO_PIN_10;
    GPIO_InitStruct.Mode = GPIO_MODE_AF_PP;
    GPIO_InitStruct.Pull = GPIO_NOPULL;
    GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_VERY_HIGH;
    GPIO_InitStruct.Alternate = GPIO_AF7_USART1;
    HAL_GPIO_Init(GPIOA, &GPIO_InitStruct);

    /* USART1 DMA Init */
    /* USART1_RX Init */
    hdma_usart1_rx.Instance = DMA1_Channel5;
    hdma_usart1_rx.Init.Request = DMA_REQUEST_2;
    hdma_usart1_rx.Init.Direction = DMA_PERIPH_TO_MEMORY;
    hdma_usart1_rx.Init.PeriphInc = DMA_PINC_DISABLE;
    hdma_usart1_rx.Init.MemInc = DMA_MINC_ENABLE;
    hdma_usart1_rx.Init.PeriphDataAlignment = DMA_PDATAALIGN_BYTE;
    hdma_usart1_rx.Init.MemDataAlignment = DMA_MDATAALIGN_BYTE;
    hdma_usart1_rx.Init.Mode = DMA_NORMAL;
    hdma_usart1_rx.Init.Priority = DMA_PRIORITY_LOW;
    if (HAL_DMA_Init(&hdma_usart1_rx) != HAL_OK)
    {
      _Error_Handler(__FILE__, __LINE__);
    }

    __HAL_LINKDMA(uartHandle,hdmarx,hdma_usart1_rx);

    /* USART1_TX Init */
    hdma_usart1_tx.Instance = DMA1_Channel4;
    hdma_usart1_tx.Init.Request = DMA_REQUEST_2;
    hdma_usart1_tx.Init.Direction = DMA_MEMORY_TO_PERIPH;
    hdma_usart1_tx.Init.PeriphInc = DMA_PINC_DISABLE;
    hdma_usart1_tx.Init.MemInc = DMA_MINC_ENABLE;
    hdma_usart1_tx.Init.PeriphDataAlignment = DMA_PDATAALIGN_BYTE;
    hdma_usart1_tx.Init.MemDataAlignment = DMA_MDATAALIGN_BYTE;
    hdma_usart1_tx.Init.Mode = DMA_NORMAL;
    hdma_usart1_tx.Init.Priority = DMA_PRIORITY_LOW;
    if (HAL_DMA_Init(&hdma_usart1_tx) != HAL_OK)
    {
      _Error_Handler(__FILE__, __LINE__);
    }

    __HAL_LINKDMA(uartHandle,hdmatx,hdma_usart1_tx);

    /* USART1 interrupt Init */
    HAL_NVIC_SetPriority(USART1_IRQn, 0, 0);
    HAL_NVIC_EnableIRQ(USART1_IRQn);
  /* USER CODE BEGIN USART1_MspInit 1 */

  /* USER CODE END USART1_MspInit 1 */
  }
  else if(uartHandle->Instance==USART3)
  {
  /* USER CODE BEGIN USART3_MspInit 0 */

  /* USER CODE END USART3_MspInit 0 */
    /* USART3 clock enable */
    __HAL_RCC_USART3_CLK_ENABLE();
  
    /**USART3 GPIO Configuration    
    PC10     ------> USART3_TX
    PC11     ------> USART3_RX 
    */
    GPIO_InitStruct.Pin = GPIO_PIN_10|GPIO_PIN_11;
    GPIO_InitStruct.Mode = GPIO_MODE_AF_PP;
    GPIO_InitStruct.Pull = GPIO_NOPULL;
    GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_VERY_HIGH;
    GPIO_InitStruct.Alternate = GPIO_AF7_USART3;
    HAL_GPIO_Init(GPIOC, &GPIO_InitStruct);

    /* USART3 DMA Init */
    /* USART3_RX Init */
    hdma_usart3_rx.Instance = DMA1_Channel3;
    hdma_usart3_rx.Init.Request = DMA_REQUEST_2;
    hdma_usart3_rx.Init.Direction = DMA_PERIPH_TO_MEMORY;
    hdma_usart3_rx.Init.PeriphInc = DMA_PINC_DISABLE;
    hdma_usart3_rx.Init.MemInc = DMA_MINC_ENABLE;
    hdma_usart3_rx.Init.PeriphDataAlignment = DMA_PDATAALIGN_BYTE;
    hdma_usart3_rx.Init.MemDataAlignment = DMA_MDATAALIGN_BYTE;
    hdma_usart3_rx.Init.Mode = DMA_NORMAL;
    hdma_usart3_rx.Init.Priority = DMA_PRIORITY_LOW;
    if (HAL_DMA_Init(&hdma_usart3_rx) != HAL_OK)
    {
      _Error_Handler(__FILE__, __LINE__);
    }

    __HAL_LINKDMA(uartHandle,hdmarx,hdma_usart3_rx);

    /* USART3_TX Init */
    hdma_usart3_tx.Instance = DMA1_Channel2;
    hdma_usart3_tx.Init.Request = DMA_REQUEST_2;
    hdma_usart3_tx.Init.Direction = DMA_MEMORY_TO_PERIPH;
    hdma_usart3_tx.Init.PeriphInc = DMA_PINC_DISABLE;
    hdma_usart3_tx.Init.MemInc = DMA_MINC_ENABLE;
    hdma_usart3_tx.Init.PeriphDataAlignment = DMA_PDATAALIGN_BYTE;
    hdma_usart3_tx.Init.MemDataAlignment = DMA_MDATAALIGN_BYTE;
    hdma_usart3_tx.Init.Mode = DMA_NORMAL;
    hdma_usart3_tx.Init.Priority = DMA_PRIORITY_LOW;
    if (HAL_DMA_Init(&hdma_usart3_tx) != HAL_OK)
    {
      _Error_Handler(__FILE__, __LINE__);
    }

    __HAL_LINKDMA(uartHandle,hdmatx,hdma_usart3_tx);

    /* USART3 interrupt Init */
    HAL_NVIC_SetPriority(USART3_IRQn, 0, 0);
    HAL_NVIC_EnableIRQ(USART3_IRQn);
  /* USER CODE BEGIN USART3_MspInit 1 */

  /* USER CODE END USART3_MspInit 1 */
  }
}

其中有两句:

 /* USART3 interrupt Init */
    HAL_NVIC_SetPriority(USART3_IRQn, 0, 0);
    HAL_NVIC_EnableIRQ(USART3_IRQn);
  /* USER CODE BEGIN USART3_MspInit 1 */

配置中断与开启中断,也就是此时已经开启中断了,串口中有数据进来是可以进回调函数滴,此时愚笨的我才反应过来,原来如此啊;总结一句:串口初始化完了后中断开启了,结果回调函数还没有初始化完,串口中有数据进来就死了;
目前的解决方法就是先关掉串口中断使能:
HAL_NVIC_DisableIRQ(USART1_IRQn);
HAL_NVIC_DisableIRQ(USART3_IRQn);
等初始化完void Uart_Drv_init(void)这个时,再开启, HAL_NVIC_EnableIRQ(USART1_IRQn);
HAL_NVIC_EnableIRQ(USART3_IRQn);
此时测试正常;
最后感悟一下,这个问题给我的影响非常深刻,写此博客记录入坑的心路,也为几年后回看当年小白的我!!!!

你可能感兴趣的:(STM32,单片机,嵌入式,stm32)