之前面试听一位面试官问我有没有使用过HAL库和 STM32CubeMX,那时候只是再原子的例程上看到过。但是他跟我力荐,于是不由自主的对STM32CubeMX产生了一定的好感。自己再这一年里断断续续使用过STM32CubeMX以及HAL库,一直没有再项目中使用过,于是我终于等到一个机会。
项目不方便细说,大概就是使用华为9094G模块连接服务器通讯,并且处理服务器的数据,来操纵IO口。实现项目功能。一开始也是犯了邪。我使用标准库,初始化的串口2给4G模块发数据,4G模块死活收不到我发的东西。而发送完成标记位,寄存器都自己清楚了。万般无奈自己使用了CubeMX建立了一个工程居然可以。然后就开始了挖坑。
首先CubeMX建立的工程初始化串口,并且配置中断,其实它并没有帮你设置好是什么中断,是RXNE中断还是IT_ERR中断。所以再初始化串口之后需要自己加上
__HAL_UART_ENABLE_IT(&huart2,UART_IT_RXNE); 或者
HAL_UART_Receive_IT(&huart2,aRxBuffer, 1); 这句
当然两句有不同的区别
HAL_UART_Receive_IT(&huart2,aRxBuffer, 1); 这句进入函数以后
注意!!!!这一句一定要在串口初始化函数结束之后再调用,如果这个函数的返回值是HAL_BUSY,恭喜你,你的串口是进不去中断的,只有这个函数初始化返回值是HAL_OK,才会进入串口接收中断
HAL_StatusTypeDef HAL_UART_Receive_IT(UART_HandleTypeDef *huart, uint8_t *pData, uint16_t Size)
{
uint32_t tmp_state = 0;
tmp_state = huart->State;
if((tmp_state == HAL_UART_STATE_READY) || (tmp_state == HAL_UART_STATE_BUSY_TX))
{
if((pData == NULL ) || (Size == 0))
{
return HAL_ERROR;
}
/* Process Locked */
__HAL_LOCK(huart);
huart->pRxBuffPtr = pData;
huart->RxXferSize = Size;
huart->RxXferCount = Size;
huart->ErrorCode = HAL_UART_ERROR_NONE;
/* Check if a transmit process is ongoing or not */
if(huart->State == HAL_UART_STATE_BUSY_TX)
{
huart->State = HAL_UART_STATE_BUSY_TX_RX;
}
else
{
huart->State = HAL_UART_STATE_BUSY_RX;
}
/* Process Unlocked */
__HAL_UNLOCK(huart);
/* Enable the UART Parity Error Interrupt */
__HAL_UART_ENABLE_IT(huart, UART_IT_PE);
/* Enable the UART Error Interrupt: (Frame error, noise error, overrun error) */
__HAL_UART_ENABLE_IT(huart, UART_IT_ERR);
/* Enable the UART Data Register not empty Interrupt */
__HAL_UART_ENABLE_IT(huart, UART_IT_RXNE);
return HAL_OK;
}
else
{
return HAL_BUSY;
}
}
是会通过查询串口是否准备就绪,上锁HAL库。以及将传入参数丢给串口HAL结构体
huart->pRxBuffPtr = pData;
huart->RxXferSize = Size;
huart->RxXferCount = Size;
也就是接收数据缓存地址,字节大小,长度,传给串口结构体,
然后解锁HAL库使能三个中断,其中就有RXNE中断
然后串口接收到数据之后,进入串口中断,串口中断我是这样写的
void USART2_IRQHandler(void)
{
/* USER CODE BEGIN USART2_IRQn 0 */
//使用串口中断回调函数HAL_UART_RxCpltCallback来处理
#if RXNE_CALL
HAL_UART_IRQHandler(&huart2);
uint32_t timeout = 0;
timeout=0;
while (HAL_UART_GetState(&huart2) != HAL_UART_STATE_READY)//等待就绪
{
timeout++;超时处理
if(timeout>0x1FFFF) break;
}
timeout=0;
while(HAL_UART_Receive_IT(&huart2,aRxBuffer, 1) != HAL_OK)//一次处理完成之后,重新开启中断并设置RxXferCount为1
{
timeout++; //超时处理
if(timeout>0x1FFFF) break;
}
}
再串口中断中首先进入串口中断处理函数 HAL_UART_IRQHandler(&huart2);
也就是这个样子
void HAL_UART_IRQHandler(UART_HandleTypeDef *huart)
{
uint32_t tmp_flag = 0, tmp_it_source = 0;
tmp_flag = __HAL_UART_GET_FLAG(huart, UART_FLAG_PE);
tmp_it_source = __HAL_UART_GET_IT_SOURCE(huart, UART_IT_PE);
/* UART parity error interrupt occurred ------------------------------------*/
if((tmp_flag != RESET) && (tmp_it_source != RESET))
{
huart->ErrorCode |= HAL_UART_ERROR_PE;
}
tmp_flag = __HAL_UART_GET_FLAG(huart, UART_FLAG_FE);
tmp_it_source = __HAL_UART_GET_IT_SOURCE(huart, UART_IT_ERR);
/* UART frame error interrupt occurred -------------------------------------*/
if((tmp_flag != RESET) && (tmp_it_source != RESET))
{
huart->ErrorCode |= HAL_UART_ERROR_FE;
}
tmp_flag = __HAL_UART_GET_FLAG(huart, UART_FLAG_NE);
/* UART noise error interrupt occurred -------------------------------------*/
if((tmp_flag != RESET) && (tmp_it_source != RESET))
{
huart->ErrorCode |= HAL_UART_ERROR_NE;
}
tmp_flag = __HAL_UART_GET_FLAG(huart, UART_FLAG_ORE);
/* UART Over-Run interrupt occurred ----------------------------------------*/
if((tmp_flag != RESET) && (tmp_it_source != RESET))
{
huart->ErrorCode |= HAL_UART_ERROR_ORE;
}
tmp_flag = __HAL_UART_GET_FLAG(huart, UART_FLAG_RXNE);
tmp_it_source = __HAL_UART_GET_IT_SOURCE(huart, UART_IT_RXNE);
/* UART in mode Receiver ---------------------------------------------------*/
if((tmp_flag != RESET) && (tmp_it_source != RESET))
{
UART_Receive_IT(huart);
}
tmp_flag = __HAL_UART_GET_FLAG(huart, UART_FLAG_TXE);
tmp_it_source = __HAL_UART_GET_IT_SOURCE(huart, UART_IT_TXE);
/* UART in mode Transmitter ------------------------------------------------*/
if((tmp_flag != RESET) && (tmp_it_source != RESET))
{
UART_Transmit_IT(huart);
}
tmp_flag = __HAL_UART_GET_FLAG(huart, UART_FLAG_TC);
tmp_it_source = __HAL_UART_GET_IT_SOURCE(huart, UART_IT_TC);
/* UART in mode Transmitter end --------------------------------------------*/
if((tmp_flag != RESET) && (tmp_it_source != RESET))
{
UART_EndTransmit_IT(huart);
}
if(huart->ErrorCode != HAL_UART_ERROR_NONE)
{
/* Clear all the error flag at once */
__HAL_UART_CLEAR_PEFLAG(huart);
/* Set the UART state ready to be able to start again the process */
huart->State = HAL_UART_STATE_READY;
HAL_UART_ErrorCallback(huart);
}
}
然后根据中断类型进行相对应的处理,因为是收到数据,所以会进入要此处处理
tmp_flag = __HAL_UART_GET_FLAG(huart, UART_FLAG_RXNE);
tmp_it_source = __HAL_UART_GET_IT_SOURCE(huart, UART_IT_RXNE);
/* UART in mode Receiver ---------------------------------------------------*/
if((tmp_flag != RESET) && (tmp_it_source != RESET))
{
UART_Receive_IT(huart);
}
跳入 UART_Receive_IT(huart);函数
static HAL_StatusTypeDef UART_Receive_IT(UART_HandleTypeDef *huart)
{
uint16_t* tmp;
uint32_t tmp_state = 0;
tmp_state = huart->State;
if((tmp_state == HAL_UART_STATE_BUSY_RX) || (tmp_state == HAL_UART_STATE_BUSY_TX_RX))
{
if(huart->Init.WordLength == UART_WORDLENGTH_9B)
{
tmp = (uint16_t*) huart->pRxBuffPtr;
if(huart->Init.Parity == UART_PARITY_NONE)
{
*tmp = (uint16_t)(huart->Instance->DR & (uint16_t)0x01FF);
huart->pRxBuffPtr += 2;
}
else
{
*tmp = (uint16_t)(huart->Instance->DR & (uint16_t)0x00FF);
huart->pRxBuffPtr += 1;
}
}
else
{
if(huart->Init.Parity == UART_PARITY_NONE)
{
*huart->pRxBuffPtr++ = (uint8_t)(huart->Instance->DR & (uint8_t)0x00FF);
}
else
{
*huart->pRxBuffPtr++ = (uint8_t)(huart->Instance->DR & (uint8_t)0x007F);
}
}
if(--huart->RxXferCount == 0)
{
__HAL_UART_DISABLE_IT(huart, UART_IT_RXNE);
/* Check if a transmit process is ongoing or not */
if(huart->State == HAL_UART_STATE_BUSY_TX_RX)
{
huart->State = HAL_UART_STATE_BUSY_TX;
}
else
{
/* Disable the UART Parity Error Interrupt */
__HAL_UART_DISABLE_IT(huart, UART_IT_PE);
/* Disable the UART Error Interrupt: (Frame error, noise error, overrun error) */
__HAL_UART_DISABLE_IT(huart, UART_IT_ERR);
huart->State = HAL_UART_STATE_READY;
}
HAL_UART_RxCpltCallback(huart);
return HAL_OK;
}
return HAL_OK;
}
else
{
return HAL_BUSY;
}
}
它也是看串口状态,然后根据串口配置的是8位数据位还是9位数据位,把串口接收寄存器的值读到串口的HAL结构体的接收数据地址(*huart->pRxBuffPtr)里面去,也就是我们先前传入
HAL_UART_Receive_IT(UART_HandleTypeDef *huart, uint8_t *pData, uint16_t Size)
这个函数的pData地址。
到了这个时候一个字节的数据已经接收完成,但是这个中断处理函数把我们的RXNE中断给关了。
这个也就是主循环之前为什么调用了一遍
HAL_UART_Receive_IT(UART_HandleTypeDef *huart, uint8_t *pData, uint16_t Size)
函数,再串口中断里面又调用一边这个函数。
再然后串口中断又调用了 HAL_UART_RxCpltCallback(huart);函数
这个也就是所说的串口中断回调函数,这个时候我们可以定义一个这个函数,再这个函数里面对接收的数据进行处理。
当然我们可以做简单点的处理 ,我试过也可以。
直接主循环之前调用
__HAL_UART_ENABLE_IT(&huart2,UART_IT_RXNE);
来打开RXNE中断
然后再串口中断函数里面直接处理数据也就是把串口中断写成这个样子
void USART2_IRQHandler(void)
{
uint8_t Res;
if((__HAL_UART_GET_FLAG(&huart2,UART_FLAG_RXNE)!=RESET))
{
HAL_UART_Receive(&huart2,&Res,1,1000);
if(Hua_Wei_Module.Rx_Cnt_W >= 1024) Hua_Wei_Module.Rx_Cnt_W = 0;
Hua_Wei_Module.Rx_Data_temp[Hua_Wei_Module.Rx_Cnt_W++]=Res ;// 储存到缓冲区,在定时器里处理缓冲区的内容
}
//-------下面这句中断处理最终会调用HAL_UART_RxCpltCallback(huart);这个回调函数,
//-------并且__HAL_UART_DISABLE_IT(huart, UART_IT_RXNE);禁用RX非空中断,
//-------所以需要在这个函数处理完成之后打开RXNE中断。
/* USER CODE END USART2_IRQn 0 */
HAL_UART_IRQHandler(&huart2);
/* USER CODE BEGIN USART2_IRQn 1 */
__HAL_UART_ENABLE_IT(&huart2,UART_IT_RXNE);
#endif
}
这个处理更简单粗暴,HAL库的中断处理确实复杂繁琐,初学者使用非常不方便。
第二个坑也就是HAL库的串口发送,最开始我用4G模块联网通讯一直很正常,应用层的协议我们是用公司自己制定的。然后联网发送心跳什么的都很正常数据字节也在50-100之间。这个时候使用HAL库的串口发送函数就没什么问题,一直很正常。也就是这一句
/**
* @brief 向909模块发送字符串函数
* @param data:需要发送的字符串
* @retval
*/
//void SendStr_MU909(uint8_t *data)
//{
// HAL_UART_Transmit(&huart2,data,strlen((const char*)data),0xffff);
//}
等到服务器发送需要执行的任务数据,已经MCU执行完需要返回执行结果的时候,数据大概再一百多个字节,串口发送函数就开始出毛病,MCU卡死再串口发送这一块,定时器没有死,回不去主循环,确实我调用过的是0xFFFF,应该是一直等待发送完成的意思,所以才会一直回不去主循环,导致MCU卡住。
然后我直接使用寄存器发送
void SendStr_MU909(uint8_t *data)
{
uint32_t t= 0;
uint32_t len = strlen((const char*)data);
for(t=0;tDR=data[t];
while((USART2->SR&0X40)==0);//等待发送结束
}
}
测试很久一点问题也没有·······