使用的是STM32F412RETx的芯片,板子是电子工程师做的
使用STM32CubeMX V5.2.1、Keil uVision5做开发,使用HAL库
使用过程中多次出现串口接收的问题,最后都解决了,这里记录一下
串口的HAL有3类API
// 同步堵塞收发
HAL_StatusTypeDef HAL_UART_Transmit(UART_HandleTypeDef *huart, uint8_t *pData, uint16_t Size, uint32_t Timeout);
HAL_StatusTypeDef HAL_UART_Receive(UART_HandleTypeDef *huart, uint8_t *pData, uint16_t Size, uint32_t Timeout);
// 异步中断传输
HAL_StatusTypeDef HAL_UART_Transmit_IT(UART_HandleTypeDef *huart, uint8_t *pData, uint16_t Size);
HAL_StatusTypeDef HAL_UART_Receive_IT(UART_HandleTypeDef *huart, uint8_t *pData, uint16_t Size);
// 异步DMA传输(Direct Memory Access,DMA),不经过CPU,外设直接读写内存
HAL_StatusTypeDef HAL_UART_Transmit_DMA(UART_HandleTypeDef *huart, uint8_t *pData, uint16_t Size);
HAL_StatusTypeDef HAL_UART_Receive_DMA(UART_HandleTypeDef *huart, uint8_t *pData, uint16_t Size);
我使用的是异步中断HAL_UART_Receive_IT的进行串口数据接收,这个需要用到
接收完成中断回调
void HAL_UART_RxCpltCallback(UART_HandleTypeDef *huart);
在中断回调中只进行数据的解析,不做过多其他处理,避免中断时间过长,中断中也不要使用延时函数,尽量不在中断中进行IO输出操作。
第一种串口接收问题:现象为开机后串口可以接收数据,一会儿后就一直没有数据了。
查看错误为:HAL_UART_Receive_IT返回HAL_BUSY
先说一下我的使用方法:使用HAL_UART_Receive_IT接收数据,需要在每次接收完成后,再次调用HAL_UART_Receive_IT函数,一般是在HAL_UART_RxCpltCallback函数的末尾再次HAL_UART_Receive_IT。
HAL_UART_Receive_IT有个状态返回值,可以自己看一下这个函数的实现,代码也就几十行
HAL_StatusTypeDef HAL_UART_Receive_IT(UART_HandleTypeDef *huart, uint8_t *pData, uint16_t Size)
{
/* Check that a Rx process is not already ongoing */
if (huart->RxState == HAL_UART_STATE_READY)
{
if ((pData == NULL) || (Size == 0U))
{
return HAL_ERROR;
}
/* Process Locked */
__HAL_LOCK(huart);
huart->pRxBuffPtr = pData;
huart->RxXferSize = Size;
huart->RxXferCount = Size;
huart->ErrorCode = HAL_UART_ERROR_NONE;
huart->RxState = 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_BUSY居多,这里有两种情况会返回HAL_BUSY
1. huart->RxState != HAL_UART_STATE_READY,串口没准备好或者串口正在接收数据中,如果时正在接收数据,意味着其他地方已经调用过HAL_UART_Receive_IT,直接忽略等待接收完成就好,而我知道只有在HAL_UART_RxCpltCallback有再次调用,所以其他地方调用不存在,而且失败时查看过状态huart->RxState 是等于 HAL_UART_STATE_READY
2. __HAL_LOCK(huart);加锁失败,锁被占用。
看一下__HAL_LOCK(__HANDLE__)的定义
#define __HAL_LOCK(__HANDLE__)
do{
if((__HANDLE__)->Lock == HAL_LOCKED)
{
return HAL_BUSY;
}
else
{
(__HANDLE__)->Lock = HAL_LOCKED;
}
}while (0U)
这里就判断了一次,如果锁被占用,直接返回,从我这边测试可以看到,一般都是因为锁被暂用然后返回了HAL_BUSY,那么就要看一下哪里占用了锁。
接收数据使用的是异步中断的函数HAL_UART_Receive_IT(),然而发送数据我使用的是同步堵塞的函数HAL_UART_Transmit(),可以自己看一下HAL_UART_Transmit()的实现,这里不列代码了,HAL_UART_Transmit()函数内从开始发送开始加锁,等待全部数据发送完成后才解锁,所以占用锁的时间是比较长的。而数据发送也比较多,所以基本判定是发送造成的加锁。
有两种方法可以解决发送造成的加锁问题:
1. 使用异步函数发送,这样占用锁时间就短,不过也有概率锁占用,可以选择重试几次可能就可以了
2. huart->RxState=HAL_UART_STATE_READY,且保证不存在多处代码同时发送,那么可以选择暴力解锁,我使用这种方案
下面确认一下被加锁的代码
加锁一般是因为要操作一下公用的数据。
下面我们分析一下串口UART_HandleTypeDef结构体
typedef struct __UART_HandleTypeDef
{
USART_TypeDef *Instance; /*!< UART registers base address */
UART_InitTypeDef Init; /*!< UART communication parameters */
uint8_t *pTxBuffPtr; /*!< Pointer to UART Tx transfer Buffer */
uint16_t TxXferSize; /*!< UART Tx Transfer size */
__IO uint16_t TxXferCount; /*!< UART Tx Transfer Counter */
uint8_t *pRxBuffPtr; /*!< Pointer to UART Rx transfer Buffer */
uint16_t RxXferSize; /*!< UART Rx Transfer size */
__IO uint16_t RxXferCount; /*!< UART Rx Transfer Counter */
DMA_HandleTypeDef *hdmatx; /*!< UART Tx DMA Handle parameters */
DMA_HandleTypeDef *hdmarx; /*!< UART Rx DMA Handle parameters */
HAL_LockTypeDef Lock; /*!< Locking object */
__IO HAL_UART_StateTypeDef gState; /*!< UART state information related to global Handle management
and also related to Tx operations.
This parameter can be a value of @ref HAL_UART_StateTypeDef */
__IO HAL_UART_StateTypeDef RxState; /*!< UART state information related to Rx operations.
This parameter can be a value of @ref HAL_UART_StateTypeDef */
__IO uint32_t ErrorCode; /*!< UART Error code */
} UART_HandleTypeDef;
结合HAL_UART_Receive_IT()、HAL_UART_Transmit()两个函数,发现其实收发数据时使用的成员变量基本是分开的,
发送使用pTxBuffPtr、TxXferSize、TxXferCount、hdmatx、gState
接收使用pRxBuffPtr、RxXferSize、RxXferCount、hdmarx、RxState
共用部分:ErrorCode
gState也可能是公用的,不过暂时在函数HAL_UART_Receive_IT中没发现有使用gState
检查自己的代码,发现确实是HAL_UART_Transmit()造成的锁,既然基本上主要成员没有共用,那就暴力解锁
在判断接收状态为HAL_UART_STATE_READY时,且被加锁,直接暴力解锁
if(HAL_UART_STATE_READY == huart->RxState && HAL_LOCKED == huart->Lock)
{
__HAL_UNLOCK(huart); // 暴力解锁
}
至此由于加锁问题而造成的串口突然接收不到数据的问题暂时解决了,很暴力的方式
第一种串口接收问题:同样是上电后串口可以接收数据,接收一段时间后没数据了,
而且HAL_UART_Receive_IT()函数返回的是HAL_OK
检查了HAL_UART_GetError(),获取到错误HAL_UART_ERROR_ORE,应该是串口溢出的意思。
只是为什么溢出后就直接停止接收了?就算是丢包也不要给我直接停止工作了呀!!!删库后跑路了???
解决方法如下
既然知道报错,那就考虑清楚错误标志,特地也看了一下函数HAL_UART_IRQHandler(UART_HandleTypeDef *huart);内,的确是有错误标志的时候,不会调用接收完成回调
看了多篇文章,试了几种方法后终于找到一个清除错误标志有效的
使用__HAL_UART_CLEAR_OREFLAG(__HANDLE__)可以清空错误标志
上一篇:KEIL 配置STM32 SRAM启动,调试
下一篇:STM32F103_外部RAM用作运存
推荐阅读最新更新时间:2024-11-16 23:08
设计资源 培训 开发板 精华推荐
- SATA 6Gb/s 转 USB3.1 Gen1-JMS578
- 使用 ON Semiconductor 的 CMPWR101 的参考设计
- GL823K_USB母座版本读卡器
- B数显示器修改版
- 使用 Semtech 的 SC1633 的参考设计
- 使用 NXP Semiconductors 的 MC33816 的参考设计
- AD9154-EBZ、DPG3 评估板,用于评估 AD9154 四通道、16 位、2.4 GSPS、TxDAC+ 数模转换器
- type-c口 stlink
- LTC2668CUJ-16、16 通道、16 位 ±10V VOUT SoftSpan DAC 的典型应用电路
- LTC2400 的简单差分前端