前言:
在使用小熊派开发板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);
此时测试正常;
最后感悟一下,这个问题给我的影响非常深刻,写此博客记录入坑的心路,也为几年后回看当年小白的我!!!!